@microbit/ui 0.0.0-gray.ramp.90
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 +53 -0
- package/README.md +222 -0
- package/lang/ui.ca.json +22 -0
- package/lang/ui.cy.json +22 -0
- package/lang/ui.de.json +22 -0
- package/lang/ui.en-us.json +22 -0
- package/lang/ui.en.json +22 -0
- package/lang/ui.es-es.json +22 -0
- package/lang/ui.fr.json +22 -0
- package/lang/ui.ga-ie.json +22 -0
- package/lang/ui.it.json +22 -0
- package/lang/ui.ja.json +22 -0
- package/lang/ui.ko.json +22 -0
- package/lang/ui.lol.json +22 -0
- package/lang/ui.nl.json +22 -0
- package/lang/ui.pl.json +22 -0
- package/lang/ui.pt-br.json +22 -0
- package/lang/ui.zh-cn.json +22 -0
- package/lang/ui.zh-tw.json +22 -0
- package/package.json +66 -0
- package/postcss-legacy-safari.cjs +96 -0
- package/reset.css +35 -0
- package/src/Avatar.recipe.ts +168 -0
- package/src/Avatar.tsx +276 -0
- package/src/Button.recipe.ts +192 -0
- package/src/Button.tsx +69 -0
- package/src/ButtonGroup.tsx +70 -0
- package/src/Card.recipe.ts +49 -0
- package/src/Card.tsx +56 -0
- package/src/Checkbox.recipe.ts +111 -0
- package/src/Checkbox.tsx +106 -0
- package/src/CloseButton.tsx +87 -0
- package/src/CloseIcon.tsx +40 -0
- package/src/Code.tsx +20 -0
- package/src/Collapse.tsx +180 -0
- package/src/ComboBox.tsx +192 -0
- package/src/Divider.tsx +85 -0
- package/src/Drawer.recipe.ts +98 -0
- package/src/Drawer.tsx +138 -0
- package/src/Fade.tsx +48 -0
- package/src/GridList.recipe.ts +58 -0
- package/src/GridList.tsx +81 -0
- package/src/Heading.recipe.ts +52 -0
- package/src/Heading.tsx +14 -0
- package/src/Icon.tsx +75 -0
- package/src/IconButton.tsx +44 -0
- package/src/Image.tsx +11 -0
- package/src/Input.recipe.ts +75 -0
- package/src/Input.tsx +44 -0
- package/src/InputGroup.tsx +62 -0
- package/src/Kbd.tsx +26 -0
- package/src/Link.tsx +23 -0
- package/src/LinkBox.tsx +88 -0
- package/src/LinkButton.tsx +80 -0
- package/src/List.tsx +31 -0
- package/src/ListBox.recipe.ts +43 -0
- package/src/ListBox.tsx +88 -0
- package/src/Menu.recipe.ts +124 -0
- package/src/Menu.tsx +291 -0
- package/src/Modal.recipe.ts +163 -0
- package/src/Modal.tsx +377 -0
- package/src/NativeSelect.tsx +94 -0
- package/src/NumberField.recipe.ts +67 -0
- package/src/NumberField.tsx +86 -0
- package/src/PopoverArrow.tsx +67 -0
- package/src/ProgressBar.tsx +61 -0
- package/src/Radio.recipe.ts +112 -0
- package/src/Radio.tsx +89 -0
- package/src/Select.recipe.ts +210 -0
- package/src/Select.tsx +153 -0
- package/src/SharedUIProvider.tsx +55 -0
- package/src/Skeleton.tsx +146 -0
- package/src/Slide.tsx +54 -0
- package/src/Slider.recipe.ts +102 -0
- package/src/Slider.tsx +163 -0
- package/src/Spinner.tsx +74 -0
- package/src/Svg.tsx +23 -0
- package/src/Switch.recipe.ts +107 -0
- package/src/Switch.tsx +62 -0
- package/src/Text.recipe.ts +29 -0
- package/src/Text.tsx +16 -0
- package/src/TextField.recipe.ts +54 -0
- package/src/TextField.tsx +91 -0
- package/src/Toast.recipe.ts +98 -0
- package/src/Toast.tsx +173 -0
- package/src/Tooltip.recipe.ts +37 -0
- package/src/Tooltip.tsx +76 -0
- package/src/UnmountCallback.tsx +18 -0
- package/src/VisuallyHidden.tsx +14 -0
- package/src/base-preset.ts +371 -0
- package/src/base-tokens.ts +1017 -0
- package/src/button-icon.ts +23 -0
- package/src/data-attrs.ts +16 -0
- package/src/dense-preset.ts +108 -0
- package/src/hooks/useBreakpointValue.ts +63 -0
- package/src/hooks/useClipboard.ts +64 -0
- package/src/hooks/useDisclosure.ts +32 -0
- package/src/hooks/useMediaQuery.ts +28 -0
- package/src/hooks/usePrevious.ts +18 -0
- package/src/index.ts +63 -0
- package/src/messages.ts +17 -0
- package/src/system.ts +45 -0
package/src/Menu.tsx
ADDED
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* (c) 2026, Micro:bit Educational Foundation and contributors
|
|
3
|
+
*
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
import { ReactNode, useCallback, useState } from "react";
|
|
7
|
+
import {
|
|
8
|
+
Header as RACHeader,
|
|
9
|
+
Menu as RACMenu,
|
|
10
|
+
MenuItem as RACMenuItem,
|
|
11
|
+
MenuItemProps as RACMenuItemProps,
|
|
12
|
+
MenuSection as RACMenuSection,
|
|
13
|
+
MenuTrigger as RACMenuTrigger,
|
|
14
|
+
Popover,
|
|
15
|
+
PopoverProps,
|
|
16
|
+
Separator,
|
|
17
|
+
} from "react-aria-components";
|
|
18
|
+
import { RiCheckLine } from "react-icons/ri";
|
|
19
|
+
import { css, cx } from "styled-system/css";
|
|
20
|
+
import { menu } from "styled-system/recipes";
|
|
21
|
+
import { SystemStyleObject } from "styled-system/types";
|
|
22
|
+
import { Icon } from "./Icon";
|
|
23
|
+
import { useOverlayCloseRegistrar } from "./SharedUIProvider";
|
|
24
|
+
|
|
25
|
+
export interface MenuTriggerProps {
|
|
26
|
+
/** Called when the menu opens. */
|
|
27
|
+
onOpen?: () => void;
|
|
28
|
+
/** Called when the menu closes. */
|
|
29
|
+
onClose?: () => void;
|
|
30
|
+
/** The trigger element followed by a `MenuList` (react-aria pattern). */
|
|
31
|
+
children: ReactNode;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* MenuTrigger — react-aria-components' <MenuTrigger>. Its first child is the
|
|
36
|
+
* trigger (e.g. a Button); the second is a `MenuList`. RAC returns focus to the
|
|
37
|
+
* trigger when the menu closes.
|
|
38
|
+
*
|
|
39
|
+
* When the app installs an overlay-close registrar (e.g. so the Android back
|
|
40
|
+
* button can close the menu), the trigger runs controlled and registers its
|
|
41
|
+
* close function while open. Otherwise it's an uncontrolled pass-through.
|
|
42
|
+
*/
|
|
43
|
+
export const MenuTrigger = ({
|
|
44
|
+
onOpen,
|
|
45
|
+
onClose,
|
|
46
|
+
children,
|
|
47
|
+
}: MenuTriggerProps) => {
|
|
48
|
+
const registerClose = useOverlayCloseRegistrar();
|
|
49
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
50
|
+
|
|
51
|
+
const handleOpenChange = useCallback(
|
|
52
|
+
(open: boolean) => {
|
|
53
|
+
setIsOpen(open);
|
|
54
|
+
if (open) {
|
|
55
|
+
registerClose?.(() => {
|
|
56
|
+
setIsOpen(false);
|
|
57
|
+
registerClose(null);
|
|
58
|
+
});
|
|
59
|
+
onOpen?.();
|
|
60
|
+
} else {
|
|
61
|
+
registerClose?.(null);
|
|
62
|
+
onClose?.();
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
[registerClose, onOpen, onClose],
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
if (!registerClose) {
|
|
69
|
+
return (
|
|
70
|
+
<RACMenuTrigger
|
|
71
|
+
onOpenChange={(open) => (open ? onOpen?.() : onClose?.())}
|
|
72
|
+
>
|
|
73
|
+
{children}
|
|
74
|
+
</RACMenuTrigger>
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return (
|
|
79
|
+
<RACMenuTrigger isOpen={isOpen} onOpenChange={handleOpenChange}>
|
|
80
|
+
{children}
|
|
81
|
+
</RACMenuTrigger>
|
|
82
|
+
);
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export interface MenuListProps {
|
|
86
|
+
children: ReactNode;
|
|
87
|
+
/** Placement of the dropdown relative to the trigger. */
|
|
88
|
+
placement?: PopoverProps["placement"];
|
|
89
|
+
/** Per-instance style overrides, merged after the recipe. */
|
|
90
|
+
css?: SystemStyleObject;
|
|
91
|
+
className?: string;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* MenuList — the dropdown surface: RAC's <Popover> (positioned card) wrapping a
|
|
96
|
+
* RAC <Menu> (the list). Place `MenuItem`s inside.
|
|
97
|
+
*/
|
|
98
|
+
export const MenuList = ({
|
|
99
|
+
children,
|
|
100
|
+
// Matches Chakra Menu's default "bottom-start".
|
|
101
|
+
placement = "bottom start",
|
|
102
|
+
css: cssProp,
|
|
103
|
+
className,
|
|
104
|
+
}: MenuListProps) => {
|
|
105
|
+
const slots = menu();
|
|
106
|
+
return (
|
|
107
|
+
<Popover
|
|
108
|
+
placement={placement}
|
|
109
|
+
className={cx(
|
|
110
|
+
slots.content,
|
|
111
|
+
cssProp ? css(cssProp) : undefined,
|
|
112
|
+
className,
|
|
113
|
+
)}
|
|
114
|
+
>
|
|
115
|
+
<RACMenu className={slots.list}>{children}</RACMenu>
|
|
116
|
+
</Popover>
|
|
117
|
+
);
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
export interface MenuItemProps
|
|
121
|
+
extends Omit<RACMenuItemProps, "className" | "children"> {
|
|
122
|
+
/** Icon rendered before the label, matching Chakra's MenuItem `icon`. */
|
|
123
|
+
icon?: ReactNode;
|
|
124
|
+
/** Per-instance style overrides, merged after the recipe. */
|
|
125
|
+
css?: SystemStyleObject;
|
|
126
|
+
className?: string;
|
|
127
|
+
children?: ReactNode;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* MenuItem — a RAC <MenuItem> styled with the `menu` recipe's `item` slot.
|
|
132
|
+
* Use `onAction` for the click behaviour (RAC closes the menu automatically).
|
|
133
|
+
*/
|
|
134
|
+
export const MenuItem = ({
|
|
135
|
+
icon,
|
|
136
|
+
css: cssProp,
|
|
137
|
+
className,
|
|
138
|
+
children,
|
|
139
|
+
...rest
|
|
140
|
+
}: MenuItemProps) => {
|
|
141
|
+
const slots = menu();
|
|
142
|
+
return (
|
|
143
|
+
<RACMenuItem
|
|
144
|
+
className={cx(slots.item, cssProp ? css(cssProp) : undefined, className)}
|
|
145
|
+
{...rest}
|
|
146
|
+
>
|
|
147
|
+
{icon ? (
|
|
148
|
+
<>
|
|
149
|
+
<span className={slots.icon}>{icon}</span>
|
|
150
|
+
<span className={slots.label}>{children}</span>
|
|
151
|
+
</>
|
|
152
|
+
) : (
|
|
153
|
+
children
|
|
154
|
+
)}
|
|
155
|
+
</RACMenuItem>
|
|
156
|
+
);
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
interface MenuOptionGroupBaseProps {
|
|
160
|
+
/** Group heading shown above the options (Chakra's `title`). */
|
|
161
|
+
title?: ReactNode;
|
|
162
|
+
/** `MenuItemOption` children. */
|
|
163
|
+
children: ReactNode;
|
|
164
|
+
css?: SystemStyleObject;
|
|
165
|
+
className?: string;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export interface MenuOptionGroupRadioProps extends MenuOptionGroupBaseProps {
|
|
169
|
+
type?: "radio";
|
|
170
|
+
/** The selected `MenuItemOption`'s value. */
|
|
171
|
+
value?: string;
|
|
172
|
+
/** Called with the newly selected option's value. */
|
|
173
|
+
onChange?: (value: string) => void;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export interface MenuOptionGroupCheckboxProps extends MenuOptionGroupBaseProps {
|
|
177
|
+
type: "checkbox";
|
|
178
|
+
/** The checked `MenuItemOption`s' values. */
|
|
179
|
+
value?: string[];
|
|
180
|
+
/** Called with the full set of checked values after a toggle. */
|
|
181
|
+
onChange?: (value: string[]) => void;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export type MenuOptionGroupProps =
|
|
185
|
+
| MenuOptionGroupRadioProps
|
|
186
|
+
| MenuOptionGroupCheckboxProps;
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* MenuOptionGroup — a group of checkable `MenuItemOption`s within a menu,
|
|
190
|
+
* replacing Chakra's `MenuOptionGroup`. `type="radio"` (the default) is
|
|
191
|
+
* single-select and renders `menuitemradio`; `type="checkbox"` is multi-select
|
|
192
|
+
* and renders `menuitemcheckbox`, each option toggling independently.
|
|
193
|
+
*
|
|
194
|
+
* Selection is section-scoped (RAC MenuSection), so a menu can mix action items
|
|
195
|
+
* and option groups. An option's own `onAction` still fires on every press,
|
|
196
|
+
* including the press that deselects it — so a lone toggle can be driven either
|
|
197
|
+
* by the group's `onChange` or by the item's `onAction`.
|
|
198
|
+
*
|
|
199
|
+
* Choosing an option leaves the menu open (a plain `MenuItem` closes it), which
|
|
200
|
+
* is what Chakra's checkbox groups did. Chakra's *radio* groups closed on
|
|
201
|
+
* select, so a ported radio group is a deliberate behaviour change.
|
|
202
|
+
*/
|
|
203
|
+
export const MenuOptionGroup = (props: MenuOptionGroupProps) => {
|
|
204
|
+
const { title, children, css: cssProp, className } = props;
|
|
205
|
+
const slots = menu();
|
|
206
|
+
const selectedKeys =
|
|
207
|
+
props.type === "checkbox"
|
|
208
|
+
? props.value ?? []
|
|
209
|
+
: props.value != null
|
|
210
|
+
? [props.value]
|
|
211
|
+
: [];
|
|
212
|
+
return (
|
|
213
|
+
<RACMenuSection
|
|
214
|
+
className={cx(slots.group, cssProp ? css(cssProp) : undefined, className)}
|
|
215
|
+
selectionMode={props.type === "checkbox" ? "multiple" : "single"}
|
|
216
|
+
selectedKeys={selectedKeys}
|
|
217
|
+
onSelectionChange={(keys) => {
|
|
218
|
+
if (keys === "all") {
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
const values = [...keys].map(String);
|
|
222
|
+
if (props.type === "checkbox") {
|
|
223
|
+
props.onChange?.(values);
|
|
224
|
+
} else if (values.length > 0) {
|
|
225
|
+
// Radio: a press that clears the selection reports nothing, matching
|
|
226
|
+
// Chakra, which had no way to express an empty radio group.
|
|
227
|
+
props.onChange?.(values[0]);
|
|
228
|
+
}
|
|
229
|
+
}}
|
|
230
|
+
>
|
|
231
|
+
{title != null && (
|
|
232
|
+
<RACHeader className={slots.groupTitle}>{title}</RACHeader>
|
|
233
|
+
)}
|
|
234
|
+
{children}
|
|
235
|
+
</RACMenuSection>
|
|
236
|
+
);
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
export interface MenuItemOptionProps
|
|
240
|
+
extends Omit<RACMenuItemProps, "className" | "children" | "id" | "value"> {
|
|
241
|
+
/** This option's value within its `MenuOptionGroup`. */
|
|
242
|
+
value: string;
|
|
243
|
+
css?: SystemStyleObject;
|
|
244
|
+
className?: string;
|
|
245
|
+
children?: ReactNode;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* MenuItemOption — a selectable option inside a `MenuOptionGroup`, with a
|
|
250
|
+
* check indicator on the selected item (Chakra's MenuItemOption).
|
|
251
|
+
*/
|
|
252
|
+
export const MenuItemOption = ({
|
|
253
|
+
value,
|
|
254
|
+
css: cssProp,
|
|
255
|
+
className,
|
|
256
|
+
children,
|
|
257
|
+
...rest
|
|
258
|
+
}: MenuItemOptionProps) => {
|
|
259
|
+
const slots = menu();
|
|
260
|
+
return (
|
|
261
|
+
<RACMenuItem
|
|
262
|
+
id={value}
|
|
263
|
+
className={cx(slots.item, cssProp ? css(cssProp) : undefined, className)}
|
|
264
|
+
{...rest}
|
|
265
|
+
>
|
|
266
|
+
<span className={slots.itemIndicator} aria-hidden>
|
|
267
|
+
<Icon as={RiCheckLine} />
|
|
268
|
+
</span>
|
|
269
|
+
<span className={slots.label}>{children}</span>
|
|
270
|
+
</RACMenuItem>
|
|
271
|
+
);
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
export interface MenuDividerProps {
|
|
275
|
+
css?: SystemStyleObject;
|
|
276
|
+
className?: string;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/** Horizontal rule separating groups of menu items. */
|
|
280
|
+
export const MenuDivider = ({ css: cssProp, className }: MenuDividerProps) => {
|
|
281
|
+
const slots = menu();
|
|
282
|
+
return (
|
|
283
|
+
<Separator
|
|
284
|
+
className={cx(
|
|
285
|
+
slots.divider,
|
|
286
|
+
cssProp ? css(cssProp) : undefined,
|
|
287
|
+
className,
|
|
288
|
+
)}
|
|
289
|
+
/>
|
|
290
|
+
);
|
|
291
|
+
};
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* (c) 2026, Micro:bit Educational Foundation and contributors
|
|
3
|
+
*
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
import { defineSlotRecipe } from "@pandacss/dev";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Dialog slot recipe — Chakra's default Modal parts (light mode), plus this
|
|
10
|
+
* app's overrides: a full-viewport overlay (the iOS WKWebView 100% fix) and a
|
|
11
|
+
* `full` size with safe-area insets and the brand status-bar gradient.
|
|
12
|
+
*
|
|
13
|
+
* A config recipe (rather than an atomic `sva`) so the `size` variant accepts
|
|
14
|
+
* responsive values, e.g. `{ base: "full", md: "4xl" }`. Consumed by the
|
|
15
|
+
* shared-ui Modal, which maps the slots onto react-aria-components'
|
|
16
|
+
* ModalOverlay / Modal / Dialog.
|
|
17
|
+
*
|
|
18
|
+
* Registered in the base preset (base-preset.ts).
|
|
19
|
+
*/
|
|
20
|
+
// Appearance of a normal (non-full) modal box. Restated by every non-full size
|
|
21
|
+
// variant so that, when `size` is responsive (e.g. { base: "full", md: "4xl" }),
|
|
22
|
+
// the larger breakpoint fully overrides the `full` variant rather than leaking
|
|
23
|
+
// its margin:0 / border-radius:0 / full-height / gradient into desktop. Panda
|
|
24
|
+
// applies the base-breakpoint variant value unconditionally, so symmetric
|
|
25
|
+
// property sets across size values are required.
|
|
26
|
+
const dialogBox = {
|
|
27
|
+
my: "16",
|
|
28
|
+
mx: "2",
|
|
29
|
+
borderRadius: "md",
|
|
30
|
+
background: "white",
|
|
31
|
+
minHeight: "auto",
|
|
32
|
+
padding: "0",
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
// The `full` variant also styles these slots; same symmetry requirement.
|
|
36
|
+
const dialogSlots = {
|
|
37
|
+
header: { pl: "6" },
|
|
38
|
+
body: { overflowY: "visible" },
|
|
39
|
+
closeTrigger: { top: "2" },
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export const dialog = defineSlotRecipe({
|
|
43
|
+
className: "dialog",
|
|
44
|
+
slots: [
|
|
45
|
+
"overlay",
|
|
46
|
+
"content",
|
|
47
|
+
"inner",
|
|
48
|
+
"header",
|
|
49
|
+
"body",
|
|
50
|
+
"footer",
|
|
51
|
+
"closeTrigger",
|
|
52
|
+
],
|
|
53
|
+
base: {
|
|
54
|
+
overlay: {
|
|
55
|
+
position: "fixed",
|
|
56
|
+
inset: 0,
|
|
57
|
+
w: "100%",
|
|
58
|
+
h: "100%",
|
|
59
|
+
bg: "blackAlpha.600",
|
|
60
|
+
zIndex: "modal",
|
|
61
|
+
display: "flex",
|
|
62
|
+
justifyContent: "center",
|
|
63
|
+
alignItems: "flex-start",
|
|
64
|
+
overflow: "auto",
|
|
65
|
+
overscrollBehaviorY: "none",
|
|
66
|
+
// Fade the backdrop in/out. RAC toggles data-entering/data-exiting and
|
|
67
|
+
// waits for the transition before unmounting.
|
|
68
|
+
opacity: 1,
|
|
69
|
+
transition: "opacity 0.2s ease-out",
|
|
70
|
+
"&[data-entering]": { opacity: 0 },
|
|
71
|
+
"&[data-exiting]": { opacity: 0 },
|
|
72
|
+
_motionReduce: { transition: "none" },
|
|
73
|
+
},
|
|
74
|
+
content: {
|
|
75
|
+
position: "relative",
|
|
76
|
+
color: "inherit",
|
|
77
|
+
boxShadow: "lg",
|
|
78
|
+
width: "100%",
|
|
79
|
+
display: "flex",
|
|
80
|
+
flexDirection: "column",
|
|
81
|
+
outline: "none",
|
|
82
|
+
// Approximate Chakra's fade + scale enter/exit.
|
|
83
|
+
opacity: 1,
|
|
84
|
+
transform: "scale(1)",
|
|
85
|
+
transition: "opacity 0.2s ease-out, transform 0.2s ease-out",
|
|
86
|
+
"&[data-entering]": { opacity: 0, transform: "scale(0.95)" },
|
|
87
|
+
"&[data-exiting]": { opacity: 0, transform: "scale(0.95)" },
|
|
88
|
+
_motionReduce: { transition: "none" },
|
|
89
|
+
},
|
|
90
|
+
inner: {
|
|
91
|
+
outline: "none",
|
|
92
|
+
display: "flex",
|
|
93
|
+
flexDirection: "column",
|
|
94
|
+
width: "100%",
|
|
95
|
+
flex: "1 1 auto",
|
|
96
|
+
},
|
|
97
|
+
header: {
|
|
98
|
+
px: "6",
|
|
99
|
+
py: "4",
|
|
100
|
+
fontSize: "xl",
|
|
101
|
+
fontWeight: "semibold",
|
|
102
|
+
flexShrink: 0,
|
|
103
|
+
},
|
|
104
|
+
body: { px: "6", py: "2", flex: "1" },
|
|
105
|
+
footer: {
|
|
106
|
+
px: "6",
|
|
107
|
+
py: "4",
|
|
108
|
+
display: "flex",
|
|
109
|
+
alignItems: "center",
|
|
110
|
+
justifyContent: "flex-end",
|
|
111
|
+
// Chakra had no footer gap (call sites wrapped buttons in HStacks);
|
|
112
|
+
// baked in as the house style. Override via css for tighter layouts.
|
|
113
|
+
gap: "5",
|
|
114
|
+
flexShrink: 0,
|
|
115
|
+
},
|
|
116
|
+
closeTrigger: { position: "absolute", top: "2", insetEnd: "3" },
|
|
117
|
+
},
|
|
118
|
+
variants: {
|
|
119
|
+
// Chakra's `isCentered`: vertically centre the dialog in the viewport
|
|
120
|
+
// rather than the default top alignment.
|
|
121
|
+
centered: {
|
|
122
|
+
true: {
|
|
123
|
+
overlay: { alignItems: "center" },
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
size: {
|
|
127
|
+
xs: { ...dialogSlots, content: { ...dialogBox, maxWidth: "xs" } },
|
|
128
|
+
sm: { ...dialogSlots, content: { ...dialogBox, maxWidth: "sm" } },
|
|
129
|
+
md: { ...dialogSlots, content: { ...dialogBox, maxWidth: "md" } },
|
|
130
|
+
lg: { ...dialogSlots, content: { ...dialogBox, maxWidth: "lg" } },
|
|
131
|
+
xl: { ...dialogSlots, content: { ...dialogBox, maxWidth: "xl" } },
|
|
132
|
+
"2xl": { ...dialogSlots, content: { ...dialogBox, maxWidth: "2xl" } },
|
|
133
|
+
"3xl": { ...dialogSlots, content: { ...dialogBox, maxWidth: "3xl" } },
|
|
134
|
+
"4xl": { ...dialogSlots, content: { ...dialogBox, maxWidth: "4xl" } },
|
|
135
|
+
"5xl": { ...dialogSlots, content: { ...dialogBox, maxWidth: "5xl" } },
|
|
136
|
+
"6xl": { ...dialogSlots, content: { ...dialogBox, maxWidth: "6xl" } },
|
|
137
|
+
full: {
|
|
138
|
+
content: {
|
|
139
|
+
maxWidth: "100vw",
|
|
140
|
+
minHeight: "100dvh",
|
|
141
|
+
my: "0",
|
|
142
|
+
mx: "0",
|
|
143
|
+
borderRadius: "0",
|
|
144
|
+
paddingTop: "env(safe-area-inset-top)",
|
|
145
|
+
paddingBottom: "env(safe-area-inset-bottom)",
|
|
146
|
+
paddingLeft: "env(safe-area-inset-left)",
|
|
147
|
+
paddingRight: "env(safe-area-inset-right)",
|
|
148
|
+
// brand colour in the status-bar area, white below (matches ActionBar)
|
|
149
|
+
background:
|
|
150
|
+
"linear-gradient(to bottom, token(colors.statusBarBg) env(safe-area-inset-top), white env(safe-area-inset-top))",
|
|
151
|
+
},
|
|
152
|
+
header: {
|
|
153
|
+
pl: "calc(var(--window-controls-left, 0px) + token(spacing.6))",
|
|
154
|
+
},
|
|
155
|
+
body: { flex: "1", overflowY: "auto" },
|
|
156
|
+
closeTrigger: {
|
|
157
|
+
top: "calc(env(safe-area-inset-top) + token(spacing.2))",
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
},
|
|
162
|
+
defaultVariants: { size: "md" },
|
|
163
|
+
});
|