@crisp-ui-kit/crisp 0.54.8 → 0.54.10

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.
Files changed (59) hide show
  1. package/dist/board.css +32 -8
  2. package/dist/board.layered.css +32 -8
  3. package/dist/cjs/components/contextmenu/contextmenu.js +2 -2
  4. package/dist/cjs/components/contextmenu/contextmenu.recipe.js +24 -0
  5. package/dist/cjs/components/datatable/datatable.js +593 -51
  6. package/dist/cjs/components/datatable/datatable.recipe.js +67 -3
  7. package/dist/cjs/components/dialog/dialog.js +65 -1
  8. package/dist/cjs/components/tabletoolbar/tabletoolbar.chrome.js +37 -2
  9. package/dist/cjs/components/tabletoolbar/tabletoolbar.js +218 -39
  10. package/dist/cjs/components/tabletoolbar/tabletoolbar.recipe.js +48 -1
  11. package/dist/cjs/components/text/text.recipe.js +17 -7
  12. package/dist/cjs/product/board/board.js +62 -28
  13. package/dist/cjs/product/board/board.recipe.js +42 -2
  14. package/dist/cjs/product/comments/comments.js +11 -2
  15. package/dist/cjs/product/comments/comments.recipe.js +14 -4
  16. package/dist/cjs/product/recordstable/recordstable.js +1 -17
  17. package/dist/cjs/product/savedviews/savedviews.js +55 -24
  18. package/dist/cjs/product/savedviews/savedviews.recipe.js +27 -0
  19. package/dist/comments.css +14 -4
  20. package/dist/comments.layered.css +14 -4
  21. package/dist/contextmenu.css +24 -0
  22. package/dist/contextmenu.layered.css +24 -0
  23. package/dist/datatable.css +106 -77
  24. package/dist/datatable.layered.css +106 -77
  25. package/dist/esm/components/contextmenu/contextmenu.js +2 -2
  26. package/dist/esm/components/contextmenu/contextmenu.recipe.js +24 -0
  27. package/dist/esm/components/datatable/datatable.js +593 -52
  28. package/dist/esm/components/datatable/datatable.recipe.js +68 -4
  29. package/dist/esm/components/dialog/dialog.js +65 -1
  30. package/dist/esm/components/tabletoolbar/tabletoolbar.chrome.js +37 -2
  31. package/dist/esm/components/tabletoolbar/tabletoolbar.js +219 -40
  32. package/dist/esm/components/tabletoolbar/tabletoolbar.recipe.js +50 -3
  33. package/dist/esm/components/text/text.recipe.js +17 -7
  34. package/dist/esm/product/board/board.js +63 -29
  35. package/dist/esm/product/board/board.recipe.js +42 -2
  36. package/dist/esm/product/comments/comments.js +12 -3
  37. package/dist/esm/product/comments/comments.recipe.js +14 -4
  38. package/dist/esm/product/recordstable/recordstable.js +2 -18
  39. package/dist/esm/product/savedviews/savedviews.js +57 -26
  40. package/dist/esm/product/savedviews/savedviews.recipe.js +27 -0
  41. package/dist/recordstable.css +33 -31
  42. package/dist/recordstable.layered.css +33 -31
  43. package/dist/savedviews.css +27 -0
  44. package/dist/savedviews.layered.css +27 -0
  45. package/dist/styles.css +282 -125
  46. package/dist/styles.layered.css +282 -125
  47. package/dist/tabletoolbar.css +45 -4
  48. package/dist/tabletoolbar.layered.css +45 -4
  49. package/dist/text.css +1 -1
  50. package/dist/text.layered.css +1 -1
  51. package/dist/types/components/contextmenu/contextmenu.d.ts +8 -1
  52. package/dist/types/components/datatable/datatable.d.ts +98 -2
  53. package/dist/types/components/tabletoolbar/tabletoolbar.d.ts +72 -2
  54. package/dist/types/components/text/text.recipe.d.ts +1 -1
  55. package/dist/types/product/board/board.d.ts +48 -2
  56. package/dist/types/product/comments/comments.d.ts +6 -1
  57. package/dist/types/product/recordstable/recordstable.d.ts +0 -1
  58. package/dist/types/product/savedviews/savedviews.d.ts +45 -8
  59. package/package.json +1 -1
