@xaui/native 0.9.1-alpha.8 → 0.9.1-alpha.9

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.
@@ -1,52 +1,11 @@
1
1
  import * as react from 'react';
2
- import { Ref, ComponentType, ReactNode, Provider, RefCallback } from 'react';
2
+ import { Ref, ReactNode, Provider, RefCallback } from 'react';
3
+ import { b as IconProps, a as IconContextValue, S as SlotAnimation, F as FeedbackContext, c as AnimationProp, R as ResolvedAnimation, d as FeedbackVariant } from '../create-recipe-BjxSp9_k.cjs';
4
+ export { A as AnimationConfig, h as Axes, C as CompoundVariant, I as IconComponentProps, P as PressableFeedbackProps, f as Recipe, i as RecipeConfig, g as ResolveArgs, j as ResolvedSelection, k as ResolvedStyles, l as Selection, m as SlotStyle, n as SlotStyles, o as StateName, p as States, q as StyleFn, T as TintArgs, V as VariantColors, r as VariantRole, s as VariantTokens, e as createRecipe } from '../create-recipe-BjxSp9_k.cjs';
3
5
  import * as react_native from 'react-native';
4
- import { ImageSourcePropType, StyleProp, ImageStyle, PressableProps, ViewStyle, TextStyle } from 'react-native';
5
- import { SharedValue } from 'react-native-reanimated';
6
- import { g as XAUITheme, X as XAUIColors } from '../theme.type-C9bFJKFm.cjs';
7
-
8
- /** Anything React accepts as a ref, plus the absence of one. */
9
- type PossibleRef<T> = Ref<T> | undefined;
10
- /**
11
- * The props `mergeProps` knows how to combine. Deliberately loose: it merges whatever a
12
- * root hands to whatever child it was given, and neither side is knowable from here.
13
- */
14
- type MergeableProps = Record<string, unknown>;
15
- type AsChildProps = {
16
- /**
17
- * Merge this component's props into its single child instead of rendering an element
18
- * of its own — a navigation `Link` as a `Button`, a bespoke trigger as a `Select`.
19
- */
20
- asChild?: boolean;
21
- };
22
-
23
- /**
24
- * The props Lucide, Ionicons and `react-native-vector-icons` all accept — the shape
25
- * `as` is handed. It is a convention rather than an interface anyone published, which is
26
- * exactly why `Icon` exists: without it every call site computes the two values by hand.
27
- */
28
- type IconComponentProps = {
29
- size?: number;
30
- color?: string;
31
- };
32
- type IconProps = {
33
- /** An icon component. `size` and `color` are injected into it. */
34
- as?: ComponentType<IconComponentProps>;
35
- /** A raw `react-native-svg` element, cloned with the resolved size and colour. */
36
- children?: ReactNode;
37
- /** An image, tinted with the resolved colour. */
38
- source?: ImageSourcePropType;
39
- /** Overrides what the surrounding slot asked for. */
40
- size?: number;
41
- /** A raw value (R7), never a token. Overrides the slot's. */
42
- color?: string;
43
- style?: StyleProp<ImageStyle>;
44
- };
45
- /** What a component root publishes so the icons inside it need no props at all. */
46
- type IconContextValue = {
47
- size?: number;
48
- color?: string;
49
- };
6
+ import { StyleProp, ViewStyle } from 'react-native';
7
+ import 'react-native-reanimated';
8
+ import '../theme.type-C9bFJKFm.cjs';
50
9
 
