Yayaw
Documentation
Afficher et explorer

Vues enregistrées

Enregistrer des dispositions, partager des vues et choisir un favori personnel.

Une vue enregistrée est un instantané sérialisable de la disposition du tableau. Elle reste distincte du catalogue de configuration et des données affichées.

Droits propres à chaque vue

TableView.canEdit et TableView.canDelete permettent à l’application de décrire séparément les droits de l’utilisateur courant. La valeur false désactive l’action correspondante dans React et Vue. Si les propriétés sont absentes, le comportement existant des vues non système est conservé. Les vues système et allowViewSave: false restent en lecture seule.

Une vue partagée reste sélectionnable, copiable et utilisable comme favori personnel sans droit de modification. Renvoyez ces propriétés depuis les actions de liste, de création et de modification selon le propriétaire et l’espace courant. L’adaptateur local conserve et respecte les propriétés explicites ; les actions distantes doivent contrôler elles-mêmes les autorisations et les modifications concurrentes. Les propriétés d’interface ne constituent pas une protection côté serveur.

Stockez le favori dans une préférence individuelle, séparée de la définition partagée. Choisir un favori ne doit ni renommer ni remplacer la vue d’un collègue, ni la définir par défaut pour toute l’organisation.

Contrôles d’affichage cohérents

Les choix de densité utilisent une graisse normale, y compris le choix sélectionné, la sélection étant indiquée par son fond et son état accessible. Le contrôle Mode d’affichage est une liste déroulante étiquetée (Select Base UI en React, TableSelect en Vue) ; les barres d’outils compactes et les tiroirs tactiles conservent les boutons. Les réglages de galerie, Kanban et Gantt utilisent des champs compacts et un seul tiroir sur mobile, y compris pour les choix imbriqués. Sur les layouts tactiles, la mise en page et la densité sont des lignes affichant leur valeur courante qui ouvrent le même écran de choix que les propriétés, le filtre, le tri et le groupement. Partager se trouve dans le menu Données, à côté d’Exporter et de Connecter, avec la même hauteur de 32 px sur desktop et une cible tactile d’au moins 44 px sur les layouts compacts.

Exemple de configuration

Partez du démarrage React ou du démarrage Vue. Le fichier suivant complète leur catalogue de produits. Passez cette configuration via getTableConfig en React ou config en Vue.

view-actions.ts
import type {
  TableView,
  TableViewActions,
} from "@/components/ui/yayaw-table/types/view-types";

/** Demo persistence lasts for this module's lifetime. Scope server storage by user and organization. */
const views = new Map<string, TableView>();
const favorites = new Map<string, string | null>();
export const viewActions: TableViewActions = {
  list: ({ tableId }) =>
    Promise.resolve({
      data: [...views.values()].filter((view) => view.tableId === tableId),
    }),
  create: (input) => {
    const view = {
      ...input,
      id: crypto.randomUUID(),
      createdById: "demo-user",
    };
    views.set(view.id, structuredClone(view));
    return Promise.resolve({ success: true, data: view });
  },
  update: (id, input) => {
    const current = views.get(id);
    if (!current || (input.tableId && input.tableId !== current.tableId)) {
      return Promise.resolve({ success: false, error: "View not found." });
    }
    const view = { ...current, ...input };
    views.set(id, structuredClone(view));
    return Promise.resolve({ success: true, data: view });
  },
  delete: (id, { tableId }) => {
    if (views.get(id)?.tableId !== tableId) {
      return Promise.resolve({ success: false, error: "View not found." });
    }
    views.delete(id);
    if (favorites.get(tableId) === id) {
      favorites.delete(tableId);
    }
    return Promise.resolve({ success: true, data: { id } });
  },
  getFavorite: ({ tableId }) =>
    Promise.resolve({
      success: true,
      data: { viewId: favorites.get(tableId) ?? null },
    }),
  setFavorite: (viewId, { tableId }) => {
    if (viewId && views.get(viewId)?.tableId !== tableId) {
      return Promise.resolve({ success: false, error: "View not found." });
    }
    favorites.set(tableId, viewId);
    return Promise.resolve({ success: true, data: { viewId } });
  },
};
view-actions.ts
import type {
  TableView,
  TableViewActions,
} from "@/components/ui/yayaw-table-vue/types";

