@crisp-ui-kit/crisp 0.17.0 → 0.19.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/columncalc/columncalc.js +2 -2
- package/dist/cjs/components/datepickerfield/datepickerfield.js +3 -1
- package/dist/cjs/components/multiselect/multiselect.js +2 -2
- package/dist/cjs/components/select/select.js +2 -2
- package/dist/cjs/components/tabletoolbar/tabletoolbar.js +4 -4
- package/dist/cjs/product/recordpage/recordpage.js +8 -3
- package/dist/cjs/product/recordstable/recordstable.js +6 -3
- package/dist/cjs/product/sidebar/sidebar.js +2 -2
- package/dist/cjs/product/viewtoggle/viewtoggle.js +4 -1
- package/dist/esm/components/columncalc/columncalc.js +2 -2
- package/dist/esm/components/datepickerfield/datepickerfield.js +3 -1
- package/dist/esm/components/multiselect/multiselect.js +2 -2
- package/dist/esm/components/select/select.js +2 -2
- package/dist/esm/components/tabletoolbar/tabletoolbar.js +4 -4
- package/dist/esm/product/recordpage/recordpage.js +8 -3
- package/dist/esm/product/recordstable/recordstable.js +6 -3
- package/dist/esm/product/sidebar/sidebar.js +2 -2
- package/dist/esm/product/viewtoggle/viewtoggle.js +4 -1
- package/dist/types/components/columncalc/columncalc.d.ts +5 -1
- package/dist/types/components/datepickerfield/datepickerfield.d.ts +9 -1
- package/dist/types/components/multiselect/multiselect.d.ts +3 -1
- package/dist/types/components/select/select.d.ts +3 -1
- package/dist/types/components/tabletoolbar/tabletoolbar.d.ts +9 -1
- package/dist/types/product/recordpage/recordpage.d.ts +10 -0
- package/dist/types/product/recordstable/recordstable.d.ts +6 -0
- package/dist/types/product/sidebar/sidebar.d.ts +4 -0
- package/dist/types/product/viewtoggle/viewtoggle.d.ts +10 -4
- package/package.json +1 -1
|
@@ -117,7 +117,7 @@ function computeCalc(values, op) {
|
|
|
117
117
|
const defaultFormat = (n, op) => op === "percentEmpty" || op === "percentFilled"
|
|
118
118
|
? `${Math.round(n)}%`
|
|
119
119
|
: n.toLocaleString(undefined, { maximumFractionDigits: 2 });
|
|
120
|
-
function ColumnCalc({ rows, value, numeric = false, defaultOp = "none", op: opProp, onOpChange, format = defaultFormat, align, opLabels, className, ...rest }) {
|
|
120
|
+
function ColumnCalc({ rows, value, numeric = false, defaultOp = "none", op: opProp, onOpChange, addLabel = "Add calculation", sectionLabel = "Aggregations", format = defaultFormat, align, opLabels, className, ...rest }) {
|
|
121
121
|
// a partial override, so a consumer can translate one op without
|
|
122
122
|
// restating all nine
|
|
123
123
|
const labelFor = (o) => opLabels?.[o] ?? exports.CALC_LABEL[o];
|
|
@@ -135,5 +135,5 @@ function ColumnCalc({ rows, value, numeric = false, defaultOp = "none", op: opPr
|
|
|
135
135
|
...(numeric ? exports.NUMERIC_CALCS : []),
|
|
136
136
|
"none",
|
|
137
137
|
];
|
|
138
|
-
return ((0, jsx_runtime_1.jsx)("span", { className: (0, columncalc_recipe_1.columncalcRecipe)({ className }), ...rest, children: (0, jsx_runtime_1.jsxs)(menu_1.Menu, { trigger: (0, jsx_runtime_1.jsx)("button", { type: "button", className: "columncalc-trigger", "data-align": align === "right" ? "right" : undefined, children: op === "none" || result === null ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Plus, { size: 12, strokeWidth: 2 }), (0, jsx_runtime_1.jsx)("span", { className: "columncalc-placeholder", children:
|
|
138
|
+
return ((0, jsx_runtime_1.jsx)("span", { className: (0, columncalc_recipe_1.columncalcRecipe)({ className }), ...rest, children: (0, jsx_runtime_1.jsxs)(menu_1.Menu, { trigger: (0, jsx_runtime_1.jsx)("button", { type: "button", className: "columncalc-trigger", "data-align": align === "right" ? "right" : undefined, children: op === "none" || result === null ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Plus, { size: 12, strokeWidth: 2 }), (0, jsx_runtime_1.jsx)("span", { className: "columncalc-placeholder", children: addLabel })] })) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("span", { className: "columncalc-value", children: format(result, op) }), (0, jsx_runtime_1.jsx)("span", { className: "columncalc-op", children: labelFor(op) }), (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronDown, { size: 12, strokeWidth: 2 })] })) }), children: [(0, jsx_runtime_1.jsx)("div", { className: "columncalc-section", children: sectionLabel }), ops.map((o) => ((0, jsx_runtime_1.jsx)(menu_1.MenuItem, { onSelect: () => setOp(o), children: labelFor(o) }, o)))] }) }));
|
|
139
139
|
}
|
|
@@ -57,13 +57,15 @@ const fromDate = (d) => ({
|
|
|
57
57
|
* popover. This is what `DatePicker` used to be; `DatePicker` is now
|
|
58
58
|
* seed-design's bare calendar, and this composes it.
|
|
59
59
|
*/
|
|
60
|
-
function DatePickerField({ value, defaultValue, onChange, placeholder = "Set date…", variant = "default", format = defaultFormat, presets, calendarProps, "aria-label": ariaLabel, className, ...rest }) {
|
|
60
|
+
function DatePickerField({ value, defaultValue, onValueChange, onChange, placeholder = "Set date…", variant = "default", format = defaultFormat, presets, calendarProps, "aria-label": ariaLabel, className, ...rest }) {
|
|
61
61
|
const [internal, setInternal] = React.useState(defaultValue ?? null);
|
|
62
62
|
const selected = value ?? internal;
|
|
63
63
|
const [open, setOpen] = React.useState(false);
|
|
64
64
|
const pick = (d) => {
|
|
65
65
|
if (value === undefined)
|
|
66
66
|
setInternal(d);
|
|
67
|
+
// both, so a consumer passing either name is heard
|
|
68
|
+
onValueChange?.(d);
|
|
67
69
|
onChange?.(d);
|
|
68
70
|
setOpen(false);
|
|
69
71
|
};
|
|
@@ -64,7 +64,7 @@ function Chip({ opt }) {
|
|
|
64
64
|
* popover with a search field over rows of "[chip] … [checkbox]" that toggle on
|
|
65
65
|
* click (the menu stays open). Keyboard: ↑/↓ move, Enter toggles, Esc closes.
|
|
66
66
|
*/
|
|
67
|
-
function MultiSelect({ options, value: valueProp, defaultValue, onValueChange, placeholder = "Select…", searchable = true, searchPlaceholder = "Search…", disabled, className, "aria-label": ariaLabel, ...rest }) {
|
|
67
|
+
function MultiSelect({ options, value: valueProp, defaultValue, onValueChange, placeholder = "Select…", searchable = true, searchPlaceholder = "Search…", emptyLabel = "No results", disabled, className, "aria-label": ariaLabel, ...rest }) {
|
|
68
68
|
// Virtual focus: DOM focus stays on the trigger (or the search field) and the
|
|
69
69
|
// active option is named by `aria-activedescendant`. `data-highlighted` is a
|
|
70
70
|
// styling hook only -- a screen reader cannot see it.
|
|
@@ -161,7 +161,7 @@ function MultiSelect({ options, value: valueProp, defaultValue, onValueChange, p
|
|
|
161
161
|
}, children: [(0, jsx_runtime_1.jsx)("span", { className: "multiselect-chips", children: selectedOptions.length === 0 ? ((0, jsx_runtime_1.jsx)("span", { className: "multiselect-placeholder", children: placeholder })) : (selectedOptions.map((o) => (0, jsx_runtime_1.jsx)(Chip, { opt: o }, o.value))) }), (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronDown, { className: "multiselect-chevron", size: 16, strokeWidth: 1.75 })] }), open && ((0, jsx_runtime_1.jsxs)("div", { id: listboxId, className: "multiselect-listbox", role: "listbox", "aria-multiselectable": "true", "aria-label": ariaLabel, "data-up": up ? "" : undefined, style: pos, children: [searchable && ((0, jsx_runtime_1.jsxs)("div", { className: "multiselect-search", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Search, { size: 15, strokeWidth: 1.75 }), (0, jsx_runtime_1.jsx)("input", { ref: searchRef, type: "text", placeholder: searchPlaceholder, "aria-label": searchPlaceholder, "aria-controls": listboxId, "aria-activedescendant": activeId, value: query, onChange: (e) => {
|
|
162
162
|
setQuery(e.target.value);
|
|
163
163
|
setHighlight(0);
|
|
164
|
-
}, onKeyDown: onNavKeys })] })), filtered.length === 0 ? ((0, jsx_runtime_1.jsx)("div", { className: "multiselect-empty", children:
|
|
164
|
+
}, onKeyDown: onNavKeys })] })), filtered.length === 0 ? ((0, jsx_runtime_1.jsx)("div", { className: "multiselect-empty", children: emptyLabel })) : (filtered.map((opt, i) => {
|
|
165
165
|
const sel = selectedSet.has(opt.value);
|
|
166
166
|
return ((0, jsx_runtime_1.jsxs)("button", { type: "button", id: optionId(i), role: "option", "aria-selected": sel, tabIndex: -1, "data-highlighted": i === highlight ? "" : undefined, className: "multiselect-option", onClick: () => toggle(opt.value), onMouseEnter: () => setHighlight(i), children: [(0, jsx_runtime_1.jsx)("span", { className: "multiselect-option-chip", children: (0, jsx_runtime_1.jsx)(Chip, { opt: opt }) }), (0, jsx_runtime_1.jsx)("span", { className: "multiselect-check", children: sel && (0, jsx_runtime_1.jsx)(lucide_react_1.Check, { size: 12, strokeWidth: 3 }) })] }, opt.value));
|
|
167
167
|
}))] }))] }));
|
|
@@ -51,7 +51,7 @@ function optionText(o) {
|
|
|
51
51
|
* "No results" empty state). Full keyboard model — ↑/↓ move, Enter/Space pick,
|
|
52
52
|
* Esc close, Home/End jump, opens upward when tight. Controlled or uncontrolled.
|
|
53
53
|
*/
|
|
54
|
-
function Select({ options, value, defaultValue, onValueChange, placeholder = "Select…", variant = "default", width = "full", searchable, searchPlaceholder = "Search…", disabled, className, "aria-label": ariaLabel, id, ...rest }) {
|
|
54
|
+
function Select({ options, value, defaultValue, onValueChange, placeholder = "Select…", variant = "default", width = "full", searchable, searchPlaceholder = "Search…", emptyLabel = "No results", disabled, className, "aria-label": ariaLabel, id, ...rest }) {
|
|
55
55
|
// Virtual focus: DOM focus stays on the trigger (or the search field), and the
|
|
56
56
|
// active option is named by `aria-activedescendant`. That is the only thing a
|
|
57
57
|
// screen reader has to go on -- `data-highlighted` is invisible to it -- so the
|
|
@@ -150,7 +150,7 @@ function Select({ options, value, defaultValue, onValueChange, placeholder = "Se
|
|
|
150
150
|
return ((0, jsx_runtime_1.jsxs)("div", { ref: rootRef, className: (0, select_recipe_1.selectRecipe)({ variant, width, className }), ...rest, children: [(0, jsx_runtime_1.jsxs)("button", { ref: triggerRef, type: "button", id: id, role: "combobox", "aria-haspopup": "listbox", "aria-expanded": open, "aria-controls": open ? listboxId : undefined, "aria-activedescendant": searchable ? undefined : activeId, "aria-label": ariaLabel, disabled: disabled, "data-open": open ? "" : undefined, className: "select-trigger", onClick: () => (open ? setOpen(false) : openMenu()), onKeyDown: onTriggerKeyDown, children: [(0, jsx_runtime_1.jsx)("span", { className: "select-value", children: selectedOption ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [selectedOption.color && ((0, jsx_runtime_1.jsx)("span", { className: "select-option-dot", style: { background: selectedOption.color } })), selectedOption.label] })) : ((0, jsx_runtime_1.jsx)("span", { className: "select-placeholder", children: placeholder })) }), (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronDown, { className: "select-chevron", size: 16, strokeWidth: 1.75 })] }), open && ((0, jsx_runtime_1.jsxs)("div", { id: listboxId, className: "select-listbox", role: "listbox", "aria-label": ariaLabel, "data-up": up ? "" : undefined, style: pos, children: [searchable && ((0, jsx_runtime_1.jsxs)("div", { className: "select-search", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Search, { size: 15, strokeWidth: 1.75 }), (0, jsx_runtime_1.jsx)("input", { ref: searchRef, type: "text", placeholder: searchPlaceholder, "aria-label": searchPlaceholder, "aria-controls": listboxId, "aria-activedescendant": activeId, value: query, onChange: (e) => {
|
|
151
151
|
setQuery(e.target.value);
|
|
152
152
|
setHighlight(0);
|
|
153
|
-
}, onKeyDown: onNavKeys })] })), filtered.length === 0 ? ((0, jsx_runtime_1.jsx)("div", { className: "select-empty", children:
|
|
153
|
+
}, onKeyDown: onNavKeys })] })), filtered.length === 0 ? ((0, jsx_runtime_1.jsx)("div", { className: "select-empty", children: emptyLabel })) : (filtered.map((opt, i) => {
|
|
154
154
|
const isSel = opt.value === selected;
|
|
155
155
|
return ((0, jsx_runtime_1.jsxs)("button", { type: "button", id: optionId(i), role: "option", "aria-selected": isSel, tabIndex: -1, disabled: opt.disabled, "data-disabled": opt.disabled ? "" : undefined, "data-highlighted": i === highlight ? "" : undefined, className: "select-option", onClick: () => pick(opt), onMouseEnter: () => setHighlight(i), children: [opt.color && ((0, jsx_runtime_1.jsx)("span", { className: "select-option-dot", style: { background: opt.color } })), (0, jsx_runtime_1.jsx)("span", { className: "select-option-label", children: opt.label }), isSel && ((0, jsx_runtime_1.jsx)(lucide_react_1.Check, { className: "select-option-check", size: 15, strokeWidth: 2 }))] }, opt.value));
|
|
156
156
|
}))] }))] }));
|
|
@@ -134,7 +134,7 @@ function applySorts(rows, sorts, get = (row, key) => row[key]) {
|
|
|
134
134
|
* composable version, to put above `DataTable` or any table of your own. Pair
|
|
135
135
|
* it with `applyFilters` / `applySorts` to turn its state into rows.
|
|
136
136
|
*/
|
|
137
|
-
function TableToolbar({ columns, sorts: sortsProp, defaultSorts, onSortsChange, filters: filtersProp, defaultFilters, onFiltersChange, count, children, helpHref, className, sortLabel = "Sort", reorderSortLabel = "Reorder sort", sortFieldLabel = "Sort field", sortDirectionLabel = "Sort direction", removeSortLabel = "Remove sort", filterFieldLabel = "Filter by field", filterFieldPlaceholder = "Filter by…", filterValuePlaceholder = "Value…", ascendingLabel = "Ascending", descendingLabel = "Descending", sortTriggerLabel, filterTriggerLabel, ...rest }) {
|
|
137
|
+
function TableToolbar({ columns, sorts: sortsProp, defaultSorts, onSortsChange, filters: filtersProp, defaultFilters, onFiltersChange, count, children, helpHref, className, sortLabel = "Sort", reorderSortLabel = "Reorder sort", noSortsLabel = "No sorts applied to this view.", addSortLabel = "Add sort", learnSortingLabel = "Learn about sorting", selectConditionLabel = "Select condition", sortFieldLabel = "Sort field", sortDirectionLabel = "Sort direction", removeSortLabel = "Remove sort", filterFieldLabel = "Filter by field", filterFieldPlaceholder = "Filter by…", filterValuePlaceholder = "Value…", ascendingLabel = "Ascending", descendingLabel = "Descending", sortTriggerLabel, filterTriggerLabel, ...rest }) {
|
|
138
138
|
const [sortsState, setSortsState] = React.useState(defaultSorts ?? []);
|
|
139
139
|
const [filtersState, setFiltersState] = React.useState(defaultFilters ?? []);
|
|
140
140
|
const sorts = sortsProp ?? sortsState;
|
|
@@ -206,7 +206,7 @@ function TableToolbar({ columns, sorts: sortsProp, defaultSorts, onSortsChange,
|
|
|
206
206
|
: sorts.length
|
|
207
207
|
? `Sorted by ${columns.find((c) => c.key === sorts[0].key)?.label}${sorts.length > 1 ? ` +${sorts.length - 1}` : ""}`
|
|
208
208
|
: "Sort";
|
|
209
|
-
return ((0, jsx_runtime_1.jsx)("div", { className: (0, tabletoolbar_recipe_1.tabletoolbarRecipe)({ className }), ...rest, children: (0, jsx_runtime_1.jsxs)("div", { className: "tabletoolbar-toolbar", children: [(0, jsx_runtime_1.jsxs)("span", { className: "tabletoolbar-toolgroup", children: [(0, jsx_runtime_1.jsxs)("button", { type: "button", className: `tabletoolbar-tool${sorts.length ? " tabletoolbar-tool--active" : ""}`, onClick: () => setOpenMenu(openMenu === "sort" ? null : "sort"), "aria-haspopup": "dialog", "aria-expanded": openMenu === "sort", "data-menu-trigger": true, children: [(0, jsx_runtime_1.jsx)(lucide_react_1.ArrowUpDown, { size: 14, strokeWidth: 1.75, "aria-hidden": "true" }), sortedLabel] }), openMenu === "sort" && ((0, jsx_runtime_1.jsxs)("div", { className: "tabletoolbar-menu tabletoolbar-sortpop", role: "dialog", "aria-label": sortLabel, children: [sorts.length === 0 ? ((0, jsx_runtime_1.jsx)("div", { className: "tabletoolbar-sort-empty", children:
|
|
209
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: (0, tabletoolbar_recipe_1.tabletoolbarRecipe)({ className }), ...rest, children: (0, jsx_runtime_1.jsxs)("div", { className: "tabletoolbar-toolbar", children: [(0, jsx_runtime_1.jsxs)("span", { className: "tabletoolbar-toolgroup", children: [(0, jsx_runtime_1.jsxs)("button", { type: "button", className: `tabletoolbar-tool${sorts.length ? " tabletoolbar-tool--active" : ""}`, onClick: () => setOpenMenu(openMenu === "sort" ? null : "sort"), "aria-haspopup": "dialog", "aria-expanded": openMenu === "sort", "data-menu-trigger": true, children: [(0, jsx_runtime_1.jsx)(lucide_react_1.ArrowUpDown, { size: 14, strokeWidth: 1.75, "aria-hidden": "true" }), sortedLabel] }), openMenu === "sort" && ((0, jsx_runtime_1.jsxs)("div", { className: "tabletoolbar-menu tabletoolbar-sortpop", role: "dialog", "aria-label": sortLabel, children: [sorts.length === 0 ? ((0, jsx_runtime_1.jsx)("div", { className: "tabletoolbar-sort-empty", children: noSortsLabel })) : (sorts.map((s, i) => ((0, jsx_runtime_1.jsxs)("div", { className: "tabletoolbar-sort-row", onDragOver: (e) => {
|
|
210
210
|
if (dragSort !== null)
|
|
211
211
|
e.preventDefault();
|
|
212
212
|
}, onDrop: () => {
|
|
@@ -228,7 +228,7 @@ function TableToolbar({ columns, sorts: sortsProp, defaultSorts, onSortsChange,
|
|
|
228
228
|
label: ((0, jsx_runtime_1.jsxs)("span", { className: "tabletoolbar-sort-optlabel", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.ArrowDownWideNarrow, { size: 15, strokeWidth: 1.75 }), descendingLabel] })),
|
|
229
229
|
keywords: descendingLabel,
|
|
230
230
|
},
|
|
231
|
-
], "aria-label": sortDirectionLabel }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "tabletoolbar-sort-remove", onClick: () => removeSort(i), "aria-label": removeSortLabel, children: (0, jsx_runtime_1.jsx)(lucide_react_1.X, { size: 15, strokeWidth: 1.75 }) })] }, s.key)))), (0, jsx_runtime_1.jsx)("div", { className: "tabletoolbar-sort-addrow", children: (0, jsx_runtime_1.jsxs)("button", { type: "button", className: "tabletoolbar-sort-add", onClick: addSort, children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Plus, { size: 15, strokeWidth: 1.75 }),
|
|
231
|
+
], "aria-label": sortDirectionLabel }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "tabletoolbar-sort-remove", onClick: () => removeSort(i), "aria-label": removeSortLabel, children: (0, jsx_runtime_1.jsx)(lucide_react_1.X, { size: 15, strokeWidth: 1.75 }) })] }, s.key)))), (0, jsx_runtime_1.jsx)("div", { className: "tabletoolbar-sort-addrow", children: (0, jsx_runtime_1.jsxs)("button", { type: "button", className: "tabletoolbar-sort-add", onClick: addSort, children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Plus, { size: 15, strokeWidth: 1.75 }), addSortLabel] }) }), helpHref && ((0, jsx_runtime_1.jsx)("div", { className: "tabletoolbar-sort-foot", children: (0, jsx_runtime_1.jsxs)("a", { className: "tabletoolbar-sort-learn", href: helpHref, target: "_blank", rel: "noreferrer", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.CircleHelp, { size: 14, strokeWidth: 1.75 }), learnSortingLabel] }) }))] }))] }), (0, jsx_runtime_1.jsxs)("span", { className: "tabletoolbar-toolgroup", children: [(0, jsx_runtime_1.jsxs)("button", { type: "button", className: `tabletoolbar-tool tabletoolbar-tool--ghost${activeFilters ? " tabletoolbar-tool--active" : ""}`, onClick: () => setOpenMenu(openMenu === "filter" ? null : "filter"), "aria-haspopup": "dialog", "aria-expanded": openMenu === "filter", "data-menu-trigger": true, children: [(0, jsx_runtime_1.jsx)(lucide_react_1.ListFilter, { size: 14, strokeWidth: 1.75, "aria-hidden": "true" }), filterTriggerLabel
|
|
232
232
|
? filterTriggerLabel(activeFilters)
|
|
233
233
|
: activeFilters
|
|
234
234
|
? `Filter · ${activeFilters}`
|
|
@@ -242,7 +242,7 @@ function TableToolbar({ columns, sorts: sortsProp, defaultSorts, onSortsChange,
|
|
|
242
242
|
const col = columns.find((c) => c.key === cond.key);
|
|
243
243
|
const open = openMenu === `fcond:${cond.id}`;
|
|
244
244
|
const hasValue = !opNeedsValue(cond.op) || cond.value.trim();
|
|
245
|
-
return ((0, jsx_runtime_1.jsxs)("span", { className: "tabletoolbar-toolgroup", children: [(0, jsx_runtime_1.jsxs)("span", { className: "tabletoolbar-fchip", children: [(0, jsx_runtime_1.jsxs)("button", { type: "button", className: "tabletoolbar-fchip-main", "data-menu-trigger": true, "aria-haspopup": "dialog", onClick: () => setOpenMenu(open ? null : `fcond:${cond.id}`), children: [(0, jsx_runtime_1.jsx)("span", { className: "tabletoolbar-fchip-field", children: col?.label }), hasValue ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("span", { className: "tabletoolbar-fchip-op", children: OP_LABELS[cond.op] }), opNeedsValue(cond.op) && ((0, jsx_runtime_1.jsx)("span", { className: "tabletoolbar-fchip-val", children: cond.value }))] })) : ((0, jsx_runtime_1.jsx)("span", { className: "tabletoolbar-fchip-op", children:
|
|
245
|
+
return ((0, jsx_runtime_1.jsxs)("span", { className: "tabletoolbar-toolgroup", children: [(0, jsx_runtime_1.jsxs)("span", { className: "tabletoolbar-fchip", children: [(0, jsx_runtime_1.jsxs)("button", { type: "button", className: "tabletoolbar-fchip-main", "data-menu-trigger": true, "aria-haspopup": "dialog", onClick: () => setOpenMenu(open ? null : `fcond:${cond.id}`), children: [(0, jsx_runtime_1.jsx)("span", { className: "tabletoolbar-fchip-field", children: col?.label }), hasValue ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("span", { className: "tabletoolbar-fchip-op", children: OP_LABELS[cond.op] }), opNeedsValue(cond.op) && ((0, jsx_runtime_1.jsx)("span", { className: "tabletoolbar-fchip-val", children: cond.value }))] })) : ((0, jsx_runtime_1.jsx)("span", { className: "tabletoolbar-fchip-op", children: selectConditionLabel }))] }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "tabletoolbar-fchip-x", "aria-label": `Remove ${col?.label} filter`, onClick: () => removeFilter(cond.id), children: (0, jsx_runtime_1.jsx)(lucide_react_1.X, { size: 13, strokeWidth: 2 }) })] }), open && ((0, jsx_runtime_1.jsxs)("div", { className: "tabletoolbar-menu", role: "dialog", "aria-label": `${col?.label} condition`, children: [(0, jsx_runtime_1.jsxs)("div", { className: "tabletoolbar-menu-head", children: [col?.label, " condition"] }), OP_ORDER.map((op) => ((0, jsx_runtime_1.jsxs)("button", { type: "button", className: "tabletoolbar-menu-item", "data-on": cond.op === op ? "" : undefined, onClick: () => patchFilter(cond.id, { op }), children: [OP_LABELS[op], cond.op === op ? ((0, jsx_runtime_1.jsx)(lucide_react_1.Check, { className: "tabletoolbar-menu-check", size: 15, strokeWidth: 1.75 })) : null] }, op))), opNeedsValue(cond.op) && ((0, jsx_runtime_1.jsx)("input", { className: "tabletoolbar-menu-input", placeholder: filterValuePlaceholder, "aria-label": `${col?.label} filter value`,
|
|
246
246
|
// biome-ignore lint/a11y/noAutofocus: focus the value field
|
|
247
247
|
autoFocus: true, value: cond.value, onChange: (e) => patchFilter(cond.id, { value: e.target.value }) }))] }))] }, cond.id));
|
|
248
248
|
}), (0, jsx_runtime_1.jsx)("span", { className: "tabletoolbar-spacer" }), count != null && (0, jsx_runtime_1.jsx)("span", { className: "tabletoolbar-count", children: count }), children] }) }));
|
|
@@ -64,6 +64,7 @@ exports.recordPageLabels = {
|
|
|
64
64
|
composeEmail: "Compose email",
|
|
65
65
|
askAttio: "Ask Attio",
|
|
66
66
|
addToList: "Add to list",
|
|
67
|
+
noLists: "This record has not been added to any lists.",
|
|
67
68
|
highlights: "Highlights",
|
|
68
69
|
recordDetails: "Record Details",
|
|
69
70
|
searchFields: "Search fields",
|
|
@@ -76,6 +77,10 @@ exports.recordPageLabels = {
|
|
|
76
77
|
viewLess: "View less",
|
|
77
78
|
newNote: "New note",
|
|
78
79
|
newTask: "New task",
|
|
80
|
+
notePlaceholder: "Write a note… (⌘↵ to save)",
|
|
81
|
+
saveNote: "Save note",
|
|
82
|
+
taskPlaceholder: "Task name… (↵ to add)",
|
|
83
|
+
addTask: "Add task",
|
|
79
84
|
cancel: "Cancel",
|
|
80
85
|
noNotesYet: "No notes yet.",
|
|
81
86
|
noTasksYet: "No tasks yet.",
|
|
@@ -159,14 +164,14 @@ function Composer({ placeholder, submitLabel, multiline, onSubmit, onCancel, lab
|
|
|
159
164
|
}
|
|
160
165
|
function NotesSection({ notes, onAdd, labels, }) {
|
|
161
166
|
const [open, setOpen] = React.useState(false);
|
|
162
|
-
return ((0, jsx_runtime_1.jsxs)("section", { children: [(0, jsx_runtime_1.jsx)(SectionHead, { title: labels.tabs.notes, count: notes.length, action: (0, jsx_runtime_1.jsxs)("button", { type: "button", className: "recordpage-addbtn", onClick: () => setOpen(true), children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Plus, { size: 14, strokeWidth: 2 }), labels.newNote] }) }), open && ((0, jsx_runtime_1.jsx)(Composer, { labels: labels, multiline: true, placeholder:
|
|
167
|
+
return ((0, jsx_runtime_1.jsxs)("section", { children: [(0, jsx_runtime_1.jsx)(SectionHead, { title: labels.tabs.notes, count: notes.length, action: (0, jsx_runtime_1.jsxs)("button", { type: "button", className: "recordpage-addbtn", onClick: () => setOpen(true), children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Plus, { size: 14, strokeWidth: 2 }), labels.newNote] }) }), open && ((0, jsx_runtime_1.jsx)(Composer, { labels: labels, multiline: true, placeholder: labels.notePlaceholder, submitLabel: labels.saveNote, onCancel: () => setOpen(false), onSubmit: (t) => {
|
|
163
168
|
onAdd(t);
|
|
164
169
|
setOpen(false);
|
|
165
170
|
} })), notes.length === 0 && !open ? ((0, jsx_runtime_1.jsx)("div", { className: "recordpage-emptystate", children: labels.noNotesYet })) : (notes.map((n) => ((0, jsx_runtime_1.jsxs)("div", { className: "recordpage-note", children: [(0, jsx_runtime_1.jsx)(avatar_1.Avatar, { size: "sm", shape: "circle", tone: "neutral", children: "Y" }), (0, jsx_runtime_1.jsxs)("div", { className: "recordpage-note-body", children: [(0, jsx_runtime_1.jsxs)("div", { className: "recordpage-note-head", children: [(0, jsx_runtime_1.jsx)("span", { className: "recordpage-note-author", children: labels.you }), (0, jsx_runtime_1.jsx)("span", { className: "recordpage-note-time", children: n.time })] }), (0, jsx_runtime_1.jsx)("div", { className: "recordpage-note-text", children: n.text })] })] }, n.id))))] }));
|
|
166
171
|
}
|
|
167
172
|
function TasksSection({ tasks, onAdd, onToggle, labels, }) {
|
|
168
173
|
const [open, setOpen] = React.useState(false);
|
|
169
|
-
return ((0, jsx_runtime_1.jsxs)("section", { children: [(0, jsx_runtime_1.jsx)(SectionHead, { title: labels.tabs.tasks, count: tasks.length, action: (0, jsx_runtime_1.jsxs)("button", { type: "button", className: "recordpage-addbtn", onClick: () => setOpen(true), children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Plus, { size: 14, strokeWidth: 2 }), labels.newTask] }) }), open && ((0, jsx_runtime_1.jsx)(Composer, { labels: labels, placeholder:
|
|
174
|
+
return ((0, jsx_runtime_1.jsxs)("section", { children: [(0, jsx_runtime_1.jsx)(SectionHead, { title: labels.tabs.tasks, count: tasks.length, action: (0, jsx_runtime_1.jsxs)("button", { type: "button", className: "recordpage-addbtn", onClick: () => setOpen(true), children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Plus, { size: 14, strokeWidth: 2 }), labels.newTask] }) }), open && ((0, jsx_runtime_1.jsx)(Composer, { labels: labels, placeholder: labels.taskPlaceholder, submitLabel: labels.addTask, onCancel: () => setOpen(false), onSubmit: (t) => {
|
|
170
175
|
onAdd(t);
|
|
171
176
|
setOpen(false);
|
|
172
177
|
} })), tasks.length === 0 && !open ? ((0, jsx_runtime_1.jsx)("div", { className: "recordpage-emptystate", children: labels.noTasksYet })) : (tasks.map((t) => ((0, jsx_runtime_1.jsxs)("div", { className: "recordpage-task", "data-done": t.done ? "" : undefined, children: [(0, jsx_runtime_1.jsx)(checkbox_1.Checkbox, { checked: t.done, onChange: () => onToggle(t.id), "aria-label": t.text }), (0, jsx_runtime_1.jsx)("span", { className: "recordpage-task-text", children: t.text })] }, t.id))))] }));
|
|
@@ -399,7 +404,7 @@ function RecordPage({ record, columns, collection = "Records", labels, onBack, o
|
|
|
399
404
|
setSearchOpen(next);
|
|
400
405
|
if (!next)
|
|
401
406
|
setFieldQuery("");
|
|
402
|
-
}, children: showAllValues ? t.viewLess : t.viewAllValues }))] })) : ((0, jsx_runtime_1.jsxs)("div", { className: "recordpage-noresults", children: ["No fields match \u201C", fieldQuery, "\u201D."] })))] }), (0, jsx_runtime_1.jsxs)("div", { className: "recordpage-section", children: [(0, jsx_runtime_1.jsx)(SectionHead, { title: t.tabs.lists, action: (0, jsx_runtime_1.jsxs)("button", { type: "button", className: "recordpage-addbtn", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Plus, { size: 14, strokeWidth: 2 }), t.addToList] }) }), (0, jsx_runtime_1.jsx)("div", { className: "recordpage-emptystate", children:
|
|
407
|
+
}, children: showAllValues ? t.viewLess : t.viewAllValues }))] })) : ((0, jsx_runtime_1.jsxs)("div", { className: "recordpage-noresults", children: ["No fields match \u201C", fieldQuery, "\u201D."] })))] }), (0, jsx_runtime_1.jsxs)("div", { className: "recordpage-section", children: [(0, jsx_runtime_1.jsx)(SectionHead, { title: t.tabs.lists, action: (0, jsx_runtime_1.jsxs)("button", { type: "button", className: "recordpage-addbtn", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Plus, { size: 14, strokeWidth: 2 }), t.addToList] }) }), (0, jsx_runtime_1.jsx)("div", { className: "recordpage-emptystate", children: t.noLists })] })] }), (0, jsx_runtime_1.jsx)("div", { className: "recordpage-resizer", "data-active": resizing ? "" : undefined, role: "separator", "aria-orientation": "vertical", "aria-label": t.resizeDetails, "aria-valuenow": Math.round(detailsW), "aria-valuemin": 300, "aria-valuemax": 560, tabIndex: 0, onPointerDown: onResizeDown, onPointerMove: onResizeMove, onPointerUp: onResizeUp, onKeyDown: (e) => {
|
|
403
408
|
if (e.key === "ArrowLeft")
|
|
404
409
|
setDetailsW((w) => Math.max(300, w - 16));
|
|
405
410
|
else if (e.key === "ArrowRight")
|
|
@@ -124,6 +124,9 @@ exports.recordsTableLabels = {
|
|
|
124
124
|
duplicate: "Duplicate",
|
|
125
125
|
addToList: "Add to list",
|
|
126
126
|
inAList: "In a list",
|
|
127
|
+
searchRecordsPlaceholder: "Search records…",
|
|
128
|
+
searchOptionsPlaceholder: "Search options…",
|
|
129
|
+
selectedCount: "Selected",
|
|
127
130
|
delete: "Delete",
|
|
128
131
|
sendEmail: "Send email",
|
|
129
132
|
exportCsv: "Export CSV",
|
|
@@ -309,14 +312,14 @@ function StatusCell({ column, row, onChange, }) {
|
|
|
309
312
|
return ((0, jsx_runtime_1.jsx)(select_1.Select, { variant: "ghost", "aria-label": column.label, placeholder: "\u2014", value: row[column.key] ?? undefined, onValueChange: onChange, options: column.options ?? [] }));
|
|
310
313
|
}
|
|
311
314
|
function ReferenceCell({ column, row, onChange, labels, }) {
|
|
312
|
-
return ((0, jsx_runtime_1.jsx)(select_1.Select, { variant: "ghost", searchable: true, searchPlaceholder:
|
|
315
|
+
return ((0, jsx_runtime_1.jsx)(select_1.Select, { variant: "ghost", searchable: true, searchPlaceholder: labels.searchRecordsPlaceholder, "aria-label": column.label, placeholder: labels.setRecordPlaceholder, value: row[column.key] ?? undefined, onValueChange: onChange, options: (column.refOptions ?? []).map((o) => ({
|
|
313
316
|
value: o.value,
|
|
314
317
|
label: o.label,
|
|
315
318
|
})) }));
|
|
316
319
|
}
|
|
317
320
|
function TagsCell({ column, row, options, onChange, labels, }) {
|
|
318
321
|
const value = row[column.key] ?? [];
|
|
319
|
-
return ((0, jsx_runtime_1.jsx)(multiselect_1.MultiSelect, { options: options, value: value, onValueChange: onChange, placeholder: labels.emptyPlaceholder, searchPlaceholder:
|
|
322
|
+
return ((0, jsx_runtime_1.jsx)(multiselect_1.MultiSelect, { options: options, value: value, onValueChange: onChange, placeholder: labels.emptyPlaceholder, searchPlaceholder: labels.searchOptionsPlaceholder, "aria-label": column.label }));
|
|
320
323
|
}
|
|
321
324
|
function DateCell({ column, row, onChange, }) {
|
|
322
325
|
const raw = row[column.key];
|
|
@@ -1047,7 +1050,7 @@ function RecordsTable({ labels, columns: columnsProp = exports.defaultColumns, r
|
|
|
1047
1050
|
(0, react_dom_1.createPortal)((0, jsx_runtime_1.jsx)("div", { ref: menuRef, className: "recordstable-menu recordstable-menu--fixed recordstable-menu--up", role: "menu", "aria-label": `${col.label} summary`, style: menuPos ?? undefined, onKeyDown: onMenuKeyDown, children: aggOptions(col).map((opt) => ((0, jsx_runtime_1.jsxs)("button", { type: "button", className: "recordstable-menu-item", role: "menuitemradio", tabIndex: -1, "aria-checked": a === opt, "data-on": a === opt ? "" : undefined, onClick: () => setAggValue(col.key, opt), children: [t.agg[opt], a === opt ? ((0, jsx_runtime_1.jsx)(lucide_react_1.Check, { className: "recordstable-menu-check", size: 15, strokeWidth: 1.75 })) : null] }, opt))) }), rootRef.current)] }) }, col.key));
|
|
1048
1051
|
}), (0, jsx_runtime_1.jsx)("td", { className: "recordstable-sum recordstable-addcol" })] }) })] }), visibleRows.length === 0 ? ((0, jsx_runtime_1.jsx)("div", { className: "recordstable-empty", children: (0, jsx_runtime_1.jsx)(emptystate_1.EmptyState, { variant: "page", icon: activeFilters ? ((0, jsx_runtime_1.jsx)(lucide_react_1.ListFilter, { size: 18, strokeWidth: 1.75, "aria-hidden": "true" })) : ((0, jsx_runtime_1.jsx)(lucide_react_1.Inbox, { size: 18, strokeWidth: 1.75, "aria-hidden": "true" })), title: activeFilters ? "No records match that filter" : t.noRecords, description: activeFilters
|
|
1049
1052
|
? "None of your records match the filters that are set."
|
|
1050
|
-
: `Create your first ${cols.find((c) => c.type === "entity")?.label?.toLowerCase() ?? "record"} to get started.`, action: activeFilters ? ((0, jsx_runtime_1.jsxs)(button_1.Button, { intent: "primary", size: "sm", onClick: () => setFilterConds([]), children: [(0, jsx_runtime_1.jsx)(lucide_react_1.ListFilter, { size: 15, strokeWidth: 1.75 }), t.clearFilter] })) : ((0, jsx_runtime_1.jsxs)(button_1.Button, { intent: "primary", size: "sm", onClick: addRow, children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Plus, { size: 15, strokeWidth: 1.75 }), t.newRecord] })) }) })) : ((0, jsx_runtime_1.jsxs)("button", { type: "button", className: "recordstable-addrow", onClick: addRow, children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Plus, { size: 15, strokeWidth: 1.75, "aria-hidden": "true" }), t.newRecord] }))] }), selected.size > 0 && ((0, jsx_runtime_1.jsxs)(bulkbar_1.BulkBar, { count: selected.size, countLabel:
|
|
1053
|
+
: `Create your first ${cols.find((c) => c.type === "entity")?.label?.toLowerCase() ?? "record"} to get started.`, action: activeFilters ? ((0, jsx_runtime_1.jsxs)(button_1.Button, { intent: "primary", size: "sm", onClick: () => setFilterConds([]), children: [(0, jsx_runtime_1.jsx)(lucide_react_1.ListFilter, { size: 15, strokeWidth: 1.75 }), t.clearFilter] })) : ((0, jsx_runtime_1.jsxs)(button_1.Button, { intent: "primary", size: "sm", onClick: addRow, children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Plus, { size: 15, strokeWidth: 1.75 }), t.newRecord] })) }) })) : ((0, jsx_runtime_1.jsxs)("button", { type: "button", className: "recordstable-addrow", onClick: addRow, children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Plus, { size: 15, strokeWidth: 1.75, "aria-hidden": "true" }), t.newRecord] }))] }), selected.size > 0 && ((0, jsx_runtime_1.jsxs)(bulkbar_1.BulkBar, { count: selected.size, countLabel: t.selectedCount, onClear: () => setSelected(new Set()), children: [(0, jsx_runtime_1.jsx)(bulkbar_1.BulkBarButton, { icon: (0, jsx_runtime_1.jsx)(lucide_react_1.ListPlus, { size: 15, strokeWidth: 1.75 }), onClick: addToList, children: t.addToList }), (0, jsx_runtime_1.jsx)(bulkbar_1.BulkBarButton, { icon: (0, jsx_runtime_1.jsx)(lucide_react_1.Mail, { size: 15, strokeWidth: 1.75 }), onClick: sendEmail, children: t.sendEmail }), (0, jsx_runtime_1.jsx)(bulkbar_1.BulkBarButton, { icon: (0, jsx_runtime_1.jsx)(lucide_react_1.Workflow, { size: 15, strokeWidth: 1.75 }), onClick: runWorkflow, children: t.runWorkflow }), (0, jsx_runtime_1.jsxs)(menu_1.Menu, { align: "end", "aria-label": t.moreActions, trigger: (0, jsx_runtime_1.jsx)(bulkbar_1.BulkBarButton, { icon: (0, jsx_runtime_1.jsx)(lucide_react_1.MoreHorizontal, { size: 15, strokeWidth: 1.75 }), children: t.more }), children: [(0, jsx_runtime_1.jsx)(menu_1.MenuItem, { icon: (0, jsx_runtime_1.jsx)(lucide_react_1.Download, { size: 15, strokeWidth: 1.75 }), onSelect: exportSelected, children: t.exportCsv }), (0, jsx_runtime_1.jsx)(menu_1.MenuItem, { icon: (0, jsx_runtime_1.jsx)(lucide_react_1.Trash2, { size: 15, strokeWidth: 1.75 }), danger: true, onSelect: deleteSelected, children: t.delete })] })] })), (0, jsx_runtime_1.jsx)(CreateRecordDialog, { labels: t, open: creating, onOpenChange: setCreating, columns: cols, entityLabel: cols.find((c) => c.type === "entity")?.label ?? "record", onCreate: (row) => setData((prev) => [...prev, row]) }), ctxMenu &&
|
|
1051
1054
|
(0, react_dom_1.createPortal)((0, jsx_runtime_1.jsxs)("div", { ref: ctxRef, className: "recordstable-menu recordstable-ctx", role: "menu", "aria-label": t.recordActions, style: { position: "fixed", top: ctxMenu.y, left: ctxMenu.x }, onKeyDown: onCtxKeyDown, children: [(0, jsx_runtime_1.jsxs)("button", { type: "button", className: "recordstable-menu-item", role: "menuitem", tabIndex: -1, onClick: () => copyRow(ctxMenu.id), children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Copy, { size: 15, strokeWidth: 1.75 }), t.copy] }), (0, jsx_runtime_1.jsxs)("button", { type: "button", className: "recordstable-menu-item", role: "menuitem", tabIndex: -1, onClick: () => duplicateRow(ctxMenu.id), children: [(0, jsx_runtime_1.jsx)(lucide_react_1.CopyPlus, { size: 15, strokeWidth: 1.75 }), t.duplicate] }), (0, jsx_runtime_1.jsxs)("button", { type: "button", className: "recordstable-menu-item", role: "menuitem", tabIndex: -1, onClick: () => pinRow(ctxMenu.id), children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Star, { size: 15, strokeWidth: 1.75 }), t.addToList] }), (0, jsx_runtime_1.jsxs)("button", { type: "button", className: "recordstable-menu-item recordstable-menu-item--danger", role: "menuitem", tabIndex: -1, onClick: () => deleteRow(ctxMenu.id), children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Trash2, { size: 15, strokeWidth: 1.75 }), t.delete] })] }), rootRef.current ?? document.body)] }));
|
|
1052
1055
|
}
|
|
1053
1056
|
exports.defaultColumns = [
|
|
@@ -117,7 +117,7 @@ exports.ATTIO_SIDEBAR_FOOTER = {
|
|
|
117
117
|
function SectionHeader({ label, open, onToggle, }) {
|
|
118
118
|
return ((0, jsx_runtime_1.jsxs)("button", { type: "button", className: "sidebar-section-header", "data-collapsed": open ? undefined : "", "aria-expanded": open, onClick: onToggle, children: [(0, jsx_runtime_1.jsx)(IconChevronMini, {}), (0, jsx_runtime_1.jsx)("span", { children: label })] }));
|
|
119
119
|
}
|
|
120
|
-
exports.Sidebar = React.forwardRef(({ className, asChild, onToggleCollapse, collapseLabel = "Collapse sidebar", inboxLabel = "Inbox", workspace = exports.ATTIO_SIDEBAR_WORKSPACE, items = exports.ATTIO_SIDEBAR_ITEMS, recordsSection = exports.ATTIO_SIDEBAR_RECORDS_SECTION, listsSection = exports.ATTIO_SIDEBAR_LISTS_SECTION, chatsSection = exports.ATTIO_SIDEBAR_CHATS_SECTION, footer = exports.ATTIO_SIDEBAR_FOOTER, ...props }, ref) => {
|
|
120
|
+
exports.Sidebar = React.forwardRef(({ className, asChild, onToggleCollapse, collapseLabel = "Collapse sidebar", inboxLabel = "Inbox", searchLabel = "Search", workspace = exports.ATTIO_SIDEBAR_WORKSPACE, items = exports.ATTIO_SIDEBAR_ITEMS, recordsSection = exports.ATTIO_SIDEBAR_RECORDS_SECTION, listsSection = exports.ATTIO_SIDEBAR_LISTS_SECTION, chatsSection = exports.ATTIO_SIDEBAR_CHATS_SECTION, footer = exports.ATTIO_SIDEBAR_FOOTER, ...props }, ref) => {
|
|
121
121
|
const [collapsedSections, setCollapsedSections] = React.useState(() => new Set());
|
|
122
122
|
const [commandOpen, setCommandOpen] = React.useState(false);
|
|
123
123
|
React.useEffect(() => {
|
|
@@ -143,6 +143,6 @@ exports.Sidebar = React.forwardRef(({ className, asChild, onToggleCollapse, coll
|
|
|
143
143
|
return next;
|
|
144
144
|
});
|
|
145
145
|
}, []);
|
|
146
|
-
return ((0, jsx_runtime_1.jsxs)(core_1.Primitive.div, { ref: ref, asChild: asChild, className: (0, sidebar_recipe_1.sidebarRecipe)({ className }), ...props, children: [(0, jsx_runtime_1.jsxs)("div", { className: "sidebar-ws", children: [(0, jsx_runtime_1.jsx)(avatar_1.Avatar, { size: "lg", shape: "rounded", tone: workspace.avatarTone, children: workspace.avatarLabel }), (0, jsx_runtime_1.jsx)("span", { className: "sidebar-ws-name", children: workspace.name }), (0, jsx_runtime_1.jsx)(IconChevronDown, {}), (0, jsx_runtime_1.jsx)(button_1.Button, { intent: "ghost", size: "sm", shape: "icon", className: "sidebar-ws-collapse", "aria-label": collapseLabel, onClick: onToggleCollapse, children: (0, jsx_runtime_1.jsx)(IconCollapsePanel, {}) })] }), (0, jsx_runtime_1.jsxs)("div", { className: "sidebar-searchrow", children: [(0, jsx_runtime_1.jsxs)("button", { type: "button", className: "sidebar-search", onClick: () => setCommandOpen(true), children: [(0, jsx_runtime_1.jsx)(IconSearch, {}), (0, jsx_runtime_1.jsx)("span", { className: "sidebar-search-label", children:
|
|
146
|
+
return ((0, jsx_runtime_1.jsxs)(core_1.Primitive.div, { ref: ref, asChild: asChild, className: (0, sidebar_recipe_1.sidebarRecipe)({ className }), ...props, children: [(0, jsx_runtime_1.jsxs)("div", { className: "sidebar-ws", children: [(0, jsx_runtime_1.jsx)(avatar_1.Avatar, { size: "lg", shape: "rounded", tone: workspace.avatarTone, children: workspace.avatarLabel }), (0, jsx_runtime_1.jsx)("span", { className: "sidebar-ws-name", children: workspace.name }), (0, jsx_runtime_1.jsx)(IconChevronDown, {}), (0, jsx_runtime_1.jsx)(button_1.Button, { intent: "ghost", size: "sm", shape: "icon", className: "sidebar-ws-collapse", "aria-label": collapseLabel, onClick: onToggleCollapse, children: (0, jsx_runtime_1.jsx)(IconCollapsePanel, {}) })] }), (0, jsx_runtime_1.jsxs)("div", { className: "sidebar-searchrow", children: [(0, jsx_runtime_1.jsxs)("button", { type: "button", className: "sidebar-search", onClick: () => setCommandOpen(true), children: [(0, jsx_runtime_1.jsx)(IconSearch, {}), (0, jsx_runtime_1.jsx)("span", { className: "sidebar-search-label", children: searchLabel }), (0, jsx_runtime_1.jsx)("span", { className: "sidebar-search-kbd", children: "\u2318K" })] }), (0, jsx_runtime_1.jsx)(button_1.Button, { intent: "neutral", size: "sm", shape: "icon", "aria-label": inboxLabel, children: (0, jsx_runtime_1.jsx)(IconInbox, {}) })] }), (0, jsx_runtime_1.jsx)("div", { className: "sidebar-divider" }), (0, jsx_runtime_1.jsx)("div", { className: "sidebar-section", children: items.map(({ label, icon: ItemIcon, active, trailing }) => ((0, jsx_runtime_1.jsx)(navitem_1.NavItem, { href: "#", active: active, icon: (0, jsx_runtime_1.jsx)(ItemIcon, {}), trailing: trailing, children: label }, label))) }), (0, jsx_runtime_1.jsx)(SectionHeader, { label: recordsSection.label, open: !collapsedSections.has("records"), onToggle: () => toggleSection("records") }), (0, jsx_runtime_1.jsx)("div", { className: "sidebar-section", "data-collapsed": collapsedSections.has("records") ? "" : undefined, children: recordsSection.items.map(({ label, tone, glyph: Glyph }) => ((0, jsx_runtime_1.jsx)(navitem_1.NavItem, { href: "#", icon: (0, jsx_runtime_1.jsx)(avatar_1.Avatar, { size: "sm", shape: "rounded", tone: tone, children: (0, jsx_runtime_1.jsx)(Glyph, {}) }), children: label }, label))) }), (0, jsx_runtime_1.jsx)(SectionHeader, { label: listsSection.label, open: !collapsedSections.has("lists"), onToggle: () => toggleSection("lists") }), (0, jsx_runtime_1.jsx)("div", { className: "sidebar-section", "data-collapsed": collapsedSections.has("lists") ? "" : undefined, children: (0, jsx_runtime_1.jsx)("button", { type: "button", className: "sidebar-newlist", children: listsSection.newListLabel }) }), (0, jsx_runtime_1.jsx)(SectionHeader, { label: chatsSection.label, open: !collapsedSections.has("chats"), onToggle: () => toggleSection("chats") }), (0, jsx_runtime_1.jsx)("div", { className: "sidebar-section", "data-collapsed": collapsedSections.has("chats") ? "" : undefined, children: chatsSection.items.map(({ label, icon: ItemIcon }) => ((0, jsx_runtime_1.jsx)(navitem_1.NavItem, { href: "#", icon: (0, jsx_runtime_1.jsx)(ItemIcon, {}), children: label }, label))) }), (0, jsx_runtime_1.jsxs)("div", { className: "sidebar-footer", children: [(0, jsx_runtime_1.jsx)(button_1.Button, { intent: "neutral", size: "sm", className: "sidebar-pill", children: footer.gettingStartedLabel }), (0, jsx_runtime_1.jsx)(navitem_1.NavItem, { href: "#", icon: (0, jsx_runtime_1.jsx)(IconInvite, {}), children: footer.inviteLabel }), (0, jsx_runtime_1.jsx)("div", { className: "sidebar-divider" }), (0, jsx_runtime_1.jsxs)("div", { className: "sidebar-trial", children: [(0, jsx_runtime_1.jsx)("span", { children: footer.trialText }), (0, jsx_runtime_1.jsx)(button_1.Button, { intent: "neutral", size: "sm", children: footer.trialActionLabel })] })] }), (0, jsx_runtime_1.jsx)(command_1.Command, { open: commandOpen, onOpenChange: setCommandOpen })] }));
|
|
147
147
|
});
|
|
148
148
|
exports.Sidebar.displayName = "Sidebar";
|
|
@@ -42,12 +42,15 @@ const viewtoggle_recipe_1 = require("./viewtoggle.recipe");
|
|
|
42
42
|
* of icon tiles; the selected tile is raised. Use for view-mode switches (list ⇄
|
|
43
43
|
* grid) or any small mutually-exclusive icon choice.
|
|
44
44
|
*/
|
|
45
|
-
function ViewToggle({ options, value: valueProp, defaultValue, onChange, "aria-label": ariaLabel, className, ...rest }) {
|
|
45
|
+
function ViewToggle({ options, value: valueProp, defaultValue, onValueChange, onChange, "aria-label": ariaLabel, className, ...rest }) {
|
|
46
46
|
const [internalValue, setInternalValue] = React.useState(defaultValue);
|
|
47
47
|
const value = valueProp ?? internalValue ?? options[0]?.value;
|
|
48
48
|
const pick = (next) => {
|
|
49
49
|
if (valueProp === undefined)
|
|
50
50
|
setInternalValue(next);
|
|
51
|
+
// both, so a consumer passing either name is heard and one passing both
|
|
52
|
+
// does not silently lose one
|
|
53
|
+
onValueChange?.(next);
|
|
51
54
|
onChange?.(next);
|
|
52
55
|
};
|
|
53
56
|
return ((0, jsx_runtime_1.jsx)("div", { className: (0, viewtoggle_recipe_1.viewtoggleRecipe)({ className }), ...rest, role: "group", "aria-label": ariaLabel, children: options.map((o) => ((0, jsx_runtime_1.jsx)("button", { type: "button", className: "viewtoggle-tile", "aria-pressed": o.value === value, "aria-label": o.label, title: o.label, "data-active": o.value === value ? "" : undefined, onClick: () => pick(o.value), children: o.icon }, o.value))) }));
|
|
@@ -78,7 +78,7 @@ export function computeCalc(values, op) {
|
|
|
78
78
|
const defaultFormat = (n, op) => op === "percentEmpty" || op === "percentFilled"
|
|
79
79
|
? `${Math.round(n)}%`
|
|
80
80
|
: n.toLocaleString(undefined, { maximumFractionDigits: 2 });
|
|
81
|
-
export function ColumnCalc({ rows, value, numeric = false, defaultOp = "none", op: opProp, onOpChange, format = defaultFormat, align, opLabels, className, ...rest }) {
|
|
81
|
+
export function ColumnCalc({ rows, value, numeric = false, defaultOp = "none", op: opProp, onOpChange, addLabel = "Add calculation", sectionLabel = "Aggregations", format = defaultFormat, align, opLabels, className, ...rest }) {
|
|
82
82
|
// a partial override, so a consumer can translate one op without
|
|
83
83
|
// restating all nine
|
|
84
84
|
const labelFor = (o) => opLabels?.[o] ?? CALC_LABEL[o];
|
|
@@ -96,5 +96,5 @@ export function ColumnCalc({ rows, value, numeric = false, defaultOp = "none", o
|
|
|
96
96
|
...(numeric ? NUMERIC_CALCS : []),
|
|
97
97
|
"none",
|
|
98
98
|
];
|
|
99
|
-
return (_jsx("span", { className: columncalcRecipe({ className }), ...rest, children: _jsxs(Menu, { trigger: _jsx("button", { type: "button", className: "columncalc-trigger", "data-align": align === "right" ? "right" : undefined, children: op === "none" || result === null ? (_jsxs(_Fragment, { children: [_jsx(Plus, { size: 12, strokeWidth: 2 }), _jsx("span", { className: "columncalc-placeholder", children:
|
|
99
|
+
return (_jsx("span", { className: columncalcRecipe({ className }), ...rest, children: _jsxs(Menu, { trigger: _jsx("button", { type: "button", className: "columncalc-trigger", "data-align": align === "right" ? "right" : undefined, children: op === "none" || result === null ? (_jsxs(_Fragment, { children: [_jsx(Plus, { size: 12, strokeWidth: 2 }), _jsx("span", { className: "columncalc-placeholder", children: addLabel })] })) : (_jsxs(_Fragment, { children: [_jsx("span", { className: "columncalc-value", children: format(result, op) }), _jsx("span", { className: "columncalc-op", children: labelFor(op) }), _jsx(ChevronDown, { size: 12, strokeWidth: 2 })] })) }), children: [_jsx("div", { className: "columncalc-section", children: sectionLabel }), ops.map((o) => (_jsx(MenuItem, { onSelect: () => setOp(o), children: labelFor(o) }, o)))] }) }));
|
|
100
100
|
}
|
|
@@ -21,13 +21,15 @@ const fromDate = (d) => ({
|
|
|
21
21
|
* popover. This is what `DatePicker` used to be; `DatePicker` is now
|
|
22
22
|
* seed-design's bare calendar, and this composes it.
|
|
23
23
|
*/
|
|
24
|
-
export function DatePickerField({ value, defaultValue, onChange, placeholder = "Set date…", variant = "default", format = defaultFormat, presets, calendarProps, "aria-label": ariaLabel, className, ...rest }) {
|
|
24
|
+
export function DatePickerField({ value, defaultValue, onValueChange, onChange, placeholder = "Set date…", variant = "default", format = defaultFormat, presets, calendarProps, "aria-label": ariaLabel, className, ...rest }) {
|
|
25
25
|
const [internal, setInternal] = React.useState(defaultValue ?? null);
|
|
26
26
|
const selected = value ?? internal;
|
|
27
27
|
const [open, setOpen] = React.useState(false);
|
|
28
28
|
const pick = (d) => {
|
|
29
29
|
if (value === undefined)
|
|
30
30
|
setInternal(d);
|
|
31
|
+
// both, so a consumer passing either name is heard
|
|
32
|
+
onValueChange?.(d);
|
|
31
33
|
onChange?.(d);
|
|
32
34
|
setOpen(false);
|
|
33
35
|
};
|
|
@@ -28,7 +28,7 @@ function Chip({ opt }) {
|
|
|
28
28
|
* popover with a search field over rows of "[chip] … [checkbox]" that toggle on
|
|
29
29
|
* click (the menu stays open). Keyboard: ↑/↓ move, Enter toggles, Esc closes.
|
|
30
30
|
*/
|
|
31
|
-
export function MultiSelect({ options, value: valueProp, defaultValue, onValueChange, placeholder = "Select…", searchable = true, searchPlaceholder = "Search…", disabled, className, "aria-label": ariaLabel, ...rest }) {
|
|
31
|
+
export function MultiSelect({ options, value: valueProp, defaultValue, onValueChange, placeholder = "Select…", searchable = true, searchPlaceholder = "Search…", emptyLabel = "No results", disabled, className, "aria-label": ariaLabel, ...rest }) {
|
|
32
32
|
// Virtual focus: DOM focus stays on the trigger (or the search field) and the
|
|
33
33
|
// active option is named by `aria-activedescendant`. `data-highlighted` is a
|
|
34
34
|
// styling hook only -- a screen reader cannot see it.
|
|
@@ -125,7 +125,7 @@ export function MultiSelect({ options, value: valueProp, defaultValue, onValueCh
|
|
|
125
125
|
}, children: [_jsx("span", { className: "multiselect-chips", children: selectedOptions.length === 0 ? (_jsx("span", { className: "multiselect-placeholder", children: placeholder })) : (selectedOptions.map((o) => _jsx(Chip, { opt: o }, o.value))) }), _jsx(ChevronDown, { className: "multiselect-chevron", size: 16, strokeWidth: 1.75 })] }), open && (_jsxs("div", { id: listboxId, className: "multiselect-listbox", role: "listbox", "aria-multiselectable": "true", "aria-label": ariaLabel, "data-up": up ? "" : undefined, style: pos, children: [searchable && (_jsxs("div", { className: "multiselect-search", children: [_jsx(Search, { size: 15, strokeWidth: 1.75 }), _jsx("input", { ref: searchRef, type: "text", placeholder: searchPlaceholder, "aria-label": searchPlaceholder, "aria-controls": listboxId, "aria-activedescendant": activeId, value: query, onChange: (e) => {
|
|
126
126
|
setQuery(e.target.value);
|
|
127
127
|
setHighlight(0);
|
|
128
|
-
}, onKeyDown: onNavKeys })] })), filtered.length === 0 ? (_jsx("div", { className: "multiselect-empty", children:
|
|
128
|
+
}, onKeyDown: onNavKeys })] })), filtered.length === 0 ? (_jsx("div", { className: "multiselect-empty", children: emptyLabel })) : (filtered.map((opt, i) => {
|
|
129
129
|
const sel = selectedSet.has(opt.value);
|
|
130
130
|
return (_jsxs("button", { type: "button", id: optionId(i), role: "option", "aria-selected": sel, tabIndex: -1, "data-highlighted": i === highlight ? "" : undefined, className: "multiselect-option", onClick: () => toggle(opt.value), onMouseEnter: () => setHighlight(i), children: [_jsx("span", { className: "multiselect-option-chip", children: _jsx(Chip, { opt: opt }) }), _jsx("span", { className: "multiselect-check", children: sel && _jsx(Check, { size: 12, strokeWidth: 3 }) })] }, opt.value));
|
|
131
131
|
}))] }))] }));
|
|
@@ -15,7 +15,7 @@ function optionText(o) {
|
|
|
15
15
|
* "No results" empty state). Full keyboard model — ↑/↓ move, Enter/Space pick,
|
|
16
16
|
* Esc close, Home/End jump, opens upward when tight. Controlled or uncontrolled.
|
|
17
17
|
*/
|
|
18
|
-
export function Select({ options, value, defaultValue, onValueChange, placeholder = "Select…", variant = "default", width = "full", searchable, searchPlaceholder = "Search…", disabled, className, "aria-label": ariaLabel, id, ...rest }) {
|
|
18
|
+
export function Select({ options, value, defaultValue, onValueChange, placeholder = "Select…", variant = "default", width = "full", searchable, searchPlaceholder = "Search…", emptyLabel = "No results", disabled, className, "aria-label": ariaLabel, id, ...rest }) {
|
|
19
19
|
// Virtual focus: DOM focus stays on the trigger (or the search field), and the
|
|
20
20
|
// active option is named by `aria-activedescendant`. That is the only thing a
|
|
21
21
|
// screen reader has to go on -- `data-highlighted` is invisible to it -- so the
|
|
@@ -114,7 +114,7 @@ export function Select({ options, value, defaultValue, onValueChange, placeholde
|
|
|
114
114
|
return (_jsxs("div", { ref: rootRef, className: selectRecipe({ variant, width, className }), ...rest, children: [_jsxs("button", { ref: triggerRef, type: "button", id: id, role: "combobox", "aria-haspopup": "listbox", "aria-expanded": open, "aria-controls": open ? listboxId : undefined, "aria-activedescendant": searchable ? undefined : activeId, "aria-label": ariaLabel, disabled: disabled, "data-open": open ? "" : undefined, className: "select-trigger", onClick: () => (open ? setOpen(false) : openMenu()), onKeyDown: onTriggerKeyDown, children: [_jsx("span", { className: "select-value", children: selectedOption ? (_jsxs(_Fragment, { children: [selectedOption.color && (_jsx("span", { className: "select-option-dot", style: { background: selectedOption.color } })), selectedOption.label] })) : (_jsx("span", { className: "select-placeholder", children: placeholder })) }), _jsx(ChevronDown, { className: "select-chevron", size: 16, strokeWidth: 1.75 })] }), open && (_jsxs("div", { id: listboxId, className: "select-listbox", role: "listbox", "aria-label": ariaLabel, "data-up": up ? "" : undefined, style: pos, children: [searchable && (_jsxs("div", { className: "select-search", children: [_jsx(Search, { size: 15, strokeWidth: 1.75 }), _jsx("input", { ref: searchRef, type: "text", placeholder: searchPlaceholder, "aria-label": searchPlaceholder, "aria-controls": listboxId, "aria-activedescendant": activeId, value: query, onChange: (e) => {
|
|
115
115
|
setQuery(e.target.value);
|
|
116
116
|
setHighlight(0);
|
|
117
|
-
}, onKeyDown: onNavKeys })] })), filtered.length === 0 ? (_jsx("div", { className: "select-empty", children:
|
|
117
|
+
}, onKeyDown: onNavKeys })] })), filtered.length === 0 ? (_jsx("div", { className: "select-empty", children: emptyLabel })) : (filtered.map((opt, i) => {
|
|
118
118
|
const isSel = opt.value === selected;
|
|
119
119
|
return (_jsxs("button", { type: "button", id: optionId(i), role: "option", "aria-selected": isSel, tabIndex: -1, disabled: opt.disabled, "data-disabled": opt.disabled ? "" : undefined, "data-highlighted": i === highlight ? "" : undefined, className: "select-option", onClick: () => pick(opt), onMouseEnter: () => setHighlight(i), children: [opt.color && (_jsx("span", { className: "select-option-dot", style: { background: opt.color } })), _jsx("span", { className: "select-option-label", children: opt.label }), isSel && (_jsx(Check, { className: "select-option-check", size: 15, strokeWidth: 2 }))] }, opt.value));
|
|
120
120
|
}))] }))] }));
|
|
@@ -96,7 +96,7 @@ export function applySorts(rows, sorts, get = (row, key) => row[key]) {
|
|
|
96
96
|
* composable version, to put above `DataTable` or any table of your own. Pair
|
|
97
97
|
* it with `applyFilters` / `applySorts` to turn its state into rows.
|
|
98
98
|
*/
|
|
99
|
-
export function TableToolbar({ columns, sorts: sortsProp, defaultSorts, onSortsChange, filters: filtersProp, defaultFilters, onFiltersChange, count, children, helpHref, className, sortLabel = "Sort", reorderSortLabel = "Reorder sort", sortFieldLabel = "Sort field", sortDirectionLabel = "Sort direction", removeSortLabel = "Remove sort", filterFieldLabel = "Filter by field", filterFieldPlaceholder = "Filter by…", filterValuePlaceholder = "Value…", ascendingLabel = "Ascending", descendingLabel = "Descending", sortTriggerLabel, filterTriggerLabel, ...rest }) {
|
|
99
|
+
export function TableToolbar({ columns, sorts: sortsProp, defaultSorts, onSortsChange, filters: filtersProp, defaultFilters, onFiltersChange, count, children, helpHref, className, sortLabel = "Sort", reorderSortLabel = "Reorder sort", noSortsLabel = "No sorts applied to this view.", addSortLabel = "Add sort", learnSortingLabel = "Learn about sorting", selectConditionLabel = "Select condition", sortFieldLabel = "Sort field", sortDirectionLabel = "Sort direction", removeSortLabel = "Remove sort", filterFieldLabel = "Filter by field", filterFieldPlaceholder = "Filter by…", filterValuePlaceholder = "Value…", ascendingLabel = "Ascending", descendingLabel = "Descending", sortTriggerLabel, filterTriggerLabel, ...rest }) {
|
|
100
100
|
const [sortsState, setSortsState] = React.useState(defaultSorts ?? []);
|
|
101
101
|
const [filtersState, setFiltersState] = React.useState(defaultFilters ?? []);
|
|
102
102
|
const sorts = sortsProp ?? sortsState;
|
|
@@ -168,7 +168,7 @@ export function TableToolbar({ columns, sorts: sortsProp, defaultSorts, onSortsC
|
|
|
168
168
|
: sorts.length
|
|
169
169
|
? `Sorted by ${columns.find((c) => c.key === sorts[0].key)?.label}${sorts.length > 1 ? ` +${sorts.length - 1}` : ""}`
|
|
170
170
|
: "Sort";
|
|
171
|
-
return (_jsx("div", { className: tabletoolbarRecipe({ className }), ...rest, children: _jsxs("div", { className: "tabletoolbar-toolbar", children: [_jsxs("span", { className: "tabletoolbar-toolgroup", children: [_jsxs("button", { type: "button", className: `tabletoolbar-tool${sorts.length ? " tabletoolbar-tool--active" : ""}`, onClick: () => setOpenMenu(openMenu === "sort" ? null : "sort"), "aria-haspopup": "dialog", "aria-expanded": openMenu === "sort", "data-menu-trigger": true, children: [_jsx(ArrowUpDown, { size: 14, strokeWidth: 1.75, "aria-hidden": "true" }), sortedLabel] }), openMenu === "sort" && (_jsxs("div", { className: "tabletoolbar-menu tabletoolbar-sortpop", role: "dialog", "aria-label": sortLabel, children: [sorts.length === 0 ? (_jsx("div", { className: "tabletoolbar-sort-empty", children:
|
|
171
|
+
return (_jsx("div", { className: tabletoolbarRecipe({ className }), ...rest, children: _jsxs("div", { className: "tabletoolbar-toolbar", children: [_jsxs("span", { className: "tabletoolbar-toolgroup", children: [_jsxs("button", { type: "button", className: `tabletoolbar-tool${sorts.length ? " tabletoolbar-tool--active" : ""}`, onClick: () => setOpenMenu(openMenu === "sort" ? null : "sort"), "aria-haspopup": "dialog", "aria-expanded": openMenu === "sort", "data-menu-trigger": true, children: [_jsx(ArrowUpDown, { size: 14, strokeWidth: 1.75, "aria-hidden": "true" }), sortedLabel] }), openMenu === "sort" && (_jsxs("div", { className: "tabletoolbar-menu tabletoolbar-sortpop", role: "dialog", "aria-label": sortLabel, children: [sorts.length === 0 ? (_jsx("div", { className: "tabletoolbar-sort-empty", children: noSortsLabel })) : (sorts.map((s, i) => (_jsxs("div", { className: "tabletoolbar-sort-row", onDragOver: (e) => {
|
|
172
172
|
if (dragSort !== null)
|
|
173
173
|
e.preventDefault();
|
|
174
174
|
}, onDrop: () => {
|
|
@@ -190,7 +190,7 @@ export function TableToolbar({ columns, sorts: sortsProp, defaultSorts, onSortsC
|
|
|
190
190
|
label: (_jsxs("span", { className: "tabletoolbar-sort-optlabel", children: [_jsx(ArrowDownWideNarrow, { size: 15, strokeWidth: 1.75 }), descendingLabel] })),
|
|
191
191
|
keywords: descendingLabel,
|
|
192
192
|
},
|
|
193
|
-
], "aria-label": sortDirectionLabel }), _jsx("button", { type: "button", className: "tabletoolbar-sort-remove", onClick: () => removeSort(i), "aria-label": removeSortLabel, children: _jsx(X, { size: 15, strokeWidth: 1.75 }) })] }, s.key)))), _jsx("div", { className: "tabletoolbar-sort-addrow", children: _jsxs("button", { type: "button", className: "tabletoolbar-sort-add", onClick: addSort, children: [_jsx(Plus, { size: 15, strokeWidth: 1.75 }),
|
|
193
|
+
], "aria-label": sortDirectionLabel }), _jsx("button", { type: "button", className: "tabletoolbar-sort-remove", onClick: () => removeSort(i), "aria-label": removeSortLabel, children: _jsx(X, { size: 15, strokeWidth: 1.75 }) })] }, s.key)))), _jsx("div", { className: "tabletoolbar-sort-addrow", children: _jsxs("button", { type: "button", className: "tabletoolbar-sort-add", onClick: addSort, children: [_jsx(Plus, { size: 15, strokeWidth: 1.75 }), addSortLabel] }) }), helpHref && (_jsx("div", { className: "tabletoolbar-sort-foot", children: _jsxs("a", { className: "tabletoolbar-sort-learn", href: helpHref, target: "_blank", rel: "noreferrer", children: [_jsx(CircleHelp, { size: 14, strokeWidth: 1.75 }), learnSortingLabel] }) }))] }))] }), _jsxs("span", { className: "tabletoolbar-toolgroup", children: [_jsxs("button", { type: "button", className: `tabletoolbar-tool tabletoolbar-tool--ghost${activeFilters ? " tabletoolbar-tool--active" : ""}`, onClick: () => setOpenMenu(openMenu === "filter" ? null : "filter"), "aria-haspopup": "dialog", "aria-expanded": openMenu === "filter", "data-menu-trigger": true, children: [_jsx(ListFilter, { size: 14, strokeWidth: 1.75, "aria-hidden": "true" }), filterTriggerLabel
|
|
194
194
|
? filterTriggerLabel(activeFilters)
|
|
195
195
|
: activeFilters
|
|
196
196
|
? `Filter · ${activeFilters}`
|
|
@@ -204,7 +204,7 @@ export function TableToolbar({ columns, sorts: sortsProp, defaultSorts, onSortsC
|
|
|
204
204
|
const col = columns.find((c) => c.key === cond.key);
|
|
205
205
|
const open = openMenu === `fcond:${cond.id}`;
|
|
206
206
|
const hasValue = !opNeedsValue(cond.op) || cond.value.trim();
|
|
207
|
-
return (_jsxs("span", { className: "tabletoolbar-toolgroup", children: [_jsxs("span", { className: "tabletoolbar-fchip", children: [_jsxs("button", { type: "button", className: "tabletoolbar-fchip-main", "data-menu-trigger": true, "aria-haspopup": "dialog", onClick: () => setOpenMenu(open ? null : `fcond:${cond.id}`), children: [_jsx("span", { className: "tabletoolbar-fchip-field", children: col?.label }), hasValue ? (_jsxs(_Fragment, { children: [_jsx("span", { className: "tabletoolbar-fchip-op", children: OP_LABELS[cond.op] }), opNeedsValue(cond.op) && (_jsx("span", { className: "tabletoolbar-fchip-val", children: cond.value }))] })) : (_jsx("span", { className: "tabletoolbar-fchip-op", children:
|
|
207
|
+
return (_jsxs("span", { className: "tabletoolbar-toolgroup", children: [_jsxs("span", { className: "tabletoolbar-fchip", children: [_jsxs("button", { type: "button", className: "tabletoolbar-fchip-main", "data-menu-trigger": true, "aria-haspopup": "dialog", onClick: () => setOpenMenu(open ? null : `fcond:${cond.id}`), children: [_jsx("span", { className: "tabletoolbar-fchip-field", children: col?.label }), hasValue ? (_jsxs(_Fragment, { children: [_jsx("span", { className: "tabletoolbar-fchip-op", children: OP_LABELS[cond.op] }), opNeedsValue(cond.op) && (_jsx("span", { className: "tabletoolbar-fchip-val", children: cond.value }))] })) : (_jsx("span", { className: "tabletoolbar-fchip-op", children: selectConditionLabel }))] }), _jsx("button", { type: "button", className: "tabletoolbar-fchip-x", "aria-label": `Remove ${col?.label} filter`, onClick: () => removeFilter(cond.id), children: _jsx(X, { size: 13, strokeWidth: 2 }) })] }), open && (_jsxs("div", { className: "tabletoolbar-menu", role: "dialog", "aria-label": `${col?.label} condition`, children: [_jsxs("div", { className: "tabletoolbar-menu-head", children: [col?.label, " condition"] }), OP_ORDER.map((op) => (_jsxs("button", { type: "button", className: "tabletoolbar-menu-item", "data-on": cond.op === op ? "" : undefined, onClick: () => patchFilter(cond.id, { op }), children: [OP_LABELS[op], cond.op === op ? (_jsx(Check, { className: "tabletoolbar-menu-check", size: 15, strokeWidth: 1.75 })) : null] }, op))), opNeedsValue(cond.op) && (_jsx("input", { className: "tabletoolbar-menu-input", placeholder: filterValuePlaceholder, "aria-label": `${col?.label} filter value`,
|
|
208
208
|
// biome-ignore lint/a11y/noAutofocus: focus the value field
|
|
209
209
|
autoFocus: true, value: cond.value, onChange: (e) => patchFilter(cond.id, { value: e.target.value }) }))] }))] }, cond.id));
|
|
210
210
|
}), _jsx("span", { className: "tabletoolbar-spacer" }), count != null && _jsx("span", { className: "tabletoolbar-count", children: count }), children] }) }));
|
|
@@ -27,6 +27,7 @@ export const recordPageLabels = {
|
|
|
27
27
|
composeEmail: "Compose email",
|
|
28
28
|
askAttio: "Ask Attio",
|
|
29
29
|
addToList: "Add to list",
|
|
30
|
+
noLists: "This record has not been added to any lists.",
|
|
30
31
|
highlights: "Highlights",
|
|
31
32
|
recordDetails: "Record Details",
|
|
32
33
|
searchFields: "Search fields",
|
|
@@ -39,6 +40,10 @@ export const recordPageLabels = {
|
|
|
39
40
|
viewLess: "View less",
|
|
40
41
|
newNote: "New note",
|
|
41
42
|
newTask: "New task",
|
|
43
|
+
notePlaceholder: "Write a note… (⌘↵ to save)",
|
|
44
|
+
saveNote: "Save note",
|
|
45
|
+
taskPlaceholder: "Task name… (↵ to add)",
|
|
46
|
+
addTask: "Add task",
|
|
42
47
|
cancel: "Cancel",
|
|
43
48
|
noNotesYet: "No notes yet.",
|
|
44
49
|
noTasksYet: "No tasks yet.",
|
|
@@ -122,14 +127,14 @@ function Composer({ placeholder, submitLabel, multiline, onSubmit, onCancel, lab
|
|
|
122
127
|
}
|
|
123
128
|
function NotesSection({ notes, onAdd, labels, }) {
|
|
124
129
|
const [open, setOpen] = React.useState(false);
|
|
125
|
-
return (_jsxs("section", { children: [_jsx(SectionHead, { title: labels.tabs.notes, count: notes.length, action: _jsxs("button", { type: "button", className: "recordpage-addbtn", onClick: () => setOpen(true), children: [_jsx(Plus, { size: 14, strokeWidth: 2 }), labels.newNote] }) }), open && (_jsx(Composer, { labels: labels, multiline: true, placeholder:
|
|
130
|
+
return (_jsxs("section", { children: [_jsx(SectionHead, { title: labels.tabs.notes, count: notes.length, action: _jsxs("button", { type: "button", className: "recordpage-addbtn", onClick: () => setOpen(true), children: [_jsx(Plus, { size: 14, strokeWidth: 2 }), labels.newNote] }) }), open && (_jsx(Composer, { labels: labels, multiline: true, placeholder: labels.notePlaceholder, submitLabel: labels.saveNote, onCancel: () => setOpen(false), onSubmit: (t) => {
|
|
126
131
|
onAdd(t);
|
|
127
132
|
setOpen(false);
|
|
128
133
|
} })), notes.length === 0 && !open ? (_jsx("div", { className: "recordpage-emptystate", children: labels.noNotesYet })) : (notes.map((n) => (_jsxs("div", { className: "recordpage-note", children: [_jsx(Avatar, { size: "sm", shape: "circle", tone: "neutral", children: "Y" }), _jsxs("div", { className: "recordpage-note-body", children: [_jsxs("div", { className: "recordpage-note-head", children: [_jsx("span", { className: "recordpage-note-author", children: labels.you }), _jsx("span", { className: "recordpage-note-time", children: n.time })] }), _jsx("div", { className: "recordpage-note-text", children: n.text })] })] }, n.id))))] }));
|
|
129
134
|
}
|
|
130
135
|
function TasksSection({ tasks, onAdd, onToggle, labels, }) {
|
|
131
136
|
const [open, setOpen] = React.useState(false);
|
|
132
|
-
return (_jsxs("section", { children: [_jsx(SectionHead, { title: labels.tabs.tasks, count: tasks.length, action: _jsxs("button", { type: "button", className: "recordpage-addbtn", onClick: () => setOpen(true), children: [_jsx(Plus, { size: 14, strokeWidth: 2 }), labels.newTask] }) }), open && (_jsx(Composer, { labels: labels, placeholder:
|
|
137
|
+
return (_jsxs("section", { children: [_jsx(SectionHead, { title: labels.tabs.tasks, count: tasks.length, action: _jsxs("button", { type: "button", className: "recordpage-addbtn", onClick: () => setOpen(true), children: [_jsx(Plus, { size: 14, strokeWidth: 2 }), labels.newTask] }) }), open && (_jsx(Composer, { labels: labels, placeholder: labels.taskPlaceholder, submitLabel: labels.addTask, onCancel: () => setOpen(false), onSubmit: (t) => {
|
|
133
138
|
onAdd(t);
|
|
134
139
|
setOpen(false);
|
|
135
140
|
} })), tasks.length === 0 && !open ? (_jsx("div", { className: "recordpage-emptystate", children: labels.noTasksYet })) : (tasks.map((t) => (_jsxs("div", { className: "recordpage-task", "data-done": t.done ? "" : undefined, children: [_jsx(Checkbox, { checked: t.done, onChange: () => onToggle(t.id), "aria-label": t.text }), _jsx("span", { className: "recordpage-task-text", children: t.text })] }, t.id))))] }));
|
|
@@ -362,7 +367,7 @@ export function RecordPage({ record, columns, collection = "Records", labels, on
|
|
|
362
367
|
setSearchOpen(next);
|
|
363
368
|
if (!next)
|
|
364
369
|
setFieldQuery("");
|
|
365
|
-
}, children: showAllValues ? t.viewLess : t.viewAllValues }))] })) : (_jsxs("div", { className: "recordpage-noresults", children: ["No fields match \u201C", fieldQuery, "\u201D."] })))] }), _jsxs("div", { className: "recordpage-section", children: [_jsx(SectionHead, { title: t.tabs.lists, action: _jsxs("button", { type: "button", className: "recordpage-addbtn", children: [_jsx(Plus, { size: 14, strokeWidth: 2 }), t.addToList] }) }), _jsx("div", { className: "recordpage-emptystate", children:
|
|
370
|
+
}, children: showAllValues ? t.viewLess : t.viewAllValues }))] })) : (_jsxs("div", { className: "recordpage-noresults", children: ["No fields match \u201C", fieldQuery, "\u201D."] })))] }), _jsxs("div", { className: "recordpage-section", children: [_jsx(SectionHead, { title: t.tabs.lists, action: _jsxs("button", { type: "button", className: "recordpage-addbtn", children: [_jsx(Plus, { size: 14, strokeWidth: 2 }), t.addToList] }) }), _jsx("div", { className: "recordpage-emptystate", children: t.noLists })] })] }), _jsx("div", { className: "recordpage-resizer", "data-active": resizing ? "" : undefined, role: "separator", "aria-orientation": "vertical", "aria-label": t.resizeDetails, "aria-valuenow": Math.round(detailsW), "aria-valuemin": 300, "aria-valuemax": 560, tabIndex: 0, onPointerDown: onResizeDown, onPointerMove: onResizeMove, onPointerUp: onResizeUp, onKeyDown: (e) => {
|
|
366
371
|
if (e.key === "ArrowLeft")
|
|
367
372
|
setDetailsW((w) => Math.max(300, w - 16));
|
|
368
373
|
else if (e.key === "ArrowRight")
|
|
@@ -86,6 +86,9 @@ export const recordsTableLabels = {
|
|
|
86
86
|
duplicate: "Duplicate",
|
|
87
87
|
addToList: "Add to list",
|
|
88
88
|
inAList: "In a list",
|
|
89
|
+
searchRecordsPlaceholder: "Search records…",
|
|
90
|
+
searchOptionsPlaceholder: "Search options…",
|
|
91
|
+
selectedCount: "Selected",
|
|
89
92
|
delete: "Delete",
|
|
90
93
|
sendEmail: "Send email",
|
|
91
94
|
exportCsv: "Export CSV",
|
|
@@ -271,14 +274,14 @@ function StatusCell({ column, row, onChange, }) {
|
|
|
271
274
|
return (_jsx(Select, { variant: "ghost", "aria-label": column.label, placeholder: "\u2014", value: row[column.key] ?? undefined, onValueChange: onChange, options: column.options ?? [] }));
|
|
272
275
|
}
|
|
273
276
|
function ReferenceCell({ column, row, onChange, labels, }) {
|
|
274
|
-
return (_jsx(Select, { variant: "ghost", searchable: true, searchPlaceholder:
|
|
277
|
+
return (_jsx(Select, { variant: "ghost", searchable: true, searchPlaceholder: labels.searchRecordsPlaceholder, "aria-label": column.label, placeholder: labels.setRecordPlaceholder, value: row[column.key] ?? undefined, onValueChange: onChange, options: (column.refOptions ?? []).map((o) => ({
|
|
275
278
|
value: o.value,
|
|
276
279
|
label: o.label,
|
|
277
280
|
})) }));
|
|
278
281
|
}
|
|
279
282
|
function TagsCell({ column, row, options, onChange, labels, }) {
|
|
280
283
|
const value = row[column.key] ?? [];
|
|
281
|
-
return (_jsx(MultiSelect, { options: options, value: value, onValueChange: onChange, placeholder: labels.emptyPlaceholder, searchPlaceholder:
|
|
284
|
+
return (_jsx(MultiSelect, { options: options, value: value, onValueChange: onChange, placeholder: labels.emptyPlaceholder, searchPlaceholder: labels.searchOptionsPlaceholder, "aria-label": column.label }));
|
|
282
285
|
}
|
|
283
286
|
function DateCell({ column, row, onChange, }) {
|
|
284
287
|
const raw = row[column.key];
|
|
@@ -1009,7 +1012,7 @@ export function RecordsTable({ labels, columns: columnsProp = defaultColumns, ro
|
|
|
1009
1012
|
createPortal(_jsx("div", { ref: menuRef, className: "recordstable-menu recordstable-menu--fixed recordstable-menu--up", role: "menu", "aria-label": `${col.label} summary`, style: menuPos ?? undefined, onKeyDown: onMenuKeyDown, children: aggOptions(col).map((opt) => (_jsxs("button", { type: "button", className: "recordstable-menu-item", role: "menuitemradio", tabIndex: -1, "aria-checked": a === opt, "data-on": a === opt ? "" : undefined, onClick: () => setAggValue(col.key, opt), children: [t.agg[opt], a === opt ? (_jsx(Check, { className: "recordstable-menu-check", size: 15, strokeWidth: 1.75 })) : null] }, opt))) }), rootRef.current)] }) }, col.key));
|
|
1010
1013
|
}), _jsx("td", { className: "recordstable-sum recordstable-addcol" })] }) })] }), visibleRows.length === 0 ? (_jsx("div", { className: "recordstable-empty", children: _jsx(EmptyState, { variant: "page", icon: activeFilters ? (_jsx(ListFilter, { size: 18, strokeWidth: 1.75, "aria-hidden": "true" })) : (_jsx(Inbox, { size: 18, strokeWidth: 1.75, "aria-hidden": "true" })), title: activeFilters ? "No records match that filter" : t.noRecords, description: activeFilters
|
|
1011
1014
|
? "None of your records match the filters that are set."
|
|
1012
|
-
: `Create your first ${cols.find((c) => c.type === "entity")?.label?.toLowerCase() ?? "record"} to get started.`, action: activeFilters ? (_jsxs(Button, { intent: "primary", size: "sm", onClick: () => setFilterConds([]), children: [_jsx(ListFilter, { size: 15, strokeWidth: 1.75 }), t.clearFilter] })) : (_jsxs(Button, { intent: "primary", size: "sm", onClick: addRow, children: [_jsx(Plus, { size: 15, strokeWidth: 1.75 }), t.newRecord] })) }) })) : (_jsxs("button", { type: "button", className: "recordstable-addrow", onClick: addRow, children: [_jsx(Plus, { size: 15, strokeWidth: 1.75, "aria-hidden": "true" }), t.newRecord] }))] }), selected.size > 0 && (_jsxs(BulkBar, { count: selected.size, countLabel:
|
|
1015
|
+
: `Create your first ${cols.find((c) => c.type === "entity")?.label?.toLowerCase() ?? "record"} to get started.`, action: activeFilters ? (_jsxs(Button, { intent: "primary", size: "sm", onClick: () => setFilterConds([]), children: [_jsx(ListFilter, { size: 15, strokeWidth: 1.75 }), t.clearFilter] })) : (_jsxs(Button, { intent: "primary", size: "sm", onClick: addRow, children: [_jsx(Plus, { size: 15, strokeWidth: 1.75 }), t.newRecord] })) }) })) : (_jsxs("button", { type: "button", className: "recordstable-addrow", onClick: addRow, children: [_jsx(Plus, { size: 15, strokeWidth: 1.75, "aria-hidden": "true" }), t.newRecord] }))] }), selected.size > 0 && (_jsxs(BulkBar, { count: selected.size, countLabel: t.selectedCount, onClear: () => setSelected(new Set()), children: [_jsx(BulkBarButton, { icon: _jsx(ListPlus, { size: 15, strokeWidth: 1.75 }), onClick: addToList, children: t.addToList }), _jsx(BulkBarButton, { icon: _jsx(Mail, { size: 15, strokeWidth: 1.75 }), onClick: sendEmail, children: t.sendEmail }), _jsx(BulkBarButton, { icon: _jsx(Workflow, { size: 15, strokeWidth: 1.75 }), onClick: runWorkflow, children: t.runWorkflow }), _jsxs(Menu, { align: "end", "aria-label": t.moreActions, trigger: _jsx(BulkBarButton, { icon: _jsx(MoreHorizontal, { size: 15, strokeWidth: 1.75 }), children: t.more }), children: [_jsx(MenuItem, { icon: _jsx(Download, { size: 15, strokeWidth: 1.75 }), onSelect: exportSelected, children: t.exportCsv }), _jsx(MenuItem, { icon: _jsx(Trash2, { size: 15, strokeWidth: 1.75 }), danger: true, onSelect: deleteSelected, children: t.delete })] })] })), _jsx(CreateRecordDialog, { labels: t, open: creating, onOpenChange: setCreating, columns: cols, entityLabel: cols.find((c) => c.type === "entity")?.label ?? "record", onCreate: (row) => setData((prev) => [...prev, row]) }), ctxMenu &&
|
|
1013
1016
|
createPortal(_jsxs("div", { ref: ctxRef, className: "recordstable-menu recordstable-ctx", role: "menu", "aria-label": t.recordActions, style: { position: "fixed", top: ctxMenu.y, left: ctxMenu.x }, onKeyDown: onCtxKeyDown, children: [_jsxs("button", { type: "button", className: "recordstable-menu-item", role: "menuitem", tabIndex: -1, onClick: () => copyRow(ctxMenu.id), children: [_jsx(Copy, { size: 15, strokeWidth: 1.75 }), t.copy] }), _jsxs("button", { type: "button", className: "recordstable-menu-item", role: "menuitem", tabIndex: -1, onClick: () => duplicateRow(ctxMenu.id), children: [_jsx(CopyPlus, { size: 15, strokeWidth: 1.75 }), t.duplicate] }), _jsxs("button", { type: "button", className: "recordstable-menu-item", role: "menuitem", tabIndex: -1, onClick: () => pinRow(ctxMenu.id), children: [_jsx(Star, { size: 15, strokeWidth: 1.75 }), t.addToList] }), _jsxs("button", { type: "button", className: "recordstable-menu-item recordstable-menu-item--danger", role: "menuitem", tabIndex: -1, onClick: () => deleteRow(ctxMenu.id), children: [_jsx(Trash2, { size: 15, strokeWidth: 1.75 }), t.delete] })] }), rootRef.current ?? document.body)] }));
|
|
1014
1017
|
}
|
|
1015
1018
|
export const defaultColumns = [
|
|
@@ -81,7 +81,7 @@ export const ATTIO_SIDEBAR_FOOTER = {
|
|
|
81
81
|
function SectionHeader({ label, open, onToggle, }) {
|
|
82
82
|
return (_jsxs("button", { type: "button", className: "sidebar-section-header", "data-collapsed": open ? undefined : "", "aria-expanded": open, onClick: onToggle, children: [_jsx(IconChevronMini, {}), _jsx("span", { children: label })] }));
|
|
83
83
|
}
|
|
84
|
-
export const Sidebar = React.forwardRef(({ className, asChild, onToggleCollapse, collapseLabel = "Collapse sidebar", inboxLabel = "Inbox", workspace = ATTIO_SIDEBAR_WORKSPACE, items = ATTIO_SIDEBAR_ITEMS, recordsSection = ATTIO_SIDEBAR_RECORDS_SECTION, listsSection = ATTIO_SIDEBAR_LISTS_SECTION, chatsSection = ATTIO_SIDEBAR_CHATS_SECTION, footer = ATTIO_SIDEBAR_FOOTER, ...props }, ref) => {
|
|
84
|
+
export const Sidebar = React.forwardRef(({ className, asChild, onToggleCollapse, collapseLabel = "Collapse sidebar", inboxLabel = "Inbox", searchLabel = "Search", workspace = ATTIO_SIDEBAR_WORKSPACE, items = ATTIO_SIDEBAR_ITEMS, recordsSection = ATTIO_SIDEBAR_RECORDS_SECTION, listsSection = ATTIO_SIDEBAR_LISTS_SECTION, chatsSection = ATTIO_SIDEBAR_CHATS_SECTION, footer = ATTIO_SIDEBAR_FOOTER, ...props }, ref) => {
|
|
85
85
|
const [collapsedSections, setCollapsedSections] = React.useState(() => new Set());
|
|
86
86
|
const [commandOpen, setCommandOpen] = React.useState(false);
|
|
87
87
|
React.useEffect(() => {
|
|
@@ -107,6 +107,6 @@ export const Sidebar = React.forwardRef(({ className, asChild, onToggleCollapse,
|
|
|
107
107
|
return next;
|
|
108
108
|
});
|
|
109
109
|
}, []);
|
|
110
|
-
return (_jsxs(Primitive.div, { ref: ref, asChild: asChild, className: sidebarRecipe({ className }), ...props, children: [_jsxs("div", { className: "sidebar-ws", children: [_jsx(Avatar, { size: "lg", shape: "rounded", tone: workspace.avatarTone, children: workspace.avatarLabel }), _jsx("span", { className: "sidebar-ws-name", children: workspace.name }), _jsx(IconChevronDown, {}), _jsx(Button, { intent: "ghost", size: "sm", shape: "icon", className: "sidebar-ws-collapse", "aria-label": collapseLabel, onClick: onToggleCollapse, children: _jsx(IconCollapsePanel, {}) })] }), _jsxs("div", { className: "sidebar-searchrow", children: [_jsxs("button", { type: "button", className: "sidebar-search", onClick: () => setCommandOpen(true), children: [_jsx(IconSearch, {}), _jsx("span", { className: "sidebar-search-label", children:
|
|
110
|
+
return (_jsxs(Primitive.div, { ref: ref, asChild: asChild, className: sidebarRecipe({ className }), ...props, children: [_jsxs("div", { className: "sidebar-ws", children: [_jsx(Avatar, { size: "lg", shape: "rounded", tone: workspace.avatarTone, children: workspace.avatarLabel }), _jsx("span", { className: "sidebar-ws-name", children: workspace.name }), _jsx(IconChevronDown, {}), _jsx(Button, { intent: "ghost", size: "sm", shape: "icon", className: "sidebar-ws-collapse", "aria-label": collapseLabel, onClick: onToggleCollapse, children: _jsx(IconCollapsePanel, {}) })] }), _jsxs("div", { className: "sidebar-searchrow", children: [_jsxs("button", { type: "button", className: "sidebar-search", onClick: () => setCommandOpen(true), children: [_jsx(IconSearch, {}), _jsx("span", { className: "sidebar-search-label", children: searchLabel }), _jsx("span", { className: "sidebar-search-kbd", children: "\u2318K" })] }), _jsx(Button, { intent: "neutral", size: "sm", shape: "icon", "aria-label": inboxLabel, children: _jsx(IconInbox, {}) })] }), _jsx("div", { className: "sidebar-divider" }), _jsx("div", { className: "sidebar-section", children: items.map(({ label, icon: ItemIcon, active, trailing }) => (_jsx(NavItem, { href: "#", active: active, icon: _jsx(ItemIcon, {}), trailing: trailing, children: label }, label))) }), _jsx(SectionHeader, { label: recordsSection.label, open: !collapsedSections.has("records"), onToggle: () => toggleSection("records") }), _jsx("div", { className: "sidebar-section", "data-collapsed": collapsedSections.has("records") ? "" : undefined, children: recordsSection.items.map(({ label, tone, glyph: Glyph }) => (_jsx(NavItem, { href: "#", icon: _jsx(Avatar, { size: "sm", shape: "rounded", tone: tone, children: _jsx(Glyph, {}) }), children: label }, label))) }), _jsx(SectionHeader, { label: listsSection.label, open: !collapsedSections.has("lists"), onToggle: () => toggleSection("lists") }), _jsx("div", { className: "sidebar-section", "data-collapsed": collapsedSections.has("lists") ? "" : undefined, children: _jsx("button", { type: "button", className: "sidebar-newlist", children: listsSection.newListLabel }) }), _jsx(SectionHeader, { label: chatsSection.label, open: !collapsedSections.has("chats"), onToggle: () => toggleSection("chats") }), _jsx("div", { className: "sidebar-section", "data-collapsed": collapsedSections.has("chats") ? "" : undefined, children: chatsSection.items.map(({ label, icon: ItemIcon }) => (_jsx(NavItem, { href: "#", icon: _jsx(ItemIcon, {}), children: label }, label))) }), _jsxs("div", { className: "sidebar-footer", children: [_jsx(Button, { intent: "neutral", size: "sm", className: "sidebar-pill", children: footer.gettingStartedLabel }), _jsx(NavItem, { href: "#", icon: _jsx(IconInvite, {}), children: footer.inviteLabel }), _jsx("div", { className: "sidebar-divider" }), _jsxs("div", { className: "sidebar-trial", children: [_jsx("span", { children: footer.trialText }), _jsx(Button, { intent: "neutral", size: "sm", children: footer.trialActionLabel })] })] }), _jsx(Command, { open: commandOpen, onOpenChange: setCommandOpen })] }));
|
|
111
111
|
});
|
|
112
112
|
Sidebar.displayName = "Sidebar";
|
|
@@ -6,12 +6,15 @@ import { viewtoggleRecipe } from "./viewtoggle.recipe.js";
|
|
|
6
6
|
* of icon tiles; the selected tile is raised. Use for view-mode switches (list ⇄
|
|
7
7
|
* grid) or any small mutually-exclusive icon choice.
|
|
8
8
|
*/
|
|
9
|
-
export function ViewToggle({ options, value: valueProp, defaultValue, onChange, "aria-label": ariaLabel, className, ...rest }) {
|
|
9
|
+
export function ViewToggle({ options, value: valueProp, defaultValue, onValueChange, onChange, "aria-label": ariaLabel, className, ...rest }) {
|
|
10
10
|
const [internalValue, setInternalValue] = React.useState(defaultValue);
|
|
11
11
|
const value = valueProp ?? internalValue ?? options[0]?.value;
|
|
12
12
|
const pick = (next) => {
|
|
13
13
|
if (valueProp === undefined)
|
|
14
14
|
setInternalValue(next);
|
|
15
|
+
// both, so a consumer passing either name is heard and one passing both
|
|
16
|
+
// does not silently lose one
|
|
17
|
+
onValueChange?.(next);
|
|
15
18
|
onChange?.(next);
|
|
16
19
|
};
|
|
17
20
|
return (_jsx("div", { className: viewtoggleRecipe({ className }), ...rest, role: "group", "aria-label": ariaLabel, children: options.map((o) => (_jsx("button", { type: "button", className: "viewtoggle-tile", "aria-pressed": o.value === value, "aria-label": o.label, title: o.label, "data-active": o.value === value ? "" : undefined, onClick: () => pick(o.value), children: o.icon }, o.value))) }));
|
|
@@ -37,6 +37,10 @@ export type ColumnCalcProps<T> = Omit<React.ComponentPropsWithoutRef<"span">, "v
|
|
|
37
37
|
/** Controlled operation. */
|
|
38
38
|
op?: CalcOp;
|
|
39
39
|
onOpChange?: (op: CalcOp) => void;
|
|
40
|
+
/** Placeholder in the cell when no calculation is chosen. */
|
|
41
|
+
addLabel?: string;
|
|
42
|
+
/** Heading above the operation list in the menu. */
|
|
43
|
+
sectionLabel?: string;
|
|
40
44
|
/** Overrides the operation names shown in the menu and the cell. Partial —
|
|
41
45
|
* anything omitted keeps its English default from `CALC_LABEL`. */
|
|
42
46
|
opLabels?: Partial<Record<CalcOp, string>>;
|
|
@@ -46,4 +50,4 @@ export type ColumnCalcProps<T> = Omit<React.ComponentPropsWithoutRef<"span">, "v
|
|
|
46
50
|
align?: "left" | "right";
|
|
47
51
|
className?: string;
|
|
48
52
|
};
|
|
49
|
-
export declare function ColumnCalc<T>({ rows, value, numeric, defaultOp, op: opProp, onOpChange, format, align, opLabels, className, ...rest }: ColumnCalcProps<T>): React.JSX.Element;
|
|
53
|
+
export declare function ColumnCalc<T>({ rows, value, numeric, defaultOp, op: opProp, onOpChange, addLabel, sectionLabel, format, align, opLabels, className, ...rest }: ColumnCalcProps<T>): React.JSX.Element;
|
|
@@ -11,6 +11,14 @@ export type DatePickerFieldProps = Omit<React.ComponentPropsWithoutRef<"div">, "
|
|
|
11
11
|
/** Controlled value. Omit to let the field manage it (see defaultValue). */
|
|
12
12
|
value?: Date | null;
|
|
13
13
|
defaultValue?: Date | null;
|
|
14
|
+
/** Fires with the picked date, controlled or not. */
|
|
15
|
+
onValueChange?: (date: Date) => void;
|
|
16
|
+
/**
|
|
17
|
+
* The original name for `onValueChange`, kept working so no consumer breaks.
|
|
18
|
+
* Prefer `onValueChange`: it is what every other crisp component calls this,
|
|
19
|
+
* and passing the house name to a component that only knew `onChange` was
|
|
20
|
+
* not a type error — the handler simply never fired (#202).
|
|
21
|
+
*/
|
|
14
22
|
onChange?: (date: Date) => void;
|
|
15
23
|
placeholder?: string;
|
|
16
24
|
/** `default` = outlined trigger; `ghost` = transparent (for table cells). */
|
|
@@ -29,7 +37,7 @@ export type DatePickerFieldProps = Omit<React.ComponentPropsWithoutRef<"div">, "
|
|
|
29
37
|
* popover. This is what `DatePicker` used to be; `DatePicker` is now
|
|
30
38
|
* seed-design's bare calendar, and this composes it.
|
|
31
39
|
*/
|
|
32
|
-
export declare function DatePickerField({ value, defaultValue, onChange, placeholder, variant, format, presets, calendarProps, "aria-label": ariaLabel, className, ...rest }: DatePickerFieldProps): React.JSX.Element;
|
|
40
|
+
export declare function DatePickerField({ value, defaultValue, onValueChange, onChange, placeholder, variant, format, presets, calendarProps, "aria-label": ariaLabel, className, ...rest }: DatePickerFieldProps): React.JSX.Element;
|
|
33
41
|
export declare namespace DatePickerField {
|
|
34
42
|
var displayName: string;
|
|
35
43
|
}
|
|
@@ -17,6 +17,8 @@ export type MultiSelectProps = Omit<React.ComponentPropsWithoutRef<"div">, "valu
|
|
|
17
17
|
/** Show a filter field at the top of the list (default true). */
|
|
18
18
|
searchable?: boolean;
|
|
19
19
|
searchPlaceholder?: string;
|
|
20
|
+
/** Shown when the search matches nothing. */
|
|
21
|
+
emptyLabel?: string;
|
|
20
22
|
disabled?: boolean;
|
|
21
23
|
className?: string;
|
|
22
24
|
"aria-label"?: string;
|
|
@@ -27,7 +29,7 @@ export type MultiSelectProps = Omit<React.ComponentPropsWithoutRef<"div">, "valu
|
|
|
27
29
|
* popover with a search field over rows of "[chip] … [checkbox]" that toggle on
|
|
28
30
|
* click (the menu stays open). Keyboard: ↑/↓ move, Enter toggles, Esc closes.
|
|
29
31
|
*/
|
|
30
|
-
export declare function MultiSelect({ options, value: valueProp, defaultValue, onValueChange, placeholder, searchable, searchPlaceholder, disabled, className, "aria-label": ariaLabel, ...rest }: MultiSelectProps): React.JSX.Element;
|
|
32
|
+
export declare function MultiSelect({ options, value: valueProp, defaultValue, onValueChange, placeholder, searchable, searchPlaceholder, emptyLabel, disabled, className, "aria-label": ariaLabel, ...rest }: MultiSelectProps): React.JSX.Element;
|
|
31
33
|
export declare namespace MultiSelect {
|
|
32
34
|
var displayName: string;
|
|
33
35
|
}
|
|
@@ -22,6 +22,8 @@ export type SelectProps = Omit<React.ComponentPropsWithoutRef<"div">, "value" |
|
|
|
22
22
|
/** Show a search field at the top of the list that filters options (Linear/cmdk style). */
|
|
23
23
|
searchable?: boolean;
|
|
24
24
|
searchPlaceholder?: string;
|
|
25
|
+
/** Shown when the search matches nothing. */
|
|
26
|
+
emptyLabel?: string;
|
|
25
27
|
disabled?: boolean;
|
|
26
28
|
className?: string;
|
|
27
29
|
"aria-label"?: string;
|
|
@@ -34,7 +36,7 @@ export type SelectProps = Omit<React.ComponentPropsWithoutRef<"div">, "value" |
|
|
|
34
36
|
* "No results" empty state). Full keyboard model — ↑/↓ move, Enter/Space pick,
|
|
35
37
|
* Esc close, Home/End jump, opens upward when tight. Controlled or uncontrolled.
|
|
36
38
|
*/
|
|
37
|
-
export declare function Select({ options, value, defaultValue, onValueChange, placeholder, variant, width, searchable, searchPlaceholder, disabled, className, "aria-label": ariaLabel, id, ...rest }: SelectProps): React.JSX.Element;
|
|
39
|
+
export declare function Select({ options, value, defaultValue, onValueChange, placeholder, variant, width, searchable, searchPlaceholder, emptyLabel, disabled, className, "aria-label": ariaLabel, id, ...rest }: SelectProps): React.JSX.Element;
|
|
38
40
|
export declare namespace Select {
|
|
39
41
|
var displayName: string;
|
|
40
42
|
}
|
|
@@ -36,6 +36,14 @@ export type TableToolbarProps = Omit<React.ComponentPropsWithoutRef<"div">, "chi
|
|
|
36
36
|
count?: React.ReactNode;
|
|
37
37
|
/** Right-aligned actions after the count (a "New" button, say). */
|
|
38
38
|
children?: React.ReactNode;
|
|
39
|
+
/** Empty state in the sort popover. */
|
|
40
|
+
noSortsLabel?: string;
|
|
41
|
+
/** Label on the button that appends a sort condition. */
|
|
42
|
+
addSortLabel?: string;
|
|
43
|
+
/** Text of the help link, when `helpHref` is set. */
|
|
44
|
+
learnSortingLabel?: string;
|
|
45
|
+
/** Placeholder on a filter condition's operator picker. */
|
|
46
|
+
selectConditionLabel?: string;
|
|
39
47
|
/** Shows a "Learn about sorting" link in the sort popover when set. */
|
|
40
48
|
helpHref?: string;
|
|
41
49
|
/** aria-label on the Sort trigger (default "Sort"). */
|
|
@@ -85,5 +93,5 @@ export declare function applySorts<T>(rows: T[], sorts: ToolbarSort[], get?: (ro
|
|
|
85
93
|
* composable version, to put above `DataTable` or any table of your own. Pair
|
|
86
94
|
* it with `applyFilters` / `applySorts` to turn its state into rows.
|
|
87
95
|
*/
|
|
88
|
-
export declare function TableToolbar({ columns, sorts: sortsProp, defaultSorts, onSortsChange, filters: filtersProp, defaultFilters, onFiltersChange, count, children, helpHref, className, sortLabel, reorderSortLabel, sortFieldLabel, sortDirectionLabel, removeSortLabel, filterFieldLabel, filterFieldPlaceholder, filterValuePlaceholder, ascendingLabel, descendingLabel, sortTriggerLabel, filterTriggerLabel, ...rest }: TableToolbarProps): React.JSX.Element;
|
|
96
|
+
export declare function TableToolbar({ columns, sorts: sortsProp, defaultSorts, onSortsChange, filters: filtersProp, defaultFilters, onFiltersChange, count, children, helpHref, className, sortLabel, reorderSortLabel, noSortsLabel, addSortLabel, learnSortingLabel, selectConditionLabel, sortFieldLabel, sortDirectionLabel, removeSortLabel, filterFieldLabel, filterFieldPlaceholder, filterValuePlaceholder, ascendingLabel, descendingLabel, sortTriggerLabel, filterTriggerLabel, ...rest }: TableToolbarProps): React.JSX.Element;
|
|
89
97
|
export {};
|
|
@@ -40,6 +40,8 @@ export type RecordPageLabels = {
|
|
|
40
40
|
composeEmail: string;
|
|
41
41
|
askAttio: string;
|
|
42
42
|
addToList: string;
|
|
43
|
+
/** Empty state under the Lists panel. */
|
|
44
|
+
noLists: string;
|
|
43
45
|
highlights: string;
|
|
44
46
|
recordDetails: string;
|
|
45
47
|
searchFields: string;
|
|
@@ -52,6 +54,14 @@ export type RecordPageLabels = {
|
|
|
52
54
|
viewLess: string;
|
|
53
55
|
newNote: string;
|
|
54
56
|
newTask: string;
|
|
57
|
+
/** Placeholder in the note composer. */
|
|
58
|
+
notePlaceholder: string;
|
|
59
|
+
/** Submit button in the note composer. */
|
|
60
|
+
saveNote: string;
|
|
61
|
+
/** Placeholder in the task composer. */
|
|
62
|
+
taskPlaceholder: string;
|
|
63
|
+
/** Submit button in the task composer. */
|
|
64
|
+
addTask: string;
|
|
55
65
|
cancel: string;
|
|
56
66
|
noNotesYet: string;
|
|
57
67
|
noTasksYet: string;
|
|
@@ -146,6 +146,12 @@ export type RecordsTableLabels = {
|
|
|
146
146
|
duplicate: string;
|
|
147
147
|
addToList: string;
|
|
148
148
|
inAList: string;
|
|
149
|
+
/** Placeholder in the record search field. */
|
|
150
|
+
searchRecordsPlaceholder: string;
|
|
151
|
+
/** Placeholder in a select cell's option search. */
|
|
152
|
+
searchOptionsPlaceholder: string;
|
|
153
|
+
/** Prefix on the bulk bar's selected count. */
|
|
154
|
+
selectedCount: string;
|
|
149
155
|
delete: string;
|
|
150
156
|
sendEmail: string;
|
|
151
157
|
exportCsv: string;
|
|
@@ -52,6 +52,8 @@ export type SidebarProps = React.ComponentPropsWithoutRef<"nav"> & {
|
|
|
52
52
|
collapseLabel?: string;
|
|
53
53
|
/** Label for the inbox quick-action. */
|
|
54
54
|
inboxLabel?: string;
|
|
55
|
+
/** Label for the search / command-palette button. */
|
|
56
|
+
searchLabel?: string;
|
|
55
57
|
asChild?: boolean;
|
|
56
58
|
/** Called when the collapse-panel button in the header is pressed. */
|
|
57
59
|
onToggleCollapse?: () => void;
|
|
@@ -85,6 +87,8 @@ export declare const Sidebar: React.ForwardRefExoticComponent<Omit<React.Detaile
|
|
|
85
87
|
collapseLabel?: string;
|
|
86
88
|
/** Label for the inbox quick-action. */
|
|
87
89
|
inboxLabel?: string;
|
|
90
|
+
/** Label for the search / command-palette button. */
|
|
91
|
+
searchLabel?: string;
|
|
88
92
|
asChild?: boolean;
|
|
89
93
|
/** Called when the collapse-panel button in the header is pressed. */
|
|
90
94
|
onToggleCollapse?: () => void;
|
|
@@ -12,9 +12,15 @@ export type ViewToggleProps<T extends string = string> = Omit<React.ComponentPro
|
|
|
12
12
|
value?: T;
|
|
13
13
|
/** Initially selected value, when uncontrolled. Defaults to the first option. */
|
|
14
14
|
defaultValue?: T;
|
|
15
|
-
/** Fires with the next value on every pick, controlled or not.
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
/** Fires with the next value on every pick, controlled or not. */
|
|
16
|
+
onValueChange?: (value: T) => void;
|
|
17
|
+
/**
|
|
18
|
+
* The original name for `onValueChange`, kept working so no consumer breaks.
|
|
19
|
+
* Prefer `onValueChange`: it is what every other crisp component calls this,
|
|
20
|
+
* and because these props extend a `<div>`'s, passing the house name to a
|
|
21
|
+
* component that only knew `onChange` was not a type error — it landed on
|
|
22
|
+
* the DOM and React dropped it with a development-only warning (#202).
|
|
23
|
+
*/
|
|
18
24
|
onChange?: (value: T) => void;
|
|
19
25
|
"aria-label"?: string;
|
|
20
26
|
className?: string;
|
|
@@ -24,7 +30,7 @@ export type ViewToggleProps<T extends string = string> = Omit<React.ComponentPro
|
|
|
24
30
|
* of icon tiles; the selected tile is raised. Use for view-mode switches (list ⇄
|
|
25
31
|
* grid) or any small mutually-exclusive icon choice.
|
|
26
32
|
*/
|
|
27
|
-
export declare function ViewToggle<T extends string = string>({ options, value: valueProp, defaultValue, onChange, "aria-label": ariaLabel, className, ...rest }: ViewToggleProps<T>): React.JSX.Element;
|
|
33
|
+
export declare function ViewToggle<T extends string = string>({ options, value: valueProp, defaultValue, onValueChange, onChange, "aria-label": ariaLabel, className, ...rest }: ViewToggleProps<T>): React.JSX.Element;
|
|
28
34
|
export declare namespace ViewToggle {
|
|
29
35
|
var displayName: string;
|
|
30
36
|
}
|