import { AppBar as SurfaceAppBar, type ButtonIconSpec } from '@ankhorage/surface'; import React from 'react'; import type { AppBarMode, AppBarOverflowMenu, AppBarProps } from '../../../../types/app-bar'; import { IconButton } from '../../../button/public'; import { View } from '../../../layout/public'; import { PopoverMenu } from '../../../popover-menu/public'; import { withZoraThemeScope } from '../../../theme/adapters/inbound/withZoraThemeScope'; import { useZoraTheme } from '../../../theme/composition/useZoraTheme'; import { Heading, Text } from '../../../typography/public'; const DEFAULT_CANCEL_ICON = { name: 'close-outline' } satisfies ButtonIconSpec; const DEFAULT_OVERFLOW_ICON = { name: 'ellipsis-vertical' } satisfies ButtonIconSpec; /*** Renders a top app bar with title/subtitle and optional leading/trailing actions. */ export const AppBar = withZoraThemeScope(AppBarInner); /*** Composes the opinionated ZORA AppBar over the Surface layout primitive. */ function AppBarInner({ themeId: _themeId, mode: _mode, title, subtitle, leading, actions, overflow, appMode, children, safeAreaTop = true, divider = true, testID, interactionPolicy, }: AppBarProps) { const { theme } = useZoraTheme(); const resolvedMode = resolveMode(appMode); const isSelectionMode = resolvedMode.type === 'selection'; const resolvedLeading = leading ?? resolveSelectionLeading(resolvedMode, interactionPolicy); const overflowMenu = resolveOverflowMenu(overflow, interactionPolicy, testID); const resolvedTrailing = resolveTrailing(actions, overflowMenu); const resolvedCenter = resolveCenter({ children, isSelectionMode, resolvedMode, subtitle, title, }); return ( {resolvedCenter ? {resolvedCenter} : null} ); } /*** Resolves the effective AppBar mode. */ function resolveMode(mode: AppBarMode | undefined): AppBarMode { return mode ?? { type: 'default' }; } /*** Formats the active selection label with its optional count. */ function resolveSelectionLabel({ count, label }: { count?: number; label: string }): string { if (count === undefined) return label; return `${label} (${count})`; } /*** Resolves the accessible label for the overflow menu trigger. */ function resolveOverflowLabel(overflow: AppBarOverflowMenu): string { return overflow.label ?? 'More options'; } /*** Resolves the accessible label for leaving selection mode. */ function resolveCancelLabel(mode: Extract): string { return mode.cancelLabel ?? 'Cancel selection'; } /*** Resolves the selection action shown at the leading edge. */ function resolveSelectionLeading( mode: AppBarMode, interactionPolicy: AppBarProps['interactionPolicy'], ): React.ReactNode | undefined { if (mode.type !== 'selection') return undefined; return ( ); } /*** Resolves the optional AppBar overflow action menu. */ function resolveOverflowMenu( overflow: AppBarOverflowMenu | undefined, interactionPolicy: AppBarProps['interactionPolicy'], testID: string | undefined, ): React.ReactNode { if (overflow === undefined || overflow.actions.length === 0) return null; return ( ( )} /> ); } /*** Composes trailing actions and overflow controls. */ function resolveTrailing(actions: React.ReactNode, overflowMenu: React.ReactNode): React.ReactNode { if (!actions && !overflowMenu) return undefined; return ( {actions} {overflowMenu} ); } /*** Resolves the center content for default and selection modes. */ function resolveCenter({ children, isSelectionMode, resolvedMode, subtitle, title, }: { children: React.ReactNode; isSelectionMode: boolean; resolvedMode: AppBarMode; subtitle: React.ReactNode; title: React.ReactNode; }): React.ReactNode { if (children !== undefined) return children; if (isSelectionMode && resolvedMode.type === 'selection') { return ( {resolveSelectionLabel(resolvedMode)} ); } if (title == null && subtitle == null) return null; return ( {title != null ? ( {title} ) : null} {subtitle != null ? ( {subtitle} ) : null} ); }