@cahyo-dimas/freeday 2.1.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 +67 -0
- package/COMPONENTS.md +34 -13
- 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/react/components/FdyDrawer.tsx +3 -1
- package/adapters/react/components/FdyModal.tsx +3 -1
- package/adapters/vue/components/FdyDrawer.vue +4 -1
- package/adapters/vue/components/FdyModal.vue +4 -1
- package/dist/freeday-autocomplete.js +17 -1
- package/dist/freeday-cascade.js +53 -3
- package/dist/freeday-chart.js +33 -3
- package/dist/freeday-datepicker.js +109 -17
- package/dist/freeday-select.js +18 -1
- package/dist/freeday-table.js +4 -2
- package/dist/freeday-timepicker.js +19 -1
- package/dist/freeday.js +253 -28
- package/docs/getting-started.md +1 -1
- 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).
|
|
@@ -603,6 +619,8 @@
|
|
|
603
619
|
* forking this file. Keeping them in ONE table is also what lets a guard prove none is
|
|
604
620
|
* hard-coded further down. */
|
|
605
621
|
var TEXT = {
|
|
622
|
+
label: 'Select',
|
|
623
|
+
placeholder: 'Select…',
|
|
606
624
|
back: 'Back one level',
|
|
607
625
|
submenu: '{label}, submenu'
|
|
608
626
|
};
|
|
@@ -632,8 +650,8 @@
|
|
|
632
650
|
var root = sourceUl ? parse(sourceUl) : [];
|
|
633
651
|
if (sourceUl) sourceUl.remove();
|
|
634
652
|
|
|
635
|
-
var label = wrap.getAttribute('data-label') || '
|
|
636
|
-
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');
|
|
637
655
|
var sep = wrap.getAttribute('data-separator') || ' / ';
|
|
638
656
|
|
|
639
657
|
var trigger = document.createElement('button');
|
|
@@ -748,7 +766,44 @@
|
|
|
748
766
|
|
|
749
767
|
var _pop = null;
|
|
750
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
|
+
|
|
751
805
|
function open() {
|
|
806
|
+
if (stateDisabled || stateReadonly) return;
|
|
752
807
|
if (!panel.hidden) return;
|
|
753
808
|
// Re-open at the selected leaf's level for quick re-selection.
|
|
754
809
|
var trail = selectedValue ? pathTo(root, selectedValue, []) : null;
|
|
@@ -803,8 +858,11 @@
|
|
|
803
858
|
valueSpan.classList.add('fdy-cascade__value--placeholder');
|
|
804
859
|
}
|
|
805
860
|
|
|
861
|
+
applyState();
|
|
862
|
+
|
|
806
863
|
var api = {
|
|
807
864
|
wrap: wrap,
|
|
865
|
+
setState: setState,
|
|
808
866
|
getValue: function () { return selectedValue; },
|
|
809
867
|
clear: function () { selectedValue = ''; valueSpan.textContent = placeholder; valueSpan.classList.add('fdy-cascade__value--placeholder'); }
|
|
810
868
|
};
|
|
@@ -825,7 +883,15 @@
|
|
|
825
883
|
initAll();
|
|
826
884
|
}
|
|
827
885
|
|
|
828
|
-
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
|
+
};
|
|
829
895
|
})();
|
|
830
896
|
|
|
831
897
|
/* Freeday, choose-from-list enhancer (optional, zero-dependency).
|
|
@@ -1104,6 +1170,28 @@
|
|
|
1104
1170
|
|
|
1105
1171
|
var NS = 'http://www.w3.org/2000/svg';
|
|
1106
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
|
+
|
|
1107
1195
|
// Categorical chart palette: 8 validated fixed-order slots (--chart-1..8). Series index i
|
|
1108
1196
|
// (0-based) -> slot i+1; series beyond the 8-slot cap reuse --chart-8 (never cycled).
|
|
1109
1197
|
function chartSlotVar(i) { return 'var(--chart-' + (i < 8 ? i + 1 : 8) + ')'; }
|
|
@@ -1312,7 +1400,7 @@
|
|
|
1312
1400
|
var li = document.createElement('li');
|
|
1313
1401
|
var sw = document.createElement('span'); sw.className = 'fdy-chart__swatch'; sw.style.background = colorFor(si);
|
|
1314
1402
|
li.appendChild(sw);
|
|
1315
|
-
li.appendChild(document.createTextNode(s.label || (
|
|
1403
|
+
li.appendChild(document.createTextNode(s.label || textOf(el, 'series', { n: si + 1 })));
|
|
1316
1404
|
legend.appendChild(li);
|
|
1317
1405
|
});
|
|
1318
1406
|
el.appendChild(legend);
|
|
@@ -1486,8 +1574,16 @@
|
|
|
1486
1574
|
ring.style.background = 'conic-gradient(' + stops.join(',') + ')';
|
|
1487
1575
|
var center = document.createElement('div'); center.className = 'fdy-donut__center';
|
|
1488
1576
|
var centerLabel = el.getAttribute('data-fdy-center');
|
|
1489
|
-
|
|
1490
|
-
|
|
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
|
+
}
|
|
1491
1587
|
ring.appendChild(center);
|
|
1492
1588
|
var svg = svgEl('svg');
|
|
1493
1589
|
svg.setAttribute('class', 'fdy-donut__hit');
|
|
@@ -1646,12 +1742,12 @@
|
|
|
1646
1742
|
* Locale comes from <html lang> (via Intl), month/weekday/value formatting is not hardcoded.
|
|
1647
1743
|
*
|
|
1648
1744
|
* Markup contract:
|
|
1649
|
-
* - Single: <div data-fdy-datepicker data-value="2026-07-21" data-label="
|
|
1650
|
-
* data-placeholder="
|
|
1651
|
-
* - Range: <div data-fdy-daterange role="group" aria-label="
|
|
1652
|
-
* <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>
|
|
1653
1749
|
* <span class="fdy-daterange__sep">–</span>
|
|
1654
|
-
* <div data-fdy-datepicker data-role="to" data-placeholder="
|
|
1750
|
+
* <div data-fdy-datepicker data-role="to" data-placeholder="To"></div>
|
|
1655
1751
|
* </div>
|
|
1656
1752
|
* The range links the two: the end can never precede the start (out-of-range days disable).
|
|
1657
1753
|
*
|
|
@@ -1667,6 +1763,47 @@
|
|
|
1667
1763
|
weekday names back automatically. The FALLBACK follows the kit's default language, or a
|
|
1668
1764
|
page without `lang` would read English labels around Indonesian month names. */
|
|
1669
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
|
+
}
|
|
1670
1807
|
var uidSeq = 0;
|
|
1671
1808
|
function uid(p) { uidSeq += 1; return p + '-' + uidSeq; }
|
|
1672
1809
|
function pad(n) { return n < 10 ? '0' + n : '' + n; }
|
|
@@ -1704,8 +1841,8 @@
|
|
|
1704
1841
|
wrap.dataset.fdyDpReady = '1';
|
|
1705
1842
|
wrap.classList.add('fdy-datepicker');
|
|
1706
1843
|
|
|
1707
|
-
var placeholder = wrap.getAttribute('data-placeholder') ||
|
|
1708
|
-
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');
|
|
1709
1846
|
var selected = parseISO(wrap.getAttribute('data-value'));
|
|
1710
1847
|
var minDate = parseISO(wrap.getAttribute('data-min'));
|
|
1711
1848
|
var maxDate = parseISO(wrap.getAttribute('data-max'));
|
|
@@ -1803,16 +1940,16 @@
|
|
|
1803
1940
|
panel.innerHTML = '';
|
|
1804
1941
|
var head = document.createElement('div');
|
|
1805
1942
|
head.className = 'fdy-cal__head';
|
|
1806
|
-
var title = titleButton(monthFmt.format(view), monthFmt.format(view)
|
|
1943
|
+
var title = titleButton(monthFmt.format(view), textOf(wrap, 'chooseMonth', { label: monthFmt.format(view) }), function () {
|
|
1807
1944
|
mode = 'months';
|
|
1808
1945
|
focusMonth = view.getMonth();
|
|
1809
1946
|
render();
|
|
1810
1947
|
focusMonthCell();
|
|
1811
1948
|
});
|
|
1812
1949
|
panel.setAttribute('aria-labelledby', title.id);
|
|
1813
|
-
head.appendChild(navButton('‹',
|
|
1950
|
+
head.appendChild(navButton('‹', textOf(wrap, 'prevMonth'), function () { view = addMonths(view, -1); render(); }));
|
|
1814
1951
|
head.appendChild(title);
|
|
1815
|
-
head.appendChild(navButton('›',
|
|
1952
|
+
head.appendChild(navButton('›', textOf(wrap, 'nextMonth'), function () { view = addMonths(view, 1); render(); }));
|
|
1816
1953
|
panel.appendChild(head);
|
|
1817
1954
|
|
|
1818
1955
|
var grid = document.createElement('div');
|
|
@@ -1869,16 +2006,16 @@
|
|
|
1869
2006
|
var year = view.getFullYear();
|
|
1870
2007
|
var head = document.createElement('div');
|
|
1871
2008
|
head.className = 'fdy-cal__head';
|
|
1872
|
-
var title = titleButton(String(year),
|
|
2009
|
+
var title = titleButton(String(year), textOf(wrap, 'chooseYear', { label: year }), function () {
|
|
1873
2010
|
mode = 'years';
|
|
1874
2011
|
focusYear = year;
|
|
1875
2012
|
render();
|
|
1876
2013
|
focusYearCell();
|
|
1877
2014
|
});
|
|
1878
2015
|
panel.setAttribute('aria-labelledby', title.id);
|
|
1879
|
-
head.appendChild(navButton('‹',
|
|
2016
|
+
head.appendChild(navButton('‹', textOf(wrap, 'prevYear'), function () { view = addMonths(view, -12); render(); focusMonthCell(); }));
|
|
1880
2017
|
head.appendChild(title);
|
|
1881
|
-
head.appendChild(navButton('›',
|
|
2018
|
+
head.appendChild(navButton('›', textOf(wrap, 'nextYear'), function () { view = addMonths(view, 12); render(); focusMonthCell(); }));
|
|
1882
2019
|
panel.appendChild(head);
|
|
1883
2020
|
|
|
1884
2021
|
var grid = document.createElement('div');
|
|
@@ -1930,16 +2067,16 @@
|
|
|
1930
2067
|
var end = start + YEARS_PER_PAGE - 1;
|
|
1931
2068
|
var head = document.createElement('div');
|
|
1932
2069
|
head.className = 'fdy-cal__head';
|
|
1933
|
-
var title = titleButton(start + ' – ' + end,
|
|
2070
|
+
var title = titleButton(start + ' – ' + end, textOf(wrap, 'backToMonths', { start: start, end: end }), function () {
|
|
1934
2071
|
mode = 'months';
|
|
1935
2072
|
focusMonth = view.getMonth();
|
|
1936
2073
|
render();
|
|
1937
2074
|
focusMonthCell();
|
|
1938
2075
|
});
|
|
1939
2076
|
panel.setAttribute('aria-labelledby', title.id);
|
|
1940
|
-
head.appendChild(navButton('‹',
|
|
2077
|
+
head.appendChild(navButton('‹', textOf(wrap, 'prevYears'), function () { moveYearFocus(focusYear - YEARS_PER_PAGE); }));
|
|
1941
2078
|
head.appendChild(title);
|
|
1942
|
-
head.appendChild(navButton('›',
|
|
2079
|
+
head.appendChild(navButton('›', textOf(wrap, 'nextYears'), function () { moveYearFocus(focusYear + YEARS_PER_PAGE); }));
|
|
1943
2080
|
panel.appendChild(head);
|
|
1944
2081
|
|
|
1945
2082
|
var grid = document.createElement('div');
|
|
@@ -2100,7 +2237,46 @@
|
|
|
2100
2237
|
|
|
2101
2238
|
var _pop = null;
|
|
2102
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
|
+
|
|
2103
2278
|
function open() {
|
|
2279
|
+
if (stateDisabled || stateReadonly) return;
|
|
2104
2280
|
if (!panel.hidden) return;
|
|
2105
2281
|
mode = 'days';
|
|
2106
2282
|
focusDate = selected || focusDate || new Date();
|
|
@@ -2130,8 +2306,11 @@
|
|
|
2130
2306
|
|
|
2131
2307
|
updateDisplay();
|
|
2132
2308
|
|
|
2309
|
+
applyState();
|
|
2310
|
+
|
|
2133
2311
|
var api = {
|
|
2134
2312
|
wrap: wrap,
|
|
2313
|
+
setState: setState,
|
|
2135
2314
|
getValue: function () { return selected ? toISO(selected) : ''; },
|
|
2136
2315
|
clear: function () { selected = null; updateDisplay(); if (!panel.hidden) render(); },
|
|
2137
2316
|
setMin: function (iso) { minDate = parseISO(iso); if (!panel.hidden) render(); },
|
|
@@ -2191,7 +2370,16 @@
|
|
|
2191
2370
|
initAll();
|
|
2192
2371
|
}
|
|
2193
2372
|
|
|
2194
|
-
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
|
+
};
|
|
2195
2383
|
})();
|
|
2196
2384
|
|
|
2197
2385
|
/* Freeday, datetime picker composer (optional, zero-dependency).
|
|
@@ -3215,7 +3403,24 @@
|
|
|
3215
3403
|
if (root && root._fdyCombo) root._fdyCombo.setValue(value);
|
|
3216
3404
|
}
|
|
3217
3405
|
|
|
3218
|
-
|
|
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 };
|
|
3219
3424
|
})();
|
|
3220
3425
|
|
|
3221
3426
|
/* Freeday, slider value binding (optional, zero-dependency).
|
|
@@ -3426,6 +3631,8 @@
|
|
|
3426
3631
|
filterText: 'Contains text',
|
|
3427
3632
|
filterTextPlaceholder: 'Contains…',
|
|
3428
3633
|
filterEnum: 'Show values',
|
|
3634
|
+
filterMin: 'Min',
|
|
3635
|
+
filterMax: 'Max',
|
|
3429
3636
|
filterRange: 'Value range',
|
|
3430
3637
|
reset: 'Reset',
|
|
3431
3638
|
close: 'Close',
|
|
@@ -3698,8 +3905,8 @@
|
|
|
3698
3905
|
pop.appendChild(filterTitle(textOf(root, 'filterRange')));
|
|
3699
3906
|
var range = document.createElement('div');
|
|
3700
3907
|
range.className = 'fdy-filter__range';
|
|
3701
|
-
var minI = numberInput('
|
|
3702
|
-
var maxI = numberInput('
|
|
3908
|
+
var minI = numberInput(textOf(root, 'filterMin'), f.min);
|
|
3909
|
+
var maxI = numberInput(textOf(root, 'filterMax'), f.max);
|
|
3703
3910
|
var applyRange = function () {
|
|
3704
3911
|
f.min = minI.value !== '' ? parseNum(minI.value) : null;
|
|
3705
3912
|
f.max = maxI.value !== '' ? parseNum(maxI.value) : null;
|
|
@@ -3998,6 +4205,24 @@
|
|
|
3998
4205
|
(function () {
|
|
3999
4206
|
'use strict';
|
|
4000
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
|
+
|
|
4001
4226
|
var seq = 0;
|
|
4002
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>';
|
|
4003
4228
|
|
|
@@ -4012,7 +4237,7 @@
|
|
|
4012
4237
|
wrap.dataset.fdyTpReady = '1';
|
|
4013
4238
|
wrap.classList.add('fdy-timepicker');
|
|
4014
4239
|
|
|
4015
|
-
var label = wrap.getAttribute('data-label') ||
|
|
4240
|
+
var label = wrap.getAttribute('data-label') || textOf(wrap, 'label');
|
|
4016
4241
|
var placeholder = wrap.getAttribute('data-placeholder') || '--:--';
|
|
4017
4242
|
var step = Math.max(1, parseInt(wrap.getAttribute('data-step') || '30', 10));
|
|
4018
4243
|
var minM = valid(wrap.getAttribute('data-min')) ? toMin(wrap.getAttribute('data-min')) : 0;
|
package/docs/getting-started.md
CHANGED
|
@@ -191,7 +191,7 @@ live docs also have a copy button per component.
|
|
|
191
191
|
```bash
|
|
192
192
|
npm i @cahyo-dimas/freeday
|
|
193
193
|
```
|
|
194
|
-
Lands in `package.json` as `"@cahyo-dimas/freeday": "^2.
|
|
194
|
+
Lands in `package.json` as `"@cahyo-dimas/freeday": "^2.2.0"` (public npm package). `dist/` is
|
|
195
195
|
committed and published → no build step; `npm ci` runs without auth.
|
|
196
196
|
|
|
197
197
|
### 2. Import the CSS + enhancers **once** in your entry (`src/main.ts`)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cahyo-dimas/freeday",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "Freeday: token-driven, framework-agnostic UI KIT (design source-of-truth).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
"scripts": {
|
|
93
93
|
"build": "node tokens/build.mjs",
|
|
94
94
|
"test": "node --test",
|
|
95
|
-
"test:browser": "node --test --test-concurrency=3 browser/vanilla.mjs browser/adapter.mjs browser/layout.mjs browser/theme.mjs browser/state.mjs browser/root-init.mjs browser/upload-states.mjs browser/number.mjs browser/card-stretch.mjs browser/text-override.mjs browser/control-heights.mjs browser/cfl-multi.mjs browser/crowding.mjs browser/over-dialog.mjs browser/chart-scale.mjs browser/overlay-stack.mjs browser/chart-a11y.mjs browser/app-shell.mjs",
|
|
95
|
+
"test:browser": "node --test --test-concurrency=3 browser/vanilla.mjs browser/adapter.mjs browser/layout.mjs browser/theme.mjs browser/state.mjs browser/root-init.mjs browser/upload-states.mjs browser/number.mjs browser/card-stretch.mjs browser/text-override.mjs browser/control-heights.mjs browser/cfl-multi.mjs browser/crowding.mjs browser/over-dialog.mjs browser/chart-scale.mjs browser/overlay-stack.mjs browser/chart-a11y.mjs browser/app-shell.mjs browser/picker-states.mjs",
|
|
96
96
|
"prepack": "node tokens/build.mjs",
|
|
97
97
|
"version": "node tokens/build.mjs && git add dist",
|
|
98
98
|
"typecheck:react": "tsc -p adapters/react/tsconfig.json --noEmit",
|