panelui-native 0.75.0 → 0.76.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 (155) hide show
  1. package/lib/module/components/ai-input/ai-input-growth.js +62 -0
  2. package/lib/module/components/ai-input/ai-input-growth.js.map +1 -0
  3. package/lib/module/components/ai-input/index.js +1568 -0
  4. package/lib/module/components/ai-input/index.js.map +1 -0
  5. package/lib/module/components/alert/index.js +1 -1
  6. package/lib/module/components/alert/index.js.map +1 -1
  7. package/lib/module/components/avatar/avatar-source.js +15 -0
  8. package/lib/module/components/avatar/avatar-source.js.map +1 -0
  9. package/lib/module/components/avatar/index.js +32 -31
  10. package/lib/module/components/avatar/index.js.map +1 -1
  11. package/lib/module/components/bottom-sheet/index.js +3 -2
  12. package/lib/module/components/bottom-sheet/index.js.map +1 -1
  13. package/lib/module/components/button-group/index.js +2 -3
  14. package/lib/module/components/button-group/index.js.map +1 -1
  15. package/lib/module/components/carousel/index.js +10 -7
  16. package/lib/module/components/carousel/index.js.map +1 -1
  17. package/lib/module/components/collapsible/index.js +1 -1
  18. package/lib/module/components/collapsible/index.js.map +1 -1
  19. package/lib/module/components/drawer/index.js +18 -14
  20. package/lib/module/components/drawer/index.js.map +1 -1
  21. package/lib/module/components/field/index.js +7 -8
  22. package/lib/module/components/field/index.js.map +1 -1
  23. package/lib/module/components/label/index.js +1 -1
  24. package/lib/module/components/label/index.js.map +1 -1
  25. package/lib/module/components/marquee/index.js +36 -11
  26. package/lib/module/components/marquee/index.js.map +1 -1
  27. package/lib/module/components/marquee/marquee-math.js +22 -6
  28. package/lib/module/components/marquee/marquee-math.js.map +1 -1
  29. package/lib/module/components/plan/index.js +2 -1
  30. package/lib/module/components/plan/index.js.map +1 -1
  31. package/lib/module/components/plot/index.js +27 -9
  32. package/lib/module/components/plot/index.js.map +1 -1
  33. package/lib/module/components/search-bar/index.js +11 -3
  34. package/lib/module/components/search-bar/index.js.map +1 -1
  35. package/lib/module/components/split-view/index.js +26 -27
  36. package/lib/module/components/split-view/index.js.map +1 -1
  37. package/lib/module/components/split-view/split-view-lifecycle.js +32 -0
  38. package/lib/module/components/split-view/split-view-lifecycle.js.map +1 -0
  39. package/lib/module/components/splitter/index.js +7 -13
  40. package/lib/module/components/splitter/index.js.map +1 -1
  41. package/lib/module/components/splitter/splitter-math.js +14 -0
  42. package/lib/module/components/splitter/splitter-math.js.map +1 -1
  43. package/lib/module/components/tabs/index.js +1 -1
  44. package/lib/module/components/tabs/index.js.map +1 -1
  45. package/lib/module/components/timeline/index.js +156 -42
  46. package/lib/module/components/timeline/index.js.map +1 -1
  47. package/lib/module/components/timeline/timeline-geometry.js +11 -0
  48. package/lib/module/components/timeline/timeline-geometry.js.map +1 -1
  49. package/lib/module/components/toast/index.js +1 -1
  50. package/lib/module/components/toast/index.js.map +1 -1
  51. package/lib/module/components/tooltip/index.js +2 -2
  52. package/lib/module/components/tooltip/index.js.map +1 -1
  53. package/lib/module/components/tree/index.js +2 -2
  54. package/lib/module/components/tree/index.js.map +1 -1
  55. package/lib/module/components/typography/index.js +2 -1
  56. package/lib/module/components/typography/index.js.map +1 -1
  57. package/lib/module/components/waterfall-chart/index.js +22 -3
  58. package/lib/module/components/waterfall-chart/index.js.map +1 -1
  59. package/lib/module/hooks/use-disclosure.js +10 -8
  60. package/lib/module/hooks/use-disclosure.js.map +1 -1
  61. package/lib/module/icons/index.js +135 -0
  62. package/lib/module/icons/index.js.map +1 -1
  63. package/lib/module/index.js +5 -1
  64. package/lib/module/index.js.map +1 -1
  65. package/lib/module/primitives/controllable-state.js +61 -0
  66. package/lib/module/primitives/controllable-state.js.map +1 -0
  67. package/lib/module/primitives/finite-chart.js +15 -0
  68. package/lib/module/primitives/finite-chart.js.map +1 -0
  69. package/lib/module/primitives/glass.js +136 -0
  70. package/lib/module/primitives/glass.js.map +1 -0
  71. package/lib/module/primitives/scrim.js +8 -1
  72. package/lib/module/primitives/scrim.js.map +1 -1
  73. package/lib/typescript/src/components/ai-input/ai-input-growth.d.ts +59 -0
  74. package/lib/typescript/src/components/ai-input/ai-input-growth.d.ts.map +1 -0
  75. package/lib/typescript/src/components/ai-input/index.d.ts +423 -0
  76. package/lib/typescript/src/components/ai-input/index.d.ts.map +1 -0
  77. package/lib/typescript/src/components/avatar/avatar-source.d.ts +4 -0
  78. package/lib/typescript/src/components/avatar/avatar-source.d.ts.map +1 -0
  79. package/lib/typescript/src/components/avatar/index.d.ts.map +1 -1
  80. package/lib/typescript/src/components/bottom-sheet/index.d.ts +10 -1
  81. package/lib/typescript/src/components/bottom-sheet/index.d.ts.map +1 -1
  82. package/lib/typescript/src/components/carousel/index.d.ts.map +1 -1
  83. package/lib/typescript/src/components/drawer/index.d.ts.map +1 -1
  84. package/lib/typescript/src/components/marquee/index.d.ts +15 -6
  85. package/lib/typescript/src/components/marquee/index.d.ts.map +1 -1
  86. package/lib/typescript/src/components/marquee/marquee-math.d.ts +6 -0
  87. package/lib/typescript/src/components/marquee/marquee-math.d.ts.map +1 -1
  88. package/lib/typescript/src/components/plan/index.d.ts.map +1 -1
  89. package/lib/typescript/src/components/plot/index.d.ts +2 -1
  90. package/lib/typescript/src/components/plot/index.d.ts.map +1 -1
  91. package/lib/typescript/src/components/search-bar/index.d.ts.map +1 -1
  92. package/lib/typescript/src/components/split-view/index.d.ts.map +1 -1
  93. package/lib/typescript/src/components/split-view/split-view-lifecycle.d.ts +14 -0
  94. package/lib/typescript/src/components/split-view/split-view-lifecycle.d.ts.map +1 -0
  95. package/lib/typescript/src/components/splitter/index.d.ts +2 -2
  96. package/lib/typescript/src/components/splitter/index.d.ts.map +1 -1
  97. package/lib/typescript/src/components/splitter/splitter-math.d.ts +2 -0
  98. package/lib/typescript/src/components/splitter/splitter-math.d.ts.map +1 -1
  99. package/lib/typescript/src/components/timeline/index.d.ts +24 -2
  100. package/lib/typescript/src/components/timeline/index.d.ts.map +1 -1
  101. package/lib/typescript/src/components/timeline/timeline-geometry.d.ts +2 -0
  102. package/lib/typescript/src/components/timeline/timeline-geometry.d.ts.map +1 -1
  103. package/lib/typescript/src/components/toast/index.d.ts.map +1 -1
  104. package/lib/typescript/src/components/waterfall-chart/index.d.ts +2 -1
  105. package/lib/typescript/src/components/waterfall-chart/index.d.ts.map +1 -1
  106. package/lib/typescript/src/hooks/use-disclosure.d.ts.map +1 -1
  107. package/lib/typescript/src/icons/index.d.ts +18 -0
  108. package/lib/typescript/src/icons/index.d.ts.map +1 -1
  109. package/lib/typescript/src/index.d.ts +3 -1
  110. package/lib/typescript/src/index.d.ts.map +1 -1
  111. package/lib/typescript/src/primitives/controllable-state.d.ts +33 -0
  112. package/lib/typescript/src/primitives/controllable-state.d.ts.map +1 -0
  113. package/lib/typescript/src/primitives/finite-chart.d.ts +5 -0
  114. package/lib/typescript/src/primitives/finite-chart.d.ts.map +1 -0
  115. package/lib/typescript/src/primitives/glass.d.ts +43 -0
  116. package/lib/typescript/src/primitives/glass.d.ts.map +1 -0
  117. package/lib/typescript/src/primitives/scrim.d.ts +7 -0
  118. package/lib/typescript/src/primitives/scrim.d.ts.map +1 -1
  119. package/package.json +7 -2
  120. package/src/components/ai-input/ai-input-growth.ts +65 -0
  121. package/src/components/ai-input/index.tsx +1937 -0
  122. package/src/components/alert/index.tsx +1 -1
  123. package/src/components/avatar/avatar-source.ts +18 -0
  124. package/src/components/avatar/index.tsx +35 -27
  125. package/src/components/bottom-sheet/index.tsx +15 -3
  126. package/src/components/button-group/index.tsx +1 -1
  127. package/src/components/carousel/index.tsx +10 -11
  128. package/src/components/collapsible/index.tsx +1 -1
  129. package/src/components/drawer/index.tsx +24 -20
  130. package/src/components/field/index.tsx +4 -4
  131. package/src/components/label/index.tsx +1 -1
  132. package/src/components/marquee/index.tsx +56 -12
  133. package/src/components/marquee/marquee-math.ts +26 -3
  134. package/src/components/plan/index.tsx +6 -2
  135. package/src/components/plot/index.tsx +37 -11
  136. package/src/components/search-bar/index.tsx +9 -3
  137. package/src/components/split-view/index.tsx +24 -33
  138. package/src/components/split-view/split-view-lifecycle.ts +43 -0
  139. package/src/components/splitter/index.tsx +7 -11
  140. package/src/components/splitter/splitter-math.ts +28 -0
  141. package/src/components/tabs/index.tsx +1 -1
  142. package/src/components/timeline/index.tsx +189 -34
  143. package/src/components/timeline/timeline-geometry.ts +11 -0
  144. package/src/components/toast/index.tsx +6 -1
  145. package/src/components/tooltip/index.tsx +1 -1
  146. package/src/components/tree/index.tsx +2 -2
  147. package/src/components/typography/index.tsx +2 -2
  148. package/src/components/waterfall-chart/index.tsx +37 -4
  149. package/src/hooks/use-disclosure.ts +7 -12
  150. package/src/icons/index.tsx +106 -0
  151. package/src/index.ts +27 -0
  152. package/src/primitives/controllable-state.ts +112 -0
  153. package/src/primitives/finite-chart.ts +14 -0
  154. package/src/primitives/glass.tsx +171 -0
  155. package/src/primitives/scrim.tsx +7 -1
