// UI Kit Vasco : la moitié bureau du catalogue. // // Décision de Sfar du 14/09/2026 : Web bureau, Web mobile et mobile natif sont // trois cibles séparées. `ui-kit.jsx` reste donc le catalogue de la cible // téléphone, identique à son contenu du gel ; ce fichier-ci ajoute, au-delà du // seuil seulement, la démonstration des primitives qui n'existent qu'au bureau. // // Il ne recopie rien du catalogue téléphone : il appelle l'écran existant, pose // la coquille réelle autour, et ajoute ses sections à la suite des siennes. Les // trois commandes qui ouvrent les surfaces modales sont celles du catalogue, // reprises par leur libellé dans l'arbre qu'il vient de rendre : il n'y a qu'un // seul jeu d'états de démonstration. const VASCO_UI_KIT_LEGACY_SCREEN = VascoUiKitScreen; // Doublure de démonstration : elle reproduit la forme exacte des feuilles de la // référence figée, un cadre plein écran, un voile cliquable et un panneau ancré // en bas avec sa propre animation. L'hôte la recadre sans la connaître. function VascoUiKitLegacySurface({ onClose, tokens }) { return
; } // Une commande du catalogue, reprise dans l'arbre qu'il vient de rendre. On la // repère par son libellé, qui est le texte que le catalogue affiche : rien n'est // recréé, et les états de démonstration restent les siens. function vascoUiKitCommand(node, label, depth = 0) { if (depth > 10 || node === null || node === undefined || typeof node === 'boolean') return null; if (Array.isArray(node)) { for (const child of node) { const found = vascoUiKitCommand(child, label, depth); if (found) return found; } return null; } if (!React.isValidElement(node)) return null; if (node.props.children === label && typeof node.props.onClick === 'function') { return node.props.onClick; } return vascoUiKitCommand(node.props.children, label, depth + 1); } function VascoUiKitDesktopSections({ navTab, onNavTab, legacyOpen, onLegacyOpen, onLegacyClose, openForm, openBottom, openConfirm }) { const { tokens, Button, InlineAction, InteractiveRow, StatusNotice, SideNav, PageHeader, PageGrid, PageColumn, Panel, SplitView, TimeGrid, CalendarEvent, LegacySurfaceFrame, useColumns } = window.VascoUI; const columns = useColumns(); return <>

L’entrée active porte aria-current="page", la teinte d’accent et sa couleur. Le survol n’éclaire une entrée inactive que sur un pointeur précis ; la tabulation révèle un contour de focus, et Entrée comme Espace changent d’onglet. Entrée sélectionnée : {navTab}.

Une page de bureau se compose d’un PageHeader, d’une PageGrid de {tokens.layout.grid.columns} colonnes et de Panel. Au-dessous de {tokens.layout.grid.singleColumnMaxWidth} px, la grille repasse en colonne unique. Grille actuelle : {columns === 'split' ? 'deux colonnes' : 'colonne unique'}.

Action principale} style={{ paddingTop: 0 }} /> Tout ouvrir} footer={1 CRO · 1 CRC}>
Les lignes réutilisent InteractiveRow : clavier, focus et cible identiques.
{}} />

Une liste à gauche, le détail de l’élément choisi à droite. Chaque volet défile pour son compte et porte son étiquette. La liste mesure {tokens.layout.split.listWidth} px, et {tokens.layout.split.narrowListWidth} px sous le seuil de scission.

{['Premier élément', 'Deuxième élément', 'Troisième élément'].map((label, index) => ( {}} style={{ minHeight: tokens.layout.panel.rowHeight, marginBottom: tokens.space.xs, borderColor: index === 1 ? tokens.color.accent : 'transparent', background: index === 1 ? tokens.color.accentTint : 'transparent', }}> {label} ))}
} detail={
Le détail de l’élément sélectionné vit ici. Changer de sélection ne fait pas bouger la liste : son défilement, sa recherche et son filtre restent en place.
} />

Un axe des heures et des colonnes de jours. La primitive ne calcule ni horaire ni chevauchement : elle reçoit une géométrie, des heures et des blocs déjà placés. Une heure mesure {tokens.layout.agenda.hourHeight} px.

({ minutes, offset: index * tokens.layout.agenda.hourHeight, label: `${String(Math.floor(minutes / 60)).padStart(2, '0')}:00`, }))} nowOffset={tokens.layout.agenda.hourHeight * 1.5} columns={[ { key: 'lun', header: Lundi, children: [ {}} />, {}} />, ] }, { key: 'mar', header: Mardi, children: [ {}} />, ] }, { key: 'mer', header: Mercredi, muted: true, children: [] }, // Une journée de congé : grisée en tête, hachurée sous ses blocs, qui // restent lisibles. Jamais rouge – partir en congé n’est pas un danger. { key: 'jeu', header: Jeudi, absent: true, children: [ {}} />, ] }, ]} />

Les feuilles de la référence figée gardent leur contenu et leur propre croix. Au bureau, un hôte unique recadre leur cadre, leur voile et leur panneau : dialogue centré de {' '}{tokens.layout.dialog.wideMaxWidth} px, quatre coins arrondis, pas de poignée, fermeture par Échap et par le fond. Sous le seuil, elles glissent du bas, inchangées.

{legacyOpen && }
; } // Au-delà du seuil, le catalogue s'affiche dans la coquille réelle : la // navigation latérale et la colonne de contenu se relisent avec les mêmes // primitives que l'application. Sous le seuil, il est rendu tel quel. VascoUiKitScreen = function VascoUiKitScreenForLayout(props) { const desktop = window.VascoUI.useLayout() === 'desktop'; const [navTab, setNavTab] = React.useState('PAT'); const [legacyOpen, setLegacyOpen] = React.useState(false); // Appel inconditionnel : les états de démonstration du catalogue appartiennent // alors à ce composant et y restent stables d'un rendu à l'autre. const tree = VASCO_UI_KIT_LEGACY_SCREEN(props); if (!desktop || !React.isValidElement(tree)) return tree; const children = React.Children.toArray(tree.props.children); const scroll = children.find((child) => React.isValidElement(child) && child.props.className === 'vasco-scroll'); if (!scroll) return tree; const sections = setLegacyOpen(true)} onLegacyClose={() => setLegacyOpen(false)} openForm={vascoUiKitCommand(scroll, 'Ouvrir l’exemple')} openBottom={vascoUiKitCommand(scroll, 'Ouvrir depuis le bas')} openConfirm={vascoUiKitCommand(scroll, 'Ouvrir la confirmation')} />; const rebuilt = children.map((child) => (child === scroll ? React.cloneElement(child, undefined, ...React.Children.toArray(child.props.children), sections) : child)); return }> {React.cloneElement(tree, undefined, ...rebuilt)} ; }; window.VascoUiKitScreen = VascoUiKitScreen;