@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,6 +1,7 @@
1
1
  import { type ComponentPropsWithRef } from 'react';
2
2
  import { type KptChartConfig } from '@konce-pt/chart';
3
- export interface KptChartProps extends Omit<ComponentPropsWithRef<'div'>, 'children'> {
3
+ import { type KptI18nOverrides } from '../i18n/i18n-context.tsx';
4
+ export interface KptChartProps extends Omit<ComponentPropsWithRef<'div'>, 'children'>, KptI18nOverrides {
4
5
  config: KptChartConfig;
5
6
  /** Szerokość viewBoxa w jednostkach SVG; wykres skaluje się do kontenera. */
6
7
  width?: number;
@@ -9,10 +10,10 @@ export interface KptChartProps extends Omit<ComponentPropsWithRef<'div'>, 'child
9
10
  }
10
11
  /**
11
12
  * Wykres SVG sterowany jednym obiektem `config`: line, bar, pie, doughnut, polarArea, radar.
12
- * Cała geometria pochodzi z `@konce-pt/chart` — paczki bez frameworka, wspólnej z portem
13
+ * Cała geometria pochodzi z `@konce-pt/chart` - paczki bez frameworka, wspólnej z portem
13
14
  * Angulara; komponent renderuje wyłącznie SVG, bez manipulacji DOM.
14
15
  *
15
16
  * @example
16
17
  * <KptChart config={{ type: 'line', labels: ['Sty', 'Lut'], series: [{ name: 'Sprzedaż', data: [12, 19] }] }} />
17
18
  */
18
- export declare function KptChart({ config, width, height, className, style, ...rest }: Readonly<KptChartProps>): import("react").JSX.Element;
19
+ export declare function KptChart({ config, width, height, locale, dictionary, className, style, ...rest }: Readonly<KptChartProps>): import("react").JSX.Element;
@@ -1,7 +1,9 @@
1
+ 'use client';
1
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { useState } from 'react';
3
- import { areaPath, bandScale, barLayout, extent, formatNumber, linePath, linearScale, niceScale, pieSlices, plotArea, polarSlices, polarToCartesian, polygonPath, radarGrid, radarPoints, radialArea, seriesColor, } from '@konce-pt/chart';
3
+ import { useId, useState } from 'react';
4
+ import { areaPath, bandScale, barLayout, chartAccessibleName, chartDataTable, extent, formatNumber, linePath, linearScale, niceScale, pieSlices, plotArea, polarSlices, polarToCartesian, polygonPath, radarGrid, radarPoints, radialArea, seriesColor, } from '@konce-pt/chart';
4
5
  import { cn } from "../internal/cn.js";
6
+ import { useKptMessages } from "../i18n/i18n-context.js";
5
7
  import { KptChartLegend } from "./chart-legend.js";
6
8
  import { KptChartTooltip } from "./chart-tooltip.js";
7
9
  const VERTICAL_INSETS = { top: 12, right: 16, bottom: 28, left: 46 };
@@ -15,17 +17,23 @@ function styleFor(align) {
15
17
  }
16
18
  /**
17
19
  * Wykres SVG sterowany jednym obiektem `config`: line, bar, pie, doughnut, polarArea, radar.
18
- * Cała geometria pochodzi z `@konce-pt/chart` — paczki bez frameworka, wspólnej z portem
20
+ * Cała geometria pochodzi z `@konce-pt/chart` - paczki bez frameworka, wspólnej z portem
19
21
  * Angulara; komponent renderuje wyłącznie SVG, bez manipulacji DOM.
20
22
  *
21
23
  * @example
22
24
  * <KptChart config={{ type: 'line', labels: ['Sty', 'Lut'], series: [{ name: 'Sprzedaż', data: [12, 19] }] }} />
23
25
  */
24
- export function KptChart({ config, width = 600, height = 360, className, style, ...rest }) {
26
+ export function KptChart({ config, width = 600, height = 360, locale, dictionary, className, style, ...rest }) {
27
+ const { t, locale: activeLocale } = useKptMessages({ locale, dictionary });
28
+ const descId = `${useId()}-desc`;
29
+ // Alternatywa tekstowa (WCAG 1.1.1): nazwa SVG (`ariaLabel` -> tytuł -> i18n) i dane w ukrytej
30
+ // wizualnie tabeli - dymek na najechanie jest dostępny tylko myszą.
31
+ const accessibleName = chartAccessibleName(config, t('chart.label'));
32
+ const dataTable = chartDataTable(config, activeLocale);
25
33
  const [hidden, setHidden] = useState(new Set());
26
34
  const [highlighted, setHighlighted] = useState(null);
27
35
  const [tooltip, setTooltip] = useState(null);
28
- /** Indeks kategorii pod kursorem — prowadnica crosshair w wykresie liniowym. */
36
+ /** Indeks kategorii pod kursorem - prowadnica crosshair w wykresie liniowym. */
29
37
  const [activeIndex, setActiveIndex] = useState(null);
30
38
  const anim = {
31
39
  enabled: config.animation?.enabled ?? true,
@@ -71,7 +79,7 @@ export function KptChart({ config, width = 600, height = 360, className, style,
71
79
  next.add(key);
72
80
  setHidden(next);
73
81
  };
74
- // ── Wykres liniowy ──
82
+ // -- Wykres liniowy --
75
83
  const lineVm = (() => {
76
84
  if (config.type !== 'line')
77
85
  return null;
@@ -124,7 +132,7 @@ export function KptChart({ config, width = 600, height = 360, className, style,
124
132
  } })] }, idx)))] }, key));
125
133
  })] }));
126
134
  })();
127
- // ── Wykres słupkowy ──
135
+ // -- Wykres słupkowy --
128
136
  const barVm = (() => {
129
137
  if (config.type !== 'bar')
130
138
  return null;
@@ -159,7 +167,7 @@ export function KptChart({ config, width = 600, height = 360, className, style,
159
167
  return (_jsx("path", { className: cn('kpt-chart__bar', dim(key) && 'kpt-chart__series--dim'), d: b.path, style: { fill: color, ['--kpt-chart-i']: order }, onPointerEnter: () => showTip(tipX, tipY, names[b.seriesIndex], config.labels[b.categoryIndex] ?? '', b.value, color), onPointerLeave: hideTip }, `${key}#${order}`));
160
168
  }) })] }));
