@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
@@ -0,0 +1,113 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.useEditableField = useEditableField;
37
+ exports.editableInputType = editableInputType;
38
+ const React = __importStar(require("react"));
39
+ /**
40
+ * The async-commit state machine behind `RecordPage`'s editable Details
41
+ * column (#431) — the same shape `DataTable`'s own `commitEdit` uses
42
+ * (#385/#430): a sync `onEdit` (returns `undefined`) commits and closes
43
+ * immediately; a Promise-returning `onEdit` keeps the field open and pending
44
+ * while it settles, then either closes it (resolve) or closes it AND
45
+ * reverts while showing the rejection's message (reject). The revert needs
46
+ * no extra code here either: `RecordPage` never mutates `record` itself, so
47
+ * once `editingKey` clears, the field simply renders `record[key]` again —
48
+ * whatever it still says.
49
+ *
50
+ * `RecordPanel` (the peek panel) used to share this hook too, before #448
51
+ * found that Attio's own peek has no editable field at all to attach it to
52
+ * — only `RecordPage`'s Details column does. Kept as its own module rather
53
+ * than folded back into `recordpage.tsx`: a second consumer turning up
54
+ * again is a smaller change than re-extracting it would be.
55
+ */
56
+ function useEditableField(onEdit) {
57
+ const [editingKey, setEditingKey] = React.useState(null);
58
+ // Set only while an async `onEdit` is in flight -- a sync `onEdit` never
59
+ // touches this.
60
+ const [pendingKey, setPendingKey] = React.useState(null);
61
+ const [error, setError] = React.useState(null);
62
+ // Opening a field's editor also clears any stale error left over from a
63
+ // PREVIOUS failed commit on it -- otherwise a rejection from three edits
64
+ // ago would still be sitting there while the reader tries again.
65
+ const openEditing = React.useCallback((key) => {
66
+ setError(null);
67
+ setEditingKey(key);
68
+ }, []);
69
+ const closeEditing = React.useCallback(() => setEditingKey(null), []);
70
+ const commit = React.useCallback((id, key, value) => {
71
+ setError(null);
72
+ const result = onEdit?.(id, key, value);
73
+ if (result && typeof result.then === "function") {
74
+ setPendingKey(key);
75
+ result.then(() => {
76
+ setPendingKey(null);
77
+ setEditingKey(null);
78
+ }, (err) => {
79
+ setPendingKey(null);
80
+ setEditingKey(null);
81
+ setError({
82
+ key,
83
+ message: err instanceof Error
84
+ ? err.message
85
+ : typeof err === "string"
86
+ ? err
87
+ : "Could not save",
88
+ });
89
+ });
90
+ return;
91
+ }
92
+ setEditingKey(null);
93
+ }, [onEdit]);
94
+ return { editingKey, pendingKey, error, openEditing, closeEditing, commit };
95
+ }
96
+ /**
97
+ * Which plain editor a column gets when it's `editable` without an
98
+ * `editor: "select" | "switch"` — identical to `DataTable`'s own
99
+ * `editorFor` (#385): number/currency get a numeric field, date a native
100
+ * date field, everything else (text, entity, tags, url, status, reference)
101
+ * a plain text field. Kept as one function so the two never drift apart.
102
+ */
103
+ function editableInputType(colType) {
104
+ switch (colType) {
105
+ case "number":
106
+ case "currency":
107
+ return { type: "number", inputMode: "decimal" };
108
+ case "date":
109
+ return { type: "date" };
110
+ default:
111
+ return { type: "text", inputMode: "text" };
112
+ }
113
+ }
@@ -1,6 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.recordstableRecipe = exports.tagTone = exports.RecordsTable = exports.defaultRows = exports.defaultColumns = void 0;
3
+ exports.recordstableRecipe = exports.tagTone = exports.RecordsTable = exports.defaultRows = exports.defaultColumns = exports.useEditableField = exports.editableInputType = void 0;
4
+ var editable_field_1 = require("./editable-field");
5
+ Object.defineProperty(exports, "editableInputType", { enumerable: true, get: function () { return editable_field_1.editableInputType; } });
6
+ Object.defineProperty(exports, "useEditableField", { enumerable: true, get: function () { return editable_field_1.useEditableField; } });
4
7
  var recordstable_1 = require("./recordstable");
