@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
@@ -1,3 +1,4 @@
1
+ 'use client';
1
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
3
  import { useEffect } from 'react';
3
4
  import { KptButton } from "../button/button.js";
@@ -26,9 +27,11 @@ export function KptPaginator({ length, pageSize, defaultPageSize = 10, onPageSiz
26
27
  defaultValue: defaultPageIndex,
27
28
  onChange: onPageIndexChange,
28
29
  });
29
- const totalPages = Math.max(1, Math.ceil(length / size));
30
- const rangeStart = length === 0 ? 0 : index * size + 1;
31
- const rangeEnd = Math.min(length, (index + 1) * size);
30
+ // Rozmiar strony do rachunków - `0` albo ujemny dawałby `Infinity` stron i wieczne "dalej".
31
+ const step = Math.max(1, size);
32
+ const totalPages = Math.max(1, Math.ceil(length / step));
33
+ const rangeStart = length === 0 ? 0 : index * step + 1;
34
+ const rangeEnd = Math.min(length, (index + 1) * step);
32
35
  const canPrev = index > 0;
33
36
  const canNext = index < totalPages - 1;
34
37
  // Utrzymuj poprawny indeks, gdy zmieni się długość lub rozmiar strony.
@@ -47,5 +50,5 @@ export function KptPaginator({ length, pageSize, defaultPageSize = 10, onPageSiz
47
50
  const sizeOptions = pageSizeOptions.includes(size)
48
51
  ? pageSizeOptions
49
52
  : [...pageSizeOptions, size].sort((a, b) => a - b);
50
- return (_jsxs("div", { className: cn('kpt-paginator', className), ...rest, children: [_jsx("div", { className: "kpt-paginator__size", children: _jsxs("label", { children: [t('paginator.rowsPerPage'), _jsx("select", { className: "kpt-paginator__select", value: size, onChange: onSizeChange, children: sizeOptions.map((option) => (_jsx("option", { value: option, children: option }, option))) })] }) }), _jsx("span", { className: "kpt-paginator__range", children: t('paginator.range', { start: rangeStart, end: rangeEnd, total: length }) }), _jsxs("div", { className: "kpt-paginator__nav", children: [_jsx(KptButton, { variant: "outline", size: "sm", disabled: !canPrev, onClick: () => setIndex(index - 1), "aria-label": t('paginator.prevPage'), children: prevIcon }), _jsx(KptButton, { variant: "outline", size: "sm", disabled: !canNext, onClick: () => setIndex(index + 1), "aria-label": t('paginator.nextPage'), children: nextIcon })] })] }));
53
+ return (_jsxs("div", { className: cn('kpt-paginator', className), ...rest, children: [_jsx("div", { className: "kpt-paginator__size", children: _jsxs("label", { children: [t('paginator.rowsPerPage'), _jsx("select", { className: "kpt-paginator__select", value: size, onChange: onSizeChange, children: sizeOptions.map((option) => (_jsx("option", { value: option, children: option }, option))) })] }) }), _jsx("span", { className: "kpt-paginator__range", "aria-live": "polite", children: t('paginator.range', { start: rangeStart, end: rangeEnd, total: length }) }), _jsxs("div", { className: "kpt-paginator__nav", children: [_jsx(KptButton, { variant: "outline", size: "sm", disabled: !canPrev, onClick: () => setIndex(index - 1), "aria-label": t('paginator.prevPage'), children: prevIcon }), _jsx(KptButton, { variant: "outline", size: "sm", disabled: !canNext, onClick: () => setIndex(index + 1), "aria-label": t('paginator.nextPage'), children: nextIcon })] })] }));
51
54
  }
@@ -8,13 +8,13 @@ Import: `import { KptPanel } from '@konce-pt/react';`
8
8
  <div class="kpt-panel">
9
9
  <div class="kpt-panel__header">
10
10
  <span class="kpt-panel__title">Settings</span>
11
- <span class="kpt-panel__actions">…actions…</span>
11
+ <span class="kpt-panel__actions">...actions...</span>
12
12
  <button class="kpt-panel__toggle" aria-expanded="true" aria-controls="kpt-panel-body-r1"
13
- aria-label="Collapse / expand Settings">…</button>
13
+ aria-label="Collapse / expand Settings">...</button>
14
14
  </div>
15
15
  <div class="kpt-panel__collapse is-open" id="kpt-panel-body-r1">
16
16
  <div class="kpt-panel__collapse-inner">
17
- <div class="kpt-panel__body">…children…</div>
17
+ <div class="kpt-panel__body">...children...</div>
18
18
  </div>
19
19
  </div>
20
20
  </div>
@@ -22,37 +22,49 @@ Import: `import { KptPanel } from '@konce-pt/react';`
22
22
 
23
23
  ## Props
24
24
  - `header`: string
25
- - `toggleable`: boolean — adds the collapse button to the header
26
- - `collapsed` / `defaultCollapsed` / `onCollapsedChange` — controlled or uncontrolled
25
+ - `toggleable`: boolean - adds the collapse button to the header
26
+ - `collapsed` / `defaultCollapsed` / `onCollapsedChange` - controlled or uncontrolled
27
27
  - `animated`: boolean (default true), `animationDuration`: number (ms, default 500)
28
- - `locale`, `dictionary` — i18n overrides for this component only
28
+ - `collapseMotion`: `'spring' | 'fade' | 'none'` (default from `KptMotionProvider`, else `spring`) - see "Collapse motion"
29
+ - `locale`, `dictionary` - i18n overrides for this component only
29
30
  - `className`, `ref` and every other `<div>` prop go to the host
30
31
 
31
32
  ## Sub-components
32
- - `KptPanel.Actions` — lifted into the header, next to the title, before the collapse button.
33
+ - `KptPanel.Actions` - lifted into the header, next to the title, before the collapse button.
33
34
 
34
35
  ## Collapsed content stays mounted
35
36
  It gets `inert` and `aria-hidden` rather than being removed, so the height is animatable while
36
37
  nothing inside can take focus or be read out. That is also why a collapsed panel keeps the state of
37
- whatever lives in it — a half-filled form inside survives a collapse.
38
+ whatever lives in it - a half-filled form inside survives a collapse.
38
39
 
39
40
  ## i18n
40
41
  The toggle's label comes from `panel.toggleNamed` (with `{header}`) or `panel.toggle` in the dictionary.
41
42
 
42
43
  ## Examples
43
- <KptPanel header="Settings" toggleable>Panel content…</KptPanel>
44
+ <KptPanel header="Settings" toggleable>Panel content...</KptPanel>
44
45
 
45
46
  <KptPanel header="Filters" toggleable collapsed={collapsed} onCollapsedChange={setCollapsed}>
46
47
  <KptPanel.Actions><KptIconButton aria-label="Reset"><KptIcon name="x" /></KptIconButton></KptPanel.Actions>
47
- …
48
+ ...
48
49
  </KptPanel>
49
50
 
51
+ ## Collapse motion
52
+ - `spring` - the height opens on a soft spring, the content slides in from above and sharpens out
53
+ of a blur; when collapsing, the content fades first and only then the section closes. The chevron
54
+ turns on a spring.
55
+ - `fade` - the height on the standard curve, the content cross-fades.
56
+ - `none` - the state changes without motion.
57
+
58
+ With no value it takes `collapse` from `KptMotionProvider` (default `spring`). `animated={false}`, `enabled: false`
59
+ in the config and the system "reduce motion" setting always turn it off. `animationDuration` sets
60
+ the time of every kind. Pure CSS: the kind sits in `data-motion` on the component root.
61
+
50
62
  ## Tokens
51
63
  `--kpt-color-surface`, `--kpt-color-border`, `--kpt-radius-lg`, `--kpt-motion-easing-standard`.
52
64
 
53
65
  ## Accessibility
54
66
  The toggle is a native button with `aria-expanded` and `aria-controls` pointing at the body. Its
55
- name says which panel it collapses — `panel.toggleNamed` (“Collapse / expand Settings”) when there
56
- is a `header`, `panel.toggle` otherwise — so several panels on one page are told apart. A collapsed
57
- body is `inert` and `aria-hidden`. The header title is not a heading — add your own when the panel
67
+ name says which panel it collapses - `panel.toggleNamed` ("Collapse / expand Settings") when there
68
+ is a `header`, `panel.toggle` otherwise - so several panels on one page are told apart. A collapsed
69
+ body is `inert` and `aria-hidden`. The header title is not a heading - add your own when the panel
58
70
  should appear in the document outline.
@@ -1,5 +1,6 @@
1
1
  import { type ComponentPropsWithRef, type ReactNode } from 'react';
2
2
  import { type KptI18nOverrides } from '../i18n/i18n-context.tsx';
3
+ import { type KptCollapseMotion } from '@konce-pt/motion';
3
4
  export interface KptPanelProps extends ComponentPropsWithRef<'div'>, KptI18nOverrides {
4
5
  header?: string;
5
6
  /** Dokłada przycisk zwijania w nagłówku. */
@@ -9,18 +10,25 @@ export interface KptPanelProps extends ComponentPropsWithRef<'div'>, KptI18nOver
9
10
  onCollapsedChange?: (collapsed: boolean) => void;
10
11
  animated?: boolean;
11
12
  animationDuration?: number;
13
+ /**
14
+ * Ruch zwijania: `spring` - wysokość na sprężynie, treść wjeżdża i wyostrza się, przy zwijaniu
15
+ * najpierw gaśnie; `fade` - treść się przenika; `none` - bez ruchu. Bez wartości bierze `collapse`
16
+ * z `KptMotionProvider` (domyślnie `spring`); `animated={false}`, `enabled={false}` i systemowe
17
+ * "ogranicz ruch" wyłączają go zawsze.
18
+ */
19
+ collapseMotion?: KptCollapseMotion;
12
20
  children?: ReactNode;
13
21
  }
14
22
  /**
15
23
  * Panel z nagłówkiem i opcjonalnym zwijaniem. Akcje nagłówka wstaw przez `<KptPanel.Actions>`.
16
24
  *
17
- * Zwinięta treść zostaje w DOM, ale dostaje `inert` i `aria-hidden` — dzięki temu wysokość da się
25
+ * Zwinięta treść zostaje w DOM, ale dostaje `inert` i `aria-hidden` - dzięki temu wysokość da się
18
26
  * animować, a mimo to nic z jej wnętrza nie łapie fokusu ani nie trafia do czytnika ekranu.
19
27
  *
20
28
  * @example
21
- * <KptPanel header="Ustawienia" toggleable>Treść…</KptPanel>
29
+ * <KptPanel header="Ustawienia" toggleable>Treść...</KptPanel>
22
30
  */
23
- export declare function KptPanel({ header, toggleable, collapsed, defaultCollapsed, onCollapsedChange, animated, animationDuration, locale, dictionary, className, style, children, ...rest }: Readonly<KptPanelProps>): import("react").JSX.Element;
31
+ export declare function KptPanel({ header, toggleable, collapsed, defaultCollapsed, onCollapsedChange, animated, animationDuration, collapseMotion, locale, dictionary, className, style, children, ...rest }: Readonly<KptPanelProps>): import("react").JSX.Element;
24
32
  export declare namespace KptPanel {
25
33
  var Actions: typeof KptPanelActions;
26
34
  }
@@ -1,22 +1,27 @@
1
+ 'use client';
1
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Children, isValidElement } from 'react';
3
+ import { isValidElement } from 'react';
3
4
  import { cn } from "../internal/cn.js";
