import type { ComponentType, ReactNode } from 'react'; export { NativeHost, type NativeHostProps } from './native-host.js'; interface NativeUIModule { Host: ComponentType<{ children?: ReactNode; /** * Which axes the platform is allowed to size. * * **It is not a one-off measurement.** An axis given to `matchContents` is * given for good: the host writes the platform's measured size straight * into the layout every time the platform's geometry changes, and dirties * the layout when it does. So a control that lays itself out again under a * press drags its box with it, and everything below it moves — which is * the defect this spent three attempts on, twice reasoning about the first * measurement when the problem was every one after it. * * The rule that comes out of that: **never hand over an axis whose size * you already know.** State it in `style` instead and match only what is * genuinely the platform's — a labelled button's width, a toggle's width. * An axis left out is never written to, so an explicit size on it is safe. * * Where nothing can be stated the axis has to stay matched. A picker is * the honest example: a menu is a compact button and a wheel is a rotor, * and only the platform knows which it drew. */ matchContents?: boolean | { vertical?: boolean; horizontal?: boolean; }; /** * Which safe areas the host lets the platform inset its content for. * * Every host here passes `keyboard`, and it is not optional. A host is a * hosting controller, and a hosting controller insets its content for the * keyboard by default — so a control docked above the keyboard has its * content moved *inside* the box React Native gave it, over whatever is * above it, while React Native's own layout says nothing has moved. That * is a composer whose buttons sit on its text until the next layout pass * puts them back. * * React Native owns the layout of everything in this library, so a * platform that moves content inside a box we positioned is never right. * `keyboard` and not `all`: the notch and the home indicator are the * platform's business and stay its business. */ ignoreSafeArea?: 'all' | 'container' | 'keyboard'; /** * The appearance the platform draws the hosted control in. * * Passed by `NativeHost` from the app's own theme, because the host would * otherwise resolve it from the system — which is a different question, * and one whose answer does not change when the theme does. */ colorScheme?: 'light' | 'dark'; style?: unknown; [key: string]: unknown; }>; /** * Hosts React Native views inside the native tree. Anything of ours that * goes inside a native container has to be wrapped in this or the native * layout does not measure it — children spill outside their container. */ RNHostView: ComponentType<{ children?: ReactNode; matchContents?: boolean; style?: unknown; }>; /** * `style` here is not a React Native style — it is the small portable subset * (`width`, `height`, padding, `backgroundColor`, `borderRadius`, `opacity`) * that the toolkit compiles into real SwiftUI and Compose modifiers. It is * how a control is given a definite size without the host having to guess. */ Button: ComponentType>; Switch: ComponentType>; Slider: ComponentType>; Picker: ComponentType> & { Item: ComponentType>; }; BottomSheet: ComponentType>; } /** * The native UI module, or null when it is not installed or the platform has * no toolkit behind it. Resolved once and cached — a failed require is not * retried on every render. */ export declare function getNativeUI(): NativeUIModule | null; /** Whether `native` will actually render a platform control. */ export declare function hasNativeUI(): boolean; /** * The SwiftUI view modifiers, for the handful of things the portable props * cannot express. * * The universal components cover the common ground — a variant, a size, an * enabled flag — but the platform has looks with no cross-platform equivalent, * and Liquid Glass is the one that matters: it is the material iOS 26 draws its * own floating controls in, and nothing in the portable API asks for it. A * modifier is how SwiftUI is configured, so this is the door to it. * * iOS only, and lazily resolved like everything else here. Android's toolkit * has its own modifier system and no equivalent material, so asking for glass * there is not a downgrade — it is a different question. */ interface SwiftUIModifiers { buttonStyle: (style: 'automatic' | 'bordered' | 'borderedProminent' | 'borderless' | 'glass' | 'glassProminent' | 'plain') => unknown; buttonBorderShape: (shape: 'automatic' | 'capsule' | 'roundedRectangle' | 'circle', cornerRadius?: number) => unknown; controlSize: (size: 'mini' | 'small' | 'regular' | 'large' | 'extraLarge') => unknown; /** * Paints a sheet's own surface, rather than a background behind its content. * * It is the only way out of the material a sheet is drawn in by default, and * it reaches what an ordinary background cannot: the grabber's strip at the * top and the safe-area inset at the bottom, which belong to the sheet's * chrome and not to anything hosted inside it. * * **iOS 16.4 and up.** Below that it is inert and the sheet keeps its * material — the same shape as glass being inert below iOS 26, and the same * trap: indistinguishable from the prop not working. Check the OS before * changing any code on a report of "the colour did nothing". */ presentationBackground: (color: string) => unknown; /** * Greys a control out and stops it answering. * * Optional because the module is cast whole rather than feature-checked, and * an older `@expo/ui` without it would otherwise be a crash instead of a * menu row that is merely still tappable. Call it through a guard. */ disabled?: (disabled: boolean) => unknown; /** * Fires when the view is put on screen, and when it is taken off again. * * The way to find out that a menu has opened. SwiftUI builds a menu's * content only once it is presented, so an item inside one appears exactly * when the menu does — which is the only signal the control gives, since it * owns its open state and reports nothing about it. * * Optional for the same reason as `disabled`: the module is cast whole, and * a version without these should lose the backdrop rather than crash. */ onAppear?: (handler: () => void) => unknown; onDisappear?: (handler: () => void) => unknown; } /** * The SwiftUI-only components, for the ones the universal set does not carry. * * `@expo/ui` exports a portable component for everything both toolkits agree * on, and that is what `getNativeUI` reaches. A popover is not on that list: * SwiftUI has one that anchors to a view and adapts itself on a compact * screen, and Compose's nearest relative is a dropdown menu, which is a * different control with different rules. Rather than pretend the two are one * thing, the iOS one is reached here and Android keeps the styled panel. */ interface SwiftUIComponents { Host: ComponentType<{ children?: ReactNode; matchContents?: boolean | { vertical?: boolean; horizontal?: boolean; }; ignoreSafeArea?: 'all' | 'container' | 'keyboard'; /** As on the portable host above — see `NativeHost`. */ colorScheme?: 'light' | 'dark'; style?: unknown; }>; RNHostView: ComponentType<{ children?: ReactNode; matchContents?: boolean; }>; Popover: ComponentType<{ children?: ReactNode; isPresented?: boolean; onIsPresentedChange?: (isPresented: boolean) => void; attachmentAnchor?: 'leading' | 'trailing' | 'center' | 'top' | 'bottom'; arrowEdge?: 'leading' | 'trailing' | 'top' | 'bottom' | 'none'; }> & { Trigger: ComponentType<{ children?: ReactNode; }>; Content: ComponentType<{ children?: ReactNode; }>; }; } export declare function getSwiftUI(): SwiftUIComponents | null; /** * A hosting boundary, as both toolkits declare it. The same three props matter * on either side — see `NativeUIModule.Host` above for what each one costs. */ type NativeHostComponent = ComponentType<{ children?: ReactNode; matchContents?: boolean | { vertical?: boolean; horizontal?: boolean; }; ignoreSafeArea?: 'all' | 'container' | 'keyboard'; colorScheme?: 'light' | 'dark'; style?: unknown; }>; type RNHostComponent = ComponentType<{ children?: ReactNode; matchContents?: boolean; style?: unknown; }>; /** * SwiftUI's menu, and the button that fills a row of it. * * Separate from `getSwiftUI` on purpose: that resolver refuses a module with no * popover in it, and a version that ships one control but not the other would * take the menu down with it. Each native path asks only for what it needs. */ interface SwiftUIMenuComponents { Host: NativeHostComponent; RNHostView: RNHostComponent; /** * `label` takes a React element as well as a string — the element is passed * through a native slot and becomes the thing you press. That is what lets * the trigger stay a real Fab rather than a platform button wearing its name. */ Menu: ComponentType<{ label?: ReactNode; children?: ReactNode; modifiers?: unknown[]; }>; Button: ComponentType<{ label?: string; systemImage?: string; role?: 'default' | 'cancel' | 'destructive'; onPress?: () => void; modifiers?: unknown[]; }>; } export declare function getSwiftUIMenu(): SwiftUIMenuComponents | null; /** * Compose's dropdown menu — the same instruction, answered by a different * control. * * It is not the shape SwiftUI's menu is. This one is controlled: it takes the * open state rather than owning it, and it splits the trigger and the items * into named slots instead of reading the label off a prop. Both differences * reach the caller, so they are written out here rather than smoothed over. */ interface ComposeMenuComponents { Host: NativeHostComponent; RNHostView: RNHostComponent; Text: ComponentType<{ children?: ReactNode; color?: string; }>; DropdownMenu: ComponentType<{ children?: ReactNode; expanded?: boolean; onDismissRequest?: () => void; }> & { Trigger: ComponentType<{ children?: ReactNode; }>; Items: ComponentType<{ children?: ReactNode; }>; }; DropdownMenuItem: ComponentType<{ children?: ReactNode; enabled?: boolean; onClick?: () => void; elementColors?: { textColor?: string; disabledTextColor?: string; }; }> & { Text: ComponentType<{ children?: ReactNode; }>; LeadingIcon: ComponentType<{ children?: ReactNode; }>; }; } export declare function getComposeMenu(): ComposeMenuComponents | null; export declare function getSwiftUIModifiers(): SwiftUIModifiers | null; /** * The Compose view modifiers — the other half of the same door. * * Android's toolkit has its own modifier system and its own vocabulary, so a * modifier built for one platform is not a modifier the other can read. Where * both are asked the same question the answers are written separately, here and * above, rather than one being sent to both. */ interface ComposeModifiers { background: (color: string) => unknown; } export declare function getComposeModifiers(): ComposeModifiers | null; //# sourceMappingURL=index.d.ts.map