@crisp-ui-kit/crisp 0.50.0 → 0.52.0

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 (128) hide show
  1. package/dist/cjs/components/composewindow/composewindow.js +144 -0
  2. package/dist/cjs/components/composewindow/composewindow.recipe.js +45 -0
  3. package/dist/cjs/components/composewindow/index.js +8 -0
  4. package/dist/cjs/components/datatable/datatable.js +224 -43
  5. package/dist/cjs/components/datatable/datatable.recipe.js +37 -3
  6. package/dist/cjs/components/datatable/index.js +6 -0
  7. package/dist/cjs/components/datepickerfield/datepickerfield.js +25 -15
  8. package/dist/cjs/components/datepickerfield/datepickerfield.recipe.js +25 -8
  9. package/dist/cjs/components/dialog/dialog.recipe.js +9 -1
  10. package/dist/cjs/components/menu/menu.js +4 -2
  11. package/dist/cjs/components/multiselect/multiselect.recipe.js +14 -8
  12. package/dist/cjs/components/select/select.js +2 -1
  13. package/dist/cjs/components/table/table.recipe.js +12 -3
  14. package/dist/cjs/components/tabletoolbar/tabletoolbar.chrome.js +1 -1
  15. package/dist/cjs/components/tabletoolbar/tabletoolbar.js +18 -7
  16. package/dist/cjs/core/caret-rect.js +73 -0
  17. package/dist/cjs/core/cell-date.js +11 -0
  18. package/dist/cjs/core/index.js +8 -1
  19. package/dist/cjs/core/mention.js +32 -0
  20. package/dist/cjs/core/use-mention-trigger.js +124 -0
  21. package/dist/cjs/index.js +1 -0
  22. package/dist/cjs/product/recordpage/recordpage.js +88 -10
  23. package/dist/cjs/product/recordpage/recordpage.recipe.js +30 -0
  24. package/dist/cjs/product/recordpanel/recordpanel.js +11 -29
  25. package/dist/cjs/product/recordpanel/recordpanel.recipe.js +55 -17
  26. package/dist/cjs/product/recordstable/editable-field.js +113 -0
  27. package/dist/cjs/product/recordstable/index.js +4 -1
  28. package/dist/cjs/product/recordstable/recordstable.js +67 -11
  29. package/dist/cjs/product/recordstable/recordstable.recipe.js +9 -1
  30. package/dist/cjs/product/recordstable/recordstable.tag.js +8 -3
  31. package/dist/cjs/product/richtext/index.js +2 -1
  32. package/dist/cjs/product/richtext/richtext.js +440 -23
  33. package/dist/cjs/product/richtext/richtext.recipe.js +59 -9
  34. package/dist/cjs/product/settingsshell/settingsshell.recipe.js +13 -4
  35. package/dist/cjs/product/sidebar/sidebar.js +15 -3
  36. package/dist/cjs/product/sidebar/sidebar.recipe.js +21 -2
  37. package/dist/composewindow.css +24 -0
  38. package/dist/composewindow.d.ts +7 -0
  39. package/dist/composewindow.layered.css +26 -0
  40. package/dist/datatable.css +33 -14
  41. package/dist/datatable.layered.css +33 -14
  42. package/dist/datepickerfield.css +9 -4
  43. package/dist/datepickerfield.layered.css +9 -4
  44. package/dist/dialog.css +2 -1
  45. package/dist/dialog.layered.css +2 -1
  46. package/dist/esm/components/composewindow/composewindow.js +107 -0
  47. package/dist/esm/components/composewindow/composewindow.recipe.js +42 -0
  48. package/dist/esm/components/composewindow/index.js +2 -0
  49. package/dist/esm/components/datatable/datatable.js +226 -45
  50. package/dist/esm/components/datatable/datatable.recipe.js +37 -3
  51. package/dist/esm/components/datatable/index.js +6 -0
  52. package/dist/esm/components/datepickerfield/datepickerfield.js +26 -16
  53. package/dist/esm/components/datepickerfield/datepickerfield.recipe.js +25 -8
  54. package/dist/esm/components/dialog/dialog.recipe.js +9 -1
  55. package/dist/esm/components/menu/menu.js +4 -2
  56. package/dist/esm/components/multiselect/multiselect.recipe.js +14 -8
  57. package/dist/esm/components/select/select.js +2 -1
  58. package/dist/esm/components/table/table.recipe.js +12 -3
  59. package/dist/esm/components/tabletoolbar/tabletoolbar.chrome.js +1 -1
  60. package/dist/esm/components/tabletoolbar/tabletoolbar.js +18 -7
  61. package/dist/esm/core/caret-rect.js +70 -0
  62. package/dist/esm/core/cell-date.js +10 -0
  63. package/dist/esm/core/index.js +4 -1
  64. package/dist/esm/core/mention.js +29 -0
  65. package/dist/esm/core/use-mention-trigger.js +88 -0
  66. package/dist/esm/index.js +1 -0
  67. package/dist/esm/product/recordpage/recordpage.js +89 -11
  68. package/dist/esm/product/recordpage/recordpage.recipe.js +30 -0
  69. package/dist/esm/product/recordpanel/index.js +1 -1
  70. package/dist/esm/product/recordpanel/recordpanel.js +13 -31
  71. package/dist/esm/product/recordpanel/recordpanel.recipe.js +56 -18
  72. package/dist/esm/product/recordstable/editable-field.js +76 -0
  73. package/dist/esm/product/recordstable/index.js +1 -0
  74. package/dist/esm/product/recordstable/recordstable.js +69 -13
  75. package/dist/esm/product/recordstable/recordstable.recipe.js +9 -1
  76. package/dist/esm/product/recordstable/recordstable.tag.js +8 -3
  77. package/dist/esm/product/richtext/index.js +1 -1
  78. package/dist/esm/product/richtext/richtext.js +439 -22
  79. package/dist/esm/product/richtext/richtext.recipe.js +60 -10
  80. package/dist/esm/product/settingsshell/settingsshell.recipe.js +13 -4
  81. package/dist/esm/product/sidebar/sidebar.js +15 -3
  82. package/dist/esm/product/sidebar/sidebar.recipe.js +21 -2
  83. package/dist/multiselect.css +8 -8
  84. package/dist/multiselect.layered.css +8 -8
  85. package/dist/recordpage.css +25 -13
  86. package/dist/recordpage.layered.css +25 -13
  87. package/dist/recordpanel.css +43 -20
  88. package/dist/recordpanel.layered.css +43 -20
  89. package/dist/recordstable.css +9 -2
  90. package/dist/recordstable.layered.css +9 -2
  91. package/dist/richtext.css +31 -5
  92. package/dist/richtext.layered.css +31 -5
  93. package/dist/settingsshell.css +2 -2
  94. package/dist/settingsshell.layered.css +2 -2
  95. package/dist/sidebar.css +7 -2
  96. package/dist/sidebar.layered.css +7 -2
  97. package/dist/styles.css +199 -74
  98. package/dist/styles.layered.css +199 -74
  99. package/dist/table.css +4 -2
  100. package/dist/table.layered.css +4 -2
  101. package/dist/tabletoolbar.css +1 -1
  102. package/dist/tabletoolbar.layered.css +1 -1
  103. package/dist/types/components/composewindow/composewindow.d.ts +80 -0
  104. package/dist/types/components/composewindow/composewindow.recipe.d.ts +3 -0
  105. package/dist/types/components/composewindow/index.d.ts +2 -0
  106. package/dist/types/components/datatable/datatable.d.ts +58 -4
  107. package/dist/types/components/datatable/index.d.ts +1 -1
  108. package/dist/types/components/datepickerfield/datepickerfield.d.ts +18 -4
  109. package/dist/types/components/menu/menu.d.ts +10 -1
  110. package/dist/types/components/select/select.d.ts +6 -1
  111. package/dist/types/components/tabletoolbar/tabletoolbar.d.ts +29 -5
  112. package/dist/types/core/caret-rect.d.ts +16 -0
  113. package/dist/types/core/cell-date.d.ts +17 -0
  114. package/dist/types/core/index.d.ts +4 -1
  115. package/dist/types/core/mention.d.ts +27 -0
  116. package/dist/types/core/use-mention-trigger.d.ts +41 -0
  117. package/dist/types/index.d.ts +1 -0
  118. package/dist/types/product/recordpage/recordpage.d.ts +59 -1
  119. package/dist/types/product/recordpanel/index.d.ts +1 -1
  120. package/dist/types/product/recordpanel/recordpanel.d.ts +81 -17
  121. package/dist/types/product/recordstable/editable-field.d.ts +48 -0
  122. package/dist/types/product/recordstable/index.d.ts +1 -0
  123. package/dist/types/product/recordstable/recordstable.d.ts +29 -0
  124. package/dist/types/product/recordstable/recordstable.tag.d.ts +8 -3
  125. package/dist/types/product/richtext/index.d.ts +1 -1
  126. package/dist/types/product/richtext/richtext.d.ts +168 -7
  127. package/dist/types/product/sidebar/sidebar.d.ts +60 -4
  128. package/package.json +6 -1
