@crisp-ui-kit/crisp 0.54.1 → 0.54.2

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 (106) hide show
  1. package/dist/cjs/components/datatable/datatable.js +2 -1
  2. package/dist/cjs/components/datatable/datatable.recipe.js +4 -0
  3. package/dist/cjs/components/mentionchip/index.js +7 -0
  4. package/dist/cjs/components/mentionchip/mentionchip.js +57 -0
  5. package/dist/cjs/components/mentionchip/mentionchip.recipe.js +38 -0
  6. package/dist/cjs/components/table/table.recipe.js +15 -3
  7. package/dist/cjs/core/floating-stack.js +18 -0
  8. package/dist/cjs/core/index.js +2 -1
  9. package/dist/cjs/index.js +6 -0
  10. package/dist/cjs/product/formdialog/formdialog.js +171 -0
  11. package/dist/cjs/product/formdialog/formdialog.recipe.js +26 -0
  12. package/dist/cjs/product/formdialog/index.js +8 -0
  13. package/dist/cjs/product/importdialog/importdialog.js +134 -0
  14. package/dist/cjs/product/importdialog/importdialog.recipe.js +92 -0
  15. package/dist/cjs/product/importdialog/index.js +18 -0
  16. package/dist/cjs/product/savedviews/index.js +7 -0
  17. package/dist/cjs/product/savedviews/savedviews.js +207 -0
  18. package/dist/cjs/product/savedviews/savedviews.recipe.js +103 -0
  19. package/dist/cjs/product/taskstable/index.js +7 -0
  20. package/dist/cjs/product/taskstable/taskstable.js +27 -0
  21. package/dist/cjs/product/taskstable/taskstable.recipe.js +47 -0
  22. package/dist/cjs/product/themepicker/index.js +17 -0
  23. package/dist/cjs/product/themepicker/themepicker.js +32 -0
  24. package/dist/cjs/product/themepicker/themepicker.recipe.js +98 -0
  25. package/dist/cjs/tokens/palette.js +30 -13
  26. package/dist/cjs/tokens/semantic.js +6 -0
  27. package/dist/datatable.css +20 -21
  28. package/dist/datatable.layered.css +20 -21
  29. package/dist/esm/components/datatable/datatable.js +2 -1
  30. package/dist/esm/components/datatable/datatable.recipe.js +4 -0
  31. package/dist/esm/components/mentionchip/index.js +2 -0
  32. package/dist/esm/components/mentionchip/mentionchip.js +21 -0
  33. package/dist/esm/components/mentionchip/mentionchip.recipe.js +35 -0
  34. package/dist/esm/components/table/table.recipe.js +15 -3
  35. package/dist/esm/core/floating-stack.js +17 -0
  36. package/dist/esm/core/index.js +1 -1
  37. package/dist/esm/index.js +6 -0
  38. package/dist/esm/product/formdialog/formdialog.js +134 -0
  39. package/dist/esm/product/formdialog/formdialog.recipe.js +23 -0
  40. package/dist/esm/product/formdialog/index.js +2 -0
  41. package/dist/esm/product/importdialog/importdialog.js +97 -0
  42. package/dist/esm/product/importdialog/importdialog.recipe.js +89 -0
  43. package/dist/esm/product/importdialog/index.js +2 -0
  44. package/dist/esm/product/savedviews/index.js +2 -0
  45. package/dist/esm/product/savedviews/savedviews.js +171 -0
  46. package/dist/esm/product/savedviews/savedviews.recipe.js +100 -0
  47. package/dist/esm/product/taskstable/index.js +2 -0
  48. package/dist/esm/product/taskstable/taskstable.js +24 -0
  49. package/dist/esm/product/taskstable/taskstable.recipe.js +44 -0
  50. package/dist/esm/product/themepicker/index.js +1 -0
  51. package/dist/esm/product/themepicker/themepicker.js +29 -0
  52. package/dist/esm/product/themepicker/themepicker.recipe.js +95 -0
  53. package/dist/esm/tokens/palette.js +30 -13
  54. package/dist/esm/tokens/semantic.js +6 -0
  55. package/dist/formdialog.css +6 -0
  56. package/dist/formdialog.d.ts +7 -0
  57. package/dist/formdialog.layered.css +8 -0
  58. package/dist/importdialog.css +38 -0
  59. package/dist/importdialog.d.ts +7 -0
  60. package/dist/importdialog.layered.css +40 -0
  61. package/dist/mentionchip.css +3 -0
  62. package/dist/mentionchip.d.ts +7 -0
  63. package/dist/mentionchip.layered.css +5 -0
  64. package/dist/native/tokens.d.ts +1 -1
  65. package/dist/native/tokens.js +14 -6
  66. package/dist/savedviews.css +78 -0
  67. package/dist/savedviews.d.ts +7 -0
  68. package/dist/savedviews.layered.css +80 -0
  69. package/dist/styles.css +223 -30
  70. package/dist/styles.layered.css +223 -30
  71. package/dist/table.css +15 -6
  72. package/dist/table.layered.css +15 -6
  73. package/dist/taskstable.css +23 -0
  74. package/dist/taskstable.d.ts +7 -0
  75. package/dist/taskstable.layered.css +25 -0
  76. package/dist/themepicker.css +27 -0
  77. package/dist/themepicker.d.ts +7 -0
  78. package/dist/themepicker.layered.css +29 -0
  79. package/dist/tokens.css +7 -3
  80. package/dist/tokens.json +14 -6
  81. package/dist/types/components/datatable/datatable.d.ts +12 -1
  82. package/dist/types/components/mentionchip/index.d.ts +2 -0
  83. package/dist/types/components/mentionchip/mentionchip.d.ts +20 -0
  84. package/dist/types/components/mentionchip/mentionchip.recipe.d.ts +18 -0
  85. package/dist/types/core/floating-stack.d.ts +14 -0
  86. package/dist/types/core/index.d.ts +1 -1
  87. package/dist/types/index.d.ts +6 -0
  88. package/dist/types/product/formdialog/formdialog.d.ts +57 -0
  89. package/dist/types/product/formdialog/formdialog.recipe.d.ts +3 -0
  90. package/dist/types/product/formdialog/index.d.ts +2 -0
  91. package/dist/types/product/importdialog/importdialog.d.ts +86 -0
  92. package/dist/types/product/importdialog/importdialog.recipe.d.ts +3 -0
  93. package/dist/types/product/importdialog/index.d.ts +2 -0
  94. package/dist/types/product/savedviews/index.d.ts +2 -0
  95. package/dist/types/product/savedviews/savedviews.d.ts +55 -0
  96. package/dist/types/product/savedviews/savedviews.recipe.d.ts +3 -0
  97. package/dist/types/product/taskstable/index.d.ts +2 -0
  98. package/dist/types/product/taskstable/taskstable.d.ts +54 -0
  99. package/dist/types/product/taskstable/taskstable.recipe.d.ts +3 -0
  100. package/dist/types/product/themepicker/index.d.ts +1 -0
  101. package/dist/types/product/themepicker/themepicker.d.ts +21 -0
  102. package/dist/types/product/themepicker/themepicker.recipe.d.ts +3 -0
  103. package/dist/types/tokens/contract.d.ts +2 -0
  104. package/dist/types/tokens/palette.d.ts +11 -3
  105. package/dist/types/tokens/semantic.d.ts +6 -0
  106. package/package.json +31 -1
