@konce-pt/react 0.11.2 → 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 +14 -1
- package/dist/button-group/button-group.js +30 -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 +90 -31
- 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
package/dist/clock/clock.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type ComponentPropsWithRef } from 'react';
|
|
2
|
-
|
|
2
|
+
import { type KptI18nOverrides } from '../i18n/i18n-context.tsx';
|
|
3
|
+
export interface KptClockProps extends Omit<ComponentPropsWithRef<'div'>, 'onChange'>, KptI18nOverrides {
|
|
3
4
|
/** Godzina jako 'HH:mm' — zawsze 24h, niezależnie od `hourFormat`. */
|
|
4
5
|
value?: string;
|
|
5
6
|
onValueChange?: (value: string) => void;
|
|
@@ -8,10 +9,14 @@ export interface KptClockProps extends Omit<ComponentPropsWithRef<'div'>, 'onCha
|
|
|
8
9
|
}
|
|
9
10
|
/**
|
|
10
11
|
* Tarcza zegara — dwa etapy: godziny → minuty, klikiem albo przeciąganiem. W trybie 24h dochodzi
|
|
11
|
-
* wewnętrzny pierścień 13–00; w 12h przełącznik AM/PM
|
|
12
|
-
* i `KptDateRange`.
|
|
12
|
+
* wewnętrzny pierścień 13–00; w 12h przełącznik AM/PM z etykietami pory dnia z `Intl`. Używana
|
|
13
|
+
* przez `KptDatepicker` i `KptDateRange`.
|
|
14
|
+
*
|
|
15
|
+
* Klawiatura: segmenty godzin i minut to `role="spinbutton"` — `↑`/`↓` o jeden krok (minuty
|
|
16
|
+
* o `minuteStep`), `PageUp`/`PageDown` o pięć, `Home`/`End` do granic. Tak da się ustawić każdą
|
|
17
|
+
* minutę, także spoza znaczników tarczy co pięć.
|
|
13
18
|
*
|
|
14
19
|
* @example
|
|
15
20
|
* <KptClock value={time} onValueChange={setTime} hourFormat={12} minuteStep={5} />
|
|
16
21
|
*/
|
|
17
|
-
export declare function KptClock({ value, onValueChange, hourFormat, minuteStep, className, ...rest }: Readonly<KptClockProps>): import("react").JSX.Element;
|
|
22
|
+
export declare function KptClock({ value, onValueChange, hourFormat, minuteStep, locale, dictionary, className, ...rest }: Readonly<KptClockProps>): import("react").JSX.Element;
|
package/dist/clock/clock.js
CHANGED
|
@@ -1,16 +1,25 @@
|
|
|
1
|
+
'use client';
|
|
1
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
3
|
import { useRef, useState } from 'react';
|
|
3
|
-
import { from12h, handAngle, handIsInner, hourFromAngle, hourMarks, minuteFromAngle, minuteMarks, pad, parseTime, } from '@konce-pt/datetime';
|
|
4
|
+
import { dayPeriodLabels, from12h, handAngle, handIsInner, hourFromAngle, hourMarks, minuteFromAngle, minuteMarks, pad, parseTime, stepClockTime, } from '@konce-pt/datetime';
|
|
4
5
|
import { cn } from "../internal/cn.js";
|
|
6
|
+
import { useKptMessages } from "../i18n/i18n-context.js";
|
|
7
|
+
/** Klawisze segmentu czasu → liczba kroków. */
|
|
8
|
+
const SEGMENT_STEPS = { ArrowUp: 1, ArrowDown: -1, PageUp: 5, PageDown: -5 };
|
|
5
9
|
/**
|
|
6
10
|
* Tarcza zegara — dwa etapy: godziny → minuty, klikiem albo przeciąganiem. W trybie 24h dochodzi
|
|
7
|
-
* wewnętrzny pierścień 13–00; w 12h przełącznik AM/PM
|
|
8
|
-
* i `KptDateRange`.
|
|
11
|
+
* wewnętrzny pierścień 13–00; w 12h przełącznik AM/PM z etykietami pory dnia z `Intl`. Używana
|
|
12
|
+
* przez `KptDatepicker` i `KptDateRange`.
|
|
13
|
+
*
|
|
14
|
+
* Klawiatura: segmenty godzin i minut to `role="spinbutton"` — `↑`/`↓` o jeden krok (minuty
|
|
15
|
+
* o `minuteStep`), `PageUp`/`PageDown` o pięć, `Home`/`End` do granic. Tak da się ustawić każdą
|
|
16
|
+
* minutę, także spoza znaczników tarczy co pięć.
|
|
9
17
|
*
|
|
10
18
|
* @example
|
|
11
19
|
* <KptClock value={time} onValueChange={setTime} hourFormat={12} minuteStep={5} />
|
|
12
20
|
*/
|
|
13
|
-
export function KptClock({ value = '', onValueChange, hourFormat = 24, minuteStep = 1, className, ...rest }) {
|
|
21
|
+
export function KptClock({ value = '', onValueChange, hourFormat = 24, minuteStep = 1, locale, dictionary, className, ...rest }) {
|
|
22
|
+
const { t, locale: activeLocale } = useKptMessages({ locale, dictionary });
|
|
14
23
|
const faceRef = useRef(null);
|
|
15
24
|
const [phase, setPhase] = useState('hours');
|
|
16
25
|
// Wartość jest jedynym źródłem prawdy — stan tarczy wyliczamy z niej przy każdym renderze,
|
|
@@ -20,7 +29,28 @@ export function KptClock({ value = '', onValueChange, hourFormat = 24, minuteSte
|
|
|
20
29
|
const minute = parsed?.m ?? 0;
|
|
21
30
|
const ampm = hour < 12 ? 'AM' : 'PM';
|
|
22
31
|
const hour12 = hour % 12 === 0 ? 12 : hour % 12;
|
|
32
|
+
const periods = dayPeriodLabels(activeLocale);
|
|
33
|
+
const hourLabel = hourFormat === 12 ? String(hour12) : pad(hour);
|
|
34
|
+
// W 12h czytnik słyszy godzinę z porą dnia („9 PM”), nie samą liczbę 21.
|
|
35
|
+
const hourText = hourFormat === 12 ? `${hour12} ${ampm === 'AM' ? periods.am : periods.pm}` : hourLabel;
|
|
23
36
|
const emit = (h, m) => onValueChange?.(`${pad(h)}:${pad(m)}`);
|
|
37
|
+
const onSegmentKey = (part, event) => {
|
|
38
|
+
let next;
|
|
39
|
+
if (event.key === 'Home') {
|
|
40
|
+
next = part === 'hours' ? { h: 0, m: minute } : { h: hour, m: 0 };
|
|
41
|
+
}
|
|
42
|
+
else if (event.key === 'End') {
|
|
43
|
+
next = part === 'hours' ? { h: 23, m: minute } : { h: hour, m: 60 - Math.max(1, minuteStep) };
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
const delta = SEGMENT_STEPS[event.key];
|
|
47
|
+
if (delta === undefined)
|
|
48
|
+
return;
|
|
49
|
+
next = stepClockTime(hour, minute, part, delta, minuteStep);
|
|
50
|
+
}
|
|
51
|
+
event.preventDefault();
|
|
52
|
+
emit(next.h, next.m);
|
|
53
|
+
};
|
|
24
54
|
const onDialPointer = (event) => {
|
|
25
55
|
const face = faceRef.current;
|
|
26
56
|
if (!face)
|
|
@@ -45,7 +75,7 @@ export function KptClock({ value = '', onValueChange, hourFormat = 24, minuteSte
|
|
|
45
75
|
}
|
|
46
76
|
};
|
|
47
77
|
const marks = phase === 'hours' ? hourMarks(hour, hourFormat) : minuteMarks(minute);
|
|
48
|
-
return (_jsx("div", { className: cn('kpt-clock-host', className), ...rest, children: _jsxs("div", { className: "kpt-clock", children: [_jsxs("div", { className: "kpt-clock__time", children: [_jsx("
|
|
78
|
+
return (_jsx("div", { className: cn('kpt-clock-host', className), ...rest, children: _jsxs("div", { className: "kpt-clock", children: [_jsxs("div", { className: "kpt-clock__time", children: [_jsx("span", { className: cn('kpt-clock__seg', phase === 'hours' && 'is-active'), role: "spinbutton", tabIndex: 0, "aria-label": t('clock.hours'), "aria-valuemin": 0, "aria-valuemax": 23, "aria-valuenow": hour, "aria-valuetext": hourText, onClick: () => setPhase('hours'), onFocus: () => setPhase('hours'), onKeyDown: (event) => onSegmentKey('hours', event), children: hourLabel }), _jsx("span", { className: "kpt-clock__colon", "aria-hidden": "true", children: ":" }), _jsx("span", { className: cn('kpt-clock__seg', phase === 'minutes' && 'is-active'), role: "spinbutton", tabIndex: 0, "aria-label": t('clock.minutes'), "aria-valuemin": 0, "aria-valuemax": 59, "aria-valuenow": minute, "aria-valuetext": pad(minute), onClick: () => setPhase('minutes'), onFocus: () => setPhase('minutes'), onKeyDown: (event) => onSegmentKey('minutes', event), children: pad(minute) }), hourFormat === 12 ? (_jsxs("div", { className: "kpt-clock__ampm", role: "group", children: [_jsx("button", { type: "button", className: cn(ampm === 'AM' && 'is-active'), "aria-pressed": ampm === 'AM', onClick: () => emit(from12h(hour12, 'AM'), minute), children: periods.am }), _jsx("button", { type: "button", className: cn(ampm === 'PM' && 'is-active'), "aria-pressed": ampm === 'PM', onClick: () => emit(from12h(hour12, 'PM'), minute), children: periods.pm })] })) : null] }), _jsxs("div", { ref: faceRef, className: "kpt-clock__face", role: "group", "aria-label": phase === 'hours' ? t('clock.hours') : t('clock.minutes'), onPointerDown: onDialPointer, onPointerMove: onDialPointer, onPointerUp: () => phase === 'hours' && setPhase('minutes'), children: [_jsx("span", { className: "kpt-clock__center" }), _jsx("span", { className: cn('kpt-clock__hand', handIsInner(hour, hourFormat, phase) && 'kpt-clock__hand--inner'), style: { transform: `rotate(${handAngle(hour, minute, phase)}deg)` } }), marks.map((mark) => (_jsx("button", { type: "button", className: cn('kpt-clock__num', mark.inner && 'kpt-clock__num--inner', mark.selected && 'kpt-clock__num--selected'), "aria-pressed": mark.selected, style: { left: `${mark.x}%`, top: `${mark.y}%` }, onClick: () => {
|
|
49
79
|
if (phase === 'hours') {
|
|
50
80
|
emit(mark.value, minute);
|
|
51
81
|
setPhase('minutes');
|
package/dist/clock/llms.txt
CHANGED
|
@@ -46,5 +46,9 @@ The dial `--kpt-color-surface-raised`, the hand and the selection `--kpt-color-p
|
|
|
46
46
|
the numbers `--kpt-color-on-surface`.
|
|
47
47
|
|
|
48
48
|
## Accessibility
|
|
49
|
-
Every mark is a real `<button
|
|
50
|
-
segments are
|
|
49
|
+
Every mark is a real `<button>` with `aria-pressed`, inside a group named "Hours"/"Minutes". The hour and
|
|
50
|
+
minute segments are `role="spinbutton"`: Tab to them, `↑`/`↓` step by one (minutes by `minuteStep`),
|
|
51
|
+
`PageUp`/`PageDown` by five, `Home`/`End` to the bounds — so every minute is reachable, not only the
|
|
52
|
+
five-minute marks. Focusing a segment switches the stage. AM/PM labels come from `Intl` for the active
|
|
53
|
+
language and the hour's `aria-valuetext` includes the period ("9 PM"). `locale`/`dictionary` override the
|
|
54
|
+
language for this component.
|
|
@@ -20,6 +20,10 @@ export interface KptColorPickerProps extends Omit<ComponentPropsWithRef<'div'>,
|
|
|
20
20
|
/** Nazwa dostępna, gdy kontrolki nie opisuje etykieta (`<label for>` albo `KptFormField`). */
|
|
21
21
|
ariaLabel?: string | null;
|
|
22
22
|
disabled?: boolean;
|
|
23
|
+
/** Tylko do odczytu — kolor widać, nie da się go zmienić. */
|
|
24
|
+
readOnly?: boolean;
|
|
25
|
+
/** Pole wymagane — `aria-required` na polu kodu. */
|
|
26
|
+
required?: boolean;
|
|
23
27
|
invalid?: boolean;
|
|
24
28
|
touched?: boolean;
|
|
25
29
|
onTouch?: () => void;
|
|
@@ -33,4 +37,4 @@ export interface KptColorPickerProps extends Omit<ComponentPropsWithRef<'div'>,
|
|
|
33
37
|
* @example
|
|
34
38
|
* <KptColorPicker value={color} onValueChange={setColor} />
|
|
35
39
|
*/
|
|
36
|
-
export declare function KptColorPicker({ value, defaultValue, onValueChange, presets, id, ariaLabel, disabled, invalid, touched, onTouch, locale, dictionary, className, ...rest }: Readonly<KptColorPickerProps>): import("react").JSX.Element;
|
|
40
|
+
export declare function KptColorPicker({ value, defaultValue, onValueChange, presets, id, ariaLabel, disabled, readOnly, required, invalid, touched, onTouch, locale, dictionary, className, ...rest }: Readonly<KptColorPickerProps>): import("react").JSX.Element;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
'use client';
|
|
1
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
3
|
import { useState } from 'react';
|
|
3
4
|
import { cn } from "../internal/cn.js";
|
|
@@ -16,7 +17,16 @@ const DEFAULT_NAMES = {
|
|
|
16
17
|
'#111827': 'black',
|
|
17
18
|
'#ffffff': 'white',
|
|
18
19
|
};
|
|
19
|
-
|
|
20
|
+
/**
|
|
21
|
+
* Kod z pola tekstowego jako `#rrggbb` albo `null`. Przyjmuje zapis bez `#` i skrót `#rgb`
|
|
22
|
+
* (`#fff` → `#ffffff`). Bliźniak w porcie Angulara (`color-picker.component.ts`) — zmiana w obu.
|
|
23
|
+
*/
|
|
24
|
+
function parseHex(text) {
|
|
25
|
+
const raw = text.trim().replace(/^#/, '').toLowerCase();
|
|
26
|
+
if (/^[0-9a-f]{3}$/.test(raw))
|
|
27
|
+
return `#${[...raw].map((c) => c + c).join('')}`;
|
|
28
|
+
return /^[0-9a-f]{6}$/.test(raw) ? `#${raw}` : null;
|
|
29
|
+
}
|
|
20
30
|
/**
|
|
21
31
|
* Wybór koloru: próbka, pole hex i presety. Wartością jest hex.
|
|
22
32
|
*
|
|
@@ -26,12 +36,16 @@ const HEX = /^#[0-9a-f]{6}$/i;
|
|
|
26
36
|
* @example
|
|
27
37
|
* <KptColorPicker value={color} onValueChange={setColor} />
|
|
28
38
|
*/
|
|
29
|
-
export function KptColorPicker({ value, defaultValue = '#3b82f6', onValueChange, presets = DEFAULT_PRESETS, id, ariaLabel = null, disabled = false, invalid = false, touched, onTouch, locale, dictionary, className, ...rest }) {
|
|
39
|
+
export function KptColorPicker({ value, defaultValue = '#3b82f6', onValueChange, presets = DEFAULT_PRESETS, id, ariaLabel = null, disabled = false, readOnly = false, required = false, invalid = false, touched, onTouch, locale, dictionary, className, ...rest }) {
|
|
30
40
|
const { t } = useKptMessages({ locale, dictionary });
|
|
31
41
|
const field = useKptFieldA11y(id, 'kpt-color-picker');
|
|
32
|
-
/**
|
|
42
|
+
/**
|
|
43
|
+
* Tekst w polu hex dokładnie tak, jak go wpisano, do wyjścia z pola. Wartość zmienia się, gdy tekst
|
|
44
|
+
* staje się poprawnym kodem, ale pole nie jest przepisywane w trakcie pisania (doklejenie `#`
|
|
45
|
+
* przenosiło kursor na koniec przy edycji w środku).
|
|
46
|
+
*/
|
|
33
47
|
const [hexDraft, setHexDraft] = useState(null);
|
|
34
|
-
const hexInvalid = hexDraft !== null &&
|
|
48
|
+
const hexInvalid = hexDraft !== null && hexDraft.trim() !== '' && parseHex(hexDraft) === null;
|
|
35
49
|
const presetList = presets.map((preset) => {
|
|
36
50
|
const item = typeof preset === 'string' ? { value: preset, label: '' } : preset;
|
|
37
51
|
const named = DEFAULT_NAMES[item.value.toLowerCase()];
|
|
@@ -39,24 +53,29 @@ export function KptColorPicker({ value, defaultValue = '#3b82f6', onValueChange,
|
|
|
39
53
|
});
|
|
40
54
|
const isActive = (color) => color.toLowerCase() === current.toLowerCase();
|
|
41
55
|
const [current, setCurrent] = useControllableState({ value, defaultValue, onChange: onValueChange });
|
|
42
|
-
return (_jsx("div", { className: cn('kpt-color-picker-host', className), ...rest, children: _jsxs("div", { className: "kpt-color-picker", children: [_jsx("label", { className: "kpt-color-picker__swatch", style: { backgroundColor: current }, children: _jsx("input", { type: "color", value: current, disabled: disabled, "aria-label": t('colorPicker.picker'),
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
56
|
+
return (_jsx("div", { className: cn('kpt-color-picker-host', className), ...rest, children: _jsxs("div", { className: "kpt-color-picker", children: [_jsx("label", { className: "kpt-color-picker__swatch", style: { backgroundColor: current }, children: _jsx("input", { type: "color", value: current, disabled: disabled, "aria-label": t('colorPicker.picker'), "aria-readonly": readOnly || undefined, onClick: (event) => {
|
|
57
|
+
if (readOnly)
|
|
58
|
+
event.preventDefault();
|
|
59
|
+
}, onChange: (event) => {
|
|
60
|
+
if (readOnly)
|
|
61
|
+
return;
|
|
47
62
|
setHexDraft(null);
|
|
48
|
-
setCurrent(
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
63
|
+
setCurrent(event.target.value);
|
|
64
|
+
}, onBlur: () => onTouch?.() }) }), _jsx("input", { type: "text", className: "kpt-color-picker__hex", id: field.id, "aria-label": field.labelledBy ? undefined : (ariaLabel ?? t('colorPicker.hex')), "aria-describedby": field.describedBy, value: hexDraft ?? current, disabled: disabled, readOnly: readOnly, "aria-required": required || undefined, maxLength: 7, "aria-invalid": (invalid && (touched ?? true)) || hexInvalid || undefined, onChange: (event) => {
|
|
65
|
+
const text = event.target.value;
|
|
66
|
+
setHexDraft(text);
|
|
67
|
+
const hex = parseHex(text);
|
|
68
|
+
if (hex)
|
|
69
|
+
setCurrent(hex);
|
|
53
70
|
}, onBlur: () => {
|
|
54
|
-
//
|
|
55
|
-
if (hexDraft === '')
|
|
71
|
+
// Poprawny albo pusty kod chowa szkic; niepełny zostaje widoczny z `aria-invalid`.
|
|
72
|
+
if (hexDraft !== null && (hexDraft.trim() === '' || parseHex(hexDraft)))
|
|
56
73
|
setHexDraft(null);
|
|
57
74
|
onTouch?.();
|
|
58
|
-
} }), _jsx("div", { className: "kpt-color-picker__presets", children: presetList.map((preset) => (_jsx("button", { type: "button", className: cn('kpt-color-picker__preset', isActive(preset.value) && 'is-active'), style: { backgroundColor: preset.value }, disabled: disabled, "aria-pressed": isActive(preset.value), onClick: () => {
|
|
75
|
+
} }), _jsx("div", { className: "kpt-color-picker__presets", children: presetList.map((preset) => (_jsx("button", { type: "button", className: cn('kpt-color-picker__preset', isActive(preset.value) && 'is-active'), style: { backgroundColor: preset.value }, disabled: disabled || readOnly, "aria-pressed": isActive(preset.value), onClick: () => {
|
|
59
76
|
setHexDraft(null);
|
|
60
77
|
setCurrent(preset.value);
|
|
78
|
+
// Wybór presetem to też dotknięcie pola — inaczej błąd walidacji nigdy by się nie pokazał.
|
|
79
|
+
onTouch?.();
|
|
61
80
|
}, "aria-label": preset.label }, preset.value))) })] }) }));
|
|
62
81
|
}
|
|
@@ -52,3 +52,6 @@ preset `--kpt-color-focus-ring`.
|
|
|
52
52
|
## Accessibility
|
|
53
53
|
Every preset is a `<button>` labelled with its hex value; the hex field carries `aria-invalid` once
|
|
54
54
|
the field is touched.
|
|
55
|
+
|
|
56
|
+
## Read-only and required
|
|
57
|
+
`readOnly` blocks the picker, the code field and the presets; `required` sets `aria-required` on the code field. Picking a preset or a colour counts as a touch. The code field takes `fff`, `#fff` and `#ffffff` and is not rewritten while typing, so the caret does not jump.
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useSyncExternalStore } from 'react';
|
|
2
|
+
import { isDevBuild } from "../internal/dev.js";
|
|
2
3
|
/*
|
|
3
4
|
* Magazyn potwierdzeń jako moduł-singleton — odpowiednik serwisu `providedIn: 'root'` z Angulara,
|
|
4
5
|
* ten sam wzorzec co `kptToast`. Dzięki temu `await kptConfirm(...)` woła się także spoza drzewa
|
|
@@ -21,6 +22,11 @@ function emit() {
|
|
|
21
22
|
* if (await kptConfirm({ message: 'Usunąć rekord?', variant: 'danger' })) { … }
|
|
22
23
|
*/
|
|
23
24
|
export function kptConfirm(options) {
|
|
25
|
+
// Kontener subskrybuje magazyn — bez subskrybentów nikt nie pokaże okna ani nie rozstrzygnie obietnicy.
|
|
26
|
+
if (isDevBuild() && listeners.size === 0) {
|
|
27
|
+
console.warn('kptConfirm: brak <KptConfirmContainer /> w aplikacji — okno się nie pokaże, ' +
|
|
28
|
+
'a obietnica nie rozstrzygnie się, dopóki kontener nie zostanie dodany.');
|
|
29
|
+
}
|
|
24
30
|
return new Promise((resolve) => {
|
|
25
31
|
current?.resolve(false);
|
|
26
32
|
current = { id: ++seq, ...options, resolve };
|
package/dist/confirm/llms.txt
CHANGED
|
@@ -22,7 +22,8 @@ current request through `useSyncExternalStore` for anyone rendering their own co
|
|
|
22
22
|
## The promise always settles
|
|
23
23
|
Escape and the × close the dialog through `onOpenChange`, which resolves `false` — an awaiting
|
|
24
24
|
caller is never left hanging. A second `kptConfirm()` while one is open rejects the previous
|
|
25
|
-
request for the same reason: one request at a time, and nothing is dropped silently.
|
|
25
|
+
request for the same reason: one request at a time, and nothing is dropped silently. Without a
|
|
26
|
+
mounted `<KptConfirmContainer />` nobody can show the dialog, so a dev build warns in the console.
|
|
26
27
|
|
|
27
28
|
## Examples
|
|
28
29
|
<KptConfirmContainer />
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useEffectEvent, useLayoutEffect, useRef, useState } from 'react';
|
|
3
4
|
import { cn } from "../internal/cn.js";
|
|
4
5
|
import { MenuRows } from "../menu/menu-rows.js";
|
|
5
6
|
import { focusMenuEdge, handleMenuKeydown } from "../menu/menu-keys.js";
|
|
@@ -26,6 +27,9 @@ export function KptContextMenu({ items = [], children, onSelect, className, ...r
|
|
|
26
27
|
const [open, setOpen] = useState(false);
|
|
27
28
|
const [point, setPoint] = useState({ x: 0, y: 0 });
|
|
28
29
|
const panelRef = useRef(null);
|
|
30
|
+
const layerRef = useRef(null);
|
|
31
|
+
/** Licznik otwarć — każde PPM (także przy otwartym menu) dociska panel i ustawia fokus od nowa. */
|
|
32
|
+
const [openSeq, setOpenSeq] = useState(0);
|
|
29
33
|
/** Element, który miał fokus przed otwarciem — tam fokus wraca po zamknięciu. */
|
|
30
34
|
const returnTo = useRef(null);
|
|
31
35
|
const onContextMenu = (event) => {
|
|
@@ -41,21 +45,27 @@ export function KptContextMenu({ items = [], children, onSelect, className, ...r
|
|
|
41
45
|
setPoint({ x: event.clientX, y: event.clientY });
|
|
42
46
|
}
|
|
43
47
|
setOpen(true);
|
|
48
|
+
setOpenSeq((n) => n + 1);
|
|
44
49
|
};
|
|
45
|
-
// Po otwarciu: dociśnięcie do okna i fokus na pierwszej pozycji.
|
|
46
|
-
|
|
50
|
+
// Po każdym otwarciu: warstwa w top layer, dociśnięcie do okna i fokus na pierwszej pozycji.
|
|
51
|
+
// Przed malowaniem (`useLayoutEffect`), żeby panel nie mignął w niedociśniętym miejscu. Docisk
|
|
52
|
+
// idzie wprost w styl elementu — stan `point` zostaje, więc React go nie nadpisze.
|
|
53
|
+
useLayoutEffect(() => {
|
|
47
54
|
const panel = panelRef.current;
|
|
48
|
-
if (
|
|
55
|
+
if (openSeq === 0 || !panel)
|
|
49
56
|
return;
|
|
57
|
+
const layer = layerRef.current;
|
|
58
|
+
if (layer && typeof layer.showPopover === 'function' && !layer.matches(':popover-open'))
|
|
59
|
+
layer.showPopover();
|
|
50
60
|
const r = panel.getBoundingClientRect();
|
|
51
61
|
const maxX = window.innerWidth - r.width - EDGE;
|
|
52
62
|
const maxY = window.innerHeight - r.height - EDGE;
|
|
53
|
-
if (
|
|
54
|
-
|
|
55
|
-
|
|
63
|
+
if (r.left > maxX)
|
|
64
|
+
panel.style.left = `${Math.max(EDGE, maxX)}px`;
|
|
65
|
+
if (r.top > maxY)
|
|
66
|
+
panel.style.top = `${Math.max(EDGE, maxY)}px`;
|
|
56
67
|
focusMenuEdge(panel, 'first');
|
|
57
|
-
|
|
58
|
-
}, [open]);
|
|
68
|
+
}, [openSeq]);
|
|
59
69
|
const close = () => {
|
|
60
70
|
setOpen(false);
|
|
61
71
|
const target = returnTo.current;
|
|
@@ -63,6 +73,19 @@ export function KptContextMenu({ items = [], children, onSelect, className, ...r
|
|
|
63
73
|
if (target?.isConnected)
|
|
64
74
|
target.focus();
|
|
65
75
|
};
|
|
76
|
+
// Przewinięcie albo zmiana rozmiaru okna odrywa panel od miejsca kliknięcia — zamykamy go.
|
|
77
|
+
const closeOnMove = useEffectEvent(() => close());
|
|
78
|
+
useEffect(() => {
|
|
79
|
+
if (!open)
|
|
80
|
+
return;
|
|
81
|
+
const onMove = () => closeOnMove();
|
|
82
|
+
window.addEventListener('scroll', onMove, true);
|
|
83
|
+
window.addEventListener('resize', onMove);
|
|
84
|
+
return () => {
|
|
85
|
+
window.removeEventListener('scroll', onMove, true);
|
|
86
|
+
window.removeEventListener('resize', onMove);
|
|
87
|
+
};
|
|
88
|
+
}, [open]);
|
|
66
89
|
const onPanelKeyDown = (event) => {
|
|
67
90
|
if (panelRef.current && handleMenuKeydown(panelRef.current, event.nativeEvent) === 'close')
|
|
68
91
|
close();
|
|
@@ -73,8 +96,8 @@ export function KptContextMenu({ items = [], children, onSelect, className, ...r
|
|
|
73
96
|
onSelect?.(item);
|
|
74
97
|
close();
|
|
75
98
|
};
|
|
76
|
-
return (_jsxs("div", { className: cn('kpt-context-menu-host', className), ...rest, children: [_jsx("div", { className: "kpt-context-menu__area", onContextMenu: onContextMenu, children: children }), open ? (_jsxs(
|
|
99
|
+
return (_jsxs("div", { className: cn('kpt-context-menu-host', className), ...rest, children: [_jsx("div", { className: "kpt-context-menu__area", onContextMenu: onContextMenu, children: children }), open ? (_jsxs("div", { ref: layerRef, className: "kpt-context-menu__layer", popover: "manual", children: [_jsx("div", { className: "kpt-context-menu__backdrop", onClick: close, onContextMenu: (event) => {
|
|
77
100
|
event.preventDefault();
|
|
78
101
|
close();
|
|
79
|
-
} }), _jsx("div", { ref: panelRef, className: "kpt-context-menu__panel", role: "menu", style: { left: point.x, top: point.y }, onKeyDown: onPanelKeyDown, children: _jsx(MenuRows, { items: items, block: "kpt-context-menu", onPick: pick }) })] })) : null] }));
|
|
102
|
+
} }), _jsx("div", { ref: panelRef, className: "kpt-context-menu__panel", role: "menu", tabIndex: -1, style: { left: point.x, top: point.y }, onKeyDown: onPanelKeyDown, children: _jsx(MenuRows, { items: items, block: "kpt-context-menu", onPick: pick }) })] })) : null] }));
|
|
80
103
|
}
|
|
@@ -44,3 +44,6 @@ no right click, so still offer the same actions through a visible button with `K
|
|
|
44
44
|
## Accessibility
|
|
45
45
|
The panel is `role="menu"` with `role="menuitem"` buttons at `tabindex="-1"` (one Tab stop). Keyboard — see
|
|
46
46
|
"Keyboard" above.
|
|
47
|
+
|
|
48
|
+
## Positioning
|
|
49
|
+
The panel opens in the top layer (`popover="manual"`), so an ancestor with `transform`, `filter` or `backdrop-filter` (an animated card, a glass panel) no longer shifts it and `overflow: hidden` no longer clips it. Scrolling or resizing the window closes the menu.
|
|
@@ -11,7 +11,17 @@ export interface KptDataTableProps<T> extends Omit<ComponentPropsWithRef<'div'>,
|
|
|
11
11
|
pageSize?: number;
|
|
12
12
|
pageSizeOptions?: readonly number[];
|
|
13
13
|
selectable?: 'none' | 'single' | 'multiple';
|
|
14
|
+
/** Trwa ładowanie — tabela dostaje `aria-busy`, a ciało pokazuje komunikat `dataTable.loading`. */
|
|
14
15
|
loading?: boolean;
|
|
16
|
+
/** Nazwa dostępna tabeli, gdy nie ma widocznego podpisu (`caption`). */
|
|
17
|
+
ariaLabel?: string | null;
|
|
18
|
+
/** Widoczny podpis tabeli (`<caption>`) — zarazem jej nazwa dla czytnika. */
|
|
19
|
+
caption?: string | null;
|
|
20
|
+
/**
|
|
21
|
+
* Wiersze jako akcja: fokusowalne (Tab) i aktywowane Enterem/Spacją, które wołają `onRowClick`.
|
|
22
|
+
* Domyślnie włączone, gdy podano `onRowClick` — inaczej klik w wiersz działałby tylko myszą.
|
|
23
|
+
*/
|
|
24
|
+
clickableRows?: boolean;
|
|
15
25
|
emptyMessage?: string;
|
|
16
26
|
/** Pole identyfikujące wiersz; bez niego wiersze porównujemy po referencji. */
|
|
17
27
|
rowKey?: string | null;
|
|
@@ -40,6 +50,7 @@ export interface KptDataTableProps<T> extends Omit<ComponentPropsWithRef<'div'>,
|
|
|
40
50
|
defaultHiddenFields?: readonly string[];
|
|
41
51
|
onHiddenFieldsChange?: (fields: readonly string[]) => void;
|
|
42
52
|
onRowClick?: (row: T) => void;
|
|
53
|
+
/** Tryb lazy: woła się na starcie (pierwsza strona) i po każdej zmianie sortowania/filtra/strony. */
|
|
43
54
|
onLazyLoad?: (state: KptTableLazyState) => void;
|
|
44
55
|
onCellEdit?: (event: KptCellEditEvent<T>) => void;
|
|
45
56
|
/** Szablony komórek: pole → funkcja renderująca. */
|
|
@@ -51,4 +62,4 @@ export interface KptDataTableProps<T> extends Omit<ComponentPropsWithRef<'div'>,
|
|
|
51
62
|
/** Treść po lewej stronie paginatora (odpowiednik slotu `[kptPaginatorActions]`). */
|
|
52
63
|
paginatorActions?: ReactNode;
|
|
53
64
|
}
|
|
54
|
-
export declare function KptDataTable<T = Record<string, unknown>>({ columns, data, filterable, filterRowPosition, pageSize, pageSizeOptions, selectable, loading, emptyMessage, rowKey, scrollHeight, virtualScroll, rowHeight, reorderable, exportable, exportFilename, lazy, totalRecords, expandable, multiExpand, summaryLabel, summaryValues, selection, defaultSelection, onSelectionChange, expandedRows, defaultExpandedRows, onExpandedRowsChange, hiddenFields, defaultHiddenFields, onHiddenFieldsChange, onRowClick, onLazyLoad, onCellEdit, cells, cellEditors, renderRowDetail, paginatorActions, locale, dictionary, className, style, ...rest }: Readonly<KptDataTableProps<T>>): import("react").JSX.Element;
|
|
65
|
+
export declare function KptDataTable<T = Record<string, unknown>>({ columns, data, filterable, filterRowPosition, pageSize, pageSizeOptions, selectable, loading, ariaLabel, caption, clickableRows, emptyMessage, rowKey, scrollHeight, virtualScroll, rowHeight, reorderable, exportable, exportFilename, lazy, totalRecords, expandable, multiExpand, summaryLabel, summaryValues, selection, defaultSelection, onSelectionChange, expandedRows, defaultExpandedRows, onExpandedRowsChange, hiddenFields, defaultHiddenFields, onHiddenFieldsChange, onRowClick, onLazyLoad, onCellEdit, cells, cellEditors, renderRowDetail, paginatorActions, locale, dictionary, className, style, ...rest }: Readonly<KptDataTableProps<T>>): import("react").JSX.Element;
|