@crispy-seed/data-table 0.10.5 → 0.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/data-table-recipe.css +58 -0
- package/dist/data-table-recipe.layered.css +63 -0
- package/dist/index.cjs +725 -359
- package/dist/index.d.ts +82 -15
- package/dist/index.js +725 -360
- package/dist/styles.css +63 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -18,6 +18,9 @@ import { Checkbox, useCheckboxContext } from '@seed-design/react-checkbox';
|
|
|
18
18
|
import { useScaleFeedback } from '@seed-design/react-scale-feedback';
|
|
19
19
|
import { checkboxGroup } from '@seed-design/css/recipes/checkbox-group';
|
|
20
20
|
import { createPortal } from 'react-dom';
|
|
21
|
+
import { Menu, useMenuContext, useMenuItemContext } from '@seed-design/react-menu';
|
|
22
|
+
import { menu } from '@seed-design/css/recipes/menu';
|
|
23
|
+
import { menuItem } from '@seed-design/css/recipes/menu-item';
|
|
21
24
|
import { Image } from '@seed-design/react-image';
|
|
22
25
|
import { avatar } from '@seed-design/css/recipes/avatar';
|
|
23
26
|
import '@seed-design/css/recipes/avatar-stack';
|
|
@@ -32,9 +35,6 @@ import { switchStyle } from '@seed-design/css/recipes/switch';
|
|
|
32
35
|
import { switchmark } from '@seed-design/css/recipes/switchmark';
|
|
33
36
|
import { TextField, useTextFieldContext } from '@seed-design/react-text-field';
|
|
34
37
|
import { textInput } from '@seed-design/css/recipes/text-input';
|
|
35
|
-
import { Menu, useMenuContext, useMenuItemContext } from '@seed-design/react-menu';
|
|
36
|
-
import { menu } from '@seed-design/css/recipes/menu';
|
|
37
|
-
import { menuItem } from '@seed-design/css/recipes/menu-item';
|
|
38
38
|
import { tabs } from '@seed-design/css/recipes/tabs';
|
|
39
39
|
|
|
40
40
|
// src/IconArrowUpLine.tsx
|
|
@@ -761,7 +761,10 @@ function isFunction$1(value) {
|
|
|
761
761
|
const DATA_TABLE_UNIVERSAL_CALC_OPS = [
|
|
762
762
|
"count",
|
|
763
763
|
"empty",
|
|
764
|
-
"notEmpty"
|
|
764
|
+
"notEmpty",
|
|
765
|
+
"percentEmpty",
|
|
766
|
+
"percentFilled",
|
|
767
|
+
"none"
|
|
765
768
|
];
|
|
766
769
|
const DATA_TABLE_NUMERIC_CALC_OPS = [
|
|
767
770
|
"sum",
|
|
@@ -776,8 +779,11 @@ const DATA_TABLE_NUMERIC_CALC_OPS = [
|
|
|
776
779
|
];
|
|
777
780
|
const DATA_TABLE_CALC_LABEL = {
|
|
778
781
|
count: "Count",
|
|
779
|
-
empty: "
|
|
780
|
-
notEmpty: "
|
|
782
|
+
empty: "Count empty",
|
|
783
|
+
notEmpty: "Count filled",
|
|
784
|
+
percentEmpty: "Percent empty",
|
|
785
|
+
percentFilled: "Percent filled",
|
|
786
|
+
none: "None",
|
|
781
787
|
sum: "Sum",
|
|
782
788
|
average: "Average",
|
|
783
789
|
median: "Median",
|
|
@@ -807,7 +813,12 @@ const DATA_TABLE_CALC_LABEL = {
|
|
|
807
813
|
* (`JSON.stringify`-normalized, so two arrays/objects with the same shape count
|
|
808
814
|
* once) -- empties are already their own op (`empty`), so folding them into "one
|
|
809
815
|
* more unique value" here would double-count the same information two different
|
|
810
|
-
* ways in the same menu.
|
|
816
|
+
* ways in the same menu. `percentEmpty`/`percentFilled` are `empty`/`notEmpty` as a
|
|
817
|
+
* share of the row count (0-100, unrounded -- formatting rounds it for display) --
|
|
818
|
+
* `null` on zero rows, since 0/0 has no meaningful percentage (not 0%, not 100%).
|
|
819
|
+
* `none` always renders nothing: it is Attio's own "no calculation" menu entry, not a
|
|
820
|
+
* separate always-appended "Clear" item, and picking it is a real, storable op so it
|
|
821
|
+
* overrides a `summaryDefaultOp` instead of falling back to it.
|
|
811
822
|
*/ function computeColumnCalc(values, op) {
|
|
812
823
|
switch(op){
|
|
813
824
|
case "count":
|
|
@@ -816,6 +827,14 @@ const DATA_TABLE_CALC_LABEL = {
|
|
|
816
827
|
return values.filter(isEmptyValueForCalc).length;
|
|
817
828
|
case "notEmpty":
|
|
818
829
|
return values.filter((value)=>!isEmptyValueForCalc(value)).length;
|
|
830
|
+
case "percentEmpty":
|
|
831
|
+
if (values.length === 0) return null;
|
|
832
|
+
return values.filter(isEmptyValueForCalc).length / values.length * 100;
|
|
833
|
+
case "percentFilled":
|
|
834
|
+
if (values.length === 0) return null;
|
|
835
|
+
return values.filter((value)=>!isEmptyValueForCalc(value)).length / values.length * 100;
|
|
836
|
+
case "none":
|
|
837
|
+
return null;
|
|
819
838
|
case "countUnique":
|
|
820
839
|
{
|
|
821
840
|
const seen = new Set(values.filter((value)=>!isEmptyValueForCalc(value)).map((value)=>JSON.stringify(value)));
|
|
@@ -949,15 +968,33 @@ function sameCell$1(a, id, key) {
|
|
|
949
968
|
* first captured) nor keeps a ghost entry for one that was removed. Known keys keep
|
|
950
969
|
* their position from `order`; any key in `keys` missing from `order` is appended, in
|
|
951
970
|
* `keys`' own order; any entry in `order` no longer in `keys` is dropped.
|
|
952
|
-
|
|
971
|
+
*
|
|
972
|
+
* `actionsKey` (the styled layer's `actions`-flagged column, e.g. the row-menu `⋯`
|
|
973
|
+
* column) is kept LAST no matter what -- without this, a brand-new column added to
|
|
974
|
+
* `columns` after the table first mounted got appended past it (the "any key in `keys`
|
|
975
|
+
* missing from `order` is appended" rule above has no notion of "past the end" vs. "at
|
|
976
|
+
* the very end"), stranding it in the middle of the table since `stickyLast`/`actions`
|
|
977
|
+
* only sticks the actual trailing column. A real consumer hit this: their newly-added
|
|
978
|
+
* column landed to the right of the already-pinned-sticky `actions` column, covered by
|
|
979
|
+
* it under horizontal scroll. Column-layout's own `pinnedCellProps` independently
|
|
980
|
+
* targets the `actions` column by FLAG rather than by position (belt and suspenders --
|
|
981
|
+
* see that file's own doc comment), but this still keeps the VISUAL order sane so nothing
|
|
982
|
+
* renders hidden behind the sticky cell in the first place.
|
|
983
|
+
*/ function reconcileColumnOrder(order, keys, actionsKey) {
|
|
953
984
|
const keySet = new Set(keys);
|
|
954
985
|
const known = order.filter((key)=>keySet.has(key));
|
|
955
986
|
const knownSet = new Set(known);
|
|
956
987
|
const added = keys.filter((key)=>!knownSet.has(key));
|
|
957
|
-
|
|
988
|
+
const merged = [
|
|
958
989
|
...known,
|
|
959
990
|
...added
|
|
960
991
|
];
|
|
992
|
+
if (actionsKey === undefined || !keySet.has(actionsKey)) return merged;
|
|
993
|
+
const withoutActions = merged.filter((key)=>key !== actionsKey);
|
|
994
|
+
return [
|
|
995
|
+
...withoutActions,
|
|
996
|
+
actionsKey
|
|
997
|
+
];
|
|
961
998
|
}
|
|
962
999
|
/**
|
|
963
1000
|
* Moves `key` one step toward `direction` in `order` -- a no-op (same array, well,
|
|
@@ -1136,7 +1173,7 @@ const EMPTY_RECORD = {};
|
|
|
1136
1173
|
* state this hook already owns, not independent state
|
|
1137
1174
|
* of their own.
|
|
1138
1175
|
*/ function useDataTableColumns(props) {
|
|
1139
|
-
const { columnKeys = EMPTY_STRINGS, columnOrder: columnOrderProp, defaultColumnOrder, onColumnOrderChange, hiddenColumnKeys: hiddenProp, defaultHiddenColumnKeys = EMPTY_STRINGS, onHiddenColumnKeysChange, columnWidths: widthsProp, defaultColumnWidths = EMPTY_RECORD, onColumnWidthsChange, columnLabels: labelsProp, defaultColumnLabels = EMPTY_RECORD, onColumnLabelsChange, pinnedColumnKeys: pinnedProp, defaultPinnedColumnKeys = EMPTY_STRINGS, onPinnedColumnKeysChange } = props;
|
|
1176
|
+
const { columnKeys = EMPTY_STRINGS, actionsColumnKey, columnOrder: columnOrderProp, defaultColumnOrder, onColumnOrderChange, hiddenColumnKeys: hiddenProp, defaultHiddenColumnKeys = EMPTY_STRINGS, onHiddenColumnKeysChange, columnWidths: widthsProp, defaultColumnWidths = EMPTY_RECORD, onColumnWidthsChange, columnLabels: labelsProp, defaultColumnLabels = EMPTY_RECORD, onColumnLabelsChange, pinnedColumnKeys: pinnedProp, defaultPinnedColumnKeys = EMPTY_STRINGS, onPinnedColumnKeysChange } = props;
|
|
1140
1177
|
const [rawOrder, setRawOrder] = useControllableState$1({
|
|
1141
1178
|
prop: columnOrderProp,
|
|
1142
1179
|
defaultProp: defaultColumnOrder ?? columnKeys,
|
|
@@ -1147,9 +1184,10 @@ const EMPTY_RECORD = {};
|
|
|
1147
1184
|
// consumer persisted before a new column existed must still show that new column
|
|
1148
1185
|
// (appended), and one holding a column that got removed from `columns` must not
|
|
1149
1186
|
// render a ghost header for it.
|
|
1150
|
-
const columnOrder = useMemo(()=>reconcileColumnOrder(rawOrder ?? columnKeys, columnKeys), [
|
|
1187
|
+
const columnOrder = useMemo(()=>reconcileColumnOrder(rawOrder ?? columnKeys, columnKeys, actionsColumnKey), [
|
|
1151
1188
|
rawOrder,
|
|
1152
|
-
columnKeys
|
|
1189
|
+
columnKeys,
|
|
1190
|
+
actionsColumnKey
|
|
1153
1191
|
]);
|
|
1154
1192
|
const [hiddenColumnKeys = EMPTY_STRINGS, setHiddenColumnKeys] = useControllableState$1({
|
|
1155
1193
|
prop: hiddenProp,
|
|
@@ -1634,6 +1672,17 @@ function useDataTableSelection(props) {
|
|
|
1634
1672
|
rowIds,
|
|
1635
1673
|
setSelectedIds
|
|
1636
1674
|
]);
|
|
1675
|
+
// Bulk-action bar's own "×" (gap #4, DATATABLE-ATTIO-SPEC's Attio review) --
|
|
1676
|
+
// deliberately its own function rather than reusing `toggleAll`: `toggleAll` only
|
|
1677
|
+
// clears from an "all" selection, and otherwise SELECTS every row (`selectionState
|
|
1678
|
+
// === "some"` -> `rowIds`, above) -- exactly the wrong direction for a "clear
|
|
1679
|
+
// selection" affordance shown while `selectionState` is "some". This always empties
|
|
1680
|
+
// the set, regardless of `selectionState`.
|
|
1681
|
+
const clearSelection = useCallback(()=>{
|
|
1682
|
+
setSelectedIds([]);
|
|
1683
|
+
}, [
|
|
1684
|
+
setSelectedIds
|
|
1685
|
+
]);
|
|
1637
1686
|
const getRowProps = useCallback((rowId)=>{
|
|
1638
1687
|
const isRowSelected = isSelected(rowId);
|
|
1639
1688
|
return elementProps({
|
|
@@ -1652,6 +1701,7 @@ function useDataTableSelection(props) {
|
|
|
1652
1701
|
isSelected,
|
|
1653
1702
|
toggleRow,
|
|
1654
1703
|
toggleAll,
|
|
1704
|
+
clearSelection,
|
|
1655
1705
|
getRowProps
|
|
1656
1706
|
};
|
|
1657
1707
|
}
|
|
@@ -2119,7 +2169,7 @@ function useDataTableRowContext({ strict = true } = {}) {
|
|
|
2119
2169
|
return context;
|
|
2120
2170
|
}
|
|
2121
2171
|
const DataTableRoot = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
2122
|
-
const { sort, defaultSort, onSortChange, rowIds, selectable, selectedIds, defaultSelectedIds, onSelectionChange, query, defaultQuery, onQueryChange, toolbarSorts, defaultToolbarSorts, onToolbarSortsChange, toolbarFilters, defaultToolbarFilters, onToolbarFiltersChange, columnKeys, columnOrder, defaultColumnOrder, onColumnOrderChange, hiddenColumnKeys, defaultHiddenColumnKeys, onHiddenColumnKeysChange, columnWidths, defaultColumnWidths, onColumnWidthsChange, columnLabels, defaultColumnLabels, onColumnLabelsChange, pinnedColumnKeys, defaultPinnedColumnKeys, onPinnedColumnKeysChange, columnCalcOps, defaultColumnCalcOps, onColumnCalcOpsChange, onEdit, onLoadMore, hasMore, loadingMore, ...otherProps } = props;
|
|
2172
|
+
const { sort, defaultSort, onSortChange, rowIds, selectable, selectedIds, defaultSelectedIds, onSelectionChange, query, defaultQuery, onQueryChange, toolbarSorts, defaultToolbarSorts, onToolbarSortsChange, toolbarFilters, defaultToolbarFilters, onToolbarFiltersChange, columnKeys, actionsColumnKey, columnOrder, defaultColumnOrder, onColumnOrderChange, hiddenColumnKeys, defaultHiddenColumnKeys, onHiddenColumnKeysChange, columnWidths, defaultColumnWidths, onColumnWidthsChange, columnLabels, defaultColumnLabels, onColumnLabelsChange, pinnedColumnKeys, defaultPinnedColumnKeys, onPinnedColumnKeysChange, columnCalcOps, defaultColumnCalcOps, onColumnCalcOpsChange, onEdit, onLoadMore, hasMore, loadingMore, ...otherProps } = props;
|
|
2123
2173
|
const api = useDataTable({
|
|
2124
2174
|
sort,
|
|
2125
2175
|
defaultSort,
|
|
@@ -2139,6 +2189,7 @@ const DataTableRoot = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
|
2139
2189
|
defaultToolbarFilters,
|
|
2140
2190
|
onToolbarFiltersChange,
|
|
2141
2191
|
columnKeys,
|
|
2192
|
+
actionsColumnKey,
|
|
2142
2193
|
columnOrder,
|
|
2143
2194
|
defaultColumnOrder,
|
|
2144
2195
|
onColumnOrderChange,
|
|
@@ -2660,11 +2711,70 @@ CheckboxIndicator.displayName = "CheckboxIndicator";
|
|
|
2660
2711
|
const CheckboxLabel = withContext$6(withStateProps$4(Primitive.span), "label");
|
|
2661
2712
|
const CheckboxHiddenInput = Checkbox.HiddenInput;
|
|
2662
2713
|
|
|
2663
|
-
const {
|
|
2714
|
+
const { ClassNamesProvider: ClassNamesProvider$2, withContext: withContext$5, useClassNames: useClassNames$2 } = createSlotRecipeContext(menu);
|
|
2715
|
+
const { PropsProvider: ItemPropsProvider$1, useProps: useItemProps$1, withContext: withItemContext$1, ClassNamesProvider: ItemClassNamesProvider$1 } = createSlotRecipeContext(menuItem);
|
|
2716
|
+
const withStateProps$3 = createWithStateProps([
|
|
2717
|
+
useMenuContext
|
|
2718
|
+
]);
|
|
2719
|
+
const withItemStateProps$1 = createWithStateProps([
|
|
2720
|
+
useMenuItemContext
|
|
2721
|
+
]);
|
|
2722
|
+
const MenuRoot = (props)=>{
|
|
2723
|
+
const [variantProps, otherProps] = menu.splitVariantProps(props);
|
|
2724
|
+
const classNames = menu(variantProps);
|
|
2725
|
+
return /*#__PURE__*/ jsx(ClassNamesProvider$2, {
|
|
2726
|
+
value: classNames,
|
|
2727
|
+
children: /*#__PURE__*/ jsx(ItemPropsProvider$1, {
|
|
2728
|
+
value: {
|
|
2729
|
+
size: variantProps.size
|
|
2730
|
+
},
|
|
2731
|
+
children: /*#__PURE__*/ jsx(Menu.Root, {
|
|
2732
|
+
...otherProps
|
|
2733
|
+
})
|
|
2734
|
+
})
|
|
2735
|
+
});
|
|
2736
|
+
};
|
|
2737
|
+
const MenuAnchor = Menu.Anchor;
|
|
2738
|
+
const MenuTrigger = Menu.Trigger;
|
|
2739
|
+
const MenuPositioner = /*#__PURE__*/ React.forwardRef(({ className, ...props }, ref)=>{
|
|
2740
|
+
const classNames = useClassNames$2();
|
|
2741
|
+
return /*#__PURE__*/ jsx(Menu.Positioner, {
|
|
2742
|
+
ref: ref,
|
|
2743
|
+
className: clsx(classNames.positioner, className),
|
|
2744
|
+
...props
|
|
2745
|
+
});
|
|
2746
|
+
});
|
|
2747
|
+
MenuPositioner.displayName = "MenuPositioner";
|
|
2748
|
+
const MenuContent = withContext$5(Menu.Content, "content");
|
|
2749
|
+
const MenuScrollArea = withContext$5(withStateProps$3(Primitive.div), "scrollArea");
|
|
2750
|
+
withContext$5(Menu.Group, "group");
|
|
2751
|
+
withContext$5(Menu.GroupLabel, "groupLabel");
|
|
2752
|
+
const MenuItem = /*#__PURE__*/ React.forwardRef(({ className: propClassName, ...props }, ref)=>{
|
|
2753
|
+
const [variantProps, otherProps] = menuItem.splitVariantProps(props);
|
|
2754
|
+
const parentProps = useItemProps$1();
|
|
2755
|
+
const classNames = menuItem({
|
|
2756
|
+
...parentProps,
|
|
2757
|
+
...variantProps
|
|
2758
|
+
});
|
|
2759
|
+
return /*#__PURE__*/ jsx(ItemClassNamesProvider$1, {
|
|
2760
|
+
value: classNames,
|
|
2761
|
+
children: /*#__PURE__*/ jsx(Menu.Item, {
|
|
2762
|
+
ref: ref,
|
|
2763
|
+
className: clsx(classNames.root, propClassName),
|
|
2764
|
+
...otherProps
|
|
2765
|
+
})
|
|
2766
|
+
});
|
|
2767
|
+
});
|
|
2768
|
+
MenuItem.displayName = "MenuItem";
|
|
2769
|
+
withItemContext$1(withItemStateProps$1(Primitive.div), "body");
|
|
2770
|
+
const MenuItemLabel = withItemContext$1(withItemStateProps$1(Primitive.span), "label");
|
|
2771
|
+
withItemContext$1(withItemStateProps$1(Primitive.span), "description");
|
|
2772
|
+
|
|
2773
|
+
const { withProvider: withProvider$2, withContext: withContext$4 } = createSlotRecipeContext(avatar);
|
|
2664
2774
|
const AvatarRoot = withProvider$2(Image.Root, "root");
|
|
2665
|
-
const AvatarImage = withContext$
|
|
2666
|
-
const AvatarFallback = withContext$
|
|
2667
|
-
withContext$
|
|
2775
|
+
const AvatarImage = withContext$4(Image.Content, "image");
|
|
2776
|
+
const AvatarFallback = withContext$4(Image.Fallback, "fallback");
|
|
2777
|
+
withContext$4(Primitive.div, "badge");
|
|
2668
2778
|
|
|
2669
2779
|
const Badge = /*#__PURE__*/ forwardRef(({ className, children, ...props }, ref)=>{
|
|
2670
2780
|
const [variantProps, restProps] = badge.splitVariantProps(props);
|
|
@@ -3208,6 +3318,141 @@ function RatingCell({ value, maxRating = 5 }) {
|
|
|
3208
3318
|
});
|
|
3209
3319
|
}
|
|
3210
3320
|
|
|
3321
|
+
/** Clear is withheld for `chip-list`: that column already owns its own per-tag remove
|
|
3322
|
+
* affordance (`ChipListEditor`) and commits a JSON-STRINGIFIED array through `onEdit`
|
|
3323
|
+
* (`DataTable.chips.test.tsx`'s own `TagsHarness` -- and this package's own Storybook
|
|
3324
|
+
* demo, `CellFeaturesDemo`, does exactly `JSON.parse(value)` on commit) -- committing
|
|
3325
|
+
* a bare `""` through that same path throws in the consumer's `onEdit`, it doesn't
|
|
3326
|
+
* clear anything. Every OTHER editable shape's commit value is a plain string
|
|
3327
|
+
* (`"true"`/`"false"`, a stringified rating, an option's own `value`, free text) that
|
|
3328
|
+
* a consumer's `onEdit` already has to accept a possibly-empty version of. */ function isClearableEditableCell(column) {
|
|
3329
|
+
return !!column.editable && column.type !== "chip-list";
|
|
3330
|
+
}
|
|
3331
|
+
/** Paste is narrower still -- restricted to a PLAIN `editable` column (no `editor`,
|
|
3332
|
+
* and not one of the always-live widget types) whose committed value IS the raw
|
|
3333
|
+
* text/number a user typed. `select`/`reference`/`actor` expect one of THEIR OWN
|
|
3334
|
+
* option values (not arbitrary clipboard text), `switch`/`rating`/`checkbox` are
|
|
3335
|
+
* always-live widgets with their own encoding, and `chip-list` is excluded for the
|
|
3336
|
+
* same JSON reason `isClearableEditableCell` documents above. This is the task's own
|
|
3337
|
+
* "only for simple editable text/number cells" guard -- committing pasted text
|
|
3338
|
+
* through any of the excluded shapes would corrupt the row rather than edit it
|
|
3339
|
+
* (crispy-seed's own #1094 lesson: never assume an editor's commit shape).
|
|
3340
|
+
*/ function isSimpleEditableCell(column) {
|
|
3341
|
+
if (!column.editable) return false;
|
|
3342
|
+
if (column.editor) return false;
|
|
3343
|
+
if (column.type === "chip-list" || column.type === "checkbox" || column.type === "rating") {
|
|
3344
|
+
return false;
|
|
3345
|
+
}
|
|
3346
|
+
return true;
|
|
3347
|
+
}
|
|
3348
|
+
/** Copy's own text representation -- deliberately mirrors `DisplayCellContent`'s
|
|
3349
|
+
* (body-cells.tsx) switch closely enough to read the same as what's on screen, but
|
|
3350
|
+
* returns a plain string (`navigator.clipboard.writeText`) rather than JSX. Not
|
|
3351
|
+
* exported -- it exists only to feed Copy, not as a general-purpose formatter. */ function cellValueAsText(column, value, locale) {
|
|
3352
|
+
if (value == null) return "";
|
|
3353
|
+
switch(column.type){
|
|
3354
|
+
case "entity":
|
|
3355
|
+
if (typeof value === "object") return String(value.name ?? "");
|
|
3356
|
+
return String(value);
|
|
3357
|
+
case "tags":
|
|
3358
|
+
case "chip-list":
|
|
3359
|
+
return Array.isArray(value) ? value.join(", ") : String(value);
|
|
3360
|
+
case "number":
|
|
3361
|
+
return typeof value === "number" ? String(formatNumberCell(value, locale) ?? "") : String(value);
|
|
3362
|
+
case "currency":
|
|
3363
|
+
return typeof value === "number" ? String(formatCurrencyCell(value, column.symbol ?? "$", locale) ?? "") : String(value);
|
|
3364
|
+
case "date":
|
|
3365
|
+
return String(formatDateCell(value, locale) ?? "");
|
|
3366
|
+
case "datetime":
|
|
3367
|
+
return String(formatDateTimeCell(value, locale) ?? "");
|
|
3368
|
+
case "checkbox":
|
|
3369
|
+
return value === true || value === "true" ? "true" : "false";
|
|
3370
|
+
default:
|
|
3371
|
+
return String(value);
|
|
3372
|
+
}
|
|
3373
|
+
}
|
|
3374
|
+
/**
|
|
3375
|
+
* One cell's right-click menu -- mounted by `body-cells.tsx`'s `DataTableBodyCell`
|
|
3376
|
+
* only while actually open (the same "don't keep an unused floating-ui instance alive
|
|
3377
|
+
* per cell" reasoning as that file's own portaled chip-list popover), so this never
|
|
3378
|
+
* costs anything for the row×column cells a user never right-clicks.
|
|
3379
|
+
*/ function CellContextMenu({ column, row, value, locale, point, open, onOpenChange, onCommit, onViewEditHistory, cellContextMenuItems }) {
|
|
3380
|
+
const clearable = isClearableEditableCell(column);
|
|
3381
|
+
const pasteable = isSimpleEditableCell(column);
|
|
3382
|
+
return /*#__PURE__*/ jsxs(MenuRoot, {
|
|
3383
|
+
open: open,
|
|
3384
|
+
onOpenChange: onOpenChange,
|
|
3385
|
+
placement: "bottom-start",
|
|
3386
|
+
strategy: "fixed",
|
|
3387
|
+
gutter: 2,
|
|
3388
|
+
children: [
|
|
3389
|
+
/*#__PURE__*/ jsx(MenuAnchor, {
|
|
3390
|
+
"aria-hidden": true,
|
|
3391
|
+
style: {
|
|
3392
|
+
position: "fixed",
|
|
3393
|
+
top: point.y,
|
|
3394
|
+
left: point.x,
|
|
3395
|
+
width: 0,
|
|
3396
|
+
height: 0,
|
|
3397
|
+
pointerEvents: "none"
|
|
3398
|
+
}
|
|
3399
|
+
}),
|
|
3400
|
+
/*#__PURE__*/ jsx(MenuPositioner, {
|
|
3401
|
+
children: /*#__PURE__*/ jsx(MenuContent, {
|
|
3402
|
+
style: {
|
|
3403
|
+
minWidth: 180
|
|
3404
|
+
},
|
|
3405
|
+
children: /*#__PURE__*/ jsxs(MenuScrollArea, {
|
|
3406
|
+
children: [
|
|
3407
|
+
/*#__PURE__*/ jsx(MenuItem, {
|
|
3408
|
+
onClick: ()=>{
|
|
3409
|
+
const text = cellValueAsText(column, value, locale);
|
|
3410
|
+
// Best-effort: an insecure context or a denied permission rejects
|
|
3411
|
+
// this silently (no `MenuItem` state depends on the outcome) --
|
|
3412
|
+
// there's nothing else for a plain "Copy" item to do about a
|
|
3413
|
+
// browser that refused the write.
|
|
3414
|
+
void navigator.clipboard?.writeText?.(text);
|
|
3415
|
+
},
|
|
3416
|
+
children: /*#__PURE__*/ jsx(MenuItemLabel, {
|
|
3417
|
+
children: "Copy"
|
|
3418
|
+
})
|
|
3419
|
+
}),
|
|
3420
|
+
pasteable && /*#__PURE__*/ jsx(MenuItem, {
|
|
3421
|
+
onClick: ()=>{
|
|
3422
|
+
void navigator.clipboard?.readText?.().then((text)=>onCommit(text)).catch(()=>{
|
|
3423
|
+
// Clipboard read denied/unavailable -- best-effort, same
|
|
3424
|
+
// reasoning as Copy above.
|
|
3425
|
+
});
|
|
3426
|
+
},
|
|
3427
|
+
children: /*#__PURE__*/ jsx(MenuItemLabel, {
|
|
3428
|
+
children: "Paste"
|
|
3429
|
+
})
|
|
3430
|
+
}),
|
|
3431
|
+
clearable && /*#__PURE__*/ jsx(MenuItem, {
|
|
3432
|
+
onClick: ()=>onCommit(""),
|
|
3433
|
+
children: /*#__PURE__*/ jsx(MenuItemLabel, {
|
|
3434
|
+
children: "Clear value"
|
|
3435
|
+
})
|
|
3436
|
+
}),
|
|
3437
|
+
onViewEditHistory && /*#__PURE__*/ jsx(MenuItem, {
|
|
3438
|
+
onClick: ()=>onViewEditHistory(row, column.key),
|
|
3439
|
+
children: /*#__PURE__*/ jsx(MenuItemLabel, {
|
|
3440
|
+
children: "View edit history"
|
|
3441
|
+
})
|
|
3442
|
+
}),
|
|
3443
|
+
cellContextMenuItems?.({
|
|
3444
|
+
row,
|
|
3445
|
+
column,
|
|
3446
|
+
value
|
|
3447
|
+
})
|
|
3448
|
+
]
|
|
3449
|
+
})
|
|
3450
|
+
})
|
|
3451
|
+
})
|
|
3452
|
+
]
|
|
3453
|
+
});
|
|
3454
|
+
}
|
|
3455
|
+
|
|
3211
3456
|
/** Fallback width (px) used to compute a pinned column's cumulative sticky offset when
|
|
3212
3457
|
* it has no explicit `width` and hasn't been drag-resized -- pinning needs a STATIC
|
|
3213
3458
|
* number to put in an inline `insetInlineStart`, and `table-layout: fixed` only
|
|
@@ -3289,10 +3534,25 @@ function RatingCell({ value, maxRating = 5 }) {
|
|
|
3289
3534
|
width,
|
|
3290
3535
|
pinned,
|
|
3291
3536
|
pinOffset,
|
|
3292
|
-
pinnedEdge: false
|
|
3537
|
+
pinnedEdge: false,
|
|
3538
|
+
stickyLastTarget: false
|
|
3293
3539
|
};
|
|
3294
3540
|
});
|
|
3295
3541
|
if (pinnedCount > 0) entries[pinnedCount - 1].pinnedEdge = true;
|
|
3542
|
+
// `stickyLast` sticks the `actions`-flagged column specifically -- not whichever
|
|
3543
|
+
// column merely landed last in render order. Without this, `reconcileColumnOrder`
|
|
3544
|
+
// appending a brand-new column past `actions` (this file's own `<colgroup>` doc
|
|
3545
|
+
// comment on why order must stay in lockstep) silently moved the sticky-right
|
|
3546
|
+
// treatment onto that new column instead, stranding `actions` in the middle of the
|
|
3547
|
+
// table -- a real consumer hit this. Falls back to the genuine last entry when no
|
|
3548
|
+
// column is flagged `actions` at all, matching `stickyLast`'s original single-
|
|
3549
|
+
// column behavior.
|
|
3550
|
+
if (entries.length > 0) {
|
|
3551
|
+
const actionsColumn = columns.find((column)=>column.actions);
|
|
3552
|
+
const actionsIndex = actionsColumn ? entries.findIndex((entry)=>entry.column.key === actionsColumn.key) : -1;
|
|
3553
|
+
const stickyLastIndex = actionsIndex !== -1 ? actionsIndex : entries.length - 1;
|
|
3554
|
+
entries[stickyLastIndex].stickyLastTarget = true;
|
|
3555
|
+
}
|
|
3296
3556
|
return entries;
|
|
3297
3557
|
}, [
|
|
3298
3558
|
columns,
|
|
@@ -3332,9 +3592,12 @@ function RatingCell({ value, maxRating = 5 }) {
|
|
|
3332
3592
|
}
|
|
3333
3593
|
/** Sticky positioning for one DATA cell (header, body, or ColumnCalc footer) -- pinned-
|
|
3334
3594
|
* left (this column's own `pinOffset`, `data-sticky-edge` only on the group's
|
|
3335
|
-
* trailing-most member) takes priority over sticky-right (`stickyLast`,
|
|
3336
|
-
*
|
|
3337
|
-
* one edge.
|
|
3595
|
+
* trailing-most member) takes priority over sticky-right (`stickyLast`, the
|
|
3596
|
+
* `actions`-flagged column when one exists, else the genuine last VISIBLE column)
|
|
3597
|
+
* since a column can only stick to one edge. Reads `entry.stickyLastTarget`
|
|
3598
|
+
* (`useEffectiveColumns`) rather than an `isLast` index the caller computes itself --
|
|
3599
|
+
* that index is just "last in render order", which is exactly what stranded a
|
|
3600
|
+
* reordered-past `actions` column in the first place. */ function pinnedCellProps(entry, stickyLast) {
|
|
3338
3601
|
if (entry.pinned) {
|
|
3339
3602
|
return {
|
|
3340
3603
|
"data-sticky": "",
|
|
@@ -3346,7 +3609,7 @@ function RatingCell({ value, maxRating = 5 }) {
|
|
|
3346
3609
|
}
|
|
3347
3610
|
};
|
|
3348
3611
|
}
|
|
3349
|
-
if (stickyLast &&
|
|
3612
|
+
if (stickyLast && entry.stickyLastTarget) {
|
|
3350
3613
|
return {
|
|
3351
3614
|
"data-sticky": "",
|
|
3352
3615
|
"data-sticky-edge-end": "",
|
|
@@ -3366,11 +3629,11 @@ function RatingCell({ value, maxRating = 5 }) {
|
|
|
3366
3629
|
// recipe, so a per-instance variant can override the root's default.
|
|
3367
3630
|
const { ClassNamesProvider: TriggerClassNamesProvider, PropsProvider: TriggerPropsProvider, useProps: useTriggerProps, withContext: withTriggerContext, useClassNames: useTriggerClassNames } = createSlotRecipeContext(selectTrigger);
|
|
3368
3631
|
const { ClassNamesProvider: ContentClassNamesProvider, withContext: withContentContext, useClassNames: useContentClassNames } = createSlotRecipeContext(select);
|
|
3369
|
-
const { ClassNamesProvider: ItemClassNamesProvider
|
|
3370
|
-
const withStateProps$
|
|
3632
|
+
const { ClassNamesProvider: ItemClassNamesProvider, PropsProvider: ItemPropsProvider, useProps: useItemProps, withContext: withItemContext, useClassNames: useItemClassNames } = createSlotRecipeContext(selectItem);
|
|
3633
|
+
const withStateProps$2 = createWithStateProps([
|
|
3371
3634
|
useSelectContext
|
|
3372
3635
|
]);
|
|
3373
|
-
const withItemStateProps
|
|
3636
|
+
const withItemStateProps = createWithStateProps([
|
|
3374
3637
|
useSelectItemContext
|
|
3375
3638
|
]);
|
|
3376
3639
|
// NOTE: `gutter` and `overflowPadding` are specified in Rootage (`select.yaml`,
|
|
@@ -3391,7 +3654,7 @@ const SelectRoot = (props)=>{
|
|
|
3391
3654
|
value: contentClassNames,
|
|
3392
3655
|
children: /*#__PURE__*/ jsx(TriggerPropsProvider, {
|
|
3393
3656
|
value: triggerVariantProps,
|
|
3394
|
-
children: /*#__PURE__*/ jsx(ItemPropsProvider
|
|
3657
|
+
children: /*#__PURE__*/ jsx(ItemPropsProvider, {
|
|
3395
3658
|
value: itemVariantProps,
|
|
3396
3659
|
children: /*#__PURE__*/ jsx(Select.Root, {
|
|
3397
3660
|
...otherProps
|
|
@@ -3431,8 +3694,8 @@ const SelectTrigger = /*#__PURE__*/ React.forwardRef(({ className, ...props }, r
|
|
|
3431
3694
|
});
|
|
3432
3695
|
});
|
|
3433
3696
|
SelectTrigger.displayName = "SelectTrigger";
|
|
3434
|
-
const SelectValue = withTriggerContext(withStateProps$
|
|
3435
|
-
const SelectPlaceholder = withTriggerContext(withStateProps$
|
|
3697
|
+
const SelectValue = withTriggerContext(withStateProps$2(Select.Value), "value");
|
|
3698
|
+
const SelectPlaceholder = withTriggerContext(withStateProps$2(Select.Placeholder), "placeholder");
|
|
3436
3699
|
const SelectPrefixIcon = /*#__PURE__*/ React.forwardRef(({ fallback, className, ...otherProps }, ref)=>{
|
|
3437
3700
|
const { selectedItem, stateProps } = useSelectContext();
|
|
3438
3701
|
const classNames = useTriggerClassNames();
|
|
@@ -3447,7 +3710,7 @@ const SelectPrefixIcon = /*#__PURE__*/ React.forwardRef(({ fallback, className,
|
|
|
3447
3710
|
});
|
|
3448
3711
|
});
|
|
3449
3712
|
SelectPrefixIcon.displayName = "SelectPrefixIcon";
|
|
3450
|
-
const SelectSuffixIcon = withTriggerContext(withStateProps$
|
|
3713
|
+
const SelectSuffixIcon = withTriggerContext(withStateProps$2(InternalIcon), "suffixIcon");
|
|
3451
3714
|
const SelectPositioner = /*#__PURE__*/ React.forwardRef(({ className, ...props }, ref)=>{
|
|
3452
3715
|
const classNames = useContentClassNames();
|
|
3453
3716
|
return /*#__PURE__*/ jsx(Select.Positioner, {
|
|
@@ -3478,12 +3741,12 @@ withContentContext(Select.Group, "group");
|
|
|
3478
3741
|
withContentContext(Select.GroupLabel, "groupLabel");
|
|
3479
3742
|
const SelectItem = /*#__PURE__*/ React.forwardRef(({ prefixIcon, className, ...props }, ref)=>{
|
|
3480
3743
|
const [variantProps, otherProps] = selectItem.splitVariantProps(props);
|
|
3481
|
-
const rootProps = useItemProps
|
|
3744
|
+
const rootProps = useItemProps();
|
|
3482
3745
|
const classNames = selectItem({
|
|
3483
3746
|
...rootProps,
|
|
3484
3747
|
...variantProps
|
|
3485
3748
|
});
|
|
3486
|
-
return /*#__PURE__*/ jsx(ItemClassNamesProvider
|
|
3749
|
+
return /*#__PURE__*/ jsx(ItemClassNamesProvider, {
|
|
3487
3750
|
value: classNames,
|
|
3488
3751
|
children: /*#__PURE__*/ jsx(Select.Item, {
|
|
3489
3752
|
ref: ref,
|
|
@@ -3508,7 +3771,7 @@ const SelectItemPrefixIcon = /*#__PURE__*/ React.forwardRef(({ svg: svgOverride,
|
|
|
3508
3771
|
});
|
|
3509
3772
|
});
|
|
3510
3773
|
SelectItemPrefixIcon.displayName = "SelectItemPrefixIcon";
|
|
3511
|
-
const SelectItemBody = withItemContext
|
|
3774
|
+
const SelectItemBody = withItemContext(withItemStateProps(Primitive.div), "body");
|
|
3512
3775
|
const SelectItemLabel = /*#__PURE__*/ React.forwardRef(({ children, className, ...otherProps }, ref)=>{
|
|
3513
3776
|
const { label, stateProps } = useSelectItemContext();
|
|
3514
3777
|
const classNames = useItemClassNames();
|
|
@@ -3523,7 +3786,7 @@ const SelectItemLabel = /*#__PURE__*/ React.forwardRef(({ children, className, .
|
|
|
3523
3786
|
});
|
|
3524
3787
|
});
|
|
3525
3788
|
SelectItemLabel.displayName = "SelectItemLabel";
|
|
3526
|
-
withItemContext
|
|
3789
|
+
withItemContext(withItemStateProps(Primitive.span), "description");
|
|
3527
3790
|
const SelectItemIndicator = /*#__PURE__*/ React.forwardRef(({ selected: selectedSvg, unselected: unselectedSvg, className, ...otherProps }, ref)=>{
|
|
3528
3791
|
const { isSelected, stateProps } = useSelectItemContext();
|
|
3529
3792
|
const classNames = useItemClassNames();
|
|
@@ -3555,9 +3818,9 @@ const SelectHiddenSelect = /*#__PURE__*/ React.forwardRef((props, ref)=>{
|
|
|
3555
3818
|
});
|
|
3556
3819
|
SelectHiddenSelect.displayName = "SelectHiddenSelect";
|
|
3557
3820
|
|
|
3558
|
-
const { withContext: withContext$
|
|
3821
|
+
const { withContext: withContext$3, ClassNamesProvider: ClassNamesProvider$1 } = createSlotRecipeContext(switchStyle);
|
|
3559
3822
|
const { withContext: withControlContext, PropsProvider: ControlPropsProvider, withProvider: withControlProvider } = createSlotRecipeContext(switchmark);
|
|
3560
|
-
const withStateProps$
|
|
3823
|
+
const withStateProps$1 = createWithStateProps([
|
|
3561
3824
|
useSwitchContext
|
|
3562
3825
|
]);
|
|
3563
3826
|
const SwitchRoot = /*#__PURE__*/ React__default.forwardRef(({ className, ...props }, ref)=>{
|
|
@@ -3568,7 +3831,7 @@ const SwitchRoot = /*#__PURE__*/ React__default.forwardRef(({ className, ...prop
|
|
|
3568
3831
|
const classNames = switchStyle(switchVariantProps);
|
|
3569
3832
|
return /*#__PURE__*/ jsx(ControlPropsProvider, {
|
|
3570
3833
|
value: switchmarkVariantProps,
|
|
3571
|
-
children: /*#__PURE__*/ jsx(ClassNamesProvider$
|
|
3834
|
+
children: /*#__PURE__*/ jsx(ClassNamesProvider$1, {
|
|
3572
3835
|
value: classNames,
|
|
3573
3836
|
children: /*#__PURE__*/ jsx(Switch.Root, {
|
|
3574
3837
|
ref: ref,
|
|
@@ -3581,20 +3844,20 @@ const SwitchRoot = /*#__PURE__*/ React__default.forwardRef(({ className, ...prop
|
|
|
3581
3844
|
SwitchRoot.displayName = "SwitchRoot";
|
|
3582
3845
|
const SwitchControl = withScaleFeedback(withControlProvider(Switch.Control, "root"));
|
|
3583
3846
|
const SwitchThumb = withControlContext(Switch.Thumb, "thumb");
|
|
3584
|
-
withContext$
|
|
3847
|
+
withContext$3(withStateProps$1(Primitive.span), "label");
|
|
3585
3848
|
const SwitchHiddenInput = Switch.HiddenInput;
|
|
3586
3849
|
|
|
3587
3850
|
// src/use-layout-effect.tsx
|
|
3588
3851
|
var useLayoutEffect2 = globalThis?.document ? React.useLayoutEffect : ()=>{};
|
|
3589
3852
|
|
|
3590
|
-
const { withProvider: withProvider$1, withContext: withContext$
|
|
3853
|
+
const { withProvider: withProvider$1, withContext: withContext$2, useClassNames: useClassNames$1 } = createSlotRecipeContext(textInput);
|
|
3591
3854
|
const withFieldStateProps = createWithStateProps([
|
|
3592
3855
|
{
|
|
3593
3856
|
useContext: useFieldContext,
|
|
3594
3857
|
strict: false
|
|
3595
3858
|
}
|
|
3596
3859
|
]);
|
|
3597
|
-
const withStateProps
|
|
3860
|
+
const withStateProps = createWithStateProps([
|
|
3598
3861
|
useTextFieldContext,
|
|
3599
3862
|
{
|
|
3600
3863
|
useContext: useFieldContext,
|
|
@@ -3602,12 +3865,12 @@ const withStateProps$1 = createWithStateProps([
|
|
|
3602
3865
|
}
|
|
3603
3866
|
]);
|
|
3604
3867
|
const TextFieldRoot = withProvider$1(withFieldStateProps(TextField.Root), "root");
|
|
3605
|
-
withContext$
|
|
3606
|
-
withContext$
|
|
3607
|
-
withContext$
|
|
3608
|
-
withContext$
|
|
3868
|
+
withContext$2(withStateProps(InternalIcon), "prefixIcon");
|
|
3869
|
+
withContext$2(withStateProps(Primitive.span), "prefixText");
|
|
3870
|
+
withContext$2(withStateProps(InternalIcon), "suffixIcon");
|
|
3871
|
+
withContext$2(withStateProps(Primitive.span), "suffixText");
|
|
3609
3872
|
const TextFieldInput = /*#__PURE__*/ forwardRef(({ className, ...otherProps }, ref)=>{
|
|
3610
|
-
const classNames = useClassNames$
|
|
3873
|
+
const classNames = useClassNames$1();
|
|
3611
3874
|
const textFieldContext = useTextFieldContext();
|
|
3612
3875
|
const fieldContext = useFieldContext({
|
|
3613
3876
|
strict: false
|
|
@@ -3625,7 +3888,7 @@ const TextFieldInput = /*#__PURE__*/ forwardRef(({ className, ...otherProps }, r
|
|
|
3625
3888
|
});
|
|
3626
3889
|
TextFieldInput.displayName = "TextFieldInput";
|
|
3627
3890
|
const TextFieldTextarea = /*#__PURE__*/ forwardRef(({ className, autoresize = true, ...otherProps }, ref)=>{
|
|
3628
|
-
const classNames = useClassNames$
|
|
3891
|
+
const classNames = useClassNames$1();
|
|
3629
3892
|
const textFieldContext = useTextFieldContext();
|
|
3630
3893
|
const fieldContext = useFieldContext({
|
|
3631
3894
|
strict: false
|
|
@@ -3682,7 +3945,7 @@ const TextFieldTextarea = /*#__PURE__*/ forwardRef(({ className, autoresize = tr
|
|
|
3682
3945
|
});
|
|
3683
3946
|
TextFieldTextarea.displayName = "TextFieldTextarea";
|
|
3684
3947
|
|
|
3685
|
-
__insertCSS(".seed-data-table__root{box-sizing:border-box;display:flex;flex-direction:column;width:100%;border-width:1px;border-style:solid;font-family:Inter,-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif}.seed-data-table__table{width:100%;border-collapse:separate;border-spacing:0;table-layout:fixed}.seed-data-table__headerCell{box-sizing:border-box;position:sticky;top:0;z-index:2;vertical-align:middle;text-align:start;padding:0;height:var(--seed-dimension-x10);background-color:var(--seed-color-bg-layer-default);border-top:1px solid var(--seed-color-stroke-neutral-muted);border-bottom:1px solid var(--seed-color-stroke-neutral-muted);border-inline-end:1px solid var(--seed-color-stroke-neutral-muted);border-inline-start:none;color:var(--seed-color-fg-neutral);font-size:var(--seed-font-size-t4);line-height:var(--seed-line-height-t4);font-weight:var(--seed-font-weight-medium);letter-spacing:-.14px}.seed-data-table__headerCell:last-child{border-inline-end:none}.seed-data-table__headerCell[data-sortable]{cursor:pointer;user-select:none}.seed-data-table__headerCell [data-chevron]{opacity:0;transition:opacity .12s}.seed-data-table__headerCell[data-sortable]:hover [data-chevron]{opacity:.6}.seed-data-table__headerCell[data-sorted] [data-chevron]{opacity:1;color:var(--seed-color-fg-neutral)}.seed-data-table__headerCell:hover [data-column-menu]{opacity:1}.seed-data-table__headerCell [data-column-menu]:focus-visible,.seed-data-table__headerCell [data-column-menu][data-open]{opacity:1}.seed-data-table__headerCell[data-dragging]{opacity:.5}.seed-data-table__headerCell[data-sticky]{position:sticky;z-index:3;background-color:var(--seed-color-bg-layer-default)}.seed-data-table__headerCell{outline:var(--seed-dimension-x0_5) solid transparent;outline-offset:calc(var(--seed-dimension-x0_5) * -1)}.seed-data-table__headerCell:is(:focus-visible,[data-focus-visible]){outline:var(--seed-dimension-x0_5) solid var(--seed-color-stroke-focus-ring);outline-offset:calc(var(--seed-dimension-x0_5) * -1)}.seed-data-table__headerCell .seed-data-table-resize-handle{position:absolute;top:0;inset-inline-end:calc(7px / -2);width:7px;height:100%;z-index:4;display:flex;align-items:center;justify-content:center;cursor:col-resize;touch-action:none}.seed-data-table__headerCell .seed-data-table-resize-handle-inner{width:3px;height:100%;border-radius:3px;background-color:#0298d9;opacity:0;transition:opacity .1s}.seed-data-table__headerCell .seed-data-table-resize-handle:hover .seed-data-table-resize-handle-inner,.seed-data-table__headerCell .seed-data-table-resize-handle[data-resizing] .seed-data-table-resize-handle-inner{opacity:1}.seed-data-table__headerCellInner{box-sizing:border-box;display:flex;align-items:center;width:100%;height:100%;min-width:0;gap:var(--seed-dimension-x1_5);padding-inline:var(--seed-dimension-x3)}.seed-data-table__headerCellLabel{min-width:0;flex:1 1 0%;overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__headerCellIcon{flex-shrink:0;margin-inline-start:auto;display:inline-flex;align-items:center;justify-content:center;width:var(--seed-dimension-x4);height:var(--seed-dimension-x4);color:var(--seed-color-fg-neutral-subtle);border:none;background:0 0;padding:0;font:inherit;cursor:pointer}.seed-data-table__headerCellIcon[data-active]{color:var(--seed-color-fg-neutral)}.seed-data-table__headerCellIcon[data-column-menu]{opacity:0;transition:opacity .12s}.seed-data-table__row{position:relative;background-color:var(--seed-color-bg-layer-default);--seed-data-table-row-bg:var(--seed-color-bg-layer-default)}.seed-data-table__row[data-clickable]{cursor:pointer}.seed-data-table__row:is([aria-selected=true],[data-selected]){background-color:color-mix(in srgb,#0298d9 10%,var(--seed-color-bg-layer-default));--seed-data-table-row-bg:color-mix(in srgb, #0298d9 10%, var(--seed-color-bg-layer-default))}.seed-data-table__row:is([aria-selected=true],[data-selected]):is(:hover,[data-hover]){background-color:color-mix(in srgb,var(--seed-color-fg-neutral) 3%,color-mix(in srgb,#0298d9 10%,var(--seed-color-bg-layer-default)));--seed-data-table-row-bg:color-mix(in srgb, var(--seed-color-fg-neutral) 3%, color-mix(in srgb, #0298d9 10%, var(--seed-color-bg-layer-default)))}.seed-data-table__cell{box-sizing:border-box;position:relative;vertical-align:middle;text-align:start;height:var(--seed-dimension-x9);border-bottom:1px solid var(--seed-color-stroke-neutral-muted);border-inline-end:1px solid var(--seed-color-stroke-neutral-muted);border-top:none;border-inline-start:none;padding-block:0;padding-inline:var(--seed-dimension-x3);color:var(--seed-color-fg-neutral);font-size:var(--seed-font-size-t4);line-height:var(--seed-line-height-t4);font-weight:var(--seed-font-weight-regular);letter-spacing:-.14px;overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__cell:not([data-editable]){user-select:none}.seed-data-table__cell:last-child{border-inline-end:none}tbody tr:last-child .seed-data-table__cell{border-bottom:none}.seed-data-table__cell[data-numeric]{text-align:end;font-variant-numeric:tabular-nums}.seed-data-table__cell[data-sticky]{position:sticky;z-index:1;background-color:var(--seed-data-table-row-bg)}.seed-data-table__cell[data-overlay-open]{overflow:visible;z-index:20}.seed-data-table__cell[data-editable]{cursor:default}.seed-data-table__cell .seed-data-table-edit-picker{display:flex;align-items:center;gap:var(--seed-dimension-x1_5);min-width:0}.seed-data-table__cell .seed-data-table-edit-pending{flex-shrink:0;color:var(--seed-color-fg-neutral-subtle);animation:rotate .6s linear infinite}.seed-data-table__cell .seed-data-table-edit-error{position:absolute;top:100%;inset-inline-start:var(--seed-dimension-x3);inset-inline-end:var(--seed-dimension-x3);z-index:2;padding-top:2px;font-size:12px;line-height:1.3;color:var(--seed-color-fg-critical);overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__entity{display:flex;align-items:center;min-width:0;gap:var(--seed-dimension-x1_5)}.seed-data-table__entityName{min-width:0;font-weight:var(--seed-font-weight-medium);overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__tagList{display:flex;align-items:center;gap:var(--seed-dimension-x1_5);overflow:hidden;min-width:0}.seed-data-table__entityActions{display:flex;align-items:center;gap:6px;margin-inline-start:auto;flex-shrink:0;opacity:0;transition:opacity .15s}tr:hover .seed-data-table__entityActions{opacity:1}.seed-data-table__entityActions:focus-within{opacity:1}.seed-data-table__entityAction{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;margin:0;padding:2px;border:none;border-radius:6px;background:0 0;font-family:inherit;color:color-mix(in srgb,var(--seed-color-fg-neutral) 55%,transparent);cursor:pointer;flex-shrink:0;transition:background-color .2s,color .2s,box-shadow .2s}.seed-data-table__entityAction:hover{background:color-mix(in srgb,var(--seed-color-fg-neutral) 4%,transparent);color:var(--seed-color-fg-neutral)}.seed-data-table__link{color:var(--seed-color-fg-neutral);text-decoration:none;min-width:0;overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__link:is(:hover,[data-hover]){text-decoration:underline}.seed-data-table__chip{box-sizing:border-box;display:inline-flex;align-items:center;flex-shrink:0;max-width:100%;height:22px;padding:0 4px;border-radius:7px;font-size:10px;font-weight:var(--seed-font-weight-regular);line-height:15px;overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__chipList{position:relative;display:flex;align-items:center;flex-wrap:nowrap;gap:3px;overflow:hidden;min-width:0;width:100%}.seed-data-table__chipOverflow{box-sizing:border-box;display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;height:20px;padding:0 4px;border-radius:6px;background-color:#f6f7f7;color:#000;font-size:10px;font-weight:var(--seed-font-weight-regular);line-height:15px;white-space:nowrap}.seed-data-table__chipListEditor{display:flex;align-items:center;flex-wrap:wrap;gap:3px;min-width:0;width:100%}.seed-data-table__chipRemove{box-sizing:border-box;display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;width:12px;height:12px;margin:0;margin-inline-start:2px;padding:0;border:none;border-radius:999px;background:0 0;color:inherit;opacity:.6;cursor:pointer;transition:opacity .12s,background-color .12s}.seed-data-table__chipRemove:hover{opacity:1;background-color:rgba(0,0,0,.08)}.seed-data-table__chipInput{flex:1 1 auto;min-width:40px;height:22px;margin:0;padding:0;border:none;outline:0;background:0 0;font-family:inherit;font-size:var(--seed-font-size-t4);color:var(--seed-color-fg-neutral);letter-spacing:-.14px}.seed-data-table__expandOverlay{box-sizing:border-box;position:absolute;top:-1px;inset-inline-start:-1px;inset-inline-end:-1px;z-index:10;min-height:calc(100% + 2px);display:flex;align-items:flex-start;padding-inline:var(--seed-dimension-x3);padding-block:var(--seed-dimension-x3);background-color:var(--seed-color-bg-layer-floating);border-radius:4px;box-shadow:none;border:1px solid #266df0;color:var(--seed-color-fg-neutral);font-size:var(--seed-font-size-t4);line-height:var(--seed-line-height-t4);letter-spacing:-.14px;cursor:default}.seed-data-table__expandOverlayText{width:100%;min-width:0;white-space:normal;word-break:break-word}.seed-data-table__cellSuffixRow{display:flex;align-items:center;gap:4px;min-width:0;width:100%}.seed-data-table__cellSuffixText{flex:1 1 0%;min-width:0;overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__cellSuffixIcon{flex-shrink:0;display:inline-flex;align-items:center;color:color-mix(in srgb,var(--seed-color-fg-neutral) 55%,transparent)}.seed-data-table__toolbar{box-sizing:border-box;display:flex;align-items:center;width:100%;flex-shrink:0;min-height:49px;gap:var(--seed-dimension-x2);padding-inline:var(--seed-dimension-x3);border-top:1px solid var(--seed-color-stroke-neutral-muted)}.seed-data-table__search{position:relative;display:inline-flex;align-items:center;flex:1 1 0%;max-width:280px}.seed-data-table__search svg{position:absolute;inset-inline-start:var(--seed-dimension-x2);width:var(--seed-dimension-x4);height:var(--seed-dimension-x4);color:var(--seed-color-fg-neutral-muted);pointer-events:none}.seed-data-table__search input{width:100%;height:var(--seed-dimension-x8);padding-inline-start:var(--seed-dimension-x7);padding-inline-end:var(--seed-dimension-x2);border:none;border-radius:var(--seed-radius-r2);background-color:var(--seed-color-bg-layer-default);box-shadow:inset 0 0 0 1px var(--seed-color-stroke-neutral-weak);font-family:inherit;font-size:var(--seed-font-size-t4);color:var(--seed-color-fg-neutral);outline:0;transition:box-shadow .1s var(--seed-timing-function-easing)}.seed-data-table__search input::placeholder{color:var(--seed-color-fg-neutral-subtle)}.seed-data-table__search input:focus{box-shadow:inset 0 0 0 2px var(--seed-color-stroke-neutral-contrast)}.seed-data-table__count{flex-shrink:0;color:var(--seed-color-fg-neutral-subtle);font-size:var(--seed-font-size-t3);font-variant-numeric:tabular-nums}.seed-data-table__toolbarEnd{flex-shrink:0;margin-inline-start:auto;display:inline-flex;align-items:center;gap:var(--seed-dimension-x2)}.seed-data-table__scrollArea{position:relative;display:flex;flex-direction:column;width:100%;min-width:0;flex:1 1 auto;min-height:0;overflow-x:auto;overflow-y:auto}.seed-data-table__scrollArea[data-scrolled-start] [data-sticky-edge]{clip-path:inset(0 -38px 0 0);box-shadow:6px 0 16px 4px rgba(0,0,0,.12)}.seed-data-table__scrollArea[data-scrolled-end] [data-sticky-edge-end]{clip-path:inset(0 0 0 -38px);box-shadow:-6px 0 16px 4px rgba(0,0,0,.12)}.seed-data-table__footerCell{box-sizing:border-box;position:sticky;bottom:0;z-index:2;vertical-align:middle;padding:0;height:var(--seed-dimension-x9);background-color:var(--seed-color-bg-layer-default);border-top:1px solid var(--seed-color-stroke-neutral-muted);border-inline-end:1px solid var(--seed-color-stroke-neutral-muted);border-inline-start:none}.seed-data-table__footerCell:last-child{border-inline-end:none}.seed-data-table__footerCell .seed-data-table-column-calc-trigger{display:flex;align-items:center;gap:var(--seed-dimension-x1_5);width:100%;height:100%;min-width:0;margin:0;padding:0 var(--seed-dimension-x3);border:none;background:0 0;color:var(--seed-color-fg-neutral-subtle);font-family:inherit;font-size:var(--seed-font-size-t4);font-weight:var(--seed-font-weight-medium);white-space:nowrap;overflow:clip;cursor:pointer;transition:background-color .12s,color .12s}.seed-data-table__footerCell .seed-data-table-column-calc-trigger:hover{background-color:color-mix(in srgb,var(--seed-color-fg-neutral) 4%,transparent);color:var(--seed-color-fg-neutral)}.seed-data-table__footerCell .seed-data-table-column-calc-trigger[data-align=right]{justify-content:flex-end}.seed-data-table__footerCell .seed-data-table-column-calc-trigger svg{flex-shrink:0;opacity:.7}.seed-data-table__footerCell .seed-data-table-column-calc-placeholder{overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__footerCell .seed-data-table-column-calc-value{min-width:0;flex-shrink:1;color:var(--seed-color-fg-neutral);font-variant-numeric:tabular-nums;overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__footerCell .seed-data-table-column-calc-op{min-width:0;flex-shrink:1;color:color-mix(in srgb,var(--seed-color-fg-neutral) 63%,transparent);overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__emptyState{box-sizing:border-box;text-align:center;padding-block:var(--seed-dimension-x8);padding-inline:var(--seed-dimension-x3);color:var(--seed-color-fg-neutral-subtle);font-size:var(--seed-font-size-t4)}.seed-data-table__loadingState{box-sizing:border-box;display:flex;align-items:center;justify-content:center;gap:10px;padding-block:var(--seed-dimension-x8);padding-inline:var(--seed-dimension-x3);color:color-mix(in srgb,var(--seed-color-fg-neutral) 63%,transparent);font-size:var(--seed-font-size-t4);line-height:var(--seed-line-height-t4);font-weight:var(--seed-font-weight-medium)}.seed-data-table__loadingState[data-inline]{padding-block:0;padding-inline:0;height:var(--seed-dimension-x9)}.seed-data-table__loadingState .seed-data-table-loading-spinner{flex-shrink:0;color:color-mix(in srgb,var(--seed-color-fg-neutral) 63%,transparent);animation:rotate .6s linear infinite}.seed-data-table__addRowCell{box-sizing:border-box;padding:0;border-top:none}.seed-data-table__addRow{box-sizing:border-box;display:flex;align-items:center;width:100%;margin:0;border:none;border-top:1px solid var(--seed-color-stroke-neutral-muted);background-color:var(--seed-color-bg-layer-default);height:var(--seed-dimension-x9);gap:var(--seed-dimension-x2);padding-inline:var(--seed-dimension-x2);color:var(--seed-color-fg-neutral-subtle);font-family:inherit;font-size:var(--seed-font-size-t4);cursor:pointer;transition:background-color .12s,color .12s}.seed-data-table__addRow:hover{background-color:color-mix(in srgb,var(--seed-color-fg-neutral) 4%,var(--seed-color-bg-layer-default));color:var(--seed-color-fg-neutral)}.seed-data-table__addRow svg{flex-shrink:0}.seed-data-table__selectionCell{text-align:center;padding:0}.seed-data-table__root--surface_card{border-color:var(--seed-color-stroke-neutral-muted);background-color:var(--seed-color-bg-layer-default);border-radius:var(--seed-radius-r3)}.seed-data-table__root--surface_bleed{border-color:var(--seed-color-bg-transparent);background-color:var(--seed-color-bg-transparent)}.seed-data-table__row--interactive_true{transition:background-color .12s}.seed-data-table__row--interactive_true:is(:hover,[data-hover]){background-color:color-mix(in srgb,var(--seed-color-fg-neutral) 3%,transparent);--seed-data-table-row-bg:color-mix(in srgb, var(--seed-color-fg-neutral) 3%, var(--seed-color-bg-layer-default))}");
|
|
3948
|
+
__insertCSS(".seed-data-table__root{box-sizing:border-box;position:relative;display:flex;flex-direction:column;width:100%;border-width:1px;border-style:solid;font-family:Inter,-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif}.seed-data-table__table{width:100%;border-collapse:separate;border-spacing:0;table-layout:fixed}.seed-data-table__headerCell{box-sizing:border-box;position:sticky;top:0;z-index:2;vertical-align:middle;text-align:start;padding:0;height:var(--seed-dimension-x10);background-color:var(--seed-color-bg-layer-default);border-top:1px solid var(--seed-color-stroke-neutral-muted);border-bottom:1px solid var(--seed-color-stroke-neutral-muted);border-inline-end:1px solid var(--seed-color-stroke-neutral-muted);border-inline-start:none;color:var(--seed-color-fg-neutral);font-size:var(--seed-font-size-t4);line-height:var(--seed-line-height-t4);font-weight:var(--seed-font-weight-medium);letter-spacing:-.14px}.seed-data-table__headerCell:last-child{border-inline-end:none}.seed-data-table__headerCell[data-sortable]{cursor:pointer;user-select:none}.seed-data-table__headerCell [data-chevron]{opacity:0;transition:opacity .12s}.seed-data-table__headerCell[data-sortable]:hover [data-chevron]{opacity:.6}.seed-data-table__headerCell[data-sorted] [data-chevron]{opacity:1;color:var(--seed-color-fg-neutral)}.seed-data-table__headerCell:hover [data-column-menu]{opacity:1}.seed-data-table__headerCell [data-column-menu]:focus-visible,.seed-data-table__headerCell [data-column-menu][data-open]{opacity:1}.seed-data-table__headerCell[data-dragging]{opacity:.5}.seed-data-table__headerCell[data-sticky]{position:sticky;z-index:3;background-color:var(--seed-color-bg-layer-default)}.seed-data-table__headerCell{outline:var(--seed-dimension-x0_5) solid transparent;outline-offset:calc(var(--seed-dimension-x0_5) * -1)}.seed-data-table__headerCell:is(:focus-visible,[data-focus-visible]){outline:var(--seed-dimension-x0_5) solid var(--seed-color-stroke-focus-ring);outline-offset:calc(var(--seed-dimension-x0_5) * -1)}.seed-data-table__headerCell .seed-data-table-resize-handle{position:absolute;top:0;inset-inline-end:calc(7px / -2);width:7px;height:100%;z-index:4;display:flex;align-items:center;justify-content:center;cursor:col-resize;touch-action:none}.seed-data-table__headerCell .seed-data-table-resize-handle-inner{width:3px;height:100%;border-radius:3px;background-color:#0298d9;opacity:0;transition:opacity .1s}.seed-data-table__headerCell .seed-data-table-resize-handle:hover .seed-data-table-resize-handle-inner,.seed-data-table__headerCell .seed-data-table-resize-handle[data-resizing] .seed-data-table-resize-handle-inner{opacity:1}.seed-data-table__headerCellInner{box-sizing:border-box;display:flex;align-items:center;width:100%;height:100%;min-width:0;gap:var(--seed-dimension-x1_5);padding-inline:var(--seed-dimension-x3)}.seed-data-table__headerCellLabel{min-width:0;flex:1 1 0%;overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__headerCellIcon{flex-shrink:0;margin-inline-start:auto;display:inline-flex;align-items:center;justify-content:center;width:var(--seed-dimension-x4);height:var(--seed-dimension-x4);color:var(--seed-color-fg-neutral-subtle);border:none;background:0 0;padding:0;font:inherit;cursor:pointer}.seed-data-table__headerCellIcon[data-active]{color:var(--seed-color-fg-neutral)}.seed-data-table__headerCellIcon[data-column-menu]{opacity:0;transition:opacity .12s}.seed-data-table__row{position:relative;background-color:var(--seed-color-bg-layer-default);--seed-data-table-row-bg:var(--seed-color-bg-layer-default)}.seed-data-table__row[data-clickable]{cursor:pointer}.seed-data-table__row:is([aria-selected=true],[data-selected]){background-color:color-mix(in srgb,#0298d9 10%,var(--seed-color-bg-layer-default));--seed-data-table-row-bg:color-mix(in srgb, #0298d9 10%, var(--seed-color-bg-layer-default))}.seed-data-table__row:is([aria-selected=true],[data-selected]):is(:hover,[data-hover]){background-color:color-mix(in srgb,var(--seed-color-fg-neutral) 3%,color-mix(in srgb,#0298d9 10%,var(--seed-color-bg-layer-default)));--seed-data-table-row-bg:color-mix(in srgb, var(--seed-color-fg-neutral) 3%, color-mix(in srgb, #0298d9 10%, var(--seed-color-bg-layer-default)))}.seed-data-table__cell{box-sizing:border-box;position:relative;vertical-align:middle;text-align:start;height:var(--seed-dimension-x9);border-bottom:1px solid var(--seed-color-stroke-neutral-muted);border-inline-end:1px solid var(--seed-color-stroke-neutral-muted);border-top:none;border-inline-start:none;padding-block:0;padding-inline:var(--seed-dimension-x3);color:var(--seed-color-fg-neutral);font-size:var(--seed-font-size-t4);line-height:var(--seed-line-height-t4);font-weight:var(--seed-font-weight-regular);letter-spacing:-.14px;overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__cell:not([data-editable]){user-select:none}.seed-data-table__cell:last-child{border-inline-end:none}tbody tr:last-child .seed-data-table__cell{border-bottom:none}.seed-data-table__cell[data-numeric]{text-align:end;font-variant-numeric:tabular-nums}.seed-data-table__cell[data-sticky]{position:sticky;z-index:1;background-color:var(--seed-data-table-row-bg)}.seed-data-table__cell[data-overlay-open]{overflow:visible;z-index:20}.seed-data-table__cell[data-editable]{cursor:default}.seed-data-table__cell .seed-data-table-edit-picker{display:flex;align-items:center;gap:var(--seed-dimension-x1_5);min-width:0}.seed-data-table__cell .seed-data-table-edit-pending{flex-shrink:0;color:var(--seed-color-fg-neutral-subtle);animation:rotate .6s linear infinite}.seed-data-table__cell .seed-data-table-edit-error{position:absolute;top:100%;inset-inline-start:var(--seed-dimension-x3);inset-inline-end:var(--seed-dimension-x3);z-index:2;padding-top:2px;font-size:12px;line-height:1.3;color:var(--seed-color-fg-critical);overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__entity{display:flex;align-items:center;min-width:0;gap:var(--seed-dimension-x1_5)}.seed-data-table__entityName{min-width:0;font-weight:var(--seed-font-weight-medium);overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__tagList{display:flex;align-items:center;gap:var(--seed-dimension-x1_5);overflow:hidden;min-width:0}.seed-data-table__entityActions{display:flex;align-items:center;gap:6px;margin-inline-start:auto;flex-shrink:0;opacity:0;transition:opacity .15s}tr:hover .seed-data-table__entityActions{opacity:1}.seed-data-table__entityActions:focus-within{opacity:1}.seed-data-table__entityAction{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;margin:0;padding:2px;border:none;border-radius:6px;background:0 0;font-family:inherit;color:color-mix(in srgb,var(--seed-color-fg-neutral) 55%,transparent);cursor:pointer;flex-shrink:0;transition:background-color .2s,color .2s,box-shadow .2s}.seed-data-table__entityAction:hover{background:color-mix(in srgb,var(--seed-color-fg-neutral) 4%,transparent);color:var(--seed-color-fg-neutral)}.seed-data-table__link{color:var(--seed-color-fg-neutral);text-decoration:none;min-width:0;overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__link:is(:hover,[data-hover]){text-decoration:underline}.seed-data-table__chip{box-sizing:border-box;display:inline-flex;align-items:center;flex-shrink:0;max-width:100%;height:22px;padding:0 4px;border-radius:7px;font-size:10px;font-weight:var(--seed-font-weight-regular);line-height:15px;overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__chipList{position:relative;display:flex;align-items:center;flex-wrap:nowrap;gap:3px;overflow:hidden;min-width:0;width:100%}.seed-data-table__chipOverflow{box-sizing:border-box;display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;height:20px;padding:0 4px;border-radius:6px;background-color:#f6f7f7;color:#000;font-size:10px;font-weight:var(--seed-font-weight-regular);line-height:15px;white-space:nowrap}.seed-data-table__chipListEditor{display:flex;align-items:center;flex-wrap:wrap;gap:3px;min-width:0;width:100%}.seed-data-table__chipRemove{box-sizing:border-box;display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;width:12px;height:12px;margin:0;margin-inline-start:2px;padding:0;border:none;border-radius:999px;background:0 0;color:inherit;opacity:.6;cursor:pointer;transition:opacity .12s,background-color .12s}.seed-data-table__chipRemove:hover{opacity:1;background-color:rgba(0,0,0,.08)}.seed-data-table__chipInput{flex:1 1 auto;min-width:40px;height:22px;margin:0;padding:0;border:none;outline:0;background:0 0;font-family:inherit;font-size:var(--seed-font-size-t4);color:var(--seed-color-fg-neutral);letter-spacing:-.14px}.seed-data-table__expandOverlay{box-sizing:border-box;position:absolute;top:-1px;inset-inline-start:-1px;inset-inline-end:-1px;z-index:10;min-height:calc(100% + 2px);display:flex;align-items:flex-start;padding-inline:var(--seed-dimension-x3);padding-block:var(--seed-dimension-x3);background-color:var(--seed-color-bg-layer-floating);border-radius:4px;box-shadow:none;border:1px solid #266df0;color:var(--seed-color-fg-neutral);font-size:var(--seed-font-size-t4);line-height:var(--seed-line-height-t4);letter-spacing:-.14px;cursor:default}.seed-data-table__expandOverlayText{width:100%;min-width:0;white-space:normal;word-break:break-word}.seed-data-table__cellSuffixRow{display:flex;align-items:center;gap:4px;min-width:0;width:100%}.seed-data-table__cellSuffixText{flex:1 1 0%;min-width:0;overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__cellSuffixIcon{flex-shrink:0;display:inline-flex;align-items:center;color:color-mix(in srgb,var(--seed-color-fg-neutral) 55%,transparent)}.seed-data-table__toolbar{box-sizing:border-box;display:flex;align-items:center;width:100%;flex-shrink:0;min-height:49px;gap:var(--seed-dimension-x2);padding-inline:var(--seed-dimension-x3);border-top:1px solid var(--seed-color-stroke-neutral-muted)}.seed-data-table__search{position:relative;display:inline-flex;align-items:center;flex:1 1 0%;max-width:280px}.seed-data-table__search svg{position:absolute;inset-inline-start:var(--seed-dimension-x2);width:var(--seed-dimension-x4);height:var(--seed-dimension-x4);color:var(--seed-color-fg-neutral-muted);pointer-events:none}.seed-data-table__search input{width:100%;height:var(--seed-dimension-x8);padding-inline-start:var(--seed-dimension-x7);padding-inline-end:var(--seed-dimension-x2);border:none;border-radius:var(--seed-radius-r2);background-color:var(--seed-color-bg-layer-default);box-shadow:inset 0 0 0 1px var(--seed-color-stroke-neutral-weak);font-family:inherit;font-size:var(--seed-font-size-t4);color:var(--seed-color-fg-neutral);outline:0;transition:box-shadow .1s var(--seed-timing-function-easing)}.seed-data-table__search input::placeholder{color:var(--seed-color-fg-neutral-subtle)}.seed-data-table__search input:focus{box-shadow:inset 0 0 0 2px var(--seed-color-stroke-neutral-contrast)}.seed-data-table__count{flex-shrink:0;color:var(--seed-color-fg-neutral-subtle);font-size:var(--seed-font-size-t3);font-variant-numeric:tabular-nums}.seed-data-table__toolbarEnd{flex-shrink:0;margin-inline-start:auto;display:inline-flex;align-items:center;gap:var(--seed-dimension-x2)}.seed-data-table__scrollArea{position:relative;display:flex;flex-direction:column;width:100%;min-width:0;flex:1 1 auto;min-height:0;overflow-x:auto;overflow-y:auto}.seed-data-table__scrollArea[data-scrolled-start] [data-sticky-edge]{clip-path:inset(0 -38px 0 0);box-shadow:6px 0 16px 4px rgba(0,0,0,.12)}.seed-data-table__scrollArea[data-scrolled-end] [data-sticky-edge-end]{clip-path:inset(0 0 0 -38px);box-shadow:-6px 0 16px 4px rgba(0,0,0,.12)}.seed-data-table__footerCell{box-sizing:border-box;position:sticky;bottom:0;z-index:2;vertical-align:middle;padding:0;height:var(--seed-dimension-x9);background-color:var(--seed-color-bg-layer-default);border-top:1px solid var(--seed-color-stroke-neutral-muted);border-inline-end:1px solid var(--seed-color-stroke-neutral-muted);border-inline-start:none}.seed-data-table__footerCell:last-child{border-inline-end:none}.seed-data-table__footerCell .seed-data-table-column-calc-trigger{display:flex;align-items:center;gap:var(--seed-dimension-x1_5);width:100%;height:100%;min-width:0;margin:0;padding:0 var(--seed-dimension-x3);border:none;background:0 0;color:var(--seed-color-fg-neutral-subtle);font-family:inherit;font-size:var(--seed-font-size-t4);font-weight:var(--seed-font-weight-medium);white-space:nowrap;overflow:clip;cursor:pointer;transition:background-color .12s,color .12s}.seed-data-table__footerCell .seed-data-table-column-calc-trigger:hover{background-color:color-mix(in srgb,var(--seed-color-fg-neutral) 4%,transparent);color:var(--seed-color-fg-neutral)}.seed-data-table__footerCell .seed-data-table-column-calc-trigger[data-align=right]{justify-content:flex-end}.seed-data-table__footerCell .seed-data-table-column-calc-trigger svg{flex-shrink:0;opacity:.7}.seed-data-table__footerCell .seed-data-table-column-calc-placeholder{overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__footerCell .seed-data-table-column-calc-value{min-width:0;flex-shrink:1;color:var(--seed-color-fg-neutral);font-variant-numeric:tabular-nums;overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__footerCell .seed-data-table-column-calc-op{min-width:0;flex-shrink:1;color:color-mix(in srgb,var(--seed-color-fg-neutral) 63%,transparent);overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__emptyState{box-sizing:border-box;text-align:center;padding-block:var(--seed-dimension-x8);padding-inline:var(--seed-dimension-x3);color:var(--seed-color-fg-neutral-subtle);font-size:var(--seed-font-size-t4)}.seed-data-table__loadingState{box-sizing:border-box;display:flex;align-items:center;justify-content:center;gap:10px;padding-block:var(--seed-dimension-x8);padding-inline:var(--seed-dimension-x3);color:color-mix(in srgb,var(--seed-color-fg-neutral) 63%,transparent);font-size:var(--seed-font-size-t4);line-height:var(--seed-line-height-t4);font-weight:var(--seed-font-weight-medium)}.seed-data-table__loadingState[data-inline]{padding-block:0;padding-inline:0;height:var(--seed-dimension-x9)}.seed-data-table__loadingState .seed-data-table-loading-spinner{flex-shrink:0;color:color-mix(in srgb,var(--seed-color-fg-neutral) 63%,transparent);animation:rotate .6s linear infinite}.seed-data-table__addRowCell{box-sizing:border-box;padding:0;border-top:none}.seed-data-table__addRow{box-sizing:border-box;display:flex;align-items:center;width:100%;margin:0;border:none;border-top:1px solid var(--seed-color-stroke-neutral-muted);background-color:var(--seed-color-bg-layer-default);height:var(--seed-dimension-x9);gap:var(--seed-dimension-x2);padding-inline:var(--seed-dimension-x2);color:var(--seed-color-fg-neutral-subtle);font-family:inherit;font-size:var(--seed-font-size-t4);cursor:pointer;transition:background-color .12s,color .12s}.seed-data-table__addRow:hover{background-color:color-mix(in srgb,var(--seed-color-fg-neutral) 4%,var(--seed-color-bg-layer-default));color:var(--seed-color-fg-neutral)}.seed-data-table__addRow svg{flex-shrink:0}.seed-data-table__selectionCell{text-align:center;padding:0}.seed-data-table__bulkBar{box-sizing:border-box;position:absolute;inset-inline-start:50%;transform:translateX(-50%);bottom:var(--seed-dimension-x4);z-index:30;display:flex;align-items:center;gap:var(--seed-dimension-x3);min-height:36px;padding-inline:var(--seed-dimension-x3_5);padding-block:var(--seed-dimension-x2);border-radius:var(--seed-radius-full);box-shadow:var(--seed-shadow-s3);background-color:var(--seed-color-bg-layer-floating);color:var(--seed-color-fg-neutral);font-family:inherit;font-size:var(--seed-font-size-t4);font-weight:var(--seed-font-weight-medium);white-space:nowrap}.seed-data-table__bulkBarCount{flex-shrink:0;color:var(--seed-color-fg-neutral);font-variant-numeric:tabular-nums}.seed-data-table__bulkBarClear{box-sizing:border-box;display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;width:20px;height:20px;margin:0;padding:2px;border:none;border-radius:6px;background:0 0;font-family:inherit;color:color-mix(in srgb,var(--seed-color-fg-neutral) 55%,transparent);cursor:pointer;transition:background-color .2s,color .2s}.seed-data-table__bulkBarClear:hover{background-color:color-mix(in srgb,var(--seed-color-fg-neutral) 8%,transparent);color:var(--seed-color-fg-neutral)}.seed-data-table__bulkBarActions{display:flex;align-items:center;flex-shrink:0;gap:var(--seed-dimension-x3);padding-inline-start:var(--seed-dimension-x3);border-inline-start:1px solid color-mix(in srgb,var(--seed-color-fg-neutral) 16%,transparent)}.seed-data-table__root--surface_card{border-color:var(--seed-color-stroke-neutral-muted);background-color:var(--seed-color-bg-layer-default);border-radius:var(--seed-radius-r3)}.seed-data-table__root--surface_bleed{border-color:var(--seed-color-bg-transparent);background-color:var(--seed-color-bg-transparent)}.seed-data-table__row--interactive_true{transition:background-color .12s}.seed-data-table__row--interactive_true:is(:hover,[data-hover]){background-color:color-mix(in srgb,var(--seed-color-fg-neutral) 3%,transparent);--seed-data-table-row-bg:color-mix(in srgb, var(--seed-color-fg-neutral) 3%, var(--seed-color-bg-layer-default))}");
|
|
3686
3949
|
|
|
3687
3950
|
const createClassName = (className, variants, compoundVariants = [])=>{
|
|
3688
3951
|
const variantKeys = Object.keys(variants);
|
|
@@ -3876,6 +4139,22 @@ const dataTableSlotNames = [
|
|
|
3876
4139
|
[
|
|
3877
4140
|
"selectionCell",
|
|
3878
4141
|
"seed-data-table__selectionCell"
|
|
4142
|
+
],
|
|
4143
|
+
[
|
|
4144
|
+
"bulkBar",
|
|
4145
|
+
"seed-data-table__bulkBar"
|
|
4146
|
+
],
|
|
4147
|
+
[
|
|
4148
|
+
"bulkBarCount",
|
|
4149
|
+
"seed-data-table__bulkBarCount"
|
|
4150
|
+
],
|
|
4151
|
+
[
|
|
4152
|
+
"bulkBarClear",
|
|
4153
|
+
"seed-data-table__bulkBarClear"
|
|
4154
|
+
],
|
|
4155
|
+
[
|
|
4156
|
+
"bulkBarActions",
|
|
4157
|
+
"seed-data-table__bulkBarActions"
|
|
3879
4158
|
]
|
|
3880
4159
|
];
|
|
3881
4160
|
const defaultVariant$6 = {
|
|
@@ -3913,7 +4192,7 @@ Object.assign(dataTable, {
|
|
|
3913
4192
|
// other file (`DataTable.tsx`, `column-menu.tsx`, `resize.tsx`, `column-calc.tsx`,
|
|
3914
4193
|
// `toolbar.tsx`, `body-cells.tsx`, `editors.tsx`) imports its exports from here rather
|
|
3915
4194
|
// than re-deriving its own.
|
|
3916
|
-
const { ClassNamesProvider
|
|
4195
|
+
const { ClassNamesProvider, withContext: withContext$1, useClassNames } = createSlotRecipeContext(dataTable);
|
|
3917
4196
|
|
|
3918
4197
|
/**
|
|
3919
4198
|
* The pure filter itself, split out of `useSelectSearch` below so it can be
|
|
@@ -4489,26 +4768,87 @@ function ChipListEditor({ column, value, pending, onCommit, classNames }) {
|
|
|
4489
4768
|
}));
|
|
4490
4769
|
}
|
|
4491
4770
|
////////////////////////////////////////////////////////////////////////////////////
|
|
4492
|
-
// -- Feature C (#12): cell-expand "show full content" overlay --
|
|
4493
|
-
//
|
|
4494
|
-
//
|
|
4495
|
-
//
|
|
4496
|
-
//
|
|
4497
|
-
//
|
|
4498
|
-
//
|
|
4499
|
-
//
|
|
4500
|
-
|
|
4501
|
-
|
|
4771
|
+
// -- Feature C (#12): cell-expand "show full content" overlay -- a box that wraps
|
|
4772
|
+
// whatever a truncated cell would otherwise show inline: an editable column's OWN
|
|
4773
|
+
// editor node (unchanged, just relocated into this box instead of rendered flush in
|
|
4774
|
+
// the 36px row) for the editable case, or the plain full value for a read-only one.
|
|
4775
|
+
// `body-cells.tsx` decides WHEN to render this (a click on a cell whose content is
|
|
4776
|
+
// actually truncated, `truncate.ts`'s `isCellTruncated`); this component only
|
|
4777
|
+
// supplies the box.
|
|
4778
|
+
//
|
|
4779
|
+
// Portals to `document.body` (crispy-seed#1094's own pinned-column follow-up,
|
|
4780
|
+
// ported from the chip-list editor's popover, `body-cells.tsx`'s
|
|
4781
|
+
// `isChipListEditable` -- that one shipped this exact fix in 0.10.3) rather than
|
|
4782
|
+
// positioning `absolute` against its own cell: a `position: sticky` `<td>` clips ANY
|
|
4783
|
+
// descendant that grows past its own box, in every browser tested, regardless of
|
|
4784
|
+
// `overflow`/`z-index` on anything in between, so a pinned column's own truncated
|
|
4785
|
+
// cell used to clip this overlay's grown content instead of showing it in full. A
|
|
4786
|
+
// portal sidesteps that by not being a descendant of the sticky `<td>` at all once
|
|
4787
|
+
// mounted -- position is instead measured live off `anchorRef`'s own
|
|
4788
|
+
// `getBoundingClientRect()`, the same technique the chip-list popover already uses.
|
|
4789
|
+
function CellExpandOverlay({ className, anchorRef, contentRef, children }) {
|
|
4790
|
+
const [style, setStyle] = React.useState(null);
|
|
4791
|
+
// Grows 1px past the cell's own edges in every direction, matching this box's
|
|
4792
|
+
// pre-portal `top: -1px; insetInlineStart: -1px; insetInlineEnd: -1px` (the recipe's
|
|
4793
|
+
// `expandOverlay` class, `data-table.ts`) -- hides the 1px border seam against the
|
|
4794
|
+
// cell it's expanding out of. `minHeight`/`insetInlineEnd` from that same class no
|
|
4795
|
+
// longer apply once portaled (they resolved against the `<td>` as this box's own
|
|
4796
|
+
// containing block, meaningless once it's a `position: fixed` child of `<body>`
|
|
4797
|
+
// instead -- the same override the chip-list popover's own `measurePopoverPosition`,
|
|
4798
|
+
// `body-cells.tsx`, already makes), so this overrides both explicitly.
|
|
4799
|
+
const measure = React.useCallback(()=>{
|
|
4800
|
+
const cell = anchorRef.current;
|
|
4801
|
+
if (!cell) return;
|
|
4802
|
+
const rect = cell.getBoundingClientRect();
|
|
4803
|
+
setStyle({
|
|
4804
|
+
position: "fixed",
|
|
4805
|
+
top: rect.top - 1,
|
|
4806
|
+
insetInlineStart: rect.left - 1,
|
|
4807
|
+
insetInlineEnd: "auto",
|
|
4808
|
+
width: rect.width + 2,
|
|
4809
|
+
minHeight: "auto",
|
|
4810
|
+
zIndex: 1000
|
|
4811
|
+
});
|
|
4812
|
+
}, [
|
|
4813
|
+
anchorRef
|
|
4814
|
+
]);
|
|
4815
|
+
// Measured before paint, on mount -- see this component's own prop doc comment for
|
|
4816
|
+
// why it can't just trust a caller to have measured first.
|
|
4817
|
+
React.useLayoutEffect(()=>{
|
|
4818
|
+
measure();
|
|
4819
|
+
}, [
|
|
4820
|
+
measure
|
|
4821
|
+
]);
|
|
4822
|
+
// Same capture-phase listeners as the chip-list popover's own reposition effect
|
|
4823
|
+
// (`body-cells.tsx`) -- `scroll` doesn't bubble, so the table's own horizontal/
|
|
4824
|
+
// vertical scroll container (an arbitrary ancestor, not necessarily `window` itself)
|
|
4825
|
+
// is only observable this way.
|
|
4826
|
+
React.useEffect(()=>{
|
|
4827
|
+
window.addEventListener("scroll", measure, true);
|
|
4828
|
+
window.addEventListener("resize", measure);
|
|
4829
|
+
return ()=>{
|
|
4830
|
+
window.removeEventListener("scroll", measure, true);
|
|
4831
|
+
window.removeEventListener("resize", measure);
|
|
4832
|
+
};
|
|
4833
|
+
}, [
|
|
4834
|
+
measure
|
|
4835
|
+
]);
|
|
4836
|
+
// Nothing to portal until the first measurement lands (avoids a one-frame flash at
|
|
4837
|
+
// the origin corner before `useLayoutEffect` runs).
|
|
4838
|
+
if (!style) return null;
|
|
4839
|
+
return /*#__PURE__*/ createPortal(// `stopPropagation` -- an overlay sits on top of (and past) its own cell/row;
|
|
4502
4840
|
// a click anywhere inside it must never ALSO fire the row's own `onRowClick`,
|
|
4503
4841
|
// the same guard every other interactive cell affordance in this directory
|
|
4504
4842
|
// already applies (`LinkCell`, `EntityCell`'s preview button, editors.tsx's
|
|
4505
4843
|
// `TextEditor` root).
|
|
4506
4844
|
// biome-ignore lint/a11y/noStaticElementInteractions: not a widget -- this div never responds to interaction on ITSELF, only stops propagation of events that already occurred on a real interactive descendant (the editor/text it wraps). No role fits it because it isn't a control.
|
|
4507
4845
|
/*#__PURE__*/ jsx("div", {
|
|
4846
|
+
ref: contentRef,
|
|
4508
4847
|
className: className,
|
|
4848
|
+
style: style,
|
|
4509
4849
|
onClick: (event)=>event.stopPropagation(),
|
|
4510
4850
|
children: children
|
|
4511
|
-
}));
|
|
4851
|
+
}), document.body);
|
|
4512
4852
|
}
|
|
4513
4853
|
|
|
4514
4854
|
// Row-narrowing for the toolbar's search box -- split out of DataTable.tsx the way
|
|
@@ -4604,39 +4944,37 @@ function isCellTruncated(cell) {
|
|
|
4604
4944
|
* close itself after the first tag commits, see `ChipListEditor`'s own doc
|
|
4605
4945
|
* comment).
|
|
4606
4946
|
*
|
|
4607
|
-
*
|
|
4608
|
-
*
|
|
4609
|
-
*
|
|
4610
|
-
*
|
|
4611
|
-
*
|
|
4612
|
-
* `<td>` clips ANY descendant content that grows past its own
|
|
4613
|
-
* scrollport, in EVERY browser tested, regardless of
|
|
4614
|
-
* element in between and regardless of how high its
|
|
4615
|
-
* of which is a stacking-context or overflow bug
|
|
4616
|
-
* INSIDE. A portal sidesteps it entirely by not being a
|
|
4617
|
-
* `<td>` at all once mounted.
|
|
4618
|
-
* OTHER gated editors' `CellExpandOverlay` too when their own column is pinned,
|
|
4619
|
-
* but porting all of Feature C to this same portal model is its own change,
|
|
4620
|
-
* out of scope here -- this fix is scoped to the one editor crispy-seed#1094
|
|
4621
|
-
* actually reported against a pinned column. */ function isChipListEditable(column) {
|
|
4947
|
+
* This one renders through its OWN REAL React portal to `document.body`
|
|
4948
|
+
* (`DataTableBodyCell`'s own `popoverStyle`) rather than `editors.tsx`'s shared
|
|
4949
|
+
* `CellExpandOverlay`, since its shape is different (Attio's own click-anywhere-
|
|
4950
|
+
* opens gesture, no truncation gate). `CellExpandOverlay` itself ALSO portals to
|
|
4951
|
+
* `document.body` now (ported from this exact fix) -- both exist because a
|
|
4952
|
+
* `position: sticky` `<td>` clips ANY descendant content that grows past its own
|
|
4953
|
+
* box to the table's scrollport, in EVERY browser tested, regardless of
|
|
4954
|
+
* `overflow: visible` on every element in between and regardless of how high its
|
|
4955
|
+
* `z-index` is raised -- neither of which is a stacking-context or overflow bug
|
|
4956
|
+
* fixable from the INSIDE. A portal sidesteps it entirely by not being a
|
|
4957
|
+
* descendant of the sticky `<td>` at all once mounted. */ function isChipListEditable(column) {
|
|
4622
4958
|
return !!column.editable && column.type === "chip-list";
|
|
4623
4959
|
}
|
|
4624
|
-
function ColumnBodyCells({ columns, row, classNames, locale, selectable, stickyFirst, stickyLast }) {
|
|
4960
|
+
function ColumnBodyCells({ columns, row, classNames, locale, selectable, stickyFirst, stickyLast, cellContextMenu, onViewEditHistory, cellContextMenuItems }) {
|
|
4625
4961
|
const effective = useEffectiveColumns(columns, {
|
|
4626
4962
|
selectable,
|
|
4627
4963
|
stickyFirst
|
|
4628
4964
|
});
|
|
4629
4965
|
return /*#__PURE__*/ jsx(Fragment, {
|
|
4630
|
-
children: effective.map((entry
|
|
4966
|
+
children: effective.map((entry)=>{
|
|
4631
4967
|
const { column } = entry;
|
|
4632
|
-
const
|
|
4633
|
-
const sticky = pinnedCellProps(entry, stickyLast, isLast);
|
|
4968
|
+
const sticky = pinnedCellProps(entry, stickyLast);
|
|
4634
4969
|
return /*#__PURE__*/ jsx(DataTableBodyCell, {
|
|
4635
4970
|
column: column,
|
|
4636
4971
|
row: row,
|
|
4637
4972
|
classNames: classNames,
|
|
4638
4973
|
locale: locale,
|
|
4639
|
-
sticky: sticky
|
|
4974
|
+
sticky: sticky,
|
|
4975
|
+
cellContextMenu: cellContextMenu,
|
|
4976
|
+
onViewEditHistory: onViewEditHistory,
|
|
4977
|
+
cellContextMenuItems: cellContextMenuItems
|
|
4640
4978
|
}, column.key);
|
|
4641
4979
|
})
|
|
4642
4980
|
});
|
|
@@ -4648,7 +4986,7 @@ function ColumnBodyCells({ columns, row, classNames, locale, selectable, stickyF
|
|
|
4648
4986
|
* on every row's render. React keeps this instance stable across re-renders because
|
|
4649
4987
|
* `ColumnBodyCells` keys it by `column.key` the same way it always keyed the bare
|
|
4650
4988
|
* `<DataTableCell>` before this split.
|
|
4651
|
-
*/ function DataTableBodyCell({ column, row, classNames, locale, sticky }) {
|
|
4989
|
+
*/ function DataTableBodyCell({ column, row, classNames, locale, sticky, cellContextMenu, onViewEditHistory, cellContextMenuItems }) {
|
|
4652
4990
|
const { startEdit, cancelEdit, isEditing, isPending, commitEdit, isPeeking, openPeek, closePeek } = useDataTableContext();
|
|
4653
4991
|
const cellRef = React.useRef(null);
|
|
4654
4992
|
// Feature B rework (#8) -- the chip-list popover's own live position, in VIEWPORT
|
|
@@ -4664,6 +5002,16 @@ function ColumnBodyCells({ columns, row, classNames, locale, selectable, stickyF
|
|
|
4664
5002
|
// editable cell's existing double-click-to-edit (tranche 5, unchanged) must keep
|
|
4665
5003
|
// rendering exactly as it always has, not suddenly gain overlay chrome.
|
|
4666
5004
|
const [overlayOpen, setOverlayOpen] = React.useState(false);
|
|
5005
|
+
// Attio parity (attio-column-features-full.md gap #1) -- the right-click menu's own
|
|
5006
|
+
// point + open state. `contextMenuPoint` is frozen at the moment of the right-click
|
|
5007
|
+
// (never re-read live), same reasoning as `overlayOpen` above: the menu shouldn't
|
|
5008
|
+
// chase the cursor after it opens. `<CellContextMenu>` itself is only rendered below
|
|
5009
|
+
// while `contextMenuOpen` is true (not permanently mounted per cell) -- unlike
|
|
5010
|
+
// `ColumnMenu` (one per COLUMN, header row only), this component runs once per
|
|
5011
|
+
// row×column cell, and a floating-ui instance genuinely idle in every one of
|
|
5012
|
+
// thousands of cells would be real, avoidable overhead.
|
|
5013
|
+
const [contextMenuPoint, setContextMenuPoint] = React.useState(null);
|
|
5014
|
+
const [contextMenuOpen, setContextMenuOpen] = React.useState(false);
|
|
4667
5015
|
const alwaysLive = isAlwaysLiveEditable(column);
|
|
4668
5016
|
const chipListEditable = isChipListEditable(column);
|
|
4669
5017
|
// A gated `editable` column (everything except the always-live widgets AND the
|
|
@@ -4715,6 +5063,19 @@ function ColumnBodyCells({ columns, row, classNames, locale, selectable, stickyF
|
|
|
4715
5063
|
setOverlayOpen(isCellTruncated(event.currentTarget));
|
|
4716
5064
|
startEdit(row.id, column.key);
|
|
4717
5065
|
}
|
|
5066
|
+
function handleContextMenu(event) {
|
|
5067
|
+
if (!cellContextMenu) return;
|
|
5068
|
+
// Replaces the browser's own native context menu (Attio's own gesture) -- and
|
|
5069
|
+
// must not also reach the row's own `onRowClick`, same reasoning as every other
|
|
5070
|
+
// cell-local click handler in this file.
|
|
5071
|
+
event.preventDefault();
|
|
5072
|
+
event.stopPropagation();
|
|
5073
|
+
setContextMenuPoint({
|
|
5074
|
+
x: event.clientX,
|
|
5075
|
+
y: event.clientY
|
|
5076
|
+
});
|
|
5077
|
+
setContextMenuOpen(true);
|
|
5078
|
+
}
|
|
4718
5079
|
function handleClick(event) {
|
|
4719
5080
|
if (chipListEditable) {
|
|
4720
5081
|
// Attio's own chip-list gesture: click opens the editor regardless of
|
|
@@ -4752,6 +5113,16 @@ function ColumnBodyCells({ columns, row, classNames, locale, selectable, stickyF
|
|
|
4752
5113
|
// mounted, in `document.body` -- see `isChipListEditable`'s doc comment) and by
|
|
4753
5114
|
// the reposition effect further down.
|
|
4754
5115
|
const popoverRef = React.useRef(null);
|
|
5116
|
+
// Feature C's own portaled overlay (`CellExpandOverlay`, editors.tsx) -- its own DOM
|
|
5117
|
+
// node, once mounted, same reasoning as `popoverRef` above: it lives outside
|
|
5118
|
+
// `cellRef`'s subtree (a sibling of the table in `document.body`, not a descendant of
|
|
5119
|
+
// the `<td>`), so the outside-click detector below needs a ref to it too. Without
|
|
5120
|
+
// this, `isInside` saw every click/keystroke INSIDE the portaled editor as "outside"
|
|
5121
|
+
// once the overlay actually escaped the `<td>` (this fix's whole point) -- e.g.
|
|
5122
|
+
// typing into the overlay's own `TextEditor` fires a pointerdown on an input that is
|
|
5123
|
+
// no longer `cellRef.current`'s descendant, so every keystroke closed the overlay and
|
|
5124
|
+
// cancelled the edit before the user finished typing.
|
|
5125
|
+
const expandOverlayRef = React.useRef(null);
|
|
4755
5126
|
// Closes the overlay on Escape or an outside click/pointerdown -- a gated
|
|
4756
5127
|
// editor's OWN Escape/blur handling already ends `isEditing` on its own (see
|
|
4757
5128
|
// `TextEditor`/`SelectEditor`'s existing onCancel/onOpenChange wiring), which
|
|
@@ -4764,7 +5135,7 @@ function ColumnBodyCells({ columns, row, classNames, locale, selectable, stickyF
|
|
|
4764
5135
|
React.useEffect(()=>{
|
|
4765
5136
|
if (!overlayOpen) return;
|
|
4766
5137
|
function isInside(target) {
|
|
4767
|
-
return (cellRef.current?.contains(target) ?? false) || (popoverRef.current?.contains(target) ?? false);
|
|
5138
|
+
return (cellRef.current?.contains(target) ?? false) || (popoverRef.current?.contains(target) ?? false) || (expandOverlayRef.current?.contains(target) ?? false);
|
|
4768
5139
|
}
|
|
4769
5140
|
function handlePointerDown(event) {
|
|
4770
5141
|
if (!isInside(event.target)) {
|
|
@@ -4818,7 +5189,7 @@ function ColumnBodyCells({ columns, row, classNames, locale, selectable, stickyF
|
|
|
4818
5189
|
const pending = isPending(row.id, column.key);
|
|
4819
5190
|
return /*#__PURE__*/ jsxs(Fragment, {
|
|
4820
5191
|
children: [
|
|
4821
|
-
/*#__PURE__*/
|
|
5192
|
+
/*#__PURE__*/ jsxs(DataTableCell, {
|
|
4822
5193
|
ref: cellRef,
|
|
4823
5194
|
"data-numeric": dataAttr(column.type === "number" || column.type === "currency"),
|
|
4824
5195
|
"data-editable": dataAttr(!!column.editable),
|
|
@@ -4829,14 +5200,36 @@ function ColumnBodyCells({ columns, row, classNames, locale, selectable, stickyF
|
|
|
4829
5200
|
style: sticky.style,
|
|
4830
5201
|
onClick: handleClick,
|
|
4831
5202
|
onDoubleClick: handleDoubleClick,
|
|
4832
|
-
|
|
4833
|
-
|
|
4834
|
-
|
|
4835
|
-
|
|
4836
|
-
|
|
4837
|
-
|
|
4838
|
-
|
|
4839
|
-
|
|
5203
|
+
onContextMenu: handleContextMenu,
|
|
5204
|
+
children: [
|
|
5205
|
+
/*#__PURE__*/ jsx(CellContent, {
|
|
5206
|
+
column: column,
|
|
5207
|
+
row: row,
|
|
5208
|
+
rowId: row.id,
|
|
5209
|
+
classNames: classNames,
|
|
5210
|
+
locale: locale,
|
|
5211
|
+
overlayActive: overlayActive,
|
|
5212
|
+
cellRef: cellRef,
|
|
5213
|
+
expandOverlayRef: expandOverlayRef
|
|
5214
|
+
}),
|
|
5215
|
+
cellContextMenu && contextMenuOpen && contextMenuPoint && // A `<td>` (unlike its own parent `<tr>`) can validly hold any flow
|
|
5216
|
+
// content, so `CellContextMenu`'s own 0x0 `MenuAnchor` -- itself never
|
|
5217
|
+
// portaled, only `MenuPositioner`'s actual dropdown content is (via
|
|
5218
|
+
// floating-ui's own `FloatingPortal`, straight to `document.body`) --
|
|
5219
|
+
// renders here, INSIDE this cell, rather than as a sibling of it.
|
|
5220
|
+
/*#__PURE__*/ jsx(CellContextMenu, {
|
|
5221
|
+
column: column,
|
|
5222
|
+
row: row,
|
|
5223
|
+
value: rawValue,
|
|
5224
|
+
locale: locale,
|
|
5225
|
+
point: contextMenuPoint,
|
|
5226
|
+
open: contextMenuOpen,
|
|
5227
|
+
onOpenChange: setContextMenuOpen,
|
|
5228
|
+
onCommit: (value)=>commitEdit(row.id, column.key, value),
|
|
5229
|
+
onViewEditHistory: onViewEditHistory,
|
|
5230
|
+
cellContextMenuItems: cellContextMenuItems
|
|
5231
|
+
})
|
|
5232
|
+
]
|
|
4840
5233
|
}),
|
|
4841
5234
|
chipListEditorOpen && popoverStyle && /*#__PURE__*/ createPortal(// biome-ignore lint/a11y/noStaticElementInteractions: not a widget -- same shape as `CellExpandOverlay` (editors.tsx), which this box is a portaled variant of: it never responds to interaction on ITSELF, only stops propagation of clicks that already occurred on a real interactive descendant (the editor's input/remove buttons). No role fits it because it isn't a control.
|
|
4842
5235
|
/*#__PURE__*/ jsx("div", {
|
|
@@ -4862,7 +5255,7 @@ function ColumnBodyCells({ columns, row, classNames, locale, selectable, stickyF
|
|
|
4862
5255
|
* display (a custom `render` wins there, same as before `editable` existed; it never
|
|
4863
5256
|
* overrides an actual editing/always-live state, matching crisp's own split between
|
|
4864
5257
|
* `renderTyped`'s rest-state branch and the editing branches above it).
|
|
4865
|
-
*/ function CellContent({ column, row, rowId, classNames, locale, overlayActive }) {
|
|
5258
|
+
*/ function CellContent({ column, row, rowId, classNames, locale, overlayActive, cellRef, expandOverlayRef }) {
|
|
4866
5259
|
const { isEditing, isPending, getEditError, commitEdit, cancelEdit } = useDataTableContext();
|
|
4867
5260
|
const rawValue = getColumnValue(column, row);
|
|
4868
5261
|
const pending = isPending(rowId, column.key);
|
|
@@ -4956,6 +5349,8 @@ function ColumnBodyCells({ columns, row, classNames, locale, selectable, stickyF
|
|
|
4956
5349
|
children: [
|
|
4957
5350
|
overlayActive ? /*#__PURE__*/ jsx(CellExpandOverlay, {
|
|
4958
5351
|
className: classNames.expandOverlay,
|
|
5352
|
+
anchorRef: cellRef,
|
|
5353
|
+
contentRef: expandOverlayRef,
|
|
4959
5354
|
children: column.editable ? node : /*#__PURE__*/ jsx("span", {
|
|
4960
5355
|
className: classNames.expandOverlayText,
|
|
4961
5356
|
children: node
|
|
@@ -5066,64 +5461,52 @@ function DisplayCellContent({ column, row, value, classNames, locale }) {
|
|
|
5066
5461
|
}
|
|
5067
5462
|
}
|
|
5068
5463
|
|
|
5069
|
-
const
|
|
5070
|
-
const
|
|
5071
|
-
const
|
|
5072
|
-
|
|
5073
|
-
|
|
5074
|
-
|
|
5075
|
-
|
|
5076
|
-
|
|
5077
|
-
|
|
5078
|
-
|
|
5079
|
-
|
|
5080
|
-
|
|
5081
|
-
|
|
5082
|
-
|
|
5083
|
-
|
|
5084
|
-
|
|
5085
|
-
|
|
5086
|
-
|
|
5087
|
-
|
|
5464
|
+
const DataTableBulkBarRoot = withContext$1(Primitive.div, "bulkBar");
|
|
5465
|
+
const DataTableBulkBarCount = withContext$1(Primitive.span, "bulkBarCount");
|
|
5466
|
+
const DataTableBulkBarClear = withContext$1(Primitive.button, "bulkBarClear");
|
|
5467
|
+
/** Wraps whatever the CONSUMER passes as this bar's own action buttons -- the bar
|
|
5468
|
+
* itself only ever supplies chrome (count + clear); every actual action (Add to
|
|
5469
|
+
* list, Send email, …) is the consumer's own `<ActionButton>`/`<Menu>`, the same
|
|
5470
|
+
* "chrome vs. consumer content" split `toolbarActions`/`toolbarEnd` already draw for
|
|
5471
|
+
* the toolbar's own custom actions. */ const DataTableBulkBarActions = withContext$1(Primitive.div, "bulkBarActions");
|
|
5472
|
+
/**
|
|
5473
|
+
* The floating bulk-action pill (gap #4) -- renders nothing while no row is selected
|
|
5474
|
+
* (reads `selectedIds` off the table-wide context itself, so a consumer doesn't have
|
|
5475
|
+
* to gate this on `selectable`/`selectedIds` on their own). Composed as the LAST
|
|
5476
|
+
* child of the table's own root (`DataTable.tsx`), which is `position: relative` for
|
|
5477
|
+
* exactly this reason -- see `data-table.ts`'s own `bulkBar` slot comment for why its
|
|
5478
|
+
* geometry borrows `contextual-floating-button`'s already-measured vars rather than a
|
|
5479
|
+
* guessed literal.
|
|
5480
|
+
*/ function DataTableBulkBar({ clearLabel = "Clear selection", children }) {
|
|
5481
|
+
const { selectedIds, clearSelection } = useDataTableContext();
|
|
5482
|
+
if (selectedIds.length === 0) return null;
|
|
5483
|
+
return /*#__PURE__*/ jsxs(DataTableBulkBarRoot, {
|
|
5484
|
+
role: "toolbar",
|
|
5485
|
+
"aria-label": "Bulk actions",
|
|
5486
|
+
"aria-orientation": "horizontal",
|
|
5487
|
+
children: [
|
|
5488
|
+
/*#__PURE__*/ jsxs(DataTableBulkBarCount, {
|
|
5489
|
+
"aria-live": "polite",
|
|
5490
|
+
children: [
|
|
5491
|
+
selectedIds.length,
|
|
5492
|
+
" selected"
|
|
5493
|
+
]
|
|
5494
|
+
}),
|
|
5495
|
+
/*#__PURE__*/ jsx(DataTableBulkBarClear, {
|
|
5496
|
+
type: "button",
|
|
5497
|
+
"aria-label": clearLabel,
|
|
5498
|
+
onClick: clearSelection,
|
|
5499
|
+
children: /*#__PURE__*/ jsx(IconXmarkLine_default, {
|
|
5500
|
+
size: 12,
|
|
5501
|
+
"aria-hidden": "true"
|
|
5502
|
+
})
|
|
5503
|
+
}),
|
|
5504
|
+
children && /*#__PURE__*/ jsx(DataTableBulkBarActions, {
|
|
5505
|
+
children: children(selectedIds)
|
|
5088
5506
|
})
|
|
5089
|
-
|
|
5090
|
-
});
|
|
5091
|
-
};
|
|
5092
|
-
Menu.Anchor;
|
|
5093
|
-
const MenuTrigger = Menu.Trigger;
|
|
5094
|
-
const MenuPositioner = /*#__PURE__*/ React.forwardRef(({ className, ...props }, ref)=>{
|
|
5095
|
-
const classNames = useClassNames();
|
|
5096
|
-
return /*#__PURE__*/ jsx(Menu.Positioner, {
|
|
5097
|
-
ref: ref,
|
|
5098
|
-
className: clsx(classNames.positioner, className),
|
|
5099
|
-
...props
|
|
5100
|
-
});
|
|
5101
|
-
});
|
|
5102
|
-
MenuPositioner.displayName = "MenuPositioner";
|
|
5103
|
-
const MenuContent = withContext$1(Menu.Content, "content");
|
|
5104
|
-
const MenuScrollArea = withContext$1(withStateProps(Primitive.div), "scrollArea");
|
|
5105
|
-
withContext$1(Menu.Group, "group");
|
|
5106
|
-
withContext$1(Menu.GroupLabel, "groupLabel");
|
|
5107
|
-
const MenuItem = /*#__PURE__*/ React.forwardRef(({ className: propClassName, ...props }, ref)=>{
|
|
5108
|
-
const [variantProps, otherProps] = menuItem.splitVariantProps(props);
|
|
5109
|
-
const parentProps = useItemProps();
|
|
5110
|
-
const classNames = menuItem({
|
|
5111
|
-
...parentProps,
|
|
5112
|
-
...variantProps
|
|
5113
|
-
});
|
|
5114
|
-
return /*#__PURE__*/ jsx(ItemClassNamesProvider, {
|
|
5115
|
-
value: classNames,
|
|
5116
|
-
children: /*#__PURE__*/ jsx(Menu.Item, {
|
|
5117
|
-
ref: ref,
|
|
5118
|
-
className: clsx(classNames.root, propClassName),
|
|
5119
|
-
...otherProps
|
|
5120
|
-
})
|
|
5507
|
+
]
|
|
5121
5508
|
});
|
|
5122
|
-
}
|
|
5123
|
-
MenuItem.displayName = "MenuItem";
|
|
5124
|
-
withItemContext(withItemStateProps(Primitive.div), "body");
|
|
5125
|
-
const MenuItemLabel = withItemContext(withItemStateProps(Primitive.span), "label");
|
|
5126
|
-
withItemContext(withItemStateProps(Primitive.span), "description");
|
|
5509
|
+
}
|
|
5127
5510
|
|
|
5128
5511
|
function ColumnCalcCells({ columns, rows, selectable, stickyFirst, stickyLast }) {
|
|
5129
5512
|
const effective = useEffectiveColumns(columns, {
|
|
@@ -5131,10 +5514,9 @@ function ColumnCalcCells({ columns, rows, selectable, stickyFirst, stickyLast })
|
|
|
5131
5514
|
stickyFirst
|
|
5132
5515
|
});
|
|
5133
5516
|
return /*#__PURE__*/ jsx(Fragment, {
|
|
5134
|
-
children: effective.map((entry
|
|
5517
|
+
children: effective.map((entry)=>{
|
|
5135
5518
|
const { column } = entry;
|
|
5136
|
-
const
|
|
5137
|
-
const sticky = pinnedCellProps(entry, stickyLast, isLast);
|
|
5519
|
+
const sticky = pinnedCellProps(entry, stickyLast);
|
|
5138
5520
|
return /*#__PURE__*/ jsx(DataTableFooterCell, {
|
|
5139
5521
|
"data-sticky": sticky["data-sticky"],
|
|
5140
5522
|
"data-sticky-edge": sticky["data-sticky-edge"],
|
|
@@ -5150,10 +5532,11 @@ function ColumnCalcCells({ columns, rows, selectable, stickyFirst, stickyLast })
|
|
|
5150
5532
|
})
|
|
5151
5533
|
});
|
|
5152
5534
|
}
|
|
5153
|
-
function formatCalcValue(value) {
|
|
5154
|
-
|
|
5535
|
+
function formatCalcValue(value, op) {
|
|
5536
|
+
const formatted = value.toLocaleString(undefined, {
|
|
5155
5537
|
maximumFractionDigits: 2
|
|
5156
5538
|
});
|
|
5539
|
+
return op === "percentEmpty" || op === "percentFilled" ? `${formatted}%` : formatted;
|
|
5157
5540
|
}
|
|
5158
5541
|
/**
|
|
5159
5542
|
* One ColumnCalc footer cell -- a click-anywhere-in-the-cell trigger (crisp's own
|
|
@@ -5188,7 +5571,7 @@ function formatCalcValue(value) {
|
|
|
5188
5571
|
children: [
|
|
5189
5572
|
/*#__PURE__*/ jsx("span", {
|
|
5190
5573
|
className: "seed-data-table-column-calc-value",
|
|
5191
|
-
children: formatCalcValue(result)
|
|
5574
|
+
children: formatCalcValue(result, op)
|
|
5192
5575
|
}),
|
|
5193
5576
|
/*#__PURE__*/ jsx("span", {
|
|
5194
5577
|
className: "seed-data-table-column-calc-op",
|
|
@@ -5209,21 +5592,13 @@ function formatCalcValue(value) {
|
|
|
5209
5592
|
style: {
|
|
5210
5593
|
minWidth: 160
|
|
5211
5594
|
},
|
|
5212
|
-
children: /*#__PURE__*/
|
|
5213
|
-
children:
|
|
5214
|
-
|
|
5215
|
-
onClick: ()=>setColumnCalcOp(column.key, candidate),
|
|
5216
|
-
children: /*#__PURE__*/ jsx(MenuItemLabel, {
|
|
5217
|
-
children: DATA_TABLE_CALC_LABEL[candidate]
|
|
5218
|
-
})
|
|
5219
|
-
}, candidate)),
|
|
5220
|
-
op && /*#__PURE__*/ jsx(MenuItem, {
|
|
5221
|
-
onClick: ()=>setColumnCalcOp(column.key, undefined),
|
|
5595
|
+
children: /*#__PURE__*/ jsx(MenuScrollArea, {
|
|
5596
|
+
children: ops.map((candidate)=>/*#__PURE__*/ jsx(MenuItem, {
|
|
5597
|
+
onClick: ()=>setColumnCalcOp(column.key, candidate),
|
|
5222
5598
|
children: /*#__PURE__*/ jsx(MenuItemLabel, {
|
|
5223
|
-
children:
|
|
5599
|
+
children: DATA_TABLE_CALC_LABEL[candidate]
|
|
5224
5600
|
})
|
|
5225
|
-
})
|
|
5226
|
-
]
|
|
5601
|
+
}, candidate))
|
|
5227
5602
|
})
|
|
5228
5603
|
})
|
|
5229
5604
|
})
|
|
@@ -5364,10 +5739,9 @@ const DEFAULT_SORT_ICON = {
|
|
|
5364
5739
|
});
|
|
5365
5740
|
const [renamingKey, setRenamingKey] = React.useState(null);
|
|
5366
5741
|
return /*#__PURE__*/ jsx(Fragment, {
|
|
5367
|
-
children: effective.map((entry
|
|
5742
|
+
children: effective.map((entry)=>{
|
|
5368
5743
|
const { column, label, width } = entry;
|
|
5369
|
-
const
|
|
5370
|
-
const sticky = pinnedCellProps(entry, stickyLast, isLast);
|
|
5744
|
+
const sticky = pinnedCellProps(entry, stickyLast);
|
|
5371
5745
|
return /*#__PURE__*/ jsxs(DataTableHeaderCell, {
|
|
5372
5746
|
columnKey: column.key,
|
|
5373
5747
|
sortable: column.sortable,
|
|
@@ -10488,6 +10862,18 @@ ContinuousDatePicker.displayName = "ContinuousDatePicker";
|
|
|
10488
10862
|
// value list) down to `is`/`is not`/`empty`/`not empty`. `"isanyof"` is appended to any
|
|
10489
10863
|
// of the four whenever the column supplies `filterValues` -- a faceted checkbox list,
|
|
10490
10864
|
// independent of the column's own kind.
|
|
10865
|
+
//
|
|
10866
|
+
// Multi-select fix (DATATABLE-ATTIO-SPEC.md gap #12, live-measured 2026-09-30): a
|
|
10867
|
+
// `kind: "select"` column whose CELL is multi-valued (`type: "tags"`/`"chip-list"` --
|
|
10868
|
+
// a chip-list/tag column, not a single status pill) offers Attio's own
|
|
10869
|
+
// `contains`/`does not contain` instead of `is`/`is not` -- "is" reads as an exact-
|
|
10870
|
+
// set-equality check against an array value, which is not what Attio's own multi-
|
|
10871
|
+
// select filter means or what `applyToolbarFilters`' `is`/`isnot` branch (a plain
|
|
10872
|
+
// string-equality compare via `asText`) computes. `contains`/`doesnotcontain` are
|
|
10873
|
+
// already correct here unmodified: `applyToolbarFilters` already runs them against
|
|
10874
|
+
// `asText(raw)` (an array's own values joined), which is exactly "does any tag
|
|
10875
|
+
// match". Single-select (no `filterValues`-array cell type) keeps `is`/`is not` --
|
|
10876
|
+
// not re-measured against Attio, so left alone per the discovery doc's own hedge.
|
|
10491
10877
|
////////////////////////////////////////////////////////////////////////////////////
|
|
10492
10878
|
const OP_ORDER = [
|
|
10493
10879
|
"contains",
|
|
@@ -10521,9 +10907,19 @@ const SELECT_OP_ORDER = [
|
|
|
10521
10907
|
"empty",
|
|
10522
10908
|
"notempty"
|
|
10523
10909
|
];
|
|
10910
|
+
const MULTI_SELECT_OP_ORDER = [
|
|
10911
|
+
"contains",
|
|
10912
|
+
"doesnotcontain",
|
|
10913
|
+
"empty",
|
|
10914
|
+
"notempty"
|
|
10915
|
+
];
|
|
10524
10916
|
/** The operators a column of this `kind` offers, in display order -- `hasValues`
|
|
10525
|
-
* (the column supplies `filterValues`) appends `"isanyof"` regardless of kind.
|
|
10526
|
-
|
|
10917
|
+
* (the column supplies `filterValues`) appends `"isanyof"` regardless of kind.
|
|
10918
|
+
* `multiValue` (the column's CELL type is a chip-list/tags column, not just its
|
|
10919
|
+
* filter `kind`) swaps `SELECT_OP_ORDER`'s `is`/`is not` for `contains`/`does not
|
|
10920
|
+
* contain`, matching Attio's own multi-select filter -- see the operator-sets
|
|
10921
|
+
* comment above. */ function opsForKind(kind, hasValues, multiValue = false) {
|
|
10922
|
+
const base = kind === "number" ? NUMBER_OP_ORDER : kind === "date" ? DATE_OP_ORDER : kind === "select" ? multiValue ? MULTI_SELECT_OP_ORDER : SELECT_OP_ORDER : OP_ORDER;
|
|
10527
10923
|
return hasValues ? [
|
|
10528
10924
|
...base,
|
|
10529
10925
|
"isanyof"
|
|
@@ -10891,66 +11287,58 @@ const formatFilterDate = (iso)=>{
|
|
|
10891
11287
|
return `${OPERATOR_LABELS[filter.op]} ${value}`;
|
|
10892
11288
|
}
|
|
10893
11289
|
/**
|
|
10894
|
-
* One filter condition, INSIDE the Filter popover --
|
|
10895
|
-
* (
|
|
10896
|
-
*
|
|
10897
|
-
*
|
|
10898
|
-
*
|
|
10899
|
-
*
|
|
10900
|
-
*
|
|
10901
|
-
*
|
|
10902
|
-
*
|
|
10903
|
-
|
|
11290
|
+
* One filter condition, INSIDE the Filter popover -- Attio's own compact chip shape
|
|
11291
|
+
* (DATATABLE-ATTIO-SPEC.md gap #12, re-measured live 2026-09-30): `[attribute ▾]
|
|
11292
|
+
* [operator …] [value] [remove ×]`, ALL of it visible at once, never behind a click-
|
|
11293
|
+
* to-expand accordion. Supersedes this file's earlier 2026-09-28 note (which read
|
|
11294
|
+
* Attio as keeping conditions collapsed behind a bare "Filter · N" count and put them
|
|
11295
|
+
* in an accordion for that reason) -- the fresh read found the opposite: every active
|
|
11296
|
+
* condition shows attribute/operator/value simultaneously. This keeps the outer shell
|
|
11297
|
+
* (the "Filter"/"Filter · N" trigger opening ONE popover, `SortControl`'s own
|
|
11298
|
+
* list-of-rows shape) rather than moving chips into the toolbar row itself, so the
|
|
11299
|
+
* already-covered trigger/`Add filter` DOM (`DataTable.test.tsx`) stays put; only the
|
|
11300
|
+
* PER-CONDITION row tightens, from an accordion to an always-open card. The operator
|
|
11301
|
+
* set stays a row of small pill buttons (not a dropdown) so every existing test that
|
|
11302
|
+
* clicks an operator by its exact button name keeps working unchanged.
|
|
11303
|
+
*/ function FilterConditionRow({ filter, columns }) {
|
|
10904
11304
|
const { patchToolbarFilter, changeToolbarFilterField, removeToolbarFilter } = useDataTableContext();
|
|
10905
11305
|
const column = columns.find((c)=>c.key === filter.key);
|
|
10906
11306
|
const kind = column?.kind;
|
|
11307
|
+
const multiValue = column?.type === "tags" || column?.type === "chip-list";
|
|
10907
11308
|
const values = column?.filterValues ?? [];
|
|
10908
|
-
const ops = opsForKind(kind, values.length > 0);
|
|
10909
|
-
return /*#__PURE__*/ jsxs("
|
|
11309
|
+
const ops = opsForKind(kind, values.length > 0, multiValue);
|
|
11310
|
+
return /*#__PURE__*/ jsxs("fieldset", {
|
|
11311
|
+
"aria-label": `${column ? columnLabel(column) : filter.key} filter: ${conditionSummary(filter, column)}`,
|
|
10910
11312
|
style: {
|
|
11313
|
+
margin: 0,
|
|
10911
11314
|
display: "flex",
|
|
10912
11315
|
flexDirection: "column",
|
|
10913
|
-
gap:
|
|
11316
|
+
gap: 8,
|
|
10914
11317
|
borderRadius: 6,
|
|
10915
|
-
|
|
10916
|
-
|
|
11318
|
+
border: "1px solid var(--seed-color-border-neutral)",
|
|
11319
|
+
background: "var(--seed-color-bg-neutral-weak)",
|
|
11320
|
+
padding: 6
|
|
10917
11321
|
},
|
|
10918
11322
|
children: [
|
|
10919
11323
|
/*#__PURE__*/ jsxs("div", {
|
|
10920
11324
|
style: {
|
|
10921
11325
|
display: "flex",
|
|
10922
11326
|
alignItems: "center",
|
|
10923
|
-
gap:
|
|
11327
|
+
gap: 4
|
|
10924
11328
|
},
|
|
10925
11329
|
children: [
|
|
10926
|
-
/*#__PURE__*/
|
|
10927
|
-
|
|
10928
|
-
|
|
11330
|
+
/*#__PURE__*/ jsx(InlineSelect, {
|
|
11331
|
+
ariaLabel: "Filter by field",
|
|
11332
|
+
value: filter.key,
|
|
11333
|
+
onChange: (key)=>changeToolbarFilterField(filter.id, key),
|
|
11334
|
+
options: columns.map((c)=>({
|
|
11335
|
+
value: c.key,
|
|
11336
|
+
label: columnLabel(c)
|
|
11337
|
+
})),
|
|
10929
11338
|
style: {
|
|
10930
11339
|
flex: 1,
|
|
10931
|
-
minWidth: 0
|
|
10932
|
-
|
|
10933
|
-
padding: "6px 8px",
|
|
10934
|
-
border: "none",
|
|
10935
|
-
borderRadius: 6,
|
|
10936
|
-
background: "transparent",
|
|
10937
|
-
cursor: "pointer",
|
|
10938
|
-
fontSize: 13,
|
|
10939
|
-
overflow: "hidden",
|
|
10940
|
-
textOverflow: "ellipsis",
|
|
10941
|
-
whiteSpace: "nowrap"
|
|
10942
|
-
},
|
|
10943
|
-
children: [
|
|
10944
|
-
/*#__PURE__*/ jsx("strong", {
|
|
10945
|
-
style: {
|
|
10946
|
-
fontWeight: 500
|
|
10947
|
-
},
|
|
10948
|
-
children: column ? columnLabel(column) : filter.key
|
|
10949
|
-
}),
|
|
10950
|
-
":",
|
|
10951
|
-
" ",
|
|
10952
|
-
conditionSummary(filter, column)
|
|
10953
|
-
]
|
|
11340
|
+
minWidth: 0
|
|
11341
|
+
}
|
|
10954
11342
|
}),
|
|
10955
11343
|
/*#__PURE__*/ jsx("button", {
|
|
10956
11344
|
type: "button",
|
|
@@ -10964,136 +11352,103 @@ const formatFilterDate = (iso)=>{
|
|
|
10964
11352
|
})
|
|
10965
11353
|
]
|
|
10966
11354
|
}),
|
|
10967
|
-
|
|
11355
|
+
/*#__PURE__*/ jsx("div", {
|
|
10968
11356
|
style: {
|
|
10969
|
-
padding: "0 8px 4px",
|
|
10970
11357
|
display: "flex",
|
|
10971
|
-
|
|
10972
|
-
gap:
|
|
11358
|
+
flexWrap: "wrap",
|
|
11359
|
+
gap: 2
|
|
10973
11360
|
},
|
|
10974
|
-
children:
|
|
10975
|
-
|
|
10976
|
-
|
|
10977
|
-
|
|
10978
|
-
|
|
10979
|
-
|
|
10980
|
-
children: "Field"
|
|
10981
|
-
}),
|
|
10982
|
-
/*#__PURE__*/ jsx(InlineSelect, {
|
|
10983
|
-
ariaLabel: "Filter by field",
|
|
10984
|
-
value: filter.key,
|
|
10985
|
-
onChange: (key)=>changeToolbarFilterField(filter.id, key),
|
|
10986
|
-
options: columns.map((c)=>({
|
|
10987
|
-
value: c.key,
|
|
10988
|
-
label: columnLabel(c)
|
|
10989
|
-
}))
|
|
10990
|
-
}),
|
|
10991
|
-
/*#__PURE__*/ jsx("div", {
|
|
10992
|
-
style: {
|
|
10993
|
-
fontSize: 12,
|
|
10994
|
-
color: "var(--seed-color-fg-neutral-subtle)"
|
|
10995
|
-
},
|
|
10996
|
-
children: "Condition"
|
|
10997
|
-
}),
|
|
10998
|
-
/*#__PURE__*/ jsx("div", {
|
|
11361
|
+
children: ops.map((op)=>/*#__PURE__*/ jsx("button", {
|
|
11362
|
+
type: "button",
|
|
11363
|
+
"data-active": filter.op === op ? "" : undefined,
|
|
11364
|
+
onClick: ()=>patchToolbarFilter(filter.id, {
|
|
11365
|
+
op
|
|
11366
|
+
}),
|
|
10999
11367
|
style: {
|
|
11000
|
-
|
|
11001
|
-
|
|
11002
|
-
|
|
11368
|
+
textAlign: "left",
|
|
11369
|
+
padding: "4px 8px",
|
|
11370
|
+
borderRadius: 999,
|
|
11371
|
+
border: "none",
|
|
11372
|
+
cursor: "pointer",
|
|
11373
|
+
background: filter.op === op ? "var(--seed-color-bg-layer-default)" : "transparent",
|
|
11374
|
+
fontSize: 12
|
|
11003
11375
|
},
|
|
11004
|
-
children:
|
|
11005
|
-
|
|
11006
|
-
|
|
11007
|
-
|
|
11008
|
-
|
|
11009
|
-
|
|
11010
|
-
|
|
11011
|
-
|
|
11012
|
-
|
|
11013
|
-
|
|
11014
|
-
|
|
11015
|
-
|
|
11016
|
-
|
|
11017
|
-
|
|
11018
|
-
|
|
11019
|
-
|
|
11020
|
-
|
|
11021
|
-
|
|
11022
|
-
|
|
11376
|
+
children: OPERATOR_LABELS[op]
|
|
11377
|
+
}, op))
|
|
11378
|
+
}),
|
|
11379
|
+
opNeedsValues(filter.op) && /*#__PURE__*/ jsx("div", {
|
|
11380
|
+
style: {
|
|
11381
|
+
display: "flex",
|
|
11382
|
+
flexDirection: "column",
|
|
11383
|
+
gap: 4,
|
|
11384
|
+
maxHeight: 160,
|
|
11385
|
+
overflow: "auto"
|
|
11386
|
+
},
|
|
11387
|
+
children: values.map((value)=>{
|
|
11388
|
+
const checked = (filter.values ?? []).includes(value);
|
|
11389
|
+
return /*#__PURE__*/ jsxs(CheckboxRoot, {
|
|
11390
|
+
checked: checked,
|
|
11391
|
+
onCheckedChange: ()=>patchToolbarFilter(filter.id, {
|
|
11392
|
+
values: checked ? (filter.values ?? []).filter((v)=>v !== value) : [
|
|
11393
|
+
...filter.values ?? [],
|
|
11394
|
+
value
|
|
11395
|
+
]
|
|
11396
|
+
}),
|
|
11023
11397
|
style: {
|
|
11024
11398
|
display: "flex",
|
|
11025
|
-
|
|
11026
|
-
gap:
|
|
11027
|
-
|
|
11028
|
-
|
|
11399
|
+
alignItems: "center",
|
|
11400
|
+
gap: 8,
|
|
11401
|
+
padding: "4px 0",
|
|
11402
|
+
cursor: "pointer"
|
|
11029
11403
|
},
|
|
11030
|
-
children:
|
|
11031
|
-
|
|
11032
|
-
|
|
11033
|
-
|
|
11034
|
-
|
|
11035
|
-
values: checked ? (filter.values ?? []).filter((v)=>v !== value) : [
|
|
11036
|
-
...filter.values ?? [],
|
|
11037
|
-
value
|
|
11038
|
-
]
|
|
11039
|
-
}),
|
|
11040
|
-
style: {
|
|
11041
|
-
display: "flex",
|
|
11042
|
-
alignItems: "center",
|
|
11043
|
-
gap: 8,
|
|
11044
|
-
padding: "4px 0",
|
|
11045
|
-
cursor: "pointer"
|
|
11046
|
-
},
|
|
11047
|
-
children: [
|
|
11048
|
-
/*#__PURE__*/ jsx(CheckboxControl, {
|
|
11049
|
-
children: /*#__PURE__*/ jsx(CheckboxIndicator, {
|
|
11050
|
-
checked: /*#__PURE__*/ jsx(IconCheckmarkLine_default, {
|
|
11051
|
-
size: 12
|
|
11052
|
-
})
|
|
11053
|
-
})
|
|
11054
|
-
}),
|
|
11055
|
-
/*#__PURE__*/ jsx(CheckboxLabel, {
|
|
11056
|
-
children: value
|
|
11057
|
-
}),
|
|
11058
|
-
/*#__PURE__*/ jsx(CheckboxHiddenInput, {
|
|
11059
|
-
"aria-label": value
|
|
11404
|
+
children: [
|
|
11405
|
+
/*#__PURE__*/ jsx(CheckboxControl, {
|
|
11406
|
+
children: /*#__PURE__*/ jsx(CheckboxIndicator, {
|
|
11407
|
+
checked: /*#__PURE__*/ jsx(IconCheckmarkLine_default, {
|
|
11408
|
+
size: 12
|
|
11060
11409
|
})
|
|
11061
|
-
|
|
11062
|
-
}, value);
|
|
11063
|
-
})
|
|
11064
|
-
}),
|
|
11065
|
-
opNeedsValue(filter.op) && kind === "date" && /*#__PURE__*/ jsx(DatePicker, {
|
|
11066
|
-
value: filter.value ? toDatePickerDate(isoToDate(filter.value) ?? new Date()) : undefined,
|
|
11067
|
-
onValueChange: (d)=>d && patchToolbarFilter(filter.id, {
|
|
11068
|
-
value: dateToISO(fromDatePickerDate(d))
|
|
11069
|
-
})
|
|
11070
|
-
}),
|
|
11071
|
-
opNeedsValue(filter.op) && kind === "select" && /*#__PURE__*/ jsx(InlineSelect, {
|
|
11072
|
-
ariaLabel: "Filter value",
|
|
11073
|
-
placeholder: "Value…",
|
|
11074
|
-
value: filter.value || undefined,
|
|
11075
|
-
onChange: (value)=>patchToolbarFilter(filter.id, {
|
|
11076
|
-
value
|
|
11410
|
+
})
|
|
11077
11411
|
}),
|
|
11078
|
-
|
|
11079
|
-
value
|
|
11080
|
-
|
|
11081
|
-
|
|
11082
|
-
|
|
11083
|
-
|
|
11084
|
-
|
|
11085
|
-
|
|
11086
|
-
|
|
11087
|
-
|
|
11088
|
-
|
|
11089
|
-
|
|
11090
|
-
|
|
11091
|
-
|
|
11092
|
-
placeholder: "Value…",
|
|
11093
|
-
"aria-label": "Filter value"
|
|
11094
|
-
})
|
|
11412
|
+
/*#__PURE__*/ jsx(CheckboxLabel, {
|
|
11413
|
+
children: value
|
|
11414
|
+
}),
|
|
11415
|
+
/*#__PURE__*/ jsx(CheckboxHiddenInput, {
|
|
11416
|
+
"aria-label": value
|
|
11417
|
+
})
|
|
11418
|
+
]
|
|
11419
|
+
}, value);
|
|
11420
|
+
})
|
|
11421
|
+
}),
|
|
11422
|
+
opNeedsValue(filter.op) && kind === "date" && /*#__PURE__*/ jsx(DatePicker, {
|
|
11423
|
+
value: filter.value ? toDatePickerDate(isoToDate(filter.value) ?? new Date()) : undefined,
|
|
11424
|
+
onValueChange: (d)=>d && patchToolbarFilter(filter.id, {
|
|
11425
|
+
value: dateToISO(fromDatePickerDate(d))
|
|
11095
11426
|
})
|
|
11096
|
-
|
|
11427
|
+
}),
|
|
11428
|
+
opNeedsValue(filter.op) && kind === "select" && /*#__PURE__*/ jsx(InlineSelect, {
|
|
11429
|
+
ariaLabel: "Filter value",
|
|
11430
|
+
placeholder: "Value…",
|
|
11431
|
+
value: filter.value || undefined,
|
|
11432
|
+
onChange: (value)=>patchToolbarFilter(filter.id, {
|
|
11433
|
+
value
|
|
11434
|
+
}),
|
|
11435
|
+
options: values.map((value)=>({
|
|
11436
|
+
value,
|
|
11437
|
+
label: value
|
|
11438
|
+
}))
|
|
11439
|
+
}),
|
|
11440
|
+
opNeedsValue(filter.op) && kind !== "date" && kind !== "select" && /*#__PURE__*/ jsx(TextFieldRoot, {
|
|
11441
|
+
variant: "outline",
|
|
11442
|
+
size: "medium",
|
|
11443
|
+
children: /*#__PURE__*/ jsx(TextFieldInput, {
|
|
11444
|
+
type: kind === "number" ? "number" : "text",
|
|
11445
|
+
value: filter.value,
|
|
11446
|
+
onChange: (e)=>patchToolbarFilter(filter.id, {
|
|
11447
|
+
value: e.target.value
|
|
11448
|
+
}),
|
|
11449
|
+
placeholder: "Value…",
|
|
11450
|
+
"aria-label": "Filter value"
|
|
11451
|
+
})
|
|
11097
11452
|
})
|
|
11098
11453
|
]
|
|
11099
11454
|
});
|
|
@@ -11101,15 +11456,14 @@ const formatFilterDate = (iso)=>{
|
|
|
11101
11456
|
/**
|
|
11102
11457
|
* The toolbar's Filter trigger + popover -- a compact `"Filter"` / `"Filter · N"`
|
|
11103
11458
|
* button (matching `SortControl`'s own `"Sort"` / `"Sort · N"`), opening ONE popover
|
|
11104
|
-
* that holds both the active conditions (compact
|
|
11105
|
-
*
|
|
11106
|
-
* new one
|
|
11107
|
-
*
|
|
11459
|
+
* that holds both the active conditions (compact chips, `FilterConditionRow` above,
|
|
11460
|
+
* ALL open at once -- no accordion) and, behind "+ Add filter", the field picker that
|
|
11461
|
+
* appends a new one. See `FilterConditionRow`'s own doc comment for why the chips
|
|
11462
|
+
* live inside this one popover rather than spread across the toolbar row itself.
|
|
11108
11463
|
*/ function FilterControl({ columns, label = "Filter" }) {
|
|
11109
11464
|
const { toolbarFilters, addToolbarFilter } = useDataTableContext();
|
|
11110
11465
|
const [open, setOpen] = React.useState(false);
|
|
11111
11466
|
const [pickingField, setPickingField] = React.useState(false);
|
|
11112
|
-
const [expandedId, setExpandedId] = React.useState(null);
|
|
11113
11467
|
return /*#__PURE__*/ jsxs(MenuRoot, {
|
|
11114
11468
|
open: open,
|
|
11115
11469
|
onOpenChange: (next)=>{
|
|
@@ -11171,8 +11525,7 @@ const formatFilterDate = (iso)=>{
|
|
|
11171
11525
|
columns.map((c)=>/*#__PURE__*/ jsx("button", {
|
|
11172
11526
|
type: "button",
|
|
11173
11527
|
onClick: ()=>{
|
|
11174
|
-
|
|
11175
|
-
setExpandedId(id);
|
|
11528
|
+
addToolbarFilter(c.key);
|
|
11176
11529
|
setPickingField(false);
|
|
11177
11530
|
},
|
|
11178
11531
|
style: {
|
|
@@ -11205,9 +11558,7 @@ const formatFilterDate = (iso)=>{
|
|
|
11205
11558
|
}),
|
|
11206
11559
|
toolbarFilters.map((f)=>/*#__PURE__*/ jsx(FilterConditionRow, {
|
|
11207
11560
|
filter: f,
|
|
11208
|
-
columns: columns
|
|
11209
|
-
expanded: expandedId === f.id,
|
|
11210
|
-
onToggle: ()=>setExpandedId((id)=>id === f.id ? null : f.id)
|
|
11561
|
+
columns: columns
|
|
11211
11562
|
}, f.id)),
|
|
11212
11563
|
/*#__PURE__*/ jsxs("button", {
|
|
11213
11564
|
type: "button",
|
|
@@ -11236,8 +11587,8 @@ const formatFilterDate = (iso)=>{
|
|
|
11236
11587
|
});
|
|
11237
11588
|
}
|
|
11238
11589
|
|
|
11239
|
-
const DataTableToolbar = withContext$
|
|
11240
|
-
const DataTableCount = withContext$
|
|
11590
|
+
const DataTableToolbar = withContext$1(Primitive.div, "toolbar");
|
|
11591
|
+
const DataTableCount = withContext$1(Primitive.span, "count");
|
|
11241
11592
|
/**
|
|
11242
11593
|
* The toolbar's own right-hand group -- `count`'s `marginInlineStart: auto` moved
|
|
11243
11594
|
* here (off the `<span>` itself) so a consumer's `toolbarActions` and the row count
|
|
@@ -11248,7 +11599,7 @@ const DataTableCount = withContext$2(Primitive.span, "count");
|
|
|
11248
11599
|
* it. `DataTable.tsx` always renders this wrapper once either `toolbarActions` or
|
|
11249
11600
|
* `showCount` is on, so it degrades to exactly the old single-count layout when no
|
|
11250
11601
|
* `toolbarActions` is passed.
|
|
11251
|
-
*/ const DataTableToolbarEnd = withContext$
|
|
11602
|
+
*/ const DataTableToolbarEnd = withContext$1(Primitive.div, "toolbarEnd");
|
|
11252
11603
|
/**
|
|
11253
11604
|
* A compact, borderless search box -- crisp's own measured `.crisp-datatable-search`
|
|
11254
11605
|
* (32px, `bgSurface` background, a leading icon, a soft rest-state shadow, a 1px brand
|
|
@@ -11298,7 +11649,7 @@ function RowCount({ shown, total, countLabel }) {
|
|
|
11298
11649
|
}
|
|
11299
11650
|
|
|
11300
11651
|
function DataTableInner(props, ref) {
|
|
11301
|
-
const { columns, data, surface, className, locale = "en-US", sort, defaultSort, onSortChange, sortIcon, selectable, selectedIds, defaultSelectedIds, onSelectionChange, selectAllLabel = "Select all rows", getRowSelectionLabel = ()=>"Select row", selectionCheckedIcon, selectionIndeterminateIcon, query, defaultQuery, onQueryChange, searchPlaceholder = "Search…", searchLabel = "Search table", toolbarSorts, defaultToolbarSorts, onToolbarSortsChange, toolbarFilters, defaultToolbarFilters, onToolbarFiltersChange, showCount = true, countLabel, toolbarActions, columnMenu = false, reorderable = true, resizable = true, columnOrder, defaultColumnOrder, onColumnOrderChange, hiddenColumnKeys, defaultHiddenColumnKeys, onHiddenColumnKeysChange, columnWidths, defaultColumnWidths, onColumnWidthsChange, columnLabels, defaultColumnLabels, onColumnLabelsChange, pinnedColumnKeys, defaultPinnedColumnKeys, onPinnedColumnKeysChange, onColumnsChange, columnCalcOps, defaultColumnCalcOps, onColumnCalcOpsChange, stickyFirst = false, stickyLast = false, onEdit, interactive = false, onRowClick, onAddRow, addRowLabel = "New record", loading = false, loadingLabel = "Loading...", error, empty = "No results.", onLoadMore, hasMore, loadingMore, scrollProps, ...restProps } = props;
|
|
11652
|
+
const { columns, data, surface, className, locale = "en-US", sort, defaultSort, onSortChange, sortIcon, selectable, selectedIds, defaultSelectedIds, onSelectionChange, selectAllLabel = "Select all rows", getRowSelectionLabel = ()=>"Select row", selectionCheckedIcon, selectionIndeterminateIcon, bulkActions, clearSelectionLabel, query, defaultQuery, onQueryChange, searchPlaceholder = "Search…", searchLabel = "Search table", toolbarSorts, defaultToolbarSorts, onToolbarSortsChange, toolbarFilters, defaultToolbarFilters, onToolbarFiltersChange, showCount = true, countLabel, toolbarActions, columnMenu = false, reorderable = true, resizable = true, columnOrder, defaultColumnOrder, onColumnOrderChange, hiddenColumnKeys, defaultHiddenColumnKeys, onHiddenColumnKeysChange, columnWidths, defaultColumnWidths, onColumnWidthsChange, columnLabels, defaultColumnLabels, onColumnLabelsChange, pinnedColumnKeys, defaultPinnedColumnKeys, onPinnedColumnKeysChange, onColumnsChange, columnCalcOps, defaultColumnCalcOps, onColumnCalcOpsChange, stickyFirst = false, stickyLast = false, onEdit, cellContextMenu = true, onViewEditHistory, cellContextMenuItems, interactive = false, onRowClick, onAddRow, addRowLabel = "New record", loading = false, loadingLabel = "Loading...", error, empty = "No results.", onLoadMore, hasMore, loadingMore, scrollProps, ...restProps } = props;
|
|
11302
11653
|
const classNames = dataTable({
|
|
11303
11654
|
surface,
|
|
11304
11655
|
interactive
|
|
@@ -11400,6 +11751,12 @@ function DataTableInner(props, ref) {
|
|
|
11400
11751
|
const columnKeys = React.useMemo(()=>columns.map((column)=>column.key), [
|
|
11401
11752
|
columns
|
|
11402
11753
|
]);
|
|
11754
|
+
// Threaded down to `reconcileColumnOrder` (react-headless's `dom.ts`) so a brand-new
|
|
11755
|
+
// column added to `columns` after mount gets appended BEFORE this one, not past it --
|
|
11756
|
+
// see that function's own doc comment for the bug this prevents.
|
|
11757
|
+
const actionsColumnKey = React.useMemo(()=>columns.find((column)=>column.actions)?.key, [
|
|
11758
|
+
columns
|
|
11759
|
+
]);
|
|
11403
11760
|
const searchable = React.useMemo(()=>columns.some((column)=>column.searchable), [
|
|
11404
11761
|
columns
|
|
11405
11762
|
]);
|
|
@@ -11422,7 +11779,7 @@ function DataTableInner(props, ref) {
|
|
|
11422
11779
|
// loading/error/empty body, infinite scroll's sentinel, the add-row footer) reads
|
|
11423
11780
|
// this ONE value rather than each re-deriving it, so they can never drift apart.
|
|
11424
11781
|
const totalCols = columns.length + (selectable ? 1 : 0);
|
|
11425
|
-
return /*#__PURE__*/ jsx(ClassNamesProvider
|
|
11782
|
+
return /*#__PURE__*/ jsx(ClassNamesProvider, {
|
|
11426
11783
|
value: classNames,
|
|
11427
11784
|
children: /*#__PURE__*/ jsxs(DataTableRoot, {
|
|
11428
11785
|
ref: ref,
|
|
@@ -11442,6 +11799,7 @@ function DataTableInner(props, ref) {
|
|
|
11442
11799
|
toolbarFilters: resolvedToolbarFilters,
|
|
11443
11800
|
onToolbarFiltersChange: handleToolbarFiltersChange,
|
|
11444
11801
|
columnKeys: columnKeys,
|
|
11802
|
+
actionsColumnKey: actionsColumnKey,
|
|
11445
11803
|
columnOrder: columnOrder,
|
|
11446
11804
|
defaultColumnOrder: defaultColumnOrder,
|
|
11447
11805
|
onColumnOrderChange: onColumnOrderChange,
|
|
@@ -11578,7 +11936,10 @@ function DataTableInner(props, ref) {
|
|
|
11578
11936
|
locale: locale,
|
|
11579
11937
|
selectable: !!selectable,
|
|
11580
11938
|
stickyFirst: stickyFirst,
|
|
11581
|
-
stickyLast: resolvedStickyLast
|
|
11939
|
+
stickyLast: resolvedStickyLast,
|
|
11940
|
+
cellContextMenu: cellContextMenu,
|
|
11941
|
+
onViewEditHistory: onViewEditHistory,
|
|
11942
|
+
cellContextMenuItems: cellContextMenuItems
|
|
11582
11943
|
})
|
|
11583
11944
|
]
|
|
11584
11945
|
}, row.id)),
|
|
@@ -11617,6 +11978,10 @@ function DataTableInner(props, ref) {
|
|
|
11617
11978
|
})
|
|
11618
11979
|
]
|
|
11619
11980
|
})
|
|
11981
|
+
}),
|
|
11982
|
+
selectable && /*#__PURE__*/ jsx(DataTableBulkBar, {
|
|
11983
|
+
clearLabel: clearSelectionLabel,
|
|
11984
|
+
children: bulkActions
|
|
11620
11985
|
})
|
|
11621
11986
|
]
|
|
11622
11987
|
})
|
|
@@ -11670,7 +12035,7 @@ DataTableInner.displayName = "DataTable";
|
|
|
11670
12035
|
// is a SIBLING of the toolbar (both inside `DataTablePrimitiveRoot`'s card `<div>`)
|
|
11671
12036
|
// rather than `DataTablePrimitiveRoot` itself: a `<table>` cannot host the toolbar as a
|
|
11672
12037
|
// child without the HTML parser foster-parenting it back out.
|
|
11673
|
-
const DataTableTable = withContext$
|
|
12038
|
+
const DataTableTable = withContext$1(DataTableTable$1, "table");
|
|
11674
12039
|
/** One `<col>` per rendered column (plus the 40px selection column, if any) -- the
|
|
11675
12040
|
* SINGLE place a column's width is ever set now. Every row's header/body/footer cell
|
|
11676
12041
|
* reads its own width off this shared `<colgroup>` by construction (native table
|
|
@@ -11707,15 +12072,15 @@ const DataTableTable = withContext$2(DataTableTable$1, "table");
|
|
|
11707
12072
|
]
|
|
11708
12073
|
});
|
|
11709
12074
|
}
|
|
11710
|
-
const DataTableHeader = withContext$
|
|
11711
|
-
const DataTableHeaderCell = withContext$
|
|
12075
|
+
const DataTableHeader = withContext$1(DataTableHeader$1, "header");
|
|
12076
|
+
const DataTableHeaderCell = withContext$1(DataTableHeaderCell$1, "headerCell");
|
|
11712
12077
|
// `.crisp-datatable-th-inner` -- the flex row INSIDE a `<th>` (label + filter trigger +
|
|
11713
12078
|
// sort chevron + column menu). See qvism-preset's own `headerCellInner` slot comment
|
|
11714
12079
|
// for why this lives one level below the cell itself rather than on it.
|
|
11715
|
-
const DataTableHeaderCellInner = withContext$
|
|
12080
|
+
const DataTableHeaderCellInner = withContext$1(Primitive.div, "headerCellInner");
|
|
11716
12081
|
/** The label text specifically -- the one thing in `DataTableHeaderCellInner` that
|
|
11717
12082
|
* truncates; the trailing filter/sort/menu icons stay fully visible regardless of how
|
|
11718
|
-
* long a column's own header text is. */ const DataTableHeaderCellLabel = withContext$
|
|
12083
|
+
* long a column's own header text is. */ const DataTableHeaderCellLabel = withContext$1(Primitive.span, "headerCellLabel");
|
|
11719
12084
|
/**
|
|
11720
12085
|
* A selection `<th>`/`<td>`/footer `<td>` composes ITS base slot (`headerCell`/`cell`/
|
|
11721
12086
|
* `footerCell`) together with the `selectionCell` MODIFIER, rather than replacing it --
|
|
@@ -11726,7 +12091,7 @@ const DataTableHeaderCellInner = withContext$2(Primitive.div, "headerCellInner")
|
|
|
11726
12091
|
* a second recipe call.
|
|
11727
12092
|
*/ function withSelectionCell(Component, baseSlot) {
|
|
11728
12093
|
const StyledComponent = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
11729
|
-
const classNames = useClassNames
|
|
12094
|
+
const classNames = useClassNames();
|
|
11730
12095
|
return /*#__PURE__*/ jsx(Component, {
|
|
11731
12096
|
ref: ref,
|
|
11732
12097
|
...props,
|
|
@@ -11737,12 +12102,12 @@ const DataTableHeaderCellInner = withContext$2(Primitive.div, "headerCellInner")
|
|
|
11737
12102
|
return StyledComponent;
|
|
11738
12103
|
}
|
|
11739
12104
|
const DataTableSelectionHeaderCell = withSelectionCell(DataTableHeaderCell$1, "headerCell");
|
|
11740
|
-
const DataTableHeaderCellIcon = withContext$
|
|
12105
|
+
const DataTableHeaderCellIcon = withContext$1(Primitive.span, "headerCellIcon");
|
|
11741
12106
|
// body has no CSS slot of its own in the rootage spec -- it is a plain structural
|
|
11742
12107
|
// `<tbody>`, so it is not wrapped in `withContext`.
|
|
11743
12108
|
const DataTableBody = DataTableBody$1;
|
|
11744
|
-
const DataTableRow = withContext$
|
|
11745
|
-
const DataTableCell = withContext$
|
|
12109
|
+
const DataTableRow = withContext$1(DataTableRow$1, "row");
|
|
12110
|
+
const DataTableCell = withContext$1(DataTableCell$1, "cell");
|
|
11746
12111
|
const DataTableSelectionCell = withSelectionCell(DataTableCell$1, "cell");
|
|
11747
12112
|
// Tranche 4 -- column menu + reorder + resize + sticky columns. A scroll boundary
|
|
11748
12113
|
// wrapping the whole `<table>`: `overflowX: auto` is what makes a wide table (more
|
|
@@ -11752,7 +12117,7 @@ const DataTableSelectionCell = withSelectionCell(DataTableCell$1, "cell");
|
|
|
11752
12117
|
// stays out of this slot's own base css (nothing here has a bounded height of its own
|
|
11753
12118
|
// to clip), so it never introduces a second vertical scrollbar next to whatever
|
|
11754
12119
|
// ambient container the whole DataTable already sits in.
|
|
11755
|
-
const DataTableScrollBodyBase = withContext$
|
|
12120
|
+
const DataTableScrollBodyBase = withContext$1(Primitive.div, "scrollArea");
|
|
11756
12121
|
function DataTableScrollBody({ children, scrollProps }) {
|
|
11757
12122
|
const { scrollAreaRef, scrolledStart, scrolledEnd, onScroll } = useDataTableContext();
|
|
11758
12123
|
const { className: scrollClassName, style: scrollStyle, ...otherScrollProps } = scrollProps ?? {};
|
|
@@ -11767,7 +12132,7 @@ function DataTableScrollBody({ children, scrollProps }) {
|
|
|
11767
12132
|
children: children
|
|
11768
12133
|
});
|
|
11769
12134
|
}
|
|
11770
|
-
const DataTableFooterBase = withContext$
|
|
12135
|
+
const DataTableFooterBase = withContext$1(Primitive.tfoot, "footer");
|
|
11771
12136
|
function DataTableFooter({ children, ...props }) {
|
|
11772
12137
|
return /*#__PURE__*/ jsx(DataTableFooterBase, {
|
|
11773
12138
|
...props,
|
|
@@ -11776,7 +12141,7 @@ function DataTableFooter({ children, ...props }) {
|
|
|
11776
12141
|
})
|
|
11777
12142
|
});
|
|
11778
12143
|
}
|
|
11779
|
-
const DataTableFooterCell = withContext$
|
|
12144
|
+
const DataTableFooterCell = withContext$1(Primitive.td, "footerCell");
|
|
11780
12145
|
// The selection column's OWN sticky wrappers -- one each for header/body/footer,
|
|
11781
12146
|
// reading the live pinned set via `useSelectionStickyFirst` (column-layout.ts) instead
|
|
11782
12147
|
// of the `stickyFirst` PROP alone, so the checkbox column sticks the moment ANY column
|
|
@@ -15713,4 +16078,4 @@ function RecordPreviewPanel({ open: _open, record, onClose, className, animation
|
|
|
15713
16078
|
}
|
|
15714
16079
|
RecordPreviewPanel.displayName = "RecordPreviewPanel";
|
|
15715
16080
|
|
|
15716
|
-
export { DataTable, DataTableViewsControl, RecordPreview, ResizablePanel };
|
|
16081
|
+
export { DataTable, DataTableBulkBar, DataTableViewsControl, RecordPreview, ResizablePanel };
|