@juanarenas31/metrik-ui 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +15 -0
- package/README.md +268 -0
- package/dist/index.d.ts +646 -0
- package/dist/index.js +2244 -0
- package/dist/index.js.map +1 -0
- package/dist/styles.css +57 -0
- package/dist/tailwind-preset.d.ts +13 -0
- package/dist/tailwind-preset.js +87 -0
- package/dist/tailwind-preset.js.map +1 -0
- package/dist/tokens.css +141 -0
- package/package.json +101 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Universidad Simón Bolívar · Unidad de Ciencia de los Datos
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND.
|
package/README.md
ADDED
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
# @juanarenas31/metrik-ui
|
|
2
|
+
|
|
3
|
+
> Librería de componentes React para construir **dashboards analíticos** de la Unidad de Ciencia de los Datos · Universidad Simón Bolívar.
|
|
4
|
+
|
|
5
|
+
[](#)
|
|
6
|
+
[](./LICENSE)
|
|
7
|
+
[](#)
|
|
8
|
+
|
|
9
|
+
Inspirada en shadcn/ui · Radix · Mantine. Construida sobre **React 18+ · TypeScript · TailwindCSS · Radix Primitives · CVA**.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## ✨ Características
|
|
14
|
+
|
|
15
|
+
- **22+ componentes** accesibles (ARIA · keyboard · focus visible)
|
|
16
|
+
- **Tokens semánticos** para light + dark mode con un sólo `.dark` en `<html>`
|
|
17
|
+
- **Tree-shakeable** · cada componente se importa de su sub-path
|
|
18
|
+
- **`forwardRef` + `asChild`** en todo lo interactivo
|
|
19
|
+
- **Sistema de variantes tipadas** con `class-variance-authority`
|
|
20
|
+
- **TypeScript 5** estricto · types incluidos
|
|
21
|
+
- **Preset de Tailwind** plug-and-play · mapea tokens a clases utility
|
|
22
|
+
- **Hooks reutilizables** · `useTheme`, `useDisclosure`, `useMediaQuery`, `useDebounce`, `useLocalStorage`…
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## 📦 Instalación
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
# pnpm
|
|
30
|
+
pnpm add @juanarenas31/metrik-ui
|
|
31
|
+
|
|
32
|
+
# npm
|
|
33
|
+
npm install @juanarenas31/metrik-ui
|
|
34
|
+
|
|
35
|
+
# yarn
|
|
36
|
+
yarn add @juanarenas31/metrik-ui
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
**peerDependencies** que debes tener instaladas:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
pnpm add react react-dom tailwindcss
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## 🚀 Setup en 3 pasos
|
|
48
|
+
|
|
49
|
+
### 1 · Importa los estilos base
|
|
50
|
+
|
|
51
|
+
En tu archivo de estilos global (`globals.css` o `index.css`):
|
|
52
|
+
|
|
53
|
+
```css
|
|
54
|
+
@import "@juanarenas31/metrik-ui/styles.css";
|
|
55
|
+
|
|
56
|
+
@tailwind base;
|
|
57
|
+
@tailwind components;
|
|
58
|
+
@tailwind utilities;
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
> Solo necesitas el preset si quieres usar las clases utility (`bg-primary`, `text-fg-muted`, etc.). Si prefieres usar las CSS variables directamente, importa únicamente `tokens.css`.
|
|
62
|
+
|
|
63
|
+
### 2 · Agrega el preset de Tailwind
|
|
64
|
+
|
|
65
|
+
En `tailwind.config.ts`:
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
import metrikPreset from "@juanarenas31/metrik-ui/tailwind-preset";
|
|
69
|
+
import type { Config } from "tailwindcss";
|
|
70
|
+
|
|
71
|
+
export default {
|
|
72
|
+
presets: [metrikPreset],
|
|
73
|
+
content: [
|
|
74
|
+
"./src/**/*.{ts,tsx}",
|
|
75
|
+
"./node_modules/@juanarenas31/metrik-ui/dist/**/*.js",
|
|
76
|
+
],
|
|
77
|
+
} satisfies Config;
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### 3 · Empieza a usar componentes
|
|
81
|
+
|
|
82
|
+
```tsx
|
|
83
|
+
import { Button, Card, CardHeader, CardTitle, CardContent, Badge } from "@juanarenas31/metrik-ui";
|
|
84
|
+
import { TrendingUp } from "lucide-react";
|
|
85
|
+
|
|
86
|
+
export function KPI() {
|
|
87
|
+
return (
|
|
88
|
+
<Card className="max-w-sm">
|
|
89
|
+
<CardHeader>
|
|
90
|
+
<div className="flex items-center justify-between">
|
|
91
|
+
<CardTitle>Estudiantes activos</CardTitle>
|
|
92
|
+
<Badge tone="primary" dot>+4.2%</Badge>
|
|
93
|
+
</div>
|
|
94
|
+
</CardHeader>
|
|
95
|
+
<CardContent>
|
|
96
|
+
<div className="text-3xl font-semibold tabular-nums">12 488</div>
|
|
97
|
+
<Button variant="outline" size="sm" className="mt-4" leftIcon={<TrendingUp />}>
|
|
98
|
+
Ver tendencia
|
|
99
|
+
</Button>
|
|
100
|
+
</CardContent>
|
|
101
|
+
</Card>
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## 🌗 Dark mode
|
|
109
|
+
|
|
110
|
+
```tsx
|
|
111
|
+
import { useTheme, Switch } from "@juanarenas31/metrik-ui";
|
|
112
|
+
|
|
113
|
+
export function ThemeToggle() {
|
|
114
|
+
const { resolvedTheme, toggleTheme } = useTheme();
|
|
115
|
+
return (
|
|
116
|
+
<Switch
|
|
117
|
+
checked={resolvedTheme === "dark"}
|
|
118
|
+
onCheckedChange={toggleTheme}
|
|
119
|
+
aria-label="Cambiar tema"
|
|
120
|
+
/>
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Para evitar el "flash of unstyled content" en SSR, inyecta este script en tu `<head>`:
|
|
126
|
+
|
|
127
|
+
```html
|
|
128
|
+
<script>
|
|
129
|
+
(function () {
|
|
130
|
+
var t = localStorage.getItem("metrik-theme") || "system";
|
|
131
|
+
var d = t === "dark" || (t === "system" && matchMedia("(prefers-color-scheme: dark)").matches);
|
|
132
|
+
document.documentElement.classList.toggle("dark", d);
|
|
133
|
+
document.documentElement.classList.toggle("light", !d);
|
|
134
|
+
})();
|
|
135
|
+
</script>
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## 📚 Componentes
|
|
141
|
+
|
|
142
|
+
| Componente | Sub-path | Descripción |
|
|
143
|
+
|-------------------|---------------------------------------|----------------------------------------------|
|
|
144
|
+
| `Alert` | `@juanarenas31/metrik-ui` | Avisos · info · success · warning · danger |
|
|
145
|
+
| `Avatar` | `@juanarenas31/metrik-ui` | Imagen de usuario con fallback |
|
|
146
|
+
| `Badge` | `@juanarenas31/metrik-ui` | Etiquetas pequeñas · 7 tonos · 3 tamaños |
|
|
147
|
+
| `Button` | `@juanarenas31/metrik-ui` | 6 variantes · 4 tamaños · loading · iconos |
|
|
148
|
+
| `Card` | `@juanarenas31/metrik-ui` | Compound · Header/Title/Description/Content/Footer |
|
|
149
|
+
| `Checkbox` | `@juanarenas31/metrik-ui` | Checkbox accesible · indeterminate |
|
|
150
|
+
| `Dialog` | `@juanarenas31/metrik-ui` | Modal · focus trap · scroll-lock · portal |
|
|
151
|
+
| `DropdownMenu` | `@juanarenas31/metrik-ui` | Menú con checkbox · radio · sub-menús |
|
|
152
|
+
| `Input` | `@juanarenas31/metrik-ui` | Input texto · 3 tamaños · estados error/success |
|
|
153
|
+
| `Label` | `@juanarenas31/metrik-ui` | Label semántico · required |
|
|
154
|
+
| `Popover` | `@juanarenas31/metrik-ui` | Popover con anchor opcional |
|
|
155
|
+
| `Select` | `@juanarenas31/metrik-ui` | Select accesible · grupos · keyboard nav |
|
|
156
|
+
| `Separator` | `@juanarenas31/metrik-ui` | Línea divisoria · horizontal/vertical |
|
|
157
|
+
| `Skeleton` | `@juanarenas31/metrik-ui` | Loading placeholder con shimmer |
|
|
158
|
+
| `Spinner` | `@juanarenas31/metrik-ui` | Indicador de carga · 4 tamaños |
|
|
159
|
+
| `Switch` | `@juanarenas31/metrik-ui` | Toggle on/off |
|
|
160
|
+
| `Tabs` | `@juanarenas31/metrik-ui` | Tabs · underline o pill |
|
|
161
|
+
| `Textarea` | `@juanarenas31/metrik-ui` | Textarea con estado invalid |
|
|
162
|
+
| `Tooltip` | `@juanarenas31/metrik-ui` | Tooltip · provider + content |
|
|
163
|
+
|
|
164
|
+
### Hooks
|
|
165
|
+
|
|
166
|
+
| Hook | Descripción |
|
|
167
|
+
|---------------------|---------------------------------------------------------------|
|
|
168
|
+
| `useTheme` | Light · dark · system con persistencia en localStorage |
|
|
169
|
+
| `useDisclosure` | Estado open/close para overlays |
|
|
170
|
+
| `useMediaQuery` | Suscríbete a media queries · incluye `useBreakpoint.lg()` |
|
|
171
|
+
| `useDebounce` | Valor debounced · útil para búsquedas |
|
|
172
|
+
| `useLocalStorage` | Estado sincronizado con localStorage |
|
|
173
|
+
| `useClickOutside` | Cierra paneles cuando el usuario hace click afuera |
|
|
174
|
+
| `useToggle` | Helper para booleanos |
|
|
175
|
+
|
|
176
|
+
### Utilidades
|
|
177
|
+
|
|
178
|
+
| Export | Descripción |
|
|
179
|
+
|--------------|---------------------------------------------------------------|
|
|
180
|
+
| `cn(...)` | Combina clases de Tailwind resolviendo conflictos (twMerge) |
|
|
181
|
+
| `cva` | Re-export de class-variance-authority |
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## 🎨 Tokens
|
|
186
|
+
|
|
187
|
+
Los tokens se exponen como **CSS variables**. Puedes usarlos:
|
|
188
|
+
|
|
189
|
+
- vía clases utility de Tailwind (`bg-primary`, `text-fg-muted`, `border-border`…)
|
|
190
|
+
- vía variables CSS directas (`var(--metrik-primary)`, `var(--metrik-fg)`…)
|
|
191
|
+
|
|
192
|
+
### Color · semántico
|
|
193
|
+
|
|
194
|
+
| Token | Light | Dark | Uso |
|
|
195
|
+
|--------------------|------------------|------------------|------------------------------|
|
|
196
|
+
| `--metrik-bg` | slate-50 | slate-950 | Fondo de la app |
|
|
197
|
+
| `--metrik-surface` | white | slate-900 | Cards, panels |
|
|
198
|
+
| `--metrik-surface-muted` | slate-100 | slate-800 | Hover states |
|
|
199
|
+
| `--metrik-border` | slate-200 | slate-700 | Bordes sutiles |
|
|
200
|
+
| `--metrik-fg` | slate-800 | slate-50 | Texto principal |
|
|
201
|
+
| `--metrik-fg-muted` | slate-500 | slate-400 | Texto secundario |
|
|
202
|
+
| `--metrik-primary` | teal-500 | teal-400 | CTA principal |
|
|
203
|
+
| `--metrik-accent` | coral-500 | coral-400 | Resaltar, alertas |
|
|
204
|
+
| `--metrik-success` | teal-600 | teal-400 | Estados positivos |
|
|
205
|
+
| `--metrik-danger` | coral-600 | coral-400 | Errores, destructivos |
|
|
206
|
+
| `--metrik-warning` | amber-500 | amber-300 | Advertencias |
|
|
207
|
+
|
|
208
|
+
### Radius · shadow · motion
|
|
209
|
+
|
|
210
|
+
`rounded-{sm,md,lg,xl,full}` · `shadow-{xs,sm,md,lg}` · `duration-{fast,base,slow}` · `ease-metrik`
|
|
211
|
+
|
|
212
|
+
### Z-index
|
|
213
|
+
|
|
214
|
+
`z-{sticky,dropdown,overlay,modal,toast}` · 10 · 100 · 1000 · 1100 · 1500
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## 🔧 Tree-shaking
|
|
219
|
+
|
|
220
|
+
Para máximo control sobre el bundle, importa por sub-path cuando trabajes con bundlers que no eliminan barrels eficientemente:
|
|
221
|
+
|
|
222
|
+
```ts
|
|
223
|
+
// re-export root (~ tree-shake estándar)
|
|
224
|
+
import { Button } from "@juanarenas31/metrik-ui";
|
|
225
|
+
|
|
226
|
+
// alternativa equivalente · idéntico resultado con esbuild/vite/turbopack
|
|
227
|
+
import { Button } from "@juanarenas31/metrik-ui";
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Todos los componentes están marcados como side-effect-free excepto los archivos `.css`, lo cual permite a tu bundler eliminar cualquier componente no usado.
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
## 🛠️ Desarrollo local
|
|
235
|
+
|
|
236
|
+
```bash
|
|
237
|
+
git clone <repo>
|
|
238
|
+
cd metrik-ui
|
|
239
|
+
pnpm install
|
|
240
|
+
pnpm dev # tsup watch
|
|
241
|
+
pnpm typecheck
|
|
242
|
+
pnpm lint
|
|
243
|
+
pnpm build
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
## 📐 Convenciones
|
|
249
|
+
|
|
250
|
+
- **Naming** · `PascalCase` para componentes, `camelCase + use*` para hooks
|
|
251
|
+
- **Compound components** para piezas con múltiples partes (`Card.Header`, `Dialog.Footer`…)
|
|
252
|
+
- **`asChild` prop** disponible donde tiene sentido (Button, Trigger primitives) usando `Radix Slot`
|
|
253
|
+
- **Variantes** tipadas con `cva`, exportadas como `<Component>Variants` para extender
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
## 🗺️ Roadmap
|
|
258
|
+
|
|
259
|
+
- [ ] **v0.2** · DataTable · Toast · Drawer · Slider · DatePicker · FileUpload
|
|
260
|
+
- [ ] **v0.3** · Charts (recharts wrappers) · KPIWidget · StatsCard · ActivityFeed
|
|
261
|
+
- [ ] **v0.4** · Layouts pre-armados (DashboardLayout · AuthLayout · AdminLayout)
|
|
262
|
+
- [ ] **v1.0** · Documentación interactiva (Storybook + Astro Starlight) · CI/CD · npm público
|
|
263
|
+
|
|
264
|
+
---
|
|
265
|
+
|
|
266
|
+
## 📄 Licencia
|
|
267
|
+
|
|
268
|
+
[MIT](./LICENSE) © 2026 Universidad Simón Bolívar · Unidad de Ciencia de los Datos
|