@konce-pt/react 0.12.0 → 0.15.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 (378) hide show
  1. package/README.md +122 -64
  2. package/dist/accordion/accordion.d.ts +14 -6
  3. package/dist/accordion/accordion.js +14 -7
  4. package/dist/accordion/llms.txt +22 -10
  5. package/dist/alert/alert.d.ts +2 -2
  6. package/dist/alert/alert.js +2 -2
  7. package/dist/alert/llms.txt +10 -10
  8. package/dist/app-shell/app-shell.d.ts +14 -14
  9. package/dist/app-shell/app-shell.js +11 -11
  10. package/dist/app-shell/llms.txt +13 -13
  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 +7 -7
  14. package/dist/auth/llms.txt +9 -9
  15. package/dist/autocomplete/autocomplete.d.ts +19 -6
  16. package/dist/autocomplete/autocomplete.js +23 -10
  17. package/dist/autocomplete/llms.txt +16 -5
  18. package/dist/avatar/avatar.d.ts +6 -6
  19. package/dist/avatar/avatar.js +5 -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 +3 -3
  23. package/dist/avatar-group/llms.txt +8 -8
  24. package/dist/backdrop/backdrop.d.ts +7 -7
  25. package/dist/backdrop/backdrop.js +16 -16
  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 +4 -4
  31. package/dist/bottom-sheet/bottom-sheet.js +2 -2
  32. package/dist/bottom-sheet/llms.txt +9 -9
  33. package/dist/breadcrumb/breadcrumb.d.ts +2 -2
  34. package/dist/breadcrumb/breadcrumb.js +3 -3
  35. package/dist/breadcrumb/llms.txt +3 -3
  36. package/dist/button/button.d.ts +3 -3
  37. package/dist/button/button.js +8 -8
  38. package/dist/button/llms.txt +27 -19
  39. package/dist/button-group/button-group.d.ts +9 -9
  40. package/dist/button-group/button-group.js +30 -8
  41. package/dist/button-group/llms.txt +20 -14
  42. package/dist/card/card.d.ts +8 -8
  43. package/dist/card/card.js +6 -6
  44. package/dist/card/llms.txt +20 -20
  45. package/dist/carousel/carousel.d.ts +6 -6
  46. package/dist/carousel/carousel.js +4 -4
  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 +1 -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 +1 -1
  53. package/dist/charts/chart.js +8 -8
  54. package/dist/charts/llms.txt +6 -6
  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 +42 -18
  58. package/dist/chat/llms.txt +32 -17
  59. package/dist/chat-composer/chat-composer.d.ts +17 -7
  60. package/dist/chat-composer/chat-composer.js +21 -8
  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 +9 -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 +17 -7
  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 +3 -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 +26 -7
  74. package/dist/checkbox/llms.txt +24 -12
  75. package/dist/chip/chip.d.ts +4 -4
  76. package/dist/chip/chip.js +2 -2
  77. package/dist/chip/llms.txt +11 -11
  78. package/dist/chips-input/chips-input.d.ts +4 -4
  79. package/dist/chips-input/chips-input.js +35 -9
  80. package/dist/chips-input/llms.txt +15 -8
  81. package/dist/clock/clock.d.ts +4 -4
  82. package/dist/clock/clock.js +7 -7
  83. package/dist/clock/llms.txt +8 -8
  84. package/dist/color-picker/color-picker.d.ts +6 -6
  85. package/dist/color-picker/color-picker.js +3 -3
  86. package/dist/color-picker/llms.txt +5 -5
  87. package/dist/confirm/confirm-container.d.ts +3 -3
  88. package/dist/confirm/confirm-container.js +5 -5
  89. package/dist/confirm/confirm-store.d.ts +4 -4
  90. package/dist/confirm/confirm-store.js +7 -7
  91. package/dist/confirm/llms.txt +6 -6
  92. package/dist/context-menu/context-menu.d.ts +1 -1
  93. package/dist/context-menu/context-menu.js +5 -5
  94. package/dist/context-menu/llms.txt +7 -7
  95. package/dist/data-table/data-table-types.d.ts +5 -5
  96. package/dist/data-table/data-table.d.ts +29 -8
  97. package/dist/data-table/data-table.js +340 -95
  98. package/dist/data-table/export-csv.d.ts +3 -3
  99. package/dist/data-table/export-csv.js +4 -4
  100. package/dist/data-table/llms.txt +127 -94
  101. package/dist/data-view/data-view.d.ts +13 -5
  102. package/dist/data-view/data-view.js +67 -6
  103. package/dist/data-view/llms.txt +29 -10
  104. package/dist/date-range/date-range.d.ts +9 -9
  105. package/dist/date-range/date-range.js +24 -24
  106. package/dist/date-range/llms.txt +20 -20
  107. package/dist/datepicker/datepicker.d.ts +10 -10
  108. package/dist/datepicker/datepicker.js +29 -29
  109. package/dist/datepicker/llms.txt +24 -24
  110. package/dist/dialog/dialog.d.ts +12 -7
  111. package/dist/dialog/dialog.js +4 -4
  112. package/dist/dialog/llms.txt +14 -11
  113. package/dist/divider/divider.d.ts +1 -1
  114. package/dist/divider/llms.txt +4 -4
  115. package/dist/drawer/drawer.d.ts +5 -5
  116. package/dist/drawer/drawer.js +2 -2
  117. package/dist/drawer/llms.txt +11 -11
  118. package/dist/empty/empty.d.ts +2 -2
  119. package/dist/empty/empty.js +1 -1
  120. package/dist/empty/llms.txt +9 -9
  121. package/dist/fab/fab.d.ts +1 -1
  122. package/dist/fab/fab.js +1 -1
  123. package/dist/fab/llms.txt +10 -6
  124. package/dist/fieldset/fieldset.d.ts +18 -4
  125. package/dist/fieldset/fieldset.js +9 -6
  126. package/dist/fieldset/llms.txt +26 -9
  127. package/dist/file-upload/file-upload.d.ts +3 -3
  128. package/dist/file-upload/file-upload.js +7 -7
  129. package/dist/file-upload/llms.txt +7 -7
  130. package/dist/form-field/form-field-context.d.ts +4 -4
  131. package/dist/form-field/form-field.d.ts +12 -4
  132. package/dist/form-field/form-field.js +26 -6
  133. package/dist/form-field/llms.txt +25 -11
  134. package/dist/galleria/galleria.d.ts +3 -3
  135. package/dist/galleria/galleria.js +5 -5
  136. package/dist/galleria/llms.txt +7 -7
  137. package/dist/grid/col.d.ts +4 -4
  138. package/dist/grid/col.js +2 -2
  139. package/dist/grid/flex.d.ts +3 -3
  140. package/dist/grid/flex.js +1 -1
  141. package/dist/grid/grid.d.ts +7 -7
  142. package/dist/grid/grid.js +3 -3
  143. package/dist/grid/layout.d.ts +2 -2
  144. package/dist/grid/layout.js +2 -2
  145. package/dist/grid/llms.txt +15 -15
  146. package/dist/grid-entry/public-api.js +1 -1
  147. package/dist/i18n/i18n-context.d.ts +3 -3
  148. package/dist/i18n/i18n-context.js +7 -7
  149. package/dist/icon/icon-registry.d.ts +3 -3
  150. package/dist/icon/icon-registry.js +3 -3
  151. package/dist/icon/icon.d.ts +4 -4
  152. package/dist/icon/icon.js +6 -6
  153. package/dist/icon/llms.txt +13 -13
  154. package/dist/icon-button/icon-button.d.ts +2 -2
  155. package/dist/icon-button/icon-button.js +1 -1
  156. package/dist/icon-button/llms.txt +9 -5
  157. package/dist/icons-entry/public-api.d.ts +2 -2
  158. package/dist/icons-entry/public-api.js +3 -3
  159. package/dist/image/image.js +1 -1
  160. package/dist/image/llms.txt +13 -13
  161. package/dist/input/input.d.ts +18 -5
  162. package/dist/input/input.js +21 -6
  163. package/dist/input/llms.txt +25 -11
  164. package/dist/input-mask/input-mask.d.ts +1 -1
  165. package/dist/input-mask/input-mask.js +2 -2
  166. package/dist/input-mask/llms.txt +5 -5
  167. package/dist/input-number/input-number.d.ts +9 -9
  168. package/dist/input-number/input-number.js +11 -11
  169. package/dist/input-number/llms.txt +14 -14
  170. package/dist/input-otp/input-otp.d.ts +6 -6
  171. package/dist/input-otp/input-otp.js +10 -10
  172. package/dist/input-otp/llms.txt +9 -9
  173. package/dist/internal/calendar-focus.d.ts +1 -1
  174. package/dist/internal/calendar-focus.js +3 -3
  175. package/dist/internal/cn.d.ts +1 -1
  176. package/dist/internal/mask.d.ts +2 -2
  177. package/dist/internal/mask.js +4 -4
  178. package/dist/internal/option-key.d.ts +2 -2
  179. package/dist/internal/option-key.js +2 -2
  180. package/dist/internal/option-nav.d.ts +4 -4
  181. package/dist/internal/option-nav.js +5 -5
  182. package/dist/internal/overlay-container.js +1 -1
  183. package/dist/internal/overlay.d.ts +3 -3
  184. package/dist/internal/overlay.js +8 -8
  185. package/dist/internal/password-strength.d.ts +2 -2
  186. package/dist/internal/password-strength.js +6 -6
  187. package/dist/internal/roving.d.ts +2 -2
  188. package/dist/internal/roving.js +3 -3
  189. package/dist/internal/scroll-lock.d.ts +1 -1
  190. package/dist/internal/scroll-lock.js +2 -2
  191. package/dist/internal/slot-children.d.ts +3 -3
  192. package/dist/internal/slot-children.js +4 -4
  193. package/dist/internal/slot.d.ts +3 -3
  194. package/dist/internal/slot.js +2 -2
  195. package/dist/internal/step.d.ts +3 -3
  196. package/dist/internal/step.js +4 -4
  197. package/dist/internal/tween.d.ts +5 -5
  198. package/dist/internal/tween.js +5 -5
  199. package/dist/internal/types.d.ts +2 -2
  200. package/dist/internal/use-breakpoint.d.ts +2 -2
  201. package/dist/internal/use-breakpoint.js +4 -4
  202. package/dist/internal/use-controllable-state.d.ts +1 -1
  203. package/dist/internal/use-controllable-state.js +3 -3
  204. package/dist/internal/use-id.d.ts +2 -2
  205. package/dist/internal/use-id.js +2 -2
  206. package/dist/internal/use-modal-focus.d.ts +1 -1
  207. package/dist/internal/use-modal-focus.js +3 -3
  208. package/dist/knob/knob.d.ts +4 -4
  209. package/dist/knob/knob.js +14 -10
  210. package/dist/knob/llms.txt +17 -12
  211. package/dist/listbox/listbox.d.ts +6 -6
  212. package/dist/listbox/listbox.js +9 -9
  213. package/dist/listbox/llms.txt +10 -10
  214. package/dist/loader/llms.txt +56 -0
  215. package/dist/loader/loader.d.ts +49 -0
  216. package/dist/loader/loader.js +68 -0
  217. package/dist/map/llms.txt +25 -22
  218. package/dist/map/map-create-dialog.d.ts +4 -4
  219. package/dist/map/map-create-dialog.js +10 -10
  220. package/dist/map/map-hours-field.d.ts +4 -4
  221. package/dist/map/map-hours-field.js +7 -7
  222. package/dist/map/map-image-field.d.ts +2 -2
  223. package/dist/map/map-image-field.js +4 -4
  224. package/dist/map/map-legend.d.ts +4 -4
  225. package/dist/map/map-legend.js +2 -2
  226. package/dist/map/map-poi-card.d.ts +3 -3
  227. package/dist/map/map-poi-card.js +9 -9
  228. package/dist/map/map-search.d.ts +17 -6
  229. package/dist/map/map-search.js +21 -18
  230. package/dist/map/map.d.ts +21 -14
  231. package/dist/map/map.js +30 -29
  232. package/dist/map/ports.d.ts +9 -9
  233. package/dist/map/ports.js +6 -6
  234. package/dist/map-entry/public-api.js +2 -2
  235. package/dist/megamenu/llms.txt +9 -9
  236. package/dist/megamenu/megamenu.d.ts +6 -6
  237. package/dist/megamenu/megamenu.js +10 -10
  238. package/dist/mention/llms.txt +15 -15
  239. package/dist/mention/mention.d.ts +5 -5
  240. package/dist/mention/mention.js +2 -2
  241. package/dist/mention/mentions.d.ts +1 -1
  242. package/dist/mention/mentions.js +1 -1
  243. package/dist/menu/llms.txt +12 -12
  244. package/dist/menu/menu-item.d.ts +4 -4
  245. package/dist/menu/menu-keys.d.ts +2 -2
  246. package/dist/menu/menu-keys.js +5 -5
  247. package/dist/menu/menu.d.ts +2 -2
  248. package/dist/menu/menu.js +6 -6
  249. package/dist/menubar/llms.txt +10 -10
  250. package/dist/menubar/menubar.d.ts +5 -5
  251. package/dist/menubar/menubar.js +10 -10
  252. package/dist/meter-group/llms.txt +12 -12
  253. package/dist/meter-group/meter-group.d.ts +2 -2
  254. package/dist/meter-group/meter-group.js +1 -1
  255. package/dist/motion/motion-context.d.ts +22 -0
  256. package/dist/motion/motion-context.js +28 -0
  257. package/dist/order-list/llms.txt +22 -10
  258. package/dist/order-list/order-list.d.ts +12 -4
  259. package/dist/order-list/order-list.js +104 -21
  260. package/dist/paginator/llms.txt +8 -8
  261. package/dist/paginator/paginator.js +1 -1
  262. package/dist/panel/llms.txt +25 -13
  263. package/dist/panel/panel.d.ts +11 -3
  264. package/dist/panel/panel.js +7 -4
  265. package/dist/password/llms.txt +16 -8
  266. package/dist/password/password.d.ts +2 -2
  267. package/dist/password/password.js +24 -3
  268. package/dist/peek/llms.txt +10 -10
  269. package/dist/peek/peek.d.ts +6 -6
  270. package/dist/peek/peek.js +7 -7
  271. package/dist/pick-list/llms.txt +33 -13
  272. package/dist/pick-list/pick-list.d.ts +12 -4
  273. package/dist/pick-list/pick-list.js +111 -23
  274. package/dist/popover/llms.txt +11 -11
  275. package/dist/popover/popover.d.ts +4 -4
  276. package/dist/popover/popover.js +5 -5
  277. package/dist/progress/llms.txt +17 -17
  278. package/dist/progress/progress.d.ts +7 -7
  279. package/dist/progress/progress.js +6 -6
  280. package/dist/public-api.d.ts +15 -3
  281. package/dist/public-api.js +13 -7
  282. package/dist/radio-group/llms.txt +13 -6
  283. package/dist/radio-group/radio-group.d.ts +11 -4
  284. package/dist/radio-group/radio-group.js +15 -7
  285. package/dist/rating/llms.txt +20 -13
  286. package/dist/rating/rating-pointer.d.ts +16 -0
  287. package/dist/rating/rating-pointer.js +26 -0
  288. package/dist/rating/rating.d.ts +6 -6
  289. package/dist/rating/rating.js +76 -12
  290. package/dist/rich-text/lists.d.ts +2 -2
  291. package/dist/rich-text/lists.js +7 -7
  292. package/dist/rich-text/llms.txt +26 -17
  293. package/dist/rich-text/rich-text.d.ts +12 -7
  294. package/dist/rich-text/rich-text.js +78 -54
  295. package/dist/rich-text/sanitize.d.ts +3 -3
  296. package/dist/rich-text/sanitize.js +12 -12
  297. package/dist/rich-text/tables.d.ts +3 -3
  298. package/dist/rich-text/tables.js +3 -3
  299. package/dist/roadmap/llms.txt +15 -15
  300. package/dist/roadmap/roadmap.d.ts +6 -6
  301. package/dist/roadmap/roadmap.js +19 -19
  302. package/dist/roadmap-entry/public-api.js +2 -2
  303. package/dist/scroll-top/llms.txt +6 -6
  304. package/dist/scroll-top/scroll-top.d.ts +1 -1
  305. package/dist/scroll-top/scroll-top.js +1 -1
  306. package/dist/select/llms.txt +19 -12
  307. package/dist/select/select.d.ts +8 -8
  308. package/dist/select/select.js +47 -21
  309. package/dist/shared/types.d.ts +5 -5
  310. package/dist/shared/types.js +1 -1
  311. package/dist/sidenav/llms.txt +9 -9
  312. package/dist/sidenav/sidenav.d.ts +1 -1
  313. package/dist/sidenav/sidenav.js +1 -1
  314. package/dist/skeleton/llms.txt +3 -3
  315. package/dist/skeleton/skeleton.d.ts +1 -1
  316. package/dist/skeleton/skeleton.js +1 -1
  317. package/dist/slider/llms.txt +32 -13
  318. package/dist/slider/slider-drag.d.ts +26 -0
  319. package/dist/slider/slider-drag.js +79 -0
  320. package/dist/slider/slider.d.ts +6 -5
  321. package/dist/slider/slider.js +27 -8
  322. package/dist/speech/llms.txt +97 -0
  323. package/dist/speech/speech-button.d.ts +44 -0
  324. package/dist/speech/speech-button.js +86 -0
  325. package/dist/speech/speech-wave.d.ts +19 -0
  326. package/dist/speech/speech-wave.js +16 -0
  327. package/dist/speech/use-speech-recognition.d.ts +81 -0
  328. package/dist/speech/use-speech-recognition.js +179 -0
  329. package/dist/speed-dial/llms.txt +14 -8
  330. package/dist/speed-dial/speed-dial.d.ts +1 -1
  331. package/dist/speed-dial/speed-dial.js +1 -1
  332. package/dist/spinner/llms.txt +7 -7
  333. package/dist/spinner/spinner.d.ts +1 -1
  334. package/dist/spinner/spinner.js +1 -1
  335. package/dist/split-button/llms.txt +21 -15
  336. package/dist/split-button/split-button.d.ts +2 -2
  337. package/dist/split-button/split-button.js +4 -4
  338. package/dist/splitter/llms.txt +11 -11
  339. package/dist/splitter/splitter.d.ts +3 -3
  340. package/dist/splitter/splitter.js +5 -5
  341. package/dist/stepper/llms.txt +33 -14
  342. package/dist/stepper/stepper.d.ts +6 -6
  343. package/dist/stepper/stepper.js +53 -10
  344. package/dist/switch/llms.txt +12 -5
  345. package/dist/switch/switch.d.ts +10 -3
  346. package/dist/switch/switch.js +12 -5
  347. package/dist/switch-group/llms.txt +9 -9
  348. package/dist/switch-group/switch-group.d.ts +4 -4
  349. package/dist/switch-group/switch-group.js +4 -4
  350. package/dist/tabs/llms.txt +10 -10
  351. package/dist/tabs/tabs.d.ts +5 -5
  352. package/dist/tabs/tabs.js +8 -8
  353. package/dist/textarea/llms.txt +23 -12
  354. package/dist/textarea/textarea.d.ts +19 -6
  355. package/dist/textarea/textarea.js +21 -7
  356. package/dist/timeline/llms.txt +4 -4
  357. package/dist/timeline/timeline.d.ts +1 -1
  358. package/dist/timeline/timeline.js +1 -1
  359. package/dist/toast/llms.txt +47 -17
  360. package/dist/toast/toast-container.d.ts +9 -3
  361. package/dist/toast/toast-container.js +17 -9
  362. package/dist/toast/toast-store.d.ts +40 -4
  363. package/dist/toast/toast-store.js +36 -1
  364. package/dist/toolbar/llms.txt +7 -7
  365. package/dist/toolbar/toolbar.d.ts +3 -3
  366. package/dist/toolbar/toolbar.js +2 -2
  367. package/dist/tooltip/llms.txt +10 -10
  368. package/dist/tooltip/tooltip.d.ts +3 -3
  369. package/dist/tooltip/tooltip.js +18 -18
  370. package/dist/tree/llms.txt +25 -12
  371. package/dist/tree/tree-keys.d.ts +5 -5
  372. package/dist/tree/tree-keys.js +7 -7
  373. package/dist/tree/tree.d.ts +14 -7
  374. package/dist/tree/tree.js +53 -18
  375. package/dist/update/llms.txt +54 -0
  376. package/dist/update/update.d.ts +49 -0
  377. package/dist/update/update.js +82 -0
  378. package/package.json +15 -13
