import { useCallback, useMemo } from "react"; import { Pressable, Text, View, type PressableStateCallbackType } from "react-native"; import { StyleSheet, withUnistyles } from "react-native-unistyles"; import type { LucideIcon } from "lucide-react-native"; import { HEADER_INNER_HEIGHT, HEADER_INNER_HEIGHT_MOBILE } from "@/constants/layout"; import { ICON_SIZE } from "@/styles/theme"; import type { Theme } from "@/styles/theme"; import { Shortcut } from "@/components/ui/shortcut"; import type { ShortcutKey } from "@/utils/format-shortcut"; const foregroundColorMapping = (theme: Theme) => ({ color: theme.colors.foreground }); const foregroundMutedColorMapping = (theme: Theme) => ({ color: theme.colors.foregroundMuted }); type SidebarHeaderRowVariant = "header" | "compact"; interface SidebarHeaderRowProps { icon: LucideIcon; label: string; onPress: () => void; isActive?: boolean; testID?: string; nativeID?: string; accessibilityLabel?: string; /** * "header" (default): a sidebar-height row with its own bottom separator — * the lone header at the top of a sidebar (settings "Back to workspace"). * "compact": a workspace-row-height row with no separator, for entries that * sit in a header group whose wrapper owns the single divider. */ variant?: SidebarHeaderRowVariant; shortcutKeys?: ShortcutKey[][] | null; } export function SidebarHeaderRow({ icon: Icon, label, onPress, isActive = false, testID, nativeID, accessibilityLabel, variant = "header", shortcutKeys = null, }: SidebarHeaderRowProps) { const ThemedIcon = useMemo(() => withUnistyles(Icon), [Icon]); const containerStyle = useMemo( () => (variant === "compact" ? styles.containerCompact : styles.container), [variant], ); const buttonStyle = useCallback( ({ hovered }: PressableStateCallbackType & { hovered?: boolean }) => [ styles.button, variant === "compact" && styles.buttonCompact, (Boolean(hovered) || isActive) && styles.buttonHovered, ], [isActive, variant], ); const renderChildren = useCallback( (state: PressableStateCallbackType & { hovered?: boolean }) => { const isHighlighted = Boolean(state.hovered) || isActive; return ( <> {shortcutKeys && Boolean(state.hovered) ? ( ) : null} ); }, [ThemedIcon, isActive, label, shortcutKeys], ); return ( {renderChildren} ); } function SidebarHeaderRowLabel({ label, isHighlighted, }: { label: string; isHighlighted: boolean; }) { const labelStyle = useMemo( () => [styles.label, isHighlighted && styles.labelHighlighted], [isHighlighted], ); return {label}; } const styles = StyleSheet.create((theme) => ({ container: { height: { xs: HEADER_INNER_HEIGHT_MOBILE, md: HEADER_INNER_HEIGHT, }, paddingHorizontal: theme.spacing[2], justifyContent: "center", borderBottomWidth: 1, borderBottomColor: theme.colors.border, userSelect: "none", }, containerCompact: { paddingHorizontal: theme.spacing[2], justifyContent: "center", userSelect: "none", }, button: { flexDirection: "row", alignItems: "center", gap: theme.spacing[2], // Match the sidebar workspace-row shape (height, padding, radius) so the // compact header entries sit tight against the workspace list below. minHeight: 36, paddingVertical: theme.spacing[2], paddingHorizontal: theme.spacing[3], borderRadius: theme.borderRadius.lg, }, // Compact header entries (New workspace / History) sit tighter than the // workspace-row shape the base button mirrors. buttonCompact: { minHeight: 32, paddingVertical: theme.spacing[1.5], // Match the project rows' inner padding so the icons align on one vertical // edge with the workspace list below (base button uses a wider spacing[3]). paddingHorizontal: theme.spacing[2], }, buttonHovered: { backgroundColor: theme.colors.surfaceSidebarHover, }, label: { fontSize: theme.fontSize.sm, fontWeight: theme.fontWeight.normal, color: theme.colors.foregroundMuted, }, labelHighlighted: { color: theme.colors.foreground, }, shortcut: { marginLeft: "auto", }, }));