Skip to content

Interrupteur - DsfrToggleSwitch

🌟 Introduction

Le DsfrToggleSwitch est un composant Vue versatile, conçu pour permettre à l’utilisateur de faire un choix entre deux états opposés (activé / désactivé).

🏅 La documentation sur les interrupteurs sur le DSFR

La story sur la carte sur le storybook de VueDsfr

🛠️ Props

NomTypeDéfautObligatoireDescription
modelValuebooleanValeur booléenne associée à la case à cocher
disabledbooleanValeur booléenne pour désactiver le toggle
hintstringTexte d'information complémentaire affiché en dessous de l'interrupteur
labelstring''Texte du label associé à l'interrupteur
namestringundefinedAttribut name de l’input
labelLeftbooleanfalse⚠️ prop obsolète (donc désactivée) ♿️
borderBottombooleanfalseAffiche une bordure sous l'interrupteur et le label
inputIdstringuseRandomId('toggle')Identifiant unique pour le toggle. Utilisé pour l'accessibilité.
activeTextstringActivéTexte à afficher sous l'interrupteur lorsqu'il est activé
inactiveTextstringDésactivéTexte à afficher sous l'interrupteur lorsqu'il est désactivé
noTextbooleanfalseDésactive l'affichage de activeText et inactiveText
statusstringundefinedstatut du message.
validMessagestringMessage de validité
errorMessagestringMessage d'erreur

📡Évenements

DsfrToggleSwitch émet l'événement suivant :

NomtypeDescription
update:modelValuebooleanEst émis lorsque la valeur de l'interrupteur change

🧩 Slots

Aucun slot n'est prévu dans ce composant. Le contenu est entièrement basé sur les props.

📝 Exemples

Exemple Basique

vue
<script lang="ts" setup>
import DsfrToggleSwitch from '../DsfrToggleSwitch.vue'
</script>

<template>
  <div
    class="flex flex-col"
  >
    <div>
      <DsfrToggleSwitch label="Label action interrupteur" />
    </div>
    <div>
      <DsfrToggleSwitch
        label="Label action interrupteur"
      />
    </div>
    <div>
      <DsfrToggleSwitch
        label="Vitesse lumière ?"
        border-bottom
        active-text="Vers l'infini et au-delà"
        inactive-text="restons terre à terre"
      />
    </div>
  </div>
</template>

Exemple en erreur

vue
<script lang="ts" setup>
import DsfrToggleSwitch from '../DsfrToggleSwitch.vue'
</script>

<template>
  <div
    class="flex flex-col"
  >
    <div>
      <DsfrToggleSwitch
        label="Label action interrupteur (sans statut)"
        border-bottom
      />
    </div>
    <div>
      <DsfrToggleSwitch
        label="Label action interrupteur (valide)"
        border-bottom
        status="valid"
        valid-message="C'est tout bon !"
      />
    </div>
    <div>
      <DsfrToggleSwitch
        label="Label action interrupteur (en erreur)"
        border-bottom
        status="error"
        error-message="Il y a une erreur"
      />
    </div>
  </div>
</template>

⚙️ Code source du composant

vue
<script lang="ts" setup>
import type { DsfrToggleSwitchProps } from './DsfrToggleSwitch.types'

import { computed } from 'vue'

import { useRandomId } from '../../utils/random-utils'

export type { DsfrToggleSwitchProps }

const props = withDefaults(defineProps<DsfrToggleSwitchProps>(), {
  inputId: () => useRandomId('toggle'),
  hint: '',
  label: '',
  labelLeft: false,
  borderBottom: false,
  activeText: 'Activé',
  inactiveText: 'Désactivé',
  noText: false,
  name: undefined,
  status: undefined,
})

const emit = defineEmits<{
  /** Émis par le composant DsfrToggleSwitch */
  'update:modelValue': [payload: boolean]
}>()

const labelId = computed(() => {
  return `${props.inputId}-label`
})

const message = computed(() => {
  if (props.status === 'valid') {
    return props.validMessage
  }
  if (props.status === 'error') {
    return props.errorMessage
  }
  return undefined
})
</script>

<template>
  <div
    class="fr-toggle"
    :class="{
      [`fr-toggle--${status}`]: status,
      'fr-toggle--border-bottom': borderBottom,
    }"
  >
    <input
      :id="inputId"
      :disabled="disabled"
      :aria-disabled="disabled"
      type="checkbox"
      :checked="modelValue"
      :data-testid="inputId"
      class="fr-toggle__input"
      :aria-describedby="labelId"
      :name="name"
      @input="emit('update:modelValue', ($event.target as HTMLInputElement).checked)"
    >
    <label
      :id="labelId"
      class="fr-toggle__label"
      :for="inputId"
      :data-fr-checked-label="noText ? undefined : activeText"
      :data-fr-unchecked-label="noText ? undefined : inactiveText"
      style="--toggle-status-width: 3.55208125rem;"
    >
      {{ label }}
    </label>
    <p
      v-if="hint"
      :id="`${inputId}-hint-text`"
      class="fr-hint-text"
    >
      {{ hint }}
    </p>
    <div
      v-if="status"
      :id="`${labelId}-messages`"
      class="fr-messages-group"
      aria-live="polite"
    >
      <p
        :id="`${labelId}-message-${status}`"
        class="fr-message"
        :class="{
          'fr-message--error': status === 'error',
          'fr-message--valid': status === 'valid',
        }"
      >
        {{ message }}
      </p>
    </div>
  </div>
</template>
ts
import type { InputHTMLAttributes } from 'vue'

export type DsfrToggleSwitchProps = {
  modelValue?: boolean
  inputId?: string
  hint?: string
  label?: string
  disabled?: boolean
  labelLeft?: boolean
  borderBottom?: boolean
  activeText?: string
  inactiveText?: string
  noText?: boolean
  name?: string
  status?: undefined | 'valid' | 'error'
  validMessage?: string
  errorMessage?: string
}
export type DsfrToggleSwitchGroupProps = {
  id?: string
  legend?: string
  disabled?: boolean
  toggleSwitches?: (DsfrToggleSwitchProps & InputHTMLAttributes)[]
  borders?: boolean
  activeText?: string
  inactiveText?: string
  noText?: boolean
  status?: undefined | 'valid' | 'error'
  validMessage?: string
  errorMessage?: string
}