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