expo-router
Version:
Expo Router is a file-based router for React Native and web applications.
215 lines (213 loc) • 9.07 kB
JavaScript
'use client';
;
Object.defineProperty(exports, "__esModule", {
value: true
});
0 && (module.exports = {
StackToolbarMenu: null,
StackToolbarMenuAction: null,
convertStackToolbarMenuActionPropsToRNHeaderItem: null,
convertStackToolbarMenuPropsToRNHeaderItem: null
});
function _export(target, all) {
for(var name in all)Object.defineProperty(target, name, {
enumerable: true,
get: Object.getOwnPropertyDescriptor(all, name).get
});
}
_export(exports, {
get StackToolbarMenu () {
return StackToolbarMenu;
},
get StackToolbarMenuAction () {
return StackToolbarMenuAction;
},
get convertStackToolbarMenuActionPropsToRNHeaderItem () {
return convertStackToolbarMenuActionPropsToRNHeaderItem;
},
get convertStackToolbarMenuPropsToRNHeaderItem () {
return convertStackToolbarMenuPropsToRNHeaderItem;
}
});
function _jsxruntime() {
const data = require("react/jsx-runtime");
_jsxruntime = function() {
return data;
};
return data;
}
function _react() {
const data = require("react");
_react = function() {
return data;
};
return data;
}
const _children = require("../../../../utils/children");
const _context = require("../context");
const _shared = require("../shared");
const _toolbarprimitives = require("../toolbar-primitives");
const _native = require("./native");
/**
* 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.getFirstChildOfType)(children, _toolbarprimitives.StackToolbarLabel);
const labelFromChild = labelChild?.props.children;
return {
label: labelFromChild ?? title ?? '',
menuTitle: title ?? ''
};
}
const StackToolbarMenu = (props)=>{
const placement = (0, _context.useToolbarPlacement)();
if (process.env.EXPO_OS === 'ios' && placement !== 'bottom' || placement == null) {
// On ios, 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().useMemo)(()=>(0, _children.filterAllowedChildrenElements)(props.children, ALLOWED_CHILDREN), [
props.children
]);
// Use shared conversion that doesn't bail on hidden, so source is always computed
// and NativeToolbarMenu always renders — letting AnimatedItemContainer handle visibility.
const sharedProps = (0, _shared.convertStackHeaderSharedPropsToRNSharedHeaderItem)(props, true);
const { label: computedLabel, menuTitle: computedMenuTitle } = computeMenuLabelAndTitle(props.children, props.title);
const icon = sharedProps?.icon?.type === 'sfSymbol' ? sharedProps.icon.name : undefined;
const source = sharedProps?.icon?.type === 'image' ? sharedProps.icon.source : undefined;
const xcassetName = (0, _shared.extractXcassetName)(props);
const imageRenderingMode = (0, _shared.extractIconRenderingMode)(props) ?? props.iconRenderingMode;
if (process.env.NODE_ENV !== 'production') {
const allChildren = _react().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' && placement === 'bottom') {
const hasBadge = (0, _children.getFirstChildOfType)(props.children, _toolbarprimitives.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 /*#__PURE__*/ (0, _jsxruntime().jsx)(_native.NativeToolbarMenu, {
...props,
icon: icon,
source: source,
xcassetName: xcassetName,
image: props.image,
imageRenderingMode: imageRenderingMode,
label: computedLabel,
title: computedMenuTitle,
badge: sharedProps?.badge,
children: validChildren
});
};
function convertStackToolbarMenuPropsToRNHeaderItem(props, isBottomPlacement = false) {
if (props.hidden) {
return undefined;
}
const { title, ...rest } = props;
const actions = _react().Children.toArray(props.children).filter((child)=>(0, _children.isChildOfType)(child, StackToolbarMenuAction) || (0, _children.isChildOfType)(child, StackToolbarMenu));
const { label: computedLabel, menuTitle: computedMenuTitle } = computeMenuLabelAndTitle(props.children, title);
const sharedProps = (0, _shared.convertStackHeaderSharedPropsToRNSharedHeaderItem)(rest, isBottomPlacement);
const item = {
...sharedProps,
label: computedLabel,
type: 'menu',
menu: {
multiselectable: true,
items: actions.map((action)=>{
if ((0, _children.isChildOfType)(action, StackToolbarMenu)) {
return convertStackToolbarSubmenuMenuPropsToRNHeaderItem(action.props);
}
return convertStackToolbarMenuActionPropsToRNHeaderItem(action.props);
}).filter((i)=>!!i)
}
};
if (computedMenuTitle) {
item.menu.title = computedMenuTitle;
}
return item;
}
function convertStackToolbarSubmenuMenuPropsToRNHeaderItem(props) {
if (props.hidden) {
return undefined;
}
const sharedProps = (0, _shared.convertStackHeaderSharedPropsToRNSharedHeaderItem)(props);
const actions = _react().Children.toArray(props.children).filter((child)=>(0, _children.isChildOfType)(child, StackToolbarMenuAction) || (0, _children.isChildOfType)(child, StackToolbarMenu));
const item = {
type: 'submenu',
items: actions.map((action)=>{
if ((0, _children.isChildOfType)(action, 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) {
item.icon = sharedProps.icon;
}
return item;
}
const StackToolbarMenuAction = (props)=>{
const placement = (0, _context.useToolbarPlacement)();
if (process.env.EXPO_OS === 'ios' && 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;
const source = typeof props.icon !== 'string' ? props.icon : undefined;
return /*#__PURE__*/ (0, _jsxruntime().jsx)(_native.NativeToolbarMenuAction, {
...props,
icon: icon,
source: source,
image: props.image,
imageRenderingMode: props.iconRenderingMode
});
};
function convertStackToolbarMenuActionPropsToRNHeaderItem(props) {
const { children, isOn, unstable_keepPresented, icon, ...rest } = props;
const sharedProps = (0, _shared.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) {
item.icon = sharedProps.icon;
}
return item;
}
const ALLOWED_CHILDREN = [
StackToolbarMenu,
StackToolbarMenuAction,
_native.NativeToolbarMenu,
_native.NativeToolbarMenuAction,
_toolbarprimitives.StackToolbarLabel,
_toolbarprimitives.StackToolbarIcon,
_toolbarprimitives.StackToolbarBadge
];
//# sourceMappingURL=index.js.map