161
169
  })();
162
- // ── Pie / doughnut / polar area ──
170
+ // -- Pie / doughnut / polar area --
163
171
  const pieVm = (() => {
164
172
  if (!isPieLike)
165
173
  return null;
@@ -184,7 +192,7 @@ export function KptChart({ config, width = 600, height = 360, className, style,
184
192
  return (_jsx("path", { className: cn('kpt-chart__slice', dim(key) && 'kpt-chart__series--dim'), d: slice.path, style: { fill: entry.color, ['--kpt-chart-i']: k }, onPointerEnter: () => showTip(tip.x, tip.y, entry.label, '', entry.value, entry.color), onPointerLeave: hideTip }, key));
185
193
  }) }));
186
194
  })();
187
- // ── Radar ──
195
+ // -- Radar --
188
196
  const radarVm = (() => {
189
197
  if (config.type !== 'radar')
190
198
  return null;
@@ -227,5 +235,5 @@ export function KptChart({ config, width = 600, height = 360, className, style,
227
235
  fontWeight: config.subtitle.font?.weight,
228
236
  paddingTop: config.subtitle.padding?.top,
229
237
  paddingBottom: config.subtitle.padding?.bottom,
230
- }, children: config.subtitle.text })) : null, _jsxs("div", { className: "kpt-chart__body", children: [_jsxs("div", { className: "kpt-chart__canvas", children: [_jsxs("svg", { className: "kpt-chart__svg", viewBox: `0 0 ${width} ${height}`, role: "img", preserveAspectRatio: "xMidYMid meet", children: [lineVm, barVm, radarVm, pieVm] }), _jsx(KptChartTooltip, { state: tooltip })] }), legendOpts.display ? (_jsx(KptChartLegend, { items: legendItems, position: legendOpts.position, align: legendOpts.align, pointStyle: legendOpts.pointStyle, title: legendOpts.title, onToggle: toggle, onHighlight: setHighlighted })) : null] })] }) }));
238
+ }, children: config.subtitle.text })) : null, _jsxs("div", { className: "kpt-chart__body", children: [_jsxs("div", { className: "kpt-chart__canvas", children: [_jsxs("svg", { className: "kpt-chart__svg", viewBox: `0 0 ${width} ${height}`, role: "img", "aria-label": accessibleName, "aria-describedby": config.description ? descId : undefined, preserveAspectRatio: "xMidYMid meet", children: [lineVm, barVm, radarVm, pieVm] }), config.description ? (_jsx("p", { className: "kpt-chart__sr", id: descId, children: config.description })) : null, _jsxs("table", { className: "kpt-chart__sr", children: [_jsxs("caption", { children: [t('chart.dataTable'), ": ", accessibleName] }), _jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { scope: "col", children: t('chart.category') }), dataTable.columns.map((col, i) => (_jsx("th", { scope: "col", children: col }, i)))] }) }), _jsx("tbody", { children: dataTable.rows.map((row, i) => (_jsxs("tr", { children: [_jsx("th", { scope: "row", children: row.label }), row.values.map((value, j) => (_jsx("td", { children: value }, j)))] }, i))) })] }), _jsx(KptChartTooltip, { state: tooltip })] }), legendOpts.display ? (_jsx(KptChartLegend, { items: legendItems, position: legendOpts.position, align: legendOpts.align, pointStyle: legendOpts.pointStyle, title: legendOpts.title, onToggle: toggle, onHighlight: setHighlighted })) : null] })] }) }));
231
239
  }
@@ -8,22 +8,22 @@ and not every app needs them.
8
8
 
9
9
  ## Props
10
10
  - `config`: `KptChartConfig` (required)
11
- - `width`, `height` — the viewBox in SVG units (600 × 360); the chart scales to its container
11
+ - `width`, `height` - the viewBox in SVG units (600 x 360); the chart scales to its container
12
12
 
13
13
  The whole drawing lives in that coordinate system, TYPOGRAPHY INCLUDED: the axis labels are set in
14
14
  `font-size: 11px`, but inside an SVG that means 11 user units, not 11 device pixels. Render a
15
15
  600-unit chart in a 300px column and the labels come out at roughly half size. `width`/`height` are
16
- therefore not just an aspect ratio — they are the text scale. In a narrow container shrink them
16
+ therefore not just an aspect ratio - they are the text scale. In a narrow container shrink them
17
17
  proportionally (`width={300} height={180}`) instead of letting a 600-unit chart squeeze itself.
18
18
  - `className`, `ref` and every other `<div>` prop go to the host
19
19
 
20
20
  ## The config is the API
