@crisp-ui-kit/crisp 0.57.3 → 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 +1 -1
- package/dist/button.layered.css +1 -1
- package/dist/cjs/components/button/button.recipe.js +15 -10
- package/dist/cjs/components/datatable/datatable.js +6 -3
- package/dist/cjs/components/datatable/datatable.recipe.js +35 -2
- package/dist/cjs/components/menu/menu.js +48 -8
- 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/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/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/recordpage.js +64 -5
- package/dist/cjs/product/recordpage/recordpage.recipe.js +38 -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 +40 -7
- package/dist/datatable.layered.css +40 -7
- package/dist/esm/components/button/button.recipe.js +15 -10
- package/dist/esm/components/datatable/datatable.js +6 -3
- package/dist/esm/components/datatable/datatable.recipe.js +35 -2
- package/dist/esm/components/menu/menu.js +47 -7
- 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/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/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/recordpage.js +64 -5
- package/dist/esm/product/recordpage/recordpage.recipe.js +38 -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 +36 -0
- package/dist/recordpage.layered.css +36 -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 +387 -8
- package/dist/styles.layered.css +387 -8
- 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/button/button.recipe.d.ts +2 -3
- package/dist/types/components/datatable/datatable.d.ts +8 -0
- 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/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/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 +96 -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
|
@@ -0,0 +1,127 @@
|
|
|
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.AssigneePicker = AssigneePicker;
|
|
37
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
38
|
+
const lucide_react_1 = require("lucide-react");
|
|
39
|
+
const React = __importStar(require("react"));
|
|
40
|
+
const avatar_1 = require("../../components/avatar");
|
|
41
|
+
const personchip_1 = require("../../components/personchip");
|
|
42
|
+
const select_1 = require("../../components/select");
|
|
43
|
+
const core_1 = require("../../core");
|
|
44
|
+
const assigneepicker_recipe_1 = require("./assigneepicker.recipe");
|
|
45
|
+
// Same five hashed avatar tones as `PersonChip`'s own (transcribed, not
|
|
46
|
+
// imported -- PersonChip's are private to that file, and this is the same
|
|
47
|
+
// one-line pure function `personchip.tsx` itself transcribes from
|
|
48
|
+
// `datatable.tsx`'s `TAG_TONES` rather than sharing an import; see its own
|
|
49
|
+
// comment for why that's fine here).
|
|
50
|
+
const HASH_TONES = ["brand", "orange", "cyan", "purple", "green"];
|
|
51
|
+
function hashTone(name) {
|
|
52
|
+
let h = 0;
|
|
53
|
+
for (let i = 0; i < name.length; i++)
|
|
54
|
+
h = (h * 31 + name.charCodeAt(i)) >>> 0;
|
|
55
|
+
return HASH_TONES[h % HASH_TONES.length];
|
|
56
|
+
}
|
|
57
|
+
function initials(name) {
|
|
58
|
+
const parts = name.trim().split(/\s+/).filter(Boolean);
|
|
59
|
+
if (parts.length === 0)
|
|
60
|
+
return "?";
|
|
61
|
+
return (parts[0][0] + (parts[1]?.[0] ?? "")).toUpperCase();
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* The picker behind a task's "Assigned to" chip -- measured live on
|
|
65
|
+
* app.attio.com's Tasks page ("Edit task" -> "Assigned to You" -> its own
|
|
66
|
+
* sub-popover, `Popover`-sized at the same measured 350px): a "Find a
|
|
67
|
+
* user..." search, the current assignee as a removable pill, then a plain
|
|
68
|
+
* option list, or "No users" when nothing matches. Attio's task assignment
|
|
69
|
+
* is single-user (one pill, replaced by picking another), unlike a
|
|
70
|
+
* multi-value field.
|
|
71
|
+
*/
|
|
72
|
+
function AssigneePicker({ users, assignee = null, onAssigneeChange, query: queryProp, onQueryChange, searchLabel: searchLabelProp, removeLabel: removeLabelProp, emptyLabel: emptyLabelProp, className, ...rest }) {
|
|
73
|
+
const searchLabel = (0, core_1.useCrispMessage)(searchLabelProp, "Find a user...");
|
|
74
|
+
const removeLabel = (0, core_1.useCrispMessage)(removeLabelProp, "Remove assignee");
|
|
75
|
+
// crisp/#471: `emptyLabel` is typed `React.ReactNode`, so it resolves by
|
|
76
|
+
// hand rather than through `useCrispMessage`, which is string-only --
|
|
77
|
+
// same reasoning as `Inbox`'s own `emptyTitle`/`emptySubtitle`.
|
|
78
|
+
const crispMessages = (0, core_1.useCrispMessages)();
|
|
79
|
+
const emptyLabel = emptyLabelProp ?? crispMessages["No users"] ?? "No users";
|
|
80
|
+
const [internalQuery, setInternalQuery] = React.useState("");
|
|
81
|
+
const query = queryProp ?? internalQuery;
|
|
82
|
+
const setQuery = (q) => {
|
|
83
|
+
if (queryProp === undefined)
|
|
84
|
+
setInternalQuery(q);
|
|
85
|
+
onQueryChange?.(q);
|
|
86
|
+
};
|
|
87
|
+
const [highlight, setHighlight] = React.useState(0);
|
|
88
|
+
const autoId = React.useId();
|
|
89
|
+
const listboxId = `${autoId}-listbox`;
|
|
90
|
+
const optionId = (i) => `${autoId}-opt-${i}`;
|
|
91
|
+
const q = query.trim().toLowerCase();
|
|
92
|
+
const candidates = users.filter((u) => u.id !== assignee?.id);
|
|
93
|
+
const filtered = q
|
|
94
|
+
? candidates.filter((u) => u.name.toLowerCase().includes(q))
|
|
95
|
+
: candidates;
|
|
96
|
+
const select = (u) => {
|
|
97
|
+
onAssigneeChange?.(u);
|
|
98
|
+
setQuery("");
|
|
99
|
+
setHighlight(0);
|
|
100
|
+
};
|
|
101
|
+
// crisp's shared popup-listbox model (#198), same as Select/MultiSelect --
|
|
102
|
+
// arrows, Home/End and wrapping, instead of a second hand-rolled one.
|
|
103
|
+
const { onKeyDown } = (0, select_1.useListboxKeyboard)({
|
|
104
|
+
enabledIndexes: filtered.map((_, i) => i),
|
|
105
|
+
highlight,
|
|
106
|
+
setHighlight,
|
|
107
|
+
commit: () => {
|
|
108
|
+
const u = filtered[highlight];
|
|
109
|
+
if (u)
|
|
110
|
+
select(u);
|
|
111
|
+
},
|
|
112
|
+
// Escape/Tab are for the enclosing Popover to handle (it owns open/close
|
|
113
|
+
// here, this picker has no floating state of its own).
|
|
114
|
+
close: () => { },
|
|
115
|
+
dismiss: () => { },
|
|
116
|
+
});
|
|
117
|
+
const activeId = highlight >= 0 && highlight < filtered.length
|
|
118
|
+
? optionId(highlight)
|
|
119
|
+
: undefined;
|
|
120
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: (0, assigneepicker_recipe_1.assigneepickerRecipe)({ className }), ...rest, children: [(0, jsx_runtime_1.jsxs)("div", { className: "crisp-assigneepicker-search", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Search, { size: 16, strokeWidth: 1.75 }), (0, jsx_runtime_1.jsx)("input", { type: "text", className: "crisp-assigneepicker-search-input", placeholder: searchLabel, value: query, onChange: (e) => {
|
|
121
|
+
setQuery(e.target.value);
|
|
122
|
+
setHighlight(0);
|
|
123
|
+
}, onKeyDown: onKeyDown, role: "combobox", "aria-expanded": "true", "aria-controls": listboxId, "aria-activedescendant": activeId,
|
|
124
|
+
// biome-ignore lint/a11y/noAutofocus: focus the search field the moment its enclosing popover opens
|
|
125
|
+
autoFocus: true })] }), assignee != null && ((0, jsx_runtime_1.jsx)("div", { className: "crisp-assigneepicker-pill", children: (0, jsx_runtime_1.jsx)(personchip_1.PersonChip, { name: assignee.name, avatar: assignee.avatar, trailing: (0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-assigneepicker-remove", "aria-label": removeLabel, onClick: () => onAssigneeChange?.(null), children: (0, jsx_runtime_1.jsx)(lucide_react_1.X, { size: 10, strokeWidth: 2 }) }) }) })), filtered.length === 0 ? ((0, jsx_runtime_1.jsx)("div", { className: "crisp-assigneepicker-empty", children: emptyLabel })) : ((0, jsx_runtime_1.jsx)("div", { className: "crisp-assigneepicker-list", role: "listbox", id: listboxId, children: filtered.map((u, i) => ((0, jsx_runtime_1.jsxs)("button", { type: "button", id: optionId(i), role: "option", tabIndex: -1, "aria-selected": i === highlight, "data-highlighted": i === highlight ? "" : undefined, className: "crisp-assigneepicker-option", onMouseEnter: () => setHighlight(i), onClick: () => select(u), children: [(0, jsx_runtime_1.jsx)(avatar_1.Avatar, { size: "sm", shape: "circle", tone: u.avatar ? "neutral" : hashTone(u.name), style: { flexShrink: 0, marginInlineEnd: 8 }, children: u.avatar ? (0, jsx_runtime_1.jsx)("img", { src: u.avatar, alt: "" }) : initials(u.name) }), u.name] }, u.id))) }))] }));
|
|
126
|
+
}
|
|
127
|
+
AssigneePicker.displayName = "AssigneePicker";
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.assigneepickerRecipe = void 0;
|
|
4
|
+
const core_1 = require("../../core");
|
|
5
|
+
const tokens_1 = require("../../tokens");
|
|
6
|
+
// The assignee picker behind a task's "Assigned to" chip -- measured live on
|
|
7
|
+
// app.attio.com's Tasks page (Edit task -> "Assigned to You" -> its own
|
|
8
|
+
// sub-popover): a borderless 14px/500 "Find a user..." search row, the
|
|
9
|
+
// current assignee as a removable pill below it, then a plain option list
|
|
10
|
+
// (32px rows, radius 8, the same measured hover crisp's other listboxes
|
|
11
|
+
// share -- see menu.recipe.ts's own comment) or a centred "No users" line.
|
|
12
|
+
// The popover shell itself (radius/shadow/width) is `Popover`'s own --
|
|
13
|
+
// this is only the 350px-wide content Attio's own picker measured at.
|
|
14
|
+
const config = (0, core_1.defineRecipe)({
|
|
15
|
+
name: "assigneepicker",
|
|
16
|
+
base: `display: flex; flex-direction: column; box-sizing: border-box; width: 350px; font-family: ${tokens_1.contract.font.sans};
|
|
17
|
+
|
|
18
|
+
& .crisp-assigneepicker-search { display: flex; align-items: center; gap: ${tokens_1.contract.space["2"]}; height: 36px; padding: 0 ${tokens_1.contract.space["3"]}; color: ${tokens_1.contract.fgMuted}; }
|
|
19
|
+
& .crisp-assigneepicker-search svg { flex-shrink: 0; }
|
|
20
|
+
& .crisp-assigneepicker-search-input { flex: 1; min-width: 0; border: none; outline: none; background: none; font-family: inherit; font-size: ${tokens_1.contract.text.lg}; font-weight: ${tokens_1.contract.weight.medium}; line-height: 20px; color: ${tokens_1.contract.fgPrimary}; }
|
|
21
|
+
& .crisp-assigneepicker-search-input::placeholder { color: ${tokens_1.contract.fgMuted}; }
|
|
22
|
+
|
|
23
|
+
& .crisp-assigneepicker-pill { display: flex; padding: 0 ${tokens_1.contract.space["3"]} ${tokens_1.contract.space["2"]}; }
|
|
24
|
+
& .crisp-assigneepicker-remove { display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; width: 14px; height: 14px; margin: 0; padding: 0; border: none; background: none; color: inherit; font-family: inherit; cursor: pointer; }
|
|
25
|
+
|
|
26
|
+
& .crisp-assigneepicker-empty { padding: ${tokens_1.contract.space["4"]} ${tokens_1.contract.space["3"]}; text-align: center; font-size: ${tokens_1.contract.text.lg}; font-weight: ${tokens_1.contract.weight.medium}; color: color-mix(in srgb, ${tokens_1.contract.fgPrimary} 63%, transparent); }
|
|
27
|
+
|
|
28
|
+
& .crisp-assigneepicker-list { display: flex; flex-direction: column; margin: 0; padding: ${tokens_1.contract.space["1"]}; list-style: none; }
|
|
29
|
+
& .crisp-assigneepicker-option { display: flex; align-items: center; width: 100%; box-sizing: border-box; height: 32px; padding: 0 ${tokens_1.contract.space["2"]}; border: none; border-radius: ${tokens_1.contract.radius.md}; background: none; font-family: inherit; text-align: start; cursor: pointer; }
|
|
30
|
+
& .crisp-assigneepicker-option:hover, & .crisp-assigneepicker-option[data-highlighted] { background: ${tokens_1.contract.bgRaised}; }`,
|
|
31
|
+
variants: {},
|
|
32
|
+
defaultVariants: {},
|
|
33
|
+
compoundVariants: [],
|
|
34
|
+
});
|
|
35
|
+
exports.assigneepickerRecipe = (0, core_1.resolveRecipe)(config);
|
|
36
|
+
exports.default = config;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.assigneepickerRecipe = exports.AssigneePicker = void 0;
|
|
4
|
+
var assigneepicker_1 = require("./assigneepicker");
|
|
5
|
+
Object.defineProperty(exports, "AssigneePicker", { enumerable: true, get: function () { return assigneepicker_1.AssigneePicker; } });
|
|
6
|
+
var assigneepicker_recipe_1 = require("./assigneepicker.recipe");
|
|
7
|
+
Object.defineProperty(exports, "assigneepickerRecipe", { enumerable: true, get: function () { return assigneepicker_recipe_1.assigneepickerRecipe; } });
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.notesgalleryRecipe = exports.NotesGallery = void 0;
|
|
4
|
+
var notesgallery_1 = require("./notesgallery");
|
|
5
|
+
Object.defineProperty(exports, "NotesGallery", { enumerable: true, get: function () { return notesgallery_1.NotesGallery; } });
|
|
6
|
+
var notesgallery_recipe_1 = require("./notesgallery.recipe");
|
|
7
|
+
Object.defineProperty(exports, "notesgalleryRecipe", { enumerable: true, get: function () { return notesgallery_recipe_1.notesgalleryRecipe; } });
|
|
@@ -0,0 +1,80 @@
|
|
|
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.NotesGallery = NotesGallery;
|
|
37
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
38
|
+
const React = __importStar(require("react"));
|
|
39
|
+
const tabs_1 = require("../../components/tabs");
|
|
40
|
+
const core_1 = require("../../core");
|
|
41
|
+
const entitycard_1 = require("../entitycard");
|
|
42
|
+
const notesgallery_recipe_1 = require("./notesgallery.recipe");
|
|
43
|
+
function NoteCard({ note }) {
|
|
44
|
+
return ((0, jsx_runtime_1.jsx)(entitycard_1.EntityCard, { author: note.linkedRecord, title: note.title, excerpt: note.excerpt, footer: note.author, date: note.date, onClick: note.onClick }));
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* The standalone Notes gallery -- measured live on app.attio.com's Notes
|
|
48
|
+
* page: "Notes N"/"Templates N" sub-tabs, a toolbar (Sort/View
|
|
49
|
+
* toggle/settings/New note, all consumer-composed), a dashed "Favorites"
|
|
50
|
+
* region, and date-grouped card sections. The card itself is `EntityCard`
|
|
51
|
+
* (crisp#474's own "measured from a Notes grid" component) — this shell
|
|
52
|
+
* only adds the gallery chrome around it.
|
|
53
|
+
*/
|
|
54
|
+
function NotesGallery({ notesCount, templatesCount, activeTab: activeTabProp, defaultActiveTab = "notes", onActiveTabChange, notesLabel: notesLabelProp, templatesLabel: templatesLabelProp, leading, trailing, favoritesLabel: favoritesLabelProp, favorites, favoritesEmptyLabel: favoritesEmptyLabelProp, groups, emptyLabel: emptyLabelProp, className, ...rest }) {
|
|
55
|
+
const notesLabel = (0, core_1.useCrispMessage)(notesLabelProp, "Notes");
|
|
56
|
+
const templatesLabel = (0, core_1.useCrispMessage)(templatesLabelProp, "Templates");
|
|
57
|
+
const favoritesLabel = (0, core_1.useCrispMessage)(favoritesLabelProp, "Favorites");
|
|
58
|
+
const favoritesEmptyLabel = (0, core_1.useCrispMessage)(favoritesEmptyLabelProp, "Notes that you favorite will appear here");
|
|
59
|
+
// crisp/#471: `emptyLabel` is typed `React.ReactNode`, so it resolves by
|
|
60
|
+
// hand rather than through `useCrispMessage`, which is string-only.
|
|
61
|
+
const crispMessages = (0, core_1.useCrispMessages)();
|
|
62
|
+
const emptyLabel = emptyLabelProp ?? crispMessages["No notes"] ?? "No notes";
|
|
63
|
+
const [internalTab, setInternalTab] = React.useState(defaultActiveTab);
|
|
64
|
+
const activeTab = activeTabProp ?? internalTab;
|
|
65
|
+
const setActiveTab = (v) => {
|
|
66
|
+
if (activeTabProp === undefined)
|
|
67
|
+
setInternalTab(v);
|
|
68
|
+
onActiveTabChange?.(v);
|
|
69
|
+
};
|
|
70
|
+
const total = groups.reduce((s, g) => s + g.notes.length, 0);
|
|
71
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: (0, notesgallery_recipe_1.notesgalleryRecipe)({ className }), ...rest, children: [(0, jsx_runtime_1.jsx)("div", { className: "crisp-notesgallery-tabs", children: (0, jsx_runtime_1.jsx)(tabs_1.Tabs, { tabs: [
|
|
72
|
+
{ value: "notes", label: notesLabel, badge: notesCount },
|
|
73
|
+
{
|
|
74
|
+
value: "templates",
|
|
75
|
+
label: templatesLabel,
|
|
76
|
+
badge: templatesCount,
|
|
77
|
+
},
|
|
78
|
+
], value: activeTab, onValueChange: setActiveTab }) }), (0, jsx_runtime_1.jsxs)("div", { className: "crisp-notesgallery-toolbar", children: [leading, trailing != null && ((0, jsx_runtime_1.jsx)("div", { className: "crisp-notesgallery-toolbar-trail", children: trailing }))] }), (0, jsx_runtime_1.jsxs)("div", { className: "crisp-notesgallery-favorites-section", children: [(0, jsx_runtime_1.jsx)("div", { className: "crisp-notesgallery-favorites-label", children: favoritesLabel }), (0, jsx_runtime_1.jsx)("div", { className: "crisp-notesgallery-favorites", children: favorites != null && favorites.length > 0 ? ((0, jsx_runtime_1.jsx)("div", { className: "crisp-notesgallery-grid", children: favorites.map((n) => ((0, jsx_runtime_1.jsx)(NoteCard, { note: n }, n.id))) })) : ((0, jsx_runtime_1.jsx)("div", { className: "crisp-notesgallery-favorites-empty", children: favoritesEmptyLabel })) })] }), (0, jsx_runtime_1.jsx)("div", { className: "crisp-notesgallery-body", children: total === 0 ? ((0, jsx_runtime_1.jsx)("div", { className: "crisp-notesgallery-empty", children: emptyLabel })) : (groups.map((g) => ((0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsxs)("div", { className: "crisp-notesgallery-group", children: [g.label, (0, jsx_runtime_1.jsx)("span", { className: "crisp-notesgallery-group-count", children: g.count })] }), (0, jsx_runtime_1.jsx)("div", { className: "crisp-notesgallery-grid", children: g.notes.map((n) => ((0, jsx_runtime_1.jsx)(NoteCard, { note: n }, n.id))) })] }, g.id)))) })] }));
|
|
79
|
+
}
|
|
80
|
+
NotesGallery.displayName = "NotesGallery";
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.notesgalleryRecipe = void 0;
|
|
4
|
+
const core_1 = require("../../core");
|
|
5
|
+
const tokens_1 = require("../../tokens");
|
|
6
|
+
// The standalone Notes gallery -- measured live on app.attio.com's Notes
|
|
7
|
+
// page: a toolbar (leading Sort control, trailing view toggle/View
|
|
8
|
+
// settings/New note, both consumer-composed slots -- crisp doesn't own the
|
|
9
|
+
// exact button set, same reasoning as `ListPage`'s own leading/trailing), a
|
|
10
|
+
// dashed "Favorites" region, then date-grouped sections (a group header +
|
|
11
|
+
// count badge, transcribed from `ListPage`'s own already-measured values
|
|
12
|
+
// rather than re-deriving them -- this IS the same Attio pattern, applied
|
|
13
|
+
// above a card grid instead of a row list) holding a responsive card grid of
|
|
14
|
+
// `EntityCard`s.
|
|
15
|
+
//
|
|
16
|
+
// The grid itself: a single real note card measured 356px wide -- with only
|
|
17
|
+
// one note in the workspace there was no second card to read a column gap or
|
|
18
|
+
// count from, so this uses a responsive `auto-fill` grid at that measured
|
|
19
|
+
// per-card width instead of inventing a fixed column count or gap.
|
|
20
|
+
const config = (0, core_1.defineRecipe)({
|
|
21
|
+
name: "notesgallery",
|
|
22
|
+
base: `display: flex; flex-direction: column; min-height: 0; box-sizing: border-box; font-family: ${tokens_1.contract.font.sans}; color: ${tokens_1.contract.fgPrimary};
|
|
23
|
+
-webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
|
|
24
|
+
|
|
25
|
+
& .crisp-notesgallery-tabs { display: flex; align-items: center; gap: ${tokens_1.contract.space["2"]}; height: 44px; flex-shrink: 0; padding: 0 ${tokens_1.contract.space["3"]}; }
|
|
26
|
+
|
|
27
|
+
& .crisp-notesgallery-toolbar { display: flex; align-items: center; gap: ${tokens_1.contract.space["2"]}; height: ${tokens_1.contract.size.header}; flex-shrink: 0; padding: 0 ${tokens_1.contract.space["3"]}; }
|
|
28
|
+
& .crisp-notesgallery-toolbar-trail { display: flex; align-items: center; gap: ${tokens_1.contract.space["2"]}; margin-inline-start: auto; }
|
|
29
|
+
|
|
30
|
+
& .crisp-notesgallery-favorites-section { background: ${tokens_1.contract.bgSubtle}; box-shadow: inset 0 -1px 0 ${tokens_1.contract.borderSubtle}; }
|
|
31
|
+
& .crisp-notesgallery-favorites-label { padding: ${tokens_1.contract.space["2"]} ${tokens_1.contract.space["4"]} 0; font-size: ${tokens_1.contract.text.sm}; font-weight: ${tokens_1.contract.weight.medium}; color: ${tokens_1.contract.fgMuted}; }
|
|
32
|
+
& .crisp-notesgallery-favorites { display: flex; align-items: center; justify-content: center; min-height: 200px; padding: ${tokens_1.contract.space["6"]}; }
|
|
33
|
+
& .crisp-notesgallery-favorites-empty { box-sizing: border-box; width: 100%; padding: ${tokens_1.contract.space["6"]}; border: 1px dashed ${tokens_1.contract.borderDefault}; border-radius: ${tokens_1.contract.radius.xl}; text-align: center; font-size: ${tokens_1.contract.text.md}; color: ${tokens_1.contract.fgMuted}; }
|
|
34
|
+
|
|
35
|
+
& .crisp-notesgallery-body { flex: 1; min-height: 0; overflow-y: auto; padding: 0 ${tokens_1.contract.space["3"]} ${tokens_1.contract.space["3"]}; }
|
|
36
|
+
& .crisp-notesgallery-group { display: flex; align-items: center; gap: ${tokens_1.contract.space["1.5"]}; height: ${tokens_1.contract.size.menuRow}; padding: ${tokens_1.contract.space["2"]} ${tokens_1.contract.space["1"]}; font-size: ${tokens_1.contract.text.sm}; font-weight: ${tokens_1.contract.weight.medium}; color: ${tokens_1.contract.fgMuted}; }
|
|
37
|
+
& .crisp-notesgallery-group-count { display: inline-flex; align-items: center; justify-content: center; min-width: 16px; height: 16px; padding: 0 ${tokens_1.contract.space["1"]}; border-radius: ${tokens_1.contract.radius.sm}; background: color-mix(in srgb, ${tokens_1.contract.fgPrimary} 6%, transparent); font-size: ${tokens_1.contract.text.xs}; color: ${tokens_1.contract.fgMuted}; }
|
|
38
|
+
& .crisp-notesgallery-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(340px, 1fr)); gap: ${tokens_1.contract.space["4"]}; padding-bottom: ${tokens_1.contract.space["4"]}; }
|
|
39
|
+
|
|
40
|
+
& .crisp-notesgallery-empty { padding: ${tokens_1.contract.space["8"]} ${tokens_1.contract.space["3"]}; text-align: center; font-size: ${tokens_1.contract.text.md}; color: ${tokens_1.contract.fgMuted}; }`,
|
|
41
|
+
variants: {},
|
|
42
|
+
defaultVariants: {},
|
|
43
|
+
compoundVariants: [],
|
|
44
|
+
});
|
|
45
|
+
exports.notesgalleryRecipe = (0, core_1.resolveRecipe)(config);
|
|
46
|
+
exports.default = config;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./objectpagemenu"), exports);
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ObjectPageMenu = ObjectPageMenu;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const lucide_react_1 = require("lucide-react");
|
|
6
|
+
const button_1 = require("../../components/button");
|
|
7
|
+
const menu_1 = require("../../components/menu");
|
|
8
|
+
const core_1 = require("../../core");
|
|
9
|
+
/**
|
|
10
|
+
* The page-level "…" menu on Attio's object page: Object settings / Manage
|
|
11
|
+
* attributes / a Notifications submenu ("Connect Slack workspace") — crisp
|
|
12
|
+
* parity checklist rows G-020/P-005 (crisp#616). Live-measured on
|
|
13
|
+
* app.attio.com/techpartners/companies: 208×114 popover (12px radius, 4px
|
|
14
|
+
* padding, 32px rows), a hairline separator before Notifications, and its
|
|
15
|
+
* submenu opening flush against the item with no gap, flipping side when
|
|
16
|
+
* the measured width doesn't fit (`placeSubmenu`, core/place-floating.ts).
|
|
17
|
+
*/
|
|
18
|
+
function ObjectPageMenu({ trigger, onObjectSettings, onManageAttributes, onConnectSlack, objectSettingsLabel, manageAttributesLabel, notificationsLabel, connectSlackLabel, "aria-label": ariaLabelProp, align = "end", }) {
|
|
19
|
+
const objectSettings = (0, core_1.useCrispMessage)(objectSettingsLabel, "Object settings");
|
|
20
|
+
const manageAttributes = (0, core_1.useCrispMessage)(manageAttributesLabel, "Manage attributes");
|
|
21
|
+
const notifications = (0, core_1.useCrispMessage)(notificationsLabel, "Notifications");
|
|
22
|
+
const connectSlack = (0, core_1.useCrispMessage)(connectSlackLabel, "Connect Slack workspace");
|
|
23
|
+
const ariaLabel = (0, core_1.useCrispMessage)(ariaLabelProp, "Show menu");
|
|
24
|
+
return ((0, jsx_runtime_1.jsxs)(menu_1.Menu, { align: align, "aria-label": ariaLabel, trigger: trigger ?? ((0, jsx_runtime_1.jsx)(button_1.Button, { intent: "ghost", size: "md", shape: "icon", "aria-label": ariaLabel, 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.Settings, { size: 16, strokeWidth: 1.75 }), onSelect: onObjectSettings, children: objectSettings }), (0, jsx_runtime_1.jsx)(menu_1.MenuItem, { icon: (0, jsx_runtime_1.jsx)(lucide_react_1.LayoutGrid, { size: 16, strokeWidth: 1.75 }), onSelect: onManageAttributes, children: manageAttributes }), (0, jsx_runtime_1.jsx)(menu_1.MenuSeparator, {}), (0, jsx_runtime_1.jsx)(menu_1.Menu, { placement: "submenu", "aria-label": notifications, trigger: (0, jsx_runtime_1.jsx)(menu_1.MenuItem, { icon: (0, jsx_runtime_1.jsx)(lucide_react_1.Bell, { size: 16, strokeWidth: 1.75 }), trailingIcon: (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronRight, { "data-rtl-flip": "", size: 16, strokeWidth: 1.75 }), closeOnSelect: false, children: notifications }), children: (0, jsx_runtime_1.jsx)(menu_1.MenuItem, { icon: (0, jsx_runtime_1.jsx)(lucide_react_1.MessageSquare, { size: 16, strokeWidth: 1.75 }), onSelect: onConnectSlack, children: connectSlack }) })] }));
|
|
25
|
+
}
|
|
26
|
+
ObjectPageMenu.displayName = "ObjectPageMenu";
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.objectpagemenuRecipe = void 0;
|
|
4
|
+
const core_1 = require("../../core");
|
|
5
|
+
/**
|
|
6
|
+
* `ObjectPageMenu` owns no styling of its own — every visible pixel is
|
|
7
|
+
* `Menu`/`MenuItem`'s own already-measured recipe. The one thing it DOES
|
|
8
|
+
* need is the RTL mirror for its Notifications submenu chevron (crisp#616):
|
|
9
|
+
* that icon is the first direction-bearing one ever passed as a `MenuItem`
|
|
10
|
+
* `trailingIcon`, so nothing already mirrors it.
|
|
11
|
+
*
|
|
12
|
+
* Not written as `${rtlFlipCss}` (core/rtl.ts): that string relies on native
|
|
13
|
+
* CSS nesting's `&` resolving to an ENCLOSING rule, which is exactly what
|
|
14
|
+
* `Menu`'s own recipe deliberately avoids for its portaled content (see its
|
|
15
|
+
* own doc comment) — nesting this under a `.crisp-objectpagemenu` root would
|
|
16
|
+
* need a root class this component doesn't have. A plain absolute selector,
|
|
17
|
+
* matching `Menu`'s own pattern for the identical reason.
|
|
18
|
+
*/
|
|
19
|
+
const config = (0, core_1.defineRecipe)({
|
|
20
|
+
name: "objectpagemenu",
|
|
21
|
+
base: "",
|
|
22
|
+
globalCss: `
|
|
23
|
+
[dir="rtl"] .crisp-menu-item svg[data-rtl-flip] { transform: scaleX(-1); }
|
|
24
|
+
`,
|
|
25
|
+
variants: {},
|
|
26
|
+
defaultVariants: {},
|
|
27
|
+
compoundVariants: [],
|
|
28
|
+
});
|
|
29
|
+
exports.objectpagemenuRecipe = (0, core_1.resolveRecipe)(config);
|
|
30
|
+
exports.default = config;
|
|
@@ -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";
|