@@ -1,5 +1,5 @@
1
1
  import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { ArrowDown, ArrowDownWideNarrow, ArrowLeft, ArrowRight, ArrowUp, ArrowUpDown, ArrowUpNarrowWide, ArrowUpRight, AtSign, Briefcase, Building2, Calendar, Check, CircleHelp, Copy, CopyPlus, Download, EyeOff, Globe, GripVertical, Hash, Inbox, ListFilter, ListPlus, Mail, MapPin, MoreHorizontal, Pencil, Plus, Star, Tag, Trash2, TrendingUp, Users, Workflow, X, } from "lucide-react";
2
+ import { ArrowDown, ArrowDownWideNarrow, ArrowLeft, ArrowRight, ArrowUp, ArrowUpDown, ArrowUpNarrowWide, ArrowUpRight, AtSign, Briefcase, Building2, Calendar, Check, CircleHelp, CircleUserRound, Copy, CopyPlus, CornerDownLeft, Download, EyeOff, Globe, GripVertical, Hash, Inbox, ListFilter, ListPlus, Mail, MapPin, MoreHorizontal, Pencil, Plus, Star, Tag, Trash2, TrendingUp, Users, Workflow, X, } from "lucide-react";
3
3
  import * as React from "react";
4
4
  import { createPortal } from "react-dom";
