@crisp-ui-kit/crisp 0.57.2 → 0.57.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assigneepicker.css +13 -0
- package/dist/assigneepicker.d.ts +7 -0
- package/dist/assigneepicker.layered.css +15 -0
- package/dist/button.css +3 -0
- package/dist/button.d.ts +2 -2
- package/dist/button.layered.css +3 -0
- package/dist/chip.css +1 -1
- package/dist/chip.layered.css +1 -1
- package/dist/cjs/components/breadcrumb/breadcrumb.js +1 -1
- package/dist/cjs/components/button/button.recipe.js +33 -0
- package/dist/cjs/components/chip/chip.recipe.js +3 -1
- package/dist/cjs/components/datatable/datatable.js +6 -3
- package/dist/cjs/components/datatable/datatable.recipe.js +45 -6
- package/dist/cjs/components/datepickerfield/datepickerfield.js +2 -2
- package/dist/cjs/components/datepickerfield/datepickerfield.recipe.js +16 -3
- package/dist/cjs/components/menu/menu.js +65 -9
- package/dist/cjs/components/multiselect/multiselect.js +3 -0
- package/dist/cjs/components/multiselect/multiselect.recipe.js +18 -0
- package/dist/cjs/components/select/select.js +2 -2
- package/dist/cjs/components/select/select.recipe.js +19 -0
- package/dist/cjs/components/table/table.recipe.js +7 -1
- package/dist/cjs/core/floating-portal.js +7 -4
- package/dist/cjs/core/index.js +2 -1
- package/dist/cjs/core/place-floating.js +38 -0
- package/dist/cjs/index.js +8 -0
- package/dist/cjs/product/assigneepicker/assigneepicker.js +127 -0
- package/dist/cjs/product/assigneepicker/assigneepicker.recipe.js +36 -0
- package/dist/cjs/product/assigneepicker/index.js +7 -0
- package/dist/cjs/product/inbox/inbox.js +11 -3
- package/dist/cjs/product/notesgallery/index.js +7 -0
- package/dist/cjs/product/notesgallery/notesgallery.js +80 -0
- package/dist/cjs/product/notesgallery/notesgallery.recipe.js +46 -0
- package/dist/cjs/product/objectpagemenu/index.js +17 -0
- package/dist/cjs/product/objectpagemenu/objectpagemenu.js +26 -0
- package/dist/cjs/product/objectpagemenu/objectpagemenu.recipe.js +30 -0
- package/dist/cjs/product/objectsettings/objectsettings.js +107 -14
- package/dist/cjs/product/recordpage/index.js +2 -1
- package/dist/cjs/product/recordpage/recordpage.js +112 -7
- package/dist/cjs/product/recordpage/recordpage.recipe.js +63 -0
- package/dist/cjs/product/recordpicker/index.js +7 -0
- package/dist/cjs/product/recordpicker/recordpicker.js +100 -0
- package/dist/cjs/product/recordpicker/recordpicker.recipe.js +36 -0
- package/dist/cjs/product/savedviews/index.js +2 -1
- package/dist/cjs/product/savedviews/savedviews.js +22 -0
- package/dist/cjs/product/savedviews/savedviews.recipe.js +47 -0
- package/dist/cjs/product/share/index.js +7 -0
- package/dist/cjs/product/share/share.js +179 -0
- package/dist/cjs/product/share/share.recipe.js +114 -0
- package/dist/cjs/product/shortcutssheet/index.js +17 -0
- package/dist/cjs/product/shortcutssheet/shortcutssheet.js +125 -0
- package/dist/cjs/product/shortcutssheet/shortcutssheet.recipe.js +50 -0
- package/dist/cjs/product/taskeditpopover/index.js +7 -0
- package/dist/cjs/product/taskeditpopover/taskeditpopover.js +159 -0
- package/dist/cjs/product/taskeditpopover/taskeditpopover.recipe.js +68 -0
- package/dist/cjs/product/workspaceswitchermenu/index.js +17 -0
- package/dist/cjs/product/workspaceswitchermenu/workspaceswitchermenu.js +38 -0
- package/dist/cjs/product/workspaceswitchermenu/workspaceswitchermenu.recipe.js +34 -0
- package/dist/cjs/tokens/palette.js +27 -0
- package/dist/cjs/tokens/semantic.js +20 -0
- package/dist/datatable.css +50 -11
- package/dist/datatable.layered.css +50 -11
- package/dist/datepickerfield.css +8 -2
- package/dist/datepickerfield.d.ts +2 -2
- package/dist/datepickerfield.layered.css +8 -2
- package/dist/esm/components/breadcrumb/breadcrumb.js +1 -1
- package/dist/esm/components/button/button.recipe.js +33 -0
- package/dist/esm/components/chip/chip.recipe.js +3 -1
- package/dist/esm/components/datatable/datatable.js +6 -3
- package/dist/esm/components/datatable/datatable.recipe.js +45 -6
- package/dist/esm/components/datepickerfield/datepickerfield.js +2 -2
- package/dist/esm/components/datepickerfield/datepickerfield.recipe.js +16 -3
- package/dist/esm/components/menu/menu.js +64 -8
- package/dist/esm/components/multiselect/multiselect.js +3 -0
- package/dist/esm/components/multiselect/multiselect.recipe.js +18 -0
- package/dist/esm/components/select/select.js +2 -2
- package/dist/esm/components/select/select.recipe.js +19 -0
- package/dist/esm/components/table/table.recipe.js +7 -1
- package/dist/esm/core/floating-portal.js +7 -4
- package/dist/esm/core/index.js +1 -1
- package/dist/esm/core/place-floating.js +37 -0
- package/dist/esm/index.js +8 -0
- package/dist/esm/product/assigneepicker/assigneepicker.js +91 -0
- package/dist/esm/product/assigneepicker/assigneepicker.recipe.js +33 -0
- package/dist/esm/product/assigneepicker/index.js +2 -0
- package/dist/esm/product/inbox/inbox.js +11 -3
- package/dist/esm/product/notesgallery/index.js +2 -0
- package/dist/esm/product/notesgallery/notesgallery.js +44 -0
- package/dist/esm/product/notesgallery/notesgallery.recipe.js +43 -0
- package/dist/esm/product/objectpagemenu/index.js +1 -0
- package/dist/esm/product/objectpagemenu/objectpagemenu.js +23 -0
- package/dist/esm/product/objectpagemenu/objectpagemenu.recipe.js +27 -0
- package/dist/esm/product/objectsettings/objectsettings.js +106 -13
- package/dist/esm/product/recordpage/index.js +1 -1
- package/dist/esm/product/recordpage/recordpage.js +112 -8
- package/dist/esm/product/recordpage/recordpage.recipe.js +63 -0
- package/dist/esm/product/recordpicker/index.js +2 -0
- package/dist/esm/product/recordpicker/recordpicker.js +64 -0
- package/dist/esm/product/recordpicker/recordpicker.recipe.js +33 -0
- package/dist/esm/product/savedviews/index.js +1 -1
- package/dist/esm/product/savedviews/savedviews.js +22 -1
- package/dist/esm/product/savedviews/savedviews.recipe.js +47 -0
- package/dist/esm/product/share/index.js +2 -0
- package/dist/esm/product/share/share.js +143 -0
- package/dist/esm/product/share/share.recipe.js +111 -0
- package/dist/esm/product/shortcutssheet/index.js +1 -0
- package/dist/esm/product/shortcutssheet/shortcutssheet.js +88 -0
- package/dist/esm/product/shortcutssheet/shortcutssheet.recipe.js +47 -0
- package/dist/esm/product/taskeditpopover/index.js +2 -0
- package/dist/esm/product/taskeditpopover/taskeditpopover.js +123 -0
- package/dist/esm/product/taskeditpopover/taskeditpopover.recipe.js +65 -0
- package/dist/esm/product/workspaceswitchermenu/index.js +1 -0
- package/dist/esm/product/workspaceswitchermenu/workspaceswitchermenu.js +35 -0
- package/dist/esm/product/workspaceswitchermenu/workspaceswitchermenu.recipe.js +31 -0
- package/dist/esm/tokens/palette.js +27 -0
- package/dist/esm/tokens/semantic.js +20 -0
- package/dist/multiselect.css +18 -0
- package/dist/multiselect.layered.css +18 -0
- package/dist/native/tokens.d.ts +1 -1
- package/dist/native/tokens.js +56 -4
- package/dist/notesgallery.css +15 -0
- package/dist/notesgallery.d.ts +7 -0
- package/dist/notesgallery.layered.css +17 -0
- package/dist/objectpagemenu.css +3 -0
- package/dist/objectpagemenu.d.ts +7 -0
- package/dist/objectpagemenu.layered.css +5 -0
- package/dist/recordpage.css +59 -0
- package/dist/recordpage.layered.css +59 -0
- package/dist/recordpicker.css +13 -0
- package/dist/recordpicker.d.ts +7 -0
- package/dist/recordpicker.layered.css +15 -0
- package/dist/savedviews.css +47 -0
- package/dist/savedviews.layered.css +47 -0
- package/dist/select.css +19 -0
- package/dist/select.layered.css +19 -0
- package/dist/share.css +84 -0
- package/dist/share.d.ts +7 -0
- package/dist/share.layered.css +86 -0
- package/dist/shortcutssheet.css +24 -0
- package/dist/shortcutssheet.d.ts +7 -0
- package/dist/shortcutssheet.layered.css +26 -0
- package/dist/styles.css +438 -16
- package/dist/styles.layered.css +438 -16
- package/dist/table.css +7 -2
- package/dist/table.layered.css +7 -2
- package/dist/taskeditpopover.css +36 -0
- package/dist/taskeditpopover.d.ts +7 -0
- package/dist/taskeditpopover.layered.css +38 -0
- package/dist/tokens.css +26 -0
- package/dist/tokens.json +56 -4
- package/dist/types/components/breadcrumb/breadcrumb.d.ts +9 -1
- package/dist/types/components/button/button.d.ts +2 -0
- package/dist/types/components/button/button.recipe.d.ts +12 -0
- package/dist/types/components/chip/chip.recipe.d.ts +1 -1
- package/dist/types/components/datatable/datatable.d.ts +8 -0
- package/dist/types/components/datepickerfield/datepickerfield.d.ts +4 -1
- package/dist/types/components/datepickerfield/datepickerfield.recipe.d.ts +18 -3
- package/dist/types/components/menu/menu.d.ts +50 -7
- package/dist/types/components/multiselect/multiselect.d.ts +9 -0
- package/dist/types/components/select/select.d.ts +10 -0
- package/dist/types/core/floating-portal.d.ts +6 -5
- package/dist/types/core/index.d.ts +1 -1
- package/dist/types/core/place-floating.d.ts +37 -0
- package/dist/types/index.d.ts +8 -0
- package/dist/types/product/assigneepicker/assigneepicker.d.ts +36 -0
- package/dist/types/product/assigneepicker/assigneepicker.recipe.d.ts +3 -0
- package/dist/types/product/assigneepicker/index.d.ts +2 -0
- package/dist/types/product/inbox/inbox.d.ts +13 -1
- package/dist/types/product/notesgallery/index.d.ts +2 -0
- package/dist/types/product/notesgallery/notesgallery.d.ts +54 -0
- package/dist/types/product/notesgallery/notesgallery.recipe.d.ts +3 -0
- package/dist/types/product/objectpagemenu/index.d.ts +1 -0
- package/dist/types/product/objectpagemenu/objectpagemenu.d.ts +40 -0
- package/dist/types/product/objectpagemenu/objectpagemenu.recipe.d.ts +17 -0
- package/dist/types/product/objectsettings/objectsettings.d.ts +71 -15
- package/dist/types/product/recordpage/index.d.ts +1 -1
- package/dist/types/product/recordpage/recordpage.d.ts +142 -1
- package/dist/types/product/recordpicker/index.d.ts +2 -0
- package/dist/types/product/recordpicker/recordpicker.d.ts +38 -0
- package/dist/types/product/recordpicker/recordpicker.recipe.d.ts +3 -0
- package/dist/types/product/savedviews/index.d.ts +1 -1
- package/dist/types/product/savedviews/savedviews.d.ts +36 -0
- package/dist/types/product/share/index.d.ts +2 -0
- package/dist/types/product/share/share.d.ts +85 -0
- package/dist/types/product/share/share.recipe.d.ts +3 -0
- package/dist/types/product/shortcutssheet/index.d.ts +1 -0
- package/dist/types/product/shortcutssheet/shortcutssheet.d.ts +49 -0
- package/dist/types/product/shortcutssheet/shortcutssheet.recipe.d.ts +3 -0
- package/dist/types/product/taskeditpopover/index.d.ts +2 -0
- package/dist/types/product/taskeditpopover/taskeditpopover.d.ts +67 -0
- package/dist/types/product/taskeditpopover/taskeditpopover.recipe.d.ts +3 -0
- package/dist/types/product/workspaceswitchermenu/index.d.ts +1 -0
- package/dist/types/product/workspaceswitchermenu/workspaceswitchermenu.d.ts +54 -0
- package/dist/types/product/workspaceswitchermenu/workspaceswitchermenu.recipe.d.ts +19 -0
- package/dist/types/tokens/contract.d.ts +13 -0
- package/dist/types/tokens/palette.d.ts +52 -0
- package/dist/types/tokens/semantic.d.ts +20 -0
- package/dist/workspaceswitchermenu.css +4 -0
- package/dist/workspaceswitchermenu.d.ts +7 -0
- package/dist/workspaceswitchermenu.layered.css +6 -0
- package/package.json +41 -1
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { CornerDownLeft } from "lucide-react";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { Drawer, DrawerBody } from "../../components/drawer/index.js";
|
|
5
|
+
import { Kbd } from "../../components/kbd/index.js";
|
|
6
|
+
import { cx, useCrispMessage } from "../../core/index.js";
|
|
7
|
+
/** The physical Enter/Return key's printed glyph — not a reading-direction
|
|
8
|
+
* indicator (a real keyboard prints the same ↵ shape under any locale), so
|
|
9
|
+
* unlike `LogOut` elsewhere it is deliberately left unmirrored (excused in
|
|
10
|
+
* `build/spec-check.mjs`'s own RTL icon-mirroring ALLOW list). */
|
|
11
|
+
const enterKey = () => _jsx(CornerDownLeft, { size: 16, strokeWidth: 1.75 });
|
|
12
|
+
/**
|
|
13
|
+
* Attio's own canonical keyboard-shortcut list (crisp#615), live-measured
|
|
14
|
+
* off app.attio.com/techpartners' "Keyboard shortcuts" panel (Help →
|
|
15
|
+
* "Shortcuts") — 34 rows across 4 sections. The issue's own quoted table
|
|
16
|
+
* collapsed a few of these ("Navigate to the cell above/right/below/left",
|
|
17
|
+
* "Go to next/previous cell") into one row each; the live panel renders
|
|
18
|
+
* them as separate rows, which is what this list follows.
|
|
19
|
+
*/
|
|
20
|
+
export const defaultShortcutSections = [
|
|
21
|
+
{
|
|
22
|
+
label: "General",
|
|
23
|
+
rows: [
|
|
24
|
+
{ description: "Open quick actions", steps: [["⌘", "K"]] },
|
|
25
|
+
{ description: "Open search", steps: [["/"]] },
|
|
26
|
+
{ description: "Create a new note", steps: [["N"]] },
|
|
27
|
+
{ description: "Create a new entry", steps: [["E"]] },
|
|
28
|
+
{ description: "Create a new task", steps: [["T"]] },
|
|
29
|
+
{ description: "Create a new email", steps: [["C"]] },
|
|
30
|
+
{ description: "Go to home page", steps: [["G"], ["H"]] },
|
|
31
|
+
{ description: "Go to tasks page", steps: [["G"], ["T"]] },
|
|
32
|
+
{ description: "Go to all notes page", steps: [["G"], ["N"]] },
|
|
33
|
+
{ description: "Go to all people page", steps: [["G"], ["P"]] },
|
|
34
|
+
{ description: "Go to all companies page", steps: [["G"], ["C"]] },
|
|
35
|
+
{ description: "Go to email drafts page", steps: [["G"], ["E"]] },
|
|
36
|
+
{ description: "Toggle sidebar", steps: [["⌘", "."]] },
|
|
37
|
+
{ description: "Navigate up", steps: [["↑"]] },
|
|
38
|
+
{ description: "Navigate down", steps: [["↓"]] },
|
|
39
|
+
],
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
label: "Record page",
|
|
43
|
+
rows: [
|
|
44
|
+
{ description: "Go to next tab", steps: [["⌥", "→"]] },
|
|
45
|
+
{ description: "Go to previous tab", steps: [["⌥", "←"]] },
|
|
46
|
+
],
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
label: "Table view",
|
|
50
|
+
rows: [
|
|
51
|
+
{ description: "Navigate to the cell above", steps: [["↑"]] },
|
|
52
|
+
{ description: "Navigate to the cell to the right", steps: [["→"]] },
|
|
53
|
+
{ description: "Navigate to the cell below", steps: [["↓"]] },
|
|
54
|
+
{ description: "Navigate to the cell to the left", steps: [["←"]] },
|
|
55
|
+
{ description: "Jump to bottom", steps: [["⌘", "↓"]] },
|
|
56
|
+
{ description: "Jump to top", steps: [["⌘", "↑"]] },
|
|
57
|
+
{ description: "Select/deselect current row", steps: [["⇧", "␣"]] },
|
|
58
|
+
{ description: "Group select rows up", steps: [["⇧", "↑"]] },
|
|
59
|
+
{ description: "Group select rows down", steps: [["⇧", "↓"]] },
|
|
60
|
+
{ description: "Group select to top", steps: [["⌘", "⇧", "↑"]] },
|
|
61
|
+
{ description: "Group select to bottom", steps: [["⌘", "⇧", "↓"]] },
|
|
62
|
+
{
|
|
63
|
+
description: "Open record page for current entry",
|
|
64
|
+
steps: [["⌘", enterKey()]],
|
|
65
|
+
},
|
|
66
|
+
{ description: "Select/deselect all rows", steps: [["⌘", "A"]] },
|
|
67
|
+
{ description: "Go to next cell", steps: [["⇥"]] },
|
|
68
|
+
{ description: "Go to previous cell", steps: [["⇧", "⇥"]] },
|
|
69
|
+
{ description: "Edit cell / go to next cell", steps: [[enterKey()]] },
|
|
70
|
+
],
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
label: "Help",
|
|
74
|
+
rows: [{ description: "Open help drawer", steps: [["?"]] }],
|
|
75
|
+
},
|
|
76
|
+
];
|
|
77
|
+
/**
|
|
78
|
+
* Attio's "Keyboard shortcuts" panel (crisp#615, parity row P-106) — a
|
|
79
|
+
* `Drawer` narrowed to the measured 360px (Drawer's own default is 420px),
|
|
80
|
+
* listing `sections` grouped with a hairline-free 10px row rhythm and a
|
|
81
|
+
* 20px gap before each new section label.
|
|
82
|
+
*/
|
|
83
|
+
export function ShortcutsSheet({ open, onOpenChange, sections = defaultShortcutSections, title: titleProp, thenLabel: thenLabelProp, closeLabel, className, ...rest }) {
|
|
84
|
+
const title = useCrispMessage(titleProp, "Keyboard shortcuts");
|
|
85
|
+
const thenLabel = useCrispMessage(thenLabelProp, "then");
|
|
86
|
+
return (_jsx(Drawer, { open: open, onOpenChange: onOpenChange, title: title, closeLabel: closeLabel, className: cx("crisp-shortcutssheet-panel", className), ...rest, children: _jsx(DrawerBody, { children: _jsx("div", { className: "crisp-shortcutssheet-body", children: sections.map((section) => (_jsxs(React.Fragment, { children: [_jsx("div", { className: "crisp-shortcutssheet-section-label", children: section.label }), section.rows.map((row) => (_jsxs("div", { className: "crisp-shortcutssheet-row", children: [_jsx("div", { className: "crisp-shortcutssheet-desc", children: row.description }), _jsx("div", { className: "crisp-shortcutssheet-keys", children: row.steps.map((step, stepIndex) => (_jsxs(React.Fragment, { children: [stepIndex > 0 && (_jsx("span", { className: "crisp-shortcutssheet-then", children: thenLabel })), step.map((glyph, glyphIndex) => (_jsx(Kbd, { children: glyph }, glyphIndex)))] }, stepIndex))) })] }, String(row.description))))] }, String(section.label)))) }) }) }));
|
|
87
|
+
}
|
|
88
|
+
ShortcutsSheet.displayName = "ShortcutsSheet";
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { defineRecipe, resolveRecipe } from "../../core/index.js";
|
|
2
|
+
import { contract as c } from "../../tokens/index.js";
|
|
3
|
+
// Attio's "Keyboard shortcuts" panel (crisp#615), live-measured off
|
|
4
|
+
// app.attio.com/techpartners — a second overlay reached from Help →
|
|
5
|
+
// "Shortcuts": 360px wide (narrower than Drawer's own 420px default, hence
|
|
6
|
+
// the width override below), 16px body padding (already Drawer's own
|
|
7
|
+
// `crisp-drawer-body`, unchanged), rows 10px apart, a 20px gap before a new
|
|
8
|
+
// section (expressed as the flex column's own 10px `gap` plus another 10px
|
|
9
|
+
// on every section label but the first, rather than a second gap value),
|
|
10
|
+
// section labels 12px/500, row descriptions 14px/500 — both the SAME
|
|
11
|
+
// `color-mix(fgPrimary, 63%)` muted-text tint RecordPage's own notes already
|
|
12
|
+
// established (crisp#871), and the "then" connector between a sequential
|
|
13
|
+
// shortcut's two steps at 11px/500 in Kbd's own 50%-fgPrimary tint (the
|
|
14
|
+
// same de-emphasis, on the same kind of supporting glyph).
|
|
15
|
+
const config = defineRecipe({
|
|
16
|
+
name: "shortcutssheet",
|
|
17
|
+
base: "",
|
|
18
|
+
globalCss: `
|
|
19
|
+
/* Overrides Drawer's own 420px default (specificity: .crisp-drawer
|
|
20
|
+
.crisp-drawer-content.crisp-shortcutssheet-panel, higher than
|
|
21
|
+
Drawer's .crisp-drawer .crisp-drawer-content regardless of which
|
|
22
|
+
chunk of dist/styles.css concatenates first). */
|
|
23
|
+
.crisp-drawer .crisp-drawer-content.crisp-shortcutssheet-panel { width: 360px; }
|
|
24
|
+
|
|
25
|
+
.crisp-shortcutssheet-body { display: flex; flex-direction: column; gap: ${c.space["2.5"]}; }
|
|
26
|
+
.crisp-shortcutssheet-section-label {
|
|
27
|
+
font-size: ${c.text.sm}; font-weight: ${c.weight.medium};
|
|
28
|
+
color: color-mix(in srgb, ${c.fgPrimary} 63%, transparent);
|
|
29
|
+
}
|
|
30
|
+
.crisp-shortcutssheet-section-label:not(:first-child) { margin-top: ${c.space["2.5"]}; }
|
|
31
|
+
.crisp-shortcutssheet-row { display: flex; align-items: center; justify-content: space-between; gap: ${c.space["1"]}; }
|
|
32
|
+
.crisp-shortcutssheet-desc {
|
|
33
|
+
font-size: ${c.text.lg}; font-weight: ${c.weight.medium};
|
|
34
|
+
color: color-mix(in srgb, ${c.fgPrimary} 63%, transparent);
|
|
35
|
+
}
|
|
36
|
+
.crisp-shortcutssheet-keys { display: flex; align-items: center; gap: ${c.space["1"]}; flex-shrink: 0; }
|
|
37
|
+
.crisp-shortcutssheet-then {
|
|
38
|
+
font-size: ${c.text.xs}; font-weight: ${c.weight.medium};
|
|
39
|
+
color: color-mix(in srgb, ${c.fgPrimary} 50%, transparent);
|
|
40
|
+
}
|
|
41
|
+
`,
|
|
42
|
+
variants: {},
|
|
43
|
+
defaultVariants: {},
|
|
44
|
+
compoundVariants: [],
|
|
45
|
+
});
|
|
46
|
+
export const shortcutssheetRecipe = resolveRecipe(config);
|
|
47
|
+
export default config;
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { CalendarDays, CircleCheckBig, MoreHorizontal, X } from "lucide-react";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { Popover } from "../../components/popover/index.js";
|
|
5
|
+
import { FloatingPortal, isInsideLaterFloatingLayer, placeFloating, registerFloatingLayer, useCrispMessage, } from "../../core/index.js";
|
|
6
|
+
import { AssigneePicker, } from "../assigneepicker/assigneepicker.js";
|
|
7
|
+
import { RecordPicker, } from "../recordpicker/recordpicker.js";
|
|
8
|
+
import { taskeditpopoverRecipe } from "./taskeditpopover.recipe.js";
|
|
9
|
+
/**
|
|
10
|
+
* A task's "Edit task" popover -- measured live on app.attio.com's Tasks
|
|
11
|
+
* page: a checkbox + editable title, then a chip row (due date / assigned /
|
|
12
|
+
* add record), a "more actions" kebab, and Cancel/Save. Composes
|
|
13
|
+
* `AssigneePicker` and `RecordPicker` (both crisp#626) behind their own
|
|
14
|
+
* chips, using the same core floating primitives `Popover` itself is built
|
|
15
|
+
* on rather than `Popover` directly (its content hardcodes a 360px max
|
|
16
|
+
* width, well under this popover's own measured 800px).
|
|
17
|
+
*/
|
|
18
|
+
export function TaskEditPopover({ trigger, open: openProp, defaultOpen, onOpenChange, title, onTitleChange, done = false, onToggleDone, dueDateLabel, dueDatePicker, assignee = null, users = [], onAssigneeChange, linkedRecordCount = 0, recordCandidates = [], recordCandidatesLoading, onRecordQueryChange, onSelectRecord, menu, onCancel, onSave, headingLabel: headingLabelProp, closeLabel: closeLabelProp, dueDatePlaceholder: dueDatePlaceholderProp, assignLabel: assignLabelProp, assignedToLabel: assignedToLabelProp, addRecordLabel: addRecordLabelProp, linkedRecordLabel: linkedRecordLabelProp, moreActionsLabel: moreActionsLabelProp, cancelLabel: cancelLabelProp, cancelKeyLabel: cancelKeyLabelProp, saveLabel: saveLabelProp, className, }) {
|
|
19
|
+
const headingLabel = useCrispMessage(headingLabelProp, "Edit task");
|
|
20
|
+
const closeLabel = useCrispMessage(closeLabelProp, "Close");
|
|
21
|
+
const dueDatePlaceholder = useCrispMessage(dueDatePlaceholderProp, "Set due date");
|
|
22
|
+
const assignLabel = useCrispMessage(assignLabelProp, "Assign");
|
|
23
|
+
const assignedToLabel = useCrispMessage(assignedToLabelProp, "Assigned to");
|
|
24
|
+
const addRecordLabel = useCrispMessage(addRecordLabelProp, "Add record");
|
|
25
|
+
const linkedRecordLabel = useCrispMessage(linkedRecordLabelProp, linkedRecordCount === 1
|
|
26
|
+
? "1 linked record"
|
|
27
|
+
: `${linkedRecordCount} linked records`);
|
|
28
|
+
const moreActionsLabel = useCrispMessage(moreActionsLabelProp, "More actions");
|
|
29
|
+
const cancelLabel = useCrispMessage(cancelLabelProp, "Cancel");
|
|
30
|
+
const cancelKeyLabel = useCrispMessage(cancelKeyLabelProp, "Esc");
|
|
31
|
+
const saveLabel = useCrispMessage(saveLabelProp, "Save");
|
|
32
|
+
const surfaceId = React.useId();
|
|
33
|
+
const [internalOpen, setInternalOpen] = React.useState(defaultOpen ?? false);
|
|
34
|
+
const open = openProp ?? internalOpen;
|
|
35
|
+
const rootRef = React.useRef(null);
|
|
36
|
+
const triggerRef = React.useRef(null);
|
|
37
|
+
const contentRef = React.useRef(null);
|
|
38
|
+
const [pos, setPos] = React.useState({});
|
|
39
|
+
const setOpen = React.useCallback((next) => {
|
|
40
|
+
if (openProp === undefined)
|
|
41
|
+
setInternalOpen(next);
|
|
42
|
+
onOpenChange?.(next);
|
|
43
|
+
}, [openProp, onOpenChange]);
|
|
44
|
+
const place = () => {
|
|
45
|
+
const el = triggerRef.current;
|
|
46
|
+
if (!el)
|
|
47
|
+
return;
|
|
48
|
+
const { style } = placeFloating({
|
|
49
|
+
rect: el.getBoundingClientRect(),
|
|
50
|
+
align: "start",
|
|
51
|
+
gap: 6,
|
|
52
|
+
flipBelow: 300,
|
|
53
|
+
anchor: el,
|
|
54
|
+
});
|
|
55
|
+
setPos(style);
|
|
56
|
+
};
|
|
57
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies: reposition each time it opens
|
|
58
|
+
React.useLayoutEffect(() => {
|
|
59
|
+
if (open)
|
|
60
|
+
place();
|
|
61
|
+
}, [open]);
|
|
62
|
+
React.useEffect(() => {
|
|
63
|
+
if (!open || !contentRef.current)
|
|
64
|
+
return;
|
|
65
|
+
return registerFloatingLayer(contentRef.current);
|
|
66
|
+
}, [open]);
|
|
67
|
+
React.useEffect(() => {
|
|
68
|
+
if (!open)
|
|
69
|
+
return;
|
|
70
|
+
const onDown = (e) => {
|
|
71
|
+
const target = e.target;
|
|
72
|
+
if (rootRef.current?.contains(target))
|
|
73
|
+
return;
|
|
74
|
+
if (contentRef.current?.contains(target))
|
|
75
|
+
return;
|
|
76
|
+
if (isInsideLaterFloatingLayer(target, contentRef.current))
|
|
77
|
+
return;
|
|
78
|
+
setOpen(false);
|
|
79
|
+
onCancel?.();
|
|
80
|
+
};
|
|
81
|
+
document.addEventListener("pointerdown", onDown);
|
|
82
|
+
return () => document.removeEventListener("pointerdown", onDown);
|
|
83
|
+
}, [open, setOpen, onCancel]);
|
|
84
|
+
const cancel = () => {
|
|
85
|
+
setOpen(false);
|
|
86
|
+
onCancel?.();
|
|
87
|
+
};
|
|
88
|
+
const save = () => {
|
|
89
|
+
setOpen(false);
|
|
90
|
+
onSave?.();
|
|
91
|
+
};
|
|
92
|
+
const triggerEl = React.cloneElement(trigger, {
|
|
93
|
+
ref: triggerRef,
|
|
94
|
+
"aria-haspopup": "dialog",
|
|
95
|
+
"aria-expanded": open,
|
|
96
|
+
"aria-controls": open ? surfaceId : undefined,
|
|
97
|
+
onClick: (e) => {
|
|
98
|
+
trigger.props.onClick?.(e);
|
|
99
|
+
setOpen(!open);
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
return (_jsxs("div", { ref: rootRef, style: { display: "contents" }, children: [triggerEl, open && (_jsx(FloatingPortal, { sourceRef: rootRef, children: _jsxs("div", { ref: contentRef, id: surfaceId, role: "dialog", "aria-label": headingLabel, className: taskeditpopoverRecipe({ className }), style: pos, onKeyDown: (e) => {
|
|
103
|
+
if (e.key === "Escape") {
|
|
104
|
+
e.preventDefault();
|
|
105
|
+
cancel();
|
|
106
|
+
}
|
|
107
|
+
if (e.key === "Enter" &&
|
|
108
|
+
e.metaKey === false &&
|
|
109
|
+
e.ctrlKey === false &&
|
|
110
|
+
e.target.tagName !== "TEXTAREA") {
|
|
111
|
+
// Enter saves, same as Attio's own "Save ⏎" -- but never
|
|
112
|
+
// steals it from a nested picker's own Enter-to-commit.
|
|
113
|
+
if (!isInsideLaterFloatingLayer(e.target, contentRef.current)) {
|
|
114
|
+
save();
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}, children: [_jsxs("div", { className: "crisp-taskeditpopover-header", children: [_jsxs("span", { className: "crisp-taskeditpopover-heading", children: [_jsx(CircleCheckBig, { size: 16, strokeWidth: 1.75 }), headingLabel] }), _jsx("button", { type: "button", className: "crisp-taskeditpopover-close", "aria-label": closeLabel, onClick: cancel, children: _jsx(X, { size: 16, strokeWidth: 1.75 }) })] }), _jsxs("div", { className: "crisp-taskeditpopover-body", children: [_jsx("input", { type: "checkbox", className: "crisp-taskeditpopover-check", checked: done, "aria-label": title, onChange: (e) => onToggleDone?.(e.target.checked) }), _jsx("input", { type: "text", className: "crisp-taskeditpopover-title", value: title, onChange: (e) => onTitleChange?.(e.target.value) })] }), _jsxs("div", { className: "crisp-taskeditpopover-footer", children: [_jsxs("div", { className: "crisp-taskeditpopover-chips", children: [_jsx(Popover, { align: "start", "aria-label": dueDatePlaceholder, trigger: _jsxs("button", { type: "button", className: "crisp-taskeditpopover-chip", children: [_jsx(CalendarDays, { size: 14, strokeWidth: 1.75 }), dueDateLabel ?? dueDatePlaceholder] }), children: dueDatePicker }), _jsx(Popover, { align: "start", "aria-label": assignedToLabel, trigger: _jsxs("button", { type: "button", className: "crisp-taskeditpopover-chip", children: [_jsx("span", { "aria-hidden": "true", children: "@" }), assignee != null
|
|
118
|
+
? `${assignedToLabel} ${assignee.name}`
|
|
119
|
+
: assignLabel] }), children: _jsx(AssigneePicker, { users: users, assignee: assignee, onAssigneeChange: onAssigneeChange }) }), _jsx(Popover, { align: "start", "aria-label": addRecordLabel, trigger: _jsxs("button", { type: "button", className: "crisp-taskeditpopover-chip", children: [_jsx("span", { "aria-hidden": "true", children: "\u2197" }), linkedRecordCount > 0
|
|
120
|
+
? linkedRecordLabel
|
|
121
|
+
: addRecordLabel] }), children: _jsx(RecordPicker, { records: recordCandidates, loading: recordCandidatesLoading, onQueryChange: onRecordQueryChange, onSelect: onSelectRecord }) })] }), _jsxs("div", { className: "crisp-taskeditpopover-actions", children: [menu != null && (_jsx(Popover, { align: "end", "aria-label": moreActionsLabel, trigger: _jsx("button", { type: "button", className: "crisp-taskeditpopover-kebab", "aria-label": moreActionsLabel, children: _jsx(MoreHorizontal, { size: 16, strokeWidth: 1.75 }) }), children: menu })), _jsxs("button", { type: "button", className: "crisp-taskeditpopover-cancel", onClick: cancel, children: [cancelLabel, _jsx("kbd", { className: "crisp-taskeditpopover-key", children: cancelKeyLabel })] }), _jsxs("button", { type: "button", className: "crisp-taskeditpopover-save", onClick: save, children: [saveLabel, _jsx("kbd", { className: "crisp-taskeditpopover-key", children: "\u23CE" })] })] })] })] }) }))] }));
|
|
122
|
+
}
|
|
123
|
+
TaskEditPopover.displayName = "TaskEditPopover";
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { defineRecipe, resolveRecipe } from "../../core/index.js";
|
|
2
|
+
import { contract as c } from "../../tokens/index.js";
|
|
3
|
+
// The "Edit task" popover -- measured live on app.attio.com's Tasks page
|
|
4
|
+
// (click a task row): an 800px-wide floating card, radius 12, a single
|
|
5
|
+
// subtle 1px ring shadow (no blur/offset layer -- lighter than
|
|
6
|
+
// `elevation.floating`, which every other crisp overlay uses; this one
|
|
7
|
+
// really does measure as just the ring, confirmed by walking every
|
|
8
|
+
// ancestor up to the floating root). Same core floating primitives
|
|
9
|
+
// `Popover`/`savedviews` already use directly (this doesn't reuse
|
|
10
|
+
// `Popover` itself: its own `.crisp-popover-content` hardcodes a
|
|
11
|
+
// max-width of min(360px, 92vw), far under this popover's measured width).
|
|
12
|
+
//
|
|
13
|
+
// Three rows: a 44px header (icon+"Edit task", a bottom hairline divider,
|
|
14
|
+
// a close button), a body row (checkbox + editable title), and a 44px
|
|
15
|
+
// footer (the due-date/assigned/add-record chip row on the left, the
|
|
16
|
+
// kebab + Cancel/Save on the right). The chip row's own 10px/400 muted
|
|
17
|
+
// caption size and the footer buttons' matching 10px labels don't fit any
|
|
18
|
+
// existing text-scale token (closest is `text.xs` at 11px) -- raw px per
|
|
19
|
+
// Golden Rule 3's "genuinely intrinsic size" exception, same as
|
|
20
|
+
// `dialog.recipe.ts`'s own submit-key glyphs.
|
|
21
|
+
const config = defineRecipe({
|
|
22
|
+
name: "taskeditpopover",
|
|
23
|
+
// Portaled root: position/z-index/the open transition live here as raw
|
|
24
|
+
// literal CSS (not `base`, which lightningcss nests under `.${prefixed}`
|
|
25
|
+
// at BUILD time -- fine for the plain declarations below, but this exact
|
|
26
|
+
// selector text has to exist as source text for `spec-check.mjs`'s
|
|
27
|
+
// overlay-escape/animation scan, the same reason Popover/Menu/Select
|
|
28
|
+
// declare their own portaled root in `globalCss` rather than `base`).
|
|
29
|
+
globalCss: `.crisp-taskeditpopover { position: fixed; z-index: 70; pointer-events: auto; opacity: 1; transform: scale(1); transition: opacity 0.08s ease, transform 0.08s ease; @starting-style { opacity: 0; transform: scale(0.97) translateY(-4px); } }`,
|
|
30
|
+
base: `display: flex; flex-direction: column; box-sizing: border-box; width: 800px; max-width: 92vw; border-radius: ${c.radius.xl}; background: ${c.bgSurface}; box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.07); font-family: ${c.font.sans};
|
|
31
|
+
-webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
|
|
32
|
+
|
|
33
|
+
& .crisp-taskeditpopover-header { display: flex; align-items: center; justify-content: space-between; gap: ${c.space["2"]}; height: 44px; box-sizing: border-box; padding: 0 ${c.space["2"]} 0 ${c.space["3"]}; border-bottom: 1px solid ${c.borderSubtle}; }
|
|
34
|
+
& .crisp-taskeditpopover-heading { display: flex; align-items: center; gap: ${c.space["1"]}; font-size: ${c.text.lg}; font-weight: ${c.weight.medium}; color: ${c.fgPrimary}; }
|
|
35
|
+
& .crisp-taskeditpopover-heading svg { flex-shrink: 0; color: ${c.fgMuted}; }
|
|
36
|
+
& .crisp-taskeditpopover-close { display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; width: 28px; height: 28px; margin: 0; padding: 0; border: none; border-radius: ${c.radius.md}; background: none; color: ${c.fgMuted}; font-family: inherit; cursor: pointer; }
|
|
37
|
+
& .crisp-taskeditpopover-close:hover { background: ${c.bgRaised}; color: ${c.fgPrimary}; }
|
|
38
|
+
|
|
39
|
+
& .crisp-taskeditpopover-body { display: flex; align-items: center; gap: ${c.space["2.5"]}; min-height: 52px; box-sizing: border-box; padding: ${c.space["3"]}; }
|
|
40
|
+
& .crisp-taskeditpopover-check { appearance: none; -webkit-appearance: none; box-sizing: border-box; flex-shrink: 0; margin: 0; width: 16px; height: 16px; border: none; border-radius: ${c.radius.full}; background: ${c.bgSurface}; box-shadow: inset 0 0 0 1px color-mix(in srgb, ${c.fgPrimary} 9%, transparent); cursor: pointer; }
|
|
41
|
+
& .crisp-taskeditpopover-check:hover { box-shadow: inset 0 0 0 1px color-mix(in srgb, ${c.fgPrimary} 30%, transparent); }
|
|
42
|
+
& .crisp-taskeditpopover-check:checked { background: ${c.bgBrandSolid} center / 10px no-repeat; box-shadow: none; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 12 12' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M2.5 6.5 5 9l4.5-5'/%3E%3C/svg%3E"); }
|
|
43
|
+
& .crisp-taskeditpopover-title { flex: 1; min-width: 0; border: none; border-radius: ${c.radius.sm}; outline: none; background: none; font-family: inherit; font-size: ${c.text.lg}; font-weight: ${c.weight.medium}; color: ${c.fgPrimary}; }
|
|
44
|
+
& .crisp-taskeditpopover-title:focus-visible { outline: 2px solid ${c.strokeFocusRing}; outline-offset: 2px; }
|
|
45
|
+
|
|
46
|
+
& .crisp-taskeditpopover-footer { display: flex; align-items: center; justify-content: space-between; gap: ${c.space["2"]}; height: 44px; box-sizing: border-box; padding: 0 ${c.space["2"]} 0 ${c.space["3"]}; }
|
|
47
|
+
& .crisp-taskeditpopover-chips { display: flex; align-items: center; gap: ${c.space["1"]}; min-width: 0; }
|
|
48
|
+
& .crisp-taskeditpopover-chip { display: inline-flex; align-items: center; gap: 6px; flex-shrink: 0; height: 28px; box-sizing: border-box; padding: 4px 8px 4px 6px; border: none; border-radius: ${c.radius.md}; background: none; color: color-mix(in srgb, ${c.fgPrimary} 55%, transparent); font-family: inherit; font-size: 10px; white-space: nowrap; cursor: pointer; }
|
|
49
|
+
& .crisp-taskeditpopover-chip svg { flex-shrink: 0; }
|
|
50
|
+
& .crisp-taskeditpopover-chip:hover { background: ${c.bgRaised}; }
|
|
51
|
+
|
|
52
|
+
& .crisp-taskeditpopover-actions { display: flex; align-items: center; gap: ${c.space["2"]}; flex-shrink: 0; }
|
|
53
|
+
& .crisp-taskeditpopover-kebab { display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 28px; margin: 0; padding: 6px; border: none; border-radius: ${c.radius.md}; background: none; color: ${c.fgPrimary}; font-family: inherit; cursor: pointer; }
|
|
54
|
+
& .crisp-taskeditpopover-kebab:hover { background: ${c.bgRaised}; }
|
|
55
|
+
& .crisp-taskeditpopover-cancel { display: inline-flex; align-items: center; gap: 6px; height: 28px; box-sizing: border-box; padding: 4px 6px 4px 8px; border: none; border-radius: ${c.radius.md}; background: none; color: ${c.fgPrimary}; font-family: inherit; font-size: 10px; cursor: pointer; }
|
|
56
|
+
& .crisp-taskeditpopover-cancel:hover { background: ${c.bgRaised}; }
|
|
57
|
+
& .crisp-taskeditpopover-save { display: inline-flex; align-items: center; gap: 6px; height: 28px; box-sizing: border-box; padding: 4px 6px 4px 8px; border: none; border-radius: ${c.radius.md}; background: ${c.bgBrandSolid}; color: ${c.fgOnBrand}; font-family: inherit; font-size: 10px; cursor: pointer; }
|
|
58
|
+
& .crisp-taskeditpopover-save:hover { background: ${c.bgBrandSolidPressed}; }
|
|
59
|
+
& .crisp-taskeditpopover-key { color: color-mix(in srgb, currentColor 55%, transparent); font-size: 10px; }`,
|
|
60
|
+
variants: {},
|
|
61
|
+
defaultVariants: {},
|
|
62
|
+
compoundVariants: [],
|
|
63
|
+
});
|
|
64
|
+
export const taskeditpopoverRecipe = resolveRecipe(config);
|
|
65
|
+
export default config;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./workspaceswitchermenu.js";
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { CircleArrowUp, CircleCheck, Grid2x2, HeartHandshake, LogOut, Plus, Settings, User, UserPlus, } from "lucide-react";
|
|
3
|
+
import { Avatar } from "../../components/avatar/index.js";
|
|
4
|
+
import { MenuItem, MenuSeparator } from "../../components/menu/index.js";
|
|
5
|
+
import { useCrispMessage } from "../../core/index.js";
|
|
6
|
+
/**
|
|
7
|
+
* The FIXED content of Attio's own workspace-switcher popover (crisp#632) —
|
|
8
|
+
* live-measured off app.attio.com/techpartners: each workspace as a
|
|
9
|
+
* checked/unchecked row, then New workspace, then Account settings /
|
|
10
|
+
* Workspace settings, then Invite members / Upgrade Attio / Refer another
|
|
11
|
+
* team, then Apps and integrations, then Sign out — each group separated by
|
|
12
|
+
* a hairline, matching a 280px-wide popover.
|
|
13
|
+
*
|
|
14
|
+
* This is CONTENT, not a component with its own trigger/popover: pass it as
|
|
15
|
+
* `Sidebar`'s own `workspaceMenu` prop (crisp#415 already builds the real
|
|
16
|
+
* menu-button trigger/popover machinery around whatever children it's
|
|
17
|
+
* given — the workspace switcher itself has no fixed content of its own by
|
|
18
|
+
* design, since which settings exist is the consumer's; this is that fixed
|
|
19
|
+
* content, ready-made, the same way `defaultCommandItems` is for `Command`).
|
|
20
|
+
*/
|
|
21
|
+
export function WorkspaceSwitcherMenu({ workspaces, currentWorkspaceId, onSelectWorkspace, onNewWorkspace, onAccountSettings, onWorkspaceSettings, onInviteMembers, onUpgradeAttio, onReferAnotherTeam, onAppsAndIntegrations, onSignOut, newWorkspaceLabel, accountSettingsLabel, workspaceSettingsLabel, inviteMembersLabel, upgradeAttioLabel, referAnotherTeamLabel, appsAndIntegrationsLabel, signOutLabel, }) {
|
|
22
|
+
const newWorkspace = useCrispMessage(newWorkspaceLabel, "New workspace");
|
|
23
|
+
const accountSettings = useCrispMessage(accountSettingsLabel, "Account settings");
|
|
24
|
+
const workspaceSettings = useCrispMessage(workspaceSettingsLabel, "Workspace settings");
|
|
25
|
+
const inviteMembers = useCrispMessage(inviteMembersLabel, "Invite members");
|
|
26
|
+
const upgradeAttio = useCrispMessage(upgradeAttioLabel, "Upgrade Attio");
|
|
27
|
+
const referAnotherTeam = useCrispMessage(referAnotherTeamLabel, "Refer another team");
|
|
28
|
+
const appsAndIntegrations = useCrispMessage(appsAndIntegrationsLabel, "Apps and integrations");
|
|
29
|
+
const signOut = useCrispMessage(signOutLabel, "Sign out");
|
|
30
|
+
return (_jsxs(_Fragment, { children: [workspaces.map((ws) => {
|
|
31
|
+
const checked = ws.id === currentWorkspaceId;
|
|
32
|
+
return (_jsx(MenuItem, { icon: _jsx(Avatar, { size: "sm", shape: "rounded", tone: ws.avatarTone, children: ws.avatarLabel }), trailingIcon: checked ? (_jsx(CircleCheck, { size: 16, strokeWidth: 1.75, className: "crisp-workspaceswitchermenu-check" })) : undefined, "aria-checked": checked, onSelect: () => onSelectWorkspace?.(ws.id), children: ws.name }, ws.id));
|
|
33
|
+
}), _jsx(MenuItem, { icon: _jsx(Plus, { size: 16, strokeWidth: 1.75 }), onSelect: onNewWorkspace, children: newWorkspace }), _jsx(MenuSeparator, {}), _jsx(MenuItem, { icon: _jsx(User, { size: 16, strokeWidth: 1.75 }), onSelect: onAccountSettings, children: accountSettings }), _jsx(MenuItem, { icon: _jsx(Settings, { size: 16, strokeWidth: 1.75 }), onSelect: onWorkspaceSettings, children: workspaceSettings }), _jsx(MenuSeparator, {}), _jsx(MenuItem, { icon: _jsx(UserPlus, { size: 16, strokeWidth: 1.75 }), onSelect: onInviteMembers, children: inviteMembers }), _jsx(MenuItem, { icon: _jsx(CircleArrowUp, { size: 16, strokeWidth: 1.75 }), onSelect: onUpgradeAttio, children: upgradeAttio }), _jsx(MenuItem, { icon: _jsx(HeartHandshake, { size: 16, strokeWidth: 1.75 }), onSelect: onReferAnotherTeam, children: referAnotherTeam }), _jsx(MenuSeparator, {}), _jsx(MenuItem, { icon: _jsx(Grid2x2, { size: 16, strokeWidth: 1.75 }), onSelect: onAppsAndIntegrations, children: appsAndIntegrations }), _jsx(MenuSeparator, {}), _jsx(MenuItem, { icon: _jsx(LogOut, { "data-rtl-flip": "", size: 16, strokeWidth: 1.75 }), onSelect: onSignOut, children: signOut })] }));
|
|
34
|
+
}
|
|
35
|
+
WorkspaceSwitcherMenu.displayName = "WorkspaceSwitcherMenu";
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { defineRecipe, resolveRecipe } from "../../core/index.js";
|
|
2
|
+
import { contract as c } from "../../tokens/index.js";
|
|
3
|
+
/**
|
|
4
|
+
* `WorkspaceSwitcherMenu` owns almost no styling of its own — every visible
|
|
5
|
+
* pixel besides the two rules below is `Menu`/`MenuItem`'s own
|
|
6
|
+
* already-measured recipe (crisp#632):
|
|
7
|
+
* - The RTL mirror for its "Sign out" icon: `LogOut` is treated as
|
|
8
|
+
* direction-bearing (`build/spec-check.mjs`'s own DIRECTIONAL set), so
|
|
9
|
+
* it's marked and mirrored the same way ObjectPageMenu's Notifications
|
|
10
|
+
* chevron is (crisp#616) — see that file's identical recipe for why this
|
|
11
|
+
* is a plain absolute selector, not `${rtlFlipCss}`'s `&`-nesting form.
|
|
12
|
+
* - The current-workspace checkmark's colour: `.crisp-menu-item svg` already
|
|
13
|
+
* sets every menu icon to `fgMuted` (menu.recipe.ts), which is deliberate
|
|
14
|
+
* there but wrong for THIS one glyph — Attio's own is brand-coloured, not
|
|
15
|
+
* muted, to read as a status rather than a plain icon. `svg` doubled in
|
|
16
|
+
* the selector (not just the class) so it outranks that rule under equal
|
|
17
|
+
* source order instead of depending on file concatenation order.
|
|
18
|
+
*/
|
|
19
|
+
const config = defineRecipe({
|
|
20
|
+
name: "workspaceswitchermenu",
|
|
21
|
+
base: "",
|
|
22
|
+
globalCss: `
|
|
23
|
+
[dir="rtl"] .crisp-menu-item svg[data-rtl-flip] { transform: scaleX(-1); }
|
|
24
|
+
.crisp-menu-item svg.crisp-workspaceswitchermenu-check { color: ${c.fgBrandContrast}; }
|
|
25
|
+
`,
|
|
26
|
+
variants: {},
|
|
27
|
+
defaultVariants: {},
|
|
28
|
+
compoundVariants: [],
|
|
29
|
+
});
|
|
30
|
+
export const workspaceswitchermenuRecipe = resolveRecipe(config);
|
|
31
|
+
export default config;
|
|
@@ -274,4 +274,31 @@ export const palette = {
|
|
|
274
274
|
// s3 (shadow.yaml) -- the listbox's single-layer floating shadow, in place
|
|
275
275
|
// of crisp's own multi-layer `elevation.floating` for this component only.
|
|
276
276
|
selectShadow: { light: "#0000001f", dark: "#000000cc" },
|
|
277
|
+
// your-moon/crisp#998: the 12-colour tag/option-colour palette a workspace
|
|
278
|
+
// admin picks for a Select/MultiSelect option, measured live off
|
|
279
|
+
// app.attio.com's Categories multi-select field (table-cell/column-editor
|
|
280
|
+
// pill, not the larger tinted-text activity-feed variant -- see
|
|
281
|
+
// ~/grc-parity/attio-tag-palette.md). All 12 are near-white pastels with
|
|
282
|
+
// flat black text regardless of which one -- Attio's theme toggle is
|
|
283
|
+
// off-limits (same restriction every other raw entry in this file notes),
|
|
284
|
+
// so no dark variant was reachable; light is reused for dark, same as
|
|
285
|
+
// `cyan600`/`purple600`/`orange600` above already do for the same reason.
|
|
286
|
+
// Numbered 1-12 rather than named: Attio assigns them by an internal hash
|
|
287
|
+
// of the option's id, not by any hue name a consumer would recognise.
|
|
288
|
+
tagSwatch1: { light: "#fff3cc", dark: "#fff3cc" },
|
|
289
|
+
tagSwatch2: { light: "#f4fbcb", dark: "#f4fbcb" },
|
|
290
|
+
tagSwatch3: { light: "#fdf7c4", dark: "#fdf7c4" },
|
|
291
|
+
tagSwatch4: { light: "#feecf3", dark: "#feecf3" },
|
|
292
|
+
tagSwatch5: { light: "#e0fced", dark: "#e0fced" },
|
|
293
|
+
tagSwatch6: { light: "#ddf7ff", dark: "#ddf7ff" },
|
|
294
|
+
tagSwatch7: { light: "#f6f7f7", dark: "#f6f7f7" },
|
|
295
|
+
tagSwatch8: { light: "#ffebeb", dark: "#ffebeb" },
|
|
296
|
+
tagSwatch9: { light: "#feeee1", dark: "#feeee1" },
|
|
297
|
+
tagSwatch10: { light: "#e5eeff", dark: "#e5eeff" },
|
|
298
|
+
tagSwatch11: { light: "#f5f0ff", dark: "#f5f0ff" },
|
|
299
|
+
tagSwatch12: { light: "#fdedff", dark: "#fdedff" },
|
|
300
|
+
// measured alongside the palette above: flat opaque black, not `ink`
|
|
301
|
+
// (#101112) -- a genuinely different, slightly darker hex Attio uses only
|
|
302
|
+
// for this one pill's text, regardless of which of the 12 backgrounds.
|
|
303
|
+
tagSwatchInk: { light: "#000000", dark: "#000000" },
|
|
277
304
|
};
|
|
@@ -126,4 +126,24 @@ export const semantic = {
|
|
|
126
126
|
strokeSelectDivider: "selectDivider",
|
|
127
127
|
fgSelectPlaceholder: "selectPlaceholder",
|
|
128
128
|
shadowSelect: "selectShadow",
|
|
129
|
+
/** your-moon/crisp#998: a Select/MultiSelect option's fixed tag colour
|
|
130
|
+
* (`swatch`, 1-12) -- see palette.ts's own comment on `tagSwatch1` for
|
|
131
|
+
* the measurement. Distinct from `Select`'s existing `color` (an
|
|
132
|
+
* arbitrary-CSS status dot) and `MultiSelect`'s existing `tone` (crisp's
|
|
133
|
+
* own semantic 7-tone chip family): this is Attio's own enumerable,
|
|
134
|
+
* workspace-admin-assigned palette, reproduced verbatim rather than
|
|
135
|
+
* mapped onto either existing mechanism. */
|
|
136
|
+
bgTagSwatch1: "tagSwatch1",
|
|
137
|
+
bgTagSwatch2: "tagSwatch2",
|
|
138
|
+
bgTagSwatch3: "tagSwatch3",
|
|
139
|
+
bgTagSwatch4: "tagSwatch4",
|
|
140
|
+
bgTagSwatch5: "tagSwatch5",
|
|
141
|
+
bgTagSwatch6: "tagSwatch6",
|
|
142
|
+
bgTagSwatch7: "tagSwatch7",
|
|
143
|
+
bgTagSwatch8: "tagSwatch8",
|
|
144
|
+
bgTagSwatch9: "tagSwatch9",
|
|
145
|
+
bgTagSwatch10: "tagSwatch10",
|
|
146
|
+
bgTagSwatch11: "tagSwatch11",
|
|
147
|
+
bgTagSwatch12: "tagSwatch12",
|
|
148
|
+
fgTagSwatch: "tagSwatchInk",
|
|
129
149
|
};
|
package/dist/multiselect.css
CHANGED
|
@@ -50,6 +50,24 @@
|
|
|
50
50
|
.crisp-recordstable-tag[data-tone="purple"] { background: color-mix(in srgb, var(--crisp-bg-purple) 16%, transparent); color: color-mix(in srgb, var(--crisp-bg-purple) 80%, var(--crisp-fg-primary)); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--crisp-bg-purple) 30%, transparent); }
|
|
51
51
|
.crisp-recordstable-tag[data-tone="cyan"] { background: color-mix(in srgb, var(--crisp-bg-cyan) 18%, transparent); color: color-mix(in srgb, var(--crisp-bg-cyan) 82%, var(--crisp-fg-primary)); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--crisp-bg-cyan) 30%, transparent); }
|
|
52
52
|
.crisp-recordstable-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)); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--crisp-bg-orange) 30%, transparent); }
|
|
53
|
+
/* your-moon/crisp#998: Attio's own 12-colour tag palette (measured live,
|
|
54
|
+
see tokens/palette.ts's tagSwatch1 comment) -- flat black text, no
|
|
55
|
+
ring/border, unlike the tone family above. 7px radius/10px text are
|
|
56
|
+
measured literals, no matching step in the shared scale (same as
|
|
57
|
+
Select's own .crisp-select-swatch). */
|
|
58
|
+
.crisp-multiselect-swatch { display: inline-flex; align-items: center; height: var(--crisp-size-chip); padding: 0 var(--crisp-space-1); border-radius: 7px; font-size: 10px; font-weight: var(--crisp-weight-regular); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: var(--crisp-fg-tag-swatch); }
|
|
59
|
+
.crisp-multiselect-swatch[data-swatch="1"] { background: var(--crisp-bg-tag-swatch1); }
|
|
60
|
+
.crisp-multiselect-swatch[data-swatch="2"] { background: var(--crisp-bg-tag-swatch2); }
|
|
61
|
+
.crisp-multiselect-swatch[data-swatch="3"] { background: var(--crisp-bg-tag-swatch3); }
|
|
62
|
+
.crisp-multiselect-swatch[data-swatch="4"] { background: var(--crisp-bg-tag-swatch4); }
|
|
63
|
+
.crisp-multiselect-swatch[data-swatch="5"] { background: var(--crisp-bg-tag-swatch5); }
|
|
64
|
+
.crisp-multiselect-swatch[data-swatch="6"] { background: var(--crisp-bg-tag-swatch6); }
|
|
65
|
+
.crisp-multiselect-swatch[data-swatch="7"] { background: var(--crisp-bg-tag-swatch7); }
|
|
66
|
+
.crisp-multiselect-swatch[data-swatch="8"] { background: var(--crisp-bg-tag-swatch8); }
|
|
67
|
+
.crisp-multiselect-swatch[data-swatch="9"] { background: var(--crisp-bg-tag-swatch9); }
|
|
68
|
+
.crisp-multiselect-swatch[data-swatch="10"] { background: var(--crisp-bg-tag-swatch10); }
|
|
69
|
+
.crisp-multiselect-swatch[data-swatch="11"] { background: var(--crisp-bg-tag-swatch11); }
|
|
70
|
+
.crisp-multiselect-swatch[data-swatch="12"] { background: var(--crisp-bg-tag-swatch12); }
|
|
53
71
|
|
|
54
72
|
.crisp-multiselect {
|
|
55
73
|
position: relative; display: inline-flex; width: 100%; font-family: var(--crisp-font-sans); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
|
|
@@ -51,6 +51,24 @@
|
|
|
51
51
|
.crisp-recordstable-tag[data-tone="purple"] { background: color-mix(in srgb, var(--crisp-bg-purple) 16%, transparent); color: color-mix(in srgb, var(--crisp-bg-purple) 80%, var(--crisp-fg-primary)); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--crisp-bg-purple) 30%, transparent); }
|
|
52
52
|
.crisp-recordstable-tag[data-tone="cyan"] { background: color-mix(in srgb, var(--crisp-bg-cyan) 18%, transparent); color: color-mix(in srgb, var(--crisp-bg-cyan) 82%, var(--crisp-fg-primary)); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--crisp-bg-cyan) 30%, transparent); }
|
|
53
53
|
.crisp-recordstable-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)); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--crisp-bg-orange) 30%, transparent); }
|
|
54
|
+
/* your-moon/crisp#998: Attio's own 12-colour tag palette (measured live,
|
|
55
|
+
see tokens/palette.ts's tagSwatch1 comment) -- flat black text, no
|
|
56
|
+
ring/border, unlike the tone family above. 7px radius/10px text are
|
|
57
|
+
measured literals, no matching step in the shared scale (same as
|
|
58
|
+
Select's own .crisp-select-swatch). */
|
|
59
|
+
.crisp-multiselect-swatch { display: inline-flex; align-items: center; height: var(--crisp-size-chip); padding: 0 var(--crisp-space-1); border-radius: 7px; font-size: 10px; font-weight: var(--crisp-weight-regular); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: var(--crisp-fg-tag-swatch); }
|
|
60
|
+
.crisp-multiselect-swatch[data-swatch="1"] { background: var(--crisp-bg-tag-swatch1); }
|
|
61
|
+
.crisp-multiselect-swatch[data-swatch="2"] { background: var(--crisp-bg-tag-swatch2); }
|
|
62
|
+
.crisp-multiselect-swatch[data-swatch="3"] { background: var(--crisp-bg-tag-swatch3); }
|
|
63
|
+
.crisp-multiselect-swatch[data-swatch="4"] { background: var(--crisp-bg-tag-swatch4); }
|
|
64
|
+
.crisp-multiselect-swatch[data-swatch="5"] { background: var(--crisp-bg-tag-swatch5); }
|
|
65
|
+
.crisp-multiselect-swatch[data-swatch="6"] { background: var(--crisp-bg-tag-swatch6); }
|
|
66
|
+
.crisp-multiselect-swatch[data-swatch="7"] { background: var(--crisp-bg-tag-swatch7); }
|
|
67
|
+
.crisp-multiselect-swatch[data-swatch="8"] { background: var(--crisp-bg-tag-swatch8); }
|
|
68
|
+
.crisp-multiselect-swatch[data-swatch="9"] { background: var(--crisp-bg-tag-swatch9); }
|
|
69
|
+
.crisp-multiselect-swatch[data-swatch="10"] { background: var(--crisp-bg-tag-swatch10); }
|
|
70
|
+
.crisp-multiselect-swatch[data-swatch="11"] { background: var(--crisp-bg-tag-swatch11); }
|
|
71
|
+
.crisp-multiselect-swatch[data-swatch="12"] { background: var(--crisp-bg-tag-swatch12); }
|
|
54
72
|
|
|
55
73
|
.crisp-multiselect {
|
|
56
74
|
position: relative; display: inline-flex; width: 100%; font-family: var(--crisp-font-sans); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
|
package/dist/native/tokens.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// The same tokens the CSS custom properties carry, resolved per theme, with
|
|
3
3
|
// dimensions as numbers for React Native and Lynx style objects.
|
|
4
4
|
export type Theme = "light" | "dark";
|
|
5
|
-
export type ColorTokens = Record<"bgBrandSolid" | "bgBrandSolidPressed" | "bgBrandContrast" | "fgBrandContrast" | "fgOnBrand" | "fgOnTone" | "bgSurface" | "bgRaised" | "bgSurfaceMuted" | "borderDefault" | "borderSubtle" | "borderDropzone" | "borderDivider" | "ringNeutral" | "ringField" | "ringSwitch" | "bgTileActive" | "shadowTileActive" | "bgScrim" | "bgOverlayStrong" | "strokeGroupDivider" | "fgAlertDescription" | "bgOverlayFrost" | "shadowOverlay1" | "shadowOverlay2" | "shadowOverlay3" | "bgPopover" | "ringPopover" | "shadowPopover1" | "shadowPopover2" | "shadowPopover3" | "fgPrimary" | "fgMuted" | "bgDanger" | "bgDangerSolid" | "fgDanger" | "strokeFieldFocus" | "fgDisabled" | "bgNeutralWeak" | "bgNeutralInverted" | "fgOnNeutralInverted" | "fgNeutralSubtle" | "fgNeutralMuted" | "strokeFocusRing" | "bgSuccess" | "bgWarning" | "bgWarningSolid" | "bgOrange" | "bgCyan" | "bgPurple" | "bgSidebar" | "bgSubtle" | "fgBrandSoft" | "fgSuccessSoft" | "fgWarningSoft" | "fgDangerSoft" | "fgNeutralSoft" | "bgPurpleSoft" | "fgPurpleSoft" | "ringPurpleSoft" | "ringBrandSoft" | "fgSelectNeutral" | "bgSelectListbox" | "strokeSelectTrigger" | "bgSelectPressed" | "bgSelectDisabled" | "strokeSelectInvalid" | "strokeSelectDivider" | "fgSelectPlaceholder" | "shadowSelect", string>;
|
|
5
|
+
export type ColorTokens = Record<"bgBrandSolid" | "bgBrandSolidPressed" | "bgBrandContrast" | "fgBrandContrast" | "fgOnBrand" | "fgOnTone" | "bgSurface" | "bgRaised" | "bgSurfaceMuted" | "borderDefault" | "borderSubtle" | "borderDropzone" | "borderDivider" | "ringNeutral" | "ringField" | "ringSwitch" | "bgTileActive" | "shadowTileActive" | "bgScrim" | "bgOverlayStrong" | "strokeGroupDivider" | "fgAlertDescription" | "bgOverlayFrost" | "shadowOverlay1" | "shadowOverlay2" | "shadowOverlay3" | "bgPopover" | "ringPopover" | "shadowPopover1" | "shadowPopover2" | "shadowPopover3" | "fgPrimary" | "fgMuted" | "bgDanger" | "bgDangerSolid" | "fgDanger" | "strokeFieldFocus" | "fgDisabled" | "bgNeutralWeak" | "bgNeutralInverted" | "fgOnNeutralInverted" | "fgNeutralSubtle" | "fgNeutralMuted" | "strokeFocusRing" | "bgSuccess" | "bgWarning" | "bgWarningSolid" | "bgOrange" | "bgCyan" | "bgPurple" | "bgSidebar" | "bgSubtle" | "fgBrandSoft" | "fgSuccessSoft" | "fgWarningSoft" | "fgDangerSoft" | "fgNeutralSoft" | "bgPurpleSoft" | "fgPurpleSoft" | "ringPurpleSoft" | "ringBrandSoft" | "fgSelectNeutral" | "bgSelectListbox" | "strokeSelectTrigger" | "bgSelectPressed" | "bgSelectDisabled" | "strokeSelectInvalid" | "strokeSelectDivider" | "fgSelectPlaceholder" | "shadowSelect" | "bgTagSwatch1" | "bgTagSwatch2" | "bgTagSwatch3" | "bgTagSwatch4" | "bgTagSwatch5" | "bgTagSwatch6" | "bgTagSwatch7" | "bgTagSwatch8" | "bgTagSwatch9" | "bgTagSwatch10" | "bgTagSwatch11" | "bgTagSwatch12" | "fgTagSwatch", string>;
|
|
6
6
|
export declare const tokens: {
|
|
7
7
|
color: Record<Theme, ColorTokens>;
|
|
8
8
|
palette: Record<Theme, Record<string, string>>;
|