panelui-native 0.2.0 → 0.4.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/README.md +251 -0
- package/lib/module/components/alert/index.js +106 -13
- package/lib/module/components/alert/index.js.map +1 -1
- package/lib/module/components/badge/index.js +3 -3
- package/lib/module/components/badge/index.js.map +1 -1
- package/lib/module/components/button/index.js +44 -15
- package/lib/module/components/button/index.js.map +1 -1
- package/lib/module/components/checkbox/index.js +20 -7
- package/lib/module/components/checkbox/index.js.map +1 -1
- package/lib/module/components/empty-state/index.js +178 -0
- package/lib/module/components/empty-state/index.js.map +1 -0
- package/lib/module/components/input-group/index.js +132 -0
- package/lib/module/components/input-group/index.js.map +1 -0
- package/lib/module/components/label/index.js +117 -0
- package/lib/module/components/label/index.js.map +1 -0
- package/lib/module/components/progress/index.js +138 -0
- package/lib/module/components/progress/index.js.map +1 -0
- package/lib/module/components/steps/index.js +354 -0
- package/lib/module/components/steps/index.js.map +1 -0
- package/lib/module/components/tabs/index.js +9 -3
- package/lib/module/components/tabs/index.js.map +1 -1
- package/lib/module/components/toast/index.js +479 -0
- package/lib/module/components/toast/index.js.map +1 -0
- package/lib/module/components/toast/toast-store.js +81 -0
- package/lib/module/components/toast/toast-store.js.map +1 -0
- package/lib/module/components/typography/index.js +126 -0
- package/lib/module/components/typography/index.js.map +1 -0
- package/lib/module/icons/index.js +223 -2
- package/lib/module/icons/index.js.map +1 -1
- package/lib/module/index.js +9 -2
- package/lib/module/index.js.map +1 -1
- package/lib/module/providers/panel-ui-provider.js +2 -1
- package/lib/module/providers/panel-ui-provider.js.map +1 -1
- package/lib/module/theme/use-theme.js +90 -3
- package/lib/module/theme/use-theme.js.map +1 -1
- package/lib/typescript/src/components/alert/index.d.ts +56 -4
- package/lib/typescript/src/components/alert/index.d.ts.map +1 -1
- package/lib/typescript/src/components/button/index.d.ts +39 -0
- package/lib/typescript/src/components/button/index.d.ts.map +1 -1
- package/lib/typescript/src/components/checkbox/index.d.ts +8 -0
- package/lib/typescript/src/components/checkbox/index.d.ts.map +1 -1
- package/lib/typescript/src/components/empty-state/index.d.ts +73 -0
- package/lib/typescript/src/components/empty-state/index.d.ts.map +1 -0
- package/lib/typescript/src/components/input-group/index.d.ts +34 -0
- package/lib/typescript/src/components/input-group/index.d.ts.map +1 -0
- package/lib/typescript/src/components/label/index.d.ts +28 -0
- package/lib/typescript/src/components/label/index.d.ts.map +1 -0
- package/lib/typescript/src/components/progress/index.d.ts +132 -0
- package/lib/typescript/src/components/progress/index.d.ts.map +1 -0
- package/lib/typescript/src/components/steps/index.d.ts +62 -0
- package/lib/typescript/src/components/steps/index.d.ts.map +1 -0
- package/lib/typescript/src/components/tabs/index.d.ts.map +1 -1
- package/lib/typescript/src/components/toast/index.d.ts +176 -0
- package/lib/typescript/src/components/toast/index.d.ts.map +1 -0
- package/lib/typescript/src/components/toast/toast-store.d.ts +65 -0
- package/lib/typescript/src/components/toast/toast-store.d.ts.map +1 -0
- package/lib/typescript/src/components/typography/index.d.ts +77 -0
- package/lib/typescript/src/components/typography/index.d.ts.map +1 -0
- package/lib/typescript/src/icons/index.d.ts +25 -0
- package/lib/typescript/src/icons/index.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +10 -3
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/providers/panel-ui-provider.d.ts.map +1 -1
- package/lib/typescript/src/theme/use-theme.d.ts +64 -4
- package/lib/typescript/src/theme/use-theme.d.ts.map +1 -1
- package/package.json +22 -4
- package/src/components/alert/index.tsx +102 -16
- package/src/components/badge/index.tsx +3 -3
- package/src/components/button/index.tsx +47 -7
- package/src/components/checkbox/index.tsx +17 -3
- package/src/components/empty-state/index.tsx +136 -0
- package/src/components/input-group/index.tsx +164 -0
- package/src/components/label/index.tsx +128 -0
- package/src/components/progress/index.tsx +147 -0
- package/src/components/steps/index.tsx +343 -0
- package/src/components/tabs/index.tsx +10 -2
- package/src/components/toast/index.tsx +447 -0
- package/src/components/toast/toast-store.ts +120 -0
- package/src/components/typography/index.tsx +121 -0
- package/src/icons/index.tsx +152 -1
- package/src/index.ts +76 -3
- package/src/providers/panel-ui-provider.tsx +3 -0
- package/src/theme/use-theme.ts +119 -4
- package/theme.css +330 -7
package/src/icons/index.tsx
CHANGED
|
@@ -1,10 +1,33 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Icon set.
|
|
3
|
+
*
|
|
4
|
+
* Two families live here:
|
|
5
|
+
* - Stroked 24×24 icons (Lucide geometry, as used by shadcn-ui/ui) for chrome
|
|
6
|
+
* such as chevrons and the close button.
|
|
7
|
+
* - Filled 16×16 status icons for Alert and Toast indicators.
|
|
8
|
+
* Adapted from: heroui-inc/heroui-native src/components/alert/{default,success,warning}-icon.tsx
|
|
9
|
+
*
|
|
10
|
+
* `color` has no default on the status icons on purpose — callers resolve it
|
|
11
|
+
* from the active theme with `useCSSVariable`, so icons follow the theme
|
|
12
|
+
* instead of a hardcoded hex.
|
|
13
|
+
*/
|
|
14
|
+
import Svg, { Circle, Path, type SvgProps } from 'react-native-svg';
|
|
2
15
|
|
|
3
16
|
export interface IconProps extends SvgProps {
|
|
4
17
|
size?: number;
|
|
5
18
|
color?: string;
|
|
6
19
|
}
|
|
7
20
|
|
|
21
|
+
/** Props for icons that must never be announced by a screen reader. */
|
|
22
|
+
const decorative = {
|
|
23
|
+
accessibilityElementsHidden: true,
|
|
24
|
+
importantForAccessibility: 'no-hide-descendants',
|
|
25
|
+
} as const;
|
|
26
|
+
|
|
27
|
+
/* -------------------------------------------------------------------------- */
|
|
28
|
+
/* Stroked chrome icons */
|
|
29
|
+
/* -------------------------------------------------------------------------- */
|
|
30
|
+
|
|
8
31
|
export function CheckIcon({ size = 14, color = '#fff', ...props }: IconProps) {
|
|
9
32
|
return (
|
|
10
33
|
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none" {...props}>
|
|
@@ -33,6 +56,34 @@ export function ChevronDownIcon({ size = 16, color = '#737373', ...props }: Icon
|
|
|
33
56
|
);
|
|
34
57
|
}
|
|
35
58
|
|
|
59
|
+
export function ChevronLeftIcon({ size = 16, color = '#737373', ...props }: IconProps) {
|
|
60
|
+
return (
|
|
61
|
+
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none" {...props}>
|
|
62
|
+
<Path
|
|
63
|
+
d="m15 18-6-6 6-6"
|
|
64
|
+
stroke={color}
|
|
65
|
+
strokeWidth={2}
|
|
66
|
+
strokeLinecap="round"
|
|
67
|
+
strokeLinejoin="round"
|
|
68
|
+
/>
|
|
69
|
+
</Svg>
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function ChevronRightIcon({ size = 16, color = '#737373', ...props }: IconProps) {
|
|
74
|
+
return (
|
|
75
|
+
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none" {...props}>
|
|
76
|
+
<Path
|
|
77
|
+
d="m9 18 6-6-6-6"
|
|
78
|
+
stroke={color}
|
|
79
|
+
strokeWidth={2}
|
|
80
|
+
strokeLinecap="round"
|
|
81
|
+
strokeLinejoin="round"
|
|
82
|
+
/>
|
|
83
|
+
</Svg>
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
36
87
|
export function XIcon({ size = 16, color = '#737373', ...props }: IconProps) {
|
|
37
88
|
return (
|
|
38
89
|
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none" {...props}>
|
|
@@ -46,3 +97,103 @@ export function XIcon({ size = 16, color = '#737373', ...props }: IconProps) {
|
|
|
46
97
|
</Svg>
|
|
47
98
|
);
|
|
48
99
|
}
|
|
100
|
+
|
|
101
|
+
export function SearchIcon({ size = 16, color = '#737373', ...props }: IconProps) {
|
|
102
|
+
return (
|
|
103
|
+
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none" {...props}>
|
|
104
|
+
<Circle cx={11} cy={11} r={8} stroke={color} strokeWidth={2} />
|
|
105
|
+
<Path
|
|
106
|
+
d="m21 21-4.3-4.3"
|
|
107
|
+
stroke={color}
|
|
108
|
+
strokeWidth={2}
|
|
109
|
+
strokeLinecap="round"
|
|
110
|
+
strokeLinejoin="round"
|
|
111
|
+
/>
|
|
112
|
+
</Svg>
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function ArrowUpRightIcon({ size = 20, color = '#fff', ...props }: IconProps) {
|
|
117
|
+
return (
|
|
118
|
+
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none" {...props}>
|
|
119
|
+
<Path
|
|
120
|
+
d="M7 17 17 7M8 7h9v9"
|
|
121
|
+
stroke={color}
|
|
122
|
+
strokeWidth={2}
|
|
123
|
+
strokeLinecap="round"
|
|
124
|
+
strokeLinejoin="round"
|
|
125
|
+
/>
|
|
126
|
+
</Svg>
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export function SunIcon({ size = 18, color = '#f5f5f5', ...props }: IconProps) {
|
|
131
|
+
return (
|
|
132
|
+
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none" {...props}>
|
|
133
|
+
<Circle cx={12} cy={12} r={4} stroke={color} strokeWidth={2} />
|
|
134
|
+
<Path
|
|
135
|
+
d="M12 2v2m0 16v2M4.93 4.93l1.41 1.41m11.32 11.32 1.41 1.41M2 12h2m16 0h2M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41"
|
|
136
|
+
stroke={color}
|
|
137
|
+
strokeWidth={2}
|
|
138
|
+
strokeLinecap="round"
|
|
139
|
+
/>
|
|
140
|
+
</Svg>
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export function MoonIcon({ size = 18, color = '#262626', ...props }: IconProps) {
|
|
145
|
+
return (
|
|
146
|
+
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none" {...props}>
|
|
147
|
+
<Path
|
|
148
|
+
d="M21 12.79A9 9 0 1 1 11.21 3a7 7 0 0 0 9.79 9.79"
|
|
149
|
+
stroke={color}
|
|
150
|
+
strokeWidth={2}
|
|
151
|
+
strokeLinecap="round"
|
|
152
|
+
strokeLinejoin="round"
|
|
153
|
+
/>
|
|
154
|
+
</Svg>
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/* -------------------------------------------------------------------------- */
|
|
159
|
+
/* Filled status icons (Alert / Toast indicators) */
|
|
160
|
+
/* -------------------------------------------------------------------------- */
|
|
161
|
+
|
|
162
|
+
/** Info circle. Used for `default`, `info` and `destructive` status. */
|
|
163
|
+
export function InfoIcon({ size = 20, color, ...props }: IconProps) {
|
|
164
|
+
return (
|
|
165
|
+
<Svg width={size} height={size} viewBox="0 0 16 16" fill={color} {...decorative} {...props}>
|
|
166
|
+
<Path
|
|
167
|
+
d="M8 13.5a5.5 5.5 0 1 0 0-11a5.5 5.5 0 0 0 0 11M8 15A7 7 0 1 0 8 1a7 7 0 0 0 0 14m1-9.5a1 1 0 1 1-2 0a1 1 0 0 1 2 0m-.25 3a.75.75 0 0 0-1.5 0V11a.75.75 0 0 0 1.5 0z"
|
|
168
|
+
fillRule="evenodd"
|
|
169
|
+
clipRule="evenodd"
|
|
170
|
+
/>
|
|
171
|
+
</Svg>
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/** Check circle. Used for `success` status. */
|
|
176
|
+
export function CheckCircleIcon({ size = 20, color, ...props }: IconProps) {
|
|
177
|
+
return (
|
|
178
|
+
<Svg width={size} height={size} viewBox="0 0 16 16" fill={color} {...decorative} {...props}>
|
|
179
|
+
<Path
|
|
180
|
+
d="M13.5 8a5.5 5.5 0 1 1-11 0a5.5 5.5 0 0 1 11 0M15 8A7 7 0 1 1 1 8a7 7 0 0 1 14 0m-3.9-1.55a.75.75 0 1 0-1.2-.9L7.419 8.858L6.03 7.47a.75.75 0 0 0-1.06 1.06l2 2a.75.75 0 0 0 1.13-.08z"
|
|
181
|
+
fillRule="evenodd"
|
|
182
|
+
clipRule="evenodd"
|
|
183
|
+
/>
|
|
184
|
+
</Svg>
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/** Warning triangle. Used for `warning` status. */
|
|
189
|
+
export function AlertTriangleIcon({ size = 20, color, ...props }: IconProps) {
|
|
190
|
+
return (
|
|
191
|
+
<Svg width={size} height={size} viewBox="0 0 16 16" fill={color} {...decorative} {...props}>
|
|
192
|
+
<Path
|
|
193
|
+
d="M7.134 2.994L2.217 11.5a1 1 0 0 0 .866 1.5h9.834a1 1 0 0 0 .866-1.5L8.866 2.993a1 1 0 0 0-1.732 0m3.03-.75c-.962-1.665-3.366-1.665-4.329 0L.918 10.749c-.963 1.666.24 3.751 2.165 3.751h9.834c1.925 0 3.128-2.085 2.164-3.751zM8 5a.75.75 0 0 1 .75.75v2a.75.75 0 0 1-1.5 0v-2A.75.75 0 0 1 8 5m1 5.75a1 1 0 1 1-2 0a1 1 0 0 1 2 0"
|
|
194
|
+
fillRule="evenodd"
|
|
195
|
+
clipRule="evenodd"
|
|
196
|
+
/>
|
|
197
|
+
</Svg>
|
|
198
|
+
);
|
|
199
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -2,7 +2,17 @@
|
|
|
2
2
|
export { PanelUIProvider, type PanelUIProviderProps } from './providers/panel-ui-provider';
|
|
3
3
|
|
|
4
4
|
// Theme
|
|
5
|
-
export {
|
|
5
|
+
export {
|
|
6
|
+
useTheme,
|
|
7
|
+
useThemeMode,
|
|
8
|
+
PANEL_THEMES,
|
|
9
|
+
PANEL_THEME_NAMES,
|
|
10
|
+
PANEL_EXTRA_THEMES,
|
|
11
|
+
type ThemeName,
|
|
12
|
+
type ThemeMode,
|
|
13
|
+
type PanelTheme,
|
|
14
|
+
type PanelThemeFamily,
|
|
15
|
+
} from './theme/use-theme';
|
|
6
16
|
|
|
7
17
|
// Primitives
|
|
8
18
|
export { Portal, PortalHost, PortalProvider } from './primitives/portal';
|
|
@@ -13,7 +23,11 @@ export {
|
|
|
13
23
|
} from './primitives/animated-pressable';
|
|
14
24
|
|
|
15
25
|
// Components
|
|
16
|
-
export {
|
|
26
|
+
export {
|
|
27
|
+
Alert,
|
|
28
|
+
type AlertProps,
|
|
29
|
+
type AlertIndicatorProps,
|
|
30
|
+
} from './components/alert';
|
|
17
31
|
export { Avatar, type AvatarProps } from './components/avatar';
|
|
18
32
|
export { Badge, type BadgeProps } from './components/badge';
|
|
19
33
|
export {
|
|
@@ -43,7 +57,20 @@ export {
|
|
|
43
57
|
} from './components/tabs';
|
|
44
58
|
export { Card, type CardProps } from './components/card';
|
|
45
59
|
export { Checkbox, type CheckboxProps } from './components/checkbox';
|
|
60
|
+
export {
|
|
61
|
+
EmptyState,
|
|
62
|
+
type EmptyStateProps,
|
|
63
|
+
type EmptyStateMediaProps,
|
|
64
|
+
} from './components/empty-state';
|
|
46
65
|
export { Input, type InputProps } from './components/input';
|
|
66
|
+
export {
|
|
67
|
+
InputGroup,
|
|
68
|
+
type InputGroupProps,
|
|
69
|
+
type InputGroupInputProps,
|
|
70
|
+
type InputGroupDecoratorProps,
|
|
71
|
+
} from './components/input-group';
|
|
72
|
+
export { Label, type LabelProps, type LabelTextProps } from './components/label';
|
|
73
|
+
export { Progress, type ProgressProps } from './components/progress';
|
|
47
74
|
export {
|
|
48
75
|
RadioGroup,
|
|
49
76
|
type RadioGroupProps,
|
|
@@ -51,10 +78,56 @@ export {
|
|
|
51
78
|
} from './components/radio-group';
|
|
52
79
|
export { Skeleton, type SkeletonProps } from './components/skeleton';
|
|
53
80
|
export { Spinner, type SpinnerProps } from './components/spinner';
|
|
81
|
+
export {
|
|
82
|
+
Steps,
|
|
83
|
+
type StepsProps,
|
|
84
|
+
type StepsItemProps,
|
|
85
|
+
type StepsTriggerProps,
|
|
86
|
+
type StepsIndicatorProps,
|
|
87
|
+
type StepsSeparatorProps,
|
|
88
|
+
type StepState,
|
|
89
|
+
type StepsOrientation,
|
|
90
|
+
} from './components/steps';
|
|
54
91
|
export { Switch, type SwitchProps } from './components/switch';
|
|
92
|
+
export {
|
|
93
|
+
Toast,
|
|
94
|
+
ToastViewport,
|
|
95
|
+
toast,
|
|
96
|
+
useToast,
|
|
97
|
+
type ToastProps,
|
|
98
|
+
type ToastOptions,
|
|
99
|
+
type ToastItem,
|
|
100
|
+
type ToastVariant,
|
|
101
|
+
type ToastPlacement,
|
|
102
|
+
type ToastHandle,
|
|
103
|
+
type ToastIndicatorProps,
|
|
104
|
+
type ToastCloseProps,
|
|
105
|
+
} from './components/toast';
|
|
106
|
+
export {
|
|
107
|
+
Typography,
|
|
108
|
+
type TypographyProps,
|
|
109
|
+
type TypographyType,
|
|
110
|
+
type TypographyHeadingProps,
|
|
111
|
+
type TypographyParagraphProps,
|
|
112
|
+
type TypographyCodeProps,
|
|
113
|
+
} from './components/typography';
|
|
55
114
|
|
|
56
115
|
// Icons
|
|
57
|
-
export {
|
|
116
|
+
export {
|
|
117
|
+
AlertTriangleIcon,
|
|
118
|
+
ArrowUpRightIcon,
|
|
119
|
+
CheckCircleIcon,
|
|
120
|
+
CheckIcon,
|
|
121
|
+
ChevronDownIcon,
|
|
122
|
+
ChevronLeftIcon,
|
|
123
|
+
ChevronRightIcon,
|
|
124
|
+
InfoIcon,
|
|
125
|
+
MoonIcon,
|
|
126
|
+
SearchIcon,
|
|
127
|
+
SunIcon,
|
|
128
|
+
XIcon,
|
|
129
|
+
type IconProps,
|
|
130
|
+
} from './icons';
|
|
58
131
|
|
|
59
132
|
// Utils
|
|
60
133
|
export { cn } from './utils/cn';
|
|
@@ -2,6 +2,7 @@ import type { ReactNode } from 'react';
|
|
|
2
2
|
import { StyleSheet, View } from 'react-native';
|
|
3
3
|
import { GestureHandlerRootView } from 'react-native-gesture-handler';
|
|
4
4
|
import { PortalHost, PortalProvider } from '../primitives/portal';
|
|
5
|
+
import { ToastViewport } from '../components/toast';
|
|
5
6
|
import { cn } from '../utils/cn';
|
|
6
7
|
|
|
7
8
|
export interface PanelUIProviderProps {
|
|
@@ -40,6 +41,8 @@ export function PanelUIProvider({
|
|
|
40
41
|
<View className={cn('flex-1', background && 'bg-background', className)}>
|
|
41
42
|
<PortalProvider>
|
|
42
43
|
{children}
|
|
44
|
+
{/* Sits before PortalHost so it can portal into it. */}
|
|
45
|
+
<ToastViewport />
|
|
43
46
|
<PortalHost />
|
|
44
47
|
</PortalProvider>
|
|
45
48
|
</View>
|
package/src/theme/use-theme.ts
CHANGED
|
@@ -1,6 +1,87 @@
|
|
|
1
1
|
import { Uniwind, useUniwind } from 'uniwind';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
/** Themes shipped in `panelui-native/theme.css`. */
|
|
4
|
+
export type PanelTheme =
|
|
5
|
+
| 'light'
|
|
6
|
+
| 'dark'
|
|
7
|
+
| 'moon'
|
|
8
|
+
| 'moon-dark'
|
|
9
|
+
| 'grass'
|
|
10
|
+
| 'grass-dark';
|
|
11
|
+
|
|
12
|
+
export type ThemeName = PanelTheme | 'system';
|
|
13
|
+
|
|
14
|
+
/** Light/dark mode within a theme family. */
|
|
15
|
+
export type ThemeMode = 'light' | 'dark';
|
|
16
|
+
|
|
17
|
+
/** A theme family — one look, in its light and dark form. */
|
|
18
|
+
export interface PanelThemeFamily {
|
|
19
|
+
/** Stable id, e.g. `'moon'`. */
|
|
20
|
+
id: string;
|
|
21
|
+
/** Short display name, for a theme picker. */
|
|
22
|
+
name: string;
|
|
23
|
+
light: PanelTheme;
|
|
24
|
+
dark: PanelTheme;
|
|
25
|
+
/** Representative colour for a swatch, as `[light, dark]`. */
|
|
26
|
+
swatch: [string, string];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* The theme families PanelUI ships. Each sets its own palette *and* its own
|
|
31
|
+
* radius scale, so switching family changes the shape of the UI as well as
|
|
32
|
+
* its colour — Panel is the Coss default, Moon is sharp and monochrome,
|
|
33
|
+
* Grass is soft and green.
|
|
34
|
+
*
|
|
35
|
+
* Everything beyond `panel` must be registered in your metro config, or
|
|
36
|
+
* `setTheme` throws "it was not registered":
|
|
37
|
+
*
|
|
38
|
+
* ```js
|
|
39
|
+
* withUniwindConfig(config, {
|
|
40
|
+
* cssEntryFile: './global.css',
|
|
41
|
+
* extraThemes: ['moon', 'moon-dark', 'grass', 'grass-dark'],
|
|
42
|
+
* });
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
export const PANEL_THEMES: readonly PanelThemeFamily[] = [
|
|
46
|
+
{
|
|
47
|
+
id: 'panel',
|
|
48
|
+
name: 'Panel',
|
|
49
|
+
light: 'light',
|
|
50
|
+
dark: 'dark',
|
|
51
|
+
swatch: ['#262626', '#f5f5f5'],
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
id: 'moon',
|
|
55
|
+
name: 'Moon',
|
|
56
|
+
light: 'moon',
|
|
57
|
+
dark: 'moon-dark',
|
|
58
|
+
swatch: ['#0070f3', '#0070f3'],
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
id: 'grass',
|
|
62
|
+
name: 'Grass',
|
|
63
|
+
light: 'grass',
|
|
64
|
+
dark: 'grass-dark',
|
|
65
|
+
swatch: ['#24b47e', '#3ecf8e'],
|
|
66
|
+
},
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
/** Every concrete theme name, in registration order. */
|
|
70
|
+
export const PANEL_THEME_NAMES: readonly PanelTheme[] = PANEL_THEMES.flatMap(
|
|
71
|
+
(family) => [family.light, family.dark]
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
/** The names to pass as `extraThemes` in your metro config. */
|
|
75
|
+
export const PANEL_EXTRA_THEMES: readonly string[] = PANEL_THEME_NAMES.filter(
|
|
76
|
+
(name) => name !== 'light' && name !== 'dark'
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
function findFamily(theme: string): PanelThemeFamily {
|
|
80
|
+
return (
|
|
81
|
+
PANEL_THEMES.find((family) => family.light === theme || family.dark === theme) ??
|
|
82
|
+
PANEL_THEMES[0]!
|
|
83
|
+
);
|
|
84
|
+
}
|
|
4
85
|
|
|
5
86
|
/**
|
|
6
87
|
* Reactive access to the current Uniwind theme plus a setter.
|
|
@@ -10,9 +91,43 @@ export function useTheme() {
|
|
|
10
91
|
const { theme } = useUniwind();
|
|
11
92
|
|
|
12
93
|
return {
|
|
13
|
-
/**
|
|
14
|
-
theme,
|
|
15
|
-
/** Switch theme
|
|
94
|
+
/** The active theme name, e.g. `'dark'` or `'moon-dark'`. */
|
|
95
|
+
theme: theme as PanelTheme,
|
|
96
|
+
/** Switch theme by name, or `'system'` to follow the device. */
|
|
16
97
|
setTheme: (name: ThemeName) => Uniwind.setTheme(name),
|
|
17
98
|
};
|
|
18
99
|
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Theme family and light/dark mode as two separate axes — what a picker like
|
|
103
|
+
* "choose a brand, then toggle the sun" actually needs.
|
|
104
|
+
*
|
|
105
|
+
* Named themes are not adaptive in Uniwind, so each brand ships as a
|
|
106
|
+
* light/dark pair. Toggling the mode keeps the current family, so a user in
|
|
107
|
+
* Moon dark lands in Moon light rather than the default theme.
|
|
108
|
+
*
|
|
109
|
+
* ```tsx
|
|
110
|
+
* const { family, mode, setFamily, toggleMode } = useThemeMode();
|
|
111
|
+
* ```
|
|
112
|
+
*/
|
|
113
|
+
export function useThemeMode() {
|
|
114
|
+
const { theme } = useUniwind();
|
|
115
|
+
const family = findFamily(theme);
|
|
116
|
+
const mode: ThemeMode = theme === family.dark ? 'dark' : 'light';
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
/** The active theme family. */
|
|
120
|
+
family,
|
|
121
|
+
/** `'light'` or `'dark'` within that family. */
|
|
122
|
+
mode,
|
|
123
|
+
/** Switch brand, staying in the current light/dark mode. */
|
|
124
|
+
setFamily: (id: string) => {
|
|
125
|
+
const next = PANEL_THEMES.find((candidate) => candidate.id === id);
|
|
126
|
+
if (next) Uniwind.setTheme(next[mode]);
|
|
127
|
+
},
|
|
128
|
+
/** Set the mode, staying in the current family. */
|
|
129
|
+
setMode: (next: ThemeMode) => Uniwind.setTheme(family[next]),
|
|
130
|
+
/** Flip light ↔ dark within the current family. */
|
|
131
|
+
toggleMode: () => Uniwind.setTheme(mode === 'dark' ? family.light : family.dark),
|
|
132
|
+
};
|
|
133
|
+
}
|