first commit

This commit is contained in:
2026-03-10 16:18:05 +00:00
commit 11f9c069b5
31635 changed files with 3187747 additions and 0 deletions

View File

@@ -0,0 +1,108 @@
import { NativeStackNavigationOptions } from '@react-navigation/native-stack';
import { type ReactNode } from 'react';
import { type ColorValue, type StyleProp } from 'react-native';
import type { ScreenStackHeaderConfigProps } from 'react-native-screens';
export interface StackHeaderProps {
/**
* Child elements for custom header when `asChild` is true.
*/
children?: ReactNode;
/**
* Whether to hide the header completely. When set to `true`, the header will not be rendered.
*
* @default false
*/
hidden?: boolean;
/**
* When `true`, renders children as a custom header component, replacing the default header entirely.
* Use this to implement fully custom header layouts.
*
* @default false
*/
asChild?: boolean;
/**
* Whether the header should be transparent.
* When `true`, the header is absolutely positioned and content scrolls underneath.
*
* Auto-enabled when:
* - `style.backgroundColor` is 'transparent'
* - `blurEffect` is set (required for blur to work)
*
* @default false
*/
transparent?: boolean;
/**
* The blur effect to apply to the header background on iOS.
* Common values include 'regular', 'prominent', 'systemMaterial', etc.
*
* @platform ios
*/
blurEffect?: ScreenStackHeaderConfigProps['blurEffect'];
/**
* Style properties for the standard-sized header.
* - `color`: Tint color for header elements (similar to tintColor in React Navigation)
* - `backgroundColor`: Background color of the header
* - `shadowColor`: Set to 'transparent' to hide the header shadow/border
*/
style?: StyleProp<{
color?: ColorValue;
backgroundColor?: ScreenStackHeaderConfigProps['backgroundColor'];
shadowColor?: undefined | 'transparent';
}>;
/**
* Style properties for the large title header (iOS).
* - `backgroundColor`: Background color of the large title header
* - `shadowColor`: Set to 'transparent' to hide the large title shadow/border
*
* @platform ios
*/
largeStyle?: StyleProp<{
backgroundColor?: ScreenStackHeaderConfigProps['largeTitleBackgroundColor'];
shadowColor?: undefined | 'transparent';
}>;
}
/**
* The component used to configure header styling for a stack screen.
*
* Use this component to set header appearance properties like blur effect, background color,
* and shadow visibility.
*
* @example
* ```tsx
* import { Stack } from 'expo-router';
*
* export default function Page() {
* return (
* <>
* <Stack.Header
* blurEffect="systemMaterial"
* style={{ backgroundColor: '#fff' }}
* />
* <ScreenContent />
* </>
* );
* }
* ```
*
* @example
* When used inside a layout with Stack.Screen:
* ```tsx
* import { Stack } from 'expo-router';
*
* export default function Layout() {
* return (
* <Stack>
* <Stack.Screen name="index">
* <Stack.Header blurEffect="systemMaterial" />
* </Stack.Screen>
* </Stack>
* );
* }
* ```
*
* > **Note:** If multiple instances of this component are rendered for the same screen,
* the last one rendered in the component tree takes precedence.
*/
export declare function StackHeaderComponent({ children, hidden, asChild, transparent, blurEffect, style, largeStyle, }: StackHeaderProps): null;
export declare function appendStackHeaderPropsToOptions(options: NativeStackNavigationOptions, props: StackHeaderProps): NativeStackNavigationOptions;
//# sourceMappingURL=StackHeaderComponent.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"StackHeaderComponent.d.ts","sourceRoot":"","sources":["../../../src/layouts/stack-utils/StackHeaderComponent.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,4BAA4B,EAAE,MAAM,gCAAgC,CAAC;AAC9E,OAAO,EAAW,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAChD,OAAO,EAAc,KAAK,UAAU,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAC3E,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,sBAAsB,CAAC;AAIzE,MAAM,WAAW,gBAAgB;IAC/B;;OAEG;IACH,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;;;;;;OASG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,4BAA4B,CAAC,YAAY,CAAC,CAAC;IACxD;;;;;OAKG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC;QAChB,KAAK,CAAC,EAAE,UAAU,CAAC;QACnB,eAAe,CAAC,EAAE,4BAA4B,CAAC,iBAAiB,CAAC,CAAC;QAClE,WAAW,CAAC,EAAE,SAAS,GAAG,aAAa,CAAC;KACzC,CAAC,CAAC;IACH;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,SAAS,CAAC;QACrB,eAAe,CAAC,EAAE,4BAA4B,CAAC,2BAA2B,CAAC,CAAC;QAC5E,WAAW,CAAC,EAAE,SAAS,GAAG,aAAa,CAAC;KACzC,CAAC,CAAC;CACJ;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,wBAAgB,oBAAoB,CAAC,EACnC,QAAQ,EACR,MAAM,EACN,OAAO,EACP,WAAW,EACX,UAAU,EACV,KAAK,EACL,UAAU,GACX,EAAE,gBAAgB,QAelB;AAED,wBAAgB,+BAA+B,CAC7C,OAAO,EAAE,4BAA4B,EACrC,KAAK,EAAE,gBAAgB,GACtB,4BAA4B,CAiD9B"}

View File

@@ -0,0 +1,100 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.StackHeaderComponent = StackHeaderComponent;
exports.appendStackHeaderPropsToOptions = appendStackHeaderPropsToOptions;
const react_1 = require("react");
const react_native_1 = require("react-native");
const composition_options_1 = require("../../fork/native-stack/composition-options");
/**
* The component used to configure header styling for a stack screen.
*
* Use this component to set header appearance properties like blur effect, background color,
* and shadow visibility.
*
* @example
* ```tsx
* import { Stack } from 'expo-router';
*
* export default function Page() {
* return (
* <>
* <Stack.Header
* blurEffect="systemMaterial"
* style={{ backgroundColor: '#fff' }}
* />
* <ScreenContent />
* </>
* );
* }
* ```
*
* @example
* When used inside a layout with Stack.Screen:
* ```tsx
* import { Stack } from 'expo-router';
*
* export default function Layout() {
* return (
* <Stack>
* <Stack.Screen name="index">
* <Stack.Header blurEffect="systemMaterial" />
* </Stack.Screen>
* </Stack>
* );
* }
* ```
*
* > **Note:** If multiple instances of this component are rendered for the same screen,
* the last one rendered in the component tree takes precedence.
*/
function StackHeaderComponent({ children, hidden, asChild, transparent, blurEffect, style, largeStyle, }) {
const options = (0, react_1.useMemo)(() => appendStackHeaderPropsToOptions({},
// satisfies ensures every prop is listed here
{ children, hidden, asChild, transparent, blurEffect, style, largeStyle }), [children, hidden, asChild, transparent, blurEffect, style, largeStyle]);
(0, composition_options_1.useCompositionOption)(options);
return null;
}
function appendStackHeaderPropsToOptions(options, props) {
const flattenedStyle = react_native_1.StyleSheet.flatten(props.style);
const flattenedLargeStyle = react_native_1.StyleSheet.flatten(props.largeStyle);
if (props.hidden) {
return { ...options, headerShown: false };
}
if (props.asChild) {
return { ...options, header: () => props.children };
}
if (props.children && !props.asChild) {
console.warn(`To render a custom header, set the 'asChild' prop to true on Stack.Header.`);
}
// Determine if header should be transparent:
// 1. Explicitly set via `transparent` prop
// 2. Implicitly via backgroundColor === 'transparent'
// 3. Implicitly when blurEffect is set (required for blurEffect to work)
const isBackgroundTransparent = flattenedStyle?.backgroundColor === 'transparent';
const hasBlurEffect = props.blurEffect !== undefined;
const shouldBeTransparent = props.transparent === true ||
(props.transparent !== false && (isBackgroundTransparent || hasBlurEffect));
// Warn if blurEffect is set but transparent is explicitly false
if (props.blurEffect && props.transparent === false) {
console.warn(`Stack.Header: 'blurEffect' requires 'transparent' to be enabled.`);
}
return {
...options,
headerShown: !props.hidden,
headerBlurEffect: props.blurEffect,
...(shouldBeTransparent && { headerTransparent: true }),
...(props.transparent === false && { headerTransparent: false }),
...(flattenedStyle?.color && { headerTintColor: flattenedStyle.color }),
headerStyle: {
backgroundColor: flattenedStyle?.backgroundColor,
},
headerLargeStyle: {
backgroundColor: flattenedLargeStyle?.backgroundColor,
},
...(flattenedStyle?.shadowColor === 'transparent' && { headerShadowVisible: false }),
...(flattenedLargeStyle?.shadowColor === 'transparent' && {
headerLargeTitleShadowVisible: false,
}),
};
}
//# sourceMappingURL=StackHeaderComponent.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,100 @@
import type { ParamListBase, StackNavigationState } from '@react-navigation/native';
import type { NativeStackNavigationEventMap } from '@react-navigation/native-stack';
import { NativeStackNavigationOptions } from '@react-navigation/native-stack';
import { type PropsWithChildren } from 'react';
import { StackScreenTitle, StackScreenBackButton } from './screen';
import type { ScreenProps as BaseScreenProps } from '../../useScreens';
type StackBaseScreenProps = BaseScreenProps<NativeStackNavigationOptions, StackNavigationState<ParamListBase>, NativeStackNavigationEventMap>;
export interface StackScreenProps extends PropsWithChildren {
/** Name is required when used inside a Layout component. */
name?: StackBaseScreenProps['name'];
/**
* Options to configure the screen.
*
* Accepts an object or a function returning an object.
* The function form `options={({ route }) => ({})}` is only supported when used inside a Layout component.
* When used inside a page component, pass an options object directly.
*/
options?: StackBaseScreenProps['options'];
/**
* Redirect to the nearest sibling route.
* If all children are `redirect={true}`, the layout will render `null` as there are no children to render.
*
* Only supported when used inside a Layout component.
*/
redirect?: StackBaseScreenProps['redirect'];
/**
* Initial params to pass to the route.
*
* Only supported when used inside a Layout component.
*/
initialParams?: StackBaseScreenProps['initialParams'];
/**
* Listeners for navigation events.
*
* Only supported when used inside a Layout component.
*/
listeners?: StackBaseScreenProps['listeners'];
/**
* Function to determine a unique ID for the screen.
* @deprecated Use `dangerouslySingular` instead.
*
* Only supported when used inside a Layout component.
*/
getId?: StackBaseScreenProps['getId'];
/**
* When enabled, the navigator will reuse an existing screen instead of pushing a new one.
*
* Only supported when used inside a Layout component.
*/
dangerouslySingular?: StackBaseScreenProps['dangerouslySingular'];
}
/**
* Component used to define a screen in a native stack navigator.
*
* Can be used in the `_layout.tsx` files, or directly in page components.
*
* When configuring header inside page components, prefer using `Stack.Toolbar`, `Stack.Header` and `Stack.Screen.*` components.
*
* @example
* ```tsx app/_layout.tsx
* import { Stack } from 'expo-router';
*
* export default function Layout() {
* return (
* <Stack>
* <Stack.Screen
* name="home"
* options={{ title: 'Home' }}
* />
* </Stack>
* );
* }
* ```
*
* @example
* ```tsx app/home.tsx
* import { Stack } from 'expo-router';
*
* export default function HomePage() {
* return (
* <>
* <Stack.Screen
* options={{ headerTransparent: true }}
* />
* <Stack.Screen.Title>Welcome Home</Stack.Screen.Title>
* // Page content
* </>
* );
* }
* ```
*/
export declare const StackScreen: (({ children, options, ...rest }: StackScreenProps) => import("react").JSX.Element) & {
Title: typeof StackScreenTitle;
BackButton: typeof StackScreenBackButton;
};
export declare function validateStackPresentation(options: NativeStackNavigationOptions): NativeStackNavigationOptions;
export declare function validateStackPresentation<F extends (...args: never[]) => NativeStackNavigationOptions>(options: F): F;
export declare function appendScreenStackPropsToOptions(options: NativeStackNavigationOptions, props: StackScreenProps): NativeStackNavigationOptions;
export {};
//# sourceMappingURL=StackScreen.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"StackScreen.d.ts","sourceRoot":"","sources":["../../../src/layouts/stack-utils/StackScreen.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AACpF,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,gCAAgC,CAAC;AACpF,OAAO,EAAE,4BAA4B,EAAE,MAAM,gCAAgC,CAAC;AAC9E,OAAO,EAAqC,KAAK,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAGlF,OAAO,EACL,gBAAgB,EAEhB,qBAAqB,EAEtB,MAAM,UAAU,CAAC;AAElB,OAAO,KAAK,EAAE,WAAW,IAAI,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAIvE,KAAK,oBAAoB,GAAG,eAAe,CACzC,4BAA4B,EAC5B,oBAAoB,CAAC,aAAa,CAAC,EACnC,6BAA6B,CAC9B,CAAC;AAEF,MAAM,WAAW,gBAAiB,SAAQ,iBAAiB;IACzD,4DAA4D;IAC5D,IAAI,CAAC,EAAE,oBAAoB,CAAC,MAAM,CAAC,CAAC;IAEpC;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,oBAAoB,CAAC,SAAS,CAAC,CAAC;IAE1C;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,oBAAoB,CAAC,UAAU,CAAC,CAAC;IAE5C;;;;OAIG;IACH,aAAa,CAAC,EAAE,oBAAoB,CAAC,eAAe,CAAC,CAAC;IAEtD;;;;OAIG;IACH,SAAS,CAAC,EAAE,oBAAoB,CAAC,WAAW,CAAC,CAAC;IAE9C;;;;;OAKG;IACH,KAAK,CAAC,EAAE,oBAAoB,CAAC,OAAO,CAAC,CAAC;IAEtC;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,oBAAoB,CAAC,qBAAqB,CAAC,CAAC;CACnE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,eAAO,MAAM,WAAW,oCAC+B,gBAAgB;;;CAwBtE,CAAC;AAaF,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,4BAA4B,GACpC,4BAA4B,CAAC;AAChC,wBAAgB,yBAAyB,CACvC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,4BAA4B,EAC5D,OAAO,EAAE,CAAC,GAAG,CAAC,CAAC;AAwBjB,wBAAgB,+BAA+B,CAC7C,OAAO,EAAE,4BAA4B,EACrC,KAAK,EAAE,gBAAgB,GACtB,4BAA4B,CA6C9B"}

View File

@@ -0,0 +1,125 @@
"use strict";
'use client';
Object.defineProperty(exports, "__esModule", { value: true });
exports.StackScreen = void 0;
exports.validateStackPresentation = validateStackPresentation;
exports.appendScreenStackPropsToOptions = appendScreenStackPropsToOptions;
const react_1 = require("react");
const StackHeaderComponent_1 = require("./StackHeaderComponent");
const screen_1 = require("./screen");
const toolbar_1 = require("./toolbar");
const children_1 = require("../../utils/children");
const Screen_1 = require("../../views/Screen");
/**
* Component used to define a screen in a native stack navigator.
*
* Can be used in the `_layout.tsx` files, or directly in page components.
*
* When configuring header inside page components, prefer using `Stack.Toolbar`, `Stack.Header` and `Stack.Screen.*` components.
*
* @example
* ```tsx app/_layout.tsx
* import { Stack } from 'expo-router';
*
* export default function Layout() {
* return (
* <Stack>
* <Stack.Screen
* name="home"
* options={{ title: 'Home' }}
* />
* </Stack>
* );
* }
* ```
*
* @example
* ```tsx app/home.tsx
* import { Stack } from 'expo-router';
*
* export default function HomePage() {
* return (
* <>
* <Stack.Screen
* options={{ headerTransparent: true }}
* />
* <Stack.Screen.Title>Welcome Home</Stack.Screen.Title>
* // Page content
* </>
* );
* }
* ```
*/
exports.StackScreen = Object.assign(function StackScreen({ children, options, ...rest }) {
// This component will only render when used inside a page.
if (process.env.NODE_ENV !== 'production' && typeof options === 'function') {
console.warn('Stack.Screen: Function-form options are not supported inside page components. Pass an options object directly.');
}
const ownOptions = (0, react_1.useMemo)(() => validateStackPresentation(typeof options === 'function' ? {} : (options ?? {})), [options]);
return (<>
<Screen_1.Screen {...rest} options={ownOptions}/>
{children}
</>);
}, {
Title: screen_1.StackScreenTitle,
BackButton: screen_1.StackScreenBackButton,
});
const VALID_PRESENTATIONS = [
'card',
'modal',
'transparentModal',
'containedModal',
'containedTransparentModal',
'fullScreenModal',
'formSheet',
'pageSheet',
];
function validateStackPresentation(options) {
if (typeof options === 'function') {
return (...args) => {
const resolved = options(...args);
validateStackPresentation(resolved);
return resolved;
};
}
const presentation = options.presentation;
if (presentation &&
!VALID_PRESENTATIONS.includes(presentation)) {
throw new Error(`Invalid presentation value "${presentation}" passed to Stack.Screen. Valid values are: ${VALID_PRESENTATIONS.map((v) => `"${v}"`).join(', ')}.`);
}
return options;
}
function appendScreenStackPropsToOptions(options, props) {
let updatedOptions = { ...options, ...props.options };
validateStackPresentation(updatedOptions);
function appendChildOptions(child, opts) {
if ((0, children_1.isChildOfType)(child, StackHeaderComponent_1.StackHeaderComponent)) {
return (0, StackHeaderComponent_1.appendStackHeaderPropsToOptions)(opts, child.props);
}
if ((0, children_1.isChildOfType)(child, screen_1.StackScreenTitle)) {
return (0, screen_1.appendStackScreenTitlePropsToOptions)(opts, child.props);
}
if ((0, children_1.isChildOfType)(child, screen_1.StackScreenBackButton)) {
return (0, screen_1.appendStackScreenBackButtonPropsToOptions)(opts, child.props);
}
if ((0, children_1.isChildOfType)(child, toolbar_1.StackToolbar)) {
const placement = child.props.placement ?? 'bottom';
if (placement === 'bottom') {
throw new Error(`Stack.Toolbar with placement="bottom" cannot be used inside Stack.Screen.`);
}
return (0, toolbar_1.appendStackToolbarPropsToOptions)(opts, child.props);
}
const typeName = typeof child.type === 'function'
? child.type.name || 'Unknown'
: String(child.type);
console.warn(`Unknown child element passed to Stack.Screen: ${typeName}`);
return opts;
}
react_1.Children.forEach(props.children, (child) => {
if ((0, react_1.isValidElement)(child)) {
updatedOptions = appendChildOptions(child, updatedOptions);
}
});
return updatedOptions;
}
//# sourceMappingURL=StackScreen.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,33 @@
import { NativeStackNavigationOptions } from '@react-navigation/native-stack';
import type { SearchBarProps } from 'react-native-screens';
export interface StackSearchBarProps extends SearchBarProps {
}
/**
* A search bar component that integrates with the native stack header.
*
* > **Note:** Using `Stack.SearchBar` will automatically make the header visible
* (`headerShown: true`), as the search bar is rendered as part of the native header.
*
* To display the search bar in the bottom toolbar on iOS 26+, use
* `Stack.Toolbar.SearchBarSlot` inside `Stack.Toolbar`.
*
* @example
* ```tsx
* import { Stack } from 'expo-router';
*
* export default function Page() {
* return (
* <>
* <Stack.SearchBar
* placeholder="Search..."
* onChangeText={(text) => console.log(text)}
* />
* <ScreenContent />
* </>
* );
* }
* ```
*/
export declare function StackSearchBar({ ref, autoCapitalize, autoFocus, barTintColor, tintColor, cancelButtonText, disableBackButtonOverride, hideNavigationBar, hideWhenScrolling, inputType, obscureBackground, onBlur, onCancelButtonPress, onChangeText, onClose, onFocus, onOpen, onSearchButtonPress, placeholder, placement, allowToolbarIntegration, textColor, hintTextColor, headerIconColor, shouldShowHintSearchIcon, }: StackSearchBarProps): null;
export declare function appendStackSearchBarPropsToOptions(options: NativeStackNavigationOptions, props: StackSearchBarProps): NativeStackNavigationOptions;
//# sourceMappingURL=StackSearchBar.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"StackSearchBar.d.ts","sourceRoot":"","sources":["../../../src/layouts/stack-utils/StackSearchBar.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,4BAA4B,EAAE,MAAM,gCAAgC,CAAC;AAE9E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAO3D,MAAM,WAAW,mBAAoB,SAAQ,cAAc;CAAG;AAE9D;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,cAAc,CAAC,EAC7B,GAAG,EACH,cAAc,EACd,SAAS,EACT,YAAY,EACZ,SAAS,EACT,gBAAgB,EAChB,yBAAyB,EACzB,iBAAiB,EACjB,iBAAiB,EACjB,SAAS,EACT,iBAAiB,EACjB,MAAM,EACN,mBAAmB,EACnB,YAAY,EACZ,OAAO,EACP,OAAO,EACP,MAAM,EACN,mBAAmB,EACnB,WAAW,EACX,SAAS,EACT,uBAAuB,EACvB,SAAS,EACT,aAAa,EACb,eAAe,EACf,wBAAwB,GACzB,EAAE,mBAAmB,QAgErB;AAED,wBAAgB,kCAAkC,CAChD,OAAO,EAAE,4BAA4B,EACrC,KAAK,EAAE,mBAAmB,GACzB,4BAA4B,CAQ9B"}

