@crisp-ui-kit/crisp 0.57.0 → 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/accentpicker.css +5 -0
- package/dist/accentpicker.d.ts +7 -0
- package/dist/accentpicker.layered.css +7 -0
- package/dist/authshell.css +9 -4
- package/dist/authshell.layered.css +9 -4
- package/dist/avatar.css +1 -1
- package/dist/avatar.layered.css +1 -1
- package/dist/badge.css +5 -5
- package/dist/badge.layered.css +5 -5
- 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 +9 -8
- package/dist/button.layered.css +9 -8
- 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/accentpicker/accentpicker.js +66 -0
- package/dist/cjs/components/accentpicker/accentpicker.recipe.js +25 -0
- package/dist/cjs/components/accentpicker/index.js +7 -0
- package/dist/cjs/components/avatar/avatar.recipe.js +4 -1
- package/dist/cjs/components/badge/badge.recipe.js +38 -10
- 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 +55 -21
- 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 +33 -3
- package/dist/cjs/components/input/input.recipe.js +22 -7
- 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 +2 -2
- package/dist/cjs/components/multiselect/multiselect.recipe.js +45 -12
- package/dist/cjs/components/progress/progress.js +21 -4
- package/dist/cjs/components/progress/progress.recipe.js +30 -0
- package/dist/cjs/components/select/select.js +3 -3
- package/dist/cjs/components/select/select.recipe.js +63 -10
- 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/text/text.recipe.js +8 -7
- package/dist/cjs/components/textarea/textarea.js +3 -2
- package/dist/cjs/components/textarea/textarea.recipe.js +13 -9
- package/dist/cjs/components/textlink/textlink.recipe.js +6 -3
- package/dist/cjs/index.js +8 -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 +101 -9
- 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 +107 -33
- package/dist/cjs/tokens/scale.js +45 -2
- package/dist/cjs/tokens/semantic.js +26 -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/accentpicker/accentpicker.js +30 -0
- package/dist/esm/components/accentpicker/accentpicker.recipe.js +22 -0
- package/dist/esm/components/accentpicker/index.js +2 -0
- package/dist/esm/components/avatar/avatar.recipe.js +4 -1
- package/dist/esm/components/badge/badge.recipe.js +38 -10
- 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 +56 -22
- 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 +33 -3
- package/dist/esm/components/input/input.recipe.js +23 -8
- 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 +2 -2
- package/dist/esm/components/multiselect/multiselect.recipe.js +45 -12
- package/dist/esm/components/progress/progress.js +22 -5
- package/dist/esm/components/progress/progress.recipe.js +30 -0
- package/dist/esm/components/select/select.js +3 -3
- package/dist/esm/components/select/select.recipe.js +63 -10
- 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/text/text.recipe.js +8 -7
- package/dist/esm/components/textarea/textarea.js +3 -2
- package/dist/esm/components/textarea/textarea.recipe.js +14 -10
- package/dist/esm/components/textlink/textlink.recipe.js +6 -3
- package/dist/esm/index.js +8 -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 +102 -10
- 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 +107 -33
- package/dist/esm/tokens/scale.js +44 -1
- package/dist/esm/tokens/semantic.js +26 -0
- package/dist/fileupload.css +3 -1
- package/dist/fileupload.layered.css +3 -1
- package/dist/input.css +11 -2
- package/dist/input.layered.css +11 -2
- 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 +17 -6
- package/dist/multiselect.d.ts +2 -2
- package/dist/multiselect.layered.css +17 -6
- package/dist/native/tokens.d.ts +2 -1
- package/dist/native/tokens.js +53 -25
- 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/progress.css +3 -0
- package/dist/progress.layered.css +3 -0
- 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 +47 -7
- package/dist/select.d.ts +2 -2
- package/dist/select.layered.css +47 -7
- 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 +927 -365
- package/dist/styles.layered.css +927 -365
- 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/text.css +1 -1
- package/dist/text.layered.css +1 -1
- package/dist/textarea.css +4 -7
- package/dist/textarea.layered.css +4 -7
- package/dist/textlink.css +2 -2
- package/dist/textlink.layered.css +2 -2
- package/dist/themepicker.css +2 -1
- package/dist/themepicker.layered.css +2 -1
- package/dist/tokens.css +31 -13
- package/dist/tokens.json +53 -25
- package/dist/types/components/accentpicker/accentpicker.d.ts +23 -0
- package/dist/types/components/accentpicker/accentpicker.recipe.d.ts +3 -0
- package/dist/types/components/accentpicker/index.d.ts +2 -0
- 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 +13 -8
- 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 +84 -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/multiselect/multiselect.d.ts +4 -3
- package/dist/types/components/multiselect/multiselect.recipe.d.ts +3 -0
- package/dist/types/components/progress/progress.d.ts +16 -4
- package/dist/types/components/progress/progress.recipe.d.ts +22 -0
- package/dist/types/components/select/select.d.ts +6 -5
- package/dist/types/components/select/select.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/components/textarea/textarea.d.ts +16 -0
- package/dist/types/index.d.ts +8 -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 +89 -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 +7 -1
- package/dist/types/tokens/elevation.d.ts +8 -5
- package/dist/types/tokens/palette.d.ts +28 -12
- package/dist/types/tokens/scale.d.ts +12 -1
- package/dist/types/tokens/semantic.d.ts +17 -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 +41 -1
|
@@ -4,11 +4,25 @@ exports.menuRecipe = void 0;
|
|
|
4
4
|
const core_1 = require("../../core");
|
|
5
5
|
const tokens_1 = require("../../tokens");
|
|
6
6
|
// A dropdown menu — same popover surface used for ⋯ / sort / filter
|
|
7
|
-
// menus (measured from listboxes):
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
7
|
+
// menus (measured from listboxes): 4px padding, over 32px items (radius 8,
|
|
8
|
+
// icon + label + optional shortcut / check). The content portals to
|
|
9
|
+
// document.body (#339) so no ancestor's transform/backdrop-filter can
|
|
10
|
+
// capture its position: fixed box; z-index 70 keeps it above Dialog/
|
|
11
|
+
// Drawer's 60/61.
|
|
12
|
+
//
|
|
13
|
+
// your-moon/crisp#934: radius and shadow are seed-design's,
|
|
14
|
+
// not Attio's -- Menu is the ONE component the seed raw dump confirms
|
|
15
|
+
// actually uses a shadow token at all (every other floating surface in
|
|
16
|
+
// seed's own spec has none). Radius moved from `radius.xl` (12px, this
|
|
17
|
+
// component's own private value) to `radius["3xl"]` (20px) -- seed treats
|
|
18
|
+
// "floating surface radius" as one shared value across Dialog/Menu/Select,
|
|
19
|
+
// not a per-component one; `radius["3xl"]` is already that shared token
|
|
20
|
+
// (Select's listbox, crisp/#867; Dialog, this same PR). Shadow moved from
|
|
21
|
+
// `elevation.floating` (crisp's own 4-layer blue-tinted glow, still used by
|
|
22
|
+
// Popover/ContextMenu/Toast/etc -- NOT touched here, out of scope) to
|
|
23
|
+
// `elevation.selectFloating` -- already seed's exact flat `$shadow.s3`
|
|
24
|
+
// value, already in this file (Select's own listbox, #867), reused rather
|
|
25
|
+
// than duplicated.
|
|
12
26
|
//
|
|
13
27
|
// The menu's floating content and everything inside it live in `globalCss`
|
|
14
28
|
// below, not nested under `&` (#357): nesting compiles to a real descendant
|
|
@@ -35,8 +49,8 @@ const config = (0, core_1.defineRecipe)({
|
|
|
35
49
|
font-family: ${tokens_1.contract.font.sans}; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
|
|
36
50
|
position: fixed; z-index: 70; pointer-events: auto; box-sizing: border-box; display: flex; flex-direction: column; gap: ${tokens_1.contract.space.px};
|
|
37
51
|
min-width: 200px; max-height: 360px; overflow-y: auto; padding: ${tokens_1.contract.space["1"]};
|
|
38
|
-
border-radius: ${tokens_1.contract.radius
|
|
39
|
-
box-shadow: ${tokens_1.elevation.
|
|
52
|
+
border-radius: ${tokens_1.contract.radius["3xl"]}; background: ${tokens_1.contract.bgPopover};
|
|
53
|
+
box-shadow: ${tokens_1.elevation.selectFloating};
|
|
40
54
|
transform-origin: top left; opacity: 1; transform: none; transition: opacity .08s ease, transform .08s ease;
|
|
41
55
|
}
|
|
42
56
|
.crisp-menu-content[data-align="end"] { transform-origin: top right; }
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.menusheetRecipe = exports.MenuSheetItem = exports.MenuSheetGroup = exports.MenuSheet = void 0;
|
|
4
|
+
var menusheet_1 = require("./menusheet");
|
|
5
|
+
Object.defineProperty(exports, "MenuSheet", { enumerable: true, get: function () { return menusheet_1.MenuSheet; } });
|
|
6
|
+
Object.defineProperty(exports, "MenuSheetGroup", { enumerable: true, get: function () { return menusheet_1.MenuSheetGroup; } });
|
|
7
|
+
Object.defineProperty(exports, "MenuSheetItem", { enumerable: true, get: function () { return menusheet_1.MenuSheetItem; } });
|
|
8
|
+
var menusheet_recipe_1 = require("./menusheet.recipe");
|
|
9
|
+
Object.defineProperty(exports, "menusheetRecipe", { enumerable: true, get: function () { return menusheet_recipe_1.menusheetRecipe; } });
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.MenuSheet = MenuSheet;
|
|
37
|
+
exports.MenuSheetGroup = MenuSheetGroup;
|
|
38
|
+
exports.MenuSheetItem = MenuSheetItem;
|
|
39
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
40
|
+
const dialog = __importStar(require("@zag-js/dialog"));
|
|
41
|
+
const react_1 = require("@zag-js/react");
|
|
42
|
+
const React = __importStar(require("react"));
|
|
43
|
+
const core_1 = require("../../core");
|
|
44
|
+
const menu_keyboard_1 = require("../menu/menu-keyboard");
|
|
45
|
+
const menusheet_recipe_1 = require("./menusheet.recipe");
|
|
46
|
+
const MenuSheetContext = React.createContext(null);
|
|
47
|
+
let instanceCounter = 0;
|
|
48
|
+
function useMenuSheetInstanceId() {
|
|
49
|
+
const idRef = React.useRef(null);
|
|
50
|
+
if (idRef.current == null) {
|
|
51
|
+
instanceCounter += 1;
|
|
52
|
+
idRef.current = `crisp-menusheet-${instanceCounter}`;
|
|
53
|
+
}
|
|
54
|
+
return idRef.current;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* A mobile menu sheet -- a focus-trapped bottom sheet (same Zag dialog
|
|
58
|
+
* machine as BottomSheet) whose body is a role=menu list, with the identical
|
|
59
|
+
* keyboard model Menu already uses: arrow keys move, Home/End jump,
|
|
60
|
+
* typeahead, Enter/Space select, Escape closes. Compose items with
|
|
61
|
+
* MenuSheetItem, and group a run of them in one rounded card with
|
|
62
|
+
* MenuSheetGroup.
|
|
63
|
+
*/
|
|
64
|
+
function MenuSheet({ open, onOpenChange, title, description, footer, "aria-label": ariaLabel, children, className, "aria-describedby": describedBy, ...rest }) {
|
|
65
|
+
const id = useMenuSheetInstanceId();
|
|
66
|
+
const listRef = React.useRef(null);
|
|
67
|
+
const service = (0, react_1.useMachine)(dialog.machine, {
|
|
68
|
+
id,
|
|
69
|
+
open,
|
|
70
|
+
onOpenChange(details) {
|
|
71
|
+
onOpenChange(details.open);
|
|
72
|
+
},
|
|
73
|
+
"aria-label": typeof title === "string" ? title : ariaLabel,
|
|
74
|
+
onEscapeKeyDown(event) {
|
|
75
|
+
if ((0, core_1.hasOpenFloatingLayer)())
|
|
76
|
+
event.preventDefault();
|
|
77
|
+
},
|
|
78
|
+
// Without this, Zag's own focus trap parks initial focus on the dialog
|
|
79
|
+
// surface itself, which wins the race against useMenuKeyboard's rAF-
|
|
80
|
+
// scheduled "focus the first item" effect below -- the two compete for
|
|
81
|
+
// the same job and Zag's own dialog-level default was landing last.
|
|
82
|
+
// Pointing it at the first menu item directly makes Zag do that job
|
|
83
|
+
// itself instead of fighting useMenuKeyboard for it.
|
|
84
|
+
initialFocusEl() {
|
|
85
|
+
return (listRef.current?.querySelector(menu_keyboard_1.MENU_ITEM_SELECTOR) ?? null);
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
const api = dialog.connect(service, react_1.normalizeProps);
|
|
89
|
+
const close = React.useCallback(() => onOpenChange(false), [onOpenChange]);
|
|
90
|
+
const { onKeyDown: menuKeyDown, items } = (0, menu_keyboard_1.useMenuKeyboard)({
|
|
91
|
+
open: api.open,
|
|
92
|
+
close,
|
|
93
|
+
contentRef: listRef,
|
|
94
|
+
});
|
|
95
|
+
// Roving tabindex: exactly one item is a real Tab stop at a time (#237 --
|
|
96
|
+
// "selected, or first when none" -- is right in every state but the one
|
|
97
|
+
// that matters, so it is enforced imperatively here rather than trusted to
|
|
98
|
+
// a per-render default). useMenuKeyboard moves FOCUS with arrow keys; this
|
|
99
|
+
// keeps the tabindex in sync with wherever that focus actually lands,
|
|
100
|
+
// including the initial auto-focus Zag's own initialFocusEl performs.
|
|
101
|
+
const onFocus = React.useCallback((e) => {
|
|
102
|
+
for (const el of items())
|
|
103
|
+
el.tabIndex = -1;
|
|
104
|
+
e.target.tabIndex = 0;
|
|
105
|
+
}, [items]);
|
|
106
|
+
React.useEffect(() => {
|
|
107
|
+
if (!api.open)
|
|
108
|
+
return;
|
|
109
|
+
const [first] = items();
|
|
110
|
+
if (first)
|
|
111
|
+
first.tabIndex = 0;
|
|
112
|
+
}, [api.open, items]);
|
|
113
|
+
const ctx = React.useMemo(() => ({ close }), [close]);
|
|
114
|
+
if (!api.open)
|
|
115
|
+
return null;
|
|
116
|
+
return ((0, jsx_runtime_1.jsx)(react_1.Portal, { children: (0, jsx_runtime_1.jsxs)("div", { className: (0, menusheet_recipe_1.menusheetRecipe)(), children: [(0, jsx_runtime_1.jsx)("div", { ...api.getBackdropProps(), className: "crisp-menusheet-backdrop" }), (0, jsx_runtime_1.jsx)("div", { ...api.getPositionerProps(), className: "crisp-menusheet-positioner", children: (0, jsx_runtime_1.jsxs)("div", { ...rest, ...(0, core_1.withDescribedBy)(api.getContentProps(), describedBy), className: className
|
|
117
|
+
? `crisp-menusheet-content ${className}`
|
|
118
|
+
: "crisp-menusheet-content", children: [(title != null || description != null) && ((0, jsx_runtime_1.jsxs)("div", { className: "crisp-menusheet-header", children: [title != null && ((0, jsx_runtime_1.jsx)("span", { ...api.getTitleProps(), className: "crisp-menusheet-title", children: title })), description != null && ((0, jsx_runtime_1.jsx)("span", { className: "crisp-menusheet-description", children: description }))] })), (0, jsx_runtime_1.jsx)(MenuSheetContext.Provider, { value: ctx, children: (0, jsx_runtime_1.jsx)("div", { ref: listRef, role: "menu", className: "crisp-menusheet-list", onKeyDown: menuKeyDown, onFocus: onFocus, children: children }) }), footer != null && ((0, jsx_runtime_1.jsx)("div", { className: "crisp-menusheet-footer", children: footer }))] }) })] }) }));
|
|
119
|
+
}
|
|
120
|
+
MenuSheet.displayName = "MenuSheet";
|
|
121
|
+
function MenuSheetGroup({ children }) {
|
|
122
|
+
return (0, jsx_runtime_1.jsx)("div", { className: "crisp-menusheet-group", children: children });
|
|
123
|
+
}
|
|
124
|
+
MenuSheetGroup.displayName = "MenuSheetGroup";
|
|
125
|
+
function MenuSheetItem({ children, icon, description, tone = "neutral", disabled, onSelect, closeOnSelect = true, }) {
|
|
126
|
+
const ctx = React.useContext(MenuSheetContext);
|
|
127
|
+
return ((0, jsx_runtime_1.jsxs)("button", { type: "button", role: "menuitem", tabIndex: -1, disabled: disabled, "data-disabled": disabled ? "" : undefined, "data-tone": tone, className: "crisp-menusheet-item", onClick: () => {
|
|
128
|
+
onSelect?.();
|
|
129
|
+
if (closeOnSelect)
|
|
130
|
+
ctx?.close();
|
|
131
|
+
}, children: [icon != null && ((0, jsx_runtime_1.jsx)("span", { className: "crisp-menusheet-item-icon", children: icon })), (0, jsx_runtime_1.jsxs)("span", { className: "crisp-menusheet-item-content", children: [(0, jsx_runtime_1.jsx)("span", { className: "crisp-menusheet-item-label", children: children }), description != null && ((0, jsx_runtime_1.jsx)("span", { className: "crisp-menusheet-item-description", children: description }))] })] }));
|
|
132
|
+
}
|
|
133
|
+
MenuSheetItem.displayName = "MenuSheetItem";
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.menusheetRecipe = void 0;
|
|
4
|
+
const core_1 = require("../../core");
|
|
5
|
+
const tokens_1 = require("../../tokens");
|
|
6
|
+
// A mobile menu sheet -- daangn/seed-design components menu-sheet.yaml and
|
|
7
|
+
// menu-sheet-item.yaml (dev at e1c486b904297f7b6a8f17b8523f732c37015d60), not
|
|
8
|
+
// Attio (no such component there; crisp#911). Same @zag-js/dialog machine as
|
|
9
|
+
// BottomSheet, and the identical role=menu keyboard model Menu already uses
|
|
10
|
+
// (useMenuKeyboard, shared rather than reimplemented). Differs from
|
|
11
|
+
// BottomSheet in every slot value: r5 radius (not r6), content-level padding
|
|
12
|
+
// (not per-slot), no header alignment / close-button matrix at all -- menu
|
|
13
|
+
// sheet has neither in its own schema. Every numeric value cites its yaml
|
|
14
|
+
// line. NOTE: no apostrophes in this file -- one broke the recipe compiler
|
|
15
|
+
// silently on BottomSheet, see AUTHORING.md Lesson 64.
|
|
16
|
+
const config = (0, core_1.defineRecipe)({
|
|
17
|
+
name: "menusheet",
|
|
18
|
+
base: `display: contents; font-family: ${tokens_1.contract.font.sans};
|
|
19
|
+
& .crisp-menusheet-backdrop {
|
|
20
|
+
position: fixed; inset: 0; z-index: 60;
|
|
21
|
+
background: ${tokens_1.contract.bgOverlayStrong}; /* menu-sheet.yaml:95 backdrop.color = $color.bg.overlay */
|
|
22
|
+
opacity: 1; transition: opacity 100ms cubic-bezier(0,0,.15,1); /* menu-sheet.yaml:96-98 enterDuration/enterTimingFunction = $duration.d2/$timing-function.enter */
|
|
23
|
+
@starting-style { opacity: 0; }
|
|
24
|
+
}
|
|
25
|
+
& .crisp-menusheet-positioner {
|
|
26
|
+
position: fixed; inset: 0; z-index: 61;
|
|
27
|
+
display: flex; align-items: flex-end; justify-content: center;
|
|
28
|
+
}
|
|
29
|
+
& .crisp-menusheet-content {
|
|
30
|
+
-webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
|
|
31
|
+
position: relative; box-sizing: border-box; display: flex; flex-direction: column;
|
|
32
|
+
overflow: hidden; width: 100%; max-width: 480px; /* menu-sheet.yaml:104 content.maxWidth */
|
|
33
|
+
max-height: 85vh;
|
|
34
|
+
background: ${tokens_1.contract.bgSurface}; /* menu-sheet.yaml:103 content.color = $color.bg.layer-floating */
|
|
35
|
+
padding: ${tokens_1.contract.space["6"]} ${tokens_1.contract.space["4"]} ${tokens_1.contract.space["4"]}; /* menu-sheet.yaml:105-107 content.paddingX/paddingTop/paddingBottom = global-gutter/x6/x4 (16px/24px/16px) */
|
|
36
|
+
border-radius: ${tokens_1.contract.radius["3xl"]} ${tokens_1.contract.radius["3xl"]} 0 0; /* menu-sheet.yaml:108 content.topCornerRadius = $radius.r5 (20px) */
|
|
37
|
+
transform: translateY(0);
|
|
38
|
+
transition: transform 300ms cubic-bezier(.03,.4,.1,1); /* menu-sheet.yaml:109-110 content.enterDuration/enterTimingFunction (enter-expressive) */
|
|
39
|
+
@starting-style { transform: translateY(100%); }
|
|
40
|
+
}
|
|
41
|
+
& .crisp-menusheet-header {
|
|
42
|
+
display: flex; flex-direction: column;
|
|
43
|
+
gap: ${tokens_1.contract.space["1"]}; /* menu-sheet.yaml:115 header.gap = $dimension.x1 (4px) */
|
|
44
|
+
padding-bottom: ${tokens_1.contract.space["4"]}; /* menu-sheet.yaml:116 header.paddingBottom = $dimension.x4 (16px) */
|
|
45
|
+
flex-shrink: 0;
|
|
46
|
+
}
|
|
47
|
+
& .crisp-menusheet-title {
|
|
48
|
+
color: ${tokens_1.contract.fgPrimary}; /* menu-sheet.yaml:121 title.color = $color.fg.neutral */
|
|
49
|
+
font-size: 18px; line-height: 24px; font-weight: ${tokens_1.contract.weight.bold}; /* menu-sheet.yaml:118-120 title.fontSize/lineHeight/fontWeight = $font-size.t6/$line-height.t6/bold */
|
|
50
|
+
}
|
|
51
|
+
& .crisp-menusheet-description {
|
|
52
|
+
color: ${tokens_1.contract.fgMuted}; /* menu-sheet.yaml:126 description.color = $color.fg.neutral-muted */
|
|
53
|
+
font-size: ${tokens_1.contract.text.lg}; line-height: 19px; font-weight: ${tokens_1.contract.weight.regular}; /* menu-sheet.yaml:123-125 description.fontSize/lineHeight/fontWeight = $font-size.t4/$line-height.t4/regular */
|
|
54
|
+
}
|
|
55
|
+
& .crisp-menusheet-list {
|
|
56
|
+
display: flex; flex-direction: column;
|
|
57
|
+
gap: ${tokens_1.contract.space["2.5"]}; /* menu-sheet.yaml:129 list.gap = $dimension.x2_5 (10px) */
|
|
58
|
+
overflow-y: auto; min-height: 0;
|
|
59
|
+
}
|
|
60
|
+
& .crisp-menusheet-group {
|
|
61
|
+
display: flex; flex-direction: column;
|
|
62
|
+
border-radius: ${tokens_1.contract.radius["2xl"]}; overflow: hidden; /* menu-sheet.yaml:131 group.cornerRadius = $radius.r4 (16px) */
|
|
63
|
+
}
|
|
64
|
+
& .crisp-menusheet-group > * + * { border-top: 1px solid ${tokens_1.contract.strokeGroupDivider}; } /* menu-sheet.yaml:132-134 divider.strokeBottomWidth/strokeColor = 1px/$color.stroke.neutral-muted */
|
|
65
|
+
& .crisp-menusheet-footer {
|
|
66
|
+
flex-shrink: 0;
|
|
67
|
+
padding-top: ${tokens_1.contract.space["2.5"]}; /* menu-sheet.yaml:136 footer.paddingTop = $dimension.x2_5 (10px) */
|
|
68
|
+
}
|
|
69
|
+
& .crisp-menusheet-item {
|
|
70
|
+
position: relative; box-sizing: border-box; display: flex; align-items: center;
|
|
71
|
+
width: 100%; min-height: 52px; /* menu-sheet-item.yaml:72 root.minHeight */
|
|
72
|
+
padding: ${tokens_1.contract.space["3.5"]} ${tokens_1.contract.space["4"]}; gap: ${tokens_1.contract.space["3.5"]}; /* menu-sheet-item.yaml:73-75 root.paddingX/paddingY/gap = x4/x3_5/x3_5 (16px/14px/14px) */
|
|
73
|
+
border: none; margin: 0; cursor: pointer; text-align: start;
|
|
74
|
+
background: ${tokens_1.contract.bgNeutralWeak}; /* menu-sheet-item.yaml:71 root.color = $color.bg.neutral-weak */
|
|
75
|
+
font-family: inherit;
|
|
76
|
+
}
|
|
77
|
+
& .crisp-menusheet-item:hover,
|
|
78
|
+
& .crisp-menusheet-item:active { background: color-mix(in srgb, ${tokens_1.contract.fgPrimary} 6%, ${tokens_1.contract.bgNeutralWeak}); } /* menu-sheet-item.yaml:90-91 pressed.root.color = $color.bg.neutral-weak-pressed, approximated as a tint over the base fill */
|
|
79
|
+
& .crisp-menusheet-item:active .crisp-menusheet-item-content { transform: scale(0.98); } /* menu-sheet-item.yaml:92 pressed.root.scaleScope = content */
|
|
80
|
+
& .crisp-menusheet-item-icon { flex-shrink: 0; display: inline-flex; width: 22px; height: 22px; } /* menu-sheet-item.yaml:77 prefixIcon.size */
|
|
81
|
+
& .crisp-menusheet-item-content {
|
|
82
|
+
flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column;
|
|
83
|
+
gap: ${tokens_1.contract.space["0.5"]}; /* menu-sheet-item.yaml:79 content.gap = $dimension.x0_5 (2px) */
|
|
84
|
+
transition: transform 150ms cubic-bezier(0,0,.15,1);
|
|
85
|
+
}
|
|
86
|
+
& .crisp-menusheet-item-label {
|
|
87
|
+
font-size: ${tokens_1.contract.text.xl}; line-height: 22px; font-weight: ${tokens_1.contract.weight.regular}; /* menu-sheet-item.yaml:81-83 label.fontSize/lineHeight/fontWeight = $font-size.t5/$line-height.t5/regular */
|
|
88
|
+
}
|
|
89
|
+
& .crisp-menusheet-item-description {
|
|
90
|
+
color: ${tokens_1.contract.fgNeutralSubtle}; /* menu-sheet-item.yaml:88 description.color = $color.fg.neutral-subtle */
|
|
91
|
+
font-size: ${tokens_1.contract.text.md}; line-height: 18px; font-weight: ${tokens_1.contract.weight.medium}; /* menu-sheet-item.yaml:85-87 description.fontSize/lineHeight/fontWeight = $font-size.t3/$line-height.t3/medium */
|
|
92
|
+
}
|
|
93
|
+
& .crisp-menusheet-item[data-tone="neutral"] .crisp-menusheet-item-icon,
|
|
94
|
+
& .crisp-menusheet-item[data-tone="neutral"] .crisp-menusheet-item-label { color: ${tokens_1.contract.fgPrimary}; } /* menu-sheet-item.yaml:93-98 tone=neutral */
|
|
95
|
+
& .crisp-menusheet-item[data-tone="critical"] .crisp-menusheet-item-icon,
|
|
96
|
+
& .crisp-menusheet-item[data-tone="critical"] .crisp-menusheet-item-label { color: ${tokens_1.contract.fgDanger}; } /* menu-sheet-item.yaml:99-104 tone=critical */
|
|
97
|
+
`,
|
|
98
|
+
variants: {},
|
|
99
|
+
defaultVariants: {},
|
|
100
|
+
compoundVariants: [],
|
|
101
|
+
});
|
|
102
|
+
exports.menusheetRecipe = (0, core_1.resolveRecipe)(config);
|
|
103
|
+
exports.default = config;
|
|
@@ -71,7 +71,7 @@ function MultiSelect({ options, value: valueProp, defaultValue, onValueChange, n
|
|
|
71
71
|
// The id belongs on the trigger, not the wrapper: `<label for>` only names a
|
|
72
72
|
// LABELABLE element, so an id left on the root div meant a FormField label
|
|
73
73
|
// above this control named nothing (#247). Select already did this.
|
|
74
|
-
id, placeholder: placeholderProp, searchable = true, searchPlaceholder: searchPlaceholderProp, emptyLabel: emptyLabelProp, disabled, readOnly, invalid, size = "
|
|
74
|
+
id, placeholder: placeholderProp, searchable = true, searchPlaceholder: searchPlaceholderProp, emptyLabel: emptyLabelProp, disabled, readOnly, invalid, size = "small", variant = "default", className, "aria-label": ariaLabel, ...rest }) {
|
|
75
75
|
// crisp/#471: same resolution order as Select's own.
|
|
76
76
|
const placeholder = (0, core_1.useCrispMessage)(placeholderProp, "Select…");
|
|
77
77
|
const searchPlaceholder = (0, core_1.useCrispMessage)(searchPlaceholderProp, "Search…");
|
|
@@ -222,7 +222,7 @@ id, placeholder: placeholderProp, searchable = true, searchPlaceholder: searchPl
|
|
|
222
222
|
}, children: [(0, jsx_runtime_1.jsx)("span", { className: "crisp-multiselect-chips", children: selectedOptions.length === 0 ? ((0, jsx_runtime_1.jsx)("span", { className: "crisp-multiselect-placeholder", children: placeholder })) : (selectedOptions.map((o) => (0, jsx_runtime_1.jsx)(Chip, { opt: o }, o.value))) }), (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronDown, { className: "crisp-multiselect-chevron", size: 16, strokeWidth: 1.75 })] }), open && ((0, jsx_runtime_1.jsx)(core_1.FloatingPortal, { sourceRef: rootRef, children: (0, jsx_runtime_1.jsxs)("div", { ref: listboxRef, id: listboxId, className: "crisp-multiselect-listbox", role: "listbox", "aria-multiselectable": "true", "aria-label": ariaLabel, "data-up": up ? "" : undefined, style: pos, children: [searchable && ((0, jsx_runtime_1.jsxs)("div", { className: "crisp-multiselect-search", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Search, { size: 16, strokeWidth: 1.75 }), (0, jsx_runtime_1.jsx)("input", { ref: searchRef, type: "text", placeholder: searchPlaceholder, "aria-label": searchPlaceholder, "aria-controls": listboxId, "aria-activedescendant": activeId, value: query, onChange: (e) => {
|
|
223
223
|
setQuery(e.target.value);
|
|
224
224
|
setHighlight(0);
|
|
225
|
-
}, onKeyDown: onNavKeys })] })), filtered.length === 0 ? ((0, jsx_runtime_1.jsx)("div", { className: "crisp-multiselect-empty", children: emptyLabel })) : (filtered.map((opt, i) => {
|
|
225
|
+
}, onKeyDown: onNavKeys })] })), searchable && (0, jsx_runtime_1.jsx)("div", { className: "crisp-multiselect-divider" }), filtered.length === 0 ? ((0, jsx_runtime_1.jsx)("div", { className: "crisp-multiselect-empty", children: emptyLabel })) : (filtered.map((opt, i) => {
|
|
226
226
|
const sel = selectedSet.has(opt.value);
|
|
227
227
|
// #343: a header renders once per run of same-`group`
|
|
228
228
|
// options, so consecutive rows sharing a group render one
|
|
@@ -16,10 +16,14 @@ const tokens_1 = require("../../tokens");
|
|
|
16
16
|
// styling (shared with RecordsTable/DataTable elsewhere), the checkbox-style
|
|
17
17
|
// selected indicator, and the toggle-without-closing interaction.
|
|
18
18
|
//
|
|
19
|
-
// The search row
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
//
|
|
19
|
+
// The search row now follows seed too (owner decision, 2026-09-26: Select
|
|
20
|
+
// AND MultiSelect both follow seed-design, superseding #481's old Attio
|
|
21
|
+
// lock on this row's geometry -- crisp/#904's MultiSelect follow-up). It
|
|
22
|
+
// reads the exact same composed @seed-design/rootage `text-input.yaml`
|
|
23
|
+
// (variant=outline,size=medium) values #913 resolved for Select's own
|
|
24
|
+
// search row -- see that recipe's matching comment for the full citation.
|
|
25
|
+
// `build/spec-check.mjs`'s guard was rewritten to assert these seed values
|
|
26
|
+
// instead of the old 36px Attio measurement.
|
|
23
27
|
//
|
|
24
28
|
// The listbox portals to `document.body` (#339), so its rules live in
|
|
25
29
|
// `globalCss`, not nested under `&` -- see Select's own recipe for the full
|
|
@@ -64,13 +68,19 @@ const config = (0, core_1.defineRecipe)({
|
|
|
64
68
|
}
|
|
65
69
|
.crisp-multiselect-listbox[data-up] { transform-origin: bottom left; }
|
|
66
70
|
.crisp-multiselect-listbox { @starting-style { opacity: 0; transform: scale(0.95); } }
|
|
67
|
-
/*
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
+
/* Seed geometry (build/spec-check.mjs guards these four properties
|
|
72
|
+
directly), superseding #481's old Attio lock -- see this file's
|
|
73
|
+
header comment and Select's matching .crisp-select-search comment
|
|
74
|
+
for the full citation. An ordinary flex child of the listbox, so its
|
|
75
|
+
own 8px gap provides the inset/seam on every side, same as Select. */
|
|
76
|
+
.crisp-multiselect-search { display: flex; align-items: center; gap: ${tokens_1.contract.space["2"]}; height: ${tokens_1.contract.size.selectTriggerMd}; flex-shrink: 0; padding: 0 ${tokens_1.contract.space["3.5"]}; border: 1px solid ${tokens_1.contract.strokeSelectTrigger}; border-radius: ${tokens_1.contract.radius.md}; color: ${tokens_1.contract.fgNeutralMuted}; }
|
|
71
77
|
.crisp-multiselect-search svg { flex-shrink: 0; }
|
|
72
|
-
.crisp-multiselect-search input { flex: 1; min-width: 0; border: none; outline: none; background: none; font-family: inherit; font-size: ${tokens_1.contract.text.lg}; color: ${tokens_1.contract.fgSelectNeutral}; }
|
|
78
|
+
.crisp-multiselect-search input { flex: 1; min-width: 0; border: none; outline: none; background: none; font-family: inherit; font-size: ${tokens_1.contract.text.lg}; line-height: 19px; color: ${tokens_1.contract.fgSelectNeutral}; }
|
|
73
79
|
.crisp-multiselect-search input::placeholder { color: ${tokens_1.contract.fgSelectPlaceholder}; }
|
|
80
|
+
/* Same header/body seam as Select's own .crisp-select-divider -- see
|
|
81
|
+
that recipe's comment for the seed source (select.yaml's divider
|
|
82
|
+
slot, stroke.neutral-muted). */
|
|
83
|
+
.crisp-multiselect-divider { flex-shrink: 0; height: 1px; margin-inline: ${tokens_1.contract.space["4"]}; background: ${tokens_1.contract.strokeSelectDivider}; }
|
|
74
84
|
/* Same groupLabel treatment as Select's (13px/regular/neutral-subtle). */
|
|
75
85
|
.crisp-multiselect-label { padding: ${tokens_1.contract.space["2"]} ${tokens_1.contract.space["4"]} 0; font-size: ${tokens_1.contract.text.md}; line-height: 18px; font-weight: ${tokens_1.contract.weight.regular}; color: ${tokens_1.contract.fgNeutralSubtle}; }
|
|
76
86
|
.crisp-multiselect-option {
|
|
@@ -99,9 +109,32 @@ const config = (0, core_1.defineRecipe)({
|
|
|
99
109
|
.crisp-recordstable-tag[data-tone="orange"] { background: color-mix(in srgb, ${tokens_1.contract.bgOrange} 16%, transparent); color: color-mix(in srgb, ${tokens_1.contract.bgOrange} 82%, ${tokens_1.contract.fgPrimary}); box-shadow: inset 0 0 0 1px color-mix(in srgb, ${tokens_1.contract.bgOrange} 30%, transparent); }
|
|
100
110
|
`,
|
|
101
111
|
variants: {
|
|
102
|
-
//
|
|
103
|
-
//
|
|
112
|
+
// Owner's follow-up after #867/#879/#882 (crisp/#889): SIZE is Attio's
|
|
113
|
+
// again, LOOK stays seed's -- see Select's own recipe comment for the
|
|
114
|
+
// full reasoning (measured live in GRC's "New employee" dialog: a 40px
|
|
115
|
+
// trigger next to a 32px `Input` visibly mismatched). `small` is the new
|
|
116
|
+
// default: `c.size.menuRow` (32px) and the original, pre-#882
|
|
117
|
+
// Attio-measured asymmetric padding for a chip-wrapping trigger.
|
|
118
|
+
// `medium`/`large` (seed's own 40/52px) stay, opt-in.
|
|
119
|
+
//
|
|
120
|
+
// crisp/#904 (MultiSelect follow-up, briefly moved then reverted, same
|
|
121
|
+
// ruling as Select's own): `small`'s `gap` moved from `space["1"]`
|
|
122
|
+
// (4px) to `space["2"]` (8px), matching `medium`/`large`'s seed gap --
|
|
123
|
+
// team-lead's review ruling on Select's matching change reversed it:
|
|
124
|
+
// padding AND gap are SPACING, and the owner's direction is "sizes/
|
|
125
|
+
// spacing from Attio, look from seed", not seed's literal gap number.
|
|
126
|
+
// Back to `space["1"]` (4px), the original pre-#882 Attio-measured
|
|
127
|
+
// value. `padding` was never touched either way: the pre-#882,
|
|
128
|
+
// Attio-measured asymmetric padding a chip-wrapping trigger needs
|
|
129
|
+
// (chips sit close to the start edge, the chevron gets more room at
|
|
130
|
+
// the end) -- seed has no multi-value combobox of its own to source a
|
|
131
|
+
// replacement number from (see this file's header comment) even if
|
|
132
|
+
// the ruling had gone the other way.
|
|
104
133
|
size: {
|
|
134
|
+
small: `
|
|
135
|
+
& .crisp-multiselect-trigger { min-height: ${tokens_1.contract.size.menuRow}; border-radius: ${tokens_1.contract.radius.md}; gap: ${tokens_1.contract.space["1"]}; padding: ${tokens_1.contract.space["0.5"]} ${tokens_1.contract.space["1.5"]} ${tokens_1.contract.space["0.5"]} ${tokens_1.contract.space["2"]}; }
|
|
136
|
+
& .crisp-multiselect-chevron { width: ${tokens_1.contract.size.icon}; height: ${tokens_1.contract.size.icon}; }
|
|
137
|
+
`,
|
|
105
138
|
medium: `
|
|
106
139
|
& .crisp-multiselect-trigger { min-height: ${tokens_1.contract.size.selectTriggerMd}; border-radius: ${tokens_1.contract.radius.md}; gap: ${tokens_1.contract.space["2"]}; padding: ${tokens_1.contract.space["1"]} ${tokens_1.contract.space["3.5"]}; }
|
|
107
140
|
& .crisp-multiselect-chevron { width: ${tokens_1.contract.size.icon}; height: ${tokens_1.contract.size.icon}; }
|
|
@@ -134,7 +167,7 @@ const config = (0, core_1.defineRecipe)({
|
|
|
134
167
|
`,
|
|
135
168
|
},
|
|
136
169
|
},
|
|
137
|
-
defaultVariants: { size: "
|
|
170
|
+
defaultVariants: { size: "small", invalid: false, variant: "default" },
|
|
138
171
|
compoundVariants: [],
|
|
139
172
|
});
|
|
140
173
|
exports.multiselectRecipe = Object.assign((0, core_1.resolveRecipe)(config), {
|
|
@@ -4,11 +4,12 @@ exports.Progress = Progress;
|
|
|
4
4
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
5
|
const progress_recipe_1 = require("./progress.recipe");
|
|
6
6
|
/**
|
|
7
|
-
* A progress bar — a continuous brand fill,
|
|
8
|
-
* `segments` is set (the "Getting started 3/6" pattern)
|
|
9
|
-
* `
|
|
7
|
+
* A progress bar — a continuous brand fill, a segmented row of pills when
|
|
8
|
+
* `segments` is set (the "Getting started 3/6" pattern), or a small ring
|
|
9
|
+
* (`variant="circular"`, a character-counter's own progress). Reports its
|
|
10
|
+
* state via `role="progressbar"` and the ARIA value attributes.
|
|
10
11
|
*/
|
|
11
|
-
function Progress({ value, max = 100, segments, "aria-label": ariaLabel, className, ...rest }) {
|
|
12
|
+
function Progress({ value, max = 100, segments, variant = "linear", size = 18, "aria-label": ariaLabel, className, ...rest }) {
|
|
12
13
|
const pct = Math.max(0, Math.min(100, (value / max) * 100));
|
|
13
14
|
const aria = {
|
|
14
15
|
role: "progressbar",
|
|
@@ -17,6 +18,22 @@ function Progress({ value, max = 100, segments, "aria-label": ariaLabel, classNa
|
|
|
17
18
|
"aria-valuemax": max,
|
|
18
19
|
"aria-label": ariaLabel,
|
|
19
20
|
};
|
|
21
|
+
if (variant === "circular") {
|
|
22
|
+
// Measured live: an 18x18 SVG, r=7.4, stroke-width 1.6 (a 0.6px margin on
|
|
23
|
+
// each side) — a two-circle ring (a soft-tinted track + a brand arc),
|
|
24
|
+
// the same track tint Badge/Callout's own `soft(bgBrandSolid, 14%)`
|
|
25
|
+
// formula already gives.
|
|
26
|
+
const strokeWidth = size * (1.6 / 18);
|
|
27
|
+
const r = size / 2 - strokeWidth;
|
|
28
|
+
const circumference = 2 * Math.PI * r;
|
|
29
|
+
const offset = circumference * (1 - pct / 100);
|
|
30
|
+
// The measured sample was at value=0 (a full, invisible offset either
|
|
31
|
+
// way), so the arc's start point couldn't be read off that one DOM
|
|
32
|
+
// snapshot -- 12 o'clock is the conventional start for this shape and
|
|
33
|
+
// isn't itself in tension with anything measured, but isn't independently
|
|
34
|
+
// confirmed at a nonzero value either.
|
|
35
|
+
return ((0, jsx_runtime_1.jsx)("div", { ...aria, className: (0, progress_recipe_1.progressRecipe)({ className }), ...rest, "data-variant": "circular", children: (0, jsx_runtime_1.jsxs)("svg", { width: size, height: size, viewBox: `0 0 ${size} ${size}`, "aria-hidden": "true", children: [(0, jsx_runtime_1.jsx)("circle", { className: "crisp-progress-circle-track", r: r, cx: size / 2, cy: size / 2, strokeWidth: strokeWidth, fill: "transparent" }), (0, jsx_runtime_1.jsx)("circle", { className: "crisp-progress-circle-fill", r: r, cx: size / 2, cy: size / 2, strokeWidth: strokeWidth, fill: "transparent", strokeDasharray: circumference, strokeDashoffset: offset, strokeLinecap: "round", transform: `rotate(-90 ${size / 2} ${size / 2})` })] }) }));
|
|
36
|
+
}
|
|
20
37
|
if (segments && segments > 0) {
|
|
21
38
|
const filled = Math.round((pct / 100) * segments);
|
|
22
39
|
return ((0, jsx_runtime_1.jsx)("div", { ...aria, className: (0, progress_recipe_1.progressRecipe)({ className }), ...rest, "data-segmented": "", children: Array.from({ length: segments }, (_, i) => ((0, jsx_runtime_1.jsx)("span", { className: "crisp-progress-seg", "data-filled": i < filled ? "" : undefined }, i))) }));
|
|
@@ -52,6 +52,36 @@ const spec = (0, core_1.defineSpec)({
|
|
|
52
52
|
selector: "&[data-segmented] .crisp-progress-seg[data-filled]",
|
|
53
53
|
decls: { background: tokens_1.contract.bgBrandSolid },
|
|
54
54
|
},
|
|
55
|
+
// crisp#634: measured live on Attio's own Settings > Profile
|
|
56
|
+
// character-counter ring — the track tint is the same
|
|
57
|
+
// `soft(bgBrandSolid, 14%)` formula Badge/Callout already use (measured
|
|
58
|
+
// rgb(229,238,255), within the same noise this codebase already treats
|
|
59
|
+
// as equivalent), the arc is the plain brand fill.
|
|
60
|
+
{
|
|
61
|
+
selector: '&[data-variant="circular"]',
|
|
62
|
+
decls: {
|
|
63
|
+
display: "inline-flex",
|
|
64
|
+
height: "auto",
|
|
65
|
+
borderRadius: "0",
|
|
66
|
+
background: "transparent",
|
|
67
|
+
overflow: "visible",
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
slot: "circleTrack",
|
|
72
|
+
selector: "& .crisp-progress-circle-track",
|
|
73
|
+
decls: {
|
|
74
|
+
stroke: `color-mix(in srgb, ${tokens_1.contract.bgBrandSolid} 14%, transparent)`,
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
slot: "circleFill",
|
|
79
|
+
selector: "& .crisp-progress-circle-fill",
|
|
80
|
+
decls: {
|
|
81
|
+
stroke: tokens_1.contract.bgBrandSolid,
|
|
82
|
+
transition: "stroke-dashoffset 0.25s ease",
|
|
83
|
+
},
|
|
84
|
+
},
|
|
55
85
|
],
|
|
56
86
|
});
|
|
57
87
|
exports.spec = spec;
|
|
@@ -55,7 +55,7 @@ function optionText(o) {
|
|
|
55
55
|
* move, Enter/Space pick, Esc close, Home/End jump, opens upward when tight.
|
|
56
56
|
* Controlled or uncontrolled.
|
|
57
57
|
*/
|
|
58
|
-
function Select({ options, value, defaultValue, onValueChange, name, required, placeholder: placeholderProp, variant = "default", width = "full", size = "
|
|
58
|
+
function Select({ options, value, defaultValue, onValueChange, name, required, placeholder: placeholderProp, variant = "default", width = "full", size = "small", searchable, searchPlaceholder: searchPlaceholderProp, onSearchChange, emptyLabel: emptyLabelProp, disabled, readOnly, invalid, className, "aria-label": ariaLabel, id, ...rest }) {
|
|
59
59
|
// crisp/#471: an explicit prop always wins; missing, it reads the nearest
|
|
60
60
|
// CrispMessagesProvider's translation of the English default, or that
|
|
61
61
|
// default itself with no provider — unchanged from before this existed.
|
|
@@ -217,11 +217,11 @@ function Select({ options, value, defaultValue, onValueChange, name, required, p
|
|
|
217
217
|
return ((0, jsx_runtime_1.jsxs)("div", { ref: rootRef, className: (0, select_recipe_1.selectRecipe)({ variant, width, size, invalid, className }), ...rest, children: [name && (selected || required) && ((0, jsx_runtime_1.jsx)("input", { className: "crisp-formvalue", style: core_1.formValueStyle, name: name, value: selected ?? "", required: required, tabIndex: -1, "aria-hidden": "true",
|
|
218
218
|
// controlled by the listbox above; `readOnly` would bar it from
|
|
219
219
|
// validation, which is the only reason this input is not hidden
|
|
220
|
-
onChange: () => { } })), (0, jsx_runtime_1.jsxs)("button", { ref: triggerRef, type: "button", id: id, role: "combobox", "aria-haspopup": "listbox", "aria-expanded": open, "aria-controls": open ? listboxId : undefined, "aria-activedescendant": searchable ? undefined : activeId, "aria-label": ariaLabel, "aria-invalid": invalid === true || undefined, "aria-readonly": readOnly ? "true" : undefined, disabled: disabled, "data-open": open ? "" : undefined, className: "crisp-select-trigger", onClick: () => (open ? setOpen(false) : openMenu()), onKeyDown: onTriggerKeyDown, children: [(0, jsx_runtime_1.jsx)("span", { className: "crisp-select-value", children: selectedOption ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [selectedOption.color && ((0, jsx_runtime_1.jsx)("span", { className: "crisp-select-option-dot", style: { background: selectedOption.color } })), selectedOption.triggerLabel ?? selectedOption.label] })) : ((0, jsx_runtime_1.jsx)("span", { className: "crisp-select-placeholder", children: placeholder })) }), (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronDown, { className: "crisp-select-chevron", size: 16, strokeWidth: 1.75 })] }), open && ((0, jsx_runtime_1.jsx)(core_1.FloatingPortal, { sourceRef: rootRef, children: (0, jsx_runtime_1.jsxs)("div", { ref: listboxRef, id: listboxId, className: "crisp-select-listbox", role: "listbox", "aria-label": ariaLabel, "data-up": up ? "" : undefined, style: pos, children: [searchable && ((0, jsx_runtime_1.jsxs)("div", { className: "crisp-select-search", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Search, { size:
|
|
220
|
+
onChange: () => { } })), (0, jsx_runtime_1.jsxs)("button", { ref: triggerRef, type: "button", id: id, role: "combobox", "aria-haspopup": "listbox", "aria-expanded": open, "aria-controls": open ? listboxId : undefined, "aria-activedescendant": searchable ? undefined : activeId, "aria-label": ariaLabel, "aria-invalid": invalid === true || undefined, "aria-readonly": readOnly ? "true" : undefined, disabled: disabled, "data-open": open ? "" : undefined, className: "crisp-select-trigger", onClick: () => (open ? setOpen(false) : openMenu()), onKeyDown: onTriggerKeyDown, children: [(0, jsx_runtime_1.jsx)("span", { className: "crisp-select-value", children: selectedOption ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [selectedOption.color && ((0, jsx_runtime_1.jsx)("span", { className: "crisp-select-option-dot", style: { background: selectedOption.color } })), selectedOption.triggerLabel ?? selectedOption.label] })) : ((0, jsx_runtime_1.jsx)("span", { className: "crisp-select-placeholder", children: placeholder })) }), (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronDown, { className: "crisp-select-chevron", size: 16, strokeWidth: 1.75 })] }), open && ((0, jsx_runtime_1.jsx)(core_1.FloatingPortal, { sourceRef: rootRef, children: (0, jsx_runtime_1.jsxs)("div", { ref: listboxRef, id: listboxId, className: "crisp-select-listbox", role: "listbox", "aria-label": ariaLabel, "data-up": up ? "" : undefined, style: pos, children: [searchable && ((0, jsx_runtime_1.jsxs)("div", { className: "crisp-select-search", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Search, { size: 16, strokeWidth: 1.75 }), (0, jsx_runtime_1.jsx)("input", { ref: searchRef, type: "text", placeholder: searchPlaceholder, "aria-label": searchPlaceholder, "aria-controls": listboxId, "aria-activedescendant": activeId, value: query, onChange: (e) => {
|
|
221
221
|
setQuery(e.target.value);
|
|
222
222
|
setHighlight(0);
|
|
223
223
|
onSearchChange?.(e.target.value);
|
|
224
|
-
}, onKeyDown: onNavKeys })] })), filtered.length === 0 ? ((0, jsx_runtime_1.jsx)("div", { className: "crisp-select-empty", children: emptyLabel })) : (filtered.map((opt, i) => {
|
|
224
|
+
}, onKeyDown: onNavKeys })] })), searchable && (0, jsx_runtime_1.jsx)("div", { className: "crisp-select-divider" }), filtered.length === 0 ? ((0, jsx_runtime_1.jsx)("div", { className: "crisp-select-empty", children: emptyLabel })) : (filtered.map((opt, i) => {
|
|
225
225
|
const isSel = opt.value === selected;
|
|
226
226
|
// #343: a header renders once per run of same-`group`
|
|
227
227
|
// options, so consecutive rows sharing a group render one
|