@crisp-ui-kit/crisp 0.57.1 → 0.57.2
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/authshell.css +9 -4
- package/dist/authshell.layered.css +9 -4
- package/dist/badge.css +3 -3
- package/dist/badge.layered.css +3 -3
- package/dist/board.css +28 -8
- package/dist/board.layered.css +28 -8
- package/dist/bottomsheet.css +26 -0
- package/dist/bottomsheet.d.ts +7 -0
- package/dist/bottomsheet.layered.css +28 -0
- package/dist/button.css +7 -6
- package/dist/button.layered.css +7 -6
- package/dist/checkbox.css +7 -3
- package/dist/checkbox.layered.css +7 -3
- package/dist/chip.css +15 -0
- package/dist/chip.d.ts +3 -1
- package/dist/chip.layered.css +15 -0
- package/dist/chipgroup.css +5 -0
- package/dist/chipgroup.d.ts +7 -0
- package/dist/chipgroup.layered.css +7 -0
- package/dist/cjs/components/badge/badge.recipe.js +22 -8
- package/dist/cjs/components/bottomsheet/bottomsheet.js +90 -0
- package/dist/cjs/components/bottomsheet/bottomsheet.recipe.js +77 -0
- package/dist/cjs/components/bottomsheet/index.js +9 -0
- package/dist/cjs/components/button/button.recipe.js +39 -15
- package/dist/cjs/components/checkbox/checkbox.recipe.js +61 -9
- package/dist/cjs/components/chip/chip.js +14 -2
- package/dist/cjs/components/chip/chip.recipe.js +69 -1
- package/dist/cjs/components/chipgroup/chipgroup.js +51 -0
- package/dist/cjs/components/chipgroup/chipgroup.recipe.js +36 -0
- package/dist/cjs/components/chipgroup/index.js +7 -0
- package/dist/cjs/components/datatable/datatable.js +5 -1
- package/dist/cjs/components/datatable/datatable.recipe.js +11 -2
- package/dist/cjs/components/dialog/dialog.recipe.js +21 -10
- package/dist/cjs/components/emptystate/emptystate.recipe.js +7 -2
- package/dist/cjs/components/fileupload/fileupload.js +21 -4
- package/dist/cjs/components/input/index.js +2 -1
- package/dist/cjs/components/input/input.js +31 -1
- package/dist/cjs/components/input/input.recipe.js +16 -9
- package/dist/cjs/components/menu/menu.recipe.js +21 -7
- package/dist/cjs/components/menusheet/index.js +9 -0
- package/dist/cjs/components/menusheet/menusheet.js +133 -0
- package/dist/cjs/components/menusheet/menusheet.recipe.js +103 -0
- package/dist/cjs/components/multiselect/multiselect.js +1 -1
- package/dist/cjs/components/multiselect/multiselect.recipe.js +33 -9
- package/dist/cjs/components/select/select.js +1 -1
- package/dist/cjs/components/select/select.recipe.js +38 -12
- package/dist/cjs/components/snackbar/index.js +8 -0
- package/dist/cjs/components/snackbar/snackbar.js +121 -0
- package/dist/cjs/components/snackbar/snackbar.recipe.js +71 -0
- package/dist/cjs/components/tabletoolbar/tabletoolbar.chrome.js +7 -2
- package/dist/cjs/components/tabletoolbar/tabletoolbar.js +37 -12
- package/dist/cjs/components/tabs/tabs.js +10 -4
- package/dist/cjs/components/tabs/tabs.recipe.js +23 -6
- package/dist/cjs/components/textarea/textarea.recipe.js +11 -8
- package/dist/cjs/index.js +7 -0
- package/dist/cjs/product/authshell/authshell.recipe.js +5 -1
- package/dist/cjs/product/command/command.recipe.js +4 -1
- package/dist/cjs/product/comments/comments.js +7 -2
- package/dist/cjs/product/comments/comments.recipe.js +25 -7
- package/dist/cjs/product/memberssettings/index.js +6 -0
- package/dist/cjs/product/memberssettings/memberssettings.js +99 -0
- package/dist/cjs/product/memberssettings/memberssettings.recipe.js +83 -0
- package/dist/cjs/product/notificationssettings/index.js +5 -0
- package/dist/cjs/product/notificationssettings/notificationssettings.js +19 -0
- package/dist/cjs/product/notificationssettings/notificationssettings.recipe.js +44 -0
- package/dist/cjs/product/recordpage/recordpage.js +57 -6
- package/dist/cjs/product/recordpage/recordpage.recipe.js +43 -6
- package/dist/cjs/product/workspacegeneralsettings/index.js +5 -0
- package/dist/cjs/product/workspacegeneralsettings/workspacegeneralsettings.js +21 -0
- package/dist/cjs/product/workspacegeneralsettings/workspacegeneralsettings.recipe.js +51 -0
- package/dist/cjs/tokens/contract.js +1 -0
- package/dist/cjs/tokens/elevation.js +8 -5
- package/dist/cjs/tokens/palette.js +28 -8
- package/dist/cjs/tokens/scale.js +44 -2
- package/dist/cjs/tokens/semantic.js +2 -0
- package/dist/columncalc.css +15 -7
- package/dist/columncalc.layered.css +15 -7
- package/dist/command.css +23 -19
- package/dist/command.layered.css +23 -19
- package/dist/comments.css +28 -19
- package/dist/comments.d.ts +2 -2
- package/dist/comments.layered.css +28 -19
- package/dist/datatable.css +161 -67
- package/dist/datatable.layered.css +161 -67
- package/dist/datepicker.css +13 -41
- package/dist/datepicker.layered.css +13 -41
- package/dist/datepickerfield.css +4 -1
- package/dist/datepickerfield.layered.css +4 -1
- package/dist/dialog.css +12 -7
- package/dist/dialog.layered.css +12 -7
- package/dist/emptystate.css +5 -3
- package/dist/emptystate.layered.css +5 -3
- package/dist/esm/components/badge/badge.recipe.js +22 -8
- package/dist/esm/components/bottomsheet/bottomsheet.js +52 -0
- package/dist/esm/components/bottomsheet/bottomsheet.recipe.js +74 -0
- package/dist/esm/components/bottomsheet/index.js +2 -0
- package/dist/esm/components/button/button.recipe.js +40 -16
- package/dist/esm/components/checkbox/checkbox.recipe.js +61 -9
- package/dist/esm/components/chip/chip.js +14 -2
- package/dist/esm/components/chip/chip.recipe.js +69 -1
- package/dist/esm/components/chipgroup/chipgroup.js +15 -0
- package/dist/esm/components/chipgroup/chipgroup.recipe.js +33 -0
- package/dist/esm/components/chipgroup/index.js +2 -0
- package/dist/esm/components/datatable/datatable.js +5 -1
- package/dist/esm/components/datatable/datatable.recipe.js +11 -2
- package/dist/esm/components/dialog/dialog.recipe.js +22 -11
- package/dist/esm/components/emptystate/emptystate.recipe.js +7 -2
- package/dist/esm/components/fileupload/fileupload.js +20 -5
- package/dist/esm/components/input/index.js +1 -1
- package/dist/esm/components/input/input.js +30 -0
- package/dist/esm/components/input/input.recipe.js +17 -10
- package/dist/esm/components/menu/menu.recipe.js +21 -7
- package/dist/esm/components/menusheet/index.js +2 -0
- package/dist/esm/components/menusheet/menusheet.js +95 -0
- package/dist/esm/components/menusheet/menusheet.recipe.js +100 -0
- package/dist/esm/components/multiselect/multiselect.js +1 -1
- package/dist/esm/components/multiselect/multiselect.recipe.js +33 -9
- package/dist/esm/components/select/select.js +1 -1
- package/dist/esm/components/select/select.recipe.js +38 -12
- package/dist/esm/components/snackbar/index.js +2 -0
- package/dist/esm/components/snackbar/snackbar.js +84 -0
- package/dist/esm/components/snackbar/snackbar.recipe.js +68 -0
- package/dist/esm/components/tabletoolbar/tabletoolbar.chrome.js +7 -2
- package/dist/esm/components/tabletoolbar/tabletoolbar.js +37 -12
- package/dist/esm/components/tabs/tabs.js +10 -4
- package/dist/esm/components/tabs/tabs.recipe.js +23 -6
- package/dist/esm/components/textarea/textarea.recipe.js +12 -9
- package/dist/esm/index.js +7 -0
- package/dist/esm/product/authshell/authshell.recipe.js +5 -1
- package/dist/esm/product/command/command.recipe.js +4 -1
- package/dist/esm/product/comments/comments.js +7 -2
- package/dist/esm/product/comments/comments.recipe.js +25 -7
- package/dist/esm/product/memberssettings/index.js +1 -0
- package/dist/esm/product/memberssettings/memberssettings.js +62 -0
- package/dist/esm/product/memberssettings/memberssettings.recipe.js +80 -0
- package/dist/esm/product/notificationssettings/index.js +1 -0
- package/dist/esm/product/notificationssettings/notificationssettings.js +16 -0
- package/dist/esm/product/notificationssettings/notificationssettings.recipe.js +41 -0
- package/dist/esm/product/recordpage/recordpage.js +58 -7
- package/dist/esm/product/recordpage/recordpage.recipe.js +43 -6
- package/dist/esm/product/workspacegeneralsettings/index.js +1 -0
- package/dist/esm/product/workspacegeneralsettings/workspacegeneralsettings.js +18 -0
- package/dist/esm/product/workspacegeneralsettings/workspacegeneralsettings.recipe.js +48 -0
- package/dist/esm/tokens/contract.js +2 -1
- package/dist/esm/tokens/elevation.js +8 -5
- package/dist/esm/tokens/palette.js +28 -8
- package/dist/esm/tokens/scale.js +43 -1
- package/dist/esm/tokens/semantic.js +2 -0
- package/dist/fileupload.css +3 -1
- package/dist/fileupload.layered.css +3 -1
- package/dist/input.css +6 -4
- package/dist/input.layered.css +6 -4
- package/dist/listpage.css +3 -1
- package/dist/listpage.layered.css +3 -1
- package/dist/memberssettings.css +67 -0
- package/dist/memberssettings.d.ts +7 -0
- package/dist/memberssettings.layered.css +69 -0
- package/dist/menu.css +2 -2
- package/dist/menu.layered.css +2 -2
- package/dist/menusheet.css +36 -0
- package/dist/menusheet.d.ts +7 -0
- package/dist/menusheet.layered.css +38 -0
- package/dist/multiselect.css +13 -6
- package/dist/multiselect.layered.css +13 -6
- package/dist/native/tokens.d.ts +2 -1
- package/dist/native/tokens.js +20 -3
- package/dist/notificationssettings.css +22 -0
- package/dist/notificationssettings.d.ts +7 -0
- package/dist/notificationssettings.layered.css +24 -0
- package/dist/objectsettings.css +4 -4
- package/dist/objectsettings.layered.css +4 -4
- package/dist/recordpage.css +67 -23
- package/dist/recordpage.layered.css +67 -23
- package/dist/recordpanel.css +35 -16
- package/dist/recordpanel.layered.css +35 -16
- package/dist/resizablepanel.css +8 -5
- package/dist/resizablepanel.layered.css +8 -5
- package/dist/richtext.css +24 -8
- package/dist/richtext.layered.css +24 -8
- package/dist/savedviews.css +9 -2
- package/dist/savedviews.layered.css +9 -2
- package/dist/select.css +25 -5
- package/dist/select.layered.css +25 -5
- package/dist/settingsshell.css +2 -3
- package/dist/settingsshell.layered.css +2 -3
- package/dist/slider.css +6 -27
- package/dist/slider.layered.css +6 -27
- package/dist/snackbar.css +24 -0
- package/dist/snackbar.d.ts +7 -0
- package/dist/snackbar.layered.css +26 -0
- package/dist/statcard.css +3 -1
- package/dist/statcard.layered.css +3 -1
- package/dist/styles.css +859 -345
- package/dist/styles.layered.css +859 -345
- package/dist/table.css +28 -10
- package/dist/table.layered.css +28 -10
- package/dist/tabletoolbar.css +35 -24
- package/dist/tabletoolbar.layered.css +35 -24
- package/dist/tabs.css +18 -4
- package/dist/tabs.layered.css +18 -4
- package/dist/textarea.css +2 -6
- package/dist/textarea.layered.css +2 -6
- package/dist/themepicker.css +2 -1
- package/dist/themepicker.layered.css +2 -1
- package/dist/tokens.css +13 -2
- package/dist/tokens.json +20 -3
- package/dist/types/components/bottomsheet/bottomsheet.d.ts +35 -0
- package/dist/types/components/bottomsheet/bottomsheet.recipe.d.ts +3 -0
- package/dist/types/components/bottomsheet/index.d.ts +2 -0
- package/dist/types/components/button/button.recipe.d.ts +11 -6
- package/dist/types/components/checkbox/checkbox.recipe.d.ts +28 -5
- package/dist/types/components/chip/chip.d.ts +22 -0
- package/dist/types/components/chip/chip.recipe.d.ts +48 -0
- package/dist/types/components/chipgroup/chipgroup.d.ts +14 -0
- package/dist/types/components/chipgroup/chipgroup.recipe.d.ts +5 -0
- package/dist/types/components/chipgroup/index.d.ts +2 -0
- package/dist/types/components/datatable/datatable.d.ts +19 -0
- package/dist/types/components/fileupload/fileupload.d.ts +26 -0
- package/dist/types/components/input/index.d.ts +1 -1
- package/dist/types/components/input/input.d.ts +72 -0
- package/dist/types/components/menusheet/index.d.ts +2 -0
- package/dist/types/components/menusheet/menusheet.d.ts +42 -0
- package/dist/types/components/menusheet/menusheet.recipe.d.ts +3 -0
- package/dist/types/components/snackbar/index.d.ts +2 -0
- package/dist/types/components/snackbar/snackbar.d.ts +36 -0
- package/dist/types/components/snackbar/snackbar.recipe.d.ts +5 -0
- package/dist/types/components/tabletoolbar/tabletoolbar.d.ts +28 -1
- package/dist/types/components/tabs/tabs.d.ts +11 -1
- package/dist/types/index.d.ts +7 -0
- package/dist/types/product/comments/comments.d.ts +9 -3
- package/dist/types/product/comments/comments.recipe.d.ts +12 -2
- package/dist/types/product/memberssettings/index.d.ts +1 -0
- package/dist/types/product/memberssettings/memberssettings.d.ts +85 -0
- package/dist/types/product/memberssettings/memberssettings.recipe.d.ts +3 -0
- package/dist/types/product/notificationssettings/index.d.ts +1 -0
- package/dist/types/product/notificationssettings/notificationssettings.d.ts +37 -0
- package/dist/types/product/notificationssettings/notificationssettings.recipe.d.ts +3 -0
- package/dist/types/product/recordpage/recordpage.d.ts +53 -2
- package/dist/types/product/workspacegeneralsettings/index.d.ts +1 -0
- package/dist/types/product/workspacegeneralsettings/workspacegeneralsettings.d.ts +48 -0
- package/dist/types/product/workspacegeneralsettings/workspacegeneralsettings.recipe.d.ts +3 -0
- package/dist/types/tokens/contract.d.ts +4 -1
- package/dist/types/tokens/elevation.d.ts +8 -5
- package/dist/types/tokens/palette.d.ts +9 -1
- package/dist/types/tokens/scale.d.ts +11 -1
- package/dist/types/tokens/semantic.d.ts +2 -0
- package/dist/wheelpicker.css +4 -2
- package/dist/wheelpicker.layered.css +4 -2
- package/dist/workspacegeneralsettings.css +30 -0
- package/dist/workspacegeneralsettings.d.ts +7 -0
- package/dist/workspacegeneralsettings.layered.css +32 -0
- package/package.json +36 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Activity as ActivityIcon, CalendarClock, ChevronDown, CircleDollarSign, Copy, DollarSign, FileText, Folder, Inbox, Landmark, LayoutGrid, Mail, Maximize2, Merge, MessageSquare, Minimize2, MoreHorizontal, Pencil, Phone, Plus, Search, Settings, Sparkles, SquareCheck, Star, Trash2, Users, UsersRound, Workflow, X, Zap, } from "lucide-react";
|
|
2
|
+
import { Activity as ActivityIcon, CalendarClock, ChevronDown, ChevronUp, CircleDollarSign, Copy, DollarSign, FileText, Folder, Inbox, Landmark, LayoutGrid, Mail, Maximize2, Merge, MessageSquare, Minimize2, MoreHorizontal, Pencil, Phone, Plus, Search, Settings, Sparkles, SquareCheck, Star, Trash2, Users, UsersRound, Workflow, X, Zap, } from "lucide-react";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { Avatar } from "../../components/avatar/index.js";
|
|
5
5
|
import { Checkbox } from "../../components/checkbox/index.js";
|
|
@@ -273,7 +273,14 @@ function NotesSection({ notes, onAdd, labels, }) {
|
|
|
273
273
|
return (_jsxs("section", { children: [_jsx(SectionHead, { title: labels.tabs.notes, count: notes.length, action: _jsxs("button", { type: "button", className: "crisp-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) => {
|
|
274
274
|
onAdd(t);
|
|
275
275
|
setOpen(false);
|
|
276
|
-
} })), notes.length === 0 && !open ? (_jsx("div", { className: "crisp-recordpage-emptystate", children: labels.noNotesYet })) : (
|
|
276
|
+
} })), notes.length === 0 && !open ? (_jsx("div", { className: "crisp-recordpage-emptystate", children: labels.noNotesYet })) : (
|
|
277
|
+
// #871: one line, not a two-row card -- measured live: avatar,
|
|
278
|
+
// then the note's own title and body run together inline, then a
|
|
279
|
+
// relative time at the far end of the SAME row. No author name at
|
|
280
|
+
// all (Attio's real preview never shows one here, only the note's
|
|
281
|
+
// own content) -- `labels.you` stays defined for a consumer that
|
|
282
|
+
// still wants it elsewhere, just unused in this exact spot now.
|
|
283
|
+
notes.map((n) => (_jsxs("div", { className: "crisp-recordpage-note", children: [_jsxs("span", { className: "crisp-recordpage-note-left", children: [_jsx(Avatar, { size: "sm", shape: "circle", tone: "neutral", children: "Y" }), n.title && (_jsx("span", { className: "crisp-recordpage-note-title", children: n.title })), _jsx("span", { className: "crisp-recordpage-note-text", children: n.text })] }), _jsx("span", { className: "crisp-recordpage-note-time", children: n.time })] }, n.id))))] }));
|
|
277
284
|
}
|
|
278
285
|
function TasksSection({ tasks, onAdd, onToggle, labels, }) {
|
|
279
286
|
const [open, setOpen] = React.useState(false);
|
|
@@ -306,13 +313,51 @@ function EmptyCard({ icon, children, }) {
|
|
|
306
313
|
* `RecordPage`'s own root does: `<div className={recordpageRecipe()}>`
|
|
307
314
|
* (also exported from this package).
|
|
308
315
|
*/
|
|
309
|
-
export function RecordDetails({ record, columns, sections, onEdit, labels, className, ...rest }) {
|
|
316
|
+
export function RecordDetails({ record, columns, sections, onEdit, labels, className, resizable = false, width: controlledWidth, defaultWidth = DETAILS_DEFAULT_W, onWidthChange, minWidth = DETAILS_MIN_W, maxWidth = DETAILS_MAX_W, ...rest }) {
|
|
310
317
|
const t = useRecordPageLabels(labels);
|
|
311
318
|
const { editingKey, pendingKey, error, openEditing, closeEditing, commit } = useEditableField(onEdit);
|
|
312
319
|
const [detailsOpen, setDetailsOpen] = React.useState(true);
|
|
313
320
|
const [searchOpen, setSearchOpen] = React.useState(false);
|
|
314
321
|
const [fieldQuery, setFieldQuery] = React.useState("");
|
|
315
322
|
const [showAllValues, setShowAllValues] = React.useState(false);
|
|
323
|
+
// #930: controlled/uncontrolled width, same split as RecordPanel's own
|
|
324
|
+
// resizable width -- only read when `resizable`, same reasoning as
|
|
325
|
+
// RecordPanel's own (a non-resizing consumer has no business carrying
|
|
326
|
+
// width state at all).
|
|
327
|
+
const [internalWidth, setInternalWidth] = React.useState(defaultWidth);
|
|
328
|
+
const width = controlledWidth ?? internalWidth;
|
|
329
|
+
const setWidth = (next) => {
|
|
330
|
+
const clamped = Math.max(minWidth, Math.min(maxWidth, next));
|
|
331
|
+
if (controlledWidth === undefined)
|
|
332
|
+
setInternalWidth(clamped);
|
|
333
|
+
onWidthChange?.(clamped);
|
|
334
|
+
};
|
|
335
|
+
const [detailsResizing, setDetailsResizing] = React.useState(false);
|
|
336
|
+
const detailsDragRef = React.useRef(null);
|
|
337
|
+
const onDetailsResizeDown = (e) => {
|
|
338
|
+
// This column is a leading (left-docked) rail, the mirror image of
|
|
339
|
+
// RecordPanel's own trailing (right-docked) one -- a rightward pull
|
|
340
|
+
// grows it in LTR, the same direction RecordPage's own internal aside
|
|
341
|
+
// resize already uses (#212/#255).
|
|
342
|
+
detailsDragRef.current = {
|
|
343
|
+
x: e.clientX,
|
|
344
|
+
w: width,
|
|
345
|
+
dir: isRtlElement(e.currentTarget) ? -1 : 1,
|
|
346
|
+
};
|
|
347
|
+
setDetailsResizing(true);
|
|
348
|
+
e.target.setPointerCapture(e.pointerId);
|
|
349
|
+
};
|
|
350
|
+
const onDetailsResizeMove = (e) => {
|
|
351
|
+
const r = detailsDragRef.current;
|
|
352
|
+
if (!r)
|
|
353
|
+
return;
|
|
354
|
+
setWidth(r.w + r.dir * (e.clientX - r.x));
|
|
355
|
+
};
|
|
356
|
+
const onDetailsResizeUp = (e) => {
|
|
357
|
+
detailsDragRef.current = null;
|
|
358
|
+
setDetailsResizing(false);
|
|
359
|
+
e.target.releasePointerCapture(e.pointerId);
|
|
360
|
+
};
|
|
316
361
|
const fields = columns.filter((col) => col.type !== "entity");
|
|
317
362
|
const q = fieldQuery.trim().toLowerCase();
|
|
318
363
|
const matches = (col) => !q || col.label.toLowerCase().includes(q);
|
|
@@ -349,7 +394,7 @@ export function RecordDetails({ record, columns, sections, onEdit, labels, class
|
|
|
349
394
|
groups = [{ title: undefined, fields: shown }];
|
|
350
395
|
}
|
|
351
396
|
const totalShown = groups.reduce((n, g) => n + g.fields.length, 0);
|
|
352
|
-
return (_jsxs("div", { className: cx("crisp-recordpage-section", className), ...rest, children: [_jsxs("div", { className: "crisp-recordpage-detailhead", children: [searchOpen ? (_jsx("input", {
|
|
397
|
+
return (_jsxs("div", { className: cx("crisp-recordpage-section", className), "data-resizable": resizable ? "" : undefined, style: resizable ? { width } : undefined, ...rest, children: [_jsxs("div", { className: "crisp-recordpage-detailhead", children: [searchOpen ? (_jsx("input", {
|
|
353
398
|
// biome-ignore lint/a11y/noAutofocus: focusing the just-opened field search is expected
|
|
354
399
|
autoFocus: true, type: "text", className: "crisp-recordpage-fieldsearch", placeholder: t.searchPlaceholder, "aria-label": t.searchFields, value: fieldQuery, onChange: (e) => setFieldQuery(e.target.value), onKeyDown: (e) => {
|
|
355
400
|
if (e.key === "Escape") {
|
|
@@ -423,7 +468,13 @@ export function RecordDetails({ record, columns, sections, onEdit, labels, class
|
|
|
423
468
|
setSearchOpen(next);
|
|
424
469
|
if (!next)
|
|
425
470
|
setFieldQuery("");
|
|
426
|
-
}, children: showAllValues ? t.viewLess : t.viewAllValues }))] })) : (_jsx("div", { className: "crisp-recordpage-noresults", children: t.noFieldsMatch(fieldQuery) })))
|
|
471
|
+
}, children: showAllValues ? t.viewLess : t.viewAllValues }))] })) : (_jsx("div", { className: "crisp-recordpage-noresults", children: t.noFieldsMatch(fieldQuery) }))), resizable && (_jsx("div", { className: "crisp-recordpage-detailresize", "data-active": detailsResizing ? "" : undefined, role: "separator", "aria-orientation": "vertical", "aria-label": t.resizeDetails, "aria-valuenow": Math.round(width), "aria-valuemin": minWidth, "aria-valuemax": maxWidth, tabIndex: 0, onPointerDown: onDetailsResizeDown, onPointerMove: onDetailsResizeMove, onPointerUp: onDetailsResizeUp, onKeyDown: (e) => {
|
|
472
|
+
if (e.key !== "ArrowLeft" && e.key !== "ArrowRight")
|
|
473
|
+
return;
|
|
474
|
+
const rtl = isRtlElement(e.currentTarget);
|
|
475
|
+
const grow = e.key === (rtl ? "ArrowLeft" : "ArrowRight");
|
|
476
|
+
setWidth(grow ? width + 16 : width - 16);
|
|
477
|
+
} }))] }));
|
|
427
478
|
}
|
|
428
479
|
RecordDetails.displayName = "RecordDetails";
|
|
429
480
|
/**
|
|
@@ -434,7 +485,7 @@ RecordDetails.displayName = "RecordDetails";
|
|
|
434
485
|
* Emails sections (Notes & Tasks are live — add and check things off). Drop it
|
|
435
486
|
* into the app's main content area.
|
|
436
487
|
*/
|
|
437
|
-
export function RecordPage({ record, columns, collection = "Records", labels, onBack, onToggleFull, full, className, onEdit, tabs: tabsProp, defaultTab, notes: notesProp, onAddNote, tasks: tasksProp, onAddTask, onToggleTask, sections, highlights: highlightsProp, quickActions: quickActionsProp, comments: commentsProp, onPostComment, currentUser, onShare, ...rest }) {
|
|
488
|
+
export function RecordPage({ record, columns, collection = "Records", labels, onBack, onToggleFull, full, onClose, closeLabel = "Close", onPrevious, previousRecordLabel = "Previous record", onNext, nextRecordLabel = "Next record", counterLabel, className, onEdit, tabs: tabsProp, defaultTab, notes: notesProp, onAddNote, tasks: tasksProp, onAddTask, onToggleTask, sections, highlights: highlightsProp, quickActions: quickActionsProp, comments: commentsProp, onPostComment, currentUser, onShare, ...rest }) {
|
|
438
489
|
const t = useRecordPageLabels(labels);
|
|
439
490
|
const [favorite, setFavorite] = React.useState(false);
|
|
440
491
|
// Notes/Tasks are controlled when the caller supplies `notes`/`tasks` (even
|
|
@@ -690,7 +741,7 @@ export function RecordPage({ record, columns, collection = "Records", labels, on
|
|
|
690
741
|
const effectivePanel = tabsProp
|
|
691
742
|
? (active) => tabsProp.find((tb) => tb.id === active)?.render()
|
|
692
743
|
: builtinPanel;
|
|
693
|
-
return (_jsxs("div", { className: recordpageRecipe({ className }), ...rest, children: [_jsxs("div", { className: "crisp-recordpage-topbar", children: [_jsxs("nav", { className: "crisp-recordpage-crumbs", "aria-label": t.breadcrumb, children: [_jsx("button", { type: "button", className: "crisp-recordpage-crumb", onClick: onBack, "aria-label": `Back to ${collection}`, children: collection }), _jsx("span", { className: "crisp-recordpage-crumb-sep", children: "/" }), _jsx("span", { className: "crisp-recordpage-crumb-name", children: name })] }), _jsxs("div", { className: "crisp-recordpage-actions", children: [_jsx("button", { type: "button", className: "crisp-recordpage-action crisp-recordpage-action--ghost", onClick: onShare, children: t.share }), _jsx("button", { type: "button", className: "crisp-recordpage-action crisp-recordpage-action--icon", onClick: () => setCommentsOpen(true), "aria-label": t.comments, children: _jsx(MessageSquare, { size: 15, strokeWidth: 1.75 }) }), _jsxs("button", { type: "button", className: "crisp-recordpage-action crisp-recordpage-action--ghost", children: [_jsx(Sparkles, { size: 14, strokeWidth: 1.75, color: "var(--crisp-bg-brand-solid)" }), t.askAttio] }), onToggleFull && (_jsx("button", { type: "button", className: "crisp-recordpage-action crisp-recordpage-action--icon", onClick: onToggleFull, "aria-label": full ? t.exitFullScreen : t.fullScreen, children: full ? (_jsx(Minimize2, { size: 15, strokeWidth: 1.75 })) : (_jsx(Maximize2, { size: 15, strokeWidth: 1.75 })) })), _jsxs(Menu, { align: "end", "aria-label": t.recordActions, trigger: _jsx("button", { type: "button", className: "crisp-recordpage-action crisp-recordpage-action--icon", "aria-label": t.recordActions, children: _jsx(MoreHorizontal, { size: 16, strokeWidth: 1.75 }) }), children: [_jsx(MenuItem, { icon: _jsx(Merge, { size: 15, strokeWidth: 1.75 }), children: t.mergeRecords }), _jsx(MenuItem, { icon: _jsx(Settings, { size: 15, strokeWidth: 1.75 }), children: t.configurePage }), _jsx(MenuItem, { icon: _jsx(Copy, { size: 15, strokeWidth: 1.75 }), children: t.copyPageUrl }), _jsx(MenuItem, { icon: _jsx(Copy, { size: 15, strokeWidth: 1.75 }), children: t.copyRecordId }), _jsx(MenuSeparator, {}), _jsx(MenuItem, { icon: _jsx(Star, { size: 15, strokeWidth: 1.75 }), onSelect: () => setFavorite((f) => !f), children: favorite ? t.removeFromFavourites : t.addToFavourites }), _jsx(MenuSeparator, {}), _jsx(MenuItem, { icon: _jsx(Trash2, { size: 15, strokeWidth: 1.75 }), danger: true, children: t.deleteRecord })] })] })] }), _jsxs("div", { className: "crisp-recordpage-body", children: [_jsxs("aside", { className: "crisp-recordpage-details", style: { width: detailsW }, children: [_jsxs("div", { className: "crisp-recordpage-entity", children: [entity?.avatar ? (_jsx("img", { className: "crisp-recordpage-entity-logo", src: entity.avatar, alt: "" })) : (_jsx(Avatar, { size: "lg", shape: "rounded", tone: "brand", children: name.charAt(0) })), _jsx("span", { className: "crisp-recordpage-entity-name", children: name }), _jsx("button", { type: "button", className: "crisp-recordpage-entity-iconbtn", "aria-label": t.editRecord, children: _jsx(Pencil, { size: 15, strokeWidth: 1.75 }) }), _jsx("button", { type: "button", className: "crisp-recordpage-entity-iconbtn", "data-active": favorite ? "" : undefined, "aria-pressed": favorite, onClick: () => setFavorite((f) => !f), "aria-label": favorite ? t.removeFromFavourites : t.addToFavourites, children: _jsx(Star, { size: 15, strokeWidth: 1.75 }) })] }), _jsxs("div", { className: "crisp-recordpage-quickactions", children: [_jsxs("button", { type: "button", className: "crisp-recordpage-compose", children: [_jsx(Mail, { size: 16, strokeWidth: 1.75 }), t.composeEmail] }), _jsxs("button", { type: "button", className: "crisp-recordpage-compose", children: [_jsx(Plus, { size: 16, strokeWidth: 1.75 }), t.addToList] }), quickActions.map((a, i) => (_jsx("button", { type: "button", className: "crisp-recordpage-quickbtn", "aria-label": a.label, onClick: a.onSelect, children: _jsx(a.icon, { size: 15, strokeWidth: 1.75 }) }, i)))] }), _jsx(RecordDetails, { record: record, columns: columns, sections: sections, onEdit: onEdit, labels: labels }), _jsxs("div", { className: "crisp-recordpage-section", children: [_jsx(SectionHead, { title: t.tabs.lists, action: _jsxs("button", { type: "button", className: "crisp-recordpage-addbtn", children: [_jsx(Plus, { size: 14, strokeWidth: 2 }), t.addToList] }) }), _jsx("div", { className: "crisp-recordpage-emptystate", children: t.noLists })] })] }), _jsx("div", { className: "crisp-recordpage-resizer", "data-active": resizing ? "" : undefined, role: "separator", "aria-orientation": "vertical", "aria-label": t.resizeDetails, "aria-valuenow": Math.round(detailsW), "aria-valuemin": DETAILS_MIN_W, "aria-valuemax": DETAILS_MAX_W, tabIndex: 0, onPointerDown: onResizeDown, onPointerMove: onResizeMove, onPointerUp: onResizeUp, onKeyDown: (e) => {
|
|
744
|
+
return (_jsxs("div", { className: recordpageRecipe({ className }), ...rest, children: [_jsxs("div", { className: "crisp-recordpage-topbar", "data-peek": onClose ? "" : undefined, children: [onClose ? (_jsxs("div", { className: "crisp-recordpage-peekbar", children: [_jsx("button", { type: "button", className: "crisp-recordpage-peek-iconbtn", "aria-label": closeLabel, onClick: onClose, children: _jsx(X, { size: 16, strokeWidth: 1.75 }) }), (onPrevious || onNext) && (_jsxs("span", { className: "crisp-recordpage-peek-pager", children: [_jsx("button", { type: "button", className: "crisp-recordpage-peek-navbtn", "aria-label": previousRecordLabel, disabled: !onPrevious, onClick: onPrevious, children: _jsx(ChevronUp, { size: 16, strokeWidth: 1.75 }) }), _jsx("button", { type: "button", className: "crisp-recordpage-peek-navbtn", "aria-label": nextRecordLabel, disabled: !onNext, onClick: onNext, children: _jsx(ChevronDown, { size: 16, strokeWidth: 1.75 }) })] })), counterLabel != null && (_jsx("span", { className: "crisp-recordpage-peek-counter", children: counterLabel }))] })) : (_jsxs("nav", { className: "crisp-recordpage-crumbs", "aria-label": t.breadcrumb, children: [_jsx("button", { type: "button", className: "crisp-recordpage-crumb", onClick: onBack, "aria-label": `Back to ${collection}`, children: collection }), _jsx("span", { className: "crisp-recordpage-crumb-sep", children: "/" }), _jsx("span", { className: "crisp-recordpage-crumb-name", children: name })] })), _jsxs("div", { className: "crisp-recordpage-actions", children: [_jsx("button", { type: "button", className: "crisp-recordpage-action crisp-recordpage-action--ghost", onClick: onShare, children: t.share }), _jsx("button", { type: "button", className: "crisp-recordpage-action crisp-recordpage-action--icon", onClick: () => setCommentsOpen(true), "aria-label": t.comments, children: _jsx(MessageSquare, { size: 15, strokeWidth: 1.75 }) }), _jsxs("button", { type: "button", className: "crisp-recordpage-action crisp-recordpage-action--ghost", children: [_jsx(Sparkles, { size: 14, strokeWidth: 1.75, color: "var(--crisp-bg-brand-solid)" }), t.askAttio] }), onToggleFull && (_jsx("button", { type: "button", className: "crisp-recordpage-action crisp-recordpage-action--icon", onClick: onToggleFull, "aria-label": full ? t.exitFullScreen : t.fullScreen, children: full ? (_jsx(Minimize2, { size: 15, strokeWidth: 1.75 })) : (_jsx(Maximize2, { size: 15, strokeWidth: 1.75 })) })), _jsxs(Menu, { align: "end", "aria-label": t.recordActions, trigger: _jsx("button", { type: "button", className: "crisp-recordpage-action crisp-recordpage-action--icon", "aria-label": t.recordActions, children: _jsx(MoreHorizontal, { size: 16, strokeWidth: 1.75 }) }), children: [_jsx(MenuItem, { icon: _jsx(Merge, { size: 15, strokeWidth: 1.75 }), children: t.mergeRecords }), _jsx(MenuItem, { icon: _jsx(Settings, { size: 15, strokeWidth: 1.75 }), children: t.configurePage }), _jsx(MenuItem, { icon: _jsx(Copy, { size: 15, strokeWidth: 1.75 }), children: t.copyPageUrl }), _jsx(MenuItem, { icon: _jsx(Copy, { size: 15, strokeWidth: 1.75 }), children: t.copyRecordId }), _jsx(MenuSeparator, {}), _jsx(MenuItem, { icon: _jsx(Star, { size: 15, strokeWidth: 1.75 }), onSelect: () => setFavorite((f) => !f), children: favorite ? t.removeFromFavourites : t.addToFavourites }), _jsx(MenuSeparator, {}), _jsx(MenuItem, { icon: _jsx(Trash2, { size: 15, strokeWidth: 1.75 }), danger: true, children: t.deleteRecord })] })] })] }), _jsxs("div", { className: "crisp-recordpage-body", children: [_jsxs("aside", { className: "crisp-recordpage-details", style: { width: detailsW }, children: [_jsxs("div", { className: "crisp-recordpage-entity", children: [entity?.avatar ? (_jsx("img", { className: "crisp-recordpage-entity-logo", src: entity.avatar, alt: "" })) : (_jsx(Avatar, { size: "lg", shape: "rounded", tone: "brand", children: name.charAt(0) })), _jsx("span", { className: "crisp-recordpage-entity-name", children: name }), _jsx("button", { type: "button", className: "crisp-recordpage-entity-iconbtn", "aria-label": t.editRecord, children: _jsx(Pencil, { size: 15, strokeWidth: 1.75 }) }), _jsx("button", { type: "button", className: "crisp-recordpage-entity-iconbtn", "data-active": favorite ? "" : undefined, "aria-pressed": favorite, onClick: () => setFavorite((f) => !f), "aria-label": favorite ? t.removeFromFavourites : t.addToFavourites, children: _jsx(Star, { size: 15, strokeWidth: 1.75 }) })] }), _jsxs("div", { className: "crisp-recordpage-quickactions", children: [_jsxs("button", { type: "button", className: "crisp-recordpage-compose", children: [_jsx(Mail, { size: 16, strokeWidth: 1.75 }), t.composeEmail] }), _jsxs("button", { type: "button", className: "crisp-recordpage-compose", children: [_jsx(Plus, { size: 16, strokeWidth: 1.75 }), t.addToList] }), quickActions.map((a, i) => (_jsx("button", { type: "button", className: "crisp-recordpage-quickbtn", "aria-label": a.label, onClick: a.onSelect, children: _jsx(a.icon, { size: 15, strokeWidth: 1.75 }) }, i)))] }), _jsx(RecordDetails, { record: record, columns: columns, sections: sections, onEdit: onEdit, labels: labels }), _jsxs("div", { className: "crisp-recordpage-section", children: [_jsx(SectionHead, { title: t.tabs.lists, action: _jsxs("button", { type: "button", className: "crisp-recordpage-addbtn", children: [_jsx(Plus, { size: 14, strokeWidth: 2 }), t.addToList] }) }), _jsx("div", { className: "crisp-recordpage-emptystate", children: t.noLists })] })] }), _jsx("div", { className: "crisp-recordpage-resizer", "data-active": resizing ? "" : undefined, role: "separator", "aria-orientation": "vertical", "aria-label": t.resizeDetails, "aria-valuenow": Math.round(detailsW), "aria-valuemin": DETAILS_MIN_W, "aria-valuemax": DETAILS_MAX_W, tabIndex: 0, onPointerDown: onResizeDown, onPointerMove: onResizeMove, onPointerUp: onResizeUp, onKeyDown: (e) => {
|
|
694
745
|
if (e.key !== "ArrowLeft" && e.key !== "ArrowRight")
|
|
695
746
|
return;
|
|
696
747
|
// The pointer drag learned its direction in #212; this did not.
|
|
@@ -23,6 +23,21 @@ const config = defineRecipe({
|
|
|
23
23
|
& * { box-sizing: border-box; }
|
|
24
24
|
|
|
25
25
|
& .crisp-recordpage-topbar { display: flex; align-items: center; gap: ${c.space["2"]}; height: 44px; flex-shrink: 0; padding: 0 ${c.space["3"]}; border-bottom: 1px solid ${c.borderSubtle}; }
|
|
26
|
+
/* #624: expanding a peek to its full record keeps a hybrid header
|
|
27
|
+
(Close/Previous/Next/counter) instead of reverting to the plain
|
|
28
|
+
breadcrumb above -- measured live on a real expanded record: 49px
|
|
29
|
+
tall (not 44px), 8px/10px padding, same borderSubtle hairline. Same
|
|
30
|
+
tokens as product/recordpanel's own already-shipped toolbar, not
|
|
31
|
+
new values -- this is the same row, reused in a second component. */
|
|
32
|
+
& .crisp-recordpage-topbar[data-peek] { height: 49px; padding: ${c.space["2"]} ${c.space["2.5"]}; }
|
|
33
|
+
& .crisp-recordpage-peekbar { display: flex; align-items: center; }
|
|
34
|
+
& .crisp-recordpage-peek-iconbtn { display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 28px; flex-shrink: 0; padding: 0; border: none; border-radius: ${c.radius.md}; background: transparent; color: color-mix(in srgb, ${c.fgPrimary} 55%, transparent); font-family: inherit; cursor: pointer; transition: background-color .12s; }
|
|
35
|
+
& .crisp-recordpage-peek-iconbtn:hover { background: ${c.bgRaised}; color: ${c.fgPrimary}; }
|
|
36
|
+
& .crisp-recordpage-peek-pager { display: flex; align-items: center; gap: ${c.space["1.5"]}; margin-inline-start: ${c.space["3"]}; }
|
|
37
|
+
& .crisp-recordpage-peek-navbtn { display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 28px; flex-shrink: 0; padding: 0; border: none; border-radius: ${c.radius.md}; background: ${c.bgSurface}; box-shadow: ${e.field}; color: ${c.fgPrimary}; font-family: inherit; cursor: pointer; }
|
|
38
|
+
& .crisp-recordpage-peek-navbtn:hover:not(:disabled) { background: ${c.bgRaised}; }
|
|
39
|
+
& .crisp-recordpage-peek-navbtn:disabled { opacity: 0.4; cursor: not-allowed; }
|
|
40
|
+
& .crisp-recordpage-peek-counter { margin-inline-start: ${c.space["3"]}; font-size: 12px; font-weight: ${c.weight.medium}; color: color-mix(in srgb, ${c.fgPrimary} 63%, transparent); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
26
41
|
& .crisp-recordpage-crumbs { display: inline-flex; align-items: center; gap: ${c.space["1.5"]}; min-width: 0; }
|
|
27
42
|
& .crisp-recordpage-crumb { display: inline-flex; align-items: center; height: 26px; padding: 0 ${c.space["1.5"]}; margin: 0 calc(-1 * ${c.space["1.5"]}) 0 0; border: none; border-radius: ${c.radius.md}; background: none; font-family: inherit; font-size: ${c.text.lg}; font-weight: ${c.weight.medium}; color: ${c.fgMuted}; cursor: pointer; }
|
|
28
43
|
& .crisp-recordpage-crumb:hover { background: ${c.bgRaised}; color: ${c.fgPrimary}; }
|
|
@@ -42,6 +57,17 @@ const config = defineRecipe({
|
|
|
42
57
|
& .crisp-recordpage-resizer::before { content: ""; position: absolute; top: 0; bottom: 0; inset-inline-start: -4px; width: 9px; }
|
|
43
58
|
& .crisp-recordpage-resizer::after { content: ""; position: absolute; top: 0; bottom: 0; inset-inline-start: -1px; width: 2px; background: transparent; transition: background-color .12s ease; }
|
|
44
59
|
& .crisp-recordpage-resizer:hover::after, & .crisp-recordpage-resizer[data-active]::after { background: color-mix(in srgb, ${c.bgBrandSolid} 55%, transparent); }
|
|
60
|
+
/* #930: RecordDetails' own standalone resize handle -- same visible
|
|
61
|
+
hit-slop/line-highlight shape as .crisp-recordpage-resizer just above,
|
|
62
|
+
just absolutely positioned at the trailing edge instead of a 0-width
|
|
63
|
+
flex sibling, since RecordDetails always returns a single root (the
|
|
64
|
+
same internal-strip placement RecordPanel's own resize handle uses,
|
|
65
|
+
mirrored to the opposite edge -- this column is leading, RecordPanel
|
|
66
|
+
is trailing). */
|
|
67
|
+
& .crisp-recordpage-section[data-resizable] { position: relative; }
|
|
68
|
+
& .crisp-recordpage-detailresize { position: absolute; top: 0; bottom: 0; inset-inline-end: -4px; width: 9px; cursor: col-resize; z-index: 5; touch-action: none; }
|
|
69
|
+
& .crisp-recordpage-detailresize::after { content: ""; position: absolute; top: 0; bottom: 0; inset-inline-end: 3px; width: 2px; background: transparent; transition: background-color .12s ease; }
|
|
70
|
+
& .crisp-recordpage-detailresize:hover::after, & .crisp-recordpage-detailresize[data-active]::after { background: color-mix(in srgb, ${c.bgBrandSolid} 55%, transparent); }
|
|
45
71
|
& .crisp-recordpage-entity { display: flex; align-items: center; gap: ${c.space["2"]}; padding: ${c.space["1"]} ${c.space["1"]} ${c.space["2"]}; }
|
|
46
72
|
& .crisp-recordpage-entity-logo { width: 32px; height: 32px; border-radius: 30%; flex-shrink: 0; object-fit: cover; background: ${c.bgRaised}; }
|
|
47
73
|
& .crisp-recordpage-entity-name { flex: 0 1 auto; min-width: 0; font-size: ${c.text.xl}; font-weight: ${c.weight.semibold}; letter-spacing: -0.3px; color: ${c.fgPrimary}; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
@@ -175,12 +201,23 @@ const config = defineRecipe({
|
|
|
175
201
|
& .crisp-recordpage-btn--primary { box-shadow: none; background: ${c.bgBrandSolid}; color: ${c.fgOnBrand}; }
|
|
176
202
|
& .crisp-recordpage-btn--primary:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
177
203
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
204
|
+
/* #871: one bordered card per note (border-radius.xl, borderSubtle),
|
|
205
|
+
not a flush divider-list row -- measured live on a real populated
|
|
206
|
+
note: the whole row is flex space-between, avatar+title+body on the
|
|
207
|
+
left (its own flex, gap.2), the relative time alone on the right.
|
|
208
|
+
The real DOM nests an inner clickable button (padding 6/8, radius.md)
|
|
209
|
+
inside an outer card (padding 4px, radius.xl) -- collapsed into this
|
|
210
|
+
one non-interactive div's own combined padding (2.5/3) since crisp's
|
|
211
|
+
NotesSection has no click-to-open behaviour to justify the extra
|
|
212
|
+
layer. The 6px gap between stacked cards is NOT independently
|
|
213
|
+
measured (this workspace only ever had one real note to check) --
|
|
214
|
+
a judgement call off the existing space scale, not a live value. */
|
|
215
|
+
& .crisp-recordpage-note { display: flex; align-items: center; justify-content: space-between; gap: ${c.space["3"]}; padding: ${c.space["2.5"]} ${c.space["3"]}; border: 1px solid ${c.borderSubtle}; border-radius: ${c.radius.xl}; }
|
|
216
|
+
& .crisp-recordpage-note + .crisp-recordpage-note { margin-top: ${c.space["1.5"]}; }
|
|
217
|
+
& .crisp-recordpage-note-left { display: flex; align-items: center; gap: ${c.space["2"]}; min-width: 0; overflow: hidden; }
|
|
218
|
+
& .crisp-recordpage-note-title { flex-shrink: 0; font-size: ${c.text.lg}; font-weight: ${c.weight.medium}; color: ${c.fgPrimary}; }
|
|
219
|
+
& .crisp-recordpage-note-text { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: ${c.text.lg}; font-weight: ${c.weight.medium}; color: color-mix(in srgb, ${c.fgPrimary} 63%, transparent); }
|
|
220
|
+
& .crisp-recordpage-note-time { flex-shrink: 0; font-size: ${c.text.sm}; font-weight: ${c.weight.medium}; color: color-mix(in srgb, ${c.fgPrimary} 50%, transparent); }
|
|
184
221
|
|
|
185
222
|
& .crisp-recordpage-task { display: flex; align-items: flex-start; gap: ${c.space["2"]}; padding: ${c.space["1.5"]} 0; border-top: 1px solid ${c.borderSubtle}; }
|
|
186
223
|
& .crisp-recordpage-task-text { flex: 1; min-width: 0; font-size: ${c.text.lg}; color: ${c.fgPrimary}; padding-top: 1px; }
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { WorkspaceGeneralSettings, } from "./workspacegeneralsettings.js";
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Copy } from "lucide-react";
|
|
3
|
+
import { Avatar } from "../../components/avatar/index.js";
|
|
4
|
+
import { Button } from "../../components/button/index.js";
|
|
5
|
+
import { FormField } from "../../components/formfield/index.js";
|
|
6
|
+
import { Input } from "../../components/input/index.js";
|
|
7
|
+
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "../../components/table/index.js";
|
|
8
|
+
import { TextLink } from "../../components/textlink/index.js";
|
|
9
|
+
import { workspacegeneralsettingsRecipe } from "./workspacegeneralsettings.recipe.js";
|
|
10
|
+
/**
|
|
11
|
+
* Attio's Settings → Workspace → General page (crisp#638, P-128), measured
|
|
12
|
+
* live: a logo + Name/Slug section, an "Export workspace data" table, and a
|
|
13
|
+
* "Danger zone" delete-workspace row.
|
|
14
|
+
*/
|
|
15
|
+
export function WorkspaceGeneralSettings({ logoSrc, workspaceInitial, onUploadLogo, name, onNameChange, slug, onCopySlug, exports: exportRows, onStartExport, onDeleteWorkspace, className, title = "General", description = "Change the settings for your current workspace.", learnMoreLabel = "Learn more", learnMoreHref = "#", logoHelpText = "We only support PNGs, JPEGs and GIFs under 10MB", uploadLogoLabel = "Upload logo", nameFieldLabel = "Name", slugFieldLabel = "Slug", copySlugLabel = "Copy slug", exportSectionTitle = "Export workspace data", exportSectionDescription = "Exports are in CSV format and can be downloaded within 7 days", startExportLabel = "Start new export", typeColumnLabel = "Type", dateColumnLabel = "Date", noExportsLabel = "No exports yet", dangerSectionTitle = "Danger zone", deleteWorkspaceTitle = "Delete workspace", deleteWorkspaceWarning = "Once deleted, your workspace cannot be recovered", deleteWorkspaceLabel = "Delete workspace", ...rest }) {
|
|
16
|
+
return (_jsx("div", { className: workspacegeneralsettingsRecipe({ className }), ...rest, children: _jsxs("div", { className: "crisp-workspacegeneralsettings-page", children: [_jsxs("div", { className: "crisp-workspacegeneralsettings-heading", children: [_jsx("h1", { className: "crisp-workspacegeneralsettings-title", children: title }), _jsxs("p", { className: "crisp-workspacegeneralsettings-desc", children: [description, " ", _jsx(TextLink, { href: learnMoreHref, children: learnMoreLabel })] })] }), _jsxs("div", { className: "crisp-workspacegeneralsettings-section", children: [_jsxs("div", { className: "crisp-workspacegeneralsettings-logorow", children: [logoSrc ? (_jsx("img", { src: logoSrc, alt: "", width: 48, height: 48, style: { borderRadius: "30%", objectFit: "cover" } })) : (_jsx(Avatar, { size: "xl", shape: "rounded", tone: "brand", children: workspaceInitial })), _jsxs("div", { className: "crisp-workspacegeneralsettings-logotext", children: [_jsx("span", { className: "crisp-workspacegeneralsettings-logohelp", children: logoHelpText }), _jsx(Button, { intent: "neutral", size: "sm", onClick: onUploadLogo, children: uploadLogoLabel })] })] }), _jsxs("div", { className: "crisp-workspacegeneralsettings-fieldrow", children: [_jsx(FormField, { label: nameFieldLabel, children: _jsx(Input, { value: name, onChange: (e) => onNameChange(e.target.value) }) }), _jsx(FormField, { label: slugFieldLabel, children: _jsxs("div", { className: "crisp-workspacegeneralsettings-slugfield", children: [_jsx(Input, { value: slug, readOnly: true }), _jsx("button", { type: "button", className: "crisp-workspacegeneralsettings-copybtn", "aria-label": copySlugLabel, onClick: onCopySlug, children: _jsx(Copy, { size: 16 }) })] }) })] })] }), _jsxs("div", { className: "crisp-workspacegeneralsettings-section", children: [_jsxs("div", { className: "crisp-workspacegeneralsettings-sectionhead", children: [_jsxs("div", { className: "crisp-workspacegeneralsettings-sectionhead-text", children: [_jsx("h2", { className: "crisp-workspacegeneralsettings-sectiontitle", children: exportSectionTitle }), _jsx("p", { className: "crisp-workspacegeneralsettings-desc", children: exportSectionDescription })] }), _jsx(Button, { intent: "neutral", size: "sm", onClick: onStartExport, children: startExportLabel })] }), _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: typeColumnLabel }), _jsx(TableHead, { children: dateColumnLabel })] }) }), _jsx(TableBody, { children: exportRows.length === 0 ? (_jsx(TableRow, { children: _jsx(TableCell, { colSpan: 2, children: _jsx("div", { className: "crisp-workspacegeneralsettings-emptyrow", children: noExportsLabel }) }) })) : (exportRows.map((row) => (_jsxs(TableRow, { children: [_jsx(TableCell, { children: row.type }), _jsx(TableCell, { children: row.date })] }, row.id)))) })] })] }), _jsxs("div", { className: "crisp-workspacegeneralsettings-section", children: [_jsx("h2", { className: "crisp-workspacegeneralsettings-sectiontitle", children: dangerSectionTitle }), _jsxs("div", { className: "crisp-workspacegeneralsettings-dangerrow", children: [_jsxs("div", { className: "crisp-workspacegeneralsettings-dangertext", children: [_jsx("span", { className: "crisp-workspacegeneralsettings-rowtitle", children: deleteWorkspaceTitle }), _jsx("span", { className: "crisp-workspacegeneralsettings-dangerwarning", children: deleteWorkspaceWarning })] }), _jsx(Button, { intent: "danger", size: "sm", onClick: onDeleteWorkspace, children: deleteWorkspaceLabel })] })] })] }) }));
|
|
17
|
+
}
|
|
18
|
+
WorkspaceGeneralSettings.displayName = "WorkspaceGeneralSettings";
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { defineRecipe, resolveRecipe } from "../../core/index.js";
|
|
2
|
+
import { contract as c } from "../../tokens/index.js";
|
|
3
|
+
// Measured live against app.attio.com/<workspace>/settings/workspace
|
|
4
|
+
// (crisp#638, P-128). Logo tile + Name/Slug fields side by side, an
|
|
5
|
+
// "Export workspace data" section (header row + Type/Date table + empty
|
|
6
|
+
// state), and a "Danger zone" section (a red-tinted bordered row).
|
|
7
|
+
const config = defineRecipe({
|
|
8
|
+
name: "workspacegeneralsettings",
|
|
9
|
+
base: `& .crisp-workspacegeneralsettings-page { display: flex; flex-direction: column; gap: ${c.space["6"]}; font-family: ${c.font.sans}; }
|
|
10
|
+
& .crisp-workspacegeneralsettings-heading { display: flex; flex-direction: column; gap: ${c.space["0.5"]}; }
|
|
11
|
+
& .crisp-workspacegeneralsettings-title { font-size: 24px; line-height: 28px; font-weight: ${c.weight.semibold}; letter-spacing: -0.48px; color: ${c.fgPrimary}; margin: 0; }
|
|
12
|
+
& .crisp-workspacegeneralsettings-desc { font-size: ${c.text.lg}; line-height: 20px; color: ${c.fgMuted}; margin: 0; }
|
|
13
|
+
|
|
14
|
+
& .crisp-workspacegeneralsettings-section { display: flex; flex-direction: column; gap: ${c.space["4"]}; }
|
|
15
|
+
& .crisp-workspacegeneralsettings-sectiontitle { font-size: 16px; line-height: 20px; font-weight: ${c.weight.semibold}; color: ${c.fgPrimary}; margin: 0; }
|
|
16
|
+
& .crisp-workspacegeneralsettings-sectionhead { display: flex; align-items: flex-start; justify-content: space-between; gap: ${c.space["4"]}; }
|
|
17
|
+
& .crisp-workspacegeneralsettings-sectionhead-text { display: flex; flex-direction: column; gap: ${c.space["1"]}; }
|
|
18
|
+
|
|
19
|
+
/* Logo row: a tile + helper text + "Upload logo" button. */
|
|
20
|
+
& .crisp-workspacegeneralsettings-logorow { display: flex; align-items: center; gap: ${c.space["4"]}; }
|
|
21
|
+
& .crisp-workspacegeneralsettings-logohelp { font-size: ${c.text.sm}; color: ${c.fgMuted}; }
|
|
22
|
+
& .crisp-workspacegeneralsettings-logotext { display: flex; flex-direction: column; align-items: flex-start; gap: ${c.space["1"]}; }
|
|
23
|
+
|
|
24
|
+
/* Name + Slug sit side by side, each ~half the content width. */
|
|
25
|
+
& .crisp-workspacegeneralsettings-fieldrow { display: flex; gap: ${c.space["4"]}; }
|
|
26
|
+
& .crisp-workspacegeneralsettings-fieldrow > * { flex: 1; min-width: 0; }
|
|
27
|
+
& .crisp-workspacegeneralsettings-slugfield { display: flex; align-items: center; gap: ${c.space["1.5"]}; }
|
|
28
|
+
& .crisp-workspacegeneralsettings-slugfield > :first-child { flex: 1; min-width: 0; }
|
|
29
|
+
& .crisp-workspacegeneralsettings-copybtn { display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; width: 32px; height: 32px; border: none; border-radius: ${c.radius.md}; background: none; font-family: inherit; color: ${c.fgMuted}; cursor: pointer; }
|
|
30
|
+
& .crisp-workspacegeneralsettings-copybtn:hover { background: ${c.bgRaised}; color: ${c.fgPrimary}; }
|
|
31
|
+
|
|
32
|
+
/* Export-data empty state -- measured: 14/500/63%-opacity, centred in a
|
|
33
|
+
40px row, no border of its own (the Table wrapper already draws it). */
|
|
34
|
+
& .crisp-workspacegeneralsettings-emptyrow { padding: ${c.space["2.5"]} ${c.space["4"]}; font-size: ${c.text.lg}; font-weight: ${c.weight.medium}; color: color-mix(in srgb, ${c.fgPrimary} 63%, transparent); text-align: center; }
|
|
35
|
+
|
|
36
|
+
/* Danger zone: a red-tinted bordered row, distinct from every other
|
|
37
|
+
card on the page. */
|
|
38
|
+
& .crisp-workspacegeneralsettings-dangerrow { display: flex; align-items: center; justify-content: space-between; gap: ${c.space["6"]}; padding: ${c.space["3"]} ${c.space["4"]}; border: 1px solid color-mix(in srgb, ${c.bgDanger} 30%, transparent); border-radius: 12px; background: color-mix(in srgb, ${c.bgDanger} 6%, transparent); }
|
|
39
|
+
& .crisp-workspacegeneralsettings-dangertext { display: flex; flex-direction: column; gap: ${c.space["1"]}; }
|
|
40
|
+
& .crisp-workspacegeneralsettings-rowtitle { font-size: ${c.text.lg}; font-weight: ${c.weight.medium}; color: ${c.fgPrimary}; }
|
|
41
|
+
& .crisp-workspacegeneralsettings-dangerwarning { font-size: ${c.text.sm}; font-weight: ${c.weight.medium}; color: color-mix(in srgb, ${c.fgPrimary} 63%, transparent); }
|
|
42
|
+
`,
|
|
43
|
+
variants: {},
|
|
44
|
+
defaultVariants: {},
|
|
45
|
+
compoundVariants: [],
|
|
46
|
+
});
|
|
47
|
+
export const workspacegeneralsettingsRecipe = resolveRecipe(config);
|
|
48
|
+
export default config;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { VAR_PREFIX } from "../core/namespace.js";
|
|
2
|
-
import { font as fontScale, radius as radiusScale, size as sizeScale, space as spaceScale, text as textScale, weight as weightScale, } from "./scale.js";
|
|
2
|
+
import { font as fontScale, radius as radiusScale, size as sizeScale, space as spaceScale, text as textScale, tracking as trackingScale, weight as weightScale, } from "./scale.js";
|
|
3
3
|
import { semantic } from "./semantic.js";
|
|
4
4
|
const kebab = (s) => s.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`);
|
|
5
5
|
const toVar = (key) => `var(--${VAR_PREFIX}${kebab(key)})`;
|
|
@@ -16,4 +16,5 @@ export const contract = Object.assign(colors, {
|
|
|
16
16
|
font: mapScale("font", fontScale),
|
|
17
17
|
text: mapScale("text", textScale),
|
|
18
18
|
weight: mapScale("weight", weightScale),
|
|
19
|
+
tracking: mapScale("tracking", trackingScale),
|
|
19
20
|
});
|
|
@@ -12,9 +12,11 @@ import { contract as c } from "./contract.js";
|
|
|
12
12
|
*/
|
|
13
13
|
export const elevation = {
|
|
14
14
|
/**
|
|
15
|
-
* Tier 1 — anything floating and *attached*:
|
|
15
|
+
* Tier 1 — anything floating and *attached*: popovers, dropdowns,
|
|
16
16
|
* hover-preview cards, the floating sidebar, the record peek, drawers, the
|
|
17
17
|
* bulk bar. Pairs with a 10–12px radius and, for panels, an 8px gutter.
|
|
18
|
+
* NOT `Menu` (your-moon/crisp#934) -- that moved to
|
|
19
|
+
* `selectFloating`, seed-design's own flat shadow, below.
|
|
18
20
|
*/
|
|
19
21
|
floating: `inset 0 0 0 1px ${c.ringPopover}, 0 0 0 1px ${c.shadowPopover1}, 0 4px 8px -4px ${c.shadowPopover2}, 0 4px 12px -2px ${c.shadowPopover3}`,
|
|
20
22
|
/**
|
|
@@ -57,10 +59,11 @@ export const elevation = {
|
|
|
57
59
|
*/
|
|
58
60
|
fieldNoRing: `0 0 2px rgba(28,40,64,0.18), 0 1px 3px rgba(0,0,0,0.04)`,
|
|
59
61
|
/**
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
62
|
+
* Seed-design's own `$shadow.s3`, a single flat layer, NOT `floating`
|
|
63
|
+
* above. Originally Select's own listbox shadow (crisp/#867); also `Menu`'s
|
|
64
|
+
* (your-moon/crisp#934) -- the raw seed dump confirms Menu
|
|
65
|
+
* is the one component seed's own spec gives a shadow to at all, and it's
|
|
66
|
+
* this exact value. Despite the name, not exclusive to Select.
|
|
64
67
|
*/
|
|
65
68
|
selectFloating: `0 4px 16px ${c.shadowSelect}`,
|
|
66
69
|
};
|
|
@@ -63,14 +63,34 @@ export const palette = {
|
|
|
63
63
|
ringBrandSoft: { light: "#d6e5ff", dark: "#212224" },
|
|
64
64
|
tileActive: { light: "#ffffff", dark: "#101112" },
|
|
65
65
|
tileActiveShadow: { light: "rgba(0,0,0,0.04)", dark: "rgba(0,0,0,0)" },
|
|
66
|
-
// Dialog's page-dimming backdrop (crisp#677
|
|
67
|
-
//
|
|
68
|
-
//
|
|
69
|
-
//
|
|
70
|
-
//
|
|
71
|
-
//
|
|
72
|
-
//
|
|
73
|
-
|
|
66
|
+
// Dialog's page-dimming backdrop (crisp#677, Attio's own measured value:
|
|
67
|
+
// `rgba(0,0,0,.25)` light, `rgba(0,0,0,.55)` dark — `Dialog`'s own backdrop
|
|
68
|
+
// had shipped fully `transparent`, AUTHORING's Lesson 14 measured the
|
|
69
|
+
// command palette has NO dark backdrop and that got generalized to every
|
|
70
|
+
// `Dialog`, which this corrected).
|
|
71
|
+
//
|
|
72
|
+
// your-moon/crisp#934: light theme moved to seed-design's own scrim
|
|
73
|
+
// (`#00000074` ≈ 45% black, `bg.overlay`) — the owner's flat-dialog
|
|
74
|
+
// directive names "seed's own scrim" specifically, now that elevation is
|
|
75
|
+
// conveyed by the scrim alone (no more frosted-glass shadow/blur). Dark
|
|
76
|
+
// theme's `.55` is UNCHANGED: seed's own token system has no separate
|
|
77
|
+
// dark-theme scrim value to adopt, and `.55` wasn't found wrong -- only
|
|
78
|
+
// light had a sourced replacement value to move to.
|
|
79
|
+
bgScrim: { light: "rgba(0,0,0,0.45)", dark: "rgba(0,0,0,0.55)" },
|
|
80
|
+
// seed-design's `$color.bg.overlay` (BottomSheet/MenuSheet's own backdrop,
|
|
81
|
+
// crisp#911) — kept as its OWN token rather than folded into `bgScrim`
|
|
82
|
+
// above, even though #934 (landed after this one was added) moved
|
|
83
|
+
// `bgScrim`'s LIGHT value to the same `#00000074`: `bgOverlayStrong` is
|
|
84
|
+
// seed's literally theme-CONSTANT value (`color.yaml` gives the same hex
|
|
85
|
+
// for both `theme-light` and `theme-dark`), while `bgScrim` still differs
|
|
86
|
+
// by theme (`.45`/`.55`) -- the two tokens express different design
|
|
87
|
+
// decisions that currently coincide in one theme only.
|
|
88
|
+
bgOverlayStrong: { light: "#00000074", dark: "#00000074" },
|
|
89
|
+
// seed-design's `$color.stroke.neutral-muted` (MenuSheet group divider,
|
|
90
|
+
// crisp#911) -- a faint alpha hairline between grouped rows, theme-flipping
|
|
91
|
+
// (`color.yaml`: `static-black-alpha-300` light / `static-white-alpha-100`
|
|
92
|
+
// dark).
|
|
93
|
+
strokeGroupDivider: { light: "#00000010", dark: "#ffffff17" },
|
|
74
94
|
// The discard-confirm's description line (crisp#677, `Dialog`'s new
|
|
75
95
|
// `variant="alert"`) — measured live, both themes, over `popoverBg`: a
|
|
76
96
|
// theme-relative alpha rather than a flat gray, so it stays legible if
|
package/dist/esm/tokens/scale.js
CHANGED
|
@@ -27,13 +27,22 @@ export const space = {
|
|
|
27
27
|
// (inputs, rows, tiles); full is the pill.
|
|
28
28
|
export const radius = {
|
|
29
29
|
xs: "4px", // seed-design's $radius.r1 — small badge / checkbox box corner
|
|
30
|
-
|
|
30
|
+
// your-moon/crisp#919: was `7px` (Attio's own most-measured
|
|
31
|
+
// radius, 70 occurrences) -- now a pill, matching seed-design's own
|
|
32
|
+
// `chip.yaml` (`$radius.full` at every size, no size-based radius step at
|
|
33
|
+
// all, unlike Button/Badge). Same numeric value as `full` below, kept as
|
|
34
|
+
// its own named token because every one of this token's ~9 consumers
|
|
35
|
+
// (Chip, DataTable/RecordPage/RecordsTable/MultiSelect's own tag/mention
|
|
36
|
+
// rules, TableToolbar/DatePickerField's date-preset chips) reasons about
|
|
37
|
+
// "the chip corner" as its own concept, not literally "a generic pill".
|
|
38
|
+
chip: "999px",
|
|
31
39
|
sm: "6px", // chips, badges
|
|
32
40
|
md: "8px", // inputs, search box
|
|
33
41
|
lg: "9px", // rows, tiles, list items
|
|
34
42
|
xl: "12px", // overlay items (command rows)
|
|
35
43
|
"2xl": "16px", // overlay panels (command surface)
|
|
36
44
|
"3xl": "20px", // seed-design's $radius.r5 — Select's listbox corner (crisp/#867)
|
|
45
|
+
"4xl": "24px", // seed-design's $radius.r6 — BottomSheet's top corner radius (crisp/#911)
|
|
37
46
|
full: "999px", // pills
|
|
38
47
|
};
|
|
39
48
|
// Component sizing — recurring measured dimensions that are neither spacing nor
|
|
@@ -85,3 +94,36 @@ export const weight = {
|
|
|
85
94
|
semibold: "600",
|
|
86
95
|
bold: "700", // seed-design's font-weight-bold, for the seed-styled calendar
|
|
87
96
|
};
|
|
97
|
+
// Size-based letter-spacing ramp for DISPLAY/HEADING type (18px and up —
|
|
98
|
+
// `text.*` above tops out at 16px and stays untracked). crisp's five
|
|
99
|
+
// already-shipped headings tighten as they grow — authshell 20px/-0.4px (2%
|
|
100
|
+
// of size), objectsettings 24px/-0.48px (2%), richtext 32px/-0.6px (1.875%)
|
|
101
|
+
// — all Attio-measured (Inter) and left exactly as they are; this token
|
|
102
|
+
// never touches them, or the button (-0.07px) / table-cell (-0.14px)
|
|
103
|
+
// tracking, which are body sizes, not display type. (statcard's 28px/-0.4px,
|
|
104
|
+
// 1.43%, is a looser outlier — it sets tabular numerals, not prose — and is
|
|
105
|
+
// also left alone.)
|
|
106
|
+
//
|
|
107
|
+
// crisp#909: the study at docs/study/korean/ observed that Naver and Kakao
|
|
108
|
+
// both tighten tracking as type gets bigger, and crisp's OWN large type
|
|
109
|
+
// (EmptyState's 20px title, Dialog's 18px alert title, and any future
|
|
110
|
+
// heading) had no tracking at all — it read as default, not set. The study
|
|
111
|
+
// supplied the PATTERN only, never a number: Naver/Kakao's own values are
|
|
112
|
+
// Korean-specific and were not copied. The numbers below come from rendering
|
|
113
|
+
// real Mongolian Cyrillic ("Эрсдэлийн бүртгэл", "Хяналтын самбар") in VK Sans
|
|
114
|
+
// Display — crisp's own font override, see `font.sans` above — at every
|
|
115
|
+
// display size crisp uses today (18/20/24/26/28/32px), screenshotting six
|
|
116
|
+
// candidates per size (0%, 1%, 1.5%, 2%, 2.5%, 3% of the font-size) and
|
|
117
|
+
// checking for glyph collision. None collided even at 3%, so the pre-existing
|
|
118
|
+
// 2%-of-size rule the five Attio headings already happen to follow was kept
|
|
119
|
+
// as the ramp, rounded to the nearest 0.01px — it reads as deliberately set
|
|
120
|
+
// with headroom to spare, and keeps new display type visually consistent
|
|
121
|
+
// with the Attio-measured headings already shipped.
|
|
122
|
+
export const tracking = {
|
|
123
|
+
"18": "-0.36px",
|
|
124
|
+
"20": "-0.4px",
|
|
125
|
+
"24": "-0.48px",
|
|
126
|
+
"26": "-0.52px",
|
|
127
|
+
"28": "-0.56px",
|
|
128
|
+
"32": "-0.64px",
|
|
129
|
+
};
|
|
@@ -38,6 +38,8 @@ export const semantic = {
|
|
|
38
38
|
bgTileActive: "tileActive",
|
|
39
39
|
shadowTileActive: "tileActiveShadow",
|
|
40
40
|
bgScrim: "bgScrim",
|
|
41
|
+
bgOverlayStrong: "bgOverlayStrong",
|
|
42
|
+
strokeGroupDivider: "strokeGroupDivider",
|
|
41
43
|
fgAlertDescription: "fgAlertDescription",
|
|
42
44
|
bgOverlayFrost: "overlayFrost",
|
|
43
45
|
shadowOverlay1: "shadowOverlay1",
|
package/dist/fileupload.css
CHANGED
|
@@ -25,7 +25,9 @@
|
|
|
25
25
|
& .crisp-fileupload-remove:hover { color: var(--crisp-fg-primary); }
|
|
26
26
|
& .crisp-fileupload-remove:focus-visible { outline-color: var(--crisp-stroke-focus-ring); }
|
|
27
27
|
& .crisp-fileupload-errors { display: flex; flex-direction: column; gap: var(--crisp-space-0-5); margin: 0; padding: 0; list-style: none; }
|
|
28
|
-
/* out of flow while empty, so the always-mounted live region costs no
|
|
28
|
+
/* out of flow while empty, so the always-mounted live region costs no
|
|
29
|
+
layout: display:none would take it out of the accessibility tree, which
|
|
30
|
+
is the one thing it exists to be in */
|
|
29
31
|
& .crisp-fileupload-errors:empty { position: absolute; }
|
|
30
32
|
& .crisp-fileupload-error { color: var(--crisp-bg-danger); font-size: var(--crisp-text-md); line-height: 18px; }
|
|
31
33
|
}
|
|
@@ -26,7 +26,9 @@
|
|
|
26
26
|
& .crisp-fileupload-remove:hover { color: var(--crisp-fg-primary); }
|
|
27
27
|
& .crisp-fileupload-remove:focus-visible { outline-color: var(--crisp-stroke-focus-ring); }
|
|
28
28
|
& .crisp-fileupload-errors { display: flex; flex-direction: column; gap: var(--crisp-space-0-5); margin: 0; padding: 0; list-style: none; }
|
|
29
|
-
/* out of flow while empty, so the always-mounted live region costs no
|
|
29
|
+
/* out of flow while empty, so the always-mounted live region costs no
|
|
30
|
+
layout: display:none would take it out of the accessibility tree, which
|
|
31
|
+
is the one thing it exists to be in */
|
|
30
32
|
& .crisp-fileupload-errors:empty { position: absolute; }
|
|
31
33
|
& .crisp-fileupload-error { color: var(--crisp-bg-danger); font-size: var(--crisp-text-md); line-height: 18px; }
|
|
32
34
|
}
|
package/dist/input.css
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
.crisp-input {
|
|
2
|
-
position: relative; box-sizing: border-box; display: inline-flex; align-items: center; width: 100%; background: var(--crisp-bg-surface);
|
|
2
|
+
position: relative; box-sizing: border-box; display: inline-flex; align-items: center; width: 100%; background: var(--crisp-bg-surface); border: 1px solid var(--crisp-border-default); font-family: var(--crisp-font-sans); font-size: var(--crisp-text-lg); line-height: 20px;
|
|
3
3
|
&::after { content: ""; position: absolute; inset: 0; border-radius: inherit; border: 2px solid transparent; transition: border-color .1s ease; pointer-events: none; }
|
|
4
4
|
&:focus-within::after { border-color: var(--crisp-stroke-field-focus); }
|
|
5
5
|
& .crisp-input-value { box-sizing: border-box; flex: 1 1 auto; min-width: 0; width: 100%; align-self: stretch; margin: 0; border: none; background: none; outline: none; font: inherit; color: var(--crisp-fg-primary); }
|
|
@@ -10,8 +10,10 @@
|
|
|
10
10
|
primary-email field -- a tinted field, not just muted text, driven off
|
|
11
11
|
the native `readOnly` attribute. `ringField` (rgba(0,0,0,.06)) is an
|
|
12
12
|
exact match for the measured ring; the 2%-fgPrimary tint reuses the
|
|
13
|
-
same color-mix idiom every other soft crisp surface already uses.
|
|
14
|
-
|
|
13
|
+
same color-mix idiom every other soft crisp surface already uses.
|
|
14
|
+
`border-color: transparent` neutralises #910's new real rest border
|
|
15
|
+
so this measured ring isn't drawn twice. */
|
|
16
|
+
&[data-readonly] { background: color-mix(in srgb, var(--crisp-fg-primary) 2%, transparent); border-color: transparent; box-shadow: inset 0 0 0 1px var(--crisp-ring-field); }
|
|
15
17
|
& .crisp-input-suffix { flex-shrink: 0; margin-inline-start: var(--crisp-space-1); }
|
|
16
18
|
}
|
|
17
19
|
.crisp-input--size_sm {
|
|
@@ -23,7 +25,7 @@
|
|
|
23
25
|
& .crisp-input-value { padding-inline: var(--crisp-space-2-5); }
|
|
24
26
|
}
|
|
25
27
|
.crisp-input--size_large {
|
|
26
|
-
height: 36px; border-radius:
|
|
28
|
+
height: 36px; border-radius: var(--crisp-radius-xl);
|
|
27
29
|
& .crisp-input-value { padding-inline: var(--crisp-space-3); }
|
|
28
30
|
}
|
|
29
31
|
.crisp-input--invalid_true {
|
package/dist/input.layered.css
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
@layer crisp-components {
|
|
2
2
|
.crisp-input {
|
|
3
|
-
position: relative; box-sizing: border-box; display: inline-flex; align-items: center; width: 100%; background: var(--crisp-bg-surface);
|
|
3
|
+
position: relative; box-sizing: border-box; display: inline-flex; align-items: center; width: 100%; background: var(--crisp-bg-surface); border: 1px solid var(--crisp-border-default); font-family: var(--crisp-font-sans); font-size: var(--crisp-text-lg); line-height: 20px;
|
|
4
4
|
&::after { content: ""; position: absolute; inset: 0; border-radius: inherit; border: 2px solid transparent; transition: border-color .1s ease; pointer-events: none; }
|
|
5
5
|
&:focus-within::after { border-color: var(--crisp-stroke-field-focus); }
|
|
6
6
|
& .crisp-input-value { box-sizing: border-box; flex: 1 1 auto; min-width: 0; width: 100%; align-self: stretch; margin: 0; border: none; background: none; outline: none; font: inherit; color: var(--crisp-fg-primary); }
|
|
@@ -11,8 +11,10 @@
|
|
|
11
11
|
primary-email field -- a tinted field, not just muted text, driven off
|
|
12
12
|
the native `readOnly` attribute. `ringField` (rgba(0,0,0,.06)) is an
|
|
13
13
|
exact match for the measured ring; the 2%-fgPrimary tint reuses the
|
|
14
|
-
same color-mix idiom every other soft crisp surface already uses.
|
|
15
|
-
|
|
14
|
+
same color-mix idiom every other soft crisp surface already uses.
|
|
15
|
+
`border-color: transparent` neutralises #910's new real rest border
|
|
16
|
+
so this measured ring isn't drawn twice. */
|
|
17
|
+
&[data-readonly] { background: color-mix(in srgb, var(--crisp-fg-primary) 2%, transparent); border-color: transparent; box-shadow: inset 0 0 0 1px var(--crisp-ring-field); }
|
|
16
18
|
& .crisp-input-suffix { flex-shrink: 0; margin-inline-start: var(--crisp-space-1); }
|
|
17
19
|
}
|
|
18
20
|
.crisp-input--size_sm {
|
|
@@ -24,7 +26,7 @@
|
|
|
24
26
|
& .crisp-input-value { padding-inline: var(--crisp-space-2-5); }
|
|
25
27
|
}
|
|
26
28
|
.crisp-input--size_large {
|
|
27
|
-
height: 36px; border-radius:
|
|
29
|
+
height: 36px; border-radius: var(--crisp-radius-xl);
|
|
28
30
|
& .crisp-input-value { padding-inline: var(--crisp-space-3); }
|
|
29
31
|
}
|
|
30
32
|
.crisp-input--invalid_true {
|
package/dist/listpage.css
CHANGED
|
@@ -5,7 +5,9 @@
|
|
|
5
5
|
& .crisp-listpage-count { margin-inline-start: auto; font-size: var(--crisp-text-md); color: var(--crisp-fg-muted); white-space: nowrap; }
|
|
6
6
|
/* #641: trailing (View settings / New) has to reach the toolbar's own
|
|
7
7
|
inline-end edge on its own -- the only thing pushing it there used
|
|
8
|
-
to be .crisp-listpage-count's margin-inline-start: auto, which a
|
|
8
|
+
to be .crisp-listpage-count's margin-inline-start: auto, which a
|
|
9
|
+
consumer with no count badge (TasksTable, measured live: trailing
|
|
10
|
+
sat right after leading, nowhere near the row's own right edge)
|
|
9
11
|
never renders. A bare margin-inline-start: auto here is safely
|
|
10
12
|
overridden by the more specific rule right below whenever count IS
|
|
11
13
|
present, so the two cases don't fight -- count's own auto margin
|
|
@@ -6,7 +6,9 @@
|
|
|
6
6
|
& .crisp-listpage-count { margin-inline-start: auto; font-size: var(--crisp-text-md); color: var(--crisp-fg-muted); white-space: nowrap; }
|
|
7
7
|
/* #641: trailing (View settings / New) has to reach the toolbar's own
|
|
8
8
|
inline-end edge on its own -- the only thing pushing it there used
|
|
9
|
-
to be .crisp-listpage-count's margin-inline-start: auto, which a
|
|
9
|
+
to be .crisp-listpage-count's margin-inline-start: auto, which a
|
|
10
|
+
consumer with no count badge (TasksTable, measured live: trailing
|
|
11
|
+
sat right after leading, nowhere near the row's own right edge)
|
|
10
12
|
never renders. A bare margin-inline-start: auto here is safely
|
|
11
13
|
overridden by the more specific rule right below whenever count IS
|
|
12
14
|
present, so the two cases don't fight -- count's own auto margin
|