@crisp-ui-kit/crisp 0.54.7 → 0.54.9
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/authshell.css +5 -1
- package/dist/authshell.layered.css +5 -1
- package/dist/badge.css +5 -5
- package/dist/badge.layered.css +5 -5
- package/dist/board.css +36 -8
- package/dist/board.layered.css +36 -8
- package/dist/button.css +3 -1
- package/dist/button.layered.css +3 -1
- package/dist/cjs/components/badge/badge.recipe.js +28 -5
- package/dist/cjs/components/button/button.js +33 -2
- package/dist/cjs/components/button/button.recipe.js +31 -5
- package/dist/cjs/components/contextmenu/contextmenu.js +2 -2
- package/dist/cjs/components/contextmenu/contextmenu.recipe.js +24 -0
- package/dist/cjs/components/datatable/datatable.js +590 -41
- package/dist/cjs/components/datatable/datatable.recipe.js +118 -5
- package/dist/cjs/components/dialog/dialog.js +72 -5
- package/dist/cjs/components/dialog/dialog.recipe.js +15 -1
- package/dist/cjs/components/tabletoolbar/tabletoolbar.chrome.js +49 -5
- package/dist/cjs/components/tabletoolbar/tabletoolbar.js +365 -49
- package/dist/cjs/components/tabletoolbar/tabletoolbar.recipe.js +78 -0
- package/dist/cjs/components/tabs/tabs.recipe.js +11 -2
- package/dist/cjs/components/tooltip/tooltip.js +23 -9
- package/dist/cjs/components/tooltip/tooltip.recipe.js +10 -9
- package/dist/cjs/product/authshell/authshell.js +14 -1
- package/dist/cjs/product/authshell/authshell.recipe.js +13 -1
- package/dist/cjs/product/board/board.js +175 -46
- package/dist/cjs/product/board/board.recipe.js +56 -4
- package/dist/cjs/product/comments/comments.js +34 -12
- package/dist/cjs/product/comments/comments.recipe.js +31 -7
- package/dist/cjs/product/formdialog/formdialog.js +10 -4
- package/dist/cjs/product/navitem/navitem.js +7 -2
- package/dist/cjs/product/navitem/navitem.recipe.js +16 -0
- package/dist/cjs/product/objectsettings/index.js +1 -2
- package/dist/cjs/product/objectsettings/objectsettings.js +3 -13
- package/dist/cjs/product/recordstable/recordstable.js +1 -17
- package/dist/cjs/product/richtext/richtext.js +76 -1
- package/dist/cjs/product/savedviews/savedviews.js +55 -24
- package/dist/cjs/product/savedviews/savedviews.recipe.js +27 -0
- package/dist/cjs/product/sidebar/sidebar.js +68 -15
- package/dist/cjs/product/sidebar/sidebar.recipe.js +6 -1
- package/dist/cjs/tokens/palette.js +32 -1
- package/dist/cjs/tokens/semantic.js +11 -0
- package/dist/comments.css +50 -14
- package/dist/comments.layered.css +50 -14
- package/dist/contextmenu.css +24 -0
- package/dist/contextmenu.layered.css +24 -0
- package/dist/datatable.css +101 -37
- package/dist/datatable.layered.css +101 -37
- package/dist/dialog.css +13 -1
- package/dist/dialog.layered.css +13 -1
- package/dist/esm/components/badge/badge.recipe.js +28 -5
- package/dist/esm/components/button/button.js +34 -3
- package/dist/esm/components/button/button.recipe.js +31 -5
- package/dist/esm/components/contextmenu/contextmenu.js +2 -2
- package/dist/esm/components/contextmenu/contextmenu.recipe.js +24 -0
- package/dist/esm/components/datatable/datatable.js +590 -42
- package/dist/esm/components/datatable/datatable.recipe.js +119 -6
- package/dist/esm/components/dialog/dialog.js +72 -5
- package/dist/esm/components/dialog/dialog.recipe.js +15 -1
- package/dist/esm/components/tabletoolbar/tabletoolbar.chrome.js +49 -5
- package/dist/esm/components/tabletoolbar/tabletoolbar.js +366 -50
- package/dist/esm/components/tabletoolbar/tabletoolbar.recipe.js +80 -2
- package/dist/esm/components/tabs/tabs.recipe.js +11 -2
- package/dist/esm/components/tooltip/tooltip.js +23 -9
- package/dist/esm/components/tooltip/tooltip.recipe.js +10 -9
- package/dist/esm/product/authshell/authshell.js +15 -2
- package/dist/esm/product/authshell/authshell.recipe.js +13 -1
- package/dist/esm/product/authshell/index.js +1 -1
- package/dist/esm/product/board/board.js +176 -47
- package/dist/esm/product/board/board.recipe.js +56 -4
- package/dist/esm/product/comments/comments.js +35 -13
- package/dist/esm/product/comments/comments.recipe.js +31 -7
- package/dist/esm/product/formdialog/formdialog.js +10 -4
- package/dist/esm/product/navitem/index.js +1 -1
- package/dist/esm/product/navitem/navitem.js +7 -2
- package/dist/esm/product/navitem/navitem.recipe.js +16 -0
- package/dist/esm/product/objectsettings/index.js +1 -1
- package/dist/esm/product/objectsettings/objectsettings.js +2 -12
- package/dist/esm/product/recordstable/recordstable.js +2 -18
- package/dist/esm/product/richtext/richtext.js +76 -1
- package/dist/esm/product/savedviews/savedviews.js +57 -26
- package/dist/esm/product/savedviews/savedviews.recipe.js +27 -0
- package/dist/esm/product/sidebar/sidebar.js +69 -16
- package/dist/esm/product/sidebar/sidebar.recipe.js +6 -1
- package/dist/esm/tokens/palette.js +32 -1
- package/dist/esm/tokens/semantic.js +11 -0
- package/dist/native/tokens.d.ts +1 -1
- package/dist/native/tokens.js +16 -2
- package/dist/navitem.css +7 -0
- package/dist/navitem.layered.css +7 -0
- package/dist/recordstable.css +34 -32
- package/dist/recordstable.layered.css +34 -32
- package/dist/savedviews.css +27 -0
- package/dist/savedviews.layered.css +27 -0
- package/dist/sidebar.css +6 -1
- package/dist/sidebar.layered.css +6 -1
- package/dist/styles.css +369 -106
- package/dist/styles.layered.css +369 -106
- package/dist/tabletoolbar.css +47 -3
- package/dist/tabletoolbar.layered.css +47 -3
- package/dist/tabs.css +1 -1
- package/dist/tabs.layered.css +1 -1
- package/dist/tokens.css +9 -1
- package/dist/tokens.json +16 -2
- package/dist/tooltip.css +1 -1
- package/dist/tooltip.layered.css +1 -1
- package/dist/types/components/badge/badge.recipe.d.ts +5 -1
- package/dist/types/components/button/button.recipe.d.ts +18 -5
- package/dist/types/components/contextmenu/contextmenu.d.ts +8 -1
- package/dist/types/components/datatable/datatable.d.ts +98 -1
- package/dist/types/components/dialog/dialog.d.ts +25 -2
- package/dist/types/components/tabletoolbar/tabletoolbar.d.ts +113 -3
- package/dist/types/components/tooltip/tooltip.d.ts +12 -4
- package/dist/types/components/tooltip/tooltip.recipe.d.ts +1 -1
- package/dist/types/product/authshell/authshell.d.ts +26 -2
- package/dist/types/product/authshell/index.d.ts +1 -1
- package/dist/types/product/board/board.d.ts +53 -6
- package/dist/types/product/comments/comments.d.ts +16 -3
- package/dist/types/product/formdialog/formdialog.d.ts +2 -1
- package/dist/types/product/navitem/index.d.ts +1 -1
- package/dist/types/product/navitem/navitem.d.ts +23 -0
- package/dist/types/product/objectsettings/index.d.ts +1 -1
- package/dist/types/product/objectsettings/objectsettings.d.ts +0 -10
- package/dist/types/product/recordstable/recordstable.d.ts +0 -1
- package/dist/types/product/savedviews/savedviews.d.ts +45 -8
- package/dist/types/product/sidebar/sidebar.d.ts +33 -5
- package/dist/types/tokens/contract.d.ts +4 -0
- package/dist/types/tokens/palette.d.ts +13 -1
- package/dist/types/tokens/semantic.d.ts +11 -0
- package/package.json +1 -1
|
@@ -22,10 +22,46 @@ 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
|
-
Mirrors the summary-row fix.
|
|
28
|
-
|
|
51
|
+
Mirrors the summary-row fix.
|
|
52
|
+
\`& .crisp-table th\`, not bare \`& th\` (found while re-checking #691's
|
|
53
|
+
own fix live): DataTable's root nests \`Table\`'s own wrapper, whose
|
|
54
|
+
\`table.recipe.ts\` carries \`& th, & td { padding: 0 12px }\` at the
|
|
55
|
+
SAME specificity (.crisp-table th vs .crisp-datatable th, both one
|
|
56
|
+
class + one element) -- a bare \`& th\` here only won by source
|
|
57
|
+
order, which flipped the moment table.recipe.ts's own CSS file
|
|
58
|
+
happened to land after datatable's in the build (alphabetical:
|
|
59
|
+
datatable < table), silently reinstating 12px on every header cell
|
|
60
|
+
in the whole product and making every interactive column's button
|
|
61
|
+
double it to 24px. Scoping through \`.crisp-table\` adds a second
|
|
62
|
+
class to the selector (0,2,1 beats 0,1,1) so this can never lose a
|
|
63
|
+
specificity tie to source order again. */
|
|
64
|
+
& .crisp-table th { padding: 0; }
|
|
29
65
|
/* Same wrapper trap as AUTHORING.md Lesson 40 (ColumnCalc's summary trigger):
|
|
30
66
|
Menu wraps its trigger in a flex div that sizes to its content, so a
|
|
31
67
|
long header grew past the column instead of filling it -- with nothing
|
|
@@ -36,6 +72,20 @@ const config = (0, core_1.defineRecipe)({
|
|
|
36
72
|
& th .crisp-menu { width: 100%; height: 100%; }
|
|
37
73
|
& .crisp-datatable-th-inner { display: inline-flex; align-items: center; gap: ${tokens_1.contract.space["1.5"]}; width: 100%; min-width: 0; height: 100%; }
|
|
38
74
|
& .crisp-datatable-th-inner > svg { flex-shrink: 0; color: ${tokens_1.contract.fgMuted}; }
|
|
75
|
+
/* crisp/#605 follow-up: a header with neither a Menu nor a sort button
|
|
76
|
+
(the primary/entity column's now-inert path, or any column with no
|
|
77
|
+
columnMenu/sortable at all) renders .crisp-datatable-th-inner as a
|
|
78
|
+
DIRECT child of th instead of nested inside a button -- and loses
|
|
79
|
+
the button's own 12px inset, drifting left of the td's 12px base
|
|
80
|
+
inset. Measured live on Attio's own Companies grid: the header label
|
|
81
|
+
and the cell's own text land at the exact same x (335px from the
|
|
82
|
+
viewport, both 40px from the column's left edge) via two different
|
|
83
|
+
padding+icon+gap combinations -- Attio does not leave the inert
|
|
84
|
+
primary header flush left either. The \`>\` combinator targets only
|
|
85
|
+
the button-less case: Menu's own wrapper (\`.crisp-menu\`) and the
|
|
86
|
+
plain sort \`button\` both put >=1 more level between \`th\` and this
|
|
87
|
+
span, so their own 12px (already on the button) isn't doubled. */
|
|
88
|
+
& th > .crisp-datatable-th-inner { box-sizing: border-box; padding: 0 ${tokens_1.contract.space["3"]}; }
|
|
39
89
|
/* The summary cell has NO padding of its own -- the calculation trigger
|
|
40
90
|
owns the whole 246x37 area so a click anywhere in the cell opens the
|
|
41
91
|
menu. Zero the cell's inset and let ColumnCalc supply the 12px. */
|
|
@@ -119,17 +169,79 @@ const config = (0, core_1.defineRecipe)({
|
|
|
119
169
|
& .crisp-datatable-refoption-avatar { flex-shrink: 0; }
|
|
120
170
|
& .crisp-datatable-refoption--create { color: ${tokens_1.contract.fgMuted}; }
|
|
121
171
|
& .crisp-datatable-empty { padding: ${tokens_1.contract.space["8"]} ${tokens_1.contract.space["3"]}; text-align: center; color: ${tokens_1.contract.fgMuted}; font-size: ${tokens_1.contract.text.lg}; }
|
|
172
|
+
/* #579: click-to-expand overlay for a truncated cell -- a read-only,
|
|
173
|
+
full-value card, settled live as click (not Attio's usual
|
|
174
|
+
hover-expand) and measured position: absolute, unlike this file's
|
|
175
|
+
floating chrome (Menu/Select popovers) which portals and escapes the
|
|
176
|
+
scroll container. Deliberately NOT the edit-error pattern above
|
|
177
|
+
(grow the row, block-level, never clipped): a virtualized grid
|
|
178
|
+
assumes a fixed row height for its scroll math (owned by the grid
|
|
179
|
+
core, not this cell), so growing a row here would fight it. Anchored
|
|
180
|
+
to the SAME column width the cell itself renders at (measured:
|
|
181
|
+
Attio's own overlay came out only slightly wider than the
|
|
182
|
+
triggering cell's clientWidth -- the overshoot is this padding),
|
|
183
|
+
wrapping onto as many lines as the value needs; it CAN be clipped by
|
|
184
|
+
\`.crisp-table-scroll\`'s own \`overflow: auto\` if it grows past the
|
|
185
|
+
visible viewport, same as the edit-error comment above already
|
|
186
|
+
notes for any position:absolute escape -- unmeasured against Attio
|
|
187
|
+
for this exact case, flagged rather than guessed. */
|
|
188
|
+
& td[data-cell-expanded] { overflow: visible; z-index: 7; }
|
|
189
|
+
& .crisp-datatable-cell-overlay {
|
|
190
|
+
position: absolute; inset-inline-start: 0; top: 0; width: 100%; box-sizing: border-box;
|
|
191
|
+
padding: ${tokens_1.contract.space["2"]} ${tokens_1.contract.space["3"]}; background: ${tokens_1.contract.bgSurface};
|
|
192
|
+
border: 1px solid ${tokens_1.contract.bgBrandSolid}; border-radius: 4px;
|
|
193
|
+
font-family: ${tokens_1.contract.font.sans}; font-size: ${tokens_1.contract.text.lg}; line-height: 20px; font-weight: ${tokens_1.contract.weight.medium};
|
|
194
|
+
color: ${tokens_1.contract.fgPrimary}; white-space: normal; word-break: break-word; cursor: default;
|
|
195
|
+
}
|
|
122
196
|
/* fixed layout so column widths (and resize) are exact — like RecordsTable */
|
|
123
197
|
& table { table-layout: fixed; }
|
|
198
|
+
/* read-only/computed column marker (#596) -- sits just inside the
|
|
199
|
+
resize handle, same absolute-positioned-against-the-th approach the
|
|
200
|
+
handle itself uses, since th has zero padding and the button/inner
|
|
201
|
+
row fills the whole cell with no room left for a trailing sibling. */
|
|
202
|
+
& .crisp-datatable-readonly { position: absolute; top: 50%; inset-inline-end: 11px; transform: translateY(-50%); color: ${tokens_1.contract.fgMuted}; pointer-events: none; z-index: 5; }
|
|
124
203
|
/* column resize grip */
|
|
125
204
|
& .crisp-datatable-resize { position: absolute; top: 0; inset-inline-end: -3px; width: 9px; height: 100%; cursor: col-resize; z-index: 6; touch-action: none; }
|
|
126
205
|
& .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; }
|
|
127
206
|
& .crisp-datatable-resize:hover::after, & .crisp-datatable-resize[data-resizing]::after { opacity: 1; }
|
|
207
|
+
/* crisp/#731 -- column drag-to-reorder. Measured live against Attio's
|
|
208
|
+
own grid (the issue's own comment): the dragged header keeps full
|
|
209
|
+
opacity and the plain \`pointer\` cursor throughout (Attio draws no
|
|
210
|
+
ghost and no "grabbing" cursor of its own), so this adds nothing to
|
|
211
|
+
\`[data-dragging]\` itself -- the ONLY new paint is the drop-indicator
|
|
212
|
+
line, a 3px bar in the same measured brand blue the resize handle's
|
|
213
|
+
own hover state (above) already uses, positioned on whichever edge of
|
|
214
|
+
the hovered header the pointer is closer to. */
|
|
215
|
+
& th[data-dragging] { cursor: pointer; }
|
|
216
|
+
& th[data-drop-before]::before, & th[data-drop-after]::after {
|
|
217
|
+
content: ""; position: absolute; top: 0; width: 3px; height: 100%;
|
|
218
|
+
background: ${tokens_1.contract.bgBrandSolid}; z-index: 6; pointer-events: none;
|
|
219
|
+
}
|
|
220
|
+
& th[data-drop-before]::before { inset-inline-start: -1.5px; }
|
|
221
|
+
& th[data-drop-after]::after { inset-inline-end: -1.5px; }
|
|
128
222
|
/* typed cells — entity (avatar + name), colour-coded tags, numbers, dates */
|
|
129
223
|
& .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}; }
|
|
130
|
-
|
|
131
|
-
|
|
224
|
+
/* crisp/#601, G-050: measured LIVE (getComputedStyle on the name text,
|
|
225
|
+
plain hover vs rest) that Attio's underline is unconditional, not a
|
|
226
|
+
hover reveal -- identical at rest and on hover: text-decoration-color
|
|
227
|
+
is rgba(0,0,0,0.09), a 9% tint, not the name's own full-contrast text
|
|
228
|
+
colour. The previous :hover-gated, full-contrast rule here was itself
|
|
229
|
+
unmeasured and wrong in two ways at once. */
|
|
230
|
+
& .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); }
|
|
132
231
|
& 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%; }
|
|
232
|
+
/* crisp/#601, G-048: two 20px ghost icons, right-aligned in the primary
|
|
233
|
+
column, revealed on ROW hover (the issue's own title says "row
|
|
234
|
+
hover", and a live check confirmed hovering well past the name --
|
|
235
|
+
not just the icons' own hit target -- still reveals them) or
|
|
236
|
+
keyboard focus. Measured live: border-radius 6px, icon colour
|
|
237
|
+
rgba(0,0,0,0.55) at rest, darkening to the full text colour with a
|
|
238
|
+
rgba(0,0,0,0.04) background on the icon's OWN hover -- both
|
|
239
|
+
expressed as color-mix against fgPrimary so they stay correct in
|
|
240
|
+
dark mode instead of a literal black that would invert. */
|
|
241
|
+
& .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; }
|
|
242
|
+
& tr:hover .crisp-datatable-entity-actions, & .crisp-datatable-entity-actions:focus-within { opacity: 1; }
|
|
243
|
+
& .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; }
|
|
244
|
+
& .crisp-datatable-entity-action:hover { background: color-mix(in srgb, ${tokens_1.contract.fgPrimary} 4%, transparent); color: ${tokens_1.contract.fgPrimary}; }
|
|
133
245
|
& .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}; }
|
|
134
246
|
& .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}; }
|
|
135
247
|
& .crisp-datatable-avatar img { width: 100%; height: 100%; border-radius: inherit; object-fit: cover; }
|
|
@@ -161,7 +273,8 @@ const config = (0, core_1.defineRecipe)({
|
|
|
161
273
|
& .crisp-datatable-col-grip { width: 20px; padding: 0; text-align: center; }
|
|
162
274
|
& .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; }
|
|
163
275
|
& .crisp-datatable-grip:active { cursor: grabbing; }
|
|
164
|
-
& 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; }
|
|
276
|
+
& 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; }
|
|
277
|
+
${core_1.rtlFlipCss}`,
|
|
165
278
|
variants: {},
|
|
166
279
|
defaultVariants: {},
|
|
167
280
|
compoundVariants: [],
|
|
@@ -60,12 +60,77 @@ function useDialogInstanceId() {
|
|
|
60
60
|
* Escape / outside-click dismissal) with the measured modal surface.
|
|
61
61
|
* Compose the body with `DialogBody` and actions with `DialogFooter`.
|
|
62
62
|
*/
|
|
63
|
-
function Dialog({ open, onOpenChange, title, icon, width = 640, "aria-label": ariaLabel, closeLabel: closeLabelProp, children, className, style, "aria-describedby": describedBy, ...rest }) {
|
|
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,
|
|
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,
|
|
69
134
|
onOpenChange(details) {
|
|
70
135
|
onOpenChange(details.open);
|
|
71
136
|
},
|
|
@@ -89,15 +154,17 @@ function Dialog({ open, onOpenChange, title, icon, width = 640, "aria-label": ar
|
|
|
89
154
|
const api = dialog.connect(service, react_1.normalizeProps);
|
|
90
155
|
if (!api.open)
|
|
91
156
|
return null;
|
|
92
|
-
return ((0, jsx_runtime_1.jsx)(react_1.Portal, { children: (0, jsx_runtime_1.jsxs)("div", { className: (0, dialog_recipe_1.dialogRecipe)(), children: [(0, jsx_runtime_1.jsx)("div", { ...api.getBackdropProps(), className: "crisp-dialog-backdrop" }), (0, jsx_runtime_1.jsx)("div", { ...api.getPositionerProps(), className: "crisp-dialog-positioner", children: (0, jsx_runtime_1.jsx)("div", { ...rest, ...(0, core_1.withDescribedBy)(api.getContentProps(), describedBy), className: (0, core_1.cx)("crisp-dialog-content", className), "data-crisp-dialog": id,
|
|
157
|
+
return ((0, jsx_runtime_1.jsx)(react_1.Portal, { children: (0, jsx_runtime_1.jsxs)("div", { className: (0, dialog_recipe_1.dialogRecipe)(), children: [(0, jsx_runtime_1.jsx)("div", { ...api.getBackdropProps(), className: "crisp-dialog-backdrop" }), (0, jsx_runtime_1.jsx)("div", { ...api.getPositionerProps(), className: "crisp-dialog-positioner", "data-variant": variant, children: (0, jsx_runtime_1.jsx)("div", { ...rest, ...(0, core_1.withDescribedBy)(api.getContentProps(), describedBy), className: (0, core_1.cx)("crisp-dialog-content", className), "data-crisp-dialog": id, "data-variant": variant,
|
|
93
158
|
// `width` is the component's own knob, so it wins over a passed
|
|
94
159
|
// maxWidth; everything else a consumer sets survives
|
|
95
|
-
style: { ...style, maxWidth: `min(${width}px, 90vw)` }, children: (0, jsx_runtime_1.jsxs)("div", { className: "crisp-dialog-card", children: [
|
|
160
|
+
style: { ...style, maxWidth: `min(${width}px, 90vw)` }, children: (0, jsx_runtime_1.jsxs)("div", { className: "crisp-dialog-card", children: [variant === "alert"
|
|
161
|
+
? (title != null || description != null) && ((0, jsx_runtime_1.jsxs)("div", { className: "crisp-dialog-alert-header", children: [title != null && ((0, jsx_runtime_1.jsx)("h2", { ...api.getTitleProps(), className: "crisp-dialog-alert-title", children: title })), description != null && ((0, jsx_runtime_1.jsx)("p", { ...api.getDescriptionProps(), className: "crisp-dialog-alert-description", children: description }))] }))
|
|
162
|
+
: title != null && ((0, jsx_runtime_1.jsxs)("div", { className: "crisp-dialog-header", children: [icon, (0, jsx_runtime_1.jsx)("h2", { ...api.getTitleProps(), className: "crisp-dialog-title", children: title }), (0, jsx_runtime_1.jsx)("button", { type: "button", ...api.getCloseTriggerProps(), className: "crisp-dialog-close", "aria-label": closeLabel, children: (0, jsx_runtime_1.jsx)(lucide_react_1.X, { size: 16, strokeWidth: 2, "aria-hidden": "true" }) })] })), children] }) }) })] }) }));
|
|
96
163
|
}
|
|
97
164
|
Dialog.displayName = "Dialog";
|
|
98
165
|
function DialogBody({ children, className, }) {
|
|
99
166
|
return ((0, jsx_runtime_1.jsx)("div", { className: className ? `crisp-dialog-body ${className}` : "crisp-dialog-body", children: children }));
|
|
100
167
|
}
|
|
101
|
-
function DialogFooter({ children }) {
|
|
102
|
-
return (0, jsx_runtime_1.jsx)("div", { className: "crisp-dialog-footer", children: children });
|
|
168
|
+
function DialogFooter({ children, variant = "panel", }) {
|
|
169
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: "crisp-dialog-footer", "data-variant": variant, children: children }));
|
|
103
170
|
}
|
|
@@ -18,17 +18,31 @@ const tokens_1 = require("../../tokens");
|
|
|
18
18
|
const config = (0, core_1.defineRecipe)({
|
|
19
19
|
name: "dialog",
|
|
20
20
|
base: `display: contents; font-family: ${tokens_1.contract.font.sans};
|
|
21
|
-
& .crisp-dialog-backdrop { position: fixed; inset: 0; z-index: 60; background:
|
|
21
|
+
& .crisp-dialog-backdrop { position: fixed; inset: 0; z-index: 60; background: ${tokens_1.contract.bgScrim}; }
|
|
22
22
|
& .crisp-dialog-positioner { position: fixed; inset: 0; z-index: 61; display: flex; align-items: flex-start; justify-content: center; padding: 15vh ${tokens_1.contract.space["4"]} ${tokens_1.contract.space["4"]}; }
|
|
23
|
+
& .crisp-dialog-positioner[data-variant="alert"] { align-items: center; padding: ${tokens_1.contract.space["4"]}; }
|
|
23
24
|
& .crisp-dialog-content { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; position: relative; display: flex; flex-direction: column; box-sizing: border-box; width: 90vw; max-height: 74vh; padding: ${tokens_1.contract.space["1"]}; border-radius: ${tokens_1.contract.radius["2xl"]}; background: color-mix(in srgb, ${tokens_1.contract.bgOverlayFrost} 60%, transparent); backdrop-filter: blur(15px); -webkit-backdrop-filter: blur(15px); box-shadow: ${tokens_1.elevation.modal}; opacity: 1; transform: scale(1); transition: opacity 0.2s ease, transform 0.2s ease; @starting-style { opacity: 0; transform: scale(0.97); } }
|
|
25
|
+
& .crisp-dialog-content[data-variant="alert"] { padding: 0; background: transparent; backdrop-filter: none; -webkit-backdrop-filter: none; box-shadow: none; }
|
|
24
26
|
& .crisp-dialog-card { display: flex; flex-direction: column; flex: 1; min-height: 0; box-sizing: border-box; border-radius: ${tokens_1.contract.radius.xl}; background: ${tokens_1.contract.bgSurface}; box-shadow: 0 0 0 1px color-mix(in srgb, ${tokens_1.contract.fgPrimary} 8%, transparent); overflow: hidden; }
|
|
27
|
+
& .crisp-dialog-content[data-variant="alert"] .crisp-dialog-card { border-radius: ${tokens_1.contract.radius.xl}; background: ${tokens_1.contract.bgPopover}; box-shadow: ${tokens_1.elevation.floating}; }
|
|
25
28
|
& .crisp-dialog-header { display: flex; align-items: center; gap: ${tokens_1.contract.space["2"]}; flex-shrink: 0; height: 52px; padding: 0 ${tokens_1.contract.space["2"]} 0 ${tokens_1.contract.space["4"]}; border-bottom: 1px solid ${tokens_1.contract.borderSubtle}; }
|
|
26
29
|
& .crisp-dialog-header svg { flex-shrink: 0; color: ${tokens_1.contract.fgMuted}; }
|
|
27
30
|
& .crisp-dialog-title { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: ${tokens_1.contract.text.lg}; font-weight: ${tokens_1.contract.weight.medium}; color: ${tokens_1.contract.fgPrimary}; margin: 0; }
|
|
28
31
|
& .crisp-dialog-close { display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; width: 28px; height: 28px; margin: 0; padding: 0; border: none; border-radius: ${tokens_1.contract.radius.md}; background: none; color: ${tokens_1.contract.fgMuted}; font-family: inherit; cursor: pointer; }
|
|
29
32
|
& .crisp-dialog-close:hover { background: color-mix(in srgb, ${tokens_1.contract.fgPrimary} 6%, transparent); color: ${tokens_1.contract.fgPrimary}; }
|
|
33
|
+
/* The discard-confirm's own stacked title + sentence (crisp#677) — no
|
|
34
|
+
border, no close button, unlike the panel header above: measured
|
|
35
|
+
live, both themes, 18px/24px 600-weight title (no scale token
|
|
36
|
+
reaches 18px — the scale tops out at text.xl's 16px, so this is raw
|
|
37
|
+
px per Golden Rule 3's "genuinely intrinsic size" exception, same as
|
|
38
|
+
this file's own submit-key glyphs) then a 14px/20px 500-weight
|
|
39
|
+
sentence 4px below it. */
|
|
40
|
+
& .crisp-dialog-alert-header { display: flex; flex-direction: column; gap: ${tokens_1.contract.space["1"]}; padding: ${tokens_1.contract.space["4"]} ${tokens_1.contract.space["4"]} 0; }
|
|
41
|
+
& .crisp-dialog-alert-title { margin: 0; font-size: 18px; line-height: 24px; font-weight: ${tokens_1.contract.weight.semibold}; color: ${tokens_1.contract.fgPrimary}; }
|
|
42
|
+
& .crisp-dialog-alert-description { margin: 0; font-size: ${tokens_1.contract.text.lg}; line-height: 20px; font-weight: ${tokens_1.contract.weight.medium}; color: ${tokens_1.contract.fgAlertDescription}; }
|
|
30
43
|
& .crisp-dialog-body { flex: 1; min-height: 0; overflow-y: auto; display: flex; flex-direction: column; gap: ${tokens_1.contract.space["4"]}; padding: ${tokens_1.contract.space["5"]} ${tokens_1.contract.space["4"]}; }
|
|
31
44
|
& .crisp-dialog-footer { display: flex; align-items: center; justify-content: flex-end; gap: ${tokens_1.contract.space["3"]}; flex-shrink: 0; min-height: 56px; padding: 0 ${tokens_1.contract.space["4"]}; border-top: 1px solid ${tokens_1.contract.borderSubtle}; }
|
|
45
|
+
& .crisp-dialog-footer[data-variant="alert"] { min-height: 45px; padding: ${tokens_1.contract.space["2"]}; }
|
|
32
46
|
& .crisp-dialog-field { display: flex; flex-direction: column; gap: ${tokens_1.contract.space["1.5"]}; }
|
|
33
47
|
& .crisp-dialog-field-label { font-size: ${tokens_1.contract.text.sm}; font-weight: ${tokens_1.contract.weight.medium}; color: color-mix(in srgb, ${tokens_1.contract.fgPrimary} 50%, transparent); }
|
|
34
48
|
& .crisp-dialog-more { display: inline-flex; align-items: center; gap: ${tokens_1.contract.space["2"]}; margin-inline-end: auto; border: none; background: none; padding: 0; font-family: inherit; font-size: ${tokens_1.contract.text.md}; color: ${tokens_1.contract.fgMuted}; cursor: pointer; }
|
|
@@ -19,12 +19,34 @@ const tokens_1 = require("../../tokens");
|
|
|
19
19
|
// stay byte-for-byte identical to what shipped before the extraction.
|
|
20
20
|
//
|
|
21
21
|
// `.menu--op` (#594 follow-up) is the filter condition's own operator list —
|
|
22
|
-
//
|
|
23
|
-
//
|
|
22
|
+
// no fixed min-width of its own (#593 correction below), unlike the default
|
|
23
|
+
// 232px every other menu here uses. `.menu-item` also picked up
|
|
24
24
|
// `line-height: 20px` and `font-weight: medium` from the same measurement
|
|
25
25
|
// (its 8 rows read 14px/20px/500) — shared by every menu that renders one,
|
|
26
26
|
// not just the operator list, since crisp's menu-item typography was
|
|
27
27
|
// previously unset rather than wrong.
|
|
28
|
+
//
|
|
29
|
+
// #593 correction: `.menu--op` originally carried a fixed `min-width: 134px`
|
|
30
|
+
// (rounded from one live measurement, the 8-op text set at 135px). Adding
|
|
31
|
+
// number/date columns' own shorter operator sets and re-measuring them live
|
|
32
|
+
// (104.86px for 6 number ops, 91.16px for 6 date ops — both narrower than
|
|
33
|
+
// 134px) showed that number wasn't a real Attio constant, just one set's own
|
|
34
|
+
// content width: a floor sized for the text set overshoots the other two.
|
|
35
|
+
// Dropped to `min-width: 0` so every op set sizes to its own content, the
|
|
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.
|
|
28
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"]}; }
|
|
29
51
|
& .${namespace_1.CLASS_PREFIX}${p}-toolbar[data-inline] { padding-bottom: 0; }
|
|
30
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}; }
|
|
@@ -64,7 +86,7 @@ const toolbarChromeBody = (p) => ` & .${namespace_1.CLASS_PREFIX}${p}-menu-he
|
|
|
64
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; }
|
|
65
87
|
& .${namespace_1.CLASS_PREFIX}${p}-sort-learn svg { flex-shrink: 0; }
|
|
66
88
|
& .${namespace_1.CLASS_PREFIX}${p}-sort-learn:hover { color: ${tokens_1.contract.fgPrimary}; }
|
|
67
|
-
& .${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; }
|
|
68
90
|
& .${namespace_1.CLASS_PREFIX}${p}-menu-item:hover { background: ${tokens_1.contract.bgRaised}; }
|
|
69
91
|
& .${namespace_1.CLASS_PREFIX}${p}-menu-item:disabled { opacity: 0.4; cursor: default; }
|
|
70
92
|
& .${namespace_1.CLASS_PREFIX}${p}-menu-item:disabled:hover { background: none; }
|
|
@@ -72,12 +94,34 @@ const toolbarChromeBody = (p) => ` & .${namespace_1.CLASS_PREFIX}${p}-menu-he
|
|
|
72
94
|
& .${namespace_1.CLASS_PREFIX}${p}-menu-item[data-on] svg { color: ${tokens_1.contract.bgBrandSolid}; }
|
|
73
95
|
& .${namespace_1.CLASS_PREFIX}${p}-menu-item .${namespace_1.CLASS_PREFIX}${p}-menu-check { margin-left: auto; }
|
|
74
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; }
|
|
75
|
-
& .${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; }
|
|
76
120
|
& .${namespace_1.CLASS_PREFIX}${p}-menu--up { top: auto; bottom: calc(100% + ${tokens_1.contract.space["1"]}); transform-origin: bottom; }
|
|
77
121
|
& .${namespace_1.CLASS_PREFIX}${p}-menu--right { left: auto; right: 0; }
|
|
78
122
|
& .${namespace_1.CLASS_PREFIX}${p}-menu--fixed { position: fixed; top: auto; left: auto; z-index: 40; }
|
|
79
123
|
& .${namespace_1.CLASS_PREFIX}${p}-menu--hdr { min-width: 200px; }
|
|
80
|
-
& .${namespace_1.CLASS_PREFIX}${p}-menu--op { min-width:
|
|
124
|
+
& .${namespace_1.CLASS_PREFIX}${p}-menu--op { min-width: 0; }
|
|
81
125
|
& .${namespace_1.CLASS_PREFIX}${p}-menu-item--danger { color: ${tokens_1.contract.bgDanger}; }
|
|
82
126
|
& .${namespace_1.CLASS_PREFIX}${p}-menu-item--danger svg { color: ${tokens_1.contract.bgDanger}; }
|
|
83
127
|
& .${namespace_1.CLASS_PREFIX}${p}-menu-item--danger:hover { background: color-mix(in srgb, ${tokens_1.contract.bgDanger} 10%, transparent); }
|