@crispy-seed/data-table 0.10.1 → 0.10.3
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 +31 -2
- package/dist/data-table-recipe.layered.css +35 -3
- package/dist/index.cjs +400 -74
- package/dist/index.d.ts +42 -42
- package/dist/index.js +400 -74
- package/dist/styles.css +35 -3
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -16,6 +16,7 @@ var checkmark = require('@seed-design/css/recipes/checkmark');
|
|
|
16
16
|
var reactCheckbox = require('@seed-design/react-checkbox');
|
|
17
17
|
var reactScaleFeedback = require('@seed-design/react-scale-feedback');
|
|
18
18
|
var checkboxGroup = require('@seed-design/css/recipes/checkbox-group');
|
|
19
|
+
var reactDom = require('react-dom');
|
|
19
20
|
var reactImage = require('@seed-design/react-image');
|
|
20
21
|
var avatar = require('@seed-design/css/recipes/avatar');
|
|
21
22
|
require('@seed-design/css/recipes/avatar-stack');
|
|
@@ -2578,6 +2579,50 @@ const Badge = /*#__PURE__*/ React.forwardRef(({ className, children, ...props },
|
|
|
2578
2579
|
});
|
|
2579
2580
|
Badge.displayName = "Badge";
|
|
2580
2581
|
|
|
2582
|
+
// Feature B rework (#8, chip-list display) -- the pure "how many chips fit" decision,
|
|
2583
|
+
// isolated from the DOM measuring that drives it (`cells.tsx`'s `ChipListCell`) the same
|
|
2584
|
+
// way `truncate.ts`/`filtering.ts` already split a pure calculation out of the styled
|
|
2585
|
+
// layer: this file touches no DOM, so the actual fitting algorithm is unit-testable with
|
|
2586
|
+
// plain numbers instead of a real laid-out browser.
|
|
2587
|
+
//
|
|
2588
|
+
// Attio's own chip-list cell (e.g. Categories: "Internet", "Broadcasting", "+7") shows as
|
|
2589
|
+
// many WHOLE chips as fit left-to-right, then one "+N" badge for the rest -- never a
|
|
2590
|
+
// chip clipped mid-render, never a second wrapped line. `ChipListCell` measures every
|
|
2591
|
+
// chip's real rendered width (font/padding-dependent, so it can't be estimated from
|
|
2592
|
+
// character count) plus the container's current width, then asks this function how many
|
|
2593
|
+
// of those chips it can show before the running total (chips + gaps + the badge itself)
|
|
2594
|
+
// would exceed the container.
|
|
2595
|
+
/**
|
|
2596
|
+
* `overflowBadgeWidth` is the WORST-CASE badge width the caller measured (e.g. the
|
|
2597
|
+
* width of a badge showing every tag: `+<chipWidths.length>`) rather than the width of
|
|
2598
|
+
* the badge for whatever count this call actually returns -- the real badge (fewer
|
|
2599
|
+
* digits once some chips DO fit) is never wider than that, so reserving the worst case
|
|
2600
|
+
* up front avoids a second measuring pass and always yields a result that fits. It may
|
|
2601
|
+
* slightly under-fill the row by the odd chip in a boundary case (e.g. "+9" vs "+10" --
|
|
2602
|
+
* one fewer digit than reserved), which is the deliberate trade against re-measuring per
|
|
2603
|
+
* candidate count.
|
|
2604
|
+
*/ function computeVisibleChipCount(chipWidths, containerWidth, gap, overflowBadgeWidth) {
|
|
2605
|
+
// An unmeasured/zero-width container (not yet laid out, e.g. `display: none` or a
|
|
2606
|
+
// pre-hydration frame) has no meaningful "fits" answer -- show everything rather than
|
|
2607
|
+
// collapsing every chip behind "+N" for a frame it'll never actually paint like that.
|
|
2608
|
+
if (containerWidth <= 0) return chipWidths.length;
|
|
2609
|
+
let used = 0;
|
|
2610
|
+
let count = 0;
|
|
2611
|
+
for(let index = 0; index < chipWidths.length; index++){
|
|
2612
|
+
const withGap = count > 0 ? gap : 0;
|
|
2613
|
+
const usedIfShown = used + withGap + chipWidths[index];
|
|
2614
|
+
const remainingAfterThis = chipWidths.length - (index + 1);
|
|
2615
|
+
// Every chip except the very last one needs room left over for the overflow badge
|
|
2616
|
+
// TOO (if it's going to exist at all) -- otherwise this loop would happily show
|
|
2617
|
+
// every chip except the last, then have nowhere left to put "+1".
|
|
2618
|
+
const badgeReserve = remainingAfterThis > 0 ? gap + overflowBadgeWidth : 0;
|
|
2619
|
+
if (usedIfShown + badgeReserve > containerWidth) break;
|
|
2620
|
+
used = usedIfShown;
|
|
2621
|
+
count++;
|
|
2622
|
+
}
|
|
2623
|
+
return count;
|
|
2624
|
+
}
|
|
2625
|
+
|
|
2581
2626
|
// Attio cell-type renderers, built from seed's own components -- not exported from
|
|
2582
2627
|
// the package's public surface (kept alongside DataTable.tsx as an implementation
|
|
2583
2628
|
// detail, the way Select keeps `InternalIcon` under `components/private`). Each
|
|
@@ -2792,19 +2837,125 @@ function StatusCell({ value }) {
|
|
|
2792
2837
|
className: className
|
|
2793
2838
|
});
|
|
2794
2839
|
}
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2840
|
+
// Feature B rework (#8) -- the chip-list DISPLAY row's own gap, mirrored from the
|
|
2841
|
+
// qvism recipe's `chipList` slot (`packages/qvism-preset/src/recipes/data-table.ts`'s
|
|
2842
|
+
// `CHIP_GAP`, measured live off Attio at 3px between chips). Kept as a literal here
|
|
2843
|
+
// too, same footnote as `CHIP_SWATCHES` above: this file and the recipe are two
|
|
2844
|
+
// independent sources for numbers that have to agree, not one importing the other
|
|
2845
|
+
// (react/AGENTS.md -- style stays in the recipe; this is the one JS-side computation,
|
|
2846
|
+
// `computeVisibleChipCount`, that needs the SAME number to size against).
|
|
2847
|
+
const CHIP_LIST_GAP_PX = 3;
|
|
2848
|
+
/** `type: "chip-list"` display -- every editable/non-editable chip-list cell renders
|
|
2849
|
+
* through this component for its REST state (editable: when its click-to-edit
|
|
2850
|
+
* popover, editors.tsx's `ChipListEditor`, isn't open; read-only: always). Attio's
|
|
2851
|
+
* own model (its Categories cell: "Internet", "Broadcasting", "+7") -- show as many
|
|
2852
|
+
* WHOLE chips as fit the cell's current width, then one "+N" badge for the rest,
|
|
2853
|
+
* never a chip clipped mid-render, never a second wrapped line, never overflowing
|
|
2854
|
+
* into a neighboring (possibly sticky) column. `body-cells.tsx`'s `isAlwaysLiveEditable`
|
|
2855
|
+
* no longer lists `chip-list` -- the always-live inline editor this replaced is what
|
|
2856
|
+
* produced exactly that overflow (crispy-seed's #1094).
|
|
2857
|
+
*
|
|
2858
|
+
* The fitting decision itself (`computeVisibleChipCount`, `chip-overflow.ts`) is pure
|
|
2859
|
+
* and unit-tested on its own; this component's only job is feeding it real measured
|
|
2860
|
+
* widths. Two widths need measuring that can't be estimated from character count
|
|
2861
|
+
* (font/padding-dependent): every chip's own rendered width, and the "+N" badge's
|
|
2862
|
+
* worst-case width (reserved for the FULL tag count, so the real badge -- fewer
|
|
2863
|
+
* digits once some chips fit -- never overflows what was reserved for it; see
|
|
2864
|
+
* `computeVisibleChipCount`'s own doc comment). Both are measured off a permanently
|
|
2865
|
+
* mounted, invisible copy (`visibility: hidden`, absolutely positioned so it never
|
|
2866
|
+
* affects layout -- `position: relative` on the `chipList` slot's own recipe rule
|
|
2867
|
+
* makes THIS element, not the `<td>` two levels up, its containing block, and the
|
|
2868
|
+
* `chipList` slot's own `overflow: hidden` clips it there too) -- kept mounted at ALL
|
|
2869
|
+
* times, not just while a `ResizeObserver` fires, because widening the column back
|
|
2870
|
+
* out needs to know the width of chips currently hidden behind "+N" to decide
|
|
2871
|
+
* whether they now fit again.
|
|
2872
|
+
*/ function ChipListCell({ value, className, chipClassName, overflowClassName }) {
|
|
2800
2873
|
const tags = Array.isArray(value) ? value.filter((tag)=>!!tag) : [];
|
|
2874
|
+
const containerRef = React__namespace.useRef(null);
|
|
2875
|
+
const measureRef = React__namespace.useRef(null);
|
|
2876
|
+
const overflowMeasureRef = React__namespace.useRef(null);
|
|
2877
|
+
const [visibleCount, setVisibleCount] = React__namespace.useState(tags.length);
|
|
2878
|
+
// Every tag joined into one string -- a stable primitive dependency for the effect
|
|
2879
|
+
// below, so it only re-measures when the actual tag SET changes, not on every
|
|
2880
|
+
// unrelated re-render of the row (a new `tags` array identity from a fresh
|
|
2881
|
+
// `getColumnValue` call on every render would otherwise re-run this every time).
|
|
2882
|
+
const tagsKey = tags.join("\u0000");
|
|
2883
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies: `tagsKey` is a deliberate serialized proxy for `tags` (a fresh array identity every render would otherwise re-run this effect on every unrelated re-render) -- `tags` itself is intentionally excluded, and `tags.length` (referenced below, in the early-return branch) is already covered by `tagsKey` changing whenever the tag SET does, including a same-length rename/reorder that `tags.length` alone would miss.
|
|
2884
|
+
React__namespace.useLayoutEffect(()=>{
|
|
2885
|
+
const container = containerRef.current;
|
|
2886
|
+
const measure = measureRef.current;
|
|
2887
|
+
// No real layout to measure against (SSR, or a test environment with no layout
|
|
2888
|
+
// engine, e.g. this package's own `bun test` -- happy-dom lays out nothing, every
|
|
2889
|
+
// rect comes back zero) -- show every chip rather than a permanently-wrong "+N"
|
|
2890
|
+
// for a width this environment can never actually report.
|
|
2891
|
+
if (!container || !measure || typeof ResizeObserver === "undefined") {
|
|
2892
|
+
setVisibleCount(tags.length);
|
|
2893
|
+
return;
|
|
2894
|
+
}
|
|
2895
|
+
// Rebound to non-null consts (rather than a `!` assertion inside `recompute`
|
|
2896
|
+
// below) -- `container`/`measure` are already narrowed non-null here, and these
|
|
2897
|
+
// two bindings carry that narrowing into the closure without re-deriving it.
|
|
2898
|
+
const measuredContainer = container;
|
|
2899
|
+
const measuredList = measure;
|
|
2900
|
+
function recompute() {
|
|
2901
|
+
const chipWidths = Array.from(measuredList.children, (el)=>el.getBoundingClientRect().width);
|
|
2902
|
+
const overflowWidth = overflowMeasureRef.current?.getBoundingClientRect().width ?? 0;
|
|
2903
|
+
setVisibleCount(computeVisibleChipCount(chipWidths, measuredContainer.clientWidth, CHIP_LIST_GAP_PX, overflowWidth));
|
|
2904
|
+
}
|
|
2905
|
+
recompute();
|
|
2906
|
+
const observer = new ResizeObserver(recompute);
|
|
2907
|
+
observer.observe(container);
|
|
2908
|
+
return ()=>observer.disconnect();
|
|
2909
|
+
}, [
|
|
2910
|
+
tagsKey
|
|
2911
|
+
]);
|
|
2801
2912
|
if (!tags.length) return null;
|
|
2802
|
-
|
|
2913
|
+
const clampedVisible = Math.min(visibleCount, tags.length);
|
|
2914
|
+
const overflowCount = tags.length - clampedVisible;
|
|
2915
|
+
return /*#__PURE__*/ jsxRuntime.jsxs("span", {
|
|
2803
2916
|
className: className,
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2917
|
+
ref: containerRef,
|
|
2918
|
+
children: [
|
|
2919
|
+
tags.slice(0, clampedVisible).map((tag)=>/*#__PURE__*/ jsxRuntime.jsx(SwatchChip, {
|
|
2920
|
+
value: tag,
|
|
2921
|
+
className: chipClassName
|
|
2922
|
+
}, tag)),
|
|
2923
|
+
overflowCount > 0 && /*#__PURE__*/ jsxRuntime.jsxs("span", {
|
|
2924
|
+
className: overflowClassName,
|
|
2925
|
+
children: [
|
|
2926
|
+
"+",
|
|
2927
|
+
overflowCount
|
|
2928
|
+
]
|
|
2929
|
+
}),
|
|
2930
|
+
/*#__PURE__*/ jsxRuntime.jsx("span", {
|
|
2931
|
+
ref: measureRef,
|
|
2932
|
+
"aria-hidden": "true",
|
|
2933
|
+
className: className,
|
|
2934
|
+
style: {
|
|
2935
|
+
position: "absolute",
|
|
2936
|
+
visibility: "hidden",
|
|
2937
|
+
pointerEvents: "none"
|
|
2938
|
+
},
|
|
2939
|
+
children: tags.map((tag)=>/*#__PURE__*/ jsxRuntime.jsx(SwatchChip, {
|
|
2940
|
+
value: tag,
|
|
2941
|
+
className: chipClassName
|
|
2942
|
+
}, tag))
|
|
2943
|
+
}),
|
|
2944
|
+
/*#__PURE__*/ jsxRuntime.jsxs("span", {
|
|
2945
|
+
ref: overflowMeasureRef,
|
|
2946
|
+
"aria-hidden": "true",
|
|
2947
|
+
className: overflowClassName,
|
|
2948
|
+
style: {
|
|
2949
|
+
position: "absolute",
|
|
2950
|
+
visibility: "hidden",
|
|
2951
|
+
pointerEvents: "none"
|
|
2952
|
+
},
|
|
2953
|
+
children: [
|
|
2954
|
+
"+",
|
|
2955
|
+
tags.length
|
|
2956
|
+
]
|
|
2957
|
+
})
|
|
2958
|
+
]
|
|
2808
2959
|
});
|
|
2809
2960
|
}
|
|
2810
2961
|
/**
|
|
@@ -3376,7 +3527,7 @@ const TextFieldTextarea = /*#__PURE__*/ React.forwardRef(({ className, autoresiz
|
|
|
3376
3527
|
});
|
|
3377
3528
|
TextFieldTextarea.displayName = "TextFieldTextarea";
|
|
3378
3529
|
|
|
3379
|
-
__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: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-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:22px;overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__chipList{display:flex;align-items:center;flex-wrap:nowrap;gap:var(--seed-dimension-x1_5);overflow:hidden;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:var(--seed-radius-r5);box-shadow:inset 0 0 0 1px var(--seed-color-stroke-neutral-weak),var(--seed-shadow-s3);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))}");
|
|
3530
|
+
__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: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:var(--seed-radius-r5);box-shadow:inset 0 0 0 1px var(--seed-color-stroke-neutral-weak),var(--seed-shadow-s3);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))}");
|
|
3380
3531
|
|
|
3381
3532
|
const createClassName = (className, variants, compoundVariants = [])=>{
|
|
3382
3533
|
const variantKeys = Object.keys(variants);
|
|
@@ -3479,6 +3630,14 @@ const dataTableSlotNames = [
|
|
|
3479
3630
|
"chipList",
|
|
3480
3631
|
"seed-data-table__chipList"
|
|
3481
3632
|
],
|
|
3633
|
+
[
|
|
3634
|
+
"chipOverflow",
|
|
3635
|
+
"seed-data-table__chipOverflow"
|
|
3636
|
+
],
|
|
3637
|
+
[
|
|
3638
|
+
"chipListEditor",
|
|
3639
|
+
"seed-data-table__chipListEditor"
|
|
3640
|
+
],
|
|
3482
3641
|
[
|
|
3483
3642
|
"chipRemove",
|
|
3484
3643
|
"seed-data-table__chipRemove"
|
|
@@ -4082,20 +4241,31 @@ function RatingEditor({ column, value, pending, onCommit }) {
|
|
|
4082
4241
|
});
|
|
4083
4242
|
}
|
|
4084
4243
|
////////////////////////////////////////////////////////////////////////////////////
|
|
4085
|
-
// -- editable: "chip-list" (Feature B, #8) --
|
|
4086
|
-
//
|
|
4087
|
-
//
|
|
4088
|
-
//
|
|
4089
|
-
//
|
|
4090
|
-
//
|
|
4091
|
-
//
|
|
4092
|
-
//
|
|
4093
|
-
//
|
|
4094
|
-
//
|
|
4244
|
+
// -- editable: "chip-list" (Feature B rework, #8) -- click-to-edit, Attio's own model
|
|
4245
|
+
// rather than the always-live widget this replaced (crispy-seed#1094: every row
|
|
4246
|
+
// rendering a live add-tag editor at once overflowed the cell and collided with
|
|
4247
|
+
// sticky columns). `body-cells.tsx`'s `DataTableBodyCell` opens this INSIDE the
|
|
4248
|
+
// same anchored `CellExpandOverlay` Feature C already built for a gated editor's
|
|
4249
|
+
// expanded state, on a plain single click (no truncation gate, unlike every other
|
|
4250
|
+
// read-only cell's peek) -- clicking a short 2-tag cell must open the editor just
|
|
4251
|
+
// as readily as a 10-tag one. It stays open across MULTIPLE add/remove commits
|
|
4252
|
+
// (closes only on blur/outside-click/Escape, `DataTableBodyCell`'s own effect) by
|
|
4253
|
+
// NOT going through `useDataTableEdit`'s `isEditing` gate the way `SelectEditor`/
|
|
4254
|
+
// `TextEditor` do -- `commitEdit` closes `editingCell` the instant a SYNC commit
|
|
4255
|
+
// resolves (its own doc comment), which would have closed a gated chip editor
|
|
4256
|
+
// after the very first tag. `DataTableBodyCell` instead tracks THIS editor's own
|
|
4257
|
+
// open/closed state in its local `overlayOpen`, independent of `editingCell`.
|
|
4258
|
+
// Every add/remove still commits the FULL next array through the same one-string
|
|
4095
4259
|
// contract every other editor already uses (`useDataTableEdit`'s own doc
|
|
4096
|
-
// comment): `JSON.stringify(nextTags)`, which the consumer's `onEdit` parses
|
|
4097
|
-
//
|
|
4098
|
-
//
|
|
4260
|
+
// comment): `JSON.stringify(nextTags)`, which the consumer's `onEdit` parses back
|
|
4261
|
+
// -- crisp/GRC's contract stays "one string in, no typed union", not a second
|
|
4262
|
+
// commit shape invented for just this column.
|
|
4263
|
+
//
|
|
4264
|
+
// Renders through `classNames.chipListEditor`, NOT the read-only `chipList` slot
|
|
4265
|
+
// (cells.tsx's `ChipListCell`) -- the display row clips to one line with a "+N"
|
|
4266
|
+
// badge; this editor instead WRAPS onto as many lines as it needs inside the
|
|
4267
|
+
// overlay's own grow-in-height box, since every tag (plus the add-input) has to
|
|
4268
|
+
// stay visible and editable at once.
|
|
4099
4269
|
function ChipListEditor({ column, value, pending, onCommit, classNames }) {
|
|
4100
4270
|
const tags = Array.isArray(value) ? value.filter((tag)=>!!tag) : [];
|
|
4101
4271
|
const [draft, setDraft] = React__namespace.useState("");
|
|
@@ -4116,7 +4286,7 @@ function ChipListEditor({ column, value, pending, onCommit, classNames }) {
|
|
|
4116
4286
|
}
|
|
4117
4287
|
return(// biome-ignore lint/a11y/noStaticElementInteractions: not a widget -- this span never responds to interaction on ITSELF, only stops propagation of events that already occurred on a real interactive descendant (the remove buttons, the add-tag input). No role fits it because it isn't a control.
|
|
4118
4288
|
/*#__PURE__*/ jsxRuntime.jsxs("span", {
|
|
4119
|
-
className: classNames.
|
|
4289
|
+
className: classNames.chipListEditor,
|
|
4120
4290
|
onClick: (event)=>event.stopPropagation(),
|
|
4121
4291
|
children: [
|
|
4122
4292
|
tags.map((tag)=>/*#__PURE__*/ jsxRuntime.jsx(SwatchChip, {
|
|
@@ -4134,6 +4304,8 @@ function ChipListEditor({ column, value, pending, onCommit, classNames }) {
|
|
|
4134
4304
|
})
|
|
4135
4305
|
}, tag)),
|
|
4136
4306
|
/*#__PURE__*/ jsxRuntime.jsx("input", {
|
|
4307
|
+
// biome-ignore lint/a11y/noAutofocus: this editor only ever mounts in response to an explicit click that just opened it (never on page load), matching `SelectEditor`'s own `open` (controlled, always-open-on-mount) -- the same UX every gated editor in this file already gives via a component the linter can't see through (`TextFieldInput`/`SelectSearchField`), just on a bare `<input>` here since `chipInput` has no such wrapper.
|
|
4308
|
+
autoFocus: true,
|
|
4137
4309
|
className: classNames.chipInput,
|
|
4138
4310
|
value: draft,
|
|
4139
4311
|
disabled: pending,
|
|
@@ -4141,14 +4313,19 @@ function ChipListEditor({ column, value, pending, onCommit, classNames }) {
|
|
|
4141
4313
|
"aria-label": `Add tag to ${column.key}`,
|
|
4142
4314
|
onChange: (event)=>setDraft(event.target.value),
|
|
4143
4315
|
onKeyDown: (event)=>{
|
|
4316
|
+
// Escape is the ONE key this input must NOT stop propagation for: there is
|
|
4317
|
+
// no in-progress draft state worth clearing locally (every add/remove
|
|
4318
|
+
// already commits immediately), so it needs to bubble all the way to
|
|
4319
|
+
// `document` -- `DataTableBodyCell`'s own Escape listener is what actually
|
|
4320
|
+
// closes this popover. Every other key stops here, same as before, so it
|
|
4321
|
+
// never reaches the row's own click/keyboard handling.
|
|
4322
|
+
if (event.key === "Escape") return;
|
|
4144
4323
|
event.stopPropagation();
|
|
4145
4324
|
if (event.key === "Enter") {
|
|
4146
4325
|
event.preventDefault();
|
|
4147
4326
|
addTag();
|
|
4148
4327
|
} else if (event.key === "Backspace" && draft === "" && tags.length > 0) {
|
|
4149
4328
|
removeTag(tags[tags.length - 1]);
|
|
4150
|
-
} else if (event.key === "Escape") {
|
|
4151
|
-
setDraft("");
|
|
4152
4329
|
}
|
|
4153
4330
|
}
|
|
4154
4331
|
}),
|
|
@@ -4253,12 +4430,41 @@ function isCellTruncated(cell) {
|
|
|
4253
4430
|
/** `editable` + one of these shapes needs no double-click gate and no separate rest
|
|
4254
4431
|
* display -- interacting with the widget itself IS the commit gesture (crisp's own
|
|
4255
4432
|
* #453/#760/#761: `switch`/`rating`/`checkbox` never had a meaningfully different
|
|
4256
|
-
* "rest" state to show). `chip-list` (Feature B, #8)
|
|
4257
|
-
*
|
|
4258
|
-
*
|
|
4259
|
-
*
|
|
4433
|
+
* "rest" state to show). `chip-list` used to join them here (Feature B, #8) but no
|
|
4434
|
+
* longer does -- see `isChipListEditable` below, its own separate "click opens a
|
|
4435
|
+
* popover, closed the rest of the time" category, and `ChipListEditor`'s own doc
|
|
4436
|
+
* comment (editors.tsx) for why it isn't just the ordinary GATED (double-click)
|
|
4437
|
+
* shape either. Every other `editable` column opens on double-click. */ function isAlwaysLiveEditable(column) {
|
|
4260
4438
|
if (!column.editable) return false;
|
|
4261
|
-
return column.editor === "switch" || column.type === "rating" || column.type === "checkbox"
|
|
4439
|
+
return column.editor === "switch" || column.type === "rating" || column.type === "checkbox";
|
|
4440
|
+
}
|
|
4441
|
+
/** Feature B rework (#8) -- an editable `chip-list` column's own third category,
|
|
4442
|
+
* alongside `isAlwaysLiveEditable` (above) and the ordinary double-click GATED
|
|
4443
|
+
* shape: a single click (no truncation gate -- even a short, unclipped 2-tag cell
|
|
4444
|
+
* must open just as readily as a long one) opens `editors.tsx`'s `ChipListEditor`,
|
|
4445
|
+
* and it closes back to the plain `ChipListCell` display on blur/outside-click/
|
|
4446
|
+
* Escape (`DataTableBodyCell`'s own `overlayOpen`) -- never always-live
|
|
4447
|
+
* (crispy-seed#1094: that overflowed the cell and collided with sticky columns),
|
|
4448
|
+
* never simply GATED either (a `useDataTableEdit`-driven `isEditing` gate would
|
|
4449
|
+
* close itself after the first tag commits, see `ChipListEditor`'s own doc
|
|
4450
|
+
* comment).
|
|
4451
|
+
*
|
|
4452
|
+
* Unlike every OTHER open editor in this file, this one does NOT render inside
|
|
4453
|
+
* Feature C's `CellExpandOverlay` (an anchored box positioned relative to the
|
|
4454
|
+
* `<td>`, `position: absolute` against it) -- it renders through a REAL React
|
|
4455
|
+
* portal to `document.body` instead (`DataTableBodyCell`'s own `popoverStyle`).
|
|
4456
|
+
* Confirmed live, crispy-seed#1094's pinned-column follow-up: a `position: sticky`
|
|
4457
|
+
* `<td>` clips ANY descendant content that grows past its own box to the table's
|
|
4458
|
+
* scrollport, in EVERY browser tested, regardless of `overflow: visible` on every
|
|
4459
|
+
* element in between and regardless of how high its `z-index` is raised -- neither
|
|
4460
|
+
* of which is a stacking-context or overflow bug this file can fix from the
|
|
4461
|
+
* INSIDE. A portal sidesteps it entirely by not being a descendant of the sticky
|
|
4462
|
+
* `<td>` at all once mounted. The same sticky-clip almost certainly affects the
|
|
4463
|
+
* OTHER gated editors' `CellExpandOverlay` too when their own column is pinned,
|
|
4464
|
+
* but porting all of Feature C to this same portal model is its own change,
|
|
4465
|
+
* out of scope here -- this fix is scoped to the one editor crispy-seed#1094
|
|
4466
|
+
* actually reported against a pinned column. */ function isChipListEditable(column) {
|
|
4467
|
+
return !!column.editable && column.type === "chip-list";
|
|
4262
4468
|
}
|
|
4263
4469
|
function ColumnBodyCells({ columns, row, classNames, locale, selectable, stickyFirst, stickyLast }) {
|
|
4264
4470
|
const effective = useEffectiveColumns(columns, {
|
|
@@ -4288,8 +4494,13 @@ function ColumnBodyCells({ columns, row, classNames, locale, selectable, stickyF
|
|
|
4288
4494
|
* `ColumnBodyCells` keys it by `column.key` the same way it always keyed the bare
|
|
4289
4495
|
* `<DataTableCell>` before this split.
|
|
4290
4496
|
*/ function DataTableBodyCell({ column, row, classNames, locale, sticky }) {
|
|
4291
|
-
const { startEdit, isEditing, isPeeking, openPeek, closePeek } = useDataTableContext();
|
|
4497
|
+
const { startEdit, cancelEdit, isEditing, isPending, commitEdit, isPeeking, openPeek, closePeek } = useDataTableContext();
|
|
4292
4498
|
const cellRef = React__namespace.useRef(null);
|
|
4499
|
+
// Feature B rework (#8) -- the chip-list popover's own live position, in VIEWPORT
|
|
4500
|
+
// coordinates (`position: fixed`), since it portals to `document.body` rather than
|
|
4501
|
+
// anchoring inside the `<td>` (see `isChipListEditable`'s own doc comment for why).
|
|
4502
|
+
// `null` while closed -- nothing to portal yet.
|
|
4503
|
+
const [popoverStyle, setPopoverStyle] = React__namespace.useState(null);
|
|
4293
4504
|
// Feature C (#12) -- whether THIS activation (click or double-click) should
|
|
4294
4505
|
// render as the anchored overlay rather than plain inline. Frozen at the moment
|
|
4295
4506
|
// of activation (re-measured every time, never left stale) rather than derived
|
|
@@ -4299,20 +4510,68 @@ function ColumnBodyCells({ columns, row, classNames, locale, selectable, stickyF
|
|
|
4299
4510
|
// rendering exactly as it always has, not suddenly gain overlay chrome.
|
|
4300
4511
|
const [overlayOpen, setOverlayOpen] = React__namespace.useState(false);
|
|
4301
4512
|
const alwaysLive = isAlwaysLiveEditable(column);
|
|
4302
|
-
|
|
4303
|
-
//
|
|
4304
|
-
//
|
|
4305
|
-
//
|
|
4306
|
-
//
|
|
4307
|
-
//
|
|
4308
|
-
//
|
|
4309
|
-
|
|
4513
|
+
const chipListEditable = isChipListEditable(column);
|
|
4514
|
+
// A gated `editable` column (everything except the always-live widgets AND the
|
|
4515
|
+
// click-to-edit chip-list) opens on double-click -- Attio's own measured gesture
|
|
4516
|
+
// (crisp's #411), matched here rather than a single click, which stays "select
|
|
4517
|
+
// this cell" in crisp's own spreadsheet model and would otherwise be
|
|
4518
|
+
// indistinguishable from this table's plain row click. Feature C ADDS a single
|
|
4519
|
+
// click as a SECOND way in, but only when the cell's content is actually
|
|
4520
|
+
// truncated (see `handleClick` below) -- double-click keeps working
|
|
4521
|
+
// unconditionally, same as before this feature.
|
|
4522
|
+
const gated = column.editable && !alwaysLive && !chipListEditable;
|
|
4523
|
+
// This cell's chip-list popover is showing right now -- `overlayOpen` alone isn't
|
|
4524
|
+
// enough (it's shared with the read-only peek/gated-edit cases below), so this
|
|
4525
|
+
// ANDs in "is this actually a chip-list column" too. Gates the portal render
|
|
4526
|
+
// below (`popoverStyle` is only ever non-null while this is true).
|
|
4527
|
+
const chipListEditorOpen = chipListEditable && overlayOpen;
|
|
4528
|
+
// Feature B rework (#8) -- the chip-list popover's own position, read off the
|
|
4529
|
+
// `<td>`'s LIVE `getBoundingClientRect()` (viewport coordinates, matching
|
|
4530
|
+
// `position: fixed`) rather than anything Feature C's `pinOffset`/sticky math
|
|
4531
|
+
// already tracks -- those describe offsets WITHIN the scrolling table, not where
|
|
4532
|
+
// the cell actually sits in the viewport right now, which is what a portaled,
|
|
4533
|
+
// `position: fixed` box anchored below the cell needs.
|
|
4534
|
+
const measurePopoverPosition = React__namespace.useCallback(()=>{
|
|
4535
|
+
const cell = cellRef.current;
|
|
4536
|
+
if (!cell) return;
|
|
4537
|
+
const rect = cell.getBoundingClientRect();
|
|
4538
|
+
setPopoverStyle({
|
|
4539
|
+
position: "fixed",
|
|
4540
|
+
top: rect.bottom,
|
|
4541
|
+
insetInlineStart: rect.left,
|
|
4542
|
+
insetInlineEnd: "auto",
|
|
4543
|
+
minWidth: rect.width,
|
|
4544
|
+
// Both override the SAME `expandOverlay` class properties this box also
|
|
4545
|
+
// carries (`className={classNames.expandOverlay}`, below) -- that class's
|
|
4546
|
+
// `minHeight: calc(100% + 2px)` resolves against an ANCHORED box's own
|
|
4547
|
+
// containing block (the `<td>`, ~36px), which is meaningless (and would
|
|
4548
|
+
// resolve against the VIEWPORT instead, ~100% of the page's height) for a
|
|
4549
|
+
// portaled `position: fixed` box; `auto` lets the content size it instead.
|
|
4550
|
+
// `zIndex: 10` is Feature C's own value for an in-place overlay competing
|
|
4551
|
+
// only with OTHER table content -- a portal to `document.body` can land
|
|
4552
|
+
// beside arbitrary page chrome (this Storybook's own toolbar, a consuming
|
|
4553
|
+
// app's header), so it gets a value comfortably above typical page chrome.
|
|
4554
|
+
minHeight: "auto",
|
|
4555
|
+
zIndex: 1000
|
|
4556
|
+
});
|
|
4557
|
+
}, []);
|
|
4310
4558
|
function handleDoubleClick(event) {
|
|
4311
4559
|
if (!gated) return;
|
|
4312
4560
|
setOverlayOpen(isCellTruncated(event.currentTarget));
|
|
4313
4561
|
startEdit(row.id, column.key);
|
|
4314
4562
|
}
|
|
4315
4563
|
function handleClick(event) {
|
|
4564
|
+
if (chipListEditable) {
|
|
4565
|
+
// Attio's own chip-list gesture: click opens the editor regardless of
|
|
4566
|
+
// truncation -- a short, fully-visible 2-tag cell opens just as readily as
|
|
4567
|
+
// one already showing "+N". Must not also reach the row's own `onRowClick`,
|
|
4568
|
+
// same reasoning as the read-only peek/preview-button cases below.
|
|
4569
|
+
event.stopPropagation();
|
|
4570
|
+
measurePopoverPosition();
|
|
4571
|
+
setOverlayOpen(true);
|
|
4572
|
+
startEdit(row.id, column.key);
|
|
4573
|
+
return;
|
|
4574
|
+
}
|
|
4316
4575
|
if (!isCellTruncated(event.currentTarget)) return;
|
|
4317
4576
|
if (gated) {
|
|
4318
4577
|
setOverlayOpen(true);
|
|
@@ -4320,7 +4579,7 @@ function ColumnBodyCells({ columns, row, classNames, locale, selectable, stickyF
|
|
|
4320
4579
|
} else if (alwaysLive) {
|
|
4321
4580
|
// No separate open/edit state to start -- the always-live widget already
|
|
4322
4581
|
// renders unconditionally; this only switches ITS OWN box to the anchored
|
|
4323
|
-
// overlay so a long
|
|
4582
|
+
// overlay so a long value (etc.) gets room to show in full.
|
|
4324
4583
|
setOverlayOpen(true);
|
|
4325
4584
|
} else {
|
|
4326
4585
|
// Read-only truncated cell: this click's only job is opening the peek
|
|
@@ -4333,25 +4592,39 @@ function ColumnBodyCells({ columns, row, classNames, locale, selectable, stickyF
|
|
|
4333
4592
|
openPeek(row.id, column.key);
|
|
4334
4593
|
}
|
|
4335
4594
|
}
|
|
4595
|
+
// Feature B rework (#8) -- the portaled popover's own DOM node. Read by the
|
|
4596
|
+
// outside-click detector below (it lives outside `cellRef`'s subtree once
|
|
4597
|
+
// mounted, in `document.body` -- see `isChipListEditable`'s doc comment) and by
|
|
4598
|
+
// the reposition effect further down.
|
|
4599
|
+
const popoverRef = React__namespace.useRef(null);
|
|
4336
4600
|
// Closes the overlay on Escape or an outside click/pointerdown -- a gated
|
|
4337
4601
|
// editor's OWN Escape/blur handling already ends `isEditing` on its own (see
|
|
4338
4602
|
// `TextEditor`/`SelectEditor`'s existing onCancel/onOpenChange wiring), which
|
|
4339
4603
|
// alone is enough to hide a GATED cell's overlay (its render below is gated on
|
|
4340
|
-
// `isEditing` too); this listener is what the PEEK
|
|
4341
|
-
// since
|
|
4342
|
-
// `overlayOpen` on its own.
|
|
4604
|
+
// `isEditing` too); this listener is what the PEEK, ALWAYS-LIVE and chip-list-edit
|
|
4605
|
+
// cases need, since none of them has any other mechanism that ever calls
|
|
4606
|
+
// `closePeek`/`cancelEdit`/resets `overlayOpen` on its own. `cancelEdit()` is a
|
|
4607
|
+
// no-op for the peek/always-live cases (there is no `editingCell` to clear) and is
|
|
4608
|
+
// what actually ends the chip-list editor's own `startEdit` call above.
|
|
4343
4609
|
React__namespace.useEffect(()=>{
|
|
4344
4610
|
if (!overlayOpen) return;
|
|
4611
|
+
function isInside(target) {
|
|
4612
|
+
return (cellRef.current?.contains(target) ?? false) || (popoverRef.current?.contains(target) ?? false);
|
|
4613
|
+
}
|
|
4345
4614
|
function handlePointerDown(event) {
|
|
4346
|
-
if (
|
|
4615
|
+
if (!isInside(event.target)) {
|
|
4347
4616
|
setOverlayOpen(false);
|
|
4617
|
+
setPopoverStyle(null);
|
|
4348
4618
|
closePeek();
|
|
4619
|
+
cancelEdit();
|
|
4349
4620
|
}
|
|
4350
4621
|
}
|
|
4351
4622
|
function handleKeyDown(event) {
|
|
4352
4623
|
if (event.key !== "Escape") return;
|
|
4353
4624
|
setOverlayOpen(false);
|
|
4625
|
+
setPopoverStyle(null);
|
|
4354
4626
|
closePeek();
|
|
4627
|
+
cancelEdit();
|
|
4355
4628
|
}
|
|
4356
4629
|
document.addEventListener("pointerdown", handlePointerDown);
|
|
4357
4630
|
document.addEventListener("keydown", handleKeyDown);
|
|
@@ -4361,27 +4634,70 @@ function ColumnBodyCells({ columns, row, classNames, locale, selectable, stickyF
|
|
|
4361
4634
|
};
|
|
4362
4635
|
}, [
|
|
4363
4636
|
overlayOpen,
|
|
4364
|
-
closePeek
|
|
4637
|
+
closePeek,
|
|
4638
|
+
cancelEdit
|
|
4639
|
+
]);
|
|
4640
|
+
// Feature B rework (#8) -- keeps the portaled popover glued to its cell while
|
|
4641
|
+
// open: any scroll on ANY ancestor (the table's own horizontal/vertical
|
|
4642
|
+
// `scrollArea`, or the page itself) or a viewport resize re-measures. `scroll`
|
|
4643
|
+
// doesn't bubble, so this listens in the CAPTURE phase on `window` -- the
|
|
4644
|
+
// standard way to observe a scroll on an arbitrary descendant scroll container
|
|
4645
|
+
// without knowing which element it is ahead of time.
|
|
4646
|
+
React__namespace.useEffect(()=>{
|
|
4647
|
+
if (!chipListEditorOpen) return;
|
|
4648
|
+
function reposition() {
|
|
4649
|
+
measurePopoverPosition();
|
|
4650
|
+
}
|
|
4651
|
+
window.addEventListener("scroll", reposition, true);
|
|
4652
|
+
window.addEventListener("resize", reposition);
|
|
4653
|
+
return ()=>{
|
|
4654
|
+
window.removeEventListener("scroll", reposition, true);
|
|
4655
|
+
window.removeEventListener("resize", reposition);
|
|
4656
|
+
};
|
|
4657
|
+
}, [
|
|
4658
|
+
chipListEditorOpen,
|
|
4659
|
+
measurePopoverPosition
|
|
4365
4660
|
]);
|
|
4366
4661
|
const overlayActive = overlayOpen && (alwaysLive || isEditing(row.id, column.key) || isPeeking(row.id, column.key));
|
|
4367
|
-
|
|
4368
|
-
|
|
4369
|
-
|
|
4370
|
-
|
|
4371
|
-
|
|
4372
|
-
|
|
4373
|
-
|
|
4374
|
-
|
|
4375
|
-
|
|
4376
|
-
|
|
4377
|
-
|
|
4378
|
-
|
|
4379
|
-
|
|
4380
|
-
|
|
4381
|
-
|
|
4382
|
-
|
|
4383
|
-
|
|
4384
|
-
|
|
4662
|
+
const rawValue = getColumnValue(column, row);
|
|
4663
|
+
const pending = isPending(row.id, column.key);
|
|
4664
|
+
return /*#__PURE__*/ jsxRuntime.jsxs(jsxRuntime.Fragment, {
|
|
4665
|
+
children: [
|
|
4666
|
+
/*#__PURE__*/ jsxRuntime.jsx(DataTableCell, {
|
|
4667
|
+
ref: cellRef,
|
|
4668
|
+
"data-numeric": domUtils.dataAttr(column.type === "number" || column.type === "currency"),
|
|
4669
|
+
"data-editable": domUtils.dataAttr(!!column.editable),
|
|
4670
|
+
"data-sticky": sticky["data-sticky"],
|
|
4671
|
+
"data-sticky-edge": sticky["data-sticky-edge"],
|
|
4672
|
+
"data-sticky-edge-end": sticky["data-sticky-edge-end"],
|
|
4673
|
+
"data-overlay-open": domUtils.dataAttr(overlayActive),
|
|
4674
|
+
style: sticky.style,
|
|
4675
|
+
onClick: handleClick,
|
|
4676
|
+
onDoubleClick: handleDoubleClick,
|
|
4677
|
+
children: /*#__PURE__*/ jsxRuntime.jsx(CellContent, {
|
|
4678
|
+
column: column,
|
|
4679
|
+
row: row,
|
|
4680
|
+
rowId: row.id,
|
|
4681
|
+
classNames: classNames,
|
|
4682
|
+
locale: locale,
|
|
4683
|
+
overlayActive: overlayActive
|
|
4684
|
+
})
|
|
4685
|
+
}),
|
|
4686
|
+
chipListEditorOpen && popoverStyle && /*#__PURE__*/ reactDom.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.
|
|
4687
|
+
/*#__PURE__*/ jsxRuntime.jsx("div", {
|
|
4688
|
+
ref: popoverRef,
|
|
4689
|
+
className: classNames.expandOverlay,
|
|
4690
|
+
style: popoverStyle,
|
|
4691
|
+
onClick: (event)=>event.stopPropagation(),
|
|
4692
|
+
children: /*#__PURE__*/ jsxRuntime.jsx(ChipListEditor, {
|
|
4693
|
+
column: column,
|
|
4694
|
+
value: resolveEditValue(column, row, rawValue),
|
|
4695
|
+
pending: pending,
|
|
4696
|
+
onCommit: (value)=>commitEdit(row.id, column.key, value),
|
|
4697
|
+
classNames: classNames
|
|
4698
|
+
})
|
|
4699
|
+
}), document.body)
|
|
4700
|
+
]
|
|
4385
4701
|
});
|
|
4386
4702
|
}
|
|
4387
4703
|
/**
|
|
@@ -4421,12 +4737,18 @@ function ColumnBodyCells({ columns, row, classNames, locale, selectable, stickyF
|
|
|
4421
4737
|
onCommit: commit
|
|
4422
4738
|
});
|
|
4423
4739
|
} else if (column.editable && column.type === "chip-list") {
|
|
4424
|
-
|
|
4425
|
-
|
|
4426
|
-
|
|
4427
|
-
|
|
4428
|
-
|
|
4429
|
-
|
|
4740
|
+
// Feature B rework (#8) -- this column ALWAYS dispatches here to the plain
|
|
4741
|
+
// display (never to `DisplayCellContent`'s own read-only `chip-list` case,
|
|
4742
|
+
// never through the generic `isEditing` branch below): the click-to-edit
|
|
4743
|
+
// popover is a separate portal `DataTableBodyCell` renders alongside this
|
|
4744
|
+
// whole `<td>`, not a `node` this function returns -- see `isChipListEditable`'s
|
|
4745
|
+
// own doc comment (body-cells.tsx) for why it isn't anchored in place like
|
|
4746
|
+
// every other editor here.
|
|
4747
|
+
node = /*#__PURE__*/ jsxRuntime.jsx(ChipListCell, {
|
|
4748
|
+
value: rawValue,
|
|
4749
|
+
className: classNames.chipList,
|
|
4750
|
+
chipClassName: classNames.chip,
|
|
4751
|
+
overflowClassName: classNames.chipOverflow
|
|
4430
4752
|
});
|
|
4431
4753
|
} else if (column.editable && isEditing(rowId, column.key)) {
|
|
4432
4754
|
if (column.editor === "select") {
|
|
@@ -4535,10 +4857,13 @@ function DisplayCellContent({ column, row, value, classNames, locale }) {
|
|
|
4535
4857
|
className: classNames.chip
|
|
4536
4858
|
});
|
|
4537
4859
|
case "chip-list":
|
|
4860
|
+
// Non-editable chip-list only -- an editable one is fully handled by
|
|
4861
|
+
// `CellContent`'s own dedicated branch above and never reaches here.
|
|
4538
4862
|
return /*#__PURE__*/ jsxRuntime.jsx(ChipListCell, {
|
|
4539
4863
|
value: value,
|
|
4540
4864
|
className: classNames.chipList,
|
|
4541
|
-
chipClassName: classNames.chip
|
|
4865
|
+
chipClassName: classNames.chip,
|
|
4866
|
+
overflowClassName: classNames.chipOverflow
|
|
4542
4867
|
});
|
|
4543
4868
|
case "number":
|
|
4544
4869
|
return /*#__PURE__*/ jsxRuntime.jsx(jsxRuntime.Fragment, {
|
|
@@ -4925,6 +5250,7 @@ const DEFAULT_SORT_ICON = {
|
|
|
4925
5250
|
}),
|
|
4926
5251
|
columnMenu && /*#__PURE__*/ jsxRuntime.jsx(ColumnMenu, {
|
|
4927
5252
|
column: column,
|
|
5253
|
+
label: label,
|
|
4928
5254
|
onRename: ()=>setRenamingKey(column.key)
|
|
4929
5255
|
})
|
|
4930
5256
|
]
|
|
@@ -5016,9 +5342,9 @@ function ColumnRenameInput({ column, initialLabel, onDone }) {
|
|
|
5016
5342
|
* `data-table.ts`) -- crisp's own header shows the trigger only on hover, not
|
|
5017
5343
|
* permanently, so this one carries its own marker attribute distinct from that
|
|
5018
5344
|
* slot's OTHER occupants (the sort chevron, the filter trigger), which stay visible
|
|
5019
|
-
* under their own separate rules regardless of hover. */ function ColumnMenu({ column, onRename }) {
|
|
5345
|
+
* under their own separate rules regardless of hover. */ function ColumnMenu({ column, label, onRename }) {
|
|
5020
5346
|
const { moveColumn, hideColumn, isColumnPinned, togglePinColumn, sort, clearSort } = useDataTableContext();
|
|
5021
|
-
const columnLabel = typeof
|
|
5347
|
+
const columnLabel = typeof label === "string" ? label : column.key;
|
|
5022
5348
|
const pinned = isColumnPinned(column.key);
|
|
5023
5349
|
// Attio/GRC's "Remove sort" (live-review fix D) -- the header's own click-to-sort
|
|
5024
5350
|
// (`SortIndicator`/`toggleSort`, above) only cycles asc/desc once a column has been
|