@appius-fr/apx 2.6.1 → 2.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/APX.mjs +2 -0
  2. package/README.md +207 -203
  3. package/dist/2ab50e700c8fbddb45e0.svg +10 -0
  4. package/dist/2e967d8dd752e0bed703.svg +10 -0
  5. package/dist/5ddaeefe5dfbc8e09652.svg +7 -0
  6. package/dist/6dc2907ba3bbb232601d.svg +10 -0
  7. package/dist/6e1e61dfca176a885b8d.svg +3 -0
  8. package/dist/6f3a0a27a260bb2c221b.svg +9 -0
  9. package/dist/8b07a8bf719a38262b7d.svg +10 -0
  10. package/dist/APX.dev.mjs +843 -245
  11. package/dist/APX.mjs +1 -1
  12. package/dist/APX.prod.mjs +1 -1
  13. package/dist/APX.standalone.js +744 -92
  14. package/dist/APX.standalone.js.map +1 -1
  15. package/dist/bdfa755a1cdb872368c7.svg +3 -0
  16. package/dist/c9da177f7663f9fcd023.svg +10 -0
  17. package/dist/ce9ef5fceb78e17e68c9.svg +8 -0
  18. package/dist/ed5af5163957b04bc6cc.svg +7 -0
  19. package/modules/listen/README.md +242 -235
  20. package/modules/listen/listen.mjs +1 -3
  21. package/modules/scrollableTable/README.md +52 -0
  22. package/modules/scrollableTable/css/scrollableTable.css +60 -0
  23. package/modules/scrollableTable/scrollableTable.mjs +198 -0
  24. package/modules/toast/README.md +186 -153
  25. package/modules/tools/form-packer/README.md +12 -14
  26. package/modules/tools/form-packer/augment-apx.mjs +4 -2
  27. package/modules/tools/form-packer/packToJson.mjs +58 -16
  28. package/modules/tristate/CHANGELOG.md +34 -0
  29. package/modules/tristate/README.md +157 -94
  30. package/modules/tristate/assets/tristate-checked.svg +3 -0
  31. package/modules/tristate/assets/tristate-cross.svg +10 -0
  32. package/modules/tristate/assets/tristate-crossed.svg +3 -0
  33. package/modules/tristate/assets/tristate-indeterminate-dash.svg +9 -0
  34. package/modules/tristate/assets/tristate-tick.svg +10 -0
  35. package/modules/tristate/assets/tristate-unchecked.svg +7 -0
  36. package/modules/tristate/css/tristate.css +91 -24
  37. package/modules/tristate/tristate.mjs +292 -171
  38. package/package.json +5 -1
