@crisp-ui-kit/crisp 0.5.0 → 0.7.1

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 (472) hide show
  1. package/README.md +14 -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/board.css +9 -1
  10. package/dist/bulkbar.css +11 -3
  11. package/dist/button.css +15 -1
  12. package/dist/button.d.ts +2 -1
  13. package/dist/card.css +8 -0
  14. package/dist/checkbox.css +17 -1
  15. package/dist/cjs/components/accordion/accordion.js +69 -0
  16. package/dist/cjs/components/accordion/accordion.recipe.js +26 -0
  17. package/dist/cjs/components/accordion/index.js +7 -0
  18. package/dist/cjs/components/avatar/avatar.js +44 -0
  19. package/dist/cjs/components/avatar/avatar.recipe.js +42 -0
  20. package/dist/cjs/components/avatar/index.js +7 -0
  21. package/dist/cjs/components/avatargroup/avatargroup.js +52 -0
  22. package/dist/cjs/components/avatargroup/avatargroup.recipe.js +18 -0
  23. package/dist/cjs/components/avatargroup/index.js +7 -0
  24. package/dist/cjs/components/badge/badge.js +44 -0
  25. package/dist/cjs/components/badge/badge.recipe.js +68 -0
  26. package/dist/cjs/components/badge/index.js +7 -0
  27. package/dist/cjs/components/barchart/barchart.js +45 -0
  28. package/dist/cjs/components/barchart/barchart.recipe.js +34 -0
  29. package/dist/cjs/components/barchart/index.js +7 -0
  30. package/dist/cjs/components/breadcrumb/breadcrumb.js +53 -0
  31. package/dist/cjs/components/breadcrumb/breadcrumb.recipe.js +21 -0
  32. package/dist/cjs/components/breadcrumb/index.js +7 -0
  33. package/dist/cjs/components/bulkbar/bulkbar.js +53 -0
  34. package/dist/cjs/components/bulkbar/bulkbar.recipe.js +31 -0
  35. package/dist/cjs/components/bulkbar/index.js +8 -0
  36. package/dist/cjs/components/button/button.js +62 -0
  37. package/dist/cjs/components/button/button.recipe.js +58 -0
  38. package/dist/cjs/components/button/index.js +7 -0
  39. package/dist/cjs/components/callout/callout.js +25 -0
  40. package/dist/cjs/components/callout/callout.recipe.js +29 -0
  41. package/dist/cjs/components/callout/index.js +7 -0
  42. package/dist/cjs/components/card/card.js +54 -0
  43. package/dist/cjs/components/card/card.recipe.js +31 -0
  44. package/dist/cjs/components/card/index.js +10 -0
  45. package/dist/cjs/components/checkbox/checkbox.js +56 -0
  46. package/dist/cjs/components/checkbox/checkbox.recipe.js +34 -0
  47. package/dist/cjs/components/checkbox/index.js +7 -0
  48. package/dist/cjs/components/columncalc/columncalc.js +139 -0
  49. package/dist/cjs/components/columncalc/columncalc.recipe.js +33 -0
  50. package/dist/cjs/components/columncalc/index.js +10 -0
  51. package/dist/cjs/components/contextmenu/contextmenu.js +78 -0
  52. package/dist/cjs/components/contextmenu/contextmenu.recipe.js +24 -0
  53. package/dist/cjs/components/contextmenu/index.js +7 -0
  54. package/dist/cjs/components/datatable/datatable.js +508 -0
  55. package/dist/cjs/components/datatable/datatable.recipe.js +113 -0
  56. package/dist/cjs/components/datatable/index.js +7 -0
  57. package/dist/cjs/components/datepicker/datepicker.js +153 -0
  58. package/dist/cjs/components/datepicker/datepicker.recipe.js +190 -0
  59. package/dist/cjs/components/datepicker/index.js +19 -0
  60. package/dist/cjs/components/datepickerfield/datepickerfield.js +85 -0
  61. package/dist/cjs/components/datepickerfield/datepickerfield.recipe.js +44 -0
  62. package/dist/cjs/components/datepickerfield/index.js +7 -0
  63. package/dist/cjs/components/dialog/dialog.js +86 -0
  64. package/dist/cjs/components/dialog/dialog.recipe.js +35 -0
  65. package/dist/cjs/components/dialog/index.js +9 -0
  66. package/dist/cjs/components/divider/divider.js +10 -0
  67. package/dist/cjs/components/divider/divider.recipe.js +17 -0
  68. package/dist/cjs/components/divider/index.js +7 -0
  69. package/dist/cjs/components/donutchart/donutchart.js +41 -0
  70. package/dist/cjs/components/donutchart/donutchart.recipe.js +30 -0
  71. package/dist/cjs/components/donutchart/index.js +7 -0
  72. package/dist/cjs/components/drawer/drawer.js +80 -0
  73. package/dist/cjs/components/drawer/drawer.recipe.js +30 -0
  74. package/dist/cjs/components/drawer/index.js +9 -0
  75. package/dist/cjs/components/emptystate/emptystate.js +10 -0
  76. package/dist/cjs/components/emptystate/emptystate.recipe.js +34 -0
  77. package/dist/cjs/components/emptystate/index.js +7 -0
  78. package/dist/cjs/components/formfield/formfield.js +50 -0
  79. package/dist/cjs/components/formfield/formfield.recipe.js +22 -0
  80. package/dist/cjs/components/formfield/index.js +7 -0
  81. package/dist/cjs/components/input/index.js +7 -0
  82. package/dist/cjs/components/input/input.js +47 -0
  83. package/dist/cjs/components/input/input.recipe.js +59 -0
  84. package/dist/cjs/components/kbd/index.js +7 -0
  85. package/dist/cjs/components/kbd/kbd.js +44 -0
  86. package/dist/cjs/components/kbd/kbd.recipe.js +23 -0
  87. package/dist/cjs/components/linechart/index.js +7 -0
  88. package/dist/cjs/components/linechart/linechart.js +57 -0
  89. package/dist/cjs/components/linechart/linechart.recipe.js +35 -0
  90. package/dist/cjs/components/menu/index.js +14 -0
  91. package/dist/cjs/components/menu/menu-keyboard.js +117 -0
  92. package/dist/cjs/components/menu/menu.js +144 -0
  93. package/dist/cjs/components/menu/menu.recipe.js +54 -0
  94. package/dist/cjs/components/multiselect/index.js +7 -0
  95. package/dist/cjs/components/multiselect/multiselect.js +161 -0
  96. package/dist/cjs/components/multiselect/multiselect.recipe.js +71 -0
  97. package/dist/cjs/components/popover/index.js +7 -0
  98. package/dist/cjs/components/popover/popover.js +106 -0
  99. package/dist/cjs/components/popover/popover.recipe.js +21 -0
  100. package/dist/cjs/components/progress/index.js +7 -0
  101. package/dist/cjs/components/progress/progress.js +26 -0
  102. package/dist/cjs/components/progress/progress.recipe.js +20 -0
  103. package/dist/cjs/components/radiogroup/index.js +8 -0
  104. package/dist/cjs/components/radiogroup/radiogroup.js +83 -0
  105. package/dist/cjs/components/radiogroup/radiogroup.recipe.js +40 -0
  106. package/dist/cjs/components/resizablepanel/index.js +12 -0
  107. package/dist/cjs/components/resizablepanel/resizablepanel.js +176 -0
  108. package/dist/cjs/components/resizablepanel/resizablepanel.recipe.js +69 -0
  109. package/dist/cjs/components/segmented/index.js +7 -0
  110. package/dist/cjs/components/segmented/segmented.js +95 -0
  111. package/dist/cjs/components/segmented/segmented.recipe.js +23 -0
  112. package/dist/cjs/components/select/index.js +7 -0
  113. package/dist/cjs/components/select/select.js +180 -0
  114. package/dist/cjs/components/select/select.recipe.js +79 -0
  115. package/dist/cjs/components/skeleton/index.js +7 -0
  116. package/dist/cjs/components/skeleton/skeleton.js +13 -0
  117. package/dist/cjs/components/skeleton/skeleton.recipe.js +17 -0
  118. package/dist/cjs/components/spinner/index.js +7 -0
  119. package/dist/cjs/components/spinner/spinner.js +15 -0
  120. package/dist/cjs/components/spinner/spinner.recipe.js +16 -0
  121. package/dist/cjs/components/stagebar/index.js +7 -0
  122. package/dist/cjs/components/stagebar/stagebar.js +26 -0
  123. package/dist/cjs/components/stagebar/stagebar.recipe.js +17 -0
  124. package/dist/cjs/components/statcard/index.js +7 -0
  125. package/dist/cjs/components/statcard/statcard.js +15 -0
  126. package/dist/cjs/components/statcard/statcard.recipe.js +37 -0
  127. package/dist/cjs/components/switch/index.js +7 -0
  128. package/dist/cjs/components/switch/switch.js +58 -0
  129. package/dist/cjs/components/switch/switch.recipe.js +29 -0
  130. package/dist/cjs/components/table/index.js +14 -0
  131. package/dist/cjs/components/table/table.js +66 -0
  132. package/dist/cjs/components/table/table.recipe.js +43 -0
  133. package/dist/cjs/components/tabletoolbar/index.js +20 -0
  134. package/dist/cjs/components/tabletoolbar/tabletoolbar.chrome.js +84 -0
  135. package/dist/cjs/components/tabletoolbar/tabletoolbar.js +237 -0
  136. package/dist/cjs/components/tabletoolbar/tabletoolbar.recipe.js +26 -0
  137. package/dist/cjs/components/tabs/index.js +7 -0
  138. package/dist/cjs/components/tabs/tabs.js +106 -0
  139. package/dist/cjs/components/tabs/tabs.recipe.js +36 -0
  140. package/dist/cjs/components/textarea/index.js +7 -0
  141. package/dist/cjs/components/textarea/textarea.js +56 -0
  142. package/dist/cjs/components/textarea/textarea.recipe.js +46 -0
  143. package/dist/cjs/components/toast/index.js +8 -0
  144. package/dist/cjs/components/toast/toast.js +95 -0
  145. package/dist/cjs/components/toast/toast.recipe.js +31 -0
  146. package/dist/cjs/components/tooltip/index.js +7 -0
  147. package/dist/cjs/components/tooltip/tooltip.js +95 -0
  148. package/dist/cjs/components/tooltip/tooltip.recipe.js +16 -0
  149. package/dist/cjs/components/wheelpicker/index.js +7 -0
  150. package/dist/cjs/components/wheelpicker/wheelpicker.js +134 -0
  151. package/dist/cjs/components/wheelpicker/wheelpicker.recipe.js +95 -0
  152. package/dist/cjs/core/cx.js +8 -0
  153. package/dist/cjs/core/define.js +6 -0
  154. package/dist/cjs/core/index.js +11 -0
  155. package/dist/cjs/core/primitive.js +49 -0
  156. package/dist/cjs/core/resolve.js +44 -0
  157. package/dist/cjs/core/types.js +2 -0
  158. package/dist/cjs/index.js +92 -0
  159. package/dist/cjs/package.json +3 -0
  160. package/dist/cjs/product/appshell/appshell.js +104 -0
  161. package/dist/cjs/product/appshell/appshell.recipe.js +30 -0
  162. package/dist/cjs/product/appshell/index.js +7 -0
  163. package/dist/cjs/product/authshell/authshell.js +21 -0
  164. package/dist/cjs/product/authshell/authshell.recipe.js +67 -0
  165. package/dist/cjs/product/authshell/index.js +7 -0
  166. package/dist/cjs/product/board/board.js +78 -0
  167. package/dist/cjs/product/board/board.recipe.js +31 -0
  168. package/dist/cjs/product/board/index.js +7 -0
  169. package/dist/cjs/product/command/command.js +238 -0
  170. package/dist/cjs/product/command/command.recipe.js +47 -0
  171. package/dist/cjs/product/command/index.js +8 -0
  172. package/dist/cjs/product/entitycard/entitycard.js +14 -0
  173. package/dist/cjs/product/entitycard/entitycard.recipe.js +32 -0
  174. package/dist/cjs/product/entitycard/index.js +7 -0
  175. package/dist/cjs/product/flowcanvas/flowcanvas.js +121 -0
  176. package/dist/cjs/product/flowcanvas/flowcanvas.recipe.js +28 -0
  177. package/dist/cjs/product/flowcanvas/index.js +7 -0
  178. package/dist/cjs/product/inbox/inbox.js +16 -0
  179. package/dist/cjs/product/inbox/inbox.recipe.js +46 -0
  180. package/dist/cjs/product/inbox/index.js +7 -0
  181. package/dist/cjs/product/listpage/index.js +8 -0
  182. package/dist/cjs/product/listpage/listpage.js +25 -0
  183. package/dist/cjs/product/listpage/listpage.recipe.js +46 -0
  184. package/dist/cjs/product/navitem/index.js +5 -0
  185. package/dist/cjs/product/navitem/navitem.js +44 -0
  186. package/dist/cjs/product/navitem/navitem.recipe.js +36 -0
  187. package/dist/cjs/product/onboarding/index.js +7 -0
  188. package/dist/cjs/product/onboarding/onboarding.js +17 -0
  189. package/dist/cjs/product/onboarding/onboarding.recipe.js +44 -0
  190. package/dist/cjs/product/recordpage/index.js +7 -0
  191. package/dist/cjs/product/recordpage/recordpage.js +409 -0
  192. package/dist/cjs/product/recordpage/recordpage.recipe.js +162 -0
  193. package/dist/cjs/product/recordpanel/index.js +7 -0
  194. package/dist/cjs/product/recordpanel/recordpanel.js +83 -0
  195. package/dist/cjs/product/recordpanel/recordpanel.recipe.js +51 -0
  196. package/dist/cjs/product/recordstable/company-logos.js +29 -0
  197. package/dist/cjs/product/recordstable/index.js +10 -0
  198. package/dist/cjs/product/recordstable/recordstable.js +1092 -0
  199. package/dist/cjs/product/recordstable/recordstable.recipe.js +152 -0
  200. package/dist/cjs/product/recordstable/recordstable.tag.js +21 -0
  201. package/dist/cjs/product/richtext/index.js +7 -0
  202. package/dist/cjs/product/richtext/richtext.js +147 -0
  203. package/dist/cjs/product/richtext/richtext.recipe.js +43 -0
  204. package/dist/cjs/product/settingssection/index.js +7 -0
  205. package/dist/cjs/product/settingssection/settingssection.js +14 -0
  206. package/dist/cjs/product/settingssection/settingssection.recipe.js +23 -0
  207. package/dist/cjs/product/settingsshell/index.js +7 -0
  208. package/dist/cjs/product/settingsshell/settingsshell.js +59 -0
  209. package/dist/cjs/product/settingsshell/settingsshell.recipe.js +46 -0
  210. package/dist/cjs/product/sidebar/index.js +13 -0
  211. package/dist/cjs/product/sidebar/sidebar.js +148 -0
  212. package/dist/cjs/product/sidebar/sidebar.recipe.js +46 -0
  213. package/dist/cjs/product/tasklist/index.js +7 -0
  214. package/dist/cjs/product/tasklist/tasklist.js +19 -0
  215. package/dist/cjs/product/tasklist/tasklist.recipe.js +38 -0
  216. package/dist/cjs/product/trialbanner/index.js +7 -0
  217. package/dist/cjs/product/trialbanner/trialbanner.js +13 -0
  218. package/dist/cjs/product/trialbanner/trialbanner.recipe.js +32 -0
  219. package/dist/cjs/product/viewtoggle/index.js +7 -0
  220. package/dist/cjs/product/viewtoggle/viewtoggle.js +55 -0
  221. package/dist/cjs/product/viewtoggle/viewtoggle.recipe.js +27 -0
  222. package/dist/cjs/tokens/contract.js +21 -0
  223. package/dist/cjs/tokens/elevation.js +47 -0
  224. package/dist/cjs/tokens/index.js +18 -0
  225. package/dist/cjs/tokens/palette.js +63 -0
  226. package/dist/cjs/tokens/scale.js +70 -0
  227. package/dist/cjs/tokens/semantic.js +57 -0
  228. package/dist/columncalc.css +26 -0
  229. package/dist/columncalc.d.ts +7 -0
  230. package/dist/command.css +34 -1
  231. package/dist/contextmenu.css +10 -2
  232. package/dist/datatable.css +60 -14
  233. package/dist/datepicker.css +175 -19
  234. package/dist/datepicker.d.ts +2 -2
  235. package/dist/datepickerfield.css +27 -0
  236. package/dist/datepickerfield.d.ts +7 -0
  237. package/dist/dialog.css +10 -2
  238. package/dist/donutchart.css +8 -0
  239. package/dist/drawer.css +13 -5
  240. package/dist/entitycard.css +10 -2
  241. package/dist/esm/components/accordion/accordion.js +17 -16
  242. package/dist/esm/components/accordion/accordion.recipe.js +1 -1
  243. package/dist/esm/components/avatargroup/avatargroup.js +2 -2
  244. package/dist/esm/components/avatargroup/avatargroup.recipe.js +2 -2
  245. package/dist/esm/components/badge/badge.recipe.js +4 -4
  246. package/dist/esm/components/barchart/barchart.js +15 -2
  247. package/dist/esm/components/barchart/barchart.recipe.js +10 -5
  248. package/dist/esm/components/breadcrumb/breadcrumb.js +2 -2
  249. package/dist/esm/components/bulkbar/bulkbar.js +2 -2
  250. package/dist/esm/components/bulkbar/bulkbar.recipe.js +4 -4
  251. package/dist/esm/components/button/button.js +13 -2
  252. package/dist/esm/components/button/button.recipe.js +20 -4
  253. package/dist/esm/components/callout/callout.js +2 -2
  254. package/dist/esm/components/checkbox/checkbox.recipe.js +9 -1
  255. package/dist/esm/components/columncalc/columncalc.js +100 -0
  256. package/dist/esm/components/columncalc/columncalc.recipe.js +30 -0
  257. package/dist/esm/components/columncalc/index.js +1 -0
  258. package/dist/esm/components/contextmenu/contextmenu.recipe.js +4 -4
  259. package/dist/esm/components/datatable/datatable.js +276 -49
  260. package/dist/esm/components/datatable/datatable.recipe.js +54 -16
  261. package/dist/esm/components/datepicker/datepicker.js +104 -53
  262. package/dist/esm/components/datepicker/datepicker.recipe.js +180 -25
  263. package/dist/esm/components/datepicker/index.js +1 -1
  264. package/dist/esm/components/datepickerfield/datepickerfield.js +49 -0
  265. package/dist/esm/components/datepickerfield/datepickerfield.recipe.js +41 -0
  266. package/dist/esm/components/datepickerfield/index.js +2 -0
  267. package/dist/esm/components/dialog/dialog.js +2 -2
  268. package/dist/esm/components/dialog/dialog.recipe.js +4 -4
  269. package/dist/esm/components/divider/divider.js +2 -2
  270. package/dist/esm/components/donutchart/donutchart.js +2 -2
  271. package/dist/esm/components/drawer/drawer.js +2 -2
  272. package/dist/esm/components/drawer/drawer.recipe.js +6 -6
  273. package/dist/esm/components/emptystate/emptystate.js +2 -2
  274. package/dist/esm/components/formfield/formfield.js +2 -2
  275. package/dist/esm/components/input/input.js +6 -3
  276. package/dist/esm/components/input/input.recipe.js +38 -5
  277. package/dist/esm/components/linechart/linechart.js +15 -2
  278. package/dist/esm/components/linechart/linechart.recipe.js +10 -5
  279. package/dist/esm/components/menu/index.js +1 -0
  280. package/dist/esm/components/menu/menu-keyboard.js +80 -0
  281. package/dist/esm/components/menu/menu.js +27 -60
  282. package/dist/esm/components/menu/menu.recipe.js +5 -5
  283. package/dist/esm/components/multiselect/multiselect.js +10 -3
  284. package/dist/esm/components/multiselect/multiselect.recipe.js +8 -8
  285. package/dist/esm/components/popover/popover.js +2 -2
  286. package/dist/esm/components/popover/popover.recipe.js +2 -2
  287. package/dist/esm/components/progress/progress.js +3 -3
  288. package/dist/esm/components/radiogroup/radiogroup.js +2 -2
  289. package/dist/esm/components/radiogroup/radiogroup.recipe.js +1 -1
  290. package/dist/esm/components/resizablepanel/index.js +2 -0
  291. package/dist/esm/components/resizablepanel/resizablepanel.js +135 -0
  292. package/dist/esm/components/resizablepanel/resizablepanel.recipe.js +66 -0
  293. package/dist/esm/components/segmented/segmented.js +2 -2
  294. package/dist/esm/components/select/select.js +2 -2
  295. package/dist/esm/components/select/select.recipe.js +6 -6
  296. package/dist/esm/components/skeleton/skeleton.js +5 -2
  297. package/dist/esm/components/spinner/spinner.js +3 -2
  298. package/dist/esm/components/stagebar/stagebar.js +2 -2
  299. package/dist/esm/components/statcard/statcard.js +2 -2
  300. package/dist/esm/components/table/table.recipe.js +6 -6
  301. package/dist/esm/components/tabletoolbar/tabletoolbar.chrome.js +3 -3
  302. package/dist/esm/components/tabletoolbar/tabletoolbar.js +21 -13
  303. package/dist/esm/components/tabs/tabs.js +2 -2
  304. package/dist/esm/components/tabs/tabs.recipe.js +4 -4
  305. package/dist/esm/components/textarea/textarea.js +5 -5
  306. package/dist/esm/components/textarea/textarea.recipe.js +16 -4
  307. package/dist/esm/components/toast/toast.js +4 -4
  308. package/dist/esm/components/toast/toast.recipe.js +2 -2
  309. package/dist/esm/components/tooltip/tooltip.js +21 -7
  310. package/dist/esm/components/wheelpicker/index.js +2 -0
  311. package/dist/esm/components/wheelpicker/wheelpicker.js +98 -0
  312. package/dist/esm/components/wheelpicker/wheelpicker.recipe.js +92 -0
  313. package/dist/esm/index.js +5 -0
  314. package/dist/esm/product/appshell/appshell.js +33 -9
  315. package/dist/esm/product/appshell/appshell.recipe.js +4 -4
  316. package/dist/esm/product/authshell/authshell.js +18 -0
  317. package/dist/esm/product/authshell/authshell.recipe.js +64 -0
  318. package/dist/esm/product/authshell/index.js +2 -0
  319. package/dist/esm/product/board/board.js +2 -2
  320. package/dist/esm/product/board/board.recipe.js +1 -1
  321. package/dist/esm/product/command/command.js +19 -6
  322. package/dist/esm/product/command/command.recipe.js +27 -2
  323. package/dist/esm/product/entitycard/entitycard.js +2 -2
  324. package/dist/esm/product/entitycard/entitycard.recipe.js +2 -2
  325. package/dist/esm/product/flowcanvas/flowcanvas.js +4 -4
  326. package/dist/esm/product/inbox/inbox.recipe.js +1 -1
  327. package/dist/esm/product/listpage/listpage.recipe.js +3 -3
  328. package/dist/esm/product/navitem/navitem.recipe.js +13 -1
  329. package/dist/esm/product/onboarding/onboarding.js +2 -2
  330. package/dist/esm/product/onboarding/onboarding.recipe.js +2 -2
  331. package/dist/esm/product/recordpage/recordpage.js +133 -47
  332. package/dist/esm/product/recordpage/recordpage.recipe.js +19 -17
  333. package/dist/esm/product/recordpanel/recordpanel.js +4 -4
  334. package/dist/esm/product/recordpanel/recordpanel.recipe.js +12 -16
  335. package/dist/esm/product/recordstable/recordstable.js +157 -66
  336. package/dist/esm/product/recordstable/recordstable.recipe.js +23 -23
  337. package/dist/esm/product/recordstable/recordstable.tag.js +18 -0
  338. package/dist/esm/product/richtext/richtext.js +3 -3
  339. package/dist/esm/product/richtext/richtext.recipe.js +3 -3
  340. package/dist/esm/product/settingssection/settingssection.js +2 -2
  341. package/dist/esm/product/settingsshell/settingsshell.js +2 -2
  342. package/dist/esm/product/settingsshell/settingsshell.recipe.js +7 -2
  343. package/dist/esm/product/sidebar/sidebar.js +2 -2
  344. package/dist/esm/product/sidebar/sidebar.recipe.js +27 -2
  345. package/dist/esm/product/tasklist/tasklist.js +4 -4
  346. package/dist/esm/product/trialbanner/trialbanner.js +2 -2
  347. package/dist/esm/product/trialbanner/trialbanner.recipe.js +2 -2
  348. package/dist/esm/product/viewtoggle/viewtoggle.js +10 -2
  349. package/dist/esm/product/viewtoggle/viewtoggle.recipe.js +1 -1
  350. package/dist/esm/tokens/elevation.js +44 -0
  351. package/dist/esm/tokens/index.js +1 -0
  352. package/dist/esm/tokens/palette.js +30 -0
  353. package/dist/esm/tokens/scale.js +7 -0
  354. package/dist/esm/tokens/semantic.js +24 -0
  355. package/dist/inbox.css +9 -1
  356. package/dist/input.css +32 -4
  357. package/dist/linechart.css +5 -5
  358. package/dist/listpage.css +11 -3
  359. package/dist/menu.css +11 -3
  360. package/dist/multiselect.css +14 -6
  361. package/dist/native/tokens.d.ts +16 -0
  362. package/dist/native/tokens.js +221 -0
  363. package/dist/navitem.css +21 -1
  364. package/dist/onboarding.css +10 -2
  365. package/dist/popover.css +8 -0
  366. package/dist/progress.css +8 -0
  367. package/dist/radiogroup.css +9 -1
  368. package/dist/recordpage.css +22 -12
  369. package/dist/recordpanel.css +25 -15
  370. package/dist/recordstable.css +37 -23
  371. package/dist/resizablepanel.css +53 -0
  372. package/dist/resizablepanel.d.ts +7 -0
  373. package/dist/richtext.css +11 -3
  374. package/dist/segmented.css +8 -0
  375. package/dist/select.css +12 -4
  376. package/dist/settingsshell.css +15 -2
  377. package/dist/sidebar.css +34 -1
  378. package/dist/skeleton.css +8 -0
  379. package/dist/spinner.css +8 -0
  380. package/dist/stagebar.css +8 -0
  381. package/dist/styles.css +2061 -1111
  382. package/dist/switch.css +8 -0
  383. package/dist/table.css +14 -6
  384. package/dist/tabletoolbar.css +8 -0
  385. package/dist/tabs.css +12 -4
  386. package/dist/tasklist.css +8 -0
  387. package/dist/textarea.css +23 -3
  388. package/dist/toast.css +10 -2
  389. package/dist/tokens.css +29 -0
  390. package/dist/tokens.json +217 -0
  391. package/dist/tooltip.css +8 -0
  392. package/dist/trialbanner.css +8 -0
  393. package/dist/types/components/accordion/accordion.d.ts +10 -8
  394. package/dist/types/components/avatargroup/avatargroup.d.ts +2 -2
  395. package/dist/types/components/barchart/barchart.d.ts +4 -2
  396. package/dist/types/components/breadcrumb/breadcrumb.d.ts +2 -2
  397. package/dist/types/components/bulkbar/bulkbar.d.ts +4 -2
  398. package/dist/types/components/button/button.d.ts +8 -0
  399. package/dist/types/components/button/button.recipe.d.ts +12 -0
  400. package/dist/types/components/callout/callout.d.ts +2 -2
  401. package/dist/types/components/columncalc/columncalc.d.ts +49 -0
  402. package/dist/types/components/columncalc/columncalc.recipe.d.ts +3 -0
  403. package/dist/types/components/columncalc/index.d.ts +1 -0
  404. package/dist/types/components/datatable/datatable.d.ts +58 -3
  405. package/dist/types/components/datepicker/datepicker.d.ts +41 -18
  406. package/dist/types/components/datepicker/datepicker.recipe.d.ts +25 -2
  407. package/dist/types/components/datepicker/index.d.ts +1 -1
  408. package/dist/types/components/datepickerfield/datepickerfield.d.ts +35 -0
  409. package/dist/types/components/datepickerfield/datepickerfield.recipe.d.ts +5 -0
  410. package/dist/types/components/datepickerfield/index.d.ts +2 -0
  411. package/dist/types/components/dialog/dialog.d.ts +3 -1
  412. package/dist/types/components/divider/divider.d.ts +2 -2
  413. package/dist/types/components/donutchart/donutchart.d.ts +2 -2
  414. package/dist/types/components/drawer/drawer.d.ts +3 -1
  415. package/dist/types/components/emptystate/emptystate.d.ts +2 -2
  416. package/dist/types/components/formfield/formfield.d.ts +7 -4
  417. package/dist/types/components/input/input.d.ts +8 -2
  418. package/dist/types/components/linechart/linechart.d.ts +4 -2
  419. package/dist/types/components/menu/index.d.ts +1 -0
  420. package/dist/types/components/menu/menu-keyboard.d.ts +33 -0
  421. package/dist/types/components/menu/menu.d.ts +7 -1
  422. package/dist/types/components/multiselect/multiselect.d.ts +8 -4
  423. package/dist/types/components/popover/popover.d.ts +4 -1
  424. package/dist/types/components/progress/progress.d.ts +2 -2
  425. package/dist/types/components/radiogroup/radiogroup.d.ts +2 -2
  426. package/dist/types/components/resizablepanel/index.d.ts +2 -0
  427. package/dist/types/components/resizablepanel/resizablepanel.d.ts +75 -0
  428. package/dist/types/components/resizablepanel/resizablepanel.recipe.d.ts +3 -0
  429. package/dist/types/components/segmented/segmented.d.ts +2 -2
  430. package/dist/types/components/select/select.d.ts +2 -2
  431. package/dist/types/components/skeleton/skeleton.d.ts +2 -2
  432. package/dist/types/components/spinner/spinner.d.ts +2 -2
  433. package/dist/types/components/stagebar/stagebar.d.ts +2 -2
  434. package/dist/types/components/statcard/statcard.d.ts +2 -2
  435. package/dist/types/components/tabletoolbar/tabletoolbar.d.ts +28 -2
  436. package/dist/types/components/tabs/tabs.d.ts +2 -2
  437. package/dist/types/components/toast/toast.d.ts +7 -1
  438. package/dist/types/components/tooltip/tooltip.d.ts +8 -1
  439. package/dist/types/components/wheelpicker/index.d.ts +2 -0
  440. package/dist/types/components/wheelpicker/wheelpicker.d.ts +37 -0
  441. package/dist/types/components/wheelpicker/wheelpicker.recipe.d.ts +26 -0
  442. package/dist/types/index.d.ts +5 -0
  443. package/dist/types/product/appshell/appshell.d.ts +16 -2
  444. package/dist/types/product/authshell/authshell.d.ts +28 -0
  445. package/dist/types/product/authshell/authshell.recipe.d.ts +3 -0
  446. package/dist/types/product/authshell/index.d.ts +2 -0
  447. package/dist/types/product/board/board.d.ts +2 -2
  448. package/dist/types/product/command/command.d.ts +25 -1
  449. package/dist/types/product/entitycard/entitycard.d.ts +2 -2
  450. package/dist/types/product/flowcanvas/flowcanvas.d.ts +9 -1
  451. package/dist/types/product/onboarding/onboarding.d.ts +7 -1
  452. package/dist/types/product/recordpage/recordpage.d.ts +65 -1
  453. package/dist/types/product/recordpanel/recordpanel.d.ts +8 -2
  454. package/dist/types/product/recordstable/recordstable.d.ts +90 -1
  455. package/dist/types/product/recordstable/recordstable.tag.d.ts +10 -0
  456. package/dist/types/product/richtext/richtext.d.ts +5 -1
  457. package/dist/types/product/settingssection/settingssection.d.ts +2 -2
  458. package/dist/types/product/settingsshell/settingsshell.d.ts +6 -2
  459. package/dist/types/product/sidebar/sidebar.d.ts +8 -0
  460. package/dist/types/product/tasklist/tasklist.d.ts +5 -1
  461. package/dist/types/product/trialbanner/trialbanner.d.ts +2 -2
  462. package/dist/types/product/viewtoggle/viewtoggle.d.ts +11 -5
  463. package/dist/types/tokens/contract.d.ts +14 -3
  464. package/dist/types/tokens/elevation.d.ts +44 -0
  465. package/dist/types/tokens/index.d.ts +1 -0
  466. package/dist/types/tokens/palette.d.ts +40 -0
  467. package/dist/types/tokens/scale.d.ts +7 -0
  468. package/dist/types/tokens/semantic.d.ts +19 -0
  469. package/dist/viewtoggle.css +9 -1
  470. package/dist/wheelpicker.css +68 -0
  471. package/dist/wheelpicker.d.ts +7 -0
  472. package/package.json +160 -68
