@ixfx/components 0.13.2 → 0.13.4

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 (306) hide show
  1. package/bundle/index.d.ts +1649 -128
  2. package/bundle/index.js +1595 -44437
  3. package/bundle/rolldown-runtime-CEoa-xHZ.js +1 -0
  4. package/dist/ac-text.d.ts +2 -3
  5. package/dist/ac-text.js +0 -2
  6. package/dist/{ac-token-TCLAZeVg.d.ts → ac-token--_vCHXWw.d.ts} +2 -3
  7. package/dist/ac-token.d.ts +1 -1
  8. package/dist/ac-token.js +1 -3
  9. package/dist/button-B7mgqtxR.js +0 -2
  10. package/dist/{button-CTL45aBl.d.ts → button-BYuf72aR.d.ts} +2 -3
  11. package/dist/button.d.ts +1 -1
  12. package/dist/checkbox.d.ts +1 -2
  13. package/dist/checkbox.js +0 -2
  14. package/dist/checked-select-CglRukbP.d.ts +1 -2
  15. package/dist/checked-select-lDgAQQ7v.js +0 -2
  16. package/dist/colour-picker-BCMU7W7R.js +0 -2
  17. package/dist/colour-picker.d.ts +1 -1
  18. package/dist/crumbs.d.ts +2 -3
  19. package/dist/crumbs.js +1 -3
  20. package/dist/data-controller-DOlmqNKb.js +0 -2
  21. package/dist/data-display.d.ts +1 -2
  22. package/dist/data-display.js +0 -2
  23. package/dist/{data-grid-Ck1Qx2H1.d.ts → data-grid-BtBZt8M1.d.ts} +10 -9
  24. package/dist/data-grid-FHr7wZ_k.js +2108 -0
  25. package/dist/data-grid.d.ts +1 -1
  26. package/dist/data-grid.js +1 -1923
  27. package/dist/data-provider-35TyWclX.js +0 -2
  28. package/dist/data-provider-BAn_-oR1.d.ts +1 -2
  29. package/dist/decorate-VRK8rslU.js +0 -2
  30. package/dist/defaults-B4ePs4LB.js +0 -2
  31. package/dist/detail-list-BfYHNh6B.js +1999 -0
  32. package/dist/{detail-list-C1xzr1z7.d.ts → detail-list-u2rWnAXQ.d.ts} +13 -5
  33. package/dist/detail-list.d.ts +2 -2
  34. package/dist/detail-list.js +1 -1680
  35. package/dist/drag-ghost-DtSDvjri.js +0 -2
  36. package/dist/editable-label-rzXkjf1H.d.ts +1 -2
  37. package/dist/editable-label.js +0 -2
  38. package/dist/fallbacks-DQ_5OsmR.js +0 -2
  39. package/dist/{flat-list-Ohxqsy0g.d.ts → flat-list-DNnTQ65v.d.ts} +3 -4
  40. package/dist/flat-list.d.ts +1 -1
  41. package/dist/flat-list.js +5 -3
  42. package/dist/form-CsXJX15p.js +0 -2
  43. package/dist/form.d.ts +1 -2
  44. package/dist/{grid-list-pefLORIr.d.ts → grid-list-BLnV9oZE.d.ts} +9 -3
  45. package/dist/{grid-list-DjIl910A.js → grid-list-CkU8gHCh.js} +189 -42
  46. package/dist/grid-list.d.ts +1 -1
  47. package/dist/grid-list.js +1 -1
  48. package/dist/{grouped-item-lister-Bhyo-dkH.d.ts → grouped-item-lister-DETte0Dh.d.ts} +2 -3
  49. package/dist/grouped-item-lister.d.ts +1 -1
  50. package/dist/grouped-item-lister.js +0 -2
  51. package/dist/grouping-CUKgmkMm.d.ts +1 -2
  52. package/dist/grouping-RYPnHkrS.js +0 -2
  53. package/dist/handle-DAXs7kbJ.js +0 -2
  54. package/dist/hex-editor-Caq8vNQk.d.ts +1 -2
  55. package/dist/hex-editor-DiYpgIKs.js +0 -2
  56. package/dist/highlight-B-eQrhhb.js +0 -2
  57. package/dist/icon-1gpwtPx8.js +0 -2
  58. package/dist/{icon-DR_3rSyZ.d.ts → icon-CDGprSdE.d.ts} +2 -3
  59. package/dist/icons.d.ts +2 -2
  60. package/dist/icons.js +0 -2
  61. package/dist/{incr-search-D91M4Ozq.js → incr-search-JIhwB49y.js} +26 -6
  62. package/dist/incr-search.d.ts +1 -1
  63. package/dist/incr-search.js +1 -1
  64. package/dist/{index-Dq30pOnn.d.ts → index-B-hzcIP0.d.ts} +3 -4
  65. package/dist/{index-CxZ61DO1.d.ts → index-BYIRgv49.d.ts} +9 -3
  66. package/dist/index-CCC0G1nO.d.ts +324 -0
  67. package/dist/{index-garxxqgV.d.ts → index-CYndvTWH.d.ts} +2 -3
  68. package/dist/{index-BUB5SICW.d.ts → index-CoqtMSd9.d.ts} +2 -3
  69. package/dist/index-DV3iaUBW.d.ts +1 -2
  70. package/dist/{index-CJ5fshD3.d.ts → index-UvnoZUAo.d.ts} +2 -3
  71. package/dist/index-o-CWDt11.d.ts +1 -2
  72. package/dist/{index-BOUaDIW-.d.ts → index-tq6f8007.d.ts} +2 -3
  73. package/dist/index.d.ts +21 -22
  74. package/dist/index.js +48 -29
  75. package/dist/item-appearance-BjcMyauw.js +0 -2
  76. package/dist/keyboard-B5xD7Fz-.js +0 -2
  77. package/dist/labelled-input-base-BFa4xlYV.js +0 -2
  78. package/dist/{labelled-input-base-Dp_9KP0G.d.ts → labelled-input-base-DiTme1JO.d.ts} +2 -3
  79. package/dist/labelled-radial-input.d.ts +3 -4
  80. package/dist/labelled-radial-input.js +0 -2
  81. package/dist/labelled-range-input.d.ts +1 -1
  82. package/dist/labelled-range-input.js +0 -2
  83. package/dist/labelled-slider-input-FdCRi6st.js +0 -2
  84. package/dist/labelled-slider-input.d.ts +1 -1
  85. package/dist/led.d.ts +1 -2
  86. package/dist/led.js +0 -2
  87. package/dist/list-group-header.d.ts +2 -3
  88. package/dist/list-group-header.js +0 -2
  89. package/dist/list-item-animation-BUoEKi5s.js +0 -2
  90. package/dist/list-native-drag-controller-B9xO0XWn.js +0 -2
  91. package/dist/list-native-drag-controller-ChIi3eK5.d.ts +1 -2
  92. package/dist/list-selection-controller-BeE70lbB.js +0 -2
  93. package/dist/list-selection-types-PjKdhF-a.d.ts +1 -2
  94. package/dist/list-viewport-types-BRZsLcvp.d.ts +1 -2
  95. package/dist/menu-CwcXXeLM.js +0 -2
  96. package/dist/menu-item-BDEekBVy.js +0 -2
  97. package/dist/{menu-item-B0zqw2xp.d.ts → menu-item-bF6narIz.d.ts} +2 -3
  98. package/dist/menu.d.ts +2 -2
  99. package/dist/miller.d.ts +2 -3
  100. package/dist/miller.js +146 -102
  101. package/dist/narrowed-text.js +1 -3
  102. package/dist/notification-pill-DyZydXoZ.d.ts +1 -2
  103. package/dist/notifier.js +0 -2
  104. package/dist/pan-text-CoYmt2qd.d.ts +1 -2
  105. package/dist/pan-text-CqtvjPyK.js +0 -2
  106. package/dist/panel.d.ts +2 -3
  107. package/dist/panel.js +0 -2
  108. package/dist/polar-pad.d.ts +1 -2
  109. package/dist/polar-pad.js +0 -2
  110. package/dist/popup-D80sj2QC.js +0 -2
  111. package/dist/prominence-DlFShic3.js +0 -2
  112. package/dist/radial-input-DU6t2hYO.d.ts +1 -2
  113. package/dist/radial-input-KMVltQfe.js +0 -2
  114. package/dist/range-input.d.ts +1 -2
  115. package/dist/range-input.js +0 -2
  116. package/dist/range.d.ts +1 -2
  117. package/dist/range.js +0 -2
  118. package/dist/registry-BEtb8_wB.js +0 -2
  119. package/dist/{scroll-fade-BrNUIYnm.js → scroll-fade-Xx28D7mH.js} +11 -7
  120. package/dist/select-horiz.d.ts +1 -2
  121. package/dist/select-horiz.js +1 -3
  122. package/dist/slider-input-BxURAAmD.d.ts +1 -2
  123. package/dist/slider-input-ByHdotzp.js +0 -2
  124. package/dist/snackbar.d.ts +1 -2
  125. package/dist/snackbar.js +0 -2
  126. package/dist/snap-container.d.ts +1 -2
  127. package/dist/snap-container.js +0 -2
  128. package/dist/{split-layout-CXotkdbx.js → split-layout-Deoxy47d.js} +5 -2
  129. package/dist/{split-layout-C5SYbDrq.d.ts → split-layout-JK3xWVX2.d.ts} +2 -3
  130. package/dist/split-layout.d.ts +1 -1
  131. package/dist/split-layout.js +1 -1
  132. package/dist/swipe.d.ts +1 -2
  133. package/dist/swipe.js +0 -2
  134. package/dist/{tab-list-v8mKX7Fu.d.ts → tab-list-CexFFg6O.d.ts} +3 -5
  135. package/dist/tabs.d.ts +1 -1
  136. package/dist/tabs.js +23 -3
  137. package/dist/tickled-controller-D-HZymaZ.js +0 -2
  138. package/dist/tickled-controller-DodxD3eQ.d.ts +1 -2
  139. package/dist/tickled-styles-BrAHEj1q.js +0 -2
  140. package/dist/timeline-BP1Ux2EV.js +0 -2
  141. package/dist/{titlebar-B5zSTZnF.d.ts → titlebar-Tqu-VvMU.d.ts} +2 -3
  142. package/dist/titlebar.d.ts +1 -1
  143. package/dist/titlebar.js +0 -2
  144. package/dist/tooltip-BlvBUQxx.d.ts +1 -2
  145. package/dist/tooltip-CJt3WLmN.js +0 -2
  146. package/dist/transitory-label.d.ts +1 -2
  147. package/dist/transitory-label.js +0 -2
  148. package/dist/{tree-DGveys4K.js → tree-DD8OaqsK.js} +1009 -231
  149. package/dist/tree-component-Cj8V3uar.d.ts +1 -2
  150. package/dist/tree.d.ts +4 -267
  151. package/dist/tree.js +1 -1
  152. package/dist/types-DQ7KOUIj.d.ts +1 -2
  153. package/dist/{vertical-list-CqBc-t8w.d.ts → vertical-list-m7GJtXwv.d.ts} +3 -3
  154. package/dist/vertical-list.d.ts +1 -1
  155. package/dist/vertical-list.js +114 -27
  156. package/dist/wheel-nudge-By5s_ioH.d.ts +1 -2
  157. package/dist/wheel-nudge-D5rmht3v.js +0 -2
  158. package/dist/xy-axis-CyMd_Bn1.js +0 -2
  159. package/dist/xy-axis-DQqd2yXJ.d.ts +1 -2
  160. package/dist/xy-pad.d.ts +1 -2
  161. package/dist/xy-pad.js +0 -2
  162. package/docs-user/data-grid.md +74 -4
  163. package/docs-user/detail-list.md +65 -0
  164. package/docs-user/grid-list.md +81 -4
  165. package/docs-user/index.json +2 -2
  166. package/docs-user/miller.md +59 -3
  167. package/docs-user/tree.md +63 -2
  168. package/docs-user/vertical-list.md +9 -1
  169. package/package.json +6 -1
  170. package/bundle/index.d.ts.map +0 -1
  171. package/bundle/index.js.map +0 -1
  172. package/bundle/rolldown-runtime-D7D4PA-g.js +0 -13
  173. package/dist/ac-text.d.ts.map +0 -1
  174. package/dist/ac-text.js.map +0 -1
  175. package/dist/ac-token-TCLAZeVg.d.ts.map +0 -1
  176. package/dist/ac-token.js.map +0 -1
  177. package/dist/button-B7mgqtxR.js.map +0 -1
  178. package/dist/button-CTL45aBl.d.ts.map +0 -1
  179. package/dist/checkbox.d.ts.map +0 -1
  180. package/dist/checkbox.js.map +0 -1
  181. package/dist/checked-select-CglRukbP.d.ts.map +0 -1
  182. package/dist/checked-select-lDgAQQ7v.js.map +0 -1
  183. package/dist/colour-picker-BCMU7W7R.js.map +0 -1
  184. package/dist/crumbs.d.ts.map +0 -1
  185. package/dist/crumbs.js.map +0 -1
  186. package/dist/data-controller-DOlmqNKb.js.map +0 -1
  187. package/dist/data-display.d.ts.map +0 -1
  188. package/dist/data-display.js.map +0 -1
  189. package/dist/data-grid-Ck1Qx2H1.d.ts.map +0 -1
  190. package/dist/data-grid.js.map +0 -1
  191. package/dist/data-provider-35TyWclX.js.map +0 -1
  192. package/dist/data-provider-BAn_-oR1.d.ts.map +0 -1
  193. package/dist/decorate-VRK8rslU.js.map +0 -1
  194. package/dist/defaults-B4ePs4LB.js.map +0 -1
  195. package/dist/detail-list-C1xzr1z7.d.ts.map +0 -1
  196. package/dist/detail-list.js.map +0 -1
  197. package/dist/drag-ghost-DtSDvjri.js.map +0 -1
  198. package/dist/editable-label-rzXkjf1H.d.ts.map +0 -1
  199. package/dist/editable-label.js.map +0 -1
  200. package/dist/fallbacks-DQ_5OsmR.js.map +0 -1
  201. package/dist/flat-list-Ohxqsy0g.d.ts.map +0 -1
  202. package/dist/flat-list.js.map +0 -1
  203. package/dist/form-CsXJX15p.js.map +0 -1
  204. package/dist/form.d.ts.map +0 -1
  205. package/dist/grid-list-DjIl910A.js.map +0 -1
  206. package/dist/grid-list-pefLORIr.d.ts.map +0 -1
  207. package/dist/grouped-item-lister-Bhyo-dkH.d.ts.map +0 -1
  208. package/dist/grouped-item-lister.js.map +0 -1
  209. package/dist/grouping-CUKgmkMm.d.ts.map +0 -1
  210. package/dist/grouping-RYPnHkrS.js.map +0 -1
  211. package/dist/handle-DAXs7kbJ.js.map +0 -1
  212. package/dist/hex-editor-Caq8vNQk.d.ts.map +0 -1
  213. package/dist/hex-editor-DiYpgIKs.js.map +0 -1
  214. package/dist/highlight-B-eQrhhb.js.map +0 -1
  215. package/dist/icon-1gpwtPx8.js.map +0 -1
  216. package/dist/icon-DR_3rSyZ.d.ts.map +0 -1
  217. package/dist/icons.js.map +0 -1
  218. package/dist/incr-search-D91M4Ozq.js.map +0 -1
  219. package/dist/index-BOUaDIW-.d.ts.map +0 -1
  220. package/dist/index-BUB5SICW.d.ts.map +0 -1
  221. package/dist/index-CJ5fshD3.d.ts.map +0 -1
  222. package/dist/index-CxZ61DO1.d.ts.map +0 -1
  223. package/dist/index-DV3iaUBW.d.ts.map +0 -1
  224. package/dist/index-Dq30pOnn.d.ts.map +0 -1
  225. package/dist/index-garxxqgV.d.ts.map +0 -1
  226. package/dist/index-o-CWDt11.d.ts.map +0 -1
  227. package/dist/index.d.ts.map +0 -1
  228. package/dist/index.js.map +0 -1
  229. package/dist/item-appearance-BjcMyauw.js.map +0 -1
  230. package/dist/keyboard-B5xD7Fz-.js.map +0 -1
  231. package/dist/labelled-input-base-BFa4xlYV.js.map +0 -1
  232. package/dist/labelled-input-base-Dp_9KP0G.d.ts.map +0 -1
  233. package/dist/labelled-radial-input.d.ts.map +0 -1
  234. package/dist/labelled-radial-input.js.map +0 -1
  235. package/dist/labelled-range-input.js.map +0 -1
  236. package/dist/labelled-slider-input-FdCRi6st.js.map +0 -1
  237. package/dist/led.d.ts.map +0 -1
  238. package/dist/led.js.map +0 -1
  239. package/dist/list-group-header.d.ts.map +0 -1
  240. package/dist/list-group-header.js.map +0 -1
  241. package/dist/list-item-animation-BUoEKi5s.js.map +0 -1
  242. package/dist/list-native-drag-controller-B9xO0XWn.js.map +0 -1
  243. package/dist/list-native-drag-controller-ChIi3eK5.d.ts.map +0 -1
  244. package/dist/list-selection-controller-BeE70lbB.js.map +0 -1
  245. package/dist/list-selection-types-PjKdhF-a.d.ts.map +0 -1
  246. package/dist/list-viewport-types-BRZsLcvp.d.ts.map +0 -1
  247. package/dist/menu-CwcXXeLM.js.map +0 -1
  248. package/dist/menu-item-B0zqw2xp.d.ts.map +0 -1
  249. package/dist/menu-item-BDEekBVy.js.map +0 -1
  250. package/dist/miller.d.ts.map +0 -1
  251. package/dist/miller.js.map +0 -1
  252. package/dist/narrowed-text.js.map +0 -1
  253. package/dist/notification-pill-DyZydXoZ.d.ts.map +0 -1
  254. package/dist/notifier.js.map +0 -1
  255. package/dist/pan-text-CoYmt2qd.d.ts.map +0 -1
  256. package/dist/pan-text-CqtvjPyK.js.map +0 -1
  257. package/dist/panel.d.ts.map +0 -1
  258. package/dist/panel.js.map +0 -1
  259. package/dist/polar-pad.d.ts.map +0 -1
  260. package/dist/polar-pad.js.map +0 -1
  261. package/dist/popup-D80sj2QC.js.map +0 -1
  262. package/dist/prominence-DlFShic3.js.map +0 -1
  263. package/dist/radial-input-DU6t2hYO.d.ts.map +0 -1
  264. package/dist/radial-input-KMVltQfe.js.map +0 -1
  265. package/dist/range-input.d.ts.map +0 -1
  266. package/dist/range-input.js.map +0 -1
  267. package/dist/range.d.ts.map +0 -1
  268. package/dist/range.js.map +0 -1
  269. package/dist/registry-BEtb8_wB.js.map +0 -1
  270. package/dist/scroll-fade-BrNUIYnm.js.map +0 -1
  271. package/dist/select-horiz.d.ts.map +0 -1
  272. package/dist/select-horiz.js.map +0 -1
  273. package/dist/slider-input-BxURAAmD.d.ts.map +0 -1
  274. package/dist/slider-input-ByHdotzp.js.map +0 -1
  275. package/dist/snackbar.d.ts.map +0 -1
  276. package/dist/snackbar.js.map +0 -1
  277. package/dist/snap-container.d.ts.map +0 -1
  278. package/dist/snap-container.js.map +0 -1
  279. package/dist/split-layout-C5SYbDrq.d.ts.map +0 -1
  280. package/dist/split-layout-CXotkdbx.js.map +0 -1
  281. package/dist/swipe.d.ts.map +0 -1
  282. package/dist/swipe.js.map +0 -1
  283. package/dist/tab-list-v8mKX7Fu.d.ts.map +0 -1
  284. package/dist/tabs.js.map +0 -1
  285. package/dist/tickled-controller-D-HZymaZ.js.map +0 -1
  286. package/dist/tickled-controller-DodxD3eQ.d.ts.map +0 -1
  287. package/dist/tickled-styles-BrAHEj1q.js.map +0 -1
  288. package/dist/timeline-BP1Ux2EV.js.map +0 -1
  289. package/dist/titlebar-B5zSTZnF.d.ts.map +0 -1
  290. package/dist/titlebar.js.map +0 -1
  291. package/dist/tooltip-BlvBUQxx.d.ts.map +0 -1
  292. package/dist/tooltip-CJt3WLmN.js.map +0 -1
  293. package/dist/transitory-label.d.ts.map +0 -1
  294. package/dist/transitory-label.js.map +0 -1
  295. package/dist/tree-DGveys4K.js.map +0 -1
  296. package/dist/tree-component-Cj8V3uar.d.ts.map +0 -1
  297. package/dist/tree.d.ts.map +0 -1
  298. package/dist/types-DQ7KOUIj.d.ts.map +0 -1
  299. package/dist/vertical-list-CqBc-t8w.d.ts.map +0 -1
  300. package/dist/vertical-list.js.map +0 -1
  301. package/dist/wheel-nudge-By5s_ioH.d.ts.map +0 -1
  302. package/dist/wheel-nudge-D5rmht3v.js.map +0 -1
  303. package/dist/xy-axis-CyMd_Bn1.js.map +0 -1
  304. package/dist/xy-axis-DQqd2yXJ.d.ts.map +0 -1
  305. package/dist/xy-pad.d.ts.map +0 -1
  306. package/dist/xy-pad.js.map +0 -1