@@ -1,172 +1,293 @@
1
- import './css/tristate.css';
2
-
3
- /**
4
- * Creates a tri-state checkbox for each checkbox element in the apx.elements array.
5
- * @param {Array<HTMLElement>} apx - An array of HTMLElements which is returned by APX()
6
- * @example
7
- * // Call the tristate function on an array of checkbox elements
8
- * apx.tristate();
9
- */
10
- export default function (apx) {
11
- apx.tristate = function (options = {}){
12
- apx.elements.forEach((element) => {
13
- if (element.type === 'checkbox') {
14
- const [originalCheckbox, tristateDom, hiddenInput] = createTriStateCheckbox(element,options);
15
- //If there is a "options.callbacks.after" callback in options call it
16
- if(options.callbacks && options.callbacks.after && typeof options.callbacks.after === 'function') {
17
- options.callbacks.after(originalCheckbox, tristateDom, hiddenInput);
18
- }
19
- }
20
- });
21
- }
22
- }
23
-
24
- function toggleTriStateCheckboxState(customCheckbox, actualCheckbox, hiddenInput) {
25
- if (customCheckbox.classList.contains('checked')) {
26
- customCheckbox.classList.remove('checked');
27
- customCheckbox.classList.add('crossed');
28
- actualCheckbox.checked = false;
29
- hiddenInput.value = 'false';
30
- } else if (customCheckbox.classList.contains('crossed')) {
31
- customCheckbox.classList.remove('crossed');
32
- customCheckbox.classList.add('unchecked');
33
- hiddenInput.removeAttribute('name'); // Ensure the hidden input doesn't post any value
34
- hiddenInput.removeAttribute('value'); // Ensure the hidden input doesn't post any value
35
- } else {
36
- customCheckbox.classList.remove('unchecked');
37
- customCheckbox.classList.add('checked');
38
- actualCheckbox.checked = true;
39
- hiddenInput.value = 'true';
40
- hiddenInput.setAttribute('name', actualCheckbox.dataset.name); // Restore the name for posting
41
- }
42
- }
43
-
44
- function createTriStateCheckbox(checkboxElement, options) {
45
-
46
- //get the checkbox margins including browser default
47
- const checkboxStyle = window.getComputedStyle(checkboxElement);
48
- // Hide the original checkbox
49
- checkboxElement.style.display = 'none';
50
-
51
- // Create the custom checkbox div and set its initial state
52
- const tristateDom = document.createElement('div');
53
- tristateDom.classList.add('apx-tristate');
54
- if (options.size) {
55
- if (options.size.width) {
56
- tristateDom.style.width = options.size.width + 'px';
57
- }
58
- if (options.size.height) {
59
- tristateDom.style.height = options.size.height + 'px';
60
- }
61
- }
62
- if(options.classes) tristateDom.classList.add(...computeClasses(options.classes));
63
-
64
- tristateDom.style.margin = checkboxStyle.margin;
65
-
66
- if (checkboxElement.checked) {
67
- tristateDom.classList.add('checked');
68
- } else {
69
- tristateDom.classList.add('unchecked');
70
- }
71
-
72
- // Create the hidden input field
73
- const hiddenInput = document.createElement('input');
74
- hiddenInput.type = 'hidden';
75
- hiddenInput.name = checkboxElement.name; // Copy the name from the original checkbox
76
- checkboxElement.dataset.name = checkboxElement.name; // Store the name in a data attribute for later use
77
- checkboxElement.removeAttribute('name'); // Remove the name from the original checkbox to avoid double posting
78
-
79
- if (options.defaultState) setDefaultState(options, tristateDom, hiddenInput);
80
- else{
81
- // Set the initial value for the hidden input based on the checkbox's state
82
- if (checkboxElement.checked) {
83
- hiddenInput.value = 'true';
84
- } else {
85
- hiddenInput.name = ''; // Ensure the hidden input doesn't post any value
86
- hiddenInput.value = '';
87
- }
88
- }
89
-
90
- //handle accessibility, set the tabindex to the same as the checkbox
91
- tristateDom.tabIndex = checkboxElement.tabIndex;
92
-
93
- // Insert the hidden input inside the custom checkbox div
94
- tristateDom.appendChild(hiddenInput);
95
-
96
- // Insert it next to the actual checkbox
97
- checkboxElement.parentNode.insertBefore(tristateDom, checkboxElement.nextSibling);
98
-
99
- // Add event listener
100
- tristateDom.addEventListener('click', function (e) {
101
- toggleTriStateCheckboxState(tristateDom, checkboxElement, hiddenInput);
102
- bubbleEventsToOriginalCheckbox(options, checkboxElement, tristateDom, hiddenInput, e);
103
- });
104
- tristateDom.addEventListener('keyup', onkeyup.bind(null, checkboxElement));
105
-
106
- return [checkboxElement, tristateDom, hiddenInput];
107
- }
108
-
109
- function setDefaultState(options, tristateDom, hiddenInput) {
110
- //for all values possible for defaultState, set the initial state of the checkbox
111
- if (options.defaultState === 'checked') {
112
- tristateDom.classList.remove('unchecked');
113
- tristateDom.classList.add('checked');
114
- hiddenInput.value = 'true';
115
- }
116
- else if (options.defaultState === 'unchecked') {
117
- tristateDom.classList.remove('checked');
118
- tristateDom.classList.add('unchecked');
119
- hiddenInput.removeAttribute('name'); // Ensure the hidden input doesn't post any value
120
- hiddenInput.removeAttribute('value'); // Ensure the hidden input doesn't post any value
121
- }
122
- else if (options.defaultState === 'crossed') {
123
- tristateDom.classList.remove('checked');
124
- tristateDom.classList.add('crossed');
125
- hiddenInput.value = 'false';
126
- }
127
- }
128
-
129
- function computeClasses(classes) {
130
- let computedClasses = [];
131
- if(typeof classes === 'string') {
132
- //replace multiple white spaces with a single space
133
- classes = classes.replace(/\s\s+/g, ' ');
134
- if(classes.indexOf(' ') > -1)
135
- computedClasses = classes.split(' ');
136
- else if(classes.indexOf(',') > -1)
137
- computedClasses = classes.split(',');
138
- else
139
- computedClasses = [classes];
140
- }
141
- else if(Array.isArray(classes)) {
142
- computedClasses = classes;
143
- }
144
- return computedClasses;
145
- }
146
-
147
- function bubbleEventsToOriginalCheckbox(options, checkboxElement, tristateDom, hiddenInput, event) {
148
- //if options.callbacks.change is set, call it
149
- if(options.callbacks && options.callbacks.change && typeof options.callbacks.change === 'function') {
150
- options.callbacks.change(checkboxElement, tristateDom, hiddenInput);
151
- }
152
-
153
- if(!options.bubbleEvents) return;
154
-
155
- if(event.type === 'click') {
156
- checkboxElement.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }));
157
- }
158
- else if(event.type === 'keyup') {
159
- checkboxElement.dispatchEvent(new KeyboardEvent('keyup', { key: ' ', bubbles: true, cancelable: true }));
160
- }
161
- else if(event.type === 'change') {
162
- checkboxElement.dispatchEvent(new Event('change', { bubbles: true, cancelable: true }));
163
- }
164
- }
165
-
166
- function onkeyup(checkboxElement, event) {
167
- if (event.keyCode === 32) {
168
- bubbleEventsToOriginalCheckbox(checkboxElement, event);
169
- event.preventDefault();
170
- event.target.click();
171
- }
1
+ import './css/tristate.css';
2
+
3
+ /**
4
+ * Creates a tri-state checkbox for each checkbox element in the apx.elements array.
5
+ * @param {Array<HTMLElement>} apx - An array of HTMLElements which is returned by APX()
6
+ * @example
7
+ * // Call the tristate function on an array of checkbox elements
8
+ * apx.tristate();
9
+ */
10
+ export default function (apx) {
11
+ apx.tristate = function (options = {}){
12
+ apx.elements.forEach((element) => {
13
+ if (element.type === 'checkbox') {
14
+ const [originalCheckbox, tristateDom, hiddenInput] = createTriStateCheckbox(element, options);
15
+ originalCheckbox._apxTristate = { dom: tristateDom, hiddenInput };
16
+ if (options.callbacks && options.callbacks.after && typeof options.callbacks.after === 'function') {
17
+ options.callbacks.after(originalCheckbox, tristateDom, hiddenInput);
18
+ }
19
+ }
20
+ });
21
+ return {
22
+ setChildren(childrenApx) {
23
+ return setChildren(apx, childrenApx, options);
24
+ }
25
+ };
26
+ };
27
+ }
28
+
29
+ /**
30
+ * Link parent tristate(s) to child checkboxes. When exactly one parent: that parent gets
31
+ * indeterminate-dash automatically and shows dash when children are mixed; click parent to apply its state to all children.
32
+ * @param {Object} parentApx - APX instance whose elements are the parent checkbox(es)
33
+ * @param {Object} childrenApx - APX instance whose elements are the child checkbox(es)
34
+ * @param {Object} options - Options used for the parent (and applied to children when initializing them)
35
+ */
36
+ function setChildren(parentApx, childrenApx, options) {
37
+ const parents = parentApx.all().filter((el) => el.type === 'checkbox');
38
+ if (parents.length === 0) return;
39
+ if (parents.length > 1) {
40
+ console.warn('APX tristate setChildren: only the first parent is linked to all children.');
41
+ }
42
+ const parentCheckbox = parents[0];
43
+ const parentRef = parentCheckbox._apxTristate;
44
+ if (!parentRef) return;
45
+
46
+ const parentDom = parentRef.dom;
47
+ const parentHiddenInput = parentRef.hiddenInput;
48
+
49
+ // Automatically enable indeterminate (dash) on parent when it has children: dash shows only when children are mixed (--mixed)
50
+ parentDom.classList.add('apx-tristate--unchecked-indeterminate', 'apx-tristate--indeterminate-dash');
51
+
52
+ const childCheckboxes = childrenApx.all().filter((el) => el.type === 'checkbox');
53
+ const childRefs = [];
54
+
55
+ function syncParentFromChildren() {
56
+ if (childRefs.length === 0) return;
57
+ const states = childRefs.map((ref) => getTristateState(ref.dom));
58
+ const allChecked = states.every((s) => s === 'checked');
59
+ const allCrossed = states.every((s) => s === 'crossed');
60
+ const allUnchecked = states.every((s) => s === 'unchecked');
61
+ const mixed = !allChecked && !allCrossed && !allUnchecked;
62
+
63
+ parentDom.classList.remove('unchecked', 'checked', 'crossed');
64
+ if (mixed) parentDom.classList.add('apx-tristate--mixed');
65
+ else parentDom.classList.remove('apx-tristate--mixed');
66
+
67
+ if (allChecked) {
68
+ parentDom.classList.add('checked');
69
+ parentHiddenInput.value = 'true';
70
+ } else if (allCrossed) {
71
+ parentDom.classList.add('crossed');
72
+ parentHiddenInput.value = 'false';
73
+ } else {
74
+ parentDom.classList.add('unchecked');
75
+ parentHiddenInput.removeAttribute('name');
76
+ parentHiddenInput.removeAttribute('value');
77
+ }
78
+ }
79
+
80
+ function applyParentToChildren() {
81
+ const parentState = getTristateState(parentDom);
82
+ childRefs.forEach((ref) => setTristateState(ref.checkbox, ref.dom, ref.hiddenInput, parentState));
83
+ }
84
+
85
+ const childOptions = {
86
+ ...options,
87
+ callbacks: {
88
+ after(checkbox, dom, hiddenInput) {
89
+ childRefs.push({ checkbox, dom, hiddenInput });
90
+ },
91
+ change: syncParentFromChildren
92
+ }
93
+ };
94
+
95
+ childrenApx.tristate(childOptions);
96
+
97
+ syncParentFromChildren();
98
+ parentDom.addEventListener('click', () => setTimeout(applyParentToChildren, 0));
99
+ }
100
+
101
+ function getTristateState(dom) {
102
+ if (dom.classList.contains('checked')) return 'checked';
103
+ if (dom.classList.contains('crossed')) return 'crossed';
104
+ return 'unchecked';
105
+ }
106
+
107
+ function setTristateState(checkbox, dom, hiddenInput, state) {
108
+ dom.classList.remove('unchecked', 'checked', 'crossed');
109
+ dom.classList.add(state);
110
+ const name = checkbox.dataset.name || checkbox.getAttribute('data-name') || checkbox.name || '';
111
+ if (state === 'checked') {
112
+ hiddenInput.value = 'true';
113
+ if (name) hiddenInput.setAttribute('name', name);
114
+ checkbox.checked = true;
115
+ } else if (state === 'crossed') {
116
+ hiddenInput.value = 'false';
117
+ if (name) hiddenInput.setAttribute('name', name);
118
+ checkbox.checked = false;
119
+ } else {
120
+ hiddenInput.removeAttribute('name');
121
+ hiddenInput.removeAttribute('value');
122
+ checkbox.checked = false;
123
+ }
124
+ }
125
+
126
+ function toggleTriStateCheckboxState(customCheckbox, actualCheckbox, hiddenInput) {
127
+ const wasChecked = customCheckbox.classList.contains('checked');
128
+ const wasCrossed = customCheckbox.classList.contains('crossed');
129
+ customCheckbox.classList.remove('unchecked', 'checked', 'crossed');
130
+ if (wasChecked) {
131
+ customCheckbox.classList.add('crossed');
132
+ actualCheckbox.checked = false;
133
+ hiddenInput.value = 'false';
134
+ } else if (wasCrossed) {
135
+ customCheckbox.classList.add('unchecked');
136
+ hiddenInput.removeAttribute('name'); // Ensure the hidden input doesn't post any value
137
+ hiddenInput.removeAttribute('value'); // Ensure the hidden input doesn't post any value
138
+ } else {
139
+ customCheckbox.classList.add('checked');
140
+ actualCheckbox.checked = true;
141
+ hiddenInput.value = 'true';
142
+ hiddenInput.setAttribute('name', actualCheckbox.dataset.name); // Restore the name for posting
143
+ }
144
+ }
145
+
146
+ function createTriStateCheckbox(checkboxElement, options) {
147
+
148
+ //get the checkbox margins including browser default
149
+ const checkboxStyle = window.getComputedStyle(checkboxElement);
150
+ // Hide the original checkbox
151
+ checkboxElement.style.display = 'none';
152
+
153
+ // Create the custom checkbox div and set its initial state
154
+ const tristateDom = document.createElement('div');
155
+ tristateDom.classList.add('apx-tristate');
156
+ if (options.size) {
157
+ if (options.size.width != null) {
158
+ const w = options.size.width;
159
+ if (typeof w !== 'number' && typeof w !== 'string') {
160
+ throw new TypeError('tristate size.width must be a number (px) or a string (CSS length).');
161
+ }
162
+ tristateDom.style.width = typeof w === 'number' ? w + 'px' : String(w);
163
+ }
164
+ if (options.size.height != null) {
165
+ const h = options.size.height;
166
+ if (typeof h !== 'number' && typeof h !== 'string') {
167
+ throw new TypeError('tristate size.height must be a number (px) or a string (CSS length).');
168
+ }
169
+ tristateDom.style.height = typeof h === 'number' ? h + 'px' : String(h);
170
+ }
171
+ }
172
+ if(options.classes) tristateDom.classList.add(...computeClasses(options.classes));
173
+ const colors = resolveColors(options);
174
+ if (colors.tick != null) tristateDom.style.setProperty('--apx-tristate-tick-color', String(colors.tick));
175
+ if (colors.checked != null) tristateDom.style.setProperty('--apx-tristate-checked-color', String(colors.checked));
176
+ if (colors.crossed != null) tristateDom.style.setProperty('--apx-tristate-crossed-color', String(colors.crossed));
177
+
178
+ tristateDom.style.margin = checkboxStyle.margin;
179
+
180
+ if (checkboxElement.checked) {
181
+ tristateDom.classList.add('checked');
182
+ } else {
183
+ tristateDom.classList.add('unchecked');
184
+ }
185
+
186
+ // Create the hidden input field
187
+ const hiddenInput = document.createElement('input');
188
+ hiddenInput.type = 'hidden';
189
+ hiddenInput.name = checkboxElement.name; // Copy the name from the original checkbox
190
+ checkboxElement.dataset.name = checkboxElement.name; // Store the name in a data attribute for later use
191
+ checkboxElement.removeAttribute('name'); // Remove the name from the original checkbox to avoid double posting
192
+
193
+ if (options.defaultState) setDefaultState(options, tristateDom, hiddenInput);
194
+ else{
195
+ // Set the initial value for the hidden input based on the checkbox's state
196
+ if (checkboxElement.checked) {
197
+ hiddenInput.value = 'true';
198
+ } else {
199
+ hiddenInput.name = ''; // Ensure the hidden input doesn't post any value
200
+ hiddenInput.value = '';
201
+ }
202
+ }
203
+
204
+ //handle accessibility, set the tabindex to the same as the checkbox
205
+ tristateDom.tabIndex = checkboxElement.tabIndex;
206
+
207
+ // Insert the hidden input inside the custom checkbox div
208
+ tristateDom.appendChild(hiddenInput);
209
+
210
+ // Insert it next to the actual checkbox
211
+ checkboxElement.parentNode.insertBefore(tristateDom, checkboxElement.nextSibling);
212
+
213
+ // Add event listener
214
+ tristateDom.addEventListener('click', function (e) {
215
+ toggleTriStateCheckboxState(tristateDom, checkboxElement, hiddenInput);
216
+ bubbleEventsToOriginalCheckbox(options, checkboxElement, tristateDom, hiddenInput, e);
217
+ });
218
+ tristateDom.addEventListener('keyup', onkeyup.bind(null, checkboxElement));
219
+
220
+ return [checkboxElement, tristateDom, hiddenInput];
221
+ }
222
+
223
+ function setDefaultState(options, tristateDom, hiddenInput) {
224
+ // Ensure only one state class is set (uncheckedAppearance modifiers must not affect checked/crossed)
225
+ tristateDom.classList.remove('unchecked', 'checked', 'crossed');
226
+ if (options.defaultState === 'checked') {
227
+ tristateDom.classList.add('checked');
228
+ hiddenInput.value = 'true';
229
+ }
230
+ else if (options.defaultState === 'unchecked') {
231
+ tristateDom.classList.add('unchecked');
232
+ hiddenInput.removeAttribute('name'); // Ensure the hidden input doesn't post any value
233
+ hiddenInput.removeAttribute('value'); // Ensure the hidden input doesn't post any value
234
+ }
235
+ else if (options.defaultState === 'crossed') {
236
+ tristateDom.classList.add('crossed');
237
+ hiddenInput.value = 'false';
238
+ }
239
+ }
240
+
241
+ function computeClasses(classes) {
242
+ let computedClasses = [];
243
+ if(typeof classes === 'string') {
244
+ //replace multiple white spaces with a single space
245
+ classes = classes.replace(/\s\s+/g, ' ');
246
+ if(classes.indexOf(' ') > -1)
247
+ computedClasses = classes.split(' ');
248
+ else if(classes.indexOf(',') > -1)
249
+ computedClasses = classes.split(',');
250
+ else
251
+ computedClasses = [classes];
252
+ }
253
+ else if(Array.isArray(classes)) {
254
+ computedClasses = classes;
255
+ }
256
+ return computedClasses;
257
+ }
258
+
259
+ function resolveColors(options) {
260
+ const c = options.colors || {};
261
+ return {
262
+ tick: c.tick != null ? c.tick : options.tickColor,
263
+ checked: c.checked != null ? c.checked : options.checkedColor,
264
+ crossed: c.crossed != null ? c.crossed : options.crossedColor
265
+ };
266
+ }
267
+
268
+ function bubbleEventsToOriginalCheckbox(options, checkboxElement, tristateDom, hiddenInput, event) {
269
+ //if options.callbacks.change is set, call it
270
+ if(options.callbacks && options.callbacks.change && typeof options.callbacks.change === 'function') {
271
+ options.callbacks.change(checkboxElement, tristateDom, hiddenInput);
272
+ }
273
+
274
+ if(!options.bubbleEvents) return;
275
+
276
+ if(event.type === 'click') {
277
+ checkboxElement.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }));
278
+ }
279
+ else if(event.type === 'keyup') {
280
+ checkboxElement.dispatchEvent(new KeyboardEvent('keyup', { key: ' ', bubbles: true, cancelable: true }));
281
+ }
282
+ else if(event.type === 'change') {
283
+ checkboxElement.dispatchEvent(new Event('change', { bubbles: true, cancelable: true }));
284
+ }
285
+ }
286
+
287
+ function onkeyup(checkboxElement, event) {
288
+ if (event.keyCode === 32) {
289
+ bubbleEventsToOriginalCheckbox(checkboxElement, event);
290
+ event.preventDefault();
291
+ event.target.click();
292
+ }
172
293
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@appius-fr/apx",
3
- "version": "2.6.1",
3
+ "version": "2.7.0",
4
4
  "description": "Appius Extended JS - A powerful JavaScript extension library",
5
5
  "main": "dist/APX.prod.mjs",
6
6
  "module": "dist/APX.prod.mjs",
@@ -9,6 +9,10 @@
9
9
  "import": "./dist/APX.prod.mjs",
10
10
  "require": "./dist/APX.prod.mjs"
11
11
  },
12
+ "./source": {
13
+ "import": "./APX.mjs",
14
+ "require": "./APX.mjs"
15
+ },
12
16
  "./dev": {
13
17
  "import": "./dist/APX.dev.mjs",
14
18
  "require": "./dist/APX.dev.mjs"