@crispy-seed/data-table 0.11.0 → 0.11.2

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/index.js CHANGED
@@ -968,15 +968,33 @@ function sameCell$1(a, id, key) {
968
968
  * first captured) nor keeps a ghost entry for one that was removed. Known keys keep
969
969
  * their position from `order`; any key in `keys` missing from `order` is appended, in
970
970
  * `keys`' own order; any entry in `order` no longer in `keys` is dropped.
971
- */ function reconcileColumnOrder(order, keys) {
971
+ *
972
+ * `actionsKey` (the styled layer's `actions`-flagged column, e.g. the row-menu `⋯`
973
+ * column) is kept LAST no matter what -- without this, a brand-new column added to
974
+ * `columns` after the table first mounted got appended past it (the "any key in `keys`
975
+ * missing from `order` is appended" rule above has no notion of "past the end" vs. "at
976
+ * the very end"), stranding it in the middle of the table since `stickyLast`/`actions`
977
+ * only sticks the actual trailing column. A real consumer hit this: their newly-added
978
+ * column landed to the right of the already-pinned-sticky `actions` column, covered by
979
+ * it under horizontal scroll. Column-layout's own `pinnedCellProps` independently
980
+ * targets the `actions` column by FLAG rather than by position (belt and suspenders --
981
+ * see that file's own doc comment), but this still keeps the VISUAL order sane so nothing
982
+ * renders hidden behind the sticky cell in the first place.
983
+ */ function reconcileColumnOrder(order, keys, actionsKey) {
972
984
  const keySet = new Set(keys);
973
985
  const known = order.filter((key)=>keySet.has(key));
974
986
  const knownSet = new Set(known);
975
987
  const added = keys.filter((key)=>!knownSet.has(key));
976
- return [
988
+ const merged = [
977
989
  ...known,
978
990
  ...added
979
991
  ];
992
+ if (actionsKey === undefined || !keySet.has(actionsKey)) return merged;
993
+ const withoutActions = merged.filter((key)=>key !== actionsKey);
994
+ return [
995
+ ...withoutActions,
996
+ actionsKey
997
+ ];
980
998
  }
981
999
  /**
982
1000
  * Moves `key` one step toward `direction` in `order` -- a no-op (same array, well,
@@ -1155,7 +1173,7 @@ const EMPTY_RECORD = {};
1155
1173
  * state this hook already owns, not independent state
1156
1174
  * of their own.
1157
1175
  */ function useDataTableColumns(props) {
1158
- const { columnKeys = EMPTY_STRINGS, columnOrder: columnOrderProp, defaultColumnOrder, onColumnOrderChange, hiddenColumnKeys: hiddenProp, defaultHiddenColumnKeys = EMPTY_STRINGS, onHiddenColumnKeysChange, columnWidths: widthsProp, defaultColumnWidths = EMPTY_RECORD, onColumnWidthsChange, columnLabels: labelsProp, defaultColumnLabels = EMPTY_RECORD, onColumnLabelsChange, pinnedColumnKeys: pinnedProp, defaultPinnedColumnKeys = EMPTY_STRINGS, onPinnedColumnKeysChange } = props;
1176
+ const { columnKeys = EMPTY_STRINGS, actionsColumnKey, columnOrder: columnOrderProp, defaultColumnOrder, onColumnOrderChange, hiddenColumnKeys: hiddenProp, defaultHiddenColumnKeys = EMPTY_STRINGS, onHiddenColumnKeysChange, columnWidths: widthsProp, defaultColumnWidths = EMPTY_RECORD, onColumnWidthsChange, columnLabels: labelsProp, defaultColumnLabels = EMPTY_RECORD, onColumnLabelsChange, pinnedColumnKeys: pinnedProp, defaultPinnedColumnKeys = EMPTY_STRINGS, onPinnedColumnKeysChange } = props;
1159
1177
  const [rawOrder, setRawOrder] = useControllableState$1({
1160
1178
  prop: columnOrderProp,
1161
1179
  defaultProp: defaultColumnOrder ?? columnKeys,
@@ -1166,9 +1184,10 @@ const EMPTY_RECORD = {};
1166
1184
  // consumer persisted before a new column existed must still show that new column
1167
1185
  // (appended), and one holding a column that got removed from `columns` must not
1168
1186
  // render a ghost header for it.
1169
- const columnOrder = useMemo(()=>reconcileColumnOrder(rawOrder ?? columnKeys, columnKeys), [
1187
+ const columnOrder = useMemo(()=>reconcileColumnOrder(rawOrder ?? columnKeys, columnKeys, actionsColumnKey), [
1170
1188
  rawOrder,
1171
- columnKeys
1189
+ columnKeys,
1190
+ actionsColumnKey
1172
1191
  ]);
1173
1192
  const [hiddenColumnKeys = EMPTY_STRINGS, setHiddenColumnKeys] = useControllableState$1({
1174
1193
  prop: hiddenProp,
@@ -2150,7 +2169,7 @@ function useDataTableRowContext({ strict = true } = {}) {
2150
2169
  return context;
2151
2170
  }
2152
2171
  const DataTableRoot = /*#__PURE__*/ forwardRef((props, ref)=>{
2153
- const { sort, defaultSort, onSortChange, rowIds, selectable, selectedIds, defaultSelectedIds, onSelectionChange, query, defaultQuery, onQueryChange, toolbarSorts, defaultToolbarSorts, onToolbarSortsChange, toolbarFilters, defaultToolbarFilters, onToolbarFiltersChange, columnKeys, columnOrder, defaultColumnOrder, onColumnOrderChange, hiddenColumnKeys, defaultHiddenColumnKeys, onHiddenColumnKeysChange, columnWidths, defaultColumnWidths, onColumnWidthsChange, columnLabels, defaultColumnLabels, onColumnLabelsChange, pinnedColumnKeys, defaultPinnedColumnKeys, onPinnedColumnKeysChange, columnCalcOps, defaultColumnCalcOps, onColumnCalcOpsChange, onEdit, onLoadMore, hasMore, loadingMore, ...otherProps } = props;
2172
+ const { sort, defaultSort, onSortChange, rowIds, selectable, selectedIds, defaultSelectedIds, onSelectionChange, query, defaultQuery, onQueryChange, toolbarSorts, defaultToolbarSorts, onToolbarSortsChange, toolbarFilters, defaultToolbarFilters, onToolbarFiltersChange, columnKeys, actionsColumnKey, columnOrder, defaultColumnOrder, onColumnOrderChange, hiddenColumnKeys, defaultHiddenColumnKeys, onHiddenColumnKeysChange, columnWidths, defaultColumnWidths, onColumnWidthsChange, columnLabels, defaultColumnLabels, onColumnLabelsChange, pinnedColumnKeys, defaultPinnedColumnKeys, onPinnedColumnKeysChange, columnCalcOps, defaultColumnCalcOps, onColumnCalcOpsChange, onEdit, onLoadMore, hasMore, loadingMore, ...otherProps } = props;
2154
2173
  const api = useDataTable({
2155
2174
  sort,
2156
2175
  defaultSort,
@@ -2170,6 +2189,7 @@ const DataTableRoot = /*#__PURE__*/ forwardRef((props, ref)=>{
2170
2189
  defaultToolbarFilters,
2171
2190
  onToolbarFiltersChange,
2172
2191
  columnKeys,
2192
+ actionsColumnKey,
2173
2193
  columnOrder,
2174
2194
  defaultColumnOrder,
2175
2195
  onColumnOrderChange,
@@ -2972,6 +2992,40 @@ function EntityCell({ value, className, nameClassName, actionsClassName, actionC
2972
2992
  name: String(value ?? "")
2973
2993
  };
2974
2994
  if (!entity.name) return null;
2995
+ // Click-to-navigate (your-moon/crisp, Attio parity) -- `onOpenRecord` wins over a
2996
+ // plain `href` when both are set (see `onOpenRecord`'s own doc comment,
2997
+ // `DataTable.tsx`); neither -> the original plain, non-clickable `<span>`. Both
2998
+ // clickable variants carry `data-clickable` -- the ONLY thing the recipe's
2999
+ // `entityName` hover-underline rule keys off (`data-table.ts`), so a plain name
3000
+ // never underlines and a clickable one always does, regardless of which variant
3001
+ // rendered it.
3002
+ const nameNode = column?.onOpenRecord && row !== undefined ? // A real `<button>`, not a `<span role="link">` -- `onOpenRecord` is an
3003
+ // arbitrary consumer callback (like `onOpenPreview`'s own button below), never
3004
+ // a genuine URL, so `<button>` is the correct semantic element (native
3005
+ // keyboard activation/focusability for free, no `role`/`tabIndex`/`onKeyDown`
3006
+ // needed) -- unlike the `href` branch just below, which IS a real URL and
3007
+ // gets a real `<a>`. The recipe's `entityName` slot resets this button's own
3008
+ // chrome (border/background/padding/font/color/text-align) back to plain
3009
+ // inline text (`data-table.ts`).
3010
+ /*#__PURE__*/ jsx("button", {
3011
+ type: "button",
3012
+ className: nameClassName,
3013
+ "data-clickable": "",
3014
+ onClick: (event)=>{
3015
+ event.stopPropagation();
3016
+ column.onOpenRecord?.(row);
3017
+ },
3018
+ children: entity.name
3019
+ }) : entity.href ? /*#__PURE__*/ jsx("a", {
3020
+ className: nameClassName,
3021
+ "data-clickable": "",
3022
+ href: entity.href,
3023
+ onClick: (event)=>event.stopPropagation(),
3024
+ children: entity.name
3025
+ }) : /*#__PURE__*/ jsx("span", {
3026
+ className: nameClassName,
3027
+ children: entity.name
3028
+ });
2975
3029
  return /*#__PURE__*/ jsxs("span", {
2976
3030
  className: className,
2977
3031
  children: [
@@ -2987,10 +3041,7 @@ function EntityCell({ value, className, nameClassName, actionsClassName, actionC
2987
3041
  })
2988
3042
  ]
2989
3043
  }),
2990
- /*#__PURE__*/ jsx("span", {
2991
- className: nameClassName,
2992
- children: entity.name
2993
- }),
3044
+ nameNode,
2994
3045
  column?.onOpenPreview && row !== undefined && /*#__PURE__*/ jsx("span", {
2995
3046
  className: actionsClassName,
2996
3047
  children: /*#__PURE__*/ jsx("button", {
@@ -3514,10 +3565,25 @@ function RatingCell({ value, maxRating = 5 }) {
3514
3565
  width,
3515
3566
  pinned,
3516
3567
  pinOffset,
3517
- pinnedEdge: false
3568
+ pinnedEdge: false,
3569
+ stickyLastTarget: false
3518
3570
  };
3519
3571
  });
3520
3572
  if (pinnedCount > 0) entries[pinnedCount - 1].pinnedEdge = true;
3573
+ // `stickyLast` sticks the `actions`-flagged column specifically -- not whichever
3574
+ // column merely landed last in render order. Without this, `reconcileColumnOrder`
3575
+ // appending a brand-new column past `actions` (this file's own `<colgroup>` doc
3576
+ // comment on why order must stay in lockstep) silently moved the sticky-right
3577
+ // treatment onto that new column instead, stranding `actions` in the middle of the
3578
+ // table -- a real consumer hit this. Falls back to the genuine last entry when no
3579
+ // column is flagged `actions` at all, matching `stickyLast`'s original single-
3580
+ // column behavior.
3581
+ if (entries.length > 0) {
3582
+ const actionsColumn = columns.find((column)=>column.actions);
3583
+ const actionsIndex = actionsColumn ? entries.findIndex((entry)=>entry.column.key === actionsColumn.key) : -1;
3584
+ const stickyLastIndex = actionsIndex !== -1 ? actionsIndex : entries.length - 1;
3585
+ entries[stickyLastIndex].stickyLastTarget = true;
3586
+ }
3521
3587
  return entries;
3522
3588
  }, [
3523
3589
  columns,
@@ -3557,9 +3623,12 @@ function RatingCell({ value, maxRating = 5 }) {
3557
3623
  }
3558
3624
  /** Sticky positioning for one DATA cell (header, body, or ColumnCalc footer) -- pinned-
3559
3625
  * left (this column's own `pinOffset`, `data-sticky-edge` only on the group's
3560
- * trailing-most member) takes priority over sticky-right (`stickyLast`, always just
3561
- * the last VISIBLE column, e.g. an `actions` column) since a column can only stick to
3562
- * one edge. */ function pinnedCellProps(entry, stickyLast, isLast) {
3626
+ * trailing-most member) takes priority over sticky-right (`stickyLast`, the
3627
+ * `actions`-flagged column when one exists, else the genuine last VISIBLE column)
3628
+ * since a column can only stick to one edge. Reads `entry.stickyLastTarget`
3629
+ * (`useEffectiveColumns`) rather than an `isLast` index the caller computes itself --
3630
+ * that index is just "last in render order", which is exactly what stranded a
3631
+ * reordered-past `actions` column in the first place. */ function pinnedCellProps(entry, stickyLast) {
3563
3632
  if (entry.pinned) {
3564
3633
  return {
3565
3634
  "data-sticky": "",
@@ -3571,7 +3640,7 @@ function RatingCell({ value, maxRating = 5 }) {
3571
3640
  }
3572
3641
  };
3573
3642
  }
3574
- if (stickyLast && isLast) {
3643
+ if (stickyLast && entry.stickyLastTarget) {
3575
3644
  return {
3576
3645
  "data-sticky": "",
3577
3646
  "data-sticky-edge-end": "",
@@ -3907,7 +3976,7 @@ const TextFieldTextarea = /*#__PURE__*/ forwardRef(({ className, autoresize = tr
3907
3976
  });
3908
3977
  TextFieldTextarea.displayName = "TextFieldTextarea";
3909
3978
 
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))}");
3979
+ __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__entityName[data-clickable]{cursor:pointer;border:none;background:0 0;padding:0;margin:0;font-family:inherit;font-size:inherit;line-height:inherit;color:inherit;text-align:start}.seed-data-table__entityName[data-clickable]:is(:hover,[data-hover]){text-decoration:underline}.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:13px;font-weight:var(--seed-font-weight-medium);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))}");
3911
3980
 
3912
3981
  const createClassName = (className, variants, compoundVariants = [])=>{
3913
3982
  const variantKeys = Object.keys(variants);
@@ -4730,26 +4799,87 @@ function ChipListEditor({ column, value, pending, onCommit, classNames }) {
4730
4799
  }));
4731
4800
  }
4732
4801
  ////////////////////////////////////////////////////////////////////////////////////
4733
- // -- Feature C (#12): cell-expand "show full content" overlay -- an anchored box
4734
- // (position:absolute over the cell it belongs to, grows in HEIGHT, elevated)
4735
- // that wraps whatever a truncated cell would otherwise show inline: an editable
4736
- // column's OWN editor node (unchanged, just relocated into this box instead of
4737
- // rendered flush in the 36px row) for the editable case, or the plain full value
4738
- // for a read-only one. `body-cells.tsx` decides WHEN to render this (a click on a
4739
- // cell whose content is actually truncated, `truncate.ts`'s `isCellTruncated`);
4740
- // this component only supplies the box.
4741
- function CellExpandOverlay({ className, children }) {
4742
- return(// `stopPropagation` -- an overlay sits on top of (and past) its own cell/row;
4802
+ // -- Feature C (#12): cell-expand "show full content" overlay -- a box that wraps
4803
+ // whatever a truncated cell would otherwise show inline: an editable column's OWN
4804
+ // editor node (unchanged, just relocated into this box instead of rendered flush in
4805
+ // the 36px row) for the editable case, or the plain full value for a read-only one.
4806
+ // `body-cells.tsx` decides WHEN to render this (a click on a cell whose content is
4807
+ // actually truncated, `truncate.ts`'s `isCellTruncated`); this component only
4808
+ // supplies the box.
4809
+ //
4810
+ // Portals to `document.body` (crispy-seed#1094's own pinned-column follow-up,
4811
+ // ported from the chip-list editor's popover, `body-cells.tsx`'s
4812
+ // `isChipListEditable` -- that one shipped this exact fix in 0.10.3) rather than
4813
+ // positioning `absolute` against its own cell: a `position: sticky` `<td>` clips ANY
4814
+ // descendant that grows past its own box, in every browser tested, regardless of
4815
+ // `overflow`/`z-index` on anything in between, so a pinned column's own truncated
4816
+ // cell used to clip this overlay's grown content instead of showing it in full. A
4817
+ // portal sidesteps that by not being a descendant of the sticky `<td>` at all once
4818
+ // mounted -- position is instead measured live off `anchorRef`'s own
4819
+ // `getBoundingClientRect()`, the same technique the chip-list popover already uses.
4820
+ function CellExpandOverlay({ className, anchorRef, contentRef, children }) {
4821
+ const [style, setStyle] = React.useState(null);
4822
+ // Grows 1px past the cell's own edges in every direction, matching this box's
4823
+ // pre-portal `top: -1px; insetInlineStart: -1px; insetInlineEnd: -1px` (the recipe's
4824
+ // `expandOverlay` class, `data-table.ts`) -- hides the 1px border seam against the
4825
+ // cell it's expanding out of. `minHeight`/`insetInlineEnd` from that same class no
4826
+ // longer apply once portaled (they resolved against the `<td>` as this box's own
4827
+ // containing block, meaningless once it's a `position: fixed` child of `<body>`
4828
+ // instead -- the same override the chip-list popover's own `measurePopoverPosition`,
4829
+ // `body-cells.tsx`, already makes), so this overrides both explicitly.
4830
+ const measure = React.useCallback(()=>{
4831
+ const cell = anchorRef.current;
4832
+ if (!cell) return;
4833
+ const rect = cell.getBoundingClientRect();
4834
+ setStyle({
4835
+ position: "fixed",
4836
+ top: rect.top - 1,
4837
+ insetInlineStart: rect.left - 1,
4838
+ insetInlineEnd: "auto",
4839
+ width: rect.width + 2,
4840
+ minHeight: "auto",
4841
+ zIndex: 1000
4842
+ });
4843
+ }, [
4844
+ anchorRef
4845
+ ]);
4846
+ // Measured before paint, on mount -- see this component's own prop doc comment for
4847
+ // why it can't just trust a caller to have measured first.
4848
+ React.useLayoutEffect(()=>{
4849
+ measure();
4850
+ }, [
4851
+ measure
4852
+ ]);
4853
+ // Same capture-phase listeners as the chip-list popover's own reposition effect
4854
+ // (`body-cells.tsx`) -- `scroll` doesn't bubble, so the table's own horizontal/
4855
+ // vertical scroll container (an arbitrary ancestor, not necessarily `window` itself)
4856
+ // is only observable this way.
4857
+ React.useEffect(()=>{
4858
+ window.addEventListener("scroll", measure, true);
4859
+ window.addEventListener("resize", measure);
4860
+ return ()=>{
4861
+ window.removeEventListener("scroll", measure, true);
4862
+ window.removeEventListener("resize", measure);
4863
+ };
4864
+ }, [
4865
+ measure
4866
+ ]);
4867
+ // Nothing to portal until the first measurement lands (avoids a one-frame flash at
4868
+ // the origin corner before `useLayoutEffect` runs).
4869
+ if (!style) return null;
4870
+ return /*#__PURE__*/ createPortal(// `stopPropagation` -- an overlay sits on top of (and past) its own cell/row;
4743
4871
  // a click anywhere inside it must never ALSO fire the row's own `onRowClick`,
4744
4872
  // the same guard every other interactive cell affordance in this directory
4745
4873
  // already applies (`LinkCell`, `EntityCell`'s preview button, editors.tsx's
4746
4874
  // `TextEditor` root).
4747
4875
  // biome-ignore lint/a11y/noStaticElementInteractions: not a widget -- this div never responds to interaction on ITSELF, only stops propagation of events that already occurred on a real interactive descendant (the editor/text it wraps). No role fits it because it isn't a control.
4748
4876
  /*#__PURE__*/ jsx("div", {
4877
+ ref: contentRef,
4749
4878
  className: className,
4879
+ style: style,
4750
4880
  onClick: (event)=>event.stopPropagation(),
4751
4881
  children: children
4752
- }));
4882
+ }), document.body);
4753
4883
  }
4754
4884
 
4755
4885
  // Row-narrowing for the toolbar's search box -- split out of DataTable.tsx the way
@@ -4845,21 +4975,17 @@ function isCellTruncated(cell) {
4845
4975
  * close itself after the first tag commits, see `ChipListEditor`'s own doc
4846
4976
  * comment).
4847
4977
  *
4848
- * Unlike every OTHER open editor in this file, this one does NOT render inside
4849
- * Feature C's `CellExpandOverlay` (an anchored box positioned relative to the
4850
- * `<td>`, `position: absolute` against it) -- it renders through a REAL React
4851
- * portal to `document.body` instead (`DataTableBodyCell`'s own `popoverStyle`).
4852
- * Confirmed live, crispy-seed#1094's pinned-column follow-up: a `position: sticky`
4853
- * `<td>` clips ANY descendant content that grows past its own box to the table's
4854
- * scrollport, in EVERY browser tested, regardless of `overflow: visible` on every
4855
- * element in between and regardless of how high its `z-index` is raised -- neither
4856
- * of which is a stacking-context or overflow bug this file can fix from the
4857
- * INSIDE. A portal sidesteps it entirely by not being a descendant of the sticky
4858
- * `<td>` at all once mounted. The same sticky-clip almost certainly affects the
4859
- * OTHER gated editors' `CellExpandOverlay` too when their own column is pinned,
4860
- * but porting all of Feature C to this same portal model is its own change,
4861
- * out of scope here -- this fix is scoped to the one editor crispy-seed#1094
4862
- * actually reported against a pinned column. */ function isChipListEditable(column) {
4978
+ * This one renders through its OWN REAL React portal to `document.body`
4979
+ * (`DataTableBodyCell`'s own `popoverStyle`) rather than `editors.tsx`'s shared
4980
+ * `CellExpandOverlay`, since its shape is different (Attio's own click-anywhere-
4981
+ * opens gesture, no truncation gate). `CellExpandOverlay` itself ALSO portals to
4982
+ * `document.body` now (ported from this exact fix) -- both exist because a
4983
+ * `position: sticky` `<td>` clips ANY descendant content that grows past its own
4984
+ * box to the table's scrollport, in EVERY browser tested, regardless of
4985
+ * `overflow: visible` on every element in between and regardless of how high its
4986
+ * `z-index` is raised -- neither of which is a stacking-context or overflow bug
4987
+ * fixable from the INSIDE. A portal sidesteps it entirely by not being a
4988
+ * descendant of the sticky `<td>` at all once mounted. */ function isChipListEditable(column) {
4863
4989
  return !!column.editable && column.type === "chip-list";
4864
4990
  }
4865
4991
  function ColumnBodyCells({ columns, row, classNames, locale, selectable, stickyFirst, stickyLast, cellContextMenu, onViewEditHistory, cellContextMenuItems }) {
@@ -4868,10 +4994,9 @@ function ColumnBodyCells({ columns, row, classNames, locale, selectable, stickyF
4868
4994
  stickyFirst
4869
4995
  });
4870
4996
  return /*#__PURE__*/ jsx(Fragment, {
4871
- children: effective.map((entry, index)=>{
4997
+ children: effective.map((entry)=>{
4872
4998
  const { column } = entry;
4873
- const isLast = index === effective.length - 1;
4874
- const sticky = pinnedCellProps(entry, stickyLast, isLast);
4999
+ const sticky = pinnedCellProps(entry, stickyLast);
4875
5000
  return /*#__PURE__*/ jsx(DataTableBodyCell, {
4876
5001
  column: column,
4877
5002
  row: row,
@@ -5019,6 +5144,16 @@ function ColumnBodyCells({ columns, row, classNames, locale, selectable, stickyF
5019
5144
  // mounted, in `document.body` -- see `isChipListEditable`'s doc comment) and by
5020
5145
  // the reposition effect further down.
5021
5146
  const popoverRef = React.useRef(null);
5147
+ // Feature C's own portaled overlay (`CellExpandOverlay`, editors.tsx) -- its own DOM
5148
+ // node, once mounted, same reasoning as `popoverRef` above: it lives outside
5149
+ // `cellRef`'s subtree (a sibling of the table in `document.body`, not a descendant of
5150
+ // the `<td>`), so the outside-click detector below needs a ref to it too. Without
5151
+ // this, `isInside` saw every click/keystroke INSIDE the portaled editor as "outside"
5152
+ // once the overlay actually escaped the `<td>` (this fix's whole point) -- e.g.
5153
+ // typing into the overlay's own `TextEditor` fires a pointerdown on an input that is
5154
+ // no longer `cellRef.current`'s descendant, so every keystroke closed the overlay and
5155
+ // cancelled the edit before the user finished typing.
5156
+ const expandOverlayRef = React.useRef(null);
5022
5157
  // Closes the overlay on Escape or an outside click/pointerdown -- a gated
5023
5158
  // editor's OWN Escape/blur handling already ends `isEditing` on its own (see
5024
5159
  // `TextEditor`/`SelectEditor`'s existing onCancel/onOpenChange wiring), which
@@ -5031,7 +5166,7 @@ function ColumnBodyCells({ columns, row, classNames, locale, selectable, stickyF
5031
5166
  React.useEffect(()=>{
5032
5167
  if (!overlayOpen) return;
5033
5168
  function isInside(target) {
5034
- return (cellRef.current?.contains(target) ?? false) || (popoverRef.current?.contains(target) ?? false);
5169
+ return (cellRef.current?.contains(target) ?? false) || (popoverRef.current?.contains(target) ?? false) || (expandOverlayRef.current?.contains(target) ?? false);
5035
5170
  }
5036
5171
  function handlePointerDown(event) {
5037
5172
  if (!isInside(event.target)) {
@@ -5104,7 +5239,9 @@ function ColumnBodyCells({ columns, row, classNames, locale, selectable, stickyF
5104
5239
  rowId: row.id,
5105
5240
  classNames: classNames,
5106
5241
  locale: locale,
5107
- overlayActive: overlayActive
5242
+ overlayActive: overlayActive,
5243
+ cellRef: cellRef,
5244
+ expandOverlayRef: expandOverlayRef
5108
5245
  }),
5109
5246
  cellContextMenu && contextMenuOpen && contextMenuPoint && // A `<td>` (unlike its own parent `<tr>`) can validly hold any flow
5110
5247
  // content, so `CellContextMenu`'s own 0x0 `MenuAnchor` -- itself never
@@ -5149,7 +5286,7 @@ function ColumnBodyCells({ columns, row, classNames, locale, selectable, stickyF
5149
5286
  * display (a custom `render` wins there, same as before `editable` existed; it never
5150
5287
  * overrides an actual editing/always-live state, matching crisp's own split between
5151
5288
  * `renderTyped`'s rest-state branch and the editing branches above it).
5152
- */ function CellContent({ column, row, rowId, classNames, locale, overlayActive }) {
5289
+ */ function CellContent({ column, row, rowId, classNames, locale, overlayActive, cellRef, expandOverlayRef }) {
5153
5290
  const { isEditing, isPending, getEditError, commitEdit, cancelEdit } = useDataTableContext();
5154
5291
  const rawValue = getColumnValue(column, row);
5155
5292
  const pending = isPending(rowId, column.key);
@@ -5243,6 +5380,8 @@ function ColumnBodyCells({ columns, row, classNames, locale, selectable, stickyF
5243
5380
  children: [
5244
5381
  overlayActive ? /*#__PURE__*/ jsx(CellExpandOverlay, {
5245
5382
  className: classNames.expandOverlay,
5383
+ anchorRef: cellRef,
5384
+ contentRef: expandOverlayRef,
5246
5385
  children: column.editable ? node : /*#__PURE__*/ jsx("span", {
5247
5386
  className: classNames.expandOverlayText,
5248
5387
  children: node
@@ -5406,10 +5545,9 @@ function ColumnCalcCells({ columns, rows, selectable, stickyFirst, stickyLast })
5406
5545
  stickyFirst
5407
5546
  });
5408
5547
  return /*#__PURE__*/ jsx(Fragment, {
5409
- children: effective.map((entry, index)=>{
5548
+ children: effective.map((entry)=>{
5410
5549
  const { column } = entry;
5411
- const isLast = index === effective.length - 1;
5412
- const sticky = pinnedCellProps(entry, stickyLast, isLast);
5550
+ const sticky = pinnedCellProps(entry, stickyLast);
5413
5551
  return /*#__PURE__*/ jsx(DataTableFooterCell, {
5414
5552
  "data-sticky": sticky["data-sticky"],
5415
5553
  "data-sticky-edge": sticky["data-sticky-edge"],
@@ -5474,9 +5612,20 @@ function formatCalcValue(value, op) {
5474
5612
  size: 14
5475
5613
  })
5476
5614
  ]
5477
- }) : /*#__PURE__*/ jsx("span", {
5615
+ }) : /*#__PURE__*/ jsxs("span", {
5478
5616
  className: "seed-data-table-column-calc-placeholder",
5479
- children: "Add calculation"
5617
+ style: {
5618
+ display: "inline-flex",
5619
+ alignItems: "center",
5620
+ gap: 4
5621
+ },
5622
+ children: [
5623
+ /*#__PURE__*/ jsx(IconPlusLine_default, {
5624
+ size: 13,
5625
+ strokeWidth: 1.75
5626
+ }),
5627
+ "Add calculation"
5628
+ ]
5480
5629
  })
5481
5630
  })
5482
5631
  }),
@@ -5632,10 +5781,9 @@ const DEFAULT_SORT_ICON = {
5632
5781
  });
5633
5782
  const [renamingKey, setRenamingKey] = React.useState(null);
5634
5783
  return /*#__PURE__*/ jsx(Fragment, {
5635
- children: effective.map((entry, index)=>{
5784
+ children: effective.map((entry)=>{
5636
5785
  const { column, label, width } = entry;
5637
- const isLast = index === effective.length - 1;
5638
- const sticky = pinnedCellProps(entry, stickyLast, isLast);
5786
+ const sticky = pinnedCellProps(entry, stickyLast);
5639
5787
  return /*#__PURE__*/ jsxs(DataTableHeaderCell, {
5640
5788
  columnKey: column.key,
5641
5789
  sortable: column.sortable,
@@ -11645,6 +11793,12 @@ function DataTableInner(props, ref) {
11645
11793
  const columnKeys = React.useMemo(()=>columns.map((column)=>column.key), [
11646
11794
  columns
11647
11795
  ]);
11796
+ // Threaded down to `reconcileColumnOrder` (react-headless's `dom.ts`) so a brand-new
11797
+ // column added to `columns` after mount gets appended BEFORE this one, not past it --
11798
+ // see that function's own doc comment for the bug this prevents.
11799
+ const actionsColumnKey = React.useMemo(()=>columns.find((column)=>column.actions)?.key, [
11800
+ columns
11801
+ ]);
11648
11802
  const searchable = React.useMemo(()=>columns.some((column)=>column.searchable), [
11649
11803
  columns
11650
11804
  ]);
@@ -11687,6 +11841,7 @@ function DataTableInner(props, ref) {
11687
11841
  toolbarFilters: resolvedToolbarFilters,
11688
11842
  onToolbarFiltersChange: handleToolbarFiltersChange,
11689
11843
  columnKeys: columnKeys,
11844
+ actionsColumnKey: actionsColumnKey,
11690
11845
  columnOrder: columnOrder,
11691
11846
  defaultColumnOrder: defaultColumnOrder,
11692
11847
  onColumnOrderChange: onColumnOrderChange,
package/dist/styles.css CHANGED
@@ -4934,6 +4934,23 @@ tbody tr:last-child .seed-data-table__cell {
4934
4934
  overflow: clip;
4935
4935
  }
4936
4936
 
4937
+ .seed-data-table__entityName[data-clickable] {
4938
+ cursor: pointer;
4939
+ font-family: inherit;
4940
+ font-size: inherit;
4941
+ line-height: inherit;
4942
+ color: inherit;
4943
+ text-align: start;
4944
+ background: none;
4945
+ border: none;
4946
+ margin: 0;
4947
+ padding: 0;
4948
+ }
4949
+
4950
+ .seed-data-table__entityName[data-clickable]:is(:hover, [data-hover]) {
4951
+ text-decoration: underline;
4952
+ }
4953
+
4937
4954
  .seed-data-table__tagList {
4938
4955
  align-items: center;
4939
4956
  gap: var(--seed-dimension-x1_5);
@@ -4996,8 +5013,8 @@ tr:hover .seed-data-table__entityActions, .seed-data-table__entityActions:focus-
4996
5013
  box-sizing: border-box;
4997
5014
  max-width: 100%;
4998
5015
  height: 22px;
4999
- font-size: 10px;
5000
- font-weight: var(--seed-font-weight-regular);
5016
+ font-size: 13px;
5017
+ font-weight: var(--seed-font-weight-medium);
5001
5018
  text-overflow: ellipsis;
5002
5019
  white-space: nowrap;
5003
5020
  border-radius: 7px;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crispy-seed/data-table",
3
- "version": "0.11.0",
3
+ "version": "0.11.2",
4
4
  "description": "Self-contained, publishable build of seed-design's Attio-measured DataTable, ResizablePanel and RecordPreview — one install, one CSS import.",
5
5
  "repository": {
6
6
  "type": "git",