View File

@@ -0,0 +1,101 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.StackSearchBar = StackSearchBar;
exports.appendStackSearchBarPropsToOptions = appendStackSearchBarPropsToOptions;
const react_1 = require("react");
const composition_options_1 = require("../../fork/native-stack/composition-options");
/**
* A search bar component that integrates with the native stack header.
*
* > **Note:** Using `Stack.SearchBar` will automatically make the header visible
* (`headerShown: true`), as the search bar is rendered as part of the native header.
*
* To display the search bar in the bottom toolbar on iOS 26+, use
* `Stack.Toolbar.SearchBarSlot` inside `Stack.Toolbar`.
*
* @example
* ```tsx
* import { Stack } from 'expo-router';
*
* export default function Page() {
* return (
* <>
* <Stack.SearchBar
* placeholder="Search..."
* onChangeText={(text) => console.log(text)}
* />
* <ScreenContent />
* </>
* );
* }
* ```
*/
function StackSearchBar({ ref, autoCapitalize, autoFocus, barTintColor, tintColor, cancelButtonText, disableBackButtonOverride, hideNavigationBar, hideWhenScrolling, inputType, obscureBackground, onBlur, onCancelButtonPress, onChangeText, onClose, onFocus, onOpen, onSearchButtonPress, placeholder, placement, allowToolbarIntegration, textColor, hintTextColor, headerIconColor, shouldShowHintSearchIcon, }) {
const options = (0, react_1.useMemo)(() => appendStackSearchBarPropsToOptions({},
// satisfies ensures every prop is listed here
{
ref,
autoCapitalize,
autoFocus,
barTintColor,
tintColor,
cancelButtonText,
disableBackButtonOverride,
hideNavigationBar,
hideWhenScrolling,
inputType,
obscureBackground,
onBlur,
onCancelButtonPress,
onChangeText,
onClose,
onFocus,
onOpen,
onSearchButtonPress,
placeholder,
placement,
allowToolbarIntegration,
textColor,
hintTextColor,
headerIconColor,
shouldShowHintSearchIcon,
}), [
ref,
autoCapitalize,
autoFocus,
barTintColor,
tintColor,
cancelButtonText,
disableBackButtonOverride,
hideNavigationBar,
hideWhenScrolling,
inputType,
obscureBackground,
onBlur,
onCancelButtonPress,
onChangeText,
onClose,
onFocus,
onOpen,
onSearchButtonPress,
placeholder,
placement,
allowToolbarIntegration,
textColor,
hintTextColor,
headerIconColor,
shouldShowHintSearchIcon,
]);
(0, composition_options_1.useCompositionOption)(options);
return null;
}
function appendStackSearchBarPropsToOptions(options, props) {
return {
...options,
headerShown: true,
headerSearchBarOptions: {
...props,
},
};
}
//# sourceMappingURL=StackSearchBar.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"StackSearchBar.js","sourceRoot":"","sources":["../../../src/layouts/stack-utils/StackSearchBar.tsx"],"names":[],"mappings":";;AAqCA,wCA0FC;AAED,gFAWC;AA3ID,iCAAgC;AAGhC,qFAAmF;AAOnF;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,SAAgB,cAAc,CAAC,EAC7B,GAAG,EACH,cAAc,EACd,SAAS,EACT,YAAY,EACZ,SAAS,EACT,gBAAgB,EAChB,yBAAyB,EACzB,iBAAiB,EACjB,iBAAiB,EACjB,SAAS,EACT,iBAAiB,EACjB,MAAM,EACN,mBAAmB,EACnB,YAAY,EACZ,OAAO,EACP,OAAO,EACP,MAAM,EACN,mBAAmB,EACnB,WAAW,EACX,SAAS,EACT,uBAAuB,EACvB,SAAS,EACT,aAAa,EACb,eAAe,EACf,wBAAwB,GACJ;IACpB,MAAM,OAAO,GAAG,IAAA,eAAO,EACrB,GAAG,EAAE,CACH,kCAAkC,CAChC,EAAE;IACF,8CAA8C;IAC9C;QACE,GAAG;QACH,cAAc;QACd,SAAS;QACT,YAAY;QACZ,SAAS;QACT,gBAAgB;QAChB,yBAAyB;QACzB,iBAAiB;QACjB,iBAAiB;QACjB,SAAS;QACT,iBAAiB;QACjB,MAAM;QACN,mBAAmB;QACnB,YAAY;QACZ,OAAO;QACP,OAAO;QACP,MAAM;QACN,mBAAmB;QACnB,WAAW;QACX,SAAS;QACT,uBAAuB;QACvB,SAAS;QACT,aAAa;QACb,eAAe;QACf,wBAAwB;KAC4B,CACvD,EACH;QACE,GAAG;QACH,cAAc;QACd,SAAS;QACT,YAAY;QACZ,SAAS;QACT,gBAAgB;QAChB,yBAAyB;QACzB,iBAAiB;QACjB,iBAAiB;QACjB,SAAS;QACT,iBAAiB;QACjB,MAAM;QACN,mBAAmB;QACnB,YAAY;QACZ,OAAO;QACP,OAAO;QACP,MAAM;QACN,mBAAmB;QACnB,WAAW;QACX,SAAS;QACT,uBAAuB;QACvB,SAAS;QACT,aAAa;QACb,eAAe;QACf,wBAAwB;KACzB,CACF,CAAC;IACF,IAAA,0CAAoB,EAAC,OAAO,CAAC,CAAC;IAC9B,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAgB,kCAAkC,CAChD,OAAqC,EACrC,KAA0B;IAE1B,OAAO;QACL,GAAG,OAAO;QACV,WAAW,EAAE,IAAI;QACjB,sBAAsB,EAAE;YACtB,GAAG,KAAK;SACT;KACF,CAAC;AACJ,CAAC","sourcesContent":["import { NativeStackNavigationOptions } from '@react-navigation/native-stack';\nimport { useMemo } from 'react';\nimport type { SearchBarProps } from 'react-native-screens';\n\nimport { useCompositionOption } from '../../fork/native-stack/composition-options';\n\n// TODO: Discuss adding SearchBarSlot to react-native-screens header items\n// and exposing it as Stack.Header.SearchBarPreferredSlot\n// https://linear.app/expo/issue/ENG-18555\nexport interface StackSearchBarProps extends SearchBarProps {}\n\n/**\n * A search bar component that integrates with the native stack header.\n *\n * > **Note:** Using `Stack.SearchBar` will automatically make the header visible\n * (`headerShown: true`), as the search bar is rendered as part of the native header.\n *\n * To display the search bar in the bottom toolbar on iOS 26+, use\n * `Stack.Toolbar.SearchBarSlot` inside `Stack.Toolbar`.\n *\n * @example\n * ```tsx\n * import { Stack } from 'expo-router';\n *\n * export default function Page() {\n * return (\n * <>\n * <Stack.SearchBar\n * placeholder=\"Search...\"\n * onChangeText={(text) => console.log(text)}\n * />\n * <ScreenContent />\n * </>\n * );\n * }\n * ```\n */\nexport function StackSearchBar({\n ref,\n autoCapitalize,\n autoFocus,\n barTintColor,\n tintColor,\n cancelButtonText,\n disableBackButtonOverride,\n hideNavigationBar,\n hideWhenScrolling,\n inputType,\n obscureBackground,\n onBlur,\n onCancelButtonPress,\n onChangeText,\n onClose,\n onFocus,\n onOpen,\n onSearchButtonPress,\n placeholder,\n placement,\n allowToolbarIntegration,\n textColor,\n hintTextColor,\n headerIconColor,\n shouldShowHintSearchIcon,\n}: StackSearchBarProps) {\n const options = useMemo(\n () =>\n appendStackSearchBarPropsToOptions(\n {},\n // satisfies ensures every prop is listed here\n {\n ref,\n autoCapitalize,\n autoFocus,\n barTintColor,\n tintColor,\n cancelButtonText,\n disableBackButtonOverride,\n hideNavigationBar,\n hideWhenScrolling,\n inputType,\n obscureBackground,\n onBlur,\n onCancelButtonPress,\n onChangeText,\n onClose,\n onFocus,\n onOpen,\n onSearchButtonPress,\n placeholder,\n placement,\n allowToolbarIntegration,\n textColor,\n hintTextColor,\n headerIconColor,\n shouldShowHintSearchIcon,\n } satisfies Record<keyof StackSearchBarProps, unknown>\n ),\n [\n ref,\n autoCapitalize,\n autoFocus,\n barTintColor,\n tintColor,\n cancelButtonText,\n disableBackButtonOverride,\n hideNavigationBar,\n hideWhenScrolling,\n inputType,\n obscureBackground,\n onBlur,\n onCancelButtonPress,\n onChangeText,\n onClose,\n onFocus,\n onOpen,\n onSearchButtonPress,\n placeholder,\n placement,\n allowToolbarIntegration,\n textColor,\n hintTextColor,\n headerIconColor,\n shouldShowHintSearchIcon,\n ]\n );\n useCompositionOption(options);\n return null;\n}\n\nexport function appendStackSearchBarPropsToOptions(\n options: NativeStackNavigationOptions,\n props: StackSearchBarProps\n): NativeStackNavigationOptions {\n return {\n ...options,\n headerShown: true,\n headerSearchBarOptions: {\n ...props,\n },\n };\n}\n"]}

View File

@@ -0,0 +1,9 @@
import { StackHeaderComponent } from './StackHeaderComponent';
export declare const StackHeader: typeof StackHeaderComponent;
export type { StackHeaderProps } from './StackHeaderComponent';
export { StackSearchBar, type StackSearchBarProps } from './StackSearchBar';
export * from './toolbar';
export * from './screen';
export { StackScreen, appendScreenStackPropsToOptions, validateStackPresentation, type StackScreenProps, } from './StackScreen';
export { mapProtectedScreen } from './mapProtectedScreen';
//# sourceMappingURL=index.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/layouts/stack-utils/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,eAAO,MAAM,WAAW,6BAAuB,CAAC;AAEhD,YAAY,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,EAAE,cAAc,EAAE,KAAK,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAE5E,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC;AAEzB,OAAO,EACL,WAAW,EACX,+BAA+B,EAC/B,yBAAyB,EACzB,KAAK,gBAAgB,GACtB,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC"}

View File

@@ -0,0 +1,30 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.mapProtectedScreen = exports.validateStackPresentation = exports.appendScreenStackPropsToOptions = exports.StackScreen = exports.StackSearchBar = exports.StackHeader = void 0;
const StackHeaderComponent_1 = require("./StackHeaderComponent");
exports.StackHeader = StackHeaderComponent_1.StackHeaderComponent;
var StackSearchBar_1 = require("./StackSearchBar");
Object.defineProperty(exports, "StackSearchBar", { enumerable: true, get: function () { return StackSearchBar_1.StackSearchBar; } });
__exportStar(require("./toolbar"), exports);
__exportStar(require("./screen"), exports);
var StackScreen_1 = require("./StackScreen");
Object.defineProperty(exports, "StackScreen", { enumerable: true, get: function () { return StackScreen_1.StackScreen; } });
Object.defineProperty(exports, "appendScreenStackPropsToOptions", { enumerable: true, get: function () { return StackScreen_1.appendScreenStackPropsToOptions; } });
Object.defineProperty(exports, "validateStackPresentation", { enumerable: true, get: function () { return StackScreen_1.validateStackPresentation; } });
var mapProtectedScreen_1 = require("./mapProtectedScreen");
Object.defineProperty(exports, "mapProtectedScreen", { enumerable: true, get: function () { return mapProtectedScreen_1.mapProtectedScreen; } });
//# sourceMappingURL=index.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/layouts/stack-utils/index.tsx"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA,iEAA8D;AACjD,QAAA,WAAW,GAAG,2CAAoB,CAAC;AAGhD,mDAA4E;AAAnE,gHAAA,cAAc,OAAA;AAEvB,4CAA0B;AAC1B,2CAAyB;AAEzB,6CAKuB;AAJrB,0GAAA,WAAW,OAAA;AACX,8HAAA,+BAA+B,OAAA;AAC/B,wHAAA,yBAAyB,OAAA;AAI3B,2DAA0D;AAAjD,wHAAA,kBAAkB,OAAA","sourcesContent":["import { StackHeaderComponent } from './StackHeaderComponent';\nexport const StackHeader = StackHeaderComponent;\n\nexport type { StackHeaderProps } from './StackHeaderComponent';\nexport { StackSearchBar, type StackSearchBarProps } from './StackSearchBar';\n\nexport * from './toolbar';\nexport * from './screen';\n\nexport {\n StackScreen,\n appendScreenStackPropsToOptions,\n validateStackPresentation,\n type StackScreenProps,\n} from './StackScreen';\n\nexport { mapProtectedScreen } from './mapProtectedScreen';\n"]}

View File

@@ -0,0 +1,3 @@
import { type ProtectedProps } from '../../views/Protected';
export declare function mapProtectedScreen(props: ProtectedProps): ProtectedProps;
//# sourceMappingURL=mapProtectedScreen.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"mapProtectedScreen.d.ts","sourceRoot":"","sources":["../../../src/layouts/stack-utils/mapProtectedScreen.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAa,KAAK,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAGvE,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,cAAc,GAAG,cAAc,CA8BxE"}

View File

@@ -0,0 +1,76 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.mapProtectedScreen = mapProtectedScreen;
const react_1 = __importStar(require("react"));
const StackHeaderComponent_1 = require("./StackHeaderComponent");
const StackScreen_1 = require("./StackScreen");
const children_1 = require("../../utils/children");
const Protected_1 = require("../../views/Protected");
const Screen_1 = require("../../views/Screen");
function mapProtectedScreen(props) {
return {
...props,
children: react_1.Children.toArray(props.children)
.map((child, index) => {
if ((0, children_1.isChildOfType)(child, StackScreen_1.StackScreen)) {
const { children, options: childOptions, ...rest } = child.props;
const options = typeof childOptions === 'function'
? (...params) => (0, StackScreen_1.appendScreenStackPropsToOptions)(childOptions(...params), { children })
: (0, StackScreen_1.appendScreenStackPropsToOptions)(childOptions ?? {}, { children });
return <Screen_1.Screen key={rest.name} {...rest} options={options}/>;
}
else if ((0, children_1.isChildOfType)(child, Protected_1.Protected)) {
return <Protected_1.Protected key={`${index}-${props.guard}`} {...mapProtectedScreen(child.props)}/>;
}
else if ((0, children_1.isChildOfType)(child, StackHeaderComponent_1.StackHeaderComponent)) {
// Ignore Stack.Header, because it can be used to set header options for Stack
// and we use this function to process children of Stack, as well.
return null;
}
else {
if (react_1.default.isValidElement(child)) {
console.warn(`Unknown child element passed to Stack: ${child.type}`);
}
else {
console.warn(`Unknown child element passed to Stack: ${child}`);
}
}
return null;
})
.filter(Boolean),
};
}
//# sourceMappingURL=mapProtectedScreen.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"mapProtectedScreen.js","sourceRoot":"","sources":["../../../src/layouts/stack-utils/mapProtectedScreen.tsx"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAQA,gDA8BC;AAtCD,+CAAwC;AAExC,iEAA8D;AAC9D,+CAA6E;AAC7E,mDAAqD;AACrD,qDAAuE;AACvE,+CAA4C;AAE5C,SAAgB,kBAAkB,CAAC,KAAqB;IACtD,OAAO;QACL,GAAG,KAAK;QACR,QAAQ,EAAE,gBAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC;aACvC,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;YACpB,IAAI,IAAA,wBAAa,EAAC,KAAK,EAAE,yBAAW,CAAC,EAAE,CAAC;gBACtC,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,YAAY,EAAE,GAAG,IAAI,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC;gBACjE,MAAM,OAAO,GACX,OAAO,YAAY,KAAK,UAAU;oBAChC,CAAC,CAAC,CAAC,GAAG,MAAuC,EAAE,EAAE,CAC7C,IAAA,6CAA+B,EAAC,YAAY,CAAC,GAAG,MAAM,CAAC,EAAE,EAAE,QAAQ,EAAE,CAAC;oBAC1E,CAAC,CAAC,IAAA,6CAA+B,EAAC,YAAY,IAAI,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC;gBACxE,OAAO,CAAC,eAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,EAAG,CAAC;YAChE,CAAC;iBAAM,IAAI,IAAA,wBAAa,EAAC,KAAK,EAAE,qBAAS,CAAC,EAAE,CAAC;gBAC3C,OAAO,CAAC,qBAAS,CAAC,GAAG,CAAC,CAAC,GAAG,KAAK,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,IAAI,kBAAkB,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAG,CAAC;YAC5F,CAAC;iBAAM,IAAI,IAAA,wBAAa,EAAC,KAAK,EAAE,2CAAoB,CAAC,EAAE,CAAC;gBACtD,8EAA8E;gBAC9E,kEAAkE;gBAClE,OAAO,IAAI,CAAC;YACd,CAAC;iBAAM,CAAC;gBACN,IAAI,eAAK,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,CAAC;oBAChC,OAAO,CAAC,IAAI,CAAC,0CAA0C,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;gBACvE,CAAC;qBAAM,CAAC;oBACN,OAAO,CAAC,IAAI,CAAC,0CAA0C,KAAK,EAAE,CAAC,CAAC;gBAClE,CAAC;YACH,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC,CAAC;aACD,MAAM,CAAC,OAAO,CAAC;KACnB,CAAC;AACJ,CAAC","sourcesContent":["import React, { Children } from 'react';\n\nimport { StackHeaderComponent } from './StackHeaderComponent';\nimport { StackScreen, appendScreenStackPropsToOptions } from './StackScreen';\nimport { isChildOfType } from '../../utils/children';\nimport { Protected, type ProtectedProps } from '../../views/Protected';\nimport { Screen } from '../../views/Screen';\n\nexport function mapProtectedScreen(props: ProtectedProps): ProtectedProps {\n return {\n ...props,\n children: Children.toArray(props.children)\n .map((child, index) => {\n if (isChildOfType(child, StackScreen)) {\n const { children, options: childOptions, ...rest } = child.props;\n const options =\n typeof childOptions === 'function'\n ? (...params: Parameters<typeof childOptions>) =>\n appendScreenStackPropsToOptions(childOptions(...params), { children })\n : appendScreenStackPropsToOptions(childOptions ?? {}, { children });\n return <Screen key={rest.name} {...rest} options={options} />;\n } else if (isChildOfType(child, Protected)) {\n return <Protected key={`${index}-${props.guard}`} {...mapProtectedScreen(child.props)} />;\n } else if (isChildOfType(child, StackHeaderComponent)) {\n // Ignore Stack.Header, because it can be used to set header options for Stack\n // and we use this function to process children of Stack, as well.\n return null;\n } else {\n if (React.isValidElement(child)) {\n console.warn(`Unknown child element passed to Stack: ${child.type}`);\n } else {\n console.warn(`Unknown child element passed to Stack: ${child}`);\n }\n }\n return null;\n })\n .filter(Boolean),\n };\n}\n"]}

