@crisp-ui-kit/crisp 0.53.5 → 0.54.0
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/cjs/components/bulkbar/bulkbar.js +5 -1
- package/dist/cjs/components/fileupload/fileupload.js +19 -3
- package/dist/cjs/components/formfield/formfield.js +8 -1
- package/dist/cjs/components/heatmapchart/heatmapchart.js +14 -2
- package/dist/cjs/components/skiplink/index.js +17 -0
- package/dist/cjs/components/skiplink/skiplink.js +16 -0
- package/dist/cjs/components/skiplink/skiplink.recipe.js +33 -0
- package/dist/cjs/components/textlink/index.js +7 -0
- package/dist/cjs/components/textlink/textlink.js +16 -0
- package/dist/cjs/components/textlink/textlink.recipe.js +35 -0
- package/dist/cjs/components/visuallyhidden/index.js +17 -0
- package/dist/cjs/components/visuallyhidden/visuallyhidden.js +16 -0
- package/dist/cjs/components/visuallyhidden/visuallyhidden.recipe.js +20 -0
- package/dist/cjs/index.js +4 -0
- package/dist/cjs/product/board/board.js +11 -2
- package/dist/cjs/product/command/command.js +13 -1
- package/dist/cjs/product/flowcanvas/flowcanvas.js +9 -2
- package/dist/cjs/product/headerbar/headerbar.js +15 -0
- package/dist/cjs/product/headerbar/headerbar.recipe.js +35 -0
- package/dist/cjs/product/headerbar/index.js +17 -0
- package/dist/cjs/product/inbox/inbox.js +13 -1
- package/dist/cjs/product/onboarding/onboarding.js +22 -1
- package/dist/cjs/product/recordpage/index.js +2 -1
- package/dist/cjs/product/recordpage/recordpage.js +197 -123
- package/dist/cjs/product/recordpage/recordpage.recipe.js +5 -0
- package/dist/cjs/product/tasklist/tasklist.js +8 -1
- package/dist/esm/components/bulkbar/bulkbar.js +5 -1
- package/dist/esm/components/fileupload/fileupload.js +20 -4
- package/dist/esm/components/formfield/formfield.js +8 -1
- package/dist/esm/components/heatmapchart/heatmapchart.js +14 -2
- package/dist/esm/components/skiplink/index.js +1 -0
- package/dist/esm/components/skiplink/skiplink.js +13 -0
- package/dist/esm/components/skiplink/skiplink.recipe.js +30 -0
- package/dist/esm/components/textlink/index.js +2 -0
- package/dist/esm/components/textlink/textlink.js +13 -0
- package/dist/esm/components/textlink/textlink.recipe.js +32 -0
- package/dist/esm/components/visuallyhidden/index.js +1 -0
- package/dist/esm/components/visuallyhidden/visuallyhidden.js +13 -0
- package/dist/esm/components/visuallyhidden/visuallyhidden.recipe.js +17 -0
- package/dist/esm/index.js +4 -0
- package/dist/esm/product/board/board.js +12 -3
- package/dist/esm/product/command/command.js +14 -2
- package/dist/esm/product/flowcanvas/flowcanvas.js +9 -2
- package/dist/esm/product/headerbar/headerbar.js +12 -0
- package/dist/esm/product/headerbar/headerbar.recipe.js +32 -0
- package/dist/esm/product/headerbar/index.js +1 -0
- package/dist/esm/product/inbox/inbox.js +13 -1
- package/dist/esm/product/onboarding/onboarding.js +22 -1
- package/dist/esm/product/recordpage/index.js +1 -1
- package/dist/esm/product/recordpage/recordpage.js +197 -124
- package/dist/esm/product/recordpage/recordpage.recipe.js +5 -0
- package/dist/esm/product/tasklist/tasklist.js +8 -1
- package/dist/headerbar.css +7 -0
- package/dist/headerbar.d.ts +7 -0
- package/dist/headerbar.layered.css +9 -0
- package/dist/recordpage.css +17 -31
- package/dist/recordpage.layered.css +17 -31
- package/dist/skiplink.css +5 -0
- package/dist/skiplink.d.ts +7 -0
- package/dist/skiplink.layered.css +7 -0
- package/dist/styles.css +47 -31
- package/dist/styles.layered.css +47 -31
- package/dist/textlink.css +11 -0
- package/dist/textlink.d.ts +7 -0
- package/dist/textlink.layered.css +13 -0
- package/dist/types/components/bulkbar/bulkbar.d.ts +1 -1
- package/dist/types/components/heatmapchart/heatmapchart.d.ts +1 -1
- package/dist/types/components/skiplink/index.d.ts +1 -0
- package/dist/types/components/skiplink/skiplink.d.ts +18 -0
- package/dist/types/components/skiplink/skiplink.recipe.d.ts +3 -0
- package/dist/types/components/textlink/index.d.ts +2 -0
- package/dist/types/components/textlink/textlink.d.ts +19 -0
- package/dist/types/components/textlink/textlink.recipe.d.ts +13 -0
- package/dist/types/components/visuallyhidden/index.d.ts +1 -0
- package/dist/types/components/visuallyhidden/visuallyhidden.d.ts +15 -0
- package/dist/types/components/visuallyhidden/visuallyhidden.recipe.d.ts +3 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/product/board/board.d.ts +1 -1
- package/dist/types/product/command/command.d.ts +1 -1
- package/dist/types/product/flowcanvas/flowcanvas.d.ts +1 -1
- package/dist/types/product/headerbar/headerbar.d.ts +27 -0
- package/dist/types/product/headerbar/headerbar.recipe.d.ts +3 -0
- package/dist/types/product/headerbar/index.d.ts +1 -0
- package/dist/types/product/inbox/inbox.d.ts +1 -1
- package/dist/types/product/onboarding/onboarding.d.ts +1 -1
- package/dist/types/product/recordpage/index.d.ts +1 -1
- package/dist/types/product/recordpage/recordpage.d.ts +90 -1
- package/dist/types/product/tasklist/tasklist.d.ts +1 -1
- package/dist/visuallyhidden.css +3 -0
- package/dist/visuallyhidden.d.ts +7 -0
- package/dist/visuallyhidden.layered.css +5 -0
- package/package.json +21 -1
|
@@ -38,13 +38,17 @@ exports.BulkBar = BulkBar;
|
|
|
38
38
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
39
39
|
const lucide_react_1 = require("lucide-react");
|
|
40
40
|
const React = __importStar(require("react"));
|
|
41
|
+
const core_1 = require("../../core");
|
|
41
42
|
const bulkbar_recipe_1 = require("./bulkbar.recipe");
|
|
42
43
|
/**
|
|
43
44
|
* A floating bulk-action bar. Rises from the bottom-centre of its nearest
|
|
44
45
|
* positioned ancestor when a selection is active; render it conditionally
|
|
45
46
|
* (e.g. `count > 0 && <BulkBar …/>`). Measured from a records table.
|
|
46
47
|
*/
|
|
47
|
-
function BulkBar({ count, onClear, countLabel
|
|
48
|
+
function BulkBar({ count, onClear, countLabel: countLabelProp, countText = (count, label) => ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [count, " ", label] })), children, className, clearLabel: clearLabelProp, ...rest }) {
|
|
49
|
+
// crisp/#471: same resolution order as every other component's own.
|
|
50
|
+
const countLabel = (0, core_1.useCrispMessage)(countLabelProp, "Selected");
|
|
51
|
+
const clearLabel = (0, core_1.useCrispMessage)(clearLabelProp, "Clear selection");
|
|
48
52
|
return ((0, jsx_runtime_1.jsx)("div", { className: (0, bulkbar_recipe_1.bulkbarRecipe)({ className }), ...rest, children: (0, jsx_runtime_1.jsxs)("div", { className: "crisp-bulkbar-bar", children: [count != null && ((0, jsx_runtime_1.jsxs)("button", { type: "button", className: "crisp-bulkbar-count", onClick: onClear, title: clearLabel, children: [countText(count, countLabel), (0, jsx_runtime_1.jsx)("span", { className: "crisp-bulkbar-clearlabel", children: clearLabel }), (0, jsx_runtime_1.jsx)(lucide_react_1.X, { size: 13, strokeWidth: 2, "aria-hidden": "true" })] })), children] }) }));
|
|
49
53
|
}
|
|
50
54
|
BulkBar.displayName = "BulkBar";
|
|
@@ -93,10 +93,26 @@ function FileUpload({ className, labels, "aria-label": ariaLabel, id, accept, di
|
|
|
93
93
|
onAcceptedFileEntriesChange,
|
|
94
94
|
};
|
|
95
95
|
const [rejections, setRejections] = React.useState([]);
|
|
96
|
+
// crisp/#471: same resolution order as every other component's own —
|
|
97
|
+
// explicit `labels` entry wins, then a CrispMessagesProvider's
|
|
98
|
+
// translation of the built-in English default, then that default itself.
|
|
99
|
+
const title = (0, core_1.useCrispMessage)(labels?.title, exports.fileUploadLabels.title);
|
|
100
|
+
const hint = (0, core_1.useCrispMessage)(labels?.hint, exports.fileUploadLabels.hint);
|
|
101
|
+
const remove = (0, core_1.useCrispMessage)(labels?.remove, exports.fileUploadLabels.remove);
|
|
102
|
+
const crispMessages = (0, core_1.useCrispMessages)();
|
|
96
103
|
const t = {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
104
|
+
title,
|
|
105
|
+
hint,
|
|
106
|
+
remove,
|
|
107
|
+
errors: Object.fromEntries(Object.keys(exports.fileUploadLabels.errors).map((code) => {
|
|
108
|
+
const englishDefault = exports.fileUploadLabels.errors[code];
|
|
109
|
+
return [
|
|
110
|
+
code,
|
|
111
|
+
labels?.errors?.[code] ??
|
|
112
|
+
crispMessages[englishDefault] ??
|
|
113
|
+
englishDefault,
|
|
114
|
+
];
|
|
115
|
+
})),
|
|
100
116
|
};
|
|
101
117
|
const api = (0, react_file_upload_1.useFileUpload)({
|
|
102
118
|
...props,
|
|
@@ -36,6 +36,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
36
36
|
exports.FormField = FormField;
|
|
37
37
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
38
38
|
const React = __importStar(require("react"));
|
|
39
|
+
const core_1 = require("../../core");
|
|
39
40
|
const formfield_recipe_1 = require("./formfield.recipe");
|
|
40
41
|
/**
|
|
41
42
|
* Tell the control what the field knows about it.
|
|
@@ -96,12 +97,18 @@ function useFieldWiring({ htmlFor, messageId, invalid, required, }) {
|
|
|
96
97
|
function FormField({ label, htmlFor, required, optional, description, error, children, className, ...rest }) {
|
|
97
98
|
const descId = React.useId();
|
|
98
99
|
const hasMsg = error != null || description != null;
|
|
100
|
+
// crisp/#471: `optional` doubles as a boolean flag and a ReactNode
|
|
101
|
+
// override — passing any node instead of `true` already renders it
|
|
102
|
+
// verbatim below, with no lookup needed. `true` reads a
|
|
103
|
+
// CrispMessagesProvider's translation of the built-in default text, same
|
|
104
|
+
// resolution order as every other component's own.
|
|
105
|
+
const optionalDefault = (0, core_1.useCrispMessage)(undefined, "(optional)");
|
|
99
106
|
useFieldWiring({
|
|
100
107
|
htmlFor,
|
|
101
108
|
messageId: hasMsg ? descId : undefined,
|
|
102
109
|
invalid: error != null,
|
|
103
110
|
required: !!required,
|
|
104
111
|
});
|
|
105
|
-
return ((0, jsx_runtime_1.jsxs)("div", { className: (0, formfield_recipe_1.formfieldRecipe)({ className }), ...rest, children: [(0, jsx_runtime_1.jsxs)("label", { className: "crisp-formfield-label", htmlFor: htmlFor, children: [label, required && ((0, jsx_runtime_1.jsx)("span", { className: "crisp-formfield-required", "aria-hidden": "true", children: "*" })), optional && !required && ((0, jsx_runtime_1.jsx)("span", { className: "crisp-formfield-optional", children: optional === true ?
|
|
112
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: (0, formfield_recipe_1.formfieldRecipe)({ className }), ...rest, children: [(0, jsx_runtime_1.jsxs)("label", { className: "crisp-formfield-label", htmlFor: htmlFor, children: [label, required && ((0, jsx_runtime_1.jsx)("span", { className: "crisp-formfield-required", "aria-hidden": "true", children: "*" })), optional && !required && ((0, jsx_runtime_1.jsx)("span", { className: "crisp-formfield-optional", children: optional === true ? optionalDefault : optional }))] }), children, error != null ? ((0, jsx_runtime_1.jsx)("span", { className: "crisp-formfield-error", id: hasMsg ? descId : undefined, children: error })) : description != null ? ((0, jsx_runtime_1.jsx)("span", { className: "crisp-formfield-desc", id: hasMsg ? descId : undefined, children: description })) : null] }));
|
|
106
113
|
}
|
|
107
114
|
FormField.displayName = "FormField";
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.HeatmapChart = HeatmapChart;
|
|
4
4
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const core_1 = require("../../core");
|
|
5
6
|
const heatmapchart_recipe_1 = require("./heatmapchart.recipe");
|
|
6
7
|
/**
|
|
7
8
|
* An NxM cell-matrix chart — GRC's own risk heatmap (impact x likelihood)
|
|
@@ -10,9 +11,20 @@ const heatmapchart_recipe_1 = require("./heatmapchart.recipe");
|
|
|
10
11
|
* the recipe's own doc comment for why (and for its measurement — or lack
|
|
11
12
|
* of one — against Attio).
|
|
12
13
|
*/
|
|
13
|
-
function HeatmapChart({ data, rowAxis, colAxis, format = (cell) => (cell.value ? cell.value : ""), cellTitle, onCellClick, isCellClickable = (cell) => cell.value > 0, empty, emptyLabel
|
|
14
|
+
function HeatmapChart({ data, rowAxis, colAxis, format = (cell) => (cell.value ? cell.value : ""), cellTitle, onCellClick, isCellClickable = (cell) => cell.value > 0, empty, emptyLabel: emptyLabelProp, "aria-label": ariaLabel, className, ...rest }) {
|
|
14
15
|
const cols = data[0]?.length ?? 0;
|
|
15
|
-
|
|
16
|
+
// crisp/#471: `emptyLabel` is typed `React.ReactNode`, so it resolves by
|
|
17
|
+
// hand rather than through `useCrispMessage`, which is string-only. The
|
|
18
|
+
// default `aria-label` is a sentence built from two static fragments
|
|
19
|
+
// around live data (row/column counts, not translatable), so each
|
|
20
|
+
// fragment gets its own lookup — same as TableToolbar's "Sorted by"
|
|
21
|
+
// (crisp/#217).
|
|
22
|
+
const crispMessages = (0, core_1.useCrispMessages)();
|
|
23
|
+
const emptyLabel = emptyLabelProp ??
|
|
24
|
+
crispMessages["Nothing to plot yet."] ??
|
|
25
|
+
"Nothing to plot yet.";
|
|
26
|
+
const summary = ariaLabel ??
|
|
27
|
+
`${crispMessages.Heatmap ?? "Heatmap"}: ${data.length} ${crispMessages.x ?? "x"} ${cols} ${crispMessages.grid ?? "grid"}`;
|
|
16
28
|
return ((0, jsx_runtime_1.jsx)("div", { className: (0, heatmapchart_recipe_1.heatmapchartRecipe)({ className }), ...rest, role: "img", "aria-label": summary, children: empty ? ((0, jsx_runtime_1.jsx)("div", { className: "crisp-heatmapchart-empty", children: emptyLabel })) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)("div", { className: "crisp-heatmapchart-rowaxis", children: [(0, jsx_runtime_1.jsx)("span", { children: rowAxis.high }), rowAxis.title != null && ((0, jsx_runtime_1.jsx)("span", { className: "crisp-heatmapchart-rowaxis-title", children: rowAxis.title })), (0, jsx_runtime_1.jsx)("span", { children: rowAxis.low })] }), (0, jsx_runtime_1.jsxs)("div", { className: "crisp-heatmapchart-body", children: [(0, jsx_runtime_1.jsx)("div", { className: "crisp-heatmapchart-grid", style: { gridTemplateColumns: `repeat(${cols}, 1fr)` }, children: data.map((row, r) => row.map((cell, cc) => {
|
|
17
29
|
const clickable = onCellClick != null && isCellClickable(cell, r, cc);
|
|
18
30
|
const title = cellTitle?.(cell, r, cc);
|
|
@@ -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("./skiplink"), exports);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SkipLink = SkipLink;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const skiplink_recipe_1 = require("./skiplink.recipe");
|
|
6
|
+
/**
|
|
7
|
+
* A "skip to content" link — invisible until it is the thing with focus
|
|
8
|
+
* (a sighted mouse user never sees it), so a keyboard user's first Tab from
|
|
9
|
+
* a cold load reaches it instead of walking the whole nav first. Render it
|
|
10
|
+
* as the very first thing on the page, before the sidebar/header. See the
|
|
11
|
+
* recipe's own doc comment for why there's no Attio value behind its chrome.
|
|
12
|
+
*/
|
|
13
|
+
function SkipLink({ href = "#main-content", children = "Skip to content", className, ...rest }) {
|
|
14
|
+
return ((0, jsx_runtime_1.jsx)("a", { href: href, className: (0, skiplink_recipe_1.skiplinkRecipe)({ className }), ...rest, children: children }));
|
|
15
|
+
}
|
|
16
|
+
SkipLink.displayName = "SkipLink";
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.skiplinkRecipe = void 0;
|
|
4
|
+
const core_1 = require("../../core");
|
|
5
|
+
const tokens_1 = require("../../tokens");
|
|
6
|
+
// A "skip to content" link: invisible (the same sr-only clip
|
|
7
|
+
// VisuallyHidden uses) until it is the thing with focus, so a sighted
|
|
8
|
+
// mouse user never sees it but a keyboard user's first Tab from a cold
|
|
9
|
+
// load reaches it. No Attio screen ships one to measure -- this is
|
|
10
|
+
// boilerplate accessibility furniture, not a design value (crisp#462 GRC
|
|
11
|
+
// audit gap 2 of 4, crisp#540) -- so the un-hidden chrome reuses already-measured
|
|
12
|
+
// crisp values throughout: the `bgPopover` floating surface + its
|
|
13
|
+
// `elevation.floating` shadow (the same pairing Menu/Popover/ContextMenu/
|
|
14
|
+
// Toast use for "a surface that appears above everything else"), `radius.md`,
|
|
15
|
+
// and the canonical `:focus-visible` ring every other crisp control uses
|
|
16
|
+
// (`outline: 2px solid ${bgBrandSolid}`, 1px offset) -- no new value is
|
|
17
|
+
// introduced.
|
|
18
|
+
const config = (0, core_1.defineRecipe)({
|
|
19
|
+
name: "skiplink",
|
|
20
|
+
base: `position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; text-decoration: none;
|
|
21
|
+
font-family: ${tokens_1.contract.font.sans}; font-size: ${tokens_1.contract.text.lg}; font-weight: ${tokens_1.contract.weight.medium}; color: ${tokens_1.contract.fgPrimary};
|
|
22
|
+
|
|
23
|
+
&:focus {
|
|
24
|
+
position: fixed; top: ${tokens_1.contract.space["3"]}; inset-inline-start: ${tokens_1.contract.space["3"]}; z-index: 90; width: auto; height: auto; margin: 0; overflow: visible; clip: auto; white-space: normal;
|
|
25
|
+
padding: ${tokens_1.contract.space["2"]} ${tokens_1.contract.space["3"]}; border-radius: ${tokens_1.contract.radius.md}; background: ${tokens_1.contract.bgPopover}; box-shadow: ${tokens_1.elevation.floating};
|
|
26
|
+
}
|
|
27
|
+
&:focus-visible { outline: 2px solid ${tokens_1.contract.bgBrandSolid}; outline-offset: 1px; }`,
|
|
28
|
+
variants: {},
|
|
29
|
+
defaultVariants: {},
|
|
30
|
+
compoundVariants: [],
|
|
31
|
+
});
|
|
32
|
+
exports.skiplinkRecipe = (0, core_1.resolveRecipe)(config);
|
|
33
|
+
exports.default = config;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.textlinkRecipe = exports.TextLink = void 0;
|
|
4
|
+
var textlink_1 = require("./textlink");
|
|
5
|
+
Object.defineProperty(exports, "TextLink", { enumerable: true, get: function () { return textlink_1.TextLink; } });
|
|
6
|
+
var textlink_recipe_1 = require("./textlink.recipe");
|
|
7
|
+
Object.defineProperty(exports, "textlinkRecipe", { enumerable: true, get: function () { return textlink_recipe_1.textlinkRecipe; } });
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TextLink = TextLink;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const textlink_recipe_1 = require("./textlink.recipe");
|
|
6
|
+
/**
|
|
7
|
+
* Inline navigable text — a real `<a href>` always, the same href+onClick
|
|
8
|
+
* division `StatList` uses: `href` gives ordinary browser behaviour (new
|
|
9
|
+
* tab, copy link, hover status), an `onClick` that calls
|
|
10
|
+
* `event.preventDefault()` pairs it with client-side routing (e.g.
|
|
11
|
+
* react-router's own `Link`).
|
|
12
|
+
*/
|
|
13
|
+
function TextLink({ tone = "muted", className, ...rest }) {
|
|
14
|
+
return (0, jsx_runtime_1.jsx)("a", { className: (0, textlink_recipe_1.textlinkRecipe)({ tone, className }), ...rest });
|
|
15
|
+
}
|
|
16
|
+
TextLink.displayName = "TextLink";
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.textlinkRecipe = void 0;
|
|
4
|
+
const core_1 = require("../../core");
|
|
5
|
+
const tokens_1 = require("../../tokens");
|
|
6
|
+
// The one inline-navigable-text shape GRC's own screens kept hand-typing at
|
|
7
|
+
// every call site (internal/grc#110 / your-moon/crisp#529): a card's own
|
|
8
|
+
// trailing "View all →", and a muted in-context reference link that goes
|
|
9
|
+
// brand on hover. No new colour or size here — both variants read the exact
|
|
10
|
+
// tokens those call sites already used (`bgBrandSolid` reused as a text
|
|
11
|
+
// colour, the same alias GRC's own `--color-fg-brand` resolves to; `fgMuted`;
|
|
12
|
+
// the existing 12px/13px text sizes) rather than a fresh guess, and neither
|
|
13
|
+
// is measured against one specific Attio widget for the same reason
|
|
14
|
+
// StatList/HeatmapChart's own doc comments give: this exact "small trailing
|
|
15
|
+
// link" isn't a distinct Attio component to point a CDP session at, it's a
|
|
16
|
+
// reuse of already-measured type and colour values in a new arrangement.
|
|
17
|
+
const config = (0, core_1.defineRecipe)({
|
|
18
|
+
name: "textlink",
|
|
19
|
+
base: `display: inline; font-family: ${tokens_1.contract.font.sans}; text-decoration: none; cursor: pointer;
|
|
20
|
+
&:hover { text-decoration: underline; }`,
|
|
21
|
+
variants: {
|
|
22
|
+
tone: {
|
|
23
|
+
// The bold, always-brand-coloured trailing link a Card's own `action`
|
|
24
|
+
// slot uses ("View all →", "All registers →").
|
|
25
|
+
brand: `font-size: ${tokens_1.contract.text.sm}; font-weight: ${tokens_1.contract.weight.medium}; color: ${tokens_1.contract.bgBrandSolid};`,
|
|
26
|
+
// A quieter in-context reference (a sample record under a finding)
|
|
27
|
+
// that only goes brand on hover.
|
|
28
|
+
muted: `font-size: ${tokens_1.contract.text.md}; font-weight: ${tokens_1.contract.weight.regular}; color: ${tokens_1.contract.fgMuted}; &:hover { color: ${tokens_1.contract.bgBrandSolid}; }`,
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
defaultVariants: { tone: "muted" },
|
|
32
|
+
compoundVariants: [],
|
|
33
|
+
});
|
|
34
|
+
exports.textlinkRecipe = (0, core_1.resolveRecipe)(config);
|
|
35
|
+
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("./visuallyhidden"), exports);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.VisuallyHidden = VisuallyHidden;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const visuallyhidden_recipe_1 = require("./visuallyhidden.recipe");
|
|
6
|
+
/**
|
|
7
|
+
* Hides content visually while keeping it in the accessibility tree and
|
|
8
|
+
* the tab order — a label for an icon-only control, extra context for a
|
|
9
|
+
* screen-reader user that a sighted one gets from layout alone. Renders a
|
|
10
|
+
* `<span>`; wrap the element that needs the treatment, or read its content
|
|
11
|
+
* as a `label`/`aria-label` source in a test.
|
|
12
|
+
*/
|
|
13
|
+
function VisuallyHidden({ className, ...rest }) {
|
|
14
|
+
return (0, jsx_runtime_1.jsx)("span", { className: (0, visuallyhidden_recipe_1.visuallyhiddenRecipe)({ className }), ...rest });
|
|
15
|
+
}
|
|
16
|
+
VisuallyHidden.displayName = "VisuallyHidden";
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.visuallyhiddenRecipe = void 0;
|
|
4
|
+
const core_1 = require("../../core");
|
|
5
|
+
// The standard "sr-only" clip pattern: content stays in the accessibility
|
|
6
|
+
// tree and the tab order, but occupies no visible space and is never
|
|
7
|
+
// painted. Pure CSS mechanics, not a design value -- there is nothing to
|
|
8
|
+
// measure against Attio here (crisp#462 GRC audit gap 2 of 4, crisp#540):
|
|
9
|
+
// no Attio screen ships content that is deliberately invisible-but-reachable,
|
|
10
|
+
// so this is the standard web-platform pattern, same shape `SkipLink` also
|
|
11
|
+
// starts from before it un-hides on focus.
|
|
12
|
+
const config = (0, core_1.defineRecipe)({
|
|
13
|
+
name: "visuallyhidden",
|
|
14
|
+
base: `position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;`,
|
|
15
|
+
variants: {},
|
|
16
|
+
defaultVariants: {},
|
|
17
|
+
compoundVariants: [],
|
|
18
|
+
});
|
|
19
|
+
exports.visuallyhiddenRecipe = (0, core_1.resolveRecipe)(config);
|
|
20
|
+
exports.default = config;
|
package/dist/cjs/index.js
CHANGED
|
@@ -60,6 +60,7 @@ __exportStar(require("./components/resizablepanel"), exports);
|
|
|
60
60
|
__exportStar(require("./components/segmented"), exports);
|
|
61
61
|
__exportStar(require("./components/select"), exports);
|
|
62
62
|
__exportStar(require("./components/skeleton"), exports);
|
|
63
|
+
__exportStar(require("./components/skiplink"), exports);
|
|
63
64
|
__exportStar(require("./components/slider"), exports);
|
|
64
65
|
__exportStar(require("./components/spinner"), exports);
|
|
65
66
|
__exportStar(require("./components/stagebar"), exports);
|
|
@@ -69,9 +70,11 @@ __exportStar(require("./components/table"), exports);
|
|
|
69
70
|
__exportStar(require("./components/tabletoolbar"), exports);
|
|
70
71
|
__exportStar(require("./components/tabs"), exports);
|
|
71
72
|
__exportStar(require("./components/textarea"), exports);
|
|
73
|
+
__exportStar(require("./components/textlink"), exports);
|
|
72
74
|
__exportStar(require("./components/timepicker"), exports);
|
|
73
75
|
__exportStar(require("./components/toast"), exports);
|
|
74
76
|
__exportStar(require("./components/tooltip"), exports);
|
|
77
|
+
__exportStar(require("./components/visuallyhidden"), exports);
|
|
75
78
|
__exportStar(require("./components/wheelpicker"), exports);
|
|
76
79
|
__exportStar(require("./core"), exports);
|
|
77
80
|
// product surfaces (product/) — faithful, pixel-measured rebuilds of specific
|
|
@@ -84,6 +87,7 @@ __exportStar(require("./product/board"), exports);
|
|
|
84
87
|
__exportStar(require("./product/command"), exports);
|
|
85
88
|
__exportStar(require("./product/entitycard"), exports);
|
|
86
89
|
__exportStar(require("./product/flowcanvas"), exports);
|
|
90
|
+
__exportStar(require("./product/headerbar"), exports);
|
|
87
91
|
__exportStar(require("./product/inbox"), exports);
|
|
88
92
|
__exportStar(require("./product/listpage"), exports);
|
|
89
93
|
__exportStar(require("./product/navitem"), exports);
|
|
@@ -44,7 +44,16 @@ const board_recipe_1 = require("./board.recipe");
|
|
|
44
44
|
* (the parent owns the cards, so the move is a controlled state update).
|
|
45
45
|
* Measured from a deals pipeline.
|
|
46
46
|
*/
|
|
47
|
-
function Board({ columns, cards, onCardsChange, renderCard, onNewCard, newCardLabel
|
|
47
|
+
function Board({ columns, cards, onCardsChange, renderCard, onNewCard, newCardLabel: newCardLabelProp, getCardLabel, announceMove: announceMoveProp, className, ...rest }) {
|
|
48
|
+
// crisp/#471: same resolution order as every other component's own —
|
|
49
|
+
// explicit prop, then a CrispMessagesProvider's translation of the English
|
|
50
|
+
// default, then that default itself.
|
|
51
|
+
const newCardLabel = (0, core_1.useCrispMessage)(newCardLabelProp, "New");
|
|
52
|
+
const crispMessages = (0, core_1.useCrispMessages)();
|
|
53
|
+
const announceMove = announceMoveProp ??
|
|
54
|
+
((column, card) => card
|
|
55
|
+
? `${card} ${crispMessages["moved to"] ?? "moved to"} ${column}`
|
|
56
|
+
: `${crispMessages["Moved to"] ?? "Moved to"} ${column}`);
|
|
48
57
|
const [dragId, setDragId] = React.useState(null);
|
|
49
58
|
const [overCol, setOverCol] = React.useState(null);
|
|
50
59
|
const [announcement, setAnnouncement] = React.useState("");
|
|
@@ -101,7 +110,7 @@ function Board({ columns, cards, onCardsChange, renderCard, onNewCard, newCardLa
|
|
|
101
110
|
move(dragId, col.id);
|
|
102
111
|
setDragId(null);
|
|
103
112
|
setOverCol(null);
|
|
104
|
-
}, children: [(0, jsx_runtime_1.jsxs)("div", { className: "crisp-board-col-header", children: [col.accent && ((0, jsx_runtime_1.jsx)("span", { className: "crisp-board-col-dot", style: { background: col.accent } })), (0, jsx_runtime_1.jsx)("span", { className: "crisp-board-col-title", children: col.title }), (0, jsx_runtime_1.jsx)("span", { className: "crisp-board-col-count", children: colCards.length }), onNewCard && ((0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-board-col-add", "aria-label":
|
|
113
|
+
}, children: [(0, jsx_runtime_1.jsxs)("div", { className: "crisp-board-col-header", children: [col.accent && ((0, jsx_runtime_1.jsx)("span", { className: "crisp-board-col-dot", style: { background: col.accent } })), (0, jsx_runtime_1.jsx)("span", { className: "crisp-board-col-title", children: col.title }), (0, jsx_runtime_1.jsx)("span", { className: "crisp-board-col-count", children: colCards.length }), onNewCard && ((0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-board-col-add", "aria-label": `${crispMessages["Add to"] ?? "Add to"} ${col.title}`, onClick: () => onNewCard(col.id), children: "+" }))] }), (0, jsx_runtime_1.jsxs)("div", { className: "crisp-board-col-body", role: "list", "aria-label": col.title, children: [onNewCard && ((0, jsx_runtime_1.jsxs)("button", { type: "button", className: "crisp-board-newcard", onClick: () => onNewCard(col.id), children: ["+ ", newCardLabel] })), colCards.map((card) => ((0, jsx_runtime_1.jsx)("div", { className: "crisp-board-card", role: "listitem",
|
|
105
114
|
// biome-ignore lint/a11y/noNoninteractiveTabindex: the rule assumes a listitem cannot be operated. This one carries the board's only keyboard path to move a card, so it has to be reachable by Tab
|
|
106
115
|
tabIndex: 0, "aria-label": getCardLabel?.(card), "data-card-id": card.id, draggable: true, "data-dragging": dragId === card.id ? "" : undefined, onDragStart: () => setDragId(card.id), onDragEnd: () => {
|
|
107
116
|
setDragId(null);
|
|
@@ -147,7 +147,19 @@ function useCommandInstanceId() {
|
|
|
147
147
|
* option list) so keyboard/focus correctness comes from the machines, not
|
|
148
148
|
* hand-rolled event handling.
|
|
149
149
|
*/
|
|
150
|
-
function Command({ open, onOpenChange, items = exports.defaultCommandItems, placeholder
|
|
150
|
+
function Command({ open, onOpenChange, items = exports.defaultCommandItems, placeholder: placeholderProp, "aria-label": ariaLabelProp, navigateLabel: navigateLabelProp, selectLabel: selectLabelProp, emptyLabel: emptyLabelProp, query: queryProp, onQueryChange, filterItems = true, className, "aria-describedby": describedBy, ...rest }) {
|
|
151
|
+
// crisp/#471: same resolution order as every other component's own.
|
|
152
|
+
// `navigateLabel`/`emptyLabel` are typed `React.ReactNode`, so they
|
|
153
|
+
// resolve by hand rather than through `useCrispMessage`, which is
|
|
154
|
+
// string-only.
|
|
155
|
+
const crispMessages = (0, core_1.useCrispMessages)();
|
|
156
|
+
const placeholder = (0, core_1.useCrispMessage)(placeholderProp, "Search actions and records…");
|
|
157
|
+
const ariaLabel = (0, core_1.useCrispMessage)(ariaLabelProp, "Command palette");
|
|
158
|
+
const navigateLabel = navigateLabelProp ?? crispMessages.Navigate ?? "Navigate";
|
|
159
|
+
const selectLabel = (0, core_1.useCrispMessage)(selectLabelProp, "Select");
|
|
160
|
+
const emptyLabel = emptyLabelProp ??
|
|
161
|
+
crispMessages["No matching commands"] ??
|
|
162
|
+
"No matching commands";
|
|
151
163
|
const instanceId = useCommandInstanceId();
|
|
152
164
|
const dialogId = `${instanceId}-dialog`;
|
|
153
165
|
const comboId = `${instanceId}-combobox`;
|
|
@@ -37,6 +37,7 @@ exports.FlowCanvas = FlowCanvas;
|
|
|
37
37
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
38
38
|
const lucide_react_1 = require("lucide-react");
|
|
39
39
|
const React = __importStar(require("react"));
|
|
40
|
+
const core_1 = require("../../core");
|
|
40
41
|
const flowcanvas_recipe_1 = require("./flowcanvas.recipe");
|
|
41
42
|
const GRID = 20;
|
|
42
43
|
const clampZoom = (z) => Math.min(1.5, Math.max(0.5, z));
|
|
@@ -45,7 +46,13 @@ const clampZoom = (z) => Math.min(1.5, Math.max(0.5, z));
|
|
|
45
46
|
* measured from a workflows editor. Drag a node to move it (fires
|
|
46
47
|
* `onNodesChange`); drag the background to pan; use the toolbar to zoom.
|
|
47
48
|
*/
|
|
48
|
-
function FlowCanvas({ nodes, edges, onNodesChange, renderNode, nodeWidth = 240, nodeHeight = 64, "aria-label":
|
|
49
|
+
function FlowCanvas({ nodes, edges, onNodesChange, renderNode, nodeWidth = 240, nodeHeight = 64, "aria-label": ariaLabelProp, className, zoomOutLabel: zoomOutLabelProp, zoomInLabel: zoomInLabelProp, resetLabel: resetLabelProp, edgesLabel: edgesLabelProp, ...rest }) {
|
|
50
|
+
// crisp/#471: same resolution order as every other component's own.
|
|
51
|
+
const ariaLabel = (0, core_1.useCrispMessage)(ariaLabelProp, "Flow canvas");
|
|
52
|
+
const zoomOutLabel = (0, core_1.useCrispMessage)(zoomOutLabelProp, "Zoom out");
|
|
53
|
+
const zoomInLabel = (0, core_1.useCrispMessage)(zoomInLabelProp, "Zoom in");
|
|
54
|
+
const resetLabel = (0, core_1.useCrispMessage)(resetLabelProp, "Reset");
|
|
55
|
+
const edgesLabel = (0, core_1.useCrispMessage)(edgesLabelProp, "Edges");
|
|
49
56
|
const [pan, setPan] = React.useState({ x: 40, y: 40 });
|
|
50
57
|
const [zoom, setZoom] = React.useState(1);
|
|
51
58
|
const [panning, setPanning] = React.useState(false);
|
|
@@ -100,7 +107,7 @@ function FlowCanvas({ nodes, edges, onNodesChange, renderNode, nodeWidth = 240,
|
|
|
100
107
|
...rest.style,
|
|
101
108
|
backgroundSize: `${GRID * zoom}px ${GRID * zoom}px`,
|
|
102
109
|
backgroundPosition: `${pan.x}px ${pan.y}px`,
|
|
103
|
-
}, role: "application", "aria-label": ariaLabel
|
|
110
|
+
}, role: "application", "aria-label": ariaLabel, onPointerDown: onBackgroundPointerDown, onPointerMove: onPointerMove, onPointerUp: onPointerUp, onPointerCancel: onPointerUp, children: [(0, jsx_runtime_1.jsxs)("div", { className: "crisp-flowcanvas-viewport", style: {
|
|
104
111
|
transform: `translate(${pan.x}px, ${pan.y}px) scale(${zoom})`,
|
|
105
112
|
}, children: [(0, jsx_runtime_1.jsxs)("svg", { className: "crisp-flowcanvas-edges", "aria-hidden": "true", children: [(0, jsx_runtime_1.jsx)("title", { children: edgesLabel }), edges.map((edge) => {
|
|
106
113
|
const a = byId.get(edge.from);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.HeaderBar = HeaderBar;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const headerbar_recipe_1 = require("./headerbar.recipe");
|
|
6
|
+
/**
|
|
7
|
+
* A page-level header row: an optional leading slot, a flexible title/
|
|
8
|
+
* breadcrumb region, and a trailing action cluster, on the measured 48px
|
|
9
|
+
* bar Attio uses for both its page-header and its view-bar row. See the
|
|
10
|
+
* recipe's own doc comment for the live measurement (crisp#462).
|
|
11
|
+
*/
|
|
12
|
+
function HeaderBar({ leading, title, trailing, bordered = true, className, ...rest }) {
|
|
13
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: (0, headerbar_recipe_1.headerbarRecipe)({ className }), "data-bordered": bordered ? "" : undefined, ...rest, children: [leading ? ((0, jsx_runtime_1.jsx)("span", { className: "crisp-headerbar-leading", children: leading })) : null, title ? (0, jsx_runtime_1.jsx)("div", { className: "crisp-headerbar-title", children: title }) : null, trailing ? ((0, jsx_runtime_1.jsx)("span", { className: "crisp-headerbar-trailing", children: trailing })) : null] }));
|
|
14
|
+
}
|
|
15
|
+
HeaderBar.displayName = "HeaderBar";
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.headerbarRecipe = void 0;
|
|
4
|
+
const core_1 = require("../../core");
|
|
5
|
+
const tokens_1 = require("../../tokens");
|
|
6
|
+
// A page-level header row — measured live at app.attio.com/techpartners/companies
|
|
7
|
+
// against two stacked bars: the top utility bar (breadcrumb + trailing icon
|
|
8
|
+
// actions, e.g. search/notifications) and the view bar directly under it
|
|
9
|
+
// ("All Companies" view name + tabs + "New Company"). Both share the same
|
|
10
|
+
// 48px content height and `0 ${space3}` padding; only the outer (first) bar
|
|
11
|
+
// carries the divider -- measured `border-bottom: 1px solid rgb(238,239,241)`
|
|
12
|
+
// (`borderSubtle`/`line100`, exact match) on the utility bar, no border of
|
|
13
|
+
// its own on the view bar directly beneath it. `bordered` (default true)
|
|
14
|
+
// lets a consumer that stacks two HeaderBars turn it off on the inner one,
|
|
15
|
+
// the way GRC's own AppLayout UtilityBar + Page viewBar do today with two
|
|
16
|
+
// hand-rolled, drifting copies of the same 49px bar (crisp#462 GRC audit,
|
|
17
|
+
// crisp#527).
|
|
18
|
+
//
|
|
19
|
+
// No bell, no breadcrumb, no search box baked in -- those are the `leading`/
|
|
20
|
+
// `title`/`trailing` slot content a consumer supplies. `size.header` (48px)
|
|
21
|
+
// is reused rather than a new token, since it's the same "workspace-switcher
|
|
22
|
+
// header height" measured elsewhere in the sidebar.
|
|
23
|
+
const config = (0, core_1.defineRecipe)({
|
|
24
|
+
name: "headerbar",
|
|
25
|
+
base: `box-sizing: border-box; display: flex; align-items: center; justify-content: space-between; gap: ${tokens_1.contract.space["2"]}; height: ${tokens_1.contract.size.header}; flex-shrink: 0; padding: 0 ${tokens_1.contract.space["3"]}; font-family: ${tokens_1.contract.font.sans}; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
|
|
26
|
+
&[data-bordered] { border-bottom: 1px solid ${tokens_1.contract.borderSubtle}; }
|
|
27
|
+
& .crisp-headerbar-leading { display: flex; align-items: center; gap: ${tokens_1.contract.space["2"]}; flex-shrink: 0; min-width: 0; }
|
|
28
|
+
& .crisp-headerbar-title { flex: 1; min-width: 0; display: flex; align-items: center; gap: ${tokens_1.contract.space["2"]}; overflow: hidden; }
|
|
29
|
+
& .crisp-headerbar-trailing { display: flex; align-items: center; gap: ${tokens_1.contract.space["2"]}; flex-shrink: 0; }`,
|
|
30
|
+
variants: {},
|
|
31
|
+
defaultVariants: {},
|
|
32
|
+
compoundVariants: [],
|
|
33
|
+
});
|
|
34
|
+
exports.headerbarRecipe = (0, core_1.resolveRecipe)(config);
|
|
35
|
+
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("./headerbar"), exports);
|
|
@@ -38,6 +38,7 @@ const jsx_runtime_1 = require("react/jsx-runtime");
|
|
|
38
38
|
const lucide_react_1 = require("lucide-react");
|
|
39
39
|
const React = __importStar(require("react"));
|
|
40
40
|
const tabs_1 = require("../../components/tabs");
|
|
41
|
+
const core_1 = require("../../core");
|
|
41
42
|
const inbox_recipe_1 = require("./inbox.recipe");
|
|
42
43
|
/**
|
|
43
44
|
* A notifications inbox panel — measured from a notification flyout. A
|
|
@@ -45,8 +46,19 @@ const inbox_recipe_1 = require("./inbox.recipe");
|
|
|
45
46
|
* (avatar + message + time + unread dot) or a centred empty state. Put it inside
|
|
46
47
|
* a Popover for the flyout.
|
|
47
48
|
*/
|
|
48
|
-
function Inbox({ notifications = [], title
|
|
49
|
+
function Inbox({ notifications = [], title: titleProp, tabs, activeTab, onTabChange, emptyTitle: emptyTitleProp, emptySubtitle: emptySubtitleProp, emptyAction, tabsLabel: tabsLabelProp, className, ...rest }) {
|
|
49
50
|
const listRef = React.useRef(null);
|
|
51
|
+
// crisp/#471: `title`/`emptyTitle`/`emptySubtitle` are typed
|
|
52
|
+
// `React.ReactNode`, so they resolve by hand rather than through
|
|
53
|
+
// `useCrispMessage`, which is string-only — same reasoning as DataTable's
|
|
54
|
+
// own `empty`.
|
|
55
|
+
const crispMessages = (0, core_1.useCrispMessages)();
|
|
56
|
+
const title = titleProp ?? crispMessages.Notifications ?? "Notifications";
|
|
57
|
+
const emptyTitle = emptyTitleProp ?? crispMessages["No notifications"] ?? "No notifications";
|
|
58
|
+
const emptySubtitle = emptySubtitleProp ??
|
|
59
|
+
crispMessages["Notifications such as mentions and important updates will be displayed here."] ??
|
|
60
|
+
"Notifications such as mentions and important updates will be displayed here.";
|
|
61
|
+
const tabsLabel = (0, core_1.useCrispMessage)(tabsLabelProp, "Notification views");
|
|
50
62
|
// crisp's shared tablist model, the one `Tabs` runs (#195). Hand-rolling a
|
|
51
63
|
// second one is how Inbox ended up with no arrow keys, no roving tabindex
|
|
52
64
|
// and no name while `Tabs` had all three.
|
|
@@ -4,6 +4,7 @@ exports.onboardingLabels = void 0;
|
|
|
4
4
|
exports.OnboardingChecklist = OnboardingChecklist;
|
|
5
5
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
6
6
|
const lucide_react_1 = require("lucide-react");
|
|
7
|
+
const core_1 = require("../../core");
|
|
7
8
|
const onboarding_recipe_1 = require("./onboarding.recipe");
|
|
8
9
|
/**
|
|
9
10
|
* The English defaults for the two pieces of copy that are formatters rather
|
|
@@ -19,9 +20,29 @@ exports.onboardingLabels = {
|
|
|
19
20
|
* started" widget. Title + optional menu/minimize, a segmented progress bar,
|
|
20
21
|
* and step rows (completed rows strike through with a brand check).
|
|
21
22
|
*/
|
|
22
|
-
function OnboardingChecklist({ title
|
|
23
|
+
function OnboardingChecklist({ title: titleProp, steps, encouragement: encouragementProp, onStepClick, onMenu, onMinimize, className, optionsLabel: optionsLabelProp, minimizeLabel: minimizeLabelProp, progressLabel: progressLabelProp, progressText: progressTextProp, completedStepLabel: completedStepLabelProp, ...rest }) {
|
|
23
24
|
const total = steps.length;
|
|
24
25
|
const done = steps.filter((s) => s.done).length;
|
|
26
|
+
// crisp/#471: same resolution order as every other component's own —
|
|
27
|
+
// explicit prop, then a CrispMessagesProvider's translation of the English
|
|
28
|
+
// default, then that default itself. `title`/`encouragement` are typed
|
|
29
|
+
// `React.ReactNode` (a caller can pass a translated element), so they
|
|
30
|
+
// resolve by hand rather than through `useCrispMessage`, which is
|
|
31
|
+
// string-only — same reasoning as DataTable's own `empty`.
|
|
32
|
+
const crispMessages = (0, core_1.useCrispMessages)();
|
|
33
|
+
const title = titleProp ?? crispMessages["Getting started"] ?? "Getting started";
|
|
34
|
+
const encouragement = encouragementProp ?? crispMessages["Keep it up!"] ?? "Keep it up!";
|
|
35
|
+
const optionsLabel = (0, core_1.useCrispMessage)(optionsLabelProp, "Checklist options");
|
|
36
|
+
const minimizeLabel = (0, core_1.useCrispMessage)(minimizeLabelProp, "Minimize");
|
|
37
|
+
const progressLabel = (0, core_1.useCrispMessage)(progressLabelProp, "Onboarding progress");
|
|
38
|
+
// `progressText`/`completedStepLabel` are formatters, not strings (the
|
|
39
|
+
// count moves within the sentence between languages, #216) — each of
|
|
40
|
+
// their own static fragments gets its own lookup, same as
|
|
41
|
+
// TableToolbar's "Sorted by" (crisp/#217).
|
|
42
|
+
const progressText = progressTextProp ??
|
|
43
|
+
((doneCount, totalCount) => ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)("b", { children: [doneCount, "/", totalCount] }), " ", crispMessages["steps completed"] ?? "steps completed"] })));
|
|
44
|
+
const completedStepLabel = completedStepLabelProp ??
|
|
45
|
+
((label) => `${String(label)} ${crispMessages["— completed"] ?? "— completed"}`);
|
|
25
46
|
return ((0, jsx_runtime_1.jsxs)("div", { className: (0, onboarding_recipe_1.onboardingRecipe)({ className }), ...rest, children: [(0, jsx_runtime_1.jsxs)("div", { className: "crisp-onboarding-head", children: [(0, jsx_runtime_1.jsx)("span", { className: "crisp-onboarding-title", children: title }), onMenu && ((0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-onboarding-iconbtn", "aria-label": optionsLabel, onClick: onMenu, children: (0, jsx_runtime_1.jsx)(lucide_react_1.MoreHorizontal, { size: 16, strokeWidth: 1.75 }) })), onMinimize && ((0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-onboarding-iconbtn", "aria-label": minimizeLabel, onClick: onMinimize, children: (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronDown, { size: 16, strokeWidth: 1.75 }) }))] }), (0, jsx_runtime_1.jsxs)("span", { className: "crisp-onboarding-sub", children: [progressText(done, total), encouragement ? (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [" \u00B7 ", encouragement] }) : null] }), (0, jsx_runtime_1.jsx)("div", { className: "crisp-onboarding-progress", role: "progressbar", "aria-valuenow": done, "aria-valuemin": 0, "aria-valuemax": total, "aria-label": progressLabel, children: Array.from({ length: total }, (_, i) => ((0, jsx_runtime_1.jsx)("span", { className: "crisp-onboarding-seg", "data-done": i < done ? "" : undefined }, i))) }), (0, jsx_runtime_1.jsx)("div", { className: "crisp-onboarding-steps", children: steps.map((s) => ((0, jsx_runtime_1.jsxs)("button", { type: "button", className: "crisp-onboarding-step", "data-done": s.done ? "" : undefined, "aria-label": s.done ? completedStepLabel(s.label) : undefined, disabled: s.done, onClick: s.done ? undefined : () => onStepClick?.(s.id), children: [(0, jsx_runtime_1.jsx)("span", { className: "crisp-onboarding-step-icon", children: s.icon }), (0, jsx_runtime_1.jsx)("span", { className: "crisp-onboarding-step-label", children: s.label }), (0, jsx_runtime_1.jsx)("span", { className: "crisp-onboarding-step-check", children: s.done && (0, jsx_runtime_1.jsx)(lucide_react_1.Check, { size: 12, strokeWidth: 3 }) })] }, s.id))) })] }));
|
|
26
47
|
}
|
|
27
48
|
OnboardingChecklist.displayName = "OnboardingChecklist";
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.recordpageRecipe = exports.recordPageLabels = exports.RecordPage = void 0;
|
|
3
|
+
exports.recordpageRecipe = exports.recordPageLabels = exports.RecordPage = exports.RecordDetails = void 0;
|
|
4
4
|
var recordpage_1 = require("./recordpage");
|
|
5
|
+
Object.defineProperty(exports, "RecordDetails", { enumerable: true, get: function () { return recordpage_1.RecordDetails; } });
|
|
5
6
|
Object.defineProperty(exports, "RecordPage", { enumerable: true, get: function () { return recordpage_1.RecordPage; } });
|
|
6
7
|
Object.defineProperty(exports, "recordPageLabels", { enumerable: true, get: function () { return recordpage_1.recordPageLabels; } });
|
|
7
8
|
var recordpage_recipe_1 = require("./recordpage.recipe");
|