panelui-native 0.7.0 → 0.12.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 (161) hide show
  1. package/README.md +5 -3
  2. package/lib/module/components/attachment/index.js +219 -0
  3. package/lib/module/components/attachment/index.js.map +1 -0
  4. package/lib/module/components/bottom-sheet/index.js +53 -3
  5. package/lib/module/components/bottom-sheet/index.js.map +1 -1
  6. package/lib/module/components/button/index.js +19 -1
  7. package/lib/module/components/button/index.js.map +1 -1
  8. package/lib/module/components/dialog/index.js +6 -2
  9. package/lib/module/components/dialog/index.js.map +1 -1
  10. package/lib/module/components/empty-state/index.js +94 -17
  11. package/lib/module/components/empty-state/index.js.map +1 -1
  12. package/lib/module/components/frame/index.js +128 -15
  13. package/lib/module/components/frame/index.js.map +1 -1
  14. package/lib/module/components/input/index.js +107 -15
  15. package/lib/module/components/input/index.js.map +1 -1
  16. package/lib/module/components/line-chart/index.js +1024 -0
  17. package/lib/module/components/line-chart/index.js.map +1 -0
  18. package/lib/module/components/marker/index.js +190 -0
  19. package/lib/module/components/marker/index.js.map +1 -0
  20. package/lib/module/components/message/index.js +24 -8
  21. package/lib/module/components/message/index.js.map +1 -1
  22. package/lib/module/components/message-scroller/index.js +451 -0
  23. package/lib/module/components/message-scroller/index.js.map +1 -0
  24. package/lib/module/components/popover/index.js +511 -0
  25. package/lib/module/components/popover/index.js.map +1 -0
  26. package/lib/module/components/progress/index.js +52 -2
  27. package/lib/module/components/progress/index.js.map +1 -1
  28. package/lib/module/components/radio-group/index.js +132 -17
  29. package/lib/module/components/radio-group/index.js.map +1 -1
  30. package/lib/module/components/section-rail/index.js +355 -0
  31. package/lib/module/components/section-rail/index.js.map +1 -0
  32. package/lib/module/components/select/index.js +245 -56
  33. package/lib/module/components/select/index.js.map +1 -1
  34. package/lib/module/components/separator/index.js +91 -0
  35. package/lib/module/components/separator/index.js.map +1 -0
  36. package/lib/module/components/slider/index.js +355 -0
  37. package/lib/module/components/slider/index.js.map +1 -0
  38. package/lib/module/components/surface/index.js +21 -2
  39. package/lib/module/components/surface/index.js.map +1 -1
  40. package/lib/module/components/switch/index.js +10 -1
  41. package/lib/module/components/switch/index.js.map +1 -1
  42. package/lib/module/components/tabs/index.js +186 -16
  43. package/lib/module/components/tabs/index.js.map +1 -1
  44. package/lib/module/components/toggle-button/index.js +297 -0
  45. package/lib/module/components/toggle-button/index.js.map +1 -0
  46. package/lib/module/hooks/index.js +1 -0
  47. package/lib/module/hooks/index.js.map +1 -1
  48. package/lib/module/hooks/use-keyboard-avoidance.js +57 -8
  49. package/lib/module/hooks/use-keyboard-avoidance.js.map +1 -1
  50. package/lib/module/hooks/use-scroll-sections.js +89 -0
  51. package/lib/module/hooks/use-scroll-sections.js.map +1 -0
  52. package/lib/module/icons/index.js +69 -0
  53. package/lib/module/icons/index.js.map +1 -1
  54. package/lib/module/index.js +11 -2
  55. package/lib/module/index.js.map +1 -1
  56. package/lib/module/native/index.js.map +1 -1
  57. package/lib/module/primitives/keyboard-avoider.js +2 -0
  58. package/lib/module/primitives/keyboard-avoider.js.map +1 -1
  59. package/lib/module/primitives/scrim.js +72 -0
  60. package/lib/module/primitives/scrim.js.map +1 -0
  61. package/lib/module/utils/haptics.js +57 -0
  62. package/lib/module/utils/haptics.js.map +1 -0
  63. package/lib/typescript/src/components/attachment/index.d.ts +93 -0
  64. package/lib/typescript/src/components/attachment/index.d.ts.map +1 -0
  65. package/lib/typescript/src/components/bottom-sheet/index.d.ts +6 -1
  66. package/lib/typescript/src/components/bottom-sheet/index.d.ts.map +1 -1
  67. package/lib/typescript/src/components/button/index.d.ts +9 -4
  68. package/lib/typescript/src/components/button/index.d.ts.map +1 -1
  69. package/lib/typescript/src/components/dialog/index.d.ts +7 -1
  70. package/lib/typescript/src/components/dialog/index.d.ts.map +1 -1
  71. package/lib/typescript/src/components/empty-state/index.d.ts +152 -2
  72. package/lib/typescript/src/components/empty-state/index.d.ts.map +1 -1
  73. package/lib/typescript/src/components/frame/index.d.ts +47 -7
  74. package/lib/typescript/src/components/frame/index.d.ts.map +1 -1
  75. package/lib/typescript/src/components/input/index.d.ts +113 -2
  76. package/lib/typescript/src/components/input/index.d.ts.map +1 -1
  77. package/lib/typescript/src/components/line-chart/index.d.ts +247 -0
  78. package/lib/typescript/src/components/line-chart/index.d.ts.map +1 -0
  79. package/lib/typescript/src/components/marker/index.d.ts +137 -0
  80. package/lib/typescript/src/components/marker/index.d.ts.map +1 -0
  81. package/lib/typescript/src/components/message/index.d.ts +8 -1
  82. package/lib/typescript/src/components/message/index.d.ts.map +1 -1
  83. package/lib/typescript/src/components/message-scroller/index.d.ts +150 -0
  84. package/lib/typescript/src/components/message-scroller/index.d.ts.map +1 -0
  85. package/lib/typescript/src/components/popover/index.d.ts +136 -0
  86. package/lib/typescript/src/components/popover/index.d.ts.map +1 -0
  87. package/lib/typescript/src/components/progress/index.d.ts +33 -0
  88. package/lib/typescript/src/components/progress/index.d.ts.map +1 -1
  89. package/lib/typescript/src/components/radio-group/index.d.ts +39 -0
  90. package/lib/typescript/src/components/radio-group/index.d.ts.map +1 -1
  91. package/lib/typescript/src/components/section-rail/index.d.ts +115 -0
  92. package/lib/typescript/src/components/section-rail/index.d.ts.map +1 -0
  93. package/lib/typescript/src/components/select/index.d.ts +46 -7
  94. package/lib/typescript/src/components/select/index.d.ts.map +1 -1
  95. package/lib/typescript/src/components/separator/index.d.ts +47 -0
  96. package/lib/typescript/src/components/separator/index.d.ts.map +1 -0
  97. package/lib/typescript/src/components/slider/index.d.ts +203 -0
  98. package/lib/typescript/src/components/slider/index.d.ts.map +1 -0
  99. package/lib/typescript/src/components/surface/index.d.ts +51 -2
  100. package/lib/typescript/src/components/surface/index.d.ts.map +1 -1
  101. package/lib/typescript/src/components/switch/index.d.ts.map +1 -1
  102. package/lib/typescript/src/components/tabs/index.d.ts +46 -4
  103. package/lib/typescript/src/components/tabs/index.d.ts.map +1 -1
  104. package/lib/typescript/src/components/toggle-button/index.d.ts +244 -0
  105. package/lib/typescript/src/components/toggle-button/index.d.ts.map +1 -0
  106. package/lib/typescript/src/hooks/index.d.ts +1 -0
  107. package/lib/typescript/src/hooks/index.d.ts.map +1 -1
  108. package/lib/typescript/src/hooks/use-keyboard-avoidance.d.ts +10 -1
  109. package/lib/typescript/src/hooks/use-keyboard-avoidance.d.ts.map +1 -1
  110. package/lib/typescript/src/hooks/use-scroll-sections.d.ts +31 -0
  111. package/lib/typescript/src/hooks/use-scroll-sections.d.ts.map +1 -0
  112. package/lib/typescript/src/icons/index.d.ts +6 -0
  113. package/lib/typescript/src/icons/index.d.ts.map +1 -1
  114. package/lib/typescript/src/index.d.ts +13 -4
  115. package/lib/typescript/src/index.d.ts.map +1 -1
  116. package/lib/typescript/src/native/index.d.ts +16 -1
  117. package/lib/typescript/src/native/index.d.ts.map +1 -1
  118. package/lib/typescript/src/primitives/keyboard-avoider.d.ts +8 -1
  119. package/lib/typescript/src/primitives/keyboard-avoider.d.ts.map +1 -1
  120. package/lib/typescript/src/primitives/scrim.d.ts +21 -0
  121. package/lib/typescript/src/primitives/scrim.d.ts.map +1 -0
  122. package/lib/typescript/src/utils/haptics.d.ts +12 -0
  123. package/lib/typescript/src/utils/haptics.d.ts.map +1 -0
  124. package/package.json +9 -1
  125. package/src/components/attachment/index.tsx +279 -0
  126. package/src/components/bottom-sheet/index.tsx +58 -2
  127. package/src/components/button/index.tsx +26 -5
  128. package/src/components/dialog/index.tsx +10 -1
  129. package/src/components/empty-state/index.tsx +60 -14
  130. package/src/components/frame/index.tsx +175 -29
  131. package/src/components/input/index.tsx +122 -16
  132. package/src/components/line-chart/index.tsx +1224 -0
  133. package/src/components/marker/index.tsx +194 -0
  134. package/src/components/message/index.tsx +29 -5
  135. package/src/components/message-scroller/index.tsx +574 -0
  136. package/src/components/popover/index.tsx +624 -0
  137. package/src/components/progress/index.tsx +81 -1
  138. package/src/components/radio-group/index.tsx +156 -18
  139. package/src/components/section-rail/index.tsx +459 -0
  140. package/src/components/select/index.tsx +265 -52
  141. package/src/components/separator/index.tsx +97 -0
  142. package/src/components/slider/index.tsx +371 -0
  143. package/src/components/surface/index.tsx +29 -3
  144. package/src/components/switch/index.tsx +8 -1
  145. package/src/components/tabs/index.tsx +208 -28
  146. package/src/components/toggle-button/index.tsx +374 -0
  147. package/src/hooks/index.ts +5 -0
  148. package/src/hooks/use-keyboard-avoidance.ts +73 -7
  149. package/src/hooks/use-scroll-sections.ts +127 -0
  150. package/src/icons/index.tsx +48 -0
  151. package/src/index.ts +97 -6
  152. package/src/native/index.ts +13 -1
  153. package/src/primitives/keyboard-avoider.tsx +9 -1
  154. package/src/primitives/scrim.tsx +95 -0
  155. package/src/utils/haptics.ts +63 -0
  156. package/theme.css +64 -2
  157. package/lib/module/components/inline-select/index.js +0 -284
  158. package/lib/module/components/inline-select/index.js.map +0 -1
  159. package/lib/typescript/src/components/inline-select/index.d.ts +0 -48
  160. package/lib/typescript/src/components/inline-select/index.d.ts.map +0 -1
  161. package/src/components/inline-select/index.tsx +0 -332