21
21
  `{ type, labels, series, points?, area?, interpolation?, orientation?, borderRadius?, innerRadius?,
22
- crosshair?, tooltip?, legend?, title?, subtitle?, animation? }` — the same shape the Angular port
22
+ crosshair?, tooltip?, legend?, title?, subtitle?, animation?, ariaLabel?, description? }` - the same shape the Angular port
23
23
  takes, because both read it from `@konce-pt/chart`.
24
24
 
25
25
  ## The geometry lives in a package
26
- Scales, paths, bar layout and arcs come from `@konce-pt/chart` — pure functions with `node --test`
26
+ Scales, paths, bar layout and arcs come from `@konce-pt/chart` - pure functions with `node --test`
27
27
  coverage, shared with the Angular port. The component only turns them into SVG elements; there is
28
28
  no DOM manipulation and no canvas.
29
29
 
@@ -32,7 +32,7 @@ A 4px point is not something a pointer can reliably hit, so each one is paired w
32
32
  12px circle that carries the events. The visible point only reacts to the state.
33
33
 
34
34
  ## Interaction is local state
35
- Hidden series, the highlighted one, the tooltip and the active category are `useState` — the config
35
+ Hidden series, the highlighted one, the tooltip and the active category are `useState` - the config
36
36
  stays a plain object the caller owns, and nothing is written back into it.
37
37
 
38
38
  ## Examples
@@ -53,5 +53,8 @@ stays a plain object the caller owns, and nothing is written back into it.
53
53
  `KPT_CHART_PALETTE` or `series.color`.
54
54
 
55
55
  ## Accessibility
56
- The `<svg>` carries `role="img"`; the legend is a list of real buttons, so series can be toggled
57
- from the keyboard.
56
+ The `<svg>` is `role="img"` with a name: `config.ariaLabel`, else the title text, else `chart.label`
57
+ from i18n. `config.description` (one sentence with the takeaway) is tied to it through
58
+ `aria-describedby`. The data itself sits in a visually hidden `<table>` (categories in rows, series in
59
+ columns, numbers in the active language), so a screen reader can walk the values - the tooltip is
60
+ pointer-only. The legend is a list of real buttons, so series can be toggled from the keyboard.
@@ -1,7 +1,7 @@
1
1
  /*
2
- * @konce-pt/react/charts — wykresy SVG. Osobny subpath, bo geometria i style wykresów są
2
+ * @konce-pt/react/charts - wykresy SVG. Osobny subpath, bo geometria i style wykresów są
3
3
  * ciężkie względem rdzenia biblioteki i nie każda aplikacja ich potrzebuje.
4
4
  */
5
5
  export { KptChart } from "../charts/chart.js";
6
- // Rdzeń obliczeniowy — przydatny do własnych rozszerzeń i renderowania po stronie serwera.
6
+ // Rdzeń obliczeniowy - przydatny do własnych rozszerzeń i renderowania po stronie serwera.
7
7
  export { KPT_CHART_PALETTE, seriesColor, linePath, areaPath, pieSlices, polarSlices, barLayout, radarGrid, radarPoints, niceScale, } from '@konce-pt/chart';
@@ -1,19 +1,26 @@
1
1
  import { type ComponentPropsWithRef, type ReactNode } from 'react';
2
+ import { type KptEnterMotion } from '@konce-pt/motion';
2
3
  import type { KptChatAuthor, KptChatMessage as KptChatMessageModel, KptChatMessageRow, KptChatReactionOption, KptChatReplyRef } from '@konce-pt/chat';
3
4
  import { type KptI18nOverrides } from '../i18n/i18n-context.tsx';
4
- /** Wybór reakcji na wiadomości — komponent mówi, w co kliknięto, dopisanie należy do aplikacji. */
5
+ /** Wybór reakcji na wiadomości - komponent mówi, w co kliknięto, dopisanie należy do aplikacji. */
5
6
  export interface KptChatReactEvent<T = unknown> {
6
7
  message: KptChatMessageModel<T>;
7
8
  emoji: string;
8
9
  }
9
10
  export interface KptChatProps<T = unknown> extends Omit<ComponentPropsWithRef<'div'>, 'children'>, KptI18nOverrides {
10
- /** Wiadomości w kolejności chronologicznej — komponent ich nie sortuje. */
11
+ /** Wiadomości w kolejności chronologicznej - komponent ich nie sortuje. */
11
12
  messages?: readonly KptChatMessageModel<T>[];
12
- /** `id` nadawcy uznawanego za „mnie": jego wiadomości idą na prawo i mają status. */
13
+ /**
14
+ * Ruch pojawiania się (nowe wiadomości, reakcje, status): `spring` - wyrasta z miejsca, z którego przychodzi, na sprężynie;
15
+ * `fade` - tylko się przenika; `none` - bez ruchu. Bez wartości bierze `enter` z `KptMotionProvider`
16
+ * (domyślnie `spring`); `enabled={false}` i systemowe "ogranicz ruch" wyłączają go zawsze.
17
+ */
18
+ enterMotion?: KptEnterMotion;
19
+ /** `id` nadawcy uznawanego za "mnie": jego wiadomości idą na prawo i mają status. */
13
20
  currentUser?: string;
14
21
  /** Przerwa łamiąca grupę wiadomości jednego nadawcy. */
15
22
  groupGapMs?: number;
16
- /** `id` pierwszej nieprzeczytanej wiadomości — nad nią stanie dzielnik. */
23
+ /** `id` pierwszej nieprzeczytanej wiadomości - nad nią stanie dzielnik. */
17
24
  unreadFrom?: string | null;
18
25
  /** Brak połączenia: pasek u góry listy z liczbą wiadomości czekających na wysłanie. */
19
26
  offline?: boolean;
@@ -31,23 +38,23 @@ export interface KptChatProps<T = unknown> extends Omit<ComponentPropsWithRef<'d
31
38
  onReply?: (message: KptChatMessageModel<T>) => void;
32
39
  onReact?: (event: KptChatReactEvent<T>) => void;
33
40
  onQuoted?: (quote: KptChatReplyRef) => void;
34
- /** Przewinięcie na sam szczyt — moment na dociągnięcie starszych wiadomości. */
41
+ /** Przewinięcie na sam szczyt - moment na dociągnięcie starszych wiadomości. */
35
42
  onLoadMore?: () => void;
36
- /** Szablon treści bąbla — odpowiednik `<ng-template let-message>` z Angulara. */
43
+ /** Szablon treści bąbla - odpowiednik `<ng-template let-message>` z Angulara. */
37
44
  renderMessage?: (message: KptChatMessageModel<T>, row: KptChatMessageRow<T>) => ReactNode;
38
45
  }
39
46
  /**
40
- * Lista rozmowy — grupuje wiadomości, wstawia separatory dni i dzielnik nieprzeczytanych,
47
+ * Lista rozmowy - grupuje wiadomości, wstawia separatory dni i dzielnik nieprzeczytanych,
41
48
  * trzyma widok przy dole i pokazuje pasek offline z liczbą wiadomości czekających na wysłanie.
42
49
  *
43
50
  * Sama logika wierszy mieszka w `@konce-pt/chat` (`buildChatRows`), więc port Angulara liczy ją
44
51
  * tak samo. Komponent nie ma nic wspólnego z transportem: wiadomości dostaje, a `onRetry`
45
- * i `onLoadMore` tylko zgłasza — ponowieniem wysyłki i dociąganiem historii zajmuje się aplikacja.
52
+ * i `onLoadMore` tylko zgłasza - ponowieniem wysyłki i dociąganiem historii zajmuje się aplikacja.
46
53
  *
47
- * Kompozytor (`KptChatComposer`) jest osobnym komponentem i stoi obok listy — dzięki temu
54
+ * Kompozytor (`KptChatComposer`) jest osobnym komponentem i stoi obok listy - dzięki temu
48
55
  * czat bez pola wpisywania (podgląd, archiwum) nie wozi go ze sobą.
49
56
  *
50
57
  * @example
51
58
  * <KptChat messages={messages} currentUser="me" typing={typing} onRetry={resend} />
52
59
  */
53
- export declare function KptChat<T = unknown>({ messages, currentUser, groupGapMs, unreadFrom, offline, typing, autoScroll, actions, reactionOptions, emptyLabel, onRetry, onReply, onReact, onQuoted, onLoadMore, renderMessage, locale, dictionary, className, ...rest }: Readonly<KptChatProps<T>>): import("react").JSX.Element;
60
+ export declare function KptChat<T = unknown>({ messages, currentUser, groupGapMs, unreadFrom, offline, typing, autoScroll, actions, reactionOptions, emptyLabel, enterMotion, onRetry, onReply, onReact, onQuoted, onLoadMore, renderMessage, locale, dictionary, className, ...rest }: Readonly<KptChatProps<T>>): import("react").JSX.Element;
package/dist/chat/chat.js CHANGED
@@ -1,34 +1,37 @@
1
+ 'use client';
1
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react';
3
+ import { useCallback, useEffect, useEffectEvent, useLayoutEffect, useRef, useState } from 'react';
3
4
  import { KPT_CHAT_GROUP_GAP_MS, KPT_CHAT_REACTIONS, buildChatRows, chatDayIndicator, pendingCount } from '@konce-pt/chat';
5
+ import { playEnterMotion, resolveEnterMotion } from '@konce-pt/motion';
4
6
  import { cn } from "../internal/cn.js";
5
7
  import { useKptMessages } from "../i18n/i18n-context.js";
6
8
  import { KptIcon } from "../icon/icon.js";
7
9
  import { KptChatMessage } from "../chat-message/chat-message.js";
8
10
  import { KptChatTyping } from "../chat-typing/chat-typing.js";
11
+ import { useKptMotion } from "../motion/motion-context.js";
9
12
  /** Odległość od dołu (px), przy której listę uznajemy jeszcze za przypiętą. */
10
13
  const PIN_THRESHOLD = 48;
11
14
  /**
12
15
  * Wiersze, które nie mogą znaleźć się pod pływającą pigułką dnia: separatory i komunikaty
13
- * systemowe, czyli wszystko wyśrodkowane, co czyta się jak jej bliźniak. Bąble są poza listą —
16
+ * systemowe, czyli wszystko wyśrodkowane, co czyta się jak jej bliźniak. Bąble są poza listą -
14
17
  * pigułka nad wypowiedzią to normalny obrazek czatu.
15
18
  */
16
19
  const BLOCKING_ROWS = '.kpt-chat__date, .kpt-chat__unread, .kpt-chat-message__system';
17
20
  /**
18
- * Lista rozmowy — grupuje wiadomości, wstawia separatory dni i dzielnik nieprzeczytanych,
21
+ * Lista rozmowy - grupuje wiadomości, wstawia separatory dni i dzielnik nieprzeczytanych,
19
22
  * trzyma widok przy dole i pokazuje pasek offline z liczbą wiadomości czekających na wysłanie.
20
23
  *
21
24
  * Sama logika wierszy mieszka w `@konce-pt/chat` (`buildChatRows`), więc port Angulara liczy ją
22
25
  * tak samo. Komponent nie ma nic wspólnego z transportem: wiadomości dostaje, a `onRetry`
23
- * i `onLoadMore` tylko zgłasza — ponowieniem wysyłki i dociąganiem historii zajmuje się aplikacja.
26
+ * i `onLoadMore` tylko zgłasza - ponowieniem wysyłki i dociąganiem historii zajmuje się aplikacja.
24
27
  *
25
- * Kompozytor (`KptChatComposer`) jest osobnym komponentem i stoi obok listy — dzięki temu
28
+ * Kompozytor (`KptChatComposer`) jest osobnym komponentem i stoi obok listy - dzięki temu
26
29
  * czat bez pola wpisywania (podgląd, archiwum) nie wozi go ze sobą.
27
30
  *
28
31
  * @example
29
32
  * <KptChat messages={messages} currentUser="me" typing={typing} onRetry={resend} />
30
33
  */
31
- export function KptChat({ messages = [], currentUser = '', groupGapMs = KPT_CHAT_GROUP_GAP_MS, unreadFrom = null, offline = false, typing = [], autoScroll = true, actions = false, reactionOptions = KPT_CHAT_REACTIONS, emptyLabel = '', onRetry, onReply, onReact, onQuoted, onLoadMore, renderMessage, locale, dictionary, className, ...rest }) {
34
+ export function KptChat({ messages = [], currentUser = '', groupGapMs = KPT_CHAT_GROUP_GAP_MS, unreadFrom = null, offline = false, typing = [], autoScroll = true, actions = false, reactionOptions = KPT_CHAT_REACTIONS, emptyLabel = '', enterMotion, onRetry, onReply, onReact, onQuoted, onLoadMore, renderMessage, locale, dictionary, className, ...rest }) {
32
35
  const { t, locale: activeLocale } = useKptMessages({ locale, dictionary });
33
36
  const viewportRef = useRef(null);
34
37
  const dayRef = useRef(null);
@@ -36,16 +39,17 @@ export function KptChat({ messages = [], currentUser = '', groupGapMs = KPT_CHAT
36
39
  const [pinned, setPinned] = useState(true);
37
40
  /** Ile wiadomości przyszło, odkąd użytkownik odjechał w górę. */
38
41
  const [newCount, setNewCount] = useState(0);
39
- /** Tekst pływającej pigułki dnia — zostaje po zgaszeniu, żeby jej pas nie zmieniał wysokości. */
42
+ /** Tekst pływającej pigułki dnia - zostaje po zgaszeniu, żeby jej pas nie zmieniał wysokości. */
40
43
  const [dayLabel, setDayLabel] = useState('');
41
44
  /** Czy pigułka dnia jest widoczna. */
42
45
  const [dayVisible, setDayVisible] = useState(false);
43
46
  const lastCount = useRef(0);
44
47
  const wasAtTop = useRef(false);
45
48
  const rows = buildChatRows(messages, { currentUserId: currentUser, gapMs: groupGapMs, unreadFrom });
49
+ const motion = resolveEnterMotion(enterMotion, useKptMotion());
46
50
  const pending = pendingCount(messages);
47
51
  /**
48
- * Ustawia tekst pływającej pigułki dnia — co ma na niej być, liczy `chatDayIndicator`
52
+ * Ustawia tekst pływającej pigułki dnia - co ma na niej być, liczy `chatDayIndicator`
49
53
  * z `@konce-pt/chat`, tutaj zostaje czytanie prostokątów i przepisanie etykiety.
50
54
  *
51
55
  * Wszystkie pomiary idą przed zapisem stanu, więc przeglądarka przelicza układ raz
@@ -77,24 +81,65 @@ export function KptChat({ messages = [], currentUser = '', groupGapMs = KPT_CHAT
77
81
  const reduced = globalThis.matchMedia?.('(prefers-reduced-motion: reduce)').matches ?? false;
78
82
  element.scrollTo({ top: element.scrollHeight, behavior: behavior ?? (reduced ? 'auto' : 'smooth') });
79
83
  };
80
- // Dosunięcie liczone po ułożeniu, nie po malowaniu — inaczej widać skok listy.
84
+ const firstIdRef = useRef(null);
85
+ const prevScrollHeight = useRef(0);
86
+ const dayFrame = useRef(0);
87
+ // Wartości czytane w efekcie listy, ale nieuruchamiające go - przez zdarzenia efektu.
88
+ const autoScrollOnNew = useEffectEvent(() => autoScroll);
89
+ const pinnedNow = useEffectEvent(() => pinned);
90
+ const bumpNewCount = useEffectEvent((added) => setNewCount((value) => value + added));
91
+ /**
92
+ * Wejście wiadomości doklejonych na dole: własna wyrasta od pola pisania, cudza wchodzi od strony
93
+ * nadawcy. Pierwsze wejście i starsze doczytane na górze się nie ruszają - to nie są nowości.
94
+ */
95
+ const playEntered = useEffectEvent((viewport, added) => {
96
+ if (motion === 'none')
97
+ return;
98
+ const hosts = viewport.querySelectorAll(':scope > .kpt-chat-message-host');
99
+ const messageRows = rows.filter((row) => row.kind === 'message');
100
+ const count = Math.min(added, 5, hosts.length);
101
+ for (let k = 0; k < count; k++) {
102
+ const index = hosts.length - count + k;
103
+ const row = messageRows[index];
104
+ const host = hosts[index];
105
+ if (!row || !host || row.kind !== 'message')
106
+ continue;
107
+ playEnterMotion(host, motion, row.own ? 'below' : 'start', k * 40);
108
+ }
109
+ });
110
+ // Dosunięcie liczone po ułożeniu, nie po malowaniu - inaczej widać skok listy.
81
111
  useLayoutEffect(() => {
82
112
  if (!viewportRef.current)
83
113
  return;
114
+ const element = viewportRef.current;
84
115
  const count = messages.length;
85
- const grew = count > lastCount.current;
116
+ const added = count - lastCount.current;
86
117
  const first = lastCount.current === 0;
118
+ const firstId = messages[0]?.id ?? null;
119
+ // Starsze wiadomości doklejone na górze (`onLoadMore`) - nie są nowe, a widok ma zostać w miejscu.
120
+ const prepended = !first && added > 0 && firstId !== firstIdRef.current;
87
121
  lastCount.current = count;
88
- if (!grew)
122
+ firstIdRef.current = firstId;
123
+ if (prepended) {
124
+ // Przeglądarki z `overflow-anchor` kotwiczą same; Safari go nie ma i bez tego lista skacze.
125
+ if (!globalThis.CSS?.supports('overflow-anchor', 'auto'))
126
+ element.scrollTop += element.scrollHeight - prevScrollHeight.current;
127
+ prevScrollHeight.current = element.scrollHeight;
89
128
  return;
90
- // Pierwsze wejście zawsze ląduje na dole — rozmowę czyta się od końca.
91
- if (first || (autoScroll && pinned))
129
+ }
130
+ prevScrollHeight.current = element.scrollHeight;
131
+ if (added <= 0)
132
+ return;
133
+ if (!first)
134
+ playEntered(element, added);
135
+ // Pierwsze wejście zawsze ląduje na dole - rozmowę czyta się od końca.
136
+ if (first || (autoScrollOnNew() && pinnedNow()))
92
137
  scrollToBottom(first ? 'auto' : undefined);
138
+ // Tyle, ile naprawdę przyszło - paczka pięciu wiadomości to "5 nowych", nie "1".
93
139
  else
94
- setNewCount((value) => value + 1);
95
- // eslint-disable-next-line react-hooks/exhaustive-deps
140
+ bumpNewCount(added);
96
141
  }, [messages]);
97
- // Po każdej zmianie listy wiersze stoją gdzie indziej — pigułkę trzeba przeliczyć, inaczej
142
+ // Po każdej zmianie listy wiersze stoją gdzie indziej - pigułkę trzeba przeliczyć, inaczej
98
143
  // pokazywałaby dzień sprzed dojścia nowej wiadomości.
99
144
  useEffect(() => {
100
145
  syncDay();
@@ -114,13 +159,19 @@ export function KptChat({ messages = [], currentUser = '', groupGapMs = KPT_CHAT
114
159
  setPinned(atBottom);
115
160
  if (atBottom)
116
161
  setNewCount(0);
117
- // `onLoadMore` tylko na wejściu w strefę szczytu — przy każdym zdarzeniu scroll
162
+ // `onLoadMore` tylko na wejściu w strefę szczytu - przy każdym zdarzeniu scroll
118
163
  // aplikacja dostawałaby dziesiątki żądań na jedno przewinięcie.
119
164
  const atTop = element.scrollTop < PIN_THRESHOLD;
120
165
  if (atTop && !wasAtTop.current)
121
166
  onLoadMore?.();
122
167
  wasAtTop.current = atTop;
123
- syncDay();
168
+ // Pomiary pigułki raz na klatkę, nie przy każdym zdarzeniu `scroll` (wymuszony układ).
169
+ if (dayFrame.current)
170
+ return;
171
+ dayFrame.current = requestAnimationFrame(() => {
172
+ dayFrame.current = 0;
173
+ syncDay();
174
+ });
124
175
  };
125
176
  const jumpToLatest = () => {
126
177
  setPinned(true);
@@ -128,8 +179,8 @@ export function KptChat({ messages = [], currentUser = '', groupGapMs = KPT_CHAT
128
179
  scrollToBottom();
129
180
  };
130
181
  /**
131
- * Etykieta separatora dnia. Rdzeń zwraca rodzaj, słownik daje „dziś"/„wczoraj",
132
- * a nazwę dnia i pełną datę — `Intl`, żeby nie trzymać w słowniku kalendarza.
182
+ * Etykieta separatora dnia. Rdzeń zwraca rodzaj, słownik daje "dziś"/"wczoraj",
183
+ * a nazwę dnia i pełną datę - `Intl`, żeby nie trzymać w słowniku kalendarza.
133
184
  */
134
185
  const dateText = (label) => {
135
186
  switch (label.kind) {
@@ -147,7 +198,10 @@ export function KptChat({ messages = [], currentUser = '', groupGapMs = KPT_CHAT
147
198
  }).format(label.date);
148
199
  }
149
200
  };
150
- return (_jsxs("div", { className: cn('kpt-chat-host', className), ...rest, children: [offline ? (_jsxs("p", { className: "kpt-chat__offline", role: "status", children: [_jsx(KptIcon, { name: "alert-triangle" }), _jsx("span", { children: t('chat.offline') }), pending ? (_jsx("span", { className: "kpt-chat__offline-count", children: t('chat.pending', { count: pending }) })) : null] })) : null, _jsxs("div", { className: "kpt-chat__body", children: [_jsxs("div", { ref: viewportRef, className: "kpt-chat__viewport", role: "log", "aria-live": "polite", "aria-relevant": "additions", onScroll: (event) => onScroll(event.currentTarget), children: [rows.length === 0 ? (_jsx("p", { className: "kpt-chat__empty", children: emptyLabel || t('chat.empty') })) : null, rows.map((row) => {
201
+ return (_jsxs("div", { className: cn('kpt-chat-host', className), ...rest, children: [offline ? (_jsxs("p", { className: "kpt-chat__offline", role: "status", children: [_jsx(KptIcon, { name: "alert-triangle" }), _jsx("span", { children: t('chat.offline') }), pending ? (_jsx("span", { className: "kpt-chat__offline-count", children: t('chat.pending', { count: pending }) })) : null] })) : null, _jsxs("div", { className: "kpt-chat__body", children: [_jsxs("div", { ref: viewportRef, className: "kpt-chat__viewport", role: "log", "aria-live": "polite", "aria-relevant": "additions",
202
+ // Przewijana historia musi być osiągalna z klawiatury także bez fokusowalnych bąbli.
203
+ // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex -- przewijany region musi być osiągalny z klawiatury (WCAG 2.1.1) - role="log" nie jest interaktywna, przewijanie tak
204
+ tabIndex: 0, "aria-label": t('chat.history'), onScroll: (event) => onScroll(event.currentTarget), children: [rows.length === 0 ? (_jsx("p", { className: "kpt-chat__empty", children: emptyLabel || t('chat.empty') })) : null, rows.map((row) => {
151
205
  if (row.kind === 'date') {
152
206
  const label = dateText(row.label);
153
207
  return (_jsx("p", { className: "kpt-chat__date", role: "separator", "aria-label": label, children: label }, row.key));
@@ -155,6 +209,6 @@ export function KptChat({ messages = [], currentUser = '', groupGapMs = KPT_CHAT
155
209
  if (row.kind === 'unread') {
156
210
  return (_jsx("p", { className: "kpt-chat__unread", role: "separator", "aria-label": t('chat.newMessages', { count: row.count }), children: _jsx("span", { children: t('chat.unread') }) }, row.key));
157
211
  }
158
- return (_jsx(KptChatMessage, { message: row.message, text: renderMessage ? '' : null, align: row.own ? 'end' : 'start', position: row.position, showAuthor: row.showAuthor, showAvatar: row.showAvatar, showMeta: row.showMeta, actions: actions, reactionOptions: reactionOptions, locale: locale, dictionary: dictionary, onRetry: () => onRetry?.(row.message), onReply: () => onReply?.(row.message), onReact: (emoji) => onReact?.({ message: row.message, emoji }), onQuoted: (quote) => onQuoted?.(quote), children: renderMessage ? renderMessage(row.message, row) : null }, row.key));
159
- }), typing.length ? _jsx(KptChatTyping, { authors: typing, locale: locale, dictionary: dictionary }) : null] }), _jsx("p", { ref: dayRef, className: "kpt-chat__day", "aria-hidden": "true", "data-hidden": dayVisible ? undefined : '', children: dayLabel }), newCount ? (_jsxs("button", { type: "button", className: "kpt-chat__jump", onClick: jumpToLatest, children: [_jsx(KptIcon, { name: "arrow-down" }), t('chat.newMessages', { count: newCount })] })) : null] })] }));
212
+ return (_jsx(KptChatMessage, { message: row.message, text: renderMessage ? '' : null, align: row.own ? 'end' : 'start', position: row.position, showAuthor: row.showAuthor, showAvatar: row.showAvatar, showMeta: row.showMeta, actions: actions, reactionOptions: reactionOptions, enterMotion: motion, locale: locale, dictionary: dictionary, onRetry: () => onRetry?.(row.message), onReply: () => onReply?.(row.message), onReact: (emoji) => onReact?.({ message: row.message, emoji }), onQuoted: (quote) => onQuoted?.(quote), children: renderMessage ? renderMessage(row.message, row) : null }, row.key));
213
+ }), typing.length ? (_jsx("div", { "aria-live": "off", children: _jsx(KptChatTyping, { authors: typing, locale: locale, dictionary: dictionary }) })) : null] }), _jsx("p", { ref: dayRef, className: "kpt-chat__day", "aria-hidden": "true", "data-hidden": dayVisible ? undefined : '', children: dayLabel }), newCount ? (_jsxs("button", { type: "button", className: "kpt-chat__jump", onClick: jumpToLatest, children: [_jsx(KptIcon, { name: "arrow-down" }), t('chat.newMessages', { count: newCount })] })) : null] })] }));
160
214
  }
@@ -6,13 +6,13 @@ Import: `import { KptChat } from '@konce-pt/react/chat';`
6
6
 
7
7
  ## DOM
8
8
  <div class="kpt-chat-host">
9
- <p class="kpt-chat__offline" role="status">…</p>
9
+ <p class="kpt-chat__offline" role="status">...</p>
10
10
  <div class="kpt-chat__body">
11
11
  <div class="kpt-chat__viewport" role="log" aria-live="polite">
12
12
  <p class="kpt-chat__date" role="separator">Today</p>
13
- <div class="kpt-chat-message-host">…</div>
14
- <p class="kpt-chat__unread" role="separator">…</p>
15
- <div class="kpt-chat-typing-host">…</div>
13
+ <div class="kpt-chat-message-host">...</div>
14
+ <p class="kpt-chat__unread" role="separator">...</p>
15
+ <div class="kpt-chat-typing-host">...</div>
16
16
  </div>
17
17
  <p class="kpt-chat__day" aria-hidden="true">Today</p>
18
18
  <button class="kpt-chat__jump">3 new messages</button>
@@ -20,31 +20,32 @@ Import: `import { KptChat } from '@konce-pt/react/chat';`
20
20
  </div>
21
21
 
22
22
  ## Props
23
- - `messages`: `KptChatMessage<T>[]` — **in chronological order**; the component does not sort
24
- - `currentUser`: string — the author id treated as "me"
23
+ - `messages`: `KptChatMessage<T>[]` - **in chronological order**; the component does not sort
24
+ - `currentUser`: string - the author id treated as "me"
25
25
  - `groupGapMs`: number (default `KPT_CHAT_GROUP_GAP_MS`, 5 min)
26
- - `unreadFrom`: string | null — the id of the first unread message
26
+ - `unreadFrom`: string | null - the id of the first unread message
27
27
  - `offline`: boolean, `typing`: `KptChatAuthor[]`, `autoScroll`: boolean (default true)
28
- - `actions`: boolean — reply and react buttons on hover; `emptyLabel`: string
29
- - `reactionOptions`: `readonly KptChatReactionOption[]` (default `KPT_CHAT_REACTIONS`) — passed to the bubbles
30
- - `onRetry` / `onReply` — `(message) => void`; `onReact` — `(event: KptChatReactEvent) => void`
31
- (`{ message, emoji }`); `onQuoted` — `(quote) => void`
32
- - `onLoadMore` — fired once when the viewport reaches the top, not on every scroll event
33
- - `renderMessage(message, row)` — the equivalent of `<ng-template let-message>`; it replaces the
28
+ - `actions`: boolean - reply and react buttons on hover; `emptyLabel`: string
29
+ - `reactionOptions`: `readonly KptChatReactionOption[]` (default `KPT_CHAT_REACTIONS`) - passed to the bubbles
30
+ - `enterMotion`: `'spring' | 'fade' | 'none'` (default from `KptMotionProvider`, else `spring`) - see "Motion"
31
+ - `onRetry` / `onReply` - `(message) => void`; `onReact` - `(event: KptChatReactEvent) => void`
32
+ (`{ message, emoji }`); `onQuoted` - `(quote) => void`
33
+ - `onLoadMore` - fired once when the viewport reaches the top, not on every scroll event
34
+ - `renderMessage(message, row)` - the equivalent of `<ng-template let-message>`; it replaces the
34
35
  bubble's text, and the bubble, its grouping and its meta row stay in place
35
36
  - `className`, `ref` and every other `<div>` prop go to the host
36
37
 
37
38
  ## The composer is a sibling, not a slot
38
39
  A read-only conversation (an archive, a preview) should not have to carry a text field it never
39
40
  shows, so `KptChatComposer` is placed next to the list rather than inside it. The parent is what
40
- gives the list its height — `.kpt-chat-host` is a column flexbox with `min-height: 0`.
41
+ gives the list its height - `.kpt-chat-host` is a column flexbox with `min-height: 0`.
41
42
 
42
43
  ## Transport stays in the application
43
44
  The component takes messages and only reports. Retrying a send is a status change the application
44
45
  makes; `mergeStatus()` from `@konce-pt/chat` is there for out-of-order acks.
45
46
 
46
47
  ## Bubble actions: reply and react
47
- `actions` puts two buttons beside the bubble. The arrow emits `onReply` with the message —
48
+ `actions` puts two buttons beside the bubble. The arrow emits `onReply` with the message -
48
49
  `replyRefFrom()` from `@konce-pt/chat` turns it into the `replyTo` the composer's quote bar wants.
49
50
  The smiley opens a picker of five reactions (`KPT_CHAT_REACTIONS`) and emits `onReact` with the
50
51
  chosen emoji; `toggleReaction(message.reactions, emoji)` is the arithmetic that puts it in the
@@ -57,6 +58,20 @@ re-render and resize) the component measures the separators, the unread divider
57
58
  notices, hands the rectangles to `chatDayIndicator()` from `@konce-pt/chat` and swaps the pill's
58
59
  text; a centred label driving into the pill's band puts the pill out, never the content.
59
60
 
61
+ ## Motion
62
+ `enterMotion` (`spring | fade | none`) on the chat, the bubble and the dock:
63
+ - a new message at the bottom - your own grows from the composer side, someone else's comes in from
64
+ the sender's side, sharpening out of a blur; the first load and older messages loaded at the top
65
+ (`loadMore`) do not move - they are not news;
66
+ - a new reaction pops in, a changed count rolls in like a drum, a new status icon turns into place
67
+ (only after the bubble's first render, so loaded history stays still);
68
+ - the dock: `spring` grows the window out of the button (a capsule the size of the FAB opens into
69
+ the panel), `fade` slides and fades it as before.
70
+
71
+ With no value it takes `enter` from `KptMotionProvider` (default `spring`); `enabled: false` and the system
72
+ "reduce motion" setting always win. The typing dots are a state indicator - with motion off they
73
+ pulse slower instead of stopping.
74
+
60
75
  ## Examples
61
76
  <KptChat messages={messages} currentUser="me" typing={typing} onRetry={resend} />
62
77
  <KptChat messages={messages} currentUser="me" offline unreadFrom="m-42" onLoadMore={loadOlder} />
@@ -76,3 +91,12 @@ pill `--kpt-color-primary-border` + `--kpt-elevation-2`.
76
91
  `role="log"` with `aria-live="polite"` announces a new message without cutting off what is being
77
92
  read. Separators are `role="separator"` with a label. Auto-scroll uses `behavior: 'auto'` under
78
93
  `prefers-reduced-motion`.
94
+
95
+ ## Keyboard, counters and history
96
+ The scrolling history (`role="log"`) is a Tab stop named "Conversation history" (`chat.history`), so the
97
+ keyboard can scroll it even when no bubble holds a focusable element. The "new messages" button counts
98
+ the messages that actually arrived (a batch of five gives "5"). The typing indicator sits inside the log
99
+ but with `aria-live="off"`, so "X is typing..." is not announced like a message. Older messages loaded at
100
+ the top keep the view on the same message, also in Safari (no `overflow-anchor`). The composer reports
101
+ typing on the first keystroke and then at most every 3 s (`KPT_CHAT_TYPING_THROTTLE_MS`). A custom
102
+ reaction option may carry `label` - its accessible name.
@@ -1,7 +1,8 @@
1
1
  import { type ComponentPropsWithRef } from 'react';
2
- import type { KptChatReplyRef } from '@konce-pt/chat';
2
+ import { type KptChatReplyRef } from '@konce-pt/chat';
3
3
  import { type KptI18nOverrides } from '../i18n/i18n-context.tsx';
4
- /** Ładunek wysyłki. Kompozytor niczego nie wysyła — pakuje dane i oddaje aplikacji. */
4
+ import { type KptSpeechColor } from '../speech/speech-button.tsx';
5
+ /** Ładunek wysyłki. Kompozytor niczego nie wysyła - pakuje dane i oddaje aplikacji. */
5
6
  export interface KptChatComposerSubmit {
6
7
  text: string;
7
8
  files: readonly File[];
@@ -14,28 +15,40 @@ export interface KptChatComposerProps extends Omit<ComponentPropsWithRef<'div'>,
14
15
  /** Podpowiedź w pustym polu; bez niej etykieta ze słownika. */
15
16
  placeholder?: string;
16
17
  disabled?: boolean;
17
- /** Brak połączenia — przycisk mówi o kolejkowaniu, nie o wysyłce. */
18
+ /** Brak połączenia - przycisk mówi o kolejkowaniu, nie o wysyłce. */
18
19
  offline?: boolean;
19
20
  allowAttachments?: boolean;
20
21
  /** Ile wierszy pole może urosnąć, zanim zacznie się przewijać. */
21
22
  maxRows?: number;
22
23
  enterToSend?: boolean;
24
+ /**
25
+ * Mikrofon do dyktowania (Web Speech API) między polem a przyciskiem wysyłki. Bez wsparcia
26
+ * przeglądarki się nie pokazuje. Wysyłka w trakcie dyktowania zatwierdza to, co widać w polu.
27
+ */
28
+ speech?: boolean;
29
+ /** Język dyktowania (BCP 47); bez niego z `KptI18nProvider`. */
30
+ speechLang?: string;
31
+ /** Kolor "stop" i fali w trakcie nasłuchu; domyślnie kolor motywu. */
32
+ speechColor?: KptSpeechColor;
23
33
  /** Wiadomość, na którą odpowiadamy. Po wysłaniu czyści się razem z treścią. */
24
34
  replyTo?: KptChatReplyRef | null;
25
35
  defaultReplyTo?: KptChatReplyRef | null;
26
36
  onReplyToChange?: (replyTo: KptChatReplyRef | null) => void;
27
37
  onSend?: (payload: KptChatComposerSubmit) => void;
28
- /** Uderzenie w klawiaturę — sygnał „piszę" dla drugiej strony. */
38
+ /**
39
+ * Sygnał "piszę" dla drugiej strony: przy pierwszym znaku, a w trakcie pisania najwyżej raz na
40
+ * `KPT_CHAT_TYPING_THROTTLE_MS` (3 s) - nie przy każdym znaku.
41
+ */
29
42
  onTyping?: () => void;
30
43
  }
31
44
  /**
32
- * Pole pisania wiadomości — autorosnące, z załącznikami i paskiem cytatu.
45
+ * Pole pisania wiadomości - autorosnące, z załącznikami i paskiem cytatu.
33
46
  *
34
47
  * `Enter` wysyła, `Shift+Enter` łamie linię (`enterToSend={false}` odwraca układ).
35
- * Przy `offline` przycisk zmienia etykietę na kolejkowanie — wiadomość dalej da się napisać
48
+ * Przy `offline` przycisk zmienia etykietę na kolejkowanie - wiadomość dalej da się napisać
36
49
  * i oddać aplikacji, bo to ona decyduje, co zrobić z kolejką.
37
50
  *
38
51
  * @example
39
52
  * <KptChatComposer allowAttachments onSend={(payload) => send(payload)} />
40
53
  */
41
- export declare function KptChatComposer({ value, defaultValue, onValueChange, placeholder, disabled, offline, allowAttachments, maxRows, enterToSend, replyTo, defaultReplyTo, onReplyToChange, onSend, onTyping, locale, dictionary, className, ...rest }: Readonly<KptChatComposerProps>): import("react").JSX.Element;
54
+ export declare function KptChatComposer({ value, defaultValue, onValueChange, placeholder, disabled, offline, allowAttachments, maxRows, enterToSend, speech, speechLang, speechColor, replyTo, defaultReplyTo, onReplyToChange, onSend, onTyping, locale, dictionary, className, ...rest }: Readonly<KptChatComposerProps>): import("react").JSX.Element;