@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
|
@@ -29,11 +29,11 @@ const tokens_1 = require("../../tokens");
|
|
|
29
29
|
// pattern with no precedent anywhere else in the repo, for one component.
|
|
30
30
|
// The enter animation (scale 0.95→1, opacity 0→1, seed's own curve) IS
|
|
31
31
|
// reproduced below.
|
|
32
|
-
// - A
|
|
33
|
-
// crisp or GRC groups options with an explicit separator
|
|
34
|
-
// AUTHORING's rule against building what nothing consumes
|
|
35
|
-
// too.
|
|
36
|
-
//
|
|
32
|
+
// - A general-purpose `divider` slot between arbitrary option groups:
|
|
33
|
+
// nothing in crisp or GRC groups options with an explicit separator
|
|
34
|
+
// today, and AUTHORING's rule against building what nothing consumes
|
|
35
|
+
// applies to CSS too. (`select.yaml`'s resolved value IS used below, just
|
|
36
|
+
// for one specific seam — see `.crisp-select-divider`'s own comment.)
|
|
37
37
|
const config = (0, core_1.defineRecipe)({
|
|
38
38
|
name: "select",
|
|
39
39
|
base: `
|
|
@@ -88,10 +88,34 @@ const config = (0, core_1.defineRecipe)({
|
|
|
88
88
|
}
|
|
89
89
|
.crisp-select-listbox[data-up] { transform-origin: bottom left; }
|
|
90
90
|
.crisp-select-listbox { @starting-style { opacity: 0; transform: scale(0.95); } }
|
|
91
|
-
|
|
91
|
+
/* Composed from @seed-design/rootage's text-input.yaml
|
|
92
|
+
(variant=outline,size=medium) -- seed's Select has no search field of
|
|
93
|
+
its own to resolve this from (see the file header), so this reuses
|
|
94
|
+
the SAME tokens #867 already resolved for the trigger at this size:
|
|
95
|
+
minHeight x10 (${tokens_1.contract.size.selectTriggerMd}), paddingX x3_5
|
|
96
|
+
(${tokens_1.contract.space["3.5"]}), gap x2 (${tokens_1.contract.space["2"]}), cornerRadius r2
|
|
97
|
+
(${tokens_1.contract.radius.md}), strokeColor stroke.neutral-weak
|
|
98
|
+
(${tokens_1.contract.strokeSelectTrigger}) -- a real bordered field sitting flush
|
|
99
|
+
inside the listbox's own 8px padding, replacing the old bare
|
|
100
|
+
34px/no-border/bleeds-to-the-edge strip (crisp/#904). */
|
|
101
|
+
.crisp-select-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}; }
|
|
92
102
|
.crisp-select-search svg { flex-shrink: 0; }
|
|
93
|
-
.crisp-select-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}; }
|
|
103
|
+
.crisp-select-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}; }
|
|
94
104
|
.crisp-select-search input::placeholder { color: ${tokens_1.contract.fgSelectPlaceholder}; }
|
|
105
|
+
/* select.yaml's own divider slot (marginX x4, height 1px, colour
|
|
106
|
+
stroke.neutral-muted -- described upstream as "the line marking a
|
|
107
|
+
boundary between semantic units: between sections, between content
|
|
108
|
+
and an action area, between a header and a body" -- exactly the
|
|
109
|
+
search-field/option-list boundary crisp#904's review flagged as
|
|
110
|
+
reading too tight). Not the general-purpose separator #867 declined
|
|
111
|
+
to build (nothing groups options with one) -- scoped to this one
|
|
112
|
+
specific header/body seam, which now exists because searchable
|
|
113
|
+
does. Sits as an ordinary flex child, so the listbox's own 8px gap
|
|
114
|
+
still applies on both sides of it -- total visual seam is
|
|
115
|
+
gap+1px+gap (17px), deliberately more than the 8px between two
|
|
116
|
+
plain option rows, so it reads as a section break, not a wider list
|
|
117
|
+
gap. */
|
|
118
|
+
.crisp-select-divider { flex-shrink: 0; height: 1px; margin-inline: ${tokens_1.contract.space["4"]}; background: ${tokens_1.contract.strokeSelectDivider}; }
|
|
95
119
|
.crisp-select-empty { padding: ${tokens_1.contract.space["3"]} ${tokens_1.contract.space["4"]}; text-align: center; font-size: ${tokens_1.contract.text.md}; color: ${tokens_1.contract.fgNeutralSubtle}; }
|
|
96
120
|
/* select.yaml's groupLabel -- font-size/line-height/weight are the same
|
|
97
121
|
across sizes, only paddingY differs (medium 8px, large 10px). */
|
|
@@ -124,9 +148,38 @@ const config = (0, core_1.defineRecipe)({
|
|
|
124
148
|
full: `width: 100%;`,
|
|
125
149
|
auto: `width: auto;`,
|
|
126
150
|
},
|
|
127
|
-
//
|
|
128
|
-
//
|
|
151
|
+
// Owner's follow-up after #867/#879 (crisp/#889): the LOOK stays seed's
|
|
152
|
+
// (border, listbox, radius, shadow, items, states, all unchanged below),
|
|
153
|
+
// but the HEIGHT is Attio's again -- a 40px trigger next to a 32px
|
|
154
|
+
// `Input` in the same form visibly mismatched (measured live in GRC's
|
|
155
|
+
// own "New employee" dialog). `small` is the new default: Attio's
|
|
156
|
+
// original 32px `c.size.menuRow`. `medium`/`large` (seed's own 40/52px)
|
|
157
|
+
// stay, opt-in. Item rows are IDENTICAL across all three -- only the
|
|
158
|
+
// trigger differs; only it was ever the mismatch.
|
|
159
|
+
//
|
|
160
|
+
// crisp/#904 (follow-up, then reverted): `small`'s gap/padding-inline
|
|
161
|
+
// briefly moved to seed's literal `select-trigger.yaml` `size=medium`
|
|
162
|
+
// values (8px/14px), on the theory that even the 32px trigger should
|
|
163
|
+
// read seed's numbers verbatim. Team-lead ruling on that PR's review:
|
|
164
|
+
// padding and gap are SPACING, and the owner's actual direction is
|
|
165
|
+
// "sizes/spacing from Attio, look from seed" -- Select and Input sit
|
|
166
|
+
// side by side in every form, so spacing follows Input's measured
|
|
167
|
+
// values here, same as `medium`/`large` below follow seed's. Reverted
|
|
168
|
+
// to `space["1.5"]`/`space["2.5"]` (6px/10px, `Input`'s own gap/padding
|
|
169
|
+
// at this row height) -- everything else `small` already got from seed
|
|
170
|
+
// (fill/stroke/border/radius/chevron/colours/states, see this file's
|
|
171
|
+
// other comments) is unaffected and stays seed's.
|
|
129
172
|
size: {
|
|
173
|
+
small: `
|
|
174
|
+
& .crisp-select-trigger { height: ${tokens_1.contract.size.menuRow}; border-radius: ${tokens_1.contract.radius.md}; gap: ${tokens_1.contract.space["1.5"]}; padding-inline: ${tokens_1.contract.space["2.5"]}; }
|
|
175
|
+
& .crisp-select-value, & .crisp-select-placeholder { font-size: ${tokens_1.contract.text.lg}; line-height: 20px; }
|
|
176
|
+
& .crisp-select-chevron { width: ${tokens_1.contract.size.icon}; height: ${tokens_1.contract.size.icon}; }
|
|
177
|
+
.crisp-select-option { gap: ${tokens_1.contract.space["2"]}; }
|
|
178
|
+
.crisp-select-option-label { font-size: ${tokens_1.contract.text.lg}; line-height: 19px; }
|
|
179
|
+
.crisp-select-option-description { font-size: ${tokens_1.contract.text.sm}; line-height: 16px; }
|
|
180
|
+
.crisp-select-option-check { width: ${tokens_1.contract.space["3"]}; height: ${tokens_1.contract.space["3"]}; }
|
|
181
|
+
.crisp-select-label { padding-top: ${tokens_1.contract.space["2"]}; }
|
|
182
|
+
`,
|
|
130
183
|
medium: `
|
|
131
184
|
& .crisp-select-trigger { height: ${tokens_1.contract.size.selectTriggerMd}; border-radius: ${tokens_1.contract.radius.md}; gap: ${tokens_1.contract.space["2"]}; padding-inline: ${tokens_1.contract.space["3.5"]}; }
|
|
132
185
|
& .crisp-select-value, & .crisp-select-placeholder { font-size: ${tokens_1.contract.text.lg}; line-height: 19px; }
|
|
@@ -178,7 +231,7 @@ const config = (0, core_1.defineRecipe)({
|
|
|
178
231
|
},
|
|
179
232
|
defaultVariants: {
|
|
180
233
|
width: "full",
|
|
181
|
-
size: "
|
|
234
|
+
size: "small",
|
|
182
235
|
invalid: false,
|
|
183
236
|
variant: "default",
|
|
184
237
|
},
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.snackbarRecipe = exports.useSnackbar = exports.SnackbarProvider = void 0;
|
|
4
|
+
var snackbar_1 = require("./snackbar");
|
|
5
|
+
Object.defineProperty(exports, "SnackbarProvider", { enumerable: true, get: function () { return snackbar_1.SnackbarProvider; } });
|
|
6
|
+
Object.defineProperty(exports, "useSnackbar", { enumerable: true, get: function () { return snackbar_1.useSnackbar; } });
|
|
7
|
+
var snackbar_recipe_1 = require("./snackbar.recipe");
|
|
8
|
+
Object.defineProperty(exports, "snackbarRecipe", { enumerable: true, get: function () { return snackbar_recipe_1.snackbarRecipe; } });
|
|
@@ -0,0 +1,121 @@
|
|
|
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.SnackbarProvider = SnackbarProvider;
|
|
37
|
+
exports.useSnackbar = useSnackbar;
|
|
38
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
39
|
+
const lucide_react_1 = require("lucide-react");
|
|
40
|
+
const React = __importStar(require("react"));
|
|
41
|
+
const core_1 = require("../../core");
|
|
42
|
+
const snackbar_recipe_1 = require("./snackbar.recipe");
|
|
43
|
+
const SnackbarContext = React.createContext(null);
|
|
44
|
+
const ICONS = {
|
|
45
|
+
positive: lucide_react_1.CircleCheck,
|
|
46
|
+
critical: lucide_react_1.CircleAlert,
|
|
47
|
+
};
|
|
48
|
+
function SnackbarPill({ item, onClose, }) {
|
|
49
|
+
const Icon = ICONS[item.variant ?? "default"];
|
|
50
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: "crisp-snackbar", "data-variant": item.variant ?? "default", children: [(0, jsx_runtime_1.jsxs)("div", { className: "crisp-snackbar-content", children: [Icon != null && ((0, jsx_runtime_1.jsx)("span", { className: "crisp-snackbar-icon", children: (0, jsx_runtime_1.jsx)(Icon, { size: 24, strokeWidth: 1.75, "aria-hidden": "true" }) })), (0, jsx_runtime_1.jsx)("span", { className: "crisp-snackbar-message", children: item.message })] }), item.action && ((0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-snackbar-action", onClick: () => {
|
|
51
|
+
item.action?.onClick();
|
|
52
|
+
onClose();
|
|
53
|
+
}, children: item.action.label }))] }));
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Wrap your app in a `SnackbarProvider`, then call `useSnackbar().show({...})`
|
|
57
|
+
* from anywhere to queue a bottom-anchored snackbar. Only one shows at a time
|
|
58
|
+
* (seed-design's mobile pattern, crisp#911) -- a second call queues behind the
|
|
59
|
+
* first and shows once it dismisses. Auto-dismisses after `duration` (default
|
|
60
|
+
* 4s; 0 keeps it until `dismiss()` or its action fires -- there is no close
|
|
61
|
+
* button, matching seed's own anatomy, which has none).
|
|
62
|
+
*/
|
|
63
|
+
function SnackbarProvider({ children, duration = 4000, regionLabel: regionLabelProp, }) {
|
|
64
|
+
const regionLabel = (0, core_1.useCrispMessage)(regionLabelProp, "Notifications");
|
|
65
|
+
const [queue, setQueue] = React.useState([]);
|
|
66
|
+
const idRef = React.useRef(0);
|
|
67
|
+
const timer = React.useRef(null);
|
|
68
|
+
const regionRef = React.useRef(null);
|
|
69
|
+
const cameFrom = React.useRef(null);
|
|
70
|
+
const focusAfterDismiss = (0, core_1.useFocusAfterRemoval)(regionRef, ".crisp-snackbar-action", cameFrom);
|
|
71
|
+
React.useEffect(() => {
|
|
72
|
+
const onFocusIn = (e) => {
|
|
73
|
+
const target = e.target;
|
|
74
|
+
if (!target || regionRef.current?.contains(target))
|
|
75
|
+
return;
|
|
76
|
+
cameFrom.current = target;
|
|
77
|
+
};
|
|
78
|
+
document.addEventListener("focusin", onFocusIn);
|
|
79
|
+
return () => document.removeEventListener("focusin", onFocusIn);
|
|
80
|
+
}, []);
|
|
81
|
+
const dismiss = React.useCallback((id) => {
|
|
82
|
+
setQueue((list) => list.filter((s) => s.id !== id));
|
|
83
|
+
if (timer.current) {
|
|
84
|
+
clearTimeout(timer.current);
|
|
85
|
+
timer.current = null;
|
|
86
|
+
}
|
|
87
|
+
}, []);
|
|
88
|
+
const show = React.useCallback((o) => {
|
|
89
|
+
idRef.current += 1;
|
|
90
|
+
const id = idRef.current;
|
|
91
|
+
setQueue((list) => [...list, { ...o, id }]);
|
|
92
|
+
return id;
|
|
93
|
+
}, []);
|
|
94
|
+
// Only the FRONT of the queue is ever mounted, so its own auto-dismiss
|
|
95
|
+
// timer starts when it becomes visible, not when `show` was called.
|
|
96
|
+
const current = queue[0];
|
|
97
|
+
React.useEffect(() => {
|
|
98
|
+
if (!current)
|
|
99
|
+
return;
|
|
100
|
+
const d = current.duration ?? duration;
|
|
101
|
+
if (d <= 0)
|
|
102
|
+
return;
|
|
103
|
+
timer.current = setTimeout(() => dismiss(current.id), d);
|
|
104
|
+
return () => {
|
|
105
|
+
if (timer.current)
|
|
106
|
+
clearTimeout(timer.current);
|
|
107
|
+
};
|
|
108
|
+
}, [current, duration, dismiss]);
|
|
109
|
+
const api = React.useMemo(() => ({ show, dismiss }), [show, dismiss]);
|
|
110
|
+
return ((0, jsx_runtime_1.jsxs)(SnackbarContext.Provider, { value: api, children: [children, (0, jsx_runtime_1.jsx)("div", { className: (0, snackbar_recipe_1.snackbarRecipe)(), children: (0, jsx_runtime_1.jsx)("div", { ref: regionRef, className: "crisp-snackbar-region", role: "region", "aria-label": regionLabel, "aria-live": "polite", "aria-atomic": "false", children: current && ((0, jsx_runtime_1.jsx)(SnackbarPill, { item: current, onClose: () => {
|
|
111
|
+
focusAfterDismiss(0);
|
|
112
|
+
dismiss(current.id);
|
|
113
|
+
} }, current.id)) }) })] }));
|
|
114
|
+
}
|
|
115
|
+
SnackbarProvider.displayName = "SnackbarProvider";
|
|
116
|
+
function useSnackbar() {
|
|
117
|
+
const ctx = React.useContext(SnackbarContext);
|
|
118
|
+
if (!ctx)
|
|
119
|
+
throw new Error("useSnackbar must be used within a SnackbarProvider");
|
|
120
|
+
return ctx;
|
|
121
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.snackbarRecipe = void 0;
|
|
4
|
+
const core_1 = require("../../core");
|
|
5
|
+
const tokens_1 = require("../../tokens");
|
|
6
|
+
// A mobile-pattern snackbar — daangn/seed-design's
|
|
7
|
+
// packages/rootage/components/snackbar.yaml (dev@e1c486b904297f7b6a8f17b8523f732c37015d60),
|
|
8
|
+
// not Attio (no such component lives there; crisp#911). A single dark
|
|
9
|
+
// `bgNeutralInverted` pill, bottom-anchored, holding an optional tone-coloured
|
|
10
|
+
// prefix icon, a message, and an optional bold action. Every numeric value
|
|
11
|
+
// below cites its yaml line; sizes/radii already equal existing crisp tokens
|
|
12
|
+
// (crisp's space/radius scale was itself derived 2px-granular, same as
|
|
13
|
+
// seed's `dimension`/`radius` scales, so no new tokens were needed).
|
|
14
|
+
//
|
|
15
|
+
// Deviation from the spec's literal `actionButton.color: $color.fg.brand`
|
|
16
|
+
// (snackbar.yaml:128, seed's carrot-600 `#ff6600`): crisp's OWN brand token
|
|
17
|
+
// is TechPartners' verified blue (`bgBrandSolid`, `#0298d9`), not seed's
|
|
18
|
+
// orange (the identity migration takes seed's STRUCTURE, crisp's own colour
|
|
19
|
+
// -- `~/grc-parity/seed-identity-spec.md`). Computed contrast of
|
|
20
|
+
// `bgBrandSolid` on `bgNeutralInverted` (light theme: `#0298d9` on `#2a3038`)
|
|
21
|
+
// is 4.11:1 -- below AA's 4.5:1 for 14px text (bold 14px does not clear the
|
|
22
|
+
// large-text 3:1 threshold, which needs 18.66px). crisp has no brand step
|
|
23
|
+
// bright enough on this dark surface yet, so the action reads in
|
|
24
|
+
// `fgOnNeutralInverted` (the same token the message already uses) at bold
|
|
25
|
+
// weight instead -- distinguished by weight, not colour, until a dedicated
|
|
26
|
+
// token is measured.
|
|
27
|
+
const config = (0, core_1.defineRecipe)({
|
|
28
|
+
name: "snackbar",
|
|
29
|
+
base: `
|
|
30
|
+
& .crisp-snackbar-region {
|
|
31
|
+
position: fixed; z-index: 80; inset-inline: 0; bottom: 0;
|
|
32
|
+
display: flex; justify-content: center;
|
|
33
|
+
padding: ${tokens_1.contract.space["2"]}; /* snackbar.yaml:95-96 region.paddingX/paddingY = $dimension.x2 (8px) */
|
|
34
|
+
pointer-events: none;
|
|
35
|
+
}
|
|
36
|
+
& .crisp-snackbar {
|
|
37
|
+
pointer-events: auto; box-sizing: border-box;
|
|
38
|
+
display: flex; align-items: center; gap: ${tokens_1.contract.space["2.5"]}; /* snackbar.yaml:116 content.gap = $dimension.x2_5 (10px), reused as the icon+message-to-action gap */
|
|
39
|
+
min-height: 44px; max-width: 464px; /* snackbar.yaml:102-103 */
|
|
40
|
+
width: max-content;
|
|
41
|
+
padding: ${tokens_1.contract.space["2.5"]}; /* snackbar.yaml:104-105 root.paddingX/paddingY = $dimension.x2_5 (10px) */
|
|
42
|
+
border-radius: ${tokens_1.contract.radius.md}; /* snackbar.yaml:101 root.cornerRadius = $radius.r2 (8px) */
|
|
43
|
+
background: ${tokens_1.contract.bgNeutralInverted}; /* snackbar.yaml:100 root.color = $color.bg.neutral-inverted */
|
|
44
|
+
font-family: ${tokens_1.contract.font.sans};
|
|
45
|
+
opacity: 1; transform: scale(1);
|
|
46
|
+
transition: opacity 150ms cubic-bezier(0,0,.15,1), transform 150ms cubic-bezier(0,0,.15,1); /* snackbar.yaml:108-109 enterDuration/enterTimingFunction = $duration.d3 (150ms) / $timing-function.enter */
|
|
47
|
+
@starting-style { opacity: 0; transform: scale(0.8); } /* snackbar.yaml:106-107 enterOpacity/enterScale */
|
|
48
|
+
}
|
|
49
|
+
& .crisp-snackbar-content { flex: 1 1 auto; min-width: 0; display: flex; align-items: center; gap: ${tokens_1.contract.space["0.5"]}; } /* snackbar.yaml:124 prefixIcon.paddingRight = $dimension.x0_5 (2px), used as the icon-to-message gap */
|
|
50
|
+
& .crisp-snackbar-icon { flex-shrink: 0; display: inline-flex; }
|
|
51
|
+
& .crisp-snackbar[data-variant="positive"] .crisp-snackbar-icon { color: ${tokens_1.contract.bgSuccess}; } /* snackbar.yaml:138-139 variant=positive prefixIcon.color = $color.fg.positive */
|
|
52
|
+
& .crisp-snackbar[data-variant="critical"] .crisp-snackbar-icon { color: ${tokens_1.contract.bgDanger}; } /* snackbar.yaml:142-143 variant=critical prefixIcon.color = $color.fg.critical */
|
|
53
|
+
& .crisp-snackbar-message { flex: 1 1 auto; min-width: 0; color: ${tokens_1.contract.fgOnNeutralInverted}; font-size: ${tokens_1.contract.text.lg}; line-height: 19px; font-weight: ${tokens_1.contract.weight.regular}; } /* snackbar.yaml:117-121: message.color = $color.fg.neutral-inverted, fontSize/lineHeight = $font-size.t4/$line-height.t4 (14px/19px), fontWeight = regular */
|
|
54
|
+
& .crisp-snackbar-action {
|
|
55
|
+
flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center;
|
|
56
|
+
min-height: 44px; padding: 0 ${tokens_1.contract.space["2"]}; margin: -12px calc(-1 * ${tokens_1.contract.space["2"]}); /* snackbar.yaml:126-127 targetPaddingX/targetMinHeight = $dimension.x2 (8px, doubled here as horizontal padding)/44px -- a larger invisible tap target than the pill's own 44px content row, via negative margin so it doesn't grow the pill visually */
|
|
57
|
+
border: none; border-radius: ${tokens_1.contract.radius.md}; background: none;
|
|
58
|
+
color: ${tokens_1.contract.fgOnNeutralInverted}; font-family: inherit; font-size: ${tokens_1.contract.text.lg}; line-height: 19px; font-weight: ${tokens_1.contract.weight.bold}; /* fontSize/lineHeight = t4/line-height.t4; fontWeight = bold (snackbar.yaml:129-131); colour deviation documented above */
|
|
59
|
+
cursor: pointer;
|
|
60
|
+
transition: transform 150ms cubic-bezier(0,0,.15,1);
|
|
61
|
+
}
|
|
62
|
+
& .crisp-snackbar-action:active { transform: scale(0.98); } /* snackbar.yaml:133-134 pressed.actionButton.scaleScope = self */
|
|
63
|
+
`,
|
|
64
|
+
variants: {},
|
|
65
|
+
defaultVariants: {},
|
|
66
|
+
compoundVariants: [],
|
|
67
|
+
});
|
|
68
|
+
exports.snackbarRecipe = Object.assign((0, core_1.resolveRecipe)(config), {
|
|
69
|
+
config,
|
|
70
|
+
});
|
|
71
|
+
exports.default = config;
|
|
@@ -179,6 +179,11 @@ const toolbarChromeBody = (p) => ` & .${namespace_1.CLASS_PREFIX}${p}-menu-he
|
|
|
179
179
|
& .${namespace_1.CLASS_PREFIX}${p}-fchip-x:hover { background: color-mix(in srgb, ${tokens_1.contract.fgPrimary} 10%, transparent); color: ${tokens_1.contract.fgPrimary}; }
|
|
180
180
|
& .${namespace_1.CLASS_PREFIX}${p}-spacer { flex: 1; }
|
|
181
181
|
& .${namespace_1.CLASS_PREFIX}${p}-count { font-size: ${tokens_1.contract.text.md}; color: ${tokens_1.contract.fgMuted}; }
|
|
182
|
-
|
|
183
|
-
|
|
182
|
+
/* your-moon/crisp#953: bgBrandContrast, not bgBrandSolid --
|
|
183
|
+
the accent (#0298d9) only clears 3.23:1 with white; the darker sibling
|
|
184
|
+
clears 4.5:1+, same fix already applied to Button/Badge/Avatar (#897).
|
|
185
|
+
Hover is a brightness filter on the new fill, not bgBrandSolidPressed
|
|
186
|
+
(that token stays tuned for the lighter accent's own consumers). */
|
|
187
|
+
& .${namespace_1.CLASS_PREFIX}${p}-new { display: inline-flex; align-items: center; gap: ${tokens_1.contract.space["1"]}; height: ${tokens_1.contract.size.row}; padding: 0 ${tokens_1.contract.space["2.5"]}; border: none; border-radius: ${tokens_1.contract.radius.md}; background: ${tokens_1.contract.bgBrandContrast}; color: ${tokens_1.contract.fgOnBrand}; font-family: inherit; font-size: ${tokens_1.contract.text.md}; font-weight: ${tokens_1.contract.weight.medium}; cursor: pointer; transition: filter .12s; }
|
|
188
|
+
& .${namespace_1.CLASS_PREFIX}${p}-new:hover { filter: brightness(0.96); }`;
|
|
184
189
|
exports.toolbarChromeBody = toolbarChromeBody;
|
|
@@ -33,7 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.ToolbarTool = void 0;
|
|
36
|
+
exports.ToolbarTool = exports.toolbarOperatorLabels = void 0;
|
|
37
37
|
exports.applyFilters = applyFilters;
|
|
38
38
|
exports.applySorts = applySorts;
|
|
39
39
|
exports.TableToolbar = TableToolbar;
|
|
@@ -83,7 +83,7 @@ function ViewBarSlot({ target, children, }) {
|
|
|
83
83
|
// file, not per call site, and three independently attributed tags for
|
|
84
84
|
// the same name over-counts against that (it expects one per name).
|
|
85
85
|
const RtlChevronRight = ({ size, className, }) => ((0, jsx_runtime_1.jsx)(lucide_react_1.ChevronRight, { "data-rtl-flip": "", size: size, strokeWidth: 1.75, className: className }));
|
|
86
|
-
|
|
86
|
+
exports.toolbarOperatorLabels = {
|
|
87
87
|
contains: "contains",
|
|
88
88
|
doesnotcontain: "does not contain",
|
|
89
89
|
startswith: "starts with",
|
|
@@ -104,6 +104,33 @@ const OP_LABELS = {
|
|
|
104
104
|
before: "before",
|
|
105
105
|
after: "after",
|
|
106
106
|
};
|
|
107
|
+
/**
|
|
108
|
+
* crisp/#962: resolves each operator word through four layers, explicit
|
|
109
|
+
* prop winning, same "prop beats provider beats built-in" order as every
|
|
110
|
+
* other label in this file — `operatorLabels` prop, then a
|
|
111
|
+
* `CrispMessagesProvider`'s `componentDefaults.tableToolbar` (the new,
|
|
112
|
+
* discoverable, typed override), then the OLD whole-`CrispMessages`-
|
|
113
|
+
* dictionary-keyed-by-English-string lookup this file already had (kept
|
|
114
|
+
* so a consumer who already added entries there keeps working, unchanged),
|
|
115
|
+
* then the hardcoded English word. Not `useCrispMessage` in a loop over 13
|
|
116
|
+
* keys — that breaks the rules of hooks the same reason `useCrispMessages`
|
|
117
|
+
* itself exists (see that hook's own doc comment).
|
|
118
|
+
*/
|
|
119
|
+
function useToolbarOperatorLabels(operatorLabels, crispMessages) {
|
|
120
|
+
const providerLabels = (0, core_1.useCrispComponentDefaults)("tableToolbar");
|
|
121
|
+
return React.useMemo(() => {
|
|
122
|
+
const resolved = {};
|
|
123
|
+
for (const op of Object.keys(exports.toolbarOperatorLabels)) {
|
|
124
|
+
const builtIn = exports.toolbarOperatorLabels[op];
|
|
125
|
+
resolved[op] =
|
|
126
|
+
operatorLabels?.[op] ??
|
|
127
|
+
providerLabels?.[op] ??
|
|
128
|
+
crispMessages[builtIn] ??
|
|
129
|
+
builtIn;
|
|
130
|
+
}
|
|
131
|
+
return resolved;
|
|
132
|
+
}, [operatorLabels, providerLabels, crispMessages]);
|
|
133
|
+
}
|
|
107
134
|
// #619: order and label set match Attio's own text-filter operator picker
|
|
108
135
|
// verbatim (measured live, per RecordsTable's own #578 port of the same
|
|
109
136
|
// set: contains · does not contain · starts with · ends with · is · is not ·
|
|
@@ -368,7 +395,7 @@ function applySorts(rows, sorts, get = (row, key) => row[key]) {
|
|
|
368
395
|
* this (Sort: Due date/Assignee/Creation date; Filter: Assignee/Record/
|
|
369
396
|
* Created by), which one shared list can't reproduce.
|
|
370
397
|
*/
|
|
371
|
-
function TableToolbar({ columns, sortColumns: sortColumnsProp, filterColumns: filterColumnsProp, sorts: sortsProp, defaultSorts, onSortsChange, filters: filtersProp, defaultFilters, onFiltersChange, count, children, helpHref, className, sortLabel: sortLabelProp, filterLabel: filterLabelProp, reorderSortLabel: reorderSortLabelProp, noSortsLabel: noSortsLabelProp, addSortLabel: addSortLabelProp, learnSortingLabel: learnSortingLabelProp, selectConditionLabel: selectConditionLabelProp, sortFieldLabel: sortFieldLabelProp, sortDirectionLabel: sortDirectionLabelProp, removeSortLabel: removeSortLabelProp, filterFieldLabel: filterFieldLabelProp, filterFieldPlaceholder: filterFieldPlaceholderProp, filterValuePlaceholder: filterValuePlaceholderProp, filterOptionsLabel: filterOptionsLabelProp, deleteFilterLabel: deleteFilterLabelProp, ascendingLabel: ascendingLabelProp, descendingLabel: descendingLabelProp, sortTriggerLabel, filterTriggerLabel, inline, showSort = true, showViewSettings = true, viewSettingsRef, importExportRef, viewColumns: viewColumnsProp, shownKeys: shownKeysProp, defaultShownKeys, onShownKeysChange, viewSettingsLabel: viewSettingsLabelProp, reorderAttributeLabel: reorderAttributeLabelProp, hideAttributeLabel, addAttributeToViewLabel: addAttributeToViewLabelProp, searchAttributesPlaceholder: searchAttributesPlaceholderProp, importExportLabel: importExportLabelProp, onImportCsv, onExportCsv, onExportExcel, importExportHelpHref, importCsvLabel: importCsvLabelProp, exportCsvLabel: exportCsvLabelProp, exportExcelLabel: exportExcelLabelProp, learnImportExportLabel: learnImportExportLabelProp, backLabel: backLabelProp, nestedFieldPlaceholder, ...rest }) {
|
|
398
|
+
function TableToolbar({ columns, sortColumns: sortColumnsProp, filterColumns: filterColumnsProp, sorts: sortsProp, defaultSorts, onSortsChange, filters: filtersProp, defaultFilters, onFiltersChange, count, children, helpHref, className, sortLabel: sortLabelProp, filterLabel: filterLabelProp, reorderSortLabel: reorderSortLabelProp, noSortsLabel: noSortsLabelProp, addSortLabel: addSortLabelProp, learnSortingLabel: learnSortingLabelProp, selectConditionLabel: selectConditionLabelProp, sortFieldLabel: sortFieldLabelProp, sortDirectionLabel: sortDirectionLabelProp, removeSortLabel: removeSortLabelProp, filterFieldLabel: filterFieldLabelProp, filterFieldPlaceholder: filterFieldPlaceholderProp, filterValuePlaceholder: filterValuePlaceholderProp, filterOptionsLabel: filterOptionsLabelProp, deleteFilterLabel: deleteFilterLabelProp, operatorLabels, ascendingLabel: ascendingLabelProp, descendingLabel: descendingLabelProp, sortTriggerLabel, filterTriggerLabel, inline, showSort = true, showViewSettings = true, viewSettingsRef, importExportRef, viewColumns: viewColumnsProp, shownKeys: shownKeysProp, defaultShownKeys, onShownKeysChange, viewSettingsLabel: viewSettingsLabelProp, reorderAttributeLabel: reorderAttributeLabelProp, hideAttributeLabel, addAttributeToViewLabel: addAttributeToViewLabelProp, searchAttributesPlaceholder: searchAttributesPlaceholderProp, importExportLabel: importExportLabelProp, onImportCsv, onExportCsv, onExportExcel, importExportHelpHref, importCsvLabel: importCsvLabelProp, exportCsvLabel: exportCsvLabelProp, exportExcelLabel: exportExcelLabelProp, learnImportExportLabel: learnImportExportLabelProp, backLabel: backLabelProp, nestedFieldPlaceholder, ...rest }) {
|
|
372
399
|
// crisp/#471: an explicit prop always wins; missing, it reads the nearest
|
|
373
400
|
// CrispMessagesProvider's translation of the English default below, or
|
|
374
401
|
// that default itself with no provider — unchanged from before this
|
|
@@ -401,12 +428,12 @@ function TableToolbar({ columns, sortColumns: sortColumnsProp, filterColumns: fi
|
|
|
401
428
|
const exportExcelLabel = (0, core_1.useCrispMessage)(exportExcelLabelProp, "Export view as Excel");
|
|
402
429
|
const learnImportExportLabel = (0, core_1.useCrispMessage)(learnImportExportLabelProp, "Learn about imports and exports");
|
|
403
430
|
const backLabel = (0, core_1.useCrispMessage)(backLabelProp, "Back");
|
|
404
|
-
// OP_LABELS' own words ("contains", "is", "is any of", …) have no prop of
|
|
405
|
-
// their own — a whole lookup table, not one string — so they translate
|
|
406
|
-
// straight from the provider's dictionary at the two spots they render
|
|
407
|
-
// (#628, #673 below), same fallback order as every resolved constant
|
|
408
|
-
// above.
|
|
409
431
|
const crispMessages = (0, core_1.useCrispMessages)();
|
|
432
|
+
// crisp/#962: `operatorLabels` prop + `componentDefaults.tableToolbar`,
|
|
433
|
+
// layered over the old whole-dictionary lookup — see
|
|
434
|
+
// `useToolbarOperatorLabels`'s own doc comment for the full resolution
|
|
435
|
+
// order.
|
|
436
|
+
const opLabels = useToolbarOperatorLabels(operatorLabels, crispMessages);
|
|
410
437
|
// Each side falls back to the shared `columns` independently — a caller
|
|
411
438
|
// that only overrides one (Filter's own Record/Created by, say) still
|
|
412
439
|
// gets `columns` on the other, unchanged from before either prop existed.
|
|
@@ -842,9 +869,7 @@ function TableToolbar({ columns, sortColumns: sortColumnsProp, filterColumns: fi
|
|
|
842
869
|
return ((0, jsx_runtime_1.jsxs)("span", { className: "crisp-tabletoolbar-toolgroup", children: [(0, jsx_runtime_1.jsxs)("span", { className: "crisp-tabletoolbar-fcond", "data-complete": complete ? "" : undefined, children: [(0, jsx_runtime_1.jsxs)("button", { type: "button", className: "crisp-tabletoolbar-fcond-seg crisp-tabletoolbar-fcond-seg--field", "data-menu-trigger": true, "aria-haspopup": "dialog", "aria-expanded": fieldOpen, onClick: () => {
|
|
843
870
|
setFieldPickerPath([]);
|
|
844
871
|
setOpenMenu(fieldOpen ? null : `ffield:${cond.id}`);
|
|
845
|
-
}, children: [col?.icon ? (0, jsx_runtime_1.jsx)(col.icon, { size: 14, strokeWidth: 1.75 }) : null, colPath.length > 1 ? ((0, jsx_runtime_1.jsx)("span", { className: "crisp-tabletoolbar-fcond-path", children: colPath.map((c, i) => ((0, jsx_runtime_1.jsxs)(React.Fragment, { children: [i > 0 && ((0, jsx_runtime_1.jsx)(RtlChevronRight, { size: 14, className: "crisp-tabletoolbar-fcond-path-sep" })), (0, jsx_runtime_1.jsx)("span", { className: "crisp-tabletoolbar-fcond-path-seg", children: c.label })] }, c.key))) })) : (col?.label)] }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-tabletoolbar-fcond-seg crisp-tabletoolbar-fcond-seg--op", "data-menu-trigger": true, "aria-haspopup": "menu", "aria-expanded": opOpen, onClick: () => setOpenMenu(opOpen ? null : `fop:${cond.id}`), children: cond.op
|
|
846
|
-
? (crispMessages[OP_LABELS[cond.op]] ?? OP_LABELS[cond.op])
|
|
847
|
-
: selectConditionLabel }), showValueSeg && ((0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-tabletoolbar-fcond-seg crisp-tabletoolbar-fcond-seg--val", "data-menu-trigger": true, "aria-haspopup": "dialog", "aria-expanded": valOpen, onClick: () => setOpenMenu(valOpen ? null : `fval:${cond.id}`), children: cond.op === "isanyof"
|
|
872
|
+
}, children: [col?.icon ? (0, jsx_runtime_1.jsx)(col.icon, { size: 14, strokeWidth: 1.75 }) : null, colPath.length > 1 ? ((0, jsx_runtime_1.jsx)("span", { className: "crisp-tabletoolbar-fcond-path", children: colPath.map((c, i) => ((0, jsx_runtime_1.jsxs)(React.Fragment, { children: [i > 0 && ((0, jsx_runtime_1.jsx)(RtlChevronRight, { size: 14, className: "crisp-tabletoolbar-fcond-path-sep" })), (0, jsx_runtime_1.jsx)("span", { className: "crisp-tabletoolbar-fcond-path-seg", children: c.label })] }, c.key))) })) : (col?.label)] }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-tabletoolbar-fcond-seg crisp-tabletoolbar-fcond-seg--op", "data-menu-trigger": true, "aria-haspopup": "menu", "aria-expanded": opOpen, onClick: () => setOpenMenu(opOpen ? null : `fop:${cond.id}`), children: cond.op ? opLabels[cond.op] : selectConditionLabel }), showValueSeg && ((0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-tabletoolbar-fcond-seg crisp-tabletoolbar-fcond-seg--val", "data-menu-trigger": true, "aria-haspopup": "dialog", "aria-expanded": valOpen, onClick: () => setOpenMenu(valOpen ? null : `fval:${cond.id}`), children: cond.op === "isanyof"
|
|
848
873
|
? cond.values?.length
|
|
849
874
|
? isAnyOfSummary(cond.values)
|
|
850
875
|
: filterValuePlaceholder
|
|
@@ -852,7 +877,7 @@ function TableToolbar({ columns, sortColumns: sortColumnsProp, filterColumns: fi
|
|
|
852
877
|
? col?.type === "date"
|
|
853
878
|
? formatFilterDate(cond.value)
|
|
854
879
|
: cond.value
|
|
855
|
-
: filterValuePlaceholder })), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-tabletoolbar-fcond-seg crisp-tabletoolbar-fcond-seg--kebab", "data-menu-trigger": true, "aria-haspopup": "menu", "aria-expanded": kebabOpen, "aria-label": `${col?.label} ${filterOptionsLabel}`, onClick: () => setOpenMenu(kebabOpen ? null : `fkebab:${cond.id}`), children: (0, jsx_runtime_1.jsx)(lucide_react_1.MoreVertical, { size: 13, strokeWidth: 2 }) })] }), kebabOpen && ((0, jsx_runtime_1.jsx)("div", { ref: kebabMenuRef, className: "crisp-tabletoolbar-menu crisp-tabletoolbar-menu--right crisp-tabletoolbar-menu--hdr", role: "menu", "aria-label": `${col?.label} ${filterOptionsLabel}`, onKeyDown: onKebabKeyDown, children: (0, jsx_runtime_1.jsxs)("button", { type: "button", role: "menuitem", className: "crisp-tabletoolbar-menu-item crisp-tabletoolbar-menu-item--danger", onClick: () => removeFilter(cond.id), children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Trash2, { size: 16, strokeWidth: 1.75 }), deleteFilterLabel] }) })), fieldOpen && ((0, jsx_runtime_1.jsx)("div", { className: "crisp-tabletoolbar-menu", role: "dialog", "aria-label": filterFieldLabel, children: nestedFieldPicker(filterColumns, (key) => changeFilterField(cond.id, key), filterFieldPlaceholder, filterFieldLabel) })), opOpen && ((0, jsx_runtime_1.jsx)("div", { ref: opMenuRef, className: "crisp-tabletoolbar-menu crisp-tabletoolbar-menu--op", role: "menu", "aria-label": `${col?.label} ${crispMessages.condition ?? "condition"}`, onKeyDown: onOpKeyDown, children: opsFor(col).map((op) => ((0, jsx_runtime_1.jsxs)("button", { type: "button", role: "menuitem", className: "crisp-tabletoolbar-menu-item", "data-on": cond.op === op ? "" : undefined, onClick: () => pickOp(cond.id, op), children: [
|
|
880
|
+
: filterValuePlaceholder })), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-tabletoolbar-fcond-seg crisp-tabletoolbar-fcond-seg--kebab", "data-menu-trigger": true, "aria-haspopup": "menu", "aria-expanded": kebabOpen, "aria-label": `${col?.label} ${filterOptionsLabel}`, onClick: () => setOpenMenu(kebabOpen ? null : `fkebab:${cond.id}`), children: (0, jsx_runtime_1.jsx)(lucide_react_1.MoreVertical, { size: 13, strokeWidth: 2 }) })] }), kebabOpen && ((0, jsx_runtime_1.jsx)("div", { ref: kebabMenuRef, className: "crisp-tabletoolbar-menu crisp-tabletoolbar-menu--right crisp-tabletoolbar-menu--hdr", role: "menu", "aria-label": `${col?.label} ${filterOptionsLabel}`, onKeyDown: onKebabKeyDown, children: (0, jsx_runtime_1.jsxs)("button", { type: "button", role: "menuitem", className: "crisp-tabletoolbar-menu-item crisp-tabletoolbar-menu-item--danger", onClick: () => removeFilter(cond.id), children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Trash2, { size: 16, strokeWidth: 1.75 }), deleteFilterLabel] }) })), fieldOpen && ((0, jsx_runtime_1.jsx)("div", { className: "crisp-tabletoolbar-menu", role: "dialog", "aria-label": filterFieldLabel, children: nestedFieldPicker(filterColumns, (key) => changeFilterField(cond.id, key), filterFieldPlaceholder, filterFieldLabel) })), opOpen && ((0, jsx_runtime_1.jsx)("div", { ref: opMenuRef, className: "crisp-tabletoolbar-menu crisp-tabletoolbar-menu--op", role: "menu", "aria-label": `${col?.label} ${crispMessages.condition ?? "condition"}`, onKeyDown: onOpKeyDown, children: opsFor(col).map((op) => ((0, jsx_runtime_1.jsxs)("button", { type: "button", role: "menuitem", className: "crisp-tabletoolbar-menu-item", "data-on": cond.op === op ? "" : undefined, onClick: () => pickOp(cond.id, op), children: [opLabels[op], cond.op === op ? ((0, jsx_runtime_1.jsx)(lucide_react_1.Check, { className: "crisp-tabletoolbar-menu-check", size: 15, strokeWidth: 1.75 })) : null] }, op))) })), valOpen && showValueSeg && ((0, jsx_runtime_1.jsx)("div", { className: "crisp-tabletoolbar-menu crisp-tabletoolbar-menu--val", role: "dialog", "aria-label": `${col?.label} filter value`, children: cond.op === "isanyof" && col?.values ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("input", { className: "crisp-tabletoolbar-menu-input", placeholder: filterValuePlaceholder, "aria-label": `${col?.label} filter values`,
|
|
856
881
|
// biome-ignore lint/a11y/noAutofocus: focus the search field
|
|
857
882
|
autoFocus: true, value: anyOfQuery, onChange: (e) => setAnyOfQuery(e.target.value) }), col.values
|
|
858
883
|
.filter((v) => v
|
|
@@ -46,7 +46,7 @@ const tabs_keyboard_1 = require("./tabs-keyboard");
|
|
|
46
46
|
* Home/End jump); activation follows focus. Controlled via
|
|
47
47
|
* `value`/`onValueChange`, or uncontrolled from `defaultValue`.
|
|
48
48
|
*/
|
|
49
|
-
function Tabs({ tabs, value, defaultValue, onValueChange, leading, children, moreLabel = (n) => `+${n} more`, moreMenuLabel = "More tabs", className, fill, "aria-label": ariaLabel, ...rest }) {
|
|
49
|
+
function Tabs({ tabs, value, defaultValue, onValueChange, leading, children, moreLabel = (n) => `+${n} more`, moreMenuLabel = "More tabs", overflow = "collapse", className, fill, "aria-label": ariaLabel, ...rest }) {
|
|
50
50
|
// Scoped to this instance: `tab-${value}` and `panel-${value}` are global,
|
|
51
51
|
// so two tab bars sharing a value mint the same id twice and every
|
|
52
52
|
// aria-labelledby resolves to the first one (#227).
|
|
@@ -93,6 +93,12 @@ function Tabs({ tabs, value, defaultValue, onValueChange, leading, children, mor
|
|
|
93
93
|
const [hiddenFrom, setHiddenFrom] = React.useState(null);
|
|
94
94
|
const moreRef = React.useRef(null);
|
|
95
95
|
React.useLayoutEffect(() => {
|
|
96
|
+
// crisp#908: "scroll" never collapses anything -- the row scrolls
|
|
97
|
+
// instead, so there is nothing to measure or hide.
|
|
98
|
+
if (overflow !== "collapse") {
|
|
99
|
+
setHiddenFrom(null);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
96
102
|
const list = listRef.current;
|
|
97
103
|
if (!list || tabs.length === 0)
|
|
98
104
|
return;
|
|
@@ -133,7 +139,7 @@ function Tabs({ tabs, value, defaultValue, onValueChange, leading, children, mor
|
|
|
133
139
|
const ro = new ResizeObserver(recompute);
|
|
134
140
|
ro.observe(list);
|
|
135
141
|
return () => ro.disconnect();
|
|
136
|
-
}, [tabs, active]);
|
|
142
|
+
}, [tabs, active, overflow]);
|
|
137
143
|
const hiddenTabs = hiddenFrom == null ? [] : tabs.slice(hiddenFrom);
|
|
138
144
|
const { onKeyDown, tabIndexFor } = (0, tabs_keyboard_1.useTablistKeyboard)({
|
|
139
145
|
tabs,
|
|
@@ -141,11 +147,11 @@ function Tabs({ tabs, value, defaultValue, onValueChange, leading, children, mor
|
|
|
141
147
|
select,
|
|
142
148
|
listRef,
|
|
143
149
|
});
|
|
144
|
-
return ((0, jsx_runtime_1.jsxs)("div", { className: (0, tabs_recipe_1.tabsRecipe)({ fill, className }), ...rest, children: [(0, jsx_runtime_1.jsxs)("div", { ref: listRef, className: "crisp-tabs-list", role: "tablist", "aria-label": ariaLabel, onKeyDown: onKeyDown, children: [leading && (0, jsx_runtime_1.jsx)("span", { className: "crisp-tabs-leading", children: leading }), tabs.map((t, i) => {
|
|
150
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: (0, tabs_recipe_1.tabsRecipe)({ fill, className }), ...rest, children: [(0, jsx_runtime_1.jsxs)("div", { ref: listRef, className: "crisp-tabs-list", "data-overflow": overflow, role: "tablist", "aria-label": ariaLabel, onKeyDown: onKeyDown, children: [leading && (0, jsx_runtime_1.jsx)("span", { className: "crisp-tabs-leading", children: leading }), tabs.map((t, i) => {
|
|
145
151
|
const selected = t.value === active;
|
|
146
152
|
const overflowHidden = hiddenFrom != null && i >= hiddenFrom;
|
|
147
153
|
return ((0, jsx_runtime_1.jsxs)("button", { type: "button", role: "tab", id: tabId(t.value), "aria-selected": selected, "aria-controls": children && selected ? panelId(t.value) : undefined, tabIndex: overflowHidden ? -1 : tabIndexFor(t.value), "data-value": t.value, "data-selected": selected ? "" : undefined, "data-tabs-overflow-hidden": overflowHidden ? "" : undefined, disabled: t.disabled, className: "crisp-tabs-tab", onClick: () => select(t.value), children: [t.icon, (0, jsx_runtime_1.jsx)("span", { className: "tabs-tab-label", children: t.label }), t.badge != null && ((0, jsx_runtime_1.jsx)("span", { className: "crisp-tabs-tab-badge", children: t.badge }))] }, t.value));
|
|
148
|
-
}), (0, jsx_runtime_1.jsx)("span", { ref: moreRef, className: "crisp-tabs-more", "data-tabs-overflow-hidden": hiddenTabs.length === 0 ? "" : undefined, children: (0, jsx_runtime_1.jsx)(menu_1.Menu, { "aria-label": moreMenuLabel, trigger: (0, jsx_runtime_1.jsxs)("button", { type: "button", className: "crisp-tabs-tab", children: [(0, jsx_runtime_1.jsx)("span", { className: "tabs-tab-label", children: moreLabel(hiddenTabs.length) }), (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronDown, { size: 16, strokeWidth: 1.75, "aria-hidden": "true" })] }), children: hiddenTabs.map((t) => ((0, jsx_runtime_1.jsxs)(menu_1.MenuItem, { icon: t.icon, onSelect: () => select(t.value), children: [t.label, t.badge != null && ((0, jsx_runtime_1.jsx)("span", { className: "crisp-tabs-tab-badge", children: t.badge }))] }, t.value))) }) }), indicator && ((0, jsx_runtime_1.jsx)("span", { className: "crisp-tabs-indicator", style: {
|
|
154
|
+
}), overflow === "collapse" && ((0, jsx_runtime_1.jsx)("span", { ref: moreRef, className: "crisp-tabs-more", "data-tabs-overflow-hidden": hiddenTabs.length === 0 ? "" : undefined, children: (0, jsx_runtime_1.jsx)(menu_1.Menu, { "aria-label": moreMenuLabel, trigger: (0, jsx_runtime_1.jsxs)("button", { type: "button", className: "crisp-tabs-tab", children: [(0, jsx_runtime_1.jsx)("span", { className: "tabs-tab-label", children: moreLabel(hiddenTabs.length) }), (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronDown, { size: 16, strokeWidth: 1.75, "aria-hidden": "true" })] }), children: hiddenTabs.map((t) => ((0, jsx_runtime_1.jsxs)(menu_1.MenuItem, { icon: t.icon, onSelect: () => select(t.value), children: [t.label, t.badge != null && ((0, jsx_runtime_1.jsx)("span", { className: "crisp-tabs-tab-badge", children: t.badge }))] }, t.value))) }) })), indicator && ((0, jsx_runtime_1.jsx)("span", { className: "crisp-tabs-indicator", style: {
|
|
149
155
|
width: indicator.width,
|
|
150
156
|
transform: `translateX(${indicator.left}px)`,
|
|
151
157
|
} }))] }), children && ((0, jsx_runtime_1.jsx)("div", { className: "crisp-tabs-panel", role: "tabpanel", id: panelId(active), "aria-labelledby": tabId(active),
|
|
@@ -8,10 +8,20 @@ const tokens_1 = require("../../tokens");
|
|
|
8
8
|
// baseline needs, reserved as bottom padding so it's real box height and not
|
|
9
9
|
// paint that trailing content can sit on top of. The bar carries 12px inline
|
|
10
10
|
// padding too, and draws its own full-width `::after` baseline in
|
|
11
|
-
// borderSubtle at its bottom edge, spanning that padding.
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
11
|
+
// borderSubtle at its bottom edge, spanning that padding.
|
|
12
|
+
//
|
|
13
|
+
// your-moon/crisp#934: the selected tab used to be a light
|
|
14
|
+
// filled pill (bg #fbfbfb + inset ring borderSubtle, r8) with a 1px ink
|
|
15
|
+
// underline on top of it. Owner-directed: underline tabs, no fill pill --
|
|
16
|
+
// seed-design's own `tab.yaml` has no `root.color` property at all, at any
|
|
17
|
+
// state (selected/hover/pressed); selection is carried purely by the label
|
|
18
|
+
// darkening (muted -> ink, unchanged, already matched seed) plus the
|
|
19
|
+
// existing underline (already `fgPrimary`, "same colour as selected text,
|
|
20
|
+
// not brand" -- also already matched seed). Only the selected pill's own
|
|
21
|
+
// fill/ring is removed here; unselected tabs keep their existing subtle
|
|
22
|
+
// hover tint (a desktop hover affordance seed's spec doesn't need to
|
|
23
|
+
// consider one way or the other, and the owner didn't ask to remove).
|
|
24
|
+
// Inactive tabs are muted and fill to a subtle pill on hover. Each tab carries
|
|
15
25
|
// a 16px leading icon and an optional count badge. Measured from the live
|
|
16
26
|
// app: bar `padding: 0 12px 8px`, 28px pills, 4/8 padding, 6px icon-label
|
|
17
27
|
// gap, 14/500 labels. The count badge (crisp#697, re-measured against a
|
|
@@ -48,11 +58,18 @@ const config = (0, core_1.defineRecipe)({
|
|
|
48
58
|
|
|
49
59
|
& .crisp-tabs-list { position: relative; display: flex; align-items: center; gap: ${tokens_1.contract.space["1"]}; box-sizing: content-box; height: ${tokens_1.contract.size.row}; padding: 0 ${tokens_1.contract.space["3"]} ${tokens_1.contract.space["2"]}; flex-shrink: 0; }
|
|
50
60
|
& .crisp-tabs-list::after { content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 1px; background: ${tokens_1.contract.borderSubtle}; }
|
|
61
|
+
/* crisp#908: an alternative to the "+N more" collapse above (crisp#863) --
|
|
62
|
+
a consumer opts into a single scrolling row instead (Naver/Kakao's own
|
|
63
|
+
pattern, docs/study/korean/), never both at once. No new visual value:
|
|
64
|
+
reuses the same list gap/tab geometry, only adds overflow-x + snap. */
|
|
65
|
+
& .crisp-tabs-list[data-overflow="scroll"] { overflow-x: auto; flex-wrap: nowrap; scroll-snap-type: x mandatory; scrollbar-width: none; }
|
|
66
|
+
& .crisp-tabs-list[data-overflow="scroll"]::-webkit-scrollbar { display: none; }
|
|
67
|
+
& .crisp-tabs-list[data-overflow="scroll"] .crisp-tabs-tab { flex-shrink: 0; scroll-snap-align: start; }
|
|
51
68
|
& .crisp-tabs-leading { display: inline-flex; align-items: center; margin-inline-end: ${tokens_1.contract.space["1"]}; }
|
|
52
|
-
& .crisp-tabs-tab { position: relative; display: inline-flex; align-items: center; gap: ${tokens_1.contract.space["1.5"]}; height: 28px; margin: 0; padding: 0 ${tokens_1.contract.space["2"]}; border: none; background: none;
|
|
69
|
+
& .crisp-tabs-tab { position: relative; display: inline-flex; align-items: center; gap: ${tokens_1.contract.space["1.5"]}; height: 28px; margin: 0; padding: 0 ${tokens_1.contract.space["2"]}; border: none; background: none; border-radius: ${tokens_1.contract.radius.md}; font-family: inherit; font-size: ${tokens_1.contract.text.lg}; font-weight: ${tokens_1.contract.weight.medium}; letter-spacing: -0.14px; color: ${tokens_1.contract.fgMuted}; cursor: pointer; white-space: nowrap; transition: color .12s ease, background-color .12s ease; }
|
|
53
70
|
& .crisp-tabs-tab svg { flex-shrink: 0; opacity: 0.85; }
|
|
54
71
|
& .crisp-tabs-tab:hover:not([data-selected]) { background: color-mix(in srgb, ${tokens_1.contract.fgPrimary} 4%, transparent); color: ${tokens_1.contract.fgPrimary}; }
|
|
55
|
-
& .crisp-tabs-tab[data-selected] { color: ${tokens_1.contract.fgPrimary};
|
|
72
|
+
& .crisp-tabs-tab[data-selected] { color: ${tokens_1.contract.fgPrimary}; }
|
|
56
73
|
& .crisp-tabs-tab:focus-visible { outline: 2px solid ${tokens_1.contract.bgBrandSolid}; outline-offset: 1px; }
|
|
57
74
|
& .crisp-tabs-tab-badge { display: inline-flex; align-items: center; justify-content: center; min-width: 14px; height: 14px; padding: 0 ${tokens_1.contract.space["1"]}; border-radius: 4px; background: color-mix(in srgb, ${tokens_1.contract.fgPrimary} 4%, transparent); box-shadow: inset 0 0 0 1px ${tokens_1.contract.borderSubtle}; font-size: 10px; line-height: 14px; font-weight: ${tokens_1.contract.weight.medium}; color: ${tokens_1.contract.fgNeutralSoft}; }
|
|
58
75
|
& .crisp-tabs-tab[data-selected] .crisp-tabs-tab-badge { color: ${tokens_1.contract.fgPrimary}; }
|
|
@@ -36,13 +36,14 @@ const spec = (0, core_1.defineSpec)({
|
|
|
36
36
|
muted: { color: tokens_1.contract.fgNeutralMuted },
|
|
37
37
|
// #858: no new measurement -- both reuse tokens already measured
|
|
38
38
|
// against Attio for the same colours in a different arrangement.
|
|
39
|
-
// `brand` is `textlink`'s own reused brand-text colour
|
|
40
|
-
//
|
|
41
|
-
//
|
|
42
|
-
//
|
|
43
|
-
//
|
|
44
|
-
//
|
|
45
|
-
|
|
39
|
+
// `brand` is `textlink`'s own reused brand-text colour, now
|
|
40
|
+
// `fgBrandContrast` (your-moon/crisp#897: the plain accent,
|
|
41
|
+
// `bgBrandSolid`, only clears 3.23:1 as text on a white page).
|
|
42
|
+
// `danger` is `fgDanger` ("solid critical/destructive TEXT... an
|
|
43
|
+
// overdue due-date, a destructive menu item's label" -- see its own
|
|
44
|
+
// doc in tokens/semantic.ts), not `fgDangerSoft` (that one's a soft
|
|
45
|
+
// Badge pill's text, a different measured hex).
|
|
46
|
+
brand: { color: tokens_1.contract.fgBrandContrast },
|
|
46
47
|
danger: { color: tokens_1.contract.fgDanger },
|
|
47
48
|
},
|
|
48
49
|
},
|
|
@@ -36,13 +36,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
36
36
|
exports.Textarea = void 0;
|
|
37
37
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
38
38
|
const React = __importStar(require("react"));
|
|
39
|
+
const progress_1 = require("../progress");
|
|
39
40
|
const textarea_recipe_1 = require("./textarea.recipe");
|
|
40
41
|
/**
|
|
41
42
|
* A multiline text field — measured from settings. Forwards every native
|
|
42
43
|
* `<textarea>` prop; pass `showCount` with `maxLength` to render a live
|
|
43
44
|
* character counter (which turns red if the value exceeds the limit).
|
|
44
45
|
*/
|
|
45
|
-
exports.Textarea = React.forwardRef(({ showCount, maxLength, className, wrapperClassName, value, defaultValue, onChange,
|
|
46
|
+
exports.Textarea = React.forwardRef(({ showCount, showProgress, maxLength, className, wrapperClassName, value, defaultValue, onChange,
|
|
46
47
|
// `style` is pulled out and put on the ROOT, the way Input does it.
|
|
47
48
|
// Everything else belongs on the <textarea> -- id, name, form
|
|
48
49
|
// association, aria -- but `style` sizes the visual box, and the box is
|
|
@@ -58,6 +59,6 @@ style, ...props }, ref) => {
|
|
|
58
59
|
if (value === undefined)
|
|
59
60
|
setCount(e.target.value.length);
|
|
60
61
|
onChange?.(e);
|
|
61
|
-
}, ...props }) }), counted && ((0, jsx_runtime_1.jsxs)("span", { className: "crisp-textarea-count", "data-over": len > (maxLength ?? 0) ? "" : undefined, "aria-hidden": "true", children: [len, " / ", maxLength] }))] }));
|
|
62
|
+
}, ...props }) }), counted && ((0, jsx_runtime_1.jsxs)("span", { className: "crisp-textarea-count", "data-over": len > (maxLength ?? 0) ? "" : undefined, "aria-hidden": "true", children: [showProgress && ((0, jsx_runtime_1.jsx)(progress_1.Progress, { variant: "circular", value: len, max: maxLength ?? 0, className: "crisp-textarea-progress" })), len, " / ", maxLength] }))] }));
|
|
62
63
|
});
|
|
63
64
|
exports.Textarea.displayName = "Textarea";
|