/** Demo persistence lasts for this module's lifetime. Scope server storage by user and organization. */
const views = new Map<string, TableView>();
const favorites = new Map<string, string | null>();
export const viewActions: TableViewActions = {
  list: ({ tableId }) =>
    Promise.resolve({
      data: [...views.values()].filter((view) => view.tableId === tableId),
    }),
  create: (input) => {
    const view = {
      ...input,
      id: crypto.randomUUID(),
      createdById: "demo-user",
    };
    views.set(view.id, structuredClone(view));
    return Promise.resolve({ success: true, data: view });
  },
  update: (id, input) => {
    const current = views.get(id);
    if (!current || (input.tableId && input.tableId !== current.tableId)) {
      return Promise.resolve({ success: false, error: "View not found." });
    }
    const view = { ...current, ...input };
    views.set(id, structuredClone(view));
    return Promise.resolve({ success: true, data: view });
  },
  delete: (id, { tableId }) => {
    if (views.get(id)?.tableId !== tableId) {
      return Promise.resolve({ success: false, error: "View not found." });
    }
    views.delete(id);
    if (favorites.get(tableId) === id) {
      favorites.delete(tableId);
    }
    return Promise.resolve({ success: true });
  },
  getFavorite: ({ tableId }) =>
    Promise.resolve({
      success: true,
      data: { viewId: favorites.get(tableId) ?? null },
    }),
  setFavorite: (viewId, { tableId }) => {
    if (viewId && views.get(viewId)?.tableId !== tableId) {
      return Promise.resolve({ success: false, error: "View not found." });
    }
    favorites.set(tableId, viewId);
    return Promise.resolve({ success: true, data: { viewId } });
  },
};

Brancher les actions

Ajoutez viewActions dans getTableActions(tableType).views. Activez enableViews et allowViewSave ; activez allowViewSharing seulement si l’application gère un partage par organisation. L’exemple stocke les vues en mémoire et les perd au rechargement. Utilisez une base pour des vues persistantes. La recette fiche branche le parcours complet.

Contenu d’une vue

Une vue (TableViewConfig) peut restaurer la recherche (globalSearch), les filtres simples et avancés, le tri, le groupement, la visibilité, l’ordre, les dimensions et l’épinglage des colonnes, la densité, l’affichage des calculs de pied de table, la taille de page, le mode d’affichage et les réglages de chaque mode : kanban, gallery, list, filetree, calendar, chart, feed, map, form et gantt. Stockez des valeurs et identifiants de colonnes stables. Ne sérialisez pas les callbacks, renderers, autorisations ou clients de requête.

Les règles de date sont enregistrées et partagées comme des jours du calendrier (AAAA-MM-JJ). Les vues enregistrées par les versions antérieures à v3.8.0 gardent leurs instants jusqu’à leur prochain enregistrement, et s’ouvrent toujours sur les mêmes jours : la table lit ces instants comme les jours du lecteur, sans marquer la vue comme modifiée.

Favoris et partage

isGlobal décrit une vue partagée dans le périmètre d’organisation de l’application. getFavorite et setFavorite gèrent une vue d’arrivée personnelle sans modifier la vue partagée. Effacez un favori dont la vue a été supprimée ou devient inaccessible. L’URL peut représenter une disposition temporaire ; consultez l’état dans l’URL.

Modifier ou réinitialiser une vue

Ouvrez le menu de la vue pour choisir une vue ou un favori, modifier ses réglages, enregistrer, réinitialiser ou supprimer. Le mode et les six densités du tableau sont accessibles directement. Filtres, tri, groupement et propriétés ont leurs écrans avec retour. Désactiver enableViews conserve un bouton Vue pour les réglages.

La pastille bleue indique uniquement une différence avec la vue enregistrée active. Une vue enregistrée filtrée s'ouvre sans pastille. Densité, mode, recherche, filtres, tri, groupement, colonnes, cartes, taille de page et visibilité des calculs participent à la même comparaison. La sélection et la page courante sont exclues. Revenir manuellement à la configuration enregistrée fait disparaître la pastille.

