/* ============================================================ mobile-kit.jsx Composants mobile-first du design system. Tous nommés explicitement et exposés sur window. Tactile-ready : hit targets ≥ 44px, animations fluides, pas de hover, feedback au touch. ============================================================ */ const { useState: uM, useRef: rM, useEffect: eM } = React; /* ============================================================ StatusBar — barre de statut iOS-like (en haut de l'écran) Nom système : StatusBar Usage : décor en haut de toute page mobile. ============================================================ */ function StatusBar({ time = '14:02', battery = 78, signal = 4 }) { return (
{time} {/* signal bars */} {[1, 2, 3, 4].map((b) => ( ))} {/* battery */}
); } /* ============================================================ NavBar — barre de navigation en haut (titre + actions) Nom système : NavBar Usage : titre d'écran avec retour optionnel à gauche, actions à droite. ============================================================ */ function NavBar({ title, subtitle, onBack, right, large }) { return (
{onBack && ( )}
{!large && (
{title}
)}
{right &&
{right}
}
{large && (
{title}
{subtitle &&
{subtitle}
}
)}
); } /* ============================================================ TabBar — barre d'onglets en bas (iOS/Android) Nom système : TabBar Usage : navigation principale entre 3-5 sections de l'app. ============================================================ */ function TabBar({ items, active, onSelect }) { return (
{items.map((it) => { const isActive = active === it.id; return ( ); })}
); } /* ============================================================ ListRow — ligne d'une liste réglages (style iOS) Nom système : ListRow Usage : option dans une liste de réglages. ≥ 44px de hauteur. ============================================================ */ function ListRow({ icon, iconColor, label, value, right, onClick, danger }) { const isInteractive = !!onClick; const Tag = isInteractive ? 'button' : 'div'; return ( e.currentTarget.style.background = 'var(--bg-3)' : undefined} onTouchEnd={isInteractive ? (e) => e.currentTarget.style.background = 'transparent' : undefined}> {icon && ( )} {label} {value && {value}} {right === undefined && onClick && } {right} ); } /* ============================================================ ListSection — groupe de ListRow avec titre Nom système : ListSection ============================================================ */ function ListSection({ title, hint, children }) { return (
{title && (
{title}
)}
{children}
{hint && (
{hint}
)}
); } /* ============================================================ ActionCard — grosse carte d'action tactile Nom système : ActionCard Usage : actions principales sur écran d'accueil. ============================================================ */ function ActionCard({ icon, iconColor, title, subtitle, value, onClick, badge }) { return ( ); } /* ============================================================ PrimaryButton — gros bouton plein largeur tactile Nom système : PrimaryButton Usage : action principale d'un écran (sauvegarder, valider). ============================================================ */ function PrimaryButton({ children, icon, onClick, variant = 'primary', size = 'lg' }) { const sizes = { md: { h: 44, fontSize: 14 }, lg: { h: 52, fontSize: 16 }, }[size]; const styles = { primary: { bg: 'var(--accent)', fg: 'var(--bg-1)', bd: 'var(--accent-soft)' }, ghost: { bg: 'var(--bg-3)', fg: 'var(--ink-1)', bd: 'var(--border-2)' }, danger: { bg: 'var(--err)', fg: '#fff', bd: 'var(--err)' }, }[variant]; return ( ); } /* ============================================================ SegmentedControl — sélecteur segmenté iOS-style Nom système : SegmentedControl Usage : 2-4 options exclusives, jamais plus. ============================================================ */ function SegmentedControl({ value, onChange, options }) { return (
{options.map((o) => { const v = typeof o === 'string' ? o : o.value; const l = typeof o === 'string' ? o : o.label; const ic = typeof o === 'string' ? null : o.icon; const active = value === v; return ( ); })}
); } /* ============================================================ SearchBar — champ de recherche mobile Nom système : SearchBar ============================================================ */ function SearchBar({ value, onChange, placeholder = 'Rechercher' }) { return (
onChange(e.target.value)} placeholder={placeholder} style={{ flex: 1, minWidth: 0, background: 'transparent', border: 'none', outline: 'none', color: 'var(--ink-1)', fontFamily: 'var(--font-ui)', fontSize: 15, }} /> {value && ( )}
); } Object.assign(window, { StatusBar, NavBar, TabBar, ListRow, ListSection, ActionCard, PrimaryButton, SegmentedControl, SearchBar, }); /* Effets tactiles : pression au touch (pas de hover) */ (function injectMobileFX() { if (document.getElementById('mobile-fx')) return; const s = document.createElement('style'); s.id = 'mobile-fx'; s.textContent = ` .touch-press { transition: transform .08s ease-out, filter .08s, box-shadow .08s; } .touch-press:active { transform: scale(0.97); filter: brightness(0.92); } `; document.head.appendChild(s); })();