View File

@@ -0,0 +1,73 @@
import { NativeStackNavigationOptions } from '@react-navigation/native-stack';
import type { ImageSourcePropType } from 'react-native';
import type { ScreenStackHeaderConfigProps } from 'react-native-screens';
export interface StackScreenBackButtonProps {
/**
* The title to display for the back button.
*/
children?: string;
/**
* Style for the back button title.
*/
style?: NativeStackNavigationOptions['headerBackTitleStyle'];
/**
* Whether to show a context menu when long pressing the back button.
*
* @platform ios
*/
withMenu?: boolean;
/**
* The display mode for the back button.
*
* @platform ios
*/
displayMode?: ScreenStackHeaderConfigProps['backButtonDisplayMode'];
/**
* Whether to hide the back button.
*/
hidden?: boolean;
/**
* Custom image source for the back button.
*/
src?: ImageSourcePropType;
}
/**
* Component to configure the back button.
*
* Can be used inside Stack.Screen in a layout or directly inside a screen component.
*
* @example
* ```tsx
* import { Stack } from 'expo-router';
*
* export default function Layout() {
* return (
* <Stack>
* <Stack.Screen name="detail">
* <Stack.Screen.BackButton displayMode="minimal">Back</Stack.Screen.BackButton>
* </Stack.Screen>
* </Stack>
* );
* }
* ```
*
* @example
* ```tsx
* import { Stack } from 'expo-router';
*
* export default function Page() {
* return (
* <>
* <Stack.Screen.BackButton hidden />
* <ScreenContent />
* </>
* );
* }
* ```
*
* > **Note:** If multiple instances of this component are rendered for the same screen,
* the last one rendered in the component tree takes precedence.
*/
export declare function StackScreenBackButton({ children, style, withMenu, displayMode, hidden, src, }: StackScreenBackButtonProps): null;
export declare function appendStackScreenBackButtonPropsToOptions(options: NativeStackNavigationOptions, props: StackScreenBackButtonProps): NativeStackNavigationOptions;
//# sourceMappingURL=StackScreenBackButton.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"StackScreenBackButton.d.ts","sourceRoot":"","sources":["../../../../src/layouts/stack-utils/screen/StackScreenBackButton.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,4BAA4B,EAAE,MAAM,gCAAgC,CAAC;AAE9E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACxD,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,sBAAsB,CAAC;AAIzE,MAAM,WAAW,0BAA0B;IACzC;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;OAEG;IACH,KAAK,CAAC,EAAE,4BAA4B,CAAC,sBAAsB,CAAC,CAAC;IAC7D;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;OAIG;IACH,WAAW,CAAC,EAAE,4BAA4B,CAAC,uBAAuB,CAAC,CAAC;IACpE;;OAEG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;OAEG;IACH,GAAG,CAAC,EAAE,mBAAmB,CAAC;CAC3B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAgB,qBAAqB,CAAC,EACpC,QAAQ,EACR,KAAK,EACL,QAAQ,EACR,WAAW,EACX,MAAM,EACN,GAAG,GACJ,EAAE,0BAA0B,QAe5B;AAED,wBAAgB,yCAAyC,CACvD,OAAO,EAAE,4BAA4B,EACrC,KAAK,EAAE,0BAA0B,GAChC,4BAA4B,CAU9B"}

View File

@@ -0,0 +1,62 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.StackScreenBackButton = StackScreenBackButton;
exports.appendStackScreenBackButtonPropsToOptions = appendStackScreenBackButtonPropsToOptions;
const react_1 = require("react");
const composition_options_1 = require("../../../fork/native-stack/composition-options");
/**
* Component to configure the back button.
*
* Can be used inside Stack.Screen in a layout or directly inside a screen component.
*
* @example
* ```tsx
* import { Stack } from 'expo-router';
*
* export default function Layout() {
* return (
* <Stack>
* <Stack.Screen name="detail">
* <Stack.Screen.BackButton displayMode="minimal">Back</Stack.Screen.BackButton>
* </Stack.Screen>
* </Stack>
* );
* }
* ```
*
* @example
* ```tsx
* import { Stack } from 'expo-router';
*
* export default function Page() {
* return (
* <>
* <Stack.Screen.BackButton hidden />
* <ScreenContent />
* </>
* );
* }
* ```
*
* > **Note:** If multiple instances of this component are rendered for the same screen,
* the last one rendered in the component tree takes precedence.
*/
function StackScreenBackButton({ children, style, withMenu, displayMode, hidden, src, }) {
const options = (0, react_1.useMemo)(() => appendStackScreenBackButtonPropsToOptions({},
// satisfies ensures every prop is listed here
{ children, style, withMenu, displayMode, hidden, src }), [children, style, withMenu, displayMode, hidden, src]);
(0, composition_options_1.useCompositionOption)(options);
return null;
}
function appendStackScreenBackButtonPropsToOptions(options, props) {
return {
...options,
headerBackTitle: props.children,
headerBackTitleStyle: props.style,
headerBackImageSource: props.src,
headerBackButtonDisplayMode: props.displayMode,
headerBackButtonMenuEnabled: props.withMenu,
headerBackVisible: !props.hidden,
};
}
//# sourceMappingURL=StackScreenBackButton.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"StackScreenBackButton.js","sourceRoot":"","sources":["../../../../src/layouts/stack-utils/screen/StackScreenBackButton.tsx"],"names":[],"mappings":";;AA2EA,sDAsBC;AAED,8FAaC;AA/GD,iCAAgC;AAIhC,wFAAsF;AAiCtF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,SAAgB,qBAAqB,CAAC,EACpC,QAAQ,EACR,KAAK,EACL,QAAQ,EACR,WAAW,EACX,MAAM,EACN,GAAG,GACwB;IAC3B,MAAM,OAAO,GAAG,IAAA,eAAO,EACrB,GAAG,EAAE,CACH,yCAAyC,CACvC,EAAE;IACF,8CAA8C;IAC9C,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,EAGpD,CACF,EACH,CAAC,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,CAAC,CACtD,CAAC;IACF,IAAA,0CAAoB,EAAC,OAAO,CAAC,CAAC;IAC9B,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAgB,yCAAyC,CACvD,OAAqC,EACrC,KAAiC;IAEjC,OAAO;QACL,GAAG,OAAO;QACV,eAAe,EAAE,KAAK,CAAC,QAAQ;QAC/B,oBAAoB,EAAE,KAAK,CAAC,KAAK;QACjC,qBAAqB,EAAE,KAAK,CAAC,GAAG;QAChC,2BAA2B,EAAE,KAAK,CAAC,WAAW;QAC9C,2BAA2B,EAAE,KAAK,CAAC,QAAQ;QAC3C,iBAAiB,EAAE,CAAC,KAAK,CAAC,MAAM;KACjC,CAAC;AACJ,CAAC","sourcesContent":["import { NativeStackNavigationOptions } from '@react-navigation/native-stack';\nimport { useMemo } from 'react';\nimport type { ImageSourcePropType } from 'react-native';\nimport type { ScreenStackHeaderConfigProps } from 'react-native-screens';\n\nimport { useCompositionOption } from '../../../fork/native-stack/composition-options';\n\nexport interface StackScreenBackButtonProps {\n /**\n * The title to display for the back button.\n */\n children?: string;\n /**\n * Style for the back button title.\n */\n style?: NativeStackNavigationOptions['headerBackTitleStyle'];\n /**\n * Whether to show a context menu when long pressing the back button.\n *\n * @platform ios\n */\n withMenu?: boolean;\n /**\n * The display mode for the back button.\n *\n * @platform ios\n */\n displayMode?: ScreenStackHeaderConfigProps['backButtonDisplayMode'];\n /**\n * Whether to hide the back button.\n */\n hidden?: boolean;\n /**\n * Custom image source for the back button.\n */\n src?: ImageSourcePropType;\n}\n\n/**\n * Component to configure the back button.\n *\n * Can be used inside Stack.Screen in a layout or directly inside a screen component.\n *\n * @example\n * ```tsx\n * import { Stack } from 'expo-router';\n *\n * export default function Layout() {\n * return (\n * <Stack>\n * <Stack.Screen name=\"detail\">\n * <Stack.Screen.BackButton displayMode=\"minimal\">Back</Stack.Screen.BackButton>\n * </Stack.Screen>\n * </Stack>\n * );\n * }\n * ```\n *\n * @example\n * ```tsx\n * import { Stack } from 'expo-router';\n *\n * export default function Page() {\n * return (\n * <>\n * <Stack.Screen.BackButton hidden />\n * <ScreenContent />\n * </>\n * );\n * }\n * ```\n *\n * > **Note:** If multiple instances of this component are rendered for the same screen,\n * the last one rendered in the component tree takes precedence.\n */\nexport function StackScreenBackButton({\n children,\n style,\n withMenu,\n displayMode,\n hidden,\n src,\n}: StackScreenBackButtonProps) {\n const options = useMemo(\n () =>\n appendStackScreenBackButtonPropsToOptions(\n {},\n // satisfies ensures every prop is listed here\n { children, style, withMenu, displayMode, hidden, src } satisfies Record<\n keyof StackScreenBackButtonProps,\n unknown\n >\n ),\n [children, style, withMenu, displayMode, hidden, src]\n );\n useCompositionOption(options);\n return null;\n}\n\nexport function appendStackScreenBackButtonPropsToOptions(\n options: NativeStackNavigationOptions,\n props: StackScreenBackButtonProps\n): NativeStackNavigationOptions {\n return {\n ...options,\n headerBackTitle: props.children,\n headerBackTitleStyle: props.style,\n headerBackImageSource: props.src,\n headerBackButtonDisplayMode: props.displayMode,\n headerBackButtonMenuEnabled: props.withMenu,\n headerBackVisible: !props.hidden,\n };\n}\n"]}

View File

@@ -0,0 +1,105 @@
import { NativeStackNavigationOptions } from '@react-navigation/native-stack';
import React from 'react';
import { type StyleProp, type TextStyle } from 'react-native';
export type StackScreenTitleProps = {
/**
* The title content. Pass a string for a plain text title,
* or a custom component when `asChild` is enabled.
*/
children?: React.ReactNode;
/**
* Use this to render a custom component as the header title.
*
* @example
* ```tsx
* <Stack.Screen.Title asChild>
* <MyCustomTitle />
* </Stack.Screen.Title>
* ```
*/
asChild?: boolean;
style?: StyleProp<{
fontFamily?: TextStyle['fontFamily'];
fontSize?: TextStyle['fontSize'];
fontWeight?: Exclude<TextStyle['fontWeight'], number>;
color?: string;
textAlign?: 'left' | 'center';
}>;
/**
* Style properties for the large title header.
*
* @platform ios
*/
largeStyle?: StyleProp<{
fontFamily?: TextStyle['fontFamily'];
fontSize?: TextStyle['fontSize'];
fontWeight?: Exclude<TextStyle['fontWeight'], number>;
color?: string;
}>;
/**
* Enables large title mode.
*
* @platform ios
*/
large?: boolean;
};
/**
* Component to set the screen title.
*
* Can be used inside Stack.Screen in a layout or directly inside a screen component.
*
* @example
* String title in a layout:
* ```tsx
* import { Stack } from 'expo-router';
*
* export default function Layout() {
* return (
* <Stack>
* <Stack.Screen name="index">
* <Stack.Screen.Title large>Home</Stack.Screen.Title>
* </Stack.Screen>
* </Stack>
* );
* }
* ```
*
* @example
* String title inside a screen:
* ```tsx
* import { Stack } from 'expo-router';
*
* export default function Page() {
* return (
* <>
* <Stack.Screen.Title>My Page</Stack.Screen.Title>
* <ScreenContent />
* </>
* );
* }
* ```
*
* @example
* Custom component as the title using `asChild`:
* ```tsx
* import { Stack } from 'expo-router';
*
* export default function Layout() {
* return (
* <Stack>
* <Stack.Screen name="index">
* <Stack.Screen.Title asChild>
* <MyCustomTitle />
* </Stack.Screen.Title>
* </Stack.Screen>
* </Stack>
* );
* }
* ```
*
* > **Note:** If multiple instances of this component are rendered for the same screen,
* the last one rendered in the component tree takes precedence.
*/
export declare function StackScreenTitle({ children, asChild, style, largeStyle, large, }: StackScreenTitleProps): null;
export declare function appendStackScreenTitlePropsToOptions(options: NativeStackNavigationOptions, props: StackScreenTitleProps): NativeStackNavigationOptions;
//# sourceMappingURL=StackScreenTitle.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"StackScreenTitle.d.ts","sourceRoot":"","sources":["../../../../src/layouts/stack-utils/screen/StackScreenTitle.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,4BAA4B,EAAE,MAAM,gCAAgC,CAAC;AAC9E,OAAO,KAAkB,MAAM,OAAO,CAAC;AACvC,OAAO,EAAc,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAK1E,MAAM,MAAM,qBAAqB,GAAG;IAClC;;;OAGG;IACH,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B;;;;;;;;;OASG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,SAAS,CAAC;QAChB,UAAU,CAAC,EAAE,SAAS,CAAC,YAAY,CAAC,CAAC;QACrC,QAAQ,CAAC,EAAE,SAAS,CAAC,UAAU,CAAC,CAAC;QACjC,UAAU,CAAC,EAAE,OAAO,CAAC,SAAS,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC,CAAC;QAGtD,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,SAAS,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;KAC/B,CAAC,CAAC;IACH;;;;OAIG;IACH,UAAU,CAAC,EAAE,SAAS,CAAC;QACrB,UAAU,CAAC,EAAE,SAAS,CAAC,YAAY,CAAC,CAAC;QACrC,QAAQ,CAAC,EAAE,SAAS,CAAC,UAAU,CAAC,CAAC;QACjC,UAAU,CAAC,EAAE,OAAO,CAAC,SAAS,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC,CAAC;QAGtD,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC,CAAC;IACH;;;;OAIG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwDG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,QAAQ,EACR,OAAO,EACP,KAAK,EACL,UAAU,EACV,KAAK,GACN,EAAE,qBAAqB,QAevB;AAED,wBAAgB,oCAAoC,CAClD,OAAO,EAAE,4BAA4B,EACrC,KAAK,EAAE,qBAAqB,GAC3B,4BAA4B,CA+C9B"}

View File

@@ -0,0 +1,147 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.StackScreenTitle = StackScreenTitle;
exports.appendStackScreenTitlePropsToOptions = appendStackScreenTitlePropsToOptions;
const react_1 = __importStar(require("react"));
const react_native_1 = require("react-native");
const composition_options_1 = require("../../../fork/native-stack/composition-options");
const style_1 = require("../../../utils/style");
/**
* Component to set the screen title.
*
* Can be used inside Stack.Screen in a layout or directly inside a screen component.
*
* @example
* String title in a layout:
* ```tsx
* import { Stack } from 'expo-router';
*
* export default function Layout() {
* return (
* <Stack>
* <Stack.Screen name="index">
* <Stack.Screen.Title large>Home</Stack.Screen.Title>
* </Stack.Screen>
* </Stack>
* );
* }
* ```
*
* @example
* String title inside a screen:
* ```tsx
* import { Stack } from 'expo-router';
*
* export default function Page() {
* return (
* <>
* <Stack.Screen.Title>My Page</Stack.Screen.Title>
* <ScreenContent />
* </>
* );
* }
* ```
*
* @example
* Custom component as the title using `asChild`:
* ```tsx
* import { Stack } from 'expo-router';
*
* export default function Layout() {
* return (
* <Stack>
* <Stack.Screen name="index">
* <Stack.Screen.Title asChild>
* <MyCustomTitle />
* </Stack.Screen.Title>
* </Stack.Screen>
* </Stack>
* );
* }
* ```
*
* > **Note:** If multiple instances of this component are rendered for the same screen,
* the last one rendered in the component tree takes precedence.
*/
function StackScreenTitle({ children, asChild, style, largeStyle, large, }) {
const options = (0, react_1.useMemo)(() => appendStackScreenTitlePropsToOptions({},
// satisfies ensures every prop is listed here
{ children, asChild, style, largeStyle, large }), [children, asChild, style, largeStyle, large]);
(0, composition_options_1.useCompositionOption)(options);
return null;
}
function appendStackScreenTitlePropsToOptions(options, props) {
const flattenedStyle = react_native_1.StyleSheet.flatten(props.style);
const flattenedLargeStyle = react_native_1.StyleSheet.flatten(props.largeStyle);
let titleOptions = props.asChild
? { headerTitle: () => <>{props.children}</> }
: { title: props.children };
if (props.asChild && typeof props.children === 'string') {
if (__DEV__) {
console.warn("Stack.Screen.Title: 'asChild' expects a custom component as children, string received.");
}
titleOptions = {};
}
if (!props.asChild && props.children != null && typeof props.children !== 'string') {
if (__DEV__) {
console.warn('Stack.Screen.Title: Component passed to Stack.Screen.Title without `asChild` enabled. In order to render a custom component as the title, set `asChild` to true.');
}
titleOptions = {};
}
return {
...options,
...titleOptions,
headerLargeTitle: props.large,
headerTitleAlign: flattenedStyle?.textAlign,
headerTitleStyle: {
...flattenedStyle,
...(flattenedStyle?.fontWeight
? {
fontWeight: (0, style_1.convertFontWeightToStringFontWeight)(flattenedStyle?.fontWeight),
}
: {}),
},
headerLargeTitleStyle: {
...flattenedLargeStyle,
...(flattenedLargeStyle?.fontWeight
? {
fontWeight: (0, style_1.convertFontWeightToStringFontWeight)(flattenedLargeStyle?.fontWeight),
}
: {}),
},
};
}
//# sourceMappingURL=StackScreenTitle.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,3 @@
export { StackScreenTitle, appendStackScreenTitlePropsToOptions, type StackScreenTitleProps, } from './StackScreenTitle';
export { StackScreenBackButton, appendStackScreenBackButtonPropsToOptions, type StackScreenBackButtonProps, } from './StackScreenBackButton';
//# sourceMappingURL=index.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/layouts/stack-utils/screen/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,oCAAoC,EACpC,KAAK,qBAAqB,GAC3B,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,qBAAqB,EACrB,yCAAyC,EACzC,KAAK,0BAA0B,GAChC,MAAM,yBAAyB,CAAC"}

View File

@@ -0,0 +1,10 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.appendStackScreenBackButtonPropsToOptions = exports.StackScreenBackButton = exports.appendStackScreenTitlePropsToOptions = exports.StackScreenTitle = void 0;
var StackScreenTitle_1 = require("./StackScreenTitle");
Object.defineProperty(exports, "StackScreenTitle", { enumerable: true, get: function () { return StackScreenTitle_1.StackScreenTitle; } });
Object.defineProperty(exports, "appendStackScreenTitlePropsToOptions", { enumerable: true, get: function () { return StackScreenTitle_1.appendStackScreenTitlePropsToOptions; } });
var StackScreenBackButton_1 = require("./StackScreenBackButton");
Object.defineProperty(exports, "StackScreenBackButton", { enumerable: true, get: function () { return StackScreenBackButton_1.StackScreenBackButton; } });
Object.defineProperty(exports, "appendStackScreenBackButtonPropsToOptions", { enumerable: true, get: function () { return StackScreenBackButton_1.appendStackScreenBackButtonPropsToOptions; } });
//# sourceMappingURL=index.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/layouts/stack-utils/screen/index.tsx"],"names":[],"mappings":";;;AAAA,uDAI4B;AAH1B,oHAAA,gBAAgB,OAAA;AAChB,wIAAA,oCAAoC,OAAA;AAItC,iEAIiC;AAH/B,8HAAA,qBAAqB,OAAA;AACrB,kJAAA,yCAAyC,OAAA","sourcesContent":["export {\n StackScreenTitle,\n appendStackScreenTitlePropsToOptions,\n type StackScreenTitleProps,\n} from './StackScreenTitle';\n\nexport {\n StackScreenBackButton,\n appendStackScreenBackButtonPropsToOptions,\n type StackScreenBackButtonProps,\n} from './StackScreenBackButton';\n"]}

