@crisp-ui-kit/crisp 0.57.2 → 0.57.4

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 (200) hide show
  1. package/dist/assigneepicker.css +13 -0
  2. package/dist/assigneepicker.d.ts +7 -0
  3. package/dist/assigneepicker.layered.css +15 -0
  4. package/dist/button.css +3 -0
  5. package/dist/button.d.ts +2 -2
  6. package/dist/button.layered.css +3 -0
  7. package/dist/chip.css +1 -1
  8. package/dist/chip.layered.css +1 -1
  9. package/dist/cjs/components/breadcrumb/breadcrumb.js +1 -1
  10. package/dist/cjs/components/button/button.recipe.js +33 -0
  11. package/dist/cjs/components/chip/chip.recipe.js +3 -1
  12. package/dist/cjs/components/datatable/datatable.js +6 -3
  13. package/dist/cjs/components/datatable/datatable.recipe.js +45 -6
  14. package/dist/cjs/components/datepickerfield/datepickerfield.js +2 -2
  15. package/dist/cjs/components/datepickerfield/datepickerfield.recipe.js +16 -3
  16. package/dist/cjs/components/menu/menu.js +65 -9
  17. package/dist/cjs/components/multiselect/multiselect.js +3 -0
  18. package/dist/cjs/components/multiselect/multiselect.recipe.js +18 -0
  19. package/dist/cjs/components/select/select.js +2 -2
  20. package/dist/cjs/components/select/select.recipe.js +19 -0
  21. package/dist/cjs/components/table/table.recipe.js +7 -1
  22. package/dist/cjs/core/floating-portal.js +7 -4
  23. package/dist/cjs/core/index.js +2 -1
  24. package/dist/cjs/core/place-floating.js +38 -0
  25. package/dist/cjs/index.js +8 -0
  26. package/dist/cjs/product/assigneepicker/assigneepicker.js +127 -0
  27. package/dist/cjs/product/assigneepicker/assigneepicker.recipe.js +36 -0
  28. package/dist/cjs/product/assigneepicker/index.js +7 -0
  29. package/dist/cjs/product/inbox/inbox.js +11 -3
  30. package/dist/cjs/product/notesgallery/index.js +7 -0
  31. package/dist/cjs/product/notesgallery/notesgallery.js +80 -0
  32. package/dist/cjs/product/notesgallery/notesgallery.recipe.js +46 -0
  33. package/dist/cjs/product/objectpagemenu/index.js +17 -0
  34. package/dist/cjs/product/objectpagemenu/objectpagemenu.js +26 -0
  35. package/dist/cjs/product/objectpagemenu/objectpagemenu.recipe.js +30 -0
  36. package/dist/cjs/product/objectsettings/objectsettings.js +107 -14
  37. package/dist/cjs/product/recordpage/index.js +2 -1
  38. package/dist/cjs/product/recordpage/recordpage.js +112 -7
  39. package/dist/cjs/product/recordpage/recordpage.recipe.js +63 -0
  40. package/dist/cjs/product/recordpicker/index.js +7 -0
  41. package/dist/cjs/product/recordpicker/recordpicker.js +100 -0
  42. package/dist/cjs/product/recordpicker/recordpicker.recipe.js +36 -0
  43. package/dist/cjs/product/savedviews/index.js +2 -1
  44. package/dist/cjs/product/savedviews/savedviews.js +22 -0
  45. package/dist/cjs/product/savedviews/savedviews.recipe.js +47 -0
  46. package/dist/cjs/product/share/index.js +7 -0
  47. package/dist/cjs/product/share/share.js +179 -0
  48. package/dist/cjs/product/share/share.recipe.js +114 -0
  49. package/dist/cjs/product/shortcutssheet/index.js +17 -0
  50. package/dist/cjs/product/shortcutssheet/shortcutssheet.js +125 -0
  51. package/dist/cjs/product/shortcutssheet/shortcutssheet.recipe.js +50 -0
  52. package/dist/cjs/product/taskeditpopover/index.js +7 -0
  53. package/dist/cjs/product/taskeditpopover/taskeditpopover.js +159 -0
  54. package/dist/cjs/product/taskeditpopover/taskeditpopover.recipe.js +68 -0
  55. package/dist/cjs/product/workspaceswitchermenu/index.js +17 -0
  56. package/dist/cjs/product/workspaceswitchermenu/workspaceswitchermenu.js +38 -0
  57. package/dist/cjs/product/workspaceswitchermenu/workspaceswitchermenu.recipe.js +34 -0
  58. package/dist/cjs/tokens/palette.js +27 -0
  59. package/dist/cjs/tokens/semantic.js +20 -0
  60. package/dist/datatable.css +50 -11
  61. package/dist/datatable.layered.css +50 -11
  62. package/dist/datepickerfield.css +8 -2
  63. package/dist/datepickerfield.d.ts +2 -2
  64. package/dist/datepickerfield.layered.css +8 -2
  65. package/dist/esm/components/breadcrumb/breadcrumb.js +1 -1
  66. package/dist/esm/components/button/button.recipe.js +33 -0
  67. package/dist/esm/components/chip/chip.recipe.js +3 -1
  68. package/dist/esm/components/datatable/datatable.js +6 -3
  69. package/dist/esm/components/datatable/datatable.recipe.js +45 -6
  70. package/dist/esm/components/datepickerfield/datepickerfield.js +2 -2
  71. package/dist/esm/components/datepickerfield/datepickerfield.recipe.js +16 -3
  72. package/dist/esm/components/menu/menu.js +64 -8
  73. package/dist/esm/components/multiselect/multiselect.js +3 -0
  74. package/dist/esm/components/multiselect/multiselect.recipe.js +18 -0
  75. package/dist/esm/components/select/select.js +2 -2
  76. package/dist/esm/components/select/select.recipe.js +19 -0
  77. package/dist/esm/components/table/table.recipe.js +7 -1
  78. package/dist/esm/core/floating-portal.js +7 -4
  79. package/dist/esm/core/index.js +1 -1
  80. package/dist/esm/core/place-floating.js +37 -0
  81. package/dist/esm/index.js +8 -0
  82. package/dist/esm/product/assigneepicker/assigneepicker.js +91 -0
  83. package/dist/esm/product/assigneepicker/assigneepicker.recipe.js +33 -0
  84. package/dist/esm/product/assigneepicker/index.js +2 -0
  85. package/dist/esm/product/inbox/inbox.js +11 -3
  86. package/dist/esm/product/notesgallery/index.js +2 -0
  87. package/dist/esm/product/notesgallery/notesgallery.js +44 -0
  88. package/dist/esm/product/notesgallery/notesgallery.recipe.js +43 -0
  89. package/dist/esm/product/objectpagemenu/index.js +1 -0
  90. package/dist/esm/product/objectpagemenu/objectpagemenu.js +23 -0
  91. package/dist/esm/product/objectpagemenu/objectpagemenu.recipe.js +27 -0
  92. package/dist/esm/product/objectsettings/objectsettings.js +106 -13
  93. package/dist/esm/product/recordpage/index.js +1 -1
  94. package/dist/esm/product/recordpage/recordpage.js +112 -8
  95. package/dist/esm/product/recordpage/recordpage.recipe.js +63 -0
  96. package/dist/esm/product/recordpicker/index.js +2 -0
  97. package/dist/esm/product/recordpicker/recordpicker.js +64 -0
  98. package/dist/esm/product/recordpicker/recordpicker.recipe.js +33 -0
  99. package/dist/esm/product/savedviews/index.js +1 -1
  100. package/dist/esm/product/savedviews/savedviews.js +22 -1
  101. package/dist/esm/product/savedviews/savedviews.recipe.js +47 -0
  102. package/dist/esm/product/share/index.js +2 -0
  103. package/dist/esm/product/share/share.js +143 -0
  104. package/dist/esm/product/share/share.recipe.js +111 -0
  105. package/dist/esm/product/shortcutssheet/index.js +1 -0
  106. package/dist/esm/product/shortcutssheet/shortcutssheet.js +88 -0
  107. package/dist/esm/product/shortcutssheet/shortcutssheet.recipe.js +47 -0
  108. package/dist/esm/product/taskeditpopover/index.js +2 -0
  109. package/dist/esm/product/taskeditpopover/taskeditpopover.js +123 -0
  110. package/dist/esm/product/taskeditpopover/taskeditpopover.recipe.js +65 -0
  111. package/dist/esm/product/workspaceswitchermenu/index.js +1 -0
  112. package/dist/esm/product/workspaceswitchermenu/workspaceswitchermenu.js +35 -0
  113. package/dist/esm/product/workspaceswitchermenu/workspaceswitchermenu.recipe.js +31 -0
  114. package/dist/esm/tokens/palette.js +27 -0
  115. package/dist/esm/tokens/semantic.js +20 -0
  116. package/dist/multiselect.css +18 -0
  117. package/dist/multiselect.layered.css +18 -0
  118. package/dist/native/tokens.d.ts +1 -1
  119. package/dist/native/tokens.js +56 -4
  120. package/dist/notesgallery.css +15 -0
  121. package/dist/notesgallery.d.ts +7 -0
  122. package/dist/notesgallery.layered.css +17 -0
  123. package/dist/objectpagemenu.css +3 -0
  124. package/dist/objectpagemenu.d.ts +7 -0
  125. package/dist/objectpagemenu.layered.css +5 -0
  126. package/dist/recordpage.css +59 -0
  127. package/dist/recordpage.layered.css +59 -0
  128. package/dist/recordpicker.css +13 -0
  129. package/dist/recordpicker.d.ts +7 -0
  130. package/dist/recordpicker.layered.css +15 -0
  131. package/dist/savedviews.css +47 -0
  132. package/dist/savedviews.layered.css +47 -0
  133. package/dist/select.css +19 -0
  134. package/dist/select.layered.css +19 -0
  135. package/dist/share.css +84 -0
  136. package/dist/share.d.ts +7 -0
  137. package/dist/share.layered.css +86 -0
  138. package/dist/shortcutssheet.css +24 -0
  139. package/dist/shortcutssheet.d.ts +7 -0
  140. package/dist/shortcutssheet.layered.css +26 -0
  141. package/dist/styles.css +438 -16
  142. package/dist/styles.layered.css +438 -16
  143. package/dist/table.css +7 -2
  144. package/dist/table.layered.css +7 -2
  145. package/dist/taskeditpopover.css +36 -0
  146. package/dist/taskeditpopover.d.ts +7 -0
  147. package/dist/taskeditpopover.layered.css +38 -0
  148. package/dist/tokens.css +26 -0
  149. package/dist/tokens.json +56 -4
  150. package/dist/types/components/breadcrumb/breadcrumb.d.ts +9 -1
  151. package/dist/types/components/button/button.d.ts +2 -0
  152. package/dist/types/components/button/button.recipe.d.ts +12 -0
  153. package/dist/types/components/chip/chip.recipe.d.ts +1 -1
  154. package/dist/types/components/datatable/datatable.d.ts +8 -0
  155. package/dist/types/components/datepickerfield/datepickerfield.d.ts +4 -1
  156. package/dist/types/components/datepickerfield/datepickerfield.recipe.d.ts +18 -3
  157. package/dist/types/components/menu/menu.d.ts +50 -7
  158. package/dist/types/components/multiselect/multiselect.d.ts +9 -0
  159. package/dist/types/components/select/select.d.ts +10 -0
  160. package/dist/types/core/floating-portal.d.ts +6 -5
  161. package/dist/types/core/index.d.ts +1 -1
  162. package/dist/types/core/place-floating.d.ts +37 -0
  163. package/dist/types/index.d.ts +8 -0
  164. package/dist/types/product/assigneepicker/assigneepicker.d.ts +36 -0
  165. package/dist/types/product/assigneepicker/assigneepicker.recipe.d.ts +3 -0
  166. package/dist/types/product/assigneepicker/index.d.ts +2 -0
  167. package/dist/types/product/inbox/inbox.d.ts +13 -1
  168. package/dist/types/product/notesgallery/index.d.ts +2 -0
  169. package/dist/types/product/notesgallery/notesgallery.d.ts +54 -0
  170. package/dist/types/product/notesgallery/notesgallery.recipe.d.ts +3 -0
  171. package/dist/types/product/objectpagemenu/index.d.ts +1 -0
  172. package/dist/types/product/objectpagemenu/objectpagemenu.d.ts +40 -0
  173. package/dist/types/product/objectpagemenu/objectpagemenu.recipe.d.ts +17 -0
  174. package/dist/types/product/objectsettings/objectsettings.d.ts +71 -15
  175. package/dist/types/product/recordpage/index.d.ts +1 -1
  176. package/dist/types/product/recordpage/recordpage.d.ts +142 -1
  177. package/dist/types/product/recordpicker/index.d.ts +2 -0
  178. package/dist/types/product/recordpicker/recordpicker.d.ts +38 -0
  179. package/dist/types/product/recordpicker/recordpicker.recipe.d.ts +3 -0
  180. package/dist/types/product/savedviews/index.d.ts +1 -1
  181. package/dist/types/product/savedviews/savedviews.d.ts +36 -0
  182. package/dist/types/product/share/index.d.ts +2 -0
  183. package/dist/types/product/share/share.d.ts +85 -0
  184. package/dist/types/product/share/share.recipe.d.ts +3 -0
  185. package/dist/types/product/shortcutssheet/index.d.ts +1 -0
  186. package/dist/types/product/shortcutssheet/shortcutssheet.d.ts +49 -0
  187. package/dist/types/product/shortcutssheet/shortcutssheet.recipe.d.ts +3 -0
  188. package/dist/types/product/taskeditpopover/index.d.ts +2 -0
  189. package/dist/types/product/taskeditpopover/taskeditpopover.d.ts +67 -0
  190. package/dist/types/product/taskeditpopover/taskeditpopover.recipe.d.ts +3 -0
  191. package/dist/types/product/workspaceswitchermenu/index.d.ts +1 -0
  192. package/dist/types/product/workspaceswitchermenu/workspaceswitchermenu.d.ts +54 -0
  193. package/dist/types/product/workspaceswitchermenu/workspaceswitchermenu.recipe.d.ts +19 -0
  194. package/dist/types/tokens/contract.d.ts +13 -0
  195. package/dist/types/tokens/palette.d.ts +52 -0
  196. package/dist/types/tokens/semantic.d.ts +20 -0
  197. package/dist/workspaceswitchermenu.css +4 -0
  198. package/dist/workspaceswitchermenu.d.ts +7 -0
  199. package/dist/workspaceswitchermenu.layered.css +6 -0
  200. package/package.json +41 -1
