@crisp-ui-kit/crisp 0.54.9 → 0.54.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/dist/cjs/components/datatable/datatable.js +175 -12
  2. package/dist/cjs/components/datatable/datatable.recipe.js +30 -0
  3. package/dist/cjs/components/tabletoolbar/tabletoolbar.chrome.js +25 -2
  4. package/dist/cjs/components/tabletoolbar/tabletoolbar.js +159 -63
  5. package/dist/cjs/components/tabletoolbar/tabletoolbar.recipe.js +41 -9
  6. package/dist/cjs/components/text/text.recipe.js +17 -7
  7. package/dist/cjs/product/recordpage/column.js +19 -0
  8. package/dist/cjs/product/recordpage/recordpage.js +5 -4
  9. package/dist/cjs/product/recordpage/test-fixtures.js +55 -0
  10. package/dist/cjs/product/recordstable/index.js +1 -1
  11. package/dist/cjs/product/recordstable/recordstable.js +5 -0
  12. package/dist/cjs/product/savedviews/savedviews.recipe.js +13 -11
  13. package/dist/cjs/product/taskstable/taskstable.js +5 -0
  14. package/dist/datatable.css +17 -0
  15. package/dist/datatable.layered.css +17 -0
  16. package/dist/esm/components/datatable/datatable.js +175 -12
  17. package/dist/esm/components/datatable/datatable.recipe.js +30 -0
  18. package/dist/esm/components/tabletoolbar/tabletoolbar.chrome.js +25 -2
  19. package/dist/esm/components/tabletoolbar/tabletoolbar.js +159 -63
  20. package/dist/esm/components/tabletoolbar/tabletoolbar.recipe.js +41 -9
  21. package/dist/esm/components/text/text.recipe.js +17 -7
  22. package/dist/esm/product/recordpage/column.js +16 -0
  23. package/dist/esm/product/recordpage/recordpage.js +2 -1
  24. package/dist/esm/product/recordpage/test-fixtures.js +52 -0
  25. package/dist/esm/product/recordstable/index.js +1 -1
  26. package/dist/esm/product/recordstable/recordstable.js +5 -0
  27. package/dist/esm/product/savedviews/savedviews.recipe.js +13 -11
  28. package/dist/esm/product/taskstable/taskstable.js +5 -0
  29. package/dist/recordstable.css +19 -2
  30. package/dist/recordstable.layered.css +19 -2
  31. package/dist/savedviews.css +6 -18
  32. package/dist/savedviews.layered.css +6 -18
  33. package/dist/styles.css +78 -24
  34. package/dist/styles.layered.css +78 -24
  35. package/dist/tabletoolbar.css +35 -3
  36. package/dist/tabletoolbar.layered.css +35 -3
  37. package/dist/text.css +1 -1
  38. package/dist/text.layered.css +1 -1
  39. package/dist/types/components/datatable/datatable.d.ts +38 -3
  40. package/dist/types/components/tabletoolbar/tabletoolbar.d.ts +26 -1
  41. package/dist/types/components/text/text.recipe.d.ts +1 -1
  42. package/dist/types/product/recordpage/column.d.ts +112 -0
  43. package/dist/types/product/recordpage/recordpage.d.ts +1 -1
  44. package/dist/types/product/recordpage/test-fixtures.d.ts +3 -0
  45. package/dist/types/product/recordpanel/recordpanel.d.ts +1 -1
  46. package/dist/types/product/recordstable/index.d.ts +1 -1
  47. package/dist/types/product/recordstable/recordstable.d.ts +6 -1
  48. package/dist/types/product/taskstable/taskstable.d.ts +5 -0
  49. package/package.json +1 -1
  50. /package/dist/cjs/product/{recordstable → recordpage}/editable-field.js +0 -0
  51. /package/dist/esm/product/{recordstable → recordpage}/editable-field.js +0 -0
  52. /package/dist/types/product/{recordstable → recordpage}/editable-field.d.ts +0 -0
@@ -7,12 +7,29 @@
7
7
  & .crisp-tabletoolbar-search svg { flex-shrink: 0; }
8
8
  & .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; }
9
9
  & .crisp-tabletoolbar-search input::placeholder { color: var(--crisp-fg-muted); }
10
- & .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: transparent; box-shadow: inset 0 0 0 1px var(--crisp-border-default); 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; }
10
+ & .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
11
  & .crisp-tabletoolbar-tool:hover { background: var(--crisp-bg-raised); }
12
12
  & .crisp-tabletoolbar-tool svg { flex-shrink: 0; color: var(--crisp-fg-muted); }
13
- & .crisp-tabletoolbar-tool--active { background: var(--crisp-bg-raised); }
13
+ /* 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
14
+ base .tool rule above now already sets; this override existed to
15
+ distinguish active from a base state that used to be transparent.
16
+ Kept explicit (not just deleted) so a future change to the base
17
+ rule's background can't silently change this one too. */
18
+ & .crisp-tabletoolbar-tool--active { background: var(--crisp-bg-surface); }
14
19
  & .crisp-tabletoolbar-tool--ghost { box-shadow: none; background: color-mix(in srgb, var(--crisp-fg-primary) 3%, transparent); color: var(--crisp-fg-muted); }
