# TabbedChipsAlpha

A chip component commonly used in filter context to refine a date source

## Import

```tsx
import { TabbedChips } from '@coinbase/cds-mobile/alpha/tabbed-chips/TabbedChips'
```

## Examples

### Basic usage

```tsx
function ExampleDefault() {
  const tabs = [
    { id: 'all', label: 'All' },
    { id: 'swap', label: 'Swap' },
    { id: 'collect', label: 'Collect' },
    { id: 'bridge', label: 'Bridge' },
  ];
  const [activeTab, setActiveTab] = useState(tabs[0]);
  return <TabbedChips activeTab={activeTab} onChange={setActiveTab} tabs={tabs} />;
}
```

### Compact

```tsx
function ExampleCompactNoStart() {
  const tabs = [
    { id: 'all', label: 'All' },
    { id: 'swap', label: 'Swap' },
    { id: 'collect', label: 'Collect' },
    { id: 'bridge', label: 'Bridge' },
  ];
  const [activeTab, setActiveTab] = useState(tabs[0]);
  return <TabbedChips activeTab={activeTab} compact onChange={setActiveTab} tabs={tabs} />;
}
```

### Many tabs (scrollable)

:::tip Scrolling behavior
The list becomes horizontally scrollable when content overflows; an `OverflowGradient` appears on the edge to hint more content.
:::

```tsx
function ExampleManyTabs() {
  const tabs = Array.from({ length: 12 }).map((_, i) => ({
    id: `tab_${i + 1}`,
    label: `Tab ${i + 1}`,
  }));
  const [activeTab, setActiveTab] = useState(tabs[0]);
  return <TabbedChips activeTab={activeTab} onChange={setActiveTab} tabs={tabs} />;
}
```

### With autoScrollOffset

:::tip Auto-scroll offset
The `autoScrollOffset` prop controls the X position offset when auto-scrolling to the active tab. This prevents the active tab from being covered by the overflow gradient on the left side. Try clicking tabs near the edges to see the difference.
:::

```tsx
function ExampleAutoScrollOffset() {
  const tabs = Array.from({ length: 25 }).map((_, i) => ({
    id: `tab_${i + 1}`,
    label: `Tab ${i + 1}`,
  }));
  const [activeTab, setActiveTab] = useState(tabs[0]);
  return (
    <VStack gap={2}>
      <Text>Default offset (30px)</Text>
      <TabbedChips activeTab={activeTab} onChange={setActiveTab} tabs={tabs} />
      <Text>Custom offset (60px)</Text>
      <TabbedChips
        activeTab={activeTab}
        onChange={setActiveTab}
        tabs={tabs}
        autoScrollOffset={60}
      />
    </VStack>
  );
}
```

### Long text tabs

```tsx
function ExampleLongText() {
  const tabs = [
    { id: 'a', label: 'Very long tab label that can wrap on small widths' },
    { id: 'b', label: 'Another extra long label to test overflow' },
    { id: 'c', label: 'Short' },
  ];
  const [activeTab, setActiveTab] = useState(tabs[0]);
  return <TabbedChips activeTab={activeTab} onChange={setActiveTab} tabs={tabs} />;
}
```

### Disabled tab

```tsx
function ExampleDisabled() {
  const tabs = [
    { id: 'first', label: 'First' },
    { id: 'second', label: 'Second', disabled: true },
    { id: 'third', label: 'Third' },
  ];
  const [activeTab, setActiveTab] = useState(tabs[0]);
  return <TabbedChips activeTab={activeTab} onChange={setActiveTab} tabs={tabs} />;
}
```

### With start media

:::tip Media sizing
For start media, use circular images sized 24×24 for regular chips and 16×16 for compact chips.
:::

