@cahyo-dimas/freeday 1.54.0 → 2.1.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 +713 -545
- package/COMPONENTS.md +417 -139
- package/README.id.md +46 -45
- package/README.md +46 -43
- package/USAGE.md +34 -34
- package/adapters/blazor/FdyAppShell.razor +2 -2
- package/adapters/blazor/FdyAppShell.razor.cs +6 -6
- 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 +1 -1
- package/adapters/core/app-shell.js +6 -6
- package/adapters/core/cfl-value.d.ts +11 -0
- package/adapters/core/cfl-value.js +31 -0
- package/adapters/core/table-model.d.ts +2 -2
- package/adapters/core/table-model.js +8 -8
- package/adapters/react/components/FdyAppShell.tsx +5 -5
- 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 +4 -1
- package/adapters/react/index.js +1 -0
- package/adapters/react/useFreeday.js +3 -3
- package/adapters/vue/components/FdyAppShell.vue +5 -5
- 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 +5 -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 +9 -9
- package/dist/freeday-autocomplete.js +2 -2
- package/dist/freeday-breakpoint.js +1 -1
- package/dist/freeday-carousel.js +8 -7
- package/dist/freeday-cascade.js +10 -9
- package/dist/freeday-cfl.js +12 -11
- 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 +17 -16
- package/dist/freeday-mask.js +9 -8
- 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 +10 -9
- package/dist/freeday-table.js +18 -17
- package/dist/freeday-tabs.js +1 -1
- package/dist/freeday-timepicker.js +2 -2
- package/dist/freeday-toast.js +11 -10
- package/dist/freeday-tree.js +3 -3
- package/dist/freeday-upload.js +28 -27
- package/dist/freeday.bundle.css +176 -176
- package/dist/freeday.css +170 -170
- package/dist/freeday.d.ts +2 -2
- package/dist/freeday.js +189 -176
- package/dist/freeday.tokens.css +5 -5
- package/docs/agent-onboarding.md +49 -46
- package/docs/getting-started.md +49 -40
- package/docs/integrations.md +18 -18
- package/docs/reference-screen.html +14 -14
- package/package.json +4 -3
- 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,25 +1,25 @@
|
|
|
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
19
|
* Emits a bubbling `fdy-app-nav` CustomEvent (detail {visible}) whenever the nav's visibility
|
|
20
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
|
|
22
|
-
* wrapper binding @bind-NavOpen, an app persisting the collapsed preference
|
|
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
23
|
* hear the change and to drive it, without owning the behaviour twice.
|
|
24
24
|
* FreedayAppShell.init(root) for late-mounted markup.
|
|
25
25
|
*/
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
+ ' textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
|
|
34
34
|
|
|
35
35
|
/* getClientRects(), not offsetParent: an overlay sidebar is position:fixed, and a fixed element
|
|
36
|
-
reports no offsetParent at all
|
|
36
|
+
reports no offsetParent at all, filtering on it would call every nav item invisible. */
|
|
37
37
|
function focusables(root) {
|
|
38
38
|
return Array.prototype.filter.call(root.querySelectorAll(FOCUSABLE), function (el) {
|
|
39
39
|
return el.getClientRects().length > 0;
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
function isCollapsed() { return app.classList.contains('fdy-app--nav-collapsed'); }
|
|
65
65
|
function navVisible() { return mqWide.matches ? !isCollapsed() : isOverlayOpen(); }
|
|
66
66
|
|
|
67
|
-
/* 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
|
|
68
68
|
kit's business, not the reader's. */
|
|
69
69
|
function sync() {
|
|
70
70
|
var visible = navVisible();
|
|
@@ -93,7 +93,7 @@
|
|
|
93
93
|
}
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
-
/* 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
|
|
97
97
|
yank focus out from under whatever the reader was doing. */
|
|
98
98
|
function close(restoreFocus) {
|
|
99
99
|
if (!isOverlayOpen()) return;
|
|
@@ -120,7 +120,7 @@
|
|
|
120
120
|
|
|
121
121
|
if (backdrop) backdrop.addEventListener('click', function () { close(); });
|
|
122
122
|
|
|
123
|
-
/* 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
|
|
124
124
|
the page you just asked for is the one thing every hand-rolled copy in this repo disagreed on. */
|
|
125
125
|
sidebar.addEventListener('click', function (e) {
|
|
126
126
|
if (!isOverlayOpen()) return;
|
|
@@ -208,10 +208,10 @@
|
|
|
208
208
|
};
|
|
209
209
|
})();
|
|
210
210
|
|
|
211
|
-
/* Freeday
|
|
211
|
+
/* Freeday, autocomplete enhancer (optional, zero-dependency).
|
|
212
212
|
* Editable combobox (WAI-ARIA APG): a text input filters a role="listbox" of role="option"
|
|
213
213
|
* items as you type; select fills the input. Auto-inits [data-fdy-autocomplete]. Options are
|
|
214
|
-
* static markup filtered client-side
|
|
214
|
+
* static markup filtered client-side, in an app, fetch/filter server-side and re-render options.
|
|
215
215
|
*
|
|
216
216
|
* Markup: <div data-fdy-autocomplete class="fdy-autocomplete">
|
|
217
217
|
* <input class="fdy-input" role="combobox" aria-expanded="false" aria-autocomplete="list"
|
|
@@ -347,7 +347,7 @@
|
|
|
347
347
|
window.FreedayAutocomplete = { init: initAutocomplete, initAll: initAll };
|
|
348
348
|
})();
|
|
349
349
|
|
|
350
|
-
/* Freeday
|
|
350
|
+
/* Freeday, breakpoint provider (optional, zero-dependency).
|
|
351
351
|
* The MudBreakpointProvider equivalent for JS consumers: stamps the current breakpoint on
|
|
352
352
|
* <html data-breakpoint="xs|sm|md|lg|xl"> and emits a bubbling "fdy-breakpoint-change"
|
|
353
353
|
* CustomEvent (detail {breakpoint}) whenever it changes. Also fills any element with
|
|
@@ -399,7 +399,7 @@
|
|
|
399
399
|
};
|
|
400
400
|
})();
|
|
401
401
|
|
|
402
|
-
/* Freeday
|
|
402
|
+
/* Freeday, carousel enhancer (optional, zero-dependency).
|
|
403
403
|
* Scroll-snap slider with prev/next arrows, generated dot indicators, keyboard (←/→),
|
|
404
404
|
* and optional autoplay (data-fdy-autoplay="4000", pauses on hover/focus). Auto-inits
|
|
405
405
|
* [data-fdy-carousel].
|
|
@@ -419,16 +419,17 @@
|
|
|
419
419
|
'use strict';
|
|
420
420
|
|
|
421
421
|
|
|
422
|
-
/* User-facing strings.
|
|
423
|
-
* path
|
|
424
|
-
*
|
|
425
|
-
* Keeping them in ONE table is also what lets a guard prove none is
|
|
422
|
+
/* User-facing strings. English by default since 2.0.0, documented and deliberate for the raw
|
|
423
|
+
* enhancer path, and every one overridable per element with `data-fdy-text-<key>`, so a host
|
|
424
|
+
* that speaks another language (an Indonesian app on the raw path) supplies its own without
|
|
425
|
+
* forking this file. Keeping them in ONE table is also what lets a guard prove none is
|
|
426
|
+
* hard-coded further down. */
|
|
426
427
|
var TEXT = {
|
|
427
|
-
position: '{n}
|
|
428
|
+
position: '{n} of {total}',
|
|
428
429
|
slide: 'Slide {n}'
|
|
429
430
|
};
|
|
430
431
|
/* HTML lowercases attribute names, so a camelCase key like `filterText` can only ever be written
|
|
431
|
-
as `data-fdy-text-filtertext
|
|
432
|
+
as `data-fdy-text-filtertext`, while the kebab form anybody would reach for,
|
|
432
433
|
`data-fdy-text-filter-text`, becomes a DIFFERENT attribute the enhancer never reads, and the
|
|
433
434
|
override fails silently. So the key is kebab-cased for the lookup; the run-together spelling
|
|
434
435
|
still resolves, for markup written against 1.39.0. */
|
|
@@ -540,7 +541,7 @@
|
|
|
540
541
|
window.FreedayCarousel = { init: initCarousel, initAll: initAll };
|
|
541
542
|
})();
|
|
542
543
|
|
|
543
|
-
/* Freeday
|
|
544
|
+
/* Freeday, cascade select enhancer (optional, zero-dependency).
|
|
544
545
|
* Turns a [data-fdy-cascade] wrapper into a hierarchical drill-down picker. The
|
|
545
546
|
* data model is a hidden nested <ul> inside the wrapper: a <li> with a child <ul>
|
|
546
547
|
* is a branch (drills in), a <li> without is a leaf (selects). Each <li> carries
|
|
@@ -596,16 +597,17 @@
|
|
|
596
597
|
}
|
|
597
598
|
|
|
598
599
|
|
|
599
|
-
/* User-facing strings.
|
|
600
|
-
* path
|
|
601
|
-
*
|
|
602
|
-
* Keeping them in ONE table is also what lets a guard prove none is
|
|
600
|
+
/* User-facing strings. English by default since 2.0.0, documented and deliberate for the raw
|
|
601
|
+
* enhancer path, and every one overridable per element with `data-fdy-text-<key>`, so a host
|
|
602
|
+
* that speaks another language (an Indonesian app on the raw path) supplies its own without
|
|
603
|
+
* forking this file. Keeping them in ONE table is also what lets a guard prove none is
|
|
604
|
+
* hard-coded further down. */
|
|
603
605
|
var TEXT = {
|
|
604
|
-
back: '
|
|
606
|
+
back: 'Back one level',
|
|
605
607
|
submenu: '{label}, submenu'
|
|
606
608
|
};
|
|
607
609
|
/* HTML lowercases attribute names, so a camelCase key like `filterText` can only ever be written
|
|
608
|
-
as `data-fdy-text-filtertext
|
|
610
|
+
as `data-fdy-text-filtertext`, while the kebab form anybody would reach for,
|
|
609
611
|
`data-fdy-text-filter-text`, becomes a DIFFERENT attribute the enhancer never reads, and the
|
|
610
612
|
override fails silently. So the key is kebab-cased for the lookup; the run-together spelling
|
|
611
613
|
still resolves, for markup written against 1.39.0. */
|
|
@@ -630,8 +632,8 @@
|
|
|
630
632
|
var root = sourceUl ? parse(sourceUl) : [];
|
|
631
633
|
if (sourceUl) sourceUl.remove();
|
|
632
634
|
|
|
633
|
-
var label = wrap.getAttribute('data-label') || '
|
|
634
|
-
var placeholder = wrap.getAttribute('data-placeholder') || '
|
|
635
|
+
var label = wrap.getAttribute('data-label') || 'Select';
|
|
636
|
+
var placeholder = wrap.getAttribute('data-placeholder') || 'Select…';
|
|
635
637
|
var sep = wrap.getAttribute('data-separator') || ' / ';
|
|
636
638
|
|
|
637
639
|
var trigger = document.createElement('button');
|
|
@@ -826,24 +828,24 @@
|
|
|
826
828
|
window.FreedayCascade = { init: initCascade, initAll: initAll };
|
|
827
829
|
})();
|
|
828
830
|
|
|
829
|
-
/* Freeday
|
|
831
|
+
/* Freeday, choose-from-list enhancer (optional, zero-dependency).
|
|
830
832
|
* Wires a searchable master-data picker dialog to read-only fields.
|
|
831
833
|
* Progressive enhancement over a native <dialog>; in a framework app, drive the
|
|
832
834
|
* dialog as a controlled component (fetchPage callback + server cache), not a store.
|
|
833
835
|
*
|
|
834
836
|
* Markup contract (see docs):
|
|
835
|
-
* - Field group: [data-fdy-cfl="<dialogId>"]
|
|
837
|
+
* - Field group: [data-fdy-cfl="<dialogId>"], usually a `.fdy-input-group` holding a
|
|
836
838
|
* read-only `.fdy-input` and a trigger `<button class="fdy-input-group__btn">`.
|
|
837
839
|
* Optional `data-fdy-cfl-display="<key>"` picks which row dataset key fills the input
|
|
838
840
|
* (default "value"); optional `data-fdy-cfl-summary="{n} dipilih"` formats the multi label.
|
|
839
|
-
* - Standalone opener: [data-fdy-cfl-open="<dialogId>"]
|
|
841
|
+
* - Standalone opener: [data-fdy-cfl-open="<dialogId>"], opens the dialog with no bound field.
|
|
840
842
|
* - Dialog: <dialog id="<dialogId>" class="fdy-modal fdy-modal--cfl"> containing
|
|
841
843
|
* a `[data-fdy-cfl-search]` input, `.fdy-cfl__row` <tr> rows carrying `data-*` fields,
|
|
842
844
|
* a `[data-fdy-cfl-empty]` element, and (multi only) `[data-fdy-cfl-multiple]` on the
|
|
843
845
|
* dialog plus `[data-fdy-cfl-count]` and `[data-fdy-cfl-confirm]` in the footer.
|
|
844
846
|
*
|
|
845
847
|
* Emits a bubbling "fdy-cfl-select" CustomEvent on the field group (or the dialog when
|
|
846
|
-
* opened standalone): detail {row} for single-select, {rows} for multi-select
|
|
848
|
+
* opened standalone): detail {row} for single-select, {rows} for multi-select, each a plain
|
|
847
849
|
* object copy of the row's dataset. Consumers fill sibling fields (code → name) from it.
|
|
848
850
|
*/
|
|
849
851
|
(function () {
|
|
@@ -862,15 +864,16 @@
|
|
|
862
864
|
}
|
|
863
865
|
|
|
864
866
|
|
|
865
|
-
/* User-facing strings.
|
|
866
|
-
* path
|
|
867
|
-
*
|
|
868
|
-
* Keeping them in ONE table is also what lets a guard prove none is
|
|
867
|
+
/* User-facing strings. English by default since 2.0.0, documented and deliberate for the raw
|
|
868
|
+
* enhancer path, and every one overridable per element with `data-fdy-text-<key>`, so a host
|
|
869
|
+
* that speaks another language (an Indonesian app on the raw path) supplies its own without
|
|
870
|
+
* forking this file. Keeping them in ONE table is also what lets a guard prove none is
|
|
871
|
+
* hard-coded further down. */
|
|
869
872
|
var TEXT = {
|
|
870
|
-
selected: '{n}
|
|
873
|
+
selected: '{n} selected'
|
|
871
874
|
};
|
|
872
875
|
/* HTML lowercases attribute names, so a camelCase key like `filterText` can only ever be written
|
|
873
|
-
as `data-fdy-text-filtertext
|
|
876
|
+
as `data-fdy-text-filtertext`, while the kebab form anybody would reach for,
|
|
874
877
|
`data-fdy-text-filter-text`, becomes a DIFFERENT attribute the enhancer never reads, and the
|
|
875
878
|
override fails silently. So the key is kebab-cased for the lookup; the run-together spelling
|
|
876
879
|
still resolves, for markup written against 1.39.0. */
|
|
@@ -931,7 +934,7 @@
|
|
|
931
934
|
var input = activeGroup.querySelector('.fdy-input');
|
|
932
935
|
if (input) {
|
|
933
936
|
if (detail.rows) {
|
|
934
|
-
var tpl = activeGroup.getAttribute('data-fdy-cfl-summary') || '{n}
|
|
937
|
+
var tpl = activeGroup.getAttribute('data-fdy-cfl-summary') || '{n} selected';
|
|
935
938
|
input.value = detail.rows.length ? tpl.replace('{n}', String(detail.rows.length)) : '';
|
|
936
939
|
} else {
|
|
937
940
|
var key = activeGroup.getAttribute('data-fdy-cfl-display') || 'value';
|
|
@@ -1070,7 +1073,7 @@
|
|
|
1070
1073
|
window.FreedayCfl = { init: initTriggers };
|
|
1071
1074
|
})();
|
|
1072
1075
|
|
|
1073
|
-
/* Freeday
|
|
1076
|
+
/* Freeday, chart renderer (optional, zero-dependency, no external chart lib).
|
|
1074
1077
|
* Renders sparkline / bar / line / area / donut into an element from data attributes.
|
|
1075
1078
|
* Auto-inits [data-fdy-chart] once; call FreedayChart.update(el) to re-render after the
|
|
1076
1079
|
* data attributes change (used by the freeday/vue <FdyChart> wrapper for reactive data).
|
|
@@ -1078,14 +1081,14 @@
|
|
|
1078
1081
|
* Single-series colour (sparkline / simple bar / single line) via data-fdy-color references a
|
|
1079
1082
|
* semantic token by name (primary, accent, success, warning, danger, info). Multi-series
|
|
1080
1083
|
* defaults draw from the validated categorical --chart-1..8 palette in fixed order (never
|
|
1081
|
-
* cycled, 8-series cap); pass data-fdy-colors to override
|
|
1084
|
+
* cycled, 8-series cap); pass data-fdy-colors to override, a name is a semantic token
|
|
1082
1085
|
* (var(--color-<name>)) OR a slot "chart-1".."chart-8" (var(--chart-N)) to pin a series to the
|
|
1083
1086
|
* validated palette. Both data-fdy-color and data-fdy-colors accept the chart-N form.
|
|
1084
1087
|
* Cartesian charts (line/area, multi-series/stacked bar) draw themed axes from --chart-grid /
|
|
1085
1088
|
* --chart-tick and format ticks + tooltips via data-fdy-format (number|percent|currency). Their
|
|
1086
1089
|
* viewBox is sized to the measured plot (1 user unit = 1 CSS pixel) and repainted from a
|
|
1087
1090
|
* ResizeObserver, so axis type stays in real pixels and is set in CSS via --fdy-chart-tick-size.
|
|
1088
|
-
* Charts are an enhancement
|
|
1091
|
+
* Charts are an enhancement, put a table/text fallback inside the element and it will be
|
|
1089
1092
|
* replaced on render; give the element role="img" + aria-label.
|
|
1090
1093
|
*
|
|
1091
1094
|
* Sparkline: <span data-fdy-chart="sparkline" data-values="4,6,5,8,7,10" data-fdy-color="primary">
|
|
@@ -1122,7 +1125,7 @@
|
|
|
1122
1125
|
return v ? v.split(',').map(function (s) { return s.trim(); }) : [];
|
|
1123
1126
|
}
|
|
1124
1127
|
// role="img" is Children Presentational per ARIA, so the docs used to say a chart's rendered
|
|
1125
|
-
// legend, bar values and donut centre are not exposed. No browser actually prunes that subtree
|
|
1128
|
+
// legend, bar values and donut centre are not exposed. No browser actually prunes that subtree,
|
|
1126
1129
|
// measured in Chrome's AX tree, every one of those nodes is live and unignored; what saves the
|
|
1127
1130
|
// advice is only that AT treats a NAMED role="img" as a leaf and does not descend. So the kit
|
|
1128
1131
|
// hides them itself, and the author's aria-label really is the whole text alternative.
|
|
@@ -1297,7 +1300,7 @@
|
|
|
1297
1300
|
// Lay the chrome out first so the plot can be measured, then size the viewBox to it in CSS
|
|
1298
1301
|
// pixels: one user unit is one pixel, so font-size / r / stroke-width mean what they say at
|
|
1299
1302
|
// every container width. (A fixed 320x180 viewBox stretched by CSS multiplied every declared
|
|
1300
|
-
// size by plotWidth/320 instead
|
|
1303
|
+
// size by plotWidth/320 instead, a 9px axis label rendered ~39px on a 1728px viewport.)
|
|
1301
1304
|
var legendMode = el.getAttribute('data-fdy-legend') || 'auto';
|
|
1302
1305
|
var showLegend = legendMode !== 'none' && (series.length >= 2 || legendMode === 'always');
|
|
1303
1306
|
el.classList.add('fdy-chart-xy');
|
|
@@ -1318,7 +1321,7 @@
|
|
|
1318
1321
|
el.appendChild(plot);
|
|
1319
1322
|
|
|
1320
1323
|
// Measured while empty: .fdy-chart-xy__plot carries its own width / aspect-ratio / min-height,
|
|
1321
|
-
// so the box is already final. A detached or display:none chart measures 0
|
|
1324
|
+
// so the box is already final. A detached or display:none chart measures 0, fall back to the
|
|
1322
1325
|
// legacy box and let the ResizeObserver repaint it once it is actually laid out.
|
|
1323
1326
|
var box = plot.getBoundingClientRect();
|
|
1324
1327
|
var W = Math.round(box.width) || 320, H = Math.round(box.height) || 180;
|
|
@@ -1362,7 +1365,7 @@
|
|
|
1362
1365
|
|
|
1363
1366
|
// x category labels with autoskip. Labels are author-supplied, so estimate the widest one instead
|
|
1364
1367
|
// of assuming a fixed 40px slot; keep every step-th plus the last, then drop any kept label that
|
|
1365
|
-
// would collide with the one before it
|
|
1368
|
+
// would collide with the one before it, including the forced last one (a fraction-of-a-slot
|
|
1366
1369
|
// overlap happens when n-1 lands next to the last kept multiple of step, e.g. n=24 at most widths).
|
|
1367
1370
|
var widest = 0;
|
|
1368
1371
|
for (var wi = 0; wi < n; wi++) widest = Math.max(widest, String(labels[wi] || ('#' + (wi + 1))).length * charW);
|
|
@@ -1373,7 +1376,7 @@
|
|
|
1373
1376
|
for (var xi = 0; xi < n; xi++) {
|
|
1374
1377
|
if (xi % step === 0 || xi === n - 1) kept.push(xi);
|
|
1375
1378
|
}
|
|
1376
|
-
// The last label is worth forcing, but not on top of its neighbour
|
|
1379
|
+
// The last label is worth forcing, but not on top of its neighbour, drop the penultimate one.
|
|
1377
1380
|
if (kept.length >= 2 && xPos(kept[kept.length - 1]) - xPos(kept[kept.length - 2]) < minGap) {
|
|
1378
1381
|
kept.splice(kept.length - 2, 1);
|
|
1379
1382
|
}
|
|
@@ -1506,17 +1509,17 @@
|
|
|
1506
1509
|
var li = document.createElement('li');
|
|
1507
1510
|
var sw = document.createElement('span'); sw.className = 'fdy-chart__swatch'; sw.style.background = colorAt(i);
|
|
1508
1511
|
li.appendChild(sw);
|
|
1509
|
-
li.appendChild(document.createTextNode((labels[i] || ('#' + (i + 1))) + '
|
|
1512
|
+
li.appendChild(document.createTextNode((labels[i] || ('#' + (i + 1))) + ' · ' + Math.round((v / total) * 100) + '%'));
|
|
1510
1513
|
legend.appendChild(li);
|
|
1511
1514
|
});
|
|
1512
1515
|
el.appendChild(ring);
|
|
1513
|
-
// Honour data-fdy-legend for the donut too
|
|
1516
|
+
// Honour data-fdy-legend for the donut too, a caller may supply its own richer legend. 'auto'
|
|
1514
1517
|
// keeps today's behaviour (a donut is unreadable without labels, so it shows); 'none' suppresses it.
|
|
1515
1518
|
if ((el.getAttribute('data-fdy-legend') || 'auto') !== 'none') el.appendChild(legend);
|
|
1516
1519
|
ensureImg(el);
|
|
1517
1520
|
}
|
|
1518
1521
|
|
|
1519
|
-
// Full (re-)render from current data attributes. Idempotent
|
|
1522
|
+
// Full (re-)render from current data attributes. Idempotent, safe to call repeatedly.
|
|
1520
1523
|
function renderChart(el) {
|
|
1521
1524
|
var type = el.getAttribute('data-fdy-chart');
|
|
1522
1525
|
if (type === 'sparkline') renderSparkline(el);
|
|
@@ -1551,12 +1554,12 @@
|
|
|
1551
1554
|
window.FreedayChart = { init: initChart, initAll: initAll, update: renderChart };
|
|
1552
1555
|
})();
|
|
1553
1556
|
|
|
1554
|
-
/* Freeday
|
|
1557
|
+
/* Freeday, chip enhancer (optional, zero-dependency).
|
|
1555
1558
|
* Two behaviours, both progressive enhancement:
|
|
1556
1559
|
*
|
|
1557
|
-
* 1. Removable chips
|
|
1560
|
+
* 1. Removable chips, any .fdy-chip__remove button removes its chip and emits a
|
|
1558
1561
|
* bubbling "fdy-chip-remove" (detail {value}).
|
|
1559
|
-
* 2. Choice / filter groups
|
|
1562
|
+
* 2. Choice / filter groups, a [data-fdy-chips] container of interactive chips
|
|
1560
1563
|
* (.fdy-chip--choice / .fdy-chip--filter, each a <button>) toggles aria-pressed
|
|
1561
1564
|
* on click. Add data-single for one-at-a-time (radio-like) selection; filter
|
|
1562
1565
|
* chips get a leading check when pressed. Emits "fdy-chip-change" on the group
|
|
@@ -1638,9 +1641,9 @@
|
|
|
1638
1641
|
window.FreedayChip = { initGroup: initGroup, initRemove: initRemove, initAll: initAll };
|
|
1639
1642
|
})();
|
|
1640
1643
|
|
|
1641
|
-
/* Freeday
|
|
1644
|
+
/* Freeday, date picker enhancer (optional, zero-dependency).
|
|
1642
1645
|
* Builds an input-styled trigger + calendar popover from an empty wrapper.
|
|
1643
|
-
* Locale comes from <html lang> (via Intl)
|
|
1646
|
+
* Locale comes from <html lang> (via Intl), month/weekday/value formatting is not hardcoded.
|
|
1644
1647
|
*
|
|
1645
1648
|
* Markup contract:
|
|
1646
1649
|
* - Single: <div data-fdy-datepicker data-value="2026-07-21" data-label="Tanggal unggah"
|
|
@@ -1659,7 +1662,11 @@
|
|
|
1659
1662
|
(function () {
|
|
1660
1663
|
'use strict';
|
|
1661
1664
|
|
|
1662
|
-
|
|
1665
|
+
/* The page's own `lang` wins, which is the better escape hatch than any attribute the kit
|
|
1666
|
+
could invent: an Indonesian app writes <html lang="id"> and gets Indonesian month and
|
|
1667
|
+
weekday names back automatically. The FALLBACK follows the kit's default language, or a
|
|
1668
|
+
page without `lang` would read English labels around Indonesian month names. */
|
|
1669
|
+
var LOCALE = document.documentElement.getAttribute('lang') || 'en';
|
|
1663
1670
|
var uidSeq = 0;
|
|
1664
1671
|
function uid(p) { uidSeq += 1; return p + '-' + uidSeq; }
|
|
1665
1672
|
function pad(n) { return n < 10 ? '0' + n : '' + n; }
|
|
@@ -1706,10 +1713,10 @@
|
|
|
1706
1713
|
var view = startOfDay(selected || new Date());
|
|
1707
1714
|
view = new Date(view.getFullYear(), view.getMonth(), 1);
|
|
1708
1715
|
var focusDate = null;
|
|
1709
|
-
/* 'days' | 'months'
|
|
1716
|
+
/* 'days' | 'months', the calendar drills one level up rather than growing furniture beside the
|
|
1710
1717
|
title. Before this, the only pointer route to another month was one click per month: from
|
|
1711
1718
|
August 2026 to March 2022 is fifty-three of them. Shift+PageUp already jumped a year, but a
|
|
1712
|
-
shortcut nobody can see is not an affordance
|
|
1719
|
+
shortcut nobody can see is not an affordance, the repo's own test helper clicked "previous
|
|
1713
1720
|
month" twenty-four times to reach a date two years back. */
|
|
1714
1721
|
var mode = 'days';
|
|
1715
1722
|
var focusMonth = null;
|
|
@@ -1908,7 +1915,7 @@
|
|
|
1908
1915
|
var YEARS_PER_PAGE = 12;
|
|
1909
1916
|
function yearPageStart(y) { return Math.floor(y / YEARS_PER_PAGE) * YEARS_PER_PAGE; }
|
|
1910
1917
|
|
|
1911
|
-
/* A year is only unreachable when EVERY month in it falls outside min/max
|
|
1918
|
+
/* A year is only unreachable when EVERY month in it falls outside min/max, the same rule
|
|
1912
1919
|
monthDisabled applies one level down, for the same reason: a bound that sits inside the year
|
|
1913
1920
|
disables neither end. */
|
|
1914
1921
|
function yearDisabled(y) {
|
|
@@ -2153,7 +2160,7 @@
|
|
|
2153
2160
|
var f = fromApi.getValue();
|
|
2154
2161
|
var t = toApi.getValue();
|
|
2155
2162
|
toApi.setMin(f || null); // end can't precede start (one-directional, per Foundation)
|
|
2156
|
-
fromApi.setRange(f, t); // start stays free
|
|
2163
|
+
fromApi.setRange(f, t); // start stays free, picking a later start clears the end below
|
|
2157
2164
|
toApi.setRange(f, t);
|
|
2158
2165
|
}
|
|
2159
2166
|
fromEl.addEventListener('fdy-datepicker-change', function () {
|
|
@@ -2187,9 +2194,9 @@
|
|
|
2187
2194
|
window.FreedayDatepicker = { init: initPicker, initAll: initAll };
|
|
2188
2195
|
})();
|
|
2189
2196
|
|
|
2190
|
-
/* Freeday
|
|
2197
|
+
/* Freeday, datetime picker composer (optional, zero-dependency).
|
|
2191
2198
|
* Combines a child date picker and time picker into one value. It does not build
|
|
2192
|
-
* anything itself
|
|
2199
|
+
* anything itself, freeday-datepicker.js and freeday-timepicker.js enhance the
|
|
2193
2200
|
* children; this only listens for their change events and emits a single combined
|
|
2194
2201
|
* "fdy-datetime-change" (detail {date, time, value}). value is "YYYY-MM-DDTHH:MM"
|
|
2195
2202
|
* when both parts are set, else "". Icon variants are controlled per child picker
|
|
@@ -2199,7 +2206,7 @@
|
|
|
2199
2206
|
* <div data-fdy-datepicker data-value="2026-07-21"></div>
|
|
2200
2207
|
* <div data-fdy-timepicker data-value="14:30"></div></div>
|
|
2201
2208
|
* State: add data-fdy-disabled or data-fdy-invalid on the wrapper to reflect it onto both
|
|
2202
|
-
* child triggers as one control (read-only is adapter-only
|
|
2209
|
+
* child triggers as one control (read-only is adapter-only, a vanilla datetime is interactive).
|
|
2203
2210
|
*/
|
|
2204
2211
|
(function () {
|
|
2205
2212
|
'use strict';
|
|
@@ -2214,7 +2221,7 @@
|
|
|
2214
2221
|
if (!dp || !tp) return;
|
|
2215
2222
|
|
|
2216
2223
|
// Reflect one wrapper-level state onto BOTH child triggers so a datetime reads as a single
|
|
2217
|
-
// disabled/invalid control. Deferred with setTimeout(0)
|
|
2224
|
+
// disabled/invalid control. Deferred with setTimeout(0). NOT a microtask: the timepicker
|
|
2218
2225
|
// enhancer registers its DOMContentLoaded init AFTER this composer, and microtasks drain
|
|
2219
2226
|
// *between* listeners (so a microtask would run before the timepicker trigger exists). A
|
|
2220
2227
|
// macrotask waits until the whole init pass is done and both triggers are built. (readonly is
|
|
@@ -2245,7 +2252,7 @@
|
|
|
2245
2252
|
}));
|
|
2246
2253
|
}
|
|
2247
2254
|
|
|
2248
|
-
// Listen for the children's bubbling change events (order-independent
|
|
2255
|
+
// Listen for the children's bubbling change events (order-independent, no need
|
|
2249
2256
|
// for the child enhancers to have run before this composer).
|
|
2250
2257
|
wrap.addEventListener('fdy-datepicker-change', function (e) { date = e.detail.value; emit(); });
|
|
2251
2258
|
wrap.addEventListener('fdy-time-select', function (e) { time = e.detail.value; emit(); });
|
|
@@ -2274,7 +2281,7 @@
|
|
|
2274
2281
|
window.FreedayDatetime = { init: initDatetime, initAll: initAll };
|
|
2275
2282
|
})();
|
|
2276
2283
|
|
|
2277
|
-
/* Freeday
|
|
2284
|
+
/* Freeday, drawer enhancer (optional, zero-dependency).
|
|
2278
2285
|
* A trigger [data-fdy-drawer="drawerId"] opens the <dialog class="fdy-drawer" id="drawerId">
|
|
2279
2286
|
* as a modal side panel (native showModal gives focus-trap, Esc, and return-focus). Clicking
|
|
2280
2287
|
* the backdrop or any [data-close] inside closes it.
|
|
@@ -2323,7 +2330,7 @@
|
|
|
2323
2330
|
window.FreedayDrawer = { init: initTriggers, initAll: initTriggers };
|
|
2324
2331
|
})();
|
|
2325
2332
|
|
|
2326
|
-
/* Freeday
|
|
2333
|
+
/* Freeday, form validation enhancer (optional, zero-dependency).
|
|
2327
2334
|
* Wires the native Constraint Validation API to accessible inline errors on any
|
|
2328
2335
|
* [data-fdy-validate] <form>. Native-first: constraints come from the standard
|
|
2329
2336
|
* markup attributes (required, type, pattern, min/max, minlength/maxlength, step);
|
|
@@ -2363,24 +2370,25 @@
|
|
|
2363
2370
|
badInput: 'type',
|
|
2364
2371
|
customError: 'mismatch'
|
|
2365
2372
|
};
|
|
2366
|
-
/* User-facing strings.
|
|
2367
|
-
* path
|
|
2368
|
-
* `data-fdy-msg` on the field, then `data-fdy-text-<alias>` on the FORM. The form
|
|
2369
|
-
* a host in another language needs: it sets nine messages once instead of on
|
|
2373
|
+
/* User-facing strings. English by default since 2.0.0, documented and deliberate for the raw
|
|
2374
|
+
* enhancer path, and overridable at three levels, narrowest first: `data-fdy-msg-<alias>` on
|
|
2375
|
+
* the field, `data-fdy-msg` on the field, then `data-fdy-text-<alias>` on the FORM. The form
|
|
2376
|
+
* level is what a host in another language needs: it sets nine messages once instead of on
|
|
2377
|
+
* every input.
|
|
2370
2378
|
* Keeping them in ONE table is also what lets a guard prove none is hard-coded further down. */
|
|
2371
2379
|
var TEXT = {
|
|
2372
|
-
required: '
|
|
2373
|
-
type: '
|
|
2374
|
-
pattern: '
|
|
2375
|
-
minlength: '
|
|
2376
|
-
maxlength: '
|
|
2377
|
-
min: '
|
|
2378
|
-
max: '
|
|
2379
|
-
step: '
|
|
2380
|
-
mismatch: '
|
|
2381
|
-
invalid: '
|
|
2380
|
+
required: 'Required.',
|
|
2381
|
+
type: 'Invalid format.',
|
|
2382
|
+
pattern: 'Doesn’t match the required format.',
|
|
2383
|
+
minlength: 'Too short.',
|
|
2384
|
+
maxlength: 'Too long.',
|
|
2385
|
+
min: 'Too small.',
|
|
2386
|
+
max: 'Too large.',
|
|
2387
|
+
step: 'Not a valid step.',
|
|
2388
|
+
mismatch: 'Values don’t match.',
|
|
2389
|
+
invalid: 'Invalid.'
|
|
2382
2390
|
};
|
|
2383
|
-
/* Kebab-cased for the lookup
|
|
2391
|
+
/* Kebab-cased for the lookup, see the note in the other enhancers: HTML lowercases attribute
|
|
2384
2392
|
names, so `data-fdy-text-filter-text` and `data-fdy-text-filtertext` are different attributes
|
|
2385
2393
|
and only one of them is what an author would write. */
|
|
2386
2394
|
function textOf(root, key) {
|
|
@@ -2524,7 +2532,7 @@
|
|
|
2524
2532
|
window.FreedayForm = { init: initForm, initAll: initAll };
|
|
2525
2533
|
})();
|
|
2526
2534
|
|
|
2527
|
-
/* Freeday
|
|
2535
|
+
/* Freeday, input mask + password reveal enhancer (optional, zero-dependency).
|
|
2528
2536
|
*
|
|
2529
2537
|
* [data-fdy-mask]="pattern" formats a text input as you type. Placeholders:
|
|
2530
2538
|
* # a digit (0-9) A a letter (a-z/A-Z) * a letter or digit
|
|
@@ -2577,16 +2585,17 @@
|
|
|
2577
2585
|
}
|
|
2578
2586
|
|
|
2579
2587
|
|
|
2580
|
-
/* User-facing strings.
|
|
2581
|
-
* path
|
|
2582
|
-
*
|
|
2583
|
-
* Keeping them in ONE table is also what lets a guard prove none is
|
|
2588
|
+
/* User-facing strings. English by default since 2.0.0, documented and deliberate for the raw
|
|
2589
|
+
* enhancer path, and every one overridable per element with `data-fdy-text-<key>`, so a host
|
|
2590
|
+
* that speaks another language (an Indonesian app on the raw path) supplies its own without
|
|
2591
|
+
* forking this file. Keeping them in ONE table is also what lets a guard prove none is
|
|
2592
|
+
* hard-coded further down. */
|
|
2584
2593
|
var TEXT = {
|
|
2585
|
-
show: '
|
|
2586
|
-
hide: '
|
|
2594
|
+
show: 'Show password',
|
|
2595
|
+
hide: 'Hide password'
|
|
2587
2596
|
};
|
|
2588
2597
|
/* HTML lowercases attribute names, so a camelCase key like `filterText` can only ever be written
|
|
2589
|
-
as `data-fdy-text-filtertext
|
|
2598
|
+
as `data-fdy-text-filtertext`, while the kebab form anybody would reach for,
|
|
2590
2599
|
`data-fdy-text-filter-text`, becomes a DIFFERENT attribute the enhancer never reads, and the
|
|
2591
2600
|
override fails silently. So the key is kebab-cased for the lookup; the run-together spelling
|
|
2592
2601
|
still resolves, for markup written against 1.39.0. */
|
|
@@ -2668,7 +2677,7 @@
|
|
|
2668
2677
|
window.FreedayMask = { initMask: initMask, initReveal: initReveal, initAll: initAll };
|
|
2669
2678
|
})();
|
|
2670
2679
|
|
|
2671
|
-
/* Freeday
|
|
2680
|
+
/* Freeday, menu enhancer (optional, zero-dependency).
|
|
2672
2681
|
* Menu-button pattern (WAI-ARIA APG): a trigger with aria-haspopup="menu" toggles a
|
|
2673
2682
|
* role="menu" popup of role="menuitem" buttons. Also powers the split button
|
|
2674
2683
|
* (main action + attached caret toggle). Auto-inits [data-fdy-menu].
|
|
@@ -2765,10 +2774,10 @@
|
|
|
2765
2774
|
window.FreedayMenu = { init: initMenu, initAll: initAll };
|
|
2766
2775
|
})();
|
|
2767
2776
|
|
|
2768
|
-
/* Freeday
|
|
2777
|
+
/* Freeday, number field enhancer (optional, zero-dependency).
|
|
2769
2778
|
* Gives a native <input type="number"> its increment/decrement affordance back, on the kit's terms:
|
|
2770
2779
|
* .fdy-input hides the user agent's own spin buttons (unthemeable OS widgets), so this puts two real
|
|
2771
|
-
* buttons in an .fdy-input-group instead. No new block
|
|
2780
|
+
* buttons in an .fdy-input-group instead. No new block, the group already owns the shared border,
|
|
2772
2781
|
* :focus-within ring and error promotion.
|
|
2773
2782
|
*
|
|
2774
2783
|
* Markup contract:
|
|
@@ -2834,7 +2843,7 @@
|
|
|
2834
2843
|
if (frozen()) return;
|
|
2835
2844
|
if (Number(btn.getAttribute('data-fdy-number-step')) < 0) input.stepDown();
|
|
2836
2845
|
else input.stepUp();
|
|
2837
|
-
/* The value changed without the user typing, so say so the way the platform does
|
|
2846
|
+
/* The value changed without the user typing, so say so the way the platform does, frameworks
|
|
2838
2847
|
listen to these, not to a kit-specific event. */
|
|
2839
2848
|
input.dispatchEvent(new Event('input', { bubbles: true }));
|
|
2840
2849
|
input.dispatchEvent(new Event('change', { bubbles: true }));
|
|
@@ -2870,10 +2879,10 @@
|
|
|
2870
2879
|
window.FreedayNumber = { init: initNumber, initAll: initAll };
|
|
2871
2880
|
})();
|
|
2872
2881
|
|
|
2873
|
-
/* Freeday
|
|
2882
|
+
/* Freeday, popover positioning helper (optional, zero-dependency).
|
|
2874
2883
|
* Lifts a dropdown panel into the top layer via the native Popover API so it escapes ANY
|
|
2875
2884
|
* ancestor overflow clip (a .fdy-card with overflow:hidden, an app-shell main with
|
|
2876
|
-
* overflow:auto, …), then positions it `fixed` against its trigger
|
|
2885
|
+
* overflow:auto, …), then positions it `fixed` against its trigger, flipping above when
|
|
2877
2886
|
* there is no room below, and matching the trigger width. The panel stays a DOM child of its
|
|
2878
2887
|
* component, so focus, outside-click (.closest), and ARIA relationships keep working unchanged.
|
|
2879
2888
|
*
|
|
@@ -2893,7 +2902,7 @@
|
|
|
2893
2902
|
&& typeof HTMLElement.prototype.showPopover === 'function';
|
|
2894
2903
|
|
|
2895
2904
|
// Position `panel` (already shown, so it has a measurable box) against `trigger`'s rect.
|
|
2896
|
-
// matchWidth (default true) floors the panel to the trigger width
|
|
2905
|
+
// matchWidth (default true) floors the panel to the trigger width, right for input-shaped
|
|
2897
2906
|
// dropdowns (combo/cascade/date/time), skipped for menus that keep their own min-width.
|
|
2898
2907
|
function place(panel, trigger, matchWidth) {
|
|
2899
2908
|
var r = trigger.getBoundingClientRect();
|
|
@@ -2940,10 +2949,10 @@
|
|
|
2940
2949
|
window.FreedayPopover = { attach: attach, place: place, supported: supported };
|
|
2941
2950
|
})();
|
|
2942
2951
|
|
|
2943
|
-
/* Freeday
|
|
2952
|
+
/* Freeday, rating enhancer (optional, zero-dependency).
|
|
2944
2953
|
* Fills stars up to the checked radio (and previews on hover) for an interactive
|
|
2945
2954
|
* [data-fdy-rating] radio group; for [data-fdy-readonly] it fills from data-value.
|
|
2946
|
-
* The radios are native
|
|
2955
|
+
* The radios are native, arrow-key selection and form association come for free.
|
|
2947
2956
|
*/
|
|
2948
2957
|
(function () {
|
|
2949
2958
|
'use strict';
|
|
@@ -2994,11 +3003,11 @@
|
|
|
2994
3003
|
window.FreedayRating = { init: initRating, initAll: initAll };
|
|
2995
3004
|
})();
|
|
2996
3005
|
|
|
2997
|
-
/* Freeday
|
|
3006
|
+
/* Freeday, combobox enhancer (optional, zero-dependency).
|
|
2998
3007
|
* Implements the WAI-ARIA APG "select-only combobox" pattern for every
|
|
2999
3008
|
* [data-fdy-combo] on the page. Progressive enhancement: the markup is the
|
|
3000
3009
|
* component, this script wires behavior. In a Vue/React/Blazor app you would
|
|
3001
|
-
* bind your own state instead
|
|
3010
|
+
* bind your own state instead, this is the framework-agnostic reference.
|
|
3002
3011
|
*
|
|
3003
3012
|
* Expected markup:
|
|
3004
3013
|
* <div class="fdy-combo" data-fdy-combo data-value="x">
|
|
@@ -3021,7 +3030,7 @@
|
|
|
3021
3030
|
var seq = 0;
|
|
3022
3031
|
var openClose = null; // close() of the currently-open combo, or null
|
|
3023
3032
|
|
|
3024
|
-
/* The tick lives in CSS now, so the option's text IS its label
|
|
3033
|
+
/* The tick lives in CSS now, so the option's text IS its label, no sibling walk to skip a glyph. */
|
|
3025
3034
|
function optionLabel(opt) {
|
|
3026
3035
|
return (opt.textContent || '').trim();
|
|
3027
3036
|
}
|
|
@@ -3087,7 +3096,7 @@
|
|
|
3087
3096
|
if (openClose === close) openClose = null;
|
|
3088
3097
|
}
|
|
3089
3098
|
// Mark an option selected + reflect it in the button. `silent` skips the change
|
|
3090
|
-
// event, close, and focus
|
|
3099
|
+
// event, close, and focus, used by the programmatic setValue() below so a host
|
|
3091
3100
|
// (e.g. a Blazor/framework wrapper) can push a value in without echoing an event
|
|
3092
3101
|
// back to itself or stealing focus. User-driven picks go through choose() (loud).
|
|
3093
3102
|
function select(opt, silent) {
|
|
@@ -3122,7 +3131,7 @@
|
|
|
3122
3131
|
isOpen() ? close() : open();
|
|
3123
3132
|
});
|
|
3124
3133
|
// Pressing an option must not blur the button: a blur fires the focusout
|
|
3125
|
-
// handler below, which closes the listbox before the option's click lands
|
|
3134
|
+
// handler below, which closes the listbox before the option's click lands,
|
|
3126
3135
|
// so the pick is lost (real mouse only; synthetic .click() never blurs).
|
|
3127
3136
|
// preventDefault on mousedown keeps focus on the button without blocking click.
|
|
3128
3137
|
listbox.addEventListener('mousedown', function (e) { e.preventDefault(); });
|
|
@@ -3209,10 +3218,10 @@
|
|
|
3209
3218
|
window.FreedayCombo = { init: initCombo, initAll: initAll, setValue: setValue };
|
|
3210
3219
|
})();
|
|
3211
3220
|
|
|
3212
|
-
/* Freeday
|
|
3221
|
+
/* Freeday, slider value binding (optional, zero-dependency).
|
|
3213
3222
|
* Mirrors a [data-fdy-slider] range input's value into <output for="inputId">, with an
|
|
3214
3223
|
* optional prefix/suffix on the output (data-fdy-prefix / data-fdy-suffix). The range itself
|
|
3215
|
-
* is a native input
|
|
3224
|
+
* is a native input, keyboard and ARIA are built in; this only updates the visible readout.
|
|
3216
3225
|
*/
|
|
3217
3226
|
(function () {
|
|
3218
3227
|
'use strict';
|
|
@@ -3247,9 +3256,9 @@
|
|
|
3247
3256
|
window.FreedaySlider = { init: initSlider, initAll: initAll };
|
|
3248
3257
|
})();
|
|
3249
3258
|
|
|
3250
|
-
/* Freeday
|
|
3259
|
+
/* Freeday, stepper/wizard enhancer (optional, zero-dependency).
|
|
3251
3260
|
* Drives a step header + panels: prev/next navigation, completed/active state, and
|
|
3252
|
-
* clicking a reached step to revisit it (linear
|
|
3261
|
+
* clicking a reached step to revisit it (linear, can't skip ahead). Auto-inits
|
|
3253
3262
|
* [data-fdy-stepper].
|
|
3254
3263
|
*
|
|
3255
3264
|
* Markup: <div data-fdy-stepper>
|
|
@@ -3268,16 +3277,17 @@
|
|
|
3268
3277
|
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>';
|
|
3269
3278
|
|
|
3270
3279
|
|
|
3271
|
-
/* User-facing strings.
|
|
3272
|
-
* path
|
|
3273
|
-
*
|
|
3274
|
-
* Keeping them in ONE table is also what lets a guard prove none is
|
|
3280
|
+
/* User-facing strings. English by default since 2.0.0, documented and deliberate for the raw
|
|
3281
|
+
* enhancer path, and every one overridable per element with `data-fdy-text-<key>`, so a host
|
|
3282
|
+
* that speaks another language (an Indonesian app on the raw path) supplies its own without
|
|
3283
|
+
* forking this file. Keeping them in ONE table is also what lets a guard prove none is
|
|
3284
|
+
* hard-coded further down. */
|
|
3275
3285
|
var TEXT = {
|
|
3276
|
-
done: '
|
|
3277
|
-
next: '
|
|
3286
|
+
done: 'Done',
|
|
3287
|
+
next: 'Next'
|
|
3278
3288
|
};
|
|
3279
3289
|
/* HTML lowercases attribute names, so a camelCase key like `filterText` can only ever be written
|
|
3280
|
-
as `data-fdy-text-filtertext
|
|
3290
|
+
as `data-fdy-text-filtertext`, while the kebab form anybody would reach for,
|
|
3281
3291
|
`data-fdy-text-filter-text`, becomes a DIFFERENT attribute the enhancer never reads, and the
|
|
3282
3292
|
override fails silently. So the key is kebab-cased for the lookup; the run-together spelling
|
|
3283
3293
|
still resolves, for markup written against 1.39.0. */
|
|
@@ -3367,7 +3377,7 @@
|
|
|
3367
3377
|
window.FreedayStepper = { init: initStepper, initAll: initAll };
|
|
3368
3378
|
})();
|
|
3369
3379
|
|
|
3370
|
-
/* Freeday
|
|
3380
|
+
/* Freeday, data-table enhancer (optional, zero-dependency).
|
|
3371
3381
|
* Adds client-side search, column sort, pagination and row selection to any
|
|
3372
3382
|
* [data-fdy-table]. Progressive enhancement over a plain semantic <table>;
|
|
3373
3383
|
* in a framework app, do this server- or store-side instead.
|
|
@@ -3403,26 +3413,27 @@
|
|
|
3403
3413
|
}
|
|
3404
3414
|
|
|
3405
3415
|
|
|
3406
|
-
/* User-facing strings.
|
|
3407
|
-
* path
|
|
3408
|
-
*
|
|
3409
|
-
* Keeping them in ONE table is also what lets a guard prove none is
|
|
3416
|
+
/* User-facing strings. English by default since 2.0.0, documented and deliberate for the raw
|
|
3417
|
+
* enhancer path, and every one overridable per element with `data-fdy-text-<key>`, so a host
|
|
3418
|
+
* that speaks another language (an Indonesian app on the raw path) supplies its own without
|
|
3419
|
+
* forking this file. Keeping them in ONE table is also what lets a guard prove none is
|
|
3420
|
+
* hard-coded further down. */
|
|
3410
3421
|
var TEXT = {
|
|
3411
|
-
prev: '
|
|
3412
|
-
next: '
|
|
3413
|
-
selected: '{n}
|
|
3414
|
-
filter: 'Filter
|
|
3415
|
-
filterText: '
|
|
3416
|
-
filterTextPlaceholder: '
|
|
3417
|
-
filterEnum: '
|
|
3418
|
-
filterRange: '
|
|
3422
|
+
prev: 'Previous',
|
|
3423
|
+
next: 'Next',
|
|
3424
|
+
selected: '{n} selected',
|
|
3425
|
+
filter: 'Filter column',
|
|
3426
|
+
filterText: 'Contains text',
|
|
3427
|
+
filterTextPlaceholder: 'Contains…',
|
|
3428
|
+
filterEnum: 'Show values',
|
|
3429
|
+
filterRange: 'Value range',
|
|
3419
3430
|
reset: 'Reset',
|
|
3420
|
-
close: '
|
|
3421
|
-
rows: '{n}
|
|
3422
|
-
info: '
|
|
3431
|
+
close: 'Close',
|
|
3432
|
+
rows: '{n} rows',
|
|
3433
|
+
info: 'Showing {from}–{to} of {total}'
|
|
3423
3434
|
};
|
|
3424
3435
|
/* HTML lowercases attribute names, so a camelCase key like `filterText` can only ever be written
|
|
3425
|
-
as `data-fdy-text-filtertext
|
|
3436
|
+
as `data-fdy-text-filtertext`, while the kebab form anybody would reach for,
|
|
3426
3437
|
`data-fdy-text-filter-text`, becomes a DIFFERENT attribute the enhancer never reads, and the
|
|
3427
3438
|
override fails silently. So the key is kebab-cased for the lookup; the run-together spelling
|
|
3428
3439
|
still resolves, for markup written against 1.39.0. */
|
|
@@ -3902,7 +3913,7 @@
|
|
|
3902
3913
|
window.FreedayTable = { init: initTable, initAll: initAll };
|
|
3903
3914
|
})();
|
|
3904
3915
|
|
|
3905
|
-
/* Freeday
|
|
3916
|
+
/* Freeday, tabs enhancer (optional, zero-dependency, WAI-ARIA APG tabs).
|
|
3906
3917
|
* Auto-inits every [data-fdy-tabs]. Roving tabindex + arrow/Home/End nav.
|
|
3907
3918
|
*
|
|
3908
3919
|
* Markup: a [data-fdy-tabs] with a [role="tablist"] of
|
|
@@ -3971,7 +3982,7 @@
|
|
|
3971
3982
|
window.FreedayTabs = { init: initTabs, initAll: initAll };
|
|
3972
3983
|
})();
|
|
3973
3984
|
|
|
3974
|
-
/* Freeday
|
|
3985
|
+
/* Freeday, time picker enhancer (optional, zero-dependency).
|
|
3975
3986
|
* Builds an input-styled trigger + scrollable time-list popover (WAI-ARIA listbox)
|
|
3976
3987
|
* from an empty [data-fdy-timepicker] wrapper. 24h HH:MM. Options run from
|
|
3977
3988
|
* data-min (default 00:00) to data-max (default 23:59) every data-step minutes
|
|
@@ -4001,7 +4012,7 @@
|
|
|
4001
4012
|
wrap.dataset.fdyTpReady = '1';
|
|
4002
4013
|
wrap.classList.add('fdy-timepicker');
|
|
4003
4014
|
|
|
4004
|
-
var label = wrap.getAttribute('data-label') || '
|
|
4015
|
+
var label = wrap.getAttribute('data-label') || 'Choose a time';
|
|
4005
4016
|
var placeholder = wrap.getAttribute('data-placeholder') || '--:--';
|
|
4006
4017
|
var step = Math.max(1, parseInt(wrap.getAttribute('data-step') || '30', 10));
|
|
4007
4018
|
var minM = valid(wrap.getAttribute('data-min')) ? toMin(wrap.getAttribute('data-min')) : 0;
|
|
@@ -4155,12 +4166,12 @@
|
|
|
4155
4166
|
window.FreedayTimepicker = { init: initTime, initAll: initAll };
|
|
4156
4167
|
})();
|
|
4157
4168
|
|
|
4158
|
-
/* Freeday
|
|
4169
|
+
/* Freeday, toast API (optional, zero-dependency).
|
|
4159
4170
|
* Freeday.toast({ variant, title, message, timeout, key }) shows a transient
|
|
4160
4171
|
* notification in a live region (created on first use). Returns the element.
|
|
4161
4172
|
* variant: 'success' | 'warning' | 'danger' | 'info' (default: neutral)
|
|
4162
4173
|
* timeout: ms before auto-dismiss (default 4000; 0 = sticky)
|
|
4163
|
-
* key: optional stable id
|
|
4174
|
+
* key: optional stable id, a new toast with the same key REPLACES the
|
|
4164
4175
|
* existing one in place instead of stacking a duplicate (e.g. a burst
|
|
4165
4176
|
* of identical failures shows one toast, refreshed).
|
|
4166
4177
|
* Freeday.dismiss(nodeOrKey) closes a toast early (pass the returned element or its key).
|
|
@@ -4196,14 +4207,14 @@
|
|
|
4196
4207
|
|
|
4197
4208
|
/* Put the region in the TOP LAYER, above any open <dialog>.
|
|
4198
4209
|
*
|
|
4199
|
-
* A modal dialog lives in the top layer, where z-index does not reach
|
|
4210
|
+
* A modal dialog lives in the top layer, where z-index does not reach, so a toast raised by an
|
|
4200
4211
|
* action taken inside a modal was painted behind that modal's backdrop, which is where an error
|
|
4201
4212
|
* message is least useful. showPopover() joins the same layer, and membership there is ordered by
|
|
4202
4213
|
* when you joined: hide-then-show re-joins at the top, so a toast shown while a dialog is open
|
|
4203
4214
|
* lands above it rather than under the dialog that was opened after the last toast.
|
|
4204
4215
|
*
|
|
4205
4216
|
* Guarded twice on purpose. `showPopover` is absent before Chrome 114 / Safari 17 / Firefox 125,
|
|
4206
|
-
* and the call throws if the element is disconnected or already open
|
|
4217
|
+
* and the call throws if the element is disconnected or already open, in every one of those
|
|
4207
4218
|
* cases the region stays an ordinary fixed block at z-index 200, which is what it has always
|
|
4208
4219
|
* been. A notification must never be the thing that throws.
|
|
4209
4220
|
*/
|
|
@@ -4234,15 +4245,16 @@
|
|
|
4234
4245
|
}
|
|
4235
4246
|
|
|
4236
4247
|
|
|
4237
|
-
/* User-facing strings.
|
|
4238
|
-
* path
|
|
4239
|
-
*
|
|
4240
|
-
* Keeping them in ONE table is also what lets a guard prove none is
|
|
4248
|
+
/* User-facing strings. English by default since 2.0.0, documented and deliberate for the raw
|
|
4249
|
+
* enhancer path, and every one overridable per element with `data-fdy-text-<key>`, so a host
|
|
4250
|
+
* that speaks another language (an Indonesian app on the raw path) supplies its own without
|
|
4251
|
+
* forking this file. Keeping them in ONE table is also what lets a guard prove none is
|
|
4252
|
+
* hard-coded further down. */
|
|
4241
4253
|
var TEXT = {
|
|
4242
|
-
close: '
|
|
4254
|
+
close: 'Close'
|
|
4243
4255
|
};
|
|
4244
4256
|
/* HTML lowercases attribute names, so a camelCase key like `filterText` can only ever be written
|
|
4245
|
-
as `data-fdy-text-filtertext
|
|
4257
|
+
as `data-fdy-text-filtertext`, while the kebab form anybody would reach for,
|
|
4246
4258
|
`data-fdy-text-filter-text`, becomes a DIFFERENT attribute the enhancer never reads, and the
|
|
4247
4259
|
override fails silently. So the key is kebab-cased for the lookup; the run-together spelling
|
|
4248
4260
|
still resolves, for markup written against 1.39.0. */
|
|
@@ -4317,10 +4329,10 @@
|
|
|
4317
4329
|
window.Freeday.dismiss = dismiss; // dismiss(nodeOrKey)
|
|
4318
4330
|
})();
|
|
4319
4331
|
|
|
4320
|
-
/* Freeday
|
|
4332
|
+
/* Freeday, tree checkbox cascade (optional, zero-dependency).
|
|
4321
4333
|
* Turns a [data-fdy-tree] checkbox tree into a selectable one: checking a branch checks
|
|
4322
4334
|
* every descendant; a partially-selected branch shows the native :indeterminate state.
|
|
4323
|
-
* Rows are native <input class="fdy-checkbox">, labelled via aria-label
|
|
4335
|
+
* Rows are native <input class="fdy-checkbox">, labelled via aria-label. Space toggles.
|
|
4324
4336
|
* Selection never toggles the <details> (the checkbox stops click propagation).
|
|
4325
4337
|
*
|
|
4326
4338
|
* Markup contract:
|
|
@@ -4344,7 +4356,7 @@
|
|
|
4344
4356
|
return Array.prototype.filter.call(ul.children, function (n) { return n.tagName === 'LI'; });
|
|
4345
4357
|
}
|
|
4346
4358
|
|
|
4347
|
-
// The checkbox belonging to a row's own <li
|
|
4359
|
+
// The checkbox belonging to a row's own <li>, branch summary or leaf, never a child's.
|
|
4348
4360
|
function ownBox(li) {
|
|
4349
4361
|
return li.querySelector(':scope > details > summary input[type="checkbox"]')
|
|
4350
4362
|
|| li.querySelector(':scope > input[type="checkbox"]');
|
|
@@ -4415,16 +4427,16 @@
|
|
|
4415
4427
|
window.FreedayTree = { init: initTree, initAll: initAll };
|
|
4416
4428
|
})();
|
|
4417
4429
|
|
|
4418
|
-
/* Freeday
|
|
4430
|
+
/* Freeday, file-upload enhancer (optional, zero-dependency).
|
|
4419
4431
|
* A drop target that also opens the file dialog on click / Enter / Space, validates each file
|
|
4420
4432
|
* (accept + max size), and renders a per-file row with explicit state. Actual upload is the
|
|
4421
|
-
* consumer's job
|
|
4433
|
+
* consumer's job, this is a controlled reference: wire the emitted event's row API to your
|
|
4422
4434
|
* upload call, or set data-fdy-upload-simulate for a demo progress animation.
|
|
4423
4435
|
*
|
|
4424
4436
|
* Markup contract:
|
|
4425
4437
|
* <div class="fdy-dropzone" data-fdy-dropzone role="button" tabindex="0"
|
|
4426
4438
|
* data-max-size="10485760" data-filelist="#dz-list" [data-fdy-upload-simulate]
|
|
4427
|
-
* aria-label="Unggah berkas
|
|
4439
|
+
* aria-label="Unggah berkas, seret ke sini atau tekan Enter">
|
|
4428
4440
|
* <span class="fdy-dropzone__icon">…svg…</span>
|
|
4429
4441
|
* <span class="fdy-dropzone__title">…</span>
|
|
4430
4442
|
* <span class="fdy-dropzone__hint">…</span>
|
|
@@ -4432,15 +4444,15 @@
|
|
|
4432
4444
|
* </div>
|
|
4433
4445
|
* <div class="fdy-filelist" id="dz-list"></div>
|
|
4434
4446
|
*
|
|
4435
|
-
* Emits bubbling CustomEvents
|
|
4447
|
+
* Emits bubbling CustomEvents. BOTH on the dropzone element, which is the one target a consumer
|
|
4436
4448
|
* needs to listen on:
|
|
4437
4449
|
* "fdy-upload-add" {file, rejected, reason, row}
|
|
4438
4450
|
* "fdy-upload-remove" {file}
|
|
4439
4451
|
* `row` is the state machine over the rendered .fdy-file:
|
|
4440
|
-
* .ready() (rest
|
|
4452
|
+
* .ready() (rest, where a dropped file starts) / .uploading() / .setProgress(pct) /
|
|
4441
4453
|
* .waiting(label) (sent, awaiting the server) / .done() / .fail(msg) / .el
|
|
4442
4454
|
* Note the file list is a SIBLING of the dropzone above, so nothing dispatched on a row would ever
|
|
4443
|
-
* bubble through the zone
|
|
4455
|
+
* bubble through the zone, which is why removal fires on the zone and not on the row.
|
|
4444
4456
|
*/
|
|
4445
4457
|
(function () {
|
|
4446
4458
|
'use strict';
|
|
@@ -4468,21 +4480,22 @@
|
|
|
4468
4480
|
}
|
|
4469
4481
|
|
|
4470
4482
|
|
|
4471
|
-
/* User-facing strings.
|
|
4472
|
-
* path
|
|
4473
|
-
*
|
|
4474
|
-
* Keeping them in ONE table is also what lets a guard prove none is
|
|
4483
|
+
/* User-facing strings. English by default since 2.0.0, documented and deliberate for the raw
|
|
4484
|
+
* enhancer path, and every one overridable per element with `data-fdy-text-<key>`, so a host
|
|
4485
|
+
* that speaks another language (an Indonesian app on the raw path) supplies its own without
|
|
4486
|
+
* forking this file. Keeping them in ONE table is also what lets a guard prove none is
|
|
4487
|
+
* hard-coded further down. */
|
|
4475
4488
|
var TEXT = {
|
|
4476
|
-
remove: '
|
|
4477
|
-
progress: '
|
|
4478
|
-
uploading: '
|
|
4479
|
-
waiting: '
|
|
4480
|
-
done: '
|
|
4481
|
-
badType: '
|
|
4482
|
-
tooBig: '
|
|
4489
|
+
remove: 'Remove {name}',
|
|
4490
|
+
progress: 'Upload progress for {name}',
|
|
4491
|
+
uploading: 'Uploading…',
|
|
4492
|
+
waiting: 'Waiting for the server…',
|
|
4493
|
+
done: 'Uploaded',
|
|
4494
|
+
badType: 'Unsupported file type.',
|
|
4495
|
+
tooBig: 'Over the {max} limit.'
|
|
4483
4496
|
};
|
|
4484
4497
|
/* HTML lowercases attribute names, so a camelCase key like `filterText` can only ever be written
|
|
4485
|
-
as `data-fdy-text-filtertext
|
|
4498
|
+
as `data-fdy-text-filtertext`, while the kebab form anybody would reach for,
|
|
4486
4499
|
`data-fdy-text-filter-text`, becomes a DIFFERENT attribute the enhancer never reads, and the
|
|
4487
4500
|
override fails silently. So the key is kebab-cased for the lookup; the run-together spelling
|
|
4488
4501
|
still resolves, for markup written against 1.39.0. */
|
|
@@ -4520,12 +4533,12 @@
|
|
|
4520
4533
|
remove.setAttribute('aria-label', textOf(zone, 'remove', { name: file.name }));
|
|
4521
4534
|
remove.innerHTML = '×';
|
|
4522
4535
|
remove.addEventListener('click', function () {
|
|
4523
|
-
/* Dispatched on the ZONE, not on the row
|
|
4536
|
+
/* Dispatched on the ZONE, not on the row, the same target as fdy-upload-add, so one listener
|
|
4524
4537
|
on the dropzone gets both. The row lives in the file list, which the kit's own markup
|
|
4525
4538
|
contract puts as a SIBLING of the dropzone, so a row event never bubbles through the zone:
|
|
4526
4539
|
a consumer following the docs saw `add` arrive and `remove` never fire, with no error.
|
|
4527
|
-
Firing on both would look safer, but it makes the pair asymmetric
|
|
4528
|
-
anyone delegating on a common ancestor, two `remove`s
|
|
4540
|
+
Firing on both would look safer, but it makes the pair asymmetric, one `add` and, for
|
|
4541
|
+
anyone delegating on a common ancestor, two `remove`s, which is its own silent bug. It
|
|
4529
4542
|
also fixes the listless case: a row that was never attached bubbles to nothing at all. */
|
|
4530
4543
|
zone.dispatchEvent(new CustomEvent('fdy-upload-remove', { bubbles: true, detail: { file: file } }));
|
|
4531
4544
|
el.remove();
|
|
@@ -4557,14 +4570,14 @@
|
|
|
4557
4570
|
}
|
|
4558
4571
|
/* Back to a measured bar. While the indeterminate modifier is on it owns the bar's width, so an
|
|
4559
4572
|
explicit one has to be restored when it comes off: .fdy-progress__bar is a plain block div, and
|
|
4560
|
-
with no width at all it fills the track
|
|
4573
|
+
with no width at all it fills the track, a full bar, which is the opposite of what 0% means. */
|
|
4561
4574
|
function determinate() {
|
|
4562
4575
|
progressEl.classList.remove('fdy-progress--indeterminate');
|
|
4563
4576
|
if (!bar.style.width) bar.style.width = '0%';
|
|
4564
4577
|
}
|
|
4565
4578
|
return {
|
|
4566
4579
|
el: el,
|
|
4567
|
-
/* The state a row starts in: chosen, not yet sent. The size alone
|
|
4580
|
+
/* The state a row starts in: chosen, not yet sent. The size alone, it makes no claim about
|
|
4568
4581
|
a transfer, and no progress bar is shown, because nothing is in flight. Without this the
|
|
4569
4582
|
state machine had no start state: done() claims success, fail() claims an error, and
|
|
4570
4583
|
uploading() is a lie until the consumer actually sends the file. */
|
|
@@ -4588,12 +4601,12 @@
|
|
|
4588
4601
|
bar.style.width = v + '%';
|
|
4589
4602
|
progressEl.setAttribute('aria-valuenow', String(Math.round(v)));
|
|
4590
4603
|
},
|
|
4591
|
-
/* The bytes are gone and the server has not answered yet
|
|
4604
|
+
/* The bytes are gone and the server has not answered yet, extraction, scanning, transcoding.
|
|
4592
4605
|
"Mengunggah…" turns false the moment the last byte leaves, and a determinate bar parked at
|
|
4593
4606
|
100% is the most convincing "hung" signal a UI can produce, so this state reports no
|
|
4594
4607
|
percentage: the bar goes indeterminate and the label belongs to the consumer, because only
|
|
4595
4608
|
they know what the server is doing ("Membaca PDF…", "Memindai…"). done()/fail()/ready() need
|
|
4596
|
-
no counterpart here
|
|
4609
|
+
no counterpart here, they drop the progress element outright, modifier and all. */
|
|
4597
4610
|
waiting: function (label) {
|
|
4598
4611
|
el.classList.remove('fdy-file--error', 'fdy-file--success');
|
|
4599
4612
|
icon.innerHTML = FILE_ICON;
|
|
@@ -4601,7 +4614,7 @@
|
|
|
4601
4614
|
ensureProgress();
|
|
4602
4615
|
progressEl.classList.add('fdy-progress--indeterminate');
|
|
4603
4616
|
/* The modifier styles .fdy-progress__bar, so it must sit on the CONTAINER, and the inline
|
|
4604
|
-
width setProgress wrote has to go
|
|
4617
|
+
width setProgress wrote has to go, an inline style beats any rule the modifier brings.
|
|
4605
4618
|
aria-valuenow goes with it: a progressbar with no value is precisely what ARIA calls
|
|
4606
4619
|
indeterminate, which is the contract COMPONENTS.md already states for this component. */
|
|
4607
4620
|
bar.style.width = '';
|
|
@@ -4672,7 +4685,7 @@
|
|
|
4672
4685
|
if (input) input.addEventListener('change', function () { handleFiles(input.files); input.value = ''; });
|
|
4673
4686
|
|
|
4674
4687
|
/* The list is optional: rendering a row and announcing the file are separate jobs. A consumer
|
|
4675
|
-
that wants its own markup simply provides no list
|
|
4688
|
+
that wants its own markup simply provides no list, it still gets `fdy-upload-add`, and
|
|
4676
4689
|
`detail.row` still works (its element is just never attached). Gating the EVENT on the list
|
|
4677
4690
|
meant "bring your own row" silently cost you the notification that a file had arrived. */
|
|
4678
4691
|
function handleFiles(fileList) {
|