5
8
  Object.defineProperty(exports, "defaultColumns", { enumerable: true, get: function () { return recordstable_1.defaultColumns; } });
6
9
  Object.defineProperty(exports, "defaultRows", { enumerable: true, get: function () { return recordstable_1.defaultRows; } });
@@ -262,15 +262,17 @@ function EntityCell({ value, pinned, onOpenPage, avatarTone = "brand", labels, }
262
262
  }
263
263
  function CellView({ column, row, pinned, onOpenPage, avatarTone = "brand", labels, }) {
264
264
  const value = row[column.key];
265
- // `render` fully owns the cell's content — no empty-dash fallback, no
266
- // type renderer; it's the only thing drawn here (see the doc comment on
265
+ // `render` fully owns the cell's content — no empty fallback, no type
266
+ // renderer; it's the only thing drawn here (see the doc comment on
267
267
  // `Column.render`).
268
268
  if (column.render)
269
269
  return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: column.render(value, row) });
270
270
  if (value == null ||
271
271
  value === "" ||
272
272
  (Array.isArray(value) && !value.length)) {
273
- return (0, jsx_runtime_1.jsx)("span", { className: "crisp-recordstable-muted", children: "\u2014" });
273
+ // Attio's own empty cell is genuinely blank -- no placeholder character
274
+ // at all (#412).
275
+ return null;
274
276
  }
275
277
  // `format` keeps the type renderer's chrome below and only replaces the
276
278
  // text it displays.
@@ -313,11 +315,40 @@ function CellView({ column, row, pinned, onOpenPage, avatarTone = "brand", label
313
315
  function StatusCell({ column, row, onChange, }) {
314
316
  return ((0, jsx_runtime_1.jsx)(select_1.Select, { variant: "ghost", "aria-label": column.label, placeholder: "\u2014", value: row[column.key] ?? undefined, onValueChange: onChange, options: column.options ?? [] }));
315
317
  }
318
+ // A sentinel that can't collide with a real record id (crisp never generates
319
+ // one, and a consumer's own ids come from their own data store) -- picking
320
+ // this row calls `onCreateReference`, not `onChange` (#449).
321
+ const CREATE_REFERENCE_VALUE = "__crisp_create_reference__";
322
+ /** The 16px avatar Attio's own reference picker draws before every option's
323
+ * label -- a photo when the option carries one, a generic person glyph
324
+ * otherwise (measured live, #449: Attio's own fallback is an icon, not
325
+ * initials -- a different, deliberate choice from the entity cell's own
326
+ * initials fallback elsewhere in this file). */
327
+ function referenceOptionLabel(o) {
328
+ return ((0, jsx_runtime_1.jsxs)("span", { className: "crisp-recordstable-refoption", children: [(0, jsx_runtime_1.jsx)(avatar_1.Avatar, { size: "sm", className: "crisp-recordstable-refoption-avatar", children: o.avatar ? ((0, jsx_runtime_1.jsx)("img", { src: o.avatar, alt: "" })) : ((0, jsx_runtime_1.jsx)(lucide_react_1.CircleUserRound, { size: 12, strokeWidth: 1.75, "aria-hidden": "true" })) }), o.label] }));
329
+ }
316
330
  function ReferenceCell({ column, row, onChange, labels, }) {
317
- return ((0, jsx_runtime_1.jsx)(select_1.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) => ({
318
- value: o.value,
319
- label: o.label,
320
- })) }));
331
+ const [query, setQuery] = React.useState("");
332
+ const options = (column.refOptions ?? []).map((o) => ({
333
+ value: o.value,
334
+ label: referenceOptionLabel(o),
335
+ keywords: o.label,
336
+ }));
337
+ if (column.onCreateReference) {
338
+ options.push({
339
+ value: CREATE_REFERENCE_VALUE,
340
+ label: ((0, jsx_runtime_1.jsxs)("span", { className: "crisp-recordstable-refoption crisp-recordstable-refoption--create", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Plus, { size: 16, strokeWidth: 1.75, "aria-hidden": "true" }), `Create ${column.createLabel ?? "record"}`] })),
341
+ // always reachable regardless of the typed query -- it's an action,
342
+ // not a record to filter away
343
+ keywords: query,
344
+ });
345
+ }
346
+ return ((0, jsx_runtime_1.jsx)(select_1.Select, { variant: "ghost", searchable: true, searchPlaceholder: labels.searchRecordsPlaceholder, onSearchChange: setQuery, "aria-label": column.label, placeholder: labels.setRecordPlaceholder, value: row[column.key] ?? undefined, onValueChange: (v) => {
347
+ if (v === CREATE_REFERENCE_VALUE)
348
+ column.onCreateReference?.(query);
349
+ else
350
+ onChange(v);
351
+ }, options: options }));
321
352
  }