@@ -0,0 +1,100 @@
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.RecordPicker = RecordPicker;
37
+ const jsx_runtime_1 = require("react/jsx-runtime");
38
+ const lucide_react_1 = require("lucide-react");
39
+ const React = __importStar(require("react"));
40
+ const select_1 = require("../../components/select");
41
+ const spinner_1 = require("../../components/spinner");
42
+ const core_1 = require("../../core");
43
+ const recordpicker_recipe_1 = require("./recordpicker.recipe");
44
+ /**
45
+ * The record-search picker behind a task's "Add record" chip -- measured
46
+ * live on app.attio.com's Tasks page ("Edit task" -> "Add record"): a
47
+ * "Search..." row, then a FLAT list mixed across every object type (a
48
+ * person result sat first, ahead of several companies, with no section
49
+ * headers -- unlike the mention picker's own grouped Users/Records shape,
50
+ * crisp#646). Attio's own live search took a moment to resolve, showing a
51
+ * centred spinner + "Loading..." row meanwhile; not independently
52
+ * re-measured against a second empty-results case, so `emptyLabel`'s
53
+ * default copy ("No records") is inferred by symmetry with
54
+ * `AssigneePicker`'s own measured "No users", not itself pixel-measured.
55
+ */
56
+ function RecordPicker({ records, loading = false, onSelect, query: queryProp, onQueryChange, searchLabel: searchLabelProp, loadingLabel: loadingLabelProp, emptyLabel: emptyLabelProp, className, ...rest }) {
57
+ const searchLabel = (0, core_1.useCrispMessage)(searchLabelProp, "Search...");
58
+ const loadingLabel = (0, core_1.useCrispMessage)(loadingLabelProp, "Loading...");
59
+ // crisp/#471: `emptyLabel` is typed `React.ReactNode`, so it resolves by
60
+ // hand rather than through `useCrispMessage`, which is string-only --
61
+ // same reasoning as `Inbox`'s own `emptyTitle`/`emptySubtitle`.
62
+ const crispMessages = (0, core_1.useCrispMessages)();
63
+ const emptyLabel = emptyLabelProp ?? crispMessages["No records"] ?? "No records";
64
+ const [internalQuery, setInternalQuery] = React.useState("");
65
+ const query = queryProp ?? internalQuery;
66
+ const setQuery = (q) => {
67
+ if (queryProp === undefined)
68
+ setInternalQuery(q);
69
+ onQueryChange?.(q);
70
+ };
71
+ const [highlight, setHighlight] = React.useState(0);
72
+ const autoId = React.useId();
73
+ const listboxId = `${autoId}-listbox`;
74
+ const optionId = (i) => `${autoId}-opt-${i}`;
75
+ const select = (r) => {
76
+ onSelect?.(r);
77
+ };
78
+ const { onKeyDown } = (0, select_1.useListboxKeyboard)({
79
+ enabledIndexes: records.map((_, i) => i),
80
+ highlight,
81
+ setHighlight,
82
+ commit: () => {
83
+ const r = records[highlight];
84
+ if (r)
85
+ select(r);
86
+ },
87
+ close: () => { },
88
+ dismiss: () => { },
89
+ });
90
+ const activeId = !loading && highlight >= 0 && highlight < records.length
91
+ ? optionId(highlight)
92
+ : undefined;
93
+ return ((0, jsx_runtime_1.jsxs)("div", { className: (0, recordpicker_recipe_1.recordpickerRecipe)({ className }), ...rest, children: [(0, jsx_runtime_1.jsxs)("div", { className: "crisp-recordpicker-search", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Search, { size: 16, strokeWidth: 1.75 }), (0, jsx_runtime_1.jsx)("input", { type: "text", className: "crisp-recordpicker-search-input", placeholder: searchLabel, value: query, onChange: (e) => {
94
+ setQuery(e.target.value);
95
+ setHighlight(0);
96
+ }, onKeyDown: onKeyDown, role: "combobox", "aria-expanded": "true", "aria-controls": listboxId, "aria-activedescendant": activeId,
97
+ // biome-ignore lint/a11y/noAutofocus: focus the search field the moment its enclosing popover opens
98
+ autoFocus: true })] }), loading ? ((0, jsx_runtime_1.jsxs)("div", { className: "crisp-recordpicker-loading", children: [(0, jsx_runtime_1.jsx)(spinner_1.Spinner, { size: 16 }), loadingLabel] })) : records.length === 0 ? ((0, jsx_runtime_1.jsx)("div", { className: "crisp-recordpicker-empty", children: emptyLabel })) : ((0, jsx_runtime_1.jsx)("div", { className: "crisp-recordpicker-list", role: "listbox", id: listboxId, children: records.map((r, i) => ((0, jsx_runtime_1.jsxs)("button", { type: "button", id: optionId(i), role: "option", tabIndex: -1, "aria-selected": i === highlight, "data-highlighted": i === highlight ? "" : undefined, className: "crisp-recordpicker-option", onMouseEnter: () => setHighlight(i), onClick: () => select(r), children: [r.icon, r.name] }, r.id))) }))] }));
99
+ }
100
+ RecordPicker.displayName = "RecordPicker";
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.recordpickerRecipe = void 0;
4
+ const core_1 = require("../../core");
5
+ const tokens_1 = require("../../tokens");
6
+ // The record picker behind a task's "Add record" chip -- measured live on
7
+ // app.attio.com's Tasks page ("Edit task" -> "Add record" -> its own
8
+ // sub-popover, same measured 350px width as `AssigneePicker`'s): a
9
+ // borderless "Search..." row, then a flat list mixed across every object
10
+ // type (not grouped into sections -- that's the *mention* picker's own
11
+ // shape, crisp#646, a different, still-ungrouped-by-default surface here),
12
+ // each row a consumer-supplied type icon + the record's name. A "Loading..."
13
+ // row appears while results are in flight (the same measured centred
14
+ // Spinner + text DataTable's own #826 loading state already uses).
15
+ const config = (0, core_1.defineRecipe)({
16
+ name: "recordpicker",
17
+ base: `display: flex; flex-direction: column; box-sizing: border-box; width: 350px; font-family: ${tokens_1.contract.font.sans};
18
+
19
+ & .crisp-recordpicker-search { display: flex; align-items: center; gap: ${tokens_1.contract.space["2"]}; height: 36px; padding: 0 ${tokens_1.contract.space["3"]}; color: ${tokens_1.contract.fgMuted}; }
20
+ & .crisp-recordpicker-search svg { flex-shrink: 0; }
21
+ & .crisp-recordpicker-search-input { flex: 1; min-width: 0; border: none; outline: none; background: none; font-family: inherit; font-size: ${tokens_1.contract.text.lg}; font-weight: ${tokens_1.contract.weight.medium}; line-height: 20px; color: ${tokens_1.contract.fgPrimary}; }
22
+ & .crisp-recordpicker-search-input::placeholder { color: ${tokens_1.contract.fgMuted}; }
23
+
24
+ & .crisp-recordpicker-loading { display: flex; align-items: center; justify-content: center; gap: 10px; padding: ${tokens_1.contract.space["8"]} ${tokens_1.contract.space["3"]}; color: color-mix(in srgb, ${tokens_1.contract.fgPrimary} 63%, transparent); font-size: ${tokens_1.contract.text.lg}; font-weight: ${tokens_1.contract.weight.medium}; line-height: 20px; }
25
+ & .crisp-recordpicker-empty { padding: ${tokens_1.contract.space["4"]} ${tokens_1.contract.space["3"]}; text-align: center; font-size: ${tokens_1.contract.text.lg}; font-weight: ${tokens_1.contract.weight.medium}; color: color-mix(in srgb, ${tokens_1.contract.fgPrimary} 63%, transparent); }
26
+
27
+ & .crisp-recordpicker-list { display: flex; flex-direction: column; margin: 0; padding: ${tokens_1.contract.space["1"]}; list-style: none; max-height: 280px; overflow-y: auto; }
28
+ & .crisp-recordpicker-option { display: flex; align-items: center; gap: ${tokens_1.contract.space["2"]}; width: 100%; box-sizing: border-box; height: 32px; padding: 0 ${tokens_1.contract.space["2"]}; border: none; border-radius: ${tokens_1.contract.radius.md}; background: none; font-family: inherit; font-size: ${tokens_1.contract.text.lg}; color: ${tokens_1.contract.fgPrimary}; text-align: start; cursor: pointer; }
29
+ & .crisp-recordpicker-option svg { flex-shrink: 0; color: ${tokens_1.contract.fgMuted}; }
30
+ & .crisp-recordpicker-option:hover, & .crisp-recordpicker-option[data-highlighted] { background: ${tokens_1.contract.bgRaised}; }`,
31
+ variants: {},
32
+ defaultVariants: {},
33
+ compoundVariants: [],
34
+ });
35
+ exports.recordpickerRecipe = (0, core_1.resolveRecipe)(config);
36
+ exports.default = config;
@@ -1,7 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.savedviewsRecipe = exports.SavedViews = void 0;
3
+ exports.savedviewsRecipe = exports.SavedViewsSaveBar = exports.SavedViews = void 0;
4
4
  var savedviews_1 = require("./savedviews");
