@verfrut/ui
v1.0.3
Published
Design System de Verfrut
Readme
@verfrut/ui — Design System
Librería de componentes web (Web Components) basada en Lit. Funciona en cualquier framework: React, Vue, Angular, HTML puro y proyectos .cshtml.
Instalación
npm install @verfrut/uiConfiguración inicial
Importa una sola vez en el entry point del proyecto:
React / Vue / Vite — en main.jsx o main.js:
import '@verfrut/ui';
import '@verfrut/ui/tokens.css';Angular — en main.ts + agregar schema en el módulo:
// main.ts
import '@verfrut/ui';
import '@verfrut/ui/tokens.css';
// app.module.ts
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
@NgModule({
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})HTML / .cshtml:
<head>
<link rel="stylesheet" href="https://unpkg.com/@verfrut/ui/dist/tokens.css" />
</head>
<body>
<script type="module">
import 'https://unpkg.com/@verfrut/ui/dist/index.esm.js';
</script>
</body>Actualizar a la última versión
npm install @verfrut/ui@latestUso en React
Los atributos string/boolean van directo en JSX. Para arrays y eventos custom usa useRef:
import { useRef, useEffect } from 'react';
function Navbar() {
const ref = useRef(null);
useEffect(() => {
const el = ref.current;
el.empresas = [{ id: '1', codigo: 'VRF', nombre: 'Verfrut Chile' }];
el.temporadas = [{ id: '1', codigo: '2024-2025' }];
el.especies = [{ id: '1', descripcion: 'Uva de Mesa', codigo: 'UM', color: '#8B5CF6' }];
const onLogout = () => { /* cerrar sesión */ };
const onApply = (e) => console.log(e.detail); // { empresa, temporada }
el.addEventListener('nav-logout', onLogout);
el.addEventListener('ctx-apply', onApply);
return () => {
el.removeEventListener('nav-logout', onLogout);
el.removeEventListener('ctx-apply', onApply);
};
}, []);
return (
<my-navbar
ref={ref}
user-name="Juan Pérez"
user-initials="JP"
especie="Uva de Mesa"
especie-color="#8B5CF6"
empresa-codigo="VRF"
temporada-codigo="2024-2025"
/>
);
}| Tipo de prop | Cómo pasarlo en React |
|---|---|
| String / boolean | Atributo JSX directo |
| Array / objeto | useRef + el.prop = valor en useEffect |
| Evento custom | useRef + addEventListener en useEffect |
Componentes
Button <my-button>
<my-button variant="primary">Guardar</my-button>
<my-button variant="secondary">Cancelar</my-button>
<my-button variant="danger">Eliminar</my-button>
<my-button variant="primary" size="sm">Pequeño</my-button>
<my-button variant="primary" size="lg">Grande</my-button>
<my-button variant="primary" disabled>Deshabilitado</my-button>| Prop | Valores | Default |
|------|---------|---------|
| variant | primary secondary danger | primary |
| size | sm md lg | md |
| disabled | boolean | false |
Input <my-input>
<my-input label="Nombre" placeholder="Escribe aquí..."></my-input>
<my-input label="Email" type="email" value="[email protected]"></my-input>
<my-input label="Contraseña" type="password"></my-input>
<my-input label="Campo" error="Este campo es obligatorio"></my-input>
<my-input label="Campo" disabled></my-input>| Prop | Valores | Default |
|------|---------|---------|
| label | string | '' |
| placeholder | string | '' |
| value | string | '' |
| type | text email password number | text |
| error | string | '' |
| disabled | boolean | false |
| Evento | Detalle |
|--------|---------|
| my-input | { detail: string } — valor actual |
Badge <my-badge>
<my-badge variant="primary">Nuevo</my-badge>
<my-badge variant="success">Activo</my-badge>
<my-badge variant="warning">Pendiente</my-badge>
<my-badge variant="danger">Error</my-badge>
<my-badge variant="neutral">Inactivo</my-badge>
<my-badge variant="primary" size="sm">Pequeño</my-badge>| Prop | Valores | Default |
|------|---------|---------|
| variant | primary success warning danger neutral | primary |
| size | sm md | md |
Checkbox <my-checkbox>
<my-checkbox label="Acepto los términos"></my-checkbox>
<my-checkbox label="Seleccionado" checked></my-checkbox>
<my-checkbox label="Deshabilitado" disabled></my-checkbox>| Prop | Valores | Default |
|------|---------|---------|
| label | string | '' |
| checked | boolean | false |
| disabled | boolean | false |
| Evento | Detalle |
|--------|---------|
| my-change | { detail: boolean } — estado actual |
Alert <my-alert>
<my-alert variant="info" title="Información">Mensaje informativo.</my-alert>
<my-alert variant="success" title="Éxito">Cambios guardados correctamente.</my-alert>
<my-alert variant="warning" title="Atención">Esta acción no se puede deshacer.</my-alert>
<my-alert variant="danger" title="Error">No se pudo completar la solicitud.</my-alert>| Prop | Valores | Default |
|------|---------|---------|
| variant | info success warning danger | info |
| title | string | '' |
Spinner <my-spinner>
<my-spinner></my-spinner>
<my-spinner size="sm"></my-spinner>
<my-spinner size="lg"></my-spinner>| Prop | Valores | Default |
|------|---------|---------|
| size | sm md lg | md |
Card <my-card>
<my-card>
<span slot="header">Título</span>
<p>Contenido del cuerpo.</p>
<div slot="footer">Acciones del footer</div>
</my-card>
<my-card shadow>
<p>Card con sombra, sin header ni footer.</p>
</my-card>| Prop | Valores | Default |
|------|---------|---------|
| shadow | boolean | false |
| Slot | Descripción |
|------|-------------|
| header | Título de la tarjeta |
| (default) | Cuerpo de la tarjeta |
| footer | Pie de la tarjeta |
Divider <my-divider>
<my-divider></my-divider>
<my-divider label="o continúa con"></my-divider>
<my-divider orientation="vertical" style="height:40px"></my-divider>| Prop | Valores | Default |
|------|---------|---------|
| orientation | horizontal vertical | horizontal |
| label | string | '' |
Navbar <my-navbar>
<my-navbar
user-name="Juan Pérez"
user-initials="JP"
especie="Uva de Mesa"
especie-color="#8B5CF6"
empresa-codigo="VRF"
temporada-codigo="2024-2025"
></my-navbar>Pasar listas (requiere JavaScript):
const navbar = document.querySelector('my-navbar');
navbar.empresas = [{ id: '1', codigo: 'VRF', nombre: 'Verfrut Chile' }];
navbar.temporadas = [{ id: '1', codigo: '2024-2025' }];
navbar.especies = [{ id: '1', descripcion: 'Uva de Mesa', codigo: 'UM', color: '#8B5CF6' }];| Prop | Tipo | Descripción |
|------|------|-------------|
| sidebar-open | boolean | Estado del sidebar |
| user-name | string | Nombre completo |
| user-initials | string | Iniciales del usuario |
| especie | string | Especie activa |
| especie-color | string | Color de la especie |
| empresa-codigo | string | Código empresa activa |
| temporada-codigo | string | Código temporada activa |
| empresas | Array | Lista de empresas |
| temporadas | Array | Lista de temporadas |
| especies | Array | Lista de especies |
| loading-empresas | boolean | Estado de carga |
| loading-temporadas | boolean | Estado de carga |
| loading-especies | boolean | Estado de carga |
| Evento | Detalle |
|--------|---------|
| sidebar-toggle | { open: boolean } |
| ctx-open | — Solicita cargar empresas y temporadas |
| ctx-apply | { empresa, temporada } |
| especie-open | — Solicita cargar especies |
| especie-confirm | { especie } |
| nav-profile | — Navegar a perfil |
| nav-logout | — Cerrar sesión |
Tokens de diseño
Variables CSS globales disponibles en todos los componentes.
/* Colores */
--color-primary /* #184288 */
--color-primary-dark /* #123470 */
--color-secondary /* #000000 */
--color-success /* #057A55 */
--color-danger /* #C81E1E */
--color-warning /* #B45309 */
/* Texto */
--color-text /* #1E293B */
--color-text-muted /* #64748B */
--color-text-white /* #FFFFFF */
/* Fondos */
--color-bg /* #FFFFFF */
--color-bg-subtle /* #F8FAFC */
/* Tipografía */
--font-family /* 'Inter', Arial, sans-serif */
--font-size-sm /* 12px */
--font-size-base /* 14px */
--font-size-lg /* 16px */
--font-size-xl /* 20px */
/* Espaciado */
--space-1 /* 4px */
--space-2 /* 8px */
--space-3 /* 12px */
--space-4 /* 16px */
--space-6 /* 24px */
/* Border radius */
--radius-sm /* 2px */
--radius-md /* 6px */
--radius-lg /* 9px */
--radius-full /* 9999px */Sobreescribir tokens en tu proyecto:
:root {
--color-primary: #FF0000;
--font-family: 'Roboto', sans-serif;
}Storybook
Para explorar y probar los componentes visualmente:
npm run storybookAbre http://localhost:6006
