// Design system officiel du Web Vasco. // // Les écrans consomment ces primitives au lieu de reconstruire leurs boutons, // lignes de réglages, champs et feuilles. Les tokens historiques restent la // source des couleurs : ce module leur ajoute les contrats de composition, // de typographie et de mouvement qui manquaient à l'application connectée. if (!window.VascoDesignContract) { throw new Error('Le contrat visuel Vasco doit être chargé avant le UI Kit.'); } const VASCO_UI_TOKENS = window.VascoDesignContract; function VascoCloseIcon({ size = 18 }) { return ; } function VascoChevronIcon({ direction = 'right', size = 16 }) { const path = direction === 'left' ? 'M15 6l-6 6 6 6' : 'M9 6l6 6-6 6'; return ; } // Babel Standalone transforme chaque fichier JSX dans la portée globale. Ses // variables d'aide (`_excluded`, `_excluded2`, …) peuvent donc être remplacées // par un fichier chargé plus tard. Une décomposition avec `...buttonProps` // finirait alors par transmettre des propriétés React internes au DOM. Cette // allowlist garde les attributs HTML/accessibilité utiles sans dépendre de ces // helpers globaux. // // Aucune primitive du socle n'utilise plus `...rest` : le tri se fait par ce // qu'un élément natif accepte, jamais par ce qu'on pense avoir retiré. Une // primitive dont les données portent un nom d'attribut HTML (`value`, // `options`, `name`) déclare ce qu'elle consomme dans `ownProps` : cette part // reste à elle et ne traverse pas vers le DOM. function vascoSafeButtonProps(props, ownProps = null) { const safe = {}; const htmlAttributes = new Set(['id', 'name', 'value', 'form', 'tabIndex', 'autoFocus', 'role']); const domEvents = new Set([ 'onBlur', 'onFocus', 'onKeyDown', 'onKeyUp', 'onMouseEnter', 'onMouseLeave', 'onPointerDown', 'onPointerUp', ]); Object.entries(props || {}).forEach(([key, value]) => { if (ownProps && ownProps.has(key)) return; if (key.startsWith('aria-') || key.startsWith('data-') || htmlAttributes.has(key) || domEvents.has(key)) safe[key] = value; }); return safe; } function VascoButton(props) { const { children, onClick, variant = 'secondary', size = 'large', disabled = false, busy = false, busyLabel = null, icon = null, grow = false, type = 'button', ariaLabel = null, buttonRef = null, style = {} } = props; const buttonProps = vascoSafeButtonProps(props); const solid = variant === 'solid'; const accent = variant === 'accent'; const accentSoft = variant === 'accent-soft'; const successSoft = variant === 'success-soft'; const neutralSoft = variant === 'neutral-soft'; const danger = variant === 'danger'; const ghost = variant === 'ghost'; const height = { small: VASCO_UI_TOKENS.control.small, medium: VASCO_UI_TOKENS.control.medium, large: VASCO_UI_TOKENS.control.large, }[size] || VASCO_UI_TOKENS.control.large; const inactive = disabled || busy; return ; } function VascoIconButton({ label, onClick, children, variant = 'secondary', disabled = false, size = VASCO_UI_TOKENS.control.icon, buttonRef = null, pressed = null, expanded = null, title = null, style = {} }) { const ghost = variant === 'ghost'; return ; } // Action textuelle discrète : relancer, modifier, effacer, afficher la suite. // Elle évite que chaque écran réinvente le faux lien posé sous une carte. function VascoInlineAction(props) { const { children, onClick, disabled = false, busy = false, danger = false, icon = null, ariaLabel = null, title = null, type = 'button', style = {} } = props; const buttonProps = vascoSafeButtonProps(props); const inactive = disabled || busy; return ; } // Choix compact pour les filtres et variantes exclusives. `aria-pressed` porte // l'état : la couleur n'est jamais la seule information disponible. function VascoChoiceChip(props) { const { children, selected = false, onClick, disabled = false, ariaLabel = null, title = null, style = {} } = props; const buttonProps = vascoSafeButtonProps(props); return ; } // État global d'une fonctionnalité : information, simulation, indisponibilité // ou erreur. Le texte reste la preuve principale ; la couleur et le symbole ne // font que faciliter le balayage visuel. function VascoStatusNotice({ title, message = null, tone = 'info', compact = false, style = {} }) { const appearances = { info: { color: VASCO_UI_TOKENS.color.accent, background: VASCO_UI_TOKENS.color.accentTint, symbol: 'i' }, success: { color: VASCO_UI_TOKENS.color.success, background: VASCO_UI_TOKENS.color.surfaceAlt, symbol: '✓' }, warning: { color: VASCO_UI_TOKENS.color.warning, background: VASCO_UI_TOKENS.color.surfaceAlt, symbol: '!' }, danger: { color: VASCO_UI_TOKENS.color.danger, background: VASCO_UI_TOKENS.color.surfaceAlt, symbol: '!' }, }; const appearance = appearances[tone] || appearances.info; return
{title} {message && {message}}
; } // Ligne ou carte de choix composée. Les contenus métier restent libres mais la // surface, l'état sélectionné et la cible tactile restent identiques. function VascoInteractiveRow(props) { const { title = null, subtitle = null, children = null, leading = null, trailing = null, onClick, selected = false, disabled = false, ariaLabel = null, buttonRef = null, style = {} } = props; const buttonProps = vascoSafeButtonProps(props); return ; } function VascoSettingRow({ icon, label, sub, right, onClick, danger = false, reason = null, role, checked }) { const actionable = Boolean(onClick) && !reason; const body = <>
{label}
{sub &&
{sub}
} {reason &&
{reason}
}
{right || (actionable ? : null)} ; const shared = { width: '100%', display: 'flex', alignItems: 'center', gap: 14, padding: '14px 16px', background: 'none', border: 'none', fontFamily: VASCO_UI_TOKENS.type.body, textAlign: 'left', }; if (!actionable) return
{body}
; return ; } function VascoSettingGroup({ title, children, style = {} }) { return
{title}
{React.Children.map(children, (child, index) => {index > 0 &&
; } function VascoToggle({ on, disabled = false }) { return