@konce-pt/react 0.12.0 → 0.15.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 +122 -64
- package/dist/accordion/accordion.d.ts +14 -6
- package/dist/accordion/accordion.js +14 -7
- package/dist/accordion/llms.txt +22 -10
- package/dist/alert/alert.d.ts +2 -2
- package/dist/alert/alert.js +2 -2
- package/dist/alert/llms.txt +10 -10
- package/dist/app-shell/app-shell.d.ts +14 -14
- package/dist/app-shell/app-shell.js +11 -11
- package/dist/app-shell/llms.txt +13 -13
- package/dist/auth/auth-types.d.ts +6 -6
- package/dist/auth/auth.d.ts +2 -2
- package/dist/auth/auth.js +7 -7
- package/dist/auth/llms.txt +9 -9
- package/dist/autocomplete/autocomplete.d.ts +19 -6
- package/dist/autocomplete/autocomplete.js +23 -10
- package/dist/autocomplete/llms.txt +16 -5
- package/dist/avatar/avatar.d.ts +6 -6
- package/dist/avatar/avatar.js +5 -5
- package/dist/avatar/llms.txt +10 -10
- package/dist/avatar-group/avatar-group.d.ts +4 -4
- package/dist/avatar-group/avatar-group.js +3 -3
- package/dist/avatar-group/llms.txt +8 -8
- package/dist/backdrop/backdrop.d.ts +7 -7
- package/dist/backdrop/backdrop.js +16 -16
- package/dist/backdrop/llms.txt +27 -27
- package/dist/badge/badge.d.ts +3 -3
- package/dist/badge/badge.js +1 -1
- package/dist/badge/llms.txt +11 -11
- package/dist/bottom-sheet/bottom-sheet.d.ts +4 -4
- package/dist/bottom-sheet/bottom-sheet.js +2 -2
- package/dist/bottom-sheet/llms.txt +9 -9
- package/dist/breadcrumb/breadcrumb.d.ts +2 -2
- package/dist/breadcrumb/breadcrumb.js +3 -3
- package/dist/breadcrumb/llms.txt +3 -3
- package/dist/button/button.d.ts +3 -3
- package/dist/button/button.js +8 -8
- package/dist/button/llms.txt +27 -19
- package/dist/button-group/button-group.d.ts +9 -9
- package/dist/button-group/button-group.js +30 -8
- package/dist/button-group/llms.txt +20 -14
- package/dist/card/card.d.ts +8 -8
- package/dist/card/card.js +6 -6
- package/dist/card/llms.txt +20 -20
- package/dist/carousel/carousel.d.ts +6 -6
- package/dist/carousel/carousel.js +4 -4
- package/dist/carousel/llms.txt +10 -10
- package/dist/charts/chart-legend.d.ts +3 -3
- package/dist/charts/chart-legend.js +1 -1
- package/dist/charts/chart-tooltip.d.ts +3 -3
- package/dist/charts/chart-tooltip.js +1 -1
- package/dist/charts/chart.d.ts +1 -1
- package/dist/charts/chart.js +8 -8
- package/dist/charts/llms.txt +6 -6
- package/dist/charts-entry/public-api.js +2 -2
- package/dist/chat/chat.d.ts +17 -10
- package/dist/chat/chat.js +42 -18
- package/dist/chat/llms.txt +32 -17
- package/dist/chat-composer/chat-composer.d.ts +17 -7
- package/dist/chat-composer/chat-composer.js +21 -8
- package/dist/chat-composer/llms.txt +20 -11
- package/dist/chat-dock/chat-dock.d.ts +11 -4
- package/dist/chat-dock/chat-dock.js +9 -6
- package/dist/chat-dock/llms.txt +15 -14
- package/dist/chat-entry/public-api.js +1 -1
- package/dist/chat-message/chat-message.d.ts +12 -5
- package/dist/chat-message/chat-message.js +17 -7
- package/dist/chat-message/llms.txt +16 -15
- package/dist/chat-typing/chat-typing.d.ts +2 -2
- package/dist/chat-typing/chat-typing.js +3 -3
- package/dist/chat-typing/llms.txt +6 -6
- package/dist/checkbox/checkbox.d.ts +13 -6
- package/dist/checkbox/checkbox.js +26 -7
- package/dist/checkbox/llms.txt +24 -12
- package/dist/chip/chip.d.ts +4 -4
- package/dist/chip/chip.js +2 -2
- package/dist/chip/llms.txt +11 -11
- package/dist/chips-input/chips-input.d.ts +4 -4
- package/dist/chips-input/chips-input.js +35 -9
- package/dist/chips-input/llms.txt +15 -8
- package/dist/clock/clock.d.ts +4 -4
- package/dist/clock/clock.js +7 -7
- package/dist/clock/llms.txt +8 -8
- package/dist/color-picker/color-picker.d.ts +6 -6
- package/dist/color-picker/color-picker.js +3 -3
- package/dist/color-picker/llms.txt +5 -5
- package/dist/confirm/confirm-container.d.ts +3 -3
- package/dist/confirm/confirm-container.js +5 -5
- package/dist/confirm/confirm-store.d.ts +4 -4
- package/dist/confirm/confirm-store.js +7 -7
- package/dist/confirm/llms.txt +6 -6
- package/dist/context-menu/context-menu.d.ts +1 -1
- package/dist/context-menu/context-menu.js +5 -5
- package/dist/context-menu/llms.txt +7 -7
- package/dist/data-table/data-table-types.d.ts +5 -5
- package/dist/data-table/data-table.d.ts +29 -8
- package/dist/data-table/data-table.js +340 -95
- package/dist/data-table/export-csv.d.ts +3 -3
- package/dist/data-table/export-csv.js +4 -4
- package/dist/data-table/llms.txt +127 -94
- package/dist/data-view/data-view.d.ts +13 -5
- package/dist/data-view/data-view.js +67 -6
- package/dist/data-view/llms.txt +29 -10
- package/dist/date-range/date-range.d.ts +9 -9
- package/dist/date-range/date-range.js +24 -24
- package/dist/date-range/llms.txt +20 -20
- package/dist/datepicker/datepicker.d.ts +10 -10
- package/dist/datepicker/datepicker.js +29 -29
- package/dist/datepicker/llms.txt +24 -24
- package/dist/dialog/dialog.d.ts +12 -7
- package/dist/dialog/dialog.js +4 -4
- package/dist/dialog/llms.txt +14 -11
- package/dist/divider/divider.d.ts +1 -1
- package/dist/divider/llms.txt +4 -4
- package/dist/drawer/drawer.d.ts +5 -5
- package/dist/drawer/drawer.js +2 -2
- package/dist/drawer/llms.txt +11 -11
- package/dist/empty/empty.d.ts +2 -2
- package/dist/empty/empty.js +1 -1
- package/dist/empty/llms.txt +9 -9
- package/dist/fab/fab.d.ts +1 -1
- package/dist/fab/fab.js +1 -1
- package/dist/fab/llms.txt +10 -6
- package/dist/fieldset/fieldset.d.ts +18 -4
- package/dist/fieldset/fieldset.js +9 -6
- package/dist/fieldset/llms.txt +26 -9
- package/dist/file-upload/file-upload.d.ts +3 -3
- package/dist/file-upload/file-upload.js +7 -7
- package/dist/file-upload/llms.txt +7 -7
- package/dist/form-field/form-field-context.d.ts +4 -4
- package/dist/form-field/form-field.d.ts +12 -4
- package/dist/form-field/form-field.js +26 -6
- package/dist/form-field/llms.txt +25 -11
- package/dist/galleria/galleria.d.ts +3 -3
- package/dist/galleria/galleria.js +5 -5
- package/dist/galleria/llms.txt +7 -7
- package/dist/grid/col.d.ts +4 -4
- package/dist/grid/col.js +2 -2
- package/dist/grid/flex.d.ts +3 -3
- package/dist/grid/flex.js +1 -1
- package/dist/grid/grid.d.ts +7 -7
- package/dist/grid/grid.js +3 -3
- package/dist/grid/layout.d.ts +2 -2
- package/dist/grid/layout.js +2 -2
- package/dist/grid/llms.txt +15 -15
- package/dist/grid-entry/public-api.js +1 -1
- package/dist/i18n/i18n-context.d.ts +3 -3
- package/dist/i18n/i18n-context.js +7 -7
- package/dist/icon/icon-registry.d.ts +3 -3
- package/dist/icon/icon-registry.js +3 -3
- package/dist/icon/icon.d.ts +4 -4
- package/dist/icon/icon.js +6 -6
- package/dist/icon/llms.txt +13 -13
- package/dist/icon-button/icon-button.d.ts +2 -2
- package/dist/icon-button/icon-button.js +1 -1
- package/dist/icon-button/llms.txt +9 -5
- package/dist/icons-entry/public-api.d.ts +2 -2
- package/dist/icons-entry/public-api.js +3 -3
- package/dist/image/image.js +1 -1
- package/dist/image/llms.txt +13 -13
- package/dist/input/input.d.ts +18 -5
- package/dist/input/input.js +21 -6
- package/dist/input/llms.txt +25 -11
- package/dist/input-mask/input-mask.d.ts +1 -1
- package/dist/input-mask/input-mask.js +2 -2
- package/dist/input-mask/llms.txt +5 -5
- package/dist/input-number/input-number.d.ts +9 -9
- package/dist/input-number/input-number.js +11 -11
- package/dist/input-number/llms.txt +14 -14
- package/dist/input-otp/input-otp.d.ts +6 -6
- package/dist/input-otp/input-otp.js +10 -10
- package/dist/input-otp/llms.txt +9 -9
- package/dist/internal/calendar-focus.d.ts +1 -1
- package/dist/internal/calendar-focus.js +3 -3
- package/dist/internal/cn.d.ts +1 -1
- package/dist/internal/mask.d.ts +2 -2
- package/dist/internal/mask.js +4 -4
- package/dist/internal/option-key.d.ts +2 -2
- package/dist/internal/option-key.js +2 -2
- package/dist/internal/option-nav.d.ts +4 -4
- package/dist/internal/option-nav.js +5 -5
- package/dist/internal/overlay-container.js +1 -1
- package/dist/internal/overlay.d.ts +3 -3
- package/dist/internal/overlay.js +8 -8
- package/dist/internal/password-strength.d.ts +2 -2
- package/dist/internal/password-strength.js +6 -6
- package/dist/internal/roving.d.ts +2 -2
- package/dist/internal/roving.js +3 -3
- package/dist/internal/scroll-lock.d.ts +1 -1
- package/dist/internal/scroll-lock.js +2 -2
- package/dist/internal/slot-children.d.ts +3 -3
- package/dist/internal/slot-children.js +4 -4
- package/dist/internal/slot.d.ts +3 -3
- package/dist/internal/slot.js +2 -2
- package/dist/internal/step.d.ts +3 -3
- package/dist/internal/step.js +4 -4
- package/dist/internal/tween.d.ts +5 -5
- package/dist/internal/tween.js +5 -5
- package/dist/internal/types.d.ts +2 -2
- package/dist/internal/use-breakpoint.d.ts +2 -2
- package/dist/internal/use-breakpoint.js +4 -4
- package/dist/internal/use-controllable-state.d.ts +1 -1
- package/dist/internal/use-controllable-state.js +3 -3
- package/dist/internal/use-id.d.ts +2 -2
- package/dist/internal/use-id.js +2 -2
- package/dist/internal/use-modal-focus.d.ts +1 -1
- package/dist/internal/use-modal-focus.js +3 -3
- package/dist/knob/knob.d.ts +4 -4
- package/dist/knob/knob.js +14 -10
- package/dist/knob/llms.txt +17 -12
- package/dist/listbox/listbox.d.ts +6 -6
- package/dist/listbox/listbox.js +9 -9
- package/dist/listbox/llms.txt +10 -10
- package/dist/loader/llms.txt +56 -0
- package/dist/loader/loader.d.ts +49 -0
- package/dist/loader/loader.js +68 -0
- package/dist/map/llms.txt +25 -22
- package/dist/map/map-create-dialog.d.ts +4 -4
- package/dist/map/map-create-dialog.js +10 -10
- package/dist/map/map-hours-field.d.ts +4 -4
- package/dist/map/map-hours-field.js +7 -7
- package/dist/map/map-image-field.d.ts +2 -2
- package/dist/map/map-image-field.js +4 -4
- package/dist/map/map-legend.d.ts +4 -4
- package/dist/map/map-legend.js +2 -2
- package/dist/map/map-poi-card.d.ts +3 -3
- package/dist/map/map-poi-card.js +9 -9
- package/dist/map/map-search.d.ts +17 -6
- package/dist/map/map-search.js +21 -18
- package/dist/map/map.d.ts +21 -14
- package/dist/map/map.js +30 -29
- package/dist/map/ports.d.ts +9 -9
- package/dist/map/ports.js +6 -6
- package/dist/map-entry/public-api.js +2 -2
- package/dist/megamenu/llms.txt +9 -9
- package/dist/megamenu/megamenu.d.ts +6 -6
- package/dist/megamenu/megamenu.js +10 -10
- package/dist/mention/llms.txt +15 -15
- package/dist/mention/mention.d.ts +5 -5
- package/dist/mention/mention.js +2 -2
- package/dist/mention/mentions.d.ts +1 -1
- package/dist/mention/mentions.js +1 -1
- package/dist/menu/llms.txt +12 -12
- package/dist/menu/menu-item.d.ts +4 -4
- package/dist/menu/menu-keys.d.ts +2 -2
- package/dist/menu/menu-keys.js +5 -5
- package/dist/menu/menu.d.ts +2 -2
- package/dist/menu/menu.js +6 -6
- package/dist/menubar/llms.txt +10 -10
- package/dist/menubar/menubar.d.ts +5 -5
- package/dist/menubar/menubar.js +10 -10
- package/dist/meter-group/llms.txt +12 -12
- package/dist/meter-group/meter-group.d.ts +2 -2
- package/dist/meter-group/meter-group.js +1 -1
- package/dist/motion/motion-context.d.ts +22 -0
- package/dist/motion/motion-context.js +28 -0
- package/dist/order-list/llms.txt +22 -10
- package/dist/order-list/order-list.d.ts +12 -4
- package/dist/order-list/order-list.js +104 -21
- package/dist/paginator/llms.txt +8 -8
- package/dist/paginator/paginator.js +1 -1
- package/dist/panel/llms.txt +25 -13
- package/dist/panel/panel.d.ts +11 -3
- package/dist/panel/panel.js +7 -4
- package/dist/password/llms.txt +16 -8
- package/dist/password/password.d.ts +2 -2
- package/dist/password/password.js +24 -3
- package/dist/peek/llms.txt +10 -10
- package/dist/peek/peek.d.ts +6 -6
- package/dist/peek/peek.js +7 -7
- package/dist/pick-list/llms.txt +33 -13
- package/dist/pick-list/pick-list.d.ts +12 -4
- package/dist/pick-list/pick-list.js +111 -23
- package/dist/popover/llms.txt +11 -11
- package/dist/popover/popover.d.ts +4 -4
- package/dist/popover/popover.js +5 -5
- package/dist/progress/llms.txt +17 -17
- package/dist/progress/progress.d.ts +7 -7
- package/dist/progress/progress.js +6 -6
- package/dist/public-api.d.ts +15 -3
- package/dist/public-api.js +13 -7
- package/dist/radio-group/llms.txt +13 -6
- package/dist/radio-group/radio-group.d.ts +11 -4
- package/dist/radio-group/radio-group.js +15 -7
- package/dist/rating/llms.txt +20 -13
- package/dist/rating/rating-pointer.d.ts +16 -0
- package/dist/rating/rating-pointer.js +26 -0
- package/dist/rating/rating.d.ts +6 -6
- package/dist/rating/rating.js +76 -12
- package/dist/rich-text/lists.d.ts +2 -2
- package/dist/rich-text/lists.js +7 -7
- package/dist/rich-text/llms.txt +26 -17
- package/dist/rich-text/rich-text.d.ts +12 -7
- package/dist/rich-text/rich-text.js +78 -54
- package/dist/rich-text/sanitize.d.ts +3 -3
- package/dist/rich-text/sanitize.js +12 -12
- package/dist/rich-text/tables.d.ts +3 -3
- package/dist/rich-text/tables.js +3 -3
- package/dist/roadmap/llms.txt +15 -15
- package/dist/roadmap/roadmap.d.ts +6 -6
- package/dist/roadmap/roadmap.js +19 -19
- package/dist/roadmap-entry/public-api.js +2 -2
- package/dist/scroll-top/llms.txt +6 -6
- package/dist/scroll-top/scroll-top.d.ts +1 -1
- package/dist/scroll-top/scroll-top.js +1 -1
- package/dist/select/llms.txt +19 -12
- package/dist/select/select.d.ts +8 -8
- package/dist/select/select.js +47 -21
- package/dist/shared/types.d.ts +5 -5
- package/dist/shared/types.js +1 -1
- package/dist/sidenav/llms.txt +9 -9
- package/dist/sidenav/sidenav.d.ts +1 -1
- package/dist/sidenav/sidenav.js +1 -1
- package/dist/skeleton/llms.txt +3 -3
- package/dist/skeleton/skeleton.d.ts +1 -1
- package/dist/skeleton/skeleton.js +1 -1
- package/dist/slider/llms.txt +32 -13
- package/dist/slider/slider-drag.d.ts +26 -0
- package/dist/slider/slider-drag.js +79 -0
- package/dist/slider/slider.d.ts +6 -5
- package/dist/slider/slider.js +27 -8
- package/dist/speech/llms.txt +97 -0
- package/dist/speech/speech-button.d.ts +44 -0
- package/dist/speech/speech-button.js +86 -0
- package/dist/speech/speech-wave.d.ts +19 -0
- package/dist/speech/speech-wave.js +16 -0
- package/dist/speech/use-speech-recognition.d.ts +81 -0
- package/dist/speech/use-speech-recognition.js +179 -0
- package/dist/speed-dial/llms.txt +14 -8
- package/dist/speed-dial/speed-dial.d.ts +1 -1
- package/dist/speed-dial/speed-dial.js +1 -1
- package/dist/spinner/llms.txt +7 -7
- package/dist/spinner/spinner.d.ts +1 -1
- package/dist/spinner/spinner.js +1 -1
- package/dist/split-button/llms.txt +21 -15
- package/dist/split-button/split-button.d.ts +2 -2
- package/dist/split-button/split-button.js +4 -4
- package/dist/splitter/llms.txt +11 -11
- package/dist/splitter/splitter.d.ts +3 -3
- package/dist/splitter/splitter.js +5 -5
- package/dist/stepper/llms.txt +33 -14
- package/dist/stepper/stepper.d.ts +6 -6
- package/dist/stepper/stepper.js +53 -10
- package/dist/switch/llms.txt +12 -5
- package/dist/switch/switch.d.ts +10 -3
- package/dist/switch/switch.js +12 -5
- package/dist/switch-group/llms.txt +9 -9
- package/dist/switch-group/switch-group.d.ts +4 -4
- package/dist/switch-group/switch-group.js +4 -4
- package/dist/tabs/llms.txt +10 -10
- package/dist/tabs/tabs.d.ts +5 -5
- package/dist/tabs/tabs.js +8 -8
- package/dist/textarea/llms.txt +23 -12
- package/dist/textarea/textarea.d.ts +19 -6
- package/dist/textarea/textarea.js +21 -7
- package/dist/timeline/llms.txt +4 -4
- package/dist/timeline/timeline.d.ts +1 -1
- package/dist/timeline/timeline.js +1 -1
- package/dist/toast/llms.txt +47 -17
- package/dist/toast/toast-container.d.ts +9 -3
- package/dist/toast/toast-container.js +17 -9
- package/dist/toast/toast-store.d.ts +40 -4
- package/dist/toast/toast-store.js +36 -1
- package/dist/toolbar/llms.txt +7 -7
- package/dist/toolbar/toolbar.d.ts +3 -3
- package/dist/toolbar/toolbar.js +2 -2
- package/dist/tooltip/llms.txt +10 -10
- package/dist/tooltip/tooltip.d.ts +3 -3
- package/dist/tooltip/tooltip.js +18 -18
- package/dist/tree/llms.txt +25 -12
- package/dist/tree/tree-keys.d.ts +5 -5
- package/dist/tree/tree-keys.js +7 -7
- package/dist/tree/tree.d.ts +14 -7
- package/dist/tree/tree.js +53 -18
- package/dist/update/llms.txt +54 -0
- package/dist/update/update.d.ts +49 -0
- package/dist/update/update.js +82 -0
- package/package.json +15 -13
package/dist/rich-text/llms.txt
CHANGED
|
@@ -5,24 +5,24 @@ editing. The value is HTML.
|
|
|
5
5
|
Import: `import { KptRichText, type KptRichTool } from '@konce-pt/react';`
|
|
6
6
|
|
|
7
7
|
## Props
|
|
8
|
-
- `value` / `defaultValue` / `onValueChange`
|
|
9
|
-
- `toolbar`: `KptRichTool[] | KptRichTool[][]`
|
|
8
|
+
- `value` / `defaultValue` / `onValueChange` - the HTML
|
|
9
|
+
- `toolbar`: `KptRichTool[] | KptRichTool[][]` - a flat list, or groups = sections separated by a
|
|
10
10
|
divider. Leaving a tool or a group out disables it; the full toolbar in 5 sections by default.
|
|
11
11
|
- `placeholder`, `maxListLevel` (6), `disabled`, `invalid`, `touched`, `onTouch()`
|
|
12
|
-
- `mentions`: `readonly KptMentionItem[]`, `mentionLimit` (8), `onMention(item)`
|
|
12
|
+
- `mentions`: `readonly KptMentionItem[]`, `mentionLimit` (8), `onMention(item)` - suggestions
|
|
13
13
|
for `@`; an empty list turns the mechanism off, so `@` stays an ordinary character
|
|
14
14
|
- `className`, `ref` and every other `<div>` prop go to the host
|
|
15
15
|
|
|
16
16
|
## Mentions (`@`)
|
|
17
17
|
With `mentions` set, typing `@` opens a suggestion list anchored at the caret: arrows move through
|
|
18
18
|
it, Enter or Tab inserts, Escape closes. The query is read from the **text node** at the caret and
|
|
19
|
-
recognised by `mentionQuery()` from `@konce-pt/mention`
|
|
19
|
+
recognised by `mentionQuery()` from `@konce-pt/mention` - the same rules as in the Angular port.
|
|
20
20
|
An inserted mention is `<span class="kpt-mention" data-mention="id" contenteditable="false">@Name`
|
|
21
21
|
plus a non-breaking space: the id survives saving, the flag keeps the mention in one piece and the
|
|
22
22
|
space gives the caret a place to stand after it.
|
|
23
23
|
|
|
24
24
|
## Tools (KptRichTool)
|
|
25
|
-
Clipboard: `copy`, `paste` (sanitized), `pasteText`. Style: `heading` (a panel: Normal + H1
|
|
25
|
+
Clipboard: `copy`, `paste` (sanitized), `pasteText`. Style: `heading` (a panel: Normal + H1-H6).
|
|
26
26
|
Format: `bold`, `italic`, `underline`, `strike`, `ul`, `ol`, `indent`, `outdent`.
|
|
27
27
|
Colours: `foreColor`, `backColor` (a palette + a native colour input). Alignment: `alignLeft`,
|
|
28
28
|
`alignCenter`, `alignRight`, `alignJustify`. Plus `link`, `table`, `clear`, `html`.
|
|
@@ -30,43 +30,43 @@ Colours: `foreColor`, `backColor` (a palette + a native colour input). Alignment
|
|
|
30
30
|
## The editor is not rendered by React
|
|
31
31
|
`contentEditable` cannot be a React-rendered subtree: re-rendering would wipe the selection and the
|
|
32
32
|
caret on every keystroke. The value is written into the DOM by hand, only when it differs from what
|
|
33
|
-
is already there
|
|
33
|
+
is already there - so typing never triggers a rewrite, and sanitization runs solely on writes
|
|
34
34
|
coming from the application or the HTML source view.
|
|
35
35
|
|
|
36
36
|
## Everything that enters the DOM is sanitized
|
|
37
37
|
Not just paste: a `value` from a database, a CMS or another user is untrusted too. Tags outside the
|
|
38
38
|
allowlist are unwrapped (the text survives), `on*` handlers and `javascript:` URLs are dropped,
|
|
39
39
|
`style` is filtered declaration by declaration. `sanitize.ts` knows nothing about React.
|
|
40
|
-
Links keep only relative addresses and `http(s):`, `mailto:`, `tel:`
|
|
40
|
+
Links keep only relative addresses and `http(s):`, `mailto:`, `tel:` - the same allowlist as the link tool, so
|
|
41
41
|
`data:`, `vbscript:` or `blob:` from a paste are dropped. `class` keeps only the editor's own classes
|
|
42
42
|
(`kpt-rt-*`, `kpt-mention`), a link with `target` always gets `rel="noopener noreferrer"`, and when the
|
|
43
|
-
sanitizer removed something, the cleaned HTML is written back to `value`
|
|
43
|
+
sanitizer removed something, the cleaned HTML is written back to `value` - the model never holds the
|
|
44
44
|
unsafe original (a pure formatting difference does not touch it, so a clean value does not dirty a form).
|
|
45
45
|
|
|
46
46
|
## Lists are indented by our own DOM code
|
|
47
47
|
`execCommand('indent')` produces invalid `ul > ul` in Chrome, so nesting goes through `lists.ts`
|
|
48
48
|
(`li > ul > li`, a sublist inherits the parent's type). Tab and Shift+Tab indent and outdent, but
|
|
49
|
-
only when the operation actually changed something
|
|
49
|
+
only when the operation actually changed something - otherwise Tab stays native and the editor
|
|
50
50
|
never becomes a keyboard trap (WCAG 2.1.2). In a table cell Tab moves to the next cell; a list
|
|
51
51
|
inside the cell takes precedence.
|
|
52
52
|
|
|
53
53
|
## The toolbar is one Tab stop
|
|
54
54
|
The toolbar is a named `role="toolbar"` ("Formatting", `richText.toolbar`) with a roving tabindex:
|
|
55
|
-
|
|
55
|
+
`ArrowLeft`/`ArrowRight` (mirrored in RTL) and `Home`/`End` move between tools, skipping disabled ones; the stop
|
|
56
56
|
remembers the last tool, so Tab from the toolbar goes straight to the editor.
|
|
57
57
|
|
|
58
58
|
Tool panels open in the overlay container at the end of `<body>`, so focus moves into them:
|
|
59
|
-
the paragraph style list is a `listbox` (
|
|
60
|
-
the table grid is one stop (arrows change the size 1
|
|
61
|
-
the colour palette starts on the first swatch (
|
|
59
|
+
the paragraph style list is a `listbox` (`ArrowUp`/`ArrowDown`, `Home`/`End`, `Enter`/`Space` picks, `Escape`/`Tab` close);
|
|
60
|
+
the table grid is one stop (arrows change the size 1-8, the size is announced, `Enter` inserts);
|
|
61
|
+
the colour palette starts on the first swatch (`ArrowLeft`/`ArrowRight` between swatches, `Tab` cycles inside the
|
|
62
62
|
panel). `Escape` closes any of them and returns focus to the tool.
|
|
63
63
|
|
|
64
64
|
## Tables
|
|
65
|
-
Insert from an
|
|
65
|
+
Insert from an 8x8 grid popover. Dragging across cells makes a rectangular selection that grows
|
|
66
66
|
until it fully covers merged cells on its edges. Right-click opens the menu: insert and delete rows
|
|
67
|
-
and columns, header row and column (td
|
|
67
|
+
and columns, header row and column (td<->th), cell background, alignment, borders, merge and split,
|
|
68
68
|
delete the table. Dragging a column's right edge changes its width. `tables.ts` holds the grid
|
|
69
|
-
math
|
|
69
|
+
math - also framework-free.
|
|
70
70
|
|
|
71
71
|
## Point-anchored panels
|
|
72
72
|
The table menu and its popovers have no trigger element, so they do not go through `KptOverlay`:
|
|
@@ -98,8 +98,17 @@ their hex codes. The table size grid is still mouse-only.
|
|
|
98
98
|
`id` goes to the focusable element, not the host; without one it comes from `KptFormField` or is
|
|
99
99
|
generated. A `div` with a role is not named by `label[for]`, so inside `KptFormField` it gets
|
|
100
100
|
`aria-labelledby` pointing at the wrapper's label (unless `ariaLabel` is set), and the hint or error
|
|
101
|
-
is added to `aria-describedby`. New: `id`, `ariaLabel` and `required` (
|
|
101
|
+
is added to `aria-describedby`. New: `id`, `ariaLabel` and `required` (-> `aria-required`) on the
|
|
102
102
|
`role="textbox"` area.
|
|
103
103
|
|
|
104
104
|
## Read-only and required
|
|
105
105
|
`readOnly` keeps the content selectable and copyable but not editable (`aria-readonly`); the toolbar is disabled.
|
|
106
|
+
|
|
107
|
+
## Dictation
|
|
108
|
+
The `dictate` tool (not in the default toolbar) dictates at the caret: final text goes in through
|
|
109
|
+
`insertText` (native undo, `input` -> value); interim text is an overlay next to the caret
|
|
110
|
+
(`.kpt-speech-ghost`) and never enters the HTML. Hidden without browser support, stopped by the HTML
|
|
111
|
+
source view and by `disabled` / `readOnly`. Language: `speechLang` (BCP 47, default from
|
|
112
|
+
`KptI18nProvider`), listening colour: `speechColor` (theme colour by default).
|
|
113
|
+
|
|
114
|
+
<KptRichText value={body} onValueChange={setBody} toolbar={[['bold', 'italic'], ['dictate']]} />
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { type ComponentPropsWithRef } from 'react';
|
|
2
2
|
import { type KptMentionItem } from '@konce-pt/mention';
|
|
3
3
|
import { type KptI18nOverrides } from '../i18n/i18n-context.tsx';
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
import { type KptSpeechColor } from '../speech/speech-button.tsx';
|
|
5
|
+
export type KptRichTool = 'copy' | 'paste' | 'pasteText' | 'heading' | 'bold' | 'italic' | 'underline' | 'strike' | 'ul' | 'ol' | 'indent' | 'outdent' | 'foreColor' | 'backColor' | 'alignLeft' | 'alignCenter' | 'alignRight' | 'alignJustify' | 'link' | 'table' | 'clear' | 'html' | 'dictate';
|
|
6
|
+
/** Pasek: płaska lista narzędzi albo grupy - sekcje rozdzielone separatorem. */
|
|
6
7
|
export type KptRichToolbar = readonly KptRichTool[] | readonly (readonly KptRichTool[])[];
|
|
7
8
|
export interface KptRichTextProps extends Omit<ComponentPropsWithRef<'div'>, 'onChange' | 'children'>, KptI18nOverrides {
|
|
8
9
|
/** Wartość jako HTML. */
|
|
@@ -11,16 +12,16 @@ export interface KptRichTextProps extends Omit<ComponentPropsWithRef<'div'>, 'on
|
|
|
11
12
|
onValueChange?: (value: string) => void;
|
|
12
13
|
toolbar?: KptRichToolbar;
|
|
13
14
|
placeholder?: string;
|
|
14
|
-
/** Maksymalna głębokość zagnieżdżenia list (Tab,
|
|
15
|
+
/** Maksymalna głębokość zagnieżdżenia list (Tab, "Dodaj wcięcie"). */
|
|
15
16
|
maxListLevel?: number;
|
|
16
|
-
/** Id elementu fokusowalnego (nie hosta); bez niego generowane, w `KptFormField`
|
|
17
|
+
/** Id elementu fokusowalnego (nie hosta); bez niego generowane, w `KptFormField` - z wrappera. */
|
|
17
18
|
id?: string;
|
|
18
19
|
/** Nazwa dostępna, gdy kontrolki nie opisuje etykieta (`<label for>` albo `KptFormField`). */
|
|
19
20
|
ariaLabel?: string | null;
|
|
20
21
|
/** Oznacza pole jako wymagane atrybutem `aria-required`. */
|
|
21
22
|
required?: boolean;
|
|
22
23
|
disabled?: boolean;
|
|
23
|
-
/** Tylko do odczytu
|
|
24
|
+
/** Tylko do odczytu - treść da się zaznaczyć i skopiować, nie edytować (`aria-readonly`). */
|
|
24
25
|
readOnly?: boolean;
|
|
25
26
|
invalid?: boolean;
|
|
26
27
|
touched?: boolean;
|
|
@@ -32,7 +33,11 @@ export interface KptRichTextProps extends Omit<ComponentPropsWithRef<'div'>, 'on
|
|
|
32
33
|
mentions?: readonly KptMentionItem[];
|
|
33
34
|
/** Ile podpowiedzi naraz. */
|
|
34
35
|
mentionLimit?: number;
|
|
35
|
-
/** Wstawiona wzmianka
|
|
36
|
+
/** Wstawiona wzmianka - moment na powiadomienie osoby albo dopięcie powiązania. */
|
|
36
37
|
onMention?: (item: KptMentionItem) => void;
|
|
38
|
+
/** Język narzędzia `dictate` (BCP 47); bez niego z `KptI18nProvider`. */
|
|
39
|
+
speechLang?: string;
|
|
40
|
+
/** Kolor "stop" i fali w trakcie nasłuchu; domyślnie kolor motywu. */
|
|
41
|
+
speechColor?: KptSpeechColor;
|
|
37
42
|
}
|
|
38
|
-
export declare function KptRichText({ value, defaultValue, onValueChange, toolbar, placeholder, maxListLevel, id, ariaLabel, required, disabled: disabledProp, readOnly, invalid, touched, onTouch, mentions, mentionLimit, onMention, locale, dictionary, className, ...rest }: Readonly<KptRichTextProps>): import("react").JSX.Element;
|
|
43
|
+
export declare function KptRichText({ value, defaultValue, onValueChange, toolbar, placeholder, maxListLevel, id, ariaLabel, required, disabled: disabledProp, readOnly, invalid, touched, onTouch, mentions, mentionLimit, onMention, speechLang, speechColor, locale, dictionary, className, ...rest }: Readonly<KptRichTextProps>): import("react").JSX.Element;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
-
import { useCallback, useEffect, useRef, useState, } from 'react';
|
|
3
|
+
import { useCallback, useEffect, useEffectEvent, useRef, useState, } from 'react';
|
|
4
4
|
import { createPortal } from 'react-dom';
|
|
5
5
|
import { cn } from "../internal/cn.js";
|
|
6
6
|
import { useControllableState } from "../internal/use-controllable-state.js";
|
|
@@ -9,6 +9,8 @@ import { useKptOverlayContainer } from "../internal/overlay-container.js";
|
|
|
9
9
|
import { KptOverlay } from "../internal/overlay.js";
|
|
10
10
|
import { useKptMessages } from "../i18n/i18n-context.js";
|
|
11
11
|
import { KptIcon } from "../icon/icon.js";
|
|
12
|
+
import { KptSpeechButton } from "../speech/speech-button.js";
|
|
13
|
+
import { useKptSpeechRecognition } from "../speech/use-speech-recognition.js";
|
|
12
14
|
import { sanitizeHtml, sanitizeHtmlReport, sanitizeUrl } from "./sanitize.js";
|
|
13
15
|
import { canIndentItem, canOutdentItem, selectedItems, shiftItems } from "./lists.js";
|
|
14
16
|
import { CELL_SELECTED, deleteColumn, deleteRow, insertColumn, insertRow, mergeCells, regionBetween, splitCell, tableHtml, toggleHeaderColumn, toggleHeaderRow, } from "./tables.js";
|
|
@@ -20,7 +22,7 @@ const ICON = {
|
|
|
20
22
|
copy: 'copy',
|
|
21
23
|
paste: 'clipboard',
|
|
22
24
|
pasteText: 'clipboard-text',
|
|
23
|
-
heading: 'code', // nieużywane
|
|
25
|
+
heading: 'code', // nieużywane - "heading" renderuje select
|
|
24
26
|
bold: 'bold',
|
|
25
27
|
italic: 'italic',
|
|
26
28
|
underline: 'underline',
|
|
@@ -39,8 +41,9 @@ const ICON = {
|
|
|
39
41
|
table: 'table',
|
|
40
42
|
clear: 'clear-formatting',
|
|
41
43
|
html: 'code',
|
|
44
|
+
dictate: 'microphone', // nieużywane - "dictate" renderuje KptSpeechButton
|
|
42
45
|
};
|
|
43
|
-
/** Narzędzia zwykłe
|
|
46
|
+
/** Narzędzia zwykłe -> komenda `execCommand` (zarazem nazwa dla `queryCommandState`). */
|
|
44
47
|
const COMMAND = {
|
|
45
48
|
bold: 'bold',
|
|
46
49
|
italic: 'italic',
|
|
@@ -54,7 +57,7 @@ const COMMAND = {
|
|
|
54
57
|
alignJustify: 'justifyFull',
|
|
55
58
|
clear: 'removeFormat',
|
|
56
59
|
};
|
|
57
|
-
/** Paleta kolorów z nazwami (`richText.swatches.*`)
|
|
60
|
+
/** Paleta kolorów z nazwami (`richText.swatches.*`) - sam kod szesnastkowy nic czytnikowi nie mówi. */
|
|
58
61
|
const SWATCHES = [
|
|
59
62
|
{ color: '#000000', name: 'black' },
|
|
60
63
|
{ color: '#434343', name: 'darkGray' },
|
|
@@ -98,10 +101,10 @@ function legacyState(cmd) {
|
|
|
98
101
|
return false;
|
|
99
102
|
}
|
|
100
103
|
}
|
|
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
|
|
104
|
+
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, speechLang, speechColor, locale, dictionary, className, ...rest }) {
|
|
105
|
+
/** Edycja zablokowana: wyłączone pole albo tylko do odczytu - narzędzia i skróty nie działają. */
|
|
103
106
|
const disabled = disabledProp || readOnly;
|
|
104
|
-
/** `contenteditable` nie dostaje nazwy z `label[for]`
|
|
107
|
+
/** `contenteditable` nie dostaje nazwy z `label[for]` - stąd `aria-labelledby` na etykietę wrappera. */
|
|
105
108
|
const field = useKptFieldA11y(id, 'kpt-rich-text');
|
|
106
109
|
const { t } = useKptMessages({ locale, dictionary });
|
|
107
110
|
const container = useKptOverlayContainer();
|
|
@@ -109,7 +112,7 @@ export function KptRichText({ value, defaultValue = '', onValueChange, toolbar =
|
|
|
109
112
|
const savedRange = useRef(null);
|
|
110
113
|
const [current, setCurrent] = useControllableState({ value, defaultValue, onChange: onValueChange });
|
|
111
114
|
const showInvalid = invalid && (touched ?? true);
|
|
112
|
-
// Kotwice narzędzi z panelem
|
|
115
|
+
// Kotwice narzędzi z panelem - `KptOverlay` przypina panel do konkretnego przycisku.
|
|
113
116
|
const [styleAnchor, setStyleAnchor] = useState(null);
|
|
114
117
|
const [foreAnchor, setForeAnchor] = useState(null);
|
|
115
118
|
const [backAnchor, setBackAnchor] = useState(null);
|
|
@@ -118,10 +121,10 @@ export function KptRichText({ value, defaultValue = '', onValueChange, toolbar =
|
|
|
118
121
|
/** Okienko adresu linku (zamiast `window.prompt()`) i wpisywany adres. */
|
|
119
122
|
const [linkOpen, setLinkOpen] = useState(false);
|
|
120
123
|
const [linkDraft, setLinkDraft] = useState('');
|
|
121
|
-
/** Menu tabeli otwarte z klawiatury
|
|
124
|
+
/** Menu tabeli otwarte z klawiatury - po zamontowaniu fokus idzie na pierwszą pozycję. */
|
|
122
125
|
const menuFromKeyboard = useRef(false);
|
|
123
126
|
const [styleOpen, setStyleOpen] = useState(false);
|
|
124
|
-
/** Narzędzie, na którym ostatnio stał fokus paska
|
|
127
|
+
/** Narzędzie, na którym ostatnio stał fokus paska - przystanek Tab przy powrocie. */
|
|
125
128
|
const [lastTool, setLastTool] = useState(null);
|
|
126
129
|
const [colorOpen, setColorOpen] = useState(false);
|
|
127
130
|
const [colorKind, setColorKind] = useState('foreColor');
|
|
@@ -132,13 +135,13 @@ export function KptRichText({ value, defaultValue = '', onValueChange, toolbar =
|
|
|
132
135
|
const [sourceView, setSourceView] = useState(false);
|
|
133
136
|
const [menuOpen, setMenuOpen] = useState(false);
|
|
134
137
|
const [menuPoint, setMenuPoint] = useState({ x: 0, y: 0 });
|
|
135
|
-
//
|
|
138
|
+
// -- Wzmianki (`@`) --
|
|
136
139
|
const [mentionOpen, setMentionOpen] = useState(false);
|
|
137
140
|
const [mentionMatches, setMentionMatches] = useState([]);
|
|
138
141
|
const [mentionActive, setMentionActive] = useState(0);
|
|
139
|
-
/** Zaczepienie listy w miejscu kursora
|
|
142
|
+
/** Zaczepienie listy w miejscu kursora - punkt, nie element, bo `@` nie ma własnego elementu. */
|
|
140
143
|
const [mentionPoint, setMentionPoint] = useState({ x: 0, y: 0 });
|
|
141
|
-
/** Węzeł tekstowy i zakres zapytania
|
|
144
|
+
/** Węzeł tekstowy i zakres zapytania `@...`, który podmieni wybrana pozycja. */
|
|
142
145
|
const mentionRange = useRef(null);
|
|
143
146
|
const [borderOpen, setBorderOpen] = useState(false);
|
|
144
147
|
const [borderTarget, setBorderTarget] = useState('table');
|
|
@@ -158,6 +161,18 @@ export function KptRichText({ value, defaultValue = '', onValueChange, toolbar =
|
|
|
158
161
|
: Array.isArray(toolbar[0])
|
|
159
162
|
? toolbar
|
|
160
163
|
: [toolbar];
|
|
164
|
+
// -- Dyktowanie (narzędzie `dictate`) --
|
|
165
|
+
// Cel to sam obszar edycji: tekst zatwierdzony wchodzi przez `insertText` w miejsce kursora
|
|
166
|
+
// (natywne undo, zdarzenie `input` -> wartość), tymczasowy jest nakładką przy kursorze i nie trafia
|
|
167
|
+
// do HTML-a. Bez narzędzia w pasku kontroler nie tworzy sesji.
|
|
168
|
+
const hasDictate = sections.some((section) => section.includes('dictate'));
|
|
169
|
+
const mic = useKptSpeechRecognition({ enabled: hasDictate, target: editorRef, lang: speechLang, disabled });
|
|
170
|
+
// Widok źródła zdejmuje obszar edycji, a blokada zabrania pisania - dyktowanie przerywamy.
|
|
171
|
+
const stopDictation = useEffectEvent(() => mic.abort());
|
|
172
|
+
useEffect(() => {
|
|
173
|
+
if (sourceView || disabled)
|
|
174
|
+
stopDictation();
|
|
175
|
+
}, [sourceView, disabled]);
|
|
161
176
|
const pushValue = useCallback(() => {
|
|
162
177
|
const el = editorRef.current;
|
|
163
178
|
if (el)
|
|
@@ -165,10 +180,10 @@ export function KptRichText({ value, defaultValue = '', onValueChange, toolbar =
|
|
|
165
180
|
}, [setCurrent]);
|
|
166
181
|
/*
|
|
167
182
|
* Zapis z zewnątrz wstawiamy ręcznie, bo `contenteditable` nie może być renderowany przez
|
|
168
|
-
* Reacta
|
|
183
|
+
* Reacta - przerysowanie kasowałoby zaznaczenie i pozycję kursora przy każdym znaku.
|
|
169
184
|
* Porównanie z surowym `innerHTML` sprawia, że pisanie w edytorze nie wchodzi w tę gałąź,
|
|
170
185
|
* 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
|
|
186
|
+
* sanityzator coś usunął, oczyszczona treść wraca też do wartości - inaczej aplikacja dostawałaby
|
|
172
187
|
* z powrotem niebezpieczny HTML. Sama różnica zapisu (cudzysłowy) wartości nie rusza.
|
|
173
188
|
*/
|
|
174
189
|
useEffect(() => {
|
|
@@ -185,7 +200,7 @@ export function KptRichText({ value, defaultValue = '', onValueChange, toolbar =
|
|
|
185
200
|
const selection = window.getSelection();
|
|
186
201
|
return !!el && !!selection && selection.rangeCount > 0 && el.contains(selection.anchorNode);
|
|
187
202
|
}, []);
|
|
188
|
-
/** Tag bloku, w którym stoi kursor
|
|
203
|
+
/** Tag bloku, w którym stoi kursor - dla selecta stylu; domyślnie paragraf. */
|
|
189
204
|
const blockTag = useCallback(() => {
|
|
190
205
|
let node = window.getSelection()?.anchorNode;
|
|
191
206
|
while (node && node !== editorRef.current) {
|
|
@@ -216,19 +231,19 @@ export function KptRichText({ value, defaultValue = '', onValueChange, toolbar =
|
|
|
216
231
|
selCells.current = new Set();
|
|
217
232
|
setSelCount(0);
|
|
218
233
|
}, []);
|
|
219
|
-
/** Zamknięcie podpowiedzi
|
|
234
|
+
/** Zamknięcie podpowiedzi - czyści też zapamiętany zakres zapytania. */
|
|
220
235
|
const closeMention = useCallback(() => {
|
|
221
236
|
mentionRange.current = null;
|
|
222
237
|
setMentionMatches([]);
|
|
223
238
|
setMentionOpen(false);
|
|
224
239
|
}, []);
|
|
225
|
-
/** Punkt pod kursorem
|
|
240
|
+
/** Punkt pod kursorem - lista podpowiedzi zaczepia się o niego, bo `@` nie ma swojego elementu. */
|
|
226
241
|
const caretPoint = useCallback((node, offset) => {
|
|
227
242
|
const range = document.createRange();
|
|
228
243
|
range.setStart(node, Math.min(offset, node.length));
|
|
229
244
|
range.collapse(true);
|
|
230
245
|
const rect = range.getBoundingClientRect();
|
|
231
|
-
// Zwinięty zakres na początku wiersza potrafi dać zerowy prostokąt
|
|
246
|
+
// Zwinięty zakres na początku wiersza potrafi dać zerowy prostokąt - wtedy bierzemy
|
|
232
247
|
// krawędź samego edytora, żeby lista nie wylądowała w rogu ekranu.
|
|
233
248
|
if (rect.width === 0 && rect.height === 0) {
|
|
234
249
|
const fallback = editorRef.current?.getBoundingClientRect();
|
|
@@ -237,11 +252,11 @@ export function KptRichText({ value, defaultValue = '', onValueChange, toolbar =
|
|
|
237
252
|
return { x: rect.left, y: rect.bottom };
|
|
238
253
|
}, []);
|
|
239
254
|
/**
|
|
240
|
-
* Sprawdza, czy kursor stoi w zapytaniu
|
|
255
|
+
* Sprawdza, czy kursor stoi w zapytaniu `@...`, i ustawia listę podpowiedzi.
|
|
241
256
|
*
|
|
242
257
|
* Zapytanie liczymy z **węzła tekstowego**, nie z całego HTML-a: w treści siedzą tagi, a `@`
|
|
243
258
|
* ma widzieć tylko to, co użytkownik naprawdę napisał w tym miejscu. Rozpoznanie zostawiamy
|
|
244
|
-
* `mentionQuery()` z `@konce-pt/mention`
|
|
259
|
+
* `mentionQuery()` z `@konce-pt/mention` - te same reguły działają w obu portach.
|
|
245
260
|
*/
|
|
246
261
|
const syncMention = useCallback(() => {
|
|
247
262
|
if (disabled || !mentions.length) {
|
|
@@ -271,10 +286,10 @@ export function KptRichText({ value, defaultValue = '', onValueChange, toolbar =
|
|
|
271
286
|
setMentionOpen(true);
|
|
272
287
|
}, [caretPoint, closeMention, disabled, mentionLimit, mentions]);
|
|
273
288
|
/**
|
|
274
|
-
* Wstawia wzmiankę w miejsce zapytania
|
|
289
|
+
* Wstawia wzmiankę w miejsce zapytania `@...`.
|
|
275
290
|
*
|
|
276
291
|
* Wzmianka jest elementem, nie samym tekstem: `data-mention` niesie identyfikator, który
|
|
277
|
-
* przeżyje zmianę nazwy, a `contenteditable="false"` trzyma ją w jednym kawałku
|
|
292
|
+
* przeżyje zmianę nazwy, a `contenteditable="false"` trzyma ją w jednym kawałku - bez tego
|
|
278
293
|
* kursor wchodziłby w środek nazwiska i psuł zapis. Spacja po wzmiance daje gdzie postawić
|
|
279
294
|
* kursor: bez niej nie da się wyjść poza element, który nie jest edytowalny.
|
|
280
295
|
*/
|
|
@@ -309,7 +324,7 @@ export function KptRichText({ value, defaultValue = '', onValueChange, toolbar =
|
|
|
309
324
|
useEffect(() => {
|
|
310
325
|
const onSelectionChange = () => {
|
|
311
326
|
refreshState();
|
|
312
|
-
// Kursor może odjechać od `@` także bez pisania (klik, strzałki)
|
|
327
|
+
// Kursor może odjechać od `@` także bez pisania (klik, strzałki) - wtedy podpowiedzi
|
|
313
328
|
// muszą zniknąć, inaczej Enter wstawiłby wzmiankę w zupełnie innym miejscu.
|
|
314
329
|
syncMention();
|
|
315
330
|
};
|
|
@@ -374,7 +389,7 @@ export function KptRichText({ value, defaultValue = '', onValueChange, toolbar =
|
|
|
374
389
|
selection.addRange(savedRange.current);
|
|
375
390
|
}
|
|
376
391
|
};
|
|
377
|
-
//
|
|
392
|
+
// -- Schowek --
|
|
378
393
|
const copySelection = async () => {
|
|
379
394
|
const selection = window.getSelection();
|
|
380
395
|
if (!selection || selection.rangeCount === 0 || selection.isCollapsed)
|
|
@@ -411,7 +426,7 @@ export function KptRichText({ value, defaultValue = '', onValueChange, toolbar =
|
|
|
411
426
|
pushValue();
|
|
412
427
|
}
|
|
413
428
|
catch {
|
|
414
|
-
/* brak uprawnień albo API
|
|
429
|
+
/* brak uprawnień albo API - ignorujemy */
|
|
415
430
|
}
|
|
416
431
|
};
|
|
417
432
|
const pastePlain = async () => {
|
|
@@ -422,10 +437,10 @@ export function KptRichText({ value, defaultValue = '', onValueChange, toolbar =
|
|
|
422
437
|
pushValue();
|
|
423
438
|
}
|
|
424
439
|
catch {
|
|
425
|
-
/* brak uprawnień albo API
|
|
440
|
+
/* brak uprawnień albo API - ignorujemy */
|
|
426
441
|
}
|
|
427
442
|
};
|
|
428
|
-
//
|
|
443
|
+
// -- Narzędzia --
|
|
429
444
|
const exec = (item) => {
|
|
430
445
|
if (disabled)
|
|
431
446
|
return;
|
|
@@ -470,7 +485,7 @@ export function KptRichText({ value, defaultValue = '', onValueChange, toolbar =
|
|
|
470
485
|
};
|
|
471
486
|
/**
|
|
472
487
|
* Tab: wcięcie w liście, przeskok komórki w tabeli. Poza nimi i na krawędziach zostawiamy Tab
|
|
473
|
-
* natywny
|
|
488
|
+
* natywny - inaczej edytor byłby pułapką klawiaturową (WCAG 2.1.2).
|
|
474
489
|
*/
|
|
475
490
|
/** Klawiatura listy podpowiedzi; `true` = zdarzenie obsłużone i edytor go już nie zobaczy. */
|
|
476
491
|
const onMentionKeyDown = (event) => {
|
|
@@ -531,7 +546,7 @@ export function KptRichText({ value, defaultValue = '', onValueChange, toolbar =
|
|
|
531
546
|
const cells = [...table.querySelectorAll('td, th')];
|
|
532
547
|
const next = cells[cells.indexOf(cell) + (event.shiftKey ? -1 : 1)];
|
|
533
548
|
if (!next)
|
|
534
|
-
return; // koniec tabeli
|
|
549
|
+
return; // koniec tabeli - Tab zostaje natywny
|
|
535
550
|
event.preventDefault();
|
|
536
551
|
const range = document.createRange();
|
|
537
552
|
range.selectNodeContents(next);
|
|
@@ -541,7 +556,7 @@ export function KptRichText({ value, defaultValue = '', onValueChange, toolbar =
|
|
|
541
556
|
clearCellSelection();
|
|
542
557
|
refreshState();
|
|
543
558
|
};
|
|
544
|
-
/** Natywne wklejanie
|
|
559
|
+
/** Natywne wklejanie - czyścimy treść, żeby nie wnosić śmieci z edytorów tekstu i stron. */
|
|
545
560
|
const onPaste = (event) => {
|
|
546
561
|
if (disabled)
|
|
547
562
|
return;
|
|
@@ -554,7 +569,7 @@ export function KptRichText({ value, defaultValue = '', onValueChange, toolbar =
|
|
|
554
569
|
legacyExec('insertText', text);
|
|
555
570
|
pushValue();
|
|
556
571
|
};
|
|
557
|
-
//
|
|
572
|
+
// -- Kolory --
|
|
558
573
|
const activeCells = () => {
|
|
559
574
|
if (selCells.current.size)
|
|
560
575
|
return [...selCells.current];
|
|
@@ -576,7 +591,7 @@ export function KptRichText({ value, defaultValue = '', onValueChange, toolbar =
|
|
|
576
591
|
legacyExec('backColor', color);
|
|
577
592
|
pushValue();
|
|
578
593
|
};
|
|
579
|
-
//
|
|
594
|
+
// -- Tabele --
|
|
580
595
|
const pickStyle = (tag) => {
|
|
581
596
|
setStyleOpen(false);
|
|
582
597
|
focusEditor();
|
|
@@ -585,7 +600,7 @@ export function KptRichText({ value, defaultValue = '', onValueChange, toolbar =
|
|
|
585
600
|
pushValue();
|
|
586
601
|
refreshState();
|
|
587
602
|
};
|
|
588
|
-
/** Siatka tabeli z klawiatury: strzałki zmieniają rozmiar (1
|
|
603
|
+
/** Siatka tabeli z klawiatury: strzałki zmieniają rozmiar (1-8), Enter wstawia, Escape wraca. */
|
|
589
604
|
const onGridKeyDown = (event) => {
|
|
590
605
|
const clamp = (n) => Math.min(GRID_ROWS.length, Math.max(1, n));
|
|
591
606
|
const r = grid.r || 1;
|
|
@@ -621,7 +636,7 @@ export function KptRichText({ value, defaultValue = '', onValueChange, toolbar =
|
|
|
621
636
|
const afterTableOp = () => {
|
|
622
637
|
const fromMenu = !!document.activeElement?.closest('.kpt-rt-menu');
|
|
623
638
|
setMenuOpen(false);
|
|
624
|
-
// Pozycja wybrana z klawiatury znika razem z menu
|
|
639
|
+
// Pozycja wybrana z klawiatury znika razem z menu - fokus wraca do edytora, nie na <body>.
|
|
625
640
|
if (fromMenu)
|
|
626
641
|
focusEditor();
|
|
627
642
|
pushValue();
|
|
@@ -637,7 +652,7 @@ export function KptRichText({ value, defaultValue = '', onValueChange, toolbar =
|
|
|
637
652
|
if (handleMenuKeydown(event.currentTarget, event.nativeEvent) === 'close')
|
|
638
653
|
closeMenuToEditor();
|
|
639
654
|
};
|
|
640
|
-
/** Menu tabeli z klawiatury: klawisz menu albo `Shift+F10` w komórce
|
|
655
|
+
/** Menu tabeli z klawiatury: klawisz menu albo `Shift+F10` w komórce - jak natywne menu kontekstowe. */
|
|
641
656
|
const openMenuFromKeyboard = () => {
|
|
642
657
|
const node = window.getSelection()?.anchorNode ?? null;
|
|
643
658
|
const el = node instanceof Element ? node : (node?.parentElement ?? null);
|
|
@@ -653,7 +668,7 @@ export function KptRichText({ value, defaultValue = '', onValueChange, toolbar =
|
|
|
653
668
|
setMenuOpen(true);
|
|
654
669
|
return true;
|
|
655
670
|
};
|
|
656
|
-
/** Okienko adresu zamiast `window.prompt()`
|
|
671
|
+
/** Okienko adresu zamiast `window.prompt()` - nazwane pole, `Enter` wstawia, `Escape` wraca do tekstu. */
|
|
657
672
|
const openLink = () => {
|
|
658
673
|
if (disabled)
|
|
659
674
|
return;
|
|
@@ -753,7 +768,7 @@ export function KptRichText({ value, defaultValue = '', onValueChange, toolbar =
|
|
|
753
768
|
}
|
|
754
769
|
pushValue();
|
|
755
770
|
};
|
|
756
|
-
//
|
|
771
|
+
// -- Prezentacja narzędzi --
|
|
757
772
|
const styleOptions = [
|
|
758
773
|
{ tag: 'p', label: t('richText.styleNormal') },
|
|
759
774
|
...[1, 2, 3, 4, 5, 6].map((level) => ({
|
|
@@ -768,17 +783,19 @@ export function KptRichText({ value, defaultValue = '', onValueChange, toolbar =
|
|
|
768
783
|
const cmd = COMMAND[item];
|
|
769
784
|
return !!cmd && activeCmds.has(cmd);
|
|
770
785
|
};
|
|
771
|
-
/** `aria-pressed` tylko dla przełączników
|
|
786
|
+
/** `aria-pressed` tylko dla przełączników - wcięcia są akcjami, nie stanem. */
|
|
772
787
|
const pressed = (item) => item === 'indent' || item === 'outdent' ? undefined : isActive(item);
|
|
773
788
|
const enabled = (item) => {
|
|
774
789
|
if (item === 'indent')
|
|
775
790
|
return canIndent;
|
|
776
791
|
if (item === 'outdent')
|
|
777
792
|
return canOutdent;
|
|
793
|
+
if (item === 'dictate')
|
|
794
|
+
return mic.available && !sourceView;
|
|
778
795
|
return true;
|
|
779
796
|
};
|
|
780
797
|
/**
|
|
781
|
-
* Panele narzędzi żyją w nakładce na końcu `<body>`
|
|
798
|
+
* Panele narzędzi żyją w nakładce na końcu `<body>` - po zamontowaniu fokus wchodzi do środka,
|
|
782
799
|
* inaczej Tab musiałby przejść przez całą stronę, żeby do nich dojść (WCAG 2.1.1).
|
|
783
800
|
*/
|
|
784
801
|
// Refy stabilne (`useCallback`): nowa funkcja przy każdym renderze byłaby wołana od nowa
|
|
@@ -826,7 +843,7 @@ export function KptRichText({ value, defaultValue = '', onValueChange, toolbar =
|
|
|
826
843
|
};
|
|
827
844
|
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) })] })] }));
|
|
828
845
|
/**
|
|
829
|
-
* Panel otwarty przy dolnej albo prawej krawędzi okna wychodziłby poza ekran
|
|
846
|
+
* Panel otwarty przy dolnej albo prawej krawędzi okna wychodziłby poza ekran - CDK robił to
|
|
830
847
|
* w Angularze sam, tutaj przesuwamy go po zamontowaniu, gdy już znać jego rozmiar.
|
|
831
848
|
*/
|
|
832
849
|
const clampToViewport = (el) => {
|
|
@@ -839,13 +856,13 @@ export function KptRichText({ value, defaultValue = '', onValueChange, toolbar =
|
|
|
839
856
|
el.style.top = `${top}px`;
|
|
840
857
|
};
|
|
841
858
|
/**
|
|
842
|
-
* Panel przypięty do punktu kursora
|
|
859
|
+
* Panel przypięty do punktu kursora - menu tabeli i jego popovery nie mają wyzwalacza, więc
|
|
843
860
|
* nie przechodzą przez `KptOverlay`; pozycję niosą współrzędne kliknięcia, tak jak
|
|
844
861
|
* w `KptContextMenu`.
|
|
845
862
|
*/
|
|
846
863
|
const pointPanel = (content, onClose) => {
|
|
847
864
|
if (!container)
|
|
848
|
-
return null; // render na serwerze
|
|
865
|
+
return null; // render na serwerze - nie ma czego portalować
|
|
849
866
|
const layer = { zIndex: 'var(--kpt-z-overlay, 1100)' };
|
|
850
867
|
return createPortal(_jsxs(_Fragment, { children: [_jsx("div", { style: { position: 'fixed', inset: 0, ...layer }, onClick: onClose, onContextMenu: (event) => {
|
|
851
868
|
event.preventDefault();
|
|
@@ -853,33 +870,35 @@ export function KptRichText({ value, defaultValue = '', onValueChange, toolbar =
|
|
|
853
870
|
} }), _jsx("div", { ref: clampToViewport, style: { position: 'fixed', left: menuPoint.x, top: menuPoint.y, ...layer }, children: content })] }), container);
|
|
854
871
|
};
|
|
855
872
|
/**
|
|
856
|
-
* Przystanek Tab paska: ostatnio użyte narzędzie, a gdy jest wyłączone albo go nie ma
|
|
873
|
+
* Przystanek Tab paska: ostatnio użyte narzędzie, a gdy jest wyłączone albo go nie ma -
|
|
857
874
|
* pierwsze dostępne. Dzięki temu `Tab` z paska idzie od razu do edytora, a nie przez kilkanaście
|
|
858
875
|
* przycisków.
|
|
859
876
|
*/
|
|
860
877
|
const usableTools = sections.flat().filter((item) => !disabled && enabled(item));
|
|
861
878
|
const toolStop = lastTool && usableTools.includes(lastTool) ? lastTool : (usableTools[0] ?? null);
|
|
862
879
|
const rovingProps = (item) => ({ 'data-tool': item, tabIndex: item === toolStop ? 0 : -1 });
|
|
863
|
-
/** Strzałki po pasku (w RTL odwrócone), `Home`/`End`
|
|
880
|
+
/** Strzałki po pasku (w RTL odwrócone), `Home`/`End` - skrajne; wyłączone narzędzia są pomijane. */
|
|
864
881
|
const onToolbarKeyDown = (event) => {
|
|
865
882
|
const bar = event.currentTarget;
|
|
866
|
-
const
|
|
883
|
+
const tools = [...bar.querySelectorAll('[data-tool]')];
|
|
884
|
+
// Narzędzie może być komponentem (`dictate` to KptSpeechButton) - fokus dostaje jego przycisk.
|
|
885
|
+
const buttons = tools.map((tool) => (tool instanceof HTMLButtonElement ? tool : tool.querySelector('button')));
|
|
867
886
|
const current = buttons.indexOf(event.target);
|
|
868
887
|
if (current < 0)
|
|
869
888
|
return;
|
|
870
889
|
const rtl = getComputedStyle(bar).direction === 'rtl';
|
|
871
890
|
const key = rtl && event.key === 'ArrowLeft' ? 'ArrowRight' : rtl && event.key === 'ArrowRight' ? 'ArrowLeft' : event.key;
|
|
872
|
-
const next = nextRovingIndex(buttons.map((button) => button.disabled), current, key);
|
|
891
|
+
const next = nextRovingIndex(buttons.map((button) => !button || button.disabled), current, key);
|
|
873
892
|
if (next === null)
|
|
874
893
|
return;
|
|
875
894
|
event.preventDefault();
|
|
876
|
-
setLastTool(
|
|
877
|
-
buttons[next]
|
|
895
|
+
setLastTool(tools[next].dataset['tool']);
|
|
896
|
+
buttons[next]?.focus();
|
|
878
897
|
};
|
|
879
898
|
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));
|
|
880
899
|
/**
|
|
881
900
|
* Akcje menu tabeli po identyfikatorze. Handlery żyją tutaj, a nie jako domknięcia przekazywane
|
|
882
|
-
* do `menuItem()`
|
|
901
|
+
* do `menuItem()` - funkcja wołana w renderze nie dostaje wtedy niczego, co czyta refy.
|
|
883
902
|
*/
|
|
884
903
|
const runTableAction = (action) => {
|
|
885
904
|
switch (action) {
|
|
@@ -1004,6 +1023,11 @@ export function KptRichText({ value, defaultValue = '', onValueChange, toolbar =
|
|
|
1004
1023
|
if (item === 'table') {
|
|
1005
1024
|
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));
|
|
1006
1025
|
}
|
|
1026
|
+
if (item === 'dictate') {
|
|
1027
|
+
// `data-tool` na hoście - roving znajduje narzędzie, a fokus dostaje jego przycisk.
|
|
1028
|
+
const host = { className: 'kpt-rich-text__dictate', 'data-tool': item };
|
|
1029
|
+
return (_jsx(KptSpeechButton, { listeningColor: speechColor, speech: mic, size: "sm", unsupported: "hide", wavePosition: "end", tabIndex: item === toolStop ? 0 : -1, disabled: disabled || sourceView, hostProps: host }, item));
|
|
1030
|
+
}
|
|
1007
1031
|
return toolButton(item);
|
|
1008
1032
|
})] }, 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
1033
|
// Wyłączony edytor (contentEditable=false) wypada z kolejności Tab jak wyłączone pole.
|
|
@@ -1023,7 +1047,7 @@ export function KptRichText({ value, defaultValue = '', onValueChange, toolbar =
|
|
|
1023
1047
|
insertMention(item);
|
|
1024
1048
|
}, 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)
|
|
1025
1049
|
: null, _jsx(KptOverlay, { open: linkOpen, anchor: linkAnchor, onClose: () => closeLink(false), offset: 4, children: _jsxs("form", { className: "kpt-rt-link", role: "dialog",
|
|
1026
|
-
// Bez walidacji przeglądarki:
|
|
1050
|
+
// Bez walidacji przeglądarki: "example.com" jest poprawnym wpisem - `sanitizeUrl` dopisze https://.
|
|
1027
1051
|
noValidate: true, "aria-label": t('richText.link'), onSubmit: (event) => {
|
|
1028
1052
|
event.preventDefault();
|
|
1029
1053
|
applyLink();
|
|
@@ -1034,7 +1058,7 @@ export function KptRichText({ value, defaultValue = '', onValueChange, toolbar =
|
|
|
1034
1058
|
event.stopPropagation();
|
|
1035
1059
|
closeLink(true);
|
|
1036
1060
|
}, children: [_jsx("label", { className: "kpt-rt-link__label", htmlFor: `${field.id}-link`, children: t('richText.linkPrompt') }), _jsx("input", { ref: (el) => {
|
|
1037
|
-
// Panel jest niewidoczny do pierwszego pomiaru
|
|
1061
|
+
// Panel jest niewidoczny do pierwszego pomiaru - fokus po klatce.
|
|
1038
1062
|
if (el)
|
|
1039
1063
|
requestAnimationFrame(() => el.focus());
|
|
1040
1064
|
}, 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) => {
|
|
@@ -1061,9 +1085,9 @@ export function KptRichText({ value, defaultValue = '', onValueChange, toolbar =
|
|
|
1061
1085
|
setTableOpen(false);
|
|
1062
1086
|
setGrid({ r: 0, c: 0 });
|
|
1063
1087
|
}, 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)
|
|
1088
|
+
// Jeden przystanek ze strzałkami (rozmiar tabeli) - grupa z własną obsługą klawiatury.
|
|
1065
1089
|
// 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, "
|
|
1090
|
+
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, " x ", grid.c] })] }) }), menuOpen
|
|
1067
1091
|
? pointPanel(_jsxs("div", { ref: (el) => {
|
|
1068
1092
|
if (el && menuFromKeyboard.current) {
|
|
1069
1093
|
menuFromKeyboard.current = false;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Czyści HTML i mówi, czy coś usunął. `changed: false` znaczy, że treść była czysta
|
|
2
|
+
* Czyści HTML i mówi, czy coś usunął. `changed: false` znaczy, że treść była czysta - wtedy
|
|
3
3
|
* wołający nie musi nadpisywać wartości modelu (serializacja DOM potrafi zmienić zapis, np.
|
|
4
4
|
* cudzysłowy, bez zmiany treści, a taka różnica nie może brudzić formularza).
|
|
5
5
|
*/
|
|
@@ -13,7 +13,7 @@ export declare function sanitizeHtmlReport(html: string): {
|
|
|
13
13
|
*/
|
|
14
14
|
export declare function sanitizeHtml(html: string): string;
|
|
15
15
|
/**
|
|
16
|
-
* Waliduje URL z narzędzia
|
|
17
|
-
* bez zmian; schemat spoza allowlisty
|
|
16
|
+
* Waliduje URL z narzędzia "link". Brak schematu -> dopisujemy `https://`; `#` i `/` przechodzą
|
|
17
|
+
* bez zmian; schemat spoza allowlisty -> `null`, czyli link nie zostaje wstawiony.
|
|
18
18
|
*/
|
|
19
19
|
export declare function sanitizeUrl(url: string): string | null;
|