@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
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { defineRecipe, resolveRecipe } from "../../core/index.js";
|
|
2
|
+
import { contract as c } from "../../tokens/index.js";
|
|
3
|
+
// A mobile-pattern snackbar — daangn/seed-design's
|
|
4
|
+
// packages/rootage/components/snackbar.yaml (dev@e1c486b904297f7b6a8f17b8523f732c37015d60),
|
|
5
|
+
// not Attio (no such component lives there; crisp#911). A single dark
|
|
6
|
+
// `bgNeutralInverted` pill, bottom-anchored, holding an optional tone-coloured
|
|
7
|
+
// prefix icon, a message, and an optional bold action. Every numeric value
|
|
8
|
+
// below cites its yaml line; sizes/radii already equal existing crisp tokens
|
|
9
|
+
// (crisp's space/radius scale was itself derived 2px-granular, same as
|
|
10
|
+
// seed's `dimension`/`radius` scales, so no new tokens were needed).
|
|
11
|
+
//
|
|
12
|
+
// Deviation from the spec's literal `actionButton.color: $color.fg.brand`
|
|
13
|
+
// (snackbar.yaml:128, seed's carrot-600 `#ff6600`): crisp's OWN brand token
|
|
14
|
+
// is TechPartners' verified blue (`bgBrandSolid`, `#0298d9`), not seed's
|
|
15
|
+
// orange (the identity migration takes seed's STRUCTURE, crisp's own colour
|
|
16
|
+
// -- `~/grc-parity/seed-identity-spec.md`). Computed contrast of
|
|
17
|
+
// `bgBrandSolid` on `bgNeutralInverted` (light theme: `#0298d9` on `#2a3038`)
|
|
18
|
+
// is 4.11:1 -- below AA's 4.5:1 for 14px text (bold 14px does not clear the
|
|
19
|
+
// large-text 3:1 threshold, which needs 18.66px). crisp has no brand step
|
|
20
|
+
// bright enough on this dark surface yet, so the action reads in
|
|
21
|
+
// `fgOnNeutralInverted` (the same token the message already uses) at bold
|
|
22
|
+
// weight instead -- distinguished by weight, not colour, until a dedicated
|
|
23
|
+
// token is measured.
|
|
24
|
+
const config = defineRecipe({
|
|
25
|
+
name: "snackbar",
|
|
26
|
+
base: `
|
|
27
|
+
& .crisp-snackbar-region {
|
|
28
|
+
position: fixed; z-index: 80; inset-inline: 0; bottom: 0;
|
|
29
|
+
display: flex; justify-content: center;
|
|
30
|
+
padding: ${c.space["2"]}; /* snackbar.yaml:95-96 region.paddingX/paddingY = $dimension.x2 (8px) */
|
|
31
|
+
pointer-events: none;
|
|
32
|
+
}
|
|
33
|
+
& .crisp-snackbar {
|
|
34
|
+
pointer-events: auto; box-sizing: border-box;
|
|
35
|
+
display: flex; align-items: center; gap: ${c.space["2.5"]}; /* snackbar.yaml:116 content.gap = $dimension.x2_5 (10px), reused as the icon+message-to-action gap */
|
|
36
|
+
min-height: 44px; max-width: 464px; /* snackbar.yaml:102-103 */
|
|
37
|
+
width: max-content;
|
|
38
|
+
padding: ${c.space["2.5"]}; /* snackbar.yaml:104-105 root.paddingX/paddingY = $dimension.x2_5 (10px) */
|
|
39
|
+
border-radius: ${c.radius.md}; /* snackbar.yaml:101 root.cornerRadius = $radius.r2 (8px) */
|
|
40
|
+
background: ${c.bgNeutralInverted}; /* snackbar.yaml:100 root.color = $color.bg.neutral-inverted */
|
|
41
|
+
font-family: ${c.font.sans};
|
|
42
|
+
opacity: 1; transform: scale(1);
|
|
43
|
+
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 */
|
|
44
|
+
@starting-style { opacity: 0; transform: scale(0.8); } /* snackbar.yaml:106-107 enterOpacity/enterScale */
|
|
45
|
+
}
|
|
46
|
+
& .crisp-snackbar-content { flex: 1 1 auto; min-width: 0; display: flex; align-items: center; gap: ${c.space["0.5"]}; } /* snackbar.yaml:124 prefixIcon.paddingRight = $dimension.x0_5 (2px), used as the icon-to-message gap */
|
|
47
|
+
& .crisp-snackbar-icon { flex-shrink: 0; display: inline-flex; }
|
|
48
|
+
& .crisp-snackbar[data-variant="positive"] .crisp-snackbar-icon { color: ${c.bgSuccess}; } /* snackbar.yaml:138-139 variant=positive prefixIcon.color = $color.fg.positive */
|
|
49
|
+
& .crisp-snackbar[data-variant="critical"] .crisp-snackbar-icon { color: ${c.bgDanger}; } /* snackbar.yaml:142-143 variant=critical prefixIcon.color = $color.fg.critical */
|
|
50
|
+
& .crisp-snackbar-message { flex: 1 1 auto; min-width: 0; color: ${c.fgOnNeutralInverted}; font-size: ${c.text.lg}; line-height: 19px; font-weight: ${c.weight.regular}; } /* snackbar.yaml:117-121: message.color = $color.fg.neutral-inverted, fontSize/lineHeight = $font-size.t4/$line-height.t4 (14px/19px), fontWeight = regular */
|
|
51
|
+
& .crisp-snackbar-action {
|
|
52
|
+
flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center;
|
|
53
|
+
min-height: 44px; padding: 0 ${c.space["2"]}; margin: -12px calc(-1 * ${c.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 */
|
|
54
|
+
border: none; border-radius: ${c.radius.md}; background: none;
|
|
55
|
+
color: ${c.fgOnNeutralInverted}; font-family: inherit; font-size: ${c.text.lg}; line-height: 19px; font-weight: ${c.weight.bold}; /* fontSize/lineHeight = t4/line-height.t4; fontWeight = bold (snackbar.yaml:129-131); colour deviation documented above */
|
|
56
|
+
cursor: pointer;
|
|
57
|
+
transition: transform 150ms cubic-bezier(0,0,.15,1);
|
|
58
|
+
}
|
|
59
|
+
& .crisp-snackbar-action:active { transform: scale(0.98); } /* snackbar.yaml:133-134 pressed.actionButton.scaleScope = self */
|
|
60
|
+
`,
|
|
61
|
+
variants: {},
|
|
62
|
+
defaultVariants: {},
|
|
63
|
+
compoundVariants: [],
|
|
64
|
+
});
|
|
65
|
+
export const snackbarRecipe = Object.assign(resolveRecipe(config), {
|
|
66
|
+
config,
|
|
67
|
+
});
|
|
68
|
+
export default config;
|
|
@@ -175,5 +175,10 @@ export const toolbarChromeBody = (p) => ` & .${CLASS_PREFIX}${p}-menu-head {
|
|
|
175
175
|
& .${CLASS_PREFIX}${p}-fchip-x:hover { background: color-mix(in srgb, ${c.fgPrimary} 10%, transparent); color: ${c.fgPrimary}; }
|
|
176
176
|
& .${CLASS_PREFIX}${p}-spacer { flex: 1; }
|
|
177
177
|
& .${CLASS_PREFIX}${p}-count { font-size: ${c.text.md}; color: ${c.fgMuted}; }
|
|
178
|
-
|
|
179
|
-
|
|
178
|
+
/* your-moon/crisp#953: bgBrandContrast, not bgBrandSolid --
|
|
179
|
+
the accent (#0298d9) only clears 3.23:1 with white; the darker sibling
|
|
180
|
+
clears 4.5:1+, same fix already applied to Button/Badge/Avatar (#897).
|
|
181
|
+
Hover is a brightness filter on the new fill, not bgBrandSolidPressed
|
|
182
|
+
(that token stays tuned for the lighter accent's own consumers). */
|
|
183
|
+
& .${CLASS_PREFIX}${p}-new { display: inline-flex; align-items: center; gap: ${c.space["1"]}; height: ${c.size.row}; padding: 0 ${c.space["2.5"]}; border: none; border-radius: ${c.radius.md}; background: ${c.bgBrandContrast}; color: ${c.fgOnBrand}; font-family: inherit; font-size: ${c.text.md}; font-weight: ${c.weight.medium}; cursor: pointer; transition: filter .12s; }
|
|
184
|
+
& .${CLASS_PREFIX}${p}-new:hover { filter: brightness(0.96); }`;
|
|
@@ -2,7 +2,7 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
2
2
|
import { ArrowDownWideNarrow, ArrowUpDown, ArrowUpNarrowWide, Check, ChevronDown, ChevronLeft, ChevronRight, CircleHelp, Download, GripVertical, ListFilter, MoreVertical, Plus, SlidersHorizontal, Trash2, Upload, X, } from "lucide-react";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { createPortal } from "react-dom";
|
|
5
|
-
import { FloatingPortal, isInsideLaterFloatingLayer, placeFloating, registerFloatingLayer, useCrispMessage, useCrispMessages, useFocusAfterRemoval, } from "../../core/index.js";
|
|
5
|
+
import { FloatingPortal, isInsideLaterFloatingLayer, placeFloating, registerFloatingLayer, useCrispComponentDefaults, useCrispMessage, useCrispMessages, useFocusAfterRemoval, } from "../../core/index.js";
|
|
6
6
|
import { DatePicker, } from "../datepicker/index.js";
|
|
7
7
|
import { MenuCheckboxItem, useMenuKeyboard } from "../menu/index.js";
|
|
8
8
|
import { Select } from "../select/index.js";
|
|
@@ -44,7 +44,7 @@ function ViewBarSlot({ target, children, }) {
|
|
|
44
44
|
// file, not per call site, and three independently attributed tags for
|
|
45
45
|
// the same name over-counts against that (it expects one per name).
|
|
46
46
|
const RtlChevronRight = ({ size, className, }) => (_jsx(ChevronRight, { "data-rtl-flip": "", size: size, strokeWidth: 1.75, className: className }));
|
|
47
|
-
const
|
|
47
|
+
export const toolbarOperatorLabels = {
|
|
48
48
|
contains: "contains",
|
|
49
49
|
doesnotcontain: "does not contain",
|
|
50
50
|
startswith: "starts with",
|
|
@@ -65,6 +65,33 @@ const OP_LABELS = {
|
|
|
65
65
|
before: "before",
|
|
66
66
|
after: "after",
|
|
67
67
|
};
|
|
68
|
+
/**
|
|
69
|
+
* crisp/#962: resolves each operator word through four layers, explicit
|
|
70
|
+
* prop winning, same "prop beats provider beats built-in" order as every
|
|
71
|
+
* other label in this file — `operatorLabels` prop, then a
|
|
72
|
+
* `CrispMessagesProvider`'s `componentDefaults.tableToolbar` (the new,
|
|
73
|
+
* discoverable, typed override), then the OLD whole-`CrispMessages`-
|
|
74
|
+
* dictionary-keyed-by-English-string lookup this file already had (kept
|
|
75
|
+
* so a consumer who already added entries there keeps working, unchanged),
|
|
76
|
+
* then the hardcoded English word. Not `useCrispMessage` in a loop over 13
|
|
77
|
+
* keys — that breaks the rules of hooks the same reason `useCrispMessages`
|
|
78
|
+
* itself exists (see that hook's own doc comment).
|
|
79
|
+
*/
|
|
80
|
+
function useToolbarOperatorLabels(operatorLabels, crispMessages) {
|
|
81
|
+
const providerLabels = useCrispComponentDefaults("tableToolbar");
|
|
82
|
+
return React.useMemo(() => {
|
|
83
|
+
const resolved = {};
|
|
84
|
+
for (const op of Object.keys(toolbarOperatorLabels)) {
|
|
85
|
+
const builtIn = toolbarOperatorLabels[op];
|
|
86
|
+
resolved[op] =
|
|
87
|
+
operatorLabels?.[op] ??
|
|
88
|
+
providerLabels?.[op] ??
|
|
89
|
+
crispMessages[builtIn] ??
|
|
90
|
+
builtIn;
|
|
91
|
+
}
|
|
92
|
+
return resolved;
|
|
93
|
+
}, [operatorLabels, providerLabels, crispMessages]);
|
|
94
|
+
}
|
|
68
95
|
// #619: order and label set match Attio's own text-filter operator picker
|
|
69
96
|
// verbatim (measured live, per RecordsTable's own #578 port of the same
|
|
70
97
|
// set: contains · does not contain · starts with · ends with · is · is not ·
|
|
@@ -329,7 +356,7 @@ export function applySorts(rows, sorts, get = (row, key) => row[key]) {
|
|
|
329
356
|
* this (Sort: Due date/Assignee/Creation date; Filter: Assignee/Record/
|
|
330
357
|
* Created by), which one shared list can't reproduce.
|
|
331
358
|
*/
|
|
332
|
-
export 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 }) {
|
|
359
|
+
export 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 }) {
|
|
333
360
|
// crisp/#471: an explicit prop always wins; missing, it reads the nearest
|
|
334
361
|
// CrispMessagesProvider's translation of the English default below, or
|
|
335
362
|
// that default itself with no provider — unchanged from before this
|
|
@@ -362,12 +389,12 @@ export function TableToolbar({ columns, sortColumns: sortColumnsProp, filterColu
|
|
|
362
389
|
const exportExcelLabel = useCrispMessage(exportExcelLabelProp, "Export view as Excel");
|
|
363
390
|
const learnImportExportLabel = useCrispMessage(learnImportExportLabelProp, "Learn about imports and exports");
|
|
364
391
|
const backLabel = useCrispMessage(backLabelProp, "Back");
|
|
365
|
-
// OP_LABELS' own words ("contains", "is", "is any of", …) have no prop of
|
|
366
|
-
// their own — a whole lookup table, not one string — so they translate
|
|
367
|
-
// straight from the provider's dictionary at the two spots they render
|
|
368
|
-
// (#628, #673 below), same fallback order as every resolved constant
|
|
369
|
-
// above.
|
|
370
392
|
const crispMessages = useCrispMessages();
|
|
393
|
+
// crisp/#962: `operatorLabels` prop + `componentDefaults.tableToolbar`,
|
|
394
|
+
// layered over the old whole-dictionary lookup — see
|
|
395
|
+
// `useToolbarOperatorLabels`'s own doc comment for the full resolution
|
|
396
|
+
// order.
|
|
397
|
+
const opLabels = useToolbarOperatorLabels(operatorLabels, crispMessages);
|
|
371
398
|
// Each side falls back to the shared `columns` independently — a caller
|
|
372
399
|
// that only overrides one (Filter's own Record/Created by, say) still
|
|
373
400
|
// gets `columns` on the other, unchanged from before either prop existed.
|
|
@@ -803,9 +830,7 @@ export function TableToolbar({ columns, sortColumns: sortColumnsProp, filterColu
|
|
|
803
830
|
return (_jsxs("span", { className: "crisp-tabletoolbar-toolgroup", children: [_jsxs("span", { className: "crisp-tabletoolbar-fcond", "data-complete": complete ? "" : undefined, children: [_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: () => {
|
|
804
831
|
setFieldPickerPath([]);
|
|
805
832
|
setOpenMenu(fieldOpen ? null : `ffield:${cond.id}`);
|
|
806
|
-
}, children: [col?.icon ? _jsx(col.icon, { size: 14, strokeWidth: 1.75 }) : null, colPath.length > 1 ? (_jsx("span", { className: "crisp-tabletoolbar-fcond-path", children: colPath.map((c, i) => (_jsxs(React.Fragment, { children: [i > 0 && (_jsx(RtlChevronRight, { size: 14, className: "crisp-tabletoolbar-fcond-path-sep" })), _jsx("span", { className: "crisp-tabletoolbar-fcond-path-seg", children: c.label })] }, c.key))) })) : (col?.label)] }), _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
|
|
807
|
-
? (crispMessages[OP_LABELS[cond.op]] ?? OP_LABELS[cond.op])
|
|
808
|
-
: selectConditionLabel }), showValueSeg && (_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"
|
|
833
|
+
}, children: [col?.icon ? _jsx(col.icon, { size: 14, strokeWidth: 1.75 }) : null, colPath.length > 1 ? (_jsx("span", { className: "crisp-tabletoolbar-fcond-path", children: colPath.map((c, i) => (_jsxs(React.Fragment, { children: [i > 0 && (_jsx(RtlChevronRight, { size: 14, className: "crisp-tabletoolbar-fcond-path-sep" })), _jsx("span", { className: "crisp-tabletoolbar-fcond-path-seg", children: c.label })] }, c.key))) })) : (col?.label)] }), _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 && (_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"
|
|
809
834
|
? cond.values?.length
|
|
810
835
|
? isAnyOfSummary(cond.values)
|
|
811
836
|
: filterValuePlaceholder
|
|
@@ -813,7 +838,7 @@ export function TableToolbar({ columns, sortColumns: sortColumnsProp, filterColu
|
|
|
813
838
|
? col?.type === "date"
|
|
814
839
|
? formatFilterDate(cond.value)
|
|
815
840
|
: cond.value
|
|
816
|
-
: filterValuePlaceholder })), _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: _jsx(MoreVertical, { size: 13, strokeWidth: 2 }) })] }), kebabOpen && (_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: _jsxs("button", { type: "button", role: "menuitem", className: "crisp-tabletoolbar-menu-item crisp-tabletoolbar-menu-item--danger", onClick: () => removeFilter(cond.id), children: [_jsx(Trash2, { size: 16, strokeWidth: 1.75 }), deleteFilterLabel] }) })), fieldOpen && (_jsx("div", { className: "crisp-tabletoolbar-menu", role: "dialog", "aria-label": filterFieldLabel, children: nestedFieldPicker(filterColumns, (key) => changeFilterField(cond.id, key), filterFieldPlaceholder, filterFieldLabel) })), opOpen && (_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) => (_jsxs("button", { type: "button", role: "menuitem", className: "crisp-tabletoolbar-menu-item", "data-on": cond.op === op ? "" : undefined, onClick: () => pickOp(cond.id, op), children: [
|
|
841
|
+
: filterValuePlaceholder })), _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: _jsx(MoreVertical, { size: 13, strokeWidth: 2 }) })] }), kebabOpen && (_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: _jsxs("button", { type: "button", role: "menuitem", className: "crisp-tabletoolbar-menu-item crisp-tabletoolbar-menu-item--danger", onClick: () => removeFilter(cond.id), children: [_jsx(Trash2, { size: 16, strokeWidth: 1.75 }), deleteFilterLabel] }) })), fieldOpen && (_jsx("div", { className: "crisp-tabletoolbar-menu", role: "dialog", "aria-label": filterFieldLabel, children: nestedFieldPicker(filterColumns, (key) => changeFilterField(cond.id, key), filterFieldPlaceholder, filterFieldLabel) })), opOpen && (_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) => (_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 ? (_jsx(Check, { className: "crisp-tabletoolbar-menu-check", size: 15, strokeWidth: 1.75 })) : null] }, op))) })), valOpen && showValueSeg && (_jsx("div", { className: "crisp-tabletoolbar-menu crisp-tabletoolbar-menu--val", role: "dialog", "aria-label": `${col?.label} filter value`, children: cond.op === "isanyof" && col?.values ? (_jsxs(_Fragment, { children: [_jsx("input", { className: "crisp-tabletoolbar-menu-input", placeholder: filterValuePlaceholder, "aria-label": `${col?.label} filter values`,
|
|
817
842
|
// biome-ignore lint/a11y/noAutofocus: focus the search field
|
|
818
843
|
autoFocus: true, value: anyOfQuery, onChange: (e) => setAnyOfQuery(e.target.value) }), col.values
|
|
819
844
|
.filter((v) => v
|
|
@@ -10,7 +10,7 @@ import { useTablistKeyboard } from "./tabs-keyboard.js";
|
|
|
10
10
|
* Home/End jump); activation follows focus. Controlled via
|
|
11
11
|
* `value`/`onValueChange`, or uncontrolled from `defaultValue`.
|
|
12
12
|
*/
|
|
13
|
-
export function Tabs({ tabs, value, defaultValue, onValueChange, leading, children, moreLabel = (n) => `+${n} more`, moreMenuLabel = "More tabs", className, fill, "aria-label": ariaLabel, ...rest }) {
|
|
13
|
+
export function Tabs({ tabs, value, defaultValue, onValueChange, leading, children, moreLabel = (n) => `+${n} more`, moreMenuLabel = "More tabs", overflow = "collapse", className, fill, "aria-label": ariaLabel, ...rest }) {
|
|
14
14
|
// Scoped to this instance: `tab-${value}` and `panel-${value}` are global,
|
|
15
15
|
// so two tab bars sharing a value mint the same id twice and every
|
|
16
16
|
// aria-labelledby resolves to the first one (#227).
|
|
@@ -57,6 +57,12 @@ export function Tabs({ tabs, value, defaultValue, onValueChange, leading, childr
|
|
|
57
57
|
const [hiddenFrom, setHiddenFrom] = React.useState(null);
|
|
58
58
|
const moreRef = React.useRef(null);
|
|
59
59
|
React.useLayoutEffect(() => {
|
|
60
|
+
// crisp#908: "scroll" never collapses anything -- the row scrolls
|
|
61
|
+
// instead, so there is nothing to measure or hide.
|
|
62
|
+
if (overflow !== "collapse") {
|
|
63
|
+
setHiddenFrom(null);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
60
66
|
const list = listRef.current;
|
|
61
67
|
if (!list || tabs.length === 0)
|
|
62
68
|
return;
|
|
@@ -97,7 +103,7 @@ export function Tabs({ tabs, value, defaultValue, onValueChange, leading, childr
|
|
|
97
103
|
const ro = new ResizeObserver(recompute);
|
|
98
104
|
ro.observe(list);
|
|
99
105
|
return () => ro.disconnect();
|
|
100
|
-
}, [tabs, active]);
|
|
106
|
+
}, [tabs, active, overflow]);
|
|
101
107
|
const hiddenTabs = hiddenFrom == null ? [] : tabs.slice(hiddenFrom);
|
|
102
108
|
const { onKeyDown, tabIndexFor } = useTablistKeyboard({
|
|
103
109
|
tabs,
|
|
@@ -105,11 +111,11 @@ export function Tabs({ tabs, value, defaultValue, onValueChange, leading, childr
|
|
|
105
111
|
select,
|
|
106
112
|
listRef,
|
|
107
113
|
});
|
|
108
|
-
return (_jsxs("div", { className: tabsRecipe({ fill, className }), ...rest, children: [_jsxs("div", { ref: listRef, className: "crisp-tabs-list", role: "tablist", "aria-label": ariaLabel, onKeyDown: onKeyDown, children: [leading && _jsx("span", { className: "crisp-tabs-leading", children: leading }), tabs.map((t, i) => {
|
|
114
|
+
return (_jsxs("div", { className: tabsRecipe({ fill, className }), ...rest, children: [_jsxs("div", { ref: listRef, className: "crisp-tabs-list", "data-overflow": overflow, role: "tablist", "aria-label": ariaLabel, onKeyDown: onKeyDown, children: [leading && _jsx("span", { className: "crisp-tabs-leading", children: leading }), tabs.map((t, i) => {
|
|
109
115
|
const selected = t.value === active;
|
|
110
116
|
const overflowHidden = hiddenFrom != null && i >= hiddenFrom;
|
|
111
117
|
return (_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, _jsx("span", { className: "tabs-tab-label", children: t.label }), t.badge != null && (_jsx("span", { className: "crisp-tabs-tab-badge", children: t.badge }))] }, t.value));
|
|
112
|
-
}), _jsx("span", { ref: moreRef, className: "crisp-tabs-more", "data-tabs-overflow-hidden": hiddenTabs.length === 0 ? "" : undefined, children: _jsx(Menu, { "aria-label": moreMenuLabel, trigger: _jsxs("button", { type: "button", className: "crisp-tabs-tab", children: [_jsx("span", { className: "tabs-tab-label", children: moreLabel(hiddenTabs.length) }), _jsx(ChevronDown, { size: 16, strokeWidth: 1.75, "aria-hidden": "true" })] }), children: hiddenTabs.map((t) => (_jsxs(MenuItem, { icon: t.icon, onSelect: () => select(t.value), children: [t.label, t.badge != null && (_jsx("span", { className: "crisp-tabs-tab-badge", children: t.badge }))] }, t.value))) }) }), indicator && (_jsx("span", { className: "crisp-tabs-indicator", style: {
|
|
118
|
+
}), overflow === "collapse" && (_jsx("span", { ref: moreRef, className: "crisp-tabs-more", "data-tabs-overflow-hidden": hiddenTabs.length === 0 ? "" : undefined, children: _jsx(Menu, { "aria-label": moreMenuLabel, trigger: _jsxs("button", { type: "button", className: "crisp-tabs-tab", children: [_jsx("span", { className: "tabs-tab-label", children: moreLabel(hiddenTabs.length) }), _jsx(ChevronDown, { size: 16, strokeWidth: 1.75, "aria-hidden": "true" })] }), children: hiddenTabs.map((t) => (_jsxs(MenuItem, { icon: t.icon, onSelect: () => select(t.value), children: [t.label, t.badge != null && (_jsx("span", { className: "crisp-tabs-tab-badge", children: t.badge }))] }, t.value))) }) })), indicator && (_jsx("span", { className: "crisp-tabs-indicator", style: {
|
|
113
119
|
width: indicator.width,
|
|
114
120
|
transform: `translateX(${indicator.left}px)`,
|
|
115
121
|
} }))] }), children && (_jsx("div", { className: "crisp-tabs-panel", role: "tabpanel", id: panelId(active), "aria-labelledby": tabId(active),
|
|
@@ -5,10 +5,20 @@ import { contract as c } from "../../tokens/index.js";
|
|
|
5
5
|
// baseline needs, reserved as bottom padding so it's real box height and not
|
|
6
6
|
// paint that trailing content can sit on top of. The bar carries 12px inline
|
|
7
7
|
// padding too, and draws its own full-width `::after` baseline in
|
|
8
|
-
// borderSubtle at its bottom edge, spanning that padding.
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
8
|
+
// borderSubtle at its bottom edge, spanning that padding.
|
|
9
|
+
//
|
|
10
|
+
// your-moon/crisp#934: the selected tab used to be a light
|
|
11
|
+
// filled pill (bg #fbfbfb + inset ring borderSubtle, r8) with a 1px ink
|
|
12
|
+
// underline on top of it. Owner-directed: underline tabs, no fill pill --
|
|
13
|
+
// seed-design's own `tab.yaml` has no `root.color` property at all, at any
|
|
14
|
+
// state (selected/hover/pressed); selection is carried purely by the label
|
|
15
|
+
// darkening (muted -> ink, unchanged, already matched seed) plus the
|
|
16
|
+
// existing underline (already `fgPrimary`, "same colour as selected text,
|
|
17
|
+
// not brand" -- also already matched seed). Only the selected pill's own
|
|
18
|
+
// fill/ring is removed here; unselected tabs keep their existing subtle
|
|
19
|
+
// hover tint (a desktop hover affordance seed's spec doesn't need to
|
|
20
|
+
// consider one way or the other, and the owner didn't ask to remove).
|
|
21
|
+
// Inactive tabs are muted and fill to a subtle pill on hover. Each tab carries
|
|
12
22
|
// a 16px leading icon and an optional count badge. Measured from the live
|
|
13
23
|
// app: bar `padding: 0 12px 8px`, 28px pills, 4/8 padding, 6px icon-label
|
|
14
24
|
// gap, 14/500 labels. The count badge (crisp#697, re-measured against a
|
|
@@ -45,11 +55,18 @@ const config = defineRecipe({
|
|
|
45
55
|
|
|
46
56
|
& .crisp-tabs-list { position: relative; display: flex; align-items: center; gap: ${c.space["1"]}; box-sizing: content-box; height: ${c.size.row}; padding: 0 ${c.space["3"]} ${c.space["2"]}; flex-shrink: 0; }
|
|
47
57
|
& .crisp-tabs-list::after { content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 1px; background: ${c.borderSubtle}; }
|
|
58
|
+
/* crisp#908: an alternative to the "+N more" collapse above (crisp#863) --
|
|
59
|
+
a consumer opts into a single scrolling row instead (Naver/Kakao's own
|
|
60
|
+
pattern, docs/study/korean/), never both at once. No new visual value:
|
|
61
|
+
reuses the same list gap/tab geometry, only adds overflow-x + snap. */
|
|
62
|
+
& .crisp-tabs-list[data-overflow="scroll"] { overflow-x: auto; flex-wrap: nowrap; scroll-snap-type: x mandatory; scrollbar-width: none; }
|
|
63
|
+
& .crisp-tabs-list[data-overflow="scroll"]::-webkit-scrollbar { display: none; }
|
|
64
|
+
& .crisp-tabs-list[data-overflow="scroll"] .crisp-tabs-tab { flex-shrink: 0; scroll-snap-align: start; }
|
|
48
65
|
& .crisp-tabs-leading { display: inline-flex; align-items: center; margin-inline-end: ${c.space["1"]}; }
|
|
49
|
-
& .crisp-tabs-tab { position: relative; display: inline-flex; align-items: center; gap: ${c.space["1.5"]}; height: 28px; margin: 0; padding: 0 ${c.space["2"]}; border: none; background: none;
|
|
66
|
+
& .crisp-tabs-tab { position: relative; display: inline-flex; align-items: center; gap: ${c.space["1.5"]}; height: 28px; margin: 0; padding: 0 ${c.space["2"]}; border: none; background: none; border-radius: ${c.radius.md}; font-family: inherit; font-size: ${c.text.lg}; font-weight: ${c.weight.medium}; letter-spacing: -0.14px; color: ${c.fgMuted}; cursor: pointer; white-space: nowrap; transition: color .12s ease, background-color .12s ease; }
|
|
50
67
|
& .crisp-tabs-tab svg { flex-shrink: 0; opacity: 0.85; }
|
|
51
68
|
& .crisp-tabs-tab:hover:not([data-selected]) { background: color-mix(in srgb, ${c.fgPrimary} 4%, transparent); color: ${c.fgPrimary}; }
|
|
52
|
-
& .crisp-tabs-tab[data-selected] { color: ${c.fgPrimary};
|
|
69
|
+
& .crisp-tabs-tab[data-selected] { color: ${c.fgPrimary}; }
|
|
53
70
|
& .crisp-tabs-tab:focus-visible { outline: 2px solid ${c.bgBrandSolid}; outline-offset: 1px; }
|
|
54
71
|
& .crisp-tabs-tab-badge { display: inline-flex; align-items: center; justify-content: center; min-width: 14px; height: 14px; padding: 0 ${c.space["1"]}; border-radius: 4px; background: color-mix(in srgb, ${c.fgPrimary} 4%, transparent); box-shadow: inset 0 0 0 1px ${c.borderSubtle}; font-size: 10px; line-height: 14px; font-weight: ${c.weight.medium}; color: ${c.fgNeutralSoft}; }
|
|
55
72
|
& .crisp-tabs-tab[data-selected] .crisp-tabs-tab-badge { color: ${c.fgPrimary}; }
|
|
@@ -33,13 +33,14 @@ const spec = defineSpec({
|
|
|
33
33
|
muted: { color: c.fgNeutralMuted },
|
|
34
34
|
// #858: no new measurement -- both reuse tokens already measured
|
|
35
35
|
// against Attio for the same colours in a different arrangement.
|
|
36
|
-
// `brand` is `textlink`'s own reused brand-text colour
|
|
37
|
-
//
|
|
38
|
-
//
|
|
39
|
-
//
|
|
40
|
-
//
|
|
41
|
-
//
|
|
42
|
-
|
|
36
|
+
// `brand` is `textlink`'s own reused brand-text colour, now
|
|
37
|
+
// `fgBrandContrast` (your-moon/crisp#897: the plain accent,
|
|
38
|
+
// `bgBrandSolid`, only clears 3.23:1 as text on a white page).
|
|
39
|
+
// `danger` is `fgDanger` ("solid critical/destructive TEXT... an
|
|
40
|
+
// overdue due-date, a destructive menu item's label" -- see its own
|
|
41
|
+
// doc in tokens/semantic.ts), not `fgDangerSoft` (that one's a soft
|
|
42
|
+
// Badge pill's text, a different measured hex).
|
|
43
|
+
brand: { color: c.fgBrandContrast },
|
|
43
44
|
danger: { color: c.fgDanger },
|
|
44
45
|
},
|
|
45
46
|
},
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import * as React from "react";
|
|
3
|
+
import { Progress } from "../progress/index.js";
|
|
3
4
|
import { textareaRecipe } from "./textarea.recipe.js";
|
|
4
5
|
/**
|
|
5
6
|
* A multiline text field — measured from settings. Forwards every native
|
|
6
7
|
* `<textarea>` prop; pass `showCount` with `maxLength` to render a live
|
|
7
8
|
* character counter (which turns red if the value exceeds the limit).
|
|
8
9
|
*/
|
|
9
|
-
export const Textarea = React.forwardRef(({ showCount, maxLength, className, wrapperClassName, value, defaultValue, onChange,
|
|
10
|
+
export const Textarea = React.forwardRef(({ showCount, showProgress, maxLength, className, wrapperClassName, value, defaultValue, onChange,
|
|
10
11
|
// `style` is pulled out and put on the ROOT, the way Input does it.
|
|
11
12
|
// Everything else belongs on the <textarea> -- id, name, form
|
|
12
13
|
// association, aria -- but `style` sizes the visual box, and the box is
|
|
@@ -22,6 +23,6 @@ style, ...props }, ref) => {
|
|
|
22
23
|
if (value === undefined)
|
|
23
24
|
setCount(e.target.value.length);
|
|
24
25
|
onChange?.(e);
|
|
25
|
-
}, ...props }) }), counted && (_jsxs("span", { className: "crisp-textarea-count", "data-over": len > (maxLength ?? 0) ? "" : undefined, "aria-hidden": "true", children: [len, " / ", maxLength] }))] }));
|
|
26
|
+
}, ...props }) }), counted && (_jsxs("span", { className: "crisp-textarea-count", "data-over": len > (maxLength ?? 0) ? "" : undefined, "aria-hidden": "true", children: [showProgress && (_jsx(Progress, { variant: "circular", value: len, max: maxLength ?? 0, className: "crisp-textarea-progress" })), len, " / ", maxLength] }))] }));
|
|
26
27
|
});
|
|
27
28
|
Textarea.displayName = "Textarea";
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { defineRecipe, resolveRecipe } from "../../core/index.js";
|
|
2
|
-
import { contract as c
|
|
3
|
-
// A multiline text field:
|
|
2
|
+
import { contract as c } from "../../tokens/index.js";
|
|
3
|
+
// A multiline text field: a real 1px border (radius 8), 14px text, and an
|
|
4
4
|
// optional character counter pinned bottom-right (12px, muted). Focus rings
|
|
5
5
|
// the field brand.
|
|
6
6
|
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
7
|
+
// your-moon/crisp#920: the rest-state edge used to be `elevation.textField`,
|
|
8
|
+
// a `box-shadow` ring (#466's fix for a real typed field's 1px ring, not
|
|
9
|
+
// `field`'s button-chrome glow/drop-shadow layers) -- now a real `border`,
|
|
10
|
+
// matching Input's own #914/#910 technique and seed-design's `text-input.yaml`
|
|
11
|
+
// (an actual border that THICKENS on focus/invalid via the ::after overlay
|
|
12
|
+
// below, not a fixed-width ring that only recolors). `borderDefault` is
|
|
13
|
+
// seed's own `stroke.neutral-weak`, already the crisp token's exact value.
|
|
11
14
|
//
|
|
12
15
|
// The inset is seed-design's, not Attio's -- the second deliberate divergence
|
|
13
16
|
// after the focus treatment. seed's text-input sets its inline inset to
|
|
@@ -24,10 +27,10 @@ const config = defineRecipe({
|
|
|
24
27
|
|
|
25
28
|
& .crisp-textarea-field {
|
|
26
29
|
box-sizing: border-box; display: block; width: 100%; min-height: 80px; resize: vertical;
|
|
27
|
-
margin: 0; padding: ${c.space["2.5"]} 14px; border
|
|
28
|
-
background: ${c.bgSurface};
|
|
30
|
+
margin: 0; padding: ${c.space["2.5"]} 14px; border-radius: ${c.radius.md};
|
|
31
|
+
background: ${c.bgSurface}; border: 1px solid ${c.borderDefault};
|
|
29
32
|
font-family: inherit; font-size: ${c.text.lg}; line-height: 20px; color: ${c.fgPrimary};
|
|
30
|
-
outline: none;
|
|
33
|
+
outline: none;
|
|
31
34
|
-webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
|
|
32
35
|
}
|
|
33
36
|
& .crisp-textarea-field::placeholder { color: ${c.fgMuted}; }
|
|
@@ -46,8 +49,9 @@ const config = defineRecipe({
|
|
|
46
49
|
&[data-invalid] .crisp-textarea-wrap::after { border-color: ${c.bgDanger}; }
|
|
47
50
|
&[data-invalid] .crisp-textarea-wrap:focus-within::after { border-color: ${c.bgDanger}; }
|
|
48
51
|
&[data-counted] .crisp-textarea-field { padding-bottom: ${c.space["5"]}; }
|
|
49
|
-
& .crisp-textarea-count { position: absolute; inset-inline-end: 14px; bottom: ${c.space["2.5"]}; font-size: ${c.text.sm}; color: ${c.fgMuted}; pointer-events: none; }
|
|
52
|
+
& .crisp-textarea-count { position: absolute; inset-inline-end: 14px; bottom: ${c.space["2.5"]}; display: flex; align-items: center; gap: ${c.space["1"]}; font-size: ${c.text.sm}; color: ${c.fgMuted}; pointer-events: none; }
|
|
50
53
|
& .crisp-textarea-count[data-over] { color: ${c.bgDanger}; }
|
|
54
|
+
& .crisp-textarea-progress { flex-shrink: 0; }
|
|
51
55
|
`,
|
|
52
56
|
variants: {},
|
|
53
57
|
defaultVariants: {},
|
|
@@ -18,11 +18,14 @@ const config = defineRecipe({
|
|
|
18
18
|
variants: {
|
|
19
19
|
tone: {
|
|
20
20
|
// The bold, always-brand-coloured trailing link a Card's own `action`
|
|
21
|
-
// slot uses ("View all →", "All registers →").
|
|
22
|
-
|
|
21
|
+
// slot uses ("View all →", "All registers →"). your-moon/crisp#897:
|
|
22
|
+
// `fgBrandContrast`, not `bgBrandSolid` (#0298d9) directly -- as TEXT
|
|
23
|
+
// on a white page the accent itself only clears 3.23:1; the darker
|
|
24
|
+
// contrast-safe sibling clears 4.5:1+.
|
|
25
|
+
brand: `font-size: ${c.text.sm}; font-weight: ${c.weight.medium}; color: ${c.fgBrandContrast};`,
|
|
23
26
|
// A quieter in-context reference (a sample record under a finding)
|
|
24
27
|
// that only goes brand on hover.
|
|
25
|
-
muted: `font-size: ${c.text.md}; font-weight: ${c.weight.regular}; color: ${c.fgMuted}; &:hover { color: ${c.
|
|
28
|
+
muted: `font-size: ${c.text.md}; font-weight: ${c.weight.regular}; color: ${c.fgMuted}; &:hover { color: ${c.fgBrandContrast}; }`,
|
|
26
29
|
},
|
|
27
30
|
// crisp#874: an explicit, OPTIONAL size override -- no `defaultVariants`
|
|
28
31
|
// entry below, so omitting `size` emits no `--size_*` class at all and
|
package/dist/esm/index.js
CHANGED
|
@@ -2,11 +2,13 @@
|
|
|
2
2
|
// component, the design tokens, and the recipe core. Consumers also import the
|
|
3
3
|
// stylesheet once: import "@crisp-ui-kit/crisp/styles.css";
|
|
4
4
|
// primitives (components/)
|
|
5
|
+
export * from "./components/accentpicker/index.js";
|
|
5
6
|
export * from "./components/accordion/index.js";
|
|
6
7
|
export * from "./components/avatar/index.js";
|
|
7
8
|
export * from "./components/avatargroup/index.js";
|
|
8
9
|
export * from "./components/badge/index.js";
|
|
9
10
|
export * from "./components/barchart/index.js";
|
|
11
|
+
export * from "./components/bottomsheet/index.js";
|
|
10
12
|
export * from "./components/breadcrumb/index.js";
|
|
11
13
|
export * from "./components/bulkbar/index.js";
|
|
12
14
|
export * from "./components/button/index.js";
|
|
@@ -14,6 +16,7 @@ export * from "./components/callout/index.js";
|
|
|
14
16
|
export * from "./components/card/index.js";
|
|
15
17
|
export * from "./components/checkbox/index.js";
|
|
16
18
|
export * from "./components/chip/index.js";
|
|
19
|
+
export * from "./components/chipgroup/index.js";
|
|
17
20
|
export * from "./components/columncalc/index.js";
|
|
18
21
|
export * from "./components/composewindow/index.js";
|
|
19
22
|
export * from "./components/contextmenu/index.js";
|
|
@@ -35,6 +38,7 @@ export * from "./components/kbd/index.js";
|
|
|
35
38
|
export * from "./components/linechart/index.js";
|
|
36
39
|
export * from "./components/mentionchip/index.js";
|
|
37
40
|
export * from "./components/menu/index.js";
|
|
41
|
+
export * from "./components/menusheet/index.js";
|
|
38
42
|
export * from "./components/middletruncate/index.js";
|
|
39
43
|
export * from "./components/multiselect/index.js";
|
|
40
44
|
export * from "./components/personchip/index.js";
|
|
@@ -48,6 +52,7 @@ export * from "./components/select/index.js";
|
|
|
48
52
|
export * from "./components/skeleton/index.js";
|
|
49
53
|
export * from "./components/skiplink/index.js";
|
|
50
54
|
export * from "./components/slider/index.js";
|
|
55
|
+
export * from "./components/snackbar/index.js";
|
|
51
56
|
export * from "./components/spinner/index.js";
|
|
52
57
|
export * from "./components/stagebar/index.js";
|
|
53
58
|
export * from "./components/statcard/index.js";
|
|
@@ -82,7 +87,9 @@ export * from "./product/headerbar/index.js";
|
|
|
82
87
|
export * from "./product/importdialog/index.js";
|
|
83
88
|
export * from "./product/inbox/index.js";
|
|
84
89
|
export * from "./product/listpage/index.js";
|
|
90
|
+
export * from "./product/memberssettings/index.js";
|
|
85
91
|
export * from "./product/navitem/index.js";
|
|
92
|
+
export * from "./product/notificationssettings/index.js";
|
|
86
93
|
export * from "./product/objectsettings/index.js";
|
|
87
94
|
export * from "./product/onboarding/index.js";
|
|
88
95
|
export * from "./product/recordpage/index.js";
|
|
@@ -97,5 +104,6 @@ export * from "./product/tasklist/index.js";
|
|
|
97
104
|
export * from "./product/themepicker/index.js";
|
|
98
105
|
export * from "./product/trialbanner/index.js";
|
|
99
106
|
export * from "./product/viewtoggle/index.js";
|
|
107
|
+
export * from "./product/workspacegeneralsettings/index.js";
|
|
100
108
|
// design tokens + recipe core (for building custom components)
|
|
101
109
|
export * from "./tokens/index.js";
|
|
@@ -40,7 +40,11 @@ const config = defineRecipe({
|
|
|
40
40
|
& .crisp-authshell-progress-fill { height: 2px; background: ${c.fgPrimary}; border-radius: 4px; transition: width .2s; }
|
|
41
41
|
|
|
42
42
|
& .crisp-authshell-logo { margin-top: ${c.space["8"]}; min-height: 24px; display: flex; align-items: center; gap: ${c.space["2.5"]}; }
|
|
43
|
-
|
|
43
|
+
/* your-moon/crisp#953: bgBrandContrast, not bgBrandSolid --
|
|
44
|
+
the accent (#0298d9) only clears 3.23:1 with white; the darker
|
|
45
|
+
sibling clears 4.5:1+, same fix already applied to Button/Badge/
|
|
46
|
+
Avatar (#897). */
|
|
47
|
+
& .crisp-authshell-logo-badge { flex-shrink: 0; box-sizing: border-box; width: 32px; height: 32px; border-radius: ${c.radius.md}; background: ${c.bgBrandContrast}; color: #fff; display: flex; align-items: center; justify-content: center; }
|
|
44
48
|
& .crisp-authshell-logo-text { display: flex; flex-direction: column; line-height: 1.2; }
|
|
45
49
|
& .crisp-authshell-logo-title { font-size: ${c.text.xl}; font-weight: ${c.weight.bold}; letter-spacing: -0.2px; color: ${c.fgPrimary}; }
|
|
46
50
|
& .crisp-authshell-logo-subtitle { font-size: ${c.text.sm}; color: ${c.fgMuted}; }
|
|
@@ -29,7 +29,10 @@ const config = defineRecipe({
|
|
|
29
29
|
& .crisp-command-list > .crisp-command-group-label:first-child { padding-top: ${c.space["1"]}; }
|
|
30
30
|
& .crisp-command-footer { display: flex; align-items: center; justify-content: space-between; flex-shrink: 0; height: 44px; padding: 0 ${c.space["2.5"]}; border-top: 1px solid ${c.borderSubtle}; }
|
|
31
31
|
& .crisp-command-footer-nav { display: flex; align-items: center; gap: ${c.space["1.5"]}; font-size: ${c.text.sm}; color: ${c.fgMuted}; }
|
|
32
|
-
|
|
32
|
+
/* your-moon/crisp#953: bgBrandContrast, not bgBrandSolid --
|
|
33
|
+
the accent (#0298d9) only clears 3.23:1 with white; the darker sibling
|
|
34
|
+
clears 4.5:1+, same fix already applied to Button/Badge/Avatar (#897). */
|
|
35
|
+
& .crisp-command-footer-action { display: flex; align-items: center; gap: ${c.space["2"]}; height: 28px; max-width: 60%; padding: 0 ${c.space["1"]} 0 ${c.space["2.5"]}; border: none; border-radius: ${c.radius.md}; background: ${c.bgBrandContrast}; color: ${c.fgOnBrand}; font-family: inherit; font-size: ${c.text.md}; font-weight: ${c.weight.medium}; cursor: pointer; }
|
|
33
36
|
& .crisp-command-footer-action:disabled { opacity: 0.5; cursor: default; }
|
|
34
37
|
& .crisp-command-footer-action-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
35
38
|
& .crisp-command-footer-key { display: inline-flex; align-items: center; justify-content: center; min-width: 18px; height: 18px; padding: 0 ${c.space["1"]}; border-radius: ${c.radius.sm}; background: rgba(255,255,255,0.22); font-size: ${c.text.xs}; }
|
|
@@ -277,8 +277,13 @@ function CommentRow({ comment, replyLabel, reactLabel, resolveLabel, moreLabel,
|
|
|
277
277
|
* nothing on its own beyond that data. See `CommentCountBadge` for the
|
|
278
278
|
* separate row affordance (P-051) — deliberately NOT part of this
|
|
279
279
|
* component, since it lives in a table cell this component doesn't own.
|
|
280
|
+
*
|
|
281
|
+
* `variant="drawer"` (default) is today's measured 320px + left-border
|
|
282
|
+
* look; `variant="pane"` (crisp#960) fills its container's width instead,
|
|
283
|
+
* for a consumer embedding this in a full-width tab pane rather than a
|
|
284
|
+
* true slide-over/drawer.
|
|
280
285
|
*/
|
|
281
|
-
export function CommentsPanel({ threads, currentUserId, subject, subjectIcon, showResolved = false, onShowResolvedChange, onlyMine = false, onOnlyMineChange, activeThreadId: activeThreadIdProp, onActiveThreadChange, onPost, onReact, onResolve, onDelete, onCopyLink, emojiOptions = DEFAULT_EMOJI, mentionTrigger = "@", renderMentionPicker, onClose, closeLabel: closeLabelProp, backLabel: backLabelProp, title: titleProp, threadTitle: threadTitleProp, breadcrumbPrefix: breadcrumbPrefixProp, composerPlaceholder: composerPlaceholderProp, replyPlaceholder: replyPlaceholderProp, postLabel: postLabelProp, replyPostLabel: replyPostLabelProp, emptyLabel, filterLabel: filterLabelProp, showResolvedLabel: showResolvedLabelProp, onlyMineLabel: onlyMineLabelProp, replyActionLabel: replyActionLabelProp, reactActionLabel: reactActionLabelProp, resolveActionLabel: resolveActionLabelProp, resolvedByYouLabel: resolvedByYouLabelProp, resolvedByLabel, moreActionsLabel: moreActionsLabelProp, copyLinkLabel: copyLinkLabelProp, deleteLabel: deleteLabelProp, addReactionLabel: addReactionLabelProp, reactionSearchLabel: reactionSearchLabelProp, deleteConfirmTitle: deleteConfirmTitleProp, deleteConfirmBody: deleteConfirmBodyProp, deleteConfirmActionLabel: deleteConfirmActionLabelProp, cancelLabel: cancelLabelProp, composerMentionToolLabel: composerMentionToolLabelProp, className, ...rest }) {
|
|
286
|
+
export function CommentsPanel({ threads, currentUserId, subject, subjectIcon, showResolved = false, onShowResolvedChange, onlyMine = false, onOnlyMineChange, activeThreadId: activeThreadIdProp, onActiveThreadChange, onPost, onReact, onResolve, onDelete, onCopyLink, emojiOptions = DEFAULT_EMOJI, mentionTrigger = "@", renderMentionPicker, onClose, closeLabel: closeLabelProp, backLabel: backLabelProp, title: titleProp, threadTitle: threadTitleProp, breadcrumbPrefix: breadcrumbPrefixProp, composerPlaceholder: composerPlaceholderProp, replyPlaceholder: replyPlaceholderProp, postLabel: postLabelProp, replyPostLabel: replyPostLabelProp, emptyLabel, filterLabel: filterLabelProp, showResolvedLabel: showResolvedLabelProp, onlyMineLabel: onlyMineLabelProp, replyActionLabel: replyActionLabelProp, reactActionLabel: reactActionLabelProp, resolveActionLabel: resolveActionLabelProp, resolvedByYouLabel: resolvedByYouLabelProp, resolvedByLabel, moreActionsLabel: moreActionsLabelProp, copyLinkLabel: copyLinkLabelProp, deleteLabel: deleteLabelProp, addReactionLabel: addReactionLabelProp, reactionSearchLabel: reactionSearchLabelProp, deleteConfirmTitle: deleteConfirmTitleProp, deleteConfirmBody: deleteConfirmBodyProp, deleteConfirmActionLabel: deleteConfirmActionLabelProp, cancelLabel: cancelLabelProp, composerMentionToolLabel: composerMentionToolLabelProp, variant, className, ...rest }) {
|
|
282
287
|
const closeLabel = useCrispMessage(closeLabelProp, "Close");
|
|
283
288
|
const backLabel = useCrispMessage(backLabelProp, "Back");
|
|
284
289
|
const title = useCrispMessage(titleProp, "All comments");
|
|
@@ -331,7 +336,7 @@ export function CommentsPanel({ threads, currentUserId, subject, subjectIcon, sh
|
|
|
331
336
|
const activeThread = activeThreadId
|
|
332
337
|
? threads.find((t) => t.id === activeThreadId)
|
|
333
338
|
: null;
|
|
334
|
-
return (_jsxs("div", { className: commentsRecipe({ className }), role: "dialog", "aria-label": title, ...rest, children: [_jsxs("div", { className: "crisp-comments-head", children: [activeThread ? (_jsx("button", { type: "button", className: "crisp-comments-headbtn", "aria-label": backLabel, onClick: () => setActiveThreadId(null), children: _jsx(ChevronLeft, { "data-rtl-flip": "", size: 16, strokeWidth: 1.75, "aria-hidden": "true" }) })) : null, _jsxs("div", { className: "crisp-comments-title-wrap", children: [_jsx("span", { className: "crisp-comments-title", children: activeThread ? threadTitle : title }), activeThread && (_jsxs("span", { className: "crisp-comments-breadcrumb", children: [breadcrumbPrefix, " ", subjectIcon, " ", subject] }))] }), !activeThread && (_jsxs(Menu, { align: "end", trigger: _jsx("button", { type: "button", className: "crisp-comments-headbtn", "aria-label": filterLabel, children: _jsx(ListFilter, { size: 16, strokeWidth: 1.75, "aria-hidden": "true" }) }), children: [_jsx(MenuCheckboxItem, { checked: showResolved, onCheckedChange: onShowResolvedChange, children: showResolvedLabel }), _jsx(MenuCheckboxItem, { checked: onlyMine, onCheckedChange: onOnlyMineChange, children: onlyMineLabel })] })), onClose && (_jsx("button", { type: "button", className: "crisp-comments-headbtn", "aria-label": closeLabel, onClick: onClose, children: _jsx(X, { size: 16, strokeWidth: 1.75, "aria-hidden": "true" }) }))] }), _jsx("div", { className: "crisp-comments-body crisp-comments-body--pad-top", children: activeThread ? (_jsxs("div", { children: [_jsx(CommentRow, { comment: activeThread.root, currentUserId: currentUserId, replyLabel: replyActionLabel, reactLabel: reactActionLabel, resolveLabel: resolveActionLabel, moreLabel: moreActionsLabel, copyLinkLabel: copyLinkLabel, deleteLabel: deleteLabel, addReactionLabel: addReactionLabel, reactionSearchLabel: reactionSearchLabel, emojiOptions: emojiOptions, resolvable: !!onResolve, resolved: activeThread.resolved, resolvedLabel: activeThread.resolved
|
|
339
|
+
return (_jsxs("div", { className: commentsRecipe({ variant, className }), role: "dialog", "aria-label": title, ...rest, children: [_jsxs("div", { className: "crisp-comments-head", children: [activeThread ? (_jsx("button", { type: "button", className: "crisp-comments-headbtn", "aria-label": backLabel, onClick: () => setActiveThreadId(null), children: _jsx(ChevronLeft, { "data-rtl-flip": "", size: 16, strokeWidth: 1.75, "aria-hidden": "true" }) })) : null, _jsxs("div", { className: "crisp-comments-title-wrap", children: [_jsx("span", { className: "crisp-comments-title", children: activeThread ? threadTitle : title }), activeThread && (_jsxs("span", { className: "crisp-comments-breadcrumb", children: [breadcrumbPrefix, " ", subjectIcon, " ", subject] }))] }), !activeThread && (_jsxs(Menu, { align: "end", trigger: _jsx("button", { type: "button", className: "crisp-comments-headbtn", "aria-label": filterLabel, children: _jsx(ListFilter, { size: 16, strokeWidth: 1.75, "aria-hidden": "true" }) }), children: [_jsx(MenuCheckboxItem, { checked: showResolved, onCheckedChange: onShowResolvedChange, children: showResolvedLabel }), _jsx(MenuCheckboxItem, { checked: onlyMine, onCheckedChange: onOnlyMineChange, children: onlyMineLabel })] })), onClose && (_jsx("button", { type: "button", className: "crisp-comments-headbtn", "aria-label": closeLabel, onClick: onClose, children: _jsx(X, { size: 16, strokeWidth: 1.75, "aria-hidden": "true" }) }))] }), _jsx("div", { className: "crisp-comments-body crisp-comments-body--pad-top", children: activeThread ? (_jsxs("div", { children: [_jsx(CommentRow, { comment: activeThread.root, currentUserId: currentUserId, replyLabel: replyActionLabel, reactLabel: reactActionLabel, resolveLabel: resolveActionLabel, moreLabel: moreActionsLabel, copyLinkLabel: copyLinkLabel, deleteLabel: deleteLabel, addReactionLabel: addReactionLabel, reactionSearchLabel: reactionSearchLabel, emojiOptions: emojiOptions, resolvable: !!onResolve, resolved: activeThread.resolved, resolvedLabel: activeThread.resolved
|
|
335
340
|
? activeThread.resolvedBy?.id === currentUserId ||
|
|
336
341
|
!activeThread.resolvedBy
|
|
337
342
|
? resolvedByYouLabel
|
|
@@ -15,8 +15,8 @@ import { contract as c, elevation as e } from "../../tokens/index.js";
|
|
|
15
15
|
// width and still gets an odd number).
|
|
16
16
|
const config = defineRecipe({
|
|
17
17
|
name: "comments",
|
|
18
|
-
base: `display: flex; flex-direction: column; box-sizing: border-box;
|
|
19
|
-
background: ${c.bgSurface};
|
|
18
|
+
base: `display: flex; flex-direction: column; box-sizing: border-box; height: 100%;
|
|
19
|
+
background: ${c.bgSurface};
|
|
20
20
|
font-family: ${c.font.sans};
|
|
21
21
|
-webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
|
|
22
22
|
& * { box-sizing: border-box; }
|
|
@@ -96,9 +96,13 @@ const config = defineRecipe({
|
|
|
96
96
|
box-shadow, so this reuses that literal rather than re-deriving it.
|
|
97
97
|
32px/radius.lg/6px-10px padding has no matching Button \`size\` yet
|
|
98
98
|
(24/28/40 are the three that exist), so this stays hand-rolled instead
|
|
99
|
-
of stretching one of those.
|
|
100
|
-
|
|
101
|
-
|
|
99
|
+
of stretching one of those. your-moon/crisp#953:
|
|
100
|
+
bgBrandContrast, not bgBrandSolid -- the accent (#0298d9) only clears
|
|
101
|
+
3.23:1 with white; same fix already applied to Button/Badge/Avatar
|
|
102
|
+
(#897). Hover is a brightness filter on the new fill, matching that
|
|
103
|
+
PR's own Button primary treatment, not bgBrandSolidPressed. */
|
|
104
|
+
& .crisp-comments-post { display: inline-flex; align-items: center; justify-content: center; height: 32px; padding: 6px ${c.space["2.5"]}; border: none; border-radius: ${c.radius.lg}; background: ${c.bgBrandContrast}; box-shadow: inset 0 0 0 1px rgba(0,0,0,0.06), 0 2px 4px -2px rgba(38,109,240,0.12), 0 3px 6px -2px rgba(38,109,240,0.08); font-family: inherit; font-size: ${c.text.lg}; font-weight: ${c.weight.semibold}; color: ${c.fgOnBrand}; cursor: pointer; opacity: 1; transition: opacity .12s, filter .12s; }
|
|
105
|
+
& .crisp-comments-post:hover:not(:disabled) { filter: brightness(0.96); }
|
|
102
106
|
& .crisp-comments-post:disabled { opacity: .4; cursor: default; }
|
|
103
107
|
|
|
104
108
|
/* Filter menu -- measured: 220px, radius 12 (radius.xl), 4px padding,
|
|
@@ -110,8 +114,22 @@ const config = defineRecipe({
|
|
|
110
114
|
rather than re-derived since it's the same atomic-chip affordance. */
|
|
111
115
|
& .crisp-comments-mention { display: inline; padding: 0 2px; border-radius: 4px; background: color-mix(in srgb, ${c.bgBrandSolid} 12%, transparent); color: ${c.bgBrandSolid}; font-weight: ${c.weight.medium}; user-select: none; }
|
|
112
116
|
${rtlFlipCss}`,
|
|
113
|
-
variants: {
|
|
114
|
-
|
|
117
|
+
variants: {
|
|
118
|
+
// your-moon/crisp#960: the 320px width + left-border hairline is a
|
|
119
|
+
// drawer-consumer look (RecordPage's own comments drawer, #864), not
|
|
120
|
+
// an intrinsic property of the panel itself. A consumer embedding this
|
|
121
|
+
// in a full-width tab pane (internal/grc's own record-page Comments
|
|
122
|
+
// tab, sibling to Related/Activity/Notes/Tasks/Lists, all of which
|
|
123
|
+
// fill the pane) needs `width: 100%` and no border -- a tab pane
|
|
124
|
+
// doesn't need a seam separating it from itself. `drawer` (default)
|
|
125
|
+
// is today's exact, already-measured look; nothing changes for an
|
|
126
|
+
// existing consumer that doesn't pass `variant`.
|
|
127
|
+
variant: {
|
|
128
|
+
drawer: `width: 320px; border-inline-start: 1px solid ${c.borderSubtle};`,
|
|
129
|
+
pane: `width: 100%;`,
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
defaultVariants: { variant: "drawer" },
|
|
115
133
|
compoundVariants: [],
|
|
116
134
|
// Two pieces below intentionally live OUTSIDE the nested \`base\` above,
|
|
117
135
|
// the same reason Menu's own \`.crisp-menu-content\` does (menu.recipe.ts's
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { InviteMembersDialog, MembersSettings, } from "./memberssettings.js";
|