import { Controls, Canvas, Meta, Source } from '@storybook/addon-docs/blocks';
import * as SyTable from './SyTable.stories';
import * as RowEditing from './RowEditing.stories';
import * as BulkActions from './BulkActions.stories';
import '@/stories/styles/shared.css';

<Meta of={SyTable} />

<div className="header">
  <h1>SyTable</h1>
  <p>Le composant `SyTable` est utilisé pour afficher des données tabulaires côté client. Il s'appuie sur le composant `VDataTable` de Vuetify et offre des fonctionnalités supplémentaires comme le stockage local des options de tableau et des améliorations d'accessibilité.</p>
</div>

{/* func-version-start */}
<p className="func-version-badge">Dernière mise à jour fonctionnelle : V1.1.4 - 25/08/2026</p>
{/* func-version-end */}
{/* a11y-version-start */}
<p className="a11y-version-badge">Dernière mise à jour accessibilité : V1.1.3 - 22/07/2026</p>
{/* a11y-version-end */}


<Canvas story={{height: '550px'}} of={SyTable.Default} />

## API

<Controls of={SyTable.Default} />

## Fonctionnalités

### Stockage local des options

Le composant `SyTable` enregistre automatiquement les options du tableau (tri, pagination, etc.) dans le localStorage du navigateur. Ces options sont restaurées lorsque l'utilisateur revient sur la page.

Pour gérer individuellement le stockage des options pour différents tableaux, utilisez la prop `suffix`.

### Tri des colonnes

Le composant permet de trier les données par colonne en cliquant sur l'en-tête de la colonne.

Le multi-tri est également supporté. Vous pouvez activer cette fonctionnalité en utilisant la prop `multi-sort`.

### Filtres des colonnes

Le composant permet d'appliquer des filtres sur les colonnes. Vous pouvez définir des filtres personnalisés pour chaque colonne en utilisant la prop `show-filters`. Voir d'autres exemples de filtres dans les stories.

### Resize des colonnes

Le composant peut permettre de redimensionner les colonnes en utilisant la prop `resizable-columns`. Vous pouvez activer ou désactiver cette fonctionnalité selon vos besoins.

### Réorganisation des colonnes

Le composant permet de cacher ou réorganiser l'ordre des colonnes en utilisant la prop `enable-column-controls`. Vous pouvez activer ou désactiver cette fonctionnalité selon vos besoins.

### Selection des lignes

Le composant permet de sélectionner des lignes individuellement ou en masse. Vous pouvez activer la sélection en utilisant la prop `show-select`.

Par défaut, la clé utilisée pour identifier chaque ligne lors de la sélection est `id`. Si vos éléments ne possèdent pas de propriété `id`, la valeur sélectionnée correspondra à l'objet complet de la ligne.

Vous pouvez personnaliser cette clé avec la prop `selection-key` pour indiquer quel champ utiliser (ex: `userId`).

Exemple d'utilisation :
<Source dark code={`
<SyTable
  v-model="selected"
  :items="items"
  :headers="headers"
  show-select
  selection-key="userId"
/>
`}/>

### Click des lignes

La prop `clickableRow` active le clic sur toute la ligne du tableau. Quand cette prop vaut `true`, un clic sur une ligne émet l'événement `row-click` avec l'item correspondant.

Les lignes deviennent également focusables au clavier afin de pouvoir être activées avec <kbd>Entrée</kbd> ou <kbd>Espace</kbd>.

Les éléments interactifs déjà présents dans la ligne, comme les cases à cocher, liens ou boutons, conservent leur comportement propre et ne déclenchent pas `row-click`.

L'événement `row-click` est émis lorsqu'une ligne est activée alors que `clickableRow` est à `true`.

- Payload : l'objet de la ligne cliquée
- Cas pris en charge : clic souris sur la ligne, activation clavier via <kbd>Entrée</kbd> ou <kbd>Espace</kbd>
- Cas exclus : interaction avec un élément interactif imbriqué dans la ligne

Exemple :
<Source dark code={`
<SyTable
  :items="items"
  :headers="headers"
  clickable-row
  @row-click="handleRowClick"
/>
`}/>


### Édition des lignes

