shirkasoft-ui-components 1.0.23 → 1.0.24

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 CHANGED
@@ -1,22 +1,29 @@
1
1
  # Shirkasoft UI Components
2
2
 
3
3
  <p align="center">
4
- Creado por <a href="https://shirkasoft.com" target="_blank"><strong>Shirkasoft</strong></a>
4
+ <a href="https://github.com/Ansem13/shirkasoft-ui-components/releases"><img src="https://img.shields.io/npm/v/shirkasoft-ui-components?style=flat&logo=npm&label=version" alt="npm version" /></a>
5
+ <a href="https://github.com/Ansem13/shirkasoft-ui-components/issues"><img src="https://img.shields.io/github/issues/Ansem13/shirkasoft-ui-components?style=flat&logo=github" alt="GitHub issues" /></a>
6
+ <a href="https://github.com/Ansem13/shirkasoft-ui-components"><img src="https://img.shields.io/badge/source-github-181717?style=flat&logo=github" alt="GitHub source" /></a>
7
+ <a href="https://ansem13.github.io/shirkasoft-ui-components/"><img src="https://img.shields.io/badge/showcase-online-10b981?style=flat&logo=githubpages" alt="Showcase" /></a>
5
8
  </p>
6
9
 
7
- Librería de componentes UI para Angular 21+ con **Tailwind CSS**, **standalone components**, señales (Angular signals) y soporte de **tema claro/oscuro** mediante CSS custom properties.
10
+ <p align="center">
11
+ Created by <a href="https://shirkasoft.com" target="_blank"><strong>Shirkasoft</strong></a>
12
+ </p>
13
+
14
+ Angular 21+ UI components library built with **Tailwind CSS**, **standalone components**, **Angular signals**, and **light/dark theme** support via CSS custom properties.
8
15
 
9
- ## Instalación
16
+ ## Installation
10
17
 
11
18
  ```bash
12
19
  pnpm add shirkasoft-ui-components
13
- # o
20
+ # or
14
21
  npm install shirkasoft-ui-components
15
22
  ```
16
23
 
17
- ### Dependencias pares
24
+ ### Peer Dependencies
18
25
 
19
- | Paquete | Versión mínima |
26
+ | Package | Minimum version |
20
27
  |---|---|
21
28
  | @angular/common | ^21.2.0 |
22
29
  | @angular/core | ^21.2.0 |
@@ -26,15 +33,15 @@ npm install shirkasoft-ui-components
26
33
  | @lucide/angular | ^1.17.0 |
27
34
  | chart.js | ^4.4.0 |
28
35
 
29
- ## Configuración del tema
36
+ ## Theme Setup
30
37
 
31
- Importar el tema en `styles.css` global:
38
+ Import the theme in your global `styles.css`:
32
39
 
33
40
  ```css
34
41
  @import '@shirkasoft/ui-components/theme.css';
35
42
  ```
36
43
 
37
- El tema se basa en variables CSS `--shk-*`. Para personalizar colores, sobrescribí las variables en `:root` (modo claro) y `.dark` (modo oscuro):
44
+ The theme is based on `--shk-*` CSS variables. To customize colors, override the variables in `:root` (light mode) and `.dark` (dark mode):
38
45
 
39
46
  ```css
40
47
  :root {
@@ -46,7 +53,7 @@ El tema se basa en variables CSS `--shk-*`. Para personalizar colores, sobrescri
46
53
 
47
54
  ### Dark mode
48
55
 
49
- Agregá la clase `dark` al elemento `<html>` para activar el modo oscuro.
56
+ Add the `dark` class to the `<html>` element to enable dark mode.
50
57
 
51
58
  ```typescript
52
59
  document.documentElement.classList.toggle('dark');
@@ -54,9 +61,9 @@ document.documentElement.classList.toggle('dark');
54
61
 
55
62
  ---
56
63
 
57
- ## Componentes
64
+ ## Components
58
65
 
