@cahyo-dimas/freeday 1.54.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 +625 -545
- package/COMPONENTS.md +141 -138
- 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/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 +1 -1
- 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 +2 -2
- 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 +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 +162 -158
- package/dist/freeday.tokens.css +5 -5
- package/docs/agent-onboarding.md +41 -41
- package/docs/getting-started.md +35 -35
- 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,16 @@
|
|
|
419
419
|
'use strict';
|
|
420
420
|
|
|
421
421
|
|
|
422
|
-
/* User-facing strings. Indonesian by default
|
|
423
|
-
* 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
|
|
424
424
|
* Blazor adapters, an English app on the raw path) supplies its own without forking this file.
|
|
425
425
|
* Keeping them in ONE table is also what lets a guard prove none is hard-coded further down. */
|
|
426
426
|
var TEXT = {
|
|
427
|
-
position: '{n}
|
|
427
|
+
position: '{n} of {total}',
|
|
428
428
|
slide: 'Slide {n}'
|
|
429
429
|
};
|
|
430
430
|
/* HTML lowercases attribute names, so a camelCase key like `filterText` can only ever be written
|
|
431
|
-
as `data-fdy-text-filtertext
|
|
431
|
+
as `data-fdy-text-filtertext`, while the kebab form anybody would reach for,
|
|
432
432
|
`data-fdy-text-filter-text`, becomes a DIFFERENT attribute the enhancer never reads, and the
|
|
433
433
|
override fails silently. So the key is kebab-cased for the lookup; the run-together spelling
|
|
434
434
|
still resolves, for markup written against 1.39.0. */
|
|
@@ -540,7 +540,7 @@
|
|
|
540
540
|
window.FreedayCarousel = { init: initCarousel, initAll: initAll };
|
|
541
541
|
})();
|
|
542
542
|
|
|
543
|
-
/* Freeday
|
|
543
|
+
/* Freeday, cascade select enhancer (optional, zero-dependency).
|
|
544
544
|
* Turns a [data-fdy-cascade] wrapper into a hierarchical drill-down picker. The
|
|
545
545
|
* data model is a hidden nested <ul> inside the wrapper: a <li> with a child <ul>
|
|
546
546
|
* is a branch (drills in), a <li> without is a leaf (selects). Each <li> carries
|
|
@@ -596,16 +596,16 @@
|
|
|
596
596
|
}
|
|
597
597
|
|
|
598
598
|
|
|
599
|
-
/* User-facing strings. Indonesian by default
|
|
600
|
-
* 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
|
|
601
601
|
* Blazor adapters, an English app on the raw path) supplies its own without forking this file.
|
|
602
602
|
* Keeping them in ONE table is also what lets a guard prove none is hard-coded further down. */
|
|
603
603
|
var TEXT = {
|
|
604
|
-
back: '
|
|
604
|
+
back: 'Back one level',
|
|
605
605
|
submenu: '{label}, submenu'
|
|
606
606
|
};
|
|
607
607
|
/* HTML lowercases attribute names, so a camelCase key like `filterText` can only ever be written
|
|
608
|
-
as `data-fdy-text-filtertext
|
|
608
|
+
as `data-fdy-text-filtertext`, while the kebab form anybody would reach for,
|
|
609
609
|
`data-fdy-text-filter-text`, becomes a DIFFERENT attribute the enhancer never reads, and the
|
|
610
610
|
override fails silently. So the key is kebab-cased for the lookup; the run-together spelling
|
|
611
611
|
still resolves, for markup written against 1.39.0. */
|
|
@@ -630,8 +630,8 @@
|
|
|
630
630
|
var root = sourceUl ? parse(sourceUl) : [];
|
|
631
631
|
if (sourceUl) sourceUl.remove();
|
|
632
632
|
|
|
633
|
-
var label = wrap.getAttribute('data-label') || '
|
|
634
|
-
var placeholder = wrap.getAttribute('data-placeholder') || '
|
|
633
|
+
var label = wrap.getAttribute('data-label') || 'Select';
|
|
634
|
+
var placeholder = wrap.getAttribute('data-placeholder') || 'Select…';
|
|
635
635
|
var sep = wrap.getAttribute('data-separator') || ' / ';
|
|
636
636
|
|
|
637
637
|
var trigger = document.createElement('button');
|
|
@@ -826,24 +826,24 @@
|
|
|
826
826
|
window.FreedayCascade = { init: initCascade, initAll: initAll };
|
|
827
827
|
})();
|
|
828
828
|
|
|
829
|
-
/* Freeday
|
|
829
|
+
/* Freeday, choose-from-list enhancer (optional, zero-dependency).
|
|
830
830
|
* Wires a searchable master-data picker dialog to read-only fields.
|
|
831
831
|
* Progressive enhancement over a native <dialog>; in a framework app, drive the
|
|
832
832
|
* dialog as a controlled component (fetchPage callback + server cache), not a store.
|
|
833
833
|
*
|
|
834
834
|
* Markup contract (see docs):
|
|
835
|
-
* - Field group: [data-fdy-cfl="<dialogId>"]
|
|
835
|
+
* - Field group: [data-fdy-cfl="<dialogId>"], usually a `.fdy-input-group` holding a
|
|
836
836
|
* read-only `.fdy-input` and a trigger `<button class="fdy-input-group__btn">`.
|
|
837
837
|
* Optional `data-fdy-cfl-display="<key>"` picks which row dataset key fills the input
|
|
838
838
|
* (default "value"); optional `data-fdy-cfl-summary="{n} dipilih"` formats the multi label.
|
|
839
|
-
* - Standalone opener: [data-fdy-cfl-open="<dialogId>"]
|
|
839
|
+
* - Standalone opener: [data-fdy-cfl-open="<dialogId>"], opens the dialog with no bound field.
|
|
840
840
|
* - Dialog: <dialog id="<dialogId>" class="fdy-modal fdy-modal--cfl"> containing
|
|
841
841
|
* a `[data-fdy-cfl-search]` input, `.fdy-cfl__row` <tr> rows carrying `data-*` fields,
|
|
842
842
|
* a `[data-fdy-cfl-empty]` element, and (multi only) `[data-fdy-cfl-multiple]` on the
|
|
843
843
|
* dialog plus `[data-fdy-cfl-count]` and `[data-fdy-cfl-confirm]` in the footer.
|
|
844
844
|
*
|
|
845
845
|
* 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
|
|
846
|
+
* opened standalone): detail {row} for single-select, {rows} for multi-select, each a plain
|
|
847
847
|
* object copy of the row's dataset. Consumers fill sibling fields (code → name) from it.
|
|
848
848
|
*/
|
|
849
849
|
(function () {
|
|
@@ -862,15 +862,15 @@
|
|
|
862
862
|
}
|
|
863
863
|
|
|
864
864
|
|
|
865
|
-
/* User-facing strings. Indonesian by default
|
|
866
|
-
* 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
|
|
867
867
|
* Blazor adapters, an English app on the raw path) supplies its own without forking this file.
|
|
868
868
|
* Keeping them in ONE table is also what lets a guard prove none is hard-coded further down. */
|
|
869
869
|
var TEXT = {
|
|
870
|
-
selected: '{n}
|
|
870
|
+
selected: '{n} selected'
|
|
871
871
|
};
|
|
872
872
|
/* HTML lowercases attribute names, so a camelCase key like `filterText` can only ever be written
|
|
873
|
-
as `data-fdy-text-filtertext
|
|
873
|
+
as `data-fdy-text-filtertext`, while the kebab form anybody would reach for,
|
|
874
874
|
`data-fdy-text-filter-text`, becomes a DIFFERENT attribute the enhancer never reads, and the
|
|
875
875
|
override fails silently. So the key is kebab-cased for the lookup; the run-together spelling
|
|
876
876
|
still resolves, for markup written against 1.39.0. */
|
|
@@ -931,7 +931,7 @@
|
|
|
931
931
|
var input = activeGroup.querySelector('.fdy-input');
|
|
932
932
|
if (input) {
|
|
933
933
|
if (detail.rows) {
|
|
934
|
-
var tpl = activeGroup.getAttribute('data-fdy-cfl-summary') || '{n}
|
|
934
|
+
var tpl = activeGroup.getAttribute('data-fdy-cfl-summary') || '{n} selected';
|
|
935
935
|
input.value = detail.rows.length ? tpl.replace('{n}', String(detail.rows.length)) : '';
|
|
936
936
|
} else {
|
|
937
937
|
var key = activeGroup.getAttribute('data-fdy-cfl-display') || 'value';
|
|
@@ -1070,7 +1070,7 @@
|
|
|
1070
1070
|
window.FreedayCfl = { init: initTriggers };
|
|
1071
1071
|
})();
|
|
1072
1072
|
|
|
1073
|
-
/* Freeday
|
|
1073
|
+
/* Freeday, chart renderer (optional, zero-dependency, no external chart lib).
|
|
1074
1074
|
* Renders sparkline / bar / line / area / donut into an element from data attributes.
|
|
1075
1075
|
* Auto-inits [data-fdy-chart] once; call FreedayChart.update(el) to re-render after the
|
|
1076
1076
|
* data attributes change (used by the freeday/vue <FdyChart> wrapper for reactive data).
|
|
@@ -1078,14 +1078,14 @@
|
|
|
1078
1078
|
* Single-series colour (sparkline / simple bar / single line) via data-fdy-color references a
|
|
1079
1079
|
* semantic token by name (primary, accent, success, warning, danger, info). Multi-series
|
|
1080
1080
|
* 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
|
|
1081
|
+
* cycled, 8-series cap); pass data-fdy-colors to override, a name is a semantic token
|
|
1082
1082
|
* (var(--color-<name>)) OR a slot "chart-1".."chart-8" (var(--chart-N)) to pin a series to the
|
|
1083
1083
|
* validated palette. Both data-fdy-color and data-fdy-colors accept the chart-N form.
|
|
1084
1084
|
* Cartesian charts (line/area, multi-series/stacked bar) draw themed axes from --chart-grid /
|
|
1085
1085
|
* --chart-tick and format ticks + tooltips via data-fdy-format (number|percent|currency). Their
|
|
1086
1086
|
* viewBox is sized to the measured plot (1 user unit = 1 CSS pixel) and repainted from a
|
|
1087
1087
|
* ResizeObserver, so axis type stays in real pixels and is set in CSS via --fdy-chart-tick-size.
|
|
1088
|
-
* Charts are an enhancement
|
|
1088
|
+
* Charts are an enhancement, put a table/text fallback inside the element and it will be
|
|
1089
1089
|
* replaced on render; give the element role="img" + aria-label.
|
|
1090
1090
|
*
|
|
1091
1091
|
* Sparkline: <span data-fdy-chart="sparkline" data-values="4,6,5,8,7,10" data-fdy-color="primary">
|
|
@@ -1122,7 +1122,7 @@
|
|
|
1122
1122
|
return v ? v.split(',').map(function (s) { return s.trim(); }) : [];
|
|
1123
1123
|
}
|
|
1124
1124
|
// 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
|
|
1125
|
+
// legend, bar values and donut centre are not exposed. No browser actually prunes that subtree,
|
|
1126
1126
|
// measured in Chrome's AX tree, every one of those nodes is live and unignored; what saves the
|
|
1127
1127
|
// advice is only that AT treats a NAMED role="img" as a leaf and does not descend. So the kit
|
|
1128
1128
|
// hides them itself, and the author's aria-label really is the whole text alternative.
|
|
@@ -1297,7 +1297,7 @@
|
|
|
1297
1297
|
// Lay the chrome out first so the plot can be measured, then size the viewBox to it in CSS
|
|
1298
1298
|
// pixels: one user unit is one pixel, so font-size / r / stroke-width mean what they say at
|
|
1299
1299
|
// every container width. (A fixed 320x180 viewBox stretched by CSS multiplied every declared
|
|
1300
|
-
// size by plotWidth/320 instead
|
|
1300
|
+
// size by plotWidth/320 instead, a 9px axis label rendered ~39px on a 1728px viewport.)
|
|
1301
1301
|
var legendMode = el.getAttribute('data-fdy-legend') || 'auto';
|
|
1302
1302
|
var showLegend = legendMode !== 'none' && (series.length >= 2 || legendMode === 'always');
|
|
1303
1303
|
el.classList.add('fdy-chart-xy');
|
|
@@ -1318,7 +1318,7 @@
|
|
|
1318
1318
|
el.appendChild(plot);
|
|
1319
1319
|
|
|
1320
1320
|
// 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
|
|
1321
|
+
// so the box is already final. A detached or display:none chart measures 0, fall back to the
|
|
1322
1322
|
// legacy box and let the ResizeObserver repaint it once it is actually laid out.
|
|
1323
1323
|
var box = plot.getBoundingClientRect();
|
|
1324
1324
|
var W = Math.round(box.width) || 320, H = Math.round(box.height) || 180;
|
|
@@ -1362,7 +1362,7 @@
|
|
|
1362
1362
|
|
|
1363
1363
|
// x category labels with autoskip. Labels are author-supplied, so estimate the widest one instead
|
|
1364
1364
|
// 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
|
|
1365
|
+
// would collide with the one before it, including the forced last one (a fraction-of-a-slot
|
|
1366
1366
|
// overlap happens when n-1 lands next to the last kept multiple of step, e.g. n=24 at most widths).
|
|
1367
1367
|
var widest = 0;
|
|
1368
1368
|
for (var wi = 0; wi < n; wi++) widest = Math.max(widest, String(labels[wi] || ('#' + (wi + 1))).length * charW);
|
|
@@ -1373,7 +1373,7 @@
|
|
|
1373
1373
|
for (var xi = 0; xi < n; xi++) {
|
|
1374
1374
|
if (xi % step === 0 || xi === n - 1) kept.push(xi);
|
|
1375
1375
|
}
|
|
1376
|
-
// 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.
|
|
1377
1377
|
if (kept.length >= 2 && xPos(kept[kept.length - 1]) - xPos(kept[kept.length - 2]) < minGap) {
|
|
1378
1378
|
kept.splice(kept.length - 2, 1);
|
|
1379
1379
|
}
|
|
@@ -1506,17 +1506,17 @@
|
|
|
1506
1506
|
var li = document.createElement('li');
|
|
1507
1507
|
var sw = document.createElement('span'); sw.className = 'fdy-chart__swatch'; sw.style.background = colorAt(i);
|
|
1508
1508
|
li.appendChild(sw);
|
|
1509
|
-
li.appendChild(document.createTextNode((labels[i] || ('#' + (i + 1))) + '
|
|
1509
|
+
li.appendChild(document.createTextNode((labels[i] || ('#' + (i + 1))) + ' · ' + Math.round((v / total) * 100) + '%'));
|
|
1510
1510
|
legend.appendChild(li);
|
|
1511
1511
|
});
|
|
1512
1512
|
el.appendChild(ring);
|
|
1513
|
-
// 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'
|
|
1514
1514
|
// keeps today's behaviour (a donut is unreadable without labels, so it shows); 'none' suppresses it.
|
|
1515
1515
|
if ((el.getAttribute('data-fdy-legend') || 'auto') !== 'none') el.appendChild(legend);
|
|
1516
1516
|
ensureImg(el);
|
|
1517
1517
|
}
|
|
1518
1518
|
|
|
1519
|
-
// Full (re-)render from current data attributes. Idempotent
|
|
1519
|
+
// Full (re-)render from current data attributes. Idempotent, safe to call repeatedly.
|
|
1520
1520
|
function renderChart(el) {
|
|
1521
1521
|
var type = el.getAttribute('data-fdy-chart');
|
|
1522
1522
|
if (type === 'sparkline') renderSparkline(el);
|
|
@@ -1551,12 +1551,12 @@
|
|
|
1551
1551
|
window.FreedayChart = { init: initChart, initAll: initAll, update: renderChart };
|
|
1552
1552
|
})();
|
|
1553
1553
|
|
|
1554
|
-
/* Freeday
|
|
1554
|
+
/* Freeday, chip enhancer (optional, zero-dependency).
|
|
1555
1555
|
* Two behaviours, both progressive enhancement:
|
|
1556
1556
|
*
|
|
1557
|
-
* 1. Removable chips
|
|
1557
|
+
* 1. Removable chips, any .fdy-chip__remove button removes its chip and emits a
|
|
1558
1558
|
* bubbling "fdy-chip-remove" (detail {value}).
|
|
1559
|
-
* 2. Choice / filter groups
|
|
1559
|
+
* 2. Choice / filter groups, a [data-fdy-chips] container of interactive chips
|
|
1560
1560
|
* (.fdy-chip--choice / .fdy-chip--filter, each a <button>) toggles aria-pressed
|
|
1561
1561
|
* on click. Add data-single for one-at-a-time (radio-like) selection; filter
|
|
1562
1562
|
* chips get a leading check when pressed. Emits "fdy-chip-change" on the group
|
|
@@ -1638,9 +1638,9 @@
|
|
|
1638
1638
|
window.FreedayChip = { initGroup: initGroup, initRemove: initRemove, initAll: initAll };
|
|
1639
1639
|
})();
|
|
1640
1640
|
|
|
1641
|
-
/* Freeday
|
|
1641
|
+
/* Freeday, date picker enhancer (optional, zero-dependency).
|
|
1642
1642
|
* Builds an input-styled trigger + calendar popover from an empty wrapper.
|
|
1643
|
-
* Locale comes from <html lang> (via Intl)
|
|
1643
|
+
* Locale comes from <html lang> (via Intl), month/weekday/value formatting is not hardcoded.
|
|
1644
1644
|
*
|
|
1645
1645
|
* Markup contract:
|
|
1646
1646
|
* - Single: <div data-fdy-datepicker data-value="2026-07-21" data-label="Tanggal unggah"
|
|
@@ -1659,7 +1659,11 @@
|
|
|
1659
1659
|
(function () {
|
|
1660
1660
|
'use strict';
|
|
1661
1661
|
|
|
1662
|
-
|
|
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';
|
|
1663
1667
|
var uidSeq = 0;
|
|
1664
1668
|
function uid(p) { uidSeq += 1; return p + '-' + uidSeq; }
|
|
1665
1669
|
function pad(n) { return n < 10 ? '0' + n : '' + n; }
|
|
@@ -1706,10 +1710,10 @@
|
|
|
1706
1710
|
var view = startOfDay(selected || new Date());
|
|
1707
1711
|
view = new Date(view.getFullYear(), view.getMonth(), 1);
|
|
1708
1712
|
var focusDate = null;
|
|
1709
|
-
/* 'days' | 'months'
|
|
1713
|
+
/* 'days' | 'months', the calendar drills one level up rather than growing furniture beside the
|
|
1710
1714
|
title. Before this, the only pointer route to another month was one click per month: from
|
|
1711
1715
|
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
|
|
1716
|
+
shortcut nobody can see is not an affordance, the repo's own test helper clicked "previous
|
|
1713
1717
|
month" twenty-four times to reach a date two years back. */
|
|
1714
1718
|
var mode = 'days';
|
|
1715
1719
|
var focusMonth = null;
|
|
@@ -1908,7 +1912,7 @@
|
|
|
1908
1912
|
var YEARS_PER_PAGE = 12;
|
|
1909
1913
|
function yearPageStart(y) { return Math.floor(y / YEARS_PER_PAGE) * YEARS_PER_PAGE; }
|
|
1910
1914
|
|
|
1911
|
-
/* 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
|
|
1912
1916
|
monthDisabled applies one level down, for the same reason: a bound that sits inside the year
|
|
1913
1917
|
disables neither end. */
|
|
1914
1918
|
function yearDisabled(y) {
|
|
@@ -2153,7 +2157,7 @@
|
|
|
2153
2157
|
var f = fromApi.getValue();
|
|
2154
2158
|
var t = toApi.getValue();
|
|
2155
2159
|
toApi.setMin(f || null); // end can't precede start (one-directional, per Foundation)
|
|
2156
|
-
fromApi.setRange(f, t); // start stays free
|
|
2160
|
+
fromApi.setRange(f, t); // start stays free, picking a later start clears the end below
|
|
2157
2161
|
toApi.setRange(f, t);
|
|
2158
2162
|
}
|
|
2159
2163
|
fromEl.addEventListener('fdy-datepicker-change', function () {
|
|
@@ -2187,9 +2191,9 @@
|
|
|
2187
2191
|
window.FreedayDatepicker = { init: initPicker, initAll: initAll };
|
|
2188
2192
|
})();
|
|
2189
2193
|
|
|
2190
|
-
/* Freeday
|
|
2194
|
+
/* Freeday, datetime picker composer (optional, zero-dependency).
|
|
2191
2195
|
* Combines a child date picker and time picker into one value. It does not build
|
|
2192
|
-
* anything itself
|
|
2196
|
+
* anything itself, freeday-datepicker.js and freeday-timepicker.js enhance the
|
|
2193
2197
|
* children; this only listens for their change events and emits a single combined
|
|
2194
2198
|
* "fdy-datetime-change" (detail {date, time, value}). value is "YYYY-MM-DDTHH:MM"
|
|
2195
2199
|
* when both parts are set, else "". Icon variants are controlled per child picker
|
|
@@ -2199,7 +2203,7 @@
|
|
|
2199
2203
|
* <div data-fdy-datepicker data-value="2026-07-21"></div>
|
|
2200
2204
|
* <div data-fdy-timepicker data-value="14:30"></div></div>
|
|
2201
2205
|
* 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
|
|
2206
|
+
* child triggers as one control (read-only is adapter-only, a vanilla datetime is interactive).
|
|
2203
2207
|
*/
|
|
2204
2208
|
(function () {
|
|
2205
2209
|
'use strict';
|
|
@@ -2214,7 +2218,7 @@
|
|
|
2214
2218
|
if (!dp || !tp) return;
|
|
2215
2219
|
|
|
2216
2220
|
// Reflect one wrapper-level state onto BOTH child triggers so a datetime reads as a single
|
|
2217
|
-
// disabled/invalid control. Deferred with setTimeout(0)
|
|
2221
|
+
// disabled/invalid control. Deferred with setTimeout(0). NOT a microtask: the timepicker
|
|
2218
2222
|
// enhancer registers its DOMContentLoaded init AFTER this composer, and microtasks drain
|
|
2219
2223
|
// *between* listeners (so a microtask would run before the timepicker trigger exists). A
|
|
2220
2224
|
// macrotask waits until the whole init pass is done and both triggers are built. (readonly is
|
|
@@ -2245,7 +2249,7 @@
|
|
|
2245
2249
|
}));
|
|
2246
2250
|
}
|
|
2247
2251
|
|
|
2248
|
-
// Listen for the children's bubbling change events (order-independent
|
|
2252
|
+
// Listen for the children's bubbling change events (order-independent, no need
|
|
2249
2253
|
// for the child enhancers to have run before this composer).
|
|
2250
2254
|
wrap.addEventListener('fdy-datepicker-change', function (e) { date = e.detail.value; emit(); });
|
|
2251
2255
|
wrap.addEventListener('fdy-time-select', function (e) { time = e.detail.value; emit(); });
|
|
@@ -2274,7 +2278,7 @@
|
|
|
2274
2278
|
window.FreedayDatetime = { init: initDatetime, initAll: initAll };
|
|
2275
2279
|
})();
|
|
2276
2280
|
|
|
2277
|
-
/* Freeday
|
|
2281
|
+
/* Freeday, drawer enhancer (optional, zero-dependency).
|
|
2278
2282
|
* A trigger [data-fdy-drawer="drawerId"] opens the <dialog class="fdy-drawer" id="drawerId">
|
|
2279
2283
|
* as a modal side panel (native showModal gives focus-trap, Esc, and return-focus). Clicking
|
|
2280
2284
|
* the backdrop or any [data-close] inside closes it.
|
|
@@ -2323,7 +2327,7 @@
|
|
|
2323
2327
|
window.FreedayDrawer = { init: initTriggers, initAll: initTriggers };
|
|
2324
2328
|
})();
|
|
2325
2329
|
|
|
2326
|
-
/* Freeday
|
|
2330
|
+
/* Freeday, form validation enhancer (optional, zero-dependency).
|
|
2327
2331
|
* Wires the native Constraint Validation API to accessible inline errors on any
|
|
2328
2332
|
* [data-fdy-validate] <form>. Native-first: constraints come from the standard
|
|
2329
2333
|
* markup attributes (required, type, pattern, min/max, minlength/maxlength, step);
|
|
@@ -2363,24 +2367,24 @@
|
|
|
2363
2367
|
badInput: 'type',
|
|
2364
2368
|
customError: 'mismatch'
|
|
2365
2369
|
};
|
|
2366
|
-
/* User-facing strings. Indonesian by default
|
|
2367
|
-
* 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,
|
|
2368
2372
|
* `data-fdy-msg` on the field, then `data-fdy-text-<alias>` on the FORM. The form level is what
|
|
2369
2373
|
* a host in another language needs: it sets nine messages once instead of on every input.
|
|
2370
2374
|
* Keeping them in ONE table is also what lets a guard prove none is hard-coded further down. */
|
|
2371
2375
|
var TEXT = {
|
|
2372
|
-
required: '
|
|
2373
|
-
type: '
|
|
2374
|
-
pattern: '
|
|
2375
|
-
minlength: '
|
|
2376
|
-
maxlength: '
|
|
2377
|
-
min: '
|
|
2378
|
-
max: '
|
|
2379
|
-
step: '
|
|
2380
|
-
mismatch: '
|
|
2381
|
-
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.'
|
|
2382
2386
|
};
|
|
2383
|
-
/* Kebab-cased for the lookup
|
|
2387
|
+
/* Kebab-cased for the lookup, see the note in the other enhancers: HTML lowercases attribute
|
|
2384
2388
|
names, so `data-fdy-text-filter-text` and `data-fdy-text-filtertext` are different attributes
|
|
2385
2389
|
and only one of them is what an author would write. */
|
|
2386
2390
|
function textOf(root, key) {
|
|
@@ -2524,7 +2528,7 @@
|
|
|
2524
2528
|
window.FreedayForm = { init: initForm, initAll: initAll };
|
|
2525
2529
|
})();
|
|
2526
2530
|
|
|
2527
|
-
/* Freeday
|
|
2531
|
+
/* Freeday, input mask + password reveal enhancer (optional, zero-dependency).
|
|
2528
2532
|
*
|
|
2529
2533
|
* [data-fdy-mask]="pattern" formats a text input as you type. Placeholders:
|
|
2530
2534
|
* # a digit (0-9) A a letter (a-z/A-Z) * a letter or digit
|
|
@@ -2577,16 +2581,16 @@
|
|
|
2577
2581
|
}
|
|
2578
2582
|
|
|
2579
2583
|
|
|
2580
|
-
/* User-facing strings. Indonesian by default
|
|
2581
|
-
* 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
|
|
2582
2586
|
* Blazor adapters, an English app on the raw path) supplies its own without forking this file.
|
|
2583
2587
|
* Keeping them in ONE table is also what lets a guard prove none is hard-coded further down. */
|
|
2584
2588
|
var TEXT = {
|
|
2585
|
-
show: '
|
|
2586
|
-
hide: '
|
|
2589
|
+
show: 'Show password',
|
|
2590
|
+
hide: 'Hide password'
|
|
2587
2591
|
};
|
|
2588
2592
|
/* HTML lowercases attribute names, so a camelCase key like `filterText` can only ever be written
|
|
2589
|
-
as `data-fdy-text-filtertext
|
|
2593
|
+
as `data-fdy-text-filtertext`, while the kebab form anybody would reach for,
|
|
2590
2594
|
`data-fdy-text-filter-text`, becomes a DIFFERENT attribute the enhancer never reads, and the
|
|
2591
2595
|
override fails silently. So the key is kebab-cased for the lookup; the run-together spelling
|
|
2592
2596
|
still resolves, for markup written against 1.39.0. */
|
|
@@ -2668,7 +2672,7 @@
|
|
|
2668
2672
|
window.FreedayMask = { initMask: initMask, initReveal: initReveal, initAll: initAll };
|
|
2669
2673
|
})();
|
|
2670
2674
|
|
|
2671
|
-
/* Freeday
|
|
2675
|
+
/* Freeday, menu enhancer (optional, zero-dependency).
|
|
2672
2676
|
* Menu-button pattern (WAI-ARIA APG): a trigger with aria-haspopup="menu" toggles a
|
|
2673
2677
|
* role="menu" popup of role="menuitem" buttons. Also powers the split button
|
|
2674
2678
|
* (main action + attached caret toggle). Auto-inits [data-fdy-menu].
|
|
@@ -2765,10 +2769,10 @@
|
|
|
2765
2769
|
window.FreedayMenu = { init: initMenu, initAll: initAll };
|
|
2766
2770
|
})();
|
|
2767
2771
|
|
|
2768
|
-
/* Freeday
|
|
2772
|
+
/* Freeday, number field enhancer (optional, zero-dependency).
|
|
2769
2773
|
* Gives a native <input type="number"> its increment/decrement affordance back, on the kit's terms:
|
|
2770
2774
|
* .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
|
|
2775
|
+
* buttons in an .fdy-input-group instead. No new block, the group already owns the shared border,
|
|
2772
2776
|
* :focus-within ring and error promotion.
|
|
2773
2777
|
*
|
|
2774
2778
|
* Markup contract:
|
|
@@ -2834,7 +2838,7 @@
|
|
|
2834
2838
|
if (frozen()) return;
|
|
2835
2839
|
if (Number(btn.getAttribute('data-fdy-number-step')) < 0) input.stepDown();
|
|
2836
2840
|
else input.stepUp();
|
|
2837
|
-
/* 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
|
|
2838
2842
|
listen to these, not to a kit-specific event. */
|
|
2839
2843
|
input.dispatchEvent(new Event('input', { bubbles: true }));
|
|
2840
2844
|
input.dispatchEvent(new Event('change', { bubbles: true }));
|
|
@@ -2870,10 +2874,10 @@
|
|
|
2870
2874
|
window.FreedayNumber = { init: initNumber, initAll: initAll };
|
|
2871
2875
|
})();
|
|
2872
2876
|
|
|
2873
|
-
/* Freeday
|
|
2877
|
+
/* Freeday, popover positioning helper (optional, zero-dependency).
|
|
2874
2878
|
* Lifts a dropdown panel into the top layer via the native Popover API so it escapes ANY
|
|
2875
2879
|
* ancestor overflow clip (a .fdy-card with overflow:hidden, an app-shell main with
|
|
2876
|
-
* overflow:auto, …), then positions it `fixed` against its trigger
|
|
2880
|
+
* overflow:auto, …), then positions it `fixed` against its trigger, flipping above when
|
|
2877
2881
|
* there is no room below, and matching the trigger width. The panel stays a DOM child of its
|
|
2878
2882
|
* component, so focus, outside-click (.closest), and ARIA relationships keep working unchanged.
|
|
2879
2883
|
*
|
|
@@ -2893,7 +2897,7 @@
|
|
|
2893
2897
|
&& typeof HTMLElement.prototype.showPopover === 'function';
|
|
2894
2898
|
|
|
2895
2899
|
// 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
|
|
2900
|
+
// matchWidth (default true) floors the panel to the trigger width, right for input-shaped
|
|
2897
2901
|
// dropdowns (combo/cascade/date/time), skipped for menus that keep their own min-width.
|
|
2898
2902
|
function place(panel, trigger, matchWidth) {
|
|
2899
2903
|
var r = trigger.getBoundingClientRect();
|
|
@@ -2940,10 +2944,10 @@
|
|
|
2940
2944
|
window.FreedayPopover = { attach: attach, place: place, supported: supported };
|
|
2941
2945
|
})();
|
|
2942
2946
|
|
|
2943
|
-
/* Freeday
|
|
2947
|
+
/* Freeday, rating enhancer (optional, zero-dependency).
|
|
2944
2948
|
* Fills stars up to the checked radio (and previews on hover) for an interactive
|
|
2945
2949
|
* [data-fdy-rating] radio group; for [data-fdy-readonly] it fills from data-value.
|
|
2946
|
-
* The radios are native
|
|
2950
|
+
* The radios are native, arrow-key selection and form association come for free.
|
|
2947
2951
|
*/
|
|
2948
2952
|
(function () {
|
|
2949
2953
|
'use strict';
|
|
@@ -2994,11 +2998,11 @@
|
|
|
2994
2998
|
window.FreedayRating = { init: initRating, initAll: initAll };
|
|
2995
2999
|
})();
|
|
2996
3000
|
|
|
2997
|
-
/* Freeday
|
|
3001
|
+
/* Freeday, combobox enhancer (optional, zero-dependency).
|
|
2998
3002
|
* Implements the WAI-ARIA APG "select-only combobox" pattern for every
|
|
2999
3003
|
* [data-fdy-combo] on the page. Progressive enhancement: the markup is the
|
|
3000
3004
|
* component, this script wires behavior. In a Vue/React/Blazor app you would
|
|
3001
|
-
* bind your own state instead
|
|
3005
|
+
* bind your own state instead, this is the framework-agnostic reference.
|
|
3002
3006
|
*
|
|
3003
3007
|
* Expected markup:
|
|
3004
3008
|
* <div class="fdy-combo" data-fdy-combo data-value="x">
|
|
@@ -3021,7 +3025,7 @@
|
|
|
3021
3025
|
var seq = 0;
|
|
3022
3026
|
var openClose = null; // close() of the currently-open combo, or null
|
|
3023
3027
|
|
|
3024
|
-
/* 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. */
|
|
3025
3029
|
function optionLabel(opt) {
|
|
3026
3030
|
return (opt.textContent || '').trim();
|
|
3027
3031
|
}
|
|
@@ -3087,7 +3091,7 @@
|
|
|
3087
3091
|
if (openClose === close) openClose = null;
|
|
3088
3092
|
}
|
|
3089
3093
|
// Mark an option selected + reflect it in the button. `silent` skips the change
|
|
3090
|
-
// event, close, and focus
|
|
3094
|
+
// event, close, and focus, used by the programmatic setValue() below so a host
|
|
3091
3095
|
// (e.g. a Blazor/framework wrapper) can push a value in without echoing an event
|
|
3092
3096
|
// back to itself or stealing focus. User-driven picks go through choose() (loud).
|
|
3093
3097
|
function select(opt, silent) {
|
|
@@ -3122,7 +3126,7 @@
|
|
|
3122
3126
|
isOpen() ? close() : open();
|
|
3123
3127
|
});
|
|
3124
3128
|
// 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
|
|
3129
|
+
// handler below, which closes the listbox before the option's click lands,
|
|
3126
3130
|
// so the pick is lost (real mouse only; synthetic .click() never blurs).
|
|
3127
3131
|
// preventDefault on mousedown keeps focus on the button without blocking click.
|
|
3128
3132
|
listbox.addEventListener('mousedown', function (e) { e.preventDefault(); });
|
|
@@ -3209,10 +3213,10 @@
|
|
|
3209
3213
|
window.FreedayCombo = { init: initCombo, initAll: initAll, setValue: setValue };
|
|
3210
3214
|
})();
|
|
3211
3215
|
|
|
3212
|
-
/* Freeday
|
|
3216
|
+
/* Freeday, slider value binding (optional, zero-dependency).
|
|
3213
3217
|
* Mirrors a [data-fdy-slider] range input's value into <output for="inputId">, with an
|
|
3214
3218
|
* optional prefix/suffix on the output (data-fdy-prefix / data-fdy-suffix). The range itself
|
|
3215
|
-
* is a native input
|
|
3219
|
+
* is a native input, keyboard and ARIA are built in; this only updates the visible readout.
|
|
3216
3220
|
*/
|
|
3217
3221
|
(function () {
|
|
3218
3222
|
'use strict';
|
|
@@ -3247,9 +3251,9 @@
|
|
|
3247
3251
|
window.FreedaySlider = { init: initSlider, initAll: initAll };
|
|
3248
3252
|
})();
|
|
3249
3253
|
|
|
3250
|
-
/* Freeday
|
|
3254
|
+
/* Freeday, stepper/wizard enhancer (optional, zero-dependency).
|
|
3251
3255
|
* Drives a step header + panels: prev/next navigation, completed/active state, and
|
|
3252
|
-
* clicking a reached step to revisit it (linear
|
|
3256
|
+
* clicking a reached step to revisit it (linear, can't skip ahead). Auto-inits
|
|
3253
3257
|
* [data-fdy-stepper].
|
|
3254
3258
|
*
|
|
3255
3259
|
* Markup: <div data-fdy-stepper>
|
|
@@ -3268,16 +3272,16 @@
|
|
|
3268
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>';
|
|
3269
3273
|
|
|
3270
3274
|
|
|
3271
|
-
/* User-facing strings. Indonesian by default
|
|
3272
|
-
* 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
|
|
3273
3277
|
* Blazor adapters, an English app on the raw path) supplies its own without forking this file.
|
|
3274
3278
|
* Keeping them in ONE table is also what lets a guard prove none is hard-coded further down. */
|
|
3275
3279
|
var TEXT = {
|
|
3276
|
-
done: '
|
|
3277
|
-
next: '
|
|
3280
|
+
done: 'Done',
|
|
3281
|
+
next: 'Next'
|
|
3278
3282
|
};
|
|
3279
3283
|
/* HTML lowercases attribute names, so a camelCase key like `filterText` can only ever be written
|
|
3280
|
-
as `data-fdy-text-filtertext
|
|
3284
|
+
as `data-fdy-text-filtertext`, while the kebab form anybody would reach for,
|
|
3281
3285
|
`data-fdy-text-filter-text`, becomes a DIFFERENT attribute the enhancer never reads, and the
|
|
3282
3286
|
override fails silently. So the key is kebab-cased for the lookup; the run-together spelling
|
|
3283
3287
|
still resolves, for markup written against 1.39.0. */
|
|
@@ -3367,7 +3371,7 @@
|
|
|
3367
3371
|
window.FreedayStepper = { init: initStepper, initAll: initAll };
|
|
3368
3372
|
})();
|
|
3369
3373
|
|
|
3370
|
-
/* Freeday
|
|
3374
|
+
/* Freeday, data-table enhancer (optional, zero-dependency).
|
|
3371
3375
|
* Adds client-side search, column sort, pagination and row selection to any
|
|
3372
3376
|
* [data-fdy-table]. Progressive enhancement over a plain semantic <table>;
|
|
3373
3377
|
* in a framework app, do this server- or store-side instead.
|
|
@@ -3403,26 +3407,26 @@
|
|
|
3403
3407
|
}
|
|
3404
3408
|
|
|
3405
3409
|
|
|
3406
|
-
/* User-facing strings. Indonesian by default
|
|
3407
|
-
* 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
|
|
3408
3412
|
* Blazor adapters, an English app on the raw path) supplies its own without forking this file.
|
|
3409
3413
|
* Keeping them in ONE table is also what lets a guard prove none is hard-coded further down. */
|
|
3410
3414
|
var TEXT = {
|
|
3411
|
-
prev: '
|
|
3412
|
-
next: '
|
|
3413
|
-
selected: '{n}
|
|
3414
|
-
filter: 'Filter
|
|
3415
|
-
filterText: '
|
|
3416
|
-
filterTextPlaceholder: '
|
|
3417
|
-
filterEnum: '
|
|
3418
|
-
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',
|
|
3419
3423
|
reset: 'Reset',
|
|
3420
|
-
close: '
|
|
3421
|
-
rows: '{n}
|
|
3422
|
-
info: '
|
|
3424
|
+
close: 'Close',
|
|
3425
|
+
rows: '{n} rows',
|
|
3426
|
+
info: 'Showing {from}–{to} of {total}'
|
|
3423
3427
|
};
|
|
3424
3428
|
/* HTML lowercases attribute names, so a camelCase key like `filterText` can only ever be written
|
|
3425
|
-
as `data-fdy-text-filtertext
|
|
3429
|
+
as `data-fdy-text-filtertext`, while the kebab form anybody would reach for,
|
|
3426
3430
|
`data-fdy-text-filter-text`, becomes a DIFFERENT attribute the enhancer never reads, and the
|
|
3427
3431
|
override fails silently. So the key is kebab-cased for the lookup; the run-together spelling
|
|
3428
3432
|
still resolves, for markup written against 1.39.0. */
|
|
@@ -3902,7 +3906,7 @@
|
|
|
3902
3906
|
window.FreedayTable = { init: initTable, initAll: initAll };
|
|
3903
3907
|
})();
|
|
3904
3908
|
|
|
3905
|
-
/* Freeday
|
|
3909
|
+
/* Freeday, tabs enhancer (optional, zero-dependency, WAI-ARIA APG tabs).
|
|
3906
3910
|
* Auto-inits every [data-fdy-tabs]. Roving tabindex + arrow/Home/End nav.
|
|
3907
3911
|
*
|
|
3908
3912
|
* Markup: a [data-fdy-tabs] with a [role="tablist"] of
|
|
@@ -3971,7 +3975,7 @@
|
|
|
3971
3975
|
window.FreedayTabs = { init: initTabs, initAll: initAll };
|
|
3972
3976
|
})();
|
|
3973
3977
|
|
|
3974
|
-
/* Freeday
|
|
3978
|
+
/* Freeday, time picker enhancer (optional, zero-dependency).
|
|
3975
3979
|
* Builds an input-styled trigger + scrollable time-list popover (WAI-ARIA listbox)
|
|
3976
3980
|
* from an empty [data-fdy-timepicker] wrapper. 24h HH:MM. Options run from
|
|
3977
3981
|
* data-min (default 00:00) to data-max (default 23:59) every data-step minutes
|
|
@@ -4001,7 +4005,7 @@
|
|
|
4001
4005
|
wrap.dataset.fdyTpReady = '1';
|
|
4002
4006
|
wrap.classList.add('fdy-timepicker');
|
|
4003
4007
|
|
|
4004
|
-
var label = wrap.getAttribute('data-label') || '
|
|
4008
|
+
var label = wrap.getAttribute('data-label') || 'Choose a time';
|
|
4005
4009
|
var placeholder = wrap.getAttribute('data-placeholder') || '--:--';
|
|
4006
4010
|
var step = Math.max(1, parseInt(wrap.getAttribute('data-step') || '30', 10));
|
|
4007
4011
|
var minM = valid(wrap.getAttribute('data-min')) ? toMin(wrap.getAttribute('data-min')) : 0;
|
|
@@ -4155,12 +4159,12 @@
|
|
|
4155
4159
|
window.FreedayTimepicker = { init: initTime, initAll: initAll };
|
|
4156
4160
|
})();
|
|
4157
4161
|
|
|
4158
|
-
/* Freeday
|
|
4162
|
+
/* Freeday, toast API (optional, zero-dependency).
|
|
4159
4163
|
* Freeday.toast({ variant, title, message, timeout, key }) shows a transient
|
|
4160
4164
|
* notification in a live region (created on first use). Returns the element.
|
|
4161
4165
|
* variant: 'success' | 'warning' | 'danger' | 'info' (default: neutral)
|
|
4162
4166
|
* timeout: ms before auto-dismiss (default 4000; 0 = sticky)
|
|
4163
|
-
* key: optional stable id
|
|
4167
|
+
* key: optional stable id, a new toast with the same key REPLACES the
|
|
4164
4168
|
* existing one in place instead of stacking a duplicate (e.g. a burst
|
|
4165
4169
|
* of identical failures shows one toast, refreshed).
|
|
4166
4170
|
* Freeday.dismiss(nodeOrKey) closes a toast early (pass the returned element or its key).
|
|
@@ -4196,14 +4200,14 @@
|
|
|
4196
4200
|
|
|
4197
4201
|
/* Put the region in the TOP LAYER, above any open <dialog>.
|
|
4198
4202
|
*
|
|
4199
|
-
* 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
|
|
4200
4204
|
* action taken inside a modal was painted behind that modal's backdrop, which is where an error
|
|
4201
4205
|
* message is least useful. showPopover() joins the same layer, and membership there is ordered by
|
|
4202
4206
|
* when you joined: hide-then-show re-joins at the top, so a toast shown while a dialog is open
|
|
4203
4207
|
* lands above it rather than under the dialog that was opened after the last toast.
|
|
4204
4208
|
*
|
|
4205
4209
|
* 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
|
|
4210
|
+
* and the call throws if the element is disconnected or already open, in every one of those
|
|
4207
4211
|
* cases the region stays an ordinary fixed block at z-index 200, which is what it has always
|
|
4208
4212
|
* been. A notification must never be the thing that throws.
|
|
4209
4213
|
*/
|
|
@@ -4234,15 +4238,15 @@
|
|
|
4234
4238
|
}
|
|
4235
4239
|
|
|
4236
4240
|
|
|
4237
|
-
/* User-facing strings. Indonesian by default
|
|
4238
|
-
* 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
|
|
4239
4243
|
* Blazor adapters, an English app on the raw path) supplies its own without forking this file.
|
|
4240
4244
|
* Keeping them in ONE table is also what lets a guard prove none is hard-coded further down. */
|
|
4241
4245
|
var TEXT = {
|
|
4242
|
-
close: '
|
|
4246
|
+
close: 'Close'
|
|
4243
4247
|
};
|
|
4244
4248
|
/* HTML lowercases attribute names, so a camelCase key like `filterText` can only ever be written
|
|
4245
|
-
as `data-fdy-text-filtertext
|
|
4249
|
+
as `data-fdy-text-filtertext`, while the kebab form anybody would reach for,
|
|
4246
4250
|
`data-fdy-text-filter-text`, becomes a DIFFERENT attribute the enhancer never reads, and the
|
|
4247
4251
|
override fails silently. So the key is kebab-cased for the lookup; the run-together spelling
|
|
4248
4252
|
still resolves, for markup written against 1.39.0. */
|
|
@@ -4317,10 +4321,10 @@
|
|
|
4317
4321
|
window.Freeday.dismiss = dismiss; // dismiss(nodeOrKey)
|
|
4318
4322
|
})();
|
|
4319
4323
|
|
|
4320
|
-
/* Freeday
|
|
4324
|
+
/* Freeday, tree checkbox cascade (optional, zero-dependency).
|
|
4321
4325
|
* Turns a [data-fdy-tree] checkbox tree into a selectable one: checking a branch checks
|
|
4322
4326
|
* every descendant; a partially-selected branch shows the native :indeterminate state.
|
|
4323
|
-
* 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.
|
|
4324
4328
|
* Selection never toggles the <details> (the checkbox stops click propagation).
|
|
4325
4329
|
*
|
|
4326
4330
|
* Markup contract:
|
|
@@ -4344,7 +4348,7 @@
|
|
|
4344
4348
|
return Array.prototype.filter.call(ul.children, function (n) { return n.tagName === 'LI'; });
|
|
4345
4349
|
}
|
|
4346
4350
|
|
|
4347
|
-
// 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.
|
|
4348
4352
|
function ownBox(li) {
|
|
4349
4353
|
return li.querySelector(':scope > details > summary input[type="checkbox"]')
|
|
4350
4354
|
|| li.querySelector(':scope > input[type="checkbox"]');
|
|
@@ -4415,16 +4419,16 @@
|
|
|
4415
4419
|
window.FreedayTree = { init: initTree, initAll: initAll };
|
|
4416
4420
|
})();
|
|
4417
4421
|
|
|
4418
|
-
/* Freeday
|
|
4422
|
+
/* Freeday, file-upload enhancer (optional, zero-dependency).
|
|
4419
4423
|
* A drop target that also opens the file dialog on click / Enter / Space, validates each file
|
|
4420
4424
|
* (accept + max size), and renders a per-file row with explicit state. Actual upload is the
|
|
4421
|
-
* consumer's job
|
|
4425
|
+
* consumer's job, this is a controlled reference: wire the emitted event's row API to your
|
|
4422
4426
|
* upload call, or set data-fdy-upload-simulate for a demo progress animation.
|
|
4423
4427
|
*
|
|
4424
4428
|
* Markup contract:
|
|
4425
4429
|
* <div class="fdy-dropzone" data-fdy-dropzone role="button" tabindex="0"
|
|
4426
4430
|
* data-max-size="10485760" data-filelist="#dz-list" [data-fdy-upload-simulate]
|
|
4427
|
-
* aria-label="Unggah berkas
|
|
4431
|
+
* aria-label="Unggah berkas, seret ke sini atau tekan Enter">
|
|
4428
4432
|
* <span class="fdy-dropzone__icon">…svg…</span>
|
|
4429
4433
|
* <span class="fdy-dropzone__title">…</span>
|
|
4430
4434
|
* <span class="fdy-dropzone__hint">…</span>
|
|
@@ -4432,15 +4436,15 @@
|
|
|
4432
4436
|
* </div>
|
|
4433
4437
|
* <div class="fdy-filelist" id="dz-list"></div>
|
|
4434
4438
|
*
|
|
4435
|
-
* Emits bubbling CustomEvents
|
|
4439
|
+
* Emits bubbling CustomEvents. BOTH on the dropzone element, which is the one target a consumer
|
|
4436
4440
|
* needs to listen on:
|
|
4437
4441
|
* "fdy-upload-add" {file, rejected, reason, row}
|
|
4438
4442
|
* "fdy-upload-remove" {file}
|
|
4439
4443
|
* `row` is the state machine over the rendered .fdy-file:
|
|
4440
|
-
* .ready() (rest
|
|
4444
|
+
* .ready() (rest, where a dropped file starts) / .uploading() / .setProgress(pct) /
|
|
4441
4445
|
* .waiting(label) (sent, awaiting the server) / .done() / .fail(msg) / .el
|
|
4442
4446
|
* Note the file list is a SIBLING of the dropzone above, so nothing dispatched on a row would ever
|
|
4443
|
-
* bubble through the zone
|
|
4447
|
+
* bubble through the zone, which is why removal fires on the zone and not on the row.
|
|
4444
4448
|
*/
|
|
4445
4449
|
(function () {
|
|
4446
4450
|
'use strict';
|
|
@@ -4468,21 +4472,21 @@
|
|
|
4468
4472
|
}
|
|
4469
4473
|
|
|
4470
4474
|
|
|
4471
|
-
/* User-facing strings. Indonesian by default
|
|
4472
|
-
* 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
|
|
4473
4477
|
* Blazor adapters, an English app on the raw path) supplies its own without forking this file.
|
|
4474
4478
|
* Keeping them in ONE table is also what lets a guard prove none is hard-coded further down. */
|
|
4475
4479
|
var TEXT = {
|
|
4476
|
-
remove: '
|
|
4477
|
-
progress: '
|
|
4478
|
-
uploading: '
|
|
4479
|
-
waiting: '
|
|
4480
|
-
done: '
|
|
4481
|
-
badType: '
|
|
4482
|
-
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.'
|
|
4483
4487
|
};
|
|
4484
4488
|
/* HTML lowercases attribute names, so a camelCase key like `filterText` can only ever be written
|
|
4485
|
-
as `data-fdy-text-filtertext
|
|
4489
|
+
as `data-fdy-text-filtertext`, while the kebab form anybody would reach for,
|
|
4486
4490
|
`data-fdy-text-filter-text`, becomes a DIFFERENT attribute the enhancer never reads, and the
|
|
4487
4491
|
override fails silently. So the key is kebab-cased for the lookup; the run-together spelling
|
|
4488
4492
|
still resolves, for markup written against 1.39.0. */
|
|
@@ -4520,12 +4524,12 @@
|
|
|
4520
4524
|
remove.setAttribute('aria-label', textOf(zone, 'remove', { name: file.name }));
|
|
4521
4525
|
remove.innerHTML = '×';
|
|
4522
4526
|
remove.addEventListener('click', function () {
|
|
4523
|
-
/* 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
|
|
4524
4528
|
on the dropzone gets both. The row lives in the file list, which the kit's own markup
|
|
4525
4529
|
contract puts as a SIBLING of the dropzone, so a row event never bubbles through the zone:
|
|
4526
4530
|
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
|
|
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
|
|
4529
4533
|
also fixes the listless case: a row that was never attached bubbles to nothing at all. */
|
|
4530
4534
|
zone.dispatchEvent(new CustomEvent('fdy-upload-remove', { bubbles: true, detail: { file: file } }));
|
|
4531
4535
|
el.remove();
|
|
@@ -4557,14 +4561,14 @@
|
|
|
4557
4561
|
}
|
|
4558
4562
|
/* Back to a measured bar. While the indeterminate modifier is on it owns the bar's width, so an
|
|
4559
4563
|
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
|
|
4564
|
+
with no width at all it fills the track, a full bar, which is the opposite of what 0% means. */
|
|
4561
4565
|
function determinate() {
|
|
4562
4566
|
progressEl.classList.remove('fdy-progress--indeterminate');
|
|
4563
4567
|
if (!bar.style.width) bar.style.width = '0%';
|
|
4564
4568
|
}
|
|
4565
4569
|
return {
|
|
4566
4570
|
el: el,
|
|
4567
|
-
/* 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
|
|
4568
4572
|
a transfer, and no progress bar is shown, because nothing is in flight. Without this the
|
|
4569
4573
|
state machine had no start state: done() claims success, fail() claims an error, and
|
|
4570
4574
|
uploading() is a lie until the consumer actually sends the file. */
|
|
@@ -4588,12 +4592,12 @@
|
|
|
4588
4592
|
bar.style.width = v + '%';
|
|
4589
4593
|
progressEl.setAttribute('aria-valuenow', String(Math.round(v)));
|
|
4590
4594
|
},
|
|
4591
|
-
/* 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.
|
|
4592
4596
|
"Mengunggah…" turns false the moment the last byte leaves, and a determinate bar parked at
|
|
4593
4597
|
100% is the most convincing "hung" signal a UI can produce, so this state reports no
|
|
4594
4598
|
percentage: the bar goes indeterminate and the label belongs to the consumer, because only
|
|
4595
4599
|
they know what the server is doing ("Membaca PDF…", "Memindai…"). done()/fail()/ready() need
|
|
4596
|
-
no counterpart here
|
|
4600
|
+
no counterpart here, they drop the progress element outright, modifier and all. */
|
|
4597
4601
|
waiting: function (label) {
|
|
4598
4602
|
el.classList.remove('fdy-file--error', 'fdy-file--success');
|
|
4599
4603
|
icon.innerHTML = FILE_ICON;
|
|
@@ -4601,7 +4605,7 @@
|
|
|
4601
4605
|
ensureProgress();
|
|
4602
4606
|
progressEl.classList.add('fdy-progress--indeterminate');
|
|
4603
4607
|
/* The modifier styles .fdy-progress__bar, so it must sit on the CONTAINER, and the inline
|
|
4604
|
-
width setProgress wrote has to go
|
|
4608
|
+
width setProgress wrote has to go, an inline style beats any rule the modifier brings.
|
|
4605
4609
|
aria-valuenow goes with it: a progressbar with no value is precisely what ARIA calls
|
|
4606
4610
|
indeterminate, which is the contract COMPONENTS.md already states for this component. */
|
|
4607
4611
|
bar.style.width = '';
|
|
@@ -4672,7 +4676,7 @@
|
|
|
4672
4676
|
if (input) input.addEventListener('change', function () { handleFiles(input.files); input.value = ''; });
|
|
4673
4677
|
|
|
4674
4678
|
/* 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
|
|
4679
|
+
that wants its own markup simply provides no list, it still gets `fdy-upload-add`, and
|
|
4676
4680
|
`detail.row` still works (its element is just never attached). Gating the EVENT on the list
|
|
4677
4681
|
meant "bring your own row" silently cost you the notification that a file had arrived. */
|
|
4678
4682
|
function handleFiles(fileList) {
|