@crisp-ui-kit/crisp 0.54.2 → 0.54.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/board.css +13 -1
- package/dist/board.layered.css +13 -1
- package/dist/chip.css +7 -1
- package/dist/chip.d.ts +2 -1
- package/dist/chip.layered.css +7 -1
- package/dist/cjs/components/chip/chip.js +3 -2
- package/dist/cjs/components/chip/chip.recipe.js +37 -1
- package/dist/cjs/components/datatable/datatable.js +489 -111
- package/dist/cjs/components/emptystate/emptystate.js +3 -2
- package/dist/cjs/components/emptystate/emptystate.recipe.js +26 -1
- package/dist/cjs/components/personchip/index.js +17 -0
- package/dist/cjs/components/personchip/personchip.js +50 -0
- package/dist/cjs/components/table/table.js +2 -2
- package/dist/cjs/components/tabletoolbar/tabletoolbar.js +41 -4
- package/dist/cjs/components/tabs/tabs.js +2 -2
- package/dist/cjs/components/tabs/tabs.recipe.js +30 -3
- package/dist/cjs/components/text/index.js +7 -0
- package/dist/cjs/components/text/text.js +55 -0
- package/dist/cjs/components/text/text.recipe.js +34 -0
- package/dist/cjs/index.js +4 -0
- package/dist/cjs/product/board/board.js +60 -2
- package/dist/cjs/product/board/board.recipe.js +10 -0
- package/dist/cjs/product/comments/comments.js +426 -0
- package/dist/cjs/product/comments/comments.recipe.js +126 -0
- package/dist/cjs/product/comments/index.js +8 -0
- package/dist/cjs/product/headerbar/headerbar.recipe.js +9 -1
- package/dist/cjs/product/listpage/listpage.recipe.js +10 -1
- package/dist/cjs/product/objectsettings/index.js +16 -0
- package/dist/cjs/product/objectsettings/objectsettings.js +365 -0
- package/dist/cjs/product/objectsettings/objectsettings.recipe.js +65 -0
- package/dist/cjs/product/recordpage/recordpage.recipe.js +10 -1
- package/dist/cjs/product/recordstable/recordstable.js +89 -6
- package/dist/cjs/product/richtext/richtext.recipe.js +26 -1
- package/dist/cjs/product/taskstable/taskstable.recipe.js +10 -1
- package/dist/comments.css +80 -0
- package/dist/comments.d.ts +7 -0
- package/dist/comments.layered.css +82 -0
- package/dist/emptystate.css +15 -1
- package/dist/emptystate.d.ts +2 -2
- package/dist/emptystate.layered.css +15 -1
- package/dist/esm/components/chip/chip.js +3 -2
- package/dist/esm/components/chip/chip.recipe.js +37 -1
- package/dist/esm/components/datatable/datatable.js +488 -111
- package/dist/esm/components/emptystate/emptystate.js +4 -3
- package/dist/esm/components/emptystate/emptystate.recipe.js +26 -1
- package/dist/esm/components/emptystate/index.js +1 -1
- package/dist/esm/components/personchip/index.js +1 -0
- package/dist/esm/components/personchip/personchip.js +47 -0
- package/dist/esm/components/table/table.js +2 -2
- package/dist/esm/components/tabletoolbar/tabletoolbar.js +43 -6
- package/dist/esm/components/tabs/tabs.js +2 -2
- package/dist/esm/components/tabs/tabs.recipe.js +30 -3
- package/dist/esm/components/text/index.js +2 -0
- package/dist/esm/components/text/text.js +19 -0
- package/dist/esm/components/text/text.recipe.js +31 -0
- package/dist/esm/index.js +4 -0
- package/dist/esm/product/board/board.js +61 -3
- package/dist/esm/product/board/board.recipe.js +10 -0
- package/dist/esm/product/comments/comments.js +388 -0
- package/dist/esm/product/comments/comments.recipe.js +123 -0
- package/dist/esm/product/comments/index.js +2 -0
- package/dist/esm/product/headerbar/headerbar.recipe.js +9 -1
- package/dist/esm/product/listpage/listpage.recipe.js +10 -1
- package/dist/esm/product/objectsettings/index.js +2 -0
- package/dist/esm/product/objectsettings/objectsettings.js +322 -0
- package/dist/esm/product/objectsettings/objectsettings.recipe.js +62 -0
- package/dist/esm/product/recordpage/recordpage.recipe.js +10 -1
- package/dist/esm/product/recordstable/recordstable.js +90 -8
- package/dist/esm/product/richtext/richtext.recipe.js +26 -1
- package/dist/esm/product/taskstable/taskstable.recipe.js +10 -1
- package/dist/headerbar.css +1 -1
- package/dist/headerbar.layered.css +1 -1
- package/dist/listpage.css +8 -1
- package/dist/listpage.layered.css +8 -1
- package/dist/objectsettings.css +49 -0
- package/dist/objectsettings.d.ts +7 -0
- package/dist/objectsettings.layered.css +51 -0
- package/dist/recordpage.css +13 -2
- package/dist/recordpage.layered.css +13 -2
- package/dist/richtext.css +11 -1
- package/dist/richtext.layered.css +11 -1
- package/dist/styles.css +224 -10
- package/dist/styles.layered.css +224 -10
- package/dist/tabs.css +7 -1
- package/dist/tabs.d.ts +2 -2
- package/dist/tabs.layered.css +7 -1
- package/dist/taskstable.css +8 -1
- package/dist/taskstable.layered.css +8 -1
- package/dist/text.css +9 -0
- package/dist/text.d.ts +7 -0
- package/dist/text.layered.css +11 -0
- package/dist/types/components/chip/chip.d.ts +32 -0
- package/dist/types/components/chip/chip.recipe.d.ts +31 -0
- package/dist/types/components/datatable/datatable.d.ts +28 -4
- package/dist/types/components/emptystate/emptystate.d.ts +29 -4
- package/dist/types/components/emptystate/emptystate.recipe.d.ts +2 -0
- package/dist/types/components/emptystate/index.d.ts +1 -1
- package/dist/types/components/personchip/index.d.ts +1 -0
- package/dist/types/components/personchip/personchip.d.ts +37 -0
- package/dist/types/components/table/table.d.ts +36 -0
- package/dist/types/components/tabletoolbar/tabletoolbar.d.ts +8 -2
- package/dist/types/components/tabs/tabs.d.ts +4 -2
- package/dist/types/components/tabs/tabs.recipe.d.ts +18 -3
- package/dist/types/components/text/index.d.ts +2 -0
- package/dist/types/components/text/text.d.ts +32 -0
- package/dist/types/components/text/text.recipe.d.ts +40 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/product/board/board.d.ts +20 -1
- package/dist/types/product/comments/comments.d.ts +192 -0
- package/dist/types/product/comments/comments.recipe.d.ts +3 -0
- package/dist/types/product/comments/index.d.ts +2 -0
- package/dist/types/product/objectsettings/index.d.ts +2 -0
- package/dist/types/product/objectsettings/objectsettings.d.ts +261 -0
- package/dist/types/product/objectsettings/objectsettings.recipe.d.ts +3 -0
- package/dist/types/product/recordstable/recordstable.d.ts +24 -2
- package/package.json +21 -1
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.objectsettingsRecipe = exports.ObjectsList = exports.ObjectSettings = exports.ObjectGeneralSection = exports.ObjectDetailPage = exports.ObjectAppearanceSection = exports.CreateAttributeDialog = exports.AttributeTypeSelect = exports.AttributesTable = exports.ATTRIBUTE_TYPE_OPTIONS = exports.ATTRIBUTE_AI_AUTOFILL_OPTIONS = void 0;
|
|
4
|
+
var objectsettings_1 = require("./objectsettings");
|
|
5
|
+
Object.defineProperty(exports, "ATTRIBUTE_AI_AUTOFILL_OPTIONS", { enumerable: true, get: function () { return objectsettings_1.ATTRIBUTE_AI_AUTOFILL_OPTIONS; } });
|
|
6
|
+
Object.defineProperty(exports, "ATTRIBUTE_TYPE_OPTIONS", { enumerable: true, get: function () { return objectsettings_1.ATTRIBUTE_TYPE_OPTIONS; } });
|
|
7
|
+
Object.defineProperty(exports, "AttributesTable", { enumerable: true, get: function () { return objectsettings_1.AttributesTable; } });
|
|
8
|
+
Object.defineProperty(exports, "AttributeTypeSelect", { enumerable: true, get: function () { return objectsettings_1.AttributeTypeSelect; } });
|
|
9
|
+
Object.defineProperty(exports, "CreateAttributeDialog", { enumerable: true, get: function () { return objectsettings_1.CreateAttributeDialog; } });
|
|
10
|
+
Object.defineProperty(exports, "ObjectAppearanceSection", { enumerable: true, get: function () { return objectsettings_1.ObjectAppearanceSection; } });
|
|
11
|
+
Object.defineProperty(exports, "ObjectDetailPage", { enumerable: true, get: function () { return objectsettings_1.ObjectDetailPage; } });
|
|
12
|
+
Object.defineProperty(exports, "ObjectGeneralSection", { enumerable: true, get: function () { return objectsettings_1.ObjectGeneralSection; } });
|
|
13
|
+
Object.defineProperty(exports, "ObjectSettings", { enumerable: true, get: function () { return objectsettings_1.ObjectSettings; } });
|
|
14
|
+
Object.defineProperty(exports, "ObjectsList", { enumerable: true, get: function () { return objectsettings_1.ObjectsList; } });
|
|
15
|
+
var objectsettings_recipe_1 = require("./objectsettings.recipe");
|
|
16
|
+
Object.defineProperty(exports, "objectsettingsRecipe", { enumerable: true, get: function () { return objectsettings_recipe_1.objectsettingsRecipe; } });
|
|
@@ -0,0 +1,365 @@
|
|
|
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.ObjectSettings = exports.ATTRIBUTE_AI_AUTOFILL_OPTIONS = exports.ATTRIBUTE_TYPE_OPTIONS = void 0;
|
|
37
|
+
exports.ObjectsList = ObjectsList;
|
|
38
|
+
exports.ObjectDetailPage = ObjectDetailPage;
|
|
39
|
+
exports.ObjectGeneralSection = ObjectGeneralSection;
|
|
40
|
+
exports.ObjectAppearanceSection = ObjectAppearanceSection;
|
|
41
|
+
exports.AttributesTable = AttributesTable;
|
|
42
|
+
exports.AttributeTypeSelect = AttributeTypeSelect;
|
|
43
|
+
exports.CreateAttributeDialog = CreateAttributeDialog;
|
|
44
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
45
|
+
const lucide_react_1 = require("lucide-react");
|
|
46
|
+
const React = __importStar(require("react"));
|
|
47
|
+
const badge_1 = require("../../components/badge");
|
|
48
|
+
const datatable_1 = require("../../components/datatable");
|
|
49
|
+
const formfield_1 = require("../../components/formfield");
|
|
50
|
+
const input_1 = require("../../components/input");
|
|
51
|
+
const menu_1 = require("../../components/menu");
|
|
52
|
+
const select_1 = require("../../components/select");
|
|
53
|
+
const switch_1 = require("../../components/switch");
|
|
54
|
+
const tabs_1 = require("../../components/tabs");
|
|
55
|
+
const textarea_1 = require("../../components/textarea");
|
|
56
|
+
const core_1 = require("../../core");
|
|
57
|
+
const formdialog_1 = require("../formdialog");
|
|
58
|
+
const settingssection_1 = require("../settingssection");
|
|
59
|
+
const objectsettings_recipe_1 = require("./objectsettings.recipe");
|
|
60
|
+
/**
|
|
61
|
+
* Settings → Objects (crisp#637, P-122): H1 + subtitle, a search+Filter row,
|
|
62
|
+
* and a DataTable of every object (icon+name, type, record count,
|
|
63
|
+
* attribute count, a per-row "…" menu). Measured live against
|
|
64
|
+
* app.attio.com/<workspace>/settings/data/objects — the plan-gate banner
|
|
65
|
+
* above the table and the "5/3" object-count pill in the "Object" header
|
|
66
|
+
* (both billing-tier chrome) are skipped by decision, not oversight.
|
|
67
|
+
*/
|
|
68
|
+
function ObjectsList({ objects, onOpenObject, onEditObject, onCopyObjectId, onCreateObject, title: titleProp, subtitle: subtitleProp, newObjectLabel: newObjectLabelProp, searchPlaceholder: searchPlaceholderProp, filterLabel: filterLabelProp, objectColumnLabel: objectColumnLabelProp, typeColumnLabel: typeColumnLabelProp, recordsColumnLabel: recordsColumnLabelProp, attributesColumnLabel: attributesColumnLabelProp, noRecordsLabel: noRecordsLabelProp, editLabel: editLabelProp, copyIdLabel: copyIdLabelProp, rowMenuLabel, emptyLabel, className, ...rest }) {
|
|
69
|
+
const title = (0, core_1.useCrispMessage)(titleProp, "Objects");
|
|
70
|
+
const subtitle = (0, core_1.useCrispMessage)(subtitleProp, "Modify and add objects in your workspace");
|
|
71
|
+
const newObjectLabel = (0, core_1.useCrispMessage)(newObjectLabelProp, "New custom object");
|
|
72
|
+
const searchPlaceholder = (0, core_1.useCrispMessage)(searchPlaceholderProp, "Search objects");
|
|
73
|
+
const filterLabel = (0, core_1.useCrispMessage)(filterLabelProp, "Filter");
|
|
74
|
+
const objectColumnLabel = (0, core_1.useCrispMessage)(objectColumnLabelProp, "Object");
|
|
75
|
+
const typeColumnLabel = (0, core_1.useCrispMessage)(typeColumnLabelProp, "Type");
|
|
76
|
+
const recordsColumnLabel = (0, core_1.useCrispMessage)(recordsColumnLabelProp, "Records");
|
|
77
|
+
const attributesColumnLabel = (0, core_1.useCrispMessage)(attributesColumnLabelProp, "Attributes");
|
|
78
|
+
const noRecordsLabel = (0, core_1.useCrispMessage)(noRecordsLabelProp, "No records");
|
|
79
|
+
const editLabel = (0, core_1.useCrispMessage)(editLabelProp, "Edit");
|
|
80
|
+
const copyIdLabel = (0, core_1.useCrispMessage)(copyIdLabelProp, "Copy ID");
|
|
81
|
+
const [query, setQuery] = React.useState("");
|
|
82
|
+
const filtered = React.useMemo(() => {
|
|
83
|
+
if (!query.trim())
|
|
84
|
+
return objects;
|
|
85
|
+
const q = query.trim().toLowerCase();
|
|
86
|
+
return objects.filter((o) => String(o.name ?? "")
|
|
87
|
+
.toLowerCase()
|
|
88
|
+
.includes(q));
|
|
89
|
+
}, [objects, query]);
|
|
90
|
+
const columns = React.useMemo(() => [
|
|
91
|
+
{
|
|
92
|
+
key: "name",
|
|
93
|
+
header: objectColumnLabel,
|
|
94
|
+
render: (_v, row) => ((0, jsx_runtime_1.jsxs)("span", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [(0, jsx_runtime_1.jsx)("span", { className: "crisp-objectsettings-icon", style: { background: row.iconColor ?? "currentColor" }, children: row.icon }), row.name] })),
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
key: "typeLabel",
|
|
98
|
+
header: typeColumnLabel,
|
|
99
|
+
render: (_v, row) => ((0, jsx_runtime_1.jsx)("span", { className: "crisp-objectsettings-typebadge", children: row.typeLabel })),
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
key: "recordCount",
|
|
103
|
+
header: recordsColumnLabel,
|
|
104
|
+
align: "left",
|
|
105
|
+
render: (_v, row) => row.recordCount ? (row.recordCount) : ((0, jsx_runtime_1.jsx)("span", { className: "crisp-objectsettings-muted", children: noRecordsLabel })),
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
key: "attributeCount",
|
|
109
|
+
header: attributesColumnLabel,
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
key: "actions",
|
|
113
|
+
header: "",
|
|
114
|
+
align: "right",
|
|
115
|
+
render: (_v, row) => ((0, jsx_runtime_1.jsxs)(menu_1.Menu, { align: "end", trigger: (0, jsx_runtime_1.jsx)("button", { type: "button", "aria-label": rowMenuLabel?.(row) ?? `${row.name}`, style: {
|
|
116
|
+
display: "inline-flex",
|
|
117
|
+
border: "none",
|
|
118
|
+
background: "none",
|
|
119
|
+
cursor: "pointer",
|
|
120
|
+
color: "inherit",
|
|
121
|
+
padding: 4,
|
|
122
|
+
}, children: (0, jsx_runtime_1.jsx)(lucide_react_1.MoreHorizontal, { size: 16 }) }), children: [(0, jsx_runtime_1.jsx)(menu_1.MenuItem, { icon: (0, jsx_runtime_1.jsx)(lucide_react_1.Pencil, { size: 14 }), onSelect: () => onEditObject?.(row.id), children: editLabel }), (0, jsx_runtime_1.jsx)(menu_1.MenuItem, { onSelect: () => onCopyObjectId?.(row.id), children: copyIdLabel })] })),
|
|
123
|
+
},
|
|
124
|
+
], [
|
|
125
|
+
objectColumnLabel,
|
|
126
|
+
typeColumnLabel,
|
|
127
|
+
recordsColumnLabel,
|
|
128
|
+
attributesColumnLabel,
|
|
129
|
+
noRecordsLabel,
|
|
130
|
+
editLabel,
|
|
131
|
+
copyIdLabel,
|
|
132
|
+
onEditObject,
|
|
133
|
+
onCopyObjectId,
|
|
134
|
+
rowMenuLabel,
|
|
135
|
+
]);
|
|
136
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: (0, objectsettings_recipe_1.objectsettingsRecipe)({ className }), ...rest, children: (0, jsx_runtime_1.jsxs)("div", { className: "crisp-objectsettings-page", children: [(0, jsx_runtime_1.jsxs)("div", { className: "crisp-objectsettings-head", children: [(0, jsx_runtime_1.jsxs)("div", { className: "crisp-objectsettings-heading", children: [(0, jsx_runtime_1.jsx)("h1", { className: "crisp-objectsettings-title", children: title }), (0, jsx_runtime_1.jsx)("p", { className: "crisp-objectsettings-subtitle", children: subtitle })] }), onCreateObject && ((0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-objectsettings-filterbtn", onClick: onCreateObject, children: newObjectLabel }))] }), (0, jsx_runtime_1.jsxs)("div", { className: "crisp-objectsettings-toolbar", children: [(0, jsx_runtime_1.jsxs)("span", { className: "crisp-objectsettings-search", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Search, { size: 15, strokeWidth: 1.75, "aria-hidden": "true" }), (0, jsx_runtime_1.jsx)("input", { type: "search", placeholder: searchPlaceholder, value: query, onChange: (e) => setQuery(e.target.value), "aria-label": searchPlaceholder })] }), (0, jsx_runtime_1.jsxs)("button", { type: "button", className: "crisp-objectsettings-filterbtn", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.SlidersHorizontal, { size: 14 }), filterLabel] })] }), (0, jsx_runtime_1.jsx)(datatable_1.DataTable, { columns: columns, data: filtered, onRowClick: onOpenObject ? (row) => onOpenObject(row.id) : undefined, empty: emptyLabel })] }) }));
|
|
137
|
+
}
|
|
138
|
+
ObjectsList.displayName = "ObjectsList";
|
|
139
|
+
/**
|
|
140
|
+
* Settings → Objects → an object (crisp#637, P-123, verified live against
|
|
141
|
+
* .../objects/companies/general): 48px icon tile + name + type badge, a
|
|
142
|
+
* description line, then a Configuration/Attributes tab bar (Attio's own
|
|
143
|
+
* tab bar also carries Permissions/Templates/Notifications/Imports — out of
|
|
144
|
+
* scope here, see #613/#638; only the two rows this task covers are built).
|
|
145
|
+
* Tab content is a render prop, the same shape crisp's own `Tabs` already
|
|
146
|
+
* takes, so a consumer composes `ObjectGeneralSection`/
|
|
147
|
+
* `ObjectAppearanceSection` and `AttributesTable` underneath.
|
|
148
|
+
*/
|
|
149
|
+
function ObjectDetailPage({ icon, iconColor, name, typeLabel, description, tab, defaultTab = "configuration", onTabChange, attributeCount, configurationLabel: configurationLabelProp, attributesLabel: attributesLabelProp, children, }) {
|
|
150
|
+
const configurationLabel = (0, core_1.useCrispMessage)(configurationLabelProp, "Configuration");
|
|
151
|
+
const attributesLabel = (0, core_1.useCrispMessage)(attributesLabelProp, "Attributes");
|
|
152
|
+
const [internalTab, setInternalTab] = React.useState(defaultTab);
|
|
153
|
+
const active = tab ?? internalTab;
|
|
154
|
+
const setTab = (v) => {
|
|
155
|
+
const next = v;
|
|
156
|
+
if (tab === undefined)
|
|
157
|
+
setInternalTab(next);
|
|
158
|
+
onTabChange?.(next);
|
|
159
|
+
};
|
|
160
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: (0, objectsettings_recipe_1.objectsettingsRecipe)(), children: (0, jsx_runtime_1.jsxs)("div", { className: "crisp-objectsettings-page", children: [(0, jsx_runtime_1.jsx)("div", { className: "crisp-objectsettings-head", children: (0, jsx_runtime_1.jsxs)("div", { style: { display: "flex", alignItems: "flex-start", gap: 12 }, children: [(0, jsx_runtime_1.jsx)("span", { className: "crisp-objectsettings-icon crisp-objectsettings-icon--lg", style: { background: iconColor ?? "currentColor" }, children: icon }), (0, jsx_runtime_1.jsxs)("div", { className: "crisp-objectsettings-heading", children: [(0, jsx_runtime_1.jsxs)("h1", { className: "crisp-objectsettings-title", children: [name, typeLabel != null && ((0, jsx_runtime_1.jsx)(badge_1.Badge, { tone: "neutral", size: "small", children: typeLabel }))] }), description != null && ((0, jsx_runtime_1.jsx)("p", { className: "crisp-objectsettings-subtitle", children: description }))] })] }) }), (0, jsx_runtime_1.jsx)(tabs_1.Tabs, { value: active, onValueChange: setTab, tabs: [
|
|
161
|
+
{ value: "configuration", label: configurationLabel },
|
|
162
|
+
{
|
|
163
|
+
value: "attributes",
|
|
164
|
+
label: attributesLabel,
|
|
165
|
+
badge: attributeCount,
|
|
166
|
+
},
|
|
167
|
+
], children: (v) => children(v) })] }) }));
|
|
168
|
+
}
|
|
169
|
+
ObjectDetailPage.displayName = "ObjectDetailPage";
|
|
170
|
+
/** The Configuration tab's "General" section (crisp#637, P-123): plural/
|
|
171
|
+
* singular noun inputs side by side, then a slug input Attio locks for
|
|
172
|
+
* system objects. Composes `SettingsSection` + `FormField`, no new chrome. */
|
|
173
|
+
function ObjectGeneralSection({ pluralNoun, onPluralNounChange, singularNoun, onSingularNounChange, slug, slugEditable = false, onSlugChange, titleLabel: titleLabelProp, descriptionLabel: descriptionLabelProp, pluralLabel: pluralLabelProp, singularLabel: singularLabelProp, slugLabel: slugLabelProp, slugHelpLabel: slugHelpLabelProp, }) {
|
|
174
|
+
const titleLabel = (0, core_1.useCrispMessage)(titleLabelProp, "General");
|
|
175
|
+
const descriptionLabel = (0, core_1.useCrispMessage)(descriptionLabelProp, "Set words to describe a single and multiple objects of this type");
|
|
176
|
+
const pluralLabel = (0, core_1.useCrispMessage)(pluralLabelProp, "Plural noun");
|
|
177
|
+
const singularLabel = (0, core_1.useCrispMessage)(singularLabelProp, "Singular noun");
|
|
178
|
+
const slugLabel = (0, core_1.useCrispMessage)(slugLabelProp, "Slug");
|
|
179
|
+
const slugHelpLabel = (0, core_1.useCrispMessage)(slugHelpLabelProp, "You can't change the slug of a system object");
|
|
180
|
+
const pluralId = React.useId();
|
|
181
|
+
const singularId = React.useId();
|
|
182
|
+
const slugId = React.useId();
|
|
183
|
+
return ((0, jsx_runtime_1.jsxs)(settingssection_1.SettingsSection, { title: titleLabel, description: descriptionLabel, children: [(0, jsx_runtime_1.jsxs)("div", { className: "crisp-objectsettings-fieldrow", children: [(0, jsx_runtime_1.jsx)(formfield_1.FormField, { label: pluralLabel, htmlFor: pluralId, children: (0, jsx_runtime_1.jsx)(input_1.Input, { id: pluralId, value: pluralNoun, onChange: (e) => onPluralNounChange?.(e.target.value) }) }), (0, jsx_runtime_1.jsx)(formfield_1.FormField, { label: singularLabel, htmlFor: singularId, children: (0, jsx_runtime_1.jsx)(input_1.Input, { id: singularId, value: singularNoun, onChange: (e) => onSingularNounChange?.(e.target.value) }) })] }), (0, jsx_runtime_1.jsx)(formfield_1.FormField, { label: slugLabel, htmlFor: slugId, description: !slugEditable ? slugHelpLabel : undefined, children: (0, jsx_runtime_1.jsx)(input_1.Input, { id: slugId, value: slug, disabled: !slugEditable, onChange: (e) => onSlugChange?.(e.target.value) }) })] }));
|
|
184
|
+
}
|
|
185
|
+
ObjectGeneralSection.displayName = "ObjectGeneralSection";
|
|
186
|
+
/** The Configuration tab's "Appearance" section (crisp#637, P-123): a single
|
|
187
|
+
* "Record page layout" row with a "Configure record page" button — the
|
|
188
|
+
* target flow itself (crisp's own record-page-layout editor) is a separate,
|
|
189
|
+
* unbuilt surface; this only renders the row and its click. */
|
|
190
|
+
function ObjectAppearanceSection({ onConfigureRecordPage, titleLabel: titleLabelProp, rowLabel: rowLabelProp, rowDescriptionLabel: rowDescriptionLabelProp, actionLabel: actionLabelProp, }) {
|
|
191
|
+
const titleLabel = (0, core_1.useCrispMessage)(titleLabelProp, "Appearance");
|
|
192
|
+
const rowLabel = (0, core_1.useCrispMessage)(rowLabelProp, "Record page layout");
|
|
193
|
+
const rowDescriptionLabel = (0, core_1.useCrispMessage)(rowDescriptionLabelProp, "Configure tabs, widgets, and layout for this object's record pages");
|
|
194
|
+
const actionLabel = (0, core_1.useCrispMessage)(actionLabelProp, "Configure record page");
|
|
195
|
+
return ((0, jsx_runtime_1.jsx)(settingssection_1.SettingsSection, { title: titleLabel, children: (0, jsx_runtime_1.jsxs)("div", { className: "crisp-objectsettings-fieldrow", style: { alignItems: "center" }, children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("div", { style: { fontWeight: 500 }, children: rowLabel }), (0, jsx_runtime_1.jsx)("p", { className: "crisp-objectsettings-subtitle", children: rowDescriptionLabel })] }), onConfigureRecordPage && ((0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-objectsettings-filterbtn", style: { justifySelf: "end" }, onClick: onConfigureRecordPage, children: actionLabel }))] }) }));
|
|
196
|
+
}
|
|
197
|
+
ObjectAppearanceSection.displayName = "ObjectAppearanceSection";
|
|
198
|
+
/**
|
|
199
|
+
* The Attributes tab's table (crisp#637, P-124, verified live): H2 +
|
|
200
|
+
* "Create attribute", a search+Filter row, then a DataTable of every
|
|
201
|
+
* attribute — drag handle (via `DataTable`'s own `onRowsReorder`) + type
|
|
202
|
+
* icon + name, Data type, a constraint label, and System (grey)/Enriched
|
|
203
|
+
* (purple) property badges. Row height is DataTable's own 36px; Attio's
|
|
204
|
+
* measures 40px here — a real, small divergence left to DataTable's owner
|
|
205
|
+
* rather than patched locally (see the PR).
|
|
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, }) {
|
|
208
|
+
const title = (0, core_1.useCrispMessage)(titleProp, "Attributes");
|
|
209
|
+
const subtitle = (0, core_1.useCrispMessage)(subtitleProp, "Modify and add object attributes");
|
|
210
|
+
const createLabel = (0, core_1.useCrispMessage)(createLabelProp, "Create attribute");
|
|
211
|
+
const searchPlaceholder = (0, core_1.useCrispMessage)(searchPlaceholderProp, "Search attributes");
|
|
212
|
+
const filterLabel = (0, core_1.useCrispMessage)(filterLabelProp, "Filter");
|
|
213
|
+
const nameColumnLabel = (0, core_1.useCrispMessage)(nameColumnLabelProp, "Name");
|
|
214
|
+
const dataTypeColumnLabel = (0, core_1.useCrispMessage)(dataTypeColumnLabelProp, "Data type");
|
|
215
|
+
const constraintsColumnLabel = (0, core_1.useCrispMessage)(constraintsColumnLabelProp, "Constraints");
|
|
216
|
+
const propertiesColumnLabel = (0, core_1.useCrispMessage)(propertiesColumnLabelProp, "Properties");
|
|
217
|
+
const systemLabel = (0, core_1.useCrispMessage)(systemLabelProp, "System");
|
|
218
|
+
const enrichedLabel = (0, core_1.useCrispMessage)(enrichedLabelProp, "Enriched");
|
|
219
|
+
const editLabel = (0, core_1.useCrispMessage)(editLabelProp, "Edit");
|
|
220
|
+
const archiveLabel = (0, core_1.useCrispMessage)(archiveLabelProp, "Archive");
|
|
221
|
+
const [query, setQuery] = React.useState("");
|
|
222
|
+
const filtered = React.useMemo(() => {
|
|
223
|
+
if (!query.trim())
|
|
224
|
+
return attributes;
|
|
225
|
+
const q = query.trim().toLowerCase();
|
|
226
|
+
return attributes.filter((a) => String(a.name ?? "")
|
|
227
|
+
.toLowerCase()
|
|
228
|
+
.includes(q));
|
|
229
|
+
}, [attributes, query]);
|
|
230
|
+
const columns = React.useMemo(() => [
|
|
231
|
+
{
|
|
232
|
+
key: "name",
|
|
233
|
+
header: nameColumnLabel,
|
|
234
|
+
render: (_v, row) => ((0, jsx_runtime_1.jsxs)("span", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [(0, jsx_runtime_1.jsx)("span", { style: {
|
|
235
|
+
display: "inline-flex",
|
|
236
|
+
color: "var(--crisp-fg-muted, inherit)",
|
|
237
|
+
}, children: row.icon }), row.name] })),
|
|
238
|
+
},
|
|
239
|
+
{ key: "dataType", header: dataTypeColumnLabel },
|
|
240
|
+
{
|
|
241
|
+
key: "constraint",
|
|
242
|
+
header: constraintsColumnLabel,
|
|
243
|
+
render: (_v, row) => row.constraint ?? null,
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
key: "properties",
|
|
247
|
+
header: propertiesColumnLabel,
|
|
248
|
+
render: (_v, row) => ((0, jsx_runtime_1.jsxs)("span", { style: { display: "flex", gap: 6 }, children: [row.system && ((0, jsx_runtime_1.jsx)(badge_1.Badge, { tone: "neutral", size: "small", children: systemLabel })), row.enriched && ((0, jsx_runtime_1.jsx)(badge_1.Badge, { tone: "purple", size: "small", children: enrichedLabel }))] })),
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
key: "actions",
|
|
252
|
+
header: "",
|
|
253
|
+
align: "right",
|
|
254
|
+
render: (_v, row) => ((0, jsx_runtime_1.jsx)(menu_1.Menu, { align: "end", trigger: (0, jsx_runtime_1.jsx)("button", { type: "button", "aria-label": `${row.name}`, style: {
|
|
255
|
+
display: "inline-flex",
|
|
256
|
+
border: "none",
|
|
257
|
+
background: "none",
|
|
258
|
+
cursor: "pointer",
|
|
259
|
+
color: "inherit",
|
|
260
|
+
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 })] })) })),
|
|
262
|
+
},
|
|
263
|
+
], [
|
|
264
|
+
nameColumnLabel,
|
|
265
|
+
dataTypeColumnLabel,
|
|
266
|
+
constraintsColumnLabel,
|
|
267
|
+
propertiesColumnLabel,
|
|
268
|
+
systemLabel,
|
|
269
|
+
enrichedLabel,
|
|
270
|
+
editLabel,
|
|
271
|
+
archiveLabel,
|
|
272
|
+
onEditAttribute,
|
|
273
|
+
onArchiveAttribute,
|
|
274
|
+
rowActions,
|
|
275
|
+
]);
|
|
276
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: (0, objectsettings_recipe_1.objectsettingsRecipe)(), children: (0, jsx_runtime_1.jsxs)("div", { className: "crisp-objectsettings-page", children: [(0, jsx_runtime_1.jsxs)("div", { className: "crisp-objectsettings-head", children: [(0, jsx_runtime_1.jsxs)("div", { className: "crisp-objectsettings-heading", children: [(0, jsx_runtime_1.jsx)("h2", { className: "crisp-objectsettings-title", style: { fontSize: 16, lineHeight: "20px" }, children: title }), (0, jsx_runtime_1.jsx)("p", { className: "crisp-objectsettings-subtitle", children: subtitle })] }), onCreateAttribute && ((0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-objectsettings-filterbtn", onClick: onCreateAttribute, children: createLabel }))] }), (0, jsx_runtime_1.jsxs)("div", { className: "crisp-objectsettings-toolbar", children: [(0, jsx_runtime_1.jsxs)("span", { className: "crisp-objectsettings-search", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Search, { size: 15, strokeWidth: 1.75, "aria-hidden": "true" }), (0, jsx_runtime_1.jsx)("input", { type: "search", placeholder: searchPlaceholder, value: query, onChange: (e) => setQuery(e.target.value), "aria-label": searchPlaceholder })] }), (0, jsx_runtime_1.jsxs)("button", { type: "button", className: "crisp-objectsettings-filterbtn", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.SlidersHorizontal, { size: 14 }), filterLabel] })] }), (0, jsx_runtime_1.jsx)(datatable_1.DataTable, { columns: columns, data: filtered, onRowClick: onOpenAttribute ? (row) => onOpenAttribute(row.id) : undefined, onRowsReorder: onReorder, empty: emptyLabel })] }) }));
|
|
277
|
+
}
|
|
278
|
+
AttributesTable.displayName = "AttributesTable";
|
|
279
|
+
/** Attio's own "Type" group, verbatim (crisp#637, P-126) — icons are
|
|
280
|
+
* crisp's Lucide choices, not Attio's proprietary set (golden rule 4). */
|
|
281
|
+
exports.ATTRIBUTE_TYPE_OPTIONS = [
|
|
282
|
+
{ value: "checkbox", label: "Checkbox", icon: lucide_react_1.CheckSquare },
|
|
283
|
+
{ value: "currency", label: "Currency", icon: lucide_react_1.DollarSign },
|
|
284
|
+
{ value: "date", label: "Date", icon: lucide_react_1.Calendar },
|
|
285
|
+
{ value: "location", label: "Location", icon: lucide_react_1.MapPin },
|
|
286
|
+
{ value: "multiselect", label: "Multi-select", icon: lucide_react_1.ListChecks },
|
|
287
|
+
{ value: "number", label: "Number", icon: lucide_react_1.Hash },
|
|
288
|
+
{ value: "phone", label: "Phone Number", icon: lucide_react_1.Phone },
|
|
289
|
+
{ value: "rating", label: "Rating", icon: lucide_react_1.Star },
|
|
290
|
+
{ value: "record", label: "Record", icon: lucide_react_1.FileText },
|
|
291
|
+
{ value: "relationship", label: "Relationship", icon: lucide_react_1.Link2, badge: "Pro" },
|
|
292
|
+
{ value: "select", label: "Select", icon: lucide_react_1.ListChecks },
|
|
293
|
+
{ value: "status", label: "Status", icon: lucide_react_1.Flag },
|
|
294
|
+
{ value: "text", label: "Text", icon: lucide_react_1.Type },
|
|
295
|
+
{ value: "timestamp", label: "Timestamp", icon: lucide_react_1.Clock },
|
|
296
|
+
{ value: "user", label: "User", icon: lucide_react_1.User },
|
|
297
|
+
];
|
|
298
|
+
/** Attio's own "AI Autofill" group, verbatim (crisp#637, P-126) — opt in via
|
|
299
|
+
* `AttributeTypeSelect`'s `aiAutofillOptions`; omitted by default, since a
|
|
300
|
+
* consumer with no autofill backend of its own (GRC) has nothing to wire
|
|
301
|
+
* these to. */
|
|
302
|
+
exports.ATTRIBUTE_AI_AUTOFILL_OPTIONS = [
|
|
303
|
+
{ value: "classify-record", label: "Classify record", icon: lucide_react_1.Sparkles },
|
|
304
|
+
{ value: "prompt-completion", label: "Prompt completion", icon: lucide_react_1.Sparkles },
|
|
305
|
+
{ value: "summarize-record", label: "Summarize record", icon: lucide_react_1.Sparkles },
|
|
306
|
+
{ value: "web-agent", label: "Web agent", icon: lucide_react_1.Sparkles },
|
|
307
|
+
];
|
|
308
|
+
function toSelectOption(o, group) {
|
|
309
|
+
const Icon = o.icon;
|
|
310
|
+
return {
|
|
311
|
+
value: o.value,
|
|
312
|
+
keywords: o.label,
|
|
313
|
+
group,
|
|
314
|
+
label: ((0, jsx_runtime_1.jsxs)("span", { className: "crisp-objectsettings-typeoption", children: [(0, jsx_runtime_1.jsx)(Icon, { size: 14 }), (0, jsx_runtime_1.jsx)("span", { className: "crisp-objectsettings-typeoption-label", children: o.label }), o.badge != null && ((0, jsx_runtime_1.jsx)("span", { className: "crisp-objectsettings-typeoption-badge", children: o.badge }))] })),
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
/** The "Attribute Type" dropdown (crisp#637, P-126, verified live): a
|
|
318
|
+
* searchable, grouped `Select` — reused as-is, not a new picker primitive.
|
|
319
|
+
* Grouping and the per-item trailing "Pro" badge are composed through
|
|
320
|
+
* `Select`'s existing `group` field and rich `label` ReactNode. */
|
|
321
|
+
function AttributeTypeSelect({ value, onValueChange, options = exports.ATTRIBUTE_TYPE_OPTIONS, aiAutofillOptions, aiAutofillGroupLabel: aiAutofillGroupLabelProp, typeGroupLabel: typeGroupLabelProp, searchPlaceholder: searchPlaceholderProp, id, }) {
|
|
322
|
+
const aiAutofillGroupLabel = (0, core_1.useCrispMessage)(aiAutofillGroupLabelProp, "AI Autofill");
|
|
323
|
+
const typeGroupLabel = (0, core_1.useCrispMessage)(typeGroupLabelProp, "Type");
|
|
324
|
+
const searchPlaceholder = (0, core_1.useCrispMessage)(searchPlaceholderProp, "Search attribute types...");
|
|
325
|
+
const selectOptions = React.useMemo(() => {
|
|
326
|
+
const ai = (aiAutofillOptions ?? []).map((o) => toSelectOption(o, aiAutofillGroupLabel));
|
|
327
|
+
const type = options.map((o) => toSelectOption(o, typeGroupLabel));
|
|
328
|
+
return [...ai, ...type];
|
|
329
|
+
}, [aiAutofillOptions, options, aiAutofillGroupLabel, typeGroupLabel]);
|
|
330
|
+
return ((0, jsx_runtime_1.jsx)(select_1.Select, { id: id, options: selectOptions, value: value, onValueChange: onValueChange, searchable: true, searchPlaceholder: searchPlaceholder, width: "full" }));
|
|
331
|
+
}
|
|
332
|
+
AttributeTypeSelect.displayName = "AttributeTypeSelect";
|
|
333
|
+
/**
|
|
334
|
+
* "Create attribute" (crisp#637, P-125, verified live): Attribute Type
|
|
335
|
+
* select (defaults to Text) + Name (autofocused) + Description (optional)
|
|
336
|
+
* + an AI-autofill switch row, footer Cancel/Create. Built on `FormDialog`
|
|
337
|
+
* in its plain (non-`createMode`) shape — Attio's own footer here has a
|
|
338
|
+
* real Cancel button, unlike the record-create dialog `createMode` models
|
|
339
|
+
* (crisp#548), so `createMode` would be the wrong fit even though the
|
|
340
|
+
* button carries the same ⌘↵ hint; that hint is rendered locally instead.
|
|
341
|
+
*/
|
|
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, }) {
|
|
343
|
+
const title = (0, core_1.useCrispMessage)(titleProp, "Create attribute");
|
|
344
|
+
const attributeTypeLabel = (0, core_1.useCrispMessage)(attributeTypeLabelProp, "Attribute Type");
|
|
345
|
+
const nameLabel = (0, core_1.useCrispMessage)(nameLabelProp, "Name");
|
|
346
|
+
const descriptionLabel = (0, core_1.useCrispMessage)(descriptionLabelProp, "Description");
|
|
347
|
+
const descriptionPlaceholder = (0, core_1.useCrispMessage)(descriptionPlaceholderProp, "Add a description for this attribute");
|
|
348
|
+
const aiAutofillLabel = (0, core_1.useCrispMessage)(aiAutofillLabelProp, "AI autofill");
|
|
349
|
+
const setUpAiAutofillLabel = (0, core_1.useCrispMessage)(setUpAiAutofillLabelProp, "Set up AI autofill");
|
|
350
|
+
const createLabel = (0, core_1.useCrispMessage)(createLabelProp, "Create attribute");
|
|
351
|
+
const switchId = React.useId();
|
|
352
|
+
const nameId = React.useId();
|
|
353
|
+
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: onTypeChange, 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, { 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) }) }), (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
|
+
}
|
|
355
|
+
CreateAttributeDialog.displayName = "CreateAttributeDialog";
|
|
356
|
+
/**
|
|
357
|
+
* This folder holds several components (`ObjectsList`, `ObjectDetailPage`,
|
|
358
|
+
* `AttributesTable`, `CreateAttributeDialog`, …), not one named after the
|
|
359
|
+
* slug — unlike every other product dir, whose docs `<ManualInstallation>`
|
|
360
|
+
* resolves an import binding from the folder slug ("avatargroup" →
|
|
361
|
+
* `AvatarGroup`) by reading this file's exports. This alias exists only so
|
|
362
|
+
* that resolver finds a match for "objectsettings"; consumers should import
|
|
363
|
+
* the real names above, never this one.
|
|
364
|
+
*/
|
|
365
|
+
exports.ObjectSettings = ObjectsList;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.objectsettingsRecipe = void 0;
|
|
4
|
+
const core_1 = require("../../core");
|
|
5
|
+
const tokens_1 = require("../../tokens");
|
|
6
|
+
// Measured live against app.attio.com/<workspace>/settings/data/objects and
|
|
7
|
+
// .../objects/companies/{general,attributes} (crisp#637, P-122/P-123/P-124/
|
|
8
|
+
// P-125/P-126). Values with no matching scale token are one-off literals,
|
|
9
|
+
// same precedent as authshell's own 20px title (crisp#637 golden rule 3):
|
|
10
|
+
// the 24/600/28/-0.48 page title, the 12px/16px badge dot sizes, and the
|
|
11
|
+
// 30% squircle radius on the object-icon tile.
|
|
12
|
+
const config = (0, core_1.defineRecipe)({
|
|
13
|
+
name: "objectsettings",
|
|
14
|
+
base: `& .crisp-objectsettings-page { display: flex; flex-direction: column; gap: ${tokens_1.contract.space["6"]}; }
|
|
15
|
+
& .crisp-objectsettings-head { display: flex; align-items: flex-start; justify-content: space-between; gap: ${tokens_1.contract.space["4"]}; }
|
|
16
|
+
& .crisp-objectsettings-heading { display: flex; flex-direction: column; gap: ${tokens_1.contract.space["0.5"]}; min-width: 0; }
|
|
17
|
+
& .crisp-objectsettings-title { display: flex; align-items: center; gap: ${tokens_1.contract.space["2"]}; font-size: 24px; line-height: 28px; font-weight: ${tokens_1.contract.weight.semibold}; letter-spacing: -0.48px; color: ${tokens_1.contract.fgPrimary}; margin: 0; }
|
|
18
|
+
& .crisp-objectsettings-subtitle { font-size: ${tokens_1.contract.text.lg}; line-height: 20px; font-weight: ${tokens_1.contract.weight.medium}; color: ${tokens_1.contract.fgMuted}; margin: 0; }
|
|
19
|
+
|
|
20
|
+
& .crisp-objectsettings-toolbar { display: flex; align-items: center; gap: ${tokens_1.contract.space["2"]}; }
|
|
21
|
+
& .crisp-objectsettings-search { display: flex; align-items: center; flex: 1; gap: ${tokens_1.contract.space["1.5"]}; height: ${tokens_1.contract.size.menuRow}; padding: 0 ${tokens_1.contract.space["2.5"]}; border-radius: ${tokens_1.contract.radius.md}; background: ${tokens_1.contract.bgSurface}; box-shadow: inset 0 0 0 1px ${tokens_1.contract.borderDefault}; color: ${tokens_1.contract.fgMuted}; transition: box-shadow .15s; }
|
|
22
|
+
& .crisp-objectsettings-search:focus-within { box-shadow: inset 0 0 0 1px ${tokens_1.contract.bgBrandSolid}; }
|
|
23
|
+
& .crisp-objectsettings-search input { flex: 1; min-width: 0; border: none; outline: none; background: none; font-family: inherit; font-size: ${tokens_1.contract.text.lg}; color: ${tokens_1.contract.fgPrimary}; }
|
|
24
|
+
& .crisp-objectsettings-search input::placeholder { color: ${tokens_1.contract.fgMuted}; }
|
|
25
|
+
& .crisp-objectsettings-filterbtn { display: inline-flex; align-items: center; gap: ${tokens_1.contract.space["1.5"]}; height: ${tokens_1.contract.size.menuRow}; padding: 0 ${tokens_1.contract.space["2.5"]} 0 ${tokens_1.contract.space["2"]}; border: none; border-radius: ${tokens_1.contract.radius.lg}; background: ${tokens_1.contract.bgSurface}; box-shadow: inset 0 0 0 1px ${tokens_1.contract.borderDefault}; color: ${tokens_1.contract.fgPrimary}; font-family: inherit; font-size: ${tokens_1.contract.text.lg}; font-weight: ${tokens_1.contract.weight.medium}; cursor: pointer; white-space: nowrap; }
|
|
26
|
+
& .crisp-objectsettings-filterbtn:hover { background: ${tokens_1.contract.bgRaised}; }
|
|
27
|
+
|
|
28
|
+
/* Object / attribute icon tile — Attio's own squircle (measured
|
|
29
|
+
border-radius: 30%, not a scale token: it's a proportional radius
|
|
30
|
+
tied to the tile's own size, unlike every other radius crisp uses,
|
|
31
|
+
which is a fixed px). */
|
|
32
|
+
& .crisp-objectsettings-icon { display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; width: 16px; height: 16px; border-radius: 30%; color: ${tokens_1.contract.fgOnBrand}; }
|
|
33
|
+
& .crisp-objectsettings-icon--lg { width: 48px; height: 48px; border-radius: 16px; }
|
|
34
|
+
|
|
35
|
+
/* "Standard"/"Custom" type badge and the muted "No records" cell —
|
|
36
|
+
plain text, not a Badge pill (measured: transparent background). */
|
|
37
|
+
& .crisp-objectsettings-typebadge { font-size: ${tokens_1.contract.text.sm}; font-weight: ${tokens_1.contract.weight.medium}; color: ${tokens_1.contract.fgNeutralSoft}; }
|
|
38
|
+
& .crisp-objectsettings-muted { color: ${tokens_1.contract.fgMuted}; }
|
|
39
|
+
|
|
40
|
+
/* Attribute-type option row (icon + label + optional trailing badge),
|
|
41
|
+
composed as a Select option's \`label\` ReactNode. */
|
|
42
|
+
& .crisp-objectsettings-typeoption { display: flex; align-items: center; gap: ${tokens_1.contract.space["2"]}; width: 100%; min-width: 0; }
|
|
43
|
+
& .crisp-objectsettings-typeoption-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
|
|
44
|
+
& .crisp-objectsettings-typeoption-badge { flex-shrink: 0; font-size: ${tokens_1.contract.text.sm}; font-weight: ${tokens_1.contract.weight.medium}; color: ${tokens_1.contract.fgBrandSoft}; }
|
|
45
|
+
|
|
46
|
+
/* Create-attribute dialog's AI-autofill row and the submit button's
|
|
47
|
+
⌘↵ hint (measured off the same dialog, same shape as Dialog's own
|
|
48
|
+
.crisp-dialog-submit-key, kept local rather than reaching into
|
|
49
|
+
another component's scoped classes). */
|
|
50
|
+
& .crisp-objectsettings-aiautofill { display: flex; flex-direction: column; gap: ${tokens_1.contract.space["1.5"]}; }
|
|
51
|
+
& .crisp-objectsettings-aiautofill-label { font-size: ${tokens_1.contract.text.md}; font-weight: ${tokens_1.contract.weight.medium}; color: ${tokens_1.contract.fgMuted}; }
|
|
52
|
+
& .crisp-objectsettings-aiautofill-row { display: flex; align-items: center; gap: ${tokens_1.contract.space["2"]}; height: ${tokens_1.contract.size.menuRow}; padding: 0 ${tokens_1.contract.space["2.5"]}; border-radius: ${tokens_1.contract.radius.md}; box-shadow: inset 0 0 0 1px ${tokens_1.contract.borderDefault}; font-size: ${tokens_1.contract.text.lg}; color: ${tokens_1.contract.fgPrimary}; cursor: pointer; }
|
|
53
|
+
& .crisp-objectsettings-aiautofill-row > svg { flex-shrink: 0; color: ${tokens_1.contract.fgMuted}; }
|
|
54
|
+
& .crisp-objectsettings-aiautofill-row > span { flex: 1; }
|
|
55
|
+
& .crisp-objectsettings-kbd { display: inline-flex; align-items: center; gap: ${tokens_1.contract.space["1"]}; margin-inline-start: ${tokens_1.contract.space["1"]}; }
|
|
56
|
+
& .crisp-objectsettings-kbd kbd { box-sizing: border-box; display: inline-flex; align-items: center; justify-content: center; width: 16px; height: 16px; padding: 0 3px; border-radius: 4px; background: transparent; color: inherit; font-family: inherit; font-style: normal; font-size: 10px; }
|
|
57
|
+
|
|
58
|
+
& .crisp-objectsettings-fieldrow { display: grid; grid-template-columns: 1fr 1fr; gap: ${tokens_1.contract.space["4"]}; }
|
|
59
|
+
`,
|
|
60
|
+
variants: {},
|
|
61
|
+
defaultVariants: {},
|
|
62
|
+
compoundVariants: [],
|
|
63
|
+
});
|
|
64
|
+
exports.objectsettingsRecipe = (0, core_1.resolveRecipe)(config);
|
|
65
|
+
exports.default = config;
|
|
@@ -9,6 +9,14 @@ const tokens_1 = require("../../tokens");
|
|
|
9
9
|
// column (transparent, border-right) and a flexible main area whose tab bar
|
|
10
10
|
// (14/500 labels over a sliding ink underline) switches content — Overview shows
|
|
11
11
|
// a bordered Highlights stat-grid and an Activity feed.
|
|
12
|
+
//
|
|
13
|
+
// `.crisp-recordpage-panel`'s own `padding-top` (c.space["4"], 16px) was
|
|
14
|
+
// dropped (your-moon/crisp#588): this panel is only ever rendered as Tabs'
|
|
15
|
+
// own children (every branch of `builtinPanel` in recordpage.tsx, all fed to
|
|
16
|
+
// `<Tabs>` below), so it sat inside `.crisp-tabs-panel`, which now carries
|
|
17
|
+
// its own measured 32px top-inset by default — keeping both would have
|
|
18
|
+
// stacked to 48px. Caught by tracing every consumer of `Tabs` before
|
|
19
|
+
// shipping its new default, not by a screenshot.
|
|
12
20
|
const config = (0, core_1.defineRecipe)({
|
|
13
21
|
name: "recordpage",
|
|
14
22
|
base: `
|
|
@@ -128,7 +136,8 @@ const config = (0, core_1.defineRecipe)({
|
|
|
128
136
|
|
|
129
137
|
& .crisp-recordpage-main { flex: 1; min-width: 0; display: flex; flex-direction: column; min-height: 0; overflow-y: auto; padding: ${tokens_1.contract.space["3"]} ${tokens_1.contract.space["5"]}; }
|
|
130
138
|
|
|
131
|
-
|
|
139
|
+
/* no padding-top: Tabs' own panel supplies it now (#588) */
|
|
140
|
+
& .crisp-recordpage-panel { display: flex; flex-direction: column; gap: ${tokens_1.contract.space["4"]}; }
|
|
132
141
|
& .crisp-recordpage-card { border: 1px solid ${tokens_1.contract.borderSubtle}; border-radius: ${tokens_1.contract.radius["2xl"]}; background: ${tokens_1.contract.bgSurface}; overflow: hidden; }
|
|
133
142
|
& .crisp-recordpage-card-title { font-size: ${tokens_1.contract.text.lg}; font-weight: ${tokens_1.contract.weight.semibold}; letter-spacing: -0.14px; color: ${tokens_1.contract.fgPrimary}; padding: ${tokens_1.contract.space["3"]} ${tokens_1.contract.space["3"]} ${tokens_1.contract.space["2"]}; }
|
|
134
143
|
& .crisp-recordpage-hl-card { border: 1px solid ${tokens_1.contract.borderSubtle}; border-radius: ${tokens_1.contract.radius["2xl"]}; overflow: hidden; }
|