59
- | # | Componente | Selector | ControlValueAccessor | Servicio asociado |
66
+ | # | Component | Selector | ControlValueAccessor | Associated service |
60
67
  |---|---|---|---|---|
61
68
  | 1 | TextField | `shk-text-field` | ✅ | — |
62
69
  | 2 | Toggle | `shk-toggle` | ✅ | — |
@@ -75,23 +82,23 @@ document.documentElement.classList.toggle('dark');
75
82
 
76
83
  ### TextField (`shk-text-field`)
77
84
 
78
- Campo de texto con soporte para input, textarea, password, búsqueda, formato de tarjeta de crédito, y validación de errores. Implementa `ControlValueAccessor`.
85
+ Text input with support for input, textarea, password, search, credit card formatting, and error validation. Implements `ControlValueAccessor`.
79
86
 
80
87
  ```html
81
88
  <shk-text-field
82
- [label]="'Nombre'"
89
+ [label]="'Name'"
83
90
  [control]="myControl"
84
91
  [type]="'text'"
85
- [placeholder]="'Ingresá tu nombre'"
92
+ [placeholder]="'Enter your name'"
86
93
  [disabled]="false"
87
- [errorMessages]="{ required: 'Campo obligatorio' }"
94
+ [errorMessages]="{ required: 'This field is required' }"
88
95
  [isTextArea]="false"
89
96
  [searchMode]="false"
90
97
  (searchButtonClick)="onSearch()"
91
98
  />
92
99
  ```
93
100
 
94
- | Input | Tipo | Default |
101
+ | Input | Type | Default |
95
102
  |---|---|---|
96
103
  | `id` | `string` | `''` |
97
104
  | `label` | `string` | `''` |
@@ -114,30 +121,30 @@ Campo de texto con soporte para input, textarea, password, búsqueda, formato de
114
121
  | `displayValue` | `string` | `''` |
115
122
  | `autocomplete` | `string` | `'new-password'` |
116
123
 
117
- > También expone `inputElement` y `textareaElement` como `viewChild`.
124
+ > Also exposes `inputElement` and `textareaElement` as `viewChild`.
118
125
 
119
126
  ---
120
127
 
121
128
  ### Toggle (`shk-toggle`)
122
129
 
123
- Switch o checkbox. Implementa `ControlValueAccessor`.
130
+ Switch or checkbox. Implements `ControlValueAccessor`.
124
131
 
125
132
  ```html
126
133
  <shk-toggle
127
- [label]="'Activar notificaciones'"
134
+ [label]="'Enable notifications'"
128
135
  [mode]="'toggle'"
129
136
  [(checked)]="isChecked"
130
137
  (checkedChange)="onChange($event)"
131
138
  />
132
139
  ```
133
140
 
134
- | Input | Tipo | Default |
141
+ | Input | Type | Default |
135
142
  |---|---|---|
136
143
  | `label` | `string` | `''` |
137
144
  | `mode` | `'toggle' \| 'checkbox'` | `'toggle'` |
138
145
  | `checked` | `boolean` | `undefined` |
139
146
 
140
- | Output | Tipo |
147
+ | Output | Type |
141
148
  |---|---|
142
149
  | `checkedChange` | `boolean` |
143
150
 
@@ -145,19 +152,19 @@ Switch o checkbox. Implementa `ControlValueAccessor`.
145
152
 
146
153
  ### Select (`shk-select`)
147
154
 
148
- Dropdown seleccionable con búsqueda, paginación, selección múltiple, opciones personalizadas y validación. Implementa `ControlValueAccessor`.
155
+ Searchable dropdown with pagination, multiple selection, custom options, and validation. Implements `ControlValueAccessor`.
149
156
 