View File

@@ -0,0 +1,3 @@
import StackToolbar from './StackToolbarClient';
export { StackToolbar };
//# sourceMappingURL=StackToolbar.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"StackToolbar.d.ts","sourceRoot":"","sources":["../../../../src/layouts/stack-utils/toolbar/StackToolbar.tsx"],"names":[],"mappings":"AACA,OAAO,YAAY,MAAM,sBAAsB,CAAC;AAiBhD,OAAO,EAAE,YAAY,EAAE,CAAC"}

View File

@@ -0,0 +1,24 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.StackToolbar = void 0;
const StackToolbarButton_1 = require("./StackToolbarButton");
const StackToolbarClient_1 = __importDefault(require("./StackToolbarClient"));
exports.StackToolbar = StackToolbarClient_1.default;
const StackToolbarMenu_1 = require("./StackToolbarMenu");
const StackToolbarSearchBarSlot_1 = require("./StackToolbarSearchBarSlot");
const StackToolbarSpacer_1 = require("./StackToolbarSpacer");
const StackToolbarView_1 = require("./StackToolbarView");
const toolbar_primitives_1 = require("./toolbar-primitives");
StackToolbarClient_1.default.Button = StackToolbarButton_1.StackToolbarButton;
StackToolbarClient_1.default.Menu = StackToolbarMenu_1.StackToolbarMenu;
StackToolbarClient_1.default.MenuAction = StackToolbarMenu_1.StackToolbarMenuAction;
StackToolbarClient_1.default.SearchBarSlot = StackToolbarSearchBarSlot_1.StackToolbarSearchBarSlot;
StackToolbarClient_1.default.Spacer = StackToolbarSpacer_1.StackToolbarSpacer;
StackToolbarClient_1.default.View = StackToolbarView_1.StackToolbarView;
StackToolbarClient_1.default.Label = toolbar_primitives_1.StackToolbarLabel;
StackToolbarClient_1.default.Icon = toolbar_primitives_1.StackToolbarIcon;
StackToolbarClient_1.default.Badge = toolbar_primitives_1.StackToolbarBadge;
//# sourceMappingURL=StackToolbar.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"StackToolbar.js","sourceRoot":"","sources":["../../../../src/layouts/stack-utils/toolbar/StackToolbar.tsx"],"names":[],"mappings":";;;;;;AAAA,6DAA0D;AAC1D,8EAAgD;AAiBvC,uBAjBF,4BAAY,CAiBE;AAhBrB,yDAA8E;AAC9E,2EAAwE;AACxE,6DAA0D;AAC1D,yDAAsD;AACtD,6DAA8F;AAE9F,4BAAY,CAAC,MAAM,GAAG,uCAAkB,CAAC;AACzC,4BAAY,CAAC,IAAI,GAAG,mCAAgB,CAAC;AACrC,4BAAY,CAAC,UAAU,GAAG,yCAAsB,CAAC;AACjD,4BAAY,CAAC,aAAa,GAAG,qDAAyB,CAAC;AACvD,4BAAY,CAAC,MAAM,GAAG,uCAAkB,CAAC;AACzC,4BAAY,CAAC,IAAI,GAAG,mCAAgB,CAAC;AACrC,4BAAY,CAAC,KAAK,GAAG,sCAAiB,CAAC;AACvC,4BAAY,CAAC,IAAI,GAAG,qCAAgB,CAAC;AACrC,4BAAY,CAAC,KAAK,GAAG,sCAAiB,CAAC","sourcesContent":["import { StackToolbarButton } from './StackToolbarButton';\nimport StackToolbar from './StackToolbarClient';\nimport { StackToolbarMenu, StackToolbarMenuAction } from './StackToolbarMenu';\nimport { StackToolbarSearchBarSlot } from './StackToolbarSearchBarSlot';\nimport { StackToolbarSpacer } from './StackToolbarSpacer';\nimport { StackToolbarView } from './StackToolbarView';\nimport { StackToolbarBadge, StackToolbarIcon, StackToolbarLabel } from './toolbar-primitives';\n\nStackToolbar.Button = StackToolbarButton;\nStackToolbar.Menu = StackToolbarMenu;\nStackToolbar.MenuAction = StackToolbarMenuAction;\nStackToolbar.SearchBarSlot = StackToolbarSearchBarSlot;\nStackToolbar.Spacer = StackToolbarSpacer;\nStackToolbar.View = StackToolbarView;\nStackToolbar.Label = StackToolbarLabel;\nStackToolbar.Icon = StackToolbarIcon;\nStackToolbar.Badge = StackToolbarBadge;\n\nexport { StackToolbar };\n"]}

View File

@@ -0,0 +1,163 @@
import type { NativeStackHeaderItemButton } from '@react-navigation/native-stack';
import type { ImageRef } from 'expo-image';
import { type ReactNode } from 'react';
import { type StyleProp, type TextStyle } from 'react-native';
import { type StackHeaderItemSharedProps } from './shared';
export interface StackToolbarButtonProps {
accessibilityLabel?: string;
accessibilityHint?: string;
/**
* There are two ways to specify the content of the button:
*
* @example
* ```tsx
* import { Stack } from 'expo-router';
*
* export default function Page() {
* return (
* <>
* <Stack.Toolbar placement="left">
* <Stack.Toolbar.Button icon="star.fill">As text passed as children</Stack.Toolbar.Button>
* </Stack.Toolbar>
* <ScreenContent />
* </>
* );
* }
* ```
*
* @example
* ```tsx
* import { Stack } from 'expo-router';
*
* export default function Page() {
* return (
* <>
* <Stack.Toolbar placement="left">
* <Stack.Toolbar.Button>
* <Stack.Toolbar.Icon sf="star.fill" />
* <Stack.Toolbar.Label>As components</Stack.Toolbar.Label>
* <Stack.Toolbar.Badge>3</Stack.Toolbar.Badge>
* </Stack.Toolbar.Button>
* </Stack.Toolbar>
* <ScreenContent />
* </>
* );
* }
* ```
*
* > **Note**: When icon is used, the label will not be shown and will be used for accessibility purposes only. Badge is only supported in left/right placements, not in bottom (iOS toolbar limitation).
*/
children?: ReactNode;
disabled?: boolean;
/**
* Whether the button should be hidden.
*
* @default false
*/
hidden?: boolean;
/**
* Whether to hide the shared background.
*
* @platform iOS 26+
*/
hidesSharedBackground?: boolean;
/**
* Icon to display in the button.
*
* Can be a string representing an SFSymbol or an image source.
*
* > **Note**: When used in `placement="bottom"`, only string SFSymbols are supported. Use the `image` prop to provide custom images.
*/
icon?: StackHeaderItemSharedProps['icon'];
/**
* Image to display in the button.
*
* > **Note**: This prop is only supported in toolbar with `placement="bottom"`.
*/
image?: ImageRef;
/**
* Controls how image-based icons are rendered on iOS.
*
* - `'template'`: iOS applies tint color to the icon
* - `'original'`: Preserves original icon colors (useful for multi-color icons)
*
* **Default behavior:**
* - If `tintColor` is specified, defaults to `'template'`
* - If no `tintColor`, defaults to `'original'`
*
* This prop only affects image-based icons (not SF Symbols).
*
* @see [Apple documentation](https://developer.apple.com/documentation/uikit/uiimage/renderingmode-swift.enum) for more information.
*
* @platform ios
*/
iconRenderingMode?: 'template' | 'original';
onPress?: () => void;
/**
* Whether to separate the background of this item from other header items.
*
* @default false
*/
separateBackground?: boolean;
/**
* Whether the button is in a selected state
*
* @see [Apple documentation](https://developer.apple.com/documentation/uikit/uibarbuttonitem/isselected) for more information
*/
selected?: boolean;
/**
* Style for the label of the header item.
*/
style?: StyleProp<TextStyle>;
/**
* The tint color to apply to the button item
*
* @see [Apple documentation](https://developer.apple.com/documentation/uikit/uibarbuttonitem/tintcolor) for more information.
*/
tintColor?: StackHeaderItemSharedProps['tintColor'];
/**
* @default 'plain'
*/
variant?: StackHeaderItemSharedProps['variant'];
}
/**
* A button used inside `Stack.Toolbar`.
*
* @example
* ```tsx
* import { Stack } from 'expo-router';
*
* export default function Layout() {
* return (
* <Stack>
* <Stack.Screen name="index">
* <Stack.Toolbar placement="left">
* <Stack.Toolbar.Button icon="arrow.left.circle" onPress={() => alert('Left pressed')} />
* </Stack.Toolbar>
* </Stack.Screen>
* </Stack>
* );
* }
* ```
*
* @example
* ```tsx
* import { Stack } from 'expo-router';
*
* export default function Page() {
* return (
* <>
* <Stack.Toolbar placement="left">
* <Stack.Toolbar.Button icon="arrow.left.circle" onPress={() => alert('Left pressed')} />
* </Stack.Toolbar>
* <ScreenContent />
* </>
* );
* }
* ```
*
* @platform ios
*/
export declare const StackToolbarButton: React.FC<StackToolbarButtonProps>;
export declare function convertStackToolbarButtonPropsToRNHeaderItem(props: StackToolbarButtonProps): NativeStackHeaderItemButton | undefined;
//# sourceMappingURL=StackToolbarButton.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"StackToolbarButton.d.ts","sourceRoot":"","sources":["../../../../src/layouts/stack-utils/toolbar/StackToolbarButton.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,gCAAgC,CAAC;AAClF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAA4B,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AACjE,OAAO,EAA+B,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAI3F,OAAO,EAIL,KAAK,0BAA0B,EAChC,MAAM,UAAU,CAAC;AAMlB,MAAM,WAAW,uBAAuB;IACtC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAwCG;IACH,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;;OAIG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,0BAA0B,CAAC,MAAM,CAAC,CAAC;IAE1C;;;;OAIG;IACH,KAAK,CAAC,EAAE,QAAQ,CAAC;IACjB;;;;;;;;;;;;;;;OAeG;IACH,iBAAiB,CAAC,EAAE,UAAU,GAAG,UAAU,CAAC;IAC5C,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;OAEG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B;;;;OAIG;IACH,SAAS,CAAC,EAAE,0BAA0B,CAAC,WAAW,CAAC,CAAC;IACpD;;OAEG;IACH,OAAO,CAAC,EAAE,0BAA0B,CAAC,SAAS,CAAC,CAAC;CACjD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,eAAO,MAAM,kBAAkB,EAAE,KAAK,CAAC,EAAE,CAAC,uBAAuB,CA+ChE,CAAC;AAEF,wBAAgB,4CAA4C,CAC1D,KAAK,EAAE,uBAAuB,GAC7B,2BAA2B,GAAG,SAAS,CAWzC"}

View File

@@ -0,0 +1,102 @@
"use strict";
'use client';
Object.defineProperty(exports, "__esModule", { value: true });
exports.StackToolbarButton = void 0;
exports.convertStackToolbarButtonPropsToRNHeaderItem = convertStackToolbarButtonPropsToRNHeaderItem;
const react_1 = require("react");
const react_native_1 = require("react-native");
const context_1 = require("./context");
const shared_1 = require("./shared");
const toolbar_primitives_1 = require("./toolbar-primitives");
const native_1 = require("../../../toolbar/native");
const children_1 = require("../../../utils/children");
/**
* A button used inside `Stack.Toolbar`.
*
* @example
* ```tsx
* import { Stack } from 'expo-router';
*
* export default function Layout() {
* return (
* <Stack>
* <Stack.Screen name="index">
* <Stack.Toolbar placement="left">
* <Stack.Toolbar.Button icon="arrow.left.circle" onPress={() => alert('Left pressed')} />
* </Stack.Toolbar>
* </Stack.Screen>
* </Stack>
* );
* }
* ```
*
* @example
* ```tsx
* import { Stack } from 'expo-router';
*
* export default function Page() {
* return (
* <>
* <Stack.Toolbar placement="left">
* <Stack.Toolbar.Button icon="arrow.left.circle" onPress={() => alert('Left pressed')} />
* </Stack.Toolbar>
* <ScreenContent />
* </>
* );
* }
* ```
*
* @platform ios
*/
const StackToolbarButton = (props) => {
const placement = (0, context_1.useToolbarPlacement)();
const validChildren = (0, react_1.useMemo)(() => (0, children_1.filterAllowedChildrenElements)(props.children, ALLOWED_CHILDREN), [props.children]);
if (process.env.NODE_ENV !== 'production') {
// Skip validation for string children
if (typeof props.children !== 'string') {
const allChildren = react_1.Children.toArray(props.children);
if (allChildren.length !== validChildren.length) {
throw new Error(`Stack.Toolbar.Button only accepts a single string or Stack.Toolbar.Label, Stack.Toolbar.Icon, and Stack.Toolbar.Badge as its children.`);
}
}
}
if (process.env.NODE_ENV !== 'production' && placement === 'bottom') {
const hasBadge = (0, children_1.getFirstChildOfType)(props.children, toolbar_primitives_1.StackToolbarBadge);
if (hasBadge) {
console.warn('Stack.Toolbar.Badge is not supported in bottom toolbar (iOS limitation). The badge will be ignored.');
}
}
if (placement !== 'bottom') {
throw new Error('Stack.Toolbar.Button must be used inside a Stack.Toolbar');
}
const sharedProps = (0, shared_1.convertStackHeaderSharedPropsToRNSharedHeaderItem)(props, true);
// TODO(@ubax): Handle image loading using useImage in a follow-up PR.
const icon = sharedProps?.icon?.type === 'sfSymbol' ? sharedProps.icon.name : undefined;
const xcassetName = (0, shared_1.extractXcassetName)(props);
const imageRenderingMode = (0, shared_1.extractIconRenderingMode)(props) ?? props.iconRenderingMode;
return (<NativeToolbarButton {...sharedProps} icon={icon} xcassetName={xcassetName} image={props.image} imageRenderingMode={imageRenderingMode}/>);
};
exports.StackToolbarButton = StackToolbarButton;
function convertStackToolbarButtonPropsToRNHeaderItem(props) {
if (props.hidden) {
return undefined;
}
return {
...(0, shared_1.convertStackHeaderSharedPropsToRNSharedHeaderItem)(props),
type: 'button',
onPress: props.onPress ?? (() => { }),
selected: !!props.selected,
};
}
const ALLOWED_CHILDREN = [toolbar_primitives_1.StackToolbarLabel, toolbar_primitives_1.StackToolbarIcon, toolbar_primitives_1.StackToolbarBadge];
/**
* Native toolbar button component for bottom toolbar.
* Renders as RouterToolbarItem.
*/
const NativeToolbarButton = (props) => {
const id = (0, react_1.useId)();
const renderingMode = props.imageRenderingMode ?? (props.tintColor !== undefined ? 'template' : 'original');
return (<native_1.RouterToolbarItem accessibilityHint={props.accessibilityHint} accessibilityLabel={props.accessibilityLabel} barButtonItemStyle={props.variant === 'done' ? 'prominent' : props.variant} disabled={props.disabled} hidden={props.hidden} hidesSharedBackground={props.hidesSharedBackground} identifier={id} image={props.image} imageRenderingMode={renderingMode} onSelected={props.onPress} possibleTitles={props.possibleTitles} selected={props.selected} sharesBackground={!props.separateBackground} systemImageName={props.icon} xcassetName={props.xcassetName} title={props.label} tintColor={props.tintColor} titleStyle={react_native_1.StyleSheet.flatten(props.style)}/>);
};
// #endregion
//# sourceMappingURL=StackToolbarButton.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,107 @@
import { NativeStackNavigationOptions } from '@react-navigation/native-stack';
import React, { type ReactNode } from 'react';
import { type ToolbarPlacement } from './context';
export interface StackToolbarProps {
/**
* Child elements to compose the toolbar. Can include Stack.Toolbar.Button,
* Stack.Toolbar.Menu, Stack.Toolbar.View, Stack.Toolbar.Spacer, and
* Stack.Toolbar.SearchBarSlot (bottom only) components.
*/
children?: ReactNode;
/**
* The placement of the toolbar.
*
* - `'left'`: Renders items in the left area of the header.
* - `'right'`: Renders items in the right area of the header.
* - `'bottom'`: Renders items in the bottom toolbar (iOS only).
*
* @default 'bottom'
*/
placement?: ToolbarPlacement;
/**
* When `true`, renders children as a custom component in the header area,
* replacing the default header layout.
*
* Only applies to `placement="left"` and `placement="right"`.
*
* @default false
*/
asChild?: boolean;
}
/**
* The component used to configure the stack toolbar.
*
* - Use `placement="left"` to customize the left side of the header.
* - Use `placement="right"` to customize the right side of the header.
* - Use `placement="bottom"` (default) to show a bottom toolbar (iOS only).
*
* If multiple instances of this component are rendered for the same screen,
* the last one rendered in the component tree takes precedence.
*
* > **Note:** Using `Stack.Toolbar` with `placement="left"` or `placement="right"` will
* automatically make the header visible (`headerShown: true`), as the toolbar is rendered
* as part of the native header.
*
* > **Note:** `Stack.Toolbar` with `placement="bottom"` can only be used inside **page**
* components, not in layout components.
*
*
* @example
* ```tsx
* import { Stack } from 'expo-router';
*
* export default function Layout() {
* return (
* <Stack>
* <Stack.Screen name="index">
* <Stack.Toolbar placement="left">
* <Stack.Toolbar.Button icon="sidebar.left" onPress={() => alert('Left button pressed!')} />
* </Stack.Toolbar>
* <Stack.Toolbar placement="right">
* <Stack.Toolbar.Button icon="ellipsis.circle" onPress={() => alert('Right button pressed!')} />
* </Stack.Toolbar>
* </Stack.Screen>
* </Stack>
* );
* }
* ```
*
* @example
* ```tsx
* import { Stack } from 'expo-router';
*
* export default function Page() {
* return (
* <>
* <Stack.Toolbar placement="left">
* <Stack.Toolbar.Button icon="sidebar.left" onPress={() => alert('Left button pressed!')} />
* </Stack.Toolbar>
* <Stack.Toolbar>
* <Stack.Toolbar.Spacer />
* <Stack.Toolbar.Button icon="magnifyingglass" onPress={() => {}} />
* <Stack.Toolbar.Spacer />
* </Stack.Toolbar>
* <ScreenContent />
* </>
* );
* }
* ```
*
* @experimental
* @platform ios
*/
export declare const StackToolbar: {
(props: StackToolbarProps): React.JSX.Element;
Button: React.FC<import("./StackToolbarButton").StackToolbarButtonProps>;
Menu: React.FC<import("./StackToolbarMenu").StackToolbarMenuProps>;
MenuAction: React.FC<import("./StackToolbarMenu").StackToolbarMenuActionProps>;
SearchBarSlot: React.FC<import("./StackToolbarSearchBarSlot").StackToolbarSearchBarSlotProps>;
Spacer: React.FC<import("./StackToolbarSpacer").StackToolbarSpacerProps>;
View: React.FC<import("./StackToolbarView").StackToolbarViewProps>;
Label: React.FC<import("./toolbar-primitives").StackToolbarLabelProps>;
Icon: React.FC<import("./toolbar-primitives").StackToolbarIconProps>;
Badge: React.FC<import("./toolbar-primitives").StackToolbarBadgeProps>;
};
export declare function appendStackToolbarPropsToOptions(options: NativeStackNavigationOptions, props: StackToolbarProps): NativeStackNavigationOptions;
export default StackToolbar;
//# sourceMappingURL=StackToolbarClient.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"StackToolbarClient.d.ts","sourceRoot":"","sources":["../../../../src/layouts/stack-utils/toolbar/StackToolbarClient.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,4BAA4B,EAAE,MAAM,gCAAgC,CAAC;AAC9E,OAAO,KAAK,EAAE,EAAqC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAiBjF,OAAO,EAAgD,KAAK,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAOhG,MAAM,WAAW,iBAAiB;IAChC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB;;;;;;;;OAQG;IACH,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6DG;AACH,eAAO,MAAM,YAAY;YAAW,iBAAiB;;;;;;;;;;CAWpD,CAAC;AAgFF,wBAAgB,gCAAgC,CAC9C,OAAO,EAAE,4BAA4B,EACrC,KAAK,EAAE,iBAAiB,GACvB,4BAA4B,CAqC9B;AAYD,eAAe,YAAY,CAAC"}