package/src/index.ts CHANGED
@@ -20,6 +20,9 @@ export { Portal, PortalHost, PortalProvider } from './primitives/portal';
20
20
  // recede. Public so a custom overlay gets the same backdrop — and the same
21
21
  // graceful fall back from blur to dim — as the built-in ones.
22
22
  export { Scrim, hasBlur, type ScrimProps } from './primitives/scrim';
23
+ // The material iOS draws its floating controls in, with a solid fallback where
24
+ // there is none. Public so a surface of your own gets the same treatment.
25
+ export { Glass, hasGlass, type GlassProps, type GlassVariant, type GlassRadius } from './primitives/glass';
23
26
  export { Text, type TextProps } from './primitives/text';
24
27
  export { Collapse, type CollapseProps } from './primitives/collapse';
25
28
  export {
@@ -935,6 +938,25 @@ export {
935
938
  type SignatureToolbarProps,
936
939
  type SignatureButtonProps,
937
940
  } from './components/signature';
941
+ export {
942
+ AIInput,
943
+ type AIInputProps,
944
+ type AIInputStatus,
945
+ type AIInputFieldProps,
946
+ type AIInputRowProps,
947
+ type AIInputToolbarProps,
948
+ type AIInputActionProps,
949
+ type AIInputPillProps,
950
+ type AIInputSubmitProps,
951
+ type AIInputRecordingProps,
952
+ type AIInputSheetProps,
953
+ type AIInputSheetScreenProps,
954
+ type AIInputSheetGroupProps,
955
+ type AIInputSheetRowProps,
956
+ type AIInputSheetToggleProps,
957
+ type AIInputSheetChoiceProps,
958
+ type AIInputVoiceModeProps,
959
+ } from './components/ai-input';
938
960
  export { Shimmer, type ShimmerProps } from './components/shimmer';
939
961
  export {
940
962
  Reasoning,
@@ -1190,17 +1212,20 @@ export {
1190
1212
  ArrowDownIcon,
1191
1213
  ArrowUpIcon,
1192
1214
  ArrowUpRightIcon,
1215
+ AudioLinesIcon,
1193
1216
  BadgeCheckIcon,
1194
1217
  BellIcon,
1195
1218
  BookmarkIcon,
1196
1219
  BoldIcon,
1197
1220
  CalendarIcon,
1221
+ CameraIcon,
1198
1222
  CardIcon,
1199
1223
  CheckCircleIcon,
1200
1224
  CheckIcon,
1201
1225
  ChevronDownIcon,
1202
1226
  ChevronLeftIcon,
1203
1227
  ChevronRightIcon,
1228
+ ChevronsUpDownIcon,
1204
1229
  ChevronUpIcon,
1205
1230
  CircleIcon,
1206
1231
  CodeIcon,
@@ -1215,6 +1240,7 @@ export {
1215
1240
  FileIcon,
1216
1241
  FolderIcon,
1217
1242
  FolderOpenIcon,
1243
+ GlobeIcon,
1218
1244
  GoogleIcon,
1219
1245
  HeartIcon,
1220
1246
  HeadingIcon,
@@ -1247,6 +1273,7 @@ export {
1247
1273
  RotateCcwIcon,
1248
1274
  RotateCwIcon,
1249
1275
  SearchIcon,
1276
+ SendArrowIcon,
1250
1277
  SendIcon,
1251
1278
  ShareNodesIcon,
1252
1279
  ShieldAlertIcon,
@@ -0,0 +1,112 @@
1
+ import {
2
+ useCallback,
3
+ useLayoutEffect,
4
+ useMemo,
5
+ useRef,
6
+ useState,
7
+ type Dispatch,
8
+ type SetStateAction,
9
+ } from 'react';
10
+
11
+ const identity = <T,>(value: T): T => value;
12
+ const objectIs = <T,>(left: T, right: T): boolean => Object.is(left, right);
13
+
14
+ export interface ControllableStateOptions<T> {
15
+ /** Accepted owner state. `undefined` selects uncontrolled ownership. */
16
+ value: T | undefined;
17
+ /** Initial state used only while uncontrolled. */
18
+ defaultValue: T;
19
+ /** Reports a distinct requested value exactly once per setter invocation. */
20
+ onChange?: (value: T) => void;
21
+ /** Reports accepted state changes, whether requested or externally reset. */
22
+ onSettled?: (value: T) => void;
23
+ /** Normalize defaults, controlled values, and requests through one boundary. */
24
+ normalize?: (value: T) => T;
25
+ /** Equality used for no-op requests and accepted settlement. */
26
+ isEqual?: (left: T, right: T) => boolean;
27
+ /** Stable semantic identity for controlled objects recreated by their owner. */
28
+ getValueKey?: (value: T) => unknown;
29
+ }
30
+
31
+ export interface ControllableStateResult<T> {
32
+ /** State accepted by the current owner; controlled requests never render optimistically. */
33
+ value: T;
34
+ setValue: Dispatch<SetStateAction<T>>;
35
+ isControlled: boolean;
36
+ }
37
+
38
+ /**
39
+ * Owner-driven controlled/uncontrolled state with an explicit settlement edge.
40
+ *
41
+ * Switching from controlled to uncontrolled retains the last accepted owner
42
+ * value instead of resurrecting a stale default. Switching into controlled
43
+ * mode adopts the prop immediately. `undefined` is the ownership sentinel and
44
+ * therefore cannot itself be a controlled value; use `null` for empty state.
45
+ */
46
+ export function useControllableState<T>({
47
+ value: valueProp,
48
+ defaultValue,
49
+ onChange,
50
+ onSettled,
51
+ normalize = identity,
52
+ isEqual = objectIs,
53
+ getValueKey,
54
+ }: ControllableStateOptions<T>): ControllableStateResult<T> {
55
+ const [internalValue, setInternalValue] = useState(() =>
56
+ normalize(defaultValue)
57
+ );
58
+ const isControlled = valueProp !== undefined;
59
+ const controlledKey = isControlled
60
+ ? getValueKey
61
+ ? getValueKey(valueProp as T)
62
+ : valueProp
63
+ : undefined;
64
+ const controlledValue = useMemo(
65
+ () => (isControlled ? normalize(valueProp as T) : undefined),
66
+ // The semantic key deliberately replaces object identity for equivalent owner values.
67
+ // eslint-disable-next-line react-hooks/exhaustive-deps
68
+ [controlledKey, isControlled, normalize]
69
+ );
70
+ const wasControlled = useRef(isControlled);
71
+ const lastControlledValue = useRef<T | undefined>(controlledValue);
72
+ if (isControlled) lastControlledValue.current = controlledValue;
73
+
74
+ const leavingControlled = wasControlled.current && !isControlled;
75
+ const value = isControlled
76
+ ? (controlledValue as T)
77
+ : leavingControlled
78
+ ? (lastControlledValue.current as T)
79
+ : internalValue;
80
+ const valueRef = useRef(value);
81
+ valueRef.current = value;
82
+
83
+ useLayoutEffect(() => {
84
+ if (wasControlled.current && !isControlled) {
85
+ setInternalValue(lastControlledValue.current as T);
86
+ }
87
+ wasControlled.current = isControlled;
88
+ }, [isControlled]);
89
+
90
+ const previousSettled = useRef(value);
91
+ useLayoutEffect(() => {
92
+ if (isEqual(previousSettled.current, value)) return;
93
+ previousSettled.current = value;
94
+ onSettled?.(value);
95
+ }, [isEqual, onSettled, value]);
96
+
97
+ const setValue = useCallback<Dispatch<SetStateAction<T>>>(
98
+ (next) => {
99
+ const requested = normalize(
100
+ typeof next === 'function'
101
+ ? (next as (current: T) => T)(valueRef.current)
102
+ : next
103
+ );
104
+ if (isEqual(valueRef.current, requested)) return;
105
+ if (!isControlled) setInternalValue(requested);
106
+ onChange?.(requested);
107
+ },
108
+ [isControlled, isEqual, normalize, onChange]
109
+ );
110
+
111
+ return { value, setValue, isControlled };
112
+ }
@@ -0,0 +1,14 @@
1
+ /** Returns a number only when it is safe to use in chart arithmetic. */
2
+ export function finiteChartNumber(value: unknown): number | undefined {
3
+ return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
4
+ }
5
+
6
+ /** Accepts an explicit domain only when both ends are finite. */
7
+ export function finiteChartDomain(
8
+ domain: readonly [number, number] | undefined
9
+ ): [number, number] | undefined {
10
+ if (!domain) return undefined;
11
+ const low = finiteChartNumber(domain[0]);
12
+ const high = finiteChartNumber(domain[1]);
13
+ return low === undefined || high === undefined ? undefined : [low, high];
14
+ }
@@ -0,0 +1,171 @@
1
+ /**
2
+ * Glass — the material iOS draws its own floating controls in.
3
+ *
4
+ * A control that floats over content needs to read as *over* it, and a flat
5
+ * fill cannot say that: it either hides what is behind it or disappears into
6
+ * it. The system material does both jobs at once — it refracts what is behind,
7
+ * lifts its own edge, and stays legible over anything.
8
+ *
9
+ * ```tsx
10
+ * <Glass radius={28} className="px-4 py-3">
11
+ * <Text>Over whatever is behind it.</Text>
12
+ * </Glass>
13
+ * ```
14
+ *
15
+ * ## Where it is real, and where it is not
16
+ *
17
+ * The material exists on iOS 26 and above. Below that, on Android, on web, and
18
+ * for anyone who has switched Reduce Transparency on, this draws
19
+ * `fallbackClassName` instead — a solid token surface. That is deliberate
20
+ * rather than a gap: a hand-drawn approximation of a system material is a
21
+ * near-miss on the one platform that has the real thing, and on Android it is
22
+ * an iOS look pasted onto a platform that never asked for it.
23
+ *
24
+ * So `Glass` is not a promise that the screen will be glass. It is a promise
25
+ * that the container will be drawn correctly either way, and callers should
26
+ * pick a `fallbackClassName` that stands on its own.
27
+ *
28
+ * ## The material is a layer, not the box
29
+ *
30
+ * The native view is rendered as a non-interactive layer filling this
31
+ * container, with the content above it, for the same reason `Scrim` layers a
32
+ * blur: it keeps every layout class, every token and every touch target on an
33
+ * ordinary `View` that behaves the way the rest of the library does.
34
+ *
35
+ * `radius` is a number rather than a class because the material has to round
36
+ * its own corners — clipping a square one to a rounded parent throws away the
37
+ * lit edge that makes it read as glass.
38
+ *
39
+ * ## Do not fade it
40
+ *
41
+ * Setting `opacity` to `0` on the material or on anything above it stops it
42
+ * rendering at all, and it does not come back when the opacity does. Move it,
43
+ * or unmount it; never animate it out.
44
+ */
45
+ import type { ComponentType, ReactNode } from 'react';
46
+ import { Platform, StyleSheet, View, type StyleProp, type ViewProps, type ViewStyle } from 'react-native';
47
+ import { cn } from '../utils/cn';
48
+ import { useReduceTransparency } from './scrim';
49
+
50
+ /** How the material treats what is behind it. */
51
+ export type GlassVariant = 'regular' | 'clear';
52
+
53
+ /**
54
+ * Corner radius in points — one number for all four, or a top and a bottom.
55
+ *
56
+ * The two-sided form is for a surface with an edge that is not a real edge: a
57
+ * sheet docked to the bottom of the screen rounds its top and leaves its
58
+ * bottom square, because the screen edge is where it ends.
59
+ */
60
+ export type GlassRadius = number | { top?: number; bottom?: number };
61
+
62
+ function shapeOf(radius: GlassRadius | undefined) {
63
+ if (radius === undefined) return null;
64
+ if (typeof radius === 'number') return { borderRadius: radius };
65
+ return {
66
+ borderTopLeftRadius: radius.top ?? 0,
67
+ borderTopRightRadius: radius.top ?? 0,
68
+ borderBottomLeftRadius: radius.bottom ?? 0,
69
+ borderBottomRightRadius: radius.bottom ?? 0,
70
+ };
71
+ }
72
+
73
+ interface GlassViewProps {
74
+ glassEffectStyle?: GlassVariant | 'none';
75
+ tintColor?: string;
76
+ colorScheme?: 'auto' | 'light' | 'dark';
77
+ style?: StyleProp<ViewStyle>;
78
+ pointerEvents?: ViewProps['pointerEvents'];
79
+ children?: ReactNode;
80
+ }
81
+
82
+ /**
83
+ * `expo-glass-effect`'s GlassView, or null when the material cannot be drawn
84
+ * here.
85
+ *
86
+ * Resolved once at module load, behind three gates that all have to pass. The
87
+ * package is optional, so the require can fail; the API is missing from some
88
+ * iOS 26 builds, and reaching for it there crashes; and the design itself is
89
+ * only present when the app was compiled for it. Asking all three once is
90
+ * cheaper than a try/catch on every render, and there is no answer that can
91
+ * change while the process is alive.
92
+ */
93
+ const GlassView: ComponentType<GlassViewProps> | null = (() => {
94
+ if (Platform.OS !== 'ios') return null;
95
+ try {
96
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
97
+ const mod = require('expo-glass-effect');
98
+ if (typeof mod?.isGlassEffectAPIAvailable === 'function' && !mod.isGlassEffectAPIAvailable()) {
99
+ return null;
100
+ }
101
+ if (typeof mod?.isLiquidGlassAvailable === 'function' && !mod.isLiquidGlassAvailable()) {
102
+ return null;
103
+ }
104
+ return (mod?.GlassView as ComponentType<GlassViewProps>) ?? null;
105
+ } catch {
106
+ return null;
107
+ }
108
+ })();
109
+
110
+ /**
111
+ * True when the real material can be drawn — for a caller that wants to know
112
+ * before it commits to a look. It says nothing about Reduce Transparency, which
113
+ * is a live preference and belongs to the hook.
114
+ */
115
+ export const hasGlass = GlassView !== null;
116
+
117
+ export interface GlassProps extends ViewProps {
118
+ /**
119
+ * How much of what is behind shows through. `regular` is the everyday
120
+ * material; `clear` is thinner, for something over its own artwork.
121
+ */
122
+ variant?: GlassVariant;
123
+ /** Tints the material. Takes a colour, not a token name. */
124
+ tint?: string;
125
+ /**
126
+ * Corner radius in points. The material rounds itself to this, so give it
127
+ * the same shape the container is drawn with — clipping a square material
128
+ * to a rounded parent throws away the lit edge that makes it read as glass.
129
+ */
130
+ radius?: GlassRadius;
131
+ /** Applied only when the material cannot be drawn. Give it a real surface. */
132
+ fallbackClassName?: string;
133
+ className?: string;
134
+ }
135
+
136
+ export function Glass({
137
+ variant = 'regular',
138
+ tint,
139
+ radius,
140
+ fallbackClassName = 'bg-card',
141
+ className,
142
+ children,
143
+ style,
144
+ ...props
145
+ }: GlassProps) {
146
+ const reduceTransparency = useReduceTransparency();
147
+ // Not knowing yet counts as "do not draw it": the material arriving a frame
148
+ // late is invisible, and one flashing at somebody who opted out is not.
149
+ const material = GlassView !== null && reduceTransparency === false;
150
+ const shape = shapeOf(radius);
151
+
152
+ return (
153
+ <View
154
+ className={cn('overflow-hidden', material ? null : fallbackClassName, className)}
155
+ style={[shape, style]}
156
+ {...props}
157
+ >
158
+ {material && GlassView ? (
159
+ <GlassView
160
+ glassEffectStyle={variant}
161
+ tintColor={tint}
162
+ pointerEvents="none"
163
+ style={[StyleSheet.absoluteFill, shape]}
164
+ />
165
+ ) : null}
166
+ {children}
167
+ </View>
168
+ );
169
+ }
170
+
171
+ Glass.displayName = 'Glass';
@@ -113,7 +113,13 @@ function observeReduceTransparency(
113
113
  */
114
114
  let knownReduceTransparency: boolean | null = null;
115
115
 
116
- function useReduceTransparency() {
116
+ /**
117
+ * Whether the device asks for Reduce Transparency: `true`, `false`, or `null`
118
+ * while the platform has not answered yet. Exported because every material in
119
+ * the library has to respect the same preference, and two copies of this would
120
+ * be two answers.
121
+ */
122
+ export function useReduceTransparency() {
117
123
  // Not knowing is deliberately conservative: draw opaque until the platform
118
124
  // says blur is allowed, rather than flashing blur at someone who opted out.
119
125
  const [enabled, setEnabled] = useState<boolean | null>(knownReduceTransparency);