-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcheckswipe.js
150 lines (122 loc) · 5.48 KB
/
checkswipe.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
function checkswipe() {
/**
* @param {HTMLInputElement} checkbox
* @param {HTMLElement} parent
*/
function attachSingle(checkbox, parent) {
if (!(parent instanceof HTMLElement)) {
console.error('checkswipe: element', parent, `must be an html element; is '${typeof parent}'.`)
return
}
if (parent.parentElement?.closest('[data-checkswipe]') || parent.querySelector('[data-checkswipe]')) {
console.error('checkswipe: invalid structure for', parent, '; nested `data-checkswipe` elements are not allowed.')
return
}
if (!(checkbox instanceof HTMLInputElement) || checkbox.type !== 'checkbox') {
console.error('checkswipe: element', checkbox, `must be an html input element (type checkbox); is '${typeof checkbox}'.`)
return
}
if (!parent.contains(checkbox)) {
console.error('checkswipe: checkbox', checkbox, 'must be a child of parent', parent, '.')
return
}
if (!parent.dataset.checkswipe) {
parent.dataset.checkswipe = ''
}
if (parent.dataset.checkswipeSpecify !== undefined && checkbox.dataset.checkswipeUse === undefined) {
return
}
if (parent.dataset.checkswipeSpecify === undefined && checkbox.dataset.checkswipeUse !== undefined) {
console.error('checkswipe: checkbox', checkbox, 'is set to be specifically used (with `data-checkswipe-use`), but parent', parent, 'does not have `data-checkswipe-specify` attribute. this is not allowed as it causes inconsistencies – please do not manually checkswipe-ify single checkboxes without proper attributes on the parent.')
return
}
if (checkbox.__checkswipe) {
return
}
checkbox.__checkswipe = true
checkbox.addEventListener('mousedown', () => {
const state = !checkbox.checked
checkbox.checked = state
parent.dataset.checkswipe = state ? 'checked' : 'unchecked'
checkbox.dispatchEvent(new Event('change'))
const temporaryMouseUpHandler = () => {
parent.dataset.checkswipe = ''
document.removeEventListener('mouseup', temporaryMouseUpHandler)
}
document.addEventListener('mouseup', temporaryMouseUpHandler)
})
checkbox.addEventListener('mousemove', () => {
let state = parent.dataset.checkswipe
if (state !== 'checked' && state !== 'unchecked') {
return
}
const checked = state === 'checked'
if (checked !== checkbox.checked) {
checkbox.checked = checked
checkbox.dispatchEvent(new Event('change'))
}
})
checkbox.addEventListener('click', event => {
if (event instanceof MouseEvent && event.detail > 0) {
event.preventDefault()
}
})
}
/**
* @param {HTMLElement} group
*/
function attachGroup(group) {
if (!(group instanceof HTMLElement)) {
console.error('checkswipe: element', group, `must be an html element; is '${typeof group}'.`)
return
}
if (group.parentElement?.closest('[data-checkswipe]') || group.querySelector('[data-checkswipe]')) {
console.error('checkswipe: invalid structure for', group, '; nested `data-checkswipe` elements are not allowed.')
return
}
const hasSpecificCheckbox = group.querySelector('input[type=checkbox][data-checkswipe-use]') !== null
if (hasSpecificCheckbox && group.dataset.checkswipeSpecify === undefined) {
console.error('checkswipe: element', group, 'is missing attribute `data-checkswipe-specify`; has checkboxes with attribute `data-checkswipe-use`. this is not allowed.')
return
}
const checkboxes = group.querySelectorAll('input[type=checkbox]')
checkboxes.forEach(checkbox => attachSingle(checkbox, group))
}
if (typeof checkswipe.inject === 'function') {
checkswipe.inject()
}
const groups = document.querySelectorAll('[data-checkswipe]')
groups.forEach(group => attachGroup(group))
checkswipe.on = attachGroup
}
checkswipe.inject = function (nonce) {
// nuclear option >:)
checkswipe.inject = undefined
if (document.querySelector('head>style#checkswipe-injected')) {
console.warn('checkswipe: injected styles already exists, skipping injection of style...')
return
}
let style = document.createElement('style')
style.id = 'checkswipe-injected'
if (typeof nonce === 'string') {
style.setAttribute('nonce', nonce)
}
style.textContent = `
:root {
/* checkswipe defaults */
--checkswipe-scale: 1.3;
--checkswipe-delay: 0.1s;
--checkswipe-duration: 0.1s;
--checkswipe-easing: cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
[data-checkswipe]:not([data-checkswipe-specify]) input[type=checkbox],
[data-checkswipe][data-checkswipe-specify] input[type=checkbox][data-checkswipe-use] {
transition: transform var(--checkswipe-duration) var(--checkswipe-easing) var(--checkswipe-delay);
}
[data-checkswipe]:not([data-checkswipe='']):not([data-checkswipe-specify]) input[type=checkbox],
[data-checkswipe][data-checkswipe-specify]:not([data-checkswipe='']) input[type=checkbox][data-checkswipe-use] {
transform: scale(var(--checkswipe-scale));
}
`
document.head.appendChild(style)
}