@@ -22,6 +22,30 @@ const config = (0, core_1.defineRecipe)({
22
22
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
23
23
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
24
24
  }
25
+ /* crisp/#645: visually-hidden-but-focusable, not visually-hidden-and-
26
+ inert like .crisp-datatable-live above -- same clip-rect shape,
27
+ this one just needs to actually hold DOM focus so a native
28
+ copy/paste event has something to fire on (see its own comment
29
+ in datatable.tsx). resize: none keeps a stray native textarea handle
30
+ from ever being drawable, even though it's clipped to 1px anyway. */
31
+ & .crisp-datatable-clipboard-proxy {
32
+ position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
33
+ overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
34
+ resize: none;
35
+ }
36
+ /* single active cell (crisp/#645 follow-up -- this had no styling of its
37
+ own at all before, a gap against Golden Rule 0: RecordsTable's own
38
+ measured value, .crisp-recordstable-td[data-selected], brought
39
+ across unchanged now that a visible anchor actually matters for the
40
+ Shift+Click range below). */
41
+ & td[data-selected] { outline: 2px solid ${tokens_1.contract.bgBrandSolid}; outline-offset: -2px; border-radius: ${tokens_1.contract.radius.sm}; z-index: 3; }
42
+ /* crisp/#645, G-101: a Shift+Click range -- reuses the row-selection
43
+ tint formula already used twice below (tbody tr[data-selected] td)
44
+ for the same reason a selected group of rows gets one: a selected
45
+ GROUP of cells is the same kind of state, just cell-scoped instead of
46
+ row-scoped. Not independently re-measured against Attio's own range
47
+ highlight (out of scope this slice -- flagged on crisp#645). */
48
+ & td[data-in-range] { background: color-mix(in srgb, ${tokens_1.contract.bgBrandSolid} 10%, ${tokens_1.contract.bgSurface}); }
25
49
  /* The header cell has no padding of its own -- the button owns the
26
50
  full cell so the whole header is clickable (its own inset is 12px).
27
51
  Mirrors the summary-row fix.
@@ -114,6 +138,12 @@ const config = (0, core_1.defineRecipe)({
114
138
  & tbody tr[data-selected] td { background: color-mix(in srgb, ${tokens_1.contract.bgBrandSolid} 10%, ${tokens_1.contract.bgSurface}); }
115
139
  & tbody tr[data-selected]:hover td { background: color-mix(in srgb, ${tokens_1.contract.fgPrimary} 4%, color-mix(in srgb, ${tokens_1.contract.bgBrandSolid} 10%, ${tokens_1.contract.bgSurface})); }
116
140
  & .crisp-datatable-editable:hover { background: color-mix(in srgb, ${tokens_1.contract.fgPrimary} 4%, ${tokens_1.contract.bgSurface}); cursor: text; }
141
+ /* crisp/#758: AI-enriched column marker, ported from RecordsTable's own
142
+ already-measured value -- a full-bleed #9b69ff (bgPurple) wash at 4%,
143
+ composited OVER whatever row-state background the cell already has
144
+ (hover, selected), which is why it's an ::after overlay and not a
145
+ plain background. */
146
+ & td[data-enriched]::after { content: ""; position: absolute; inset: 0; pointer-events: none; background: color-mix(in srgb, ${tokens_1.contract.bgPurple} 4%, transparent); }
117
147
  & .crisp-datatable-edit { width: 100%; height: 28px; margin: 0; padding: 0 ${tokens_1.contract.space["1.5"]}; border: none; border-radius: ${tokens_1.contract.radius.sm}; background: ${tokens_1.contract.bgSurface}; box-shadow: inset 0 0 0 1px ${tokens_1.contract.bgBrandSolid}; font-family: inherit; font-size: ${tokens_1.contract.text.lg}; color: ${tokens_1.contract.fgPrimary}; outline: none; vertical-align: middle; }
118
148
  & .crisp-datatable-edit:focus { box-shadow: inset 0 0 0 1px ${tokens_1.contract.bgBrandSolid}; }
119
149
  /* An async onEdit's in-flight state (#385) -- the field/picker dims and
@@ -180,11 +210,44 @@ const config = (0, core_1.defineRecipe)({
180
210
  & .crisp-datatable-resize { position: absolute; top: 0; inset-inline-end: -3px; width: 9px; height: 100%; cursor: col-resize; z-index: 6; touch-action: none; }
181
211
  & .crisp-datatable-resize::after { content: ""; position: absolute; top: 0; inset-inline-start: 3px; width: 2px; height: 100%; background: ${tokens_1.contract.bgBrandSolid}; opacity: 0; transition: opacity .12s; }
182
212
  & .crisp-datatable-resize:hover::after, & .crisp-datatable-resize[data-resizing]::after { opacity: 1; }
213
+ /* crisp/#731 -- column drag-to-reorder. Measured live against Attio's
214
+ own grid (the issue's own comment): the dragged header keeps full
215
+ opacity and the plain \`pointer\` cursor throughout (Attio draws no
216
+ ghost and no "grabbing" cursor of its own), so this adds nothing to
217
+ \`[data-dragging]\` itself -- the ONLY new paint is the drop-indicator
218
+ line, a 3px bar in the same measured brand blue the resize handle's
219
+ own hover state (above) already uses, positioned on whichever edge of
220
+ the hovered header the pointer is closer to. */
221
+ & th[data-dragging] { cursor: pointer; }
222
+ & th[data-drop-before]::before, & th[data-drop-after]::after {
223
+ content: ""; position: absolute; top: 0; width: 3px; height: 100%;
224
+ background: ${tokens_1.contract.bgBrandSolid}; z-index: 6; pointer-events: none;
225
+ }
226
+ & th[data-drop-before]::before { inset-inline-start: -1.5px; }
227
+ & th[data-drop-after]::after { inset-inline-end: -1.5px; }
183
228
  /* typed cells — entity (avatar + name), colour-coded tags, numbers, dates */
184
229
  & .crisp-datatable-entity { display: flex; align-items: center; gap: ${tokens_1.contract.space["1.5"]}; min-width: 0; color: ${tokens_1.contract.fgPrimary}; font-weight: ${tokens_1.contract.weight.medium}; }
185
- & .crisp-datatable-entity-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
186
- & .crisp-datatable-entity:hover .crisp-datatable-entity-name { text-decoration: underline; }
230
+ /* crisp/#601, G-050: measured LIVE (getComputedStyle on the name text,
231
+ plain hover vs rest) that Attio's underline is unconditional, not a
232
+ hover reveal -- identical at rest and on hover: text-decoration-color
233
+ is rgba(0,0,0,0.09), a 9% tint, not the name's own full-contrast text
234
+ colour. The previous :hover-gated, full-contrast rule here was itself
235
+ unmeasured and wrong in two ways at once. */
236
+ & .crisp-datatable-entity-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-decoration: underline; text-decoration-color: color-mix(in srgb, ${tokens_1.contract.fgPrimary} 9%, transparent); }
187
237
  & button.crisp-datatable-entity-name--link { margin: 0; padding: 0; border: none; background: none; font: inherit; color: inherit; letter-spacing: inherit; text-align: start; cursor: pointer; max-width: 100%; }
