@c80/ui 2.0.0 → 2.0.1
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 +49 -49
- package/esm2022/lib/action-list/action-list.component.js +1 -1
- package/esm2022/lib/action-list/action-list.component.js.map +1 -1
- package/esm2022/lib/card-level/card-level.component.js +4 -15
- package/esm2022/lib/card-level/card-level.component.js.map +1 -1
- package/esm2022/lib/error-notification/error-notification.component.js +1 -1
- package/esm2022/lib/error-notification/error-notification.component.js.map +1 -1
- package/esm2022/lib/header/header.component.js +1 -1
- package/esm2022/lib/header/header.component.js.map +1 -1
- package/esm2022/lib/icon/icon.component.js +1 -12
- package/esm2022/lib/icon/icon.component.js.map +1 -1
- package/esm2022/lib/icon/theme.service.js +3 -0
- package/esm2022/lib/icon/theme.service.js.map +1 -1
- package/esm2022/lib/info-list/info-list.component.js +1 -1
- package/esm2022/lib/info-list/info-list.component.js.map +1 -1
- package/esm2022/lib/info-message/info-message.component.js +5 -10
- package/esm2022/lib/info-message/info-message.component.js.map +1 -1
- package/esm2022/lib/input-field/input-field.component.js +1 -1
- package/esm2022/lib/input-field/input-field.component.js.map +1 -1
- package/esm2022/lib/input-search/c80-input-search.component.js +1 -1
- package/esm2022/lib/input-search/c80-input-search.component.js.map +1 -1
- package/esm2022/lib/legal-disclaimer/legal-disclaimer.component.js +1 -1
- package/esm2022/lib/legal-disclaimer/legal-disclaimer.component.js.map +1 -1
- package/esm2022/lib/modal/modal.component.js +3 -5
- package/esm2022/lib/modal/modal.component.js.map +1 -1
- package/esm2022/lib/modal/modal.service.js +6 -0
- package/esm2022/lib/modal/modal.service.js.map +1 -1
- package/esm2022/lib/navigation-card/navigation-card.component.js +1 -1
- package/esm2022/lib/navigation-card/navigation-card.component.js.map +1 -1
- package/esm2022/lib/profile-stats/profile-stats.component.js +1 -1
- package/esm2022/lib/profile-stats/profile-stats.component.js.map +1 -1
- package/esm2022/lib/rating-display/rating-display.component.js +1 -1
- package/esm2022/lib/rating-display/rating-display.component.js.map +1 -1
- package/esm2022/lib/rating-stars/rating-stars.component.js +3 -4
- package/esm2022/lib/rating-stars/rating-stars.component.js.map +1 -1
- package/esm2022/lib/select/select.component.js +1 -12
- package/esm2022/lib/select/select.component.js.map +1 -1
- package/esm2022/lib/snackbar/snackbar.component.js +1 -3
- package/esm2022/lib/snackbar/snackbar.component.js.map +1 -1
- package/esm2022/lib/stat-card/stat-card.component.js +1 -2
- package/esm2022/lib/stat-card/stat-card.component.js.map +1 -1
- package/esm2022/lib/tab/c80-tab.component.js +4 -7
- package/esm2022/lib/tab/c80-tab.component.js.map +1 -1
- package/esm2022/lib/tab/directives/c80-tab-item.directive.js +0 -1
- package/esm2022/lib/tab/directives/c80-tab-item.directive.js.map +1 -1
- package/esm2022/lib/tab/directives/c80-tab-label.directive.js +0 -1
- package/esm2022/lib/tab/directives/c80-tab-label.directive.js.map +1 -1
- package/esm2022/lib/tab/index.js.map +1 -1
- package/esm2022/lib/table/table.component.js +3 -29
- package/esm2022/lib/table/table.component.js.map +1 -1
- package/lib/card-level/card-level.component.d.ts +1 -1
- package/lib/info-message/info-message.component.d.ts +2 -5
- package/lib/modal/modal.component.d.ts +0 -1
- package/lib/rating-stars/rating-stars.component.d.ts +0 -1
- package/lib/tab/c80-tab.component.d.ts +1 -2
- package/lib/tab/index.d.ts +0 -1
- package/package.json +1 -1
- package/esm2022/lib/tab/c80-tab.types.js +0 -1
- package/esm2022/lib/tab/c80-tab.types.js.map +0 -1
- package/lib/tab/c80-tab.types.d.ts +0 -4
package/README.md
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
1
|
# UI Library (`@c80/ui`)
|
|
3
2
|
|
|
4
3
|
Librería de componentes de interfaz de usuario reutilizables para Angular, diseñada para acelerar el desarrollo de aplicaciones con una experiencia visual consistente y componentes listos para producción.
|
|
@@ -24,9 +23,8 @@ Componente para mostrar íconos SVG personalizables, con soporte para distintos
|
|
|
24
23
|
<c80-icon icon="add" [size]="2"></c80-icon>
|
|
25
24
|
```
|
|
26
25
|
|
|
27
|
-
|
|
28
26
|
### `c80-table`
|
|
29
|
-
Componente de tabla editable y dinámica, con soporte para operaciones CRUD, definición de columnas, edición en línea y acciones integradas.
|
|
27
|
+
Componente de tabla editable y dinámica, con soporte para operaciones CRUD, definición de columnas, edición en línea y acciones integradas. Desacoplado: recibe los datos como observable y emite eventos para las acciones.
|
|
30
28
|
|
|
31
29
|
**Props principales:**
|
|
32
30
|
- `data$`: `Observable<T[]>` (observable con los datos a mostrar)
|
|
@@ -64,72 +62,74 @@ userColumns = [
|
|
|
64
62
|
];
|
|
65
63
|
|
|
66
64
|
users$: Observable<User[]> = this.userService.getAll();
|
|
65
|
+
```
|
|
67
66
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
67
|
+
### `c80-action-list`
|
|
68
|
+
Lista de acciones con título opcional, descripción e iconos. Emite el ID de la acción al hacer click (o ejecuta la función embebida en el `ActionItem`).
|
|
69
|
+
|
|
70
|
+
**Props principales:**
|
|
71
|
+
- `title`: `string` (título opcional de la sección)
|
|
72
|
+
- `actions`: `ActionItem[]` (acciones a mostrar)
|
|
73
|
+
- `actionClick`: `EventEmitter<string>` (emite el `action` string al hacer click; si `action` es una función, se ejecuta en lugar de emitir)
|
|
74
|
+
|
|
75
|
+
**ActionItem:**
|
|
76
|
+
```typescript
|
|
77
|
+
interface ActionItem {
|
|
78
|
+
label: string; // Texto principal
|
|
79
|
+
description?: string; // Texto secundario
|
|
80
|
+
icon?: IconType; // Icono a mostrar
|
|
81
|
+
action?: string | (() => void); // ID de acción a emitir, o función a ejecutar
|
|
82
|
+
isDanger?: boolean; // Estilo de peligro
|
|
83
|
+
isNavigable?: boolean; // Estilo de enlace de navegación
|
|
76
84
|
}
|
|
77
85
|
```
|
|
78
86
|
|
|
87
|
+
**Ejemplo de uso:**
|
|
88
|
+
```html
|
|
89
|
+
<c80-action-list
|
|
90
|
+
title="Mi sección"
|
|
91
|
+
[actions]="actions()"
|
|
92
|
+
(actionClick)="onActionClick($event)"></c80-action-list>
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### `c80-input-field`
|
|
96
|
+
Campo de entrada (`text` | `email` | `password`) que mantiene la estética de `c80-info-list`, pensado para edición inline con consistencia visual. Maneja estados de focus/readonly y eventos de teclado.
|
|
97
|
+
|
|
98
|
+
**Outputs:**
|
|
99
|
+
- `valueChange`: `EventEmitter<string>` (emite cuando cambia el valor)
|
|
100
|
+
- `enterPressed`: `EventEmitter<void>` (emite al presionar Enter)
|
|
101
|
+
|
|
102
|
+
```html
|
|
103
|
+
<c80-input-field
|
|
104
|
+
(valueChange)="onValueChange($event)"
|
|
105
|
+
(enterPressed)="onSubmit()"></c80-input-field>
|
|
106
|
+
```
|
|
107
|
+
|
|
79
108
|
## Instalación y uso
|
|
80
|
-
Esta librería está pensada para
|
|
109
|
+
Esta librería está pensada para usarse dentro del monorepo Nx. Importa los componentes directamente desde el punto de entrada público:
|
|
81
110
|
|
|
82
111
|
```typescript
|
|
83
112
|
import { C80IconComponent, C80TableComponent } from '@c80/ui';
|
|
84
113
|
```
|
|
85
114
|
|
|
86
115
|
## Estructura del proyecto
|
|
87
|
-
- `src/lib
|
|
88
|
-
- `src/
|
|
89
|
-
- `src/index.ts`: Punto de entrada público de la librería
|
|
90
|
-
|
|
91
|
-
## Publicación y visualización en npm
|
|
92
|
-
Una vez publicada, la librería puede consultarse en:
|
|
93
|
-
https://www.npmjs.com/package/@c80/ui
|
|
116
|
+
- `src/lib/<componente>/`: un directorio por componente (`icon`, `table`, `action-list`, `input-field`, etc.)
|
|
117
|
+
- `src/index.ts`: punto de entrada público de la librería
|
|
94
118
|
|
|
95
119
|
## Desarrollo y contribución
|
|
96
|
-
1.
|
|
97
|
-
2.
|
|
98
|
-
3. Ejecuta pruebas y build local:
|
|
120
|
+
1. Realiza los cambios necesarios en los componentes o estilos.
|
|
121
|
+
2. Ejecuta la build local:
|
|
99
122
|
```sh
|
|
100
|
-
pnpm nx test ui
|
|
101
123
|
pnpm nx build ui
|
|
102
124
|
```
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
## Contacto y soporte
|
|
106
|
-
Para reportar errores, solicitar funcionalidades o recibir soporte, utiliza el sistema de issues del repositorio o contacta al equipo de desarrollo.
|
|
107
|
-
|
|
108
|
-
---
|
|
109
|
-
© 2025 c80. Todos los derechos reservados.
|
|
125
|
+
3. Realiza un PR siguiendo las convenciones del repositorio.
|
|
110
126
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
3. Ejecuta las pruebas y la build local:
|
|
114
|
-
```sh
|
|
115
|
-
pnpm nx test ui
|
|
116
|
-
pnpm nx build ui
|
|
117
|
-
```
|
|
118
|
-
4. Realiza un PR siguiendo las convenciones del repositorio.
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
## Publicación y visualización en npm
|
|
122
|
-
Esta librería puede ser publicada y visualizada en el registro de npm para su distribución y consulta pública. Una vez publicada, puedes encontrarla buscando el nombre del paquete en [npmjs.com](https://www.npmjs.com/):
|
|
123
|
-
|
|
124
|
-
```
|
|
127
|
+
## Publicación en npm
|
|
128
|
+
Una vez publicada, la librería puede consultarse en:
|
|
125
129
|
https://www.npmjs.com/package/@c80/ui
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
Desde la página de npm podrás consultar la documentación, versiones, dependencias y descargar el paquete para su uso externo si así se habilita.
|
|
129
130
|
|
|
130
131
|
## Contacto y soporte
|
|
131
|
-
Para reportar errores, solicitar
|
|
132
|
+
Para reportar errores, solicitar funcionalidades o recibir soporte, utiliza el sistema de issues del repositorio o contacta al equipo de desarrollo.
|
|
132
133
|
|
|
133
134
|
---
|
|
134
135
|
© 2025 c80. Todos los derechos reservados.
|
|
135
|
-
|
|
@@ -25,6 +25,6 @@ export class ActionListComponent {
|
|
|
25
25
|
}
|
|
26
26
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: ActionListComponent, decorators: [{
|
|
27
27
|
type: Component,
|
|
28
|
-
args: [{ selector: 'c80-action-list',
|
|
28
|
+
args: [{ selector: 'c80-action-list', imports: [IconComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"action-list\">\n @if (title()) {\n <h2 class=\"action-list__title\">{{ title() }}</h2>\n }\n\n @for (action of actions(); track $index) {\n <button class=\"action-list__item\" [class.action-list__item--danger]=\"action.isDanger\" [class.action-list__item--navigable]=\"action.isNavigable\" (click)=\"onActionClick(action)\">\n\n <div class=\"action-list__info\">\n <span class=\"action-list__label\">{{ action.label }}</span>\n @if (action.description) {\n <span class=\"action-list__description\">{{ action.description }}</span>\n }\n </div>\n\n @if (action.icon || action.isNavigable) {\n <c80-icon [button]=\"!action.isNavigable\" [icon]=\"action.icon || (action.isNavigable ? 'arrowRight' : '')\" [color]=\"action.isDanger ? 'warn' : undefined\" [title]=\"action.label\" />\n }\n </button>\n }\n</div>", styles: [".action-list__title{font-size:.875rem;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--color-text-secondary);margin:1rem 0px .4rem}.action-list__item{display:flex;justify-content:space-between;align-items:center;padding:1rem;background:var(--color-surface);border-radius:var(--radius-lg);margin-bottom:.75rem;border:none;width:100%;text-align:left;cursor:pointer;transition:all var(--transition-base)}.action-list__item:hover{transform:translate(2px);background:var(--color-bg-dark)}.action-list__item:last-child{margin-bottom:0}.action-list__item--danger .action-list__label{color:var(--color-accent)}.action-list__item--navigable:hover{transform:translate(2px);background:var(--color-bg-dark)}.action-list__info{display:flex;flex-direction:column;gap:.25rem}.action-list__label{font-size:1rem;font-weight:500;color:var(--color-text-primary)}.action-list__description{font-size:.875rem;color:var(--color-text-secondary)}\n"] }]
|
|
29
29
|
}], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], actions: [{ type: i0.Input, args: [{ isSignal: true, alias: "actions", required: false }] }], actionClick: [{ type: i0.Output, args: ["actionClick"] }] } });
|
|
30
30
|
//# sourceMappingURL=action-list.component.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"action-list.component.js","sourceRoot":"","sources":["../../../../../libs/ui/src/lib/action-list/action-list.component.ts","../../../../../libs/ui/src/lib/action-list/action-list.component.html"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAElF,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;;AAExC;;GAEG;
|
|
1
|
+
{"version":3,"file":"action-list.component.js","sourceRoot":"","sources":["../../../../../libs/ui/src/lib/action-list/action-list.component.ts","../../../../../libs/ui/src/lib/action-list/action-list.component.html"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAElF,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;;AAExC;;GAEG;AAQH,MAAM,OAAO,mBAAmB;IAC5B,KAAK,GAAG,KAAK,CAAS,EAAE,4EAAC,CAAC;IAC1B,OAAO,GAAG,KAAK,CAAe,EAAE,8EAAC,CAAC;IAElC,WAAW,GAAG,MAAM,EAAU,CAAC;IAE/B;;;OAGG;IACO,aAAa,CAAC,MAAkB;QACtC,IAAI,OAAO,MAAM,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;YACpC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACzC,CAAC;aAAM,IAAI,OAAO,MAAM,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;YAC7C,MAAM,CAAC,MAAM,EAAE,CAAC;QACpB,CAAC;IACL,CAAC;wGAhBQ,mBAAmB;4FAAnB,mBAAmB,sXCdhC,w5BAoBM,8+BDXQ,aAAa;;4FAKd,mBAAmB;kBAP/B,SAAS;+BACI,iBAAiB,WAClB,CAAC,aAAa,CAAC,mBAGP,uBAAuB,CAAC,MAAM","sourcesContent":["import { Component, input, output, ChangeDetectionStrategy } from '@angular/core';\r\nimport type { ActionItem } from './action-list.types';\r\nimport { IconComponent } from '../icon';\r\n\r\n/**\r\n *\r\n */\r\n@Component({\r\n selector: 'c80-action-list',\r\n imports: [IconComponent],\r\n templateUrl: './action-list.component.html',\r\n styleUrl: './action-list.component.scss',\r\n changeDetection: ChangeDetectionStrategy.OnPush\r\n})\r\nexport class ActionListComponent {\r\n title = input<string>('');\r\n actions = input<ActionItem[]>([]);\r\n\r\n actionClick = output<string>();\r\n\r\n /**\r\n *\r\n * @param action\r\n */\r\n protected onActionClick(action: ActionItem): void {\r\n if (typeof action.action === 'string') {\r\n this.actionClick.emit(action.action);\r\n } else if (typeof action.action === 'function') {\r\n action.action();\r\n }\r\n }\r\n}","<div class=\"action-list\">\n @if (title()) {\n <h2 class=\"action-list__title\">{{ title() }}</h2>\n }\n\n @for (action of actions(); track $index) {\n <button class=\"action-list__item\" [class.action-list__item--danger]=\"action.isDanger\" [class.action-list__item--navigable]=\"action.isNavigable\" (click)=\"onActionClick(action)\">\n\n <div class=\"action-list__info\">\n <span class=\"action-list__label\">{{ action.label }}</span>\n @if (action.description) {\n <span class=\"action-list__description\">{{ action.description }}</span>\n }\n </div>\n\n @if (action.icon || action.isNavigable) {\n <c80-icon [button]=\"!action.isNavigable\" [icon]=\"action.icon || (action.isNavigable ? 'arrowRight' : '')\" [color]=\"action.isDanger ? 'warn' : undefined\" [title]=\"action.label\" />\n }\n </button>\n }\n</div>"]}
|
|
@@ -1,45 +1,34 @@
|
|
|
1
1
|
import { Component, input, computed, ChangeDetectionStrategy } from '@angular/core';
|
|
2
2
|
import * as i0 from "@angular/core";
|
|
3
|
+
let uniqueIdCounter = 0;
|
|
3
4
|
/**
|
|
4
5
|
*
|
|
5
6
|
*/
|
|
6
7
|
export class CardLevelComponent {
|
|
7
|
-
/* v8 ignore next */
|
|
8
8
|
cardLevelData = input.required(...(ngDevMode ? [{ debugName: "cardLevelData" }] : /* istanbul ignore next */ []));
|
|
9
|
-
/* v8 ignore next */
|
|
10
9
|
size = input(1, ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ [])); // Multiplicador del tamaño base (220px)
|
|
11
10
|
// ID único generado para el componente
|
|
12
|
-
|
|
13
|
-
generatedId = computed(() => {
|
|
14
|
-
const label = this.cardLevelData().label.toLowerCase().replaceAll(/\s+/g, '-');
|
|
15
|
-
const randomNum = Math.floor(Math.random() * 10000);
|
|
16
|
-
return `${label}-${randomNum}`;
|
|
17
|
-
}, ...(ngDevMode ? [{ debugName: "generatedId" }] : /* istanbul ignore next */ []));
|
|
11
|
+
generatedId = `c80-card-level-${++uniqueIdCounter}`;
|
|
18
12
|
// Ancho calculado de la card
|
|
19
|
-
/* v8 ignore next 4 */
|
|
20
13
|
cardWidth = computed(() => {
|
|
21
14
|
const baseWidth = 140;
|
|
22
15
|
const calculatedWidth = baseWidth * this.size();
|
|
23
16
|
return `${calculatedWidth}px`;
|
|
24
17
|
}, ...(ngDevMode ? [{ debugName: "cardWidth" }] : /* istanbul ignore next */ []));
|
|
25
18
|
// Detecta si el rango es bidireccional (min negativo, max positivo)
|
|
26
|
-
/* v8 ignore next 4 */
|
|
27
19
|
isBidirectional = computed(() => {
|
|
28
20
|
const data = this.cardLevelData();
|
|
29
21
|
return data.min < 0 && data.max > 0;
|
|
30
22
|
}, ...(ngDevMode ? [{ debugName: "isBidirectional" }] : /* istanbul ignore next */ []));
|
|
31
23
|
// Calcula el porcentaje de fill para barras bidireccionales
|
|
32
|
-
/* v8 ignore next 4 */
|
|
33
24
|
bidirectionalFillPercent = computed(() => {
|
|
34
25
|
const data = this.cardLevelData();
|
|
35
26
|
const totalRange = data.max - data.min;
|
|
36
27
|
return Math.abs(data.value / totalRange) * 100;
|
|
37
28
|
}, ...(ngDevMode ? [{ debugName: "bidirectionalFillPercent" }] : /* istanbul ignore next */ []));
|
|
38
29
|
// Determina la dirección del fill (left/right)
|
|
39
|
-
/* v8 ignore next 3 */
|
|
40
30
|
fillDirection = computed(() => this.cardLevelData().value >= 0 ? 'right' : 'left', ...(ngDevMode ? [{ debugName: "fillDirection" }] : /* istanbul ignore next */ []));
|
|
41
31
|
// Color según el valor y umbrales
|
|
42
|
-
/* v8 ignore next 11 */
|
|
43
32
|
fillColor = computed(() => {
|
|
44
33
|
const data = this.cardLevelData();
|
|
45
34
|
const absValue = Math.abs(data.value);
|
|
@@ -54,10 +43,10 @@ export class CardLevelComponent {
|
|
|
54
43
|
return 'var(--color-success)';
|
|
55
44
|
}, ...(ngDevMode ? [{ debugName: "fillColor" }] : /* istanbul ignore next */ []));
|
|
56
45
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: CardLevelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
57
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: CardLevelComponent, isStandalone: true, selector: "c80-card-level", inputs: { cardLevelData: { classPropertyName: "cardLevelData", publicName: "cardLevelData", isSignal: true, isRequired: true, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"card-level-container\" [style.width]=\"cardWidth()\">\r\n <label [for]=\"generatedId
|
|
46
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: CardLevelComponent, isStandalone: true, selector: "c80-card-level", inputs: { cardLevelData: { classPropertyName: "cardLevelData", publicName: "cardLevelData", isSignal: true, isRequired: true, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"card-level-container\" [style.width]=\"cardWidth()\">\r\n <label [for]=\"generatedId\">\r\n <strong>{{ cardLevelData().label }}</strong>\r\n\r\n <span class=\"value-display\">\r\n @if (isBidirectional()) {\r\n <!-- Barra bidireccional desde el centro -->\r\n <div class=\"bidirectional-meter\">\r\n <div class=\"meter-track\">\r\n <div class=\"center-line\"></div>\r\n <div class=\"meter-fill\" [class.fill-left]=\"fillDirection() === 'left'\" [class.fill-right]=\"fillDirection() === 'right'\" [style.width.%]=\"bidirectionalFillPercent()\" [style.background]=\"fillColor()\">\r\n </div>\r\n </div>\r\n </div>\r\n } @else {\r\n <!-- Meter est\u00E1ndar para rangos unidireccionales -->\r\n <meter [id]=\"generatedId\" [optimum]=\"cardLevelData().optimum\" [min]=\"cardLevelData().min\" [max]=\"cardLevelData().max\" [low]=\"cardLevelData().low\" [high]=\"cardLevelData().high\" [value]=\"cardLevelData().value\">\r\n </meter>\r\n }\r\n\r\n {{ cardLevelData().value }}{{ cardLevelData().unit }}\r\n </span>\r\n </label>\r\n</div>\r\n", styles: [".card-level-container{background:#fff;border-radius:5px;padding:8px;margin:4px;box-shadow:0 4px 6px #0000001a,0 1px 3px #00000014;min-width:100px;flex:1}.card-level-container label{display:flex;flex-direction:column;margin:0}.card-level-container label strong{font-size:12px;font-weight:600;color:#2d3748;text-transform:uppercase;letter-spacing:.5px;margin-bottom:4px}.card-level-container label .value-display{font-size:12px;font-weight:600;color:#1a202c;text-align:center;padding:0 5px;background:#4299e11a;border-radius:5px}.card-level-container label .bidirectional-meter{width:100%;margin-bottom:4px}.card-level-container label .bidirectional-meter .meter-track{position:relative;width:100%;height:10px;background:#e2e8f0;border-radius:12px;box-shadow:inset 0 2px 4px #0000001a;overflow:hidden}.card-level-container label .bidirectional-meter .meter-track .center-line{position:absolute;left:50%;top:0;bottom:0;width:4px;background:#4a5568;transform:translate(-50%);z-index:1}.card-level-container label .bidirectional-meter .meter-track .meter-fill{position:absolute;top:0;bottom:0;border-radius:5px}.card-level-container label .bidirectional-meter .meter-track .meter-fill.fill-right{left:50%}.card-level-container label .bidirectional-meter .meter-track .meter-fill.fill-left{right:50%}.card-level-container label meter{width:100%;border-radius:12px;border:none;background:#e2e8f0;margin-bottom:4px}.card-level-container label meter::-webkit-meter-bar{background:#e2e8f0;border-radius:12px;box-shadow:inset 0 2px 4px #0000001a}.card-level-container label meter::-webkit-meter-optimum-value{background:linear-gradient(90deg,#48bb78,#38a169);border-radius:12px}.card-level-container label meter::-webkit-meter-suboptimum-value{background:linear-gradient(90deg,#ed8936,#dd6b20);border-radius:12px}.card-level-container label meter::-webkit-meter-even-less-good-value{background:linear-gradient(90deg,#f56565,#e53e3e);border-radius:12px}.card-level-container label meter::-moz-meter-bar{background:linear-gradient(90deg,#48bb78,#38a169);border-radius:12px}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
58
47
|
}
|
|
59
48
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: CardLevelComponent, decorators: [{
|
|
60
49
|
type: Component,
|
|
61
|
-
args: [{ selector: 'c80-card-level',
|
|
50
|
+
args: [{ selector: 'c80-card-level', imports: [], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"card-level-container\" [style.width]=\"cardWidth()\">\r\n <label [for]=\"generatedId\">\r\n <strong>{{ cardLevelData().label }}</strong>\r\n\r\n <span class=\"value-display\">\r\n @if (isBidirectional()) {\r\n <!-- Barra bidireccional desde el centro -->\r\n <div class=\"bidirectional-meter\">\r\n <div class=\"meter-track\">\r\n <div class=\"center-line\"></div>\r\n <div class=\"meter-fill\" [class.fill-left]=\"fillDirection() === 'left'\" [class.fill-right]=\"fillDirection() === 'right'\" [style.width.%]=\"bidirectionalFillPercent()\" [style.background]=\"fillColor()\">\r\n </div>\r\n </div>\r\n </div>\r\n } @else {\r\n <!-- Meter est\u00E1ndar para rangos unidireccionales -->\r\n <meter [id]=\"generatedId\" [optimum]=\"cardLevelData().optimum\" [min]=\"cardLevelData().min\" [max]=\"cardLevelData().max\" [low]=\"cardLevelData().low\" [high]=\"cardLevelData().high\" [value]=\"cardLevelData().value\">\r\n </meter>\r\n }\r\n\r\n {{ cardLevelData().value }}{{ cardLevelData().unit }}\r\n </span>\r\n </label>\r\n</div>\r\n", styles: [".card-level-container{background:#fff;border-radius:5px;padding:8px;margin:4px;box-shadow:0 4px 6px #0000001a,0 1px 3px #00000014;min-width:100px;flex:1}.card-level-container label{display:flex;flex-direction:column;margin:0}.card-level-container label strong{font-size:12px;font-weight:600;color:#2d3748;text-transform:uppercase;letter-spacing:.5px;margin-bottom:4px}.card-level-container label .value-display{font-size:12px;font-weight:600;color:#1a202c;text-align:center;padding:0 5px;background:#4299e11a;border-radius:5px}.card-level-container label .bidirectional-meter{width:100%;margin-bottom:4px}.card-level-container label .bidirectional-meter .meter-track{position:relative;width:100%;height:10px;background:#e2e8f0;border-radius:12px;box-shadow:inset 0 2px 4px #0000001a;overflow:hidden}.card-level-container label .bidirectional-meter .meter-track .center-line{position:absolute;left:50%;top:0;bottom:0;width:4px;background:#4a5568;transform:translate(-50%);z-index:1}.card-level-container label .bidirectional-meter .meter-track .meter-fill{position:absolute;top:0;bottom:0;border-radius:5px}.card-level-container label .bidirectional-meter .meter-track .meter-fill.fill-right{left:50%}.card-level-container label .bidirectional-meter .meter-track .meter-fill.fill-left{right:50%}.card-level-container label meter{width:100%;border-radius:12px;border:none;background:#e2e8f0;margin-bottom:4px}.card-level-container label meter::-webkit-meter-bar{background:#e2e8f0;border-radius:12px;box-shadow:inset 0 2px 4px #0000001a}.card-level-container label meter::-webkit-meter-optimum-value{background:linear-gradient(90deg,#48bb78,#38a169);border-radius:12px}.card-level-container label meter::-webkit-meter-suboptimum-value{background:linear-gradient(90deg,#ed8936,#dd6b20);border-radius:12px}.card-level-container label meter::-webkit-meter-even-less-good-value{background:linear-gradient(90deg,#f56565,#e53e3e);border-radius:12px}.card-level-container label meter::-moz-meter-bar{background:linear-gradient(90deg,#48bb78,#38a169);border-radius:12px}\n"] }]
|
|
62
51
|
}], propDecorators: { cardLevelData: [{ type: i0.Input, args: [{ isSignal: true, alias: "cardLevelData", required: true }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }] } });
|
|
63
52
|
//# sourceMappingURL=card-level.component.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"card-level.component.js","sourceRoot":"","sources":["../../../../../libs/ui/src/lib/card-level/card-level.component.ts","../../../../../libs/ui/src/lib/card-level/card-level.component.html"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;;AAGpF;;GAEG;
|
|
1
|
+
{"version":3,"file":"card-level.component.js","sourceRoot":"","sources":["../../../../../libs/ui/src/lib/card-level/card-level.component.ts","../../../../../libs/ui/src/lib/card-level/card-level.component.html"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;;AAGpF,IAAI,eAAe,GAAG,CAAC,CAAC;AAExB;;GAEG;AAQH,MAAM,OAAO,kBAAkB;IAC7B,aAAa,GAAG,KAAK,CAAC,QAAQ,mFAAiB,CAAC;IAChD,IAAI,GAAG,KAAK,CAAS,CAAC,2EAAC,CAAC,CAAC,wCAAwC;IAEjE,uCAAuC;IAC9B,WAAW,GAAG,kBAAkB,EAAE,eAAe,EAAE,CAAC;IAE7D,6BAA6B;IACpB,SAAS,GAAG,QAAQ,CAAC,GAAG,EAAE;QACjC,MAAM,SAAS,GAAG,GAAG,CAAC;QACtB,MAAM,eAAe,GAAG,SAAS,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAChD,OAAO,GAAG,eAAe,IAAI,CAAC;IAChC,CAAC,gFAAC,CAAC;IAEH,oEAAoE;IACpE,eAAe,GAAG,QAAQ,CAAC,GAAG,EAAE;QAC9B,MAAM,IAAI,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC;QAClC,OAAO,IAAI,CAAC,GAAG,GAAG,CAAC,IAAI,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC;IACtC,CAAC,sFAAC,CAAC;IAEH,4DAA4D;IAC5D,wBAAwB,GAAG,QAAQ,CAAC,GAAG,EAAE;QACvC,MAAM,IAAI,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC;QAClC,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC;QACvC,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,GAAG,UAAU,CAAC,GAAG,GAAG,CAAC;IACjD,CAAC,+FAAC,CAAC;IAEH,+CAA+C;IAC/C,aAAa,GAAG,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,oFAAC,CAAC;IAEnF,kCAAkC;IAClC,SAAS,GAAG,QAAQ,CAAC,GAAG,EAAE;QACxB,MAAM,IAAI,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC;QAClC,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACtC,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACpC,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAElC,IAAI,QAAQ,IAAI,OAAO,EAAE,CAAC;YACxB,OAAO,qBAAqB,CAAC;QAC/B,CAAC;QACD,IAAI,QAAQ,IAAI,MAAM,EAAE,CAAC;YACvB,OAAO,sBAAsB,CAAC;QAChC,CAAC;QACD,OAAO,sBAAsB,CAAC;IAChC,CAAC,gFAAC,CAAC;wGA5CQ,kBAAkB;4FAAlB,kBAAkB,0VCf/B,8nCAwBA;;4FDTa,kBAAkB;kBAP9B,SAAS;+BACE,gBAAgB,WACjB,EAAE,mBAGM,uBAAuB,CAAC,MAAM","sourcesContent":["import { Component, input, computed, ChangeDetectionStrategy } from '@angular/core';\r\nimport type { CardLevelData } from './card-level.interface';\r\n\r\nlet uniqueIdCounter = 0;\r\n\r\n/**\r\n *\r\n */\r\n@Component({\r\n selector: 'c80-card-level',\r\n imports: [],\r\n templateUrl: './card-level.component.html',\r\n styleUrl: './card-level.component.scss',\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n})\r\nexport class CardLevelComponent {\r\n cardLevelData = input.required<CardLevelData>();\r\n size = input<number>(1); // Multiplicador del tamaño base (220px)\r\n\r\n // ID único generado para el componente\r\n readonly generatedId = `c80-card-level-${++uniqueIdCounter}`;\r\n\r\n // Ancho calculado de la card\r\n readonly cardWidth = computed(() => {\r\n const baseWidth = 140;\r\n const calculatedWidth = baseWidth * this.size();\r\n return `${calculatedWidth}px`;\r\n });\r\n\r\n // Detecta si el rango es bidireccional (min negativo, max positivo)\r\n isBidirectional = computed(() => {\r\n const data = this.cardLevelData();\r\n return data.min < 0 && data.max > 0;\r\n });\r\n\r\n // Calcula el porcentaje de fill para barras bidireccionales\r\n bidirectionalFillPercent = computed(() => {\r\n const data = this.cardLevelData();\r\n const totalRange = data.max - data.min;\r\n return Math.abs(data.value / totalRange) * 100;\r\n });\r\n\r\n // Determina la dirección del fill (left/right)\r\n fillDirection = computed(() => this.cardLevelData().value >= 0 ? 'right' : 'left');\r\n\r\n // Color según el valor y umbrales\r\n fillColor = computed(() => {\r\n const data = this.cardLevelData();\r\n const absValue = Math.abs(data.value);\r\n const absHigh = Math.abs(data.high);\r\n const absLow = Math.abs(data.low);\r\n\r\n if (absValue >= absHigh) {\r\n return 'var(--color-danger)';\r\n }\r\n if (absValue >= absLow) {\r\n return 'var(--color-warning)';\r\n }\r\n return 'var(--color-success)';\r\n });\r\n}\r\n","<div class=\"card-level-container\" [style.width]=\"cardWidth()\">\r\n <label [for]=\"generatedId\">\r\n <strong>{{ cardLevelData().label }}</strong>\r\n\r\n <span class=\"value-display\">\r\n @if (isBidirectional()) {\r\n <!-- Barra bidireccional desde el centro -->\r\n <div class=\"bidirectional-meter\">\r\n <div class=\"meter-track\">\r\n <div class=\"center-line\"></div>\r\n <div class=\"meter-fill\" [class.fill-left]=\"fillDirection() === 'left'\" [class.fill-right]=\"fillDirection() === 'right'\" [style.width.%]=\"bidirectionalFillPercent()\" [style.background]=\"fillColor()\">\r\n </div>\r\n </div>\r\n </div>\r\n } @else {\r\n <!-- Meter estándar para rangos unidireccionales -->\r\n <meter [id]=\"generatedId\" [optimum]=\"cardLevelData().optimum\" [min]=\"cardLevelData().min\" [max]=\"cardLevelData().max\" [low]=\"cardLevelData().low\" [high]=\"cardLevelData().high\" [value]=\"cardLevelData().value\">\r\n </meter>\r\n }\r\n\r\n {{ cardLevelData().value }}{{ cardLevelData().unit }}\r\n </span>\r\n </label>\r\n</div>\r\n"]}
|
|
@@ -36,6 +36,6 @@ export class ErrorNotificationComponent {
|
|
|
36
36
|
}
|
|
37
37
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: ErrorNotificationComponent, decorators: [{
|
|
38
38
|
type: Component,
|
|
39
|
-
args: [{ selector: 'c80-error-notification',
|
|
39
|
+
args: [{ selector: 'c80-error-notification', imports: [IconComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"error-notification\">\n <div class=\"error-notification__content\">\n <c80-icon icon=\"error\" [size]=\"1.2\" />\n <div class=\"error-notification__message\">\n @for (sentence of sentences(); track $index) {\n <p>{{ sentence }}</p>\n }\n </div>\n </div>\n @if (config().dismissible !== false) {\n <c80-icon icon=\"close\" [size]=\"0.8\" (click)=\"onDismiss()\" (keydown.enter)=\"onDismiss()\" class=\"error-notification__close\" aria-label=\"Cerrar notificaci\u00F3n\" />\n }\n</div>", styles: [".error-notification{display:flex;align-items:center;justify-content:space-between;gap:var(--spacing-sm);padding:var(--spacing-md);background:#8b000014;border:1px solid rgba(139,68,68,.2);border-radius:var(--border-radius-lg);color:#8b0000;animation:slideDown .3s cubic-bezier(.4,0,.2,1)}.error-notification p{margin:0;font-size:.875rem;line-height:1.5;font-weight:var(--font-weight-medium)}.error-notification__message{display:flex;flex-direction:column}.error-notification__content{display:flex;align-items:center;gap:var(--spacing-sm);flex:1}.error-notification__content c80-icon{flex-shrink:0}.error-notification__close{flex-shrink:0;cursor:pointer;opacity:.7;transition:opacity .2s}.error-notification__close:hover{opacity:1}@keyframes slideDown{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}\n"] }]
|
|
40
40
|
}], propDecorators: { config: [{ type: i0.Input, args: [{ isSignal: true, alias: "config", required: true }] }], dismiss: [{ type: i0.Output, args: ["dismiss"] }] } });
|
|
41
41
|
//# sourceMappingURL=error-notification.component.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error-notification.component.js","sourceRoot":"","sources":["../../../../../libs/ui/src/lib/error-notification/error-notification.component.ts","../../../../../libs/ui/src/lib/error-notification/error-notification.component.html"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,SAAS,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAC5F,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;;AAGxC;;GAEG;
|
|
1
|
+
{"version":3,"file":"error-notification.component.js","sourceRoot":"","sources":["../../../../../libs/ui/src/lib/error-notification/error-notification.component.ts","../../../../../libs/ui/src/lib/error-notification/error-notification.component.html"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,SAAS,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAC5F,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;;AAGxC;;GAEG;AAQH,MAAM,OAAO,0BAA0B;IACnC,MAAM,GAAG,KAAK,CAAC,QAAQ,4EAA2B,CAAC;IAEnD,OAAO,GAAG,MAAM,EAAQ,CAAC;IAEzB;;;OAGG;IACgB,SAAS,GAAG,QAAQ,CAAC,GAAG,EAAE;QACzC,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC;QACtC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAElC,8CAA8C;QAC9C,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACrB,OAAO,CAAC,OAAO,CAAC,CAAC;QACrB,CAAC;QAED,wEAAwE;QACxE,IAAI,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC;YACtB,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC;QACtD,CAAC;QAED,qDAAqD;QACrD,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAC7B,KAAK,KAAK,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,CACjD,CAAC;IACN,CAAC,gFAAC,CAAC;IAEH;;OAEG;IACO,SAAS;QACf,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;IACxB,CAAC;wGAlCQ,0BAA0B;4FAA1B,0BAA0B,yPCdvC,ujBAYM,y3BDHQ,aAAa;;4FAKd,0BAA0B;kBAPtC,SAAS;+BACI,wBAAwB,WACzB,CAAC,aAAa,CAAC,mBAGP,uBAAuB,CAAC,MAAM","sourcesContent":["import { ChangeDetectionStrategy, Component, computed, input, output } from '@angular/core';\r\nimport { IconComponent } from '../icon';\r\nimport type { ErrorNotificationConfig } from './error-notification.types';\r\n\r\n/**\r\n * Componente reutilizable para mostrar mensajes de error\r\n */\r\n@Component({\r\n selector: 'c80-error-notification',\r\n imports: [IconComponent],\r\n templateUrl: './error-notification.component.html',\r\n styleUrl: './error-notification.component.scss',\r\n changeDetection: ChangeDetectionStrategy.OnPush\r\n})\r\nexport class ErrorNotificationComponent {\r\n config = input.required<ErrorNotificationConfig>();\r\n\r\n dismiss = output<void>();\r\n\r\n /**\r\n * Divide el mensaje en oraciones separadas por '. '\r\n * Excluye la última oración si termina en punto\r\n */\r\n protected readonly sentences = computed(() => {\r\n const message = this.config().message;\r\n const parts = message.split('. ');\r\n\r\n // Si solo hay una parte, devolverla como está\r\n if (parts.length === 1) {\r\n return [message];\r\n }\r\n\r\n // Si la última parte está vacía (mensaje terminaba en '. '), eliminarla\r\n if (parts.at(-1) === '') {\r\n return parts.slice(0, -1).map(part => `${part}.`);\r\n }\r\n\r\n // Agregar punto a todas las partes excepto la última\r\n return parts.map((part, index) =>\r\n index === parts.length - 1 ? part : `${part}.`\r\n );\r\n });\r\n\r\n /**\r\n * Emite evento de cierre\r\n */\r\n protected onDismiss(): void {\r\n this.dismiss.emit();\r\n }\r\n}\r\n","<div class=\"error-notification\">\n <div class=\"error-notification__content\">\n <c80-icon icon=\"error\" [size]=\"1.2\" />\n <div class=\"error-notification__message\">\n @for (sentence of sentences(); track $index) {\n <p>{{ sentence }}</p>\n }\n </div>\n </div>\n @if (config().dismissible !== false) {\n <c80-icon icon=\"close\" [size]=\"0.8\" (click)=\"onDismiss()\" (keydown.enter)=\"onDismiss()\" class=\"error-notification__close\" aria-label=\"Cerrar notificación\" />\n }\n</div>"]}
|
|
@@ -20,6 +20,6 @@ export class HeaderComponent {
|
|
|
20
20
|
}
|
|
21
21
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: HeaderComponent, decorators: [{
|
|
22
22
|
type: Component,
|
|
23
|
-
args: [{ selector: 'c80-header',
|
|
23
|
+
args: [{ selector: 'c80-header', imports: [IconComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<header class=\"header\" [class.header--no-border]=\"config().showBorder === false\">\n @if (config().showBackButton !== false) {\n <c80-icon button [icon]=\"backIcon()\" [size]=\"1.2\" (iconClick)=\"onBackClick()\" title=\"Volver\" class=\"header__back-btn\" />\n }\n <h1 class=\"header__title\">{{ config().title }}</h1>\n <div class=\"header__actions\">\n <ng-content />\n </div>\n</header>", styles: [".header{display:flex;align-items:center;justify-content:space-between;gap:1rem;margin-bottom:1rem;padding:1rem;border-bottom:1px solid var(--color-border)}.header--no-border{border-bottom:none}.header__back-btn{flex-shrink:0}.header__title{font-size:1.5rem;font-weight:600;color:var(--color-text-primary);margin:0;-webkit-user-select:none;user-select:none}.header__actions{display:flex;align-items:center;gap:var(--spacing-sm);flex-shrink:0;margin-left:auto}@media(max-width:768px){.header{padding:.75rem;gap:.75rem;margin-bottom:.5rem}.header__title{font-size:1.25rem}}\n"] }]
|
|
24
24
|
}], propDecorators: { config: [{ type: i0.Input, args: [{ isSignal: true, alias: "config", required: true }] }], backIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "backIcon", required: false }] }] } });
|
|
25
25
|
//# sourceMappingURL=header.component.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"header.component.js","sourceRoot":"","sources":["../../../../../libs/ui/src/lib/header/header.component.ts","../../../../../libs/ui/src/lib/header/header.component.html"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,EAAE,uBAAuB,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAClF,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;;AAIxC;;GAEG;
|
|
1
|
+
{"version":3,"file":"header.component.js","sourceRoot":"","sources":["../../../../../libs/ui/src/lib/header/header.component.ts","../../../../../libs/ui/src/lib/header/header.component.html"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,EAAE,uBAAuB,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAClF,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;;AAIxC;;GAEG;AAQH,MAAM,OAAO,eAAe;IACP,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;IAE7C,MAAM,GAAG,KAAK,CAAC,QAAQ,4EAAgB,CAAC;IACxC,QAAQ,GAAG,KAAK,CAAW,aAAa,+EAAC,CAAC;IAE1C;;OAEG;IACO,WAAW;QACjB,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;IACzB,CAAC;wGAXQ,eAAe;4FAAf,eAAe,6UChB5B,oaAQS,snBDGK,aAAa;;4FAKd,eAAe;kBAP3B,SAAS;+BACI,YAAY,WACb,CAAC,aAAa,CAAC,mBAGP,uBAAuB,CAAC,MAAM","sourcesContent":["import { Location } from '@angular/common';\r\nimport { ChangeDetectionStrategy, Component, inject, input } from '@angular/core';\r\nimport { IconComponent } from '../icon';\r\nimport type { IconType } from '../icon';\r\nimport type { HeaderConfig } from './header.types';\r\n\r\n/**\r\n *\r\n */\r\n@Component({\r\n selector: 'c80-header',\r\n imports: [IconComponent],\r\n templateUrl: './header.component.html',\r\n styleUrl: './header.component.scss',\r\n changeDetection: ChangeDetectionStrategy.OnPush\r\n})\r\nexport class HeaderComponent {\r\n private readonly location = inject(Location);\r\n\r\n config = input.required<HeaderConfig>();\r\n backIcon = input<IconType>('chevronLeft');\r\n\r\n /**\r\n *\r\n */\r\n protected onBackClick(): void {\r\n this.location.back();\r\n }\r\n}","<header class=\"header\" [class.header--no-border]=\"config().showBorder === false\">\n @if (config().showBackButton !== false) {\n <c80-icon button [icon]=\"backIcon()\" [size]=\"1.2\" (iconClick)=\"onBackClick()\" title=\"Volver\" class=\"header__back-btn\" />\n }\n <h1 class=\"header__title\">{{ config().title }}</h1>\n <div class=\"header__actions\">\n <ng-content />\n </div>\n</header>"]}
|
|
@@ -10,27 +10,16 @@ import * as i0 from "@angular/core";
|
|
|
10
10
|
*/
|
|
11
11
|
export class IconComponent {
|
|
12
12
|
themeService = inject(ThemeService);
|
|
13
|
-
/* v8 ignore next */
|
|
14
13
|
icon = input('check', ...(ngDevMode ? [{ debugName: "icon" }] : /* istanbul ignore next */ [])); // Tipo de icono a mostrar
|
|
15
|
-
/* v8 ignore next */
|
|
16
14
|
color = input(undefined, ...(ngDevMode ? [{ debugName: "color" }] : /* istanbul ignore next */ [])); // Color del icono (primary, secondary, warn, success)
|
|
17
|
-
/* v8 ignore next */
|
|
18
15
|
customColor = input(undefined, ...(ngDevMode ? [{ debugName: "customColor" }] : /* istanbul ignore next */ [])); // Color personalizado (sobrescribe color)
|
|
19
|
-
/* v8 ignore next */
|
|
20
16
|
disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ [])); // Estado deshabilitado
|
|
21
|
-
/* v8 ignore next */
|
|
22
17
|
size = input(1, ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ [])); // Multiplicador de tamaño (1 = 24px)
|
|
23
|
-
/* v8 ignore next */
|
|
24
18
|
button = input(false, { ...(ngDevMode ? { debugName: "button" } : /* istanbul ignore next */ {}), transform: transformToBoolean }); // Renderiza como botón clickeable
|
|
25
|
-
/* v8 ignore next */
|
|
26
19
|
border = input(false, { ...(ngDevMode ? { debugName: "border" } : /* istanbul ignore next */ {}), transform: transformToBoolean }); // Agrega borde al wrapper
|
|
27
|
-
/* v8 ignore next */
|
|
28
20
|
type = input('button', ...(ngDevMode ? [{ debugName: "type" }] : /* istanbul ignore next */ [])); // Tipo de botón (button, submit, reset)
|
|
29
|
-
/* v8 ignore next */
|
|
30
21
|
textLeft = input(undefined, ...(ngDevMode ? [{ debugName: "textLeft" }] : /* istanbul ignore next */ [])); // Texto a la izquierda del icono
|
|
31
|
-
/* v8 ignore next */
|
|
32
22
|
textRight = input(undefined, ...(ngDevMode ? [{ debugName: "textRight" }] : /* istanbul ignore next */ [])); // Texto a la derecha del icono
|
|
33
|
-
/* v8 ignore next */
|
|
34
23
|
dark = input(false, { ...(ngDevMode ? { debugName: "dark" } : /* istanbul ignore next */ {}), transform: transformToBoolean }); // Modo oscuro
|
|
35
24
|
iconClick = output(); // Evento emitido al hacer click (solo si button=true)
|
|
36
25
|
iconSize = computed(() => BASE_ICON_SIZE * this.size(), ...(ngDevMode ? [{ debugName: "iconSize" }] : /* istanbul ignore next */ []));
|
|
@@ -80,6 +69,6 @@ export class IconComponent {
|
|
|
80
69
|
}
|
|
81
70
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: IconComponent, decorators: [{
|
|
82
71
|
type: Component,
|
|
83
|
-
args: [{ selector: 'c80-icon',
|
|
72
|
+
args: [{ selector: 'c80-icon', imports: [NgTemplateOutlet], changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-template #svgContent>\r\n <svg [attr.width]=\"iconSize()\" [attr.height]=\"iconSize()\" viewBox=\"0 0 24 24\" fill=\"none\" [style.opacity]=\"iconOpacity()\">\r\n @for (shape of additionalShapes(); track $index) {\r\n @if (shape.type === 'circle') {\r\n <circle [attr.cx]=\"shape.cx\" [attr.cy]=\"shape.cy\" [attr.r]=\"shape.r\" [attr.stroke]=\"iconColor()\" [attr.stroke-width]=\"svgStrokeAttrs.strokeWidth\" [attr.fill]=\"shape.fill === 'color' ? iconColor() : 'none'\" />\r\n } @else if (shape.type === 'rect') {\r\n <rect [attr.x]=\"shape.x\" [attr.y]=\"shape.y\" [attr.width]=\"shape.width\" [attr.height]=\"shape.height\" [attr.rx]=\"shape.rx || null\" [attr.stroke]=\"iconColor()\" [attr.stroke-width]=\"svgStrokeAttrs.strokeWidth\"\r\n [attr.fill]=\"shape.fill === 'color' ? iconColor() : 'none'\" />\r\n } @else if (shape.type === 'path') {\r\n <path [attr.d]=\"shape.d\" [attr.stroke]=\"iconColor()\" [attr.stroke-width]=\"svgStrokeAttrs.strokeWidth\" [attr.stroke-linecap]=\"svgStrokeAttrs.strokeLinecap\" [attr.stroke-linejoin]=\"svgStrokeAttrs.strokeLinejoin\"\r\n [attr.fill]=\"shape.fill === 'color' ? iconColor() : 'none'\" />\r\n }\r\n }\r\n\r\n @if (hasMultiPaths()) {\r\n <!-- Iconos con m\u00FAltiples paths y colores personalizados -->\r\n @for (path of multiColorIcon()!.paths; track $index) {\r\n <path [attr.d]=\"path\" [attr.fill]=\"multiColorIcon()!.colors[$index] || iconColor()\" stroke=\"none\" />\r\n }\r\n } @else {\r\n <!-- Iconos con un solo path -->\r\n <path [attr.d]=\"iconPath()\" [attr.stroke]=\"iconColor()\" [attr.fill]=\"shouldFillIcon() ? iconColor() : 'none'\" [attr.stroke-width]=\"iconStrokeWidth()\" [attr.stroke-linecap]=\"svgStrokeAttrs.strokeLinecap\"\r\n [attr.stroke-linejoin]=\"svgStrokeAttrs.strokeLinejoin\" />\r\n }\r\n </svg>\r\n</ng-template>\r\n\r\n<ng-template #textContent>\r\n @if (textLeft()) {\r\n <span class=\"icon-text ms-3\">{{ textLeft() }}</span>\r\n }\r\n <span class=\"icon-content\" [style.width.px]=\"iconSize() + 8\" [style.height.px]=\"iconSize() + 8\">\r\n <ng-container *ngTemplateOutlet=\"svgContent\" />\r\n </span>\r\n @if (textRight()) {\r\n <span class=\"icon-text me-3\">{{ textRight() }}</span>\r\n }\r\n</ng-template>\r\n\r\n@if (button()) {\r\n<button [type]=\"type()\" [disabled]=\"disabled()\" class=\"icon-wrapper\" [class.icon-wrapper-border]=\"border()\" [class.dark-mode]=\"isDarkMode()\" (click)=\"onButtonClick($event)\">\r\n <ng-container *ngTemplateOutlet=\"textContent\" />\r\n</button>\r\n} @else {\r\n<span class=\"icon-wrapper\" [class.icon-wrapper-border]=\"border()\" [class.dark-mode]=\"isDarkMode()\">\r\n <ng-container *ngTemplateOutlet=\"textContent\" />\r\n</span>\r\n}", styles: [":host{--icon-border-color: #e5e7eb;--icon-border-color-hover: #d1d5db;--icon-border-color-disabled: #f3f4f6;--icon-bg-hover: #f3f4f6;--icon-bg-active: #e5e7eb;--icon-text-color: #111827;--icon-border-radius: 4px;--icon-transition: .2s}:host .dark-mode{--icon-border-color: #374151;--icon-border-color-hover: #4b5563;--icon-border-color-disabled: #1f2937;--icon-bg-hover: #374151;--icon-bg-active: #4b5563;--icon-text-color: #f9fafb}:host .icon-wrapper{display:inline-flex;align-items:center;gap:8px;background:transparent;padding:0;border:none;outline:none;cursor:pointer;transition:opacity var(--icon-transition)}:host button.icon-wrapper:focus-visible{outline:none;outline-offset:2px;border-radius:var(--icon-border-radius)}:host button.icon-wrapper:disabled{opacity:.5;cursor:default}:host .icon-content{display:inline-flex;align-items:center;justify-content:center;border-radius:50%;min-width:0;min-height:0;padding:4px;margin:0 4px;transition:background var(--icon-transition);box-sizing:border-box}:host button.icon-wrapper:hover:not(:disabled) .icon-content{background:var(--icon-bg-hover)}:host button.icon-wrapper:active:not(:disabled) .icon-content{background:var(--icon-bg-active)}:host .icon-text{font-size:14px;line-height:1;white-space:nowrap;-webkit-user-select:none;user-select:none;color:var(--icon-text-color)}:host .icon-wrapper-border{border:1px solid var(--icon-border-color);border-radius:var(--icon-border-radius);padding:4px 8px}:host button.icon-wrapper-border:hover:not(:disabled){border-color:var(--icon-border-color-hover)}:host button.icon-wrapper-border:disabled{border-color:var(--icon-border-color-disabled)}\n"] }]
|
|
84
73
|
}], propDecorators: { icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], customColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "customColor", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], button: [{ type: i0.Input, args: [{ isSignal: true, alias: "button", required: false }] }], border: [{ type: i0.Input, args: [{ isSignal: true, alias: "border", required: false }] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], textLeft: [{ type: i0.Input, args: [{ isSignal: true, alias: "textLeft", required: false }] }], textRight: [{ type: i0.Input, args: [{ isSignal: true, alias: "textRight", required: false }] }], dark: [{ type: i0.Input, args: [{ isSignal: true, alias: "dark", required: false }] }], iconClick: [{ type: i0.Output, args: ["iconClick"] }] } });
|
|
85
74
|
//# sourceMappingURL=icon.component.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"icon.component.js","sourceRoot":"","sources":["../../../../../libs/ui/src/lib/icon/icon.component.ts","../../../../../libs/ui/src/lib/icon/icon.component.html"],"names":[],"mappings":"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","sourcesContent":["import { NgTemplateOutlet } from '@angular/common';\r\nimport { Component, input, output, computed, ChangeDetectionStrategy, inject } from '@angular/core';\r\nimport {\r\n BASE_ICON_SIZE,\r\n BASE_ICON_COLORS,\r\n DISABLED_COLOR,\r\n DEFAULT_ICON_COLOR,\r\n DISABLED_OPACITY,\r\n SECONDARY_WARN_OPACITY,\r\n DEFAULT_OPACITY,\r\n OPACITY_REDUCED_COLORS,\r\n SVG_STROKE_ATTRS,\r\n THEME_DARK_COLOR,\r\n THEME_LIGHT_COLOR,\r\n} from './icon.constants';\r\nimport { ICON_DEFINITIONS } from './icon.definitions';\r\nimport type { ColorType, ButtonType, IconType } from './icon.types';\r\nimport { transformToBoolean, shouldIconUseFill } from './icon.utils';\r\nimport { ThemeService } from './theme.service';\r\n\r\n\r\n/**\r\n *\r\n */\r\n@Component({\r\n selector: 'c80-icon',\r\n standalone: true,\r\n imports: [NgTemplateOutlet],\r\n templateUrl: './icon.component.html',\r\n styleUrls: ['./icon.component.scss'],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n})\r\nexport class IconComponent {\r\n private readonly themeService = inject(ThemeService);\r\n\r\n /* v8 ignore next */\r\n readonly icon = input<IconType>('check'); // Tipo de icono a mostrar\r\n /* v8 ignore next */\r\n readonly color = input<ColorType | undefined>(undefined); // Color del icono (primary, secondary, warn, success)\r\n /* v8 ignore next */\r\n readonly customColor = input<string | undefined>(undefined); // Color personalizado (sobrescribe color)\r\n /* v8 ignore next */\r\n readonly disabled = input(false); // Estado deshabilitado\r\n /* v8 ignore next */\r\n readonly size = input(1); // Multiplicador de tamaño (1 = 24px)\r\n /* v8 ignore next */\r\n readonly button = input(false, { transform: transformToBoolean }); // Renderiza como botón clickeable\r\n /* v8 ignore next */\r\n readonly border = input(false, { transform: transformToBoolean }); // Agrega borde al wrapper\r\n /* v8 ignore next */\r\n readonly type = input<ButtonType>('button'); // Tipo de botón (button, submit, reset)\r\n /* v8 ignore next */\r\n readonly textLeft = input<string | undefined>(undefined); // Texto a la izquierda del icono\r\n /* v8 ignore next */\r\n readonly textRight = input<string | undefined>(undefined); // Texto a la derecha del icono\r\n /* v8 ignore next */\r\n readonly dark = input(false, { transform: transformToBoolean }); // Modo oscuro\r\n\r\n readonly iconClick = output<Event>(); // Evento emitido al hacer click (solo si button=true)\r\n\r\n readonly iconSize = computed(() => BASE_ICON_SIZE * this.size());\r\n\r\n readonly isDarkMode = computed(() => this.dark() || this.themeService.isDark());\r\n\r\n readonly iconColor = computed(() => {\r\n if (this.disabled()) return DISABLED_COLOR;\r\n\r\n const custom = this.customColor();\r\n if (custom) return custom;\r\n\r\n const colorType = this.color();\r\n\r\n // Si no hay color específico, usar color adaptativo al tema\r\n if (!colorType) {\r\n return this.themeService.isDark() ? THEME_DARK_COLOR : THEME_LIGHT_COLOR;\r\n }\r\n\r\n return BASE_ICON_COLORS[colorType] ?? DEFAULT_ICON_COLOR;\r\n });\r\n\r\n readonly iconOpacity = computed(() => {\r\n if (this.disabled()) return DISABLED_OPACITY;\r\n\r\n const hasCustomColor = this.customColor() !== undefined;\r\n const colorType = this.color();\r\n const isOpacityReducedColor = colorType && OPACITY_REDUCED_COLORS.includes(colorType);\r\n\r\n return !hasCustomColor && isOpacityReducedColor ? SECONDARY_WARN_OPACITY : DEFAULT_OPACITY;\r\n });\r\n\r\n // Optimización: Un solo lookup a ICON_DEFINITIONS\r\n readonly iconDefinition = computed(() => ICON_DEFINITIONS[this.icon()] ?? ICON_DEFINITIONS['default']);\r\n\r\n readonly iconPath = computed(() => this.iconDefinition().path);\r\n\r\n readonly additionalShapes = computed(() => this.iconDefinition().additionalShapes ?? []);\r\n\r\n readonly shouldFillIcon = computed(() => shouldIconUseFill(this.icon()));\r\n\r\n readonly multiColorIcon = computed(() => this.iconDefinition().multiColor);\r\n\r\n readonly hasMultiPaths = computed(() => this.multiColorIcon() !== undefined);\r\n\r\n readonly iconStrokeWidth = computed(() => this.iconDefinition().strokeWidth ?? SVG_STROKE_ATTRS.strokeWidth);\r\n\r\n // Exponer constantes SVG al template\r\n readonly svgStrokeAttrs = SVG_STROKE_ATTRS;\r\n\r\n /**\r\n *\r\n * @param event\r\n */\r\n onButtonClick(event: Event): void {\r\n if (!this.disabled()) {\r\n this.iconClick.emit(event);\r\n }\r\n }\r\n}\r\n","<ng-template #svgContent>\r\n <svg [attr.width]=\"iconSize()\" [attr.height]=\"iconSize()\" viewBox=\"0 0 24 24\" fill=\"none\" [style.opacity]=\"iconOpacity()\">\r\n @for (shape of additionalShapes(); track $index) {\r\n @if (shape.type === 'circle') {\r\n <circle [attr.cx]=\"shape.cx\" [attr.cy]=\"shape.cy\" [attr.r]=\"shape.r\" [attr.stroke]=\"iconColor()\" [attr.stroke-width]=\"svgStrokeAttrs.strokeWidth\" [attr.fill]=\"shape.fill === 'color' ? iconColor() : 'none'\" />\r\n } @else if (shape.type === 'rect') {\r\n <rect [attr.x]=\"shape.x\" [attr.y]=\"shape.y\" [attr.width]=\"shape.width\" [attr.height]=\"shape.height\" [attr.rx]=\"shape.rx || null\" [attr.stroke]=\"iconColor()\" [attr.stroke-width]=\"svgStrokeAttrs.strokeWidth\"\r\n [attr.fill]=\"shape.fill === 'color' ? iconColor() : 'none'\" />\r\n } @else if (shape.type === 'path') {\r\n <path [attr.d]=\"shape.d\" [attr.stroke]=\"iconColor()\" [attr.stroke-width]=\"svgStrokeAttrs.strokeWidth\" [attr.stroke-linecap]=\"svgStrokeAttrs.strokeLinecap\" [attr.stroke-linejoin]=\"svgStrokeAttrs.strokeLinejoin\"\r\n [attr.fill]=\"shape.fill === 'color' ? iconColor() : 'none'\" />\r\n }\r\n }\r\n\r\n @if (hasMultiPaths()) {\r\n <!-- Iconos con múltiples paths y colores personalizados -->\r\n @for (path of multiColorIcon()!.paths; track $index) {\r\n <path [attr.d]=\"path\" [attr.fill]=\"multiColorIcon()!.colors[$index] || iconColor()\" stroke=\"none\" />\r\n }\r\n } @else {\r\n <!-- Iconos con un solo path -->\r\n <path [attr.d]=\"iconPath()\" [attr.stroke]=\"iconColor()\" [attr.fill]=\"shouldFillIcon() ? iconColor() : 'none'\" [attr.stroke-width]=\"iconStrokeWidth()\" [attr.stroke-linecap]=\"svgStrokeAttrs.strokeLinecap\"\r\n [attr.stroke-linejoin]=\"svgStrokeAttrs.strokeLinejoin\" />\r\n }\r\n </svg>\r\n</ng-template>\r\n\r\n<ng-template #textContent>\r\n @if (textLeft()) {\r\n <span class=\"icon-text ms-3\">{{ textLeft() }}</span>\r\n }\r\n <span class=\"icon-content\" [style.width.px]=\"iconSize() + 8\" [style.height.px]=\"iconSize() + 8\">\r\n <ng-container *ngTemplateOutlet=\"svgContent\" />\r\n </span>\r\n @if (textRight()) {\r\n <span class=\"icon-text me-3\">{{ textRight() }}</span>\r\n }\r\n</ng-template>\r\n\r\n@if (button()) {\r\n<button [type]=\"type()\" [disabled]=\"disabled()\" class=\"icon-wrapper\" [class.icon-wrapper-border]=\"border()\" [class.dark-mode]=\"isDarkMode()\" (click)=\"onButtonClick($event)\">\r\n <ng-container *ngTemplateOutlet=\"textContent\" />\r\n</button>\r\n} @else {\r\n<span class=\"icon-wrapper\" [class.icon-wrapper-border]=\"border()\" [class.dark-mode]=\"isDarkMode()\">\r\n <ng-container *ngTemplateOutlet=\"textContent\" />\r\n</span>\r\n}"]}
|
|
1
|
+
{"version":3,"file":"icon.component.js","sourceRoot":"","sources":["../../../../../libs/ui/src/lib/icon/icon.component.ts","../../../../../libs/ui/src/lib/icon/icon.component.html"],"names":[],"mappings":"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","sourcesContent":["import { NgTemplateOutlet } from '@angular/common';\r\nimport { Component, input, output, computed, ChangeDetectionStrategy, inject } from '@angular/core';\r\nimport {\r\n BASE_ICON_SIZE,\r\n BASE_ICON_COLORS,\r\n DISABLED_COLOR,\r\n DEFAULT_ICON_COLOR,\r\n DISABLED_OPACITY,\r\n SECONDARY_WARN_OPACITY,\r\n DEFAULT_OPACITY,\r\n OPACITY_REDUCED_COLORS,\r\n SVG_STROKE_ATTRS,\r\n THEME_DARK_COLOR,\r\n THEME_LIGHT_COLOR,\r\n} from './icon.constants';\r\nimport { ICON_DEFINITIONS } from './icon.definitions';\r\nimport type { ColorType, ButtonType, IconType } from './icon.types';\r\nimport { transformToBoolean, shouldIconUseFill } from './icon.utils';\r\nimport { ThemeService } from './theme.service';\r\n\r\n\r\n/**\r\n *\r\n */\r\n@Component({\r\n selector: 'c80-icon',\r\n imports: [NgTemplateOutlet],\r\n templateUrl: './icon.component.html',\r\n styleUrls: ['./icon.component.scss'],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n})\r\nexport class IconComponent {\r\n private readonly themeService = inject(ThemeService);\r\n\r\n readonly icon = input<IconType>('check'); // Tipo de icono a mostrar\r\n readonly color = input<ColorType | undefined>(undefined); // Color del icono (primary, secondary, warn, success)\r\n readonly customColor = input<string | undefined>(undefined); // Color personalizado (sobrescribe color)\r\n readonly disabled = input(false); // Estado deshabilitado\r\n readonly size = input(1); // Multiplicador de tamaño (1 = 24px)\r\n readonly button = input(false, { transform: transformToBoolean }); // Renderiza como botón clickeable\r\n readonly border = input(false, { transform: transformToBoolean }); // Agrega borde al wrapper\r\n readonly type = input<ButtonType>('button'); // Tipo de botón (button, submit, reset)\r\n readonly textLeft = input<string | undefined>(undefined); // Texto a la izquierda del icono\r\n readonly textRight = input<string | undefined>(undefined); // Texto a la derecha del icono\r\n readonly dark = input(false, { transform: transformToBoolean }); // Modo oscuro\r\n\r\n readonly iconClick = output<Event>(); // Evento emitido al hacer click (solo si button=true)\r\n\r\n readonly iconSize = computed(() => BASE_ICON_SIZE * this.size());\r\n\r\n readonly isDarkMode = computed(() => this.dark() || this.themeService.isDark());\r\n\r\n readonly iconColor = computed(() => {\r\n if (this.disabled()) return DISABLED_COLOR;\r\n\r\n const custom = this.customColor();\r\n if (custom) return custom;\r\n\r\n const colorType = this.color();\r\n\r\n // Si no hay color específico, usar color adaptativo al tema\r\n if (!colorType) {\r\n return this.themeService.isDark() ? THEME_DARK_COLOR : THEME_LIGHT_COLOR;\r\n }\r\n\r\n return BASE_ICON_COLORS[colorType] ?? DEFAULT_ICON_COLOR;\r\n });\r\n\r\n readonly iconOpacity = computed(() => {\r\n if (this.disabled()) return DISABLED_OPACITY;\r\n\r\n const hasCustomColor = this.customColor() !== undefined;\r\n const colorType = this.color();\r\n const isOpacityReducedColor = colorType && OPACITY_REDUCED_COLORS.includes(colorType);\r\n\r\n return !hasCustomColor && isOpacityReducedColor ? SECONDARY_WARN_OPACITY : DEFAULT_OPACITY;\r\n });\r\n\r\n // Optimización: Un solo lookup a ICON_DEFINITIONS\r\n readonly iconDefinition = computed(() => ICON_DEFINITIONS[this.icon()] ?? ICON_DEFINITIONS['default']);\r\n\r\n readonly iconPath = computed(() => this.iconDefinition().path);\r\n\r\n readonly additionalShapes = computed(() => this.iconDefinition().additionalShapes ?? []);\r\n\r\n readonly shouldFillIcon = computed(() => shouldIconUseFill(this.icon()));\r\n\r\n readonly multiColorIcon = computed(() => this.iconDefinition().multiColor);\r\n\r\n readonly hasMultiPaths = computed(() => this.multiColorIcon() !== undefined);\r\n\r\n readonly iconStrokeWidth = computed(() => this.iconDefinition().strokeWidth ?? SVG_STROKE_ATTRS.strokeWidth);\r\n\r\n // Exponer constantes SVG al template\r\n readonly svgStrokeAttrs = SVG_STROKE_ATTRS;\r\n\r\n /**\r\n *\r\n * @param event\r\n */\r\n onButtonClick(event: Event): void {\r\n if (!this.disabled()) {\r\n this.iconClick.emit(event);\r\n }\r\n }\r\n}\r\n","<ng-template #svgContent>\r\n <svg [attr.width]=\"iconSize()\" [attr.height]=\"iconSize()\" viewBox=\"0 0 24 24\" fill=\"none\" [style.opacity]=\"iconOpacity()\">\r\n @for (shape of additionalShapes(); track $index) {\r\n @if (shape.type === 'circle') {\r\n <circle [attr.cx]=\"shape.cx\" [attr.cy]=\"shape.cy\" [attr.r]=\"shape.r\" [attr.stroke]=\"iconColor()\" [attr.stroke-width]=\"svgStrokeAttrs.strokeWidth\" [attr.fill]=\"shape.fill === 'color' ? iconColor() : 'none'\" />\r\n } @else if (shape.type === 'rect') {\r\n <rect [attr.x]=\"shape.x\" [attr.y]=\"shape.y\" [attr.width]=\"shape.width\" [attr.height]=\"shape.height\" [attr.rx]=\"shape.rx || null\" [attr.stroke]=\"iconColor()\" [attr.stroke-width]=\"svgStrokeAttrs.strokeWidth\"\r\n [attr.fill]=\"shape.fill === 'color' ? iconColor() : 'none'\" />\r\n } @else if (shape.type === 'path') {\r\n <path [attr.d]=\"shape.d\" [attr.stroke]=\"iconColor()\" [attr.stroke-width]=\"svgStrokeAttrs.strokeWidth\" [attr.stroke-linecap]=\"svgStrokeAttrs.strokeLinecap\" [attr.stroke-linejoin]=\"svgStrokeAttrs.strokeLinejoin\"\r\n [attr.fill]=\"shape.fill === 'color' ? iconColor() : 'none'\" />\r\n }\r\n }\r\n\r\n @if (hasMultiPaths()) {\r\n <!-- Iconos con múltiples paths y colores personalizados -->\r\n @for (path of multiColorIcon()!.paths; track $index) {\r\n <path [attr.d]=\"path\" [attr.fill]=\"multiColorIcon()!.colors[$index] || iconColor()\" stroke=\"none\" />\r\n }\r\n } @else {\r\n <!-- Iconos con un solo path -->\r\n <path [attr.d]=\"iconPath()\" [attr.stroke]=\"iconColor()\" [attr.fill]=\"shouldFillIcon() ? iconColor() : 'none'\" [attr.stroke-width]=\"iconStrokeWidth()\" [attr.stroke-linecap]=\"svgStrokeAttrs.strokeLinecap\"\r\n [attr.stroke-linejoin]=\"svgStrokeAttrs.strokeLinejoin\" />\r\n }\r\n </svg>\r\n</ng-template>\r\n\r\n<ng-template #textContent>\r\n @if (textLeft()) {\r\n <span class=\"icon-text ms-3\">{{ textLeft() }}</span>\r\n }\r\n <span class=\"icon-content\" [style.width.px]=\"iconSize() + 8\" [style.height.px]=\"iconSize() + 8\">\r\n <ng-container *ngTemplateOutlet=\"svgContent\" />\r\n </span>\r\n @if (textRight()) {\r\n <span class=\"icon-text me-3\">{{ textRight() }}</span>\r\n }\r\n</ng-template>\r\n\r\n@if (button()) {\r\n<button [type]=\"type()\" [disabled]=\"disabled()\" class=\"icon-wrapper\" [class.icon-wrapper-border]=\"border()\" [class.dark-mode]=\"isDarkMode()\" (click)=\"onButtonClick($event)\">\r\n <ng-container *ngTemplateOutlet=\"textContent\" />\r\n</button>\r\n} @else {\r\n<span class=\"icon-wrapper\" [class.icon-wrapper-border]=\"border()\" [class.dark-mode]=\"isDarkMode()\">\r\n <ng-container *ngTemplateOutlet=\"textContent\" />\r\n</span>\r\n}"]}
|
|
@@ -57,6 +57,9 @@ export class ThemeService {
|
|
|
57
57
|
*/
|
|
58
58
|
initialize() {
|
|
59
59
|
this.applyTheme(this.isDark());
|
|
60
|
+
if (typeof globalThis.matchMedia !== 'function') {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
60
63
|
globalThis.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => {
|
|
61
64
|
if (!this.storage.has(THEME_STORAGE_KEY)) {
|
|
62
65
|
this.isDark.set(e.matches);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"theme.service.js","sourceRoot":"","sources":["../../../../../libs/ui/src/lib/icon/theme.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;;AAE3E,wEAAwE;AACxE,MAAM,CAAC,MAAM,iBAAiB,GAAG,OAAO,CAAC;AA4BzC;;;GAGG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,cAAc,CAAe,eAAe,EAAE;IAC3E,UAAU,EAAE,MAAM;IAClB,OAAO,EAAE,GAAiB,EAAE,CAAC,CAAC;QAC1B,GAAG,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,YAAY,CAAC,OAAO,CAAC,GAAG,CAAC;QACvC,GAAG,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,CAAC,YAAY,CAAC,OAAO,CAAC,GAAG,EAAE,KAAK,CAAC;QACrD,GAAG,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,YAAY,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI;KACnD,CAAC;CACL,CAAC,CAAC;AAEH;;GAEG;AAEH,MAAM,OAAO,YAAY;IACJ,OAAO,GAAG,MAAM,CAAC,aAAa,CAAC,CAAC;IACxC,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,eAAe,EAAE,6EAAC,CAAC;IAEjD;;;OAGG;IACK,eAAe;QACnB,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;QACnD,IAAI,MAAM,EAAE,CAAC;YACT,OAAO,MAAM,KAAK,MAAM,CAAC;QAC7B,CAAC;QACD,IAAI,OAAO,UAAU,CAAC,UAAU,KAAK,UAAU,EAAE,CAAC;YAC9C,OAAO,KAAK,CAAC;QACjB,CAAC;QACD,OAAO,UAAU,CAAC,UAAU,CAAC,8BAA8B,CAAC,CAAC,OAAO,CAAC;IACzE,CAAC;IAED;;OAEG;IACH,MAAM;QACF,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE;YACzB,MAAM,QAAQ,GAAG,CAAC,OAAO,CAAC;YAC1B,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,EAAE,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;YACjE,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;YAC1B,OAAO,QAAQ,CAAC;QACpB,CAAC,CAAC,CAAC;IACP,CAAC;IAED;;;OAGG;IACK,UAAU,CAAC,MAAe;QAC9B,QAAQ,CAAC,eAAe,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;IAC1E,CAAC;IAED;;OAEG;IACH,UAAU;QACN,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;QAE/B,UAAU,CAAC,UAAU,CAAC,8BAA8B,CAAC,CAAC,gBAAgB,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE;YACnF,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,EAAE,CAAC;gBACvC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;gBAC3B,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;YAC/B,CAAC;QACL,CAAC,CAAC,CAAC;IACP,CAAC;
|
|
1
|
+
{"version":3,"file":"theme.service.js","sourceRoot":"","sources":["../../../../../libs/ui/src/lib/icon/theme.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;;AAE3E,wEAAwE;AACxE,MAAM,CAAC,MAAM,iBAAiB,GAAG,OAAO,CAAC;AA4BzC;;;GAGG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,cAAc,CAAe,eAAe,EAAE;IAC3E,UAAU,EAAE,MAAM;IAClB,OAAO,EAAE,GAAiB,EAAE,CAAC,CAAC;QAC1B,GAAG,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,YAAY,CAAC,OAAO,CAAC,GAAG,CAAC;QACvC,GAAG,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,CAAC,YAAY,CAAC,OAAO,CAAC,GAAG,EAAE,KAAK,CAAC;QACrD,GAAG,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,YAAY,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI;KACnD,CAAC;CACL,CAAC,CAAC;AAEH;;GAEG;AAEH,MAAM,OAAO,YAAY;IACJ,OAAO,GAAG,MAAM,CAAC,aAAa,CAAC,CAAC;IACxC,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,eAAe,EAAE,6EAAC,CAAC;IAEjD;;;OAGG;IACK,eAAe;QACnB,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;QACnD,IAAI,MAAM,EAAE,CAAC;YACT,OAAO,MAAM,KAAK,MAAM,CAAC;QAC7B,CAAC;QACD,IAAI,OAAO,UAAU,CAAC,UAAU,KAAK,UAAU,EAAE,CAAC;YAC9C,OAAO,KAAK,CAAC;QACjB,CAAC;QACD,OAAO,UAAU,CAAC,UAAU,CAAC,8BAA8B,CAAC,CAAC,OAAO,CAAC;IACzE,CAAC;IAED;;OAEG;IACH,MAAM;QACF,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE;YACzB,MAAM,QAAQ,GAAG,CAAC,OAAO,CAAC;YAC1B,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,EAAE,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;YACjE,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;YAC1B,OAAO,QAAQ,CAAC;QACpB,CAAC,CAAC,CAAC;IACP,CAAC;IAED;;;OAGG;IACK,UAAU,CAAC,MAAe;QAC9B,QAAQ,CAAC,eAAe,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;IAC1E,CAAC;IAED;;OAEG;IACH,UAAU;QACN,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;QAE/B,IAAI,OAAO,UAAU,CAAC,UAAU,KAAK,UAAU,EAAE,CAAC;YAC9C,OAAO;QACX,CAAC;QAED,UAAU,CAAC,UAAU,CAAC,8BAA8B,CAAC,CAAC,gBAAgB,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE;YACnF,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,EAAE,CAAC;gBACvC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;gBAC3B,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;YAC/B,CAAC;QACL,CAAC,CAAC,CAAC;IACP,CAAC;wGAvDQ,YAAY;4GAAZ,YAAY,cADC,MAAM;;4FACnB,YAAY;kBADxB,UAAU;mBAAC,EAAE,UAAU,EAAE,MAAM,EAAE","sourcesContent":["import { inject, Injectable, InjectionToken, signal } from '@angular/core';\n\n/** Clave de almacenamiento donde se persiste la preferencia de tema. */\nexport const THEME_STORAGE_KEY = 'theme';\n\n/**\n * Contrato mínimo de almacenamiento que necesita ThemeService.\n * Permite a las apps consumidoras inyectar su propio backend\n * (por ejemplo, un StorageService propio) sin acoplar la librería a localStorage.\n */\nexport interface ThemeStorage {\n /**\n *\n * @param key\n * @returns Valor almacenado o null si no existe\n */\n get(key: string): string | null;\n /**\n *\n * @param key\n * @param value\n */\n set(key: string, value: string): void;\n /**\n *\n * @param key\n * @returns true si hay un valor almacenado para la clave\n */\n has(key: string): boolean;\n}\n\n/**\n * Token del almacenamiento usado por ThemeService.\n * El default reproduce el comportamiento histórico respaldado por localStorage.\n */\nexport const THEME_STORAGE = new InjectionToken<ThemeStorage>('THEME_STORAGE', {\n providedIn: 'root',\n factory: (): ThemeStorage => ({\n get: (key) => localStorage.getItem(key),\n set: (key, value) => localStorage.setItem(key, value),\n has: (key) => localStorage.getItem(key) !== null,\n }),\n});\n\n/**\n *\n */\n@Injectable({ providedIn: 'root' })\nexport class ThemeService {\n private readonly storage = inject(THEME_STORAGE);\n readonly isDark = signal(this.getInitialTheme());\n\n /**\n *\n * @returns true si el tema es oscuro\n */\n private getInitialTheme(): boolean {\n const stored = this.storage.get(THEME_STORAGE_KEY);\n if (stored) {\n return stored === 'dark';\n }\n if (typeof globalThis.matchMedia !== 'function') {\n return false;\n }\n return globalThis.matchMedia('(prefers-color-scheme: dark)').matches;\n }\n\n /**\n *\n */\n toggle(): void {\n this.isDark.update(current => {\n const newValue = !current;\n this.storage.set(THEME_STORAGE_KEY, newValue ? 'dark' : 'light');\n this.applyTheme(newValue);\n return newValue;\n });\n }\n\n /**\n *\n * @param isDark\n */\n private applyTheme(isDark: boolean): void {\n document.documentElement.dataset['theme'] = isDark ? 'dark' : 'light';\n }\n\n /**\n *\n */\n initialize(): void {\n this.applyTheme(this.isDark());\n\n if (typeof globalThis.matchMedia !== 'function') {\n return;\n }\n\n globalThis.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => {\n if (!this.storage.has(THEME_STORAGE_KEY)) {\n this.isDark.set(e.matches);\n this.applyTheme(e.matches);\n }\n });\n }\n}\n"]}
|
|
@@ -10,6 +10,6 @@ export class InfoListComponent {
|
|
|
10
10
|
}
|
|
11
11
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: InfoListComponent, decorators: [{
|
|
12
12
|
type: Component,
|
|
13
|
-
args: [{ selector: 'c80-info-list',
|
|
13
|
+
args: [{ selector: 'c80-info-list', imports: [], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"info-list\">\n @for (item of items(); track $index) {\n @if (item.value) {\n <div class=\"info-list__item\">\n <span class=\"info-list__label\">{{ item.label }}</span>\n <span class=\"info-list__value\">{{ item.value }}</span>\n </div>\n }\n }\n</div>", styles: [".info-list{display:flex;flex-direction:column;gap:1rem;-webkit-user-select:none;user-select:none}.info-list__item{display:flex;justify-content:space-between;align-items:flex-start;padding:1rem;background:var(--color-surface);border:1px solid var(--color-border);border-radius:.5rem;gap:1rem}.info-list__label{font-size:.875rem;font-weight:500;color:var(--color-text-secondary);flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;line-height:1.4}.info-list__value{font-size:1rem;font-weight:500;color:var(--color-text-primary);text-align:right;max-width:60%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:1.4;pointer-events:none}@media(max-width:768px){.info-list__item{flex-direction:column;align-items:flex-start;gap:.5rem;padding:.75rem}.info-list__value{text-align:left;max-width:100%}}\n"] }]
|
|
14
14
|
}], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: true }] }] } });
|
|
15
15
|
//# sourceMappingURL=info-list.component.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"info-list.component.js","sourceRoot":"","sources":["../../../../../libs/ui/src/lib/info-list/info-list.component.ts","../../../../../libs/ui/src/lib/info-list/info-list.component.html"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;;AAG1E;;GAEG;
|
|
1
|
+
{"version":3,"file":"info-list.component.js","sourceRoot":"","sources":["../../../../../libs/ui/src/lib/info-list/info-list.component.ts","../../../../../libs/ui/src/lib/info-list/info-list.component.html"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;;AAG1E;;GAEG;AAQH,MAAM,OAAO,iBAAiB;IACjB,KAAK,GAAG,KAAK,CAAC,QAAQ,2EAAc,CAAC;wGADrC,iBAAiB;4FAAjB,iBAAiB,4MCb9B,0SASM;;4FDIO,iBAAiB;kBAP7B,SAAS;+BACI,eAAe,WAChB,EAAE,mBAGM,uBAAuB,CAAC,MAAM","sourcesContent":["import { Component, input, ChangeDetectionStrategy } from '@angular/core';\r\nimport type { InfoItem } from './info-list.types';\r\n\r\n/**\r\n *\r\n */\r\n@Component({\r\n selector: 'c80-info-list',\r\n imports: [],\r\n templateUrl: './info-list.component.html',\r\n styleUrl: './info-list.component.scss',\r\n changeDetection: ChangeDetectionStrategy.OnPush\r\n})\r\nexport class InfoListComponent {\r\n readonly items = input.required<InfoItem[]>();\r\n}","<div class=\"info-list\">\n @for (item of items(); track $index) {\n @if (item.value) {\n <div class=\"info-list__item\">\n <span class=\"info-list__label\">{{ item.label }}</span>\n <span class=\"info-list__value\">{{ item.value }}</span>\n </div>\n }\n }\n</div>"]}
|