La prop `editable` active l'édition inline. Chaque colonne dont le header porte `editable: true` devient un champ en mode édition (par défaut un `SyTextField`). Le composant **ne mute jamais `items`** : il travaille sur un brouillon et émet `save`, `cancel` et `delete` — c'est à l'application parente de persister les changements.

Les boutons d'action se placent dans le slot `#item.actions`, qui expose les helpers `{ item, isEditing, edit, save, cancel, remove }`. Prévoyez une colonne `actions` dans les headers.

<Source dark code={`
const headers = [
  { title: 'Nom', key: 'lastname', editable: true },
  { title: 'Prénom', key: 'firstname', editable: true },
  { title: 'Actions', key: 'actions', sortable: false },
]
`}/>

<Source dark code={`
<SyTable
  editable
  selection-key="id"
  :headers="headers"
  :items="items"
  @save="onSave"
  @delete="onDelete"
>
  <template #item.actions="{ isEditing, edit, save, cancel, remove }">
    <template v-if="!isEditing">
      <SyIconButton :icon="mdiPencil" label="Éditer" @click-icon-button="edit" />
      <SyIconButton :icon="mdiDelete" label="Supprimer" @click-icon-button="remove" />
    </template>
    <template v-else>
      <SyIconButton :icon="mdiCheck" label="Valider" @click-icon-button="save" />
      <SyIconButton :icon="mdiClose" label="Annuler" @click-icon-button="cancel" />
    </template>
  </template>
</SyTable>
`}/>

L'application reste maîtresse de la persistance, par exemple :

<Source dark code={`
function onSave(updated) {
  const index = items.value.findIndex(i => i.id === updated.id)
  if (index !== -1) items.value[index] = { ...items.value[index], ...updated }
}

function onDelete(item) {
  items.value = items.value.filter(i => i.id !== item.id)
}
`}/>

L'éditeur d'une cellule peut être personnalisé via le slot `#edit.<columnKey>`. Il reçoit `value` (le brouillon), `update(value)` (pour écrire dans le brouillon) **et les mêmes `cellProps` que le slot de lecture** `#item.<columnKey>` (`item`, `column`, `index`…), pour une API cohérente entre lecture et édition. La valeur passée à `update()` conserve le type que vous lui donnez (pas de conversion).

> ⚠️ L'éditeur par défaut (`SyTextField`) ne gère que les **valeurs primitives** (chaîne, nombre, booléen). Pour une colonne dont la valeur est un **objet, un tableau ou une `Date`**, fournissez un slot `#edit.<columnKey>` avec un éditeur adapté (`SySelect`, `DatePicker`, `<input type="date">`…) — sinon aucun éditeur n'est rendu et un avertissement est émis en développement. Le rendu **hors édition** d'une telle valeur passe par `#item.<columnKey>`. Dans tous les cas, **ne mutez jamais la valeur en place** (ex. `item.role.code = …`) : appelez `update()` avec une **nouvelle** valeur/copie, afin de préserver l'annulation (`cancel`) et l'immutabilité de `items`.

