@crisp-ui-kit/crisp 0.2.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (590) hide show
  1. package/README.md +20 -0
  2. package/dist/accordion.css +9 -1
  3. package/dist/appshell.css +12 -4
  4. package/dist/authshell.css +57 -0
  5. package/dist/authshell.d.ts +7 -0
  6. package/dist/avatargroup.css +2 -2
  7. package/dist/badge.css +4 -4
  8. package/dist/barchart.css +13 -5
  9. package/dist/base.css +25 -0
  10. package/dist/board.css +9 -1
  11. package/dist/bulkbar.css +11 -3
  12. package/dist/button.css +16 -2
  13. package/dist/button.d.ts +2 -1
  14. package/dist/card.css +8 -0
  15. package/dist/checkbox.css +17 -1
  16. package/dist/cjs/components/accordion/accordion.js +69 -0
  17. package/dist/cjs/components/accordion/accordion.recipe.js +26 -0
  18. package/dist/cjs/components/accordion/index.js +7 -0
  19. package/dist/cjs/components/avatar/avatar.js +44 -0
  20. package/dist/cjs/components/avatar/avatar.recipe.js +42 -0
  21. package/dist/cjs/components/avatar/index.js +7 -0
  22. package/dist/cjs/components/avatargroup/avatargroup.js +52 -0
  23. package/dist/cjs/components/avatargroup/avatargroup.recipe.js +18 -0
  24. package/dist/cjs/components/avatargroup/index.js +7 -0
  25. package/dist/cjs/components/badge/badge.js +44 -0
  26. package/dist/cjs/components/badge/badge.recipe.js +68 -0
  27. package/dist/cjs/components/badge/index.js +7 -0
  28. package/dist/cjs/components/barchart/barchart.js +45 -0
  29. package/dist/cjs/components/barchart/barchart.recipe.js +34 -0
  30. package/dist/cjs/components/barchart/index.js +7 -0
  31. package/dist/cjs/components/breadcrumb/breadcrumb.js +53 -0
  32. package/dist/cjs/components/breadcrumb/breadcrumb.recipe.js +21 -0
  33. package/dist/cjs/components/breadcrumb/index.js +7 -0
  34. package/dist/cjs/components/bulkbar/bulkbar.js +53 -0
  35. package/dist/cjs/components/bulkbar/bulkbar.recipe.js +31 -0
  36. package/dist/cjs/components/bulkbar/index.js +8 -0
  37. package/dist/cjs/components/button/button.js +62 -0
  38. package/dist/cjs/components/button/button.recipe.js +58 -0
  39. package/dist/cjs/components/button/index.js +7 -0
  40. package/dist/cjs/components/callout/callout.js +25 -0
  41. package/dist/cjs/components/callout/callout.recipe.js +29 -0
  42. package/dist/cjs/components/callout/index.js +7 -0
  43. package/dist/cjs/components/card/card.js +54 -0
  44. package/dist/cjs/components/card/card.recipe.js +31 -0
  45. package/dist/cjs/components/card/index.js +10 -0
  46. package/dist/cjs/components/checkbox/checkbox.js +56 -0
  47. package/dist/cjs/components/checkbox/checkbox.recipe.js +34 -0
  48. package/dist/cjs/components/checkbox/index.js +7 -0
  49. package/dist/cjs/components/columncalc/columncalc.js +139 -0
  50. package/dist/cjs/components/columncalc/columncalc.recipe.js +33 -0
  51. package/dist/cjs/components/columncalc/index.js +10 -0
  52. package/dist/cjs/components/contextmenu/contextmenu.js +78 -0
  53. package/dist/cjs/components/contextmenu/contextmenu.recipe.js +24 -0
  54. package/dist/cjs/components/contextmenu/index.js +7 -0
  55. package/dist/cjs/components/datatable/datatable.js +508 -0
  56. package/dist/cjs/components/datatable/datatable.recipe.js +113 -0
  57. package/dist/cjs/components/datatable/index.js +7 -0
  58. package/dist/cjs/components/datepicker/datepicker.js +153 -0
  59. package/dist/cjs/components/datepicker/datepicker.recipe.js +180 -0
  60. package/dist/cjs/components/datepicker/index.js +19 -0
  61. package/dist/cjs/components/datepickerfield/datepickerfield.js +85 -0
  62. package/dist/cjs/components/datepickerfield/datepickerfield.recipe.js +44 -0
  63. package/dist/cjs/components/datepickerfield/index.js +7 -0
  64. package/dist/cjs/components/dialog/dialog.js +86 -0
  65. package/dist/cjs/components/dialog/dialog.recipe.js +35 -0
  66. package/dist/cjs/components/dialog/index.js +9 -0
  67. package/dist/cjs/components/divider/divider.js +10 -0
  68. package/dist/cjs/components/divider/divider.recipe.js +17 -0
  69. package/dist/cjs/components/divider/index.js +7 -0
  70. package/dist/cjs/components/donutchart/donutchart.js +41 -0
  71. package/dist/cjs/components/donutchart/donutchart.recipe.js +30 -0
  72. package/dist/cjs/components/donutchart/index.js +7 -0
  73. package/dist/cjs/components/drawer/drawer.js +80 -0
  74. package/dist/cjs/components/drawer/drawer.recipe.js +30 -0
  75. package/dist/cjs/components/drawer/index.js +9 -0
  76. package/dist/cjs/components/emptystate/emptystate.js +10 -0
  77. package/dist/cjs/components/emptystate/emptystate.recipe.js +34 -0
  78. package/dist/cjs/components/emptystate/index.js +7 -0
  79. package/dist/cjs/components/formfield/formfield.js +50 -0
  80. package/dist/cjs/components/formfield/formfield.recipe.js +22 -0
  81. package/dist/cjs/components/formfield/index.js +7 -0
  82. package/dist/cjs/components/input/index.js +7 -0
  83. package/dist/cjs/components/input/input.js +47 -0
  84. package/dist/cjs/components/input/input.recipe.js +59 -0
  85. package/dist/cjs/components/kbd/index.js +7 -0
  86. package/dist/cjs/components/kbd/kbd.js +44 -0
  87. package/dist/cjs/components/kbd/kbd.recipe.js +23 -0
  88. package/dist/cjs/components/linechart/index.js +7 -0
  89. package/dist/cjs/components/linechart/linechart.js +57 -0
  90. package/dist/cjs/components/linechart/linechart.recipe.js +35 -0
  91. package/dist/cjs/components/menu/index.js +14 -0
  92. package/dist/cjs/components/menu/menu-keyboard.js +117 -0
  93. package/dist/cjs/components/menu/menu.js +144 -0
  94. package/dist/cjs/components/menu/menu.recipe.js +54 -0
  95. package/dist/cjs/components/multiselect/index.js +7 -0
  96. package/dist/cjs/components/multiselect/multiselect.js +161 -0
  97. package/dist/cjs/components/multiselect/multiselect.recipe.js +71 -0
  98. package/dist/cjs/components/popover/index.js +7 -0
  99. package/dist/cjs/components/popover/popover.js +106 -0
  100. package/dist/cjs/components/popover/popover.recipe.js +21 -0
  101. package/dist/cjs/components/progress/index.js +7 -0
  102. package/dist/cjs/components/progress/progress.js +26 -0
  103. package/dist/cjs/components/progress/progress.recipe.js +20 -0
  104. package/dist/cjs/components/radiogroup/index.js +8 -0
  105. package/dist/cjs/components/radiogroup/radiogroup.js +83 -0
  106. package/dist/cjs/components/radiogroup/radiogroup.recipe.js +40 -0
  107. package/dist/cjs/components/resizablepanel/index.js +12 -0
  108. package/dist/cjs/components/resizablepanel/resizablepanel.js +176 -0
  109. package/dist/cjs/components/resizablepanel/resizablepanel.recipe.js +69 -0
  110. package/dist/cjs/components/segmented/index.js +7 -0
  111. package/dist/cjs/components/segmented/segmented.js +95 -0
  112. package/dist/cjs/components/segmented/segmented.recipe.js +23 -0
  113. package/dist/cjs/components/select/index.js +7 -0
  114. package/dist/cjs/components/select/select.js +180 -0
  115. package/dist/cjs/components/select/select.recipe.js +79 -0
  116. package/dist/cjs/components/skeleton/index.js +7 -0
  117. package/dist/cjs/components/skeleton/skeleton.js +13 -0
  118. package/dist/cjs/components/skeleton/skeleton.recipe.js +17 -0
  119. package/dist/cjs/components/spinner/index.js +7 -0
  120. package/dist/cjs/components/spinner/spinner.js +15 -0
  121. package/dist/cjs/components/spinner/spinner.recipe.js +16 -0
  122. package/dist/cjs/components/stagebar/index.js +7 -0
  123. package/dist/cjs/components/stagebar/stagebar.js +26 -0
  124. package/dist/cjs/components/stagebar/stagebar.recipe.js +17 -0
  125. package/dist/cjs/components/statcard/index.js +7 -0
  126. package/dist/cjs/components/statcard/statcard.js +15 -0
  127. package/dist/cjs/components/statcard/statcard.recipe.js +37 -0
  128. package/dist/cjs/components/switch/index.js +7 -0
  129. package/dist/cjs/components/switch/switch.js +58 -0
  130. package/dist/cjs/components/switch/switch.recipe.js +29 -0
  131. package/dist/cjs/components/table/index.js +14 -0
  132. package/dist/cjs/components/table/table.js +66 -0
  133. package/dist/cjs/components/table/table.recipe.js +43 -0
  134. package/dist/cjs/components/tabletoolbar/index.js +20 -0
  135. package/dist/cjs/components/tabletoolbar/tabletoolbar.chrome.js +84 -0
  136. package/dist/cjs/components/tabletoolbar/tabletoolbar.js +237 -0
  137. package/dist/cjs/components/tabletoolbar/tabletoolbar.recipe.js +26 -0
  138. package/dist/cjs/components/tabs/index.js +7 -0
  139. package/dist/cjs/components/tabs/tabs.js +106 -0
  140. package/dist/cjs/components/tabs/tabs.recipe.js +36 -0
  141. package/dist/cjs/components/textarea/index.js +7 -0
  142. package/dist/cjs/components/textarea/textarea.js +56 -0
  143. package/dist/cjs/components/textarea/textarea.recipe.js +46 -0
  144. package/dist/cjs/components/toast/index.js +8 -0
  145. package/dist/cjs/components/toast/toast.js +95 -0
  146. package/dist/cjs/components/toast/toast.recipe.js +31 -0
  147. package/dist/cjs/components/tooltip/index.js +7 -0
  148. package/dist/cjs/components/tooltip/tooltip.js +95 -0
  149. package/dist/cjs/components/tooltip/tooltip.recipe.js +16 -0
  150. package/dist/cjs/components/wheelpicker/index.js +7 -0
  151. package/dist/cjs/components/wheelpicker/wheelpicker.js +134 -0
  152. package/dist/cjs/components/wheelpicker/wheelpicker.recipe.js +95 -0
  153. package/dist/cjs/core/cx.js +8 -0
  154. package/dist/cjs/core/define.js +6 -0
  155. package/dist/cjs/core/index.js +11 -0
  156. package/dist/cjs/core/primitive.js +49 -0
  157. package/dist/cjs/core/resolve.js +44 -0
  158. package/dist/cjs/core/types.js +2 -0
  159. package/dist/cjs/index.js +92 -0
  160. package/dist/cjs/package.json +3 -0
  161. package/dist/cjs/product/appshell/appshell.js +104 -0
  162. package/dist/cjs/product/appshell/appshell.recipe.js +30 -0
  163. package/dist/cjs/product/appshell/index.js +7 -0
  164. package/dist/cjs/product/authshell/authshell.js +21 -0
  165. package/dist/cjs/product/authshell/authshell.recipe.js +67 -0
  166. package/dist/cjs/product/authshell/index.js +7 -0
  167. package/dist/cjs/product/board/board.js +78 -0
  168. package/dist/cjs/product/board/board.recipe.js +31 -0
  169. package/dist/cjs/product/board/index.js +7 -0
  170. package/dist/cjs/product/command/command.js +238 -0
  171. package/dist/cjs/product/command/command.recipe.js +47 -0
  172. package/dist/cjs/product/command/index.js +8 -0
  173. package/dist/cjs/product/entitycard/entitycard.js +14 -0
  174. package/dist/cjs/product/entitycard/entitycard.recipe.js +32 -0
  175. package/dist/cjs/product/entitycard/index.js +7 -0
  176. package/dist/cjs/product/flowcanvas/flowcanvas.js +121 -0
  177. package/dist/cjs/product/flowcanvas/flowcanvas.recipe.js +28 -0
  178. package/dist/cjs/product/flowcanvas/index.js +7 -0
  179. package/dist/cjs/product/inbox/inbox.js +16 -0
  180. package/dist/cjs/product/inbox/inbox.recipe.js +46 -0
  181. package/dist/cjs/product/inbox/index.js +7 -0
  182. package/dist/cjs/product/listpage/index.js +8 -0
  183. package/dist/cjs/product/listpage/listpage.js +25 -0
  184. package/dist/cjs/product/listpage/listpage.recipe.js +46 -0
  185. package/dist/cjs/product/navitem/index.js +5 -0
  186. package/dist/cjs/product/navitem/navitem.js +44 -0
  187. package/dist/cjs/product/navitem/navitem.recipe.js +36 -0
  188. package/dist/cjs/product/onboarding/index.js +7 -0
  189. package/dist/cjs/product/onboarding/onboarding.js +17 -0
  190. package/dist/cjs/product/onboarding/onboarding.recipe.js +44 -0
  191. package/dist/cjs/product/recordpage/index.js +7 -0
  192. package/dist/cjs/product/recordpage/recordpage.js +409 -0
  193. package/dist/cjs/product/recordpage/recordpage.recipe.js +162 -0
  194. package/dist/cjs/product/recordpanel/index.js +7 -0
  195. package/dist/cjs/product/recordpanel/recordpanel.js +83 -0
  196. package/dist/cjs/product/recordpanel/recordpanel.recipe.js +51 -0
  197. package/dist/cjs/product/recordstable/company-logos.js +29 -0
  198. package/dist/cjs/product/recordstable/index.js +10 -0
  199. package/dist/cjs/product/recordstable/recordstable.js +1092 -0
  200. package/dist/cjs/product/recordstable/recordstable.recipe.js +152 -0
  201. package/dist/cjs/product/recordstable/recordstable.tag.js +21 -0
  202. package/dist/cjs/product/richtext/index.js +7 -0
  203. package/dist/cjs/product/richtext/richtext.js +147 -0
  204. package/dist/cjs/product/richtext/richtext.recipe.js +43 -0
  205. package/dist/cjs/product/settingssection/index.js +7 -0
  206. package/dist/cjs/product/settingssection/settingssection.js +14 -0
  207. package/dist/cjs/product/settingssection/settingssection.recipe.js +23 -0
  208. package/dist/cjs/product/settingsshell/index.js +7 -0
  209. package/dist/cjs/product/settingsshell/settingsshell.js +59 -0
  210. package/dist/cjs/product/settingsshell/settingsshell.recipe.js +46 -0
  211. package/dist/cjs/product/sidebar/index.js +13 -0
  212. package/dist/cjs/product/sidebar/sidebar.js +148 -0
  213. package/dist/cjs/product/sidebar/sidebar.recipe.js +46 -0
  214. package/dist/cjs/product/tasklist/index.js +7 -0
  215. package/dist/cjs/product/tasklist/tasklist.js +19 -0
  216. package/dist/cjs/product/tasklist/tasklist.recipe.js +38 -0
  217. package/dist/cjs/product/trialbanner/index.js +7 -0
  218. package/dist/cjs/product/trialbanner/trialbanner.js +13 -0
  219. package/dist/cjs/product/trialbanner/trialbanner.recipe.js +32 -0
  220. package/dist/cjs/product/viewtoggle/index.js +7 -0
  221. package/dist/cjs/product/viewtoggle/viewtoggle.js +55 -0
  222. package/dist/cjs/product/viewtoggle/viewtoggle.recipe.js +27 -0
  223. package/dist/cjs/tokens/contract.js +21 -0
  224. package/dist/cjs/tokens/elevation.js +47 -0
  225. package/dist/cjs/tokens/index.js +18 -0
  226. package/dist/cjs/tokens/palette.js +63 -0
  227. package/dist/cjs/tokens/scale.js +70 -0
  228. package/dist/cjs/tokens/semantic.js +57 -0
  229. package/dist/columncalc.css +26 -0
  230. package/dist/columncalc.d.ts +7 -0
  231. package/dist/command.css +34 -1
  232. package/dist/contextmenu.css +11 -3
  233. package/dist/datatable.css +106 -0
  234. package/dist/datatable.d.ts +7 -0
  235. package/dist/datepicker.css +165 -19
  236. package/dist/datepicker.d.ts +2 -2
  237. package/dist/datepickerfield.css +27 -0
  238. package/dist/datepickerfield.d.ts +7 -0
  239. package/dist/dialog.css +11 -3
  240. package/dist/donutchart.css +8 -0
  241. package/dist/drawer.css +13 -5
  242. package/dist/entitycard.css +10 -2
  243. package/dist/esm/components/accordion/accordion.js +33 -0
  244. package/dist/esm/components/accordion/accordion.recipe.js +23 -0
  245. package/dist/esm/components/accordion/index.js +2 -0
  246. package/dist/esm/components/avatar/avatar.js +8 -0
  247. package/dist/esm/components/avatar/avatar.recipe.js +39 -0
  248. package/dist/esm/components/avatar/index.js +2 -0
  249. package/dist/esm/components/avatargroup/avatargroup.js +16 -0
  250. package/dist/esm/components/avatargroup/avatargroup.recipe.js +15 -0
  251. package/dist/esm/components/avatargroup/index.js +2 -0
  252. package/dist/esm/components/badge/badge.js +8 -0
  253. package/dist/esm/components/badge/badge.recipe.js +65 -0
  254. package/dist/esm/components/badge/index.js +2 -0
  255. package/dist/esm/components/barchart/barchart.js +42 -0
  256. package/dist/esm/components/barchart/barchart.recipe.js +31 -0
  257. package/dist/esm/components/barchart/index.js +2 -0
  258. package/dist/esm/components/breadcrumb/breadcrumb.js +17 -0
  259. package/dist/esm/components/breadcrumb/breadcrumb.recipe.js +18 -0
  260. package/dist/esm/components/breadcrumb/index.js +2 -0
  261. package/dist/esm/components/bulkbar/bulkbar.js +16 -0
  262. package/dist/esm/components/bulkbar/bulkbar.recipe.js +28 -0
  263. package/dist/esm/components/bulkbar/index.js +2 -0
  264. package/dist/esm/components/button/button.js +26 -0
  265. package/dist/esm/components/button/button.recipe.js +55 -0
  266. package/dist/esm/components/button/index.js +2 -0
  267. package/dist/esm/components/callout/callout.js +22 -0
  268. package/dist/esm/components/callout/callout.recipe.js +26 -0
  269. package/dist/esm/components/callout/index.js +2 -0
  270. package/dist/esm/components/card/card.js +15 -0
  271. package/dist/esm/components/card/card.recipe.js +28 -0
  272. package/dist/esm/components/card/index.js +2 -0
  273. package/dist/esm/components/checkbox/checkbox.js +20 -0
  274. package/dist/esm/components/checkbox/checkbox.recipe.js +31 -0
  275. package/dist/esm/components/checkbox/index.js +2 -0
  276. package/dist/esm/components/columncalc/columncalc.js +100 -0
  277. package/dist/esm/components/columncalc/columncalc.recipe.js +30 -0
  278. package/dist/esm/components/columncalc/index.js +1 -0
  279. package/dist/esm/components/contextmenu/contextmenu.js +42 -0
  280. package/dist/esm/components/contextmenu/contextmenu.recipe.js +21 -0
  281. package/dist/esm/components/contextmenu/index.js +2 -0
  282. package/dist/esm/components/datatable/datatable.js +472 -0
  283. package/dist/esm/components/datatable/datatable.recipe.js +110 -0
  284. package/dist/esm/components/datatable/index.js +2 -0
  285. package/dist/esm/components/datepicker/datepicker.js +108 -0
  286. package/dist/esm/components/datepicker/datepicker.recipe.js +177 -0
  287. package/dist/esm/components/datepicker/index.js +2 -0
  288. package/dist/esm/components/datepickerfield/datepickerfield.js +49 -0
  289. package/dist/esm/components/datepickerfield/datepickerfield.recipe.js +41 -0
  290. package/dist/esm/components/datepickerfield/index.js +2 -0
  291. package/dist/esm/components/dialog/dialog.js +48 -0
  292. package/dist/esm/components/dialog/dialog.recipe.js +32 -0
  293. package/dist/esm/components/dialog/index.js +2 -0
  294. package/dist/esm/components/divider/divider.js +7 -0
  295. package/dist/esm/components/divider/divider.recipe.js +14 -0
  296. package/dist/esm/components/divider/index.js +2 -0
  297. package/dist/esm/components/donutchart/donutchart.js +38 -0
  298. package/dist/esm/components/donutchart/donutchart.recipe.js +27 -0
  299. package/dist/esm/components/donutchart/index.js +2 -0
  300. package/dist/esm/components/drawer/drawer.js +42 -0
  301. package/dist/esm/components/drawer/drawer.recipe.js +27 -0
  302. package/dist/esm/components/drawer/index.js +2 -0
  303. package/dist/esm/components/emptystate/emptystate.js +7 -0
  304. package/dist/esm/components/emptystate/emptystate.recipe.js +31 -0
  305. package/dist/esm/components/emptystate/index.js +2 -0
  306. package/dist/esm/components/formfield/formfield.js +14 -0
  307. package/dist/esm/components/formfield/formfield.recipe.js +19 -0
  308. package/dist/esm/components/formfield/index.js +2 -0
  309. package/dist/esm/components/input/index.js +2 -0
  310. package/dist/esm/components/input/input.js +11 -0
  311. package/dist/esm/components/input/input.recipe.js +56 -0
  312. package/dist/esm/components/kbd/index.js +2 -0
  313. package/dist/esm/components/kbd/kbd.js +8 -0
  314. package/dist/esm/components/kbd/kbd.recipe.js +20 -0
  315. package/dist/esm/components/linechart/index.js +2 -0
  316. package/dist/esm/components/linechart/linechart.js +54 -0
  317. package/dist/esm/components/linechart/linechart.recipe.js +32 -0
  318. package/dist/esm/components/menu/index.js +3 -0
  319. package/dist/esm/components/menu/menu-keyboard.js +80 -0
  320. package/dist/esm/components/menu/menu.js +104 -0
  321. package/dist/esm/components/menu/menu.recipe.js +51 -0
  322. package/dist/esm/components/multiselect/index.js +2 -0
  323. package/dist/esm/components/multiselect/multiselect.js +125 -0
  324. package/dist/esm/components/multiselect/multiselect.recipe.js +68 -0
  325. package/dist/esm/components/popover/index.js +2 -0
  326. package/dist/esm/components/popover/popover.js +70 -0
  327. package/dist/esm/components/popover/popover.recipe.js +18 -0
  328. package/dist/esm/components/progress/index.js +2 -0
  329. package/dist/esm/components/progress/progress.js +23 -0
  330. package/dist/esm/components/progress/progress.recipe.js +17 -0
  331. package/dist/esm/components/radiogroup/index.js +2 -0
  332. package/dist/esm/components/radiogroup/radiogroup.js +46 -0
  333. package/dist/esm/components/radiogroup/radiogroup.recipe.js +37 -0
  334. package/dist/esm/components/resizablepanel/index.js +2 -0
  335. package/dist/esm/components/resizablepanel/resizablepanel.js +135 -0
  336. package/dist/esm/components/resizablepanel/resizablepanel.recipe.js +66 -0
  337. package/dist/esm/components/segmented/index.js +2 -0
  338. package/dist/esm/components/segmented/segmented.js +59 -0
  339. package/dist/esm/components/segmented/segmented.recipe.js +20 -0
  340. package/dist/esm/components/select/index.js +2 -0
  341. package/dist/esm/components/select/select.js +144 -0
  342. package/dist/esm/components/select/select.recipe.js +76 -0
  343. package/dist/esm/components/skeleton/index.js +2 -0
  344. package/dist/esm/components/skeleton/skeleton.js +10 -0
  345. package/dist/esm/components/skeleton/skeleton.recipe.js +14 -0
  346. package/dist/esm/components/spinner/index.js +2 -0
  347. package/dist/esm/components/spinner/spinner.js +12 -0
  348. package/dist/esm/components/spinner/spinner.recipe.js +13 -0
  349. package/dist/esm/components/stagebar/index.js +2 -0
  350. package/dist/esm/components/stagebar/stagebar.js +23 -0
  351. package/dist/esm/components/stagebar/stagebar.recipe.js +14 -0
  352. package/dist/esm/components/statcard/index.js +2 -0
  353. package/dist/esm/components/statcard/statcard.js +12 -0
  354. package/dist/esm/components/statcard/statcard.recipe.js +34 -0
  355. package/dist/esm/components/switch/index.js +2 -0
  356. package/dist/esm/components/switch/switch.js +22 -0
  357. package/dist/esm/components/switch/switch.recipe.js +26 -0
  358. package/dist/esm/components/table/index.js +2 -0
  359. package/dist/esm/components/table/table.js +30 -0
  360. package/dist/esm/components/table/table.recipe.js +40 -0
  361. package/dist/esm/components/tabletoolbar/index.js +2 -0
  362. package/dist/esm/components/tabletoolbar/tabletoolbar.chrome.js +79 -0
  363. package/dist/esm/components/tabletoolbar/tabletoolbar.js +199 -0
  364. package/dist/esm/components/tabletoolbar/tabletoolbar.recipe.js +23 -0
  365. package/dist/esm/components/tabs/index.js +2 -0
  366. package/dist/esm/components/tabs/tabs.js +70 -0
  367. package/dist/esm/components/tabs/tabs.recipe.js +33 -0
  368. package/dist/esm/components/textarea/index.js +2 -0
  369. package/dist/esm/components/textarea/textarea.js +20 -0
  370. package/dist/esm/components/textarea/textarea.recipe.js +43 -0
  371. package/dist/esm/components/toast/index.js +2 -0
  372. package/dist/esm/components/toast/toast.js +58 -0
  373. package/dist/esm/components/toast/toast.recipe.js +28 -0
  374. package/dist/esm/components/tooltip/index.js +2 -0
  375. package/dist/esm/components/tooltip/tooltip.js +59 -0
  376. package/dist/esm/components/tooltip/tooltip.recipe.js +13 -0
  377. package/dist/esm/components/wheelpicker/index.js +2 -0
  378. package/dist/esm/components/wheelpicker/wheelpicker.js +98 -0
  379. package/dist/esm/components/wheelpicker/wheelpicker.recipe.js +92 -0
  380. package/dist/esm/core/cx.js +1 -0
  381. package/dist/esm/core/define.js +3 -0
  382. package/dist/esm/core/index.js +4 -0
  383. package/dist/esm/core/primitive.js +13 -0
  384. package/dist/esm/core/resolve.js +41 -0
  385. package/dist/esm/core/types.js +1 -0
  386. package/dist/esm/index.js +76 -0
  387. package/dist/esm/product/appshell/appshell.js +68 -0
  388. package/dist/esm/product/appshell/appshell.recipe.js +27 -0
  389. package/dist/esm/product/appshell/index.js +2 -0
  390. package/dist/esm/product/authshell/authshell.js +18 -0
  391. package/dist/esm/product/authshell/authshell.recipe.js +64 -0
  392. package/dist/esm/product/authshell/index.js +2 -0
  393. package/dist/esm/product/board/board.js +42 -0
  394. package/dist/esm/product/board/board.recipe.js +28 -0
  395. package/dist/esm/product/board/index.js +2 -0
  396. package/dist/esm/product/command/command.js +201 -0
  397. package/dist/esm/product/command/command.recipe.js +44 -0
  398. package/dist/esm/product/command/index.js +2 -0
  399. package/dist/esm/product/entitycard/entitycard.js +11 -0
  400. package/dist/esm/product/entitycard/entitycard.recipe.js +29 -0
  401. package/dist/esm/product/entitycard/index.js +2 -0
  402. package/dist/esm/product/flowcanvas/flowcanvas.js +85 -0
  403. package/dist/esm/product/flowcanvas/flowcanvas.recipe.js +25 -0
  404. package/dist/esm/product/flowcanvas/index.js +2 -0
  405. package/dist/esm/product/inbox/inbox.js +13 -0
  406. package/dist/esm/product/inbox/inbox.recipe.js +43 -0
  407. package/dist/esm/product/inbox/index.js +2 -0
  408. package/dist/esm/product/listpage/index.js +2 -0
  409. package/dist/esm/product/listpage/listpage.js +21 -0
  410. package/dist/esm/product/listpage/listpage.recipe.js +43 -0
  411. package/dist/esm/product/navitem/index.js +1 -0
  412. package/dist/esm/product/navitem/navitem.js +8 -0
  413. package/dist/esm/product/navitem/navitem.recipe.js +33 -0
  414. package/dist/esm/product/onboarding/index.js +2 -0
  415. package/dist/esm/product/onboarding/onboarding.js +14 -0
  416. package/dist/esm/product/onboarding/onboarding.recipe.js +41 -0
  417. package/dist/esm/product/recordpage/index.js +2 -0
  418. package/dist/esm/product/recordpage/recordpage.js +372 -0
  419. package/dist/esm/product/recordpage/recordpage.recipe.js +159 -0
  420. package/dist/esm/product/recordpanel/index.js +2 -0
  421. package/dist/esm/product/recordpanel/recordpanel.js +47 -0
  422. package/dist/esm/product/recordpanel/recordpanel.recipe.js +48 -0
  423. package/dist/esm/product/recordstable/company-logos.js +26 -0
  424. package/dist/esm/product/recordstable/index.js +2 -0
  425. package/dist/esm/product/recordstable/recordstable.js +1054 -0
  426. package/dist/esm/product/recordstable/recordstable.recipe.js +149 -0
  427. package/dist/esm/product/recordstable/recordstable.tag.js +18 -0
  428. package/dist/esm/product/richtext/index.js +2 -0
  429. package/dist/esm/product/richtext/richtext.js +111 -0
  430. package/dist/esm/product/richtext/richtext.recipe.js +40 -0
  431. package/dist/esm/product/settingssection/index.js +2 -0
  432. package/dist/esm/product/settingssection/settingssection.js +11 -0
  433. package/dist/esm/product/settingssection/settingssection.recipe.js +20 -0
  434. package/dist/esm/product/settingsshell/index.js +2 -0
  435. package/dist/esm/product/settingsshell/settingsshell.js +23 -0
  436. package/dist/esm/product/settingsshell/settingsshell.recipe.js +43 -0
  437. package/dist/esm/product/sidebar/index.js +2 -0
  438. package/dist/esm/product/sidebar/sidebar.js +112 -0
  439. package/dist/esm/product/sidebar/sidebar.recipe.js +43 -0
  440. package/dist/esm/product/tasklist/index.js +2 -0
  441. package/dist/esm/product/tasklist/tasklist.js +16 -0
  442. package/dist/esm/product/tasklist/tasklist.recipe.js +35 -0
  443. package/dist/esm/product/trialbanner/index.js +2 -0
  444. package/dist/esm/product/trialbanner/trialbanner.js +10 -0
  445. package/dist/esm/product/trialbanner/trialbanner.recipe.js +29 -0
  446. package/dist/esm/product/viewtoggle/index.js +2 -0
  447. package/dist/esm/product/viewtoggle/viewtoggle.js +19 -0
  448. package/dist/esm/product/viewtoggle/viewtoggle.recipe.js +24 -0
  449. package/dist/esm/tokens/contract.js +18 -0
  450. package/dist/esm/tokens/elevation.js +44 -0
  451. package/dist/esm/tokens/index.js +5 -0
  452. package/dist/esm/tokens/palette.js +60 -0
  453. package/dist/esm/tokens/scale.js +67 -0
  454. package/dist/esm/tokens/semantic.js +54 -0
  455. package/dist/inbox.css +9 -1
  456. package/dist/input.css +32 -4
  457. package/dist/linechart.css +5 -5
  458. package/dist/listpage.css +11 -3
  459. package/dist/menu.css +13 -5
  460. package/dist/multiselect.css +17 -9
  461. package/dist/native/tokens.d.ts +16 -0
  462. package/dist/native/tokens.js +221 -0
  463. package/dist/navitem.css +21 -1
  464. package/dist/onboarding.css +10 -2
  465. package/dist/popover.css +9 -1
  466. package/dist/progress.css +8 -0
  467. package/dist/radiogroup.css +9 -1
  468. package/dist/recordpage.css +26 -16
  469. package/dist/recordpanel.css +25 -15
  470. package/dist/recordstable.css +41 -27
  471. package/dist/resizablepanel.css +53 -0
  472. package/dist/resizablepanel.d.ts +7 -0
  473. package/dist/richtext.css +11 -3
  474. package/dist/segmented.css +8 -0
  475. package/dist/select.css +22 -8
  476. package/dist/select.d.ts +2 -1
  477. package/dist/settingsshell.css +15 -2
  478. package/dist/sidebar.css +34 -1
  479. package/dist/skeleton.css +8 -0
  480. package/dist/spinner.css +8 -0
  481. package/dist/stagebar.css +8 -0
  482. package/dist/statcard.css +6 -0
  483. package/dist/statcard.d.ts +2 -2
  484. package/dist/styles.css +2134 -999
  485. package/dist/switch.css +8 -0
  486. package/dist/table.css +34 -0
  487. package/dist/table.d.ts +8 -0
  488. package/dist/tabletoolbar.css +78 -0
  489. package/dist/tabletoolbar.d.ts +7 -0
  490. package/dist/tabs.css +12 -4
  491. package/dist/tasklist.css +8 -0
  492. package/dist/textarea.css +23 -3
  493. package/dist/toast.css +10 -2
  494. package/dist/tokens.css +53 -0
  495. package/dist/tokens.json +217 -0
  496. package/dist/tooltip.css +8 -0
  497. package/dist/trialbanner.css +8 -0
  498. package/dist/types/components/accordion/accordion.d.ts +10 -8
  499. package/dist/types/components/avatargroup/avatargroup.d.ts +2 -2
  500. package/dist/types/components/barchart/barchart.d.ts +4 -2
  501. package/dist/types/components/breadcrumb/breadcrumb.d.ts +2 -2
  502. package/dist/types/components/bulkbar/bulkbar.d.ts +4 -2
  503. package/dist/types/components/button/button.d.ts +8 -0
  504. package/dist/types/components/button/button.recipe.d.ts +12 -0
  505. package/dist/types/components/callout/callout.d.ts +2 -2
  506. package/dist/types/components/columncalc/columncalc.d.ts +49 -0
  507. package/dist/types/components/columncalc/columncalc.recipe.d.ts +3 -0
  508. package/dist/types/components/columncalc/index.d.ts +1 -0
  509. package/dist/types/components/datatable/datatable.d.ts +153 -0
  510. package/dist/types/components/datatable/datatable.recipe.d.ts +3 -0
  511. package/dist/types/components/datatable/index.d.ts +2 -0
  512. package/dist/types/components/datepicker/datepicker.d.ts +41 -18
  513. package/dist/types/components/datepicker/datepicker.recipe.d.ts +25 -2
  514. package/dist/types/components/datepicker/index.d.ts +1 -1
  515. package/dist/types/components/datepickerfield/datepickerfield.d.ts +35 -0
  516. package/dist/types/components/datepickerfield/datepickerfield.recipe.d.ts +5 -0
  517. package/dist/types/components/datepickerfield/index.d.ts +2 -0
  518. package/dist/types/components/dialog/dialog.d.ts +3 -1
  519. package/dist/types/components/divider/divider.d.ts +2 -2
  520. package/dist/types/components/donutchart/donutchart.d.ts +2 -2
  521. package/dist/types/components/drawer/drawer.d.ts +3 -1
  522. package/dist/types/components/emptystate/emptystate.d.ts +2 -2
  523. package/dist/types/components/formfield/formfield.d.ts +7 -4
  524. package/dist/types/components/input/input.d.ts +8 -2
  525. package/dist/types/components/linechart/linechart.d.ts +4 -2
  526. package/dist/types/components/menu/index.d.ts +1 -0
  527. package/dist/types/components/menu/menu-keyboard.d.ts +33 -0
  528. package/dist/types/components/menu/menu.d.ts +7 -1
  529. package/dist/types/components/multiselect/multiselect.d.ts +8 -4
  530. package/dist/types/components/popover/popover.d.ts +4 -1
  531. package/dist/types/components/progress/progress.d.ts +2 -2
  532. package/dist/types/components/radiogroup/radiogroup.d.ts +2 -2
  533. package/dist/types/components/resizablepanel/index.d.ts +2 -0
  534. package/dist/types/components/resizablepanel/resizablepanel.d.ts +75 -0
  535. package/dist/types/components/resizablepanel/resizablepanel.recipe.d.ts +3 -0
  536. package/dist/types/components/segmented/segmented.d.ts +2 -2
  537. package/dist/types/components/select/select.d.ts +4 -2
  538. package/dist/types/components/select/select.recipe.d.ts +12 -0
  539. package/dist/types/components/skeleton/skeleton.d.ts +2 -2
  540. package/dist/types/components/spinner/spinner.d.ts +2 -2
  541. package/dist/types/components/stagebar/stagebar.d.ts +2 -2
  542. package/dist/types/components/statcard/statcard.d.ts +7 -2
  543. package/dist/types/components/statcard/statcard.recipe.d.ts +12 -2
  544. package/dist/types/components/table/index.d.ts +2 -0
  545. package/dist/types/components/table/table.d.ts +53 -0
  546. package/dist/types/components/table/table.recipe.d.ts +21 -0
  547. package/dist/types/components/tabletoolbar/index.d.ts +2 -0
  548. package/dist/types/components/tabletoolbar/tabletoolbar.chrome.d.ts +2 -0
  549. package/dist/types/components/tabletoolbar/tabletoolbar.d.ts +89 -0
  550. package/dist/types/components/tabletoolbar/tabletoolbar.recipe.d.ts +3 -0
  551. package/dist/types/components/tabs/tabs.d.ts +2 -2
  552. package/dist/types/components/toast/toast.d.ts +7 -1
  553. package/dist/types/components/tooltip/tooltip.d.ts +8 -1
  554. package/dist/types/components/wheelpicker/index.d.ts +2 -0
  555. package/dist/types/components/wheelpicker/wheelpicker.d.ts +37 -0
  556. package/dist/types/components/wheelpicker/wheelpicker.recipe.d.ts +26 -0
  557. package/dist/types/index.d.ts +8 -0
  558. package/dist/types/product/appshell/appshell.d.ts +16 -2
  559. package/dist/types/product/authshell/authshell.d.ts +28 -0
  560. package/dist/types/product/authshell/authshell.recipe.d.ts +3 -0
  561. package/dist/types/product/authshell/index.d.ts +2 -0
  562. package/dist/types/product/board/board.d.ts +2 -2
  563. package/dist/types/product/command/command.d.ts +25 -1
  564. package/dist/types/product/entitycard/entitycard.d.ts +2 -2
  565. package/dist/types/product/flowcanvas/flowcanvas.d.ts +9 -1
  566. package/dist/types/product/onboarding/onboarding.d.ts +7 -1
  567. package/dist/types/product/recordpage/recordpage.d.ts +65 -1
  568. package/dist/types/product/recordpanel/recordpanel.d.ts +8 -2
  569. package/dist/types/product/recordstable/company-logos.d.ts +1 -0
  570. package/dist/types/product/recordstable/recordstable.d.ts +131 -1
  571. package/dist/types/product/recordstable/recordstable.tag.d.ts +10 -0
  572. package/dist/types/product/richtext/richtext.d.ts +5 -1
  573. package/dist/types/product/settingssection/settingssection.d.ts +2 -2
  574. package/dist/types/product/settingsshell/settingsshell.d.ts +6 -2
  575. package/dist/types/product/sidebar/index.d.ts +1 -1
  576. package/dist/types/product/sidebar/sidebar.d.ts +93 -0
  577. package/dist/types/product/tasklist/tasklist.d.ts +5 -1
  578. package/dist/types/product/trialbanner/trialbanner.d.ts +2 -2
  579. package/dist/types/product/viewtoggle/viewtoggle.d.ts +11 -5
  580. package/dist/types/tokens/contract.d.ts +26 -3
  581. package/dist/types/tokens/elevation.d.ts +44 -0
  582. package/dist/types/tokens/index.d.ts +1 -0
  583. package/dist/types/tokens/palette.d.ts +88 -0
  584. package/dist/types/tokens/scale.d.ts +7 -0
  585. package/dist/types/tokens/semantic.d.ts +31 -0
  586. package/dist/viewtoggle.css +11 -3
  587. package/dist/wheelpicker.css +68 -0
  588. package/dist/wheelpicker.d.ts +7 -0
  589. package/package.json +351 -9
  590. package/dist/index.js +0 -9690
