@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
package/dist/map/map.js
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
|
+
'use client';
|
|
1
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { KPT_MAP_DEFAULT_ATTRIBUTION, KPT_MAP_DEFAULT_TILE_URL,
|
|
3
|
-
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, } from 'react';
|
|
3
|
+
import { KPT_MAP_DEFAULT_ATTRIBUTION, KPT_MAP_DEFAULT_TILE_URL, clusterLocations, countByCategory, haversine, isCluster, isSplittable, renderClusterHtml, renderMarkerHtml, renderUserMarkerHtml, resolveCountryView, } from '@konce-pt/map';
|
|
4
|
+
import { useCallback, useEffect, useEffectEvent, useLayoutEffect, useMemo, useRef, useState, } from 'react';
|
|
4
5
|
import { cn } from "../internal/cn.js";
|
|
5
6
|
import { useControllableState } from "../internal/use-controllable-state.js";
|
|
6
7
|
import { useKptMessages } from "../i18n/i18n-context.js";
|
|
7
8
|
import { getKptIcon } from "../icon/icon-registry.js";
|
|
9
|
+
import { KptIcon } from "../icon/icon.js";
|
|
8
10
|
import { KptDialog } from "../dialog/dialog.js";
|
|
9
11
|
import { KptMapCreateDialog } from "./map-create-dialog.js";
|
|
10
12
|
import { KptMapLegend } from "./map-legend.js";
|
|
11
13
|
import { KptMapSearch } from "./map-search.js";
|
|
12
|
-
/** Rozmiary ikon w pikselach
|
|
14
|
+
/** Rozmiary ikon w pikselach - silnik potrzebuje ich do zakotwiczenia znacznika. */
|
|
13
15
|
const MARKER_SIZE = { width: 32, height: 42 };
|
|
14
16
|
const DOT_SIZE = { width: 24, height: 24 };
|
|
15
17
|
const CLUSTER_SIZE = { width: 44, height: 44 };
|
|
@@ -22,15 +24,15 @@ const POPUP_MARGIN = 16;
|
|
|
22
24
|
* w nią niczego nie otwiera.
|
|
23
25
|
*/
|
|
24
26
|
const SEARCH_MARKER_ID = 'kpt-map:search';
|
|
25
|
-
/** Brak silnika to najczęstsza pomyłka przy pierwszym użyciu
|
|
26
|
-
const NO_ENGINE_ERROR = 'KptMap: brak silnika mapy. Podaj fabrykę przez prop `engine`
|
|
27
|
+
/** Brak silnika to najczęstsza pomyłka przy pierwszym użyciu - komunikat niesie instrukcję. */
|
|
28
|
+
const NO_ENGINE_ERROR = 'KptMap: brak silnika mapy. Podaj fabrykę przez prop `engine` - ' +
|
|
27
29
|
'`engine={() => createLeafletEngine()}` albo ' +
|
|
28
30
|
'`engine={() => createGoogleEngine({ apiKey, mapId })}` z `@konce-pt/react/map`.';
|
|
29
31
|
/**
|
|
30
32
|
* Mapa z konfigurowalnymi znacznikami: kategorie z kolorem i ikoną, grupowanie z licznikiem,
|
|
31
33
|
* popup szczegółów, legenda, wyszukiwarka adresów i dodawanie lokalizacji prawym przyciskiem.
|
|
32
34
|
*
|
|
33
|
-
* Silnik (Leaflet albo Google) przychodzi propem `engine` z `@konce-pt/map`
|
|
35
|
+
* Silnik (Leaflet albo Google) przychodzi propem `engine` z `@konce-pt/map` - ta sama warstwa,
|
|
34
36
|
* na której stoi port Angulara. Leaflet ładuje się dynamicznym `import()`, więc aplikacja bez
|
|
35
37
|
* mapy go nie pobiera; wymaga też arkusza `leaflet/dist/leaflet.css`.
|
|
36
38
|
*
|
|
@@ -42,22 +44,26 @@ const NO_ENGINE_ERROR = 'KptMap: brak silnika mapy. Podaj fabrykę przez prop `e
|
|
|
42
44
|
* renderPopup={({ location, data }) => <strong>{location.title}</strong>}
|
|
43
45
|
* />
|
|
44
46
|
*/
|
|
45
|
-
export function KptMap({ locations, categories, country = 'PL', view = null, bounds = null, restrictToCountry = false, cluster = true, clusterOptions, clusterColorBy = 'none', legend = true, legendCounts = true, hiddenCategories, defaultHiddenCategories = [], onHiddenCategoriesChange, showUserLocation = false, followUser = false, geolocation = null, allowCreate = false, search = false, searchPlaceholder = '', searchOptions, searchZoom = 16, searchMarker = true, geocoder = null, popupLayout = 'plain', distanceFrom = null, height = '480px', tileUrl = null, attribution = null, engine, renderPopup, renderCreateForm, onLocationClick, onClusterClick, onLocationCreate, onSearchSelect, onViewChange, onUserPositionChange, onUserLocationError, locale: localeOverride, dictionary, className, style, ...rest }) {
|
|
47
|
+
export function KptMap({ locations, categories, country = 'PL', view = null, bounds = null, restrictToCountry = false, cluster = true, clusterOptions, clusterColorBy = 'none', legend = true, legendCounts = true, hiddenCategories, defaultHiddenCategories = [], onHiddenCategoriesChange, showUserLocation = false, followUser = false, geolocation = null, allowCreate = false, search = false, searchPlaceholder = '', searchOptions, searchZoom = 16, searchMarker = true, searchSpeech = false, searchSpeechLang, searchSpeechColor, geocoder = null, popupLayout = 'plain', distanceFrom = null, height = '480px', tileUrl = null, attribution = null, engine, renderPopup, renderCreateForm, onLocationClick, onClusterClick, onLocationCreate, onSearchSelect, onViewChange, onUserPositionChange, onUserLocationError, onEngineError, locale: localeOverride, dictionary, className, style, ...rest }) {
|
|
46
48
|
const { t } = useKptMessages({ locale: localeOverride, dictionary });
|
|
47
49
|
const canvasRef = useRef(null);
|
|
48
50
|
const popupRef = useRef(null);
|
|
49
51
|
const engineRef = useRef(null);
|
|
50
52
|
/**
|
|
51
53
|
* Łańcuch montażu i demontażu tego kontenera. Silnik mapowy oznacza element jako zajęty
|
|
52
|
-
* na czas życia mapy, a w trybie ścisłym React montuje efekt dwa razy pod rząd
|
|
54
|
+
* na czas życia mapy, a w trybie ścisłym React montuje efekt dwa razy pod rząd - drugi
|
|
53
55
|
* montaż musi więc poczekać, aż pierwszy skończy sprzątać.
|
|
54
56
|
*/
|
|
55
57
|
const mountChain = useRef(Promise.resolve());
|
|
56
|
-
|
|
57
|
-
|
|
58
|
+
/**
|
|
59
|
+
* Silnik po udanym `init()` - w stanie, nie tylko w refie, bo render czyta z niego pozycje
|
|
60
|
+
* nakładek (`project`). Ref zostaje dla handlerów zdarzeń, które żyją dłużej niż render.
|
|
61
|
+
*/
|
|
62
|
+
const [liveEngine, setLiveEngine] = useState(null);
|
|
63
|
+
const ready = liveEngine !== null;
|
|
58
64
|
/** Zoom trzymamy w stanie, bo od niego zależy wynik grupowania. */
|
|
59
65
|
const [zoom, setZoom] = useState(6);
|
|
60
|
-
/** Rośnie przy każdym ruchu mapy
|
|
66
|
+
/** Rośnie przy każdym ruchu mapy - wymusza przeliczenie pozycji nakładek. */
|
|
61
67
|
const [viewportTick, setViewportTick] = useState(0);
|
|
62
68
|
const [popup, setPopup] = useState(null);
|
|
63
69
|
const [contextTarget, setContextTarget] = useState(null);
|
|
@@ -65,7 +71,7 @@ export function KptMap({ locations, categories, country = 'PL', view = null, bou
|
|
|
65
71
|
const [createOpen, setCreateOpen] = useState(false);
|
|
66
72
|
const [userPosition, setUserPosition] = useState(null);
|
|
67
73
|
const [locationBlocked, setLocationBlocked] = useState(false);
|
|
68
|
-
/** Ostatni wybrany wynik wyszukiwania
|
|
74
|
+
/** Ostatni wybrany wynik wyszukiwania - źródło tymczasowej pinezki. */
|
|
69
75
|
const [searchPin, setSearchPin] = useState(null);
|
|
70
76
|
const [hidden, setHidden] = useControllableState({
|
|
71
77
|
value: hiddenCategories,
|
|
@@ -79,7 +85,7 @@ export function KptMap({ locations, categories, country = 'PL', view = null, bou
|
|
|
79
85
|
return hiddenSet.size === 0 ? locations : locations.filter((l) => !hiddenSet.has(l.categoryId));
|
|
80
86
|
}, [locations, hidden]);
|
|
81
87
|
const counts = useMemo(() => (legendCounts ? countByCategory(locations) : null), [legendCounts, locations]);
|
|
82
|
-
/** Wynik grupowania dla aktualnego zoomu
|
|
88
|
+
/** Wynik grupowania dla aktualnego zoomu - grupy i punkty pojedyncze. */
|
|
83
89
|
const entries = useMemo(() => {
|
|
84
90
|
if (!cluster)
|
|
85
91
|
return [...visibleLocations];
|
|
@@ -106,7 +112,7 @@ export function KptMap({ locations, categories, country = 'PL', view = null, bou
|
|
|
106
112
|
}),
|
|
107
113
|
size: CLUSTER_SIZE,
|
|
108
114
|
anchor: { x: CLUSTER_SIZE.width / 2, y: CLUSTER_SIZE.height / 2 },
|
|
109
|
-
// Grupy nad pojedynczymi punktami
|
|
115
|
+
// Grupy nad pojedynczymi punktami - licznik ma być czytelny.
|
|
110
116
|
zIndex: 500,
|
|
111
117
|
};
|
|
112
118
|
}
|
|
@@ -144,7 +150,7 @@ export function KptMap({ locations, categories, country = 'PL', view = null, bou
|
|
|
144
150
|
}),
|
|
145
151
|
size: MARKER_SIZE,
|
|
146
152
|
anchor: { x: MARKER_SIZE.width / 2, y: MARKER_SIZE.height },
|
|
147
|
-
// Nad grupami (500)
|
|
153
|
+
// Nad grupami (500) - to na nią użytkownik właśnie kazał mapie polecieć.
|
|
148
154
|
zIndex: 600,
|
|
149
155
|
});
|
|
150
156
|
}
|
|
@@ -184,7 +190,11 @@ export function KptMap({ locations, categories, country = 'PL', view = null, bou
|
|
|
184
190
|
* a jego callbacki żyją dłużej niż render, w którym powstały.
|
|
185
191
|
*/
|
|
186
192
|
const latest = useRef({ entries, allowCreate, popup, contextTarget, onLocationClick, onClusterClick, onViewChange });
|
|
187
|
-
|
|
193
|
+
// Odświeżane po zatwierdzeniu renderu, nie w jego trakcie - przerwany render nie zostawi w refie danych,
|
|
194
|
+
// których nigdy nie było na ekranie.
|
|
195
|
+
useLayoutEffect(() => {
|
|
196
|
+
latest.current = { entries, allowCreate, popup, contextTarget, onLocationClick, onClusterClick, onViewChange };
|
|
197
|
+
});
|
|
188
198
|
const emitViewChange = useCallback(() => {
|
|
189
199
|
const map = engineRef.current;
|
|
190
200
|
if (!map)
|
|
@@ -199,7 +209,7 @@ export function KptMap({ locations, categories, country = 'PL', view = null, bou
|
|
|
199
209
|
return;
|
|
200
210
|
if (isCluster(entry)) {
|
|
201
211
|
latest.current.onClusterClick?.(entry);
|
|
202
|
-
// Grupa punktów o tej samej współrzędnej nie da się rozbić zoomem
|
|
212
|
+
// Grupa punktów o tej samej współrzędnej nie da się rozbić zoomem - pokaż listę.
|
|
203
213
|
if (isSplittable(entry)) {
|
|
204
214
|
engineRef.current?.fitBounds(entry.bounds, FIT_PADDING);
|
|
205
215
|
return;
|
|
@@ -215,18 +225,21 @@ export function KptMap({ locations, categories, country = 'PL', view = null, bou
|
|
|
215
225
|
latest.current.onLocationClick?.(entry);
|
|
216
226
|
}, [autoPanToPopup]);
|
|
217
227
|
/*
|
|
218
|
-
* Montaż silnika
|
|
228
|
+
* Montaż silnika - raz na życie komponentu.
|
|
219
229
|
*
|
|
220
230
|
* Sprzątanie czeka na zakończenie `init()`: w trybie ścisłym React montuje efekt dwa razy,
|
|
221
231
|
* a silnik mapowy oznacza kontener jako zajęty już przy pierwszym wywołaniu. Zniszczenie
|
|
222
|
-
*
|
|
223
|
-
* kończyłby się błędem
|
|
232
|
+
* "w locie", przed rozwiązaniem obietnicy, zostawiłoby oznaczony kontener i drugi montaż
|
|
233
|
+
* kończyłby się błędem "Map container is already initialized".
|
|
224
234
|
*/
|
|
235
|
+
const reportEngineError = useEffectEvent((error) => {
|
|
236
|
+
console.error(error);
|
|
237
|
+
onEngineError?.(error);
|
|
238
|
+
});
|
|
225
239
|
useEffect(() => {
|
|
226
240
|
let disposed = false;
|
|
227
241
|
const map = engine();
|
|
228
242
|
engineRef.current = map;
|
|
229
|
-
setEngineKind(map.kind);
|
|
230
243
|
const preset = resolveCountryView(country);
|
|
231
244
|
const canvas = canvasRef.current;
|
|
232
245
|
if (!canvas)
|
|
@@ -265,18 +278,24 @@ export function KptMap({ locations, categories, country = 'PL', view = null, bou
|
|
|
265
278
|
});
|
|
266
279
|
map.on('markerclick', (id) => onMarkerClick(id));
|
|
267
280
|
setZoom(map.getZoom());
|
|
268
|
-
|
|
281
|
+
setLiveEngine(map);
|
|
269
282
|
emitViewChange();
|
|
283
|
+
})
|
|
284
|
+
// Bez tego odrzucony `init()` to "Uncaught (in promise)" i mapa bez słowa wyjaśnienia.
|
|
285
|
+
.catch((error) => {
|
|
286
|
+
if (!disposed)
|
|
287
|
+
reportEngineError(error);
|
|
270
288
|
});
|
|
271
289
|
return () => {
|
|
272
290
|
disposed = true;
|
|
273
291
|
engineRef.current = null;
|
|
274
|
-
|
|
292
|
+
setLiveEngine(null);
|
|
275
293
|
// Zniszczenie dopinamy do łańcucha: dopiero po `init()`, bo inaczej `destroy()` trafiłby
|
|
276
294
|
// w mapę, której jeszcze nie ma, a kontener zostałby oznaczony jako zajęty na zawsze.
|
|
277
295
|
mountChain.current = mountChain.current.finally(() => map.destroy());
|
|
278
296
|
};
|
|
279
|
-
// Silnik i widok startowy czytamy raz
|
|
297
|
+
// Silnik i widok startowy czytamy raz - podmiana po starcie nie przeładowuje mapy.
|
|
298
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps -- montaż raz na życie komponentu (patrz wyżej)
|
|
280
299
|
}, []);
|
|
281
300
|
// Bez tego Leaflet zostawia szare pola po każdej zmianie układu strony.
|
|
282
301
|
useLayoutEffect(() => {
|
|
@@ -287,12 +306,12 @@ export function KptMap({ locations, categories, country = 'PL', view = null, bou
|
|
|
287
306
|
observer.observe(canvas);
|
|
288
307
|
return () => observer.disconnect();
|
|
289
308
|
}, []);
|
|
290
|
-
// Wypychanie znaczników do silnika
|
|
309
|
+
// Wypychanie znaczników do silnika - jedyne miejsce, które je rysuje.
|
|
291
310
|
useEffect(() => {
|
|
292
311
|
if (ready)
|
|
293
312
|
engineRef.current?.setMarkers(markers);
|
|
294
313
|
}, [ready, markers]);
|
|
295
|
-
// Zmiana źródła kafli w locie
|
|
314
|
+
// Zmiana źródła kafli w locie - bez przeładowania mapy.
|
|
296
315
|
useEffect(() => {
|
|
297
316
|
if (ready) {
|
|
298
317
|
engineRef.current?.setTileSource(tileUrl ?? KPT_MAP_DEFAULT_TILE_URL, attribution ?? KPT_MAP_DEFAULT_ATTRIBUTION);
|
|
@@ -307,32 +326,42 @@ export function KptMap({ locations, categories, country = 'PL', view = null, bou
|
|
|
307
326
|
return;
|
|
308
327
|
engineRef.current?.setUserPosition(userPosition, renderUserMarkerHtml({ label: t('map.myLocation'), iconSvgInner: getKptIcon('user') }));
|
|
309
328
|
}, [ready, userPosition, t]);
|
|
329
|
+
/*
|
|
330
|
+
* Włączenie albo podmiana nasłuchu pozycji zeruje jego stan - liczone w renderze (wzorzec
|
|
331
|
+
* poprzedniej wartości), a nie w efekcie, który pokazałby jedną klatkę starej pozycji.
|
|
332
|
+
*/
|
|
333
|
+
const watching = showUserLocation && !!geolocation;
|
|
334
|
+
const [watchSource, setWatchSource] = useState({ watching, geolocation });
|
|
335
|
+
if (watchSource.watching !== watching || watchSource.geolocation !== geolocation) {
|
|
336
|
+
setWatchSource({ watching, geolocation });
|
|
337
|
+
setUserPosition(null);
|
|
338
|
+
setLocationBlocked(false);
|
|
339
|
+
}
|
|
340
|
+
// Handlery nasłuchu widzą zawsze świeże `followUser` i callbacki, a ich zmiana nie restartuje nasłuchu.
|
|
341
|
+
const handlePosition = useEffectEvent((position) => {
|
|
342
|
+
setUserPosition(position);
|
|
343
|
+
onUserPositionChange?.(position);
|
|
344
|
+
if (position && followUser) {
|
|
345
|
+
const map = engineRef.current;
|
|
346
|
+
if (map)
|
|
347
|
+
map.setView(position.at, Math.max(map.getZoom(), 13));
|
|
348
|
+
}
|
|
349
|
+
});
|
|
350
|
+
const handleLocationError = useEffectEvent((error) => {
|
|
351
|
+
setUserPosition(null);
|
|
352
|
+
setLocationBlocked(true);
|
|
353
|
+
onUserLocationError?.(error);
|
|
354
|
+
});
|
|
310
355
|
// Śledzenie pozycji użytkownika. Odmowa zgody wyłącza kontrolkę, ale nie wywraca komponentu.
|
|
311
356
|
useEffect(() => {
|
|
312
|
-
if (!showUserLocation || !geolocation)
|
|
313
|
-
setUserPosition(null);
|
|
357
|
+
if (!showUserLocation || !geolocation)
|
|
314
358
|
return;
|
|
315
|
-
|
|
316
|
-
setLocationBlocked(false);
|
|
317
|
-
return geolocation.watch((position) => {
|
|
318
|
-
setUserPosition(position);
|
|
319
|
-
onUserPositionChange?.(position);
|
|
320
|
-
if (position && followUser) {
|
|
321
|
-
const map = engineRef.current;
|
|
322
|
-
if (map)
|
|
323
|
-
map.setView(position.at, Math.max(map.getZoom(), 13));
|
|
324
|
-
}
|
|
325
|
-
}, (error) => {
|
|
326
|
-
setUserPosition(null);
|
|
327
|
-
setLocationBlocked(true);
|
|
328
|
-
onUserLocationError?.(error);
|
|
329
|
-
});
|
|
330
|
-
// `followUser` czytamy w callbacku, więc nie restartuje nasłuchu.
|
|
359
|
+
return geolocation.watch((position) => handlePosition(position), (error) => handleLocationError(error));
|
|
331
360
|
}, [showUserLocation, geolocation]);
|
|
332
361
|
const popupCategory = popup ? categoryById.get(popup.location.categoryId) : undefined;
|
|
333
362
|
/**
|
|
334
363
|
* Odległość otwartego punktu od `distanceFrom` (albo od użytkownika) w metrach. `null`, gdy
|
|
335
|
-
* nie ma od czego liczyć
|
|
364
|
+
* nie ma od czego liczyć - treść popupu chowa wtedy wiersz dystansu zamiast pokazywać zero.
|
|
336
365
|
*/
|
|
337
366
|
const popupDistance = useMemo(() => {
|
|
338
367
|
if (!popup)
|
|
@@ -343,31 +372,31 @@ export function KptMap({ locations, categories, country = 'PL', view = null, bou
|
|
|
343
372
|
/** Pozycje nakładek w pikselach kontenera; zależą od `viewportTick`, czyli od ruchu mapy. */
|
|
344
373
|
const popupPosition = useMemo(() => {
|
|
345
374
|
void viewportTick;
|
|
346
|
-
if (!popup || !
|
|
375
|
+
if (!popup || !liveEngine)
|
|
347
376
|
return null;
|
|
348
|
-
return
|
|
349
|
-
}, [viewportTick, popup,
|
|
377
|
+
return liveEngine.project(popup.location.at);
|
|
378
|
+
}, [viewportTick, popup, liveEngine]);
|
|
350
379
|
const contextPosition = useMemo(() => {
|
|
351
380
|
void viewportTick;
|
|
352
|
-
if (!contextTarget || !
|
|
381
|
+
if (!contextTarget || !liveEngine)
|
|
353
382
|
return null;
|
|
354
|
-
return
|
|
355
|
-
}, [viewportTick, contextTarget,
|
|
356
|
-
/** Kategorie oferowane w menu
|
|
383
|
+
return liveEngine.project(contextTarget.at);
|
|
384
|
+
}, [viewportTick, contextTarget, liveEngine]);
|
|
385
|
+
/** Kategorie oferowane w menu "dodaj" - ukryte w legendzie odpadają. */
|
|
357
386
|
const creatableCategories = useMemo(() => {
|
|
358
387
|
const hiddenSet = new Set(hidden);
|
|
359
388
|
return categories.filter((category) => !hiddenSet.has(category.id) && (category.fields?.length || renderCreateForm));
|
|
360
389
|
}, [categories, hidden, renderCreateForm]);
|
|
361
|
-
/** Czy jest czym szukać
|
|
390
|
+
/** Czy jest czym szukać - port bez `search()` nie zasługuje na pole, które nic nie robi. */
|
|
362
391
|
const searchEnabled = search && !!geocoder?.search;
|
|
363
392
|
/**
|
|
364
|
-
* Prostokąt preferowanych wyników: zasięg jawny, a bez niego preset kraju
|
|
393
|
+
* Prostokąt preferowanych wyników: zasięg jawny, a bez niego preset kraju - "Kwiatowa" ma
|
|
365
394
|
* trafiać w tę pod nosem. To sam bias: adresu z zagranicy dalej da się poszukać.
|
|
366
395
|
*/
|
|
367
396
|
const searchViewbox = bounds ?? resolveCountryView(country).bounds;
|
|
368
397
|
/**
|
|
369
398
|
* Twarde zawężenie do krajów. Jawne `searchOptions.countryCodes` wygrywa; bez niego odcinamy
|
|
370
|
-
* zagranicę dopiero przy `restrictToCountry`
|
|
399
|
+
* zagranicę dopiero przy `restrictToCountry` - tam mapa i tak nie pozwoli tam przesunąć widoku.
|
|
371
400
|
*/
|
|
372
401
|
const searchCountryCodes = searchOptions?.countryCodes ?? (restrictToCountry ? [country] : null);
|
|
373
402
|
const closeCreate = () => {
|
|
@@ -403,11 +432,11 @@ export function KptMap({ locations, categories, country = 'PL', view = null, bou
|
|
|
403
432
|
map?.setView(result.at, searchZoom);
|
|
404
433
|
onSearchSelect?.(result);
|
|
405
434
|
};
|
|
406
|
-
return (_jsxs("div", { className: cn('kpt-map', className), "data-engine":
|
|
435
|
+
return (_jsxs("div", { className: cn('kpt-map', className), "data-engine": liveEngine?.kind, style: { ...style, '--kpt-map-height': height }, ...rest, children: [_jsxs("div", { className: "kpt-map__viewport", children: [_jsx("div", { ref: canvasRef, className: "kpt-map__canvas" }), _jsxs("div", { className: "kpt-map__controls", role: "group", "aria-label": t('map.legend'), children: [_jsx("button", { className: "kpt-map__control", type: "button", "aria-label": t('map.zoomIn'), title: t('map.zoomIn'), disabled: !ready, onClick: () => zoomBy(1), children: _jsx(KptIcon, { name: "plus" }) }), _jsx("button", { className: "kpt-map__control", type: "button", "aria-label": t('map.zoomOut'), title: t('map.zoomOut'), disabled: !ready, onClick: () => zoomBy(-1), children: _jsx(KptIcon, { name: "minus" }) }), showUserLocation ? (_jsx("button", { className: "kpt-map__control kpt-map__control--locate", type: "button", "aria-label": locationBlocked ? t('map.locationDenied') : t('map.locateMe'), title: locationBlocked ? t('map.locationDenied') : t('map.locateMe'), disabled: !userPosition, onClick: centreOnUser, children: _jsx("span", { className: "kpt-map__crosshair", "aria-hidden": "true" }) })) : null] }), searchEnabled ? (_jsx("div", { className: "kpt-map__search", children: _jsx(KptMapSearch, { geocoder: geocoder, placeholder: searchPlaceholder, minLength: searchOptions?.minLength ?? 3, debounce: searchOptions?.debounce ?? 300, limit: searchOptions?.limit ?? 5, viewbox: searchViewbox, countryCodes: searchCountryCodes, speech: searchSpeech, speechLang: searchSpeechLang, speechColor: searchSpeechColor, onSelect: handleSearchSelect, onClear: () => setSearchPin(null) }) })) : null, allowCreate && contextTarget && contextPosition ? (_jsxs("div", { className: "kpt-map__context", role: "menu", "aria-label": t('map.addLocation'), style: { '--_kpt-map-x': `${contextPosition.x}px`, '--_kpt-map-y': `${contextPosition.y}px` }, children: [_jsx("p", { className: "kpt-map__context-title", children: t('map.chooseCategory') }), creatableCategories.length === 0 ? (_jsx("p", { className: "kpt-map__context-empty", children: t('map.empty') })) : (creatableCategories.map((category) => (_jsxs("button", { className: "kpt-map__context-item", type: "button", role: "menuitem", onClick: () => {
|
|
407
436
|
setContextTarget(null);
|
|
408
437
|
setCreateFor({ at: contextTarget.at, category });
|
|
409
438
|
setCreateOpen(true);
|
|
410
|
-
}, children: [_jsx("span", { className: "kpt-map__context-dot", "aria-hidden": "true" }), category.label] }, category.id))))] })) : null, popup && popupPosition ? (_jsxs("div", { ref: popupRef, className: "kpt-map__popup", role: "dialog", "data-layout": popupLayout, "aria-label": popup.location.title, style: { '--_kpt-map-x': `${popupPosition.x}px`, '--_kpt-map-y': `${popupPosition.y}px` }, children: [popupLayout === 'plain' ? (_jsx("button", { className: "kpt-map__popup-close", type: "button", "aria-label": t('common.close'), onClick: () => setPopup(null), children: _jsx("span", { "aria-hidden": "true", children: "
|
|
439
|
+
}, children: [_jsx("span", { className: "kpt-map__context-dot", "aria-hidden": "true" }), category.label] }, category.id))))] })) : null, popup && popupPosition ? (_jsxs("div", { ref: popupRef, className: "kpt-map__popup", role: "dialog", "data-layout": popupLayout, "aria-label": popup.location.title, style: { '--_kpt-map-x': `${popupPosition.x}px`, '--_kpt-map-y': `${popupPosition.y}px` }, children: [popupLayout === 'plain' ? (_jsx("button", { className: "kpt-map__popup-close", type: "button", "aria-label": t('common.close'), onClick: () => setPopup(null), children: _jsx("span", { "aria-hidden": "true", children: "x" }) })) : null, popup.group ? (_jsxs(_Fragment, { children: [_jsx("p", { className: "kpt-map__popup-group", children: t('map.clusterItems') }), _jsx("div", { className: "kpt-map__popup-tabs", role: "tablist", children: popup.group.items.map((item) => (_jsx("button", { className: "kpt-map__popup-tab", type: "button", role: "tab", "aria-selected": item.id === popup.location.id, onClick: () => {
|
|
411
440
|
setPopup({ location: item, group: popup.group });
|
|
412
441
|
onLocationClick?.(item);
|
|
413
442
|
}, children: item.title }, item.id))) })] })) : null, _jsx("div", { className: "kpt-map__popup-body", children: renderPopup ? (renderPopup({
|
package/dist/map/ports.d.ts
CHANGED
|
@@ -3,10 +3,10 @@ export interface KptMapSearchOptions {
|
|
|
3
3
|
/** Język odpowiedzi. Domyślnie to, co ustawi dostawca. */
|
|
4
4
|
language?: string;
|
|
5
5
|
signal?: AbortSignal;
|
|
6
|
-
/** Ile wyników najwyżej. Domyślnie 5
|
|
6
|
+
/** Ile wyników najwyżej. Domyślnie 5 - to lista podpowiedzi, nie wyszukiwarka pełnotekstowa. */
|
|
7
7
|
limit?: number;
|
|
8
8
|
/**
|
|
9
|
-
* Preferuj wyniki z tego prostokąta. **Nie odcina** reszty świata
|
|
9
|
+
* Preferuj wyniki z tego prostokąta. **Nie odcina** reszty świata - adres z zagranicy
|
|
10
10
|
* dalej da się znaleźć, tylko wypada niżej na liście.
|
|
11
11
|
*/
|
|
12
12
|
viewbox?: KptMapBounds;
|
|
@@ -20,12 +20,12 @@ export interface KptMapGeocoderPort {
|
|
|
20
20
|
signal?: AbortSignal;
|
|
21
21
|
}): Promise<KptMapAddress | null>;
|
|
22
22
|
/**
|
|
23
|
-
* Geokodowanie wprost: tekst
|
|
23
|
+
* Geokodowanie wprost: tekst -> miejsca. Metoda jest **opcjonalna** - port bez niej po prostu
|
|
24
24
|
* nie dostaje pola wyszukiwania (komponent go wtedy nie renderuje).
|
|
25
25
|
*/
|
|
26
26
|
search?(query: string, options?: KptMapSearchOptions): Promise<readonly KptMapSuggestion[]>;
|
|
27
27
|
/**
|
|
28
|
-
* Uzupełnia podpowiedź o współrzędne. Wołane **raz, po wybraniu pozycji z listy**
|
|
28
|
+
* Uzupełnia podpowiedź o współrzędne. Wołane **raz, po wybraniu pozycji z listy** - nigdy
|
|
29
29
|
* w trakcie pisania. Potrzebne tylko dostawcom, których `search()` nie zwraca `at`.
|
|
30
30
|
*/
|
|
31
31
|
resolve?(suggestion: KptMapSuggestion, options?: {
|
|
@@ -35,17 +35,17 @@ export interface KptMapGeocoderPort {
|
|
|
35
35
|
}
|
|
36
36
|
export interface KptMapGeolocationPort {
|
|
37
37
|
/**
|
|
38
|
-
* Śledzi pozycję użytkownika. Zwraca funkcję sprzątającą
|
|
38
|
+
* Śledzi pozycję użytkownika. Zwraca funkcję sprzątającą - tak samo jak efekt Reacta,
|
|
39
39
|
* więc wpina się bez adaptera.
|
|
40
40
|
*/
|
|
41
41
|
watch(onPosition: (position: KptMapUserPosition | null) => void, onError: (error: KptMapGeolocationError) => void): () => void;
|
|
42
42
|
}
|
|
43
43
|
/** Geolokalizacja przeglądarki. Brak zgody czy brak API nie wywraca komponentu. */
|
|
44
44
|
export declare function browserGeolocation(options?: PositionOptions): KptMapGeolocationPort;
|
|
45
|
-
/** Stała pozycja
|
|
45
|
+
/** Stała pozycja - do dem, testów i SSR, gdzie prawdziwej geolokalizacji nie ma. */
|
|
46
46
|
export declare function geolocationStub(at: KptLatLng, accuracy?: number): KptMapGeolocationPort;
|
|
47
47
|
export interface KptNominatimOptions {
|
|
48
|
-
/** Adres instancji Nominatim. Domyślnie publiczna instancja OSM
|
|
48
|
+
/** Adres instancji Nominatim. Domyślnie publiczna instancja OSM - patrz ostrzeżenie niżej. */
|
|
49
49
|
baseUrl?: string;
|
|
50
50
|
/** Język odpowiedzi (`Accept-Language`). Domyślnie język przeglądarki. */
|
|
51
51
|
language?: string;
|
|
@@ -54,7 +54,7 @@ export interface KptNominatimOptions {
|
|
|
54
54
|
/**
|
|
55
55
|
* Najmniejszy odstęp między zapytaniami w milisekundach.
|
|
56
56
|
*
|
|
57
|
-
* Sekunda nie jest cechą Nominatima, tylko wymogiem regulaminu **publicznej instancji OSM**
|
|
57
|
+
* Sekunda nie jest cechą Nominatima, tylko wymogiem regulaminu **publicznej instancji OSM** -
|
|
58
58
|
* dlatego domyślna zależy od `baseUrl`: bez niego 1000 ms, z własnym adresem 0.
|
|
59
59
|
*/
|
|
60
60
|
minIntervalMs?: number;
|
|
@@ -63,7 +63,7 @@ export interface KptNominatimOptions {
|
|
|
63
63
|
* Adapter Nominatim (OpenStreetMap): geokodowanie wsteczne i podpowiedzi adresowe.
|
|
64
64
|
*
|
|
65
65
|
* Kolejkuje zapytania z odstępem sekundy, bo tego wymaga regulamin publicznej instancji.
|
|
66
|
-
* Odstęp liczony od zegara, a nie stałym opóźnieniem przed każdym zapytaniem
|
|
66
|
+
* Odstęp liczony od zegara, a nie stałym opóźnieniem przed każdym zapytaniem - inaczej
|
|
67
67
|
* pierwsza podpowiedź po minucie bezczynności też płaciłaby sekundę.
|
|
68
68
|
*
|
|
69
69
|
* **Nie rejestruj publicznej instancji w produkcji bez przeczytania jej polityki użycia.**
|
package/dist/map/ports.js
CHANGED
|
@@ -26,7 +26,7 @@ function kindOf(error) {
|
|
|
26
26
|
return 'unavailable';
|
|
27
27
|
return 'timeout';
|
|
28
28
|
}
|
|
29
|
-
/** Stała pozycja
|
|
29
|
+
/** Stała pozycja - do dem, testów i SSR, gdzie prawdziwej geolokalizacji nie ma. */
|
|
30
30
|
export function geolocationStub(at, accuracy = 30) {
|
|
31
31
|
return {
|
|
32
32
|
watch(onPosition) {
|
|
@@ -42,7 +42,7 @@ const NOMINATIM_MIN_INTERVAL_MS = 1000;
|
|
|
42
42
|
* Adapter Nominatim (OpenStreetMap): geokodowanie wsteczne i podpowiedzi adresowe.
|
|
43
43
|
*
|
|
44
44
|
* Kolejkuje zapytania z odstępem sekundy, bo tego wymaga regulamin publicznej instancji.
|
|
45
|
-
* Odstęp liczony od zegara, a nie stałym opóźnieniem przed każdym zapytaniem
|
|
45
|
+
* Odstęp liczony od zegara, a nie stałym opóźnieniem przed każdym zapytaniem - inaczej
|
|
46
46
|
* pierwsza podpowiedź po minucie bezczynności też płaciłaby sekundę.
|
|
47
47
|
*
|
|
48
48
|
* **Nie rejestruj publicznej instancji w produkcji bez przeczytania jej polityki użycia.**
|
|
@@ -50,7 +50,7 @@ const NOMINATIM_MIN_INTERVAL_MS = 1000;
|
|
|
50
50
|
export function createNominatimGeocoder(options = {}) {
|
|
51
51
|
const baseUrl = options.baseUrl ?? NOMINATIM_URL;
|
|
52
52
|
const minInterval = options.minIntervalMs ?? (options.baseUrl ? 0 : NOMINATIM_MIN_INTERVAL_MS);
|
|
53
|
-
/** Ogon kolejki
|
|
53
|
+
/** Ogon kolejki - każde kolejne zapytanie czeka na swoją kolej. */
|
|
54
54
|
let next = Promise.resolve();
|
|
55
55
|
let lastAt = 0;
|
|
56
56
|
const slot = () => {
|
|
@@ -89,7 +89,7 @@ export function createNominatimGeocoder(options = {}) {
|
|
|
89
89
|
return toAddress((await response.json()));
|
|
90
90
|
}
|
|
91
91
|
catch {
|
|
92
|
-
// Brak adresu to nie awaria formularza
|
|
92
|
+
// Brak adresu to nie awaria formularza - pole zostaje puste.
|
|
93
93
|
return null;
|
|
94
94
|
}
|
|
95
95
|
},
|
|
@@ -147,7 +147,7 @@ function toAddress(place) {
|
|
|
147
147
|
return {
|
|
148
148
|
street,
|
|
149
149
|
houseNumber,
|
|
150
|
-
// Jedno pole
|
|
150
|
+
// Jedno pole "Adres" - ulica z numerem, bo tak wypełnia się formularz.
|
|
151
151
|
address: [street, houseNumber].filter(Boolean).join(' '),
|
|
152
152
|
city: address.city ?? address.town ?? address.village ?? address.municipality ?? address.hamlet ?? '',
|
|
153
153
|
postcode: address.postcode ?? '',
|
|
@@ -167,7 +167,7 @@ function toSuggestion(place) {
|
|
|
167
167
|
id: String(place.place_id ?? place.display_name ?? at.lat + ':' + at.lng),
|
|
168
168
|
label: place.display_name ?? '',
|
|
169
169
|
at,
|
|
170
|
-
// Punktowy
|
|
170
|
+
// Punktowy "prostokąt" pojedynczego budynku nie nadaje się do wpasowania w kadr.
|
|
171
171
|
bounds: bounds && isFittableBounds(bounds) ? bounds : undefined,
|
|
172
172
|
address: toAddress(place) ?? undefined,
|
|
173
173
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* @konce-pt/react/map
|
|
2
|
+
* @konce-pt/react/map - mapa z konfigurowalnymi znacznikami. Osobny subpath, bo silnik mapowy
|
|
3
3
|
* i style mapy są ciężkie względem rdzenia biblioteki i nie każda aplikacja ich potrzebuje.
|
|
4
4
|
*
|
|
5
5
|
* Silnik Leaflet wymaga zainstalowanej paczki `leaflet` (peer opcjonalny) i arkusza
|
|
@@ -14,6 +14,6 @@ export { KptMapHoursField } from "../map/map-hours-field.js";
|
|
|
14
14
|
export { KptMapImageField } from "../map/map-image-field.js";
|
|
15
15
|
// Porty: geokoder i geolokalizacja. W Reakcie podaje się je propem, nie przez DI.
|
|
16
16
|
export { browserGeolocation, geolocationStub, createNominatimGeocoder, createGeocoderStub } from "../map/ports.js";
|
|
17
|
-
// Silniki i rdzeń (pure TS)
|
|
17
|
+
// Silniki i rdzeń (pure TS) - wspólne z portem Angulara.
|
|
18
18
|
export { createLeafletEngine, createGoogleEngine, loadGoogleMapsApi } from '@konce-pt/map';
|
|
19
19
|
export { isCluster, clusterLocations, dominantCategory, countByCategory, isSplittable, KPT_CLUSTER_DEFAULTS, KPT_MAP_COUNTRIES, KPT_MAP_DEFAULT_COUNTRY, countryPreset, countryCodes, resolveCountryView, centerOfBounds, project, unproject, boundsOf, boundsCenter, isFittableBounds, KPT_MIN_FIT_SPAN, boundsContain, padBounds, haversine, metersPerPixel, zoomForBounds, clampLat, normalizeLng, worldSize, KPT_TILE_SIZE, KPT_MAX_LATITUDE, KPT_EARTH_RADIUS, renderMarkerHtml, renderClusterHtml, renderUserMarkerHtml, colorVarOf, contrastVarOf, foregroundVarOf, clusterTier, KPT_MAP_DEFAULT_TILE_URL, KPT_MAP_DEFAULT_ATTRIBUTION, parseTime, formatTime, isoWeekday, isOpenAt, isAlwaysOpen, nextChange, weekdayName, groupWeek, emptyWeek, formatDistance, formatLatLng, } from '@konce-pt/map';
|
package/dist/megamenu/llms.txt
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# KptMegamenu (@konce-pt/react)
|
|
2
2
|
|
|
3
|
-
A mega menu
|
|
3
|
+
A mega menu - a horizontal bar with a wide, multi-column panel.
|
|
4
4
|
Import: `import { KptMegamenu } from '@konce-pt/react';`
|
|
5
5
|
|
|
6
6
|
## DOM
|
|
7
7
|
<div class="kpt-megamenu-host">
|
|
8
8
|
<div class="kpt-megamenu" role="menubar" aria-orientation="horizontal" aria-label="Main menu">
|
|
9
9
|
<button class="kpt-megamenu__top is-open" role="menuitem" tabindex="0"
|
|
10
|
-
aria-haspopup="menu" aria-expanded="true" aria-controls="kpt-megamenu-r1">Products
|
|
10
|
+
aria-haspopup="menu" aria-expanded="true" aria-controls="kpt-megamenu-r1">Products ...caret...</button>
|
|
11
11
|
</div>
|
|
12
12
|
</div>
|
|
13
13
|
<!-- while open, in a portal: -->
|
|
@@ -19,9 +19,9 @@ Import: `import { KptMegamenu } from '@konce-pt/react';`
|
|
|
19
19
|
</div>
|
|
20
20
|
|
|
21
21
|
## Props
|
|
22
|
-
- `items`: `KptMenuItem<T>[]`
|
|
23
|
-
- `onSelect(link)`
|
|
24
|
-
- `ariaLabel`: string
|
|
22
|
+
- `items`: `KptMenuItem<T>[]` - three levels: top item -> `items` (columns) -> column `items` (links)
|
|
23
|
+
- `onSelect(link)` - a picked link from a column, or a top item without columns
|
|
24
|
+
- `ariaLabel`: string - the bar's name for screen readers, e.g. "Main menu"
|
|
25
25
|
- `className`, `ref` and every other `<div>` prop go to the host
|
|
26
26
|
|
|
27
27
|
## The panel is anchored to the whole bar
|
|
@@ -32,7 +32,7 @@ without closing the panel, so browsing across sections costs one click each.
|
|
|
32
32
|
## Examples
|
|
33
33
|
<KptMegamenu items={items} onSelect={onSelect} />
|
|
34
34
|
|
|
35
|
-
// top
|
|
35
|
+
// top -> columns -> links
|
|
36
36
|
const items: KptMenuItem[] = [{
|
|
37
37
|
label: 'Products',
|
|
38
38
|
items: [
|
|
@@ -43,7 +43,7 @@ without closing the panel, so browsing across sections costs one click each.
|
|
|
43
43
|
|
|
44
44
|
## Links (`url`)
|
|
45
45
|
An entry with `url` and no children (and not disabled) renders as a real `<a href>` with the same
|
|
46
|
-
look, so the browser handles navigation
|
|
46
|
+
look, so the browser handles navigation - opening in a new tab, the status-bar preview. `onSelect`
|
|
47
47
|
still fires before the browser follows the link.
|
|
48
48
|
|
|
49
49
|
## Tokens
|
|
@@ -64,3 +64,14 @@ along it, `ArrowDown` (or `Enter`/`Space`) opens the panel on the first link, `A
|
|
|
64
64
|
In the panel `ArrowDown`/`ArrowUp`/`Home`/`End` walk through all links column by column,
|
|
65
65
|
`ArrowLeft`/`ArrowRight` show the neighbouring item's panel, `Escape` or `Tab` close it and return
|
|
66
66
|
focus to the top item.
|
|
67
|
+
|
|
68
|
+
## Router
|
|
69
|
+
Links are plain `<a href>`, so without a router a click reloads the page. `onNavigate` fires on a plain
|
|
70
|
+
click (left button, no Ctrl/Cmd/Shift, no `target="_blank"`) with `{ item, url, event }`: call
|
|
71
|
+
`event.preventDefault()` and navigate with your router. Modified clicks stay native (a new tab, a new
|
|
72
|
+
window). The library depends on no router.
|
|
73
|
+
|
|
74
|
+
<KptMegamenu items={items} onNavigate={({ event, url }) => { event.preventDefault(); navigate(url); }} />
|
|
75
|
+
|
|
76
|
+
## When to use it
|
|
77
|
+
The `menubar`/`menu` roles switch a screen reader into application mode: page navigation by headings and links stops, and arrow keys are expected. That fits an application menu (File, Edit, View) or a large product menu. For plain site navigation (a few links in a header) the WAI-ARIA APG recommends the disclosure pattern instead - a `<nav>` with buttons carrying `aria-expanded` and ordinary link lists - which you can build from `kpt-button` and lists. The menu does not render a third level: nested `items` of a submenu item are ignored.
|
|
@@ -1,15 +1,21 @@
|
|
|
1
1
|
import { type ComponentPropsWithRef } from 'react';
|
|
2
|
+
import { type KptNavigateEvent } from '../shared/types.ts';
|
|
2
3
|
import type { KptMenuItem } from '../menu/menu-item.ts';
|
|
3
4
|
export interface KptMegamenuProps<T> extends Omit<ComponentPropsWithRef<'div'>, 'onSelect'> {
|
|
4
5
|
items?: readonly KptMenuItem<T>[];
|
|
5
6
|
onSelect?: (item: KptMenuItem<T>) => void;
|
|
6
|
-
/**
|
|
7
|
+
/**
|
|
8
|
+
* Zwykły klik w pozycję z `url`. Aplikacja z routerem woła `event.preventDefault()` i nawiguje
|
|
9
|
+
* sama - bez tego `<a href>` przeładowuje stronę.
|
|
10
|
+
*/
|
|
11
|
+
onNavigate?: (event: KptNavigateEvent<KptMenuItem<T>>) => void;
|
|
12
|
+
/** Nazwa belki dla czytników ekranu, np. "Menu główne". */
|
|
7
13
|
ariaLabel?: string;
|
|
8
14
|
}
|
|
9
15
|
/**
|
|
10
|
-
* Mega-menu
|
|
11
|
-
* Struktura: pozycja górna
|
|
12
|
-
* Pozycja górna bez kolumn działa jak akcja
|
|
16
|
+
* Mega-menu - pozioma belka z szerokim, wielokolumnowym panelem.
|
|
17
|
+
* Struktura: pozycja górna -> `items` (kolumny) -> `items` kolumny (odnośniki).
|
|
18
|
+
* Pozycja górna bez kolumn działa jak akcja - wywołuje `onSelect` zamiast otwierać pusty panel.
|
|
13
19
|
*
|
|
14
20
|
* Panel kotwiczy się do CAŁEJ belki, nie do pojedynczego przycisku: jest szeroki, więc ma
|
|
15
21
|
* zaczynać się przy krawędzi menu, a nie przy przypadkowej pozycji, w którą kliknięto.
|
|
@@ -17,10 +23,10 @@ export interface KptMegamenuProps<T> extends Omit<ComponentPropsWithRef<'div'>,
|
|
|
17
23
|
* Klawiatura jak w `KptMenubar`: belka to jeden przystanek Tab, strzałki w lewo/prawo
|
|
18
24
|
* i `Home`/`End` chodzą po pozycjach górnych, strzałka w dół (albo `Enter`/spacja) otwiera panel
|
|
19
25
|
* z fokusem na pierwszym odnośniku. W panelu strzałki w górę/dół przechodzą po wszystkich
|
|
20
|
-
* odnośnikach kolumna po kolumnie, w lewo/prawo
|
|
26
|
+
* odnośnikach kolumna po kolumnie, w lewo/prawo - do panelu sąsiedniej pozycji, a `Escape`
|
|
21
27
|
* zamyka panel i oddaje fokus belce.
|
|
22
28
|
*
|
|
23
29
|
* @example
|
|
24
30
|
* <KptMegamenu items={items} onSelect={onSelect} />
|
|
25
31
|
*/
|
|
26
|
-
export declare function KptMegamenu<T>({ items, onSelect, ariaLabel, className, ...rest }: Readonly<KptMegamenuProps<T>>): import("react").JSX.Element;
|
|
32
|
+
export declare function KptMegamenu<T>({ items, onSelect, onNavigate, ariaLabel, className, ...rest }: Readonly<KptMegamenuProps<T>>): import("react").JSX.Element;
|