15
20
  & .crisp-tabletoolbar-tool--ghost:hover { background: color-mix(in srgb, var(--crisp-fg-primary) 6%, transparent); }
21
+ /* crisp/#772 follow-up, re-measured live 2026-09-25: an EMPTY Sort/Filter
22
+ trigger (no sort/condition set) isn't a solid-ring chip at all -- it's
23
+ a dashed ::before (1px dashed, border-radius: inherit, absolutely
24
+ positioned inset: 0) over a barely-there fill, confirmed on Attio's own empty Filter chip (its ::before border computes to exactly "1px dashed rgba(0, 0, 0, 0.06)"). Once a value IS set, the trigger drops the dashed pseudo and picks up the base tool's own solid ring
25
+ instead (the --active modifier, unchanged) -- confirmed on Sort with
26
+ an actual sort applied. color-mix (not the raw rgba Attio computes
27
+ to) so both the border and the fill flip correctly in dark mode, the
28
+ same reasoning --ghost above already uses. Only wired into
29
+ TableToolbar itself (tabletoolbar.tsx) this pass -- RecordsTable's own Sort/Filter triggers share this same chrome function but are deprecated, not touched here. */
30
+ & .crisp-tabletoolbar-tool--dashed { position: relative; box-shadow: none; background: color-mix(in srgb, var(--crisp-fg-primary) 2%, transparent); }
31
+ & .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; }
32
+ & .crisp-tabletoolbar-tool--dashed:hover { background: color-mix(in srgb, var(--crisp-fg-primary) 4%, transparent); }
16
33
  & .crisp-tabletoolbar-toolgroup { position: relative; display: inline-flex; }
17
34
  & .crisp-tabletoolbar-divider { flex-shrink: 0; width: 1px; height: 20px; background: var(--crisp-border-subtle); }
18
35
  & .crisp-tabletoolbar-menu { position: absolute; top: calc(100% + var(--crisp-space-1)); left: 0; z-index: 30; min-width: 232px; padding: var(--crisp-space-1); border-radius: var(--crisp-radius-xl); background: var(--crisp-bg-popover); box-shadow: inset 0 0 0 1px var(--crisp-ring-popover), 0 0 0 1px var(--crisp-shadow-popover1), 0 4px 8px -4px var(--crisp-shadow-popover2), 0 4px 12px -2px var(--crisp-shadow-popover3); display: flex; flex-direction: column; gap: var(--crisp-space-px); transform-origin: top; opacity: 1; transform: none; transition: opacity .13s ease, transform .13s ease; }
@@ -105,12 +122,27 @@
105
122
  & .crisp-tabletoolbar-fcond-seg--val { font-weight: var(--crisp-weight-medium); max-width: 140px; overflow: hidden; text-overflow: ellipsis; }
106
123
  & .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); }
107
124
  & .crisp-tabletoolbar-fcond-seg--kebab:hover { color: var(--crisp-fg-primary); }
125
+ /* #754: the sort row's own field trigger, replacing a plain Select
126
+ with the same nested-drill-down popover Filter's field picker already has — reuses Select's own already-measured trigger chrome
127
+ 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;
128
+ these values are copied, not re-measured). */
129
+ & .crisp-tabletoolbar-sort-field-picker { position: relative; display: inline-flex; flex: 1; min-width: 0; }
130
+ & .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; }
131
+ & .crisp-tabletoolbar-sort-field-trigger:hover { background: var(--crisp-bg-raised); }
132
+ & .crisp-tabletoolbar-sort-field-trigger[aria-expanded="true"],
133
+ & .crisp-tabletoolbar-sort-field-trigger:focus-visible { outline: none; box-shadow: inset 0 0 0 1px var(--crisp-bg-brand-solid); background: var(--crisp-bg-surface); }
134
+ & .crisp-tabletoolbar-sort-field-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-align: start; }
135
+ & .crisp-tabletoolbar-sort-field-picker .crisp-tabletoolbar-menu { min-width: 200px; }
108
136
  & .crisp-tabletoolbar-menu--val { min-width: 134px; }
109
137
  & .crisp-tabletoolbar-datepop { width: 300px; }
110
138
  & .crisp-tabletoolbar-datepop-presets { display: flex; flex-wrap: wrap; gap: var(--crisp-space-1); margin-top: var(--crisp-space-2); padding: 0 var(--crisp-space-1); }
111
139
  & .crisp-tabletoolbar-datepop-preset { height: var(--crisp-size-chip); padding: 0 var(--crisp-space-2); border: none; border-radius: var(--crisp-radius-chip); background: var(--crisp-bg-raised); color: var(--crisp-fg-primary); font-family: inherit; font-size: var(--crisp-text-sm); cursor: pointer; }
