import { useCallback, useMemo, type ReactElement } from "react"; import { View, Text, ActivityIndicator, Pressable, type PressableStateCallbackType, } from "react-native"; import { StyleSheet, useUnistyles } from "react-native-unistyles"; import { ChevronDown, Info, MoreVertical } from "lucide-react-native"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { Shortcut } from "@/components/ui/shortcut"; import { useShortcutKeys } from "@/hooks/use-shortcut-keys"; import { useToast } from "@/contexts/toast-context"; import type { GitAction, GitActions } from "@/components/git-actions-policy"; interface GitActionsSplitButtonProps { gitActions: GitActions; hideLabels?: boolean; } interface GitActionMenuItemProps { action: GitAction; onSelect: (action: GitAction) => void; trailing?: ReactElement | null; needsSeparator?: boolean; showSeparator?: boolean; closeOnSelect?: boolean; } function GitActionMenuItem({ action, onSelect, trailing, needsSeparator, showSeparator, closeOnSelect, }: GitActionMenuItemProps) { const handleSelect = useCallback(() => onSelect(action), [onSelect, action]); return ( {needsSeparator && showSeparator ? : null} {action.label} ); } export function GitActionsSplitButton({ gitActions, hideLabels }: GitActionsSplitButtonProps) { const { theme } = useUnistyles(); const toast = useToast(); const archiveShortcutKeys = useShortcutKeys("archive-worktree"); const getActionDisplayLabel = useCallback((action: GitAction): string => { if (action.status === "pending") return action.pendingLabel; if (action.status === "success") return action.successLabel; return action.label; }, []); const handleActionSelect = useCallback( (action: GitAction) => { if (action.unavailableMessage) { toast.show(action.unavailableMessage, { durationMs: 3200, icon: , }); return; } action.handler(); }, [theme.colors.foreground, toast], ); const overflowMenuButtonStyle = useMemo(() => [styles.iconButton, styles.overflowMenuButton], []); const primaryDisabled = gitActions.primary?.disabled; const primaryPressableStyle = useCallback( ({ hovered, pressed }: PressableStateCallbackType & { hovered?: boolean }) => [ styles.splitButtonPrimary, (Boolean(hovered) || pressed) && styles.splitButtonPrimaryHovered, primaryDisabled && styles.splitButtonPrimaryDisabled, ], [primaryDisabled], ); const caretTriggerStyle = useCallback( ({ hovered, pressed, open }: { hovered: boolean; pressed: boolean; open: boolean }) => [ styles.splitButtonCaret, (hovered || pressed || open) && styles.splitButtonCaretHovered, ], [], ); return ( {gitActions.primary ? ( {gitActions.primary.status === "pending" ? ( ) : ( {gitActions.primary.icon} {!hideLabels && ( {getActionDisplayLabel(gitActions.primary)} )} )} {gitActions.secondary.length > 0 ? ( {gitActions.secondary.map((action, index) => ( ) : undefined } needsSeparator={ action.id === "merge-from-base" || action.id === "archive-worktree" } showSeparator={index > 0} closeOnSelect={ action.status === "idle" && action.id === "pr" && action.label === "View PR" } /> ))} ) : null} ) : null} {gitActions.menu.length > 0 ? ( {gitActions.menu.map((action) => ( ))} ) : null} ); } const styles = StyleSheet.create((theme) => ({ row: { flexDirection: "row", alignItems: "center", gap: theme.spacing[1], flexShrink: 0, }, splitButton: { flexDirection: "row", alignItems: "stretch", borderRadius: theme.borderRadius.md, borderWidth: theme.borderWidth[1], borderColor: theme.colors.borderAccent, overflow: "hidden", }, splitButtonPrimary: { paddingHorizontal: theme.spacing[3], paddingVertical: theme.spacing[1], justifyContent: "center", position: "relative", }, splitButtonPrimaryHovered: { backgroundColor: theme.colors.surface2, }, splitButtonPrimaryDisabled: { opacity: 0.6, }, splitButtonText: { fontSize: theme.fontSize.sm, lineHeight: theme.fontSize.sm * 1.5, color: theme.colors.foreground, fontWeight: theme.fontWeight.normal, }, splitButtonContent: { flexDirection: "row", alignItems: "center", justifyContent: "center", gap: theme.spacing[2], }, splitButtonSpinnerOnly: { transform: [{ scale: 0.8 }], }, splitButtonCaret: { width: 28, alignItems: "center", justifyContent: "center", borderLeftWidth: theme.borderWidth[1], borderLeftColor: theme.colors.borderAccent, }, splitButtonCaretHovered: { backgroundColor: theme.colors.surface2, }, iconButton: { width: 32, height: 32, alignItems: "center", justifyContent: "center", borderRadius: theme.borderRadius.md, }, overflowMenuButton: { marginRight: -theme.spacing[2], }, }));