@cahyo-dimas/freeday 3.13.0 → 3.14.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.
- package/CHANGELOG.md +63 -0
- package/COMPONENTS.md +45 -0
- package/README.id.md +1 -1
- package/README.md +1 -1
- package/adapters/react/components/FdyDrawer.tsx +4 -0
- package/adapters/react/index.d.ts +2 -0
- package/adapters/vue/components/FdyDrawer.vue +4 -0
- package/adapters/vue/index.d.ts +2 -0
- package/dist/freeday-app-shell.js +1 -0
- package/dist/freeday-autocomplete.js +1 -0
- package/dist/freeday-carousel.js +1 -0
- package/dist/freeday-cascade.js +1 -0
- package/dist/freeday-cfl.js +2 -0
- package/dist/freeday-chart.js +1 -0
- package/dist/freeday-chip.js +2 -0
- package/dist/freeday-datepicker.js +2 -0
- package/dist/freeday-datetime.js +1 -0
- package/dist/freeday-drawer.js +2 -0
- package/dist/freeday-form.js +1 -0
- package/dist/freeday-mask.js +2 -0
- package/dist/freeday-menu.js +1 -0
- package/dist/freeday-number.js +1 -0
- package/dist/freeday-rating.js +1 -0
- package/dist/freeday-search.js +1 -0
- package/dist/freeday-select.js +1 -0
- package/dist/freeday-slider.js +1 -0
- package/dist/freeday-sortable.js +222 -0
- package/dist/freeday-stepper.js +1 -0
- package/dist/freeday-table.js +1 -0
- package/dist/freeday-tabs.js +1 -0
- package/dist/freeday-timepicker.js +1 -0
- package/dist/freeday-tree.js +1 -0
- package/dist/freeday-unhydrated.js +9 -2
- package/dist/freeday-upload.js +1 -0
- package/dist/freeday.bundle.css +18 -0
- package/dist/freeday.css +18 -0
- package/dist/freeday.js +261 -2
- package/docs/agent-onboarding.md +2 -0
- package/docs/getting-started.md +1 -1
- package/docs/integrations.md +1 -0
- package/package.json +2 -2
- package/src/components/sortable.css +17 -0
package/dist/freeday-table.js
CHANGED
|
@@ -77,6 +77,7 @@
|
|
|
77
77
|
|
|
78
78
|
function initTable(root) {
|
|
79
79
|
if (root.dataset.fdyTableReady === '1') return;
|
|
80
|
+
if (root.closest('[data-fdy-ignore]')) return;
|
|
80
81
|
var table = root.querySelector('table.fdy-table');
|
|
81
82
|
var tbody = table && table.querySelector('tbody');
|
|
82
83
|
if (!table || !tbody) return;
|
package/dist/freeday-tabs.js
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
function initTabs(root) {
|
|
18
18
|
if (root.dataset.fdyTabsReady === '1') return;
|
|
19
|
+
if (root.closest('[data-fdy-ignore]')) return;
|
|
19
20
|
var tabs = Array.prototype.slice.call(root.querySelectorAll('[role="tab"]'));
|
|
20
21
|
if (tabs.length === 0) return;
|
|
21
22
|
root.dataset.fdyTabsReady = '1';
|
package/dist/freeday-tree.js
CHANGED
|
@@ -66,6 +66,7 @@
|
|
|
66
66
|
|
|
67
67
|
function initTree(tree) {
|
|
68
68
|
if (tree.dataset.fdyTreeReady === '1') return;
|
|
69
|
+
if (tree.closest('[data-fdy-ignore]')) return;
|
|
69
70
|
tree.dataset.fdyTreeReady = '1';
|
|
70
71
|
Array.prototype.forEach.call(tree.querySelectorAll('input[type="checkbox"]'), function (box) {
|
|
71
72
|
// A branch checkbox lives inside <summary>; keep its click from toggling the branch.
|
|
@@ -15,6 +15,11 @@
|
|
|
15
15
|
* namespace that owns it. A hook whose enhancer is not loaded is skipped: that is a different
|
|
16
16
|
* mistake, and this message would misname it. test/docs.test.mjs fails if an enhancer gains a
|
|
17
17
|
* ready flag this table does not list.
|
|
18
|
+
*
|
|
19
|
+
* `data-fdy-ignore` on an element or any ancestor is the sanctioned way out: every enhancer skips
|
|
20
|
+
* that subtree, and this stays quiet about it. It is for markup whose behaviour something else
|
|
21
|
+
* owns: the kit's own Vue/React FdyDrawer, and kit classes an app uses for their look
|
|
22
|
+
* alone (CATATAN-FREEDAY §14: a chip matrix the app drives itself).
|
|
18
23
|
*/
|
|
19
24
|
(function () {
|
|
20
25
|
'use strict';
|
|
@@ -43,6 +48,7 @@
|
|
|
43
48
|
['[data-fdy-search]', 'fdySearchReady', 'FreedaySearch'],
|
|
44
49
|
['[data-fdy-combo]', 'fdyComboReady', 'FreedayCombo'],
|
|
45
50
|
['[data-fdy-slider]', 'fdySliderReady', 'FreedaySlider'],
|
|
51
|
+
['[data-fdy-sortable]', 'fdySortableReady', 'FreedaySortable'],
|
|
46
52
|
['[data-fdy-stepper]', 'fdyStepperReady', 'FreedayStepper'],
|
|
47
53
|
['[data-fdy-table]', 'fdyTableReady', 'FreedayTable'],
|
|
48
54
|
['[data-fdy-tabs]', 'fdyTabsReady', 'FreedayTabs'],
|
|
@@ -59,12 +65,13 @@
|
|
|
59
65
|
var hook = HOOKS[i];
|
|
60
66
|
if (!window[hook[2]]) continue;
|
|
61
67
|
var el = e.target.closest(hook[0]);
|
|
62
|
-
if (!el || el.dataset[hook[1]] === '1' || warned.has(el)) continue;
|
|
68
|
+
if (!el || el.dataset[hook[1]] === '1' || warned.has(el) || el.closest('[data-fdy-ignore]')) continue;
|
|
63
69
|
warned.add(el);
|
|
64
70
|
console.warn('Freeday: this ' + hook[0] + ' was never enhanced, so it does nothing. Either it is '
|
|
65
71
|
+ 'outside every initAll() scope (Vue/React: inside the element given to useFreeday(); Blazor: '
|
|
66
72
|
+ 'FreedayBlazor.initAll after render; anything else: ' + hook[2] + '.initAll(el) once it is in '
|
|
67
|
-
+ 'the DOM), or its markup lacks a part the enhancer needs (see COMPONENTS.md).'
|
|
73
|
+
+ 'the DOM), or its markup lacks a part the enhancer needs (see COMPONENTS.md). If the app '
|
|
74
|
+
+ 'drives this element itself on purpose, mark it or an ancestor data-fdy-ignore.', el);
|
|
68
75
|
}
|
|
69
76
|
}
|
|
70
77
|
|
package/dist/freeday-upload.js
CHANGED
package/dist/freeday.bundle.css
CHANGED
|
@@ -2900,6 +2900,24 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
2900
2900
|
.fdy-slider__value{font-family:var(--font-mono);font-size:var(--text-sm);font-weight:var(--weight-semibold);color:var(--color-primary-strong);font-variant-numeric:tabular-nums;}
|
|
2901
2901
|
.fdy-slider__scale{display:flex;justify-content:space-between;font-size:var(--text-xs);color:var(--color-text-muted);font-variant-numeric:tabular-nums;}
|
|
2902
2902
|
|
|
2903
|
+
/* Freeday. Sortable list (freeday-sortable.js). The kit styles the handle and the drag states only;
|
|
2904
|
+
* the list itself is whatever it already was: .fdy-list, a table body, plain rows.
|
|
2905
|
+
*
|
|
2906
|
+
* The handle is a control-height square so a thumb can hold it on a phone, and `touch-action:none`
|
|
2907
|
+
* keeps that drag from scrolling the page. Its glyph is the only thing marking it as a control, so
|
|
2908
|
+
* it takes --color-text-muted, not --color-text-subtle: a non-text control still owes 3:1. */
|
|
2909
|
+
.fdy-sortable__handle{display:inline-flex;flex:none;align-items:center;justify-content:center;width:var(--control-h);height:var(--control-h);padding:0;border:0;border-radius:var(--radius-md);background:transparent;color:var(--color-text-muted);cursor:grab;touch-action:none;}
|
|
2910
|
+
.fdy-sortable__handle:hover{background:var(--color-surface-2);color:var(--color-text);}
|
|
2911
|
+
.fdy-sortable__handle:active{cursor:grabbing;}
|
|
2912
|
+
.fdy-sortable__handle[aria-disabled="true"]{cursor:not-allowed;opacity:.4;}
|
|
2913
|
+
.fdy-sortable__handle[aria-disabled="true"]:hover{background:transparent;color:var(--color-text-muted);}
|
|
2914
|
+
.fdy-sortable.is-sorting{-webkit-user-select:none;user-select:none;}
|
|
2915
|
+
.fdy-sortable > .is-dragging{opacity:.45;}
|
|
2916
|
+
/* The landing line. On a table row it is drawn on the cells: a box-shadow on a <tr> is not painted
|
|
2917
|
+
* by every engine the kit supports. */
|
|
2918
|
+
.fdy-sortable > :not(tr).is-drop-before,.fdy-sortable > tr.is-drop-before > *{box-shadow:inset 0 var(--focus-ring-width) 0 var(--color-primary);}
|
|
2919
|
+
.fdy-sortable > :not(tr).is-drop-after,.fdy-sortable > tr.is-drop-after > *{box-shadow:inset 0 calc(var(--focus-ring-width) * -1) 0 var(--color-primary);}
|
|
2920
|
+
|
|
2903
2921
|
/* Freeday. Spinner */
|
|
2904
2922
|
.fdy-spinner{display:inline-block;width:1.5rem;height:1.5rem;border:2.5px solid var(--color-border-strong);border-top-color:var(--color-primary);border-radius:var(--radius-full);animation:fdy-spin .7s linear infinite;}
|
|
2905
2923
|
.fdy-spinner--sm{width:1rem;height:1rem;border-width:2px;}
|
package/dist/freeday.css
CHANGED
|
@@ -1723,6 +1723,24 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1723
1723
|
.fdy-slider__value{font-family:var(--font-mono);font-size:var(--text-sm);font-weight:var(--weight-semibold);color:var(--color-primary-strong);font-variant-numeric:tabular-nums;}
|
|
1724
1724
|
.fdy-slider__scale{display:flex;justify-content:space-between;font-size:var(--text-xs);color:var(--color-text-muted);font-variant-numeric:tabular-nums;}
|
|
1725
1725
|
|
|
1726
|
+
/* Freeday. Sortable list (freeday-sortable.js). The kit styles the handle and the drag states only;
|
|
1727
|
+
* the list itself is whatever it already was: .fdy-list, a table body, plain rows.
|
|
1728
|
+
*
|
|
1729
|
+
* The handle is a control-height square so a thumb can hold it on a phone, and `touch-action:none`
|
|
1730
|
+
* keeps that drag from scrolling the page. Its glyph is the only thing marking it as a control, so
|
|
1731
|
+
* it takes --color-text-muted, not --color-text-subtle: a non-text control still owes 3:1. */
|
|
1732
|
+
.fdy-sortable__handle{display:inline-flex;flex:none;align-items:center;justify-content:center;width:var(--control-h);height:var(--control-h);padding:0;border:0;border-radius:var(--radius-md);background:transparent;color:var(--color-text-muted);cursor:grab;touch-action:none;}
|
|
1733
|
+
.fdy-sortable__handle:hover{background:var(--color-surface-2);color:var(--color-text);}
|
|
1734
|
+
.fdy-sortable__handle:active{cursor:grabbing;}
|
|
1735
|
+
.fdy-sortable__handle[aria-disabled="true"]{cursor:not-allowed;opacity:.4;}
|
|
1736
|
+
.fdy-sortable__handle[aria-disabled="true"]:hover{background:transparent;color:var(--color-text-muted);}
|
|
1737
|
+
.fdy-sortable.is-sorting{-webkit-user-select:none;user-select:none;}
|
|
1738
|
+
.fdy-sortable > .is-dragging{opacity:.45;}
|
|
1739
|
+
/* The landing line. On a table row it is drawn on the cells: a box-shadow on a <tr> is not painted
|
|
1740
|
+
* by every engine the kit supports. */
|
|
1741
|
+
.fdy-sortable > :not(tr).is-drop-before,.fdy-sortable > tr.is-drop-before > *{box-shadow:inset 0 var(--focus-ring-width) 0 var(--color-primary);}
|
|
1742
|
+
.fdy-sortable > :not(tr).is-drop-after,.fdy-sortable > tr.is-drop-after > *{box-shadow:inset 0 calc(var(--focus-ring-width) * -1) 0 var(--color-primary);}
|
|
1743
|
+
|
|
1726
1744
|
/* Freeday. Spinner */
|
|
1727
1745
|
.fdy-spinner{display:inline-block;width:1.5rem;height:1.5rem;border:2.5px solid var(--color-border-strong);border-top-color:var(--color-primary);border-radius:var(--radius-full);animation:fdy-spin .7s linear infinite;}
|
|
1728
1746
|
.fdy-spinner--sm{width:1rem;height:1rem;border-width:2px;}
|
package/dist/freeday.js
CHANGED
|
@@ -48,6 +48,7 @@
|
|
|
48
48
|
|
|
49
49
|
function initShell(app) {
|
|
50
50
|
if (app.dataset.fdyAppShellReady === '1') return;
|
|
51
|
+
if (app.closest('[data-fdy-ignore]')) return;
|
|
51
52
|
app.dataset.fdyAppShellReady = '1';
|
|
52
53
|
|
|
53
54
|
var toggle = app.querySelector('.fdy-app__navtoggle');
|
|
@@ -246,6 +247,7 @@
|
|
|
246
247
|
|
|
247
248
|
function initAutocomplete(root) {
|
|
248
249
|
if (root.dataset.fdyAcReady === '1') return;
|
|
250
|
+
if (root.closest('[data-fdy-ignore]')) return;
|
|
249
251
|
root.dataset.fdyAcReady = '1';
|
|
250
252
|
|
|
251
253
|
var input = root.querySelector('input[role="combobox"]') || root.querySelector('input');
|
|
@@ -660,6 +662,7 @@
|
|
|
660
662
|
|
|
661
663
|
function initCarousel(root) {
|
|
662
664
|
if (root.dataset.fdyCarouselReady === '1') return;
|
|
665
|
+
if (root.closest('[data-fdy-ignore]')) return;
|
|
663
666
|
root.dataset.fdyCarouselReady = '1';
|
|
664
667
|
|
|
665
668
|
var viewport = root.querySelector('.fdy-carousel__viewport');
|
|
@@ -840,6 +843,7 @@
|
|
|
840
843
|
|
|
841
844
|
function initCascade(wrap) {
|
|
842
845
|
if (wrap.dataset.fdyCascadeReady === '1') return;
|
|
846
|
+
if (wrap.closest('[data-fdy-ignore]')) return;
|
|
843
847
|
wrap.dataset.fdyCascadeReady = '1';
|
|
844
848
|
wrap.classList.add('fdy-cascade');
|
|
845
849
|
|
|
@@ -1305,6 +1309,7 @@
|
|
|
1305
1309
|
|
|
1306
1310
|
function bindGroup(group) {
|
|
1307
1311
|
if (group.dataset.fdyCflBound === '1') return;
|
|
1312
|
+
if (group.closest('[data-fdy-ignore]')) return;
|
|
1308
1313
|
var dialog = document.getElementById(group.getAttribute('data-fdy-cfl'));
|
|
1309
1314
|
if (!dialog) return;
|
|
1310
1315
|
initDialog(dialog);
|
|
@@ -1316,6 +1321,7 @@
|
|
|
1316
1321
|
|
|
1317
1322
|
function bindOpener(opener) {
|
|
1318
1323
|
if (opener.dataset.fdyCflBound === '1') return;
|
|
1324
|
+
if (opener.closest('[data-fdy-ignore]')) return;
|
|
1319
1325
|
var dialog = document.getElementById(opener.getAttribute('data-fdy-cfl-open'));
|
|
1320
1326
|
if (!dialog) return;
|
|
1321
1327
|
initDialog(dialog);
|
|
@@ -1834,6 +1840,7 @@
|
|
|
1834
1840
|
// Auto-init renders once; explicit update() re-renders after data changes.
|
|
1835
1841
|
function initChart(el) {
|
|
1836
1842
|
if (el.dataset.fdyChartReady === '1') return;
|
|
1843
|
+
if (el.closest('[data-fdy-ignore]')) return;
|
|
1837
1844
|
el.dataset.fdyChartReady = '1';
|
|
1838
1845
|
renderChart(el);
|
|
1839
1846
|
}
|
|
@@ -1874,6 +1881,7 @@
|
|
|
1874
1881
|
|
|
1875
1882
|
function initRemove(btn) {
|
|
1876
1883
|
if (btn.dataset.fdyChipRmReady === '1') return;
|
|
1884
|
+
if (btn.closest('[data-fdy-ignore]')) return;
|
|
1877
1885
|
btn.dataset.fdyChipRmReady = '1';
|
|
1878
1886
|
btn.addEventListener('click', function () {
|
|
1879
1887
|
var chip = btn.closest('.fdy-chip');
|
|
@@ -1897,6 +1905,7 @@
|
|
|
1897
1905
|
|
|
1898
1906
|
function initGroup(group) {
|
|
1899
1907
|
if (group.dataset.fdyChipsReady === '1') return;
|
|
1908
|
+
if (group.closest('[data-fdy-ignore]')) return;
|
|
1900
1909
|
group.dataset.fdyChipsReady = '1';
|
|
1901
1910
|
var single = group.hasAttribute('data-single');
|
|
1902
1911
|
if (!group.getAttribute('role')) group.setAttribute('role', 'group');
|
|
@@ -2228,6 +2237,7 @@
|
|
|
2228
2237
|
|
|
2229
2238
|
function initPicker(wrap) {
|
|
2230
2239
|
if (wrap.dataset.fdyDpReady === '1') return wrap._fdyDp || null;
|
|
2240
|
+
if (wrap.closest('[data-fdy-ignore]')) return null;
|
|
2231
2241
|
wrap.dataset.fdyDpReady = '1';
|
|
2232
2242
|
wrap.classList.add('fdy-datepicker');
|
|
2233
2243
|
|
|
@@ -2757,6 +2767,7 @@
|
|
|
2757
2767
|
|
|
2758
2768
|
function initRange(root) {
|
|
2759
2769
|
if (root.dataset.fdyDrReady === '1') return;
|
|
2770
|
+
if (root.closest('[data-fdy-ignore]')) return;
|
|
2760
2771
|
root.dataset.fdyDrReady = '1';
|
|
2761
2772
|
var fromEl = root.querySelector('[data-fdy-datepicker][data-role="from"]');
|
|
2762
2773
|
var toEl = root.querySelector('[data-fdy-datepicker][data-role="to"]');
|
|
@@ -2846,6 +2857,7 @@
|
|
|
2846
2857
|
|
|
2847
2858
|
function initDatetime(wrap) {
|
|
2848
2859
|
if (wrap.dataset.fdyDtReady === '1') return;
|
|
2860
|
+
if (wrap.closest('[data-fdy-ignore]')) return;
|
|
2849
2861
|
wrap.dataset.fdyDtReady = '1';
|
|
2850
2862
|
wrap.classList.add('fdy-datetimepicker');
|
|
2851
2863
|
|
|
@@ -2924,6 +2936,7 @@
|
|
|
2924
2936
|
|
|
2925
2937
|
function initDrawer(dialog) {
|
|
2926
2938
|
if (dialog.dataset.fdyDrawerReady === '1') return;
|
|
2939
|
+
if (dialog.closest('[data-fdy-ignore]')) return;
|
|
2927
2940
|
dialog.dataset.fdyDrawerReady = '1';
|
|
2928
2941
|
// Click on the backdrop (target is the dialog box itself) closes it.
|
|
2929
2942
|
dialog.addEventListener('click', function (e) { if (e.target === dialog) dialog.close(); });
|
|
@@ -2934,6 +2947,7 @@
|
|
|
2934
2947
|
|
|
2935
2948
|
function bindTrigger(btn) {
|
|
2936
2949
|
if (btn.dataset.fdyDrawerBound === '1') return;
|
|
2950
|
+
if (btn.closest('[data-fdy-ignore]')) return;
|
|
2937
2951
|
var dialog = document.getElementById(btn.getAttribute('data-fdy-drawer'));
|
|
2938
2952
|
if (!dialog) return;
|
|
2939
2953
|
initDrawer(dialog);
|
|
@@ -3151,6 +3165,7 @@
|
|
|
3151
3165
|
|
|
3152
3166
|
function initForm(form) {
|
|
3153
3167
|
if (form.dataset.fdyFormReady === '1') return;
|
|
3168
|
+
if (form.closest('[data-fdy-ignore]')) return;
|
|
3154
3169
|
form.dataset.fdyFormReady = '1';
|
|
3155
3170
|
form.setAttribute('novalidate', '');
|
|
3156
3171
|
var eager = form.hasAttribute('data-fdy-eager');
|
|
@@ -3335,6 +3350,7 @@
|
|
|
3335
3350
|
|
|
3336
3351
|
function initMask(el) {
|
|
3337
3352
|
if (el.dataset.fdyMaskReady === '1') return;
|
|
3353
|
+
if (el.closest('[data-fdy-ignore]')) return;
|
|
3338
3354
|
el.dataset.fdyMaskReady = '1';
|
|
3339
3355
|
var pattern = el.getAttribute('data-fdy-mask') || '';
|
|
3340
3356
|
if (!pattern) return;
|
|
@@ -3357,6 +3373,7 @@
|
|
|
3357
3373
|
|
|
3358
3374
|
function initReveal(input) {
|
|
3359
3375
|
if (input.dataset.fdyPwReady === '1') return;
|
|
3376
|
+
if (input.closest('[data-fdy-ignore]')) return;
|
|
3360
3377
|
input.dataset.fdyPwReady = '1';
|
|
3361
3378
|
|
|
3362
3379
|
var group = document.createElement('span');
|
|
@@ -3420,6 +3437,7 @@
|
|
|
3420
3437
|
|
|
3421
3438
|
function initMenu(wrap) {
|
|
3422
3439
|
if (wrap.dataset.fdyMenuReady === '1') return;
|
|
3440
|
+
if (wrap.closest('[data-fdy-ignore]')) return;
|
|
3423
3441
|
wrap.dataset.fdyMenuReady = '1';
|
|
3424
3442
|
var trigger = wrap.querySelector('[aria-haspopup="menu"]');
|
|
3425
3443
|
var menu = wrap.querySelector('.fdy-menu, [role="menu"]');
|
|
@@ -3539,6 +3557,7 @@
|
|
|
3539
3557
|
|
|
3540
3558
|
function initNumber(root) {
|
|
3541
3559
|
if (root.dataset.fdyNumberReady === '1') return;
|
|
3560
|
+
if (root.closest('[data-fdy-ignore]')) return;
|
|
3542
3561
|
|
|
3543
3562
|
var input = root.querySelector('input[type="number"]');
|
|
3544
3563
|
var buttons = Array.prototype.slice.call(root.querySelectorAll('[data-fdy-number-step]'));
|
|
@@ -3717,6 +3736,7 @@
|
|
|
3717
3736
|
|
|
3718
3737
|
function initRating(root) {
|
|
3719
3738
|
if (root.dataset.fdyRatingReady === '1') return;
|
|
3739
|
+
if (root.closest('[data-fdy-ignore]')) return;
|
|
3720
3740
|
root.dataset.fdyRatingReady = '1';
|
|
3721
3741
|
|
|
3722
3742
|
var stars = Array.prototype.slice.call(root.querySelectorAll('.fdy-rating__star'));
|
|
@@ -3787,6 +3807,7 @@
|
|
|
3787
3807
|
|
|
3788
3808
|
function initSearch(root) {
|
|
3789
3809
|
if (root.dataset.fdySearchReady === '1') return;
|
|
3810
|
+
if (root.closest('[data-fdy-ignore]')) return;
|
|
3790
3811
|
var input = root.querySelector('input');
|
|
3791
3812
|
if (!input) return;
|
|
3792
3813
|
root.dataset.fdySearchReady = '1';
|
|
@@ -3873,6 +3894,7 @@
|
|
|
3873
3894
|
|
|
3874
3895
|
function initCombo(root) {
|
|
3875
3896
|
if (root.dataset.fdyComboReady === '1') return; // idempotent
|
|
3897
|
+
if (root.closest('[data-fdy-ignore]')) return;
|
|
3876
3898
|
var button = root.querySelector('.fdy-combo__button');
|
|
3877
3899
|
var listbox = root.querySelector('.fdy-combo__listbox');
|
|
3878
3900
|
var valueEl = root.querySelector('.fdy-combo__value');
|
|
@@ -4085,6 +4107,7 @@
|
|
|
4085
4107
|
|
|
4086
4108
|
function initSlider(input) {
|
|
4087
4109
|
if (input.dataset.fdySliderReady === '1') return;
|
|
4110
|
+
if (input.closest('[data-fdy-ignore]')) return;
|
|
4088
4111
|
input.dataset.fdySliderReady = '1';
|
|
4089
4112
|
var out = input.id ? document.querySelector('output[for="' + input.id + '"]') : null;
|
|
4090
4113
|
if (!out) return;
|
|
@@ -4109,6 +4132,229 @@
|
|
|
4109
4132
|
window.FreedaySlider = { init: initSlider, initAll: initAll };
|
|
4110
4133
|
})();
|
|
4111
4134
|
|
|
4135
|
+
/* Freeday, sortable list enhancer (optional, zero-dependency).
|
|
4136
|
+
* Reorder the children of a list by dragging a handle, or by focusing it and pressing ↑ / ↓. The
|
|
4137
|
+
* keyboard path is not an extra: drag-only reordering locks out every keyboard and screen-reader
|
|
4138
|
+
* user from a settings screen, and that path is the part each app used to write for itself
|
|
4139
|
+
* (CATATAN-FREEDAY §15).
|
|
4140
|
+
*
|
|
4141
|
+
* Markup contract:
|
|
4142
|
+
* <ul class="fdy-sortable" data-fdy-sortable> (or a <tbody>, or a <div>)
|
|
4143
|
+
* <li data-key="ketua" data-label="Ketua">
|
|
4144
|
+
* <button class="fdy-sortable__handle" type="button">…icon…</button> Ketua
|
|
4145
|
+
* </li>
|
|
4146
|
+
* <li data-key="sistem" data-fdy-sort-fixed>…</li> (never moves, and nothing passes it)
|
|
4147
|
+
* </ul>
|
|
4148
|
+
* The items are the container's element children. `data-key` names each in the event; `data-label`
|
|
4149
|
+
* is what the handle's default aria-label and the announcement call it (the item text otherwise).
|
|
4150
|
+
* `data-disabled` on the container switches reordering off, e.g. while the list is filtered, and
|
|
4151
|
+
* the handles follow it through aria-disabled.
|
|
4152
|
+
*
|
|
4153
|
+
* Emits a bubbling, CANCELABLE "fdy-sort-change" on the container, detail {key, from, to, order},
|
|
4154
|
+
* `order` being every item's key in the new order. Not cancelled, the kit moves the element, which
|
|
4155
|
+
* is the whole job on a static page. A framework that renders the list from state cancels it and
|
|
4156
|
+
* reorders the state instead, because the DOM is the framework's and an element moved behind its
|
|
4157
|
+
* back leaves the two disagreeing about where each row is. Either way the kit announces the move
|
|
4158
|
+
* and, after a keyboard step, puts focus back on the moved item's handle (looked up by data-key,
|
|
4159
|
+
* since a framework re-render may have replaced the element).
|
|
4160
|
+
*
|
|
4161
|
+
* The drag shows where the item will land (is-drop-before / is-drop-after on the item it lands
|
|
4162
|
+
* against) and moves nothing until the pointer is released; Escape cancels it. Exposes
|
|
4163
|
+
* window.FreedaySortable.
|
|
4164
|
+
*/
|
|
4165
|
+
(function () {
|
|
4166
|
+
'use strict';
|
|
4167
|
+
|
|
4168
|
+
var TEXT = {
|
|
4169
|
+
handle: 'Move {label}. Drag, or press the up and down arrow keys.',
|
|
4170
|
+
moved: '{label} moved to position {position} of {total}.'
|
|
4171
|
+
};
|
|
4172
|
+
/* Kebab-cased for the lookup, see the note in freeday-upload.js. */
|
|
4173
|
+
function textAttr(root, key) {
|
|
4174
|
+
if (!root || !root.getAttribute) return null;
|
|
4175
|
+
var kebab = root.getAttribute('data-fdy-text-' + key.replace(/[A-Z]/g, function (c) { return '-' + c.toLowerCase(); }));
|
|
4176
|
+
return kebab != null && kebab !== '' ? kebab : root.getAttribute('data-fdy-text-' + key);
|
|
4177
|
+
}
|
|
4178
|
+
function textOf(root, key, vars) {
|
|
4179
|
+
var custom = textAttr(root, key);
|
|
4180
|
+
var s = custom != null && custom !== '' ? custom : TEXT[key];
|
|
4181
|
+
for (var k in vars) if (Object.prototype.hasOwnProperty.call(vars, k)) s = s.split('{' + k + '}').join(vars[k]);
|
|
4182
|
+
return s;
|
|
4183
|
+
}
|
|
4184
|
+
|
|
4185
|
+
var HANDLE = '.fdy-sortable__handle';
|
|
4186
|
+
|
|
4187
|
+
var live = null;
|
|
4188
|
+
function announce(message) {
|
|
4189
|
+
if (live === null) {
|
|
4190
|
+
live = document.createElement('div');
|
|
4191
|
+
live.className = 'fdy-visually-hidden';
|
|
4192
|
+
live.setAttribute('aria-live', 'polite');
|
|
4193
|
+
document.body.appendChild(live);
|
|
4194
|
+
}
|
|
4195
|
+
/* The same sentence twice in a row is no change, and a live region announces changes. */
|
|
4196
|
+
live.textContent = live.textContent === message ? message + ' ' : message;
|
|
4197
|
+
}
|
|
4198
|
+
|
|
4199
|
+
function items(root) {
|
|
4200
|
+
return Array.prototype.slice.call(root.children);
|
|
4201
|
+
}
|
|
4202
|
+
function keyOf(item, index) {
|
|
4203
|
+
var key = item.getAttribute('data-key');
|
|
4204
|
+
return key !== null ? key : String(index);
|
|
4205
|
+
}
|
|
4206
|
+
function labelOf(item) {
|
|
4207
|
+
var label = item.getAttribute('data-label');
|
|
4208
|
+
return label !== null ? label : (item.textContent || '').replace(/\s+/g, ' ').trim();
|
|
4209
|
+
}
|
|
4210
|
+
function disabled(root) {
|
|
4211
|
+
return root.hasAttribute('data-disabled');
|
|
4212
|
+
}
|
|
4213
|
+
function itemOf(root, node) {
|
|
4214
|
+
for (var n = node; n && n !== root; n = n.parentElement) {
|
|
4215
|
+
if (n.parentElement === root) return n;
|
|
4216
|
+
}
|
|
4217
|
+
return null;
|
|
4218
|
+
}
|
|
4219
|
+
|
|
4220
|
+
/* A move is allowed only if it shifts no fixed item, which is the same as saying the item does
|
|
4221
|
+
not start fixed and does not cross one on its way. */
|
|
4222
|
+
function allowed(list, from, to) {
|
|
4223
|
+
if (from === to || to < 0 || to >= list.length) return false;
|
|
4224
|
+
var lo = Math.min(from, to), hi = Math.max(from, to);
|
|
4225
|
+
for (var i = lo; i <= hi; i++) if (list[i].hasAttribute('data-fdy-sort-fixed')) return false;
|
|
4226
|
+
return true;
|
|
4227
|
+
}
|
|
4228
|
+
|
|
4229
|
+
function syncHandles(root) {
|
|
4230
|
+
var off = disabled(root) ? 'true' : 'false';
|
|
4231
|
+
items(root).forEach(function (item) {
|
|
4232
|
+
var handle = item.querySelector(HANDLE);
|
|
4233
|
+
if (!handle) return;
|
|
4234
|
+
if (handle.getAttribute('aria-disabled') !== off) handle.setAttribute('aria-disabled', off);
|
|
4235
|
+
if (!handle.hasAttribute('aria-label')) handle.setAttribute('aria-label', textOf(root, 'handle', { label: labelOf(item) }));
|
|
4236
|
+
});
|
|
4237
|
+
}
|
|
4238
|
+
|
|
4239
|
+
function commit(root, from, to, refocus) {
|
|
4240
|
+
var list = items(root);
|
|
4241
|
+
var item = list[from];
|
|
4242
|
+
var key = keyOf(item, from);
|
|
4243
|
+
var keys = list.map(keyOf);
|
|
4244
|
+
keys.splice(to, 0, keys.splice(from, 1)[0]);
|
|
4245
|
+
var ev = new CustomEvent('fdy-sort-change', {
|
|
4246
|
+
bubbles: true,
|
|
4247
|
+
cancelable: true,
|
|
4248
|
+
detail: { key: key, from: from, to: to, order: keys }
|
|
4249
|
+
});
|
|
4250
|
+
root.dispatchEvent(ev);
|
|
4251
|
+
if (!ev.defaultPrevented) root.insertBefore(item, to > from ? list[to].nextSibling : list[to]);
|
|
4252
|
+
announce(textOf(root, 'moved', { label: labelOf(item), position: String(to + 1), total: String(list.length) }));
|
|
4253
|
+
if (!refocus) return;
|
|
4254
|
+
/* After the app's own render, which a framework runs after this handler returns. Moving a focused
|
|
4255
|
+
element drops its focus, and a keyed re-render may have replaced the element outright. */
|
|
4256
|
+
setTimeout(function () {
|
|
4257
|
+
var now = items(root);
|
|
4258
|
+
var moved = item.parentElement === root ? item : now.filter(function (el, i) { return keyOf(el, i) === key; })[0];
|
|
4259
|
+
var handle = moved ? moved.querySelector(HANDLE) : null;
|
|
4260
|
+
if (handle && document.activeElement !== handle) handle.focus();
|
|
4261
|
+
}, 0);
|
|
4262
|
+
}
|
|
4263
|
+
|
|
4264
|
+
function initSortable(root) {
|
|
4265
|
+
if (root.dataset.fdySortableReady === '1') return;
|
|
4266
|
+
if (root.closest('[data-fdy-ignore]')) return;
|
|
4267
|
+
root.dataset.fdySortableReady = '1';
|
|
4268
|
+
syncHandles(root);
|
|
4269
|
+
/* Rows a framework adds later get their aria-label, and data-disabled reaches every handle. */
|
|
4270
|
+
new MutationObserver(function () { syncHandles(root); })
|
|
4271
|
+
.observe(root, { childList: true, attributes: true, attributeFilter: ['data-disabled'] });
|
|
4272
|
+
|
|
4273
|
+
root.addEventListener('keydown', function (e) {
|
|
4274
|
+
var handle = e.target.closest ? e.target.closest(HANDLE) : null;
|
|
4275
|
+
if (!handle || !root.contains(handle) || disabled(root)) return;
|
|
4276
|
+
if (e.key !== 'ArrowUp' && e.key !== 'ArrowDown') return;
|
|
4277
|
+
e.preventDefault();
|
|
4278
|
+
var list = items(root);
|
|
4279
|
+
var from = list.indexOf(itemOf(root, handle));
|
|
4280
|
+
var to = from + (e.key === 'ArrowUp' ? -1 : 1);
|
|
4281
|
+
if (from !== -1 && allowed(list, from, to)) commit(root, from, to, true);
|
|
4282
|
+
});
|
|
4283
|
+
|
|
4284
|
+
var drag = null;
|
|
4285
|
+
|
|
4286
|
+
function clearMarks() {
|
|
4287
|
+
items(root).forEach(function (el) { el.classList.remove('is-drop-before', 'is-drop-after'); });
|
|
4288
|
+
}
|
|
4289
|
+
function end(apply) {
|
|
4290
|
+
if (drag === null) return;
|
|
4291
|
+
var d = drag;
|
|
4292
|
+
drag = null;
|
|
4293
|
+
document.removeEventListener('keydown', onEscape, true);
|
|
4294
|
+
clearMarks();
|
|
4295
|
+
d.item.classList.remove('is-dragging');
|
|
4296
|
+
root.classList.remove('is-sorting');
|
|
4297
|
+
if (apply && d.started && d.to !== null) commit(root, d.from, d.to, false);
|
|
4298
|
+
}
|
|
4299
|
+
function onEscape(e) {
|
|
4300
|
+
if (e.key === 'Escape') { e.preventDefault(); end(false); }
|
|
4301
|
+
}
|
|
4302
|
+
|
|
4303
|
+
root.addEventListener('pointerdown', function (e) {
|
|
4304
|
+
var handle = e.target.closest ? e.target.closest(HANDLE) : null;
|
|
4305
|
+
if (!handle || !root.contains(handle) || disabled(root) || e.button !== 0) return;
|
|
4306
|
+
var item = itemOf(root, handle);
|
|
4307
|
+
if (!item) return;
|
|
4308
|
+
e.preventDefault();
|
|
4309
|
+
handle.focus();
|
|
4310
|
+
handle.setPointerCapture(e.pointerId);
|
|
4311
|
+
drag = { item: item, from: items(root).indexOf(item), started: false, to: null };
|
|
4312
|
+
document.addEventListener('keydown', onEscape, true);
|
|
4313
|
+
});
|
|
4314
|
+
|
|
4315
|
+
root.addEventListener('pointermove', function (e) {
|
|
4316
|
+
if (drag === null) return;
|
|
4317
|
+
/* No travel threshold: a press that never leaves its own row resolves to to === from, which
|
|
4318
|
+
allowed() refuses, so a click on the handle cannot reorder anything. */
|
|
4319
|
+
if (!drag.started) {
|
|
4320
|
+
drag.started = true;
|
|
4321
|
+
drag.item.classList.add('is-dragging');
|
|
4322
|
+
root.classList.add('is-sorting');
|
|
4323
|
+
}
|
|
4324
|
+
clearMarks();
|
|
4325
|
+
drag.to = null;
|
|
4326
|
+
var list = items(root);
|
|
4327
|
+
var over = itemOf(root, document.elementFromPoint(e.clientX, e.clientY));
|
|
4328
|
+
if (!over) return;
|
|
4329
|
+
var rect = over.getBoundingClientRect();
|
|
4330
|
+
var after = e.clientY > rect.top + rect.height / 2;
|
|
4331
|
+
var slot = list.indexOf(over) + (after ? 1 : 0);
|
|
4332
|
+
var to = slot > drag.from ? slot - 1 : slot;
|
|
4333
|
+
if (!allowed(list, drag.from, to)) return;
|
|
4334
|
+
drag.to = to;
|
|
4335
|
+
over.classList.add(after ? 'is-drop-after' : 'is-drop-before');
|
|
4336
|
+
});
|
|
4337
|
+
|
|
4338
|
+
root.addEventListener('pointerup', function () { end(true); });
|
|
4339
|
+
root.addEventListener('pointercancel', function () { end(false); });
|
|
4340
|
+
}
|
|
4341
|
+
|
|
4342
|
+
function initAll(context) {
|
|
4343
|
+
var root = context || document;
|
|
4344
|
+
/* root included: querySelectorAll never matches its own root, and a framework ref often sits ON the widget. */
|
|
4345
|
+
if (root.matches && root.matches('[data-fdy-sortable]')) initSortable(root);
|
|
4346
|
+
Array.prototype.forEach.call(root.querySelectorAll('[data-fdy-sortable]'), initSortable);
|
|
4347
|
+
}
|
|
4348
|
+
|
|
4349
|
+
if (document.readyState === 'loading') {
|
|
4350
|
+
document.addEventListener('DOMContentLoaded', function () { initAll(); });
|
|
4351
|
+
} else {
|
|
4352
|
+
initAll();
|
|
4353
|
+
}
|
|
4354
|
+
|
|
4355
|
+
window.FreedaySortable = { init: initSortable, initAll: initAll };
|
|
4356
|
+
})();
|
|
4357
|
+
|
|
4112
4358
|
/* Freeday, stepper/wizard enhancer (optional, zero-dependency).
|
|
4113
4359
|
* Drives a step header + panels: prev/next navigation, completed/active state, and
|
|
4114
4360
|
* clicking a reached step to revisit it (linear, can't skip ahead). Auto-inits
|
|
@@ -4160,6 +4406,7 @@
|
|
|
4160
4406
|
|
|
4161
4407
|
function initStepper(root) {
|
|
4162
4408
|
if (root.dataset.fdyStepperReady === '1') return;
|
|
4409
|
+
if (root.closest('[data-fdy-ignore]')) return;
|
|
4163
4410
|
root.dataset.fdyStepperReady = '1';
|
|
4164
4411
|
|
|
4165
4412
|
var steps = Array.prototype.slice.call(root.querySelectorAll('.fdy-step'));
|
|
@@ -4359,6 +4606,7 @@
|
|
|
4359
4606
|
|
|
4360
4607
|
function initTable(root) {
|
|
4361
4608
|
if (root.dataset.fdyTableReady === '1') return;
|
|
4609
|
+
if (root.closest('[data-fdy-ignore]')) return;
|
|
4362
4610
|
var table = root.querySelector('table.fdy-table');
|
|
4363
4611
|
var tbody = table && table.querySelector('tbody');
|
|
4364
4612
|
if (!table || !tbody) return;
|
|
@@ -4902,6 +5150,7 @@
|
|
|
4902
5150
|
|
|
4903
5151
|
function initTabs(root) {
|
|
4904
5152
|
if (root.dataset.fdyTabsReady === '1') return;
|
|
5153
|
+
if (root.closest('[data-fdy-ignore]')) return;
|
|
4905
5154
|
var tabs = Array.prototype.slice.call(root.querySelectorAll('[role="tab"]'));
|
|
4906
5155
|
if (tabs.length === 0) return;
|
|
4907
5156
|
root.dataset.fdyTabsReady = '1';
|
|
@@ -5017,6 +5266,7 @@
|
|
|
5017
5266
|
|
|
5018
5267
|
function initTime(wrap) {
|
|
5019
5268
|
if (wrap.dataset.fdyTpReady === '1') return;
|
|
5269
|
+
if (wrap.closest('[data-fdy-ignore]')) return;
|
|
5020
5270
|
wrap.dataset.fdyTpReady = '1';
|
|
5021
5271
|
wrap.classList.add('fdy-timepicker');
|
|
5022
5272
|
|
|
@@ -5453,6 +5703,7 @@
|
|
|
5453
5703
|
|
|
5454
5704
|
function initTree(tree) {
|
|
5455
5705
|
if (tree.dataset.fdyTreeReady === '1') return;
|
|
5706
|
+
if (tree.closest('[data-fdy-ignore]')) return;
|
|
5456
5707
|
tree.dataset.fdyTreeReady = '1';
|
|
5457
5708
|
Array.prototype.forEach.call(tree.querySelectorAll('input[type="checkbox"]'), function (box) {
|
|
5458
5709
|
// A branch checkbox lives inside <summary>; keep its click from toggling the branch.
|
|
@@ -5500,6 +5751,11 @@
|
|
|
5500
5751
|
* namespace that owns it. A hook whose enhancer is not loaded is skipped: that is a different
|
|
5501
5752
|
* mistake, and this message would misname it. test/docs.test.mjs fails if an enhancer gains a
|
|
5502
5753
|
* ready flag this table does not list.
|
|
5754
|
+
*
|
|
5755
|
+
* `data-fdy-ignore` on an element or any ancestor is the sanctioned way out: every enhancer skips
|
|
5756
|
+
* that subtree, and this stays quiet about it. It is for markup whose behaviour something else
|
|
5757
|
+
* owns: the kit's own Vue/React FdyDrawer, and kit classes an app uses for their look
|
|
5758
|
+
* alone (CATATAN-FREEDAY §14: a chip matrix the app drives itself).
|
|
5503
5759
|
*/
|
|
5504
5760
|
(function () {
|
|
5505
5761
|
'use strict';
|
|
@@ -5528,6 +5784,7 @@
|
|
|
5528
5784
|
['[data-fdy-search]', 'fdySearchReady', 'FreedaySearch'],
|
|
5529
5785
|
['[data-fdy-combo]', 'fdyComboReady', 'FreedayCombo'],
|
|
5530
5786
|
['[data-fdy-slider]', 'fdySliderReady', 'FreedaySlider'],
|
|
5787
|
+
['[data-fdy-sortable]', 'fdySortableReady', 'FreedaySortable'],
|
|
5531
5788
|
['[data-fdy-stepper]', 'fdyStepperReady', 'FreedayStepper'],
|
|
5532
5789
|
['[data-fdy-table]', 'fdyTableReady', 'FreedayTable'],
|
|
5533
5790
|
['[data-fdy-tabs]', 'fdyTabsReady', 'FreedayTabs'],
|
|
@@ -5544,12 +5801,13 @@
|
|
|
5544
5801
|
var hook = HOOKS[i];
|
|
5545
5802
|
if (!window[hook[2]]) continue;
|
|
5546
5803
|
var el = e.target.closest(hook[0]);
|
|
5547
|
-
if (!el || el.dataset[hook[1]] === '1' || warned.has(el)) continue;
|
|
5804
|
+
if (!el || el.dataset[hook[1]] === '1' || warned.has(el) || el.closest('[data-fdy-ignore]')) continue;
|
|
5548
5805
|
warned.add(el);
|
|
5549
5806
|
console.warn('Freeday: this ' + hook[0] + ' was never enhanced, so it does nothing. Either it is '
|
|
5550
5807
|
+ 'outside every initAll() scope (Vue/React: inside the element given to useFreeday(); Blazor: '
|
|
5551
5808
|
+ 'FreedayBlazor.initAll after render; anything else: ' + hook[2] + '.initAll(el) once it is in '
|
|
5552
|
-
+ 'the DOM), or its markup lacks a part the enhancer needs (see COMPONENTS.md).'
|
|
5809
|
+
+ 'the DOM), or its markup lacks a part the enhancer needs (see COMPONENTS.md). If the app '
|
|
5810
|
+
+ 'drives this element itself on purpose, mark it or an ancestor data-fdy-ignore.', el);
|
|
5553
5811
|
}
|
|
5554
5812
|
}
|
|
5555
5813
|
|
|
@@ -5790,6 +6048,7 @@
|
|
|
5790
6048
|
|
|
5791
6049
|
function initDropzone(zone) {
|
|
5792
6050
|
if (zone.dataset.fdyUploadReady === '1') return;
|
|
6051
|
+
if (zone.closest('[data-fdy-ignore]')) return;
|
|
5793
6052
|
zone.dataset.fdyUploadReady = '1';
|
|
5794
6053
|
|
|
5795
6054
|
var input = zone.querySelector('input[type="file"]');
|
package/docs/agent-onboarding.md
CHANGED
|
@@ -118,6 +118,8 @@ Recent additions most likely to replace something an app hand-rolled (all detail
|
|
|
118
118
|
|
|
119
119
|
| Reach for | Instead of |
|
|
120
120
|
|---|---|
|
|
121
|
+
| `data-fdy-sortable` + `.fdy-sortable__handle` | SortableJS plus a hand-written arrow-key path, or a number box for an `order` column |
|
|
122
|
+
| `data-fdy-ignore` | narrowing `useFreeday()` to dodge kit markup the app drives itself, which leaves every form outside the scope dead |
|
|
121
123
|
| `.fdy-btn--stretch` | a hand-rolled overlay to make a card or list row clickable |
|
|
122
124
|
| `[data-fdy-number]` | a bare `<input type="number">` with the browser's own spin buttons |
|
|
123
125
|
| `clearable` on `FdyCfl` / `FdyDatepicker` | a value that can be set but never unset |
|
package/docs/getting-started.md
CHANGED
|
@@ -198,7 +198,7 @@ live docs also have a copy button per component.
|
|
|
198
198
|
```bash
|
|
199
199
|
npm i @cahyo-dimas/freeday
|
|
200
200
|
```
|
|
201
|
-
Lands in `package.json` as `"@cahyo-dimas/freeday": "^3.
|
|
201
|
+
Lands in `package.json` as `"@cahyo-dimas/freeday": "^3.14.0"` (public npm package). `dist/` is
|
|
202
202
|
committed and published → no build step; `npm ci` runs without auth.
|
|
203
203
|
|
|
204
204
|
### 2. Import the CSS + enhancers **once** in your entry (`src/main.ts`)
|
package/docs/integrations.md
CHANGED
|
@@ -42,6 +42,7 @@ Every integration comes down to one of these:
|
|
|
42
42
|
| `freeday-table` | `fdy-table-change` · `fdy-row-select` | `FreedayTable` |
|
|
43
43
|
| `freeday-chip` | `fdy-chip-change` `{value,pressed,selected}` · `fdy-chip-remove` `{value}`, cancelable | `FreedayChip` |
|
|
44
44
|
| `freeday-upload` | `fdy-upload-add` / `fdy-upload-remove` (cancelable) | `FreedayUpload` |
|
|
45
|
+
| `freeday-sortable` | `fdy-sort-change` `{key,from,to,order}`, cancelable | `FreedaySortable` |
|
|
45
46
|
| `freeday-stepper` | `fdy-step-change` | `FreedayStepper` |
|
|
46
47
|
| `freeday-carousel` | `fdy-carousel-change` | `FreedayCarousel` |
|
|
47
48
|
| `freeday-breakpoint` | `fdy-breakpoint-change` | `FreedayBreakpoint` |
|