@@ -0,0 +1,1092 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.defaultRows = exports.defaultColumns = exports.recordsTableLabels = void 0;
37
+ exports.tagTone = tagTone;
38
+ exports.RecordsTable = RecordsTable;
39
+ const jsx_runtime_1 = require("react/jsx-runtime");
40
+ const lucide_react_1 = require("lucide-react");
41
+ const React = __importStar(require("react"));
42
+ const react_dom_1 = require("react-dom");
43
+ const avatar_1 = require("../../components/avatar");
44
+ const bulkbar_1 = require("../../components/bulkbar");
45
+ const button_1 = require("../../components/button");
46
+ const checkbox_1 = require("../../components/checkbox");
47
+ const datepickerfield_1 = require("../../components/datepickerfield");
48
+ const dialog_1 = require("../../components/dialog");
49
+ const emptystate_1 = require("../../components/emptystate");
50
+ const input_1 = require("../../components/input");
51
+ const menu_1 = require("../../components/menu");
52
+ const multiselect_1 = require("../../components/multiselect");
53
+ const select_1 = require("../../components/select");
54
+ const switch_1 = require("../../components/switch");
55
+ const company_logos_1 = require("./company-logos");
56
+ const recordstable_recipe_1 = require("./recordstable.recipe");
57
+ const EDITABLE = new Set(["text", "number", "url", "currency"]);
58
+ // types whose cell is an always-interactive picker (not double-click-to-edit)
59
+ const PICKER = new Set(["status", "reference", "date", "tags"]);
60
+ // a `render` column owns its content fully — never editable, never a picker
61
+ const canEditCol = (col) => EDITABLE.has(col.type ?? "text") && !col.render;
62
+ const isPickerCol = (col) => PICKER.has(col.type ?? "text") && !col.render;
63
+ // deterministic pastel colour per tag value (a select option keeps its colour)
64
+ const TAG_TONES = [
65
+ "brand",
66
+ "success",
67
+ "warning",
68
+ "danger",
69
+ "purple",
70
+ "cyan",
71
+ "orange",
72
+ ];
73
+ function tagTone(t) {
74
+ let h = 0;
75
+ for (let i = 0; i < t.length; i++)
76
+ h = (h * 31 + t.charCodeAt(i)) >>> 0;
77
+ return TAG_TONES[h % TAG_TONES.length];
78
+ }
79
+ // deterministic per-name colour for `avatarTone="hashed"` — a distinct
80
+ // palette from TAG_TONES because it must stay within the tones the `Avatar`
81
+ // DS component itself supports (no "success"/"warning"/"danger").
82
+ const AVATAR_TONES = ["brand", "orange", "cyan", "purple", "green"];
83
+ function avatarToneFor(name) {
84
+ let h = 0;
85
+ for (let i = 0; i < name.length; i++)
86
+ h = (h * 31 + name.charCodeAt(i)) >>> 0;
87
+ return AVATAR_TONES[h % AVATAR_TONES.length];
88
+ }
89
+ exports.recordsTableLabels = {
90
+ selectAll: "Select all",
91
+ sort: "Sort",
92
+ sortField: "Sort field",
93
+ sortDirection: "Sort direction",
94
+ removeSort: "Remove sort",
95
+ reorderSort: "Reorder sort",
96
+ addSort: "Add sort",
97
+ ascending: "Ascending",
98
+ descending: "Descending",
99
+ noSorts: "No sorts applied to this view.",
100
+ learnSorting: "Learn about sorting",
101
+ filter: "Filter",
102
+ addFilter: "Add filter",
103
+ filterByField: "Filter by field",
104
+ filterByPlaceholder: "Filter by…",
105
+ filterValuePlaceholder: "Value…",
106
+ selectCondition: "Select condition",
107
+ clearFilter: "Clear filter",
108
+ addColumn: "Add column",
109
+ columnLabel: "Column label",
110
+ editColumnLabel: "Edit column label",
111
+ hideFromView: "Hide from view",
112
+ moveLeft: "Move left",
113
+ moveRight: "Move right",
114
+ sortAscending: "Sort ascending",
115
+ sortDescending: "Sort descending",
116
+ newRecord: "New record",
117
+ createRecord: "Create record",
118
+ createMore: "Create more",
119
+ newButton: "New",
120
+ more: "More",
121
+ moreActions: "More actions",
122
+ recordActions: "Record actions",
123
+ copy: "Copy",
124
+ duplicate: "Duplicate",
125
+ addToList: "Add to list",
126
+ inAList: "In a list",
127
+ delete: "Delete",
128
+ sendEmail: "Send email",
129
+ exportCsv: "Export CSV",
130
+ runWorkflow: "Run workflow",
131
+ calculate: "Calculate",
132
+ emptyPlaceholder: "Empty",
133
+ setRecordPlaceholder: "Set a record…",
134
+ noRecords: "No records yet",
135
+ untitled: "Untitled",
136
+ agg: {
137
+ none: "None",
138
+ count: "Count all",
139
+ filled: "Not empty",
140
+ sum: "Sum",
141
+ avg: "Average",
142
+ min: "Min",
143
+ max: "Max",
144
+ },
145
+ op: {
146
+ contains: "contains",
147
+ is: "is",
148
+ isnot: "is not",
149
+ empty: "is empty",
150
+ notempty: "is not empty",
151
+ },
152
+ newRecordTitle: (entity) => `New ${entity}`,
153
+ setFieldPlaceholder: (field) => `Set ${field}…`,
154
+ newColumnLabel: (index) => `Field ${index}`,
155
+ sortedBy: (first, extra) => `Sorted by ${first}${extra ? ` +${extra}` : ""}`,
156
+ filterCount: (active) => (active ? `Filter · ${active}` : "Filter"),
157
+ recordCount: (n) => `${n} record${n === 1 ? "" : "s"}`,
158
+ conditionTitle: (field) => `${field} condition`,
159
+ };
160
+ // currency is numeric data too — it gets the same numeric aggregations as
161
+ // `number` (only `number` auto-defaults to Sum on mount; currency starts at
162
+ // "none" like every other type and is picked explicitly from this menu)
163
+ const aggOptions = (col) => col.type === "number" || col.type === "currency"
164
+ ? ["sum", "avg", "min", "max", "count", "filled", "none"]
165
+ : ["count", "filled", "none"];
166
+ const compact = new Intl.NumberFormat("en", {
167
+ notation: "compact",
168
+ maximumFractionDigits: 1,
169
+ });
170
+ const CHECK_W = 40;
171
+ const ADDCOL_W = 44;
172
+ const colWidth = (col) => col.width ?? (col.type === "entity" ? 240 : 180);
173
+ const OP_ORDER = ["contains", "is", "isnot", "empty", "notempty"];
174
+ const opNeedsValue = (op) => op === "contains" || op === "is" || op === "isnot";
175
+ function matchesFilter(col, row, cond) {
176
+ if (cond.op === "empty")
177
+ return !isFilled(col, row);
178
+ if (cond.op === "notempty")
179
+ return isFilled(col, row);
180
+ // filter always reads the raw underlying value, never `format`'s display
181
+ // string — a formatted "$1,200" must still match a filter value of "1200"
182
+ const t = rawText(col, row).toLowerCase();
183
+ const v = cond.value.trim().toLowerCase();
184
+ if (!v)
185
+ return true;
186
+ if (cond.op === "contains")
187
+ return t.includes(v);
188
+ if (cond.op === "is")
189
+ return t === v;
190
+ return t !== v; // isnot
191
+ }
192
+ // the raw value stringified, ignoring `format`/`render` entirely — the basis
193
+ // for filtering and the fallback for `cellText`'s copy path
194
+ function rawText(col, row) {
195
+ const v = row[col.key];
196
+ if (v == null)
197
+ return "";
198
+ if (col.type === "entity")
199
+ return v.name ?? "";
200
+ if (Array.isArray(v))
201
+ return v.join(", ");
202
+ return String(v);
203
+ }
204
+ // what ⌘C / row-copy / CSV export puts on the clipboard for this cell: a
205
+ // `render` column has no serializable node, so this never touches `render` —
206
+ // it uses `format`'s display string when the column has one, else the raw
207
+ // value stringified exactly like a plain column.
208
+ function cellText(col, row) {
209
+ if (col.format)
210
+ return col.format(row[col.key], row);
211
+ return rawText(col, row);
212
+ }
213
+ function isFilled(col, row) {
214
+ const v = row[col.key];
215
+ if (v == null || v === "")
216
+ return false;
217
+ if (Array.isArray(v))
218
+ return v.length > 0;
219
+ if (col.type === "entity")
220
+ return Boolean(v.name);
221
+ return true;
222
+ }
223
+ function summarize(col, agg, rows) {
224
+ if (agg === "none")
225
+ return null;
226
+ if (agg === "count")
227
+ return { label: "count", value: String(rows.length) };
228
+ if (agg === "filled") {
229
+ const n = rows.filter((r) => isFilled(col, r)).length;
230
+ return { label: "filled", value: String(n) };
231
+ }
232
+ const nums = rows
233
+ .map((r) => r[col.key])
234
+ .filter((v) => typeof v === "number");
235
+ if (!nums.length)
236
+ return { label: agg, value: "—" };
237
+ let n;
238
+ if (agg === "sum")
239
+ n = nums.reduce((a, b) => a + b, 0);
240
+ else if (agg === "avg")
241
+ n = nums.reduce((a, b) => a + b, 0) / nums.length;
242
+ else if (agg === "min")
243
+ n = Math.min(...nums);
244
+ else
245
+ n = Math.max(...nums);
246
+ return { label: agg, value: compact.format(n) };
247
+ }
248
+ function EntityCell({ value, pinned, onOpenPage, avatarTone = "brand", labels, }) {
249
+ // a real favicon/logo when we have one; fall back to the letter tile if it
250
+ // 404s or the row has no avatar
251
+ const [imgFailed, setImgFailed] = React.useState(false);
252
+ const showImg = Boolean(value.avatar) && !imgFailed;
253
+ return ((0, jsx_runtime_1.jsxs)("span", { className: "recordstable-entity", children: [showImg ? ((0, jsx_runtime_1.jsx)("img", { className: "recordstable-fav", src: value.avatar, alt: "", onError: () => setImgFailed(true) })) : ((0, jsx_runtime_1.jsx)(avatar_1.Avatar, { size: "sm", shape: "rounded", tone: avatarTone === "hashed" ? avatarToneFor(value.name) : "brand", children: value.name.charAt(0) })), onOpenPage ? ((0, jsx_runtime_1.jsx)("button", { type: "button", className: "recordstable-entity-name recordstable-entity-name--link", onClick: (e) => {
254
+ e.stopPropagation();
255
+ onOpenPage();
256
+ }, children: value.name })) : ((0, jsx_runtime_1.jsx)("span", { className: "recordstable-entity-name", children: value.name })), pinned ? ((0, jsx_runtime_1.jsx)(lucide_react_1.Star, { className: "recordstable-pin", size: 13, strokeWidth: 2, "aria-label": labels.inAList })) : null] }));
257
+ }
258
+ function CellView({ column, row, pinned, onOpenPage, avatarTone = "brand", labels, }) {
259
+ const value = row[column.key];
260
+ // `render` fully owns the cell's content — no empty-dash fallback, no
261
+ // type renderer; it's the only thing drawn here (see the doc comment on
262
+ // `Column.render`).
263
+ if (column.render)
264
+ return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: column.render(value, row) });
265
+ if (value == null ||
266
+ value === "" ||
267
+ (Array.isArray(value) && !value.length)) {
268
+ return (0, jsx_runtime_1.jsx)("span", { className: "recordstable-muted", children: "\u2014" });
269
+ }
270
+ // `format` keeps the type renderer's chrome below and only replaces the
271
+ // text it displays.
272
+ const display = column.format?.(value, row);
273
+ switch (column.type) {
274
+ case "entity":
275
+ return ((0, jsx_runtime_1.jsx)(EntityCell, { labels: labels, value: value, pinned: pinned, onOpenPage: onOpenPage, avatarTone: avatarTone }));
276
+ case "tags":
277
+ return ((0, jsx_runtime_1.jsx)("span", { className: "recordstable-cell recordstable-cell--tags", children: value.map((t) => ((0, jsx_runtime_1.jsx)("span", { className: "recordstable-tag", "data-tone": tagTone(t), children: t }, t))) }));
278
+ case "number":
279
+ return ((0, jsx_runtime_1.jsx)("span", { className: "recordstable-cell recordstable-cell--num", children: display ??
280
+ (typeof value === "number" ? value.toLocaleString() : value) }));
281
+ case "url":
282
+ return ((0, jsx_runtime_1.jsx)("a", { className: "recordstable-link", href: `https://${value}`, target: "_blank", rel: "noreferrer", onClick: (e) => e.stopPropagation(), children: display ?? value }));
283
+ case "date":
284
+ return (0, jsx_runtime_1.jsx)("span", { className: "recordstable-muted", children: display ?? value });
285
+ case "currency": {
286
+ const n = typeof value === "number" ? value : Number(value);
287
+ return ((0, jsx_runtime_1.jsx)("span", { className: "recordstable-cell recordstable-cell--num", children: display ??
288
+ `${column.symbol ?? "$"}${Number.isFinite(n) ? n.toLocaleString() : value}` }));
289
+ }
290
+ case "status": {
291
+ const opt = column.options?.find((o) => o.value === value);
292
+ if (!opt)
293
+ return (0, jsx_runtime_1.jsx)("span", { className: "recordstable-muted", children: "\u2014" });
294
+ return ((0, jsx_runtime_1.jsxs)("span", { className: "recordstable-status", children: [(0, jsx_runtime_1.jsx)("span", { className: "recordstable-status-dot", style: { background: opt.color } }), display ?? opt.label] }));
295
+ }
296
+ case "reference": {
297
+ const opt = column.refOptions?.find((o) => o.value === value);
298
+ const label = display ?? opt?.label ?? String(value);
299
+ return ((0, jsx_runtime_1.jsxs)("span", { className: "recordstable-entity", children: [(0, jsx_runtime_1.jsx)(avatar_1.Avatar, { size: "sm", shape: "rounded", tone: avatarTone === "hashed" ? avatarToneFor(label) : "brand", children: label.charAt(0) }), (0, jsx_runtime_1.jsx)("span", { className: "recordstable-entity-name", children: label })] }));
300
+ }
301
+ default:
302
+ return ((0, jsx_runtime_1.jsx)("span", { className: "recordstable-entity-name", children: display ?? value }));
303
+ }
304
+ }
305
+ // Always-interactive cell editors for the picker column types (status / date /
306
+ // reference). Each is a ghost crisp control that reads the cell value and
307
+ // commits the new one — matching Attio's inline field editors.
308
+ function StatusCell({ column, row, onChange, }) {
309
+ return ((0, jsx_runtime_1.jsx)(select_1.Select, { variant: "ghost", "aria-label": column.label, placeholder: "\u2014", value: row[column.key] ?? undefined, onValueChange: onChange, options: column.options ?? [] }));
310
+ }
311
+ function ReferenceCell({ column, row, onChange, labels, }) {
312
+ return ((0, jsx_runtime_1.jsx)(select_1.Select, { variant: "ghost", searchable: true, searchPlaceholder: "Search records\u2026", "aria-label": column.label, placeholder: labels.setRecordPlaceholder, value: row[column.key] ?? undefined, onValueChange: onChange, options: (column.refOptions ?? []).map((o) => ({
313
+ value: o.value,
314
+ label: o.label,
315
+ })) }));
316
+ }
317
+ function TagsCell({ column, row, options, onChange, labels, }) {
318
+ const value = row[column.key] ?? [];
319
+ return ((0, jsx_runtime_1.jsx)(multiselect_1.MultiSelect, { options: options, value: value, onValueChange: onChange, placeholder: labels.emptyPlaceholder, searchPlaceholder: "Search options\u2026", "aria-label": column.label }));
320
+ }
321
+ function DateCell({ column, row, onChange, }) {
322
+ const raw = row[column.key];
323
+ const value = raw ? new Date(raw) : null;
324
+ return ((0, jsx_runtime_1.jsx)(datepickerfield_1.DatePickerField, { variant: "ghost", "aria-label": column.label, value: value, onChange: (d) => onChange(d.toISOString().slice(0, 10)) }));
325
+ }
326
+ /**
327
+ * The "New <object>" create modal — measured from Attio's create-record dialog:
328
+ * a stacked label + "Set …" input per column, a "Create more" toggle, and a
329
+ * primary "Create record" action (⌘↵). Building the row mirrors the inline
330
+ * editor's per-type coercion so a created record reads back like an edited one.
331
+ */
332
+ function CreateRecordDialog({ open, onOpenChange, columns, entityLabel, onCreate, labels, }) {
333
+ const [values, setValues] = React.useState({});
334
+ const [createMore, setCreateMore] = React.useState(false);
335
+ const firstRef = React.useRef(null);
336
+ // reset the form each time the modal (re)opens
337
+ React.useEffect(() => {
338
+ if (open)
339
+ setValues({});
340
+ }, [open]);
341
+ const set = (key, v) => setValues((prev) => ({ ...prev, [key]: v }));
342
+ const buildRow = () => {
343
+ const row = {
344
+ id: `new-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`,
345
+ };
346
+ for (const col of columns) {
347
+ const raw = (values[col.key] ?? "").trim();
348
+ if (col.type === "entity") {
349
+ row[col.key] = { name: raw || labels.untitled };
350
+ }
351
+ else if (!raw) {
352
+ // leave empty fields unset so the cell renders its "—" placeholder
353
+ }
354
+ else if (col.type === "tags") {
355
+ row[col.key] = raw
356
+ .split(",")
357
+ .map((t) => t.trim())
358
+ .filter(Boolean);
359
+ }
360
+ else if (col.type === "number") {
361
+ row[col.key] = Number(raw.replace(/[^0-9.-]/g, "")) || null;
362
+ }
363
+ else {
364
+ row[col.key] = raw;
365
+ }
366
+ }
367
+ return row;
368
+ };
369
+ const submit = () => {
370
+ onCreate(buildRow());
371
+ if (createMore) {
372
+ setValues({});
373
+ firstRef.current?.focus();
374
+ }
375
+ else {
376
+ onOpenChange(false);
377
+ }
378
+ };
379
+ const entityCol = columns.find((c) => c.type === "entity");
380
+ const EntityIcon = entityCol?.icon;
381
+ return ((0, jsx_runtime_1.jsxs)(dialog_1.Dialog, { open: open, onOpenChange: onOpenChange, title: labels.newRecordTitle(entityLabel), icon: EntityIcon ? (0, jsx_runtime_1.jsx)(EntityIcon, { size: 16, strokeWidth: 1.75 }) : undefined, width: 720, children: [(0, jsx_runtime_1.jsx)(dialog_1.DialogBody, { children: columns.map((col, i) => ((0, jsx_runtime_1.jsxs)("div", { className: "dialog-field", children: [(0, jsx_runtime_1.jsx)("label", { className: "dialog-field-label", htmlFor: `create-${col.key}`, children: col.label }), (0, jsx_runtime_1.jsx)(input_1.Input, { id: `create-${col.key}`, ref: i === 0 ? firstRef : undefined, value: values[col.key] ?? "", onChange: (e) => set(col.key, e.currentTarget.value), placeholder: labels.setFieldPlaceholder(col.label), onKeyDown: (e) => {
382
+ if (e.key === "Enter" && (e.metaKey || e.ctrlKey))
383
+ submit();
384
+ } })] }, col.key))) }), (0, jsx_runtime_1.jsxs)(dialog_1.DialogFooter, { children: [(0, jsx_runtime_1.jsxs)("span", { className: "dialog-more", children: [labels.createMore, (0, jsx_runtime_1.jsx)(switch_1.Switch, { checked: createMore, onCheckedChange: setCreateMore, "aria-label": labels.createMore })] }), (0, jsx_runtime_1.jsxs)("button", { type: "button", className: "dialog-submit", onClick: submit, children: [labels.createRecord, (0, jsx_runtime_1.jsx)("span", { className: "dialog-submit-key", children: "\u2318\u21B5" })] })] })] }));
385
+ }
386
+ /**
387
+ * Attio-style record grid — fully featured. Toolbar (search / sort status /
388
+ * count / New), sticky checkbox + entity columns, 36px rows, typed cells,
389
+ * click-to-sort headers, column resize by dragging a header edge, inline cell
390
+ * editing (double-click a text/number/url/date cell), add-row and add-column,
391
+ * and a floating bulk-action bar (delete / deselect) when rows are selected.
392
+ * Selection can be controlled via `selectedIds`/`onSelectionChange`.
393
+ */
394
+ function RecordsTable({ labels, columns: columnsProp = exports.defaultColumns, rows: rowsProp = exports.defaultRows, selectedIds, onSelectionChange, showNew = true, allowAddColumn = true, onOpenRecord, onOpenRecordPage, avatarTone = "brand", className, }) {
395
+ const [cols, setCols] = React.useState(columnsProp);
396
+ const [data, setData] = React.useState(rowsProp);
397
+ // Attio's multi-criteria sort: an ordered list of {field, direction} rows.
398
+ const [sorts, setSorts] = React.useState([]);
399
+ const [dragSort, setDragSort] = React.useState(null);
400
+ const [editing, setEditing] = React.useState(null);
401
+ const [renamingCol, setRenamingCol] = React.useState(null);
402
+ const [creating, setCreating] = React.useState(false);
403
+ // Attio-style filter: an ordered list of conditions, each shown as a toolbar
404
+ // chip with an editable value (operator is "contains")
405
+ const [filterConds, setFilterConds] = React.useState([]);
406
+ const [filterQuery, setFilterQuery] = React.useState("");
407
+ // one popover open at a time: "sort" | "filter" | "more" | `sum:<key>`
408
+ const t = React.useMemo(() => ({
409
+ ...exports.recordsTableLabels,
410
+ ...labels,
411
+ agg: { ...exports.recordsTableLabels.agg, ...labels?.agg },
412
+ op: { ...exports.recordsTableLabels.op, ...labels?.op },
413
+ }), [labels]);
414
+ const [openMenu, setOpenMenu] = React.useState(null);
415
+ // the element that opened the current menu, so Esc/select can hand focus back
416
+ const menuReturn = React.useRef(null);
417
+ const menuRef = React.useRef(null);
418
+ const ctxRef = React.useRef(null);
419
+ const ctxReturn = React.useRef(null);
420
+ // viewport-anchored position for menus that live inside the scroll container
421
+ // (summary footer, column headers) — rendered fixed to escape the overflow clip
422
+ const [menuPos, setMenuPos] = React.useState(null);
423
+ const [pinned, setPinned] = React.useState(() => new Set());
424
+ const [scrolledX, setScrolledX] = React.useState(false);
425
+ const idCounter = React.useRef(0);
426
+ // fixed menus (summary / header) portal here — outside the sticky cells'
427
+ // stacking contexts, but still inside .recordstable so the styles apply
428
+ const rootRef = React.useRef(null);
429
+ const scrollRef = React.useRef(null);
430
+ // spreadsheet-style cell selection (click a cell, arrow-key to navigate)
431
+ const [selCell, setSelCell] = React.useState(null);
432
+ // shift-click range selection — anchor row + whether shift was held on click
433
+ const selAnchor = React.useRef(null);
434
+ const shiftHeld = React.useRef(false);
435
+ // right-click row context menu (fixed at the cursor)
436
+ const [ctxMenu, setCtxMenu] = React.useState(null);
437
+ // column drag-to-reorder — key of the header currently being dragged
438
+ const [dragCol, setDragCol] = React.useState(null);
439
+ // Let a plain vertical wheel scroll the (wide) table horizontally when it has
440
+ // no vertical overflow of its own — otherwise the wheel just scrolls the page
441
+ // and the extra columns feel unreachable. Non-passive so we can preventDefault.
442
+ React.useEffect(() => {
443
+ const el = scrollRef.current;
444
+ if (!el)
445
+ return;
446
+ const onWheel = (e) => {
447
+ // don't hijack the wheel when it's over an open cell popover (its own list
448
+ // scrolls) — only map vertical wheel to horizontal for the table itself
449
+ if (e.target?.closest?.(".select-listbox, .multiselect-listbox, .datepicker-cal, .recordstable-menu"))
450
+ return;
451
+ const canH = el.scrollWidth > el.clientWidth;
452
+ const canV = el.scrollHeight > el.clientHeight;
453
+ if (canH && !canV && e.deltaX === 0 && e.deltaY !== 0) {
454
+ el.scrollLeft += e.deltaY;
455
+ e.preventDefault();
456
+ }
457
+ };
458
+ el.addEventListener("wheel", onWheel, { passive: false });
459
+ return () => el.removeEventListener("wheel", onWheel);
460
+ }, []);
461
+ // close any open menu on outside click (anything not inside a menu or its
462
+ // trigger) or Escape
463
+ React.useEffect(() => {
464
+ if (!openMenu)
465
+ return;
466
+ const onDown = (e) => {
467
+ const t = e.target;
468
+ if (!t.closest(".recordstable-menu, [data-menu-trigger]"))
469
+ setOpenMenu(null);
470
+ };
471
+ const onKey = (e) => e.key === "Escape" && setOpenMenu(null);
472
+ document.addEventListener("mousedown", onDown);
473
+ document.addEventListener("keydown", onKey);
474
+ return () => {
475
+ document.removeEventListener("mousedown", onDown);
476
+ document.removeEventListener("keydown", onKey);
477
+ };
478
+ }, [openMenu]);
479
+ // dismiss the right-click row context menu on any outside click / Escape
480
+ React.useEffect(() => {
481
+ if (!ctxMenu)
482
+ return;
483
+ const onDown = (e) => {
484
+ if (!e.target.closest(".recordstable-ctx"))
485
+ setCtxMenu(null);
486
+ };
487
+ const onKey = (e) => e.key === "Escape" && setCtxMenu(null);
488
+ document.addEventListener("mousedown", onDown);
489
+ document.addEventListener("keydown", onKey);
490
+ return () => {
491
+ document.removeEventListener("mousedown", onDown);
492
+ document.removeEventListener("keydown", onKey);
493
+ };
494
+ }, [ctxMenu]);
495
+ const activeFilters = filterConds.length;
496
+ const addFilter = (key) => {
497
+ idCounter.current += 1;
498
+ const id = `filter-${idCounter.current}`;
499
+ setFilterConds((prev) => [...prev, { id, key, op: "contains", value: "" }]);
500
+ setFilterQuery("");
501
+ setOpenMenu(`fcond:${id}`); // open its condition editor right away
502
+ };
503
+ const patchFilter = (id, patch) => setFilterConds((prev) => prev.map((c) => (c.id === id ? { ...c, ...patch } : c)));
504
+ const removeFilter = (id) => {
505
+ setFilterConds((prev) => prev.filter((c) => c.id !== id));
506
+ setOpenMenu(null);
507
+ };
508
+ const [agg, setAgg] = React.useState(() => {
509
+ const m = {};
510
+ for (const col of columnsProp) {
511
+ m[col.key] =
512
+ col.type === "number"
513
+ ? "sum"
514
+ : col.type === "entity"
515
+ ? "count"
516
+ : "none";
517
+ }
518
+ return m;
519
+ });
520
+ // selection — controlled if `selectedIds` is provided, else internal
521
+ const [internalSel, setInternalSel] = React.useState(() => new Set());
522
+ const selected = React.useMemo(() => (selectedIds ? new Set(selectedIds) : internalSel), [selectedIds, internalSel]);
523
+ const setSelected = React.useCallback((next) => {
524
+ onSelectionChange?.([...next]);
525
+ if (!selectedIds)
526
+ setInternalSel(next);
527
+ }, [onSelectionChange, selectedIds]);
528
+ const filtered = React.useMemo(() => {
529
+ const active = filterConds.filter((c) => !opNeedsValue(c.op) || c.value.trim());
530
+ if (!active.length)
531
+ return data;
532
+ return data.filter((row) => active.every((cond) => {
533
+ const col = cols.find((c) => c.key === cond.key);
534
+ return col ? matchesFilter(col, row, cond) : true;
535
+ }));
536
+ }, [data, cols, filterConds]);
537
+ const visibleRows = React.useMemo(() => {
538
+ if (sorts.length === 0)
539
+ return filtered;
540
+ const getters = sorts.map((s) => {
541
+ const col = cols.find((c) => c.key === s.key);
542
+ return (r) => {
543
+ const v = r[s.key];
544
+ if (col?.type === "entity")
545
+ return v?.name ?? "";
546
+ if (Array.isArray(v))
547
+ return v[0] ?? "";
548
+ return v ?? "";
549
+ };
550
+ });
551
+ return [...filtered].sort((a, b) => {
552
+ for (let i = 0; i < sorts.length; i++) {
553
+ const get = getters[i];
554
+ const av = get(a);
555
+ const bv = get(b);
556
+ const cmp = typeof av === "number" && typeof bv === "number"
557
+ ? av - bv
558
+ : String(av).localeCompare(String(bv));
559
+ if (cmp !== 0)
560
+ return sorts[i].dir === "asc" ? cmp : -cmp;
561
+ }
562
+ return 0;
563
+ });
564
+ }, [filtered, cols, sorts]);
565
+ // arrow-key navigation over the selected cell (spreadsheet-style)
566
+ const onGridKeyDown = (e) => {
567
+ if (!selCell || editing)
568
+ return;
569
+ // ⌘C / Ctrl+C — copy the selected cell's rendered text to the clipboard
570
+ if ((e.metaKey || e.ctrlKey) && (e.key === "c" || e.key === "C")) {
571
+ const row = visibleRows.find((rw) => rw.id === selCell.id);
572
+ const col = cols.find((cc) => cc.key === selCell.key);
573
+ if (row && col)
574
+ navigator.clipboard?.writeText(cellText(col, row));
575
+ e.preventDefault();
576
+ return;
577
+ }
578
+ const r = visibleRows.findIndex((row) => row.id === selCell.id);
579
+ const c = cols.findIndex((col) => col.key === selCell.key);
580
+ if (r < 0 || c < 0)
581
+ return;
582
+ let nr = r;
583
+ let nc = c;
584
+ if (e.key === "ArrowUp")
585
+ nr = Math.max(0, r - 1);
586
+ else if (e.key === "ArrowDown")
587
+ nr = Math.min(visibleRows.length - 1, r + 1);
588
+ else if (e.key === "ArrowLeft")
589
+ nc = Math.max(0, c - 1);
590
+ else if (e.key === "ArrowRight")
591
+ nc = Math.min(cols.length - 1, c + 1);
592
+ else if (e.key === "Escape") {
593
+ setSelCell(null);
594
+ return;
595
+ }
596
+ else if (e.key === "Enter") {
597
+ if (canEditCol(cols[c])) {
598
+ e.preventDefault();
599
+ setEditing({ id: selCell.id, key: selCell.key });
600
+ }
601
+ return;
602
+ }
603
+ else
604
+ return;
605
+ e.preventDefault();
606
+ setSelCell({ id: visibleRows[nr].id, key: cols[nc].key });
607
+ };
608
+ const allSelected = visibleRows.length > 0 && visibleRows.every((r) => selected.has(r.id));
609
+ const someSelected = selected.size > 0 && !allSelected;
610
+ const toggleAll = () => setSelected(allSelected ? new Set() : new Set(visibleRows.map((r) => r.id)));
611
+ const toggleRow = (id) => {
612
+ const next = new Set(selected);
613
+ // shift-click → select the contiguous range from the anchor to this row
614
+ if (shiftHeld.current && selAnchor.current) {
615
+ const a = visibleRows.findIndex((r) => r.id === selAnchor.current);
616
+ const b = visibleRows.findIndex((r) => r.id === id);
617
+ if (a >= 0 && b >= 0) {
618
+ const [lo, hi] = a < b ? [a, b] : [b, a];
619
+ for (let i = lo; i <= hi; i++)
620
+ next.add(visibleRows[i].id);
621
+ shiftHeld.current = false;
622
+ setSelected(next);
623
+ return;
624
+ }
625
+ }
626
+ if (next.has(id))
627
+ next.delete(id);
628
+ else
629
+ next.add(id);
630
+ selAnchor.current = id;
631
+ shiftHeld.current = false;
632
+ setSelected(next);
633
+ };
634
+ const addSort = () => {
635
+ const used = new Set(sorts.map((s) => s.key));
636
+ const col = cols.find((c) => !used.has(c.key));
637
+ if (col)
638
+ setSorts([...sorts, { key: col.key, dir: "asc" }]);
639
+ };
640
+ const moveSort = (from, to) => setSorts((prev) => {
641
+ if (from === to)
642
+ return prev;
643
+ const next = [...prev];
644
+ const [moved] = next.splice(from, 1);
645
+ next.splice(to, 0, moved);
646
+ return next;
647
+ });
648
+ const setSortField = (i, key) => setSorts(sorts.map((s, j) => (j === i ? { ...s, key } : s)));
649
+ const setSortDir = (i, dir) => setSorts(sorts.map((s, j) => (j === i ? { ...s, dir } : s)));
650
+ const removeSort = (i) => setSorts(sorts.filter((_, j) => j !== i));
651
+ const commitEdit = (id, key, raw) => {
652
+ const col = cols.find((c) => c.key === key);
653
+ const numeric = col?.type === "number" || col?.type === "currency";
654
+ const coerce = (r) => {
655
+ if (numeric)
656
+ return raw === "" ? null : Number(raw.replace(/[^0-9.-]/g, ""));
657
+ if (col?.type === "tags")
658
+ return raw
659
+ .split(",")
660
+ .map((t) => t.trim())
661
+ .filter(Boolean);
662
+ if (col?.type === "entity")
663
+ return { ...r[key], name: raw };
664
+ return raw;
665
+ };
666
+ setData((prev) => prev.map((r) => (r.id === id ? { ...r, [key]: coerce(r) } : r)));
667
+ setEditing(null);
668
+ };
669
+ // the option set for a `tags` column — the union of every value in that column,
670
+ // so the tag editor offers the existing options (each keeps a stable colour).
671
+ const tagOptionsFor = React.useCallback((key) => {
672
+ const set = new Set();
673
+ for (const r of data) {
674
+ const v = r[key];
675
+ if (Array.isArray(v))
676
+ for (const t of v)
677
+ set.add(t);
678
+ }
679
+ return [...set].map((t) => ({ value: t, label: t, tone: tagTone(t) }));
680
+ }, [data]);
681
+ // set any cell value directly (used by the status / date / reference pickers)
682
+ // biome-ignore lint/suspicious/noExplicitAny: cell values are column-type dependent
683
+ const setCell = (id, key, value) => {
684
+ setData((prev) => prev.map((r) => (r.id === id ? { ...r, [key]: value } : r)));
685
+ };
686
+ const addRow = () => {
687
+ idCounter.current += 1;
688
+ const id = `new-${idCounter.current}`;
689
+ const entityKey = cols.find((c) => c.type === "entity")?.key;
690
+ const row = { id };
691
+ if (entityKey)
692
+ row[entityKey] = { name: t.untitled };
693
+ setData((prev) => [...prev, row]);
694
+ if (entityKey)
695
+ setEditing(null);
696
+ };
697
+ const sortBy = (key, dir) => {
698
+ setSorts([{ key, dir }]);
699
+ setOpenMenu(null);
700
+ };
701
+ const hideColumn = (key) => {
702
+ setCols((prev) => prev.filter((c) => c.key !== key));
703
+ setOpenMenu(null);
704
+ };
705
+ // Move a column one slot left/right. The first (entity) column is sticky and
706
+ // stays pinned, so reordering is clamped to index >= 1.
707
+ const moveColumn = (key, dir) => {
708
+ setCols((prev) => {
709
+ const i = prev.findIndex((c) => c.key === key);
710
+ const j = i + dir;
711
+ if (i < 1 || j < 1 || j >= prev.length)
712
+ return prev;
713
+ const next = [...prev];
714
+ [next[i], next[j]] = [next[j], next[i]];
715
+ return next;
716
+ });
717
+ setOpenMenu(null);
718
+ };
719
+ // drop the dragged column INTO `toKey`'s slot (drag-to-reorder) — the slot the
720
+ // dragover indicator highlights, and the same result the header menu's "Move
721
+ // left"/"Move right" gives for a neighbouring column. The sticky entity
722
+ // column (index 0) stays pinned — never reorder into slot 0.
723
+ const reorderColumn = (fromKey, toKey) => {
724
+ setDragCol(null);
725
+ if (fromKey === toKey)
726
+ return;
727
+ setCols((prev) => {
728
+ const from = prev.findIndex((c) => c.key === fromKey);
729
+ const to = prev.findIndex((c) => c.key === toKey);
730
+ if (from < 1 || to < 1)
731
+ return prev;
732
+ const next = [...prev];
733
+ const [moved] = next.splice(from, 1);
734
+ // insert at the TARGET'S ORIGINAL index, which is the slot the dragover
735
+ // indicator highlighted. Re-finding the target after the splice shifts
736
+ // every rightward drag one slot left, so an adjacent one does nothing.
737
+ next.splice(to, 0, moved);
738
+ return next;
739
+ });
740
+ };
741
+ const renameColumn = (key, label) => {
742
+ const name = label.trim();
743
+ if (name)
744
+ setCols((prev) => prev.map((c) => (c.key === key ? { ...c, label: name } : c)));
745
+ setRenamingCol(null);
746
+ };
747
+ // the two `role="menu"` popovers driven by `openMenu`; the sort and filter
748
+ // popovers are dialogs and keep their own focus handling
749
+ const roleMenuOpen = !!openMenu && (openMenu.startsWith("col:") || openMenu.startsWith("sum:"));
750
+ const closeRoleMenu = React.useCallback(() => {
751
+ setOpenMenu(null);
752
+ menuReturn.current?.focus();
753
+ }, []);
754
+ const { onKeyDown: onMenuKeyDown } = (0, menu_1.useMenuKeyboard)({
755
+ open: roleMenuOpen,
756
+ close: closeRoleMenu,
757
+ dismiss: () => setOpenMenu(null),
758
+ contentRef: menuRef,
759
+ });
760
+ const closeCtxMenu = React.useCallback(() => {
761
+ setCtxMenu(null);
762
+ ctxReturn.current?.focus();
763
+ }, []);
764
+ const { onKeyDown: onCtxKeyDown } = (0, menu_1.useMenuKeyboard)({
765
+ open: !!ctxMenu,
766
+ close: closeCtxMenu,
767
+ dismiss: () => setCtxMenu(null),
768
+ contentRef: ctxRef,
769
+ });
770
+ const openColumnMenu = (key, el) => {
771
+ if (openMenu === `col:${key}`) {
772
+ setOpenMenu(null);
773
+ return;
774
+ }
775
+ const r = el.getBoundingClientRect();
776
+ setMenuPos({ top: r.bottom + 4, left: r.left });
777
+ menuReturn.current = el;
778
+ setOpenMenu(`col:${key}`);
779
+ };
780
+ const addColumn = () => {
781
+ idCounter.current += 1;
782
+ const key = `field-${idCounter.current}`;
783
+ setCols((prev) => [
784
+ ...prev,
785
+ { key, label: t.newColumnLabel(prev.length), type: "text", width: 160 },
786
+ ]);
787
+ };
788
+ const deleteSelected = () => {
789
+ setData((prev) => prev.filter((r) => !selected.has(r.id)));
790
+ setSelected(new Set());
791
+ };
792
+ const exportSelected = () => {
793
+ const picked = data.filter((r) => selected.has(r.id));
794
+ const esc = (s) => `"${s.replace(/"/g, '""')}"`;
795
+ const csv = [
796
+ cols.map((c) => esc(c.label)).join(","),
797
+ ...picked.map((r) => cols.map((c) => esc(cellText(c, r))).join(",")),
798
+ ].join("\n");
799
+ const url = URL.createObjectURL(new Blob([csv], { type: "text/csv;charset=utf-8" }));
800
+ const a = document.createElement("a");
801
+ a.href = url;
802
+ a.download = "records.csv";
803
+ a.click();
804
+ URL.revokeObjectURL(url);
805
+ setOpenMenu(null);
806
+ };
807
+ const entityKey = cols.find((c) => c.type === "entity")?.key;
808
+ // "Add to list" → pin the selected rows (shows a ★ on the entity)
809
+ const addToList = () => {
810
+ setPinned((prev) => new Set([...prev, ...selected]));
811
+ setSelected(new Set());
812
+ };
813
+ // "Send email" → open the mail client addressed to the selected records
814
+ const sendEmail = () => {
815
+ const names = data
816
+ .filter((r) => selected.has(r.id))
817
+ .map((r) => entityKey ? cellText({ key: entityKey, label: "" }, r) : r.id);
818
+ window.open(`mailto:?subject=${encodeURIComponent(names.join(", "))}`);
819
+ };
820
+ // "Run workflow" → a demo automation: duplicate the selected records
821
+ const runWorkflow = () => {
822
+ setData((prev) => {
823
+ const copies = prev
824
+ .filter((r) => selected.has(r.id))
825
+ .map((r) => {
826
+ idCounter.current += 1;
827
+ return { ...r, id: `${r.id}-copy-${idCounter.current}` };
828
+ });
829
+ return [...prev, ...copies];
830
+ });
831
+ setSelected(new Set());
832
+ };
833
+ // single-row actions used by the right-click context menu
834
+ const duplicateRow = (id) => {
835
+ setData((prev) => {
836
+ const i = prev.findIndex((r) => r.id === id);
837
+ if (i < 0)
838
+ return prev;
839
+ idCounter.current += 1;
840
+ const copy = { ...prev[i], id: `${id}-copy-${idCounter.current}` };
841
+ const next = [...prev];
842
+ next.splice(i + 1, 0, copy);
843
+ return next;
844
+ });
845
+ setCtxMenu(null);
846
+ };
847
+ const deleteRow = (id) => {
848
+ setData((prev) => prev.filter((r) => r.id !== id));
849
+ if (selected.has(id)) {
850
+ const next = new Set(selected);
851
+ next.delete(id);
852
+ setSelected(next);
853
+ }
854
+ setCtxMenu(null);
855
+ };
856
+ const copyRow = (id) => {
857
+ const row = data.find((r) => r.id === id);
858
+ const col = cols.find((c) => c.type === "entity");
859
+ if (row && col)
860
+ navigator.clipboard?.writeText(cellText(col, row));
861
+ setCtxMenu(null);
862
+ };
863
+ const pinRow = (id) => {
864
+ setPinned((prev) => new Set([...prev, id]));
865
+ setCtxMenu(null);
866
+ };
867
+ const setAggValue = (key, value) => {
868
+ setAgg((prev) => ({ ...prev, [key]: value }));
869
+ setOpenMenu(null);
870
+ };
871
+ // column resize via pointer drag on the header edge
872
+ const resizeCol = (key, startX, startW) => {
873
+ // guard the whole document during the drag: kill text selection and force a
874
+ // resize cursor so the gesture doesn't select cells or flicker (was buggy)
875
+ const prevSelect = document.body.style.userSelect;
876
+ const prevCursor = document.body.style.cursor;
877
+ document.body.style.userSelect = "none";
878
+ document.body.style.cursor = "col-resize";
879
+ const onMove = (e) => {
880
+ const w = Math.round(Math.max(72, startW + (e.clientX - startX)));
881
+ setCols((prev) => prev.map((c) => (c.key === key ? { ...c, width: w } : c)));
882
+ };
883
+ const onUp = () => {
884
+ document.body.style.userSelect = prevSelect;
885
+ document.body.style.cursor = prevCursor;
886
+ window.removeEventListener("pointermove", onMove);
887
+ window.removeEventListener("pointerup", onUp);
888
+ };
889
+ window.addEventListener("pointermove", onMove);
890
+ window.addEventListener("pointerup", onUp);
891
+ };
892
+ return ((0, jsx_runtime_1.jsxs)("div", { className: (0, recordstable_recipe_1.recordstableRecipe)({ className }), ref: rootRef, children: [(0, jsx_runtime_1.jsxs)("div", { className: "recordstable-toolbar", children: [(0, jsx_runtime_1.jsxs)("span", { className: "recordstable-toolgroup", children: [(0, jsx_runtime_1.jsxs)("button", { type: "button", className: `recordstable-tool${sorts.length ? " recordstable-tool--active" : ""}`, onClick: () => setOpenMenu(openMenu === "sort" ? null : "sort"), "aria-haspopup": "dialog", "aria-expanded": openMenu === "sort", "data-menu-trigger": true, children: [(0, jsx_runtime_1.jsx)(lucide_react_1.ArrowUpDown, { size: 14, strokeWidth: 1.75, "aria-hidden": "true" }), sorts.length
893
+ ? t.sortedBy(cols.find((c) => c.key === sorts[0].key)?.label ?? "", sorts.length - 1)
894
+ : t.sort] }), openMenu === "sort" && ((0, jsx_runtime_1.jsxs)("div", { className: "recordstable-menu recordstable-sortpop", role: "dialog", "aria-label": t.sort, children: [sorts.length === 0 ? ((0, jsx_runtime_1.jsx)("div", { className: "recordstable-sort-empty", children: t.noSorts })) : (sorts.map((s, i) => ((0, jsx_runtime_1.jsxs)("div", { className: "recordstable-sort-row", onDragOver: (e) => {
895
+ if (dragSort !== null)
896
+ e.preventDefault();
897
+ }, onDrop: () => {
898
+ if (dragSort !== null)
899
+ moveSort(dragSort, i);
900
+ setDragSort(null);
901
+ }, children: [(0, jsx_runtime_1.jsx)("button", { type: "button", className: "recordstable-sort-grip", "aria-label": t.reorderSort, draggable: true, onDragStart: () => setDragSort(i), onDragEnd: () => setDragSort(null), children: (0, jsx_runtime_1.jsx)(lucide_react_1.GripVertical, { size: 15, strokeWidth: 1.75 }) }), (0, jsx_runtime_1.jsx)(select_1.Select, { className: "recordstable-sort-field", searchable: true, value: s.key, onValueChange: (k) => setSortField(i, k), options: cols.map((c) => ({
902
+ value: c.key,
903
+ label: c.icon ? ((0, jsx_runtime_1.jsxs)("span", { className: "recordstable-sort-optlabel", children: [(0, jsx_runtime_1.jsx)(c.icon, { size: 15, strokeWidth: 1.75 }), c.label] })) : (c.label),
904
+ keywords: c.label,
905
+ })), "aria-label": t.sortField }), (0, jsx_runtime_1.jsx)(select_1.Select, { className: "recordstable-sort-dir", value: s.dir, onValueChange: (d) => setSortDir(i, d), options: [
906
+ {
907
+ value: "asc",
908
+ label: ((0, jsx_runtime_1.jsxs)("span", { className: "recordstable-sort-optlabel", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.ArrowUpNarrowWide, { size: 15, strokeWidth: 1.75 }), t.ascending] })),
909
+ keywords: t.ascending,
910
+ },
911
+ {
912
+ value: "desc",
913
+ label: ((0, jsx_runtime_1.jsxs)("span", { className: "recordstable-sort-optlabel", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.ArrowDownWideNarrow, { size: 15, strokeWidth: 1.75 }), t.descending] })),
914
+ keywords: t.descending,
915
+ },
916
+ ], "aria-label": t.sortDirection }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "recordstable-sort-remove", onClick: () => removeSort(i), "aria-label": t.removeSort, children: (0, jsx_runtime_1.jsx)(lucide_react_1.X, { size: 15, strokeWidth: 1.75 }) })] }, s.key)))), (0, jsx_runtime_1.jsx)("div", { className: "recordstable-sort-addrow", children: (0, jsx_runtime_1.jsxs)("button", { type: "button", className: "recordstable-sort-add", onClick: addSort, children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Plus, { size: 15, strokeWidth: 1.75 }), t.addSort] }) }), (0, jsx_runtime_1.jsx)("div", { className: "recordstable-sort-foot", children: (0, jsx_runtime_1.jsxs)("a", { className: "recordstable-sort-learn", href: "https://attio.com/help", target: "_blank", rel: "noreferrer", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.CircleHelp, { size: 14, strokeWidth: 1.75 }), t.learnSorting] }) })] }))] }), (0, jsx_runtime_1.jsxs)("span", { className: "recordstable-toolgroup", children: [(0, jsx_runtime_1.jsxs)("button", { type: "button", className: `recordstable-tool recordstable-tool--ghost${activeFilters ? " recordstable-tool--active" : ""}`, onClick: () => setOpenMenu(openMenu === "filter" ? null : "filter"), "aria-haspopup": "dialog", "aria-expanded": openMenu === "filter", "data-menu-trigger": true, children: [(0, jsx_runtime_1.jsx)(lucide_react_1.ListFilter, { size: 14, strokeWidth: 1.75, "aria-hidden": "true" }), t.filterCount(activeFilters)] }), openMenu === "filter" && ((0, jsx_runtime_1.jsxs)("div", { className: "recordstable-menu", role: "dialog", "aria-label": t.addFilter, children: [(0, jsx_runtime_1.jsx)("input", { className: "recordstable-menu-input", placeholder: t.filterByPlaceholder, "aria-label": t.filterByField,
917
+ // biome-ignore lint/a11y/noAutofocus: focus the picker on open
918
+ autoFocus: true, value: filterQuery, onChange: (e) => setFilterQuery(e.target.value) }), cols
919
+ .filter((col) => col.label
920
+ .toLowerCase()
921
+ .includes(filterQuery.trim().toLowerCase()))
922
+ .map((col) => ((0, jsx_runtime_1.jsxs)("button", { type: "button", className: "recordstable-menu-item", onClick: () => addFilter(col.key), children: [col.icon ? ((0, jsx_runtime_1.jsx)(col.icon, { size: 15, strokeWidth: 1.75 })) : ((0, jsx_runtime_1.jsx)("span", { style: { width: 15 } })), col.label] }, col.key)))] }))] }), filterConds.map((cond) => {
923
+ const col = cols.find((c) => c.key === cond.key);
924
+ const open = openMenu === `fcond:${cond.id}`;
925
+ const hasValue = !opNeedsValue(cond.op) || cond.value.trim();
926
+ return ((0, jsx_runtime_1.jsxs)("span", { className: "recordstable-toolgroup", children: [(0, jsx_runtime_1.jsxs)("span", { className: "recordstable-fchip", children: [(0, jsx_runtime_1.jsxs)("button", { type: "button", className: "recordstable-fchip-main", "data-menu-trigger": true, "aria-haspopup": "dialog", "aria-expanded": open, onClick: () => setOpenMenu(open ? null : `fcond:${cond.id}`), children: [(0, jsx_runtime_1.jsx)("span", { className: "recordstable-fchip-field", children: col?.label }), hasValue ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("span", { className: "recordstable-fchip-op", children: t.op[cond.op] }), opNeedsValue(cond.op) && ((0, jsx_runtime_1.jsx)("span", { className: "recordstable-fchip-val", children: cond.value }))] })) : ((0, jsx_runtime_1.jsx)("span", { className: "recordstable-fchip-op", children: t.selectCondition }))] }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "recordstable-fchip-x", "aria-label": `Remove ${col?.label} filter`, onClick: () => removeFilter(cond.id), children: (0, jsx_runtime_1.jsx)(lucide_react_1.X, { size: 13, strokeWidth: 2 }) })] }), open && ((0, jsx_runtime_1.jsxs)("div", { className: "recordstable-menu", role: "dialog", "aria-label": `${col?.label} condition`, children: [(0, jsx_runtime_1.jsxs)("div", { className: "recordstable-menu-head", children: [col?.label, " condition"] }), OP_ORDER.map((op) => ((0, jsx_runtime_1.jsxs)("button", { type: "button", className: "recordstable-menu-item", "data-on": cond.op === op ? "" : undefined, onClick: () => patchFilter(cond.id, { op }), children: [t.op[op], cond.op === op ? ((0, jsx_runtime_1.jsx)(lucide_react_1.Check, { className: "recordstable-menu-check", size: 15, strokeWidth: 1.75 })) : null] }, op))), opNeedsValue(cond.op) && ((0, jsx_runtime_1.jsx)("input", { className: "recordstable-menu-input", placeholder: t.filterValuePlaceholder, "aria-label": `${col?.label} filter value`,
927
+ // biome-ignore lint/a11y/noAutofocus: focus the value field
928
+ autoFocus: true, value: cond.value, onChange: (e) => patchFilter(cond.id, { value: e.target.value }) }))] }))] }, cond.id));
929
+ }), (0, jsx_runtime_1.jsx)("span", { className: "recordstable-spacer" }), (0, jsx_runtime_1.jsx)("span", { className: "recordstable-count", children: t.recordCount(visibleRows.length) }), showNew && ((0, jsx_runtime_1.jsxs)("button", { type: "button", className: "recordstable-new", onClick: () => setCreating(true), children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Plus, { size: 15, strokeWidth: 2, "aria-hidden": "true" }), t.newButton] }))] }), (0, jsx_runtime_1.jsxs)("div", { ref: scrollRef, className: "recordstable-scroll", tabIndex: -1, "data-scrolled": scrolledX ? "" : undefined, onKeyDown: onGridKeyDown, onScroll: (e) => {
930
+ const left = e.currentTarget.scrollLeft > 0;
931
+ setScrolledX((prev) => (prev === left ? prev : left));
932
+ }, children: [(0, jsx_runtime_1.jsxs)("table", { className: "recordstable-table", style: {
933
+ // fill the container; the trailing add-column absorbs the slack (a
934
+ // flexible filler). minWidth keeps real column widths and drives the
935
+ // horizontal scroll once the columns outgrow the container.
936
+ width: "100%",
937
+ minWidth: CHECK_W +
938
+ ADDCOL_W +
939
+ cols.reduce((sum, c) => sum + colWidth(c), 0),
940
+ }, children: [(0, jsx_runtime_1.jsxs)("colgroup", { children: [(0, jsx_runtime_1.jsx)("col", { style: { width: CHECK_W } }), cols.map((col) => ((0, jsx_runtime_1.jsx)("col", { style: { width: colWidth(col) } }, col.key))), (0, jsx_runtime_1.jsx)("col", {})] }), (0, jsx_runtime_1.jsx)("thead", { children: (0, jsx_runtime_1.jsxs)("tr", { children: [(0, jsx_runtime_1.jsx)("th", { className: "recordstable-th recordstable-col-check", children: (0, jsx_runtime_1.jsx)("span", { className: "recordstable-check-inner", children: (0, jsx_runtime_1.jsx)(checkbox_1.Checkbox, { "aria-label": t.selectAll, checked: allSelected, indeterminate: someSelected, onChange: toggleAll }) }) }), cols.map((col, i) => {
941
+ const Icon = col.icon;
942
+ const sc = sorts.find((s) => s.key === col.key);
943
+ return ((0, jsx_runtime_1.jsxs)("th", { className: `recordstable-th${i === 0 ? " recordstable-col-entity" : ""}`, "data-sorted": sc?.dir, "data-drop": dragCol && dragCol !== col.key && i >= 1 ? "" : undefined, onDragOver: dragCol && i >= 1 ? (e) => e.preventDefault() : undefined, onDrop: dragCol && i >= 1
944
+ ? (e) => {
945
+ e.preventDefault();
946
+ reorderColumn(dragCol, col.key);
947
+ }
948
+ : undefined, children: [renamingCol === col.key ? ((0, jsx_runtime_1.jsx)("input", { className: "recordstable-th-rename",
949
+ // biome-ignore lint/a11y/noAutofocus: focus the rename field the moment it opens
950
+ autoFocus: true, defaultValue: col.label, "aria-label": t.columnLabel, onKeyDown: (e) => {
951
+ if (e.key === "Enter")
952
+ renameColumn(col.key, e.currentTarget.value);
953
+ else if (e.key === "Escape")
954
+ setRenamingCol(null);
955
+ }, onBlur: (e) => renameColumn(col.key, e.currentTarget.value) })) : ((0, jsx_runtime_1.jsxs)("button", { type: "button", className: "recordstable-th-inner", "data-menu-trigger": true, "aria-haspopup": "menu", draggable: i >= 1, onDragStart: i >= 1
956
+ ? (e) => {
957
+ setDragCol(col.key);
958
+ e.dataTransfer.effectAllowed = "move";
959
+ }
960
+ : undefined, onDragEnd: () => setDragCol(null), onClick: (e) => openColumnMenu(col.key, e.currentTarget), children: [Icon ? (0, jsx_runtime_1.jsx)(Icon, { size: 14, strokeWidth: 1.75 }) : null, (0, jsx_runtime_1.jsx)("span", { className: "recordstable-th-label", children: col.label }), (0, jsx_runtime_1.jsx)("span", { className: "recordstable-sort", children: (0, jsx_runtime_1.jsx)("svg", { width: "12", height: "12", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "1.5", "aria-hidden": "true", children: (0, jsx_runtime_1.jsx)("path", { d: "m4 6 4 4 4-4" }) }) })] })), openMenu === `col:${col.key}` &&
961
+ rootRef.current &&
962
+ (0, react_dom_1.createPortal)((0, jsx_runtime_1.jsxs)("div", { ref: menuRef, className: "recordstable-menu recordstable-menu--fixed recordstable-menu--hdr", role: "menu", "aria-label": `${col.label} column`, style: menuPos ?? undefined, onKeyDown: onMenuKeyDown, children: [(0, jsx_runtime_1.jsxs)("button", { type: "button", className: "recordstable-menu-item", role: "menuitem", tabIndex: -1, onClick: () => sortBy(col.key, "asc"), children: [(0, jsx_runtime_1.jsx)(lucide_react_1.ArrowUp, { size: 15, strokeWidth: 1.75 }), t.sortAscending] }), (0, jsx_runtime_1.jsxs)("button", { type: "button", className: "recordstable-menu-item", role: "menuitem", tabIndex: -1, onClick: () => sortBy(col.key, "desc"), children: [(0, jsx_runtime_1.jsx)(lucide_react_1.ArrowDown, { size: 15, strokeWidth: 1.75 }), t.sortDescending] }), (0, jsx_runtime_1.jsxs)("button", { type: "button", className: "recordstable-menu-item", role: "menuitem", tabIndex: -1, disabled: i <= 1, onClick: () => moveColumn(col.key, -1), children: [(0, jsx_runtime_1.jsx)(lucide_react_1.ArrowLeft, { size: 15, strokeWidth: 1.75 }), t.moveLeft] }), (0, jsx_runtime_1.jsxs)("button", { type: "button", className: "recordstable-menu-item", role: "menuitem", tabIndex: -1, disabled: i === 0 || i >= cols.length - 1, onClick: () => moveColumn(col.key, 1), children: [(0, jsx_runtime_1.jsx)(lucide_react_1.ArrowRight, { size: 15, strokeWidth: 1.75 }), t.moveRight] }), (0, jsx_runtime_1.jsx)("div", { className: "recordstable-menu-sep" }), (0, jsx_runtime_1.jsxs)("button", { type: "button", className: "recordstable-menu-item", role: "menuitem", tabIndex: -1, onClick: () => {
963
+ setRenamingCol(col.key);
964
+ setOpenMenu(null);
965
+ }, children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Pencil, { size: 15, strokeWidth: 1.75 }), t.editColumnLabel] }), cols.length > 1 && ((0, jsx_runtime_1.jsxs)("button", { type: "button", className: "recordstable-menu-item", role: "menuitem", tabIndex: -1, onClick: () => hideColumn(col.key), children: [(0, jsx_runtime_1.jsx)(lucide_react_1.EyeOff, { size: 15, strokeWidth: 1.75 }), t.hideFromView] }))] }), rootRef.current), (0, jsx_runtime_1.jsx)("span", { className: "recordstable-resize", "aria-hidden": "true", onPointerDown: (e) => {
966
+ e.preventDefault();
967
+ e.stopPropagation();
968
+ const th = e.currentTarget
969
+ .parentElement;
970
+ resizeCol(col.key, e.clientX, th ? th.getBoundingClientRect().width : 180);
971
+ }, onClick: (e) => e.stopPropagation() })] }, col.key));
972
+ }), (0, jsx_runtime_1.jsx)("th", { className: "recordstable-th recordstable-addcol", children: allowAddColumn && ((0, jsx_runtime_1.jsx)("button", { type: "button", onClick: addColumn, "aria-label": t.addColumn, children: (0, jsx_runtime_1.jsx)(lucide_react_1.Plus, { size: 15, strokeWidth: 1.75 }) })) })] }) }), (0, jsx_runtime_1.jsx)("tbody", { children: visibleRows.map((row) => ((0, jsx_runtime_1.jsxs)("tr", { "data-selected": selected.has(row.id) ? "" : undefined, onContextMenu: (e) => {
973
+ e.preventDefault();
974
+ ctxReturn.current = document.activeElement;
975
+ setCtxMenu({ x: e.clientX, y: e.clientY, id: row.id });
976
+ }, children: [(0, jsx_runtime_1.jsx)("td", { className: "recordstable-td recordstable-col-check", children: (0, jsx_runtime_1.jsx)("span", { className: "recordstable-check-inner", children: (0, jsx_runtime_1.jsx)(checkbox_1.Checkbox, { "aria-label": `Select ${row.id}`, checked: selected.has(row.id), onClick: (e) => {
977
+ shiftHeld.current = e.shiftKey;
978
+ }, onChange: () => toggleRow(row.id) }) }) }), cols.map((col, i) => {
979
+ const canEdit = canEditCol(col);
980
+ const isPicker = isPickerCol(col);
981
+ const isEditing = editing?.id === row.id && editing?.key === col.key;
982
+ return ((0, jsx_runtime_1.jsxs)("td", { className: `recordstable-td${i === 0 ? " recordstable-col-entity" : ""}${canEdit ? " recordstable-td--editable" : ""}${isPicker ? " recordstable-td--picker" : ""}`, "data-enriched": col.enriched ? "" : undefined, "data-selected": selCell?.id === row.id && selCell?.key === col.key
983
+ ? ""
984
+ : undefined, onClick: () => {
985
+ if (isPicker)
986
+ return;
987
+ setSelCell({ id: row.id, key: col.key });
988
+ scrollRef.current?.focus({ preventScroll: true });
989
+ }, onDoubleClick: canEdit
990
+ ? () => setEditing({ id: row.id, key: col.key })
991
+ : undefined, children: [isPicker ? (col.type === "status" ? ((0, jsx_runtime_1.jsx)(StatusCell, { column: col, row: row, onChange: (v) => setCell(row.id, col.key, v) })) : col.type === "reference" ? ((0, jsx_runtime_1.jsx)(ReferenceCell, { labels: t, column: col, row: row, onChange: (v) => setCell(row.id, col.key, v) })) : col.type === "tags" ? ((0, jsx_runtime_1.jsx)(TagsCell, { labels: t, column: col, row: row, options: tagOptionsFor(col.key), onChange: (v) => setCell(row.id, col.key, v) })) : ((0, jsx_runtime_1.jsx)(DateCell, { column: col, row: row, onChange: (v) => setCell(row.id, col.key, v) }))) : isEditing ? ((0, jsx_runtime_1.jsx)("input", { className: `recordstable-edit${col.type === "number" ? " recordstable-edit--num" : ""}`,
992
+ // biome-ignore lint/a11y/noAutofocus: focusing the just-opened editor is the expected behavior
993
+ autoFocus: true, defaultValue: cellText(col, row), onBlur: (e) => commitEdit(row.id, col.key, e.target.value), onKeyDown: (e) => {
994
+ if (e.key === "Enter")
995
+ commitEdit(row.id, col.key, e.target.value);
996
+ if (e.key === "Escape")
997
+ setEditing(null);
998
+ } })) : ((0, jsx_runtime_1.jsx)(CellView, { labels: t, column: col, row: row, pinned: i === 0 && pinned.has(row.id), onOpenPage: i === 0 && onOpenRecordPage
999
+ ? () => onOpenRecordPage(row)
1000
+ : undefined, avatarTone: avatarTone })), i === 0 && onOpenRecord && !isEditing && ((0, jsx_runtime_1.jsx)("button", { type: "button", className: "recordstable-open", "aria-label": `Open ${cellText(col, row)}`, onClick: (e) => {
1001
+ e.stopPropagation();
1002
+ onOpenRecord(row);
1003
+ }, children: (0, jsx_runtime_1.jsx)(lucide_react_1.ArrowUpRight, { size: 15, strokeWidth: 1.75 }) }))] }, col.key));
1004
+ }), (0, jsx_runtime_1.jsx)("td", { className: "recordstable-td recordstable-addcol" })] }, row.id))) }), (0, jsx_runtime_1.jsx)("tfoot", { children: (0, jsx_runtime_1.jsxs)("tr", { children: [(0, jsx_runtime_1.jsx)("td", { className: "recordstable-sum recordstable-col-check" }), cols.map((col, i) => {
1005
+ const a = agg[col.key] ?? "none";
1006
+ const res = summarize(col, a, visibleRows);
1007
+ const sumOpen = openMenu === `sum:${col.key}`;
1008
+ return ((0, jsx_runtime_1.jsx)("td", { className: `recordstable-sum${i === 0 ? " recordstable-col-entity" : ""}`, children: (0, jsx_runtime_1.jsxs)("span", { className: "recordstable-toolgroup recordstable-sumgroup", children: [(0, jsx_runtime_1.jsx)("button", { type: "button", className: "recordstable-sum-btn", "data-empty": res ? undefined : "", "data-menu-trigger": true, "aria-haspopup": "menu", onClick: (e) => {
1009
+ if (sumOpen) {
1010
+ setOpenMenu(null);
1011
+ return;
1012
+ }
1013
+ const r = e.currentTarget.getBoundingClientRect();
1014
+ setMenuPos({
1015
+ bottom: window.innerHeight - r.top + 4,
1016
+ right: window.innerWidth - r.right,
1017
+ });
1018
+ menuReturn.current = e.currentTarget;
1019
+ setOpenMenu(`sum:${col.key}`);
1020
+ }, children: res ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("span", { className: "recordstable-sum-label", children: t.agg[a] }), (0, jsx_runtime_1.jsx)("span", { className: "recordstable-sum-value", children: res.value })] })) : ((0, jsx_runtime_1.jsx)("span", { className: "recordstable-sum-label", children: t.calculate })) }), sumOpen &&
1021
+ rootRef.current &&
1022
+ (0, react_dom_1.createPortal)((0, jsx_runtime_1.jsx)("div", { ref: menuRef, className: "recordstable-menu recordstable-menu--fixed recordstable-menu--up", role: "menu", "aria-label": `${col.label} summary`, style: menuPos ?? undefined, onKeyDown: onMenuKeyDown, children: aggOptions(col).map((opt) => ((0, jsx_runtime_1.jsxs)("button", { type: "button", className: "recordstable-menu-item", role: "menuitemradio", tabIndex: -1, "aria-checked": a === opt, "data-on": a === opt ? "" : undefined, onClick: () => setAggValue(col.key, opt), children: [t.agg[opt], a === opt ? ((0, jsx_runtime_1.jsx)(lucide_react_1.Check, { className: "recordstable-menu-check", size: 15, strokeWidth: 1.75 })) : null] }, opt))) }), rootRef.current)] }) }, col.key));
1023
+ }), (0, jsx_runtime_1.jsx)("td", { className: "recordstable-sum recordstable-addcol" })] }) })] }), visibleRows.length === 0 ? ((0, jsx_runtime_1.jsx)("div", { className: "recordstable-empty", children: (0, jsx_runtime_1.jsx)(emptystate_1.EmptyState, { variant: "page", icon: activeFilters ? ((0, jsx_runtime_1.jsx)(lucide_react_1.ListFilter, { size: 18, strokeWidth: 1.75, "aria-hidden": "true" })) : ((0, jsx_runtime_1.jsx)(lucide_react_1.Inbox, { size: 18, strokeWidth: 1.75, "aria-hidden": "true" })), title: activeFilters ? "No records match that filter" : t.noRecords, description: activeFilters
1024
+ ? "None of your records match the filters that are set."
1025
+ : `Create your first ${cols.find((c) => c.type === "entity")?.label?.toLowerCase() ?? "record"} to get started.`, action: activeFilters ? ((0, jsx_runtime_1.jsxs)(button_1.Button, { intent: "primary", size: "sm", onClick: () => setFilterConds([]), children: [(0, jsx_runtime_1.jsx)(lucide_react_1.ListFilter, { size: 15, strokeWidth: 1.75 }), t.clearFilter] })) : ((0, jsx_runtime_1.jsxs)(button_1.Button, { intent: "primary", size: "sm", onClick: addRow, children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Plus, { size: 15, strokeWidth: 1.75 }), t.newRecord] })) }) })) : ((0, jsx_runtime_1.jsxs)("button", { type: "button", className: "recordstable-addrow", onClick: addRow, children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Plus, { size: 15, strokeWidth: 1.75, "aria-hidden": "true" }), t.newRecord] }))] }), selected.size > 0 && ((0, jsx_runtime_1.jsxs)(bulkbar_1.BulkBar, { count: selected.size, countLabel: "Selected", onClear: () => setSelected(new Set()), children: [(0, jsx_runtime_1.jsx)(bulkbar_1.BulkBarButton, { icon: (0, jsx_runtime_1.jsx)(lucide_react_1.ListPlus, { size: 15, strokeWidth: 1.75 }), onClick: addToList, children: t.addToList }), (0, jsx_runtime_1.jsx)(bulkbar_1.BulkBarButton, { icon: (0, jsx_runtime_1.jsx)(lucide_react_1.Mail, { size: 15, strokeWidth: 1.75 }), onClick: sendEmail, children: t.sendEmail }), (0, jsx_runtime_1.jsx)(bulkbar_1.BulkBarButton, { icon: (0, jsx_runtime_1.jsx)(lucide_react_1.Workflow, { size: 15, strokeWidth: 1.75 }), onClick: runWorkflow, children: t.runWorkflow }), (0, jsx_runtime_1.jsxs)(menu_1.Menu, { align: "end", "aria-label": t.moreActions, trigger: (0, jsx_runtime_1.jsx)(bulkbar_1.BulkBarButton, { icon: (0, jsx_runtime_1.jsx)(lucide_react_1.MoreHorizontal, { size: 15, strokeWidth: 1.75 }), children: t.more }), children: [(0, jsx_runtime_1.jsx)(menu_1.MenuItem, { icon: (0, jsx_runtime_1.jsx)(lucide_react_1.Download, { size: 15, strokeWidth: 1.75 }), onSelect: exportSelected, children: t.exportCsv }), (0, jsx_runtime_1.jsx)(menu_1.MenuItem, { icon: (0, jsx_runtime_1.jsx)(lucide_react_1.Trash2, { size: 15, strokeWidth: 1.75 }), danger: true, onSelect: deleteSelected, children: t.delete })] })] })), (0, jsx_runtime_1.jsx)(CreateRecordDialog, { labels: t, open: creating, onOpenChange: setCreating, columns: cols, entityLabel: cols.find((c) => c.type === "entity")?.label ?? "record", onCreate: (row) => setData((prev) => [...prev, row]) }), ctxMenu &&
1026
+ (0, react_dom_1.createPortal)((0, jsx_runtime_1.jsxs)("div", { ref: ctxRef, className: "recordstable-menu recordstable-ctx", role: "menu", "aria-label": t.recordActions, style: { position: "fixed", top: ctxMenu.y, left: ctxMenu.x }, onKeyDown: onCtxKeyDown, children: [(0, jsx_runtime_1.jsxs)("button", { type: "button", className: "recordstable-menu-item", role: "menuitem", tabIndex: -1, onClick: () => copyRow(ctxMenu.id), children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Copy, { size: 15, strokeWidth: 1.75 }), t.copy] }), (0, jsx_runtime_1.jsxs)("button", { type: "button", className: "recordstable-menu-item", role: "menuitem", tabIndex: -1, onClick: () => duplicateRow(ctxMenu.id), children: [(0, jsx_runtime_1.jsx)(lucide_react_1.CopyPlus, { size: 15, strokeWidth: 1.75 }), t.duplicate] }), (0, jsx_runtime_1.jsxs)("button", { type: "button", className: "recordstable-menu-item", role: "menuitem", tabIndex: -1, onClick: () => pinRow(ctxMenu.id), children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Star, { size: 15, strokeWidth: 1.75 }), t.addToList] }), (0, jsx_runtime_1.jsxs)("button", { type: "button", className: "recordstable-menu-item recordstable-menu-item--danger", role: "menuitem", tabIndex: -1, onClick: () => deleteRow(ctxMenu.id), children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Trash2, { size: 15, strokeWidth: 1.75 }), t.delete] })] }), rootRef.current ?? document.body)] }));
1027
+ }
1028
+ exports.defaultColumns = [
1029
+ { key: "company", label: "Company", type: "entity", icon: lucide_react_1.Building2 },
1030
+ { key: "domain", label: "Domain", type: "url", icon: lucide_react_1.Globe },
1031
+ {
1032
+ key: "categories",
1033
+ label: "Categories",
1034
+ type: "tags",
1035
+ icon: lucide_react_1.Tag,
1036
+ width: 240,
1037
+ },
1038
+ {
1039
+ key: "employees",
1040
+ label: "Employees",
1041
+ type: "number",
1042
+ icon: lucide_react_1.Users,
1043
+ width: 120,
1044
+ },
1045
+ { key: "location", label: "Location", type: "text", icon: lucide_react_1.MapPin },
1046
+ { key: "stage", label: "Stage", type: "tags", icon: lucide_react_1.TrendingUp, width: 150 },
1047
+ { key: "ceo", label: "CEO", type: "text", icon: lucide_react_1.Briefcase, width: 170 },
1048
+ { key: "twitter", label: "Twitter", type: "url", icon: lucide_react_1.AtSign, width: 150 },
1049
+ {
1050
+ key: "followers",
1051
+ label: "Followers",
1052
+ type: "number",
1053
+ icon: lucide_react_1.Users,
1054
+ width: 120,
1055
+ },
1056
+ {
1057
+ key: "linkedin",
1058
+ label: "LinkedIn",
1059
+ type: "url",
1060
+ icon: lucide_react_1.Globe,
1061
+ width: 180,
1062
+ },
1063
+ {
1064
+ key: "founded",
1065
+ label: "Founded",
1066
+ type: "date",
1067
+ icon: lucide_react_1.Calendar,
1068
+ width: 110,
1069
+ },
1070
+ { key: "arr", label: "Est. ARR", type: "number", icon: lucide_react_1.Hash, width: 130 },
1071
+ ];
1072
+ // biome-ignore format: one row per line keeps the wide demo dataset readable
1073
+ const RAW_ROWS = [
1074
+ { id: "google", company: { name: "Google" }, domain: "google.com", categories: ["Search", "Ads", "Cloud", "AI", "Android", "Maps", "Hardware", "Workspace", "Chrome"], employees: 182000, location: "Mountain View, CA", stage: ["Public"], ceo: "Sundar Pichai", twitter: "twitter.com/google", followers: 29800000, linkedin: "linkedin.com/company/google", founded: "1998", arr: 307000000000 },
1075
+ { id: "stripe", company: { name: "Stripe" }, domain: "stripe.com", categories: ["Payments", "Fintech"], employees: 8000, location: "San Francisco, CA", stage: ["Series I"], ceo: "Patrick Collison", twitter: "twitter.com/stripe", followers: 1200000, linkedin: "linkedin.com/company/stripe", founded: "2010", arr: 16000000000 },
1076
+ { id: "figma", company: { name: "Figma" }, domain: "figma.com", categories: ["Design", "SaaS"], employees: 1300, location: "San Francisco, CA", stage: ["Series E"], ceo: "Dylan Field", twitter: "twitter.com/figma", followers: 540000, linkedin: "linkedin.com/company/figma", founded: "2012", arr: 600000000 },
1077
+ { id: "notion", company: { name: "Notion" }, domain: "notion.so", categories: ["Productivity", "SaaS"], employees: 800, location: "San Francisco, CA", stage: ["Series C"], ceo: "Ivan Zhao", twitter: "twitter.com/notionhq", followers: 480000, linkedin: "linkedin.com/company/notionhq", founded: "2013", arr: 400000000 },
1078
+ { id: "linear", company: { name: "Linear" }, domain: "linear.app", categories: ["Dev tools"], employees: 60, location: "Remote", stage: ["Series B"], ceo: "Karri Saarinen", twitter: "twitter.com/linear", followers: 120000, linkedin: "linkedin.com/company/linear", founded: "2019", arr: 30000000 },
1079
+ { id: "vercel", company: { name: "Vercel" }, domain: "vercel.com", categories: ["Dev tools", "Cloud"], employees: 500, location: "San Francisco, CA", stage: ["Series E"], ceo: "Guillermo Rauch", twitter: "twitter.com/vercel", followers: 310000, linkedin: "linkedin.com/company/vercel", founded: "2015", arr: 100000000 },
1080
+ { id: "openai", company: { name: "OpenAI" }, domain: "openai.com", categories: ["AI", "Research"], employees: 3500, location: "San Francisco, CA", stage: ["Late stage"], ceo: "Sam Altman", twitter: "twitter.com/openai", followers: 4200000, linkedin: "linkedin.com/company/openai", founded: "2015", arr: 3400000000 },
1081
+ { id: "anthropic", company: { name: "Anthropic" }, domain: "anthropic.com", categories: ["AI", "Research"], employees: 1000, location: "San Francisco, CA", stage: ["Series E"], ceo: "Dario Amodei", twitter: "twitter.com/anthropicai", followers: 520000, linkedin: "linkedin.com/company/anthropic", founded: "2021", arr: 1000000000 },
1082
+ ];
1083
+ // Attach a real favicon per company (derived from the domain); the entity cell
1084
+ // falls back to a letter tile if it fails to load.
1085
+ exports.defaultRows = RAW_ROWS.map((r) => ({
1086
+ ...r,
1087
+ company: {
1088
+ ...r.company,
1089
+ avatar: company_logos_1.companyLogos[r.domain] ??
1090
+ `https://www.google.com/s2/favicons?domain=${r.domain}&sz=64`,
1091
+ },
1092
+ }));