@cahyo-dimas/freeday 1.53.0 → 2.0.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 +657 -535
- package/COMPONENTS.md +153 -135
- package/README.id.md +46 -45
- package/README.md +46 -43
- package/USAGE.md +39 -35
- package/adapters/blazor/FdyAppShell.razor +50 -0
- package/adapters/blazor/FdyAppShell.razor.cs +114 -0
- package/adapters/blazor/FdyCfl.razor.cs +1 -1
- package/adapters/blazor/FdyChart.razor +1 -1
- package/adapters/blazor/FdyChart.razor.cs +3 -3
- package/adapters/blazor/FdyChartSeries.cs +1 -1
- package/adapters/blazor/FdyCombo.razor.cs +2 -2
- package/adapters/blazor/FdyModal.razor +1 -1
- package/adapters/blazor/FdyTable.razor.cs +7 -7
- package/adapters/blazor/FdyTableFilter.razor +1 -1
- package/adapters/blazor/FdyTableFooter.razor +1 -1
- package/adapters/blazor/FdyTableFooter.razor.cs +4 -4
- package/adapters/blazor/Freeday.Blazor.csproj +1 -1
- package/adapters/blazor/FreedayComponentBase.cs +1 -1
- package/adapters/blazor/TableModel.cs +3 -3
- package/adapters/blazor/TableTypes.cs +3 -3
- package/adapters/blazor/freeday-blazor.js +5 -5
- package/adapters/core/app-shell.d.ts +30 -0
- package/adapters/core/app-shell.js +134 -0
- package/adapters/core/table-model.d.ts +2 -2
- package/adapters/core/table-model.js +8 -8
- package/adapters/react/components/FdyAppShell.tsx +159 -0
- package/adapters/react/components/FdyAutocomplete.tsx +1 -1
- package/adapters/react/components/FdyCascade.tsx +1 -1
- package/adapters/react/components/FdyCfl.tsx +11 -11
- package/adapters/react/components/FdyDateRange.tsx +2 -2
- package/adapters/react/components/FdyDatepicker.tsx +5 -5
- package/adapters/react/components/FdyDrawer.tsx +1 -1
- package/adapters/react/components/FdyModal.tsx +2 -2
- package/adapters/react/components/FdyTable.tsx +7 -7
- package/adapters/react/components/FdyTableFilter.tsx +1 -1
- package/adapters/react/components/FdyTableFooter.tsx +5 -5
- package/adapters/react/index.d.ts +2 -1
- package/adapters/react/index.js +1 -0
- package/adapters/react/useFreeday.js +3 -3
- package/adapters/vue/components/FdyAppShell.vue +160 -0
- package/adapters/vue/components/FdyAutocomplete.vue +1 -1
- package/adapters/vue/components/FdyCascade.vue +1 -1
- package/adapters/vue/components/FdyCfl.vue +8 -8
- package/adapters/vue/components/FdyChart.vue +2 -2
- package/adapters/vue/components/FdyCombo.vue +1 -1
- package/adapters/vue/components/FdyDateRange.vue +2 -2
- package/adapters/vue/components/FdyDatepicker.vue +5 -5
- package/adapters/vue/components/FdyDrawer.vue +3 -3
- package/adapters/vue/components/FdyModal.vue +3 -3
- package/adapters/vue/components/FdyTable.vue +7 -7
- package/adapters/vue/components/FdyTableFilter.vue +1 -1
- package/adapters/vue/components/FdyTableFooter.vue +5 -5
- package/adapters/vue/index.d.ts +3 -2
- package/adapters/vue/index.js +1 -0
- package/adapters/vue/useFreeday.js +3 -3
- package/dist/asset.d.ts +3 -3
- package/dist/freeday-app-shell.js +49 -9
- package/dist/freeday-autocomplete.js +2 -2
- package/dist/freeday-breakpoint.js +1 -1
- package/dist/freeday-carousel.js +5 -5
- package/dist/freeday-cascade.js +7 -7
- package/dist/freeday-cfl.js +9 -9
- package/dist/freeday-chart.js +11 -11
- package/dist/freeday-chip.js +3 -3
- package/dist/freeday-datepicker.js +11 -7
- package/dist/freeday-datetime.js +5 -5
- package/dist/freeday-drawer.js +1 -1
- package/dist/freeday-form.js +14 -14
- package/dist/freeday-mask.js +6 -6
- package/dist/freeday-menu.js +1 -1
- package/dist/freeday-number.js +3 -3
- package/dist/freeday-popover.js +3 -3
- package/dist/freeday-rating.js +2 -2
- package/dist/freeday-select.js +5 -5
- package/dist/freeday-slider.js +2 -2
- package/dist/freeday-stepper.js +7 -7
- package/dist/freeday-table.js +15 -15
- package/dist/freeday-tabs.js +1 -1
- package/dist/freeday-timepicker.js +2 -2
- package/dist/freeday-toast.js +8 -8
- package/dist/freeday-tree.js +3 -3
- package/dist/freeday-upload.js +25 -25
- package/dist/freeday.bundle.css +176 -176
- package/dist/freeday.css +170 -170
- package/dist/freeday.d.ts +2 -2
- package/dist/freeday.js +202 -158
- package/dist/freeday.tokens.css +5 -5
- package/docs/agent-onboarding.md +42 -42
- package/docs/getting-started.md +35 -35
- package/docs/integrations.md +18 -18
- package/docs/reference-screen.html +14 -14
- package/package.json +5 -4
- package/src/base.css +6 -6
- package/src/components/accordion.css +4 -4
- package/src/components/alert.css +1 -1
- package/src/components/app-shell.css +10 -10
- package/src/components/appbar.css +3 -3
- package/src/components/autocomplete.css +1 -1
- package/src/components/avatar.css +3 -3
- package/src/components/badge.css +5 -5
- package/src/components/breadcrumb.css +1 -1
- package/src/components/breakpoints.css +4 -4
- package/src/components/button.css +13 -13
- package/src/components/card.css +8 -8
- package/src/components/carousel.css +2 -2
- package/src/components/cascade.css +2 -2
- package/src/components/cfl.css +4 -4
- package/src/components/chart.css +3 -3
- package/src/components/chip.css +2 -2
- package/src/components/combo.css +3 -3
- package/src/components/composition.css +7 -7
- package/src/components/datepicker.css +6 -6
- package/src/components/datetimepicker.css +1 -1
- package/src/components/description-list.css +1 -1
- package/src/components/divider.css +1 -1
- package/src/components/drawer.css +1 -1
- package/src/components/file-upload.css +5 -5
- package/src/components/filterbar.css +7 -7
- package/src/components/form-grid.css +2 -2
- package/src/components/input-group.css +6 -6
- package/src/components/input.css +6 -6
- package/src/components/kbd.css +1 -1
- package/src/components/list.css +6 -6
- package/src/components/menu.css +3 -3
- package/src/components/modal.css +1 -1
- package/src/components/pagination.css +1 -1
- package/src/components/progress.css +1 -1
- package/src/components/rating.css +2 -2
- package/src/components/selection.css +4 -4
- package/src/components/skeleton.css +1 -1
- package/src/components/slider.css +1 -1
- package/src/components/spinner.css +1 -1
- package/src/components/states.css +1 -1
- package/src/components/stepper.css +2 -2
- package/src/components/table.css +15 -15
- package/src/components/tabs.css +3 -3
- package/src/components/timeline.css +1 -1
- package/src/components/timepicker.css +1 -1
- package/src/components/toast.css +3 -3
- package/src/components/tooltip.css +1 -1
- package/src/components/tree.css +2 -2
- package/tokens/breakpoints.d.ts +2 -2
- package/tokens/breakpoints.mjs +3 -3
- package/tokens/tokens.json +1 -1
package/dist/freeday.js
CHANGED
|
@@ -1,22 +1,27 @@
|
|
|
1
|
-
/* Freeday enhancers
|
|
1
|
+
/* Freeday enhancers. GENERATED by tokens/build.mjs. Do not edit by hand.
|
|
2
2
|
* Concatenation of every src/*.js enhancer (each a self-contained IIFE). */
|
|
3
|
-
/* Freeday
|
|
3
|
+
/* Freeday, app shell enhancer (optional, zero-dependency).
|
|
4
4
|
* Opt in with <div class="fdy-app" data-fdy-app>; the markup is otherwise unchanged.
|
|
5
5
|
*
|
|
6
6
|
* The shell has always shipped the state classes and no behaviour, which left every consumer to
|
|
7
|
-
* assemble the toggle, Escape, focus trap, focus restore and `inert` themselves
|
|
7
|
+
* assemble the toggle, Escape, focus trap, focus restore and `inert` themselves, and the two
|
|
8
8
|
* hand-rolled copies in this repo's own docs already disagreed about which of those exist. Below
|
|
9
9
|
* 721px the sidebar is an OVERLAY (app-shell.css: position:fixed + translateX(-100%)), so it needs
|
|
10
10
|
* the same treatment a modal drawer gets; at or above 721px it is a column that merely collapses.
|
|
11
11
|
*
|
|
12
12
|
* One rule covers both a hidden nav and a hidden page:
|
|
13
13
|
* - `__sidebar` is inert whenever the nav is NOT visible. Off-canvas and collapsed panels stay in
|
|
14
|
-
* the tab order otherwise
|
|
14
|
+
* the tab order otherwise, translateX(-100%) and width:0 hide a thing from the eye, not from
|
|
15
15
|
* the keyboard, which is how a nav nobody can see still swallows Tab.
|
|
16
16
|
* - `__content` is inert only while the nav is an OPEN OVERLAY, so Tab cannot wander behind the
|
|
17
17
|
* backdrop.
|
|
18
18
|
*
|
|
19
|
-
* Emits
|
|
19
|
+
* Emits a bubbling `fdy-app-nav` CustomEvent (detail {visible}) whenever the nav's visibility
|
|
20
|
+
* changes, and takes `FreedayAppShell.setVisible(root, visible)` from outside. Those two exist for
|
|
21
|
+
* the same reason the other enhancers have them: a host that keeps its own state, the Blazor
|
|
22
|
+
* wrapper binding @bind-NavOpen, an app persisting the collapsed preference, has to be able to
|
|
23
|
+
* hear the change and to drive it, without owning the behaviour twice.
|
|
24
|
+
* FreedayAppShell.init(root) for late-mounted markup.
|
|
20
25
|
*/
|
|
21
26
|
(function () {
|
|
22
27
|
'use strict';
|
|
@@ -28,7 +33,7 @@
|
|
|
28
33
|
+ ' textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
|
|
29
34
|
|
|
30
35
|
/* getClientRects(), not offsetParent: an overlay sidebar is position:fixed, and a fixed element
|
|
31
|
-
reports no offsetParent at all
|
|
36
|
+
reports no offsetParent at all, filtering on it would call every nav item invisible. */
|
|
32
37
|
function focusables(root) {
|
|
33
38
|
return Array.prototype.filter.call(root.querySelectorAll(FOCUSABLE), function (el) {
|
|
34
39
|
return el.getClientRects().length > 0;
|
|
@@ -53,18 +58,25 @@
|
|
|
53
58
|
|
|
54
59
|
var mqWide = window.matchMedia(WIDE);
|
|
55
60
|
var restoreTo = null;
|
|
61
|
+
var lastVisible = null;
|
|
56
62
|
|
|
57
63
|
function isOverlayOpen() { return app.classList.contains('fdy-app--nav-open'); }
|
|
58
64
|
function isCollapsed() { return app.classList.contains('fdy-app--nav-collapsed'); }
|
|
59
65
|
function navVisible() { return mqWide.matches ? !isCollapsed() : isOverlayOpen(); }
|
|
60
66
|
|
|
61
|
-
/* aria-expanded answers "is the nav showing?" in BOTH modes
|
|
67
|
+
/* aria-expanded answers "is the nav showing?" in BOTH modes, the two state classes are the
|
|
62
68
|
kit's business, not the reader's. */
|
|
63
69
|
function sync() {
|
|
64
70
|
var visible = navVisible();
|
|
65
71
|
toggle.setAttribute('aria-expanded', String(visible));
|
|
66
72
|
setInert(sidebar, !visible);
|
|
67
73
|
setInert(content, !mqWide.matches && visible);
|
|
74
|
+
/* Announce only real changes. The first sync() runs at init to describe the state the markup
|
|
75
|
+
arrived in, which is not something a host asked for and must not look like one. */
|
|
76
|
+
if (lastVisible !== null && visible !== lastVisible) {
|
|
77
|
+
app.dispatchEvent(new CustomEvent('fdy-app-nav', { bubbles: true, detail: { visible: visible } }));
|
|
78
|
+
}
|
|
79
|
+
lastVisible = visible;
|
|
68
80
|
}
|
|
69
81
|
|
|
70
82
|
function open() {
|
|
@@ -81,7 +93,7 @@
|
|
|
81
93
|
}
|
|
82
94
|
}
|
|
83
95
|
|
|
84
|
-
/* restoreFocus is false when the viewport closed it rather than the user
|
|
96
|
+
/* restoreFocus is false when the viewport closed it rather than the user, a resize must not
|
|
85
97
|
yank focus out from under whatever the reader was doing. */
|
|
86
98
|
function close(restoreFocus) {
|
|
87
99
|
if (!isOverlayOpen()) return;
|
|
@@ -108,7 +120,7 @@
|
|
|
108
120
|
|
|
109
121
|
if (backdrop) backdrop.addEventListener('click', function () { close(); });
|
|
110
122
|
|
|
111
|
-
/* Following a link inside an overlay nav means "take me there"
|
|
123
|
+
/* Following a link inside an overlay nav means "take me there", leaving the panel open over
|
|
112
124
|
the page you just asked for is the one thing every hand-rolled copy in this repo disagreed on. */
|
|
113
125
|
sidebar.addEventListener('click', function (e) {
|
|
114
126
|
if (!isOverlayOpen()) return;
|
|
@@ -149,6 +161,23 @@
|
|
|
149
161
|
});
|
|
150
162
|
|
|
151
163
|
sync();
|
|
164
|
+
|
|
165
|
+
/* The same handle the other enhancers expose (`_fdyCombo` and friends): a host that binds its
|
|
166
|
+
own state needs to drive this without reaching for the class names the kit reserves. */
|
|
167
|
+
app._fdyAppShell = {
|
|
168
|
+
isVisible: navVisible,
|
|
169
|
+
setVisible: function (visible) {
|
|
170
|
+
if (visible === navVisible()) return;
|
|
171
|
+
if (mqWide.matches) {
|
|
172
|
+
app.classList.toggle('fdy-app--nav-collapsed', !visible);
|
|
173
|
+
sync();
|
|
174
|
+
} else if (visible) {
|
|
175
|
+
open();
|
|
176
|
+
} else {
|
|
177
|
+
close();
|
|
178
|
+
}
|
|
179
|
+
},
|
|
180
|
+
};
|
|
152
181
|
}
|
|
153
182
|
|
|
154
183
|
function initShells(context) {
|
|
@@ -165,13 +194,24 @@
|
|
|
165
194
|
initShells();
|
|
166
195
|
}
|
|
167
196
|
|
|
168
|
-
window.FreedayAppShell = {
|
|
197
|
+
window.FreedayAppShell = {
|
|
198
|
+
init: initShells,
|
|
199
|
+
initAll: initShells,
|
|
200
|
+
/* Both take the shell root. A missing or un-initialised root is a no-op rather than a throw:
|
|
201
|
+
a host may race the enhancer on first render, and a crash there is worse than a late sync. */
|
|
202
|
+
setVisible: function (root, visible) {
|
|
203
|
+
if (root && root._fdyAppShell) root._fdyAppShell.setVisible(visible === true);
|
|
204
|
+
},
|
|
205
|
+
isVisible: function (root) {
|
|
206
|
+
return !!(root && root._fdyAppShell && root._fdyAppShell.isVisible());
|
|
207
|
+
},
|
|
208
|
+
};
|
|
169
209
|
})();
|
|
170
210
|
|
|
171
|
-
/* Freeday
|
|
211
|
+
/* Freeday, autocomplete enhancer (optional, zero-dependency).
|
|
172
212
|
* Editable combobox (WAI-ARIA APG): a text input filters a role="listbox" of role="option"
|
|
173
213
|
* items as you type; select fills the input. Auto-inits [data-fdy-autocomplete]. Options are
|
|
174
|
-
* static markup filtered client-side
|
|
214
|
+
* static markup filtered client-side, in an app, fetch/filter server-side and re-render options.
|
|
175
215
|
*
|
|
176
216
|
* Markup: <div data-fdy-autocomplete class="fdy-autocomplete">
|
|
177
217
|
* <input class="fdy-input" role="combobox" aria-expanded="false" aria-autocomplete="list"
|
|
@@ -307,7 +347,7 @@
|
|
|
307
347
|
window.FreedayAutocomplete = { init: initAutocomplete, initAll: initAll };
|
|
308
348
|
})();
|
|
309
349
|
|
|
310
|
-
/* Freeday
|
|
350
|
+
/* Freeday, breakpoint provider (optional, zero-dependency).
|
|
311
351
|
* The MudBreakpointProvider equivalent for JS consumers: stamps the current breakpoint on
|
|
312
352
|
* <html data-breakpoint="xs|sm|md|lg|xl"> and emits a bubbling "fdy-breakpoint-change"
|
|
313
353
|
* CustomEvent (detail {breakpoint}) whenever it changes. Also fills any element with
|
|
@@ -359,7 +399,7 @@
|
|
|
359
399
|
};
|
|
360
400
|
})();
|
|
361
401
|
|
|
362
|
-
/* Freeday
|
|
402
|
+
/* Freeday, carousel enhancer (optional, zero-dependency).
|
|
363
403
|
* Scroll-snap slider with prev/next arrows, generated dot indicators, keyboard (←/→),
|
|
364
404
|
* and optional autoplay (data-fdy-autoplay="4000", pauses on hover/focus). Auto-inits
|
|
365
405
|
* [data-fdy-carousel].
|
|
@@ -379,16 +419,16 @@
|
|
|
379
419
|
'use strict';
|
|
380
420
|
|
|
381
421
|
|
|
382
|
-
/* User-facing strings. Indonesian by default
|
|
383
|
-
* path
|
|
422
|
+
/* User-facing strings. Indonesian by default, documented and deliberate for the raw enhancer
|
|
423
|
+
* path, and every one overridable per element, so a host that speaks another language (the
|
|
384
424
|
* Blazor adapters, an English app on the raw path) supplies its own without forking this file.
|
|
385
425
|
* Keeping them in ONE table is also what lets a guard prove none is hard-coded further down. */
|
|
386
426
|
var TEXT = {
|
|
387
|
-
position: '{n}
|
|
427
|
+
position: '{n} of {total}',
|
|
388
428
|
slide: 'Slide {n}'
|
|
389
429
|
};
|
|
390
430
|
/* HTML lowercases attribute names, so a camelCase key like `filterText` can only ever be written
|
|
391
|
-
as `data-fdy-text-filtertext
|
|
431
|
+
as `data-fdy-text-filtertext`, while the kebab form anybody would reach for,
|
|
392
432
|
`data-fdy-text-filter-text`, becomes a DIFFERENT attribute the enhancer never reads, and the
|
|
393
433
|
override fails silently. So the key is kebab-cased for the lookup; the run-together spelling
|
|
394
434
|
still resolves, for markup written against 1.39.0. */
|
|
@@ -500,7 +540,7 @@
|
|
|
500
540
|
window.FreedayCarousel = { init: initCarousel, initAll: initAll };
|
|
501
541
|
})();
|
|
502
542
|
|
|
503
|
-
/* Freeday
|
|
543
|
+
/* Freeday, cascade select enhancer (optional, zero-dependency).
|
|
504
544
|
* Turns a [data-fdy-cascade] wrapper into a hierarchical drill-down picker. The
|
|
505
545
|
* data model is a hidden nested <ul> inside the wrapper: a <li> with a child <ul>
|
|
506
546
|
* is a branch (drills in), a <li> without is a leaf (selects). Each <li> carries
|
|
@@ -556,16 +596,16 @@
|
|
|
556
596
|
}
|
|
557
597
|
|
|
558
598
|
|
|
559
|
-
/* User-facing strings. Indonesian by default
|
|
560
|
-
* path
|
|
599
|
+
/* User-facing strings. Indonesian by default, documented and deliberate for the raw enhancer
|
|
600
|
+
* path, and every one overridable per element, so a host that speaks another language (the
|
|
561
601
|
* Blazor adapters, an English app on the raw path) supplies its own without forking this file.
|
|
562
602
|
* Keeping them in ONE table is also what lets a guard prove none is hard-coded further down. */
|
|
563
603
|
var TEXT = {
|
|
564
|
-
back: '
|
|
604
|
+
back: 'Back one level',
|
|
565
605
|
submenu: '{label}, submenu'
|
|
566
606
|
};
|
|
567
607
|
/* HTML lowercases attribute names, so a camelCase key like `filterText` can only ever be written
|
|
568
|
-
as `data-fdy-text-filtertext
|
|
608
|
+
as `data-fdy-text-filtertext`, while the kebab form anybody would reach for,
|
|
569
609
|
`data-fdy-text-filter-text`, becomes a DIFFERENT attribute the enhancer never reads, and the
|
|
570
610
|
override fails silently. So the key is kebab-cased for the lookup; the run-together spelling
|
|
571
611
|
still resolves, for markup written against 1.39.0. */
|
|
@@ -590,8 +630,8 @@
|
|
|
590
630
|
var root = sourceUl ? parse(sourceUl) : [];
|
|
591
631
|
if (sourceUl) sourceUl.remove();
|
|
592
632
|
|
|
593
|
-
var label = wrap.getAttribute('data-label') || '
|
|
594
|
-
var placeholder = wrap.getAttribute('data-placeholder') || '
|
|
633
|
+
var label = wrap.getAttribute('data-label') || 'Select';
|
|
634
|
+
var placeholder = wrap.getAttribute('data-placeholder') || 'Select…';
|
|
595
635
|
var sep = wrap.getAttribute('data-separator') || ' / ';
|
|
596
636
|
|
|
597
637
|
var trigger = document.createElement('button');
|
|
@@ -786,24 +826,24 @@
|
|
|
786
826
|
window.FreedayCascade = { init: initCascade, initAll: initAll };
|
|
787
827
|
})();
|
|
788
828
|
|
|
789
|
-
/* Freeday
|
|
829
|
+
/* Freeday, choose-from-list enhancer (optional, zero-dependency).
|
|
790
830
|
* Wires a searchable master-data picker dialog to read-only fields.
|
|
791
831
|
* Progressive enhancement over a native <dialog>; in a framework app, drive the
|
|
792
832
|
* dialog as a controlled component (fetchPage callback + server cache), not a store.
|
|
793
833
|
*
|
|
794
834
|
* Markup contract (see docs):
|
|
795
|
-
* - Field group: [data-fdy-cfl="<dialogId>"]
|
|
835
|
+
* - Field group: [data-fdy-cfl="<dialogId>"], usually a `.fdy-input-group` holding a
|
|
796
836
|
* read-only `.fdy-input` and a trigger `<button class="fdy-input-group__btn">`.
|
|
797
837
|
* Optional `data-fdy-cfl-display="<key>"` picks which row dataset key fills the input
|
|
798
838
|
* (default "value"); optional `data-fdy-cfl-summary="{n} dipilih"` formats the multi label.
|
|
799
|
-
* - Standalone opener: [data-fdy-cfl-open="<dialogId>"]
|
|
839
|
+
* - Standalone opener: [data-fdy-cfl-open="<dialogId>"], opens the dialog with no bound field.
|
|
800
840
|
* - Dialog: <dialog id="<dialogId>" class="fdy-modal fdy-modal--cfl"> containing
|
|
801
841
|
* a `[data-fdy-cfl-search]` input, `.fdy-cfl__row` <tr> rows carrying `data-*` fields,
|
|
802
842
|
* a `[data-fdy-cfl-empty]` element, and (multi only) `[data-fdy-cfl-multiple]` on the
|
|
803
843
|
* dialog plus `[data-fdy-cfl-count]` and `[data-fdy-cfl-confirm]` in the footer.
|
|
804
844
|
*
|
|
805
845
|
* Emits a bubbling "fdy-cfl-select" CustomEvent on the field group (or the dialog when
|
|
806
|
-
* opened standalone): detail {row} for single-select, {rows} for multi-select
|
|
846
|
+
* opened standalone): detail {row} for single-select, {rows} for multi-select, each a plain
|
|
807
847
|
* object copy of the row's dataset. Consumers fill sibling fields (code → name) from it.
|
|
808
848
|
*/
|
|
809
849
|
(function () {
|
|
@@ -822,15 +862,15 @@
|
|
|
822
862
|
}
|
|
823
863
|
|
|
824
864
|
|
|
825
|
-
/* User-facing strings. Indonesian by default
|
|
826
|
-
* path
|
|
865
|
+
/* User-facing strings. Indonesian by default, documented and deliberate for the raw enhancer
|
|
866
|
+
* path, and every one overridable per element, so a host that speaks another language (the
|
|
827
867
|
* Blazor adapters, an English app on the raw path) supplies its own without forking this file.
|
|
828
868
|
* Keeping them in ONE table is also what lets a guard prove none is hard-coded further down. */
|
|
829
869
|
var TEXT = {
|
|
830
|
-
selected: '{n}
|
|
870
|
+
selected: '{n} selected'
|
|
831
871
|
};
|
|
832
872
|
/* HTML lowercases attribute names, so a camelCase key like `filterText` can only ever be written
|
|
833
|
-
as `data-fdy-text-filtertext
|
|
873
|
+
as `data-fdy-text-filtertext`, while the kebab form anybody would reach for,
|
|
834
874
|
`data-fdy-text-filter-text`, becomes a DIFFERENT attribute the enhancer never reads, and the
|
|
835
875
|
override fails silently. So the key is kebab-cased for the lookup; the run-together spelling
|
|
836
876
|
still resolves, for markup written against 1.39.0. */
|
|
@@ -891,7 +931,7 @@
|
|
|
891
931
|
var input = activeGroup.querySelector('.fdy-input');
|
|
892
932
|
if (input) {
|
|
893
933
|
if (detail.rows) {
|
|
894
|
-
var tpl = activeGroup.getAttribute('data-fdy-cfl-summary') || '{n}
|
|
934
|
+
var tpl = activeGroup.getAttribute('data-fdy-cfl-summary') || '{n} selected';
|
|
895
935
|
input.value = detail.rows.length ? tpl.replace('{n}', String(detail.rows.length)) : '';
|
|
896
936
|
} else {
|
|
897
937
|
var key = activeGroup.getAttribute('data-fdy-cfl-display') || 'value';
|
|
@@ -1030,7 +1070,7 @@
|
|
|
1030
1070
|
window.FreedayCfl = { init: initTriggers };
|
|
1031
1071
|
})();
|
|
1032
1072
|
|
|
1033
|
-
/* Freeday
|
|
1073
|
+
/* Freeday, chart renderer (optional, zero-dependency, no external chart lib).
|
|
1034
1074
|
* Renders sparkline / bar / line / area / donut into an element from data attributes.
|
|
1035
1075
|
* Auto-inits [data-fdy-chart] once; call FreedayChart.update(el) to re-render after the
|
|
1036
1076
|
* data attributes change (used by the freeday/vue <FdyChart> wrapper for reactive data).
|
|
@@ -1038,14 +1078,14 @@
|
|
|
1038
1078
|
* Single-series colour (sparkline / simple bar / single line) via data-fdy-color references a
|
|
1039
1079
|
* semantic token by name (primary, accent, success, warning, danger, info). Multi-series
|
|
1040
1080
|
* defaults draw from the validated categorical --chart-1..8 palette in fixed order (never
|
|
1041
|
-
* cycled, 8-series cap); pass data-fdy-colors to override
|
|
1081
|
+
* cycled, 8-series cap); pass data-fdy-colors to override, a name is a semantic token
|
|
1042
1082
|
* (var(--color-<name>)) OR a slot "chart-1".."chart-8" (var(--chart-N)) to pin a series to the
|
|
1043
1083
|
* validated palette. Both data-fdy-color and data-fdy-colors accept the chart-N form.
|
|
1044
1084
|
* Cartesian charts (line/area, multi-series/stacked bar) draw themed axes from --chart-grid /
|
|
1045
1085
|
* --chart-tick and format ticks + tooltips via data-fdy-format (number|percent|currency). Their
|
|
1046
1086
|
* viewBox is sized to the measured plot (1 user unit = 1 CSS pixel) and repainted from a
|
|
1047
1087
|
* ResizeObserver, so axis type stays in real pixels and is set in CSS via --fdy-chart-tick-size.
|
|
1048
|
-
* Charts are an enhancement
|
|
1088
|
+
* Charts are an enhancement, put a table/text fallback inside the element and it will be
|
|
1049
1089
|
* replaced on render; give the element role="img" + aria-label.
|
|
1050
1090
|
*
|
|
1051
1091
|
* Sparkline: <span data-fdy-chart="sparkline" data-values="4,6,5,8,7,10" data-fdy-color="primary">
|
|
@@ -1082,7 +1122,7 @@
|
|
|
1082
1122
|
return v ? v.split(',').map(function (s) { return s.trim(); }) : [];
|
|
1083
1123
|
}
|
|
1084
1124
|
// role="img" is Children Presentational per ARIA, so the docs used to say a chart's rendered
|
|
1085
|
-
// legend, bar values and donut centre are not exposed. No browser actually prunes that subtree
|
|
1125
|
+
// legend, bar values and donut centre are not exposed. No browser actually prunes that subtree,
|
|
1086
1126
|
// measured in Chrome's AX tree, every one of those nodes is live and unignored; what saves the
|
|
1087
1127
|
// advice is only that AT treats a NAMED role="img" as a leaf and does not descend. So the kit
|
|
1088
1128
|
// hides them itself, and the author's aria-label really is the whole text alternative.
|
|
@@ -1257,7 +1297,7 @@
|
|
|
1257
1297
|
// Lay the chrome out first so the plot can be measured, then size the viewBox to it in CSS
|
|
1258
1298
|
// pixels: one user unit is one pixel, so font-size / r / stroke-width mean what they say at
|
|
1259
1299
|
// every container width. (A fixed 320x180 viewBox stretched by CSS multiplied every declared
|
|
1260
|
-
// size by plotWidth/320 instead
|
|
1300
|
+
// size by plotWidth/320 instead, a 9px axis label rendered ~39px on a 1728px viewport.)
|
|
1261
1301
|
var legendMode = el.getAttribute('data-fdy-legend') || 'auto';
|
|
1262
1302
|
var showLegend = legendMode !== 'none' && (series.length >= 2 || legendMode === 'always');
|
|
1263
1303
|
el.classList.add('fdy-chart-xy');
|
|
@@ -1278,7 +1318,7 @@
|
|
|
1278
1318
|
el.appendChild(plot);
|
|
1279
1319
|
|
|
1280
1320
|
// Measured while empty: .fdy-chart-xy__plot carries its own width / aspect-ratio / min-height,
|
|
1281
|
-
// so the box is already final. A detached or display:none chart measures 0
|
|
1321
|
+
// so the box is already final. A detached or display:none chart measures 0, fall back to the
|
|
1282
1322
|
// legacy box and let the ResizeObserver repaint it once it is actually laid out.
|
|
1283
1323
|
var box = plot.getBoundingClientRect();
|
|
1284
1324
|
var W = Math.round(box.width) || 320, H = Math.round(box.height) || 180;
|
|
@@ -1322,7 +1362,7 @@
|
|
|
1322
1362
|
|
|
1323
1363
|
// x category labels with autoskip. Labels are author-supplied, so estimate the widest one instead
|
|
1324
1364
|
// of assuming a fixed 40px slot; keep every step-th plus the last, then drop any kept label that
|
|
1325
|
-
// would collide with the one before it
|
|
1365
|
+
// would collide with the one before it, including the forced last one (a fraction-of-a-slot
|
|
1326
1366
|
// overlap happens when n-1 lands next to the last kept multiple of step, e.g. n=24 at most widths).
|
|
1327
1367
|
var widest = 0;
|
|
1328
1368
|
for (var wi = 0; wi < n; wi++) widest = Math.max(widest, String(labels[wi] || ('#' + (wi + 1))).length * charW);
|
|
@@ -1333,7 +1373,7 @@
|
|
|
1333
1373
|
for (var xi = 0; xi < n; xi++) {
|
|
1334
1374
|
if (xi % step === 0 || xi === n - 1) kept.push(xi);
|
|
1335
1375
|
}
|
|
1336
|
-
// The last label is worth forcing, but not on top of its neighbour
|
|
1376
|
+
// The last label is worth forcing, but not on top of its neighbour, drop the penultimate one.
|
|
1337
1377
|
if (kept.length >= 2 && xPos(kept[kept.length - 1]) - xPos(kept[kept.length - 2]) < minGap) {
|
|
1338
1378
|
kept.splice(kept.length - 2, 1);
|
|
1339
1379
|
}
|
|
@@ -1466,17 +1506,17 @@
|
|
|
1466
1506
|
var li = document.createElement('li');
|
|
1467
1507
|
var sw = document.createElement('span'); sw.className = 'fdy-chart__swatch'; sw.style.background = colorAt(i);
|
|
1468
1508
|
li.appendChild(sw);
|
|
1469
|
-
li.appendChild(document.createTextNode((labels[i] || ('#' + (i + 1))) + '
|
|
1509
|
+
li.appendChild(document.createTextNode((labels[i] || ('#' + (i + 1))) + ' · ' + Math.round((v / total) * 100) + '%'));
|
|
1470
1510
|
legend.appendChild(li);
|
|
1471
1511
|
});
|
|
1472
1512
|
el.appendChild(ring);
|
|
1473
|
-
// Honour data-fdy-legend for the donut too
|
|
1513
|
+
// Honour data-fdy-legend for the donut too, a caller may supply its own richer legend. 'auto'
|
|
1474
1514
|
// keeps today's behaviour (a donut is unreadable without labels, so it shows); 'none' suppresses it.
|
|
1475
1515
|
if ((el.getAttribute('data-fdy-legend') || 'auto') !== 'none') el.appendChild(legend);
|
|
1476
1516
|
ensureImg(el);
|
|
1477
1517
|
}
|
|
1478
1518
|
|
|
1479
|
-
// Full (re-)render from current data attributes. Idempotent
|
|
1519
|
+
// Full (re-)render from current data attributes. Idempotent, safe to call repeatedly.
|
|
1480
1520
|
function renderChart(el) {
|
|
1481
1521
|
var type = el.getAttribute('data-fdy-chart');
|
|
1482
1522
|
if (type === 'sparkline') renderSparkline(el);
|
|
@@ -1511,12 +1551,12 @@
|
|
|
1511
1551
|
window.FreedayChart = { init: initChart, initAll: initAll, update: renderChart };
|
|
1512
1552
|
})();
|
|
1513
1553
|
|
|
1514
|
-
/* Freeday
|
|
1554
|
+
/* Freeday, chip enhancer (optional, zero-dependency).
|
|
1515
1555
|
* Two behaviours, both progressive enhancement:
|
|
1516
1556
|
*
|
|
1517
|
-
* 1. Removable chips
|
|
1557
|
+
* 1. Removable chips, any .fdy-chip__remove button removes its chip and emits a
|
|
1518
1558
|
* bubbling "fdy-chip-remove" (detail {value}).
|
|
1519
|
-
* 2. Choice / filter groups
|
|
1559
|
+
* 2. Choice / filter groups, a [data-fdy-chips] container of interactive chips
|
|
1520
1560
|
* (.fdy-chip--choice / .fdy-chip--filter, each a <button>) toggles aria-pressed
|
|
1521
1561
|
* on click. Add data-single for one-at-a-time (radio-like) selection; filter
|
|
1522
1562
|
* chips get a leading check when pressed. Emits "fdy-chip-change" on the group
|
|
@@ -1598,9 +1638,9 @@
|
|
|
1598
1638
|
window.FreedayChip = { initGroup: initGroup, initRemove: initRemove, initAll: initAll };
|
|
1599
1639
|
})();
|
|
1600
1640
|
|
|
1601
|
-
/* Freeday
|
|
1641
|
+
/* Freeday, date picker enhancer (optional, zero-dependency).
|
|
1602
1642
|
* Builds an input-styled trigger + calendar popover from an empty wrapper.
|
|
1603
|
-
* Locale comes from <html lang> (via Intl)
|
|
1643
|
+
* Locale comes from <html lang> (via Intl), month/weekday/value formatting is not hardcoded.
|
|
1604
1644
|
*
|
|
1605
1645
|
* Markup contract:
|
|
1606
1646
|
* - Single: <div data-fdy-datepicker data-value="2026-07-21" data-label="Tanggal unggah"
|
|
@@ -1619,7 +1659,11 @@
|
|
|
1619
1659
|
(function () {
|
|
1620
1660
|
'use strict';
|
|
1621
1661
|
|
|
1622
|
-
|
|
1662
|
+
/* The page's own `lang` wins, which is the better escape hatch than any attribute the kit
|
|
1663
|
+
could invent: an Indonesian app writes <html lang="id"> and gets Indonesian month and
|
|
1664
|
+
weekday names back automatically. The FALLBACK follows the kit's default language, or a
|
|
1665
|
+
page without `lang` would read English labels around Indonesian month names. */
|
|
1666
|
+
var LOCALE = document.documentElement.getAttribute('lang') || 'en';
|
|
1623
1667
|
var uidSeq = 0;
|
|
1624
1668
|
function uid(p) { uidSeq += 1; return p + '-' + uidSeq; }
|
|
1625
1669
|
function pad(n) { return n < 10 ? '0' + n : '' + n; }
|
|
@@ -1666,10 +1710,10 @@
|
|
|
1666
1710
|
var view = startOfDay(selected || new Date());
|
|
1667
1711
|
view = new Date(view.getFullYear(), view.getMonth(), 1);
|
|
1668
1712
|
var focusDate = null;
|
|
1669
|
-
/* 'days' | 'months'
|
|
1713
|
+
/* 'days' | 'months', the calendar drills one level up rather than growing furniture beside the
|
|
1670
1714
|
title. Before this, the only pointer route to another month was one click per month: from
|
|
1671
1715
|
August 2026 to March 2022 is fifty-three of them. Shift+PageUp already jumped a year, but a
|
|
1672
|
-
shortcut nobody can see is not an affordance
|
|
1716
|
+
shortcut nobody can see is not an affordance, the repo's own test helper clicked "previous
|
|
1673
1717
|
month" twenty-four times to reach a date two years back. */
|
|
1674
1718
|
var mode = 'days';
|
|
1675
1719
|
var focusMonth = null;
|
|
@@ -1868,7 +1912,7 @@
|
|
|
1868
1912
|
var YEARS_PER_PAGE = 12;
|
|
1869
1913
|
function yearPageStart(y) { return Math.floor(y / YEARS_PER_PAGE) * YEARS_PER_PAGE; }
|
|
1870
1914
|
|
|
1871
|
-
/* A year is only unreachable when EVERY month in it falls outside min/max
|
|
1915
|
+
/* A year is only unreachable when EVERY month in it falls outside min/max, the same rule
|
|
1872
1916
|
monthDisabled applies one level down, for the same reason: a bound that sits inside the year
|
|
1873
1917
|
disables neither end. */
|
|
1874
1918
|
function yearDisabled(y) {
|
|
@@ -2113,7 +2157,7 @@
|
|
|
2113
2157
|
var f = fromApi.getValue();
|
|
2114
2158
|
var t = toApi.getValue();
|
|
2115
2159
|
toApi.setMin(f || null); // end can't precede start (one-directional, per Foundation)
|
|
2116
|
-
fromApi.setRange(f, t); // start stays free
|
|
2160
|
+
fromApi.setRange(f, t); // start stays free, picking a later start clears the end below
|
|
2117
2161
|
toApi.setRange(f, t);
|
|
2118
2162
|
}
|
|
2119
2163
|
fromEl.addEventListener('fdy-datepicker-change', function () {
|
|
@@ -2147,9 +2191,9 @@
|
|
|
2147
2191
|
window.FreedayDatepicker = { init: initPicker, initAll: initAll };
|
|
2148
2192
|
})();
|
|
2149
2193
|
|
|
2150
|
-
/* Freeday
|
|
2194
|
+
/* Freeday, datetime picker composer (optional, zero-dependency).
|
|
2151
2195
|
* Combines a child date picker and time picker into one value. It does not build
|
|
2152
|
-
* anything itself
|
|
2196
|
+
* anything itself, freeday-datepicker.js and freeday-timepicker.js enhance the
|
|
2153
2197
|
* children; this only listens for their change events and emits a single combined
|
|
2154
2198
|
* "fdy-datetime-change" (detail {date, time, value}). value is "YYYY-MM-DDTHH:MM"
|
|
2155
2199
|
* when both parts are set, else "". Icon variants are controlled per child picker
|
|
@@ -2159,7 +2203,7 @@
|
|
|
2159
2203
|
* <div data-fdy-datepicker data-value="2026-07-21"></div>
|
|
2160
2204
|
* <div data-fdy-timepicker data-value="14:30"></div></div>
|
|
2161
2205
|
* State: add data-fdy-disabled or data-fdy-invalid on the wrapper to reflect it onto both
|
|
2162
|
-
* child triggers as one control (read-only is adapter-only
|
|
2206
|
+
* child triggers as one control (read-only is adapter-only, a vanilla datetime is interactive).
|
|
2163
2207
|
*/
|
|
2164
2208
|
(function () {
|
|
2165
2209
|
'use strict';
|
|
@@ -2174,7 +2218,7 @@
|
|
|
2174
2218
|
if (!dp || !tp) return;
|
|
2175
2219
|
|
|
2176
2220
|
// Reflect one wrapper-level state onto BOTH child triggers so a datetime reads as a single
|
|
2177
|
-
// disabled/invalid control. Deferred with setTimeout(0)
|
|
2221
|
+
// disabled/invalid control. Deferred with setTimeout(0). NOT a microtask: the timepicker
|
|
2178
2222
|
// enhancer registers its DOMContentLoaded init AFTER this composer, and microtasks drain
|
|
2179
2223
|
// *between* listeners (so a microtask would run before the timepicker trigger exists). A
|
|
2180
2224
|
// macrotask waits until the whole init pass is done and both triggers are built. (readonly is
|
|
@@ -2205,7 +2249,7 @@
|
|
|
2205
2249
|
}));
|
|
2206
2250
|
}
|
|
2207
2251
|
|
|
2208
|
-
// Listen for the children's bubbling change events (order-independent
|
|
2252
|
+
// Listen for the children's bubbling change events (order-independent, no need
|
|
2209
2253
|
// for the child enhancers to have run before this composer).
|
|
2210
2254
|
wrap.addEventListener('fdy-datepicker-change', function (e) { date = e.detail.value; emit(); });
|
|
2211
2255
|
wrap.addEventListener('fdy-time-select', function (e) { time = e.detail.value; emit(); });
|
|
@@ -2234,7 +2278,7 @@
|
|
|
2234
2278
|
window.FreedayDatetime = { init: initDatetime, initAll: initAll };
|
|
2235
2279
|
})();
|
|
2236
2280
|
|
|
2237
|
-
/* Freeday
|
|
2281
|
+
/* Freeday, drawer enhancer (optional, zero-dependency).
|
|
2238
2282
|
* A trigger [data-fdy-drawer="drawerId"] opens the <dialog class="fdy-drawer" id="drawerId">
|
|
2239
2283
|
* as a modal side panel (native showModal gives focus-trap, Esc, and return-focus). Clicking
|
|
2240
2284
|
* the backdrop or any [data-close] inside closes it.
|
|
@@ -2283,7 +2327,7 @@
|
|
|
2283
2327
|
window.FreedayDrawer = { init: initTriggers, initAll: initTriggers };
|
|
2284
2328
|
})();
|
|
2285
2329
|
|
|
2286
|
-
/* Freeday
|
|
2330
|
+
/* Freeday, form validation enhancer (optional, zero-dependency).
|
|
2287
2331
|
* Wires the native Constraint Validation API to accessible inline errors on any
|
|
2288
2332
|
* [data-fdy-validate] <form>. Native-first: constraints come from the standard
|
|
2289
2333
|
* markup attributes (required, type, pattern, min/max, minlength/maxlength, step);
|
|
@@ -2323,24 +2367,24 @@
|
|
|
2323
2367
|
badInput: 'type',
|
|
2324
2368
|
customError: 'mismatch'
|
|
2325
2369
|
};
|
|
2326
|
-
/* User-facing strings. Indonesian by default
|
|
2327
|
-
* path
|
|
2370
|
+
/* User-facing strings. Indonesian by default, documented and deliberate for the raw enhancer
|
|
2371
|
+
* path, and overridable at three levels, narrowest first: `data-fdy-msg-<alias>` on the field,
|
|
2328
2372
|
* `data-fdy-msg` on the field, then `data-fdy-text-<alias>` on the FORM. The form level is what
|
|
2329
2373
|
* a host in another language needs: it sets nine messages once instead of on every input.
|
|
2330
2374
|
* Keeping them in ONE table is also what lets a guard prove none is hard-coded further down. */
|
|
2331
2375
|
var TEXT = {
|
|
2332
|
-
required: '
|
|
2333
|
-
type: '
|
|
2334
|
-
pattern: '
|
|
2335
|
-
minlength: '
|
|
2336
|
-
maxlength: '
|
|
2337
|
-
min: '
|
|
2338
|
-
max: '
|
|
2339
|
-
step: '
|
|
2340
|
-
mismatch: '
|
|
2341
|
-
invalid: '
|
|
2376
|
+
required: 'Required.',
|
|
2377
|
+
type: 'Invalid format.',
|
|
2378
|
+
pattern: 'Doesn’t match the required format.',
|
|
2379
|
+
minlength: 'Too short.',
|
|
2380
|
+
maxlength: 'Too long.',
|
|
2381
|
+
min: 'Too small.',
|
|
2382
|
+
max: 'Too large.',
|
|
2383
|
+
step: 'Not a valid step.',
|
|
2384
|
+
mismatch: 'Values don’t match.',
|
|
2385
|
+
invalid: 'Invalid.'
|
|
2342
2386
|
};
|
|
2343
|
-
/* Kebab-cased for the lookup
|
|
2387
|
+
/* Kebab-cased for the lookup, see the note in the other enhancers: HTML lowercases attribute
|
|
2344
2388
|
names, so `data-fdy-text-filter-text` and `data-fdy-text-filtertext` are different attributes
|
|
2345
2389
|
and only one of them is what an author would write. */
|
|
2346
2390
|
function textOf(root, key) {
|
|
@@ -2484,7 +2528,7 @@
|
|
|
2484
2528
|
window.FreedayForm = { init: initForm, initAll: initAll };
|
|
2485
2529
|
})();
|
|
2486
2530
|
|
|
2487
|
-
/* Freeday
|
|
2531
|
+
/* Freeday, input mask + password reveal enhancer (optional, zero-dependency).
|
|
2488
2532
|
*
|
|
2489
2533
|
* [data-fdy-mask]="pattern" formats a text input as you type. Placeholders:
|
|
2490
2534
|
* # a digit (0-9) A a letter (a-z/A-Z) * a letter or digit
|
|
@@ -2537,16 +2581,16 @@
|
|
|
2537
2581
|
}
|
|
2538
2582
|
|
|
2539
2583
|
|
|
2540
|
-
/* User-facing strings. Indonesian by default
|
|
2541
|
-
* path
|
|
2584
|
+
/* User-facing strings. Indonesian by default, documented and deliberate for the raw enhancer
|
|
2585
|
+
* path, and every one overridable per element, so a host that speaks another language (the
|
|
2542
2586
|
* Blazor adapters, an English app on the raw path) supplies its own without forking this file.
|
|
2543
2587
|
* Keeping them in ONE table is also what lets a guard prove none is hard-coded further down. */
|
|
2544
2588
|
var TEXT = {
|
|
2545
|
-
show: '
|
|
2546
|
-
hide: '
|
|
2589
|
+
show: 'Show password',
|
|
2590
|
+
hide: 'Hide password'
|
|
2547
2591
|
};
|
|
2548
2592
|
/* HTML lowercases attribute names, so a camelCase key like `filterText` can only ever be written
|
|
2549
|
-
as `data-fdy-text-filtertext
|
|
2593
|
+
as `data-fdy-text-filtertext`, while the kebab form anybody would reach for,
|
|
2550
2594
|
`data-fdy-text-filter-text`, becomes a DIFFERENT attribute the enhancer never reads, and the
|
|
2551
2595
|
override fails silently. So the key is kebab-cased for the lookup; the run-together spelling
|
|
2552
2596
|
still resolves, for markup written against 1.39.0. */
|
|
@@ -2628,7 +2672,7 @@
|
|
|
2628
2672
|
window.FreedayMask = { initMask: initMask, initReveal: initReveal, initAll: initAll };
|
|
2629
2673
|
})();
|
|
2630
2674
|
|
|
2631
|
-
/* Freeday
|
|
2675
|
+
/* Freeday, menu enhancer (optional, zero-dependency).
|
|
2632
2676
|
* Menu-button pattern (WAI-ARIA APG): a trigger with aria-haspopup="menu" toggles a
|
|
2633
2677
|
* role="menu" popup of role="menuitem" buttons. Also powers the split button
|
|
2634
2678
|
* (main action + attached caret toggle). Auto-inits [data-fdy-menu].
|
|
@@ -2725,10 +2769,10 @@
|
|
|
2725
2769
|
window.FreedayMenu = { init: initMenu, initAll: initAll };
|
|
2726
2770
|
})();
|
|
2727
2771
|
|
|
2728
|
-
/* Freeday
|
|
2772
|
+
/* Freeday, number field enhancer (optional, zero-dependency).
|
|
2729
2773
|
* Gives a native <input type="number"> its increment/decrement affordance back, on the kit's terms:
|
|
2730
2774
|
* .fdy-input hides the user agent's own spin buttons (unthemeable OS widgets), so this puts two real
|
|
2731
|
-
* buttons in an .fdy-input-group instead. No new block
|
|
2775
|
+
* buttons in an .fdy-input-group instead. No new block, the group already owns the shared border,
|
|
2732
2776
|
* :focus-within ring and error promotion.
|
|
2733
2777
|
*
|
|
2734
2778
|
* Markup contract:
|
|
@@ -2794,7 +2838,7 @@
|
|
|
2794
2838
|
if (frozen()) return;
|
|
2795
2839
|
if (Number(btn.getAttribute('data-fdy-number-step')) < 0) input.stepDown();
|
|
2796
2840
|
else input.stepUp();
|
|
2797
|
-
/* The value changed without the user typing, so say so the way the platform does
|
|
2841
|
+
/* The value changed without the user typing, so say so the way the platform does, frameworks
|
|
2798
2842
|
listen to these, not to a kit-specific event. */
|
|
2799
2843
|
input.dispatchEvent(new Event('input', { bubbles: true }));
|
|
2800
2844
|
input.dispatchEvent(new Event('change', { bubbles: true }));
|
|
@@ -2830,10 +2874,10 @@
|
|
|
2830
2874
|
window.FreedayNumber = { init: initNumber, initAll: initAll };
|
|
2831
2875
|
})();
|
|
2832
2876
|
|
|
2833
|
-
/* Freeday
|
|
2877
|
+
/* Freeday, popover positioning helper (optional, zero-dependency).
|
|
2834
2878
|
* Lifts a dropdown panel into the top layer via the native Popover API so it escapes ANY
|
|
2835
2879
|
* ancestor overflow clip (a .fdy-card with overflow:hidden, an app-shell main with
|
|
2836
|
-
* overflow:auto, …), then positions it `fixed` against its trigger
|
|
2880
|
+
* overflow:auto, …), then positions it `fixed` against its trigger, flipping above when
|
|
2837
2881
|
* there is no room below, and matching the trigger width. The panel stays a DOM child of its
|
|
2838
2882
|
* component, so focus, outside-click (.closest), and ARIA relationships keep working unchanged.
|
|
2839
2883
|
*
|
|
@@ -2853,7 +2897,7 @@
|
|
|
2853
2897
|
&& typeof HTMLElement.prototype.showPopover === 'function';
|
|
2854
2898
|
|
|
2855
2899
|
// Position `panel` (already shown, so it has a measurable box) against `trigger`'s rect.
|
|
2856
|
-
// matchWidth (default true) floors the panel to the trigger width
|
|
2900
|
+
// matchWidth (default true) floors the panel to the trigger width, right for input-shaped
|
|
2857
2901
|
// dropdowns (combo/cascade/date/time), skipped for menus that keep their own min-width.
|
|
2858
2902
|
function place(panel, trigger, matchWidth) {
|
|
2859
2903
|
var r = trigger.getBoundingClientRect();
|
|
@@ -2900,10 +2944,10 @@
|
|
|
2900
2944
|
window.FreedayPopover = { attach: attach, place: place, supported: supported };
|
|
2901
2945
|
})();
|
|
2902
2946
|
|
|
2903
|
-
/* Freeday
|
|
2947
|
+
/* Freeday, rating enhancer (optional, zero-dependency).
|
|
2904
2948
|
* Fills stars up to the checked radio (and previews on hover) for an interactive
|
|
2905
2949
|
* [data-fdy-rating] radio group; for [data-fdy-readonly] it fills from data-value.
|
|
2906
|
-
* The radios are native
|
|
2950
|
+
* The radios are native, arrow-key selection and form association come for free.
|
|
2907
2951
|
*/
|
|
2908
2952
|
(function () {
|
|
2909
2953
|
'use strict';
|
|
@@ -2954,11 +2998,11 @@
|
|
|
2954
2998
|
window.FreedayRating = { init: initRating, initAll: initAll };
|
|
2955
2999
|
})();
|
|
2956
3000
|
|
|
2957
|
-
/* Freeday
|
|
3001
|
+
/* Freeday, combobox enhancer (optional, zero-dependency).
|
|
2958
3002
|
* Implements the WAI-ARIA APG "select-only combobox" pattern for every
|
|
2959
3003
|
* [data-fdy-combo] on the page. Progressive enhancement: the markup is the
|
|
2960
3004
|
* component, this script wires behavior. In a Vue/React/Blazor app you would
|
|
2961
|
-
* bind your own state instead
|
|
3005
|
+
* bind your own state instead, this is the framework-agnostic reference.
|
|
2962
3006
|
*
|
|
2963
3007
|
* Expected markup:
|
|
2964
3008
|
* <div class="fdy-combo" data-fdy-combo data-value="x">
|
|
@@ -2981,7 +3025,7 @@
|
|
|
2981
3025
|
var seq = 0;
|
|
2982
3026
|
var openClose = null; // close() of the currently-open combo, or null
|
|
2983
3027
|
|
|
2984
|
-
/* The tick lives in CSS now, so the option's text IS its label
|
|
3028
|
+
/* The tick lives in CSS now, so the option's text IS its label, no sibling walk to skip a glyph. */
|
|
2985
3029
|
function optionLabel(opt) {
|
|
2986
3030
|
return (opt.textContent || '').trim();
|
|
2987
3031
|
}
|
|
@@ -3047,7 +3091,7 @@
|
|
|
3047
3091
|
if (openClose === close) openClose = null;
|
|
3048
3092
|
}
|
|
3049
3093
|
// Mark an option selected + reflect it in the button. `silent` skips the change
|
|
3050
|
-
// event, close, and focus
|
|
3094
|
+
// event, close, and focus, used by the programmatic setValue() below so a host
|
|
3051
3095
|
// (e.g. a Blazor/framework wrapper) can push a value in without echoing an event
|
|
3052
3096
|
// back to itself or stealing focus. User-driven picks go through choose() (loud).
|
|
3053
3097
|
function select(opt, silent) {
|
|
@@ -3082,7 +3126,7 @@
|
|
|
3082
3126
|
isOpen() ? close() : open();
|
|
3083
3127
|
});
|
|
3084
3128
|
// Pressing an option must not blur the button: a blur fires the focusout
|
|
3085
|
-
// handler below, which closes the listbox before the option's click lands
|
|
3129
|
+
// handler below, which closes the listbox before the option's click lands,
|
|
3086
3130
|
// so the pick is lost (real mouse only; synthetic .click() never blurs).
|
|
3087
3131
|
// preventDefault on mousedown keeps focus on the button without blocking click.
|
|
3088
3132
|
listbox.addEventListener('mousedown', function (e) { e.preventDefault(); });
|
|
@@ -3169,10 +3213,10 @@
|
|
|
3169
3213
|
window.FreedayCombo = { init: initCombo, initAll: initAll, setValue: setValue };
|
|
3170
3214
|
})();
|
|
3171
3215
|
|
|
3172
|
-
/* Freeday
|
|
3216
|
+
/* Freeday, slider value binding (optional, zero-dependency).
|
|
3173
3217
|
* Mirrors a [data-fdy-slider] range input's value into <output for="inputId">, with an
|
|
3174
3218
|
* optional prefix/suffix on the output (data-fdy-prefix / data-fdy-suffix). The range itself
|
|
3175
|
-
* is a native input
|
|
3219
|
+
* is a native input, keyboard and ARIA are built in; this only updates the visible readout.
|
|
3176
3220
|
*/
|
|
3177
3221
|
(function () {
|
|
3178
3222
|
'use strict';
|
|
@@ -3207,9 +3251,9 @@
|
|
|
3207
3251
|
window.FreedaySlider = { init: initSlider, initAll: initAll };
|
|
3208
3252
|
})();
|
|
3209
3253
|
|
|
3210
|
-
/* Freeday
|
|
3254
|
+
/* Freeday, stepper/wizard enhancer (optional, zero-dependency).
|
|
3211
3255
|
* Drives a step header + panels: prev/next navigation, completed/active state, and
|
|
3212
|
-
* clicking a reached step to revisit it (linear
|
|
3256
|
+
* clicking a reached step to revisit it (linear, can't skip ahead). Auto-inits
|
|
3213
3257
|
* [data-fdy-stepper].
|
|
3214
3258
|
*
|
|
3215
3259
|
* Markup: <div data-fdy-stepper>
|
|
@@ -3228,16 +3272,16 @@
|
|
|
3228
3272
|
var CHECK = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 6 9 17l-5-5"></path></svg>';
|
|
3229
3273
|
|
|
3230
3274
|
|
|
3231
|
-
/* User-facing strings. Indonesian by default
|
|
3232
|
-
* path
|
|
3275
|
+
/* User-facing strings. Indonesian by default, documented and deliberate for the raw enhancer
|
|
3276
|
+
* path, and every one overridable per element, so a host that speaks another language (the
|
|
3233
3277
|
* Blazor adapters, an English app on the raw path) supplies its own without forking this file.
|
|
3234
3278
|
* Keeping them in ONE table is also what lets a guard prove none is hard-coded further down. */
|
|
3235
3279
|
var TEXT = {
|
|
3236
|
-
done: '
|
|
3237
|
-
next: '
|
|
3280
|
+
done: 'Done',
|
|
3281
|
+
next: 'Next'
|
|
3238
3282
|
};
|
|
3239
3283
|
/* HTML lowercases attribute names, so a camelCase key like `filterText` can only ever be written
|
|
3240
|
-
as `data-fdy-text-filtertext
|
|
3284
|
+
as `data-fdy-text-filtertext`, while the kebab form anybody would reach for,
|
|
3241
3285
|
`data-fdy-text-filter-text`, becomes a DIFFERENT attribute the enhancer never reads, and the
|
|
3242
3286
|
override fails silently. So the key is kebab-cased for the lookup; the run-together spelling
|
|
3243
3287
|
still resolves, for markup written against 1.39.0. */
|
|
@@ -3327,7 +3371,7 @@
|
|
|
3327
3371
|
window.FreedayStepper = { init: initStepper, initAll: initAll };
|
|
3328
3372
|
})();
|
|
3329
3373
|
|
|
3330
|
-
/* Freeday
|
|
3374
|
+
/* Freeday, data-table enhancer (optional, zero-dependency).
|
|
3331
3375
|
* Adds client-side search, column sort, pagination and row selection to any
|
|
3332
3376
|
* [data-fdy-table]. Progressive enhancement over a plain semantic <table>;
|
|
3333
3377
|
* in a framework app, do this server- or store-side instead.
|
|
@@ -3363,26 +3407,26 @@
|
|
|
3363
3407
|
}
|
|
3364
3408
|
|
|
3365
3409
|
|
|
3366
|
-
/* User-facing strings. Indonesian by default
|
|
3367
|
-
* path
|
|
3410
|
+
/* User-facing strings. Indonesian by default, documented and deliberate for the raw enhancer
|
|
3411
|
+
* path, and every one overridable per element, so a host that speaks another language (the
|
|
3368
3412
|
* Blazor adapters, an English app on the raw path) supplies its own without forking this file.
|
|
3369
3413
|
* Keeping them in ONE table is also what lets a guard prove none is hard-coded further down. */
|
|
3370
3414
|
var TEXT = {
|
|
3371
|
-
prev: '
|
|
3372
|
-
next: '
|
|
3373
|
-
selected: '{n}
|
|
3374
|
-
filter: 'Filter
|
|
3375
|
-
filterText: '
|
|
3376
|
-
filterTextPlaceholder: '
|
|
3377
|
-
filterEnum: '
|
|
3378
|
-
filterRange: '
|
|
3415
|
+
prev: 'Previous',
|
|
3416
|
+
next: 'Next',
|
|
3417
|
+
selected: '{n} selected',
|
|
3418
|
+
filter: 'Filter column',
|
|
3419
|
+
filterText: 'Contains text',
|
|
3420
|
+
filterTextPlaceholder: 'Contains…',
|
|
3421
|
+
filterEnum: 'Show values',
|
|
3422
|
+
filterRange: 'Value range',
|
|
3379
3423
|
reset: 'Reset',
|
|
3380
|
-
close: '
|
|
3381
|
-
rows: '{n}
|
|
3382
|
-
info: '
|
|
3424
|
+
close: 'Close',
|
|
3425
|
+
rows: '{n} rows',
|
|
3426
|
+
info: 'Showing {from}–{to} of {total}'
|
|
3383
3427
|
};
|
|
3384
3428
|
/* HTML lowercases attribute names, so a camelCase key like `filterText` can only ever be written
|
|
3385
|
-
as `data-fdy-text-filtertext
|
|
3429
|
+
as `data-fdy-text-filtertext`, while the kebab form anybody would reach for,
|
|
3386
3430
|
`data-fdy-text-filter-text`, becomes a DIFFERENT attribute the enhancer never reads, and the
|
|
3387
3431
|
override fails silently. So the key is kebab-cased for the lookup; the run-together spelling
|
|
3388
3432
|
still resolves, for markup written against 1.39.0. */
|
|
@@ -3862,7 +3906,7 @@
|
|
|
3862
3906
|
window.FreedayTable = { init: initTable, initAll: initAll };
|
|
3863
3907
|
})();
|
|
3864
3908
|
|
|
3865
|
-
/* Freeday
|
|
3909
|
+
/* Freeday, tabs enhancer (optional, zero-dependency, WAI-ARIA APG tabs).
|
|
3866
3910
|
* Auto-inits every [data-fdy-tabs]. Roving tabindex + arrow/Home/End nav.
|
|
3867
3911
|
*
|
|
3868
3912
|
* Markup: a [data-fdy-tabs] with a [role="tablist"] of
|
|
@@ -3931,7 +3975,7 @@
|
|
|
3931
3975
|
window.FreedayTabs = { init: initTabs, initAll: initAll };
|
|
3932
3976
|
})();
|
|
3933
3977
|
|
|
3934
|
-
/* Freeday
|
|
3978
|
+
/* Freeday, time picker enhancer (optional, zero-dependency).
|
|
3935
3979
|
* Builds an input-styled trigger + scrollable time-list popover (WAI-ARIA listbox)
|
|
3936
3980
|
* from an empty [data-fdy-timepicker] wrapper. 24h HH:MM. Options run from
|
|
3937
3981
|
* data-min (default 00:00) to data-max (default 23:59) every data-step minutes
|
|
@@ -3961,7 +4005,7 @@
|
|
|
3961
4005
|
wrap.dataset.fdyTpReady = '1';
|
|
3962
4006
|
wrap.classList.add('fdy-timepicker');
|
|
3963
4007
|
|
|
3964
|
-
var label = wrap.getAttribute('data-label') || '
|
|
4008
|
+
var label = wrap.getAttribute('data-label') || 'Choose a time';
|
|
3965
4009
|
var placeholder = wrap.getAttribute('data-placeholder') || '--:--';
|
|
3966
4010
|
var step = Math.max(1, parseInt(wrap.getAttribute('data-step') || '30', 10));
|
|
3967
4011
|
var minM = valid(wrap.getAttribute('data-min')) ? toMin(wrap.getAttribute('data-min')) : 0;
|
|
@@ -4115,12 +4159,12 @@
|
|
|
4115
4159
|
window.FreedayTimepicker = { init: initTime, initAll: initAll };
|
|
4116
4160
|
})();
|
|
4117
4161
|
|
|
4118
|
-
/* Freeday
|
|
4162
|
+
/* Freeday, toast API (optional, zero-dependency).
|
|
4119
4163
|
* Freeday.toast({ variant, title, message, timeout, key }) shows a transient
|
|
4120
4164
|
* notification in a live region (created on first use). Returns the element.
|
|
4121
4165
|
* variant: 'success' | 'warning' | 'danger' | 'info' (default: neutral)
|
|
4122
4166
|
* timeout: ms before auto-dismiss (default 4000; 0 = sticky)
|
|
4123
|
-
* key: optional stable id
|
|
4167
|
+
* key: optional stable id, a new toast with the same key REPLACES the
|
|
4124
4168
|
* existing one in place instead of stacking a duplicate (e.g. a burst
|
|
4125
4169
|
* of identical failures shows one toast, refreshed).
|
|
4126
4170
|
* Freeday.dismiss(nodeOrKey) closes a toast early (pass the returned element or its key).
|
|
@@ -4156,14 +4200,14 @@
|
|
|
4156
4200
|
|
|
4157
4201
|
/* Put the region in the TOP LAYER, above any open <dialog>.
|
|
4158
4202
|
*
|
|
4159
|
-
* A modal dialog lives in the top layer, where z-index does not reach
|
|
4203
|
+
* A modal dialog lives in the top layer, where z-index does not reach, so a toast raised by an
|
|
4160
4204
|
* action taken inside a modal was painted behind that modal's backdrop, which is where an error
|
|
4161
4205
|
* message is least useful. showPopover() joins the same layer, and membership there is ordered by
|
|
4162
4206
|
* when you joined: hide-then-show re-joins at the top, so a toast shown while a dialog is open
|
|
4163
4207
|
* lands above it rather than under the dialog that was opened after the last toast.
|
|
4164
4208
|
*
|
|
4165
4209
|
* Guarded twice on purpose. `showPopover` is absent before Chrome 114 / Safari 17 / Firefox 125,
|
|
4166
|
-
* and the call throws if the element is disconnected or already open
|
|
4210
|
+
* and the call throws if the element is disconnected or already open, in every one of those
|
|
4167
4211
|
* cases the region stays an ordinary fixed block at z-index 200, which is what it has always
|
|
4168
4212
|
* been. A notification must never be the thing that throws.
|
|
4169
4213
|
*/
|
|
@@ -4194,15 +4238,15 @@
|
|
|
4194
4238
|
}
|
|
4195
4239
|
|
|
4196
4240
|
|
|
4197
|
-
/* User-facing strings. Indonesian by default
|
|
4198
|
-
* path
|
|
4241
|
+
/* User-facing strings. Indonesian by default, documented and deliberate for the raw enhancer
|
|
4242
|
+
* path, and every one overridable per element, so a host that speaks another language (the
|
|
4199
4243
|
* Blazor adapters, an English app on the raw path) supplies its own without forking this file.
|
|
4200
4244
|
* Keeping them in ONE table is also what lets a guard prove none is hard-coded further down. */
|
|
4201
4245
|
var TEXT = {
|
|
4202
|
-
close: '
|
|
4246
|
+
close: 'Close'
|
|
4203
4247
|
};
|
|
4204
4248
|
/* HTML lowercases attribute names, so a camelCase key like `filterText` can only ever be written
|
|
4205
|
-
as `data-fdy-text-filtertext
|
|
4249
|
+
as `data-fdy-text-filtertext`, while the kebab form anybody would reach for,
|
|
4206
4250
|
`data-fdy-text-filter-text`, becomes a DIFFERENT attribute the enhancer never reads, and the
|
|
4207
4251
|
override fails silently. So the key is kebab-cased for the lookup; the run-together spelling
|
|
4208
4252
|
still resolves, for markup written against 1.39.0. */
|
|
@@ -4277,10 +4321,10 @@
|
|
|
4277
4321
|
window.Freeday.dismiss = dismiss; // dismiss(nodeOrKey)
|
|
4278
4322
|
})();
|
|
4279
4323
|
|
|
4280
|
-
/* Freeday
|
|
4324
|
+
/* Freeday, tree checkbox cascade (optional, zero-dependency).
|
|
4281
4325
|
* Turns a [data-fdy-tree] checkbox tree into a selectable one: checking a branch checks
|
|
4282
4326
|
* every descendant; a partially-selected branch shows the native :indeterminate state.
|
|
4283
|
-
* Rows are native <input class="fdy-checkbox">, labelled via aria-label
|
|
4327
|
+
* Rows are native <input class="fdy-checkbox">, labelled via aria-label. Space toggles.
|
|
4284
4328
|
* Selection never toggles the <details> (the checkbox stops click propagation).
|
|
4285
4329
|
*
|
|
4286
4330
|
* Markup contract:
|
|
@@ -4304,7 +4348,7 @@
|
|
|
4304
4348
|
return Array.prototype.filter.call(ul.children, function (n) { return n.tagName === 'LI'; });
|
|
4305
4349
|
}
|
|
4306
4350
|
|
|
4307
|
-
// The checkbox belonging to a row's own <li
|
|
4351
|
+
// The checkbox belonging to a row's own <li>, branch summary or leaf, never a child's.
|
|
4308
4352
|
function ownBox(li) {
|
|
4309
4353
|
return li.querySelector(':scope > details > summary input[type="checkbox"]')
|
|
4310
4354
|
|| li.querySelector(':scope > input[type="checkbox"]');
|
|
@@ -4375,16 +4419,16 @@
|
|
|
4375
4419
|
window.FreedayTree = { init: initTree, initAll: initAll };
|
|
4376
4420
|
})();
|
|
4377
4421
|
|
|
4378
|
-
/* Freeday
|
|
4422
|
+
/* Freeday, file-upload enhancer (optional, zero-dependency).
|
|
4379
4423
|
* A drop target that also opens the file dialog on click / Enter / Space, validates each file
|
|
4380
4424
|
* (accept + max size), and renders a per-file row with explicit state. Actual upload is the
|
|
4381
|
-
* consumer's job
|
|
4425
|
+
* consumer's job, this is a controlled reference: wire the emitted event's row API to your
|
|
4382
4426
|
* upload call, or set data-fdy-upload-simulate for a demo progress animation.
|
|
4383
4427
|
*
|
|
4384
4428
|
* Markup contract:
|
|
4385
4429
|
* <div class="fdy-dropzone" data-fdy-dropzone role="button" tabindex="0"
|
|
4386
4430
|
* data-max-size="10485760" data-filelist="#dz-list" [data-fdy-upload-simulate]
|
|
4387
|
-
* aria-label="Unggah berkas
|
|
4431
|
+
* aria-label="Unggah berkas, seret ke sini atau tekan Enter">
|
|
4388
4432
|
* <span class="fdy-dropzone__icon">…svg…</span>
|
|
4389
4433
|
* <span class="fdy-dropzone__title">…</span>
|
|
4390
4434
|
* <span class="fdy-dropzone__hint">…</span>
|
|
@@ -4392,15 +4436,15 @@
|
|
|
4392
4436
|
* </div>
|
|
4393
4437
|
* <div class="fdy-filelist" id="dz-list"></div>
|
|
4394
4438
|
*
|
|
4395
|
-
* Emits bubbling CustomEvents
|
|
4439
|
+
* Emits bubbling CustomEvents. BOTH on the dropzone element, which is the one target a consumer
|
|
4396
4440
|
* needs to listen on:
|
|
4397
4441
|
* "fdy-upload-add" {file, rejected, reason, row}
|
|
4398
4442
|
* "fdy-upload-remove" {file}
|
|
4399
4443
|
* `row` is the state machine over the rendered .fdy-file:
|
|
4400
|
-
* .ready() (rest
|
|
4444
|
+
* .ready() (rest, where a dropped file starts) / .uploading() / .setProgress(pct) /
|
|
4401
4445
|
* .waiting(label) (sent, awaiting the server) / .done() / .fail(msg) / .el
|
|
4402
4446
|
* Note the file list is a SIBLING of the dropzone above, so nothing dispatched on a row would ever
|
|
4403
|
-
* bubble through the zone
|
|
4447
|
+
* bubble through the zone, which is why removal fires on the zone and not on the row.
|
|
4404
4448
|
*/
|
|
4405
4449
|
(function () {
|
|
4406
4450
|
'use strict';
|
|
@@ -4428,21 +4472,21 @@
|
|
|
4428
4472
|
}
|
|
4429
4473
|
|
|
4430
4474
|
|
|
4431
|
-
/* User-facing strings. Indonesian by default
|
|
4432
|
-
* path
|
|
4475
|
+
/* User-facing strings. Indonesian by default, documented and deliberate for the raw enhancer
|
|
4476
|
+
* path, and every one overridable per element, so a host that speaks another language (the
|
|
4433
4477
|
* Blazor adapters, an English app on the raw path) supplies its own without forking this file.
|
|
4434
4478
|
* Keeping them in ONE table is also what lets a guard prove none is hard-coded further down. */
|
|
4435
4479
|
var TEXT = {
|
|
4436
|
-
remove: '
|
|
4437
|
-
progress: '
|
|
4438
|
-
uploading: '
|
|
4439
|
-
waiting: '
|
|
4440
|
-
done: '
|
|
4441
|
-
badType: '
|
|
4442
|
-
tooBig: '
|
|
4480
|
+
remove: 'Remove {name}',
|
|
4481
|
+
progress: 'Upload progress for {name}',
|
|
4482
|
+
uploading: 'Uploading…',
|
|
4483
|
+
waiting: 'Waiting for the server…',
|
|
4484
|
+
done: 'Uploaded',
|
|
4485
|
+
badType: 'Unsupported file type.',
|
|
4486
|
+
tooBig: 'Over the {max} limit.'
|
|
4443
4487
|
};
|
|
4444
4488
|
/* HTML lowercases attribute names, so a camelCase key like `filterText` can only ever be written
|
|
4445
|
-
as `data-fdy-text-filtertext
|
|
4489
|
+
as `data-fdy-text-filtertext`, while the kebab form anybody would reach for,
|
|
4446
4490
|
`data-fdy-text-filter-text`, becomes a DIFFERENT attribute the enhancer never reads, and the
|
|
4447
4491
|
override fails silently. So the key is kebab-cased for the lookup; the run-together spelling
|
|
4448
4492
|
still resolves, for markup written against 1.39.0. */
|
|
@@ -4480,12 +4524,12 @@
|
|
|
4480
4524
|
remove.setAttribute('aria-label', textOf(zone, 'remove', { name: file.name }));
|
|
4481
4525
|
remove.innerHTML = '×';
|
|
4482
4526
|
remove.addEventListener('click', function () {
|
|
4483
|
-
/* Dispatched on the ZONE, not on the row
|
|
4527
|
+
/* Dispatched on the ZONE, not on the row, the same target as fdy-upload-add, so one listener
|
|
4484
4528
|
on the dropzone gets both. The row lives in the file list, which the kit's own markup
|
|
4485
4529
|
contract puts as a SIBLING of the dropzone, so a row event never bubbles through the zone:
|
|
4486
4530
|
a consumer following the docs saw `add` arrive and `remove` never fire, with no error.
|
|
4487
|
-
Firing on both would look safer, but it makes the pair asymmetric
|
|
4488
|
-
anyone delegating on a common ancestor, two `remove`s
|
|
4531
|
+
Firing on both would look safer, but it makes the pair asymmetric, one `add` and, for
|
|
4532
|
+
anyone delegating on a common ancestor, two `remove`s, which is its own silent bug. It
|
|
4489
4533
|
also fixes the listless case: a row that was never attached bubbles to nothing at all. */
|
|
4490
4534
|
zone.dispatchEvent(new CustomEvent('fdy-upload-remove', { bubbles: true, detail: { file: file } }));
|
|
4491
4535
|
el.remove();
|
|
@@ -4517,14 +4561,14 @@
|
|
|
4517
4561
|
}
|
|
4518
4562
|
/* Back to a measured bar. While the indeterminate modifier is on it owns the bar's width, so an
|
|
4519
4563
|
explicit one has to be restored when it comes off: .fdy-progress__bar is a plain block div, and
|
|
4520
|
-
with no width at all it fills the track
|
|
4564
|
+
with no width at all it fills the track, a full bar, which is the opposite of what 0% means. */
|
|
4521
4565
|
function determinate() {
|
|
4522
4566
|
progressEl.classList.remove('fdy-progress--indeterminate');
|
|
4523
4567
|
if (!bar.style.width) bar.style.width = '0%';
|
|
4524
4568
|
}
|
|
4525
4569
|
return {
|
|
4526
4570
|
el: el,
|
|
4527
|
-
/* The state a row starts in: chosen, not yet sent. The size alone
|
|
4571
|
+
/* The state a row starts in: chosen, not yet sent. The size alone, it makes no claim about
|
|
4528
4572
|
a transfer, and no progress bar is shown, because nothing is in flight. Without this the
|
|
4529
4573
|
state machine had no start state: done() claims success, fail() claims an error, and
|
|
4530
4574
|
uploading() is a lie until the consumer actually sends the file. */
|
|
@@ -4548,12 +4592,12 @@
|
|
|
4548
4592
|
bar.style.width = v + '%';
|
|
4549
4593
|
progressEl.setAttribute('aria-valuenow', String(Math.round(v)));
|
|
4550
4594
|
},
|
|
4551
|
-
/* The bytes are gone and the server has not answered yet
|
|
4595
|
+
/* The bytes are gone and the server has not answered yet, extraction, scanning, transcoding.
|
|
4552
4596
|
"Mengunggah…" turns false the moment the last byte leaves, and a determinate bar parked at
|
|
4553
4597
|
100% is the most convincing "hung" signal a UI can produce, so this state reports no
|
|
4554
4598
|
percentage: the bar goes indeterminate and the label belongs to the consumer, because only
|
|
4555
4599
|
they know what the server is doing ("Membaca PDF…", "Memindai…"). done()/fail()/ready() need
|
|
4556
|
-
no counterpart here
|
|
4600
|
+
no counterpart here, they drop the progress element outright, modifier and all. */
|
|
4557
4601
|
waiting: function (label) {
|
|
4558
4602
|
el.classList.remove('fdy-file--error', 'fdy-file--success');
|
|
4559
4603
|
icon.innerHTML = FILE_ICON;
|
|
@@ -4561,7 +4605,7 @@
|
|
|
4561
4605
|
ensureProgress();
|
|
4562
4606
|
progressEl.classList.add('fdy-progress--indeterminate');
|
|
4563
4607
|
/* The modifier styles .fdy-progress__bar, so it must sit on the CONTAINER, and the inline
|
|
4564
|
-
width setProgress wrote has to go
|
|
4608
|
+
width setProgress wrote has to go, an inline style beats any rule the modifier brings.
|
|
4565
4609
|
aria-valuenow goes with it: a progressbar with no value is precisely what ARIA calls
|
|
4566
4610
|
indeterminate, which is the contract COMPONENTS.md already states for this component. */
|
|
4567
4611
|
bar.style.width = '';
|
|
@@ -4632,7 +4676,7 @@
|
|
|
4632
4676
|
if (input) input.addEventListener('change', function () { handleFiles(input.files); input.value = ''; });
|
|
4633
4677
|
|
|
4634
4678
|
/* The list is optional: rendering a row and announcing the file are separate jobs. A consumer
|
|
4635
|
-
that wants its own markup simply provides no list
|
|
4679
|
+
that wants its own markup simply provides no list, it still gets `fdy-upload-add`, and
|
|
4636
4680
|
`detail.row` still works (its element is just never attached). Gating the EVENT on the list
|
|
4637
4681
|
meant "bring your own row" silently cost you the notification that a file had arrived. */
|
|
4638
4682
|
function handleFiles(fileList) {
|