@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.
Files changed (203) hide show
  1. package/README.md +8 -2
  2. package/dist/accordion/accordion.js +11 -10
  3. package/dist/alert/alert.js +1 -0
  4. package/dist/alert/llms.txt +1 -0
  5. package/dist/app-shell/app-shell.js +12 -5
  6. package/dist/app-shell/llms.txt +5 -4
  7. package/dist/auth/auth.js +17 -8
  8. package/dist/auth/llms.txt +3 -2
  9. package/dist/autocomplete/autocomplete.d.ts +1 -1
  10. package/dist/autocomplete/autocomplete.js +12 -4
  11. package/dist/autocomplete/llms.txt +3 -0
  12. package/dist/avatar/avatar.js +1 -0
  13. package/dist/avatar-group/avatar-group.js +1 -0
  14. package/dist/backdrop/backdrop.d.ts +0 -19
  15. package/dist/backdrop/backdrop.js +18 -13
  16. package/dist/bottom-sheet/bottom-sheet.d.ts +6 -1
  17. package/dist/bottom-sheet/bottom-sheet.js +3 -2
  18. package/dist/bottom-sheet/llms.txt +3 -0
  19. package/dist/breadcrumb/breadcrumb.d.ts +7 -1
  20. package/dist/breadcrumb/breadcrumb.js +10 -2
  21. package/dist/breadcrumb/llms.txt +8 -0
  22. package/dist/button/button.js +2 -1
  23. package/dist/button-group/button-group.d.ts +15 -2
  24. package/dist/button-group/button-group.js +31 -4
  25. package/dist/button-group/llms.txt +9 -6
  26. package/dist/card/card.js +4 -3
  27. package/dist/carousel/carousel.js +4 -1
  28. package/dist/charts/chart-legend.js +1 -0
  29. package/dist/charts/chart.d.ts +3 -2
  30. package/dist/charts/chart.js +12 -4
  31. package/dist/charts/llms.txt +6 -3
  32. package/dist/chat/chat.js +39 -9
  33. package/dist/chat/llms.txt +9 -0
  34. package/dist/chat-composer/chat-composer.d.ts +5 -2
  35. package/dist/chat-composer/chat-composer.js +10 -1
  36. package/dist/chat-dock/chat-dock.js +1 -0
  37. package/dist/chat-message/chat-message.js +2 -1
  38. package/dist/chat-typing/chat-typing.js +1 -0
  39. package/dist/checkbox/checkbox.d.ts +1 -1
  40. package/dist/checkbox/checkbox.js +9 -2
  41. package/dist/checkbox/llms.txt +3 -0
  42. package/dist/chip/chip.js +1 -0
  43. package/dist/chips-input/chips-input.d.ts +3 -1
  44. package/dist/chips-input/chips-input.js +14 -5
  45. package/dist/chips-input/llms.txt +3 -0
  46. package/dist/clock/clock.d.ts +9 -4
  47. package/dist/clock/clock.js +35 -5
  48. package/dist/clock/llms.txt +6 -2
  49. package/dist/color-picker/color-picker.d.ts +5 -1
  50. package/dist/color-picker/color-picker.js +36 -17
  51. package/dist/color-picker/llms.txt +3 -0
  52. package/dist/confirm/confirm-container.js +1 -0
  53. package/dist/confirm/confirm-store.js +6 -0
  54. package/dist/confirm/llms.txt +2 -1
  55. package/dist/context-menu/context-menu.js +35 -12
  56. package/dist/context-menu/llms.txt +3 -0
  57. package/dist/data-table/data-table.d.ts +12 -1
  58. package/dist/data-table/data-table.js +113 -51
  59. package/dist/data-table/export-csv.d.ts +2 -2
  60. package/dist/data-table/export-csv.js +7 -9
  61. package/dist/data-table/llms.txt +19 -4
  62. package/dist/data-view/data-view.js +1 -0
  63. package/dist/date-range/date-range.d.ts +16 -4
  64. package/dist/date-range/date-range.js +69 -46
  65. package/dist/date-range/llms.txt +17 -3
  66. package/dist/datepicker/datepicker.d.ts +17 -3
  67. package/dist/datepicker/datepicker.js +86 -54
  68. package/dist/datepicker/llms.txt +17 -3
  69. package/dist/dialog/dialog.js +29 -8
  70. package/dist/dialog/llms.txt +3 -0
  71. package/dist/divider/divider.d.ts +7 -1
  72. package/dist/divider/divider.js +3 -2
  73. package/dist/divider/llms.txt +1 -0
  74. package/dist/drawer/drawer.d.ts +6 -1
  75. package/dist/drawer/drawer.js +3 -2
  76. package/dist/drawer/llms.txt +3 -0
  77. package/dist/fieldset/fieldset.js +4 -1
  78. package/dist/file-upload/file-upload.d.ts +7 -1
  79. package/dist/file-upload/file-upload.js +18 -7
  80. package/dist/file-upload/llms.txt +6 -3
  81. package/dist/form-field/form-field.js +1 -0
  82. package/dist/galleria/galleria.js +4 -1
  83. package/dist/i18n/i18n-context.d.ts +3 -3
  84. package/dist/i18n/i18n-context.js +25 -3
  85. package/dist/icon/icon.js +4 -1
  86. package/dist/image/image.js +4 -4
  87. package/dist/input/input.js +1 -0
  88. package/dist/input-mask/input-mask.d.ts +7 -2
  89. package/dist/input-mask/input-mask.js +15 -41
  90. package/dist/input-mask/llms.txt +3 -0
  91. package/dist/input-number/input-number.d.ts +3 -1
  92. package/dist/input-number/input-number.js +15 -11
  93. package/dist/input-number/llms.txt +5 -2
  94. package/dist/input-otp/input-otp.d.ts +16 -3
  95. package/dist/input-otp/input-otp.js +43 -19
  96. package/dist/input-otp/llms.txt +11 -0
  97. package/dist/internal/mask.d.ts +14 -0
  98. package/dist/internal/mask.js +62 -0
  99. package/dist/internal/number-group.d.ts +22 -0
  100. package/dist/internal/number-group.js +53 -0
  101. package/dist/internal/option-key.d.ts +6 -0
  102. package/dist/internal/option-key.js +8 -0
  103. package/dist/internal/overlay.js +37 -7
  104. package/dist/internal/password-strength.d.ts +6 -0
  105. package/dist/internal/password-strength.js +74 -0
  106. package/dist/internal/scroll-lock.d.ts +6 -0
  107. package/dist/internal/scroll-lock.js +35 -0
  108. package/dist/internal/slot-children.d.ts +13 -0
  109. package/dist/internal/slot-children.js +31 -0
  110. package/dist/internal/step.d.ts +12 -0
  111. package/dist/internal/step.js +31 -0
  112. package/dist/internal/tween.js +2 -3
  113. package/dist/internal/use-controllable-state.d.ts +5 -0
  114. package/dist/internal/use-controllable-state.js +25 -4
  115. package/dist/internal/use-modal-focus.d.ts +1 -0
  116. package/dist/internal/use-modal-focus.js +12 -1
  117. package/dist/knob/knob.d.ts +5 -2
  118. package/dist/knob/knob.js +15 -14
  119. package/dist/knob/llms.txt +4 -2
  120. package/dist/listbox/listbox.d.ts +3 -1
  121. package/dist/listbox/listbox.js +30 -10
  122. package/dist/listbox/llms.txt +3 -0
  123. package/dist/map/llms.txt +5 -1
  124. package/dist/map/map-create-dialog.js +30 -22
  125. package/dist/map/map-hours-field.js +1 -0
  126. package/dist/map/map-image-field.js +2 -1
  127. package/dist/map/map-legend.js +1 -0
  128. package/dist/map/map-poi-card.js +2 -1
  129. package/dist/map/map-search.js +54 -30
  130. package/dist/map/map.d.ts +6 -1
  131. package/dist/map/map.js +62 -34
  132. package/dist/megamenu/llms.txt +11 -0
  133. package/dist/megamenu/megamenu.d.ts +7 -1
  134. package/dist/megamenu/megamenu.js +15 -6
  135. package/dist/menu/menu-rows.js +1 -0
  136. package/dist/menu/menu.js +10 -3
  137. package/dist/menubar/llms.txt +11 -0
  138. package/dist/menubar/menubar.d.ts +7 -1
  139. package/dist/menubar/menubar.js +15 -3
  140. package/dist/meter-group/meter-group.js +1 -0
  141. package/dist/order-list/llms.txt +3 -0
  142. package/dist/order-list/order-list.js +21 -6
  143. package/dist/paginator/paginator.js +7 -4
  144. package/dist/panel/panel.js +5 -3
  145. package/dist/password/llms.txt +6 -2
  146. package/dist/password/password.js +4 -14
  147. package/dist/peek/llms.txt +1 -1
  148. package/dist/peek/peek.js +37 -15
  149. package/dist/pick-list/llms.txt +3 -0
  150. package/dist/pick-list/pick-list.js +19 -3
  151. package/dist/popover/llms.txt +3 -0
  152. package/dist/popover/popover.d.ts +3 -1
  153. package/dist/popover/popover.js +10 -3
  154. package/dist/progress/progress.js +1 -0
  155. package/dist/public-api.d.ts +1 -1
  156. package/dist/radio-group/llms.txt +3 -0
  157. package/dist/radio-group/radio-group.d.ts +3 -1
  158. package/dist/radio-group/radio-group.js +13 -2
  159. package/dist/rating/rating.js +1 -0
  160. package/dist/rich-text/llms.txt +14 -0
  161. package/dist/rich-text/rich-text.d.ts +3 -9
  162. package/dist/rich-text/rich-text.js +241 -96
  163. package/dist/rich-text/sanitize.d.ts +9 -0
  164. package/dist/rich-text/sanitize.js +77 -29
  165. package/dist/roadmap/llms.txt +9 -3
  166. package/dist/roadmap/roadmap.js +88 -11
  167. package/dist/scroll-top/scroll-top.js +1 -0
  168. package/dist/select/llms.txt +3 -0
  169. package/dist/select/select.d.ts +3 -1
  170. package/dist/select/select.js +46 -32
  171. package/dist/shared/types.d.ts +21 -0
  172. package/dist/shared/types.js +9 -1
  173. package/dist/sidenav/sidenav.d.ts +1 -2
  174. package/dist/sidenav/sidenav.js +4 -3
  175. package/dist/slider/llms.txt +3 -0
  176. package/dist/slider/slider.d.ts +3 -1
  177. package/dist/slider/slider.js +15 -4
  178. package/dist/speed-dial/speed-dial.js +2 -1
  179. package/dist/spinner/spinner.js +1 -0
  180. package/dist/split-button/split-button.js +10 -3
  181. package/dist/splitter/splitter.js +23 -8
  182. package/dist/stepper/llms.txt +3 -0
  183. package/dist/stepper/stepper.js +9 -5
  184. package/dist/switch/llms.txt +3 -0
  185. package/dist/switch/switch.d.ts +1 -1
  186. package/dist/switch/switch.js +9 -2
  187. package/dist/switch-group/llms.txt +3 -0
  188. package/dist/switch-group/switch-group.d.ts +3 -1
  189. package/dist/switch-group/switch-group.js +5 -3
  190. package/dist/tabs/llms.txt +3 -0
  191. package/dist/tabs/tabs.js +30 -14
  192. package/dist/textarea/textarea.js +1 -0
  193. package/dist/toast/llms.txt +3 -0
  194. package/dist/toast/toast-container.js +1 -0
  195. package/dist/toast/toast-store.d.ts +5 -1
  196. package/dist/toast/toast-store.js +1 -1
  197. package/dist/tooltip/llms.txt +5 -3
  198. package/dist/tooltip/tooltip.d.ts +3 -2
  199. package/dist/tooltip/tooltip.js +148 -55
  200. package/dist/tree/llms.txt +1 -0
  201. package/dist/tree/tree.d.ts +6 -1
  202. package/dist/tree/tree.js +14 -7
  203. package/package.json +15 -14
