@crispy-seed/data-table 0.10.4 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/data-table-recipe.css +58 -0
- package/dist/data-table-recipe.layered.css +63 -0
- package/dist/index.cjs +702 -328
- package/dist/index.d.ts +119 -55
- package/dist/index.js +702 -329
- 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)));
|
|
@@ -1634,6 +1653,17 @@ function useDataTableSelection(props) {
|
|
|
1634
1653
|
rowIds,
|
|
1635
1654
|
setSelectedIds
|
|
1636
1655
|
]);
|
|
1656
|
+
// Bulk-action bar's own "×" (gap #4, DATATABLE-ATTIO-SPEC's Attio review) --
|
|
1657
|
+
// deliberately its own function rather than reusing `toggleAll`: `toggleAll` only
|
|
1658
|
+
// clears from an "all" selection, and otherwise SELECTS every row (`selectionState
|
|
1659
|
+
// === "some"` -> `rowIds`, above) -- exactly the wrong direction for a "clear
|
|
1660
|
+
// selection" affordance shown while `selectionState` is "some". This always empties
|
|
1661
|
+
// the set, regardless of `selectionState`.
|
|
1662
|
+
const clearSelection = useCallback(()=>{
|
|
1663
|
+
setSelectedIds([]);
|
|
1664
|
+
}, [
|
|
1665
|
+
setSelectedIds
|
|
1666
|
+
]);
|
|
1637
1667
|
const getRowProps = useCallback((rowId)=>{
|
|
1638
1668
|
const isRowSelected = isSelected(rowId);
|
|
1639
1669
|
return elementProps({
|
|
@@ -1652,6 +1682,7 @@ function useDataTableSelection(props) {
|
|
|
1652
1682
|
isSelected,
|
|
1653
1683
|
toggleRow,
|
|
1654
1684
|
toggleAll,
|
|
1685
|
+
clearSelection,
|
|
1655
1686
|
getRowProps
|
|
1656
1687
|
};
|
|
1657
1688
|
}
|
|
@@ -1677,24 +1708,33 @@ function useDataTableSort(props) {
|
|
|
1677
1708
|
// already passes its "cleared" shape through unconditionally; this now matches.
|
|
1678
1709
|
onChange: onSortChange
|
|
1679
1710
|
});
|
|
1711
|
+
// 3-state cycle (DATATABLE-ATTIO-SPEC.md: "asc/desc/none cycle") -- clicking a
|
|
1712
|
+
// DIFFERENT column always starts it fresh at asc, discarding whatever the previous
|
|
1713
|
+
// column's direction was (this stays a SINGLE-column secondary affordance, never a
|
|
1714
|
+
// second multi-sort list of its own). Clicking the SAME column steps asc -> desc ->
|
|
1715
|
+
// undefined ("none", i.e. this column drops out of the sort entirely) -> asc again.
|
|
1716
|
+
// Before this, the cycle wrapped desc -> asc forever, with no click-driven way back
|
|
1717
|
+
// to unsorted at all (a reported gap: "sort asc/desc has no option for no sort on
|
|
1718
|
+
// columns") -- `clearSort` below was the ONLY way there, now it's one of two.
|
|
1680
1719
|
const toggleSort = useCallback((key)=>{
|
|
1681
1720
|
setSort((current)=>{
|
|
1682
1721
|
if (!current || current.key !== key) return {
|
|
1683
1722
|
key,
|
|
1684
1723
|
dir: "asc"
|
|
1685
1724
|
};
|
|
1686
|
-
return {
|
|
1725
|
+
if (current.dir === "asc") return {
|
|
1687
1726
|
key,
|
|
1688
|
-
dir:
|
|
1727
|
+
dir: "desc"
|
|
1689
1728
|
};
|
|
1729
|
+
return undefined;
|
|
1690
1730
|
});
|
|
1691
1731
|
}, [
|
|
1692
1732
|
setSort
|
|
1693
1733
|
]);
|
|
1694
|
-
/** Attio/GRC's "Remove sort" --
|
|
1695
|
-
*
|
|
1696
|
-
*
|
|
1697
|
-
*
|
|
1734
|
+
/** Attio/GRC's "Remove sort" -- a second way back to no sort at all (the header
|
|
1735
|
+
* click cycle above reaches it too, on a column's third click), exposed for a
|
|
1736
|
+
* column's own header menu (`column-menu.tsx`'s `ColumnMenu`) to offer as a
|
|
1737
|
+
* "Clear sort" item when that column is the sorted one, without three clicks. */ const clearSort = useCallback(()=>setSort(undefined), [
|
|
1698
1738
|
setSort
|
|
1699
1739
|
]);
|
|
1700
1740
|
const getHeaderCellProps = useCallback((columnKey, sortable)=>{
|
|
@@ -1727,7 +1767,12 @@ function useDataTableSort(props) {
|
|
|
1727
1767
|
sort,
|
|
1728
1768
|
toggleSort,
|
|
1729
1769
|
clearSort,
|
|
1730
|
-
getHeaderCellProps
|
|
1770
|
+
getHeaderCellProps,
|
|
1771
|
+
// Exposed for `useDataTable.ts`'s own composition ONLY (unifying this single
|
|
1772
|
+
// click-to-sort column with `useDataTableToolbarSort`'s ordered list) -- not part
|
|
1773
|
+
// of the documented header-click contract (`toggleSort`/`clearSort` above are),
|
|
1774
|
+
// so no consumer-facing doc comment promotes it as a control of its own.
|
|
1775
|
+
setSort
|
|
1731
1776
|
};
|
|
1732
1777
|
}
|
|
1733
1778
|
|
|
@@ -1831,11 +1876,14 @@ const EMPTY_SORTS = [];
|
|
|
1831
1876
|
* concern rather than folded into `useDataTableSort` (DATATABLE-ATTIO-SPEC.md: "compose,
|
|
1832
1877
|
* don't fatten one hook"). `useDataTableSort` still owns the single click-to-sort
|
|
1833
1878
|
* column/direction a header's own click toggles -- that stays Attio's SECONDARY sort
|
|
1834
|
-
* affordance,
|
|
1835
|
-
*
|
|
1836
|
-
*
|
|
1837
|
-
*
|
|
1838
|
-
* toolbar
|
|
1879
|
+
* affordance, a separate hook from this one. They are NOT independent state, though:
|
|
1880
|
+
* `useDataTable.ts`'s own composition keeps a header-driven column's entry in THIS
|
|
1881
|
+
* array in sync (added/updated/removed as that column's header cycles asc/desc/none),
|
|
1882
|
+
* so a consumer controlling only `toolbarSorts`/`onToolbarSortsChange` still sees a
|
|
1883
|
+
* header click, and the toolbar's own "Remove sort" x on that same entry clears the
|
|
1884
|
+
* header's side too. Multiple conditions apply in list order (sort[0] wins, later ones
|
|
1885
|
+
* break ties; see the styled layer's `applyToolbarSorts`). The styled layer
|
|
1886
|
+
* (`DataTable.tsx`) decides how the merged result actually sorts `data`.
|
|
1839
1887
|
*/ function useDataTableToolbarSort(props) {
|
|
1840
1888
|
const { toolbarSorts: sortsProp, defaultToolbarSorts = EMPTY_SORTS, onToolbarSortsChange } = props;
|
|
1841
1889
|
const [toolbarSorts = EMPTY_SORTS, setToolbarSorts] = useControllableState$1({
|
|
@@ -1920,8 +1968,13 @@ const EMPTY_SORTS = [];
|
|
|
1920
1968
|
// header "is/is not" picker) -- DATATABLE-ATTIO-SPEC.md's Attio/crisp `TableToolbar`
|
|
1921
1969
|
// model is now the PRIMARY filter/sort UX, not a per-column header affordance. They stay
|
|
1922
1970
|
// SEPARATE sub-hooks from `useDataTableSort` rather than folded into it ("compose, don't
|
|
1923
|
-
// fatten one hook") --
|
|
1924
|
-
//
|
|
1971
|
+
// fatten one hook") -- but this composition point below DOES reconcile the two into one
|
|
1972
|
+
// state (the two `useEffect`s after `infiniteScroll`), so a header click and the
|
|
1973
|
+
// toolbar's own Sort popover always agree on what's sorted, regardless of which prop
|
|
1974
|
+
// pair (`sort`/`onSortChange` or `toolbarSorts`/`onToolbarSortsChange`) a consumer
|
|
1975
|
+
// actually controls. `DataTable.tsx` (styled) still decides how the RESULT actually
|
|
1976
|
+
// sorts `data` (`effectiveSorts`/`applyToolbarSorts`) -- this layer only keeps the two
|
|
1977
|
+
// STATE pools in agreement.
|
|
1925
1978
|
function useDataTable(props) {
|
|
1926
1979
|
const sort = useDataTableSort(props);
|
|
1927
1980
|
const selection = useDataTableSelection(props);
|
|
@@ -1944,6 +1997,105 @@ function useDataTable(props) {
|
|
|
1944
1997
|
...props,
|
|
1945
1998
|
scrollRootRef: scroll.elementRef
|
|
1946
1999
|
});
|
|
2000
|
+
// Unifies `sort` (the header's own single click-to-sort column, above) with
|
|
2001
|
+
// `toolbarSort.toolbarSorts` (the toolbar Sort popover's ordered list, above) into
|
|
2002
|
+
// ONE state instead of two -- before this, a header click only ever moved `sort`,
|
|
2003
|
+
// invisible to a consumer controlling `toolbarSorts`/`onToolbarSortsChange` (the
|
|
2004
|
+
// reported bug: clicking a header did nothing to the array the consumer's own Sort
|
|
2005
|
+
// popover reads, so the toolbar's "Sort" trigger/count and its popover rows silently
|
|
2006
|
+
// ignored every header click).
|
|
2007
|
+
//
|
|
2008
|
+
// ONE effect, not two -- an earlier version split this into a forward
|
|
2009
|
+
// (`sort` -> `toolbarSorts`) and a reverse (`toolbarSorts` -> `sort`) effect, each
|
|
2010
|
+
// keyed off its own dependency. That raced on the very first render: `useEffect`
|
|
2011
|
+
// runs every effect once on mount regardless of what "changed", so with
|
|
2012
|
+
// `defaultSort` set, the reverse effect ran in the SAME commit as the forward one,
|
|
2013
|
+
// read `toolbarSorts` BEFORE the forward effect's own (still-queued, not yet
|
|
2014
|
+
// re-rendered) write had landed, saw no entry for the column yet, and immediately
|
|
2015
|
+
// cleared `sort` right back out -- the defaultSort silently never took effect.
|
|
2016
|
+
// `settledRef` sidesteps this: it remembers the last `{key,dir}` this effect itself
|
|
2017
|
+
// confirmed, so on every run it can tell WHICH side moved since then, instead of
|
|
2018
|
+
// guessing from which dependency happens to have triggered the re-run.
|
|
2019
|
+
const settledRef = useRef({
|
|
2020
|
+
key: undefined,
|
|
2021
|
+
dir: undefined
|
|
2022
|
+
});
|
|
2023
|
+
useEffect(()=>{
|
|
2024
|
+
const curSort = sort.sort;
|
|
2025
|
+
const curEntry = curSort ? toolbarSort.toolbarSorts.find((s)=>s.key === curSort.key) : undefined;
|
|
2026
|
+
const settled = settledRef.current;
|
|
2027
|
+
const sortUnchangedSinceSettled = settled.key === curSort?.key && settled.dir === curSort?.dir;
|
|
2028
|
+
if (sortUnchangedSinceSettled) {
|
|
2029
|
+
// `sort` itself hasn't moved since we last settled -- if this effect is running
|
|
2030
|
+
// at all, `toolbarSorts` is what changed, so REVERSE-sync: the toolbar Sort
|
|
2031
|
+
// popover's own "Remove sort" x, or its direction select, touching this exact
|
|
2032
|
+
// column. Never reaches across to adopt some OTHER column added independently
|
|
2033
|
+
// via that popover -- only the column `sort` is already tracking.
|
|
2034
|
+
if (!curSort) return;
|
|
2035
|
+
if (!curEntry) {
|
|
2036
|
+
sort.clearSort();
|
|
2037
|
+
settledRef.current = {
|
|
2038
|
+
key: undefined,
|
|
2039
|
+
dir: undefined
|
|
2040
|
+
};
|
|
2041
|
+
} else if (curEntry.dir !== curSort.dir) {
|
|
2042
|
+
sort.setSort({
|
|
2043
|
+
key: curSort.key,
|
|
2044
|
+
dir: curEntry.dir
|
|
2045
|
+
});
|
|
2046
|
+
settledRef.current = {
|
|
2047
|
+
key: curSort.key,
|
|
2048
|
+
dir: curEntry.dir
|
|
2049
|
+
};
|
|
2050
|
+
}
|
|
2051
|
+
return;
|
|
2052
|
+
}
|
|
2053
|
+
// `sort` moved (a header click, Enter/Space, or `clearSort` -- column-menu.tsx's
|
|
2054
|
+
// menu item calls that directly, bypassing `toggleSort`) -- FORWARD-sync:
|
|
2055
|
+
// add/update/remove THIS column's own entry in `toolbarSorts`, leaving every
|
|
2056
|
+
// OTHER entry (added independently via the toolbar's own "+ Add sort") untouched.
|
|
2057
|
+
// The functional updater reads the CURRENT array off `current`, never off
|
|
2058
|
+
// `toolbarSort.toolbarSorts` in this closure, so a stale render can never clobber
|
|
2059
|
+
// an entry the toolbar's own popover added a moment earlier.
|
|
2060
|
+
const prevKey = settled.key;
|
|
2061
|
+
toolbarSort.setToolbarSorts((current)=>{
|
|
2062
|
+
let updated = current;
|
|
2063
|
+
if (prevKey && prevKey !== curSort?.key) {
|
|
2064
|
+
updated = updated.filter((s)=>s.key !== prevKey);
|
|
2065
|
+
}
|
|
2066
|
+
if (curSort) {
|
|
2067
|
+
const idx = updated.findIndex((s)=>s.key === curSort.key);
|
|
2068
|
+
if (idx === -1) {
|
|
2069
|
+
updated = [
|
|
2070
|
+
...updated,
|
|
2071
|
+
{
|
|
2072
|
+
key: curSort.key,
|
|
2073
|
+
dir: curSort.dir
|
|
2074
|
+
}
|
|
2075
|
+
];
|
|
2076
|
+
} else if (updated[idx].dir !== curSort.dir) {
|
|
2077
|
+
updated = updated.map((s, i)=>i === idx ? {
|
|
2078
|
+
...s,
|
|
2079
|
+
dir: curSort.dir
|
|
2080
|
+
} : s);
|
|
2081
|
+
}
|
|
2082
|
+
}
|
|
2083
|
+
return updated;
|
|
2084
|
+
});
|
|
2085
|
+
settledRef.current = curSort ? {
|
|
2086
|
+
key: curSort.key,
|
|
2087
|
+
dir: curSort.dir
|
|
2088
|
+
} : {
|
|
2089
|
+
key: undefined,
|
|
2090
|
+
dir: undefined
|
|
2091
|
+
};
|
|
2092
|
+
}, [
|
|
2093
|
+
sort.sort,
|
|
2094
|
+
toolbarSort.toolbarSorts,
|
|
2095
|
+
toolbarSort.setToolbarSorts,
|
|
2096
|
+
sort.clearSort,
|
|
2097
|
+
sort.setSort
|
|
2098
|
+
]);
|
|
1947
2099
|
return {
|
|
1948
2100
|
...sort,
|
|
1949
2101
|
...selection,
|
|
@@ -2539,11 +2691,70 @@ CheckboxIndicator.displayName = "CheckboxIndicator";
|
|
|
2539
2691
|
const CheckboxLabel = withContext$6(withStateProps$4(Primitive.span), "label");
|
|
2540
2692
|
const CheckboxHiddenInput = Checkbox.HiddenInput;
|
|
2541
2693
|
|
|
2542
|
-
const {
|
|
2694
|
+
const { ClassNamesProvider: ClassNamesProvider$2, withContext: withContext$5, useClassNames: useClassNames$2 } = createSlotRecipeContext(menu);
|
|
2695
|
+
const { PropsProvider: ItemPropsProvider$1, useProps: useItemProps$1, withContext: withItemContext$1, ClassNamesProvider: ItemClassNamesProvider$1 } = createSlotRecipeContext(menuItem);
|
|
2696
|
+
const withStateProps$3 = createWithStateProps([
|
|
2697
|
+
useMenuContext
|
|
2698
|
+
]);
|
|
2699
|
+
const withItemStateProps$1 = createWithStateProps([
|
|
2700
|
+
useMenuItemContext
|
|
2701
|
+
]);
|
|
2702
|
+
const MenuRoot = (props)=>{
|
|
2703
|
+
const [variantProps, otherProps] = menu.splitVariantProps(props);
|
|
2704
|
+
const classNames = menu(variantProps);
|
|
2705
|
+
return /*#__PURE__*/ jsx(ClassNamesProvider$2, {
|
|
2706
|
+
value: classNames,
|
|
2707
|
+
children: /*#__PURE__*/ jsx(ItemPropsProvider$1, {
|
|
2708
|
+
value: {
|
|
2709
|
+
size: variantProps.size
|
|
2710
|
+
},
|
|
2711
|
+
children: /*#__PURE__*/ jsx(Menu.Root, {
|
|
2712
|
+
...otherProps
|
|
2713
|
+
})
|
|
2714
|
+
})
|
|
2715
|
+
});
|
|
2716
|
+
};
|
|
2717
|
+
const MenuAnchor = Menu.Anchor;
|
|
2718
|
+
const MenuTrigger = Menu.Trigger;
|
|
2719
|
+
const MenuPositioner = /*#__PURE__*/ React.forwardRef(({ className, ...props }, ref)=>{
|
|
2720
|
+
const classNames = useClassNames$2();
|
|
2721
|
+
return /*#__PURE__*/ jsx(Menu.Positioner, {
|
|
2722
|
+
ref: ref,
|
|
2723
|
+
className: clsx(classNames.positioner, className),
|
|
2724
|
+
...props
|
|
2725
|
+
});
|
|
2726
|
+
});
|
|
2727
|
+
MenuPositioner.displayName = "MenuPositioner";
|
|
2728
|
+
const MenuContent = withContext$5(Menu.Content, "content");
|
|
2729
|
+
const MenuScrollArea = withContext$5(withStateProps$3(Primitive.div), "scrollArea");
|
|
2730
|
+
withContext$5(Menu.Group, "group");
|
|
2731
|
+
withContext$5(Menu.GroupLabel, "groupLabel");
|
|
2732
|
+
const MenuItem = /*#__PURE__*/ React.forwardRef(({ className: propClassName, ...props }, ref)=>{
|
|
2733
|
+
const [variantProps, otherProps] = menuItem.splitVariantProps(props);
|
|
2734
|
+
const parentProps = useItemProps$1();
|
|
2735
|
+
const classNames = menuItem({
|
|
2736
|
+
...parentProps,
|
|
2737
|
+
...variantProps
|
|
2738
|
+
});
|
|
2739
|
+
return /*#__PURE__*/ jsx(ItemClassNamesProvider$1, {
|
|
2740
|
+
value: classNames,
|
|
2741
|
+
children: /*#__PURE__*/ jsx(Menu.Item, {
|
|
2742
|
+
ref: ref,
|
|
2743
|
+
className: clsx(classNames.root, propClassName),
|
|
2744
|
+
...otherProps
|
|
2745
|
+
})
|
|
2746
|
+
});
|
|
2747
|
+
});
|
|
2748
|
+
MenuItem.displayName = "MenuItem";
|
|
2749
|
+
withItemContext$1(withItemStateProps$1(Primitive.div), "body");
|
|
2750
|
+
const MenuItemLabel = withItemContext$1(withItemStateProps$1(Primitive.span), "label");
|
|
2751
|
+
withItemContext$1(withItemStateProps$1(Primitive.span), "description");
|
|
2752
|
+
|
|
2753
|
+
const { withProvider: withProvider$2, withContext: withContext$4 } = createSlotRecipeContext(avatar);
|
|
2543
2754
|
const AvatarRoot = withProvider$2(Image.Root, "root");
|
|
2544
|
-
const AvatarImage = withContext$
|
|
2545
|
-
const AvatarFallback = withContext$
|
|
2546
|
-
withContext$
|
|
2755
|
+
const AvatarImage = withContext$4(Image.Content, "image");
|
|
2756
|
+
const AvatarFallback = withContext$4(Image.Fallback, "fallback");
|
|
2757
|
+
withContext$4(Primitive.div, "badge");
|
|
2547
2758
|
|
|
2548
2759
|
const Badge = /*#__PURE__*/ forwardRef(({ className, children, ...props }, ref)=>{
|
|
2549
2760
|
const [variantProps, restProps] = badge.splitVariantProps(props);
|
|
@@ -3087,6 +3298,141 @@ function RatingCell({ value, maxRating = 5 }) {
|
|
|
3087
3298
|
});
|
|
3088
3299
|
}
|
|
3089
3300
|
|
|
3301
|
+
/** Clear is withheld for `chip-list`: that column already owns its own per-tag remove
|
|
3302
|
+
* affordance (`ChipListEditor`) and commits a JSON-STRINGIFIED array through `onEdit`
|
|
3303
|
+
* (`DataTable.chips.test.tsx`'s own `TagsHarness` -- and this package's own Storybook
|
|
3304
|
+
* demo, `CellFeaturesDemo`, does exactly `JSON.parse(value)` on commit) -- committing
|
|
3305
|
+
* a bare `""` through that same path throws in the consumer's `onEdit`, it doesn't
|
|
3306
|
+
* clear anything. Every OTHER editable shape's commit value is a plain string
|
|
3307
|
+
* (`"true"`/`"false"`, a stringified rating, an option's own `value`, free text) that
|
|
3308
|
+
* a consumer's `onEdit` already has to accept a possibly-empty version of. */ function isClearableEditableCell(column) {
|
|
3309
|
+
return !!column.editable && column.type !== "chip-list";
|
|
3310
|
+
}
|
|
3311
|
+
/** Paste is narrower still -- restricted to a PLAIN `editable` column (no `editor`,
|
|
3312
|
+
* and not one of the always-live widget types) whose committed value IS the raw
|
|
3313
|
+
* text/number a user typed. `select`/`reference`/`actor` expect one of THEIR OWN
|
|
3314
|
+
* option values (not arbitrary clipboard text), `switch`/`rating`/`checkbox` are
|
|
3315
|
+
* always-live widgets with their own encoding, and `chip-list` is excluded for the
|
|
3316
|
+
* same JSON reason `isClearableEditableCell` documents above. This is the task's own
|
|
3317
|
+
* "only for simple editable text/number cells" guard -- committing pasted text
|
|
3318
|
+
* through any of the excluded shapes would corrupt the row rather than edit it
|
|
3319
|
+
* (crispy-seed's own #1094 lesson: never assume an editor's commit shape).
|
|
3320
|
+
*/ function isSimpleEditableCell(column) {
|
|
3321
|
+
if (!column.editable) return false;
|
|
3322
|
+
if (column.editor) return false;
|
|
3323
|
+
if (column.type === "chip-list" || column.type === "checkbox" || column.type === "rating") {
|
|
3324
|
+
return false;
|
|
3325
|
+
}
|
|
3326
|
+
return true;
|
|
3327
|
+
}
|
|
3328
|
+
/** Copy's own text representation -- deliberately mirrors `DisplayCellContent`'s
|
|
3329
|
+
* (body-cells.tsx) switch closely enough to read the same as what's on screen, but
|
|
3330
|
+
* returns a plain string (`navigator.clipboard.writeText`) rather than JSX. Not
|
|
3331
|
+
* exported -- it exists only to feed Copy, not as a general-purpose formatter. */ function cellValueAsText(column, value, locale) {
|
|
3332
|
+
if (value == null) return "";
|
|
3333
|
+
switch(column.type){
|
|
3334
|
+
case "entity":
|
|
3335
|
+
if (typeof value === "object") return String(value.name ?? "");
|
|
3336
|
+
return String(value);
|
|
3337
|
+
case "tags":
|
|
3338
|
+
case "chip-list":
|
|
3339
|
+
return Array.isArray(value) ? value.join(", ") : String(value);
|
|
3340
|
+
case "number":
|
|
3341
|
+
return typeof value === "number" ? String(formatNumberCell(value, locale) ?? "") : String(value);
|
|
3342
|
+
case "currency":
|
|
3343
|
+
return typeof value === "number" ? String(formatCurrencyCell(value, column.symbol ?? "$", locale) ?? "") : String(value);
|
|
3344
|
+
case "date":
|
|
3345
|
+
return String(formatDateCell(value, locale) ?? "");
|
|
3346
|
+
case "datetime":
|
|
3347
|
+
return String(formatDateTimeCell(value, locale) ?? "");
|
|
3348
|
+
case "checkbox":
|
|
3349
|
+
return value === true || value === "true" ? "true" : "false";
|
|
3350
|
+
default:
|
|
3351
|
+
return String(value);
|
|
3352
|
+
}
|
|
3353
|
+
}
|
|
3354
|
+
/**
|
|
3355
|
+
* One cell's right-click menu -- mounted by `body-cells.tsx`'s `DataTableBodyCell`
|
|
3356
|
+
* only while actually open (the same "don't keep an unused floating-ui instance alive
|
|
3357
|
+
* per cell" reasoning as that file's own portaled chip-list popover), so this never
|
|
3358
|
+
* costs anything for the row×column cells a user never right-clicks.
|
|
3359
|
+
*/ function CellContextMenu({ column, row, value, locale, point, open, onOpenChange, onCommit, onViewEditHistory, cellContextMenuItems }) {
|
|
3360
|
+
const clearable = isClearableEditableCell(column);
|
|
3361
|
+
const pasteable = isSimpleEditableCell(column);
|
|
3362
|
+
return /*#__PURE__*/ jsxs(MenuRoot, {
|
|
3363
|
+
open: open,
|
|
3364
|
+
onOpenChange: onOpenChange,
|
|
3365
|
+
placement: "bottom-start",
|
|
3366
|
+
strategy: "fixed",
|
|
3367
|
+
gutter: 2,
|
|
3368
|
+
children: [
|
|
3369
|
+
/*#__PURE__*/ jsx(MenuAnchor, {
|
|
3370
|
+
"aria-hidden": true,
|
|
3371
|
+
style: {
|
|
3372
|
+
position: "fixed",
|
|
3373
|
+
top: point.y,
|
|
3374
|
+
left: point.x,
|
|
3375
|
+
width: 0,
|
|
3376
|
+
height: 0,
|
|
3377
|
+
pointerEvents: "none"
|
|
3378
|
+
}
|
|
3379
|
+
}),
|
|
3380
|
+
/*#__PURE__*/ jsx(MenuPositioner, {
|
|
3381
|
+
children: /*#__PURE__*/ jsx(MenuContent, {
|
|
3382
|
+
style: {
|
|
3383
|
+
minWidth: 180
|
|
3384
|
+
},
|
|
3385
|
+
children: /*#__PURE__*/ jsxs(MenuScrollArea, {
|
|
3386
|
+
children: [
|
|
3387
|
+
/*#__PURE__*/ jsx(MenuItem, {
|
|
3388
|
+
onClick: ()=>{
|
|
3389
|
+
const text = cellValueAsText(column, value, locale);
|
|
3390
|
+
// Best-effort: an insecure context or a denied permission rejects
|
|
3391
|
+
// this silently (no `MenuItem` state depends on the outcome) --
|
|
3392
|
+
// there's nothing else for a plain "Copy" item to do about a
|
|
3393
|
+
// browser that refused the write.
|
|
3394
|
+
void navigator.clipboard?.writeText?.(text);
|
|
3395
|
+
},
|
|
3396
|
+
children: /*#__PURE__*/ jsx(MenuItemLabel, {
|
|
3397
|
+
children: "Copy"
|
|
3398
|
+
})
|
|
3399
|
+
}),
|
|
3400
|
+
pasteable && /*#__PURE__*/ jsx(MenuItem, {
|
|
3401
|
+
onClick: ()=>{
|
|
3402
|
+
void navigator.clipboard?.readText?.().then((text)=>onCommit(text)).catch(()=>{
|
|
3403
|
+
// Clipboard read denied/unavailable -- best-effort, same
|
|
3404
|
+
// reasoning as Copy above.
|
|
3405
|
+
});
|
|
3406
|
+
},
|
|
3407
|
+
children: /*#__PURE__*/ jsx(MenuItemLabel, {
|
|
3408
|
+
children: "Paste"
|
|
3409
|
+
})
|
|
3410
|
+
}),
|
|
3411
|
+
clearable && /*#__PURE__*/ jsx(MenuItem, {
|
|
3412
|
+
onClick: ()=>onCommit(""),
|
|
3413
|
+
children: /*#__PURE__*/ jsx(MenuItemLabel, {
|
|
3414
|
+
children: "Clear value"
|
|
3415
|
+
})
|
|
3416
|
+
}),
|
|
3417
|
+
onViewEditHistory && /*#__PURE__*/ jsx(MenuItem, {
|
|
3418
|
+
onClick: ()=>onViewEditHistory(row, column.key),
|
|
3419
|
+
children: /*#__PURE__*/ jsx(MenuItemLabel, {
|
|
3420
|
+
children: "View edit history"
|
|
3421
|
+
})
|
|
3422
|
+
}),
|
|
3423
|
+
cellContextMenuItems?.({
|
|
3424
|
+
row,
|
|
3425
|
+
column,
|
|
3426
|
+
value
|
|
3427
|
+
})
|
|
3428
|
+
]
|
|
3429
|
+
})
|
|
3430
|
+
})
|
|
3431
|
+
})
|
|
3432
|
+
]
|
|
3433
|
+
});
|
|
3434
|
+
}
|
|
3435
|
+
|
|
3090
3436
|
/** Fallback width (px) used to compute a pinned column's cumulative sticky offset when
|
|
3091
3437
|
* it has no explicit `width` and hasn't been drag-resized -- pinning needs a STATIC
|
|
3092
3438
|
* number to put in an inline `insetInlineStart`, and `table-layout: fixed` only
|
|
@@ -3245,11 +3591,11 @@ function RatingCell({ value, maxRating = 5 }) {
|
|
|
3245
3591
|
// recipe, so a per-instance variant can override the root's default.
|
|
3246
3592
|
const { ClassNamesProvider: TriggerClassNamesProvider, PropsProvider: TriggerPropsProvider, useProps: useTriggerProps, withContext: withTriggerContext, useClassNames: useTriggerClassNames } = createSlotRecipeContext(selectTrigger);
|
|
3247
3593
|
const { ClassNamesProvider: ContentClassNamesProvider, withContext: withContentContext, useClassNames: useContentClassNames } = createSlotRecipeContext(select);
|
|
3248
|
-
const { ClassNamesProvider: ItemClassNamesProvider
|
|
3249
|
-
const withStateProps$
|
|
3594
|
+
const { ClassNamesProvider: ItemClassNamesProvider, PropsProvider: ItemPropsProvider, useProps: useItemProps, withContext: withItemContext, useClassNames: useItemClassNames } = createSlotRecipeContext(selectItem);
|
|
3595
|
+
const withStateProps$2 = createWithStateProps([
|
|
3250
3596
|
useSelectContext
|
|
3251
3597
|
]);
|
|
3252
|
-
const withItemStateProps
|
|
3598
|
+
const withItemStateProps = createWithStateProps([
|
|
3253
3599
|
useSelectItemContext
|
|
3254
3600
|
]);
|
|
3255
3601
|
// NOTE: `gutter` and `overflowPadding` are specified in Rootage (`select.yaml`,
|
|
@@ -3270,7 +3616,7 @@ const SelectRoot = (props)=>{
|
|
|
3270
3616
|
value: contentClassNames,
|
|
3271
3617
|
children: /*#__PURE__*/ jsx(TriggerPropsProvider, {
|
|
3272
3618
|
value: triggerVariantProps,
|
|
3273
|
-
children: /*#__PURE__*/ jsx(ItemPropsProvider
|
|
3619
|
+
children: /*#__PURE__*/ jsx(ItemPropsProvider, {
|
|
3274
3620
|
value: itemVariantProps,
|
|
3275
3621
|
children: /*#__PURE__*/ jsx(Select.Root, {
|
|
3276
3622
|
...otherProps
|
|
@@ -3310,8 +3656,8 @@ const SelectTrigger = /*#__PURE__*/ React.forwardRef(({ className, ...props }, r
|
|
|
3310
3656
|
});
|
|
3311
3657
|
});
|
|
3312
3658
|
SelectTrigger.displayName = "SelectTrigger";
|
|
3313
|
-
const SelectValue = withTriggerContext(withStateProps$
|
|
3314
|
-
const SelectPlaceholder = withTriggerContext(withStateProps$
|
|
3659
|
+
const SelectValue = withTriggerContext(withStateProps$2(Select.Value), "value");
|
|
3660
|
+
const SelectPlaceholder = withTriggerContext(withStateProps$2(Select.Placeholder), "placeholder");
|
|
3315
3661
|
const SelectPrefixIcon = /*#__PURE__*/ React.forwardRef(({ fallback, className, ...otherProps }, ref)=>{
|
|
3316
3662
|
const { selectedItem, stateProps } = useSelectContext();
|
|
3317
3663
|
const classNames = useTriggerClassNames();
|
|
@@ -3326,7 +3672,7 @@ const SelectPrefixIcon = /*#__PURE__*/ React.forwardRef(({ fallback, className,
|
|
|
3326
3672
|
});
|
|
3327
3673
|
});
|
|
3328
3674
|
SelectPrefixIcon.displayName = "SelectPrefixIcon";
|
|
3329
|
-
const SelectSuffixIcon = withTriggerContext(withStateProps$
|
|
3675
|
+
const SelectSuffixIcon = withTriggerContext(withStateProps$2(InternalIcon), "suffixIcon");
|
|
3330
3676
|
const SelectPositioner = /*#__PURE__*/ React.forwardRef(({ className, ...props }, ref)=>{
|
|
3331
3677
|
const classNames = useContentClassNames();
|
|
3332
3678
|
return /*#__PURE__*/ jsx(Select.Positioner, {
|
|
@@ -3357,12 +3703,12 @@ withContentContext(Select.Group, "group");
|
|
|
3357
3703
|
withContentContext(Select.GroupLabel, "groupLabel");
|
|
3358
3704
|
const SelectItem = /*#__PURE__*/ React.forwardRef(({ prefixIcon, className, ...props }, ref)=>{
|
|
3359
3705
|
const [variantProps, otherProps] = selectItem.splitVariantProps(props);
|
|
3360
|
-
const rootProps = useItemProps
|
|
3706
|
+
const rootProps = useItemProps();
|
|
3361
3707
|
const classNames = selectItem({
|
|
3362
3708
|
...rootProps,
|
|
3363
3709
|
...variantProps
|
|
3364
3710
|
});
|
|
3365
|
-
return /*#__PURE__*/ jsx(ItemClassNamesProvider
|
|
3711
|
+
return /*#__PURE__*/ jsx(ItemClassNamesProvider, {
|
|
3366
3712
|
value: classNames,
|
|
3367
3713
|
children: /*#__PURE__*/ jsx(Select.Item, {
|
|
3368
3714
|
ref: ref,
|
|
@@ -3387,7 +3733,7 @@ const SelectItemPrefixIcon = /*#__PURE__*/ React.forwardRef(({ svg: svgOverride,
|
|
|
3387
3733
|
});
|
|
3388
3734
|
});
|
|
3389
3735
|
SelectItemPrefixIcon.displayName = "SelectItemPrefixIcon";
|
|
3390
|
-
const SelectItemBody = withItemContext
|
|
3736
|
+
const SelectItemBody = withItemContext(withItemStateProps(Primitive.div), "body");
|
|
3391
3737
|
const SelectItemLabel = /*#__PURE__*/ React.forwardRef(({ children, className, ...otherProps }, ref)=>{
|
|
3392
3738
|
const { label, stateProps } = useSelectItemContext();
|
|
3393
3739
|
const classNames = useItemClassNames();
|
|
@@ -3402,7 +3748,7 @@ const SelectItemLabel = /*#__PURE__*/ React.forwardRef(({ children, className, .
|
|
|
3402
3748
|
});
|
|
3403
3749
|
});
|
|
3404
3750
|
SelectItemLabel.displayName = "SelectItemLabel";
|
|
3405
|
-
withItemContext
|
|
3751
|
+
withItemContext(withItemStateProps(Primitive.span), "description");
|
|
3406
3752
|
const SelectItemIndicator = /*#__PURE__*/ React.forwardRef(({ selected: selectedSvg, unselected: unselectedSvg, className, ...otherProps }, ref)=>{
|
|
3407
3753
|
const { isSelected, stateProps } = useSelectItemContext();
|
|
3408
3754
|
const classNames = useItemClassNames();
|
|
@@ -3434,9 +3780,9 @@ const SelectHiddenSelect = /*#__PURE__*/ React.forwardRef((props, ref)=>{
|
|
|
3434
3780
|
});
|
|
3435
3781
|
SelectHiddenSelect.displayName = "SelectHiddenSelect";
|
|
3436
3782
|
|
|
3437
|
-
const { withContext: withContext$
|
|
3783
|
+
const { withContext: withContext$3, ClassNamesProvider: ClassNamesProvider$1 } = createSlotRecipeContext(switchStyle);
|
|
3438
3784
|
const { withContext: withControlContext, PropsProvider: ControlPropsProvider, withProvider: withControlProvider } = createSlotRecipeContext(switchmark);
|
|
3439
|
-
const withStateProps$
|
|
3785
|
+
const withStateProps$1 = createWithStateProps([
|
|
3440
3786
|
useSwitchContext
|
|
3441
3787
|
]);
|
|
3442
3788
|
const SwitchRoot = /*#__PURE__*/ React__default.forwardRef(({ className, ...props }, ref)=>{
|
|
@@ -3447,7 +3793,7 @@ const SwitchRoot = /*#__PURE__*/ React__default.forwardRef(({ className, ...prop
|
|
|
3447
3793
|
const classNames = switchStyle(switchVariantProps);
|
|
3448
3794
|
return /*#__PURE__*/ jsx(ControlPropsProvider, {
|
|
3449
3795
|
value: switchmarkVariantProps,
|
|
3450
|
-
children: /*#__PURE__*/ jsx(ClassNamesProvider$
|
|
3796
|
+
children: /*#__PURE__*/ jsx(ClassNamesProvider$1, {
|
|
3451
3797
|
value: classNames,
|
|
3452
3798
|
children: /*#__PURE__*/ jsx(Switch.Root, {
|
|
3453
3799
|
ref: ref,
|
|
@@ -3460,20 +3806,20 @@ const SwitchRoot = /*#__PURE__*/ React__default.forwardRef(({ className, ...prop
|
|
|
3460
3806
|
SwitchRoot.displayName = "SwitchRoot";
|
|
3461
3807
|
const SwitchControl = withScaleFeedback(withControlProvider(Switch.Control, "root"));
|
|
3462
3808
|
const SwitchThumb = withControlContext(Switch.Thumb, "thumb");
|
|
3463
|
-
withContext$
|
|
3809
|
+
withContext$3(withStateProps$1(Primitive.span), "label");
|
|
3464
3810
|
const SwitchHiddenInput = Switch.HiddenInput;
|
|
3465
3811
|
|
|
3466
3812
|
// src/use-layout-effect.tsx
|
|
3467
3813
|
var useLayoutEffect2 = globalThis?.document ? React.useLayoutEffect : ()=>{};
|
|
3468
3814
|
|
|
3469
|
-
const { withProvider: withProvider$1, withContext: withContext$
|
|
3815
|
+
const { withProvider: withProvider$1, withContext: withContext$2, useClassNames: useClassNames$1 } = createSlotRecipeContext(textInput);
|
|
3470
3816
|
const withFieldStateProps = createWithStateProps([
|
|
3471
3817
|
{
|
|
3472
3818
|
useContext: useFieldContext,
|
|
3473
3819
|
strict: false
|
|
3474
3820
|
}
|
|
3475
3821
|
]);
|
|
3476
|
-
const withStateProps
|
|
3822
|
+
const withStateProps = createWithStateProps([
|
|
3477
3823
|
useTextFieldContext,
|
|
3478
3824
|
{
|
|
3479
3825
|
useContext: useFieldContext,
|
|
@@ -3481,12 +3827,12 @@ const withStateProps$1 = createWithStateProps([
|
|
|
3481
3827
|
}
|
|
3482
3828
|
]);
|
|
3483
3829
|
const TextFieldRoot = withProvider$1(withFieldStateProps(TextField.Root), "root");
|
|
3484
|
-
withContext$
|
|
3485
|
-
withContext$
|
|
3486
|
-
withContext$
|
|
3487
|
-
withContext$
|
|
3830
|
+
withContext$2(withStateProps(InternalIcon), "prefixIcon");
|
|
3831
|
+
withContext$2(withStateProps(Primitive.span), "prefixText");
|
|
3832
|
+
withContext$2(withStateProps(InternalIcon), "suffixIcon");
|
|
3833
|
+
withContext$2(withStateProps(Primitive.span), "suffixText");
|
|
3488
3834
|
const TextFieldInput = /*#__PURE__*/ forwardRef(({ className, ...otherProps }, ref)=>{
|
|
3489
|
-
const classNames = useClassNames$
|
|
3835
|
+
const classNames = useClassNames$1();
|
|
3490
3836
|
const textFieldContext = useTextFieldContext();
|
|
3491
3837
|
const fieldContext = useFieldContext({
|
|
3492
3838
|
strict: false
|
|
@@ -3504,7 +3850,7 @@ const TextFieldInput = /*#__PURE__*/ forwardRef(({ className, ...otherProps }, r
|
|
|
3504
3850
|
});
|
|
3505
3851
|
TextFieldInput.displayName = "TextFieldInput";
|
|
3506
3852
|
const TextFieldTextarea = /*#__PURE__*/ forwardRef(({ className, autoresize = true, ...otherProps }, ref)=>{
|
|
3507
|
-
const classNames = useClassNames$
|
|
3853
|
+
const classNames = useClassNames$1();
|
|
3508
3854
|
const textFieldContext = useTextFieldContext();
|
|
3509
3855
|
const fieldContext = useFieldContext({
|
|
3510
3856
|
strict: false
|
|
@@ -3561,7 +3907,7 @@ const TextFieldTextarea = /*#__PURE__*/ forwardRef(({ className, autoresize = tr
|
|
|
3561
3907
|
});
|
|
3562
3908
|
TextFieldTextarea.displayName = "TextFieldTextarea";
|
|
3563
3909
|
|
|
3564
|
-
__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))}");
|
|
3910
|
+
__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))}");
|
|
3565
3911
|
|
|
3566
3912
|
const createClassName = (className, variants, compoundVariants = [])=>{
|
|
3567
3913
|
const variantKeys = Object.keys(variants);
|
|
@@ -3755,6 +4101,22 @@ const dataTableSlotNames = [
|
|
|
3755
4101
|
[
|
|
3756
4102
|
"selectionCell",
|
|
3757
4103
|
"seed-data-table__selectionCell"
|
|
4104
|
+
],
|
|
4105
|
+
[
|
|
4106
|
+
"bulkBar",
|
|
4107
|
+
"seed-data-table__bulkBar"
|
|
4108
|
+
],
|
|
4109
|
+
[
|
|
4110
|
+
"bulkBarCount",
|
|
4111
|
+
"seed-data-table__bulkBarCount"
|
|
4112
|
+
],
|
|
4113
|
+
[
|
|
4114
|
+
"bulkBarClear",
|
|
4115
|
+
"seed-data-table__bulkBarClear"
|
|
4116
|
+
],
|
|
4117
|
+
[
|
|
4118
|
+
"bulkBarActions",
|
|
4119
|
+
"seed-data-table__bulkBarActions"
|
|
3758
4120
|
]
|
|
3759
4121
|
];
|
|
3760
4122
|
const defaultVariant$6 = {
|
|
@@ -3792,7 +4154,7 @@ Object.assign(dataTable, {
|
|
|
3792
4154
|
// other file (`DataTable.tsx`, `column-menu.tsx`, `resize.tsx`, `column-calc.tsx`,
|
|
3793
4155
|
// `toolbar.tsx`, `body-cells.tsx`, `editors.tsx`) imports its exports from here rather
|
|
3794
4156
|
// than re-deriving its own.
|
|
3795
|
-
const { ClassNamesProvider
|
|
4157
|
+
const { ClassNamesProvider, withContext: withContext$1, useClassNames } = createSlotRecipeContext(dataTable);
|
|
3796
4158
|
|
|
3797
4159
|
/**
|
|
3798
4160
|
* The pure filter itself, split out of `useSelectSearch` below so it can be
|
|
@@ -4500,7 +4862,7 @@ function isCellTruncated(cell) {
|
|
|
4500
4862
|
* actually reported against a pinned column. */ function isChipListEditable(column) {
|
|
4501
4863
|
return !!column.editable && column.type === "chip-list";
|
|
4502
4864
|
}
|
|
4503
|
-
function ColumnBodyCells({ columns, row, classNames, locale, selectable, stickyFirst, stickyLast }) {
|
|
4865
|
+
function ColumnBodyCells({ columns, row, classNames, locale, selectable, stickyFirst, stickyLast, cellContextMenu, onViewEditHistory, cellContextMenuItems }) {
|
|
4504
4866
|
const effective = useEffectiveColumns(columns, {
|
|
4505
4867
|
selectable,
|
|
4506
4868
|
stickyFirst
|
|
@@ -4515,7 +4877,10 @@ function ColumnBodyCells({ columns, row, classNames, locale, selectable, stickyF
|
|
|
4515
4877
|
row: row,
|
|
4516
4878
|
classNames: classNames,
|
|
4517
4879
|
locale: locale,
|
|
4518
|
-
sticky: sticky
|
|
4880
|
+
sticky: sticky,
|
|
4881
|
+
cellContextMenu: cellContextMenu,
|
|
4882
|
+
onViewEditHistory: onViewEditHistory,
|
|
4883
|
+
cellContextMenuItems: cellContextMenuItems
|
|
4519
4884
|
}, column.key);
|
|
4520
4885
|
})
|
|
4521
4886
|
});
|
|
@@ -4527,7 +4892,7 @@ function ColumnBodyCells({ columns, row, classNames, locale, selectable, stickyF
|
|
|
4527
4892
|
* on every row's render. React keeps this instance stable across re-renders because
|
|
4528
4893
|
* `ColumnBodyCells` keys it by `column.key` the same way it always keyed the bare
|
|
4529
4894
|
* `<DataTableCell>` before this split.
|
|
4530
|
-
*/ function DataTableBodyCell({ column, row, classNames, locale, sticky }) {
|
|
4895
|
+
*/ function DataTableBodyCell({ column, row, classNames, locale, sticky, cellContextMenu, onViewEditHistory, cellContextMenuItems }) {
|
|
4531
4896
|
const { startEdit, cancelEdit, isEditing, isPending, commitEdit, isPeeking, openPeek, closePeek } = useDataTableContext();
|
|
4532
4897
|
const cellRef = React.useRef(null);
|
|
4533
4898
|
// Feature B rework (#8) -- the chip-list popover's own live position, in VIEWPORT
|
|
@@ -4543,6 +4908,16 @@ function ColumnBodyCells({ columns, row, classNames, locale, selectable, stickyF
|
|
|
4543
4908
|
// editable cell's existing double-click-to-edit (tranche 5, unchanged) must keep
|
|
4544
4909
|
// rendering exactly as it always has, not suddenly gain overlay chrome.
|
|
4545
4910
|
const [overlayOpen, setOverlayOpen] = React.useState(false);
|
|
4911
|
+
// Attio parity (attio-column-features-full.md gap #1) -- the right-click menu's own
|
|
4912
|
+
// point + open state. `contextMenuPoint` is frozen at the moment of the right-click
|
|
4913
|
+
// (never re-read live), same reasoning as `overlayOpen` above: the menu shouldn't
|
|
4914
|
+
// chase the cursor after it opens. `<CellContextMenu>` itself is only rendered below
|
|
4915
|
+
// while `contextMenuOpen` is true (not permanently mounted per cell) -- unlike
|
|
4916
|
+
// `ColumnMenu` (one per COLUMN, header row only), this component runs once per
|
|
4917
|
+
// row×column cell, and a floating-ui instance genuinely idle in every one of
|
|
4918
|
+
// thousands of cells would be real, avoidable overhead.
|
|
4919
|
+
const [contextMenuPoint, setContextMenuPoint] = React.useState(null);
|
|
4920
|
+
const [contextMenuOpen, setContextMenuOpen] = React.useState(false);
|
|
4546
4921
|
const alwaysLive = isAlwaysLiveEditable(column);
|
|
4547
4922
|
const chipListEditable = isChipListEditable(column);
|
|
4548
4923
|
// A gated `editable` column (everything except the always-live widgets AND the
|
|
@@ -4594,6 +4969,19 @@ function ColumnBodyCells({ columns, row, classNames, locale, selectable, stickyF
|
|
|
4594
4969
|
setOverlayOpen(isCellTruncated(event.currentTarget));
|
|
4595
4970
|
startEdit(row.id, column.key);
|
|
4596
4971
|
}
|
|
4972
|
+
function handleContextMenu(event) {
|
|
4973
|
+
if (!cellContextMenu) return;
|
|
4974
|
+
// Replaces the browser's own native context menu (Attio's own gesture) -- and
|
|
4975
|
+
// must not also reach the row's own `onRowClick`, same reasoning as every other
|
|
4976
|
+
// cell-local click handler in this file.
|
|
4977
|
+
event.preventDefault();
|
|
4978
|
+
event.stopPropagation();
|
|
4979
|
+
setContextMenuPoint({
|
|
4980
|
+
x: event.clientX,
|
|
4981
|
+
y: event.clientY
|
|
4982
|
+
});
|
|
4983
|
+
setContextMenuOpen(true);
|
|
4984
|
+
}
|
|
4597
4985
|
function handleClick(event) {
|
|
4598
4986
|
if (chipListEditable) {
|
|
4599
4987
|
// Attio's own chip-list gesture: click opens the editor regardless of
|
|
@@ -4697,7 +5085,7 @@ function ColumnBodyCells({ columns, row, classNames, locale, selectable, stickyF
|
|
|
4697
5085
|
const pending = isPending(row.id, column.key);
|
|
4698
5086
|
return /*#__PURE__*/ jsxs(Fragment, {
|
|
4699
5087
|
children: [
|
|
4700
|
-
/*#__PURE__*/
|
|
5088
|
+
/*#__PURE__*/ jsxs(DataTableCell, {
|
|
4701
5089
|
ref: cellRef,
|
|
4702
5090
|
"data-numeric": dataAttr(column.type === "number" || column.type === "currency"),
|
|
4703
5091
|
"data-editable": dataAttr(!!column.editable),
|
|
@@ -4708,14 +5096,34 @@ function ColumnBodyCells({ columns, row, classNames, locale, selectable, stickyF
|
|
|
4708
5096
|
style: sticky.style,
|
|
4709
5097
|
onClick: handleClick,
|
|
4710
5098
|
onDoubleClick: handleDoubleClick,
|
|
4711
|
-
|
|
4712
|
-
|
|
4713
|
-
|
|
4714
|
-
|
|
4715
|
-
|
|
4716
|
-
|
|
4717
|
-
|
|
4718
|
-
|
|
5099
|
+
onContextMenu: handleContextMenu,
|
|
5100
|
+
children: [
|
|
5101
|
+
/*#__PURE__*/ jsx(CellContent, {
|
|
5102
|
+
column: column,
|
|
5103
|
+
row: row,
|
|
5104
|
+
rowId: row.id,
|
|
5105
|
+
classNames: classNames,
|
|
5106
|
+
locale: locale,
|
|
5107
|
+
overlayActive: overlayActive
|
|
5108
|
+
}),
|
|
5109
|
+
cellContextMenu && contextMenuOpen && contextMenuPoint && // A `<td>` (unlike its own parent `<tr>`) can validly hold any flow
|
|
5110
|
+
// content, so `CellContextMenu`'s own 0x0 `MenuAnchor` -- itself never
|
|
5111
|
+
// portaled, only `MenuPositioner`'s actual dropdown content is (via
|
|
5112
|
+
// floating-ui's own `FloatingPortal`, straight to `document.body`) --
|
|
5113
|
+
// renders here, INSIDE this cell, rather than as a sibling of it.
|
|
5114
|
+
/*#__PURE__*/ jsx(CellContextMenu, {
|
|
5115
|
+
column: column,
|
|
5116
|
+
row: row,
|
|
5117
|
+
value: rawValue,
|
|
5118
|
+
locale: locale,
|
|
5119
|
+
point: contextMenuPoint,
|
|
5120
|
+
open: contextMenuOpen,
|
|
5121
|
+
onOpenChange: setContextMenuOpen,
|
|
5122
|
+
onCommit: (value)=>commitEdit(row.id, column.key, value),
|
|
5123
|
+
onViewEditHistory: onViewEditHistory,
|
|
5124
|
+
cellContextMenuItems: cellContextMenuItems
|
|
5125
|
+
})
|
|
5126
|
+
]
|
|
4719
5127
|
}),
|
|
4720
5128
|
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.
|
|
4721
5129
|
/*#__PURE__*/ jsx("div", {
|
|
@@ -4945,64 +5353,52 @@ function DisplayCellContent({ column, row, value, classNames, locale }) {
|
|
|
4945
5353
|
}
|
|
4946
5354
|
}
|
|
4947
5355
|
|
|
4948
|
-
const
|
|
4949
|
-
const
|
|
4950
|
-
const
|
|
4951
|
-
|
|
4952
|
-
|
|
4953
|
-
|
|
4954
|
-
|
|
4955
|
-
|
|
4956
|
-
|
|
4957
|
-
|
|
4958
|
-
|
|
4959
|
-
|
|
4960
|
-
|
|
4961
|
-
|
|
4962
|
-
|
|
4963
|
-
|
|
4964
|
-
|
|
4965
|
-
|
|
4966
|
-
|
|
5356
|
+
const DataTableBulkBarRoot = withContext$1(Primitive.div, "bulkBar");
|
|
5357
|
+
const DataTableBulkBarCount = withContext$1(Primitive.span, "bulkBarCount");
|
|
5358
|
+
const DataTableBulkBarClear = withContext$1(Primitive.button, "bulkBarClear");
|
|
5359
|
+
/** Wraps whatever the CONSUMER passes as this bar's own action buttons -- the bar
|
|
5360
|
+
* itself only ever supplies chrome (count + clear); every actual action (Add to
|
|
5361
|
+
* list, Send email, …) is the consumer's own `<ActionButton>`/`<Menu>`, the same
|
|
5362
|
+
* "chrome vs. consumer content" split `toolbarActions`/`toolbarEnd` already draw for
|
|
5363
|
+
* the toolbar's own custom actions. */ const DataTableBulkBarActions = withContext$1(Primitive.div, "bulkBarActions");
|
|
5364
|
+
/**
|
|
5365
|
+
* The floating bulk-action pill (gap #4) -- renders nothing while no row is selected
|
|
5366
|
+
* (reads `selectedIds` off the table-wide context itself, so a consumer doesn't have
|
|
5367
|
+
* to gate this on `selectable`/`selectedIds` on their own). Composed as the LAST
|
|
5368
|
+
* child of the table's own root (`DataTable.tsx`), which is `position: relative` for
|
|
5369
|
+
* exactly this reason -- see `data-table.ts`'s own `bulkBar` slot comment for why its
|
|
5370
|
+
* geometry borrows `contextual-floating-button`'s already-measured vars rather than a
|
|
5371
|
+
* guessed literal.
|
|
5372
|
+
*/ function DataTableBulkBar({ clearLabel = "Clear selection", children }) {
|
|
5373
|
+
const { selectedIds, clearSelection } = useDataTableContext();
|
|
5374
|
+
if (selectedIds.length === 0) return null;
|
|
5375
|
+
return /*#__PURE__*/ jsxs(DataTableBulkBarRoot, {
|
|
5376
|
+
role: "toolbar",
|
|
5377
|
+
"aria-label": "Bulk actions",
|
|
5378
|
+
"aria-orientation": "horizontal",
|
|
5379
|
+
children: [
|
|
5380
|
+
/*#__PURE__*/ jsxs(DataTableBulkBarCount, {
|
|
5381
|
+
"aria-live": "polite",
|
|
5382
|
+
children: [
|
|
5383
|
+
selectedIds.length,
|
|
5384
|
+
" selected"
|
|
5385
|
+
]
|
|
5386
|
+
}),
|
|
5387
|
+
/*#__PURE__*/ jsx(DataTableBulkBarClear, {
|
|
5388
|
+
type: "button",
|
|
5389
|
+
"aria-label": clearLabel,
|
|
5390
|
+
onClick: clearSelection,
|
|
5391
|
+
children: /*#__PURE__*/ jsx(IconXmarkLine_default, {
|
|
5392
|
+
size: 12,
|
|
5393
|
+
"aria-hidden": "true"
|
|
5394
|
+
})
|
|
5395
|
+
}),
|
|
5396
|
+
children && /*#__PURE__*/ jsx(DataTableBulkBarActions, {
|
|
5397
|
+
children: children(selectedIds)
|
|
4967
5398
|
})
|
|
4968
|
-
|
|
4969
|
-
});
|
|
4970
|
-
};
|
|
4971
|
-
Menu.Anchor;
|
|
4972
|
-
const MenuTrigger = Menu.Trigger;
|
|
4973
|
-
const MenuPositioner = /*#__PURE__*/ React.forwardRef(({ className, ...props }, ref)=>{
|
|
4974
|
-
const classNames = useClassNames();
|
|
4975
|
-
return /*#__PURE__*/ jsx(Menu.Positioner, {
|
|
4976
|
-
ref: ref,
|
|
4977
|
-
className: clsx(classNames.positioner, className),
|
|
4978
|
-
...props
|
|
4979
|
-
});
|
|
4980
|
-
});
|
|
4981
|
-
MenuPositioner.displayName = "MenuPositioner";
|
|
4982
|
-
const MenuContent = withContext$1(Menu.Content, "content");
|
|
4983
|
-
const MenuScrollArea = withContext$1(withStateProps(Primitive.div), "scrollArea");
|
|
4984
|
-
withContext$1(Menu.Group, "group");
|
|
4985
|
-
withContext$1(Menu.GroupLabel, "groupLabel");
|
|
4986
|
-
const MenuItem = /*#__PURE__*/ React.forwardRef(({ className: propClassName, ...props }, ref)=>{
|
|
4987
|
-
const [variantProps, otherProps] = menuItem.splitVariantProps(props);
|
|
4988
|
-
const parentProps = useItemProps();
|
|
4989
|
-
const classNames = menuItem({
|
|
4990
|
-
...parentProps,
|
|
4991
|
-
...variantProps
|
|
4992
|
-
});
|
|
4993
|
-
return /*#__PURE__*/ jsx(ItemClassNamesProvider, {
|
|
4994
|
-
value: classNames,
|
|
4995
|
-
children: /*#__PURE__*/ jsx(Menu.Item, {
|
|
4996
|
-
ref: ref,
|
|
4997
|
-
className: clsx(classNames.root, propClassName),
|
|
4998
|
-
...otherProps
|
|
4999
|
-
})
|
|
5399
|
+
]
|
|
5000
5400
|
});
|
|
5001
|
-
}
|
|
5002
|
-
MenuItem.displayName = "MenuItem";
|
|
5003
|
-
withItemContext(withItemStateProps(Primitive.div), "body");
|
|
5004
|
-
const MenuItemLabel = withItemContext(withItemStateProps(Primitive.span), "label");
|
|
5005
|
-
withItemContext(withItemStateProps(Primitive.span), "description");
|
|
5401
|
+
}
|
|
5006
5402
|
|
|
5007
5403
|
function ColumnCalcCells({ columns, rows, selectable, stickyFirst, stickyLast }) {
|
|
5008
5404
|
const effective = useEffectiveColumns(columns, {
|
|
@@ -5029,10 +5425,11 @@ function ColumnCalcCells({ columns, rows, selectable, stickyFirst, stickyLast })
|
|
|
5029
5425
|
})
|
|
5030
5426
|
});
|
|
5031
5427
|
}
|
|
5032
|
-
function formatCalcValue(value) {
|
|
5033
|
-
|
|
5428
|
+
function formatCalcValue(value, op) {
|
|
5429
|
+
const formatted = value.toLocaleString(undefined, {
|
|
5034
5430
|
maximumFractionDigits: 2
|
|
5035
5431
|
});
|
|
5432
|
+
return op === "percentEmpty" || op === "percentFilled" ? `${formatted}%` : formatted;
|
|
5036
5433
|
}
|
|
5037
5434
|
/**
|
|
5038
5435
|
* One ColumnCalc footer cell -- a click-anywhere-in-the-cell trigger (crisp's own
|
|
@@ -5067,7 +5464,7 @@ function formatCalcValue(value) {
|
|
|
5067
5464
|
children: [
|
|
5068
5465
|
/*#__PURE__*/ jsx("span", {
|
|
5069
5466
|
className: "seed-data-table-column-calc-value",
|
|
5070
|
-
children: formatCalcValue(result)
|
|
5467
|
+
children: formatCalcValue(result, op)
|
|
5071
5468
|
}),
|
|
5072
5469
|
/*#__PURE__*/ jsx("span", {
|
|
5073
5470
|
className: "seed-data-table-column-calc-op",
|
|
@@ -5088,21 +5485,13 @@ function formatCalcValue(value) {
|
|
|
5088
5485
|
style: {
|
|
5089
5486
|
minWidth: 160
|
|
5090
5487
|
},
|
|
5091
|
-
children: /*#__PURE__*/
|
|
5092
|
-
children:
|
|
5093
|
-
|
|
5094
|
-
onClick: ()=>setColumnCalcOp(column.key, candidate),
|
|
5095
|
-
children: /*#__PURE__*/ jsx(MenuItemLabel, {
|
|
5096
|
-
children: DATA_TABLE_CALC_LABEL[candidate]
|
|
5097
|
-
})
|
|
5098
|
-
}, candidate)),
|
|
5099
|
-
op && /*#__PURE__*/ jsx(MenuItem, {
|
|
5100
|
-
onClick: ()=>setColumnCalcOp(column.key, undefined),
|
|
5488
|
+
children: /*#__PURE__*/ jsx(MenuScrollArea, {
|
|
5489
|
+
children: ops.map((candidate)=>/*#__PURE__*/ jsx(MenuItem, {
|
|
5490
|
+
onClick: ()=>setColumnCalcOp(column.key, candidate),
|
|
5101
5491
|
children: /*#__PURE__*/ jsx(MenuItemLabel, {
|
|
5102
|
-
children:
|
|
5492
|
+
children: DATA_TABLE_CALC_LABEL[candidate]
|
|
5103
5493
|
})
|
|
5104
|
-
})
|
|
5105
|
-
]
|
|
5494
|
+
}, candidate))
|
|
5106
5495
|
})
|
|
5107
5496
|
})
|
|
5108
5497
|
})
|
|
@@ -5381,9 +5770,9 @@ function ColumnRenameInput({ column, initialLabel, onDone }) {
|
|
|
5381
5770
|
const columnLabel = typeof label === "string" ? label : column.key;
|
|
5382
5771
|
const pinned = isColumnPinned(column.key);
|
|
5383
5772
|
// Attio/GRC's "Remove sort" (live-review fix D) -- the header's own click-to-sort
|
|
5384
|
-
// (`SortIndicator`/`toggleSort`, above)
|
|
5385
|
-
//
|
|
5386
|
-
// sense to show on the column that IS currently sorted.
|
|
5773
|
+
// (`SortIndicator`/`toggleSort`, above) already cycles asc -> desc -> none on its
|
|
5774
|
+
// own third click, but this stays as a one-click shortcut to the same place, so it
|
|
5775
|
+
// only makes sense to show on the column that IS currently sorted.
|
|
5387
5776
|
const isSorted = sort?.key === column.key;
|
|
5388
5777
|
const stopPropagation = (event)=>event.stopPropagation();
|
|
5389
5778
|
return(// Live-review fix (D): a `MenuItem` inside `MenuPositioner` renders through a
|
|
@@ -10367,6 +10756,18 @@ ContinuousDatePicker.displayName = "ContinuousDatePicker";
|
|
|
10367
10756
|
// value list) down to `is`/`is not`/`empty`/`not empty`. `"isanyof"` is appended to any
|
|
10368
10757
|
// of the four whenever the column supplies `filterValues` -- a faceted checkbox list,
|
|
10369
10758
|
// independent of the column's own kind.
|
|
10759
|
+
//
|
|
10760
|
+
// Multi-select fix (DATATABLE-ATTIO-SPEC.md gap #12, live-measured 2026-09-30): a
|
|
10761
|
+
// `kind: "select"` column whose CELL is multi-valued (`type: "tags"`/`"chip-list"` --
|
|
10762
|
+
// a chip-list/tag column, not a single status pill) offers Attio's own
|
|
10763
|
+
// `contains`/`does not contain` instead of `is`/`is not` -- "is" reads as an exact-
|
|
10764
|
+
// set-equality check against an array value, which is not what Attio's own multi-
|
|
10765
|
+
// select filter means or what `applyToolbarFilters`' `is`/`isnot` branch (a plain
|
|
10766
|
+
// string-equality compare via `asText`) computes. `contains`/`doesnotcontain` are
|
|
10767
|
+
// already correct here unmodified: `applyToolbarFilters` already runs them against
|
|
10768
|
+
// `asText(raw)` (an array's own values joined), which is exactly "does any tag
|
|
10769
|
+
// match". Single-select (no `filterValues`-array cell type) keeps `is`/`is not` --
|
|
10770
|
+
// not re-measured against Attio, so left alone per the discovery doc's own hedge.
|
|
10370
10771
|
////////////////////////////////////////////////////////////////////////////////////
|
|
10371
10772
|
const OP_ORDER = [
|
|
10372
10773
|
"contains",
|
|
@@ -10400,9 +10801,19 @@ const SELECT_OP_ORDER = [
|
|
|
10400
10801
|
"empty",
|
|
10401
10802
|
"notempty"
|
|
10402
10803
|
];
|
|
10804
|
+
const MULTI_SELECT_OP_ORDER = [
|
|
10805
|
+
"contains",
|
|
10806
|
+
"doesnotcontain",
|
|
10807
|
+
"empty",
|
|
10808
|
+
"notempty"
|
|
10809
|
+
];
|
|
10403
10810
|
/** The operators a column of this `kind` offers, in display order -- `hasValues`
|
|
10404
|
-
* (the column supplies `filterValues`) appends `"isanyof"` regardless of kind.
|
|
10405
|
-
|
|
10811
|
+
* (the column supplies `filterValues`) appends `"isanyof"` regardless of kind.
|
|
10812
|
+
* `multiValue` (the column's CELL type is a chip-list/tags column, not just its
|
|
10813
|
+
* filter `kind`) swaps `SELECT_OP_ORDER`'s `is`/`is not` for `contains`/`does not
|
|
10814
|
+
* contain`, matching Attio's own multi-select filter -- see the operator-sets
|
|
10815
|
+
* comment above. */ function opsForKind(kind, hasValues, multiValue = false) {
|
|
10816
|
+
const base = kind === "number" ? NUMBER_OP_ORDER : kind === "date" ? DATE_OP_ORDER : kind === "select" ? multiValue ? MULTI_SELECT_OP_ORDER : SELECT_OP_ORDER : OP_ORDER;
|
|
10406
10817
|
return hasValues ? [
|
|
10407
10818
|
...base,
|
|
10408
10819
|
"isanyof"
|
|
@@ -10770,66 +11181,58 @@ const formatFilterDate = (iso)=>{
|
|
|
10770
11181
|
return `${OPERATOR_LABELS[filter.op]} ${value}`;
|
|
10771
11182
|
}
|
|
10772
11183
|
/**
|
|
10773
|
-
* One filter condition, INSIDE the Filter popover --
|
|
10774
|
-
* (
|
|
10775
|
-
*
|
|
10776
|
-
*
|
|
10777
|
-
*
|
|
10778
|
-
*
|
|
10779
|
-
*
|
|
10780
|
-
*
|
|
10781
|
-
*
|
|
10782
|
-
|
|
11184
|
+
* One filter condition, INSIDE the Filter popover -- Attio's own compact chip shape
|
|
11185
|
+
* (DATATABLE-ATTIO-SPEC.md gap #12, re-measured live 2026-09-30): `[attribute ▾]
|
|
11186
|
+
* [operator …] [value] [remove ×]`, ALL of it visible at once, never behind a click-
|
|
11187
|
+
* to-expand accordion. Supersedes this file's earlier 2026-09-28 note (which read
|
|
11188
|
+
* Attio as keeping conditions collapsed behind a bare "Filter · N" count and put them
|
|
11189
|
+
* in an accordion for that reason) -- the fresh read found the opposite: every active
|
|
11190
|
+
* condition shows attribute/operator/value simultaneously. This keeps the outer shell
|
|
11191
|
+
* (the "Filter"/"Filter · N" trigger opening ONE popover, `SortControl`'s own
|
|
11192
|
+
* list-of-rows shape) rather than moving chips into the toolbar row itself, so the
|
|
11193
|
+
* already-covered trigger/`Add filter` DOM (`DataTable.test.tsx`) stays put; only the
|
|
11194
|
+
* PER-CONDITION row tightens, from an accordion to an always-open card. The operator
|
|
11195
|
+
* set stays a row of small pill buttons (not a dropdown) so every existing test that
|
|
11196
|
+
* clicks an operator by its exact button name keeps working unchanged.
|
|
11197
|
+
*/ function FilterConditionRow({ filter, columns }) {
|
|
10783
11198
|
const { patchToolbarFilter, changeToolbarFilterField, removeToolbarFilter } = useDataTableContext();
|
|
10784
11199
|
const column = columns.find((c)=>c.key === filter.key);
|
|
10785
11200
|
const kind = column?.kind;
|
|
11201
|
+
const multiValue = column?.type === "tags" || column?.type === "chip-list";
|
|
10786
11202
|
const values = column?.filterValues ?? [];
|
|
10787
|
-
const ops = opsForKind(kind, values.length > 0);
|
|
10788
|
-
return /*#__PURE__*/ jsxs("
|
|
11203
|
+
const ops = opsForKind(kind, values.length > 0, multiValue);
|
|
11204
|
+
return /*#__PURE__*/ jsxs("fieldset", {
|
|
11205
|
+
"aria-label": `${column ? columnLabel(column) : filter.key} filter: ${conditionSummary(filter, column)}`,
|
|
10789
11206
|
style: {
|
|
11207
|
+
margin: 0,
|
|
10790
11208
|
display: "flex",
|
|
10791
11209
|
flexDirection: "column",
|
|
10792
|
-
gap:
|
|
11210
|
+
gap: 8,
|
|
10793
11211
|
borderRadius: 6,
|
|
10794
|
-
|
|
10795
|
-
|
|
11212
|
+
border: "1px solid var(--seed-color-border-neutral)",
|
|
11213
|
+
background: "var(--seed-color-bg-neutral-weak)",
|
|
11214
|
+
padding: 6
|
|
10796
11215
|
},
|
|
10797
11216
|
children: [
|
|
10798
11217
|
/*#__PURE__*/ jsxs("div", {
|
|
10799
11218
|
style: {
|
|
10800
11219
|
display: "flex",
|
|
10801
11220
|
alignItems: "center",
|
|
10802
|
-
gap:
|
|
11221
|
+
gap: 4
|
|
10803
11222
|
},
|
|
10804
11223
|
children: [
|
|
10805
|
-
/*#__PURE__*/
|
|
10806
|
-
|
|
10807
|
-
|
|
11224
|
+
/*#__PURE__*/ jsx(InlineSelect, {
|
|
11225
|
+
ariaLabel: "Filter by field",
|
|
11226
|
+
value: filter.key,
|
|
11227
|
+
onChange: (key)=>changeToolbarFilterField(filter.id, key),
|
|
11228
|
+
options: columns.map((c)=>({
|
|
11229
|
+
value: c.key,
|
|
11230
|
+
label: columnLabel(c)
|
|
11231
|
+
})),
|
|
10808
11232
|
style: {
|
|
10809
11233
|
flex: 1,
|
|
10810
|
-
minWidth: 0
|
|
10811
|
-
|
|
10812
|
-
padding: "6px 8px",
|
|
10813
|
-
border: "none",
|
|
10814
|
-
borderRadius: 6,
|
|
10815
|
-
background: "transparent",
|
|
10816
|
-
cursor: "pointer",
|
|
10817
|
-
fontSize: 13,
|
|
10818
|
-
overflow: "hidden",
|
|
10819
|
-
textOverflow: "ellipsis",
|
|
10820
|
-
whiteSpace: "nowrap"
|
|
10821
|
-
},
|
|
10822
|
-
children: [
|
|
10823
|
-
/*#__PURE__*/ jsx("strong", {
|
|
10824
|
-
style: {
|
|
10825
|
-
fontWeight: 500
|
|
10826
|
-
},
|
|
10827
|
-
children: column ? columnLabel(column) : filter.key
|
|
10828
|
-
}),
|
|
10829
|
-
":",
|
|
10830
|
-
" ",
|
|
10831
|
-
conditionSummary(filter, column)
|
|
10832
|
-
]
|
|
11234
|
+
minWidth: 0
|
|
11235
|
+
}
|
|
10833
11236
|
}),
|
|
10834
11237
|
/*#__PURE__*/ jsx("button", {
|
|
10835
11238
|
type: "button",
|
|
@@ -10843,136 +11246,103 @@ const formatFilterDate = (iso)=>{
|
|
|
10843
11246
|
})
|
|
10844
11247
|
]
|
|
10845
11248
|
}),
|
|
10846
|
-
|
|
11249
|
+
/*#__PURE__*/ jsx("div", {
|
|
10847
11250
|
style: {
|
|
10848
|
-
padding: "0 8px 4px",
|
|
10849
11251
|
display: "flex",
|
|
10850
|
-
|
|
10851
|
-
gap:
|
|
11252
|
+
flexWrap: "wrap",
|
|
11253
|
+
gap: 2
|
|
10852
11254
|
},
|
|
10853
|
-
children:
|
|
10854
|
-
|
|
10855
|
-
|
|
10856
|
-
|
|
10857
|
-
|
|
10858
|
-
|
|
10859
|
-
children: "Field"
|
|
10860
|
-
}),
|
|
10861
|
-
/*#__PURE__*/ jsx(InlineSelect, {
|
|
10862
|
-
ariaLabel: "Filter by field",
|
|
10863
|
-
value: filter.key,
|
|
10864
|
-
onChange: (key)=>changeToolbarFilterField(filter.id, key),
|
|
10865
|
-
options: columns.map((c)=>({
|
|
10866
|
-
value: c.key,
|
|
10867
|
-
label: columnLabel(c)
|
|
10868
|
-
}))
|
|
10869
|
-
}),
|
|
10870
|
-
/*#__PURE__*/ jsx("div", {
|
|
10871
|
-
style: {
|
|
10872
|
-
fontSize: 12,
|
|
10873
|
-
color: "var(--seed-color-fg-neutral-subtle)"
|
|
10874
|
-
},
|
|
10875
|
-
children: "Condition"
|
|
10876
|
-
}),
|
|
10877
|
-
/*#__PURE__*/ jsx("div", {
|
|
11255
|
+
children: ops.map((op)=>/*#__PURE__*/ jsx("button", {
|
|
11256
|
+
type: "button",
|
|
11257
|
+
"data-active": filter.op === op ? "" : undefined,
|
|
11258
|
+
onClick: ()=>patchToolbarFilter(filter.id, {
|
|
11259
|
+
op
|
|
11260
|
+
}),
|
|
10878
11261
|
style: {
|
|
10879
|
-
|
|
10880
|
-
|
|
10881
|
-
|
|
11262
|
+
textAlign: "left",
|
|
11263
|
+
padding: "4px 8px",
|
|
11264
|
+
borderRadius: 999,
|
|
11265
|
+
border: "none",
|
|
11266
|
+
cursor: "pointer",
|
|
11267
|
+
background: filter.op === op ? "var(--seed-color-bg-layer-default)" : "transparent",
|
|
11268
|
+
fontSize: 12
|
|
10882
11269
|
},
|
|
10883
|
-
children:
|
|
10884
|
-
|
|
10885
|
-
|
|
10886
|
-
|
|
10887
|
-
|
|
10888
|
-
|
|
10889
|
-
|
|
10890
|
-
|
|
10891
|
-
|
|
10892
|
-
|
|
10893
|
-
|
|
10894
|
-
|
|
10895
|
-
|
|
10896
|
-
|
|
10897
|
-
|
|
10898
|
-
|
|
10899
|
-
|
|
10900
|
-
|
|
10901
|
-
|
|
11270
|
+
children: OPERATOR_LABELS[op]
|
|
11271
|
+
}, op))
|
|
11272
|
+
}),
|
|
11273
|
+
opNeedsValues(filter.op) && /*#__PURE__*/ jsx("div", {
|
|
11274
|
+
style: {
|
|
11275
|
+
display: "flex",
|
|
11276
|
+
flexDirection: "column",
|
|
11277
|
+
gap: 4,
|
|
11278
|
+
maxHeight: 160,
|
|
11279
|
+
overflow: "auto"
|
|
11280
|
+
},
|
|
11281
|
+
children: values.map((value)=>{
|
|
11282
|
+
const checked = (filter.values ?? []).includes(value);
|
|
11283
|
+
return /*#__PURE__*/ jsxs(CheckboxRoot, {
|
|
11284
|
+
checked: checked,
|
|
11285
|
+
onCheckedChange: ()=>patchToolbarFilter(filter.id, {
|
|
11286
|
+
values: checked ? (filter.values ?? []).filter((v)=>v !== value) : [
|
|
11287
|
+
...filter.values ?? [],
|
|
11288
|
+
value
|
|
11289
|
+
]
|
|
11290
|
+
}),
|
|
10902
11291
|
style: {
|
|
10903
11292
|
display: "flex",
|
|
10904
|
-
|
|
10905
|
-
gap:
|
|
10906
|
-
|
|
10907
|
-
|
|
11293
|
+
alignItems: "center",
|
|
11294
|
+
gap: 8,
|
|
11295
|
+
padding: "4px 0",
|
|
11296
|
+
cursor: "pointer"
|
|
10908
11297
|
},
|
|
10909
|
-
children:
|
|
10910
|
-
|
|
10911
|
-
|
|
10912
|
-
|
|
10913
|
-
|
|
10914
|
-
values: checked ? (filter.values ?? []).filter((v)=>v !== value) : [
|
|
10915
|
-
...filter.values ?? [],
|
|
10916
|
-
value
|
|
10917
|
-
]
|
|
10918
|
-
}),
|
|
10919
|
-
style: {
|
|
10920
|
-
display: "flex",
|
|
10921
|
-
alignItems: "center",
|
|
10922
|
-
gap: 8,
|
|
10923
|
-
padding: "4px 0",
|
|
10924
|
-
cursor: "pointer"
|
|
10925
|
-
},
|
|
10926
|
-
children: [
|
|
10927
|
-
/*#__PURE__*/ jsx(CheckboxControl, {
|
|
10928
|
-
children: /*#__PURE__*/ jsx(CheckboxIndicator, {
|
|
10929
|
-
checked: /*#__PURE__*/ jsx(IconCheckmarkLine_default, {
|
|
10930
|
-
size: 12
|
|
10931
|
-
})
|
|
10932
|
-
})
|
|
10933
|
-
}),
|
|
10934
|
-
/*#__PURE__*/ jsx(CheckboxLabel, {
|
|
10935
|
-
children: value
|
|
10936
|
-
}),
|
|
10937
|
-
/*#__PURE__*/ jsx(CheckboxHiddenInput, {
|
|
10938
|
-
"aria-label": value
|
|
11298
|
+
children: [
|
|
11299
|
+
/*#__PURE__*/ jsx(CheckboxControl, {
|
|
11300
|
+
children: /*#__PURE__*/ jsx(CheckboxIndicator, {
|
|
11301
|
+
checked: /*#__PURE__*/ jsx(IconCheckmarkLine_default, {
|
|
11302
|
+
size: 12
|
|
10939
11303
|
})
|
|
10940
|
-
|
|
10941
|
-
}, value);
|
|
10942
|
-
})
|
|
10943
|
-
}),
|
|
10944
|
-
opNeedsValue(filter.op) && kind === "date" && /*#__PURE__*/ jsx(DatePicker, {
|
|
10945
|
-
value: filter.value ? toDatePickerDate(isoToDate(filter.value) ?? new Date()) : undefined,
|
|
10946
|
-
onValueChange: (d)=>d && patchToolbarFilter(filter.id, {
|
|
10947
|
-
value: dateToISO(fromDatePickerDate(d))
|
|
10948
|
-
})
|
|
10949
|
-
}),
|
|
10950
|
-
opNeedsValue(filter.op) && kind === "select" && /*#__PURE__*/ jsx(InlineSelect, {
|
|
10951
|
-
ariaLabel: "Filter value",
|
|
10952
|
-
placeholder: "Value…",
|
|
10953
|
-
value: filter.value || undefined,
|
|
10954
|
-
onChange: (value)=>patchToolbarFilter(filter.id, {
|
|
10955
|
-
value
|
|
11304
|
+
})
|
|
10956
11305
|
}),
|
|
10957
|
-
|
|
10958
|
-
value
|
|
10959
|
-
|
|
10960
|
-
|
|
10961
|
-
|
|
10962
|
-
|
|
10963
|
-
|
|
10964
|
-
|
|
10965
|
-
|
|
10966
|
-
|
|
10967
|
-
|
|
10968
|
-
|
|
10969
|
-
|
|
10970
|
-
|
|
10971
|
-
placeholder: "Value…",
|
|
10972
|
-
"aria-label": "Filter value"
|
|
10973
|
-
})
|
|
11306
|
+
/*#__PURE__*/ jsx(CheckboxLabel, {
|
|
11307
|
+
children: value
|
|
11308
|
+
}),
|
|
11309
|
+
/*#__PURE__*/ jsx(CheckboxHiddenInput, {
|
|
11310
|
+
"aria-label": value
|
|
11311
|
+
})
|
|
11312
|
+
]
|
|
11313
|
+
}, value);
|
|
11314
|
+
})
|
|
11315
|
+
}),
|
|
11316
|
+
opNeedsValue(filter.op) && kind === "date" && /*#__PURE__*/ jsx(DatePicker, {
|
|
11317
|
+
value: filter.value ? toDatePickerDate(isoToDate(filter.value) ?? new Date()) : undefined,
|
|
11318
|
+
onValueChange: (d)=>d && patchToolbarFilter(filter.id, {
|
|
11319
|
+
value: dateToISO(fromDatePickerDate(d))
|
|
10974
11320
|
})
|
|
10975
|
-
|
|
11321
|
+
}),
|
|
11322
|
+
opNeedsValue(filter.op) && kind === "select" && /*#__PURE__*/ jsx(InlineSelect, {
|
|
11323
|
+
ariaLabel: "Filter value",
|
|
11324
|
+
placeholder: "Value…",
|
|
11325
|
+
value: filter.value || undefined,
|
|
11326
|
+
onChange: (value)=>patchToolbarFilter(filter.id, {
|
|
11327
|
+
value
|
|
11328
|
+
}),
|
|
11329
|
+
options: values.map((value)=>({
|
|
11330
|
+
value,
|
|
11331
|
+
label: value
|
|
11332
|
+
}))
|
|
11333
|
+
}),
|
|
11334
|
+
opNeedsValue(filter.op) && kind !== "date" && kind !== "select" && /*#__PURE__*/ jsx(TextFieldRoot, {
|
|
11335
|
+
variant: "outline",
|
|
11336
|
+
size: "medium",
|
|
11337
|
+
children: /*#__PURE__*/ jsx(TextFieldInput, {
|
|
11338
|
+
type: kind === "number" ? "number" : "text",
|
|
11339
|
+
value: filter.value,
|
|
11340
|
+
onChange: (e)=>patchToolbarFilter(filter.id, {
|
|
11341
|
+
value: e.target.value
|
|
11342
|
+
}),
|
|
11343
|
+
placeholder: "Value…",
|
|
11344
|
+
"aria-label": "Filter value"
|
|
11345
|
+
})
|
|
10976
11346
|
})
|
|
10977
11347
|
]
|
|
10978
11348
|
});
|
|
@@ -10980,15 +11350,14 @@ const formatFilterDate = (iso)=>{
|
|
|
10980
11350
|
/**
|
|
10981
11351
|
* The toolbar's Filter trigger + popover -- a compact `"Filter"` / `"Filter · N"`
|
|
10982
11352
|
* button (matching `SortControl`'s own `"Sort"` / `"Sort · N"`), opening ONE popover
|
|
10983
|
-
* that holds both the active conditions (compact
|
|
10984
|
-
*
|
|
10985
|
-
* new one
|
|
10986
|
-
*
|
|
11353
|
+
* that holds both the active conditions (compact chips, `FilterConditionRow` above,
|
|
11354
|
+
* ALL open at once -- no accordion) and, behind "+ Add filter", the field picker that
|
|
11355
|
+
* appends a new one. See `FilterConditionRow`'s own doc comment for why the chips
|
|
11356
|
+
* live inside this one popover rather than spread across the toolbar row itself.
|
|
10987
11357
|
*/ function FilterControl({ columns, label = "Filter" }) {
|
|
10988
11358
|
const { toolbarFilters, addToolbarFilter } = useDataTableContext();
|
|
10989
11359
|
const [open, setOpen] = React.useState(false);
|
|
10990
11360
|
const [pickingField, setPickingField] = React.useState(false);
|
|
10991
|
-
const [expandedId, setExpandedId] = React.useState(null);
|
|
10992
11361
|
return /*#__PURE__*/ jsxs(MenuRoot, {
|
|
10993
11362
|
open: open,
|
|
10994
11363
|
onOpenChange: (next)=>{
|
|
@@ -11050,8 +11419,7 @@ const formatFilterDate = (iso)=>{
|
|
|
11050
11419
|
columns.map((c)=>/*#__PURE__*/ jsx("button", {
|
|
11051
11420
|
type: "button",
|
|
11052
11421
|
onClick: ()=>{
|
|
11053
|
-
|
|
11054
|
-
setExpandedId(id);
|
|
11422
|
+
addToolbarFilter(c.key);
|
|
11055
11423
|
setPickingField(false);
|
|
11056
11424
|
},
|
|
11057
11425
|
style: {
|
|
@@ -11084,9 +11452,7 @@ const formatFilterDate = (iso)=>{
|
|
|
11084
11452
|
}),
|
|
11085
11453
|
toolbarFilters.map((f)=>/*#__PURE__*/ jsx(FilterConditionRow, {
|
|
11086
11454
|
filter: f,
|
|
11087
|
-
columns: columns
|
|
11088
|
-
expanded: expandedId === f.id,
|
|
11089
|
-
onToggle: ()=>setExpandedId((id)=>id === f.id ? null : f.id)
|
|
11455
|
+
columns: columns
|
|
11090
11456
|
}, f.id)),
|
|
11091
11457
|
/*#__PURE__*/ jsxs("button", {
|
|
11092
11458
|
type: "button",
|
|
@@ -11115,8 +11481,8 @@ const formatFilterDate = (iso)=>{
|
|
|
11115
11481
|
});
|
|
11116
11482
|
}
|
|
11117
11483
|
|
|
11118
|
-
const DataTableToolbar = withContext$
|
|
11119
|
-
const DataTableCount = withContext$
|
|
11484
|
+
const DataTableToolbar = withContext$1(Primitive.div, "toolbar");
|
|
11485
|
+
const DataTableCount = withContext$1(Primitive.span, "count");
|
|
11120
11486
|
/**
|
|
11121
11487
|
* The toolbar's own right-hand group -- `count`'s `marginInlineStart: auto` moved
|
|
11122
11488
|
* here (off the `<span>` itself) so a consumer's `toolbarActions` and the row count
|
|
@@ -11127,7 +11493,7 @@ const DataTableCount = withContext$2(Primitive.span, "count");
|
|
|
11127
11493
|
* it. `DataTable.tsx` always renders this wrapper once either `toolbarActions` or
|
|
11128
11494
|
* `showCount` is on, so it degrades to exactly the old single-count layout when no
|
|
11129
11495
|
* `toolbarActions` is passed.
|
|
11130
|
-
*/ const DataTableToolbarEnd = withContext$
|
|
11496
|
+
*/ const DataTableToolbarEnd = withContext$1(Primitive.div, "toolbarEnd");
|
|
11131
11497
|
/**
|
|
11132
11498
|
* A compact, borderless search box -- crisp's own measured `.crisp-datatable-search`
|
|
11133
11499
|
* (32px, `bgSurface` background, a leading icon, a soft rest-state shadow, a 1px brand
|
|
@@ -11177,7 +11543,7 @@ function RowCount({ shown, total, countLabel }) {
|
|
|
11177
11543
|
}
|
|
11178
11544
|
|
|
11179
11545
|
function DataTableInner(props, ref) {
|
|
11180
|
-
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;
|
|
11546
|
+
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;
|
|
11181
11547
|
const classNames = dataTable({
|
|
11182
11548
|
surface,
|
|
11183
11549
|
interactive
|
|
@@ -11301,7 +11667,7 @@ function DataTableInner(props, ref) {
|
|
|
11301
11667
|
// loading/error/empty body, infinite scroll's sentinel, the add-row footer) reads
|
|
11302
11668
|
// this ONE value rather than each re-deriving it, so they can never drift apart.
|
|
11303
11669
|
const totalCols = columns.length + (selectable ? 1 : 0);
|
|
11304
|
-
return /*#__PURE__*/ jsx(ClassNamesProvider
|
|
11670
|
+
return /*#__PURE__*/ jsx(ClassNamesProvider, {
|
|
11305
11671
|
value: classNames,
|
|
11306
11672
|
children: /*#__PURE__*/ jsxs(DataTableRoot, {
|
|
11307
11673
|
ref: ref,
|
|
@@ -11457,7 +11823,10 @@ function DataTableInner(props, ref) {
|
|
|
11457
11823
|
locale: locale,
|
|
11458
11824
|
selectable: !!selectable,
|
|
11459
11825
|
stickyFirst: stickyFirst,
|
|
11460
|
-
stickyLast: resolvedStickyLast
|
|
11826
|
+
stickyLast: resolvedStickyLast,
|
|
11827
|
+
cellContextMenu: cellContextMenu,
|
|
11828
|
+
onViewEditHistory: onViewEditHistory,
|
|
11829
|
+
cellContextMenuItems: cellContextMenuItems
|
|
11461
11830
|
})
|
|
11462
11831
|
]
|
|
11463
11832
|
}, row.id)),
|
|
@@ -11496,6 +11865,10 @@ function DataTableInner(props, ref) {
|
|
|
11496
11865
|
})
|
|
11497
11866
|
]
|
|
11498
11867
|
})
|
|
11868
|
+
}),
|
|
11869
|
+
selectable && /*#__PURE__*/ jsx(DataTableBulkBar, {
|
|
11870
|
+
clearLabel: clearSelectionLabel,
|
|
11871
|
+
children: bulkActions
|
|
11499
11872
|
})
|
|
11500
11873
|
]
|
|
11501
11874
|
})
|
|
@@ -11549,7 +11922,7 @@ DataTableInner.displayName = "DataTable";
|
|
|
11549
11922
|
// is a SIBLING of the toolbar (both inside `DataTablePrimitiveRoot`'s card `<div>`)
|
|
11550
11923
|
// rather than `DataTablePrimitiveRoot` itself: a `<table>` cannot host the toolbar as a
|
|
11551
11924
|
// child without the HTML parser foster-parenting it back out.
|
|
11552
|
-
const DataTableTable = withContext$
|
|
11925
|
+
const DataTableTable = withContext$1(DataTableTable$1, "table");
|
|
11553
11926
|
/** One `<col>` per rendered column (plus the 40px selection column, if any) -- the
|
|
11554
11927
|
* SINGLE place a column's width is ever set now. Every row's header/body/footer cell
|
|
11555
11928
|
* reads its own width off this shared `<colgroup>` by construction (native table
|
|
@@ -11586,15 +11959,15 @@ const DataTableTable = withContext$2(DataTableTable$1, "table");
|
|
|
11586
11959
|
]
|
|
11587
11960
|
});
|
|
11588
11961
|
}
|
|
11589
|
-
const DataTableHeader = withContext$
|
|
11590
|
-
const DataTableHeaderCell = withContext$
|
|
11962
|
+
const DataTableHeader = withContext$1(DataTableHeader$1, "header");
|
|
11963
|
+
const DataTableHeaderCell = withContext$1(DataTableHeaderCell$1, "headerCell");
|
|
11591
11964
|
// `.crisp-datatable-th-inner` -- the flex row INSIDE a `<th>` (label + filter trigger +
|
|
11592
11965
|
// sort chevron + column menu). See qvism-preset's own `headerCellInner` slot comment
|
|
11593
11966
|
// for why this lives one level below the cell itself rather than on it.
|
|
11594
|
-
const DataTableHeaderCellInner = withContext$
|
|
11967
|
+
const DataTableHeaderCellInner = withContext$1(Primitive.div, "headerCellInner");
|
|
11595
11968
|
/** The label text specifically -- the one thing in `DataTableHeaderCellInner` that
|
|
11596
11969
|
* truncates; the trailing filter/sort/menu icons stay fully visible regardless of how
|
|
11597
|
-
* long a column's own header text is. */ const DataTableHeaderCellLabel = withContext$
|
|
11970
|
+
* long a column's own header text is. */ const DataTableHeaderCellLabel = withContext$1(Primitive.span, "headerCellLabel");
|
|
11598
11971
|
/**
|
|
11599
11972
|
* A selection `<th>`/`<td>`/footer `<td>` composes ITS base slot (`headerCell`/`cell`/
|
|
11600
11973
|
* `footerCell`) together with the `selectionCell` MODIFIER, rather than replacing it --
|
|
@@ -11605,7 +11978,7 @@ const DataTableHeaderCellInner = withContext$2(Primitive.div, "headerCellInner")
|
|
|
11605
11978
|
* a second recipe call.
|
|
11606
11979
|
*/ function withSelectionCell(Component, baseSlot) {
|
|
11607
11980
|
const StyledComponent = /*#__PURE__*/ forwardRef((props, ref)=>{
|
|
11608
|
-
const classNames = useClassNames
|
|
11981
|
+
const classNames = useClassNames();
|
|
11609
11982
|
return /*#__PURE__*/ jsx(Component, {
|
|
11610
11983
|
ref: ref,
|
|
11611
11984
|
...props,
|
|
@@ -11616,12 +11989,12 @@ const DataTableHeaderCellInner = withContext$2(Primitive.div, "headerCellInner")
|
|
|
11616
11989
|
return StyledComponent;
|
|
11617
11990
|
}
|
|
11618
11991
|
const DataTableSelectionHeaderCell = withSelectionCell(DataTableHeaderCell$1, "headerCell");
|
|
11619
|
-
const DataTableHeaderCellIcon = withContext$
|
|
11992
|
+
const DataTableHeaderCellIcon = withContext$1(Primitive.span, "headerCellIcon");
|
|
11620
11993
|
// body has no CSS slot of its own in the rootage spec -- it is a plain structural
|
|
11621
11994
|
// `<tbody>`, so it is not wrapped in `withContext`.
|
|
11622
11995
|
const DataTableBody = DataTableBody$1;
|
|
11623
|
-
const DataTableRow = withContext$
|
|
11624
|
-
const DataTableCell = withContext$
|
|
11996
|
+
const DataTableRow = withContext$1(DataTableRow$1, "row");
|
|
11997
|
+
const DataTableCell = withContext$1(DataTableCell$1, "cell");
|
|
11625
11998
|
const DataTableSelectionCell = withSelectionCell(DataTableCell$1, "cell");
|
|
11626
11999
|
// Tranche 4 -- column menu + reorder + resize + sticky columns. A scroll boundary
|
|
11627
12000
|
// wrapping the whole `<table>`: `overflowX: auto` is what makes a wide table (more
|
|
@@ -11631,7 +12004,7 @@ const DataTableSelectionCell = withSelectionCell(DataTableCell$1, "cell");
|
|
|
11631
12004
|
// stays out of this slot's own base css (nothing here has a bounded height of its own
|
|
11632
12005
|
// to clip), so it never introduces a second vertical scrollbar next to whatever
|
|
11633
12006
|
// ambient container the whole DataTable already sits in.
|
|
11634
|
-
const DataTableScrollBodyBase = withContext$
|
|
12007
|
+
const DataTableScrollBodyBase = withContext$1(Primitive.div, "scrollArea");
|
|
11635
12008
|
function DataTableScrollBody({ children, scrollProps }) {
|
|
11636
12009
|
const { scrollAreaRef, scrolledStart, scrolledEnd, onScroll } = useDataTableContext();
|
|
11637
12010
|
const { className: scrollClassName, style: scrollStyle, ...otherScrollProps } = scrollProps ?? {};
|
|
@@ -11646,7 +12019,7 @@ function DataTableScrollBody({ children, scrollProps }) {
|
|
|
11646
12019
|
children: children
|
|
11647
12020
|
});
|
|
11648
12021
|
}
|
|
11649
|
-
const DataTableFooterBase = withContext$
|
|
12022
|
+
const DataTableFooterBase = withContext$1(Primitive.tfoot, "footer");
|
|
11650
12023
|
function DataTableFooter({ children, ...props }) {
|
|
11651
12024
|
return /*#__PURE__*/ jsx(DataTableFooterBase, {
|
|
11652
12025
|
...props,
|
|
@@ -11655,7 +12028,7 @@ function DataTableFooter({ children, ...props }) {
|
|
|
11655
12028
|
})
|
|
11656
12029
|
});
|
|
11657
12030
|
}
|
|
11658
|
-
const DataTableFooterCell = withContext$
|
|
12031
|
+
const DataTableFooterCell = withContext$1(Primitive.td, "footerCell");
|
|
11659
12032
|
// The selection column's OWN sticky wrappers -- one each for header/body/footer,
|
|
11660
12033
|
// reading the live pinned set via `useSelectionStickyFirst` (column-layout.ts) instead
|
|
11661
12034
|
// of the `stickyFirst` PROP alone, so the checkbox column sticks the moment ANY column
|
|
@@ -15592,4 +15965,4 @@ function RecordPreviewPanel({ open: _open, record, onClose, className, animation
|
|
|
15592
15965
|
}
|
|
15593
15966
|
RecordPreviewPanel.displayName = "RecordPreviewPanel";
|
|
15594
15967
|
|
|
15595
|
-
export { DataTable, DataTableViewsControl, RecordPreview, ResizablePanel };
|
|
15968
|
+
export { DataTable, DataTableBulkBar, DataTableViewsControl, RecordPreview, ResizablePanel };
|