@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.
Files changed (382) hide show
  1. package/README.md +128 -64
  2. package/dist/accordion/accordion.d.ts +14 -6
  3. package/dist/accordion/accordion.js +25 -17
  4. package/dist/accordion/llms.txt +22 -10
  5. package/dist/alert/alert.d.ts +2 -2
  6. package/dist/alert/alert.js +3 -2
  7. package/dist/alert/llms.txt +10 -9
  8. package/dist/app-shell/app-shell.d.ts +14 -14
  9. package/dist/app-shell/app-shell.js +22 -15
  10. package/dist/app-shell/llms.txt +16 -15
  11. package/dist/auth/auth-types.d.ts +6 -6
  12. package/dist/auth/auth.d.ts +2 -2
  13. package/dist/auth/auth.js +24 -15
  14. package/dist/auth/llms.txt +12 -11
  15. package/dist/autocomplete/autocomplete.d.ts +19 -6
  16. package/dist/autocomplete/autocomplete.js +32 -11
  17. package/dist/autocomplete/llms.txt +19 -5
  18. package/dist/avatar/avatar.d.ts +6 -6
  19. package/dist/avatar/avatar.js +6 -5
  20. package/dist/avatar/llms.txt +10 -10
  21. package/dist/avatar-group/avatar-group.d.ts +4 -4
  22. package/dist/avatar-group/avatar-group.js +4 -3
  23. package/dist/avatar-group/llms.txt +8 -8
  24. package/dist/backdrop/backdrop.d.ts +7 -26
  25. package/dist/backdrop/backdrop.js +32 -27
  26. package/dist/backdrop/llms.txt +27 -27
  27. package/dist/badge/badge.d.ts +3 -3
  28. package/dist/badge/badge.js +1 -1
  29. package/dist/badge/llms.txt +11 -11
  30. package/dist/bottom-sheet/bottom-sheet.d.ts +8 -3
  31. package/dist/bottom-sheet/bottom-sheet.js +5 -4
  32. package/dist/bottom-sheet/llms.txt +11 -8
  33. package/dist/breadcrumb/breadcrumb.d.ts +8 -2
  34. package/dist/breadcrumb/breadcrumb.js +11 -3
  35. package/dist/breadcrumb/llms.txt +10 -2
  36. package/dist/button/button.d.ts +3 -3
  37. package/dist/button/button.js +10 -9
  38. package/dist/button/llms.txt +27 -19
  39. package/dist/button-group/button-group.d.ts +18 -5
  40. package/dist/button-group/button-group.js +54 -6
  41. package/dist/button-group/llms.txt +27 -18
  42. package/dist/card/card.d.ts +8 -8
  43. package/dist/card/card.js +10 -9
  44. package/dist/card/llms.txt +20 -20
  45. package/dist/carousel/carousel.d.ts +6 -6
  46. package/dist/carousel/carousel.js +8 -5
  47. package/dist/carousel/llms.txt +10 -10
  48. package/dist/charts/chart-legend.d.ts +3 -3
  49. package/dist/charts/chart-legend.js +2 -1
  50. package/dist/charts/chart-tooltip.d.ts +3 -3
  51. package/dist/charts/chart-tooltip.js +1 -1
  52. package/dist/charts/chart.d.ts +4 -3
  53. package/dist/charts/chart.js +18 -10
  54. package/dist/charts/llms.txt +10 -7
  55. package/dist/charts-entry/public-api.js +2 -2
  56. package/dist/chat/chat.d.ts +17 -10
  57. package/dist/chat/chat.js +77 -23
  58. package/dist/chat/llms.txt +39 -15
  59. package/dist/chat-composer/chat-composer.d.ts +20 -7
  60. package/dist/chat-composer/chat-composer.js +29 -7
  61. package/dist/chat-composer/llms.txt +20 -11
  62. package/dist/chat-dock/chat-dock.d.ts +11 -4
  63. package/dist/chat-dock/chat-dock.js +10 -6
  64. package/dist/chat-dock/llms.txt +15 -14
  65. package/dist/chat-entry/public-api.js +1 -1
  66. package/dist/chat-message/chat-message.d.ts +12 -5
  67. package/dist/chat-message/chat-message.js +19 -8
  68. package/dist/chat-message/llms.txt +16 -15
  69. package/dist/chat-typing/chat-typing.d.ts +2 -2
  70. package/dist/chat-typing/chat-typing.js +4 -3
  71. package/dist/chat-typing/llms.txt +6 -6
  72. package/dist/checkbox/checkbox.d.ts +13 -6
  73. package/dist/checkbox/checkbox.js +31 -5
  74. package/dist/checkbox/llms.txt +26 -11
  75. package/dist/chip/chip.d.ts +4 -4
  76. package/dist/chip/chip.js +3 -2
  77. package/dist/chip/llms.txt +11 -11
  78. package/dist/chips-input/chips-input.d.ts +6 -4
  79. package/dist/chips-input/chips-input.js +47 -12
  80. package/dist/chips-input/llms.txt +18 -8
  81. package/dist/clock/clock.d.ts +11 -6
  82. package/dist/clock/clock.js +38 -8
  83. package/dist/clock/llms.txt +12 -8
  84. package/dist/color-picker/color-picker.d.ts +9 -5
  85. package/dist/color-picker/color-picker.js +37 -18
  86. package/dist/color-picker/llms.txt +8 -5
  87. package/dist/confirm/confirm-container.d.ts +3 -3
  88. package/dist/confirm/confirm-container.js +6 -5
  89. package/dist/confirm/confirm-store.d.ts +4 -4
  90. package/dist/confirm/confirm-store.js +11 -5
  91. package/dist/confirm/llms.txt +8 -7
  92. package/dist/context-menu/context-menu.d.ts +1 -1
  93. package/dist/context-menu/context-menu.js +37 -14
  94. package/dist/context-menu/llms.txt +10 -7
  95. package/dist/data-table/data-table-types.d.ts +5 -5
  96. package/dist/data-table/data-table.d.ts +37 -5
  97. package/dist/data-table/data-table.js +426 -119
  98. package/dist/data-table/export-csv.d.ts +3 -3
  99. package/dist/data-table/export-csv.js +8 -10
  100. package/dist/data-table/llms.txt +127 -79
  101. package/dist/data-view/data-view.d.ts +13 -5
  102. package/dist/data-view/data-view.js +68 -6
  103. package/dist/data-view/llms.txt +29 -10
  104. package/dist/date-range/date-range.d.ts +20 -8
  105. package/dist/date-range/date-range.js +86 -63
  106. package/dist/date-range/llms.txt +32 -18
  107. package/dist/datepicker/datepicker.d.ts +22 -8
  108. package/dist/datepicker/datepicker.js +103 -71
  109. package/dist/datepicker/llms.txt +36 -22
  110. package/dist/dialog/dialog.d.ts +4 -4
  111. package/dist/dialog/dialog.js +32 -11
  112. package/dist/dialog/llms.txt +13 -10
  113. package/dist/divider/divider.d.ts +7 -1
  114. package/dist/divider/divider.js +3 -2
  115. package/dist/divider/llms.txt +4 -3
  116. package/dist/drawer/drawer.d.ts +9 -4
  117. package/dist/drawer/drawer.js +5 -4
  118. package/dist/drawer/llms.txt +13 -10
  119. package/dist/empty/empty.d.ts +2 -2
  120. package/dist/empty/empty.js +1 -1
  121. package/dist/empty/llms.txt +9 -9
  122. package/dist/fab/fab.d.ts +1 -1
  123. package/dist/fab/fab.js +1 -1
  124. package/dist/fab/llms.txt +10 -6
  125. package/dist/fieldset/fieldset.d.ts +18 -4
  126. package/dist/fieldset/fieldset.js +12 -6
  127. package/dist/fieldset/llms.txt +26 -9
  128. package/dist/file-upload/file-upload.d.ts +8 -2
  129. package/dist/file-upload/file-upload.js +22 -11
  130. package/dist/file-upload/llms.txt +12 -9
  131. package/dist/form-field/form-field-context.d.ts +4 -4
  132. package/dist/form-field/form-field.d.ts +12 -4
  133. package/dist/form-field/form-field.js +27 -6
  134. package/dist/form-field/llms.txt +25 -11
  135. package/dist/galleria/galleria.d.ts +3 -3
  136. package/dist/galleria/galleria.js +9 -6
  137. package/dist/galleria/llms.txt +7 -7
  138. package/dist/grid/col.d.ts +4 -4
  139. package/dist/grid/col.js +2 -2
  140. package/dist/grid/flex.d.ts +3 -3
  141. package/dist/grid/flex.js +1 -1
  142. package/dist/grid/grid.d.ts +7 -7
  143. package/dist/grid/grid.js +3 -3
  144. package/dist/grid/layout.d.ts +2 -2
  145. package/dist/grid/layout.js +2 -2
  146. package/dist/grid/llms.txt +15 -15
  147. package/dist/grid-entry/public-api.js +1 -1
  148. package/dist/i18n/i18n-context.d.ts +6 -6
  149. package/dist/i18n/i18n-context.js +27 -5
  150. package/dist/icon/icon-registry.d.ts +3 -3
  151. package/dist/icon/icon-registry.js +3 -3
  152. package/dist/icon/icon.d.ts +4 -4
  153. package/dist/icon/icon.js +10 -7
  154. package/dist/icon/llms.txt +13 -13
  155. package/dist/icon-button/icon-button.d.ts +2 -2
  156. package/dist/icon-button/icon-button.js +1 -1
  157. package/dist/icon-button/llms.txt +9 -5
  158. package/dist/icons-entry/public-api.d.ts +2 -2
  159. package/dist/icons-entry/public-api.js +3 -3
  160. package/dist/image/image.js +5 -5
  161. package/dist/image/llms.txt +13 -13
  162. package/dist/input/input.d.ts +18 -5
  163. package/dist/input/input.js +22 -6
  164. package/dist/input/llms.txt +25 -11
  165. package/dist/input-mask/input-mask.d.ts +7 -2
  166. package/dist/input-mask/input-mask.js +17 -43
  167. package/dist/input-mask/llms.txt +8 -5
  168. package/dist/input-number/input-number.d.ts +11 -9
  169. package/dist/input-number/input-number.js +25 -21
  170. package/dist/input-number/llms.txt +17 -14
  171. package/dist/input-otp/input-otp.d.ts +18 -5
  172. package/dist/input-otp/input-otp.js +48 -24
  173. package/dist/input-otp/llms.txt +18 -7
  174. package/dist/internal/calendar-focus.d.ts +1 -1
  175. package/dist/internal/calendar-focus.js +3 -3
  176. package/dist/internal/cn.d.ts +1 -1
  177. package/dist/internal/mask.d.ts +14 -0
  178. package/dist/internal/mask.js +62 -0
  179. package/dist/internal/number-group.d.ts +22 -0
  180. package/dist/internal/number-group.js +53 -0
  181. package/dist/internal/option-key.d.ts +6 -0
  182. package/dist/internal/option-key.js +8 -0
  183. package/dist/internal/option-nav.d.ts +4 -4
  184. package/dist/internal/option-nav.js +5 -5
  185. package/dist/internal/overlay-container.js +1 -1
  186. package/dist/internal/overlay.d.ts +3 -3
  187. package/dist/internal/overlay.js +40 -10
  188. package/dist/internal/password-strength.d.ts +6 -0
  189. package/dist/internal/password-strength.js +74 -0
  190. package/dist/internal/roving.d.ts +2 -2
  191. package/dist/internal/roving.js +3 -3
  192. package/dist/internal/scroll-lock.d.ts +6 -0
  193. package/dist/internal/scroll-lock.js +35 -0
  194. package/dist/internal/slot-children.d.ts +13 -0
  195. package/dist/internal/slot-children.js +31 -0
  196. package/dist/internal/slot.d.ts +3 -3
  197. package/dist/internal/slot.js +2 -2
  198. package/dist/internal/step.d.ts +12 -0
  199. package/dist/internal/step.js +31 -0
  200. package/dist/internal/tween.d.ts +5 -5
  201. package/dist/internal/tween.js +7 -8
  202. package/dist/internal/types.d.ts +2 -2
  203. package/dist/internal/use-breakpoint.d.ts +2 -2
  204. package/dist/internal/use-breakpoint.js +4 -4
  205. package/dist/internal/use-controllable-state.d.ts +5 -0
  206. package/dist/internal/use-controllable-state.js +25 -4
  207. package/dist/internal/use-id.d.ts +2 -2
  208. package/dist/internal/use-id.js +2 -2
  209. package/dist/internal/use-modal-focus.d.ts +2 -1
  210. package/dist/internal/use-modal-focus.js +14 -3
  211. package/dist/knob/knob.d.ts +7 -4
  212. package/dist/knob/knob.js +27 -22
  213. package/dist/knob/llms.txt +18 -11
  214. package/dist/listbox/listbox.d.ts +8 -6
  215. package/dist/listbox/listbox.js +37 -17
  216. package/dist/listbox/llms.txt +12 -9
  217. package/dist/loader/llms.txt +56 -0
  218. package/dist/loader/loader.d.ts +49 -0
  219. package/dist/loader/loader.js +68 -0
  220. package/dist/map/llms.txt +30 -23
  221. package/dist/map/map-create-dialog.d.ts +4 -4
  222. package/dist/map/map-create-dialog.js +36 -28
  223. package/dist/map/map-hours-field.d.ts +4 -4
  224. package/dist/map/map-hours-field.js +8 -7
  225. package/dist/map/map-image-field.d.ts +2 -2
  226. package/dist/map/map-image-field.js +6 -5
  227. package/dist/map/map-legend.d.ts +4 -4
  228. package/dist/map/map-legend.js +3 -2
  229. package/dist/map/map-poi-card.d.ts +3 -3
  230. package/dist/map/map-poi-card.js +10 -9
  231. package/dist/map/map-search.d.ts +17 -6
  232. package/dist/map/map-search.js +70 -43
  233. package/dist/map/map.d.ts +25 -13
  234. package/dist/map/map.js +86 -57
  235. package/dist/map/ports.d.ts +9 -9
  236. package/dist/map/ports.js +6 -6
  237. package/dist/map-entry/public-api.js +2 -2
  238. package/dist/megamenu/llms.txt +18 -7
  239. package/dist/megamenu/megamenu.d.ts +12 -6
  240. package/dist/megamenu/megamenu.js +23 -14
  241. package/dist/mention/llms.txt +15 -15
  242. package/dist/mention/mention.d.ts +5 -5
  243. package/dist/mention/mention.js +2 -2
  244. package/dist/mention/mentions.d.ts +1 -1
  245. package/dist/mention/mentions.js +1 -1
  246. package/dist/menu/llms.txt +12 -12
  247. package/dist/menu/menu-item.d.ts +4 -4
  248. package/dist/menu/menu-keys.d.ts +2 -2
  249. package/dist/menu/menu-keys.js +5 -5
  250. package/dist/menu/menu-rows.js +1 -0
  251. package/dist/menu/menu.d.ts +2 -2
  252. package/dist/menu/menu.js +15 -8
  253. package/dist/menubar/llms.txt +19 -8
  254. package/dist/menubar/menubar.d.ts +11 -5
  255. package/dist/menubar/menubar.js +23 -11
  256. package/dist/meter-group/llms.txt +12 -12
  257. package/dist/meter-group/meter-group.d.ts +2 -2
  258. package/dist/meter-group/meter-group.js +2 -1
  259. package/dist/motion/motion-context.d.ts +22 -0
  260. package/dist/motion/motion-context.js +28 -0
  261. package/dist/order-list/llms.txt +24 -9
  262. package/dist/order-list/order-list.d.ts +12 -4
  263. package/dist/order-list/order-list.js +123 -25
  264. package/dist/paginator/llms.txt +8 -8
  265. package/dist/paginator/paginator.js +7 -4
  266. package/dist/panel/llms.txt +25 -13
  267. package/dist/panel/panel.d.ts +11 -3
  268. package/dist/panel/panel.js +12 -7
  269. package/dist/password/llms.txt +19 -7
  270. package/dist/password/password.d.ts +2 -2
  271. package/dist/password/password.js +27 -16
  272. package/dist/peek/llms.txt +11 -11
  273. package/dist/peek/peek.d.ts +6 -6
  274. package/dist/peek/peek.js +41 -19
  275. package/dist/pick-list/llms.txt +35 -12
  276. package/dist/pick-list/pick-list.d.ts +12 -4
  277. package/dist/pick-list/pick-list.js +128 -24
  278. package/dist/popover/llms.txt +13 -10
  279. package/dist/popover/popover.d.ts +6 -4
  280. package/dist/popover/popover.js +14 -7
  281. package/dist/progress/llms.txt +17 -17
  282. package/dist/progress/progress.d.ts +7 -7
  283. package/dist/progress/progress.js +7 -6
  284. package/dist/public-api.d.ts +16 -4
  285. package/dist/public-api.js +13 -7
  286. package/dist/radio-group/llms.txt +16 -6
  287. package/dist/radio-group/radio-group.d.ts +12 -3
  288. package/dist/radio-group/radio-group.js +23 -4
  289. package/dist/rating/llms.txt +20 -13
  290. package/dist/rating/rating-pointer.d.ts +16 -0
  291. package/dist/rating/rating-pointer.js +26 -0
  292. package/dist/rating/rating.d.ts +6 -6
  293. package/dist/rating/rating.js +77 -12
  294. package/dist/rich-text/lists.d.ts +2 -2
  295. package/dist/rich-text/lists.js +7 -7
  296. package/dist/rich-text/llms.txt +35 -12
  297. package/dist/rich-text/rich-text.d.ts +13 -14
  298. package/dist/rich-text/rich-text.js +311 -142
  299. package/dist/rich-text/sanitize.d.ts +11 -2
  300. package/dist/rich-text/sanitize.js +80 -32
  301. package/dist/rich-text/tables.d.ts +3 -3
  302. package/dist/rich-text/tables.js +3 -3
  303. package/dist/roadmap/llms.txt +23 -17
  304. package/dist/roadmap/roadmap.d.ts +6 -6
  305. package/dist/roadmap/roadmap.js +102 -25
  306. package/dist/roadmap-entry/public-api.js +2 -2
  307. package/dist/scroll-top/llms.txt +6 -6
  308. package/dist/scroll-top/scroll-top.d.ts +1 -1
  309. package/dist/scroll-top/scroll-top.js +2 -1
  310. package/dist/select/llms.txt +22 -12
  311. package/dist/select/select.d.ts +10 -8
  312. package/dist/select/select.js +87 -47
  313. package/dist/shared/types.d.ts +22 -1
  314. package/dist/shared/types.js +9 -1
  315. package/dist/sidenav/llms.txt +9 -9
  316. package/dist/sidenav/sidenav.d.ts +2 -3
  317. package/dist/sidenav/sidenav.js +5 -4
  318. package/dist/skeleton/llms.txt +3 -3
  319. package/dist/skeleton/skeleton.d.ts +1 -1
  320. package/dist/skeleton/skeleton.js +1 -1
  321. package/dist/slider/llms.txt +34 -12
  322. package/dist/slider/slider-drag.d.ts +26 -0
  323. package/dist/slider/slider-drag.js +79 -0
  324. package/dist/slider/slider.d.ts +8 -5
  325. package/dist/slider/slider.js +38 -8
  326. package/dist/speech/llms.txt +97 -0
  327. package/dist/speech/speech-button.d.ts +44 -0
  328. package/dist/speech/speech-button.js +86 -0
  329. package/dist/speech/speech-wave.d.ts +19 -0
  330. package/dist/speech/speech-wave.js +16 -0
  331. package/dist/speech/use-speech-recognition.d.ts +81 -0
  332. package/dist/speech/use-speech-recognition.js +179 -0
  333. package/dist/speed-dial/llms.txt +14 -8
  334. package/dist/speed-dial/speed-dial.d.ts +1 -1
  335. package/dist/speed-dial/speed-dial.js +3 -2
  336. package/dist/spinner/llms.txt +7 -7
  337. package/dist/spinner/spinner.d.ts +1 -1
  338. package/dist/spinner/spinner.js +2 -1
  339. package/dist/split-button/llms.txt +21 -15
  340. package/dist/split-button/split-button.d.ts +2 -2
  341. package/dist/split-button/split-button.js +13 -6
  342. package/dist/splitter/llms.txt +11 -11
  343. package/dist/splitter/splitter.d.ts +3 -3
  344. package/dist/splitter/splitter.js +25 -10
  345. package/dist/stepper/llms.txt +35 -13
  346. package/dist/stepper/stepper.d.ts +6 -6
  347. package/dist/stepper/stepper.js +60 -13
  348. package/dist/switch/llms.txt +15 -5
  349. package/dist/switch/switch.d.ts +10 -3
  350. package/dist/switch/switch.js +18 -4
  351. package/dist/switch-group/llms.txt +11 -8
  352. package/dist/switch-group/switch-group.d.ts +6 -4
  353. package/dist/switch-group/switch-group.js +9 -7
  354. package/dist/tabs/llms.txt +12 -9
  355. package/dist/tabs/tabs.d.ts +5 -5
  356. package/dist/tabs/tabs.js +36 -20
  357. package/dist/textarea/llms.txt +23 -12
  358. package/dist/textarea/textarea.d.ts +19 -6
  359. package/dist/textarea/textarea.js +22 -7
  360. package/dist/timeline/llms.txt +4 -4
  361. package/dist/timeline/timeline.d.ts +1 -1
  362. package/dist/timeline/timeline.js +1 -1
  363. package/dist/toast/llms.txt +50 -17
  364. package/dist/toast/toast-container.d.ts +9 -3
  365. package/dist/toast/toast-container.js +18 -9
  366. package/dist/toast/toast-store.d.ts +42 -2
  367. package/dist/toast/toast-store.js +36 -1
  368. package/dist/toolbar/llms.txt +7 -7
  369. package/dist/toolbar/toolbar.d.ts +3 -3
  370. package/dist/toolbar/toolbar.js +2 -2
  371. package/dist/tooltip/llms.txt +14 -12
  372. package/dist/tooltip/tooltip.d.ts +5 -4
  373. package/dist/tooltip/tooltip.js +100 -41
  374. package/dist/tree/llms.txt +25 -11
  375. package/dist/tree/tree-keys.d.ts +5 -5
  376. package/dist/tree/tree-keys.js +7 -7
  377. package/dist/tree/tree.d.ts +17 -5
  378. package/dist/tree/tree.js +62 -20
  379. package/dist/update/llms.txt +54 -0
  380. package/dist/update/update.d.ts +49 -0
  381. package/dist/update/update.js +82 -0
  382. 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, boundsOf, clusterLocations, countByCategory, haversine, isCluster, isSplittable, renderClusterHtml, renderMarkerHtml, renderUserMarkerHtml, resolveCountryView, } from '@konce-pt/map';
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 — silnik potrzebuje ich do zakotwiczenia znacznika. */
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 — komunikat niesie instrukcję. */
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` — ta sama warstwa,
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 — drugi
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
- const [engineKind, setEngineKind] = useState(null);
57
- const [ready, setReady] = useState(false);
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 — wymusza przeliczenie pozycji nakładek. */
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 — źródło tymczasowej pinezki. */
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 — grupy i punkty pojedyncze. */
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 — licznik ma być czytelny.
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) — to na nią użytkownik właśnie kazał mapie polecieć.
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
- latest.current = { entries, allowCreate, popup, contextTarget, onLocationClick, onClusterClick, onViewChange };
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 — pokaż listę.
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 — raz na życie komponentu.
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
- * „w locie", przed rozwiązaniem obietnicy, zostawiłoby oznaczony kontener i drugi montaż
223
- * kończyłby się błędem „Map container is already initialized".
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
- setReady(true);
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
- setReady(false);
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 — podmiana po starcie nie przeładowuje mapy.
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 — jedyne miejsce, które je rysuje.
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 — bez przeładowania mapy.
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ć — treść popupu chowa wtedy wiersz dystansu zamiast pokazywać zero.
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 || !engineRef.current || !ready)
375
+ if (!popup || !liveEngine)
347
376
  return null;
348
- return engineRef.current.project(popup.location.at);
349
- }, [viewportTick, popup, ready]);
377
+ return liveEngine.project(popup.location.at);
378
+ }, [viewportTick, popup, liveEngine]);
350
379
  const contextPosition = useMemo(() => {
351
380
  void viewportTick;
352
- if (!contextTarget || !engineRef.current || !ready)
381
+ if (!contextTarget || !liveEngine)
353
382
  return null;
354
- return engineRef.current.project(contextTarget.at);
355
- }, [viewportTick, contextTarget, ready]);
356
- /** Kategorie oferowane w menu „dodaj" — ukryte w legendzie odpadają. */
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ć — port bez `search()` nie zasługuje na pole, które nic nie robi. */
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 — „Kwiatowa" ma
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` — tam mapa i tak nie pozwoli tam przesunąć widoku.
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": engineKind ?? undefined, 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("span", { "aria-hidden": "true", children: "+" }) }), _jsx("button", { className: "kpt-map__control", type: "button", "aria-label": t('map.zoomOut'), title: t('map.zoomOut'), disabled: !ready, onClick: () => zoomBy(-1), children: _jsx("span", { "aria-hidden": "true", children: "\u2212" }) }), 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, 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: () => {
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: "\u00D7" }) })) : 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: () => {
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({
@@ -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 — to lista podpowiedzi, nie wyszukiwarka pełnotekstowa. */
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 — adres z zagranicy
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 → miejsca. Metoda jest **opcjonalna** — port bez niej po prostu
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** — nigdy
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ą — tak samo jak efekt Reacta,
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 — do dem, testów i SSR, gdzie prawdziwej geolokalizacji nie ma. */
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 — patrz ostrzeżenie niżej. */
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 — inaczej
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 — do dem, testów i SSR, gdzie prawdziwej geolokalizacji nie ma. */
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 — inaczej
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 — każde kolejne zapytanie czeka na swoją kolej. */
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 — pole zostaje puste.
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 „Adres" — ulica z numerem, bo tak wypełnia się formularz.
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 „prostokąt" pojedynczego budynku nie nadaje się do wpasowania w kadr.
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 — mapa z konfigurowalnymi znacznikami. Osobny subpath, bo silnik mapowy
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) — wspólne z portem Angulara.
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';
@@ -1,13 +1,13 @@
1
1
  # KptMegamenu (@konce-pt/react)
2
2
 
3
- A mega menu — a horizontal bar with a wide, multi-column panel.
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 …caret…</button>
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>[]` — 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"
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 → columns → links
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 — opening in a new tab, the status-bar preview. `onSelect`
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
- /** Nazwa belki dla czytników ekranu, np. „Menu główne”. */
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 — pozioma belka z szerokim, wielokolumnowym panelem.
11
- * Struktura: pozycja górna → `items` (kolumny) → `items` kolumny (odnośniki).
12
- * Pozycja górna bez kolumn działa jak akcja — wywołuje `onSelect` zamiast otwierać pusty panel.
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 — do panelu sąsiedniej pozycji, a `Escape`
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;