@crisp-ui-kit/crisp 0.55.1 → 0.55.3

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.
@@ -8,7 +8,9 @@
8
8
  & .crisp-tabletoolbar-search svg { flex-shrink: 0; }
9
9
  & .crisp-tabletoolbar-search input { border: none; outline: none; background: transparent; font-family: inherit; font-size: var(--crisp-text-md); color: var(--crisp-fg-primary); width: 180px; }
10
10
  & .crisp-tabletoolbar-search input::placeholder { color: var(--crisp-fg-muted); }
11
- & .crisp-tabletoolbar-tool { display: inline-flex; align-items: center; gap: var(--crisp-space-1); height: var(--crisp-size-row); margin: 0; padding: 0 var(--crisp-space-2); border: none; border-radius: var(--crisp-radius-md); background: var(--crisp-bg-surface); box-shadow: inset 0 0 0 1px var(--crisp-border-divider); color: var(--crisp-fg-primary); font-family: inherit; font-size: var(--crisp-text-md); font-weight: var(--crisp-weight-medium); white-space: nowrap; cursor: pointer; transition: background-color .12s; }
11
+ /* crisp/#830: every trigger label here (Sort, Filter, View settings, Import/Export -- all compose this one shared .tool rule) read the "md" text size, 13px. Re-measured on Attio's own trigger LEAF node
12
+ (the button root itself inherits Attio's own 10px rem base and is a red herring, the same trap the original "10px font" layout-audit finding fell into): every label leaf is 14px/500/lh20, the "lg" text size. */
13
+ & .crisp-tabletoolbar-tool { display: inline-flex; align-items: center; gap: var(--crisp-space-1); height: var(--crisp-size-row); margin: 0; padding: 0 var(--crisp-space-2); border: none; border-radius: var(--crisp-radius-md); background: var(--crisp-bg-surface); box-shadow: inset 0 0 0 1px var(--crisp-border-divider); color: var(--crisp-fg-primary); font-family: inherit; font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-medium); white-space: nowrap; cursor: pointer; transition: background-color .12s; }
12
14
  & .crisp-tabletoolbar-tool:hover { background: var(--crisp-bg-raised); }
13
15
  & .crisp-tabletoolbar-tool svg { flex-shrink: 0; color: var(--crisp-fg-muted); }
14
16
  /* crisp/#772: re-measured live on Sort with an actual sort applied -- an active trigger's own fill is white (bgSurface), the same as the
@@ -27,8 +29,17 @@
27
29
  an actual sort applied. color-mix (not the raw rgba Attio computes
28
30
  to) so both the border and the fill flip correctly in dark mode, the
29
31
  same reasoning --ghost above already uses. Only wired into
30
- TableToolbar itself (tabletoolbar.tsx) this pass -- RecordsTable's own Sort/Filter triggers share this same chrome function but are deprecated, not touched here. */
31
- & .crisp-tabletoolbar-tool--dashed { position: relative; box-shadow: none; background: color-mix(in srgb, var(--crisp-fg-primary) 2%, transparent); }
32
+ TableToolbar itself (tabletoolbar.tsx) this pass -- RecordsTable's own Sort/Filter triggers share this same chrome function but are deprecated, not touched here. crisp/#844, correcting #831: the label colour was added here later, muting only the FILTER trigger (via a "--filter" modifier), on the assumption that was a Filter-specific tone distinct from Sort's.
33
+ Re-measured live and that premise was wrong -- Sort's own resting label ("Sort", no condition set) is the exact SAME muted rgba(0,0,0,.5) as Filter's, not full-strength. (The rgba(0,0,0,.63) that looked like
34
+ "Sort's colour" in an earlier pass is a different thing entirely: the
35
+ "Sorted by" PREFIX word inside an ACTIVE sort chip, #788 -- a
36
+ compound-label treatment that only exists once a sort is applied, not
37
+ a resting-state colour.) --dashed is currently only ever applied to
38
+ Sort's and Filter's own empty triggers (never View settings/
39
+ Import-Export, which stay full-strength always, confirmed live), so
40
+ the mute belongs on --dashed itself, not gated behind a Filter-only
41
+ modifier. */
42
+ & .crisp-tabletoolbar-tool--dashed { position: relative; box-shadow: none; background: color-mix(in srgb, var(--crisp-fg-primary) 2%, transparent); color: color-mix(in srgb, var(--crisp-fg-primary) 50%, transparent); }
32
43
  & .crisp-tabletoolbar-tool--dashed::before { content: ""; position: absolute; inset: 0; border-radius: inherit; border: 1px dashed color-mix(in srgb, var(--crisp-fg-primary) 6%, transparent); pointer-events: none; }