5
+ import { slotChildren } from "../internal/slot-children.js";
4
6
  import { useControllableState } from "../internal/use-controllable-state.js";
5
7
  import { useKptId } from "../internal/use-id.js";
6
8
  import { useKptMessages } from "../i18n/i18n-context.js";
7
9
  import { KptIcon } from "../icon/icon.js";
10
+ import { resolveCollapseMotion } from '@konce-pt/motion';
11
+ import { useKptMotion } from "../motion/motion-context.js";
8
12
  /**
9
13
  * Panel z nagłówkiem i opcjonalnym zwijaniem. Akcje nagłówka wstaw przez `<KptPanel.Actions>`.
10
14
  *
11
- * Zwinięta treść zostaje w DOM, ale dostaje `inert` i `aria-hidden` — dzięki temu wysokość da się
15
+ * Zwinięta treść zostaje w DOM, ale dostaje `inert` i `aria-hidden` - dzięki temu wysokość da się
12
16
  * animować, a mimo to nic z jej wnętrza nie łapie fokusu ani nie trafia do czytnika ekranu.
13
17
  *
14
18
  * @example
15
- * <KptPanel header="Ustawienia" toggleable>Treść…</KptPanel>
19
+ * <KptPanel header="Ustawienia" toggleable>Treść...</KptPanel>
16
20
  */