@@ -0,0 +1,2108 @@
1
+ import { t as TickledController } from "./tickled-controller-D-HZymaZ.js";
2
+ import { n as tickledStyles, t as tickledItemStyles } from "./tickled-styles-BrAHEj1q.js";
3
+ import { t as themeFallbacks } from "./fallbacks-DQ_5OsmR.js";
4
+ import { n as safeCustomElement, t as __decorate } from "./decorate-VRK8rslU.js";
5
+ import "./icon-1gpwtPx8.js";
6
+ import { i as ListSearchController, n as listSearchStyles, o as registerListSearchCommands, r as LIST_SEARCH_MODES } from "./incr-search-JIhwB49y.js";
7
+ import { t as handleStyles } from "./handle-DAXs7kbJ.js";
8
+ import { t as ListNativeDragController } from "./list-native-drag-controller-B9xO0XWn.js";
9
+ import { i as listItemFadeKeyframes } from "./list-item-animation-BUoEKi5s.js";
10
+ import "./interaction-ZTh4htzr.js";
11
+ import { t as scrollFadeStyles } from "./scroll-fade-Xx28D7mH.js";
12
+ import { n as appearanceColor, t as appearanceAbsent } from "./item-appearance-BjcMyauw.js";
13
+ import { t as createDragGhost } from "./drag-ghost-DtSDvjri.js";
14
+ import "./menu-CwcXXeLM.js";
15
+ import "./pan-text-CqtvjPyK.js";
16
+ import { LitElement, css, html, nothing } from "lit";
17
+ import { property, query, state } from "lit/decorators.js";
18
+ import { createCommandRegistry } from "@clinth/ui-commands";
19
+ import { repeat } from "lit/directives/repeat.js";
20
+ //#region src/data-grid/shift-wheel.ts
21
+ /**
22
+ * Shift + vertical mouse wheel → horizontal scroll.
23
+ *
24
+ * Browsers usually leave this translation to the OS (macOS turns Shift+wheel
25
+ * into a horizontal delta itself), but some mice, mouse utilities and
26
+ * platforms deliver it as a plain vertical delta with `shiftKey` set — which
27
+ * a two-axis scroller then scrolls vertically.
28
+ *
29
+ * Handling that needs a non-passive `wheel` listener, and a non-passive
30
+ * listener makes the compositor wait on the main thread before every wheel
31
+ * scroll, and not waiting is what keeps a busy virtualised grid's scrolling smooth.
32
+ * So the listener is only attached while Shift is held.
33
+ *
34
+ * @param getScroller Returns the element to scroll horizontally (and listen on).
35
+ * @returns Detach function.
36
+ */
37
+ function attachShiftWheel(getScroller) {
38
+ let attachedTo;
39
+ const onWheel = (e) => {
40
+ const el = e.currentTarget;
41
+ if (!e.shiftKey || e.deltaX !== 0 || e.deltaY === 0 || el.scrollWidth <= el.clientWidth) return;
42
+ const scale = e.deltaMode === 1 ? 16 : e.deltaMode === 2 ? el.clientWidth : 1;
43
+ el.scrollLeft += e.deltaY * scale;
44
+ e.preventDefault();
45
+ };
46
+ const detachWheel = () => {
47
+ attachedTo?.removeEventListener(`wheel`, onWheel);
48
+ attachedTo = void 0;
49
+ };
50
+ const onKey = (e) => {
51
+ if (e.key !== `Shift`) return;
52
+ detachWheel();
53
+ if (e.type === `keydown`) {
54
+ attachedTo = getScroller();
55
+ attachedTo?.addEventListener(`wheel`, onWheel, { passive: false });
56
+ }
57
+ };
58
+ window.addEventListener(`keydown`, onKey);
59
+ window.addEventListener(`keyup`, onKey);
60
+ window.addEventListener(`blur`, detachWheel);
61
+ return () => {
62
+ window.removeEventListener(`keydown`, onKey);
63
+ window.removeEventListener(`keyup`, onKey);
64
+ window.removeEventListener(`blur`, detachWheel);
65
+ detachWheel();
66
+ };
67
+ }
68
+ //#endregion
69
+ //#region src/data-grid/data-grid.ts
70
+ const EMPTY_CELL_LOOK = {
71
+ textColor: void 0,
72
+ icon: void 0,
73
+ iconColor: void 0,
74
+ badge: void 0
75
+ };
76
+ /** Shallow key-array equality, treating `undefined` (never dispatched) as different. */
77
+ function sameKeys(a, b) {
78
+ if (a === void 0 || a.length !== b.length) return false;
79
+ for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
80
+ return true;
81
+ }
82
+ /** Element-wise reference equality, treating `undefined` (never computed) as different. */
83
+ function sameRefs(a, b) {
84
+ return a !== void 0 && a.length === b.length && a.every((v, i) => v === b[i]);
85
+ }
86
+ let DataGridElement = class DataGridElement extends LitElement {
87
+ constructor(..._args) {
88
+ super(..._args);
89
+ this.columns = [];
90
+ this.rows = [];
91
+ this.selectionMode = `single`;
92
+ this.interactionMode = `standard`;
93
+ this.sortState = null;
94
+ this.rowHeight = 28;
95
+ this.getRowKey = (r, i) => r.key ?? r.id ?? String(i);
96
+ this.editable = false;
97
+ this.editability = `user`;
98
+ this.cellOverflow = `ellipsis`;
99
+ this.zebraStripes = false;
100
+ this.groupBy = null;
101
+ this.allowGrouping = false;
102
+ this.crossGroupSelection = false;
103
+ this.columnMenu = `none`;
104
+ this.textColorColumn = ``;
105
+ this.iconColumn = ``;
106
+ this.badgeColumn = ``;
107
+ this.itemsDraggable = false;
108
+ this.columnButtonIcon = `ellipsis`;
109
+ this._columnFormatters = /* @__PURE__ */ new Map();
110
+ this._resizingColKey = void 0;
111
+ this._animatingColKeys = /* @__PURE__ */ new Set();
112
+ this._selectedKeys = /* @__PURE__ */ new Set();
113
+ this._scrollTop = 0;
114
+ this._scrollLeft = 0;
115
+ this._viewportH = 300;
116
+ this._viewportW = 600;
117
+ this._collapsedGroups = /* @__PURE__ */ new Set();
118
+ this._editing = null;
119
+ this._editingRow = null;
120
+ this._lastClick = null;
121
+ this._lastClickedCell = null;
122
+ this._editValue = ``;
123
+ this._tickled = new TickledController(this, {
124
+ getItems: () => this.#tickledItems ??= this._flat.map((f) => ({
125
+ id: f.key,
126
+ enabled: f.type === `row`
127
+ })),
128
+ onStateChange: () => this.requestUpdate(),
129
+ hoverDisabled: () => this.#nativeDrag.isDragging
130
+ });
131
+ this.#nativeDragging = false;
132
+ this.#postDragSuppressTimer = 0;
133
+ this.#pointerEventSuppressess = /* @__PURE__ */ new WeakMap();
134
+ this.#nativeDrag = new ListNativeDragController(this, {
135
+ source: `data-grid`,
136
+ isEnabled: () => this.itemsDraggable && this.selectionMode !== `none` && this._flat.some((f) => f.type === `row`),
137
+ resolveEventToKey: (target) => {
138
+ if (!(target instanceof Element)) return void 0;
139
+ if (target.closest?.(`.header-cell`)) return void 0;
140
+ return (target.closest?.(`.row`))?.dataset.key;
141
+ },
142
+ prepareSelection: (originKey) => this.#prepareNativeDrag(originKey),
143
+ getMetrics: () => ({ rowHeight: this.rowHeight }),
144
+ getRenderer: () => this.dragPreviewRenderer,
145
+ getDragPreviewSourceResolver: () => this.dragPreviewSource,
146
+ dispatch: (detail) => this.dispatchEvent(new CustomEvent(`list-native-drag-start`, {
147
+ detail,
148
+ bubbles: true,
149
+ composed: true,
150
+ cancelable: true
151
+ })),
152
+ onActiveChange: (active, originKey) => {
153
+ this.#nativeDragging = active;
154
+ this.#dragOriginKey = active ? originKey : void 0;
155
+ this.toggleAttribute(`data-native-dragging`, active);
156
+ if (active) {
157
+ const count = this._selectedKeys.size;
158
+ this.setAttribute(`aria-label`, count <= 1 ? `Dragging 1 item` : `Dragging ${count} items`);
159
+ } else this.removeAttribute(`aria-label`);
160
+ this.requestUpdate();
161
+ },
162
+ onEnd: () => {
163
+ this.#nativeDragging = false;
164
+ this.#dragOriginKey = void 0;
165
+ this.toggleAttribute(`data-native-dragging`, false);
166
+ this.removeAttribute(`aria-label`);
167
+ this.#startPostDragSuppressTimer();
168
+ }
169
+ });
170
+ this._sortedRows = [];
171
+ this._flat = [];
172
+ this._flatKeys = [];
173
+ this.#flatIndexByKey = /* @__PURE__ */ new Map();
174
+ this.#flatVersion = 0;
175
+ this.#notedFlatVersion = -1;
176
+ this.#searchKeyByRow = /* @__PURE__ */ new Map();
177
+ this.search = new ListSearchController(this, { adapter: this.#createSearchAdapter() });
178
+ this.#prevFlatKeys = [];
179
+ this.#enteringRowKeys = /* @__PURE__ */ new Set();
180
+ this.#rowReflowTimer = 0;
181
+ this.#dataGridRenderedOnce = false;
182
+ this._visibleColumns = [];
183
+ this._colOffsets = [];
184
+ this._colWidthSum = 0;
185
+ this._raf = 0;
186
+ this.#viewportChangeRaf = 0;
187
+ this.#resortRaf = 0;
188
+ this._drag = null;
189
+ this._onFocusOut = (e) => {
190
+ if (this._suppressBlurCommit) return;
191
+ if (this.editability === `immediate`) return;
192
+ if (!this._editing && !this._editingRow) return;
193
+ const next = e.relatedTarget;
194
+ if (next && this.contains(next)) return;
195
+ this._editing = null;
196
+ this._editingRow = null;
197
+ this.requestUpdate();
198
+ };
199
+ this._onScroll = (e) => {
200
+ const el = e.target;
201
+ cancelAnimationFrame(this._raf);
202
+ this._raf = requestAnimationFrame(() => {
203
+ const before = this.#windowRows(2);
204
+ this._scrollTop = el.scrollTop;
205
+ this._scrollLeft = el.scrollLeft;
206
+ const after = this.#windowRows(2);
207
+ if (before.start !== after.start || before.end !== after.end) this.requestUpdate();
208
+ cancelAnimationFrame(this.#viewportChangeRaf);
209
+ this.#viewportChangeRaf = 0;
210
+ this.#emitViewportChangeIfChanged();
211
+ });
212
+ };
213
+ this._onKeyDown = (e) => {
214
+ if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === `a`) {
215
+ if (this.selectionMode === `multiple` || this.interactionMode === `checked` || this.interactionMode === `sticky` || this.interactionMode === `vscode` || this.interactionMode === `standard`) {
216
+ e.preventDefault();
217
+ const prev = new Set(this._selectedKeys);
218
+ if (this.groupBy && !this.crossGroupSelection && this._anchorKey) {
219
+ const g = this._getGroupKey(this._getRowForKey(this._anchorKey) ?? { key: this._anchorKey });
220
+ this._selectedKeys = new Set(this._flat.filter((f) => f.type === `row` && this._getGroupKey(f.row) === g).map((f) => f.key));
221
+ } else this._selectedKeys = new Set(this._flat.filter((f) => f.type === `row`).map((f) => f.key));
222
+ this.dispatchEvent(new CustomEvent(`datagrid-select`, {
223
+ detail: {
224
+ selectedKeys: new Set(this._selectedKeys),
225
+ previous: prev
226
+ },
227
+ bubbles: true,
228
+ composed: true
229
+ }));
230
+ }
231
+ return;
232
+ }
233
+ if (this._editing && (e.key === `ArrowDown` || e.key === `ArrowUp`)) {
234
+ if ((this.shadowRoot?.activeElement)?.tagName === `SELECT`) return;
235
+ e.preventDefault();
236
+ const cur = this._editing;
237
+ const dir = e.key === `ArrowDown` ? 1 : -1;
238
+ const colKey = cur.colKey;
239
+ this._suppressBlurCommit = true;
240
+ this._commitEdit();
241
+ const rowKeys = this._flat.filter((f) => f.type === `row`).map((f) => f.key);
242
+ const idx = rowKeys.indexOf(cur.rowKey);
243
+ let nextIdx = idx + dir;
244
+ while (nextIdx >= 0 && nextIdx < rowKeys.length) {
245
+ const nk = rowKeys[nextIdx];
246
+ const row = this.#rowEntry(nk)?.row;
247
+ const col = this._visibleColumns.find((c) => c.key === colKey);
248
+ if (row && col && this._isEditable(row, col)) {
249
+ this._startEdit(nk, colKey);
250
+ this._tickled.handleKeyboardNavigation(nextIdx > idx ? `next` : `previous`);
251
+ this._scrollRowIntoView(nk);
252
+ break;
253
+ }
254
+ nextIdx += dir;
255
+ }
256
+ if (nextIdx < 0 || nextIdx >= rowKeys.length) {
257
+ this._tickled.handleKeyboardNavigation(dir > 0 ? `next` : `previous`);
258
+ if (this._tickled.tickledId) this._scrollRowIntoView(this._tickled.tickledId);
259
+ }
260
+ requestAnimationFrame(() => {
261
+ this._suppressBlurCommit = false;
262
+ });
263
+ return;
264
+ }
265
+ if (e.key === `ArrowDown` || e.key === `ArrowUp` || e.key === `Home` || e.key === `End`) {
266
+ e.preventDefault();
267
+ this._tickled.setInputMode(`keyboard`);
268
+ }
269
+ switch (e.key) {
270
+ case `ArrowDown`:
271
+ this._tickled.handleKeyboardNavigation(`next`);
272
+ if (this._tickled.tickledId) this._scrollRowIntoView(this._tickled.tickledId);
273
+ if (this.interactionMode === `implicit` && this._tickled.tickledId) this._replaceSelection(this._tickled.tickledId);
274
+ if (e.shiftKey && (this.interactionMode === `standard` || this.interactionMode === `vscode`) && this._tickled.tickledId) this._rangeSelectTo(this._tickled.tickledId);
275
+ break;
276
+ case `ArrowUp`:
277
+ this._tickled.handleKeyboardNavigation(`previous`);
278
+ if (this._tickled.tickledId) this._scrollRowIntoView(this._tickled.tickledId);
279
+ if (this.interactionMode === `implicit` && this._tickled.tickledId) this._replaceSelection(this._tickled.tickledId);
280
+ if (e.shiftKey && (this.interactionMode === `standard` || this.interactionMode === `vscode`) && this._tickled.tickledId) this._rangeSelectTo(this._tickled.tickledId);
281
+ break;
282
+ case `Home`:
283
+ this._tickled.handleKeyboardNavigation(`first`);
284
+ if (this._tickled.tickledId) this._scrollRowIntoView(this._tickled.tickledId);
285
+ break;
286
+ case `End`:
287
+ this._tickled.handleKeyboardNavigation(`last`);
288
+ if (this._tickled.tickledId) this._scrollRowIntoView(this._tickled.tickledId);
289
+ break;
290
+ case `Enter`: {
291
+ const key = this._tickled.tickledId;
292
+ if (!key) break;
293
+ e.preventDefault();
294
+ if (!this._editing && this.editability === `user`) {
295
+ const entry = this.#rowEntry(key);
296
+ if (entry) {
297
+ const row = entry.row;
298
+ if (this._lastClickedCell?.rowKey === key) {
299
+ const col = this._visibleColumns.find((c) => c.key === this._lastClickedCell.colKey);
300
+ if (col && this._isEditable(row, col)) {
301
+ this._startEdit(key, col.key);
302
+ break;
303
+ }
304
+ }
305
+ const firstEditable = this._visibleColumns.find((c) => this._isEditable(row, c));
306
+ if (firstEditable) {
307
+ this._startEdit(key, firstEditable.key);
308
+ break;
309
+ }
310
+ }
311
+ }
312
+ if (!this._editing) {
313
+ const entry = this.#rowEntry(key);
314
+ const idx = entry?.idx ?? -1;
315
+ if (this._selectedKeys.has(key)) {
316
+ this._activateRow(key, entry?.row, idx);
317
+ break;
318
+ }
319
+ if (this.interactionMode !== `manual` && this.interactionMode !== `checked`) this._replaceSelection(key);
320
+ this.dispatchEvent(new CustomEvent(`datagrid-cell-click`, {
321
+ detail: {
322
+ rowKey: key,
323
+ colKey: ``,
324
+ rowIndex: idx,
325
+ colIndex: -1
326
+ },
327
+ bubbles: true,
328
+ composed: true
329
+ }));
330
+ }
331
+ break;
332
+ }
333
+ case ` `: {
334
+ if (this._editing) {
335
+ if ((this.columns.find((c) => c.key === this._editing.colKey)?.dataType ?? `text`) === `boolean`) {
336
+ e.preventDefault();
337
+ const isImmediate = this.editability === `immediate`;
338
+ const rowKey = this._editing.rowKey;
339
+ const colKey = this._editing.colKey;
340
+ const active = this.shadowRoot?.activeElement;
341
+ let next;
342
+ if (active?.type === `checkbox`) {
343
+ next = !active.checked;
344
+ active.checked = next;
345
+ } else {
346
+ const row = this.#rowEntry(rowKey)?.row;
347
+ next = !(this._editValue !== `` ? Boolean(this._editValue) : Boolean(row?.[colKey]));
348
+ }
349
+ if (isImmediate) this._commitImmediate(rowKey, colKey, next);
350
+ else {
351
+ this._editValue = next;
352
+ this._commitEdit();
353
+ }
354
+ }
355
+ break;
356
+ }
357
+ const key = this._tickled.tickledId;
358
+ if (!key) break;
359
+ e.preventDefault();
360
+ if (this.interactionMode === `checked` || this.interactionMode === `sticky` || this.interactionMode === `standard` || this.interactionMode === `vscode` || this.interactionMode === `manual`) this._toggleSelection(key);
361
+ else this._replaceSelection(key);
362
+ break;
363
+ }
364
+ case `Escape`: if (this._editing || this._editingRow) {
365
+ e.preventDefault();
366
+ this._editing = null;
367
+ this._editingRow = null;
368
+ this.requestUpdate();
369
+ } else if (this._selectedKeys.size) {
370
+ e.preventDefault();
371
+ const prev = new Set(this._selectedKeys);
372
+ this._selectedKeys = /* @__PURE__ */ new Set();
373
+ this.dispatchEvent(new CustomEvent(`datagrid-select`, {
374
+ detail: {
375
+ selectedKeys: /* @__PURE__ */ new Set(),
376
+ previous: prev
377
+ },
378
+ bubbles: true,
379
+ composed: true
380
+ }));
381
+ }
382
+ }
383
+ };
384
+ this._suppressBlurCommit = false;
385
+ }
386
+ static {
387
+ this.styles = [
388
+ themeFallbacks,
389
+ handleStyles,
390
+ tickledStyles,
391
+ tickledItemStyles,
392
+ listItemFadeKeyframes,
393
+ listSearchStyles,
394
+ scrollFadeStyles(`x`),
395
+ scrollFadeStyles(`y`),
396
+ css`
397
+ :host {
398
+ --data-grid-row-height: 28px;
399
+ --data-grid-header-height: 32px;
400
+ --data-grid-bg: var(--surface-3);
401
+ --data-grid-header-bg: var(--surface-2);
402
+ --data-grid-border: var(--border);
403
+ --data-grid-header-text: var(--surface-3-text);
404
+ --data-grid-reorder-handle-width: 4px;
405
+ /* How strongly the alternate ("zebra") row stands out from the base row
406
+ background when [zebra-stripes] is set - a color-mix() percentage. */
407
+ --data-grid-zebra-intensity: 6%;
408
+ /* Duration of the width transition when a column is auto-sized via
409
+ autoSizeColumn(). Read back in JS (_columnResizeAnimationMs) to know
410
+ when the animation has finished, so keep it a plain time value. */
411
+ --data-grid-column-resize-duration: 180ms;
412
+ /* Dense 28px rows read badly under the global 30px full-strength fade,
413
+ so default to a shorter, shallower edge hint. Consumers can override
414
+ --scroll-fade-width / --scroll-fade-strength per instance. */
415
+ --scroll-fade-width: 20px;
416
+ --scroll-fade-strength: 0.8;
417
+ position: relative;
418
+ display: block;
419
+ overflow: hidden;
420
+ border: var(--data-grid-border);
421
+ border-radius: var(--radius-m);
422
+ background: var(--data-grid-bg);
423
+ color: var(--surface-3-text);
424
+ font-family: var(--font-family);
425
+ font-size: var(--text-m);
426
+ }
427
+ :host(:focus-visible) { outline: 2px solid var(--accent); outline-offset: 2px; }
428
+ .grid { display: flex; flex-direction: column; height: 100%; min-height: 200px; position: relative; }
429
+ /* The header row lives inside .viewport and is sticky to its top, so it
430
+ scrolls horizontally with the rows on the compositor. Syncing a
431
+ separate header's scrollLeft from JS lags behind whenever the main
432
+ thread is busy rendering rows. */
433
+ .header-row {
434
+ position: sticky; top: 0; z-index: 3;
435
+ display: flex; align-items: stretch; height: var(--data-grid-header-height);
436
+ min-width: 100%; width: max-content; box-sizing: border-box;
437
+ background: var(--data-grid-header-bg); border-bottom: var(--data-grid-border);
438
+ /* Room for the last column to scroll out from under the column menu. */
439
+ padding-inline-end: var(--data-grid-actions-width, 0px);
440
+ }
441
+ /* Horizontal edge fade. The header row is not itself a scroller, so the
442
+ shared .scroll-fade-x animation reads the viewport's timeline instead,
443
+ and a second animation slides a viewport-wide mask along the
444
+ (full-width) row so the fade stays on the visible edges. */
445
+ .header-row.scroll-fade-x {
446
+ --scroll-fade-inset-start: 0px;
447
+ --scroll-fade-inset-end: var(--data-grid-actions-width, 0px);
448
+ scroll-timeline: none;
449
+ animation: scroll-fade-x-fade, data-grid-header-mask-track;
450
+ animation-timing-function: linear;
451
+ animation-timeline: scroll(nearest x), scroll(nearest x);
452
+ mask-size: var(--data-grid-viewport-width, 100%) 100%;
453
+ mask-repeat: no-repeat;
454
+ }
455
+ @keyframes data-grid-header-mask-track {
456
+ from { mask-position: 0 0; }
457
+ to { mask-position: 100% 0; }
458
+ }
459
+ .header-actions {
460
+ position: absolute; top: 0; z-index: 4;
461
+ right: var(--data-grid-scrollbar-width, 0px);
462
+ width: var(--data-grid-actions-width); height: var(--data-grid-header-height);
463
+ display: flex; box-sizing: border-box; border-bottom: var(--data-grid-border);
464
+ }
465
+ .header-cell {
466
+ display: flex; align-items: center; gap: var(--space-xs);
467
+ padding: 0 var(--space-s); border-right: var(--border-subtle);
468
+ flex-shrink: 0; user-select: none; cursor: pointer; position: relative;
469
+ background: var(--data-grid-header-bg);
470
+ box-sizing: border-box;
471
+ }
472
+ .header-cell[data-tickled] { background: var(--surface-5); }
473
+ /* Applied to a header-cell / cell while its column is animating towards
474
+ an autoSizeColumn() target width. */
475
+ .header-cell.width-animating, .cell.width-animating {
476
+ transition: width var(--data-grid-column-resize-duration, 180ms) ease-out;
477
+ }
478
+ @media (prefers-reduced-motion: reduce) {
479
+ .header-cell.width-animating, .cell.width-animating { transition: none; }
480
+ }
481
+ .header-label { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
482
+ .sort-icon { display: inline-flex; align-items: center; opacity: 0.7; }
483
+ .resize-handle {
484
+ position: absolute; right: -4px; top: 0; bottom: 0; width: 8px; cursor: col-resize; z-index: 1;
485
+ display: flex; align-items: stretch; justify-content: center;
486
+ }
487
+ .resize-handle::before {
488
+ content: ''; width: 2px; margin: 2px 0; border-radius: 1px; background: var(--split-gutter-color, transparent);
489
+ transition: background-color var(--transition, 0.15s ease-out);
490
+ }
491
+ .resize-handle:hover::before { background: var(--split-gutter-hover-color, var(--surface-6)); }
492
+ .resize-handle[data-active]::before { background: var(--split-gutter-active-color, var(--accent)); }
493
+ .reorder-handle { cursor: grab; opacity: 0; display: inline-flex; align-items: center; transition: opacity var(--transition, 0.15s ease-out); }
494
+ .header-cell:hover .reorder-handle, .header-cell:focus-within .reorder-handle, .header-cell[drag-source] .reorder-handle { opacity: var(--ixfx-handle-opacity, 0.5); }
495
+ .col-button { opacity: 0; pointer-events: none; display: inline-flex; align-items: center; justify-content: center; width: 20px; height: 20px; border-radius: var(--radius-s); cursor: pointer; flex-shrink: 0; margin-left: var(--space-xs); transition: opacity var(--transition, 0.15s ease-out), background var(--transition, 0.15s ease-out); }
496
+ .col-button ixfx-icon { width: 14px; height: 14px; }
497
+ .header-cell:hover .col-button, .header-cell:focus-within .col-button { opacity: 1; pointer-events: auto; }
498
+ .col-button:hover { background: var(--surface-4); }
499
+ .reorder-handle .ixfx-handle { --ixfx-handle-length: 14px; --ixfx-handle-thickness: var(--data-grid-reorder-handle-width, 10px); --ixfx-handle-dot-size: 3px; }
500
+ .flyout-trigger { flex-shrink: 0; width: 36px; display: flex; align-items: center; justify-content: center; cursor: pointer; border-left: var(--border-subtle); background: var(--data-grid-header-bg); }
501
+ .grid.has-actions { --data-grid-actions-width: 44px; }
502
+ .viewport {
503
+ flex: 1; overflow: auto; position: relative;
504
+ /* Keyboard / focus scrolling must not park rows under the sticky header. */
505
+ scroll-padding-top: var(--data-grid-header-height);
506
+ --scroll-fade-inset-start: var(--data-grid-header-height);
507
+ }
508
+ /* Vertical edge fade from the shared .scroll-fade-y class
509
+ (see ../styles/scroll-fade.ts). The mask is on the viewport, inset by
510
+ the header height so the top fade starts below the sticky column
511
+ headers. Reach/strength tune via --scroll-fade-width /
512
+ --scroll-fade-strength (defaults set on :host above). */
513
+ .spacer { position: relative; }
514
+ .row.item {
515
+ position: absolute; left: 0; right: 0; display: flex; align-items: stretch;
516
+ gap: 0;
517
+ height: var(--data-grid-row-height);
518
+ /* Border width is always 1px, even when not visually shown (transparent),
519
+ so hover/selection never changes the box size and shifts content. */
520
+ border: 1px solid transparent;
521
+ background: var(--item-bg);
522
+ border-radius: var(--item-border-radius);
523
+ padding: 0;
524
+ box-sizing: border-box;
525
+ /* Only background transitions - hover ("tickled") no longer changes
526
+ border, so there is nothing else to animate. */
527
+ transition: background var(--transition, 0.1s ease-out);
528
+ }
529
+ /* Zebra striping: alternate rows tint towards the text colour, which
530
+ lightens in dark themes and darkens in light themes - so it reads as
531
+ "more contrast" either way. The zebra-odd class is only applied when
532
+ zebraStripes is set (see render()). Declared before .tickled /
533
+ .selected so those states (same specificity) still win when a striped
534
+ row is also hovered or selected. */
535
+ .row.item.zebra-odd {
536
+ background: color-mix(in oklch, transparent calc(100% - var(--data-grid-zebra-intensity, 6%)), var(--surface-3-text) var(--data-grid-zebra-intensity, 6%));
537
+ }
538
+ /* Hover: background only, no border change (avoids layout shift + colour flash). */
539
+ .row.item.tickled { background: var(--item-bg-tickled); color: var(--item-text-tickled); }
540
+ .row.item.selected { background: var(--item-bg-selected); border: var(--item-border-selected); color: var(--item-text-selected); }
541
+ .row.item.selected.tickled { background: var(--item-bg-selected-tickled); }
542
+ .row.item.dragging-origin { opacity: 0.6; }
543
+
544
+ /* Insertion / removal animation. Only while [reflowing] — the ~0.3s after
545
+ the row set changes — do rows slide (their top transitions) to make or
546
+ close a gap; a freshly inserted row also fades in. Kept scoped so it
547
+ never fires while scrolling the virtualised list. */
548
+ :host([reflowing]) .row.item,
549
+ :host([reflowing]) .group-row {
550
+ transition: top var(--data-grid-row-move-duration, 200ms) cubic-bezier(0.2, 0, 0, 1);
551
+ }
552
+ .row.item.entering {
553
+ animation: ixfx-list-item-in var(--data-grid-row-move-duration, 200ms) ease both;
554
+ }
555
+ @media (prefers-reduced-motion: reduce) {
556
+ :host([reflowing]) .row.item, :host([reflowing]) .group-row { transition: none; }
557
+ .row.item.entering { animation: none; }
558
+ }
559
+ .cell {
560
+ flex-shrink: 0; display: flex; align-items: center; padding: 0 var(--space-s);
561
+ overflow: hidden; text-overflow: ellipsis; white-space: nowrap; border-right: var(--border-subtle);
562
+ box-sizing: border-box;
563
+ font-variant-numeric: tabular-nums;
564
+ }
565
+ .cell.editable-text { cursor: text; }
566
+ .cell input, .cell select { width: 100%; font: inherit; }
567
+ /* Per-row icon / badge, enabled by nominating iconColumn / badgeColumn.
568
+ The badge self-justifies to the cell's trailing edge; the value text
569
+ keeps its own flex sizing so the cell still ellipsises. */
570
+ .cell-icon {
571
+ flex: none;
572
+ display: inline-flex;
573
+ align-items: center;
574
+ justify-content: center;
575
+ margin-inline-end: var(--data-grid-cell-icon-gap, 6px);
576
+ width: var(--data-grid-cell-icon-size, 1em);
577
+ height: var(--data-grid-cell-icon-size, 1em);
578
+ }
579
+ .cell-badge {
580
+ flex: none;
581
+ display: inline-flex;
582
+ align-items: center;
583
+ margin-inline-start: auto;
584
+ padding-inline-start: var(--data-grid-badge-gap, 6px);
585
+ }
586
+ .checkbox-col { width: 28px; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
587
+ ixfx-menu-trigger { flex: 1; border-left: var(--border-subtle); background: var(--data-grid-header-bg); display: flex; align-items: center; --ixfx-button-padding: var(--space-s) var(--space-m); min-height: var(--data-grid-header-height); justify-content: center; }
588
+ .header-cell[drag-source] { opacity: 0.4; }
589
+ .drop-caret {
590
+ position: absolute; top: 0; bottom: 0; width: 2px;
591
+ background: var(--accent); border-radius: 1px; pointer-events: none; z-index: 2;
592
+ }
593
+ .group-row {
594
+ position: absolute; left: 0; right: 0; display: flex; align-items: center; gap: var(--space-s);
595
+ height: var(--data-grid-row-height); background: transparent; color: var(--surface-3-text);
596
+ border-bottom: 1px solid var(--border); font-weight: 600; padding: 0 var(--space-s);
597
+ cursor: pointer; user-select: none;
598
+ }
599
+ .group-row .caret { transition: transform 0.15s; display: inline-flex; }
600
+ .group-row.collapsed .caret { transform: rotate(-90deg); }
601
+ .group-row .divider { flex: 1; height: 1px; background: var(--border); margin-left: var(--space-s); }
602
+ .group-row .count { opacity: 0.6; font-weight: 400; font-size: 0.9em; }
603
+
604
+ `
605
+ ];
606
+ }
607
+ #viewportResizeObserver;
608
+ #nativeDragging;
609
+ #dragOriginKey;
610
+ #postDragSuppressTimer;
611
+ #pointerEventSuppressess;
612
+ #nativeDrag;
613
+ #prepareNativeDrag(originKey) {
614
+ const rows = this._flat.filter((f) => f.type === `row`);
615
+ if (!rows.some((f) => f.key === originKey)) return void 0;
616
+ if (!this._selectedKeys.has(originKey)) this._replaceSelection(originKey);
617
+ const selected = [];
618
+ let displayIndex = 0;
619
+ for (const entry of rows) {
620
+ if (!this._selectedKeys.has(entry.key)) {
621
+ displayIndex++;
622
+ continue;
623
+ }
624
+ const rowEl = this.renderRoot.querySelector(`.row[data-key="${CSS.escape(entry.key)}"]`) ?? void 0;
625
+ selected.push({
626
+ key: entry.key,
627
+ index: displayIndex,
628
+ item: entry.row,
629
+ ...rowEl ? { element: rowEl } : {}
630
+ });
631
+ displayIndex++;
632
+ }
633
+ const origin = selected.find((entry) => entry.key === originKey);
634
+ if (!origin) return void 0;
635
+ return {
636
+ origin,
637
+ selected
638
+ };
639
+ }
640
+ #startPostDragSuppressTimer() {
641
+ if (this.#postDragSuppressTimer) clearTimeout(this.#postDragSuppressTimer);
642
+ this.#postDragSuppressTimer = window.setTimeout(() => {
643
+ this.#postDragSuppressTimer = 0;
644
+ }, 0);
645
+ }
646
+ #flatIndexByKey;
647
+ /** Bumped whenever `_flat` changes; drives `#noteFlatMutation`. */
648
+ #flatVersion;
649
+ #notedFlatVersion;
650
+ /** Inputs `_sortedRows` / `_flat` were last derived from — see `#syncDerivedRows`. */
651
+ #sortInputs;
652
+ #flatInputs;
653
+ /** `_flat` projected for the tickled controller, cached until `_flat` changes. */
654
+ #tickledItems;
655
+ #searchRegistry;
656
+ #unregisterSearchCommands;
657
+ #searchKeyByRow;
658
+ /** Command registry carrying this grid's `list.search.*` commands. */
659
+ get registry() {
660
+ return this.#searchRegistry;
661
+ }
662
+ #rowSearchText(row) {
663
+ if (this.rowSearchText) return this.rowSearchText(row);
664
+ const rec = row;
665
+ return this._visibleColumns.map((c) => String(rec[c.key] ?? ``)).join(` `);
666
+ }
667
+ #createSearchAdapter() {
668
+ return {
669
+ getItems: () => {
670
+ this.#searchKeyByRow.clear();
671
+ this._sortedRows.forEach((r, i) => this.#searchKeyByRow.set(r, this.getRowKey(r, i)));
672
+ return this._sortedRows;
673
+ },
674
+ getLabel: (row) => this.#rowSearchText(row),
675
+ getKey: (row) => this.#searchKeyByRow.get(row) ?? String(this._sortedRows.indexOf(row)),
676
+ focusItem: (row) => {
677
+ if (row === void 0) return;
678
+ const key = this.#searchKeyByRow.get(row);
679
+ if (key !== void 0) this.tickleRow(key);
680
+ },
681
+ selectItems: (rows) => {
682
+ this.selectKeys(rows.map((r) => this.#searchKeyByRow.get(r)).filter((k) => k !== void 0));
683
+ },
684
+ applyFilter: (matched) => {
685
+ this.filterPredicate = matched === void 0 ? void 0 : (row) => {
686
+ const k = this.#searchKeyByRow.get(row);
687
+ return k === void 0 || matched.has(k);
688
+ };
689
+ }
690
+ };
691
+ }
692
+ #effectiveSearchModes() {
693
+ const raw = this.searchModes?.trim();
694
+ if (raw === void 0) return [...LIST_SEARCH_MODES];
695
+ const requested = new Set(raw.split(/\s+/));
696
+ return LIST_SEARCH_MODES.filter((m) => requested.has(m));
697
+ }
698
+ #syncSearchCommands() {
699
+ if (!this.#searchRegistry) return;
700
+ this.search.setModes(this.#effectiveSearchModes());
701
+ this.#unregisterSearchCommands?.();
702
+ this.#unregisterSearchCommands = registerListSearchCommands(this.#searchRegistry, this.search);
703
+ }
704
+ /** Row-key snapshot from the previous update, for the insertion / removal animation. */
705
+ #prevFlatKeys;
706
+ #enteringRowKeys;
707
+ #rowReflowTimer;
708
+ #dataGridRenderedOnce;
709
+ #viewportChangeRaf;
710
+ /** Key sets as of the last `datagrid-viewport-change` dispatch, for dedupe. */
711
+ #lastViewportRenderedKeys;
712
+ #lastViewportVisibleKeys;
713
+ #resortRaf;
714
+ #detachShiftWheel;
715
+ connectedCallback() {
716
+ super.connectedCallback();
717
+ if (!this.hasAttribute(`tabindex`)) this.tabIndex = 0;
718
+ this.addEventListener(`keydown`, this._onKeyDown);
719
+ this.addEventListener(`focusout`, this._onFocusOut);
720
+ this.#searchRegistry ??= createCommandRegistry();
721
+ this.#syncSearchCommands();
722
+ this.#detachShiftWheel = attachShiftWheel(() => this._viewportEl ?? void 0);
723
+ }
724
+ disconnectedCallback() {
725
+ super.disconnectedCallback();
726
+ this.removeEventListener(`keydown`, this._onKeyDown);
727
+ this.removeEventListener(`focusout`, this._onFocusOut);
728
+ cancelAnimationFrame(this._raf);
729
+ cancelAnimationFrame(this.#viewportChangeRaf);
730
+ this.#viewportChangeRaf = 0;
731
+ this.#unregisterSearchCommands?.();
732
+ this.#unregisterSearchCommands = void 0;
733
+ this.#searchRegistry = void 0;
734
+ if (this.#rowReflowTimer) {
735
+ clearTimeout(this.#rowReflowTimer);
736
+ this.#rowReflowTimer = 0;
737
+ }
738
+ if (this.#postDragSuppressTimer) {
739
+ clearTimeout(this.#postDragSuppressTimer);
740
+ this.#postDragSuppressTimer = 0;
741
+ }
742
+ this.#viewportResizeObserver?.disconnect();
743
+ this.#viewportResizeObserver = void 0;
744
+ this.#detachShiftWheel?.();
745
+ this.#detachShiftWheel = void 0;
746
+ }
747
+ /**
748
+ * Dispatch a command to this grid — routes through the command registry so
749
+ * `list.search.*` (and any other registered commands) work.
750
+ */
751
+ dispatch(commandId, args) {
752
+ this.#searchRegistry?.invoke(commandId, args);
753
+ }
754
+ firstUpdated() {
755
+ this.#viewportResizeObserver = new ResizeObserver(() => this.#measureViewport());
756
+ this.#viewportResizeObserver.observe(this._viewportEl);
757
+ this.#measureViewport();
758
+ }
759
+ /**
760
+ * Re-read the viewport's size and update `_viewportH`/`_viewportW` if it
761
+ * changed. A zero-sized read (e.g. while hosted in a hidden/inactive dock
762
+ * tab) is ignored so it never clobbers the last known-good geometry — the
763
+ * observer fires again with the real size once the host becomes visible.
764
+ */
765
+ #measureViewport() {
766
+ if (!this._viewportEl) return;
767
+ const vp = this._viewportEl;
768
+ const w = vp.clientWidth;
769
+ const h = this.#bodyHeight();
770
+ if (w <= 0 || h <= 0) return;
771
+ const grid = vp.parentElement;
772
+ grid.style.setProperty(`--data-grid-scrollbar-width`, `${vp.offsetWidth - w}px`);
773
+ grid.style.setProperty(`--data-grid-viewport-width`, `${w}px`);
774
+ if (w === this._viewportW && h === this._viewportH) return;
775
+ this._viewportW = w;
776
+ this._viewportH = h;
777
+ this.#scheduleViewportChangeEmit();
778
+ }
779
+ /**
780
+ * Force a fresh re-measure of the viewport and re-render. Normally
781
+ * unnecessary — a `ResizeObserver` tracks the viewport automatically — but
782
+ * gives a consumer an escape hatch to recover if an ancestor's
783
+ * layout/visibility changes without the observer noticing (matches
784
+ * `ixfx-grid-list`'s `refreshLayout()`).
785
+ */
786
+ refreshLayout() {
787
+ this.#measureViewport();
788
+ this.requestUpdate();
789
+ }
790
+ updated(changed) {
791
+ super.updated(changed);
792
+ if (changed.has(`rows`) || changed.has(`sortState`) || changed.has(`columns`) || changed.has(`groupBy`) || changed.has(`groupLabel`) || changed.has(`filterPredicate`) || changed.has(`rowHeight`)) this.#scheduleViewportChangeEmit();
793
+ if (changed.has(`_editing`) || changed.has(`_editingRow`)) this.updateComplete.then(() => {
794
+ let sel = null;
795
+ if (this._editing) sel = `.row[data-key="${this._editing.rowKey}"] .cell[data-col="${this._editing.colKey}"] input, .row[data-key="${this._editing.rowKey}"] .cell[data-col="${this._editing.colKey}"] select`;
796
+ else if (this._editingRow) sel = `.row[data-key="${this._editingRow}"] .cell input, .row[data-key="${this._editingRow}"] .cell select`;
797
+ if (!sel) return;
798
+ const el = this.renderRoot.querySelector(sel);
799
+ el?.focus();
800
+ if (el instanceof HTMLInputElement && el.type === `text`) el.select();
801
+ });
802
+ }
803
+ willUpdate(changed) {
804
+ super.willUpdate(changed);
805
+ this.setAttribute(`data-input-mode`, this._tickled.inputMode);
806
+ if (changed.has(`searchModes`)) this.#syncSearchCommands();
807
+ this.#syncDerivedRows();
808
+ this._visibleColumns = this.columns.filter((c) => c.visible !== false);
809
+ let off = 0;
810
+ this._colOffsets = this._visibleColumns.map((c) => {
811
+ const o = off;
812
+ off += c.width ?? 150;
813
+ return o;
814
+ });
815
+ this._colWidthSum = off;
816
+ if (this.interactionMode === `checked`) this._colWidthSum += 28;
817
+ if (this.#flatVersion !== this.#notedFlatVersion) {
818
+ this.#notedFlatVersion = this.#flatVersion;
819
+ this.#noteFlatMutation();
820
+ }
821
+ }
822
+ /**
823
+ * Re-derive `_sortedRows` / `_flat` only when one of their inputs changed
824
+ * identity. Scroll, hover, selection and edit-state updates touch none of
825
+ * them, so those frames cost O(window) instead of O(rows).
826
+ */
827
+ #syncDerivedRows() {
828
+ const sortInputs = [
829
+ this.rows,
830
+ this.sortState,
831
+ this.columns
832
+ ];
833
+ const flatInputs = [
834
+ this.filterPredicate,
835
+ this.groupBy,
836
+ this.groupLabel,
837
+ this._collapsedGroups,
838
+ this.getRowKey
839
+ ];
840
+ const resort = !sameRefs(this.#sortInputs, sortInputs);
841
+ if (resort) {
842
+ this.#sortInputs = sortInputs;
843
+ this._sortedRows = this._applySort([...this.rows]);
844
+ }
845
+ if (resort || !sameRefs(this.#flatInputs, flatInputs)) {
846
+ this.#flatInputs = flatInputs;
847
+ this._rebuildFlat();
848
+ }
849
+ }
850
+ /** Mark the derived rows as matching the current inputs (after an incremental update). */
851
+ #markDerivedRowsSynced() {
852
+ this.#sortInputs = [
853
+ this.rows,
854
+ this.sortState,
855
+ this.columns
856
+ ];
857
+ this.#flatInputs = [
858
+ this.filterPredicate,
859
+ this.groupBy,
860
+ this.groupLabel,
861
+ this._collapsedGroups,
862
+ this.getRowKey
863
+ ];
864
+ }
865
+ /**
866
+ * Diff the row-key list against the previous update. Rows that appeared get an
867
+ * enter fade; any add/remove opens a short `[reflowing]` window so the rows
868
+ * that shift slide (their `top` transitions) instead of snapping. Bounded in
869
+ * time so it never bleeds into scroll virtualisation.
870
+ */
871
+ #noteFlatMutation() {
872
+ const now = this._flatKeys;
873
+ const prev = this.#prevFlatKeys;
874
+ this.#prevFlatKeys = now.slice();
875
+ if (!this.#dataGridRenderedOnce) {
876
+ this.#dataGridRenderedOnce = true;
877
+ return;
878
+ }
879
+ if (prev.length === 0 || now.length === 0) {
880
+ this.#enteringRowKeys.clear();
881
+ return;
882
+ }
883
+ const before = new Set(prev);
884
+ const added = now.filter((k) => !before.has(k));
885
+ const removedCount = prev.length - (now.length - added.length);
886
+ if (added.length === now.length || added.length === 0 && removedCount === 0) return;
887
+ this.#enteringRowKeys = new Set(added);
888
+ this.toggleAttribute(`reflowing`, true);
889
+ if (this.#rowReflowTimer) clearTimeout(this.#rowReflowTimer);
890
+ this.#rowReflowTimer = window.setTimeout(() => {
891
+ this.#rowReflowTimer = 0;
892
+ this.#enteringRowKeys.clear();
893
+ this.toggleAttribute(`reflowing`, false);
894
+ this.requestUpdate();
895
+ }, 320);
896
+ }
897
+ _getGroupKey(row) {
898
+ if (!this.groupBy) return null;
899
+ if (typeof this.groupBy === `string`) return String(row[this.groupBy] ?? ``);
900
+ return String(this.groupBy(row));
901
+ }
902
+ _rebuildFlat() {
903
+ const pred = this.filterPredicate;
904
+ const sourceIdx = [];
905
+ for (let i = 0; i < this._sortedRows.length; i++) if (!pred || pred(this._sortedRows[i])) sourceIdx.push(i);
906
+ const flat = [];
907
+ const keys = [];
908
+ const pushRow = (idx) => {
909
+ const row = this._sortedRows[idx];
910
+ const key = this.getRowKey(row, idx);
911
+ flat.push({
912
+ type: `row`,
913
+ key,
914
+ row,
915
+ idx
916
+ });
917
+ keys.push(key);
918
+ };
919
+ if (!this.groupBy) sourceIdx.forEach(pushRow);
920
+ else {
921
+ const groups = /* @__PURE__ */ new Map();
922
+ for (const idx of sourceIdx) {
923
+ const k = this._getGroupKey(this._sortedRows[idx]) ?? ``;
924
+ const arr = groups.get(k);
925
+ if (arr) arr.push(idx);
926
+ else groups.set(k, [idx]);
927
+ }
928
+ for (const [gKey, idxs] of groups) {
929
+ const label = this.groupLabel ? this.groupLabel(gKey, idxs.map((i) => this._sortedRows[i])) : gKey;
930
+ const gId = `__group__${gKey}`;
931
+ flat.push({
932
+ type: `group`,
933
+ key: gId,
934
+ label,
935
+ count: idxs.length
936
+ });
937
+ keys.push(gId);
938
+ if (!this._collapsedGroups.has(gKey)) idxs.forEach(pushRow);
939
+ }
940
+ }
941
+ this._flat = flat;
942
+ this._flatKeys = keys;
943
+ this.#rebuildFlatIndex();
944
+ }
945
+ #rebuildFlatIndex() {
946
+ this.#flatIndexByKey.clear();
947
+ this._flat.forEach((f, i) => this.#flatIndexByKey.set(f.key, i));
948
+ this.#flatVersion++;
949
+ this.#tickledItems = void 0;
950
+ }
951
+ /** Flat entry for a row key, or `undefined` for unknown keys and group headers. */
952
+ #rowEntry(key) {
953
+ const i = this.#flatIndexByKey.get(key);
954
+ const entry = i === void 0 ? void 0 : this._flat[i];
955
+ if (entry?.type === `row`) return entry;
956
+ const idx = this._sortedRows.findIndex((r, j) => this.getRowKey(r, j) === key);
957
+ return idx < 0 ? void 0 : {
958
+ row: this._sortedRows[idx],
959
+ idx
960
+ };
961
+ }
962
+ _toggleGroup(gKey) {
963
+ const next = new Set(this._collapsedGroups);
964
+ if (next.has(gKey)) next.delete(gKey);
965
+ else next.add(gKey);
966
+ this._collapsedGroups = next;
967
+ }
968
+ _applySort(rows) {
969
+ if (!this.sortState) return rows;
970
+ const col = this.columns.find((c) => c.key === this.sortState.key);
971
+ if (!col) return rows;
972
+ const dir = this.sortState.direction === `asc` ? 1 : -1;
973
+ if (col.sortFn) return rows.sort((a, b) => dir * col.sortFn(a, b));
974
+ const dataType = col.dataType ?? `text`;
975
+ return rows.sort((a, b) => {
976
+ const av = a[col.key];
977
+ const bv = b[col.key];
978
+ if (av === bv) return 0;
979
+ if (dataType === `number`) return dir * (av - bv);
980
+ if (dataType === `boolean`) return dir * (Boolean(av) === Boolean(bv) ? 0 : av ? 1 : -1);
981
+ return dir * String(av).localeCompare(String(bv));
982
+ });
983
+ }
984
+ _onHeaderClick(col, e) {
985
+ if (col.sortable === false) return;
986
+ if (e) {
987
+ const pointerEvent = e.composedPath()[0];
988
+ if (pointerEvent && this.#pointerEventSuppressess.get(pointerEvent)) {
989
+ this.#pointerEventSuppressess.delete(pointerEvent);
990
+ return;
991
+ }
992
+ }
993
+ const prev = this.sortState;
994
+ let next = null;
995
+ if (!prev || prev.key !== col.key) next = {
996
+ key: col.key,
997
+ direction: `asc`
998
+ };
999
+ else if (prev.direction === `asc`) next = {
1000
+ key: col.key,
1001
+ direction: `desc`
1002
+ };
1003
+ else next = null;
1004
+ this.sortState = next;
1005
+ this.dispatchEvent(new CustomEvent(`datagrid-sort`, {
1006
+ detail: {
1007
+ sort: next,
1008
+ previous: prev
1009
+ },
1010
+ bubbles: true,
1011
+ composed: true
1012
+ }));
1013
+ }
1014
+ _onHeaderPointerUp(col, e) {
1015
+ let stopped = false;
1016
+ const customEvent = new CustomEvent(`datagrid-header-pointerup`, {
1017
+ detail: {
1018
+ key: col.key,
1019
+ column: col
1020
+ },
1021
+ bubbles: true,
1022
+ composed: true,
1023
+ cancelable: true
1024
+ });
1025
+ this.dispatchEvent(customEvent);
1026
+ if (customEvent.defaultPrevented) stopped = true;
1027
+ if (stopped) this.#pointerEventSuppressess.set(e, true);
1028
+ }
1029
+ _onColButtonClick(col, e) {
1030
+ e.stopPropagation();
1031
+ const icon = col.button === false ? this.columnButtonIcon : typeof col.button === `object` && col.button.icon ? col.button.icon : this.columnButtonIcon;
1032
+ const target = e.currentTarget;
1033
+ const customEvent = new CustomEvent(`datagrid-header-button-click`, {
1034
+ detail: {
1035
+ key: col.key,
1036
+ column: col,
1037
+ icon,
1038
+ clientX: e.clientX,
1039
+ clientY: e.clientY,
1040
+ anchorElement: target
1041
+ },
1042
+ bubbles: true,
1043
+ composed: true,
1044
+ cancelable: true
1045
+ });
1046
+ this.dispatchEvent(customEvent);
1047
+ if (customEvent.defaultPrevented) this.#pointerEventSuppressess.set(e, true);
1048
+ }
1049
+ _onRowClick(rowKey, col, e) {
1050
+ if (this.#nativeDrag.isDragging || this.#postDragSuppressTimer !== 0) return;
1051
+ if (col && col.selectable === false) return;
1052
+ if (this.selectionMode === `none` && this.interactionMode !== `checked` && this.interactionMode !== `sticky`) return;
1053
+ if (this.interactionMode === `manual`) return;
1054
+ if (this.interactionMode === `implicit`) {
1055
+ this._replaceSelection(rowKey);
1056
+ return;
1057
+ }
1058
+ if (this.interactionMode === `checked`) return;
1059
+ if (this.interactionMode === `sticky`) {
1060
+ this._toggleSelection(rowKey);
1061
+ return;
1062
+ }
1063
+ if (e.ctrlKey || e.metaKey) {
1064
+ if (this.selectionMode === `multiple` || this.interactionMode === `vscode`) this._toggleSelection(rowKey);
1065
+ } else if (e.shiftKey && (this.selectionMode === `multiple` || this.interactionMode === `vscode`)) this._rangeSelectTo(rowKey);
1066
+ else this._replaceSelection(rowKey);
1067
+ }
1068
+ _onCheckboxClick(rowKey, e) {
1069
+ e.stopPropagation();
1070
+ this._onRowClick(rowKey, null, e);
1071
+ }
1072
+ _onVisibilityToggle(col, checked) {
1073
+ col.visible = checked;
1074
+ this.columns = [...this.columns];
1075
+ this.dispatchEvent(new CustomEvent(`datagrid-column-visibility`, {
1076
+ detail: {
1077
+ key: col.key,
1078
+ visible: checked
1079
+ },
1080
+ bubbles: true,
1081
+ composed: true
1082
+ }));
1083
+ }
1084
+ _beginHeaderDrag(e, col, idx) {
1085
+ e.stopPropagation();
1086
+ const anchor = e.currentTarget.closest(`.header-cell`) ?? e.currentTarget;
1087
+ this._drag = {
1088
+ pointerId: e.pointerId,
1089
+ from: idx,
1090
+ startX: e.clientX,
1091
+ startY: e.clientY,
1092
+ active: false,
1093
+ ghost: null,
1094
+ gap: void 0,
1095
+ sourceCol: col
1096
+ };
1097
+ const onMove = (ev) => {
1098
+ if (!this._drag || ev.pointerId !== this._drag.pointerId) return;
1099
+ const dx = ev.clientX - this._drag.startX;
1100
+ const dy = ev.clientY - this._drag.startY;
1101
+ if (!this._drag.active) {
1102
+ if (Math.hypot(dx, dy) < 5) return;
1103
+ this._drag.active = true;
1104
+ const content = document.createElement(`span`);
1105
+ content.textContent = col.label;
1106
+ try {
1107
+ this._drag.ghost = createDragGhost({
1108
+ anchor,
1109
+ content
1110
+ });
1111
+ this._drag.ghost.move(ev.clientX, ev.clientY);
1112
+ } catch {}
1113
+ try {
1114
+ this._headerRowEl?.setPointerCapture(ev.pointerId);
1115
+ } catch {}
1116
+ }
1117
+ this._drag.ghost?.move(ev.clientX, ev.clientY);
1118
+ const x = ev.clientX - (this._headerRowEl?.getBoundingClientRect().left ?? 0);
1119
+ let gap = this._drag.from;
1120
+ let bestDist = Infinity;
1121
+ for (let i = 0; i < this._visibleColumns.length; i++) {
1122
+ if (i === this._drag.from) continue;
1123
+ const rectLeft = this._colOffsets[i];
1124
+ const rectRight = rectLeft + (this._visibleColumns[i].width ?? 150);
1125
+ const mid = (rectLeft + rectRight) / 2;
1126
+ const gapPos = x < mid ? rectLeft : rectRight;
1127
+ const gapIdx = x < mid ? i : i + 1;
1128
+ const d = Math.abs(x - gapPos);
1129
+ if (d < bestDist) {
1130
+ bestDist = d;
1131
+ gap = gapIdx > this._drag.from ? gapIdx - 1 : gapIdx;
1132
+ }
1133
+ }
1134
+ if (this._visibleColumns.length > 0) {
1135
+ const lastOff = this._colOffsets[this._colOffsets.length - 1] + (this._visibleColumns[this._visibleColumns.length - 1].width ?? 150);
1136
+ const d = Math.abs(x - lastOff);
1137
+ if (d < bestDist) {
1138
+ bestDist = d;
1139
+ gap = this._visibleColumns.length - 1;
1140
+ if (this._drag.from === this._visibleColumns.length - 1) gap = this._drag.from;
1141
+ else if (gap >= this._drag.from) gap = this._visibleColumns.length - 1;
1142
+ }
1143
+ }
1144
+ const siblings = this._visibleColumns.length - 1;
1145
+ let normalizedGap = gap;
1146
+ if (normalizedGap < 0) normalizedGap = 0;
1147
+ if (normalizedGap > siblings) normalizedGap = siblings;
1148
+ this._drag.gap = normalizedGap;
1149
+ this.requestUpdate();
1150
+ };
1151
+ const onUp = (ev) => {
1152
+ if (!this._drag || ev.pointerId !== this._drag.pointerId) return;
1153
+ const active = this._drag.active;
1154
+ const from = this._drag.from;
1155
+ const gap = this._drag.gap;
1156
+ this._drag.ghost?.destroy();
1157
+ try {
1158
+ this._headerRowEl?.releasePointerCapture(ev.pointerId);
1159
+ } catch {}
1160
+ window.removeEventListener(`pointermove`, onMove);
1161
+ window.removeEventListener(`pointerup`, onUp);
1162
+ window.removeEventListener(`pointercancel`, onUp);
1163
+ this._drag = null;
1164
+ this.requestUpdate();
1165
+ if (active && gap !== void 0 && gap !== from) this._onReorderDrag(from, gap);
1166
+ };
1167
+ window.addEventListener(`pointermove`, onMove);
1168
+ window.addEventListener(`pointerup`, onUp);
1169
+ window.addEventListener(`pointercancel`, onUp);
1170
+ }
1171
+ _onReorderDrag(fromIdx, toIdx) {
1172
+ if (fromIdx === toIdx) return;
1173
+ const cols = [...this.columns];
1174
+ const vis = cols.filter((c) => c.visible !== false);
1175
+ const hidden = cols.filter((c) => c.visible === false);
1176
+ const [moved] = vis.splice(fromIdx, 1);
1177
+ vis.splice(toIdx, 0, moved);
1178
+ this.columns = [...vis, ...hidden];
1179
+ this.dispatchEvent(new CustomEvent(`datagrid-column-reorder`, {
1180
+ detail: {
1181
+ from: fromIdx,
1182
+ to: toIdx,
1183
+ columns: [...this.columns]
1184
+ },
1185
+ bubbles: true,
1186
+ composed: true
1187
+ }));
1188
+ }
1189
+ _scrollRowIntoView(key) {
1190
+ const idx = this.#flatIndexByKey.get(key);
1191
+ if (idx === void 0 || !this._viewportEl) return;
1192
+ const rh = this.rowHeight;
1193
+ const top = (idx > 0 && this._flat[idx - 1].type === `group` ? idx - 1 : idx) * rh;
1194
+ const bottom = (idx + 1) * rh;
1195
+ const vt = this._viewportEl.scrollTop;
1196
+ const vh = this.#bodyHeight();
1197
+ let next = vt;
1198
+ if (top < vt) next = top;
1199
+ else if (bottom > vt + vh) next = bottom - vh;
1200
+ if (next !== vt) {
1201
+ this._viewportEl.scrollTop = next;
1202
+ this._scrollTop = next;
1203
+ this.requestUpdate();
1204
+ }
1205
+ }
1206
+ _getRowForKey(key) {
1207
+ const i = this.#flatIndexByKey.get(key);
1208
+ const entry = i === void 0 ? void 0 : this._flat[i];
1209
+ return entry?.type === `row` ? entry.row : void 0;
1210
+ }
1211
+ /** The selected row keys (read-only). */
1212
+ get selectedKeys() {
1213
+ return this._selectedKeys;
1214
+ }
1215
+ /** Replace the selection with these row keys (clamped to known rows). */
1216
+ set selectedKeys(keys) {
1217
+ this.selectKeys(keys);
1218
+ }
1219
+ /**
1220
+ * Replace the selection with the given row keys. Unknown keys are dropped;
1221
+ * in a single-select mode only the last surviving key is kept;
1222
+ * `selectionMode="none"` clears. Fires `datagrid-select` with the previous
1223
+ * set when the selection actually changes.
1224
+ */
1225
+ selectKeys(keys) {
1226
+ let next = new Set([...keys].filter((k) => this._getRowForKey(k) !== void 0));
1227
+ const multi = this.selectionMode === `multiple` || this.interactionMode === `checked` || this.interactionMode === `sticky` || this.interactionMode === `vscode`;
1228
+ if (this.selectionMode === `none`) next = /* @__PURE__ */ new Set();
1229
+ else if (!multi && next.size > 1) {
1230
+ const last = [...next].at(-1);
1231
+ next = last === void 0 ? /* @__PURE__ */ new Set() : /* @__PURE__ */ new Set([last]);
1232
+ }
1233
+ const prev = new Set(this._selectedKeys);
1234
+ if (prev.size === next.size && [...next].every((k) => prev.has(k))) return;
1235
+ this._selectedKeys = next;
1236
+ this._anchorKey = [...next].at(-1);
1237
+ this.dispatchEvent(new CustomEvent(`datagrid-select`, {
1238
+ detail: {
1239
+ selectedKeys: new Set(next),
1240
+ previous: prev
1241
+ },
1242
+ bubbles: true,
1243
+ composed: true
1244
+ }));
1245
+ }
1246
+ /** The row key under the cursor, or `undefined`. */
1247
+ get tickledKey() {
1248
+ return this._tickled.tickledId;
1249
+ }
1250
+ /** Move the cursor to this row key and scroll it into view. */
1251
+ tickleRow(key) {
1252
+ if (!this.#flatIndexByKey.has(key) || key.startsWith(`__group__`)) return;
1253
+ this._tickled.focus(key);
1254
+ this._scrollRowIntoView(key);
1255
+ }
1256
+ /**
1257
+ * Append rows without the full O(n log n) rebuild that reassigning `rows`
1258
+ * incurs. `rows` still gets a new array reference (reactivity is preserved),
1259
+ * but the derived arrays are extended over the appended slice only. When a
1260
+ * sort is active the new rows show unsorted for one frame, then a single
1261
+ * coalesced re-sort runs. Scroll position, selection and cursor are kept.
1262
+ */
1263
+ appendRows(rows) {
1264
+ if (rows.length === 0) return;
1265
+ const startIdx = this.rows.length;
1266
+ const inSync = this._sortedRows.length === startIdx && sameRefs(this.#sortInputs, [
1267
+ this.rows,
1268
+ this.sortState,
1269
+ this.columns
1270
+ ]);
1271
+ this.rows = [...this.rows, ...rows];
1272
+ if (!inSync) return;
1273
+ const appended = this.rows.slice(startIdx);
1274
+ this._sortedRows = [...this._sortedRows, ...appended];
1275
+ this.#appendToFlat(appended, startIdx);
1276
+ this.#markDerivedRowsSynced();
1277
+ if (this.sortState != null) {
1278
+ cancelAnimationFrame(this.#resortRaf);
1279
+ this.#resortRaf = requestAnimationFrame(() => {
1280
+ this.#sortInputs = void 0;
1281
+ this.requestUpdate();
1282
+ });
1283
+ }
1284
+ }
1285
+ #appendToFlat(appended, startIdx) {
1286
+ if (this.groupBy) {
1287
+ this._rebuildFlat();
1288
+ return;
1289
+ }
1290
+ for (let j = 0; j < appended.length; j++) {
1291
+ const idx = startIdx + j;
1292
+ if (this.filterPredicate && !this.filterPredicate(appended[j])) continue;
1293
+ const key = this.getRowKey(appended[j], idx);
1294
+ this._flat.push({
1295
+ type: `row`,
1296
+ key,
1297
+ row: appended[j],
1298
+ idx
1299
+ });
1300
+ this._flatKeys.push(key);
1301
+ this.#flatIndexByKey.set(key, this._flat.length - 1);
1302
+ }
1303
+ this.#flatVersion++;
1304
+ this.#tickledItems = void 0;
1305
+ }
1306
+ _replaceSelection(key) {
1307
+ if (this.selectionMode === `none`) return;
1308
+ const prev = new Set(this._selectedKeys);
1309
+ if (this.groupBy && !this.crossGroupSelection) {
1310
+ const g = this._getGroupKey(this._getRowForKey(key) ?? { key });
1311
+ [...prev].filter((k) => this._getGroupKey(this._getRowForKey(k) ?? { key: k }) === g);
1312
+ this._selectedKeys = /* @__PURE__ */ new Set([key]);
1313
+ } else this._selectedKeys = /* @__PURE__ */ new Set([key]);
1314
+ this._anchorKey = key;
1315
+ this.dispatchEvent(new CustomEvent(`datagrid-select`, {
1316
+ detail: {
1317
+ selectedKeys: new Set(this._selectedKeys),
1318
+ previous: prev
1319
+ },
1320
+ bubbles: true,
1321
+ composed: true
1322
+ }));
1323
+ }
1324
+ _toggleSelection(key) {
1325
+ const prev = new Set(this._selectedKeys);
1326
+ const next = new Set(this._selectedKeys);
1327
+ if (next.has(key)) next.delete(key);
1328
+ else {
1329
+ if (this.groupBy && !this.crossGroupSelection) {
1330
+ const g = this._getGroupKey(this._getRowForKey(key) ?? { key });
1331
+ for (const k of [...next]) if (this._getGroupKey(this._getRowForKey(k) ?? { key: k }) !== g) next.delete(k);
1332
+ }
1333
+ if (this.selectionMode !== `multiple` && this.interactionMode !== `checked` && this.interactionMode !== `sticky`) next.clear();
1334
+ next.add(key);
1335
+ }
1336
+ this._selectedKeys = next;
1337
+ this._anchorKey = key;
1338
+ this.dispatchEvent(new CustomEvent(`datagrid-select`, {
1339
+ detail: {
1340
+ selectedKeys: new Set(this._selectedKeys),
1341
+ previous: prev
1342
+ },
1343
+ bubbles: true,
1344
+ composed: true
1345
+ }));
1346
+ }
1347
+ _rangeSelectTo(key) {
1348
+ if (!this._anchorKey) {
1349
+ this._replaceSelection(key);
1350
+ return;
1351
+ }
1352
+ const keys = this._flat.filter((f) => f.type === `row`).map((f) => f.key);
1353
+ const a = keys.indexOf(this._anchorKey);
1354
+ const b = keys.indexOf(key);
1355
+ if (a < 0 || b < 0) {
1356
+ this._replaceSelection(key);
1357
+ return;
1358
+ }
1359
+ if (this.groupBy && !this.crossGroupSelection) {
1360
+ const anchorGroup = this._getGroupKey(this._getRowForKey(this._anchorKey) ?? { key: this._anchorKey });
1361
+ if (anchorGroup !== this._getGroupKey(this._getRowForKey(key) ?? { key })) {
1362
+ this._replaceSelection(key);
1363
+ return;
1364
+ }
1365
+ const groupKeys = keys.filter((k) => this._getGroupKey(this._getRowForKey(k) ?? { key: k }) === anchorGroup);
1366
+ const ga = groupKeys.indexOf(this._anchorKey);
1367
+ const gb = groupKeys.indexOf(key);
1368
+ if (ga < 0 || gb < 0) {
1369
+ this._replaceSelection(key);
1370
+ return;
1371
+ }
1372
+ const lo = Math.min(ga, gb);
1373
+ const hi = Math.max(ga, gb);
1374
+ const prev = new Set(this._selectedKeys);
1375
+ this._selectedKeys = new Set(groupKeys.slice(lo, hi + 1));
1376
+ this.dispatchEvent(new CustomEvent(`datagrid-select`, {
1377
+ detail: {
1378
+ selectedKeys: new Set(this._selectedKeys),
1379
+ previous: prev
1380
+ },
1381
+ bubbles: true,
1382
+ composed: true
1383
+ }));
1384
+ return;
1385
+ }
1386
+ const lo = Math.min(a, b);
1387
+ const hi = Math.max(a, b);
1388
+ const prev = new Set(this._selectedKeys);
1389
+ this._selectedKeys = new Set(keys.slice(lo, hi + 1));
1390
+ this.dispatchEvent(new CustomEvent(`datagrid-select`, {
1391
+ detail: {
1392
+ selectedKeys: new Set(this._selectedKeys),
1393
+ previous: prev
1394
+ },
1395
+ bubbles: true,
1396
+ composed: true
1397
+ }));
1398
+ }
1399
+ _isEditable(row, col) {
1400
+ if (this.editability === `disabled`) return false;
1401
+ if (typeof this.editable === `function`) return this.editable(row, col);
1402
+ if (this.editable === true) return col.editable !== false;
1403
+ return !!col.editable;
1404
+ }
1405
+ _isTextEditor(col) {
1406
+ const e = col.editor;
1407
+ return !e || e === `auto`;
1408
+ }
1409
+ _isCellEditing(rowKey, colKey) {
1410
+ if (this.editability === `immediate` && this._isEditable(this.#rowEntry(rowKey)?.row ?? {}, this.columns.find((c) => c.key === colKey))) return true;
1411
+ if (this._editingRow === rowKey) return this._isEditable(this.#rowEntry(rowKey)?.row ?? {}, this.columns.find((c) => c.key === colKey));
1412
+ return this._editing?.rowKey === rowKey && this._editing?.colKey === colKey;
1413
+ }
1414
+ _activateRow(rowKey, row, rowIndex) {
1415
+ this.dispatchEvent(new CustomEvent(`datagrid-row-activate`, {
1416
+ detail: {
1417
+ rowKey,
1418
+ row,
1419
+ rowIndex
1420
+ },
1421
+ bubbles: true,
1422
+ composed: true
1423
+ }));
1424
+ }
1425
+ _startEdit(rowKey, colKey) {
1426
+ if (this.editability === `disabled`) return;
1427
+ const row = this.#rowEntry(rowKey)?.row;
1428
+ if (!row) return;
1429
+ const col = this.columns.find((c) => c.key === colKey);
1430
+ if (!col || !this._isEditable(row, col)) return;
1431
+ this._editing = {
1432
+ rowKey,
1433
+ colKey
1434
+ };
1435
+ this._editingRow = null;
1436
+ this._editValue = row[colKey];
1437
+ }
1438
+ /** Programmatic: put a single cell in edit mode. Works in manual/user/immediate (no-op if disabled or not editable). */
1439
+ startEdit(rowKey, colKey) {
1440
+ this._startEdit(rowKey, colKey);
1441
+ }
1442
+ /** Programmatic: put all editable cells of a row in edit mode. */
1443
+ startRowEdit(rowKey) {
1444
+ if (this.editability === `disabled`) return;
1445
+ if (!this.#rowEntry(rowKey)?.row) return;
1446
+ this._editing = null;
1447
+ this._editingRow = rowKey;
1448
+ this.requestUpdate();
1449
+ }
1450
+ stopEdit() {
1451
+ this._editing = null;
1452
+ this._editingRow = null;
1453
+ }
1454
+ setColumnFormatter(columnKey, formatter, options) {
1455
+ if (!formatter) {
1456
+ this._columnFormatters.delete(columnKey);
1457
+ this.requestUpdate();
1458
+ return;
1459
+ }
1460
+ if (typeof formatter === `string`) {
1461
+ const digits = options?.digits ?? (formatter === `integer` ? 0 : 2);
1462
+ if (formatter === `fixed`) this._columnFormatters.set(columnKey, (v) => typeof v === `number` && Number.isFinite(v) ? v.toFixed(digits) : String(v ?? ``));
1463
+ else if (formatter === `percentage`) this._columnFormatters.set(columnKey, (v) => typeof v === `number` && Number.isFinite(v) ? `${v.toFixed(digits)}%` : String(v ?? ``));
1464
+ else if (formatter === `integer`) this._columnFormatters.set(columnKey, (v) => typeof v === `number` && Number.isFinite(v) ? Math.round(v).toString() : String(v ?? ``));
1465
+ } else this._columnFormatters.set(columnKey, formatter);
1466
+ this.requestUpdate();
1467
+ }
1468
+ _formatValue(value, col, row) {
1469
+ const perCol = this._columnFormatters.get(col.key);
1470
+ if (perCol) return perCol(value);
1471
+ if (col.formatter) return col.formatter(value, row, col);
1472
+ if (this.globalFormatter) return this.globalFormatter(value, col.key, row);
1473
+ return value;
1474
+ }
1475
+ /**
1476
+ * Resolve the per-row appearance for the cell at `colKey`, honouring the
1477
+ * nominated columns. Additive — `col.renderer` / `col.formatter` still own
1478
+ * the cell's value; this only decorates it. Returns all-`undefined` for
1479
+ * every cell when nothing is nominated (the common case).
1480
+ */
1481
+ _cellLook(row, colKey) {
1482
+ if (this.textColorColumn === `` && this.iconColumn === `` && this.badgeColumn === ``) return EMPTY_CELL_LOOK;
1483
+ const rec = typeof row === `object` && row !== null ? row : {};
1484
+ const textColor = colKey === this.textColorColumn ? appearanceColor(rec.textColor) : void 0;
1485
+ const icon = colKey === this.iconColumn && !appearanceAbsent(rec.icon) ? rec.icon : void 0;
1486
+ const iconColor = icon === void 0 ? void 0 : appearanceColor(rec.iconColor);
1487
+ const badge = colKey === this.badgeColumn && !appearanceAbsent(rec.badge) ? rec.badge : void 0;
1488
+ if (textColor === void 0 && icon === void 0 && badge === void 0) return EMPTY_CELL_LOOK;
1489
+ return {
1490
+ textColor,
1491
+ icon,
1492
+ iconColor,
1493
+ badge
1494
+ };
1495
+ }
1496
+ _handleCellSecondClick(rowKey, col) {
1497
+ if (this.editability !== `user`) return;
1498
+ const now = Date.now();
1499
+ if (this._lastClick && this._lastClick.key === rowKey && this._lastClick.col === col.key && now - this._lastClick.time < 700 && now - this._lastClick.time > 150) {
1500
+ this._startEdit(rowKey, col.key);
1501
+ this._lastClick = null;
1502
+ } else this._lastClick = {
1503
+ key: rowKey,
1504
+ col: col.key,
1505
+ time: now
1506
+ };
1507
+ }
1508
+ _commitEdit(fromImmediate = false) {
1509
+ if (!this._editing && !fromImmediate) return;
1510
+ const target = this._editing;
1511
+ if (!target) return;
1512
+ const { rowKey, colKey } = target;
1513
+ const row = this.#rowEntry(rowKey)?.row;
1514
+ if (!row) {
1515
+ this._editing = null;
1516
+ return;
1517
+ }
1518
+ const oldValue = row[colKey];
1519
+ const newValue = this._editValue;
1520
+ const col = this.columns.find((c) => c.key === colKey);
1521
+ if (col?.editor && typeof col.editor === `function`) {} else {
1522
+ row[colKey] = newValue;
1523
+ this.rows = [...this.rows];
1524
+ }
1525
+ this._editing = null;
1526
+ this.dispatchEvent(new CustomEvent(`datagrid-cell-edit`, {
1527
+ detail: {
1528
+ rowKey,
1529
+ colKey,
1530
+ oldValue,
1531
+ newValue
1532
+ },
1533
+ bubbles: true,
1534
+ composed: true
1535
+ }));
1536
+ }
1537
+ _tabToNext(rowKey, curColKey, dir) {
1538
+ const row = this.#rowEntry(rowKey)?.row;
1539
+ if (!row) return;
1540
+ const cols = this._visibleColumns;
1541
+ let next = cols.findIndex((c) => c.key === curColKey) + dir;
1542
+ while (next >= 0 && next < cols.length) {
1543
+ if (this._isEditable(row, cols[next])) {
1544
+ if (this._editing) this._commitEdit();
1545
+ this._suppressBlurCommit = true;
1546
+ this._startEdit(rowKey, cols[next].key);
1547
+ requestAnimationFrame(() => {
1548
+ this._suppressBlurCommit = false;
1549
+ });
1550
+ return;
1551
+ }
1552
+ next += dir;
1553
+ }
1554
+ if (this._editing) this._commitEdit();
1555
+ this._editing = null;
1556
+ this.requestUpdate();
1557
+ }
1558
+ _commitImmediate(rowKey, colKey, value) {
1559
+ const row = this.#rowEntry(rowKey)?.row;
1560
+ if (!row) return;
1561
+ const oldValue = row[colKey];
1562
+ if (oldValue === value) return;
1563
+ row[colKey] = value;
1564
+ this.rows = [...this.rows];
1565
+ this.dispatchEvent(new CustomEvent(`datagrid-cell-edit`, {
1566
+ detail: {
1567
+ rowKey,
1568
+ colKey,
1569
+ oldValue,
1570
+ newValue: value
1571
+ },
1572
+ bubbles: true,
1573
+ composed: true
1574
+ }));
1575
+ }
1576
+ /**
1577
+ * Measure the width a column would need to show its header label and every
1578
+ * row's (formatted) value without truncation, clamped to `minWidth`/`maxWidth`.
1579
+ * Values are measured from their formatted string via canvas `measureText` -
1580
+ * accurate for plain text, an estimate for anything else.
1581
+ */
1582
+ _measureAutoWidth(col) {
1583
+ const cs = getComputedStyle(this);
1584
+ const font = `${cs.fontSize} ${cs.fontFamily}`;
1585
+ const ctx = document.createElement(`canvas`).getContext(`2d`);
1586
+ const cellPad = 24;
1587
+ const headerExtra = 40;
1588
+ const measure = (text) => {
1589
+ if (!ctx) return text.length * 8;
1590
+ ctx.font = font;
1591
+ return Math.ceil(ctx.measureText(text).width);
1592
+ };
1593
+ let maxW = measure(col.label) + headerExtra;
1594
+ for (const r of this.rows) {
1595
+ const v = r[col.key];
1596
+ if (v === null || v === void 0 || v === ``) continue;
1597
+ const w = measure(String(this._formatValue(v, col, r))) + cellPad;
1598
+ if (w > maxW) maxW = w;
1599
+ }
1600
+ const min = col.minWidth ?? 40;
1601
+ const max = col.maxWidth ?? 600;
1602
+ return Math.max(min, Math.min(max, maxW));
1603
+ }
1604
+ _autoSizeColumn(col) {
1605
+ const w = this._measureAutoWidth(col);
1606
+ col.width = w;
1607
+ this.requestUpdate();
1608
+ this.dispatchEvent(new CustomEvent(`datagrid-column-resize`, {
1609
+ detail: {
1610
+ key: col.key,
1611
+ width: w
1612
+ },
1613
+ bubbles: true,
1614
+ composed: true
1615
+ }));
1616
+ }
1617
+ /**
1618
+ * Resize a single column, by key, to fit its contents (header label + every
1619
+ * row's formatted value), animating the width change smoothly. Only works
1620
+ * for plain columns - one with a custom `renderer` can't be measured
1621
+ * reliably from its raw value, so it's left untouched. Returns `false` (and
1622
+ * does nothing) if the column doesn't exist, isn't resizable, or has a
1623
+ * `renderer`.
1624
+ */
1625
+ autoSizeColumn(key) {
1626
+ const col = this.columns.find((c) => c.key === key);
1627
+ if (!col || col.resizable === false || col.renderer) return false;
1628
+ const target = this._measureAutoWidth(col);
1629
+ if ((col.width ?? 150) === target) return true;
1630
+ this._animatingColKeys = new Set(this._animatingColKeys).add(key);
1631
+ requestAnimationFrame(() => {
1632
+ col.width = target;
1633
+ this.requestUpdate();
1634
+ this.dispatchEvent(new CustomEvent(`datagrid-column-resize`, {
1635
+ detail: {
1636
+ key,
1637
+ width: target
1638
+ },
1639
+ bubbles: true,
1640
+ composed: true
1641
+ }));
1642
+ window.setTimeout(() => {
1643
+ const next = new Set(this._animatingColKeys);
1644
+ next.delete(key);
1645
+ this._animatingColKeys = next;
1646
+ }, this._columnResizeAnimationMs());
1647
+ });
1648
+ return true;
1649
+ }
1650
+ _columnResizeAnimationMs() {
1651
+ const raw = getComputedStyle(this).getPropertyValue(`--data-grid-column-resize-duration`).trim();
1652
+ const ms = raw.endsWith(`ms`) ? Number.parseFloat(raw) : raw.endsWith(`s`) ? Number.parseFloat(raw) * 1e3 : Number.parseFloat(raw);
1653
+ return (Number.isFinite(ms) && ms > 0 ? ms : 180) + 30;
1654
+ }
1655
+ _onResizePointerDown(e, col) {
1656
+ e.preventDefault();
1657
+ e.stopPropagation();
1658
+ const startX = e.clientX;
1659
+ const startW = col.width ?? 150;
1660
+ this._resizingColKey = col.key;
1661
+ const onMove = (ev) => {
1662
+ col.width = Math.max(col.minWidth ?? 40, Math.min(col.maxWidth ?? 600, startW + ev.clientX - startX));
1663
+ this.requestUpdate();
1664
+ };
1665
+ const onUp = (ev) => {
1666
+ window.removeEventListener(`pointermove`, onMove);
1667
+ window.removeEventListener(`pointerup`, onUp);
1668
+ this._resizingColKey = void 0;
1669
+ const w = Math.max(col.minWidth ?? 40, Math.min(col.maxWidth ?? 600, startW + ev.clientX - startX));
1670
+ this.dispatchEvent(new CustomEvent(`datagrid-column-resize`, {
1671
+ detail: {
1672
+ key: col.key,
1673
+ width: w
1674
+ },
1675
+ bubbles: true,
1676
+ composed: true
1677
+ }));
1678
+ };
1679
+ window.addEventListener(`pointermove`, onMove);
1680
+ window.addEventListener(`pointerup`, onUp);
1681
+ }
1682
+ /** Height of the row area: the viewport minus the sticky header row inside it. */
1683
+ #bodyHeight() {
1684
+ return this._viewportEl.clientHeight - (this._headerRowEl?.offsetHeight ?? 0);
1685
+ }
1686
+ /** Flat-index window `[start, end)` intersecting the viewport, widened by `overscan` rows each side. */
1687
+ #windowRows(overscan) {
1688
+ const rh = this.rowHeight;
1689
+ return {
1690
+ start: Math.max(0, Math.floor(this._scrollTop / rh) - overscan),
1691
+ end: Math.min(this._flat.length, Math.ceil((this._scrollTop + this._viewportH) / rh) + overscan)
1692
+ };
1693
+ }
1694
+ getVisibleRange() {
1695
+ const { start: startRow, end: endRow } = this.#windowRows(2);
1696
+ return {
1697
+ startRow,
1698
+ endRow,
1699
+ startCol: this._findFirstVisibleCol(this._scrollLeft),
1700
+ endCol: this._findLastVisibleCol(this._scrollLeft + this._viewportW)
1701
+ };
1702
+ }
1703
+ /** Row keys currently within the windowed/visible range, in display order. */
1704
+ renderedKeys() {
1705
+ const { startRow, endRow } = this.getVisibleRange();
1706
+ return this._flat.slice(startRow, endRow).filter((f) => f.type === `row`).map((f) => f.key);
1707
+ }
1708
+ /**
1709
+ * Row keys strictly intersecting the viewport (no overscan), mirroring
1710
+ * `ixfx-grid-list.visibleKeys()` for the `datagrid-viewport-change` detail.
1711
+ */
1712
+ #visibleKeys() {
1713
+ const { start, end } = this.#windowRows(0);
1714
+ return this._flat.slice(start, end).filter((f) => f.type === `row`).map((f) => f.key);
1715
+ }
1716
+ /** Coalesce viewport-change dispatches to at most one per animation frame. */
1717
+ #scheduleViewportChangeEmit() {
1718
+ if (this.#viewportChangeRaf !== 0) return;
1719
+ this.#viewportChangeRaf = requestAnimationFrame(() => {
1720
+ this.#viewportChangeRaf = 0;
1721
+ this.#emitViewportChangeIfChanged();
1722
+ });
1723
+ }
1724
+ /**
1725
+ * Dispatch `datagrid-viewport-change` with the current key sets — unless
1726
+ * neither set changed since the last dispatch, so coalesced frames and
1727
+ * no-op scrolls stay silent.
1728
+ */
1729
+ #emitViewportChangeIfChanged() {
1730
+ const renderedKeys = this.renderedKeys();
1731
+ const visibleKeys = this.#visibleKeys();
1732
+ if (sameKeys(this.#lastViewportRenderedKeys, renderedKeys) && sameKeys(this.#lastViewportVisibleKeys, visibleKeys)) return;
1733
+ this.#lastViewportRenderedKeys = renderedKeys;
1734
+ this.#lastViewportVisibleKeys = visibleKeys;
1735
+ this.dispatchEvent(new CustomEvent(`datagrid-viewport-change`, {
1736
+ detail: {
1737
+ renderedKeys,
1738
+ visibleKeys
1739
+ },
1740
+ bubbles: true,
1741
+ composed: true
1742
+ }));
1743
+ }
1744
+ _findFirstVisibleCol(scrollLeft) {
1745
+ let lo = 0;
1746
+ let hi = this._colOffsets.length - 1;
1747
+ let ans = 0;
1748
+ while (lo <= hi) {
1749
+ const mid = lo + hi >> 1;
1750
+ if (this._colOffsets[mid] <= scrollLeft) {
1751
+ ans = mid;
1752
+ lo = mid + 1;
1753
+ } else hi = mid - 1;
1754
+ }
1755
+ return ans;
1756
+ }
1757
+ _findLastVisibleCol(right) {
1758
+ for (let i = 0; i < this._colOffsets.length; i++) if (this._colOffsets[i] > right) return i;
1759
+ return this._colOffsets.length;
1760
+ }
1761
+ render() {
1762
+ const rh = this.rowHeight;
1763
+ const { start: startRow, end: endRow } = this.#windowRows(2);
1764
+ const visibleFlat = this._flat.slice(startRow, endRow).map((entry, i) => ({
1765
+ entry,
1766
+ absIdx: startRow + i
1767
+ }));
1768
+ const pinnedKey = this._editing?.rowKey ?? this._editingRow;
1769
+ const pinnedIdx = pinnedKey ? this.#flatIndexByKey.get(pinnedKey) : void 0;
1770
+ if (pinnedIdx !== void 0) {
1771
+ if (pinnedIdx >= startRow && pinnedIdx < endRow) visibleFlat.splice(pinnedIdx - startRow, 1);
1772
+ visibleFlat.unshift({
1773
+ entry: this._flat[pinnedIdx],
1774
+ absIdx: pinnedIdx
1775
+ });
1776
+ }
1777
+ const showCheckbox = this.interactionMode === `checked`;
1778
+ return html`
1779
+ <div class="grid ${this.columnMenu === `auto` ? `has-actions` : ``}" role="grid" aria-rowcount=${this._flat.length + 1} aria-colcount=${this._visibleColumns.length} aria-multiselectable=${this.selectionMode === `multiple` ? `true` : `false`}>
1780
+ <div class="viewport scroll-fade-y" @scroll=${this._onScroll}>
1781
+ <div class="header-row scroll-fade-x" role="row" aria-rowindex="1">
1782
+ ${showCheckbox ? html`<div class="header-cell checkbox-col" role="columnheader"></div>` : nothing}
1783
+ ${repeat(this._visibleColumns, (c) => c.key, (col, idx) => html`
1784
+ <div class="header-cell ${this._animatingColKeys.has(col.key) ? `width-animating` : ``}" role="columnheader" aria-sort=${this.sortState?.key === col.key ? this.sortState.direction === `asc` ? `ascending` : `descending` : `none`} style="width:${col.width ?? 150}px" ?drag-source=${this._drag?.active && this._drag.sourceCol.key === col.key}
1785
+ @click=${(e) => this._onHeaderClick(col, e)}
1786
+ @pointerenter=${() => this._tickled.handlePointerEnter(col.key)}>
1787
+ ${col.reorderable !== false ? html`<span class="reorder-handle" draggable="false"
1788
+ @pointerdown=${(e) => this._beginHeaderDrag(e, col, idx)}
1789
+ ><span class="ixfx-handle" data-handle="dotted" data-shape="vertical" part="reorder-handle"></span></span>` : nothing}
1790
+ <span class="header-label" @pointerup=${(e) => this._onHeaderPointerUp(col, e)}>${col.label}</span>
1791
+ ${this.sortState?.key === col.key ? html`<span class="sort-icon"><ixfx-icon name=${this.sortState.direction === `asc` ? `chevron-up` : `chevron-down`} size="12"></ixfx-icon></span>` : nothing}
1792
+ ${col.button !== false ? html`<span class="col-button" @click=${(e) => this._onColButtonClick(col, e)}><ixfx-icon name=${col.button && typeof col.button === `object` && col.button.icon ? col.button.icon : this.columnButtonIcon} size="14"></ixfx-icon></span>` : nothing}
1793
+ ${col.resizable !== false ? html`<span class="resize-handle" ?data-active=${this._resizingColKey === col.key} @pointerdown=${(e) => this._onResizePointerDown(e, col)} @click=${(e) => e.stopPropagation()} @dblclick=${(e) => {
1794
+ e.preventDefault();
1795
+ e.stopPropagation();
1796
+ this._autoSizeColumn(col);
1797
+ }}></span>` : nothing}
1798
+ </div>
1799
+ `)}
1800
+ ${this._drag?.active && this._drag.gap !== void 0 ? html`<div class="drop-caret" style="left:${(() => {
1801
+ const gap = this._drag.gap;
1802
+ if (gap < this._colOffsets.length) return this._colOffsets[gap];
1803
+ const last = this._colOffsets.length - 1;
1804
+ return last >= 0 ? this._colOffsets[last] + (this._visibleColumns[last].width ?? 150) : 0;
1805
+ })()}px"></div>` : nothing}
1806
+ </div>
1807
+ <div class="spacer" style="height:${this._flat.length * rh}px; width:${this._colWidthSum}px">
1808
+ ${repeat(visibleFlat, (f) => f.entry.key, ({ entry, absIdx }) => {
1809
+ if (entry.type === `group`) {
1810
+ const gKey = entry.key.replace(/^__group__/, ``);
1811
+ const collapsed = this._collapsedGroups.has(gKey);
1812
+ return html`<div class="group-row ${collapsed ? `collapsed` : ``}" role="row" aria-rowindex=${absIdx + 2} style="top:${absIdx * rh}px; height:${rh}px" @click=${() => this._toggleGroup(gKey)}><span class="caret"><ixfx-icon name="chevron-down" size="12"></ixfx-icon></span><span role="rowheader" aria-expanded=${collapsed ? `false` : `true`}>${entry.label}</span><span class="count">(${entry.count})</span><span class="divider"></span></div>`;
1813
+ }
1814
+ const row = entry.row;
1815
+ const absIdx2 = entry.idx;
1816
+ const rowKey = entry.key;
1817
+ const selected = this._selectedKeys.has(rowKey);
1818
+ const tickled = this._tickled.isTickled(rowKey);
1819
+ return html`
1820
+ <div class="row item ${selected ? `selected` : ``} ${tickled ? `tickled` : ``} ${this.zebraStripes && absIdx2 % 2 === 1 ? `zebra-odd` : ``} ${this.#enteringRowKeys.has(rowKey) ? `entering` : ``} ${this.#nativeDragging && rowKey === this.#dragOriginKey ? `dragging-origin` : ``}" role="row" aria-rowindex=${absIdx + 2} aria-selected=${selected ? `true` : `false`} style="top:${absIdx * rh}px; height:${rh}px"
1821
+ data-key=${rowKey}
1822
+ draggable=${this.#nativeDrag.draggableAttr}
1823
+ @click=${(e) => {
1824
+ this._onRowClick(rowKey, null, e);
1825
+ this.dispatchEvent(new CustomEvent(`datagrid-cell-click`, {
1826
+ detail: {
1827
+ rowKey,
1828
+ colKey: ``,
1829
+ rowIndex: absIdx2,
1830
+ colIndex: -1
1831
+ },
1832
+ bubbles: true,
1833
+ composed: true
1834
+ }));
1835
+ }}
1836
+ @pointerenter=${() => this._tickled.handlePointerEnter(rowKey)}
1837
+ @pointerleave=${() => this._tickled.clearTickled()}
1838
+ @dblclick=${() => {
1839
+ if (this._visibleColumns[0] && this._isEditable(row, this._visibleColumns[0])) this._startEdit(rowKey, this._visibleColumns[0].key);
1840
+ else this._activateRow(rowKey, row, absIdx2);
1841
+ }}>
1842
+ ${showCheckbox ? html`<div class="checkbox-col"><input type="checkbox" .checked=${selected} @click=${(e) => this._onCheckboxClick(rowKey, e)} /></div>` : nothing}
1843
+ ${repeat(this._visibleColumns, (c) => c.key, (col) => {
1844
+ const val = row[col.key];
1845
+ if (this._isCellEditing(rowKey, col.key)) {
1846
+ const immediate = this.editability === `immediate` || this._editingRow === rowKey;
1847
+ const curVal = immediate ? val : this._editValue ?? val;
1848
+ const effectiveDataType = col.dataType ?? `text`;
1849
+ if (effectiveDataType === `boolean` && (col.editor === `auto` || col.editor === `select` || !col.editor)) return html`<div class="cell" role="gridcell" data-col=${col.key} style="width:${col.width ?? 150}px;justify-content:center;align-items:center"><input type="checkbox" .checked=${Boolean(curVal)} @change=${(e) => {
1850
+ const v = e.target.checked;
1851
+ if (immediate) this._commitImmediate(rowKey, col.key, v);
1852
+ else {
1853
+ this._editValue = v;
1854
+ this._commitEdit();
1855
+ }
1856
+ }} /></div>`;
1857
+ if (col.editor === `select`) {
1858
+ const opts = col.editorOptions ?? [];
1859
+ return html`<div class="cell" role="gridcell" data-col=${col.key} style="width:${col.width ?? 150}px"><select @change=${(e) => {
1860
+ const v = e.target.value;
1861
+ if (immediate) this._commitImmediate(rowKey, col.key, v);
1862
+ else {
1863
+ this._editValue = v;
1864
+ this._commitEdit();
1865
+ }
1866
+ }} @keydown=${(e) => {
1867
+ if (e.key === `Tab`) {
1868
+ e.preventDefault();
1869
+ e.stopPropagation();
1870
+ this._tabToNext(rowKey, col.key, e.shiftKey ? -1 : 1);
1871
+ }
1872
+ if (e.key === `Escape`) {
1873
+ this._editing = null;
1874
+ this._editingRow = null;
1875
+ }
1876
+ }} @blur=${() => {
1877
+ if (this._suppressBlurCommit) return;
1878
+ if (!immediate) this._commitEdit();
1879
+ }}><option value=""></option>${opts.map((o) => {
1880
+ const v = typeof o === `string` ? o : o.value;
1881
+ const l = typeof o === `string` ? o : o.label;
1882
+ return html`<option value=${v} ?selected=${String(curVal) === v}>${l}</option>`;
1883
+ })}</select></div>`;
1884
+ }
1885
+ if (typeof col.editor === `function`) {
1886
+ const factory = col.editor;
1887
+ const ctx = {
1888
+ row,
1889
+ column: col,
1890
+ rowIndex: absIdx2,
1891
+ colIndex: 0,
1892
+ value: curVal,
1893
+ commit: (v) => {
1894
+ if (immediate) this._commitImmediate(rowKey, col.key, v);
1895
+ else {
1896
+ this._editValue = v;
1897
+ this._commitEdit();
1898
+ }
1899
+ },
1900
+ cancel: () => {
1901
+ this._editing = null;
1902
+ this._editingRow = null;
1903
+ }
1904
+ };
1905
+ return html`<div class="cell" role="gridcell" data-col=${col.key} style="width:${col.width ?? 150}px">${factory(ctx)}</div>`;
1906
+ }
1907
+ const inputType = effectiveDataType === `number` ? `number` : `text`;
1908
+ const inputValue = effectiveDataType === `number` ? String(curVal ?? ``) : String(curVal ?? ``);
1909
+ return html`<div class="cell" role="gridcell" data-col=${col.key} style="width:${col.width ?? 150}px"><input type=${inputType} .value=${inputValue} @input=${(e) => this._editValue = e.target.value} @keydown=${(e) => {
1910
+ if (e.key === `Enter`) this._commitEdit();
1911
+ if (e.key === `Escape`) {
1912
+ this._editing = null;
1913
+ this._editingRow = null;
1914
+ }
1915
+ if (e.key === `Tab`) {
1916
+ e.preventDefault();
1917
+ e.stopPropagation();
1918
+ this._tabToNext(rowKey, col.key, e.shiftKey ? -1 : 1);
1919
+ }
1920
+ }} @blur=${() => {
1921
+ if (this._suppressBlurCommit) return;
1922
+ if (!immediate) this._commitEdit();
1923
+ }} autofocus /></div>`;
1924
+ }
1925
+ const displayVal = this._formatValue(val, col, row);
1926
+ const rendered = col.renderer ? col.renderer(row, col, absIdx2) : displayVal;
1927
+ const cellContent = col.renderer ? rendered : displayVal;
1928
+ const textEditable = this._isEditable(row, col) && this._isTextEditor(col);
1929
+ const indent = col.align !== `right` && col.align !== `center` && col.reorderable !== false ? `padding-left:calc(var(--space-s) + var(--data-grid-reorder-handle-width, 10px) + var(--space-xs));` : ``;
1930
+ const canPan = this.cellOverflow === `pan-text` && !col.renderer && (selected || tickled);
1931
+ const look = this._cellLook(row, col.key);
1932
+ const cellStyle = look.textColor === void 0 ? `` : `color:${look.textColor};`;
1933
+ return html`<div class="cell ${textEditable ? `editable-text` : ``} ${this._animatingColKeys.has(col.key) ? `width-animating` : ``}" role="gridcell" data-col=${col.key} style="width:${col.width ?? 150}px; justify-content:${col.align === `right` ? `flex-end` : col.align === `center` ? `center` : `flex-start`}; ${indent} ${cellStyle}"
1934
+ @click=${(e) => {
1935
+ this._lastClickedCell = {
1936
+ rowKey,
1937
+ colKey: col.key
1938
+ };
1939
+ if (col.selectable !== false) {
1940
+ e.stopPropagation();
1941
+ this._onRowClick(rowKey, col, e);
1942
+ this._handleCellSecondClick(rowKey, col);
1943
+ }
1944
+ this.dispatchEvent(new CustomEvent(`datagrid-cell-click`, {
1945
+ detail: {
1946
+ rowKey,
1947
+ colKey: col.key,
1948
+ rowIndex: absIdx2,
1949
+ colIndex: this.columns.indexOf(col)
1950
+ },
1951
+ bubbles: true,
1952
+ composed: true
1953
+ }));
1954
+ }}
1955
+ >${look.icon === void 0 ? nothing : html`<span class="cell-icon" part="icon" style=${look.iconColor === void 0 ? nothing : `color:${look.iconColor}`}>${look.icon}</span>`}${canPan ? html`<ixfx-pan-text mode="loop">${cellContent}</ixfx-pan-text>` : cellContent}${look.badge === void 0 ? nothing : html`<span class="cell-badge" part="badge">${look.badge}</span>`}</div>`;
1956
+ })}
1957
+ </div>
1958
+ `;
1959
+ })}
1960
+ </div>
1961
+ </div>
1962
+ ${this.#renderColumnMenu()}
1963
+ ${this.search.renderField()}
1964
+ </div>
1965
+ `;
1966
+ }
1967
+ /** Built-in column/group menu, pinned over the header row's end. Only for `columnMenu: auto`. */
1968
+ #renderColumnMenu() {
1969
+ if (this.columnMenu !== `auto`) return nothing;
1970
+ return html`
1971
+ <div class="header-actions">
1972
+ <ixfx-menu-trigger label="⚙" placement="bottom-end" @menu-command=${(e) => {
1973
+ const cmd = e.detail.command;
1974
+ const checked = e.detail.checked;
1975
+ const value = e.detail.value;
1976
+ if (cmd?.startsWith(`col:`)) {
1977
+ const key = cmd.slice(4);
1978
+ const col = this.columns.find((c) => c.key === key);
1979
+ if (col) this._onVisibilityToggle(col, !!checked);
1980
+ e.stopPropagation();
1981
+ return;
1982
+ }
1983
+ if (cmd === `groupBy`) {
1984
+ const v = value ?? ``;
1985
+ this.groupBy = v || null;
1986
+ this._collapsedGroups = /* @__PURE__ */ new Set();
1987
+ this.dispatchEvent(new CustomEvent(`datagrid-group-change`, {
1988
+ detail: { groupBy: this.groupBy },
1989
+ bubbles: true,
1990
+ composed: true
1991
+ }));
1992
+ e.stopPropagation();
1993
+ }
1994
+ }}>
1995
+ <ixfx-menu-container slot="menu">
1996
+ <ixfx-menu-header label="Columns"></ixfx-menu-header>
1997
+ ${repeat(this.columns, (c) => c.key, (col) => html`
1998
+ <ixfx-menu-check-item command="col:${col.key}" label=${col.label} ?checked=${col.visible !== false}></ixfx-menu-check-item>
1999
+ `)}
2000
+ ${this.allowGrouping ? html`
2001
+ <ixfx-menu-separator></ixfx-menu-separator>
2002
+ <ixfx-menu-header label="Group by"></ixfx-menu-header>
2003
+ <ixfx-menu-radio-group name="groupBy" value=${typeof this.groupBy === `string` ? this.groupBy : ``}>
2004
+ <ixfx-menu-radio-item value="" label="None"></ixfx-menu-radio-item>
2005
+ ${this.columns.filter((c) => c.groupable !== false && (!this.groupableColumns || this.groupableColumns.includes(c.key))).map((col) => html`
2006
+ <ixfx-menu-radio-item value=${col.key} label=${col.label}></ixfx-menu-radio-item>
2007
+ `)}
2008
+ </ixfx-menu-radio-group>
2009
+ ` : nothing}
2010
+ </ixfx-menu-container>
2011
+ </ixfx-menu-trigger>
2012
+ </div>`;
2013
+ }
2014
+ };
2015
+ __decorate([property({ type: Array })], DataGridElement.prototype, "columns", void 0);
2016
+ __decorate([property({ type: Array })], DataGridElement.prototype, "rows", void 0);
2017
+ __decorate([property({
2018
+ type: String,
2019
+ attribute: `selection-mode`
2020
+ })], DataGridElement.prototype, "selectionMode", void 0);
2021
+ __decorate([property({
2022
+ type: String,
2023
+ attribute: `interaction-mode`
2024
+ })], DataGridElement.prototype, "interactionMode", void 0);
2025
+ __decorate([property({ attribute: false })], DataGridElement.prototype, "sortState", void 0);
2026
+ __decorate([property({
2027
+ type: Number,
2028
+ attribute: `row-height`
2029
+ })], DataGridElement.prototype, "rowHeight", void 0);
2030
+ __decorate([property({ attribute: false })], DataGridElement.prototype, "getRowKey", void 0);
2031
+ __decorate([property({ attribute: false })], DataGridElement.prototype, "checkboxRenderer", void 0);
2032
+ __decorate([property({ attribute: false })], DataGridElement.prototype, "editable", void 0);
2033
+ __decorate([property({
2034
+ type: String,
2035
+ reflect: true
2036
+ })], DataGridElement.prototype, "editability", void 0);
2037
+ __decorate([property({
2038
+ type: String,
2039
+ attribute: `cell-overflow`
2040
+ })], DataGridElement.prototype, "cellOverflow", void 0);
2041
+ __decorate([property({
2042
+ type: Boolean,
2043
+ attribute: `zebra-stripes`,
2044
+ reflect: true
2045
+ })], DataGridElement.prototype, "zebraStripes", void 0);
2046
+ __decorate([property({ attribute: false })], DataGridElement.prototype, "groupBy", void 0);
2047
+ __decorate([property({
2048
+ type: Boolean,
2049
+ attribute: `allow-grouping`
2050
+ })], DataGridElement.prototype, "allowGrouping", void 0);
2051
+ __decorate([property({ attribute: false })], DataGridElement.prototype, "groupLabel", void 0);
2052
+ __decorate([property({ attribute: false })], DataGridElement.prototype, "groupableColumns", void 0);
2053
+ __decorate([property({
2054
+ type: Boolean,
2055
+ attribute: `cross-group-selection`,
2056
+ reflect: true
2057
+ })], DataGridElement.prototype, "crossGroupSelection", void 0);
2058
+ __decorate([property({
2059
+ type: String,
2060
+ attribute: `column-menu`
2061
+ })], DataGridElement.prototype, "columnMenu", void 0);
2062
+ __decorate([property({ attribute: false })], DataGridElement.prototype, "globalFormatter", void 0);
2063
+ __decorate([property({
2064
+ type: String,
2065
+ attribute: `text-color-column`
2066
+ })], DataGridElement.prototype, "textColorColumn", void 0);
2067
+ __decorate([property({
2068
+ type: String,
2069
+ attribute: `icon-column`
2070
+ })], DataGridElement.prototype, "iconColumn", void 0);
2071
+ __decorate([property({
2072
+ type: String,
2073
+ attribute: `badge-column`
2074
+ })], DataGridElement.prototype, "badgeColumn", void 0);
2075
+ __decorate([property({ attribute: false })], DataGridElement.prototype, "filterPredicate", void 0);
2076
+ __decorate([property({ attribute: false })], DataGridElement.prototype, "rowSearchText", void 0);
2077
+ __decorate([property({
2078
+ type: String,
2079
+ attribute: `search-modes`,
2080
+ reflect: true
2081
+ })], DataGridElement.prototype, "searchModes", void 0);
2082
+ __decorate([property({
2083
+ type: Boolean,
2084
+ attribute: `items-draggable`,
2085
+ reflect: true
2086
+ })], DataGridElement.prototype, "itemsDraggable", void 0);
2087
+ __decorate([property({ attribute: false })], DataGridElement.prototype, "dragPreviewRenderer", void 0);
2088
+ __decorate([property({ attribute: false })], DataGridElement.prototype, "dragPreviewSource", void 0);
2089
+ __decorate([property({
2090
+ type: String,
2091
+ attribute: `column-button-icon`
2092
+ })], DataGridElement.prototype, "columnButtonIcon", void 0);
2093
+ __decorate([state()], DataGridElement.prototype, "_resizingColKey", void 0);
2094
+ __decorate([state()], DataGridElement.prototype, "_animatingColKeys", void 0);
2095
+ __decorate([state()], DataGridElement.prototype, "_selectedKeys", void 0);
2096
+ __decorate([state()], DataGridElement.prototype, "_viewportH", void 0);
2097
+ __decorate([state()], DataGridElement.prototype, "_viewportW", void 0);
2098
+ __decorate([state()], DataGridElement.prototype, "_collapsedGroups", void 0);
2099
+ __decorate([state()], DataGridElement.prototype, "_editing", void 0);
2100
+ __decorate([state()], DataGridElement.prototype, "_editingRow", void 0);
2101
+ __decorate([state()], DataGridElement.prototype, "_lastClick", void 0);
2102
+ __decorate([state()], DataGridElement.prototype, "_lastClickedCell", void 0);
2103
+ __decorate([state()], DataGridElement.prototype, "_editValue", void 0);
2104
+ __decorate([query(`.viewport`)], DataGridElement.prototype, "_viewportEl", void 0);
2105
+ __decorate([query(`.header-row`)], DataGridElement.prototype, "_headerRowEl", void 0);
2106
+ DataGridElement = __decorate([safeCustomElement(`ixfx-data-grid`)], DataGridElement);
2107
+ //#endregion
2108
+ export { DataGridElement as t };