UNPKG

one

Version:

One is a React framework for web, iOS, and Android apps.

481 lines (480 loc) • 19.3 kB
var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var stackToolbarDescriptors_exports = {}; __export(stackToolbarDescriptors_exports, { TOOLBAR_KIND: () => TOOLBAR_KIND, appendStackToolbarPropsToOptions: () => appendStackToolbarPropsToOptions, isToolbarKind: () => isKind, toolbarChildrenToBottomData: () => toolbarChildrenToBottomData, toolbarChildrenToHeaderItems: () => toolbarChildrenToHeaderItems, toolbarChildrenToString: () => toolbarChildrenToString }); module.exports = __toCommonJS(stackToolbarDescriptors_exports); var import_react = require("react"); const TOOLBAR_KIND = "__oneStackToolbarKind"; function kindOf(element) { if (!(0, import_react.isValidElement)(element)) return void 0; return 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) { if (!(0, import_react.isValidElement)(child)) return "unknown"; const type = child.type; if (typeof type === "string") return type; if (typeof type === "symbol") return "Fragment"; return type?.name ?? "unknown"; } function fallbackIdentifier(slot, index, title) { const slug = (title ?? "item").toLowerCase().replace(/[^a-z0-9]+/g, "-").slice(0, 24) || "item"; return `stack-toolbar-${slot}-${slug}-${index}`; } function firstChildOfKind(children, kind) { let found = null; import_react.Children.forEach(children, (child) => { if (found || !(0, import_react.isValidElement)(child)) return; if (isKind(child, kind)) found = child; }); return found; } function toolbarChildrenToString(children) { return import_react.Children.toArray(children).filter((child) => typeof child === "string" || typeof child === "number").join(""); } function resolveIcon(iconProp, children) { const iconChild = firstChildOfKind(children, "icon"); if (iconChild) { const 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; const all = import_react.Children.toArray(children); const valid = all.filter((child) => typeof child === "string" || typeof child === "number" || (0, import_react.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; const all = import_react.Children.toArray(children); const valid = all.filter((child) => (0, import_react.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) { const labelChild = firstChildOfKind(children, "label"); if (labelChild) return labelChild.props.children ?? ""; return toolbarChildrenToString(children); } function badgeFromChildren(children) { const badgeChild = firstChildOfKind(children, "badge"); if (!badgeChild) return void 0; const props = badgeChild.props; const style = props.style; return { value: props.children ?? "", ...style?.backgroundColor !== void 0 && { backgroundColor: style.backgroundColor }, ...style?.color !== void 0 && { color: style.color }, ...style?.fontFamily !== void 0 && { fontFamily: style.fontFamily }, ...style?.fontSize !== void 0 && { fontSize: style.fontSize }, ...style?.fontWeight !== void 0 && { fontWeight: style.fontWeight } }; } function labelStyleFromStyle(style) { if (!style) return void 0; const flat = style; const 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) { if (!resolution) return void 0; if (resolution.form === "sf") { return { type: "sfSymbol", name: resolution.name }; } const source = resolution.form === "src" ? resolution.source : { uri: resolution.name }; const effectiveMode = resolution.renderingMode ?? iconRenderingMode ?? (tintColor ? "template" : "original"); return { type: "image", source, tinted: effectiveMode === "template" }; } function buttonPropsToHeaderButton(props) { if (props.hidden) return null; checkButtonChildren(props.children); const label = labelFromChildren(props.children); const icon = headerIconFromResolution(resolveIcon(props.icon, props.children), props.iconRenderingMode, props.tintColor); const 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 ?? (() => {}), selected: !!props.selected }; } function menuLabelAndTitle(children, title) { const labelChild = firstChildOfKind(children, "label"); const labelFromChild = labelChild?.props?.children; return { label: labelFromChild ?? title ?? "", menuTitle: title ?? "" }; } function menuActionPropsToHeaderAction(props) { const { children, subtitle, isOn, unstable_keepPresented, icon, image, iconRenderingMode, ...rest } = props; void image; const shared = labelFromChildren(children); const iconResolution = resolveIcon(icon, children); const item = { ...rest, description: subtitle, type: "action", label: shared, state: isOn ? "on" : "off", onPress: props.onPress ?? (() => {}) }; if (unstable_keepPresented !== void 0) { item.keepsMenuPresented = unstable_keepPresented; } const headerIcon = headerIconFromResolution(iconResolution, iconRenderingMode, void 0); if (headerIcon) item.icon = headerIcon; return item; } function menuElementToHeaderSubmenu(element, placement) { const props = element.props; if (props.hidden) return null; checkMenuChildren(props.children); const 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) { const items = []; import_react.Children.forEach(children, (child) => { if (!(0, import_react.isValidElement)(child)) return; if (isKind(child, "menuAction")) { items.push(menuActionPropsToHeaderAction(child.props)); } else if (isKind(child, "menu")) { const submenu = menuElementToHeaderSubmenu(child, placement); if (submenu) items.push(submenu); } }); return items; } function menuPropsToHeaderMenu(element, placement) { const props = element.props; if (props.hidden) return null; checkMenuChildren(props.children); const { label, menuTitle } = menuLabelAndTitle(props.children, props.title); const icon = headerIconFromResolution(resolveIcon(props.icon, props.children), props.iconRenderingMode, props.tintColor); const 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) { const items = []; import_react.Children.forEach(children, (child) => { if (!(0, import_react.isValidElement)(child)) return; if (isKind(child, "button")) { const item = buttonPropsToHeaderButton(child.props); if (item) items.push(item); } else if (isKind(child, "menu")) { const menu = menuPropsToHeaderMenu(child, placement); if (menu) items.push(menu); } else if (isKind(child, "spacer")) { const 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) { const resolution = resolveIcon(iconProp, children); if (!resolution) { return { ...imageProp !== void 0 && { image: imageProp, imageRenderingMode: iconRenderingMode ?? (tintColor !== void 0 ? "template" : "original") } }; } if (resolution.form === "sf") return { sfName: resolution.name }; if (resolution.form === "xcasset") { return { xcassetName: resolution.name, imageRenderingMode: 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) { checkButtonChildren(props.children); const title = labelFromChildren(props.children) || void 0; const { 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 ?? (() => {}) }; } function bottomMenuActionData(props, index, path) { const title = labelFromChildren(props.children); const { 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 ?? (() => {}) }; } function bottomMenuChildren(children, path) { const data = []; import_react.Children.forEach(children, (child, index) => { if (!(0, import_react.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) { const props = element.props; checkMenuChildren(props.children); const title = labelFromChildren(props.children) || props.title || ""; const { 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); const { label, menuTitle } = menuLabelAndTitle(props.children, props.title); const { 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) { const data = []; import_react.Children.forEach(children, (child, index) => { if (!(0, import_react.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")) { const 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")) { const props = child.props; data.push({ kind: "searchBar", identifier: fallbackIdentifier("bottom", index, "search"), sharesBackground: !props.separateBackground, hidesSharedBackground: props.hidesSharedBackground, hidden: props.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) { const placement = 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) { const key2 = placement === "left" ? "headerLeft" : "headerRight"; return { ...options, headerShown: true, [key2]: () => props.children }; } const items = toolbarChildrenToHeaderItems(props.children, placement); const key = placement === "left" ? "unstable_headerLeftItems" : "unstable_headerRightItems"; return { ...options, headerShown: true, [key]: () => items }; }