View File

@@ -0,0 +1,223 @@
"use strict";
'use client';
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.StackToolbar = void 0;
exports.appendStackToolbarPropsToOptions = appendStackToolbarPropsToOptions;
const react_1 = __importStar(require("react"));
const StackToolbarButton_1 = require("./StackToolbarButton");
const StackToolbarMenu_1 = require("./StackToolbarMenu");
const StackToolbarSearchBarSlot_1 = require("./StackToolbarSearchBarSlot");
const StackToolbarSpacer_1 = require("./StackToolbarSpacer");
const StackToolbarView_1 = require("./StackToolbarView");
const context_1 = require("./context");
const toolbar_primitives_1 = require("./toolbar-primitives");
const composition_options_1 = require("../../../fork/native-stack/composition-options");
const NativeMenuContext_1 = require("../../../link/NativeMenuContext");
const native_1 = require("../../../toolbar/native");
const children_1 = require("../../../utils/children");
/**
* The component used to configure the stack toolbar.
*
* - Use `placement="left"` to customize the left side of the header.
* - Use `placement="right"` to customize the right side of the header.
* - Use `placement="bottom"` (default) to show a bottom toolbar (iOS only).
*
* If multiple instances of this component are rendered for the same screen,
* the last one rendered in the component tree takes precedence.
*
* > **Note:** Using `Stack.Toolbar` with `placement="left"` or `placement="right"` will
* automatically make the header visible (`headerShown: true`), as the toolbar is rendered
* as part of the native header.
*
* > **Note:** `Stack.Toolbar` with `placement="bottom"` can only be used inside **page**
* components, not in layout components.
*
*
* @example
* ```tsx
* import { Stack } from 'expo-router';
*
* export default function Layout() {
* return (
* <Stack>
* <Stack.Screen name="index">
* <Stack.Toolbar placement="left">
* <Stack.Toolbar.Button icon="sidebar.left" onPress={() => alert('Left button pressed!')} />
* </Stack.Toolbar>
* <Stack.Toolbar placement="right">
* <Stack.Toolbar.Button icon="ellipsis.circle" onPress={() => alert('Right button pressed!')} />
* </Stack.Toolbar>
* </Stack.Screen>
* </Stack>
* );
* }
* ```
*
* @example
* ```tsx
* import { Stack } from 'expo-router';
*
* export default function Page() {
* return (
* <>
* <Stack.Toolbar placement="left">
* <Stack.Toolbar.Button icon="sidebar.left" onPress={() => alert('Left button pressed!')} />
* </Stack.Toolbar>
* <Stack.Toolbar>
* <Stack.Toolbar.Spacer />
* <Stack.Toolbar.Button icon="magnifyingglass" onPress={() => {}} />
* <Stack.Toolbar.Spacer />
* </Stack.Toolbar>
* <ScreenContent />
* </>
* );
* }
* ```
*
* @experimental
* @platform ios
*/
const StackToolbar = (props) => {
const parentPlacement = (0, context_1.useToolbarPlacement)();
if (parentPlacement) {
throw new Error(`Stack.Toolbar cannot be nested inside another Stack.Toolbar.`);
}
if (props.placement === 'bottom' || !props.placement) {
return <StackToolbarBottom {...props}/>;
}
return <StackToolbarHeader {...props} key={props.placement}/>;
};
exports.StackToolbar = StackToolbar;
const StackToolbarBottom = ({ children }) => {
return (<context_1.ToolbarPlacementContext.Provider value="bottom">
<NativeMenuContext_1.NativeMenuContext value>
<native_1.RouterToolbarHost>{children}</native_1.RouterToolbarHost>
</NativeMenuContext_1.NativeMenuContext>
</context_1.ToolbarPlacementContext.Provider>);
};
const StackToolbarHeader = ({ children, placement, asChild }) => {
if (placement !== 'left' && placement !== 'right') {
throw new Error(`Invalid placement "${placement}" for Stack.Toolbar. Expected "left" or "right".`);
}
const options = (0, react_1.useMemo)(() => appendStackToolbarPropsToOptions({},
// satisfies ensures every prop is listed here
{ children, placement, asChild }), [children, placement, asChild]);
(0, composition_options_1.useCompositionOption)(options);
return null;
};
function convertToolbarChildrenToUnstableItems(children, side) {
const allChildren = react_1.default.Children.toArray(children);
const actions = allChildren.filter((child) => (0, children_1.isChildOfType)(child, StackToolbarButton_1.StackToolbarButton) ||
(0, children_1.isChildOfType)(child, StackToolbarMenu_1.StackToolbarMenu) ||
(0, children_1.isChildOfType)(child, StackToolbarSpacer_1.StackToolbarSpacer) ||
(0, children_1.isChildOfType)(child, StackToolbarView_1.StackToolbarView));
if (actions.length !== allChildren.length && process.env.NODE_ENV !== 'production') {
const otherElements = allChildren
.filter((child) => !actions.some((action) => action === child))
.map((e) => {
if ((0, react_1.isValidElement)(e)) {
if (e.type === react_1.Fragment) {
return '<Fragment>';
}
else {
return e.type?.name ?? e.type;
}
}
return String(e);
});
console.warn(`Stack.Toolbar with placement="${side}" only accepts <Stack.Toolbar.Button>, <Stack.Toolbar.Menu>, <Stack.Toolbar.View>, and <Stack.Toolbar.Spacer> as children. Found invalid children: ${otherElements.join(', ')}`);
}
return () => actions
.map((action) => {
if ((0, children_1.isChildOfType)(action, StackToolbarButton_1.StackToolbarButton)) {
return (0, StackToolbarButton_1.convertStackToolbarButtonPropsToRNHeaderItem)(action.props);
}
else if ((0, children_1.isChildOfType)(action, StackToolbarMenu_1.StackToolbarMenu)) {
return (0, StackToolbarMenu_1.convertStackToolbarMenuPropsToRNHeaderItem)(action.props);
}
else if ((0, children_1.isChildOfType)(action, StackToolbarSpacer_1.StackToolbarSpacer)) {
return (0, StackToolbarSpacer_1.convertStackToolbarSpacerPropsToRNHeaderItem)(action.props);
}
return (0, StackToolbarView_1.convertStackToolbarViewPropsToRNHeaderItem)(action.props);
})
.filter((item) => !!item);
}
function appendStackToolbarPropsToOptions(options, props) {
const { children, placement = 'bottom', asChild } = props;
if (placement === 'bottom') {
// Bottom toolbar doesn't modify navigation options
return options;
}
if (asChild) {
if (placement === 'left') {
return {
...options,
headerShown: true,
headerLeft: () => children,
};
}
else {
return {
...options,
headerShown: true,
headerRight: () => children,
};
}
}
if (placement === 'left') {
return {
...options,
headerShown: true,
unstable_headerLeftItems: convertToolbarChildrenToUnstableItems(children, 'left'),
};
}
return {
...options,
headerShown: true,
unstable_headerRightItems: convertToolbarChildrenToUnstableItems(children, 'right'),
};
}
exports.StackToolbar.Button = StackToolbarButton_1.StackToolbarButton;
exports.StackToolbar.Menu = StackToolbarMenu_1.StackToolbarMenu;
exports.StackToolbar.MenuAction = StackToolbarMenu_1.StackToolbarMenuAction;
exports.StackToolbar.SearchBarSlot = StackToolbarSearchBarSlot_1.StackToolbarSearchBarSlot;
exports.StackToolbar.Spacer = StackToolbarSpacer_1.StackToolbarSpacer;
exports.StackToolbar.View = StackToolbarView_1.StackToolbarView;
exports.StackToolbar.Label = toolbar_primitives_1.StackToolbarLabel;
exports.StackToolbar.Icon = toolbar_primitives_1.StackToolbarIcon;
exports.StackToolbar.Badge = toolbar_primitives_1.StackToolbarBadge;
exports.default = exports.StackToolbar;
//# sourceMappingURL=StackToolbarClient.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,255 @@
import type { NativeStackHeaderItemMenu, NativeStackHeaderItemMenuAction } from '@react-navigation/native-stack';
import type { ImageRef } from 'expo-image';
import { type ReactNode } from 'react';
import { type ImageSourcePropType } from 'react-native';
import type { SFSymbol } from 'sf-symbols-typescript';
import { type StackHeaderItemSharedProps } from './shared';
export interface StackToolbarMenuProps {
accessibilityLabel?: string;
accessibilityHint?: string;
/**
* Menu content - can include icons, labels, badges and menu actions.
*
* @example
* ```tsx
* <Stack.Toolbar.Menu>
* <Stack.Toolbar.Icon sfSymbol="ellipsis.circle" />
* <Stack.Toolbar.Label>Options</Stack.Toolbar.Label>
* <Stack.Toolbar.MenuAction onPress={() => {}}>Action 1</Stack.Toolbar.MenuAction>
* </Stack.Toolbar.Menu>
* ```
*/
children?: ReactNode;
/**
* If `true`, the menu item will be displayed as destructive.
*
* @see [Apple documentation](https://developer.apple.com/documentation/uikit/uimenuelement/attributes/destructive) for more information.
*/
destructive?: boolean;
disabled?: boolean;
/**
* Image to display for the menu item.
*
* > **Note**: This prop is only supported in toolbar with `placement="bottom"`.
*/
image?: ImageRef;
/**
* Whether to hide the shared background.
*
* @see [Official Apple documentation](https://developer.apple.com/documentation/uikit/uibarbuttonitem/hidessharedbackground) for more information.
*
* @platform iOS 26+
*/
hidesSharedBackground?: boolean;
/**
* Whether the menu should be hidden.
*
* @default false
*/
hidden?: boolean;
/**
* Icon for the menu item.
*
* Can be an SF Symbol name or an image source.
*
* > **Note**: When used in `placement="bottom"`, only string SFSymbols are supported. Use the `image` prop to provide custom images.
*/
icon?: StackHeaderItemSharedProps['icon'];
/**
* Controls how image-based icons are rendered on iOS.
*
* - `'template'`: iOS applies tint color to the icon (useful for monochrome icons)
* - `'original'`: Preserves original icon colors (useful for multi-color icons)
*
* **Default behavior:**
* - If `tintColor` is specified, defaults to `'template'`
* - If no `tintColor`, defaults to `'original'`
*
* This prop only affects image-based icons (not SF Symbols).
*
* @see [Apple documentation](https://developer.apple.com/documentation/uikit/uiimage/renderingmode-swift.enum) for more information.
*
* @platform ios
*/
iconRenderingMode?: 'template' | 'original';
/**
* If `true`, the menu will be displayed inline.
* This means that the menu will not be collapsed
*
* > **Note**: Inline menus are only supported in submenus.
*
* @see [Apple documentation](https://developer.apple.com/documentation/uikit/uimenu/options-swift.struct/displayinline) for more information.
*/
inline?: boolean;
/**
* If `true`, the menu will be displayed as a palette.
* This means that the menu will be displayed as one row
*
* > **Note**: Palette menus are only supported in submenus.
*
* @see [Apple documentation](https://developer.apple.com/documentation/uikit/uimenu/options-swift.struct/displayaspalette) for more information.
*/
palette?: boolean;
/**
* Whether to separate the background of this item from other header items.
*
* @default false
*/
separateBackground?: boolean;
/**
* Style for the label of the header item.
*/
style?: StackHeaderItemSharedProps['style'];
/**
* The tint color to apply to the button item
*
* @see [Apple documentation](https://developer.apple.com/documentation/uikit/uibarbuttonitem/tintcolor) for more information.
*/
tintColor?: StackHeaderItemSharedProps['tintColor'];
/**
* Optional title to show on top of the menu.
*/
title?: string;
/**
* @default 'plain'
*/
variant?: StackHeaderItemSharedProps['variant'];
/**
* The preferred size of the menu elements.
*
* > **Note**: This prop is only supported in `Stack.Toolbar.Bottom`.
*
* @see [Apple documentation](https://developer.apple.com/documentation/uikit/uimenu/preferredelementsize) for more information.
*
* @platform iOS 16.0+
*/
elementSize?: 'auto' | 'small' | 'medium' | 'large';
}
/**
* Use as `Stack.Toolbar.Menu` to provide menus in iOS toolbar.
* It accepts `Stack.Toolbar.MenuAction` and nested `Stack.Toolbar.Menu`
* elements. Menu can be configured using both component props and child
* elements.
*
* @example
* ```tsx
* import { Stack } from 'expo-router';
* import { Alert } from 'react-native';
*
* export default function Page() {
* return (
* <>
* <Stack.Toolbar placement="right">
* <Stack.Toolbar.Menu icon="ellipsis.circle">
* <Stack.Toolbar.MenuAction onPress={() => Alert.alert('Action pressed!')}>
* Action 1
* </Stack.Toolbar.MenuAction>
* </Stack.Toolbar.Menu>
* </Stack.Toolbar>
* <ScreenContent />
* </>
* );
* }
* ```
*
* @see [Human Interface Guidelines](https://developer.apple.com/design/human-interface-guidelines/menus) for more information about menus on iOS.
*
* @platform ios
*/
export declare const StackToolbarMenu: React.FC<StackToolbarMenuProps>;
export declare function convertStackToolbarMenuPropsToRNHeaderItem(props: StackToolbarMenuProps, isBottomPlacement?: boolean): NativeStackHeaderItemMenu | undefined;
export interface StackToolbarMenuActionProps {
/**
* Can be an Icon, Label or string title.
*/
children?: ReactNode;
/**
* If `true`, the menu item will be disabled and not selectable.
*
* @see [Apple documentation](https://developer.apple.com/documentation/uikit/uimenuelement/attributes/disabled) for more information.
*/
disabled?: boolean;
icon?: SFSymbol | ImageSourcePropType;
/**
* Image to display for the menu action.
*
* > **Note**: This prop is only supported in `Stack.Toolbar.Bottom`.
*/
image?: ImageRef;
/**
* Controls how image-based icons are rendered on iOS.
*
* - `'template'`: iOS applies tint color to the icon (useful for monochrome icons)
* - `'original'`: Preserves original icon colors (useful for multi-color icons)
*
* **Default behavior:**
* - If `tintColor` is specified, defaults to `'template'`
* - If no `tintColor`, defaults to `'original'`
*
* This prop only affects image-based icons (not SF Symbols).
*
* @see [Apple documentation](https://developer.apple.com/documentation/uikit/uiimage/renderingmode-swift.enum) for more information.
*
* @platform ios
*/
iconRenderingMode?: 'template' | 'original';
/**
* If `true`, the menu item will be displayed as destructive.
*
* @see [Apple documentation](https://developer.apple.com/documentation/uikit/uimenuelement/attributes/destructive) for more information.
*/
destructive?: boolean;
/**
* If `true`, the menu will be kept presented after the action is selected.
*
* This is marked as unstable, because when action is selected it will recreate the menu,
* which will close all opened submenus and reset the scroll position.
*
* @see [Apple documentation](https://developer.apple.com/documentation/uikit/uimenuelement/attributes/keepsmenupresented) for more information.
*/
unstable_keepPresented?: boolean;
/**
* If `true`, the menu item will be displayed as selected.
*/
isOn?: boolean;
onPress?: () => void;
/**
* An elaborated title that explains the purpose of the action.
*/
discoverabilityLabel?: string;
/**
* An optional subtitle for the menu item.
*
* @see [Apple documentation](https://developer.apple.com/documentation/uikit/uimenuelement/subtitle) for more information.
*/
subtitle?: string;
hidden?: boolean;
}
/**
* An action item for a `Stack.Toolbar.Menu`.
*
* @example
* ```tsx
* import { Stack } from 'expo-router';
*
* export default function Page() {
* return (
* <>
* <Stack.Toolbar placement="right">
* <Stack.Toolbar.Menu icon="ellipsis.circle">
* <Stack.Toolbar.MenuAction onPress={() => alert('Action pressed!')}>
* Action 1
* </Stack.Toolbar.MenuAction>
* </Stack.Toolbar.Menu>
* </Stack.Toolbar>
* <ScreenContent />
* </>
* );
* }
* ```
*
* @platform ios
*/
export declare const StackToolbarMenuAction: React.FC<StackToolbarMenuActionProps>;
export declare function convertStackToolbarMenuActionPropsToRNHeaderItem(props: StackToolbarMenuActionProps): NativeStackHeaderItemMenuAction;
//# sourceMappingURL=StackToolbarMenu.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"StackToolbarMenu.d.ts","sourceRoot":"","sources":["../../../../src/layouts/stack-utils/toolbar/StackToolbarMenu.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,yBAAyB,EACzB,+BAA+B,EAEhC,MAAM,gCAAgC,CAAC;AACxC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAA4B,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AACjE,OAAO,EAGL,KAAK,mBAAmB,EAGzB,MAAM,cAAc,CAAC;AAEtB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAGtD,OAAO,EAIL,KAAK,0BAA0B,EAChC,MAAM,UAAU,CAAC;AA6BlB,MAAM,WAAW,qBAAqB;IACpC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB;;;;OAIG;IACH,KAAK,CAAC,EAAE,QAAQ,CAAC;IACjB;;;;;;OAMG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,0BAA0B,CAAC,MAAM,CAAC,CAAC;IAC1C;;;;;;;;;;;;;;;OAeG;IACH,iBAAiB,CAAC,EAAE,UAAU,GAAG,UAAU,CAAC;IAC5C;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B;;OAEG;IACH,KAAK,CAAC,EAAE,0BAA0B,CAAC,OAAO,CAAC,CAAC;IAC5C;;;;OAIG;IACH,SAAS,CAAC,EAAE,0BAA0B,CAAC,WAAW,CAAC,CAAC;IACpD;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;OAEG;IACH,OAAO,CAAC,EAAE,0BAA0B,CAAC,SAAS,CAAC,CAAC;IAEhD;;;;;;;;OAQG;IACH,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC;CACrD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,eAAO,MAAM,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,CAsD5D,CAAC;AAEF,wBAAgB,0CAA0C,CACxD,KAAK,EAAE,qBAAqB,EAC5B,iBAAiB,GAAE,OAAe,GACjC,yBAAyB,GAAG,SAAS,CAsCvC;AAiED,MAAM,WAAW,2BAA2B;IAC1C;;OAEG;IACH,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,IAAI,CAAC,EAAE,QAAQ,GAAG,mBAAmB,CAAC;IAEtC;;;;OAIG;IACH,KAAK,CAAC,EAAE,QAAQ,CAAC;IACjB;;;;;;;;;;;;;;;OAeG;IACH,iBAAiB,CAAC,EAAE,UAAU,GAAG,UAAU,CAAC;IAC5C;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;;;;;;OAOG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC;;OAEG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB;;OAEG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,sBAAsB,EAAE,KAAK,CAAC,EAAE,CAAC,2BAA2B,CAiBxE,CAAC;AAEF,wBAAgB,gDAAgD,CAC9D,KAAK,EAAE,2BAA2B,GACjC,+BAA+B,CAwBjC"}

View File

