react-native-terra-ui 0.1.0 → 0.2.0
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/CHANGELOG.md +90 -0
- package/lib/module/components/avatar/Avatar.js +118 -35
- package/lib/module/components/avatar/Avatar.js.map +1 -1
- package/lib/module/components/avatar/index.js.map +1 -1
- package/lib/module/components/box/Box.js +15 -2
- package/lib/module/components/box/Box.js.map +1 -1
- package/lib/module/components/box/index.js.map +1 -1
- package/lib/module/components/box/utils.js +5 -2
- package/lib/module/components/box/utils.js.map +1 -1
- package/lib/module/components/button/Button.js +7 -14
- package/lib/module/components/button/Button.js.map +1 -1
- package/lib/module/components/button/index.js.map +1 -1
- package/lib/module/components/chip/Chip.js +372 -0
- package/lib/module/components/chip/Chip.js.map +1 -0
- package/lib/module/components/chip/index.js +4 -0
- package/lib/module/components/chip/index.js.map +1 -0
- package/lib/module/components/chip/types.js +2 -0
- package/lib/module/components/chip/types.js.map +1 -0
- package/lib/module/components/chip/utils.js +36 -0
- package/lib/module/components/chip/utils.js.map +1 -0
- package/lib/module/components/empty-state/EmptyState.js +200 -0
- package/lib/module/components/empty-state/EmptyState.js.map +1 -0
- package/lib/module/components/empty-state/index.js +4 -0
- package/lib/module/components/empty-state/index.js.map +1 -0
- package/lib/module/components/empty-state/types.js +2 -0
- package/lib/module/components/empty-state/types.js.map +1 -0
- package/lib/module/components/header/Header.js.map +1 -1
- package/lib/module/components/header/header-buttons.js +31 -36
- package/lib/module/components/header/header-buttons.js.map +1 -1
- package/lib/module/components/header/index.js +1 -1
- package/lib/module/components/header/index.js.map +1 -1
- package/lib/module/components/header/parts/HeaderGradientOverlay.js +38 -0
- package/lib/module/components/header/parts/HeaderGradientOverlay.js.map +1 -0
- package/lib/module/components/header/variants/LargeTitleHeader/index.js +71 -101
- package/lib/module/components/header/variants/LargeTitleHeader/index.js.map +1 -1
- package/lib/module/components/header/variants/TitleHeader/index.js +34 -65
- package/lib/module/components/header/variants/TitleHeader/index.js.map +1 -1
- package/lib/module/components/icon/Icon.js +21 -9
- package/lib/module/components/icon/Icon.js.map +1 -1
- package/lib/module/components/icon/index.js.map +1 -1
- package/lib/module/components/image/Image.js +54 -0
- package/lib/module/components/image/Image.js.map +1 -0
- package/lib/module/components/image/index.js +4 -0
- package/lib/module/components/image/index.js.map +1 -0
- package/lib/module/components/image/types.js +4 -0
- package/lib/module/components/image/types.js.map +1 -0
- package/lib/module/components/index.js +4 -0
- package/lib/module/components/index.js.map +1 -1
- package/lib/module/components/page-indicator/PageIndicator.js.map +1 -1
- package/lib/module/components/page-indicator/PageIndicatorRoot.js.map +1 -1
- package/lib/module/components/page-indicator/hooks/use-page-indicator-colors.js +1 -1
- package/lib/module/components/page-indicator/hooks/use-page-indicator-colors.js.map +1 -1
- package/lib/module/components/page-indicator/hooks/use-page-indicator-core.js.map +1 -1
- package/lib/module/components/page-indicator/hooks/use-page-indicator-progress.js.map +1 -1
- package/lib/module/components/page-indicator/index.js.map +1 -1
- package/lib/module/components/page-indicator/variants/DotIndicator/InactiveDot.js.map +1 -1
- package/lib/module/components/page-indicator/variants/DotIndicator/LoadingOrbit.js.map +1 -1
- package/lib/module/components/page-indicator/variants/DotIndicator/TravelingPill.js.map +1 -1
- package/lib/module/components/page-indicator/variants/DotIndicator/index.js.map +1 -1
- package/lib/module/components/page-indicator/variants/PillIndicator/PillIndicatorDot.js.map +1 -1
- package/lib/module/components/page-indicator/variants/PillIndicator/index.js.map +1 -1
- package/lib/module/components/portal/Portal.js +42 -0
- package/lib/module/components/portal/Portal.js.map +1 -0
- package/lib/module/components/portal/PortalContext.js +1 -16
- package/lib/module/components/portal/PortalContext.js.map +1 -1
- package/lib/module/components/portal/PortalHost.js +16 -14
- package/lib/module/components/portal/PortalHost.js.map +1 -1
- package/lib/module/components/portal/PortalProvider.js +51 -19
- package/lib/module/components/portal/PortalProvider.js.map +1 -1
- package/lib/module/components/portal/index.js +1 -1
- package/lib/module/components/portal/index.js.map +1 -1
- package/lib/module/components/screen/Screen.js +26 -29
- package/lib/module/components/screen/Screen.js.map +1 -1
- package/lib/module/components/screen/ScreenContext.js +7 -5
- package/lib/module/components/screen/ScreenContext.js.map +1 -1
- package/lib/module/components/screen/ScreenFlashList.js +144 -0
- package/lib/module/components/screen/ScreenFlashList.js.map +1 -0
- package/lib/module/components/screen/ScreenFlatList.js +88 -27
- package/lib/module/components/screen/ScreenFlatList.js.map +1 -1
- package/lib/module/components/screen/ScreenScrollView.js +101 -34
- package/lib/module/components/screen/ScreenScrollView.js.map +1 -1
- package/lib/module/components/screen/index.js +1 -0
- package/lib/module/components/screen/index.js.map +1 -1
- package/lib/module/components/screen/parts/BottomSafeArea.js +62 -0
- package/lib/module/components/screen/parts/BottomSafeArea.js.map +1 -0
- package/lib/module/components/screen/parts/ScreenPlaceholder.js +50 -0
- package/lib/module/components/screen/parts/ScreenPlaceholder.js.map +1 -0
- package/lib/module/components/screen/types.js +4 -0
- package/lib/module/components/screen/types.js.map +1 -0
- package/lib/module/components/screen/utils.js +12 -9
- package/lib/module/components/screen/utils.js.map +1 -1
- package/lib/module/components/slot/Slot.js.map +1 -1
- package/lib/module/components/slot/index.js.map +1 -1
- package/lib/module/components/spinner/Spinner.js +1 -1
- package/lib/module/components/spinner/Spinner.js.map +1 -1
- package/lib/module/components/spinner/index.js.map +1 -1
- package/lib/module/components/surface/Surface.js +16 -9
- package/lib/module/components/surface/Surface.js.map +1 -1
- package/lib/module/components/surface/index.js.map +1 -1
- package/lib/module/components/text/Text.js +25 -7
- package/lib/module/components/text/Text.js.map +1 -1
- package/lib/module/components/text/index.js.map +1 -1
- package/lib/module/components/toast/Toast.js +8 -8
- package/lib/module/components/toast/Toast.js.map +1 -1
- package/lib/module/components/toast/ToastProvider.js.map +1 -1
- package/lib/module/components/toast/index.js.map +1 -1
- package/lib/module/components/toolbar/Toolbar.js +268 -0
- package/lib/module/components/toolbar/Toolbar.js.map +1 -0
- package/lib/module/components/toolbar/index.js +4 -0
- package/lib/module/components/toolbar/index.js.map +1 -0
- package/lib/module/context/ThemeProvider.js +5 -2
- package/lib/module/context/ThemeProvider.js.map +1 -1
- package/lib/module/context/index.js.map +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/{components/icon/utils.js → theme/defaults/icons.js} +1 -1
- package/lib/module/theme/defaults/icons.js.map +1 -0
- package/lib/module/theme/defaults/image.js +30 -0
- package/lib/module/theme/defaults/image.js.map +1 -0
- package/lib/module/theme/index.js +6 -6
- package/lib/module/theme/index.js.map +1 -1
- package/lib/module/theme/{configure.js → registry.js} +23 -30
- package/lib/module/theme/registry.js.map +1 -0
- package/lib/module/theme/runtime.js +1 -1
- package/lib/module/theme/runtime.js.map +1 -1
- package/lib/module/theme/theme.js +92 -11
- package/lib/module/theme/theme.js.map +1 -1
- package/lib/module/theme/tokens/dark.js +59 -68
- package/lib/module/theme/tokens/dark.js.map +1 -1
- package/lib/module/theme/tokens/light.js +49 -67
- package/lib/module/theme/tokens/light.js.map +1 -1
- package/lib/module/theme/tokens/primitives.js +241 -20
- package/lib/module/theme/tokens/primitives.js.map +1 -1
- package/lib/module/utils/accent-utils.js +4 -4
- package/lib/module/utils/accent-utils.js.map +1 -1
- package/lib/typescript/src/components/avatar/Avatar.d.ts.map +1 -1
- package/lib/typescript/src/components/avatar/types.d.ts +5 -5
- package/lib/typescript/src/components/avatar/types.d.ts.map +1 -1
- package/lib/typescript/src/components/box/Box.d.ts.map +1 -1
- package/lib/typescript/src/components/box/utils.d.ts +6 -1
- package/lib/typescript/src/components/box/utils.d.ts.map +1 -1
- package/lib/typescript/src/components/button/Button.d.ts +2 -2
- package/lib/typescript/src/components/button/Button.d.ts.map +1 -1
- package/lib/typescript/src/components/chip/Chip.d.ts +24 -0
- package/lib/typescript/src/components/chip/Chip.d.ts.map +1 -0
- package/lib/typescript/src/components/chip/index.d.ts +4 -0
- package/lib/typescript/src/components/chip/index.d.ts.map +1 -0
- package/lib/typescript/src/components/chip/types.d.ts +8 -0
- package/lib/typescript/src/components/chip/types.d.ts.map +1 -0
- package/lib/typescript/src/components/chip/utils.d.ts +8 -0
- package/lib/typescript/src/components/chip/utils.d.ts.map +1 -0
- package/lib/typescript/src/components/empty-state/EmptyState.d.ts +45 -0
- package/lib/typescript/src/components/empty-state/EmptyState.d.ts.map +1 -0
- package/lib/typescript/src/components/empty-state/index.d.ts +4 -0
- package/lib/typescript/src/components/empty-state/index.d.ts.map +1 -0
- package/lib/typescript/src/components/empty-state/types.d.ts +6 -0
- package/lib/typescript/src/components/empty-state/types.d.ts.map +1 -0
- package/lib/typescript/src/components/header/header-buttons.d.ts +27 -0
- package/lib/typescript/src/components/header/header-buttons.d.ts.map +1 -1
- package/lib/typescript/src/components/header/index.d.ts +1 -1
- package/lib/typescript/src/components/header/index.d.ts.map +1 -1
- package/lib/typescript/src/components/header/parts/HeaderGradientOverlay.d.ts +7 -0
- package/lib/typescript/src/components/header/parts/HeaderGradientOverlay.d.ts.map +1 -0
- package/lib/typescript/src/components/header/variants/LargeTitleHeader/index.d.ts +1 -1
- package/lib/typescript/src/components/header/variants/LargeTitleHeader/index.d.ts.map +1 -1
- package/lib/typescript/src/components/header/variants/TitleHeader/index.d.ts +19 -8
- package/lib/typescript/src/components/header/variants/TitleHeader/index.d.ts.map +1 -1
- package/lib/typescript/src/components/icon/Icon.d.ts +1 -1
- package/lib/typescript/src/components/icon/Icon.d.ts.map +1 -1
- package/lib/typescript/src/components/image/Image.d.ts +16 -0
- package/lib/typescript/src/components/image/Image.d.ts.map +1 -0
- package/lib/typescript/src/components/image/index.d.ts +3 -0
- package/lib/typescript/src/components/image/index.d.ts.map +1 -0
- package/lib/typescript/src/components/image/types.d.ts +7 -0
- package/lib/typescript/src/components/image/types.d.ts.map +1 -0
- package/lib/typescript/src/components/index.d.ts +4 -0
- package/lib/typescript/src/components/index.d.ts.map +1 -1
- package/lib/typescript/src/components/page-indicator/PageIndicator.d.ts +1 -1
- package/lib/typescript/src/components/page-indicator/hooks/use-page-indicator-colors.d.ts +1 -1
- package/lib/typescript/src/components/page-indicator/variants/DotIndicator/index.d.ts +1 -1
- package/lib/typescript/src/components/page-indicator/variants/PillIndicator/index.d.ts +1 -1
- package/lib/typescript/src/components/portal/Portal.d.ts +26 -0
- package/lib/typescript/src/components/portal/Portal.d.ts.map +1 -0
- package/lib/typescript/src/components/portal/PortalContext.d.ts +11 -14
- package/lib/typescript/src/components/portal/PortalContext.d.ts.map +1 -1
- package/lib/typescript/src/components/portal/PortalHost.d.ts +9 -4
- package/lib/typescript/src/components/portal/PortalHost.d.ts.map +1 -1
- package/lib/typescript/src/components/portal/PortalProvider.d.ts +3 -3
- package/lib/typescript/src/components/portal/PortalProvider.d.ts.map +1 -1
- package/lib/typescript/src/components/portal/index.d.ts +4 -2
- package/lib/typescript/src/components/portal/index.d.ts.map +1 -1
- package/lib/typescript/src/components/screen/Screen.d.ts +19 -14
- package/lib/typescript/src/components/screen/Screen.d.ts.map +1 -1
- package/lib/typescript/src/components/screen/ScreenContext.d.ts +12 -9
- package/lib/typescript/src/components/screen/ScreenContext.d.ts.map +1 -1
- package/lib/typescript/src/components/screen/ScreenFlashList.d.ts +43 -0
- package/lib/typescript/src/components/screen/ScreenFlashList.d.ts.map +1 -0
- package/lib/typescript/src/components/screen/ScreenFlatList.d.ts +17 -7
- package/lib/typescript/src/components/screen/ScreenFlatList.d.ts.map +1 -1
- package/lib/typescript/src/components/screen/ScreenScrollView.d.ts +51 -16
- package/lib/typescript/src/components/screen/ScreenScrollView.d.ts.map +1 -1
- package/lib/typescript/src/components/screen/index.d.ts +3 -0
- package/lib/typescript/src/components/screen/index.d.ts.map +1 -1
- package/lib/typescript/src/components/screen/parts/BottomSafeArea.d.ts +30 -0
- package/lib/typescript/src/components/screen/parts/BottomSafeArea.d.ts.map +1 -0
- package/lib/typescript/src/components/screen/parts/ScreenPlaceholder.d.ts +22 -0
- package/lib/typescript/src/components/screen/parts/ScreenPlaceholder.d.ts.map +1 -0
- package/lib/typescript/src/components/screen/types.d.ts +42 -0
- package/lib/typescript/src/components/screen/types.d.ts.map +1 -0
- package/lib/typescript/src/components/screen/utils.d.ts +9 -9
- package/lib/typescript/src/components/screen/utils.d.ts.map +1 -1
- package/lib/typescript/src/components/spinner/Spinner.d.ts +1 -1
- package/lib/typescript/src/components/surface/Surface.d.ts +8 -1
- package/lib/typescript/src/components/surface/Surface.d.ts.map +1 -1
- package/lib/typescript/src/components/text/Text.d.ts +1 -1
- package/lib/typescript/src/components/text/Text.d.ts.map +1 -1
- package/lib/typescript/src/components/toast/Toast.d.ts +1 -1
- package/lib/typescript/src/components/toolbar/Toolbar.d.ts +115 -0
- package/lib/typescript/src/components/toolbar/Toolbar.d.ts.map +1 -0
- package/lib/typescript/src/components/toolbar/index.d.ts +3 -0
- package/lib/typescript/src/components/toolbar/index.d.ts.map +1 -0
- package/lib/typescript/src/context/ThemeProvider.d.ts.map +1 -1
- package/lib/typescript/src/{components/icon/utils.d.ts → theme/defaults/icons.d.ts} +2 -2
- package/lib/typescript/src/theme/defaults/icons.d.ts.map +1 -0
- package/lib/typescript/src/theme/defaults/image.d.ts +4 -0
- package/lib/typescript/src/theme/defaults/image.d.ts.map +1 -0
- package/lib/typescript/src/theme/index.d.ts +7 -7
- package/lib/typescript/src/theme/index.d.ts.map +1 -1
- package/lib/typescript/src/theme/{configure.d.ts → registry.d.ts} +1 -1
- package/lib/typescript/src/theme/registry.d.ts.map +1 -0
- package/lib/typescript/src/theme/theme.d.ts +21 -1
- package/lib/typescript/src/theme/theme.d.ts.map +1 -1
- package/lib/typescript/src/theme/tokens/dark.d.ts +41 -64
- package/lib/typescript/src/theme/tokens/dark.d.ts.map +1 -1
- package/lib/typescript/src/theme/tokens/light.d.ts +44 -65
- package/lib/typescript/src/theme/tokens/light.d.ts.map +1 -1
- package/lib/typescript/src/theme/tokens/primitives.d.ts +228 -17
- package/lib/typescript/src/theme/tokens/primitives.d.ts.map +1 -1
- package/lib/typescript/src/theme/types.d.ts +66 -36
- package/lib/typescript/src/theme/types.d.ts.map +1 -1
- package/lib/typescript/src/utils/accent-utils.d.ts +1 -1
- package/lib/typescript/src/utils/resolve-theme-color.d.ts +1 -1
- package/lib/typescript/src/utils/typography.d.ts +1 -1
- package/package.json +28 -12
- package/skills/terra-ui/SKILL.md +193 -0
- package/skills/terra-ui/references/recipes.md +198 -0
- package/skills/terra-ui/references/troubleshooting.md +112 -0
- package/src/components/avatar/Avatar.tsx +128 -38
- package/src/components/avatar/types.ts +5 -5
- package/src/components/box/Box.tsx +13 -1
- package/src/components/box/utils.ts +10 -0
- package/src/components/button/Button.tsx +7 -15
- package/src/components/chip/Chip.tsx +444 -0
- package/src/components/chip/index.ts +3 -0
- package/src/components/chip/types.ts +15 -0
- package/src/components/chip/utils.ts +34 -0
- package/src/components/empty-state/EmptyState.tsx +209 -0
- package/src/components/empty-state/index.ts +3 -0
- package/src/components/empty-state/types.ts +5 -0
- package/src/components/header/header-buttons.tsx +33 -45
- package/src/components/header/index.ts +2 -0
- package/src/components/header/parts/HeaderGradientOverlay.tsx +29 -0
- package/src/components/header/variants/LargeTitleHeader/index.tsx +68 -102
- package/src/components/header/variants/TitleHeader/index.tsx +47 -56
- package/src/components/icon/Icon.tsx +20 -15
- package/src/components/image/Image.tsx +48 -0
- package/src/components/image/index.ts +2 -0
- package/src/components/image/types.ts +7 -0
- package/src/components/index.ts +4 -0
- package/src/components/portal/Portal.tsx +55 -0
- package/src/components/portal/PortalContext.tsx +14 -22
- package/src/components/portal/PortalHost.tsx +17 -10
- package/src/components/portal/PortalProvider.tsx +58 -21
- package/src/components/portal/index.ts +4 -2
- package/src/components/screen/Screen.tsx +45 -59
- package/src/components/screen/ScreenContext.tsx +19 -12
- package/src/components/screen/ScreenFlashList.tsx +196 -0
- package/src/components/screen/ScreenFlatList.tsx +100 -39
- package/src/components/screen/ScreenScrollView.tsx +114 -53
- package/src/components/screen/index.ts +3 -0
- package/src/components/screen/parts/BottomSafeArea.tsx +60 -0
- package/src/components/screen/parts/ScreenPlaceholder.tsx +55 -0
- package/src/components/screen/types.ts +43 -0
- package/src/components/screen/utils.ts +29 -15
- package/src/components/surface/Surface.tsx +17 -20
- package/src/components/text/Text.tsx +23 -5
- package/src/components/toast/Toast.tsx +8 -8
- package/src/components/toolbar/Toolbar.tsx +353 -0
- package/src/components/toolbar/index.ts +6 -0
- package/src/context/ThemeProvider.tsx +5 -4
- package/src/{components/icon/utils.tsx → theme/defaults/icons.tsx} +1 -1
- package/src/theme/defaults/image.tsx +34 -0
- package/src/theme/index.ts +1 -1
- package/src/theme/{configure.ts → registry.ts} +19 -34
- package/src/theme/runtime.ts +1 -1
- package/src/theme/theme.ts +127 -13
- package/src/theme/tokens/dark.ts +60 -69
- package/src/theme/tokens/light.ts +50 -68
- package/src/theme/tokens/primitives.ts +248 -20
- package/src/theme/types.ts +67 -35
- package/src/utils/accent-utils.ts +4 -4
- package/lib/module/components/icon/utils.js.map +0 -1
- package/lib/module/theme/configure.js.map +0 -1
- package/lib/typescript/src/components/icon/utils.d.ts.map +0 -1
- package/lib/typescript/src/theme/configure.d.ts.map +0 -1
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Children,
|
|
3
|
+
type ComponentRef,
|
|
4
|
+
createContext,
|
|
5
|
+
forwardRef,
|
|
6
|
+
type ReactNode,
|
|
7
|
+
useContext,
|
|
8
|
+
useMemo,
|
|
9
|
+
} from 'react';
|
|
10
|
+
import {
|
|
11
|
+
Pressable,
|
|
12
|
+
type PressableProps,
|
|
13
|
+
View,
|
|
14
|
+
type ViewProps,
|
|
15
|
+
} from 'react-native';
|
|
16
|
+
|
|
17
|
+
import { StyleSheet } from 'react-native-unistyles';
|
|
18
|
+
|
|
19
|
+
import type { ColorToken, SpacingKey, TerraIconName } from '#theme/types';
|
|
20
|
+
import { resolveThemeColor } from '#utils/resolve-theme-color';
|
|
21
|
+
|
|
22
|
+
import { Icon } from '../icon';
|
|
23
|
+
import { Text } from '../text';
|
|
24
|
+
|
|
25
|
+
// ─── Context (lets Toolbar.Button inherit the group's paint) ──────────────────
|
|
26
|
+
//
|
|
27
|
+
// Both colors are unresolved **tokens**, never resolved values — the button and
|
|
28
|
+
// its `Text`/`Icon` resolve them against the live theme, so each leaf owns its
|
|
29
|
+
// own subscription and keeps repainting on a scheme switch however the group
|
|
30
|
+
// above it was rendered. Same contract as `ChipContext`.
|
|
31
|
+
|
|
32
|
+
interface ToolbarGroupContextValue {
|
|
33
|
+
/** Fill token for a button's own capsule. A group sets `'transparent'`. */
|
|
34
|
+
background: ColorToken;
|
|
35
|
+
/** Foreground token inherited by each button's icon and label. */
|
|
36
|
+
tint: ColorToken;
|
|
37
|
+
/**
|
|
38
|
+
* True inside a {@link ToolbarGroup}. Adjacent buttons share a cell edge, so
|
|
39
|
+
* a horizontal `hitSlop` would reach into a neighbour's bounds and steal
|
|
40
|
+
* taps from it — grouped buttons expand vertically only.
|
|
41
|
+
*/
|
|
42
|
+
isGrouped: boolean;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Non-null default, so a bare `Toolbar.Button` still paints its own circle. */
|
|
46
|
+
const ToolbarGroupContext = createContext<ToolbarGroupContextValue>({
|
|
47
|
+
background: 'surface.default',
|
|
48
|
+
tint: 'text.default',
|
|
49
|
+
isGrouped: false,
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
/** Square cell of an icon-only item, in dp. */
|
|
53
|
+
const ITEM_SIZE = 40;
|
|
54
|
+
|
|
55
|
+
/** Touch expansion outside the cell, in dp. */
|
|
56
|
+
const HIT_SLOP = 8;
|
|
57
|
+
|
|
58
|
+
/** Typographic role of a text item's label. */
|
|
59
|
+
const LABEL_VARIANT = 'label-md';
|
|
60
|
+
|
|
61
|
+
// ─── Button ───────────────────────────────────────────────────────────────────
|
|
62
|
+
|
|
63
|
+
interface ToolbarButtonBaseProps
|
|
64
|
+
extends Omit<PressableProps, 'children' | 'style'> {
|
|
65
|
+
/** Icon to render, before the label when both are given. */
|
|
66
|
+
icon?: TerraIconName;
|
|
67
|
+
/** Icon size in density-independent pixels. Defaults to `20`. */
|
|
68
|
+
iconSize?: number;
|
|
69
|
+
/**
|
|
70
|
+
* Foreground token for the icon and label. Defaults to the group's `tint`,
|
|
71
|
+
* or `text.default` outside a group.
|
|
72
|
+
*/
|
|
73
|
+
tint?: ColorToken;
|
|
74
|
+
/**
|
|
75
|
+
* Fill. Defaults to `surface.default` standalone, and to `transparent`
|
|
76
|
+
* inside a {@link ToolbarGroup}, which paints the shared capsule instead.
|
|
77
|
+
* Set it explicitly to opt back out of the group's fill.
|
|
78
|
+
*/
|
|
79
|
+
background?: ColorToken;
|
|
80
|
+
/**
|
|
81
|
+
* Label content. A `string` or `number` renders as themed text in the
|
|
82
|
+
* button's `tint`; anything else renders as given. The cell grows
|
|
83
|
+
* horizontally to fit, so a text item is a pill rather than a circle.
|
|
84
|
+
*/
|
|
85
|
+
children?: ReactNode;
|
|
86
|
+
/**
|
|
87
|
+
* Merged over the base style, so a single item can be repositioned (e.g.
|
|
88
|
+
* pinned over a hero image) without a wrapper view. Supports `Pressable`'s
|
|
89
|
+
* function form for press-state styling.
|
|
90
|
+
*/
|
|
91
|
+
style?: PressableProps['style'];
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* An icon-only button has no visible text, so it must carry an
|
|
96
|
+
* `accessibilityLabel`; one with children may rely on them instead.
|
|
97
|
+
*/
|
|
98
|
+
export type ToolbarButtonProps = ToolbarButtonBaseProps &
|
|
99
|
+
(
|
|
100
|
+
| { children: ReactNode; accessibilityLabel?: string }
|
|
101
|
+
| { icon: TerraIconName; accessibilityLabel: string }
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* An action inside a toolbar — a circular icon button, a text pill, or both.
|
|
106
|
+
* Paints its own capsule standalone, and goes transparent inside a
|
|
107
|
+
* {@link ToolbarGroup} so the group's shared capsule shows through.
|
|
108
|
+
*
|
|
109
|
+
* @example
|
|
110
|
+
* ```tsx
|
|
111
|
+
* <Toolbar.Button icon="share" accessibilityLabel="Share" onPress={share} />
|
|
112
|
+
* <Toolbar.Button onPress={save}>Save</Toolbar.Button>
|
|
113
|
+
* ```
|
|
114
|
+
*/
|
|
115
|
+
export function ToolbarButton({
|
|
116
|
+
icon,
|
|
117
|
+
iconSize = 20,
|
|
118
|
+
tint,
|
|
119
|
+
background,
|
|
120
|
+
children,
|
|
121
|
+
accessibilityLabel,
|
|
122
|
+
onPress,
|
|
123
|
+
hitSlop = HIT_SLOP,
|
|
124
|
+
style,
|
|
125
|
+
...rest
|
|
126
|
+
}: ToolbarButtonProps) {
|
|
127
|
+
const {
|
|
128
|
+
background: groupBackground,
|
|
129
|
+
tint: groupTint,
|
|
130
|
+
isGrouped,
|
|
131
|
+
} = useContext(ToolbarGroupContext);
|
|
132
|
+
|
|
133
|
+
// Tokens, not resolved values: `Icon` and `Text` each resolve against the
|
|
134
|
+
// live theme, so they keep their own subscriptions.
|
|
135
|
+
const fg = tint ?? groupTint;
|
|
136
|
+
const bg = background ?? groupBackground;
|
|
137
|
+
const shape = Children.count(children) > 0 ? 'label' : 'icon';
|
|
138
|
+
|
|
139
|
+
styles.useVariants({ shape, grouped: isGrouped });
|
|
140
|
+
|
|
141
|
+
// Read during render rather than inside the `style` callback below. The
|
|
142
|
+
// callback can run after sibling buttons have rendered, and `useVariants` is
|
|
143
|
+
// set per render pass, so resolving in there risks picking up whichever
|
|
144
|
+
// sibling called it last. Reading here pins this instance's variants. (The
|
|
145
|
+
// Jest mock resolves both spellings identically, so the difference only shows
|
|
146
|
+
// on a device — hence the comment rather than a test.)
|
|
147
|
+
const cell = styles.button;
|
|
148
|
+
|
|
149
|
+
return (
|
|
150
|
+
<Pressable
|
|
151
|
+
accessibilityLabel={accessibilityLabel}
|
|
152
|
+
accessibilityRole="button"
|
|
153
|
+
hitSlop={hitSlop}
|
|
154
|
+
onPress={onPress}
|
|
155
|
+
{...rest}
|
|
156
|
+
style={(state) => [
|
|
157
|
+
cell,
|
|
158
|
+
styles.fill(bg, state.pressed),
|
|
159
|
+
typeof style === 'function' ? style(state) : style,
|
|
160
|
+
]}
|
|
161
|
+
>
|
|
162
|
+
{icon !== undefined && <Icon size={iconSize} color={fg} name={icon} />}
|
|
163
|
+
{renderLabel(children, fg)}
|
|
164
|
+
</Pressable>
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/** Auto-wraps text children in the button's tint, as `Chip` does its label. */
|
|
169
|
+
function renderLabel(children: ReactNode, tint: ColorToken): ReactNode {
|
|
170
|
+
return Children.map(children, (child) => {
|
|
171
|
+
if (typeof child === 'string') {
|
|
172
|
+
return child.trim().length > 0 ? (
|
|
173
|
+
<Text variant={LABEL_VARIANT} color={tint} weight="bold">
|
|
174
|
+
{child}
|
|
175
|
+
</Text>
|
|
176
|
+
) : null;
|
|
177
|
+
}
|
|
178
|
+
if (typeof child === 'number') {
|
|
179
|
+
return (
|
|
180
|
+
<Text variant={LABEL_VARIANT} color={tint} weight="bold">
|
|
181
|
+
{child}
|
|
182
|
+
</Text>
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
return child;
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// ─── Group ────────────────────────────────────────────────────────────────────
|
|
190
|
+
|
|
191
|
+
export interface ToolbarGroupProps extends ViewProps {
|
|
192
|
+
/** Capsule fill. Defaults to `surface.default`. */
|
|
193
|
+
background?: ColorToken;
|
|
194
|
+
/**
|
|
195
|
+
* Foreground token inherited by every item's icon and label. Defaults to
|
|
196
|
+
* `text.default`; a per-item `tint` still wins.
|
|
197
|
+
*/
|
|
198
|
+
tint?: ColorToken;
|
|
199
|
+
|
|
200
|
+
children?: ReactNode;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Wraps several {@link ToolbarButton}s in one shared capsule — the iOS
|
|
205
|
+
* `ToolbarItemGroup` look. Children drop their individual circular fill and
|
|
206
|
+
* inherit the group's, so the row reads as a single segmented pill.
|
|
207
|
+
*
|
|
208
|
+
* No height of its own: the capsule takes the height of its tallest child, and
|
|
209
|
+
* its full radius clamps to half of that for an exact stadium.
|
|
210
|
+
*
|
|
211
|
+
* @example
|
|
212
|
+
* ```tsx
|
|
213
|
+
* <Toolbar.Group>
|
|
214
|
+
* <Toolbar.Button icon="add" accessibilityLabel="Add" onPress={add} />
|
|
215
|
+
* <Toolbar.Button icon="download" accessibilityLabel="Save" onPress={save} />
|
|
216
|
+
* </Toolbar.Group>
|
|
217
|
+
* ```
|
|
218
|
+
*/
|
|
219
|
+
export const ToolbarGroup = forwardRef<
|
|
220
|
+
ComponentRef<typeof View>,
|
|
221
|
+
ToolbarGroupProps
|
|
222
|
+
>(function ToolbarGroup(
|
|
223
|
+
{
|
|
224
|
+
background = 'surface.default',
|
|
225
|
+
tint = 'text.default',
|
|
226
|
+
children,
|
|
227
|
+
style,
|
|
228
|
+
...rest
|
|
229
|
+
},
|
|
230
|
+
ref
|
|
231
|
+
) {
|
|
232
|
+
const context = useMemo<ToolbarGroupContextValue>(
|
|
233
|
+
() => ({ background: 'transparent', tint, isGrouped: true }),
|
|
234
|
+
[tint]
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
return (
|
|
238
|
+
<ToolbarGroupContext.Provider value={context}>
|
|
239
|
+
<View ref={ref} style={[styles.capsule(background), style]} {...rest}>
|
|
240
|
+
{children}
|
|
241
|
+
</View>
|
|
242
|
+
</ToolbarGroupContext.Provider>
|
|
243
|
+
);
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
// ─── Root ─────────────────────────────────────────────────────────────────────
|
|
247
|
+
|
|
248
|
+
export interface ToolbarProps extends ViewProps {
|
|
249
|
+
/**
|
|
250
|
+
* Space between groups and standalone items. Defaults to `'2'` (8dp) — the
|
|
251
|
+
* separator between a capsule and an adjacent standalone item.
|
|
252
|
+
*/
|
|
253
|
+
gap?: SpacingKey;
|
|
254
|
+
children?: ReactNode;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* A row of {@link ToolbarGroup}s and standalone {@link ToolbarButton}s. The
|
|
259
|
+
* header's `LeftToolbar` / `RightToolbar` slots supply one automatically, so
|
|
260
|
+
* you only need to render it yourself when placing a toolbar elsewhere — over
|
|
261
|
+
* a hero image, or floating above content.
|
|
262
|
+
*
|
|
263
|
+
* @example
|
|
264
|
+
* ```tsx
|
|
265
|
+
* <Toolbar style={{ position: 'absolute', right: 16, bottom: 24 }}>
|
|
266
|
+
* <Toolbar.Group>
|
|
267
|
+
* <Toolbar.Button icon="add" accessibilityLabel="Add" onPress={add} />
|
|
268
|
+
* <Toolbar.Button icon="download" accessibilityLabel="Save" onPress={save} />
|
|
269
|
+
* </Toolbar.Group>
|
|
270
|
+
* <Toolbar.Button icon="share" accessibilityLabel="Share" onPress={share} />
|
|
271
|
+
* </Toolbar>
|
|
272
|
+
* ```
|
|
273
|
+
*/
|
|
274
|
+
const ToolbarRoot = forwardRef<ComponentRef<typeof View>, ToolbarProps>(
|
|
275
|
+
function Toolbar({ gap = '2', children, style, ...rest }, ref) {
|
|
276
|
+
return (
|
|
277
|
+
<View ref={ref} style={[styles.row(gap), style]} {...rest}>
|
|
278
|
+
{children}
|
|
279
|
+
</View>
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
);
|
|
283
|
+
|
|
284
|
+
type ToolbarComponent = typeof ToolbarRoot & {
|
|
285
|
+
Group: typeof ToolbarGroup;
|
|
286
|
+
Button: typeof ToolbarButton;
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
export const Toolbar = ToolbarRoot as ToolbarComponent;
|
|
290
|
+
Toolbar.Group = ToolbarGroup;
|
|
291
|
+
Toolbar.Button = ToolbarButton;
|
|
292
|
+
|
|
293
|
+
// ─── Styles ───────────────────────────────────────────────────────────────────
|
|
294
|
+
|
|
295
|
+
const styles = StyleSheet.create((theme) => ({
|
|
296
|
+
row: (gap: SpacingKey) => ({
|
|
297
|
+
flexDirection: 'row',
|
|
298
|
+
alignItems: 'center',
|
|
299
|
+
gap: theme.spacing[gap],
|
|
300
|
+
}),
|
|
301
|
+
// `background` is an open `ColorToken` — it accepts raw literals, so it cannot
|
|
302
|
+
// be enumerated as a variant. Resolving inside the factory keeps the fill tied
|
|
303
|
+
// to the live theme.
|
|
304
|
+
capsule: (background: ColorToken) => ({
|
|
305
|
+
flexDirection: 'row',
|
|
306
|
+
alignItems: 'center',
|
|
307
|
+
gap: theme.spacing['5'],
|
|
308
|
+
paddingHorizontal: 10,
|
|
309
|
+
borderRadius: theme.radius.full,
|
|
310
|
+
backgroundColor: resolveThemeColor(background, theme),
|
|
311
|
+
borderWidth: StyleSheet.hairlineWidth,
|
|
312
|
+
borderColor: theme.color['border.subtle'],
|
|
313
|
+
}),
|
|
314
|
+
// Geometry is two finite axes, so it belongs in `variants` rather than in
|
|
315
|
+
// conditionals: an icon-only cell is an exact square, a labelled one keeps the
|
|
316
|
+
// height and grows; a grouped cell drops its own padding because the capsule
|
|
317
|
+
// supplies it.
|
|
318
|
+
button: {
|
|
319
|
+
height: ITEM_SIZE,
|
|
320
|
+
flexDirection: 'row',
|
|
321
|
+
alignItems: 'center',
|
|
322
|
+
justifyContent: 'center',
|
|
323
|
+
borderRadius: theme.radius.full,
|
|
324
|
+
variants: {
|
|
325
|
+
shape: {
|
|
326
|
+
icon: { width: ITEM_SIZE },
|
|
327
|
+
label: { gap: theme.spacing['1'] },
|
|
328
|
+
},
|
|
329
|
+
grouped: {
|
|
330
|
+
true: { paddingHorizontal: 0 },
|
|
331
|
+
false: {
|
|
332
|
+
minWidth: ITEM_SIZE,
|
|
333
|
+
paddingHorizontal: 10,
|
|
334
|
+
borderWidth: StyleSheet.hairlineWidth,
|
|
335
|
+
borderColor: theme.color['border.subtle'],
|
|
336
|
+
},
|
|
337
|
+
},
|
|
338
|
+
},
|
|
339
|
+
compoundVariants: [
|
|
340
|
+
{
|
|
341
|
+
shape: 'icon',
|
|
342
|
+
grouped: true,
|
|
343
|
+
styles: { width: undefined },
|
|
344
|
+
},
|
|
345
|
+
],
|
|
346
|
+
},
|
|
347
|
+
// Split from `button` so the open-ended fill and the press state stay in the
|
|
348
|
+
// stylesheet without turning the geometry into a dynamic function too.
|
|
349
|
+
fill: (background: ColorToken, pressed: boolean) => ({
|
|
350
|
+
backgroundColor: resolveThemeColor(background, theme),
|
|
351
|
+
opacity: pressed ? theme.opacity.pressed : 1,
|
|
352
|
+
}),
|
|
353
|
+
}));
|
|
@@ -6,10 +6,9 @@ import {
|
|
|
6
6
|
useMemo,
|
|
7
7
|
useState,
|
|
8
8
|
} from 'react';
|
|
9
|
-
|
|
9
|
+
import { SafeAreaProvider } from 'react-native-safe-area-context';
|
|
10
10
|
import { useUnistyles } from 'react-native-unistyles';
|
|
11
|
-
|
|
12
|
-
import { configureTerraUI, getIsConfigured } from '../theme/configure';
|
|
11
|
+
import { configureTerraUI, getIsConfigured } from '../theme/registry';
|
|
13
12
|
import { applyAccent, applyScheme, getCurrentAccent } from '../theme/runtime';
|
|
14
13
|
import type { Scheme, TerraTheme } from '../theme/types';
|
|
15
14
|
|
|
@@ -50,7 +49,9 @@ export const TerraUIProvider = ({ children }: PropsWithChildren) => {
|
|
|
50
49
|
);
|
|
51
50
|
|
|
52
51
|
return (
|
|
53
|
-
<ThemeContext.Provider value={value}>
|
|
52
|
+
<ThemeContext.Provider value={value}>
|
|
53
|
+
<SafeAreaProvider>{children}</SafeAreaProvider>
|
|
54
|
+
</ThemeContext.Provider>
|
|
54
55
|
);
|
|
55
56
|
};
|
|
56
57
|
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { createElement } from 'react';
|
|
2
|
+
import { Image } from 'react-native';
|
|
3
|
+
|
|
4
|
+
import type { TerraImageComponent, TerraImageContentFit } from '../types';
|
|
5
|
+
|
|
6
|
+
const CONTENT_FIT_MAP = {
|
|
7
|
+
cover: 'cover',
|
|
8
|
+
contain: 'contain',
|
|
9
|
+
fill: 'stretch',
|
|
10
|
+
} as const satisfies Record<
|
|
11
|
+
TerraImageContentFit,
|
|
12
|
+
'cover' | 'contain' | 'stretch'
|
|
13
|
+
>;
|
|
14
|
+
|
|
15
|
+
/** Default image implementation, backed by React Native's `Image`. */
|
|
16
|
+
export const defaultImage: TerraImageComponent = ({
|
|
17
|
+
contentFit = 'cover',
|
|
18
|
+
source,
|
|
19
|
+
style,
|
|
20
|
+
accessibilityLabel,
|
|
21
|
+
placeholder,
|
|
22
|
+
onError,
|
|
23
|
+
}) =>
|
|
24
|
+
createElement(Image, {
|
|
25
|
+
source,
|
|
26
|
+
style,
|
|
27
|
+
accessibilityLabel,
|
|
28
|
+
// RN splits "show this until loaded" across two platform-specific props:
|
|
29
|
+
// `defaultSource` (iOS only) and `loadingIndicatorSource` (Android only).
|
|
30
|
+
defaultSource: placeholder,
|
|
31
|
+
loadingIndicatorSource: placeholder,
|
|
32
|
+
resizeMode: CONTENT_FIT_MAP[contentFit],
|
|
33
|
+
onError: onError ? () => onError() : undefined,
|
|
34
|
+
});
|
package/src/theme/index.ts
CHANGED
|
@@ -23,7 +23,7 @@ export {
|
|
|
23
23
|
getRequiredIcon,
|
|
24
24
|
getSurfaceElevation,
|
|
25
25
|
resolveTheme,
|
|
26
|
-
} from './
|
|
26
|
+
} from './registry';
|
|
27
27
|
export { applyAccent, applyScheme, getCurrentAccent } from './runtime';
|
|
28
28
|
// ─── Default themes ───────────────────────────────────────────────────────────
|
|
29
29
|
export { defaultDarkTheme, defaultLightTheme } from './theme';
|
|
@@ -1,12 +1,16 @@
|
|
|
1
|
-
import { createElement } from 'react';
|
|
2
|
-
import { Image } from 'react-native';
|
|
3
1
|
import { StyleSheet } from 'react-native-unistyles';
|
|
4
2
|
|
|
5
|
-
import { defaultIcons } from '#components/icon/utils';
|
|
6
3
|
import { normalizeAccent } from '#utils/accent-utils';
|
|
7
4
|
import { deepMerge } from '#utils/deep-merge';
|
|
8
5
|
|
|
9
|
-
import {
|
|
6
|
+
import { defaultIcons } from './defaults/icons';
|
|
7
|
+
import { defaultImage } from './defaults/image';
|
|
8
|
+
import {
|
|
9
|
+
buildRadiusScale,
|
|
10
|
+
buildSpacingScale,
|
|
11
|
+
defaultDarkTheme,
|
|
12
|
+
defaultLightTheme,
|
|
13
|
+
} from './theme';
|
|
10
14
|
import type {
|
|
11
15
|
ComponentDefaults,
|
|
12
16
|
DefaultRadiusToken,
|
|
@@ -17,38 +21,11 @@ import type {
|
|
|
17
21
|
TerraConfig,
|
|
18
22
|
TerraIconComponent,
|
|
19
23
|
TerraImageComponent,
|
|
20
|
-
TerraImageContentFit,
|
|
21
24
|
TerraSemanticIconName,
|
|
22
25
|
TerraTheme,
|
|
23
26
|
TerraThemeOverride,
|
|
24
27
|
} from './types';
|
|
25
28
|
|
|
26
|
-
// ─── Default image component ──────────────────────────────────────────────────
|
|
27
|
-
|
|
28
|
-
const CONTENT_FIT_MAP = {
|
|
29
|
-
cover: 'cover',
|
|
30
|
-
contain: 'contain',
|
|
31
|
-
fill: 'stretch',
|
|
32
|
-
} as const satisfies Record<
|
|
33
|
-
TerraImageContentFit,
|
|
34
|
-
'cover' | 'contain' | 'stretch'
|
|
35
|
-
>;
|
|
36
|
-
|
|
37
|
-
const DefaultImage: TerraImageComponent = ({
|
|
38
|
-
contentFit = 'cover',
|
|
39
|
-
source,
|
|
40
|
-
style,
|
|
41
|
-
accessibilityLabel,
|
|
42
|
-
onError,
|
|
43
|
-
}) =>
|
|
44
|
-
createElement(Image, {
|
|
45
|
-
source,
|
|
46
|
-
style,
|
|
47
|
-
accessibilityLabel,
|
|
48
|
-
resizeMode: CONTENT_FIT_MAP[contentFit],
|
|
49
|
-
onError: onError ? () => onError() : undefined,
|
|
50
|
-
});
|
|
51
|
-
|
|
52
29
|
// ─── Radius / elevation defaults ─────────────────────────────────────────────
|
|
53
30
|
|
|
54
31
|
/** Fallback when a component's radius is not configured. */
|
|
@@ -94,7 +71,7 @@ const registry: Registry = {
|
|
|
94
71
|
defaultRadius: resolveDefaultRadius(),
|
|
95
72
|
surfaceElevation: DEFAULT_ELEVATION,
|
|
96
73
|
icons: {},
|
|
97
|
-
imageComponent:
|
|
74
|
+
imageComponent: defaultImage,
|
|
98
75
|
configured: false,
|
|
99
76
|
};
|
|
100
77
|
|
|
@@ -143,7 +120,7 @@ const mergeThemeOverride = (
|
|
|
143
120
|
): TerraTheme => {
|
|
144
121
|
if (!override) return theme;
|
|
145
122
|
|
|
146
|
-
const { radius, ...rest } = override;
|
|
123
|
+
const { radius, spacing, ...rest } = override;
|
|
147
124
|
let next = deepMerge(theme, rest);
|
|
148
125
|
|
|
149
126
|
if (radius) {
|
|
@@ -154,6 +131,14 @@ const mergeThemeOverride = (
|
|
|
154
131
|
next = deepMerge(next, { radius: radiusTokens });
|
|
155
132
|
}
|
|
156
133
|
|
|
134
|
+
if (spacing) {
|
|
135
|
+
const { base, ...spacingTokens } = spacing;
|
|
136
|
+
if (base != null) {
|
|
137
|
+
next = deepMerge(next, { spacing: buildSpacingScale(base) });
|
|
138
|
+
}
|
|
139
|
+
next = deepMerge(next, { spacing: spacingTokens });
|
|
140
|
+
}
|
|
141
|
+
|
|
157
142
|
return next;
|
|
158
143
|
};
|
|
159
144
|
|
|
@@ -187,7 +172,7 @@ export function configureTerraUI(config: TerraConfig = {}): void {
|
|
|
187
172
|
registry.surfaceElevation =
|
|
188
173
|
config.components?.surface?.elevation ?? DEFAULT_ELEVATION;
|
|
189
174
|
registry.icons = config.icons ?? {};
|
|
190
|
-
registry.imageComponent = config.image ??
|
|
175
|
+
registry.imageComponent = config.image ?? defaultImage;
|
|
191
176
|
|
|
192
177
|
registry.accents = {};
|
|
193
178
|
for (const [name, accent] of Object.entries(config.accents ?? {})) {
|
package/src/theme/runtime.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { UnistylesRuntime } from 'react-native-unistyles';
|
|
2
2
|
|
|
3
|
-
import { getRegistry, resolveTheme } from './
|
|
3
|
+
import { getRegistry, resolveTheme } from './registry';
|
|
4
4
|
import type { Scheme } from './types';
|
|
5
5
|
|
|
6
6
|
/** Apply a named accent at runtime by updating both registered themes. */
|