package/dist/tokens.css CHANGED
@@ -7,6 +7,7 @@
7
7
  --crisp-bg-raised: #f4f4f5;
8
8
  --crisp-border-default: #e4e4e7;
9
9
  --crisp-border-subtle: #eeeff1;
10
+ --crisp-border-dropzone: #e6e7ea;
10
11
  --crisp-ring-neutral: transparent;
11
12
  --crisp-ring-field: rgba(0,0,0,0.06);
12
13
  --crisp-ring-switch: #d9dade;
@@ -25,6 +26,7 @@
25
26
  --crisp-fg-muted: #71717a;
26
27
  --crisp-bg-danger: #e5484d;
27
28
  --crisp-bg-danger-solid: #ba2525;
29
+ --crisp-fg-danger: #eb3b3b;
28
30
  --crisp-stroke-field-focus: #1a1c20;
29
31
  --crisp-fg-disabled: #d1d3d8;
30
32
  --crisp-bg-neutral-weak: #f3f4f5;
@@ -59,6 +61,7 @@
59
61
  --crisp-bg-raised: #101112;
60
62
  --crisp-border-default: #212224;
61
63
  --crisp-border-subtle: #1c1d1f;
64
+ --crisp-border-dropzone: #212224;
62
65
  --crisp-ring-neutral: #212224;
63
66
  --crisp-ring-field: #212224;
64
67
  --crisp-ring-switch: #212224;
@@ -77,6 +80,7 @@
77
80
  --crisp-fg-muted: #9a9aa2;
78
81
  --crisp-bg-danger: #ff5a5f;
79
82
  --crisp-bg-danger-solid: #ba2525;
83
+ --crisp-fg-danger: #ff5a5f;
80
84
  --crisp-stroke-field-focus: #f3f4f5;
81
85
  --crisp-fg-disabled: #5b606a;
82
86
  --crisp-bg-neutral-weak: #1d2025;
@@ -97,9 +101,9 @@
97
101
  --crisp-fg-warning-soft: #e0982a;
98
102
  --crisp-fg-danger-soft: #ff5a5f;
99
103
  --crisp-fg-neutral-soft: #9a9aa2;
100
- --crisp-bg-purple-soft: #f5f0ff;
101
- --crisp-fg-purple-soft: #6238b5;
102
- --crisp-ring-purple-soft: #e8ddfe;
104
+ --crisp-bg-purple-soft: #2f1e5a;
105
+ --crisp-fg-purple-soft: #d8c4ff;
106
+ --crisp-ring-purple-soft: #45297d;
103
107
  }
104
108
 
