one
Version:
One is a React framework for web, iOS, and Android apps.
478 lines (475 loc) • 19.9 kB
JavaScript
import { Children, isValidElement } from "react";
var TOOLBAR_KIND = "__oneStackToolbarKind";
function kindOf(element) {
var _element_type;
if (!isValidElement(element)) return void 0;
return (_element_type = element.type) === null || _element_type === void 0 ? void 0 : _element_type[TOOLBAR_KIND];
}
function isKind(element, kind) {
return kindOf(element) === kind;
}
function warn(message) {
if (process.env.NODE_ENV !== "production") {
console.warn(message);
}
}
function childName(child) {
var _ref;
if (!isValidElement(child)) return "unknown";
var type = child.type;
if (typeof type === "string") return type;
if (typeof type === "symbol") return "Fragment";
return (_ref = type === null || type === void 0 ? void 0 : type.name) !== null && _ref !== void 0 ? _ref : "unknown";
}
function fallbackIdentifier(slot, index, title) {
var slug = (title !== null && title !== void 0 ? title : "item").toLowerCase().replace(/[^a-z0-9]+/g, "-").slice(0, 24) || "item";
return `stack-toolbar-${slot}-${slug}-${index}`;
}
function firstChildOfKind(children, kind) {
var found = null;
Children.forEach(children, function(child) {
if (found || !isValidElement(child)) return;
if (isKind(child, kind)) found = child;
});
return found;
}
function toolbarChildrenToString(children) {
return Children.toArray(children).filter(function(child) {
return typeof child === "string" || typeof child === "number";
}).join("");
}
function resolveIcon(iconProp, children) {
var iconChild = firstChildOfKind(children, "icon");
if (iconChild) {
var props = iconChild.props;
if ("sf" in props) return {
form: "sf",
name: props.sf
};
if ("xcasset" in props) return {
form: "xcasset",
name: props.xcasset,
renderingMode: props.renderingMode
};
return {
form: "src",
source: props.src,
renderingMode: props.renderingMode
};
}
if (typeof iconProp === "string") return {
form: "sf",
name: iconProp
};
if (iconProp) return {
form: "src",
source: iconProp
};
return void 0;
}
function checkButtonChildren(children) {
if (process.env.NODE_ENV === "production" || typeof children === "string") return;
var all = Children.toArray(children);
var valid = all.filter(function(child) {
return typeof child === "string" || typeof child === "number" || isValidElement(child) && (isKind(child, "label") || isKind(child, "icon") || isKind(child, "badge"));
});
if (all.length !== valid.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.");
}
}
function checkMenuChildren(children) {
if (process.env.NODE_ENV === "production") return;
var all = Children.toArray(children);
var valid = all.filter(function(child) {
return isValidElement(child) && (isKind(child, "menu") || isKind(child, "menuAction") || isKind(child, "label") || isKind(child, "icon") || isKind(child, "badge"));
});
if (all.length !== valid.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.");
}
}
function labelFromChildren(children) {
var _labelChild_props_children;
var labelChild = firstChildOfKind(children, "label");
if (labelChild) return (_labelChild_props_children = labelChild.props.children) !== null && _labelChild_props_children !== void 0 ? _labelChild_props_children : "";
return toolbarChildrenToString(children);
}
function badgeFromChildren(children) {
var _props_children;
var badgeChild = firstChildOfKind(children, "badge");
if (!badgeChild) return void 0;
var props = badgeChild.props;
var style = props.style;
return {
value: (_props_children = props.children) !== null && _props_children !== void 0 ? _props_children : "",
...(style === null || style === void 0 ? void 0 : style.backgroundColor) !== void 0 && { backgroundColor: style.backgroundColor },
...(style === null || style === void 0 ? void 0 : style.color) !== void 0 && { color: style.color },
...(style === null || style === void 0 ? void 0 : style.fontFamily) !== void 0 && { fontFamily: style.fontFamily },
...(style === null || style === void 0 ? void 0 : style.fontSize) !== void 0 && { fontSize: style.fontSize },
...(style === null || style === void 0 ? void 0 : style.fontWeight) !== void 0 && { fontWeight: style.fontWeight }
};
}
function labelStyleFromStyle(style) {
if (!style) return void 0;
var flat = style;
var picked = {
...flat.fontFamily !== void 0 && { fontFamily: flat.fontFamily },
...flat.fontSize !== void 0 && { fontSize: flat.fontSize },
...flat.fontWeight !== void 0 && { fontWeight: flat.fontWeight },
...flat.color !== void 0 && { color: flat.color }
};
return Object.keys(picked).length ? picked : void 0;
}
function headerIconFromResolution(resolution, iconRenderingMode, tintColor) {
var _ref, _resolution_renderingMode;
if (!resolution) return void 0;
if (resolution.form === "sf") {
return {
type: "sfSymbol",
name: resolution.name
};
}
var source = resolution.form === "src" ? resolution.source : { uri: resolution.name };
var effectiveMode = (_ref = (_resolution_renderingMode = resolution.renderingMode) !== null && _resolution_renderingMode !== void 0 ? _resolution_renderingMode : iconRenderingMode) !== null && _ref !== void 0 ? _ref : tintColor ? "template" : "original";
return {
type: "image",
source,
tinted: effectiveMode === "template"
};
}
function buttonPropsToHeaderButton(props) {
var _props_onPress;
if (props.hidden) return null;
checkButtonChildren(props.children);
var label = labelFromChildren(props.children);
var icon = headerIconFromResolution(resolveIcon(props.icon, props.children), props.iconRenderingMode, props.tintColor);
var badge = badgeFromChildren(props.children);
return {
type: "button",
label,
...icon && { icon },
...props.variant !== void 0 && { variant: props.variant },
...props.tintColor !== void 0 && { tintColor: props.tintColor },
...props.disabled !== void 0 && { disabled: props.disabled },
sharesBackground: !props.separateBackground,
...props.hidesSharedBackground !== void 0 && { hidesSharedBackground: props.hidesSharedBackground },
...badge && { badge: {
value: badge.value,
...(badge.color !== void 0 || badge.backgroundColor !== void 0 || badge.fontFamily !== void 0 || badge.fontSize !== void 0 || badge.fontWeight !== void 0) && { style: {
...badge.color !== void 0 && { color: badge.color },
...badge.backgroundColor !== void 0 && { backgroundColor: badge.backgroundColor },
...badge.fontFamily !== void 0 && { fontFamily: badge.fontFamily },
...badge.fontSize !== void 0 && { fontSize: badge.fontSize },
...badge.fontWeight !== void 0 && { fontWeight: badge.fontWeight }
} }
} },
...labelStyleFromStyle(props.style) && { labelStyle: labelStyleFromStyle(props.style) },
...props.accessibilityLabel !== void 0 && { accessibilityLabel: props.accessibilityLabel },
...props.accessibilityHint !== void 0 && { accessibilityHint: props.accessibilityHint },
onPress: (_props_onPress = props.onPress) !== null && _props_onPress !== void 0 ? _props_onPress : function() {},
selected: !!props.selected
};
}
function menuLabelAndTitle(children, title) {
var _ref;
var _labelChild_props;
var labelChild = firstChildOfKind(children, "label");
var labelFromChild = labelChild === null || labelChild === void 0 ? void 0 : (_labelChild_props = labelChild.props) === null || _labelChild_props === void 0 ? void 0 : _labelChild_props.children;
return {
label: (_ref = labelFromChild !== null && labelFromChild !== void 0 ? labelFromChild : title) !== null && _ref !== void 0 ? _ref : "",
menuTitle: title !== null && title !== void 0 ? title : ""
};
}
function menuActionPropsToHeaderAction(props) {
var _props_onPress;
var { children, subtitle, isOn, unstable_keepPresented, icon, image, iconRenderingMode, ...rest } = props;
void image;
var shared = labelFromChildren(children);
var iconResolution = resolveIcon(icon, children);
var item = {
...rest,
description: subtitle,
type: "action",
label: shared,
state: isOn ? "on" : "off",
onPress: (_props_onPress = props.onPress) !== null && _props_onPress !== void 0 ? _props_onPress : function() {}
};
if (unstable_keepPresented !== void 0) {
item.keepsMenuPresented = unstable_keepPresented;
}
var headerIcon = headerIconFromResolution(iconResolution, iconRenderingMode, void 0);
if (headerIcon) item.icon = headerIcon;
return item;
}
function menuElementToHeaderSubmenu(element, placement) {
var props = element.props;
if (props.hidden) return null;
checkMenuChildren(props.children);
var items = menuChildrenToHeaderMenuItems(props.children, placement);
return {
type: "submenu",
label: labelFromChildren(props.children) || props.title || "",
multiselectable: true,
...props.inline !== void 0 && { inline: props.inline },
...props.palette !== void 0 && { layout: props.palette ? "palette" : "default" },
...props.destructive !== void 0 && { destructive: props.destructive },
...headerIconFromResolution(resolveIcon(props.icon, props.children), props.iconRenderingMode, props.tintColor) && { icon: headerIconFromResolution(resolveIcon(props.icon, props.children), props.iconRenderingMode, props.tintColor) },
items
};
}
function menuChildrenToHeaderMenuItems(children, placement) {
var items = [];
Children.forEach(children, function(child) {
if (!isValidElement(child)) return;
if (isKind(child, "menuAction")) {
items.push(menuActionPropsToHeaderAction(child.props));
} else if (isKind(child, "menu")) {
var submenu = menuElementToHeaderSubmenu(child, placement);
if (submenu) items.push(submenu);
}
});
return items;
}
function menuPropsToHeaderMenu(element, placement) {
var props = element.props;
if (props.hidden) return null;
checkMenuChildren(props.children);
var { label, menuTitle } = menuLabelAndTitle(props.children, props.title);
var icon = headerIconFromResolution(resolveIcon(props.icon, props.children), props.iconRenderingMode, props.tintColor);
var badge = badgeFromChildren(props.children);
return {
type: "menu",
label,
...icon && { icon },
...props.variant !== void 0 && { variant: props.variant },
...props.tintColor !== void 0 && { tintColor: props.tintColor },
...props.disabled !== void 0 && { disabled: props.disabled },
sharesBackground: !props.separateBackground,
...props.hidesSharedBackground !== void 0 && { hidesSharedBackground: props.hidesSharedBackground },
...badge && { badge: { value: badge.value } },
...props.accessibilityLabel !== void 0 && { accessibilityLabel: props.accessibilityLabel },
...props.accessibilityHint !== void 0 && { accessibilityHint: props.accessibilityHint },
menu: {
...menuTitle ? { title: menuTitle } : null,
multiselectable: true,
...props.palette !== void 0 && { layout: props.palette ? "palette" : "default" },
items: menuChildrenToHeaderMenuItems(props.children, placement)
}
};
}
function spacerPropsToHeaderSpacing(props, placement) {
if (props.hidden) return null;
if (props.width === void 0) {
warn(`Warning: Stack.Toolbar.Spacer requires \`width\` when used in ${placement} placement. Flexible spacers are only supported in bottom placement.`);
return null;
}
return {
type: "spacing",
spacing: props.width
};
}
function toolbarChildrenToHeaderItems(children, placement) {
var items = [];
Children.forEach(children, function(child) {
if (!isValidElement(child)) return;
if (isKind(child, "button")) {
var item = buttonPropsToHeaderButton(child.props);
if (item) items.push(item);
} else if (isKind(child, "menu")) {
var menu = menuPropsToHeaderMenu(child, placement);
if (menu) items.push(menu);
} else if (isKind(child, "spacer")) {
var spacing = spacerPropsToHeaderSpacing(child.props, placement);
if (spacing) items.push(spacing);
} else {
warn(`Warning: Stack.Toolbar with placement="${placement}" only accepts <Stack.Toolbar.Button>, <Stack.Toolbar.Menu>, and <Stack.Toolbar.Spacer> as children. Found invalid child: ${childName(child)}`);
}
});
return items;
}
function bottomIconFields(iconProp, imageProp, iconRenderingMode, tintColor, children, component) {
var resolution = resolveIcon(iconProp, children);
if (!resolution) {
return { ...imageProp !== void 0 && {
image: imageProp,
imageRenderingMode: iconRenderingMode !== null && iconRenderingMode !== void 0 ? iconRenderingMode : tintColor !== void 0 ? "template" : "original"
} };
}
if (resolution.form === "sf") return { sfName: resolution.name };
if (resolution.form === "xcasset") {
var _resolution_renderingMode;
return {
xcassetName: resolution.name,
imageRenderingMode: (_resolution_renderingMode = resolution.renderingMode) !== null && _resolution_renderingMode !== void 0 ? _resolution_renderingMode : iconRenderingMode
};
}
warn(`Warning: Stack.Toolbar.${component} in bottom placement does not support image icons via the \`icon\` prop or <Stack.Toolbar.Icon src={...} />; the image will not render. Use the \`icon\` prop with an SF Symbol name, the \`image\` prop, or <Stack.Toolbar.Icon xcasset="..." />.`);
return {};
}
function bottomButtonData(props, index) {
var _props_onPress;
checkButtonChildren(props.children);
var title = labelFromChildren(props.children) || void 0;
var { sfName, ...iconRest } = bottomIconFields(props.icon, props.image, props.iconRenderingMode, props.tintColor, props.children, "Button");
return {
kind: "button",
identifier: fallbackIdentifier("bottom", index, title),
...title !== void 0 && { title },
...sfName !== void 0 && { systemImageName: sfName },
...iconRest,
tintColor: props.tintColor,
...props.variant !== void 0 && { barButtonItemStyle: props.variant },
sharesBackground: !props.separateBackground,
hidesSharedBackground: props.hidesSharedBackground,
hidden: props.hidden,
selected: props.selected,
disabled: props.disabled,
badgeConfiguration: badgeFromChildren(props.children),
...labelStyleFromStyle(props.style) && { titleStyle: labelStyleFromStyle(props.style) },
accessibilityLabel: props.accessibilityLabel,
accessibilityHint: props.accessibilityHint,
handler: (_props_onPress = props.onPress) !== null && _props_onPress !== void 0 ? _props_onPress : function() {}
};
}
function bottomMenuActionData(props, index, path) {
var _props_onPress;
var title = labelFromChildren(props.children);
var { sfName, ...iconRest } = bottomIconFields(props.icon, props.image, props.iconRenderingMode, void 0, props.children, "MenuAction");
return {
kind: "action",
identifier: fallbackIdentifier(path, index, title),
title,
...sfName !== void 0 && { icon: sfName },
...iconRest,
disabled: props.disabled,
destructive: props.destructive,
hidden: props.hidden,
isOn: props.isOn,
keepPresented: props.unstable_keepPresented,
discoverabilityLabel: props.discoverabilityLabel,
subtitle: props.subtitle,
accessibilityLabel: props.accessibilityLabel,
accessibilityHint: props.accessibilityHint,
handler: (_props_onPress = props.onPress) !== null && _props_onPress !== void 0 ? _props_onPress : function() {}
};
}
function bottomMenuChildren(children, path) {
var data = [];
Children.forEach(children, function(child, index) {
if (!isValidElement(child)) return;
if (isKind(child, "menuAction")) {
data.push(bottomMenuActionData(child.props, index, path));
} else if (isKind(child, "menu")) {
data.push(bottomSubmenuData(child, index, path));
}
});
return data;
}
function bottomSubmenuData(element, index, path) {
var props = element.props;
checkMenuChildren(props.children);
var title = labelFromChildren(props.children) || props.title || "";
var { sfName, ...iconRest } = bottomIconFields(props.icon, props.image, props.iconRenderingMode, props.tintColor, props.children, "Menu");
return {
kind: "submenu",
identifier: fallbackIdentifier(path, index, title),
title,
...sfName !== void 0 && { icon: sfName },
...iconRest,
destructive: props.destructive,
hidden: props.hidden,
inline: props.inline,
palette: props.palette,
accessibilityLabel: props.accessibilityLabel,
accessibilityHint: props.accessibilityHint,
children: bottomMenuChildren(props.children, `${path}-submenu-${index}`)
};
}
function bottomMenuData(props, index) {
checkMenuChildren(props.children);
var { label, menuTitle } = menuLabelAndTitle(props.children, props.title);
var { sfName, ...iconRest } = bottomIconFields(props.icon, props.image, props.iconRenderingMode, props.tintColor, props.children, "Menu");
return {
kind: "menu",
identifier: fallbackIdentifier("bottom", index, label || menuTitle),
title: menuTitle,
label,
...sfName !== void 0 && { systemImageName: sfName },
...iconRest,
tintColor: props.tintColor,
...props.variant !== void 0 && { barButtonItemStyle: props.variant },
sharesBackground: !props.separateBackground,
hidesSharedBackground: props.hidesSharedBackground,
disabled: props.disabled,
destructive: props.destructive,
hidden: props.hidden,
inline: props.inline,
palette: props.palette,
elementSize: props.elementSize,
accessibilityLabel: props.accessibilityLabel,
accessibilityHint: props.accessibilityHint,
children: bottomMenuChildren(props.children, `bottom-menu-${index}`)
};
}
function toolbarChildrenToBottomData(children) {
var data = [];
Children.forEach(children, function(child, index) {
if (!isValidElement(child)) return;
if (isKind(child, "button")) {
data.push(bottomButtonData(child.props, index));
} else if (isKind(child, "menu")) {
data.push(bottomMenuData(child.props, index));
} else if (isKind(child, "spacer")) {
var props = child.props;
data.push({
kind: "spacer",
identifier: fallbackIdentifier("bottom", index, "spacer"),
...props.width !== void 0 && { width: props.width },
...props.sharesBackground !== void 0 && { sharesBackground: props.sharesBackground },
...props.hidden !== void 0 && { hidden: props.hidden }
});
} else if (isKind(child, "searchBarSlot")) {
var props1 = child.props;
data.push({
kind: "searchBar",
identifier: fallbackIdentifier("bottom", index, "search"),
sharesBackground: !props1.separateBackground,
hidesSharedBackground: props1.hidesSharedBackground,
hidden: props1.hidden
});
} else {
warn(`Warning: Stack.Toolbar with bottom placement only accepts <Stack.Toolbar.Button>, <Stack.Toolbar.Menu>, <Stack.Toolbar.Spacer>, and <Stack.Toolbar.SearchBarSlot> as children. Found invalid child: ${childName(child)}`);
}
});
return data;
}
function appendStackToolbarPropsToOptions(options, props) {
var _props_placement;
var placement = (_props_placement = props.placement) !== null && _props_placement !== void 0 ? _props_placement : "bottom";
if (placement === "bottom") {
if (process.env.NODE_ENV !== "production") {
warn("Warning: Stack.Toolbar with bottom placement in layout config has no effect. Mount it in screen content so the toolbar host sits inside the screen view controller.");
}
return options;
}
if (props.asChild) {
var key = placement === "left" ? "headerLeft" : "headerRight";
return {
...options,
headerShown: true,
[key]: function() {
return props.children;
}
};
}
var items = toolbarChildrenToHeaderItems(props.children, placement);
var key1 = placement === "left" ? "unstable_headerLeftItems" : "unstable_headerRightItems";
return {
...options,
headerShown: true,
[key1]: function() {
return items;
}
};
}
export { TOOLBAR_KIND, appendStackToolbarPropsToOptions, isKind as isToolbarKind, toolbarChildrenToBottomData, toolbarChildrenToHeaderItems, toolbarChildrenToString };
//# sourceMappingURL=stackToolbarDescriptors.native.js.map