@cahyo-dimas/freeday 2.0.0 → 2.2.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 +155 -0
- package/COMPONENTS.md +298 -2
- package/README.id.md +1 -1
- package/README.md +1 -1
- package/adapters/blazor/FdyAutocomplete.razor +4 -1
- package/adapters/blazor/FdyAutocomplete.razor.cs +26 -0
- package/adapters/blazor/FdyCascade.razor +6 -1
- package/adapters/blazor/FdyCascade.razor.cs +24 -0
- package/adapters/blazor/FdyCombo.razor +1 -0
- package/adapters/blazor/FdyCombo.razor.cs +13 -0
- package/adapters/blazor/FdyDatepicker.razor +15 -1
- package/adapters/blazor/FdyDatepicker.razor.cs +46 -0
- package/adapters/core/cfl-value.d.ts +11 -0
- package/adapters/core/cfl-value.js +31 -0
- package/adapters/react/components/FdyDrawer.tsx +3 -1
- package/adapters/react/components/FdyModal.tsx +3 -1
- package/adapters/react/index.d.ts +3 -0
- package/adapters/react/index.js +1 -0
- package/adapters/vue/components/FdyDrawer.vue +4 -1
- package/adapters/vue/components/FdyModal.vue +4 -1
- package/adapters/vue/index.d.ts +3 -0
- package/adapters/vue/index.js +1 -0
- package/dist/freeday-autocomplete.js +17 -1
- package/dist/freeday-carousel.js +5 -4
- package/dist/freeday-cascade.js +58 -7
- package/dist/freeday-cfl.js +5 -4
- package/dist/freeday-chart.js +33 -3
- package/dist/freeday-datepicker.js +109 -17
- package/dist/freeday-form.js +5 -4
- package/dist/freeday-mask.js +5 -4
- package/dist/freeday-select.js +18 -1
- package/dist/freeday-stepper.js +5 -4
- package/dist/freeday-table.js +9 -6
- package/dist/freeday-timepicker.js +19 -1
- package/dist/freeday-toast.js +5 -4
- package/dist/freeday-upload.js +5 -4
- package/dist/freeday.js +298 -64
- package/docs/agent-onboarding.md +9 -6
- package/docs/getting-started.md +15 -6
- package/docs/integrations.md +2 -2
- package/package.json +2 -2
package/dist/freeday.js
CHANGED
|
@@ -250,6 +250,9 @@
|
|
|
250
250
|
var _pop = null;
|
|
251
251
|
function popCtl() { if (_pop === null && window.FreedayPopover) _pop = window.FreedayPopover.attach(listbox, input); return _pop; }
|
|
252
252
|
function open() {
|
|
253
|
+
/* The input carries these natively; a disabled one fires nothing, but a READONLY one still
|
|
254
|
+
takes focus and clicks, and the list used to open over a field nobody can edit. */
|
|
255
|
+
if (input.readOnly || input.disabled) return;
|
|
253
256
|
if (!listbox.hidden) return;
|
|
254
257
|
var p = popCtl(); if (p) p.show(); else listbox.hidden = false;
|
|
255
258
|
input.setAttribute('aria-expanded', 'true');
|
|
@@ -344,7 +347,20 @@
|
|
|
344
347
|
initAll();
|
|
345
348
|
}
|
|
346
349
|
|
|
347
|
-
|
|
350
|
+
/* Same contract as the combo's: the states live on the input natively, and a host that stops
|
|
351
|
+
re-rendering after hydration needs a way to change them afterwards. */
|
|
352
|
+
function setState(root, state) {
|
|
353
|
+
var input = root ? root.querySelector('input') : null;
|
|
354
|
+
if (!input || !state) return;
|
|
355
|
+
if (state.disabled != null) input.disabled = !!state.disabled;
|
|
356
|
+
if (state.readonly != null) input.readOnly = !!state.readonly;
|
|
357
|
+
if (state.invalid != null) {
|
|
358
|
+
if (state.invalid) input.setAttribute('aria-invalid', 'true');
|
|
359
|
+
else input.removeAttribute('aria-invalid');
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
window.FreedayAutocomplete = { init: initAutocomplete, initAll: initAll, setState: setState };
|
|
348
364
|
})();
|
|
349
365
|
|
|
350
366
|
/* Freeday, breakpoint provider (optional, zero-dependency).
|
|
@@ -419,10 +435,11 @@
|
|
|
419
435
|
'use strict';
|
|
420
436
|
|
|
421
437
|
|
|
422
|
-
/* User-facing strings.
|
|
423
|
-
* path, and every one overridable per element
|
|
424
|
-
*
|
|
425
|
-
* Keeping them in ONE table is also what lets a guard prove none is
|
|
438
|
+
/* User-facing strings. English by default since 2.0.0, documented and deliberate for the raw
|
|
439
|
+
* enhancer path, and every one overridable per element with `data-fdy-text-<key>`, so a host
|
|
440
|
+
* that speaks another language (an Indonesian app on the raw path) supplies its own without
|
|
441
|
+
* forking this file. Keeping them in ONE table is also what lets a guard prove none is
|
|
442
|
+
* hard-coded further down. */
|
|
426
443
|
var TEXT = {
|
|
427
444
|
position: '{n} of {total}',
|
|
428
445
|
slide: 'Slide {n}'
|
|
@@ -596,11 +613,14 @@
|
|
|
596
613
|
}
|
|
597
614
|
|
|
598
615
|
|
|
599
|
-
/* User-facing strings.
|
|
600
|
-
* path, and every one overridable per element
|
|
601
|
-
*
|
|
602
|
-
* Keeping them in ONE table is also what lets a guard prove none is
|
|
616
|
+
/* User-facing strings. English by default since 2.0.0, documented and deliberate for the raw
|
|
617
|
+
* enhancer path, and every one overridable per element with `data-fdy-text-<key>`, so a host
|
|
618
|
+
* that speaks another language (an Indonesian app on the raw path) supplies its own without
|
|
619
|
+
* forking this file. Keeping them in ONE table is also what lets a guard prove none is
|
|
620
|
+
* hard-coded further down. */
|
|
603
621
|
var TEXT = {
|
|
622
|
+
label: 'Select',
|
|
623
|
+
placeholder: 'Select…',
|
|
604
624
|
back: 'Back one level',
|
|
605
625
|
submenu: '{label}, submenu'
|
|
606
626
|
};
|
|
@@ -630,8 +650,8 @@
|
|
|
630
650
|
var root = sourceUl ? parse(sourceUl) : [];
|
|
631
651
|
if (sourceUl) sourceUl.remove();
|
|
632
652
|
|
|
633
|
-
var label = wrap.getAttribute('data-label') || '
|
|
634
|
-
var placeholder = wrap.getAttribute('data-placeholder') || '
|
|
653
|
+
var label = wrap.getAttribute('data-label') || textOf(wrap, 'label');
|
|
654
|
+
var placeholder = wrap.getAttribute('data-placeholder') || textOf(wrap, 'placeholder');
|
|
635
655
|
var sep = wrap.getAttribute('data-separator') || ' / ';
|
|
636
656
|
|
|
637
657
|
var trigger = document.createElement('button');
|
|
@@ -746,7 +766,44 @@
|
|
|
746
766
|
|
|
747
767
|
var _pop = null;
|
|
748
768
|
function popCtl() { if (_pop === null && window.FreedayPopover) _pop = window.FreedayPopover.attach(panel, trigger); return _pop; }
|
|
769
|
+
|
|
770
|
+
/* The three field states the CSS has always styled (`:disabled`, `[aria-readonly="true"]`,
|
|
771
|
+
`[aria-invalid="true"]`) and this enhancer never set, so only the stacks that re-implement
|
|
772
|
+
the control natively had them. Read from the seed at init and settable afterwards, because
|
|
773
|
+
a host that renders once — every Blazor wrapper does, `ShouldRender => false` — cannot
|
|
774
|
+
express a later change any other way. */
|
|
775
|
+
function flagOf(name) {
|
|
776
|
+
var v = wrap.getAttribute('data-' + name);
|
|
777
|
+
return v != null && v !== 'false';
|
|
778
|
+
}
|
|
779
|
+
/* Named `state*` to match the datepicker, where `is*` collided with an older function. */
|
|
780
|
+
var stateDisabled = flagOf('disabled');
|
|
781
|
+
var stateReadonly = flagOf('readonly');
|
|
782
|
+
var stateInvalid = flagOf('invalid');
|
|
783
|
+
/* `data-id` and `data-describedby`: the trigger this enhancer BUILDS is the element a form
|
|
784
|
+
has to point its label and its error text at, and the raw path had no way to say so. */
|
|
785
|
+
if (wrap.getAttribute('data-id')) trigger.id = wrap.getAttribute('data-id');
|
|
786
|
+
if (wrap.getAttribute('data-describedby')) trigger.setAttribute('aria-describedby', wrap.getAttribute('data-describedby'));
|
|
787
|
+
|
|
788
|
+
function applyState() {
|
|
789
|
+
trigger.disabled = stateDisabled;
|
|
790
|
+
if (stateReadonly) trigger.setAttribute('aria-readonly', 'true');
|
|
791
|
+
else trigger.removeAttribute('aria-readonly');
|
|
792
|
+
if (stateInvalid) trigger.setAttribute('aria-invalid', 'true');
|
|
793
|
+
else trigger.removeAttribute('aria-invalid');
|
|
794
|
+
wrap.classList.toggle('fdy-cascade--error', stateInvalid);
|
|
795
|
+
if ((stateDisabled || stateReadonly) && !panel.hidden) close(false);
|
|
796
|
+
}
|
|
797
|
+
function setState(next) {
|
|
798
|
+
if (!next) return;
|
|
799
|
+
if (next.disabled != null) stateDisabled = !!next.disabled;
|
|
800
|
+
if (next.readonly != null) stateReadonly = !!next.readonly;
|
|
801
|
+
if (next.invalid != null) stateInvalid = !!next.invalid;
|
|
802
|
+
applyState();
|
|
803
|
+
}
|
|
804
|
+
|
|
749
805
|
function open() {
|
|
806
|
+
if (stateDisabled || stateReadonly) return;
|
|
750
807
|
if (!panel.hidden) return;
|
|
751
808
|
// Re-open at the selected leaf's level for quick re-selection.
|
|
752
809
|
var trail = selectedValue ? pathTo(root, selectedValue, []) : null;
|
|
@@ -801,8 +858,11 @@
|
|
|
801
858
|
valueSpan.classList.add('fdy-cascade__value--placeholder');
|
|
802
859
|
}
|
|
803
860
|
|
|
861
|
+
applyState();
|
|
862
|
+
|
|
804
863
|
var api = {
|
|
805
864
|
wrap: wrap,
|
|
865
|
+
setState: setState,
|
|
806
866
|
getValue: function () { return selectedValue; },
|
|
807
867
|
clear: function () { selectedValue = ''; valueSpan.textContent = placeholder; valueSpan.classList.add('fdy-cascade__value--placeholder'); }
|
|
808
868
|
};
|
|
@@ -823,7 +883,15 @@
|
|
|
823
883
|
initAll();
|
|
824
884
|
}
|
|
825
885
|
|
|
826
|
-
window.FreedayCascade = {
|
|
886
|
+
window.FreedayCascade = {
|
|
887
|
+
init: initCascade,
|
|
888
|
+
initAll: initAll,
|
|
889
|
+
/* Same reason as the datepicker's: a seed rendered once still has to be lockable later. */
|
|
890
|
+
setState: function (root, state) {
|
|
891
|
+
var api = root && root._fdyCascade ? root._fdyCascade : null;
|
|
892
|
+
if (api && api.setState) api.setState(state);
|
|
893
|
+
}
|
|
894
|
+
};
|
|
827
895
|
})();
|
|
828
896
|
|
|
829
897
|
/* Freeday, choose-from-list enhancer (optional, zero-dependency).
|
|
@@ -862,10 +930,11 @@
|
|
|
862
930
|
}
|
|
863
931
|
|
|
864
932
|
|
|
865
|
-
/* User-facing strings.
|
|
866
|
-
* path, and every one overridable per element
|
|
867
|
-
*
|
|
868
|
-
* Keeping them in ONE table is also what lets a guard prove none is
|
|
933
|
+
/* User-facing strings. English by default since 2.0.0, documented and deliberate for the raw
|
|
934
|
+
* enhancer path, and every one overridable per element with `data-fdy-text-<key>`, so a host
|
|
935
|
+
* that speaks another language (an Indonesian app on the raw path) supplies its own without
|
|
936
|
+
* forking this file. Keeping them in ONE table is also what lets a guard prove none is
|
|
937
|
+
* hard-coded further down. */
|
|
869
938
|
var TEXT = {
|
|
870
939
|
selected: '{n} selected'
|
|
871
940
|
};
|
|
@@ -1101,6 +1170,28 @@
|
|
|
1101
1170
|
|
|
1102
1171
|
var NS = 'http://www.w3.org/2000/svg';
|
|
1103
1172
|
|
|
1173
|
+
/* User-facing strings. Two of them, and both shipped wrong until 2.2.0: the legend's fallback
|
|
1174
|
+
* label read `Seri 1` — Indonesian, three months after 2.0.0 turned every enhancer English —
|
|
1175
|
+
* and the donut's centre caption was hard-coded, so no host could rename it. Neither was
|
|
1176
|
+
* reachable by the guards: one goes into the DOM through `createTextNode`, the other through
|
|
1177
|
+
* `innerHTML`, and both guards look for `textContent` / `setAttribute`. Overridable per element
|
|
1178
|
+
* with `data-fdy-text-<key>`, like every other enhancer. */
|
|
1179
|
+
var TEXT = {
|
|
1180
|
+
series: 'Series {n}',
|
|
1181
|
+
total: 'Total'
|
|
1182
|
+
};
|
|
1183
|
+
function textAttr(root, key) {
|
|
1184
|
+
if (!root || !root.getAttribute) return null;
|
|
1185
|
+
var kebab = root.getAttribute('data-fdy-text-' + key.replace(/[A-Z]/g, function (c) { return '-' + c.toLowerCase(); }));
|
|
1186
|
+
return kebab != null && kebab !== '' ? kebab : root.getAttribute('data-fdy-text-' + key);
|
|
1187
|
+
}
|
|
1188
|
+
function textOf(root, key, vars) {
|
|
1189
|
+
var custom = textAttr(root, key);
|
|
1190
|
+
var s = custom != null && custom !== '' ? custom : TEXT[key];
|
|
1191
|
+
if (vars) for (var k in vars) if (Object.prototype.hasOwnProperty.call(vars, k)) s = s.split('{' + k + '}').join(vars[k]);
|
|
1192
|
+
return s;
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1104
1195
|
// Categorical chart palette: 8 validated fixed-order slots (--chart-1..8). Series index i
|
|
1105
1196
|
// (0-based) -> slot i+1; series beyond the 8-slot cap reuse --chart-8 (never cycled).
|
|
1106
1197
|
function chartSlotVar(i) { return 'var(--chart-' + (i < 8 ? i + 1 : 8) + ')'; }
|
|
@@ -1309,7 +1400,7 @@
|
|
|
1309
1400
|
var li = document.createElement('li');
|
|
1310
1401
|
var sw = document.createElement('span'); sw.className = 'fdy-chart__swatch'; sw.style.background = colorFor(si);
|
|
1311
1402
|
li.appendChild(sw);
|
|
1312
|
-
li.appendChild(document.createTextNode(s.label || (
|
|
1403
|
+
li.appendChild(document.createTextNode(s.label || textOf(el, 'series', { n: si + 1 })));
|
|
1313
1404
|
legend.appendChild(li);
|
|
1314
1405
|
});
|
|
1315
1406
|
el.appendChild(legend);
|
|
@@ -1483,8 +1574,16 @@
|
|
|
1483
1574
|
ring.style.background = 'conic-gradient(' + stops.join(',') + ')';
|
|
1484
1575
|
var center = document.createElement('div'); center.className = 'fdy-donut__center';
|
|
1485
1576
|
var centerLabel = el.getAttribute('data-fdy-center');
|
|
1486
|
-
|
|
1487
|
-
|
|
1577
|
+
var centerValue = document.createElement('b');
|
|
1578
|
+
centerValue.textContent = centerLabel != null ? centerLabel : String(total);
|
|
1579
|
+
center.appendChild(centerValue);
|
|
1580
|
+
/* Built rather than assigned as innerHTML: the caption is overridable now, and an author's
|
|
1581
|
+
string is not markup. */
|
|
1582
|
+
if (!centerLabel) {
|
|
1583
|
+
var centerCaption = document.createElement('span');
|
|
1584
|
+
centerCaption.textContent = textOf(el, 'total');
|
|
1585
|
+
center.appendChild(centerCaption);
|
|
1586
|
+
}
|
|
1488
1587
|
ring.appendChild(center);
|
|
1489
1588
|
var svg = svgEl('svg');
|
|
1490
1589
|
svg.setAttribute('class', 'fdy-donut__hit');
|
|
@@ -1643,12 +1742,12 @@
|
|
|
1643
1742
|
* Locale comes from <html lang> (via Intl), month/weekday/value formatting is not hardcoded.
|
|
1644
1743
|
*
|
|
1645
1744
|
* Markup contract:
|
|
1646
|
-
* - Single: <div data-fdy-datepicker data-value="2026-07-21" data-label="
|
|
1647
|
-
* data-placeholder="
|
|
1648
|
-
* - Range: <div data-fdy-daterange role="group" aria-label="
|
|
1649
|
-
* <div data-fdy-datepicker data-role="from" data-placeholder="
|
|
1745
|
+
* - Single: <div data-fdy-datepicker data-value="2026-07-21" data-label="Upload date"
|
|
1746
|
+
* data-placeholder="Choose a date" data-min="2026-01-01" data-max="2026-12-31"></div>
|
|
1747
|
+
* - Range: <div data-fdy-daterange role="group" aria-label="Date range">
|
|
1748
|
+
* <div data-fdy-datepicker data-role="from" data-placeholder="From"></div>
|
|
1650
1749
|
* <span class="fdy-daterange__sep">–</span>
|
|
1651
|
-
* <div data-fdy-datepicker data-role="to" data-placeholder="
|
|
1750
|
+
* <div data-fdy-datepicker data-role="to" data-placeholder="To"></div>
|
|
1652
1751
|
* </div>
|
|
1653
1752
|
* The range links the two: the end can never precede the start (out-of-range days disable).
|
|
1654
1753
|
*
|
|
@@ -1664,6 +1763,47 @@
|
|
|
1664
1763
|
weekday names back automatically. The FALLBACK follows the kit's default language, or a
|
|
1665
1764
|
page without `lang` would read English labels around Indonesian month names. */
|
|
1666
1765
|
var LOCALE = document.documentElement.getAttribute('lang') || 'en';
|
|
1766
|
+
|
|
1767
|
+
/* User-facing strings. English by default, and every one overridable per element with
|
|
1768
|
+
* `data-fdy-text-<key>`, so a host that speaks another language (an Indonesian app on the raw
|
|
1769
|
+
* path, and every Blazor app, whose picker IS this enhancer) supplies its own without forking
|
|
1770
|
+
* this file.
|
|
1771
|
+
*
|
|
1772
|
+
* This table arrived late, in 2.2.0: the ten labels below were written as literals passed to
|
|
1773
|
+
* `navButton()` / `titleButton()`, so the guard that proves no enhancer string is hard-coded
|
|
1774
|
+
* never saw them — it looks for the line that writes to the DOM, and here that line only ever
|
|
1775
|
+
* sees a variable. Month and weekday names are NOT here on purpose: they come from `Intl`
|
|
1776
|
+
* through the page's `lang`, which is a better hatch than anything the kit could invent.
|
|
1777
|
+
* The `{label}` in the three title strings is the period the button drills into. */
|
|
1778
|
+
var TEXT = {
|
|
1779
|
+
label: 'Date',
|
|
1780
|
+
placeholder: 'Choose a date',
|
|
1781
|
+
prevMonth: 'Previous month',
|
|
1782
|
+
nextMonth: 'Next month',
|
|
1783
|
+
prevYear: 'Previous year',
|
|
1784
|
+
nextYear: 'Next year',
|
|
1785
|
+
prevYears: 'Previous years',
|
|
1786
|
+
nextYears: 'Next years',
|
|
1787
|
+
chooseMonth: '{label}, choose month',
|
|
1788
|
+
chooseYear: '{label}, choose year',
|
|
1789
|
+
backToMonths: '{start} to {end}, back to months'
|
|
1790
|
+
};
|
|
1791
|
+
/* HTML lowercases attribute names, so a camelCase key like `prevMonth` can only ever be written
|
|
1792
|
+
as `data-fdy-text-prevmonth`, while the kebab form anybody would reach for,
|
|
1793
|
+
`data-fdy-text-prev-month`, becomes a DIFFERENT attribute the enhancer never reads, and the
|
|
1794
|
+
override fails silently. So the key is kebab-cased for the lookup; the run-together spelling
|
|
1795
|
+
still resolves. */
|
|
1796
|
+
function textAttr(root, key) {
|
|
1797
|
+
if (!root || !root.getAttribute) return null;
|
|
1798
|
+
var kebab = root.getAttribute('data-fdy-text-' + key.replace(/[A-Z]/g, function (c) { return '-' + c.toLowerCase(); }));
|
|
1799
|
+
return kebab != null && kebab !== '' ? kebab : root.getAttribute('data-fdy-text-' + key);
|
|
1800
|
+
}
|
|
1801
|
+
function textOf(root, key, vars) {
|
|
1802
|
+
var custom = textAttr(root, key);
|
|
1803
|
+
var s = custom != null && custom !== '' ? custom : TEXT[key];
|
|
1804
|
+
if (vars) for (var k in vars) if (Object.prototype.hasOwnProperty.call(vars, k)) s = s.split('{' + k + '}').join(vars[k]);
|
|
1805
|
+
return s;
|
|
1806
|
+
}
|
|
1667
1807
|
var uidSeq = 0;
|
|
1668
1808
|
function uid(p) { uidSeq += 1; return p + '-' + uidSeq; }
|
|
1669
1809
|
function pad(n) { return n < 10 ? '0' + n : '' + n; }
|
|
@@ -1701,8 +1841,8 @@
|
|
|
1701
1841
|
wrap.dataset.fdyDpReady = '1';
|
|
1702
1842
|
wrap.classList.add('fdy-datepicker');
|
|
1703
1843
|
|
|
1704
|
-
var placeholder = wrap.getAttribute('data-placeholder') ||
|
|
1705
|
-
var label = wrap.getAttribute('data-label') || '
|
|
1844
|
+
var placeholder = wrap.getAttribute('data-placeholder') || textOf(wrap, 'placeholder');
|
|
1845
|
+
var label = wrap.getAttribute('data-label') || textOf(wrap, 'label');
|
|
1706
1846
|
var selected = parseISO(wrap.getAttribute('data-value'));
|
|
1707
1847
|
var minDate = parseISO(wrap.getAttribute('data-min'));
|
|
1708
1848
|
var maxDate = parseISO(wrap.getAttribute('data-max'));
|
|
@@ -1800,16 +1940,16 @@
|
|
|
1800
1940
|
panel.innerHTML = '';
|
|
1801
1941
|
var head = document.createElement('div');
|
|
1802
1942
|
head.className = 'fdy-cal__head';
|
|
1803
|
-
var title = titleButton(monthFmt.format(view), monthFmt.format(view)
|
|
1943
|
+
var title = titleButton(monthFmt.format(view), textOf(wrap, 'chooseMonth', { label: monthFmt.format(view) }), function () {
|
|
1804
1944
|
mode = 'months';
|
|
1805
1945
|
focusMonth = view.getMonth();
|
|
1806
1946
|
render();
|
|
1807
1947
|
focusMonthCell();
|
|
1808
1948
|
});
|
|
1809
1949
|
panel.setAttribute('aria-labelledby', title.id);
|
|
1810
|
-
head.appendChild(navButton('‹',
|
|
1950
|
+
head.appendChild(navButton('‹', textOf(wrap, 'prevMonth'), function () { view = addMonths(view, -1); render(); }));
|
|
1811
1951
|
head.appendChild(title);
|
|
1812
|
-
head.appendChild(navButton('›',
|
|
1952
|
+
head.appendChild(navButton('›', textOf(wrap, 'nextMonth'), function () { view = addMonths(view, 1); render(); }));
|
|
1813
1953
|
panel.appendChild(head);
|
|
1814
1954
|
|
|
1815
1955
|
var grid = document.createElement('div');
|
|
@@ -1866,16 +2006,16 @@
|
|
|
1866
2006
|
var year = view.getFullYear();
|
|
1867
2007
|
var head = document.createElement('div');
|
|
1868
2008
|
head.className = 'fdy-cal__head';
|
|
1869
|
-
var title = titleButton(String(year),
|
|
2009
|
+
var title = titleButton(String(year), textOf(wrap, 'chooseYear', { label: year }), function () {
|
|
1870
2010
|
mode = 'years';
|
|
1871
2011
|
focusYear = year;
|
|
1872
2012
|
render();
|
|
1873
2013
|
focusYearCell();
|
|
1874
2014
|
});
|
|
1875
2015
|
panel.setAttribute('aria-labelledby', title.id);
|
|
1876
|
-
head.appendChild(navButton('‹',
|
|
2016
|
+
head.appendChild(navButton('‹', textOf(wrap, 'prevYear'), function () { view = addMonths(view, -12); render(); focusMonthCell(); }));
|
|
1877
2017
|
head.appendChild(title);
|
|
1878
|
-
head.appendChild(navButton('›',
|
|
2018
|
+
head.appendChild(navButton('›', textOf(wrap, 'nextYear'), function () { view = addMonths(view, 12); render(); focusMonthCell(); }));
|
|
1879
2019
|
panel.appendChild(head);
|
|
1880
2020
|
|
|
1881
2021
|
var grid = document.createElement('div');
|
|
@@ -1927,16 +2067,16 @@
|
|
|
1927
2067
|
var end = start + YEARS_PER_PAGE - 1;
|
|
1928
2068
|
var head = document.createElement('div');
|
|
1929
2069
|
head.className = 'fdy-cal__head';
|
|
1930
|
-
var title = titleButton(start + ' – ' + end,
|
|
2070
|
+
var title = titleButton(start + ' – ' + end, textOf(wrap, 'backToMonths', { start: start, end: end }), function () {
|
|
1931
2071
|
mode = 'months';
|
|
1932
2072
|
focusMonth = view.getMonth();
|
|
1933
2073
|
render();
|
|
1934
2074
|
focusMonthCell();
|
|
1935
2075
|
});
|
|
1936
2076
|
panel.setAttribute('aria-labelledby', title.id);
|
|
1937
|
-
head.appendChild(navButton('‹',
|
|
2077
|
+
head.appendChild(navButton('‹', textOf(wrap, 'prevYears'), function () { moveYearFocus(focusYear - YEARS_PER_PAGE); }));
|
|
1938
2078
|
head.appendChild(title);
|
|
1939
|
-
head.appendChild(navButton('›',
|
|
2079
|
+
head.appendChild(navButton('›', textOf(wrap, 'nextYears'), function () { moveYearFocus(focusYear + YEARS_PER_PAGE); }));
|
|
1940
2080
|
panel.appendChild(head);
|
|
1941
2081
|
|
|
1942
2082
|
var grid = document.createElement('div');
|
|
@@ -2097,7 +2237,46 @@
|
|
|
2097
2237
|
|
|
2098
2238
|
var _pop = null;
|
|
2099
2239
|
function popCtl() { if (_pop === null && window.FreedayPopover) _pop = window.FreedayPopover.attach(panel, trigger); return _pop; }
|
|
2240
|
+
|
|
2241
|
+
/* The three field states the CSS has always styled (`:disabled`, `[aria-readonly="true"]`,
|
|
2242
|
+
`[aria-invalid="true"]`) and this enhancer never set, so only the stacks that re-implement
|
|
2243
|
+
the control natively had them. Read from the seed at init and settable afterwards, because
|
|
2244
|
+
a host that renders once — every Blazor wrapper does, `ShouldRender => false` — cannot
|
|
2245
|
+
express a later change any other way. */
|
|
2246
|
+
function flagOf(name) {
|
|
2247
|
+
var v = wrap.getAttribute('data-' + name);
|
|
2248
|
+
return v != null && v !== 'false';
|
|
2249
|
+
}
|
|
2250
|
+
/* Named `state*`, not `is*`: this file already has an `isDisabled(date)` deciding whether a
|
|
2251
|
+
DAY falls outside min/max, and shadowing it with a boolean made the day grid throw on every
|
|
2252
|
+
render — silently, since the panel still opened and only its cells went missing. */
|
|
2253
|
+
var stateDisabled = flagOf('disabled');
|
|
2254
|
+
var stateReadonly = flagOf('readonly');
|
|
2255
|
+
var stateInvalid = flagOf('invalid');
|
|
2256
|
+
/* `data-id` and `data-describedby`: the trigger this enhancer BUILDS is the element a form
|
|
2257
|
+
has to point its label and its error text at, and the raw path had no way to say so. */
|
|
2258
|
+
if (wrap.getAttribute('data-id')) trigger.id = wrap.getAttribute('data-id');
|
|
2259
|
+
if (wrap.getAttribute('data-describedby')) trigger.setAttribute('aria-describedby', wrap.getAttribute('data-describedby'));
|
|
2260
|
+
|
|
2261
|
+
function applyState() {
|
|
2262
|
+
trigger.disabled = stateDisabled;
|
|
2263
|
+
if (stateReadonly) trigger.setAttribute('aria-readonly', 'true');
|
|
2264
|
+
else trigger.removeAttribute('aria-readonly');
|
|
2265
|
+
if (stateInvalid) trigger.setAttribute('aria-invalid', 'true');
|
|
2266
|
+
else trigger.removeAttribute('aria-invalid');
|
|
2267
|
+
wrap.classList.toggle('fdy-datepicker--error', stateInvalid);
|
|
2268
|
+
if ((stateDisabled || stateReadonly) && !panel.hidden) close(false);
|
|
2269
|
+
}
|
|
2270
|
+
function setState(next) {
|
|
2271
|
+
if (!next) return;
|
|
2272
|
+
if (next.disabled != null) stateDisabled = !!next.disabled;
|
|
2273
|
+
if (next.readonly != null) stateReadonly = !!next.readonly;
|
|
2274
|
+
if (next.invalid != null) stateInvalid = !!next.invalid;
|
|
2275
|
+
applyState();
|
|
2276
|
+
}
|
|
2277
|
+
|
|
2100
2278
|
function open() {
|
|
2279
|
+
if (stateDisabled || stateReadonly) return;
|
|
2101
2280
|
if (!panel.hidden) return;
|
|
2102
2281
|
mode = 'days';
|
|
2103
2282
|
focusDate = selected || focusDate || new Date();
|
|
@@ -2127,8 +2306,11 @@
|
|
|
2127
2306
|
|
|
2128
2307
|
updateDisplay();
|
|
2129
2308
|
|
|
2309
|
+
applyState();
|
|
2310
|
+
|
|
2130
2311
|
var api = {
|
|
2131
2312
|
wrap: wrap,
|
|
2313
|
+
setState: setState,
|
|
2132
2314
|
getValue: function () { return selected ? toISO(selected) : ''; },
|
|
2133
2315
|
clear: function () { selected = null; updateDisplay(); if (!panel.hidden) render(); },
|
|
2134
2316
|
setMin: function (iso) { minDate = parseISO(iso); if (!panel.hidden) render(); },
|
|
@@ -2188,7 +2370,16 @@
|
|
|
2188
2370
|
initAll();
|
|
2189
2371
|
}
|
|
2190
2372
|
|
|
2191
|
-
window.FreedayDatepicker = {
|
|
2373
|
+
window.FreedayDatepicker = {
|
|
2374
|
+
init: initPicker,
|
|
2375
|
+
initAll: initAll,
|
|
2376
|
+
/* A host that rendered its seed once and cannot re-render it (Blazor) still has to be able to
|
|
2377
|
+
disable, lock or invalidate the field later. */
|
|
2378
|
+
setState: function (root, state) {
|
|
2379
|
+
var api = root && root._fdyDp ? root._fdyDp : null;
|
|
2380
|
+
if (api && api.setState) api.setState(state);
|
|
2381
|
+
}
|
|
2382
|
+
};
|
|
2192
2383
|
})();
|
|
2193
2384
|
|
|
2194
2385
|
/* Freeday, datetime picker composer (optional, zero-dependency).
|
|
@@ -2367,10 +2558,11 @@
|
|
|
2367
2558
|
badInput: 'type',
|
|
2368
2559
|
customError: 'mismatch'
|
|
2369
2560
|
};
|
|
2370
|
-
/* User-facing strings.
|
|
2371
|
-
* path, and overridable at three levels, narrowest first: `data-fdy-msg-<alias>` on
|
|
2372
|
-
* `data-fdy-msg` on the field, then `data-fdy-text-<alias>` on the FORM. The form
|
|
2373
|
-
* a host in another language needs: it sets nine messages once instead of on
|
|
2561
|
+
/* User-facing strings. English by default since 2.0.0, documented and deliberate for the raw
|
|
2562
|
+
* enhancer path, and overridable at three levels, narrowest first: `data-fdy-msg-<alias>` on
|
|
2563
|
+
* the field, `data-fdy-msg` on the field, then `data-fdy-text-<alias>` on the FORM. The form
|
|
2564
|
+
* level is what a host in another language needs: it sets nine messages once instead of on
|
|
2565
|
+
* every input.
|
|
2374
2566
|
* Keeping them in ONE table is also what lets a guard prove none is hard-coded further down. */
|
|
2375
2567
|
var TEXT = {
|
|
2376
2568
|
required: 'Required.',
|
|
@@ -2581,10 +2773,11 @@
|
|
|
2581
2773
|
}
|
|
2582
2774
|
|
|
2583
2775
|
|
|
2584
|
-
/* User-facing strings.
|
|
2585
|
-
* path, and every one overridable per element
|
|
2586
|
-
*
|
|
2587
|
-
* Keeping them in ONE table is also what lets a guard prove none is
|
|
2776
|
+
/* User-facing strings. English by default since 2.0.0, documented and deliberate for the raw
|
|
2777
|
+
* enhancer path, and every one overridable per element with `data-fdy-text-<key>`, so a host
|
|
2778
|
+
* that speaks another language (an Indonesian app on the raw path) supplies its own without
|
|
2779
|
+
* forking this file. Keeping them in ONE table is also what lets a guard prove none is
|
|
2780
|
+
* hard-coded further down. */
|
|
2588
2781
|
var TEXT = {
|
|
2589
2782
|
show: 'Show password',
|
|
2590
2783
|
hide: 'Hide password'
|
|
@@ -3210,7 +3403,24 @@
|
|
|
3210
3403
|
if (root && root._fdyCombo) root._fdyCombo.setValue(value);
|
|
3211
3404
|
}
|
|
3212
3405
|
|
|
3213
|
-
|
|
3406
|
+
/* Beside setValue for the same reason it exists: a host that renders its markup once (every
|
|
3407
|
+
Blazor wrapper, `ShouldRender => false`) cannot express a later state change any other way,
|
|
3408
|
+
and a parameter that silently stops working after the first render is worse than none. */
|
|
3409
|
+
function setState(root, state) {
|
|
3410
|
+
var button = root ? root.querySelector('.fdy-combo__button') : null;
|
|
3411
|
+
if (!button || !state) return;
|
|
3412
|
+
if (state.disabled != null) button.disabled = !!state.disabled;
|
|
3413
|
+
if (state.readonly != null) {
|
|
3414
|
+
if (state.readonly) button.setAttribute('aria-readonly', 'true');
|
|
3415
|
+
else button.removeAttribute('aria-readonly');
|
|
3416
|
+
}
|
|
3417
|
+
if (state.invalid != null) {
|
|
3418
|
+
if (state.invalid) button.setAttribute('aria-invalid', 'true');
|
|
3419
|
+
else button.removeAttribute('aria-invalid');
|
|
3420
|
+
}
|
|
3421
|
+
}
|
|
3422
|
+
|
|
3423
|
+
window.FreedayCombo = { init: initCombo, initAll: initAll, setValue: setValue, setState: setState };
|
|
3214
3424
|
})();
|
|
3215
3425
|
|
|
3216
3426
|
/* Freeday, slider value binding (optional, zero-dependency).
|
|
@@ -3272,10 +3482,11 @@
|
|
|
3272
3482
|
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>';
|
|
3273
3483
|
|
|
3274
3484
|
|
|
3275
|
-
/* User-facing strings.
|
|
3276
|
-
* path, and every one overridable per element
|
|
3277
|
-
*
|
|
3278
|
-
* Keeping them in ONE table is also what lets a guard prove none is
|
|
3485
|
+
/* User-facing strings. English by default since 2.0.0, documented and deliberate for the raw
|
|
3486
|
+
* enhancer path, and every one overridable per element with `data-fdy-text-<key>`, so a host
|
|
3487
|
+
* that speaks another language (an Indonesian app on the raw path) supplies its own without
|
|
3488
|
+
* forking this file. Keeping them in ONE table is also what lets a guard prove none is
|
|
3489
|
+
* hard-coded further down. */
|
|
3279
3490
|
var TEXT = {
|
|
3280
3491
|
done: 'Done',
|
|
3281
3492
|
next: 'Next'
|
|
@@ -3407,10 +3618,11 @@
|
|
|
3407
3618
|
}
|
|
3408
3619
|
|
|
3409
3620
|
|
|
3410
|
-
/* User-facing strings.
|
|
3411
|
-
* path, and every one overridable per element
|
|
3412
|
-
*
|
|
3413
|
-
* Keeping them in ONE table is also what lets a guard prove none is
|
|
3621
|
+
/* User-facing strings. English by default since 2.0.0, documented and deliberate for the raw
|
|
3622
|
+
* enhancer path, and every one overridable per element with `data-fdy-text-<key>`, so a host
|
|
3623
|
+
* that speaks another language (an Indonesian app on the raw path) supplies its own without
|
|
3624
|
+
* forking this file. Keeping them in ONE table is also what lets a guard prove none is
|
|
3625
|
+
* hard-coded further down. */
|
|
3414
3626
|
var TEXT = {
|
|
3415
3627
|
prev: 'Previous',
|
|
3416
3628
|
next: 'Next',
|
|
@@ -3419,6 +3631,8 @@
|
|
|
3419
3631
|
filterText: 'Contains text',
|
|
3420
3632
|
filterTextPlaceholder: 'Contains…',
|
|
3421
3633
|
filterEnum: 'Show values',
|
|
3634
|
+
filterMin: 'Min',
|
|
3635
|
+
filterMax: 'Max',
|
|
3422
3636
|
filterRange: 'Value range',
|
|
3423
3637
|
reset: 'Reset',
|
|
3424
3638
|
close: 'Close',
|
|
@@ -3691,8 +3905,8 @@
|
|
|
3691
3905
|
pop.appendChild(filterTitle(textOf(root, 'filterRange')));
|
|
3692
3906
|
var range = document.createElement('div');
|
|
3693
3907
|
range.className = 'fdy-filter__range';
|
|
3694
|
-
var minI = numberInput('
|
|
3695
|
-
var maxI = numberInput('
|
|
3908
|
+
var minI = numberInput(textOf(root, 'filterMin'), f.min);
|
|
3909
|
+
var maxI = numberInput(textOf(root, 'filterMax'), f.max);
|
|
3696
3910
|
var applyRange = function () {
|
|
3697
3911
|
f.min = minI.value !== '' ? parseNum(minI.value) : null;
|
|
3698
3912
|
f.max = maxI.value !== '' ? parseNum(maxI.value) : null;
|
|
@@ -3991,6 +4205,24 @@
|
|
|
3991
4205
|
(function () {
|
|
3992
4206
|
'use strict';
|
|
3993
4207
|
|
|
4208
|
+
/* User-facing strings, overridable per element with `data-fdy-text-<key>`. One entry, and it
|
|
4209
|
+
* still needed the table: it reached the DOM as an argument to a helper, which is how it sat
|
|
4210
|
+
* outside every string guard until 2.2.0. */
|
|
4211
|
+
var TEXT = {
|
|
4212
|
+
label: 'Choose a time'
|
|
4213
|
+
};
|
|
4214
|
+
function textAttr(root, key) {
|
|
4215
|
+
if (!root || !root.getAttribute) return null;
|
|
4216
|
+
var kebab = root.getAttribute('data-fdy-text-' + key.replace(/[A-Z]/g, function (c) { return '-' + c.toLowerCase(); }));
|
|
4217
|
+
return kebab != null && kebab !== '' ? kebab : root.getAttribute('data-fdy-text-' + key);
|
|
4218
|
+
}
|
|
4219
|
+
function textOf(root, key, vars) {
|
|
4220
|
+
var custom = textAttr(root, key);
|
|
4221
|
+
var s = custom != null && custom !== '' ? custom : TEXT[key];
|
|
4222
|
+
if (vars) for (var k in vars) if (Object.prototype.hasOwnProperty.call(vars, k)) s = s.split('{' + k + '}').join(vars[k]);
|
|
4223
|
+
return s;
|
|
4224
|
+
}
|
|
4225
|
+
|
|
3994
4226
|
var seq = 0;
|
|
3995
4227
|
var CLOCK = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="9"></circle><path d="M12 7v5l3 2"></path></svg>';
|
|
3996
4228
|
|
|
@@ -4005,7 +4237,7 @@
|
|
|
4005
4237
|
wrap.dataset.fdyTpReady = '1';
|
|
4006
4238
|
wrap.classList.add('fdy-timepicker');
|
|
4007
4239
|
|
|
4008
|
-
var label = wrap.getAttribute('data-label') ||
|
|
4240
|
+
var label = wrap.getAttribute('data-label') || textOf(wrap, 'label');
|
|
4009
4241
|
var placeholder = wrap.getAttribute('data-placeholder') || '--:--';
|
|
4010
4242
|
var step = Math.max(1, parseInt(wrap.getAttribute('data-step') || '30', 10));
|
|
4011
4243
|
var minM = valid(wrap.getAttribute('data-min')) ? toMin(wrap.getAttribute('data-min')) : 0;
|
|
@@ -4238,10 +4470,11 @@
|
|
|
4238
4470
|
}
|
|
4239
4471
|
|
|
4240
4472
|
|
|
4241
|
-
/* User-facing strings.
|
|
4242
|
-
* path, and every one overridable per element
|
|
4243
|
-
*
|
|
4244
|
-
* Keeping them in ONE table is also what lets a guard prove none is
|
|
4473
|
+
/* User-facing strings. English by default since 2.0.0, documented and deliberate for the raw
|
|
4474
|
+
* enhancer path, and every one overridable per element with `data-fdy-text-<key>`, so a host
|
|
4475
|
+
* that speaks another language (an Indonesian app on the raw path) supplies its own without
|
|
4476
|
+
* forking this file. Keeping them in ONE table is also what lets a guard prove none is
|
|
4477
|
+
* hard-coded further down. */
|
|
4245
4478
|
var TEXT = {
|
|
4246
4479
|
close: 'Close'
|
|
4247
4480
|
};
|
|
@@ -4472,10 +4705,11 @@
|
|
|
4472
4705
|
}
|
|
4473
4706
|
|
|
4474
4707
|
|
|
4475
|
-
/* User-facing strings.
|
|
4476
|
-
* path, and every one overridable per element
|
|
4477
|
-
*
|
|
4478
|
-
* Keeping them in ONE table is also what lets a guard prove none is
|
|
4708
|
+
/* User-facing strings. English by default since 2.0.0, documented and deliberate for the raw
|
|
4709
|
+
* enhancer path, and every one overridable per element with `data-fdy-text-<key>`, so a host
|
|
4710
|
+
* that speaks another language (an Indonesian app on the raw path) supplies its own without
|
|
4711
|
+
* forking this file. Keeping them in ONE table is also what lets a guard prove none is
|
|
4712
|
+
* hard-coded further down. */
|
|
4479
4713
|
var TEXT = {
|
|
4480
4714
|
remove: 'Remove {name}',
|
|
4481
4715
|
progress: 'Upload progress for {name}',
|