@neural-ui/core 1.2.1 → 1.3.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 +56 -88
- package/accordion/package.json +4 -0
- package/alert/package.json +4 -0
- package/autocomplete/package.json +4 -0
- package/avatar/package.json +4 -0
- package/badge/package.json +4 -0
- package/block-ui/package.json +4 -0
- package/breadcrumb/package.json +4 -0
- package/button/package.json +4 -0
- package/card/package.json +4 -0
- package/chart/package.json +4 -0
- package/checkbox/package.json +4 -0
- package/chip/package.json +4 -0
- package/code-block/package.json +4 -0
- package/color-picker/package.json +4 -0
- package/command-palette/package.json +4 -0
- package/confirm-dialog/package.json +4 -0
- package/context-menu/package.json +4 -0
- package/dashboard-grid/package.json +4 -0
- package/date-input/package.json +4 -0
- package/divider/package.json +4 -0
- package/empty-state/package.json +4 -0
- package/fesm2022/neural-ui-core-accordion.mjs +162 -0
- package/fesm2022/neural-ui-core-accordion.mjs.map +1 -0
- package/fesm2022/neural-ui-core-alert.mjs +116 -0
- package/fesm2022/neural-ui-core-alert.mjs.map +1 -0
- package/fesm2022/neural-ui-core-autocomplete.mjs +406 -0
- package/fesm2022/neural-ui-core-autocomplete.mjs.map +1 -0
- package/fesm2022/neural-ui-core-avatar.mjs +109 -0
- package/fesm2022/neural-ui-core-avatar.mjs.map +1 -0
- package/fesm2022/neural-ui-core-badge.mjs +54 -0
- package/fesm2022/neural-ui-core-badge.mjs.map +1 -0
- package/fesm2022/neural-ui-core-block-ui.mjs +95 -0
- package/fesm2022/neural-ui-core-block-ui.mjs.map +1 -0
- package/fesm2022/neural-ui-core-breadcrumb.mjs +84 -0
- package/fesm2022/neural-ui-core-breadcrumb.mjs.map +1 -0
- package/fesm2022/neural-ui-core-button.mjs +125 -0
- package/fesm2022/neural-ui-core-button.mjs.map +1 -0
- package/fesm2022/neural-ui-core-card.mjs +69 -0
- package/fesm2022/neural-ui-core-card.mjs.map +1 -0
- package/fesm2022/neural-ui-core-chart.mjs +287 -0
- package/fesm2022/neural-ui-core-chart.mjs.map +1 -0
- package/fesm2022/neural-ui-core-checkbox.mjs +138 -0
- package/fesm2022/neural-ui-core-checkbox.mjs.map +1 -0
- package/fesm2022/neural-ui-core-chip.mjs +130 -0
- package/fesm2022/neural-ui-core-chip.mjs.map +1 -0
- package/fesm2022/neural-ui-core-code-block.mjs +250 -0
- package/fesm2022/neural-ui-core-code-block.mjs.map +1 -0
- package/fesm2022/neural-ui-core-color-picker.mjs +435 -0
- package/fesm2022/neural-ui-core-color-picker.mjs.map +1 -0
- package/fesm2022/neural-ui-core-command-palette.mjs +235 -0
- package/fesm2022/neural-ui-core-command-palette.mjs.map +1 -0
- package/fesm2022/neural-ui-core-confirm-dialog.mjs +118 -0
- package/fesm2022/neural-ui-core-confirm-dialog.mjs.map +1 -0
- package/fesm2022/neural-ui-core-context-menu.mjs +158 -0
- package/fesm2022/neural-ui-core-context-menu.mjs.map +1 -0
- package/fesm2022/neural-ui-core-dashboard-grid.mjs +144 -0
- package/fesm2022/neural-ui-core-dashboard-grid.mjs.map +1 -0
- package/fesm2022/neural-ui-core-date-input.mjs +1332 -0
- package/fesm2022/neural-ui-core-date-input.mjs.map +1 -0
- package/fesm2022/neural-ui-core-divider.mjs +54 -0
- package/fesm2022/neural-ui-core-divider.mjs.map +1 -0
- package/fesm2022/neural-ui-core-empty-state.mjs +84 -0
- package/fesm2022/neural-ui-core-empty-state.mjs.map +1 -0
- package/fesm2022/neural-ui-core-filter-bar.mjs +118 -0
- package/fesm2022/neural-ui-core-filter-bar.mjs.map +1 -0
- package/fesm2022/neural-ui-core-icon.mjs +50 -0
- package/fesm2022/neural-ui-core-icon.mjs.map +1 -0
- package/fesm2022/neural-ui-core-image-viewer.mjs +309 -0
- package/fesm2022/neural-ui-core-image-viewer.mjs.map +1 -0
- package/fesm2022/neural-ui-core-input-otp.mjs +192 -0
- package/fesm2022/neural-ui-core-input-otp.mjs.map +1 -0
- package/fesm2022/neural-ui-core-input.mjs +320 -0
- package/fesm2022/neural-ui-core-input.mjs.map +1 -0
- package/fesm2022/neural-ui-core-knob.mjs +323 -0
- package/fesm2022/neural-ui-core-knob.mjs.map +1 -0
- package/fesm2022/neural-ui-core-meter-group.mjs +122 -0
- package/fesm2022/neural-ui-core-meter-group.mjs.map +1 -0
- package/fesm2022/neural-ui-core-modal.mjs +156 -0
- package/fesm2022/neural-ui-core-modal.mjs.map +1 -0
- package/fesm2022/neural-ui-core-multiselect.mjs +825 -0
- package/fesm2022/neural-ui-core-multiselect.mjs.map +1 -0
- package/fesm2022/neural-ui-core-nav.mjs +952 -0
- package/fesm2022/neural-ui-core-nav.mjs.map +1 -0
- package/fesm2022/neural-ui-core-notification-center.mjs +264 -0
- package/fesm2022/neural-ui-core-notification-center.mjs.map +1 -0
- package/fesm2022/neural-ui-core-number-input.mjs +331 -0
- package/fesm2022/neural-ui-core-number-input.mjs.map +1 -0
- package/fesm2022/neural-ui-core-pagination.mjs +198 -0
- package/fesm2022/neural-ui-core-pagination.mjs.map +1 -0
- package/fesm2022/neural-ui-core-popover.mjs +207 -0
- package/fesm2022/neural-ui-core-popover.mjs.map +1 -0
- package/fesm2022/neural-ui-core-progress-bar.mjs +105 -0
- package/fesm2022/neural-ui-core-progress-bar.mjs.map +1 -0
- package/fesm2022/neural-ui-core-radio.mjs +171 -0
- package/fesm2022/neural-ui-core-radio.mjs.map +1 -0
- package/fesm2022/neural-ui-core-rating.mjs +151 -0
- package/fesm2022/neural-ui-core-rating.mjs.map +1 -0
- package/fesm2022/neural-ui-core-select.mjs +710 -0
- package/fesm2022/neural-ui-core-select.mjs.map +1 -0
- package/fesm2022/neural-ui-core-sidebar.mjs +214 -0
- package/fesm2022/neural-ui-core-sidebar.mjs.map +1 -0
- package/fesm2022/neural-ui-core-skeleton.mjs +40 -0
- package/fesm2022/neural-ui-core-skeleton.mjs.map +1 -0
- package/fesm2022/neural-ui-core-slider.mjs +146 -0
- package/fesm2022/neural-ui-core-slider.mjs.map +1 -0
- package/fesm2022/neural-ui-core-spinner.mjs +113 -0
- package/fesm2022/neural-ui-core-spinner.mjs.map +1 -0
- package/fesm2022/neural-ui-core-split-button.mjs +252 -0
- package/fesm2022/neural-ui-core-split-button.mjs.map +1 -0
- package/fesm2022/neural-ui-core-splitter.mjs +174 -0
- package/fesm2022/neural-ui-core-splitter.mjs.map +1 -0
- package/fesm2022/neural-ui-core-stats-card.mjs +163 -0
- package/fesm2022/neural-ui-core-stats-card.mjs.map +1 -0
- package/fesm2022/neural-ui-core-stepper.mjs +204 -0
- package/fesm2022/neural-ui-core-stepper.mjs.map +1 -0
- package/fesm2022/neural-ui-core-switch.mjs +111 -0
- package/fesm2022/neural-ui-core-switch.mjs.map +1 -0
- package/fesm2022/neural-ui-core-table.mjs +1872 -0
- package/fesm2022/neural-ui-core-table.mjs.map +1 -0
- package/fesm2022/neural-ui-core-tabs.mjs +338 -0
- package/fesm2022/neural-ui-core-tabs.mjs.map +1 -0
- package/fesm2022/neural-ui-core-textarea.mjs +188 -0
- package/fesm2022/neural-ui-core-textarea.mjs.map +1 -0
- package/fesm2022/neural-ui-core-timeline.mjs +117 -0
- package/fesm2022/neural-ui-core-timeline.mjs.map +1 -0
- package/fesm2022/neural-ui-core-toast.mjs +171 -0
- package/fesm2022/neural-ui-core-toast.mjs.map +1 -0
- package/fesm2022/neural-ui-core-toggle-button-group.mjs +162 -0
- package/fesm2022/neural-ui-core-toggle-button-group.mjs.map +1 -0
- package/fesm2022/neural-ui-core-toolbar.mjs +67 -0
- package/fesm2022/neural-ui-core-toolbar.mjs.map +1 -0
- package/fesm2022/neural-ui-core-tooltip.mjs +151 -0
- package/fesm2022/neural-ui-core-tooltip.mjs.map +1 -0
- package/fesm2022/neural-ui-core-url-state.mjs +96 -0
- package/fesm2022/neural-ui-core-url-state.mjs.map +1 -0
- package/fesm2022/neural-ui-core-virtual-list.mjs +126 -0
- package/fesm2022/neural-ui-core-virtual-list.mjs.map +1 -0
- package/fesm2022/neural-ui-core.mjs +11 -8544
- package/fesm2022/neural-ui-core.mjs.map +1 -1
- package/filter-bar/package.json +4 -0
- package/icon/package.json +4 -0
- package/image-viewer/package.json +4 -0
- package/input/package.json +4 -0
- package/input-otp/package.json +4 -0
- package/knob/package.json +4 -0
- package/meter-group/package.json +4 -0
- package/modal/package.json +4 -0
- package/multiselect/package.json +4 -0
- package/nav/package.json +4 -0
- package/notification-center/package.json +4 -0
- package/number-input/package.json +4 -0
- package/package.json +252 -5
- package/pagination/package.json +4 -0
- package/popover/package.json +4 -0
- package/progress-bar/package.json +4 -0
- package/radio/package.json +4 -0
- package/rating/package.json +4 -0
- package/select/package.json +4 -0
- package/sidebar/package.json +4 -0
- package/skeleton/package.json +4 -0
- package/slider/package.json +4 -0
- package/spinner/package.json +4 -0
- package/split-button/package.json +4 -0
- package/splitter/package.json +4 -0
- package/stats-card/package.json +4 -0
- package/stepper/package.json +4 -0
- package/styles/_tokens.scss +202 -0
- package/styles.scss +1 -0
- package/switch/package.json +4 -0
- package/table/package.json +4 -0
- package/tabs/package.json +4 -0
- package/textarea/package.json +4 -0
- package/timeline/package.json +4 -0
- package/toast/package.json +4 -0
- package/toggle-button-group/package.json +4 -0
- package/toolbar/package.json +4 -0
- package/tooltip/package.json +4 -0
- package/types/neural-ui-core-accordion.d.ts +55 -0
- package/types/neural-ui-core-alert.d.ts +47 -0
- package/types/neural-ui-core-autocomplete.d.ts +75 -0
- package/types/neural-ui-core-avatar.d.ts +39 -0
- package/types/neural-ui-core-badge.d.ts +36 -0
- package/types/neural-ui-core-block-ui.d.ts +46 -0
- package/types/neural-ui-core-breadcrumb.d.ts +38 -0
- package/types/neural-ui-core-button.d.ts +55 -0
- package/types/neural-ui-core-card.d.ts +37 -0
- package/types/neural-ui-core-chart.d.ts +236 -0
- package/types/neural-ui-core-checkbox.d.ts +33 -0
- package/types/neural-ui-core-chip.d.ts +53 -0
- package/types/neural-ui-core-code-block.d.ts +55 -0
- package/types/neural-ui-core-color-picker.d.ts +55 -0
- package/types/neural-ui-core-command-palette.d.ts +56 -0
- package/types/neural-ui-core-confirm-dialog.d.ts +50 -0
- package/types/neural-ui-core-context-menu.d.ts +66 -0
- package/types/neural-ui-core-dashboard-grid.d.ts +41 -0
- package/types/neural-ui-core-date-input.d.ts +178 -0
- package/types/neural-ui-core-divider.d.ts +20 -0
- package/types/neural-ui-core-empty-state.d.ts +32 -0
- package/types/neural-ui-core-filter-bar.d.ts +49 -0
- package/types/neural-ui-core-icon.d.ts +33 -0
- package/types/neural-ui-core-image-viewer.d.ts +67 -0
- package/types/neural-ui-core-input-otp.d.ts +49 -0
- package/types/neural-ui-core-input.d.ts +86 -0
- package/types/neural-ui-core-knob.d.ts +68 -0
- package/types/neural-ui-core-meter-group.d.ts +52 -0
- package/types/neural-ui-core-modal.d.ts +54 -0
- package/types/neural-ui-core-multiselect.d.ts +138 -0
- package/types/neural-ui-core-nav.d.ts +69 -0
- package/types/neural-ui-core-notification-center.d.ts +60 -0
- package/types/neural-ui-core-number-input.d.ts +63 -0
- package/types/neural-ui-core-pagination.d.ts +30 -0
- package/types/neural-ui-core-popover.d.ts +73 -0
- package/types/neural-ui-core-progress-bar.d.ts +35 -0
- package/types/neural-ui-core-radio.d.ts +51 -0
- package/types/neural-ui-core-rating.d.ts +34 -0
- package/types/neural-ui-core-select.d.ts +170 -0
- package/types/neural-ui-core-sidebar.d.ts +57 -0
- package/types/neural-ui-core-skeleton.d.ts +22 -0
- package/types/neural-ui-core-slider.d.ts +42 -0
- package/types/neural-ui-core-spinner.d.ts +38 -0
- package/types/neural-ui-core-split-button.d.ts +65 -0
- package/types/neural-ui-core-splitter.d.ts +28 -0
- package/types/neural-ui-core-stats-card.d.ts +39 -0
- package/types/neural-ui-core-stepper.d.ts +51 -0
- package/types/neural-ui-core-switch.d.ts +34 -0
- package/types/neural-ui-core-table.d.ts +285 -0
- package/types/neural-ui-core-tabs.d.ts +88 -0
- package/types/neural-ui-core-textarea.d.ts +52 -0
- package/types/neural-ui-core-timeline.d.ts +33 -0
- package/types/neural-ui-core-toast.d.ts +70 -0
- package/types/neural-ui-core-toggle-button-group.d.ts +63 -0
- package/types/neural-ui-core-toolbar.d.ts +36 -0
- package/types/neural-ui-core-tooltip.d.ts +48 -0
- package/types/neural-ui-core-url-state.d.ts +58 -0
- package/types/neural-ui-core-virtual-list.d.ts +60 -0
- package/types/neural-ui-core.d.ts +3 -2105
- package/url-state/package.json +4 -0
- package/virtual-list/package.json +4 -0
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { input, computed, ChangeDetectionStrategy, ViewEncapsulation, Component } from '@angular/core';
|
|
3
|
+
import { NeuIconComponent } from '@neural-ui/core/icon';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* NeuStatsCard — Card de métrica con título, valor, tendencia y sparkline.
|
|
7
|
+
*
|
|
8
|
+
* Uso:
|
|
9
|
+
* <neu-stats-card
|
|
10
|
+
* title="Ingresos"
|
|
11
|
+
* value="$12,450"
|
|
12
|
+
* change="+12.5%"
|
|
13
|
+
* trend="up"
|
|
14
|
+
* icon="lucideDollarSign"
|
|
15
|
+
* [sparkData]="[30,45,38,52,60,55,70]"
|
|
16
|
+
* />
|
|
17
|
+
*/
|
|
18
|
+
class NeuStatsCardComponent {
|
|
19
|
+
/** Título o etiqueta de la métrica. / Metric title or label. */
|
|
20
|
+
title = input('', ...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
|
|
21
|
+
/** Valor principal formateado (p.ej. "$12,450" o "98.2%"). / Main formatted value (e.g. "$12,450" or "98.2%"). */
|
|
22
|
+
value = input('', ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
|
|
23
|
+
/** Cambio porcentual o absoluto (p.ej. "+12.5%" o "-3"). / Percentage or absolute change (e.g. "+12.5%" or "-3"). */
|
|
24
|
+
change = input('', ...(ngDevMode ? [{ debugName: "change" }] : /* istanbul ignore next */ []));
|
|
25
|
+
/** Dirección del cambio. Afecta el color del change. / Change direction. Affects the change color. */
|
|
26
|
+
trend = input('neutral', ...(ngDevMode ? [{ debugName: "trend" }] : /* istanbul ignore next */ []));
|
|
27
|
+
/** Texto auxiliar bajo el cambio (p.ej. "vs. mes anterior"). / Auxiliary text below the change (e.g. "vs. previous month"). */
|
|
28
|
+
label = input('', ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
|
|
29
|
+
/** Nombre del icono Lucide para el encabezado. / Lucide icon name for the header. */
|
|
30
|
+
icon = input('', ...(ngDevMode ? [{ debugName: "icon" }] : /* istanbul ignore next */ []));
|
|
31
|
+
/** Array de valores numéricos para la sparkline. Mín. 2 puntos. / Array of numeric values for the sparkline. Min. 2 points. */
|
|
32
|
+
sparkData = input([], ...(ngDevMode ? [{ debugName: "sparkData" }] : /* istanbul ignore next */ []));
|
|
33
|
+
/** @internal Genera los puntos SVG de la sparkline / Generates the SVG sparkline points */
|
|
34
|
+
sparkPoints = computed(() => {
|
|
35
|
+
const data = this.sparkData();
|
|
36
|
+
if (data.length < 2)
|
|
37
|
+
return '';
|
|
38
|
+
const min = Math.min(...data);
|
|
39
|
+
const max = Math.max(...data);
|
|
40
|
+
const range = max - min || 1;
|
|
41
|
+
const step = 100 / (data.length - 1);
|
|
42
|
+
return data
|
|
43
|
+
.map((v, i) => `${(i * step).toFixed(1)},${(30 - ((v - min) / range) * 25 + 2.5).toFixed(1)}`)
|
|
44
|
+
.join(' ');
|
|
45
|
+
}, ...(ngDevMode ? [{ debugName: "sparkPoints" }] : /* istanbul ignore next */ []));
|
|
46
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: NeuStatsCardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
47
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: NeuStatsCardComponent, isStandalone: true, selector: "neu-stats-card", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, change: { classPropertyName: "change", publicName: "change", isSignal: true, isRequired: false, transformFunction: null }, trend: { classPropertyName: "trend", publicName: "trend", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, sparkData: { classPropertyName: "sparkData", publicName: "sparkData", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "neu-stats-card" }, ngImport: i0, template: `
|
|
48
|
+
<div class="neu-stats-card__inner">
|
|
49
|
+
<!-- Header: icono + título -->
|
|
50
|
+
<div class="neu-stats-card__header">
|
|
51
|
+
@if (icon()) {
|
|
52
|
+
<span class="neu-stats-card__icon">
|
|
53
|
+
<neu-icon [name]="icon()" size="1.125rem" />
|
|
54
|
+
</span>
|
|
55
|
+
}
|
|
56
|
+
<span class="neu-stats-card__title">{{ title() }}</span>
|
|
57
|
+
</div>
|
|
58
|
+
|
|
59
|
+
<!-- Valor principal -->
|
|
60
|
+
<div class="neu-stats-card__value">{{ value() }}</div>
|
|
61
|
+
|
|
62
|
+
<!-- Footer: cambio + sparkline -->
|
|
63
|
+
<div class="neu-stats-card__footer">
|
|
64
|
+
@if (change()) {
|
|
65
|
+
<span class="neu-stats-card__change neu-stats-card__change--{{ trend() }}">
|
|
66
|
+
@if (trend() === 'up') {
|
|
67
|
+
<neu-icon name="lucideTrendingUp" size="0.875rem" />
|
|
68
|
+
} @else if (trend() === 'down') {
|
|
69
|
+
<neu-icon name="lucideTrendingDown" size="0.875rem" />
|
|
70
|
+
} @else {
|
|
71
|
+
<neu-icon name="lucideMinus" size="0.875rem" />
|
|
72
|
+
}
|
|
73
|
+
{{ change() }}
|
|
74
|
+
</span>
|
|
75
|
+
}
|
|
76
|
+
@if (label()) {
|
|
77
|
+
<span class="neu-stats-card__label">{{ label() }}</span>
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
@if (sparkData().length > 1) {
|
|
81
|
+
<svg
|
|
82
|
+
class="neu-stats-card__sparkline"
|
|
83
|
+
viewBox="0 0 100 30"
|
|
84
|
+
preserveAspectRatio="none"
|
|
85
|
+
aria-hidden="true"
|
|
86
|
+
>
|
|
87
|
+
<polyline
|
|
88
|
+
class="neu-stats-card__spark-line"
|
|
89
|
+
[attr.points]="sparkPoints()"
|
|
90
|
+
fill="none"
|
|
91
|
+
stroke-width="1.5"
|
|
92
|
+
stroke-linecap="round"
|
|
93
|
+
stroke-linejoin="round"
|
|
94
|
+
/>
|
|
95
|
+
</svg>
|
|
96
|
+
}
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
`, isInline: true, styles: [".neu-stats-card{display:block}.neu-stats-card__inner{background:var(--neu-surface);border:1px solid var(--neu-border);border-radius:var(--neu-radius-lg, 16px);padding:var(--neu-space-5, 1.25rem);display:flex;flex-direction:column;gap:var(--neu-space-3, .75rem);transition:box-shadow var(--neu-transition)}.neu-stats-card__inner:hover{box-shadow:var(--neu-shadow-sm)}.neu-stats-card__header{display:flex;align-items:center;gap:var(--neu-space-2, .5rem)}.neu-stats-card__icon{display:flex;align-items:center;justify-content:center;width:32px;height:32px;border-radius:var(--neu-radius, 8px);background:var(--neu-primary-50, #eff6ff);color:var(--neu-primary);flex-shrink:0}.neu-stats-card__title{font-size:var(--neu-text-sm, .875rem);font-weight:500;color:var(--neu-text-muted);line-height:1.3}.neu-stats-card__value{font-size:var(--neu-text-2xl, 1.5rem);font-weight:700;color:var(--neu-text);letter-spacing:-.03em;line-height:1.1;font-family:var(--neu-font-sans)}.neu-stats-card__footer{display:flex;align-items:center;gap:var(--neu-space-2, .5rem);margin-top:auto}.neu-stats-card__change{display:inline-flex;align-items:center;gap:3px;font-size:var(--neu-text-xs, .75rem);font-weight:600;padding:2px 6px;border-radius:var(--neu-radius-full, 999px)}.neu-stats-card__change--up{background:#dcfce7;color:#15803d}.neu-stats-card__change--down{background:#fee2e2;color:#b91c1c}.neu-stats-card__change--neutral{background:var(--neu-surface-2, #f8fafc);color:var(--neu-text-muted)}.neu-stats-card__label{font-size:var(--neu-text-xs, .75rem);color:var(--neu-text-muted)}.neu-stats-card__sparkline{margin-left:auto;width:80px;height:30px;flex-shrink:0}.neu-stats-card__spark-line{stroke:var(--neu-primary);opacity:.8}\n"], dependencies: [{ kind: "component", type: NeuIconComponent, selector: "neu-icon", inputs: ["name", "strokeWidth", "size"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
100
|
+
}
|
|
101
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: NeuStatsCardComponent, decorators: [{
|
|
102
|
+
type: Component,
|
|
103
|
+
args: [{ selector: 'neu-stats-card', imports: [NeuIconComponent], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, host: { class: 'neu-stats-card' }, template: `
|
|
104
|
+
<div class="neu-stats-card__inner">
|
|
105
|
+
<!-- Header: icono + título -->
|
|
106
|
+
<div class="neu-stats-card__header">
|
|
107
|
+
@if (icon()) {
|
|
108
|
+
<span class="neu-stats-card__icon">
|
|
109
|
+
<neu-icon [name]="icon()" size="1.125rem" />
|
|
110
|
+
</span>
|
|
111
|
+
}
|
|
112
|
+
<span class="neu-stats-card__title">{{ title() }}</span>
|
|
113
|
+
</div>
|
|
114
|
+
|
|
115
|
+
<!-- Valor principal -->
|
|
116
|
+
<div class="neu-stats-card__value">{{ value() }}</div>
|
|
117
|
+
|
|
118
|
+
<!-- Footer: cambio + sparkline -->
|
|
119
|
+
<div class="neu-stats-card__footer">
|
|
120
|
+
@if (change()) {
|
|
121
|
+
<span class="neu-stats-card__change neu-stats-card__change--{{ trend() }}">
|
|
122
|
+
@if (trend() === 'up') {
|
|
123
|
+
<neu-icon name="lucideTrendingUp" size="0.875rem" />
|
|
124
|
+
} @else if (trend() === 'down') {
|
|
125
|
+
<neu-icon name="lucideTrendingDown" size="0.875rem" />
|
|
126
|
+
} @else {
|
|
127
|
+
<neu-icon name="lucideMinus" size="0.875rem" />
|
|
128
|
+
}
|
|
129
|
+
{{ change() }}
|
|
130
|
+
</span>
|
|
131
|
+
}
|
|
132
|
+
@if (label()) {
|
|
133
|
+
<span class="neu-stats-card__label">{{ label() }}</span>
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
@if (sparkData().length > 1) {
|
|
137
|
+
<svg
|
|
138
|
+
class="neu-stats-card__sparkline"
|
|
139
|
+
viewBox="0 0 100 30"
|
|
140
|
+
preserveAspectRatio="none"
|
|
141
|
+
aria-hidden="true"
|
|
142
|
+
>
|
|
143
|
+
<polyline
|
|
144
|
+
class="neu-stats-card__spark-line"
|
|
145
|
+
[attr.points]="sparkPoints()"
|
|
146
|
+
fill="none"
|
|
147
|
+
stroke-width="1.5"
|
|
148
|
+
stroke-linecap="round"
|
|
149
|
+
stroke-linejoin="round"
|
|
150
|
+
/>
|
|
151
|
+
</svg>
|
|
152
|
+
}
|
|
153
|
+
</div>
|
|
154
|
+
</div>
|
|
155
|
+
`, styles: [".neu-stats-card{display:block}.neu-stats-card__inner{background:var(--neu-surface);border:1px solid var(--neu-border);border-radius:var(--neu-radius-lg, 16px);padding:var(--neu-space-5, 1.25rem);display:flex;flex-direction:column;gap:var(--neu-space-3, .75rem);transition:box-shadow var(--neu-transition)}.neu-stats-card__inner:hover{box-shadow:var(--neu-shadow-sm)}.neu-stats-card__header{display:flex;align-items:center;gap:var(--neu-space-2, .5rem)}.neu-stats-card__icon{display:flex;align-items:center;justify-content:center;width:32px;height:32px;border-radius:var(--neu-radius, 8px);background:var(--neu-primary-50, #eff6ff);color:var(--neu-primary);flex-shrink:0}.neu-stats-card__title{font-size:var(--neu-text-sm, .875rem);font-weight:500;color:var(--neu-text-muted);line-height:1.3}.neu-stats-card__value{font-size:var(--neu-text-2xl, 1.5rem);font-weight:700;color:var(--neu-text);letter-spacing:-.03em;line-height:1.1;font-family:var(--neu-font-sans)}.neu-stats-card__footer{display:flex;align-items:center;gap:var(--neu-space-2, .5rem);margin-top:auto}.neu-stats-card__change{display:inline-flex;align-items:center;gap:3px;font-size:var(--neu-text-xs, .75rem);font-weight:600;padding:2px 6px;border-radius:var(--neu-radius-full, 999px)}.neu-stats-card__change--up{background:#dcfce7;color:#15803d}.neu-stats-card__change--down{background:#fee2e2;color:#b91c1c}.neu-stats-card__change--neutral{background:var(--neu-surface-2, #f8fafc);color:var(--neu-text-muted)}.neu-stats-card__label{font-size:var(--neu-text-xs, .75rem);color:var(--neu-text-muted)}.neu-stats-card__sparkline{margin-left:auto;width:80px;height:30px;flex-shrink:0}.neu-stats-card__spark-line{stroke:var(--neu-primary);opacity:.8}\n"] }]
|
|
156
|
+
}], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], change: [{ type: i0.Input, args: [{ isSignal: true, alias: "change", required: false }] }], trend: [{ type: i0.Input, args: [{ isSignal: true, alias: "trend", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], sparkData: [{ type: i0.Input, args: [{ isSignal: true, alias: "sparkData", required: false }] }] } });
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Generated bundle index. Do not edit.
|
|
160
|
+
*/
|
|
161
|
+
|
|
162
|
+
export { NeuStatsCardComponent };
|
|
163
|
+
//# sourceMappingURL=neural-ui-core-stats-card.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"neural-ui-core-stats-card.mjs","sources":["../../../../projects/ui-core/stats-card/neu-stats-card.component.ts","../../../../projects/ui-core/stats-card/neural-ui-core-stats-card.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n ViewEncapsulation,\n computed,\n input,\n} from '@angular/core';\nimport { NeuIconComponent } from '@neural-ui/core/icon';\n\nexport type NeuStatsTrend = 'up' | 'down' | 'neutral';\n\n/**\n * NeuStatsCard — Card de métrica con título, valor, tendencia y sparkline.\n *\n * Uso:\n * <neu-stats-card\n * title=\"Ingresos\"\n * value=\"$12,450\"\n * change=\"+12.5%\"\n * trend=\"up\"\n * icon=\"lucideDollarSign\"\n * [sparkData]=\"[30,45,38,52,60,55,70]\"\n * />\n */\n@Component({\n selector: 'neu-stats-card',\n imports: [NeuIconComponent],\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { class: 'neu-stats-card' },\n template: `\n <div class=\"neu-stats-card__inner\">\n <!-- Header: icono + título -->\n <div class=\"neu-stats-card__header\">\n @if (icon()) {\n <span class=\"neu-stats-card__icon\">\n <neu-icon [name]=\"icon()\" size=\"1.125rem\" />\n </span>\n }\n <span class=\"neu-stats-card__title\">{{ title() }}</span>\n </div>\n\n <!-- Valor principal -->\n <div class=\"neu-stats-card__value\">{{ value() }}</div>\n\n <!-- Footer: cambio + sparkline -->\n <div class=\"neu-stats-card__footer\">\n @if (change()) {\n <span class=\"neu-stats-card__change neu-stats-card__change--{{ trend() }}\">\n @if (trend() === 'up') {\n <neu-icon name=\"lucideTrendingUp\" size=\"0.875rem\" />\n } @else if (trend() === 'down') {\n <neu-icon name=\"lucideTrendingDown\" size=\"0.875rem\" />\n } @else {\n <neu-icon name=\"lucideMinus\" size=\"0.875rem\" />\n }\n {{ change() }}\n </span>\n }\n @if (label()) {\n <span class=\"neu-stats-card__label\">{{ label() }}</span>\n }\n\n @if (sparkData().length > 1) {\n <svg\n class=\"neu-stats-card__sparkline\"\n viewBox=\"0 0 100 30\"\n preserveAspectRatio=\"none\"\n aria-hidden=\"true\"\n >\n <polyline\n class=\"neu-stats-card__spark-line\"\n [attr.points]=\"sparkPoints()\"\n fill=\"none\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n }\n </div>\n </div>\n `,\n styleUrl: './neu-stats-card.component.scss',\n})\nexport class NeuStatsCardComponent {\n /** Título o etiqueta de la métrica. / Metric title or label. */\n title = input<string>('');\n /** Valor principal formateado (p.ej. \"$12,450\" o \"98.2%\"). / Main formatted value (e.g. \"$12,450\" or \"98.2%\"). */\n value = input<string>('');\n /** Cambio porcentual o absoluto (p.ej. \"+12.5%\" o \"-3\"). / Percentage or absolute change (e.g. \"+12.5%\" or \"-3\"). */\n change = input<string>('');\n /** Dirección del cambio. Afecta el color del change. / Change direction. Affects the change color. */\n trend = input<NeuStatsTrend>('neutral');\n /** Texto auxiliar bajo el cambio (p.ej. \"vs. mes anterior\"). / Auxiliary text below the change (e.g. \"vs. previous month\"). */\n label = input<string>('');\n /** Nombre del icono Lucide para el encabezado. / Lucide icon name for the header. */\n icon = input<string>('');\n /** Array de valores numéricos para la sparkline. Mín. 2 puntos. / Array of numeric values for the sparkline. Min. 2 points. */\n sparkData = input<number[]>([]);\n\n /** @internal Genera los puntos SVG de la sparkline / Generates the SVG sparkline points */\n protected readonly sparkPoints = computed(() => {\n const data = this.sparkData();\n if (data.length < 2) return '';\n const min = Math.min(...data);\n const max = Math.max(...data);\n const range = max - min || 1;\n const step = 100 / (data.length - 1);\n return data\n .map((v, i) => `${(i * step).toFixed(1)},${(30 - ((v - min) / range) * 25 + 2.5).toFixed(1)}`)\n .join(' ');\n });\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public_api';\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { inject, DestroyRef, input, output, signal, ChangeDetectionStrategy, ViewEncapsulation, Component } from '@angular/core';
|
|
3
|
+
import { NeuTooltipDirective } from '@neural-ui/core/tooltip';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* NeuralUI Stepper Component
|
|
7
|
+
*
|
|
8
|
+
* Wizard paso a paso con estado de completado, lineal u opcional.
|
|
9
|
+
* Expone métodos next() / prev() y emite stepChange.
|
|
10
|
+
*
|
|
11
|
+
* Uso:
|
|
12
|
+
* <neu-stepper [steps]="steps" [activeStep]="step" (stepChange)="step = $event">
|
|
13
|
+
* <ng-template neuStepContent>Contenido paso 1</ng-template>
|
|
14
|
+
* <ng-template neuStepContent>Contenido paso 2</ng-template>
|
|
15
|
+
* </neu-stepper>
|
|
16
|
+
*/
|
|
17
|
+
class NeuStepperComponent {
|
|
18
|
+
_destroyRef = inject(DestroyRef);
|
|
19
|
+
/** Pasos del wizard / Wizard steps */
|
|
20
|
+
steps = input([], ...(ngDevMode ? [{ debugName: "steps" }] : /* istanbul ignore next */ []));
|
|
21
|
+
/** Índice del paso activo (0-based) / Active step index (0-based) */
|
|
22
|
+
activeStep = input(0, ...(ngDevMode ? [{ debugName: "activeStep" }] : /* istanbul ignore next */ []));
|
|
23
|
+
/** Si true, solo permite ir hacia adelante secuencialmente / If true, only allows moving forward sequentially */
|
|
24
|
+
linear = input(false, ...(ngDevMode ? [{ debugName: "linear" }] : /* istanbul ignore next */ []));
|
|
25
|
+
/** Emite el nuevo índice al cambiar / Emits the new index on change */
|
|
26
|
+
stepChange = output();
|
|
27
|
+
/** Set de pasos completados / Set of completed steps */
|
|
28
|
+
_completed = signal(new Set(), ...(ngDevMode ? [{ debugName: "_completed" }] : /* istanbul ignore next */ []));
|
|
29
|
+
isCompact = signal(typeof window !== 'undefined' ? window.innerWidth <= 767 : false, ...(ngDevMode ? [{ debugName: "isCompact" }] : /* istanbul ignore next */ []));
|
|
30
|
+
isCompleted = (i) => this._completed().has(i) || (this.steps()[i]?.completed ?? false) || i < this.activeStep();
|
|
31
|
+
stepTooltip = (step) => step.description ? `${step.label} - ${step.description}` : step.label;
|
|
32
|
+
stepAriaLabel = (step) => this.stepTooltip(step);
|
|
33
|
+
constructor() {
|
|
34
|
+
if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const mediaQuery = window.matchMedia('(max-width: 767px)');
|
|
38
|
+
const updateCompact = (event) => {
|
|
39
|
+
this.isCompact.set(event?.matches ?? mediaQuery.matches);
|
|
40
|
+
};
|
|
41
|
+
updateCompact();
|
|
42
|
+
mediaQuery.addEventListener('change', updateCompact);
|
|
43
|
+
this._destroyRef.onDestroy(() => mediaQuery.removeEventListener('change', updateCompact));
|
|
44
|
+
}
|
|
45
|
+
goTo(i) {
|
|
46
|
+
const step = this.steps()[i];
|
|
47
|
+
if (step?.disabled)
|
|
48
|
+
return;
|
|
49
|
+
if (this.linear() && i > this.activeStep() + 1 && !this.isCompleted(this.activeStep()))
|
|
50
|
+
return;
|
|
51
|
+
this.stepChange.emit(i);
|
|
52
|
+
}
|
|
53
|
+
/** Marca el paso actual como completado y avanza al siguiente / Marks the current step as completed and advances to the next */
|
|
54
|
+
next() {
|
|
55
|
+
const current = this.activeStep();
|
|
56
|
+
const updated = new Set(this._completed());
|
|
57
|
+
updated.add(current);
|
|
58
|
+
this._completed.set(updated);
|
|
59
|
+
if (current < this.steps().length - 1) {
|
|
60
|
+
this.stepChange.emit(current + 1);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
prev() {
|
|
64
|
+
const current = this.activeStep();
|
|
65
|
+
if (current > 0) {
|
|
66
|
+
this.stepChange.emit(current - 1);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: NeuStepperComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
70
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: NeuStepperComponent, isStandalone: true, selector: "neu-stepper", inputs: { steps: { classPropertyName: "steps", publicName: "steps", isSignal: true, isRequired: false, transformFunction: null }, activeStep: { classPropertyName: "activeStep", publicName: "activeStep", isSignal: true, isRequired: false, transformFunction: null }, linear: { classPropertyName: "linear", publicName: "linear", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { stepChange: "stepChange" }, ngImport: i0, template: `
|
|
71
|
+
<!-- Cabecera de pasos -->
|
|
72
|
+
<div class="neu-stepper">
|
|
73
|
+
<div class="neu-stepper__header">
|
|
74
|
+
@for (step of steps(); track step.label; let i = $index; let last = $last) {
|
|
75
|
+
<div
|
|
76
|
+
class="neu-stepper__step"
|
|
77
|
+
[class.neu-stepper__step--active]="i === activeStep()"
|
|
78
|
+
[class.neu-stepper__step--completed]="isCompleted(i)"
|
|
79
|
+
[class.neu-stepper__step--disabled]="step.disabled"
|
|
80
|
+
>
|
|
81
|
+
@if (!last) {
|
|
82
|
+
<div
|
|
83
|
+
class="neu-stepper__connector"
|
|
84
|
+
[class.neu-stepper__connector--done]="isCompleted(i) || i < activeStep()"
|
|
85
|
+
></div>
|
|
86
|
+
}
|
|
87
|
+
<button
|
|
88
|
+
class="neu-stepper__step-btn"
|
|
89
|
+
type="button"
|
|
90
|
+
[disabled]="step.disabled"
|
|
91
|
+
[attr.aria-label]="stepAriaLabel(step)"
|
|
92
|
+
[neuTooltip]="stepTooltip(step)"
|
|
93
|
+
[neuTooltipDisabled]="!isCompact()"
|
|
94
|
+
neuTooltipPosition="bottom"
|
|
95
|
+
(click)="goTo(i)"
|
|
96
|
+
>
|
|
97
|
+
<span class="neu-stepper__indicator">
|
|
98
|
+
@if (isCompleted(i)) {
|
|
99
|
+
<svg
|
|
100
|
+
width="14"
|
|
101
|
+
height="14"
|
|
102
|
+
viewBox="0 0 24 24"
|
|
103
|
+
fill="none"
|
|
104
|
+
stroke="currentColor"
|
|
105
|
+
stroke-width="3"
|
|
106
|
+
stroke-linecap="round"
|
|
107
|
+
aria-hidden="true"
|
|
108
|
+
>
|
|
109
|
+
<polyline points="20 6 9 17 4 12" />
|
|
110
|
+
</svg>
|
|
111
|
+
} @else {
|
|
112
|
+
{{ i + 1 }}
|
|
113
|
+
}
|
|
114
|
+
</span>
|
|
115
|
+
<span class="neu-stepper__step-info">
|
|
116
|
+
<span class="neu-stepper__step-label">{{ step.label }}</span>
|
|
117
|
+
@if (step.description) {
|
|
118
|
+
<span class="neu-stepper__step-desc">{{ step.description }}</span>
|
|
119
|
+
}
|
|
120
|
+
</span>
|
|
121
|
+
</button>
|
|
122
|
+
</div>
|
|
123
|
+
}
|
|
124
|
+
</div>
|
|
125
|
+
|
|
126
|
+
<!-- Panel de contenido -->
|
|
127
|
+
<div class="neu-stepper__content">
|
|
128
|
+
<ng-content />
|
|
129
|
+
</div>
|
|
130
|
+
</div>
|
|
131
|
+
`, isInline: true, styles: [".neu-stepper{--neu-stepper-indicator-size: 32px;--neu-stepper-indicator-radius: calc(var(--neu-stepper-indicator-size) / 2);--neu-stepper-connector-gap: 8px;display:flex;flex-direction:column;gap:var(--neu-space-6);font-family:var(--neu-font-sans)}.neu-stepper__header{display:flex;align-items:flex-start;gap:0;overflow-x:auto;scrollbar-width:thin;padding-bottom:var(--neu-space-2)}.neu-stepper__step{position:relative;display:flex;flex-direction:column;align-items:center;flex:1 0 8.5rem;min-width:8.5rem}.neu-stepper__step-btn{display:flex;flex-direction:column;align-items:center;width:100%;gap:var(--neu-space-3);background:none;border:none;padding:0 var(--neu-space-2);cursor:pointer;min-width:0;outline:none;position:relative;z-index:1}.neu-stepper__step-btn:focus-visible .neu-stepper__indicator{box-shadow:0 0 0 3px var(--neu-primary-100)}.neu-stepper__step-btn:disabled{cursor:not-allowed}.neu-stepper__indicator{width:var(--neu-stepper-indicator-size);height:var(--neu-stepper-indicator-size);border-radius:50%;display:flex;align-items:center;justify-content:center;flex-shrink:0;font-size:.8125rem;font-weight:700;background:var(--neu-surface-2);border:2px solid var(--neu-border);color:var(--neu-text-disabled);transition:background var(--neu-transition),border-color var(--neu-transition),color var(--neu-transition)}.neu-stepper__step--active .neu-stepper__indicator{background:var(--neu-primary);border-color:var(--neu-primary);color:var(--neu-primary-fg)}.neu-stepper__step--completed .neu-stepper__indicator{background:var(--neu-success);border-color:var(--neu-success);color:var(--neu-primary-fg)}.neu-stepper__step-info{display:flex;flex-direction:column;align-items:center;text-align:center;min-width:0;width:100%;gap:2px}.neu-stepper__step-label{display:block;font-size:var(--neu-text-sm);font-weight:600;color:var(--neu-text-muted);transition:color var(--neu-transition);line-height:1.25;overflow-wrap:anywhere}.neu-stepper__step--active .neu-stepper__step-label{color:var(--neu-text)}.neu-stepper__step--completed .neu-stepper__step-label{color:var(--neu-text-muted)}.neu-stepper__step-desc{display:block;max-width:100%;font-size:var(--neu-text-xs);color:var(--neu-text-disabled);margin-top:1px;line-height:1.35;overflow-wrap:anywhere}.neu-stepper__connector{position:absolute;top:calc(var(--neu-stepper-indicator-radius) - 1px);left:calc(50% + var(--neu-stepper-indicator-radius) + var(--neu-stepper-connector-gap));width:calc(100% - var(--neu-stepper-indicator-size) - var(--neu-stepper-connector-gap) * 2);height:2px;background:var(--neu-border);transition:background var(--neu-transition);z-index:0}.neu-stepper__connector--done{background:var(--neu-success)}.neu-stepper__content{min-height:80px}@media(max-width:767px){.neu-stepper__header{align-items:flex-start;gap:var(--neu-space-2);scroll-snap-type:x proximity}.neu-stepper__step{flex:0 0 auto;min-width:3.5rem}.neu-stepper__step-btn{padding-inline:0}.neu-stepper__connector{left:calc(50% + var(--neu-stepper-indicator-radius) + var(--neu-stepper-connector-gap));width:calc(100% - var(--neu-stepper-indicator-size) - var(--neu-stepper-connector-gap) * 2)}.neu-stepper__step-info{display:none}}\n"], dependencies: [{ kind: "directive", type: NeuTooltipDirective, selector: "[neuTooltip]", inputs: ["neuTooltip", "neuTooltipPosition", "neuTooltipDisabled"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
132
|
+
}
|
|
133
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: NeuStepperComponent, decorators: [{
|
|
134
|
+
type: Component,
|
|
135
|
+
args: [{ selector: 'neu-stepper', imports: [NeuTooltipDirective], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
136
|
+
<!-- Cabecera de pasos -->
|
|
137
|
+
<div class="neu-stepper">
|
|
138
|
+
<div class="neu-stepper__header">
|
|
139
|
+
@for (step of steps(); track step.label; let i = $index; let last = $last) {
|
|
140
|
+
<div
|
|
141
|
+
class="neu-stepper__step"
|
|
142
|
+
[class.neu-stepper__step--active]="i === activeStep()"
|
|
143
|
+
[class.neu-stepper__step--completed]="isCompleted(i)"
|
|
144
|
+
[class.neu-stepper__step--disabled]="step.disabled"
|
|
145
|
+
>
|
|
146
|
+
@if (!last) {
|
|
147
|
+
<div
|
|
148
|
+
class="neu-stepper__connector"
|
|
149
|
+
[class.neu-stepper__connector--done]="isCompleted(i) || i < activeStep()"
|
|
150
|
+
></div>
|
|
151
|
+
}
|
|
152
|
+
<button
|
|
153
|
+
class="neu-stepper__step-btn"
|
|
154
|
+
type="button"
|
|
155
|
+
[disabled]="step.disabled"
|
|
156
|
+
[attr.aria-label]="stepAriaLabel(step)"
|
|
157
|
+
[neuTooltip]="stepTooltip(step)"
|
|
158
|
+
[neuTooltipDisabled]="!isCompact()"
|
|
159
|
+
neuTooltipPosition="bottom"
|
|
160
|
+
(click)="goTo(i)"
|
|
161
|
+
>
|
|
162
|
+
<span class="neu-stepper__indicator">
|
|
163
|
+
@if (isCompleted(i)) {
|
|
164
|
+
<svg
|
|
165
|
+
width="14"
|
|
166
|
+
height="14"
|
|
167
|
+
viewBox="0 0 24 24"
|
|
168
|
+
fill="none"
|
|
169
|
+
stroke="currentColor"
|
|
170
|
+
stroke-width="3"
|
|
171
|
+
stroke-linecap="round"
|
|
172
|
+
aria-hidden="true"
|
|
173
|
+
>
|
|
174
|
+
<polyline points="20 6 9 17 4 12" />
|
|
175
|
+
</svg>
|
|
176
|
+
} @else {
|
|
177
|
+
{{ i + 1 }}
|
|
178
|
+
}
|
|
179
|
+
</span>
|
|
180
|
+
<span class="neu-stepper__step-info">
|
|
181
|
+
<span class="neu-stepper__step-label">{{ step.label }}</span>
|
|
182
|
+
@if (step.description) {
|
|
183
|
+
<span class="neu-stepper__step-desc">{{ step.description }}</span>
|
|
184
|
+
}
|
|
185
|
+
</span>
|
|
186
|
+
</button>
|
|
187
|
+
</div>
|
|
188
|
+
}
|
|
189
|
+
</div>
|
|
190
|
+
|
|
191
|
+
<!-- Panel de contenido -->
|
|
192
|
+
<div class="neu-stepper__content">
|
|
193
|
+
<ng-content />
|
|
194
|
+
</div>
|
|
195
|
+
</div>
|
|
196
|
+
`, styles: [".neu-stepper{--neu-stepper-indicator-size: 32px;--neu-stepper-indicator-radius: calc(var(--neu-stepper-indicator-size) / 2);--neu-stepper-connector-gap: 8px;display:flex;flex-direction:column;gap:var(--neu-space-6);font-family:var(--neu-font-sans)}.neu-stepper__header{display:flex;align-items:flex-start;gap:0;overflow-x:auto;scrollbar-width:thin;padding-bottom:var(--neu-space-2)}.neu-stepper__step{position:relative;display:flex;flex-direction:column;align-items:center;flex:1 0 8.5rem;min-width:8.5rem}.neu-stepper__step-btn{display:flex;flex-direction:column;align-items:center;width:100%;gap:var(--neu-space-3);background:none;border:none;padding:0 var(--neu-space-2);cursor:pointer;min-width:0;outline:none;position:relative;z-index:1}.neu-stepper__step-btn:focus-visible .neu-stepper__indicator{box-shadow:0 0 0 3px var(--neu-primary-100)}.neu-stepper__step-btn:disabled{cursor:not-allowed}.neu-stepper__indicator{width:var(--neu-stepper-indicator-size);height:var(--neu-stepper-indicator-size);border-radius:50%;display:flex;align-items:center;justify-content:center;flex-shrink:0;font-size:.8125rem;font-weight:700;background:var(--neu-surface-2);border:2px solid var(--neu-border);color:var(--neu-text-disabled);transition:background var(--neu-transition),border-color var(--neu-transition),color var(--neu-transition)}.neu-stepper__step--active .neu-stepper__indicator{background:var(--neu-primary);border-color:var(--neu-primary);color:var(--neu-primary-fg)}.neu-stepper__step--completed .neu-stepper__indicator{background:var(--neu-success);border-color:var(--neu-success);color:var(--neu-primary-fg)}.neu-stepper__step-info{display:flex;flex-direction:column;align-items:center;text-align:center;min-width:0;width:100%;gap:2px}.neu-stepper__step-label{display:block;font-size:var(--neu-text-sm);font-weight:600;color:var(--neu-text-muted);transition:color var(--neu-transition);line-height:1.25;overflow-wrap:anywhere}.neu-stepper__step--active .neu-stepper__step-label{color:var(--neu-text)}.neu-stepper__step--completed .neu-stepper__step-label{color:var(--neu-text-muted)}.neu-stepper__step-desc{display:block;max-width:100%;font-size:var(--neu-text-xs);color:var(--neu-text-disabled);margin-top:1px;line-height:1.35;overflow-wrap:anywhere}.neu-stepper__connector{position:absolute;top:calc(var(--neu-stepper-indicator-radius) - 1px);left:calc(50% + var(--neu-stepper-indicator-radius) + var(--neu-stepper-connector-gap));width:calc(100% - var(--neu-stepper-indicator-size) - var(--neu-stepper-connector-gap) * 2);height:2px;background:var(--neu-border);transition:background var(--neu-transition);z-index:0}.neu-stepper__connector--done{background:var(--neu-success)}.neu-stepper__content{min-height:80px}@media(max-width:767px){.neu-stepper__header{align-items:flex-start;gap:var(--neu-space-2);scroll-snap-type:x proximity}.neu-stepper__step{flex:0 0 auto;min-width:3.5rem}.neu-stepper__step-btn{padding-inline:0}.neu-stepper__connector{left:calc(50% + var(--neu-stepper-indicator-radius) + var(--neu-stepper-connector-gap));width:calc(100% - var(--neu-stepper-indicator-size) - var(--neu-stepper-connector-gap) * 2)}.neu-stepper__step-info{display:none}}\n"] }]
|
|
197
|
+
}], ctorParameters: () => [], propDecorators: { steps: [{ type: i0.Input, args: [{ isSignal: true, alias: "steps", required: false }] }], activeStep: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeStep", required: false }] }], linear: [{ type: i0.Input, args: [{ isSignal: true, alias: "linear", required: false }] }], stepChange: [{ type: i0.Output, args: ["stepChange"] }] } });
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Generated bundle index. Do not edit.
|
|
201
|
+
*/
|
|
202
|
+
|
|
203
|
+
export { NeuStepperComponent };
|
|
204
|
+
//# sourceMappingURL=neural-ui-core-stepper.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"neural-ui-core-stepper.mjs","sources":["../../../../projects/ui-core/stepper/neu-stepper.component.ts","../../../../projects/ui-core/stepper/neural-ui-core-stepper.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n DestroyRef,\n ViewEncapsulation,\n inject,\n input,\n output,\n signal,\n} from '@angular/core';\nimport { NeuTooltipDirective } from '@neural-ui/core/tooltip';\n\nexport interface NeuStepperStep {\n /** Etiqueta del paso / Step label */\n label: string;\n /** Descripción corta opcional / Optional short description */\n description?: string;\n /** Marca el paso como completado externamente / Marks the step as completed externally */\n completed?: boolean;\n /** Desactiva el paso / Disables the step */\n disabled?: boolean;\n}\n\n/**\n * NeuralUI Stepper Component\n *\n * Wizard paso a paso con estado de completado, lineal u opcional.\n * Expone métodos next() / prev() y emite stepChange.\n *\n * Uso:\n * <neu-stepper [steps]=\"steps\" [activeStep]=\"step\" (stepChange)=\"step = $event\">\n * <ng-template neuStepContent>Contenido paso 1</ng-template>\n * <ng-template neuStepContent>Contenido paso 2</ng-template>\n * </neu-stepper>\n */\n@Component({\n selector: 'neu-stepper',\n imports: [NeuTooltipDirective],\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <!-- Cabecera de pasos -->\n <div class=\"neu-stepper\">\n <div class=\"neu-stepper__header\">\n @for (step of steps(); track step.label; let i = $index; let last = $last) {\n <div\n class=\"neu-stepper__step\"\n [class.neu-stepper__step--active]=\"i === activeStep()\"\n [class.neu-stepper__step--completed]=\"isCompleted(i)\"\n [class.neu-stepper__step--disabled]=\"step.disabled\"\n >\n @if (!last) {\n <div\n class=\"neu-stepper__connector\"\n [class.neu-stepper__connector--done]=\"isCompleted(i) || i < activeStep()\"\n ></div>\n }\n <button\n class=\"neu-stepper__step-btn\"\n type=\"button\"\n [disabled]=\"step.disabled\"\n [attr.aria-label]=\"stepAriaLabel(step)\"\n [neuTooltip]=\"stepTooltip(step)\"\n [neuTooltipDisabled]=\"!isCompact()\"\n neuTooltipPosition=\"bottom\"\n (click)=\"goTo(i)\"\n >\n <span class=\"neu-stepper__indicator\">\n @if (isCompleted(i)) {\n <svg\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"3\"\n stroke-linecap=\"round\"\n aria-hidden=\"true\"\n >\n <polyline points=\"20 6 9 17 4 12\" />\n </svg>\n } @else {\n {{ i + 1 }}\n }\n </span>\n <span class=\"neu-stepper__step-info\">\n <span class=\"neu-stepper__step-label\">{{ step.label }}</span>\n @if (step.description) {\n <span class=\"neu-stepper__step-desc\">{{ step.description }}</span>\n }\n </span>\n </button>\n </div>\n }\n </div>\n\n <!-- Panel de contenido -->\n <div class=\"neu-stepper__content\">\n <ng-content />\n </div>\n </div>\n `,\n styleUrl: './neu-stepper.component.scss',\n})\nexport class NeuStepperComponent {\n private readonly _destroyRef = inject(DestroyRef);\n\n /** Pasos del wizard / Wizard steps */\n steps = input<NeuStepperStep[]>([]);\n\n /** Índice del paso activo (0-based) / Active step index (0-based) */\n activeStep = input<number>(0);\n\n /** Si true, solo permite ir hacia adelante secuencialmente / If true, only allows moving forward sequentially */\n linear = input<boolean>(false);\n\n /** Emite el nuevo índice al cambiar / Emits the new index on change */\n stepChange = output<number>();\n\n /** Set de pasos completados / Set of completed steps */\n private readonly _completed = signal<Set<number>>(new Set());\n readonly isCompact = signal(typeof window !== 'undefined' ? window.innerWidth <= 767 : false);\n\n readonly isCompleted = (i: number) =>\n this._completed().has(i) || (this.steps()[i]?.completed ?? false) || i < this.activeStep();\n\n readonly stepTooltip = (step: NeuStepperStep) =>\n step.description ? `${step.label} - ${step.description}` : step.label;\n\n readonly stepAriaLabel = (step: NeuStepperStep) => this.stepTooltip(step);\n\n constructor() {\n if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') {\n return;\n }\n\n const mediaQuery = window.matchMedia('(max-width: 767px)');\n const updateCompact = (event?: MediaQueryListEvent) => {\n this.isCompact.set(event?.matches ?? mediaQuery.matches);\n };\n\n updateCompact();\n mediaQuery.addEventListener('change', updateCompact);\n this._destroyRef.onDestroy(() => mediaQuery.removeEventListener('change', updateCompact));\n }\n\n goTo(i: number): void {\n const step = this.steps()[i];\n if (step?.disabled) return;\n if (this.linear() && i > this.activeStep() + 1 && !this.isCompleted(this.activeStep())) return;\n this.stepChange.emit(i);\n }\n\n /** Marca el paso actual como completado y avanza al siguiente / Marks the current step as completed and advances to the next */\n next(): void {\n const current = this.activeStep();\n const updated = new Set(this._completed());\n updated.add(current);\n this._completed.set(updated);\n if (current < this.steps().length - 1) {\n this.stepChange.emit(current + 1);\n }\n }\n\n prev(): void {\n const current = this.activeStep();\n if (current > 0) {\n this.stepChange.emit(current - 1);\n }\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public_api';\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { input, signal, computed, forwardRef, ChangeDetectionStrategy, ViewEncapsulation, Component } from '@angular/core';
|
|
3
|
+
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
|
4
|
+
|
|
5
|
+
let _neuSwitchIdSeq = 0;
|
|
6
|
+
/**
|
|
7
|
+
* NeuralUI Switch Component
|
|
8
|
+
*
|
|
9
|
+
* Toggle animado para formularios de configuración en dashboards.
|
|
10
|
+
* Usa el Electric-Blue (--neu-primary) cuando está activo.
|
|
11
|
+
*
|
|
12
|
+
* Uso:
|
|
13
|
+
* <neu-switch label="Notificaciones" [formControl]="notifsCtrl" />
|
|
14
|
+
*/
|
|
15
|
+
class NeuSwitchComponent {
|
|
16
|
+
label = input('', ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
|
|
17
|
+
name = input('', ...(ngDevMode ? [{ debugName: "name" }] : /* istanbul ignore next */ []));
|
|
18
|
+
disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
|
|
19
|
+
_id = `neu-switch-${_neuSwitchIdSeq++}`;
|
|
20
|
+
_checked = signal(false, ...(ngDevMode ? [{ debugName: "_checked" }] : /* istanbul ignore next */ []));
|
|
21
|
+
/** Estado disabled interno — combina el input `disabled` con el CVA setDisabledState / Internal disabled state — combines the `disabled` input with CVA setDisabledState */
|
|
22
|
+
_cvaDisabled = signal(false, ...(ngDevMode ? [{ debugName: "_cvaDisabled" }] : /* istanbul ignore next */ []));
|
|
23
|
+
_isDisabled = computed(() => this.disabled() || this._cvaDisabled(), ...(ngDevMode ? [{ debugName: "_isDisabled" }] : /* istanbul ignore next */ []));
|
|
24
|
+
_onChange = () => { };
|
|
25
|
+
_onTouched = () => { };
|
|
26
|
+
onChange(event) {
|
|
27
|
+
const checked = event.target.checked;
|
|
28
|
+
this._checked.set(checked);
|
|
29
|
+
this._onChange(checked);
|
|
30
|
+
}
|
|
31
|
+
onBlur() {
|
|
32
|
+
this._onTouched();
|
|
33
|
+
}
|
|
34
|
+
writeValue(val) {
|
|
35
|
+
this._checked.set(!!val);
|
|
36
|
+
}
|
|
37
|
+
registerOnChange(fn) {
|
|
38
|
+
this._onChange = fn;
|
|
39
|
+
}
|
|
40
|
+
registerOnTouched(fn) {
|
|
41
|
+
this._onTouched = fn;
|
|
42
|
+
}
|
|
43
|
+
setDisabledState(isDisabled) {
|
|
44
|
+
this._cvaDisabled.set(isDisabled);
|
|
45
|
+
}
|
|
46
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: NeuSwitchComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
47
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: NeuSwitchComponent, isStandalone: true, selector: "neu-switch", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "neu-switch-host" }, providers: [
|
|
48
|
+
{
|
|
49
|
+
provide: NG_VALUE_ACCESSOR,
|
|
50
|
+
useExisting: forwardRef(() => NeuSwitchComponent),
|
|
51
|
+
multi: true,
|
|
52
|
+
},
|
|
53
|
+
], ngImport: i0, template: `
|
|
54
|
+
<label class="neu-switch" [class.neu-switch--disabled]="_isDisabled()" [for]="_id">
|
|
55
|
+
<input
|
|
56
|
+
type="checkbox"
|
|
57
|
+
role="switch"
|
|
58
|
+
class="neu-switch__input"
|
|
59
|
+
[id]="_id"
|
|
60
|
+
[attr.name]="name() || null"
|
|
61
|
+
[checked]="_checked()"
|
|
62
|
+
[disabled]="_isDisabled()"
|
|
63
|
+
(change)="onChange($event)"
|
|
64
|
+
(blur)="onBlur()"
|
|
65
|
+
/>
|
|
66
|
+
<span class="neu-switch__track" [class.neu-switch__track--on]="_checked()">
|
|
67
|
+
<span class="neu-switch__thumb" [class.neu-switch__thumb--on]="_checked()"></span>
|
|
68
|
+
</span>
|
|
69
|
+
@if (label()) {
|
|
70
|
+
<span class="neu-switch__label">{{ label() }}</span>
|
|
71
|
+
}
|
|
72
|
+
</label>
|
|
73
|
+
`, isInline: true, styles: [".neu-switch-host{display:inline-block}.neu-switch{display:inline-flex;align-items:center;gap:var(--neu-space-3);cursor:pointer;-webkit-user-select:none;user-select:none}.neu-switch--disabled{opacity:.5;pointer-events:none}.neu-switch__input{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.neu-switch__input:focus-visible+.neu-switch__track{outline:2px solid var(--neu-primary);outline-offset:2px}.neu-switch__track{position:relative;display:inline-block;width:44px;height:24px;background:var(--neu-surface-3);border-radius:var(--neu-radius-full);transition:background-color var(--neu-transition),box-shadow var(--neu-transition);flex-shrink:0}.neu-switch__track--on{background:var(--neu-primary);box-shadow:0 0 12px #007aff59}.neu-switch__thumb{position:absolute;top:3px;left:3px;width:18px;height:18px;background:#fff;border-radius:var(--neu-radius-full);box-shadow:var(--neu-shadow-xs);transition:transform var(--neu-transition-bounce)}.neu-switch__thumb--on{transform:translate(20px)}.neu-switch__label{font-family:var(--neu-font-sans);font-size:var(--neu-text-sm);color:var(--neu-text);font-weight:500;line-height:1}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
74
|
+
}
|
|
75
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: NeuSwitchComponent, decorators: [{
|
|
76
|
+
type: Component,
|
|
77
|
+
args: [{ selector: 'neu-switch', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, host: { class: 'neu-switch-host' }, providers: [
|
|
78
|
+
{
|
|
79
|
+
provide: NG_VALUE_ACCESSOR,
|
|
80
|
+
useExisting: forwardRef(() => NeuSwitchComponent),
|
|
81
|
+
multi: true,
|
|
82
|
+
},
|
|
83
|
+
], template: `
|
|
84
|
+
<label class="neu-switch" [class.neu-switch--disabled]="_isDisabled()" [for]="_id">
|
|
85
|
+
<input
|
|
86
|
+
type="checkbox"
|
|
87
|
+
role="switch"
|
|
88
|
+
class="neu-switch__input"
|
|
89
|
+
[id]="_id"
|
|
90
|
+
[attr.name]="name() || null"
|
|
91
|
+
[checked]="_checked()"
|
|
92
|
+
[disabled]="_isDisabled()"
|
|
93
|
+
(change)="onChange($event)"
|
|
94
|
+
(blur)="onBlur()"
|
|
95
|
+
/>
|
|
96
|
+
<span class="neu-switch__track" [class.neu-switch__track--on]="_checked()">
|
|
97
|
+
<span class="neu-switch__thumb" [class.neu-switch__thumb--on]="_checked()"></span>
|
|
98
|
+
</span>
|
|
99
|
+
@if (label()) {
|
|
100
|
+
<span class="neu-switch__label">{{ label() }}</span>
|
|
101
|
+
}
|
|
102
|
+
</label>
|
|
103
|
+
`, styles: [".neu-switch-host{display:inline-block}.neu-switch{display:inline-flex;align-items:center;gap:var(--neu-space-3);cursor:pointer;-webkit-user-select:none;user-select:none}.neu-switch--disabled{opacity:.5;pointer-events:none}.neu-switch__input{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.neu-switch__input:focus-visible+.neu-switch__track{outline:2px solid var(--neu-primary);outline-offset:2px}.neu-switch__track{position:relative;display:inline-block;width:44px;height:24px;background:var(--neu-surface-3);border-radius:var(--neu-radius-full);transition:background-color var(--neu-transition),box-shadow var(--neu-transition);flex-shrink:0}.neu-switch__track--on{background:var(--neu-primary);box-shadow:0 0 12px #007aff59}.neu-switch__thumb{position:absolute;top:3px;left:3px;width:18px;height:18px;background:#fff;border-radius:var(--neu-radius-full);box-shadow:var(--neu-shadow-xs);transition:transform var(--neu-transition-bounce)}.neu-switch__thumb--on{transform:translate(20px)}.neu-switch__label{font-family:var(--neu-font-sans);font-size:var(--neu-text-sm);color:var(--neu-text);font-weight:500;line-height:1}\n"] }]
|
|
104
|
+
}], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }] } });
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Generated bundle index. Do not edit.
|
|
108
|
+
*/
|
|
109
|
+
|
|
110
|
+
export { NeuSwitchComponent };
|
|
111
|
+
//# sourceMappingURL=neural-ui-core-switch.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"neural-ui-core-switch.mjs","sources":["../../../../projects/ui-core/switch/neu-switch.component.ts","../../../../projects/ui-core/switch/neural-ui-core-switch.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n ViewEncapsulation,\n computed,\n forwardRef,\n input,\n signal,\n} from '@angular/core';\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\n\nlet _neuSwitchIdSeq = 0;\n\n/**\n * NeuralUI Switch Component\n *\n * Toggle animado para formularios de configuración en dashboards.\n * Usa el Electric-Blue (--neu-primary) cuando está activo.\n *\n * Uso:\n * <neu-switch label=\"Notificaciones\" [formControl]=\"notifsCtrl\" />\n */\n@Component({\n selector: 'neu-switch',\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { class: 'neu-switch-host' },\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => NeuSwitchComponent),\n multi: true,\n },\n ],\n template: `\n <label class=\"neu-switch\" [class.neu-switch--disabled]=\"_isDisabled()\" [for]=\"_id\">\n <input\n type=\"checkbox\"\n role=\"switch\"\n class=\"neu-switch__input\"\n [id]=\"_id\"\n [attr.name]=\"name() || null\"\n [checked]=\"_checked()\"\n [disabled]=\"_isDisabled()\"\n (change)=\"onChange($event)\"\n (blur)=\"onBlur()\"\n />\n <span class=\"neu-switch__track\" [class.neu-switch__track--on]=\"_checked()\">\n <span class=\"neu-switch__thumb\" [class.neu-switch__thumb--on]=\"_checked()\"></span>\n </span>\n @if (label()) {\n <span class=\"neu-switch__label\">{{ label() }}</span>\n }\n </label>\n `,\n styleUrl: './neu-switch.component.scss',\n})\nexport class NeuSwitchComponent implements ControlValueAccessor {\n readonly label = input<string>('');\n readonly name = input<string>('');\n readonly disabled = input<boolean>(false);\n\n readonly _id = `neu-switch-${_neuSwitchIdSeq++}`;\n\n protected readonly _checked = signal(false);\n /** Estado disabled interno — combina el input `disabled` con el CVA setDisabledState / Internal disabled state — combines the `disabled` input with CVA setDisabledState */\n private readonly _cvaDisabled = signal(false);\n protected readonly _isDisabled = computed(() => this.disabled() || this._cvaDisabled());\n\n private _onChange: (v: boolean) => void = () => {};\n private _onTouched: () => void = () => {};\n\n onChange(event: Event): void {\n const checked = (event.target as HTMLInputElement).checked;\n this._checked.set(checked);\n this._onChange(checked);\n }\n\n onBlur(): void {\n this._onTouched();\n }\n\n writeValue(val: unknown): void {\n this._checked.set(!!val);\n }\n\n registerOnChange(fn: (v: boolean) => void): void {\n this._onChange = fn;\n }\n\n registerOnTouched(fn: () => void): void {\n this._onTouched = fn;\n }\n\n setDisabledState(isDisabled: boolean): void {\n this._cvaDisabled.set(isDisabled);\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public_api';\n"],"names":[],"mappings":"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"}
|