33
44
  & .crisp-tabletoolbar-tool--dashed:hover { background: color-mix(in srgb, var(--crisp-fg-primary) 4%, transparent); }
34
45
  & .crisp-tabletoolbar-toolgroup { position: relative; display: inline-flex; }
@@ -68,7 +79,11 @@
68
79
  & .crisp-tabletoolbar-menu-sep { height: 1px; border: none; background: var(--crisp-border-subtle); margin: var(--crisp-space-0-5) 0; }
69
80
  /* #592: the Filter field picker's nested-scope header (back control +
70
81
  search, once drilled into a nestedColumns column) and a search
71
- result's own breadcrumb row ("Team › Twitter follower count", surfaced by the SAME query matching a nested column). menu-back reuses the established 28px icon-only control pattern this file already uses twice (sort-remove, the kebab segment) rather than an independent measurement of Attio's own back arrow. The breadcrumb
82
+ result's own breadcrumb row ("Team › Twitter follower count",
83
+ surfaced by the SAME query matching a nested column). menu-back
84
+ reuses the established 28px icon-only control pattern this file
85
+ already uses twice (sort-remove, the kebab segment) rather than
86
+ an independent measurement of Attio's own back arrow. The breadcrumb
72
87
  colours ARE measured live: the parent segment muted at the same
73
88
  exact color-mix 63% sort-learn above already uses, the
74
89
  separator chevron and leaf segment both full fgPrimary (the
@@ -112,9 +127,15 @@
112
127
  & .crisp-tabletoolbar-fcond[data-complete] .crisp-tabletoolbar-fcond-seg--val { background: var(--crisp-bg-surface); }
113
128
  & .crisp-tabletoolbar-fcond-seg--field { border-radius: var(--crisp-radius-md) 0 0 var(--crisp-radius-md); font-weight: var(--crisp-weight-medium); max-width: 200px; }
114
129
  & .crisp-tabletoolbar-fcond-seg--field svg { color: var(--crisp-fg-muted); }
115
- /* #592: a nested pick's field segment ("Team › Twitter follower
116
- count") — measured live: BOTH path segments the same colour (chip context only; the picker's own menu rows keep the parent muted, see
117
- tabletoolbar.chrome.ts), each independently truncated. Attio's own layout gives the leaf segment priority (it stays fully readable longer, the parent truncates first) via a two-part min-content measurement this reproduces qualitatively with a flex-shrink differential rather than byte-for-byte (the exact px split Attio computes is content-length-dependent, not a fixed value) — not independently re-verified at other path-segment lengths. */
130
+ /* #592: a nested pick's field segment ("Team › Twitter follower count") — measured live: BOTH path segments the same colour (chip
131
+ context only; the picker's own menu rows keep the parent muted, see
132
+ tabletoolbar.chrome.ts), each independently truncated. Attio's own
133
+ layout gives the leaf segment priority (it stays fully readable
134
+ longer, the parent truncates first) via a two-part min-content
135
+ measurement this reproduces qualitatively with a flex-shrink
136
+ differential rather than byte-for-byte (the exact px split Attio
137
+ computes is content-length-dependent, not a fixed value) — not
138
+ independently re-verified at other path-segment lengths. */
118
139
  & .crisp-tabletoolbar-fcond-path { display: inline-flex; align-items: center; gap: 2px; min-width: 0; max-width: 178px; }
119
140
  & .crisp-tabletoolbar-fcond-path-seg { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; flex: 1 1 auto; }
120
141
  & .crisp-tabletoolbar-fcond-path-seg:first-child { flex: 0 1 auto; }
@@ -124,8 +145,10 @@
124
145
  & .crisp-tabletoolbar-fcond-seg--kebab { background: var(--crisp-bg-surface); justify-content: center; width: 28px; padding: 0; border-radius: 0 var(--crisp-radius-md) var(--crisp-radius-md) 0; color: var(--crisp-fg-muted); }
