shirkasoft-ui-components 1.2.0 → 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.
Files changed (79) hide show
  1. package/fesm2022/shirkasoft-ui-components-breadcrumb.mjs +199 -0
  2. package/fesm2022/shirkasoft-ui-components-breadcrumb.mjs.map +1 -0
  3. package/fesm2022/shirkasoft-ui-components-button.mjs +148 -0
  4. package/fesm2022/shirkasoft-ui-components-button.mjs.map +1 -0
  5. package/fesm2022/shirkasoft-ui-components-chart.mjs +153 -0
  6. package/fesm2022/shirkasoft-ui-components-chart.mjs.map +1 -0
  7. package/fesm2022/shirkasoft-ui-components-confirm-dialog.mjs +143 -0
  8. package/fesm2022/shirkasoft-ui-components-confirm-dialog.mjs.map +1 -0
  9. package/fesm2022/shirkasoft-ui-components-date-picker.mjs +638 -0
  10. package/fesm2022/shirkasoft-ui-components-date-picker.mjs.map +1 -0
  11. package/fesm2022/shirkasoft-ui-components-fieldset.mjs +94 -0
  12. package/fesm2022/shirkasoft-ui-components-fieldset.mjs.map +1 -0
  13. package/fesm2022/shirkasoft-ui-components-file-upload.mjs +294 -0
  14. package/fesm2022/shirkasoft-ui-components-file-upload.mjs.map +1 -0
  15. package/fesm2022/shirkasoft-ui-components-knob.mjs +269 -0
  16. package/fesm2022/shirkasoft-ui-components-knob.mjs.map +1 -0
  17. package/fesm2022/shirkasoft-ui-components-loading.mjs +255 -0
  18. package/fesm2022/shirkasoft-ui-components-loading.mjs.map +1 -0
  19. package/fesm2022/shirkasoft-ui-components-modal.mjs +290 -0
  20. package/fesm2022/shirkasoft-ui-components-modal.mjs.map +1 -0
  21. package/fesm2022/shirkasoft-ui-components-notification.mjs +143 -0
  22. package/fesm2022/shirkasoft-ui-components-notification.mjs.map +1 -0
  23. package/fesm2022/shirkasoft-ui-components-price-input.mjs +243 -0
  24. package/fesm2022/shirkasoft-ui-components-price-input.mjs.map +1 -0
  25. package/fesm2022/shirkasoft-ui-components-rail-sidebar.mjs +205 -0
  26. package/fesm2022/shirkasoft-ui-components-rail-sidebar.mjs.map +1 -0
  27. package/fesm2022/shirkasoft-ui-components-select.mjs +737 -0
  28. package/fesm2022/shirkasoft-ui-components-select.mjs.map +1 -0
  29. package/fesm2022/shirkasoft-ui-components-sidebar.mjs +185 -0
  30. package/fesm2022/shirkasoft-ui-components-sidebar.mjs.map +1 -0
  31. package/fesm2022/shirkasoft-ui-components-skeleton.mjs +74 -0
  32. package/fesm2022/shirkasoft-ui-components-skeleton.mjs.map +1 -0
  33. package/fesm2022/shirkasoft-ui-components-table.mjs +689 -0
  34. package/fesm2022/shirkasoft-ui-components-table.mjs.map +1 -0
  35. package/fesm2022/shirkasoft-ui-components-tabs.mjs +249 -0
  36. package/fesm2022/shirkasoft-ui-components-tabs.mjs.map +1 -0
  37. package/fesm2022/shirkasoft-ui-components-tag.mjs +70 -0
  38. package/fesm2022/shirkasoft-ui-components-tag.mjs.map +1 -0
  39. package/fesm2022/shirkasoft-ui-components-text-field.mjs +370 -0
  40. package/fesm2022/shirkasoft-ui-components-text-field.mjs.map +1 -0
  41. package/fesm2022/shirkasoft-ui-components-timeline.mjs +222 -0
  42. package/fesm2022/shirkasoft-ui-components-timeline.mjs.map +1 -0
  43. package/fesm2022/shirkasoft-ui-components-toggle.mjs +186 -0
  44. package/fesm2022/shirkasoft-ui-components-toggle.mjs.map +1 -0
  45. package/fesm2022/shirkasoft-ui-components-tooltip.mjs +169 -0
  46. package/fesm2022/shirkasoft-ui-components-tooltip.mjs.map +1 -0
  47. package/fesm2022/shirkasoft-ui-components-utils.mjs +69 -0
  48. package/fesm2022/shirkasoft-ui-components-utils.mjs.map +1 -0
  49. package/fesm2022/shirkasoft-ui-components-wizard.mjs +167 -0
  50. package/fesm2022/shirkasoft-ui-components-wizard.mjs.map +1 -0
  51. package/fesm2022/shirkasoft-ui-components.mjs +0 -5729
  52. package/fesm2022/shirkasoft-ui-components.mjs.map +1 -1
  53. package/package.json +101 -1
  54. package/types/shirkasoft-ui-components-breadcrumb.d.ts +68 -0
  55. package/types/shirkasoft-ui-components-button.d.ts +59 -0
  56. package/types/shirkasoft-ui-components-chart.d.ts +65 -0
  57. package/types/shirkasoft-ui-components-confirm-dialog.d.ts +88 -0
  58. package/types/shirkasoft-ui-components-date-picker.d.ts +200 -0
  59. package/types/shirkasoft-ui-components-fieldset.d.ts +72 -0
  60. package/types/shirkasoft-ui-components-file-upload.d.ts +121 -0
  61. package/types/shirkasoft-ui-components-knob.d.ts +69 -0
  62. package/types/shirkasoft-ui-components-loading.d.ts +94 -0
  63. package/types/shirkasoft-ui-components-modal.d.ts +139 -0
  64. package/types/shirkasoft-ui-components-notification.d.ts +82 -0
  65. package/types/shirkasoft-ui-components-price-input.d.ts +90 -0
  66. package/types/shirkasoft-ui-components-rail-sidebar.d.ts +121 -0
  67. package/types/shirkasoft-ui-components-select.d.ts +182 -0
  68. package/types/shirkasoft-ui-components-sidebar.d.ts +106 -0
  69. package/types/shirkasoft-ui-components-skeleton.d.ts +36 -0
  70. package/types/shirkasoft-ui-components-table.d.ts +293 -0
  71. package/types/shirkasoft-ui-components-tabs.d.ts +102 -0
  72. package/types/shirkasoft-ui-components-tag.d.ts +31 -0
  73. package/types/shirkasoft-ui-components-text-field.d.ts +120 -0
  74. package/types/shirkasoft-ui-components-timeline.d.ts +178 -0
  75. package/types/shirkasoft-ui-components-toggle.d.ts +48 -0
  76. package/types/shirkasoft-ui-components-tooltip.d.ts +47 -0
  77. package/types/shirkasoft-ui-components-utils.d.ts +9 -0
  78. package/types/shirkasoft-ui-components-wizard.d.ts +101 -0
  79. package/types/shirkasoft-ui-components.d.ts +1 -2333