51
10
  /**
52
11
  * The gap nobody else closes: an icon is a third-party component, so a slot context does
@@ -76,132 +35,6 @@ declare namespace Icon {
76
35
  declare const IconContext: react.Context<IconContextValue>;
77
36
  declare function useIconContext(): IconContextValue;
78
37
 
79
- type PortalProps = {
80
- children: ReactNode;
81
- };
82
- /**
83
- * Renders its children into the nearest `PortalHost` instead of where it sits. What
84
- * `Dialog`, `Sheet`, `Drawer` and `Snackbar` are built on: an overlay has to escape the
85
- * clipping and stacking of whatever container happened to hold the trigger.
86
- *
87
- * Both halves run in layout effects rather than effects, which is deliberate: the content
88
- * lands in the same commit as the trigger's, so an overlay neither shows one frame late
89
- * nor survives one frame past the unmount that closed it. The unpublish sits in its own
90
- * effect so that it does not depend on `children` — a re-publish then keeps the portal's
91
- * place in the host's order instead of dropping it and re-adding it at the end.
92
- */
93
- declare function Portal({ children }: PortalProps): null;
94
- declare namespace Portal {
95
- var displayName: string;
96
- }
97
-
98
- type PortalMethods = {
99
- addPortal: (key: string, element: ReactNode) => void;
100
- removePortal: (key: string) => void;
101
- };
102
- /**
103
- * `null` outside a host, and `Portal` treats that as "render nothing" rather than
104
- * throwing: an app that forgot `PortalHost` should lose its overlays, not crash on the
105
- * first `Dialog`.
106
- */
107
- declare const PortalContext: react.Context<PortalMethods | null>;
108
-
109
- type PortalHostProps = {
110
- children: ReactNode;
111
- };
112
- /**
113
- * Where every `Portal` in the tree below renders. Mounted once, at the root of the app,
114
- * above navigation — an overlay that renders inside a screen is clipped by it.
115
- */
116
- declare function PortalHost({ children }: PortalHostProps): react.JSX.Element;
117
- declare namespace PortalHost {
118
- var displayName: string;
119
- }
120
-
121
- /**
122
- * What the root does under the finger. `scale-highlight` and `scale-ripple` mount their
123
- * overlay themselves; `scale` mounts none, which is what a root picks when it renders its
124
- * own `<PressableFeedback.Highlight>` to style it (R1: no prop reaches into another
125
- * component's insides).
126
- */
127
- type FeedbackVariant = 'scale-highlight' | 'scale-ripple' | 'scale' | 'none';
128
- type AnimationConfig = {
129
- scale?: boolean;
130
- highlight?: boolean;
131
- ripple?: boolean;
132
- };
133
- /**
134
- * `false` and `'disabled'` turn this component's animations off. `'disable-all'` turns
135
- * them off for its descendants too — a long list kills every row's worklets with one
136
- * prop instead of threading it down. An object switches them off one at a time.
137
- */
138
- type AnimationProp = boolean | 'disabled' | 'disable-all' | AnimationConfig;
139
- /** `animation` once normalised — what the components actually read. */
140
- type ResolvedAnimation = {
141
- scale: boolean;
142
- highlight: boolean;
143
- ripple: boolean;
144
- /** True when every sub-animation is off: the branch that mounts no worklet at all. */
145
- none: boolean;
146
- /** Propagated to descendants through context. */
147
- disableAll: boolean;
148
- };
149
- type PressableFeedbackProps = Omit<PressableProps, 'style' | 'children' | 'disabled'> & {
150
- /** Controlled: the root owns the state, because its recipe resolves on it (R5). */
151
- isPressed?: boolean;
152
- /** R8: `disabled` is not part of the public vocabulary, `isX` is. */
153
- isDisabled?: boolean;
154
- /**
155
- * Merge into the single child instead of rendering a pressable (R12) — **keeping the
156
- * feedback**. Swapping this component out for a bare `Slot` would silently drop the
157
- * touch feedback of every `asChild` control.
158
- */
159
- asChild?: boolean;
160
- feedbackVariant?: FeedbackVariant;
161
- animation?: AnimationProp;
162
- style?: StyleProp<ViewStyle>;
163
- /**
164
- * `Pressable`'s function form is dropped on purpose. It exists to hand the press state
165
- * to children; here the root above already owns that state and this publishes it
166
- * through context, so the function form would be a second, quieter source of truth.
167
- */
168
- children?: ReactNode;
169
- };
170
- /**
171
- * A slot's own animation, overriding the blanket one on the root. `false` switches that
172
- * slot off; the object tunes it. Deliberately two knobs rather than a full timing
173
- * surface — anything past this is a different animation, and that is a component's job,
174
- * not a prop's.
175
- */
176
- type SlotAnimation = boolean | {
177
- /** Milliseconds. Falls back to the shared press timing. */
178
- duration?: number;
179
- /** How far the overlay goes at full press, 0 to 1. */
180
- opacity?: number;
181
- };
182
- type FeedbackContext = {
183
- isPressed: boolean;
184
- animation: ResolvedAnimation;
185
- /** Absent on the static branch, where nothing animates and no worklet is mounted. */
186
- progress?: SharedValue<number>;
187
- /**
188
- * Bumped on every press-in. The ripple starts from this rather than from a `useEffect`
189
- * on `isPressed`: a one-shot driven by a boolean depends on React re-rendering between
190
- * the two touch events, and starting it from the event that carries the coordinates is
191
- * both simpler and impossible to miss.
192
- */
193
- pressCount?: SharedValue<number>;
194
- /** Where the finger landed, and how big the root is — the ripple needs both. */
195
- origin?: SharedValue<{
196
- x: number;
197
- y: number;
198
- }>;
199
- size?: SharedValue<{
200
- width: number;
201
- height: number;
202
- }>;
203
- };
204
-
205
38
  type PressableFeedbackHighlightProps = {
206
39
  style?: StyleProp<ViewStyle>;
207
40
  /** Overrides the blanket `animation` on the root, for this overlay only. */
@@ -295,84 +128,62 @@ declare const PressableFeedback: react.ForwardRefExoticComponent<Omit<react_nati
295
128
  Ripple: typeof PressableFeedbackRipple;
296
129
  };