105
109
  :root {
package/dist/tokens.json CHANGED
@@ -9,6 +9,7 @@
9
9
  "bgRaised": "#f4f4f5",
10
10
  "borderDefault": "#e4e4e7",
11
11
  "borderSubtle": "#eeeff1",
12
+ "borderDropzone": "#e6e7ea",
12
13
  "ringNeutral": "transparent",
13
14
  "ringField": "rgba(0,0,0,0.06)",
14
15
  "ringSwitch": "#d9dade",
@@ -27,6 +28,7 @@
27
28
  "fgMuted": "#71717a",
28
29
  "bgDanger": "#e5484d",
29
30
  "bgDangerSolid": "#ba2525",
31
+ "fgDanger": "#eb3b3b",
30
32
  "strokeFieldFocus": "#1a1c20",
31
33
  "fgDisabled": "#d1d3d8",
32
34
  "bgNeutralWeak": "#f3f4f5",
@@ -60,6 +62,7 @@
60
62
  "bgRaised": "#101112",
61
63
  "borderDefault": "#212224",
62
64
  "borderSubtle": "#1c1d1f",
65
+ "borderDropzone": "#212224",
63
66
  "ringNeutral": "#212224",
64
67
  "ringField": "#212224",
65
68
  "ringSwitch": "#212224",
@@ -78,6 +81,7 @@
78
81
  "fgMuted": "#9a9aa2",
79
82
  "bgDanger": "#ff5a5f",
80
83
  "bgDangerSolid": "#ba2525",
84
+ "fgDanger": "#ff5a5f",
81
85
  "strokeFieldFocus": "#f3f4f5",
82
86
  "fgDisabled": "#5b606a",
83
87
  "bgNeutralWeak": "#1d2025",
@@ -98,9 +102,9 @@
98
102
  "fgWarningSoft": "#e0982a",
99
103
  "fgDangerSoft": "#ff5a5f",
100
104
  "fgNeutralSoft": "#9a9aa2",
101
- "bgPurpleSoft": "#f5f0ff",
102
- "fgPurpleSoft": "#6238b5",
103
- "ringPurpleSoft": "#e8ddfe"
105
+ "bgPurpleSoft": "#2f1e5a",
106
+ "fgPurpleSoft": "#d8c4ff",
107
+ "ringPurpleSoft": "#45297d"
104
108
  }
105
109
  },
106
110
  "palette": {
@@ -112,6 +116,7 @@
112
116
  "gray900": "#101112",
113
117
  "line200": "#e4e4e7",
114
118
  "line100": "#eeeff1",
119
+ "line150": "#e6e7ea",
115
120
  "ringNeutral": "transparent",
116
121
  "ringField": "rgba(0,0,0,0.06)",
117
122
  "ringSwitch": "#d9dade",
@@ -131,6 +136,7 @@
131
136
  "brand700": "#1f5fd6",
132
137
  "red600": "#e5484d",
133
138
  "red700": "#ba2525",
139
+ "red500": "#eb3b3b",
134
140
  "fieldFocus": "#1a1c20",
135
141
  "disabledFg": "#d1d3d8",
136
142
  "neutralWeak": "#f3f4f5",
@@ -162,6 +168,7 @@
162
168
  "gray900": "#f5f5f5",
163
169
  "line200": "#212224",
164
170
  "line100": "#1c1d1f",
171
+ "line150": "#212224",
165
172
  "ringNeutral": "#212224",
166
173
  "ringField": "#212224",
167
174
  "ringSwitch": "#212224",
@@ -181,6 +188,7 @@
181
188
  "brand700": "#1f5fd6",
182
189
  "red600": "#ff5a5f",
183
190
  "red700": "#ba2525",
191
+ "red500": "#ff5a5f",
184
192
  "fieldFocus": "#f3f4f5",
185
193
  "disabledFg": "#5b606a",
186
194
  "neutralWeak": "#1d2025",
@@ -200,9 +208,9 @@
200
208
  "softWarningText": "#e0982a",
201
209
  "softDangerText": "#ff5a5f",
202
210
  "softNeutralText": "#9a9aa2",
203
- "purpleTint": "#f5f0ff",
204
- "softPurpleText": "#6238b5",
205
- "purpleRing": "#e8ddfe"
211
+ "purpleTint": "#2f1e5a",
212
+ "softPurpleText": "#d8c4ff",
213
+ "purpleRing": "#45297d"
206
214
  }
207
215
  },