112
140
  & .crisp-tabletoolbar-datepop-preset:hover { background: color-mix(in srgb, var(--crisp-fg-primary) 8%, transparent); }
113
- & .crisp-tabletoolbar-tool--raised { border: none; background: var(--crisp-bg-surface-muted); box-shadow: inset 0 0 0 1px var(--crisp-ring-neutral), 0 0 2px rgba(28,40,64,0.18), 0 1px 3px rgba(0,0,0,0.04); font-family: inherit; cursor: pointer; }
141
+
142
+ & .crisp-tabletoolbar-tool--raised { border: none; background: var(--crisp-bg-surface); box-shadow: inset 0 0 0 1px var(--crisp-ring-neutral), 0 0 2px rgba(28,40,64,0.18), 0 1px 3px rgba(0,0,0,0.04); font-family: inherit; cursor: pointer; }
143
+
144
+ & .crisp-tabletoolbar-sorted-prefix { color: color-mix(in srgb, var(--crisp-fg-primary) 63%, transparent); }
145
+ & .crisp-tabletoolbar-sorted-count { font-size: var(--crisp-text-sm); color: color-mix(in srgb, var(--crisp-fg-primary) 73%, transparent); }
114
146
  [dir="rtl"] & [data-rtl-flip] { transform: scaleX(-1); }
115
147
  }
116
148
 
@@ -8,12 +8,29 @@
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: transparent; box-shadow: inset 0 0 0 1px var(--crisp-border-default); 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-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; }
12
12
  & .crisp-tabletoolbar-tool:hover { background: var(--crisp-bg-raised); }
13
13
  & .crisp-tabletoolbar-tool svg { flex-shrink: 0; color: var(--crisp-fg-muted); }
14
- & .crisp-tabletoolbar-tool--active { background: var(--crisp-bg-raised); }
14
+ /* 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
15
+ base .tool rule above now already sets; this override existed to
16
+ distinguish active from a base state that used to be transparent.
17
+ Kept explicit (not just deleted) so a future change to the base
18
+ rule's background can't silently change this one too. */
19
+ & .crisp-tabletoolbar-tool--active { background: var(--crisp-bg-surface); }
15
20
  & .crisp-tabletoolbar-tool--ghost { box-shadow: none; background: color-mix(in srgb, var(--crisp-fg-primary) 3%, transparent); color: var(--crisp-fg-muted); }
16
21
  & .crisp-tabletoolbar-tool--ghost:hover { background: color-mix(in srgb, var(--crisp-fg-primary) 6%, transparent); }
22
+ /* crisp/#772 follow-up, re-measured live 2026-09-25: an EMPTY Sort/Filter
23
+ trigger (no sort/condition set) isn't a solid-ring chip at all -- it's
24
+ a dashed ::before (1px dashed, border-radius: inherit, absolutely
25
+ positioned inset: 0) over a barely-there fill, confirmed on Attio's own empty Filter chip (its ::before border computes to exactly "1px dashed rgba(0, 0, 0, 0.06)"). Once a value IS set, the trigger drops the dashed pseudo and picks up the base tool's own solid ring
26
+ instead (the --active modifier, unchanged) -- confirmed on Sort with
27
+ an actual sort applied. color-mix (not the raw rgba Attio computes
28
+ to) so both the border and the fill flip correctly in dark mode, the
29
+ 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
+ & .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
+ & .crisp-tabletoolbar-tool--dashed:hover { background: color-mix(in srgb, var(--crisp-fg-primary) 4%, transparent); }
17
34
  & .crisp-tabletoolbar-toolgroup { position: relative; display: inline-flex; }
18
35
  & .crisp-tabletoolbar-divider { flex-shrink: 0; width: 1px; height: 20px; background: var(--crisp-border-subtle); }
19
36
  & .crisp-tabletoolbar-menu { position: absolute; top: calc(100% + var(--crisp-space-1)); left: 0; z-index: 30; min-width: 232px; padding: var(--crisp-space-1); border-radius: var(--crisp-radius-xl); background: var(--crisp-bg-popover); box-shadow: inset 0 0 0 1px var(--crisp-ring-popover), 0 0 0 1px var(--crisp-shadow-popover1), 0 4px 8px -4px var(--crisp-shadow-popover2), 0 4px 12px -2px var(--crisp-shadow-popover3); display: flex; flex-direction: column; gap: var(--crisp-space-px); transform-origin: top; opacity: 1; transform: none; transition: opacity .13s ease, transform .13s ease; }
@@ -106,12 +123,27 @@
106
123
  & .crisp-tabletoolbar-fcond-seg--val { font-weight: var(--crisp-weight-medium); max-width: 140px; overflow: hidden; text-overflow: ellipsis; }
107
124
  & .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); }
108
125
  & .crisp-tabletoolbar-fcond-seg--kebab:hover { color: var(--crisp-fg-primary); }
126
+ /* #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;
129
+ these values are copied, not re-measured). */
130
+ & .crisp-tabletoolbar-sort-field-picker { position: relative; display: inline-flex; flex: 1; min-width: 0; }
131
+ & .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; }
132
+ & .crisp-tabletoolbar-sort-field-trigger:hover { background: var(--crisp-bg-raised); }
133
+ & .crisp-tabletoolbar-sort-field-trigger[aria-expanded="true"],
134
+ & .crisp-tabletoolbar-sort-field-trigger:focus-visible { outline: none; box-shadow: inset 0 0 0 1px var(--crisp-bg-brand-solid); background: var(--crisp-bg-surface); }
135
+ & .crisp-tabletoolbar-sort-field-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-align: start; }
136
+ & .crisp-tabletoolbar-sort-field-picker .crisp-tabletoolbar-menu { min-width: 200px; }
109
137
  & .crisp-tabletoolbar-menu--val { min-width: 134px; }