@@ -0,0 +1,243 @@
1
+ import * as i0 from '@angular/core';
2
+ import { input, model, inject, DestroyRef, signal, computed, effect, forwardRef, ChangeDetectionStrategy, Component } from '@angular/core';
3
+ import * as i1 from '@angular/common';
4
+ import { CommonModule } from '@angular/common';
5
+ import { ReactiveFormsModule, NG_VALUE_ACCESSOR } from '@angular/forms';
6
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
7
+
8
+ /**
9
+ * Shirkasoft UI Components.
10
+ * Authors: Roberto Valladares Martin (Ansem13), Julio César García García (Sanjuli14).
11
+ * Belongs to Shirkasoft.
12
+ */
13
+ /**
14
+ * Currency/percentage numeric input with reactive forms support.
15
+ *
16
+ * Binding modes (highest precedence first):
17
+ * - `control`: a FormControl (or use it inside a form with `formControlName`).
18
+ * - `value`/`valueChange` or `[(value)]`: two-way model signals.
19
+ *
20
+ * Text-based input (`type="text"` + `inputmode="decimal"`) with decimal
21
+ * precision control, min/max clamping on blur and thousands formatting.
22
+ */
23
+ class PriceInputComponent {
24
+ constructor() {
25
+ /** Reactive control. When provided it takes precedence over `value`/`valueChange`. */
26
+ this.control = input(undefined, ...(ngDevMode ? [{ debugName: "control" }] : /* istanbul ignore next */ []));
27
+ /** Numeric value (two-way), used when no `control` is bound. */
28
+ this.value = model(0, ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
29
+ /** Whether the input works as a percentage (two-way). */
30
+ this.isPercentage = model(false, ...(ngDevMode ? [{ debugName: "isPercentage" }] : /* istanbul ignore next */ []));
31
+ /** Label displayed above the field. */
32
+ this.label = input('', ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
33
+ /** id attribute applied to the native input. */
34
+ this.inputId = input('', ...(ngDevMode ? [{ debugName: "inputId" }] : /* istanbul ignore next */ []));
35
+ /** Marks the field as required. */
36
+ this.required = input(false, ...(ngDevMode ? [{ debugName: "required" }] : /* istanbul ignore next */ []));
37
+ /** Disables the input. */
38
+ this.disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
39
+ /** Minimum allowed value (clamped on blur). */
40
+ this.min = input(0, ...(ngDevMode ? [{ debugName: "min" }] : /* istanbul ignore next */ []));
41
+ /** Maximum allowed value (clamped on blur). */
42
+ this.max = input(Infinity, ...(ngDevMode ? [{ debugName: "max" }] : /* istanbul ignore next */ []));
43
+ /** Maximum number of decimal places (0 = integer). */
44
+ this.decimals = input(2, ...(ngDevMode ? [{ debugName: "decimals" }] : /* istanbul ignore next */ []));
45
+ /** Symbol shown in the mode toggle button when not a percentage. */
46
+ this.currency = input('$', ...(ngDevMode ? [{ debugName: "currency" }] : /* istanbul ignore next */ []));
47
+ /** Placeholder text of the native input. */
48
+ this.placeholder = input('', ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
49
+ /** Accessible label; falls back to `label`. */
50
+ this.ariaLabel = input('', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
51
+ /** Blocks the minus sign from being typed. */
52
+ this.preventNegative = input(false, ...(ngDevMode ? [{ debugName: "preventNegative" }] : /* istanbul ignore next */ []));
53
+ /** Shows the error styling on the field. */
54
+ this.showError = input(false, ...(ngDevMode ? [{ debugName: "showError" }] : /* istanbul ignore next */ []));
55
+ /** Message rendered when the field is in error state. */
56
+ this.errorMessage = input('', ...(ngDevMode ? [{ debugName: "errorMessage" }] : /* istanbul ignore next */ []));
57
+ this.destroyRef = inject(DestroyRef);
58
+ /** Text currently shown in the input (raw while focused, formatted on blur). */
59
+ this.displayText = signal('', ...(ngDevMode ? [{ debugName: "displayText" }] : /* istanbul ignore next */ []));
60
+ /** Whether the native input currently has focus. */
61
+ this.focused = signal(false, ...(ngDevMode ? [{ debugName: "focused" }] : /* istanbul ignore next */ []));
62
+ /** Disabled state coming from the reactive control. */
63
+ this.controlDisabled = signal(false, ...(ngDevMode ? [{ debugName: "controlDisabled" }] : /* istanbul ignore next */ []));
64
+ /** Prevents re-subscribing to the same form control. */
65
+ this.ctrlSubscribed = false;
66
+ /** Effective disabled state combining the input and the form binding. */
67
+ this.isDisabled = computed(() => this.disabled() || this.controlDisabled(), ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
68
+ /** Callbacks used by the ControlValueAccessor contract. */
69
+ this.onChange = () => { };
70
+ this.onTouched = () => { };
71
+ // Subscribe once to the bound control for change/status tracking.
72
+ effect(() => {
73
+ const ctrl = this.control();
74
+ if (!ctrl || this.ctrlSubscribed)
75
+ return;
76
+ this.ctrlSubscribed = true;
77
+ this.controlDisabled.set(ctrl.disabled);
78
+ ctrl.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((v) => {
79
+ if (!this.focused()) {
80
+ this.displayText.set(this.formatNumber(v ?? 0));
81
+ }
82
+ });
83
+ ctrl.statusChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => {
84
+ this.controlDisabled.set(ctrl.disabled);
85
+ });
86
+ });
87
+ // Reflect the effective value into the display (unless the user is typing).
88
+ effect(() => {
89
+ const ctrl = this.control();
90
+ const v = ctrl ? ctrl.value : this.value();
91
+ if (!this.focused()) {
92
+ this.displayText.set(this.formatNumber(v ?? 0));
93
+ }
94
+ });
95
+ }
96
+ /** Toggles between amount and percentage modes (unless disabled). */
97
+ toggleMode() {
98
+ if (this.isDisabled())
99
+ return;
100
+ this.isPercentage.update((v) => !v);
101
+ }
102
+ /** Selects the whole value on focus so the user can type over it. */
103
+ onFocus(event) {
104
+ this.focused.set(true);
105
+ event.target.select();
106
+ }
107
+ /** Clamps, rounds and formats the value when the field loses focus. */
108
+ onBlur() {
109
+ this.focused.set(false);
110
+ this.onTouched();
111
+ let v = parseFloat(this.displayText().replace(/,/g, ''));
112
+ if (isNaN(v))
113
+ v = 0;
114
+ const min = this.min();
115
+ const max = this.max();
116
+ if (isFinite(min) && v < min)
117
+ v = min;
118
+ if (isFinite(max) && v > max)
119
+ v = max;
120
+ v = this.roundTo(v);
121
+ this.setValue(v);
122
+ this.displayText.set(this.formatNumber(v));
123
+ }
124
+ /** Blocks characters that would break the numeric format. */
125
+ onKeydown(event) {
126
+ const allowed = [
127
+ 'Backspace',
128
+ 'Tab',
129
+ 'End',
130
+ 'Home',
131
+ 'ArrowLeft',
132
+ 'ArrowRight',
133
+ 'Delete',
134
+ 'Enter',
135
+ 'Escape',
136
+ ];
137
+ if (allowed.includes(event.key))
138
+ return;
139
+ if (/^\d$/.test(event.key))
140
+ return;
141
+ if (event.key === '.' || event.key === ',') {
142
+ const input = event.target;
143
+ if (input.value.includes('.') || input.value.includes(','))
144
+ event.preventDefault();
145
+ return;
146
+ }
147
+ if (event.key === '-') {
148
+ const input = event.target;
149
+ if (this.preventNegative() || this.min() >= 0) {
150
+ event.preventDefault();
151
+ }
152
+ else if (input.value.includes('-')) {
153
+ event.preventDefault();
154
+ }
155
+ return;
156
+ }
157
+ event.preventDefault();
158
+ }
159
+ /** Sanitizes the raw input (digits + one separator + optional sign) and writes the value once. */
160
+ onInput(event) {
161
+ const input = event.target;
162
+ const pos = input.selectionStart ?? input.value.length;
163
+ let cleaned = input.value.replace(/[^0-9.,-]/g, '');
164
+ if (this.preventNegative() || this.min() >= 0)
165
+ cleaned = cleaned.replace(/-/g, '');
166
+ cleaned = cleaned.replace(/,/g, '.');
167
+ const dot = cleaned.indexOf('.');
168
+ if (dot >= 0) {
169
+ const dec = Math.max(0, this.decimals());
170
+ const intPart = cleaned.slice(0, dot);
171
+ const decPart = cleaned.slice(dot + 1, dot + 1 + dec);
172
+ cleaned = intPart + '.' + decPart;
173
+ }
174
+ input.value = cleaned;
175
+ const newPos = Math.min(pos, cleaned.length);
176
+ input.setSelectionRange(newPos, newPos);
177
+ this.displayText.set(cleaned);
178
+ const num = parseFloat(cleaned);
179
+ this.setValue(isNaN(num) ? 0 : num);
180
+ }
181
+ /** Writes the value to the effective binding source. */
182
+ setValue(v) {
183
+ const ctrl = this.control();
184
+ if (ctrl) {
185
+ ctrl.setValue(v, { emitEvent: true });
186
+ }
187
+ else {
188
+ this.value.set(v);
189
+ }
190
+ this.onChange(v);
191
+ }
192
+ roundTo(v) {
193
+ const factor = Math.pow(10, Math.max(0, this.decimals()));
194
+ return Math.round(v * factor) / factor;
195
+ }
196
+ /** Formats a number with thousands separator and up to `decimals` places. */
197
+ formatNumber(v) {
198
+ if (v == null || isNaN(v))
199
+ return '0';
200
+ const dec = Math.max(0, this.decimals());
201
+ return v.toLocaleString('en-US', { minimumFractionDigits: 0, maximumFractionDigits: dec });
202
+ }
203
+ // ── ControlValueAccessor ───────────────────────────────────────────────────
204
+ writeValue(value) {
205
+ if (!this.focused()) {
206
+ this.displayText.set(this.formatNumber(value ?? 0));
207
+ }
208
+ }
209
+ registerOnChange(fn) {
210
+ this.onChange = fn;
211
+ }
212
+ registerOnTouched(fn) {
213
+ this.onTouched = fn;
214
+ }
215
+ setDisabledState(isDisabled) {
216
+ this.controlDisabled.set(isDisabled);
217
+ }
218
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: PriceInputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
219
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.18", type: PriceInputComponent, isStandalone: true, selector: "shk-price-input", inputs: { control: { classPropertyName: "control", publicName: "control", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, isPercentage: { classPropertyName: "isPercentage", publicName: "isPercentage", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, inputId: { classPropertyName: "inputId", publicName: "inputId", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, decimals: { classPropertyName: "decimals", publicName: "decimals", isSignal: true, isRequired: false, transformFunction: null }, currency: { classPropertyName: "currency", publicName: "currency", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, preventNegative: { classPropertyName: "preventNegative", publicName: "preventNegative", isSignal: true, isRequired: false, transformFunction: null }, showError: { classPropertyName: "showError", publicName: "showError", isSignal: true, isRequired: false, transformFunction: null }, errorMessage: { classPropertyName: "errorMessage", publicName: "errorMessage", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", isPercentage: "isPercentageChange" }, providers: [
220
+ {
221
+ provide: NG_VALUE_ACCESSOR,
222
+ useExisting: forwardRef(() => PriceInputComponent),
223
+ multi: true,
224
+ },
225
+ ], ngImport: i0, template: "<div>\n @if (label()) {\n <div class=\"text-sm font-medium _shk-text-surface-700 dark:[color:var(--shk-surface-600)] mb-1\">\n {{ label() }}\n @if (required()) {\n <span class=\"text-red-400\">*</span>\n }\n </div>\n }\n <div class=\"flex\">\n <input\n [id]=\"inputId()\"\n type=\"text\"\n inputmode=\"decimal\"\n autocomplete=\"off\"\n [value]=\"displayText()\"\n [placeholder]=\"placeholder()\"\n [attr.aria-label]=\"ariaLabel() || label()\"\n [attr.aria-invalid]=\"showError() || null\"\n [disabled]=\"isDisabled()\"\n (focus)=\"onFocus($event)\"\n (blur)=\"onBlur()\"\n (keydown)=\"onKeydown($event)\"\n (input)=\"onInput($event)\"\n class=\"block w-full px-3 py-2 _shk-text-surface-700 dark:[color:var(--shk-surface-700)] _shk-bg-surface-50 dark:[background-color:var(--shk-surface-100)] border rounded-l-lg transition-colors placeholder:[color:var(--shk-surface-400)] dark:placeholder:[color:var(--shk-surface-400)] focus:outline-none focus:[background-color:var(--shk-surface-50)] dark:focus:[background-color:var(--shk-surface-100)] focus:ring-1 disabled:[background-color:var(--shk-surface-50)] dark:disabled:[background-color:var(--shk-surface-100)] disabled:cursor-not-allowed sm:text-sm\"\n [ngClass]=\"{\n 'border-red-500 focus:border-red-500 focus:ring-red-500': showError(),\n '_shk-border dark:[border-color:var(--shk-surface-200)] focus:[border-color:var(--shk-primary-500)] focus:[--tw-ring-color:var(--shk-primary-500)]': !showError()\n }\"\n />\n <button\n type=\"button\"\n (click)=\"toggleMode()\"\n [disabled]=\"isDisabled()\"\n class=\"px-3 py-2 text-sm font-medium border rounded-r-lg transition-colors shrink-0\"\n [ngClass]=\"{\n '_shk-bg-primary-soft _shk-border-primary-200 _shk-text-primary-700 dark:[background-color:var(--shk-primary-900)] dark:[border-color:var(--shk-primary-700)] dark:[color:var(--shk-primary-300)]': isPercentage(),\n '_shk-bg-white _shk-border _shk-text-surface-500 dark:[background-color:var(--shk-surface-100)] dark:[border-color:var(--shk-surface-200)] dark:[color:var(--shk-surface-400)]': !isPercentage(),\n 'cursor-not-allowed opacity-50': isDisabled()\n }\"\n >\n {{ isPercentage() ? '%' : currency() }}\n </button>\n </div>\n @if (showError()) {\n <div class=\"text-xs _shk-text-red-500 mt-1\">\n {{ errorMessage() }}\n </div>\n }\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: ReactiveFormsModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
226
+ }
227
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: PriceInputComponent, decorators: [{
228
+ type: Component,
229
+ args: [{ selector: 'shk-price-input', standalone: true, imports: [CommonModule, ReactiveFormsModule], changeDetection: ChangeDetectionStrategy.OnPush, providers: [
230
+ {
231
+ provide: NG_VALUE_ACCESSOR,
232
+ useExisting: forwardRef(() => PriceInputComponent),
233
+ multi: true,
234
+ },
235
+ ], template: "<div>\n @if (label()) {\n <div class=\"text-sm font-medium _shk-text-surface-700 dark:[color:var(--shk-surface-600)] mb-1\">\n {{ label() }}\n @if (required()) {\n <span class=\"text-red-400\">*</span>\n }\n </div>\n }\n <div class=\"flex\">\n <input\n [id]=\"inputId()\"\n type=\"text\"\n inputmode=\"decimal\"\n autocomplete=\"off\"\n [value]=\"displayText()\"\n [placeholder]=\"placeholder()\"\n [attr.aria-label]=\"ariaLabel() || label()\"\n [attr.aria-invalid]=\"showError() || null\"\n [disabled]=\"isDisabled()\"\n (focus)=\"onFocus($event)\"\n (blur)=\"onBlur()\"\n (keydown)=\"onKeydown($event)\"\n (input)=\"onInput($event)\"\n class=\"block w-full px-3 py-2 _shk-text-surface-700 dark:[color:var(--shk-surface-700)] _shk-bg-surface-50 dark:[background-color:var(--shk-surface-100)] border rounded-l-lg transition-colors placeholder:[color:var(--shk-surface-400)] dark:placeholder:[color:var(--shk-surface-400)] focus:outline-none focus:[background-color:var(--shk-surface-50)] dark:focus:[background-color:var(--shk-surface-100)] focus:ring-1 disabled:[background-color:var(--shk-surface-50)] dark:disabled:[background-color:var(--shk-surface-100)] disabled:cursor-not-allowed sm:text-sm\"\n [ngClass]=\"{\n 'border-red-500 focus:border-red-500 focus:ring-red-500': showError(),\n '_shk-border dark:[border-color:var(--shk-surface-200)] focus:[border-color:var(--shk-primary-500)] focus:[--tw-ring-color:var(--shk-primary-500)]': !showError()\n }\"\n />\n <button\n type=\"button\"\n (click)=\"toggleMode()\"\n [disabled]=\"isDisabled()\"\n class=\"px-3 py-2 text-sm font-medium border rounded-r-lg transition-colors shrink-0\"\n [ngClass]=\"{\n '_shk-bg-primary-soft _shk-border-primary-200 _shk-text-primary-700 dark:[background-color:var(--shk-primary-900)] dark:[border-color:var(--shk-primary-700)] dark:[color:var(--shk-primary-300)]': isPercentage(),\n '_shk-bg-white _shk-border _shk-text-surface-500 dark:[background-color:var(--shk-surface-100)] dark:[border-color:var(--shk-surface-200)] dark:[color:var(--shk-surface-400)]': !isPercentage(),\n 'cursor-not-allowed opacity-50': isDisabled()\n }\"\n >\n {{ isPercentage() ? '%' : currency() }}\n </button>\n </div>\n @if (showError()) {\n <div class=\"text-xs _shk-text-red-500 mt-1\">\n {{ errorMessage() }}\n </div>\n }\n</div>\n" }]
236
+ }], ctorParameters: () => [], propDecorators: { control: [{ type: i0.Input, args: [{ isSignal: true, alias: "control", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], isPercentage: [{ type: i0.Input, args: [{ isSignal: true, alias: "isPercentage", required: false }] }, { type: i0.Output, args: ["isPercentageChange"] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], inputId: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputId", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], decimals: [{ type: i0.Input, args: [{ isSignal: true, alias: "decimals", required: false }] }], currency: [{ type: i0.Input, args: [{ isSignal: true, alias: "currency", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], preventNegative: [{ type: i0.Input, args: [{ isSignal: true, alias: "preventNegative", required: false }] }], showError: [{ type: i0.Input, args: [{ isSignal: true, alias: "showError", required: false }] }], errorMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorMessage", required: false }] }] } });
237
+
238
+ /**
239
+ * Generated bundle index. Do not edit.
240
+ */
241
+
242
+ export { PriceInputComponent };
243
+ //# sourceMappingURL=shirkasoft-ui-components-price-input.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shirkasoft-ui-components-price-input.mjs","sources":["../../../projects/ui-components/price-input/price-input.component.ts","../../../projects/ui-components/price-input/price-input.component.html","../../../projects/ui-components/price-input/shirkasoft-ui-components-price-input.ts"],"sourcesContent":["import {\n Component,\n model,\n input,\n signal,\n computed,\n effect,\n inject,\n forwardRef,\n DestroyRef,\n ChangeDetectionStrategy,\n} from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport {\n ControlValueAccessor,\n FormControl,\n NG_VALUE_ACCESSOR,\n ReactiveFormsModule,\n} from '@angular/forms';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\n\n/**\n * Shirkasoft UI Components.\n * Authors: Roberto Valladares Martin (Ansem13), Julio César García García (Sanjuli14).\n * Belongs to Shirkasoft.\n */\n\n/**\n * Currency/percentage numeric input with reactive forms support.\n *\n * Binding modes (highest precedence first):\n * - `control`: a FormControl (or use it inside a form with `formControlName`).\n * - `value`/`valueChange` or `[(value)]`: two-way model signals.\n *\n * Text-based input (`type=\"text\"` + `inputmode=\"decimal\"`) with decimal\n * precision control, min/max clamping on blur and thousands formatting.\n */\n@Component({\n selector: 'shk-price-input',\n standalone: true,\n imports: [CommonModule, ReactiveFormsModule],\n templateUrl: './price-input.component.html',\n changeDetection: ChangeDetectionStrategy.OnPush,\n providers: [\n {\n provide: NG_VALUE_ACCESSOR,\n useExisting: forwardRef(() => PriceInputComponent),\n multi: true,\n },\n ],\n})\nexport class PriceInputComponent implements ControlValueAccessor {\n /** Reactive control. When provided it takes precedence over `value`/`valueChange`. */\n control = input<FormControl<number | null> | undefined>(undefined);\n /** Numeric value (two-way), used when no `control` is bound. */\n value = model<number | null>(0);\n /** Whether the input works as a percentage (two-way). */\n isPercentage = model<boolean>(false);\n /** Label displayed above the field. */\n label = input<string>('');\n /** id attribute applied to the native input. */\n inputId = input<string>('');\n /** Marks the field as required. */\n required = input<boolean>(false);\n /** Disables the input. */\n disabled = input<boolean>(false);\n /** Minimum allowed value (clamped on blur). */\n min = input<number>(0);\n /** Maximum allowed value (clamped on blur). */\n max = input<number>(Infinity);\n /** Maximum number of decimal places (0 = integer). */\n decimals = input<number>(2);\n /** Symbol shown in the mode toggle button when not a percentage. */\n currency = input<string>('$');\n /** Placeholder text of the native input. */\n placeholder = input<string>('');\n /** Accessible label; falls back to `label`. */\n ariaLabel = input<string>('');\n /** Blocks the minus sign from being typed. */\n preventNegative = input<boolean>(false);\n /** Shows the error styling on the field. */\n showError = input<boolean>(false);\n /** Message rendered when the field is in error state. */\n errorMessage = input<string>('');\n\n private destroyRef = inject(DestroyRef);\n\n /** Text currently shown in the input (raw while focused, formatted on blur). */\n displayText = signal<string>('');\n /** Whether the native input currently has focus. */\n focused = signal<boolean>(false);\n /** Disabled state coming from the reactive control. */\n private controlDisabled = signal<boolean>(false);\n /** Prevents re-subscribing to the same form control. */\n private ctrlSubscribed = false;\n\n /** Effective disabled state combining the input and the form binding. */\n isDisabled = computed(() => this.disabled() || this.controlDisabled());\n\n /** Callbacks used by the ControlValueAccessor contract. */\n private onChange: (value: number | null) => void = () => {};\n private onTouched: () => void = () => {};\n\n constructor() {\n // Subscribe once to the bound control for change/status tracking.\n effect(() => {\n const ctrl = this.control();\n if (!ctrl || this.ctrlSubscribed) return;\n this.ctrlSubscribed = true;\n this.controlDisabled.set(ctrl.disabled);\n\n ctrl.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((v) => {\n if (!this.focused()) {\n this.displayText.set(this.formatNumber(v ?? 0));\n }\n });\n ctrl.statusChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => {\n this.controlDisabled.set(ctrl.disabled);\n });\n });\n\n // Reflect the effective value into the display (unless the user is typing).\n effect(() => {\n const ctrl = this.control();\n const v = ctrl ? (ctrl.value as number | null) : this.value();\n if (!this.focused()) {\n this.displayText.set(this.formatNumber(v ?? 0));\n }\n });\n }\n\n /** Toggles between amount and percentage modes (unless disabled). */\n toggleMode(): void {\n if (this.isDisabled()) return;\n this.isPercentage.update((v) => !v);\n }\n\n /** Selects the whole value on focus so the user can type over it. */\n onFocus(event: FocusEvent): void {\n this.focused.set(true);\n (event.target as HTMLInputElement).select();\n }\n\n /** Clamps, rounds and formats the value when the field loses focus. */\n onBlur(): void {\n this.focused.set(false);\n this.onTouched();\n\n let v = parseFloat(this.displayText().replace(/,/g, ''));\n if (isNaN(v)) v = 0;\n\n const min = this.min();\n const max = this.max();\n if (isFinite(min) && v < min) v = min;\n if (isFinite(max) && v > max) v = max;\n\n v = this.roundTo(v);\n this.setValue(v);\n this.displayText.set(this.formatNumber(v));\n }\n\n /** Blocks characters that would break the numeric format. */\n onKeydown(event: KeyboardEvent): void {\n const allowed = [\n 'Backspace',\n 'Tab',\n 'End',\n 'Home',\n 'ArrowLeft',\n 'ArrowRight',\n 'Delete',\n 'Enter',\n 'Escape',\n ];\n if (allowed.includes(event.key)) return;\n if (/^\\d$/.test(event.key)) return;\n\n if (event.key === '.' || event.key === ',') {\n const input = event.target as HTMLInputElement;\n if (input.value.includes('.') || input.value.includes(',')) event.preventDefault();\n return;\n }\n\n if (event.key === '-') {\n const input = event.target as HTMLInputElement;\n if (this.preventNegative() || this.min() >= 0) {\n event.preventDefault();\n } else if (input.value.includes('-')) {\n event.preventDefault();\n }\n return;\n }\n\n event.preventDefault();\n }\n\n /** Sanitizes the raw input (digits + one separator + optional sign) and writes the value once. */\n onInput(event: Event): void {\n const input = event.target as HTMLInputElement;\n const pos = input.selectionStart ?? input.value.length;\n\n let cleaned = input.value.replace(/[^0-9.,-]/g, '');\n if (this.preventNegative() || this.min() >= 0) cleaned = cleaned.replace(/-/g, '');\n cleaned = cleaned.replace(/,/g, '.');\n\n const dot = cleaned.indexOf('.');\n if (dot >= 0) {\n const dec = Math.max(0, this.decimals());\n const intPart = cleaned.slice(0, dot);\n const decPart = cleaned.slice(dot + 1, dot + 1 + dec);\n cleaned = intPart + '.' + decPart;\n }\n\n input.value = cleaned;\n const newPos = Math.min(pos, cleaned.length);\n input.setSelectionRange(newPos, newPos);\n this.displayText.set(cleaned);\n\n const num = parseFloat(cleaned);\n this.setValue(isNaN(num) ? 0 : num);\n }\n\n /** Writes the value to the effective binding source. */\n private setValue(v: number): void {\n const ctrl = this.control();\n if (ctrl) {\n ctrl.setValue(v, { emitEvent: true });\n } else {\n this.value.set(v);\n }\n this.onChange(v);\n }\n\n private roundTo(v: number): number {\n const factor = Math.pow(10, Math.max(0, this.decimals()));\n return Math.round(v * factor) / factor;\n }\n\n /** Formats a number with thousands separator and up to `decimals` places. */\n private formatNumber(v: number): string {\n if (v == null || isNaN(v)) return '0';\n const dec = Math.max(0, this.decimals());\n return v.toLocaleString('en-US', { minimumFractionDigits: 0, maximumFractionDigits: dec });\n }\n\n // ── ControlValueAccessor ───────────────────────────────────────────────────\n\n writeValue(value: any): void {\n if (!this.focused()) {\n this.displayText.set(this.formatNumber(value ?? 0));\n }\n }\n\n registerOnChange(fn: any): void {\n this.onChange = fn;\n }\n\n registerOnTouched(fn: any): void {\n this.onTouched = fn;\n }\n\n setDisabledState(isDisabled: boolean): void {\n this.controlDisabled.set(isDisabled);\n }\n}\n","<div>\n @if (label()) {\n <div class=\"text-sm font-medium _shk-text-surface-700 dark:[color:var(--shk-surface-600)] mb-1\">\n {{ label() }}\n @if (required()) {\n <span class=\"text-red-400\">*</span>\n }\n </div>\n }\n <div class=\"flex\">\n <input\n [id]=\"inputId()\"\n type=\"text\"\n inputmode=\"decimal\"\n autocomplete=\"off\"\n [value]=\"displayText()\"\n [placeholder]=\"placeholder()\"\n [attr.aria-label]=\"ariaLabel() || label()\"\n [attr.aria-invalid]=\"showError() || null\"\n [disabled]=\"isDisabled()\"\n (focus)=\"onFocus($event)\"\n (blur)=\"onBlur()\"\n (keydown)=\"onKeydown($event)\"\n (input)=\"onInput($event)\"\n class=\"block w-full px-3 py-2 _shk-text-surface-700 dark:[color:var(--shk-surface-700)] _shk-bg-surface-50 dark:[background-color:var(--shk-surface-100)] border rounded-l-lg transition-colors placeholder:[color:var(--shk-surface-400)] dark:placeholder:[color:var(--shk-surface-400)] focus:outline-none focus:[background-color:var(--shk-surface-50)] dark:focus:[background-color:var(--shk-surface-100)] focus:ring-1 disabled:[background-color:var(--shk-surface-50)] dark:disabled:[background-color:var(--shk-surface-100)] disabled:cursor-not-allowed sm:text-sm\"\n [ngClass]=\"{\n 'border-red-500 focus:border-red-500 focus:ring-red-500': showError(),\n '_shk-border dark:[border-color:var(--shk-surface-200)] focus:[border-color:var(--shk-primary-500)] focus:[--tw-ring-color:var(--shk-primary-500)]': !showError()\n }\"\n />\n <button\n type=\"button\"\n (click)=\"toggleMode()\"\n [disabled]=\"isDisabled()\"\n class=\"px-3 py-2 text-sm font-medium border rounded-r-lg transition-colors shrink-0\"\n [ngClass]=\"{\n '_shk-bg-primary-soft _shk-border-primary-200 _shk-text-primary-700 dark:[background-color:var(--shk-primary-900)] dark:[border-color:var(--shk-primary-700)] dark:[color:var(--shk-primary-300)]': isPercentage(),\n '_shk-bg-white _shk-border _shk-text-surface-500 dark:[background-color:var(--shk-surface-100)] dark:[border-color:var(--shk-surface-200)] dark:[color:var(--shk-surface-400)]': !isPercentage(),\n 'cursor-not-allowed opacity-50': isDisabled()\n }\"\n >\n {{ isPercentage() ? '%' : currency() }}\n </button>\n </div>\n @if (showError()) {\n <div class=\"text-xs _shk-text-red-500 mt-1\">\n {{ errorMessage() }}\n </div>\n }\n</div>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,205 @@
1
+ import * as i0 from '@angular/core';
2
+ import { input, model, output, signal, computed, effect, Component } from '@angular/core';
3
+ import * as i1 from '@angular/common';
4
+ import { CommonModule } from '@angular/common';
5
+ import { LucideMenu, LucideDynamicIcon } from '@lucide/angular';
6
+ import { getIcon } from 'shirkasoft-ui-components/utils';
7
+ import { TooltipComponent } from 'shirkasoft-ui-components/tooltip';
8
+
9
+ /**
10
+ * Shirkasoft UI Components.
11
+ * Authors: Roberto Valladares Martin (Ansem13), Julio César García García (Sanjuli14).
12
+ * Belongs to Shirkasoft.
13
+ */
14
+ /**
15
+ * Narrow "rail" sidebar that expands into sections. Supports sections, roles,
16
+ * language switching, collapse/expand and responsive tooltips.
17
+ */
18
+ class RailSidebarComponent {
19
+ constructor() {
20
+ /** Sections presented as expandable areas of the rail. */
21
+ this.sections = input([], ...(ngDevMode ? [{ debugName: "sections" }] : /* istanbul ignore next */ []));
22
+ /** Current location url used to detect the active section/item. */
23
+ this.activeUrl = input('', ...(ngDevMode ? [{ debugName: "activeUrl" }] : /* istanbul ignore next */ []));
24
+ /** Key of the active section, bindable with [(activeSectionKey)]. */
25
+ this.activeSectionKey = model('', ...(ngDevMode ? [{ debugName: "activeSectionKey" }] : /* istanbul ignore next */ []));
26
+ /** Collapsed (rail only) state, bindable with [(collapsed)]. */
27
+ this.collapsed = model(false, ...(ngDevMode ? [{ debugName: "collapsed" }] : /* istanbul ignore next */ []));
28
+ /** Active language code. */
29
+ this.activeLang = input('es', ...(ngDevMode ? [{ debugName: "activeLang" }] : /* istanbul ignore next */ []));
30
+ /** Languages available in the language switcher. */
31
+ this.langs = input(['es', 'en'], ...(ngDevMode ? [{ debugName: "langs" }] : /* istanbul ignore next */ []));
32
+ /** Brand logo image/url. */
33
+ this.logo = input('', ...(ngDevMode ? [{ debugName: "logo" }] : /* istanbul ignore next */ []));
34
+ /** Application name shown when expanded. */
35
+ this.appName = input('', ...(ngDevMode ? [{ debugName: "appName" }] : /* istanbul ignore next */ []));
36
+ /** Role used to filter items that declare `roles`. */
37
+ this.userRole = input('', ...(ngDevMode ? [{ debugName: "userRole" }] : /* istanbul ignore next */ []));
38
+ /** Optional transform applied to item labels (e.g. i18n). */
39
+ this.labelPipe = input((label) => label, ...(ngDevMode ? [{ debugName: "labelPipe" }] : /* istanbul ignore next */ []));
40
+ /** Width of the collapsed rail. */
41
+ this.railWidth = input('4.5rem', ...(ngDevMode ? [{ debugName: "railWidth" }] : /* istanbul ignore next */ []));
42
+ /** Width when a section is expanded. */
43
+ this.expandedWidth = input('16.5rem', ...(ngDevMode ? [{ debugName: "expandedWidth" }] : /* istanbul ignore next */ []));
44
+ /** Tooltip for the workspace/home action. */
45
+ this.workspaceTooltip = input('Inicio', ...(ngDevMode ? [{ debugName: "workspaceTooltip" }] : /* istanbul ignore next */ []));
46
+ /** Tooltip for the logout action. */
47
+ this.logoutTooltip = input('Cerrar sesión', ...(ngDevMode ? [{ debugName: "logoutTooltip" }] : /* istanbul ignore next */ []));
48
+ /** Tooltip for collapsing the sidebar. */
49
+ this.collapseTooltip = input('Colapsar', ...(ngDevMode ? [{ debugName: "collapseTooltip" }] : /* istanbul ignore next */ []));
50
+ /** Tooltip for expanding the sidebar. */
51
+ this.expandTooltip = input('Expandir', ...(ngDevMode ? [{ debugName: "expandTooltip" }] : /* istanbul ignore next */ []));
52
+ /** Emitted when a section is selected, with its key and optional route. */
53
+ this.sectionSelected = output();
54
+ /** Emitted when a navigation item is clicked. */
55
+ this.itemClick = output();
56
+ /** Emitted with the next language code when the user switches language. */
57
+ this.setLang = output();
58
+ /** Emitted when the user requests to log out. */
59
+ this.logoutRequested = output();
60
+ /** Emitted with the new collapsed state after toggling. */
61
+ this.toggleSidebar = output();
62
+ /** Emitted when the workspace/home entry is clicked. */
63
+ this.workspaceClick = output();
64
+ this.getIcon = (name) => getIcon(name, LucideMenu);
65
+ /** Set of group ids currently expanded. */
66
+ this.expandedGroups = signal(new Set(), ...(ngDevMode ? [{ debugName: "expandedGroups" }] : /* istanbul ignore next */ []));
67
+ /** Current sidebar width depending on the collapsed state. */
68
+ this.width = computed(() => this.collapsed() ? this.railWidth() : this.expandedWidth(), ...(ngDevMode ? [{ debugName: "width" }] : /* istanbul ignore next */ []));
69
+ /** The section currently selected. */
70
+ this.activeSection = computed(() => this.sections().find((s) => s.key === this.activeSectionKey()) ?? null, ...(ngDevMode ? [{ debugName: "activeSection" }] : /* istanbul ignore next */ []));
71
+ /** Items of the active section, filtered by role. */
72
+ this.navItems = computed(() => {
73
+ const role = this.userRole();
74
+ const items = this.activeSection()?.items ?? [];
75
+ return items.filter((item) => {
76
+ if (!item.roles || item.roles.length === 0)
77
+ return true;
78
+ return role ? item.roles.includes(role) : false;
79
+ });
80
+ }, ...(ngDevMode ? [{ debugName: "navItems" }] : /* istanbul ignore next */ []));
81
+ effect(() => {
82
+ const url = this.activeUrl();
83
+ if (!url)
84
+ return;
85
+ const section = this.sections().find((s) => {
86
+ if (s.matchPrefixes?.length) {
87
+ return s.matchPrefixes.some((prefix) => url.startsWith(prefix));
88
+ }
89
+ return s.routerLink ? url.startsWith(s.routerLink) : false;
90
+ });
91
+ if (section) {
92
+ this.activeSectionKey.set(section.key);
93
+ }
94
+ });
95
+ }
96
+ /** Applies the `labelPipe` transform to a label. */
97
+ label(label) {
98
+ return this.labelPipe()(label);
99
+ }
100
+ /** Whether the section matches the active key. */
101
+ isSectionActive(section) {
102
+ return section.key === this.activeSectionKey();
103
+ }
104
+ /** Whether the item is the best route match for the active url. */
105
+ isItemActive(item) {
106
+ if (!item.routerLink)
107
+ return false;
108
+ return this.bestMatchItem() === item;
109
+ }
110
+ /** Whether one of the item's children matches the active url. */
111
+ isGroupActive(item) {
112
+ return (item.children?.some((c) => c.routerLink && this.matchesUrl(c.routerLink, this.activeUrl())) ?? false);
113
+ }
114
+ /** Exact or prefix match between a router link and the active url. */
115
+ matchesUrl(routerLink, url) {
116
+ return url === routerLink || url.startsWith(routerLink + '/');
117
+ }
118
+ /** Longest (most specific) item whose route matches the active url. */
119
+ bestMatchItem() {
120
+ const url = this.activeUrl();
121
+ let best = null;
122
+ for (const item of this.navItems()) {
123
+ if (item.routerLink && this.matchesUrl(item.routerLink, url)) {
124
+ if (!best || item.routerLink.length > best.routerLink.length) {
125
+ best = item;
126
+ }
127
+ }
128
+ for (const child of item.children ?? []) {
129
+ if (!child.routerLink || !this.matchesUrl(child.routerLink, url))
130
+ continue;
131
+ if (!best || child.routerLink.length > best.routerLink.length) {
132
+ best = child;
133
+ }
134
+ }
135
+ }
136
+ return best;
137
+ }
138
+ /** Whether a group is expanded. */
139
+ isGroupExpanded(item) {
140
+ return this.expandedGroups().has(item.id);
141
+ }
142
+ /** Toggles the expanded state of a group. */
143
+ toggleGroup(item) {
144
+ this.expandedGroups.update((set) => {
145
+ const next = new Set(set);
146
+ if (next.has(item.id)) {
147
+ next.delete(item.id);
148
+ }
149
+ else {
150
+ next.add(item.id);
151
+ }
152
+ return next;
153
+ });
154
+ }
155
+ /** Selects a section, emits `sectionSelected` and expands the rail when collapsed. */
156
+ onSectionClick(section) {
157
+ this.activeSectionKey.set(section.key);
158
+ this.sectionSelected.emit({ key: section.key, routerLink: section.routerLink });
159
+ if (this.collapsed()) {
160
+ this.collapsed.set(false);
161
+ this.toggleSidebar.emit(false);
162
+ }
163
+ }
164
+ /** Emits `itemClick` for a navigation item (skipping disabled items). */
165
+ onItemClick(item) {
166
+ if (item.disabled)
167
+ return;
168
+ this.itemClick.emit(item);
169
+ }
170
+ /** Emits `workspaceClick` and expands the sidebar if it was collapsed. */
171
+ onWorkspace() {
172
+ this.workspaceClick.emit();
173
+ if (this.collapsed()) {
174
+ this.collapsed.set(false);
175
+ this.toggleSidebar.emit(false);
176
+ }
177
+ }
178
+ /** Cycles to the next language and emits `setLang`. */
179
+ onToggleLang() {
180
+ const langs = this.langs();
181
+ if (langs.length === 0)
182
+ return;
183
+ const idx = langs.indexOf(this.activeLang());
184
+ const next = langs[(idx + 1) % langs.length] ?? langs[0];
185
+ this.setLang.emit(next);
186
+ }
187
+ /** Toggles collapse and emits `toggleSidebar`. */
188
+ onToggleSidebar() {
189
+ this.collapsed.update((v) => !v);
190
+ this.toggleSidebar.emit(this.collapsed());
191
+ }
192
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: RailSidebarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
193
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.18", type: RailSidebarComponent, isStandalone: true, selector: "shk-rail-sidebar", inputs: { sections: { classPropertyName: "sections", publicName: "sections", isSignal: true, isRequired: false, transformFunction: null }, activeUrl: { classPropertyName: "activeUrl", publicName: "activeUrl", isSignal: true, isRequired: false, transformFunction: null }, activeSectionKey: { classPropertyName: "activeSectionKey", publicName: "activeSectionKey", isSignal: true, isRequired: false, transformFunction: null }, collapsed: { classPropertyName: "collapsed", publicName: "collapsed", isSignal: true, isRequired: false, transformFunction: null }, activeLang: { classPropertyName: "activeLang", publicName: "activeLang", isSignal: true, isRequired: false, transformFunction: null }, langs: { classPropertyName: "langs", publicName: "langs", isSignal: true, isRequired: false, transformFunction: null }, logo: { classPropertyName: "logo", publicName: "logo", isSignal: true, isRequired: false, transformFunction: null }, appName: { classPropertyName: "appName", publicName: "appName", isSignal: true, isRequired: false, transformFunction: null }, userRole: { classPropertyName: "userRole", publicName: "userRole", isSignal: true, isRequired: false, transformFunction: null }, labelPipe: { classPropertyName: "labelPipe", publicName: "labelPipe", isSignal: true, isRequired: false, transformFunction: null }, railWidth: { classPropertyName: "railWidth", publicName: "railWidth", isSignal: true, isRequired: false, transformFunction: null }, expandedWidth: { classPropertyName: "expandedWidth", publicName: "expandedWidth", isSignal: true, isRequired: false, transformFunction: null }, workspaceTooltip: { classPropertyName: "workspaceTooltip", publicName: "workspaceTooltip", isSignal: true, isRequired: false, transformFunction: null }, logoutTooltip: { classPropertyName: "logoutTooltip", publicName: "logoutTooltip", isSignal: true, isRequired: false, transformFunction: null }, collapseTooltip: { classPropertyName: "collapseTooltip", publicName: "collapseTooltip", isSignal: true, isRequired: false, transformFunction: null }, expandTooltip: { classPropertyName: "expandTooltip", publicName: "expandTooltip", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { activeSectionKey: "activeSectionKeyChange", collapsed: "collapsedChange", sectionSelected: "sectionSelected", itemClick: "itemClick", setLang: "setLang", logoutRequested: "logoutRequested", toggleSidebar: "toggleSidebar", workspaceClick: "workspaceClick" }, ngImport: i0, template: "<aside\n class=\"h-full flex overflow-hidden shrink-0 _shk-bg-white dark:[background-color:var(--shk-surface-50)] border-r _shk-border dark:[border-color:var(--shk-surface-100)]\"\n [style.width]=\"width()\"\n>\n <!-- Rail: vertical icon column -->\n <div\n class=\"h-full flex flex-col items-center shrink-0 px-[0.3rem] py-2 gap-1 border-r _shk-border dark:[border-color:var(--shk-surface-100)]\"\n [style.width]=\"railWidth()\"\n >\n <!-- Workspace / logo -->\n <div class=\"flex items-center justify-center w-full\">\n <button\n type=\"button\"\n (click)=\"onWorkspace()\"\n [class.is-collapsed]=\"collapsed()\"\n class=\"rail-workspace w-[3.2rem] h-[3.2rem] rounded-xl _shk-bg-surface-100 dark:[background-color:var(--shk-surface-200)] border-none grid place-items-center p-0 relative transition-all\"\n [title]=\"workspaceTooltip()\"\n >\n @if (logo()) {\n <img [src]=\"logo()\" alt=\"Logo\" class=\"rail-app-icon w-8 h-8 object-contain\" />\n } @else {\n <svg [lucideIcon]=\"getIcon('menu')\" class=\"rail-app-icon w-6 h-6 _shk-text-surface-700 dark:[color:var(--shk-surface-700)]\"></svg>\n }\n @if (collapsed()) {\n <svg [lucideIcon]=\"getIcon('panel-right')\" class=\"rail-toggle-icon w-5 h-5 _shk-text-surface-700 dark:[color:var(--shk-surface-700)]\"></svg>\n }\n </button>\n </div>\n\n <!-- Section actions -->\n <div class=\"rail-actions flex-1 flex flex-col items-center gap-1 pt-1\">\n @for (section of sections(); track section.key) {\n <shk-tooltip [text]=\"label(section.label)\" position=\"right\" mode=\"fixed\">\n <button\n type=\"button\"\n (click)=\"onSectionClick(section)\"\n class=\"rail-icon w-[3.4rem] h-[3.4rem] rounded-[0.85rem] grid place-items-center transition-all cursor-pointer\"\n [class.is-active]=\"isSectionActive(section)\"\n [attr.aria-label]=\"label(section.label)\"\n >\n <svg [lucideIcon]=\"getIcon(section.icon)\" class=\"w-5 h-5\"></svg>\n </button>\n </shk-tooltip>\n }\n </div>\n\n <!-- Bottom: language + logout -->\n <div class=\"rail-bottom mt-auto flex flex-col items-center gap-1\">\n <button\n type=\"button\"\n (click)=\"onToggleLang()\"\n class=\"rail-icon w-[3.4rem] h-[3.4rem] rounded-[0.85rem] grid place-items-center transition-all cursor-pointer\"\n [title]=\"activeLang()\"\n >\n <div class=\"flex flex-col items-center gap-[0.15rem]\">\n <svg [lucideIcon]=\"getIcon('globe')\" class=\"w-5 h-5\"></svg>\n <span class=\"rail-lang-code text-[0.5rem] font-bold tracking-[0.05em] leading-none\">{{ activeLang() | uppercase }}</span>\n </div>\n </button>\n\n <button\n type=\"button\"\n (click)=\"logoutRequested.emit()\"\n class=\"rail-icon rail-logout w-[3.4rem] h-[3.4rem] rounded-[0.85rem] grid place-items-center transition-all cursor-pointer\"\n [title]=\"logoutTooltip()\"\n >\n <svg [lucideIcon]=\"getIcon('log-out')\" class=\"w-5 h-5\"></svg>\n </button>\n </div>\n </div>\n\n <!-- Nav: section panel (hidden when collapsed) -->\n @if (!collapsed()) {\n <div class=\"flex-1 min-w-0 flex flex-col _shk-bg-white dark:[background-color:var(--shk-surface-50)]\">\n <!-- Brand header -->\n <div class=\"flex items-center justify-between gap-2 px-3 py-4 border-b _shk-border dark:[border-color:var(--shk-surface-100)]\">\n <span class=\"nav-app text-base font-bold whitespace-nowrap overflow-hidden text-ellipsis _shk-text-surface-800 dark:[color:var(--shk-surface-800)]\">\n {{ appName() }}\n </span>\n <button\n type=\"button\"\n (click)=\"onToggleSidebar()\"\n class=\"nav-toggle w-[1.8rem] h-[1.8rem] rounded-lg border-none bg-transparent grid place-items-center cursor-pointer shrink-0 transition-colors _shk-text-surface-700 dark:[color:var(--shk-surface-700)]\"\n [title]=\"collapseTooltip()\"\n >\n <svg [lucideIcon]=\"getIcon('panel-right')\" class=\"w-4 h-4\"></svg>\n </button>\n </div>\n\n <!-- Menu -->\n <nav class=\"flex-1 overflow-y-auto _shk-scrollbar px-2 py-3\">\n @if (activeSection()) {\n <div class=\"px-2 pb-3\">\n <span class=\"nav-section-label text-xs font-bold tracking-[0.08em] uppercase _shk-text-surface-400 dark:[color:var(--shk-surface-500)] block whitespace-nowrap overflow-hidden text-ellipsis\">\n {{ label(activeSection()!.label) }}\n </span>\n </div>\n }\n\n <ul class=\"space-y-1\">\n @for (item of navItems(); track item.id) {\n @if (item.separator) {\n <li class=\"my-2 border-t _shk-border dark:[border-color:var(--shk-surface-200)]\"></li>\n }\n\n @if (item.children?.length) {\n <!-- Expandable group -->\n <li>\n <button\n type=\"button\"\n (click)=\"toggleGroup(item)\"\n class=\"w-full flex items-center gap-3 px-3 py-2.5 rounded-lg text-sm font-medium transition-all duration-200\"\n [class._shk-bg-primary]=\"isGroupActive(item)\"\n [class._shk-text-white]=\"isGroupActive(item)\"\n [class.shadow-md]=\"isGroupActive(item)\"\n [class._shk-text-surface-700]=\"!isGroupActive(item)\"\n [class.dark:[color:var(--shk-surface-600)]]=\"!isGroupActive(item)\"\n [class.hover:[background-color:var(--shk-surface-100)]]=\"!isGroupActive(item)\"\n [class.dark:hover:[background-color:var(--shk-surface-200)]]=\"!isGroupActive(item)\"\n >\n @if (item.icon) {\n <svg [lucideIcon]=\"getIcon(item.icon)\" class=\"w-5 h-5 shrink-0\"></svg>\n }\n <span class=\"flex-1 text-left whitespace-nowrap overflow-hidden\">{{ label(item.label) }}</span>\n <svg\n [lucideIcon]=\"getIcon('chevron-down')\"\n class=\"w-4 h-4 transition-transform duration-200 shrink-0\"\n [class.rotate-180]=\"isGroupExpanded(item)\"\n ></svg>\n </button>\n\n @if (isGroupExpanded(item)) {\n <ul class=\"ml-3 mt-1 space-y-1 border-l-2 _shk-border dark:[border-color:var(--shk-surface-200)] pl-2\">\n @for (child of item.children; track child.id) {\n <li>\n <button\n type=\"button\"\n (click)=\"onItemClick(child)\"\n class=\"w-full flex items-center gap-3 px-3 py-2 rounded-lg text-sm font-medium transition-all duration-200\"\n [class._shk-bg-primary-soft]=\"isItemActive(child)\"\n [class._shk-bg-primary-soft-dark]=\"isItemActive(child)\"\n [class._shk-text-primary-700]=\"isItemActive(child)\"\n [class.dark:[color:var(--shk-primary-300)]]=\"isItemActive(child)\"\n [class._shk-text-surface-600]=\"!isItemActive(child)\"\n [class.dark:[color:var(--shk-surface-400)]]=\"!isItemActive(child)\"\n [class.hover:[background-color:var(--shk-surface-100)]]=\"!isItemActive(child)\"\n [class.dark:hover:[background-color:var(--shk-surface-200)]]=\"!isItemActive(child)\"\n [class.hover:[color:var(--shk-surface-900)]]=\"!isItemActive(child)\"\n [class.dark:hover:[color:var(--shk-surface-700)]]=\"!isItemActive(child)\"\n >\n @if (child.icon) {\n <svg [lucideIcon]=\"getIcon(child.icon)\" class=\"w-4 h-4 shrink-0\"></svg>\n }\n <span class=\"whitespace-nowrap overflow-hidden\">{{ label(child.label) }}</span>\n @if (isItemActive(child)) {\n <span class=\"w-1.5 h-1.5 rounded-full _shk-bg-primary shrink-0 ml-auto\"></span>\n }\n </button>\n </li>\n }\n </ul>\n }\n </li>\n } @else {\n <!-- Standalone item -->\n <li>\n <button\n type=\"button\"\n (click)=\"onItemClick(item)\"\n class=\"w-full flex items-center gap-3 px-3 py-2.5 rounded-lg text-sm font-medium transition-all duration-200\"\n [class._shk-bg-primary]=\"isItemActive(item)\"\n [class._shk-text-white]=\"isItemActive(item)\"\n [class.shadow-md]=\"isItemActive(item)\"\n [class._shk-text-surface-700]=\"!isItemActive(item)\"\n [class.dark:[color:var(--shk-surface-600)]]=\"!isItemActive(item)\"\n [class.hover:[background-color:var(--shk-surface-100)]]=\"!isItemActive(item)\"\n [class.dark:hover:[background-color:var(--shk-surface-200)]]=\"!isItemActive(item)\"\n >\n @if (item.icon) {\n <svg [lucideIcon]=\"getIcon(item.icon)\" class=\"w-5 h-5 shrink-0\"></svg>\n }\n <span class=\"flex-1 text-left whitespace-nowrap overflow-hidden\">{{ label(item.label) }}</span>\n @if (item.badge !== undefined) {\n <span class=\"text-xs font-bold px-2 py-0.5 rounded-full _shk-bg-primary _shk-text-white shrink-0\">\n {{ item.badge }}\n </span>\n }\n </button>\n </li>\n }\n }\n </ul>\n </nav>\n </div>\n }\n</aside>\n", styles: [":host{display:block;height:100%}.rail-workspace{cursor:pointer;transition:opacity .2s ease,box-shadow .2s ease}.rail-workspace.is-collapsed:hover{cursor:col-resize}.rail-workspace .rail-app-icon,.rail-workspace .rail-toggle-icon{grid-area:1 / 1 / 2 / 2;transition:opacity .2s ease}.rail-toggle-icon,.rail-workspace.is-collapsed:hover .rail-app-icon{opacity:0}.rail-workspace.is-collapsed:hover .rail-toggle-icon{opacity:1}.rail-icon{background:transparent;color:var(--shk-surface-700);border:1px solid transparent;transition:transform .18s ease,color .18s ease,background-color .18s ease,box-shadow .18s ease}.rail-icon svg{stroke-width:1.8}.rail-icon:hover{background:var(--shk-surface-100);color:var(--shk-primary-600)}.dark .rail-icon:hover{background:var(--shk-surface-200);color:var(--shk-primary-400)}.rail-icon.is-active{background:var(--shk-primary-600);color:#fff;box-shadow:0 1px 2px #0003}.dark .rail-icon.is-active{background:var(--shk-primary-500);color:#fff}.rail-logout:hover{color:var(--shk-error-500)}.dark .rail-logout:hover{color:var(--shk-error-400)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: LucideDynamicIcon, selector: "svg[lucideIcon]", inputs: ["lucideIcon"] }, { kind: "component", type: TooltipComponent, selector: "shk-tooltip", inputs: ["text", "position", "mode", "offset"] }, { kind: "pipe", type: i1.UpperCasePipe, name: "uppercase" }] }); }
194
+ }
195
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: RailSidebarComponent, decorators: [{
196
+ type: Component,
197
+ args: [{ selector: 'shk-rail-sidebar', standalone: true, imports: [CommonModule, LucideDynamicIcon, TooltipComponent], template: "<aside\n class=\"h-full flex overflow-hidden shrink-0 _shk-bg-white dark:[background-color:var(--shk-surface-50)] border-r _shk-border dark:[border-color:var(--shk-surface-100)]\"\n [style.width]=\"width()\"\n>\n <!-- Rail: vertical icon column -->\n <div\n class=\"h-full flex flex-col items-center shrink-0 px-[0.3rem] py-2 gap-1 border-r _shk-border dark:[border-color:var(--shk-surface-100)]\"\n [style.width]=\"railWidth()\"\n >\n <!-- Workspace / logo -->\n <div class=\"flex items-center justify-center w-full\">\n <button\n type=\"button\"\n (click)=\"onWorkspace()\"\n [class.is-collapsed]=\"collapsed()\"\n class=\"rail-workspace w-[3.2rem] h-[3.2rem] rounded-xl _shk-bg-surface-100 dark:[background-color:var(--shk-surface-200)] border-none grid place-items-center p-0 relative transition-all\"\n [title]=\"workspaceTooltip()\"\n >\n @if (logo()) {\n <img [src]=\"logo()\" alt=\"Logo\" class=\"rail-app-icon w-8 h-8 object-contain\" />\n } @else {\n <svg [lucideIcon]=\"getIcon('menu')\" class=\"rail-app-icon w-6 h-6 _shk-text-surface-700 dark:[color:var(--shk-surface-700)]\"></svg>\n }\n @if (collapsed()) {\n <svg [lucideIcon]=\"getIcon('panel-right')\" class=\"rail-toggle-icon w-5 h-5 _shk-text-surface-700 dark:[color:var(--shk-surface-700)]\"></svg>\n }\n </button>\n </div>\n\n <!-- Section actions -->\n <div class=\"rail-actions flex-1 flex flex-col items-center gap-1 pt-1\">\n @for (section of sections(); track section.key) {\n <shk-tooltip [text]=\"label(section.label)\" position=\"right\" mode=\"fixed\">\n <button\n type=\"button\"\n (click)=\"onSectionClick(section)\"\n class=\"rail-icon w-[3.4rem] h-[3.4rem] rounded-[0.85rem] grid place-items-center transition-all cursor-pointer\"\n [class.is-active]=\"isSectionActive(section)\"\n [attr.aria-label]=\"label(section.label)\"\n >\n <svg [lucideIcon]=\"getIcon(section.icon)\" class=\"w-5 h-5\"></svg>\n </button>\n </shk-tooltip>\n }\n </div>\n\n <!-- Bottom: language + logout -->\n <div class=\"rail-bottom mt-auto flex flex-col items-center gap-1\">\n <button\n type=\"button\"\n (click)=\"onToggleLang()\"\n class=\"rail-icon w-[3.4rem] h-[3.4rem] rounded-[0.85rem] grid place-items-center transition-all cursor-pointer\"\n [title]=\"activeLang()\"\n >\n <div class=\"flex flex-col items-center gap-[0.15rem]\">\n <svg [lucideIcon]=\"getIcon('globe')\" class=\"w-5 h-5\"></svg>\n <span class=\"rail-lang-code text-[0.5rem] font-bold tracking-[0.05em] leading-none\">{{ activeLang() | uppercase }}</span>\n </div>\n </button>\n\n <button\n type=\"button\"\n (click)=\"logoutRequested.emit()\"\n class=\"rail-icon rail-logout w-[3.4rem] h-[3.4rem] rounded-[0.85rem] grid place-items-center transition-all cursor-pointer\"\n [title]=\"logoutTooltip()\"\n >\n <svg [lucideIcon]=\"getIcon('log-out')\" class=\"w-5 h-5\"></svg>\n </button>\n </div>\n </div>\n\n <!-- Nav: section panel (hidden when collapsed) -->\n @if (!collapsed()) {\n <div class=\"flex-1 min-w-0 flex flex-col _shk-bg-white dark:[background-color:var(--shk-surface-50)]\">\n <!-- Brand header -->\n <div class=\"flex items-center justify-between gap-2 px-3 py-4 border-b _shk-border dark:[border-color:var(--shk-surface-100)]\">\n <span class=\"nav-app text-base font-bold whitespace-nowrap overflow-hidden text-ellipsis _shk-text-surface-800 dark:[color:var(--shk-surface-800)]\">\n {{ appName() }}\n </span>\n <button\n type=\"button\"\n (click)=\"onToggleSidebar()\"\n class=\"nav-toggle w-[1.8rem] h-[1.8rem] rounded-lg border-none bg-transparent grid place-items-center cursor-pointer shrink-0 transition-colors _shk-text-surface-700 dark:[color:var(--shk-surface-700)]\"\n [title]=\"collapseTooltip()\"\n >\n <svg [lucideIcon]=\"getIcon('panel-right')\" class=\"w-4 h-4\"></svg>\n </button>\n </div>\n\n <!-- Menu -->\n <nav class=\"flex-1 overflow-y-auto _shk-scrollbar px-2 py-3\">\n @if (activeSection()) {\n <div class=\"px-2 pb-3\">\n <span class=\"nav-section-label text-xs font-bold tracking-[0.08em] uppercase _shk-text-surface-400 dark:[color:var(--shk-surface-500)] block whitespace-nowrap overflow-hidden text-ellipsis\">\n {{ label(activeSection()!.label) }}\n </span>\n </div>\n }\n\n <ul class=\"space-y-1\">\n @for (item of navItems(); track item.id) {\n @if (item.separator) {\n <li class=\"my-2 border-t _shk-border dark:[border-color:var(--shk-surface-200)]\"></li>\n }\n\n @if (item.children?.length) {\n <!-- Expandable group -->\n <li>\n <button\n type=\"button\"\n (click)=\"toggleGroup(item)\"\n class=\"w-full flex items-center gap-3 px-3 py-2.5 rounded-lg text-sm font-medium transition-all duration-200\"\n [class._shk-bg-primary]=\"isGroupActive(item)\"\n [class._shk-text-white]=\"isGroupActive(item)\"\n [class.shadow-md]=\"isGroupActive(item)\"\n [class._shk-text-surface-700]=\"!isGroupActive(item)\"\n [class.dark:[color:var(--shk-surface-600)]]=\"!isGroupActive(item)\"\n [class.hover:[background-color:var(--shk-surface-100)]]=\"!isGroupActive(item)\"\n [class.dark:hover:[background-color:var(--shk-surface-200)]]=\"!isGroupActive(item)\"\n >\n @if (item.icon) {\n <svg [lucideIcon]=\"getIcon(item.icon)\" class=\"w-5 h-5 shrink-0\"></svg>\n }\n <span class=\"flex-1 text-left whitespace-nowrap overflow-hidden\">{{ label(item.label) }}</span>\n <svg\n [lucideIcon]=\"getIcon('chevron-down')\"\n class=\"w-4 h-4 transition-transform duration-200 shrink-0\"\n [class.rotate-180]=\"isGroupExpanded(item)\"\n ></svg>\n </button>\n\n @if (isGroupExpanded(item)) {\n <ul class=\"ml-3 mt-1 space-y-1 border-l-2 _shk-border dark:[border-color:var(--shk-surface-200)] pl-2\">\n @for (child of item.children; track child.id) {\n <li>\n <button\n type=\"button\"\n (click)=\"onItemClick(child)\"\n class=\"w-full flex items-center gap-3 px-3 py-2 rounded-lg text-sm font-medium transition-all duration-200\"\n [class._shk-bg-primary-soft]=\"isItemActive(child)\"\n [class._shk-bg-primary-soft-dark]=\"isItemActive(child)\"\n [class._shk-text-primary-700]=\"isItemActive(child)\"\n [class.dark:[color:var(--shk-primary-300)]]=\"isItemActive(child)\"\n [class._shk-text-surface-600]=\"!isItemActive(child)\"\n [class.dark:[color:var(--shk-surface-400)]]=\"!isItemActive(child)\"\n [class.hover:[background-color:var(--shk-surface-100)]]=\"!isItemActive(child)\"\n [class.dark:hover:[background-color:var(--shk-surface-200)]]=\"!isItemActive(child)\"\n [class.hover:[color:var(--shk-surface-900)]]=\"!isItemActive(child)\"\n [class.dark:hover:[color:var(--shk-surface-700)]]=\"!isItemActive(child)\"\n >\n @if (child.icon) {\n <svg [lucideIcon]=\"getIcon(child.icon)\" class=\"w-4 h-4 shrink-0\"></svg>\n }\n <span class=\"whitespace-nowrap overflow-hidden\">{{ label(child.label) }}</span>\n @if (isItemActive(child)) {\n <span class=\"w-1.5 h-1.5 rounded-full _shk-bg-primary shrink-0 ml-auto\"></span>\n }\n </button>\n </li>\n }\n </ul>\n }\n </li>\n } @else {\n <!-- Standalone item -->\n <li>\n <button\n type=\"button\"\n (click)=\"onItemClick(item)\"\n class=\"w-full flex items-center gap-3 px-3 py-2.5 rounded-lg text-sm font-medium transition-all duration-200\"\n [class._shk-bg-primary]=\"isItemActive(item)\"\n [class._shk-text-white]=\"isItemActive(item)\"\n [class.shadow-md]=\"isItemActive(item)\"\n [class._shk-text-surface-700]=\"!isItemActive(item)\"\n [class.dark:[color:var(--shk-surface-600)]]=\"!isItemActive(item)\"\n [class.hover:[background-color:var(--shk-surface-100)]]=\"!isItemActive(item)\"\n [class.dark:hover:[background-color:var(--shk-surface-200)]]=\"!isItemActive(item)\"\n >\n @if (item.icon) {\n <svg [lucideIcon]=\"getIcon(item.icon)\" class=\"w-5 h-5 shrink-0\"></svg>\n }\n <span class=\"flex-1 text-left whitespace-nowrap overflow-hidden\">{{ label(item.label) }}</span>\n @if (item.badge !== undefined) {\n <span class=\"text-xs font-bold px-2 py-0.5 rounded-full _shk-bg-primary _shk-text-white shrink-0\">\n {{ item.badge }}\n </span>\n }\n </button>\n </li>\n }\n }\n </ul>\n </nav>\n </div>\n }\n</aside>\n", styles: [":host{display:block;height:100%}.rail-workspace{cursor:pointer;transition:opacity .2s ease,box-shadow .2s ease}.rail-workspace.is-collapsed:hover{cursor:col-resize}.rail-workspace .rail-app-icon,.rail-workspace .rail-toggle-icon{grid-area:1 / 1 / 2 / 2;transition:opacity .2s ease}.rail-toggle-icon,.rail-workspace.is-collapsed:hover .rail-app-icon{opacity:0}.rail-workspace.is-collapsed:hover .rail-toggle-icon{opacity:1}.rail-icon{background:transparent;color:var(--shk-surface-700);border:1px solid transparent;transition:transform .18s ease,color .18s ease,background-color .18s ease,box-shadow .18s ease}.rail-icon svg{stroke-width:1.8}.rail-icon:hover{background:var(--shk-surface-100);color:var(--shk-primary-600)}.dark .rail-icon:hover{background:var(--shk-surface-200);color:var(--shk-primary-400)}.rail-icon.is-active{background:var(--shk-primary-600);color:#fff;box-shadow:0 1px 2px #0003}.dark .rail-icon.is-active{background:var(--shk-primary-500);color:#fff}.rail-logout:hover{color:var(--shk-error-500)}.dark .rail-logout:hover{color:var(--shk-error-400)}\n"] }]
198
+ }], ctorParameters: () => [], propDecorators: { sections: [{ type: i0.Input, args: [{ isSignal: true, alias: "sections", required: false }] }], activeUrl: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeUrl", required: false }] }], activeSectionKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeSectionKey", required: false }] }, { type: i0.Output, args: ["activeSectionKeyChange"] }], collapsed: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsed", required: false }] }, { type: i0.Output, args: ["collapsedChange"] }], activeLang: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeLang", required: false }] }], langs: [{ type: i0.Input, args: [{ isSignal: true, alias: "langs", required: false }] }], logo: [{ type: i0.Input, args: [{ isSignal: true, alias: "logo", required: false }] }], appName: [{ type: i0.Input, args: [{ isSignal: true, alias: "appName", required: false }] }], userRole: [{ type: i0.Input, args: [{ isSignal: true, alias: "userRole", required: false }] }], labelPipe: [{ type: i0.Input, args: [{ isSignal: true, alias: "labelPipe", required: false }] }], railWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "railWidth", required: false }] }], expandedWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandedWidth", required: false }] }], workspaceTooltip: [{ type: i0.Input, args: [{ isSignal: true, alias: "workspaceTooltip", required: false }] }], logoutTooltip: [{ type: i0.Input, args: [{ isSignal: true, alias: "logoutTooltip", required: false }] }], collapseTooltip: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapseTooltip", required: false }] }], expandTooltip: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandTooltip", required: false }] }], sectionSelected: [{ type: i0.Output, args: ["sectionSelected"] }], itemClick: [{ type: i0.Output, args: ["itemClick"] }], setLang: [{ type: i0.Output, args: ["setLang"] }], logoutRequested: [{ type: i0.Output, args: ["logoutRequested"] }], toggleSidebar: [{ type: i0.Output, args: ["toggleSidebar"] }], workspaceClick: [{ type: i0.Output, args: ["workspaceClick"] }] } });
199
+
200
+ /**
201
+ * Generated bundle index. Do not edit.
202
+ */
203
+
204
+ export { RailSidebarComponent };
205
+ //# sourceMappingURL=shirkasoft-ui-components-rail-sidebar.mjs.map