@konce-pt/react 0.11.1 → 0.12.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/README.md +8 -2
- package/dist/accordion/accordion.js +11 -10
- package/dist/alert/alert.js +1 -0
- package/dist/alert/llms.txt +1 -0
- package/dist/app-shell/app-shell.js +12 -5
- package/dist/app-shell/llms.txt +5 -4
- package/dist/auth/auth.js +17 -8
- package/dist/auth/llms.txt +3 -2
- package/dist/autocomplete/autocomplete.d.ts +1 -1
- package/dist/autocomplete/autocomplete.js +12 -4
- package/dist/autocomplete/llms.txt +3 -0
- package/dist/avatar/avatar.js +1 -0
- package/dist/avatar-group/avatar-group.js +1 -0
- package/dist/backdrop/backdrop.d.ts +0 -19
- package/dist/backdrop/backdrop.js +18 -13
- package/dist/bottom-sheet/bottom-sheet.d.ts +6 -1
- package/dist/bottom-sheet/bottom-sheet.js +3 -2
- package/dist/bottom-sheet/llms.txt +3 -0
- package/dist/breadcrumb/breadcrumb.d.ts +7 -1
- package/dist/breadcrumb/breadcrumb.js +10 -2
- package/dist/breadcrumb/llms.txt +8 -0
- package/dist/button/button.js +2 -1
- package/dist/button-group/button-group.d.ts +15 -2
- package/dist/button-group/button-group.js +31 -4
- package/dist/button-group/llms.txt +9 -6
- package/dist/card/card.js +4 -3
- package/dist/carousel/carousel.js +4 -1
- package/dist/charts/chart-legend.js +1 -0
- package/dist/charts/chart.d.ts +3 -2
- package/dist/charts/chart.js +12 -4
- package/dist/charts/llms.txt +6 -3
- package/dist/chat/chat.js +39 -9
- package/dist/chat/llms.txt +9 -0
- package/dist/chat-composer/chat-composer.d.ts +5 -2
- package/dist/chat-composer/chat-composer.js +10 -1
- package/dist/chat-dock/chat-dock.js +1 -0
- package/dist/chat-message/chat-message.js +2 -1
- package/dist/chat-typing/chat-typing.js +1 -0
- package/dist/checkbox/checkbox.d.ts +1 -1
- package/dist/checkbox/checkbox.js +9 -2
- package/dist/checkbox/llms.txt +3 -0
- package/dist/chip/chip.js +1 -0
- package/dist/chips-input/chips-input.d.ts +3 -1
- package/dist/chips-input/chips-input.js +14 -5
- package/dist/chips-input/llms.txt +3 -0
- package/dist/clock/clock.d.ts +9 -4
- package/dist/clock/clock.js +35 -5
- package/dist/clock/llms.txt +6 -2
- package/dist/color-picker/color-picker.d.ts +5 -1
- package/dist/color-picker/color-picker.js +36 -17
- package/dist/color-picker/llms.txt +3 -0
- package/dist/confirm/confirm-container.js +1 -0
- package/dist/confirm/confirm-store.js +6 -0
- package/dist/confirm/llms.txt +2 -1
- package/dist/context-menu/context-menu.js +35 -12
- package/dist/context-menu/llms.txt +3 -0
- package/dist/data-table/data-table.d.ts +12 -1
- package/dist/data-table/data-table.js +113 -51
- package/dist/data-table/export-csv.d.ts +2 -2
- package/dist/data-table/export-csv.js +7 -9
- package/dist/data-table/llms.txt +19 -4
- package/dist/data-view/data-view.js +1 -0
- package/dist/date-range/date-range.d.ts +16 -4
- package/dist/date-range/date-range.js +69 -46
- package/dist/date-range/llms.txt +17 -3
- package/dist/datepicker/datepicker.d.ts +17 -3
- package/dist/datepicker/datepicker.js +86 -54
- package/dist/datepicker/llms.txt +17 -3
- package/dist/dialog/dialog.js +29 -8
- package/dist/dialog/llms.txt +3 -0
- package/dist/divider/divider.d.ts +7 -1
- package/dist/divider/divider.js +3 -2
- package/dist/divider/llms.txt +1 -0
- package/dist/drawer/drawer.d.ts +6 -1
- package/dist/drawer/drawer.js +3 -2
- package/dist/drawer/llms.txt +3 -0
- package/dist/fieldset/fieldset.js +4 -1
- package/dist/file-upload/file-upload.d.ts +7 -1
- package/dist/file-upload/file-upload.js +18 -7
- package/dist/file-upload/llms.txt +6 -3
- package/dist/form-field/form-field.js +1 -0
- package/dist/galleria/galleria.js +4 -1
- package/dist/i18n/i18n-context.d.ts +3 -3
- package/dist/i18n/i18n-context.js +25 -3
- package/dist/icon/icon.js +4 -1
- package/dist/image/image.js +4 -4
- package/dist/input/input.js +1 -0
- package/dist/input-mask/input-mask.d.ts +7 -2
- package/dist/input-mask/input-mask.js +15 -41
- package/dist/input-mask/llms.txt +3 -0
- package/dist/input-number/input-number.d.ts +3 -1
- package/dist/input-number/input-number.js +15 -11
- package/dist/input-number/llms.txt +5 -2
- package/dist/input-otp/input-otp.d.ts +16 -3
- package/dist/input-otp/input-otp.js +43 -19
- package/dist/input-otp/llms.txt +11 -0
- package/dist/internal/mask.d.ts +14 -0
- package/dist/internal/mask.js +62 -0
- package/dist/internal/number-group.d.ts +22 -0
- package/dist/internal/number-group.js +53 -0
- package/dist/internal/option-key.d.ts +6 -0
- package/dist/internal/option-key.js +8 -0
- package/dist/internal/overlay.js +37 -7
- package/dist/internal/password-strength.d.ts +6 -0
- package/dist/internal/password-strength.js +74 -0
- package/dist/internal/scroll-lock.d.ts +6 -0
- package/dist/internal/scroll-lock.js +35 -0
- package/dist/internal/slot-children.d.ts +13 -0
- package/dist/internal/slot-children.js +31 -0
- package/dist/internal/step.d.ts +12 -0
- package/dist/internal/step.js +31 -0
- package/dist/internal/tween.js +2 -3
- package/dist/internal/use-controllable-state.d.ts +5 -0
- package/dist/internal/use-controllable-state.js +25 -4
- package/dist/internal/use-modal-focus.d.ts +1 -0
- package/dist/internal/use-modal-focus.js +12 -1
- package/dist/knob/knob.d.ts +5 -2
- package/dist/knob/knob.js +15 -14
- package/dist/knob/llms.txt +4 -2
- package/dist/listbox/listbox.d.ts +3 -1
- package/dist/listbox/listbox.js +30 -10
- package/dist/listbox/llms.txt +3 -0
- package/dist/map/llms.txt +5 -1
- package/dist/map/map-create-dialog.js +30 -22
- package/dist/map/map-hours-field.js +1 -0
- package/dist/map/map-image-field.js +2 -1
- package/dist/map/map-legend.js +1 -0
- package/dist/map/map-poi-card.js +2 -1
- package/dist/map/map-search.js +54 -30
- package/dist/map/map.d.ts +6 -1
- package/dist/map/map.js +62 -34
- package/dist/megamenu/llms.txt +11 -0
- package/dist/megamenu/megamenu.d.ts +7 -1
- package/dist/megamenu/megamenu.js +15 -6
- package/dist/menu/menu-rows.js +1 -0
- package/dist/menu/menu.js +10 -3
- package/dist/menubar/llms.txt +11 -0
- package/dist/menubar/menubar.d.ts +7 -1
- package/dist/menubar/menubar.js +15 -3
- package/dist/meter-group/meter-group.js +1 -0
- package/dist/order-list/llms.txt +3 -0
- package/dist/order-list/order-list.js +21 -6
- package/dist/paginator/paginator.js +7 -4
- package/dist/panel/panel.js +5 -3
- package/dist/password/llms.txt +6 -2
- package/dist/password/password.js +4 -14
- package/dist/peek/llms.txt +1 -1
- package/dist/peek/peek.js +37 -15
- package/dist/pick-list/llms.txt +3 -0
- package/dist/pick-list/pick-list.js +19 -3
- package/dist/popover/llms.txt +3 -0
- package/dist/popover/popover.d.ts +3 -1
- package/dist/popover/popover.js +10 -3
- package/dist/progress/progress.js +1 -0
- package/dist/public-api.d.ts +1 -1
- package/dist/radio-group/llms.txt +3 -0
- package/dist/radio-group/radio-group.d.ts +3 -1
- package/dist/radio-group/radio-group.js +13 -2
- package/dist/rating/rating.js +1 -0
- package/dist/rich-text/llms.txt +14 -0
- package/dist/rich-text/rich-text.d.ts +3 -9
- package/dist/rich-text/rich-text.js +241 -96
- package/dist/rich-text/sanitize.d.ts +9 -0
- package/dist/rich-text/sanitize.js +77 -29
- package/dist/roadmap/llms.txt +9 -3
- package/dist/roadmap/roadmap.js +88 -11
- package/dist/scroll-top/scroll-top.js +1 -0
- package/dist/select/llms.txt +3 -0
- package/dist/select/select.d.ts +3 -1
- package/dist/select/select.js +46 -32
- package/dist/shared/types.d.ts +21 -0
- package/dist/shared/types.js +9 -1
- package/dist/sidenav/sidenav.d.ts +1 -2
- package/dist/sidenav/sidenav.js +4 -3
- package/dist/slider/llms.txt +3 -0
- package/dist/slider/slider.d.ts +3 -1
- package/dist/slider/slider.js +15 -4
- package/dist/speed-dial/speed-dial.js +2 -1
- package/dist/spinner/spinner.js +1 -0
- package/dist/split-button/split-button.js +10 -3
- package/dist/splitter/splitter.js +23 -8
- package/dist/stepper/llms.txt +3 -0
- package/dist/stepper/stepper.js +9 -5
- package/dist/switch/llms.txt +3 -0
- package/dist/switch/switch.d.ts +1 -1
- package/dist/switch/switch.js +9 -2
- package/dist/switch-group/llms.txt +3 -0
- package/dist/switch-group/switch-group.d.ts +3 -1
- package/dist/switch-group/switch-group.js +5 -3
- package/dist/tabs/llms.txt +3 -0
- package/dist/tabs/tabs.js +30 -14
- package/dist/textarea/textarea.js +1 -0
- package/dist/toast/llms.txt +3 -0
- package/dist/toast/toast-container.js +1 -0
- package/dist/toast/toast-store.d.ts +5 -1
- package/dist/toast/toast-store.js +1 -1
- package/dist/tooltip/llms.txt +5 -3
- package/dist/tooltip/tooltip.d.ts +3 -2
- package/dist/tooltip/tooltip.js +148 -55
- package/dist/tree/llms.txt +1 -0
- package/dist/tree/tree.d.ts +6 -1
- package/dist/tree/tree.js +14 -7
- package/package.json +15 -14
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
'use client';
|
|
1
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { useEffect, useRef, useState } from 'react';
|
|
3
|
-
import { buildRangeGrid, clampVisibleMonths, datePart,
|
|
3
|
+
import { useEffect, useEffectEvent, useRef, useState, } from 'react';
|
|
4
|
+
import { buildRangeGrid, clampVisibleMonths, datePart, dayPeriodLabels, displayDate, formatTime, kptFirstDayOfWeek, orderWeekdays, initialFocusDate, moveFocusDate, pad, parseDate, parseTime, pickRangeDay, timePart, toIso, } from '@konce-pt/datetime';
|
|
4
5
|
import { monthNames, weekdayNames } from '@konce-pt/i18n';
|
|
5
6
|
import { cn } from "../internal/cn.js";
|
|
6
7
|
import { useControllableState } from "../internal/use-controllable-state.js";
|
|
@@ -21,18 +22,20 @@ const EMPTY_RANGE = { start: '', end: '' };
|
|
|
21
22
|
* Klawiatura jak w `KptDatepicker`: `Enter`/`Spacja`/`↓` otwierają panel z fokusem na początku
|
|
22
23
|
* zakresu (albo dziś), strzałki, `Home`/`End` i `PageUp`/`PageDown` (z `Shift` — rok) chodzą po
|
|
23
24
|
* dniach przez wszystkie panele, `Tab` krąży w panelu, `Escape` i wybór końca oddają fokus polu.
|
|
24
|
-
* Krańce i środek zakresu są w nazwie dnia („…, początek zakresu”), a wybór
|
|
25
|
+
* Krańce i środek zakresu są w nazwie dnia („…, początek zakresu”), a wybór i przewinięcie miesiąca
|
|
26
|
+
* ogłasza region `polite`. Pole to `role="combobox"` z `aria-haspopup="dialog"`, więc niesie
|
|
27
|
+
* `aria-required`, `aria-readonly` i `aria-invalid`. Tydzień zaczyna się wg aktywnego języka.
|
|
25
28
|
*
|
|
26
29
|
* @example
|
|
27
30
|
* <KptDateRange value={period} onValueChange={setPeriod} visibleMonths={2} />
|
|
28
31
|
* <KptDateRange mode="datetime" hourFormat={12} value={span} onValueChange={setSpan} />
|
|
29
32
|
*/
|
|
30
|
-
export function KptDateRange({ value, defaultValue = EMPTY_RANGE, onValueChange, mode = 'date', visibleMonths = 1, hourFormat = 24, minuteStep = 1, placeholder, displayFormat = 'pl', minDate = null, maxDate = null, ariaLabel = null, id, disabled = false, invalid = false, touched, onTouch, locale, dictionary, className, ...rest }) {
|
|
33
|
+
export function KptDateRange({ value, defaultValue = EMPTY_RANGE, onValueChange, mode = 'date', visibleMonths = 1, hourFormat = 24, minuteStep = 1, placeholder, displayFormat = 'pl', firstDayOfWeek = null, open, onOpenChange, minDate = null, maxDate = null, ariaLabel = null, id, disabled = false, readOnly = false, required = false, invalid = false, touched, onTouch, locale, dictionary, className, ...rest }) {
|
|
31
34
|
const { t, locale: activeLocale } = useKptMessages({ locale, dictionary });
|
|
32
35
|
/** `div` z rolą nie dostaje nazwy z `label[for]` — stąd `aria-labelledby` na etykietę wrappera. */
|
|
33
36
|
const field = useKptFieldA11y(id, 'kpt-date-range');
|
|
34
37
|
const [current, setCurrent] = useControllableState({ value, defaultValue, onChange: onValueChange });
|
|
35
|
-
const [isOpen, setOpen] =
|
|
38
|
+
const [isOpen, setOpen] = useControllableState({ value: open, defaultValue: false, onChange: onOpenChange });
|
|
36
39
|
const [anchor, setAnchor] = useState(null);
|
|
37
40
|
const triggerRef = useRef(null);
|
|
38
41
|
const panelRef = useRef(null);
|
|
@@ -41,10 +44,14 @@ export function KptDateRange({ value, defaultValue = EMPTY_RANGE, onValueChange,
|
|
|
41
44
|
const [focusIso, setFocusIso] = useState('');
|
|
42
45
|
/** Komunikat regionu `polite` po wyborze początku i całego zakresu. */
|
|
43
46
|
const [announcement, setAnnouncement] = useState('');
|
|
44
|
-
/** Prośba o przeniesienie fokusu po najbliższym renderze (otwarcie, ruch klawiszem). */
|
|
45
|
-
const
|
|
46
|
-
const
|
|
47
|
+
/** Prośba o przeniesienie fokusu po najbliższym renderze (otwarcie, ruch klawiszem) — licznik w stanie. */
|
|
48
|
+
const [focusRequest, setFocusRequest] = useState(0);
|
|
49
|
+
const requestFocus = () => setFocusRequest((n) => n + 1);
|
|
50
|
+
/** „Dziś” liczone przy każdym otwarciu — panel otwarty po północy nie pokazuje wczorajszego dnia. */
|
|
51
|
+
const [today, setToday] = useState(() => new Date());
|
|
47
52
|
const todayIso = toIso(today);
|
|
53
|
+
const firstDay = firstDayOfWeek ?? kptFirstDayOfWeek(activeLocale);
|
|
54
|
+
const periods = dayPeriodLabels(activeLocale);
|
|
48
55
|
const showInvalid = invalid && (touched ?? true);
|
|
49
56
|
const [page, setPage] = useState(() => ({ y: today.getFullYear(), m: today.getMonth() }));
|
|
50
57
|
const [startTime, setStartTime] = useState('');
|
|
@@ -54,7 +61,7 @@ export function KptDateRange({ value, defaultValue = EMPTY_RANGE, onValueChange,
|
|
|
54
61
|
const [startDate, setStartDate] = useState('');
|
|
55
62
|
const [endDate, setEndDate] = useState('');
|
|
56
63
|
const months = monthNames(activeLocale);
|
|
57
|
-
const weekdays = weekdayNames(activeLocale);
|
|
64
|
+
const weekdays = orderWeekdays(weekdayNames(activeLocale), firstDay);
|
|
58
65
|
const panelCount = mode === 'time' ? 1 : clampVisibleMonths(visibleMonths);
|
|
59
66
|
const multi = panelCount > 1;
|
|
60
67
|
const dayFormat = new Intl.DateTimeFormat(activeLocale, { weekday: 'long', day: 'numeric', month: 'long', year: 'numeric' });
|
|
@@ -75,7 +82,7 @@ export function KptDateRange({ value, defaultValue = EMPTY_RANGE, onValueChange,
|
|
|
75
82
|
};
|
|
76
83
|
const fmtTime = (hhmm) => {
|
|
77
84
|
const parsed = parseTime(hhmm);
|
|
78
|
-
return parsed ? formatTime(parsed.h, parsed.m, hourFormat) : '';
|
|
85
|
+
return parsed ? formatTime(parsed.h, parsed.m, hourFormat, periods) : '';
|
|
79
86
|
};
|
|
80
87
|
const display = (() => {
|
|
81
88
|
const { start, end } = current;
|
|
@@ -86,38 +93,47 @@ export function KptDateRange({ value, defaultValue = EMPTY_RANGE, onValueChange,
|
|
|
86
93
|
}
|
|
87
94
|
if (mode === 'datetime') {
|
|
88
95
|
const one = (v) => {
|
|
89
|
-
const date =
|
|
96
|
+
const date = displayDate(datePart(v), displayFormat, activeLocale);
|
|
90
97
|
const time = fmtTime(timePart(v));
|
|
91
98
|
return time ? `${date} ${time}` : date;
|
|
92
99
|
};
|
|
93
100
|
return end ? `${one(start)} – ${one(end)}` : `${one(start)} – …`;
|
|
94
101
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
: `${formatDate(start, displayFormat)} – …`;
|
|
102
|
+
const fmt = (iso) => displayDate(iso, displayFormat, activeLocale);
|
|
103
|
+
return end ? `${fmt(start)} – ${fmt(end)}` : `${fmt(start)} – …`;
|
|
98
104
|
})();
|
|
105
|
+
// Panel otwiera też `open` z zewnątrz — stan początkowy liczymy przy każdym przejściu w stan
|
|
106
|
+
// otwarty, wzorcem „poprzednia wartość w stanie” (bez efektu i klatki ze starym widokiem).
|
|
107
|
+
const [wasOpen, setWasOpen] = useState(isOpen);
|
|
108
|
+
if (wasOpen !== isOpen) {
|
|
109
|
+
setWasOpen(isOpen);
|
|
110
|
+
if (isOpen)
|
|
111
|
+
prepareOpen();
|
|
112
|
+
}
|
|
113
|
+
function prepareOpen() {
|
|
114
|
+
const now = new Date();
|
|
115
|
+
setToday(now);
|
|
116
|
+
const { start, end } = current;
|
|
117
|
+
const focus = initialFocusDate(datePart(start), toIso(now), minDate, maxDate);
|
|
118
|
+
setFocusIso(focus);
|
|
119
|
+
setAnnouncement('');
|
|
120
|
+
setPage({ y: +focus.slice(0, 4), m: +focus.slice(5, 7) - 1 });
|
|
121
|
+
requestFocus();
|
|
122
|
+
if (mode === 'datetime') {
|
|
123
|
+
setStartDate(datePart(start));
|
|
124
|
+
setEndDate(datePart(end));
|
|
125
|
+
setStartTime(timePart(start) || '00:00');
|
|
126
|
+
setEndTime(timePart(end) || '00:00');
|
|
127
|
+
}
|
|
128
|
+
else if (mode === 'time') {
|
|
129
|
+
setStartTime(/^\d{2}:\d{2}$/.test(start) ? start : '00:00');
|
|
130
|
+
setEndTime(/^\d{2}:\d{2}$/.test(end) ? end : '00:00');
|
|
131
|
+
}
|
|
132
|
+
setTimeEnd('start');
|
|
133
|
+
}
|
|
99
134
|
const toggleOpen = () => {
|
|
100
|
-
if (disabled)
|
|
135
|
+
if (disabled || readOnly)
|
|
101
136
|
return;
|
|
102
|
-
if (!isOpen) {
|
|
103
|
-
const { start, end } = current;
|
|
104
|
-
const focus = initialFocusDate(datePart(start), todayIso, minDate, maxDate);
|
|
105
|
-
setFocusIso(focus);
|
|
106
|
-
setAnnouncement('');
|
|
107
|
-
setPage({ y: +focus.slice(0, 4), m: +focus.slice(5, 7) - 1 });
|
|
108
|
-
wantFocus.current = true;
|
|
109
|
-
if (mode === 'datetime') {
|
|
110
|
-
setStartDate(datePart(start));
|
|
111
|
-
setEndDate(datePart(end));
|
|
112
|
-
setStartTime(timePart(start) || '00:00');
|
|
113
|
-
setEndTime(timePart(end) || '00:00');
|
|
114
|
-
}
|
|
115
|
-
else if (mode === 'time') {
|
|
116
|
-
setStartTime(/^\d{2}:\d{2}$/.test(start) ? start : '00:00');
|
|
117
|
-
setEndTime(/^\d{2}:\d{2}$/.test(end) ? end : '00:00');
|
|
118
|
-
}
|
|
119
|
-
setTimeEnd('start');
|
|
120
|
-
}
|
|
121
137
|
setOpen(!isOpen);
|
|
122
138
|
};
|
|
123
139
|
const close = () => {
|
|
@@ -132,15 +148,13 @@ export function KptDateRange({ value, defaultValue = EMPTY_RANGE, onValueChange,
|
|
|
132
148
|
triggerRef.current?.focus();
|
|
133
149
|
};
|
|
134
150
|
// Fokus po renderze. Panel w nakładce jest niewidoczny do pierwszego pomiaru — stąd rAF.
|
|
151
|
+
const focusActive = useEffectEvent(() => focusCalendarActive(panelRef.current, focusIso));
|
|
135
152
|
useEffect(() => {
|
|
136
|
-
if (!isOpen ||
|
|
153
|
+
if (!isOpen || focusRequest === 0)
|
|
137
154
|
return;
|
|
138
|
-
const frame = requestAnimationFrame(() =>
|
|
139
|
-
wantFocus.current = false;
|
|
140
|
-
focusCalendarActive(panelRef.current, focusIso);
|
|
141
|
-
});
|
|
155
|
+
const frame = requestAnimationFrame(() => focusActive());
|
|
142
156
|
return () => cancelAnimationFrame(frame);
|
|
143
|
-
});
|
|
157
|
+
}, [focusRequest, isOpen]);
|
|
144
158
|
/** Przesuwa okno paneli tak, żeby miesiąc dnia `iso` był widoczny — z najmniejszym skokiem. */
|
|
145
159
|
const revealMonth = (iso) => {
|
|
146
160
|
const target = +iso.slice(0, 4) * 12 + (+iso.slice(5, 7) - 1);
|
|
@@ -154,19 +168,24 @@ export function KptDateRange({ value, defaultValue = EMPTY_RANGE, onValueChange,
|
|
|
154
168
|
};
|
|
155
169
|
const onDayKeyDown = (event) => {
|
|
156
170
|
const rtl = !!panelRef.current && getComputedStyle(panelRef.current).direction === 'rtl';
|
|
157
|
-
const next = moveFocusDate(focusIso, event.key, { shift: event.shiftKey, rtl, minDate, maxDate });
|
|
171
|
+
const next = moveFocusDate(focusIso, event.key, { shift: event.shiftKey, rtl, minDate, maxDate, firstDay });
|
|
158
172
|
if (next === null)
|
|
159
173
|
return;
|
|
160
174
|
event.preventDefault();
|
|
161
175
|
setFocusIso(next);
|
|
162
176
|
revealMonth(next);
|
|
163
|
-
|
|
177
|
+
requestFocus();
|
|
164
178
|
};
|
|
165
179
|
/** Strzałki ‹ ›: fokus zostaje na strzałce, ale przystanek siatki idzie za stroną. */
|
|
166
180
|
const turnPage = (dir) => {
|
|
167
181
|
const first = page.y * 12 + page.m + dir;
|
|
168
182
|
setPage({ y: Math.floor(first / 12), m: ((first % 12) + 12) % 12 });
|
|
169
|
-
|
|
183
|
+
// Przewinięcie ogłasza widoczne miesiące (APG: `aria-live` na nagłówku).
|
|
184
|
+
setAnnouncement(Array.from({ length: panelCount }, (_, i) => {
|
|
185
|
+
const month = first + i;
|
|
186
|
+
return `${months[((month % 12) + 12) % 12]} ${Math.floor(month / 12)}`;
|
|
187
|
+
}).join(' – '));
|
|
188
|
+
const moved = moveFocusDate(focusIso, dir === 1 ? 'PageDown' : 'PageUp', { minDate, maxDate, firstDay });
|
|
170
189
|
if (moved) {
|
|
171
190
|
const target = +moved.slice(0, 4) * 12 + (+moved.slice(5, 7) - 1);
|
|
172
191
|
// Nowe okno zaczyna się od `first` — dzień spoza niego nie dostałby przystanku Tab.
|
|
@@ -194,6 +213,7 @@ export function KptDateRange({ value, defaultValue = EMPTY_RANGE, onValueChange,
|
|
|
194
213
|
hideOutside: multi,
|
|
195
214
|
minDate,
|
|
196
215
|
maxDate,
|
|
216
|
+
firstDay,
|
|
197
217
|
}),
|
|
198
218
|
};
|
|
199
219
|
});
|
|
@@ -232,17 +252,20 @@ export function KptDateRange({ value, defaultValue = EMPTY_RANGE, onValueChange,
|
|
|
232
252
|
};
|
|
233
253
|
const clear = (event) => {
|
|
234
254
|
event.stopPropagation();
|
|
255
|
+
if (readOnly)
|
|
256
|
+
return;
|
|
235
257
|
setCurrent(EMPTY_RANGE);
|
|
236
258
|
setStartDate('');
|
|
237
259
|
setEndDate('');
|
|
260
|
+
onTouch?.();
|
|
238
261
|
};
|
|
239
262
|
const clockValue = timeEnd === 'start' ? startTime : endTime;
|
|
240
263
|
const setClockValue = timeEnd === 'start' ? setStartTime : setEndTime;
|
|
241
264
|
return (_jsxs("div", { className: cn('kpt-date-range-host', className), ...rest, children: [_jsxs("div", { ref: (el) => {
|
|
242
265
|
triggerRef.current = el;
|
|
243
266
|
setAnchor(el);
|
|
244
|
-
}, className: cn('kpt-date-range', isOpen && 'kpt-date-range--open', disabled && 'kpt-date-range--disabled', showInvalid && 'kpt-date-range--invalid'), role: "
|
|
245
|
-
if (disabled)
|
|
267
|
+
}, className: cn('kpt-date-range', isOpen && 'kpt-date-range--open', disabled && 'kpt-date-range--disabled', readOnly && 'kpt-date-range--readonly', showInvalid && 'kpt-date-range--invalid'), role: "combobox", tabIndex: 0, "aria-keyshortcuts": "Delete Backspace", "aria-required": required || undefined, "aria-readonly": readOnly || undefined, "aria-haspopup": "dialog", "aria-expanded": isOpen, "aria-controls": isOpen ? panelId : undefined, id: field.id, "aria-labelledby": ariaLabel ? undefined : field.labelledBy, "aria-describedby": field.describedBy, "aria-disabled": disabled || undefined, "aria-invalid": showInvalid || undefined, "aria-label": ariaLabel ?? undefined, onClick: toggleOpen, onKeyDown: (event) => {
|
|
268
|
+
if (disabled || readOnly)
|
|
246
269
|
return;
|
|
247
270
|
if (event.key === 'Enter' || event.key === ' ' || event.key === 'ArrowDown') {
|
|
248
271
|
event.preventDefault();
|
|
@@ -255,7 +278,7 @@ export function KptDateRange({ value, defaultValue = EMPTY_RANGE, onValueChange,
|
|
|
255
278
|
}
|
|
256
279
|
},
|
|
257
280
|
// Przy otwartym panelu fokus idzie do kalendarza — „dotknięcie” wyśle dopiero zamknięcie.
|
|
258
|
-
onBlur: () => !isOpen && onTouch?.(), children: [display ? (_jsx("span", { className: "kpt-date-range__value", children: display })) : (_jsx("span", { className: "kpt-date-range__placeholder", children: placeholder ?? t('dateRange.placeholder') })), _jsx("span", { className: "kpt-date-range__spacer" }), display ? (_jsx("button", { type: "button", className: "kpt-date-range__clear", tabIndex: -1, onClick: clear, "aria-label": t('common.clear'), children: _jsx(KptIcon, { name: "x" }) })) : null, _jsx(KptIcon, { className: "kpt-date-range__icon", name: "calendar" })] }), _jsx(KptOverlay, { open: isOpen, anchor: anchor, onClose: close, offset: 4, children: _jsxs("div", { ref: panelRef, id: panelId, role: "dialog", "aria-label": t('dateRange.dialog'), className: cn('kpt-calendar', mode === 'time' && 'kpt-calendar--clock', mode !== 'time' && 'kpt-calendar--range', multi && mode !== 'time' && 'kpt-calendar--multi'), onKeyDown: (event) => {
|
|
281
|
+
onBlur: () => !isOpen && onTouch?.(), children: [display ? (_jsx("span", { className: "kpt-date-range__value", children: display })) : (_jsx("span", { className: "kpt-date-range__placeholder", children: placeholder ?? t('dateRange.placeholder') })), _jsx("span", { className: "kpt-date-range__spacer" }), display && !readOnly ? (_jsx("button", { type: "button", className: "kpt-date-range__clear", tabIndex: -1, onClick: clear, "aria-label": t('common.clear'), children: _jsx(KptIcon, { name: "x" }) })) : null, _jsx(KptIcon, { className: "kpt-date-range__icon", name: "calendar" })] }), _jsx(KptOverlay, { open: isOpen, anchor: anchor, onClose: close, offset: 4, children: _jsxs("div", { ref: panelRef, id: panelId, role: "dialog", "aria-label": t('dateRange.dialog'), className: cn('kpt-calendar', mode === 'time' && 'kpt-calendar--clock', mode !== 'time' && 'kpt-calendar--range', multi && mode !== 'time' && 'kpt-calendar--multi'), onKeyDown: (event) => {
|
|
259
282
|
if (event.key === 'Escape') {
|
|
260
283
|
event.preventDefault();
|
|
261
284
|
event.stopPropagation();
|
package/dist/date-range/llms.txt
CHANGED
|
@@ -5,7 +5,7 @@ Import: `import { KptDateRange, type KptDateRangeValue } from '@konce-pt/react';
|
|
|
5
5
|
|
|
6
6
|
## DOM
|
|
7
7
|
<div class="kpt-date-range-host">
|
|
8
|
-
<div class="kpt-date-range" role="
|
|
8
|
+
<div class="kpt-date-range" role="combobox" tabindex="0" aria-haspopup="dialog">
|
|
9
9
|
<span class="kpt-date-range__value">08.09.2026 – 14.09.2026</span>
|
|
10
10
|
…clear, icon…
|
|
11
11
|
</div>
|
|
@@ -19,7 +19,7 @@ Import: `import { KptDateRange, type KptDateRangeValue } from '@konce-pt/react';
|
|
|
19
19
|
- `visibleMonths`: number (default 1, clamped to 1–3) — months side by side; ignored in 'time' mode
|
|
20
20
|
- `hourFormat`: 12 | 24 (default 24); `minuteStep`: number (default 1)
|
|
21
21
|
- `placeholder` — without one it falls back to `dateRange.placeholder`
|
|
22
|
-
- `displayFormat`: `'pl'` (default) | `'iso'`
|
|
22
|
+
- `displayFormat`: `'pl'` (default) | `'iso'` | `'locale'`
|
|
23
23
|
- `minDate`, `maxDate`: 'YYYY-MM-DD' bounds (inclusive); days outside are disabled
|
|
24
24
|
- `ariaLabel` — the accessible name when no `<label for>` describes the field
|
|
25
25
|
- `disabled`, `invalid`, `touched`
|
|
@@ -60,6 +60,20 @@ in Angular that came for free from two separate template branches.
|
|
|
60
60
|
Day and month names come from `Intl`; "From/To", the navigation, "Done" and the placeholder come
|
|
61
61
|
from the `dateRange`, `datepicker` and `common` namespaces.
|
|
62
62
|
|
|
63
|
+
## Week start, display format and field state
|
|
64
|
+
- The week starts by the active language (`Intl.Locale#getWeekInfo`, with a fallback list of regions):
|
|
65
|
+
en / en-US on Sunday, pl, de, en-GB on Monday. `firstDayOfWeek` (1 = Monday … 7 = Sunday) overrides it;
|
|
66
|
+
the headers, the grid and `Home`/`End` follow it.
|
|
67
|
+
- `displayFormat`: `'pl'` (DD.MM.YYYY, default), `'iso'` (YYYY-MM-DD) or `'locale'` — order and separators
|
|
68
|
+
from `Intl` (en-US: MM/DD/YYYY). The placeholder follows the format and its letters follow the language
|
|
69
|
+
(EN `YYYY`, PL `RRRR`, the `datepicker.yearToken`/`monthToken`/`dayToken` keys). AM/PM come from `Intl`.
|
|
70
|
+
- The field is `role="combobox"` with `aria-haspopup="dialog"` (the APG select-only combobox), so it carries
|
|
71
|
+
`aria-required` (`required`), `aria-readonly` (`readOnly` — the value shows, the panel does not open,
|
|
72
|
+
there is no clear button) and `aria-invalid`.
|
|
73
|
+
- Turning the page with ‹ › is announced (`aria-live="polite"`); the title buttons are named
|
|
74
|
+
"October, Choose month" — the visible text first (WCAG 2.5.3). Clearing the value counts as a touch.
|
|
75
|
+
- "Today" is computed on every open, so a panel opened after midnight marks the right day.
|
|
76
|
+
|
|
63
77
|
## Label and description
|
|
64
78
|
`id` goes to the focusable element, not the host; without one it comes from `KptFormField` or is
|
|
65
79
|
generated. A `div` with a role is not named by `label[for]`, so inside `KptFormField` it gets
|
|
@@ -71,7 +85,7 @@ The field `--kpt-form-field-*`; the calendar `--kpt-color-surface-raised`, the r
|
|
|
71
85
|
`--kpt-color-primary`.
|
|
72
86
|
|
|
73
87
|
## Accessibility
|
|
74
|
-
The trigger is `role="
|
|
88
|
+
The trigger is `role="combobox"` with `aria-haspopup="dialog"`, `aria-expanded` and `aria-controls`;
|
|
75
89
|
the panel is a named `role="dialog"`; the days are real buttons named with the date and their range
|
|
76
90
|
role, and the time ends are switched with a two-button toggle rather than a custom control. The
|
|
77
91
|
keyboard is described above.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type ComponentPropsWithRef } from 'react';
|
|
2
|
-
import { type KptCalendarView, type KptDateSelectionMode } from '@konce-pt/datetime';
|
|
2
|
+
import { type KptCalendarView, type KptDateDisplayFormat, type KptDateSelectionMode, type KptFirstDayOfWeek } from '@konce-pt/datetime';
|
|
3
3
|
import { type KptI18nOverrides } from '../i18n/i18n-context.tsx';
|
|
4
4
|
export type { KptDateSelectionMode as KptDatepickerMode, KptCalendarView as KptDatepickerView };
|
|
5
5
|
export interface KptDatepickerProps extends Omit<ComponentPropsWithRef<'div'>, 'onChange' | 'defaultValue'>, KptI18nOverrides {
|
|
@@ -15,7 +15,13 @@ export interface KptDatepickerProps extends Omit<ComponentPropsWithRef<'div'>, '
|
|
|
15
15
|
placeholder?: string;
|
|
16
16
|
/** Prezentacja miesiąca w trybie 'month' i w etykietach: słownie czy liczbą. */
|
|
17
17
|
monthDisplay?: 'long' | 'number';
|
|
18
|
-
|
|
18
|
+
/**
|
|
19
|
+
* Zapis daty: `'pl'` (DD.MM.RRRR, domyślnie), `'iso'` albo `'locale'` — kolejność i separatory
|
|
20
|
+
* z `Intl` dla aktywnego języka. Placeholder idzie za formatem, a jego litery za językiem.
|
|
21
|
+
*/
|
|
22
|
+
displayFormat?: KptDateDisplayFormat;
|
|
23
|
+
/** Pierwszy dzień tygodnia (1 = poniedziałek … 7 = niedziela); `null` = wg aktywnego języka. */
|
|
24
|
+
firstDayOfWeek?: KptFirstDayOfWeek | null;
|
|
19
25
|
hourFormat?: 12 | 24;
|
|
20
26
|
minuteStep?: number;
|
|
21
27
|
/** Stan panelu — pozwala otworzyć kalendarz z zewnątrz i wiedzieć, kiedy wybór się skończył. */
|
|
@@ -26,6 +32,10 @@ export interface KptDatepickerProps extends Omit<ComponentPropsWithRef<'div'>, '
|
|
|
26
32
|
/** Id pola (nie hosta); bez niego generowane, w `KptFormField` — z wrappera. */
|
|
27
33
|
id?: string;
|
|
28
34
|
disabled?: boolean;
|
|
35
|
+
/** Tylko do odczytu — wartość widać, panel się nie otwiera (`aria-readonly`). */
|
|
36
|
+
readOnly?: boolean;
|
|
37
|
+
/** Pole wymagane — `aria-required`. */
|
|
38
|
+
required?: boolean;
|
|
29
39
|
invalid?: boolean;
|
|
30
40
|
touched?: boolean;
|
|
31
41
|
onTouch?: () => void;
|
|
@@ -41,8 +51,12 @@ export interface KptDatepickerProps extends Omit<ComponentPropsWithRef<'div'>, '
|
|
|
41
51
|
* w panelu, `Escape` zamyka go, a wybór i zamknięcie oddają fokus polu. `Delete`/`Backspace` na polu
|
|
42
52
|
* czyści wartość.
|
|
43
53
|
*
|
|
54
|
+
* Pole to `role="combobox"` z `aria-haspopup="dialog"`, więc niesie `aria-required`,
|
|
55
|
+
* `aria-readonly` i `aria-invalid`. Tydzień zaczyna się wg aktywnego języka, chyba że podano
|
|
56
|
+
* `firstDayOfWeek`; przewinięcie strony strzałką ‹ › jest ogłaszane.
|
|
57
|
+
*
|
|
44
58
|
* @example
|
|
45
59
|
* <KptDatepicker value={date} onValueChange={setDate} startView="decade" minDate="1920-01-01" />
|
|
46
60
|
* <KptDatepicker selectionMode="datetime" hourFormat={12} value={at} onValueChange={setAt} />
|
|
47
61
|
*/
|
|
48
|
-
export declare function KptDatepicker({ value, defaultValue, onValueChange, selectionMode, startView, minDate, maxDate, placeholder, monthDisplay, displayFormat, hourFormat, minuteStep, open, onOpenChange, ariaLabel, id, disabled, invalid, touched, onTouch, locale, dictionary, className, ...rest }: Readonly<KptDatepickerProps>): import("react").JSX.Element;
|
|
62
|
+
export declare function KptDatepicker({ value, defaultValue, onValueChange, selectionMode, startView, minDate, maxDate, placeholder, monthDisplay, displayFormat, firstDayOfWeek, hourFormat, minuteStep, open, onOpenChange, ariaLabel, id, disabled, readOnly, required, invalid, touched, onTouch, locale, dictionary, className, ...rest }: Readonly<KptDatepickerProps>): import("react").JSX.Element;
|