@@ -6,11 +6,11 @@ import { Avatar } from "../../components/avatar/index.js";
6
6
  import { BulkBar, BulkBarButton } from "../../components/bulkbar/index.js";
7
7
  import { Button } from "../../components/button/index.js";
8
8
  import { Checkbox } from "../../components/checkbox/index.js";
9
- import { DatePicker } from "../../components/datepicker/index.js";
9
+ import { DatePickerField } from "../../components/datepickerfield/index.js";
10
10
  import { Dialog, DialogBody, DialogFooter } from "../../components/dialog/index.js";
11
11
  import { EmptyState } from "../../components/emptystate/index.js";
12
12
  import { Input } from "../../components/input/index.js";
13
- import { Menu, MenuItem } from "../../components/menu/index.js";
13
+ import { Menu, MenuItem, useMenuKeyboard } from "../../components/menu/index.js";
14
14
  import { MultiSelect } from "../../components/multiselect/index.js";
15
15
  import { Select } from "../../components/select/index.js";
16
16
  import { Switch } from "../../components/switch/index.js";
@@ -48,14 +48,76 @@ function avatarToneFor(name) {
48
48
  h = (h * 31 + name.charCodeAt(i)) >>> 0;
49
49
  return AVATAR_TONES[h % AVATAR_TONES.length];
50
50
  }