Enregistrer les modifications reste visible mais grisé sans changement. Une infobulle l'explique au survol et au focus sur desktop ; le texte apparaît sous l'action sur mobile. Enregistrer comme nouvelle vue crée une autre vue. Une vue temporaire propose Enregistrer cette vue…, sans pastille. Les erreurs de sauvegarde conservent les modifications ; les favoris restent disponibles sans droit d'enregistrement.

Réinitialiser la vue, avec Lucide ListRestart, restaure la version enregistrée ou la configuration initiale de l'application pour une vue temporaire. Cette action ne modifie aucune donnée et ne supprime pas la vue. showClearFilters et son alias historique showResetFilters gardent leur portée limitée aux filtres, dans l'écran Filtres.

Le snapshot partagé inclut maintenant footerCalculationsVisible?: boolean. Les anciennes vues sans cette propriété héritent de la visibilité initiale ; le flag enableCalculations continue de contrôler la disponibilité de la fonction. Conservez les réglages des modes inactifs lors de la persistance des snapshots.

Par exemple, ouvrez une vue enregistrée sans groupement, filtrée sur Open, groupez-la par statut et changez sa densité. Réinitialiser la vue retire ce groupe, restaure la densité enregistrée (ou celle de la configuration pour une ancienne vue), conserve le filtre Open et retire la pastille bleue. Une colonne kanban mémorisée pour un mode inactif n'ajoute pas de groupement au tableau. Réinitialiser une vue temporaire retire également les groupes ajoutés.

Onglets de vues

La barre d'outils sépare le sélecteur de vues, à gauche, des réglages de la vue, à droite. Les vues enregistrées s'affichent sous forme d'onglets dans le sélecteur sur les grands écrans, dès que la table compte au moins une vue enregistrée. La vue par défaut est toujours le premier onglet ; chaque onglet affiche l'icône de son mode d'affichage et un point quand la vue active a des changements non enregistrés. Cliquer sur un onglet applique la vue.

Quand il y a plus de vues que table.viewTabs.maxVisible (4 par défaut), le reste passe sous …, un bouton à icône nommé « Plus de vues » (avec la même infobulle) dont le menu affiche leurs noms complets ; la vue active reste toujours visible, à la place du dernier onglet affiché. + (« Nouvelle vue ») ouvre le formulaire de sauvegarde et ne s'affiche que si des vues peuvent être créées (allowViewSave et une action de création). Le formulaire de sauvegarde propose un choix Mise en page — les modes d'affichage que la table propose, celui en cours par défaut ; créer une vue dans une autre mise en page bascule vers elle.

Avec les onglets, un chevron Actions de la vue à côté d'eux regroupe enregistrer les changements, enregistrer comme nouvelle vue, favori, Déplacer à gauche et Déplacer à droite (voir Ordre des vues), réinitialiser et supprimer. Les barres d'outils compactes et les layouts tactiles remplacent les onglets par un déclencheur à icône seule — sans libellé ni chevron, pour laisser la place à la recherche sur la ligne — avec un nom accessible construit à partir de views.current (« Current view » par défaut) suivi du nom de la vue, et un petit point pour les changements non enregistrés. Son menu liste les vues — défilant, avec un filtre Chercher une vue au-delà de sept — suivies des mêmes actions. Mettez table.viewTabs: false pour garder le déclencheur nommé (icône, nom de la vue et chevron) même sur les grands écrans.

Ordre des vues