```tsx
function ExampleWithStart() {
  const icon = { height: 24, width: 24, shape: 'circle', source: assets.eth.imageUrl };
  const compactIcon = { height: 16, width: 16, shape: 'circle', source: assets.eth.imageUrl };
  const tabs = [
    { id: 'all', label: 'All', start: <RemoteImage {...icon} /> },
    { id: 'swap', label: 'Swap', start: <RemoteImage {...icon} /> },
    { id: 'collect', label: 'Collect', start: <RemoteImage {...icon} /> },
    { id: 'bridge', label: 'Bridge', start: <RemoteImage {...icon} /> },
  ];
  const compactTabs = tabs.map((tab) => ({ ...tab, start: <RemoteImage {...compactIcon} /> }));
  const [activeTab, setActiveTab] = useState(tabs[0]);
  return (
    <VStack gap={2}>
      <TabbedChips activeTab={activeTab} onChange={setActiveTab} tabs={tabs} />
      <TabbedChips compact activeTab={activeTab} onChange={setActiveTab} tabs={compactTabs} />
    </VStack>
  );
}
```

### Custom TabComponent

:::tip Custom Tab behavior
When providing a custom TabComponent, use `useTabsContext()` and call `updateActiveTab(id)` to update selection state. Reflect the active state (e.g., end icon state) based on `activeTab?.id === id`.
:::

```tsx
function CustomTab({ id, label, ...props }: TabbedChipProps) {
  const { activeTab, updateActiveTab } = useTabsContext();
  const isActive = activeTab?.id === id;
  return (
    <MediaChip
      onPress={() => updateActiveTab(id)}
      end={<Icon size="s" active={isActive} name="star" />}
      {...props}
    >
      {label}
    </MediaChip>
  );
}

const tabs = [
  { id: 'all', label: 'All' },
  { id: 'swap', label: 'Swap' },
  { id: 'collect', label: 'Collect' },
];

function Example() {
  const [activeTab, setActiveTab] = useState(tabs[0]);
  return (
    <TabbedChips
      activeTab={activeTab}
      onChange={setActiveTab}
      tabs={tabs}
      TabComponent={CustomTab}
    />
  );
}

render(<Example />);
```

## Props

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `activeTab` | `TabValue<T> \| null` | Yes | `-` | React state for the currently active tab. Setting it to null results in no active tab. |
| `onChange` | `(activeTab: TabValue<T> \| null) => void` | Yes | `-` | Callback that is fired when the active tab changes. Use this callback to update the activeTab state. |
| `tabs` | `TabbedChipProps<T>[]` | Yes | `-` | - |
| `TabComponent` | `FC<TabbedChipProps<T>>` | No | `-` | - |
| `TabsActiveIndicatorComponent` | `TabsActiveIndicatorComponent` | No | `-` | - |
| `autoScrollOffset` | `number` | No | `30` | X position offset when auto-scrolling to active tab (to avoid active tab being covered by the overflow gradient on the left side, default: 30px) |
| `compact` | `boolean` | No | `false` | Turn on to use a compact Chip component for each tab. |
| `disabled` | `boolean` | No | `-` | Disable interactions on all the tabs. |
| `gap` | `0 \| 1 \| 2 \| 0.25 \| 0.5 \| 0.75 \| 1.5 \| 3 \| 4 \| 5 \| 6 \| 7 \| 8 \| 9 \| 10` | No | `1` | The spacing between Tabs |
| `ref` | `null \| (instance: View \| null) => void \| MutableRefObject<View \| null>` | No | `-` | - |
| `styles` | `{ root?: StyleProp<ViewStyle>; tabs?: StyleProp<ViewStyle>; }` | No | `-` | - |
| `testID` | `string` | No | `-` | Used to locate this element in unit and end-to-end tests. Under the hood, testID translates to data-testid on Web. On Mobile, testID stays the same - testID |
| `width` | `string \| number` | No | `-` | The width of the scroll container, defaults to 100% of the parent container If the tabs are wider than the width of the container, paddles will be shown to scroll the tabs. |