@@ -1,11 +1,13 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import { useEffect, useEffectEvent, useId, useLayoutEffect, useMemo, useRef, useState, } from 'react';
4
- import { aggregate, createRowComparator, cssLengthToPx, isEmptyFilterValue, matchesColumnFilter, matchesGlobalFilter, toText, } from '@konce-pt/table';
4
+ import { aggregate, columnOffsets, createGlobalFilter, cssLengthToPx, isEmptyFilterValue, matchesColumnFilter, sortRows, toText, virtualColumnRange, virtualRange, } from '@konce-pt/table';
5
5
  import { cn } from "../internal/cn.js";
6
6
  import { useControllableState } from "../internal/use-controllable-state.js";
7
7
  import { useKptMessages } from "../i18n/i18n-context.js";
8
8
  import { KptButton } from "../button/button.js";
9
+ import { KptSpeechButton } from "../speech/speech-button.js";
10
+ import { useKptSpeechRecognition } from "../speech/use-speech-recognition.js";
9
11
  import { KptDatepicker } from "../datepicker/datepicker.js";
10
12
  import { KptDateRange } from "../date-range/date-range.js";
11
13
  import { KptIcon } from "../icon/icon.js";
@@ -18,8 +20,31 @@ import { exportTableCsv } from "./export-csv.js";
18
20
  const REM_PX = 16;
