File size: 3,438 Bytes
52efc7b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
/**
 * @license
 * Copyright 2026 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */

import React from 'react';
import { Text, Box } from 'ink';
import { theme } from '../../semantic-colors.js';

/**
 * Represents a single tab in the TabHeader.
 */
export interface Tab {
  /** Unique identifier for this tab */
  key: string;
  /** Header text displayed in the tab indicator */
  header: string;
  /** Optional custom status icon for this tab */
  statusIcon?: string;
  /** Whether this is a special tab (like "Review") - uses different default icon */
  isSpecial?: boolean;
}

/**
 * Props for the TabHeader component.
 */
export interface TabHeaderProps {
  /** Array of tab definitions */
  tabs: Tab[];
  /** Currently active tab index */
  currentIndex: number;
  /** Set of indices for tabs that show a completion indicator */
  completedIndices?: Set<number>;
  /** Show navigation arrow hints on sides (default: true) */
  showArrows?: boolean;
  /** Show status icons (checkmark/box) before tab headers (default: true) */
  showStatusIcons?: boolean;
  /**
   * Custom status icon renderer. Return undefined to use default icons.
   * Default icons: 'βœ“' for completed, 'β–‘' for incomplete, '≑' for special tabs
   */
  renderStatusIcon?: (
    tab: Tab,
    index: number,
    isCompleted: boolean,
  ) => string | undefined;
}

/**
 * A header component that displays tab indicators for multi-tab interfaces.
 *
 * Renders in the format: `← Tab1 β”‚ Tab2 β”‚ Tab3 β†’`
 *
 * Features:
 * - Shows completion status (βœ“ or β–‘) per tab
 * - Highlights current tab with accent color
 * - Supports special tabs (like "Review") with different icons
 * - Customizable status icons
 */
export function TabHeader({
  tabs,
  currentIndex,
  completedIndices = new Set(),
  showArrows = true,
  showStatusIcons = true,
  renderStatusIcon,
}: TabHeaderProps): React.JSX.Element | null {
  if (tabs.length <= 1) return null;

  const getStatusIcon = (tab: Tab, index: number): string => {
    const isCompleted = completedIndices.has(index);

    // Try custom renderer first
    if (renderStatusIcon) {
      const customIcon = renderStatusIcon(tab, index, isCompleted);
      if (customIcon !== undefined) return customIcon;
    }

    // Use tab's own icon if provided
    if (tab.statusIcon) return tab.statusIcon;

    // Default icons
    if (tab.isSpecial) return '≑';
    return isCompleted ? 'βœ“' : 'β–‘';
  };

  return (
    <Box flexDirection="row" marginBottom={1} aria-role="tablist">
      {showArrows && <Text color={theme.text.secondary}>{'← '}</Text>}
      {tabs.map((tab, i) => (
        <React.Fragment key={tab.key}>
          {i > 0 && <Text color={theme.text.secondary}>{' β”‚ '}</Text>}
          {showStatusIcons && (
            <Text color={theme.text.secondary}>{getStatusIcon(tab, i)} </Text>
          )}
          <Box maxWidth={i !== currentIndex ? 16 : 100}>
            <Text
              color={
                i === currentIndex ? theme.status.success : theme.text.secondary
              }
              bold={i === currentIndex}
              underline={i === currentIndex}
              aria-current={i === currentIndex ? 'step' : undefined}
              wrap="truncate"
            >
              {tab.header}
            </Text>
          </Box>
        </React.Fragment>
      ))}
      {showArrows && <Text color={theme.text.secondary}>{' β†’'}</Text>}
    </Box>
  );
}