150
157
  ```html
151
158
  <shk-select
152
159
  [options]="options"
153
- [label]="'País'"
160
+ [label]="'Country'"
154
161
  [control]="countryControl"
155
162
  [multiple]="false"
156
163
  [isSearchable]="true"
157
164
  [usePagination]="true"
158
165
  [itemsPerPage]="10"
159
166
  [allowCustomEntries]="false"
160
- [placeholder]="'Seleccionar...'"
167
+ [placeholder]="'Select...'"
161
168
  [isLoading]="false"
162
169
  (selectionChange)="onSelect($event)"
163
170
  (search)="onSearch($event)"
@@ -172,11 +179,11 @@ interface SelectOption {
172
179
  }
173
180
  ```
174
181
 
175
- | Input | Tipo | Default |
182
+ | Input | Type | Default |
176
183
  |---|---|---|
177
184
  | `options` | `SelectOption[]` | `[]` |
178
185
  | `label` | `string` | `undefined` |
179
- | `placeholder` | `string` | `'Seleccionar...'` |
186
+ | `placeholder` | `string` | `'Select...'` |
180
187
  | `disabled` | `boolean` | `false` |
181
188
  | `control` | `FormControl` | `undefined` |
182
189
  | `formGroup` | `FormGroup` | `undefined` |