@@ -1,3 +1,4 @@
1
+ 'use client';
1
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
3
  import { useCallback, useEffect, useRef, useState, } from 'react';
3
4
  import { createPortal } from 'react-dom';
@@ -8,11 +9,12 @@ import { useKptOverlayContainer } from "../internal/overlay-container.js";
8
9
  import { KptOverlay } from "../internal/overlay.js";
9
10
  import { useKptMessages } from "../i18n/i18n-context.js";
10
11
  import { KptIcon } from "../icon/icon.js";
11
- import { sanitizeHtml, sanitizeUrl } from "./sanitize.js";
12
+ import { sanitizeHtml, sanitizeHtmlReport, sanitizeUrl } from "./sanitize.js";
12
13
  import { canIndentItem, canOutdentItem, selectedItems, shiftItems } from "./lists.js";
13
14
  import { CELL_SELECTED, deleteColumn, deleteRow, insertColumn, insertRow, mergeCells, regionBetween, splitCell, tableHtml, toggleHeaderColumn, toggleHeaderRow, } from "./tables.js";
14
15
  import { useKptFieldA11y } from "../form-field/form-field-context.js";
15
16
  import { nextRovingIndex } from "../internal/roving.js";
17
+ import { trapTab } from "../internal/calendar-focus.js";
16
18
  import { focusMenuEdge, handleMenuKeydown } from "../menu/menu-keys.js";
