@neural-ui/core 1.2.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +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 +332 -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 +748 -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 +638 -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 +1860 -0
- package/fesm2022/neural-ui-core-table.mjs.map +1 -0
- package/fesm2022/neural-ui-core-tabs.mjs +246 -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 +209 -7
- 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 +69 -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 +129 -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 +161 -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 +282 -0
- package/types/neural-ui-core-tabs.d.ts +76 -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 +5 -2107
- package/url-state/package.json +4 -0
- package/virtual-list/package.json +4 -0
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { input, output, signal, computed, forwardRef, ChangeDetectionStrategy, ViewEncapsulation, Component } from '@angular/core';
|
|
3
|
+
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
|
4
|
+
import { NeuIconComponent } from '@neural-ui/core/icon';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* NeuralUI ToggleButtonGroup Component
|
|
8
|
+
*
|
|
9
|
+
* Grupo de botones de selección (single o múltiple). / Selection button group (single or multiple).
|
|
10
|
+
* Selector segmentado para selección única o múltiple. / Segmented selector for single or multiple selection.
|
|
11
|
+
*
|
|
12
|
+
* Uso (single):
|
|
13
|
+
* <neu-toggle-button-group [options]="opts" [formControl]="valueCtrl" />
|
|
14
|
+
*
|
|
15
|
+
* Uso (múltiple):
|
|
16
|
+
* <neu-toggle-button-group [options]="opts" [multiple]="true" [formControl]="valuesCtrl" />
|
|
17
|
+
*/
|
|
18
|
+
class NeuToggleButtonGroupComponent {
|
|
19
|
+
/** Lista de opciones del grupo / Group option list */
|
|
20
|
+
options = input([], ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
|
|
21
|
+
/**
|
|
22
|
+
* Permite seleccionar múltiples opciones.
|
|
23
|
+
* - false (por defecto): valor es `T | null`
|
|
24
|
+
* - true: valor es `T[]`
|
|
25
|
+
*/
|
|
26
|
+
multiple = input(false, ...(ngDevMode ? [{ debugName: "multiple" }] : /* istanbul ignore next */ []));
|
|
27
|
+
/** Tamaño visual / Visual size */
|
|
28
|
+
size = input('md', ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
29
|
+
/** Deshabilita todo el grupo / Disables the entire group */
|
|
30
|
+
disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
|
|
31
|
+
/** Emite el nuevo valor al cambiar (útil sin formControl) / Emits the new value on change (useful without formControl) */
|
|
32
|
+
neuChange = output();
|
|
33
|
+
_value = signal(null, ...(ngDevMode ? [{ debugName: "_value" }] : /* istanbul ignore next */ []));
|
|
34
|
+
_isDisabled = signal(false, ...(ngDevMode ? [{ debugName: "_isDisabled" }] : /* istanbul ignore next */ []));
|
|
35
|
+
groupClasses = computed(() => ({
|
|
36
|
+
'neu-toggle-group': true,
|
|
37
|
+
[`neu-toggle-group--${this.size()}`]: true,
|
|
38
|
+
'neu-toggle-group--disabled': this._isDisabled(),
|
|
39
|
+
}), ...(ngDevMode ? [{ debugName: "groupClasses" }] : /* istanbul ignore next */ []));
|
|
40
|
+
isSelected(value) {
|
|
41
|
+
const v = this._value();
|
|
42
|
+
if (this.multiple()) {
|
|
43
|
+
return Array.isArray(v) && v.includes(value);
|
|
44
|
+
}
|
|
45
|
+
return v === value;
|
|
46
|
+
}
|
|
47
|
+
toggle(opt) {
|
|
48
|
+
if (opt.disabled || this._isDisabled())
|
|
49
|
+
return;
|
|
50
|
+
let next;
|
|
51
|
+
if (this.multiple()) {
|
|
52
|
+
const current = Array.isArray(this._value()) ? this._value() : [];
|
|
53
|
+
const idx = current.indexOf(opt.value);
|
|
54
|
+
next = idx >= 0 ? current.filter((_, i) => i !== idx) : [...current, opt.value];
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
next = this._value() === opt.value ? null : opt.value;
|
|
58
|
+
}
|
|
59
|
+
this._value.set(next);
|
|
60
|
+
this._onChange(next);
|
|
61
|
+
this._onTouched();
|
|
62
|
+
this.neuChange.emit(next);
|
|
63
|
+
}
|
|
64
|
+
onBlur() {
|
|
65
|
+
this._onTouched();
|
|
66
|
+
}
|
|
67
|
+
// ---- CVA ----
|
|
68
|
+
_onChange = () => { };
|
|
69
|
+
_onTouched = () => { };
|
|
70
|
+
writeValue(val) {
|
|
71
|
+
if (val === null || val === undefined) {
|
|
72
|
+
this._value.set(this.multiple() ? [] : null);
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
this._value.set(val);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
registerOnChange(fn) {
|
|
79
|
+
this._onChange = fn;
|
|
80
|
+
}
|
|
81
|
+
registerOnTouched(fn) {
|
|
82
|
+
this._onTouched = fn;
|
|
83
|
+
}
|
|
84
|
+
setDisabledState(isDisabled) {
|
|
85
|
+
this._isDisabled.set(isDisabled);
|
|
86
|
+
}
|
|
87
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: NeuToggleButtonGroupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
88
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: NeuToggleButtonGroupComponent, isStandalone: true, selector: "neu-toggle-button-group", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { neuChange: "neuChange" }, host: { classAttribute: "neu-toggle-group-host" }, providers: [
|
|
89
|
+
{
|
|
90
|
+
provide: NG_VALUE_ACCESSOR,
|
|
91
|
+
useExisting: forwardRef(() => NeuToggleButtonGroupComponent),
|
|
92
|
+
multi: true,
|
|
93
|
+
},
|
|
94
|
+
], ngImport: i0, template: `
|
|
95
|
+
<div
|
|
96
|
+
class="neu-toggle-group"
|
|
97
|
+
[class]="groupClasses()"
|
|
98
|
+
role="group"
|
|
99
|
+
[attr.aria-disabled]="_isDisabled() || null"
|
|
100
|
+
>
|
|
101
|
+
@for (opt of options(); track opt.value) {
|
|
102
|
+
<button
|
|
103
|
+
type="button"
|
|
104
|
+
class="neu-toggle-group__btn"
|
|
105
|
+
[class.neu-toggle-group__btn--active]="isSelected(opt.value)"
|
|
106
|
+
[class.neu-toggle-group__btn--disabled]="opt.disabled || _isDisabled()"
|
|
107
|
+
[attr.aria-pressed]="isSelected(opt.value)"
|
|
108
|
+
[disabled]="opt.disabled || _isDisabled() ? '' : null"
|
|
109
|
+
(click)="toggle(opt)"
|
|
110
|
+
(blur)="onBlur()"
|
|
111
|
+
>
|
|
112
|
+
@if (opt.icon) {
|
|
113
|
+
<neu-icon [name]="opt.icon" size="16px" strokeWidth="2" />
|
|
114
|
+
}
|
|
115
|
+
{{ opt.label }}
|
|
116
|
+
</button>
|
|
117
|
+
}
|
|
118
|
+
</div>
|
|
119
|
+
`, isInline: true, styles: [".neu-toggle-group-host{display:inline-block}.neu-toggle-group{display:inline-flex;border:1px solid var(--neu-border);border-radius:var(--neu-radius);overflow:hidden;background:var(--neu-surface)}.neu-toggle-group__btn{display:inline-flex;align-items:center;justify-content:center;gap:var(--neu-space-2);border:none;border-right:1px solid var(--neu-border);background:transparent;color:var(--neu-text-muted);font-family:var(--neu-font-sans);font-size:var(--neu-text-sm);font-weight:500;line-height:1;white-space:nowrap;cursor:pointer;padding:.5rem var(--neu-space-4);transition:background-color .15s ease,color .15s ease,border-color .15s ease;outline:none;-webkit-user-select:none;user-select:none}.neu-toggle-group__btn:last-child{border-right:none}.neu-toggle-group__btn:hover:not(:disabled):not(.neu-toggle-group__btn--disabled){background:var(--neu-surface-2);color:var(--neu-text)}.neu-toggle-group__btn:focus-visible{position:relative;z-index:1;box-shadow:inset 0 0 0 2px var(--neu-primary)}.neu-toggle-group__btn--active{background:var(--neu-primary);color:var(--neu-primary-fg);border-right-color:var(--neu-primary-dark)}.neu-toggle-group__btn--active:hover:not(:disabled):not(.neu-toggle-group__btn--disabled){background:var(--neu-primary-dark);color:var(--neu-primary-fg)}.neu-toggle-group__btn--active+.neu-toggle-group__btn{border-left-color:var(--neu-primary-dark)}.neu-toggle-group__btn--disabled{opacity:.45;cursor:not-allowed}.neu-toggle-group--disabled{opacity:.6;pointer-events:none}.neu-toggle-group--sm .neu-toggle-group__btn{font-size:var(--neu-text-xs);padding:.375rem var(--neu-space-3)}.neu-toggle-group--md .neu-toggle-group__btn{font-size:var(--neu-text-sm);padding:.5rem var(--neu-space-4)}.neu-toggle-group--lg .neu-toggle-group__btn{font-size:var(--neu-text-base);padding:.625rem var(--neu-space-5)}\n"], dependencies: [{ kind: "component", type: NeuIconComponent, selector: "neu-icon", inputs: ["name", "strokeWidth", "size"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
120
|
+
}
|
|
121
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: NeuToggleButtonGroupComponent, decorators: [{
|
|
122
|
+
type: Component,
|
|
123
|
+
args: [{ selector: 'neu-toggle-button-group', imports: [NeuIconComponent], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, providers: [
|
|
124
|
+
{
|
|
125
|
+
provide: NG_VALUE_ACCESSOR,
|
|
126
|
+
useExisting: forwardRef(() => NeuToggleButtonGroupComponent),
|
|
127
|
+
multi: true,
|
|
128
|
+
},
|
|
129
|
+
], host: { class: 'neu-toggle-group-host' }, template: `
|
|
130
|
+
<div
|
|
131
|
+
class="neu-toggle-group"
|
|
132
|
+
[class]="groupClasses()"
|
|
133
|
+
role="group"
|
|
134
|
+
[attr.aria-disabled]="_isDisabled() || null"
|
|
135
|
+
>
|
|
136
|
+
@for (opt of options(); track opt.value) {
|
|
137
|
+
<button
|
|
138
|
+
type="button"
|
|
139
|
+
class="neu-toggle-group__btn"
|
|
140
|
+
[class.neu-toggle-group__btn--active]="isSelected(opt.value)"
|
|
141
|
+
[class.neu-toggle-group__btn--disabled]="opt.disabled || _isDisabled()"
|
|
142
|
+
[attr.aria-pressed]="isSelected(opt.value)"
|
|
143
|
+
[disabled]="opt.disabled || _isDisabled() ? '' : null"
|
|
144
|
+
(click)="toggle(opt)"
|
|
145
|
+
(blur)="onBlur()"
|
|
146
|
+
>
|
|
147
|
+
@if (opt.icon) {
|
|
148
|
+
<neu-icon [name]="opt.icon" size="16px" strokeWidth="2" />
|
|
149
|
+
}
|
|
150
|
+
{{ opt.label }}
|
|
151
|
+
</button>
|
|
152
|
+
}
|
|
153
|
+
</div>
|
|
154
|
+
`, styles: [".neu-toggle-group-host{display:inline-block}.neu-toggle-group{display:inline-flex;border:1px solid var(--neu-border);border-radius:var(--neu-radius);overflow:hidden;background:var(--neu-surface)}.neu-toggle-group__btn{display:inline-flex;align-items:center;justify-content:center;gap:var(--neu-space-2);border:none;border-right:1px solid var(--neu-border);background:transparent;color:var(--neu-text-muted);font-family:var(--neu-font-sans);font-size:var(--neu-text-sm);font-weight:500;line-height:1;white-space:nowrap;cursor:pointer;padding:.5rem var(--neu-space-4);transition:background-color .15s ease,color .15s ease,border-color .15s ease;outline:none;-webkit-user-select:none;user-select:none}.neu-toggle-group__btn:last-child{border-right:none}.neu-toggle-group__btn:hover:not(:disabled):not(.neu-toggle-group__btn--disabled){background:var(--neu-surface-2);color:var(--neu-text)}.neu-toggle-group__btn:focus-visible{position:relative;z-index:1;box-shadow:inset 0 0 0 2px var(--neu-primary)}.neu-toggle-group__btn--active{background:var(--neu-primary);color:var(--neu-primary-fg);border-right-color:var(--neu-primary-dark)}.neu-toggle-group__btn--active:hover:not(:disabled):not(.neu-toggle-group__btn--disabled){background:var(--neu-primary-dark);color:var(--neu-primary-fg)}.neu-toggle-group__btn--active+.neu-toggle-group__btn{border-left-color:var(--neu-primary-dark)}.neu-toggle-group__btn--disabled{opacity:.45;cursor:not-allowed}.neu-toggle-group--disabled{opacity:.6;pointer-events:none}.neu-toggle-group--sm .neu-toggle-group__btn{font-size:var(--neu-text-xs);padding:.375rem var(--neu-space-3)}.neu-toggle-group--md .neu-toggle-group__btn{font-size:var(--neu-text-sm);padding:.5rem var(--neu-space-4)}.neu-toggle-group--lg .neu-toggle-group__btn{font-size:var(--neu-text-base);padding:.625rem var(--neu-space-5)}\n"] }]
|
|
155
|
+
}], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], neuChange: [{ type: i0.Output, args: ["neuChange"] }] } });
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Generated bundle index. Do not edit.
|
|
159
|
+
*/
|
|
160
|
+
|
|
161
|
+
export { NeuToggleButtonGroupComponent };
|
|
162
|
+
//# sourceMappingURL=neural-ui-core-toggle-button-group.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"neural-ui-core-toggle-button-group.mjs","sources":["../../../../projects/ui-core/toggle-button-group/neu-toggle-button-group.component.ts","../../../../projects/ui-core/toggle-button-group/neural-ui-core-toggle-button-group.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n ViewEncapsulation,\n computed,\n forwardRef,\n input,\n output,\n signal,\n} from '@angular/core';\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\nimport { NeuButtonSize } from '@neural-ui/core/button';\nimport { NeuIconComponent } from '@neural-ui/core/icon';\n\nexport interface NeuToggleOption<T = unknown> {\n /** Texto visible del botón / Visible button text */\n label: string;\n /** Valor asociado a esta opción / Value associated with this option */\n value: T;\n /** Nombre de icono Lucide (opcional) / Lucide icon name (optional) */\n icon?: string;\n /** Deshabilita solo esta opción / Disables this option only */\n disabled?: boolean;\n}\n\n/**\n * NeuralUI ToggleButtonGroup Component\n *\n * Grupo de botones de selección (single o múltiple). / Selection button group (single or multiple).\n * Selector segmentado para selección única o múltiple. / Segmented selector for single or multiple selection.\n *\n * Uso (single):\n * <neu-toggle-button-group [options]=\"opts\" [formControl]=\"valueCtrl\" />\n *\n * Uso (múltiple):\n * <neu-toggle-button-group [options]=\"opts\" [multiple]=\"true\" [formControl]=\"valuesCtrl\" />\n */\n@Component({\n selector: 'neu-toggle-button-group',\n imports: [NeuIconComponent],\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => NeuToggleButtonGroupComponent),\n multi: true,\n },\n ],\n host: { class: 'neu-toggle-group-host' },\n template: `\n <div\n class=\"neu-toggle-group\"\n [class]=\"groupClasses()\"\n role=\"group\"\n [attr.aria-disabled]=\"_isDisabled() || null\"\n >\n @for (opt of options(); track opt.value) {\n <button\n type=\"button\"\n class=\"neu-toggle-group__btn\"\n [class.neu-toggle-group__btn--active]=\"isSelected(opt.value)\"\n [class.neu-toggle-group__btn--disabled]=\"opt.disabled || _isDisabled()\"\n [attr.aria-pressed]=\"isSelected(opt.value)\"\n [disabled]=\"opt.disabled || _isDisabled() ? '' : null\"\n (click)=\"toggle(opt)\"\n (blur)=\"onBlur()\"\n >\n @if (opt.icon) {\n <neu-icon [name]=\"opt.icon\" size=\"16px\" strokeWidth=\"2\" />\n }\n {{ opt.label }}\n </button>\n }\n </div>\n `,\n styleUrl: './neu-toggle-button-group.component.scss',\n})\nexport class NeuToggleButtonGroupComponent<T = unknown> implements ControlValueAccessor {\n /** Lista de opciones del grupo / Group option list */\n options = input<NeuToggleOption<T>[]>([]);\n\n /**\n * Permite seleccionar múltiples opciones.\n * - false (por defecto): valor es `T | null`\n * - true: valor es `T[]`\n */\n multiple = input<boolean>(false);\n\n /** Tamaño visual / Visual size */\n size = input<NeuButtonSize>('md');\n\n /** Deshabilita todo el grupo / Disables the entire group */\n disabled = input<boolean>(false);\n\n /** Emite el nuevo valor al cambiar (útil sin formControl) / Emits the new value on change (useful without formControl) */\n neuChange = output<T | T[] | null>();\n\n readonly _value = signal<T | T[] | null>(null);\n readonly _isDisabled = signal(false);\n\n readonly groupClasses = computed(() => ({\n 'neu-toggle-group': true,\n [`neu-toggle-group--${this.size()}`]: true,\n 'neu-toggle-group--disabled': this._isDisabled(),\n }));\n\n isSelected(value: T): boolean {\n const v = this._value();\n if (this.multiple()) {\n return Array.isArray(v) && (v as T[]).includes(value);\n }\n return v === value;\n }\n\n toggle(opt: NeuToggleOption<T>): void {\n if (opt.disabled || this._isDisabled()) return;\n\n let next: T | T[] | null;\n\n if (this.multiple()) {\n const current: T[] = Array.isArray(this._value()) ? (this._value() as T[]) : [];\n const idx = current.indexOf(opt.value);\n next = idx >= 0 ? current.filter((_, i) => i !== idx) : [...current, opt.value];\n } else {\n next = this._value() === opt.value ? null : opt.value;\n }\n\n this._value.set(next);\n this._onChange(next);\n this._onTouched();\n this.neuChange.emit(next);\n }\n\n onBlur(): void {\n this._onTouched();\n }\n\n // ---- CVA ----\n\n private _onChange: (v: unknown) => void = () => {};\n private _onTouched: () => void = () => {};\n\n writeValue(val: T | T[] | null): void {\n if (val === null || val === undefined) {\n this._value.set(this.multiple() ? ([] as T[]) : null);\n } else {\n this._value.set(val);\n }\n }\n\n registerOnChange(fn: (v: unknown) => 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._isDisabled.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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"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { input, computed, ChangeDetectionStrategy, ViewEncapsulation, Component } from '@angular/core';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* NeuralUI Toolbar Component
|
|
6
|
+
*
|
|
7
|
+
* Barra horizontal con tres zonas de contenido: start (izquierda), center (centro) y end (derecha).
|
|
8
|
+
*
|
|
9
|
+
* Uso:
|
|
10
|
+
* <neu-toolbar>
|
|
11
|
+
* <span neu-toolbar-start>Logo</span>
|
|
12
|
+
* <span neu-toolbar-center>Título</span>
|
|
13
|
+
* <span neu-toolbar-end><neu-button>Acción</neu-button></span>
|
|
14
|
+
* </neu-toolbar>
|
|
15
|
+
*/
|
|
16
|
+
class NeuToolbarComponent {
|
|
17
|
+
/** Tamaño de la toolbar / Toolbar size */
|
|
18
|
+
size = input('md', ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
19
|
+
/** Añade sombra en la parte inferior / Adds shadow at the bottom */
|
|
20
|
+
shadow = input(false, ...(ngDevMode ? [{ debugName: "shadow" }] : /* istanbul ignore next */ []));
|
|
21
|
+
/** Añade separador inferior / Adds bottom separator */
|
|
22
|
+
bordered = input(false, ...(ngDevMode ? [{ debugName: "bordered" }] : /* istanbul ignore next */ []));
|
|
23
|
+
/** Color de fondo personalizado vía CSS (pasa a la variable local) / Custom background color via CSS */
|
|
24
|
+
surface = input('surface', ...(ngDevMode ? [{ debugName: "surface" }] : /* istanbul ignore next */ []));
|
|
25
|
+
hostClasses = computed(() => ({
|
|
26
|
+
'neu-toolbar': true,
|
|
27
|
+
[`neu-toolbar--${this.size()}`]: true,
|
|
28
|
+
[`neu-toolbar--${this.surface()}`]: true,
|
|
29
|
+
'neu-toolbar--shadow': this.shadow(),
|
|
30
|
+
'neu-toolbar--bordered': this.bordered(),
|
|
31
|
+
}), ...(ngDevMode ? [{ debugName: "hostClasses" }] : /* istanbul ignore next */ []));
|
|
32
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: NeuToolbarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
33
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.8", type: NeuToolbarComponent, isStandalone: true, selector: "neu-toolbar", inputs: { size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, shadow: { classPropertyName: "shadow", publicName: "shadow", isSignal: true, isRequired: false, transformFunction: null }, bordered: { classPropertyName: "bordered", publicName: "bordered", isSignal: true, isRequired: false, transformFunction: null }, surface: { classPropertyName: "surface", publicName: "surface", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "hostClasses()" } }, ngImport: i0, template: `
|
|
34
|
+
<div class="neu-toolbar__section neu-toolbar__section--start">
|
|
35
|
+
<ng-content select="[neu-toolbar-start]" />
|
|
36
|
+
</div>
|
|
37
|
+
<div class="neu-toolbar__section neu-toolbar__section--center">
|
|
38
|
+
<ng-content select="[neu-toolbar-center]" />
|
|
39
|
+
</div>
|
|
40
|
+
<div class="neu-toolbar__section neu-toolbar__section--end">
|
|
41
|
+
<ng-content select="[neu-toolbar-end]" />
|
|
42
|
+
</div>
|
|
43
|
+
<ng-content />
|
|
44
|
+
`, isInline: true, styles: ["@charset \"UTF-8\";.neu-toolbar{display:flex;align-items:center;width:100%;gap:8px;background:var(--neu-toolbar-bg, var(--neu-surface-1, #ffffff));color:var(--neu-toolbar-color, var(--neu-text-primary, #111))}.neu-toolbar--sm{padding:6px 12px;min-height:40px}.neu-toolbar--md{padding:8px 16px;min-height:52px}.neu-toolbar--lg{padding:12px 20px;min-height:64px}.neu-toolbar--primary{background:var(--neu-toolbar-primary-bg, var(--neu-color-primary, #0ea5e9));color:var(--neu-toolbar-primary-color, #fff)}.neu-toolbar--none{background:transparent}.neu-toolbar--shadow{box-shadow:0 1px 4px #0000001a}.neu-toolbar--bordered{border-bottom:1px solid var(--neu-border-color, #e5e7eb)}.neu-toolbar__section{display:flex;align-items:center;gap:8px}.neu-toolbar__section--start{flex:0 0 auto;margin-right:auto}.neu-toolbar__section--center{flex:0 1 auto;justify-content:center}.neu-toolbar__section--end{flex:0 0 auto;margin-left:auto}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
45
|
+
}
|
|
46
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: NeuToolbarComponent, decorators: [{
|
|
47
|
+
type: Component,
|
|
48
|
+
args: [{ selector: 'neu-toolbar', imports: [], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, host: { '[class]': 'hostClasses()' }, template: `
|
|
49
|
+
<div class="neu-toolbar__section neu-toolbar__section--start">
|
|
50
|
+
<ng-content select="[neu-toolbar-start]" />
|
|
51
|
+
</div>
|
|
52
|
+
<div class="neu-toolbar__section neu-toolbar__section--center">
|
|
53
|
+
<ng-content select="[neu-toolbar-center]" />
|
|
54
|
+
</div>
|
|
55
|
+
<div class="neu-toolbar__section neu-toolbar__section--end">
|
|
56
|
+
<ng-content select="[neu-toolbar-end]" />
|
|
57
|
+
</div>
|
|
58
|
+
<ng-content />
|
|
59
|
+
`, styles: ["@charset \"UTF-8\";.neu-toolbar{display:flex;align-items:center;width:100%;gap:8px;background:var(--neu-toolbar-bg, var(--neu-surface-1, #ffffff));color:var(--neu-toolbar-color, var(--neu-text-primary, #111))}.neu-toolbar--sm{padding:6px 12px;min-height:40px}.neu-toolbar--md{padding:8px 16px;min-height:52px}.neu-toolbar--lg{padding:12px 20px;min-height:64px}.neu-toolbar--primary{background:var(--neu-toolbar-primary-bg, var(--neu-color-primary, #0ea5e9));color:var(--neu-toolbar-primary-color, #fff)}.neu-toolbar--none{background:transparent}.neu-toolbar--shadow{box-shadow:0 1px 4px #0000001a}.neu-toolbar--bordered{border-bottom:1px solid var(--neu-border-color, #e5e7eb)}.neu-toolbar__section{display:flex;align-items:center;gap:8px}.neu-toolbar__section--start{flex:0 0 auto;margin-right:auto}.neu-toolbar__section--center{flex:0 1 auto;justify-content:center}.neu-toolbar__section--end{flex:0 0 auto;margin-left:auto}\n"] }]
|
|
60
|
+
}], propDecorators: { size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], shadow: [{ type: i0.Input, args: [{ isSignal: true, alias: "shadow", required: false }] }], bordered: [{ type: i0.Input, args: [{ isSignal: true, alias: "bordered", required: false }] }], surface: [{ type: i0.Input, args: [{ isSignal: true, alias: "surface", required: false }] }] } });
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Generated bundle index. Do not edit.
|
|
64
|
+
*/
|
|
65
|
+
|
|
66
|
+
export { NeuToolbarComponent };
|
|
67
|
+
//# sourceMappingURL=neural-ui-core-toolbar.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"neural-ui-core-toolbar.mjs","sources":["../../../../projects/ui-core/toolbar/neu-toolbar.component.ts","../../../../projects/ui-core/toolbar/neural-ui-core-toolbar.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n ViewEncapsulation,\n computed,\n input,\n} from '@angular/core';\n\nexport type NeuToolbarSize = 'sm' | 'md' | 'lg';\n\n/**\n * NeuralUI Toolbar Component\n *\n * Barra horizontal con tres zonas de contenido: start (izquierda), center (centro) y end (derecha).\n *\n * Uso:\n * <neu-toolbar>\n * <span neu-toolbar-start>Logo</span>\n * <span neu-toolbar-center>Título</span>\n * <span neu-toolbar-end><neu-button>Acción</neu-button></span>\n * </neu-toolbar>\n */\n@Component({\n selector: 'neu-toolbar',\n imports: [],\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { '[class]': 'hostClasses()' },\n template: `\n <div class=\"neu-toolbar__section neu-toolbar__section--start\">\n <ng-content select=\"[neu-toolbar-start]\" />\n </div>\n <div class=\"neu-toolbar__section neu-toolbar__section--center\">\n <ng-content select=\"[neu-toolbar-center]\" />\n </div>\n <div class=\"neu-toolbar__section neu-toolbar__section--end\">\n <ng-content select=\"[neu-toolbar-end]\" />\n </div>\n <ng-content />\n `,\n styleUrl: './neu-toolbar.component.scss',\n})\nexport class NeuToolbarComponent {\n /** Tamaño de la toolbar / Toolbar size */\n readonly size = input<NeuToolbarSize>('md');\n\n /** Añade sombra en la parte inferior / Adds shadow at the bottom */\n readonly shadow = input<boolean>(false);\n\n /** Añade separador inferior / Adds bottom separator */\n readonly bordered = input<boolean>(false);\n\n /** Color de fondo personalizado vía CSS (pasa a la variable local) / Custom background color via CSS */\n readonly surface = input<'primary' | 'surface' | 'none'>('surface');\n\n readonly hostClasses = computed(() => ({\n 'neu-toolbar': true,\n [`neu-toolbar--${this.size()}`]: true,\n [`neu-toolbar--${this.surface()}`]: true,\n 'neu-toolbar--shadow': this.shadow(),\n 'neu-toolbar--bordered': this.bordered(),\n }));\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public_api';\n"],"names":[],"mappings":";;;AAUA;;;;;;;;;;;AAWG;MAqBU,mBAAmB,CAAA;;AAErB,IAAA,IAAI,GAAG,KAAK,CAAiB,IAAI,2EAAC;;AAGlC,IAAA,MAAM,GAAG,KAAK,CAAU,KAAK,6EAAC;;AAG9B,IAAA,QAAQ,GAAG,KAAK,CAAU,KAAK,+EAAC;;AAGhC,IAAA,OAAO,GAAG,KAAK,CAAiC,SAAS,8EAAC;AAE1D,IAAA,WAAW,GAAG,QAAQ,CAAC,OAAO;AACrC,QAAA,aAAa,EAAE,IAAI;QACnB,CAAC,CAAA,aAAA,EAAgB,IAAI,CAAC,IAAI,EAAE,CAAA,CAAE,GAAG,IAAI;QACrC,CAAC,CAAA,aAAA,EAAgB,IAAI,CAAC,OAAO,EAAE,CAAA,CAAE,GAAG,IAAI;AACxC,QAAA,qBAAqB,EAAE,IAAI,CAAC,MAAM,EAAE;AACpC,QAAA,uBAAuB,EAAE,IAAI,CAAC,QAAQ,EAAE;AACzC,KAAA,CAAC,kFAAC;uGAnBQ,mBAAmB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAnB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,mBAAmB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,aAAA,EAAA,MAAA,EAAA,EAAA,IAAA,EAAA,EAAA,iBAAA,EAAA,MAAA,EAAA,UAAA,EAAA,MAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,MAAA,EAAA,EAAA,iBAAA,EAAA,QAAA,EAAA,UAAA,EAAA,QAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,QAAA,EAAA,EAAA,iBAAA,EAAA,UAAA,EAAA,UAAA,EAAA,UAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,OAAA,EAAA,EAAA,iBAAA,EAAA,SAAA,EAAA,UAAA,EAAA,SAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,IAAA,EAAA,EAAA,UAAA,EAAA,EAAA,OAAA,EAAA,eAAA,EAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EAdpB;;;;;;;;;;;AAWT,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,MAAA,EAAA,CAAA,i6BAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,aAAA,EAAA,EAAA,CAAA,iBAAA,CAAA,IAAA,EAAA,CAAA;;2FAGU,mBAAmB,EAAA,UAAA,EAAA,CAAA;kBApB/B,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,aAAa,WACd,EAAE,EAAA,aAAA,EACI,iBAAiB,CAAC,IAAI,EAAA,eAAA,EACpB,uBAAuB,CAAC,MAAM,QACzC,EAAE,SAAS,EAAE,eAAe,EAAE,EAAA,QAAA,EAC1B;;;;;;;;;;;AAWT,EAAA,CAAA,EAAA,MAAA,EAAA,CAAA,i6BAAA,CAAA,EAAA;;;ACvCH;;AAEG;;;;"}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { input, ChangeDetectionStrategy, ViewEncapsulation, Component, inject, ElementRef, Injector, HostListener, Directive } from '@angular/core';
|
|
3
|
+
import { Overlay } from '@angular/cdk/overlay';
|
|
4
|
+
import { ComponentPortal } from '@angular/cdk/portal';
|
|
5
|
+
|
|
6
|
+
/** @internal — componente flotante del tooltip, renderizado vía CDK Portal / floating tooltip component, rendered via CDK Portal */
|
|
7
|
+
class NeuTooltipOverlayComponent {
|
|
8
|
+
text = input.required(...(ngDevMode ? [{ debugName: "text" }] : /* istanbul ignore next */ []));
|
|
9
|
+
tooltipId = input('', ...(ngDevMode ? [{ debugName: "tooltipId" }] : /* istanbul ignore next */ []));
|
|
10
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: NeuTooltipOverlayComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
11
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.8", type: NeuTooltipOverlayComponent, isStandalone: true, selector: "neu-tooltip-overlay", inputs: { text: { classPropertyName: "text", publicName: "text", isSignal: true, isRequired: true, transformFunction: null }, tooltipId: { classPropertyName: "tooltipId", publicName: "tooltipId", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "tooltip" }, properties: { "id": "tooltipId()" }, classAttribute: "neu-tooltip" }, ngImport: i0, template: `<span class="neu-tooltip__text">{{ text() }}</span>`, isInline: true, styles: [".neu-tooltip-panel{pointer-events:none}.neu-tooltip{display:block;animation:neu-tooltip-in .15s ease forwards}.neu-tooltip__text{display:inline-block;padding:var(--neu-space-2) var(--neu-space-3);background:var(--neu-tooltip-bg, #0f172a);color:var(--neu-tooltip-fg, #ffffff);font-family:var(--neu-font-sans);font-size:var(--neu-text-xs);font-weight:500;line-height:1.4;border-radius:var(--neu-tooltip-radius, 6px);box-shadow:var(--neu-tooltip-shadow, 0 4px 12px rgba(15, 23, 42, .24));white-space:nowrap;max-width:280px;overflow:hidden;text-overflow:ellipsis}@keyframes neu-tooltip-in{0%{opacity:0;transform:translateY(4px) scale(.96)}to{opacity:1;transform:translateY(0) scale(1)}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
12
|
+
}
|
|
13
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: NeuTooltipOverlayComponent, decorators: [{
|
|
14
|
+
type: Component,
|
|
15
|
+
args: [{ selector: 'neu-tooltip-overlay', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
16
|
+
class: 'neu-tooltip',
|
|
17
|
+
role: 'tooltip',
|
|
18
|
+
'[id]': 'tooltipId()',
|
|
19
|
+
}, template: `<span class="neu-tooltip__text">{{ text() }}</span>`, styles: [".neu-tooltip-panel{pointer-events:none}.neu-tooltip{display:block;animation:neu-tooltip-in .15s ease forwards}.neu-tooltip__text{display:inline-block;padding:var(--neu-space-2) var(--neu-space-3);background:var(--neu-tooltip-bg, #0f172a);color:var(--neu-tooltip-fg, #ffffff);font-family:var(--neu-font-sans);font-size:var(--neu-text-xs);font-weight:500;line-height:1.4;border-radius:var(--neu-tooltip-radius, 6px);box-shadow:var(--neu-tooltip-shadow, 0 4px 12px rgba(15, 23, 42, .24));white-space:nowrap;max-width:280px;overflow:hidden;text-overflow:ellipsis}@keyframes neu-tooltip-in{0%{opacity:0;transform:translateY(4px) scale(.96)}to{opacity:1;transform:translateY(0) scale(1)}}\n"] }]
|
|
20
|
+
}], propDecorators: { text: [{ type: i0.Input, args: [{ isSignal: true, alias: "text", required: true }] }], tooltipId: [{ type: i0.Input, args: [{ isSignal: true, alias: "tooltipId", required: false }] }] } });
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* NeuralUI Tooltip Directive
|
|
24
|
+
*
|
|
25
|
+
* Muestra un globo informativo al hacer hover/focus sobre el elemento host.
|
|
26
|
+
* Usa CDK Overlay para posicionamiento robusto.
|
|
27
|
+
*
|
|
28
|
+
* Uso:
|
|
29
|
+
* <button [neuTooltip]="'Guardar cambios'">Guardar</button>
|
|
30
|
+
* <button [neuTooltip]="'Eliminar'" neuTooltipPosition="bottom">Eliminar</button>
|
|
31
|
+
*/
|
|
32
|
+
class NeuTooltipDirective {
|
|
33
|
+
neuTooltip = input.required(...(ngDevMode ? [{ debugName: "neuTooltip" }] : /* istanbul ignore next */ []));
|
|
34
|
+
neuTooltipPosition = input('top', ...(ngDevMode ? [{ debugName: "neuTooltipPosition" }] : /* istanbul ignore next */ []));
|
|
35
|
+
neuTooltipDisabled = input(false, ...(ngDevMode ? [{ debugName: "neuTooltipDisabled" }] : /* istanbul ignore next */ []));
|
|
36
|
+
_overlay = inject(Overlay);
|
|
37
|
+
_elementRef = inject((ElementRef));
|
|
38
|
+
_injector = inject(Injector);
|
|
39
|
+
_tooltipId = `neu-tooltip-${Math.random().toString(36).slice(2, 7)}`;
|
|
40
|
+
/** Elementos HTML nativamente focusables que no necesitan tabindex extra / Natively focusable HTML elements that don't need extra tabindex */
|
|
41
|
+
_NATIVE_FOCUSABLE = /^(a|button|input|select|textarea|details|summary)$/i;
|
|
42
|
+
_needsTabindex = () => !this._NATIVE_FOCUSABLE.test(this._elementRef.nativeElement.tagName);
|
|
43
|
+
_overlayRef = null;
|
|
44
|
+
_tooltipRef = null;
|
|
45
|
+
_hideTimeout = null;
|
|
46
|
+
show() {
|
|
47
|
+
if (this.neuTooltipDisabled() || !this.neuTooltip())
|
|
48
|
+
return;
|
|
49
|
+
if (this._hideTimeout) {
|
|
50
|
+
clearTimeout(this._hideTimeout);
|
|
51
|
+
this._hideTimeout = null;
|
|
52
|
+
}
|
|
53
|
+
if (this._overlayRef?.hasAttached())
|
|
54
|
+
return;
|
|
55
|
+
this._createOverlay();
|
|
56
|
+
const portal = new ComponentPortal(NeuTooltipOverlayComponent, null, this._injector);
|
|
57
|
+
this._tooltipRef = this._overlayRef.attach(portal);
|
|
58
|
+
this._tooltipRef.setInput('text', this.neuTooltip());
|
|
59
|
+
this._tooltipRef.setInput('tooltipId', this._tooltipId);
|
|
60
|
+
}
|
|
61
|
+
hide() {
|
|
62
|
+
this._hideTimeout = setTimeout(() => this._detach(), 100);
|
|
63
|
+
}
|
|
64
|
+
_createOverlay() {
|
|
65
|
+
if (this._overlayRef)
|
|
66
|
+
return;
|
|
67
|
+
const positionStrategy = this._overlay
|
|
68
|
+
.position()
|
|
69
|
+
.flexibleConnectedTo(this._elementRef)
|
|
70
|
+
.withPositions(this._getPositions());
|
|
71
|
+
this._overlayRef = this._overlay.create({
|
|
72
|
+
positionStrategy,
|
|
73
|
+
scrollStrategy: this._overlay.scrollStrategies.reposition(),
|
|
74
|
+
panelClass: 'neu-tooltip-panel',
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
_getPositions() {
|
|
78
|
+
const map = {
|
|
79
|
+
top: {
|
|
80
|
+
originX: 'center',
|
|
81
|
+
originY: 'top',
|
|
82
|
+
overlayX: 'center',
|
|
83
|
+
overlayY: 'bottom',
|
|
84
|
+
offsetY: -8,
|
|
85
|
+
},
|
|
86
|
+
bottom: {
|
|
87
|
+
originX: 'center',
|
|
88
|
+
originY: 'bottom',
|
|
89
|
+
overlayX: 'center',
|
|
90
|
+
overlayY: 'top',
|
|
91
|
+
offsetY: 8,
|
|
92
|
+
},
|
|
93
|
+
left: {
|
|
94
|
+
originX: 'start',
|
|
95
|
+
originY: 'center',
|
|
96
|
+
overlayX: 'end',
|
|
97
|
+
overlayY: 'center',
|
|
98
|
+
offsetX: -8,
|
|
99
|
+
},
|
|
100
|
+
right: {
|
|
101
|
+
originX: 'end',
|
|
102
|
+
originY: 'center',
|
|
103
|
+
overlayX: 'start',
|
|
104
|
+
overlayY: 'center',
|
|
105
|
+
offsetX: 8,
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
return [map[this.neuTooltipPosition()], map['top'], map['bottom']];
|
|
109
|
+
}
|
|
110
|
+
_detach() {
|
|
111
|
+
this._overlayRef?.detach();
|
|
112
|
+
this._tooltipRef = null;
|
|
113
|
+
}
|
|
114
|
+
ngOnDestroy() {
|
|
115
|
+
if (this._hideTimeout)
|
|
116
|
+
clearTimeout(this._hideTimeout);
|
|
117
|
+
this._overlayRef?.dispose();
|
|
118
|
+
}
|
|
119
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: NeuTooltipDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
120
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.8", type: NeuTooltipDirective, isStandalone: true, selector: "[neuTooltip]", inputs: { neuTooltip: { classPropertyName: "neuTooltip", publicName: "neuTooltip", isSignal: true, isRequired: true, transformFunction: null }, neuTooltipPosition: { classPropertyName: "neuTooltipPosition", publicName: "neuTooltipPosition", isSignal: true, isRequired: false, transformFunction: null }, neuTooltipDisabled: { classPropertyName: "neuTooltipDisabled", publicName: "neuTooltipDisabled", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "mouseenter": "show()", "focus": "show()", "mouseleave": "hide()", "blur": "hide()" }, properties: { "attr.aria-describedby": "_tooltipId", "attr.tabindex": "_needsTabindex() ? \"0\" : null" } }, ngImport: i0 });
|
|
121
|
+
}
|
|
122
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: NeuTooltipDirective, decorators: [{
|
|
123
|
+
type: Directive,
|
|
124
|
+
args: [{
|
|
125
|
+
selector: '[neuTooltip]',
|
|
126
|
+
host: {
|
|
127
|
+
'[attr.aria-describedby]': '_tooltipId',
|
|
128
|
+
// Sólo añadimos tabindex en elementos que no son nativamente focusables
|
|
129
|
+
'[attr.tabindex]': '_needsTabindex() ? "0" : null',
|
|
130
|
+
},
|
|
131
|
+
}]
|
|
132
|
+
}], propDecorators: { neuTooltip: [{ type: i0.Input, args: [{ isSignal: true, alias: "neuTooltip", required: true }] }], neuTooltipPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "neuTooltipPosition", required: false }] }], neuTooltipDisabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "neuTooltipDisabled", required: false }] }], show: [{
|
|
133
|
+
type: HostListener,
|
|
134
|
+
args: ['mouseenter']
|
|
135
|
+
}, {
|
|
136
|
+
type: HostListener,
|
|
137
|
+
args: ['focus']
|
|
138
|
+
}], hide: [{
|
|
139
|
+
type: HostListener,
|
|
140
|
+
args: ['mouseleave']
|
|
141
|
+
}, {
|
|
142
|
+
type: HostListener,
|
|
143
|
+
args: ['blur']
|
|
144
|
+
}] } });
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Generated bundle index. Do not edit.
|
|
148
|
+
*/
|
|
149
|
+
|
|
150
|
+
export { NeuTooltipDirective, NeuTooltipOverlayComponent };
|
|
151
|
+
//# sourceMappingURL=neural-ui-core-tooltip.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"neural-ui-core-tooltip.mjs","sources":["../../../../projects/ui-core/tooltip/neu-tooltip.component.ts","../../../../projects/ui-core/tooltip/neu-tooltip.directive.ts","../../../../projects/ui-core/tooltip/neural-ui-core-tooltip.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, ViewEncapsulation, input } from '@angular/core';\n\n/** @internal — componente flotante del tooltip, renderizado vía CDK Portal / floating tooltip component, rendered via CDK Portal */\n@Component({\n selector: 'neu-tooltip-overlay',\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n class: 'neu-tooltip',\n role: 'tooltip',\n '[id]': 'tooltipId()',\n },\n template: `<span class=\"neu-tooltip__text\">{{ text() }}</span>`,\n styleUrl: './neu-tooltip.component.scss',\n})\nexport class NeuTooltipOverlayComponent {\n readonly text = input.required<string>();\n readonly tooltipId = input<string>('');\n}\n","import {\n ComponentRef,\n Directive,\n ElementRef,\n HostListener,\n Injector,\n OnDestroy,\n inject,\n input,\n} from '@angular/core';\nimport { Overlay, OverlayRef } from '@angular/cdk/overlay';\nimport { ComponentPortal } from '@angular/cdk/portal';\nimport { ConnectedPosition } from '@angular/cdk/overlay';\nimport { NeuTooltipOverlayComponent } from './neu-tooltip.component';\n\nexport type NeuTooltipPosition = 'top' | 'bottom' | 'left' | 'right';\n\n/**\n * NeuralUI Tooltip Directive\n *\n * Muestra un globo informativo al hacer hover/focus sobre el elemento host.\n * Usa CDK Overlay para posicionamiento robusto.\n *\n * Uso:\n * <button [neuTooltip]=\"'Guardar cambios'\">Guardar</button>\n * <button [neuTooltip]=\"'Eliminar'\" neuTooltipPosition=\"bottom\">Eliminar</button>\n */\n@Directive({\n selector: '[neuTooltip]',\n host: {\n '[attr.aria-describedby]': '_tooltipId',\n // Sólo añadimos tabindex en elementos que no son nativamente focusables\n '[attr.tabindex]': '_needsTabindex() ? \"0\" : null',\n },\n})\nexport class NeuTooltipDirective implements OnDestroy {\n readonly neuTooltip = input.required<string>();\n readonly neuTooltipPosition = input<NeuTooltipPosition>('top');\n readonly neuTooltipDisabled = input<boolean>(false);\n\n private readonly _overlay = inject(Overlay);\n private readonly _elementRef = inject(ElementRef<HTMLElement>);\n private readonly _injector = inject(Injector);\n\n readonly _tooltipId = `neu-tooltip-${Math.random().toString(36).slice(2, 7)}`;\n\n /** Elementos HTML nativamente focusables que no necesitan tabindex extra / Natively focusable HTML elements that don't need extra tabindex */\n private readonly _NATIVE_FOCUSABLE = /^(a|button|input|select|textarea|details|summary)$/i;\n protected readonly _needsTabindex = () =>\n !this._NATIVE_FOCUSABLE.test(this._elementRef.nativeElement.tagName);\n\n private _overlayRef: OverlayRef | null = null;\n private _tooltipRef: ComponentRef<NeuTooltipOverlayComponent> | null = null;\n private _hideTimeout: ReturnType<typeof setTimeout> | null = null;\n\n @HostListener('mouseenter')\n @HostListener('focus')\n show(): void {\n if (this.neuTooltipDisabled() || !this.neuTooltip()) return;\n if (this._hideTimeout) {\n clearTimeout(this._hideTimeout);\n this._hideTimeout = null;\n }\n if (this._overlayRef?.hasAttached()) return;\n\n this._createOverlay();\n const portal = new ComponentPortal(NeuTooltipOverlayComponent, null, this._injector);\n this._tooltipRef = this._overlayRef!.attach(portal);\n this._tooltipRef!.setInput('text', this.neuTooltip());\n this._tooltipRef!.setInput('tooltipId', this._tooltipId);\n }\n\n @HostListener('mouseleave')\n @HostListener('blur')\n hide(): void {\n this._hideTimeout = setTimeout(() => this._detach(), 100);\n }\n\n private _createOverlay(): void {\n if (this._overlayRef) return;\n\n const positionStrategy = this._overlay\n .position()\n .flexibleConnectedTo(this._elementRef)\n .withPositions(this._getPositions());\n\n this._overlayRef = this._overlay.create({\n positionStrategy,\n scrollStrategy: this._overlay.scrollStrategies.reposition(),\n panelClass: 'neu-tooltip-panel',\n });\n }\n\n private _getPositions(): ConnectedPosition[] {\n const map: Record<NeuTooltipPosition, ConnectedPosition> = {\n top: {\n originX: 'center',\n originY: 'top',\n overlayX: 'center',\n overlayY: 'bottom',\n offsetY: -8,\n },\n bottom: {\n originX: 'center',\n originY: 'bottom',\n overlayX: 'center',\n overlayY: 'top',\n offsetY: 8,\n },\n left: {\n originX: 'start',\n originY: 'center',\n overlayX: 'end',\n overlayY: 'center',\n offsetX: -8,\n },\n right: {\n originX: 'end',\n originY: 'center',\n overlayX: 'start',\n overlayY: 'center',\n offsetX: 8,\n },\n };\n return [map[this.neuTooltipPosition()], map['top'], map['bottom']];\n }\n\n private _detach(): void {\n this._overlayRef?.detach();\n this._tooltipRef = null;\n }\n\n ngOnDestroy(): void {\n if (this._hideTimeout) clearTimeout(this._hideTimeout);\n this._overlayRef?.dispose();\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,96 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { computed, Injectable } from '@angular/core';
|
|
3
|
+
import { toSignal } from '@angular/core/rxjs-interop';
|
|
4
|
+
import * as i1 from '@angular/router';
|
|
5
|
+
import { NavigationEnd } from '@angular/router';
|
|
6
|
+
import { filter, startWith, map } from 'rxjs/operators';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* NeuUrlStateService — El Sistema Nervioso de NeuralUI
|
|
10
|
+
*
|
|
11
|
+
* Sincroniza el estado de la UI con los QueryParams de la URL.
|
|
12
|
+
* Completamente reactivo via Angular Signals.
|
|
13
|
+
*
|
|
14
|
+
* Uso:
|
|
15
|
+
* const urlState = inject(NeuUrlStateService);
|
|
16
|
+
* const page = urlState.getParam('page'); // Signal<string | null>
|
|
17
|
+
* urlState.setParam('menu', 'open'); // Actualiza ?menu=open
|
|
18
|
+
* urlState.patchParams({ page: '2', q: 'filter' }); // Actualiza múltiples
|
|
19
|
+
*/
|
|
20
|
+
class NeuUrlStateService {
|
|
21
|
+
injector;
|
|
22
|
+
router;
|
|
23
|
+
/**
|
|
24
|
+
* Signal con el mapa completo de queryParams actual.
|
|
25
|
+
* Se actualiza automáticamente en cada NavigationEnd.
|
|
26
|
+
*/
|
|
27
|
+
params;
|
|
28
|
+
constructor(injector, router) {
|
|
29
|
+
this.injector = injector;
|
|
30
|
+
this.router = router;
|
|
31
|
+
this.params = toSignal(this.router.events.pipe(filter((e) => e instanceof NavigationEnd), startWith(null), map(() => this.router.parseUrl(this.router.url).queryParams)), {
|
|
32
|
+
initialValue: this.router.parseUrl(this.router.url).queryParams,
|
|
33
|
+
injector: this.injector,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Devuelve un Signal reactivo con el valor del parámetro indicado.
|
|
38
|
+
* Memorizar el resultado: no llamar en bucle pues crea un computed nuevo c/vez.
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* readonly menuOpen = computed(() => this.urlState.getParam('menu')() === 'open');
|
|
42
|
+
*/
|
|
43
|
+
getParam(key) {
|
|
44
|
+
return computed(() => this.params()[key] ?? null);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Establece un único queryParam en la URL.
|
|
48
|
+
*
|
|
49
|
+
* @param key Nombre del parámetro
|
|
50
|
+
* @param value Valor. Pasar `null` para eliminarlo de la URL.
|
|
51
|
+
* @param replaceUrl Si true (default) usa replaceState — no ensucia el historial.
|
|
52
|
+
* Pasar false para acciones que el usuario debe poder deshacer con Atrás.
|
|
53
|
+
*/
|
|
54
|
+
setParam(key, value, replaceUrl = true) {
|
|
55
|
+
void this.router.navigate([], {
|
|
56
|
+
queryParams: { [key]: value },
|
|
57
|
+
queryParamsHandling: 'merge',
|
|
58
|
+
replaceUrl,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Actualiza múltiples queryParams en una sola navegación.
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* urlState.patchParams({ page: '1', q: 'Angular' });
|
|
66
|
+
*/
|
|
67
|
+
patchParams(params, replaceUrl = true) {
|
|
68
|
+
void this.router.navigate([], {
|
|
69
|
+
queryParams: params,
|
|
70
|
+
queryParamsHandling: 'merge',
|
|
71
|
+
replaceUrl,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Elimina todos los queryParams de la URL de una vez.
|
|
76
|
+
*/
|
|
77
|
+
clearParams(replaceUrl = true) {
|
|
78
|
+
void this.router.navigate([], {
|
|
79
|
+
queryParams: {},
|
|
80
|
+
replaceUrl,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: NeuUrlStateService, deps: [{ token: i0.Injector }, { token: i1.Router }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
84
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: NeuUrlStateService, providedIn: 'root' });
|
|
85
|
+
}
|
|
86
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: NeuUrlStateService, decorators: [{
|
|
87
|
+
type: Injectable,
|
|
88
|
+
args: [{ providedIn: 'root' }]
|
|
89
|
+
}], ctorParameters: () => [{ type: i0.Injector }, { type: i1.Router }] });
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Generated bundle index. Do not edit.
|
|
93
|
+
*/
|
|
94
|
+
|
|
95
|
+
export { NeuUrlStateService };
|
|
96
|
+
//# sourceMappingURL=neural-ui-core-url-state.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"neural-ui-core-url-state.mjs","sources":["../../../../projects/ui-core/url-state/neu-url-state.service.ts","../../../../projects/ui-core/url-state/neural-ui-core-url-state.ts"],"sourcesContent":["import { Injectable, Injector, Signal, computed } from '@angular/core';\nimport { toSignal } from '@angular/core/rxjs-interop';\nimport { NavigationEnd, Params, Router } from '@angular/router';\nimport { filter, map, startWith } from 'rxjs/operators';\n\n/**\n * NeuUrlStateService — El Sistema Nervioso de NeuralUI\n *\n * Sincroniza el estado de la UI con los QueryParams de la URL.\n * Completamente reactivo via Angular Signals.\n *\n * Uso:\n * const urlState = inject(NeuUrlStateService);\n * const page = urlState.getParam('page'); // Signal<string | null>\n * urlState.setParam('menu', 'open'); // Actualiza ?menu=open\n * urlState.patchParams({ page: '2', q: 'filter' }); // Actualiza múltiples\n */\n@Injectable({ providedIn: 'root' })\nexport class NeuUrlStateService {\n /**\n * Signal con el mapa completo de queryParams actual.\n * Se actualiza automáticamente en cada NavigationEnd.\n */\n readonly params: Signal<Params>;\n\n constructor(\n private readonly injector: Injector,\n private readonly router: Router,\n ) {\n this.params = toSignal(\n this.router.events.pipe(\n filter((e): e is NavigationEnd => e instanceof NavigationEnd),\n startWith(null as null),\n map(() => this.router.parseUrl(this.router.url).queryParams as Params),\n ),\n {\n initialValue: this.router.parseUrl(this.router.url).queryParams as Params,\n injector: this.injector,\n },\n );\n }\n\n /**\n * Devuelve un Signal reactivo con el valor del parámetro indicado.\n * Memorizar el resultado: no llamar en bucle pues crea un computed nuevo c/vez.\n *\n * @example\n * readonly menuOpen = computed(() => this.urlState.getParam('menu')() === 'open');\n */\n getParam(key: string): Signal<string | null> {\n return computed(() => (this.params()[key] as string | undefined) ?? null);\n }\n\n /**\n * Establece un único queryParam en la URL.\n *\n * @param key Nombre del parámetro\n * @param value Valor. Pasar `null` para eliminarlo de la URL.\n * @param replaceUrl Si true (default) usa replaceState — no ensucia el historial.\n * Pasar false para acciones que el usuario debe poder deshacer con Atrás.\n */\n setParam(key: string, value: string | null, replaceUrl = true): void {\n void this.router.navigate([], {\n queryParams: { [key]: value },\n queryParamsHandling: 'merge',\n replaceUrl,\n });\n }\n\n /**\n * Actualiza múltiples queryParams en una sola navegación.\n *\n * @example\n * urlState.patchParams({ page: '1', q: 'Angular' });\n */\n patchParams(params: Record<string, string | null>, replaceUrl = true): void {\n void this.router.navigate([], {\n queryParams: params,\n queryParamsHandling: 'merge',\n replaceUrl,\n });\n }\n\n /**\n * Elimina todos los queryParams de la URL de una vez.\n */\n clearParams(replaceUrl = true): void {\n void this.router.navigate([], {\n queryParams: {},\n replaceUrl,\n });\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public_api';\n"],"names":[],"mappings":";;;;;;;AAKA;;;;;;;;;;;AAWG;MAEU,kBAAkB,CAAA;AAQV,IAAA,QAAA;AACA,IAAA,MAAA;AARnB;;;AAGG;AACM,IAAA,MAAM;IAEf,WAAA,CACmB,QAAkB,EAClB,MAAc,EAAA;QADd,IAAA,CAAA,QAAQ,GAAR,QAAQ;QACR,IAAA,CAAA,MAAM,GAAN,MAAM;QAEvB,IAAI,CAAC,MAAM,GAAG,QAAQ,CACpB,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CACrB,MAAM,CAAC,CAAC,CAAC,KAAyB,CAAC,YAAY,aAAa,CAAC,EAC7D,SAAS,CAAC,IAAY,CAAC,EACvB,GAAG,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,WAAqB,CAAC,CACvE,EACD;AACE,YAAA,YAAY,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,WAAqB;YACzE,QAAQ,EAAE,IAAI,CAAC,QAAQ;AACxB,SAAA,CACF;IACH;AAEA;;;;;;AAMG;AACH,IAAA,QAAQ,CAAC,GAAW,EAAA;AAClB,QAAA,OAAO,QAAQ,CAAC,MAAO,IAAI,CAAC,MAAM,EAAE,CAAC,GAAG,CAAwB,IAAI,IAAI,CAAC;IAC3E;AAEA;;;;;;;AAOG;AACH,IAAA,QAAQ,CAAC,GAAW,EAAE,KAAoB,EAAE,UAAU,GAAG,IAAI,EAAA;AAC3D,QAAA,KAAK,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,EAAE;AAC5B,YAAA,WAAW,EAAE,EAAE,CAAC,GAAG,GAAG,KAAK,EAAE;AAC7B,YAAA,mBAAmB,EAAE,OAAO;YAC5B,UAAU;AACX,SAAA,CAAC;IACJ;AAEA;;;;;AAKG;AACH,IAAA,WAAW,CAAC,MAAqC,EAAE,UAAU,GAAG,IAAI,EAAA;AAClE,QAAA,KAAK,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,EAAE;AAC5B,YAAA,WAAW,EAAE,MAAM;AACnB,YAAA,mBAAmB,EAAE,OAAO;YAC5B,UAAU;AACX,SAAA,CAAC;IACJ;AAEA;;AAEG;IACH,WAAW,CAAC,UAAU,GAAG,IAAI,EAAA;AAC3B,QAAA,KAAK,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,EAAE;AAC5B,YAAA,WAAW,EAAE,EAAE;YACf,UAAU;AACX,SAAA,CAAC;IACJ;uGAzEW,kBAAkB,EAAA,IAAA,EAAA,CAAA,EAAA,KAAA,EAAA,EAAA,CAAA,QAAA,EAAA,EAAA,EAAA,KAAA,EAAA,EAAA,CAAA,MAAA,EAAA,CAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,UAAA,EAAA,CAAA;AAAlB,IAAA,OAAA,KAAA,GAAA,EAAA,CAAA,qBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,QAAA,EAAA,EAAA,EAAA,IAAA,EAAA,kBAAkB,cADL,MAAM,EAAA,CAAA;;2FACnB,kBAAkB,EAAA,UAAA,EAAA,CAAA;kBAD9B,UAAU;mBAAC,EAAE,UAAU,EAAE,MAAM,EAAE;;;ACjBlC;;AAEG;;;;"}
|