@microbit/ui 0.1.0-alpha.18 → 0.1.0-alpha.20
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 +8 -0
- package/README.md +34 -11
- package/lang/ui.ca.json +36 -0
- package/lang/ui.cy.json +36 -0
- package/lang/ui.de.json +36 -0
- package/lang/ui.en-us.json +36 -0
- package/lang/ui.en.json +28 -0
- package/lang/ui.es-es.json +36 -0
- package/lang/ui.fr.json +36 -0
- package/lang/ui.ga-ie.json +36 -0
- package/lang/ui.it.json +36 -0
- package/lang/ui.ja.json +36 -0
- package/lang/ui.ko.json +36 -0
- package/lang/ui.lol.json +36 -0
- package/lang/ui.nl.json +36 -0
- package/lang/ui.pl.json +36 -0
- package/lang/ui.pt-br.json +36 -0
- package/lang/ui.zh-cn.json +36 -0
- package/lang/ui.zh-tw.json +36 -0
- package/package.json +1 -1
- package/src/Avatar.recipe.ts +3 -1
- package/src/Checkbox.recipe.ts +5 -0
- package/src/Checkbox.tsx +12 -0
- package/src/CheckboxGroup.tsx +6 -1
- package/src/ComboBox.tsx +12 -1
- package/src/Input.recipe.ts +4 -2
- package/src/NativeSelectField.tsx +4 -1
- package/src/NumberField.tsx +13 -0
- package/src/ProgressBar.tsx +1 -1
- package/src/Radio.recipe.ts +4 -0
- package/src/RadioGroup.tsx +6 -1
- package/src/Select.recipe.ts +5 -2
- package/src/Select.tsx +15 -2
- package/src/Skeleton.tsx +5 -3
- package/src/Slider.recipe.ts +2 -1
- package/src/TextField.tsx +5 -0
- package/src/Toast.tsx +17 -2
- package/src/base-preset.ts +49 -18
package/src/Toast.tsx
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* SPDX-License-Identifier: MIT
|
|
5
5
|
*/
|
|
6
|
-
import { ReactNode, useMemo } from "react";
|
|
6
|
+
import { ReactNode, useMemo, useSyncExternalStore } from "react";
|
|
7
7
|
import {
|
|
8
8
|
Button as RACButton,
|
|
9
9
|
UNSTABLE_Toast as RACToast,
|
|
@@ -55,6 +55,10 @@ const statusIcon: Record<ToastStatus, IconType> = {
|
|
|
55
55
|
error: RiErrorWarningFill,
|
|
56
56
|
};
|
|
57
57
|
|
|
58
|
+
// Module scope so useSyncExternalStore doesn't resubscribe every render.
|
|
59
|
+
const subscribeToQueue = (fn: () => void) => toastQueue.subscribe(fn);
|
|
60
|
+
const getVisibleCount = () => toastQueue.visibleToasts.length;
|
|
61
|
+
|
|
58
62
|
/**
|
|
59
63
|
* Mount once near the app root, inside the IntlProvider (the close button's
|
|
60
64
|
* label and the status announcements are react-intl messages).
|
|
@@ -63,8 +67,19 @@ const statusIcon: Record<ToastStatus, IconType> = {
|
|
|
63
67
|
export const ToastProvider = () => {
|
|
64
68
|
const intl = useIntl();
|
|
65
69
|
const slots = toastRecipe();
|
|
70
|
+
// The region's landmark label counts the visible toasts, so it has to
|
|
71
|
+
// track the queue.
|
|
72
|
+
const count = useSyncExternalStore(
|
|
73
|
+
subscribeToQueue,
|
|
74
|
+
getVisibleCount,
|
|
75
|
+
getVisibleCount,
|
|
76
|
+
);
|
|
66
77
|
return (
|
|
67
|
-
<RACToastRegion
|
|
78
|
+
<RACToastRegion
|
|
79
|
+
queue={toastQueue}
|
|
80
|
+
aria-label={intl.formatMessage(uiMessage("ui.toast-region"), { count })}
|
|
81
|
+
className={slots.region}
|
|
82
|
+
>
|
|
68
83
|
{({ toast }) => {
|
|
69
84
|
const status = toast.content.status ?? "info";
|
|
70
85
|
return (
|
package/src/base-preset.ts
CHANGED
|
@@ -45,6 +45,43 @@ import { tooltip } from "./Tooltip.recipe";
|
|
|
45
45
|
import { field } from "./Field.recipe";
|
|
46
46
|
import { toast } from "./Toast.recipe";
|
|
47
47
|
|
|
48
|
+
// The family gray ramp: pure neutrals as the library default. The two
|
|
49
|
+
// halves have different jobs and different override rules:
|
|
50
|
+
//
|
|
51
|
+
// - 10–300 are surface stops (panels, page backdrops, hairlines, subtle
|
|
52
|
+
// fills). Presets may freely override these values — pinning an app's
|
|
53
|
+
// existing surfaces or applying a brand tint — because a few bits of
|
|
54
|
+
// lightness here reads across a whole viewport.
|
|
55
|
+
// - 350 is the decorative/state fill stop (~2.1:1): avatar discs, skeleton
|
|
56
|
+
// pulse, pressed fills. Never text or boundaries.
|
|
57
|
+
// - 400–900 are ink stops (outlines, placeholders, text) with a contrast
|
|
58
|
+
// contract on white: 400 ≥ 3:1, the accessible form-outline stop
|
|
59
|
+
// (WCAG 1.4.11); 500 ≥ 4.5:1, text-safe secondary (placeholders, muted
|
|
60
|
+
// icons). Presets may re-tint these only luminance-matched — the
|
|
61
|
+
// contrast figures are the contract, hue is free.
|
|
62
|
+
//
|
|
63
|
+
// Override values, never names: raw var(--colors-gray-*) references and
|
|
64
|
+
// paired private presets depend on the names, so a rename is a breaking
|
|
65
|
+
// change to both and needs every app and paired preset moved in lockstep
|
|
66
|
+
// (as was done when Chakra's misnamed darker-than-50 stop `25` became
|
|
67
|
+
// `75`). And never override partially in a way that lets a stop fall
|
|
68
|
+
// through to a different grey system.
|
|
69
|
+
const gray = {
|
|
70
|
+
10: { value: "#fcfcfc" },
|
|
71
|
+
50: { value: "#f9f9f9" },
|
|
72
|
+
75: { value: "#f5f5f5" },
|
|
73
|
+
100: { value: "#f1f1f1" },
|
|
74
|
+
200: { value: "#e7e7e7" },
|
|
75
|
+
300: { value: "#d4d4d4" },
|
|
76
|
+
350: { value: "#b4b4b4" }, // ~2.1:1 — decorative fills only
|
|
77
|
+
400: { value: "#949494" }, // 3.05:1 — accessible outline stop
|
|
78
|
+
500: { value: "#767676" }, // 4.54:1 — text-safe secondary
|
|
79
|
+
600: { value: "#575757" },
|
|
80
|
+
700: { value: "#404040" },
|
|
81
|
+
800: { value: "#262626" },
|
|
82
|
+
900: { value: "#1a1a1a" },
|
|
83
|
+
};
|
|
84
|
+
|
|
48
85
|
/**
|
|
49
86
|
* The base preset: the complete, working micro:bit design system. The base
|
|
50
87
|
* token scales (base-tokens.ts), the micro:bit house style
|
|
@@ -92,16 +129,12 @@ export const basePreset = definePreset({
|
|
|
92
129
|
tokens: {
|
|
93
130
|
colors: {
|
|
94
131
|
...colors,
|
|
95
|
-
gray
|
|
96
|
-
...colors.gray,
|
|
97
|
-
// Very light grays the family's designs use below Chakra's 50.
|
|
98
|
-
10: { value: "#fcfcfc" },
|
|
99
|
-
25: { value: "#f5f5f5" },
|
|
100
|
-
},
|
|
132
|
+
gray,
|
|
101
133
|
// OSS default brand ramps (see the brand contract above). `brand`
|
|
102
|
-
// aliases Chakra blue; `brand2` Chakra's
|
|
103
|
-
//
|
|
104
|
-
//
|
|
134
|
+
// aliases Chakra blue; `brand2` stays Chakra's slate gray — a frozen
|
|
135
|
+
// legacy alias, deliberately decoupled from the neutral `gray` above
|
|
136
|
+
// so ml-trainer's OSS look and `statusBarBg`'s default don't move.
|
|
137
|
+
// Removing the slot is a follow-up needing an ml-trainer lockstep.
|
|
105
138
|
brand: colors.blue,
|
|
106
139
|
brand2: colors.gray,
|
|
107
140
|
},
|
|
@@ -146,15 +179,13 @@ export const basePreset = definePreset({
|
|
|
146
179
|
},
|
|
147
180
|
semanticTokens: {
|
|
148
181
|
colors: {
|
|
149
|
-
// Checked/focus states of form controls
|
|
150
|
-
//
|
|
151
|
-
//
|
|
152
|
-
//
|
|
153
|
-
controlCheckedBg: { value: "{colors.
|
|
154
|
-
controlCheckedHoverBg: { value: "{colors.
|
|
155
|
-
focusBorder: { value: "{colors.
|
|
156
|
-
sliderFilledTrack: { value: "{colors.blue.500}" },
|
|
157
|
-
progressFilledTrack: { value: "{colors.blue.500}" },
|
|
182
|
+
// Checked/focus states of form controls: Checkbox/Switch/Radio
|
|
183
|
+
// checked backgrounds and the Input/Select focus border. Semantic
|
|
184
|
+
// so a brand can diverge them from its ramp — e.g. a light
|
|
185
|
+
// brand.500 needing a darker 3:1 focus border.
|
|
186
|
+
controlCheckedBg: { value: "{colors.brand.500}" },
|
|
187
|
+
controlCheckedHoverBg: { value: "{colors.brand.600}" },
|
|
188
|
+
focusBorder: { value: "{colors.brand.500}" },
|
|
158
189
|
// Error/destructive ramp (Chakra red). Destructive button variants,
|
|
159
190
|
// field error states and the error toast; the record* button
|
|
160
191
|
// variants deliberately stay on red.* (recording vocabulary, not
|