package/src/index.ts CHANGED
@@ -28,6 +28,7 @@ export {
28
28
 
29
29
  // Native UI bridge
30
30
  export { hasNativeUI } from './native';
31
+ export { hasHaptics, selectionTick } from './utils/haptics';
31
32
 
32
33
  // Components
33
34
  export {
@@ -50,6 +51,18 @@ export {
50
51
  type AvatarProps,
51
52
  type AvatarBadgeProps,
52
53
  } from './components/avatar';
54
+ export {
55
+ Attachment,
56
+ type AttachmentProps,
57
+ type AttachmentGroupProps,
58
+ type AttachmentMediaProps,
59
+ type AttachmentContentProps,
60
+ type AttachmentTitleProps,
61
+ type AttachmentDescriptionProps,
62
+ type AttachmentActionsProps,
63
+ type AttachmentActionProps,
64
+ type AttachmentState,
65
+ } from './components/attachment';
53
66
  export { Badge, type BadgeProps } from './components/badge';
54
67
  export {
55
68
  BottomSheet,
@@ -57,18 +70,47 @@ export {
57
70
  type BottomSheetContentProps,
58
71
  } from './components/bottom-sheet';
59
72
  export { Button, type ButtonProps } from './components/button';
73
+ export {
74
+ ToggleButton,
75
+ ToggleButtonGroup,
76
+ useToggleButton,
77
+ type ToggleButtonProps,
78
+ type ToggleButtonGroupProps,
79
+ type ToggleButtonLabelProps,
80
+ type ToggleButtonSize,
81
+ type ToggleButtonVariant,
82
+ type ToggleSelectionMode,
83
+ } from './components/toggle-button';
60
84
  export {
61
85
  Dialog,
62
86
  type DialogProps,
63
87
  type DialogContentProps,
64
88
  } from './components/dialog';
65
- export { Frame, type FrameProps } from './components/frame';
66
89
  export {
67
- InlineSelect,
68
- type InlineSelectProps,
69
- type InlineSelectItemProps,
70
- } from './components/inline-select';
71
- export { Select, type SelectProps, type SelectItemProps } from './components/select';
90
+ Frame,
91
+ type FrameProps,
92
+ type FrameRootProps,
93
+ type FramePanelProps,
94
+ type FrameRowProps,
95
+ type FrameSectionProps,
96
+ type FrameVariant,
97
+ } from './components/frame';
98
+ export {
99
+ Select,
100
+ type SelectProps,
101
+ type SelectItemProps,
102
+ type SelectPresentation,
103
+ } from './components/select';
104
+ export {
105
+ SectionRail,
106
+ type SectionRailProps,
107
+ type SectionRailTriggerProps,
108
+ type SectionRailBarProps,
109
+ type SectionRailContentProps,
110
+ type SectionRailItemProps,
111
+ type SectionRailPlacement,
112
+ type SectionRailAlign,
113
+ } from './components/section-rail';
72
114
  export {
73
115
  Tabs,
74
116
  type TabsProps,
@@ -104,6 +146,39 @@ export {
104
146
  type ItemFooterProps,
105
147
  } from './components/item';
106
148
  export { Label, type LabelProps, type LabelTextProps } from './components/label';
149
+ export {
150
+ LineChart,
151
+ useLineChart,
152
+ type LineChartProps,
153
+ type LineChartHandle,
154
+ type LineChartGridProps,
155
+ type LineChartLineProps,
156
+ type LineChartAreaProps,
157
+ type LineChartSkeletonProps,
158
+ type LineChartXAxisProps,
159
+ type LineChartTooltipProps,
160
+ type LineChartLegendProps,
161
+ type LineChartDatum,
162
+ type LineChartStatus,
163
+ type LineChartCurve,
164
+ } from './components/line-chart';
165
+ export {
166
+ Marker,
167
+ type MarkerProps,
168
+ type MarkerIconProps,
169
+ type MarkerContentProps,
170
+ } from './components/marker';
171
+ export {
172
+ MessageScroller,
173
+ useMessageScroller,
174
+ useMessageScrollerVisibility,
175
+ type MessageScrollerProps,
176
+ type MessageScrollerViewportProps,
177
+ type MessageScrollerContentProps,
178
+ type MessageScrollerItemProps,
179
+ type MessageScrollerButtonProps,
180
+ type MessageScrollerPosition,
181
+ } from './components/message-scroller';
107
182
  export {
108
183
  Message,
109
184
  type MessageProps,
@@ -116,6 +191,17 @@ export {
116
191
  type MessageFooterProps,
117
192
  type MessageActionsProps,
118
193
  } from './components/message';
194
+ export {
195
+ Popover,
196
+ type PopoverProps,
197
+ type PopoverTriggerProps,
198
+ type PopoverContentProps,
199
+ type PopoverArrowProps,
200
+ type PopoverCloseProps,
201
+ type PopoverPlacement,
202
+ type PopoverAlign,
203
+ type PopoverPresentation,
204
+ } from './components/popover';
119
205
  export { Progress, type ProgressProps } from './components/progress';
120
206
  export {
121
207
  RadioGroup,
@@ -126,8 +212,10 @@ export {
126
212
  ScrollFade,
127
213
  type ScrollFadeProps,
128
214
  } from './components/scroll-fade';
215
+ export { Separator, type SeparatorProps } from './components/separator';
129
216
  export { Shimmer, type ShimmerProps } from './components/shimmer';
130
217
  export { Skeleton, type SkeletonProps } from './components/skeleton';
218
+ export { Slider, type SliderProps } from './components/slider';
131
219
  export { Spinner, type SpinnerProps } from './components/spinner';
132
220
  export {
133
221
  Steps,
@@ -186,11 +274,14 @@ export {
186
274
  ChevronLeftIcon,
187
275
  ChevronRightIcon,
188
276
  FacebookIcon,
277
+ FileIcon,
189
278
  GoogleIcon,
190
279
  IconColorProvider,
191
280
  InfoIcon,
281
+ ImageIcon,
192
282
  MoonIcon,
193
283
  PackageIcon,
284
+ PaperclipIcon,
194
285
  PlusSquareIcon,
195
286
  ReceiptIcon,
196
287
  SearchIcon,
@@ -25,7 +25,18 @@ import type { ComponentType, ReactNode } from 'react';
25
25
  interface NativeUIModule {
26
26
  Host: ComponentType<{
27
27
  children?: ReactNode;
28
- matchContents?: boolean;
28
+ /**
29
+ * Whether the host resizes itself to the platform content.
30
+ *
31
+ * Every control here gives the host an explicit height instead, and takes
32
+ * its width from ordinary layout. The measurement arrives a frame late and
33
+ * again whenever the platform's content changes, so a host left to work
34
+ * its own size out renders at nothing and then jumps — on first paint, and
35
+ * again on first press. The per-axis form does not help; the answer is not
36
+ * to ask. Only the natively-presented sheet still uses it, where the host
37
+ * is a zero-size anchor rather than the control itself.
38
+ */
39
+ matchContents?: boolean | { vertical?: boolean; horizontal?: boolean };
29
40
  style?: unknown;
30
41
  [key: string]: unknown;
31
42
  }>;
@@ -41,6 +52,7 @@ interface NativeUIModule {
41
52
  }>;
42
53
  Button: ComponentType<Record<string, unknown>>;
43
54
  Switch: ComponentType<Record<string, unknown>>;
55
+ Slider: ComponentType<Record<string, unknown>>;
44
56
  Picker: ComponentType<Record<string, unknown>> & {
45
57
  Item: ComponentType<Record<string, unknown>>;
46
58
  };
@@ -14,6 +14,13 @@ export interface KeyboardAvoiderProps extends ViewProps {
14
14
  className?: string;
15
15
  /** Set false to leave the content where it is. */
16
16
  enabled?: boolean;
17
+ /**
18
+ * Whether this is the element that should get out of the keyboard's way.
19
+ * Defaults to true, which is right for a composer or a toolbar. Pass a
20
+ * field's focus state when wrapping a single field, so the others on the
21
+ * screen stay put.
22
+ */
23
+ active?: boolean;
17
24
  /** Gap to keep between the content's bottom edge and the keyboard. */
18
25
  offset?: number;
19
26
  children?: ReactNode;
@@ -22,13 +29,14 @@ export interface KeyboardAvoiderProps extends ViewProps {
22
29
  export function KeyboardAvoider({
23
30
  className,
24
31
  enabled = true,
32
+ active = true,
25
33
  offset = 16,
26
34
  children,
27
35
  style,
28
36
  onLayout,
29
37
  ...props
30
38
  }: KeyboardAvoiderProps) {
31
- const avoidance = useKeyboardAvoidance({ enabled, offset });
39
+ const avoidance = useKeyboardAvoidance({ enabled, active, offset });
32
40
 
33
41
  return (
34
42
  <Animated.View
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Scrim — the layer behind an overlay.
3
+ *
4
+ * An overlay needs the screen behind it to recede. There are two honest ways to
5
+ * do that: dim it, or blur it. Dimming is free and always works; blurring reads
6
+ * as more physical — the content behind stays legible as shape and colour while
7
+ * losing its detail — but it needs a native view to do it.
8
+ *
9
+ * `expo-blur` is resolved lazily and only when `blur` is asked for, so a project
10
+ * that never blurs anything installs nothing. When it is asked for and the
11
+ * package is missing, this falls back to a dim rather than failing: a blur you
12
+ * cannot draw is better shown as a darkened screen than as a crash.
13
+ *
14
+ * It fills its parent and does not intercept touches itself — the overlay layers
15
+ * its own dismiss `Pressable` over it — so it is purely the visual backdrop.
16
+ */
17
+ import { type ComponentType } from 'react';
18
+ import { StyleSheet, View, type ViewProps } from 'react-native';
19
+ import Animated, { FadeIn, FadeOut } from 'react-native-reanimated';
20
+
21
+ interface BlurViewProps {
22
+ intensity?: number;
23
+ tint?: 'light' | 'dark' | 'default' | 'systemMaterial';
24
+ style?: unknown;
25
+ children?: React.ReactNode;
26
+ }
27
+
28
+ /**
29
+ * `expo-blur`'s BlurView, or null when it is not installed. Resolved once at
30
+ * module load — the require is cheap and caching it avoids a try/catch on every
31
+ * render.
32
+ */
33
+ const BlurView: ComponentType<BlurViewProps> | null = (() => {
34
+ try {
35
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
36
+ const mod = require('expo-blur');
37
+ return (mod?.BlurView as ComponentType<BlurViewProps>) ?? null;
38
+ } catch {
39
+ return null;
40
+ }
41
+ })();
42
+
43
+ /** True when a real blur can be drawn — for a caller that wants to know. */
44
+ export const hasBlur = BlurView !== null;
45
+
46
+ export interface ScrimProps extends Omit<ViewProps, 'children'> {
47
+ /** Frost the backdrop instead of dimming it. Falls back to a dim if it can't. */
48
+ blur?: boolean;
49
+ /** Blur strength, 0–100. */
50
+ intensity?: number;
51
+ /** Which way the blur tints. */
52
+ tint?: 'light' | 'dark' | 'default' | 'systemMaterial';
53
+ /**
54
+ * The dim used when not blurring — and when blurring is asked for but
55
+ * unavailable. A popover passes a lighter one than a dialog.
56
+ */
57
+ dimClassName?: string;
58
+ }
59
+
60
+ export function Scrim({
61
+ blur = false,
62
+ intensity = 24,
63
+ tint = 'default',
64
+ dimClassName = 'bg-black/50',
65
+ style,
66
+ ...props
67
+ }: ScrimProps) {
68
+ if (blur && BlurView) {
69
+ return (
70
+ <Animated.View
71
+ entering={FadeIn.duration(180)}
72
+ exiting={FadeOut.duration(150)}
73
+ style={[StyleSheet.absoluteFill, style]}
74
+ {...props}
75
+ >
76
+ {/* A faint dim under the blur so the frost has something to sit on —
77
+ a pure blur over a dark scene is nearly invisible. */}
78
+ <BlurView intensity={intensity} tint={tint} style={StyleSheet.absoluteFill} />
79
+ <View className="absolute inset-0 bg-black/10" />
80
+ </Animated.View>
81
+ );
82
+ }
83
+
84
+ return (
85
+ <Animated.View
86
+ entering={FadeIn.duration(150)}
87
+ exiting={FadeOut.duration(150)}
88
+ style={[StyleSheet.absoluteFill, style]}
89
+ className={dimClassName}
90
+ {...props}
91
+ />
92
+ );
93
+ }
94
+
95
+ Scrim.displayName = 'Scrim';
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Optional bridge to the platform's haptic engine.
3
+ *
4
+ * A tick under the finger is the difference between a control that responds
5
+ * and one that merely updates — but it is also the kind of thing that has to
6
+ * be free to ignore. `expo-haptics` is resolved lazily and every caller falls
7
+ * back to doing nothing, so a component with `haptics` set is silent rather
8
+ * than broken when the package is absent.
9
+ *
10
+ * ```sh
11
+ * npx expo install expo-haptics
12
+ * ```
13
+ */
14
+ import { Platform } from 'react-native';
15
+
16
+ interface HapticsModule {
17
+ selectionAsync: () => Promise<void>;
18
+ impactAsync: (style?: unknown) => Promise<void>;
19
+ ImpactFeedbackStyle?: Record<string, unknown>;
20
+ }
21
+
22
+ let resolved = false;
23
+ let module: HapticsModule | null = null;
24
+
25
+ /**
26
+ * The haptics module, or null when it is not installed or the platform has no
27
+ * engine behind it. Resolved once and cached — a failed require is not retried
28
+ * on every keystroke.
29
+ */
30
+ function getHaptics(): HapticsModule | null {
31
+ if (resolved) return module;
32
+ resolved = true;
33
+
34
+ // Web has no haptic engine; the Vibration API is a different thing and a
35
+ // worse one — a buzz where a tick was meant.
36
+ if (Platform.OS === 'web') return null;
37
+
38
+ try {
39
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
40
+ module = require('expo-haptics') as HapticsModule;
41
+ } catch {
42
+ module = null;
43
+ }
44
+
45
+ return module;
46
+ }
47
+
48
+ /** Whether a haptic will actually be felt. */
49
+ export function hasHaptics(): boolean {
50
+ return getHaptics() !== null;
51
+ }
52
+
53
+ /**
54
+ * The light tick for moving between options — a segmented control, a picker,
55
+ * a section marker. Deliberately not an impact: selection is the lightest of
56
+ * the feedbacks, and anything heavier gets tiring when it fires on scroll.
57
+ *
58
+ * Fire-and-forget. A rejected haptic is not worth an unhandled rejection, and
59
+ * there is nothing useful to do about one.
60
+ */
61
+ export function selectionTick(): void {
62
+ void getHaptics()?.selectionAsync().catch(() => {});
63
+ }
package/theme.css CHANGED
@@ -67,6 +67,22 @@
67
67
  --color-destructive-soft: unset;
68
68
  --color-destructive-subtle: unset;
69
69
 
70
+ /*
71
+ * Chart series. A separate ramp from the status colours because they answer
72
+ * a different question: a status colour means something (this failed, this
73
+ * is fine), a series colour only has to be told apart from the other four.
74
+ * Ordered by prominence — chart-1 is the series the chart is about, and each
75
+ * theme starts it on that family's own accent.
76
+ *
77
+ * These are the tokens to override in your app's global.css, after the
78
+ * `panelui-native/theme.css` import, to put a chart on brand.
79
+ */
80
+ --color-chart-1: unset;
81
+ --color-chart-2: unset;
82
+ --color-chart-3: unset;
83
+ --color-chart-4: unset;
84
+ --color-chart-5: unset;
85
+
70
86
  /*
71
87
  * Radius scale. Themed rather than static: a theme is a shape as well as a
72
88
  * palette, so each family sets its own corner rounding. --radius-3xl is
@@ -143,6 +159,13 @@
143
159
  --color-warning-subtle: rgba(245, 158, 11, 0.08);
144
160
  --color-destructive-soft: rgba(239, 68, 68, 0.1);
145
161
  --color-destructive-subtle: rgba(239, 68, 68, 0.08);
162
+
163
+ /* chart series, in order of prominence */
164
+ --color-chart-1: #262626;
165
+ --color-chart-2: #3b82f6;
166
+ --color-chart-3: #10b981;
167
+ --color-chart-4: #f59e0b;
168
+ --color-chart-5: #8b5cf6;
146
169
 
147
170
  /* Panel shape scale */
148
171
  --radius-xs: 4px;
@@ -176,8 +199,12 @@
176
199
  --color-secondary-foreground: #f5f5f5;
177
200
 
178
201
  --color-muted: rgba(255, 255, 255, 0.08);
179
- /* color-mix(neutral-500 90%, white) */
180
- --color-muted-foreground: #818181;
202
+ /*
203
+ * color-mix(neutral-500 86%, white). Was 90% (#818181), which came out
204
+ * at 4.42:1 on the popover surface — under the 4.5:1 floor for body
205
+ * text. Every other theme's muted foreground already clears it.
206
+ */
207
+ --color-muted-foreground: #878787;
181
208
 
182
209
  --color-accent: rgba(255, 255, 255, 0.08);
183
210
  --color-accent-foreground: #f5f5f5;
@@ -216,6 +243,13 @@
216
243
  --color-warning-subtle: rgba(245, 158, 11, 0.16);
217
244
  --color-destructive-soft: rgba(241, 87, 87, 0.06);
218
245
  --color-destructive-subtle: rgba(241, 87, 87, 0.16);
246
+
247
+ /* chart series, in order of prominence */
248
+ --color-chart-1: #fafafa;
249
+ --color-chart-2: #60a5fa;
250
+ --color-chart-3: #34d399;
251
+ --color-chart-4: #fbbf24;
252
+ --color-chart-5: #a78bfa;
219
253
 
220
254
  /* Panel shape scale */
221
255
  --radius-xs: 4px;
@@ -298,6 +332,13 @@
298
332
  --color-warning-subtle: rgba(245, 166, 35, 0.08);
299
333
  --color-destructive-soft: rgba(229, 72, 77, 0.1);
300
334
  --color-destructive-subtle: rgba(229, 72, 77, 0.08);
335
+
336
+ /* chart series, in order of prominence */
337
+ --color-chart-1: #0070f3;
338
+ --color-chart-2: #171717;
339
+ --color-chart-3: #7928ca;
340
+ --color-chart-4: #f5a623;
341
+ --color-chart-5: #0a9396;
301
342
 
302
343
  /* Moon shape scale */
303
344
  --radius-xs: 2px;
@@ -360,6 +401,13 @@
360
401
  --color-warning-subtle: rgba(245, 166, 35, 0.16);
361
402
  --color-destructive-soft: rgba(229, 72, 77, 0.1);
362
403
  --color-destructive-subtle: rgba(229, 72, 77, 0.16);
404
+
405
+ /* chart series, in order of prominence */
406
+ --color-chart-1: #3291ff;
407
+ --color-chart-2: #ededed;
408
+ --color-chart-3: #a855f7;
409
+ --color-chart-4: #fbbf24;
410
+ --color-chart-5: #2dd4bf;
363
411
 
364
412
  /* Moon shape scale */
365
413
  --radius-xs: 2px;
@@ -425,6 +473,13 @@
425
473
  --color-warning-subtle: rgba(245, 158, 11, 0.08);
426
474
  --color-destructive-soft: rgba(239, 68, 68, 0.1);
427
475
  --color-destructive-subtle: rgba(239, 68, 68, 0.08);
476
+
477
+ /* chart series, in order of prominence */
478
+ --color-chart-1: #24b47e;
479
+ --color-chart-2: #1f2937;
480
+ --color-chart-3: #2563eb;
481
+ --color-chart-4: #f59e0b;
482
+ --color-chart-5: #db2777;
428
483
 
429
484
  /* Grass shape scale */
430
485
  --radius-xs: 6px;
@@ -487,6 +542,13 @@
487
542
  --color-warning-subtle: rgba(245, 158, 11, 0.16);
488
543
  --color-destructive-soft: rgba(241, 87, 87, 0.08);
489
544
  --color-destructive-subtle: rgba(241, 87, 87, 0.16);
545
+
546
+ /* chart series, in order of prominence */
547
+ --color-chart-1: #3ecf8e;
548
+ --color-chart-2: #ededed;
549
+ --color-chart-3: #60a5fa;
550
+ --color-chart-4: #fbbf24;
551
+ --color-chart-5: #f472b6;
490
552
 
491
553
  /* Grass shape scale */
492
554
  --radius-xs: 6px;