322
353
  function TagsCell({ column, row, options, onChange, labels, }) {
323
354
  const value = row[column.key] ?? [];
@@ -343,10 +374,35 @@ function TagsCell({ column, row, options, onChange, labels, }) {
343
374
  const core_2 = require("../../core");
344
375
  Object.defineProperty(exports, "formatCellDate", { enumerable: true, get: function () { return core_2.formatCellDate; } });
345
376
  Object.defineProperty(exports, "parseCellDate", { enumerable: true, get: function () { return core_2.parseCellDate; } });
377
+ // "1998" for a year-only raw value, "Sep 1998" for year-month, and the full
378
+ // "Sep 4, 1998" only once the raw value actually named a day (#312) --
379
+ // DatePickerField's own default format always prints full precision, since
380
+ // as a general calendar field it has no raw string to read a precision from.
381
+ function formatByPrecision(d, precision) {
382
+ if (precision === "year")
383
+ return String(d.getFullYear());
384
+ return d.toLocaleDateString(undefined, {
385
+ month: "short",
386
+ day: precision === "month" ? undefined : "numeric",
387
+ year: "numeric",
388
+ });
389
+ }
346
390
  function DateCell({ column, row, onChange, }) {
347
391
  const raw = row[column.key];
348
392
  const value = (0, core_2.parseCellDate)(raw);
349
- return ((0, jsx_runtime_1.jsx)(datepickerfield_1.DatePickerField, { variant: "ghost", "aria-label": column.label, value: value, onChange: (d) => onChange((0, core_2.formatCellDate)(d)) }));
393
+ const precision = raw ? (0, core_2.cellDatePrecision)(raw) : "day";
394
+ return ((0, jsx_runtime_1.jsx)(datepickerfield_1.DatePickerField, { variant: "ghost", "aria-label": column.label, value: value,
395
+ // Out of scope for #394 (which added Clear to DatePickerField itself):
396
+ // a cell's OWN clear-to-empty affordance is a different, unmeasured
397
+ // question, and this callback's `string` contract has no "empty" of
398
+ // its own to report yet. Keeps this cell's exact pre-#394 behavior.
399
+ clearable: false,
400
+ // #417: the trigger displays the value at its stored precision
401
+ // (year-only stays year-only) rather than always a full date.
402
+ format: (d) => formatByPrecision(d, precision), onChange: (d) => {
403
+ if (d)
404
+ onChange((0, core_2.formatCellDate)(d));
405
+ } }));
350
406
  }
351
407
  /**
352
408
  * The "New <object>" create modal — measured from a create-record dialog:
@@ -374,7 +430,7 @@ function CreateRecordDialog({ open, onOpenChange, columns, entityLabel, onCreate
374
430
  row[col.key] = { name: raw || labels.untitled };
375
431
  }
376
432
  else if (!raw) {
377
- // leave empty fields unset so the cell renders its "—" placeholder
433
+ // leave empty fields unset so the cell renders blank (#412)
378
434
  }
379
435
  else if (col.type === "tags") {
380
436
  row[col.key] = raw
@@ -406,7 +462,7 @@ function CreateRecordDialog({ open, onOpenChange, columns, entityLabel, onCreate
406
462
  return ((0, jsx_runtime_1.jsxs)(dialog_1.Dialog, { open: open, onOpenChange: onOpenChange, title: labels.newRecordTitle(entityLabel), icon: EntityIcon ? (0, jsx_runtime_1.jsx)(EntityIcon, { size: 16, strokeWidth: 1.75 }) : undefined, width: 720, children: [(0, jsx_runtime_1.jsx)(dialog_1.DialogBody, { children: columns.map((col, i) => ((0, jsx_runtime_1.jsxs)("div", { className: "crisp-dialog-field", children: [(0, jsx_runtime_1.jsx)("label", { className: "crisp-dialog-field-label", htmlFor: `create-${col.key}`, children: col.label }), (0, jsx_runtime_1.jsx)(input_1.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) => {
407
463
  if (e.key === "Enter" && (e.metaKey || e.ctrlKey))
408
464
  submit();
409
- } })] }, col.key))) }), (0, jsx_runtime_1.jsxs)(dialog_1.DialogFooter, { children: [(0, jsx_runtime_1.jsxs)("span", { className: "crisp-dialog-more", children: [labels.createMore, (0, jsx_runtime_1.jsx)(switch_1.Switch, { checked: createMore, onCheckedChange: setCreateMore, "aria-label": labels.createMore })] }), (0, jsx_runtime_1.jsxs)("button", { type: "button", className: "crisp-dialog-submit", onClick: submit, children: [labels.createRecord, (0, jsx_runtime_1.jsx)("span", { className: "crisp-dialog-submit-key", children: "\u2318\u21B5" })] })] })] }));
465
+ } })] }, col.key))) }), (0, jsx_runtime_1.jsxs)(dialog_1.DialogFooter, { children: [(0, jsx_runtime_1.jsxs)("span", { className: "crisp-dialog-more", children: [(0, jsx_runtime_1.jsx)(switch_1.Switch, { checked: createMore, onCheckedChange: setCreateMore, "aria-label": labels.createMore }), labels.createMore] }), (0, jsx_runtime_1.jsxs)("button", { type: "button", className: "crisp-dialog-submit", onClick: submit, children: [labels.createRecord, (0, jsx_runtime_1.jsxs)("span", { className: "crisp-dialog-submit-key", "aria-hidden": "true", children: [(0, jsx_runtime_1.jsx)("kbd", { children: "\u2318" }), (0, jsx_runtime_1.jsx)("kbd", { children: (0, jsx_runtime_1.jsx)(lucide_react_1.CornerDownLeft, { size: 10, strokeWidth: 2 }) })] })] })] })] }));
410
466
  }
411
467
  /**
412
468
  * A measured-style record grid — fully featured. Toolbar (search / sort status /
@@ -1073,7 +1129,7 @@ function RecordsTable({ labels, columns: columnsProp = exports.defaultColumns, r
1073
1129
  setDragCol(col.key);
1074
1130
  e.dataTransfer.effectAllowed = "move";
1075
1131
  }
1076
- : undefined, onDragEnd: () => setDragCol(null), onClick: (e) => openColumnMenu(col.key, e.currentTarget), children: [Icon ? (0, jsx_runtime_1.jsx)(Icon, { size: 14, strokeWidth: 1.75 }) : null, (0, jsx_runtime_1.jsx)("span", { className: "crisp-recordstable-th-label", children: col.label }), (0, jsx_runtime_1.jsx)("span", { className: "crisp-recordstable-sort", children: (0, jsx_runtime_1.jsx)("svg", { width: "12", height: "12", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "1.5", "aria-hidden": "true", children: (0, jsx_runtime_1.jsx)("path", { d: "m4 6 4 4 4-4" }) }) })] })), openMenu === `col:${col.key}` &&
1132
+ : undefined, onDragEnd: () => setDragCol(null), onClick: (e) => openColumnMenu(col.key, e.currentTarget), children: [Icon ? (0, jsx_runtime_1.jsx)(Icon, { size: 16, strokeWidth: 1.75 }) : null, (0, jsx_runtime_1.jsx)("span", { className: "crisp-recordstable-th-label", children: col.label }), (0, jsx_runtime_1.jsx)("span", { className: "crisp-recordstable-sort", children: (0, jsx_runtime_1.jsx)("svg", { width: "12", height: "12", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "1.5", "aria-hidden": "true", children: (0, jsx_runtime_1.jsx)("path", { d: "m4 6 4 4 4-4" }) }) })] })), openMenu === `col:${col.key}` &&
1077
1133
  rootRef.current &&
1078
1134
  (0, react_dom_1.createPortal)((0, jsx_runtime_1.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: [(0, jsx_runtime_1.jsxs)("button", { type: "button", className: "crisp-recordstable-menu-item", role: "menuitem", tabIndex: -1, onClick: () => sortBy(col.key, "asc"), children: [(0, jsx_runtime_1.jsx)(lucide_react_1.ArrowUp, { size: 15, strokeWidth: 1.75 }), t.sortAscending] }), (0, jsx_runtime_1.jsxs)("button", { type: "button", className: "crisp-recordstable-menu-item", role: "menuitem", tabIndex: -1, onClick: () => sortBy(col.key, "desc"), children: [(0, jsx_runtime_1.jsx)(lucide_react_1.ArrowDown, { size: 15, strokeWidth: 1.75 }), t.sortDescending] }), (0, jsx_runtime_1.jsxs)("button", { type: "button", className: "crisp-recordstable-menu-item", role: "menuitem", tabIndex: -1, disabled: i <= 1, onClick: () => moveColumn(col.key, -1), children: [(0, jsx_runtime_1.jsx)(lucide_react_1.ArrowLeft, { "data-rtl-flip": "", size: 15, strokeWidth: 1.75 }), t.moveLeft] }), (0, jsx_runtime_1.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: [(0, jsx_runtime_1.jsx)(lucide_react_1.ArrowRight, { "data-rtl-flip": "", size: 15, strokeWidth: 1.75 }), t.moveRight] }), (0, jsx_runtime_1.jsx)("div", { className: "crisp-recordstable-menu-sep" }), (0, jsx_runtime_1.jsxs)("button", { type: "button", className: "crisp-recordstable-menu-item", role: "menuitem", tabIndex: -1, onClick: () => {
1079
1135
  setRenamingCol(col.key);
@@ -74,6 +74,14 @@ ${(0, tabletoolbar_chrome_1.toolbarChromeBody)("recordstable")}
74
74
  & .crisp-recordstable-td--picker .crisp-multiselect-chips .crisp-recordstable-tag { flex-shrink: 0; max-width: none; }
75
75
  & .crisp-recordstable-td--picker .crisp-multiselect-chevron { display: none; }
76
76
  & .crisp-recordstable-td--picker .crisp-multiselect-placeholder { color: ${tokens_1.contract.fgMuted}; }
77
+ /* reference picker: a leading 16px avatar per option, plus a trailing
78
+ "Create X" row when the consumer supplies one (#449) -- measured live
79
+ against Attio's own picker (16px circular avatar, 16px plain icon for
80
+ the create row, both sitting in the option's existing 8px gap, no new
81
+ gap value invented). */
82
+ & .crisp-recordstable-refoption { display: flex; align-items: center; gap: ${tokens_1.contract.space["2"]}; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
83
+ & .crisp-recordstable-refoption-avatar { flex-shrink: 0; }
84
+ & .crisp-recordstable-refoption--create { color: ${tokens_1.contract.fgMuted}; }
77
85
  & .crisp-recordstable-status { display: inline-flex; align-items: center; gap: ${tokens_1.contract.space["1.5"]}; min-width: 0; }
78
86
  & .crisp-recordstable-status-dot { flex-shrink: 0; width: 8px; height: 8px; border-radius: ${tokens_1.contract.radius.full}; }
79
87
  & .crisp-recordstable-cell { display: flex; align-items: center; gap: ${tokens_1.contract.space["2"]}; height: 100%; min-width: 0; }
@@ -132,7 +140,7 @@ ${(0, tabletoolbar_chrome_1.toolbarChromeBody)("recordstable")}
132
140
  & .crisp-recordstable-empty { display: flex; align-items: center; justify-content: center; min-height: 320px; padding: ${tokens_1.contract.space["8"]} ${tokens_1.contract.space["4"]}; }
133
141
 
134
142
  /* summary footer — per-column aggregation, sticky to the bottom */
135
- & .crisp-recordstable-sum { position: sticky; bottom: 0; z-index: 2; height: ${tokens_1.contract.size.tableRow}; padding: 0; border-top: 1px solid ${tokens_1.contract.borderSubtle}; border-inline-end: 1px solid ${tokens_1.contract.borderSubtle}; background: ${tokens_1.contract.bgSurface}; }
143
+ & .crisp-recordstable-sum { position: sticky; bottom: 0; z-index: 2; height: ${tokens_1.contract.size.tableRow}; padding: 0; border-top: 1px solid ${tokens_1.contract.borderSubtle}; border-bottom: 1px solid ${tokens_1.contract.borderSubtle}; border-inline-end: 1px solid ${tokens_1.contract.borderSubtle}; background: ${tokens_1.contract.bgSurface}; }
136
144
  & tfoot .crisp-recordstable-col-check, & tfoot .crisp-recordstable-col-entity { position: sticky; bottom: 0; z-index: 3; }
137
145
  & .crisp-recordstable-sum:last-child { border-inline-end: none; }
138
146
  & .crisp-recordstable-sum-btn { display: flex; align-items: center; justify-content: flex-end; gap: ${tokens_1.contract.space["1.5"]}; width: 100%; height: 100%; margin: 0; padding: 0 ${tokens_1.contract.space["3"]}; border: none; background: none; color: color-mix(in srgb, ${tokens_1.contract.fgPrimary} 63%, transparent); font-family: inherit; font-size: ${tokens_1.contract.text.lg}; font-weight: ${tokens_1.contract.weight.medium}; cursor: pointer; white-space: nowrap; overflow: hidden; }
@@ -7,9 +7,14 @@ const tokens_1 = require("../../tokens");
7
7
  * `border-radius: ${c.radius.chip}`, 14px/20px at weight 500, and a 1px inset ring in the
8
8
  * tone's colour.
9
9
  *
10
- * Shared because RecordsTable and RecordPanel render the *same* chip. It used
11
- * to be pasted into both recipes, and the two copies had already started to
12
- * drift — adding the measured inset ring meant patching the same rules twice.
10
+ * Its own module (not pasted into `recordstable.recipe.ts`) because it used
11
+ * to be shared with `RecordPanel`'s own Details field list too, and the two
12
+ * copies had already started to drift before that sharing existed — adding
13
+ * the measured inset ring meant patching the same rules twice. `RecordPanel`
14
+ * no longer renders a Details list (#448, Attio's own peek doesn't have
15
+ * one), so `RecordsTable` is this module's only consumer now; kept as its
16
+ * own file rather than folded back in, since a second consumer showing up
17
+ * again is exactly how the drift happened the first time.
13
18
  */
14
19
  exports.recordTagCss = `& .crisp-recordstable-tag { display: inline-flex; align-items: center; height: ${tokens_1.contract.size.chip}; padding: 0 ${tokens_1.contract.space["1"]}; border-radius: ${tokens_1.contract.radius.chip}; font-size: ${tokens_1.contract.text.lg}; line-height: 20px; font-weight: ${tokens_1.contract.weight.medium}; white-space: nowrap; background: color-mix(in srgb, ${tokens_1.contract.fgPrimary} 6%, transparent); color: color-mix(in srgb, ${tokens_1.contract.fgPrimary} 82%, transparent); box-shadow: inset 0 0 0 1px ${tokens_1.contract.borderSubtle}; }
15
20
  & .crisp-recordstable-tag[data-tone="brand"] { background: color-mix(in srgb, ${tokens_1.contract.bgBrandSolid} 14%, transparent); color: color-mix(in srgb, ${tokens_1.contract.bgBrandSolid} 82%, ${tokens_1.contract.fgPrimary}); box-shadow: inset 0 0 0 1px color-mix(in srgb, ${tokens_1.contract.bgBrandSolid} 30%, transparent); }
@@ -1,7 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.richtextRecipe = exports.RichTextEditor = void 0;
3
+ exports.richtextRecipe = exports.RichTextEditor = exports.RICHTEXT_COLOR_TONES = void 0;
4
4
  var richtext_1 = require("./richtext");
5
+ Object.defineProperty(exports, "RICHTEXT_COLOR_TONES", { enumerable: true, get: function () { return richtext_1.RICHTEXT_COLOR_TONES; } });
5
6
  Object.defineProperty(exports, "RichTextEditor", { enumerable: true, get: function () { return richtext_1.RichTextEditor; } });
6
7
  var richtext_recipe_1 = require("./richtext.recipe");
7
8
  Object.defineProperty(exports, "richtextRecipe", { enumerable: true, get: function () { return richtext_recipe_1.richtextRecipe; } });