@@ -0,0 +1,256 @@
"use strict";
'use client';
Object.defineProperty(exports, "__esModule", { value: true });
exports.StackToolbarMenuAction = exports.StackToolbarMenu = void 0;
exports.convertStackToolbarMenuPropsToRNHeaderItem = convertStackToolbarMenuPropsToRNHeaderItem;
exports.convertStackToolbarMenuActionPropsToRNHeaderItem = convertStackToolbarMenuActionPropsToRNHeaderItem;
const react_1 = require("react");
const react_native_1 = require("react-native");
const context_1 = require("./context");
const shared_1 = require("./shared");
const toolbar_primitives_1 = require("./toolbar-primitives");
const elements_1 = require("../../../link/elements");
const native_1 = require("../../../link/preview/native");
const children_1 = require("../../../utils/children");
/**
* Computes the label and menu title from children and title prop.
*
* - If only `title` prop is provided, it is used for both the label (button text) and menu title
* - If only `.Label` child is provided, it is used for the label and the menu title is an empty string
* - If both `.Label` child and `title` prop are provided. `.Label` is used for the label, and `title` is used for the menu title
*/
function computeMenuLabelAndTitle(children, title) {
const labelChild = (0, children_1.getFirstChildOfType)(children, toolbar_primitives_1.StackToolbarLabel);
const labelFromChild = labelChild?.props.children;
return {
label: labelFromChild ?? title ?? '',
menuTitle: title ?? '',
};
}
/**
* Use as `Stack.Toolbar.Menu` to provide menus in iOS toolbar.
* It accepts `Stack.Toolbar.MenuAction` and nested `Stack.Toolbar.Menu`
* elements. Menu can be configured using both component props and child
* elements.
*
* @example
* ```tsx
* import { Stack } from 'expo-router';
* import { Alert } from 'react-native';
*
* export default function Page() {
* return (
* <>
* <Stack.Toolbar placement="right">
* <Stack.Toolbar.Menu icon="ellipsis.circle">
* <Stack.Toolbar.MenuAction onPress={() => Alert.alert('Action pressed!')}>
* Action 1
* </Stack.Toolbar.MenuAction>
* </Stack.Toolbar.Menu>
* </Stack.Toolbar>
* <ScreenContent />
* </>
* );
* }
* ```
*
* @see [Human Interface Guidelines](https://developer.apple.com/design/human-interface-guidelines/menus) for more information about menus on iOS.
*
* @platform ios
*/
const StackToolbarMenu = (props) => {
const placement = (0, context_1.useToolbarPlacement)();
if (placement !== 'bottom') {
// For placement other than bottom, this component will not render, and should be
// converted to RN header item using convertStackToolbarMenuPropsToRNHeaderItem.
// So if we reach here, it means we're not inside a toolbar or something else is wrong.
throw new Error('Stack.Toolbar.Menu must be used inside a Stack.Toolbar');
}
const validChildren = (0, react_1.useMemo)(() => (0, children_1.filterAllowedChildrenElements)(props.children, ALLOWED_CHILDREN), [props.children]);
const sharedProps = convertStackToolbarMenuPropsToRNHeaderItem(props, true);
const computedLabel = sharedProps?.label;
const computedMenuTitle = sharedProps?.menu?.title;
const icon = sharedProps?.icon?.type === 'sfSymbol' ? sharedProps.icon.name : undefined;
const xcassetName = (0, shared_1.extractXcassetName)(props);
const imageRenderingMode = (0, shared_1.extractIconRenderingMode)(props) ?? props.iconRenderingMode;
if (process.env.NODE_ENV !== 'production') {
const allChildren = react_1.Children.toArray(props.children);
if (allChildren.length !== validChildren.length) {
throw new Error(`Stack.Toolbar.Menu only accepts Stack.Toolbar.Menu, Stack.Toolbar.MenuAction, Stack.Toolbar.Label, Stack.Toolbar.Icon, and Stack.Toolbar.Badge as its children.`);
}
}
if (process.env.NODE_ENV !== 'production') {
const hasBadge = (0, children_1.getFirstChildOfType)(props.children, toolbar_primitives_1.StackToolbarBadge);
if (hasBadge) {
console.warn('Stack.Toolbar.Badge is not supported in bottom toolbar (iOS limitation). The badge will be ignored.');
}
}
// TODO(@ubax): Handle image loading using useImage in a follow-up PR.
return (<NativeToolbarMenu {...props} icon={icon} xcassetName={xcassetName} image={props.image} imageRenderingMode={imageRenderingMode} label={computedLabel} title={computedMenuTitle} children={validChildren}/>);
};
exports.StackToolbarMenu = StackToolbarMenu;
function convertStackToolbarMenuPropsToRNHeaderItem(props, isBottomPlacement = false) {
if (props.hidden) {
return undefined;
}
const { title, ...rest } = props;
const actions = react_1.Children.toArray(props.children).filter((child) => (0, children_1.isChildOfType)(child, exports.StackToolbarMenuAction) || (0, children_1.isChildOfType)(child, exports.StackToolbarMenu));
const { label: computedLabel, menuTitle: computedMenuTitle } = computeMenuLabelAndTitle(props.children, title);
const sharedProps = (0, shared_1.convertStackHeaderSharedPropsToRNSharedHeaderItem)(rest, isBottomPlacement);
const item = {
...sharedProps,
label: computedLabel,
type: 'menu',
menu: {
multiselectable: true,
items: actions
.map((action) => {
if ((0, children_1.isChildOfType)(action, exports.StackToolbarMenu)) {
return convertStackToolbarSubmenuMenuPropsToRNHeaderItem(action.props);
}
return convertStackToolbarMenuActionPropsToRNHeaderItem(action.props);
})
.filter((i) => !!i),
},
};
if (computedMenuTitle) {
item.menu.title = computedMenuTitle;
}
return item;
}
// Custom menu action icons are not supported in react-navigation yet
// But they are supported in react-native-screens
// TODO(@ubax): Remove this workaround once react-navigation supports custom icons for menu actions.
// https://linear.app/expo/issue/ENG-19853/remove-custom-conversion-logic-for-icon-from-packagesexpo
function convertImageIconToPlatformIcon(icon) {
return icon.tinted
? { type: 'templateSource', templateSource: icon.source }
: { type: 'imageSource', imageSource: icon.source };
}
function convertStackToolbarSubmenuMenuPropsToRNHeaderItem(props) {
if (props.hidden) {
return undefined;
}
const sharedProps = (0, shared_1.convertStackHeaderSharedPropsToRNSharedHeaderItem)(props);
const actions = react_1.Children.toArray(props.children).filter((child) => (0, children_1.isChildOfType)(child, exports.StackToolbarMenuAction) || (0, children_1.isChildOfType)(child, exports.StackToolbarMenu));
const item = {
type: 'submenu',
items: actions
.map((action) => {
if ((0, children_1.isChildOfType)(action, exports.StackToolbarMenu)) {
return convertStackToolbarSubmenuMenuPropsToRNHeaderItem(action.props);
}
return convertStackToolbarMenuActionPropsToRNHeaderItem(action.props);
})
.filter((i) => !!i),
label: sharedProps.label || props.title || '',
multiselectable: true,
};
if (props.inline !== undefined) {
item.inline = props.inline;
}
if (props.palette !== undefined) {
item.layout = props.palette ? 'palette' : 'default';
}
if (props.destructive !== undefined) {
item.destructive = props.destructive;
}
// TODO: Add elementSize to react-native-screens
if (sharedProps.icon) {
if (sharedProps.icon.type === 'sfSymbol') {
item.icon = sharedProps.icon;
}
else {
item.icon = convertImageIconToPlatformIcon(sharedProps.icon);
}
}
return item;
}
/**
* An action item for a `Stack.Toolbar.Menu`.
*
* @example
* ```tsx
* import { Stack } from 'expo-router';
*
* export default function Page() {
* return (
* <>
* <Stack.Toolbar placement="right">
* <Stack.Toolbar.Menu icon="ellipsis.circle">
* <Stack.Toolbar.MenuAction onPress={() => alert('Action pressed!')}>
* Action 1
* </Stack.Toolbar.MenuAction>
* </Stack.Toolbar.Menu>
* </Stack.Toolbar>
* <ScreenContent />
* </>
* );
* }
* ```
*
* @platform ios
*/
const StackToolbarMenuAction = (props) => {
const placement = (0, context_1.useToolbarPlacement)();
if (placement !== 'bottom') {
throw new Error('Stack.Toolbar.MenuAction must be used inside a Stack.Toolbar.Menu');
}
// TODO(@ubax): Handle image loading using useImage in a follow-up PR.
const icon = typeof props.icon === 'string' ? props.icon : undefined;
return (<NativeToolbarMenuAction {...props} icon={icon} image={props.image} imageRenderingMode={props.iconRenderingMode}/>);
};
exports.StackToolbarMenuAction = StackToolbarMenuAction;
function convertStackToolbarMenuActionPropsToRNHeaderItem(props) {
const { children, isOn, unstable_keepPresented, icon, ...rest } = props;
const sharedProps = (0, shared_1.convertStackHeaderSharedPropsToRNSharedHeaderItem)(props);
const item = {
...rest,
description: props.subtitle,
type: 'action',
label: sharedProps.label,
state: isOn ? 'on' : 'off',
onPress: props.onPress ?? (() => { }),
};
if (unstable_keepPresented !== undefined) {
item.keepsMenuPresented = unstable_keepPresented;
}
if (sharedProps.icon) {
if (sharedProps.icon.type === 'sfSymbol') {
item.icon = sharedProps.icon;
}
else {
item.icon = convertImageIconToPlatformIcon(sharedProps.icon);
}
}
return item;
}
/**
* Native toolbar menu component for bottom toolbar.
* Renders as NativeLinkPreviewAction.
*/
const NativeToolbarMenu = ({ accessibilityHint, accessibilityLabel, separateBackground, hidesSharedBackground, palette, inline, hidden, subtitle, title, label, destructive, children, icon, xcassetName, image, imageRenderingMode, tintColor, variant, style, elementSize, }) => {
const identifier = (0, react_1.useId)();
const titleStyle = react_native_1.StyleSheet.flatten(style);
const renderingMode = imageRenderingMode ?? (tintColor !== undefined ? 'template' : 'original');
return (<native_1.NativeLinkPreviewAction sharesBackground={!separateBackground} hidesSharedBackground={hidesSharedBackground} hidden={hidden} icon={icon} xcassetName={xcassetName}
// TODO(@ubax): Handle image loading using useImage in a follow-up PR.
image={image} imageRenderingMode={renderingMode} destructive={destructive} subtitle={subtitle} accessibilityLabel={accessibilityLabel} accessibilityHint={accessibilityHint} displayAsPalette={palette} displayInline={inline} preferredElementSize={elementSize} tintColor={tintColor} titleStyle={titleStyle} barButtonItemStyle={variant === 'done' ? 'prominent' : variant} title={title ?? ''} label={label} onSelected={() => { }} children={children} identifier={identifier}/>);
};
// #endregion
// #region NativeToolbarMenuAction
/**
* Native toolbar menu action - reuses LinkMenuAction.
*/
const NativeToolbarMenuAction = elements_1.LinkMenuAction;
// #endregion
const ALLOWED_CHILDREN = [
exports.StackToolbarMenu,
exports.StackToolbarMenuAction,
NativeToolbarMenu,
NativeToolbarMenuAction,
toolbar_primitives_1.StackToolbarLabel,
toolbar_primitives_1.StackToolbarIcon,
toolbar_primitives_1.StackToolbarBadge,
];
//# sourceMappingURL=StackToolbarMenu.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,60 @@
export interface StackToolbarSearchBarSlotProps {
/**
* Whether the search bar slot should be hidden.
*
* @default false
*/
hidden?: boolean;
/**
* Whether to hide the shared background.
*
* @platform iOS 26+
*/
hidesSharedBackground?: boolean;
/**
* Whether this search bar slot has a separate background from adjacent items. When this prop is `true`, the search bar will always render as `integratedButton`.
*
* In order to render the search bar with a separate background, ensure that adjacent toolbar items have `separateBackground` set to `true` or use `Stack.Toolbar.Spacer` to create spacing.
*
* @example
* ```tsx
* <Stack.SearchBar onChangeText={()=>{}} />
* <Stack.Toolbar placement="bottom">
* <Stack.Toolbar.SearchBarSlot />
* <Stack.Toolbar.Spacer />
* <Stack.Toolbar.Button icon="square.and.pencil" />
* </Stack.Toolbar>
* ```
*
* @platform iOS 26+
*/
separateBackground?: boolean;
}
/**
* A search bar slot for the bottom toolbar. This reserves space for the search bar
* in the toolbar and allows positioning it among other toolbar items.
*
* This component is only available in bottom placement (`<Stack.Toolbar>` or `<Stack.Toolbar placement="bottom">`).
*
* @example
* ```tsx
* import { Stack } from 'expo-router';
*
* export default function Page() {
* return (
* <>
* <Stack.Toolbar>
* <Stack.Toolbar.Button icon="folder" />
* <Stack.Toolbar.SearchBarSlot />
* <Stack.Toolbar.Button icon="ellipsis.circle" />
* </Stack.Toolbar>
* <ScreenContent />
* </>
* );
* }
* ```
*
* @platform iOS 26+
*/
export declare const StackToolbarSearchBarSlot: React.FC<StackToolbarSearchBarSlotProps>;
//# sourceMappingURL=StackToolbarSearchBarSlot.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"StackToolbarSearchBarSlot.d.ts","sourceRoot":"","sources":["../../../../src/layouts/stack-utils/toolbar/StackToolbarSearchBarSlot.tsx"],"names":[],"mappings":"AAOA,MAAM,WAAW,8BAA8B;IAC7C;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;;OAIG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC;;;;;;;;;;;;;;;;OAgBG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,eAAO,MAAM,yBAAyB,EAAE,KAAK,CAAC,EAAE,CAAC,8BAA8B,CAQ9E,CAAC"}

View File

@@ -0,0 +1,58 @@
"use strict";
'use client';
Object.defineProperty(exports, "__esModule", { value: true });
exports.StackToolbarSearchBarSlot = void 0;
const react_1 = require("react");
const react_native_1 = require("react-native");
const context_1 = require("./context");
const native_1 = require("../../../toolbar/native");
/**
* A search bar slot for the bottom toolbar. This reserves space for the search bar
* in the toolbar and allows positioning it among other toolbar items.
*
* This component is only available in bottom placement (`<Stack.Toolbar>` or `<Stack.Toolbar placement="bottom">`).
*
* @example
* ```tsx
* import { Stack } from 'expo-router';
*
* export default function Page() {
* return (
* <>
* <Stack.Toolbar>
* <Stack.Toolbar.Button icon="folder" />
* <Stack.Toolbar.SearchBarSlot />
* <Stack.Toolbar.Button icon="ellipsis.circle" />
* </Stack.Toolbar>
* <ScreenContent />
* </>
* );
* }
* ```
*
* @platform iOS 26+
*/
const StackToolbarSearchBarSlot = (props) => {
const placement = (0, context_1.useToolbarPlacement)();
if (placement !== 'bottom') {
throw new Error('Stack.Toolbar.SearchBarSlot must be used inside a Stack.Toolbar');
}
return <NativeToolbarSearchBarSlot {...props}/>;
};
exports.StackToolbarSearchBarSlot = StackToolbarSearchBarSlot;
/**
* Native toolbar search bar slot for bottom toolbar (iOS 26+).
* Renders as RouterToolbarItem with type 'searchBar'.
*/
const NativeToolbarSearchBarSlot = ({ hidesSharedBackground, hidden, separateBackground, }) => {
const id = (0, react_1.useId)();
if (process.env.EXPO_OS !== 'ios' || parseInt(String(react_native_1.Platform.Version).split('.')[0], 10) < 26) {
return null;
}
if (hidden) {
return null;
}
return (<native_1.RouterToolbarItem hidesSharedBackground={hidesSharedBackground} identifier={id} sharesBackground={!separateBackground} type="searchBar"/>);
};
// #endregion
//# sourceMappingURL=StackToolbarSearchBarSlot.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"StackToolbarSearchBarSlot.js","sourceRoot":"","sources":["../../../../src/layouts/stack-utils/toolbar/StackToolbarSearchBarSlot.tsx"],"names":[],"mappings":";AAAA,YAAY,CAAC;;;AACb,iCAA8B;AAC9B,+CAAwC;AAExC,uCAAgD;AAChD,oDAA4D;AAmC5D;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACI,MAAM,yBAAyB,GAA6C,CAAC,KAAK,EAAE,EAAE;IAC3F,MAAM,SAAS,GAAG,IAAA,6BAAmB,GAAE,CAAC;IAExC,IAAI,SAAS,KAAK,QAAQ,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CAAC,iEAAiE,CAAC,CAAC;IACrF,CAAC;IAED,OAAO,CAAC,0BAA0B,CAAC,IAAI,KAAK,CAAC,EAAG,CAAC;AACnD,CAAC,CAAC;AARW,QAAA,yBAAyB,6BAQpC;AAUF;;;GAGG;AACH,MAAM,0BAA0B,GAA8C,CAAC,EAC7E,qBAAqB,EACrB,MAAM,EACN,kBAAkB,GACnB,EAAE,EAAE;IACH,MAAM,EAAE,GAAG,IAAA,aAAK,GAAE,CAAC;IACnB,IAAI,OAAO,CAAC,GAAG,CAAC,OAAO,KAAK,KAAK,IAAI,QAAQ,CAAC,MAAM,CAAC,uBAAQ,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC;QAC/F,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,MAAM,EAAE,CAAC;QACX,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,CACL,CAAC,0BAAiB,CAChB,qBAAqB,CAAC,CAAC,qBAAqB,CAAC,CAC7C,UAAU,CAAC,CAAC,EAAE,CAAC,CACf,gBAAgB,CAAC,CAAC,CAAC,kBAAkB,CAAC,CACtC,IAAI,CAAC,WAAW,EAChB,CACH,CAAC;AACJ,CAAC,CAAC;AAEF,aAAa","sourcesContent":["'use client';\nimport { useId } from 'react';\nimport { Platform } from 'react-native';\n\nimport { useToolbarPlacement } from './context';\nimport { RouterToolbarItem } from '../../../toolbar/native';\n\nexport interface StackToolbarSearchBarSlotProps {\n /**\n * Whether the search bar slot should be hidden.\n *\n * @default false\n */\n hidden?: boolean;\n /**\n * Whether to hide the shared background.\n *\n * @platform iOS 26+\n */\n hidesSharedBackground?: boolean;\n /**\n * Whether this search bar slot has a separate background from adjacent items. When this prop is `true`, the search bar will always render as `integratedButton`.\n *\n * In order to render the search bar with a separate background, ensure that adjacent toolbar items have `separateBackground` set to `true` or use `Stack.Toolbar.Spacer` to create spacing.\n *\n * @example\n * ```tsx\n * <Stack.SearchBar onChangeText={()=>{}} />\n * <Stack.Toolbar placement=\"bottom\">\n * <Stack.Toolbar.SearchBarSlot />\n * <Stack.Toolbar.Spacer />\n * <Stack.Toolbar.Button icon=\"square.and.pencil\" />\n * </Stack.Toolbar>\n * ```\n *\n * @platform iOS 26+\n */\n separateBackground?: boolean;\n}\n\n/**\n * A search bar slot for the bottom toolbar. This reserves space for the search bar\n * in the toolbar and allows positioning it among other toolbar items.\n *\n * This component is only available in bottom placement (`<Stack.Toolbar>` or `<Stack.Toolbar placement=\"bottom\">`).\n *\n * @example\n * ```tsx\n * import { Stack } from 'expo-router';\n *\n * export default function Page() {\n * return (\n * <>\n * <Stack.Toolbar>\n * <Stack.Toolbar.Button icon=\"folder\" />\n * <Stack.Toolbar.SearchBarSlot />\n * <Stack.Toolbar.Button icon=\"ellipsis.circle\" />\n * </Stack.Toolbar>\n * <ScreenContent />\n * </>\n * );\n * }\n * ```\n *\n * @platform iOS 26+\n */\nexport const StackToolbarSearchBarSlot: React.FC<StackToolbarSearchBarSlotProps> = (props) => {\n const placement = useToolbarPlacement();\n\n if (placement !== 'bottom') {\n throw new Error('Stack.Toolbar.SearchBarSlot must be used inside a Stack.Toolbar');\n }\n\n return <NativeToolbarSearchBarSlot {...props} />;\n};\n\n// #region NativeToolbarSearchBarSlot\n\ninterface NativeToolbarSearchBarSlotProps {\n hidesSharedBackground?: boolean;\n hidden?: boolean;\n separateBackground?: boolean;\n}\n\n/**\n * Native toolbar search bar slot for bottom toolbar (iOS 26+).\n * Renders as RouterToolbarItem with type 'searchBar'.\n */\nconst NativeToolbarSearchBarSlot: React.FC<NativeToolbarSearchBarSlotProps> = ({\n hidesSharedBackground,\n hidden,\n separateBackground,\n}) => {\n const id = useId();\n if (process.env.EXPO_OS !== 'ios' || parseInt(String(Platform.Version).split('.')[0], 10) < 26) {\n return null;\n }\n if (hidden) {\n return null;\n }\n return (\n <RouterToolbarItem\n hidesSharedBackground={hidesSharedBackground}\n identifier={id}\n sharesBackground={!separateBackground}\n type=\"searchBar\"\n />\n );\n};\n\n// #endregion\n"]}