Chaque personne ordonne ses propres vues enregistrées. Le menu de la vue déplace la vue enregistrée courante d'un cran : Déplacer à gauche et Déplacer à droite à côté des onglets, Déplacer vers le haut et Déplacer vers le bas là où le menu liste les vues (téléphones et viewTabs: false), juste après le favori. À chaque extrémité, l'action reste focalisable mais inactive (aria-disabled), pour que le focus y reste, et chaque déplacement est annoncé aux lecteurs d'écran, par exemple « Vue « Ventes » déplacée en position 3 sur 6 » (la position compte d'abord la vue par défaut intégrée). L'ordre s'applique aux onglets, à la liste … et à la liste des vues du menu ; la vue sur laquelle une table s'ouvre n'en dépend pas.

  • Les vues système (isSystem) et la vue par défaut (isDefault, comme la vue propre d'un écran de tableau de bord) restent en tête, dans l'ordre de la liste, sans actions de déplacement.

  • Les vues que l'ordre ne nomme pas, comme les nouvelles, viennent en dernier dans l'ordre de la liste. Les identifiants inconnus sont ignorés, et avec moins de deux vues à ordonner, il n'y a rien à déplacer.

Conservez l'ordre sur votre serveur avec l'action facultative views.setOrder. Elle reçoit le nouvel ordre complet de la personne, du premier au dernier, sans les vues système ni la vue par défaut ; stockez-le tel quel, par utilisateur, organisation, type de table et identifiant de table, et renvoyez-le depuis list dans order. La table trie les vues avec, selon les règles ci-dessus, donc votre serveur n'a rien à trier (un serveur peut aussi lister les vues déjà dans cet ordre et omettre order). orderViews(views, order), dans utils/view-order.ts, trie de la même façon quand votre serveur en a besoin. Une écriture refusée ({ success: false, error }) garde l'ordre précédent et affiche l'erreur dans le gestionnaire de vues.

// À côté de list, create, update, delete, getFavorite et setFavorite.
const orders = new Map<string, string[]>(); // par utilisateur, organisation, type de table et table

export const viewActions: TableViewActions = {
  list: async ({ tableId, tableType }) => ({
    data: await listViews(tableId),
    order: orders.get(orderKey(tableType, tableId)),
  }),
  setOrder: ({ tableId, tableType, viewIds }) => {
    orders.set(orderKey(tableType, tableId), viewIds);
    return Promise.resolve({ success: true, data: { viewIds } });
  },
};

L'action et la réponse de list (TableViewListResult, { data, order? }) sont les mêmes en React et en Vue ; le list de Vue peut toujours répondre un simple tableau. Sans setOrder, l'ordre reste dans le localStorage du navigateur, sous yayaw-table-view-order:<JSON [tableType, tableId]>, la portée du favori, partagée par tous les gestionnaires de cette table sur la page. createLocalTableViewActions() n'a pas de setOrder (son type est LocalTableViewActions), donc les tables qui l'utilisent gardent ce repli.

Partager le lien courant

Partager se trouve dans le menu Données, ouvert depuis le bouton à icône de base de données à côté des Réglages de la vue, aux côtés d'Exporter et de Connecter. Sans destination de partage personnalisée, l'action copie directement l'URL courante exacte sur desktop (sur mobile, elle utilise le partage natif quand il est disponible, avec copie en repli ; une annulation reste silencieuse). Cette action ne donne aucun droit et ne modifie pas isGlobal. Le partage organisationnel reste une option distincte du formulaire de sauvegarde. Mettez table.share: false pour masquer la ligne Partager.

Les destinations de connexion et de partage personnalisées déclarées dans actions.destinations (webhooks, n8n, connecteurs) ont chacune leur propre ligne dans le même menu Données : les destinations de connexion sous Connecter › (masquée s'il n'y en a aucune), les destinations de partage sous Partager ›, après le « Copier le lien » intégré — avec des destinations de partage déclarées, Partager ouvre cet écran au lieu de copier directement. Voir Destinations sur mesure.

Activer les vues enregistrées

Branchez l’adaptateur viewActions présenté plus haut dans la propriété views des actions. Enregistrez une vue filtrée, rouvrez-la, changez sa densité et réinitialisez : le filtre enregistré reste et la pastille disparaît.

Copiez cette configuration complète à côté de product-config.ts. Utilisez () => exampleConfig pour getTableConfig en React, ou :config="exampleConfig" en Vue.

Agrandir ↗

Données de démonstration. Les modifications restent dans cet aperçu.

Agrandir ↗

Données de démonstration. Les modifications restent dans cet aperçu.

Conserver les réglages sans vues enregistrées

Utilisez le bouton Vue pour les filtres, le tri, les propriétés et la densité sans catalogue de vues enregistrées. Réinitialiser restaure la configuration initiale de l’application.

Copiez cette configuration complète à côté de product-config.ts. Utilisez () => exampleConfig pour getTableConfig en React, ou :config="exampleConfig" en Vue.

Agrandir ↗

Données de démonstration. Les modifications restent dans cet aperçu.

Agrandir ↗

Données de démonstration. Les modifications restent dans cet aperçu.