@c80/ui 1.0.41 → 1.0.44

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.
@@ -1,7 +1,7 @@
1
- import { EventEmitter, OnInit, OnDestroy } from '@angular/core';
1
+ import { OnInit, OnDestroy } from '@angular/core';
2
2
  import { Observable } from 'rxjs';
3
- import { type IconType } from '../icon';
4
- import { C80TableColDef, ID } from './table.types';
3
+ import { C80TableColDef, CustomTableAction } from './table.types';
4
+ import { getActionTooltip } from './table.utils';
5
5
  import * as i0 from "@angular/core";
6
6
  /**
7
7
  * C80TableComponent - Componente de tabla avanzado con funcionalidades CRUD
@@ -51,142 +51,170 @@ import * as i0 from "@angular/core";
51
51
  * - 'password': Texto oculto (input type="password")
52
52
  * - 'enum': Lista de opciones predefinidas (select)
53
53
  *
54
- * EJEMPLOS:
54
+ * SISTEMA DE ACCIONES UNIFICADO (customActions):
55
+ * =============================================
56
+ *
57
+ * Input: `customActions: CustomTableAction[]` - Array de acciones dinámicas
58
+ * Output: `(actionClick)` - Evento unificado que emite { action: string, row: T }
59
+ *
60
+ * ACCIONES CRUD PREDEFINIDAS (TABLE_CRUD_ACTIONS):
61
+ * ------------------------------------------------
62
+ * - CREATE: Solo aparece como botón "+" en header (no en filas)
63
+ * - UPDATE: Activa modo edición (muestra inputs), botón "✓" guarda cambios
64
+ * - DELETE: Muestra confirmación y emite evento delete
65
+ * - CANCEL: Muestra confirmación y emite evento cancel (opcional)
66
+ *
67
+ * ACCIONES PERSONALIZADAS (Custom Actions):
68
+ * -----------------------------------------
69
+ * Interfaz: { name: string, icon: IconType, condition?: (row) => boolean, tooltip?: string }
70
+ * - name: ID único de la acción
71
+ * - icon: Icono del botón (ej: 'settings', 'upload', 'refresh')
72
+ * - condition: Función opcional para mostrar/ocultar según estado de fila
73
+ * - tooltip: Texto al hacer hover (default: name)
74
+ *
75
+ * MANEJO EN COMPONENTE:
76
+ * --------------------
77
+ * ```typescript
78
+ * // 1. Definir acciones en constants:
79
+ * export const ENTITY_TABLE_ACTIONS: CustomTableAction[] = [
80
+ * TABLE_CRUD_ACTIONS.CREATE,
81
+ * TABLE_CRUD_ACTIONS.UPDATE,
82
+ * { name: 'custom-action', icon: 'settings', tooltip: 'Configurar' },
83
+ * TABLE_CRUD_ACTIONS.DELETE
84
+ * ];
85
+ *
86
+ * // 2. En el componente:
87
+ * readonly tableActions = ENTITY_TABLE_ACTIONS;
88
+ *
89
+ * handleAction({ action, row }) {
90
+ * const entity = row as unknown as EntityType;
91
+ * switch (action) {
92
+ * case 'create': this.onCreate(row); break;
93
+ * case 'update': this.onUpdate(entity); break;
94
+ * case 'delete': this.onDelete(entity.id); break;
95
+ * case 'custom-action': this.onCustom(entity); break;
96
+ * }
97
+ * }
98
+ * ```
99
+ *
100
+ * ACCIONES CON CONDICIÓN:
101
+ * ----------------------
102
+ * ```typescript
103
+ * {
104
+ * name: 'enable',
105
+ * icon: 'toggleOn',
106
+ * tooltip: 'Habilitar',
107
+ * condition: (row) => row['enabled'] === false
108
+ * }
109
+ * ```
110
+ *
111
+ * CONSTANTES REUTILIZABLES:
112
+ * ------------------------
113
+ * - TABLE_CRUD_ACTIONS: Objeto con CREATE, UPDATE, DELETE, CANCEL
114
+ * - BASIC_CRUD_ACTIONS: Array con [CREATE, UPDATE, DELETE]
115
+ * Importar desde: `@shared` o `@shared/constants`
116
+ *
117
+ * EJEMPLOS COMPLETOS:
118
+ * ------------------
55
119
  * - { accessor: 'id', visible: false } → NUNCA se muestra
56
120
  * - { accessor: 'motorPos', hideIfAllValuesAreNull: true, default: 0, type: 'integer' } → Entero (5.7 → 5)
57
121
  * - { accessor: 'weight', type: 'number', default: 2.5 } → Decimal preservado (5.7 → 5.7)
58
122
  * - { accessor: 'status', default: 'active' } → Se autorellena con 'active' en creación
59
123
  * - { accessor: 'name' } → Siempre visible, sin valor por defecto (cadena vacía)
60
124
  * - inputValues$ emite { motorPos: 5, weight: 2.3 } → actualiza inputs dinámicamente
125
+ * - customActions con CREATE → Muestra botón "+" en header
126
+ * - customActions con UPDATE → Botón "edit" activa modo edición
127
+ * - customActions con custom → Botón personalizado emite evento
61
128
  */ export declare class C80TableComponent<T extends Record<string, unknown>> implements OnInit, OnDestroy {
62
129
  private readonly modalService;
63
130
  private readonly visibilityService;
64
131
  private readonly dataUtils;
132
+ private readonly dataConverter;
65
133
  private readonly selectionService;
66
134
  private readonly crudService;
67
135
  data$: Observable<T[]>;
68
136
  columns: C80TableColDef[];
69
137
  inputValues$?: Observable<Partial<T>>;
138
+ customActions: CustomTableAction[];
70
139
  size: number;
71
140
  multiple: boolean;
141
+ searchable: boolean;
142
+ allowSelection: boolean;
72
143
  noConfirm: boolean;
73
- customActionIcon: IconType;
74
- customActionTooltip: string;
75
- createAction: EventEmitter<{
76
- row: Partial<T>;
77
- done: (result: boolean) => void;
78
- }>;
79
- updateAction: EventEmitter<{
80
- id: ID;
81
- changes: Partial<T>;
82
- done: (result: boolean) => void;
83
- }>;
84
- deleteAction: EventEmitter<{
85
- id: ID;
86
- done: (result: boolean) => void;
144
+ readonly actionClick: import("@angular/core").OutputEmitterRef<{
145
+ action: string;
146
+ row: T;
87
147
  }>;
88
- cancelAction: EventEmitter<{
89
- id: ID;
90
- done: (result: boolean) => void;
91
- }>;
92
- viewAction: EventEmitter<T>;
93
- uploadAction: EventEmitter<T>;
94
- getRowButtonAction: EventEmitter<T>;
95
- customAction: EventEmitter<T>;
96
- moveUpAction: EventEmitter<{
97
- id: ID;
98
- done: (result: boolean) => void;
99
- }>;
100
- moveDownAction: EventEmitter<{
101
- id: ID;
102
- done: (result: boolean) => void;
103
- }>;
104
- enableAction: EventEmitter<{
105
- id: ID;
106
- enabled: boolean;
107
- done: (result: boolean) => void;
108
- }>;
109
- searchTerm: EventEmitter<string>;
110
- errorEvent: EventEmitter<string>;
111
- selectable: EventEmitter<T[]>;
148
+ readonly searchTerm: import("@angular/core").OutputEmitterRef<string>;
149
+ readonly errorEvent: import("@angular/core").OutputEmitterRef<string>;
150
+ readonly selectable: import("@angular/core").OutputEmitterRef<T[]>;
112
151
  readonly data: import("@angular/core").WritableSignal<T[]>;
113
- readonly keys: import("@angular/core").WritableSignal<string[]>;
114
152
  readonly searchValue: import("@angular/core").WritableSignal<string>;
115
153
  private readonly selectionState;
116
- readonly selectedItems: import("@angular/core").Signal<Set<ID>>;
154
+ readonly selectedItems: import("@angular/core").Signal<Set<string>>;
117
155
  readonly selectAllChecked: import("@angular/core").Signal<boolean>;
118
156
  readonly selectAllIndeterminate: import("@angular/core").Signal<boolean>;
119
157
  private readonly crudState;
120
158
  readonly creating: import("@angular/core").Signal<boolean>;
121
159
  readonly newRow: import("@angular/core").Signal<Partial<T> | null>;
122
- readonly editing: import("@angular/core").Signal<ID | null>;
160
+ readonly editing: import("@angular/core").Signal<string | null>;
123
161
  readonly editRow: import("@angular/core").Signal<Partial<T> | null>;
124
- readonly hasCreateActionListener: import("@angular/core").WritableSignal<boolean>;
125
- readonly hasUpdateActionListener: import("@angular/core").WritableSignal<boolean>;
126
- readonly hasDeleteActionListener: import("@angular/core").WritableSignal<boolean>;
127
- readonly hasCancelActionListener: import("@angular/core").WritableSignal<boolean>;
128
- readonly hasViewActionListener: import("@angular/core").WritableSignal<boolean>;
129
- readonly hasUploadActionListener: import("@angular/core").WritableSignal<boolean>;
130
- readonly hasGetRowButtonListener: import("@angular/core").WritableSignal<boolean>;
131
- readonly hasCustomActionListener: import("@angular/core").WritableSignal<boolean>;
132
- readonly hasMoveUpActionListener: import("@angular/core").WritableSignal<boolean>;
133
- readonly hasMoveDownActionListener: import("@angular/core").WritableSignal<boolean>;
134
- readonly hasEnableActionListener: import("@angular/core").WritableSignal<boolean>;
135
- readonly hasSearchTermListener: import("@angular/core").WritableSignal<boolean>;
136
- readonly hasSelectableListener: import("@angular/core").WritableSignal<boolean>;
162
+ readonly keys: import("@angular/core").Signal<string[]>;
163
+ readonly hasAnyActions: import("@angular/core").Signal<boolean>;
164
+ readonly hasCrudCreate: import("@angular/core").Signal<boolean>;
165
+ readonly hasCrudUpdate: import("@angular/core").Signal<boolean>;
166
+ readonly hasCrudDelete: import("@angular/core").Signal<boolean>;
167
+ readonly hasCrudCancel: import("@angular/core").Signal<boolean>;
168
+ readonly tableMaxHeight: import("@angular/core").Signal<string | undefined>;
137
169
  private dataSub?;
138
170
  private inputValuesSub?;
139
- private getErrorMessage;
140
171
  /**
141
- * Actualiza las keys de columnas visibles basándose en el estado actual
172
+ * Maneja input de creación/edición de forma unificada
142
173
  */
143
- private updateVisibleKeys;
174
+ private handleInput;
144
175
  /**
145
- * Aplica valores parciales a los inputs en modo creación o edición.
146
- * Solo actualiza si estamos en modo creación (creating = true) o edición (editing != null)
176
+ * Maneja confirmación modal de forma genérica
147
177
  */
148
- private applyInputValues;
178
+ private handleConfirmAction;
149
179
  ngOnInit(): void;
150
180
  ngOnDestroy(): void;
151
181
  onInput(event: Event, key: string, col?: C80TableColDef): void;
182
+ onEditInput(event: Event, key: string, col?: C80TableColDef): void;
152
183
  onDelete(row: T): Promise<void>;
153
184
  onCancel(row: T): Promise<void>;
154
185
  startCreate(): void;
155
186
  cancelCreate(): void;
156
- updateNewRow(key: string, value: unknown): void;
157
187
  saveCreate(): void;
158
188
  onEdit(row: T): void;
159
189
  cancelEdit(): void;
160
- onEditInput(event: Event, key: string, col?: C80TableColDef): void;
161
190
  saveEdit(row: T): void;
162
191
  /**
163
- * TrackBy function for ngFor to avoid DOM re-creation (NG0956 warning).
192
+ * Convierte todos los valores de una fila según los tipos definidos en las columnas
193
+ * Asegura que los datos emitidos tengan el tipo correcto (integer, number, boolean, etc.)
164
194
  */
165
- trackById(index: number, row: T): number | string;
195
+ private convertRowTypes;
166
196
  /**
167
- * Emits the view event with the entire row data
168
- */
169
- onView(row: T): void;
170
- /**
171
- * Emits the getRowButtonAction event with the entire row data
197
+ * TrackBy function for ngFor to avoid DOM re-creation (NG0956 warning).
172
198
  */
173
- onGetRowButton(row: T): void;
199
+ readonly trackById: (index: number, row: T) => string | number;
174
200
  /**
175
- * Emits the customAction event with the entire row data
201
+ * Verifica si una acción personalizada debe mostrarse para una fila específica
176
202
  */
177
- onCustomAction(row: T): void;
203
+ readonly shouldShowAction: (action: CustomTableAction, row: T) => boolean;
178
204
  /**
179
- * Emits the moveUpAction event with the row id
205
+ * Obtiene el tooltip de una acción
180
206
  */
181
- onMoveUp(row: T): Promise<void>;
207
+ readonly getActionTooltip: typeof getActionTooltip;
182
208
  /**
183
- * Emits the moveDownAction event with the row id
209
+ * Maneja el click en una acción personalizada dinámica
210
+ * UPDATE activa el modo edición, otras acciones emiten directamente
211
+ * Si la acción tiene configuración de confirmación, muestra modal antes de ejecutar
184
212
  */
185
- onMoveDown(row: T): Promise<void>;
213
+ onDynamicAction(action: CustomTableAction, row: T): void;
186
214
  /**
187
- * Emits the enableAction event with the row id and current enabled status
215
+ * Maneja acciones que requieren confirmación del usuario
188
216
  */
189
- onEnable(row: T): Promise<void>;
217
+ private handleActionWithConfirmation;
190
218
  /**
191
219
  * Handles search input changes with debouncing
192
220
  */
@@ -195,15 +223,14 @@ import * as i0 from "@angular/core";
195
223
  * Clears the search input
196
224
  */
197
225
  clearSearch(): void;
198
- getCellValue(row: T, accessor: string): unknown;
199
- getDisplayValue(value: unknown): string;
200
- getEnumDisplayValue(value: unknown, col: C80TableColDef): string;
201
- getEnumOptions(col: C80TableColDef): {
226
+ readonly getCellValue: <T_1 extends Record<string, unknown>>(row: T_1, accessor: string) => unknown;
227
+ readonly getDisplayValue: (value: unknown, col?: C80TableColDef) => string;
228
+ readonly getEnumDisplayValue: (value: unknown, col: C80TableColDef) => string;
229
+ readonly getEnumOptions: (col: C80TableColDef) => {
202
230
  value: string | number;
203
231
  label: string;
204
232
  }[];
205
- getCellColor(value: unknown, col: C80TableColDef): string | undefined;
206
- getTableMaxHeight(): string | undefined;
233
+ readonly getCellColor: (value: unknown, col: C80TableColDef) => string | undefined;
207
234
  /**
208
235
  * Selection methods
209
236
  */
@@ -216,11 +243,12 @@ import * as i0 from "@angular/core";
216
243
  toggleSelectAll(): void;
217
244
  toggleItemSelection(item: T): void;
218
245
  isItemSelected(item: T): boolean;
219
- isColumnVisible(column: C80TableColDef, forceShowInCreation?: boolean): boolean;
246
+ isColumnVisible(column: C80TableColDef): boolean;
220
247
  isColumnVisibleInHeader(column: C80TableColDef): boolean;
221
248
  isColumnVisibleForRow(column: C80TableColDef, row: T): boolean;
222
- onUpload(row: T): void;
223
249
  static ɵfac: i0.ɵɵFactoryDeclaration<C80TableComponent<any>, never>;
224
- static ɵcmp: i0.ɵɵComponentDeclaration<C80TableComponent<any>, "c80-table", never, { "data$": { "alias": "data$"; "required": false; }; "columns": { "alias": "columns"; "required": false; }; "inputValues$": { "alias": "inputValues$"; "required": false; }; "size": { "alias": "size"; "required": false; }; "multiple": { "alias": "multiple"; "required": false; }; "noConfirm": { "alias": "noConfirm"; "required": false; }; "customActionIcon": { "alias": "customActionIcon"; "required": false; }; "customActionTooltip": { "alias": "customActionTooltip"; "required": false; }; }, { "createAction": "createAction"; "updateAction": "updateAction"; "deleteAction": "deleteAction"; "cancelAction": "cancelAction"; "viewAction": "viewAction"; "uploadAction": "uploadAction"; "getRowButtonAction": "getRowButtonAction"; "customAction": "customAction"; "moveUpAction": "moveUpAction"; "moveDownAction": "moveDownAction"; "enableAction": "enableAction"; "searchTerm": "searchTerm"; "errorEvent": "errorEvent"; "selectable": "selectable"; }, never, never, true, never>;
250
+ static ɵcmp: i0.ɵɵComponentDeclaration<C80TableComponent<any>, "c80-table", never, { "data$": { "alias": "data$"; "required": false; }; "columns": { "alias": "columns"; "required": false; }; "inputValues$": { "alias": "inputValues$"; "required": false; }; "customActions": { "alias": "customActions"; "required": false; }; "size": { "alias": "size"; "required": false; }; "multiple": { "alias": "multiple"; "required": false; }; "searchable": { "alias": "searchable"; "required": false; }; "allowSelection": { "alias": "allowSelection"; "required": false; }; "noConfirm": { "alias": "noConfirm"; "required": false; }; }, { "actionClick": "actionClick"; "searchTerm": "searchTerm"; "errorEvent": "errorEvent"; "selectable": "selectable"; }, never, never, true, never>;
251
+ static ngAcceptInputType_searchable: boolean | string;
252
+ static ngAcceptInputType_allowSelection: boolean | string;
225
253
  static ngAcceptInputType_noConfirm: boolean | string;
226
254
  }
@@ -1,16 +1,39 @@
1
1
  /**
2
2
  * Tipos comunes para las tablas C80
3
3
  */
4
- export type ID = number | string;
4
+ import type { IconType } from '../icon';
5
5
  export interface C80TableColDef {
6
6
  accessor: string;
7
7
  label: string;
8
8
  visible?: boolean;
9
- type?: 'string' | 'number' | 'integer' | 'boolean' | 'password' | 'enum';
9
+ type?: 'string' | 'number' | 'integer' | 'boolean' | 'password' | 'enum' | 'date';
10
10
  order?: 'ASC' | 'DESC';
11
11
  readOnly?: boolean;
12
12
  enum?: Record<string | number, string>;
13
13
  color?: Record<string | number, string>;
14
14
  hideIfAllValuesAreNull?: boolean;
15
15
  default?: unknown;
16
+ min?: number;
17
+ max?: number;
18
+ }
19
+ /**
20
+ * Configuración de confirmación para acciones de tabla
21
+ */
22
+ export interface ActionConfirmation {
23
+ title: string;
24
+ message: string;
25
+ confirmText?: string;
26
+ cancelText?: string;
27
+ }
28
+ /**
29
+ * Definición de acción personalizada para tabla
30
+ * Permite agregar acciones dinámicas sin modificar el componente
31
+ */
32
+ export interface CustomTableAction {
33
+ name: string;
34
+ icon: IconType;
35
+ color?: string;
36
+ condition?: <T extends Record<string, unknown>>(row: T) => boolean;
37
+ tooltip?: string;
38
+ confirmation?: ActionConfirmation;
16
39
  }
@@ -0,0 +1,42 @@
1
+ import type { C80TableColDef, CustomTableAction } from './table.types';
2
+ /**
3
+ * Transform function para convertir valores string/boolean a boolean
4
+ * Usado en @Input() con transform para habilitar sintaxis de atributos booleanos
5
+ * @example [searchable]="true" o simplemente searchable
6
+ */
7
+ export declare function booleanAttribute(value: boolean | string): boolean;
8
+ /**
9
+ * Extrae mensaje de error de forma segura desde un objeto unknown
10
+ * @param err - Error desconocido (puede ser Error, string, object, etc.)
11
+ * @returns Mensaje de error legible
12
+ */
13
+ export declare function getErrorMessage(err: unknown): string;
14
+ /**
15
+ * Obtiene el valor de un input HTML según el tipo de columna
16
+ * IMPORTANTE: Convierte el valor al tipo correcto antes de retornarlo
17
+ * @param event - Evento del input
18
+ * @param col - Definición de columna (opcional)
19
+ * @returns Valor extraído y convertido según el tipo de columna
20
+ */
21
+ export declare function getInputValue(event: Event, col?: C80TableColDef): unknown;
22
+ /**
23
+ * Extrae el ID de una fila para usar como trackBy en ngFor
24
+ * @param index - Índice de la fila
25
+ * @param row - Fila de datos
26
+ * @returns ID como string o índice como fallback
27
+ */
28
+ export declare function trackById<T extends Record<string, unknown>>(index: number, row: T): string | number;
29
+ /**
30
+ * Verifica si una acción debe mostrarse para una fila específica
31
+ * CREATE se excluye porque se maneja con el botón "+" en el header
32
+ * @param action - Acción a verificar
33
+ * @param row - Fila de datos
34
+ * @returns true si la acción debe mostrarse
35
+ */
36
+ export declare function shouldShowAction<T extends Record<string, unknown>>(action: CustomTableAction, row: T): boolean;
37
+ /**
38
+ * Obtiene el tooltip de una acción (usa tooltip personalizado o name por defecto)
39
+ * @param action - Acción
40
+ * @returns Texto del tooltip
41
+ */
42
+ export declare function getActionTooltip(action: CustomTableAction): string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@c80/ui",
3
- "version": "1.0.41",
3
+ "version": "1.0.44",
4
4
  "peerDependencies": {
5
5
  "@angular/core": "^18.2.0",
6
6
  "rxjs": "~7.8.0",