View File

@@ -0,0 +1,72 @@
import type { NativeStackHeaderItemSpacing } from '@react-navigation/native-stack';
export interface StackToolbarSpacerProps {
/**
* Whether the spacer should be hidden.
*
* @default false
*/
hidden?: boolean;
/**
* The width of the spacing element.
*
* In Left/Right placements, width is required.
* In Bottom placement, if width is not provided, the spacer will be flexible
* and expand to fill available space.
*/
width?: number;
/**
* Whether this spacer shares background with adjacent items.
*
* Only available in bottom placement.
*
* @platform iOS 26+
*/
sharesBackground?: boolean;
}
/**
* A spacing helper used inside `Stack.Toolbar` to create empty space between toolbar items.
*
* In left/right placements, width is required.
* In bottom placement, if width is not provided, creates a flexible spacer that expands to fill space.
*
* @example
* ```tsx
* import { Stack } from 'expo-router';
*
* export default function Page() {
* return (
* <>
* <Stack.Toolbar placement="left">
* <Stack.Toolbar.Button icon="arrow.left" />
* <Stack.Toolbar.Spacer width={8} />
* <Stack.Toolbar.Button icon="arrow.right" />
* </Stack.Toolbar>
* <ScreenContent />
* </>
* );
* }
* ```
*
* @example
* ```tsx
* import { Stack } from 'expo-router';
*
* export default function Page() {
* return (
* <>
* <Stack.Toolbar>
* <Stack.Toolbar.Spacer />
* <Stack.Toolbar.Button icon="search" />
* <Stack.Toolbar.Spacer />
* </Stack.Toolbar>
* <ScreenContent />
* </>
* );
* }
* ```
*
* @platform ios
*/
export declare const StackToolbarSpacer: React.FC<StackToolbarSpacerProps>;
export declare function convertStackToolbarSpacerPropsToRNHeaderItem(props: StackToolbarSpacerProps): NativeStackHeaderItemSpacing | undefined;
//# sourceMappingURL=StackToolbarSpacer.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"StackToolbarSpacer.d.ts","sourceRoot":"","sources":["../../../../src/layouts/stack-utils/toolbar/StackToolbarSpacer.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,gCAAgC,CAAC;AAMnF,MAAM,WAAW,uBAAuB;IACtC;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;;;;;OAMG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,eAAO,MAAM,kBAAkB,EAAE,KAAK,CAAC,EAAE,CAAC,uBAAuB,CAQhE,CAAC;AAEF,wBAAgB,4CAA4C,CAC1D,KAAK,EAAE,uBAAuB,GAC7B,4BAA4B,GAAG,SAAS,CAqB1C"}

View File

@@ -0,0 +1,87 @@
"use strict";
'use client';
Object.defineProperty(exports, "__esModule", { value: true });
exports.StackToolbarSpacer = void 0;
exports.convertStackToolbarSpacerPropsToRNHeaderItem = convertStackToolbarSpacerPropsToRNHeaderItem;
const react_1 = require("react");
const context_1 = require("./context");
const native_1 = require("../../../toolbar/native");
/**
* A spacing helper used inside `Stack.Toolbar` to create empty space between toolbar items.
*
* In left/right placements, width is required.
* In bottom placement, if width is not provided, creates a flexible spacer that expands to fill space.
*
* @example
* ```tsx
* import { Stack } from 'expo-router';
*
* export default function Page() {
* return (
* <>
* <Stack.Toolbar placement="left">
* <Stack.Toolbar.Button icon="arrow.left" />
* <Stack.Toolbar.Spacer width={8} />
* <Stack.Toolbar.Button icon="arrow.right" />
* </Stack.Toolbar>
* <ScreenContent />
* </>
* );
* }
* ```
*
* @example
* ```tsx
* import { Stack } from 'expo-router';
*
* export default function Page() {
* return (
* <>
* <Stack.Toolbar>
* <Stack.Toolbar.Spacer />
* <Stack.Toolbar.Button icon="search" />
* <Stack.Toolbar.Spacer />
* </Stack.Toolbar>
* <ScreenContent />
* </>
* );
* }
* ```
*
* @platform ios
*/
const StackToolbarSpacer = (props) => {
const placement = (0, context_1.useToolbarPlacement)();
if (placement !== 'bottom') {
throw new Error('Stack.Toolbar.Spacer must be used inside a Stack.Toolbar');
}
return <NativeToolbarSpacer {...props} hidesSharedBackground={!props.sharesBackground}/>;
};
exports.StackToolbarSpacer = StackToolbarSpacer;
function convertStackToolbarSpacerPropsToRNHeaderItem(props) {
const { hidden, width } = props;
if (hidden) {
return undefined;
}
// Warn if using flexible spacer in Left/Right placement
if (width === undefined) {
if (process.env.NODE_ENV !== 'production') {
console.warn('Stack.Toolbar.Spacer requires `width` when used in left or right placement. Flexible spacers are only supported in Bottom placement.');
}
return undefined;
}
return {
type: 'spacing',
spacing: width ?? 0,
};
}
/**
* Native toolbar spacer component for bottom toolbar.
* Renders as RouterToolbarItem with type 'fixedSpacer' or 'fluidSpacer'.
*/
const NativeToolbarSpacer = (props) => {
const id = (0, react_1.useId)();
return (<native_1.RouterToolbarItem hidesSharedBackground={props.hidesSharedBackground} hidden={props.hidden} identifier={id} sharesBackground={props.sharesBackground} type={props.width ? 'fixedSpacer' : 'fluidSpacer'} width={props.width}/>);
};
// #endregion
//# sourceMappingURL=StackToolbarSpacer.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,62 @@
import type { NativeStackHeaderItemCustom } from '@react-navigation/native-stack';
export interface StackToolbarViewProps {
/**
* Can be any React node.
*/
children?: NativeStackHeaderItemCustom['element'];
/**
* Whether the view should be hidden.
*
* @default false
*/
hidden?: boolean;
/**
* Whether to hide the shared background.
*
* @see [Official Apple documentation](https://developer.apple.com/documentation/uikit/uibarbuttonitem/hidessharedbackground) for more information.
*
* @platform iOS 26+
*/
hidesSharedBackground?: boolean;
/**
* Whether to separate the background of this item from other items.
*
* Only available in bottom placement.
*
* @default false
*/
separateBackground?: boolean;
}
/**
* A wrapper to render custom content in the toolbar.
*
* Use inside `Stack.Toolbar` to render a custom React element.
*
* @example
* ```tsx
* import { Stack } from 'expo-router';
* import { Text } from 'react-native';
*
* function CustomElement() {
* return <Text>Custom Element</Text>;
* }
*
* export default function Page() {
* return (
* <>
* <Stack.Toolbar>
* <Stack.Toolbar.View>
* <CustomElement />
* </Stack.Toolbar.View>
* </Stack.Toolbar>
* <ScreenContent />
* </>
* );
* }
* ```
*
* @platform ios
*/
export declare const StackToolbarView: React.FC<StackToolbarViewProps>;
export declare function convertStackToolbarViewPropsToRNHeaderItem(props: StackToolbarViewProps): NativeStackHeaderItemCustom | undefined;
//# sourceMappingURL=StackToolbarView.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"StackToolbarView.d.ts","sourceRoot":"","sources":["../../../../src/layouts/stack-utils/toolbar/StackToolbarView.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,gCAAgC,CAAC;AAMlF,MAAM,WAAW,qBAAqB;IACpC;;OAEG;IACH,QAAQ,CAAC,EAAE,2BAA2B,CAAC,SAAS,CAAC,CAAC;IAClD;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;;;;OAMG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAEhC;;;;;;OAMG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,eAAO,MAAM,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,CAQ5D,CAAC;AAEF,wBAAgB,0CAA0C,CACxD,KAAK,EAAE,qBAAqB,GAC3B,2BAA2B,GAAG,SAAS,CAgBzC"}

View File

@@ -0,0 +1,73 @@
"use strict";
'use client';
Object.defineProperty(exports, "__esModule", { value: true });
exports.StackToolbarView = void 0;
exports.convertStackToolbarViewPropsToRNHeaderItem = convertStackToolbarViewPropsToRNHeaderItem;
const react_1 = require("react");
const context_1 = require("./context");
const native_1 = require("../../../toolbar/native");
/**
* A wrapper to render custom content in the toolbar.
*
* Use inside `Stack.Toolbar` to render a custom React element.
*
* @example
* ```tsx
* import { Stack } from 'expo-router';
* import { Text } from 'react-native';
*
* function CustomElement() {
* return <Text>Custom Element</Text>;
* }
*
* export default function Page() {
* return (
* <>
* <Stack.Toolbar>
* <Stack.Toolbar.View>
* <CustomElement />
* </Stack.Toolbar.View>
* </Stack.Toolbar>
* <ScreenContent />
* </>
* );
* }
* ```
*
* @platform ios
*/
const StackToolbarView = (props) => {
const placement = (0, context_1.useToolbarPlacement)();
if (placement !== 'bottom') {
throw new Error('Stack.Toolbar.View must be used inside a Stack.Toolbar');
}
return <NativeToolbarView {...props}>{props.children}</NativeToolbarView>;
};
exports.StackToolbarView = StackToolbarView;
function convertStackToolbarViewPropsToRNHeaderItem(props) {
if (props.hidden) {
return undefined;
}
const { children, hidesSharedBackground } = props;
if (!children) {
console.warn('Stack.Toolbar.View requires a child element to render custom content in the toolbar.');
}
const element = children ? children : <></>;
return {
type: 'custom',
element,
hidesSharedBackground,
};
}
/**
* Native toolbar view component for bottom toolbar.
* Renders as RouterToolbarItem with children.
*/
const NativeToolbarView = ({ children, hidden, hidesSharedBackground, separateBackground, }) => {
const id = (0, react_1.useId)();
return (<native_1.RouterToolbarItem hidesSharedBackground={hidesSharedBackground} hidden={hidden} identifier={id} sharesBackground={!separateBackground}>
{children}
</native_1.RouterToolbarItem>);
};
// #endregion
//# sourceMappingURL=StackToolbarView.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"StackToolbarView.js","sourceRoot":"","sources":["../../../../src/layouts/stack-utils/toolbar/StackToolbarView.tsx"],"names":[],"mappings":";AAAA,YAAY,CAAC;;;AA6Eb,gGAkBC;AA7FD,iCAA8C;AAE9C,uCAAgD;AAChD,oDAA4D;AAgC5D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACI,MAAM,gBAAgB,GAAoC,CAAC,KAAK,EAAE,EAAE;IACzE,MAAM,SAAS,GAAG,IAAA,6BAAmB,GAAE,CAAC;IAExC,IAAI,SAAS,KAAK,QAAQ,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAC;IAC5E,CAAC;IAED,OAAO,CAAC,iBAAiB,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,iBAAiB,CAAC,CAAC;AAC5E,CAAC,CAAC;AARW,QAAA,gBAAgB,oBAQ3B;AAEF,SAAgB,0CAA0C,CACxD,KAA4B;IAE5B,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;QACjB,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,EAAE,QAAQ,EAAE,qBAAqB,EAAE,GAAG,KAAK,CAAC;IAClD,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO,CAAC,IAAI,CACV,sFAAsF,CACvF,CAAC;IACJ,CAAC;IACD,MAAM,OAAO,GAAG,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC;IAC5C,OAAO;QACL,IAAI,EAAE,QAAQ;QACd,OAAO;QACP,qBAAqB;KACtB,CAAC;AACJ,CAAC;AAWD;;;GAGG;AACH,MAAM,iBAAiB,GAAqC,CAAC,EAC3D,QAAQ,EACR,MAAM,EACN,qBAAqB,EACrB,kBAAkB,GACnB,EAAE,EAAE;IACH,MAAM,EAAE,GAAG,IAAA,aAAK,GAAE,CAAC;IACnB,OAAO,CACL,CAAC,0BAAiB,CAChB,qBAAqB,CAAC,CAAC,qBAAqB,CAAC,CAC7C,MAAM,CAAC,CAAC,MAAM,CAAC,CACf,UAAU,CAAC,CAAC,EAAE,CAAC,CACf,gBAAgB,CAAC,CAAC,CAAC,kBAAkB,CAAC,CACtC;MAAA,CAAC,QAAQ,CACX;IAAA,EAAE,0BAAiB,CAAC,CACrB,CAAC;AACJ,CAAC,CAAC;AAEF,aAAa","sourcesContent":["'use client';\nimport type { NativeStackHeaderItemCustom } from '@react-navigation/native-stack';\nimport { useId, type ReactNode } from 'react';\n\nimport { useToolbarPlacement } from './context';\nimport { RouterToolbarItem } from '../../../toolbar/native';\n\nexport interface StackToolbarViewProps {\n /**\n * Can be any React node.\n */\n children?: NativeStackHeaderItemCustom['element'];\n /**\n * Whether the view should be hidden.\n *\n * @default false\n */\n hidden?: boolean;\n /**\n * Whether to hide the shared background.\n *\n * @see [Official Apple documentation](https://developer.apple.com/documentation/uikit/uibarbuttonitem/hidessharedbackground) for more information.\n *\n * @platform iOS 26+\n */\n hidesSharedBackground?: boolean;\n // TODO(@ubax): implement missing props in react-native-screens\n /**\n * Whether to separate the background of this item from other items.\n *\n * Only available in bottom placement.\n *\n * @default false\n */\n separateBackground?: boolean;\n}\n\n/**\n * A wrapper to render custom content in the toolbar.\n *\n * Use inside `Stack.Toolbar` to render a custom React element.\n *\n * @example\n * ```tsx\n * import { Stack } from 'expo-router';\n * import { Text } from 'react-native';\n *\n * function CustomElement() {\n * return <Text>Custom Element</Text>;\n * }\n *\n * export default function Page() {\n * return (\n * <>\n * <Stack.Toolbar>\n * <Stack.Toolbar.View>\n * <CustomElement />\n * </Stack.Toolbar.View>\n * </Stack.Toolbar>\n * <ScreenContent />\n * </>\n * );\n * }\n * ```\n *\n * @platform ios\n */\nexport const StackToolbarView: React.FC<StackToolbarViewProps> = (props) => {\n const placement = useToolbarPlacement();\n\n if (placement !== 'bottom') {\n throw new Error('Stack.Toolbar.View must be used inside a Stack.Toolbar');\n }\n\n return <NativeToolbarView {...props}>{props.children}</NativeToolbarView>;\n};\n\nexport function convertStackToolbarViewPropsToRNHeaderItem(\n props: StackToolbarViewProps\n): NativeStackHeaderItemCustom | undefined {\n if (props.hidden) {\n return undefined;\n }\n const { children, hidesSharedBackground } = props;\n if (!children) {\n console.warn(\n 'Stack.Toolbar.View requires a child element to render custom content in the toolbar.'\n );\n }\n const element = children ? children : <></>;\n return {\n type: 'custom',\n element,\n hidesSharedBackground,\n };\n}\n\n// #region NativeToolbarView\n\ninterface NativeToolbarViewProps {\n children?: ReactNode;\n hidden?: boolean;\n hidesSharedBackground?: boolean;\n separateBackground?: boolean;\n}\n\n/**\n * Native toolbar view component for bottom toolbar.\n * Renders as RouterToolbarItem with children.\n */\nconst NativeToolbarView: React.FC<NativeToolbarViewProps> = ({\n children,\n hidden,\n hidesSharedBackground,\n separateBackground,\n}) => {\n const id = useId();\n return (\n <RouterToolbarItem\n hidesSharedBackground={hidesSharedBackground}\n hidden={hidden}\n identifier={id}\n sharesBackground={!separateBackground}>\n {children}\n </RouterToolbarItem>\n );\n};\n\n// #endregion\n"]}

View File

@@ -0,0 +1,9 @@
export type ToolbarPlacement = 'left' | 'right' | 'bottom';
/**
* Context to track which toolbar placement the current component is in.
* This allows shared components (Button, Menu, Spacer, etc.) to behave
* differently based on their placement.
*/
export declare const ToolbarPlacementContext: import("react").Context<ToolbarPlacement | null>;
export declare function useToolbarPlacement(): ToolbarPlacement | null;
//# sourceMappingURL=context.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../../../src/layouts/stack-utils/toolbar/context.tsx"],"names":[],"mappings":"AAGA,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;AAE3D;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,kDAA+C,CAAC;AAEpF,wBAAgB,mBAAmB,IAAI,gBAAgB,GAAG,IAAI,CAE7D"}

View File

@@ -0,0 +1,16 @@
"use strict";
'use client';
Object.defineProperty(exports, "__esModule", { value: true });
exports.ToolbarPlacementContext = void 0;
exports.useToolbarPlacement = useToolbarPlacement;
const react_1 = require("react");
/**
* Context to track which toolbar placement the current component is in.
* This allows shared components (Button, Menu, Spacer, etc.) to behave
* differently based on their placement.
*/
exports.ToolbarPlacementContext = (0, react_1.createContext)(null);
function useToolbarPlacement() {
return (0, react_1.useContext)(exports.ToolbarPlacementContext);
}
//# sourceMappingURL=context.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../../../src/layouts/stack-utils/toolbar/context.tsx"],"names":[],"mappings":";AAAA,YAAY,CAAC;;;AAYb,kDAEC;AAbD,iCAAkD;AAIlD;;;;GAIG;AACU,QAAA,uBAAuB,GAAG,IAAA,qBAAa,EAA0B,IAAI,CAAC,CAAC;AAEpF,SAAgB,mBAAmB;IACjC,OAAO,IAAA,kBAAU,EAAC,+BAAuB,CAAC,CAAC;AAC7C,CAAC","sourcesContent":["'use client';\nimport { createContext, useContext } from 'react';\n\nexport type ToolbarPlacement = 'left' | 'right' | 'bottom';\n\n/**\n * Context to track which toolbar placement the current component is in.\n * This allows shared components (Button, Menu, Spacer, etc.) to behave\n * differently based on their placement.\n */\nexport const ToolbarPlacementContext = createContext<ToolbarPlacement | null>(null);\n\nexport function useToolbarPlacement(): ToolbarPlacement | null {\n return useContext(ToolbarPlacementContext);\n}\n"]}

View File