125
146
  & .crisp-tabletoolbar-fcond-seg--kebab:hover { color: var(--crisp-fg-primary); }
126
147
  /* #754: the sort row's own field trigger, replacing a plain Select
127
- with the same nested-drill-down popover Filter's field picker already has — reuses Select's own already-measured trigger chrome
128
- verbatim (its class is scoped under Select's own root class, so it can't be reused as a bare class name outside an actual Select tree;
148
+ with the same nested-drill-down popover Filter's field picker
149
+ already has — reuses Select's own already-measured trigger chrome
150
+ verbatim (its class is scoped under Select's own root class, so it
151
+ can't be reused as a bare class name outside an actual Select tree;
129
152
  these values are copied, not re-measured). */
130
153
  & .crisp-tabletoolbar-sort-field-picker { position: relative; display: inline-flex; flex: 1; min-width: 0; }
131
154
  & .crisp-tabletoolbar-sort-field-trigger { box-sizing: border-box; display: inline-flex; align-items: center; gap: var(--crisp-space-1-5); height: var(--crisp-size-menuRow); width: 100%; min-width: 0; margin: 0; padding: 0 var(--crisp-space-1-5) 0 var(--crisp-space-2); border: none; border-radius: var(--crisp-radius-md); background: transparent; box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--crisp-fg-primary) 7%, transparent); font-family: inherit; font-size: var(--crisp-text-lg); color: var(--crisp-fg-primary); cursor: pointer; transition: background-color .12s ease, box-shadow .12s ease; }
@@ -50,9 +50,9 @@ export type DataCellType = "text" | "entity" | "tags" | "status" | "date" | "dat
50
50
  * country selection survives even for a national number that's still
