Pular para o conteúdo

Cascader

Caixa de seleção em cascata para dados hierárquicos como localizações ou categorias.

import { Cascader } from 'asterui'

Básico

Seleção hierárquica simples.

import { Cascader } from 'asterui'

function App() {
  const options = [
    {
      value: 'usa',
      label: 'United States',
      children: [
        {
          value: 'ca',
          label: 'California',
          children: [
            { value: 'sf', label: 'San Francisco' },
            { value: 'la', label: 'Los Angeles' },
          ],
        },
        {
          value: 'ny',
          label: 'New York',
          children: [
            { value: 'nyc', label: 'New York City' },
            { value: 'buf', label: 'Buffalo' },
          ],
        },
      ],
    },
  ]
  
  return (
      <Cascader options={options} placeholder="Select location" />
    )
}

export default App

Expandir ao Passar Mouse

Expandir submenus ao passar o mouse.

import { Cascader } from 'asterui'

function App() {
  const options = [
    {
      value: 'electronics',
      label: 'Electronics',
      children: [
        {
          value: 'phones',
          label: 'Phones',
          children: [
            { value: 'iphone', label: 'iPhone' },
            { value: 'android', label: 'Android' },
          ],
        },
        {
          value: 'computers',
          label: 'Computers',
          children: [
            { value: 'laptop', label: 'Laptop' },
            { value: 'desktop', label: 'Desktop' },
          ],
        },
      ],
    },
  ]
  
  return (
      <Cascader options={options} expandTrigger="hover" placeholder="Select category" />
    )
}

export default App

Tamanhos

Diferentes tamanhos de cascader.

import { Cascader } from 'asterui'

function App() {
  const options = [
    { value: 'opt1', label: 'Option 1' },
    { value: 'opt2', label: 'Option 2' },
  ]
  
  return (
      <div className="flex flex-col gap-2">
        <Cascader size="xs" options={options} placeholder="Extra small" />
        <Cascader size="sm" options={options} placeholder="Small" />
        <Cascader size="md" options={options} placeholder="Medium" />
        <Cascader size="lg" options={options} placeholder="Large" />
      </div>
    )
}

export default App

Desabilitado

Estado de cascader desabilitado.

import { Cascader } from 'asterui'

function App() {
  const options = [
    { value: 'opt1', label: 'Option 1' },
    { value: 'opt2', label: 'Option 2' },
  ]
  
  return (
      <Cascader options={options} disabled value={['opt1']} />
    )
}

export default App

Pesquisa

Habilitar funcionalidade de pesquisa/filtro.

import { Cascader } from 'asterui'

function App() {
  const options = [
    {
      value: 'usa',
      label: 'United States',
      children: [
        {
          value: 'ca',
          label: 'California',
          children: [
            { value: 'sf', label: 'San Francisco' },
            { value: 'la', label: 'Los Angeles' },
          ],
        },
      ],
    },
  ]
  
  return (
      <Cascader options={options} showSearch placeholder="Search locations" />
    )
}

export default App

Mudar ao Selecionar

Permitir seleção de qualquer nível, não apenas nós folha.

Selected: None

import { Cascader } from 'asterui'
import { useState } from 'react'

function App() {
  const [value, setValue] = useState<string[]>([])
  const options = [
    {
      value: 'usa',
      label: 'United States',
      children: [
        { value: 'ca', label: 'California' },
        { value: 'ny', label: 'New York' },
      ],
    },
  ]
  
  return (
      <div>
        <Cascader
          options={options}
          changeOnSelect
          value={value}
          onChange={setValue}
          placeholder="Select any level"
        />
        <p className="mt-2 text-sm">Selected: {value.join(' / ') || 'None'}</p>
      </div>
    )
}

export default App

Carregamento Assíncrono

Carregar opções dinamicamente ao expandir.

import { Cascader } from 'asterui'
import { useState } from 'react'

function App() {
  const [options, setOptions] = useState<CascaderOption[]>([
    { value: 'region1', label: 'Region 1' },
    { value: 'region2', label: 'Region 2' },
  ])
  
  const loadData = async (selectedOptions: CascaderOption[]) => {
    const targetOption = selectedOptions[selectedOptions.length - 1]
    await new Promise(resolve => setTimeout(resolve, 1000))
  
    setOptions(prev => {
      const update = (opts: CascaderOption[]): CascaderOption[] =>
        opts.map(opt =>
          opt.value === targetOption.value
            ? {
                ...opt,
                children: [
                  { value: `${opt.value}-1`, label: 'Child 1', isLeaf: true },
                  { value: `${opt.value}-2`, label: 'Child 2', isLeaf: true },
                ],
              }
            : { ...opt, children: opt.children ? update(opt.children) : undefined }
        )
      return update(prev)
    })
  }
  
  return (
      <Cascader options={options} loadData={loadData} placeholder="Load on expand" />
    )
}