110
138
  & .crisp-tabletoolbar-datepop { width: 300px; }
111
139
  & .crisp-tabletoolbar-datepop-presets { display: flex; flex-wrap: wrap; gap: var(--crisp-space-1); margin-top: var(--crisp-space-2); padding: 0 var(--crisp-space-1); }
112
140
  & .crisp-tabletoolbar-datepop-preset { height: var(--crisp-size-chip); padding: 0 var(--crisp-space-2); border: none; border-radius: var(--crisp-radius-chip); background: var(--crisp-bg-raised); color: var(--crisp-fg-primary); font-family: inherit; font-size: var(--crisp-text-sm); cursor: pointer; }
113
141
  & .crisp-tabletoolbar-datepop-preset:hover { background: color-mix(in srgb, var(--crisp-fg-primary) 8%, transparent); }
114
- & .crisp-tabletoolbar-tool--raised { border: none; background: var(--crisp-bg-surface-muted); box-shadow: inset 0 0 0 1px var(--crisp-ring-neutral), 0 0 2px rgba(28,40,64,0.18), 0 1px 3px rgba(0,0,0,0.04); font-family: inherit; cursor: pointer; }
142
+
143
+ & .crisp-tabletoolbar-tool--raised { border: none; background: var(--crisp-bg-surface); box-shadow: inset 0 0 0 1px var(--crisp-ring-neutral), 0 0 2px rgba(28,40,64,0.18), 0 1px 3px rgba(0,0,0,0.04); font-family: inherit; cursor: pointer; }
144
+
145
+ & .crisp-tabletoolbar-sorted-prefix { color: color-mix(in srgb, var(--crisp-fg-primary) 63%, transparent); }
146
+ & .crisp-tabletoolbar-sorted-count { font-size: var(--crisp-text-sm); color: color-mix(in srgb, var(--crisp-fg-primary) 73%, transparent); }
115
147
  [dir="rtl"] & [data-rtl-flip] { transform: scaleX(-1); }
116
148
  }
117
149
 
package/dist/text.css CHANGED
@@ -1,5 +1,5 @@
1
1
  .crisp-text {
2
- display: inline;
2
+ vertical-align: baseline;
3
3
  }
