@crisp-ui-kit/crisp 0.50.0 → 0.51.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/composewindow/composewindow.js +144 -0
- package/dist/cjs/components/composewindow/composewindow.recipe.js +45 -0
- package/dist/cjs/components/composewindow/index.js +8 -0
- package/dist/cjs/components/datatable/datatable.js +224 -43
- package/dist/cjs/components/datatable/datatable.recipe.js +37 -3
- package/dist/cjs/components/datatable/index.js +6 -0
- package/dist/cjs/components/datepickerfield/datepickerfield.js +25 -15
- package/dist/cjs/components/datepickerfield/datepickerfield.recipe.js +25 -8
- package/dist/cjs/components/dialog/dialog.recipe.js +9 -1
- package/dist/cjs/components/menu/menu.js +4 -2
- package/dist/cjs/components/multiselect/multiselect.recipe.js +14 -8
- package/dist/cjs/components/select/select.js +2 -1
- package/dist/cjs/components/table/table.recipe.js +12 -3
- package/dist/cjs/components/tabletoolbar/tabletoolbar.chrome.js +1 -1
- package/dist/cjs/components/tabletoolbar/tabletoolbar.js +18 -7
- package/dist/cjs/core/cell-date.js +11 -0
- package/dist/cjs/core/index.js +2 -1
- package/dist/cjs/index.js +1 -0
- package/dist/cjs/product/recordpage/recordpage.js +88 -10
- package/dist/cjs/product/recordpage/recordpage.recipe.js +30 -0
- package/dist/cjs/product/recordpanel/recordpanel.js +54 -4
- package/dist/cjs/product/recordpanel/recordpanel.recipe.js +29 -0
- package/dist/cjs/product/recordstable/editable-field.js +110 -0
- package/dist/cjs/product/recordstable/index.js +4 -1
- package/dist/cjs/product/recordstable/recordstable.js +67 -11
- package/dist/cjs/product/recordstable/recordstable.recipe.js +9 -1
- package/dist/cjs/product/settingsshell/settingsshell.recipe.js +13 -4
- package/dist/cjs/product/sidebar/sidebar.js +15 -3
- package/dist/cjs/product/sidebar/sidebar.recipe.js +21 -2
- package/dist/composewindow.css +24 -0
- package/dist/composewindow.d.ts +7 -0
- package/dist/composewindow.layered.css +26 -0
- package/dist/datatable.css +33 -14
- package/dist/datatable.layered.css +33 -14
- package/dist/datepickerfield.css +9 -4
- package/dist/datepickerfield.layered.css +9 -4
- package/dist/dialog.css +2 -1
- package/dist/dialog.layered.css +2 -1
- package/dist/esm/components/composewindow/composewindow.js +107 -0
- package/dist/esm/components/composewindow/composewindow.recipe.js +42 -0
- package/dist/esm/components/composewindow/index.js +2 -0
- package/dist/esm/components/datatable/datatable.js +226 -45
- package/dist/esm/components/datatable/datatable.recipe.js +37 -3
- package/dist/esm/components/datatable/index.js +6 -0
- package/dist/esm/components/datepickerfield/datepickerfield.js +26 -16
- package/dist/esm/components/datepickerfield/datepickerfield.recipe.js +25 -8
- package/dist/esm/components/dialog/dialog.recipe.js +9 -1
- package/dist/esm/components/menu/menu.js +4 -2
- package/dist/esm/components/multiselect/multiselect.recipe.js +14 -8
- package/dist/esm/components/select/select.js +2 -1
- package/dist/esm/components/table/table.recipe.js +12 -3
- package/dist/esm/components/tabletoolbar/tabletoolbar.chrome.js +1 -1
- package/dist/esm/components/tabletoolbar/tabletoolbar.js +18 -7
- package/dist/esm/core/cell-date.js +10 -0
- package/dist/esm/core/index.js +1 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/product/recordpage/recordpage.js +89 -11
- package/dist/esm/product/recordpage/recordpage.recipe.js +30 -0
- package/dist/esm/product/recordpanel/recordpanel.js +55 -5
- package/dist/esm/product/recordpanel/recordpanel.recipe.js +29 -0
- package/dist/esm/product/recordstable/editable-field.js +73 -0
- package/dist/esm/product/recordstable/index.js +1 -0
- package/dist/esm/product/recordstable/recordstable.js +69 -13
- package/dist/esm/product/recordstable/recordstable.recipe.js +9 -1
- package/dist/esm/product/settingsshell/settingsshell.recipe.js +13 -4
- package/dist/esm/product/sidebar/sidebar.js +15 -3
- package/dist/esm/product/sidebar/sidebar.recipe.js +21 -2
- package/dist/multiselect.css +8 -8
- package/dist/multiselect.layered.css +8 -8
- package/dist/recordpage.css +25 -13
- package/dist/recordpage.layered.css +25 -13
- package/dist/recordpanel.css +22 -5
- package/dist/recordpanel.layered.css +22 -5
- package/dist/recordstable.css +9 -2
- package/dist/recordstable.layered.css +9 -2
- package/dist/settingsshell.css +2 -2
- package/dist/settingsshell.layered.css +2 -2
- package/dist/sidebar.css +7 -2
- package/dist/sidebar.layered.css +7 -2
- package/dist/styles.css +147 -54
- package/dist/styles.layered.css +147 -54
- package/dist/table.css +4 -2
- package/dist/table.layered.css +4 -2
- package/dist/tabletoolbar.css +1 -1
- package/dist/tabletoolbar.layered.css +1 -1
- package/dist/types/components/composewindow/composewindow.d.ts +80 -0
- package/dist/types/components/composewindow/composewindow.recipe.d.ts +3 -0
- package/dist/types/components/composewindow/index.d.ts +2 -0
- package/dist/types/components/datatable/datatable.d.ts +58 -4
- package/dist/types/components/datatable/index.d.ts +1 -1
- package/dist/types/components/datepickerfield/datepickerfield.d.ts +18 -4
- package/dist/types/components/menu/menu.d.ts +10 -1
- package/dist/types/components/select/select.d.ts +6 -1
- package/dist/types/components/tabletoolbar/tabletoolbar.d.ts +29 -5
- package/dist/types/core/cell-date.d.ts +17 -0
- package/dist/types/core/index.d.ts +1 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/product/recordpage/recordpage.d.ts +59 -1
- package/dist/types/product/recordpanel/recordpanel.d.ts +11 -1
- package/dist/types/product/recordstable/editable-field.d.ts +45 -0
- package/dist/types/product/recordstable/index.d.ts +1 -0
- package/dist/types/product/recordstable/recordstable.d.ts +29 -0
- package/dist/types/product/sidebar/sidebar.d.ts +60 -4
- package/package.json +6 -1
|
@@ -262,15 +262,17 @@ function EntityCell({ value, pinned, onOpenPage, avatarTone = "brand", labels, }
|
|
|
262
262
|
}
|
|
263
263
|
function CellView({ column, row, pinned, onOpenPage, avatarTone = "brand", labels, }) {
|
|
264
264
|
const value = row[column.key];
|
|
265
|
-
// `render` fully owns the cell's content — no empty
|
|
266
|
-
//
|
|
265
|
+
// `render` fully owns the cell's content — no empty fallback, no type
|
|
266
|
+
// renderer; it's the only thing drawn here (see the doc comment on
|
|
267
267
|
// `Column.render`).
|
|
268
268
|
if (column.render)
|
|
269
269
|
return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: column.render(value, row) });
|
|
270
270
|
if (value == null ||
|
|
271
271
|
value === "" ||
|
|
272
272
|
(Array.isArray(value) && !value.length)) {
|
|
273
|
-
|
|
273
|
+
// Attio's own empty cell is genuinely blank -- no placeholder character
|
|
274
|
+
// at all (#412).
|
|
275
|
+
return null;
|
|
274
276
|
}
|
|
275
277
|
// `format` keeps the type renderer's chrome below and only replaces the
|
|
276
278
|
// text it displays.
|
|
@@ -313,11 +315,40 @@ function CellView({ column, row, pinned, onOpenPage, avatarTone = "brand", label
|
|
|
313
315
|
function StatusCell({ column, row, onChange, }) {
|
|
314
316
|
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 ?? [] }));
|
|
315
317
|
}
|
|
318
|
+
// A sentinel that can't collide with a real record id (crisp never generates
|
|
319
|
+
// one, and a consumer's own ids come from their own data store) -- picking
|
|
320
|
+
// this row calls `onCreateReference`, not `onChange` (#449).
|
|
321
|
+
const CREATE_REFERENCE_VALUE = "__crisp_create_reference__";
|
|
322
|
+
/** The 16px avatar Attio's own reference picker draws before every option's
|
|
323
|
+
* label -- a photo when the option carries one, a generic person glyph
|
|
324
|
+
* otherwise (measured live, #449: Attio's own fallback is an icon, not
|
|
325
|
+
* initials -- a different, deliberate choice from the entity cell's own
|
|
326
|
+
* initials fallback elsewhere in this file). */
|
|
327
|
+
function referenceOptionLabel(o) {
|
|
328
|
+
return ((0, jsx_runtime_1.jsxs)("span", { className: "crisp-recordstable-refoption", children: [(0, jsx_runtime_1.jsx)(avatar_1.Avatar, { size: "sm", className: "crisp-recordstable-refoption-avatar", children: o.avatar ? ((0, jsx_runtime_1.jsx)("img", { src: o.avatar, alt: "" })) : ((0, jsx_runtime_1.jsx)(lucide_react_1.CircleUserRound, { size: 12, strokeWidth: 1.75, "aria-hidden": "true" })) }), o.label] }));
|
|
329
|
+
}
|
|
316
330
|
function ReferenceCell({ column, row, onChange, labels, }) {
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
331
|
+
const [query, setQuery] = React.useState("");
|
|
332
|
+
const options = (column.refOptions ?? []).map((o) => ({
|
|
333
|
+
value: o.value,
|
|
334
|
+
label: referenceOptionLabel(o),
|
|
335
|
+
keywords: o.label,
|
|
336
|
+
}));
|
|
337
|
+
if (column.onCreateReference) {
|
|
338
|
+
options.push({
|
|
339
|
+
value: CREATE_REFERENCE_VALUE,
|
|
340
|
+
label: ((0, jsx_runtime_1.jsxs)("span", { className: "crisp-recordstable-refoption crisp-recordstable-refoption--create", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Plus, { size: 16, strokeWidth: 1.75, "aria-hidden": "true" }), `Create ${column.createLabel ?? "record"}`] })),
|
|
341
|
+
// always reachable regardless of the typed query -- it's an action,
|
|
342
|
+
// not a record to filter away
|
|
343
|
+
keywords: query,
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
return ((0, jsx_runtime_1.jsx)(select_1.Select, { variant: "ghost", searchable: true, searchPlaceholder: labels.searchRecordsPlaceholder, onSearchChange: setQuery, "aria-label": column.label, placeholder: labels.setRecordPlaceholder, value: row[column.key] ?? undefined, onValueChange: (v) => {
|
|
347
|
+
if (v === CREATE_REFERENCE_VALUE)
|
|
348
|
+
column.onCreateReference?.(query);
|
|
349
|
+
else
|
|
350
|
+
onChange(v);
|
|
351
|
+
}, options: options }));
|
|
321
352
|
}
|
|
322
353
|
function TagsCell({ column, row, options, onChange, labels, }) {
|
|
323
354
|
const value = row[column.key] ?? [];
|
|
@@ -343,10 +374,35 @@ function TagsCell({ column, row, options, onChange, labels, }) {
|
|
|
343
374
|
const core_2 = require("../../core");
|
|
344
375
|
Object.defineProperty(exports, "formatCellDate", { enumerable: true, get: function () { return core_2.formatCellDate; } });
|
|
345
376
|
Object.defineProperty(exports, "parseCellDate", { enumerable: true, get: function () { return core_2.parseCellDate; } });
|
|
377
|
+
// "1998" for a year-only raw value, "Sep 1998" for year-month, and the full
|
|
378
|
+
// "Sep 4, 1998" only once the raw value actually named a day (#312) --
|
|
379
|
+
// DatePickerField's own default format always prints full precision, since
|
|
380
|
+
// as a general calendar field it has no raw string to read a precision from.
|
|
381
|
+
function formatByPrecision(d, precision) {
|
|
382
|
+
if (precision === "year")
|
|
383
|
+
return String(d.getFullYear());
|
|
384
|
+
return d.toLocaleDateString(undefined, {
|
|
385
|
+
month: "short",
|
|
386
|
+
day: precision === "month" ? undefined : "numeric",
|
|
387
|
+
year: "numeric",
|
|
388
|
+
});
|
|
389
|
+
}
|
|
346
390
|
function DateCell({ column, row, onChange, }) {
|
|
347
391
|
const raw = row[column.key];
|
|
348
392
|
const value = (0, core_2.parseCellDate)(raw);
|
|
349
|
-
|
|
393
|
+
const precision = raw ? (0, core_2.cellDatePrecision)(raw) : "day";
|
|
394
|
+
return ((0, jsx_runtime_1.jsx)(datepickerfield_1.DatePickerField, { variant: "ghost", "aria-label": column.label, value: value,
|
|
395
|
+
// Out of scope for #394 (which added Clear to DatePickerField itself):
|
|
396
|
+
// a cell's OWN clear-to-empty affordance is a different, unmeasured
|
|
397
|
+
// question, and this callback's `string` contract has no "empty" of
|
|
398
|
+
// its own to report yet. Keeps this cell's exact pre-#394 behavior.
|
|
399
|
+
clearable: false,
|
|
400
|
+
// #417: the trigger displays the value at its stored precision
|
|
401
|
+
// (year-only stays year-only) rather than always a full date.
|
|
402
|
+
format: (d) => formatByPrecision(d, precision), onChange: (d) => {
|
|
403
|
+
if (d)
|
|
404
|
+
onChange((0, core_2.formatCellDate)(d));
|
|
405
|
+
} }));
|
|
350
406
|
}
|
|
351
407
|
/**
|
|
352
408
|
* The "New <object>" create modal — measured from a create-record dialog:
|
|
@@ -374,7 +430,7 @@ function CreateRecordDialog({ open, onOpenChange, columns, entityLabel, onCreate
|
|
|
374
430
|
row[col.key] = { name: raw || labels.untitled };
|
|
375
431
|
}
|
|
376
432
|
else if (!raw) {
|
|
377
|
-
// leave empty fields unset so the cell renders
|
|
433
|
+
// leave empty fields unset so the cell renders blank (#412)
|
|
378
434
|
}
|
|
379
435
|
else if (col.type === "tags") {
|
|
380
436
|
row[col.key] = raw
|
|
@@ -406,7 +462,7 @@ function CreateRecordDialog({ open, onOpenChange, columns, entityLabel, onCreate
|
|
|
406
462
|
return ((0, jsx_runtime_1.jsxs)(dialog_1.Dialog, { open: open, onOpenChange: onOpenChange, title: labels.newRecordTitle(entityLabel), icon: EntityIcon ? (0, jsx_runtime_1.jsx)(EntityIcon, { size: 16, strokeWidth: 1.75 }) : undefined, width: 720, children: [(0, jsx_runtime_1.jsx)(dialog_1.DialogBody, { children: columns.map((col, i) => ((0, jsx_runtime_1.jsxs)("div", { className: "crisp-dialog-field", children: [(0, jsx_runtime_1.jsx)("label", { className: "crisp-dialog-field-label", htmlFor: `create-${col.key}`, children: col.label }), (0, jsx_runtime_1.jsx)(input_1.Input, { id: `create-${col.key}`, ref: i === 0 ? firstRef : undefined, value: values[col.key] ?? "", onChange: (e) => set(col.key, e.currentTarget.value), placeholder: labels.setFieldPlaceholder(col.label), onKeyDown: (e) => {
|
|
407
463
|
if (e.key === "Enter" && (e.metaKey || e.ctrlKey))
|
|
408
464
|
submit();
|
|
409
|
-
} })] }, col.key))) }), (0, jsx_runtime_1.jsxs)(dialog_1.DialogFooter, { children: [(0, jsx_runtime_1.jsxs)("span", { className: "crisp-dialog-more", children: [
|
|
465
|
+
} })] }, col.key))) }), (0, jsx_runtime_1.jsxs)(dialog_1.DialogFooter, { children: [(0, jsx_runtime_1.jsxs)("span", { className: "crisp-dialog-more", children: [(0, jsx_runtime_1.jsx)(switch_1.Switch, { checked: createMore, onCheckedChange: setCreateMore, "aria-label": labels.createMore }), labels.createMore] }), (0, jsx_runtime_1.jsxs)("button", { type: "button", className: "crisp-dialog-submit", onClick: submit, children: [labels.createRecord, (0, jsx_runtime_1.jsxs)("span", { className: "crisp-dialog-submit-key", "aria-hidden": "true", children: [(0, jsx_runtime_1.jsx)("kbd", { children: "\u2318" }), (0, jsx_runtime_1.jsx)("kbd", { children: (0, jsx_runtime_1.jsx)(lucide_react_1.CornerDownLeft, { size: 10, strokeWidth: 2 }) })] })] })] })] }));
|
|
410
466
|
}
|
|
411
467
|
/**
|
|
412
468
|
* A measured-style record grid — fully featured. Toolbar (search / sort status /
|
|
@@ -1073,7 +1129,7 @@ function RecordsTable({ labels, columns: columnsProp = exports.defaultColumns, r
|
|
|
1073
1129
|
setDragCol(col.key);
|
|
1074
1130
|
e.dataTransfer.effectAllowed = "move";
|
|
1075
1131
|
}
|
|
1076
|
-
: undefined, onDragEnd: () => setDragCol(null), onClick: (e) => openColumnMenu(col.key, e.currentTarget), children: [Icon ? (0, jsx_runtime_1.jsx)(Icon, { size:
|
|
1132
|
+
: undefined, onDragEnd: () => setDragCol(null), onClick: (e) => openColumnMenu(col.key, e.currentTarget), children: [Icon ? (0, jsx_runtime_1.jsx)(Icon, { size: 16, strokeWidth: 1.75 }) : null, (0, jsx_runtime_1.jsx)("span", { className: "crisp-recordstable-th-label", children: col.label }), (0, jsx_runtime_1.jsx)("span", { className: "crisp-recordstable-sort", children: (0, jsx_runtime_1.jsx)("svg", { width: "12", height: "12", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "1.5", "aria-hidden": "true", children: (0, jsx_runtime_1.jsx)("path", { d: "m4 6 4 4 4-4" }) }) })] })), openMenu === `col:${col.key}` &&
|
|
1077
1133
|
rootRef.current &&
|
|
1078
1134
|
(0, react_dom_1.createPortal)((0, jsx_runtime_1.jsxs)("div", { ref: menuRef, className: "crisp-recordstable-menu crisp-recordstable-menu--fixed crisp-recordstable-menu--hdr", role: "menu", "aria-label": `${col.label} column`, style: menuPos ?? undefined, onKeyDown: onMenuKeyDown, children: [(0, jsx_runtime_1.jsxs)("button", { type: "button", className: "crisp-recordstable-menu-item", role: "menuitem", tabIndex: -1, onClick: () => sortBy(col.key, "asc"), children: [(0, jsx_runtime_1.jsx)(lucide_react_1.ArrowUp, { size: 15, strokeWidth: 1.75 }), t.sortAscending] }), (0, jsx_runtime_1.jsxs)("button", { type: "button", className: "crisp-recordstable-menu-item", role: "menuitem", tabIndex: -1, onClick: () => sortBy(col.key, "desc"), children: [(0, jsx_runtime_1.jsx)(lucide_react_1.ArrowDown, { size: 15, strokeWidth: 1.75 }), t.sortDescending] }), (0, jsx_runtime_1.jsxs)("button", { type: "button", className: "crisp-recordstable-menu-item", role: "menuitem", tabIndex: -1, disabled: i <= 1, onClick: () => moveColumn(col.key, -1), children: [(0, jsx_runtime_1.jsx)(lucide_react_1.ArrowLeft, { "data-rtl-flip": "", size: 15, strokeWidth: 1.75 }), t.moveLeft] }), (0, jsx_runtime_1.jsxs)("button", { type: "button", className: "crisp-recordstable-menu-item", role: "menuitem", tabIndex: -1, disabled: i === 0 || i >= cols.length - 1, onClick: () => moveColumn(col.key, 1), children: [(0, jsx_runtime_1.jsx)(lucide_react_1.ArrowRight, { "data-rtl-flip": "", size: 15, strokeWidth: 1.75 }), t.moveRight] }), (0, jsx_runtime_1.jsx)("div", { className: "crisp-recordstable-menu-sep" }), (0, jsx_runtime_1.jsxs)("button", { type: "button", className: "crisp-recordstable-menu-item", role: "menuitem", tabIndex: -1, onClick: () => {
|
|
1079
1135
|
setRenamingCol(col.key);
|
|
@@ -74,6 +74,14 @@ ${(0, tabletoolbar_chrome_1.toolbarChromeBody)("recordstable")}
|
|
|
74
74
|
& .crisp-recordstable-td--picker .crisp-multiselect-chips .crisp-recordstable-tag { flex-shrink: 0; max-width: none; }
|
|
75
75
|
& .crisp-recordstable-td--picker .crisp-multiselect-chevron { display: none; }
|
|
76
76
|
& .crisp-recordstable-td--picker .crisp-multiselect-placeholder { color: ${tokens_1.contract.fgMuted}; }
|
|
77
|
+
/* reference picker: a leading 16px avatar per option, plus a trailing
|
|
78
|
+
"Create X" row when the consumer supplies one (#449) -- measured live
|
|
79
|
+
against Attio's own picker (16px circular avatar, 16px plain icon for
|
|
80
|
+
the create row, both sitting in the option's existing 8px gap, no new
|
|
81
|
+
gap value invented). */
|
|
82
|
+
& .crisp-recordstable-refoption { display: flex; align-items: center; gap: ${tokens_1.contract.space["2"]}; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
|
|
83
|
+
& .crisp-recordstable-refoption-avatar { flex-shrink: 0; }
|
|
84
|
+
& .crisp-recordstable-refoption--create { color: ${tokens_1.contract.fgMuted}; }
|
|
77
85
|
& .crisp-recordstable-status { display: inline-flex; align-items: center; gap: ${tokens_1.contract.space["1.5"]}; min-width: 0; }
|
|
78
86
|
& .crisp-recordstable-status-dot { flex-shrink: 0; width: 8px; height: 8px; border-radius: ${tokens_1.contract.radius.full}; }
|
|
79
87
|
& .crisp-recordstable-cell { display: flex; align-items: center; gap: ${tokens_1.contract.space["2"]}; height: 100%; min-width: 0; }
|
|
@@ -132,7 +140,7 @@ ${(0, tabletoolbar_chrome_1.toolbarChromeBody)("recordstable")}
|
|
|
132
140
|
& .crisp-recordstable-empty { display: flex; align-items: center; justify-content: center; min-height: 320px; padding: ${tokens_1.contract.space["8"]} ${tokens_1.contract.space["4"]}; }
|
|
133
141
|
|
|
134
142
|
/* summary footer — per-column aggregation, sticky to the bottom */
|
|
135
|
-
& .crisp-recordstable-sum { position: sticky; bottom: 0; z-index: 2; height: ${tokens_1.contract.size.tableRow}; padding: 0; border-top: 1px solid ${tokens_1.contract.borderSubtle}; border-inline-end: 1px solid ${tokens_1.contract.borderSubtle}; background: ${tokens_1.contract.bgSurface}; }
|
|
143
|
+
& .crisp-recordstable-sum { position: sticky; bottom: 0; z-index: 2; height: ${tokens_1.contract.size.tableRow}; padding: 0; border-top: 1px solid ${tokens_1.contract.borderSubtle}; border-bottom: 1px solid ${tokens_1.contract.borderSubtle}; border-inline-end: 1px solid ${tokens_1.contract.borderSubtle}; background: ${tokens_1.contract.bgSurface}; }
|
|
136
144
|
& tfoot .crisp-recordstable-col-check, & tfoot .crisp-recordstable-col-entity { position: sticky; bottom: 0; z-index: 3; }
|
|
137
145
|
& .crisp-recordstable-sum:last-child { border-inline-end: none; }
|
|
138
146
|
& .crisp-recordstable-sum-btn { display: flex; align-items: center; justify-content: flex-end; gap: ${tokens_1.contract.space["1.5"]}; width: 100%; height: 100%; margin: 0; padding: 0 ${tokens_1.contract.space["3"]}; border: none; background: none; color: color-mix(in srgb, ${tokens_1.contract.fgPrimary} 63%, transparent); font-family: inherit; font-size: ${tokens_1.contract.text.lg}; font-weight: ${tokens_1.contract.weight.medium}; cursor: pointer; white-space: nowrap; overflow: hidden; }
|
|
@@ -3,15 +3,24 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.settingsshellRecipe = void 0;
|
|
4
4
|
const core_1 = require("../../core");
|
|
5
5
|
const tokens_1 = require("../../tokens");
|
|
6
|
-
// The Settings layout — measured from a Settings screen: a
|
|
6
|
+
// The Settings layout — measured from a Settings screen: a 275px left nav with a
|
|
7
7
|
// back header, a search field, and grouped nav items (active row raised), beside
|
|
8
|
-
// a scrolling content pane.
|
|
8
|
+
// a scrolling content pane. Fills whatever height its container gives it, the
|
|
9
|
+
// way Attio's settings nav runs the container's full height (#435) — the root
|
|
10
|
+
// used to carry only `min-height: 0` (lets it *shrink* inside a flex ancestor)
|
|
11
|
+
// with no `height` of its own, so it sized to its content instead of filling,
|
|
12
|
+
// and the nav's own border-inline-end stopped where the nav content ended
|
|
13
|
+
// rather than running the full height. A consumer (GRC) had to paper over it
|
|
14
|
+
// with an external `h-full` class.
|
|
15
|
+
// 275px, not the 256px this recipe used to claim (#410) — the same width
|
|
16
|
+
// AppShell's own Attio-measured `defaultWidth` already uses for the main
|
|
17
|
+
// sidebar pane; re-measured live at app.attio.com/<workspace>/settings/account.
|
|
9
18
|
const config = (0, core_1.defineRecipe)({
|
|
10
19
|
name: "settingsshell",
|
|
11
|
-
base: `display: flex; min-height: 0; box-sizing: border-box; font-family: ${tokens_1.contract.font.sans}; color: ${tokens_1.contract.fgPrimary};
|
|
20
|
+
base: `display: flex; height: 100%; min-height: 0; box-sizing: border-box; font-family: ${tokens_1.contract.font.sans}; color: ${tokens_1.contract.fgPrimary};
|
|
12
21
|
-webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
|
|
13
22
|
|
|
14
|
-
& .crisp-settingsshell-nav { display: flex; flex-direction: column; width:
|
|
23
|
+
& .crisp-settingsshell-nav { display: flex; flex-direction: column; width: 275px; flex-shrink: 0; min-height: 0; padding: ${tokens_1.contract.space["3"]}; border-inline-end: 1px solid ${tokens_1.contract.borderSubtle}; }
|
|
15
24
|
& .crisp-settingsshell-back { display: inline-flex; align-items: center; gap: ${tokens_1.contract.space["1.5"]}; height: 28px; padding: 0 ${tokens_1.contract.space["1"]}; margin: 0 0 ${tokens_1.contract.space["2"]}; border: none; background: none; font-family: inherit; font-size: ${tokens_1.contract.text.lg}; font-weight: ${tokens_1.contract.weight.semibold}; color: ${tokens_1.contract.fgPrimary}; cursor: pointer; border-radius: ${tokens_1.contract.radius.md}; }
|
|
16
25
|
& .crisp-settingsshell-back svg { color: ${tokens_1.contract.fgMuted}; }
|
|
17
26
|
& .crisp-settingsshell-back:hover svg { color: ${tokens_1.contract.fgPrimary}; }
|
|
@@ -40,10 +40,20 @@ const React = __importStar(require("react"));
|
|
|
40
40
|
const avatar_1 = require("../../components/avatar");
|
|
41
41
|
const badge_1 = require("../../components/badge");
|
|
42
42
|
const button_1 = require("../../components/button");
|
|
43
|
+
const menu_1 = require("../../components/menu");
|
|
43
44
|
const core_1 = require("../../core");
|
|
44
45
|
const command_1 = require("../command");
|
|
45
46
|
const navitem_1 = require("../navitem");
|
|
46
47
|
const sidebar_recipe_1 = require("./sidebar.recipe");
|
|
48
|
+
/** A plain `{ gettingStartedLabel, inviteLabel, trialText, trialActionLabel }`
|
|
49
|
+
* object (not a React element) — the shape `footer` renders as Attio's fixed
|
|
50
|
+
* onboarding/invite/trial copy, as opposed to a caller-supplied override. */
|
|
51
|
+
function isSidebarFooterCopy(value) {
|
|
52
|
+
return (typeof value === "object" &&
|
|
53
|
+
value !== null &&
|
|
54
|
+
!React.isValidElement(value) &&
|
|
55
|
+
"gettingStartedLabel" in value);
|
|
56
|
+
}
|
|
47
57
|
const IconHome = ({ className }) => ((0, jsx_runtime_1.jsx)(lucide_react_1.House, { className: className, size: 16, strokeWidth: 1.75, "aria-hidden": "true" }));
|
|
48
58
|
const IconTasks = ({ className }) => ((0, jsx_runtime_1.jsx)(lucide_react_1.SquareCheck, { className: className, size: 16, strokeWidth: 1.75, "aria-hidden": "true" }));
|
|
49
59
|
const IconNotes = ({ className }) => ((0, jsx_runtime_1.jsx)(lucide_react_1.FileText, { className: className, size: 16, strokeWidth: 1.75, "aria-hidden": "true" }));
|
|
@@ -117,7 +127,7 @@ exports.ATTIO_SIDEBAR_FOOTER = {
|
|
|
117
127
|
function SectionHeader({ label, open, onToggle, }) {
|
|
118
128
|
return ((0, jsx_runtime_1.jsxs)("button", { type: "button", className: "crisp-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
129
|
}
|
|
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, groups, footer = exports.ATTIO_SIDEBAR_FOOTER, commandItems, commandOpen: commandOpenProp, defaultCommandOpen, onCommandOpenChange, commandShortcut = true, "aria-label": ariaLabel = "Main", ...props }, ref) => {
|
|
130
|
+
exports.Sidebar = React.forwardRef(({ className, asChild, onToggleCollapse, collapseLabel = "Collapse sidebar", inboxLabel = "Inbox", searchLabel = "Search", workspace = exports.ATTIO_SIDEBAR_WORKSPACE, workspaceMenu, items = exports.ATTIO_SIDEBAR_ITEMS, recordsSection = exports.ATTIO_SIDEBAR_RECORDS_SECTION, listsSection = exports.ATTIO_SIDEBAR_LISTS_SECTION, chatsSection = exports.ATTIO_SIDEBAR_CHATS_SECTION, groups, footer = exports.ATTIO_SIDEBAR_FOOTER, commandItems, commandOpen: commandOpenProp, defaultCommandOpen, onCommandOpenChange, commandShortcut = true, "aria-label": ariaLabel = "Main", ...props }, ref) => {
|
|
121
131
|
// Seeded once, on mount, from whatever groups start collapsed — not
|
|
122
132
|
// reactive to `groups` changing later, matching every other piece of
|
|
123
133
|
// this component's state (recordsSection/listsSection/chatsSection have
|
|
@@ -163,13 +173,15 @@ exports.Sidebar = React.forwardRef(({ className, asChild, onToggleCollapse, coll
|
|
|
163
173
|
return next;
|
|
164
174
|
});
|
|
165
175
|
}, []);
|
|
166
|
-
|
|
176
|
+
const workspaceTrigger = ((0, jsx_runtime_1.jsxs)("button", { type: "button", className: "crisp-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: "crisp-sidebar-ws-name", children: workspace.name }), (0, jsx_runtime_1.jsx)(IconChevronDown, {})] }));
|
|
177
|
+
return ((0, jsx_runtime_1.jsxs)(core_1.Primitive.nav, { ref: ref, asChild: asChild, "aria-label": ariaLabel, className: (0, sidebar_recipe_1.sidebarRecipe)({ className }), ...props, children: [(0, jsx_runtime_1.jsxs)("div", { className: "crisp-sidebar-ws-row", children: [workspaceMenu ? ((0, jsx_runtime_1.jsx)(menu_1.Menu, { trigger: workspaceTrigger, className: "crisp-sidebar-ws-menuroot", contentClassName: "crisp-sidebar-ws-menu-content", children: workspaceMenu })) : (workspaceTrigger), (0, jsx_runtime_1.jsx)(button_1.Button, { intent: "ghost", size: "sm", shape: "icon", className: "crisp-sidebar-ws-collapse", "aria-label": collapseLabel, onClick: onToggleCollapse, children: (0, jsx_runtime_1.jsx)(IconCollapsePanel, {}) })] }), (0, jsx_runtime_1.jsxs)("div", { className: "crisp-sidebar-searchrow", children: [(0, jsx_runtime_1.jsxs)("button", { type: "button", className: "crisp-sidebar-search", onClick: () => setCommandOpen(true), children: [(0, jsx_runtime_1.jsx)(IconSearch, {}), (0, jsx_runtime_1.jsx)("span", { className: "crisp-sidebar-search-label", children: searchLabel }), (0, jsx_runtime_1.jsx)("span", { className: "crisp-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: "crisp-sidebar-divider" }), (0, jsx_runtime_1.jsx)("div", { className: "crisp-sidebar-section", children: items.map(({ label, icon: ItemIcon, active, trailing, href, onSelect }) => ((0, jsx_runtime_1.jsx)(navitem_1.NavItem, { href: href ?? "#", active: active, icon: (0, jsx_runtime_1.jsx)(ItemIcon, {}), trailing: trailing, onClick: onSelect, children: label }, href ?? label))) }), groups ? (
|
|
167
178
|
// #325: navigation as data, reusing the exact section markup/
|
|
168
179
|
// recipe above — SectionHeader + .crisp-sidebar-section — instead
|
|
169
180
|
// of the three fixed Attio sections.
|
|
170
181
|
groups.map((group) => {
|
|
171
182
|
const groupKey = group.key ?? group.label;
|
|
172
183
|
return ((0, jsx_runtime_1.jsxs)(React.Fragment, { children: [(0, jsx_runtime_1.jsx)(SectionHeader, { label: group.label, open: !collapsedSections.has(groupKey), onToggle: () => toggleSection(groupKey) }), (0, jsx_runtime_1.jsx)("div", { className: "crisp-sidebar-section", "data-collapsed": collapsedSections.has(groupKey) ? "" : undefined, children: group.items.map((item) => ((0, jsx_runtime_1.jsx)(navitem_1.NavItem, { href: item.href ?? "#", active: item.active, icon: item.icon ? (0, jsx_runtime_1.jsx)(item.icon, {}) : undefined, trailing: item.count, onClick: item.onSelect, children: item.label }, item.href ?? item.label))) })] }, groupKey));
|
|
173
|
-
})) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(SectionHeader, { label: recordsSection.label, open: !collapsedSections.has("records"), onToggle: () => toggleSection("records") }), (0, jsx_runtime_1.jsx)("div", { className: "crisp-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: "crisp-sidebar-section", "data-collapsed": collapsedSections.has("lists") ? "" : undefined, children: (0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-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: "crisp-sidebar-section", "data-collapsed": collapsedSections.has("chats") ? "" : undefined, children: chatsSection.items.map(({ label, icon: ItemIcon
|
|
184
|
+
})) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(SectionHeader, { label: recordsSection.label, open: !collapsedSections.has("records"), onToggle: () => toggleSection("records") }), (0, jsx_runtime_1.jsx)("div", { className: "crisp-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: "crisp-sidebar-section", "data-collapsed": collapsedSections.has("lists") ? "" : undefined, children: (0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-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: "crisp-sidebar-section", "data-collapsed": collapsedSections.has("chats") ? "" : undefined, children: chatsSection.items.map(({ label, icon: ItemIcon, active, trailing, href, onSelect, }) => ((0, jsx_runtime_1.jsx)(navitem_1.NavItem, { href: href ?? "#", active: active, icon: (0, jsx_runtime_1.jsx)(ItemIcon, {}), trailing: trailing, onClick: onSelect, children: label }, href ?? label))) })] })), footer !== false &&
|
|
185
|
+
(isSidebarFooterCopy(footer) ? ((0, jsx_runtime_1.jsxs)("div", { className: "crisp-sidebar-footer", children: [(0, jsx_runtime_1.jsx)(button_1.Button, { intent: "neutral", size: "sm", className: "crisp-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: "crisp-sidebar-divider" }), (0, jsx_runtime_1.jsxs)("div", { className: "crisp-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)("div", { className: "crisp-sidebar-footer", children: footer }))), (0, jsx_runtime_1.jsx)(command_1.Command, { open: commandOpen, onOpenChange: setCommandOpen, items: commandItems })] }));
|
|
174
186
|
});
|
|
175
187
|
exports.Sidebar.displayName = "Sidebar";
|
|
@@ -3,6 +3,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.sidebarRecipe = void 0;
|
|
4
4
|
const core_1 = require("../../core");
|
|
5
5
|
const tokens_1 = require("../../tokens");
|
|
6
|
+
// The workspace switcher's menu (#415) is 280px wide on app.attio.com --
|
|
7
|
+
// measured live via getComputedStyle on its [role="menu"], not derived from
|
|
8
|
+
// the trigger's own width (which reads 295-296px including its collapse
|
|
9
|
+
// button) or from Menu's generic 200px content-sized min-width. Everything
|
|
10
|
+
// else about the popover -- 4px padding, 12px radius, 32px/8px-radius items,
|
|
11
|
+
// 14px/500 labels, the layered shadow -- already matches Menu's own measured
|
|
12
|
+
// chrome, confirmed against this specific menu rather than assumed from it.
|
|
6
13
|
const config = (0, core_1.defineRecipe)({
|
|
7
14
|
name: "sidebar",
|
|
8
15
|
base: `box-sizing: border-box; display: flex; flex-direction: column; width: 275px; height: 100%; background: ${tokens_1.contract.bgSidebar}
|
|
@@ -15,10 +22,12 @@ const config = (0, core_1.defineRecipe)({
|
|
|
15
22
|
& .crisp-sidebar-section-header { display: flex; align-items: center; gap: ${tokens_1.contract.space["1"]}; width: 100%; margin: 0; border: none; background: none; padding: ${tokens_1.contract.space["1.5"]} ${tokens_1.contract.space["2"]} ${tokens_1.contract.space["0.5"]}; font-family: ${tokens_1.contract.font.sans}; font-size: ${tokens_1.contract.text.sm}; font-weight: ${tokens_1.contract.weight.medium}; color: ${tokens_1.contract.fgMuted}; cursor: pointer; }
|
|
16
23
|
& .crisp-sidebar-section-header svg { flex-shrink: 0; transition: transform .15s ease; }
|
|
17
24
|
& .crisp-sidebar-section-header[data-collapsed] svg { transform: rotate(-90deg); }
|
|
18
|
-
& .crisp-sidebar-ws { display: flex; align-items: center; gap: ${tokens_1.contract.space["2"]}; height: ${tokens_1.contract.size.header}; padding: 0 ${tokens_1.contract.space["2"]};
|
|
25
|
+
& .crisp-sidebar-ws-row { display: flex; align-items: center; gap: ${tokens_1.contract.space["2"]}; height: ${tokens_1.contract.size.header}; padding: 0 ${tokens_1.contract.space["2"]}; }
|
|
26
|
+
& .crisp-sidebar-ws-menuroot { display: flex; flex: 1; min-width: 0; }
|
|
27
|
+
& .crisp-sidebar-ws { box-sizing: border-box; display: flex; align-items: center; gap: ${tokens_1.contract.space["2"]}; flex: 1; min-width: 0; height: 100%; margin: 0; padding: 0; border: none; background: none; border-radius: ${tokens_1.contract.radius.lg}; font-family: inherit; font-size: inherit; color: inherit; text-align: start; cursor: pointer; transition: background-color 0.16s; }
|
|
19
28
|
& .crisp-sidebar-ws:hover { background: color-mix(in srgb, ${tokens_1.contract.fgPrimary} 5%, transparent); }
|
|
20
29
|
& .crisp-sidebar-ws-name { font-size: ${tokens_1.contract.text.lg}; font-weight: ${tokens_1.contract.weight.semibold}; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
21
|
-
& .crisp-sidebar-ws-collapse {
|
|
30
|
+
& .crisp-sidebar-ws-collapse { flex-shrink: 0; }
|
|
22
31
|
& .crisp-sidebar-searchrow { display: flex; align-items: center; gap: ${tokens_1.contract.space["1.5"]}; margin-top: ${tokens_1.contract.space["2.5"]}; padding-inline-end: ${tokens_1.contract.space["2"]}; }
|
|
23
32
|
& .crisp-sidebar-footer { margin-top: auto; display: flex; flex-direction: column; gap: ${tokens_1.contract.space["0.5"]}; }
|
|
24
33
|
& .crisp-sidebar-trial { display: flex; align-items: center; justify-content: space-between; gap: ${tokens_1.contract.space["2"]}; padding: ${tokens_1.contract.space["1.5"]} ${tokens_1.contract.space["2"]}; font-size: ${tokens_1.contract.text.sm}; color: ${tokens_1.contract.fgMuted}; }
|
|
@@ -32,6 +41,16 @@ const config = (0, core_1.defineRecipe)({
|
|
|
32
41
|
& .crisp-sidebar-newlist:hover { background: ${tokens_1.contract.bgRaised}; color: ${tokens_1.contract.fgPrimary}; }
|
|
33
42
|
|
|
34
43
|
${core_1.rtlFlipCss}
|
|
44
|
+
`,
|
|
45
|
+
// Menu's own floating content is portaled to document.body (#357), so a
|
|
46
|
+
// `&`-nested rule here -- which compiles to a real descendant combinator
|
|
47
|
+
// scoped to .crisp-sidebar -- matches nothing once the popover leaves the
|
|
48
|
+
// sidebar's DOM subtree. Same fix Menu's own recipe already uses: the
|
|
49
|
+
// content's override lives in globalCss instead, keyed off the
|
|
50
|
+
// \`contentClassName\` Menu now hands the workspace switcher's portaled
|
|
51
|
+
// surface (#415).
|
|
52
|
+
globalCss: `
|
|
53
|
+
.crisp-sidebar-ws-menu-content { width: 280px; }
|
|
35
54
|
`,
|
|
36
55
|
variants: {},
|
|
37
56
|
defaultVariants: {},
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
.crisp-composewindow {
|
|
2
|
+
display: contents; font-family: var(--crisp-font-sans);
|
|
3
|
+
& .crisp-composewindow-backdrop { position: fixed; inset: 0; z-index: 60; background: transparent; pointer-events: none; }
|
|
4
|
+
& .crisp-composewindow-positioner { position: fixed; inset: 0; z-index: 61; display: flex; align-items: flex-start; justify-content: center; padding: 15vh var(--crisp-space-4) var(--crisp-space-4); pointer-events: none; }
|
|
5
|
+
& .crisp-composewindow-content { pointer-events: auto; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; position: relative; display: flex; flex-direction: column; box-sizing: border-box; width: 90vw; max-height: 74vh; padding: var(--crisp-space-1); border-radius: var(--crisp-radius-2xl); background: color-mix(in srgb, var(--crisp-bg-overlay-frost) 60%, transparent); backdrop-filter: blur(15px); -webkit-backdrop-filter: blur(15px); box-shadow: 0 8px 28px -6px var(--crisp-shadow-overlay1), 0 18px 88px -4px var(--crisp-shadow-overlay2), 0 0 1px var(--crisp-shadow-overlay3); opacity: 1; transform: scale(1); transition: opacity 0.2s ease, transform 0.2s ease; @starting-style { opacity: 0; transform: scale(0.97); } }
|
|
6
|
+
& .crisp-composewindow-content[data-minimized] { visibility: hidden; opacity: 0; }
|
|
7
|
+
& .crisp-composewindow-card { display: flex; flex-direction: column; flex: 1; min-height: 0; box-sizing: border-box; border-radius: var(--crisp-radius-xl); background: var(--crisp-bg-surface); box-shadow: 0 0 0 1px color-mix(in srgb, var(--crisp-fg-primary) 8%, transparent); overflow: hidden; }
|
|
8
|
+
& .crisp-composewindow-header { display: flex; align-items: center; justify-content: space-between; gap: var(--crisp-space-2); flex-shrink: 0; height: 52px; padding: 0 var(--crisp-space-2) 0 var(--crisp-space-4); border-bottom: 1px solid var(--crisp-border-subtle); }
|
|
9
|
+
& .crisp-composewindow-breadcrumb { display: flex; align-items: center; gap: var(--crisp-space-1); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--crisp-text-md); color: var(--crisp-fg-muted); }
|
|
10
|
+
& .crisp-composewindow-actions { display: flex; align-items: center; gap: var(--crisp-space-0-5); flex-shrink: 0; }
|
|
11
|
+
& .crisp-composewindow-action-btn { display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; width: 28px; height: 28px; margin: 0; padding: 0; border: none; border-radius: var(--crisp-radius-md); background: none; color: var(--crisp-fg-muted); font-family: inherit; cursor: pointer; }
|
|
12
|
+
& .crisp-composewindow-action-btn:hover { background: color-mix(in srgb, var(--crisp-fg-primary) 6%, transparent); color: var(--crisp-fg-primary); }
|
|
13
|
+
& .crisp-composewindow-action-btn[data-state="on"] { background: color-mix(in srgb, var(--crisp-bg-brand-solid) 14%, transparent); color: var(--crisp-bg-brand-solid); }
|
|
14
|
+
& .crisp-composewindow-body { flex: 1; min-height: 0; overflow-y: auto; display: flex; flex-direction: column; padding: var(--crisp-space-5) var(--crisp-space-4); }
|
|
15
|
+
& .crisp-composewindow-content-body { display: flex; flex-direction: column; gap: var(--crisp-space-4); }
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
@media (prefers-reduced-motion: reduce) {
|
|
19
|
+
.crisp-composewindow, .crisp-composewindow * {
|
|
20
|
+
animation-duration: 0.01ms !important;
|
|
21
|
+
animation-iteration-count: 1 !important;
|
|
22
|
+
transition-duration: 0.01ms !important;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
@layer crisp-components {
|
|
2
|
+
.crisp-composewindow {
|
|
3
|
+
display: contents; font-family: var(--crisp-font-sans);
|
|
4
|
+
& .crisp-composewindow-backdrop { position: fixed; inset: 0; z-index: 60; background: transparent; pointer-events: none; }
|
|
5
|
+
& .crisp-composewindow-positioner { position: fixed; inset: 0; z-index: 61; display: flex; align-items: flex-start; justify-content: center; padding: 15vh var(--crisp-space-4) var(--crisp-space-4); pointer-events: none; }
|
|
6
|
+
& .crisp-composewindow-content { pointer-events: auto; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; position: relative; display: flex; flex-direction: column; box-sizing: border-box; width: 90vw; max-height: 74vh; padding: var(--crisp-space-1); border-radius: var(--crisp-radius-2xl); background: color-mix(in srgb, var(--crisp-bg-overlay-frost) 60%, transparent); backdrop-filter: blur(15px); -webkit-backdrop-filter: blur(15px); box-shadow: 0 8px 28px -6px var(--crisp-shadow-overlay1), 0 18px 88px -4px var(--crisp-shadow-overlay2), 0 0 1px var(--crisp-shadow-overlay3); opacity: 1; transform: scale(1); transition: opacity 0.2s ease, transform 0.2s ease; @starting-style { opacity: 0; transform: scale(0.97); } }
|
|
7
|
+
& .crisp-composewindow-content[data-minimized] { visibility: hidden; opacity: 0; }
|
|
8
|
+
& .crisp-composewindow-card { display: flex; flex-direction: column; flex: 1; min-height: 0; box-sizing: border-box; border-radius: var(--crisp-radius-xl); background: var(--crisp-bg-surface); box-shadow: 0 0 0 1px color-mix(in srgb, var(--crisp-fg-primary) 8%, transparent); overflow: hidden; }
|
|
9
|
+
& .crisp-composewindow-header { display: flex; align-items: center; justify-content: space-between; gap: var(--crisp-space-2); flex-shrink: 0; height: 52px; padding: 0 var(--crisp-space-2) 0 var(--crisp-space-4); border-bottom: 1px solid var(--crisp-border-subtle); }
|
|
10
|
+
& .crisp-composewindow-breadcrumb { display: flex; align-items: center; gap: var(--crisp-space-1); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--crisp-text-md); color: var(--crisp-fg-muted); }
|
|
11
|
+
& .crisp-composewindow-actions { display: flex; align-items: center; gap: var(--crisp-space-0-5); flex-shrink: 0; }
|
|
12
|
+
& .crisp-composewindow-action-btn { display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; width: 28px; height: 28px; margin: 0; padding: 0; border: none; border-radius: var(--crisp-radius-md); background: none; color: var(--crisp-fg-muted); font-family: inherit; cursor: pointer; }
|
|
13
|
+
& .crisp-composewindow-action-btn:hover { background: color-mix(in srgb, var(--crisp-fg-primary) 6%, transparent); color: var(--crisp-fg-primary); }
|
|
14
|
+
& .crisp-composewindow-action-btn[data-state="on"] { background: color-mix(in srgb, var(--crisp-bg-brand-solid) 14%, transparent); color: var(--crisp-bg-brand-solid); }
|
|
15
|
+
& .crisp-composewindow-body { flex: 1; min-height: 0; overflow-y: auto; display: flex; flex-direction: column; padding: var(--crisp-space-5) var(--crisp-space-4); }
|
|
16
|
+
& .crisp-composewindow-content-body { display: flex; flex-direction: column; gap: var(--crisp-space-4); }
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
@media (prefers-reduced-motion: reduce) {
|
|
20
|
+
.crisp-composewindow, .crisp-composewindow * {
|
|
21
|
+
animation-duration: 0.01ms !important;
|
|
22
|
+
animation-iteration-count: 1 !important;
|
|
23
|
+
transition-duration: 0.01ms !important;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
package/dist/datatable.css
CHANGED
|
@@ -32,10 +32,13 @@
|
|
|
32
32
|
& .crisp-datatable-th-rename:focus { box-shadow: inset 0 0 0 1px var(--crisp-bg-brand-solid); }
|
|
33
33
|
& .crisp-datatable-th-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
|
|
34
34
|
& .crisp-datatable-sort { display: flex; align-items: center; gap: var(--crisp-space-1-5); width: 100%; min-width: 0; height: 100%; margin: 0; padding: 0 var(--crisp-space-2); border: none; background: none; font: inherit; letter-spacing: inherit; color: inherit; text-align: start; cursor: pointer; }
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
/* A right-aligned column's header keeps its field-icon + label packed at
|
|
36
|
+
the START, matching its own values only in that the VALUES right-align
|
|
37
|
+
-- Attio's own right-aligned header (measured live: "Twitter follower
|
|
38
|
+
count") keeps the icon+label left and only the trailing column-menu icon sits pinned to the right edge, independent of the label's width
|
|
39
|
+
(#406, reversing #393's own justify-content: flex-end here). The chevron's own margin-inline-start: auto below already pins IT to the
|
|
40
|
+
end of a flex-start row with no override needed -- that's what pins the trailing icon on Attio too. */
|
|
37
41
|
& .crisp-datatable-chevron { margin-inline-start: auto; flex-shrink: 0; color: var(--crisp-fg-muted); opacity: 0; transition: opacity .12s; }
|
|
38
|
-
& th[data-align="right"] .crisp-datatable-chevron { margin-inline-start: 0; order: -1; }
|
|
39
42
|
& .crisp-datatable-sort:hover .crisp-datatable-chevron { opacity: 0.6; }
|
|
40
43
|
& .crisp-datatable-sort[data-sorted] .crisp-datatable-chevron { opacity: 1; color: var(--crisp-fg-primary); }
|
|
41
44
|
/* sticky first column(s) + horizontal scroll shadow — RecordsTable's feel */
|
|
@@ -58,6 +61,28 @@
|
|
|
58
61
|
& .crisp-datatable-editable:hover { background: color-mix(in srgb, var(--crisp-fg-primary) 4%, var(--crisp-bg-surface)); cursor: text; }
|
|
59
62
|
& .crisp-datatable-edit { width: 100%; height: 28px; margin: 0; padding: 0 var(--crisp-space-1-5); border: none; border-radius: var(--crisp-radius-sm); background: var(--crisp-bg-surface); box-shadow: inset 0 0 0 1px var(--crisp-bg-brand-solid); font-family: inherit; font-size: var(--crisp-text-lg); color: var(--crisp-fg-primary); outline: none; vertical-align: middle; }
|
|
60
63
|
& .crisp-datatable-edit:focus { box-shadow: inset 0 0 0 1px var(--crisp-bg-brand-solid); }
|
|
64
|
+
/* An async onEdit's in-flight state (#385) -- the field/picker dims and
|
|
65
|
+
stops accepting input rather than disappearing, so the just-typed or
|
|
66
|
+
just-chosen value stays visible while its commit is pending. Same
|
|
67
|
+
treatment Input already gives its own disabled state. */
|
|
68
|
+
& .crisp-datatable-edit:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
69
|
+
& .crisp-datatable-edit-pending { flex-shrink: 0; margin-inline-start: var(--crisp-space-1); color: var(--crisp-fg-muted); vertical-align: middle; }
|
|
70
|
+
/* The rejection's message, beside the cell's own content -- a sibling in
|
|
71
|
+
the TableCell, never nested inside a column's own render output, so a hover-only wrapper (a Tooltip, say) around a custom render can't hide
|
|
72
|
+
it. Block-level: the row grows to fit it rather than floating over
|
|
73
|
+
the row below, so it can never end up clipped by the scroll
|
|
74
|
+
container's overflow the way a position:fixed escape would need guarding against. */
|
|
75
|
+
& .crisp-datatable-edit-error { display: block; padding: var(--crisp-space-0-5) 0 0; font-size: var(--crisp-text-sm); color: var(--crisp-bg-danger); }
|
|
76
|
+
/* editor: "select"/"switch" -- always live, no separate edit mode, so the cell IS the picker: same chromeless-fill treatment as RecordsTable's own --picker cells (measured there already; DataTable
|
|
77
|
+
shares RecordsTable's grid, Golden Rule 0). */
|
|
78
|
+
& .crisp-datatable-td--picker { padding: 0; }
|
|
79
|
+
& .crisp-datatable-edit-picker { display: flex; align-items: center; gap: var(--crisp-space-1); height: var(--crisp-size-tableRow); padding: 0 var(--crisp-space-2); }
|
|
80
|
+
& .crisp-datatable-edit-picker .crisp-select-trigger { flex: 1; min-width: 0; height: 100%; min-height: 0; border-radius: 0; box-shadow: none; padding: 0; }
|
|
81
|
+
& .crisp-datatable-td--picker .crisp-datatable-edit-error { padding-inline-start: var(--crisp-space-2); }
|
|
82
|
+
/* reference picker: a leading 16px avatar per option, plus a trailing "Create X" row when the consumer supplies one (#449) -- same measured values as RecordsTable's own .crisp-recordstable-refoption*. */
|
|
83
|
+
& .crisp-datatable-refoption { display: flex; align-items: center; gap: var(--crisp-space-2); min-width: 0; overflow: hidden; text-overflow: ellipsis; }
|
|
84
|
+
& .crisp-datatable-refoption-avatar { flex-shrink: 0; }
|
|
85
|
+
& .crisp-datatable-refoption--create { color: var(--crisp-fg-muted); }
|
|
61
86
|
& .crisp-datatable-empty { padding: var(--crisp-space-8) var(--crisp-space-3); text-align: center; color: var(--crisp-fg-muted); font-size: var(--crisp-text-lg); }
|
|
62
87
|
/* fixed layout so column widths (and resize) are exact — like RecordsTable */
|
|
63
88
|
& table { table-layout: fixed; }
|
|
@@ -69,12 +94,7 @@
|
|
|
69
94
|
& .crisp-datatable-entity { display: flex; align-items: center; gap: var(--crisp-space-1-5); min-width: 0; color: var(--crisp-fg-primary); font-weight: var(--crisp-weight-medium); }
|
|
70
95
|
& .crisp-datatable-entity-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
71
96
|
& .crisp-datatable-avatar { width: var(--crisp-size-icon); height: var(--crisp-size-icon); border-radius: var(--crisp-radius-sm); flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center; font-size: 10px; line-height: 1; font-weight: var(--crisp-weight-semibold); text-transform: uppercase; color: var(--crisp-fg-on-brand); background: var(--crisp-bg-brand-solid); }
|
|
72
|
-
& .crisp-datatable-avatar[data-tone="success"] { background: var(--crisp-bg-success); }
|
|
73
|
-
& .crisp-datatable-avatar[data-tone="warning"] { background: var(--crisp-bg-warning); }
|
|
74
|
-
& .crisp-datatable-avatar[data-tone="danger"] { background: var(--crisp-bg-danger); }
|
|
75
|
-
& .crisp-datatable-avatar[data-tone="purple"] { background: var(--crisp-bg-purple); }
|
|
76
|
-
& .crisp-datatable-avatar[data-tone="cyan"] { background: var(--crisp-bg-cyan); }
|
|
77
|
-
& .crisp-datatable-avatar[data-tone="orange"] { background: var(--crisp-bg-orange); }
|
|
97
|
+
& .crisp-datatable-avatar[data-tone="success"] { background: var(--crisp-bg-success); } & .crisp-datatable-avatar[data-tone="warning"] { background: var(--crisp-bg-warning); } & .crisp-datatable-avatar[data-tone="danger"] { background: var(--crisp-bg-danger); } & .crisp-datatable-avatar[data-tone="purple"] { background: var(--crisp-bg-purple); } & .crisp-datatable-avatar[data-tone="cyan"] { background: var(--crisp-bg-cyan); } & .crisp-datatable-avatar[data-tone="orange"] { background: var(--crisp-bg-orange); }
|
|
78
98
|
& .crisp-datatable-avatar img { width: 100%; height: 100%; border-radius: inherit; object-fit: cover; }
|
|
79
99
|
& .crisp-datatable-tags { display: flex; align-items: center; gap: var(--crisp-space-1); overflow: hidden; }
|
|
80
100
|
& .crisp-datatable-tag { display: inline-flex; align-items: center; height: var(--crisp-size-chip); padding: 0 var(--crisp-space-1); border-radius: var(--crisp-radius-chip); font-size: var(--crisp-text-lg); line-height: 20px; font-weight: var(--crisp-weight-medium); white-space: nowrap; background: color-mix(in srgb, var(--crisp-fg-primary) 6%, transparent); color: color-mix(in srgb, var(--crisp-fg-primary) 82%, transparent); }
|
|
@@ -87,11 +107,10 @@
|
|
|
87
107
|
& .crisp-datatable-tag[data-tone="orange"] { background: color-mix(in srgb, var(--crisp-bg-orange) 16%, transparent); color: color-mix(in srgb, var(--crisp-bg-orange) 82%, var(--crisp-fg-primary)); }
|
|
88
108
|
& .crisp-datatable-num { font-variant-numeric: tabular-nums; }
|
|
89
109
|
& .crisp-datatable-muted { color: var(--crisp-fg-muted); }
|
|
90
|
-
/* row drag-to-reorder -- a Settings > Objects > Attributes screen measures
|
|
91
|
-
hairline doubling as the drop indicator (it tints toward the brand
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
pointer-events implementation. */
|
|
110
|
+
/* row drag-to-reorder -- a Settings > Objects > Attributes screen measures
|
|
111
|
+
a leading 20px grip column, a 16px grip icon, and the row's own hairline doubling as the drop indicator (it tints toward the brand colour as the active drop target). The affordance + geometry are measured; the drag MECHANISM is crisp's existing house idiom -- HTML5
|
|
112
|
+
draggable + dataTransfer, the same one column reorder already uses
|
|
113
|
+
(RecordsTable's reorderColumn) -- not a proprietary pointer-events implementation. */
|
|
95
114
|
& .crisp-datatable-col-grip { width: 20px; padding: 0; text-align: center; }
|
|
96
115
|
& .crisp-datatable-grip { display: inline-flex; align-items: center; justify-content: center; width: 100%; height: 100%; margin: 0; border: none; border-radius: 0; background: none; font-family: inherit; color: var(--crisp-fg-muted); cursor: grab; }
|
|
97
116
|
& .crisp-datatable-grip:active { cursor: grabbing; }
|
|
@@ -33,10 +33,13 @@
|
|
|
33
33
|
& .crisp-datatable-th-rename:focus { box-shadow: inset 0 0 0 1px var(--crisp-bg-brand-solid); }
|
|
34
34
|
& .crisp-datatable-th-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
|
|
35
35
|
& .crisp-datatable-sort { display: flex; align-items: center; gap: var(--crisp-space-1-5); width: 100%; min-width: 0; height: 100%; margin: 0; padding: 0 var(--crisp-space-2); border: none; background: none; font: inherit; letter-spacing: inherit; color: inherit; text-align: start; cursor: pointer; }
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
/* A right-aligned column's header keeps its field-icon + label packed at
|
|
37
|
+
the START, matching its own values only in that the VALUES right-align
|
|
38
|
+
-- Attio's own right-aligned header (measured live: "Twitter follower
|
|
39
|
+
count") keeps the icon+label left and only the trailing column-menu icon sits pinned to the right edge, independent of the label's width
|
|
40
|
+
(#406, reversing #393's own justify-content: flex-end here). The chevron's own margin-inline-start: auto below already pins IT to the
|
|
41
|
+
end of a flex-start row with no override needed -- that's what pins the trailing icon on Attio too. */
|
|
38
42
|
& .crisp-datatable-chevron { margin-inline-start: auto; flex-shrink: 0; color: var(--crisp-fg-muted); opacity: 0; transition: opacity .12s; }
|
|
39
|
-
& th[data-align="right"] .crisp-datatable-chevron { margin-inline-start: 0; order: -1; }
|
|
40
43
|
& .crisp-datatable-sort:hover .crisp-datatable-chevron { opacity: 0.6; }
|
|
41
44
|
& .crisp-datatable-sort[data-sorted] .crisp-datatable-chevron { opacity: 1; color: var(--crisp-fg-primary); }
|
|
42
45
|
/* sticky first column(s) + horizontal scroll shadow — RecordsTable's feel */
|
|
@@ -59,6 +62,28 @@
|
|
|
59
62
|
& .crisp-datatable-editable:hover { background: color-mix(in srgb, var(--crisp-fg-primary) 4%, var(--crisp-bg-surface)); cursor: text; }
|
|
60
63
|
& .crisp-datatable-edit { width: 100%; height: 28px; margin: 0; padding: 0 var(--crisp-space-1-5); border: none; border-radius: var(--crisp-radius-sm); background: var(--crisp-bg-surface); box-shadow: inset 0 0 0 1px var(--crisp-bg-brand-solid); font-family: inherit; font-size: var(--crisp-text-lg); color: var(--crisp-fg-primary); outline: none; vertical-align: middle; }
|
|
61
64
|
& .crisp-datatable-edit:focus { box-shadow: inset 0 0 0 1px var(--crisp-bg-brand-solid); }
|
|
65
|
+
/* An async onEdit's in-flight state (#385) -- the field/picker dims and
|
|
66
|
+
stops accepting input rather than disappearing, so the just-typed or
|
|
67
|
+
just-chosen value stays visible while its commit is pending. Same
|
|
68
|
+
treatment Input already gives its own disabled state. */
|
|
69
|
+
& .crisp-datatable-edit:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
70
|
+
& .crisp-datatable-edit-pending { flex-shrink: 0; margin-inline-start: var(--crisp-space-1); color: var(--crisp-fg-muted); vertical-align: middle; }
|
|
71
|
+
/* The rejection's message, beside the cell's own content -- a sibling in
|
|
72
|
+
the TableCell, never nested inside a column's own render output, so a hover-only wrapper (a Tooltip, say) around a custom render can't hide
|
|
73
|
+
it. Block-level: the row grows to fit it rather than floating over
|
|
74
|
+
the row below, so it can never end up clipped by the scroll
|
|
75
|
+
container's overflow the way a position:fixed escape would need guarding against. */
|
|
76
|
+
& .crisp-datatable-edit-error { display: block; padding: var(--crisp-space-0-5) 0 0; font-size: var(--crisp-text-sm); color: var(--crisp-bg-danger); }
|
|
77
|
+
/* editor: "select"/"switch" -- always live, no separate edit mode, so the cell IS the picker: same chromeless-fill treatment as RecordsTable's own --picker cells (measured there already; DataTable
|
|
78
|
+
shares RecordsTable's grid, Golden Rule 0). */
|
|
79
|
+
& .crisp-datatable-td--picker { padding: 0; }
|
|
80
|
+
& .crisp-datatable-edit-picker { display: flex; align-items: center; gap: var(--crisp-space-1); height: var(--crisp-size-tableRow); padding: 0 var(--crisp-space-2); }
|
|
81
|
+
& .crisp-datatable-edit-picker .crisp-select-trigger { flex: 1; min-width: 0; height: 100%; min-height: 0; border-radius: 0; box-shadow: none; padding: 0; }
|
|
82
|
+
& .crisp-datatable-td--picker .crisp-datatable-edit-error { padding-inline-start: var(--crisp-space-2); }
|
|
83
|
+
/* reference picker: a leading 16px avatar per option, plus a trailing "Create X" row when the consumer supplies one (#449) -- same measured values as RecordsTable's own .crisp-recordstable-refoption*. */
|
|
84
|
+
& .crisp-datatable-refoption { display: flex; align-items: center; gap: var(--crisp-space-2); min-width: 0; overflow: hidden; text-overflow: ellipsis; }
|
|
85
|
+
& .crisp-datatable-refoption-avatar { flex-shrink: 0; }
|
|
86
|
+
& .crisp-datatable-refoption--create { color: var(--crisp-fg-muted); }
|
|
62
87
|
& .crisp-datatable-empty { padding: var(--crisp-space-8) var(--crisp-space-3); text-align: center; color: var(--crisp-fg-muted); font-size: var(--crisp-text-lg); }
|
|
63
88
|
/* fixed layout so column widths (and resize) are exact — like RecordsTable */
|
|
64
89
|
& table { table-layout: fixed; }
|
|
@@ -70,12 +95,7 @@
|
|
|
70
95
|
& .crisp-datatable-entity { display: flex; align-items: center; gap: var(--crisp-space-1-5); min-width: 0; color: var(--crisp-fg-primary); font-weight: var(--crisp-weight-medium); }
|
|
71
96
|
& .crisp-datatable-entity-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
72
97
|
& .crisp-datatable-avatar { width: var(--crisp-size-icon); height: var(--crisp-size-icon); border-radius: var(--crisp-radius-sm); flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center; font-size: 10px; line-height: 1; font-weight: var(--crisp-weight-semibold); text-transform: uppercase; color: var(--crisp-fg-on-brand); background: var(--crisp-bg-brand-solid); }
|
|
73
|
-
& .crisp-datatable-avatar[data-tone="success"] { background: var(--crisp-bg-success); }
|
|
74
|
-
& .crisp-datatable-avatar[data-tone="warning"] { background: var(--crisp-bg-warning); }
|
|
75
|
-
& .crisp-datatable-avatar[data-tone="danger"] { background: var(--crisp-bg-danger); }
|
|
76
|
-
& .crisp-datatable-avatar[data-tone="purple"] { background: var(--crisp-bg-purple); }
|
|
77
|
-
& .crisp-datatable-avatar[data-tone="cyan"] { background: var(--crisp-bg-cyan); }
|
|
78
|
-
& .crisp-datatable-avatar[data-tone="orange"] { background: var(--crisp-bg-orange); }
|
|
98
|
+
& .crisp-datatable-avatar[data-tone="success"] { background: var(--crisp-bg-success); } & .crisp-datatable-avatar[data-tone="warning"] { background: var(--crisp-bg-warning); } & .crisp-datatable-avatar[data-tone="danger"] { background: var(--crisp-bg-danger); } & .crisp-datatable-avatar[data-tone="purple"] { background: var(--crisp-bg-purple); } & .crisp-datatable-avatar[data-tone="cyan"] { background: var(--crisp-bg-cyan); } & .crisp-datatable-avatar[data-tone="orange"] { background: var(--crisp-bg-orange); }
|
|
79
99
|
& .crisp-datatable-avatar img { width: 100%; height: 100%; border-radius: inherit; object-fit: cover; }
|
|
80
100
|
& .crisp-datatable-tags { display: flex; align-items: center; gap: var(--crisp-space-1); overflow: hidden; }
|
|
81
101
|
& .crisp-datatable-tag { display: inline-flex; align-items: center; height: var(--crisp-size-chip); padding: 0 var(--crisp-space-1); border-radius: var(--crisp-radius-chip); font-size: var(--crisp-text-lg); line-height: 20px; font-weight: var(--crisp-weight-medium); white-space: nowrap; background: color-mix(in srgb, var(--crisp-fg-primary) 6%, transparent); color: color-mix(in srgb, var(--crisp-fg-primary) 82%, transparent); }
|
|
@@ -88,11 +108,10 @@
|
|
|
88
108
|
& .crisp-datatable-tag[data-tone="orange"] { background: color-mix(in srgb, var(--crisp-bg-orange) 16%, transparent); color: color-mix(in srgb, var(--crisp-bg-orange) 82%, var(--crisp-fg-primary)); }
|
|
89
109
|
& .crisp-datatable-num { font-variant-numeric: tabular-nums; }
|
|
90
110
|
& .crisp-datatable-muted { color: var(--crisp-fg-muted); }
|
|
91
|
-
/* row drag-to-reorder -- a Settings > Objects > Attributes screen measures
|
|
92
|
-
hairline doubling as the drop indicator (it tints toward the brand
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
pointer-events implementation. */
|
|
111
|
+
/* row drag-to-reorder -- a Settings > Objects > Attributes screen measures
|
|
112
|
+
a leading 20px grip column, a 16px grip icon, and the row's own hairline doubling as the drop indicator (it tints toward the brand colour as the active drop target). The affordance + geometry are measured; the drag MECHANISM is crisp's existing house idiom -- HTML5
|
|
113
|
+
draggable + dataTransfer, the same one column reorder already uses
|
|
114
|
+
(RecordsTable's reorderColumn) -- not a proprietary pointer-events implementation. */
|
|
96
115
|
& .crisp-datatable-col-grip { width: 20px; padding: 0; text-align: center; }
|
|
97
116
|
& .crisp-datatable-grip { display: inline-flex; align-items: center; justify-content: center; width: 100%; height: 100%; margin: 0; border: none; border-radius: 0; background: none; font-family: inherit; color: var(--crisp-fg-muted); cursor: grab; }
|
|
98
117
|
& .crisp-datatable-grip:active { cursor: grabbing; }
|
package/dist/datepickerfield.css
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
.crisp-datepickerfield {
|
|
2
2
|
display: inline-flex; font-family: var(--crisp-font-sans);
|
|
3
|
-
& .crisp-datepickerfield-
|
|
4
|
-
& .crisp-datepickerfield-
|
|
5
|
-
& .crisp-datepickerfield-
|
|
6
|
-
& .crisp-datepickerfield-trigger
|
|
3
|
+
& .crisp-datepickerfield-box { display: inline-flex; align-items: center; box-sizing: border-box; height: 32px; margin: 0; border-radius: var(--crisp-radius-md); background: transparent; box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--crisp-fg-primary) 7%, transparent); color: var(--crisp-fg-primary); font-family: inherit; font-size: var(--crisp-text-lg); }
|
|
4
|
+
& .crisp-datepickerfield-box[data-ghost] { background: transparent; box-shadow: none; }
|
|
5
|
+
& .crisp-datepickerfield-box[data-ghost]:hover { background: color-mix(in srgb, var(--crisp-fg-primary) 4%, transparent); }
|
|
6
|
+
& .crisp-datepickerfield-trigger { display: inline-flex; align-items: center; gap: var(--crisp-space-2); height: 100%; padding: 0 var(--crisp-space-2-5); margin: 0; border: none; background: none; color: inherit; font-family: inherit; font-size: inherit; white-space: nowrap; cursor: pointer; }
|
|
7
|
+
& .crisp-datepickerfield-box[data-placeholder] .crisp-datepickerfield-trigger { color: var(--crisp-fg-muted); }
|
|
7
8
|
& .crisp-datepickerfield-trigger svg { flex-shrink: 0; color: var(--crisp-fg-muted); }
|
|
9
|
+
/* Reuses TableToolbar's measured filter-chip remove button
|
|
10
|
+
(tabletoolbar.chrome.ts's .crisp-tabletoolbar-fchip-x) -- the same small inline "clear this value" affordance, not a freshly-measured one: no live Attio composition for a DatePickerField-equivalent clear was reachable this round (#394). */
|
|
11
|
+
& .crisp-datepickerfield-clear { display: inline-flex; align-items: center; justify-content: center; width: 20px; height: 20px; margin-inline-end: var(--crisp-space-1); padding: 0; border: none; border-radius: var(--crisp-radius-sm); background: none; color: var(--crisp-fg-muted); font-family: inherit; cursor: pointer; }
|
|
12
|
+
& .crisp-datepickerfield-clear:hover { background: color-mix(in srgb, var(--crisp-fg-primary) 10%, transparent); color: var(--crisp-fg-primary); }
|
|
8
13
|
& .crisp-datepickerfield-panel { padding: var(--crisp-space-2); width: 300px; }
|
|
9
14
|
& .crisp-datepickerfield-presets { display: flex; flex-wrap: wrap; gap: var(--crisp-space-1); margin-top: var(--crisp-space-2); }
|
|
10
15
|
& .crisp-datepickerfield-preset { height: var(--crisp-size-chip); padding: 0 var(--crisp-space-2); border: none; border-radius: var(--crisp-radius-chip); background: var(--crisp-bg-raised); color: var(--crisp-fg-primary); font-family: inherit; font-size: var(--crisp-text-sm); cursor: pointer; }
|