19
21
  const CHECKBOX_FROZEN_W = 3; // rem
20
22
  const EXPANDER_FROZEN_W = 2.5; // rem
21
- const VIRTUAL_BUFFER = 6;
22
- /** Edytory z panelem w nakładce — otwierane od razu i zatwierdzane na zamknięciu panelu. */
23
+ /** Wiersze dorenderowane nad i pod widocznym oknem. */
24
+ const ROW_OVERSCAN = 6;
25
+ /** Kolumny dorenderowane po obu stronach widocznego okna. */
26
+ const COLUMN_OVERSCAN = 2;
27
+ /** Powyżej tylu wierszy filtr globalny czeka na przerwę w pisaniu, zamiast liczyć co znak. */
28
+ const FILTER_DEBOUNCE_ROWS = 2000;
29
+ const FILTER_DEBOUNCE_MS = 150;
30
+ /** Kawałek rozgrzewania indeksu wyszukiwania na jedno wywołanie w czasie bezczynności. */
31
+ const WARMUP_SLICE_MS = 8;
32
+ function scheduleIdle(fn) {
33
+ return typeof requestIdleCallback === 'function' ? { idle: requestIdleCallback(fn) } : { timer: setTimeout(fn, 16) };
34
+ }
35
+ function cancelIdle(handle) {
36
+ if (!handle)
37
+ return;
38
+ if ('idle' in handle)
39
+ cancelIdleCallback(handle.idle);
40
+ else
41
+ clearTimeout(handle.timer);
42
+ }
43
+ /** Okna równe, gdy obejmują te same elementy - przewinięcie w obrębie okna nie budzi renderu. */
44
+ function sameWindow(a, b) {
45
+ return a.start === b.start && a.end === b.end && a.before === b.before && a.after === b.after;
46
+ }
47
+ /** Edytory z panelem w nakładce - otwierane od razu i zatwierdzane na zamknięciu panelu. */
23
48
  const OVERLAY_EDITORS = new Set(['date', 'select', 'multiselect']);
24
49
  /** Tablice (multiselect) porównujemy po zawartości, resztę po referencji. */
25
50
  function sameEditValue(a, b) {
@@ -29,6 +54,16 @@ function sameEditValue(a, b) {
29
54
  return a.length === b.length && a.every((v, i) => v === b[i]);
30
55
  return false;
31
56
  }
57
+ /**
58
+ * Szerokość kolumny w px: z jednostki (`px`/`rem`/`em`), a gdy się nie da - w trybie wirtualnym
59
+ * `columnWidth`, poza nim pomiar z wyrenderowanego nagłówka, a do pierwszego pomiaru 8 rem.
60
+ */
61
+ function columnPx(col, virtual, defaultPx, measured) {
62
+ const fromUnit = cssLengthToPx(col.width, REM_PX);
63
+ if (fromUnit !== null)
64
+ return fromUnit;
65
+ return virtual ? defaultPx : (measured.get(col.field) ?? 8 * REM_PX);
66
+ }
32
67
  const EMPTY_ROWS = [];
33
68
  const EMPTY_FIELDS = [];
34
69
  /**
@@ -47,7 +82,7 @@ const EMPTY_FIELDS = [];
47
82
  function getValue(row, field) {
48
83
  return row[field];
49
84
  }
50
- export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, filterRowPosition = 'bottom', pageSize = 0, pageSizeOptions = [5, 10, 20, 50], selectable = 'none', loading = false, ariaLabel = null, caption = null, clickableRows, emptyMessage, rowKey = null, scrollHeight = null, virtualScroll = false, rowHeight = 41, reorderable = false, exportable = false, exportFilename = 'export.csv', lazy = false, totalRecords = null, expandable = false, multiExpand = true, summaryLabel, summaryValues = null, selection, defaultSelection = EMPTY_ROWS, onSelectionChange, expandedRows, defaultExpandedRows = EMPTY_ROWS, onExpandedRowsChange, hiddenFields, defaultHiddenFields = EMPTY_FIELDS, onHiddenFieldsChange, onRowClick, onLazyLoad, onCellEdit, cells, cellEditors, renderRowDetail, paginatorActions, locale, dictionary, className, style, ...rest }) {
85
+ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, filterSpeech = false, filterSpeechLang, filterSpeechColor, filterRowPosition = 'bottom', pageSize = 0, pageSizeOptions = [5, 10, 20, 50], selectable = 'none', loading = false, ariaLabel = null, caption = null, clickableRows, emptyMessage, rowKey = null, scrollHeight = null, virtualScroll = false, columnWidth = '10rem', rowHeight = 41, reorderable = false, frozenSystemColumns = false, exportable = false, exportFilename = 'export.csv', lazy = false, totalRecords = null, expandable = false, multiExpand = true, summaryLabel, summaryValues = null, selection, defaultSelection = EMPTY_ROWS, onSelectionChange, expandedRows, defaultExpandedRows = EMPTY_ROWS, onExpandedRowsChange, hiddenFields, defaultHiddenFields = EMPTY_FIELDS, onHiddenFieldsChange, onRowClick, onLazyLoad, onCellEdit, cells, cellEditors, renderRowDetail, paginatorActions, locale, dictionary, className, style, ...rest }) {
51
86
  const { t, locale: activeLocale } = useKptMessages({ locale, dictionary });
52
87
  const uid = useId();
53
88
  const rowsClickable = clickableRows ?? onRowClick !== undefined;
@@ -71,13 +106,34 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
71
106
  onChange: onHiddenFieldsChange,
72
107
  });
73
108
  const [filterText, setFilterText] = useState('');
109
+ /** Treść pola wyszukiwania - przy dużych zbiorach wyprzedza `filterText` o czas debounce. */
110
+ const [filterInput, setFilterInput] = useState('');
111
+ // Filtr głosem: pole czyści się po kliknięciu, a fraza wchodzi przez `onChange` jak wpisana -
112
+ // z tym samym opóźnieniem dla dużych danych. Bez `filterSpeech` kontroler nie tworzy sesji.
113
+ const filterRef = useRef(null);
114
+ const filterMic = useKptSpeechRecognition({
115
+ enabled: filterable && filterSpeech,
116
+ target: filterRef,
117
+ lang: filterSpeechLang,
118
+ mode: 'replace',
119
+ continuous: false,
120
+ });
121
+ const filterSpeechState = filterable && filterSpeech && filterMic.available ? (filterMic.listening ? 'listening' : 'idle') : undefined;
122
+ const filterTimer = useRef(undefined);
74
123
  const [columnFilters, setColumnFilters] = useState({});
75
124
  const [sortField, setSortField] = useState(null);
76
125
  const [sortDir, setSortDir] = useState(null);
77
126
  const [pageIndex, setPageIndex] = useState(0);
78
127
  const [pageSizeState, setPageSizeState] = useState(pageSize || 10);
128
+ // Nowy `pageSize` od rodzica zastępuje rozmiar wybrany w paginatorze - jak `linkedSignal` w Angularze.
129
+ const [seenPageSize, setSeenPageSize] = useState(pageSize);
130
+ if (seenPageSize !== pageSize) {
131
+ setSeenPageSize(pageSize);
132
+ setPageSizeState(pageSize || 10);
133
+ setPageIndex(0);
134
+ }
79
135
  // Kolejność kolumn żyje w stanie (przestawianie), ale nowe `columns` z zewnątrz ją zastępują.
80
- // Poprzednie `columns` trzymamy w stanie, nie w refie — wzorzec „zmiana propsa resetuje stan”
136
+ // Poprzednie `columns` trzymamy w stanie, nie w refie - wzorzec "zmiana propsa resetuje stan"
81
137
  // z react.dev; ref zależałby od tego, czy React nie wyrzucił renderu (StrictMode, concurrent).
82
138
  const [order, setOrder] = useState(columns);
83
139
  const [seenColumns, setSeenColumns] = useState(columns);
@@ -88,8 +144,10 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
88
144
  /** Zmierzone szerokości kolumn zamrożonych bez szerokości liczalnej z jednostki (`%`, `ch`). */
89
145
  const [measuredWidths, setMeasuredWidths] = useState(() => new Map());
90
146
  const [dragField, setDragField] = useState(null);
91
- const [scrollTop, setScrollTop] = useState(0);
92
- const [viewportHeight, setViewportHeight] = useState(400);
147
+ /** Przewinięcie zapisane wtedy, gdy zmienia okno wierszy albo kolumn - nie przy każdym pikselu. */
148
+ const [scroll, setScroll] = useState({ top: 0, left: 0 });
149
+ const [viewport, setViewport] = useState({ height: 400, width: 1200 });
150
+ const scrollFrame = useRef(0);
93
151
  const [theadOffset, setTheadOffset] = useState(0);
94
152
  const scrollRef = useRef(null);
95
153
  const theadRef = useRef(null);
@@ -101,13 +159,19 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
101
159
  const hasColumnFilters = columns.some((col) => col.filter);
102
160
  const hasRowDetail = expandable && !!renderRowDetail;
103
161
  const sameRow = (a, b) => rowKey ? getValue(a, rowKey) === getValue(b, rowKey) : a === b;
104
- // ── Potok danych ──
162
+ /** Tożsamość wiersza: wartość `rowKey` albo sam wiersz - klucz zbiorów zaznaczenia i rozwinięcia. */
163
+ const keyOf = (row) => (rowKey ? getValue(row, rowKey) : row);
164
+ // -- Potok danych --
165
+ /**
166
+ * Filtr globalny z pamięcią złożonego tekstu wiersza (`@konce-pt/table`) - nowy przy zmianie
167
+ * kolumn, więc kolejne znaki frazy nie składają od nowa 20 000 x 100 komórek.
168
+ */
169
+ const globalFilter = useMemo(() => createGlobalFilter(columns.map((col) => col.field)), [columns]);
105
170
  const filtered = useMemo(() => {
106
- const fields = columns.map((col) => col.field);
107
171
  let rows = data;
108
172
  if (filterText.trim()) {
109
- // Bez diakrytyków i wielkości liter — „lodz” znajduje „Łódź” (`@konce-pt/table`).
110
- rows = rows.filter((row) => matchesGlobalFilter(row, fields, filterText));
173
+ // Bez diakrytyków i wielkości liter - "lodz" znajduje "Łódź" (`@konce-pt/table`).
174
+ rows = rows.filter((row) => globalFilter(row, filterText));
111
175
  }
112
176
  if (hasColumnFilters) {
113
177
  rows = rows.filter((row) => columns.every((col) => {
@@ -120,14 +184,38 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
120
184
  }));
121
185
  }
122
186
  return rows;
123
- }, [columnFilters, columns, data, filterText, hasColumnFilters]);
187
+ }, [columnFilters, columns, data, filterText, globalFilter, hasColumnFilters]);
124
188
  const sorted = useMemo(() => {
125
189
  if (!sortField || !sortDir)
126
190
  return filtered;
127
- // Kolator dla aktywnego języka, tworzony raz na sortowanie; puste komórki na końcu.
128
- const compare = createRowComparator(sortField, sortDir, activeLocale);
129
- return [...filtered].sort((a, b) => compare(a, b));
191
+ // Kolator dla aktywnego języka, klucze liczone raz na wiersz; puste komórki na końcu.
192
+ return sortRows(filtered, sortField, sortDir, activeLocale);
130
193
  }, [activeLocale, filtered, sortDir, sortField]);
