Aller au contenu

Cascader

Boîte de sélection en cascade pour les données hiérarchiques comme les emplacements ou les catégories.

import { Cascader } from 'asterui'

Basique

Sélection hiérarchique simple.

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

Expansion au survol

Développer les sous-menus au survol.

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

Tailles

Différentes tailles 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

Désactivé

État désactivé du cascader.

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

Recherche

Activer la fonctionnalité de recherche/filtre.

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

Changement à la sélection

Autoriser la sélection de n'importe quel niveau, pas seulement les nœuds feuilles.

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

Chargement asynchrone

Charger les options dynamiquement lors de l'expansion.

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

État de validation

Afficher l'état de validation d'erreur ou d'avertissement.

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

Sélection multiple

Sélectionner plusieurs valeurs feuilles.

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
PropriétéDescriptionTypeDéfaut
optionsDonnées d’options hiérarchiquesCascaderOption[][]
valueChemin de valeur sélectionnée contrôléestring[]-
defaultValueValeur sélectionnée par défaut (non contrôlée)string[][]
onChangeCallback lors du changement de sélection(value: string[], selectedOptions: CascaderOption[]) => void-
placeholderTexte de l’espace réservé d’entréestring'Please select'
disabledDésactiver le cascaderbooleanfalse
allowClearAfficher le bouton d’effacementbooleantrue
expandTriggerComment développer les sous-menus'click' | 'hover''click'
changeOnSelectAutoriser la sélection de n’importe quel niveau, pas seulement les feuillesbooleanfalse
displayRenderFonction de rendu d’affichage personnalisée(labels: ReactNode[], selectedOptions: CascaderOption[]) => ReactNode-
sizeTaille d’entrée'xs' | 'sm' | 'md' | 'lg''md'
colorCouleur de l’anneau de focus'primary' | 'secondary' | 'accent' | 'info' | 'success' | 'warning' | 'error'-
statusÉtat de validation'error' | 'warning'-
showSearchActiver la fonctionnalité de rechercheboolean | { filter?, render?, matchInputWidth? }false
notFoundContentContenu lorsqu’aucun résultat de rechercheReactNode'No results found'
loadDataFonction de chargement de données asynchrone(selectedOptions: CascaderOption[]) => Promise<void>-
fieldNamesNoms de champs personnalisés pour le mappage de données{ label?: string; value?: string; children?: string }-
openÉtat d’ouverture contrôlée du menu déroulantboolean-
onDropdownVisibleChangeCallback lors du changement de visibilité du menu déroulant(open: boolean) => void-
popupClassNameNom de classe pour le menu déroulantstring-
dropdownRenderRendu d’enveloppe de menu déroulant personnalisé(menu: ReactNode) => ReactNode-
multipleActiver le mode de sélection multiplebooleanfalse
maxTagCountNombre maximum d’étiquettes à afficher en mode multiplenumber | 'responsive'-
classNameClasses CSS supplémentairesstring-
PropriétéDescriptionTypeDéfaut
valueIdentifiant uniquestring-
labelLibellé d’affichageReactNode-
childrenOptions enfantsCascaderOption[]-
disabledDésactiver cette optionbooleanfalse
isLeafForcer comme nœud feuille (pas d’expansion, pas de chargement asynchrone)boolean-

Le composant Cascader suit le modèle de combobox WAI-ARIA :

  • Utilise role="combobox" sur le déclencheur avec aria-expanded, aria-haspopup
  • aria-activedescendant suit l’option focalisée pour les lecteurs d’écran
  • aria-selected indique l’état sélectionné sur les options
  • aria-disabled indique l’état désactivé
  • Le bouton d’effacement a aria-label="Clear selection"
ToucheAction
BasOuvrir le menu déroulant ou passer à l’option suivante
HautPasser à l’option précédente
DroitePasser à la colonne suivante (développer)
GauchePasser à la colonne précédente
EntréeSélectionner l’option focalisée ou ouvrir le menu déroulant
EspaceBasculer le menu déroulant (lorsque non en recherche)
ÉchapFermer le menu déroulant
DébutPasser à la première option
FinPasser à la dernière option

Le composant inclut des attributs de données pour les tests :

  • data-testid="cascader" sur le conteneur racine
  • data-testid="cascader-dropdown" sur le menu déroulant
  • data-testid="cascader-option-{value}" sur chaque option
  • data-testid="cascader-clear" sur le bouton d’effacement
  • data-state="open|closed" sur la racine indique l’état du menu déroulant
  • data-state="selected|hovered" sur les options indique l’état
  • data-value="{value}" sur les options fournit la valeur