@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.
- package/dist/assigneepicker.css +13 -0
- package/dist/assigneepicker.d.ts +7 -0
- package/dist/assigneepicker.layered.css +15 -0
- package/dist/button.css +3 -0
- package/dist/button.d.ts +2 -2
- package/dist/button.layered.css +3 -0
- package/dist/chip.css +1 -1
- package/dist/chip.layered.css +1 -1
- package/dist/cjs/components/breadcrumb/breadcrumb.js +1 -1
- package/dist/cjs/components/button/button.recipe.js +33 -0
- package/dist/cjs/components/chip/chip.recipe.js +3 -1
- package/dist/cjs/components/datatable/datatable.js +6 -3
- package/dist/cjs/components/datatable/datatable.recipe.js +45 -6
- package/dist/cjs/components/datepickerfield/datepickerfield.js +2 -2
- package/dist/cjs/components/datepickerfield/datepickerfield.recipe.js +16 -3
- package/dist/cjs/components/menu/menu.js +65 -9
- package/dist/cjs/components/multiselect/multiselect.js +3 -0
- package/dist/cjs/components/multiselect/multiselect.recipe.js +18 -0
- package/dist/cjs/components/select/select.js +2 -2
- package/dist/cjs/components/select/select.recipe.js +19 -0
- package/dist/cjs/components/table/table.recipe.js +7 -1
- package/dist/cjs/core/floating-portal.js +7 -4
- package/dist/cjs/core/index.js +2 -1
- package/dist/cjs/core/place-floating.js +38 -0
- package/dist/cjs/index.js +8 -0
- package/dist/cjs/product/assigneepicker/assigneepicker.js +127 -0
- package/dist/cjs/product/assigneepicker/assigneepicker.recipe.js +36 -0
- package/dist/cjs/product/assigneepicker/index.js +7 -0
- package/dist/cjs/product/inbox/inbox.js +11 -3
- package/dist/cjs/product/notesgallery/index.js +7 -0
- package/dist/cjs/product/notesgallery/notesgallery.js +80 -0
- package/dist/cjs/product/notesgallery/notesgallery.recipe.js +46 -0
- package/dist/cjs/product/objectpagemenu/index.js +17 -0
- package/dist/cjs/product/objectpagemenu/objectpagemenu.js +26 -0
- package/dist/cjs/product/objectpagemenu/objectpagemenu.recipe.js +30 -0
- package/dist/cjs/product/objectsettings/objectsettings.js +107 -14
- package/dist/cjs/product/recordpage/index.js +2 -1
- package/dist/cjs/product/recordpage/recordpage.js +112 -7
- package/dist/cjs/product/recordpage/recordpage.recipe.js +63 -0
- package/dist/cjs/product/recordpicker/index.js +7 -0
- package/dist/cjs/product/recordpicker/recordpicker.js +100 -0
- package/dist/cjs/product/recordpicker/recordpicker.recipe.js +36 -0
- package/dist/cjs/product/savedviews/index.js +2 -1
- package/dist/cjs/product/savedviews/savedviews.js +22 -0
- package/dist/cjs/product/savedviews/savedviews.recipe.js +47 -0
- package/dist/cjs/product/share/index.js +7 -0
- package/dist/cjs/product/share/share.js +179 -0
- package/dist/cjs/product/share/share.recipe.js +114 -0
- package/dist/cjs/product/shortcutssheet/index.js +17 -0
- package/dist/cjs/product/shortcutssheet/shortcutssheet.js +125 -0
- package/dist/cjs/product/shortcutssheet/shortcutssheet.recipe.js +50 -0
- package/dist/cjs/product/taskeditpopover/index.js +7 -0
- package/dist/cjs/product/taskeditpopover/taskeditpopover.js +159 -0
- package/dist/cjs/product/taskeditpopover/taskeditpopover.recipe.js +68 -0
- package/dist/cjs/product/workspaceswitchermenu/index.js +17 -0
- package/dist/cjs/product/workspaceswitchermenu/workspaceswitchermenu.js +38 -0
- package/dist/cjs/product/workspaceswitchermenu/workspaceswitchermenu.recipe.js +34 -0
- package/dist/cjs/tokens/palette.js +27 -0
- package/dist/cjs/tokens/semantic.js +20 -0
- package/dist/datatable.css +50 -11
- package/dist/datatable.layered.css +50 -11
- package/dist/datepickerfield.css +8 -2
- package/dist/datepickerfield.d.ts +2 -2
- package/dist/datepickerfield.layered.css +8 -2
- package/dist/esm/components/breadcrumb/breadcrumb.js +1 -1
- package/dist/esm/components/button/button.recipe.js +33 -0
- package/dist/esm/components/chip/chip.recipe.js +3 -1
- package/dist/esm/components/datatable/datatable.js +6 -3
- package/dist/esm/components/datatable/datatable.recipe.js +45 -6
- package/dist/esm/components/datepickerfield/datepickerfield.js +2 -2
- package/dist/esm/components/datepickerfield/datepickerfield.recipe.js +16 -3
- package/dist/esm/components/menu/menu.js +64 -8
- package/dist/esm/components/multiselect/multiselect.js +3 -0
- package/dist/esm/components/multiselect/multiselect.recipe.js +18 -0
- package/dist/esm/components/select/select.js +2 -2
- package/dist/esm/components/select/select.recipe.js +19 -0
- package/dist/esm/components/table/table.recipe.js +7 -1
- package/dist/esm/core/floating-portal.js +7 -4
- package/dist/esm/core/index.js +1 -1
- package/dist/esm/core/place-floating.js +37 -0
- package/dist/esm/index.js +8 -0
- package/dist/esm/product/assigneepicker/assigneepicker.js +91 -0
- package/dist/esm/product/assigneepicker/assigneepicker.recipe.js +33 -0
- package/dist/esm/product/assigneepicker/index.js +2 -0
- package/dist/esm/product/inbox/inbox.js +11 -3
- package/dist/esm/product/notesgallery/index.js +2 -0
- package/dist/esm/product/notesgallery/notesgallery.js +44 -0
- package/dist/esm/product/notesgallery/notesgallery.recipe.js +43 -0
- package/dist/esm/product/objectpagemenu/index.js +1 -0
- package/dist/esm/product/objectpagemenu/objectpagemenu.js +23 -0
- package/dist/esm/product/objectpagemenu/objectpagemenu.recipe.js +27 -0
- package/dist/esm/product/objectsettings/objectsettings.js +106 -13
- package/dist/esm/product/recordpage/index.js +1 -1
- package/dist/esm/product/recordpage/recordpage.js +112 -8
- package/dist/esm/product/recordpage/recordpage.recipe.js +63 -0
- package/dist/esm/product/recordpicker/index.js +2 -0
- package/dist/esm/product/recordpicker/recordpicker.js +64 -0
- package/dist/esm/product/recordpicker/recordpicker.recipe.js +33 -0
- package/dist/esm/product/savedviews/index.js +1 -1
- package/dist/esm/product/savedviews/savedviews.js +22 -1
- package/dist/esm/product/savedviews/savedviews.recipe.js +47 -0
- package/dist/esm/product/share/index.js +2 -0
- package/dist/esm/product/share/share.js +143 -0
- package/dist/esm/product/share/share.recipe.js +111 -0
- package/dist/esm/product/shortcutssheet/index.js +1 -0
- package/dist/esm/product/shortcutssheet/shortcutssheet.js +88 -0
- package/dist/esm/product/shortcutssheet/shortcutssheet.recipe.js +47 -0
- package/dist/esm/product/taskeditpopover/index.js +2 -0
- package/dist/esm/product/taskeditpopover/taskeditpopover.js +123 -0
- package/dist/esm/product/taskeditpopover/taskeditpopover.recipe.js +65 -0
- package/dist/esm/product/workspaceswitchermenu/index.js +1 -0
- package/dist/esm/product/workspaceswitchermenu/workspaceswitchermenu.js +35 -0
- package/dist/esm/product/workspaceswitchermenu/workspaceswitchermenu.recipe.js +31 -0
- package/dist/esm/tokens/palette.js +27 -0
- package/dist/esm/tokens/semantic.js +20 -0
- package/dist/multiselect.css +18 -0
- package/dist/multiselect.layered.css +18 -0
- package/dist/native/tokens.d.ts +1 -1
- package/dist/native/tokens.js +56 -4
- package/dist/notesgallery.css +15 -0
- package/dist/notesgallery.d.ts +7 -0
- package/dist/notesgallery.layered.css +17 -0
- package/dist/objectpagemenu.css +3 -0
- package/dist/objectpagemenu.d.ts +7 -0
- package/dist/objectpagemenu.layered.css +5 -0
- package/dist/recordpage.css +59 -0
- package/dist/recordpage.layered.css +59 -0
- package/dist/recordpicker.css +13 -0
- package/dist/recordpicker.d.ts +7 -0
- package/dist/recordpicker.layered.css +15 -0
- package/dist/savedviews.css +47 -0
- package/dist/savedviews.layered.css +47 -0
- package/dist/select.css +19 -0
- package/dist/select.layered.css +19 -0
- package/dist/share.css +84 -0
- package/dist/share.d.ts +7 -0
- package/dist/share.layered.css +86 -0
- package/dist/shortcutssheet.css +24 -0
- package/dist/shortcutssheet.d.ts +7 -0
- package/dist/shortcutssheet.layered.css +26 -0
- package/dist/styles.css +438 -16
- package/dist/styles.layered.css +438 -16
- package/dist/table.css +7 -2
- package/dist/table.layered.css +7 -2
- package/dist/taskeditpopover.css +36 -0
- package/dist/taskeditpopover.d.ts +7 -0
- package/dist/taskeditpopover.layered.css +38 -0
- package/dist/tokens.css +26 -0
- package/dist/tokens.json +56 -4
- package/dist/types/components/breadcrumb/breadcrumb.d.ts +9 -1
- package/dist/types/components/button/button.d.ts +2 -0
- package/dist/types/components/button/button.recipe.d.ts +12 -0
- package/dist/types/components/chip/chip.recipe.d.ts +1 -1
- package/dist/types/components/datatable/datatable.d.ts +8 -0
- package/dist/types/components/datepickerfield/datepickerfield.d.ts +4 -1
- package/dist/types/components/datepickerfield/datepickerfield.recipe.d.ts +18 -3
- package/dist/types/components/menu/menu.d.ts +50 -7
- package/dist/types/components/multiselect/multiselect.d.ts +9 -0
- package/dist/types/components/select/select.d.ts +10 -0
- package/dist/types/core/floating-portal.d.ts +6 -5
- package/dist/types/core/index.d.ts +1 -1
- package/dist/types/core/place-floating.d.ts +37 -0
- package/dist/types/index.d.ts +8 -0
- package/dist/types/product/assigneepicker/assigneepicker.d.ts +36 -0
- package/dist/types/product/assigneepicker/assigneepicker.recipe.d.ts +3 -0
- package/dist/types/product/assigneepicker/index.d.ts +2 -0
- package/dist/types/product/inbox/inbox.d.ts +13 -1
- package/dist/types/product/notesgallery/index.d.ts +2 -0
- package/dist/types/product/notesgallery/notesgallery.d.ts +54 -0
- package/dist/types/product/notesgallery/notesgallery.recipe.d.ts +3 -0
- package/dist/types/product/objectpagemenu/index.d.ts +1 -0
- package/dist/types/product/objectpagemenu/objectpagemenu.d.ts +40 -0
- package/dist/types/product/objectpagemenu/objectpagemenu.recipe.d.ts +17 -0
- package/dist/types/product/objectsettings/objectsettings.d.ts +71 -15
- package/dist/types/product/recordpage/index.d.ts +1 -1
- package/dist/types/product/recordpage/recordpage.d.ts +142 -1
- package/dist/types/product/recordpicker/index.d.ts +2 -0
- package/dist/types/product/recordpicker/recordpicker.d.ts +38 -0
- package/dist/types/product/recordpicker/recordpicker.recipe.d.ts +3 -0
- package/dist/types/product/savedviews/index.d.ts +1 -1
- package/dist/types/product/savedviews/savedviews.d.ts +36 -0
- package/dist/types/product/share/index.d.ts +2 -0
- package/dist/types/product/share/share.d.ts +85 -0
- package/dist/types/product/share/share.recipe.d.ts +3 -0
- package/dist/types/product/shortcutssheet/index.d.ts +1 -0
- package/dist/types/product/shortcutssheet/shortcutssheet.d.ts +49 -0
- package/dist/types/product/shortcutssheet/shortcutssheet.recipe.d.ts +3 -0
- package/dist/types/product/taskeditpopover/index.d.ts +2 -0
- package/dist/types/product/taskeditpopover/taskeditpopover.d.ts +67 -0
- package/dist/types/product/taskeditpopover/taskeditpopover.recipe.d.ts +3 -0
- package/dist/types/product/workspaceswitchermenu/index.d.ts +1 -0
- package/dist/types/product/workspaceswitchermenu/workspaceswitchermenu.d.ts +54 -0
- package/dist/types/product/workspaceswitchermenu/workspaceswitchermenu.recipe.d.ts +19 -0
- package/dist/types/tokens/contract.d.ts +13 -0
- package/dist/types/tokens/palette.d.ts +52 -0
- package/dist/types/tokens/semantic.d.ts +20 -0
- package/dist/workspaceswitchermenu.css +4 -0
- package/dist/workspaceswitchermenu.d.ts +7 -0
- package/dist/workspaceswitchermenu.layered.css +6 -0
- package/package.json +41 -1
|
@@ -33,7 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.ATTRIBUTE_AI_AUTOFILL_OPTIONS = exports.ATTRIBUTE_TYPE_OPTIONS = void 0;
|
|
36
|
+
exports.CURRENCY_GROUPING_OPTIONS = exports.CURRENCY_DECIMAL_PLACES_OPTIONS = exports.CURRENCY_DISPLAY_OPTIONS = exports.ATTRIBUTE_AI_AUTOFILL_OPTIONS = exports.ATTRIBUTE_TYPE_OPTIONS = void 0;
|
|
37
37
|
exports.ObjectsList = ObjectsList;
|
|
38
38
|
exports.ObjectDetailPage = ObjectDetailPage;
|
|
39
39
|
exports.ObjectGeneralSection = ObjectGeneralSection;
|
|
@@ -204,7 +204,7 @@ ObjectAppearanceSection.displayName = "ObjectAppearanceSection";
|
|
|
204
204
|
* measures 40px here — a real, small divergence left to DataTable's owner
|
|
205
205
|
* rather than patched locally (see the PR).
|
|
206
206
|
*/
|
|
207
|
-
function AttributesTable({ attributes, onOpenAttribute, onEditAttribute, onArchiveAttribute, onReorder, onCreateAttribute, title: titleProp, subtitle: subtitleProp, createLabel: createLabelProp, searchPlaceholder: searchPlaceholderProp, filterLabel: filterLabelProp, nameColumnLabel: nameColumnLabelProp, dataTypeColumnLabel: dataTypeColumnLabelProp, constraintsColumnLabel: constraintsColumnLabelProp, propertiesColumnLabel: propertiesColumnLabelProp, systemLabel: systemLabelProp, enrichedLabel: enrichedLabelProp, editLabel: editLabelProp, archiveLabel: archiveLabelProp, rowActions, emptyLabel, }) {
|
|
207
|
+
function AttributesTable({ attributes, onOpenAttribute, onEditAttribute, onDuplicateAttribute, onCopyId, onCopySlug, onChangeSlug, onArchiveAttribute, onReorder, onCreateAttribute, title: titleProp, subtitle: subtitleProp, createLabel: createLabelProp, searchPlaceholder: searchPlaceholderProp, filterLabel: filterLabelProp, nameColumnLabel: nameColumnLabelProp, dataTypeColumnLabel: dataTypeColumnLabelProp, constraintsColumnLabel: constraintsColumnLabelProp, propertiesColumnLabel: propertiesColumnLabelProp, systemLabel: systemLabelProp, enrichedLabel: enrichedLabelProp, editLabel: editLabelProp, duplicateLabel: duplicateLabelProp, copyIdLabel: copyIdLabelProp, copySlugLabel: copySlugLabelProp, changeSlugLabel: changeSlugLabelProp, archiveLabel: archiveLabelProp, rowActions, emptyLabel, }) {
|
|
208
208
|
const title = (0, core_1.useCrispMessage)(titleProp, "Attributes");
|
|
209
209
|
const subtitle = (0, core_1.useCrispMessage)(subtitleProp, "Modify and add object attributes");
|
|
210
210
|
const createLabel = (0, core_1.useCrispMessage)(createLabelProp, "Create attribute");
|
|
@@ -216,8 +216,37 @@ function AttributesTable({ attributes, onOpenAttribute, onEditAttribute, onArchi
|
|
|
216
216
|
const propertiesColumnLabel = (0, core_1.useCrispMessage)(propertiesColumnLabelProp, "Properties");
|
|
217
217
|
const systemLabel = (0, core_1.useCrispMessage)(systemLabelProp, "System");
|
|
218
218
|
const enrichedLabel = (0, core_1.useCrispMessage)(enrichedLabelProp, "Enriched");
|
|
219
|
-
|
|
220
|
-
|
|
219
|
+
// crisp#673, G-107 (verbatim): "Edit attribute, Duplicate attribute, Copy
|
|
220
|
+
// ID, Copy slug, Change slug, Archive attribute" — corrects this
|
|
221
|
+
// component's former "Edit"/"Archive" defaults, which were a paraphrase
|
|
222
|
+
// from an earlier, less precise measurement.
|
|
223
|
+
const editLabel = (0, core_1.useCrispMessage)(editLabelProp, "Edit attribute");
|
|
224
|
+
const duplicateLabel = (0, core_1.useCrispMessage)(duplicateLabelProp, "Duplicate attribute");
|
|
225
|
+
const copyIdLabel = (0, core_1.useCrispMessage)(copyIdLabelProp, "Copy ID");
|
|
226
|
+
const copySlugLabel = (0, core_1.useCrispMessage)(copySlugLabelProp, "Copy slug");
|
|
227
|
+
const changeSlugLabel = (0, core_1.useCrispMessage)(changeSlugLabelProp, "Change slug");
|
|
228
|
+
const archiveLabel = (0, core_1.useCrispMessage)(archiveLabelProp, "Archive attribute");
|
|
229
|
+
// Default-with-override (same shape as `CommentsPanel`'s `onCopyLink`):
|
|
230
|
+
// a consumer that only wants to know a copy happened (a toast, analytics)
|
|
231
|
+
// gets it for free; one that needs the real copy still owns it.
|
|
232
|
+
// `useCallback`, not a plain closure: the `columns` memo below references
|
|
233
|
+
// these, not `onCopyId`/`onCopySlug` directly, so their own identity has
|
|
234
|
+
// to track those props for the memo to stay correct without re-listing
|
|
235
|
+
// props it never mentions by name.
|
|
236
|
+
const copyId = React.useCallback((id) => {
|
|
237
|
+
if (onCopyId) {
|
|
238
|
+
onCopyId(id);
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
navigator.clipboard?.writeText(id).catch(() => { });
|
|
242
|
+
}, [onCopyId]);
|
|
243
|
+
const copySlug = React.useCallback((slug) => {
|
|
244
|
+
if (onCopySlug) {
|
|
245
|
+
onCopySlug(slug);
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
navigator.clipboard?.writeText(slug).catch(() => { });
|
|
249
|
+
}, [onCopySlug]);
|
|
221
250
|
const [query, setQuery] = React.useState("");
|
|
222
251
|
const filtered = React.useMemo(() => {
|
|
223
252
|
if (!query.trim())
|
|
@@ -258,7 +287,7 @@ function AttributesTable({ attributes, onOpenAttribute, onEditAttribute, onArchi
|
|
|
258
287
|
cursor: "pointer",
|
|
259
288
|
color: "inherit",
|
|
260
289
|
padding: 4,
|
|
261
|
-
}, children: (0, jsx_runtime_1.jsx)(lucide_react_1.MoreHorizontal, { size: 16 }) }), children: rowActions ? (rowActions(row)) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(menu_1.MenuItem, { onSelect: () => onEditAttribute?.(row.id), children: editLabel }), (0, jsx_runtime_1.jsx)(menu_1.MenuItem, { danger: true, onSelect: () => onArchiveAttribute?.(row.id), children: archiveLabel })] })) })),
|
|
290
|
+
}, children: (0, jsx_runtime_1.jsx)(lucide_react_1.MoreHorizontal, { size: 16 }) }), children: rowActions ? (rowActions(row)) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(menu_1.MenuItem, { onSelect: () => onEditAttribute?.(row.id), children: editLabel }), (0, jsx_runtime_1.jsx)(menu_1.MenuItem, { onSelect: () => onDuplicateAttribute?.(row.id), children: duplicateLabel }), (0, jsx_runtime_1.jsx)(menu_1.MenuItem, { onSelect: () => copyId(row.id), children: copyIdLabel }), (0, jsx_runtime_1.jsx)(menu_1.MenuItem, { disabled: !row.slug, onSelect: () => row.slug && copySlug(row.slug), children: copySlugLabel }), (0, jsx_runtime_1.jsx)(menu_1.MenuItem, { onSelect: () => onChangeSlug?.(row.id), children: changeSlugLabel }), (0, jsx_runtime_1.jsx)(menu_1.MenuItem, { danger: true, onSelect: () => onArchiveAttribute?.(row.id), children: archiveLabel })] })) })),
|
|
262
291
|
},
|
|
263
292
|
], [
|
|
264
293
|
nameColumnLabel,
|
|
@@ -268,8 +297,16 @@ function AttributesTable({ attributes, onOpenAttribute, onEditAttribute, onArchi
|
|
|
268
297
|
systemLabel,
|
|
269
298
|
enrichedLabel,
|
|
270
299
|
editLabel,
|
|
300
|
+
duplicateLabel,
|
|
301
|
+
copyIdLabel,
|
|
302
|
+
copySlugLabel,
|
|
303
|
+
changeSlugLabel,
|
|
271
304
|
archiveLabel,
|
|
272
305
|
onEditAttribute,
|
|
306
|
+
onDuplicateAttribute,
|
|
307
|
+
copyId,
|
|
308
|
+
copySlug,
|
|
309
|
+
onChangeSlug,
|
|
273
310
|
onArchiveAttribute,
|
|
274
311
|
rowActions,
|
|
275
312
|
]);
|
|
@@ -330,26 +367,82 @@ function AttributeTypeSelect({ value, onValueChange, options = exports.ATTRIBUTE
|
|
|
330
367
|
return ((0, jsx_runtime_1.jsx)(select_1.Select, { id: id, options: selectOptions, value: value, onValueChange: onValueChange, searchable: true, searchPlaceholder: searchPlaceholder, width: "full" }));
|
|
331
368
|
}
|
|
332
369
|
AttributeTypeSelect.displayName = "AttributeTypeSelect";
|
|
370
|
+
/* ── Currency's own extra required fields (crisp#673, G-104) ──────────
|
|
371
|
+
* Attio's Create-attribute dialog isn't a fixed layout: choosing "Currency"
|
|
372
|
+
* inserts four required selects between Description and the AI-autofill
|
|
373
|
+
* row (Text/Number/Checkbox/Date/Phone Number/Location/User add none).
|
|
374
|
+
* Display, Decimal Places and Grouping are Attio's own small fixed UI
|
|
375
|
+
* vocabulary (verified live, `app.attio.com` Companies object) and ship as
|
|
376
|
+
* defaults below; Currency is real reference data (ISO 4217, ~150+
|
|
377
|
+
* entries) that crisp doesn't own, so it has no default of its own — a
|
|
378
|
+
* consumer supplies its own list, the same "GRC need: later" boundary
|
|
379
|
+
* `aiAutofillTypeOptions` already draws. Each option's own live-formatted
|
|
380
|
+
* preview (e.g. "Code — USD 1,200.50") is deliberately not modelled: it
|
|
381
|
+
* needs real number-formatting logic, a separate, bigger piece of work. */
|
|
382
|
+
exports.CURRENCY_DISPLAY_OPTIONS = [
|
|
383
|
+
{ value: "code", label: "Code" },
|
|
384
|
+
{ value: "name", label: "Name" },
|
|
385
|
+
{ value: "narrow-symbol", label: "Narrow symbol" },
|
|
386
|
+
{ value: "symbol", label: "Symbol" },
|
|
387
|
+
];
|
|
388
|
+
exports.CURRENCY_DECIMAL_PLACES_OPTIONS = [
|
|
389
|
+
{ value: "2", label: "2 decimals" },
|
|
390
|
+
{ value: "0", label: "No decimals" },
|
|
391
|
+
];
|
|
392
|
+
exports.CURRENCY_GROUPING_OPTIONS = [
|
|
393
|
+
{ value: "default", label: "Default" },
|
|
394
|
+
{ value: "none", label: "No groups" },
|
|
395
|
+
];
|
|
333
396
|
/**
|
|
334
|
-
* "Create attribute" (crisp#637, P-125, verified live): Attribute Type
|
|
335
|
-
* select (defaults to Text
|
|
336
|
-
* +
|
|
337
|
-
*
|
|
338
|
-
*
|
|
339
|
-
*
|
|
340
|
-
*
|
|
397
|
+
* "Create attribute" (crisp#637/#673, P-125, verified live): Attribute Type
|
|
398
|
+
* select (defaults to Text, type-ahead search built in via `Select`'s own
|
|
399
|
+
* `searchable`) + Name (autofocused on open, and again after picking a type
|
|
400
|
+
* — G-106) + Description (optional) + Currency's own four extra required
|
|
401
|
+
* fields when `type === "currency"` (G-104) + an AI-autofill switch row,
|
|
402
|
+
* footer Cancel/Create. Built on `FormDialog` in its plain (non-`createMode`)
|
|
403
|
+
* shape — Attio's own footer here has a real Cancel button, unlike the
|
|
404
|
+
* record-create dialog `createMode` models (crisp#548), so `createMode`
|
|
405
|
+
* would be the wrong fit even though the button carries the same ⌘↵ hint;
|
|
406
|
+
* that hint is rendered locally instead. The outside-click/Escape
|
|
407
|
+
* "Discard changes" confirm gated on dirty state (G-105) needed no work
|
|
408
|
+
* here at all — `FormDialog` already tracks dirtiness generically and shows
|
|
409
|
+
* it, so this dialog inherited it for free; `CreateAttributeDialog`'s own
|
|
410
|
+
* tests below just pin the behaviour to this component specifically.
|
|
341
411
|
*/
|
|
342
|
-
function CreateAttributeDialog({ type, onTypeChange, typeOptions, aiAutofillTypeOptions, name, onNameChange, description, onDescriptionChange, aiAutofillEnabled = false, onAiAutofillChange, onSubmit, onClose, busy, error, title: titleProp, attributeTypeLabel: attributeTypeLabelProp, nameLabel: nameLabelProp, descriptionLabel: descriptionLabelProp, descriptionPlaceholder: descriptionPlaceholderProp, aiAutofillLabel: aiAutofillLabelProp, setUpAiAutofillLabel: setUpAiAutofillLabelProp, createLabel: createLabelProp, }) {
|
|
412
|
+
function CreateAttributeDialog({ type, onTypeChange, typeOptions, aiAutofillTypeOptions, name, onNameChange, description, onDescriptionChange, currency, onCurrencyChange, currencyOptions, display, onDisplayChange, displayOptions = exports.CURRENCY_DISPLAY_OPTIONS, decimalPlaces, onDecimalPlacesChange, decimalPlacesOptions = exports.CURRENCY_DECIMAL_PLACES_OPTIONS, grouping, onGroupingChange, groupingOptions = exports.CURRENCY_GROUPING_OPTIONS, currencyFieldLabel: currencyFieldLabelProp, displayFieldLabel: displayFieldLabelProp, decimalPlacesFieldLabel: decimalPlacesFieldLabelProp, groupingFieldLabel: groupingFieldLabelProp, aiAutofillEnabled = false, onAiAutofillChange, onSubmit, onClose, busy, error, title: titleProp, attributeTypeLabel: attributeTypeLabelProp, nameLabel: nameLabelProp, descriptionLabel: descriptionLabelProp, descriptionPlaceholder: descriptionPlaceholderProp, aiAutofillLabel: aiAutofillLabelProp, setUpAiAutofillLabel: setUpAiAutofillLabelProp, createLabel: createLabelProp, }) {
|
|
343
413
|
const title = (0, core_1.useCrispMessage)(titleProp, "Create attribute");
|
|
344
414
|
const attributeTypeLabel = (0, core_1.useCrispMessage)(attributeTypeLabelProp, "Attribute Type");
|
|
345
415
|
const nameLabel = (0, core_1.useCrispMessage)(nameLabelProp, "Name");
|
|
346
416
|
const descriptionLabel = (0, core_1.useCrispMessage)(descriptionLabelProp, "Description");
|
|
347
417
|
const descriptionPlaceholder = (0, core_1.useCrispMessage)(descriptionPlaceholderProp, "Add a description for this attribute");
|
|
418
|
+
// Attio's own copy for these four is the label plus a literal "(required)"
|
|
419
|
+
// suffix (not `FormField`'s asterisk convention) — the same "(optional)"-
|
|
420
|
+
// as-label-text shape `descriptionLabel` above already uses, so these are
|
|
421
|
+
// plain overridable strings rather than a new `FormField` capability.
|
|
422
|
+
const currencyFieldLabel = (0, core_1.useCrispMessage)(currencyFieldLabelProp, "Currency (required)");
|
|
423
|
+
const displayFieldLabel = (0, core_1.useCrispMessage)(displayFieldLabelProp, "Display (required)");
|
|
424
|
+
const decimalPlacesFieldLabel = (0, core_1.useCrispMessage)(decimalPlacesFieldLabelProp, "Decimal Places (required)");
|
|
425
|
+
const groupingFieldLabel = (0, core_1.useCrispMessage)(groupingFieldLabelProp, "Grouping (required)");
|
|
348
426
|
const aiAutofillLabel = (0, core_1.useCrispMessage)(aiAutofillLabelProp, "AI autofill");
|
|
349
427
|
const setUpAiAutofillLabel = (0, core_1.useCrispMessage)(setUpAiAutofillLabelProp, "Set up AI autofill");
|
|
350
428
|
const createLabel = (0, core_1.useCrispMessage)(createLabelProp, "Create attribute");
|
|
351
429
|
const switchId = React.useId();
|
|
352
430
|
const nameId = React.useId();
|
|
353
|
-
|
|
431
|
+
// G-106: selecting a type via the keyboard (filter to one match, Enter)
|
|
432
|
+
// auto-focuses Name right after — measured live. `Select`'s own
|
|
433
|
+
// `onValueChange` fires identically for a keyboard Enter and a mouse
|
|
434
|
+
// click (both go through its one `pick()`), so this can't distinguish
|
|
435
|
+
// input modality; only the keyboard path was independently re-measured,
|
|
436
|
+
// but there is no second code path to special-case even if there were
|
|
437
|
+
// one. The `requestAnimationFrame` wins the same race `Select`'s own
|
|
438
|
+
// `searchable` variant and `SavedViews`'s search box already schedule
|
|
439
|
+
// focus against (Select returns focus to its trigger synchronously on
|
|
440
|
+
// pick; this runs after that paint and wins).
|
|
441
|
+
const nameRef = React.useRef(null);
|
|
442
|
+
const handleTypeChange = (value) => {
|
|
443
|
+
onTypeChange(value);
|
|
444
|
+
requestAnimationFrame(() => nameRef.current?.focus({ preventScroll: true }));
|
|
445
|
+
};
|
|
446
|
+
return ((0, jsx_runtime_1.jsxs)(formdialog_1.FormDialog, { title: title, submitLabel: (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [createLabel, (0, jsx_runtime_1.jsxs)("span", { className: "crisp-objectsettings-kbd", "aria-hidden": "true", children: [(0, jsx_runtime_1.jsx)("kbd", { children: "\u2318" }), (0, jsx_runtime_1.jsx)("kbd", { children: "\u21B5" })] })] }), onSubmit: onSubmit, onClose: onClose, busy: busy, error: error, size: 520, className: (0, objectsettings_recipe_1.objectsettingsRecipe)(), children: [(0, jsx_runtime_1.jsx)(formfield_1.FormField, { label: attributeTypeLabel, children: (0, jsx_runtime_1.jsx)(AttributeTypeSelect, { value: type, onValueChange: handleTypeChange, options: typeOptions, aiAutofillOptions: aiAutofillTypeOptions }) }), (0, jsx_runtime_1.jsx)(formfield_1.FormField, { label: nameLabel, htmlFor: nameId, children: (0, jsx_runtime_1.jsx)(input_1.Input, { ref: nameRef, id: nameId, autoFocus: true, value: name, onChange: (e) => onNameChange(e.target.value) }) }), (0, jsx_runtime_1.jsx)(formfield_1.FormField, { label: descriptionLabel, optional: true, children: (0, jsx_runtime_1.jsx)(textarea_1.Textarea, { placeholder: descriptionPlaceholder, value: description, onChange: (e) => onDescriptionChange(e.target.value) }) }), type === "currency" && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)("div", { className: "crisp-objectsettings-fieldrow", children: [(0, jsx_runtime_1.jsx)(formfield_1.FormField, { label: currencyFieldLabel, children: (0, jsx_runtime_1.jsx)(select_1.Select, { options: currencyOptions ?? [], value: currency ?? "", onValueChange: onCurrencyChange, searchable: true }) }), (0, jsx_runtime_1.jsx)(formfield_1.FormField, { label: displayFieldLabel, children: (0, jsx_runtime_1.jsx)(select_1.Select, { options: displayOptions, value: display ?? displayOptions[0]?.value, onValueChange: onDisplayChange }) })] }), (0, jsx_runtime_1.jsxs)("div", { className: "crisp-objectsettings-fieldrow", children: [(0, jsx_runtime_1.jsx)(formfield_1.FormField, { label: decimalPlacesFieldLabel, children: (0, jsx_runtime_1.jsx)(select_1.Select, { options: decimalPlacesOptions, value: decimalPlaces ?? decimalPlacesOptions[0]?.value, onValueChange: onDecimalPlacesChange }) }), (0, jsx_runtime_1.jsx)(formfield_1.FormField, { label: groupingFieldLabel, children: (0, jsx_runtime_1.jsx)(select_1.Select, { options: groupingOptions, value: grouping ?? groupingOptions[0]?.value, onValueChange: onGroupingChange }) })] })] })), (0, jsx_runtime_1.jsxs)("div", { className: "crisp-objectsettings-aiautofill", children: [(0, jsx_runtime_1.jsx)("span", { className: "crisp-objectsettings-aiautofill-label", children: aiAutofillLabel }), (0, jsx_runtime_1.jsxs)("label", { className: "crisp-objectsettings-aiautofill-row", htmlFor: switchId, children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Sparkles, { size: 14 }), (0, jsx_runtime_1.jsx)("span", { children: setUpAiAutofillLabel }), (0, jsx_runtime_1.jsx)(switch_1.Switch, { id: switchId, checked: aiAutofillEnabled, onCheckedChange: onAiAutofillChange })] })] })] }));
|
|
354
447
|
}
|
|
355
448
|
CreateAttributeDialog.displayName = "CreateAttributeDialog";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.recordpageRecipe = exports.recordPageLabels = exports.RecordPage = exports.RecordDetails = exports.useEditableField = exports.editableInputType = exports.tagTone = void 0;
|
|
3
|
+
exports.recordpageRecipe = exports.recordPageLabels = exports.RecordPageMain = exports.RecordPage = exports.RecordDetails = exports.useEditableField = exports.editableInputType = exports.tagTone = void 0;
|
|
4
4
|
var column_1 = require("./column");
|
|
5
5
|
Object.defineProperty(exports, "tagTone", { enumerable: true, get: function () { return column_1.tagTone; } });
|
|
6
6
|
var editable_field_1 = require("./editable-field");
|
|
@@ -9,6 +9,7 @@ Object.defineProperty(exports, "useEditableField", { enumerable: true, get: func
|
|
|
9
9
|
var recordpage_1 = require("./recordpage");
|
|
10
10
|
Object.defineProperty(exports, "RecordDetails", { enumerable: true, get: function () { return recordpage_1.RecordDetails; } });
|
|
11
11
|
Object.defineProperty(exports, "RecordPage", { enumerable: true, get: function () { return recordpage_1.RecordPage; } });
|
|
12
|
+
Object.defineProperty(exports, "RecordPageMain", { enumerable: true, get: function () { return recordpage_1.RecordPageMain; } });
|
|
12
13
|
Object.defineProperty(exports, "recordPageLabels", { enumerable: true, get: function () { return recordpage_1.recordPageLabels; } });
|
|
13
14
|
var recordpage_recipe_1 = require("./recordpage.recipe");
|
|
14
15
|
Object.defineProperty(exports, "recordpageRecipe", { enumerable: true, get: function () { return recordpage_recipe_1.recordpageRecipe; } });
|
|
@@ -35,13 +35,16 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.recordPageLabels = void 0;
|
|
37
37
|
exports.RecordDetails = RecordDetails;
|
|
38
|
+
exports.RecordPageMain = RecordPageMain;
|
|
38
39
|
exports.RecordPage = RecordPage;
|
|
39
40
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
40
41
|
const lucide_react_1 = require("lucide-react");
|
|
41
42
|
const React = __importStar(require("react"));
|
|
42
43
|
const avatar_1 = require("../../components/avatar");
|
|
44
|
+
const button_1 = require("../../components/button");
|
|
43
45
|
const checkbox_1 = require("../../components/checkbox");
|
|
44
46
|
const drawer_1 = require("../../components/drawer");
|
|
47
|
+
const kbd_1 = require("../../components/kbd");
|
|
45
48
|
const menu_1 = require("../../components/menu");
|
|
46
49
|
const select_1 = require("../../components/select");
|
|
47
50
|
const spinner_1 = require("../../components/spinner");
|
|
@@ -100,6 +103,28 @@ exports.recordPageLabels = {
|
|
|
100
103
|
share: "Share",
|
|
101
104
|
comments: "Comments",
|
|
102
105
|
noLists: "This record has not been added to any lists.",
|
|
106
|
+
filters: "Filters",
|
|
107
|
+
addMeeting: "Add meeting",
|
|
108
|
+
noFiles: "No files",
|
|
109
|
+
filesHint: "Drag a file here or choose one from your computer.",
|
|
110
|
+
newFolder: "New folder",
|
|
111
|
+
uploadFile: "Upload file",
|
|
112
|
+
fileMoreActions: "More file actions",
|
|
113
|
+
noDealsFound: "No Deals found",
|
|
114
|
+
noDealsHint: "There are no related records for this relationship.",
|
|
115
|
+
newDeal: "New Deal",
|
|
116
|
+
noEmails: "No emails",
|
|
117
|
+
noEmailsHint: "This record doesn't have any emails, or they may be hidden due to permissions.",
|
|
118
|
+
configureMailbox: "Configure mailbox",
|
|
119
|
+
upgradeToViewCalls: "Upgrade to view calls",
|
|
120
|
+
callsUpgradeHint: "No call recordings yet! Upgrade to Pro to start recording calls.",
|
|
121
|
+
upgrade: "Upgrade",
|
|
122
|
+
selectWorkflowTitle: "Select workflow",
|
|
123
|
+
closeWorkflowPopover: "Close",
|
|
124
|
+
searchWorkflowsPlaceholder: "Search workflows...",
|
|
125
|
+
noWorkflows: "No workflows",
|
|
126
|
+
createNewWorkflow: "Create new workflow",
|
|
127
|
+
navigate: "Navigate",
|
|
103
128
|
highlights: "Highlights",
|
|
104
129
|
recordDetails: "Record Details",
|
|
105
130
|
searchFields: "Search fields",
|
|
@@ -497,7 +522,7 @@ function RecordDetails({ record, columns, sections, onEdit, labels, className, r
|
|
|
497
522
|
commit(record.id, col.key, e.currentTarget.value);
|
|
498
523
|
} }), isPending && ((0, jsx_runtime_1.jsx)(spinner_1.Spinner, { size: 14, className: "crisp-recordpage-edit-pending" })), fieldError && ((0, jsx_runtime_1.jsx)("span", { className: "crisp-recordpage-edit-error", children: fieldError }))] })] }, col.key));
|
|
499
524
|
}
|
|
500
|
-
return ((0, jsx_runtime_1.jsxs)("div", { className: "crisp-recordpage-field", children: [label, (0, jsx_runtime_1.jsxs)("span", { className: "crisp-recordpage-field-value", "data-editable": canEdit ? "" : undefined, children: [canEdit ? ((0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-recordpage-edit-trigger", onClick: () => openEditing(col.key), children: (0, jsx_runtime_1.jsx)(FieldValue, { col: col, row: record, labels: t }) })) : ((0, jsx_runtime_1.jsx)(FieldValue, { col: col, row: record, labels: t })), fieldError && ((0, jsx_runtime_1.jsx)("span", { className: "crisp-recordpage-edit-error", children: fieldError }))] })] }, col.key));
|
|
525
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: "crisp-recordpage-field", children: [label, (0, jsx_runtime_1.jsxs)("span", { className: "crisp-recordpage-field-value", "data-editable": canEdit ? "" : undefined, children: [canEdit && col.type === "url" ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(FieldValue, { col: col, row: record, labels: t }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-recordpage-edit-affordance", "aria-label": `Edit ${col.label}`, onClick: () => openEditing(col.key), children: (0, jsx_runtime_1.jsx)(lucide_react_1.Pencil, { size: 16, strokeWidth: 1.75 }) })] })) : canEdit ? ((0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-recordpage-edit-trigger", onClick: () => openEditing(col.key), children: (0, jsx_runtime_1.jsx)(FieldValue, { col: col, row: record, labels: t }) })) : ((0, jsx_runtime_1.jsx)(FieldValue, { col: col, row: record, labels: t })), fieldError && ((0, jsx_runtime_1.jsx)("span", { className: "crisp-recordpage-edit-error", children: fieldError }))] })] }, col.key));
|
|
501
526
|
})] }, g.title != null ? String(g.title) : `section-${i}`))), canRevealFields && ((0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-recordpage-viewall crisp-recordpage-viewall--section", onClick: () => {
|
|
502
527
|
// Revealing all fields also opens the (focused) field
|
|
503
528
|
// search; collapsing closes it.
|
|
@@ -515,6 +540,68 @@ function RecordDetails({ record, columns, sections, onEdit, labels, className, r
|
|
|
515
540
|
} }))] }));
|
|
516
541
|
}
|
|
517
542
|
RecordDetails.displayName = "RecordDetails";
|
|
543
|
+
/**
|
|
544
|
+
* The main/tab-content column's own box, on its own (#750) — the same
|
|
545
|
+
* "standalone piece" `RecordDetails` already is for the Details column,
|
|
546
|
+
* mirrored to the other side of the layout. `RecordPage` composes this
|
|
547
|
+
* internally for its own tabbed body (below) — this exact box, not a second
|
|
548
|
+
* copy — so a consumer building a custom record page (its own tab set, its
|
|
549
|
+
* own menu, `RecordDetails` for the left column, but not the full
|
|
550
|
+
* `RecordPage`, which hardcodes the CRM tab set) gets the same
|
|
551
|
+
* Attio-measured inset: `flex: 1; min-width: 0; display: flex;
|
|
552
|
+
* flex-direction: column; min-height: 0; overflow-y: auto; padding: 12px
|
|
553
|
+
* 20px` (`recordpage.recipe.ts`'s own `.crisp-recordpage-main`) — without
|
|
554
|
+
* reaching for that internal class name directly, the only option before
|
|
555
|
+
* this existed.
|
|
556
|
+
*
|
|
557
|
+
* Needs the same single `.crisp-recordpage`-scoped ancestor `RecordDetails`
|
|
558
|
+
* already documents — wrap both in `recordpageRecipe()`, the way
|
|
559
|
+
* `RecordPage`'s own root does.
|
|
560
|
+
*/
|
|
561
|
+
function RecordPageMain({ className, children, ...rest }) {
|
|
562
|
+
return ((0, jsx_runtime_1.jsx)("main", { className: (0, core_1.cx)("crisp-recordpage-main", className), ...rest, children: children }));
|
|
563
|
+
}
|
|
564
|
+
RecordPageMain.displayName = "RecordPageMain";
|
|
565
|
+
/**
|
|
566
|
+
* The "Run workflow" quick action's own popover (#620, P-018) — measured
|
|
567
|
+
* live on `app.attio.com`'s own record page: a centered, top-anchored
|
|
568
|
+
* overlay sharing the exact same frosted-bezel shell `product/command`'s
|
|
569
|
+
* own Cmd-K palette already uses (`elevation.modal`, `bgOverlayFrost` 60% +
|
|
570
|
+
* `blur(15px)`, radius `2xl`/`xl` outer/inner) — duplicated into this
|
|
571
|
+
* recipe rather than imported, the same "own copy per product surface"
|
|
572
|
+
* choice already made elsewhere in this codebase (`product/command` is
|
|
573
|
+
* outside this lane's own `product/recordpage` ownership).
|
|
574
|
+
*
|
|
575
|
+
* Chrome only, like the quick-action row's own Compose email/Add to list
|
|
576
|
+
* buttons above: crisp has no workflow data model to search or list yet, so
|
|
577
|
+
* the search box is real and typable but filters nothing, and the "No
|
|
578
|
+
* workflows" empty state always renders. The measured popover's own
|
|
579
|
+
* illustration is a bespoke Attio graphic (Golden Rule 4: Lucide only) —
|
|
580
|
+
* reuses crisp's own established "illustrated empty state" dotted-grid tile
|
|
581
|
+
* (`.crisp-recordpage-illustrated-icon`, already shipped for Files/Deals)
|
|
582
|
+
* with a `Workflow` glyph instead.
|
|
583
|
+
*/
|
|
584
|
+
function WorkflowPopover({ open, onClose, labels, }) {
|
|
585
|
+
React.useEffect(() => {
|
|
586
|
+
if (!open)
|
|
587
|
+
return;
|
|
588
|
+
const onKey = (e) => {
|
|
589
|
+
if (e.key === "Escape")
|
|
590
|
+
onClose();
|
|
591
|
+
};
|
|
592
|
+
document.addEventListener("keydown", onKey);
|
|
593
|
+
return () => document.removeEventListener("keydown", onKey);
|
|
594
|
+
}, [open, onClose]);
|
|
595
|
+
if (!open)
|
|
596
|
+
return null;
|
|
597
|
+
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("div", { className: "crisp-recordpage-workflow-backdrop" }), (0, jsx_runtime_1.jsx)("div", { className: "crisp-recordpage-workflow-positioner", onClick: (e) => {
|
|
598
|
+
if (e.target === e.currentTarget)
|
|
599
|
+
onClose();
|
|
600
|
+
}, children: (0, jsx_runtime_1.jsx)("div", { className: "crisp-recordpage-workflow-content", children: (0, jsx_runtime_1.jsxs)("div", { className: "crisp-recordpage-workflow-card", role: "dialog", "aria-modal": "true", "aria-label": labels.selectWorkflowTitle, children: [(0, jsx_runtime_1.jsxs)("div", { className: "crisp-recordpage-workflow-header", children: [(0, jsx_runtime_1.jsx)("span", { className: "crisp-recordpage-workflow-title", children: labels.selectWorkflowTitle }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-recordpage-workflow-close", "aria-label": labels.closeWorkflowPopover, onClick: onClose, children: (0, jsx_runtime_1.jsx)(lucide_react_1.X, { size: 14, strokeWidth: 1.75 }) })] }), (0, jsx_runtime_1.jsx)("div", { className: "crisp-recordpage-workflow-search", children: (0, jsx_runtime_1.jsx)("input", {
|
|
601
|
+
// biome-ignore lint/a11y/noAutofocus: focusing the just-opened popover's search is expected
|
|
602
|
+
autoFocus: true, type: "text", role: "combobox", "aria-expanded": "true", "aria-label": labels.selectWorkflowTitle, placeholder: labels.searchWorkflowsPlaceholder, className: "crisp-recordpage-workflow-input" }) }), (0, jsx_runtime_1.jsx)("div", { className: "crisp-recordpage-workflow-sep" }), (0, jsx_runtime_1.jsxs)("div", { className: "crisp-recordpage-workflow-empty", children: [(0, jsx_runtime_1.jsx)("span", { className: "crisp-recordpage-illustrated-icon", "aria-hidden": "true", children: (0, jsx_runtime_1.jsx)(lucide_react_1.Workflow, { size: 40, strokeWidth: 1.25 }) }), (0, jsx_runtime_1.jsx)("div", { className: "crisp-recordpage-illustrated-desc", children: labels.noWorkflows }), (0, jsx_runtime_1.jsxs)("button", { type: "button", className: "crisp-recordpage-workflow-create", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Plus, { size: 14, strokeWidth: 1.75 }), labels.createNewWorkflow] })] }), (0, jsx_runtime_1.jsx)("div", { className: "crisp-recordpage-workflow-footer", children: (0, jsx_runtime_1.jsxs)("span", { className: "crisp-recordpage-workflow-footer-nav", children: [(0, jsx_runtime_1.jsx)(kbd_1.Kbd, { children: "\u2191" }), (0, jsx_runtime_1.jsx)(kbd_1.Kbd, { children: "\u2193" }), labels.navigate] }) })] }) }) })] }));
|
|
603
|
+
}
|
|
604
|
+
WorkflowPopover.displayName = "WorkflowPopover";
|
|
518
605
|
/**
|
|
519
606
|
* The full-page company record view — the page that opens when you click into a
|
|
520
607
|
* record (as opposed to the peek [RecordPanel]). A top breadcrumb bar with
|
|
@@ -523,9 +610,15 @@ RecordDetails.displayName = "RecordDetails";
|
|
|
523
610
|
* Emails sections (Notes & Tasks are live — add and check things off). Drop it
|
|
524
611
|
* into the app's main content area.
|
|
525
612
|
*/
|
|
526
|
-
function RecordPage({ record, columns, collection = "Records", labels, onBack, onToggleFull, full, onClose, closeLabel = "Close", onPrevious, previousRecordLabel = "Previous record", onNext, nextRecordLabel = "Next record", counterLabel, className, onEdit, tabs: tabsProp, defaultTab, notes: notesProp, onAddNote, tasks: tasksProp, onAddTask, onToggleTask, sections, highlights: highlightsProp, quickActions: quickActionsProp, comments: commentsProp, onPostComment, currentUser, onShare, ...rest }) {
|
|
613
|
+
function RecordPage({ record, columns, collection = "Records", labels, onBack, onToggleFull, full, onClose, closeLabel = "Close", onPrevious, previousRecordLabel = "Previous record", onNext, nextRecordLabel = "Next record", counterLabel, className, onEdit, tabs: tabsProp, defaultTab, notes: notesProp, onAddNote, tasks: tasksProp, onAddTask, onToggleTask, sections, highlights: highlightsProp, quickActions: quickActionsProp, comments: commentsProp, onPostComment, currentUser, onShare, avatarShape = "rounded", onEditRecord, onDelete, actions, showBreadcrumb = true, showShare = true, showAskAttio = true, showComments = true, ...rest }) {
|
|
527
614
|
const t = useRecordPageLabels(labels);
|
|
528
615
|
const [favorite, setFavorite] = React.useState(false);
|
|
616
|
+
// #620: the "Run workflow" quick action's own popover -- only the
|
|
617
|
+
// BUILT-IN default quick-action list opens it (below); a caller supplying
|
|
618
|
+
// its own `quickActions` owns every button's behaviour itself, the same
|
|
619
|
+
// "caller's own list, not a merge" contract `tabs`/`highlights` already
|
|
620
|
+
// make.
|
|
621
|
+
const [workflowPopoverOpen, setWorkflowPopoverOpen] = React.useState(false);
|
|
529
622
|
// Notes/Tasks are controlled when the caller supplies `notes`/`tasks` (even
|
|
530
623
|
// `[]`) -- otherwise RecordPage keeps its own self-contained state, as
|
|
531
624
|
// before (#379). "Controlled" here means the array prop alone decides the
|
|
@@ -642,7 +735,13 @@ function RecordPage({ record, columns, collection = "Records", labels, onBack, o
|
|
|
642
735
|
// choice `tabs` (#379) and `highlights` (#528) already make.
|
|
643
736
|
const defaultQuickActions = [
|
|
644
737
|
{ label: t.logANote, icon: lucide_react_1.FileText },
|
|
645
|
-
|
|
738
|
+
// #620: only the built-in default wires this to crisp's own popover --
|
|
739
|
+
// see `workflowPopoverOpen`'s own comment above.
|
|
740
|
+
{
|
|
741
|
+
label: t.runWorkflow,
|
|
742
|
+
icon: lucide_react_1.Workflow,
|
|
743
|
+
onSelect: () => setWorkflowPopoverOpen(true),
|
|
744
|
+
},
|
|
646
745
|
{ label: t.createATask, icon: lucide_react_1.SquareCheck },
|
|
647
746
|
];
|
|
648
747
|
const quickActions = quickActionsProp ?? defaultQuickActions;
|
|
@@ -755,13 +854,19 @@ function RecordPage({ record, columns, collection = "Records", labels, onBack, o
|
|
|
755
854
|
case "overview":
|
|
756
855
|
return ((0, jsx_runtime_1.jsxs)("div", { className: "crisp-recordpage-panel", children: [highlights !== false && ((0, jsx_runtime_1.jsxs)("section", { children: [(0, jsx_runtime_1.jsx)("div", { className: "crisp-recordpage-section-title", children: t.highlights }), (0, jsx_runtime_1.jsx)("div", { className: "crisp-recordpage-hl-card", children: (0, jsx_runtime_1.jsx)("div", { className: "crisp-recordpage-hl-grid", children: highlights.map((h) => ((0, jsx_runtime_1.jsxs)("div", { className: "crisp-recordpage-hl-cell", children: [h.icon && ((0, jsx_runtime_1.jsx)(h.icon, { className: "crisp-recordpage-hl-icon", size: 15, strokeWidth: 1.75 })), (0, jsx_runtime_1.jsx)("div", { className: "crisp-recordpage-hl-label", children: h.label }), (0, jsx_runtime_1.jsx)("div", { className: "crisp-recordpage-hl-value", children: h.value ?? ((0, jsx_runtime_1.jsx)("span", { className: "crisp-recordpage-hl-value--empty", children: h.empty })) })] }, h.label))) }) })] })), activityFeed, (0, jsx_runtime_1.jsxs)("section", { children: [(0, jsx_runtime_1.jsx)(SectionHead, { title: t.tabs.emails, count: 0 }), emailsEmpty] }), (0, jsx_runtime_1.jsx)(NotesSection, { notes: notes, onAdd: addNote, labels: t }), (0, jsx_runtime_1.jsx)(TasksSection, { tasks: tasks, onAdd: addTask, onToggle: toggleTask, labels: t })] }));
|
|
757
856
|
case "activity":
|
|
758
|
-
return (0, jsx_runtime_1.jsx)("div", { className: "crisp-recordpage-panel", children:
|
|
857
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: "crisp-recordpage-panel", children: (0, jsx_runtime_1.jsxs)("section", { children: [(0, jsx_runtime_1.jsx)(SectionHead, { title: t.tabs.activity, action: (0, jsx_runtime_1.jsxs)("span", { className: "crisp-recordpage-tabtoolbar", children: [(0, jsx_runtime_1.jsxs)("button", { type: "button", className: "crisp-recordpage-action", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.ListFilter, { size: 14, strokeWidth: 1.75 }), t.filters] }), (0, jsx_runtime_1.jsxs)("button", { type: "button", className: "crisp-recordpage-action", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Plus, { size: 14, strokeWidth: 1.75 }), t.addMeeting] })] }) }), (0, jsx_runtime_1.jsx)("div", { className: "crisp-recordpage-feed", children: activity.map((a) => ((0, jsx_runtime_1.jsxs)("div", { className: "crisp-recordpage-feed-item", children: [(0, jsx_runtime_1.jsx)("span", { className: "crisp-recordpage-feed-dot", children: (0, jsx_runtime_1.jsx)(lucide_react_1.Activity, { size: 13, strokeWidth: 2 }) }), (0, jsx_runtime_1.jsxs)("div", { className: "crisp-recordpage-feed-body", children: [a.text, (0, jsx_runtime_1.jsx)("div", { className: "crisp-recordpage-feed-time", children: a.time })] })] }, a.id))) })] }) }));
|
|
858
|
+
case "files":
|
|
859
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: "crisp-recordpage-panel crisp-recordpage-panel--center", children: (0, jsx_runtime_1.jsxs)("div", { className: "crisp-recordpage-illustrated", children: [(0, jsx_runtime_1.jsx)("span", { className: "crisp-recordpage-illustrated-icon", "aria-hidden": "true", children: (0, jsx_runtime_1.jsx)(lucide_react_1.Folder, { size: 40, strokeWidth: 1.25 }) }), (0, jsx_runtime_1.jsx)("div", { className: "crisp-recordpage-illustrated-title", children: t.noFiles }), (0, jsx_runtime_1.jsx)("div", { className: "crisp-recordpage-illustrated-desc", children: t.filesHint }), (0, jsx_runtime_1.jsxs)("div", { className: "crisp-recordpage-illustrated-actions", children: [(0, jsx_runtime_1.jsxs)("button", { type: "button", className: "crisp-recordpage-action", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Plus, { size: 14, strokeWidth: 1.75 }), t.newFolder] }), (0, jsx_runtime_1.jsxs)("button", { type: "button", className: "crisp-recordpage-action", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Plus, { size: 14, strokeWidth: 1.75 }), t.uploadFile] }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-recordpage-action crisp-recordpage-action--icon", "aria-label": t.fileMoreActions, children: (0, jsx_runtime_1.jsx)(lucide_react_1.MoreHorizontal, { size: 16, strokeWidth: 1.75 }) })] })] }) }));
|
|
860
|
+
case "deals":
|
|
861
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: "crisp-recordpage-panel crisp-recordpage-panel--center", children: (0, jsx_runtime_1.jsxs)("div", { className: "crisp-recordpage-illustrated", children: [(0, jsx_runtime_1.jsx)("span", { className: "crisp-recordpage-illustrated-icon", "aria-hidden": "true", children: (0, jsx_runtime_1.jsx)(lucide_react_1.Merge, { size: 40, strokeWidth: 1.25 }) }), (0, jsx_runtime_1.jsx)("div", { className: "crisp-recordpage-illustrated-title", children: t.noDealsFound }), (0, jsx_runtime_1.jsx)("div", { className: "crisp-recordpage-illustrated-desc", children: t.noDealsHint }), (0, jsx_runtime_1.jsx)("div", { className: "crisp-recordpage-illustrated-actions", children: (0, jsx_runtime_1.jsxs)(button_1.Button, { intent: "primary", size: "md", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Plus, { size: 15, strokeWidth: 1.75 }), t.newDeal] }) })] }) }));
|
|
759
862
|
case "notes":
|
|
760
863
|
return ((0, jsx_runtime_1.jsx)("div", { className: "crisp-recordpage-panel", children: (0, jsx_runtime_1.jsx)(NotesSection, { notes: notes, onAdd: addNote, labels: t }) }));
|
|
761
864
|
case "tasks":
|
|
762
865
|
return ((0, jsx_runtime_1.jsx)("div", { className: "crisp-recordpage-panel", children: (0, jsx_runtime_1.jsx)(TasksSection, { tasks: tasks, onAdd: addTask, onToggle: toggleTask, labels: t }) }));
|
|
763
866
|
case "emails":
|
|
764
|
-
return (0, jsx_runtime_1.jsx)("div", { className: "crisp-recordpage-panel", children:
|
|
867
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: "crisp-recordpage-panel", children: [(0, jsx_runtime_1.jsx)(SectionHead, { title: t.tabs.emails }), (0, jsx_runtime_1.jsx)("div", { className: "crisp-recordpage-panel crisp-recordpage-panel--center", children: (0, jsx_runtime_1.jsxs)("div", { className: "crisp-recordpage-illustrated", children: [(0, jsx_runtime_1.jsx)("span", { className: "crisp-recordpage-illustrated-icon", "aria-hidden": "true", children: (0, jsx_runtime_1.jsx)(lucide_react_1.Mail, { size: 40, strokeWidth: 1.25 }) }), (0, jsx_runtime_1.jsx)("div", { className: "crisp-recordpage-illustrated-title", children: t.noEmails }), (0, jsx_runtime_1.jsx)("div", { className: "crisp-recordpage-illustrated-desc", children: t.noEmailsHint }), (0, jsx_runtime_1.jsx)("div", { className: "crisp-recordpage-illustrated-actions", children: (0, jsx_runtime_1.jsx)(button_1.Button, { intent: "primary", size: "md", children: t.configureMailbox }) })] }) })] }));
|
|
868
|
+
case "calls":
|
|
869
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: "crisp-recordpage-panel crisp-recordpage-panel--center", children: (0, jsx_runtime_1.jsxs)("div", { className: "crisp-recordpage-illustrated", children: [(0, jsx_runtime_1.jsx)("span", { className: "crisp-recordpage-illustrated-icon", "aria-hidden": "true", children: (0, jsx_runtime_1.jsx)(lucide_react_1.Phone, { size: 40, strokeWidth: 1.25 }) }), (0, jsx_runtime_1.jsx)("div", { className: "crisp-recordpage-illustrated-title crisp-recordpage-illustrated-title--lg", children: t.upgradeToViewCalls }), (0, jsx_runtime_1.jsx)("div", { className: "crisp-recordpage-illustrated-desc", children: t.callsUpgradeHint }), (0, jsx_runtime_1.jsx)("div", { className: "crisp-recordpage-illustrated-actions", children: (0, jsx_runtime_1.jsxs)(button_1.Button, { intent: "primary", size: "md", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Zap, { size: 15, strokeWidth: 1.75 }), t.upgrade] }) })] }) }));
|
|
765
870
|
default:
|
|
766
871
|
return ((0, jsx_runtime_1.jsx)("div", { className: "crisp-recordpage-panel", children: (0, jsx_runtime_1.jsx)(EmptyCard, { icon: (0, jsx_runtime_1.jsx)(lucide_react_1.Folder, { size: 22, strokeWidth: 1.5 }), children: t.nothingHereYet }) }));
|
|
767
872
|
}
|
|
@@ -779,7 +884,7 @@ function RecordPage({ record, columns, collection = "Records", labels, onBack, o
|
|
|
779
884
|
const effectivePanel = tabsProp
|
|
780
885
|
? (active) => tabsProp.find((tb) => tb.id === active)?.render()
|
|
781
886
|
: builtinPanel;
|
|
782
|
-
return ((0, jsx_runtime_1.jsxs)("div", { className: (0, recordpage_recipe_1.recordpageRecipe)({ className }), ...rest, children: [(0, jsx_runtime_1.jsxs)("div", { className: "crisp-recordpage-topbar", "data-peek": onClose ? "" : undefined, children: [onClose ? ((0, jsx_runtime_1.jsxs)("div", { className: "crisp-recordpage-peekbar", children: [(0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-recordpage-peek-iconbtn", "aria-label": closeLabel, onClick: onClose, children: (0, jsx_runtime_1.jsx)(lucide_react_1.X, { size: 16, strokeWidth: 1.75 }) }), (onPrevious || onNext) && ((0, jsx_runtime_1.jsxs)("span", { className: "crisp-recordpage-peek-pager", children: [(0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-recordpage-peek-navbtn", "aria-label": previousRecordLabel, disabled: !onPrevious, onClick: onPrevious, children: (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronUp, { size: 16, strokeWidth: 1.75 }) }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-recordpage-peek-navbtn", "aria-label": nextRecordLabel, disabled: !onNext, onClick: onNext, children: (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronDown, { size: 16, strokeWidth: 1.75 }) })] })), counterLabel != null && ((0, jsx_runtime_1.jsx)("span", { className: "crisp-recordpage-peek-counter", children: counterLabel }))] })) : ((0, jsx_runtime_1.jsxs)("nav", { className: "crisp-recordpage-crumbs", "aria-label": t.breadcrumb, children: [(0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-recordpage-crumb", onClick: onBack, "aria-label": `Back to ${collection}`, children: collection }), (0, jsx_runtime_1.jsx)("span", { className: "crisp-recordpage-crumb-sep", children: "/" }), (0, jsx_runtime_1.jsx)("span", { className: "crisp-recordpage-crumb-name", children: name })] })), (0, jsx_runtime_1.jsxs)("div", { className: "crisp-recordpage-actions", children: [(0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-recordpage-action crisp-recordpage-action--ghost", onClick: onShare, children: t.share }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-recordpage-action crisp-recordpage-action--icon", onClick: () => setCommentsOpen(true), "aria-label": t.comments, children: (0, jsx_runtime_1.jsx)(lucide_react_1.MessageSquare, { size: 15, strokeWidth: 1.75 }) }), (0, jsx_runtime_1.jsxs)("button", { type: "button", className: "crisp-recordpage-action crisp-recordpage-action--ghost", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Sparkles, { size: 14, strokeWidth: 1.75, color: "var(--crisp-bg-brand-solid)" }), t.askAttio] }), onToggleFull && ((0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-recordpage-action crisp-recordpage-action--icon", onClick: onToggleFull, "aria-label": full ? t.exitFullScreen : t.fullScreen, children: full ? ((0, jsx_runtime_1.jsx)(lucide_react_1.Minimize2, { size: 15, strokeWidth: 1.75 })) : ((0, jsx_runtime_1.jsx)(lucide_react_1.Maximize2, { size: 15, strokeWidth: 1.75 })) })), (0, jsx_runtime_1.jsxs)(menu_1.Menu, { align: "end", "aria-label": t.recordActions, trigger: (0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-recordpage-action crisp-recordpage-action--icon", "aria-label": t.recordActions, children: (0, jsx_runtime_1.jsx)(lucide_react_1.MoreHorizontal, { size: 16, strokeWidth: 1.75 }) }), children: [(0, jsx_runtime_1.jsx)(menu_1.MenuItem, { icon: (0, jsx_runtime_1.jsx)(lucide_react_1.Merge, { size: 15, strokeWidth: 1.75 }), children: t.mergeRecords }), (0, jsx_runtime_1.jsx)(menu_1.MenuItem, { icon: (0, jsx_runtime_1.jsx)(lucide_react_1.Settings, { size: 15, strokeWidth: 1.75 }), children: t.configurePage }), (0, jsx_runtime_1.jsx)(menu_1.MenuItem, { icon: (0, jsx_runtime_1.jsx)(lucide_react_1.Copy, { size: 15, strokeWidth: 1.75 }), children: t.copyPageUrl }), (0, jsx_runtime_1.jsx)(menu_1.MenuItem, { icon: (0, jsx_runtime_1.jsx)(lucide_react_1.Copy, { size: 15, strokeWidth: 1.75 }), children: t.copyRecordId }), (0, jsx_runtime_1.jsx)(menu_1.MenuSeparator, {}), (0, jsx_runtime_1.jsx)(menu_1.MenuItem, { icon: (0, jsx_runtime_1.jsx)(lucide_react_1.Star, { size: 15, strokeWidth: 1.75 }), onSelect: () => setFavorite((f) => !f), children: favorite ? t.removeFromFavourites : t.addToFavourites }), (0, jsx_runtime_1.jsx)(menu_1.MenuSeparator, {}), (0, jsx_runtime_1.jsx)(menu_1.MenuItem, { icon: (0, jsx_runtime_1.jsx)(lucide_react_1.Trash2, { size: 15, strokeWidth: 1.75 }), danger: true, children: t.deleteRecord })] })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "crisp-recordpage-body", children: [(0, jsx_runtime_1.jsxs)("aside", { className: "crisp-recordpage-details", style: { width: detailsW }, children: [(0, jsx_runtime_1.jsxs)("div", { className: "crisp-recordpage-entity", children: [entity?.avatar ? ((0, jsx_runtime_1.jsx)("img", { className: "crisp-recordpage-entity-logo", src: entity.avatar, alt: "" })) : ((0, jsx_runtime_1.jsx)(avatar_1.Avatar, { size: "lg", shape:
|
|
887
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: (0, recordpage_recipe_1.recordpageRecipe)({ className }), ...rest, children: [(0, jsx_runtime_1.jsxs)("div", { className: "crisp-recordpage-topbar", "data-peek": onClose ? "" : undefined, children: [onClose ? ((0, jsx_runtime_1.jsxs)("div", { className: "crisp-recordpage-peekbar", children: [(0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-recordpage-peek-iconbtn", "aria-label": closeLabel, onClick: onClose, children: (0, jsx_runtime_1.jsx)(lucide_react_1.X, { size: 16, strokeWidth: 1.75 }) }), (onPrevious || onNext) && ((0, jsx_runtime_1.jsxs)("span", { className: "crisp-recordpage-peek-pager", children: [(0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-recordpage-peek-navbtn", "aria-label": previousRecordLabel, disabled: !onPrevious, onClick: onPrevious, children: (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronUp, { size: 16, strokeWidth: 1.75 }) }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-recordpage-peek-navbtn", "aria-label": nextRecordLabel, disabled: !onNext, onClick: onNext, children: (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronDown, { size: 16, strokeWidth: 1.75 }) })] })), counterLabel != null && ((0, jsx_runtime_1.jsx)("span", { className: "crisp-recordpage-peek-counter", children: counterLabel }))] })) : (showBreadcrumb && ((0, jsx_runtime_1.jsxs)("nav", { className: "crisp-recordpage-crumbs", "aria-label": t.breadcrumb, children: [(0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-recordpage-crumb", onClick: onBack, "aria-label": `Back to ${collection}`, children: collection }), (0, jsx_runtime_1.jsx)("span", { className: "crisp-recordpage-crumb-sep", children: "/" }), (0, jsx_runtime_1.jsx)("span", { className: "crisp-recordpage-crumb-name", children: name })] }))), (0, jsx_runtime_1.jsxs)("div", { className: "crisp-recordpage-actions", children: [showShare && ((0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-recordpage-action crisp-recordpage-action--ghost", onClick: onShare, children: t.share })), showComments && ((0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-recordpage-action crisp-recordpage-action--icon", onClick: () => setCommentsOpen(true), "aria-label": t.comments, children: (0, jsx_runtime_1.jsx)(lucide_react_1.MessageSquare, { size: 15, strokeWidth: 1.75 }) })), showAskAttio && ((0, jsx_runtime_1.jsxs)("button", { type: "button", className: "crisp-recordpage-action crisp-recordpage-action--ghost", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Sparkles, { size: 14, strokeWidth: 1.75, color: "var(--crisp-bg-brand-solid)" }), t.askAttio] })), onToggleFull && ((0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-recordpage-action crisp-recordpage-action--icon", onClick: onToggleFull, "aria-label": full ? t.exitFullScreen : t.fullScreen, children: full ? ((0, jsx_runtime_1.jsx)(lucide_react_1.Minimize2, { size: 15, strokeWidth: 1.75 })) : ((0, jsx_runtime_1.jsx)(lucide_react_1.Maximize2, { size: 15, strokeWidth: 1.75 })) })), (0, jsx_runtime_1.jsxs)(menu_1.Menu, { align: "end", "aria-label": t.recordActions, trigger: (0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-recordpage-action crisp-recordpage-action--icon", "aria-label": t.recordActions, children: (0, jsx_runtime_1.jsx)(lucide_react_1.MoreHorizontal, { size: 16, strokeWidth: 1.75 }) }), children: [(0, jsx_runtime_1.jsx)(menu_1.MenuItem, { icon: (0, jsx_runtime_1.jsx)(lucide_react_1.Merge, { size: 15, strokeWidth: 1.75 }), children: t.mergeRecords }), (0, jsx_runtime_1.jsx)(menu_1.MenuItem, { icon: (0, jsx_runtime_1.jsx)(lucide_react_1.Settings, { size: 15, strokeWidth: 1.75 }), children: t.configurePage }), (0, jsx_runtime_1.jsx)(menu_1.MenuItem, { icon: (0, jsx_runtime_1.jsx)(lucide_react_1.Copy, { size: 15, strokeWidth: 1.75 }), children: t.copyPageUrl }), (0, jsx_runtime_1.jsx)(menu_1.MenuItem, { icon: (0, jsx_runtime_1.jsx)(lucide_react_1.Copy, { size: 15, strokeWidth: 1.75 }), children: t.copyRecordId }), (0, jsx_runtime_1.jsx)(menu_1.MenuSeparator, {}), (0, jsx_runtime_1.jsx)(menu_1.MenuItem, { icon: (0, jsx_runtime_1.jsx)(lucide_react_1.Star, { size: 15, strokeWidth: 1.75 }), onSelect: () => setFavorite((f) => !f), children: favorite ? t.removeFromFavourites : t.addToFavourites }), actions && actions.length > 0 && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(menu_1.MenuSeparator, {}), actions.map((a) => ((0, jsx_runtime_1.jsx)(menu_1.MenuItem, { icon: (0, jsx_runtime_1.jsx)(a.icon, { size: 15, strokeWidth: 1.75 }), onSelect: a.onSelect, danger: a.danger, children: a.label }, a.label)))] })), (0, jsx_runtime_1.jsx)(menu_1.MenuSeparator, {}), (0, jsx_runtime_1.jsx)(menu_1.MenuItem, { icon: (0, jsx_runtime_1.jsx)(lucide_react_1.Trash2, { size: 15, strokeWidth: 1.75 }), onSelect: onDelete, danger: true, children: t.deleteRecord })] })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "crisp-recordpage-body", children: [(0, jsx_runtime_1.jsxs)("aside", { className: "crisp-recordpage-details", style: { width: detailsW }, children: [(0, jsx_runtime_1.jsxs)("div", { className: "crisp-recordpage-entity", children: [entity?.avatar ? ((0, jsx_runtime_1.jsx)("img", { className: "crisp-recordpage-entity-logo", src: entity.avatar, alt: "" })) : ((0, jsx_runtime_1.jsx)(avatar_1.Avatar, { size: "lg", shape: avatarShape, tone: "brand", children: name.charAt(0) })), (0, jsx_runtime_1.jsx)("span", { className: "crisp-recordpage-entity-name", children: name }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-recordpage-entity-iconbtn", "aria-label": t.editRecord, onClick: onEditRecord, children: (0, jsx_runtime_1.jsx)(lucide_react_1.Pencil, { size: 15, strokeWidth: 1.75 }) }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-recordpage-entity-iconbtn", "data-active": favorite ? "" : undefined, "aria-pressed": favorite, onClick: () => setFavorite((f) => !f), "aria-label": favorite ? t.removeFromFavourites : t.addToFavourites, children: (0, jsx_runtime_1.jsx)(lucide_react_1.Star, { size: 15, strokeWidth: 1.75 }) })] }), (0, jsx_runtime_1.jsxs)("div", { className: "crisp-recordpage-quickactions", children: [(0, jsx_runtime_1.jsxs)("button", { type: "button", className: "crisp-recordpage-compose", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Mail, { size: 16, strokeWidth: 1.75 }), t.composeEmail] }), (0, jsx_runtime_1.jsxs)("button", { type: "button", className: "crisp-recordpage-compose", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Plus, { size: 16, strokeWidth: 1.75 }), t.addToList] }), quickActions.map((a, i) => ((0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-recordpage-quickbtn", "aria-label": a.label, onClick: a.onSelect, children: (0, jsx_runtime_1.jsx)(a.icon, { size: 15, strokeWidth: 1.75 }) }, i)))] }), (0, jsx_runtime_1.jsx)(RecordDetails, { record: record, columns: columns, sections: sections, onEdit: onEdit, labels: labels }), (0, jsx_runtime_1.jsxs)("div", { className: "crisp-recordpage-section", children: [(0, jsx_runtime_1.jsx)(SectionHead, { title: t.tabs.lists, action: (0, jsx_runtime_1.jsxs)("button", { type: "button", className: "crisp-recordpage-addbtn", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Plus, { size: 14, strokeWidth: 2 }), t.addToList] }) }), (0, jsx_runtime_1.jsx)("div", { className: "crisp-recordpage-emptystate", children: t.noLists })] })] }), (0, jsx_runtime_1.jsx)("div", { className: "crisp-recordpage-resizer", "data-active": resizing ? "" : undefined, role: "separator", "aria-orientation": "vertical", "aria-label": t.resizeDetails, "aria-valuenow": Math.round(detailsW), "aria-valuemin": DETAILS_MIN_W, "aria-valuemax": DETAILS_MAX_W, tabIndex: 0, onPointerDown: onResizeDown, onPointerMove: onResizeMove, onPointerUp: onResizeUp, onKeyDown: (e) => {
|
|
783
888
|
if (e.key !== "ArrowLeft" && e.key !== "ArrowRight")
|
|
784
889
|
return;
|
|
785
890
|
// The pointer drag learned its direction in #212; this did not.
|
|
@@ -791,6 +896,6 @@ function RecordPage({ record, columns, collection = "Records", labels, onBack, o
|
|
|
791
896
|
setDetailsW((w) => grow
|
|
792
897
|
? Math.min(DETAILS_MAX_W, w + 16)
|
|
793
898
|
: Math.max(DETAILS_MIN_W, w - 16));
|
|
794
|
-
} }), (0, jsx_runtime_1.jsx)(
|
|
899
|
+
} }), (0, jsx_runtime_1.jsx)(RecordPageMain, { children: (0, jsx_runtime_1.jsx)(tabs_1.Tabs, { tabs: effectiveTabs, defaultValue: tabsProp ? defaultTab : "overview", "aria-label": `${name} record`, children: effectivePanel }) })] }), (0, jsx_runtime_1.jsx)(drawer_1.Drawer, { open: commentsOpen, onOpenChange: setCommentsOpen, "aria-label": t.comments, style: { width: 320 }, children: (0, jsx_runtime_1.jsx)(comments_1.CommentsPanel, { threads: comments, currentUserId: me.id, onPost: postComment, onClose: () => setCommentsOpen(false), role: undefined }) }), (0, jsx_runtime_1.jsx)(WorkflowPopover, { open: workflowPopoverOpen, onClose: () => setWorkflowPopoverOpen(false), labels: t })] }));
|
|
795
900
|
}
|
|
796
901
|
RecordPage.displayName = "RecordPage";
|
|
@@ -127,6 +127,19 @@ const config = (0, core_1.defineRecipe)({
|
|
|
127
127
|
the component's own comment) -- NOT \`display: contents\`, which
|
|
128
128
|
drops a button's accessible semantics in some engines. */
|
|
129
129
|
& .crisp-recordpage-edit-trigger { display: inline-flex; flex-wrap: wrap; align-items: center; gap: ${tokens_1.contract.space["1"]}; margin: 0; padding: 0; border: none; background: none; text-align: start; font: inherit; color: inherit; cursor: pointer; }
|
|
130
|
+
/* #990: measured live on app.attio.com's URL/domain attribute (hover the
|
|
131
|
+
row) -- the link itself never carries the edit affordance, a
|
|
132
|
+
separate 20px button trails it, mounting only on hover: 16px icon,
|
|
133
|
+
rgba(0,0,0,0.55) (crisp's own color-mix(fgPrimary 55%) -- the exact
|
|
134
|
+
formula .crisp-datatable-entity-action's identical muted icon
|
|
135
|
+
already uses), 6px radius (radius.sm). Reveal is opacity-based,
|
|
136
|
+
not the mount/unmount Attio's own DOM does, so a keyboard user
|
|
137
|
+
tabbing to it (:focus-visible) doesn't need the row hovered first --
|
|
138
|
+
same mechanism DataTable's own row actions (#601) already use. */
|
|
139
|
+
& .crisp-recordpage-edit-affordance { display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; width: 20px; height: 20px; margin: 0; padding: 0; border: none; border-radius: ${tokens_1.contract.radius.sm}; background: none; font-family: inherit; color: color-mix(in srgb, ${tokens_1.contract.fgPrimary} 55%, transparent); cursor: pointer; opacity: 0; transition: opacity .12s, background-color .12s, color .12s; }
|
|
140
|
+
& .crisp-recordpage-field:hover .crisp-recordpage-edit-affordance,
|
|
141
|
+
& .crisp-recordpage-edit-affordance:focus-visible { opacity: 1; }
|
|
142
|
+
& .crisp-recordpage-edit-affordance:hover { background: color-mix(in srgb, ${tokens_1.contract.fgPrimary} 6%, transparent); color: ${tokens_1.contract.fgPrimary}; }
|
|
130
143
|
& .crisp-recordpage-empty { color: ${tokens_1.contract.fgMuted}; }
|
|
131
144
|
|
|
132
145
|
/* #431: measured live on app.attio.com's record page Details column --
|
|
@@ -229,6 +242,31 @@ const config = (0, core_1.defineRecipe)({
|
|
|
229
242
|
& .crisp-recordpage-emptycard { display: flex; flex-direction: column; align-items: center; gap: ${tokens_1.contract.space["1.5"]}; padding: ${tokens_1.contract.space["6"]} ${tokens_1.contract.space["3"]}; border: 1px dashed ${tokens_1.contract.borderDefault}; border-radius: ${tokens_1.contract.radius.xl}; color: ${tokens_1.contract.fgMuted}; font-size: ${tokens_1.contract.text.md}; text-align: center; }
|
|
230
243
|
& .crisp-recordpage-emptycard svg { color: ${tokens_1.contract.fgMuted}; opacity: 0.7; }
|
|
231
244
|
|
|
245
|
+
/* #622: the Activity tab's own toolbar -- "Filters" + "+ Add meeting",
|
|
246
|
+
both the same measured button chrome as the topbar's own action
|
|
247
|
+
buttons (28px, e.field shadow, 14/500 label). */
|
|
248
|
+
& .crisp-recordpage-tabtoolbar { display: inline-flex; align-items: center; gap: ${tokens_1.contract.space["1.5"]}; }
|
|
249
|
+
|
|
250
|
+
/* #622: Files tab's empty state -- measured live on Attio's own record
|
|
251
|
+
page (a company with no files uploaded). The 138x138 dotted-grid
|
|
252
|
+
backdrop is the same measured tile EmptyState's records variant uses
|
|
253
|
+
(crisp/#603), duplicated here rather than composed through that
|
|
254
|
+
component because this surface's title (16/600) and body (14/500,
|
|
255
|
+
63%-mixed fgPrimary, not the flat fgMuted EmptyState uses) measure to
|
|
256
|
+
their own values, matching neither of that component's existing
|
|
257
|
+
variants. */
|
|
258
|
+
& .crisp-recordpage-panel--center { flex: 1; align-items: center; justify-content: center; }
|
|
259
|
+
& .crisp-recordpage-illustrated { display: flex; flex-direction: column; align-items: center; gap: ${tokens_1.contract.space["1.5"]}; text-align: center; }
|
|
260
|
+
& .crisp-recordpage-illustrated-icon { display: flex; align-items: center; justify-content: center; width: 138px; height: 138px; margin-bottom: ${tokens_1.contract.space["2"]}; color: color-mix(in srgb, ${tokens_1.contract.fgPrimary} 22%, transparent); background: radial-gradient(circle, color-mix(in srgb, ${tokens_1.contract.fgPrimary} 12%, transparent) 1.2px, transparent 1.2px) 0 0 / 16px 16px; }
|
|
261
|
+
& .crisp-recordpage-illustrated-title { font-size: ${tokens_1.contract.text.xl}; font-weight: ${tokens_1.contract.weight.semibold}; color: ${tokens_1.contract.fgPrimary}; }
|
|
262
|
+
/* #988: the Calls tab's own gated state measures a larger 20/24 title
|
|
263
|
+
than Files/Deals' 16/600 -- Attio's own "empty view" pattern (the
|
|
264
|
+
same 20/600 EmptyState's page/records variants already carry, not a
|
|
265
|
+
one-off). */
|
|
266
|
+
& .crisp-recordpage-illustrated-title--lg { font-size: 20px; line-height: 24px; }
|
|
267
|
+
& .crisp-recordpage-illustrated-desc { max-width: 320px; 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); }
|
|
268
|
+
& .crisp-recordpage-illustrated-actions { display: inline-flex; align-items: center; justify-content: center; flex-wrap: wrap; gap: ${tokens_1.contract.space["1.5"]}; margin-top: ${tokens_1.contract.space["2"]}; }
|
|
269
|
+
|
|
232
270
|
& .crisp-recordpage-action[data-active] svg { color: #f5b83d; fill: #f5b83d; }
|
|
233
271
|
|
|
234
272
|
& .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); }
|
|
@@ -239,6 +277,31 @@ const config = (0, core_1.defineRecipe)({
|
|
|
239
277
|
& .crisp-recordstable-tag[data-tone="purple"] { background: color-mix(in srgb, ${tokens_1.contract.bgPurple} 16%, transparent); color: color-mix(in srgb, ${tokens_1.contract.bgPurple} 80%, ${tokens_1.contract.fgPrimary}); }
|
|
240
278
|
& .crisp-recordstable-tag[data-tone="cyan"] { background: color-mix(in srgb, ${tokens_1.contract.bgCyan} 18%, transparent); color: color-mix(in srgb, ${tokens_1.contract.bgCyan} 82%, ${tokens_1.contract.fgPrimary}); }
|
|
241
279
|
& .crisp-recordstable-tag[data-tone="orange"] { background: color-mix(in srgb, ${tokens_1.contract.bgOrange} 16%, transparent); color: color-mix(in srgb, ${tokens_1.contract.bgOrange} 82%, ${tokens_1.contract.fgPrimary}); }
|
|
280
|
+
|
|
281
|
+
/* #620: the "Run workflow" quick action's own popover -- measured live:
|
|
282
|
+
a centered, top-anchored overlay sharing product/command's own
|
|
283
|
+
Cmd-K-palette shell exactly (elevation.modal, bgOverlayFrost 60% +
|
|
284
|
+
blur(15px), radius 2xl/xl outer/inner) -- duplicated here rather than
|
|
285
|
+
imported, the same "own copy per product surface" choice already
|
|
286
|
+
made elsewhere in this codebase (product/command sits outside this
|
|
287
|
+
lane's own product/recordpage ownership). */
|
|
288
|
+
& .crisp-recordpage-workflow-backdrop { position: fixed; inset: 0; z-index: 60; background: transparent; }
|
|
289
|
+
& .crisp-recordpage-workflow-positioner { position: fixed; inset: 0; z-index: 61; display: flex; align-items: flex-start; justify-content: center; padding: 15vh ${tokens_1.contract.space["4"]} ${tokens_1.contract.space["4"]}; }
|
|
290
|
+
& .crisp-recordpage-workflow-content { position: relative; display: flex; flex-direction: column; box-sizing: border-box; width: 90vw; max-width: 808px; padding: ${tokens_1.contract.space["1"]}; border-radius: ${tokens_1.contract.radius["2xl"]}; background: color-mix(in srgb, ${tokens_1.contract.bgOverlayFrost} 60%, transparent); backdrop-filter: blur(15px); -webkit-backdrop-filter: blur(15px); box-shadow: ${tokens_1.elevation.modal}; opacity: 1; transform: scale(1); transition: opacity .2s ease, transform .2s ease; @starting-style { opacity: 0; transform: scale(0.97); } }
|
|
291
|
+
& .crisp-recordpage-workflow-card { display: flex; flex-direction: column; box-sizing: border-box; border-radius: ${tokens_1.contract.radius.xl}; background: ${tokens_1.contract.bgSurface}; overflow: hidden; }
|
|
292
|
+
& .crisp-recordpage-workflow-header { display: flex; align-items: center; gap: ${tokens_1.contract.space["2"]}; height: 52px; flex-shrink: 0; padding: 0 ${tokens_1.contract.space["2"]} 0 ${tokens_1.contract.space["4"]}; }
|
|
293
|
+
& .crisp-recordpage-workflow-title { flex: 1; 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}; letter-spacing: -0.07px; color: ${tokens_1.contract.fgPrimary}; }
|
|
294
|
+
& .crisp-recordpage-workflow-close { display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; width: 28px; height: 28px; margin: 0; padding: 0; border: none; border-radius: ${tokens_1.contract.radius.md}; background: none; color: color-mix(in srgb, ${tokens_1.contract.fgPrimary} 55%, transparent); font-family: inherit; cursor: pointer; transition: background-color .12s, color .12s; }
|
|
295
|
+
& .crisp-recordpage-workflow-close:hover { background: color-mix(in srgb, ${tokens_1.contract.fgPrimary} 6%, transparent); color: ${tokens_1.contract.fgPrimary}; }
|
|
296
|
+
& .crisp-recordpage-workflow-search { display: flex; align-items: center; min-height: 52px; padding: ${tokens_1.contract.space["4"]}; }
|
|
297
|
+
& .crisp-recordpage-workflow-input { flex: 1; min-width: 0; border: none; outline: none; background: transparent; font-family: inherit; font-size: ${tokens_1.contract.text.xl}; font-weight: ${tokens_1.contract.weight.medium}; color: ${tokens_1.contract.fgPrimary}; }
|
|
298
|
+
& .crisp-recordpage-workflow-input::placeholder { color: ${tokens_1.contract.fgMuted}; }
|
|
299
|
+
& .crisp-recordpage-workflow-sep { height: 1px; flex-shrink: 0; background: ${tokens_1.contract.borderSubtle}; }
|
|
300
|
+
& .crisp-recordpage-workflow-empty { display: flex; flex-direction: column; align-items: center; gap: ${tokens_1.contract.space["2"]}; padding: ${tokens_1.contract.space["6"]} ${tokens_1.contract.space["3"]}; text-align: center; }
|
|
301
|
+
& .crisp-recordpage-workflow-create { display: inline-flex; align-items: center; gap: ${tokens_1.contract.space["1.5"]}; height: 32px; padding: 0 ${tokens_1.contract.space["3"]}; margin-top: ${tokens_1.contract.space["2"]}; border: none; border-radius: ${tokens_1.contract.radius.md}; background: ${tokens_1.contract.bgSurface}; box-shadow: ${tokens_1.elevation.field}; font-family: inherit; font-size: ${tokens_1.contract.text.lg}; font-weight: ${tokens_1.contract.weight.medium}; color: ${tokens_1.contract.fgPrimary}; cursor: pointer; transition: background-color .12s; }
|
|
302
|
+
& .crisp-recordpage-workflow-create:hover { background: ${tokens_1.contract.bgRaised}; }
|
|
303
|
+
& .crisp-recordpage-workflow-footer { display: flex; align-items: center; height: 44px; flex-shrink: 0; padding: 0 ${tokens_1.contract.space["2.5"]}; border-top: 1px solid ${tokens_1.contract.borderSubtle}; }
|
|
304
|
+
& .crisp-recordpage-workflow-footer-nav { display: flex; align-items: center; gap: ${tokens_1.contract.space["1.5"]}; font-size: ${tokens_1.contract.text.sm}; font-weight: ${tokens_1.contract.weight.medium}; color: color-mix(in srgb, ${tokens_1.contract.fgPrimary} 63%, transparent); }
|
|
242
305
|
`,
|
|
243
306
|
variants: {},
|
|
244
307
|
defaultVariants: {},
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.recordpickerRecipe = exports.RecordPicker = void 0;
|
|
4
|
+
var recordpicker_1 = require("./recordpicker");
|
|
5
|
+
Object.defineProperty(exports, "RecordPicker", { enumerable: true, get: function () { return recordpicker_1.RecordPicker; } });
|
|
6
|
+
var recordpicker_recipe_1 = require("./recordpicker.recipe");
|
|
7
|
+
Object.defineProperty(exports, "recordpickerRecipe", { enumerable: true, get: function () { return recordpicker_recipe_1.recordpickerRecipe; } });
|