export default App

Status de Validação

Mostrar estado de validação de erro ou aviso.

import { Cascader } from 'asterui'

function App() {
  const options = [
    { value: 'opt1', label: 'Option 1' },
    { value: 'opt2', label: 'Option 2' },
  ]
  
  return (
      <div className="flex flex-col gap-2">
        <Cascader options={options} status="error" placeholder="Error state" />
        <Cascader options={options} status="warning" placeholder="Warning state" />
      </div>
    )
}

export default App

Seleção Múltipla

Selecionar múltiplos valores folha.

import { Cascader } from 'asterui'

function App() {
  const options = [
    {
      value: 'fruits',
      label: 'Fruits',
      children: [
        { value: 'apple', label: 'Apple' },
        { value: 'banana', label: 'Banana' },
      ],
    },
    {
      value: 'vegetables',
      label: 'Vegetables',
      children: [
        { value: 'carrot', label: 'Carrot' },
        { value: 'lettuce', label: 'Lettuce' },
      ],
    },
  ]
  
  return (
      <Cascader options={options} multiple placeholder="Select multiple" />
    )
}

export default App
PropriedadeDescriçãoTipoPadrão
optionsDados de opções hierárquicasCascaderOption[][]
valueCaminho de valor selecionado controladostring[]-
defaultValueValor selecionado padrão (não controlado)string[][]
onChangeCallback quando a seleção muda(value: string[], selectedOptions: CascaderOption[]) => void-
placeholderTexto placeholder do inputstring'Please select'
disabledDesabilitar o cascaderbooleanfalse
allowClearMostrar botão de limparbooleantrue
expandTriggerComo expandir submenus'click' | 'hover''click'
changeOnSelectPermitir seleção de qualquer nível, não apenas folhasbooleanfalse
displayRenderFunção de renderização de exibição personalizada(labels: ReactNode[], selectedOptions: CascaderOption[]) => ReactNode-
sizeTamanho do input'xs' | 'sm' | 'md' | 'lg''md'
colorCor do anel de foco'primary' | 'secondary' | 'accent' | 'info' | 'success' | 'warning' | 'error'-
statusStatus de validação'error' | 'warning'-
showSearchHabilitar funcionalidade de pesquisaboolean | { filter?, render?, matchInputWidth? }false
notFoundContentConteúdo quando não há resultados de pesquisaReactNode'No results found'
loadDataFunção de carregamento de dados assíncrono(selectedOptions: CascaderOption[]) => Promise<void>-
fieldNamesNomes de campo personalizados para mapeamento de dados{ label?: string; value?: string; children?: string }-
openEstado aberto controlado do dropdownboolean-
onDropdownVisibleChangeCallback quando a visibilidade do dropdown muda(open: boolean) => void-
popupClassNameNome de classe para dropdownstring-
dropdownRenderRenderização de wrapper de dropdown personalizada(menu: ReactNode) => ReactNode-
multipleHabilitar modo de seleção múltiplabooleanfalse
maxTagCountMáximo de tags para mostrar no modo múltiplonumber | 'responsive'-
classNameClasses CSS adicionaisstring-
PropriedadeDescriçãoTipoPadrão
valueIdentificador únicostring-
labelRótulo de exibiçãoReactNode-
childrenOpções filhasCascaderOption[]-
disabledDesabilitar esta opçãobooleanfalse
isLeafForçar como nó folha (sem expandir, sem carregamento assíncrono)boolean-

O componente Cascader segue o padrão de combobox WAI-ARIA:

  • Usa role="combobox" no gatilho com aria-expanded, aria-haspopup
  • aria-activedescendant rastreia a opção focada para leitores de tela
  • aria-selected indica o estado selecionado nas opções
  • aria-disabled indica o estado desabilitado
  • Botão de limpar tem aria-label="Clear selection"
TeclaAção
BaixoAbrir dropdown ou mover para próxima opção
CimaMover para opção anterior
DireitaMover para próxima coluna (expandir)
EsquerdaMover para coluna anterior
EnterSelecionar opção focada ou abrir dropdown
EspaçoAlternar dropdown (quando não está pesquisando)
EscapeFechar dropdown
HomeMover para primeira opção
EndMover para última opção

O componente inclui atributos de dados para testes:

  • data-testid="cascader" no contêiner raiz
  • data-testid="cascader-dropdown" no dropdown
  • data-testid="cascader-option-{value}" em cada opção
  • data-testid="cascader-clear" no botão de limpar
  • data-state="open|closed" na raiz indica o estado do dropdown
  • data-state="selected|hovered" nas opções indica o estado
  • data-value="{value}" nas opções fornece o valor