17
19
  const ICON = {
18
20
  copy: 'copy',
@@ -84,25 +86,21 @@ const GRID_COLS = Array.from({ length: 8 }, (_, i) => i + 1);
84
86
  * (Ctrl+Z). Własne komendy wymagałyby własnej historii, czyli przepisania edytora.
85
87
  */
86
88
  function legacyExec(cmd, value) {
89
+ // eslint-disable-next-line @typescript-eslint/no-deprecated -- brak zamiennika, patrz JSDoc
87
90
  return document.execCommand(cmd, false, value); // NOSONAR: brak zamiennika, patrz JSDoc
88
91
  }
89
92
  function legacyState(cmd) {
90
93
  try {
94
+ // eslint-disable-next-line @typescript-eslint/no-deprecated -- brak zamiennika, patrz `legacyExec`
91
95
  return document.queryCommandState(cmd); // NOSONAR: brak zamiennika
92
96
  }
93
97
  catch {
94
98
  return false;
95
99
  }
96
100
  }
97
- /**
98
- * Lekki edytor tekstu sformatowanego na `contenteditable`, z konfigurowalnym paskiem sekcji
99
- * i edycją tabel. Wartością jest HTML.
100
- *
101
- * @example
102
- * <KptRichText value={html} onValueChange={setHtml} />
103
- * <KptRichText value={html} onValueChange={setHtml} toolbar={[['bold', 'italic'], ['link']]} />
104
- */
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 }) {
101
+ export function KptRichText({ value, defaultValue = '', onValueChange, toolbar = DEFAULT_TOOLBAR, placeholder = '', maxListLevel = 6, id, ariaLabel = null, required = false, disabled: disabledProp = false, readOnly = false, invalid = false, touched, onTouch, mentions = [], mentionLimit = KPT_MENTION_LIMIT, onMention, locale, dictionary, className, ...rest }) {
102
+ /** Edycja zablokowana: wyłączone pole albo tylko do odczytu — narzędzia i skróty nie działają. */
103
+ const disabled = disabledProp || readOnly;
106
104
  /** `contenteditable` nie dostaje nazwy z `label[for]` — stąd `aria-labelledby` na etykietę wrappera. */
107
105
  const field = useKptFieldA11y(id, 'kpt-rich-text');
108
106
  const { t } = useKptMessages({ locale, dictionary });
@@ -128,6 +126,8 @@ export function KptRichText({ value, defaultValue = '', onValueChange, toolbar =
128
126
  const [colorOpen, setColorOpen] = useState(false);
129
127
  const [colorKind, setColorKind] = useState('foreColor');
130
128
  const [tableOpen, setTableOpen] = useState(false);
129
+ /** Aktywna opcja listy stylów (aria-activedescendant). */
130
+ const [styleActive, setStyleActive] = useState(0);
131
131
  const [grid, setGrid] = useState({ r: 0, c: 0 });
132
132
  const [sourceView, setSourceView] = useState(false);
133
133
  const [menuOpen, setMenuOpen] = useState(false);
@@ -148,7 +148,7 @@ export function KptRichText({ value, defaultValue = '', onValueChange, toolbar =
148
148
  const [currentBlock, setCurrentBlock] = useState('p');
149
149
  const [selCount, setSelCount] = useState(0);
150
150
  const ctxCell = useRef(null);
151
- const canSplitCtx = useRef(false);
151
+ const [canSplitCtx, setCanSplitCtx] = useState(false);
152
152
  const selCells = useRef(new Set());
153
153
  const selAnchor = useRef(null);
154
154
  const selecting = useRef(false);
@@ -167,13 +167,19 @@ export function KptRichText({ value, defaultValue = '', onValueChange, toolbar =
167
167
  * Zapis z zewnątrz wstawiamy ręcznie, bo `contenteditable` nie może być renderowany przez
168
168
  * Reacta — przerysowanie kasowałoby zaznaczenie i pozycję kursora przy każdym znaku.
169
169
  * Porównanie z surowym `innerHTML` sprawia, że pisanie w edytorze nie wchodzi w tę gałąź,
170
- * więc sanityzacja włącza się wyłącznie przy zapisie z aplikacji albo z trybu HTML.
170
+ * więc sanityzacja włącza się wyłącznie przy zapisie z aplikacji albo z trybu HTML. Gdy
171
+ * sanityzator coś usunął, oczyszczona treść wraca też do wartości — inaczej aplikacja dostawałaby
172
+ * z powrotem niebezpieczny HTML. Sama różnica zapisu (cudzysłowy) wartości nie rusza.
171
173
  */
172
174
  useEffect(() => {
173
175
  const el = editorRef.current;
174
- if (el && el.innerHTML !== current)
175
- el.innerHTML = sanitizeHtml(current);
176
- }, [current, sourceView]);
176
+ if (!el || el.innerHTML === current)
177
+ return;
178
+ const clean = sanitizeHtmlReport(current);
179
+ el.innerHTML = clean.html;
180
+ if (clean.changed)
181
+ setCurrent(clean.html);
182
+ }, [current, setCurrent, sourceView]);
177
183
  const inEditor = useCallback(() => {
178
184
  const el = editorRef.current;
179
185
  const selection = window.getSelection();
@@ -325,7 +331,7 @@ export function KptRichText({ value, defaultValue = '', onValueChange, toolbar =
325
331
  if (!(target instanceof Element))
326
332
  return;
327
333
  const focus = target.closest('td, th');
328
- if (!focus || focus.closest('table') !== selAnchor.current.closest('table'))
334
+ if (focus?.closest('table') !== selAnchor.current.closest('table'))
329
335
  return;
330
336
  if (focus === selAnchor.current && !selecting.current)
331
337
  return;
@@ -571,6 +577,39 @@ export function KptRichText({ value, defaultValue = '', onValueChange, toolbar =
571
577
  pushValue();
572
578
  };
573
579
  // ── Tabele ──
580
+ const pickStyle = (tag) => {
581
+ setStyleOpen(false);
582
+ focusEditor();
583
+ restoreSelection();
584
+ legacyExec('formatBlock', `<${tag}>`);
585
+ pushValue();
586
+ refreshState();
587
+ };
588
+ /** Siatka tabeli z klawiatury: strzałki zmieniają rozmiar (1–8), Enter wstawia, Escape wraca. */
589
+ const onGridKeyDown = (event) => {
590
+ const clamp = (n) => Math.min(GRID_ROWS.length, Math.max(1, n));
591
+ const r = grid.r || 1;
592
+ const c = grid.c || 1;
593
+ if (event.key === 'ArrowDown')
594
+ setGrid({ r: clamp(r + 1), c });
595
+ else if (event.key === 'ArrowUp')
596
+ setGrid({ r: clamp(r - 1), c });
597
+ else if (event.key === 'ArrowRight')
598
+ setGrid({ r, c: clamp(c + 1) });
599
+ else if (event.key === 'ArrowLeft')
600
+ setGrid({ r, c: clamp(c - 1) });
601
+ else if (event.key === 'Enter' || event.key === ' ')
602
+ insertTableAt(r, c);
603
+ else if (event.key === 'Escape') {
604
+ setTableOpen(false);
605
+ setGrid({ r: 0, c: 0 });
606
+ tableAnchor?.focus();
607
+ }
608
+ else
609
+ return;
610
+ event.preventDefault();
611
+ event.stopPropagation();
612
+ };
574
613
  const insertTableAt = (rows, cols) => {
575
614
  setTableOpen(false);
576
615
  setGrid({ r: 0, c: 0 });
@@ -608,7 +647,7 @@ export function KptRichText({ value, defaultValue = '', onValueChange, toolbar =
608
647
  const rect = cell.getBoundingClientRect();
609
648
  saveSelection();
610
649
  ctxCell.current = cell;
611
- canSplitCtx.current = (cell.colSpan || 1) > 1 || (cell.rowSpan || 1) > 1;
650
+ setCanSplitCtx((cell.colSpan || 1) > 1 || (cell.rowSpan || 1) > 1);
612
651
  setMenuPoint({ x: rect.left, y: rect.bottom });
613
652
  menuFromKeyboard.current = true;
614
653
  setMenuOpen(true);
@@ -650,7 +689,7 @@ export function KptRichText({ value, defaultValue = '', onValueChange, toolbar =
650
689
  event.preventDefault();
651
690
  saveSelection();
652
691
  ctxCell.current = cell;
653
- canSplitCtx.current = (cell.colSpan || 1) > 1 || (cell.rowSpan || 1) > 1;
692
+ setCanSplitCtx((cell.colSpan || 1) > 1 || (cell.rowSpan || 1) > 1);
654
693
  setMenuPoint({ x: event.clientX, y: event.clientY });
655
694
  setMenuOpen(true);
656
695
  };
@@ -738,7 +777,54 @@ export function KptRichText({ value, defaultValue = '', onValueChange, toolbar =
738
777
  return canOutdent;
739
778
  return true;
740
779
  };
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) })] })] }));
780
+ /**
781
+ * Panele narzędzi żyją w nakładce na końcu `<body>` — po zamontowaniu fokus wchodzi do środka,
782
+ * inaczej Tab musiałby przejść przez całą stronę, żeby do nich dojść (WCAG 2.1.1).
783
+ */
784
+ // Refy stabilne (`useCallback`): nowa funkcja przy każdym renderze byłaby wołana od nowa
785
+ // i przy każdej zmianie stanu edytora zabierałaby fokus z powrotem na początek panelu.
786
+ const focusPanelOnMount = useCallback((el) => {
787
+ if (el)
788
+ requestAnimationFrame(() => el.focus());
789
+ }, []);
790
+ const focusSwatchOnMount = useCallback((el) => {
791
+ if (el)
792
+ requestAnimationFrame(() => el.querySelector('.kpt-rich-text__swatch')?.focus());
793
+ }, []);
794
+ const focusGridOnMount = useCallback((el) => {
795
+ if (!el)
796
+ return;
797
+ setGrid((g) => (g.r === 0 ? { r: 1, c: 1 } : g));
798
+ requestAnimationFrame(() => el.focus());
799
+ }, []);
800
+ /** Paleta: strzałki po próbkach, Tab krąży w panelu, Escape zamyka i oddaje fokus narzędziu. */
801
+ const onPaletteKeyDown = (event) => {
802
+ const panel = event.currentTarget;
803
+ if (event.key === 'Escape') {
804
+ event.preventDefault();
805
+ event.stopPropagation();
806
+ setColorOpen(false);
807
+ if (colorKind === 'cellBg')
808
+ focusEditor();
809
+ else
810
+ (colorKind === 'foreColor' ? foreAnchor : backAnchor)?.focus();
811
+ return;
812
+ }
813
+ if (event.key === 'Tab') {
814
+ trapTab(panel, event.nativeEvent);
815
+ return;
816
+ }
817
+ const swatches = [...panel.querySelectorAll('.kpt-rich-text__swatch')];
818
+ const at = swatches.indexOf(document.activeElement);
819
+ if (at === -1)
820
+ return;
821
+ const next = nextRovingIndex(swatches.map(() => false), at, event.key);
822
+ if (next === null || next === at)
823
+ return;
824
+ event.preventDefault();
825
+ swatches[next].focus();
826
+ };
827
+ const paletteContent = (_jsxs("div", { ref: focusSwatchOnMount, className: "kpt-rich-text__palette", role: "group", "aria-label": t(colorKind === 'backColor' ? 'richText.backColor' : 'richText.foreColor'), onMouseDown: (event) => event.preventDefault(), onKeyDown: onPaletteKeyDown, 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: () => applyColor(swatch.color) }, swatch.color))) }), _jsxs("label", { className: "kpt-rich-text__picker", children: [_jsx("span", { children: t('richText.paletteCustom') }), _jsx("input", { type: "color", onInput: (event) => applyColor(event.target.value) })] })] }));
742
828
  /**
743
829
  * Panel otwarty przy dolnej albo prawej krawędzi okna wychodziłby poza ekran — CDK robił to
744
830
  * w Angularze sam, tutaj przesuwamy go po zamontowaniu, gdy już znać jego rozmiar.
@@ -791,13 +877,118 @@ export function KptRichText({ value, defaultValue = '', onValueChange, toolbar =
791
877
  buttons[next].focus();
792
878
  };
793
879
  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) => {
880
+ /**
881
+ * Akcje menu tabeli po identyfikatorze. Handlery żyją tutaj, a nie jako domknięcia przekazywane
882
+ * do `menuItem()` — funkcja wołana w renderze nie dostaje wtedy niczego, co czyta refy.
883
+ */
884
+ const runTableAction = (action) => {
885
+ switch (action) {
886
+ case 'insertLeft': {
887
+ if (ctxCell.current)
888
+ insertColumn(ctxCell.current, 'left');
889
+ afterTableOp();
890
+ break;
891
+ }
892
+ case 'insertRight': {
893
+ if (ctxCell.current)
894
+ insertColumn(ctxCell.current, 'right');
895
+ afterTableOp();
896
+ break;
897
+ }
898
+ case 'headerColumn': {
899
+ if (ctxCell.current)
900
+ toggleHeaderColumn(ctxCell.current);
901
+ ctxCell.current = null; // komórki zostały podmienione
902
+ afterTableOp();
903
+ break;
904
+ }
905
+ case 'deleteColumn': {
906
+ if (ctxCell.current)
907
+ deleteColumn(ctxCell.current);
908
+ afterTableOp();
909
+ break;
910
+ }
911
+ case 'insertAbove': {
912
+ if (ctxCell.current)
913
+ insertRow(ctxCell.current, 'above');
914
+ afterTableOp();
915
+ break;
916
+ }
917
+ case 'insertBelow': {
918
+ if (ctxCell.current)
919
+ insertRow(ctxCell.current, 'below');
920
+ afterTableOp();
921
+ break;
922
+ }
923
+ case 'headerRow': {
924
+ if (ctxCell.current)
925
+ toggleHeaderRow(ctxCell.current);
926
+ ctxCell.current = null;
927
+ afterTableOp();
928
+ break;
929
+ }
930
+ case 'deleteRow': {
931
+ if (ctxCell.current)
932
+ deleteRow(ctxCell.current);
933
+ afterTableOp();
934
+ break;
935
+ }
936
+ case 'cellBackground': {
937
+ setMenuOpen(false);
938
+ setColorKind('cellBg');
939
+ setColorOpen(true);
940
+ break;
941
+ }
942
+ case 'alignLeft': {
943
+ for (const cell of activeCells())
944
+ cell.style.textAlign = 'left';
945
+ afterTableOp();
946
+ break;
947
+ }
948
+ case 'alignCenter': {
949
+ for (const cell of activeCells())
950
+ cell.style.textAlign = 'center';
951
+ afterTableOp();
952
+ break;
953
+ }
954
+ case 'alignRight': {
955
+ for (const cell of activeCells())
956
+ cell.style.textAlign = 'right';
957
+ afterTableOp();
958
+ break;
959
+ }
960
+ case 'mergeCells': {
961
+ mergeCells([...selCells.current]);
962
+ clearCellSelection();
963
+ afterTableOp();
964
+ break;
965
+ }
966
+ case 'splitCell': {
967
+ if (ctxCell.current)
968
+ splitCell(ctxCell.current);
969
+ afterTableOp();
970
+ break;
971
+ }
972
+ case 'border': {
973
+ setMenuOpen(false);
974
+ setBorderOpen(true);
975
+ break;
976
+ }
977
+ case 'deleteTable': {
978
+ ctxCell.current?.closest('table')?.remove();
979
+ afterTableOp();
980
+ break;
981
+ }
982
+ }
983
+ };
984
+ const menuItem = (icon, label, action, extra) => (_jsxs("button", { type: "button", role: "menuitem", tabIndex: -1, className: cn('kpt-rt-menu__item', extra), "aria-disabled": extra === 'is-disabled' || undefined, onClick: () => runTableAction(action), children: [_jsx(KptIcon, { name: icon }), " ", label] }));
985
+ return (_jsxs("div", { className: cn('kpt-rich-text-host', className), ...rest, children: [_jsxs("div", { className: cn('kpt-rich-text', showInvalid && 'kpt-rich-text--invalid', disabledProp && '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) => {
796
986
  if (item === 'heading') {
797
987
  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: () => {
798
988
  if (disabled)
799
989
  return;
800
990
  saveSelection();
991
+ setStyleActive(Math.max(0, styleOptions.findIndex((o) => o.tag === currentBlock)));
801
992
  setStyleOpen(true);
802
993
  }, children: [_jsx("span", { children: styleLabel }), _jsx(KptIcon, { name: "chevron-down", className: "kpt-rich-text__style-caret" })] }, item));
803
994
  }
@@ -814,7 +1005,9 @@ export function KptRichText({ value, defaultValue = '', onValueChange, toolbar =
814
1005
  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));
815
1006
  }
816
1007
  return toolButton(item);
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: () => {
1008
+ })] }, 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,
1009
+ // Wyłączony edytor (contentEditable=false) wypada z kolejności Tab jak wyłączone pole.
1010
+ tabIndex: disabled ? -1 : 0, "aria-readonly": readOnly || undefined, 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: () => {
818
1011
  pushValue();
819
1012
  syncMention();
820
1013
  }, onKeyDown: onKeyDown, onPaste: onPaste, onContextMenu: onContextMenu, onMouseDown: onEditorMouseDown, onMouseMove: onEditorMouseMove, onBlur: () => onTouch?.() }))] }), mentionOpen && container
