@crisp-ui-kit/crisp 0.54.8 → 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/board.css +32 -8
- package/dist/board.layered.css +32 -8
- 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 +541 -51
- package/dist/cjs/components/datatable/datatable.recipe.js +61 -3
- package/dist/cjs/components/dialog/dialog.js +65 -1
- package/dist/cjs/components/tabletoolbar/tabletoolbar.chrome.js +37 -2
- package/dist/cjs/components/tabletoolbar/tabletoolbar.js +148 -15
- package/dist/cjs/components/tabletoolbar/tabletoolbar.recipe.js +35 -1
- package/dist/cjs/product/board/board.js +62 -28
- package/dist/cjs/product/board/board.recipe.js +42 -2
- package/dist/cjs/product/comments/comments.js +11 -2
- package/dist/cjs/product/comments/comments.recipe.js +14 -4
- package/dist/cjs/product/recordstable/recordstable.js +1 -17
- package/dist/cjs/product/savedviews/savedviews.js +55 -24
- package/dist/cjs/product/savedviews/savedviews.recipe.js +27 -0
- package/dist/comments.css +14 -4
- package/dist/comments.layered.css +14 -4
- package/dist/contextmenu.css +24 -0
- package/dist/contextmenu.layered.css +24 -0
- package/dist/datatable.css +103 -77
- package/dist/datatable.layered.css +103 -77
- 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 +541 -52
- package/dist/esm/components/datatable/datatable.recipe.js +62 -4
- package/dist/esm/components/dialog/dialog.js +65 -1
- package/dist/esm/components/tabletoolbar/tabletoolbar.chrome.js +37 -2
- package/dist/esm/components/tabletoolbar/tabletoolbar.js +149 -16
- package/dist/esm/components/tabletoolbar/tabletoolbar.recipe.js +37 -3
- package/dist/esm/product/board/board.js +63 -29
- package/dist/esm/product/board/board.recipe.js +42 -2
- package/dist/esm/product/comments/comments.js +12 -3
- package/dist/esm/product/comments/comments.recipe.js +14 -4
- package/dist/esm/product/recordstable/recordstable.js +2 -18
- package/dist/esm/product/savedviews/savedviews.js +57 -26
- package/dist/esm/product/savedviews/savedviews.recipe.js +27 -0
- package/dist/recordstable.css +33 -31
- package/dist/recordstable.layered.css +33 -31
- package/dist/savedviews.css +27 -0
- package/dist/savedviews.layered.css +27 -0
- package/dist/styles.css +266 -124
- package/dist/styles.layered.css +266 -124
- package/dist/tabletoolbar.css +33 -4
- package/dist/tabletoolbar.layered.css +33 -4
- package/dist/types/components/contextmenu/contextmenu.d.ts +8 -1
- package/dist/types/components/datatable/datatable.d.ts +90 -2
- package/dist/types/components/tabletoolbar/tabletoolbar.d.ts +72 -2
- package/dist/types/product/board/board.d.ts +48 -2
- package/dist/types/product/comments/comments.d.ts +6 -1
- package/dist/types/product/recordstable/recordstable.d.ts +0 -1
- package/dist/types/product/savedviews/savedviews.d.ts +45 -8
- package/package.json +1 -1
|
@@ -10,61 +10,71 @@
|
|
|
10
10
|
& .crisp-datatable-count { margin-inline-start: auto; font-size: var(--crisp-text-md); color: var(--crisp-fg-muted); }
|
|
11
11
|
/* announced, never drawn */
|
|
12
12
|
& .crisp-datatable-live { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }
|
|
13
|
-
/*
|
|
13
|
+
/* crisp/#645: visually-hidden-but-focusable, not visually-hidden-and- inert like .crisp-datatable-live above -- same clip-rect shape, this one just needs to actually hold DOM focus so a native copy/paste event has something to fire on (see its own comment in datatable.tsx). resize: none keeps a stray native textarea handle from ever being drawable, even though it's clipped to 1px anyway. */
|
|
14
|
+
& .crisp-datatable-clipboard-proxy {
|
|
15
|
+
position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
|
|
16
|
+
overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
|
|
17
|
+
resize: none;
|
|
18
|
+
}
|
|
19
|
+
/* single active cell (crisp/#645 follow-up -- this had no styling of its
|
|
20
|
+
own at all before, a gap against Golden Rule 0: RecordsTable's own measured value, .crisp-recordstable-td[data-selected], brought across unchanged now that a visible anchor actually matters for the Shift+Click range below). */
|
|
21
|
+
& td[data-selected] { outline: 2px solid var(--crisp-bg-brand-solid); outline-offset: -2px; border-radius: var(--crisp-radius-sm); z-index: 3; }
|
|
22
|
+
/* crisp/#645, G-101: a Shift+Click range -- reuses the row-selection tint formula already used twice below (tbody tr[data-selected] td) for the same reason a selected group of rows gets one: a selected GROUP of cells is the same kind of state, just cell-scoped instead of row-scoped. Not independently re-measured against Attio's own range
|
|
23
|
+
highlight (out of scope this slice -- flagged on crisp#645). */
|
|
24
|
+
& td[data-in-range] { background: color-mix(in srgb, var(--crisp-bg-brand-solid) 10%, var(--crisp-bg-surface));
|
|
25
|
+
} /* The header cell has no padding of its own -- the button owns the full cell so the whole header is clickable (its own inset is 12px). Mirrors the summary-row fix. `& .crisp-table th`, not bare `& th` (found while re-checking #691's
|
|
14
26
|
own fix live): DataTable's root nests `Table`'s own wrapper, whose
|
|
15
|
-
`table.recipe.ts` carries `& th, & td { padding: 0 12px }
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
27
|
+
`table.recipe.ts` carries `& th, & td { padding: 0 12px }
|
|
28
|
+
` at the SAME specificity (.crisp-table th vs .crisp-datatable th, both one class + one element) -- a bare `& th` here only won by source order, which flipped the moment table.recipe.ts's own CSS file
|
|
29
|
+
happened to land after datatable's in the build (alphabetical: datatable < table), silently reinstating 12px on every header cell in the whole product and making every interactive column's button
|
|
30
|
+
double it to 24px. Scoping through `.crisp-table` adds a second
|
|
31
|
+
class to the selector (0,2,1 beats 0,1,1) so this can never lose a
|
|
32
|
+
specificity tie to source order again. */
|
|
21
33
|
& .crisp-table th { padding: 0; }
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
left to squeeze, the label's own ellipsis CSS (below) had no properly -sized box to truncate against, and the th's own overflow hard-clipped
|
|
26
|
-
it with no "...". Fill the wrapper, scoped to th so Menu's default (correct everywhere else) is untouched. */
|
|
34
|
+
/* Same wrapper trap as AUTHORING.md Lesson 40 (ColumnCalc's summary trigger): Menu wraps its trigger in a flex div that sizes to its content, so a long header grew past the column instead of filling it -- with nothing left to squeeze, the label's own ellipsis CSS (below) had no properly
|
|
35
|
+
-sized box to truncate against, and the th's own overflow hard-clipped it with no "...". Fill the wrapper, scoped to th so Menu's default
|
|
36
|
+
(correct everywhere else) is untouched. */
|
|
27
37
|
& th .crisp-menu { width: 100%; height: 100%; }
|
|
28
38
|
& .crisp-datatable-th-inner { display: inline-flex; align-items: center; gap: var(--crisp-space-1-5); width: 100%; min-width: 0; height: 100%; }
|
|
29
39
|
& .crisp-datatable-th-inner > svg { flex-shrink: 0; color: var(--crisp-fg-muted); }
|
|
30
|
-
/* crisp/#605 follow-up: a header with neither a Menu nor a sort button
|
|
31
|
-
columnMenu/sortable at all) renders .crisp-datatable-th-inner as a
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
plain sort `button` both put >=1 more level between `th` and this
|
|
37
|
-
span, so their own 12px (already on the button) isn't doubled. */
|
|
40
|
+
/* crisp/#605 follow-up: a header with neither a Menu nor a sort button
|
|
41
|
+
(the primary/entity column's now-inert path, or any column with no columnMenu/sortable at all) renders .crisp-datatable-th-inner as a DIRECT child of th instead of nested inside a button -- and loses the button's own 12px inset, drifting left of the td's 12px base inset. Measured live on Attio's own Companies grid: the header label
|
|
42
|
+
and the cell's own text land at the exact same x (335px from the viewport, both 40px from the column's left edge) via two different
|
|
43
|
+
padding+icon+gap combinations -- Attio does not leave the inert
|
|
44
|
+
primary header flush left either. The `>` combinator targets only
|
|
45
|
+
the button-less case: Menu's own wrapper (`.crisp-menu`) and the plain sort `button` both put >=1 more level between `th` and this span, so their own 12px (already on the button) isn't doubled. */
|
|
38
46
|
& th > .crisp-datatable-th-inner { box-sizing: border-box; padding: 0 var(--crisp-space-3); }
|
|
39
|
-
|
|
47
|
+
/* The summary cell has NO padding of its own -- the calculation trigger
|
|
48
|
+
owns the whole 246x37 area so a click anywhere in the cell opens the
|
|
49
|
+
menu. Zero the cell's inset and let ColumnCalc supply the 12px. */
|
|
40
50
|
& tfoot td { padding: 0; }
|
|
41
|
-
/* Group heading row -- a view is grouped under a subtle label with the
|
|
42
|
-
|
|
43
|
-
& .crisp-datatable-viewsettings { display: inline-flex; align-items: center; gap: var(--crisp-space-1-5); height: var(--crisp-size-row); margin: 0; padding: 0 var(--crisp-space-2); border: none; border-radius: var(--crisp-radius-md); background: transparent; color: var(--crisp-fg-muted); font-family: inherit; font-size: var(--crisp-text-md); font-weight: var(--crisp-weight-medium); white-space: nowrap; cursor: pointer; transition: background-color .12s, color .12s; }
|
|
51
|
+
/* Group heading row -- a view is grouped under a subtle label with the bucket's count beside it. */
|
|
52
|
+
& .crisp-datatable-viewsettings { display: inline-flex; align-items: center; gap: var(--crisp-space-1-5); height: var(--crisp-size-row); margin: 0; padding: 0 var(--crisp-space-2); border: none; border-radius: var(--crisp-radius-md); background: transparent; color: var(--crisp-fg-muted); font-family: inherit; font-size: var(--crisp-text-md); font-weight: var(--crisp-weight-medium); white-space: nowrap; cursor: pointer; transition: background-color .12s, color .12s; }
|
|
44
53
|
& .crisp-datatable-viewsettings:hover { background: color-mix(in srgb, var(--crisp-fg-primary) 4%, transparent); color: var(--crisp-fg-primary); }
|
|
45
54
|
& .crisp-datatable-group-label { padding: var(--crisp-space-1-5) var(--crisp-space-2) var(--crisp-space-1); font-size: var(--crisp-text-sm); font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-muted); }
|
|
46
|
-
|
|
55
|
+
& .crisp-datatable-group { height: var(--crisp-size-menuRow); padding: 0 var(--crisp-space-3); background: var(--crisp-bg-subtle); border-bottom: 1px solid var(--crisp-border-subtle); font-size: var(--crisp-text-md); font-weight: var(--crisp-weight-medium); color: color-mix(in srgb, var(--crisp-fg-primary) 63%, transparent); }
|
|
47
56
|
& .crisp-datatable-group-count { margin-inline-start: var(--crisp-space-1-5); color: var(--crisp-fg-muted); font-variant-numeric: tabular-nums; }
|
|
48
57
|
& .crisp-datatable-th-rename { width: 100%; min-width: 0; height: var(--crisp-size-row); margin: 0; padding: 0 var(--crisp-space-1); border: none; border-radius: var(--crisp-radius-md); background: var(--crisp-bg-surface); box-shadow: inset 0 0 0 1px var(--crisp-bg-brand-solid); font-family: inherit; font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-primary); outline: none; }
|
|
49
58
|
& .crisp-datatable-th-rename:focus { box-shadow: inset 0 0 0 1px var(--crisp-bg-brand-solid); }
|
|
50
59
|
& .crisp-datatable-th-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
|
|
51
|
-
/* #465: 12px per the comment above (and Attio's measured header cell
|
|
52
|
-
padding: 0 12px) -- was still c.space["2"] (8px), drifted from its
|
|
53
|
-
own documented value. */
|
|
60
|
+
/* #465: 12px per the comment above (and Attio's measured header cell padding: 0 12px) -- was still c.space["2"] (8px), drifted from its own documented value. */
|
|
54
61
|
& .crisp-datatable-sort { display: flex; align-items: center; gap: var(--crisp-space-1-5); width: 100%; min-width: 0; height: 100%; margin: 0; padding: 0 var(--crisp-space-3); border: none; background: none; font: inherit; letter-spacing: inherit; color: inherit; text-align: start; cursor: pointer; }
|
|
55
|
-
/* A right-aligned column's header keeps its field-icon + label packed at
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
62
|
+
/* A right-aligned column's header keeps its field-icon + label packed at
|
|
63
|
+
the START, matching its own values only in that the VALUES right-align
|
|
64
|
+
-- Attio's own right-aligned header (measured live: "Twitter follower
|
|
65
|
+
count") keeps the icon+label left and only the trailing column-menu icon sits pinned to the right edge, independent of the label's width
|
|
66
|
+
(#406, reversing #393's own justify-content: flex-end here). The chevron's own margin-inline-start: auto below already pins IT to the
|
|
67
|
+
end of a flex-start row with no override needed -- that's what pins the trailing icon on Attio too. */
|
|
60
68
|
& .crisp-datatable-chevron { margin-inline-start: auto; flex-shrink: 0; color: var(--crisp-fg-muted); opacity: 0; transition: opacity .12s; }
|
|
61
69
|
& .crisp-datatable-sort:hover .crisp-datatable-chevron { opacity: 0.6; }
|
|
62
70
|
& .crisp-datatable-sort[data-sorted] .crisp-datatable-chevron { opacity: 1; color: var(--crisp-fg-primary); }
|
|
63
71
|
/* sticky first column(s) + horizontal scroll shadow — RecordsTable's feel */
|
|
64
72
|
& th[data-sticky], & td[data-sticky] { position: sticky; z-index: 1; background: var(--crisp-bg-surface); }
|
|
65
|
-
& th[data-sticky] { z-index: 3;
|
|
66
|
-
/* #554: a footer sticky-column cell sits in the bottom-sticky row (table.recipe.ts's
|
|
67
|
-
`tfoot td { z-index: 2 }
|
|
73
|
+
& th[data-sticky] { z-index: 3;
|
|
74
|
+
} /* #554: a footer sticky-column cell sits in the bottom-sticky row (table.recipe.ts's
|
|
75
|
+
`tfoot td { z-index: 2 }
|
|
76
|
+
`), which `td[data-sticky]`'s own higher-specificity
|
|
77
|
+
`z-index: 1` would otherwise undercut -- keep it level with the row it's in. */
|
|
68
78
|
& tfoot td[data-sticky] { z-index: 2; }
|
|
69
79
|
& tbody tr[data-selected] td[data-sticky] { background: color-mix(in srgb, var(--crisp-bg-brand-solid) 10%, var(--crisp-bg-surface)); }
|
|
70
80
|
& tbody tr:hover td[data-sticky] { background: color-mix(in srgb, var(--crisp-fg-primary) 4%, var(--crisp-bg-surface)); }
|
|
@@ -83,62 +93,79 @@
|
|
|
83
93
|
& .crisp-datatable-editable:hover { background: color-mix(in srgb, var(--crisp-fg-primary) 4%, var(--crisp-bg-surface)); cursor: text; }
|
|
84
94
|
& .crisp-datatable-edit { width: 100%; height: 28px; margin: 0; padding: 0 var(--crisp-space-1-5); border: none; border-radius: var(--crisp-radius-sm); background: var(--crisp-bg-surface); box-shadow: inset 0 0 0 1px var(--crisp-bg-brand-solid); font-family: inherit; font-size: var(--crisp-text-lg); color: var(--crisp-fg-primary); outline: none; vertical-align: middle; }
|
|
85
95
|
& .crisp-datatable-edit:focus { box-shadow: inset 0 0 0 1px var(--crisp-bg-brand-solid); }
|
|
86
|
-
/* An async onEdit's in-flight state (#385) -- the field/picker dims and
|
|
87
|
-
stops accepting input rather than disappearing, so the just-typed or
|
|
88
|
-
just-chosen value stays visible while its commit is pending. Same
|
|
89
|
-
treatment Input already gives its own disabled state. */
|
|
96
|
+
/* An async onEdit's in-flight state (#385) -- the field/picker dims and stops accepting input rather than disappearing, so the just-typed or just-chosen value stays visible while its commit is pending. Same treatment Input already gives its own disabled state. */
|
|
90
97
|
& .crisp-datatable-edit:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
91
98
|
& .crisp-datatable-edit-pending { flex-shrink: 0; margin-inline-start: var(--crisp-space-1); color: var(--crisp-fg-muted); vertical-align: middle; }
|
|
92
|
-
/* The rejection's message, beside the cell's own content -- a sibling in
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
the row below, so it can never end up clipped by the scroll
|
|
96
|
-
container's overflow the way a position:fixed escape would need guarding against. */
|
|
99
|
+
/* The rejection's message, beside the cell's own content -- a sibling in the TableCell, never nested inside a column's own render output, so a
|
|
100
|
+
hover-only wrapper (a Tooltip, say) around a custom render can't hide it. Block-level: the row grows to fit it rather than floating over the row below, so it can never end up clipped by the scroll container's overflow the way a position:fixed escape would need
|
|
101
|
+
guarding against. */
|
|
97
102
|
& .crisp-datatable-edit-error { display: block; padding: var(--crisp-space-0-5) 0 0; font-size: var(--crisp-text-sm); color: var(--crisp-bg-danger); }
|
|
98
|
-
/* editor: "select"/"switch" -- always live, no separate edit mode, so
|
|
99
|
-
|
|
103
|
+
/* editor: "select"/"switch" -- always live, no separate edit mode, so
|
|
104
|
+
the cell IS the picker: same chromeless-fill treatment as
|
|
105
|
+
RecordsTable's own --picker cells (measured there already; DataTable shares RecordsTable's grid, Golden Rule 0). */
|
|
100
106
|
& .crisp-datatable-td--picker { padding: 0; }
|
|
101
107
|
& .crisp-datatable-edit-picker { display: flex; align-items: center; gap: var(--crisp-space-1); height: var(--crisp-size-tableRow); padding: 0 var(--crisp-space-2); }
|
|
102
108
|
& .crisp-datatable-edit-picker .crisp-select-trigger { flex: 1; min-width: 0; height: 100%; min-height: 0; border-radius: 0; box-shadow: none; padding: 0; }
|
|
103
109
|
& .crisp-datatable-td--picker .crisp-datatable-edit-error { padding-inline-start: var(--crisp-space-2); }
|
|
104
|
-
/* reference picker: a leading 16px avatar per option, plus a trailing
|
|
110
|
+
/* reference picker: a leading 16px avatar per option, plus a trailing
|
|
111
|
+
"Create X" row when the consumer supplies one (#449) -- same measured
|
|
112
|
+
values as RecordsTable's own .crisp-recordstable-refoption*. */
|
|
105
113
|
& .crisp-datatable-refoption { display: flex; align-items: center; gap: var(--crisp-space-2); min-width: 0; overflow: hidden; text-overflow: ellipsis; }
|
|
106
114
|
& .crisp-datatable-refoption-avatar { flex-shrink: 0; }
|
|
107
115
|
& .crisp-datatable-refoption--create { color: var(--crisp-fg-muted); }
|
|
108
116
|
& .crisp-datatable-empty { padding: var(--crisp-space-8) var(--crisp-space-3); text-align: center; color: var(--crisp-fg-muted); font-size: var(--crisp-text-lg); }
|
|
109
|
-
/* #579: click-to-expand overlay for a truncated cell -- a read-only,
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
core, not this cell), so growing a row here would fight it. Anchored
|
|
116
|
-
to the SAME column width the cell itself renders at (measured:
|
|
117
|
-
Attio's own overlay came out only slightly wider than the triggering cell's clientWidth -- the overshoot is this padding),
|
|
118
|
-
wrapping onto as many lines as the value needs; it CAN be clipped by
|
|
119
|
-
`.crisp-table-scroll`'s own `overflow: auto` if it grows past the visible viewport, same as the edit-error comment above already notes for any position:absolute escape -- unmeasured against Attio for this exact case, flagged rather than guessed. */
|
|
117
|
+
/* #579: click-to-expand overlay for a truncated cell -- a read-only, full-value card, settled live as click (not Attio's usual
|
|
118
|
+
hover-expand) and measured position: absolute, unlike this file's floating chrome (Menu/Select popovers) which portals and escapes the scroll container. Deliberately NOT the edit-error pattern above (grow the row, block-level, never clipped): a virtualized grid assumes a fixed row height for its scroll math (owned by the grid core, not this cell), so growing a row here would fight it. Anchored to the SAME column width the cell itself renders at (measured: Attio's own overlay came out only slightly wider than the
|
|
119
|
+
triggering cell's clientWidth -- the overshoot is this padding), wrapping onto as many lines as the value needs; it CAN be clipped by `.crisp-table-scroll`'s own `overflow: auto` if it grows past the
|
|
120
|
+
visible viewport, same as the edit-error comment above already
|
|
121
|
+
notes for any position:absolute escape -- unmeasured against Attio
|
|
122
|
+
for this exact case, flagged rather than guessed. */
|
|
120
123
|
& td[data-cell-expanded] { overflow: visible; z-index: 7; }
|
|
121
|
-
& .crisp-datatable-cell-overlay {
|
|
124
|
+
& .crisp-datatable-cell-overlay {
|
|
125
|
+
position: absolute; inset-inline-start: 0; top: 0; width: 100%; box-sizing: border-box;
|
|
126
|
+
padding: var(--crisp-space-2) var(--crisp-space-3); background: var(--crisp-bg-surface);
|
|
127
|
+
border: 1px solid var(--crisp-bg-brand-solid); border-radius: 4px;
|
|
128
|
+
font-family: var(--crisp-font-sans); font-size: var(--crisp-text-lg); line-height: 20px; font-weight: var(--crisp-weight-medium);
|
|
129
|
+
color: var(--crisp-fg-primary); white-space: normal; word-break: break-word; cursor: default;
|
|
130
|
+
}
|
|
122
131
|
/* fixed layout so column widths (and resize) are exact — like RecordsTable */
|
|
123
132
|
& table { table-layout: fixed; }
|
|
124
|
-
/* read-only/computed column marker (#596) -- sits just inside the
|
|
133
|
+
/* read-only/computed column marker (#596) -- sits just inside the
|
|
134
|
+
resize handle, same absolute-positioned-against-the-th approach the
|
|
135
|
+
handle itself uses, since th has zero padding and the button/inner
|
|
136
|
+
row fills the whole cell with no room left for a trailing sibling. */
|
|
125
137
|
& .crisp-datatable-readonly { position: absolute; top: 50%; inset-inline-end: 11px; transform: translateY(-50%); color: var(--crisp-fg-muted); pointer-events: none; z-index: 5; }
|
|
126
138
|
/* column resize grip */
|
|
127
139
|
& .crisp-datatable-resize { position: absolute; top: 0; inset-inline-end: -3px; width: 9px; height: 100%; cursor: col-resize; z-index: 6; touch-action: none; }
|
|
128
140
|
& .crisp-datatable-resize::after { content: ""; position: absolute; top: 0; inset-inline-start: 3px; width: 2px; height: 100%; background: var(--crisp-bg-brand-solid); opacity: 0; transition: opacity .12s; }
|
|
129
141
|
& .crisp-datatable-resize:hover::after, & .crisp-datatable-resize[data-resizing]::after { opacity: 1; }
|
|
142
|
+
/* crisp/#731 -- column drag-to-reorder. Measured live against Attio's own grid (the issue's own comment): the dragged header keeps full
|
|
143
|
+
opacity and the plain `pointer` cursor throughout (Attio draws no
|
|
144
|
+
ghost and no "grabbing" cursor of its own), so this adds nothing to
|
|
145
|
+
`[data-dragging]` itself -- the ONLY new paint is the drop-indicator
|
|
146
|
+
line, a 3px bar in the same measured brand blue the resize handle's own hover state (above) already uses, positioned on whichever edge of the hovered header the pointer is closer to. */
|
|
147
|
+
& th[data-dragging] { cursor: pointer; }
|
|
148
|
+
& th[data-drop-before]::before, & th[data-drop-after]::after { content: ""; position: absolute; top: 0; width: 3px; height: 100%; background: var(--crisp-bg-brand-solid); z-index: 6; pointer-events: none; }
|
|
149
|
+
& th[data-drop-before]::before { inset-inline-start: -1.5px; }
|
|
150
|
+
& th[data-drop-after]::after { inset-inline-end: -1.5px; }
|
|
130
151
|
/* typed cells — entity (avatar + name), colour-coded tags, numbers, dates */
|
|
131
152
|
& .crisp-datatable-entity { display: flex; align-items: center; gap: var(--crisp-space-1-5); min-width: 0; color: var(--crisp-fg-primary); font-weight: var(--crisp-weight-medium); }
|
|
132
|
-
|
|
133
|
-
|
|
153
|
+
/* crisp/#601, G-050: measured LIVE (getComputedStyle on the name text, plain hover vs rest) that Attio's underline is unconditional, not a
|
|
154
|
+
hover reveal -- identical at rest and on hover: text-decoration-color
|
|
155
|
+
is rgba(0,0,0,0.09), a 9% tint, not the name's own full-contrast text colour. The previous :hover-gated, full-contrast rule here was itself unmeasured and wrong in two ways at once. */
|
|
156
|
+
& .crisp-datatable-entity-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-decoration: underline; text-decoration-color: color-mix(in srgb, var(--crisp-fg-primary) 9%, transparent); }
|
|
134
157
|
& 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%; }
|
|
158
|
+
/* crisp/#601, G-048: two 20px ghost icons, right-aligned in the primary column, revealed on ROW hover (the issue's own title says "row
|
|
159
|
+
hover", and a live check confirmed hovering well past the name --
|
|
160
|
+
not just the icons' own hit target -- still reveals them) or keyboard focus. Measured live: border-radius 6px, icon colour rgba(0,0,0,0.55) at rest, darkening to the full text colour with a rgba(0,0,0,0.04) background on the icon's OWN hover -- both
|
|
161
|
+
expressed as color-mix against fgPrimary so they stay correct in
|
|
162
|
+
dark mode instead of a literal black that would invert. */
|
|
163
|
+
& .crisp-datatable-entity-actions { display: flex; align-items: center; gap: var(--crisp-space-1-5); margin-inline-start: auto; flex-shrink: 0; opacity: 0; }
|
|
164
|
+
& tr:hover .crisp-datatable-entity-actions, & .crisp-datatable-entity-actions:focus-within { opacity: 1; }
|
|
165
|
+
& .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: var(--crisp-radius-sm); background: none; font-family: inherit; color: color-mix(in srgb, var(--crisp-fg-primary) 55%, transparent); cursor: pointer; flex-shrink: 0; }
|
|
166
|
+
& .crisp-datatable-entity-action:hover { background: color-mix(in srgb, var(--crisp-fg-primary) 4%, transparent); color: var(--crisp-fg-primary); }
|
|
135
167
|
& .crisp-datatable-avatar { width: var(--crisp-size-icon); height: var(--crisp-size-icon); border-radius: var(--crisp-radius-sm); flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center; font-size: 10px; line-height: 1; font-weight: var(--crisp-weight-semibold); text-transform: uppercase; color: var(--crisp-fg-on-brand); background: var(--crisp-bg-brand-solid); }
|
|
136
|
-
& .crisp-datatable-avatar[data-tone="success"] { background: var(--crisp-bg-success); }
|
|
137
|
-
& .crisp-datatable-avatar[data-tone="warning"] { background: var(--crisp-bg-warning); }
|
|
138
|
-
& .crisp-datatable-avatar[data-tone="danger"] { background: var(--crisp-bg-danger); }
|
|
139
|
-
& .crisp-datatable-avatar[data-tone="purple"] { background: var(--crisp-bg-purple); }
|
|
140
|
-
& .crisp-datatable-avatar[data-tone="cyan"] { background: var(--crisp-bg-cyan); }
|
|
141
|
-
& .crisp-datatable-avatar[data-tone="orange"] { background: var(--crisp-bg-orange); }
|
|
168
|
+
& .crisp-datatable-avatar[data-tone="success"] { background: var(--crisp-bg-success); } & .crisp-datatable-avatar[data-tone="warning"] { background: var(--crisp-bg-warning); } & .crisp-datatable-avatar[data-tone="danger"] { background: var(--crisp-bg-danger); } & .crisp-datatable-avatar[data-tone="purple"] { background: var(--crisp-bg-purple); } & .crisp-datatable-avatar[data-tone="cyan"] { background: var(--crisp-bg-cyan); } & .crisp-datatable-avatar[data-tone="orange"] { background: var(--crisp-bg-orange); }
|
|
142
169
|
& .crisp-datatable-avatar img { width: 100%; height: 100%; border-radius: inherit; object-fit: cover; }
|
|
143
170
|
& .crisp-datatable-tags { display: flex; align-items: center; gap: var(--crisp-space-1); overflow: hidden; }
|
|
144
171
|
& .crisp-datatable-tag { display: inline-flex; align-items: center; height: var(--crisp-size-chip); padding: 0 var(--crisp-space-1); border-radius: var(--crisp-radius-chip); font-size: var(--crisp-text-lg); line-height: 20px; font-weight: var(--crisp-weight-medium); white-space: nowrap; background: color-mix(in srgb, var(--crisp-fg-primary) 6%, transparent); color: color-mix(in srgb, var(--crisp-fg-primary) 82%, transparent); }
|
|
@@ -151,20 +178,19 @@
|
|
|
151
178
|
& .crisp-datatable-tag[data-tone="orange"] { background: color-mix(in srgb, var(--crisp-bg-orange) 16%, transparent); color: color-mix(in srgb, var(--crisp-bg-orange) 82%, var(--crisp-fg-primary)); }
|
|
152
179
|
& .crisp-datatable-num { font-variant-numeric: tabular-nums; }
|
|
153
180
|
& .crisp-datatable-muted { color: var(--crisp-fg-muted); }
|
|
154
|
-
/* url/email cell types (#654) -- same measured values as RecordsTable's
|
|
155
|
-
own .crisp-recordstable-link, which this mirrors: no underline at
|
|
156
|
-
rest, underline on hover, own-colour text rather than a browser-blue
|
|
157
|
-
link. */
|
|
181
|
+
/* url/email cell types (#654) -- same measured values as RecordsTable's own .crisp-recordstable-link, which this mirrors: no underline at rest, underline on hover, own-colour text rather than a browser-blue link. */
|
|
158
182
|
& .crisp-datatable-link { color: var(--crisp-fg-primary); text-decoration: none; overflow: hidden; text-overflow: ellipsis; }
|
|
159
183
|
& .crisp-datatable-link:hover { text-decoration: underline; }
|
|
160
|
-
/* row drag-to-reorder -- a Settings > Objects > Attributes screen measures
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
(RecordsTable's reorderColumn) -- not a proprietary
|
|
184
|
+
/* row drag-to-reorder -- a Settings > Objects > Attributes screen measures a leading 20px grip column, a 16px grip icon, and the row's own
|
|
185
|
+
hairline doubling as the drop indicator (it tints toward the brand
|
|
186
|
+
colour as the active drop target). The affordance + geometry are
|
|
187
|
+
measured; the drag MECHANISM is crisp's existing house idiom -- HTML5 draggable + dataTransfer, the same one column reorder already uses (RecordsTable's reorderColumn) -- not a proprietary
|
|
188
|
+
pointer-events implementation. */
|
|
164
189
|
& .crisp-datatable-col-grip { width: 20px; padding: 0; text-align: center; }
|
|
165
190
|
& .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: var(--crisp-fg-muted); cursor: grab; }
|
|
166
191
|
& .crisp-datatable-grip:active { cursor: grabbing; }
|
|
167
192
|
& tbody tr[data-drop-target] td, & tbody tr[data-drop-target] th { box-shadow: inset 0 2px 0 0 var(--crisp-bg-brand-solid); transition: box-shadow .12s; }
|
|
193
|
+
[dir="rtl"] & [data-rtl-flip] { transform: scaleX(-1); }
|
|
168
194
|
}
|
|
169
195
|
|
|
170
196
|
@media (prefers-reduced-motion: reduce) {
|
|
@@ -9,7 +9,7 @@ import { contextmenuRecipe } from "./contextmenu.recipe.js";
|
|
|
9
9
|
* Wrap the target; right-clicking it opens the menu (flipping at the viewport
|
|
10
10
|
* edges), closing on select, outside pointer-down, and Escape.
|
|
11
11
|
*/
|
|
12
|
-
export function ContextMenu({ children, items, menuLabel: menuLabelProp, className, asChild = false, }) {
|
|
12
|
+
export function ContextMenu({ children, items, menuLabel: menuLabelProp, className, asChild = false, variant = "light", }) {
|
|
13
13
|
// crisp/#471: same resolution order as Breadcrumb's own.
|
|
14
14
|
const menuLabel = useCrispMessage(menuLabelProp, "Context menu");
|
|
15
15
|
const rootRef = React.useRef(null);
|
|
@@ -76,7 +76,7 @@ export function ContextMenu({ children, items, menuLabel: menuLabelProp, classNa
|
|
|
76
76
|
document.removeEventListener("keydown", onKey);
|
|
77
77
|
};
|
|
78
78
|
}, [pos, close]);
|
|
79
|
-
return (_jsxs(_Fragment, { children: [asChild ? (_jsx(Slot, { ref: rootRef, className: className, onContextMenu: open, children: children })) : (_jsx("div", { ref: rootRef, className: contextmenuRecipe({ className }), children: _jsx("div", { onContextMenu: open, children: children }) })), pos && (_jsx(FloatingPortal, { sourceRef: rootRef, children: _jsx("div", { ref: menuRef, className: "crisp-contextmenu-content", role: "menu", "aria-label": menuLabel, style: { left: pos.x, top: pos.y }, onKeyDown: onMenuKeyDown, children: items.map((item, i) => item.separator ? (_jsx("hr", { className: "crisp-contextmenu-sep" }, i)) : (_jsxs("button", { type: "button", role: "menuitem", disabled: item.disabled, className: item.danger
|
|
79
|
+
return (_jsxs(_Fragment, { children: [asChild ? (_jsx(Slot, { ref: rootRef, className: className, onContextMenu: open, children: children })) : (_jsx("div", { ref: rootRef, className: contextmenuRecipe({ className }), children: _jsx("div", { onContextMenu: open, children: children }) })), pos && (_jsx(FloatingPortal, { sourceRef: rootRef, children: _jsx("div", { ref: menuRef, className: "crisp-contextmenu-content", "data-variant": variant === "dark" ? "dark" : undefined, role: "menu", "aria-label": menuLabel, style: { left: pos.x, top: pos.y }, onKeyDown: onMenuKeyDown, children: items.map((item, i) => item.separator ? (_jsx("hr", { className: "crisp-contextmenu-sep" }, i)) : (_jsxs("button", { type: "button", role: "menuitem", disabled: item.disabled, className: item.danger
|
|
80
80
|
? "crisp-contextmenu-item crisp-contextmenu-item--danger"
|
|
81
81
|
: "crisp-contextmenu-item", tabIndex: -1, onClick: () => {
|
|
82
82
|
item.onSelect?.();
|
|
@@ -32,6 +32,30 @@ const config = defineRecipe({
|
|
|
32
32
|
.crisp-contextmenu-item--danger:hover { background: color-mix(in srgb, ${c.bgDanger} 10%, transparent); }
|
|
33
33
|
.crisp-contextmenu-shortcut { margin-inline-start: auto; padding-inline-start: ${c.space["3"]}; color: ${c.fgMuted}; font-size: ${c.text.sm}; }
|
|
34
34
|
.crisp-contextmenu-sep { height: 1px; margin: ${c.space["1"]} 0; border: none; background: ${c.borderSubtle}; }
|
|
35
|
+
/* crisp/#598: Attio's OWN cell-level context menu, measured live on
|
|
36
|
+
app.attio.com/techpartners/companies (right-click a data cell) --
|
|
37
|
+
theme-INDEPENDENT dark chrome, not this app's dark THEME (every
|
|
38
|
+
geometry value above -- 12px/8px radius, 4px/8px padding, 32px item
|
|
39
|
+
height, item gap included below -- already matched what was
|
|
40
|
+
measured here; only the colour pair differs, so only colours are
|
|
41
|
+
overridden). Same "#101112 / #eeeff1" dark-pill ink Tooltip
|
|
42
|
+
already hardcodes for the identical reason (that surface is ALSO
|
|
43
|
+
theme-independent). */
|
|
44
|
+
.crisp-contextmenu-content[data-variant="dark"] { background: #101112; box-shadow: none; }
|
|
45
|
+
.crisp-contextmenu-content[data-variant="dark"] .crisp-contextmenu-item { gap: ${c.space["1.5"]}; color: #eeeff1; }
|
|
46
|
+
.crisp-contextmenu-content[data-variant="dark"] .crisp-contextmenu-item svg { color: #898a8d; }
|
|
47
|
+
/* Plain-item hover re-measured live on the same cell menu (right-click
|
|
48
|
+
"Description" on app.attio.com/techpartners/companies, hover the
|
|
49
|
+
"Copy" row): getComputedStyle on the row is rgba(255,255,255,0.1),
|
|
50
|
+
not the 0.08 this used to guess at. The danger row's own hover
|
|
51
|
+
(hovering "Clear value") still didn't get re-measured -- that one
|
|
52
|
+
remains the derived red-on-black translation below, flagged on
|
|
53
|
+
crisp#598 for someone to confirm live. */
|
|
54
|
+
.crisp-contextmenu-content[data-variant="dark"] .crisp-contextmenu-item:hover { background: rgba(255,255,255,0.1); }
|
|
55
|
+
.crisp-contextmenu-content[data-variant="dark"] .crisp-contextmenu-item--danger { color: #ff5454; }
|
|
56
|
+
.crisp-contextmenu-content[data-variant="dark"] .crisp-contextmenu-item--danger svg { color: #ff5454; }
|
|
57
|
+
.crisp-contextmenu-content[data-variant="dark"] .crisp-contextmenu-item--danger:hover { background: rgba(255,84,84,0.12); }
|
|
58
|
+
.crisp-contextmenu-content[data-variant="dark"] .crisp-contextmenu-sep { background: rgba(255,255,255,0.1); }
|
|
35
59
|
`,
|
|
36
60
|
variants: {},
|
|
37
61
|
defaultVariants: {},
|