@konce-pt/react 0.11.2 → 0.14.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 +128 -64
- package/dist/accordion/accordion.d.ts +14 -6
- package/dist/accordion/accordion.js +25 -17
- package/dist/accordion/llms.txt +22 -10
- package/dist/alert/alert.d.ts +2 -2
- package/dist/alert/alert.js +3 -2
- package/dist/alert/llms.txt +10 -9
- package/dist/app-shell/app-shell.d.ts +14 -14
- package/dist/app-shell/app-shell.js +22 -15
- package/dist/app-shell/llms.txt +16 -15
- package/dist/auth/auth-types.d.ts +6 -6
- package/dist/auth/auth.d.ts +2 -2
- package/dist/auth/auth.js +24 -15
- package/dist/auth/llms.txt +12 -11
- package/dist/autocomplete/autocomplete.d.ts +19 -6
- package/dist/autocomplete/autocomplete.js +32 -11
- package/dist/autocomplete/llms.txt +19 -5
- package/dist/avatar/avatar.d.ts +6 -6
- package/dist/avatar/avatar.js +6 -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 +4 -3
- package/dist/avatar-group/llms.txt +8 -8
- package/dist/backdrop/backdrop.d.ts +7 -26
- package/dist/backdrop/backdrop.js +32 -27
- 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 +8 -3
- package/dist/bottom-sheet/bottom-sheet.js +5 -4
- package/dist/bottom-sheet/llms.txt +11 -8
- package/dist/breadcrumb/breadcrumb.d.ts +8 -2
- package/dist/breadcrumb/breadcrumb.js +11 -3
- package/dist/breadcrumb/llms.txt +10 -2
- package/dist/button/button.d.ts +3 -3
- package/dist/button/button.js +10 -9
- package/dist/button/llms.txt +27 -19
- package/dist/button-group/button-group.d.ts +18 -5
- package/dist/button-group/button-group.js +54 -6
- package/dist/button-group/llms.txt +27 -18
- package/dist/card/card.d.ts +8 -8
- package/dist/card/card.js +10 -9
- package/dist/card/llms.txt +20 -20
- package/dist/carousel/carousel.d.ts +6 -6
- package/dist/carousel/carousel.js +8 -5
- package/dist/carousel/llms.txt +10 -10
- package/dist/charts/chart-legend.d.ts +3 -3
- package/dist/charts/chart-legend.js +2 -1
- package/dist/charts/chart-tooltip.d.ts +3 -3
- package/dist/charts/chart-tooltip.js +1 -1
- package/dist/charts/chart.d.ts +4 -3
- package/dist/charts/chart.js +18 -10
- package/dist/charts/llms.txt +10 -7
- package/dist/charts-entry/public-api.js +2 -2
- package/dist/chat/chat.d.ts +17 -10
- package/dist/chat/chat.js +77 -23
- package/dist/chat/llms.txt +39 -15
- package/dist/chat-composer/chat-composer.d.ts +20 -7
- package/dist/chat-composer/chat-composer.js +29 -7
- 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 +10 -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 +19 -8
- 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 +4 -3
- package/dist/chat-typing/llms.txt +6 -6
- package/dist/checkbox/checkbox.d.ts +13 -6
- package/dist/checkbox/checkbox.js +31 -5
- package/dist/checkbox/llms.txt +26 -11
- package/dist/chip/chip.d.ts +4 -4
- package/dist/chip/chip.js +3 -2
- package/dist/chip/llms.txt +11 -11
- package/dist/chips-input/chips-input.d.ts +6 -4
- package/dist/chips-input/chips-input.js +47 -12
- package/dist/chips-input/llms.txt +18 -8
- package/dist/clock/clock.d.ts +11 -6
- package/dist/clock/clock.js +38 -8
- package/dist/clock/llms.txt +12 -8
- package/dist/color-picker/color-picker.d.ts +9 -5
- package/dist/color-picker/color-picker.js +37 -18
- package/dist/color-picker/llms.txt +8 -5
- package/dist/confirm/confirm-container.d.ts +3 -3
- package/dist/confirm/confirm-container.js +6 -5
- package/dist/confirm/confirm-store.d.ts +4 -4
- package/dist/confirm/confirm-store.js +11 -5
- package/dist/confirm/llms.txt +8 -7
- package/dist/context-menu/context-menu.d.ts +1 -1
- package/dist/context-menu/context-menu.js +37 -14
- package/dist/context-menu/llms.txt +10 -7
- package/dist/data-table/data-table-types.d.ts +5 -5
- package/dist/data-table/data-table.d.ts +37 -5
- package/dist/data-table/data-table.js +426 -119
- package/dist/data-table/export-csv.d.ts +3 -3
- package/dist/data-table/export-csv.js +8 -10
- package/dist/data-table/llms.txt +127 -79
- package/dist/data-view/data-view.d.ts +13 -5
- package/dist/data-view/data-view.js +68 -6
- package/dist/data-view/llms.txt +29 -10
- package/dist/date-range/date-range.d.ts +20 -8
- package/dist/date-range/date-range.js +86 -63
- package/dist/date-range/llms.txt +32 -18
- package/dist/datepicker/datepicker.d.ts +22 -8
- package/dist/datepicker/datepicker.js +103 -71
- package/dist/datepicker/llms.txt +36 -22
- package/dist/dialog/dialog.d.ts +4 -4
- package/dist/dialog/dialog.js +32 -11
- package/dist/dialog/llms.txt +13 -10
- package/dist/divider/divider.d.ts +7 -1
- package/dist/divider/divider.js +3 -2
- package/dist/divider/llms.txt +4 -3
- package/dist/drawer/drawer.d.ts +9 -4
- package/dist/drawer/drawer.js +5 -4
- package/dist/drawer/llms.txt +13 -10
- 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 +12 -6
- package/dist/fieldset/llms.txt +26 -9
- package/dist/file-upload/file-upload.d.ts +8 -2
- package/dist/file-upload/file-upload.js +22 -11
- package/dist/file-upload/llms.txt +12 -9
- 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 +27 -6
- package/dist/form-field/llms.txt +25 -11
- package/dist/galleria/galleria.d.ts +3 -3
- package/dist/galleria/galleria.js +9 -6
- 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 +6 -6
- package/dist/i18n/i18n-context.js +27 -5
- 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 +10 -7
- 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 +5 -5
- package/dist/image/llms.txt +13 -13
- package/dist/input/input.d.ts +18 -5
- package/dist/input/input.js +22 -6
- package/dist/input/llms.txt +25 -11
- package/dist/input-mask/input-mask.d.ts +7 -2
- package/dist/input-mask/input-mask.js +17 -43
- package/dist/input-mask/llms.txt +8 -5
- package/dist/input-number/input-number.d.ts +11 -9
- package/dist/input-number/input-number.js +25 -21
- package/dist/input-number/llms.txt +17 -14
- package/dist/input-otp/input-otp.d.ts +18 -5
- package/dist/input-otp/input-otp.js +48 -24
- package/dist/input-otp/llms.txt +18 -7
- 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 +14 -0
- package/dist/internal/mask.js +62 -0
- package/dist/internal/number-group.d.ts +22 -0
- package/dist/internal/number-group.js +53 -0
- package/dist/internal/option-key.d.ts +6 -0
- package/dist/internal/option-key.js +8 -0
- package/dist/internal/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 +40 -10
- package/dist/internal/password-strength.d.ts +6 -0
- package/dist/internal/password-strength.js +74 -0
- package/dist/internal/roving.d.ts +2 -2
- package/dist/internal/roving.js +3 -3
- package/dist/internal/scroll-lock.d.ts +6 -0
- package/dist/internal/scroll-lock.js +35 -0
- package/dist/internal/slot-children.d.ts +13 -0
- package/dist/internal/slot-children.js +31 -0
- package/dist/internal/slot.d.ts +3 -3
- package/dist/internal/slot.js +2 -2
- package/dist/internal/step.d.ts +12 -0
- package/dist/internal/step.js +31 -0
- package/dist/internal/tween.d.ts +5 -5
- package/dist/internal/tween.js +7 -8
- 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 +5 -0
- package/dist/internal/use-controllable-state.js +25 -4
- 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 +2 -1
- package/dist/internal/use-modal-focus.js +14 -3
- package/dist/knob/knob.d.ts +7 -4
- package/dist/knob/knob.js +27 -22
- package/dist/knob/llms.txt +18 -11
- package/dist/listbox/listbox.d.ts +8 -6
- package/dist/listbox/listbox.js +37 -17
- package/dist/listbox/llms.txt +12 -9
- 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 +30 -23
- package/dist/map/map-create-dialog.d.ts +4 -4
- package/dist/map/map-create-dialog.js +36 -28
- package/dist/map/map-hours-field.d.ts +4 -4
- package/dist/map/map-hours-field.js +8 -7
- package/dist/map/map-image-field.d.ts +2 -2
- package/dist/map/map-image-field.js +6 -5
- package/dist/map/map-legend.d.ts +4 -4
- package/dist/map/map-legend.js +3 -2
- package/dist/map/map-poi-card.d.ts +3 -3
- package/dist/map/map-poi-card.js +10 -9
- package/dist/map/map-search.d.ts +17 -6
- package/dist/map/map-search.js +70 -43
- package/dist/map/map.d.ts +25 -13
- package/dist/map/map.js +86 -57
- 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 +18 -7
- package/dist/megamenu/megamenu.d.ts +12 -6
- package/dist/megamenu/megamenu.js +23 -14
- 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-rows.js +1 -0
- package/dist/menu/menu.d.ts +2 -2
- package/dist/menu/menu.js +15 -8
- package/dist/menubar/llms.txt +19 -8
- package/dist/menubar/menubar.d.ts +11 -5
- package/dist/menubar/menubar.js +23 -11
- 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 +2 -1
- package/dist/motion/motion-context.d.ts +22 -0
- package/dist/motion/motion-context.js +28 -0
- package/dist/order-list/llms.txt +24 -9
- package/dist/order-list/order-list.d.ts +12 -4
- package/dist/order-list/order-list.js +123 -25
- package/dist/paginator/llms.txt +8 -8
- package/dist/paginator/paginator.js +7 -4
- package/dist/panel/llms.txt +25 -13
- package/dist/panel/panel.d.ts +11 -3
- package/dist/panel/panel.js +12 -7
- package/dist/password/llms.txt +19 -7
- package/dist/password/password.d.ts +2 -2
- package/dist/password/password.js +27 -16
- package/dist/peek/llms.txt +11 -11
- package/dist/peek/peek.d.ts +6 -6
- package/dist/peek/peek.js +41 -19
- package/dist/pick-list/llms.txt +35 -12
- package/dist/pick-list/pick-list.d.ts +12 -4
- package/dist/pick-list/pick-list.js +128 -24
- package/dist/popover/llms.txt +13 -10
- package/dist/popover/popover.d.ts +6 -4
- package/dist/popover/popover.js +14 -7
- package/dist/progress/llms.txt +17 -17
- package/dist/progress/progress.d.ts +7 -7
- package/dist/progress/progress.js +7 -6
- package/dist/public-api.d.ts +16 -4
- package/dist/public-api.js +13 -7
- package/dist/radio-group/llms.txt +16 -6
- package/dist/radio-group/radio-group.d.ts +12 -3
- package/dist/radio-group/radio-group.js +23 -4
- 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 +77 -12
- package/dist/rich-text/lists.d.ts +2 -2
- package/dist/rich-text/lists.js +7 -7
- package/dist/rich-text/llms.txt +35 -12
- package/dist/rich-text/rich-text.d.ts +13 -14
- package/dist/rich-text/rich-text.js +311 -142
- package/dist/rich-text/sanitize.d.ts +11 -2
- package/dist/rich-text/sanitize.js +80 -32
- package/dist/rich-text/tables.d.ts +3 -3
- package/dist/rich-text/tables.js +3 -3
- package/dist/roadmap/llms.txt +23 -17
- package/dist/roadmap/roadmap.d.ts +6 -6
- package/dist/roadmap/roadmap.js +102 -25
- 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 +2 -1
- package/dist/select/llms.txt +22 -12
- package/dist/select/select.d.ts +10 -8
- package/dist/select/select.js +87 -47
- package/dist/shared/types.d.ts +22 -1
- package/dist/shared/types.js +9 -1
- package/dist/sidenav/llms.txt +9 -9
- package/dist/sidenav/sidenav.d.ts +2 -3
- package/dist/sidenav/sidenav.js +5 -4
- 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 +34 -12
- package/dist/slider/slider-drag.d.ts +26 -0
- package/dist/slider/slider-drag.js +79 -0
- package/dist/slider/slider.d.ts +8 -5
- package/dist/slider/slider.js +38 -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 +3 -2
- package/dist/spinner/llms.txt +7 -7
- package/dist/spinner/spinner.d.ts +1 -1
- package/dist/spinner/spinner.js +2 -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 +13 -6
- package/dist/splitter/llms.txt +11 -11
- package/dist/splitter/splitter.d.ts +3 -3
- package/dist/splitter/splitter.js +25 -10
- package/dist/stepper/llms.txt +35 -13
- package/dist/stepper/stepper.d.ts +6 -6
- package/dist/stepper/stepper.js +60 -13
- package/dist/switch/llms.txt +15 -5
- package/dist/switch/switch.d.ts +10 -3
- package/dist/switch/switch.js +18 -4
- package/dist/switch-group/llms.txt +11 -8
- package/dist/switch-group/switch-group.d.ts +6 -4
- package/dist/switch-group/switch-group.js +9 -7
- package/dist/tabs/llms.txt +12 -9
- package/dist/tabs/tabs.d.ts +5 -5
- package/dist/tabs/tabs.js +36 -20
- package/dist/textarea/llms.txt +23 -12
- package/dist/textarea/textarea.d.ts +19 -6
- package/dist/textarea/textarea.js +22 -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 +50 -17
- package/dist/toast/toast-container.d.ts +9 -3
- package/dist/toast/toast-container.js +18 -9
- package/dist/toast/toast-store.d.ts +42 -2
- 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 +14 -12
- package/dist/tooltip/tooltip.d.ts +5 -4
- package/dist/tooltip/tooltip.js +100 -41
- package/dist/tree/llms.txt +25 -11
- package/dist/tree/tree-keys.d.ts +5 -5
- package/dist/tree/tree-keys.js +7 -7
- package/dist/tree/tree.d.ts +17 -5
- package/dist/tree/tree.js +62 -20
- 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 +18 -15
|
@@ -1,10 +1,19 @@
|
|
|
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).
|
|
4
13
|
*/
|
|
5
14
|
export declare function sanitizeHtml(html: string): string;
|
|
6
15
|
/**
|
|
7
|
-
* Waliduje URL z narzędzia
|
|
8
|
-
* 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.
|
|
9
18
|
*/
|
|
10
19
|
export declare function sanitizeUrl(url: string): string | null;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Sanityzacja treści edytora. Kod nie zna
|
|
3
|
-
*
|
|
4
|
-
* (jak `@konce-pt/datetime`).
|
|
2
|
+
* Sanityzacja treści edytora. Kod nie zna frameworka - operuje wyłącznie na DOM. Bliźniak
|
|
3
|
+
* w porcie Angulara: `rich-text/sanitize.ts` - zmiana w obu (docelowo wspólna paczka pure-TS).
|
|
5
4
|
*/
|
|
6
5
|
/** Znaczniki dopuszczone w treści; pozostałe rozwijamy do dzieci, nie usuwając tekstu. */
|
|
7
6
|
const ALLOWED_TAGS = new Set([
|
|
@@ -16,16 +15,30 @@ const ALLOWED_STYLES = new Set([
|
|
|
16
15
|
/*
|
|
17
16
|
* `data-mention` i `contenteditable` są tu dla wzmianek: identyfikator musi przeżyć zapis treści,
|
|
18
17
|
* a `contenteditable="false"` trzyma wzmiankę jako jeden kawałek, w którego środku nie da się
|
|
19
|
-
* pisać. Oba atrybuty są bierne
|
|
20
|
-
* ich nie otwiera nowej furtki.
|
|
18
|
+
* pisać. Oba atrybuty są bierne - nie wykonują niczego i nie wskazują zasobów, więc wpuszczenie
|
|
19
|
+
* ich nie otwiera nowej furtki. `class` przechodzi osobno, tylko z klasami samego edytora.
|
|
21
20
|
*/
|
|
22
21
|
const ALLOWED_ATTRS = new Set([
|
|
23
|
-
'colspan', 'rowspan', 'span', 'width', '
|
|
22
|
+
'colspan', 'rowspan', 'span', 'width', 'target', 'rel', 'title', 'color',
|
|
24
23
|
'data-mention', 'contenteditable',
|
|
25
24
|
]);
|
|
26
|
-
|
|
25
|
+
/**
|
|
26
|
+
* Klasy, które zostawia sam edytor (`kpt-rt-table`, `kpt-mention`). Obce klasy z wklejki - aplikacji,
|
|
27
|
+
* biblioteki (`kpt-dialog`, `kpt-visually-hidden`) czy Tailwinda - przejęłyby ich wygląd tam,
|
|
28
|
+
* gdzie aplikacja wyświetla treść.
|
|
29
|
+
*/
|
|
30
|
+
const EDITOR_CLASS = /^kpt-(rt-[a-z0-9-]+|mention)$/;
|
|
31
|
+
// Schematy dopuszczone w `href` - ta sama allowlista dla narzędzia "link" i dla wklejonego HTML.
|
|
27
32
|
const SAFE_URL_SCHEME = /^(https?:|mailto:|tel:)/i;
|
|
28
33
|
const HAS_URL_SCHEME = /^[a-z][a-z0-9+.-]*:/i;
|
|
34
|
+
/** Znaki sterujące, które przeglądarka pomija przy parsowaniu URL-a (`jav\tascript:`). */
|
|
35
|
+
// eslint-disable-next-line no-control-regex -- celowo: właśnie te znaki trzeba wyciąć przed testem schematu
|
|
36
|
+
const URL_CONTROL = /[\x00-\x1f]/g;
|
|
37
|
+
/** Czy adres z treści może zostać: względny (bez schematu) albo ze schematu z allowlisty. */
|
|
38
|
+
function isSafeHref(value) {
|
|
39
|
+
const compact = value.replace(URL_CONTROL, '').trim();
|
|
40
|
+
return !HAS_URL_SCHEME.test(compact) || SAFE_URL_SCHEME.test(compact);
|
|
41
|
+
}
|
|
29
42
|
/** Deklaracje `style` z allowlisty; `null`, gdy nie zostaje nic. */
|
|
30
43
|
function sanitizeStyleAttr(value) {
|
|
31
44
|
const kept = value
|
|
@@ -34,56 +47,91 @@ function sanitizeStyleAttr(value) {
|
|
|
34
47
|
.filter((decl) => ALLOWED_STYLES.has(decl.split(':')[0]?.trim().toLowerCase() ?? ''));
|
|
35
48
|
return kept.length ? kept.join('; ') : null;
|
|
36
49
|
}
|
|
37
|
-
function sanitizeAttribute(el, attr) {
|
|
50
|
+
function sanitizeAttribute(el, attr, report) {
|
|
38
51
|
const name = attr.name.toLowerCase();
|
|
39
|
-
|
|
52
|
+
const remove = () => {
|
|
40
53
|
el.removeAttribute(attr.name);
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
if (/^\s*javascript:/i.test(attr.value.replace(/[\x00-\x1f]/g, '')))
|
|
47
|
-
el.removeAttribute(attr.name);
|
|
54
|
+
report.changed = true;
|
|
55
|
+
};
|
|
56
|
+
if (name === 'href') {
|
|
57
|
+
if (!isSafeHref(attr.value))
|
|
58
|
+
remove();
|
|
48
59
|
}
|
|
49
60
|
else if (name === 'style') {
|
|
50
61
|
const kept = sanitizeStyleAttr(attr.value);
|
|
51
|
-
|
|
62
|
+
const declared = attr.value.split(';').filter((decl) => decl.trim()).length;
|
|
63
|
+
if (kept === null)
|
|
64
|
+
remove();
|
|
65
|
+
else if (kept.split('; ').length !== declared) {
|
|
66
|
+
// Zmiana tylko wtedy, gdy wypadła deklaracja - samo przeformatowanie zapisu się nie liczy.
|
|
52
67
|
el.setAttribute('style', kept);
|
|
53
|
-
|
|
54
|
-
|
|
68
|
+
report.changed = true;
|
|
69
|
+
}
|
|
55
70
|
}
|
|
56
|
-
else if (
|
|
57
|
-
|
|
71
|
+
else if (name === 'class') {
|
|
72
|
+
const tokens = attr.value.split(/\s+/).filter(Boolean);
|
|
73
|
+
const kept = tokens.filter((token) => EDITOR_CLASS.test(token));
|
|
74
|
+
if (kept.length === 0)
|
|
75
|
+
remove();
|
|
76
|
+
else if (kept.length !== tokens.length) {
|
|
77
|
+
el.setAttribute('class', kept.join(' '));
|
|
78
|
+
report.changed = true;
|
|
79
|
+
}
|
|
58
80
|
}
|
|
81
|
+
else if (!ALLOWED_ATTRS.has(name)) {
|
|
82
|
+
// Także `on*` i `src` - żaden dopuszczony znacznik nie ładuje zasobów.
|
|
83
|
+
remove();
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/** Link otwierany w nowej karcie dostaje `noopener noreferrer` - bez tego karta sięga do `opener`. */
|
|
87
|
+
function secureTarget(el, report) {
|
|
88
|
+
if (el.tagName !== 'A' || !el.hasAttribute('target'))
|
|
89
|
+
return;
|
|
90
|
+
const rel = new Set((el.getAttribute('rel') ?? '').split(/\s+/).filter(Boolean));
|
|
91
|
+
if (rel.has('noopener') && rel.has('noreferrer'))
|
|
92
|
+
return;
|
|
93
|
+
rel.add('noopener');
|
|
94
|
+
rel.add('noreferrer');
|
|
95
|
+
el.setAttribute('rel', [...rel].join(' '));
|
|
96
|
+
report.changed = true;
|
|
59
97
|
}
|
|
60
98
|
/** Tag spoza allowlisty rozwijamy do dzieci; atrybuty czyścimy po kolei. */
|
|
61
|
-
function sanitizeElement(el) {
|
|
99
|
+
function sanitizeElement(el, report) {
|
|
62
100
|
if (!ALLOWED_TAGS.has(el.tagName)) {
|
|
63
101
|
el.replaceWith(...Array.from(el.childNodes));
|
|
102
|
+
report.changed = true;
|
|
64
103
|
return;
|
|
65
104
|
}
|
|
66
105
|
for (const attr of Array.from(el.attributes))
|
|
67
|
-
sanitizeAttribute(el, attr);
|
|
106
|
+
sanitizeAttribute(el, attr, report);
|
|
107
|
+
secureTarget(el, report);
|
|
68
108
|
}
|
|
69
109
|
/**
|
|
70
|
-
* Czyści HTML
|
|
71
|
-
*
|
|
110
|
+
* Czyści HTML i mówi, czy coś usunął. `changed: false` znaczy, że treść była czysta - wtedy
|
|
111
|
+
* wołający nie musi nadpisywać wartości modelu (serializacja DOM potrafi zmienić zapis, np.
|
|
112
|
+
* cudzysłowy, bez zmiany treści, a taka różnica nie może brudzić formularza).
|
|
72
113
|
*/
|
|
73
|
-
export function
|
|
114
|
+
export function sanitizeHtmlReport(html) {
|
|
74
115
|
const template = document.createElement('template');
|
|
75
116
|
template.innerHTML = html;
|
|
117
|
+
const report = { changed: false };
|
|
76
118
|
for (const el of Array.from(template.content.querySelectorAll('*')))
|
|
77
|
-
sanitizeElement(el);
|
|
78
|
-
return template.innerHTML;
|
|
119
|
+
sanitizeElement(el, report);
|
|
120
|
+
return { html: template.innerHTML, changed: report.changed };
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Czyści HTML wchodzący do edytora. Dotyczy **każdego** zapisu do DOM edytora, nie tylko
|
|
124
|
+
* wklejania: `value` z aplikacji też bywa niezaufane (baza, CMS, inny użytkownik).
|
|
125
|
+
*/
|
|
126
|
+
export function sanitizeHtml(html) {
|
|
127
|
+
return sanitizeHtmlReport(html).html;
|
|
79
128
|
}
|
|
80
129
|
/**
|
|
81
|
-
* Waliduje URL z narzędzia
|
|
82
|
-
* bez zmian; schemat spoza allowlisty
|
|
130
|
+
* Waliduje URL z narzędzia "link". Brak schematu -> dopisujemy `https://`; `#` i `/` przechodzą
|
|
131
|
+
* bez zmian; schemat spoza allowlisty -> `null`, czyli link nie zostaje wstawiony.
|
|
83
132
|
*/
|
|
84
133
|
export function sanitizeUrl(url) {
|
|
85
|
-
|
|
86
|
-
const trimmed = url.trim().replace(/[\x00-\x1f]/g, '');
|
|
134
|
+
const trimmed = url.trim().replace(URL_CONTROL, '');
|
|
87
135
|
if (!trimmed)
|
|
88
136
|
return null;
|
|
89
137
|
if (trimmed.startsWith('#') || trimmed.startsWith('/'))
|
|
@@ -14,7 +14,7 @@ export interface Bounds {
|
|
|
14
14
|
}
|
|
15
15
|
/** Klasa znacząca komórki objęte zaznaczeniem prostokątnym. */
|
|
16
16
|
export declare const CELL_SELECTED = "kpt-rt-cell-sel";
|
|
17
|
-
/** Macierz komórek: pozycja w siatce, a nie w DOM
|
|
17
|
+
/** Macierz komórek: pozycja w siatce, a nie w DOM - scalona komórka zajmuje kilka pól. */
|
|
18
18
|
export declare function buildGrid(table: HTMLTableElement): {
|
|
19
19
|
grid: HTMLTableCellElement[][];
|
|
20
20
|
pos: GridPos;
|
|
@@ -23,7 +23,7 @@ export declare function buildGrid(table: HTMLTableElement): {
|
|
|
23
23
|
export declare function cellsInRegion(grid: HTMLTableCellElement[][], b: Bounds): Set<HTMLTableCellElement>;
|
|
24
24
|
/**
|
|
25
25
|
* Prostokątne zaznaczenie od komórki-kotwicy do komórki pod kursorem. Granice rosną, aż region
|
|
26
|
-
* w pełni obejmie scalone komórki na brzegach
|
|
26
|
+
* w pełni obejmie scalone komórki na brzegach - inaczej scalanie działałoby na pół komórki.
|
|
27
27
|
*/
|
|
28
28
|
export declare function regionBetween(anchor: HTMLTableCellElement, focus: HTMLTableCellElement): Set<HTMLTableCellElement>;
|
|
29
29
|
/** Równe szerokości kolumn po wstawieniu albo usunięciu kolumny. */
|
|
@@ -34,7 +34,7 @@ export declare function insertColumn(cell: HTMLTableCellElement, dir: 'left' | '
|
|
|
34
34
|
export declare function deleteColumn(cell: HTMLTableCellElement): void;
|
|
35
35
|
export declare function insertRow(cell: HTMLTableCellElement, dir: 'above' | 'below'): void;
|
|
36
36
|
export declare function deleteRow(cell: HTMLTableCellElement): void;
|
|
37
|
-
/** Podmienia znacznik komórki `td`
|
|
37
|
+
/** Podmienia znacznik komórki `td` <-> `th`, zachowując atrybuty i treść. */
|
|
38
38
|
export declare function swapCellTag(cell: HTMLTableCellElement, tag: 'td' | 'th'): void;
|
|
39
39
|
export declare function toggleHeaderRow(cell: HTMLTableCellElement): void;
|
|
40
40
|
export declare function toggleHeaderColumn(cell: HTMLTableCellElement): void;
|
package/dist/rich-text/tables.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
/** Klasa znacząca komórki objęte zaznaczeniem prostokątnym. */
|
|
6
6
|
export const CELL_SELECTED = 'kpt-rt-cell-sel';
|
|
7
|
-
/** Macierz komórek: pozycja w siatce, a nie w DOM
|
|
7
|
+
/** Macierz komórek: pozycja w siatce, a nie w DOM - scalona komórka zajmuje kilka pól. */
|
|
8
8
|
export function buildGrid(table) {
|
|
9
9
|
const grid = [];
|
|
10
10
|
const pos = new Map();
|
|
@@ -65,7 +65,7 @@ function growBounds(grid, pos, b) {
|
|
|
65
65
|
}
|
|
66
66
|
/**
|
|
67
67
|
* Prostokątne zaznaczenie od komórki-kotwicy do komórki pod kursorem. Granice rosną, aż region
|
|
68
|
-
* w pełni obejmie scalone komórki na brzegach
|
|
68
|
+
* w pełni obejmie scalone komórki na brzegach - inaczej scalanie działałoby na pół komórki.
|
|
69
69
|
*/
|
|
70
70
|
export function regionBetween(anchor, focus) {
|
|
71
71
|
const table = anchor.closest('table');
|
|
@@ -153,7 +153,7 @@ export function deleteRow(cell) {
|
|
|
153
153
|
}
|
|
154
154
|
tr.remove();
|
|
155
155
|
}
|
|
156
|
-
/** Podmienia znacznik komórki `td`
|
|
156
|
+
/** Podmienia znacznik komórki `td` <-> `th`, zachowując atrybuty i treść. */
|
|
157
157
|
export function swapCellTag(cell, tag) {
|
|
158
158
|
if (cell.tagName.toLowerCase() === tag)
|
|
159
159
|
return;
|
package/dist/roadmap/llms.txt
CHANGED
|
@@ -9,40 +9,40 @@ A separate subpath, because the geometry, the styles and the data model are heav
|
|
|
9
9
|
library core and not every app needs them.
|
|
10
10
|
|
|
11
11
|
## Props
|
|
12
|
-
- `config`: `KptRoadmapConfig` (required)
|
|
13
|
-
- `scrollHeight`: string | null
|
|
14
|
-
- `labelWidth`: string
|
|
15
|
-
- `cellWidthPx`: number | null
|
|
16
|
-
- `showToday`: boolean
|
|
17
|
-
- `virtual`: boolean
|
|
18
|
-
- `collapsedGroups` / `defaultCollapsedGroups` / `onCollapsedGroupsChange`
|
|
12
|
+
- `config`: `KptRoadmapConfig` (required) - the data and every option
|
|
13
|
+
- `scrollHeight`: string | null - a fixed height, e.g. '420px'; enables the sticky header
|
|
14
|
+
- `labelWidth`: string - the width of the label column (default '14rem')
|
|
15
|
+
- `cellWidthPx`: number | null - the width of an axis column; without it the value depends on the zoom
|
|
16
|
+
- `showToday`: boolean - the "today" marker (default true)
|
|
17
|
+
- `virtual`: boolean - renders only the visible rows; worth it from roughly 100 rows up
|
|
18
|
+
- `collapsedGroups` / `defaultCollapsedGroups` / `onCollapsedGroupsChange` - the ids of collapsed
|
|
19
19
|
groups, controlled or uncontrolled
|
|
20
|
-
- `onItemClick(item)`, `onRangeChange(range)`
|
|
20
|
+
- `onItemClick(item)`, `onRangeChange(range)` - the click on a bar and the computed axis range
|
|
21
21
|
- `className`, `ref` and every other `<div>` prop go to the host
|
|
22
|
-
- `locale`, `dictionary`
|
|
22
|
+
- `locale`, `dictionary` - the per-component i18n overrides
|
|
23
23
|
|
|
24
24
|
## The config is the API
|
|
25
25
|
`{ items, groups?, links?, zoom?, range?, locale?, today?, timeline?, rows?, dependencies?,
|
|
26
|
-
interaction? }`
|
|
26
|
+
interaction? }` - the same shape the Angular port takes, because both read it from
|
|
27
27
|
`@konce-pt/roadmap`. An item is `{ id, label, start, end?, sublabel?, variant?, color?, progress?,
|
|
28
28
|
groupId?, dependsOn?, milestone?, meta? }`; no `end` means a milestone diamond, and a bare string in
|
|
29
29
|
`dependsOn` is an 'FS' dependency.
|
|
30
30
|
|
|
31
31
|
## The geometry lives in a package
|
|
32
32
|
The calendar arithmetic, the timeline bands and cells, the row layout, lane packing and the
|
|
33
|
-
dependency polylines come from `@konce-pt/roadmap`
|
|
33
|
+
dependency polylines come from `@konce-pt/roadmap` - pure functions with `node --test` coverage,
|
|
34
34
|
shared with the Angular port. The component only turns them into DOM and SVG.
|
|
35
35
|
|
|
36
36
|
## Collapsing has two sources
|
|
37
37
|
`group.collapsed` in the data and the `collapsedGroups` prop are kept apart, so an app that controls
|
|
38
38
|
the state keeps control while a group collapsed declaratively still works. Expanding removes the
|
|
39
|
-
group from both
|
|
39
|
+
group from both - otherwise `group.collapsed` would immediately collapse it again. The declarative
|
|
40
40
|
set is recomputed during render whenever the `groups` array identity changes; an effect would do it
|
|
41
41
|
one render too late, that is after the group flashed open.
|
|
42
42
|
|
|
43
43
|
## Measuring, not guessing
|
|
44
44
|
The SVG dependency layer works in pixels, so the lane width and the scroll viewport are measured
|
|
45
|
-
with a `ResizeObserver` in a layout effect. Before the first measurement no link is drawn
|
|
45
|
+
with a `ResizeObserver` in a layout effect. Before the first measurement no link is drawn - half a
|
|
46
46
|
frame without arrows beats arrows in the wrong place.
|
|
47
47
|
|
|
48
48
|
## Examples
|
|
@@ -63,13 +63,13 @@ frame without arrows beats arrows in the wrong place.
|
|
|
63
63
|
different lengths; inside a column the position is interpolated linearly.
|
|
64
64
|
- An item extending past the axis range is cut off on the side where it is clipped.
|
|
65
65
|
- Tooltips use `KptTooltip`, mostly with `onlyWhenTruncated`: a tooltip shows what cannot be seen.
|
|
66
|
-
Milestones and header cells carry one unconditionally
|
|
66
|
+
Milestones and header cells carry one unconditionally - a diamond shows no text at all.
|
|
67
67
|
- Dependencies: 'FS' (the default), 'SS', 'FF', 'SF'; violated ones are drawn as a dashed line in
|
|
68
68
|
the danger color.
|
|
69
69
|
- Entries with an invalid date or a duplicate `id` are skipped, with a warning in a development build.
|
|
70
70
|
|
|
71
71
|
## i18n
|
|
72
|
-
The `roadmap.*` keys: `empty`, `week`, `today`, `milestone`, `expandGroup`, `collapseGroup
|
|
72
|
+
The `roadmap.*` keys: `empty`, `week`, `today`, `milestone`, `expandGroup`, `collapseGroup`, `progress` (`{percent}`).
|
|
73
73
|
Month and weekday names come from `Intl` per `config.locale` or the locale from `KptI18nProvider`.
|
|
74
74
|
|
|
75
75
|
## Tokens
|
|
@@ -78,5 +78,11 @@ bars per variant `--kpt-color-{primary,success,danger,warning,info,muted}`.
|
|
|
78
78
|
Sizes: `--kpt-roadmap-label-w`, `--kpt-roadmap-cell-w` (both can be overridden from outside).
|
|
79
79
|
|
|
80
80
|
## Accessibility
|
|
81
|
-
|
|
82
|
-
|
|
81
|
+
Bars and milestones are `role="button"` with an accessible name built from the label, sublabel, date range
|
|
82
|
+
and progress (`Design, 3-14 Mar 2026, 40% complete`) - the end is exclusive, so the last occupied day is
|
|
83
|
+
announced. The whole bar grid is one Tab stop (roving tabindex): Left/Right move within a row, Up/Down go to
|
|
84
|
+
the bar with the nearest start in the neighbouring row, Home/End jump to the row's edge, Ctrl+Home/End to the
|
|
85
|
+
first/last bar; Enter and Space act like a click. With `virtual` on, moving to a row outside the window
|
|
86
|
+
scrolls it in first. Disabled items get `aria-disabled`. A group toggle is named by the group label and
|
|
87
|
+
carries `aria-expanded`; "Expand/Collapse group" is only its tooltip. The background grid and the dependency
|
|
88
|
+
layer are `aria-hidden`.
|
|
@@ -8,15 +8,15 @@ export interface KptRoadmapProps<TItem = unknown, TGroup = unknown> extends Omit
|
|
|
8
8
|
scrollHeight?: string | null;
|
|
9
9
|
/** Szerokość kolumny etykiet. */
|
|
10
10
|
labelWidth?: string;
|
|
11
|
-
/** Szerokość kolumny osi czasu; brak
|
|
11
|
+
/** Szerokość kolumny osi czasu; brak => wartość zależna od zoomu. */
|
|
12
12
|
cellWidthPx?: number | null;
|
|
13
|
-
/** Znacznik
|
|
13
|
+
/** Znacznik "dziś". */
|
|
14
14
|
showToday?: boolean;
|
|
15
15
|
/** Renderowanie tylko widocznych wierszy. Warto włączyć od ~100 wierszy. */
|
|
16
16
|
virtual?: boolean;
|
|
17
|
-
/** Zwinięte grupy
|
|
17
|
+
/** Zwinięte grupy - tryb kontrolowany. */
|
|
18
18
|
collapsedGroups?: string[];
|
|
19
|
-
/** Zwinięte grupy na starcie
|
|
19
|
+
/** Zwinięte grupy na starcie - tryb niekontrolowany. */
|
|
20
20
|
defaultCollapsedGroups?: string[];
|
|
21
21
|
onCollapsedGroupsChange?: (ids: string[]) => void;
|
|
22
22
|
onItemClick?: (item: KptRoadmapItem<TItem>) => void;
|
|
@@ -24,11 +24,11 @@ export interface KptRoadmapProps<TItem = unknown, TGroup = unknown> extends Omit
|
|
|
24
24
|
onRangeChange?: (range: KptTimeRange) => void;
|
|
25
25
|
}
|
|
26
26
|
/**
|
|
27
|
-
* Roadmapa
|
|
27
|
+
* Roadmapa - wiersze zadań na poziomej osi czasu (widok typu Gantt).
|
|
28
28
|
*
|
|
29
29
|
* Układ opiera się na DOM i `position: sticky` (kolumna etykiet oraz nagłówek),
|
|
30
30
|
* paski są pozycjonowane procentowo w torze, a zależności rysuje warstwa SVG
|
|
31
|
-
* rozpięta nad torami. Cała geometria pochodzi z `@konce-pt/roadmap`
|
|
31
|
+
* rozpięta nad torami. Cała geometria pochodzi z `@konce-pt/roadmap` - tej samej
|
|
32
32
|
* paczki, z której korzysta port Angulara.
|
|
33
33
|
*
|
|
34
34
|
* @example
|
package/dist/roadmap/roadmap.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
'use client';
|
|
1
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
3
|
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, } from 'react';
|
|
3
|
-
import { buildRows, buildTimeline, linkGeometry, resolveLinks, resolveRange, sliceRows, } from '@konce-pt/roadmap';
|
|
4
|
+
import { buildRows, buildTimeline, linkGeometry, resolveLinks, resolveRange, roadmapItemDates, roadmapNavTarget, sliceRows, } from '@konce-pt/roadmap';
|
|
4
5
|
import { cn } from "../internal/cn.js";
|
|
5
6
|
import { isDevBuild } from "../internal/dev.js";
|
|
6
7
|
import { useControllableState } from "../internal/use-controllable-state.js";
|
|
@@ -15,16 +16,16 @@ const CELL_WIDTH = {
|
|
|
15
16
|
day: 44,
|
|
16
17
|
hour: 46,
|
|
17
18
|
};
|
|
18
|
-
/** Wysokość paska w trybie
|
|
19
|
+
/** Wysokość paska w trybie "jeden item na wiersz" (px). */
|
|
19
20
|
const BAR_HEIGHT = 22;
|
|
20
|
-
/** Odstęp między pasami w trybie
|
|
21
|
+
/** Odstęp między pasami w trybie "pack" (px). */
|
|
21
22
|
const LANE_GAP = 4;
|
|
22
23
|
/**
|
|
23
|
-
* Roadmapa
|
|
24
|
+
* Roadmapa - wiersze zadań na poziomej osi czasu (widok typu Gantt).
|
|
24
25
|
*
|
|
25
26
|
* Układ opiera się na DOM i `position: sticky` (kolumna etykiet oraz nagłówek),
|
|
26
27
|
* paski są pozycjonowane procentowo w torze, a zależności rysuje warstwa SVG
|
|
27
|
-
* rozpięta nad torami. Cała geometria pochodzi z `@konce-pt/roadmap`
|
|
28
|
+
* rozpięta nad torami. Cała geometria pochodzi z `@konce-pt/roadmap` - tej samej
|
|
28
29
|
* paczki, z której korzysta port Angulara.
|
|
29
30
|
*
|
|
30
31
|
* @example
|
|
@@ -40,22 +41,22 @@ export function KptRoadmap({ config, scrollHeight = null, labelWidth = '14rem',
|
|
|
40
41
|
/**
|
|
41
42
|
* Grupy zwinięte na starcie przez `group.collapsed`. Trzymamy je osobno od stanu aplikacji,
|
|
42
43
|
* żeby sterowanie z zewnątrz pozostało w jej rękach, a zwinięcie zadeklarowane w danych
|
|
43
|
-
* nadal działało. Wyliczamy w trakcie renderu i resetujemy, gdy zmieni się lista grup
|
|
44
|
+
* nadal działało. Wyliczamy w trakcie renderu i resetujemy, gdy zmieni się lista grup -
|
|
44
45
|
* efekt zrobiłby to samo o jeden render za późno, czyli po mignięciu rozwiniętej grupy.
|
|
45
46
|
*/
|
|
46
47
|
const [fromConfig, setFromConfig] = useState(() => declaredCollapsed(config.groups));
|
|
47
|
-
const
|
|
48
|
-
if (
|
|
49
|
-
|
|
48
|
+
const [prevGroups, setPrevGroups] = useState(config.groups);
|
|
49
|
+
if (prevGroups !== config.groups) {
|
|
50
|
+
setPrevGroups(config.groups);
|
|
50
51
|
setFromConfig(declaredCollapsed(config.groups));
|
|
51
52
|
}
|
|
52
|
-
/** Suma obu źródeł
|
|
53
|
+
/** Suma obu źródeł - stan faktycznie użyty do budowy wierszy. */
|
|
53
54
|
const collapsedIds = useMemo(() => new Set([...fromConfig, ...collapsed]), [fromConfig, collapsed]);
|
|
54
55
|
const scrollRef = useRef(null);
|
|
55
56
|
const laneRef = useRef(null);
|
|
56
57
|
const [scrollTop, setScrollTop] = useState(0);
|
|
57
58
|
const [viewportHeight, setViewportHeight] = useState(0);
|
|
58
|
-
/** Szerokość toru w px
|
|
59
|
+
/** Szerokość toru w px - potrzebna warstwie SVG, która pracuje w pikselach. */
|
|
59
60
|
const [laneWidth, setLaneWidth] = useState(0);
|
|
60
61
|
const zoom = config.zoom ?? 'week';
|
|
61
62
|
const cellWidth = cellWidthPx ?? CELL_WIDTH[zoom];
|
|
@@ -68,13 +69,35 @@ export function KptRoadmap({ config, scrollHeight = null, labelWidth = '14rem',
|
|
|
68
69
|
weekPrefix: config.timeline?.weekPrefix ?? t('roadmap.week'),
|
|
69
70
|
}), [range, zoom, config, locale, t]);
|
|
70
71
|
const layout = useMemo(() => buildRows(config.items, config.groups, collapsedIds, config.rows), [config.items, config.groups, config.rows, collapsedIds]);
|
|
71
|
-
|
|
72
|
+
const itemLocale = config.locale ?? config.timeline?.locale ?? locale;
|
|
73
|
+
/**
|
|
74
|
+
* Paski widoczne na osi, wiersz po wierszu - mapa dla nawigacji strzałkami. Liczona ze wszystkich
|
|
75
|
+
* wierszy, nie z okna wirtualizacji: strzałka w dół z ostatniego wyrenderowanego wiersza ma dokąd iść.
|
|
76
|
+
*/
|
|
77
|
+
const navGrid = useMemo(() => layout.rows.map((row) => row.kind === 'group'
|
|
78
|
+
? []
|
|
79
|
+
: row.items
|
|
80
|
+
.filter((item) => timeline.span(item.start, item.end).visible)
|
|
81
|
+
.map((item) => ({ id: item.id, start: item.start }))
|
|
82
|
+
.sort((a, b) => a.start - b.start)), [layout, timeline]);
|
|
83
|
+
/** Pasek, na którym ostatnio był fokus. */
|
|
84
|
+
const [activeId, setActiveId] = useState(null);
|
|
85
|
+
/**
|
|
86
|
+
* Jedyny przystanek Tab w siatce pasków (roving tabindex) - ostatnio aktywny pasek, a gdy zniknął
|
|
87
|
+
* (zwinięta grupa, inne dane), pierwszy. Bez tego roadmapa ze stu zadaniami to sto przystanków Tab.
|
|
88
|
+
*/
|
|
89
|
+
const tabStopId = useMemo(() => {
|
|
90
|
+
if (activeId && navGrid.some((row) => row.some((item) => item.id === activeId)))
|
|
91
|
+
return activeId;
|
|
92
|
+
return navGrid.find((row) => row.length > 0)?.[0].id ?? null;
|
|
93
|
+
}, [navGrid, activeId]);
|
|
94
|
+
/** Wiersze faktycznie renderowane - całość albo okno wirtualizacji. */
|
|
72
95
|
const visibleRows = useMemo(() => {
|
|
73
96
|
if (!virtual || viewportHeight <= 0)
|
|
74
97
|
return layout.rows;
|
|
75
98
|
return sliceRows(layout, scrollTop, viewportHeight).rows;
|
|
76
99
|
}, [virtual, viewportHeight, layout, scrollTop]);
|
|
77
|
-
// Zakres jest wielkością wyliczaną
|
|
100
|
+
// Zakres jest wielkością wyliczaną - informujemy o nim po każdym przeliczeniu.
|
|
78
101
|
useEffect(() => {
|
|
79
102
|
onRangeChange?.(range);
|
|
80
103
|
}, [range, onRangeChange]);
|
|
@@ -103,13 +126,28 @@ export function KptRoadmap({ config, scrollHeight = null, labelWidth = '14rem',
|
|
|
103
126
|
console.warn('[kpt-roadmap] Pominięto elementy:', invalid.map((entry) => `${entry.id} (${entry.reason})`).join(', '));
|
|
104
127
|
}
|
|
105
128
|
}, [layout]);
|
|
106
|
-
/** Wysokość paska wraz z odstępem
|
|
129
|
+
/** Wysokość paska wraz z odstępem - wspólna dla widoku i geometrii zależności. */
|
|
107
130
|
const laneStep = barHeight + LANE_GAP;
|
|
108
131
|
/** Offset pionowy paska wewnątrz wiersza (px). */
|
|
109
132
|
const barTop = useCallback((row, lane) => {
|
|
110
133
|
const total = row.laneCount * barHeight + (row.laneCount - 1) * LANE_GAP;
|
|
111
134
|
return Math.max(0, (row.height - total) / 2) + lane * laneStep;
|
|
112
135
|
}, [barHeight, laneStep]);
|
|
136
|
+
/** Nazwa dostępna paska: "Etykieta, 3-14 mar 2026, ukończono 40%". */
|
|
137
|
+
const itemAriaLabel = useCallback((item) => {
|
|
138
|
+
const source = item.source;
|
|
139
|
+
const dates = roadmapItemDates(item.start, item.end, itemLocale, {
|
|
140
|
+
isMilestone: item.isMilestone,
|
|
141
|
+
withTime: zoom === 'hour',
|
|
142
|
+
});
|
|
143
|
+
const parts = [item.isMilestone ? `${t('roadmap.milestone')}: ${source.label}` : source.label, dates];
|
|
144
|
+
if (source.sublabel)
|
|
145
|
+
parts.splice(1, 0, source.sublabel);
|
|
146
|
+
if (source.progress != null && !item.isMilestone) {
|
|
147
|
+
parts.push(t('roadmap.progress', { percent: Math.round(source.progress * 100) }));
|
|
148
|
+
}
|
|
149
|
+
return parts.join(', ');
|
|
150
|
+
}, [itemLocale, zoom, t]);
|
|
113
151
|
/** Paski danego wiersza gotowe do renderu. */
|
|
114
152
|
const bars = useCallback((row) => {
|
|
115
153
|
const result = [];
|
|
@@ -125,17 +163,18 @@ export function KptRoadmap({ config, scrollHeight = null, labelWidth = '14rem',
|
|
|
125
163
|
top: barTop(row, item.lane),
|
|
126
164
|
height: barHeight,
|
|
127
165
|
label: source.label,
|
|
128
|
-
title: source.sublabel ? `${source.label}
|
|
166
|
+
title: source.sublabel ? `${source.label} - ${source.sublabel}` : source.label,
|
|
129
167
|
variant: source.variant ?? 'primary',
|
|
130
168
|
color: source.color,
|
|
131
169
|
progress: source.progress ?? null,
|
|
132
170
|
isMilestone: item.isMilestone,
|
|
133
171
|
clippedStart: geom.clippedStart,
|
|
134
172
|
clippedEnd: geom.clippedEnd,
|
|
173
|
+
ariaLabel: itemAriaLabel(item),
|
|
135
174
|
});
|
|
136
175
|
}
|
|
137
176
|
return result;
|
|
138
|
-
}, [timeline, barTop, barHeight]);
|
|
177
|
+
}, [timeline, barTop, barHeight, itemAriaLabel]);
|
|
139
178
|
/** Pasek zbiorczy wiersza grupy. */
|
|
140
179
|
const summary = useCallback((row) => {
|
|
141
180
|
const span = row.groupState?.span;
|
|
@@ -208,25 +247,63 @@ export function KptRoadmap({ config, scrollHeight = null, labelWidth = '14rem',
|
|
|
208
247
|
return;
|
|
209
248
|
onItemClick?.(item.source);
|
|
210
249
|
};
|
|
250
|
+
/** Przy wirtualizacji przewija tak, żeby wiersz z paskiem wszedł do okna i się wyrenderował. */
|
|
251
|
+
const revealRow = (scroll, id) => {
|
|
252
|
+
if (!virtual)
|
|
253
|
+
return;
|
|
254
|
+
const row = layout.rows.find((candidate) => candidate.items.some((item) => item.id === id));
|
|
255
|
+
if (!row)
|
|
256
|
+
return;
|
|
257
|
+
const header = scroll.querySelector('.kpt-roadmap__head')?.offsetHeight ?? 0;
|
|
258
|
+
const viewport = scroll.clientHeight - header;
|
|
259
|
+
let top = scroll.scrollTop;
|
|
260
|
+
if (row.top < top)
|
|
261
|
+
top = row.top;
|
|
262
|
+
else if (row.top + row.height > top + viewport)
|
|
263
|
+
top = row.top + row.height - viewport;
|
|
264
|
+
if (top === scroll.scrollTop)
|
|
265
|
+
return;
|
|
266
|
+
scroll.scrollTop = top;
|
|
267
|
+
setScrollTop(top);
|
|
268
|
+
};
|
|
269
|
+
/**
|
|
270
|
+
* Klawiatura na pasku: Enter/Spacja działają jak kliknięcie, strzałki przenoszą fokus po siatce
|
|
271
|
+
* (w poziomie po wierszu, w pionie między wierszami), Home/End na skraj wiersza, z Ctrl - roadmapy.
|
|
272
|
+
*/
|
|
211
273
|
const handleItemKeydown = (event, item) => {
|
|
212
|
-
if (event.key
|
|
274
|
+
if (event.key === 'Enter' || event.key === ' ') {
|
|
275
|
+
event.preventDefault();
|
|
276
|
+
handleItemClick(item);
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
const target = roadmapNavTarget(navGrid, item.id, event.key, event.ctrlKey || event.metaKey);
|
|
280
|
+
if (!target)
|
|
213
281
|
return;
|
|
214
282
|
event.preventDefault();
|
|
215
|
-
|
|
283
|
+
setActiveId(target);
|
|
284
|
+
const scroll = event.currentTarget.closest('.kpt-roadmap__scroll');
|
|
285
|
+
if (!scroll)
|
|
286
|
+
return;
|
|
287
|
+
revealRow(scroll, target);
|
|
288
|
+
// Pasek z innego wiersza może jeszcze nie istnieć w DOM (wirtualizacja). Aktualizacja ze zdarzenia
|
|
289
|
+
// klawiatury jest zatwierdzana synchronicznie, więc w następnej klatce pasek już jest.
|
|
290
|
+
requestAnimationFrame(() => {
|
|
291
|
+
scroll.querySelector(`[data-kpt-item="${CSS.escape(target)}"]`)?.focus();
|
|
292
|
+
});
|
|
216
293
|
};
|
|
217
294
|
/**
|
|
218
295
|
* Treść dymka dla kolumny etykiet. Pusta tam, gdzie dymek nie miałby czego
|
|
219
|
-
* uzupełnić
|
|
296
|
+
* uzupełnić - komponent pomija wtedy podpowiedź.
|
|
220
297
|
*/
|
|
221
298
|
const labelTooltip = (row) => {
|
|
222
299
|
// Wiersz grupy ma własny dymek, osadzony głębiej (na stosie etykiet).
|
|
223
300
|
if (row.kind === 'group')
|
|
224
301
|
return '';
|
|
225
|
-
// Tryb 'pack' mieści wiele zadań w jednym wierszu
|
|
302
|
+
// Tryb 'pack' mieści wiele zadań w jednym wierszu - nie ma jednej etykiety.
|
|
226
303
|
if (row.items.length !== 1)
|
|
227
304
|
return '';
|
|
228
305
|
const source = row.items[0].source;
|
|
229
|
-
return source.sublabel ? `${source.label}
|
|
306
|
+
return source.sublabel ? `${source.label} - ${source.sublabel}` : source.label;
|
|
230
307
|
};
|
|
231
308
|
const onScroll = (event) => {
|
|
232
309
|
setScrollTop(event.currentTarget.scrollTop);
|
|
@@ -242,25 +319,25 @@ export function KptRoadmap({ config, scrollHeight = null, labelWidth = '14rem',
|
|
|
242
319
|
const group = row.group;
|
|
243
320
|
const first = row.items[0];
|
|
244
321
|
const sum = row.kind === 'group' ? summary(row) : null;
|
|
245
|
-
return (_jsxs("div", { className: cn('kpt-roadmap__row', row.kind === 'group' && 'kpt-roadmap__row--group'), style: { insetBlockStart: `${row.top}px`, blockSize: `${row.height}px` }, children: [_jsx(KptTooltip, { text: labelTooltip(row), onlyWhenTruncated: true, children: _jsx("div", { className: cn('kpt-roadmap__label', row.kind === 'group' && 'kpt-roadmap__label--group'), style: { paddingInlineStart: `${0.75 + row.depth * 1}rem` }, children: group ? (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", className: "kpt-roadmap__toggle", "aria-expanded": !isCollapsed(group), "aria-label":
|
|
322
|
+
return (_jsxs("div", { className: cn('kpt-roadmap__row', row.kind === 'group' && 'kpt-roadmap__row--group'), style: { insetBlockStart: `${row.top}px`, blockSize: `${row.height}px` }, children: [_jsx(KptTooltip, { text: labelTooltip(row), onlyWhenTruncated: true, children: _jsx("div", { className: cn('kpt-roadmap__label', row.kind === 'group' && 'kpt-roadmap__label--group'), style: { paddingInlineStart: `${0.75 + row.depth * 1}rem` }, children: group ? (_jsxs(_Fragment, { children: [_jsx(KptTooltip, { text: isCollapsed(group) ? t('roadmap.expandGroup') : t('roadmap.collapseGroup'), children: _jsx("button", { type: "button", className: "kpt-roadmap__toggle", "aria-expanded": !isCollapsed(group), "aria-label": group.label, onClick: () => toggleGroup(group), children: _jsx("span", { className: cn('kpt-roadmap__toggle-icon', !isCollapsed(group) && 'kpt-roadmap__toggle-icon--open') }) }) }), _jsx(KptTooltip, { text: group.sublabel ? `${group.label} - ${group.sublabel}` : group.label, onlyWhenTruncated: true, children: _jsxs("span", { className: "kpt-roadmap__label-stack", children: [_jsx("span", { className: "kpt-roadmap__label-text kpt-roadmap__label-text--group", children: group.label }), group.sublabel && _jsx("span", { className: "kpt-roadmap__label-sub", children: group.sublabel })] }) }), row.groupState?.itemCount ? (_jsx("span", { className: "kpt-roadmap__label-count", children: row.groupState.itemCount })) : null] })) : (first && (_jsxs(_Fragment, { children: [_jsx("span", { className: "kpt-roadmap__label-text", children: first.source.label }), row.items.length === 1 && first.source.sublabel && (_jsx("span", { className: "kpt-roadmap__label-sub", children: first.source.sublabel }))] }))) }) }), _jsx("div", { className: "kpt-roadmap__lane", children: row.kind === 'group'
|
|
246
323
|
? sum && (_jsx("div", { className: "kpt-roadmap__summary", "data-variant": sum.variant, style: {
|
|
247
324
|
insetInlineStart: `${sum.left}%`,
|
|
248
325
|
inlineSize: `${sum.width}%`,
|
|
249
326
|
backgroundColor: sum.color,
|
|
250
327
|
} }))
|
|
251
|
-
: bars(row).map((bar) => bar.isMilestone ? (_jsx(KptTooltip, { text: bar.title, children: _jsx("div", { className: "kpt-roadmap__milestone", "data-variant": bar.variant,
|
|
328
|
+
: bars(row).map((bar) => bar.isMilestone ? (_jsx(KptTooltip, { text: bar.title, children: _jsx("div", { className: "kpt-roadmap__milestone", "data-variant": bar.variant, style: {
|
|
252
329
|
insetInlineStart: `${bar.left}%`,
|
|
253
330
|
insetBlockStart: `${bar.top}px`,
|
|
254
331
|
inlineSize: `${bar.height}px`,
|
|
255
332
|
blockSize: `${bar.height}px`,
|
|
256
333
|
backgroundColor: bar.color,
|
|
257
|
-
}, tabIndex: 0, onClick: () => handleItemClick(bar.item), onKeyDown: (event) => handleItemKeydown(event, bar.item) }) }, bar.item.id)) : (_jsx(KptTooltip, { text: bar.title, onlyWhenTruncated: true, children: _jsxs("div", { className: cn('kpt-roadmap__bar', bar.clippedStart && 'kpt-roadmap__bar--clipped-start', bar.clippedEnd && 'kpt-roadmap__bar--clipped-end', bar.item.source.disabled && 'kpt-roadmap__bar--disabled'), "data-variant": bar.variant, style: {
|
|
334
|
+
}, role: "button", "aria-label": bar.ariaLabel, "aria-disabled": bar.item.source.disabled || undefined, "data-kpt-item": bar.item.id, tabIndex: bar.item.id === tabStopId ? 0 : -1, onClick: () => handleItemClick(bar.item), onFocus: () => setActiveId(bar.item.id), onKeyDown: (event) => handleItemKeydown(event, bar.item) }) }, bar.item.id)) : (_jsx(KptTooltip, { text: bar.title, onlyWhenTruncated: true, children: _jsxs("div", { className: cn('kpt-roadmap__bar', bar.clippedStart && 'kpt-roadmap__bar--clipped-start', bar.clippedEnd && 'kpt-roadmap__bar--clipped-end', bar.item.source.disabled && 'kpt-roadmap__bar--disabled'), "data-variant": bar.variant, style: {
|
|
258
335
|
insetInlineStart: `${bar.left}%`,
|
|
259
336
|
inlineSize: `${bar.width}%`,
|
|
260
337
|
insetBlockStart: `${bar.top}px`,
|
|
261
338
|
blockSize: `${bar.height}px`,
|
|
262
339
|
backgroundColor: bar.color,
|
|
263
|
-
}, tabIndex: 0, onClick: () => handleItemClick(bar.item), onKeyDown: (event) => handleItemKeydown(event, bar.item), children: [bar.progress !== null && (_jsx("span", { className: "kpt-roadmap__bar-progress", style: { inlineSize: `${bar.progress * 100}%` } })), _jsx("span", { className: "kpt-roadmap__bar-label", children: bar.label })] }) }, bar.item.id))) })] }, row.key));
|
|
340
|
+
}, role: "button", "aria-label": bar.ariaLabel, "aria-disabled": bar.item.source.disabled || undefined, "data-kpt-item": bar.item.id, tabIndex: bar.item.id === tabStopId ? 0 : -1, onClick: () => handleItemClick(bar.item), onFocus: () => setActiveId(bar.item.id), onKeyDown: (event) => handleItemKeydown(event, bar.item), children: [bar.progress !== null && (_jsx("span", { className: "kpt-roadmap__bar-progress", style: { inlineSize: `${bar.progress * 100}%` } })), _jsx("span", { className: "kpt-roadmap__bar-label", children: bar.label })] }) }, bar.item.id))) })] }, row.key));
|
|
264
341
|
})] }))] }) }));
|
|
265
342
|
}
|
|
266
343
|
/** Identyfikatory grup zwiniętych deklaratywnie w danych. */
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* @konce-pt/react/roadmap
|
|
2
|
+
* @konce-pt/react/roadmap - oś czasu typu Gantt. Osobny subpath, bo geometria, style i dane
|
|
3
3
|
* roadmapy są ciężkie względem rdzenia biblioteki i nie każda aplikacja ich potrzebuje.
|
|
4
4
|
*/
|
|
5
5
|
export { KptRoadmap } from "../roadmap/roadmap.js";
|
|
6
|
-
// Rdzeń obliczeniowy (pure TS)
|
|
6
|
+
// Rdzeń obliczeniowy (pure TS) - przydatny do własnych rozszerzeń, SSR i testów.
|
|
7
7
|
export { buildTimeline, buildRows, normalizeItems, packLanes, sliceRows, spanOf, rollupProgress, resolveLinks, linkGeometry, roundedPolyline, arrowHead, autoRange, resolveRange, isoWeekNumber, isoWeekYear, isoWeekday, isoWeeksInYear, startOfIsoWeek, startOfDay, addDays, addHours, addMonths, addUnits, floorToUnit, ceilToUnit, countUnits, diffDays, daysInMonth, isSameDay, toTimestamp, } from '@konce-pt/roadmap';
|