208
216
  "space": {
@@ -220,6 +220,17 @@ export type DataTableProps<T extends {
220
220
  * direction like `stickyFirst` does: it pins to the trailing edge, which is
221
221
  * the left edge under `dir="rtl"` (#288). */
222
222
  stickyLast?: boolean;
223
+ /** Props on the scroll element itself (`style`/`className`/other div
224
+ * attributes) -- for bounding the grid's height so its sticky header and
225
+ * calc footer (#554) pin the way Attio's own grid does, the same
226
+ * `scrollProps` shape `Table` already takes directly (its own doc comment
227
+ * on #554 assumes this -- `DataTable` didn't actually expose it, #569).
228
+ * e.g. `scrollProps={{ style: { maxHeight: "calc(100vh - 220px)" } }}`.
229
+ * `ref` and `onScroll` are owned internally (this component tracks its own
230
+ * sticky-column scroll shadows) and are dropped if passed. `className`
231
+ * merges with the internal `crisp-table-scroll` class the same way
232
+ * `Table`'s own `scrollProps` does. */
233
+ scrollProps?: Omit<React.ComponentPropsWithoutRef<"div">, "ref" | "onScroll">;
223
234
  /** BCP 47 locale for the built-in `date`/`number`/`currency` renderers
224
235
  * (default "en-US"). Formats the date and groups the number the way that
225
236
  * locale's readers expect — e.g. "mn-MN" renders a date as "2026.09.12"
@@ -254,7 +265,7 @@ export type DataTableProps<T extends {
254
265
  };
255
266
  export declare function DataTable<T extends {
256
267
  id: string;
257
- }>({ columns: columnsProp, data, selectable, selectedIds, onSelectionChange, selectAllLabel: selectAllLabelProp, selectRowLabel, selectionAnnouncement, columnMenu, onColumnsChange, groupBy, onRowsReorder, error, sort: sortProp, defaultSort, onSortChange, filterable, searchPlaceholder: searchPlaceholderProp, searchLabel: searchLabelProp, onEdit, stickyFirst, stickyLast, locale: localeProp, avatarTone, surface, interactive, onRowClick, onAddRow, addRowLabel: addRowLabelProp, empty: emptyProp, showCount, countLabel, toolbar, className, reorderRowLabel: reorderRowLabelProp, sortAscendingLabel: sortAscendingLabelProp, sortDescendingLabel: sortDescendingLabelProp, moveColumnLeftLabel: moveColumnLeftLabelProp, moveColumnRightLabel: moveColumnRightLabelProp, editColumnLabel: editColumnLabelProp, hideColumnLabel: hideColumnLabelProp, renameColumnLabel: renameColumnLabelProp, viewSettingsLabel: viewSettingsLabelProp, columnsGroupLabel: columnsGroupLabelProp, ...rest }: DataTableProps<T>): React.JSX.Element;
268
+ }>({ columns: columnsProp, data, selectable, selectedIds, onSelectionChange, selectAllLabel: selectAllLabelProp, selectRowLabel, selectionAnnouncement, columnMenu, onColumnsChange, groupBy, onRowsReorder, error, sort: sortProp, defaultSort, onSortChange, filterable, searchPlaceholder: searchPlaceholderProp, searchLabel: searchLabelProp, onEdit, stickyFirst, stickyLast, scrollProps, locale: localeProp, avatarTone, surface, interactive, onRowClick, onAddRow, addRowLabel: addRowLabelProp, empty: emptyProp, showCount, countLabel, toolbar, className, reorderRowLabel: reorderRowLabelProp, sortAscendingLabel: sortAscendingLabelProp, sortDescendingLabel: sortDescendingLabelProp, moveColumnLeftLabel: moveColumnLeftLabelProp, moveColumnRightLabel: moveColumnRightLabelProp, editColumnLabel: editColumnLabelProp, hideColumnLabel: hideColumnLabelProp, renameColumnLabel: renameColumnLabelProp, viewSettingsLabel: viewSettingsLabelProp, columnsGroupLabel: columnsGroupLabelProp, ...rest }: DataTableProps<T>): React.JSX.Element;
258
269
  export declare namespace DataTable {
259
270
  var displayName: string;
260
271
  }
@@ -0,0 +1,2 @@
1
+ export { MentionChip, type MentionChipProps } from "./mentionchip";
2
+ export { mentionChipRecipe } from "./mentionchip.recipe";
@@ -0,0 +1,20 @@
1
+ import * as React from "react";
2
+ export type MentionChipProps = React.ComponentPropsWithoutRef<"span"> & {
3
+ asChild?: boolean;
4
+ };
5
+ /**
6
+ * A standalone "@mention" chip — the same look `RichTextEditor` draws
7
+ * inline while an editor is live (`.crisp-rte-mention`), exported on its
8
+ * own for a consumer rendering a mention OUTSIDE a mounted editor: a
9
+ * comment thread, a note's collapsed preview, anywhere a stored
10
+ * `@[Name](actor:id)`-shaped token needs to read as a person, not literal
11
+ * markup. Renders whatever `children` is given (the trigger character
12
+ * included, if the caller wants one, matching how `RichTextEditor`'s own
13
+ * chip includes it) rather than assuming an `@` prefix itself — plain text,
14
+ * no built-in click/remove behaviour (none was asked for; add one once a
15
+ * real interaction is measured, the same reasoning `Chip`'s own doc gives
16
+ * for not guessing a remove affordance).
17
+ */
18
+ export declare const MentionChip: React.ForwardRefExoticComponent<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>, "ref"> & {
19
+ asChild?: boolean;
20
+ } & React.RefAttributes<HTMLElement>>;
@@ -0,0 +1,18 @@
1
+ declare const spec: {
2
+ readonly name: "mention-chip";
3
+ readonly base: {
4
+ readonly display: "inline";
5
+ readonly borderRadius: `var(--${string})`;
6
+ readonly paddingInline: `var(--${string})`;
7
+ readonly background: `color-mix(in srgb, var(--${string}) 14%, transparent)`;
8
+ readonly color: `color-mix(in srgb, var(--${string}) 82%, var(--${string}))`;
9
+ readonly fontWeight: `var(--${string})`;
10
+ readonly cursor: "default";
11
+ };
12
+ };
13
+ declare const config: import("../..").RecipeConfig<Record<string, Record<string, string>>>;
14
+ export declare const mentionChipRecipe: import("../..").RecipeFn<Record<string, Record<string, string>>> & {
15
+ config: import("../..").RecipeConfig<Record<string, Record<string, string>>>;
16
+ };
17
+ export { spec };
18
+ export default config;
@@ -11,6 +11,20 @@ export declare function registerFloatingLayer(el: HTMLElement): () => void;
11
11
  * right query for "was this click inside some floating child I opened".
12
12
  */
13
13
  export declare function isInsideLaterFloatingLayer(target: Node, self: HTMLElement | null): boolean;
14
+ /**
15
+ * True when a layer is registered strictly after `self` — i.e. `self` has
16
+ * spawned a still-open nested layer of its own (a row's kebab `Menu` inside
17
+ * `SavedViews`' own panel, e.g.).
18
+ *
19
+ * This is `hasOpenFloatingLayer`'s sibling for a caller that DOES register
20
+ * itself on this stack: `hasOpenFloatingLayer` reads "is the stack
21
+ * non-empty", which is always true for such a caller once its own layer is
22
+ * open — checking that before closing on Escape would defer to itself
23
+ * forever and Escape would never close it. `self`'s own position on the
24
+ * stack has to be excluded from the count, which `hasOpenFloatingLayer`
25
+ * cannot do since it takes no argument.
26
+ */
27
+ export declare function hasNestedFloatingLayer(self: HTMLElement | null): boolean;
14
28
  /**
15
29
  * True when at least one `Select`/`MultiSelect`/`Menu`/`Popover`/
16
30
  * `ContextMenu` is currently open, anywhere (#361).
@@ -5,7 +5,7 @@ export { defineRecipe } from "./define";
5
5
  export { withDescribedBy } from "./dialog-description";
6
6
  export { isRtlElement } from "./direction";
7
7
  export { FloatingPortal } from "./floating-portal";
8
- export { hasOpenFloatingLayer, isInsideLaterFloatingLayer, registerFloatingLayer, } from "./floating-stack";
8
+ export { hasNestedFloatingLayer, hasOpenFloatingLayer, isInsideLaterFloatingLayer, registerFloatingLayer, } from "./floating-stack";
9
9
  export { formValueStyle } from "./form-value";
10
10
  export { type CrispComponentDefaults, type CrispMessages, CrispMessagesProvider, useCrispComponentDefaults, useCrispMessage, useCrispMessages, } from "./i18n";
11
11
  export { type MentionAnchor, type MentionMatch, matchMentionTrigger, } from "./mention";
@@ -29,6 +29,7 @@ export * from "./components/heatmapchart";
29
29
  export * from "./components/input";
30
30
  export * from "./components/kbd";
31
31
  export * from "./components/linechart";
32
+ export * from "./components/mentionchip";
32
33
  export * from "./components/menu";
33
34
  export * from "./components/middletruncate";
34
35
  export * from "./components/multiselect";
@@ -64,7 +65,9 @@ export * from "./product/board";
64
65
  export * from "./product/command";
65
66
  export * from "./product/entitycard";
66
67
  export * from "./product/flowcanvas";
68
+ export * from "./product/formdialog";
67
69
  export * from "./product/headerbar";
70
+ export * from "./product/importdialog";
68
71
  export * from "./product/inbox";
69
72
  export * from "./product/listpage";
70
73
  export * from "./product/navitem";
@@ -73,11 +76,14 @@ export * from "./product/recordpage";
73
76
  export * from "./product/recordpanel";
74
77
  export * from "./product/recordstable";
75
78
  export * from "./product/richtext";
79
+ export * from "./product/savedviews";
76
80
  export * from "./product/settingssection";
77
81
  export * from "./product/settingsshell";
78
82
  export * from "./product/sidebar";
79
83
  export * from "./product/statlist";
80
84
  export * from "./product/tasklist";
85
+ export * from "./product/taskstable";
86
+ export * from "./product/themepicker";
81
87
  export * from "./product/trialbanner";
82
88
  export * from "./product/viewtoggle";
83
89
  export * from "./tokens";
@@ -0,0 +1,57 @@
1
+ import * as React from "react";
2
+ /** See `OptionalLabelsContext`. Call from a field wrapper as
3
+ * `optional={useFormDialogOptional(required)}`. */
4
+ export declare function useFormDialogOptional(required: boolean): boolean;
5
+ export type FormDialogProps = Omit<React.ComponentPropsWithoutRef<"div">, "title" | "children" | "onSubmit" | "onClose"> & {
6
+ title: React.ReactNode;
7
+ /** The primary button's label on an edit dialog ("Save"). Ignored when
8
+ * `createMode` is set — Attio's own button always reads "Create record",
9
+ * never the object's name (measured live: "New Company"'s dialog still
10
+ * says "Create record", not "Create company"). */
11
+ submitLabel: React.ReactNode;
12
+ /** Called on submit — click, Enter in a field, or (in `createMode`)
13
+ * Cmd/Ctrl+Enter — with whether the "Create more" switch was on at that
14
+ * moment. An edit dialog (no switch, always `false`) can ignore it. */
15
+ onSubmit: (createMore: boolean) => void;
16
+ onClose: () => void;
17
+ busy?: boolean;
18
+ /** Anything the server rejected that belongs to no one field. */
19
+ error?: React.ReactNode;
20
+ children: React.ReactNode;
21
+ /** Widens the dialog for a form with more columns to hold. */
22
+ wide?: boolean;
23
+ /** Dialog content width in px, overrides `wide`. */
24
+ size?: number;
25
+ /** Bundles Attio's whole "New Company" create-dialog chrome (crisp#548,
26
+ * measured live): "(optional)" on every non-required field (via
27
+ * `useFormDialogOptional`); a footer of a "Create more" switch and a
28
+ * single "Create record ⌘↵" button with NO Cancel — Attio's dialog
29
+ * closes only from the header's ×; and Cmd/Ctrl+Enter submitting from
30
+ * anywhere inside the dialog, including a portaled Select/DatePicker
31
+ * popover. Only ever set by a create dialog — an edit dialog keeps
32
+ * Cancel + `submitLabel`, unmeasured territory the audit never touched. */
33
+ createMode?: boolean;
34
+ createMoreLabel?: string;
35
+ cancelLabel?: string;
36
+ discardTitle?: string;
37
+ discardConfirmLabel?: string;
38
+ discardCancelLabel?: string;
39
+ };
40
+ /**
41
+ * A modal for a create or edit form — crisp's own `Dialog`/`DialogBody`/
42
+ * `DialogFooter`, plus everything a record-creating form needs beyond plain
43
+ * chrome (crisp#548, moved out of internal/grc's own `forms.tsx`, called
44
+ * from nine places there): dirty tracking with a discard-changes confirm
45
+ * (any click/change/input inside the form marks it dirty; Escape/backdrop/
46
+ * header-×/Cancel all route through one `requestClose`, which only asks
47
+ * when something actually changed — a stacked crisp `Dialog`), a
48
+ * `beforeunload` guard for a reload or external navigation, and
49
+ * scroll-to-first-invalid-field once a submit attempt finishes (crisp's own
50
+ * `FormField` already marks every control `aria-invalid`, so this needs no
51
+ * per-field wiring). A record needs several fields at once to be valid,
52
+ * which is why records are not created by inline editing.
53
+ */
54
+ export declare function FormDialog({ title, submitLabel, onSubmit, onClose, busy, error, children, wide, size, createMode, createMoreLabel: createMoreLabelProp, cancelLabel: cancelLabelProp, discardTitle: discardTitleProp, discardConfirmLabel: discardConfirmLabelProp, discardCancelLabel: discardCancelLabelProp, className, ...rest }: FormDialogProps): React.JSX.Element;
55
+ export declare namespace FormDialog {
56
+ var displayName: string;
57
+ }
@@ -0,0 +1,3 @@
1
+ declare const config: import("../..").RecipeConfig<{}>;
2
+ export declare const formdialogRecipe: import("../..").RecipeFn<{}>;
3
+ export default config;
@@ -0,0 +1,2 @@
1
+ export { FormDialog, type FormDialogProps, useFormDialogOptional, } from "./formdialog";
2
+ export { formdialogRecipe } from "./formdialog.recipe";
@@ -0,0 +1,86 @@
1
+ import type { FileRejection, UseFileUploadProps } from "@seed-design/react-file-upload";
2
+ import * as React from "react";
3
+ export type { FileRejection };
4
+ export type ImportDialogStep = {
5
+ /** Stable id, compared against `activeStep`. */
6
+ id: string;
7
+ label: React.ReactNode;
8
+ };
9
+ /**
10
+ * Root props land on the DIALOG SURFACE — same convention as `Dialog`/
11
+ * `Drawer` (#223): the element a consumer actually wants to reach (for
12
+ * `aria-describedby`, a `data-testid`, a `style`) is the panel, not the
13
+ * portal wrapper.
14
+ */
15
+ export type ImportDialogProps = Omit<React.ComponentPropsWithoutRef<"div">, "title" | "children"> & {
16
+ open: boolean;
17
+ onOpenChange: (open: boolean) => void;
18
+ /** Header title (default "Import"). */
19
+ title?: React.ReactNode;
20
+ /** The fixed step header — id + label pairs, in order. */
21
+ steps: ImportDialogStep[];
22
+ /** Which step's id is current — drives both the header pill and, when a
23
+ * consumer wires `onStepClick`, which row gets `aria-current`. */
24
+ activeStep: string;
25
+ /** Fired when an earlier step's pill is clicked. Omit to make the header
26
+ * display-only (Attio's own back-navigation between steps wasn't part of
27
+ * this measurement pass). */
28
+ onStepClick?: (id: string) => void;
29
+ /** The active step's body. */
30
+ children?: React.ReactNode;
31
+ /** Primary action label (e.g. "Next", "Import 40 rows"). Default "Next". */
32
+ primaryLabel?: React.ReactNode;
33
+ onPrimary?: () => void;
34
+ primaryDisabled?: boolean;
35
+ "aria-label"?: string;
36
+ /** aria-label on the close button (default "Close"). */
37
+ closeLabel?: string;
38
+ };
39
+ /**
40
+ * A full-viewport step wizard — Attio's own CSV import flow (measured live:
41
+ * `1584x871` filling a ~1600x900 viewport, `border-radius: 0`, no shadow,
42
+ * opaque white). Composes Zag's dialog machine for focus-trap/scroll-lock/
43
+ * Escape exactly like `Dialog`/`Drawer`, with its own recipe for the
44
+ * full-bleed chrome neither of those two share (see importdialog.recipe.ts).
45
+ *
46
+ * The step header and footer are generic — the wizard doesn't know about
47
+ * CSV. Step 1's body is typically `ImportDialogDropzone`; steps 2-4
48
+ * (map columns / review values / preview import) are consumer-supplied
49
+ * `children`, since Attio's own column-mapping UI wasn't part of this
50
+ * measurement pass.
51
+ */
52
+ export declare function ImportDialog({ open, onOpenChange, title, steps, activeStep, onStepClick, children, primaryLabel, onPrimary, primaryDisabled, "aria-label": ariaLabel, closeLabel: closeLabelProp, className, style, "aria-describedby": describedBy, ...rest }: ImportDialogProps): React.JSX.Element | null;
53
+ export declare namespace ImportDialog {
54
+ var displayName: string;
55
+ }
56
+ export type ImportDialogDropzoneProps = Omit<React.ComponentPropsWithoutRef<"div">, "children" | "onDrop"> & {
57
+ /** Accepted file type(s), e.g. ".csv" (the default) or "text/csv". */
58
+ accept?: UseFileUploadProps["accept"];
59
+ disabled?: boolean;
60
+ maxFileSize?: number;
61
+ /** Fired with the accepted file. One file per drop/pick — a CSV import
62
+ * has exactly one source file, unlike the generic `FileUpload` list. */
63
+ onFileAccept?: (file: File) => void;
64
+ onFileReject?: (rejections: FileRejection[]) => void;
65
+ dropLabel?: React.ReactNode;
66
+ orLabel?: React.ReactNode;
67
+ chooseLabel?: React.ReactNode;
68
+ learnMoreHref?: string;
69
+ learnMoreLabel?: React.ReactNode;
70
+ };
71
+ /**
72
+ * The step-1 drop target — measured chrome (`border: 1px dashed
73
+ * rgb(230,231,234)`, `border-radius: 20px`, `background: rgb(251,251,251)`)
74
+ * over the SAME headless drag/drop + file-picker engine `FileUpload` uses
75
+ * (`@seed-design/react-file-upload`'s `useFileUpload`), rather than a fork
76
+ * of it. `FileUpload`'s own rendered markup (a single button carrying one
77
+ * title + one hint) can't host this dropzone's four separate pieces of copy
78
+ * — a title, an "or" divider, a "Choose a .CSV file" trigger, and a "Learn
79
+ * more" link that needs its own `<a href>` — so this composes the hook
80
+ * directly instead of wrapping `<FileUpload>`, and the engine (not this
81
+ * file) still owns every bit of drag-state/accept/reject behaviour.
82
+ */
83
+ export declare function ImportDialogDropzone({ accept, disabled, maxFileSize, onFileAccept, onFileReject, dropLabel, orLabel, chooseLabel, learnMoreHref, learnMoreLabel, className, id, ...rest }: ImportDialogDropzoneProps): React.JSX.Element;
84
+ export declare namespace ImportDialogDropzone {
85
+ var displayName: string;
86
+ }
@@ -0,0 +1,3 @@
1
+ declare const config: import("../..").RecipeConfig<{}>;
2
+ export declare const importdialogRecipe: import("../..").RecipeFn<{}>;
3
+ export default config;
@@ -0,0 +1,2 @@
1
+ export * from "./importdialog";
2
+ export * from "./importdialog.recipe";
@@ -0,0 +1,2 @@
1
+ export { type SavedView, SavedViews, type SavedViewsProps, } from "./savedviews";
2
+ export { savedviewsRecipe } from "./savedviews.recipe";
@@ -0,0 +1,55 @@
1
+ import * as React from "react";
2
+ export type SavedView = {
3
+ id: string;
4
+ name: React.ReactNode;
5
+ /** Leading 16×16 icon, coloured per-view (an emoji, a Lucide icon, …). */
6
+ icon?: React.ReactNode;
7
+ /** Renders a small shared-view indicator next to the name. */
8
+ shared?: boolean;
9
+ };
10
+ export type SavedViewsProps = Omit<React.ComponentPropsWithoutRef<"div">, "children" | "onSelect"> & {
11
+ /** The trigger's label when no view is active (Attio's "All Companies"). */
12
+ label: React.ReactNode;
13
+ /** The trigger's own leading icon when no view is active. */
14
+ icon?: React.ReactNode;
15
+ views: SavedView[];
16
+ /** The currently active view, or `null` for the unfiltered/default state. */
17
+ activeId: string | null;
18
+ onSelect: (id: string | null) => void;
19
+ /** "Create new view" — always the trailing row. */
20
+ onCreate: () => void;
21
+ onRename: (id: string) => void;
22
+ onDuplicate: (id: string) => void;
23
+ onDelete: (id: string) => void;
24
+ /** "Add to favorites" — the menu item is omitted when this isn't given. */
25
+ onToggleFavorite?: (id: string) => void;
26
+ createLabel?: string;
27
+ renameLabel?: string;
28
+ duplicateLabel?: string;
29
+ deleteLabel?: string;
30
+ favoriteLabel?: string;
31
+ /** aria-label for each row's kebab trigger. */
32
+ viewOptionsLabel?: string;
33
+ /** aria-label for the panel itself (defaults to the resolved trigger label). */
34
+ "aria-label"?: string;
35
+ className?: string;
36
+ };
37
+ /**
38
+ * A saved-view switcher — measured from Attio's "All Companies ▾" menu. A
39
+ * chip trigger opens a popover listing every saved view as a `role="option"`
40
+ * row (leading coloured icon, name, a hover-revealed kebab of
41
+ * rename/duplicate/delete), plus a trailing "Create new view" row. The
42
+ * active view is marked `aria-selected`/`aria-current="page"` and mirrored
43
+ * onto the trigger's own icon/label.
44
+ *
45
+ * Keyboard: while the trigger has focus and the panel is open, ↑/↓ move a
46
+ * virtual highlight (`aria-activedescendant`, the same "focus stays on the
47
+ * trigger" model `Select`/`MultiSelect` use without a search field), Enter
48
+ * chooses the highlighted row, Escape closes — deferring to a still-open
49
+ * kebab submenu first (`hasOpenFloatingLayer`), so Escape closes one layer
50
+ * at a time.
51
+ */
52
+ export declare function SavedViews({ label, icon, views, activeId, onSelect, onCreate, onRename, onDuplicate, onDelete, onToggleFavorite, createLabel: createLabelProp, renameLabel: renameLabelProp, duplicateLabel: duplicateLabelProp, deleteLabel: deleteLabelProp, favoriteLabel: favoriteLabelProp, viewOptionsLabel: viewOptionsLabelProp, className, "aria-label": ariaLabel, ...rest }: SavedViewsProps): React.JSX.Element;
53
+ export declare namespace SavedViews {
54
+ var displayName: string;
55
+ }
@@ -0,0 +1,3 @@
1
+ declare const config: import("../..").RecipeConfig<{}>;
2
+ export declare const savedviewsRecipe: import("../..").RecipeFn<{}>;
3
+ export default config;
@@ -0,0 +1,2 @@
1
+ export { TasksTable, type TasksTableColumn, type TasksTableGroup, type TasksTableProps, type TasksTableRow, } from "./taskstable";
2
+ export { taskstableRecipe } from "./taskstable.recipe";
@@ -0,0 +1,54 @@
1
+ import type * as React from "react";
2
+ export type TasksTableColumn = {
3
+ key: string;
4
+ label: React.ReactNode;
5
+ icon?: React.ReactNode;
6
+ /** Flex ratio for this column's header cell and every row's matching cell
7
+ * (matched by `key`). Defaults to 1. */
8
+ flex?: number;
9
+ };
10
+ export type TasksTableRow = {
11
+ id: string;
12
+ title: React.ReactNode;
13
+ done?: boolean;
14
+ onToggle?: (done: boolean) => void;
15
+ due?: {
16
+ label: React.ReactNode;
17
+ overdue?: boolean;
18
+ };
19
+ /** A record-chip slot, consumer-supplied — crisp has no generic "record
20
+ * chip" component, so this stays a plain node. */
21
+ record?: React.ReactNode;
22
+ /** A person-chip slot, same reasoning as `record`. */
23
+ assignee?: React.ReactNode;
24
+ };
25
+ export type TasksTableGroup = {
26
+ id: string;
27
+ label: React.ReactNode;
28
+ count: number;
29
+ rows: TasksTableRow[];
30
+ };
31
+ export type TasksTableProps = Omit<React.ComponentPropsWithoutRef<"div">, "children"> & {
32
+ columns: TasksTableColumn[];
33
+ groups: TasksTableGroup[];
34
+ /** Toolbar leading slot (Sort/Filter) — Attio's own Tasks page reference
35
+ * is `TableToolbar` with `columns`/`sortColumns`/`filterColumns`. */
36
+ leading?: React.ReactNode;
37
+ /** Toolbar trailing slot (View settings, "New task"). */
38
+ trailing?: React.ReactNode;
39
+ emptyLabel?: React.ReactNode;
40
+ completeLabel?: string;
41
+ incompleteLabel?: string;
42
+ className?: string;
43
+ };
44
+ /**
45
+ * A grouped due-date table pattern — Attio's Tasks page. Composes
46
+ * `ListPage`'s toolbar and group-row chrome (both class-compatible; see
47
+ * taskstable.recipe.ts) with a typed column/cell layer: a title+checkbox
48
+ * row grouped into due-date sections, a due-date cell that goes red past
49
+ * due, and consumer-supplied record/assignee chip slots.
50
+ */
51
+ export declare function TasksTable({ columns, groups, leading, trailing, emptyLabel: emptyLabelProp, completeLabel: completeLabelProp, incompleteLabel: incompleteLabelProp, className, ...rest }: TasksTableProps): React.JSX.Element;
52
+ export declare namespace TasksTable {
53
+ var displayName: string;
54
+ }
@@ -0,0 +1,3 @@
1
+ declare const config: import("../..").RecipeConfig<{}>;
2
+ export declare const taskstableRecipe: import("../..").RecipeFn<{}>;
3
+ export default config;
@@ -0,0 +1 @@
1
+ export * from "./themepicker";
@@ -0,0 +1,21 @@
1
+ import type * as React from "react";
2
+ export type ThemePickerValue = "light" | "dark" | "system";
3
+ export type ThemePickerProps = Omit<React.ComponentPropsWithoutRef<"div">, "onChange" | "role"> & {
4
+ value: ThemePickerValue;
5
+ onChange: (value: ThemePickerValue) => void;
6
+ /** Override one or more of the three option labels. Each falls back to
7
+ * its English default, translated through `CrispMessagesProvider` if
8
+ * one is mounted. */
9
+ labels?: Partial<Record<ThemePickerValue, string>>;
10
+ className?: string;
11
+ };
12
+ /**
13
+ * A light/dark/system theme picker — three cards, each a small preview of
14
+ * what that option looks like, radio-selectable. See the recipe's own doc
15
+ * comment for the live Attio measurement (crisp#462 gap 4) and for why the
16
+ * preview swatch colours are literal rather than `var(--crisp-*)` tokens.
17
+ */
18
+ export declare function ThemePicker({ value, onChange, labels, className, ...rest }: ThemePickerProps): React.JSX.Element;
19
+ export declare namespace ThemePicker {
20
+ var displayName: string;
21
+ }
@@ -0,0 +1,3 @@
1
+ declare const config: import("../..").RecipeConfig<{}>;
2
+ export declare const themepickerRecipe: import("../..").RecipeFn<{}>;
3
+ export default config;
@@ -9,6 +9,7 @@ export declare const contract: MapLeafNodes<{
9
9
  bgRaised: "gray50";
10
10
  borderDefault: "line200";
11
11
  borderSubtle: "line100";
12
+ borderDropzone: "line150";
12
13
  ringNeutral: "ringNeutral";
13
14
  ringField: "ringField";
14
15
  ringSwitch: "ringSwitch";
@@ -27,6 +28,7 @@ export declare const contract: MapLeafNodes<{
27
28
  fgMuted: "gray400";
28
29
  bgDanger: "red600";
29
30
  bgDangerSolid: "red700";
31
+ fgDanger: "red500";
30
32
  strokeFieldFocus: "fieldFocus";
31
33
  fgDisabled: "disabledFg";
32
34
  bgNeutralWeak: "neutralWeak";