@konce-pt/react 0.9.1 → 0.11.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/dist/accordion/accordion.js +7 -1
- package/dist/accordion/llms.txt +8 -4
- package/dist/alert/alert.d.ts +10 -4
- package/dist/alert/alert.js +13 -6
- package/dist/alert/llms.txt +8 -6
- package/dist/app-shell/app-shell.js +28 -2
- package/dist/app-shell/llms.txt +3 -0
- package/dist/autocomplete/autocomplete.d.ts +7 -2
- package/dist/autocomplete/autocomplete.js +46 -9
- package/dist/autocomplete/llms.txt +12 -0
- package/dist/avatar/avatar.d.ts +4 -2
- package/dist/avatar/avatar.js +8 -3
- package/dist/avatar/llms.txt +9 -5
- package/dist/avatar-group/avatar-group.d.ts +5 -2
- package/dist/avatar-group/avatar-group.js +10 -2
- package/dist/avatar-group/llms.txt +7 -4
- package/dist/badge/badge.d.ts +8 -1
- package/dist/badge/badge.js +3 -2
- package/dist/badge/llms.txt +11 -5
- package/dist/bottom-sheet/bottom-sheet.d.ts +1 -1
- package/dist/bottom-sheet/bottom-sheet.js +8 -2
- package/dist/bottom-sheet/llms.txt +8 -2
- package/dist/breadcrumb/breadcrumb.js +4 -4
- package/dist/breadcrumb/llms.txt +12 -5
- package/dist/card/card.d.ts +9 -1
- package/dist/card/card.js +2 -2
- package/dist/card/llms.txt +6 -2
- package/dist/carousel/carousel.d.ts +8 -1
- package/dist/carousel/carousel.js +9 -2
- package/dist/carousel/llms.txt +24 -10
- package/dist/checkbox/checkbox.d.ts +6 -1
- package/dist/checkbox/checkbox.js +11 -3
- package/dist/checkbox/llms.txt +11 -0
- package/dist/chip/chip.js +10 -1
- package/dist/chip/llms.txt +7 -4
- package/dist/chips-input/chips-input.d.ts +7 -1
- package/dist/chips-input/chips-input.js +21 -5
- package/dist/chips-input/llms.txt +12 -0
- package/dist/color-picker/color-picker.d.ts +18 -5
- package/dist/color-picker/color-picker.js +48 -14
- package/dist/color-picker/llms.txt +14 -0
- package/dist/confirm/confirm-container.d.ts +5 -1
- package/dist/confirm/confirm-container.js +15 -1
- package/dist/confirm/llms.txt +4 -2
- package/dist/context-menu/context-menu.d.ts +5 -0
- package/dist/context-menu/context-menu.js +48 -4
- package/dist/context-menu/llms.txt +11 -4
- package/dist/date-range/date-range.d.ts +14 -1
- package/dist/date-range/date-range.js +133 -17
- package/dist/date-range/llms.txt +26 -3
- package/dist/datepicker/datepicker.d.ts +10 -1
- package/dist/datepicker/datepicker.js +147 -14
- package/dist/datepicker/llms.txt +27 -3
- package/dist/dialog/dialog.d.ts +4 -0
- package/dist/dialog/dialog.js +7 -1
- package/dist/dialog/llms.txt +2 -0
- package/dist/drawer/drawer.js +7 -2
- package/dist/drawer/llms.txt +10 -5
- package/dist/empty/empty.d.ts +6 -1
- package/dist/empty/empty.js +2 -2
- package/dist/empty/llms.txt +4 -3
- package/dist/fab/llms.txt +2 -1
- package/dist/file-upload/file-upload.d.ts +5 -0
- package/dist/file-upload/file-upload.js +24 -3
- package/dist/file-upload/llms.txt +15 -4
- package/dist/form-field/form-field-context.d.ts +30 -1
- package/dist/form-field/form-field-context.js +27 -0
- package/dist/form-field/form-field.js +16 -2
- package/dist/form-field/llms.txt +10 -1
- package/dist/galleria/galleria.d.ts +6 -1
- package/dist/galleria/galleria.js +40 -1
- package/dist/galleria/llms.txt +17 -8
- package/dist/icon/icon.d.ts +7 -1
- package/dist/icon/icon.js +18 -4
- package/dist/icon/llms.txt +5 -2
- package/dist/image/image.d.ts +5 -0
- package/dist/image/image.js +38 -2
- package/dist/image/llms.txt +24 -17
- package/dist/input/input.d.ts +1 -1
- package/dist/input/input.js +4 -6
- package/dist/input/llms.txt +5 -0
- package/dist/input-mask/input-mask.d.ts +1 -1
- package/dist/input-mask/input-mask.js +8 -6
- package/dist/input-mask/llms.txt +11 -0
- package/dist/input-number/input-number.d.ts +13 -1
- package/dist/input-number/input-number.js +49 -9
- package/dist/input-number/llms.txt +21 -3
- package/dist/input-otp/input-otp.d.ts +7 -2
- package/dist/input-otp/input-otp.js +36 -7
- package/dist/input-otp/llms.txt +14 -0
- package/dist/internal/calendar-focus.d.ts +8 -0
- package/dist/internal/calendar-focus.js +38 -0
- package/dist/internal/option-nav.d.ts +24 -0
- package/dist/internal/option-nav.js +80 -0
- package/dist/internal/overlay.d.ts +7 -2
- package/dist/internal/overlay.js +4 -2
- package/dist/internal/roving.d.ts +9 -0
- package/dist/internal/roving.js +43 -0
- package/dist/internal/tween.d.ts +17 -0
- package/dist/internal/tween.js +40 -0
- package/dist/internal/use-modal-focus.d.ts +10 -0
- package/dist/internal/use-modal-focus.js +57 -0
- package/dist/knob/knob.d.ts +9 -1
- package/dist/knob/knob.js +41 -5
- package/dist/knob/llms.txt +15 -0
- package/dist/listbox/listbox.d.ts +13 -1
- package/dist/listbox/listbox.js +89 -6
- package/dist/listbox/llms.txt +21 -5
- package/dist/megamenu/llms.txt +28 -10
- package/dist/megamenu/megamenu.d.ts +10 -1
- package/dist/megamenu/megamenu.js +116 -5
- package/dist/menu/llms.txt +12 -1
- package/dist/menu/menu-keys.d.ts +15 -0
- package/dist/menu/menu-keys.js +53 -0
- package/dist/menu/menu-rows.js +1 -1
- package/dist/menu/menu.js +52 -4
- package/dist/menubar/llms.txt +24 -7
- package/dist/menubar/menubar.d.ts +8 -1
- package/dist/menubar/menubar.js +92 -6
- package/dist/meter-group/llms.txt +10 -3
- package/dist/meter-group/meter-group.d.ts +9 -2
- package/dist/meter-group/meter-group.js +8 -2
- package/dist/order-list/llms.txt +11 -3
- package/dist/order-list/order-list.d.ts +5 -1
- package/dist/order-list/order-list.js +46 -8
- package/dist/panel/llms.txt +9 -6
- package/dist/panel/panel.js +3 -1
- package/dist/password/llms.txt +8 -0
- package/dist/password/password.d.ts +6 -1
- package/dist/password/password.js +6 -5
- package/dist/pick-list/llms.txt +12 -3
- package/dist/pick-list/pick-list.d.ts +5 -1
- package/dist/pick-list/pick-list.js +52 -5
- package/dist/popover/llms.txt +9 -2
- package/dist/popover/popover.d.ts +9 -3
- package/dist/popover/popover.js +41 -6
- package/dist/public-api.d.ts +6 -6
- package/dist/public-api.js +1 -1
- package/dist/radio-group/llms.txt +11 -0
- package/dist/radio-group/radio-group.d.ts +9 -1
- package/dist/radio-group/radio-group.js +5 -2
- package/dist/rating/llms.txt +18 -5
- package/dist/rating/rating.d.ts +21 -3
- package/dist/rating/rating.js +87 -12
- package/dist/rich-text/llms.txt +20 -0
- package/dist/rich-text/rich-text.d.ts +7 -1
- package/dist/rich-text/rich-text.js +155 -28
- package/dist/scroll-top/llms.txt +5 -1
- package/dist/scroll-top/scroll-top.js +3 -1
- package/dist/select/llms.txt +21 -7
- package/dist/select/select.d.ts +12 -1
- package/dist/select/select.js +110 -38
- package/dist/sidenav/llms.txt +6 -0
- package/dist/skeleton/llms.txt +3 -2
- package/dist/skeleton/skeleton.d.ts +1 -1
- package/dist/skeleton/skeleton.js +2 -2
- package/dist/slider/llms.txt +14 -1
- package/dist/slider/slider.d.ts +11 -2
- package/dist/slider/slider.js +15 -2
- package/dist/speed-dial/llms.txt +6 -2
- package/dist/speed-dial/speed-dial.d.ts +1 -1
- package/dist/speed-dial/speed-dial.js +14 -3
- package/dist/spinner/llms.txt +12 -8
- package/dist/spinner/spinner.d.ts +5 -1
- package/dist/spinner/spinner.js +12 -2
- package/dist/split-button/llms.txt +11 -3
- package/dist/split-button/split-button.d.ts +3 -1
- package/dist/split-button/split-button.js +43 -3
- package/dist/splitter/llms.txt +14 -4
- package/dist/splitter/splitter.d.ts +9 -2
- package/dist/splitter/splitter.js +15 -2
- package/dist/stepper/llms.txt +8 -4
- package/dist/stepper/stepper.d.ts +3 -2
- package/dist/stepper/stepper.js +7 -2
- package/dist/switch/llms.txt +10 -0
- package/dist/switch/switch.d.ts +1 -1
- package/dist/switch/switch.js +4 -2
- package/dist/switch-group/llms.txt +11 -0
- package/dist/switch-group/switch-group.d.ts +5 -1
- package/dist/switch-group/switch-group.js +7 -2
- package/dist/tabs/llms.txt +10 -4
- package/dist/tabs/tabs.d.ts +4 -0
- package/dist/tabs/tabs.js +21 -2
- package/dist/textarea/llms.txt +10 -0
- package/dist/textarea/textarea.d.ts +3 -1
- package/dist/textarea/textarea.js +13 -8
- package/dist/toast/llms.txt +21 -7
- package/dist/toast/toast-container.d.ts +6 -1
- package/dist/toast/toast-container.js +52 -1
- package/dist/toast/toast-store.d.ts +12 -0
- package/dist/toast/toast-store.js +46 -7
- package/dist/tooltip/llms.txt +10 -3
- package/dist/tooltip/tooltip.d.ts +5 -0
- package/dist/tooltip/tooltip.js +80 -7
- package/dist/tree/llms.txt +20 -8
- package/dist/tree/tree-keys.d.ts +27 -0
- package/dist/tree/tree-keys.js +39 -0
- package/dist/tree/tree.d.ts +12 -5
- package/dist/tree/tree.js +75 -29
- package/package.json +12 -12
|
@@ -11,6 +11,9 @@ import { KptIcon } from "../icon/icon.js";
|
|
|
11
11
|
import { sanitizeHtml, sanitizeUrl } from "./sanitize.js";
|
|
12
12
|
import { canIndentItem, canOutdentItem, selectedItems, shiftItems } from "./lists.js";
|
|
13
13
|
import { CELL_SELECTED, deleteColumn, deleteRow, insertColumn, insertRow, mergeCells, regionBetween, splitCell, tableHtml, toggleHeaderColumn, toggleHeaderRow, } from "./tables.js";
|
|
14
|
+
import { useKptFieldA11y } from "../form-field/form-field-context.js";
|
|
15
|
+
import { nextRovingIndex } from "../internal/roving.js";
|
|
16
|
+
import { focusMenuEdge, handleMenuKeydown } from "../menu/menu-keys.js";
|
|
14
17
|
const ICON = {
|
|
15
18
|
copy: 'copy',
|
|
16
19
|
paste: 'clipboard',
|
|
@@ -49,9 +52,22 @@ const COMMAND = {
|
|
|
49
52
|
alignJustify: 'justifyFull',
|
|
50
53
|
clear: 'removeFormat',
|
|
51
54
|
};
|
|
55
|
+
/** Paleta kolorów z nazwami (`richText.swatches.*`) — sam kod szesnastkowy nic czytnikowi nie mówi. */
|
|
52
56
|
const SWATCHES = [
|
|
53
|
-
|
|
54
|
-
|
|
57
|
+
{ color: '#000000', name: 'black' },
|
|
58
|
+
{ color: '#434343', name: 'darkGray' },
|
|
59
|
+
{ color: '#666666', name: 'gray' },
|
|
60
|
+
{ color: '#999999', name: 'midGray' },
|
|
61
|
+
{ color: '#b7b7b7', name: 'silver' },
|
|
62
|
+
{ color: '#cccccc', name: 'lightGray' },
|
|
63
|
+
{ color: '#ffffff', name: 'white' },
|
|
64
|
+
{ color: '#e11d48', name: 'red' },
|
|
65
|
+
{ color: '#ea580c', name: 'orange' },
|
|
66
|
+
{ color: '#ca8a04', name: 'yellow' },
|
|
67
|
+
{ color: '#16a34a', name: 'green' },
|
|
68
|
+
{ color: '#0891b2', name: 'cyan' },
|
|
69
|
+
{ color: '#2563eb', name: 'blue' },
|
|
70
|
+
{ color: '#7c3aed', name: 'violet' },
|
|
55
71
|
];
|
|
56
72
|
const DEFAULT_TOOLBAR = [
|
|
57
73
|
['copy', 'paste', 'pasteText'],
|
|
@@ -86,7 +102,9 @@ function legacyState(cmd) {
|
|
|
86
102
|
* <KptRichText value={html} onValueChange={setHtml} />
|
|
87
103
|
* <KptRichText value={html} onValueChange={setHtml} toolbar={[['bold', 'italic'], ['link']]} />
|
|
88
104
|
*/
|
|
89
|
-
export function KptRichText({ value, defaultValue = '', onValueChange, toolbar = DEFAULT_TOOLBAR, placeholder = '', maxListLevel = 6, disabled = false, invalid = false, touched, onTouch, mentions = [], mentionLimit = KPT_MENTION_LIMIT, onMention, locale, dictionary, className, ...rest }) {
|
|
105
|
+
export function KptRichText({ value, defaultValue = '', onValueChange, toolbar = DEFAULT_TOOLBAR, placeholder = '', maxListLevel = 6, id, ariaLabel = null, required = false, disabled = false, invalid = false, touched, onTouch, mentions = [], mentionLimit = KPT_MENTION_LIMIT, onMention, locale, dictionary, className, ...rest }) {
|
|
106
|
+
/** `contenteditable` nie dostaje nazwy z `label[for]` — stąd `aria-labelledby` na etykietę wrappera. */
|
|
107
|
+
const field = useKptFieldA11y(id, 'kpt-rich-text');
|
|
90
108
|
const { t } = useKptMessages({ locale, dictionary });
|
|
91
109
|
const container = useKptOverlayContainer();
|
|
92
110
|
const editorRef = useRef(null);
|
|
@@ -98,7 +116,15 @@ export function KptRichText({ value, defaultValue = '', onValueChange, toolbar =
|
|
|
98
116
|
const [foreAnchor, setForeAnchor] = useState(null);
|
|
99
117
|
const [backAnchor, setBackAnchor] = useState(null);
|
|
100
118
|
const [tableAnchor, setTableAnchor] = useState(null);
|
|
119
|
+
const [linkAnchor, setLinkAnchor] = useState(null);
|
|
120
|
+
/** Okienko adresu linku (zamiast `window.prompt()`) i wpisywany adres. */
|
|
121
|
+
const [linkOpen, setLinkOpen] = useState(false);
|
|
122
|
+
const [linkDraft, setLinkDraft] = useState('');
|
|
123
|
+
/** Menu tabeli otwarte z klawiatury — po zamontowaniu fokus idzie na pierwszą pozycję. */
|
|
124
|
+
const menuFromKeyboard = useRef(false);
|
|
101
125
|
const [styleOpen, setStyleOpen] = useState(false);
|
|
126
|
+
/** Narzędzie, na którym ostatnio stał fokus paska — przystanek Tab przy powrocie. */
|
|
127
|
+
const [lastTool, setLastTool] = useState(null);
|
|
102
128
|
const [colorOpen, setColorOpen] = useState(false);
|
|
103
129
|
const [colorKind, setColorKind] = useState('foreColor');
|
|
104
130
|
const [tableOpen, setTableOpen] = useState(false);
|
|
@@ -408,15 +434,9 @@ export function KptRichText({ value, defaultValue = '', onValueChange, toolbar =
|
|
|
408
434
|
}
|
|
409
435
|
focusEditor();
|
|
410
436
|
switch (item) {
|
|
411
|
-
case 'link':
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
const safe = sanitizeUrl(url);
|
|
415
|
-
if (safe)
|
|
416
|
-
legacyExec('createLink', safe);
|
|
417
|
-
}
|
|
418
|
-
break;
|
|
419
|
-
}
|
|
437
|
+
case 'link':
|
|
438
|
+
openLink();
|
|
439
|
+
return;
|
|
420
440
|
case 'copy':
|
|
421
441
|
void copySelection();
|
|
422
442
|
return;
|
|
@@ -482,6 +502,10 @@ export function KptRichText({ value, defaultValue = '', onValueChange, toolbar =
|
|
|
482
502
|
// co innego niż w samym edytorze.
|
|
483
503
|
if (mentionOpen && onMentionKeyDown(event))
|
|
484
504
|
return;
|
|
505
|
+
if ((event.key === 'ContextMenu' || (event.shiftKey && event.key === 'F10')) && openMenuFromKeyboard()) {
|
|
506
|
+
event.preventDefault();
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
485
509
|
if (event.key !== 'Tab' || event.ctrlKey || event.altKey || event.metaKey)
|
|
486
510
|
return;
|
|
487
511
|
if (shiftItems(editorRef.current, event.shiftKey ? 'outdent' : 'indent', maxListLevel)) {
|
|
@@ -556,9 +580,67 @@ export function KptRichText({ value, defaultValue = '', onValueChange, toolbar =
|
|
|
556
580
|
pushValue();
|
|
557
581
|
};
|
|
558
582
|
const afterTableOp = () => {
|
|
583
|
+
const fromMenu = !!document.activeElement?.closest('.kpt-rt-menu');
|
|
559
584
|
setMenuOpen(false);
|
|
585
|
+
// Pozycja wybrana z klawiatury znika razem z menu — fokus wraca do edytora, nie na <body>.
|
|
586
|
+
if (fromMenu)
|
|
587
|
+
focusEditor();
|
|
560
588
|
pushValue();
|
|
561
589
|
};
|
|
590
|
+
/** Zamknięcie menu z powrotem fokusu (i kursora) do komórki, z której je otwarto. */
|
|
591
|
+
const closeMenuToEditor = () => {
|
|
592
|
+
setMenuOpen(false);
|
|
593
|
+
focusEditor();
|
|
594
|
+
restoreSelection();
|
|
595
|
+
};
|
|
596
|
+
/** Klawiatura menu kontekstowego: strzałki, `Home`/`End`; `Escape`/`Tab` zamykają i wracają do tekstu. */
|
|
597
|
+
const onMenuKeyDown = (event) => {
|
|
598
|
+
if (handleMenuKeydown(event.currentTarget, event.nativeEvent) === 'close')
|
|
599
|
+
closeMenuToEditor();
|
|
600
|
+
};
|
|
601
|
+
/** Menu tabeli z klawiatury: klawisz menu albo `Shift+F10` w komórce — jak natywne menu kontekstowe. */
|
|
602
|
+
const openMenuFromKeyboard = () => {
|
|
603
|
+
const node = window.getSelection()?.anchorNode ?? null;
|
|
604
|
+
const el = node instanceof Element ? node : (node?.parentElement ?? null);
|
|
605
|
+
const cell = el?.closest('td, th');
|
|
606
|
+
if (!cell || !editorRef.current?.contains(cell))
|
|
607
|
+
return false;
|
|
608
|
+
const rect = cell.getBoundingClientRect();
|
|
609
|
+
saveSelection();
|
|
610
|
+
ctxCell.current = cell;
|
|
611
|
+
canSplitCtx.current = (cell.colSpan || 1) > 1 || (cell.rowSpan || 1) > 1;
|
|
612
|
+
setMenuPoint({ x: rect.left, y: rect.bottom });
|
|
613
|
+
menuFromKeyboard.current = true;
|
|
614
|
+
setMenuOpen(true);
|
|
615
|
+
return true;
|
|
616
|
+
};
|
|
617
|
+
/** Okienko adresu zamiast `window.prompt()` — nazwane pole, `Enter` wstawia, `Escape` wraca do tekstu. */
|
|
618
|
+
const openLink = () => {
|
|
619
|
+
if (disabled)
|
|
620
|
+
return;
|
|
621
|
+
saveSelection();
|
|
622
|
+
const node = savedRange.current?.commonAncestorContainer ?? null;
|
|
623
|
+
const el = node instanceof Element ? node : (node?.parentElement ?? null);
|
|
624
|
+
setLinkDraft(el?.closest('a')?.getAttribute('href') ?? '');
|
|
625
|
+
setLinkOpen(true);
|
|
626
|
+
};
|
|
627
|
+
const applyLink = () => {
|
|
628
|
+
const safe = sanitizeUrl(linkDraft.trim());
|
|
629
|
+
setLinkOpen(false);
|
|
630
|
+
focusEditor();
|
|
631
|
+
restoreSelection();
|
|
632
|
+
if (safe) {
|
|
633
|
+
legacyExec('createLink', safe);
|
|
634
|
+
pushValue();
|
|
635
|
+
}
|
|
636
|
+
};
|
|
637
|
+
const closeLink = (refocus) => {
|
|
638
|
+
setLinkOpen(false);
|
|
639
|
+
if (refocus) {
|
|
640
|
+
focusEditor();
|
|
641
|
+
restoreSelection();
|
|
642
|
+
}
|
|
643
|
+
};
|
|
562
644
|
const onContextMenu = (event) => {
|
|
563
645
|
if (disabled)
|
|
564
646
|
return;
|
|
@@ -566,6 +648,7 @@ export function KptRichText({ value, defaultValue = '', onValueChange, toolbar =
|
|
|
566
648
|
if (!cell || !editorRef.current?.contains(cell))
|
|
567
649
|
return; // poza tabelą zostaje natywne menu
|
|
568
650
|
event.preventDefault();
|
|
651
|
+
saveSelection();
|
|
569
652
|
ctxCell.current = cell;
|
|
570
653
|
canSplitCtx.current = (cell.colSpan || 1) > 1 || (cell.rowSpan || 1) > 1;
|
|
571
654
|
setMenuPoint({ x: event.clientX, y: event.clientY });
|
|
@@ -655,7 +738,7 @@ export function KptRichText({ value, defaultValue = '', onValueChange, toolbar =
|
|
|
655
738
|
return canOutdent;
|
|
656
739
|
return true;
|
|
657
740
|
};
|
|
658
|
-
const palette = (onPick, customLabel) => (_jsxs("div", { className: "kpt-rich-text__palette", onMouseDown: (event) => event.preventDefault(), children: [_jsx("div", { className: "kpt-rich-text__swatches", children: SWATCHES.map((
|
|
741
|
+
const palette = (onPick, customLabel) => (_jsxs("div", { className: "kpt-rich-text__palette", onMouseDown: (event) => event.preventDefault(), children: [_jsx("div", { className: "kpt-rich-text__swatches", children: SWATCHES.map((swatch) => (_jsx("button", { type: "button", className: "kpt-rich-text__swatch", style: { background: swatch.color }, "aria-label": t(`richText.swatches.${swatch.name}`), onClick: () => onPick(swatch.color) }, swatch.color))) }), _jsxs("label", { className: "kpt-rich-text__picker", children: [_jsx("span", { children: customLabel }), _jsx("input", { type: "color", onInput: (event) => onPick(event.target.value) })] })] }));
|
|
659
742
|
/**
|
|
660
743
|
* Panel otwarty przy dolnej albo prawej krawędzi okna wychodziłby poza ekran — CDK robił to
|
|
661
744
|
* w Angularze sam, tutaj przesuwamy go po zamontowaniu, gdy już znać jego rozmiar.
|
|
@@ -683,11 +766,35 @@ export function KptRichText({ value, defaultValue = '', onValueChange, toolbar =
|
|
|
683
766
|
onClose();
|
|
684
767
|
} }), _jsx("div", { ref: clampToViewport, style: { position: 'fixed', left: menuPoint.x, top: menuPoint.y, ...layer }, children: content })] }), container);
|
|
685
768
|
};
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
769
|
+
/**
|
|
770
|
+
* Przystanek Tab paska: ostatnio użyte narzędzie, a gdy jest wyłączone albo go nie ma —
|
|
771
|
+
* pierwsze dostępne. Dzięki temu `Tab` z paska idzie od razu do edytora, a nie przez kilkanaście
|
|
772
|
+
* przycisków.
|
|
773
|
+
*/
|
|
774
|
+
const usableTools = sections.flat().filter((item) => !disabled && enabled(item));
|
|
775
|
+
const toolStop = lastTool && usableTools.includes(lastTool) ? lastTool : (usableTools[0] ?? null);
|
|
776
|
+
const rovingProps = (item) => ({ 'data-tool': item, tabIndex: item === toolStop ? 0 : -1 });
|
|
777
|
+
/** Strzałki po pasku (w RTL odwrócone), `Home`/`End` — skrajne; wyłączone narzędzia są pomijane. */
|
|
778
|
+
const onToolbarKeyDown = (event) => {
|
|
779
|
+
const bar = event.currentTarget;
|
|
780
|
+
const buttons = [...bar.querySelectorAll('[data-tool]')];
|
|
781
|
+
const current = buttons.indexOf(event.target);
|
|
782
|
+
if (current < 0)
|
|
783
|
+
return;
|
|
784
|
+
const rtl = getComputedStyle(bar).direction === 'rtl';
|
|
785
|
+
const key = rtl && event.key === 'ArrowLeft' ? 'ArrowRight' : rtl && event.key === 'ArrowRight' ? 'ArrowLeft' : event.key;
|
|
786
|
+
const next = nextRovingIndex(buttons.map((button) => button.disabled), current, key);
|
|
787
|
+
if (next === null)
|
|
788
|
+
return;
|
|
789
|
+
event.preventDefault();
|
|
790
|
+
setLastTool(buttons[next].dataset['tool']);
|
|
791
|
+
buttons[next].focus();
|
|
792
|
+
};
|
|
793
|
+
const toolButton = (item) => (_jsx("button", { ...rovingProps(item), ref: item === 'link' ? setLinkAnchor : undefined, "aria-haspopup": item === 'link' ? 'dialog' : undefined, "aria-expanded": item === 'link' ? linkOpen : undefined, type: "button", className: cn('kpt-rich-text__tool', isActive(item) && 'is-active'), "aria-label": t(`richText.${item}`), "aria-pressed": pressed(item), title: t(`richText.${item}`), disabled: disabled || !enabled(item), onMouseDown: (event) => event.preventDefault(), onClick: () => exec(item), children: _jsx(KptIcon, { name: ICON[item] }) }, item));
|
|
794
|
+
const menuItem = (icon, label, onClick, extra) => (_jsxs("button", { type: "button", role: "menuitem", tabIndex: -1, className: cn('kpt-rt-menu__item', extra), "aria-disabled": extra === 'is-disabled' || undefined, onClick: onClick, children: [_jsx(KptIcon, { name: icon }), " ", label] }));
|
|
795
|
+
return (_jsxs("div", { className: cn('kpt-rich-text-host', className), ...rest, children: [_jsxs("div", { className: cn('kpt-rich-text', showInvalid && 'kpt-rich-text--invalid', disabled && 'kpt-rich-text--disabled'), children: [_jsx("div", { className: "kpt-rich-text__toolbar", role: "toolbar", "aria-label": t('richText.toolbar'), "aria-controls": field.id, onKeyDown: onToolbarKeyDown, children: sections.map((section, index) => (_jsxs("span", { style: { display: 'contents' }, children: [index > 0 ? _jsx("span", { className: "kpt-rich-text__divider", "aria-hidden": "true" }) : null, section.map((item) => {
|
|
689
796
|
if (item === 'heading') {
|
|
690
|
-
return (_jsxs("button", { ref: setStyleAnchor, type: "button", className: cn('kpt-rich-text__style', styleOpen && 'is-open'), disabled: disabled, "aria-label": t('richText.heading'), "aria-expanded": styleOpen, title: t('richText.heading'), onMouseDown: (event) => event.preventDefault(), onClick: () => {
|
|
797
|
+
return (_jsxs("button", { ref: setStyleAnchor, ...rovingProps(item), type: "button", className: cn('kpt-rich-text__style', styleOpen && 'is-open'), disabled: disabled, "aria-label": t('richText.heading'), "aria-expanded": styleOpen, title: t('richText.heading'), onMouseDown: (event) => event.preventDefault(), onClick: () => {
|
|
691
798
|
if (disabled)
|
|
692
799
|
return;
|
|
693
800
|
saveSelection();
|
|
@@ -695,7 +802,7 @@ export function KptRichText({ value, defaultValue = '', onValueChange, toolbar =
|
|
|
695
802
|
}, children: [_jsx("span", { children: styleLabel }), _jsx(KptIcon, { name: "chevron-down", className: "kpt-rich-text__style-caret" })] }, item));
|
|
696
803
|
}
|
|
697
804
|
if (item === 'foreColor' || item === 'backColor') {
|
|
698
|
-
return (_jsx("button", { ref: item === 'foreColor' ? setForeAnchor : setBackAnchor, type: "button", className: "kpt-rich-text__tool kpt-rich-text__tool--color", "aria-label": t(`richText.${item}`), title: t(`richText.${item}`), disabled: disabled, onMouseDown: (event) => event.preventDefault(), onClick: () => {
|
|
805
|
+
return (_jsx("button", { ref: item === 'foreColor' ? setForeAnchor : setBackAnchor, ...rovingProps(item), type: "button", className: "kpt-rich-text__tool kpt-rich-text__tool--color", "aria-label": t(`richText.${item}`), title: t(`richText.${item}`), disabled: disabled, onMouseDown: (event) => event.preventDefault(), onClick: () => {
|
|
699
806
|
if (disabled)
|
|
700
807
|
return;
|
|
701
808
|
saveSelection();
|
|
@@ -704,10 +811,10 @@ export function KptRichText({ value, defaultValue = '', onValueChange, toolbar =
|
|
|
704
811
|
}, children: _jsx(KptIcon, { name: ICON[item] }) }, item));
|
|
705
812
|
}
|
|
706
813
|
if (item === 'table') {
|
|
707
|
-
return (_jsx("button", { ref: setTableAnchor, type: "button", className: cn('kpt-rich-text__tool', tableOpen && 'is-active'), "aria-label": t('richText.table'), title: t('richText.table'), disabled: disabled, onMouseDown: (event) => event.preventDefault(), onClick: () => exec(item), children: _jsx(KptIcon, { name: ICON[item] }) }, item));
|
|
814
|
+
return (_jsx("button", { ref: setTableAnchor, ...rovingProps(item), type: "button", className: cn('kpt-rich-text__tool', tableOpen && 'is-active'), "aria-label": t('richText.table'), title: t('richText.table'), disabled: disabled, onMouseDown: (event) => event.preventDefault(), onClick: () => exec(item), children: _jsx(KptIcon, { name: ICON[item] }) }, item));
|
|
708
815
|
}
|
|
709
816
|
return toolButton(item);
|
|
710
|
-
})] }, index))) }), sourceView ? (_jsx("textarea", { className: "kpt-rich-text__source", value: current, disabled: disabled, spellCheck: false, "aria-label": t('richText.htmlSource'), onChange: (event) => setCurrent(event.target.value), onBlur: () => onTouch?.() })) : (_jsx("div", { ref: editorRef, className: "kpt-rich-text__editor", contentEditable: !disabled, suppressContentEditableWarning: true, "data-placeholder": placeholder, "aria-invalid": showInvalid || undefined, role: "textbox", "aria-multiline": "true", onInput: () => {
|
|
817
|
+
})] }, index))) }), sourceView ? (_jsx("textarea", { className: "kpt-rich-text__source", value: current, disabled: disabled, spellCheck: false, "aria-label": t('richText.htmlSource'), onChange: (event) => setCurrent(event.target.value), onBlur: () => onTouch?.() })) : (_jsx("div", { ref: editorRef, className: "kpt-rich-text__editor", contentEditable: !disabled, suppressContentEditableWarning: true, "data-placeholder": placeholder, "aria-invalid": showInvalid || undefined, role: "textbox", "aria-multiline": "true", id: field.id, "aria-required": required || undefined, "aria-label": ariaLabel ?? undefined, "aria-labelledby": ariaLabel ? undefined : field.labelledBy, "aria-describedby": field.describedBy, onInput: () => {
|
|
711
818
|
pushValue();
|
|
712
819
|
syncMention();
|
|
713
820
|
}, onKeyDown: onKeyDown, onPaste: onPaste, onContextMenu: onContextMenu, onMouseDown: onEditorMouseDown, onMouseMove: onEditorMouseMove, onBlur: () => onTouch?.() }))] }), mentionOpen && container
|
|
@@ -722,7 +829,22 @@ export function KptRichText({ value, defaultValue = '', onValueChange, toolbar =
|
|
|
722
829
|
event.preventDefault();
|
|
723
830
|
insertMention(item);
|
|
724
831
|
}, onMouseEnter: () => setMentionActive(index), children: [_jsx("span", { className: "kpt-rt-mentions__label", children: item.label }), item.kind ? _jsx("span", { className: "kpt-rt-mentions__kind", children: item.kind }) : null] }, item.id))) }), container)
|
|
725
|
-
: null, _jsx(KptOverlay, { open:
|
|
832
|
+
: null, _jsx(KptOverlay, { open: linkOpen, anchor: linkAnchor, onClose: () => closeLink(false), offset: 4, children: _jsxs("form", { className: "kpt-rt-link", role: "dialog",
|
|
833
|
+
// Bez walidacji przeglądarki: „example.com” jest poprawnym wpisem — `sanitizeUrl` dopisze https://.
|
|
834
|
+
noValidate: true, "aria-label": t('richText.link'), onSubmit: (event) => {
|
|
835
|
+
event.preventDefault();
|
|
836
|
+
applyLink();
|
|
837
|
+
}, onKeyDown: (event) => {
|
|
838
|
+
if (event.key !== 'Escape')
|
|
839
|
+
return;
|
|
840
|
+
event.preventDefault();
|
|
841
|
+
event.stopPropagation();
|
|
842
|
+
closeLink(true);
|
|
843
|
+
}, children: [_jsx("label", { className: "kpt-rt-link__label", htmlFor: `${field.id}-link`, children: t('richText.linkPrompt') }), _jsx("input", { ref: (el) => {
|
|
844
|
+
// Panel jest niewidoczny do pierwszego pomiaru — fokus po klatce.
|
|
845
|
+
if (el)
|
|
846
|
+
requestAnimationFrame(() => el.focus());
|
|
847
|
+
}, className: "kpt-rt-link__input", id: `${field.id}-link`, type: "url", inputMode: "url", placeholder: "https://", value: linkDraft, onChange: (event) => setLinkDraft(event.target.value) }), _jsx("button", { type: "submit", className: "kpt-rt-link__apply", children: t('richText.linkApply') })] }) }), _jsx(KptOverlay, { open: styleOpen, anchor: styleAnchor, onClose: () => setStyleOpen(false), offset: 4, children: _jsx("ul", { className: "kpt-rich-text__style-panel", role: "listbox", children: styleOptions.map((option) => (_jsx("li", { className: cn(`kpt-rich-text__style-opt kpt-rich-text__style-opt--${option.tag}`, currentBlock === option.tag && 'is-selected'), role: "option", "aria-selected": currentBlock === option.tag, onClick: () => {
|
|
726
848
|
setStyleOpen(false);
|
|
727
849
|
focusEditor();
|
|
728
850
|
restoreSelection();
|
|
@@ -733,7 +855,12 @@ export function KptRichText({ value, defaultValue = '', onValueChange, toolbar =
|
|
|
733
855
|
setTableOpen(false);
|
|
734
856
|
setGrid({ r: 0, c: 0 });
|
|
735
857
|
}, offset: 4, children: _jsxs("div", { className: "kpt-rt-grid-pop", onMouseDown: (event) => event.preventDefault(), children: [_jsx("div", { className: "kpt-rt-grid", onMouseLeave: () => setGrid({ r: 0, c: 0 }), children: GRID_ROWS.map((r) => (_jsx("div", { className: "kpt-rt-grid__row", children: GRID_COLS.map((c) => (_jsx("span", { className: cn('kpt-rt-grid__cell', r <= grid.r && c <= grid.c && 'is-on'), onMouseEnter: () => setGrid({ r, c }), onClick: () => insertTableAt(r, c) }, c))) }, r))) }), _jsxs("div", { className: "kpt-rt-grid__label", children: [grid.r, " \u00D7 ", grid.c] })] }) }), menuOpen
|
|
736
|
-
? pointPanel(_jsxs("div", {
|
|
858
|
+
? pointPanel(_jsxs("div", { ref: (el) => {
|
|
859
|
+
if (el && menuFromKeyboard.current) {
|
|
860
|
+
menuFromKeyboard.current = false;
|
|
861
|
+
requestAnimationFrame(() => focusMenuEdge(el, 'first'));
|
|
862
|
+
}
|
|
863
|
+
}, className: "kpt-rt-menu", role: "menu", "aria-label": t('richText.table'), onContextMenu: (event) => event.preventDefault(), onKeyDown: onMenuKeyDown, children: [_jsx("div", { className: "kpt-rt-menu__group", "aria-hidden": "true", children: t('richText.menu.column') }), menuItem('plus', t('richText.menu.insertLeft'), () => {
|
|
737
864
|
if (ctxCell.current)
|
|
738
865
|
insertColumn(ctxCell.current, 'left');
|
|
739
866
|
afterTableOp();
|
|
@@ -750,7 +877,7 @@ export function KptRichText({ value, defaultValue = '', onValueChange, toolbar =
|
|
|
750
877
|
if (ctxCell.current)
|
|
751
878
|
deleteColumn(ctxCell.current);
|
|
752
879
|
afterTableOp();
|
|
753
|
-
}), _jsx("div", { className: "kpt-rt-menu__sep" }), _jsx("div", { className: "kpt-rt-menu__group", children: t('richText.menu.row') }), menuItem('plus', t('richText.menu.insertAbove'), () => {
|
|
880
|
+
}), _jsx("div", { className: "kpt-rt-menu__sep", role: "separator" }), _jsx("div", { className: "kpt-rt-menu__group", "aria-hidden": "true", children: t('richText.menu.row') }), menuItem('plus', t('richText.menu.insertAbove'), () => {
|
|
754
881
|
if (ctxCell.current)
|
|
755
882
|
insertRow(ctxCell.current, 'above');
|
|
756
883
|
afterTableOp();
|
|
@@ -767,7 +894,7 @@ export function KptRichText({ value, defaultValue = '', onValueChange, toolbar =
|
|
|
767
894
|
if (ctxCell.current)
|
|
768
895
|
deleteRow(ctxCell.current);
|
|
769
896
|
afterTableOp();
|
|
770
|
-
}), _jsx("div", { className: "kpt-rt-menu__sep" }), _jsx("div", { className: "kpt-rt-menu__group", children: t('richText.menu.cell') }), menuItem('palette', t('richText.menu.cellBackground'), () => {
|
|
897
|
+
}), _jsx("div", { className: "kpt-rt-menu__sep", role: "separator" }), _jsx("div", { className: "kpt-rt-menu__group", "aria-hidden": "true", children: t('richText.menu.cell') }), menuItem('palette', t('richText.menu.cellBackground'), () => {
|
|
771
898
|
setMenuOpen(false);
|
|
772
899
|
setColorKind('cellBg');
|
|
773
900
|
setColorOpen(true);
|
|
@@ -783,7 +910,7 @@ export function KptRichText({ value, defaultValue = '', onValueChange, toolbar =
|
|
|
783
910
|
for (const cell of activeCells())
|
|
784
911
|
cell.style.textAlign = 'right';
|
|
785
912
|
afterTableOp();
|
|
786
|
-
}), _jsx("div", { className: "kpt-rt-menu__sep" }), menuItem('table', t('richText.menu.mergeCells'), () => {
|
|
913
|
+
}), _jsx("div", { className: "kpt-rt-menu__sep", role: "separator" }), menuItem('table', t('richText.menu.mergeCells'), () => {
|
|
787
914
|
mergeCells([...selCells.current]);
|
|
788
915
|
clearCellSelection();
|
|
789
916
|
afterTableOp();
|
|
@@ -791,15 +918,15 @@ export function KptRichText({ value, defaultValue = '', onValueChange, toolbar =
|
|
|
791
918
|
if (ctxCell.current)
|
|
792
919
|
splitCell(ctxCell.current);
|
|
793
920
|
afterTableOp();
|
|
794
|
-
}, canSplitCtx.current ? undefined : 'is-disabled'), _jsx("div", { className: "kpt-rt-menu__sep" }), menuItem('table', t('richText.menu.border'), () => {
|
|
921
|
+
}, canSplitCtx.current ? undefined : 'is-disabled'), _jsx("div", { className: "kpt-rt-menu__sep", role: "separator" }), menuItem('table', t('richText.menu.border'), () => {
|
|
795
922
|
setMenuOpen(false);
|
|
796
923
|
setBorderOpen(true);
|
|
797
|
-
}), _jsx("div", { className: "kpt-rt-menu__sep" }), menuItem('trash', t('richText.menu.deleteTable'), () => {
|
|
924
|
+
}), _jsx("div", { className: "kpt-rt-menu__sep", role: "separator" }), menuItem('trash', t('richText.menu.deleteTable'), () => {
|
|
798
925
|
ctxCell.current?.closest('table')?.remove();
|
|
799
926
|
afterTableOp();
|
|
800
927
|
}, 'kpt-rt-menu__item--danger')] }), () => setMenuOpen(false))
|
|
801
928
|
: null, borderOpen
|
|
802
|
-
? pointPanel(_jsxs("div", { className: "kpt-rt-border", onMouseDown: (event) => event.preventDefault(), children: [_jsxs("div", { className: "kpt-rt-border__seg", children: [_jsx("button", { type: "button", className: cn(borderTarget === 'table' && 'is-active'), onClick: () => setBorderTarget('table'), children: t('richText.border.table') }), _jsx("button", { type: "button", className: cn(borderTarget === 'cell' && 'is-active'), onClick: () => setBorderTarget('cell'), children: t('richText.border.cell') })] }), _jsxs("div", { className: "kpt-rt-border__widths", children: [_jsx("button", { type: "button", onClick: () => setBorderWidth('none'), children: t('richText.border.none') }), _jsx("button", { type: "button", onClick: () => setBorderWidth('thin'), children: t('richText.border.thin') }), _jsx("button", { type: "button", onClick: () => setBorderWidth('medium'), children: t('richText.border.medium') }), _jsx("button", { type: "button", onClick: () => setBorderWidth('thick'), children: t('richText.border.thick') })] }), _jsx("div", { className: "kpt-rich-text__swatches", children: SWATCHES.map((
|
|
929
|
+
? pointPanel(_jsxs("div", { className: "kpt-rt-border", onMouseDown: (event) => event.preventDefault(), children: [_jsxs("div", { className: "kpt-rt-border__seg", children: [_jsx("button", { type: "button", className: cn(borderTarget === 'table' && 'is-active'), onClick: () => setBorderTarget('table'), children: t('richText.border.table') }), _jsx("button", { type: "button", className: cn(borderTarget === 'cell' && 'is-active'), onClick: () => setBorderTarget('cell'), children: t('richText.border.cell') })] }), _jsxs("div", { className: "kpt-rt-border__widths", children: [_jsx("button", { type: "button", onClick: () => setBorderWidth('none'), children: t('richText.border.none') }), _jsx("button", { type: "button", onClick: () => setBorderWidth('thin'), children: t('richText.border.thin') }), _jsx("button", { type: "button", onClick: () => setBorderWidth('medium'), children: t('richText.border.medium') }), _jsx("button", { type: "button", onClick: () => setBorderWidth('thick'), children: t('richText.border.thick') })] }), _jsx("div", { className: "kpt-rich-text__swatches", children: SWATCHES.map((swatch) => (_jsx("button", { type: "button", className: "kpt-rich-text__swatch", style: { background: swatch.color }, "aria-label": t(`richText.swatches.${swatch.name}`), onClick: () => setBorderColor(swatch.color) }, swatch.color))) }), _jsxs("label", { className: "kpt-rich-text__picker", children: [_jsx("span", { children: t('richText.border.customColor') }), _jsx("input", { type: "color", onInput: (event) => setBorderColor(event.target.value) })] })] }), () => setBorderOpen(false))
|
|
803
930
|
: null, colorOpen && colorKind === 'cellBg'
|
|
804
931
|
? pointPanel(palette(applyColor, t('richText.paletteCustom')), () => setColorOpen(false))
|
|
805
932
|
: null] }));
|
package/dist/scroll-top/llms.txt
CHANGED
|
@@ -38,4 +38,8 @@ The label comes from `scrollTop.label`.
|
|
|
38
38
|
`--kpt-color-primary` / `--kpt-color-on-primary`, `--kpt-elevation-3`.
|
|
39
39
|
|
|
40
40
|
## Accessibility
|
|
41
|
-
A real `<button>`
|
|
41
|
+
A real `<button>` labelled from `scrollTop.label`; while hidden it is `visibility: hidden`, so it is
|
|
42
|
+
out of the tab order and the accessibility tree. It sits in the bottom-end corner
|
|
43
|
+
(`inset-inline-end`), so it moves to the left in RTL. With `prefers-reduced-motion` the scroll is a
|
|
44
|
+
jump instead of smooth and the button appears without a transition. Focus stays on the button after
|
|
45
|
+
the jump — move it to your page heading if that matters.
|
|
@@ -29,7 +29,9 @@ export function KptScrollTop({ threshold = 300, target = null, locale, dictionar
|
|
|
29
29
|
const scroller = target
|
|
30
30
|
? (document.querySelector(target) ?? window)
|
|
31
31
|
: window;
|
|
32
|
-
|
|
32
|
+
// Przy `prefers-reduced-motion` skok zamiast płynnego przewijania.
|
|
33
|
+
const behavior = matchMedia('(prefers-reduced-motion: reduce)').matches ? 'auto' : 'smooth';
|
|
34
|
+
scroller.scrollTo({ top: 0, behavior });
|
|
33
35
|
};
|
|
34
36
|
return (_jsx("div", { className: cn('kpt-scroll-top-host', className), ...rest, children: _jsx("button", { type: "button", className: cn('kpt-scroll-top', visible && 'is-visible'),
|
|
35
37
|
// Ukryty przycisk zostaje w DOM (animacja pojawiania), więc z kolejności Tab wyjmuje go
|
package/dist/select/llms.txt
CHANGED
|
@@ -18,7 +18,8 @@ A select on its own panel. Variants: single, with a search box (`filter`) and mu
|
|
|
18
18
|
- `value` / `defaultValue` / `onValueChange` — a single `T`, or `T[]` in `multiple` mode
|
|
19
19
|
- `open` / `defaultOpen` / `onOpenChange` — the panel state, so the list can be opened from outside
|
|
20
20
|
- `placeholder`, `multiple`, `filter`, `disabled`, `invalid`, `touched`
|
|
21
|
-
- `onTouch()` — fires when the panel closes or the trigger
|
|
21
|
+
- `onTouch()` — fires when the panel closes or focus leaves the closed trigger
|
|
22
|
+
- `aria-label` (through the rest props) names the trigger when no `<label for>` describes it
|
|
22
23
|
- `locale`, `dictionary` — i18n overrides for this component only
|
|
23
24
|
- `className`, `ref` and every other `<div>` prop go to the trigger
|
|
24
25
|
|
|
@@ -29,9 +30,14 @@ flips above when there is no room, a transparent backdrop that closes on click,
|
|
|
29
30
|
follows scrolling and resizing, so the panel never drifts away from its trigger.
|
|
30
31
|
|
|
31
32
|
## Keyboard
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
The select-only combobox pattern: focus stays on the trigger and the active option is announced
|
|
34
|
+
through `aria-activedescendant` (with `filter`, focus moves to the search field). Closed: ArrowDown,
|
|
35
|
+
ArrowUp, Enter, Space, Home and End open the list, typing letters opens it on the first match, and
|
|
36
|
+
in `multiple` mode Backspace/Delete removes the last chip. Open: ArrowUp/ArrowDown move by one,
|
|
37
|
+
Home/End to the ends, PageUp/PageDown by ten, skipping disabled options without wrapping; Enter (and
|
|
38
|
+
Space on the trigger) picks — a single select closes and returns focus to the trigger; typing jumps
|
|
39
|
+
to the next match, diacritics ignored; Escape and Tab close. Hovering an option makes it active, so
|
|
40
|
+
the mouse and the keyboard share one highlight, and clicking it does not take focus from the trigger.
|
|
35
41
|
|
|
36
42
|
## Inside KptFormField
|
|
37
43
|
There is no native field for the wrapper to read, so the select reports its filled state with
|
|
@@ -46,11 +52,19 @@ There is no native field for the wrapper to read, so the select reports its fill
|
|
|
46
52
|
<KptSelect options={roles} value={role} onValueChange={setRole} filter />
|
|
47
53
|
<KptSelect options={tags} value={tags} onValueChange={setTags} multiple filter />
|
|
48
54
|
|
|
55
|
+
## Label and description
|
|
56
|
+
`id` goes to the focusable element, not the host; without one it comes from `KptFormField` or is
|
|
57
|
+
generated. A `div` with a role is not named by `label[for]`, so inside `KptFormField` it gets
|
|
58
|
+
`aria-labelledby` pointing at the wrapper's label (unless `aria-label` is set), and the hint or
|
|
59
|
+
error is added to `aria-describedby`. New: `id` and `required` (→ `aria-required`); the list gets
|
|
60
|
+
the same name as the trigger.
|
|
61
|
+
|
|
49
62
|
## Tokens
|
|
50
63
|
`--kpt-form-field-{bg,text,placeholder,border,border-hover,border-focus,border-error,radius}`,
|
|
51
64
|
`--kpt-color-surface-raised` and `--kpt-elevation-3` for the panel, `--kpt-z-overlay` for the layer.
|
|
52
65
|
|
|
53
66
|
## Accessibility
|
|
54
|
-
The trigger is `role="combobox"` with `aria-
|
|
55
|
-
`role="listbox"` with `aria-multiselectable`, and each
|
|
56
|
-
`aria-selected`.
|
|
67
|
+
The trigger is `role="combobox"` with `aria-haspopup="listbox"`, `aria-expanded`, `aria-controls`
|
|
68
|
+
and `aria-activedescendant`; the list is `role="listbox"` with `aria-multiselectable`, and each
|
|
69
|
+
option is `role="option"` with `aria-selected` and `aria-disabled`. Chip remove buttons sit outside
|
|
70
|
+
the Tab order and are named with the option label.
|
package/dist/select/select.d.ts
CHANGED
|
@@ -8,6 +8,10 @@ export interface KptSelectProps<T> extends Omit<ComponentPropsWithRef<'div'>, 'c
|
|
|
8
8
|
multiple?: boolean;
|
|
9
9
|
/** Pole wyszukiwania nad listą. */
|
|
10
10
|
filter?: boolean;
|
|
11
|
+
/** Id wyzwalacza; bez niego generowane, w `KptFormField` — z wrappera. */
|
|
12
|
+
id?: string;
|
|
13
|
+
/** Oznacza pole jako wymagane atrybutem `aria-required`. */
|
|
14
|
+
required?: boolean;
|
|
11
15
|
disabled?: boolean;
|
|
12
16
|
invalid?: boolean;
|
|
13
17
|
touched?: boolean;
|
|
@@ -26,6 +30,13 @@ export interface KptSelectProps<T> extends Omit<ComponentPropsWithRef<'div'>, 'c
|
|
|
26
30
|
* Lista wyboru (select) na własnym panelu. Warianty: pojedynczy, z wyszukiwarką (`filter`)
|
|
27
31
|
* i wielokrotny (`multiple`, chipy).
|
|
28
32
|
*
|
|
33
|
+
* Klawiatura według wzorca WAI-ARIA „select-only combobox”: fokus zostaje na polu, a aktywna opcja
|
|
34
|
+
* idzie przez `aria-activedescendant`. `↓`/`↑`/`Enter`/`Spacja` otwierają listę na wybranej opcji,
|
|
35
|
+
* strzałki, `Home`/`End`, `PageUp`/`PageDown` chodzą po opcjach (z pominięciem wyłączonych),
|
|
36
|
+
* wpisywane litery skaczą do pasującej opcji (bez polskich znaków), `Enter` wybiera, `Escape`
|
|
37
|
+
* zamyka. Z `filter` fokus przechodzi do pola wyszukiwania, które obsługuje te same klawisze.
|
|
38
|
+
* W trybie `multiple` `Backspace` na zamkniętym polu usuwa ostatni wybór.
|
|
39
|
+
*
|
|
29
40
|
* Wewnątrz `KptFormField` zgłasza wypełnienie hookiem `useKptFormFieldFilled`, więc etykieta
|
|
30
41
|
* Float Label unosi się nad wybraną wartością — natywnego pola, z którego wrapper mógłby to
|
|
31
42
|
* odczytać, tu nie ma.
|
|
@@ -34,4 +45,4 @@ export interface KptSelectProps<T> extends Omit<ComponentPropsWithRef<'div'>, 'c
|
|
|
34
45
|
* <KptSelect options={roles} value={role} onValueChange={setRole} placeholder="Wybierz…" />
|
|
35
46
|
* <KptSelect options={tags} multiple filter value={tags} onValueChange={setTags} />
|
|
36
47
|
*/
|
|
37
|
-
export declare function KptSelect<T>({ options, placeholder, multiple, filter, disabled, invalid, touched, value, defaultValue, onValueChange, open, defaultOpen, onOpenChange, onTouch, locale, dictionary, className, onBlur, ...rest }: Readonly<KptSelectProps<T>>): import("react").JSX.Element;
|
|
48
|
+
export declare function KptSelect<T>({ options, placeholder, multiple, filter, disabled, invalid, touched, value, defaultValue, onValueChange, open, defaultOpen, onOpenChange, onTouch, locale, dictionary, className, onBlur, id, required, 'aria-label': ariaLabel, 'aria-describedby': ariaDescribedBy, ...rest }: Readonly<KptSelectProps<T>>): import("react").JSX.Element;
|