@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.
- package/README.md +20 -0
- package/dist/accordion.css +9 -1
- package/dist/appshell.css +12 -4
- package/dist/authshell.css +57 -0
- package/dist/authshell.d.ts +7 -0
- package/dist/avatargroup.css +2 -2
- package/dist/badge.css +4 -4
- package/dist/barchart.css +13 -5
- package/dist/base.css +25 -0
- package/dist/board.css +9 -1
- package/dist/bulkbar.css +11 -3
- package/dist/button.css +16 -2
- package/dist/button.d.ts +2 -1
- package/dist/card.css +8 -0
- package/dist/checkbox.css +17 -1
- package/dist/cjs/components/accordion/accordion.js +69 -0
- package/dist/cjs/components/accordion/accordion.recipe.js +26 -0
- package/dist/cjs/components/accordion/index.js +7 -0
- package/dist/cjs/components/avatar/avatar.js +44 -0
- package/dist/cjs/components/avatar/avatar.recipe.js +42 -0
- package/dist/cjs/components/avatar/index.js +7 -0
- package/dist/cjs/components/avatargroup/avatargroup.js +52 -0
- package/dist/cjs/components/avatargroup/avatargroup.recipe.js +18 -0
- package/dist/cjs/components/avatargroup/index.js +7 -0
- package/dist/cjs/components/badge/badge.js +44 -0
- package/dist/cjs/components/badge/badge.recipe.js +68 -0
- package/dist/cjs/components/badge/index.js +7 -0
- package/dist/cjs/components/barchart/barchart.js +45 -0
- package/dist/cjs/components/barchart/barchart.recipe.js +34 -0
- package/dist/cjs/components/barchart/index.js +7 -0
- package/dist/cjs/components/breadcrumb/breadcrumb.js +53 -0
- package/dist/cjs/components/breadcrumb/breadcrumb.recipe.js +21 -0
- package/dist/cjs/components/breadcrumb/index.js +7 -0
- package/dist/cjs/components/bulkbar/bulkbar.js +53 -0
- package/dist/cjs/components/bulkbar/bulkbar.recipe.js +31 -0
- package/dist/cjs/components/bulkbar/index.js +8 -0
- package/dist/cjs/components/button/button.js +62 -0
- package/dist/cjs/components/button/button.recipe.js +58 -0
- package/dist/cjs/components/button/index.js +7 -0
- package/dist/cjs/components/callout/callout.js +25 -0
- package/dist/cjs/components/callout/callout.recipe.js +29 -0
- package/dist/cjs/components/callout/index.js +7 -0
- package/dist/cjs/components/card/card.js +54 -0
- package/dist/cjs/components/card/card.recipe.js +31 -0
- package/dist/cjs/components/card/index.js +10 -0
- package/dist/cjs/components/checkbox/checkbox.js +56 -0
- package/dist/cjs/components/checkbox/checkbox.recipe.js +34 -0
- package/dist/cjs/components/checkbox/index.js +7 -0
- package/dist/cjs/components/columncalc/columncalc.js +139 -0
- package/dist/cjs/components/columncalc/columncalc.recipe.js +33 -0
- package/dist/cjs/components/columncalc/index.js +10 -0
- package/dist/cjs/components/contextmenu/contextmenu.js +78 -0
- package/dist/cjs/components/contextmenu/contextmenu.recipe.js +24 -0
- package/dist/cjs/components/contextmenu/index.js +7 -0
- package/dist/cjs/components/datatable/datatable.js +508 -0
- package/dist/cjs/components/datatable/datatable.recipe.js +113 -0
- package/dist/cjs/components/datatable/index.js +7 -0
- package/dist/cjs/components/datepicker/datepicker.js +153 -0
- package/dist/cjs/components/datepicker/datepicker.recipe.js +180 -0
- package/dist/cjs/components/datepicker/index.js +19 -0
- package/dist/cjs/components/datepickerfield/datepickerfield.js +85 -0
- package/dist/cjs/components/datepickerfield/datepickerfield.recipe.js +44 -0
- package/dist/cjs/components/datepickerfield/index.js +7 -0
- package/dist/cjs/components/dialog/dialog.js +86 -0
- package/dist/cjs/components/dialog/dialog.recipe.js +35 -0
- package/dist/cjs/components/dialog/index.js +9 -0
- package/dist/cjs/components/divider/divider.js +10 -0
- package/dist/cjs/components/divider/divider.recipe.js +17 -0
- package/dist/cjs/components/divider/index.js +7 -0
- package/dist/cjs/components/donutchart/donutchart.js +41 -0
- package/dist/cjs/components/donutchart/donutchart.recipe.js +30 -0
- package/dist/cjs/components/donutchart/index.js +7 -0
- package/dist/cjs/components/drawer/drawer.js +80 -0
- package/dist/cjs/components/drawer/drawer.recipe.js +30 -0
- package/dist/cjs/components/drawer/index.js +9 -0
- package/dist/cjs/components/emptystate/emptystate.js +10 -0
- package/dist/cjs/components/emptystate/emptystate.recipe.js +34 -0
- package/dist/cjs/components/emptystate/index.js +7 -0
- package/dist/cjs/components/formfield/formfield.js +50 -0
- package/dist/cjs/components/formfield/formfield.recipe.js +22 -0
- package/dist/cjs/components/formfield/index.js +7 -0
- package/dist/cjs/components/input/index.js +7 -0
- package/dist/cjs/components/input/input.js +47 -0
- package/dist/cjs/components/input/input.recipe.js +59 -0
- package/dist/cjs/components/kbd/index.js +7 -0
- package/dist/cjs/components/kbd/kbd.js +44 -0
- package/dist/cjs/components/kbd/kbd.recipe.js +23 -0
- package/dist/cjs/components/linechart/index.js +7 -0
- package/dist/cjs/components/linechart/linechart.js +57 -0
- package/dist/cjs/components/linechart/linechart.recipe.js +35 -0
- package/dist/cjs/components/menu/index.js +14 -0
- package/dist/cjs/components/menu/menu-keyboard.js +117 -0
- package/dist/cjs/components/menu/menu.js +144 -0
- package/dist/cjs/components/menu/menu.recipe.js +54 -0
- package/dist/cjs/components/multiselect/index.js +7 -0
- package/dist/cjs/components/multiselect/multiselect.js +161 -0
- package/dist/cjs/components/multiselect/multiselect.recipe.js +71 -0
- package/dist/cjs/components/popover/index.js +7 -0
- package/dist/cjs/components/popover/popover.js +106 -0
- package/dist/cjs/components/popover/popover.recipe.js +21 -0
- package/dist/cjs/components/progress/index.js +7 -0
- package/dist/cjs/components/progress/progress.js +26 -0
- package/dist/cjs/components/progress/progress.recipe.js +20 -0
- package/dist/cjs/components/radiogroup/index.js +8 -0
- package/dist/cjs/components/radiogroup/radiogroup.js +83 -0
- package/dist/cjs/components/radiogroup/radiogroup.recipe.js +40 -0
- package/dist/cjs/components/resizablepanel/index.js +12 -0
- package/dist/cjs/components/resizablepanel/resizablepanel.js +176 -0
- package/dist/cjs/components/resizablepanel/resizablepanel.recipe.js +69 -0
- package/dist/cjs/components/segmented/index.js +7 -0
- package/dist/cjs/components/segmented/segmented.js +95 -0
- package/dist/cjs/components/segmented/segmented.recipe.js +23 -0
- package/dist/cjs/components/select/index.js +7 -0
- package/dist/cjs/components/select/select.js +180 -0
- package/dist/cjs/components/select/select.recipe.js +79 -0
- package/dist/cjs/components/skeleton/index.js +7 -0
- package/dist/cjs/components/skeleton/skeleton.js +13 -0
- package/dist/cjs/components/skeleton/skeleton.recipe.js +17 -0
- package/dist/cjs/components/spinner/index.js +7 -0
- package/dist/cjs/components/spinner/spinner.js +15 -0
- package/dist/cjs/components/spinner/spinner.recipe.js +16 -0
- package/dist/cjs/components/stagebar/index.js +7 -0
- package/dist/cjs/components/stagebar/stagebar.js +26 -0
- package/dist/cjs/components/stagebar/stagebar.recipe.js +17 -0
- package/dist/cjs/components/statcard/index.js +7 -0
- package/dist/cjs/components/statcard/statcard.js +15 -0
- package/dist/cjs/components/statcard/statcard.recipe.js +37 -0
- package/dist/cjs/components/switch/index.js +7 -0
- package/dist/cjs/components/switch/switch.js +58 -0
- package/dist/cjs/components/switch/switch.recipe.js +29 -0
- package/dist/cjs/components/table/index.js +14 -0
- package/dist/cjs/components/table/table.js +66 -0
- package/dist/cjs/components/table/table.recipe.js +43 -0
- package/dist/cjs/components/tabletoolbar/index.js +20 -0
- package/dist/cjs/components/tabletoolbar/tabletoolbar.chrome.js +84 -0
- package/dist/cjs/components/tabletoolbar/tabletoolbar.js +237 -0
- package/dist/cjs/components/tabletoolbar/tabletoolbar.recipe.js +26 -0
- package/dist/cjs/components/tabs/index.js +7 -0
- package/dist/cjs/components/tabs/tabs.js +106 -0
- package/dist/cjs/components/tabs/tabs.recipe.js +36 -0
- package/dist/cjs/components/textarea/index.js +7 -0
- package/dist/cjs/components/textarea/textarea.js +56 -0
- package/dist/cjs/components/textarea/textarea.recipe.js +46 -0
- package/dist/cjs/components/toast/index.js +8 -0
- package/dist/cjs/components/toast/toast.js +95 -0
- package/dist/cjs/components/toast/toast.recipe.js +31 -0
- package/dist/cjs/components/tooltip/index.js +7 -0
- package/dist/cjs/components/tooltip/tooltip.js +95 -0
- package/dist/cjs/components/tooltip/tooltip.recipe.js +16 -0
- package/dist/cjs/components/wheelpicker/index.js +7 -0
- package/dist/cjs/components/wheelpicker/wheelpicker.js +134 -0
- package/dist/cjs/components/wheelpicker/wheelpicker.recipe.js +95 -0
- package/dist/cjs/core/cx.js +8 -0
- package/dist/cjs/core/define.js +6 -0
- package/dist/cjs/core/index.js +11 -0
- package/dist/cjs/core/primitive.js +49 -0
- package/dist/cjs/core/resolve.js +44 -0
- package/dist/cjs/core/types.js +2 -0
- package/dist/cjs/index.js +92 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/product/appshell/appshell.js +104 -0
- package/dist/cjs/product/appshell/appshell.recipe.js +30 -0
- package/dist/cjs/product/appshell/index.js +7 -0
- package/dist/cjs/product/authshell/authshell.js +21 -0
- package/dist/cjs/product/authshell/authshell.recipe.js +67 -0
- package/dist/cjs/product/authshell/index.js +7 -0
- package/dist/cjs/product/board/board.js +78 -0
- package/dist/cjs/product/board/board.recipe.js +31 -0
- package/dist/cjs/product/board/index.js +7 -0
- package/dist/cjs/product/command/command.js +238 -0
- package/dist/cjs/product/command/command.recipe.js +47 -0
- package/dist/cjs/product/command/index.js +8 -0
- package/dist/cjs/product/entitycard/entitycard.js +14 -0
- package/dist/cjs/product/entitycard/entitycard.recipe.js +32 -0
- package/dist/cjs/product/entitycard/index.js +7 -0
- package/dist/cjs/product/flowcanvas/flowcanvas.js +121 -0
- package/dist/cjs/product/flowcanvas/flowcanvas.recipe.js +28 -0
- package/dist/cjs/product/flowcanvas/index.js +7 -0
- package/dist/cjs/product/inbox/inbox.js +16 -0
- package/dist/cjs/product/inbox/inbox.recipe.js +46 -0
- package/dist/cjs/product/inbox/index.js +7 -0
- package/dist/cjs/product/listpage/index.js +8 -0
- package/dist/cjs/product/listpage/listpage.js +25 -0
- package/dist/cjs/product/listpage/listpage.recipe.js +46 -0
- package/dist/cjs/product/navitem/index.js +5 -0
- package/dist/cjs/product/navitem/navitem.js +44 -0
- package/dist/cjs/product/navitem/navitem.recipe.js +36 -0
- package/dist/cjs/product/onboarding/index.js +7 -0
- package/dist/cjs/product/onboarding/onboarding.js +17 -0
- package/dist/cjs/product/onboarding/onboarding.recipe.js +44 -0
- package/dist/cjs/product/recordpage/index.js +7 -0
- package/dist/cjs/product/recordpage/recordpage.js +409 -0
- package/dist/cjs/product/recordpage/recordpage.recipe.js +162 -0
- package/dist/cjs/product/recordpanel/index.js +7 -0
- package/dist/cjs/product/recordpanel/recordpanel.js +83 -0
- package/dist/cjs/product/recordpanel/recordpanel.recipe.js +51 -0
- package/dist/cjs/product/recordstable/company-logos.js +29 -0
- package/dist/cjs/product/recordstable/index.js +10 -0
- package/dist/cjs/product/recordstable/recordstable.js +1092 -0
- package/dist/cjs/product/recordstable/recordstable.recipe.js +152 -0
- package/dist/cjs/product/recordstable/recordstable.tag.js +21 -0
- package/dist/cjs/product/richtext/index.js +7 -0
- package/dist/cjs/product/richtext/richtext.js +147 -0
- package/dist/cjs/product/richtext/richtext.recipe.js +43 -0
- package/dist/cjs/product/settingssection/index.js +7 -0
- package/dist/cjs/product/settingssection/settingssection.js +14 -0
- package/dist/cjs/product/settingssection/settingssection.recipe.js +23 -0
- package/dist/cjs/product/settingsshell/index.js +7 -0
- package/dist/cjs/product/settingsshell/settingsshell.js +59 -0
- package/dist/cjs/product/settingsshell/settingsshell.recipe.js +46 -0
- package/dist/cjs/product/sidebar/index.js +13 -0
- package/dist/cjs/product/sidebar/sidebar.js +148 -0
- package/dist/cjs/product/sidebar/sidebar.recipe.js +46 -0
- package/dist/cjs/product/tasklist/index.js +7 -0
- package/dist/cjs/product/tasklist/tasklist.js +19 -0
- package/dist/cjs/product/tasklist/tasklist.recipe.js +38 -0
- package/dist/cjs/product/trialbanner/index.js +7 -0
- package/dist/cjs/product/trialbanner/trialbanner.js +13 -0
- package/dist/cjs/product/trialbanner/trialbanner.recipe.js +32 -0
- package/dist/cjs/product/viewtoggle/index.js +7 -0
- package/dist/cjs/product/viewtoggle/viewtoggle.js +55 -0
- package/dist/cjs/product/viewtoggle/viewtoggle.recipe.js +27 -0
- package/dist/cjs/tokens/contract.js +21 -0
- package/dist/cjs/tokens/elevation.js +47 -0
- package/dist/cjs/tokens/index.js +18 -0
- package/dist/cjs/tokens/palette.js +63 -0
- package/dist/cjs/tokens/scale.js +70 -0
- package/dist/cjs/tokens/semantic.js +57 -0
- package/dist/columncalc.css +26 -0
- package/dist/columncalc.d.ts +7 -0
- package/dist/command.css +34 -1
- package/dist/contextmenu.css +11 -3
- package/dist/datatable.css +106 -0
- package/dist/datatable.d.ts +7 -0
- package/dist/datepicker.css +165 -19
- package/dist/datepicker.d.ts +2 -2
- package/dist/datepickerfield.css +27 -0
- package/dist/datepickerfield.d.ts +7 -0
- package/dist/dialog.css +11 -3
- package/dist/donutchart.css +8 -0
- package/dist/drawer.css +13 -5
- package/dist/entitycard.css +10 -2
- package/dist/esm/components/accordion/accordion.js +33 -0
- package/dist/esm/components/accordion/accordion.recipe.js +23 -0
- package/dist/esm/components/accordion/index.js +2 -0
- package/dist/esm/components/avatar/avatar.js +8 -0
- package/dist/esm/components/avatar/avatar.recipe.js +39 -0
- package/dist/esm/components/avatar/index.js +2 -0
- package/dist/esm/components/avatargroup/avatargroup.js +16 -0
- package/dist/esm/components/avatargroup/avatargroup.recipe.js +15 -0
- package/dist/esm/components/avatargroup/index.js +2 -0
- package/dist/esm/components/badge/badge.js +8 -0
- package/dist/esm/components/badge/badge.recipe.js +65 -0
- package/dist/esm/components/badge/index.js +2 -0
- package/dist/esm/components/barchart/barchart.js +42 -0
- package/dist/esm/components/barchart/barchart.recipe.js +31 -0
- package/dist/esm/components/barchart/index.js +2 -0
- package/dist/esm/components/breadcrumb/breadcrumb.js +17 -0
- package/dist/esm/components/breadcrumb/breadcrumb.recipe.js +18 -0
- package/dist/esm/components/breadcrumb/index.js +2 -0
- package/dist/esm/components/bulkbar/bulkbar.js +16 -0
- package/dist/esm/components/bulkbar/bulkbar.recipe.js +28 -0
- package/dist/esm/components/bulkbar/index.js +2 -0
- package/dist/esm/components/button/button.js +26 -0
- package/dist/esm/components/button/button.recipe.js +55 -0
- package/dist/esm/components/button/index.js +2 -0
- package/dist/esm/components/callout/callout.js +22 -0
- package/dist/esm/components/callout/callout.recipe.js +26 -0
- package/dist/esm/components/callout/index.js +2 -0
- package/dist/esm/components/card/card.js +15 -0
- package/dist/esm/components/card/card.recipe.js +28 -0
- package/dist/esm/components/card/index.js +2 -0
- package/dist/esm/components/checkbox/checkbox.js +20 -0
- package/dist/esm/components/checkbox/checkbox.recipe.js +31 -0
- package/dist/esm/components/checkbox/index.js +2 -0
- package/dist/esm/components/columncalc/columncalc.js +100 -0
- package/dist/esm/components/columncalc/columncalc.recipe.js +30 -0
- package/dist/esm/components/columncalc/index.js +1 -0
- package/dist/esm/components/contextmenu/contextmenu.js +42 -0
- package/dist/esm/components/contextmenu/contextmenu.recipe.js +21 -0
- package/dist/esm/components/contextmenu/index.js +2 -0
- package/dist/esm/components/datatable/datatable.js +472 -0
- package/dist/esm/components/datatable/datatable.recipe.js +110 -0
- package/dist/esm/components/datatable/index.js +2 -0
- package/dist/esm/components/datepicker/datepicker.js +108 -0
- package/dist/esm/components/datepicker/datepicker.recipe.js +177 -0
- package/dist/esm/components/datepicker/index.js +2 -0
- package/dist/esm/components/datepickerfield/datepickerfield.js +49 -0
- package/dist/esm/components/datepickerfield/datepickerfield.recipe.js +41 -0
- package/dist/esm/components/datepickerfield/index.js +2 -0
- package/dist/esm/components/dialog/dialog.js +48 -0
- package/dist/esm/components/dialog/dialog.recipe.js +32 -0
- package/dist/esm/components/dialog/index.js +2 -0
- package/dist/esm/components/divider/divider.js +7 -0
- package/dist/esm/components/divider/divider.recipe.js +14 -0
- package/dist/esm/components/divider/index.js +2 -0
- package/dist/esm/components/donutchart/donutchart.js +38 -0
- package/dist/esm/components/donutchart/donutchart.recipe.js +27 -0
- package/dist/esm/components/donutchart/index.js +2 -0
- package/dist/esm/components/drawer/drawer.js +42 -0
- package/dist/esm/components/drawer/drawer.recipe.js +27 -0
- package/dist/esm/components/drawer/index.js +2 -0
- package/dist/esm/components/emptystate/emptystate.js +7 -0
- package/dist/esm/components/emptystate/emptystate.recipe.js +31 -0
- package/dist/esm/components/emptystate/index.js +2 -0
- package/dist/esm/components/formfield/formfield.js +14 -0
- package/dist/esm/components/formfield/formfield.recipe.js +19 -0
- package/dist/esm/components/formfield/index.js +2 -0
- package/dist/esm/components/input/index.js +2 -0
- package/dist/esm/components/input/input.js +11 -0
- package/dist/esm/components/input/input.recipe.js +56 -0
- package/dist/esm/components/kbd/index.js +2 -0
- package/dist/esm/components/kbd/kbd.js +8 -0
- package/dist/esm/components/kbd/kbd.recipe.js +20 -0
- package/dist/esm/components/linechart/index.js +2 -0
- package/dist/esm/components/linechart/linechart.js +54 -0
- package/dist/esm/components/linechart/linechart.recipe.js +32 -0
- package/dist/esm/components/menu/index.js +3 -0
- package/dist/esm/components/menu/menu-keyboard.js +80 -0
- package/dist/esm/components/menu/menu.js +104 -0
- package/dist/esm/components/menu/menu.recipe.js +51 -0
- package/dist/esm/components/multiselect/index.js +2 -0
- package/dist/esm/components/multiselect/multiselect.js +125 -0
- package/dist/esm/components/multiselect/multiselect.recipe.js +68 -0
- package/dist/esm/components/popover/index.js +2 -0
- package/dist/esm/components/popover/popover.js +70 -0
- package/dist/esm/components/popover/popover.recipe.js +18 -0
- package/dist/esm/components/progress/index.js +2 -0
- package/dist/esm/components/progress/progress.js +23 -0
- package/dist/esm/components/progress/progress.recipe.js +17 -0
- package/dist/esm/components/radiogroup/index.js +2 -0
- package/dist/esm/components/radiogroup/radiogroup.js +46 -0
- package/dist/esm/components/radiogroup/radiogroup.recipe.js +37 -0
- package/dist/esm/components/resizablepanel/index.js +2 -0
- package/dist/esm/components/resizablepanel/resizablepanel.js +135 -0
- package/dist/esm/components/resizablepanel/resizablepanel.recipe.js +66 -0
- package/dist/esm/components/segmented/index.js +2 -0
- package/dist/esm/components/segmented/segmented.js +59 -0
- package/dist/esm/components/segmented/segmented.recipe.js +20 -0
- package/dist/esm/components/select/index.js +2 -0
- package/dist/esm/components/select/select.js +144 -0
- package/dist/esm/components/select/select.recipe.js +76 -0
- package/dist/esm/components/skeleton/index.js +2 -0
- package/dist/esm/components/skeleton/skeleton.js +10 -0
- package/dist/esm/components/skeleton/skeleton.recipe.js +14 -0
- package/dist/esm/components/spinner/index.js +2 -0
- package/dist/esm/components/spinner/spinner.js +12 -0
- package/dist/esm/components/spinner/spinner.recipe.js +13 -0
- package/dist/esm/components/stagebar/index.js +2 -0
- package/dist/esm/components/stagebar/stagebar.js +23 -0
- package/dist/esm/components/stagebar/stagebar.recipe.js +14 -0
- package/dist/esm/components/statcard/index.js +2 -0
- package/dist/esm/components/statcard/statcard.js +12 -0
- package/dist/esm/components/statcard/statcard.recipe.js +34 -0
- package/dist/esm/components/switch/index.js +2 -0
- package/dist/esm/components/switch/switch.js +22 -0
- package/dist/esm/components/switch/switch.recipe.js +26 -0
- package/dist/esm/components/table/index.js +2 -0
- package/dist/esm/components/table/table.js +30 -0
- package/dist/esm/components/table/table.recipe.js +40 -0
- package/dist/esm/components/tabletoolbar/index.js +2 -0
- package/dist/esm/components/tabletoolbar/tabletoolbar.chrome.js +79 -0
- package/dist/esm/components/tabletoolbar/tabletoolbar.js +199 -0
- package/dist/esm/components/tabletoolbar/tabletoolbar.recipe.js +23 -0
- package/dist/esm/components/tabs/index.js +2 -0
- package/dist/esm/components/tabs/tabs.js +70 -0
- package/dist/esm/components/tabs/tabs.recipe.js +33 -0
- package/dist/esm/components/textarea/index.js +2 -0
- package/dist/esm/components/textarea/textarea.js +20 -0
- package/dist/esm/components/textarea/textarea.recipe.js +43 -0
- package/dist/esm/components/toast/index.js +2 -0
- package/dist/esm/components/toast/toast.js +58 -0
- package/dist/esm/components/toast/toast.recipe.js +28 -0
- package/dist/esm/components/tooltip/index.js +2 -0
- package/dist/esm/components/tooltip/tooltip.js +59 -0
- package/dist/esm/components/tooltip/tooltip.recipe.js +13 -0
- package/dist/esm/components/wheelpicker/index.js +2 -0
- package/dist/esm/components/wheelpicker/wheelpicker.js +98 -0
- package/dist/esm/components/wheelpicker/wheelpicker.recipe.js +92 -0
- package/dist/esm/core/cx.js +1 -0
- package/dist/esm/core/define.js +3 -0
- package/dist/esm/core/index.js +4 -0
- package/dist/esm/core/primitive.js +13 -0
- package/dist/esm/core/resolve.js +41 -0
- package/dist/esm/core/types.js +1 -0
- package/dist/esm/index.js +76 -0
- package/dist/esm/product/appshell/appshell.js +68 -0
- package/dist/esm/product/appshell/appshell.recipe.js +27 -0
- package/dist/esm/product/appshell/index.js +2 -0
- package/dist/esm/product/authshell/authshell.js +18 -0
- package/dist/esm/product/authshell/authshell.recipe.js +64 -0
- package/dist/esm/product/authshell/index.js +2 -0
- package/dist/esm/product/board/board.js +42 -0
- package/dist/esm/product/board/board.recipe.js +28 -0
- package/dist/esm/product/board/index.js +2 -0
- package/dist/esm/product/command/command.js +201 -0
- package/dist/esm/product/command/command.recipe.js +44 -0
- package/dist/esm/product/command/index.js +2 -0
- package/dist/esm/product/entitycard/entitycard.js +11 -0
- package/dist/esm/product/entitycard/entitycard.recipe.js +29 -0
- package/dist/esm/product/entitycard/index.js +2 -0
- package/dist/esm/product/flowcanvas/flowcanvas.js +85 -0
- package/dist/esm/product/flowcanvas/flowcanvas.recipe.js +25 -0
- package/dist/esm/product/flowcanvas/index.js +2 -0
- package/dist/esm/product/inbox/inbox.js +13 -0
- package/dist/esm/product/inbox/inbox.recipe.js +43 -0
- package/dist/esm/product/inbox/index.js +2 -0
- package/dist/esm/product/listpage/index.js +2 -0
- package/dist/esm/product/listpage/listpage.js +21 -0
- package/dist/esm/product/listpage/listpage.recipe.js +43 -0
- package/dist/esm/product/navitem/index.js +1 -0
- package/dist/esm/product/navitem/navitem.js +8 -0
- package/dist/esm/product/navitem/navitem.recipe.js +33 -0
- package/dist/esm/product/onboarding/index.js +2 -0
- package/dist/esm/product/onboarding/onboarding.js +14 -0
- package/dist/esm/product/onboarding/onboarding.recipe.js +41 -0
- package/dist/esm/product/recordpage/index.js +2 -0
- package/dist/esm/product/recordpage/recordpage.js +372 -0
- package/dist/esm/product/recordpage/recordpage.recipe.js +159 -0
- package/dist/esm/product/recordpanel/index.js +2 -0
- package/dist/esm/product/recordpanel/recordpanel.js +47 -0
- package/dist/esm/product/recordpanel/recordpanel.recipe.js +48 -0
- package/dist/esm/product/recordstable/company-logos.js +26 -0
- package/dist/esm/product/recordstable/index.js +2 -0
- package/dist/esm/product/recordstable/recordstable.js +1054 -0
- package/dist/esm/product/recordstable/recordstable.recipe.js +149 -0
- package/dist/esm/product/recordstable/recordstable.tag.js +18 -0
- package/dist/esm/product/richtext/index.js +2 -0
- package/dist/esm/product/richtext/richtext.js +111 -0
- package/dist/esm/product/richtext/richtext.recipe.js +40 -0
- package/dist/esm/product/settingssection/index.js +2 -0
- package/dist/esm/product/settingssection/settingssection.js +11 -0
- package/dist/esm/product/settingssection/settingssection.recipe.js +20 -0
- package/dist/esm/product/settingsshell/index.js +2 -0
- package/dist/esm/product/settingsshell/settingsshell.js +23 -0
- package/dist/esm/product/settingsshell/settingsshell.recipe.js +43 -0
- package/dist/esm/product/sidebar/index.js +2 -0
- package/dist/esm/product/sidebar/sidebar.js +112 -0
- package/dist/esm/product/sidebar/sidebar.recipe.js +43 -0
- package/dist/esm/product/tasklist/index.js +2 -0
- package/dist/esm/product/tasklist/tasklist.js +16 -0
- package/dist/esm/product/tasklist/tasklist.recipe.js +35 -0
- package/dist/esm/product/trialbanner/index.js +2 -0
- package/dist/esm/product/trialbanner/trialbanner.js +10 -0
- package/dist/esm/product/trialbanner/trialbanner.recipe.js +29 -0
- package/dist/esm/product/viewtoggle/index.js +2 -0
- package/dist/esm/product/viewtoggle/viewtoggle.js +19 -0
- package/dist/esm/product/viewtoggle/viewtoggle.recipe.js +24 -0
- package/dist/esm/tokens/contract.js +18 -0
- package/dist/esm/tokens/elevation.js +44 -0
- package/dist/esm/tokens/index.js +5 -0
- package/dist/esm/tokens/palette.js +60 -0
- package/dist/esm/tokens/scale.js +67 -0
- package/dist/esm/tokens/semantic.js +54 -0
- package/dist/inbox.css +9 -1
- package/dist/input.css +32 -4
- package/dist/linechart.css +5 -5
- package/dist/listpage.css +11 -3
- package/dist/menu.css +13 -5
- package/dist/multiselect.css +17 -9
- package/dist/native/tokens.d.ts +16 -0
- package/dist/native/tokens.js +221 -0
- package/dist/navitem.css +21 -1
- package/dist/onboarding.css +10 -2
- package/dist/popover.css +9 -1
- package/dist/progress.css +8 -0
- package/dist/radiogroup.css +9 -1
- package/dist/recordpage.css +26 -16
- package/dist/recordpanel.css +25 -15
- package/dist/recordstable.css +41 -27
- package/dist/resizablepanel.css +53 -0
- package/dist/resizablepanel.d.ts +7 -0
- package/dist/richtext.css +11 -3
- package/dist/segmented.css +8 -0
- package/dist/select.css +22 -8
- package/dist/select.d.ts +2 -1
- package/dist/settingsshell.css +15 -2
- package/dist/sidebar.css +34 -1
- package/dist/skeleton.css +8 -0
- package/dist/spinner.css +8 -0
- package/dist/stagebar.css +8 -0
- package/dist/statcard.css +6 -0
- package/dist/statcard.d.ts +2 -2
- package/dist/styles.css +2134 -999
- package/dist/switch.css +8 -0
- package/dist/table.css +34 -0
- package/dist/table.d.ts +8 -0
- package/dist/tabletoolbar.css +78 -0
- package/dist/tabletoolbar.d.ts +7 -0
- package/dist/tabs.css +12 -4
- package/dist/tasklist.css +8 -0
- package/dist/textarea.css +23 -3
- package/dist/toast.css +10 -2
- package/dist/tokens.css +53 -0
- package/dist/tokens.json +217 -0
- package/dist/tooltip.css +8 -0
- package/dist/trialbanner.css +8 -0
- package/dist/types/components/accordion/accordion.d.ts +10 -8
- package/dist/types/components/avatargroup/avatargroup.d.ts +2 -2
- package/dist/types/components/barchart/barchart.d.ts +4 -2
- package/dist/types/components/breadcrumb/breadcrumb.d.ts +2 -2
- package/dist/types/components/bulkbar/bulkbar.d.ts +4 -2
- package/dist/types/components/button/button.d.ts +8 -0
- package/dist/types/components/button/button.recipe.d.ts +12 -0
- package/dist/types/components/callout/callout.d.ts +2 -2
- package/dist/types/components/columncalc/columncalc.d.ts +49 -0
- package/dist/types/components/columncalc/columncalc.recipe.d.ts +3 -0
- package/dist/types/components/columncalc/index.d.ts +1 -0
- package/dist/types/components/datatable/datatable.d.ts +153 -0
- package/dist/types/components/datatable/datatable.recipe.d.ts +3 -0
- package/dist/types/components/datatable/index.d.ts +2 -0
- package/dist/types/components/datepicker/datepicker.d.ts +41 -18
- package/dist/types/components/datepicker/datepicker.recipe.d.ts +25 -2
- package/dist/types/components/datepicker/index.d.ts +1 -1
- package/dist/types/components/datepickerfield/datepickerfield.d.ts +35 -0
- package/dist/types/components/datepickerfield/datepickerfield.recipe.d.ts +5 -0
- package/dist/types/components/datepickerfield/index.d.ts +2 -0
- package/dist/types/components/dialog/dialog.d.ts +3 -1
- package/dist/types/components/divider/divider.d.ts +2 -2
- package/dist/types/components/donutchart/donutchart.d.ts +2 -2
- package/dist/types/components/drawer/drawer.d.ts +3 -1
- package/dist/types/components/emptystate/emptystate.d.ts +2 -2
- package/dist/types/components/formfield/formfield.d.ts +7 -4
- package/dist/types/components/input/input.d.ts +8 -2
- package/dist/types/components/linechart/linechart.d.ts +4 -2
- package/dist/types/components/menu/index.d.ts +1 -0
- package/dist/types/components/menu/menu-keyboard.d.ts +33 -0
- package/dist/types/components/menu/menu.d.ts +7 -1
- package/dist/types/components/multiselect/multiselect.d.ts +8 -4
- package/dist/types/components/popover/popover.d.ts +4 -1
- package/dist/types/components/progress/progress.d.ts +2 -2
- package/dist/types/components/radiogroup/radiogroup.d.ts +2 -2
- package/dist/types/components/resizablepanel/index.d.ts +2 -0
- package/dist/types/components/resizablepanel/resizablepanel.d.ts +75 -0
- package/dist/types/components/resizablepanel/resizablepanel.recipe.d.ts +3 -0
- package/dist/types/components/segmented/segmented.d.ts +2 -2
- package/dist/types/components/select/select.d.ts +4 -2
- package/dist/types/components/select/select.recipe.d.ts +12 -0
- package/dist/types/components/skeleton/skeleton.d.ts +2 -2
- package/dist/types/components/spinner/spinner.d.ts +2 -2
- package/dist/types/components/stagebar/stagebar.d.ts +2 -2
- package/dist/types/components/statcard/statcard.d.ts +7 -2
- package/dist/types/components/statcard/statcard.recipe.d.ts +12 -2
- package/dist/types/components/table/index.d.ts +2 -0
- package/dist/types/components/table/table.d.ts +53 -0
- package/dist/types/components/table/table.recipe.d.ts +21 -0
- package/dist/types/components/tabletoolbar/index.d.ts +2 -0
- package/dist/types/components/tabletoolbar/tabletoolbar.chrome.d.ts +2 -0
- package/dist/types/components/tabletoolbar/tabletoolbar.d.ts +89 -0
- package/dist/types/components/tabletoolbar/tabletoolbar.recipe.d.ts +3 -0
- package/dist/types/components/tabs/tabs.d.ts +2 -2
- package/dist/types/components/toast/toast.d.ts +7 -1
- package/dist/types/components/tooltip/tooltip.d.ts +8 -1
- package/dist/types/components/wheelpicker/index.d.ts +2 -0
- package/dist/types/components/wheelpicker/wheelpicker.d.ts +37 -0
- package/dist/types/components/wheelpicker/wheelpicker.recipe.d.ts +26 -0
- package/dist/types/index.d.ts +8 -0
- package/dist/types/product/appshell/appshell.d.ts +16 -2
- package/dist/types/product/authshell/authshell.d.ts +28 -0
- package/dist/types/product/authshell/authshell.recipe.d.ts +3 -0
- package/dist/types/product/authshell/index.d.ts +2 -0
- package/dist/types/product/board/board.d.ts +2 -2
- package/dist/types/product/command/command.d.ts +25 -1
- package/dist/types/product/entitycard/entitycard.d.ts +2 -2
- package/dist/types/product/flowcanvas/flowcanvas.d.ts +9 -1
- package/dist/types/product/onboarding/onboarding.d.ts +7 -1
- package/dist/types/product/recordpage/recordpage.d.ts +65 -1
- package/dist/types/product/recordpanel/recordpanel.d.ts +8 -2
- package/dist/types/product/recordstable/company-logos.d.ts +1 -0
- package/dist/types/product/recordstable/recordstable.d.ts +131 -1
- package/dist/types/product/recordstable/recordstable.tag.d.ts +10 -0
- package/dist/types/product/richtext/richtext.d.ts +5 -1
- package/dist/types/product/settingssection/settingssection.d.ts +2 -2
- package/dist/types/product/settingsshell/settingsshell.d.ts +6 -2
- package/dist/types/product/sidebar/index.d.ts +1 -1
- package/dist/types/product/sidebar/sidebar.d.ts +93 -0
- package/dist/types/product/tasklist/tasklist.d.ts +5 -1
- package/dist/types/product/trialbanner/trialbanner.d.ts +2 -2
- package/dist/types/product/viewtoggle/viewtoggle.d.ts +11 -5
- package/dist/types/tokens/contract.d.ts +26 -3
- package/dist/types/tokens/elevation.d.ts +44 -0
- package/dist/types/tokens/index.d.ts +1 -0
- package/dist/types/tokens/palette.d.ts +88 -0
- package/dist/types/tokens/scale.d.ts +7 -0
- package/dist/types/tokens/semantic.d.ts +31 -0
- package/dist/viewtoggle.css +11 -3
- package/dist/wheelpicker.css +68 -0
- package/dist/wheelpicker.d.ts +7 -0
- package/package.json +351 -9
- 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
|
+
}));
|