5
5
  Object.defineProperty(exports, "SavedViews", { enumerable: true, get: function () { return savedviews_1.SavedViews; } });
6
+ Object.defineProperty(exports, "SavedViewsSaveBar", { enumerable: true, get: function () { return savedviews_1.SavedViewsSaveBar; } });
6
7
  var savedviews_recipe_1 = require("./savedviews.recipe");
7
8
  Object.defineProperty(exports, "savedviewsRecipe", { enumerable: true, get: function () { return savedviews_recipe_1.savedviewsRecipe; } });
@@ -34,6 +34,7 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.SavedViews = SavedViews;
37
+ exports.SavedViewsSaveBar = SavedViewsSaveBar;
37
38
  const jsx_runtime_1 = require("react/jsx-runtime");
38
39
  const lucide_react_1 = require("lucide-react");
39
40
  const React = __importStar(require("react"));
@@ -236,3 +237,24 @@ function SavedViews({ label, icon, views, activeId, onSelect, onCreate, onRename
236
237
  }, children: [(0, jsx_runtime_1.jsx)("span", { className: "crisp-savedviews-row-icon", "aria-hidden": "true", children: (0, jsx_runtime_1.jsx)(lucide_react_1.Plus, { size: 16, strokeWidth: 1.75 }) }), (0, jsx_runtime_1.jsx)("span", { className: "crisp-savedviews-row-label", children: createLabel })] })] }) }))] }));
