@regcheq/ui-vue-2 1.1.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/README.md ADDED
@@ -0,0 +1,334 @@
1
+ # regcheq-ui-vue-2
2
+
3
+ Librería de componentes UI de **RegCheq** para proyectos Vue 2. Documenta y distribuye los componentes del design system oficial, sincronizados directamente desde Figma.
4
+
5
+ ---
6
+
7
+ ## Tabla de contenidos
8
+
9
+ - [Descripción](#descripción)
10
+ - [Requisitos](#requisitos)
11
+ - [Instalación](#instalación)
12
+ - [Levantar Storybook](#levantar-storybook)
13
+ - [Estructura del proyecto](#estructura-del-proyecto)
14
+ - [Design Tokens](#design-tokens)
15
+ - [Colores](#colores)
16
+ - [Tipografía](#tipografía)
17
+ - [Elevaciones](#elevaciones)
18
+ - [Crear un componente](#crear-un-componente)
19
+ - [Patrón CVA](#patrón-cva)
20
+ - [Estructura de archivos](#estructura-de-archivos)
21
+ - [Storybook: convenciones de stories](#storybook-convenciones-de-stories)
22
+ - [Fuente de verdad: Figma](#fuente-de-verdad-figma)
23
+
24
+ ---
25
+
26
+ ## Descripción
27
+
28
+ Este repositorio es la **fuente de componentes UI reutilizables** para los frontends de RegCheq (`web-client`, `web-client-mx`, `web-client-proveedores`). Los tokens de diseño (colores, tipografía, sombras) están sincronizados con el design system oficial en Figma y compilados en SCSS/CSS custom properties para que sean consumibles sin dependencias adicionales en los proyectos destino.
29
+
30
+ **Stack:**
31
+
32
+ | Herramienta | Versión | Rol |
33
+ |---|---|---|
34
+ | Vue | 2.6 | Framework de componentes |
35
+ | Storybook | 7.6 | Documentación y sandbox |
36
+ | Bootstrap Vue | 2.22 | Base de componentes estructurales |
37
+ | SCSS | — | Estilos y design tokens |
38
+ | CVA | latest | Gestión de variantes de componentes |
39
+ | Inter | — | Fuente tipográfica del sistema |
40
+
41
+ ---
42
+
43
+ ## Requisitos
44
+
45
+ - Node.js `18` (ver `.nvmrc`)
46
+ - npm o yarn
47
+
48
+ ```bash
49
+ nvm use # activa Node 18 según .nvmrc
50
+ ```
51
+
52
+ ---
53
+
54
+ ## Instalación
55
+
56
+ ```bash
57
+ npm install
58
+ # o
59
+ yarn
60
+ ```
61
+
62
+ ---
63
+
64
+ ## Levantar Storybook
65
+
66
+ ```bash
67
+ npm run storybook
68
+ # o
69
+ yarn storybook
70
+ ```
71
+
72
+ Abre en `http://localhost:6006`
73
+
74
+ Para generar el build estático:
75
+
76
+ ```bash
77
+ npm run build-storybook
78
+ ```
79
+
80
+ ---
81
+
82
+ ## Estructura del proyecto
83
+
84
+ ```
85
+ regcheq-ui-vue-2/
86
+ ├── .storybook/
87
+ │ ├── main.js # Configuración de Storybook (webpack, addons, SCSS global)
88
+ │ ├── preview.js # Setup global: Bootstrap Vue, tokens, fuente Inter
89
+ │ └── manager.js # (opcional) Personalización UI de Storybook
90
+ │
91
+ └── src/
92
+ ├── assets/
93
+ │ └── scss/
94
+ │ ├── _tokens.scss # CSS custom properties (:root) — carga global
95
+ │ └── _variables.scss # Variables SCSS ($) — inyectadas en cada componente
96
+ │
97
+ ├── components/
98
+ │ ├── atoms/ # Componentes base: Button, Input, Badge, Tag...
99
+ │ │ └── [NombreComponente]/
100
+ │ │ ├── NombreComponente.vue
101
+ │ │ ├── NombreComponente.variants.js
102
+ │ │ ├── NombreComponente.scss
103
+ │ │ └── NombreComponente.stories.js
104
+ │ │
105
+ │ ├── molecules/ # Combinaciones de atoms: Card, FormField, Alert...
106
+ │ │ └── [NombreComponente]/
107
+ │ │ └── ...
108
+ │ │
109
+ │ └── organisms/ # Secciones completas: Navbar, Sidebar, DataTable...
110
+ │ └── [NombreComponente]/
111
+ │ └── ...
112
+ │
113
+ └── index.js # Entry point — exporta todos los componentes
114
+ ```
115
+
116
+ ### Jerarquía Atomic Design
117
+
118
+ | Nivel | Carpeta | Ejemplos | Title en Storybook |
119
+ |---|---|---|---|
120
+ | Atoms | `components/atoms/` | Button, Input, Badge, Icon | `Atoms/RcqButton` |
121
+ | Molecules | `components/molecules/` | Card, FormField, Alert, Tooltip | `Molecules/RcqCard` |
122
+ | Organisms | `components/organisms/` | Navbar, Sidebar, DataTable, Modal | `Organisms/RcqNavbar` |
123
+
124
+ ---
125
+
126
+ ## Design Tokens
127
+
128
+ Todos los tokens están definidos en `src/assets/scss/_tokens.scss`. Se inyectan automáticamente en todos los archivos `.scss` de los componentes, por lo que no es necesario importarlos manualmente.
129
+
130
+ Están disponibles como **CSS custom properties** (para uso inline o en cualquier contexto CSS) y como **variables SCSS** (para uso dentro de `<style lang="scss">`).
131
+
132
+ ### Colores
133
+
134
+ El sistema de colores sigue la escala `50–900` de Material Design, nombrada con el prefijo `rq-`.
135
+
136
+ | Paleta | Variable base | Uso |
137
+ |---|---|---|
138
+ | Primary | `$rq-primary` / `--rq-primary` | Color de marca principal `#040045` |
139
+ | Secondary | `$rq-secondary` / `--rq-secondary` | Color de marca secundario `#0064e0` |
140
+ | Info | `$rq-info-{n}` | Estados informativos |
141
+ | Success | `$rq-success-{n}` | Estados de éxito |
142
+ | Warning | `$rq-warning-{n}` | Estados de advertencia |
143
+ | Red | `$rq-red-{n}` | Estados de error / alerta |
144
+ | Tag | `$rq-tag-{n}` | Etiquetas, badges (fucsia) |
145
+ | Gray | `$rq-gray-{n}` | Neutros, textos, bordes |
146
+ | White | `$rq-white-{n}` | Fondos y superficies |
147
+
148
+ **Uso:**
149
+
150
+ ```scss
151
+ // SCSS variable
152
+ background: $rq-primary;
153
+ color: $rq-gray-700;
154
+ border-color: $rq-secondary-300;
155
+
156
+ // CSS custom property
157
+ background: var(--rq-primary);
158
+ color: var(--rq-gray-700);
159
+ ```
160
+
161
+ ### Tipografía
162
+
163
+ La fuente del sistema es **Inter**, cargada desde Google Fonts y aplicada globalmente a todos los componentes.
164
+
165
+ ```scss
166
+ // SCSS
167
+ font-family: $rq-font-family;
168
+
169
+ // CSS custom property
170
+ font-family: var(--rq-font-family);
171
+ ```
172
+
173
+ No es necesario declarar la fuente en los componentes — se hereda por defecto.
174
+
175
+ ### Elevaciones
176
+
177
+ 5 niveles de sombra (`L0`–`L4`) para representar profundidad en el eje Z.
178
+
179
+ | Variable | Valor | Uso recomendado |
180
+ |---|---|---|
181
+ | `$rq-elevation-0` | `none` | Flat. Cards y contenedores base |
182
+ | `$rq-elevation-1` | `0px 1px 1px` | Widgets, Top Nav en hover |
183
+ | `$rq-elevation-2` | `0px 2px 2px` | Cards en hover |
184
+ | `$rq-elevation-3` | `0px 4px 12px` | Dropdowns, tooltips, menús |
185
+ | `$rq-elevation-4` | `0px 8px 24px` | Modales, diálogos, notificaciones flotantes |
186
+
187
+ ```scss
188
+ // SCSS
189
+ box-shadow: $rq-elevation-3;
190
+
191
+ // CSS custom property
192
+ box-shadow: var(--rq-elevation-3);
193
+ ```
194
+
195
+ ---
196
+
197
+ ## Crear un componente
198
+
199
+ ### Patrón CVA
200
+
201
+ Este proyecto usa **CVA (Class Variance Authority)** para gestionar variantes de componentes. CVA genera el string de clases CSS según las props recibidas, eliminando lógica condicional sucia dentro del template.
202
+
203
+ ```
204
+ prop variant="primary" → CVA → "rcq-btn rcq-btn--primary rcq-btn--md"
205
+ ↓
206
+ SCSS define qué hace cada clase usando tokens
207
+ ```
208
+
209
+ ### Estructura de archivos
210
+
211
+ Cada componente vive en su propio directorio con 4 archivos:
212
+
213
+ #### `NombreComponente.variants.js`
214
+
215
+ Define el mapeo entre props y clases CSS. Es la única fuente de verdad de las variantes.
216
+
217
+ ```js
218
+ import { cva } from 'class-variance-authority'
219
+
220
+ export const componenteVariants = cva('rcq-componente', {
221
+ variants: {
222
+ variant: {
223
+ primary: 'rcq-componente--primary',
224
+ secondary: 'rcq-componente--secondary',
225
+ },
226
+ size: {
227
+ sm: 'rcq-componente--sm',
228
+ md: 'rcq-componente--md',
229
+ lg: 'rcq-componente--lg',
230
+ },
231
+ },
232
+ defaultVariants: {
233
+ variant: 'primary',
234
+ size: 'md',
235
+ },
236
+ })
237
+ ```
238
+
239
+ #### `NombreComponente.scss`
240
+
241
+ Define los estilos de cada clase usando los tokens del sistema. Nunca usar valores hardcodeados.
242
+
243
+ ```scss
244
+ .rcq-componente {
245
+ font-family: $rq-font-family;
246
+
247
+ &--primary {
248
+ background: $rq-primary;
249
+ color: #ffffff;
250
+ &:hover:not(:disabled) { background: $rq-primary-400; }
251
+ }
252
+
253
+ &--sm { height: 32px; font-size: 12px; }
254
+ &--md { height: 40px; font-size: 14px; }
255
+ &--lg { height: 48px; font-size: 16px; }
256
+ }
257
+ ```
258
+
259
+ #### `NombreComponente.vue`
260
+
261
+ El componente llama a CVA en un `computed` y aplica el resultado con `:class`. Sin lógica de clases en el template.
262
+
263
+ ```vue
264
+ <template>
265
+ <div :class="classes">
266
+ <slot />
267
+ </div>
268
+ </template>
269
+
270
+ <script>
271
+ import { componenteVariants } from './NombreComponente.variants'
272
+ import './NombreComponente.scss'
273
+
274
+ export default {
275
+ name: 'NombreComponente',
276
+ props: {
277
+ variant: { type: String, default: 'primary' },
278
+ size: { type: String, default: 'md' },
279
+ },
280
+ computed: {
281
+ classes() {
282
+ return componenteVariants({ variant: this.variant, size: this.size })
283
+ },
284
+ },
285
+ }
286
+ </script>
287
+ ```
288
+
289
+ #### `NombreComponente.stories.js`
290
+
291
+ Documenta el componente en Storybook con controles interactivos y todas las variantes.
292
+
293
+ ```js
294
+ import NombreComponente from './NombreComponente.vue'
295
+
296
+ export default {
297
+ title: 'Atoms/NombreComponente', // o Molecules/ u Organisms/ según corresponda
298
+ component: NombreComponente,
299
+ argTypes: {
300
+ variant: { control: 'select', options: ['primary', 'secondary'] },
301
+ size: { control: 'select', options: ['sm', 'md', 'lg'] },
302
+ },
303
+ }
304
+
305
+ const Template = (args, { argTypes }) => ({
306
+ components: { NombreComponente },
307
+ props: Object.keys(argTypes),
308
+ template: '<NombreComponente v-bind="$props"><slot>Contenido</slot></NombreComponente>',
309
+ })
310
+
311
+ export const Default = Template.bind({})
312
+ Default.args = { variant: 'primary', size: 'md' }
313
+
314
+ export const AllVariants = () => ({
315
+ components: { NombreComponente },
316
+ template: `
317
+ <div style="display:flex; gap:12px;">
318
+ <NombreComponente variant="primary">Primary</NombreComponente>
319
+ <NombreComponente variant="secondary">Secondary</NombreComponente>
320
+ </div>
321
+ `,
322
+ })
323
+ ```
324
+
325
+ ---
326
+
327
+ ## Storybook: convenciones de stories
328
+
329
+ - Cada componente expone al menos 3 stories: `Default`, `AllVariants` y `AllSizes`
330
+ - Agregar story `Disabled` cuando el componente tenga estado deshabilitado
331
+ - Usar `title: 'Atoms/NombreComponente'`, `'Molecules/...'` u `'Organisms/...'` según el nivel
332
+ - Los controles (`argTypes`) deben cubrir todas las props públicas
333
+
334
+ ---
package/dist/index.css ADDED
@@ -0,0 +1,311 @@
1
+ .rq-button {
2
+ display: inline-flex;
3
+ align-items: center;
4
+ justify-content: center;
5
+ gap: 8px;
6
+ border: 1px solid transparent;
7
+ border-radius: 8px;
8
+ font-family: "Inter", sans-serif;
9
+ font-weight: 500;
10
+ cursor: pointer;
11
+ white-space: nowrap;
12
+ text-decoration: none;
13
+ transition: background 200ms ease, color 200ms ease, border-color 200ms ease, box-shadow 200ms ease;
14
+ }
15
+ .rq-button:disabled, .rq-button[disabled] {
16
+ cursor: not-allowed;
17
+ pointer-events: none;
18
+ }
19
+ .rq-button:focus-visible {
20
+ outline: 2px solid #5497ea;
21
+ outline-offset: 2px;
22
+ }
23
+ .rq-button--primary {
24
+ background: #0064e0;
25
+ color: #ffffff;
26
+ border: 1px solid transparent;
27
+ }
28
+ .rq-button--primary:hover:not(:disabled):not([disabled]) {
29
+ background: #3383e6;
30
+ box-shadow: 0px 2px 2px 0px rgba(15, 15, 21, 0.1);
31
+ }
32
+ .rq-button--primary:active:not(:disabled):not([disabled]) {
33
+ background: #5497ea;
34
+ border-color: #3383e6;
35
+ box-shadow: none;
36
+ }
37
+ .rq-button--primary:disabled, .rq-button--primary[disabled] {
38
+ background: #e6f0fc;
39
+ color: #b0cff5;
40
+ border-color: transparent;
41
+ }
42
+ .rq-button--outline {
43
+ background: transparent;
44
+ color: #005bcc;
45
+ border: 1px solid #0064e0;
46
+ }
47
+ .rq-button--outline:hover:not(:disabled):not([disabled]) {
48
+ background: #e6f0fc;
49
+ border-color: #3383e6;
50
+ color: #3383e6;
51
+ box-shadow: 0px 2px 2px 0px rgba(15, 15, 21, 0.1);
52
+ }
53
+ .rq-button--outline:active:not(:disabled):not([disabled]) {
54
+ background: #b0cff5;
55
+ border-color: #5497ea;
56
+ color: #3383e6;
57
+ box-shadow: 0px 1px 1px 0px rgba(15, 15, 21, 0.1);
58
+ }
59
+ .rq-button--outline:disabled, .rq-button--outline[disabled] {
60
+ background: transparent;
61
+ border-color: #b0cff5;
62
+ color: #8ab8f1;
63
+ }
64
+ .rq-button--texted {
65
+ background: transparent;
66
+ color: #0064e0;
67
+ border: 1px solid transparent;
68
+ }
69
+ .rq-button--texted:hover:not(:disabled):not([disabled]) {
70
+ color: #3383e6;
71
+ }
72
+ .rq-button--texted:active:not(:disabled):not([disabled]) {
73
+ color: #5497ea;
74
+ }
75
+ .rq-button--texted:disabled, .rq-button--texted[disabled] {
76
+ color: #b0cff5;
77
+ }
78
+ .rq-button--sm {
79
+ height: 32px;
80
+ padding: 4px 8px;
81
+ font-size: 12px;
82
+ }
83
+ .rq-button--md {
84
+ height: 40px;
85
+ padding: 8px 16px;
86
+ font-size: 14px;
87
+ }
88
+ .rq-button--lg {
89
+ height: 48px;
90
+ padding: 10px 20px;
91
+ font-size: 16px;
92
+ }
93
+ .rq-button--xl {
94
+ height: 56px;
95
+ padding: 12px 24px;
96
+ font-size: 20px;
97
+ }
98
+ .rq-button--icon-only.rq-button--sm {
99
+ width: 32px;
100
+ padding: 0;
101
+ }
102
+ .rq-button--icon-only.rq-button--md {
103
+ width: 40px;
104
+ padding: 0;
105
+ }
106
+ .rq-button--icon-only.rq-button--lg {
107
+ width: 48px;
108
+ padding: 0;
109
+ }
110
+ .rq-button--icon-only.rq-button--xl {
111
+ width: 56px;
112
+ padding: 0;
113
+ }
114
+ .rq-input {
115
+ display: flex;
116
+ flex-direction: column;
117
+ gap: 0;
118
+ width: 100%;
119
+ font-family: "Inter", sans-serif;
120
+ }
121
+ .rq-input__label {
122
+ display: flex;
123
+ align-items: center;
124
+ gap: 4px;
125
+ margin-bottom: 0;
126
+ font-size: 12px;
127
+ font-weight: 500;
128
+ line-height: 16px;
129
+ color: #3f3f46;
130
+ }
131
+ .rq-input__label--error {
132
+ color: #ff2414;
133
+ }
134
+ .rq-input__optional {
135
+ font-size: 12px;
136
+ font-weight: 400;
137
+ line-height: 20px;
138
+ color: #a1a1aa;
139
+ }
140
+ .rq-input__field {
141
+ display: flex;
142
+ align-items: center;
143
+ width: 100%;
144
+ border: 1px solid #3f3f46;
145
+ border-radius: 8px;
146
+ background: #ffffff;
147
+ transition: border-color 200ms ease, box-shadow 200ms ease;
148
+ }
149
+ .rq-input__field:hover:not(.rq-input__field--disabled):not(.rq-input__field--readonly) {
150
+ border-color: #18181b;
151
+ }
152
+ .rq-input__field:focus-within:not(.rq-input__field--disabled):not(.rq-input__field--readonly) {
153
+ border-color: #0064e0;
154
+ box-shadow: 0 0 0 2px #b0cff5;
155
+ }
156
+ .rq-input__field--disabled {
157
+ background: #f4f4f5;
158
+ border-color: #d4d4d8;
159
+ cursor: not-allowed;
160
+ }
161
+ .rq-input__field--readonly {
162
+ background: #e4e4e7;
163
+ border-color: #d4d4d8;
164
+ }
165
+ .rq-input__field--error {
166
+ border-color: #ff2414;
167
+ }
168
+ .rq-input__field--error:hover:not(.rq-input__field--disabled) {
169
+ border-color: #d71709;
170
+ }
171
+ .rq-input__field--error:focus-within:not(.rq-input__field--disabled) {
172
+ border-color: #ff2414;
173
+ box-shadow: 0 0 0 2px #fed0cd;
174
+ }
175
+ .rq-input__native {
176
+ flex: 1;
177
+ min-width: 0;
178
+ border: none;
179
+ outline: none;
180
+ background: transparent;
181
+ font-family: "Inter", sans-serif;
182
+ font-size: 14px;
183
+ font-weight: 400;
184
+ line-height: 20px;
185
+ color: #18181b;
186
+ }
187
+ .rq-input__native::placeholder {
188
+ color: #a1a1aa;
189
+ }
190
+ .rq-input__native:disabled {
191
+ cursor: not-allowed;
192
+ color: #a1a1aa;
193
+ }
194
+ .rq-input__native[readonly] {
195
+ cursor: default;
196
+ color: #52525b;
197
+ }
198
+ .rq-input__icon {
199
+ display: flex;
200
+ align-items: center;
201
+ flex-shrink: 0;
202
+ color: #a1a1aa;
203
+ }
204
+ .rq-input__icon--error {
205
+ color: #ff2414;
206
+ }
207
+ .rq-input__icon--disabled {
208
+ color: #d4d4d8;
209
+ }
210
+ .rq-input__icon--readonly {
211
+ color: #71717a;
212
+ }
213
+ .rq-input__icon svg {
214
+ display: block;
215
+ }
216
+ .rq-input__label-tooltip {
217
+ position: relative;
218
+ display: inline-flex;
219
+ align-items: center;
220
+ cursor: help;
221
+ color: #a1a1aa;
222
+ margin-left: 2px;
223
+ }
224
+ .rq-input__label-tooltip:hover {
225
+ color: #52525b;
226
+ }
227
+ .rq-input__label-tooltip::after {
228
+ content: attr(data-tooltip);
229
+ position: absolute;
230
+ bottom: calc(100% + 6px);
231
+ left: 50%;
232
+ transform: translateX(-50%);
233
+ background: #18181b;
234
+ color: #ffffff;
235
+ font-family: "Inter", sans-serif;
236
+ font-size: 12px;
237
+ line-height: 16px;
238
+ font-weight: 400;
239
+ padding: 4px 8px;
240
+ border-radius: 4px;
241
+ white-space: nowrap;
242
+ pointer-events: none;
243
+ opacity: 0;
244
+ transition: opacity 200ms ease;
245
+ z-index: 10;
246
+ }
247
+ .rq-input__label-tooltip:hover::after {
248
+ opacity: 1;
249
+ }
250
+ .rq-input__footer {
251
+ display: flex;
252
+ justify-content: space-between;
253
+ align-items: flex-start;
254
+ gap: 8px;
255
+ }
256
+ .rq-input__helper {
257
+ flex: 1;
258
+ font-size: 12px;
259
+ font-weight: 400;
260
+ line-height: 20px;
261
+ color: #a1a1aa;
262
+ }
263
+ .rq-input__error-text {
264
+ flex: 1;
265
+ font-size: 12px;
266
+ font-weight: 400;
267
+ line-height: 20px;
268
+ color: #ff2414;
269
+ }
270
+ .rq-input__counter {
271
+ font-size: 12px;
272
+ font-weight: 400;
273
+ line-height: 20px;
274
+ color: #a1a1aa;
275
+ white-space: nowrap;
276
+ }
277
+ .rq-input__counter--error {
278
+ color: #ff2414;
279
+ }
280
+ .rq-input--sm .rq-input__field {
281
+ min-height: 32px;
282
+ padding: 0 8px;
283
+ }
284
+ .rq-input--sm .rq-input__native {
285
+ font-size: 12px;
286
+ line-height: 20px;
287
+ }
288
+ .rq-input--md .rq-input__field {
289
+ min-height: 40px;
290
+ padding: 0 12px;
291
+ }
292
+ .rq-input--md .rq-input__native {
293
+ font-size: 14px;
294
+ line-height: 20px;
295
+ }
296
+ .rq-input--lg .rq-input__field {
297
+ min-height: 48px;
298
+ padding: 0 16px;
299
+ }
300
+ .rq-input--lg .rq-input__native {
301
+ font-size: 14px;
302
+ line-height: 20px;
303
+ }
304
+ .rq-input--xl .rq-input__field {
305
+ min-height: 56px;
306
+ padding: 0 20px;
307
+ }
308
+ .rq-input--xl .rq-input__native {
309
+ font-size: 16px;
310
+ line-height: 24px;
311
+ }