@cahyo-dimas/freeday 1.53.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +657 -535
- package/COMPONENTS.md +153 -135
- package/README.id.md +46 -45
- package/README.md +46 -43
- package/USAGE.md +39 -35
- package/adapters/blazor/FdyAppShell.razor +50 -0
- package/adapters/blazor/FdyAppShell.razor.cs +114 -0
- package/adapters/blazor/FdyCfl.razor.cs +1 -1
- package/adapters/blazor/FdyChart.razor +1 -1
- package/adapters/blazor/FdyChart.razor.cs +3 -3
- package/adapters/blazor/FdyChartSeries.cs +1 -1
- package/adapters/blazor/FdyCombo.razor.cs +2 -2
- package/adapters/blazor/FdyModal.razor +1 -1
- package/adapters/blazor/FdyTable.razor.cs +7 -7
- package/adapters/blazor/FdyTableFilter.razor +1 -1
- package/adapters/blazor/FdyTableFooter.razor +1 -1
- package/adapters/blazor/FdyTableFooter.razor.cs +4 -4
- package/adapters/blazor/Freeday.Blazor.csproj +1 -1
- package/adapters/blazor/FreedayComponentBase.cs +1 -1
- package/adapters/blazor/TableModel.cs +3 -3
- package/adapters/blazor/TableTypes.cs +3 -3
- package/adapters/blazor/freeday-blazor.js +5 -5
- package/adapters/core/app-shell.d.ts +30 -0
- package/adapters/core/app-shell.js +134 -0
- package/adapters/core/table-model.d.ts +2 -2
- package/adapters/core/table-model.js +8 -8
- package/adapters/react/components/FdyAppShell.tsx +159 -0
- package/adapters/react/components/FdyAutocomplete.tsx +1 -1
- package/adapters/react/components/FdyCascade.tsx +1 -1
- package/adapters/react/components/FdyCfl.tsx +11 -11
- package/adapters/react/components/FdyDateRange.tsx +2 -2
- package/adapters/react/components/FdyDatepicker.tsx +5 -5
- package/adapters/react/components/FdyDrawer.tsx +1 -1
- package/adapters/react/components/FdyModal.tsx +2 -2
- package/adapters/react/components/FdyTable.tsx +7 -7
- package/adapters/react/components/FdyTableFilter.tsx +1 -1
- package/adapters/react/components/FdyTableFooter.tsx +5 -5
- package/adapters/react/index.d.ts +2 -1
- package/adapters/react/index.js +1 -0
- package/adapters/react/useFreeday.js +3 -3
- package/adapters/vue/components/FdyAppShell.vue +160 -0
- package/adapters/vue/components/FdyAutocomplete.vue +1 -1
- package/adapters/vue/components/FdyCascade.vue +1 -1
- package/adapters/vue/components/FdyCfl.vue +8 -8
- package/adapters/vue/components/FdyChart.vue +2 -2
- package/adapters/vue/components/FdyCombo.vue +1 -1
- package/adapters/vue/components/FdyDateRange.vue +2 -2
- package/adapters/vue/components/FdyDatepicker.vue +5 -5
- package/adapters/vue/components/FdyDrawer.vue +3 -3
- package/adapters/vue/components/FdyModal.vue +3 -3
- package/adapters/vue/components/FdyTable.vue +7 -7
- package/adapters/vue/components/FdyTableFilter.vue +1 -1
- package/adapters/vue/components/FdyTableFooter.vue +5 -5
- package/adapters/vue/index.d.ts +3 -2
- package/adapters/vue/index.js +1 -0
- package/adapters/vue/useFreeday.js +3 -3
- package/dist/asset.d.ts +3 -3
- package/dist/freeday-app-shell.js +49 -9
- package/dist/freeday-autocomplete.js +2 -2
- package/dist/freeday-breakpoint.js +1 -1
- package/dist/freeday-carousel.js +5 -5
- package/dist/freeday-cascade.js +7 -7
- package/dist/freeday-cfl.js +9 -9
- package/dist/freeday-chart.js +11 -11
- package/dist/freeday-chip.js +3 -3
- package/dist/freeday-datepicker.js +11 -7
- package/dist/freeday-datetime.js +5 -5
- package/dist/freeday-drawer.js +1 -1
- package/dist/freeday-form.js +14 -14
- package/dist/freeday-mask.js +6 -6
- package/dist/freeday-menu.js +1 -1
- package/dist/freeday-number.js +3 -3
- package/dist/freeday-popover.js +3 -3
- package/dist/freeday-rating.js +2 -2
- package/dist/freeday-select.js +5 -5
- package/dist/freeday-slider.js +2 -2
- package/dist/freeday-stepper.js +7 -7
- package/dist/freeday-table.js +15 -15
- package/dist/freeday-tabs.js +1 -1
- package/dist/freeday-timepicker.js +2 -2
- package/dist/freeday-toast.js +8 -8
- package/dist/freeday-tree.js +3 -3
- package/dist/freeday-upload.js +25 -25
- package/dist/freeday.bundle.css +176 -176
- package/dist/freeday.css +170 -170
- package/dist/freeday.d.ts +2 -2
- package/dist/freeday.js +202 -158
- package/dist/freeday.tokens.css +5 -5
- package/docs/agent-onboarding.md +42 -42
- package/docs/getting-started.md +35 -35
- package/docs/integrations.md +18 -18
- package/docs/reference-screen.html +14 -14
- package/package.json +5 -4
- package/src/base.css +6 -6
- package/src/components/accordion.css +4 -4
- package/src/components/alert.css +1 -1
- package/src/components/app-shell.css +10 -10
- package/src/components/appbar.css +3 -3
- package/src/components/autocomplete.css +1 -1
- package/src/components/avatar.css +3 -3
- package/src/components/badge.css +5 -5
- package/src/components/breadcrumb.css +1 -1
- package/src/components/breakpoints.css +4 -4
- package/src/components/button.css +13 -13
- package/src/components/card.css +8 -8
- package/src/components/carousel.css +2 -2
- package/src/components/cascade.css +2 -2
- package/src/components/cfl.css +4 -4
- package/src/components/chart.css +3 -3
- package/src/components/chip.css +2 -2
- package/src/components/combo.css +3 -3
- package/src/components/composition.css +7 -7
- package/src/components/datepicker.css +6 -6
- package/src/components/datetimepicker.css +1 -1
- package/src/components/description-list.css +1 -1
- package/src/components/divider.css +1 -1
- package/src/components/drawer.css +1 -1
- package/src/components/file-upload.css +5 -5
- package/src/components/filterbar.css +7 -7
- package/src/components/form-grid.css +2 -2
- package/src/components/input-group.css +6 -6
- package/src/components/input.css +6 -6
- package/src/components/kbd.css +1 -1
- package/src/components/list.css +6 -6
- package/src/components/menu.css +3 -3
- package/src/components/modal.css +1 -1
- package/src/components/pagination.css +1 -1
- package/src/components/progress.css +1 -1
- package/src/components/rating.css +2 -2
- package/src/components/selection.css +4 -4
- package/src/components/skeleton.css +1 -1
- package/src/components/slider.css +1 -1
- package/src/components/spinner.css +1 -1
- package/src/components/states.css +1 -1
- package/src/components/stepper.css +2 -2
- package/src/components/table.css +15 -15
- package/src/components/tabs.css +3 -3
- package/src/components/timeline.css +1 -1
- package/src/components/timepicker.css +1 -1
- package/src/components/toast.css +3 -3
- package/src/components/tooltip.css +1 -1
- package/src/components/tree.css +2 -2
- package/tokens/breakpoints.d.ts +2 -2
- package/tokens/breakpoints.mjs +3 -3
- package/tokens/tokens.json +1 -1
|
@@ -8,19 +8,19 @@ import FdyCombo from './FdyCombo.vue';
|
|
|
8
8
|
* The band under a table: what you are looking at, how much of it you see, where you are.
|
|
9
9
|
*
|
|
10
10
|
* `FdyTable` renders this itself, so most screens never import it. It is exported for the one shape
|
|
11
|
-
* that cannot use the table's own
|
|
11
|
+
* that cannot use the table's own, a RESPONSIVE list, where a `.fdy-datatable` at `lg` and a
|
|
12
12
|
* `.fdy-list` below it are two renderings of ONE page of rows. A footer inside the table is inside
|
|
13
13
|
* the half that is hidden on a phone, so those screens need to render it once, outside both
|
|
14
14
|
* (improvement notes #005 and #008, from IDU_EMATE_APPL_WEB).
|
|
15
15
|
*
|
|
16
|
-
* It owns nothing. `page` in, `update:page` out
|
|
16
|
+
* It owns nothing. `page` in, `update:page` out, the same contract as the table's server mode, so a
|
|
17
17
|
* screen already wired for that switches by moving the prop.
|
|
18
18
|
*/
|
|
19
19
|
const props = defineProps<{
|
|
20
20
|
/** The page being shown. `size` drives the range AND the rows-per-page control's value. */
|
|
21
21
|
page: FdyPageState;
|
|
22
22
|
/**
|
|
23
|
-
* Offer a rows-per-page control. Omit for none
|
|
23
|
+
* Offer a rows-per-page control. Omit for none; the footer is then range + pager, exactly as
|
|
24
24
|
* before. Picking a size emits `update:page` carrying the new `size` and the index that still
|
|
25
25
|
* holds the row you were looking at.
|
|
26
26
|
*/
|
|
@@ -29,7 +29,7 @@ const props = defineProps<{
|
|
|
29
29
|
|
|
30
30
|
const emit = defineEmits<{ 'update:page': [page: FdyPageState] }>();
|
|
31
31
|
|
|
32
|
-
/* The combo is labelled by the visible word beside it
|
|
32
|
+
/* The combo is labelled by the visible word beside it, a `<label for>` cannot reach inside a
|
|
33
33
|
* component, and an aria-label would leave that word unattached to anything. */
|
|
34
34
|
const sizeLabelId: string = `fdy-rows-${useId()}`;
|
|
35
35
|
|
|
@@ -54,7 +54,7 @@ const sizes: ComputedRef<readonly number[]> = computed((): readonly number[] =>
|
|
|
54
54
|
const sizeOptions: ComputedRef<Array<{ value: string; label: string }>> = computed(() =>
|
|
55
55
|
sizes.value.map((size: number) => ({ value: String(size), label: String(size) })),
|
|
56
56
|
);
|
|
57
|
-
/* One page and no size control means there is nothing here to say
|
|
57
|
+
/* One page and no size control means there is nothing here to say, the table has always withheld
|
|
58
58
|
* the whole band in that case, and this is where that decision now lives. */
|
|
59
59
|
const visible: ComputedRef<boolean> = computed((): boolean => hasPager.value || sizes.value.length > 0);
|
|
60
60
|
|
package/adapters/vue/index.d.ts
CHANGED
|
@@ -14,7 +14,7 @@ export declare function useFreeday(rootRef?: Ref<HTMLElement | null>): UseFreeda
|
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* Anchor a dropdown panel to its trigger in the top layer, so it escapes any ancestor overflow clip
|
|
17
|
-
|
|
17
|
+
*, `.fdy-card` is `overflow:hidden`, so a panel positioned inside one is otherwise cut at the card's
|
|
18
18
|
* edge. The panel element must carry `popover="manual"`. This is the plumbing every kit dropdown
|
|
19
19
|
* uses; it is exported so a control the kit does not ship can behave like one that it does, instead
|
|
20
20
|
* of each app re-implementing it from the description.
|
|
@@ -35,11 +35,12 @@ export type { CascadeNode } from './components/FdyCascade.vue';
|
|
|
35
35
|
export { default as FdyCfl } from './components/FdyCfl.vue';
|
|
36
36
|
export { default as FdyChart } from './components/FdyChart.vue';
|
|
37
37
|
export { default as FdyTable } from './components/FdyTable.vue';
|
|
38
|
-
/** The table's own footer, standalone
|
|
38
|
+
/** The table's own footer, standalone, for a responsive list whose table is hidden at some
|
|
39
39
|
* breakpoints and so cannot host it. */
|
|
40
40
|
export { default as FdyTableFooter } from './components/FdyTableFooter.vue';
|
|
41
41
|
export { default as FdyModal } from './components/FdyModal.vue';
|
|
42
42
|
export { default as FdyDrawer } from './components/FdyDrawer.vue';
|
|
43
|
+
export { default as FdyAppShell } from './components/FdyAppShell.vue';
|
|
43
44
|
|
|
44
45
|
/** Controlled data-table types (shared, framework-agnostic core). */
|
|
45
46
|
export type {
|
package/adapters/vue/index.js
CHANGED
|
@@ -11,3 +11,4 @@ export { default as FdyTable } from './components/FdyTable.vue';
|
|
|
11
11
|
export { default as FdyTableFooter } from './components/FdyTableFooter.vue';
|
|
12
12
|
export { default as FdyModal } from './components/FdyModal.vue';
|
|
13
13
|
export { default as FdyDrawer } from './components/FdyDrawer.vue';
|
|
14
|
+
export { default as FdyAppShell } from './components/FdyAppShell.vue';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* Freeday
|
|
1
|
+
/* Freeday. Vue 3 adapter (thin). The enhancers stay the source of truth; this
|
|
2
2
|
* composable just re-runs every registered window.Freeday*.initAll() over a
|
|
3
3
|
* subtree after Vue renders it, so [data-fdy-*] markup rendered by Vue gets
|
|
4
4
|
* enhanced (and re-enhanced after updates). Enhancers are idempotent, so the
|
|
@@ -10,12 +10,12 @@
|
|
|
10
10
|
* useFreeday(root); // scopes init to this component's subtree
|
|
11
11
|
* // <div ref="root"> ...[data-fdy-*] markup... </div>
|
|
12
12
|
*
|
|
13
|
-
* The ref may also sit ON the widget itself
|
|
13
|
+
* The ref may also sit ON the widget itself, <div ref="root" data-fdy-menu>, which is what you
|
|
14
14
|
* write when the component's root element IS the widget. initAll() matches its own root as
|
|
15
15
|
* well as descendants, so both shapes work. (Before 1.23.0 only the wrapping shape did, and
|
|
16
16
|
* the other failed silently: querySelectorAll never matches its root.)
|
|
17
17
|
*
|
|
18
|
-
* Events come straight through as native bubbling CustomEvents
|
|
18
|
+
* Events come straight through as native bubbling CustomEvents, bind them with
|
|
19
19
|
* plain v-on (e.g. @fdy-cascade-change) and read event.detail. See index.d.ts
|
|
20
20
|
* for the detail types.
|
|
21
21
|
*/
|
package/dist/asset.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
// Shared type stub for every export imported purely for its side effects
|
|
1
|
+
// Shared type stub for every export imported purely for its side effects: the CSS
|
|
2
2
|
// bundles (`./css`, `./css/components`, `./tokens`) and the individual enhancer scripts
|
|
3
3
|
// (`./enhancers/*`). TypeScript only needs the `types` condition to *resolve* the module;
|
|
4
|
-
// there is no value surface to describe. (The root `.` import is different
|
|
5
|
-
// window.Freeday
|
|
4
|
+
// there is no value surface to describe. (The root `.` import is different, since it augments
|
|
5
|
+
// window.Freeday, so it points at ./freeday.d.ts instead.)
|
|
6
6
|
export {};
|
|
@@ -1,20 +1,25 @@
|
|
|
1
|
-
/* Freeday
|
|
1
|
+
/* Freeday, app shell enhancer (optional, zero-dependency).
|
|
2
2
|
* Opt in with <div class="fdy-app" data-fdy-app>; the markup is otherwise unchanged.
|
|
3
3
|
*
|
|
4
4
|
* The shell has always shipped the state classes and no behaviour, which left every consumer to
|
|
5
|
-
* assemble the toggle, Escape, focus trap, focus restore and `inert` themselves
|
|
5
|
+
* assemble the toggle, Escape, focus trap, focus restore and `inert` themselves, and the two
|
|
6
6
|
* hand-rolled copies in this repo's own docs already disagreed about which of those exist. Below
|
|
7
7
|
* 721px the sidebar is an OVERLAY (app-shell.css: position:fixed + translateX(-100%)), so it needs
|
|
8
8
|
* the same treatment a modal drawer gets; at or above 721px it is a column that merely collapses.
|
|
9
9
|
*
|
|
10
10
|
* One rule covers both a hidden nav and a hidden page:
|
|
11
11
|
* - `__sidebar` is inert whenever the nav is NOT visible. Off-canvas and collapsed panels stay in
|
|
12
|
-
* the tab order otherwise
|
|
12
|
+
* the tab order otherwise, translateX(-100%) and width:0 hide a thing from the eye, not from
|
|
13
13
|
* the keyboard, which is how a nav nobody can see still swallows Tab.
|
|
14
14
|
* - `__content` is inert only while the nav is an OPEN OVERLAY, so Tab cannot wander behind the
|
|
15
15
|
* backdrop.
|
|
16
16
|
*
|
|
17
|
-
* Emits
|
|
17
|
+
* Emits a bubbling `fdy-app-nav` CustomEvent (detail {visible}) whenever the nav's visibility
|
|
18
|
+
* changes, and takes `FreedayAppShell.setVisible(root, visible)` from outside. Those two exist for
|
|
19
|
+
* the same reason the other enhancers have them: a host that keeps its own state, the Blazor
|
|
20
|
+
* wrapper binding @bind-NavOpen, an app persisting the collapsed preference, has to be able to
|
|
21
|
+
* hear the change and to drive it, without owning the behaviour twice.
|
|
22
|
+
* FreedayAppShell.init(root) for late-mounted markup.
|
|
18
23
|
*/
|
|
19
24
|
(function () {
|
|
20
25
|
'use strict';
|
|
@@ -26,7 +31,7 @@
|
|
|
26
31
|
+ ' textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
|
|
27
32
|
|
|
28
33
|
/* getClientRects(), not offsetParent: an overlay sidebar is position:fixed, and a fixed element
|
|
29
|
-
reports no offsetParent at all
|
|
34
|
+
reports no offsetParent at all, filtering on it would call every nav item invisible. */
|
|
30
35
|
function focusables(root) {
|
|
31
36
|
return Array.prototype.filter.call(root.querySelectorAll(FOCUSABLE), function (el) {
|
|
32
37
|
return el.getClientRects().length > 0;
|
|
@@ -51,18 +56,25 @@
|
|
|
51
56
|
|
|
52
57
|
var mqWide = window.matchMedia(WIDE);
|
|
53
58
|
var restoreTo = null;
|
|
59
|
+
var lastVisible = null;
|
|
54
60
|
|
|
55
61
|
function isOverlayOpen() { return app.classList.contains('fdy-app--nav-open'); }
|
|
56
62
|
function isCollapsed() { return app.classList.contains('fdy-app--nav-collapsed'); }
|
|
57
63
|
function navVisible() { return mqWide.matches ? !isCollapsed() : isOverlayOpen(); }
|
|
58
64
|
|
|
59
|
-
/* aria-expanded answers "is the nav showing?" in BOTH modes
|
|
65
|
+
/* aria-expanded answers "is the nav showing?" in BOTH modes, the two state classes are the
|
|
60
66
|
kit's business, not the reader's. */
|
|
61
67
|
function sync() {
|
|
62
68
|
var visible = navVisible();
|
|
63
69
|
toggle.setAttribute('aria-expanded', String(visible));
|
|
64
70
|
setInert(sidebar, !visible);
|
|
65
71
|
setInert(content, !mqWide.matches && visible);
|
|
72
|
+
/* Announce only real changes. The first sync() runs at init to describe the state the markup
|
|
73
|
+
arrived in, which is not something a host asked for and must not look like one. */
|
|
74
|
+
if (lastVisible !== null && visible !== lastVisible) {
|
|
75
|
+
app.dispatchEvent(new CustomEvent('fdy-app-nav', { bubbles: true, detail: { visible: visible } }));
|
|
76
|
+
}
|
|
77
|
+
lastVisible = visible;
|
|
66
78
|
}
|
|
67
79
|
|
|
68
80
|
function open() {
|
|
@@ -79,7 +91,7 @@
|
|
|
79
91
|
}
|
|
80
92
|
}
|
|
81
93
|
|
|
82
|
-
/* restoreFocus is false when the viewport closed it rather than the user
|
|
94
|
+
/* restoreFocus is false when the viewport closed it rather than the user, a resize must not
|
|
83
95
|
yank focus out from under whatever the reader was doing. */
|
|
84
96
|
function close(restoreFocus) {
|
|
85
97
|
if (!isOverlayOpen()) return;
|
|
@@ -106,7 +118,7 @@
|
|
|
106
118
|
|
|
107
119
|
if (backdrop) backdrop.addEventListener('click', function () { close(); });
|
|
108
120
|
|
|
109
|
-
/* Following a link inside an overlay nav means "take me there"
|
|
121
|
+
/* Following a link inside an overlay nav means "take me there", leaving the panel open over
|
|
110
122
|
the page you just asked for is the one thing every hand-rolled copy in this repo disagreed on. */
|
|
111
123
|
sidebar.addEventListener('click', function (e) {
|
|
112
124
|
if (!isOverlayOpen()) return;
|
|
@@ -147,6 +159,23 @@
|
|
|
147
159
|
});
|
|
148
160
|
|
|
149
161
|
sync();
|
|
162
|
+
|
|
163
|
+
/* The same handle the other enhancers expose (`_fdyCombo` and friends): a host that binds its
|
|
164
|
+
own state needs to drive this without reaching for the class names the kit reserves. */
|
|
165
|
+
app._fdyAppShell = {
|
|
166
|
+
isVisible: navVisible,
|
|
167
|
+
setVisible: function (visible) {
|
|
168
|
+
if (visible === navVisible()) return;
|
|
169
|
+
if (mqWide.matches) {
|
|
170
|
+
app.classList.toggle('fdy-app--nav-collapsed', !visible);
|
|
171
|
+
sync();
|
|
172
|
+
} else if (visible) {
|
|
173
|
+
open();
|
|
174
|
+
} else {
|
|
175
|
+
close();
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
};
|
|
150
179
|
}
|
|
151
180
|
|
|
152
181
|
function initShells(context) {
|
|
@@ -163,5 +192,16 @@
|
|
|
163
192
|
initShells();
|
|
164
193
|
}
|
|
165
194
|
|
|
166
|
-
window.FreedayAppShell = {
|
|
195
|
+
window.FreedayAppShell = {
|
|
196
|
+
init: initShells,
|
|
197
|
+
initAll: initShells,
|
|
198
|
+
/* Both take the shell root. A missing or un-initialised root is a no-op rather than a throw:
|
|
199
|
+
a host may race the enhancer on first render, and a crash there is worse than a late sync. */
|
|
200
|
+
setVisible: function (root, visible) {
|
|
201
|
+
if (root && root._fdyAppShell) root._fdyAppShell.setVisible(visible === true);
|
|
202
|
+
},
|
|
203
|
+
isVisible: function (root) {
|
|
204
|
+
return !!(root && root._fdyAppShell && root._fdyAppShell.isVisible());
|
|
205
|
+
},
|
|
206
|
+
};
|
|
167
207
|
})();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
/* Freeday
|
|
1
|
+
/* Freeday, autocomplete enhancer (optional, zero-dependency).
|
|
2
2
|
* Editable combobox (WAI-ARIA APG): a text input filters a role="listbox" of role="option"
|
|
3
3
|
* items as you type; select fills the input. Auto-inits [data-fdy-autocomplete]. Options are
|
|
4
|
-
* static markup filtered client-side
|
|
4
|
+
* static markup filtered client-side, in an app, fetch/filter server-side and re-render options.
|
|
5
5
|
*
|
|
6
6
|
* Markup: <div data-fdy-autocomplete class="fdy-autocomplete">
|
|
7
7
|
* <input class="fdy-input" role="combobox" aria-expanded="false" aria-autocomplete="list"
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* Freeday
|
|
1
|
+
/* Freeday, breakpoint provider (optional, zero-dependency).
|
|
2
2
|
* The MudBreakpointProvider equivalent for JS consumers: stamps the current breakpoint on
|
|
3
3
|
* <html data-breakpoint="xs|sm|md|lg|xl"> and emits a bubbling "fdy-breakpoint-change"
|
|
4
4
|
* CustomEvent (detail {breakpoint}) whenever it changes. Also fills any element with
|
package/dist/freeday-carousel.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* Freeday
|
|
1
|
+
/* Freeday, carousel enhancer (optional, zero-dependency).
|
|
2
2
|
* Scroll-snap slider with prev/next arrows, generated dot indicators, keyboard (←/→),
|
|
3
3
|
* and optional autoplay (data-fdy-autoplay="4000", pauses on hover/focus). Auto-inits
|
|
4
4
|
* [data-fdy-carousel].
|
|
@@ -18,16 +18,16 @@
|
|
|
18
18
|
'use strict';
|
|
19
19
|
|
|
20
20
|
|
|
21
|
-
/* User-facing strings. Indonesian by default
|
|
22
|
-
* path
|
|
21
|
+
/* User-facing strings. Indonesian by default, documented and deliberate for the raw enhancer
|
|
22
|
+
* path, and every one overridable per element, so a host that speaks another language (the
|
|
23
23
|
* Blazor adapters, an English app on the raw path) supplies its own without forking this file.
|
|
24
24
|
* Keeping them in ONE table is also what lets a guard prove none is hard-coded further down. */
|
|
25
25
|
var TEXT = {
|
|
26
|
-
position: '{n}
|
|
26
|
+
position: '{n} of {total}',
|
|
27
27
|
slide: 'Slide {n}'
|
|
28
28
|
};
|
|
29
29
|
/* HTML lowercases attribute names, so a camelCase key like `filterText` can only ever be written
|
|
30
|
-
as `data-fdy-text-filtertext
|
|
30
|
+
as `data-fdy-text-filtertext`, while the kebab form anybody would reach for,
|
|
31
31
|
`data-fdy-text-filter-text`, becomes a DIFFERENT attribute the enhancer never reads, and the
|
|
32
32
|
override fails silently. So the key is kebab-cased for the lookup; the run-together spelling
|
|
33
33
|
still resolves, for markup written against 1.39.0. */
|
package/dist/freeday-cascade.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* Freeday
|
|
1
|
+
/* Freeday, cascade select enhancer (optional, zero-dependency).
|
|
2
2
|
* Turns a [data-fdy-cascade] wrapper into a hierarchical drill-down picker. The
|
|
3
3
|
* data model is a hidden nested <ul> inside the wrapper: a <li> with a child <ul>
|
|
4
4
|
* is a branch (drills in), a <li> without is a leaf (selects). Each <li> carries
|
|
@@ -54,16 +54,16 @@
|
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
|
|
57
|
-
/* User-facing strings. Indonesian by default
|
|
58
|
-
* path
|
|
57
|
+
/* User-facing strings. Indonesian by default, documented and deliberate for the raw enhancer
|
|
58
|
+
* path, and every one overridable per element, so a host that speaks another language (the
|
|
59
59
|
* Blazor adapters, an English app on the raw path) supplies its own without forking this file.
|
|
60
60
|
* Keeping them in ONE table is also what lets a guard prove none is hard-coded further down. */
|
|
61
61
|
var TEXT = {
|
|
62
|
-
back: '
|
|
62
|
+
back: 'Back one level',
|
|
63
63
|
submenu: '{label}, submenu'
|
|
64
64
|
};
|
|
65
65
|
/* HTML lowercases attribute names, so a camelCase key like `filterText` can only ever be written
|
|
66
|
-
as `data-fdy-text-filtertext
|
|
66
|
+
as `data-fdy-text-filtertext`, while the kebab form anybody would reach for,
|
|
67
67
|
`data-fdy-text-filter-text`, becomes a DIFFERENT attribute the enhancer never reads, and the
|
|
68
68
|
override fails silently. So the key is kebab-cased for the lookup; the run-together spelling
|
|
69
69
|
still resolves, for markup written against 1.39.0. */
|
|
@@ -88,8 +88,8 @@
|
|
|
88
88
|
var root = sourceUl ? parse(sourceUl) : [];
|
|
89
89
|
if (sourceUl) sourceUl.remove();
|
|
90
90
|
|
|
91
|
-
var label = wrap.getAttribute('data-label') || '
|
|
92
|
-
var placeholder = wrap.getAttribute('data-placeholder') || '
|
|
91
|
+
var label = wrap.getAttribute('data-label') || 'Select';
|
|
92
|
+
var placeholder = wrap.getAttribute('data-placeholder') || 'Select…';
|
|
93
93
|
var sep = wrap.getAttribute('data-separator') || ' / ';
|
|
94
94
|
|
|
95
95
|
var trigger = document.createElement('button');
|
package/dist/freeday-cfl.js
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
/* Freeday
|
|
1
|
+
/* Freeday, choose-from-list enhancer (optional, zero-dependency).
|
|
2
2
|
* Wires a searchable master-data picker dialog to read-only fields.
|
|
3
3
|
* Progressive enhancement over a native <dialog>; in a framework app, drive the
|
|
4
4
|
* dialog as a controlled component (fetchPage callback + server cache), not a store.
|
|
5
5
|
*
|
|
6
6
|
* Markup contract (see docs):
|
|
7
|
-
* - Field group: [data-fdy-cfl="<dialogId>"]
|
|
7
|
+
* - Field group: [data-fdy-cfl="<dialogId>"], usually a `.fdy-input-group` holding a
|
|
8
8
|
* read-only `.fdy-input` and a trigger `<button class="fdy-input-group__btn">`.
|
|
9
9
|
* Optional `data-fdy-cfl-display="<key>"` picks which row dataset key fills the input
|
|
10
10
|
* (default "value"); optional `data-fdy-cfl-summary="{n} dipilih"` formats the multi label.
|
|
11
|
-
* - Standalone opener: [data-fdy-cfl-open="<dialogId>"]
|
|
11
|
+
* - Standalone opener: [data-fdy-cfl-open="<dialogId>"], opens the dialog with no bound field.
|
|
12
12
|
* - Dialog: <dialog id="<dialogId>" class="fdy-modal fdy-modal--cfl"> containing
|
|
13
13
|
* a `[data-fdy-cfl-search]` input, `.fdy-cfl__row` <tr> rows carrying `data-*` fields,
|
|
14
14
|
* a `[data-fdy-cfl-empty]` element, and (multi only) `[data-fdy-cfl-multiple]` on the
|
|
15
15
|
* dialog plus `[data-fdy-cfl-count]` and `[data-fdy-cfl-confirm]` in the footer.
|
|
16
16
|
*
|
|
17
17
|
* Emits a bubbling "fdy-cfl-select" CustomEvent on the field group (or the dialog when
|
|
18
|
-
* opened standalone): detail {row} for single-select, {rows} for multi-select
|
|
18
|
+
* opened standalone): detail {row} for single-select, {rows} for multi-select, each a plain
|
|
19
19
|
* object copy of the row's dataset. Consumers fill sibling fields (code → name) from it.
|
|
20
20
|
*/
|
|
21
21
|
(function () {
|
|
@@ -34,15 +34,15 @@
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
|
|
37
|
-
/* User-facing strings. Indonesian by default
|
|
38
|
-
* path
|
|
37
|
+
/* User-facing strings. Indonesian by default, documented and deliberate for the raw enhancer
|
|
38
|
+
* path, and every one overridable per element, so a host that speaks another language (the
|
|
39
39
|
* Blazor adapters, an English app on the raw path) supplies its own without forking this file.
|
|
40
40
|
* Keeping them in ONE table is also what lets a guard prove none is hard-coded further down. */
|
|
41
41
|
var TEXT = {
|
|
42
|
-
selected: '{n}
|
|
42
|
+
selected: '{n} selected'
|
|
43
43
|
};
|
|
44
44
|
/* HTML lowercases attribute names, so a camelCase key like `filterText` can only ever be written
|
|
45
|
-
as `data-fdy-text-filtertext
|
|
45
|
+
as `data-fdy-text-filtertext`, while the kebab form anybody would reach for,
|
|
46
46
|
`data-fdy-text-filter-text`, becomes a DIFFERENT attribute the enhancer never reads, and the
|
|
47
47
|
override fails silently. So the key is kebab-cased for the lookup; the run-together spelling
|
|
48
48
|
still resolves, for markup written against 1.39.0. */
|
|
@@ -103,7 +103,7 @@
|
|
|
103
103
|
var input = activeGroup.querySelector('.fdy-input');
|
|
104
104
|
if (input) {
|
|
105
105
|
if (detail.rows) {
|
|
106
|
-
var tpl = activeGroup.getAttribute('data-fdy-cfl-summary') || '{n}
|
|
106
|
+
var tpl = activeGroup.getAttribute('data-fdy-cfl-summary') || '{n} selected';
|
|
107
107
|
input.value = detail.rows.length ? tpl.replace('{n}', String(detail.rows.length)) : '';
|
|
108
108
|
} else {
|
|
109
109
|
var key = activeGroup.getAttribute('data-fdy-cfl-display') || 'value';
|
package/dist/freeday-chart.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* Freeday
|
|
1
|
+
/* Freeday, chart renderer (optional, zero-dependency, no external chart lib).
|
|
2
2
|
* Renders sparkline / bar / line / area / donut into an element from data attributes.
|
|
3
3
|
* Auto-inits [data-fdy-chart] once; call FreedayChart.update(el) to re-render after the
|
|
4
4
|
* data attributes change (used by the freeday/vue <FdyChart> wrapper for reactive data).
|
|
@@ -6,14 +6,14 @@
|
|
|
6
6
|
* Single-series colour (sparkline / simple bar / single line) via data-fdy-color references a
|
|
7
7
|
* semantic token by name (primary, accent, success, warning, danger, info). Multi-series
|
|
8
8
|
* defaults draw from the validated categorical --chart-1..8 palette in fixed order (never
|
|
9
|
-
* cycled, 8-series cap); pass data-fdy-colors to override
|
|
9
|
+
* cycled, 8-series cap); pass data-fdy-colors to override, a name is a semantic token
|
|
10
10
|
* (var(--color-<name>)) OR a slot "chart-1".."chart-8" (var(--chart-N)) to pin a series to the
|
|
11
11
|
* validated palette. Both data-fdy-color and data-fdy-colors accept the chart-N form.
|
|
12
12
|
* Cartesian charts (line/area, multi-series/stacked bar) draw themed axes from --chart-grid /
|
|
13
13
|
* --chart-tick and format ticks + tooltips via data-fdy-format (number|percent|currency). Their
|
|
14
14
|
* viewBox is sized to the measured plot (1 user unit = 1 CSS pixel) and repainted from a
|
|
15
15
|
* ResizeObserver, so axis type stays in real pixels and is set in CSS via --fdy-chart-tick-size.
|
|
16
|
-
* Charts are an enhancement
|
|
16
|
+
* Charts are an enhancement, put a table/text fallback inside the element and it will be
|
|
17
17
|
* replaced on render; give the element role="img" + aria-label.
|
|
18
18
|
*
|
|
19
19
|
* Sparkline: <span data-fdy-chart="sparkline" data-values="4,6,5,8,7,10" data-fdy-color="primary">
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
return v ? v.split(',').map(function (s) { return s.trim(); }) : [];
|
|
51
51
|
}
|
|
52
52
|
// role="img" is Children Presentational per ARIA, so the docs used to say a chart's rendered
|
|
53
|
-
// legend, bar values and donut centre are not exposed. No browser actually prunes that subtree
|
|
53
|
+
// legend, bar values and donut centre are not exposed. No browser actually prunes that subtree,
|
|
54
54
|
// measured in Chrome's AX tree, every one of those nodes is live and unignored; what saves the
|
|
55
55
|
// advice is only that AT treats a NAMED role="img" as a leaf and does not descend. So the kit
|
|
56
56
|
// hides them itself, and the author's aria-label really is the whole text alternative.
|
|
@@ -225,7 +225,7 @@
|
|
|
225
225
|
// Lay the chrome out first so the plot can be measured, then size the viewBox to it in CSS
|
|
226
226
|
// pixels: one user unit is one pixel, so font-size / r / stroke-width mean what they say at
|
|
227
227
|
// every container width. (A fixed 320x180 viewBox stretched by CSS multiplied every declared
|
|
228
|
-
// size by plotWidth/320 instead
|
|
228
|
+
// size by plotWidth/320 instead, a 9px axis label rendered ~39px on a 1728px viewport.)
|
|
229
229
|
var legendMode = el.getAttribute('data-fdy-legend') || 'auto';
|
|
230
230
|
var showLegend = legendMode !== 'none' && (series.length >= 2 || legendMode === 'always');
|
|
231
231
|
el.classList.add('fdy-chart-xy');
|
|
@@ -246,7 +246,7 @@
|
|
|
246
246
|
el.appendChild(plot);
|
|
247
247
|
|
|
248
248
|
// Measured while empty: .fdy-chart-xy__plot carries its own width / aspect-ratio / min-height,
|
|
249
|
-
// so the box is already final. A detached or display:none chart measures 0
|
|
249
|
+
// so the box is already final. A detached or display:none chart measures 0, fall back to the
|
|
250
250
|
// legacy box and let the ResizeObserver repaint it once it is actually laid out.
|
|
251
251
|
var box = plot.getBoundingClientRect();
|
|
252
252
|
var W = Math.round(box.width) || 320, H = Math.round(box.height) || 180;
|
|
@@ -290,7 +290,7 @@
|
|
|
290
290
|
|
|
291
291
|
// x category labels with autoskip. Labels are author-supplied, so estimate the widest one instead
|
|
292
292
|
// of assuming a fixed 40px slot; keep every step-th plus the last, then drop any kept label that
|
|
293
|
-
// would collide with the one before it
|
|
293
|
+
// would collide with the one before it, including the forced last one (a fraction-of-a-slot
|
|
294
294
|
// overlap happens when n-1 lands next to the last kept multiple of step, e.g. n=24 at most widths).
|
|
295
295
|
var widest = 0;
|
|
296
296
|
for (var wi = 0; wi < n; wi++) widest = Math.max(widest, String(labels[wi] || ('#' + (wi + 1))).length * charW);
|
|
@@ -301,7 +301,7 @@
|
|
|
301
301
|
for (var xi = 0; xi < n; xi++) {
|
|
302
302
|
if (xi % step === 0 || xi === n - 1) kept.push(xi);
|
|
303
303
|
}
|
|
304
|
-
// The last label is worth forcing, but not on top of its neighbour
|
|
304
|
+
// The last label is worth forcing, but not on top of its neighbour, drop the penultimate one.
|
|
305
305
|
if (kept.length >= 2 && xPos(kept[kept.length - 1]) - xPos(kept[kept.length - 2]) < minGap) {
|
|
306
306
|
kept.splice(kept.length - 2, 1);
|
|
307
307
|
}
|
|
@@ -434,17 +434,17 @@
|
|
|
434
434
|
var li = document.createElement('li');
|
|
435
435
|
var sw = document.createElement('span'); sw.className = 'fdy-chart__swatch'; sw.style.background = colorAt(i);
|
|
436
436
|
li.appendChild(sw);
|
|
437
|
-
li.appendChild(document.createTextNode((labels[i] || ('#' + (i + 1))) + '
|
|
437
|
+
li.appendChild(document.createTextNode((labels[i] || ('#' + (i + 1))) + ' · ' + Math.round((v / total) * 100) + '%'));
|
|
438
438
|
legend.appendChild(li);
|
|
439
439
|
});
|
|
440
440
|
el.appendChild(ring);
|
|
441
|
-
// Honour data-fdy-legend for the donut too
|
|
441
|
+
// Honour data-fdy-legend for the donut too, a caller may supply its own richer legend. 'auto'
|
|
442
442
|
// keeps today's behaviour (a donut is unreadable without labels, so it shows); 'none' suppresses it.
|
|
443
443
|
if ((el.getAttribute('data-fdy-legend') || 'auto') !== 'none') el.appendChild(legend);
|
|
444
444
|
ensureImg(el);
|
|
445
445
|
}
|
|
446
446
|
|
|
447
|
-
// Full (re-)render from current data attributes. Idempotent
|
|
447
|
+
// Full (re-)render from current data attributes. Idempotent, safe to call repeatedly.
|
|
448
448
|
function renderChart(el) {
|
|
449
449
|
var type = el.getAttribute('data-fdy-chart');
|
|
450
450
|
if (type === 'sparkline') renderSparkline(el);
|
package/dist/freeday-chip.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
/* Freeday
|
|
1
|
+
/* Freeday, chip enhancer (optional, zero-dependency).
|
|
2
2
|
* Two behaviours, both progressive enhancement:
|
|
3
3
|
*
|
|
4
|
-
* 1. Removable chips
|
|
4
|
+
* 1. Removable chips, any .fdy-chip__remove button removes its chip and emits a
|
|
5
5
|
* bubbling "fdy-chip-remove" (detail {value}).
|
|
6
|
-
* 2. Choice / filter groups
|
|
6
|
+
* 2. Choice / filter groups, a [data-fdy-chips] container of interactive chips
|
|
7
7
|
* (.fdy-chip--choice / .fdy-chip--filter, each a <button>) toggles aria-pressed
|
|
8
8
|
* on click. Add data-single for one-at-a-time (radio-like) selection; filter
|
|
9
9
|
* chips get a leading check when pressed. Emits "fdy-chip-change" on the group
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
/* Freeday
|
|
1
|
+
/* Freeday, date picker enhancer (optional, zero-dependency).
|
|
2
2
|
* Builds an input-styled trigger + calendar popover from an empty wrapper.
|
|
3
|
-
* Locale comes from <html lang> (via Intl)
|
|
3
|
+
* Locale comes from <html lang> (via Intl), month/weekday/value formatting is not hardcoded.
|
|
4
4
|
*
|
|
5
5
|
* Markup contract:
|
|
6
6
|
* - Single: <div data-fdy-datepicker data-value="2026-07-21" data-label="Tanggal unggah"
|
|
@@ -19,7 +19,11 @@
|
|
|
19
19
|
(function () {
|
|
20
20
|
'use strict';
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
/* The page's own `lang` wins, which is the better escape hatch than any attribute the kit
|
|
23
|
+
could invent: an Indonesian app writes <html lang="id"> and gets Indonesian month and
|
|
24
|
+
weekday names back automatically. The FALLBACK follows the kit's default language, or a
|
|
25
|
+
page without `lang` would read English labels around Indonesian month names. */
|
|
26
|
+
var LOCALE = document.documentElement.getAttribute('lang') || 'en';
|
|
23
27
|
var uidSeq = 0;
|
|
24
28
|
function uid(p) { uidSeq += 1; return p + '-' + uidSeq; }
|
|
25
29
|
function pad(n) { return n < 10 ? '0' + n : '' + n; }
|
|
@@ -66,10 +70,10 @@
|
|
|
66
70
|
var view = startOfDay(selected || new Date());
|
|
67
71
|
view = new Date(view.getFullYear(), view.getMonth(), 1);
|
|
68
72
|
var focusDate = null;
|
|
69
|
-
/* 'days' | 'months'
|
|
73
|
+
/* 'days' | 'months', the calendar drills one level up rather than growing furniture beside the
|
|
70
74
|
title. Before this, the only pointer route to another month was one click per month: from
|
|
71
75
|
August 2026 to March 2022 is fifty-three of them. Shift+PageUp already jumped a year, but a
|
|
72
|
-
shortcut nobody can see is not an affordance
|
|
76
|
+
shortcut nobody can see is not an affordance, the repo's own test helper clicked "previous
|
|
73
77
|
month" twenty-four times to reach a date two years back. */
|
|
74
78
|
var mode = 'days';
|
|
75
79
|
var focusMonth = null;
|
|
@@ -268,7 +272,7 @@
|
|
|
268
272
|
var YEARS_PER_PAGE = 12;
|
|
269
273
|
function yearPageStart(y) { return Math.floor(y / YEARS_PER_PAGE) * YEARS_PER_PAGE; }
|
|
270
274
|
|
|
271
|
-
/* A year is only unreachable when EVERY month in it falls outside min/max
|
|
275
|
+
/* A year is only unreachable when EVERY month in it falls outside min/max, the same rule
|
|
272
276
|
monthDisabled applies one level down, for the same reason: a bound that sits inside the year
|
|
273
277
|
disables neither end. */
|
|
274
278
|
function yearDisabled(y) {
|
|
@@ -513,7 +517,7 @@
|
|
|
513
517
|
var f = fromApi.getValue();
|
|
514
518
|
var t = toApi.getValue();
|
|
515
519
|
toApi.setMin(f || null); // end can't precede start (one-directional, per Foundation)
|
|
516
|
-
fromApi.setRange(f, t); // start stays free
|
|
520
|
+
fromApi.setRange(f, t); // start stays free, picking a later start clears the end below
|
|
517
521
|
toApi.setRange(f, t);
|
|
518
522
|
}
|
|
519
523
|
fromEl.addEventListener('fdy-datepicker-change', function () {
|
package/dist/freeday-datetime.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
/* Freeday
|
|
1
|
+
/* Freeday, datetime picker composer (optional, zero-dependency).
|
|
2
2
|
* Combines a child date picker and time picker into one value. It does not build
|
|
3
|
-
* anything itself
|
|
3
|
+
* anything itself, freeday-datepicker.js and freeday-timepicker.js enhance the
|
|
4
4
|
* children; this only listens for their change events and emits a single combined
|
|
5
5
|
* "fdy-datetime-change" (detail {date, time, value}). value is "YYYY-MM-DDTHH:MM"
|
|
6
6
|
* when both parts are set, else "". Icon variants are controlled per child picker
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* <div data-fdy-datepicker data-value="2026-07-21"></div>
|
|
11
11
|
* <div data-fdy-timepicker data-value="14:30"></div></div>
|
|
12
12
|
* State: add data-fdy-disabled or data-fdy-invalid on the wrapper to reflect it onto both
|
|
13
|
-
* child triggers as one control (read-only is adapter-only
|
|
13
|
+
* child triggers as one control (read-only is adapter-only, a vanilla datetime is interactive).
|
|
14
14
|
*/
|
|
15
15
|
(function () {
|
|
16
16
|
'use strict';
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
if (!dp || !tp) return;
|
|
26
26
|
|
|
27
27
|
// Reflect one wrapper-level state onto BOTH child triggers so a datetime reads as a single
|
|
28
|
-
// disabled/invalid control. Deferred with setTimeout(0)
|
|
28
|
+
// disabled/invalid control. Deferred with setTimeout(0). NOT a microtask: the timepicker
|
|
29
29
|
// enhancer registers its DOMContentLoaded init AFTER this composer, and microtasks drain
|
|
30
30
|
// *between* listeners (so a microtask would run before the timepicker trigger exists). A
|
|
31
31
|
// macrotask waits until the whole init pass is done and both triggers are built. (readonly is
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
}));
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
// Listen for the children's bubbling change events (order-independent
|
|
59
|
+
// Listen for the children's bubbling change events (order-independent, no need
|
|
60
60
|
// for the child enhancers to have run before this composer).
|
|
61
61
|
wrap.addEventListener('fdy-datepicker-change', function (e) { date = e.detail.value; emit(); });
|
|
62
62
|
wrap.addEventListener('fdy-time-select', function (e) { time = e.detail.value; emit(); });
|
package/dist/freeday-drawer.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* Freeday
|
|
1
|
+
/* Freeday, drawer enhancer (optional, zero-dependency).
|
|
2
2
|
* A trigger [data-fdy-drawer="drawerId"] opens the <dialog class="fdy-drawer" id="drawerId">
|
|
3
3
|
* as a modal side panel (native showModal gives focus-trap, Esc, and return-focus). Clicking
|
|
4
4
|
* the backdrop or any [data-close] inside closes it.
|
package/dist/freeday-form.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* Freeday
|
|
1
|
+
/* Freeday, form validation enhancer (optional, zero-dependency).
|
|
2
2
|
* Wires the native Constraint Validation API to accessible inline errors on any
|
|
3
3
|
* [data-fdy-validate] <form>. Native-first: constraints come from the standard
|
|
4
4
|
* markup attributes (required, type, pattern, min/max, minlength/maxlength, step);
|
|
@@ -38,24 +38,24 @@
|
|
|
38
38
|
badInput: 'type',
|
|
39
39
|
customError: 'mismatch'
|
|
40
40
|
};
|
|
41
|
-
/* User-facing strings. Indonesian by default
|
|
42
|
-
* path
|
|
41
|
+
/* User-facing strings. Indonesian by default, documented and deliberate for the raw enhancer
|
|
42
|
+
* path, and overridable at three levels, narrowest first: `data-fdy-msg-<alias>` on the field,
|
|
43
43
|
* `data-fdy-msg` on the field, then `data-fdy-text-<alias>` on the FORM. The form level is what
|
|
44
44
|
* a host in another language needs: it sets nine messages once instead of on every input.
|
|
45
45
|
* Keeping them in ONE table is also what lets a guard prove none is hard-coded further down. */
|
|
46
46
|
var TEXT = {
|
|
47
|
-
required: '
|
|
48
|
-
type: '
|
|
49
|
-
pattern: '
|
|
50
|
-
minlength: '
|
|
51
|
-
maxlength: '
|
|
52
|
-
min: '
|
|
53
|
-
max: '
|
|
54
|
-
step: '
|
|
55
|
-
mismatch: '
|
|
56
|
-
invalid: '
|
|
47
|
+
required: 'Required.',
|
|
48
|
+
type: 'Invalid format.',
|
|
49
|
+
pattern: 'Doesn’t match the required format.',
|
|
50
|
+
minlength: 'Too short.',
|
|
51
|
+
maxlength: 'Too long.',
|
|
52
|
+
min: 'Too small.',
|
|
53
|
+
max: 'Too large.',
|
|
54
|
+
step: 'Not a valid step.',
|
|
55
|
+
mismatch: 'Values don’t match.',
|
|
56
|
+
invalid: 'Invalid.'
|
|
57
57
|
};
|
|
58
|
-
/* Kebab-cased for the lookup
|
|
58
|
+
/* Kebab-cased for the lookup, see the note in the other enhancers: HTML lowercases attribute
|
|
59
59
|
names, so `data-fdy-text-filter-text` and `data-fdy-text-filtertext` are different attributes
|
|
60
60
|
and only one of them is what an author would write. */
|
|
61
61
|
function textOf(root, key) {
|