51
51
  * being typed (an E.164 string alone can't distinguish "no country
52
52
  * chosen yet" from "the digits happen to start with a valid dial
53
- * code"). See `editor: "phone"` for the editable picker. Multi-value
54
- * (Attio's own "Multiple values" constraint on this type) is explicitly
55
- * out of scope -- tracked as its own follow-up, not this type. */
53
+ * code"). See `editor: "phone"` for the editable picker. See `multiValue`
54
+ * (crisp#816) for Attio's own "Multiple values" constraint on this
55
+ * type. */
56
56
  | "phone";
57
57
  export type DataTableColumn<T> = {
58
58
  key: string;
@@ -87,6 +87,26 @@ export type DataTableColumn<T> = {
87
87
  resizable?: boolean;
88
88
  /** Tone overrides for `status`/`tags` values (else a stable colour per value). */
89
89
  tones?: Record<string, string>;
90
+ /** `type: "date"`/`"datetime"` only: render this cell in Attio's own
91
+ * measured overdue/danger red (`fgDanger`, `#eb3b3b` — the same token
92
+ * `TasksTable`'s own overdue due-date already uses, crisp#462) when the
93
+ * predicate returns true. A per-row PREDICATE rather than `tones`'s
94
+ * value-keyed lookup, because a date's danger state is computed over the
95
+ * WHOLE ROW (e.g. a precomputed "overdue" bucket) — `tones` can only key
96
+ * off the formatted string, which a date doesn't have a closed set of
97
+ * (crisp#822). */
98
+ danger?: (value: unknown, row: T) => boolean;
99
+ /** This column holds more than one value per row -- Attio's own
100
+ * "Multiple values" constraint (crisp#816, confirmed live on the Phone
101
+ * Number attribute type: Constraints → Multiple values, a per-attribute
102
+ * toggle, absent for e.g. Text). Built out for `type`/`editor: "phone"`
103
+ * only this pass; the raw value is that type's normal per-value string,
104
+ * several of them joined with `;` (`"MN|88112233;US|4155550123"`) rather
105
+ * than JSON -- the same "a real separator earns its keep" reasoning
106
+ * `phone`'s own single-value `"{iso2}|{digits}"` shape already uses.
107
+ * Structural flag lives on the column (type-agnostic, per the issue)
108
+ * even though only one type renders it today. */
109
+ multiValue?: boolean;
90
110
  /** Enable click-to-sort on this column's header. */
91
111
  sortable?: boolean;
92
112
  /** Exclude this column from the search filter (default: included). */
@@ -386,6 +406,22 @@ export type DataTableProps<T extends {
386
406
  * `EmptyState variant="page"` surface as the zero-row state.
387
407
  */
388
408
  error?: React.ReactNode;
409
+ /**
410
+ * A whole-view request in flight (a filter/sort change re-querying every
411
+ * row, or a fresh page boot) -- crisp#826, confirmed live on Attio's
412
+ * Companies grid on both triggers. Replaces the row body with a single
413
+ * centred `Spinner` + `loadingLabel`, `10px` gap, both in the same muted
414
+ * `rgba(0,0,0,0.63)` -- the same tone `-phone-dial` and other muted text
415
+ * already use elsewhere in this file. Takes priority over `error`/the
416
+ * empty state (a new request in flight supersedes a previous one's
417
+ * result). Distinct from the two OTHER loading states this component
418
+ * already has: a cold in-place row-body load (blank, no skeleton,
419
+ * AUTHORING.md Lesson 28) and the warm-navigation per-cell shimmer
420
+ * (crisp#599) -- neither of those swaps the whole body for this. */
421
+ loading?: boolean;
422
+ /** Text next to the spinner while `loading` (default "Loading..." --
423
+ * Attio's own measured copy, three literal periods). */
424
+ loadingLabel?: string;
389
425
  sort?: SortState | SortState[] | null;
390
426
  defaultSort?: SortState;
391
427
  onSortChange?: (s: SortState | null) => void;
@@ -484,7 +520,7 @@ export type DataTableProps<T extends {
484
520
  };
485
521
  export declare function DataTable<T extends {
486
522
  id: string;
487
- }>({ columns: columnsProp, data, selectable, selectedIds, onSelectionChange, selectAllLabel: selectAllLabelProp, selectRowLabel, selectionAnnouncement, columnMenu, onColumnsChange, columnOrder, onColumnOrderChange, groupBy, onRowsReorder, error, sort: sortProp, defaultSort, onSortChange, filterable, searchPlaceholder: searchPlaceholderProp, searchLabel: searchLabelProp, onEdit, onViewEditHistory, stickyFirst, stickyLast, scrollProps, locale: localeProp, avatarTone, surface, interactive, onRowClick, onAddRow, addRowLabel: addRowLabelProp, empty: emptyProp, showCount, 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 }: DataTableProps<T>): React.JSX.Element;
523
+ }>({ columns: columnsProp, data, selectable, selectedIds, onSelectionChange, selectAllLabel: selectAllLabelProp, selectRowLabel, selectionAnnouncement, columnMenu, onColumnsChange, columnOrder, onColumnOrderChange, groupBy, onRowsReorder, error, loading, loadingLabel, sort: sortProp, defaultSort, onSortChange, filterable, searchPlaceholder: searchPlaceholderProp, searchLabel: searchLabelProp, onEdit, onViewEditHistory, stickyFirst, stickyLast, scrollProps, locale: localeProp, avatarTone, surface, interactive, onRowClick, onAddRow, addRowLabel: addRowLabelProp, empty: emptyProp, showCount, 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 }: DataTableProps<T>): React.JSX.Element;
488
524
  export declare namespace DataTable {
489
525
  var displayName: string;
490
526
  }
@@ -2,6 +2,11 @@ import * as React from "react";
2
2
  export type SelectOption = {
3
3
  value: string;
4
4
  label: React.ReactNode;
5
+ /** Content shown in the CLOSED trigger for this option when selected, if
6
+ * different from its own option-row `label` (crisp#842 -- a country
7
+ * picker's row shows the full name + dial code, but Attio's own closed
8
+ * trigger shows only the bare 2-letter ISO code). Defaults to `label`. */
9
+ triggerLabel?: React.ReactNode;
5
10
  /** Text used for filtering when `searchable` (falls back to a string label). */
6
11
  keywords?: string;
7
12
  /** A status colour dot shown before the label (status/select fields). */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crisp-ui-kit/crisp",
3
- "version": "0.55.1",
3
+ "version": "0.55.3",
4
4
  "description": "A lean React design system, measured pixel-for-pixel from Attio.",
5
5
  "license": "MIT",
6
6
  "type": "module",