297
130
 
131
+ /** Anything React accepts as a ref, plus the absence of one. */
132
+ type PossibleRef<T> = Ref<T> | undefined;
298
133
  /**
299
- * A slot is a view or a text node, and a recipe writes one object per slot, so the two
300
- * RN style shapes are merged rather than discriminated per slot.
301
- */
302
- type SlotStyle = ViewStyle & TextStyle;
303
- type SlotStyles<Slot extends string> = Partial<Record<Slot, SlotStyle>>;
304
- /** The roles a variant consumes. The variant names tokens; `paint` says where they land. */
305
- type VariantRole = 'bg' | 'bgPressed' | 'fg' | 'border';
306
- /** Token names per role — no colour value ever appears in a recipe. */
307
- type VariantTokens = Partial<Record<VariantRole, keyof XAUIColors>>;
308
- /** The same roles resolved: theme colours, or the slices of a raw `color`. */
309
- type VariantColors = Partial<Record<VariantRole, string>>;
310
- /**
311
- * `disabled` is applied last of the three: a control that is both pressed and disabled
312
- * has to read disabled.
134
+ * The props `mergeProps` knows how to combine. Deliberately loose: it merges whatever a
135
+ * root hands to whatever child it was given, and neither side is knowable from here.
313
136
  */
314
- type StateName = 'focused' | 'pressed' | 'disabled';
315
- type States = Partial<Record<StateName, boolean>>;
316
- /** Reads the theme and the variant's resolved colours; returns one style per slot. */
317
- type StyleFn<Slot extends string> = (theme: XAUITheme, colors: VariantColors) => SlotStyles<Slot>;
318
- /** Named axes of finite token values `{ size: { sm: fn, md: fn } }`. */
319
- type Axes<Slot extends string> = Record<string, Record<string, StyleFn<Slot>>>;
320
- /** One value per axis, plus the variant. Missing keys fall back to `defaultVariants`. */
321
- type Selection<Variant extends string, A extends Axes<string>> = {
322
- variant?: Variant;
323
- } & {
324
- [Axis in keyof A]?: Extract<keyof A[Axis], string>;
325
- };
326
- type CompoundVariant<Slot extends string, Variant extends string, A extends Axes<Slot>> = {
327
- when: Selection<Variant, A>;
328
- style: StyleFn<Slot>;
329
- };
330
- type RecipeConfig<Slot extends string, Variant extends string, A extends Axes<Slot>> = {
331
- /** Every slot the component publishes. Slots a recipe never styles resolve to `{}`. */
332
- slots: readonly Slot[];
333
- base?: StyleFn<Slot>;
334
- variantTokens?: Record<Variant, VariantTokens>;
335
- /** Where the variant's colours land — written once, and it holds for every variant. */
336
- paint?: StyleFn<Slot>;
337
- variants?: A;
338
- compoundVariants?: ReadonlyArray<CompoundVariant<Slot, Variant, A>>;
339
- states?: Partial<Record<StateName, StyleFn<Slot>>>;
340
- defaultVariants?: Selection<Variant, A>;
137
+ type MergeableProps = Record<string, unknown>;
138
+ type AsChildProps = {
139
+ /**
140
+ * Merge this component's props into its single child instead of rendering an element
141
+ * of its own a navigation `Link` as a `Button`, a bespoke trigger as a `Select`.
142
+ */
143
+ asChild?: boolean;
341
144
  };
342
- /** Stable references: the same object for the same tokens, for the app's lifetime. */
343
- type ResolvedStyles<Slot extends string> = Readonly<Record<Slot, SlotStyle>>;
344
- /** A selection with `defaultVariants` already folded in, keyed by axis name. */
345
- type ResolvedSelection = Readonly<Record<string, string | undefined>>;
346
145
 
347
- type ResolveArgs<Variant extends string, A extends Axes<string>> = {
348
- theme: XAUITheme;
349
- selection?: Selection<Variant, A>;
350
- states?: States;
146
+ type PortalProps = {
147
+ children: ReactNode;
351
148
  };
352
- type TintArgs<Variant extends string, A extends Axes<string>> = ResolveArgs<Variant, A> & {
353
- color: string;
149
+ /**
150
+ * Renders its children into the nearest `PortalHost` instead of where it sits. What
151
+ * `Dialog`, `Sheet`, `Drawer` and `Snackbar` are built on: an overlay has to escape the
152
+ * clipping and stacking of whatever container happened to hold the trigger.
153
+ *
154
+ * Both halves run in layout effects rather than effects, which is deliberate: the content
155
+ * lands in the same commit as the trigger's, so an overlay neither shows one frame late
156
+ * nor survives one frame past the unmount that closed it. The unpublish sits in its own
157
+ * effect so that it does not depend on `children` — a re-publish then keeps the portal's
158
+ * place in the host's order instead of dropping it and re-adding it at the end.
159
+ */
160
+ declare function Portal({ children }: PortalProps): null;
161
+ declare namespace Portal {
162
+ var displayName: string;
163
+ }
164
+
165
+ type PortalMethods = {
166
+ addPortal: (key: string, element: ReactNode) => void;
167
+ removePortal: (key: string) => void;
354
168
  };
355
- type Recipe<Slot extends string, Variant extends string, A extends Axes<Slot>> = {
356
- readonly slots: readonly Slot[];
357
- /** The cached pass: stable `StyleSheet` references, keyed by tokens alone. */
358
- resolve(args: ResolveArgs<Variant, A>): ResolvedStyles<Slot>;
359
- /**
360
- * The tint pass: the same functions run again with `color`'s slices in place of the
361
- * theme's tokens. Uncached and allocating, and only ever called when `color` is set.
362
- */
363
- tint(args: TintArgs<Variant, A>): SlotStyles<Slot>;
169
+ /**
170
+ * `null` outside a host, and `Portal` treats that as "render nothing" rather than
171
+ * throwing: an app that forgot `PortalHost` should lose its overlays, not crash on the
172
+ * first `Dialog`.
173
+ */
174
+ declare const PortalContext: react.Context<PortalMethods | null>;
175
+
176
+ type PortalHostProps = {
177
+ children: ReactNode;
364
178
  };
365
179
  /**
366
- * A component's style, declared once. Resolution splits in two because the two halves
367
- * have different lifetimes: everything keyed by a finite token is cached forever, and
368
- * an arbitrary `color` is recomputed per render — which is what keeps the cache bounded
369
- * by the number of token combinations rather than by the palette users invent.
370
- *
371
- * const styles = buttonRecipe.resolve({ theme, selection: { variant, size }, states })
372
- * const tint = color ? buttonRecipe.tint({ theme, color, selection, states }) : undefined
373
- * <View style={[styles.root, tint?.root, style]} />
180
+ * Where every `Portal` in the tree below renders. Mounted once, at the root of the app,
181
+ * above navigation an overlay that renders inside a screen is clipped by it.
374
182
  */
375
- declare function createRecipe<Slot extends string, Variant extends string, const A extends Axes<Slot>>(config: RecipeConfig<Slot, Variant, A>): Recipe<Slot, Variant, A>;
183
+ declare function PortalHost({ children }: PortalHostProps): react.JSX.Element;
184
+ declare namespace PortalHost {
185
+ var displayName: string;
186
+ }
376
187
 
377
188
  /**
378
189
  * R3: the string a root should wrap in its default text slot, or `null` when it should
@@ -448,4 +259,4 @@ type SlotProps = MergeableProps & {
448
259
  */
449
260
  declare const Slot: react.ForwardRefExoticComponent<Omit<SlotProps, "ref"> & react.RefAttributes<unknown>>;
450
261
 
451
- export { type AnimationConfig, type AnimationProp, type AsChildProps, type Axes, type CompoundVariant, type FeedbackContext, type FeedbackVariant, HIGHLIGHT_OPACITY, Icon, type IconComponentProps, IconContext, type IconContextValue, type IconProps, type MergeableProps, PRESS_DURATION, PRESS_SCALE, Portal, PortalContext, PortalHost, type PortalHostProps, type PortalMethods, type PortalProps, type PossibleRef, PressableFeedback, type PressableFeedbackHighlightProps, type PressableFeedbackProps, type PressableFeedbackRippleProps, RELEASE_DURATION, RIPPLE_COVERAGE, RIPPLE_DURATION, RIPPLE_OPACITY, type Recipe, type RecipeConfig, type ResolveArgs, type ResolvedAnimation, type ResolvedSelection, type ResolvedStyles, type Selection, Slot, type SlotAnimation, type SlotProps, type SlotStyle, type SlotStyles, type StateName, type States, type StyleFn, type TintArgs, type VariantColors, type VariantRole, type VariantTokens, childrenToString, createRecipe, createSlotContext, mergeProps, mergeRefs, resolveAnimation, resolveSlotAnimation, useFeedback, useIconContext };
262
+ export { AnimationProp, type AsChildProps, FeedbackContext, FeedbackVariant, HIGHLIGHT_OPACITY, Icon, IconContext, IconContextValue, IconProps, type MergeableProps, PRESS_DURATION, PRESS_SCALE, Portal, PortalContext, PortalHost, type PortalHostProps, type PortalMethods, type PortalProps, type PossibleRef, PressableFeedback, type PressableFeedbackHighlightProps, type PressableFeedbackRippleProps, RELEASE_DURATION, RIPPLE_COVERAGE, RIPPLE_DURATION, RIPPLE_OPACITY, ResolvedAnimation, Slot, SlotAnimation, type SlotProps, childrenToString, createSlotContext, mergeProps, mergeRefs, resolveAnimation, resolveSlotAnimation, useFeedback, useIconContext };
@@ -1,52 +1,11 @@
1
1
  import * as react from 'react';
2
- import { Ref, ComponentType, ReactNode, Provider, RefCallback } from 'react';
2
+ import { Ref, ReactNode, Provider, RefCallback } from 'react';
3
+ import { b as IconProps, a as IconContextValue, S as SlotAnimation, F as FeedbackContext, c as AnimationProp, R as ResolvedAnimation, d as FeedbackVariant } from '../create-recipe-M99yoHrG.js';
4
+ export { A as AnimationConfig, h as Axes, C as CompoundVariant, I as IconComponentProps, P as PressableFeedbackProps, f as Recipe, i as RecipeConfig, g as ResolveArgs, j as ResolvedSelection, k as ResolvedStyles, l as Selection, m as SlotStyle, n as SlotStyles, o as StateName, p as States, q as StyleFn, T as TintArgs, V as VariantColors, r as VariantRole, s as VariantTokens, e as createRecipe } from '../create-recipe-M99yoHrG.js';
3
5
  import * as react_native from 'react-native';
4
- import { ImageSourcePropType, StyleProp, ImageStyle, PressableProps, ViewStyle, TextStyle } from 'react-native';
5
- import { SharedValue } from 'react-native-reanimated';
6
- import { g as XAUITheme, X as XAUIColors } from '../theme.type-C9bFJKFm.js';
7
-
8
- /** Anything React accepts as a ref, plus the absence of one. */
9
- type PossibleRef<T> = Ref<T> | undefined;
10
- /**
11
- * The props `mergeProps` knows how to combine. Deliberately loose: it merges whatever a
12
- * root hands to whatever child it was given, and neither side is knowable from here.
13
- */
14
- type MergeableProps = Record<string, unknown>;
15
- type AsChildProps = {
16
- /**
17
- * Merge this component's props into its single child instead of rendering an element
18
- * of its own — a navigation `Link` as a `Button`, a bespoke trigger as a `Select`.
19
- */
20
- asChild?: boolean;
21
- };
22
-
23
- /**
24
- * The props Lucide, Ionicons and `react-native-vector-icons` all accept — the shape
25
- * `as` is handed. It is a convention rather than an interface anyone published, which is
26
- * exactly why `Icon` exists: without it every call site computes the two values by hand.
27
- */
28
- type IconComponentProps = {
29
- size?: number;
30
- color?: string;
31
- };
32
- type IconProps = {
33
- /** An icon component. `size` and `color` are injected into it. */
34
- as?: ComponentType<IconComponentProps>;
35
- /** A raw `react-native-svg` element, cloned with the resolved size and colour. */
36
- children?: ReactNode;
37
- /** An image, tinted with the resolved colour. */
38
- source?: ImageSourcePropType;
39
- /** Overrides what the surrounding slot asked for. */
40
- size?: number;
41
- /** A raw value (R7), never a token. Overrides the slot's. */
42
- color?: string;
43
- style?: StyleProp<ImageStyle>;
44
- };
45
- /** What a component root publishes so the icons inside it need no props at all. */
46
- type IconContextValue = {
47
- size?: number;
48
- color?: string;
49
- };
6
+ import { StyleProp, ViewStyle } from 'react-native';
7
+ import 'react-native-reanimated';
8
+ import '../theme.type-C9bFJKFm.js';
50
9
 
51
10
  /**
52
11
  * The gap nobody else closes: an icon is a third-party component, so a slot context does
@@ -76,132 +35,6 @@ declare namespace Icon {
76
35
  declare const IconContext: react.Context<IconContextValue>;
77
36
  declare function useIconContext(): IconContextValue;
78
37
 
79
- type PortalProps = {
80
- children: ReactNode;
81
- };
82
- /**
83
- * Renders its children into the nearest `PortalHost` instead of where it sits. What
84
- * `Dialog`, `Sheet`, `Drawer` and `Snackbar` are built on: an overlay has to escape the
85
- * clipping and stacking of whatever container happened to hold the trigger.
86
- *
87
- * Both halves run in layout effects rather than effects, which is deliberate: the content
88
- * lands in the same commit as the trigger's, so an overlay neither shows one frame late
89
- * nor survives one frame past the unmount that closed it. The unpublish sits in its own
90
- * effect so that it does not depend on `children` — a re-publish then keeps the portal's
91
- * place in the host's order instead of dropping it and re-adding it at the end.
92
- */
93
- declare function Portal({ children }: PortalProps): null;
94
- declare namespace Portal {
95
- var displayName: string;
96
- }
97
-
98
- type PortalMethods = {
99
- addPortal: (key: string, element: ReactNode) => void;
100
- removePortal: (key: string) => void;
101
- };
102
- /**
103
- * `null` outside a host, and `Portal` treats that as "render nothing" rather than
104
- * throwing: an app that forgot `PortalHost` should lose its overlays, not crash on the
105
- * first `Dialog`.
106
- */
107
- declare const PortalContext: react.Context<PortalMethods | null>;
108
-
109
- type PortalHostProps = {
110
- children: ReactNode;
111
- };
112
- /**
113
- * Where every `Portal` in the tree below renders. Mounted once, at the root of the app,
114
- * above navigation — an overlay that renders inside a screen is clipped by it.
115
- */
116
- declare function PortalHost({ children }: PortalHostProps): react.JSX.Element;
117
- declare namespace PortalHost {
118
- var displayName: string;
119
- }
120
-
121
- /**
122
- * What the root does under the finger. `scale-highlight` and `scale-ripple` mount their
123
- * overlay themselves; `scale` mounts none, which is what a root picks when it renders its
124
- * own `<PressableFeedback.Highlight>` to style it (R1: no prop reaches into another
125
- * component's insides).
126
- */
127
- type FeedbackVariant = 'scale-highlight' | 'scale-ripple' | 'scale' | 'none';
128
- type AnimationConfig = {
129
- scale?: boolean;
130
- highlight?: boolean;
131
- ripple?: boolean;
132
- };
133
- /**
134
- * `false` and `'disabled'` turn this component's animations off. `'disable-all'` turns
135
- * them off for its descendants too — a long list kills every row's worklets with one
136
- * prop instead of threading it down. An object switches them off one at a time.
137
- */
138
- type AnimationProp = boolean | 'disabled' | 'disable-all' | AnimationConfig;
139
- /** `animation` once normalised — what the components actually read. */
140
- type ResolvedAnimation = {
141
- scale: boolean;
142
- highlight: boolean;
143
- ripple: boolean;
144
- /** True when every sub-animation is off: the branch that mounts no worklet at all. */
145
- none: boolean;
146
- /** Propagated to descendants through context. */
147
- disableAll: boolean;
148
- };
149
- type PressableFeedbackProps = Omit<PressableProps, 'style' | 'children' | 'disabled'> & {
150
- /** Controlled: the root owns the state, because its recipe resolves on it (R5). */
151
- isPressed?: boolean;
152
- /** R8: `disabled` is not part of the public vocabulary, `isX` is. */
153
- isDisabled?: boolean;
154
- /**
155
- * Merge into the single child instead of rendering a pressable (R12) — **keeping the
156
- * feedback**. Swapping this component out for a bare `Slot` would silently drop the
157
- * touch feedback of every `asChild` control.
158
- */
159
- asChild?: boolean;
160
- feedbackVariant?: FeedbackVariant;
161
- animation?: AnimationProp;
162
- style?: StyleProp<ViewStyle>;
163
- /**
164
- * `Pressable`'s function form is dropped on purpose. It exists to hand the press state
165
- * to children; here the root above already owns that state and this publishes it
166
- * through context, so the function form would be a second, quieter source of truth.
167
- */
168
- children?: ReactNode;
169
- };
170
- /**
171
- * A slot's own animation, overriding the blanket one on the root. `false` switches that
172
- * slot off; the object tunes it. Deliberately two knobs rather than a full timing
173
- * surface — anything past this is a different animation, and that is a component's job,
174
- * not a prop's.
175
- */
176
- type SlotAnimation = boolean | {
177
- /** Milliseconds. Falls back to the shared press timing. */
178
- duration?: number;
179
- /** How far the overlay goes at full press, 0 to 1. */
180
- opacity?: number;
181
- };
182
- type FeedbackContext = {
183
- isPressed: boolean;
184
- animation: ResolvedAnimation;
185
- /** Absent on the static branch, where nothing animates and no worklet is mounted. */
186
- progress?: SharedValue<number>;
187
- /**
188
- * Bumped on every press-in. The ripple starts from this rather than from a `useEffect`
189
- * on `isPressed`: a one-shot driven by a boolean depends on React re-rendering between
190
- * the two touch events, and starting it from the event that carries the coordinates is
191
- * both simpler and impossible to miss.
192
- */
193
- pressCount?: SharedValue<number>;
194
- /** Where the finger landed, and how big the root is — the ripple needs both. */
195
- origin?: SharedValue<{
196
- x: number;
197
- y: number;
198
- }>;
199
- size?: SharedValue<{
200
- width: number;
201
- height: number;
202
- }>;
203
- };
204
-
205
38
  type PressableFeedbackHighlightProps = {
206
39
  style?: StyleProp<ViewStyle>;
207
40
  /** Overrides the blanket `animation` on the root, for this overlay only. */
@@ -295,84 +128,62 @@ declare const PressableFeedback: react.ForwardRefExoticComponent<Omit<react_nati
295
128
  Ripple: typeof PressableFeedbackRipple;
296
129
  };
297
130
 
131
+ /** Anything React accepts as a ref, plus the absence of one. */
132
+ type PossibleRef<T> = Ref<T> | undefined;
298
133
  /**
299
- * A slot is a view or a text node, and a recipe writes one object per slot, so the two
300
- * RN style shapes are merged rather than discriminated per slot.
301
- */
302
- type SlotStyle = ViewStyle & TextStyle;
303
- type SlotStyles<Slot extends string> = Partial<Record<Slot, SlotStyle>>;
304
- /** The roles a variant consumes. The variant names tokens; `paint` says where they land. */
305
- type VariantRole = 'bg' | 'bgPressed' | 'fg' | 'border';
306
- /** Token names per role — no colour value ever appears in a recipe. */
307
- type VariantTokens = Partial<Record<VariantRole, keyof XAUIColors>>;
308
- /** The same roles resolved: theme colours, or the slices of a raw `color`. */
309
- type VariantColors = Partial<Record<VariantRole, string>>;
310
- /**
311
- * `disabled` is applied last of the three: a control that is both pressed and disabled
312
- * has to read disabled.
134
+ * The props `mergeProps` knows how to combine. Deliberately loose: it merges whatever a
135
+ * root hands to whatever child it was given, and neither side is knowable from here.
313
136
  */
314
- type StateName = 'focused' | 'pressed' | 'disabled';
315
- type States = Partial<Record<StateName, boolean>>;
316
- /** Reads the theme and the variant's resolved colours; returns one style per slot. */
317
- type StyleFn<Slot extends string> = (theme: XAUITheme, colors: VariantColors) => SlotStyles<Slot>;
318
- /** Named axes of finite token values `{ size: { sm: fn, md: fn } }`. */
319
- type Axes<Slot extends string> = Record<string, Record<string, StyleFn<Slot>>>;
320
- /** One value per axis, plus the variant. Missing keys fall back to `defaultVariants`. */
321
- type Selection<Variant extends string, A extends Axes<string>> = {
322
- variant?: Variant;
323
- } & {
324
- [Axis in keyof A]?: Extract<keyof A[Axis], string>;
325
- };
326
- type CompoundVariant<Slot extends string, Variant extends string, A extends Axes<Slot>> = {
327
- when: Selection<Variant, A>;
328
- style: StyleFn<Slot>;
329
- };
330
- type RecipeConfig<Slot extends string, Variant extends string, A extends Axes<Slot>> = {
331
- /** Every slot the component publishes. Slots a recipe never styles resolve to `{}`. */
332
- slots: readonly Slot[];
333
- base?: StyleFn<Slot>;
334
- variantTokens?: Record<Variant, VariantTokens>;
335
- /** Where the variant's colours land — written once, and it holds for every variant. */
336
- paint?: StyleFn<Slot>;
337
- variants?: A;
338
- compoundVariants?: ReadonlyArray<CompoundVariant<Slot, Variant, A>>;
339
- states?: Partial<Record<StateName, StyleFn<Slot>>>;
340
- defaultVariants?: Selection<Variant, A>;
137
+ type MergeableProps = Record<string, unknown>;
138
+ type AsChildProps = {
139
+ /**
140
+ * Merge this component's props into its single child instead of rendering an element
141
+ * of its own a navigation `Link` as a `Button`, a bespoke trigger as a `Select`.
142
+ */
143
+ asChild?: boolean;
341
144
  };
342
- /** Stable references: the same object for the same tokens, for the app's lifetime. */
343
- type ResolvedStyles<Slot extends string> = Readonly<Record<Slot, SlotStyle>>;
344
- /** A selection with `defaultVariants` already folded in, keyed by axis name. */
345
- type ResolvedSelection = Readonly<Record<string, string | undefined>>;
346
145
 
347
- type ResolveArgs<Variant extends string, A extends Axes<string>> = {
348
- theme: XAUITheme;
349
- selection?: Selection<Variant, A>;
350
- states?: States;
146
+ type PortalProps = {
147
+ children: ReactNode;
351
148
  };
352
- type TintArgs<Variant extends string, A extends Axes<string>> = ResolveArgs<Variant, A> & {
353
- color: string;
149
+ /**
150
+ * Renders its children into the nearest `PortalHost` instead of where it sits. What
151
+ * `Dialog`, `Sheet`, `Drawer` and `Snackbar` are built on: an overlay has to escape the
152
+ * clipping and stacking of whatever container happened to hold the trigger.
153
+ *
154
+ * Both halves run in layout effects rather than effects, which is deliberate: the content
155
+ * lands in the same commit as the trigger's, so an overlay neither shows one frame late
156
+ * nor survives one frame past the unmount that closed it. The unpublish sits in its own
157
+ * effect so that it does not depend on `children` — a re-publish then keeps the portal's
158
+ * place in the host's order instead of dropping it and re-adding it at the end.
159
+ */
160
+ declare function Portal({ children }: PortalProps): null;
161
+ declare namespace Portal {
162
+ var displayName: string;
163
+ }
164
+
165
+ type PortalMethods = {
166
+ addPortal: (key: string, element: ReactNode) => void;
167
+ removePortal: (key: string) => void;
354
168
  };
355
- type Recipe<Slot extends string, Variant extends string, A extends Axes<Slot>> = {
356
- readonly slots: readonly Slot[];
357
- /** The cached pass: stable `StyleSheet` references, keyed by tokens alone. */
358
- resolve(args: ResolveArgs<Variant, A>): ResolvedStyles<Slot>;
359
- /**
360
- * The tint pass: the same functions run again with `color`'s slices in place of the
361
- * theme's tokens. Uncached and allocating, and only ever called when `color` is set.
362
- */
363
- tint(args: TintArgs<Variant, A>): SlotStyles<Slot>;
169
+ /**
170
+ * `null` outside a host, and `Portal` treats that as "render nothing" rather than
171
+ * throwing: an app that forgot `PortalHost` should lose its overlays, not crash on the
172
+ * first `Dialog`.
173
+ */
174
+ declare const PortalContext: react.Context<PortalMethods | null>;
175
+
176
+ type PortalHostProps = {
177
+ children: ReactNode;
364
178
  };
365
179
  /**
366
- * A component's style, declared once. Resolution splits in two because the two halves
367
- * have different lifetimes: everything keyed by a finite token is cached forever, and
368
- * an arbitrary `color` is recomputed per render — which is what keeps the cache bounded
369
- * by the number of token combinations rather than by the palette users invent.
370
- *
371
- * const styles = buttonRecipe.resolve({ theme, selection: { variant, size }, states })
372
- * const tint = color ? buttonRecipe.tint({ theme, color, selection, states }) : undefined
373
- * <View style={[styles.root, tint?.root, style]} />
180
+ * Where every `Portal` in the tree below renders. Mounted once, at the root of the app,
181
+ * above navigation an overlay that renders inside a screen is clipped by it.
374
182
  */
375
- declare function createRecipe<Slot extends string, Variant extends string, const A extends Axes<Slot>>(config: RecipeConfig<Slot, Variant, A>): Recipe<Slot, Variant, A>;
183
+ declare function PortalHost({ children }: PortalHostProps): react.JSX.Element;
184
+ declare namespace PortalHost {
185
+ var displayName: string;
186
+ }
376
187
 
377
188
  /**
378
189
  * R3: the string a root should wrap in its default text slot, or `null` when it should
@@ -448,4 +259,4 @@ type SlotProps = MergeableProps & {
448
259
  */
449
260
  declare const Slot: react.ForwardRefExoticComponent<Omit<SlotProps, "ref"> & react.RefAttributes<unknown>>;
450
261
 
451
- export { type AnimationConfig, type AnimationProp, type AsChildProps, type Axes, type CompoundVariant, type FeedbackContext, type FeedbackVariant, HIGHLIGHT_OPACITY, Icon, type IconComponentProps, IconContext, type IconContextValue, type IconProps, type MergeableProps, PRESS_DURATION, PRESS_SCALE, Portal, PortalContext, PortalHost, type PortalHostProps, type PortalMethods, type PortalProps, type PossibleRef, PressableFeedback, type PressableFeedbackHighlightProps, type PressableFeedbackProps, type PressableFeedbackRippleProps, RELEASE_DURATION, RIPPLE_COVERAGE, RIPPLE_DURATION, RIPPLE_OPACITY, type Recipe, type RecipeConfig, type ResolveArgs, type ResolvedAnimation, type ResolvedSelection, type ResolvedStyles, type Selection, Slot, type SlotAnimation, type SlotProps, type SlotStyle, type SlotStyles, type StateName, type States, type StyleFn, type TintArgs, type VariantColors, type VariantRole, type VariantTokens, childrenToString, createRecipe, createSlotContext, mergeProps, mergeRefs, resolveAnimation, resolveSlotAnimation, useFeedback, useIconContext };
262
+ export { AnimationProp, type AsChildProps, FeedbackContext, FeedbackVariant, HIGHLIGHT_OPACITY, Icon, IconContext, IconContextValue, IconProps, type MergeableProps, PRESS_DURATION, PRESS_SCALE, Portal, PortalContext, PortalHost, type PortalHostProps, type PortalMethods, type PortalProps, type PossibleRef, PressableFeedback, type PressableFeedbackHighlightProps, type PressableFeedbackRippleProps, RELEASE_DURATION, RIPPLE_COVERAGE, RIPPLE_DURATION, RIPPLE_OPACITY, ResolvedAnimation, Slot, SlotAnimation, type SlotProps, childrenToString, createSlotContext, mergeProps, mergeRefs, resolveAnimation, resolveSlotAnimation, useFeedback, useIconContext };