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,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"]}