194
+ // Duży zbiór z wyszukiwaniem: tekst wierszy składamy w tle, kawałkami po kilka ms w czasie
195
+ // bezczynności - inaczej pierwszy znak frazy blokowałby stronę na cały zbiór naraz.
196
+ useEffect(() => {
197
+ if (!filterable || lazy)
198
+ return;
199
+ const rows = data;
200
+ if (rows.length <= FILTER_DEBOUNCE_ROWS)
201
+ return;
202
+ let index = 0;
203
+ let handle = null;
204
+ const step = () => {
205
+ const until = performance.now() + WARMUP_SLICE_MS;
206
+ while (index < rows.length && performance.now() < until)
207
+ globalFilter.prepare(rows[index++]);
208
+ if (index < rows.length)
209
+ handle = scheduleIdle(step);
210
+ };
211
+ handle = scheduleIdle(step);
212
+ return () => cancelIdle(handle);
213
+ }, [data, filterable, globalFilter, lazy]);
214
+ // Odmontowanie: porzucona klatka odczytu przewinięcia i czekający debounce filtra.
215
+ useEffect(() => () => {
216
+ cancelAnimationFrame(scrollFrame.current);
217
+ clearTimeout(filterTimer.current);
218
+ }, []);
131
219
  const paged = useMemo(() => {
132
220
  if (lazy)
133
221
  return data;
@@ -149,22 +237,27 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
149
237
  // Offset sticky drugiego wiersza nagłówka = wysokość pierwszego.
150
238
  useLayoutEffect(() => {
151
239
  const first = theadRef.current?.rows?.[0];
240
+ // Dokładna wysokość, nie `offsetHeight`: ten zaokrągla (46,5 -> 47) i przy skali ekranu innej
241
+ // niż 100% między wierszami nagłówka zostawała szpara, przez którą prześwitywały wiersze.
152
242
  if (first)
153
- setTheadOffset(first.offsetHeight);
243
+ setTheadOffset(first.getBoundingClientRect().height);
154
244
  }, [filterRowPosition, hasColumnFilters, visibleColumns]);
155
- // Wysokość viewportu dla wirtualizacji — także po zmianie rozmiaru kontenera bez przewinięcia.
245
+ // Rozmiar viewportu dla wirtualizacji - także po zmianie rozmiaru kontenera bez przewinięcia.
156
246
  useLayoutEffect(() => {
157
247
  const el = scrollRef.current;
158
248
  if (!el)
159
249
  return;
160
- setViewportHeight(el.clientHeight);
250
+ const measure = () => setViewport((prev) => prev.height === el.clientHeight && prev.width === el.clientWidth
251
+ ? prev
252
+ : { height: el.clientHeight, width: el.clientWidth });
253
+ measure();
161
254
  if (typeof ResizeObserver === 'undefined')
162
255
  return;
163
- const observer = new ResizeObserver(() => setViewportHeight(el.clientHeight));
256
+ const observer = new ResizeObserver(measure);
164
257
  observer.observe(el);
165
258
  return () => observer.disconnect();
166
259
  }, []);
167
- // Szerokości kolumn zamrożonych, których nie da się policzyć z jednostki — mierzone z nagłówków.
260
+ // Szerokości kolumn zamrożonych, których nie da się policzyć z jednostki - mierzone z nagłówków.
168
261
  // Stan zmienia się tylko przy innej wartości, więc pomiar nie zapętla renderu.
169
262
  useLayoutEffect(() => {
170
263
  const head = theadRef.current;
@@ -182,7 +275,7 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
182
275
  }
183
276
  setMeasuredWidths((prev) => prev.size === next.size && [...next].every(([key, value]) => prev.get(key) === value) ? prev : next);
184
277
  }, [visibleColumns]);
185
- // Po przestawieniu kolumny z klawiatury uchwyt jest przerysowany w nowym miejscu — fokus idzie za nim.
278
+ // Po przestawieniu kolumny z klawiatury uchwyt jest przerysowany w nowym miejscu - fokus idzie za nim.
186
279
  useLayoutEffect(() => {
187
280
  const field = focusGrip.current;
188
281
  if (field === null)
@@ -192,28 +285,17 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
192
285
  ?.querySelector(`th[data-field="${CSS.escape(field)}"] .kpt-data-table__grip`)
193
286
  ?.focus();
194
287
  }, [order]);
195
- // Wejście w edycję → fokus na kontrolce edytora.
288
+ // Wejście w edycję -> fokus na kontrolce edytora.
196
289
  useEffect(() => {
197
290
  if (!editingCell)
198
291
  return;
199
292
  editorHostRef.current?.querySelector('input, .kpt-select, [tabindex]')?.focus();
200
293
  }, [editingCell]);
201
- // ── Wirtualizacja ──
202
- const virtualStart = Math.max(0, Math.floor(scrollTop / rowHeight) - VIRTUAL_BUFFER);
203
- const virtualEnd = Math.min(sorted.length, virtualStart + Math.ceil(viewportHeight / rowHeight) + VIRTUAL_BUFFER * 2);
204
- const virtualRows = sorted.slice(virtualStart, virtualEnd);
205
- const topPad = virtualStart * rowHeight;
206
- const bottomPad = (sorted.length - virtualEnd) * rowHeight;
207
- const onScroll = (event) => {
208
- if (!virtualScroll)
209
- return;
210
- const el = event.currentTarget;
211
- setScrollTop(el.scrollTop);
212
- setViewportHeight(el.clientHeight);
213
- };
214
- // ── Kolumny zamrożone ──
294
+ // -- Kolumny zamrożone --
215
295
  const leadingCells = (selectable !== 'none' ? 1 : 0) + (hasRowDetail ? 1 : 0);
216
296
  const leadingStickyWidth = (hasRowDetail ? EXPANDER_FROZEN_W * REM_PX : 0) + (selectable !== 'none' ? CHECKBOX_FROZEN_W * REM_PX : 0);
297
+ /** `columnWidth` w px; nieliczalna jednostka wraca do 10 rem. */
298
+ const defaultColumnPx = cssLengthToPx(columnWidth, REM_PX) ?? 10 * REM_PX;
217
299
  const frozenOffsets = useMemo(() => {
218
300
  const map = new Map();
219
301
  let acc = leadingStickyWidth;
@@ -221,16 +303,108 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
221
303
  if (!col.frozen)
222
304
  break; // tylko wiodące, sąsiadujące
223
305
  map.set(col.field, acc);
224
- // Z jednostki, potem z pomiaru, a do pierwszego pomiaru 8 rem.
225
- acc += cssLengthToPx(col.width, REM_PX) ?? measuredWidths.get(col.field) ?? 8 * REM_PX;
306
+ acc += columnPx(col, virtualScroll, defaultColumnPx, measuredWidths);
226
307
  }
227
308
  return map;
228
- }, [leadingStickyWidth, measuredWidths, visibleColumns]);
229
- const hasFrozen = frozenOffsets.size > 0;
230
- const frozenLeft = (field) => frozenOffsets.get(field);
309
+ }, [defaultColumnPx, leadingStickyWidth, measuredWidths, virtualScroll, visibleColumns]);
310
+ /** Czy cokolwiek jest przyklejone do lewej - kolumny `frozen` albo same systemowe. */
311
+ const hasFrozen = frozenOffsets.size > 0 || (frozenSystemColumns && leadingCells > 0);
231
312
  const expanderStickyLeft = hasFrozen ? 0 : undefined;
