// aerOS — shared primitives. Built on ds.css tokens. Exports to window. const { useState, useRef, useEffect, useMemo, useCallback } = React; /* ----------------------------- Button ----------------------------- */ function Button({ children, variant = 'secondary', size = 'md', icon, iconRight, full, onClick, disabled, type = 'button', brand = false, title }) { const [hover, setHover] = useState(false); const accent = brand ? 'var(--brand)' : 'var(--accent)'; const onAccent = brand ? 'var(--on-brand)' : 'var(--on-accent)'; const sizes = { sm: { h: 30, px: 10, fs: 12.5, gap: 6 }, md: { h: 38, px: 14, fs: 13.5, gap: 7 }, lg: { h: 46, px: 20, fs: 15, gap: 8 }, }[size]; const base = { display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: sizes.gap, height: sizes.h, padding: `0 ${sizes.px}px`, borderRadius: 'var(--r-sm)', fontSize: sizes.fs, fontWeight: 600, cursor: disabled ? 'not-allowed' : 'pointer', fontFamily: 'inherit', transition: 'all 120ms', whiteSpace: 'nowrap', width: full ? '100%' : undefined, opacity: disabled ? 0.5 : 1, lineHeight: 1, }; const variants = { primary: { background: accent, color: onAccent, border: `1px solid ${accent}`, filter: hover && !disabled ? 'brightness(0.92)' : 'none' }, secondary: { background: 'var(--bg-0)', color: 'var(--fg-1)', border: '1px solid var(--border)', boxShadow: 'var(--shadow-sm)', background: hover && !disabled ? 'var(--bg-3)' : 'var(--bg-0)' }, ghost: { background: hover && !disabled ? 'var(--bg-3)' : 'transparent', color: 'var(--fg-2)', border: '1px solid transparent' }, danger: { background: hover && !disabled ? '#c81e1e' : 'var(--danger)', color: '#fff', border: '1px solid var(--danger)' }, dangerSoft: { background: 'var(--danger-soft)', color: 'var(--danger)', border: '1px solid transparent' }, outline: { background: hover && !disabled ? accent : 'transparent', color: hover && !disabled ? onAccent : accent, border: `1px solid ${accent}` }, }; return ( ); } function IconButton({ icon, title, onClick, active, size = 34, tone, badge }) { const [hover, setHover] = useState(false); return ( ); } /* ----------------------------- Field + inputs ----------------------------- */ function Field({ label, children, hint, error, required, span, full }) { return (
{label && ( )} {children} {error ?
{error}
: hint &&
{hint}
}
); } function Input({ value, onChange, placeholder, type = 'text', suffix, prefix, icon, readOnly, disabled, align = 'left', error, onKeyDown, autoFocus, min, max }) { const [focus, setFocus] = useState(false); return (
{icon && {typeof icon === 'function' ? icon({ size: 15 }) : icon}} {prefix && {prefix}} onChange && onChange(e.target.value)} placeholder={placeholder} readOnly={readOnly} disabled={disabled} autoFocus={autoFocus} onKeyDown={onKeyDown} onFocus={() => setFocus(true)} onBlur={() => setFocus(false)} style={{ flex: 1, border: 'none', outline: 'none', background: 'transparent', fontSize: 14, color: 'var(--fg-1)', fontFamily: 'inherit', minWidth: 0, textAlign: align, fontVariantNumeric: align === 'right' || type === 'number' ? 'tabular-nums' : 'normal' }} /> {suffix && {suffix}}
); } function Textarea({ value, onChange, placeholder, rows = 4, readOnly }) { const [focus, setFocus] = useState(false); return (