// fichaje.eu — Shared UI primitives
const Card = ({ children, style, padded = true, onClick, hoverable }) => (
{ e.currentTarget.style.boxShadow = 'var(--shadow-md)'; } : undefined}
onMouseLeave={hoverable ? (e) => { e.currentTarget.style.boxShadow = 'var(--shadow-xs)'; } : undefined}
>{children}
);
const SectionHead = ({ title, subtitle, action }) => (
{title}
{subtitle &&
{subtitle}
}
{action}
);
const Btn = ({ children, variant = 'primary', size = 'md', icon, iconRight, onClick, style, disabled, type }) => {
const v = {
primary: { bg: 'var(--brand-600)', color: '#fff', border: 'transparent', hoverBg: 'var(--brand-700)' },
secondary: { bg: 'var(--white)', color: 'var(--ink)', border: 'var(--slate-200)', hoverBg: 'var(--slate-50)' },
ghost: { bg: 'transparent', color: 'var(--slate-700)', border: 'transparent', hoverBg: 'var(--slate-100)' },
danger: { bg: 'var(--danger-500)', color: '#fff', border: 'transparent', hoverBg: 'var(--danger-600)' },
soft: { bg: 'var(--brand-50)', color: 'var(--brand-600)', border: 'transparent', hoverBg: 'var(--brand-100)' },
success: { bg: 'var(--success-500)', color: '#fff', border: 'transparent', hoverBg: 'var(--success-600)' },
}[variant];
const s = {
sm: { pad: '6px 10px', fs: 13, gap: 6 },
md: { pad: '9px 14px', fs: 14, gap: 8 },
lg: { pad: '12px 18px', fs: 15, gap: 10 },
}[size];
return (
);
};
const Badge = ({ children, tone = 'slate', dot, style }) => {
const t = {
slate: ['var(--slate-100)', 'var(--slate-700)', 'var(--slate-400)'],
brand: ['var(--brand-50)', 'var(--brand-600)', 'var(--brand-500)'],
success: ['var(--success-50)', 'var(--success-600)', 'var(--success-500)'],
warning: ['var(--warning-50)', 'var(--warning-600)', 'var(--warning-500)'],
danger: ['var(--danger-50)', 'var(--danger-600)', 'var(--danger-500)'],
violet: ['var(--violet-100)', '#6D28D9', 'var(--violet-500)'],
teal: ['var(--teal-100)', '#0F766E', 'var(--teal-500)'],
pink: ['var(--pink-100)', '#BE185D', 'var(--pink-500)'],
}[tone];
return (
{dot && }
{children}
);
};
const Avatar = ({ name, src, size = 36, color, ring }) => {
// Deterministic palette from name
const palettes = [
['#DBEAFE', '#1E40AF'], ['#FCE7F3', '#BE185D'], ['#D1FAE5', '#047857'],
['#FEF3C7', '#B45309'], ['#EDE9FE', '#6D28D9'], ['#CCFBF1', '#0F766E'],
['#FFE4E6', '#BE123C'], ['#E0E7FF', '#3730A3'],
];
const idx = (name || '?').split('').reduce((a, c) => a + c.charCodeAt(0), 0) % palettes.length;
const [bg, fg] = color || palettes[idx];
const initials = (name || '?').split(' ').map(p => p[0]).slice(0, 2).join('').toUpperCase();
return (
{initials}
);
};
const ProgressBar = ({ value, max = 100, color = 'var(--brand-600)', track = 'var(--slate-100)', height = 6 }) => (
);
const KV = ({ label, value, mono }) => (
);
const Divider = ({ vertical, style }) => (
);
const Tabs = ({ tabs, value, onChange, style }) => (
{tabs.map(t => (
))}
);
const Field = ({ label, hint, children, required }) => (
);
const Input = ({ icon, ...props }) => (
{icon &&
{icon}
}
{ e.target.style.borderColor = 'var(--brand-500)'; e.target.style.boxShadow = 'var(--ring-brand)'; }}
onBlur={(e) => { e.target.style.borderColor = 'var(--slate-200)'; e.target.style.boxShadow = 'none'; }}
/>
);
const Stat = ({ label, value, delta, icon, tone = 'brand' }) => {
const tones = {
brand: ['var(--brand-50)', 'var(--brand-600)'],
success: ['var(--success-50)', 'var(--success-600)'],
warning: ['var(--warning-50)', 'var(--warning-600)'],
violet: ['var(--violet-100)', '#6D28D9'],
}[tone] || ['var(--slate-100)', 'var(--slate-700)'];
return (
{label}
{value}
{delta &&
{delta}
}
{icon}
);
};
Object.assign(window, { Card, SectionHead, Btn, Badge, Avatar, ProgressBar, KV, Divider, Tabs, Field, Input, Stat });