@@ -844,90 +1037,42 @@ export function KptRichText({ value, defaultValue = '', onValueChange, toolbar =
844
1037
  // Panel jest niewidoczny do pierwszego pomiaru — fokus po klatce.
845
1038
  if (el)
846
1039
  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: () => {
1040
+ }, 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", { ref: focusPanelOnMount, className: "kpt-rich-text__style-panel", role: "listbox", tabIndex: -1, "aria-label": t('richText.heading'), "aria-activedescendant": `${field.id}-style-${styleActive}`, onKeyDown: (event) => {
1041
+ const count = styleOptions.length;
1042
+ if (event.key === 'ArrowDown')
1043
+ setStyleActive((i) => (i + 1) % count);
1044
+ else if (event.key === 'ArrowUp')
1045
+ setStyleActive((i) => (i - 1 + count) % count);
1046
+ else if (event.key === 'Home')
1047
+ setStyleActive(0);
1048
+ else if (event.key === 'End')
1049
+ setStyleActive(count - 1);
1050
+ else if (event.key === 'Enter' || event.key === ' ')
1051
+ pickStyle(styleOptions[styleActive].tag);
1052
+ else if (event.key === 'Escape' || event.key === 'Tab') {
848
1053
  setStyleOpen(false);
849
- focusEditor();
850
- restoreSelection();
851
- legacyExec('formatBlock', `<${option.tag}>`);
852
- pushValue();
853
- refreshState();
854
- }, children: option.label }, option.tag))) }) }), _jsx(KptOverlay, { open: colorOpen && colorKind === 'foreColor', anchor: foreAnchor, onClose: () => setColorOpen(false), offset: 4, children: palette(applyColor, t('richText.paletteCustom')) }), _jsx(KptOverlay, { open: colorOpen && colorKind === 'backColor', anchor: backAnchor, onClose: () => setColorOpen(false), offset: 4, children: palette(applyColor, t('richText.paletteCustom')) }), _jsx(KptOverlay, { open: tableOpen, anchor: tableAnchor, onClose: () => {
1054
+ styleAnchor?.focus();
1055
+ }
1056
+ else
1057
+ return;
1058
+ event.preventDefault();
1059
+ event.stopPropagation();
1060
+ }, children: styleOptions.map((option, index) => (_jsx("li", { id: `${field.id}-style-${index}`, className: cn(`kpt-rich-text__style-opt kpt-rich-text__style-opt--${option.tag}`, currentBlock === option.tag && 'is-selected', styleActive === index && 'is-active'), role: "option", "aria-selected": currentBlock === option.tag, onClick: () => pickStyle(option.tag), children: option.label }, option.tag))) }) }), _jsx(KptOverlay, { open: colorOpen && colorKind === 'foreColor', anchor: foreAnchor, onClose: () => setColorOpen(false), offset: 4, children: paletteContent }), _jsx(KptOverlay, { open: colorOpen && colorKind === 'backColor', anchor: backAnchor, onClose: () => setColorOpen(false), offset: 4, children: paletteContent }), _jsx(KptOverlay, { open: tableOpen, anchor: tableAnchor, onClose: () => {
855
1061
  setTableOpen(false);
856
1062
  setGrid({ r: 0, c: 0 });
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
1063
+ }, offset: 4, children: _jsxs("div", { className: "kpt-rt-grid-pop", onMouseDown: (event) => event.preventDefault(), children: [_jsx("div", { ref: focusGridOnMount, className: "kpt-rt-grid", role: "group",
1064
+ // Jeden przystanek ze strzałkami (rozmiar tabeli) — grupa z własną obsługą klawiatury.
1065
+ // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex -- patrz wyżej
1066
+ tabIndex: 0, "aria-label": t('richText.table'), "aria-describedby": `${field.id}-grid-size`, "aria-keyshortcuts": "ArrowUp ArrowDown ArrowLeft ArrowRight Enter Escape", onKeyDown: onGridKeyDown, 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", id: `${field.id}-grid-size`, "aria-live": "polite", children: [grid.r, " \u00D7 ", grid.c] })] }) }), menuOpen
858
1067
  ? pointPanel(_jsxs("div", { ref: (el) => {
859
1068
  if (el && menuFromKeyboard.current) {
860
1069
  menuFromKeyboard.current = false;
861
1070
  requestAnimationFrame(() => focusMenuEdge(el, 'first'));
862
1071
  }
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'), () => {
864
- if (ctxCell.current)
865
- insertColumn(ctxCell.current, 'left');
866
- afterTableOp();
867
- }), menuItem('plus', t('richText.menu.insertRight'), () => {
868
- if (ctxCell.current)
869
- insertColumn(ctxCell.current, 'right');
870
- afterTableOp();
871
- }), menuItem('table', t('richText.menu.headerColumn'), () => {
872
- if (ctxCell.current)
873
- toggleHeaderColumn(ctxCell.current);
874
- ctxCell.current = null; // komórki zostały podmienione
875
- afterTableOp();
876
- }), menuItem('trash', t('richText.menu.deleteColumn'), () => {
877
- if (ctxCell.current)
878
- deleteColumn(ctxCell.current);
879
- afterTableOp();
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'), () => {
881
- if (ctxCell.current)
882
- insertRow(ctxCell.current, 'above');
883
- afterTableOp();
884
- }), menuItem('plus', t('richText.menu.insertBelow'), () => {
885
- if (ctxCell.current)
886
- insertRow(ctxCell.current, 'below');
887
- afterTableOp();
888
- }), menuItem('table', t('richText.menu.headerRow'), () => {
889
- if (ctxCell.current)
890
- toggleHeaderRow(ctxCell.current);
891
- ctxCell.current = null;
892
- afterTableOp();
893
- }), menuItem('trash', t('richText.menu.deleteRow'), () => {
894
- if (ctxCell.current)
895
- deleteRow(ctxCell.current);
896
- afterTableOp();
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'), () => {
898
- setMenuOpen(false);
899
- setColorKind('cellBg');
900
- setColorOpen(true);
901
- }), menuItem('align-left', t('richText.menu.alignLeft'), () => {
902
- for (const cell of activeCells())
903
- cell.style.textAlign = 'left';
904
- afterTableOp();
905
- }), menuItem('align-center', t('richText.menu.alignCenter'), () => {
906
- for (const cell of activeCells())
907
- cell.style.textAlign = 'center';
908
- afterTableOp();
909
- }), menuItem('align-right', t('richText.menu.alignRight'), () => {
910
- for (const cell of activeCells())
911
- cell.style.textAlign = 'right';
912
- afterTableOp();
913
- }), _jsx("div", { className: "kpt-rt-menu__sep", role: "separator" }), menuItem('table', t('richText.menu.mergeCells'), () => {
914
- mergeCells([...selCells.current]);
915
- clearCellSelection();
916
- afterTableOp();
917
- }, selCount < 2 ? 'is-disabled' : undefined), menuItem('grip-vertical', t('richText.menu.splitCell'), () => {
918
- if (ctxCell.current)
919
- splitCell(ctxCell.current);
920
- afterTableOp();
921
- }, canSplitCtx.current ? undefined : 'is-disabled'), _jsx("div", { className: "kpt-rt-menu__sep", role: "separator" }), menuItem('table', t('richText.menu.border'), () => {
922
- setMenuOpen(false);
923
- setBorderOpen(true);
924
- }), _jsx("div", { className: "kpt-rt-menu__sep", role: "separator" }), menuItem('trash', t('richText.menu.deleteTable'), () => {
925
- ctxCell.current?.closest('table')?.remove();
926
- afterTableOp();
927
- }, 'kpt-rt-menu__item--danger')] }), () => setMenuOpen(false))
1072
+ }, className: "kpt-rt-menu", role: "menu", tabIndex: -1, "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'), 'insertLeft'), menuItem('plus', t('richText.menu.insertRight'), 'insertRight'), menuItem('table', t('richText.menu.headerColumn'), 'headerColumn'), menuItem('trash', t('richText.menu.deleteColumn'), 'deleteColumn'), _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'), 'insertAbove'), menuItem('plus', t('richText.menu.insertBelow'), 'insertBelow'), menuItem('table', t('richText.menu.headerRow'), 'headerRow'), menuItem('trash', t('richText.menu.deleteRow'), 'deleteRow'), _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'), 'cellBackground'), menuItem('align-left', t('richText.menu.alignLeft'), 'alignLeft'), menuItem('align-center', t('richText.menu.alignCenter'), 'alignCenter'), menuItem('align-right', t('richText.menu.alignRight'), 'alignRight'), _jsx("div", { className: "kpt-rt-menu__sep", role: "separator" }), menuItem('table', t('richText.menu.mergeCells'), 'mergeCells', selCount < 2 ? 'is-disabled' : undefined), menuItem('grip-vertical', t('richText.menu.splitCell'), 'splitCell', canSplitCtx ? undefined : 'is-disabled'), _jsx("div", { className: "kpt-rt-menu__sep", role: "separator" }), menuItem('table', t('richText.menu.border'), 'border'), _jsx("div", { className: "kpt-rt-menu__sep", role: "separator" }), menuItem('trash', t('richText.menu.deleteTable'), 'deleteTable', 'kpt-rt-menu__item--danger')] }), () => setMenuOpen(false))
928
1073
  : null, borderOpen
929
1074
  ? 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))
930
1075
  : null, colorOpen && colorKind === 'cellBg'
931
- ? pointPanel(palette(applyColor, t('richText.paletteCustom')), () => setColorOpen(false))
1076
+ ? pointPanel(paletteContent, () => setColorOpen(false))
932
1077
  : null] }));
933
1078
  }
@@ -1,3 +1,12 @@
1
+ /**
2
+ * Czyści HTML i mówi, czy coś usunął. `changed: false` znaczy, że treść była czysta — wtedy
3
+ * wołający nie musi nadpisywać wartości modelu (serializacja DOM potrafi zmienić zapis, np.
4
+ * cudzysłowy, bez zmiany treści, a taka różnica nie może brudzić formularza).
5
+ */
6
+ export declare function sanitizeHtmlReport(html: string): {
7
+ html: string;
8
+ changed: boolean;
9
+ };
1
10
  /**
2
11
  * Czyści HTML wchodzący do edytora. Dotyczy **każdego** zapisu do DOM edytora, nie tylko
3
12
  * wklejania: `value` z aplikacji też bywa niezaufane (baza, CMS, inny użytkownik).