238
+ /* crisp/#601, G-048: two 20px ghost icons, right-aligned in the primary
239
+ column, revealed on ROW hover (the issue's own title says "row
240
+ hover", and a live check confirmed hovering well past the name --
241
+ not just the icons' own hit target -- still reveals them) or
242
+ keyboard focus. Measured live: border-radius 6px, icon colour
243
+ rgba(0,0,0,0.55) at rest, darkening to the full text colour with a
244
+ rgba(0,0,0,0.04) background on the icon's OWN hover -- both
245
+ expressed as color-mix against fgPrimary so they stay correct in
246
+ dark mode instead of a literal black that would invert. */
247
+ & .crisp-datatable-entity-actions { display: flex; align-items: center; gap: ${tokens_1.contract.space["1.5"]}; margin-inline-start: auto; flex-shrink: 0; opacity: 0; }
248
+ & tr:hover .crisp-datatable-entity-actions, & .crisp-datatable-entity-actions:focus-within { opacity: 1; }
249
+ & .crisp-datatable-entity-action { display: inline-flex; align-items: center; justify-content: center; width: 20px; height: 20px; margin: 0; padding: 0; border: none; border-radius: ${tokens_1.contract.radius.sm}; background: none; font-family: inherit; color: color-mix(in srgb, ${tokens_1.contract.fgPrimary} 55%, transparent); cursor: pointer; flex-shrink: 0; }
250
+ & .crisp-datatable-entity-action:hover { background: color-mix(in srgb, ${tokens_1.contract.fgPrimary} 4%, transparent); color: ${tokens_1.contract.fgPrimary}; }
188
251
  & .crisp-datatable-avatar { width: ${tokens_1.contract.size.icon}; height: ${tokens_1.contract.size.icon}; border-radius: ${tokens_1.contract.radius.sm}; flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center; font-size: 10px; line-height: 1; font-weight: ${tokens_1.contract.weight.semibold}; text-transform: uppercase; color: ${tokens_1.contract.fgOnBrand}; background: ${tokens_1.contract.bgBrandSolid}; }