@@ -192,9 +199,9 @@ interface SelectOption {
192
199
  | `showEmptyOption` | `boolean` | `true` |
193
200
  | `emptyMessageKey` | `string` | `'common.no_records'` |
194
201
  | `validCombinations` | `string[][]` | `undefined` |
195
- | `errorMessages` | `{ [key: string]: string }` | `{ required: 'Este campo es requerido' }` |
202
+ | `errorMessages` | `{ [key: string]: string }` | `{ required: 'This field is required' }` |
196
203
 
197
- | Output | Tipo |
204
+ | Output | Type |
198
205
  |---|---|
199
206
  | `selectionChange` | `any` |
200
207
  | `search` | `string` |
@@ -203,11 +210,11 @@ interface SelectOption {
203
210
 
204
211
  ### DatePicker (`shk-date-picker`)
205
212
 
206
- Selector de fecha, mes o año. Implementa `ControlValueAccessor`. Soporta formatos `'date'`, `'month'`, `'year'`.
213
+ Date, month, or year picker. Implements `ControlValueAccessor`. Supports `'date'`, `'month'`, `'year'` views.
207
214
 
208
215
  ```html
209
216
  <shk-date-picker
210
- [label]="'Fecha de inicio'"
217
+ [label]="'Start date'"
211
218
  [control]="dateControl"
212
219
  [view]="'date'"
213
220
  [locale]="'es'"
@@ -216,7 +223,7 @@ Selector de fecha, mes o año. Implementa `ControlValueAccessor`. Soporta format
216
223
  />
217
224
  ```
218
225
 
219
- | Input | Tipo | Default |
226
+ | Input | Type | Default |
220
227
  |---|---|---|
221
228
  | `id` | `string` | `''` |
222
229
  | `label` | `string` | `''` |
@@ -231,31 +238,31 @@ Selector de fecha, mes o año. Implementa `ControlValueAccessor`. Soporta format
231
238
  | `maxValue` | `string` | `''` |
232
239
  | `locale` | `string` | `'es'` |
233
240
 
234
- **Valor devuelto** según el `view`:
241
+ **Return value** depends on `view`:
235
242
  - `'date'` → `'YYYY-MM-DD'`
236
243
  - `'month'` → `'YYYY-MM'`
237
244
  - `'year'` → `'YYYY'`
238
245
 
239
246
  ---
240
247
 
241
- ### PriceInput (`shk-price-input`
248
+ ### PriceInput (`shk-price-input`)
242
249
 
243
- Input numérico con toggle entre modo monto (`$`) y porcentaje (`%`). Usa `model()` para doble vinculación.
250
+ Numeric input with toggle between amount (`$`) and percentage (`%`). Uses Angular `model()` for two-way binding.
244
251
 
245
252
  ```html
246
253
  <shk-price-input
247
254
  [(value)]="price"
248
255
  [(isPercentage)]="isPercent"
249
- [label]="'Precio'"
256
+ [label]="'Price'"
250
257
  [min]="0"
251
258
  [max]="10000"
252
259
  [required]="true"
253
260
  [showError]="hasError"
254
- [errorMessage]="'Valor inválido'"
261
+ [errorMessage]="'Invalid value'"
255
262
  />
256
263
  ```
257
264
 
258
- | Input | Tipo | Default |
265
+ | Input | Type | Default |
259
266
  |---|---|---|
260
267
  | `value` | `model<number>` | **required** |
261
268
  | `isPercentage` | `model<boolean>` | `false` |
@@ -272,11 +279,11 @@ Input numérico con toggle entre modo monto (`$`) y porcentaje (`%`). Usa `model
272
279
 
273
280
  ### FileUpload (`shk-file-upload`)
274
281
 
275
- Subida de archivos con arrastrar y soltar, vista previa de imágenes, validación de tipo y tamaño.
282
+ File upload with drag & drop, image preview, file type and size validation.
276
283
 
277
284
  ```html
278
285
  <shk-file-upload
279
- [label]="'Subí tu foto'"
286
+ [label]="'Upload your photo'"
280
287
  [accept]="'image/*'"
281
288
  [maxFileSize]="5 * 1024 * 1024"
282
289
  [maxFiles]="3"
@@ -295,18 +302,18 @@ interface FileUploadError {
295
302
  }
296
303
  ```
297
304
 
298
- | Input | Tipo | Default |
305
+ | Input | Type | Default |
299
306
  |---|---|---|
300
- | `label` | `string` | `'Cargar archivo'` |
307
+ | `label` | `string` | `'Upload file'` |
301
308
  | `accept` | `string` | `'image/*'` |
302
309
  | `maxFileSize` | `number` | `2 * 1024 * 1024` (2MB) |
303
- | `maxFiles` | `number` | `0` (sin límite) |
310
+ | `maxFiles` | `number` | `0` (unlimited) |
304
311
  | `multiple` | `boolean` | `false` |
305
- | `fileUploadText` | `string` | `'Seleccionar archivo'` |
306
- | `changeFilesText` | `string` | `'Cambiar archivos'` |
312
+ | `fileUploadText` | `string` | `'Select file'` |
313
+ | `changeFilesText` | `string` | `'Change files'` |
307
314
  | `fileRecommendation` | `string` | `''` |
308
315
 
309
- | Output | Tipo |
316
+ | Output | Type |
310
317
  |---|---|
311
318
  | `fileSelected` | `File[]` |
312
319
  | `fileRemoved` | `void` |
@@ -317,7 +324,7 @@ interface FileUploadError {
317
324
 
318
325
  ### Table (`shk-table`)
319
326
 
320
- Tabla de datos con ordenamiento, filtros por columna, búsqueda global, paginación, acciones por fila y acciones de cabecera. Soporta modo cliente y servidor.
327
+ Data table with sorting, column filters, global search, pagination, row actions, and header actions. Supports client and server-side mode.
321
328
 
322
329
  ```html
323
330
  <shk-table
@@ -384,7 +391,7 @@ interface FilterChangeEvent {
384
391
  }
385
392
  ```
386
393
 
387
- | Input | Tipo | Default |
394
+ | Input | Type | Default |
388
395
  |---|---|---|
389
396
  | `data` | `any[]` | `[]` |
390
397
  | `columns` | `Column[]` | `[]` |
@@ -406,7 +413,7 @@ interface FilterChangeEvent {
406
413
  | `activeFilter` | `string` | `''` |
407
414
  | `customTemplates` | `{ [key: string]: any }` | `{}` |
408
415
 
409
- | Output | Tipo |
416
+ | Output | Type |
410
417
  |---|---|
411
418
  | `pageChange` | `PageChangeEvent` |
412
419
  | `filterChange` | `FilterChangeEvent` |
@@ -418,7 +425,7 @@ interface FilterChangeEvent {
418
425
 
419
426
  ### Modal (`shk-modal`)
420
427
 
421
- Modal dinámico que carga componentes a partir de un `ModalService`. Soporta formularios, expansión a pantalla completa y botones de aceptar/cancelar.
428
+ Dynamic modal that loads components via `ModalService`. Supports forms, fullscreen expansion, and accept/cancel buttons.
422
429
 
423
430
  ```html
424
431
  <shk-modal />
@@ -438,19 +445,19 @@ interface ModalConfig {
438
445
  }
439
446
  ```
440
447
 
441
- El componente inyectado **debe** exponer:
442
- - `form?: FormGroup` (opcional, para validación)
443
- - `onSubmit()` → llama `submitSuccess` o `submitError`
444
- - `submitSuccess?: EventEmitter<void>` (opcional)
445
- - `submitError?: EventEmitter<void>` (opcional)
446
- - `handleCancel?: () => void` (opcional)
448
+ The injected component **must** expose:
449
+ - `form?: FormGroup` (optional, for validation)
450
+ - `onSubmit()` → calls `submitSuccess` or `submitError`
451
+ - `submitSuccess?: EventEmitter<void>` (optional)
452
+ - `submitError?: EventEmitter<void>` (optional)
453
+ - `handleCancel?: () => void` (optional)
447
454
 
448
455
  ```typescript
449
456
  constructor(private modalSrv: ModalService) {}
450
457
 
451
458
  openModal() {
452
459
  this.modalSrv.open({
453
- title: 'Editar usuario',
460
+ title: 'Edit user',
454
461
  component: EditUserComponent,
455
462
  data: { userId: 123 },
456
463
  width: '600px',
@@ -464,20 +471,20 @@ closeModal() {
464
471
  }
465
472
  ```
466
473
 
467
- **Métodos del servicio:**
474
+ **Service methods:**
468
475
 
469
- | Método | Descripción |
476
+ | Method | Description |
470
477
  |---|---|
471
- | `open(config: ModalConfig)` | Abre un nuevo modal |
472
- | `close()` | Cierra el modal actual |
473
- | `accept()` | Cierra el modal (alias de close) |
474
- | `clear()` | Cierra todos los modales |
478
+ | `open(config: ModalConfig)` | Opens a new modal |
479
+ | `close()` | Closes the current modal |
480
+ | `accept()` | Closes the modal (alias for close) |
481
+ | `clear()` | Closes all modals |
475
482
 
476
483
  ---
477
484
 
478
485
  ### Notification (`shk-notification`)
479
486
 
480
- Sistema de notificaciones toast con posiciones configurables y barra de progreso opcional.
487
+ Toast notification system with configurable positions and optional progress bar.
481
488
 
482
489
  ```html
483
490
  <shk-notification position="right-top" />
@@ -498,7 +505,7 @@ constructor(private notifSrv: NotificationService) {}
498
505
 
499
506
  showNotif() {
500
507
  const id = this.notifSrv.addNotification(
501
- 'Operación exitosa',
508
+ 'Operation successful',
502
509
  'success',
503
510
  false, // showProgress
504
511
  3000 // duration (ms)
@@ -510,28 +517,28 @@ updateProgress(id: string, progress: number) {
510
517
  }
511
518
  ```
512
519
 
513
- | Input | Tipo | Default |
520
+ | Input | Type | Default |
514
521
  |---|---|---|
515
522
  | `position` | `'center-top' \| 'right-top' \| 'left-top'` | `'center-top'` |
516
523
 
517
- **Métodos del servicio:**
524
+ **Service methods:**
518
525
 
519
- | Método | Descripción |
526
+ | Method | Description |
520
527
  |---|---|
521
- | `addNotification(msg, type?, showProgress?, duration?)` | Agrega una notificación |
522
- | `updateProgress(id, progress)` | Actualiza la barra de progreso |
523
- | `removeNotification(notification)` | Elimina una notificación |
524
- | `removeNotificationById(id)` | Elimina por ID |
525
- | `clearAll()` | Elimina todas las notificaciones |
528
+ | `addNotification(msg, type?, showProgress?, duration?)` | Adds a notification |
529
+ | `updateProgress(id, progress)` | Updates the progress bar |
530
+ | `removeNotification(notification)` | Removes a notification |
531
+ | `removeNotificationById(id)` | Removes by ID |
532
+ | `clearAll()` | Removes all notifications |
526
533
 
527
534
  ---
528
535
 
529
536
  ### ConfirmDialog (`shk-confirm-dialog`)
530
537
 
531
- Diálogo de confirmación programático. Se usa a través del `ConfirmDialogService` que lo crea dinámicamente.
538
+ Programmatic confirmation dialog. Used via `ConfirmDialogService` which dynamically creates it.
532
539
 
533
540
  ```html
534
- <!-- No hace falta agregarlo al template -->
541
+ <!-- No need to add it to the template -->
535
542
  ```
536
543
 
537
544
  ```typescript
@@ -552,30 +559,30 @@ constructor(private confirmSrv: ConfirmDialogService) {}
552
559
 
553
560
  async deleteItem() {
554
561
  const confirmed = await this.confirmSrv.confirm({
555
- title: 'Eliminar usuario',
556
- message: '¿Estás seguro de eliminar este usuario?',
557
- confirmLabel: 'Eliminar',
562
+ title: 'Delete user',
563
+ message: 'Are you sure you want to delete this user?',
564
+ confirmLabel: 'Delete',
558
565
  type: 'danger',
559
566
  });
560
567
 
561
568
  if (confirmed) {
562
- // proceder con la eliminación
569
+ // proceed with deletion
563
570
  }
564
571
  }
565
572
  ```
566
573
 
567
- | Input | Tipo | Default |
574
+ | Input | Type | Default |
568
575
  |---|---|---|
569
- | `title` | `string` | `'Confirmar acción'` |
570
- | `message` | `string` | `'¿Está seguro de realizar esta acción?'` |
571
- | `confirmLabel` | `string` | `'Confirmar'` |
572
- | `cancelLabel` | `string` | `'Cancelar'` |
573
- | `loadingText` | `string` | `'Procesando…'` |
576
+ | `title` | `string` | `'Confirm action'` |
577
+ | `message` | `string` | `'Are you sure you want to proceed?'` |
578
+ | `confirmLabel` | `string` | `'Confirm'` |
579
+ | `cancelLabel` | `string` | `'Cancel'` |
580
+ | `loadingText` | `string` | `'Processing…'` |
574
581
  | `type` | `'danger' \| 'info' \| 'warning'` | `'danger'` |
575
582
  | `loading` | `boolean` | `false` |
576
583
  | `showCancel` | `boolean` | `true` |
577
584
 
578
- | Output | Tipo |
585
+ | Output | Type |
579
586
  |---|---|
580
587
  | `closed` | `void` |
581
588
 
@@ -583,7 +590,7 @@ async deleteItem() {
583
590
 
584
591
  ### Chart (`shk-chart`)
585
592
 
586
- Wrapper de Chart.js que soporta los tipos de gráfico principales.
593
+ Chart.js wrapper supporting the main chart types.
587
594
 
588
595
  ```html
589
596
  <shk-chart
@@ -596,35 +603,35 @@ Wrapper de Chart.js que soporta los tipos de gráfico principales.
596
603
  ```typescript
597
604
  import { COLOR_PALETTE, readThemeColors } from 'shirkasoft-ui-components';
598
605
 
599
- const colors = readThemeColors(); // colores del tema actual
606
+ const colors = readThemeColors(); // current theme colors
600
607
  ```
601
608
 
602
- | Input | Tipo | Default |
609
+ | Input | Type | Default |
603
610
  |---|---|---|
604
611
  | `type` | `'line' \| 'bar' \| 'pie' \| 'doughnut' \| 'polarArea'` | `'bar'` |
605
612
  | `data` | `any` | `{ datasets: [] }` |
606
613
  | `options` | `any` | `{}` |
607
614
 
608
- **Utils exportadas:**
615
+ **Exported utils:**
609
616
 
610
- | Export | Descripción |
617
+ | Export | Description |
611
618
  |---|---|
612
- | `COLOR_PALETTE` | Array de colores predefinidos (bg + border) |
613
- | `readThemeColors()` | Lee colores del tema actual (`--shk-surface-*`) |
619
+ | `COLOR_PALETTE` | Predefined color array (bg + border) |
620
+ | `readThemeColors()` | Reads current theme colors (`--shk-surface-*`) |
614
621
 
615
622
  ---
616
623
 
617
624
  ### Tooltip (`shk-tooltip`)
618
625
 
619
- Tooltip con dos modos: `hover` (posicionamiento CSS) y `fixed` (posicionamiento calculado con JS).
626
+ Tooltip with two modes: `hover` (CSS positioning) and `fixed` (JS-calculated positioning).
620
627
 
621
628
  ```html
622
- <shk-tooltip [text]="'Info adicional'" [position]="'bottom'" [mode]="'hover'">
623
- <button>Pasar el mouse</button>
629
+ <shk-tooltip [text]="'Additional info'" [position]="'bottom'" [mode]="'hover'">
630
+ <button>Hover me</button>
624
631
  </shk-tooltip>
625
632
  ```
626
633
 
627
- | Input | Tipo | Default |
634
+ | Input | Type | Default |
628
635
  |---|---|---|
629
636
  | `text` | `string` | `''` |
630
637
  | `position` | `'top' \| 'bottom' \| 'right'` | `'bottom'` |
@@ -633,23 +640,23 @@ Tooltip con dos modos: `hover` (posicionamiento CSS) y `fixed` (posicionamiento
633
640
 
634
641
  ---
635
642
 
636
- ## Desarrollo
643
+ ## Development
637
644
 
638
645
  ```bash
639
- # Construir la librería
646
+ # Build the library
640
647
  pnpm build
641
648
 
642
- # Servir la showcase (app de demostración)
649
+ # Serve the showcase (demo app)
643
650
  pnpm serve
644
651
 
645
- # Construir la showcase
652
+ # Build the showcase
646
653
  pnpm build:showcase
647
654
 
648
- # Escuchar cambios en la librería
655
+ # Watch for library changes
649
656
  pnpm watch
650
657
  ```
651
658
 
652
- ### Versionado
659
+ ### Versioning
653
660
 
654
661
  ```bash
655
662
  pnpm version:patch # 1.0.22 → 1.0.23
@@ -659,11 +666,11 @@ pnpm version:major # 1.0.22 → 2.0.0
659
666
 
660
667
  ---
661
668
 
662
- ## Publicación
669
+ ## Publishing
663
670
 
664
- CI/CD vía GitHub Actions:
665
- - Al pushear un tag `v*` se publica a npm automáticamente.
666
- - Al pushear a `main`/`master` se despliega la showcase a GitHub Pages.
671
+ CI/CD via GitHub Actions:
672
+ - Pushing a `v*` tag automatically publishes to npm.
673
+ - Pushing to `main`/`master` deploys the showcase to GitHub Pages.
667
674
 
668
675
  Manual:
669
676
 
@@ -673,6 +680,6 @@ bash scripts/publish-lib.sh
673
680
 
674
681
  ---
675
682
 
676
- ## Licencia
683
+ ## License
677
684
 
678
685
  MIT © Shirkasoft
@@ -2820,9 +2820,11 @@ class ChartComponent {
2820
2820
  this.chartInstance?.destroy();
2821
2821
  }
2822
2822
  initChart() {
2823
- const ctx = this.canvasRef().nativeElement.getContext('2d');
2823
+ const canvas = this.canvasRef().nativeElement;
2824
+ const ctx = canvas.getContext('2d');
2824
2825
  if (!ctx)
2825
2826
  return;
2827
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
2826
2828
  this.chartInstance = new Chart(ctx, {
2827
2829
  type: this.type(),
2828
2830
  data: this.data(),