> 💡 **Type préservé** : l'éditeur texte par défaut émet des chaînes ; le composant reconvertit automatiquement la valeur vers le type d'origine de la colonne (`number` → nombre, `boolean` → booléen, champ vidé d'une colonne numérique → `null`). `save(updated)` renvoie donc des types cohérents avec `items`.

**Évènements** : `edit(item)` · `save(updated, original)` · `cancel(item)` · `delete(item)`.

> ⚠️ **La ligne quitte le mode édition dès le clic sur `save`**, avant que vous ayez enregistré quoi que ce soit. `save` reçoit `updated` (la ligne modifiée) et `original` (la ligne d'avant). Si votre enregistrement est asynchrone et échoue, gérez vous-même le retour arrière en réaffichant `original`.
>
> Schéma recommandé : mettez à jour `items` avec `updated` tout de suite, et **en cas d'erreur, restaurez `original`**.

<Canvas story={{height: '350px'}} of={RowEditing.Default} />

Édition d'une valeur non primitive (`Date`) via `#edit.<columnKey>` :

<Canvas story={{height: '350px'}} of={RowEditing.NonPrimitiveEditor} />

### Sélection multiple et actions groupées

En s'appuyant sur la sélection (`show-select`), une **barre d'actions groupées** apparaît dès qu'au moins une ligne est cochée **et** qu'un slot `#bulk-actions` fournit les actions.

Le tableau **n'embarque pas** de formulaire d'édition groupée : il expose uniquement la sélection via le slot `#bulk-actions` (`{ selected, count, clearSelection }`). Le **projet** rend ses propres actions (éditer, supprimer, exporter…) et pilote leur UX avec une `DialogBox`, un drawer, une page dédiée, etc. Ce choix garde le composant léger et laisse au projet la maîtrise de l'accessibilité et des types de champs.

<Source dark code={`
<SyTable show-select selection-key="id" v-model="selected" :headers="headers" :items="items">
  <template #bulk-actions="{ selected, count, clearSelection }">
    <VBtn @click="openEdit(selected, clearSelection)">Modifier {{ count }}</VBtn>
    <VBtn @click="openDelete(selected, clearSelection)">Supprimer {{ count }}</VBtn>
  </template>
</SyTable>
`}/>

Voir la story ci-dessous : édition « appliquer une valeur à la sélection » et suppression, pilotées par des `DialogBox` du Design System (un variant séquentiel avancé est aussi fourni).

<Canvas story={{height: '350px'}} of={BulkActions.Default} />

### Slot item
Le composant permet de personnaliser l'affichage des contenus en utilisant le slot `item`. Vous pouvez définir la structure de chaque contenu en fonction de vos besoins.

### Slot headers
Le composant permet de personnaliser l'affichage des en-têtes de colonnes en utilisant le slot `headers`. Vous pouvez définir la structure de chaque en-tête en fonction de vos besoins.

### Items par page options
Le composant permet de personnaliser le nombre possible d'éléments par page en utilisant la prop `items-per-page-options`. Vous pouvez spécifier un tableau d'options pour permettre de choisir le nombre d'éléments affichés par page.

<div className="header">
  <h1>Accessibilité</h1>
</div>

Le composant améliore l'accessibilité en ajoutant automatiquement :
- Une légende (caption) pour le tableau
- Des attributs ARIA appropriés
- Des attributs scope pour les en-têtes de colonnes

## Exemples d'utilisation

### Tableau simple

<Source dark code={`
<template>
  <SyTable
    v-model:options="options"
    :headers="headers"
    :items="items"
  />
</template>

<script setup lang="ts">
  import { ref } from 'vue'
  import { SyTable } from '@cnamts/synapse'
  
  const options = ref({
    itemsPerPage: 4,
  })
import '../../../stories/styles/shared.css';
  
  const headers = ref([
    {
      title: 'Nom',
      key: 'lastname',
    },
    {
      title: 'Prénom',
      key: 'firstname',
    },
    {
      title: 'Email',
      value: 'email',
    },
  ])
    
  const items = ref([
    {
      firstname: 'Virginie',
      lastname: 'Beauchesne',
      email: 'virginie.beauchesne@example.com',
    },
    {
      firstname: 'Simone',
      lastname: 'Bellefeuille',
      email: 'simone.bellefeuille@example.com',
    },
    {
      firstname: 'Étienne',
      lastname: 'Salois',
      email: 'etienne.salois@example.com',
    },
    {
      firstname: 'Thierry',
      lastname: 'Bobu',
      email: 'thierry.bobu@example.com',
    },
    {
      firstname: 'Bernadette',
      lastname: 'Langelier',
      email: 'bernadette.langelier@exemple.com'
    },
    {
      firstname: 'Agate',
      lastname: 'Roy',
      email: 'agate.roy@exemple.com'
    }
  ])
</script>
`} />

### Plusieurs tableaux sur une même page

Pour utiliser plusieurs tableaux sur une même page avec des options indépendantes, utilisez la prop `suffix` pour chaque tableau.

## Bonnes pratiques

- Utilisez des en-têtes clairs et descriptifs pour chaque colonne
- Définissez un nombre d'éléments par page adapté à votre contenu
- Utilisez la prop `suffix` lorsque vous avez plusieurs tableaux sur une même page
- Ajoutez une légende explicite si le tableau contient des données complexes