4
4
  .crisp-text--tone_default {
5
5
  color: var(--crisp-fg-primary);
@@ -1,6 +1,6 @@
1
1
  @layer crisp-components {
2
2
  .crisp-text {
3
- display: inline;
3
+ vertical-align: baseline;
4
4
  }
5
5
  .crisp-text--tone_default {
6
6
  color: var(--crisp-fg-primary);
@@ -37,7 +37,12 @@ export type DataCellType = "text" | "entity" | "tags" | "status" | "date" | "dat
37
37
  * workspace -- this type is display-only for now; editing still falls
38
38
  * through to the plain-text editor every other non-number/date type
39
39
  * gets, not a dedicated one (see #597 for when that's measured). */
40
- | "email";
40
+ | "email"
41
+ /** A star row (crisp/#760) -- Attio's own "Rating" attribute type,
42
+ * confirmed live: 5 filled/empty 5-point stars, `maxRating` stars total
43
+ * (default 5). Read-only display without `editor: "rating"`; see that
44
+ * prop for the always-live editable version. */
45
+ | "rating";
41
46
  export type DataTableColumn<T> = {
42
47
  key: string;
43
48
  /** Truncates with an ellipsis when it's wider than the column (#534: a
@@ -63,6 +68,10 @@ export type DataTableColumn<T> = {
63
68
  };
64
69
  /** Currency symbol for `currency` columns (default "$"). */
65
70
  symbol?: string;
71
+ /** Star count for `type: "rating"` / `editor: "rating"` (default 5 --
72
+ * Attio's own scale wasn't independently varied beyond the default in
73
+ * the measured workspace, #760). */
74
+ maxRating?: number;
66
75
  /** Allow drag-to-resize on this column (default true). */
67
76
  resizable?: boolean;
68
77
  /** Tone overrides for `status`/`tags` values (else a stable colour per value). */
@@ -94,8 +103,26 @@ export type DataTableColumn<T> = {
94
103
  * empty-search state reads "No users" rather than `Select`'s own
95
104
  * generic "No results" — both still overridable the normal way.
96
105
  * Otherwise identical to `reference`: same `refOptions` shape, same
97
- * `onCreateReference`/`createLabel` "Create X" row. */
98
- editor?: "select" | "switch" | "reference" | "actor";
106
+ * `onCreateReference`/`createLabel` "Create X" row.
107
+ *
108
+ * `"rating"` (crisp/#760) is the other always-live exception, same
109
+ * category as `"switch"`: confirmed live, a single click on any star
110
+ * commits immediately (no double-click gate, no separate rest/edit
111
+ * display) — clicking the star that already equals the current value
112
+ * clears it to 0/empty rather than re-setting it. Reads/writes
113
+ * `maxRating` stars (default 5).
114
+ *
115
+ * `"checkbox"` (crisp/#761) is a THIRD, distinct boolean rendering from
116
+ * `"switch"` — confirmed live against Attio's own "Checkbox" attribute
117
+ * type (Settings → Data → Lists → Attributes → Create attribute), which
118
+ * renders a plain checked/unchecked square, not a toggle pill; `"switch"`
119
+ * was never actually measured against that surface (no cited source —
120
+ * see #761's own investigation) and stays as-is for a genuine toggle
121
+ * field. Always-live like `"switch"`/`"rating"`; renders crisp's own
122
+ * `Checkbox` component (already measured 16px/r6/brand-fill from the
123
+ * grid's own row-selection checkbox — this is the SAME visual, not a
124
+ * new one). */
125
+ editor?: "select" | "switch" | "reference" | "actor" | "rating" | "checkbox";
99
126
  /** Options for `editor: "select"` (value/label, an optional `color` a
100
127
  * caller's own `render` can read) — same shape as RecordsTable's
101
128
  * `Column.options` for its `status` picker. */
@@ -178,6 +205,14 @@ export type DataTableColumn<T> = {
178
205
  * native-copy path writes nothing to the clipboard, and its Clear-value
179
206
  * item is disabled (there is nothing editable to clear). */
180
207
  readOnly?: boolean;
208
+ /** Marks the column's values as AI-enriched (crisp/#758) — every cell
209
+ * gets a full-bleed purple wash, composited OVER whatever row-state
210
+ * background the cell already has (hover, selected), which is why it's
211
+ * an overlay rather than a plain `background`. Ported from
212
+ * `RecordsTable`'s own already-measured value (`#9b69ff` at 4%, same as
213
+ * `bgPurple`) — no new measurement needed, `RecordsTable`'s own
214
+ * `Column.enriched` predates this. */
215
+ enriched?: boolean;
181
216
  /** `type: "entity"` only (#501) — makes the cell's name its own click
182
217
  * target, separate from the row's own `onRowClick`: the name renders as a
183
218
  * `stopPropagation`-guarded button instead of a plain span, the same
@@ -192,6 +192,31 @@ export type TableToolbarProps = Omit<React.ComponentPropsWithoutRef<"div">, "chi
192
192
  * the filter chips from View settings is dropped along with it.
193
193
  */
194
194
  showViewSettings?: boolean;
195
+ /**
196
+ * Relocates the View settings trigger (and its popover) into a
197
+ * consumer-supplied element instead of this toolbar's own row
198
+ * (your-moon/crisp#766) — a plain DOM ref, e.g. a `<span ref={...} />`
199
+ * rendered right next to `SavedViews`' own trigger. Measured live: Attio's
200
+ * view bar puts the view switcher and View settings adjacent, then a
201
+ * flexible gap, then Import/Export immediately before the primary "+ New"
202
+ * action (see `importExportRef`, a SEPARATE ref — the two controls sit
203
+ * far apart, not next to each other). The row below holds only Sort and
204
+ * Filter. Neither `SavedViews` nor `TableToolbar` renders the OTHER
205
+ * component's pieces itself — they stay two separate components, each
206
+ * still owning its own state — this ref is a portal target, not a new
207
+ * cross-component API. Omit it (the default) and View settings renders
208
+ * exactly where it always has, inline in this toolbar's own row,
209
+ * unchanged — existing consumers see no difference at all.
210
+ */
211
+ viewSettingsRef?: React.RefObject<HTMLElement | null>;
212
+ /**
213
+ * Same relocation, for the Import/Export trigger (and its menu) — a
214
+ * separate ref from `viewSettingsRef` because Attio's own layout puts
215
+ * Import/Export far from View settings, immediately before the primary
216
+ * action instead of next to the view switcher. Omit it and Import/Export
217
+ * renders exactly where it always has, inline in this toolbar's own row.
218
+ */
219
+ importExportRef?: React.RefObject<HTMLElement | null>;
195
220
  /**
196
221
  * The full set of attributes View settings offers — every attribute on
197
222
  * the object, shown or not. Defaults to `columns`. Omit both, along with
@@ -288,7 +313,7 @@ export declare function applySorts<T>(rows: T[], sorts: ToolbarSort[], get?: (ro
288
313
  * this (Sort: Due date/Assignee/Creation date; Filter: Assignee/Record/
289
314
  * Created by), which one shared list can't reproduce.
290
315
  */
291
- export declare function TableToolbar({ columns, sortColumns: sortColumnsProp, filterColumns: filterColumnsProp, sorts: sortsProp, defaultSorts, onSortsChange, filters: filtersProp, defaultFilters, onFiltersChange, count, children, helpHref, className, sortLabel: sortLabelProp, filterLabel: filterLabelProp, reorderSortLabel: reorderSortLabelProp, noSortsLabel: noSortsLabelProp, addSortLabel: addSortLabelProp, learnSortingLabel: learnSortingLabelProp, selectConditionLabel: selectConditionLabelProp, sortFieldLabel: sortFieldLabelProp, sortDirectionLabel: sortDirectionLabelProp, removeSortLabel: removeSortLabelProp, filterFieldLabel: filterFieldLabelProp, filterFieldPlaceholder: filterFieldPlaceholderProp, filterValuePlaceholder: filterValuePlaceholderProp, filterOptionsLabel: filterOptionsLabelProp, deleteFilterLabel: deleteFilterLabelProp, ascendingLabel: ascendingLabelProp, descendingLabel: descendingLabelProp, sortTriggerLabel, filterTriggerLabel, inline, showSort, showViewSettings, viewColumns: viewColumnsProp, shownKeys: shownKeysProp, defaultShownKeys, onShownKeysChange, viewSettingsLabel: viewSettingsLabelProp, reorderAttributeLabel: reorderAttributeLabelProp, hideAttributeLabel, addAttributeToViewLabel: addAttributeToViewLabelProp, searchAttributesPlaceholder: searchAttributesPlaceholderProp, importExportLabel: importExportLabelProp, onImportCsv, onExportCsv, onExportExcel, importExportHelpHref, importCsvLabel: importCsvLabelProp, exportCsvLabel: exportCsvLabelProp, exportExcelLabel: exportExcelLabelProp, learnImportExportLabel: learnImportExportLabelProp, backLabel: backLabelProp, nestedFieldPlaceholder, ...rest }: TableToolbarProps): React.JSX.Element;
316
+ export declare function TableToolbar({ columns, sortColumns: sortColumnsProp, filterColumns: filterColumnsProp, sorts: sortsProp, defaultSorts, onSortsChange, filters: filtersProp, defaultFilters, onFiltersChange, count, children, helpHref, className, sortLabel: sortLabelProp, filterLabel: filterLabelProp, reorderSortLabel: reorderSortLabelProp, noSortsLabel: noSortsLabelProp, addSortLabel: addSortLabelProp, learnSortingLabel: learnSortingLabelProp, selectConditionLabel: selectConditionLabelProp, sortFieldLabel: sortFieldLabelProp, sortDirectionLabel: sortDirectionLabelProp, removeSortLabel: removeSortLabelProp, filterFieldLabel: filterFieldLabelProp, filterFieldPlaceholder: filterFieldPlaceholderProp, filterValuePlaceholder: filterValuePlaceholderProp, filterOptionsLabel: filterOptionsLabelProp, deleteFilterLabel: deleteFilterLabelProp, ascendingLabel: ascendingLabelProp, descendingLabel: descendingLabelProp, sortTriggerLabel, filterTriggerLabel, inline, showSort, showViewSettings, viewSettingsRef, importExportRef, viewColumns: viewColumnsProp, shownKeys: shownKeysProp, defaultShownKeys, onShownKeysChange, viewSettingsLabel: viewSettingsLabelProp, reorderAttributeLabel: reorderAttributeLabelProp, hideAttributeLabel, addAttributeToViewLabel: addAttributeToViewLabelProp, searchAttributesPlaceholder: searchAttributesPlaceholderProp, importExportLabel: importExportLabelProp, onImportCsv, onExportCsv, onExportExcel, importExportHelpHref, importCsvLabel: importCsvLabelProp, exportCsvLabel: exportCsvLabelProp, exportExcelLabel: exportExcelLabelProp, learnImportExportLabel: learnImportExportLabelProp, backLabel: backLabelProp, nestedFieldPlaceholder, ...rest }: TableToolbarProps): React.JSX.Element;
292
317
  export type ToolbarToolProps = React.ComponentPropsWithoutRef<"button"> & {
293
318
  /** Drawn the way Sort/Filter are once a condition is set (#399). */
294
319
  active?: boolean;
@@ -1,7 +1,7 @@
1
1
  declare const spec: {
2
2
  readonly name: "text";
3
3
  readonly base: {
4
- readonly display: "inline";
4
+ readonly verticalAlign: "baseline";
5
5
  };
6
6
  readonly variants: {
7
7
  tone: {
@@ -0,0 +1,112 @@
1
+ import type * as React from "react";
2
+ import type { EditableOption } from "./editable-field";
3
+ type IconType = React.ComponentType<{
4
+ size?: number;
5
+ strokeWidth?: number;
6
+ }>;
7
+ export type ColumnType = "entity" | "text" | "tags" | "number" | "url" | "date" | "currency" | "status" | "reference";
8
+ export type StatusOption = {
9
+ value: string;
10
+ label: string;
11
+ color: string;
12
+ };
13
+ export type ReferenceOption = {
14
+ value: string;
15
+ label: string;
16
+ avatar?: string;
17
+ };
18
+ export type EntityValue = {
19
+ name: string;
20
+ avatar?: string;
21
+ };
22
+ export type Row = {
23
+ id: string;
24
+ } & Record<string, any>;
25
+ export type Column = {
26
+ key: string;
27
+ label: string;
28
+ type?: ColumnType;
29
+ /** Mark the column's values as AI-enriched — tints the cells the way
30
+ * enriched values are washed (#9b69ff at 4%). */
31
+ enriched?: boolean;
32
+ icon?: IconType;
33
+ /** This column's values reference another type of record (e.g. a
34
+ * "Primary location" column of Country records). When the header
35
+ * overflows, its `Tooltip` appends this type's icon and name after the
36
+ * label and switches to Attio's second, reference-column tooltip chrome
37
+ * (crisp#580) — distinct from `icon` above (the column's own field-type
38
+ * glyph shown in the header row) and from `refOptions` (the picker's
39
+ * selectable values). Omit for a plain column; the tooltip stays
40
+ * label-only (#534). */
41
+ referencedType?: {
42
+ icon: IconType;
43
+ label: string;
44
+ };
45
+ width?: number;
46
+ /** Currency symbol for `currency` columns (default "$"). */
47
+ symbol?: string;
48
+ /** Choices for a `status` column (each a coloured dot + label). */
49
+ options?: StatusOption[];
50
+ /** Choices for a `reference` column (a searchable record picker). */
51
+ refOptions?: ReferenceOption[];
52
+ /** Adds a trailing "Create {createLabel}" row to the picker (Attio's own
53
+ * always has one) — rendered only when this is provided. Picking it hands
54
+ * back the picker's current search text, e.g. to open a create flow
55
+ * pre-seeded with it. */
56
+ onCreateReference?: (query: string) => void;
57
+ /** The object noun in "Create {createLabel}" (Attio: "Create Person").
58
+ * Only used with `onCreateReference`; defaults to "record". */
59
+ createLabel?: string;
60
+ /**
61
+ * Full custom cell content — an escape hatch for a display crisp can't
62
+ * express via `type` (sign-coloured currency, a maskable amount, a custom
63
+ * badge, …). Precedence is **`render` > `format` > the built-in `type`
64
+ * renderer**: when `render` is set it is the *only* thing drawn in the
65
+ * cell, replacing whatever `type` would normally render.
66
+ *
67
+ * Interaction contract for a `render` column: it keeps every grid chrome —
68
+ * row height, hairlines, padding, hover/selected tint, the cell focus ring,
69
+ * shift-click range select, the right-click row menu — but is **never**
70
+ * inline-editable and never shows a `type` picker (status/reference/date/
71
+ * tags), because crisp has no way to write a value back into custom
72
+ * content. Use `format` instead if the column should stay editable.
73
+ *
74
+ * ⌘C copies `format(value, row)` when the column also has one, otherwise
75
+ * the raw value stringified the same way a plain column would — never a
76
+ * serialization of the rendered node. Sort, filter, and the footer
77
+ * aggregation all read `row[column.key]` directly and never call `render`.
78
+ */
79
+ render?: (value: unknown, row: Row) => React.ReactNode;
80
+ /**
81
+ * Display-only string transform. Unlike `render`, a `format` column keeps
82
+ * the built-in `type` renderer's chrome (a `currency`/`number` column stays
83
+ * right-aligned in its numeric span; `text`/`url`/`date`/the default case
84
+ * keep their own wrapper) — `format`'s return value simply replaces the
85
+ * text that renderer would otherwise show. `format` fully owns that text,
86
+ * so a `currency` column's `format` should include its own symbol/masking
87
+ * if it wants one (the built-in `$` prefix is skipped once `format` is
88
+ * set). A `format` column stays editable/pickable exactly like a plain
89
+ * `type` column — only `render` disables that.
90
+ *
91
+ * Sort, filter, and the footer aggregation all read the **raw**
92
+ * `row[column.key]` value, never `format`'s output — so a grouped/prefixed
93
+ * currency string still sums and sorts numerically.
94
+ */
95
+ format?: (value: unknown, row: Row) => string;
96
+ /**
97
+ * Opts this column into `RecordPanel`/`RecordPage`'s editable-field API
98
+ * (#431) — click-to-edit there. A plain `editable` column, with no
99
+ * `editor`, opens a text/number/date field (inferred from `type`); `editor`
100
+ * below picks a `select`/`switch` picker instead. Requires `onEdit` to
101
+ * actually be given for a field to open at all.
102
+ */
103
+ editable?: boolean;
104
+ /** `select`/`switch` render as the field's own live, always-interactive
105
+ * content instead of opening on click. `select` reads `editOptions`. */
106
+ editor?: "select" | "switch";
107
+ /** Options for `editor: "select"`. Not `options` (`StatusOption` requires
108
+ * `color`, for this type's own `status` picker) so this stays additive. */
109
+ editOptions?: EditableOption[];
110
+ };
111
+ export declare function tagTone(t: string): string;
112
+ export {};
@@ -1,6 +1,6 @@
1
1
  import { type LucideIcon } from "lucide-react";
2
2
  import * as React from "react";
3
- import { type Column, type Row } from "../recordstable";
3
+ import { type Column, type Row } from "./column";
4
4
  export type RecordPageNote = {
5
5
  id: string;
6
6
  text: string;
@@ -0,0 +1,3 @@
1
+ import type { Column, Row } from "./column";
2
+ export declare const testColumns: Column[];
3
+ export declare const testRows: Row[];
@@ -1,6 +1,6 @@
1
1
  import * as React from "react";
2
2
  import { type AvatarProps } from "../../components/avatar";
3
- import type { Row } from "../recordstable";
3
+ import type { Row } from "../recordpage/column";
4
4
  type IconType = React.ComponentType<{
5
5
  size?: number;
6
6
  strokeWidth?: number;
@@ -1,3 +1,3 @@
1
- export { type EditableOption, editableInputType, useEditableField, } from "./editable-field";
1
+ export { type EditableOption, editableInputType, useEditableField, } from "../recordpage/editable-field";
2
2
  export { type Column, type ColumnType, defaultColumns, defaultRows, type EntityValue, RecordsTable, type RecordsTableProps, type Row, tagTone, } from "./recordstable";
3
3
  export { recordstableRecipe } from "./recordstable.recipe";
@@ -1,6 +1,6 @@
1
1
  import * as React from "react";
2
2
  import { type EmptyStateLearnMore } from "../../components/emptystate";
3
- import type { EditableOption } from "./editable-field";
3
+ import type { EditableOption } from "../recordpage/editable-field";
4
4
  type IconType = React.ComponentType<{
5
5
  size?: number;
6
6
  strokeWidth?: number;
@@ -283,6 +283,11 @@ export { formatCellDate, parseCellDate };
283
283
  * editing (double-click a text/number/url/date cell), add-row and add-column,
284
284
  * and a floating bulk-action bar (delete / deselect) when rows are selected.
285
285
  * Selection can be controlled via `selectedIds`/`onSelectionChange`.
286
+ *
287
+ * @deprecated Reach for `DataTable` instead — a single Attio-exact records
288
+ * grid replaces the separate table variants. `RecordsTable` has no
289
+ * consumers left (checked GRC and the fms/Mercury admin) and is slated for
290
+ * removal in a future minor.
286
291
  */
287
292
  export declare function RecordsTable({ labels, columns: columnsProp, rows: rowsProp, selectedIds, onSelectionChange, showNew, allowAddColumn, onOpenRecord, onOpenRecordPage, avatarTone, emptyStateExtra, className, ...rest }: RecordsTableProps): React.JSX.Element;
288
293
  export declare const defaultColumns: Column[];
@@ -47,6 +47,11 @@ export type TasksTableProps = Omit<React.ComponentPropsWithoutRef<"div">, "child
47
47
  * taskstable.recipe.ts) with a typed column/cell layer: a title+checkbox
48
48
  * row grouped into due-date sections, a due-date cell that goes red past
49
49
  * due, and consumer-supplied record/assignee chip slots.
50
+ *
51
+ * @deprecated Reach for `DataTable`'s `groupBy` instead — a single
52
+ * Attio-exact records grid replaces the separate table variants.
53
+ * `TasksTable` has no consumers left (checked GRC and the fms/Mercury
54
+ * admin) and is slated for removal in a future minor.
50
55
  */
51
56
  export declare function TasksTable({ columns, groups, leading, trailing, emptyLabel: emptyLabelProp, completeLabel: completeLabelProp, incompleteLabel: incompleteLabelProp, className, ...rest }: TasksTableProps): React.JSX.Element;
52
57
  export declare namespace TasksTable {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crisp-ui-kit/crisp",
3
- "version": "0.54.9",
3
+ "version": "0.54.11",
4
4
  "description": "A lean React design system, measured pixel-for-pixel from Attio.",
5
5
  "license": "MIT",
6
6
  "type": "module",