51
- const AGG_LABELS = {
52
- none: "None",
53
- count: "Count all",
54
- filled: "Not empty",
55
- sum: "Sum",
56
- avg: "Average",
57
- min: "Min",
58
- max: "Max",
51
+ export const recordsTableLabels = {
52
+ selectAll: "Select all",
53
+ sort: "Sort",
54
+ sortField: "Sort field",
55
+ sortDirection: "Sort direction",
56
+ removeSort: "Remove sort",
57
+ reorderSort: "Reorder sort",
58
+ addSort: "Add sort",
59
+ ascending: "Ascending",
60
+ descending: "Descending",
61
+ noSorts: "No sorts applied to this view.",
62
+ learnSorting: "Learn about sorting",
63
+ filter: "Filter",
64
+ addFilter: "Add filter",
65
+ filterByField: "Filter by field",
66
+ filterByPlaceholder: "Filter by…",
67
+ filterValuePlaceholder: "Value…",
68
+ selectCondition: "Select condition",
69
+ clearFilter: "Clear filter",
70
+ addColumn: "Add column",
71
+ columnLabel: "Column label",
72
+ editColumnLabel: "Edit column label",
73
+ hideFromView: "Hide from view",
74
+ moveLeft: "Move left",
75
+ moveRight: "Move right",
76
+ sortAscending: "Sort ascending",
77
+ sortDescending: "Sort descending",
78
+ newRecord: "New record",
79
+ createRecord: "Create record",
80
+ createMore: "Create more",
81
+ newButton: "New",
82
+ more: "More",
83
+ moreActions: "More actions",
84
+ recordActions: "Record actions",
85
+ copy: "Copy",
86
+ duplicate: "Duplicate",
87
+ addToList: "Add to list",
88
+ inAList: "In a list",
89
+ delete: "Delete",
90
+ sendEmail: "Send email",
91
+ exportCsv: "Export CSV",
92
+ runWorkflow: "Run workflow",
93
+ calculate: "Calculate",
94
+ emptyPlaceholder: "Empty",
95
+ setRecordPlaceholder: "Set a record…",
96
+ noRecords: "No records yet",
97
+ untitled: "Untitled",
98
+ agg: {
99
+ none: "None",
100
+ count: "Count all",
101
+ filled: "Not empty",
102
+ sum: "Sum",
103
+ avg: "Average",
104
+ min: "Min",
105
+ max: "Max",
106
+ },
107
+ op: {
108
+ contains: "contains",
109
+ is: "is",
110
+ isnot: "is not",
111
+ empty: "is empty",
112
+ notempty: "is not empty",
113
+ },
114
+ newRecordTitle: (entity) => `New ${entity}`,
115
+ setFieldPlaceholder: (field) => `Set ${field}…`,
116
+ newColumnLabel: (index) => `Field ${index}`,
117
+ sortedBy: (first, extra) => `Sorted by ${first}${extra ? ` +${extra}` : ""}`,
118
+ filterCount: (active) => (active ? `Filter · ${active}` : "Filter"),
119
+ recordCount: (n) => `${n} record${n === 1 ? "" : "s"}`,
120
+ conditionTitle: (field) => `${field} condition`,
59
121
  };
60
122
  // currency is numeric data too — it gets the same numeric aggregations as
61
123
  // `number` (only `number` auto-defaults to Sum on mount; currency starts at
@@ -70,13 +132,6 @@ const compact = new Intl.NumberFormat("en", {
70
132
  const CHECK_W = 40;
71
133
  const ADDCOL_W = 44;
72
134
  const colWidth = (col) => col.width ?? (col.type === "entity" ? 240 : 180);
73
- const OP_LABELS = {
74
- contains: "contains",
75
- is: "is",
76
- isnot: "is not",
77
- empty: "is empty",
78
- notempty: "is not empty",
79
- };
80
135
  const OP_ORDER = ["contains", "is", "isnot", "empty", "notempty"];
81
136
  const opNeedsValue = (op) => op === "contains" || op === "is" || op === "isnot";
82
137
  function matchesFilter(col, row, cond) {
@@ -152,7 +207,7 @@ function summarize(col, agg, rows) {
152
207
  n = Math.max(...nums);
153
208
  return { label: agg, value: compact.format(n) };
154
209
  }
155
- function EntityCell({ value, pinned, onOpenPage, avatarTone = "brand", }) {
210
+ function EntityCell({ value, pinned, onOpenPage, avatarTone = "brand", labels, }) {
156
211
  // a real favicon/logo when we have one; fall back to the letter tile if it
157
212
  // 404s or the row has no avatar
158
213
  const [imgFailed, setImgFailed] = React.useState(false);
@@ -160,9 +215,9 @@ function EntityCell({ value, pinned, onOpenPage, avatarTone = "brand", }) {
160
215
  return (_jsxs("span", { className: "recordstable-entity", children: [showImg ? (_jsx("img", { className: "recordstable-fav", src: value.avatar, alt: "", onError: () => setImgFailed(true) })) : (_jsx(Avatar, { size: "sm", shape: "rounded", tone: avatarTone === "hashed" ? avatarToneFor(value.name) : "brand", children: value.name.charAt(0) })), onOpenPage ? (_jsx("button", { type: "button", className: "recordstable-entity-name recordstable-entity-name--link", onClick: (e) => {
161
216
  e.stopPropagation();
162
217
  onOpenPage();
163
- }, children: value.name })) : (_jsx("span", { className: "recordstable-entity-name", children: value.name })), pinned ? (_jsx(Star, { className: "recordstable-pin", size: 13, strokeWidth: 2, "aria-label": "In a list" })) : null] }));
218
+ }, children: value.name })) : (_jsx("span", { className: "recordstable-entity-name", children: value.name })), pinned ? (_jsx(Star, { className: "recordstable-pin", size: 13, strokeWidth: 2, "aria-label": labels.inAList })) : null] }));
164
219
  }