17
- export function KptPanel({ header = '', toggleable = false, collapsed, defaultCollapsed = false, onCollapsedChange, animated = true, animationDuration = 500, locale, dictionary, className, style, children, ...rest }) {
21
+ export function KptPanel({ header = '', toggleable = false, collapsed, defaultCollapsed = false, onCollapsedChange, animated = true, animationDuration = 500, collapseMotion, locale, dictionary, className, style, children, ...rest }) {
18
22
  const { t } = useKptMessages({ locale, dictionary });
19
23
  const bodyId = useKptId(undefined, 'kpt-panel-body');
24
+ const motion = resolveCollapseMotion(collapseMotion, useKptMotion(), animated);
20
25
  const [isCollapsed, setCollapsed] = useControllableState({
21
26
  value: collapsed,
22
27
  defaultValue: defaultCollapsed,
@@ -24,11 +29,11 @@ export function KptPanel({ header = '', toggleable = false, collapsed, defaultCo
24
29
  });
25
30
  const actions = [];
26
31
  const body = [];
27
- Children.forEach(children, (child) => {
32
+ for (const child of slotChildren(children)) {
28
33
  const isActions = isValidElement(child) && child.type?.kptPanelSlot === 'actions';
29
34
  (isActions ? actions : body).push(child);
30
- });
31
- return (_jsx("div", { className: cn('kpt-panel-host', className), style: { ['--kpt-anim-duration']: `${animated ? animationDuration : 0}ms`, ...style }, ...rest, children: _jsxs("div", { className: "kpt-panel", children: [_jsxs("div", { className: "kpt-panel__header", children: [_jsx("span", { className: "kpt-panel__title", children: header }), _jsx("span", { className: "kpt-panel__actions", children: actions }), toggleable ? (_jsx("button", { type: "button", className: "kpt-panel__toggle", onClick: () => setCollapsed(!isCollapsed), "aria-expanded": !isCollapsed, "aria-controls": bodyId, "aria-label": header ? t('panel.toggleNamed', { header }) : t('panel.toggle'), children: _jsx(KptIcon, { name: isCollapsed ? 'chevron-down' : 'chevron-up' }) })) : null] }), _jsx("div", { className: cn('kpt-panel__collapse', !isCollapsed && 'is-open'), id: bodyId, inert: isCollapsed, "aria-hidden": isCollapsed, children: _jsx("div", { className: "kpt-panel__collapse-inner", children: _jsx("div", { className: "kpt-panel__body", children: body }) }) })] }) }));
35
+ }
36
+ return (_jsx("div", { className: cn('kpt-panel-host', className), style: { ['--kpt-anim-duration']: `${animated ? animationDuration : 0}ms`, ...style }, ...rest, children: _jsxs("div", { className: "kpt-panel", "data-motion": motion, children: [_jsxs("div", { className: "kpt-panel__header", children: [_jsx("span", { className: "kpt-panel__title", children: header }), _jsx("span", { className: "kpt-panel__actions", children: actions }), toggleable ? (_jsx("button", { type: "button", className: "kpt-panel__toggle", onClick: () => setCollapsed(!isCollapsed), "aria-expanded": !isCollapsed, "aria-controls": bodyId, "aria-label": header ? t('panel.toggleNamed', { header }) : t('panel.toggle'), children: _jsx(KptIcon, { name: "chevron-down", className: cn('kpt-panel__chevron', !isCollapsed && 'is-open') }) })) : null] }), _jsx("div", { className: cn('kpt-panel__collapse', !isCollapsed && 'is-open'), id: bodyId, inert: isCollapsed, "aria-hidden": isCollapsed, children: _jsx("div", { className: "kpt-panel__collapse-inner", children: _jsx("div", { className: "kpt-panel__body", children: body }) }) })] }) }));
32
37
  }
33
38
  /** Akcje w nagłówku panelu, obok tytułu. */
34
39
  export function KptPanelActions({ className, ...rest }) {
@@ -7,20 +7,20 @@ Import: `import { KptPassword } from '@konce-pt/react';`
7
7
  <div class="kpt-password-host">
8
8
  <div class="kpt-password">
9
9
  <input class="kpt-password__input" type="password" autocomplete="current-password">
10
- <button class="kpt-password__toggle" type="button" aria-label="Show password">…</button>
10
+ <button class="kpt-password__toggle" type="button" aria-label="Show password">...</button>
11
11
  </div>
12
12
  <div class="kpt-password__meter" data-score="3">
13
- <div class="kpt-password__bars"><span class="kpt-password__bar is-on"></span>…</div>
13
+ <div class="kpt-password__bars"><span class="kpt-password__bar is-on"></span>...</div>
14
14
  <span class="kpt-password__hint">Good</span>
15
15
  </div>
16
16
  </div>
17
17
 
18
18
  ## Props
19
19
  - `value` / `defaultValue` / `onValueChange`
20
- - `feedback`: boolean — shows the strength meter once something is typed
20
+ - `feedback`: boolean - shows the strength meter once something is typed
21
21
  - `autoComplete`: string | null (default 'current-password')
22
22
  - `placeholder`, `id`, `disabled`, `invalid`, `touched`, `onTouch()`
23
- - `locale`, `dictionary` — i18n overrides for this component only
23
+ - `locale`, `dictionary` - i18n overrides for this component only
24
24
 
25
25
  ## autoComplete is not a detail
26
26
  Sign-up and password reset need `autoComplete="new-password"`. With the default, a password manager
@@ -28,9 +28,13 @@ fills in the OLD password instead of offering a new one, and the user ends up sa
28
28
  thing.
29
29
 
30
30
  ## How the score is computed
31
- Four independent criteria, one point each: at least 8 characters, both letter cases, a digit, a
32
- non-alphanumeric character. The label comes from `password.strength` — the only array-valued key in
33
- the dictionary — indexed by the score, so a translation controls the wording without touching code.
31
+ A 0-4 score. The score follows length first, not composition rules (NIST SP 800-63B advises against them): an
32
+ approximate entropy over the character classes actually used, cut for repeats (`aaaa`), sequences
33
+ (`abcd`, `1234`, `qwerty`) and the "Word + digits + symbol" shape. `Password1!` is weak, thirty
34
+ lowercase letters are strong, and `ą`/`ż` are letters, not special characters. There is no
35
+ dictionary - it is a hint, not a security control.
36
+ The label comes from `password.strength` - the only array-valued key in
37
+ the dictionary - indexed by the score, so a translation controls the wording without touching code.
34
38
 
35
39
  ## Examples
36
40
  <KptPassword value={pass} onValueChange={setPass} feedback />
@@ -44,6 +48,14 @@ host no longer carries the `id` (it used to, so `label[for]` pointed at `kpt-pas
44
48
  id existed twice). New: `ariaLabel`, `required`, `readonly`/`readOnly`, `aria-invalid` after touch;
45
49
  with `feedback` the strength label is added to `aria-describedby`.
46
50
 
51
+ ## Motion
52
+ The strength meter fades in when the first character appears; lit segments fill from the start in
53
+ a wave and empty from the end when the score drops, in the colour of the current score; the score label
54
+ enters anew on every change. Turning invalid (after touch) flashes an error ring and shakes the field -
55
+ inside an invalid `kpt-form-field` only the ring, because the wrapper shakes. Follows `enter` from `KptMotionProvider`
56
+ (`spring` / `fade` / `none`); nothing moves on first render; `enabled: false` and the system
57
+ "reduce motion" setting always win.
58
+
47
59
  ## Tokens
48
60
  `--kpt-form-field-*` for the field, `--kpt-color-danger` / `-warning` / `-success` for the meter
49
61
  bars by score.
@@ -5,10 +5,10 @@ export interface KptPasswordProps extends Omit<ComponentPropsWithRef<'div'>, 'on
5
5
  defaultValue?: string;
6
6
  onValueChange?: (value: string) => void;
7
7
  placeholder?: string;
8
- /** Id natywnego pola — pozwala związać etykietę `KptFormField`. */
8
+ /** Id natywnego pola - pozwala związać etykietę `KptFormField`. */
9
9
  id?: string;
10
10
  /**
11
- * Podpowiedź autouzupełniania. Rejestracja i reset hasła wymagają `new-password` — inaczej
11
+ * Podpowiedź autouzupełniania. Rejestracja i reset hasła wymagają `new-password` - inaczej
12
12
  * menedżer haseł podstawia stare hasło zamiast zaproponować nowe.
13
13
  */
14
14
  autoComplete?: string | null;
@@ -1,24 +1,15 @@
1
+ 'use client';
1
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useState } from 'react';
3
+ import { useEffect, useRef, useState } from 'react';
4
+ import { playInvalidMotion, resolveEnterMotion } from '@konce-pt/motion';
5
+ import { useKptMotion } from "../motion/motion-context.js";
3
6
  import { cn } from "../internal/cn.js";
4
7
  import { useControllableState } from "../internal/use-controllable-state.js";
5
8
  import { useKptId } from "../internal/use-id.js";
6
9
  import { useKptMessages } from "../i18n/i18n-context.js";
7
10
  import { joinIds, useKptFieldA11y } from "../form-field/form-field-context.js";
11
+ import { kptPasswordScore } from "../internal/password-strength.js";
8
12
  import { KptIcon } from "../icon/icon.js";
9
- /** Cztery niezależne kryteria; każde spełnione podnosi wynik o jeden. */
10
- function scoreOf(password) {
11
- let score = 0;
12
- if (password.length >= 8)
13
- score++;
14
- if (/[A-Z]/.test(password) && /[a-z]/.test(password))
15
- score++;
16
- if (/\d/.test(password))
17
- score++;
18
- if (/[^A-Za-z0-9]/.test(password))
19
- score++;
20
- return Math.min(score, 4);
21
- }
22
13
  /**
23
14
  * Pole hasła z przełącznikiem widoczności i opcjonalnym miernikiem siły.
24
15
  *
@@ -33,6 +24,26 @@ export function KptPassword({ value, defaultValue = '', onValueChange, placehold
33
24
  const [current, setCurrent] = useControllableState({ value, defaultValue, onChange: onValueChange });
34
25
  const [visible, setVisible] = useState(false);
35
26
  const showInvalid = invalid && (touched ?? true);
36
- const score = scoreOf(current);
37
- return (_jsxs("div", { className: cn('kpt-password-host', className), ...rest, children: [_jsxs("div", { className: cn('kpt-password', disabled && 'kpt-password--disabled', showInvalid && 'kpt-password--invalid'), children: [_jsx("input", { className: "kpt-password__input", id: inputId, type: visible ? 'text' : 'password', value: current, disabled: disabled, readOnly: readOnly, "aria-invalid": showInvalid || undefined, "aria-required": required || undefined, "aria-label": ariaLabel ?? undefined, "aria-describedby": joinIds(field.describedBy, feedback && current && meterId), placeholder: placeholder, autoComplete: autoComplete ?? undefined, onChange: (event) => setCurrent(event.target.value), onBlur: () => onTouch?.() }), _jsx("button", { type: "button", className: "kpt-password__toggle", disabled: disabled, onClick: () => setVisible(!visible), "aria-label": visible ? t('password.hide') : t('password.show'), children: _jsx(KptIcon, { name: visible ? 'eye-off' : 'eye' }) })] }), feedback && current ? (_jsxs("div", { className: "kpt-password__meter", id: meterId, "data-score": score, children: [_jsx("div", { className: "kpt-password__bars", "aria-hidden": "true", children: [0, 1, 2, 3].map((index) => (_jsx("span", { className: cn('kpt-password__bar', index < score && 'is-on') }, index))) }), _jsx("span", { className: "kpt-password__hint", children: messages.password.strength[score] })] })) : null] }));
27
+ // Długość i różnorodność zamiast reguł składu - szczegóły w `internal/password-strength.ts`.
28
+ const score = kptPasswordScore(current);
29
+ /** Ruch miernika i błędu idzie za `enter` z `KptMotionProvider` (domyślnie `spring`). */
30
+ const motion = resolveEnterMotion(undefined, useKptMotion());
31
+ const boxRef = useRef(null);
32
+ // Po pierwszym renderze - dopiero wtedy wejścia z `@starting-style` mają prawo zagrać. Atrybut idzie
33
+ // prosto do DOM-u: host niesie `className` z propsów, a React nadpisałby klasę dopisaną z boku.
34
+ useEffect(() => {
35
+ boxRef.current?.parentElement?.setAttribute('data-live', '');
36
+ }, []);
37
+ const wasInvalid = useRef(null);
38
+ // Sygnał błędu przy przejściu "poprawne -> błędne" (nie przy błędzie obecnym od startu): pierścień
39
+ // w kolorze błędu i krótkie potrząśnięcie. W błędnym `KptFormField` potrząsa już on - tu sam pierścień.
40
+ useEffect(() => {
41
+ const before = wasInvalid.current;
42
+ wasInvalid.current = showInvalid;
43
+ const el = boxRef.current;
44
+ if (!showInvalid || before !== false || motion !== 'spring' || !el)
45
+ return;
46
+ playInvalidMotion(el, { shake: !el.closest('.kpt-form-field--invalid') });
47
+ }, [showInvalid, motion]);
48
+ return (_jsxs("div", { className: cn('kpt-password-host', className), "data-motion": motion, ...rest, children: [_jsxs("div", { ref: boxRef, className: cn('kpt-password', disabled && 'kpt-password--disabled', showInvalid && 'kpt-password--invalid'), children: [_jsx("input", { className: "kpt-password__input", id: inputId, type: visible ? 'text' : 'password', value: current, disabled: disabled, readOnly: readOnly, "aria-invalid": showInvalid || undefined, "aria-required": required || undefined, "aria-label": ariaLabel ?? undefined, "aria-describedby": joinIds(field.describedBy, feedback && current && meterId), placeholder: placeholder, autoComplete: autoComplete ?? undefined, onChange: (event) => setCurrent(event.target.value), onBlur: () => onTouch?.() }), _jsx("button", { type: "button", className: "kpt-password__toggle", disabled: disabled, onClick: () => setVisible(!visible), "aria-label": visible ? t('password.hide') : t('password.show'), children: _jsx(KptIcon, { name: visible ? 'eye-off' : 'eye' }) })] }), feedback && current ? (_jsxs("div", { className: "kpt-password__meter", id: meterId, "data-score": score, children: [_jsx("div", { className: "kpt-password__bars", "aria-hidden": "true", children: [0, 1, 2, 3].map((index) => (_jsx("span", { className: cn('kpt-password__bar', index < score && 'is-on'), style: { ['--_i']: index } }, index))) }), _jsx("span", { className: "kpt-password__hint", children: messages.password.strength[score] }, score)] })) : null] }));
38
49
  }
@@ -1,37 +1,37 @@
1
1
  # KptPeek (@konce-pt/react)
2
2
 
3
- A peek card — content tied to what you are looking at, shown without leaving the text. Hover opens
3
+ A peek card - content tied to what you are looking at, shown without leaving the text. Hover opens
4
4
  it, the card itself is interactive, and a click pins it.
5
5
  Import: `import { KptPeek } from '@konce-pt/react';`
6
6
 
7
7
  ## DOM
8
8
  <span class="kpt-peek-host">
9
- <a class="kpt-peek__trigger" aria-expanded="true" aria-controls="…">…trigger…</a>
9
+ <a class="kpt-peek__trigger" aria-expanded="true" aria-controls="...">...trigger...</a>
10
10
  </span>
11
11
  <!-- while open, in a portal on document.body: -->
12
- <div class="kpt-peek__panel" role="dialog" tabindex="-1" data-pinned>…children…</div>
12
+ <div class="kpt-peek__panel" role="dialog" tabindex="-1" data-pinned>...children...</div>
13
13
 
14
14
  ## Props
15
- - `trigger`: ReactNode — the element the card hangs on; a mention, an avatar, a chip, a link
16
- - `children`: ReactNode — the card content
15
+ - `trigger`: ReactNode - the element the card hangs on; a mention, an avatar, a chip, a link
16
+ - `children`: ReactNode - the card content
17
17
  - `placement`: 'top' | 'bottom' (default 'bottom')
18
18
  - `openDelay`: number (default 300), `closeDelay`: number (default 200)
19
- - `disabled`: boolean — the card never opens; the trigger stays a plain link or button
20
- - `label`: string — the card's accessible name; without it the trigger's text is used
21
- - `onOpenChange`: `(open: boolean) => void` — the moment to fetch the card's data
19
+ - `disabled`: boolean - the card never opens; the trigger stays a plain link or button
20
+ - `label`: string - the card's accessible name; without it the trigger's text is used
21
+ - `onOpenChange`: `(open: boolean) => void` - the moment to fetch the card's data
22
22
  - `className`, `ref` and every other `<span>` prop go to the host
23
23
 
24
24
  ## The props land on the trigger itself
25
25
  A valid element passed as `trigger` is cloned with the handlers and `aria-expanded` merged in, so
26
- the state sits on the element a screen reader actually reads — not on some `<span>` next to it.
26
+ the state sits on the element a screen reader actually reads - not on some `<span>` next to it.
27
27
  Anything else (a bare string) gets wrapped in a `<span>`.
28
28
 
29
29
  ## Hover is not the only way in
30
- Hover opens after `openDelay` and leaving closes after `closeDelay` — the pointer needs time to
30
+ Hover opens after `openDelay` and leaving closes after `closeDelay` - the pointer needs time to
31
31
  travel from the trigger to the card, so entering the card cancels the close. Focus opens it with no
32
32
  delay. A click, Enter or a tap **pins** the card: it stays after the pointer leaves and focus moves
33
33
  inside it. That is the only route to the card's content where there is no hover at all. Escape
34
- closes it and hands focus back to the trigger; so does a pointer press outside.
34
+ closes it and hands focus back to the trigger; so does a pointer press outside. Escape on the trigger itself closes a card opened by hover or focus without moving focus (WCAG 1.4.13); with the card closed the key passes through to an enclosing dialog.
35
35
 
36
36
  ## Which of the three
37
37
  `KptTooltip` carries plain text and cannot be clicked. `KptPopover` opens on click and puts a
@@ -2,14 +2,14 @@ import { type ComponentPropsWithRef, type ReactNode } from 'react';
2
2
  /** Z której strony wyzwalacza wychodzi karta. */
3
3
  export type KptPeekPlacement = 'top' | 'bottom';
4
4
  export interface KptPeekProps extends Omit<ComponentPropsWithRef<'span'>, 'content'> {
5
- /** Element, na którym wisi karta — wzmianka, awatar, chip albo zwykły link. */
5
+ /** Element, na którym wisi karta - wzmianka, awatar, chip albo zwykły link. */
6
6
  trigger: ReactNode;
7
7
  /** Treść karty. */
8
8
  children?: ReactNode;
9
9
  placement?: KptPeekPlacement;
10
- /** Zwłoka otwarcia po najechaniu (ms) — bez niej karty mrugają przy przejeżdżaniu przez tekst. */
10
+ /** Zwłoka otwarcia po najechaniu (ms) - bez niej karty mrugają przy przejeżdżaniu przez tekst. */
11
11
  openDelay?: number;
12
- /** Zwłoka zamknięcia po zjeździe kursora (ms) — tyle jest na przejazd z wyzwalacza na kartę. */
12
+ /** Zwłoka zamknięcia po zjeździe kursora (ms) - tyle jest na przejazd z wyzwalacza na kartę. */
13
13
  closeDelay?: number;
14
14
  /** Wyłączona karta nie otwiera się wcale; wyzwalacz zostaje zwykłym linkiem czy przyciskiem. */
15
15
  disabled?: boolean;
@@ -18,18 +18,18 @@ export interface KptPeekProps extends Omit<ComponentPropsWithRef<'span'>, 'conte
18
18
  onOpenChange?: (open: boolean) => void;
19
19
  }
20
20
  /**
21
- * Karta podglądu pod kursorem — treść powiązana z tym, na co się patrzy, bez opuszczania tekstu.
21
+ * Karta podglądu pod kursorem - treść powiązana z tym, na co się patrzy, bez opuszczania tekstu.
22
22
  * Tym różni się od sąsiadów: `KptTooltip` niesie sam tekst i nie da się w nim kliknąć,
23
23
  * a `KptPopover` otwiera dopiero klik i zasłania stronę backdropem.
24
24
  *
25
- * Hover pokazuje kartę po `openDelay`, zjazd kursora chowa ją po `closeDelay` — kursor musi mieć
25
+ * Hover pokazuje kartę po `openDelay`, zjazd kursora chowa ją po `closeDelay` - kursor musi mieć
26
26
  * czas przejechać z wyzwalacza na kartę, więc najazd na kartę anuluje zamknięcie. Fokus otwiera
27
27
  * kartę od razu, a klik, Enter albo dotyk **przypina** ją: karta zostaje mimo zjazdu kursora,
28
28
  * a fokus wchodzi do środka. To jedyna droga do treści karty tam, gdzie hovera nie ma w ogóle.
29
29
  *
30
30
  * @example
31
31
  * <KptPeek trigger={<a href="/ludzie/anna">@Anna Kowalska</a>}>
32
- * <KptCard>Profil, projekty, kontakt…</KptCard>
32
+ * <KptCard>Profil, projekty, kontakt...</KptCard>
33
33
  * </KptPeek>
34
34
  */
35
35
  export declare function KptPeek({ trigger, children, placement, openDelay, closeDelay, disabled, label, onOpenChange, className, ...rest }: Readonly<KptPeekProps>): import("react").JSX.Element;
package/dist/peek/peek.js CHANGED
@@ -1,21 +1,22 @@
1
+ 'use client';
1
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { cloneElement, isValidElement, useCallback, useEffect, useId, useRef, useState, } from 'react';
3
+ import { cloneElement, isValidElement, useCallback, useEffect, useEffectEvent, useId, useRef, useState, } from 'react';
3
4
  import { cn } from "../internal/cn.js";
4
5
  import { KptOverlay } from "../internal/overlay.js";
5
6
  import { mergeSlotProps } from "../internal/slot.js";
6
7
  /**
7
- * Karta podglądu pod kursorem — treść powiązana z tym, na co się patrzy, bez opuszczania tekstu.
8
+ * Karta podglądu pod kursorem - treść powiązana z tym, na co się patrzy, bez opuszczania tekstu.
8
9
  * Tym różni się od sąsiadów: `KptTooltip` niesie sam tekst i nie da się w nim kliknąć,
9
10
  * a `KptPopover` otwiera dopiero klik i zasłania stronę backdropem.
10
11
  *
11
- * Hover pokazuje kartę po `openDelay`, zjazd kursora chowa ją po `closeDelay` — kursor musi mieć
12
+ * Hover pokazuje kartę po `openDelay`, zjazd kursora chowa ją po `closeDelay` - kursor musi mieć
12
13
  * czas przejechać z wyzwalacza na kartę, więc najazd na kartę anuluje zamknięcie. Fokus otwiera
13
14
  * kartę od razu, a klik, Enter albo dotyk **przypina** ją: karta zostaje mimo zjazdu kursora,
14
15
  * a fokus wchodzi do środka. To jedyna droga do treści karty tam, gdzie hovera nie ma w ogóle.
15
16
  *
16
17
  * @example
17
18
  * <KptPeek trigger={<a href="/ludzie/anna">@Anna Kowalska</a>}>
18
- * <KptCard>Profil, projekty, kontakt…</KptCard>
19
+ * <KptCard>Profil, projekty, kontakt...</KptCard>
19
20
  * </KptPeek>
20
21
  */
21
22
  export function KptPeek({ trigger, children, placement = 'bottom', openDelay = 300, closeDelay = 200, disabled = false, label, onOpenChange, className, ...rest }) {
@@ -24,7 +25,15 @@ export function KptPeek({ trigger, children, placement = 'bottom', openDelay = 3
24
25
  const panelRef = useRef(null);
25
26
  const timer = useRef(undefined);
26
27
  const [open, setOpen] = useState(false);
27
- /** Przypięta karta zostaje po zjeździe kursora — przypina ją klik, Enter albo dotyk. */
28
+ /** Kotwica nakładki w stanie (callback ref), nie `triggerRef.current` czytane w renderze. */
29
+ const [anchor, setAnchor] = useState(null);
30
+ const setTriggerRef = useCallback((el) => {
31
+ triggerRef.current = el;
32
+ setAnchor(el);
33
+ }, []);
34
+ /** Tekst wyzwalacza jako nazwa karty - odczytany przy otwarciu, nie w renderze. */
35
+ const [triggerName, setTriggerName] = useState('');
36
+ /** Przypięta karta zostaje po zjeździe kursora - przypina ją klik, Enter albo dotyk. */
28
37
  const [pinned, setPinned] = useState(false);
29
38
  const panelId = useId();
30
39
  const stop = useCallback(() => {
@@ -40,21 +49,24 @@ export function KptPeek({ trigger, children, placement = 'bottom', openDelay = 3
40
49
  timer.current = setTimeout(action, delay);
41
50
  }, [stop]);
42
51
  const show = useCallback(() => {
43
- setOpen((was) => {
44
- if (!was)
45
- onOpenChange?.(true);
46
- return true;
47
- });
48
- }, [onOpenChange]);
52
+ setTriggerName(triggerRef.current?.textContent?.trim() ?? '');
53
+ setOpen(true);
54
+ }, []);
55
+ // `onOpenChange` po zmianie stanu, a nie wewnątrz funkcji aktualizującej `setOpen` - ta musi być
56
+ // czysta (StrictMode woła ją dwa razy, a React może ją powtórzyć przy przerwanym renderze).
57
+ const notifyOpen = useEffectEvent((value) => onOpenChange?.(value));
58
+ const reportedOpen = useRef(false);
59
+ useEffect(() => {
60
+ if (reportedOpen.current === open)
61
+ return;
62
+ reportedOpen.current = open;
63
+ notifyOpen(open);
64
+ }, [open]);
49
65
  const close = useCallback(() => {
50
66
  stop();
51
67
  setPinned(false);
52
- setOpen((was) => {
53
- if (was)
54
- onOpenChange?.(false);
55
- return false;
56
- });
57
- }, [onOpenChange, stop]);
68
+ setOpen(false);
69
+ }, [stop]);
58
70
  const hoverIn = useCallback(() => {
59
71
  if (disabled)
60
72
  return;
@@ -106,19 +118,29 @@ export function KptPeek({ trigger, children, placement = 'bottom', openDelay = 3
106
118
  }, [close, open]);
107
119
  const triggerProps = {
108
120
  className: 'kpt-peek__trigger',
109
- ref: triggerRef,
121
+ ref: setTriggerRef,
110
122
  onMouseEnter: hoverIn,
111
123
  onMouseLeave: hoverOut,
112
124
  onFocus: focusIn,
113
125
  onBlur: focusOut,
114
126
  onClick: pin,
127
+ // Escape na wyzwalaczu odprawia kartę bez przenoszenia fokusu (WCAG 1.4.13). Zamknięta karta
128
+ // przepuszcza klawisz dalej - Escape nadal zamyka otaczające okno.
129
+ onKeyDown: (event) => {
130
+ if (event.key !== 'Escape' || !open)
131
+ return;
132
+ event.preventDefault();
133
+ event.stopPropagation();
134
+ close();
135
+ },
115
136
  'aria-expanded': open,
116
137
  'aria-controls': open ? panelId : undefined,
117
138
  };
118
139
  return (_jsxs("span", { ref: hostRef, className: cn('kpt-peek-host', className), ...rest, children: [isValidElement(trigger) ? (
119
140
  // Propsy idą na sam wyzwalacz, nie na opakowanie: `aria-expanded` ma siedzieć na tym
120
141
  // elemencie, po którym jeździ czytnik ekranu, a nie na obojętnym `<span>` obok.
121
- cloneElement(trigger, mergeSlotProps(triggerProps, trigger.props))) : (_jsx("span", { ...triggerProps, children: trigger })), _jsx(KptOverlay, { open: open, anchor: triggerRef.current, onClose: close, offset: 8, withBackdrop: false, preferAbove: placement === 'top', children: _jsx("div", { ref: panelRef, id: panelId, className: "kpt-peek__panel", role: "dialog", tabIndex: -1, "aria-label": label || triggerRef.current?.textContent?.trim() || undefined, "data-pinned": pinned ? '' : undefined, onMouseEnter: hoverIn, onMouseLeave: hoverOut, onFocus: focusIn, onBlur: focusOut, onKeyDown: (event) => {
142
+ // eslint-disable-next-line react-hooks/refs -- mergeSlotProps tylko składa handlery; refy czytają one dopiero w zdarzeniach
143
+ cloneElement(trigger, mergeSlotProps(triggerProps, trigger.props))) : (_jsx("span", { ...triggerProps, children: trigger })), _jsx(KptOverlay, { open: open, anchor: anchor, onClose: close, offset: 8, withBackdrop: false, preferAbove: placement === 'top', children: _jsx("div", { ref: panelRef, id: panelId, className: "kpt-peek__panel", role: "dialog", tabIndex: -1, "aria-label": label || triggerName || undefined, "data-pinned": pinned ? '' : undefined, onMouseEnter: hoverIn, onMouseLeave: hoverOut, onFocus: focusIn, onBlur: focusOut, onKeyDown: (event) => {
122
144
  if (event.key !== 'Escape')
123
145
  return;
124
146
  close();
@@ -10,20 +10,21 @@ Import: `import { KptPickList } from '@konce-pt/react';`
10
10
  <div class="kpt-pick-list__col">
11
11
  <div class="kpt-pick-list__header">Available</div>
12
12
  <ul class="kpt-pick-list__items">
13
- <li class="kpt-pick-list__item is-selected">…</li>…
13
+ <li class="kpt-pick-list__item is-selected">...</li>...
14
14
  </ul>
15
15
  </div>
16
- <div class="kpt-pick-list__controls">…four buttons…</div>
17
- <div class="kpt-pick-list__col">…</div>
16
+ <div class="kpt-pick-list__controls">...four buttons...</div>
17
+ <div class="kpt-pick-list__col">...</div>
18
18
  </div>
19
19
  </div>
20
20
 
21
21
  ## Props
22
- - `source` / `defaultSource` / `onSourceChange` — the left list
23
- - `target` / `defaultTarget` / `onTargetChange` — the right list
24
- - `sourceHeader`, `targetHeader` — without them `pickList.available` / `pickList.selected`
25
- - `dragdrop` — `false` by default; enables dragging (opt-in, nothing changes without it)
26
- - `renderItem(item, index)` — required; the equivalent of `<ng-template let-item>`
22
+ - `source` / `defaultSource` / `onSourceChange` - the left list
23
+ - `target` / `defaultTarget` / `onTargetChange` - the right list
24
+ - `sourceHeader`, `targetHeader` - without them `pickList.available` / `pickList.selected`
25
+ - `dragdrop` - `false` by default; enables dragging (opt-in, nothing changes without it)
26
+ - `layoutMotion` - `'spring' | 'arc' | 'morph' | 'none'` (default from `KptMotionProvider`, else `spring`) - see below
27
+ - `renderItem(item, index)` - required; the equivalent of `<ng-template let-item>`
27
28
  - `className`, `ref` and every other `<div>` prop go to the host
28
29
 
29
30
  ## The selection is kept by reference
@@ -33,7 +34,7 @@ they empty; a double-click or a drag only drops the one item that left.
33
34
 
34
35
  ## A double-click moves one item
35
36
  It sends exactly the row you clicked to the other panel and appends it at the end, whatever else is
36
- selected. It does not need `dragdrop` — the two clicks underneath cancel each other out, so the rest
37
+ selected. It does not need `dragdrop` - the two clicks underneath cancel each other out, so the rest
37
38
  of the selection is left alone.
38
39
 
39
40
  ## Where a dragged item lands
@@ -57,19 +58,41 @@ half-controlled, or fully controlled by the form.
57
58
  The headers and the four move buttons come from the `pickList` namespace. Dragging and the
58
59
  double-click carry no visible label, so they add no dictionary keys.
59
60
 
61
+ ## Move motion
62
+ `layoutMotion` animates moves and reorders made by the user (buttons, double-click, `Enter`,
63
+ dragging):
64
+ - `spring` - the row glides to its new place on a spring;
65
+ - `arc` - the row lifts (shadow) and flies along an arc, tilted toward the move;
66
+ - `morph` - the browser's own morph (View Transitions); without browser support the move is
67
+ instant, and the transition blocks clicks on the page for ~0.5 s;
68
+ - `none` - no motion.
69
+
70
+ A row crossing to the other list flies as a ghost - a copy above both panels, because the panel's
71
+ `overflow` would clip the real row; the real row waits invisible where it lands. Neighbours close
72
+ the gap or make room with a calm spring. The receiving list scrolls to the last moved row, so the
73
+ landing is visible; rows scrolled out of view do not fly. A click in the middle of a flight starts
74
+ the next one from where the rows are on screen.
75
+
76
+ With no value it takes `layout` from `KptMotionProvider` (default `spring`); `enabled: false` there and
77
+ the system "reduce motion" setting always win. Lists replaced from outside (`source`, `target`) switch
78
+ instantly - the old state has to be measured before the DOM changes.
79
+
60
80
  ## Tokens
61
81
  The selection `--kpt-color-primary`, the headers `--kpt-color-surface-variant`.
62
82
 
63
83
  ## Keyboard
64
84
  Each list is a `listbox` with `aria-multiselectable` and a single Tab stop (roving tabindex):
65
- - `↓` / `↑` move between items (wrapping), `Home` / `End` jump to the ends;
85
+ - `ArrowDown` / `ArrowUp` move between items (wrapping), `Home` / `End` jump to the ends;
66
86
  - `Space` toggles the focused item's selection (`aria-selected`), the same as a click;
67
- - `Enter` moves the focused item to the other list, the same as a double-click — focus stays at
87
+ - `Enter` moves the focused item to the other list, the same as a double-click - focus stays at
68
88
  that position in the list, on the next item.
69
89
  The move buttons act on the same selection, so the whole control works without a mouse.
70
90
 
71
91
  ## Accessibility
72
92
  Each list is named after its header (`aria-label`). The four move buttons are labelled from the
73
93
  dictionary, so "move all right" is distinguishable from "move right" without seeing the double
74
- chevron. Native HTML5 drag and drop has no keyboard equivalent — `Enter` and the buttons cover
94
+ chevron. Native HTML5 drag and drop has no keyboard equivalent - `Enter` and the buttons cover
75
95
  everything it does, and `dragdrop` adds to them, it never replaces them.
96
+
97
+ ## Announcements and buttons
98
+ A transfer between the lists is announced in a polite live region: `pickList.moved` with `{count}` and `{list}` (the target header). The move buttons carry `aria-disabled` when there is nothing to move (no selection, empty list) - they stay focusable, so focus does not drop to `<body>` when the selection clears after a move.