189
252
  & .crisp-datatable-avatar[data-tone="success"] { background: ${tokens_1.contract.bgSuccess}; } & .crisp-datatable-avatar[data-tone="warning"] { background: ${tokens_1.contract.bgWarning}; } & .crisp-datatable-avatar[data-tone="danger"] { background: ${tokens_1.contract.bgDanger}; } & .crisp-datatable-avatar[data-tone="purple"] { background: ${tokens_1.contract.bgPurple}; } & .crisp-datatable-avatar[data-tone="cyan"] { background: ${tokens_1.contract.bgCyan}; } & .crisp-datatable-avatar[data-tone="orange"] { background: ${tokens_1.contract.bgOrange}; }
190
253
  & .crisp-datatable-avatar img { width: 100%; height: 100%; border-radius: inherit; object-fit: cover; }
@@ -216,7 +279,8 @@ const config = (0, core_1.defineRecipe)({
216
279
  & .crisp-datatable-col-grip { width: 20px; padding: 0; text-align: center; }
217
280
  & .crisp-datatable-grip { display: inline-flex; align-items: center; justify-content: center; width: 100%; height: 100%; margin: 0; border: none; border-radius: 0; background: none; font-family: inherit; color: ${tokens_1.contract.fgMuted}; cursor: grab; }
218
281
  & .crisp-datatable-grip:active { cursor: grabbing; }
219
- & tbody tr[data-drop-target] td, & tbody tr[data-drop-target] th { box-shadow: inset 0 2px 0 0 ${tokens_1.contract.bgBrandSolid}; transition: box-shadow .12s; }`,
282
+ & tbody tr[data-drop-target] td, & tbody tr[data-drop-target] th { box-shadow: inset 0 2px 0 0 ${tokens_1.contract.bgBrandSolid}; transition: box-shadow .12s; }
283
+ ${core_1.rtlFlipCss}`,
220
284
  variants: {},
221
285
  defaultVariants: {},
222
286
  compoundVariants: [],
@@ -63,10 +63,74 @@ function useDialogInstanceId() {
63
63
  function Dialog({ open, onOpenChange, title, icon, width = 640, variant = "panel", description, "aria-label": ariaLabel, closeLabel: closeLabelProp, children, className, style, "aria-describedby": describedBy, ...rest }) {
64
64
  const closeLabel = (0, core_1.useCrispMessage)(closeLabelProp, "Close");
65
65
  const id = useDialogInstanceId();
66
+ const isAlert = variant === "alert";
66
67
  const service = (0, react_1.useMachine)(dialog.machine, {
67
68
  id,
68
69
  open,
69
- role: variant === "alert" ? "alertdialog" : "dialog",
70
+ role: isAlert ? "alertdialog" : "dialog",
71
+ // #704: a stacked (second) Dialog can get marked `aria-hidden="true"`
72
+ // on its own outer wrapper a frame after it opens. Traced into
73
+ // `@zag-js/dialog`'s `hideContentBelow` effect and `@zag-js/aria-hidden`'s
74
+ // own source: a modal Dialog's `hideContentBelow` calls
75
+ // `hideOthers([itself])` (deferred one `requestAnimationFrame`), which
76
+ // walks `document.body`'s children and `aria-hidden`s every one that
77
+ // ISN'T an ancestor of its own content. Each open `Dialog` schedules
78
+ // this independently, with no shared registry of who else is open —
79
+ // when the effect re-runs for the FIRST (panel) Dialog (Zag re-runs an
80
+ // effect whenever any prop it reads is considered changed, which a
81
+ // parent re-render can trigger even when the boolean VALUE didn't),
82
+ // its walk runs again with the SECOND (alert) Dialog now mounted as a
83
+ // sibling body child it has never seen before, and marks that whole
84
+ // sibling subtree — including the alert's own wrapper, the thing
85
+ // actually on top and interactive — as hidden. A real upstream
86
+ // limitation (no per-instance awareness of other open Dialogs), not
87
+ // something crisp can fix by calling the library differently.
88
+ //
89
+ // `hideContentBelow` is gated purely on `modal` (no separate flag), so
90
+ // `modal: false` for the alert variant skips it — `trapFocus`/
91
+ // `preventScroll` are passed explicitly right below so they stay on
92
+ // regardless (their own defaults are ALSO `= modal`, so leaving them
93
+ // out here would silently turn them off too).
94
+ //
95
+ // A DOM-level fix was tried first — correcting the wrongly-set
96
+ // attribute after the fact instead of touching `modal` at all, so
97
+ // every other zag-managed behavior would stay untouched by
98
+ // construction. It broke `formdialog.test.tsx`'s own
99
+ // "the confirm's own Cancel keeps the dirtied form open" test (a real
100
+ // regression, reproduced with the correction reduced to literally one
101
+ // `removeAttribute` call, and reproduced again unmodified on the prior
102
+ // WIP commit — that version was never actually run against the full
103
+ // suite): `waitFor(() => expect(queryByRole("alertdialog")).toBeNull())`
104
+ // hung for the full test timeout rather than the confirm's real,
105
+ // sub-100ms unmount, in a way that traces to `aria-hidden` specifically
106
+ // (swapping the correction for a harmless, differently-named attribute
107
+ // did not reproduce it) — a fragile interaction this DOM-mutation
108
+ // approach could not be made to reliably avoid.
109
+ //
110
+ // `modal: false` also turns off `@zag-js/dismissable`'s own
111
+ // `pointerBlocking` for the alert. Verified live (a real
112
+ // headed-then-headless Chromium session against a built `FormDialog`,
113
+ // not just reasoning from the source) that this does NOT regress
114
+ // outside-click safety in practice — but for a DIFFERENT reason than
115
+ // `pointerBlocking` itself: the thing actually keeping a background
116
+ // click from reaching anything is `@zag-js/remove-scroll`'s
117
+ // `preventBodyScroll`, gated on `preventScroll` (kept `true` above,
118
+ // independent of `modal`) — it sets `pointer-events: none` directly on
119
+ // `<body>` while any modal-ish Dialog is open, and `getContentProps()`
120
+ // re-enables `pointer-events: auto` only on each Dialog's OWN content
121
+ // element. Confirmed live: the alert's content had an explicit
122
+ // `pointer-events: auto`; its OWN backdrop/positioner did not, and
123
+ // inherited body's `none`; clicking anywhere in that dead zone (the
124
+ // alert's backdrop, or the panel's, now both non-interactive) dismissed
125
+ // neither dialog, because the click lands on nothing that listens for
126
+ // it — a stronger guarantee than "click closes the wrong layer", not
127
+ // just an absence of that one failure mode. `pointerBlocking` is a
128
+ // second, redundant layer over the same problem `preventScroll` already
129
+ // solves here; the alert's own `closeOnInteractOutside` is already
130
+ // `false` for any `alertdialog` role regardless of `modal`.
131
+ modal: !isAlert,
132
+ trapFocus: true,
133
+ preventScroll: true,
70
134
  onOpenChange(details) {
71
135
  onOpenChange(details.open);
72
136
  },
@@ -34,6 +34,19 @@ const tokens_1 = require("../../tokens");
34
34
  // content width: a floor sized for the text set overshoots the other two.
35
35
  // Dropped to `min-width: 0` so every op set sizes to its own content, the
36
36
  // same as any other menu here without an explicit floor.
37
+ //
38
+ // `.menu-item` also picked up `text-decoration: none` for #595's Import/
39
+ // Export menu — Attio's own "Import CSV" and "Learn about imports and
40
+ // exports" rows are real `<a>` tags (the middle two, pure JS actions, are
41
+ // plain `<div role="menuitem">`), and an anchor needs the underline reset
42
+ // every button-based `.menu-item` never needed. No-op for every existing
43
+ // button consumer of this class. Same menu's 2 dividers use `.menu-sep` on
44
+ // a real `<hr>` (implicit `role="separator"`, no extra ARIA plumbing needed)
45
+ // rather than a `<div role="separator">` — biome's a11y rules want a
46
+ // separator either genuinely interactive (`tabIndex`, `aria-valuenow`, a
47
+ // resizable-splitter kind of thing this isn't) or the semantic element
48
+ // instead; `.menu-sep` picked up `border: none` for it, a no-op for the
49
+ // existing plain-`<div>` consumers of this class.
37
50
  const toolbarChromeHead = (p) => ` & .${namespace_1.CLASS_PREFIX}${p}-toolbar { display: flex; align-items: center; gap: ${tokens_1.contract.space["2"]}; padding: 0 ${tokens_1.contract.space["3"]} ${tokens_1.contract.space["2"]}; }
38
51
  & .${namespace_1.CLASS_PREFIX}${p}-toolbar[data-inline] { padding-bottom: 0; }
39
52
  & .${namespace_1.CLASS_PREFIX}${p}-search { display: flex; align-items: center; gap: ${tokens_1.contract.space["1.5"]}; height: ${tokens_1.contract.size.row}; padding: 0 ${tokens_1.contract.space["2.5"]}; border: none; border-radius: ${tokens_1.contract.radius.md}; background: ${tokens_1.contract.bgSurface}; box-shadow: inset 0 0 0 1px ${tokens_1.contract.borderSubtle}; color: ${tokens_1.contract.fgMuted}; }
@@ -73,7 +86,7 @@ const toolbarChromeBody = (p) => ` & .${namespace_1.CLASS_PREFIX}${p}-menu-he
73
86
  & .${namespace_1.CLASS_PREFIX}${p}-sort-learn { display: inline-flex; align-items: center; gap: ${tokens_1.contract.space["1.5"]}; font-size: ${tokens_1.contract.text.lg}; color: color-mix(in srgb, ${tokens_1.contract.fgPrimary} 63%, transparent); text-decoration: none; }
74
87
  & .${namespace_1.CLASS_PREFIX}${p}-sort-learn svg { flex-shrink: 0; }
75
88
  & .${namespace_1.CLASS_PREFIX}${p}-sort-learn:hover { color: ${tokens_1.contract.fgPrimary}; }
76
- & .${namespace_1.CLASS_PREFIX}${p}-menu-item { display: flex; align-items: center; gap: ${tokens_1.contract.space["1.5"]}; width: 100%; height: ${tokens_1.contract.size.menuRow}; margin: 0; padding: 0 ${tokens_1.contract.space["2"]}; border: none; background: none; border-radius: ${tokens_1.contract.radius.md}; font-family: inherit; font-size: ${tokens_1.contract.text.lg}; line-height: 20px; font-weight: ${tokens_1.contract.weight.medium}; color: ${tokens_1.contract.fgPrimary}; text-align: left; cursor: pointer; }
89
+ & .${namespace_1.CLASS_PREFIX}${p}-menu-item { display: flex; align-items: center; gap: ${tokens_1.contract.space["1.5"]}; width: 100%; height: ${tokens_1.contract.size.menuRow}; margin: 0; padding: 0 ${tokens_1.contract.space["2"]}; border: none; background: none; border-radius: ${tokens_1.contract.radius.md}; font-family: inherit; font-size: ${tokens_1.contract.text.lg}; line-height: 20px; font-weight: ${tokens_1.contract.weight.medium}; color: ${tokens_1.contract.fgPrimary}; text-align: left; text-decoration: none; cursor: pointer; }
77
90
  & .${namespace_1.CLASS_PREFIX}${p}-menu-item:hover { background: ${tokens_1.contract.bgRaised}; }
78
91
  & .${namespace_1.CLASS_PREFIX}${p}-menu-item:disabled { opacity: 0.4; cursor: default; }
79
92
  & .${namespace_1.CLASS_PREFIX}${p}-menu-item:disabled:hover { background: none; }
@@ -81,7 +94,29 @@ const toolbarChromeBody = (p) => ` & .${namespace_1.CLASS_PREFIX}${p}-menu-he
81
94
  & .${namespace_1.CLASS_PREFIX}${p}-menu-item[data-on] svg { color: ${tokens_1.contract.bgBrandSolid}; }
82
95
  & .${namespace_1.CLASS_PREFIX}${p}-menu-item .${namespace_1.CLASS_PREFIX}${p}-menu-check { margin-left: auto; }
83
96
  & .${namespace_1.CLASS_PREFIX}${p}-menu-input { height: ${tokens_1.contract.size.row}; margin: ${tokens_1.contract.space["0.5"]}; padding: 0 ${tokens_1.contract.space["2"]}; border: none; border-radius: ${tokens_1.contract.radius.md}; box-shadow: inset 0 0 0 1px ${tokens_1.contract.borderSubtle}; background: ${tokens_1.contract.bgSurface}; font-family: inherit; font-size: ${tokens_1.contract.text.lg}; color: ${tokens_1.contract.fgPrimary}; outline: none; }
84
- & .${namespace_1.CLASS_PREFIX}${p}-menu-sep { height: 1px; background: ${tokens_1.contract.borderSubtle}; margin: ${tokens_1.contract.space["0.5"]} 0; }
97
+ & .${namespace_1.CLASS_PREFIX}${p}-menu-sep { height: 1px; border: none; background: ${tokens_1.contract.borderSubtle}; margin: ${tokens_1.contract.space["0.5"]} 0; }
98
+ /* #592: the Filter field picker's nested-scope header (back control +
99
+ search, once drilled into a nestedColumns column) and a search
100
+ result's own breadcrumb row ("Team › Twitter follower count",
101
+ surfaced by the SAME query matching a nested column). menu-back
102
+ reuses the established 28px icon-only control pattern this file
103
+ already uses twice (sort-remove, the kebab segment) rather than
104
+ an independent measurement of Attio's own back arrow. The breadcrumb
105
+ colours ARE measured live: the parent segment muted at the same
106
+ exact color-mix 63% sort-learn above already uses, the
107
+ separator chevron and leaf segment both full fgPrimary (the
108
+ generic menu-item svg rule below defaults every icon to
109
+ fgMuted, so the separator needs its own override to read
110
+ correctly here). */
111
+ & .${namespace_1.CLASS_PREFIX}${p}-menu-nesthead { display: flex; align-items: center; gap: ${tokens_1.contract.space["1"]}; margin: ${tokens_1.contract.space["0.5"]}; }
112
+ & .${namespace_1.CLASS_PREFIX}${p}-menu-nesthead .${namespace_1.CLASS_PREFIX}${p}-menu-input { flex: 1; margin: 0; }
113
+ & .${namespace_1.CLASS_PREFIX}${p}-menu-back { display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 28px; flex-shrink: 0; padding: 0; border: none; border-radius: ${tokens_1.contract.radius.md}; background: none; color: ${tokens_1.contract.fgMuted}; cursor: pointer; }
114
+ & .${namespace_1.CLASS_PREFIX}${p}-menu-back:hover { background: ${tokens_1.contract.bgRaised}; color: ${tokens_1.contract.fgPrimary}; }
115
+ & .${namespace_1.CLASS_PREFIX}${p}-menu-item--expand { justify-content: space-between; }
116
+ & .${namespace_1.CLASS_PREFIX}${p}-menu-item-path { display: inline-flex; align-items: center; gap: 2px; min-width: 0; overflow: hidden; }
117
+ & .${namespace_1.CLASS_PREFIX}${p}-menu-item-path-parent { flex-shrink: 0; overflow: hidden; text-overflow: ellipsis; max-width: 100px; color: color-mix(in srgb, ${tokens_1.contract.fgPrimary} 63%, transparent); }
118
+ & .${namespace_1.CLASS_PREFIX}${p}-menu-item-path-sep { flex-shrink: 0; color: ${tokens_1.contract.fgPrimary}; }
119
+ & .${namespace_1.CLASS_PREFIX}${p}-menu-item-path > span:last-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
85
120
  & .${namespace_1.CLASS_PREFIX}${p}-menu--up { top: auto; bottom: calc(100% + ${tokens_1.contract.space["1"]}); transform-origin: bottom; }
86
121
  & .${namespace_1.CLASS_PREFIX}${p}-menu--right { left: auto; right: 0; }
87
122
  & .${namespace_1.CLASS_PREFIX}${p}-menu--fixed { position: fixed; top: auto; left: auto; z-index: 40; }