165
- function CellView({ column, row, pinned, onOpenPage, avatarTone = "brand", }) {
220
+ function CellView({ column, row, pinned, onOpenPage, avatarTone = "brand", labels, }) {
166
221
  const value = row[column.key];
167
222
  // `render` fully owns the cell's content — no empty-dash fallback, no
168
223
  // type renderer; it's the only thing drawn here (see the doc comment on
@@ -179,7 +234,7 @@ function CellView({ column, row, pinned, onOpenPage, avatarTone = "brand", }) {
179
234
  const display = column.format?.(value, row);
180
235
  switch (column.type) {
181
236
  case "entity":
182
- return (_jsx(EntityCell, { value: value, pinned: pinned, onOpenPage: onOpenPage, avatarTone: avatarTone }));
237
+ return (_jsx(EntityCell, { labels: labels, value: value, pinned: pinned, onOpenPage: onOpenPage, avatarTone: avatarTone }));
183
238
  case "tags":
184
239
  return (_jsx("span", { className: "recordstable-cell recordstable-cell--tags", children: value.map((t) => (_jsx("span", { className: "recordstable-tag", "data-tone": tagTone(t), children: t }, t))) }));
185
240
  case "number":
@@ -215,20 +270,20 @@ function CellView({ column, row, pinned, onOpenPage, avatarTone = "brand", }) {
215
270
  function StatusCell({ column, row, onChange, }) {
216
271
  return (_jsx(Select, { variant: "ghost", "aria-label": column.label, placeholder: "\u2014", value: row[column.key] ?? undefined, onValueChange: onChange, options: column.options ?? [] }));
217
272
  }
218
- function ReferenceCell({ column, row, onChange, }) {
219
- return (_jsx(Select, { variant: "ghost", searchable: true, searchPlaceholder: "Search records\u2026", "aria-label": column.label, placeholder: "Set a record\u2026", value: row[column.key] ?? undefined, onValueChange: onChange, options: (column.refOptions ?? []).map((o) => ({
273
+ function ReferenceCell({ column, row, onChange, labels, }) {
274
+ return (_jsx(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) => ({
220
275
  value: o.value,
221
276
  label: o.label,
222
277
  })) }));
223
278
  }
224
- function TagsCell({ column, row, options, onChange, }) {
279
+ function TagsCell({ column, row, options, onChange, labels, }) {
225
280
  const value = row[column.key] ?? [];
226
- return (_jsx(MultiSelect, { options: options, value: value, onValueChange: onChange, placeholder: "Empty", searchPlaceholder: "Search options\u2026", "aria-label": column.label }));
281
+ return (_jsx(MultiSelect, { options: options, value: value, onValueChange: onChange, placeholder: labels.emptyPlaceholder, searchPlaceholder: "Search options\u2026", "aria-label": column.label }));
227
282
  }
228
283
  function DateCell({ column, row, onChange, }) {
229
284
  const raw = row[column.key];
230
285
  const value = raw ? new Date(raw) : null;
231
- return (_jsx(DatePicker, { variant: "ghost", "aria-label": column.label, value: value, onChange: (d) => onChange(d.toISOString().slice(0, 10)) }));
286
+ return (_jsx(DatePickerField, { variant: "ghost", "aria-label": column.label, value: value, onChange: (d) => onChange(d.toISOString().slice(0, 10)) }));
232
287
  }
233
288
  /**
234
289
  * The "New <object>" create modal — measured from Attio's create-record dialog:
@@ -236,7 +291,7 @@ function DateCell({ column, row, onChange, }) {
236
291
  * primary "Create record" action (⌘↵). Building the row mirrors the inline
237
292
  * editor's per-type coercion so a created record reads back like an edited one.
238
293
  */
239
- function CreateRecordDialog({ open, onOpenChange, columns, entityLabel, onCreate, }) {
294
+ function CreateRecordDialog({ open, onOpenChange, columns, entityLabel, onCreate, labels, }) {
240
295
  const [values, setValues] = React.useState({});
241
296
  const [createMore, setCreateMore] = React.useState(false);
242
297
  const firstRef = React.useRef(null);
@@ -253,7 +308,7 @@ function CreateRecordDialog({ open, onOpenChange, columns, entityLabel, onCreate
253
308
  for (const col of columns) {
254
309
  const raw = (values[col.key] ?? "").trim();
255
310
  if (col.type === "entity") {
256
- row[col.key] = { name: raw || "Untitled" };
311
+ row[col.key] = { name: raw || labels.untitled };
257
312
  }
258
313
  else if (!raw) {
259
314
  // leave empty fields unset so the cell renders its "—" placeholder
@@ -285,10 +340,10 @@ function CreateRecordDialog({ open, onOpenChange, columns, entityLabel, onCreate
285
340
  };
286
341
  const entityCol = columns.find((c) => c.type === "entity");
287
342
  const EntityIcon = entityCol?.icon;
288
- return (_jsxs(Dialog, { open: open, onOpenChange: onOpenChange, title: `New ${entityLabel}`, icon: EntityIcon ? _jsx(EntityIcon, { size: 16, strokeWidth: 1.75 }) : undefined, width: 720, children: [_jsx(DialogBody, { children: columns.map((col, i) => (_jsxs("div", { className: "dialog-field", children: [_jsx("label", { className: "dialog-field-label", htmlFor: `create-${col.key}`, children: col.label }), _jsx(Input, { id: `create-${col.key}`, ref: i === 0 ? firstRef : undefined, value: values[col.key] ?? "", onChange: (e) => set(col.key, e.currentTarget.value), placeholder: `Set ${col.label}…`, onKeyDown: (e) => {
343
+ return (_jsxs(Dialog, { open: open, onOpenChange: onOpenChange, title: labels.newRecordTitle(entityLabel), icon: EntityIcon ? _jsx(EntityIcon, { size: 16, strokeWidth: 1.75 }) : undefined, width: 720, children: [_jsx(DialogBody, { children: columns.map((col, i) => (_jsxs("div", { className: "dialog-field", children: [_jsx("label", { className: "dialog-field-label", htmlFor: `create-${col.key}`, children: col.label }), _jsx(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) => {
289
344
  if (e.key === "Enter" && (e.metaKey || e.ctrlKey))
290
345
  submit();
291
- } })] }, col.key))) }), _jsxs(DialogFooter, { children: [_jsxs("span", { className: "dialog-more", children: ["Create more", _jsx(Switch, { checked: createMore, onCheckedChange: setCreateMore, "aria-label": "Create more" })] }), _jsxs("button", { type: "button", className: "dialog-submit", onClick: submit, children: ["Create record", _jsx("span", { className: "dialog-submit-key", children: "\u2318\u21B5" })] })] })] }));
346
+ } })] }, col.key))) }), _jsxs(DialogFooter, { children: [_jsxs("span", { className: "dialog-more", children: [labels.createMore, _jsx(Switch, { checked: createMore, onCheckedChange: setCreateMore, "aria-label": labels.createMore })] }), _jsxs("button", { type: "button", className: "dialog-submit", onClick: submit, children: [labels.createRecord, _jsx("span", { className: "dialog-submit-key", children: "\u2318\u21B5" })] })] })] }));
292
347
  }
293
348
  /**
294
349
  * Attio-style record grid — fully featured. Toolbar (search / sort status /
@@ -298,7 +353,7 @@ function CreateRecordDialog({ open, onOpenChange, columns, entityLabel, onCreate
298
353
  * and a floating bulk-action bar (delete / deselect) when rows are selected.
299
354
  * Selection can be controlled via `selectedIds`/`onSelectionChange`.
300
355
  */
301
- export function RecordsTable({ columns: columnsProp = defaultColumns, rows: rowsProp = defaultRows, selectedIds, onSelectionChange, showNew = true, allowAddColumn = true, onOpenRecord, onOpenRecordPage, avatarTone = "brand", className, }) {
356
+ export function RecordsTable({ labels, columns: columnsProp = defaultColumns, rows: rowsProp = defaultRows, selectedIds, onSelectionChange, showNew = true, allowAddColumn = true, onOpenRecord, onOpenRecordPage, avatarTone = "brand", className, }) {
302
357
  const [cols, setCols] = React.useState(columnsProp);
303
358
  const [data, setData] = React.useState(rowsProp);
304
359
  // Attio's multi-criteria sort: an ordered list of {field, direction} rows.
@@ -312,13 +367,23 @@ export function RecordsTable({ columns: columnsProp = defaultColumns, rows: rows
312
367
  const [filterConds, setFilterConds] = React.useState([]);
313
368
  const [filterQuery, setFilterQuery] = React.useState("");
314
369
  // one popover open at a time: "sort" | "filter" | "more" | `sum:<key>`
370
+ const t = React.useMemo(() => ({
371
+ ...recordsTableLabels,
372
+ ...labels,
373
+ agg: { ...recordsTableLabels.agg, ...labels?.agg },
374
+ op: { ...recordsTableLabels.op, ...labels?.op },
375
+ }), [labels]);
315
376
  const [openMenu, setOpenMenu] = React.useState(null);
377
+ // the element that opened the current menu, so Esc/select can hand focus back
378
+ const menuReturn = React.useRef(null);
379
+ const menuRef = React.useRef(null);
380
+ const ctxRef = React.useRef(null);
381
+ const ctxReturn = React.useRef(null);
316
382
  // viewport-anchored position for menus that live inside the scroll container
317
383
  // (summary footer, column headers) — rendered fixed to escape the overflow clip
318
384
  const [menuPos, setMenuPos] = React.useState(null);
319
385
  const [pinned, setPinned] = React.useState(() => new Set());
320
386
  const [scrolledX, setScrolledX] = React.useState(false);
321
- const [scrolledY, setScrolledY] = React.useState(false);
322
387
  const idCounter = React.useRef(0);
323
388
  // fixed menus (summary / header) portal here — outside the sticky cells'
324
389
  // stacking contexts, but still inside .recordstable so the styles apply
@@ -586,7 +651,7 @@ export function RecordsTable({ columns: columnsProp = defaultColumns, rows: rows
586
651
  const entityKey = cols.find((c) => c.type === "entity")?.key;
587
652
  const row = { id };
588
653
  if (entityKey)
589
- row[entityKey] = { name: "Untitled" };
654
+ row[entityKey] = { name: t.untitled };
590
655
  setData((prev) => [...prev, row]);
591
656
  if (entityKey)
592
657
  setEditing(null);
@@ -613,20 +678,24 @@ export function RecordsTable({ columns: columnsProp = defaultColumns, rows: rows
613
678
  });
614
679
  setOpenMenu(null);
615
680
  };
616
- // drop the dragged column immediately before `toKey` (drag-to-reorder). The
617
- // sticky entity column (index 0) stays pinned — never reorder into slot 0.
681
+ // drop the dragged column INTO `toKey`'s slot (drag-to-reorder) — the slot the
682
+ // dragover indicator highlights, and the same result the header menu's "Move
683
+ // left"/"Move right" gives for a neighbouring column. The sticky entity
684
+ // column (index 0) stays pinned — never reorder into slot 0.
618
685
  const reorderColumn = (fromKey, toKey) => {
619
686
  setDragCol(null);
620
687
  if (fromKey === toKey)
621
688
  return;
622
689
  setCols((prev) => {
623
690
  const from = prev.findIndex((c) => c.key === fromKey);
624
- let to = prev.findIndex((c) => c.key === toKey);
691
+ const to = prev.findIndex((c) => c.key === toKey);
625
692
  if (from < 1 || to < 1)
626
693
  return prev;
627
694
  const next = [...prev];
628
695
  const [moved] = next.splice(from, 1);
629
- to = next.findIndex((c) => c.key === toKey);
696
+ // insert at the TARGET'S ORIGINAL index, which is the slot the dragover
697
+ // indicator highlighted. Re-finding the target after the splice shifts
698
+ // every rightward drag one slot left, so an adjacent one does nothing.
630
699
  next.splice(to, 0, moved);
631
700
  return next;
632
701
  });
@@ -637,6 +706,29 @@ export function RecordsTable({ columns: columnsProp = defaultColumns, rows: rows
637
706
  setCols((prev) => prev.map((c) => (c.key === key ? { ...c, label: name } : c)));
638
707
  setRenamingCol(null);
639
708
  };
709
+ // the two `role="menu"` popovers driven by `openMenu`; the sort and filter
710
+ // popovers are dialogs and keep their own focus handling
711
+ const roleMenuOpen = !!openMenu && (openMenu.startsWith("col:") || openMenu.startsWith("sum:"));
712
+ const closeRoleMenu = React.useCallback(() => {
713
+ setOpenMenu(null);
714
+ menuReturn.current?.focus();
715
+ }, []);
716
+ const { onKeyDown: onMenuKeyDown } = useMenuKeyboard({
717
+ open: roleMenuOpen,
718
+ close: closeRoleMenu,
719
+ dismiss: () => setOpenMenu(null),
720
+ contentRef: menuRef,
721
+ });
722
+ const closeCtxMenu = React.useCallback(() => {
723
+ setCtxMenu(null);
724
+ ctxReturn.current?.focus();
725
+ }, []);
726
+ const { onKeyDown: onCtxKeyDown } = useMenuKeyboard({
727
+ open: !!ctxMenu,
728
+ close: closeCtxMenu,
729
+ dismiss: () => setCtxMenu(null),
730
+ contentRef: ctxRef,
731
+ });
640
732
  const openColumnMenu = (key, el) => {
641
733
  if (openMenu === `col:${key}`) {
642
734
  setOpenMenu(null);
@@ -644,6 +736,7 @@ export function RecordsTable({ columns: columnsProp = defaultColumns, rows: rows
644
736
  }
645
737
  const r = el.getBoundingClientRect();
646
738
  setMenuPos({ top: r.bottom + 4, left: r.left });
739
+ menuReturn.current = el;
647
740
  setOpenMenu(`col:${key}`);
648
741
  };
649
742
  const addColumn = () => {
@@ -651,7 +744,7 @@ export function RecordsTable({ columns: columnsProp = defaultColumns, rows: rows
651
744
  const key = `field-${idCounter.current}`;
652
745
  setCols((prev) => [
653
746
  ...prev,
654
- { key, label: `Field ${prev.length}`, type: "text", width: 160 },
747
+ { key, label: t.newColumnLabel(prev.length), type: "text", width: 160 },
655
748
  ]);
656
749
  };
657
750
  const deleteSelected = () => {
@@ -759,30 +852,30 @@ export function RecordsTable({ columns: columnsProp = defaultColumns, rows: rows
759
852
  window.addEventListener("pointerup", onUp);
760
853
  };
761
854
  return (_jsxs("div", { className: recordstableRecipe({ className }), ref: rootRef, children: [_jsxs("div", { className: "recordstable-toolbar", children: [_jsxs("span", { className: "recordstable-toolgroup", children: [_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: [_jsx(ArrowUpDown, { size: 14, strokeWidth: 1.75, "aria-hidden": "true" }), sorts.length
762
- ? `Sorted by ${cols.find((c) => c.key === sorts[0].key)?.label}${sorts.length > 1 ? ` +${sorts.length - 1}` : ""}`
763
- : "Sort"] }), openMenu === "sort" && (_jsxs("div", { className: "recordstable-menu recordstable-sortpop", role: "dialog", "aria-label": "Sort", children: [sorts.length === 0 ? (_jsx("div", { className: "recordstable-sort-empty", children: "No sorts applied to this view." })) : (sorts.map((s, i) => (_jsxs("div", { className: "recordstable-sort-row", onDragOver: (e) => {
855
+ ? t.sortedBy(cols.find((c) => c.key === sorts[0].key)?.label ?? "", sorts.length - 1)
856
+ : t.sort] }), openMenu === "sort" && (_jsxs("div", { className: "recordstable-menu recordstable-sortpop", role: "dialog", "aria-label": t.sort, children: [sorts.length === 0 ? (_jsx("div", { className: "recordstable-sort-empty", children: t.noSorts })) : (sorts.map((s, i) => (_jsxs("div", { className: "recordstable-sort-row", onDragOver: (e) => {
764
857
  if (dragSort !== null)
765
858
  e.preventDefault();
766
859
  }, onDrop: () => {
767
860
  if (dragSort !== null)
768
861
  moveSort(dragSort, i);
769
862
  setDragSort(null);
770
- }, children: [_jsx("button", { type: "button", className: "recordstable-sort-grip", "aria-label": "Reorder sort", draggable: true, onDragStart: () => setDragSort(i), onDragEnd: () => setDragSort(null), children: _jsx(GripVertical, { size: 15, strokeWidth: 1.75 }) }), _jsx(Select, { className: "recordstable-sort-field", searchable: true, value: s.key, onValueChange: (k) => setSortField(i, k), options: cols.map((c) => ({
863
+ }, children: [_jsx("button", { type: "button", className: "recordstable-sort-grip", "aria-label": t.reorderSort, draggable: true, onDragStart: () => setDragSort(i), onDragEnd: () => setDragSort(null), children: _jsx(GripVertical, { size: 15, strokeWidth: 1.75 }) }), _jsx(Select, { className: "recordstable-sort-field", searchable: true, value: s.key, onValueChange: (k) => setSortField(i, k), options: cols.map((c) => ({
771
864
  value: c.key,
772
865
  label: c.icon ? (_jsxs("span", { className: "recordstable-sort-optlabel", children: [_jsx(c.icon, { size: 15, strokeWidth: 1.75 }), c.label] })) : (c.label),
773
866
  keywords: c.label,
774
- })), "aria-label": "Sort field" }), _jsx(Select, { className: "recordstable-sort-dir", value: s.dir, onValueChange: (d) => setSortDir(i, d), options: [
867
+ })), "aria-label": t.sortField }), _jsx(Select, { className: "recordstable-sort-dir", value: s.dir, onValueChange: (d) => setSortDir(i, d), options: [
775
868
  {
776
869
  value: "asc",
777
- label: (_jsxs("span", { className: "recordstable-sort-optlabel", children: [_jsx(ArrowUpNarrowWide, { size: 15, strokeWidth: 1.75 }), "Ascending"] })),
778
- keywords: "Ascending",
870
+ label: (_jsxs("span", { className: "recordstable-sort-optlabel", children: [_jsx(ArrowUpNarrowWide, { size: 15, strokeWidth: 1.75 }), t.ascending] })),
871
+ keywords: t.ascending,
779
872
  },
780
873
  {
781
874
  value: "desc",
782
- label: (_jsxs("span", { className: "recordstable-sort-optlabel", children: [_jsx(ArrowDownWideNarrow, { size: 15, strokeWidth: 1.75 }), "Descending"] })),
783
- keywords: "Descending",
875
+ label: (_jsxs("span", { className: "recordstable-sort-optlabel", children: [_jsx(ArrowDownWideNarrow, { size: 15, strokeWidth: 1.75 }), t.descending] })),
876
+ keywords: t.descending,
784
877
  },
785
- ], "aria-label": "Sort direction" }), _jsx("button", { type: "button", className: "recordstable-sort-remove", onClick: () => removeSort(i), "aria-label": "Remove sort", children: _jsx(X, { size: 15, strokeWidth: 1.75 }) })] }, s.key)))), _jsx("div", { className: "recordstable-sort-addrow", children: _jsxs("button", { type: "button", className: "recordstable-sort-add", onClick: addSort, children: [_jsx(Plus, { size: 15, strokeWidth: 1.75 }), "Add sort"] }) }), _jsx("div", { className: "recordstable-sort-foot", children: _jsxs("a", { className: "recordstable-sort-learn", href: "https://attio.com/help", target: "_blank", rel: "noreferrer", children: [_jsx(CircleHelp, { size: 14, strokeWidth: 1.75 }), "Learn about sorting"] }) })] }))] }), _jsxs("span", { className: "recordstable-toolgroup", children: [_jsxs("button", { type: "button", className: `recordstable-tool recordstable-tool--ghost${activeFilters ? " recordstable-tool--active" : ""}`, onClick: () => setOpenMenu(openMenu === "filter" ? null : "filter"), "aria-haspopup": "menu", "aria-expanded": openMenu === "filter", "data-menu-trigger": true, children: [_jsx(ListFilter, { size: 14, strokeWidth: 1.75, "aria-hidden": "true" }), activeFilters ? `Filter · ${activeFilters}` : "Filter"] }), openMenu === "filter" && (_jsxs("div", { className: "recordstable-menu", role: "menu", children: [_jsx("input", { className: "recordstable-menu-input", placeholder: "Filter by\u2026", "aria-label": "Filter by field",
878
+ ], "aria-label": t.sortDirection }), _jsx("button", { type: "button", className: "recordstable-sort-remove", onClick: () => removeSort(i), "aria-label": t.removeSort, children: _jsx(X, { size: 15, strokeWidth: 1.75 }) })] }, s.key)))), _jsx("div", { className: "recordstable-sort-addrow", children: _jsxs("button", { type: "button", className: "recordstable-sort-add", onClick: addSort, children: [_jsx(Plus, { size: 15, strokeWidth: 1.75 }), t.addSort] }) }), _jsx("div", { className: "recordstable-sort-foot", children: _jsxs("a", { className: "recordstable-sort-learn", href: "https://attio.com/help", target: "_blank", rel: "noreferrer", children: [_jsx(CircleHelp, { size: 14, strokeWidth: 1.75 }), t.learnSorting] }) })] }))] }), _jsxs("span", { className: "recordstable-toolgroup", children: [_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: [_jsx(ListFilter, { size: 14, strokeWidth: 1.75, "aria-hidden": "true" }), t.filterCount(activeFilters)] }), openMenu === "filter" && (_jsxs("div", { className: "recordstable-menu", role: "dialog", "aria-label": t.addFilter, children: [_jsx("input", { className: "recordstable-menu-input", placeholder: t.filterByPlaceholder, "aria-label": t.filterByField,
786
879
  // biome-ignore lint/a11y/noAutofocus: focus the picker on open
787
880
  autoFocus: true, value: filterQuery, onChange: (e) => setFilterQuery(e.target.value) }), cols
788
881
  .filter((col) => col.label
@@ -792,14 +885,12 @@ export function RecordsTable({ columns: columnsProp = defaultColumns, rows: rows
792
885
  const col = cols.find((c) => c.key === cond.key);
793
886
  const open = openMenu === `fcond:${cond.id}`;
794
887
  const hasValue = !opNeedsValue(cond.op) || cond.value.trim();
795
- return (_jsxs("span", { className: "recordstable-toolgroup", children: [_jsxs("span", { className: "recordstable-fchip", children: [_jsxs("button", { type: "button", className: "recordstable-fchip-main", "data-menu-trigger": true, "aria-haspopup": "menu", onClick: () => setOpenMenu(open ? null : `fcond:${cond.id}`), children: [_jsx("span", { className: "recordstable-fchip-field", children: col?.label }), hasValue ? (_jsxs(_Fragment, { children: [_jsx("span", { className: "recordstable-fchip-op", children: OP_LABELS[cond.op] }), opNeedsValue(cond.op) && (_jsx("span", { className: "recordstable-fchip-val", children: cond.value }))] })) : (_jsx("span", { className: "recordstable-fchip-op", children: "Select condition" }))] }), _jsx("button", { type: "button", className: "recordstable-fchip-x", "aria-label": `Remove ${col?.label} filter`, onClick: () => removeFilter(cond.id), children: _jsx(X, { size: 13, strokeWidth: 2 }) })] }), open && (_jsxs("div", { className: "recordstable-menu", role: "menu", children: [_jsxs("div", { className: "recordstable-menu-head", children: [col?.label, " condition"] }), OP_ORDER.map((op) => (_jsxs("button", { type: "button", className: "recordstable-menu-item", "data-on": cond.op === op ? "" : undefined, onClick: () => patchFilter(cond.id, { op }), children: [OP_LABELS[op], cond.op === op ? (_jsx(Check, { className: "recordstable-menu-check", size: 15, strokeWidth: 1.75 })) : null] }, op))), opNeedsValue(cond.op) && (_jsx("input", { className: "recordstable-menu-input", placeholder: "Value\u2026", "aria-label": `${col?.label} filter value`,
888
+ return (_jsxs("span", { className: "recordstable-toolgroup", children: [_jsxs("span", { className: "recordstable-fchip", children: [_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: [_jsx("span", { className: "recordstable-fchip-field", children: col?.label }), hasValue ? (_jsxs(_Fragment, { children: [_jsx("span", { className: "recordstable-fchip-op", children: t.op[cond.op] }), opNeedsValue(cond.op) && (_jsx("span", { className: "recordstable-fchip-val", children: cond.value }))] })) : (_jsx("span", { className: "recordstable-fchip-op", children: t.selectCondition }))] }), _jsx("button", { type: "button", className: "recordstable-fchip-x", "aria-label": `Remove ${col?.label} filter`, onClick: () => removeFilter(cond.id), children: _jsx(X, { size: 13, strokeWidth: 2 }) })] }), open && (_jsxs("div", { className: "recordstable-menu", role: "dialog", "aria-label": `${col?.label} condition`, children: [_jsxs("div", { className: "recordstable-menu-head", children: [col?.label, " condition"] }), OP_ORDER.map((op) => (_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 ? (_jsx(Check, { className: "recordstable-menu-check", size: 15, strokeWidth: 1.75 })) : null] }, op))), opNeedsValue(cond.op) && (_jsx("input", { className: "recordstable-menu-input", placeholder: t.filterValuePlaceholder, "aria-label": `${col?.label} filter value`,
796
889
  // biome-ignore lint/a11y/noAutofocus: focus the value field
797
890
  autoFocus: true, value: cond.value, onChange: (e) => patchFilter(cond.id, { value: e.target.value }) }))] }))] }, cond.id));
798
- }), _jsx("span", { className: "recordstable-spacer" }), _jsxs("span", { className: "recordstable-count", children: [visibleRows.length, " record", visibleRows.length === 1 ? "" : "s"] }), showNew && (_jsxs("button", { type: "button", className: "recordstable-new", onClick: () => setCreating(true), children: [_jsx(Plus, { size: 15, strokeWidth: 2, "aria-hidden": "true" }), "New"] }))] }), _jsxs("div", { ref: scrollRef, className: "recordstable-scroll", tabIndex: -1, "data-scrolled": scrolledX ? "" : undefined, "data-scrolled-y": scrolledY ? "" : undefined, onKeyDown: onGridKeyDown, onScroll: (e) => {
891
+ }), _jsx("span", { className: "recordstable-spacer" }), _jsx("span", { className: "recordstable-count", children: t.recordCount(visibleRows.length) }), showNew && (_jsxs("button", { type: "button", className: "recordstable-new", onClick: () => setCreating(true), children: [_jsx(Plus, { size: 15, strokeWidth: 2, "aria-hidden": "true" }), t.newButton] }))] }), _jsxs("div", { ref: scrollRef, className: "recordstable-scroll", tabIndex: -1, "data-scrolled": scrolledX ? "" : undefined, onKeyDown: onGridKeyDown, onScroll: (e) => {
799
892
  const left = e.currentTarget.scrollLeft > 0;
800
- const top = e.currentTarget.scrollTop > 0;
801
893
  setScrolledX((prev) => (prev === left ? prev : left));
802
- setScrolledY((prev) => (prev === top ? prev : top));
803
894
  }, children: [_jsxs("table", { className: "recordstable-table", style: {
804
895
  // fill the container; the trailing add-column absorbs the slack (a
805
896
  // flexible filler). minWidth keeps real column widths and drives the
@@ -808,7 +899,7 @@ export function RecordsTable({ columns: columnsProp = defaultColumns, rows: rows
808
899
  minWidth: CHECK_W +
809
900
  ADDCOL_W +
810
901
  cols.reduce((sum, c) => sum + colWidth(c), 0),
811
- }, children: [_jsxs("colgroup", { children: [_jsx("col", { style: { width: CHECK_W } }), cols.map((col) => (_jsx("col", { style: { width: colWidth(col) } }, col.key))), _jsx("col", {})] }), _jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { className: "recordstable-th recordstable-col-check", children: _jsx("span", { className: "recordstable-check-inner", children: _jsx(Checkbox, { "aria-label": "Select all", checked: allSelected, indeterminate: someSelected, onChange: toggleAll }) }) }), cols.map((col, i) => {
902
+ }, children: [_jsxs("colgroup", { children: [_jsx("col", { style: { width: CHECK_W } }), cols.map((col) => (_jsx("col", { style: { width: colWidth(col) } }, col.key))), _jsx("col", {})] }), _jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { className: "recordstable-th recordstable-col-check", children: _jsx("span", { className: "recordstable-check-inner", children: _jsx(Checkbox, { "aria-label": t.selectAll, checked: allSelected, indeterminate: someSelected, onChange: toggleAll }) }) }), cols.map((col, i) => {
812
903
  const Icon = col.icon;
813
904
  const sc = sorts.find((s) => s.key === col.key);
814
905
  return (_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
@@ -818,7 +909,7 @@ export function RecordsTable({ columns: columnsProp = defaultColumns, rows: rows
818
909
  }
819
910
  : undefined, children: [renamingCol === col.key ? (_jsx("input", { className: "recordstable-th-rename",
820
911
  // biome-ignore lint/a11y/noAutofocus: focus the rename field the moment it opens
821
- autoFocus: true, defaultValue: col.label, "aria-label": "Column label", onKeyDown: (e) => {
912
+ autoFocus: true, defaultValue: col.label, "aria-label": t.columnLabel, onKeyDown: (e) => {
822
913
  if (e.key === "Enter")
823
914
  renameColumn(col.key, e.currentTarget.value);
824
915
  else if (e.key === "Escape")
@@ -830,18 +921,19 @@ export function RecordsTable({ columns: columnsProp = defaultColumns, rows: rows
830
921
  }
831
922
  : undefined, onDragEnd: () => setDragCol(null), onClick: (e) => openColumnMenu(col.key, e.currentTarget), children: [Icon ? _jsx(Icon, { size: 14, strokeWidth: 1.75 }) : null, _jsx("span", { className: "recordstable-th-label", children: col.label }), _jsx("span", { className: "recordstable-sort", children: _jsx("svg", { width: "12", height: "12", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "1.5", "aria-hidden": "true", children: _jsx("path", { d: "m4 6 4 4 4-4" }) }) })] })), openMenu === `col:${col.key}` &&
832
923
  rootRef.current &&
833
- createPortal(_jsxs("div", { className: "recordstable-menu recordstable-menu--fixed recordstable-menu--hdr", role: "menu", style: menuPos ?? undefined, children: [_jsxs("button", { type: "button", className: "recordstable-menu-item", onClick: () => sortBy(col.key, "asc"), children: [_jsx(ArrowUp, { size: 15, strokeWidth: 1.75 }), "Sort ascending"] }), _jsxs("button", { type: "button", className: "recordstable-menu-item", onClick: () => sortBy(col.key, "desc"), children: [_jsx(ArrowDown, { size: 15, strokeWidth: 1.75 }), "Sort descending"] }), _jsxs("button", { type: "button", className: "recordstable-menu-item", disabled: i <= 1, onClick: () => moveColumn(col.key, -1), children: [_jsx(ArrowLeft, { size: 15, strokeWidth: 1.75 }), "Move left"] }), _jsxs("button", { type: "button", className: "recordstable-menu-item", disabled: i === 0 || i >= cols.length - 1, onClick: () => moveColumn(col.key, 1), children: [_jsx(ArrowRight, { size: 15, strokeWidth: 1.75 }), "Move right"] }), _jsx("div", { className: "recordstable-menu-sep" }), _jsxs("button", { type: "button", className: "recordstable-menu-item", onClick: () => {
924
+ createPortal(_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: [_jsxs("button", { type: "button", className: "recordstable-menu-item", role: "menuitem", tabIndex: -1, onClick: () => sortBy(col.key, "asc"), children: [_jsx(ArrowUp, { size: 15, strokeWidth: 1.75 }), t.sortAscending] }), _jsxs("button", { type: "button", className: "recordstable-menu-item", role: "menuitem", tabIndex: -1, onClick: () => sortBy(col.key, "desc"), children: [_jsx(ArrowDown, { size: 15, strokeWidth: 1.75 }), t.sortDescending] }), _jsxs("button", { type: "button", className: "recordstable-menu-item", role: "menuitem", tabIndex: -1, disabled: i <= 1, onClick: () => moveColumn(col.key, -1), children: [_jsx(ArrowLeft, { size: 15, strokeWidth: 1.75 }), t.moveLeft] }), _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: [_jsx(ArrowRight, { size: 15, strokeWidth: 1.75 }), t.moveRight] }), _jsx("div", { className: "recordstable-menu-sep" }), _jsxs("button", { type: "button", className: "recordstable-menu-item", role: "menuitem", tabIndex: -1, onClick: () => {
834
925
  setRenamingCol(col.key);
835
926
  setOpenMenu(null);
836
- }, children: [_jsx(Pencil, { size: 15, strokeWidth: 1.75 }), "Edit column label"] }), cols.length > 1 && (_jsxs("button", { type: "button", className: "recordstable-menu-item", onClick: () => hideColumn(col.key), children: [_jsx(EyeOff, { size: 15, strokeWidth: 1.75 }), "Hide from view"] }))] }), rootRef.current), _jsx("span", { className: "recordstable-resize", "aria-hidden": "true", onPointerDown: (e) => {
927
+ }, children: [_jsx(Pencil, { size: 15, strokeWidth: 1.75 }), t.editColumnLabel] }), cols.length > 1 && (_jsxs("button", { type: "button", className: "recordstable-menu-item", role: "menuitem", tabIndex: -1, onClick: () => hideColumn(col.key), children: [_jsx(EyeOff, { size: 15, strokeWidth: 1.75 }), t.hideFromView] }))] }), rootRef.current), _jsx("span", { className: "recordstable-resize", "aria-hidden": "true", onPointerDown: (e) => {
837
928
  e.preventDefault();
838
929
  e.stopPropagation();
839
930
  const th = e.currentTarget
840
931
  .parentElement;
841
932
  resizeCol(col.key, e.clientX, th ? th.getBoundingClientRect().width : 180);
842
933
  }, onClick: (e) => e.stopPropagation() })] }, col.key));
843
- }), _jsx("th", { className: "recordstable-th recordstable-addcol", children: allowAddColumn && (_jsx("button", { type: "button", onClick: addColumn, "aria-label": "Add column", children: _jsx(Plus, { size: 15, strokeWidth: 1.75 }) })) })] }) }), _jsx("tbody", { children: visibleRows.map((row) => (_jsxs("tr", { "data-selected": selected.has(row.id) ? "" : undefined, onContextMenu: (e) => {
934
+ }), _jsx("th", { className: "recordstable-th recordstable-addcol", children: allowAddColumn && (_jsx("button", { type: "button", onClick: addColumn, "aria-label": t.addColumn, children: _jsx(Plus, { size: 15, strokeWidth: 1.75 }) })) })] }) }), _jsx("tbody", { children: visibleRows.map((row) => (_jsxs("tr", { "data-selected": selected.has(row.id) ? "" : undefined, onContextMenu: (e) => {
844
935
  e.preventDefault();
936
+ ctxReturn.current = document.activeElement;
845
937
  setCtxMenu({ x: e.clientX, y: e.clientY, id: row.id });
846
938
  }, children: [_jsx("td", { className: "recordstable-td recordstable-col-check", children: _jsx("span", { className: "recordstable-check-inner", children: _jsx(Checkbox, { "aria-label": `Select ${row.id}`, checked: selected.has(row.id), onClick: (e) => {
847
939
  shiftHeld.current = e.shiftKey;
@@ -849,7 +941,7 @@ export function RecordsTable({ columns: columnsProp = defaultColumns, rows: rows
849
941
  const canEdit = canEditCol(col);
850
942
  const isPicker = isPickerCol(col);
851
943
  const isEditing = editing?.id === row.id && editing?.key === col.key;
852
- return (_jsxs("td", { className: `recordstable-td${i === 0 ? " recordstable-col-entity" : ""}${canEdit ? " recordstable-td--editable" : ""}${isPicker ? " recordstable-td--picker" : ""}`, "data-selected": selCell?.id === row.id && selCell?.key === col.key
944
+ return (_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
853
945
  ? ""
854
946
  : undefined, onClick: () => {
855
947
  if (isPicker)
@@ -858,14 +950,14 @@ export function RecordsTable({ columns: columnsProp = defaultColumns, rows: rows
858
950
  scrollRef.current?.focus({ preventScroll: true });
859
951
  }, onDoubleClick: canEdit
860
952
  ? () => setEditing({ id: row.id, key: col.key })
861
- : undefined, children: [isPicker ? (col.type === "status" ? (_jsx(StatusCell, { column: col, row: row, onChange: (v) => setCell(row.id, col.key, v) })) : col.type === "reference" ? (_jsx(ReferenceCell, { column: col, row: row, onChange: (v) => setCell(row.id, col.key, v) })) : col.type === "tags" ? (_jsx(TagsCell, { column: col, row: row, options: tagOptionsFor(col.key), onChange: (v) => setCell(row.id, col.key, v) })) : (_jsx(DateCell, { column: col, row: row, onChange: (v) => setCell(row.id, col.key, v) }))) : isEditing ? (_jsx("input", { className: `recordstable-edit${col.type === "number" ? " recordstable-edit--num" : ""}`,
953
+ : undefined, children: [isPicker ? (col.type === "status" ? (_jsx(StatusCell, { column: col, row: row, onChange: (v) => setCell(row.id, col.key, v) })) : col.type === "reference" ? (_jsx(ReferenceCell, { labels: t, column: col, row: row, onChange: (v) => setCell(row.id, col.key, v) })) : col.type === "tags" ? (_jsx(TagsCell, { labels: t, column: col, row: row, options: tagOptionsFor(col.key), onChange: (v) => setCell(row.id, col.key, v) })) : (_jsx(DateCell, { column: col, row: row, onChange: (v) => setCell(row.id, col.key, v) }))) : isEditing ? (_jsx("input", { className: `recordstable-edit${col.type === "number" ? " recordstable-edit--num" : ""}`,
862
954
  // biome-ignore lint/a11y/noAutofocus: focusing the just-opened editor is the expected behavior
863
955
  autoFocus: true, defaultValue: cellText(col, row), onBlur: (e) => commitEdit(row.id, col.key, e.target.value), onKeyDown: (e) => {
864
956
  if (e.key === "Enter")
865
957
  commitEdit(row.id, col.key, e.target.value);
866
958
  if (e.key === "Escape")
867
959
  setEditing(null);
868
- } })) : (_jsx(CellView, { column: col, row: row, pinned: i === 0 && pinned.has(row.id), onOpenPage: i === 0 && onOpenRecordPage
960
+ } })) : (_jsx(CellView, { labels: t, column: col, row: row, pinned: i === 0 && pinned.has(row.id), onOpenPage: i === 0 && onOpenRecordPage
869
961
  ? () => onOpenRecordPage(row)
870
962
  : undefined, avatarTone: avatarTone })), i === 0 && onOpenRecord && !isEditing && (_jsx("button", { type: "button", className: "recordstable-open", "aria-label": `Open ${cellText(col, row)}`, onClick: (e) => {
871
963
  e.stopPropagation();
@@ -885,16 +977,15 @@ export function RecordsTable({ columns: columnsProp = defaultColumns, rows: rows
885
977
  bottom: window.innerHeight - r.top + 4,
886
978
  right: window.innerWidth - r.right,
887
979
  });
980
+ menuReturn.current = e.currentTarget;
888
981
  setOpenMenu(`sum:${col.key}`);
889
- }, children: res ? (_jsxs(_Fragment, { children: [_jsx("span", { className: "recordstable-sum-label", children: AGG_LABELS[a] }), _jsx("span", { className: "recordstable-sum-value", children: res.value })] })) : (_jsx("span", { className: "recordstable-sum-label", children: "Calculate" })) }), sumOpen &&
982
+ }, children: res ? (_jsxs(_Fragment, { children: [_jsx("span", { className: "recordstable-sum-label", children: t.agg[a] }), _jsx("span", { className: "recordstable-sum-value", children: res.value })] })) : (_jsx("span", { className: "recordstable-sum-label", children: t.calculate })) }), sumOpen &&
890
983
  rootRef.current &&
891
- createPortal(_jsx("div", { className: "recordstable-menu recordstable-menu--fixed recordstable-menu--up", role: "menu", style: menuPos ?? undefined, children: aggOptions(col).map((opt) => (_jsxs("button", { type: "button", className: "recordstable-menu-item", "data-on": a === opt ? "" : undefined, onClick: () => setAggValue(col.key, opt), children: [AGG_LABELS[opt], a === opt ? (_jsx(Check, { className: "recordstable-menu-check", size: 15, strokeWidth: 1.75 })) : null] }, opt))) }), rootRef.current)] }) }, col.key));
892
- }), _jsx("td", { className: "recordstable-sum recordstable-addcol" })] }) })] }), visibleRows.length === 0 ? (_jsx("div", { className: "recordstable-empty", children: _jsx(EmptyState, { variant: "page", icon: activeFilters ? (_jsx(ListFilter, { size: 18, strokeWidth: 1.75, "aria-hidden": "true" })) : (_jsx(Inbox, { size: 18, strokeWidth: 1.75, "aria-hidden": "true" })), title: activeFilters
893
- ? "No records match that filter"
894
- : "No records yet", description: activeFilters
984
+ createPortal(_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) => (_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 ? (_jsx(Check, { className: "recordstable-menu-check", size: 15, strokeWidth: 1.75 })) : null] }, opt))) }), rootRef.current)] }) }, col.key));
985
+ }), _jsx("td", { className: "recordstable-sum recordstable-addcol" })] }) })] }), visibleRows.length === 0 ? (_jsx("div", { className: "recordstable-empty", children: _jsx(EmptyState, { variant: "page", icon: activeFilters ? (_jsx(ListFilter, { size: 18, strokeWidth: 1.75, "aria-hidden": "true" })) : (_jsx(Inbox, { size: 18, strokeWidth: 1.75, "aria-hidden": "true" })), title: activeFilters ? "No records match that filter" : t.noRecords, description: activeFilters
895
986
  ? "None of your records match the filters that are set."
896
- : `Create your first ${cols.find((c) => c.type === "entity")?.label?.toLowerCase() ?? "record"} to get started.`, action: activeFilters ? (_jsxs(Button, { intent: "primary", size: "sm", onClick: () => setFilterConds([]), children: [_jsx(ListFilter, { size: 15, strokeWidth: 1.75 }), "Clear filter"] })) : (_jsxs(Button, { intent: "primary", size: "sm", onClick: addRow, children: [_jsx(Plus, { size: 15, strokeWidth: 1.75 }), "New record"] })) }) })) : (_jsxs("button", { type: "button", className: "recordstable-addrow", onClick: addRow, children: [_jsx(Plus, { size: 15, strokeWidth: 1.75, "aria-hidden": "true" }), "New record"] }))] }), selected.size > 0 && (_jsxs(BulkBar, { count: selected.size, countLabel: "Selected", onClear: () => setSelected(new Set()), children: [_jsx(BulkBarButton, { icon: _jsx(ListPlus, { size: 15, strokeWidth: 1.75 }), onClick: addToList, children: "Add to list" }), _jsx(BulkBarButton, { icon: _jsx(Mail, { size: 15, strokeWidth: 1.75 }), onClick: sendEmail, children: "Send email" }), _jsx(BulkBarButton, { icon: _jsx(Workflow, { size: 15, strokeWidth: 1.75 }), onClick: runWorkflow, children: "Run workflow" }), _jsxs(Menu, { align: "end", "aria-label": "More actions", trigger: _jsx(BulkBarButton, { icon: _jsx(MoreHorizontal, { size: 15, strokeWidth: 1.75 }), children: "More" }), children: [_jsx(MenuItem, { icon: _jsx(Download, { size: 15, strokeWidth: 1.75 }), onSelect: exportSelected, children: "Export CSV" }), _jsx(MenuItem, { icon: _jsx(Trash2, { size: 15, strokeWidth: 1.75 }), danger: true, onSelect: deleteSelected, children: "Delete" })] })] })), _jsx(CreateRecordDialog, { open: creating, onOpenChange: setCreating, columns: cols, entityLabel: cols.find((c) => c.type === "entity")?.label ?? "record", onCreate: (row) => setData((prev) => [...prev, row]) }), ctxMenu &&
897
- createPortal(_jsxs("div", { className: "recordstable-menu recordstable-ctx", role: "menu", style: { position: "fixed", top: ctxMenu.y, left: ctxMenu.x }, children: [_jsxs("button", { type: "button", className: "recordstable-menu-item", onClick: () => copyRow(ctxMenu.id), children: [_jsx(Copy, { size: 15, strokeWidth: 1.75 }), "Copy"] }), _jsxs("button", { type: "button", className: "recordstable-menu-item", onClick: () => duplicateRow(ctxMenu.id), children: [_jsx(CopyPlus, { size: 15, strokeWidth: 1.75 }), "Duplicate"] }), _jsxs("button", { type: "button", className: "recordstable-menu-item", onClick: () => pinRow(ctxMenu.id), children: [_jsx(Star, { size: 15, strokeWidth: 1.75 }), "Add to list"] }), _jsxs("button", { type: "button", className: "recordstable-menu-item recordstable-menu-item--danger", onClick: () => deleteRow(ctxMenu.id), children: [_jsx(Trash2, { size: 15, strokeWidth: 1.75 }), "Delete"] })] }), rootRef.current ?? document.body)] }));
987
+ : `Create your first ${cols.find((c) => c.type === "entity")?.label?.toLowerCase() ?? "record"} to get started.`, action: activeFilters ? (_jsxs(Button, { intent: "primary", size: "sm", onClick: () => setFilterConds([]), children: [_jsx(ListFilter, { size: 15, strokeWidth: 1.75 }), t.clearFilter] })) : (_jsxs(Button, { intent: "primary", size: "sm", onClick: addRow, children: [_jsx(Plus, { size: 15, strokeWidth: 1.75 }), t.newRecord] })) }) })) : (_jsxs("button", { type: "button", className: "recordstable-addrow", onClick: addRow, children: [_jsx(Plus, { size: 15, strokeWidth: 1.75, "aria-hidden": "true" }), t.newRecord] }))] }), selected.size > 0 && (_jsxs(BulkBar, { count: selected.size, countLabel: "Selected", onClear: () => setSelected(new Set()), children: [_jsx(BulkBarButton, { icon: _jsx(ListPlus, { size: 15, strokeWidth: 1.75 }), onClick: addToList, children: t.addToList }), _jsx(BulkBarButton, { icon: _jsx(Mail, { size: 15, strokeWidth: 1.75 }), onClick: sendEmail, children: t.sendEmail }), _jsx(BulkBarButton, { icon: _jsx(Workflow, { size: 15, strokeWidth: 1.75 }), onClick: runWorkflow, children: t.runWorkflow }), _jsxs(Menu, { align: "end", "aria-label": t.moreActions, trigger: _jsx(BulkBarButton, { icon: _jsx(MoreHorizontal, { size: 15, strokeWidth: 1.75 }), children: t.more }), children: [_jsx(MenuItem, { icon: _jsx(Download, { size: 15, strokeWidth: 1.75 }), onSelect: exportSelected, children: t.exportCsv }), _jsx(MenuItem, { icon: _jsx(Trash2, { size: 15, strokeWidth: 1.75 }), danger: true, onSelect: deleteSelected, children: t.delete })] })] })), _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 &&
988
+ createPortal(_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: [_jsxs("button", { type: "button", className: "recordstable-menu-item", role: "menuitem", tabIndex: -1, onClick: () => copyRow(ctxMenu.id), children: [_jsx(Copy, { size: 15, strokeWidth: 1.75 }), t.copy] }), _jsxs("button", { type: "button", className: "recordstable-menu-item", role: "menuitem", tabIndex: -1, onClick: () => duplicateRow(ctxMenu.id), children: [_jsx(CopyPlus, { size: 15, strokeWidth: 1.75 }), t.duplicate] }), _jsxs("button", { type: "button", className: "recordstable-menu-item", role: "menuitem", tabIndex: -1, onClick: () => pinRow(ctxMenu.id), children: [_jsx(Star, { size: 15, strokeWidth: 1.75 }), t.addToList] }), _jsxs("button", { type: "button", className: "recordstable-menu-item recordstable-menu-item--danger", role: "menuitem", tabIndex: -1, onClick: () => deleteRow(ctxMenu.id), children: [_jsx(Trash2, { size: 15, strokeWidth: 1.75 }), t.delete] })] }), rootRef.current ?? document.body)] }));
898
989
  }
899
990
  export const defaultColumns = [
900
991
  { key: "company", label: "Company", type: "entity", icon: Building2 },