@crisp-ui-kit/crisp 0.55.2 → 0.55.4
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/cjs/components/columncalc/columncalc.recipe.js +20 -4
- package/dist/cjs/components/datatable/datatable.js +25 -16
- package/dist/cjs/components/datatable/datatable.recipe.js +46 -3
- package/dist/cjs/components/select/select.js +1 -1
- package/dist/cjs/components/table/table.recipe.js +20 -3
- package/dist/cjs/components/tabletoolbar/tabletoolbar.chrome.js +24 -3
- package/dist/columncalc.css +14 -4
- package/dist/columncalc.layered.css +14 -4
- package/dist/datatable.css +93 -79
- package/dist/datatable.layered.css +93 -79
- package/dist/esm/components/columncalc/columncalc.recipe.js +20 -4
- package/dist/esm/components/datatable/datatable.js +25 -16
- package/dist/esm/components/datatable/datatable.recipe.js +46 -3
- package/dist/esm/components/select/select.js +1 -1
- package/dist/esm/components/table/table.recipe.js +20 -3
- package/dist/esm/components/tabletoolbar/tabletoolbar.chrome.js +24 -3
- package/dist/styles.css +150 -95
- package/dist/styles.layered.css +150 -95
- package/dist/table.css +11 -3
- package/dist/table.layered.css +11 -3
- package/dist/tabletoolbar.css +32 -9
- package/dist/tabletoolbar.layered.css +32 -9
- package/dist/types/components/datatable/datatable.d.ts +17 -1
- package/dist/types/components/select/select.d.ts +5 -0
- package/package.json +1 -1
|
@@ -21,16 +21,32 @@ const config = (0, core_1.defineRecipe)({
|
|
|
21
21
|
radius, and datatable-grip and the embedded pickers zero theirs. An 8px
|
|
22
22
|
radius survived the chip-to-full-cell conversion and shipped rounded
|
|
23
23
|
until #282, so the spec check now guards it. */
|
|
24
|
-
/* The hit target is the WHOLE summary cell -- measured
|
|
24
|
+
/* The hit target is the WHOLE summary cell -- measured 246x36 with
|
|
25
25
|
padding 0 12px, not a content-sized chip. Clicking anywhere in the cell
|
|
26
|
-
opens the aggregation menu.
|
|
27
|
-
|
|
26
|
+
opens the aggregation menu.
|
|
27
|
+
No \`min-height\` (crisp#834, found reproducing the footer-row-38px-
|
|
28
|
+
not-36px bug in an isolated browser test, not table.recipe.ts's own
|
|
29
|
+
border/box-sizing, the hypothesis the issue itself flagged as
|
|
30
|
+
unconfirmed): the table cell's OWN \`height: tableRow\` (36px,
|
|
31
|
+
border-box) leaves 34px of content box once its 2px of top+bottom
|
|
32
|
+
border are counted, and \`min-height: tableRow\` here demanded 36px
|
|
33
|
+
of CONTENT height regardless -- overflowing the cell by 2px, which
|
|
34
|
+
auto-height table layout resolves by growing the whole row to fit,
|
|
35
|
+
landing at exactly the reported 38. \`height: 100%\` alone already
|
|
36
|
+
fills whatever the cell's real content box is (the doc comment above
|
|
37
|
+
this rule's own stated intent), with nothing left demanding more. */
|
|
38
|
+
& .crisp-columncalc-trigger { display: flex; align-items: center; gap: ${tokens_1.contract.space["1"]}; width: 100%; height: 100%; margin: 0; padding: 0 ${tokens_1.contract.space["3"]}; border: none; background: transparent; color: ${tokens_1.contract.fgMuted}; font-family: inherit; font-size: ${tokens_1.contract.text.lg}; font-weight: ${tokens_1.contract.weight.medium}; white-space: nowrap; cursor: pointer; transition: background-color .12s, color .12s; }
|
|
28
39
|
& .crisp-columncalc-trigger:hover { background: color-mix(in srgb, ${tokens_1.contract.fgPrimary} 4%, transparent); color: ${tokens_1.contract.fgPrimary}; }
|
|
29
40
|
& .crisp-columncalc-trigger svg { flex-shrink: 0; opacity: 0.7; }
|
|
30
41
|
& .crisp-columncalc-trigger[data-align="right"] { justify-content: flex-end; }
|
|
31
42
|
& .crisp-columncalc-placeholder { overflow: hidden; text-overflow: ellipsis; }
|
|
32
43
|
& .crisp-columncalc-value { color: ${tokens_1.contract.fgPrimary}; font-variant-numeric: tabular-nums; }
|
|
33
|
-
|
|
44
|
+
/* crisp#837: re-measured live against Attio's real footer calc label --
|
|
45
|
+
14px (was 13px, \`c.text.md\`, fixed on the shared trigger rule above)
|
|
46
|
+
and rgba(0,0,0,.63), the same color-mix(fgPrimary, 63%) figure this
|
|
47
|
+
file's own -op label already should have used instead of the flatter
|
|
48
|
+
\`fgMuted\` token (a close but not measured match). */
|
|
49
|
+
& .crisp-columncalc-op { color: color-mix(in srgb, ${tokens_1.contract.fgPrimary} 63%, transparent); }
|
|
34
50
|
& .crisp-columncalc-section { padding: ${tokens_1.contract.space["1.5"]} ${tokens_1.contract.space["2"]} ${tokens_1.contract.space["1"]}; font-size: ${tokens_1.contract.text.sm}; font-weight: ${tokens_1.contract.weight.medium}; color: ${tokens_1.contract.fgMuted}; }`,
|
|
35
51
|
variants: {},
|
|
36
52
|
defaultVariants: {},
|
|
@@ -628,6 +628,23 @@ function DataReferenceEditor({ col, value, isPending, onCommit, onCancel, }) {
|
|
|
628
628
|
onCommit(v);
|
|
629
629
|
} }) }));
|
|
630
630
|
}
|
|
631
|
+
// crisp/#842, confirmed live -- Attio's own country-picker popover: each
|
|
632
|
+
// option row is plain text, name (opaque) + dial code (muted 63%), NO flag
|
|
633
|
+
// anywhere -- not in the option row, and not in the CLOSED trigger either,
|
|
634
|
+
// which shows only the bare 2-letter ISO code ("MN") + a chevron. That's a
|
|
635
|
+
// different, more compact string than the option row's own "{name}
|
|
636
|
+
// +{dialCode}", so this builds `label` (the option row) and `triggerLabel`
|
|
637
|
+
// (the closed trigger) as two different values on the same `SelectOption`
|
|
638
|
+
// rather than one flag+name+dialcode string doing both jobs -- the shape
|
|
639
|
+
// #765 originally shipped, unmeasured, and #842 corrected.
|
|
640
|
+
function buildCountryOptions() {
|
|
641
|
+
return countries_1.COUNTRIES.map((c) => ({
|
|
642
|
+
value: c.iso2,
|
|
643
|
+
label: ((0, jsx_runtime_1.jsxs)("span", { className: "crisp-datatable-country-option", children: [(0, jsx_runtime_1.jsx)("span", { className: "crisp-datatable-country-name", children: c.name }), (0, jsx_runtime_1.jsxs)("span", { className: "crisp-datatable-country-dial", children: ["+", c.dialCode] })] })),
|
|
644
|
+
triggerLabel: c.iso2,
|
|
645
|
+
keywords: `${c.name} ${c.iso2} ${c.dialCode}`,
|
|
646
|
+
}));
|
|
647
|
+
}
|
|
631
648
|
// crisp/#765, confirmed live -- Attio's own Phone Number editor: a
|
|
632
649
|
// country-code select (ghost trigger, "MN ▾"), then a static, non-editable
|
|
633
650
|
// "+{dialCode}" prefix, then a borderless digits field -- measured
|
|
@@ -635,20 +652,16 @@ function DataReferenceEditor({ col, value, isPending, onCommit, onCancel, }) {
|
|
|
635
652
|
// value already measured elsewhere in this file (14px in both cases).
|
|
636
653
|
// Reuses crisp's own searchable `Select` for the country list -- the same
|
|
637
654
|
// popover chrome every other picker in this file already uses -- rather
|
|
638
|
-
// than inventing new country-picker chrome; the
|
|
639
|
-
//
|
|
640
|
-
//
|
|
641
|
-
//
|
|
655
|
+
// than inventing new country-picker chrome; the panel/row chrome (radius,
|
|
656
|
+
// padding, row height) already lines up with `Select`'s own measured
|
|
657
|
+
// tokens (#842 re-confirmed this), so only the option content itself
|
|
658
|
+
// (`buildCountryOptions` above) needed a real fix.
|
|
642
659
|
function DataPhoneEditor({ col, value, isPending, onCommit, }) {
|
|
643
660
|
const parsed = parsePhoneValue(value);
|
|
644
661
|
const [iso2, setIso2] = React.useState(parsed?.iso2 ?? "US");
|
|
645
662
|
const [digits, setDigits] = React.useState(parsed?.digits ?? "");
|
|
646
663
|
const country = (0, countries_1.countryByIso2)(iso2);
|
|
647
|
-
const countryOptions =
|
|
648
|
-
value: c.iso2,
|
|
649
|
-
label: `${(0, countries_1.flagEmoji)(c.iso2)} ${c.name} +${c.dialCode}`,
|
|
650
|
-
keywords: `${c.name} ${c.iso2} ${c.dialCode}`,
|
|
651
|
-
}));
|
|
664
|
+
const countryOptions = buildCountryOptions();
|
|
652
665
|
const commit = (nextIso2, nextDigits) => onCommit(`${nextIso2}|${nextDigits}`);
|
|
653
666
|
return ((0, jsx_runtime_1.jsxs)("span", { className: "crisp-datatable-phone-editor", children: [(0, jsx_runtime_1.jsx)(select_1.Select, { variant: "ghost", searchable: true, "aria-label": `Edit ${col.key} country`, value: iso2, disabled: isPending, options: countryOptions, onValueChange: (v) => {
|
|
654
667
|
setIso2(v);
|
|
@@ -674,11 +687,7 @@ function DataPhoneMultiEditor({ col, value, isPending, onCommit, }) {
|
|
|
674
687
|
const committed = parsePhoneValues(value);
|
|
675
688
|
const [draftIso2, setDraftIso2] = React.useState("US");
|
|
676
689
|
const [draftDigits, setDraftDigits] = React.useState("");
|
|
677
|
-
const countryOptions =
|
|
678
|
-
value: c.iso2,
|
|
679
|
-
label: `${(0, countries_1.flagEmoji)(c.iso2)} ${c.name} +${c.dialCode}`,
|
|
680
|
-
keywords: `${c.name} ${c.iso2} ${c.dialCode}`,
|
|
681
|
-
}));
|
|
690
|
+
const countryOptions = buildCountryOptions();
|
|
682
691
|
const join = (list) => list.map((p) => `${p.iso2}|${p.digits}`).join(";");
|
|
683
692
|
const removeAt = (i) => onCommit(join(committed.filter((_, idx) => idx !== i)));
|
|
684
693
|
const commitDraft = () => {
|
|
@@ -752,7 +761,7 @@ function DataDateTimeEditor({ col, value, isPending, onCommit, }) {
|
|
|
752
761
|
commit(date, t);
|
|
753
762
|
} })] }));
|
|
754
763
|
}
|
|
755
|
-
function DataTable({ columns: columnsProp, data, selectable = false, selectedIds, onSelectionChange, selectAllLabel: selectAllLabelProp, selectRowLabel = (row) => `Select row ${row.id}`, selectionAnnouncement, columnMenu = false, onColumnsChange, columnOrder, onColumnOrderChange, groupBy, onRowsReorder, error, sort: sortProp, defaultSort, onSortChange, filterable = false, searchPlaceholder: searchPlaceholderProp, searchLabel: searchLabelProp, onEdit, onViewEditHistory, stickyFirst = true, stickyLast = false, scrollProps, locale: localeProp, avatarTone = "brand", surface, interactive = false, onRowClick, onAddRow, addRowLabel: addRowLabelProp, empty: emptyProp, showCount = true, countLabel, toolbar, height, className, reorderRowLabel: reorderRowLabelProp, openPreviewLabel: openPreviewLabelProp, sortAscendingLabel: sortAscendingLabelProp, sortDescendingLabel: sortDescendingLabelProp, moveColumnLeftLabel: moveColumnLeftLabelProp, moveColumnRightLabel: moveColumnRightLabelProp, editColumnLabel: editColumnLabelProp, hideColumnLabel: hideColumnLabelProp, renameColumnLabel: renameColumnLabelProp, viewSettingsLabel: viewSettingsLabelProp, columnsGroupLabel: columnsGroupLabelProp, cellMenuLabel: cellMenuLabelProp, copyLabel: copyLabelProp, pasteLabel: pasteLabelProp, viewEditHistoryLabel: viewEditHistoryLabelProp, clearValueLabel: clearValueLabelProp, ...rest }) {
|
|
764
|
+
function DataTable({ columns: columnsProp, data, selectable = false, selectedIds, onSelectionChange, selectAllLabel: selectAllLabelProp, selectRowLabel = (row) => `Select row ${row.id}`, selectionAnnouncement, columnMenu = false, onColumnsChange, columnOrder, onColumnOrderChange, groupBy, onRowsReorder, error, loading = false, loadingLabel = "Loading...", sort: sortProp, defaultSort, onSortChange, filterable = false, searchPlaceholder: searchPlaceholderProp, searchLabel: searchLabelProp, onEdit, onViewEditHistory, stickyFirst = true, stickyLast = false, scrollProps, locale: localeProp, avatarTone = "brand", surface, interactive = false, onRowClick, onAddRow, addRowLabel: addRowLabelProp, empty: emptyProp, showCount = true, countLabel, toolbar, height, className, reorderRowLabel: reorderRowLabelProp, openPreviewLabel: openPreviewLabelProp, sortAscendingLabel: sortAscendingLabelProp, sortDescendingLabel: sortDescendingLabelProp, moveColumnLeftLabel: moveColumnLeftLabelProp, moveColumnRightLabel: moveColumnRightLabelProp, editColumnLabel: editColumnLabelProp, hideColumnLabel: hideColumnLabelProp, renameColumnLabel: renameColumnLabelProp, viewSettingsLabel: viewSettingsLabelProp, columnsGroupLabel: columnsGroupLabelProp, cellMenuLabel: cellMenuLabelProp, copyLabel: copyLabelProp, pasteLabel: pasteLabelProp, viewEditHistoryLabel: viewEditHistoryLabelProp, clearValueLabel: clearValueLabelProp, ...rest }) {
|
|
756
765
|
// crisp/#471: same resolution order as TableToolbar's own — an explicit
|
|
757
766
|
// prop wins, then the provider's translation of the built-in English
|
|
758
767
|
// default, then that default itself with no provider in the tree.
|
|
@@ -2177,7 +2186,7 @@ function DataTable({ columns: columnsProp, data, selectable = false, selectedIds
|
|
|
2177
2186
|
} })) : columnMenu && !isPrimary ? ((0, jsx_runtime_1.jsxs)(menu_1.Menu, { align: "start", trigger: (0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-datatable-sort", "data-sorted": sortDirByKey.get(col.key), "aria-label": `${typeof col.header === "string"
|
|
2178
2187
|
? col.header
|
|
2179
2188
|
: col.key} ${crispMessages["column options"] ?? "column options"}`, children: inner }), children: [(0, jsx_runtime_1.jsx)(menu_1.MenuItem, { onSelect: () => setSort({ key: col.key, dir: "asc" }), children: sortAscendingLabel }), (0, jsx_runtime_1.jsx)(menu_1.MenuItem, { onSelect: () => setSort({ key: col.key, dir: "desc" }), children: sortDescendingLabel }), col.managed === false ? null : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(menu_1.MenuItem, { onSelect: () => moveColumn(col.key, -1), children: moveColumnLeftLabel }), (0, jsx_runtime_1.jsx)(menu_1.MenuItem, { onSelect: () => moveColumn(col.key, 1), children: moveColumnRightLabel }), (0, jsx_runtime_1.jsx)(menu_1.MenuItem, { onSelect: () => setRenaming(col.key), children: editColumnLabel }), (0, jsx_runtime_1.jsx)(menu_1.MenuItem, { onSelect: () => hideColumn(col.key), children: hideColumnLabel })] }))] })) : col.sortable && !isPrimary ? ((0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-datatable-sort", "data-sorted": sortDirByKey.get(col.key), onClick: () => cycleSort(col.key), children: inner })) : (inner), col.readOnly && ((0, jsx_runtime_1.jsx)(lucide_react_1.Lock, { className: "crisp-datatable-readonly", size: 12, strokeWidth: 1.75, "aria-hidden": "true" })), col.resizable !== false && ((0, jsx_runtime_1.jsx)("span", { className: "crisp-datatable-resize", "data-resizing": resizingKey === col.key ? "" : undefined, onPointerDown: (e) => startResize(col, e), onPointerMove: moveResize, onPointerUp: endResize }))] }, col.key));
|
|
2180
|
-
})] }) }), (0, jsx_runtime_1.jsx)(table_1.TableBody, { children: error ? ((0, jsx_runtime_1.jsx)(table_1.TableRow, { children: (0, jsx_runtime_1.jsx)(table_1.TableCell, { colSpan: totalCols, children: (0, jsx_runtime_1.jsx)("div", { className: "crisp-datatable-empty", children: error }) }) })) : rows.length === 0 ? ((0, jsx_runtime_1.jsx)(table_1.TableRow, { children: (0, jsx_runtime_1.jsx)(table_1.TableCell, { colSpan: totalCols, children: (0, jsx_runtime_1.jsx)("div", { className: "crisp-datatable-empty", children: empty }) }) })) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [topSpacerHeight > 0 && ((0, jsx_runtime_1.jsx)("tr", { "aria-hidden": "true", children: (0, jsx_runtime_1.jsx)("td", { colSpan: totalCols, style: {
|
|
2189
|
+
})] }) }), (0, jsx_runtime_1.jsx)(table_1.TableBody, { children: loading ? ((0, jsx_runtime_1.jsx)(table_1.TableRow, { children: (0, jsx_runtime_1.jsx)(table_1.TableCell, { colSpan: totalCols, children: (0, jsx_runtime_1.jsxs)("div", { className: "crisp-datatable-loading", children: [(0, jsx_runtime_1.jsx)(spinner_1.Spinner, { size: 16 }), loadingLabel] }) }) })) : error ? ((0, jsx_runtime_1.jsx)(table_1.TableRow, { children: (0, jsx_runtime_1.jsx)(table_1.TableCell, { colSpan: totalCols, children: (0, jsx_runtime_1.jsx)("div", { className: "crisp-datatable-empty", children: error }) }) })) : rows.length === 0 ? ((0, jsx_runtime_1.jsx)(table_1.TableRow, { children: (0, jsx_runtime_1.jsx)(table_1.TableCell, { colSpan: totalCols, children: (0, jsx_runtime_1.jsx)("div", { className: "crisp-datatable-empty", children: empty }) }) })) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [topSpacerHeight > 0 && ((0, jsx_runtime_1.jsx)("tr", { "aria-hidden": "true", children: (0, jsx_runtime_1.jsx)("td", { colSpan: totalCols, style: {
|
|
2181
2190
|
height: topSpacerHeight,
|
|
2182
2191
|
padding: 0,
|
|
2183
2192
|
border: "none",
|
|
@@ -68,8 +68,17 @@ const config = (0, core_1.defineRecipe)({
|
|
|
68
68
|
in the whole product and making every interactive column's button
|
|
69
69
|
double it to 24px. Scoping through \`.crisp-table\` adds a second
|
|
70
70
|
class to the selector (0,2,1 beats 0,1,1) so this can never lose a
|
|
71
|
-
specificity tie to source order again.
|
|
72
|
-
|
|
71
|
+
specificity tie to source order again.
|
|
72
|
+
\`thead th\`, not a bare \`th\` (crisp#829): #779 made the primary
|
|
73
|
+
column's BODY cell a real \`<th scope="row">\` for a11y, and a bare
|
|
74
|
+
\`th\` here matches that one too, zeroing an inset nothing else
|
|
75
|
+
supplies -- the primary column's text sat flush left, one column's
|
|
76
|
+
cell no longer matching its own header or any sibling \`<td>\`. Only
|
|
77
|
+
the header row's own \`<th>\` needs zero padding (its sort BUTTON
|
|
78
|
+
owns the inset); the body row's \`<th scope="row">\` has no button,
|
|
79
|
+
and needs the SAME base 12px (table.recipe.ts, #465) every other
|
|
80
|
+
cell already gets. */
|
|
81
|
+
& .crisp-table thead th { padding: 0; }
|
|
73
82
|
/* Same wrapper trap as AUTHORING.md Lesson 40 (ColumnCalc's summary trigger):
|
|
74
83
|
Menu wraps its trigger in a flex div that sizes to its content, so a
|
|
75
84
|
long header grew past the column instead of filling it -- with nothing
|
|
@@ -126,7 +135,22 @@ const config = (0, core_1.defineRecipe)({
|
|
|
126
135
|
& .crisp-datatable-sort[data-sorted] .crisp-datatable-chevron { opacity: 1; color: ${tokens_1.contract.fgPrimary}; }
|
|
127
136
|
/* sticky first column(s) + horizontal scroll shadow — RecordsTable's feel */
|
|
128
137
|
& th[data-sticky], & td[data-sticky] { position: sticky; z-index: 1; background: ${tokens_1.contract.bgSurface}; }
|
|
129
|
-
|
|
138
|
+
/* \`thead th[data-sticky]\`, not a bare \`th[data-sticky]\` (crisp#848, same
|
|
139
|
+
bare-\`th\` family as #829/#833/#835): this z-index:3 was written for
|
|
140
|
+
the HEADER's own sticky corner cell (sticky column x sticky header),
|
|
141
|
+
meant to win over everything else. Unscoped, it also matched the
|
|
142
|
+
primary column's BODY cell -- a real \`<th scope="row" data-sticky>\`
|
|
143
|
+
since #779 -- giving every scrolled body row's own sticky cell a
|
|
144
|
+
HIGHER z-index than the sticky FOOTER's (2), so it painted over the
|
|
145
|
+
footer instead of the footer showing through. Confirmed live on a
|
|
146
|
+
real bounded-height, 25-row table (GRC's Risk register): scrolled
|
|
147
|
+
partway, \`elementFromPoint\` at the footer's own on-screen cell
|
|
148
|
+
resolved to a body row's \`<th>\`, not the footer's \`<td>\`, and that
|
|
149
|
+
\`<th>\`'s computed z-index read 3. Scoped to \`thead\`, a body row's
|
|
150
|
+
own sticky \`<th>\` now falls back to the base rule above (z-index 1,
|
|
151
|
+
correct -- the same as a plain sticky \`<td>\`), and the footer's own
|
|
152
|
+
explicit z-index below is unaffected either way. */
|
|
153
|
+
& thead th[data-sticky] { z-index: 3; }
|
|
130
154
|
/* #554: a footer sticky-column cell sits in the bottom-sticky row (table.recipe.ts's
|
|
131
155
|
\`tfoot td { z-index: 2 }\`), which \`td[data-sticky]\`'s own higher-specificity
|
|
132
156
|
\`z-index: 1\` would otherwise undercut -- keep it level with the row it's in. */
|
|
@@ -183,6 +207,16 @@ const config = (0, core_1.defineRecipe)({
|
|
|
183
207
|
& .crisp-datatable-refoption-avatar { flex-shrink: 0; }
|
|
184
208
|
& .crisp-datatable-refoption--create { color: ${tokens_1.contract.fgMuted}; }
|
|
185
209
|
& .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}; }
|
|
210
|
+
/* #826 -- the full-body loading state (a filter/sort re-query, or a
|
|
211
|
+
fresh page boot), measured live on Attio's Companies grid on both
|
|
212
|
+
triggers: icon+text centred in the row-body area, 10px gap, 14px/500
|
|
213
|
+
weight/20px line-height, both icon and text in the SAME muted
|
|
214
|
+
rgba(0,0,0,0.63) -- the same color-mix figure this file already uses
|
|
215
|
+
for other muted text (e.g. -phone-dial). Distinct from the cold
|
|
216
|
+
in-place row load (blank, no indicator) and the warm-navigation
|
|
217
|
+
per-cell shimmer (#599) -- neither of those swaps the whole body. */
|
|
218
|
+
& .crisp-datatable-loading { display: flex; align-items: center; justify-content: center; gap: 10px; padding: ${tokens_1.contract.space["8"]} ${tokens_1.contract.space["3"]}; color: color-mix(in srgb, ${tokens_1.contract.fgPrimary} 63%, transparent); font-size: ${tokens_1.contract.text.lg}; font-weight: ${tokens_1.contract.weight.medium}; line-height: 20px; }
|
|
219
|
+
& .crisp-datatable-loading .crisp-spinner { border-top-color: color-mix(in srgb, ${tokens_1.contract.fgPrimary} 63%, transparent); }
|
|
186
220
|
/* #579: click-to-expand overlay for a truncated cell -- a read-only,
|
|
187
221
|
full-value card, settled live as click (not Attio's usual
|
|
188
222
|
hover-expand) and measured position: absolute, unlike this file's
|
|
@@ -294,6 +328,15 @@ const config = (0, core_1.defineRecipe)({
|
|
|
294
328
|
& .crisp-datatable-phone-dial { color: color-mix(in srgb, ${tokens_1.contract.fgPrimary} 63%, transparent); }
|
|
295
329
|
& .crisp-datatable-phone-editor { display: flex; align-items: center; gap: ${tokens_1.contract.space["1"]}; height: ${tokens_1.contract.size.tableRow}; padding: 0 ${tokens_1.contract.space["2"]}; }
|
|
296
330
|
& .crisp-datatable-phone-editor input { flex: 1; min-width: 0; }
|
|
331
|
+
/* country-picker option row (#842) -- confirmed live: Attio's own popover
|
|
332
|
+
is plain text, no flag anywhere (not the option row, not the closed
|
|
333
|
+
trigger), name opaque + dial code muted rgba(0,0,0,0.63) -- the SAME
|
|
334
|
+
color-mix figure as -phone-dial above. The internal name/dial gap
|
|
335
|
+
wasn't independently measured this pass (Attio's own two <div>s gave
|
|
336
|
+
no exact number); reused space["1"], the same gap -phone already uses
|
|
337
|
+
for its own flag/dial/digits row. */
|
|
338
|
+
& .crisp-datatable-country-option { display: flex; align-items: center; gap: ${tokens_1.contract.space["1"]}; }
|
|
339
|
+
& .crisp-datatable-country-dial { color: color-mix(in srgb, ${tokens_1.contract.fgPrimary} 63%, transparent); }
|
|
297
340
|
/* multi-value phone (#816) -- confirmed live (a real custom "Multiple
|
|
298
341
|
values" Phone Number attribute, and the built-in People "Phone
|
|
299
342
|
numbers" field): the collapsed cell is a 4px-gap flex row of real
|
|
@@ -204,7 +204,7 @@ function Select({ options, value, defaultValue, onValueChange, name, required, p
|
|
|
204
204
|
return ((0, jsx_runtime_1.jsxs)("div", { ref: rootRef, className: (0, select_recipe_1.selectRecipe)({ variant, width, invalid, className }), ...rest, children: [name && (selected || required) && ((0, jsx_runtime_1.jsx)("input", { className: "crisp-formvalue", style: core_1.formValueStyle, name: name, value: selected ?? "", required: required, tabIndex: -1, "aria-hidden": "true",
|
|
205
205
|
// controlled by the listbox above; `readOnly` would bar it from
|
|
206
206
|
// validation, which is the only reason this input is not hidden
|
|
207
|
-
onChange: () => { } })), (0, jsx_runtime_1.jsxs)("button", { ref: triggerRef, type: "button", id: id, role: "combobox", "aria-haspopup": "listbox", "aria-expanded": open, "aria-controls": open ? listboxId : undefined, "aria-activedescendant": searchable ? undefined : activeId, "aria-label": ariaLabel, "aria-invalid": invalid === true || undefined, disabled: disabled, "data-open": open ? "" : undefined, className: "crisp-select-trigger", onClick: () => (open ? setOpen(false) : openMenu()), onKeyDown: onTriggerKeyDown, children: [(0, jsx_runtime_1.jsx)("span", { className: "crisp-select-value", children: selectedOption ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [selectedOption.color && ((0, jsx_runtime_1.jsx)("span", { className: "crisp-select-option-dot", style: { background: selectedOption.color } })), selectedOption.label] })) : ((0, jsx_runtime_1.jsx)("span", { className: "crisp-select-placeholder", children: placeholder })) }), (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronDown, { className: "crisp-select-chevron", size: 16, strokeWidth: 1.75 })] }), open && ((0, jsx_runtime_1.jsx)(core_1.FloatingPortal, { sourceRef: rootRef, children: (0, jsx_runtime_1.jsxs)("div", { ref: listboxRef, id: listboxId, className: "crisp-select-listbox", role: "listbox", "aria-label": ariaLabel, "data-up": up ? "" : undefined, style: pos, children: [searchable && ((0, jsx_runtime_1.jsxs)("div", { className: "crisp-select-search", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Search, { size: 15, strokeWidth: 1.75 }), (0, jsx_runtime_1.jsx)("input", { ref: searchRef, type: "text", placeholder: searchPlaceholder, "aria-label": searchPlaceholder, "aria-controls": listboxId, "aria-activedescendant": activeId, value: query, onChange: (e) => {
|
|
207
|
+
onChange: () => { } })), (0, jsx_runtime_1.jsxs)("button", { ref: triggerRef, type: "button", id: id, role: "combobox", "aria-haspopup": "listbox", "aria-expanded": open, "aria-controls": open ? listboxId : undefined, "aria-activedescendant": searchable ? undefined : activeId, "aria-label": ariaLabel, "aria-invalid": invalid === true || undefined, disabled: disabled, "data-open": open ? "" : undefined, className: "crisp-select-trigger", onClick: () => (open ? setOpen(false) : openMenu()), onKeyDown: onTriggerKeyDown, children: [(0, jsx_runtime_1.jsx)("span", { className: "crisp-select-value", children: selectedOption ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [selectedOption.color && ((0, jsx_runtime_1.jsx)("span", { className: "crisp-select-option-dot", style: { background: selectedOption.color } })), selectedOption.triggerLabel ?? selectedOption.label] })) : ((0, jsx_runtime_1.jsx)("span", { className: "crisp-select-placeholder", children: placeholder })) }), (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronDown, { className: "crisp-select-chevron", size: 16, strokeWidth: 1.75 })] }), open && ((0, jsx_runtime_1.jsx)(core_1.FloatingPortal, { sourceRef: rootRef, children: (0, jsx_runtime_1.jsxs)("div", { ref: listboxRef, id: listboxId, className: "crisp-select-listbox", role: "listbox", "aria-label": ariaLabel, "data-up": up ? "" : undefined, style: pos, children: [searchable && ((0, jsx_runtime_1.jsxs)("div", { className: "crisp-select-search", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Search, { size: 15, strokeWidth: 1.75 }), (0, jsx_runtime_1.jsx)("input", { ref: searchRef, type: "text", placeholder: searchPlaceholder, "aria-label": searchPlaceholder, "aria-controls": listboxId, "aria-activedescendant": activeId, value: query, onChange: (e) => {
|
|
208
208
|
setQuery(e.target.value);
|
|
209
209
|
setHighlight(0);
|
|
210
210
|
onSearchChange?.(e.target.value);
|
|
@@ -28,8 +28,25 @@ const config = (0, core_1.defineRecipe)({
|
|
|
28
28
|
/* #554: sticky on the vertical axis, mirroring RecordsTable's own
|
|
29
29
|
\`.crisp-recordstable-th\`. z-index 2 sits above a plain body cell (auto)
|
|
30
30
|
and below a sticky-column corner cell (DataTable's \`th[data-sticky]\`,
|
|
31
|
-
z-index 3), so the corner always wins.
|
|
32
|
-
|
|
31
|
+
z-index 3), so the corner always wins.
|
|
32
|
+
\`thead th\`, not a bare \`th\` (crisp#833/#835): DataTable's primary
|
|
33
|
+
column renders its BODY cell as a real \`<th scope="row">\` (#779, for
|
|
34
|
+
a11y), and a bare \`th\` here matched that one too -- every property in
|
|
35
|
+
this rule leaked onto it: \`height: tableHeader\` (40px) stretched the
|
|
36
|
+
whole body row to 40px instead of 36 (#833, confirmed live on the
|
|
37
|
+
showcase, every td in the row equalized to the one th); \`color:
|
|
38
|
+
color-mix(...63%...)\` muted the primary column's own text below every
|
|
39
|
+
sibling \`<td>\`'s full-opacity color (#835, re-measured live against
|
|
40
|
+
Attio's own header labels -- including a genuinely computed one,
|
|
41
|
+
"Twitter follower count" -- all fully opaque \`rgb(16,17,18)\`, no
|
|
42
|
+
muting at all, so this drops the color-mix entirely rather than
|
|
43
|
+
narrowing it); \`position:sticky; top:0\` tried to stick a BODY row
|
|
44
|
+
to the scroll container's top; the extra \`border-top\` doubled that
|
|
45
|
+
row's own grid line. \`thead th\` simply never matches
|
|
46
|
+
\`tbody th[scope=row]\` at all, so none of this reaches it; the base
|
|
47
|
+
\`& th, & td\` rule above (36px, one border-bottom, full opacity via
|
|
48
|
+
\`& table\`'s own color) applies to it like every other cell. */
|
|
49
|
+
& thead th { position: sticky; top: 0; z-index: 2; height: ${tokens_1.contract.size.tableHeader}; background: ${tokens_1.contract.bgSurface}; border-top: 1px solid ${tokens_1.contract.borderSubtle}; font-size: ${tokens_1.contract.text.lg}; font-weight: ${tokens_1.contract.weight.medium}; letter-spacing: -0.14px; color: ${tokens_1.contract.fgPrimary}; user-select: none; }
|
|
33
50
|
& td { color: ${tokens_1.contract.fgPrimary}; background: ${tokens_1.contract.bgSurface}; }
|
|
34
51
|
& th:last-child, & td:last-child { border-inline-end: none; }
|
|
35
52
|
& tbody tr:last-child td { border-bottom: none; }
|
|
@@ -56,7 +73,7 @@ const config = (0, core_1.defineRecipe)({
|
|
|
56
73
|
// bottom one: the container's edge wins, cells never double it.
|
|
57
74
|
surface: {
|
|
58
75
|
card: `border: 1px solid ${tokens_1.contract.borderSubtle}; border-radius: ${tokens_1.contract.radius.xl}; overflow: hidden;
|
|
59
|
-
& th { border-top: none; }
|
|
76
|
+
& thead th { border-top: none; }
|
|
60
77
|
& tfoot td { border-bottom: none; }`,
|
|
61
78
|
bleed: `border: none; border-radius: 0;`,
|
|
62
79
|
},
|
|
@@ -53,7 +53,14 @@ const toolbarChromeHead = (p) => ` & .${namespace_1.CLASS_PREFIX}${p}-toolbar
|
|
|
53
53
|
& .${namespace_1.CLASS_PREFIX}${p}-search svg { flex-shrink: 0; }
|
|
54
54
|
& .${namespace_1.CLASS_PREFIX}${p}-search input { border: none; outline: none; background: transparent; font-family: inherit; font-size: ${tokens_1.contract.text.md}; color: ${tokens_1.contract.fgPrimary}; width: 180px; }
|
|
55
55
|
& .${namespace_1.CLASS_PREFIX}${p}-search input::placeholder { color: ${tokens_1.contract.fgMuted}; }
|
|
56
|
-
|
|
56
|
+
/* crisp/#830: every trigger label here (Sort, Filter, View settings,
|
|
57
|
+
Import/Export -- all compose this one shared .tool rule) read the
|
|
58
|
+
"md" text size, 13px. Re-measured on Attio's own trigger LEAF node
|
|
59
|
+
(the button root itself inherits Attio's own 10px rem base and is a
|
|
60
|
+
red herring, the same trap the original "10px font" layout-audit
|
|
61
|
+
finding fell into): every label leaf is 14px/500/lh20, the "lg" text
|
|
62
|
+
size. */
|
|
63
|
+
& .${namespace_1.CLASS_PREFIX}${p}-tool { display: inline-flex; align-items: center; gap: ${tokens_1.contract.space["1"]}; height: ${tokens_1.contract.size.row}; margin: 0; padding: 0 ${tokens_1.contract.space["2"]}; border: none; border-radius: ${tokens_1.contract.radius.md}; background: ${tokens_1.contract.bgSurface}; box-shadow: inset 0 0 0 1px ${tokens_1.contract.borderDivider}; color: ${tokens_1.contract.fgPrimary}; font-family: inherit; font-size: ${tokens_1.contract.text.lg}; font-weight: ${tokens_1.contract.weight.medium}; white-space: nowrap; cursor: pointer; transition: background-color .12s; }
|
|
57
64
|
& .${namespace_1.CLASS_PREFIX}${p}-tool:hover { background: ${tokens_1.contract.bgRaised}; }
|
|
58
65
|
& .${namespace_1.CLASS_PREFIX}${p}-tool svg { flex-shrink: 0; color: ${tokens_1.contract.fgMuted}; }
|
|
59
66
|
/* crisp/#772: re-measured live on Sort with an actual sort applied --
|
|
@@ -78,8 +85,22 @@ const toolbarChromeHead = (p) => ` & .${namespace_1.CLASS_PREFIX}${p}-toolbar
|
|
|
78
85
|
same reasoning --ghost above already uses. Only wired into
|
|
79
86
|
TableToolbar itself (tabletoolbar.tsx) this pass -- RecordsTable's
|
|
80
87
|
own Sort/Filter triggers share this same chrome function but are
|
|
81
|
-
deprecated, not touched here.
|
|
82
|
-
|
|
88
|
+
deprecated, not touched here.
|
|
89
|
+
crisp/#844, correcting #831: the label colour was added here later,
|
|
90
|
+
muting only the FILTER trigger (via a "--filter" modifier), on the
|
|
91
|
+
assumption that was a Filter-specific tone distinct from Sort's.
|
|
92
|
+
Re-measured live and that premise was wrong -- Sort's own resting
|
|
93
|
+
label ("Sort", no condition set) is the exact SAME muted rgba(0,0,0,.5)
|
|
94
|
+
as Filter's, not full-strength. (The rgba(0,0,0,.63) that looked like
|
|
95
|
+
"Sort's colour" in an earlier pass is a different thing entirely: the
|
|
96
|
+
"Sorted by" PREFIX word inside an ACTIVE sort chip, #788 -- a
|
|
97
|
+
compound-label treatment that only exists once a sort is applied, not
|
|
98
|
+
a resting-state colour.) --dashed is currently only ever applied to
|
|
99
|
+
Sort's and Filter's own empty triggers (never View settings/
|
|
100
|
+
Import-Export, which stay full-strength always, confirmed live), so
|
|
101
|
+
the mute belongs on --dashed itself, not gated behind a Filter-only
|
|
102
|
+
modifier. */
|
|
103
|
+
& .${namespace_1.CLASS_PREFIX}${p}-tool--dashed { position: relative; box-shadow: none; background: color-mix(in srgb, ${tokens_1.contract.fgPrimary} 2%, transparent); color: color-mix(in srgb, ${tokens_1.contract.fgPrimary} 50%, transparent); }
|
|
83
104
|
& .${namespace_1.CLASS_PREFIX}${p}-tool--dashed::before { content: ""; position: absolute; inset: 0; border-radius: inherit; border: 1px dashed color-mix(in srgb, ${tokens_1.contract.fgPrimary} 6%, transparent); pointer-events: none; }
|
|
84
105
|
& .${namespace_1.CLASS_PREFIX}${p}-tool--dashed:hover { background: color-mix(in srgb, ${tokens_1.contract.fgPrimary} 4%, transparent); }
|
|
85
106
|
& .${namespace_1.CLASS_PREFIX}${p}-toolgroup { position: relative; display: inline-flex; }
|
package/dist/columncalc.css
CHANGED
|
@@ -7,16 +7,26 @@
|
|
|
7
7
|
radius, and datatable-grip and the embedded pickers zero theirs. An 8px
|
|
8
8
|
radius survived the chip-to-full-cell conversion and shipped rounded
|
|
9
9
|
until #282, so the spec check now guards it. */
|
|
10
|
-
/* The hit target is the WHOLE summary cell -- measured
|
|
10
|
+
/* The hit target is the WHOLE summary cell -- measured 246x36 with
|
|
11
11
|
padding 0 12px, not a content-sized chip. Clicking anywhere in the cell
|
|
12
|
-
opens the aggregation menu.
|
|
13
|
-
|
|
12
|
+
opens the aggregation menu.
|
|
13
|
+
No `min-height` (crisp#834, found reproducing the footer-row-38px-
|
|
14
|
+
not-36px bug in an isolated browser test, not table.recipe.ts's own border/box-sizing, the hypothesis the issue itself flagged as unconfirmed): the table cell's OWN `height: tableRow` (36px,
|
|
15
|
+
border-box) leaves 34px of content box once its 2px of top+bottom
|
|
16
|
+
border are counted, and `min-height: tableRow` here demanded 36px
|
|
17
|
+
of CONTENT height regardless -- overflowing the cell by 2px, which
|
|
18
|
+
auto-height table layout resolves by growing the whole row to fit,
|
|
19
|
+
landing at exactly the reported 38. `height: 100%` alone already
|
|
20
|
+
fills whatever the cell's real content box is (the doc comment above this rule's own stated intent), with nothing left demanding more. */
|
|
21
|
+
& .crisp-columncalc-trigger { display: flex; align-items: center; gap: var(--crisp-space-1); width: 100%; height: 100%; margin: 0; padding: 0 var(--crisp-space-3); border: none; background: transparent; color: var(--crisp-fg-muted); font-family: inherit; font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-medium); white-space: nowrap; cursor: pointer; transition: background-color .12s, color .12s; }
|
|
14
22
|
& .crisp-columncalc-trigger:hover { background: color-mix(in srgb, var(--crisp-fg-primary) 4%, transparent); color: var(--crisp-fg-primary); }
|
|
15
23
|
& .crisp-columncalc-trigger svg { flex-shrink: 0; opacity: 0.7; }
|
|
16
24
|
& .crisp-columncalc-trigger[data-align="right"] { justify-content: flex-end; }
|
|
17
25
|
& .crisp-columncalc-placeholder { overflow: hidden; text-overflow: ellipsis; }
|
|
18
26
|
& .crisp-columncalc-value { color: var(--crisp-fg-primary); font-variant-numeric: tabular-nums; }
|
|
19
|
-
|
|
27
|
+
/* crisp#837: re-measured live against Attio's real footer calc label -- 14px (was 13px, `c.text.md`, fixed on the shared trigger rule above) and rgba(0,0,0,.63), the same color-mix(fgPrimary, 63%) figure this file's own -op label already should have used instead of the flatter
|
|
28
|
+
`fgMuted` token (a close but not measured match). */
|
|
29
|
+
& .crisp-columncalc-op { color: color-mix(in srgb, var(--crisp-fg-primary) 63%, transparent); }
|
|
20
30
|
& .crisp-columncalc-section { 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); }
|
|
21
31
|
}
|
|
22
32
|
|
|
@@ -8,16 +8,26 @@
|
|
|
8
8
|
radius, and datatable-grip and the embedded pickers zero theirs. An 8px
|
|
9
9
|
radius survived the chip-to-full-cell conversion and shipped rounded
|
|
10
10
|
until #282, so the spec check now guards it. */
|
|
11
|
-
/* The hit target is the WHOLE summary cell -- measured
|
|
11
|
+
/* The hit target is the WHOLE summary cell -- measured 246x36 with
|
|
12
12
|
padding 0 12px, not a content-sized chip. Clicking anywhere in the cell
|
|
13
|
-
opens the aggregation menu.
|
|
14
|
-
|
|
13
|
+
opens the aggregation menu.
|
|
14
|
+
No `min-height` (crisp#834, found reproducing the footer-row-38px-
|
|
15
|
+
not-36px bug in an isolated browser test, not table.recipe.ts's own border/box-sizing, the hypothesis the issue itself flagged as unconfirmed): the table cell's OWN `height: tableRow` (36px,
|
|
16
|
+
border-box) leaves 34px of content box once its 2px of top+bottom
|
|
17
|
+
border are counted, and `min-height: tableRow` here demanded 36px
|
|
18
|
+
of CONTENT height regardless -- overflowing the cell by 2px, which
|
|
19
|
+
auto-height table layout resolves by growing the whole row to fit,
|
|
20
|
+
landing at exactly the reported 38. `height: 100%` alone already
|
|
21
|
+
fills whatever the cell's real content box is (the doc comment above this rule's own stated intent), with nothing left demanding more. */
|
|
22
|
+
& .crisp-columncalc-trigger { display: flex; align-items: center; gap: var(--crisp-space-1); width: 100%; height: 100%; margin: 0; padding: 0 var(--crisp-space-3); border: none; background: transparent; color: var(--crisp-fg-muted); font-family: inherit; font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-medium); white-space: nowrap; cursor: pointer; transition: background-color .12s, color .12s; }
|
|
15
23
|
& .crisp-columncalc-trigger:hover { background: color-mix(in srgb, var(--crisp-fg-primary) 4%, transparent); color: var(--crisp-fg-primary); }
|
|
16
24
|
& .crisp-columncalc-trigger svg { flex-shrink: 0; opacity: 0.7; }
|
|
17
25
|
& .crisp-columncalc-trigger[data-align="right"] { justify-content: flex-end; }
|
|
18
26
|
& .crisp-columncalc-placeholder { overflow: hidden; text-overflow: ellipsis; }
|
|
19
27
|
& .crisp-columncalc-value { color: var(--crisp-fg-primary); font-variant-numeric: tabular-nums; }
|
|
20
|
-
|
|
28
|
+
/* crisp#837: re-measured live against Attio's real footer calc label -- 14px (was 13px, `c.text.md`, fixed on the shared trigger rule above) and rgba(0,0,0,.63), the same color-mix(fgPrimary, 63%) figure this file's own -op label already should have used instead of the flatter
|
|
29
|
+
`fgMuted` token (a close but not measured match). */
|
|
30
|
+
& .crisp-columncalc-op { color: color-mix(in srgb, var(--crisp-fg-primary) 63%, transparent); }
|
|
21
31
|
& .crisp-columncalc-section { 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); }
|
|
22
32
|
}
|
|
23
33
|
|