232
313
  const checkboxStickyLeft = hasFrozen ? (hasRowDetail ? EXPANDER_FROZEN_W * REM_PX : 0) : undefined;
233
- // ── Przestawianie kolumn ──
314
+ // -- Kolumny do renderowania (metadane + okno wirtualizacji) --
315
+ /** Widoczne kolumny z cechami policzonymi raz - nie przy każdej komórce każdego wiersza. */
316
+ const columnMeta = useMemo(() => visibleColumns.map((col, index) => ({
317
+ col,
318
+ index,
319
+ editable: !!col.edit || !!cellEditors?.[col.field],
320
+ frozen: frozenOffsets.has(col.field),
321
+ left: frozenOffsets.get(col.field),
322
+ align: col.align ?? 'start',
323
+ width: columnPx(col, virtualScroll, defaultColumnPx, measuredWidths),
324
+ })), [cellEditors, defaultColumnPx, frozenOffsets, measuredWidths, virtualScroll, visibleColumns]);
325
+ /** Kolumny przewijalne (za wiodącymi zamrożonymi) i ich sumy prefiksowe szerokości. */
326
+ const scrollable = useMemo(() => {
327
+ const pinned = columnMeta.filter((m) => m.frozen);
328
+ const rest = columnMeta.slice(pinned.length);
329
+ // Kolumny systemowe zasłaniają lewą krawędź okna tylko wtedy, gdy są przyklejone.
330
+ const leading = hasFrozen ? leadingStickyWidth : 0;
331
+ const pinnedWidth = leading + pinned.reduce((sum, m) => sum + m.width, 0);
332
+ return { pinned, rest, pinnedWidth, offsets: columnOffsets(rest.map((m) => m.width)) };
333
+ }, [columnMeta, hasFrozen, leadingStickyWidth]);
334
+ // -- Wirtualizacja --
335
+ const rowWindowAt = (top) => virtualRange({
336
+ offset: top,
337
+ viewport: viewport.height,
338
+ itemSize: rowHeight,
339
+ count: sorted.length,
340
+ overscan: ROW_OVERSCAN,
341
+ });
342
+ const columnWindowAt = (left) => virtualScroll
343
+ ? virtualColumnRange({
344
+ offsets: scrollable.offsets,
345
+ scrollLeft: left,
346
+ viewport: viewport.width,
347
+ overscan: COLUMN_OVERSCAN,
348
+ pinnedWidth: scrollable.pinnedWidth,
349
+ })
350
+ : { start: 0, end: scrollable.rest.length, before: 0, after: 0 };
351
+ const rowWindow = rowWindowAt(scroll.top);
352
+ const columnWindow = columnWindowAt(scroll.left);
353
+ const virtualRows = sorted.slice(rowWindow.start, rowWindow.end);
354
+ const topPad = rowWindow.before;
355
+ const bottomPad = rowWindow.after;
356
+ // Odczyt przewinięcia w `requestAnimationFrame` porównuje okna z tymi z ostatniego renderu -
357
+ // obie strony muszą widzieć bieżące dane, więc odświeżamy je po każdym renderze.
358
+ const windowsAt = useRef(null);
359
+ const shownWindows = useRef(null);
360
+ useLayoutEffect(() => {
361
+ windowsAt.current = (top, left) => ({ row: rowWindowAt(top), col: columnWindowAt(left) });
362
+ shownWindows.current = { row: rowWindow, col: columnWindow };
363
+ });
364
+ /** Jedna klatka na odczyt przewinięcia, a render tylko wtedy, gdy zmienia się okno wierszy lub kolumn. */
365
+ const onScroll = (event) => {
366
+ if (!virtualScroll || scrollFrame.current)
367
+ return;
368
+ const el = event.currentTarget;
369
+ scrollFrame.current = requestAnimationFrame(() => {
370
+ scrollFrame.current = 0;
371
+ const top = el.scrollTop;
372
+ const left = el.scrollLeft;
373
+ const next = windowsAt.current?.(top, left);
374
+ const shown = shownWindows.current;
375
+ if (next && shown && sameWindow(next.row, shown.row) && sameWindow(next.col, shown.col))
376
+ return;
377
+ setScroll({ top, left });
378
+ });
379
+ };
380
+ /** Nowy wynik filtra lub sortowania zaczyna się od góry - stare przewinięcie wskazywałoby w środek innego zbioru. */
381
+ const scrollToTop = () => {
382
+ const el = scrollRef.current;
383
+ if (virtualScroll && el)
384
+ el.scrollTop = 0;
385
+ };
386
+ /** Pozycje wiersza: kolumny zamrożone, dystansownik, okno kolumn, dystansownik. */
387
+ const slots = scrollable.pinned.map((m) => ({ key: m.col.field, meta: m, width: m.width, upTo: m.index }));
388
+ {
389
+ const { rest } = scrollable;
390
+ const { start, end, before, after } = columnWindow;
391
+ const firstIndex = rest[start]?.index ?? scrollable.pinned.length;
392
+ if (before > 0)
393
+ slots.push({ key: '\u0000before', meta: null, width: before, upTo: firstIndex });
394
+ for (let i = start; i < end; i++)
395
+ slots.push({ key: rest[i].col.field, meta: rest[i], width: rest[i].width, upTo: rest[i].index });
396
+ if (after > 0)
397
+ slots.push({ key: '\u0000after', meta: null, width: after, upTo: columnMeta.length });
398
+ }
399
+ /** Pełna szerokość tabeli w trybie wirtualnym (`table-layout: fixed`). */
400
+ const tableWidth = virtualScroll
401
+ ? scrollable.pinnedWidth + scrollable.offsets[scrollable.offsets.length - 1]
402
+ : undefined;
403
+ /** Liczba wierszy nagłówka (etykiety + ewentualnie filtry) - przesunięcie `aria-rowindex`. */
404
+ const headerRowCount = hasColumnFilters ? 2 : 1;
405
+ /** `aria-colindex` komórki kolumny (od 1, z kolumnami systemowymi) - tylko w trybie wirtualnym. */
406
+ const colIndex = (meta) => virtualScroll ? leadingCells + meta.index + 1 : undefined;
407
+ // -- Przestawianie kolumn --
234
408
  const frozenLeadCount = (() => {
235
409
  let n = 0;
236
410
  for (const col of visibleColumns) {
@@ -259,7 +433,7 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
259
433
  setOrder(cols);
260
434
  setDragField(null);
261
435
  };
262
- /** Strzałki na uchwycie przestawiają kolumnę o jedno miejsce — alternatywa dla przeciągania. */
436
+ /** Strzałki na uchwycie przestawiają kolumnę o jedno miejsce - alternatywa dla przeciągania. */
263
437
  const onGripKeyDown = (field, event) => {
264
438
  const delta = event.key === 'ArrowLeft' ? -1 : event.key === 'ArrowRight' ? 1 : 0;
265
439
  if (!delta)
@@ -267,7 +441,7 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
267
441
  event.preventDefault();
268
442
  const from = visibleColumns.findIndex((col) => col.field === field);
269
443
  const to = from + delta;
270
- // Kolumny zamrożone zostają wiodące — nie wolno ich minąć ani wyjechać poza tabelę.
444
+ // Kolumny zamrożone zostają wiodące - nie wolno ich minąć ani wyjechać poza tabelę.
271
445
  if (from === -1 || to < frozenLeadCount || to >= visibleColumns.length)
272
446
  return;
273
447
  const cols = [...order];
@@ -279,10 +453,11 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
279
453
  setMoveAnnouncement(t('dataTable.columnMoved', { column: moved.header, position: to + 1, count: visibleColumns.length }));
280
454
  focusGrip.current = field;
281
455
  };
282
- // ── Sortowanie ──
456
+ // -- Sortowanie --
283
457
  const toggleSort = (col) => {
284
458
  if (!col.sortable)
285
459
  return;
460
+ scrollToTop();
286
461
  if (sortField !== col.field) {
287
462
  setSortField(col.field);
288
463
  setSortDir('asc');
@@ -302,10 +477,26 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
302
477
  return 'none';
303
478
  return sortDir === 'asc' ? 'ascending' : 'descending';
304
479
  };
305
- // ── Filtry kolumn ──
480
+ // -- Filtry kolumn --
306
481
  const setColumnFilter = (field, value) => {
307
482
  setColumnFilters((current) => ({ ...current, [field]: value }));
308
483
  setPageIndex(0);
484
+ scrollToTop();
485
+ };
486
+ // -- Filtr globalny --
487
+ const applyFilter = (value) => {
488
+ setFilterText(value);
489
+ setPageIndex(0);
490
+ scrollToTop();
491
+ };
492
+ /** Przy dużych zbiorach filtr czeka na przerwę w pisaniu; pole pokazuje tekst od razu. */
493
+ const onFilterInput = (value) => {
494
+ setFilterInput(value);
495
+ clearTimeout(filterTimer.current);
496
+ if (data.length > FILTER_DEBOUNCE_ROWS)
497
+ filterTimer.current = setTimeout(() => applyFilter(value), FILTER_DEBOUNCE_MS);
498
+ else
499
+ applyFilter(value);
309
500
  };
310
501
  const rangeVal = (field) => {
311
502
  const value = columnFilters[field];
@@ -316,8 +507,10 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
316
507
  const max = col.filter?.max ?? 100;
317
508
  return columnFilters[col.field] ?? [min, max];
318
509
  };
319
- // ── Zaznaczanie ──
320
- const isSelected = (row) => picked.some((r) => sameRow(r, row));
510
+ // -- Zaznaczanie --
511
+ /** Klucze zaznaczonych wierszy - przy "zaznacz wszystko" na 20 000 wierszach `some()` dawałoby O(n^2). */
512
+ const selectedKeys = useMemo(() => new Set(picked.map((r) => (rowKey ? getValue(r, rowKey) : r))), [picked, rowKey]);
513
+ const isSelected = (row) => selectedKeys.has(keyOf(row));
321
514
  const toggleRow = (row) => {
322
515
  if (selectable === 'none')
323
516
  return;
@@ -330,20 +523,24 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
330
523
  const pageAllSelected = paged.length > 0 && paged.every((row) => isSelected(row));
331
524
  const pageSomeSelected = paged.some((row) => isSelected(row)) && !pageAllSelected;
332
525
  const toggleAllOnPage = () => {
333
- if (pageAllSelected)
334
- setPicked(picked.filter((r) => !paged.some((pr) => sameRow(pr, r))));
526
+ if (pageAllSelected) {
527
+ const pageKeys = new Set(paged.map(keyOf));
528
+ setPicked(picked.filter((r) => !pageKeys.has(keyOf(r))));
529
+ }
335
530
  else
336
531
  setPicked([...picked, ...paged.filter((row) => !isSelected(row))]);
337
532
  };
338
- // ── Rozwijane wiersze ──
339
- const isExpanded = (row) => expanded.some((r) => sameRow(r, row));
533
+ // -- Rozwijane wiersze --
534
+ /** Klucze rozwiniętych wierszy - `isExpanded` w O(1) zamiast przeszukiwania listy dla każdego wiersza. */
535
+ const expandedKeys = useMemo(() => new Set(expanded.map((r) => (rowKey ? getValue(r, rowKey) : r))), [expanded, rowKey]);
536
+ const isExpanded = (row) => expandedKeys.has(keyOf(row));
340
537
  const toggleExpand = (row) => {
341
538
  if (isExpanded(row))
342
539
  setExpanded(expanded.filter((r) => !sameRow(r, row)));
343
540
  else
344
541
  setExpanded(multiExpand ? [...expanded, row] : [row]);
345
542
  };
346
- // ── Edycja inline ──
543
+ // -- Edycja inline --
347
544
  const isEditable = (col) => !!col.edit || !!cellEditors?.[col.field];
348
545
  const isEditingCell = (row, field) => !!editingCell && editingCell.field === field && sameRow(editingCell.row, row);
349
546
  const startEdit = (row, col, event) => {
@@ -373,7 +570,7 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
373
570
  /**
374
571
  * Edytory bez panelu (text/number, własne) zatwierdza dopiero wyjście fokusu poza CAŁY edytor:
375
572
  * `KptInputNumber` ma fokusowalne strzałki obok pola, więc reakcja na blur samej kontrolki
376
- * zamykałaby edycję przy każdym kliknięciu w „+".
573
+ * zamykałaby edycję przy każdym kliknięciu w "+".
377
574
  */
378
575
  const onEditorFocusOut = (event) => {
379
576
  if (editorPanelOpen)
@@ -389,7 +586,7 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
389
586
  if (!open)
390
587
  commitEdit();
391
588
  };
392
- // ── Wiersz podsumowania ──
589
+ // -- Wiersz podsumowania --
393
590
  const summarySource = lazy ? EMPTY_ROWS : filtered;
394
591
  const overrides = summaryValues ?? {};
395
592
  const summaryFields = visibleColumns
@@ -397,22 +594,61 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
397
594
  .map((col) => col.field);
398
595
  const hasSummary = summaryFields.length > 0;
399
596
  const summaryStartIndex = summaryFields[0] === undefined ? -1 : visibleColumns.findIndex((col) => col.field === summaryFields[0]);
400
- const summaryLabelSpan = leadingCells + summaryStartIndex;
401
- const formatSummary = (raw, format) => format ? format(raw) : toText(raw);
402
- const summaryText = (col) => {
403
- if (col.field in overrides)
404
- return formatSummary(overrides[col.field], col.summary?.format);
405
- if (!col.summary)
406
- return '';
407
- const raw = typeof col.summary.fn === 'function'
408
- ? col.summary.fn(summarySource)
409
- : aggregate(summarySource, col.field, col.summary.fn);
410
- return formatSummary(raw, col.summary.format);
411
- };
412
- const colspan = visibleColumns.length + leadingCells;
597
+ /**
598
+ * Komórki wiersza podsumowania: etykieta scala kolumny systemowe, kolumny przed pierwszą
599
+ * podsumowywaną i dystansownik, o ile ten też stoi przed nią; reszta jak w `slots`.
600
+ * Przy kolumnach zamrożonych etykieta kończy się na ich granicy - jest przyklejona do lewej,
601
+ * więc scalona z kolumnami przewijanymi zasłaniałaby je przy przewijaniu w poziomie, a jej
602
+ * szerokość skakałaby przy każdym przestawieniu kolumny. Kolumny za granicą dostają puste komórki.
603
+ * Gdy przyklejone są same kolumny systemowe (`frozenSystemColumns`), są za wąskie na etykietę:
604
+ * dostają pustą przyklejoną komórkę (`systemSpan`), a etykieta staje za nimi, nad kolumnami
605
+ * przed pierwszą liczoną, i przewija się razem z nimi.
606
+ * `labelSpan` równe zero (pierwsza widoczna kolumna jest liczona) = etykieta nie ma się gdzie
607
+ * zmieścić i nie renderuje się wcale.
608
+ */
609
+ const summaryLayout = (() => {
610
+ const isBeforeStart = (slot) => slot.meta ? slot.meta.index < summaryStartIndex : slot.upTo <= summaryStartIndex;
611
+ let frozenSpan = 0;
612
+ const restSlots = [];
613
+ for (const slot of slots) {
614
+ const fitsLabel = !hasFrozen || slot.meta?.frozen === true;
615
+ if (restSlots.length === 0 && isBeforeStart(slot) && fitsLabel)
616
+ frozenSpan++;
617
+ else
618
+ restSlots.push(slot);
619
+ }
620
+ if (hasFrozen && frozenSpan === 0 && leadingCells > 0) {
621
+ let free = 0;
622
+ while (free < restSlots.length && isBeforeStart(restSlots[free]))
623
+ free++;
624
+ if (free > 0)
625
+ return { systemSpan: leadingCells, labelSpan: free, labelSticky: false, slots: restSlots.slice(free) };
626
+ }
627
+ return { systemSpan: 0, labelSpan: leadingCells + frozenSpan, labelSticky: hasFrozen, slots: restSlots };
628
+ })();
629
+ /** Gotowe teksty komórek podsumowania (pole -> tekst) - agregacja całego zbioru raz, nie przy każdym renderze. */
630
+ const summaryTexts = useMemo(() => {
631
+ const values = summaryValues ?? {};
632
+ const format = (raw, fmt) => (fmt ? fmt(raw) : toText(raw));
633
+ const map = new Map();
634
+ for (const col of visibleColumns) {
635
+ if (col.field in values)
636
+ map.set(col.field, format(values[col.field], col.summary?.format));
637
+ else if (col.summary) {
638
+ const raw = typeof col.summary.fn === 'function'
639
+ ? col.summary.fn(summarySource)
640
+ : aggregate(summarySource, col.field, col.summary.fn);
641
+ map.set(col.field, format(raw, col.summary.format));
642
+ }
643
+ }
644
+ return map;
645
+ }, [summarySource, summaryValues, visibleColumns]);
646
+ /** Komórki faktycznie renderowane w wierszu (w trybie wirtualnym okno kolumn + dystansowniki). */
647
+ const colspan = slots.length + leadingCells;
413
648
  const displayCount = virtualScroll ? sorted.length : paged.length;
414
649
  const rowsToRender = virtualScroll ? virtualRows : paged;
415
- // ── Renderowanie ──
650
+ // -- Renderowanie --
651
+ const hspacer = (slot) => (_jsx("td", { className: "kpt-data-table__hspacer", "aria-hidden": "true" }, slot.key));
416
652
  const filterCell = (col) => {
417
653
  const filter = col.filter;
418
654
  if (!filter)
@@ -435,7 +671,8 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
435
671
  return null;
436
672
  }
437
673
  };
438
- const filterRow = (_jsxs("tr", { className: "kpt-data-table__filter-row", children: [hasRowDetail ? (_jsx("th", { className: cn('kpt-data-table__expander', hasFrozen && 'kpt-data-table__cell--frozen'), style: { left: expanderStickyLeft } })) : null, selectable !== 'none' ? (_jsx("th", { className: cn('kpt-data-table__checkbox', hasFrozen && 'kpt-data-table__cell--frozen'), style: { left: checkboxStickyLeft } })) : null, visibleColumns.map((col) => (_jsx("th", { style: { width: col.width, left: frozenLeft(col.field) }, "data-align": col.align ?? 'start', className: cn(frozenOffsets.has(col.field) && 'kpt-data-table__cell--frozen'), children: filterCell(col) }, col.field)))] }));
674
+ const filterRowIndex = filterRowPosition === 'top' ? 1 : 2;
675
+ const filterRow = (_jsxs("tr", { className: "kpt-data-table__filter-row", "aria-rowindex": virtualScroll ? filterRowIndex : undefined, children: [hasRowDetail ? (_jsx("th", { className: cn('kpt-data-table__expander', hasFrozen && 'kpt-data-table__cell--frozen'), style: { left: expanderStickyLeft } })) : null, selectable !== 'none' ? (_jsx("th", { className: cn('kpt-data-table__checkbox', hasFrozen && 'kpt-data-table__cell--frozen'), style: { left: checkboxStickyLeft } })) : null, slots.map((slot) => slot.meta ? (_jsx("th", { style: { width: virtualScroll ? undefined : slot.meta.col.width, left: slot.meta.left }, "data-align": slot.meta.align, "aria-colindex": colIndex(slot.meta), className: cn(slot.meta.frozen && 'kpt-data-table__cell--frozen'), children: filterCell(slot.meta.col) }, slot.key)) : (hspacer(slot)))] }));
439
676
  const editor = (row, col) => {
440
677
  const custom = cellEditors?.[col.field];
441
678
  if (custom) {
@@ -474,9 +711,9 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
474
711
  return null;
475
712
  }
476
713
  };
477
- const renderRow = (row, index) => (_jsxs("tbody", { style: { display: 'contents' }, children: [_jsxs("tr", { className: cn(isSelected(row) && 'kpt-data-table__row--selected', hasRowDetail && isExpanded(row) && 'kpt-data-table__row--expanded', rowsClickable && 'kpt-data-table__row--clickable'), style: { height: virtualScroll ? rowHeight : undefined }, tabIndex: rowsClickable ? 0 : undefined, onClick: () => onRowClick?.(row), onKeyDown: rowsClickable
714
+ const renderRow = (row, index, ariaRowIndex) => (_jsxs("tbody", { style: { display: 'contents' }, children: [_jsxs("tr", { "aria-rowindex": ariaRowIndex, className: cn(isSelected(row) && 'kpt-data-table__row--selected', hasRowDetail && isExpanded(row) && 'kpt-data-table__row--expanded', rowsClickable && 'kpt-data-table__row--clickable'), style: { height: virtualScroll ? rowHeight : undefined }, tabIndex: rowsClickable ? 0 : undefined, onClick: () => onRowClick?.(row), onKeyDown: rowsClickable
478
715
  ? (event) => {
479
- // Tylko na samym wierszu — Enter w kontrolce komórki należy do niej.
716
+ // Tylko na samym wierszu - Enter w kontrolce komórki należy do niej.
480
717
  if (event.target !== event.currentTarget || (event.key !== 'Enter' && event.key !== ' '))
481
718
  return;
482
719
  event.preventDefault();
@@ -490,34 +727,42 @@ export function KptDataTable({ columns, data = EMPTY_ROWS, filterable = false, f
490
727
  toggleRow(row);
491
728
  }, onChange: () => {
492
729
  // Zmianę obsługuje `onClick` (radio nie zgłasza `change` przy odznaczaniu).
493
- } }) })) : null, visibleColumns.map((col, colIndex) => {
494
- const editing = isEditingCell(row, col.field);
495
- const editable = isEditable(col) && !editing;
496
- return (_jsx("td", { "data-align": col.align ?? 'start', style: { left: frozenLeft(col.field) }, className: cn(frozenOffsets.has(col.field) && 'kpt-data-table__cell--frozen', editable && 'kpt-data-table__cell--editable'), tabIndex: editable ? 0 : undefined, "aria-describedby": editable ? `${uid}-edit-${colIndex}` : undefined, onClick: (event) => {
497
- if (isEditable(col))
730
+ } }) })) : null, slots.map((slot) => {
731
+ const m = slot.meta;
732
+ if (!m)
733
+ return hspacer(slot);
734
+ const col = m.col;
735
+ // Edytor sprawdzamy tylko, gdy jakakolwiek komórka jest w edycji - reszta to odczyt gotowych cech kolumny.
736
+ const editing = m.editable && editingCell !== null && isEditingCell(row, col.field);
737
+ const editable = m.editable && !editing;
738
+ return (_jsx("td", { "data-align": m.align, style: { left: m.left }, "aria-colindex": colIndex(m), className: cn(m.frozen && 'kpt-data-table__cell--frozen', editable && 'kpt-data-table__cell--editable'), tabIndex: editable ? 0 : undefined, "aria-describedby": editable ? `${uid}-edit-${m.index}` : undefined, onClick: (event) => {
739
+ if (m.editable)
498
740
  startEdit(row, col, event);
499
741
  }, onKeyDown: (event) => {
500
742
  if (event.key === 'Enter' && editable)
501
743
  startEdit(row, col, event);
502
- }, children: editing ? (_jsx("div", { ref: editorHostRef, className: "kpt-data-table__editor", onClick: (event) => event.stopPropagation(), onBlur: onEditorFocusOut, children: editor(row, col) })) : cells?.[col.field] ? (cells[col.field](row, getValue(row, col.field))) : getValue(row, col.field) }, col.field));
744
+ }, children: editing ? (_jsx("div", { ref: editorHostRef, className: "kpt-data-table__editor", onClick: (event) => event.stopPropagation(), onBlur: onEditorFocusOut, children: editor(row, col) })) : cells?.[col.field] ? (cells[col.field](row, getValue(row, col.field))) : getValue(row, col.field) }, slot.key));
503
745
  })] }), hasRowDetail && isExpanded(row) && !virtualScroll ? (_jsx("tr", { className: "kpt-data-table__detail-row", children: _jsx("td", { className: "kpt-data-table__detail", colSpan: colspan, children: renderRowDetail?.(row) }) })) : null] }, rowKey ? String(getValue(row, rowKey)) : index));
504
- return (_jsxs("div", { className: cn('kpt-data-table', className), style: { ['--kpt-thead-offset']: `${theadOffset}px`, ...style }, ...rest, children: [filterable || exportable ? (_jsxs("div", { className: "kpt-data-table__toolbar", children: [filterable ? (_jsx("input", { className: "kpt-data-table__filter", type: "search", placeholder: t('dataTable.search'), "aria-label": t('dataTable.search'), value: filterText, onChange: (event) => {
505
- setFilterText(event.target.value);
506
- setPageIndex(0);
507
- } })) : null, exportable ? (_jsx(KptButton, { variant: "outline", size: "sm", onClick: () => exportTableCsv(visibleColumns, (lazy ? data : sorted), exportFilename), children: t('dataTable.exportCsv') })) : null] })) : null, _jsx("div", { ref: scrollRef, className: "kpt-data-table__scroll", style: { maxHeight: scrollHeight ?? undefined }, onScroll: onScroll, children: _jsxs("table", { className: cn('kpt-data-table__table', hasFrozen && 'kpt-data-table__table--frozen', virtualScroll && 'kpt-data-table__table--virtual'), "aria-label": ariaLabel ?? undefined, "aria-busy": loading || undefined, children: [caption ? _jsx("caption", { className: "kpt-data-table__caption", children: caption }) : null, _jsxs("thead", { ref: theadRef, children: [hasColumnFilters && filterRowPosition === 'top' ? filterRow : null, _jsxs("tr", { children: [hasRowDetail ? (_jsx("th", { className: cn('kpt-data-table__expander', hasFrozen && 'kpt-data-table__cell--frozen'), style: { left: expanderStickyLeft } })) : null, selectable === 'multiple' ? (_jsx("th", { className: cn('kpt-data-table__checkbox', hasFrozen && 'kpt-data-table__cell--frozen'), style: { left: checkboxStickyLeft }, children: _jsx("input", { type: "checkbox", checked: pageAllSelected, ref: (el) => {
746
+ return (_jsxs("div", { className: cn('kpt-data-table', className), style: { ['--kpt-thead-offset']: `${theadOffset}px`, ...style }, ...rest, children: [filterable || exportable ? (_jsxs("div", { className: "kpt-data-table__toolbar", children: [filterable ? (_jsxs("div", { className: "kpt-data-table__search", "data-speech": filterSpeechState, children: [_jsx("input", { ref: filterRef, className: "kpt-data-table__filter", type: "search", placeholder: t('dataTable.search'), "aria-label": t('dataTable.search'), value: filterInput, onChange: (event) => onFilterInput(event.target.value) }), filterSpeech ? (_jsx(KptSpeechButton, { listeningColor: filterSpeechColor, speech: filterMic, size: "sm", unsupported: "hide", hostProps: { className: 'kpt-data-table__speech' } })) : null] })) : null, exportable ? (_jsx(KptButton, { variant: "outline", size: "sm", onClick: () => exportTableCsv(visibleColumns, (lazy ? data : sorted), exportFilename), children: t('dataTable.exportCsv') })) : null] })) : null, _jsx("div", { ref: scrollRef, className: "kpt-data-table__scroll", style: { maxHeight: scrollHeight ?? undefined }, onScroll: onScroll, children: _jsxs("table", { className: cn('kpt-data-table__table', hasFrozen && 'kpt-data-table__table--frozen', virtualScroll && 'kpt-data-table__table--virtual'), style: { width: tableWidth }, "aria-label": ariaLabel ?? undefined, "aria-busy": loading || undefined, "aria-rowcount": virtualScroll ? headerRowCount + sorted.length + (hasSummary ? 1 : 0) : undefined, "aria-colcount": virtualScroll ? leadingCells + visibleColumns.length : undefined, children: [caption ? _jsx("caption", { className: "kpt-data-table__caption", children: _jsx("span", { className: "kpt-data-table__caption-text", children: caption }) }) : null, virtualScroll ? (_jsxs("colgroup", { children: [hasRowDetail ? _jsx("col", { className: "kpt-data-table__col-expander" }) : null, selectable !== 'none' ? _jsx("col", { className: "kpt-data-table__col-checkbox" }) : null, slots.map((slot) => (_jsx("col", { style: { width: slot.width } }, slot.key)))] })) : null, _jsxs("thead", { ref: theadRef, children: [hasColumnFilters && filterRowPosition === 'top' ? filterRow : null, _jsxs("tr", { "aria-rowindex": virtualScroll ? (hasColumnFilters && filterRowPosition === 'top' ? 2 : 1) : undefined, children: [hasRowDetail ? (_jsx("th", { className: cn('kpt-data-table__expander', hasFrozen && 'kpt-data-table__cell--frozen'), style: { left: expanderStickyLeft } })) : null, selectable === 'multiple' ? (_jsx("th", { className: cn('kpt-data-table__checkbox', hasFrozen && 'kpt-data-table__cell--frozen'), style: { left: checkboxStickyLeft }, children: _jsx("input", { type: "checkbox", checked: pageAllSelected, ref: (el) => {
508
747
  if (el)
509
748
  el.indeterminate = pageSomeSelected;
510
- }, "aria-label": t('dataTable.selectAll'), onChange: toggleAllOnPage }) })) : selectable === 'single' ? (_jsx("th", { className: cn('kpt-data-table__checkbox', hasFrozen && 'kpt-data-table__cell--frozen'), style: { left: checkboxStickyLeft } })) : null, visibleColumns.map((col) => (_jsxs("th", { draggable: isColDraggable(col) || undefined, style: { width: col.width, left: frozenLeft(col.field) }, "data-align": col.align ?? 'start', className: cn(frozenOffsets.has(col.field) && 'kpt-data-table__cell--frozen', col.sortable && 'kpt-data-table__th--sortable', isColDraggable(col) && 'kpt-data-table__th--draggable', dragField !== null && dragField !== col.field && !col.frozen && 'kpt-data-table__th--dragover'), "data-field": col.field, "aria-sort": col.sortable ? ariaSort(col) : undefined, onDragStart: (event) => {
511
- if (col.frozen)
512
- return;
513
- setDragField(col.field);
514
- event.dataTransfer.setData('text/plain', col.field);
515
- event.dataTransfer.effectAllowed = 'move';
516
- }, onDragOver: (event) => {
517
- if (!reorderable)
518
- return;
519
- const visIndex = visibleColumns.findIndex((c) => c.field === col.field);
520
- if (dragField !== null && visIndex >= frozenLeadCount)
521
- event.preventDefault();
522
- }, onDrop: (event) => reorderable && onColDrop(col.field, event), onDragEnd: () => setDragField(null), children: [isColDraggable(col) ? (_jsx("button", { type: "button", className: "kpt-data-table__grip", "aria-label": t('dataTable.moveColumn', { column: col.header }), "aria-keyshortcuts": "ArrowLeft ArrowRight", onKeyDown: (event) => onGripKeyDown(col.field, event), children: _jsx(KptIcon, { name: "grip-vertical" }) })) : null, col.sortable ? (_jsxs("button", { type: "button", className: "kpt-data-table__sort-btn", onClick: () => toggleSort(col), children: [_jsx("span", { children: col.header }), _jsx(KptIcon, { className: cn('kpt-data-table__sort', ariaSort(col) !== 'none' && 'kpt-data-table__sort--active'), name: sortField !== col.field || !sortDir ? 'selector' : sortDir === 'asc' ? 'chevron-up' : 'chevron-down' })] })) : (_jsx("span", { children: col.header }))] }, col.field)))] }), hasColumnFilters && filterRowPosition === 'bottom' ? filterRow : null] }), loading ? (_jsx("tbody", { children: _jsx("tr", { children: _jsx("td", { className: "kpt-data-table__state", colSpan: colspan, children: t('dataTable.loading') }) }) })) : displayCount === 0 ? (_jsx("tbody", { children: _jsx("tr", { children: _jsx("td", { className: "kpt-data-table__state", colSpan: colspan, children: emptyMessage ?? t('dataTable.empty') }) }) })) : (_jsxs(_Fragment, { children: [virtualScroll && topPad > 0 ? (_jsx("tbody", { children: _jsx("tr", { className: "kpt-data-table__spacer", "aria-hidden": "true", children: _jsx("td", { colSpan: colspan, style: { height: topPad } }) }) })) : null, rowsToRender.map((row, index) => renderRow(row, index)), virtualScroll && bottomPad > 0 ? (_jsx("tbody", { children: _jsx("tr", { className: "kpt-data-table__spacer", "aria-hidden": "true", children: _jsx("td", { colSpan: colspan, style: { height: bottomPad } }) }) })) : null] })), hasSummary && !loading && displayCount > 0 ? (_jsx("tfoot", { children: _jsxs("tr", { className: "kpt-data-table__summary", children: [summaryLabelSpan > 0 ? (_jsx("th", { className: cn('kpt-data-table__summary-label', hasFrozen && 'kpt-data-table__cell--frozen'), colSpan: summaryLabelSpan, style: { left: hasFrozen ? 0 : undefined }, children: summaryLabel ?? t('dataTable.total') })) : null, visibleColumns.map((col, index) => index >= summaryStartIndex ? (_jsx("td", { "data-align": col.align ?? 'end', style: { width: col.width, left: frozenLeft(col.field) }, className: cn(frozenOffsets.has(col.field) && 'kpt-data-table__cell--frozen'), children: summaryText(col) }, col.field)) : null)] }) })) : null] }) }), _jsx("div", { className: "kpt-data-table__live", "aria-live": "polite", children: moveAnnouncement }), _jsx("div", { hidden: true, children: visibleColumns.map((col, colIndex) => isEditable(col) ? (_jsx("span", { id: `${uid}-edit-${colIndex}`, children: t('dataTable.editCell', { column: col.header }) }, col.field)) : null) }), pageSize > 0 && !virtualScroll ? (_jsxs("div", { className: "kpt-data-table__pager", children: [paginatorActions, _jsx(KptPaginator, { length: filteredCount, pageIndex: pageIndex, onPageIndexChange: setPageIndex, pageSize: pageSizeState, onPageSizeChange: setPageSizeState, pageSizeOptions: pageSizeOptions })] })) : null] }));
749
+ }, "aria-label": t('dataTable.selectAll'), onChange: toggleAllOnPage }) })) : selectable === 'single' ? (_jsx("th", { className: cn('kpt-data-table__checkbox', hasFrozen && 'kpt-data-table__cell--frozen'), style: { left: checkboxStickyLeft } })) : null, slots.map((slot) => {
750
+ if (!slot.meta)
751
+ return hspacer(slot);
752
+ const m = slot.meta;
753
+ const col = m.col;
754
+ return (_jsxs("th", { draggable: isColDraggable(col) || undefined, style: { width: virtualScroll ? undefined : col.width, left: m.left }, "data-align": m.align, "aria-colindex": colIndex(m), className: cn(m.frozen && 'kpt-data-table__cell--frozen', col.sortable && 'kpt-data-table__th--sortable', isColDraggable(col) && 'kpt-data-table__th--draggable', dragField !== null && dragField !== col.field && !col.frozen && 'kpt-data-table__th--dragover'), "data-field": col.field, "aria-sort": col.sortable ? ariaSort(col) : undefined, onDragStart: (event) => {
755
+ if (col.frozen)
756
+ return;
757
+ setDragField(col.field);
758
+ event.dataTransfer.setData('text/plain', col.field);
759
+ event.dataTransfer.effectAllowed = 'move';
760
+ }, onDragOver: (event) => {
761
+ if (!reorderable)
762
+ return;
763
+ const visIndex = visibleColumns.findIndex((c) => c.field === col.field);
764
+ if (dragField !== null && visIndex >= frozenLeadCount)
765
+ event.preventDefault();
766
+ }, onDrop: (event) => reorderable && onColDrop(col.field, event), onDragEnd: () => setDragField(null), children: [isColDraggable(col) ? (_jsx("button", { type: "button", className: "kpt-data-table__grip", "aria-label": t('dataTable.moveColumn', { column: col.header }), "aria-keyshortcuts": "ArrowLeft ArrowRight", onKeyDown: (event) => onGripKeyDown(col.field, event), children: _jsx(KptIcon, { name: "grip-vertical" }) })) : null, col.sortable ? (_jsxs("button", { type: "button", className: "kpt-data-table__sort-btn", onClick: () => toggleSort(col), children: [_jsx("span", { children: col.header }), _jsx(KptIcon, { className: cn('kpt-data-table__sort', ariaSort(col) !== 'none' && 'kpt-data-table__sort--active'), name: sortField !== col.field || !sortDir ? 'selector' : sortDir === 'asc' ? 'chevron-up' : 'chevron-down' })] })) : (_jsx("span", { children: col.header }))] }, slot.key));
767
+ })] }), hasColumnFilters && filterRowPosition === 'bottom' ? filterRow : null] }), loading ? (_jsx("tbody", { children: _jsx("tr", { children: _jsx("td", { className: "kpt-data-table__state", colSpan: colspan, children: t('dataTable.loading') }) }) })) : displayCount === 0 ? (_jsx("tbody", { children: _jsx("tr", { children: _jsx("td", { className: "kpt-data-table__state", colSpan: colspan, children: emptyMessage ?? t('dataTable.empty') }) }) })) : (_jsxs(_Fragment, { children: [virtualScroll && topPad > 0 ? (_jsx("tbody", { children: _jsx("tr", { className: "kpt-data-table__spacer", "aria-hidden": "true", children: _jsx("td", { colSpan: colspan, style: { height: topPad } }) }) })) : null, rowsToRender.map((row, index) => renderRow(row, index, virtualScroll ? headerRowCount + rowWindow.start + index + 1 : undefined)), virtualScroll && bottomPad > 0 ? (_jsx("tbody", { children: _jsx("tr", { className: "kpt-data-table__spacer", "aria-hidden": "true", children: _jsx("td", { colSpan: colspan, style: { height: bottomPad } }) }) })) : null] })), hasSummary && !loading && displayCount > 0 ? (_jsx("tfoot", { children: _jsxs("tr", { className: "kpt-data-table__summary", "aria-rowindex": virtualScroll ? headerRowCount + sorted.length + 1 : undefined, children: [summaryLayout.systemSpan > 0 ? (_jsx("td", { className: "kpt-data-table__cell--frozen", colSpan: summaryLayout.systemSpan, style: { left: 0 } })) : null, summaryLayout.labelSpan > 0 ? (_jsx("th", { className: cn('kpt-data-table__summary-label', summaryLayout.labelSticky && 'kpt-data-table__cell--frozen'), colSpan: summaryLayout.labelSpan, style: { left: summaryLayout.labelSticky ? 0 : undefined }, children: summaryLabel ?? t('dataTable.total') })) : null, summaryLayout.slots.map((slot) => slot.meta ? (_jsx("td", { "data-align": slot.meta.col.align ?? 'end', style: { width: virtualScroll ? undefined : slot.meta.col.width, left: slot.meta.left }, "aria-colindex": colIndex(slot.meta), className: cn(slot.meta.frozen && 'kpt-data-table__cell--frozen'), children: summaryTexts.get(slot.meta.col.field) ?? '' }, slot.key)) : (hspacer(slot)))] }) })) : null] }) }), _jsx("div", { className: "kpt-data-table__live", "aria-live": "polite", children: moveAnnouncement }), _jsx("div", { hidden: true, children: visibleColumns.map((col, colIndex) => isEditable(col) ? (_jsx("span", { id: `${uid}-edit-${colIndex}`, children: t('dataTable.editCell', { column: col.header }) }, col.field)) : null) }), pageSize > 0 && !virtualScroll ? (_jsxs("div", { className: "kpt-data-table__pager", children: [paginatorActions, _jsx(KptPaginator, { length: filteredCount, pageIndex: pageIndex, onPageIndexChange: setPageIndex, pageSize: pageSizeState, onPageSizeChange: setPageSizeState, pageSizeOptions: pageSizeOptions })] })) : null] }));
523
768
  }