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