@@ -0,0 +1,11 @@
export { StackToolbarBadge, StackToolbarIcon, StackToolbarLabel, type StackToolbarBadgeProps, type StackToolbarIconProps, type StackToolbarLabelProps, } from './toolbar-primitives';
export { StackToolbar } from './StackToolbar';
export { appendStackToolbarPropsToOptions, type StackToolbarProps } from './StackToolbarClient';
export { StackToolbarButton, type StackToolbarButtonProps } from './StackToolbarButton';
export { StackToolbarMenu, StackToolbarMenuAction, type StackToolbarMenuProps, type StackToolbarMenuActionProps, } from './StackToolbarMenu';
export { StackToolbarSearchBarSlot, type StackToolbarSearchBarSlotProps, } from './StackToolbarSearchBarSlot';
export { StackToolbarSpacer, type StackToolbarSpacerProps } from './StackToolbarSpacer';
export { StackToolbarView, type StackToolbarViewProps } from './StackToolbarView';
export type { ToolbarPlacement } from './context';
export type { StackHeaderItemSharedProps } from './shared';
//# sourceMappingURL=index.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/layouts/stack-utils/toolbar/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,EACjB,KAAK,sBAAsB,EAC3B,KAAK,qBAAqB,EAC1B,KAAK,sBAAsB,GAC5B,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE9C,OAAO,EAAE,gCAAgC,EAAE,KAAK,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAEhG,OAAO,EAAE,kBAAkB,EAAE,KAAK,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAExF,OAAO,EACL,gBAAgB,EAChB,sBAAsB,EACtB,KAAK,qBAAqB,EAC1B,KAAK,2BAA2B,GACjC,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,yBAAyB,EACzB,KAAK,8BAA8B,GACpC,MAAM,6BAA6B,CAAC;AAErC,OAAO,EAAE,kBAAkB,EAAE,KAAK,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAExF,OAAO,EAAE,gBAAgB,EAAE,KAAK,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAElF,YAAY,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAElD,YAAY,EAAE,0BAA0B,EAAE,MAAM,UAAU,CAAC"}

View File

@@ -0,0 +1,23 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.StackToolbarView = exports.StackToolbarSpacer = exports.StackToolbarSearchBarSlot = exports.StackToolbarMenuAction = exports.StackToolbarMenu = exports.StackToolbarButton = exports.appendStackToolbarPropsToOptions = exports.StackToolbar = exports.StackToolbarLabel = exports.StackToolbarIcon = exports.StackToolbarBadge = void 0;
var toolbar_primitives_1 = require("./toolbar-primitives");
Object.defineProperty(exports, "StackToolbarBadge", { enumerable: true, get: function () { return toolbar_primitives_1.StackToolbarBadge; } });
Object.defineProperty(exports, "StackToolbarIcon", { enumerable: true, get: function () { return toolbar_primitives_1.StackToolbarIcon; } });
Object.defineProperty(exports, "StackToolbarLabel", { enumerable: true, get: function () { return toolbar_primitives_1.StackToolbarLabel; } });
var StackToolbar_1 = require("./StackToolbar");
Object.defineProperty(exports, "StackToolbar", { enumerable: true, get: function () { return StackToolbar_1.StackToolbar; } });
var StackToolbarClient_1 = require("./StackToolbarClient");
Object.defineProperty(exports, "appendStackToolbarPropsToOptions", { enumerable: true, get: function () { return StackToolbarClient_1.appendStackToolbarPropsToOptions; } });
var StackToolbarButton_1 = require("./StackToolbarButton");
Object.defineProperty(exports, "StackToolbarButton", { enumerable: true, get: function () { return StackToolbarButton_1.StackToolbarButton; } });
var StackToolbarMenu_1 = require("./StackToolbarMenu");
Object.defineProperty(exports, "StackToolbarMenu", { enumerable: true, get: function () { return StackToolbarMenu_1.StackToolbarMenu; } });
Object.defineProperty(exports, "StackToolbarMenuAction", { enumerable: true, get: function () { return StackToolbarMenu_1.StackToolbarMenuAction; } });
var StackToolbarSearchBarSlot_1 = require("./StackToolbarSearchBarSlot");
Object.defineProperty(exports, "StackToolbarSearchBarSlot", { enumerable: true, get: function () { return StackToolbarSearchBarSlot_1.StackToolbarSearchBarSlot; } });
var StackToolbarSpacer_1 = require("./StackToolbarSpacer");
Object.defineProperty(exports, "StackToolbarSpacer", { enumerable: true, get: function () { return StackToolbarSpacer_1.StackToolbarSpacer; } });
var StackToolbarView_1 = require("./StackToolbarView");
Object.defineProperty(exports, "StackToolbarView", { enumerable: true, get: function () { return StackToolbarView_1.StackToolbarView; } });
//# sourceMappingURL=index.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/layouts/stack-utils/toolbar/index.tsx"],"names":[],"mappings":";;;AAAA,2DAO8B;AAN5B,uHAAA,iBAAiB,OAAA;AACjB,sHAAA,gBAAgB,OAAA;AAChB,uHAAA,iBAAiB,OAAA;AAMnB,+CAA8C;AAArC,4GAAA,YAAY,OAAA;AAErB,2DAAgG;AAAvF,sIAAA,gCAAgC,OAAA;AAEzC,2DAAwF;AAA/E,wHAAA,kBAAkB,OAAA;AAE3B,uDAK4B;AAJ1B,oHAAA,gBAAgB,OAAA;AAChB,0HAAA,sBAAsB,OAAA;AAKxB,yEAGqC;AAFnC,sIAAA,yBAAyB,OAAA;AAI3B,2DAAwF;AAA/E,wHAAA,kBAAkB,OAAA;AAE3B,uDAAkF;AAAzE,oHAAA,gBAAgB,OAAA","sourcesContent":["export {\n StackToolbarBadge,\n StackToolbarIcon,\n StackToolbarLabel,\n type StackToolbarBadgeProps,\n type StackToolbarIconProps,\n type StackToolbarLabelProps,\n} from './toolbar-primitives';\n\nexport { StackToolbar } from './StackToolbar';\n\nexport { appendStackToolbarPropsToOptions, type StackToolbarProps } from './StackToolbarClient';\n\nexport { StackToolbarButton, type StackToolbarButtonProps } from './StackToolbarButton';\n\nexport {\n StackToolbarMenu,\n StackToolbarMenuAction,\n type StackToolbarMenuProps,\n type StackToolbarMenuActionProps,\n} from './StackToolbarMenu';\n\nexport {\n StackToolbarSearchBarSlot,\n type StackToolbarSearchBarSlotProps,\n} from './StackToolbarSearchBarSlot';\n\nexport { StackToolbarSpacer, type StackToolbarSpacerProps } from './StackToolbarSpacer';\n\nexport { StackToolbarView, type StackToolbarViewProps } from './StackToolbarView';\n\nexport type { ToolbarPlacement } from './context';\n\nexport type { StackHeaderItemSharedProps } from './shared';\n"]}

View File

@@ -0,0 +1,49 @@
import type { NativeStackHeaderItemButton } from '@react-navigation/native-stack';
import { type ReactNode } from 'react';
import { type ColorValue, type ImageSourcePropType, type StyleProp } from 'react-native';
import type { SFSymbol } from 'sf-symbols-typescript';
import { type BasicTextStyle } from '../../../utils/font';
export interface StackHeaderItemSharedProps {
children?: ReactNode;
style?: StyleProp<BasicTextStyle>;
hidesSharedBackground?: boolean;
separateBackground?: boolean;
accessibilityLabel?: string;
accessibilityHint?: string;
disabled?: boolean;
tintColor?: ColorValue;
icon?: SFSymbol | ImageSourcePropType;
/**
* Controls how image-based icons are rendered on iOS.
*
* - `'template'`: iOS applies tint color to the icon
* - `'original'`: Preserves original icon colors (useful for multi-color icons)
*
* **Default behavior:**
* - If `tintColor` is specified, defaults to `'template'`
* - If no `tintColor`, defaults to `'original'`
*
* This prop only affects image-based icons (not SF Symbols).
*
* @see [Apple documentation](https://developer.apple.com/documentation/uikit/uiimage/renderingmode-swift.enum) for more information.
*
* @platform ios
*/
iconRenderingMode?: 'template' | 'original';
/**
* @default 'plain'
*/
variant?: 'plain' | 'done' | 'prominent';
}
type RNSharedHeaderItem = Pick<NativeStackHeaderItemButton, 'label' | 'labelStyle' | 'icon' | 'variant' | 'tintColor' | 'disabled' | 'width' | 'hidesSharedBackground' | 'sharesBackground' | 'identifier' | 'badge' | 'accessibilityLabel' | 'accessibilityHint'>;
/** @internal */
export declare function extractXcassetName(props: StackHeaderItemSharedProps): string | undefined;
/**
* Extracts the rendering mode from the Icon child component (for `src` and `xcasset` variants).
* Returns undefined if no explicit rendering mode is set on the Icon child.
* @internal
*/
export declare function extractIconRenderingMode(props: StackHeaderItemSharedProps): 'template' | 'original' | undefined;
export declare function convertStackHeaderSharedPropsToRNSharedHeaderItem(props: StackHeaderItemSharedProps, isBottomPlacement?: boolean): RNSharedHeaderItem;
export {};
//# sourceMappingURL=shared.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../../../src/layouts/stack-utils/toolbar/shared.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,gCAAgC,CAAC;AAClF,OAAO,EAAY,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AACjD,OAAO,EAAE,KAAK,UAAU,EAAE,KAAK,mBAAmB,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AACzF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAItD,OAAO,EAAiC,KAAK,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAEzF,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,KAAK,CAAC,EAAE,SAAS,CAAC,cAAc,CAAC,CAAC;IAClC,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,UAAU,CAAC;IACvB,IAAI,CAAC,EAAE,QAAQ,GAAG,mBAAmB,CAAC;IACtC;;;;;;;;;;;;;;;OAeG;IACH,iBAAiB,CAAC,EAAE,UAAU,GAAG,UAAU,CAAC;IAC5C;;OAEG;IACH,OAAO,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,WAAW,CAAC;CAC1C;AAGD,KAAK,kBAAkB,GAAG,IAAI,CAC5B,2BAA2B,EACzB,OAAO,GACP,YAAY,GACZ,MAAM,GACN,SAAS,GACT,WAAW,GACX,UAAU,GACV,OAAO,GACP,uBAAuB,GACvB,kBAAkB,GAClB,YAAY,GACZ,OAAO,GACP,oBAAoB,GACpB,mBAAmB,CACtB,CAAC;AAEF,gBAAgB;AAChB,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,0BAA0B,GAAG,MAAM,GAAG,SAAS,CAMxF;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,0BAA0B,GAChC,UAAU,GAAG,UAAU,GAAG,SAAS,CAMrC;AAED,wBAAgB,iDAAiD,CAC/D,KAAK,EAAE,0BAA0B,EACjC,iBAAiB,GAAE,OAAe,GACjC,kBAAkB,CAoEpB"}

View File

@@ -0,0 +1,95 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.extractXcassetName = extractXcassetName;
exports.extractIconRenderingMode = extractIconRenderingMode;
exports.convertStackHeaderSharedPropsToRNSharedHeaderItem = convertStackHeaderSharedPropsToRNSharedHeaderItem;
const react_1 = require("react");
const toolbar_primitives_1 = require("./toolbar-primitives");
const children_1 = require("../../../utils/children");
const font_1 = require("../../../utils/font");
/** @internal */
function extractXcassetName(props) {
const iconComponentProps = (0, children_1.getFirstChildOfType)(props.children, toolbar_primitives_1.StackToolbarIcon)?.props;
if (iconComponentProps && 'xcasset' in iconComponentProps) {
return iconComponentProps.xcasset;
}
return undefined;
}
/**
* Extracts the rendering mode from the Icon child component (for `src` and `xcasset` variants).
* Returns undefined if no explicit rendering mode is set on the Icon child.
* @internal
*/
function extractIconRenderingMode(props) {
const iconComponentProps = (0, children_1.getFirstChildOfType)(props.children, toolbar_primitives_1.StackToolbarIcon)?.props;
if (iconComponentProps && 'renderingMode' in iconComponentProps) {
return iconComponentProps.renderingMode;
}
return undefined;
}
function convertStackHeaderSharedPropsToRNSharedHeaderItem(props, isBottomPlacement = false) {
const { children, style, separateBackground, icon, ...rest } = props;
const stringChildren = react_1.Children.toArray(children)
.filter((child) => typeof child === 'string')
.join('');
const label = (0, children_1.getFirstChildOfType)(children, toolbar_primitives_1.StackToolbarLabel);
const iconPropConvertedToIcon = props.icon
? typeof props.icon === 'string'
? { sf: props.icon }
: { src: props.icon }
: undefined;
const iconComponentProps = (0, children_1.getFirstChildOfType)(children, toolbar_primitives_1.StackToolbarIcon)?.props ?? iconPropConvertedToIcon;
const badgeComponent = (0, children_1.getFirstChildOfType)(children, toolbar_primitives_1.StackToolbarBadge);
const rnsIcon = (() => {
if (!iconComponentProps) {
return undefined;
}
// Bottom placement xcasset uses native xcasset type
if ('xcasset' in iconComponentProps && isBottomPlacement) {
return {
type: 'xcasset',
name: iconComponentProps.xcasset,
};
}
// Unified image path for src and xcasset (non-bottom)
if ('src' in iconComponentProps || 'xcasset' in iconComponentProps) {
const source = 'src' in iconComponentProps ? iconComponentProps.src : { uri: iconComponentProps.xcasset };
const explicitRenderingMode = 'renderingMode' in iconComponentProps ? iconComponentProps.renderingMode : undefined;
const effectiveRenderingMode = explicitRenderingMode ??
props.iconRenderingMode ??
(props.tintColor ? 'template' : 'original');
return {
type: 'image',
source,
tinted: effectiveRenderingMode === 'template',
};
}
return {
type: 'sfSymbol',
name: iconComponentProps.sf,
};
})();
const item = {
...rest,
label: label?.props.children ?? stringChildren,
sharesBackground: !separateBackground,
};
if (style) {
const convertedStyle = (0, font_1.convertTextStyleToRNTextStyle)(style) ?? {};
item.labelStyle = convertedStyle;
}
if (badgeComponent) {
item.badge = {
value: badgeComponent.props.children ?? '',
};
const badgeStyle = (0, font_1.convertTextStyleToRNTextStyle)(badgeComponent.props.style);
if (badgeStyle) {
item.badge.style = badgeStyle;
}
}
if (rnsIcon) {
item.icon = rnsIcon;
}
return item;
}
//# sourceMappingURL=shared.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,57 @@
import { type ImageSourcePropType, type StyleProp, type TextStyle } from 'react-native';
import type { SFSymbol } from 'sf-symbols-typescript';
export interface StackToolbarLabelProps {
/**
* The text to display as the label for the tab.
*/
children?: string;
}
export declare const StackToolbarLabel: React.FC<StackToolbarLabelProps>;
export type StackToolbarIconProps = {
src: ImageSourcePropType;
/**
* Controls how the image icon is rendered on iOS.
*
* - `'template'`: iOS applies tint color to the icon
* - `'original'`: Preserves original icon colors
*
* Defaults based on parent component's `tintColor`:
* - With `tintColor`: defaults to `'template'`
* - Without `tintColor`: defaults to `'original'`
*
* @platform ios
*/
renderingMode?: 'template' | 'original';
} | {
sf: SFSymbol;
} | {
/**
* Name of an image in your Xcode asset catalog (`.xcassets`).
*
* @platform ios
*/
xcasset: string;
/**
* Controls how the xcasset icon is rendered on iOS.
*
* - `'template'`: iOS applies tint color to the icon
* - `'original'`: Preserves original icon colors
*
* Defaults based on parent component's `tintColor`:
* - With `tintColor`: defaults to `'template'`
* - Without `tintColor`: defaults to `'original'`
*
* @platform ios
*/
renderingMode?: 'template' | 'original';
};
export declare const StackToolbarIcon: React.FC<StackToolbarIconProps>;
export interface StackToolbarBadgeProps {
/**
* The text to display as the badge
*/
children?: string;
style?: StyleProp<Pick<TextStyle, 'fontFamily' | 'fontSize' | 'color' | 'fontWeight' | 'backgroundColor'>>;
}
export declare const StackToolbarBadge: React.FC<StackToolbarBadgeProps>;
//# sourceMappingURL=toolbar-primitives.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"toolbar-primitives.d.ts","sourceRoot":"","sources":["../../../../src/layouts/stack-utils/toolbar/toolbar-primitives.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,mBAAmB,EAAE,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AACxF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAItD,MAAM,WAAW,sBAAsB;IACrC;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,eAAO,MAAM,iBAAiB,EAAE,KAAK,CAAC,EAAE,CAAC,sBAAsB,CAAS,CAAC;AAEzE,MAAM,MAAM,qBAAqB,GAC7B;IAEE,GAAG,EAAE,mBAAmB,CAAC;IACzB;;;;;;;;;;;OAWG;IACH,aAAa,CAAC,EAAE,UAAU,GAAG,UAAU,CAAC;CACzC,GACD;IACE,EAAE,EAAE,QAAQ,CAAC;CACd,GACD;IACE;;;;OAIG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;;;;;;;;;;OAWG;IACH,aAAa,CAAC,EAAE,UAAU,GAAG,UAAU,CAAC;CACzC,CAAC;AAEN,eAAO,MAAM,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,CAAQ,CAAC;AAEtE,MAAM,WAAW,sBAAsB;IACrC;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,KAAK,CAAC,EAAE,SAAS,CACf,IAAI,CAAC,SAAS,EAAE,YAAY,GAAG,UAAU,GAAG,OAAO,GAAG,YAAY,GAAG,iBAAiB,CAAC,CACxF,CAAC;CACH;AAED,eAAO,MAAM,iBAAiB,EAAE,KAAK,CAAC,EAAE,CAAC,sBAAsB,CAAS,CAAC"}

View File

@@ -0,0 +1,8 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.StackToolbarBadge = exports.StackToolbarIcon = exports.StackToolbarLabel = void 0;
const primitives_1 = require("../../../primitives");
exports.StackToolbarLabel = primitives_1.Label;
exports.StackToolbarIcon = primitives_1.Icon;
exports.StackToolbarBadge = primitives_1.Badge;
//# sourceMappingURL=toolbar-primitives.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"toolbar-primitives.js","sourceRoot":"","sources":["../../../../src/layouts/stack-utils/toolbar/toolbar-primitives.tsx"],"names":[],"mappings":";;;AAGA,oDAAyD;AAS5C,QAAA,iBAAiB,GAAqC,kBAAK,CAAC;AA6C5D,QAAA,gBAAgB,GAAoC,iBAAI,CAAC;AAazD,QAAA,iBAAiB,GAAqC,kBAAK,CAAC","sourcesContent":["import { type ImageSourcePropType, type StyleProp, type TextStyle } from 'react-native';\nimport type { SFSymbol } from 'sf-symbols-typescript';\n\nimport { Badge, Icon, Label } from '../../../primitives';\n\nexport interface StackToolbarLabelProps {\n /**\n * The text to display as the label for the tab.\n */\n children?: string;\n}\n\nexport const StackToolbarLabel: React.FC<StackToolbarLabelProps> = Label;\n\nexport type StackToolbarIconProps =\n | {\n // TODO: add support for vector icons\n src: ImageSourcePropType;\n /**\n * Controls how the image icon is rendered on iOS.\n *\n * - `'template'`: iOS applies tint color to the icon\n * - `'original'`: Preserves original icon colors\n *\n * Defaults based on parent component's `tintColor`:\n * - With `tintColor`: defaults to `'template'`\n * - Without `tintColor`: defaults to `'original'`\n *\n * @platform ios\n */\n renderingMode?: 'template' | 'original';\n }\n | {\n sf: SFSymbol;\n }\n | {\n /**\n * Name of an image in your Xcode asset catalog (`.xcassets`).\n *\n * @platform ios\n */\n xcasset: string;\n /**\n * Controls how the xcasset icon is rendered on iOS.\n *\n * - `'template'`: iOS applies tint color to the icon\n * - `'original'`: Preserves original icon colors\n *\n * Defaults based on parent component's `tintColor`:\n * - With `tintColor`: defaults to `'template'`\n * - Without `tintColor`: defaults to `'original'`\n *\n * @platform ios\n */\n renderingMode?: 'template' | 'original';\n };\n\nexport const StackToolbarIcon: React.FC<StackToolbarIconProps> = Icon;\n\nexport interface StackToolbarBadgeProps {\n /**\n * The text to display as the badge\n */\n children?: string;\n\n style?: StyleProp<\n Pick<TextStyle, 'fontFamily' | 'fontSize' | 'color' | 'fontWeight' | 'backgroundColor'>\n >;\n}\n\nexport const StackToolbarBadge: React.FC<StackToolbarBadgeProps> = Badge;\n"]}