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.
Files changed (84) hide show
  1. package/README.md +251 -0
  2. package/lib/module/components/alert/index.js +106 -13
  3. package/lib/module/components/alert/index.js.map +1 -1
  4. package/lib/module/components/badge/index.js +3 -3
  5. package/lib/module/components/badge/index.js.map +1 -1
  6. package/lib/module/components/button/index.js +44 -15
  7. package/lib/module/components/button/index.js.map +1 -1
  8. package/lib/module/components/checkbox/index.js +20 -7
  9. package/lib/module/components/checkbox/index.js.map +1 -1
  10. package/lib/module/components/empty-state/index.js +178 -0
  11. package/lib/module/components/empty-state/index.js.map +1 -0
  12. package/lib/module/components/input-group/index.js +132 -0
  13. package/lib/module/components/input-group/index.js.map +1 -0
  14. package/lib/module/components/label/index.js +117 -0
  15. package/lib/module/components/label/index.js.map +1 -0
  16. package/lib/module/components/progress/index.js +138 -0
  17. package/lib/module/components/progress/index.js.map +1 -0
  18. package/lib/module/components/steps/index.js +354 -0
  19. package/lib/module/components/steps/index.js.map +1 -0
  20. package/lib/module/components/tabs/index.js +9 -3
  21. package/lib/module/components/tabs/index.js.map +1 -1
  22. package/lib/module/components/toast/index.js +479 -0
  23. package/lib/module/components/toast/index.js.map +1 -0
  24. package/lib/module/components/toast/toast-store.js +81 -0
  25. package/lib/module/components/toast/toast-store.js.map +1 -0
  26. package/lib/module/components/typography/index.js +126 -0
  27. package/lib/module/components/typography/index.js.map +1 -0
  28. package/lib/module/icons/index.js +223 -2
  29. package/lib/module/icons/index.js.map +1 -1
  30. package/lib/module/index.js +9 -2
  31. package/lib/module/index.js.map +1 -1
  32. package/lib/module/providers/panel-ui-provider.js +2 -1
  33. package/lib/module/providers/panel-ui-provider.js.map +1 -1
  34. package/lib/module/theme/use-theme.js +90 -3
  35. package/lib/module/theme/use-theme.js.map +1 -1
  36. package/lib/typescript/src/components/alert/index.d.ts +56 -4
  37. package/lib/typescript/src/components/alert/index.d.ts.map +1 -1
  38. package/lib/typescript/src/components/button/index.d.ts +39 -0
  39. package/lib/typescript/src/components/button/index.d.ts.map +1 -1
  40. package/lib/typescript/src/components/checkbox/index.d.ts +8 -0
  41. package/lib/typescript/src/components/checkbox/index.d.ts.map +1 -1
  42. package/lib/typescript/src/components/empty-state/index.d.ts +73 -0
  43. package/lib/typescript/src/components/empty-state/index.d.ts.map +1 -0
  44. package/lib/typescript/src/components/input-group/index.d.ts +34 -0
  45. package/lib/typescript/src/components/input-group/index.d.ts.map +1 -0
  46. package/lib/typescript/src/components/label/index.d.ts +28 -0
  47. package/lib/typescript/src/components/label/index.d.ts.map +1 -0
  48. package/lib/typescript/src/components/progress/index.d.ts +132 -0
  49. package/lib/typescript/src/components/progress/index.d.ts.map +1 -0
  50. package/lib/typescript/src/components/steps/index.d.ts +62 -0
  51. package/lib/typescript/src/components/steps/index.d.ts.map +1 -0
  52. package/lib/typescript/src/components/tabs/index.d.ts.map +1 -1
  53. package/lib/typescript/src/components/toast/index.d.ts +176 -0
  54. package/lib/typescript/src/components/toast/index.d.ts.map +1 -0
  55. package/lib/typescript/src/components/toast/toast-store.d.ts +65 -0
  56. package/lib/typescript/src/components/toast/toast-store.d.ts.map +1 -0
  57. package/lib/typescript/src/components/typography/index.d.ts +77 -0
  58. package/lib/typescript/src/components/typography/index.d.ts.map +1 -0
  59. package/lib/typescript/src/icons/index.d.ts +25 -0
  60. package/lib/typescript/src/icons/index.d.ts.map +1 -1
  61. package/lib/typescript/src/index.d.ts +10 -3
  62. package/lib/typescript/src/index.d.ts.map +1 -1
  63. package/lib/typescript/src/providers/panel-ui-provider.d.ts.map +1 -1
  64. package/lib/typescript/src/theme/use-theme.d.ts +64 -4
  65. package/lib/typescript/src/theme/use-theme.d.ts.map +1 -1
  66. package/package.json +22 -4
  67. package/src/components/alert/index.tsx +102 -16
  68. package/src/components/badge/index.tsx +3 -3
  69. package/src/components/button/index.tsx +47 -7
  70. package/src/components/checkbox/index.tsx +17 -3
  71. package/src/components/empty-state/index.tsx +136 -0
  72. package/src/components/input-group/index.tsx +164 -0
  73. package/src/components/label/index.tsx +128 -0
  74. package/src/components/progress/index.tsx +147 -0
  75. package/src/components/steps/index.tsx +343 -0
  76. package/src/components/tabs/index.tsx +10 -2
  77. package/src/components/toast/index.tsx +447 -0
  78. package/src/components/toast/toast-store.ts +120 -0
  79. package/src/components/typography/index.tsx +121 -0
  80. package/src/icons/index.tsx +152 -1
  81. package/src/index.ts +76 -3
  82. package/src/providers/panel-ui-provider.tsx +3 -0
  83. package/src/theme/use-theme.ts +119 -4
  84. package/theme.css +330 -7
@@ -1,10 +1,33 @@
1
- import Svg, { Path, type SvgProps } from 'react-native-svg';
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 { useTheme, type ThemeName } from './theme/use-theme';
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 { Alert, type AlertProps } from './components/alert';
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 { CheckIcon, ChevronDownIcon, XIcon, type IconProps } from './icons';
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>
@@ -1,6 +1,87 @@
1
1
  import { Uniwind, useUniwind } from 'uniwind';
2
2
 
3
- export type ThemeName = 'light' | 'dark' | 'system';
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
- /** Currently resolved theme ('light' or 'dark'). */
14
- theme,
15
- /** Switch theme: 'light', 'dark', or 'system' (follows device). */
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
+ }