@microbit/ui 0.1.0-alpha.3 → 0.1.0-alpha.31
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/LICENSE.md +40 -0
- package/README.md +279 -26
- package/lang/ui.ar.json +62 -0
- package/lang/ui.ca.json +43 -3
- package/lang/ui.cy.json +62 -0
- package/lang/ui.de.json +62 -0
- package/lang/ui.en-us.json +40 -0
- package/lang/ui.en.json +40 -0
- package/lang/ui.es-es.json +43 -3
- package/lang/ui.fr.json +44 -4
- package/lang/ui.ga-ie.json +62 -0
- package/lang/ui.it.json +62 -0
- package/lang/ui.ja.json +44 -4
- package/lang/ui.ko.json +44 -4
- package/lang/ui.lo.json +62 -0
- package/lang/ui.lol.json +40 -0
- package/lang/ui.nl.json +44 -4
- package/lang/ui.pl.json +44 -4
- package/lang/ui.pt-br.json +44 -4
- package/lang/ui.vi.json +62 -0
- package/lang/ui.zh-cn.json +62 -0
- package/lang/ui.zh-tw.json +44 -4
- package/package.json +19 -10
- package/postcss-legacy-safari.cjs +96 -0
- package/reset.css +35 -0
- package/src/Avatar.recipe.ts +191 -0
- package/src/Avatar.tsx +278 -0
- package/src/Breadcrumb.recipe.ts +45 -0
- package/src/Breadcrumb.tsx +114 -0
- package/src/Button.recipe.ts +88 -50
- package/src/Button.tsx +68 -30
- package/src/ButtonGroup.tsx +37 -15
- package/src/Card.recipe.ts +1 -2
- package/src/Card.tsx +2 -1
- package/src/Checkbox.recipe.ts +49 -14
- package/src/Checkbox.tsx +111 -32
- package/src/CheckboxGroup.tsx +70 -0
- package/src/CloseButton.tsx +3 -3
- package/src/CloseIcon.tsx +6 -4
- package/src/Code.tsx +20 -0
- package/src/Collapse.tsx +179 -0
- package/src/ComboBox.tsx +246 -0
- package/src/Divider.tsx +72 -8
- package/src/Drawer.recipe.ts +21 -10
- package/src/Drawer.tsx +3 -4
- package/src/ExternalLink.tsx +43 -0
- package/src/Fade.tsx +62 -0
- package/src/Field.recipe.ts +114 -0
- package/src/Field.tsx +187 -0
- package/src/GridList.recipe.ts +57 -0
- package/src/GridList.tsx +81 -0
- package/src/Heading.recipe.ts +20 -1
- package/src/Heading.tsx +3 -3
- package/src/Icon.tsx +23 -7
- package/src/IconButton.tsx +8 -13
- package/src/Image.tsx +1 -1
- package/src/Input.recipe.ts +41 -28
- package/src/Input.tsx +23 -7
- package/src/InputGroup.tsx +26 -12
- package/src/Kbd.tsx +26 -0
- package/src/Link.tsx +3 -3
- package/src/LinkBox.tsx +2 -3
- package/src/LinkButton.tsx +81 -0
- package/src/List.tsx +8 -6
- package/src/ListBox.recipe.ts +43 -0
- package/src/ListBox.tsx +88 -0
- package/src/Menu.recipe.ts +51 -17
- package/src/Menu.tsx +117 -2
- package/src/Modal.recipe.ts +17 -9
- package/src/Modal.tsx +137 -24
- package/src/MoreMenuButton.tsx +63 -0
- package/src/NativeSelect.tsx +41 -14
- package/src/NativeSelectField.tsx +84 -0
- package/src/NumberField.recipe.ts +108 -0
- package/src/NumberField.tsx +138 -0
- package/src/PopoverArrow.tsx +19 -5
- package/src/ProgressBar.tsx +3 -5
- package/src/Radio.recipe.ts +108 -0
- package/src/Radio.tsx +62 -0
- package/src/RadioGroup.tsx +68 -0
- package/src/Select.recipe.ts +211 -0
- package/src/Select.tsx +194 -0
- package/src/SharedUIProvider.tsx +63 -7
- package/src/Skeleton.tsx +146 -0
- package/src/Slide.tsx +2 -2
- package/src/Slider.recipe.ts +27 -16
- package/src/Slider.tsx +78 -6
- package/src/Spinner.tsx +11 -9
- package/src/Svg.tsx +2 -3
- package/src/Switch.recipe.ts +57 -11
- package/src/Switch.tsx +39 -7
- package/src/Text.recipe.ts +26 -0
- package/src/Text.tsx +6 -2
- package/src/TextField.tsx +54 -40
- package/src/Toast.recipe.ts +52 -15
- package/src/Toast.tsx +151 -46
- package/src/Tooltip.recipe.ts +32 -0
- package/src/Tooltip.tsx +68 -28
- package/src/TooltipButton.tsx +229 -0
- package/src/UnmountCallback.tsx +2 -2
- package/src/VisuallyHidden.tsx +1 -1
- package/src/base-preset.ts +336 -84
- package/src/{chakra-tokens.ts → base-tokens.ts} +7 -7
- package/src/button-icon.ts +22 -0
- package/src/data-attrs.ts +16 -0
- package/src/dense-preset.ts +107 -0
- package/src/hooks/useBreakpointValue.ts +4 -4
- package/src/hooks/useClipboard.ts +63 -0
- package/src/hooks/useDisclosure.ts +31 -0
- package/src/hooks/useMediaQuery.ts +27 -0
- package/src/hooks/usePrevious.ts +15 -0
- package/src/index.ts +29 -3
- package/src/rac-locale.ts +33 -0
- package/src/system.ts +22 -1
- package/src/TextField.recipe.ts +0 -54
package/src/Button.recipe.ts
CHANGED
|
@@ -5,26 +5,24 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { defineRecipe } from "@pandacss/dev";
|
|
7
7
|
|
|
8
|
-
//
|
|
9
|
-
// token category).
|
|
8
|
+
// The common transition-property list, inlined (Panda has no
|
|
9
|
+
// transitionProperty token category). No box-shadow: focus indication
|
|
10
|
+
// must never fade in.
|
|
10
11
|
const transitionCommon =
|
|
11
|
-
"background-color, border-color, color, fill, stroke, opacity,
|
|
12
|
+
"background-color, border-color, color, fill, stroke, opacity, transform";
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
|
-
* Button recipe —
|
|
15
|
-
* borderRadius (`button` = 2rem)
|
|
16
|
-
* `components/button.ts`. Interaction conditions (`_hover`/`_active`/
|
|
15
|
+
* Button recipe — the family button base + sizes, with the house
|
|
16
|
+
* borderRadius (`button` = 2rem). Interaction conditions (`_hover`/`_active`/
|
|
17
17
|
* `_disabled`/`_focusVisible`) are widened in the preset to also match
|
|
18
|
-
* react-aria-components' data attributes, so these
|
|
19
|
-
*
|
|
18
|
+
* react-aria-components' data attributes, so these variant objects work
|
|
19
|
+
* unchanged on RAC's <Button>.
|
|
20
20
|
*
|
|
21
21
|
* A config recipe (not a component cva): styles land in the `recipes` layer so
|
|
22
22
|
* call sites can override with plain style props, and presets extend the
|
|
23
|
-
* variants.
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* `led`/`record*`/`secondary-disabled`). Brand divergence within a variant is
|
|
27
|
-
* token-driven (see the `languageText` semantic tokens).
|
|
23
|
+
* variants. Colour reaches a variant either through the `button.*` semantic
|
|
24
|
+
* tokens (the app's button idiom) or through `tone`, never through a per-app
|
|
25
|
+
* fork of a shape.
|
|
28
26
|
*
|
|
29
27
|
* Registered in the base preset (base-preset.ts).
|
|
30
28
|
*/
|
|
@@ -47,7 +45,7 @@ export const button = defineRecipe({
|
|
|
47
45
|
verticalAlign: "middle",
|
|
48
46
|
outline: "none",
|
|
49
47
|
_focusVisible: {
|
|
50
|
-
|
|
48
|
+
focusRing: "outline",
|
|
51
49
|
},
|
|
52
50
|
_disabled: {
|
|
53
51
|
opacity: 0.4,
|
|
@@ -68,7 +66,7 @@ export const button = defineRecipe({
|
|
|
68
66
|
xs: { h: "6", minW: "6", fontSize: "xs", px: "2" },
|
|
69
67
|
},
|
|
70
68
|
variant: {
|
|
71
|
-
//
|
|
69
|
+
// A full reset: the button renders as plain inline content.
|
|
72
70
|
unstyled: {
|
|
73
71
|
bg: "none",
|
|
74
72
|
color: "inherit",
|
|
@@ -78,7 +76,7 @@ export const button = defineRecipe({
|
|
|
78
76
|
p: "0",
|
|
79
77
|
borderRadius: "unset",
|
|
80
78
|
},
|
|
81
|
-
//
|
|
79
|
+
// Link-shaped button: no padding/height, underline on hover.
|
|
82
80
|
link: {
|
|
83
81
|
padding: 0,
|
|
84
82
|
height: "auto",
|
|
@@ -92,13 +90,22 @@ export const button = defineRecipe({
|
|
|
92
90
|
textDecoration: "underline",
|
|
93
91
|
},
|
|
94
92
|
},
|
|
93
|
+
// Colours come from the `button.*` semantic tokens so the family's two
|
|
94
|
+
// button idioms (brand-coloured vs black-on-white) share this recipe —
|
|
95
|
+
// see the token block in base-preset.ts.
|
|
95
96
|
secondary: {
|
|
96
97
|
borderWidth: "2px",
|
|
97
|
-
borderColor: "
|
|
98
|
-
color: "
|
|
98
|
+
borderColor: "button.secondaryBorder",
|
|
99
|
+
color: "button.secondaryText",
|
|
99
100
|
bg: "transparent",
|
|
100
|
-
_hover: {
|
|
101
|
-
|
|
101
|
+
_hover: {
|
|
102
|
+
borderColor: "button.secondaryHoverBorder",
|
|
103
|
+
bg: "button.secondaryHoverBg",
|
|
104
|
+
},
|
|
105
|
+
_active: {
|
|
106
|
+
bg: "button.secondaryActiveBg",
|
|
107
|
+
borderColor: "button.secondaryActiveBorder",
|
|
108
|
+
},
|
|
102
109
|
},
|
|
103
110
|
ghost: {
|
|
104
111
|
color: "black",
|
|
@@ -106,57 +113,88 @@ export const button = defineRecipe({
|
|
|
106
113
|
_hover: { bg: "blackAlpha.50" },
|
|
107
114
|
_active: { bg: "blackAlpha.100" },
|
|
108
115
|
},
|
|
109
|
-
//
|
|
110
|
-
//
|
|
111
|
-
//
|
|
112
|
-
// own colour and shape via instance styles.
|
|
116
|
+
// Base-only styling: a transparent, colour-inheriting button. Used for
|
|
117
|
+
// the action-bar icon-button menu triggers (settings/help), which supply
|
|
118
|
+
// their own colour and shape via instance styles.
|
|
113
119
|
plain: {
|
|
114
120
|
bg: "transparent",
|
|
115
121
|
color: "inherit",
|
|
116
122
|
},
|
|
117
123
|
primary: {
|
|
118
124
|
color: "white",
|
|
119
|
-
bg: "
|
|
120
|
-
_hover: {
|
|
121
|
-
|
|
125
|
+
bg: "button.primaryBg",
|
|
126
|
+
_hover: {
|
|
127
|
+
bg: "button.primaryHoverBg",
|
|
128
|
+
_disabled: { bg: "button.primaryBg" },
|
|
129
|
+
},
|
|
130
|
+
_active: { bg: "button.primaryActiveBg" },
|
|
122
131
|
},
|
|
123
|
-
|
|
132
|
+
// `solid`/`outline` are coloured by `tone`, and reference only the
|
|
133
|
+
// stops it guarantees (50/500/600/700). They are not `primary`/
|
|
134
|
+
// `secondary` in another colour: those follow the app's button idiom,
|
|
135
|
+
// which is black-on-white in half the family and so can't be a palette.
|
|
136
|
+
solid: {
|
|
137
|
+
color: "white",
|
|
138
|
+
bg: "colorPalette.500",
|
|
139
|
+
_hover: {
|
|
140
|
+
bg: "colorPalette.600",
|
|
141
|
+
_disabled: { bg: "colorPalette.500" },
|
|
142
|
+
},
|
|
143
|
+
_active: { bg: "colorPalette.700" },
|
|
144
|
+
},
|
|
145
|
+
// Border at 500 (a boundary needs only 3:1) so a `solid`/`outline`
|
|
146
|
+
// toggle pair shows the same edge either way; the label needs 600.
|
|
147
|
+
//
|
|
148
|
+
// Classroom restates two other outlines per call site — a neutral 1px
|
|
149
|
+
// grey one and a white-on-colour one. Promote either if a second
|
|
150
|
+
// consumer appears.
|
|
151
|
+
outline: {
|
|
124
152
|
borderWidth: "2px",
|
|
125
|
-
borderColor: "
|
|
126
|
-
color: "
|
|
153
|
+
borderColor: "colorPalette.500",
|
|
154
|
+
color: "colorPalette.600",
|
|
127
155
|
bg: "transparent",
|
|
128
|
-
_hover: { borderColor: "
|
|
129
|
-
_active: { bg: "
|
|
156
|
+
_hover: { borderColor: "colorPalette.600", color: "colorPalette.700" },
|
|
157
|
+
_active: { bg: "colorPalette.50" },
|
|
130
158
|
},
|
|
131
|
-
//
|
|
132
|
-
//
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
bg: "
|
|
138
|
-
_hover: { bg: "danger.600", _disabled: { bg: "danger.500" } },
|
|
139
|
-
_active: { bg: "danger.700" },
|
|
159
|
+
// Not a palette shape: a light fill under dark text is a different
|
|
160
|
+
// button from `solid`, and 350 exists in no other ramp.
|
|
161
|
+
neutral: {
|
|
162
|
+
color: "gray.800",
|
|
163
|
+
bg: "gray.100",
|
|
164
|
+
_hover: { bg: "gray.300", _disabled: { bg: "gray.100" } },
|
|
165
|
+
_active: { bg: "gray.350" },
|
|
140
166
|
},
|
|
141
|
-
// Family-wide
|
|
142
|
-
//
|
|
167
|
+
// Family-wide variant (every censused app has toolbar-class buttons).
|
|
168
|
+
// No ring override: the bar decides, and a dark one must spread
|
|
169
|
+
// `darkSurface` — an app adopting this variant needs the tag with it.
|
|
143
170
|
toolbar: {
|
|
144
171
|
color: "black",
|
|
145
172
|
bg: "white",
|
|
146
173
|
_hover: { bg: "whiteAlpha.900", _disabled: { bg: "white" } },
|
|
147
174
|
_active: { bg: "whiteAlpha.800" },
|
|
148
|
-
_focusVisible: { focusShadow: "outlineDark" },
|
|
149
|
-
},
|
|
150
|
-
language: {
|
|
151
|
-
borderWidth: "2px",
|
|
152
|
-
borderColor: "gray.200",
|
|
153
|
-
color: "languageText",
|
|
154
|
-
_hover: { color: "languageTextHover", bg: "gray.100" },
|
|
155
175
|
},
|
|
156
176
|
},
|
|
177
|
+
/**
|
|
178
|
+
* The palette behind `solid`/`outline`. An allowlist rather than Panda's
|
|
179
|
+
* open `colorPalette` prop: if a shape reads a stop the palette doesn't
|
|
180
|
+
* define, the button renders as nothing at all, with no error anywhere
|
|
181
|
+
* (docs/hints.md). This is where a palette is vetted, and a tone should
|
|
182
|
+
* alias a whole ramp so it has nowhere to fall through. Apps may add
|
|
183
|
+
* their own.
|
|
184
|
+
*/
|
|
185
|
+
tone: {
|
|
186
|
+
brand: { colorPalette: "brand" },
|
|
187
|
+
// The destructive role, which a brand preset can re-point.
|
|
188
|
+
danger: { colorPalette: "danger" },
|
|
189
|
+
// Conventional red that isn't destructive (record buttons), so
|
|
190
|
+
// deliberately not following a brand's error colour.
|
|
191
|
+
red: { colorPalette: "red" },
|
|
192
|
+
},
|
|
157
193
|
},
|
|
158
194
|
defaultVariants: {
|
|
159
195
|
variant: "secondary",
|
|
160
196
|
size: "md",
|
|
197
|
+
// Never absent: a shape with no palette renders nothing.
|
|
198
|
+
tone: "brand",
|
|
161
199
|
},
|
|
162
200
|
});
|
package/src/Button.tsx
CHANGED
|
@@ -4,29 +4,42 @@
|
|
|
4
4
|
* SPDX-License-Identifier: MIT
|
|
5
5
|
*/
|
|
6
6
|
import { forwardRef, ReactNode } from "react";
|
|
7
|
+
import { useIntl } from "react-intl";
|
|
7
8
|
import {
|
|
8
9
|
Button as RACButton,
|
|
9
10
|
ButtonProps as RACButtonProps,
|
|
10
11
|
} from "react-aria-components";
|
|
11
|
-
import { css,
|
|
12
|
+
import { css, cx } from "styled-system/css";
|
|
12
13
|
import { button, ButtonVariantProps } from "styled-system/recipes";
|
|
13
14
|
import { SystemStyleObject } from "styled-system/types";
|
|
15
|
+
import { buttonIcon } from "./button-icon";
|
|
16
|
+
import { uiMessage } from "./messages";
|
|
17
|
+
import { Spinner } from "./Spinner";
|
|
14
18
|
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
19
|
+
// A 1em spinner centred over the hidden label. Out of flow, so the label alone
|
|
20
|
+
// sets the button's size; the recipe's base `position: relative` is what it
|
|
21
|
+
// anchors to. Its own component so useIntl runs only while a button is
|
|
22
|
+
// actually loading — a bare Button must keep working without an IntlProvider
|
|
23
|
+
// (test renders commonly lack one).
|
|
24
|
+
const ButtonSpinner = () => {
|
|
25
|
+
const intl = useIntl();
|
|
26
|
+
return (
|
|
27
|
+
<span
|
|
28
|
+
className={css({
|
|
29
|
+
position: "absolute",
|
|
30
|
+
inset: 0,
|
|
31
|
+
display: "flex",
|
|
32
|
+
alignItems: "center",
|
|
33
|
+
justifyContent: "center",
|
|
34
|
+
})}
|
|
35
|
+
>
|
|
36
|
+
<Spinner
|
|
37
|
+
aria-label={intl.formatMessage(uiMessage("ui.loading"))}
|
|
38
|
+
css={{ width: "1em", height: "1em" }}
|
|
39
|
+
/>
|
|
40
|
+
</span>
|
|
41
|
+
);
|
|
42
|
+
};
|
|
30
43
|
|
|
31
44
|
export interface ButtonProps
|
|
32
45
|
extends Omit<RACButtonProps, "className" | "children">,
|
|
@@ -34,10 +47,16 @@ export interface ButtonProps
|
|
|
34
47
|
/** Per-instance style overrides, merged after the recipe. */
|
|
35
48
|
css?: SystemStyleObject;
|
|
36
49
|
className?: string;
|
|
37
|
-
/** Icon rendered before the label
|
|
38
|
-
|
|
39
|
-
/** Icon rendered after the label
|
|
40
|
-
|
|
50
|
+
/** Icon rendered before the label. */
|
|
51
|
+
startIcon?: ReactNode;
|
|
52
|
+
/** Icon rendered after the label. */
|
|
53
|
+
endIcon?: ReactNode;
|
|
54
|
+
/**
|
|
55
|
+
* Show a spinner in place of the label and disable interaction: the label
|
|
56
|
+
* stays in the layout but invisible, so the button keeps its size, and the
|
|
57
|
+
* dimmed disabled look applies.
|
|
58
|
+
*/
|
|
59
|
+
isLoading?: boolean;
|
|
41
60
|
children?: ReactNode;
|
|
42
61
|
}
|
|
43
62
|
|
|
@@ -52,32 +71,51 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
|
|
52
71
|
{
|
|
53
72
|
variant,
|
|
54
73
|
size,
|
|
74
|
+
tone,
|
|
55
75
|
css: cssProp,
|
|
56
76
|
className,
|
|
57
|
-
|
|
58
|
-
|
|
77
|
+
startIcon,
|
|
78
|
+
endIcon,
|
|
79
|
+
isLoading,
|
|
59
80
|
children,
|
|
60
81
|
...rest
|
|
61
82
|
},
|
|
62
83
|
ref,
|
|
63
84
|
) {
|
|
85
|
+
const label = (
|
|
86
|
+
<>
|
|
87
|
+
{startIcon ? (
|
|
88
|
+
<span className={buttonIcon({ side: "start" })}>{startIcon}</span>
|
|
89
|
+
) : null}
|
|
90
|
+
{children}
|
|
91
|
+
{endIcon ? (
|
|
92
|
+
<span className={buttonIcon({ side: "end" })}>{endIcon}</span>
|
|
93
|
+
) : null}
|
|
94
|
+
</>
|
|
95
|
+
);
|
|
64
96
|
return (
|
|
65
97
|
<RACButton
|
|
66
98
|
ref={ref}
|
|
67
99
|
className={cx(
|
|
68
|
-
button({ variant, size }),
|
|
100
|
+
button({ variant, size, tone }),
|
|
69
101
|
cssProp ? css(cssProp) : undefined,
|
|
70
102
|
className,
|
|
71
103
|
)}
|
|
104
|
+
data-loading={isLoading ? "" : undefined}
|
|
72
105
|
{...rest}
|
|
106
|
+
isDisabled={isLoading || rest.isDisabled}
|
|
73
107
|
>
|
|
74
|
-
{
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
108
|
+
{isLoading ? (
|
|
109
|
+
<>
|
|
110
|
+
<ButtonSpinner />
|
|
111
|
+
{/* Hidden with opacity, not removed: it keeps the button the size
|
|
112
|
+
it is when idle, so a row of buttons doesn't reflow. Still in
|
|
113
|
+
the accessibility tree, so the button keeps its name. */}
|
|
114
|
+
<span className={css({ opacity: 0 })}>{label}</span>
|
|
115
|
+
</>
|
|
116
|
+
) : (
|
|
117
|
+
label
|
|
118
|
+
)}
|
|
81
119
|
</RACButton>
|
|
82
120
|
);
|
|
83
121
|
},
|
package/src/ButtonGroup.tsx
CHANGED
|
@@ -9,8 +9,8 @@ import { SystemStyleObject } from "styled-system/types";
|
|
|
9
9
|
|
|
10
10
|
export interface ButtonGroupProps extends HTMLAttributes<HTMLDivElement> {
|
|
11
11
|
/**
|
|
12
|
-
* Square the inner radii so adjacent buttons form one control
|
|
13
|
-
*
|
|
12
|
+
* Square the inner radii so adjacent buttons form one control. Otherwise
|
|
13
|
+
* buttons get a small gap.
|
|
14
14
|
*/
|
|
15
15
|
isAttached?: boolean;
|
|
16
16
|
/** Per-instance style overrides. */
|
|
@@ -20,36 +20,58 @@ export interface ButtonGroupProps extends HTMLAttributes<HTMLDivElement> {
|
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
22
|
* ButtonGroup — lays out related buttons in a row. Works with any button
|
|
23
|
-
* elements (shared-ui or native).
|
|
23
|
+
* elements (shared-ui or native).
|
|
24
|
+
*
|
|
25
|
+
* Attached, the buttons divide by a hairline and the group keeps whatever
|
|
26
|
+
* outline the variant draws round the outside, so a 2px `secondary` reads as
|
|
27
|
+
* one bordered control with cells inside it rather than three boxes. A
|
|
28
|
+
* variant with no border of its own (`primary`, `solid`, `neutral`) divides
|
|
29
|
+
* by a gap in the same place, the surface behind the group showing through —
|
|
30
|
+
* see the `[data-attached]` rule in base-preset.ts.
|
|
24
31
|
*/
|
|
25
32
|
export const ButtonGroup = forwardRef<HTMLDivElement, ButtonGroupProps>(
|
|
26
33
|
function ButtonGroup({ isAttached, css: cssProp, className, ...rest }, ref) {
|
|
27
34
|
return (
|
|
28
35
|
<div
|
|
29
36
|
ref={ref}
|
|
37
|
+
data-attached={isAttached ? "" : undefined}
|
|
30
38
|
className={cx(
|
|
31
39
|
css({
|
|
32
40
|
display: "inline-flex",
|
|
33
41
|
alignItems: "center",
|
|
34
|
-
// Buttons are position: relative, so
|
|
35
|
-
//
|
|
36
|
-
//
|
|
37
|
-
|
|
42
|
+
// Buttons are position: relative, so a sibling would paint over
|
|
43
|
+
// the focused button's ring. Only while it shows: a permanently
|
|
44
|
+
// raised child would own the seam it shares, so a hover on its
|
|
45
|
+
// neighbour would stop short of it.
|
|
46
|
+
"& > *": { _focusVisible: { zIndex: 1 } },
|
|
38
47
|
}),
|
|
39
48
|
isAttached
|
|
40
49
|
? css({
|
|
41
50
|
gap: 0,
|
|
42
|
-
// :first-child/:last-child, not
|
|
43
|
-
//
|
|
44
|
-
//
|
|
45
|
-
|
|
51
|
+
// :first-child/:last-child, not -of-type: attached groups can
|
|
52
|
+
// mix element types (e.g. select + button).
|
|
53
|
+
//
|
|
54
|
+
// A hairline whatever the variant's border, which stays as the
|
|
55
|
+
// group's outline — matching it made a 2px seam read as a gap
|
|
56
|
+
// (microbit-foundation/ui#22). Both sides of a seam keep a
|
|
57
|
+
// border and overlap by its width so the two paint as one
|
|
58
|
+
// line; dropping one instead would move that button's border
|
|
59
|
+
// box off the cell you see, and `focusRing` is an outline, so
|
|
60
|
+
// the ring would land on the seam rather than outside it.
|
|
61
|
+
//
|
|
62
|
+
// padding-box so a fill doesn't paint under the transparent
|
|
63
|
+
// seam a borderless variant gets from the preset. Here rather
|
|
64
|
+
// than with that rule because `bg` is the `background`
|
|
65
|
+
// shorthand, which resets it from `recipes`.
|
|
66
|
+
"& > *": { backgroundClip: "padding-box" },
|
|
67
|
+
"& > *:not(:last-child)": {
|
|
46
68
|
borderEndRadius: 0,
|
|
69
|
+
borderInlineEndWidth: "1px",
|
|
47
70
|
},
|
|
48
|
-
"& > *:not(:first-child)
|
|
49
|
-
borderRadius: 0,
|
|
50
|
-
},
|
|
51
|
-
"& > *:not(:first-child):last-child": {
|
|
71
|
+
"& > *:not(:first-child)": {
|
|
52
72
|
borderStartRadius: 0,
|
|
73
|
+
borderInlineStartWidth: "1px",
|
|
74
|
+
marginInlineStart: "-1px",
|
|
53
75
|
},
|
|
54
76
|
})
|
|
55
77
|
: css({ gap: 2 }),
|
package/src/Card.recipe.ts
CHANGED
|
@@ -6,8 +6,7 @@
|
|
|
6
6
|
import { defineSlotRecipe } from "@pandacss/dev";
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
|
-
* Card slot recipe
|
|
10
|
-
* the `elevated` (default) and `outline` variants this app uses.
|
|
9
|
+
* Card slot recipe, with `elevated` (default) and `outline` variants.
|
|
11
10
|
*
|
|
12
11
|
* Registered in the base preset (base-preset.ts); `variant` is
|
|
13
12
|
* forwarded as a runtime prop so the variants are generated via `staticCss`.
|
package/src/Card.tsx
CHANGED
|
@@ -16,7 +16,8 @@ export interface CardProps
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
|
-
* Card —
|
|
19
|
+
* Card — a container styled with the `card` config recipe (md size). Place a
|
|
20
|
+
* CardBody inside.
|
|
20
21
|
*/
|
|
21
22
|
export const Card = forwardRef<HTMLDivElement, CardProps>(function Card(
|
|
22
23
|
{ variant, css: cssProp, className, ...rest },
|
package/src/Checkbox.recipe.ts
CHANGED
|
@@ -6,16 +6,15 @@
|
|
|
6
6
|
import { defineSlotRecipe } from "@pandacss/dev";
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
|
-
* Checkbox slot recipe
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* box from the call site.
|
|
9
|
+
* Checkbox slot recipe with an sm/md/lg size scale. The control's
|
|
10
|
+
* `borderColor: inherit` picks up a `borderColor` set on the root, so call
|
|
11
|
+
* sites can tint the box from there.
|
|
13
12
|
*
|
|
14
13
|
* State styling keys off data attributes stamped on the control by the
|
|
15
14
|
* shared-ui Checkbox (react-aria provides the state via render props).
|
|
16
15
|
*
|
|
17
|
-
* Registered in the base preset (base-preset.ts)
|
|
18
|
-
*
|
|
16
|
+
* Registered in the base preset (base-preset.ts), which also has the
|
|
17
|
+
* `staticCss` entry that keeps the runtime-prop size variants generated.
|
|
19
18
|
*/
|
|
20
19
|
export const checkbox = defineSlotRecipe({
|
|
21
20
|
className: "checkbox",
|
|
@@ -27,6 +26,11 @@ export const checkbox = defineSlotRecipe({
|
|
|
27
26
|
verticalAlign: "top",
|
|
28
27
|
cursor: "pointer",
|
|
29
28
|
position: "relative",
|
|
29
|
+
// The accessible outline stop (WCAG 1.4.11), as the input recipe.
|
|
30
|
+
// Stated here rather than on the control — whose `borderColor:
|
|
31
|
+
// inherit` reads it — so a call site can still tint the whole control
|
|
32
|
+
// by setting borderColor on the root.
|
|
33
|
+
borderColor: "gray.400",
|
|
30
34
|
"&[data-disabled]": { cursor: "not-allowed" },
|
|
31
35
|
},
|
|
32
36
|
control: {
|
|
@@ -34,11 +38,8 @@ export const checkbox = defineSlotRecipe({
|
|
|
34
38
|
alignItems: "center",
|
|
35
39
|
justifyContent: "center",
|
|
36
40
|
flexShrink: 0,
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
transitionProperty: "box-shadow",
|
|
40
|
-
transitionDuration: "normal",
|
|
41
|
-
border: "2px solid",
|
|
41
|
+
borderWidth: "2px",
|
|
42
|
+
borderStyle: "solid",
|
|
42
43
|
borderRadius: "sm",
|
|
43
44
|
borderColor: "inherit",
|
|
44
45
|
color: "white",
|
|
@@ -52,13 +53,23 @@ export const checkbox = defineSlotRecipe({
|
|
|
52
53
|
borderColor: "controlCheckedHoverBg",
|
|
53
54
|
},
|
|
54
55
|
},
|
|
56
|
+
// Disabled greys; the selected block restates _hover so the widened
|
|
57
|
+
// native-:hover condition can't re-tint a disabled control.
|
|
58
|
+
"&[data-disabled]": {
|
|
59
|
+
bg: "gray.100",
|
|
60
|
+
borderColor: "gray.100",
|
|
61
|
+
},
|
|
62
|
+
"&[data-selected][data-disabled]": {
|
|
63
|
+
bg: "gray.200",
|
|
64
|
+
borderColor: "gray.200",
|
|
65
|
+
color: "gray.500",
|
|
66
|
+
_hover: { bg: "gray.200", borderColor: "gray.200" },
|
|
67
|
+
},
|
|
55
68
|
"&[data-focus-visible]": {
|
|
56
|
-
|
|
69
|
+
focusRing: "outline",
|
|
57
70
|
},
|
|
58
71
|
},
|
|
59
72
|
icon: {
|
|
60
|
-
width: "0.75rem",
|
|
61
|
-
height: "0.75rem",
|
|
62
73
|
transitionProperty: "transform",
|
|
63
74
|
transitionDuration: "normal",
|
|
64
75
|
},
|
|
@@ -68,4 +79,28 @@ export const checkbox = defineSlotRecipe({
|
|
|
68
79
|
"&[data-disabled]": { opacity: 0.4 },
|
|
69
80
|
},
|
|
70
81
|
},
|
|
82
|
+
variants: {
|
|
83
|
+
// The icon dimensions are the 1.2em-wide check glyph at each size's icon
|
|
84
|
+
// fontSize (3xs/2xs/2xs), resolved to rem.
|
|
85
|
+
size: {
|
|
86
|
+
sm: {
|
|
87
|
+
control: { width: "3", height: "3" },
|
|
88
|
+
icon: { width: "0.54rem", height: "0.54rem" },
|
|
89
|
+
label: { fontSize: "sm" },
|
|
90
|
+
},
|
|
91
|
+
md: {
|
|
92
|
+
control: { width: "4", height: "4" },
|
|
93
|
+
icon: { width: "0.75rem", height: "0.75rem" },
|
|
94
|
+
label: { fontSize: "md" },
|
|
95
|
+
},
|
|
96
|
+
lg: {
|
|
97
|
+
control: { width: "5", height: "5" },
|
|
98
|
+
icon: { width: "0.75rem", height: "0.75rem" },
|
|
99
|
+
label: { fontSize: "lg" },
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
defaultVariants: {
|
|
104
|
+
size: "md",
|
|
105
|
+
},
|
|
71
106
|
});
|