237
238
  }
238
239
  SavedViews.displayName = "SavedViews";
240
+ /**
241
+ * The "unsaved changes" bar for a currently loaded saved view — Attio's own
242
+ * "Discard changes" + a split "Save for everyone ▾" button, measured live
243
+ * (app.attio.com, a saved Companies view with its sort changed). Not a part
244
+ * of `SavedViews`' own trigger/panel: Attio renders this in the page
245
+ * toolbar's own trailing slot, replacing whatever sits there at rest
246
+ * (Import/Export, "+ New …") — the same slot `TableToolbar`'s own
247
+ * `children` prop already documents ("Right-aligned actions after the
248
+ * count"). A consumer swaps this in for that slot's usual content once its
249
+ * own dirty check (live filters/sort/columns vs. the loaded view's
250
+ * snapshot) is true, and swaps it back out once it's false — this
251
+ * component itself carries no dirty state or visibility toggle.
252
+ */
253
+ function SavedViewsSaveBar({ onSave, onDiscard, onSaveAsNew, discardLabel: discardLabelProp, saveLabel: saveLabelProp, saveAsNewLabel: saveAsNewLabelProp, saveOptionsLabel: saveOptionsLabelProp, className, ...rest }) {
254
+ const discardLabel = (0, core_1.useCrispMessage)(discardLabelProp, "Discard changes");
255
+ const saveLabel = (0, core_1.useCrispMessage)(saveLabelProp, "Save for everyone");
256
+ const saveAsNewLabel = (0, core_1.useCrispMessage)(saveAsNewLabelProp, "Save as new view");
257
+ const saveOptionsLabel = (0, core_1.useCrispMessage)(saveOptionsLabelProp, "More save options");
258
+ return ((0, jsx_runtime_1.jsxs)("div", { className: (0, core_1.cx)("crisp-savedviews-savebar", className), ...rest, children: [(0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-savedviews-savebar-discard", onClick: onDiscard, children: discardLabel }), (0, jsx_runtime_1.jsxs)("div", { className: "crisp-savedviews-savebar-save-group", children: [(0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-savedviews-savebar-save", onClick: onSave, children: saveLabel }), onSaveAsNew && ((0, jsx_runtime_1.jsx)(menu_1.Menu, { align: "end", trigger: (0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-savedviews-savebar-save-chevron", "aria-label": saveOptionsLabel, children: (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronDown, { size: 16, strokeWidth: 1.75 }) }), children: (0, jsx_runtime_1.jsx)(menu_1.MenuItem, { onSelect: onSaveAsNew, children: saveAsNewLabel }) }))] })] }));
259
+ }
260
+ SavedViewsSaveBar.displayName = "SavedViewsSaveBar";
@@ -123,6 +123,53 @@ const config = (0, core_1.defineRecipe)({
123
123
  font-size: ${tokens_1.contract.text.sm}; font-weight: ${tokens_1.contract.weight.medium};
124
124
  color: color-mix(in srgb, ${tokens_1.contract.fgPrimary} 50%, transparent);
125
125
  }
126
+
127
+ /* crisp#568: the "Discard changes" / "Save for everyone ▾" bar a
128
+ consumer swaps into its own toolbar's trailing slot (TableToolbar's
129
+ own children prop already documents "right-aligned actions after the
130
+ count") while the loaded view is dirty -- measured live on
131
+ app.attio.com replacing that exact slot's usual Import/Export + New
132
+ button. Not rendered inside SavedViews' own trigger/panel, which
133
+ stays untouched; this is its own export, SavedViewsSaveBar. */
134
+ .crisp-savedviews-savebar { display: flex; align-items: center; gap: ${tokens_1.contract.space["2"]}; }
135
+ /* Rest-state color is a 55% color-mix of fgPrimary -- measured
136
+ identical to recordpanel's own ".crisp-recordpanel-section-action"
137
+ (its own comment: "measured 10px/400, a lighter mix..." -- font size
138
+ there was the SAME outer-vs-inner-div miss #688 corrected; the 55%
139
+ mix itself checks out independently here). Hover repeats crisp's own
140
+ already-measured Button ghost intent exactly (bgRaised + fgPrimary),
141
+ confirmed live (hover bg rgba(0,0,0,.04) + ink text matches bgRaised
142
+ + fgPrimary bit for bit) -- reused, not re-derived. */
143
+ .crisp-savedviews-savebar-discard {
144
+ box-sizing: border-box; display: inline-flex; align-items: center; height: 28px;
145
+ margin: 0; padding-inline: ${tokens_1.contract.space["2"]}; border: none; border-radius: ${tokens_1.contract.radius.md};
146
+ background: none; font-family: inherit; font-size: ${tokens_1.contract.text.lg}; font-weight: ${tokens_1.contract.weight.medium};
147
+ color: color-mix(in srgb, ${tokens_1.contract.fgPrimary} 55%, transparent); cursor: pointer;
148
+ transition: background-color .12s ease, color .12s ease;
149
+ }
150
+ .crisp-savedviews-savebar-discard:hover { background: ${tokens_1.contract.bgRaised}; color: ${tokens_1.contract.fgPrimary}; }
151
+ /* the split "Save for everyone ▾" pill -- two adjacent buttons sharing
152
+ one visual pill (measured: label half rounds only its outer/left
153
+ corners, the chevron half only its outer/right corners, seamless
154
+ between). Fill is crisp's OWN measured bgBrandContrast, not Attio's
155
+ own literal button blue -- the owner's directive is Attio structure/
156
+ sizing, seed-identity color (crisp direction doc). Hover/active reuse
157
+ Button's own already-measured primary-intent brightness filter
158
+ (crisp#910) rather than Attio's own literal darker fill, for the
159
+ same reason. */
160
+ .crisp-savedviews-savebar-save-group { display: flex; }
161
+ .crisp-savedviews-savebar-save, .crisp-savedviews-savebar-save-chevron {
162
+ box-sizing: border-box; display: inline-flex; align-items: center; justify-content: center;
163
+ height: 28px; margin: 0; border: none; background: ${tokens_1.contract.bgBrandContrast}; color: ${tokens_1.contract.fgOnBrand};
164
+ cursor: pointer; transition: filter .2s;
165
+ }
166
+ .crisp-savedviews-savebar-save:hover, .crisp-savedviews-savebar-save-chevron:hover { filter: brightness(0.96); }
167
+ .crisp-savedviews-savebar-save:active, .crisp-savedviews-savebar-save-chevron:active { filter: brightness(0.92); }
168
+ .crisp-savedviews-savebar-save {
169
+ padding-inline: ${tokens_1.contract.space["2"]} ${tokens_1.contract.space["1.5"]}; border-radius: ${tokens_1.contract.radius.md} 0 0 ${tokens_1.contract.radius.md};
170
+ font-family: inherit; font-size: ${tokens_1.contract.text.lg}; font-weight: ${tokens_1.contract.weight.medium};
171
+ }
172
+ .crisp-savedviews-savebar-save-chevron { width: 28px; padding: 0; border-radius: 0 ${tokens_1.contract.radius.md} ${tokens_1.contract.radius.md} 0; }
126
173
  `,
127
174
  variants: {},
128
175
  defaultVariants: {},
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.shareRecipe = exports.SharePopover = void 0;
4
+ var share_1 = require("./share");
5
+ Object.defineProperty(exports, "SharePopover", { enumerable: true, get: function () { return share_1.SharePopover; } });
6
+ var share_recipe_1 = require("./share.recipe");
7
+ Object.defineProperty(exports, "shareRecipe", { enumerable: true, get: function () { return share_recipe_1.shareRecipe; } });
@@ -0,0 +1,179 @@
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.SharePopover = SharePopover;
37
+ const jsx_runtime_1 = require("react/jsx-runtime");
38
+ const lucide_react_1 = require("lucide-react");
39
+ const React = __importStar(require("react"));
40
+ const avatar_1 = require("../../components/avatar");
41
+ const input_1 = require("../../components/input");
42
+ const select_1 = require("../../components/select");
43
+ const tabs_1 = require("../../components/tabs");
44
+ const core_1 = require("../../core");
45
+ const share_recipe_1 = require("./share.recipe");
46
+ /**
47
+ * A Share/permissions popover — measured live on app.attio.com (both an
48
+ * object table's and a List's own "Share" trigger, techpartners workspace):
49
+ * Members/Automations pill tabs, a "Workspace access" row (avatar + label +
50
+ * a `Read and write ⌄`-style role select), an optional plan-gated upsell
51
+ * slot, a "Clear permissions" action, an "Individual members" list (one row
52
+ * per member, its own role select), and — on the Automations tab — a
53
+ * search box + role select + "Add" row. Both tabs share a trailing "Learn
54
+ * more about permissions" link.
55
+ *
56
+ * Its own floating shell (not the generic `Popover`): the measured surface
57
+ * is 440px wide, past `Popover`'s 360px content cap. See share.recipe.ts.
58
+ */
59
+ function SharePopover({ trigger, align = "end", open: openProp, defaultOpen, onOpenChange, tab: tabProp, defaultTab = "members", onTabChange, membersTabLabel: membersTabLabelProp, automationsTabLabel: automationsTabLabelProp, workspaceIcon, workspaceAccessLabel: workspaceAccessLabelProp, workspaceAccessValue, workspaceAccessOptions, onWorkspaceAccessChange, advancedSharing, onClearPermissions, clearPermissionsLabel: clearPermissionsLabelProp, members, membersSectionLabel: membersSectionLabelProp, memberRoleOptions, onMemberAccessChange, automationSearchValue, defaultAutomationSearchValue, onAutomationSearchChange, automationSearchPlaceholder: automationSearchPlaceholderProp, automationRoleValue, automationRoleOptions, onAutomationRoleChange, onAddAutomation, addAutomationDisabled, addAutomationLabel: addAutomationLabelProp, learnMoreHref, learnMoreLabel: learnMoreLabelProp, "aria-label": ariaLabel, className, ...rest }) {
60
+ const membersTabLabel = (0, core_1.useCrispMessage)(membersTabLabelProp, "Members");
61
+ const automationsTabLabel = (0, core_1.useCrispMessage)(automationsTabLabelProp, "Automations");
62
+ const workspaceAccessLabel = (0, core_1.useCrispMessage)(workspaceAccessLabelProp, "Workspace access");
63
+ const clearPermissionsLabel = (0, core_1.useCrispMessage)(clearPermissionsLabelProp, "Clear permissions");
64
+ const membersSectionLabel = (0, core_1.useCrispMessage)(membersSectionLabelProp, "Individual members");
65
+ const automationSearchPlaceholder = (0, core_1.useCrispMessage)(automationSearchPlaceholderProp, "Search...");
66
+ const addAutomationLabel = (0, core_1.useCrispMessage)(addAutomationLabelProp, "Add");
67
+ const learnMoreLabel = (0, core_1.useCrispMessage)(learnMoreLabelProp, "Learn more about permissions");
68
+ const surfaceId = React.useId();
69
+ const triggerId = trigger.props.id ?? `${surfaceId}-trigger`;
70
+ const [internalOpen, setInternalOpen] = React.useState(defaultOpen ?? false);
71
+ const open = openProp ?? internalOpen;
72
+ const setOpen = React.useCallback((next) => {
73
+ if (openProp === undefined)
74
+ setInternalOpen(next);
75
+ onOpenChange?.(next);
76
+ }, [openProp, onOpenChange]);
77
+ const [internalTab, setInternalTab] = React.useState(defaultTab);
78
+ const activeTab = tabProp ?? internalTab;
79
+ const selectTab = (t) => {
80
+ if (tabProp === undefined)
81
+ setInternalTab(t);
82
+ onTabChange?.(t);
83
+ };
84
+ const [internalSearch, setInternalSearch] = React.useState(defaultAutomationSearchValue ?? "");
85
+ const searchValue = automationSearchValue ?? internalSearch;
86
+ const setSearch = (q) => {
87
+ if (automationSearchValue === undefined)
88
+ setInternalSearch(q);
89
+ onAutomationSearchChange?.(q);
90
+ };
91
+ const rootRef = React.useRef(null);
92
+ const triggerRef = React.useRef(null);
93
+ const contentRef = React.useRef(null);
94
+ const [pos, setPos] = React.useState({});
95
+ const [up, setUp] = React.useState(false);
96
+ const place = () => {
97
+ const el = triggerRef.current;
98
+ if (!el)
99
+ return;
100
+ const { style, up: flipUp } = (0, core_1.placeFloating)({
101
+ rect: el.getBoundingClientRect(),
102
+ align,
103
+ gap: 6,
104
+ flipBelow: 320,
105
+ anchor: el,
106
+ });
107
+ setUp(flipUp);
108
+ setPos(style);
109
+ };
110
+ // biome-ignore lint/correctness/useExhaustiveDependencies: reposition each time it opens
111
+ React.useLayoutEffect(() => {
112
+ if (open)
113
+ place();
114
+ }, [open]);
115
+ React.useEffect(() => {
116
+ if (!open || !contentRef.current)
117
+ return;
118
+ return (0, core_1.registerFloatingLayer)(contentRef.current);
119
+ }, [open]);
120
+ const close = React.useCallback(() => {
121
+ setOpen(false);
122
+ }, [setOpen]);
123
+ React.useEffect(() => {
124
+ if (!open)
125
+ return;
126
+ const onDown = (e) => {
127
+ const target = e.target;
128
+ if (rootRef.current?.contains(target))
129
+ return;
130
+ if (contentRef.current?.contains(target))
131
+ return;
132
+ if ((0, core_1.isInsideLaterFloatingLayer)(target, contentRef.current))
133
+ return;
134
+ close();
135
+ };
136
+ document.addEventListener("pointerdown", onDown);
137
+ return () => document.removeEventListener("pointerdown", onDown);
138
+ }, [open, close]);
139
+ React.useEffect(() => {
140
+ if (!open)
141
+ return;
142
+ const onKey = (e) => {
143
+ if (e.key !== "Escape")
144
+ return;
145
+ if ((0, core_1.hasNestedFloatingLayer)(contentRef.current))
146
+ return;
147
+ close();
148
+ };
149
+ document.addEventListener("keydown", onKey);
150
+ return () => document.removeEventListener("keydown", onKey);
151
+ }, [open, close]);
152
+ const triggerEl = React.cloneElement(trigger, {
153
+ ref: triggerRef,
154
+ id: triggerId,
155
+ "aria-haspopup": "dialog",
156
+ "aria-expanded": open,
157
+ "aria-controls": open ? surfaceId : undefined,
158
+ onClick: (e) => {
159
+ trigger.props.onClick?.(e);
160
+ setOpen(!open);
161
+ },
162
+ });
163
+ const tablistRef = React.useRef(null);
164
+ const tabEntries = React.useMemo(() => [{ value: "members" }, { value: "automations" }], []);
165
+ const { onKeyDown: onTabKeyDown, tabIndexFor } = (0, tabs_1.useTablistKeyboard)({
166
+ tabs: tabEntries,
167
+ active: activeTab,
168
+ select: (v) => selectTab(v),
169
+ listRef: tablistRef,
170
+ });
171
+ return ((0, jsx_runtime_1.jsxs)("div", { ref: rootRef, className: (0, share_recipe_1.shareRecipe)({ className }), children: [triggerEl, open && ((0, jsx_runtime_1.jsx)(core_1.FloatingPortal, { sourceRef: rootRef, children: (0, jsx_runtime_1.jsxs)("div", { ref: contentRef, className: "crisp-share-content", role: "dialog", id: surfaceId, "aria-label": ariaLabel, "aria-labelledby": ariaLabel ? undefined : triggerId, "data-up": up ? "" : undefined, style: pos, ...rest, children: [(0, jsx_runtime_1.jsxs)("div", { ref: tablistRef, className: "crisp-share-tabs", role: "tablist", onKeyDown: onTabKeyDown, children: [(0, jsx_runtime_1.jsxs)("button", { type: "button", role: "tab", "data-value": "members", "aria-selected": activeTab === "members", tabIndex: tabIndexFor("members"), className: "crisp-share-tab", onClick: () => selectTab("members"), children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Users, { size: 16, strokeWidth: 1.75, "aria-hidden": "true" }), membersTabLabel] }), (0, jsx_runtime_1.jsxs)("button", { type: "button", role: "tab", "data-value": "automations", "aria-selected": activeTab === "automations", tabIndex: tabIndexFor("automations"), className: "crisp-share-tab", onClick: () => selectTab("automations"), children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Play, { size: 16, strokeWidth: 1.75, "aria-hidden": "true" }), automationsTabLabel] })] }), activeTab === "members" ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)("div", { className: "crisp-share-row", children: [workspaceIcon ?? ((0, jsx_runtime_1.jsx)(avatar_1.Avatar, { size: "lg", tone: "brand", children: typeof workspaceAccessLabel === "string"
172
+ ? workspaceAccessLabel.charAt(0)
173
+ : null })), (0, jsx_runtime_1.jsx)("span", { className: "crisp-share-row-label", children: workspaceAccessLabel }), (0, jsx_runtime_1.jsx)(select_1.Select, { variant: "ghost", width: "auto", options: workspaceAccessOptions, value: workspaceAccessValue, onValueChange: onWorkspaceAccessChange, "aria-label": workspaceAccessLabel })] }), advancedSharing, onClearPermissions && ((0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-share-clear", onClick: onClearPermissions, children: clearPermissionsLabel })), members && members.length > 0 && ((0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("p", { className: "crisp-share-section-label", children: membersSectionLabel }), (0, jsx_runtime_1.jsx)("div", { className: "crisp-share-members", children: members.map((m) => ((0, jsx_runtime_1.jsxs)("div", { className: "crisp-share-member", children: [m.avatar ?? ((0, jsx_runtime_1.jsx)(avatar_1.Avatar, { size: "lg", tone: "brand", children: typeof m.name === "string"
174
+ ? m.name.charAt(0).toUpperCase()
175
+ : null })), (0, jsx_runtime_1.jsxs)("span", { className: "crisp-share-member-text", children: [(0, jsx_runtime_1.jsx)("span", { className: "crisp-share-member-name", children: m.name }), m.email && ((0, jsx_runtime_1.jsx)("span", { className: "crisp-share-member-email", children: m.email }))] }), (0, jsx_runtime_1.jsx)(select_1.Select, { variant: "ghost", width: "auto", options: memberRoleOptions, value: m.access, disabled: m.accessDisabled, onValueChange: (v) => onMemberAccessChange?.(m.id, v), "aria-label": typeof m.name === "string"
176
+ ? `${m.name}'s access`
177
+ : undefined })] }, m.id))) })] }))] })) : ((0, jsx_runtime_1.jsxs)("div", { className: "crisp-share-automations-row", children: [(0, jsx_runtime_1.jsx)(input_1.Input, { value: searchValue, onChange: (e) => setSearch(e.target.value), placeholder: automationSearchPlaceholder, "aria-label": automationSearchPlaceholder }), (0, jsx_runtime_1.jsx)(select_1.Select, { variant: "ghost", width: "auto", options: automationRoleOptions, value: automationRoleValue, onValueChange: onAutomationRoleChange, "aria-label": automationsTabLabel }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-share-add", disabled: addAutomationDisabled, onClick: onAddAutomation, children: addAutomationLabel })] })), (0, jsx_runtime_1.jsxs)("a", { className: "crisp-share-learnmore", href: learnMoreHref, target: "_blank", rel: "noreferrer", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.HelpCircle, { size: 16, strokeWidth: 1.75, "aria-hidden": "true" }), learnMoreLabel] })] }) }))] }));
178
+ }
179
+ SharePopover.displayName = "SharePopover";
@@ -0,0 +1,114 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.shareRecipe = void 0;
4
+ const core_1 = require("../../core");
5
+ const tokens_1 = require("../../tokens");
6
+ // SharePopover's shell — measured live on app.attio.com (both the object
7
+ // table's and a List's own "Share" trigger, techpartners workspace,
8
+ // 2026-09-27): a 440px-wide floating panel, radius 12 (xl), the same
9
+ // layered popover shadow every other floating surface uses (`e.floating`
10
+ // matched the measured box-shadow's four layers exactly, so no new shadow
11
+ // literal was needed).
12
+ //
13
+ // This is its own floating shell rather than a reuse of the generic
14
+ // `Popover` (components/popover): that recipe caps its content at
15
+ // `min(360px, 92vw)`, and 440px measured wider than that on every sample.
16
+ // `product/savedviews` already sets the precedent for a product surface
17
+ // owning its own portaled shell over the generic one when the measured
18
+ // geometry doesn't fit it.
19
+ //
20
+ // Three muted-text alphas were measured that don't match any existing
21
+ // color token: the workspace-access/role value text (`rgba(0,0,0,.55)`),
22
+ // the "Individual members" section label (`rgba(0,0,0,.5)`), and the
23
+ // inactive-tab/footer-link text (`rgba(0,0,0,.63)` — this one incidentally
24
+ // equals `fgAlertDescription`'s light value, but that token is Dialog's own
25
+ // alert-description colour, a different surface, so it is not reused here).
26
+ // Rather than add new palette entries for three one-off alphas (tokens/**
27
+ // is another lane's file), each is expressed as `color-mix(in srgb,
28
+ // fgPrimary N%, transparent)` — the same idiom `segmented`'s own track
29
+ // background already uses for its 5% tint — so the value is theme-reactive
30
+ // (it scales with fgPrimary's dark value too) without a new token or a
31
+ // flat literal that would go stale in dark mode.
32
+ const mutedText = (pct) => `color-mix(in srgb, ${tokens_1.contract.fgPrimary} ${pct}%, transparent)`;
33
+ const config = (0, core_1.defineRecipe)({
34
+ name: "share",
35
+ base: `display: contents; font-family: ${tokens_1.contract.font.sans};`,
36
+ globalCss: `
37
+ .crisp-share-content {
38
+ font-family: ${tokens_1.contract.font.sans}; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
39
+ position: fixed; z-index: 70; pointer-events: auto; box-sizing: border-box;
40
+ width: 440px; max-width: 92vw;
41
+ padding: ${tokens_1.contract.space["3"]}; display: flex; flex-direction: column; gap: ${tokens_1.contract.space["3"]};
42
+ border-radius: ${tokens_1.contract.radius.xl}; background: ${tokens_1.contract.bgPopover}; color: ${tokens_1.contract.fgPrimary};
43
+ box-shadow: ${tokens_1.elevation.floating};
44
+ opacity: 1; transform: none; transform-origin: top right;
45
+ transition: opacity 0.08s ease, transform 0.08s ease;
46
+ @starting-style { opacity: 0; transform: scale(0.97) translateY(-1px); }
47
+ }
48
+ .crisp-share-content[data-up] { transform-origin: bottom right; }
49
+
50
+ .crisp-share-tabs { display: flex; align-items: center; gap: ${tokens_1.contract.space["1"]}; }
51
+ .crisp-share-tab {
52
+ box-sizing: border-box; display: inline-flex; align-items: center; gap: ${tokens_1.contract.space["1.5"]};
53
+ height: ${tokens_1.contract.size.row}; padding: 0 ${tokens_1.contract.space["2"]}; border: none; border-radius: ${tokens_1.contract.radius.md};
54
+ background: transparent; color: ${mutedText(63)};
55
+ font-family: inherit; font-size: ${tokens_1.contract.text.lg}; font-weight: ${tokens_1.contract.weight.medium}; letter-spacing: -0.07px;
56
+ cursor: pointer; transition: background-color .12s ease, color .12s ease;
57
+ }
58
+ .crisp-share-tab svg { flex-shrink: 0; }
59
+ .crisp-share-tab:hover { color: ${tokens_1.contract.fgPrimary}; }
60
+ .crisp-share-tab[aria-selected="true"] { background: ${tokens_1.contract.bgSurfaceMuted}; color: ${tokens_1.contract.fgPrimary}; }
61
+
62
+ .crisp-share-row { display: flex; align-items: center; gap: ${tokens_1.contract.space["2"]}; min-height: 34px; }
63
+ .crisp-share-row-label { flex: 1; min-width: 0; font-size: ${tokens_1.contract.text.lg}; font-weight: ${tokens_1.contract.weight.medium}; color: ${tokens_1.contract.fgPrimary}; letter-spacing: -0.07px; }
64
+ .crisp-share-row .crisp-select { flex-shrink: 0; }
65
+ .crisp-share-row .crisp-select .crisp-select-value,
66
+ .crisp-share-row .crisp-select .crisp-select-chevron { color: ${mutedText(55)}; }
67
+
68
+ .crisp-share-clear {
69
+ align-self: flex-start; margin: 0; padding: 0; border: none; background: none;
70
+ font-family: inherit; font-size: ${tokens_1.contract.text.md}; font-weight: ${tokens_1.contract.weight.medium};
71
+ color: ${mutedText(63)}; cursor: pointer;
72
+ }
73
+ .crisp-share-clear:hover { color: ${tokens_1.contract.fgPrimary}; text-decoration: underline; }
74
+
75
+ .crisp-share-section-label {
76
+ margin: 0; font-size: ${tokens_1.contract.text.sm}; font-weight: ${tokens_1.contract.weight.medium}; color: ${mutedText(50)};
77
+ }
78
+ .crisp-share-members { display: flex; flex-direction: column; gap: ${tokens_1.contract.space["2"]}; }
79
+ .crisp-share-member { display: flex; align-items: center; gap: ${tokens_1.contract.space["2"]}; }
80
+ .crisp-share-member-text { flex: 1; min-width: 0; display: flex; align-items: baseline; gap: ${tokens_1.contract.space["1.5"]}; overflow: hidden; }
81
+ .crisp-share-member-name { flex-shrink: 0; font-size: ${tokens_1.contract.text.lg}; font-weight: ${tokens_1.contract.weight.medium}; color: ${tokens_1.contract.fgPrimary}; letter-spacing: -0.07px; }
82
+ .crisp-share-member-email { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: ${tokens_1.contract.text.lg}; font-weight: ${tokens_1.contract.weight.medium}; color: ${mutedText(55)}; letter-spacing: -0.07px; }
83
+ .crisp-share-member .crisp-select { flex-shrink: 0; }
84
+ .crisp-share-member .crisp-select .crisp-select-value,
85
+ .crisp-share-member .crisp-select .crisp-select-chevron { color: ${mutedText(55)}; }
86
+
87
+ .crisp-share-automations-row { display: flex; align-items: center; gap: ${tokens_1.contract.space["2"]}; }
88
+ .crisp-share-automations-row .crisp-input { flex: 1; min-width: 0; }
89
+ .crisp-share-automations-row .crisp-select { flex-shrink: 0; }
90
+
91
+ .crisp-share-add {
92
+ flex-shrink: 0; box-sizing: border-box; display: inline-flex; align-items: center; justify-content: center;
93
+ height: 32px; padding: 0 ${tokens_1.contract.space["2.5"]}; border: none; border-radius: ${tokens_1.contract.radius.lg};
94
+ background: ${tokens_1.contract.bgBrandSolid}; color: ${tokens_1.contract.fgOnBrand};
95
+ font-family: inherit; font-size: ${tokens_1.contract.text.lg}; font-weight: ${tokens_1.contract.weight.medium}; letter-spacing: -0.07px;
96
+ cursor: pointer; transition: background-color .12s ease, opacity .12s ease;
97
+ }
98
+ .crisp-share-add:hover:not(:disabled) { background: ${tokens_1.contract.bgBrandSolidPressed}; }
99
+ .crisp-share-add:disabled { opacity: 0.4; cursor: not-allowed; }
100
+
101
+ .crisp-share-learnmore {
102
+ display: inline-flex; align-items: center; gap: ${tokens_1.contract.space["1.5"]};
103
+ font-size: ${tokens_1.contract.text.lg}; font-weight: ${tokens_1.contract.weight.medium}; color: ${mutedText(63)};
104
+ text-decoration: none;
105
+ }
106
+ .crisp-share-learnmore:hover { color: ${tokens_1.contract.fgPrimary}; }
107
+ .crisp-share-learnmore svg { flex-shrink: 0; }
108
+ `,
109
+ variants: {},
110
+ defaultVariants: {},
111
+ compoundVariants: [],
112
+ });
113
+ exports.shareRecipe = (0, core_1.resolveRecipe)(config);
114
+ exports.default = config;
@@ -0,0 +1,17 @@
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 __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./shortcutssheet"), exports);