5
5
  import { Avatar } from "../../components/avatar/index.js";
@@ -224,15 +224,17 @@ function EntityCell({ value, pinned, onOpenPage, avatarTone = "brand", labels, }
224
224
  }
225
225
  function CellView({ column, row, pinned, onOpenPage, avatarTone = "brand", labels, }) {
226
226
  const value = row[column.key];
227
- // `render` fully owns the cell's content — no empty-dash fallback, no
228
- // type renderer; it's the only thing drawn here (see the doc comment on
227
+ // `render` fully owns the cell's content — no empty fallback, no type
228
+ // renderer; it's the only thing drawn here (see the doc comment on
229
229
  // `Column.render`).
230
230
  if (column.render)
231
231
  return _jsx(_Fragment, { children: column.render(value, row) });
232
232
  if (value == null ||
233
233
  value === "" ||
234
234
  (Array.isArray(value) && !value.length)) {
235
- return _jsx("span", { className: "crisp-recordstable-muted", children: "\u2014" });
235
+ // Attio's own empty cell is genuinely blank -- no placeholder character
236
+ // at all (#412).
237
+ return null;
236
238
  }
237
239
  // `format` keeps the type renderer's chrome below and only replaces the
238
240
  // text it displays.
@@ -275,11 +277,40 @@ function CellView({ column, row, pinned, onOpenPage, avatarTone = "brand", label
275
277
  function StatusCell({ column, row, onChange, }) {
276
278
  return (_jsx(Select, { variant: "ghost", "aria-label": column.label, placeholder: "\u2014", value: row[column.key] ?? undefined, onValueChange: onChange, options: column.options ?? [] }));
277
279
  }
280
+ // A sentinel that can't collide with a real record id (crisp never generates
281
+ // one, and a consumer's own ids come from their own data store) -- picking
282
+ // this row calls `onCreateReference`, not `onChange` (#449).
283
+ const CREATE_REFERENCE_VALUE = "__crisp_create_reference__";
284
+ /** The 16px avatar Attio's own reference picker draws before every option's
285
+ * label -- a photo when the option carries one, a generic person glyph
286
+ * otherwise (measured live, #449: Attio's own fallback is an icon, not
287
+ * initials -- a different, deliberate choice from the entity cell's own
288
+ * initials fallback elsewhere in this file). */
289
+ function referenceOptionLabel(o) {
290
+ return (_jsxs("span", { className: "crisp-recordstable-refoption", children: [_jsx(Avatar, { size: "sm", className: "crisp-recordstable-refoption-avatar", children: o.avatar ? (_jsx("img", { src: o.avatar, alt: "" })) : (_jsx(CircleUserRound, { size: 12, strokeWidth: 1.75, "aria-hidden": "true" })) }), o.label] }));
291
+ }
278
292
  function ReferenceCell({ column, row, onChange, labels, }) {
279
- return (_jsx(Select, { variant: "ghost", searchable: true, searchPlaceholder: labels.searchRecordsPlaceholder, "aria-label": column.label, placeholder: labels.setRecordPlaceholder, value: row[column.key] ?? undefined, onValueChange: onChange, options: (column.refOptions ?? []).map((o) => ({
280
- value: o.value,
281
- label: o.label,
282
- })) }));
293
+ const [query, setQuery] = React.useState("");
294
+ const options = (column.refOptions ?? []).map((o) => ({
295
+ value: o.value,
296
+ label: referenceOptionLabel(o),
297
+ keywords: o.label,
298
+ }));
299
+ if (column.onCreateReference) {
300
+ options.push({
301
+ value: CREATE_REFERENCE_VALUE,
302
+ label: (_jsxs("span", { className: "crisp-recordstable-refoption crisp-recordstable-refoption--create", children: [_jsx(Plus, { size: 16, strokeWidth: 1.75, "aria-hidden": "true" }), `Create ${column.createLabel ?? "record"}`] })),
303
+ // always reachable regardless of the typed query -- it's an action,
304
+ // not a record to filter away
305
+ keywords: query,
306
+ });
307
+ }
308
+ return (_jsx(Select, { variant: "ghost", searchable: true, searchPlaceholder: labels.searchRecordsPlaceholder, onSearchChange: setQuery, "aria-label": column.label, placeholder: labels.setRecordPlaceholder, value: row[column.key] ?? undefined, onValueChange: (v) => {
309
+ if (v === CREATE_REFERENCE_VALUE)
310
+ column.onCreateReference?.(query);
311
+ else
312
+ onChange(v);
313
+ }, options: options }));
283
314
  }
284
315
  function TagsCell({ column, row, options, onChange, labels, }) {
285
316
  const value = row[column.key] ?? [];
@@ -302,12 +333,37 @@ function TagsCell({ column, row, options, onChange, labels, }) {
302
333
  // reachable from this module's public surface.
303
334
  // imported for use here AND re-exported, because a bare `export ... from` does
304
335
  // not bind the names locally
305
- import { formatCellDate, parseCellDate } from "../../core/index.js";
336
+ import { cellDatePrecision, formatCellDate, parseCellDate, } from "../../core/index.js";
306
337
  export { formatCellDate, parseCellDate };
338
+ // "1998" for a year-only raw value, "Sep 1998" for year-month, and the full
339
+ // "Sep 4, 1998" only once the raw value actually named a day (#312) --
340
+ // DatePickerField's own default format always prints full precision, since
341
+ // as a general calendar field it has no raw string to read a precision from.
342
+ function formatByPrecision(d, precision) {
343
+ if (precision === "year")
344
+ return String(d.getFullYear());
345
+ return d.toLocaleDateString(undefined, {
346
+ month: "short",
347
+ day: precision === "month" ? undefined : "numeric",
348
+ year: "numeric",
349
+ });
350
+ }
307
351
  function DateCell({ column, row, onChange, }) {
308
352
  const raw = row[column.key];
309
353
  const value = parseCellDate(raw);
310
- return (_jsx(DatePickerField, { variant: "ghost", "aria-label": column.label, value: value, onChange: (d) => onChange(formatCellDate(d)) }));
354
+ const precision = raw ? cellDatePrecision(raw) : "day";
355
+ return (_jsx(DatePickerField, { variant: "ghost", "aria-label": column.label, value: value,
356
+ // Out of scope for #394 (which added Clear to DatePickerField itself):
357
+ // a cell's OWN clear-to-empty affordance is a different, unmeasured
358
+ // question, and this callback's `string` contract has no "empty" of
359
+ // its own to report yet. Keeps this cell's exact pre-#394 behavior.
360
+ clearable: false,
361
+ // #417: the trigger displays the value at its stored precision
362
+ // (year-only stays year-only) rather than always a full date.
363
+ format: (d) => formatByPrecision(d, precision), onChange: (d) => {
364
+ if (d)
365
+ onChange(formatCellDate(d));
366
+ } }));
311
367
  }
312
368
  /**
313
369
  * The "New <object>" create modal — measured from a create-record dialog:
@@ -335,7 +391,7 @@ function CreateRecordDialog({ open, onOpenChange, columns, entityLabel, onCreate
335
391
  row[col.key] = { name: raw || labels.untitled };
336
392
  }
337
393
  else if (!raw) {
338
- // leave empty fields unset so the cell renders its "—" placeholder
394
+ // leave empty fields unset so the cell renders blank (#412)
339
395
  }
340
396
  else if (col.type === "tags") {
341
397
  row[col.key] = raw
@@ -367,7 +423,7 @@ function CreateRecordDialog({ open, onOpenChange, columns, entityLabel, onCreate
367
423
  return (_jsxs(Dialog, { open: open, onOpenChange: onOpenChange, title: labels.newRecordTitle(entityLabel), icon: EntityIcon ? _jsx(EntityIcon, { size: 16, strokeWidth: 1.75 }) : undefined, width: 720, children: [_jsx(DialogBody, { children: columns.map((col, i) => (_jsxs("div", { className: "crisp-dialog-field", children: [_jsx("label", { className: "crisp-dialog-field-label", htmlFor: `create-${col.key}`, children: col.label }), _jsx(Input, { id: `create-${col.key}`, ref: i === 0 ? firstRef : undefined, value: values[col.key] ?? "", onChange: (e) => set(col.key, e.currentTarget.value), placeholder: labels.setFieldPlaceholder(col.label), onKeyDown: (e) => {
368
424
  if (e.key === "Enter" && (e.metaKey || e.ctrlKey))
369
425
  submit();
370
- } })] }, col.key))) }), _jsxs(DialogFooter, { children: [_jsxs("span", { className: "crisp-dialog-more", children: [labels.createMore, _jsx(Switch, { checked: createMore, onCheckedChange: setCreateMore, "aria-label": labels.createMore })] }), _jsxs("button", { type: "button", className: "crisp-dialog-submit", onClick: submit, children: [labels.createRecord, _jsx("span", { className: "crisp-dialog-submit-key", children: "\u2318\u21B5" })] })] })] }));
426
+ } })] }, col.key))) }), _jsxs(DialogFooter, { children: [_jsxs("span", { className: "crisp-dialog-more", children: [_jsx(Switch, { checked: createMore, onCheckedChange: setCreateMore, "aria-label": labels.createMore }), labels.createMore] }), _jsxs("button", { type: "button", className: "crisp-dialog-submit", onClick: submit, children: [labels.createRecord, _jsxs("span", { className: "crisp-dialog-submit-key", "aria-hidden": "true", children: [_jsx("kbd", { children: "\u2318" }), _jsx("kbd", { children: _jsx(CornerDownLeft, { size: 10, strokeWidth: 2 }) })] })] })] })] }));
371
427
  }
372
428
  /**
373
429
  * A measured-style record grid — fully featured. Toolbar (search / sort status /
@@ -1034,7 +1090,7 @@ export function RecordsTable({ labels, columns: columnsProp = defaultColumns, ro
1034
1090
  setDragCol(col.key);
1035
1091
  e.dataTransfer.effectAllowed = "move";
1036
1092
  }
1037
- : undefined, onDragEnd: () => setDragCol(null), onClick: (e) => openColumnMenu(col.key, e.currentTarget), children: [Icon ? _jsx(Icon, { size: 14, strokeWidth: 1.75 }) : null, _jsx("span", { className: "crisp-recordstable-th-label", children: col.label }), _jsx("span", { className: "crisp-recordstable-sort", children: _jsx("svg", { width: "12", height: "12", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "1.5", "aria-hidden": "true", children: _jsx("path", { d: "m4 6 4 4 4-4" }) }) })] })), openMenu === `col:${col.key}` &&
1093
+ : undefined, onDragEnd: () => setDragCol(null), onClick: (e) => openColumnMenu(col.key, e.currentTarget), children: [Icon ? _jsx(Icon, { size: 16, strokeWidth: 1.75 }) : null, _jsx("span", { className: "crisp-recordstable-th-label", children: col.label }), _jsx("span", { className: "crisp-recordstable-sort", children: _jsx("svg", { width: "12", height: "12", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "1.5", "aria-hidden": "true", children: _jsx("path", { d: "m4 6 4 4 4-4" }) }) })] })), openMenu === `col:${col.key}` &&
1038
1094
  rootRef.current &&
1039
1095
  createPortal(_jsxs("div", { ref: menuRef, className: "crisp-recordstable-menu crisp-recordstable-menu--fixed crisp-recordstable-menu--hdr", role: "menu", "aria-label": `${col.label} column`, style: menuPos ?? undefined, onKeyDown: onMenuKeyDown, children: [_jsxs("button", { type: "button", className: "crisp-recordstable-menu-item", role: "menuitem", tabIndex: -1, onClick: () => sortBy(col.key, "asc"), children: [_jsx(ArrowUp, { size: 15, strokeWidth: 1.75 }), t.sortAscending] }), _jsxs("button", { type: "button", className: "crisp-recordstable-menu-item", role: "menuitem", tabIndex: -1, onClick: () => sortBy(col.key, "desc"), children: [_jsx(ArrowDown, { size: 15, strokeWidth: 1.75 }), t.sortDescending] }), _jsxs("button", { type: "button", className: "crisp-recordstable-menu-item", role: "menuitem", tabIndex: -1, disabled: i <= 1, onClick: () => moveColumn(col.key, -1), children: [_jsx(ArrowLeft, { "data-rtl-flip": "", size: 15, strokeWidth: 1.75 }), t.moveLeft] }), _jsxs("button", { type: "button", className: "crisp-recordstable-menu-item", role: "menuitem", tabIndex: -1, disabled: i === 0 || i >= cols.length - 1, onClick: () => moveColumn(col.key, 1), children: [_jsx(ArrowRight, { "data-rtl-flip": "", size: 15, strokeWidth: 1.75 }), t.moveRight] }), _jsx("div", { className: "crisp-recordstable-menu-sep" }), _jsxs("button", { type: "button", className: "crisp-recordstable-menu-item", role: "menuitem", tabIndex: -1, onClick: () => {
1040
1096
  setRenamingCol(col.key);
@@ -71,6 +71,14 @@ ${toolbarChromeBody("recordstable")}
71
71
  & .crisp-recordstable-td--picker .crisp-multiselect-chips .crisp-recordstable-tag { flex-shrink: 0; max-width: none; }
72
72
  & .crisp-recordstable-td--picker .crisp-multiselect-chevron { display: none; }
73
73
  & .crisp-recordstable-td--picker .crisp-multiselect-placeholder { color: ${c.fgMuted}; }
74
+ /* reference picker: a leading 16px avatar per option, plus a trailing
75
+ "Create X" row when the consumer supplies one (#449) -- measured live
76
+ against Attio's own picker (16px circular avatar, 16px plain icon for
77
+ the create row, both sitting in the option's existing 8px gap, no new
78
+ gap value invented). */
79
+ & .crisp-recordstable-refoption { display: flex; align-items: center; gap: ${c.space["2"]}; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
80
+ & .crisp-recordstable-refoption-avatar { flex-shrink: 0; }
81
+ & .crisp-recordstable-refoption--create { color: ${c.fgMuted}; }
74
82
  & .crisp-recordstable-status { display: inline-flex; align-items: center; gap: ${c.space["1.5"]}; min-width: 0; }
75
83
  & .crisp-recordstable-status-dot { flex-shrink: 0; width: 8px; height: 8px; border-radius: ${c.radius.full}; }
76
84
  & .crisp-recordstable-cell { display: flex; align-items: center; gap: ${c.space["2"]}; height: 100%; min-width: 0; }
@@ -129,7 +137,7 @@ ${toolbarChromeBody("recordstable")}
129
137
  & .crisp-recordstable-empty { display: flex; align-items: center; justify-content: center; min-height: 320px; padding: ${c.space["8"]} ${c.space["4"]}; }
130
138
 
131
139
  /* summary footer — per-column aggregation, sticky to the bottom */
132
- & .crisp-recordstable-sum { position: sticky; bottom: 0; z-index: 2; height: ${c.size.tableRow}; padding: 0; border-top: 1px solid ${c.borderSubtle}; border-inline-end: 1px solid ${c.borderSubtle}; background: ${c.bgSurface}; }
140
+ & .crisp-recordstable-sum { position: sticky; bottom: 0; z-index: 2; height: ${c.size.tableRow}; padding: 0; border-top: 1px solid ${c.borderSubtle}; border-bottom: 1px solid ${c.borderSubtle}; border-inline-end: 1px solid ${c.borderSubtle}; background: ${c.bgSurface}; }
133
141
  & tfoot .crisp-recordstable-col-check, & tfoot .crisp-recordstable-col-entity { position: sticky; bottom: 0; z-index: 3; }
134
142
  & .crisp-recordstable-sum:last-child { border-inline-end: none; }
135
143
  & .crisp-recordstable-sum-btn { display: flex; align-items: center; justify-content: flex-end; gap: ${c.space["1.5"]}; width: 100%; height: 100%; margin: 0; padding: 0 ${c.space["3"]}; border: none; background: none; color: color-mix(in srgb, ${c.fgPrimary} 63%, transparent); font-family: inherit; font-size: ${c.text.lg}; font-weight: ${c.weight.medium}; cursor: pointer; white-space: nowrap; overflow: hidden; }
@@ -4,9 +4,14 @@ import { contract as c } from "../../tokens/index.js";
4
4
  * `border-radius: ${c.radius.chip}`, 14px/20px at weight 500, and a 1px inset ring in the
5
5
  * tone's colour.
6
6
  *
7
- * Shared because RecordsTable and RecordPanel render the *same* chip. It used
8
- * to be pasted into both recipes, and the two copies had already started to
9
- * drift — adding the measured inset ring meant patching the same rules twice.
7
+ * Its own module (not pasted into `recordstable.recipe.ts`) because it used
8
+ * to be shared with `RecordPanel`'s own Details field list too, and the two
9
+ * copies had already started to drift before that sharing existed — adding
10
+ * the measured inset ring meant patching the same rules twice. `RecordPanel`
11
+ * no longer renders a Details list (#448, Attio's own peek doesn't have
12
+ * one), so `RecordsTable` is this module's only consumer now; kept as its
13
+ * own file rather than folded back in, since a second consumer showing up
14
+ * again is exactly how the drift happened the first time.
10
15
  */
11
16
  export const recordTagCss = `& .crisp-recordstable-tag { display: inline-flex; align-items: center; height: ${c.size.chip}; padding: 0 ${c.space["1"]}; border-radius: ${c.radius.chip}; font-size: ${c.text.lg}; line-height: 20px; font-weight: ${c.weight.medium}; white-space: nowrap; background: color-mix(in srgb, ${c.fgPrimary} 6%, transparent); color: color-mix(in srgb, ${c.fgPrimary} 82%, transparent); box-shadow: inset 0 0 0 1px ${c.borderSubtle}; }
12
17
  & .crisp-recordstable-tag[data-tone="brand"] { background: color-mix(in srgb, ${c.bgBrandSolid} 14%, transparent); color: color-mix(in srgb, ${c.bgBrandSolid} 82%, ${c.fgPrimary}); box-shadow: inset 0 0 0 1px color-mix(in srgb, ${c.bgBrandSolid} 30%, transparent); }
@@ -1,2 +1,2 @@
1
- export { RichTextEditor, } from "./richtext.js";
1
+ export { RICHTEXT_COLOR_TONES, RichTextEditor, } from "./richtext.js";
2
2
  export { richtextRecipe } from "./richtext.recipe.js";