@xosue-utils/helpers 0.2.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 ADDED
@@ -0,0 +1,24 @@
1
+ # @xosue-utils/helpers
2
+
3
+ Helpers UI Angular 19 standalone (paginación, empty/error states, headers) con estética **Cupertino**.
4
+
5
+ Peer: `@xosue-utils/actions` (text-btn, lucide-icon).
6
+
7
+ ## Componentes
8
+
9
+ | Selector | Uso |
10
+ |----------|-----|
11
+ | `smart-pagination` | Paginación por query params |
12
+ | `empty-state` | Estado vacío con acciones |
13
+ | `load-error` | Error de carga + retry |
14
+ | `section-header` | Título + trailing actions |
15
+ | `key-value-list` | Filas label/value |
16
+
17
+ ## Build
18
+
19
+ ```bash
20
+ cd D:\apps\xosue-utils\actions && npm run build
21
+ cd ../helpers
22
+ npm install
23
+ npm run build
24
+ ```
@@ -0,0 +1,372 @@
1
+ import * as i0 from '@angular/core';
2
+ import { inject, Injector, signal, computed, effect, Input, Component, input, output } from '@angular/core';
3
+ import { Router, ActivatedRoute } from '@angular/router';
4
+ import { CommonModule } from '@angular/common';
5
+ import { Subscription } from 'rxjs';
6
+ import { LucideIconComponent, TextBtnComponent } from '@xosue-utils/actions';
7
+
8
+ class SmartPaginationComponent {
9
+ router = inject(Router);
10
+ route = inject(ActivatedRoute);
11
+ injector = inject(Injector);
12
+ resetOnChanges = false;
13
+ paramName = 'page';
14
+ /** Label when total === 0. */
15
+ emptyLabel = 'No hay resultados.';
16
+ /** Label when last_page === 1. */
17
+ singlePageLabel = 'Página única';
18
+ set data(value) {
19
+ if (value) {
20
+ this.paginationData.set(value);
21
+ }
22
+ else {
23
+ this.paginationData.set({
24
+ total: 0,
25
+ per_page: 10,
26
+ current_page: 1,
27
+ last_page: 1,
28
+ from: 0,
29
+ to: 0,
30
+ });
31
+ }
32
+ }
33
+ paginationData = signal({
34
+ total: 0,
35
+ per_page: 10,
36
+ current_page: 1,
37
+ last_page: 1,
38
+ from: 0,
39
+ to: 0,
40
+ });
41
+ currentPage = computed(() => this.paginationData().current_page);
42
+ lastPage = computed(() => this.paginationData().last_page);
43
+ pagesToDisplay = computed(() => {
44
+ const current = this.currentPage();
45
+ const last = this.lastPage();
46
+ const pages = [];
47
+ if (last <= 5) {
48
+ for (let i = 1; i <= last; i++)
49
+ pages.push(i);
50
+ return pages;
51
+ }
52
+ pages.push(1);
53
+ if (current <= 4) {
54
+ for (let i = 2; i <= 5; i++)
55
+ pages.push(i);
56
+ pages.push('...');
57
+ }
58
+ else if (current >= last - 3) {
59
+ pages.push('...');
60
+ for (let i = last - 4; i < last; i++)
61
+ pages.push(i);
62
+ }
63
+ else {
64
+ pages.push('...');
65
+ pages.push(current - 1, current, current + 1);
66
+ pages.push('...');
67
+ }
68
+ pages.push(last);
69
+ return pages;
70
+ });
71
+ queryParamsSub = Subscription.EMPTY;
72
+ queryParams = signal({});
73
+ isPage(p) {
74
+ return typeof p === 'number';
75
+ }
76
+ goToPage(page) {
77
+ this.router.navigate([], {
78
+ relativeTo: this.route,
79
+ queryParams: { [this.paramName]: page },
80
+ queryParamsHandling: 'merge',
81
+ });
82
+ }
83
+ generateHref(page) {
84
+ const base = this.router.url.split('?')[0];
85
+ const params = new URLSearchParams({
86
+ ...this.queryParams(),
87
+ [this.paramName]: String(page),
88
+ });
89
+ return `${base}?${params}`;
90
+ }
91
+ updateFilter(params) {
92
+ const queryParams = {};
93
+ for (const key in params) {
94
+ queryParams[key] = params[key] != null ? String(params[key]) : null;
95
+ }
96
+ this.router.navigate([], {
97
+ relativeTo: this.route,
98
+ queryParams,
99
+ queryParamsHandling: 'merge',
100
+ });
101
+ }
102
+ ngOnInit() {
103
+ const queryPage = +(this.route.snapshot.queryParamMap.get(this.paramName) || '1');
104
+ if (!queryPage || queryPage < 1) {
105
+ this.goToPage(1);
106
+ }
107
+ this.queryParamsSub = this.route.queryParamMap.subscribe((params) => {
108
+ const newParams = {};
109
+ params.keys.forEach((key) => {
110
+ if (key !== this.paramName) {
111
+ newParams[key] = params.get(key) ?? '';
112
+ }
113
+ });
114
+ this.queryParams.set(newParams);
115
+ const page = +(params.get(this.paramName) || '1');
116
+ if (!page || page < 1) {
117
+ this.goToPage(1);
118
+ }
119
+ });
120
+ effect(() => {
121
+ const pag = this.paginationData();
122
+ if (!pag)
123
+ return;
124
+ const currentParam = +(this.route.snapshot.queryParamMap.get(this.paramName) || '1');
125
+ if (this.resetOnChanges) {
126
+ if (currentParam !== 1) {
127
+ this.goToPage(1);
128
+ }
129
+ return;
130
+ }
131
+ if (pag.last_page && currentParam > pag.last_page && pag.last_page >= 1) {
132
+ this.goToPage(pag.last_page);
133
+ }
134
+ }, { injector: this.injector });
135
+ }
136
+ ngOnDestroy() {
137
+ this.queryParamsSub.unsubscribe();
138
+ }
139
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: SmartPaginationComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
140
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.11", type: SmartPaginationComponent, isStandalone: true, selector: "smart-pagination", inputs: { resetOnChanges: "resetOnChanges", paramName: "paramName", emptyLabel: "emptyLabel", singlePageLabel: "singlePageLabel", data: "data" }, ngImport: i0, template: "@if (paginationData() && paginationData().total === 0) {\r\n <p class=\"NonResults\">{{ emptyLabel }}</p>\r\n} @else if (paginationData() && paginationData().last_page > 1) {\r\n <nav class=\"pagination\" aria-label=\"Paginaci\u00F3n\">\r\n @if (currentPage() > 1) {\r\n <button type=\"button\" (click)=\"goToPage(currentPage() - 1)\" aria-label=\"Anterior\">\r\n <lucide-icon name=\"chevron-left\" [size]=\"16\"></lucide-icon>\r\n </button>\r\n }\r\n\r\n @for (p of pagesToDisplay(); track $index) {\r\n @if (isPage(p)) {\r\n <a\r\n [class.active]=\"p === currentPage()\"\r\n [href]=\"generateHref(p)\"\r\n (click)=\"goToPage(p); $event.preventDefault()\"\r\n >\r\n {{ p }}\r\n </a>\r\n } @else {\r\n <span class=\"ellipsis\">\u2026</span>\r\n }\r\n }\r\n\r\n @if (currentPage() < lastPage()) {\r\n <button type=\"button\" (click)=\"goToPage(currentPage() + 1)\" aria-label=\"Siguiente\">\r\n <lucide-icon name=\"chevron-right\" [size]=\"16\"></lucide-icon>\r\n </button>\r\n }\r\n </nav>\r\n} @else {\r\n <p class=\"UniquePage\">{{ singlePageLabel }}</p>\r\n}\r\n", styles: [":host{width:max-content;height:32px;display:flex}.pagination{width:max-content;max-width:280px;height:32px;display:flex;align-items:center;gap:4px;background-color:var(--surface-2, var(--surface));border-radius:var(--radius-lg, 12px);padding:2px;border:1px solid var(--border)}.pagination a,.pagination button{min-width:28px;height:28px;padding:2px 8px;font-size:.8125rem;font-weight:500;cursor:pointer;border:none;background-color:transparent;color:var(--text-muted);border-radius:var(--radius-md, 8px);display:flex;justify-content:center;align-items:center;text-decoration:none;transition:background-color .15s ease,color .15s ease,transform .1s ease}.pagination a:hover,.pagination button:hover{background-color:color-mix(in srgb,var(--surface) 80%,var(--border));color:var(--text-base)}.pagination a:active,.pagination button:active{transform:scale(.98)}.pagination a.active,.pagination button.active{background-color:var(--surface);color:var(--text-base);font-weight:700;box-shadow:0 1px 2px color-mix(in srgb,var(--text-base) 12%,transparent)}.pagination .ellipsis{display:flex;align-items:center;justify-content:center;width:max-content;padding:0 2px;color:var(--text-muted)}.NonResults,.UniquePage{width:max-content;max-width:280px;height:32px;display:flex;align-items:center;gap:4px;background-color:var(--surface-2, var(--surface));border-radius:var(--radius-lg, 12px);padding:4px 8px;border:1px solid var(--border);color:var(--text-muted);margin:0;font-size:.8125rem}\n"], dependencies: [{ kind: "component", type: LucideIconComponent, selector: "lucide-icon", inputs: ["name", "size", "spin", "filled"] }, { kind: "ngmodule", type: CommonModule }] });
141
+ }
142
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: SmartPaginationComponent, decorators: [{
143
+ type: Component,
144
+ args: [{ selector: 'smart-pagination', standalone: true, imports: [LucideIconComponent, CommonModule], template: "@if (paginationData() && paginationData().total === 0) {\r\n <p class=\"NonResults\">{{ emptyLabel }}</p>\r\n} @else if (paginationData() && paginationData().last_page > 1) {\r\n <nav class=\"pagination\" aria-label=\"Paginaci\u00F3n\">\r\n @if (currentPage() > 1) {\r\n <button type=\"button\" (click)=\"goToPage(currentPage() - 1)\" aria-label=\"Anterior\">\r\n <lucide-icon name=\"chevron-left\" [size]=\"16\"></lucide-icon>\r\n </button>\r\n }\r\n\r\n @for (p of pagesToDisplay(); track $index) {\r\n @if (isPage(p)) {\r\n <a\r\n [class.active]=\"p === currentPage()\"\r\n [href]=\"generateHref(p)\"\r\n (click)=\"goToPage(p); $event.preventDefault()\"\r\n >\r\n {{ p }}\r\n </a>\r\n } @else {\r\n <span class=\"ellipsis\">\u2026</span>\r\n }\r\n }\r\n\r\n @if (currentPage() < lastPage()) {\r\n <button type=\"button\" (click)=\"goToPage(currentPage() + 1)\" aria-label=\"Siguiente\">\r\n <lucide-icon name=\"chevron-right\" [size]=\"16\"></lucide-icon>\r\n </button>\r\n }\r\n </nav>\r\n} @else {\r\n <p class=\"UniquePage\">{{ singlePageLabel }}</p>\r\n}\r\n", styles: [":host{width:max-content;height:32px;display:flex}.pagination{width:max-content;max-width:280px;height:32px;display:flex;align-items:center;gap:4px;background-color:var(--surface-2, var(--surface));border-radius:var(--radius-lg, 12px);padding:2px;border:1px solid var(--border)}.pagination a,.pagination button{min-width:28px;height:28px;padding:2px 8px;font-size:.8125rem;font-weight:500;cursor:pointer;border:none;background-color:transparent;color:var(--text-muted);border-radius:var(--radius-md, 8px);display:flex;justify-content:center;align-items:center;text-decoration:none;transition:background-color .15s ease,color .15s ease,transform .1s ease}.pagination a:hover,.pagination button:hover{background-color:color-mix(in srgb,var(--surface) 80%,var(--border));color:var(--text-base)}.pagination a:active,.pagination button:active{transform:scale(.98)}.pagination a.active,.pagination button.active{background-color:var(--surface);color:var(--text-base);font-weight:700;box-shadow:0 1px 2px color-mix(in srgb,var(--text-base) 12%,transparent)}.pagination .ellipsis{display:flex;align-items:center;justify-content:center;width:max-content;padding:0 2px;color:var(--text-muted)}.NonResults,.UniquePage{width:max-content;max-width:280px;height:32px;display:flex;align-items:center;gap:4px;background-color:var(--surface-2, var(--surface));border-radius:var(--radius-lg, 12px);padding:4px 8px;border:1px solid var(--border);color:var(--text-muted);margin:0;font-size:.8125rem}\n"] }]
145
+ }], propDecorators: { resetOnChanges: [{
146
+ type: Input
147
+ }], paramName: [{
148
+ type: Input
149
+ }], emptyLabel: [{
150
+ type: Input
151
+ }], singlePageLabel: [{
152
+ type: Input
153
+ }], data: [{
154
+ type: Input
155
+ }] } });
156
+
157
+ class EmptyStateComponent {
158
+ title = input('Sin datos');
159
+ message = input(null);
160
+ icon = input('inbox');
161
+ primaryLabel = input(null);
162
+ primaryIcon = input(null);
163
+ secondaryLabel = input(null);
164
+ primary = output();
165
+ secondary = output();
166
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: EmptyStateComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
167
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.11", type: EmptyStateComponent, isStandalone: true, selector: "empty-state", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, message: { classPropertyName: "message", publicName: "message", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, primaryLabel: { classPropertyName: "primaryLabel", publicName: "primaryLabel", isSignal: true, isRequired: false, transformFunction: null }, primaryIcon: { classPropertyName: "primaryIcon", publicName: "primaryIcon", isSignal: true, isRequired: false, transformFunction: null }, secondaryLabel: { classPropertyName: "secondaryLabel", publicName: "secondaryLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { primary: "primary", secondary: "secondary" }, ngImport: i0, template: `
168
+ <div class="EmptyState">
169
+ @if (icon()) {
170
+ <div class="EmptyState__Icon">
171
+ <lucide-icon [name]="icon()!" [size]="36"></lucide-icon>
172
+ </div>
173
+ }
174
+ <h3 class="EmptyState__Title">{{ title() }}</h3>
175
+ @if (message()) {
176
+ <p class="EmptyState__Message">{{ message() }}</p>
177
+ }
178
+ @if (primaryLabel() || secondaryLabel()) {
179
+ <div class="EmptyState__Actions">
180
+ @if (secondaryLabel()) {
181
+ <text-btn
182
+ [label]="secondaryLabel()!"
183
+ color="cancel"
184
+ variant="ghost"
185
+ (clicked)="secondary.emit($event)"
186
+ ></text-btn>
187
+ }
188
+ @if (primaryLabel()) {
189
+ <text-btn
190
+ [label]="primaryLabel()!"
191
+ [icon]="primaryIcon()"
192
+ color="primary"
193
+ (clicked)="primary.emit($event)"
194
+ ></text-btn>
195
+ }
196
+ </div>
197
+ }
198
+ <ng-content></ng-content>
199
+ </div>
200
+ `, isInline: true, styles: [":host{display:block;width:100%}.EmptyState{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;padding:16px;text-align:center;border-radius:var(--radius-xl, 16px);background:var(--surface-2, var(--surface));border:1px solid var(--border)}.EmptyState__Icon{display:flex;color:var(--text-muted)}.EmptyState__Icon lucide-icon{--lucide-stroke: var(--text-muted)}.EmptyState__Title{margin:0;font-size:1rem;font-weight:600;color:var(--text-base)}.EmptyState__Message{margin:0;max-width:28rem;font-size:.875rem;line-height:1.45;color:var(--text-muted)}.EmptyState__Actions{display:flex;flex-wrap:wrap;align-items:center;justify-content:center;gap:4px;margin-top:4px}\n"], dependencies: [{ kind: "component", type: LucideIconComponent, selector: "lucide-icon", inputs: ["name", "size", "spin", "filled"] }, { kind: "component", type: TextBtnComponent, selector: "text-btn", inputs: ["label", "icon", "iconPosition", "iconSpin", "layout", "variant", "color", "rounded", "size", "ripple", "disabled", "buttonType", "href", "routerLink", "fit", "width", "textColor", "bgColor", "borderColor", "title"], outputs: ["clicked"] }] });
201
+ }
202
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: EmptyStateComponent, decorators: [{
203
+ type: Component,
204
+ args: [{ selector: 'empty-state', standalone: true, imports: [LucideIconComponent, TextBtnComponent], template: `
205
+ <div class="EmptyState">
206
+ @if (icon()) {
207
+ <div class="EmptyState__Icon">
208
+ <lucide-icon [name]="icon()!" [size]="36"></lucide-icon>
209
+ </div>
210
+ }
211
+ <h3 class="EmptyState__Title">{{ title() }}</h3>
212
+ @if (message()) {
213
+ <p class="EmptyState__Message">{{ message() }}</p>
214
+ }
215
+ @if (primaryLabel() || secondaryLabel()) {
216
+ <div class="EmptyState__Actions">
217
+ @if (secondaryLabel()) {
218
+ <text-btn
219
+ [label]="secondaryLabel()!"
220
+ color="cancel"
221
+ variant="ghost"
222
+ (clicked)="secondary.emit($event)"
223
+ ></text-btn>
224
+ }
225
+ @if (primaryLabel()) {
226
+ <text-btn
227
+ [label]="primaryLabel()!"
228
+ [icon]="primaryIcon()"
229
+ color="primary"
230
+ (clicked)="primary.emit($event)"
231
+ ></text-btn>
232
+ }
233
+ </div>
234
+ }
235
+ <ng-content></ng-content>
236
+ </div>
237
+ `, styles: [":host{display:block;width:100%}.EmptyState{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;padding:16px;text-align:center;border-radius:var(--radius-xl, 16px);background:var(--surface-2, var(--surface));border:1px solid var(--border)}.EmptyState__Icon{display:flex;color:var(--text-muted)}.EmptyState__Icon lucide-icon{--lucide-stroke: var(--text-muted)}.EmptyState__Title{margin:0;font-size:1rem;font-weight:600;color:var(--text-base)}.EmptyState__Message{margin:0;max-width:28rem;font-size:.875rem;line-height:1.45;color:var(--text-muted)}.EmptyState__Actions{display:flex;flex-wrap:wrap;align-items:center;justify-content:center;gap:4px;margin-top:4px}\n"] }]
238
+ }] });
239
+
240
+ class LoadErrorComponent {
241
+ title = input('No se pudo cargar');
242
+ message = input('Revisá tu conexión e intentá de nuevo.');
243
+ icon = input('triangle-alert');
244
+ retryLabel = input('Reintentar');
245
+ retry = output();
246
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: LoadErrorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
247
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.11", type: LoadErrorComponent, isStandalone: true, selector: "load-error", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, message: { classPropertyName: "message", publicName: "message", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, retryLabel: { classPropertyName: "retryLabel", publicName: "retryLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { retry: "retry" }, ngImport: i0, template: `
248
+ <div class="LoadError">
249
+ <div class="LoadError__Icon">
250
+ <lucide-icon [name]="icon()" [size]="36"></lucide-icon>
251
+ </div>
252
+ <h3 class="LoadError__Title">{{ title() }}</h3>
253
+ @if (message()) {
254
+ <p class="LoadError__Message">{{ message() }}</p>
255
+ }
256
+ <div class="LoadError__Actions">
257
+ <text-btn
258
+ [label]="retryLabel()"
259
+ icon="refresh-cw"
260
+ color="primary"
261
+ (clicked)="retry.emit($event)"
262
+ ></text-btn>
263
+ </div>
264
+ <ng-content></ng-content>
265
+ </div>
266
+ `, isInline: true, styles: [":host{display:block;width:100%}.LoadError{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;padding:16px;text-align:center;border-radius:var(--radius-xl, 16px);background:color-mix(in srgb,var(--error-bg, var(--surface)) 70%,var(--surface));border:1px solid var(--error-border, var(--border))}.LoadError__Icon{display:flex}.LoadError__Icon lucide-icon{--lucide-stroke: var(--error-text, var(--text-base))}.LoadError__Title{margin:0;font-size:1rem;font-weight:600;color:var(--text-base)}.LoadError__Message{margin:0;max-width:28rem;font-size:.875rem;line-height:1.45;color:var(--text-muted)}.LoadError__Actions{display:flex;margin-top:4px}\n"], dependencies: [{ kind: "component", type: LucideIconComponent, selector: "lucide-icon", inputs: ["name", "size", "spin", "filled"] }, { kind: "component", type: TextBtnComponent, selector: "text-btn", inputs: ["label", "icon", "iconPosition", "iconSpin", "layout", "variant", "color", "rounded", "size", "ripple", "disabled", "buttonType", "href", "routerLink", "fit", "width", "textColor", "bgColor", "borderColor", "title"], outputs: ["clicked"] }] });
267
+ }
268
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: LoadErrorComponent, decorators: [{
269
+ type: Component,
270
+ args: [{ selector: 'load-error', standalone: true, imports: [LucideIconComponent, TextBtnComponent], template: `
271
+ <div class="LoadError">
272
+ <div class="LoadError__Icon">
273
+ <lucide-icon [name]="icon()" [size]="36"></lucide-icon>
274
+ </div>
275
+ <h3 class="LoadError__Title">{{ title() }}</h3>
276
+ @if (message()) {
277
+ <p class="LoadError__Message">{{ message() }}</p>
278
+ }
279
+ <div class="LoadError__Actions">
280
+ <text-btn
281
+ [label]="retryLabel()"
282
+ icon="refresh-cw"
283
+ color="primary"
284
+ (clicked)="retry.emit($event)"
285
+ ></text-btn>
286
+ </div>
287
+ <ng-content></ng-content>
288
+ </div>
289
+ `, styles: [":host{display:block;width:100%}.LoadError{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;padding:16px;text-align:center;border-radius:var(--radius-xl, 16px);background:color-mix(in srgb,var(--error-bg, var(--surface)) 70%,var(--surface));border:1px solid var(--error-border, var(--border))}.LoadError__Icon{display:flex}.LoadError__Icon lucide-icon{--lucide-stroke: var(--error-text, var(--text-base))}.LoadError__Title{margin:0;font-size:1rem;font-weight:600;color:var(--text-base)}.LoadError__Message{margin:0;max-width:28rem;font-size:.875rem;line-height:1.45;color:var(--text-muted)}.LoadError__Actions{display:flex;margin-top:4px}\n"] }]
290
+ }] });
291
+
292
+ class SectionHeaderComponent {
293
+ title = input.required();
294
+ subtitle = input(null);
295
+ eyebrow = input(null);
296
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: SectionHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
297
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.11", type: SectionHeaderComponent, isStandalone: true, selector: "section-header", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: true, transformFunction: null }, subtitle: { classPropertyName: "subtitle", publicName: "subtitle", isSignal: true, isRequired: false, transformFunction: null }, eyebrow: { classPropertyName: "eyebrow", publicName: "eyebrow", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
298
+ <header class="SectionHeader">
299
+ <div class="SectionHeader__Text">
300
+ @if (eyebrow()) {
301
+ <p class="SectionHeader__Eyebrow">{{ eyebrow() }}</p>
302
+ }
303
+ <h2 class="SectionHeader__Title">{{ title() }}</h2>
304
+ @if (subtitle()) {
305
+ <p class="SectionHeader__Subtitle">{{ subtitle() }}</p>
306
+ }
307
+ </div>
308
+ <div class="SectionHeader__Actions">
309
+ <ng-content></ng-content>
310
+ </div>
311
+ </header>
312
+ `, isInline: true, styles: [":host{display:block;width:100%}.SectionHeader{display:flex;align-items:flex-start;justify-content:space-between;gap:8px;width:100%}.SectionHeader__Text{display:flex;flex-direction:column;gap:4px;min-width:0}.SectionHeader__Eyebrow{margin:0;font-size:.7rem;font-weight:600;letter-spacing:.04em;text-transform:uppercase;color:var(--text-muted)}.SectionHeader__Title{margin:0;font-size:1.125rem;font-weight:600;color:var(--text-base);line-height:1.25}.SectionHeader__Subtitle{margin:0;font-size:.875rem;color:var(--text-muted);line-height:1.4}.SectionHeader__Actions{display:flex;flex-wrap:wrap;align-items:center;justify-content:flex-end;gap:4px;flex-shrink:0}\n"] });
313
+ }
314
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: SectionHeaderComponent, decorators: [{
315
+ type: Component,
316
+ args: [{ selector: 'section-header', standalone: true, template: `
317
+ <header class="SectionHeader">
318
+ <div class="SectionHeader__Text">
319
+ @if (eyebrow()) {
320
+ <p class="SectionHeader__Eyebrow">{{ eyebrow() }}</p>
321
+ }
322
+ <h2 class="SectionHeader__Title">{{ title() }}</h2>
323
+ @if (subtitle()) {
324
+ <p class="SectionHeader__Subtitle">{{ subtitle() }}</p>
325
+ }
326
+ </div>
327
+ <div class="SectionHeader__Actions">
328
+ <ng-content></ng-content>
329
+ </div>
330
+ </header>
331
+ `, styles: [":host{display:block;width:100%}.SectionHeader{display:flex;align-items:flex-start;justify-content:space-between;gap:8px;width:100%}.SectionHeader__Text{display:flex;flex-direction:column;gap:4px;min-width:0}.SectionHeader__Eyebrow{margin:0;font-size:.7rem;font-weight:600;letter-spacing:.04em;text-transform:uppercase;color:var(--text-muted)}.SectionHeader__Title{margin:0;font-size:1.125rem;font-weight:600;color:var(--text-base);line-height:1.25}.SectionHeader__Subtitle{margin:0;font-size:.875rem;color:var(--text-muted);line-height:1.4}.SectionHeader__Actions{display:flex;flex-wrap:wrap;align-items:center;justify-content:flex-end;gap:4px;flex-shrink:0}\n"] }]
332
+ }] });
333
+
334
+ class KeyValueListComponent {
335
+ items = input([]);
336
+ emptyValue = input('—');
337
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: KeyValueListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
338
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.11", type: KeyValueListComponent, isStandalone: true, selector: "key-value-list", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, emptyValue: { classPropertyName: "emptyValue", publicName: "emptyValue", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
339
+ <dl class="KvList">
340
+ @for (item of items(); track item.label + '-' + $index) {
341
+ <div class="KvList__Row">
342
+ <dt class="KvList__Label">{{ item.label }}</dt>
343
+ <dd class="KvList__Value">{{ item.value ?? emptyValue() }}</dd>
344
+ </div>
345
+ }
346
+ </dl>
347
+ `, isInline: true, styles: [":host{display:block;width:100%}.KvList{margin:0;display:flex;flex-direction:column;gap:0;border:1px solid var(--border);border-radius:var(--radius-lg, 12px);overflow:hidden;background:var(--surface)}.KvList__Row{display:grid;grid-template-columns:minmax(6rem,40%) 1fr;gap:8px;padding:8px 12px;border-bottom:1px solid var(--border)}.KvList__Row:last-child{border-bottom:none}.KvList__Label{margin:0;font-size:.8125rem;color:var(--text-muted);font-weight:500}.KvList__Value{margin:0;font-size:.875rem;color:var(--text-base);word-break:break-word}\n"] });
348
+ }
349
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.11", ngImport: i0, type: KeyValueListComponent, decorators: [{
350
+ type: Component,
351
+ args: [{ selector: 'key-value-list', standalone: true, template: `
352
+ <dl class="KvList">
353
+ @for (item of items(); track item.label + '-' + $index) {
354
+ <div class="KvList__Row">
355
+ <dt class="KvList__Label">{{ item.label }}</dt>
356
+ <dd class="KvList__Value">{{ item.value ?? emptyValue() }}</dd>
357
+ </div>
358
+ }
359
+ </dl>
360
+ `, styles: [":host{display:block;width:100%}.KvList{margin:0;display:flex;flex-direction:column;gap:0;border:1px solid var(--border);border-radius:var(--radius-lg, 12px);overflow:hidden;background:var(--surface)}.KvList__Row{display:grid;grid-template-columns:minmax(6rem,40%) 1fr;gap:8px;padding:8px 12px;border-bottom:1px solid var(--border)}.KvList__Row:last-child{border-bottom:none}.KvList__Label{margin:0;font-size:.8125rem;color:var(--text-muted);font-weight:500}.KvList__Value{margin:0;font-size:.875rem;color:var(--text-base);word-break:break-word}\n"] }]
361
+ }] });
362
+
363
+ /*
364
+ * Public API Surface of @xosue-utils/helpers
365
+ */
366
+
367
+ /**
368
+ * Generated bundle index. Do not edit.
369
+ */
370
+
371
+ export { EmptyStateComponent, KeyValueListComponent, LoadErrorComponent, SectionHeaderComponent, SmartPaginationComponent };
372
+ //# sourceMappingURL=xosue-utils-helpers.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"xosue-utils-helpers.mjs","sources":["../../src/lib/smart-pagination/smart-pagination.component.ts","../../src/lib/smart-pagination/smart-pagination.component.html","../../src/lib/empty-state/empty-state.component.ts","../../src/lib/load-error/load-error.component.ts","../../src/lib/section-header/section-header.component.ts","../../src/lib/key-value-list/key-value-list.component.ts","../../src/public-api.ts","../../src/xosue-utils-helpers.ts"],"sourcesContent":["import {\r\n Component,\r\n Input,\r\n signal,\r\n effect,\r\n computed,\r\n inject,\r\n OnInit,\r\n OnDestroy,\r\n Injector,\r\n} from '@angular/core';\r\nimport { Router, ActivatedRoute } from '@angular/router';\r\nimport { CommonModule } from '@angular/common';\r\nimport { Subscription } from 'rxjs';\r\nimport { LucideIconComponent } from '@xosue-utils/actions';\r\n\r\nexport interface PaginationData {\r\n total: number;\r\n per_page: number;\r\n current_page: number;\r\n last_page: number;\r\n from: number;\r\n to: number;\r\n}\r\n\r\n@Component({\r\n selector: 'smart-pagination',\r\n standalone: true,\r\n imports: [LucideIconComponent, CommonModule],\r\n templateUrl: './smart-pagination.component.html',\r\n styleUrl: './smart-pagination.component.sass',\r\n})\r\nexport class SmartPaginationComponent implements OnInit, OnDestroy {\r\n private router = inject(Router);\r\n private route = inject(ActivatedRoute);\r\n private injector = inject(Injector);\r\n\r\n @Input() resetOnChanges = false;\r\n @Input() paramName = 'page';\r\n /** Label when total === 0. */\r\n @Input() emptyLabel = 'No hay resultados.';\r\n /** Label when last_page === 1. */\r\n @Input() singlePageLabel = 'Página única';\r\n\r\n @Input() set data(value: PaginationData | null | undefined) {\r\n if (value) {\r\n this.paginationData.set(value);\r\n } else {\r\n this.paginationData.set({\r\n total: 0,\r\n per_page: 10,\r\n current_page: 1,\r\n last_page: 1,\r\n from: 0,\r\n to: 0,\r\n });\r\n }\r\n }\r\n\r\n paginationData = signal<PaginationData>({\r\n total: 0,\r\n per_page: 10,\r\n current_page: 1,\r\n last_page: 1,\r\n from: 0,\r\n to: 0,\r\n });\r\n\r\n readonly currentPage = computed(() => this.paginationData().current_page);\r\n readonly lastPage = computed(() => this.paginationData().last_page);\r\n\r\n readonly pagesToDisplay = computed(() => {\r\n const current = this.currentPage();\r\n const last = this.lastPage();\r\n const pages: (number | string)[] = [];\r\n\r\n if (last <= 5) {\r\n for (let i = 1; i <= last; i++) pages.push(i);\r\n return pages;\r\n }\r\n\r\n pages.push(1);\r\n\r\n if (current <= 4) {\r\n for (let i = 2; i <= 5; i++) pages.push(i);\r\n pages.push('...');\r\n } else if (current >= last - 3) {\r\n pages.push('...');\r\n for (let i = last - 4; i < last; i++) pages.push(i);\r\n } else {\r\n pages.push('...');\r\n pages.push(current - 1, current, current + 1);\r\n pages.push('...');\r\n }\r\n\r\n pages.push(last);\r\n return pages;\r\n });\r\n\r\n private queryParamsSub = Subscription.EMPTY;\r\n readonly queryParams = signal<{ [key: string]: string }>({});\r\n\r\n isPage(p: number | string): p is number {\r\n return typeof p === 'number';\r\n }\r\n\r\n goToPage(page: number): void {\r\n this.router.navigate([], {\r\n relativeTo: this.route,\r\n queryParams: { [this.paramName]: page },\r\n queryParamsHandling: 'merge',\r\n });\r\n }\r\n\r\n generateHref(page: number): string {\r\n const base = this.router.url.split('?')[0];\r\n const params = new URLSearchParams({\r\n ...this.queryParams(),\r\n [this.paramName]: String(page),\r\n });\r\n return `${base}?${params}`;\r\n }\r\n\r\n updateFilter(params: { [key: string]: unknown }): void {\r\n const queryParams: { [key: string]: string | null } = {};\r\n for (const key in params) {\r\n queryParams[key] = params[key] != null ? String(params[key]) : null;\r\n }\r\n this.router.navigate([], {\r\n relativeTo: this.route,\r\n queryParams,\r\n queryParamsHandling: 'merge',\r\n });\r\n }\r\n\r\n ngOnInit(): void {\r\n const queryPage = +(this.route.snapshot.queryParamMap.get(this.paramName) || '1');\r\n if (!queryPage || queryPage < 1) {\r\n this.goToPage(1);\r\n }\r\n\r\n this.queryParamsSub = this.route.queryParamMap.subscribe((params) => {\r\n const newParams: { [key: string]: string } = {};\r\n params.keys.forEach((key) => {\r\n if (key !== this.paramName) {\r\n newParams[key] = params.get(key) ?? '';\r\n }\r\n });\r\n this.queryParams.set(newParams);\r\n\r\n const page = +(params.get(this.paramName) || '1');\r\n if (!page || page < 1) {\r\n this.goToPage(1);\r\n }\r\n });\r\n\r\n effect(\r\n () => {\r\n const pag = this.paginationData();\r\n if (!pag) return;\r\n\r\n const currentParam = +(this.route.snapshot.queryParamMap.get(this.paramName) || '1');\r\n\r\n if (this.resetOnChanges) {\r\n if (currentParam !== 1) {\r\n this.goToPage(1);\r\n }\r\n return;\r\n }\r\n\r\n if (pag.last_page && currentParam > pag.last_page && pag.last_page >= 1) {\r\n this.goToPage(pag.last_page);\r\n }\r\n },\r\n { injector: this.injector },\r\n );\r\n }\r\n\r\n ngOnDestroy(): void {\r\n this.queryParamsSub.unsubscribe();\r\n }\r\n}\r\n","@if (paginationData() && paginationData().total === 0) {\r\n <p class=\"NonResults\">{{ emptyLabel }}</p>\r\n} @else if (paginationData() && paginationData().last_page > 1) {\r\n <nav class=\"pagination\" aria-label=\"Paginación\">\r\n @if (currentPage() > 1) {\r\n <button type=\"button\" (click)=\"goToPage(currentPage() - 1)\" aria-label=\"Anterior\">\r\n <lucide-icon name=\"chevron-left\" [size]=\"16\"></lucide-icon>\r\n </button>\r\n }\r\n\r\n @for (p of pagesToDisplay(); track $index) {\r\n @if (isPage(p)) {\r\n <a\r\n [class.active]=\"p === currentPage()\"\r\n [href]=\"generateHref(p)\"\r\n (click)=\"goToPage(p); $event.preventDefault()\"\r\n >\r\n {{ p }}\r\n </a>\r\n } @else {\r\n <span class=\"ellipsis\">…</span>\r\n }\r\n }\r\n\r\n @if (currentPage() < lastPage()) {\r\n <button type=\"button\" (click)=\"goToPage(currentPage() + 1)\" aria-label=\"Siguiente\">\r\n <lucide-icon name=\"chevron-right\" [size]=\"16\"></lucide-icon>\r\n </button>\r\n }\r\n </nav>\r\n} @else {\r\n <p class=\"UniquePage\">{{ singlePageLabel }}</p>\r\n}\r\n","import { Component, input, output } from '@angular/core';\r\nimport { LucideIconComponent, TextBtnComponent } from '@xosue-utils/actions';\r\n\r\n@Component({\r\n selector: 'empty-state',\r\n standalone: true,\r\n imports: [LucideIconComponent, TextBtnComponent],\r\n template: `\r\n <div class=\"EmptyState\">\r\n @if (icon()) {\r\n <div class=\"EmptyState__Icon\">\r\n <lucide-icon [name]=\"icon()!\" [size]=\"36\"></lucide-icon>\r\n </div>\r\n }\r\n <h3 class=\"EmptyState__Title\">{{ title() }}</h3>\r\n @if (message()) {\r\n <p class=\"EmptyState__Message\">{{ message() }}</p>\r\n }\r\n @if (primaryLabel() || secondaryLabel()) {\r\n <div class=\"EmptyState__Actions\">\r\n @if (secondaryLabel()) {\r\n <text-btn\r\n [label]=\"secondaryLabel()!\"\r\n color=\"cancel\"\r\n variant=\"ghost\"\r\n (clicked)=\"secondary.emit($event)\"\r\n ></text-btn>\r\n }\r\n @if (primaryLabel()) {\r\n <text-btn\r\n [label]=\"primaryLabel()!\"\r\n [icon]=\"primaryIcon()\"\r\n color=\"primary\"\r\n (clicked)=\"primary.emit($event)\"\r\n ></text-btn>\r\n }\r\n </div>\r\n }\r\n <ng-content></ng-content>\r\n </div>\r\n `,\r\n styleUrl: './empty-state.component.sass',\r\n})\r\nexport class EmptyStateComponent {\r\n readonly title = input('Sin datos');\r\n readonly message = input<string | null>(null);\r\n readonly icon = input<string | null>('inbox');\r\n readonly primaryLabel = input<string | null>(null);\r\n readonly primaryIcon = input<string | null>(null);\r\n readonly secondaryLabel = input<string | null>(null);\r\n\r\n readonly primary = output<MouseEvent>();\r\n readonly secondary = output<MouseEvent>();\r\n}\r\n","import { Component, input, output } from '@angular/core';\r\nimport { LucideIconComponent, TextBtnComponent } from '@xosue-utils/actions';\r\n\r\n@Component({\r\n selector: 'load-error',\r\n standalone: true,\r\n imports: [LucideIconComponent, TextBtnComponent],\r\n template: `\r\n <div class=\"LoadError\">\r\n <div class=\"LoadError__Icon\">\r\n <lucide-icon [name]=\"icon()\" [size]=\"36\"></lucide-icon>\r\n </div>\r\n <h3 class=\"LoadError__Title\">{{ title() }}</h3>\r\n @if (message()) {\r\n <p class=\"LoadError__Message\">{{ message() }}</p>\r\n }\r\n <div class=\"LoadError__Actions\">\r\n <text-btn\r\n [label]=\"retryLabel()\"\r\n icon=\"refresh-cw\"\r\n color=\"primary\"\r\n (clicked)=\"retry.emit($event)\"\r\n ></text-btn>\r\n </div>\r\n <ng-content></ng-content>\r\n </div>\r\n `,\r\n styleUrl: './load-error.component.sass',\r\n})\r\nexport class LoadErrorComponent {\r\n readonly title = input('No se pudo cargar');\r\n readonly message = input<string | null>('Revisá tu conexión e intentá de nuevo.');\r\n readonly icon = input('triangle-alert');\r\n readonly retryLabel = input('Reintentar');\r\n\r\n readonly retry = output<MouseEvent>();\r\n}\r\n","import { Component, input } from '@angular/core';\r\n\r\n@Component({\r\n selector: 'section-header',\r\n standalone: true,\r\n template: `\r\n <header class=\"SectionHeader\">\r\n <div class=\"SectionHeader__Text\">\r\n @if (eyebrow()) {\r\n <p class=\"SectionHeader__Eyebrow\">{{ eyebrow() }}</p>\r\n }\r\n <h2 class=\"SectionHeader__Title\">{{ title() }}</h2>\r\n @if (subtitle()) {\r\n <p class=\"SectionHeader__Subtitle\">{{ subtitle() }}</p>\r\n }\r\n </div>\r\n <div class=\"SectionHeader__Actions\">\r\n <ng-content></ng-content>\r\n </div>\r\n </header>\r\n `,\r\n styleUrl: './section-header.component.sass',\r\n})\r\nexport class SectionHeaderComponent {\r\n readonly title = input.required<string>();\r\n readonly subtitle = input<string | null>(null);\r\n readonly eyebrow = input<string | null>(null);\r\n}\r\n","import { Component, input } from '@angular/core';\r\n\r\nexport interface KeyValueItem {\r\n label: string;\r\n value: string | number | null | undefined;\r\n}\r\n\r\n@Component({\r\n selector: 'key-value-list',\r\n standalone: true,\r\n template: `\r\n <dl class=\"KvList\">\r\n @for (item of items(); track item.label + '-' + $index) {\r\n <div class=\"KvList__Row\">\r\n <dt class=\"KvList__Label\">{{ item.label }}</dt>\r\n <dd class=\"KvList__Value\">{{ item.value ?? emptyValue() }}</dd>\r\n </div>\r\n }\r\n </dl>\r\n `,\r\n styleUrl: './key-value-list.component.sass',\r\n})\r\nexport class KeyValueListComponent {\r\n readonly items = input<KeyValueItem[]>([]);\r\n readonly emptyValue = input('—');\r\n}\r\n","/*\r\n * Public API Surface of @xosue-utils/helpers\r\n */\r\n\r\nexport * from './lib/index';\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
package/index.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Generated bundle index. Do not edit.
3
+ */
4
+ /// <amd-module name="@xosue-utils/helpers" />
5
+ export * from './public-api';
@@ -0,0 +1,13 @@
1
+ import * as i0 from "@angular/core";
2
+ export declare class EmptyStateComponent {
3
+ readonly title: import("@angular/core").InputSignal<string>;
4
+ readonly message: import("@angular/core").InputSignal<string>;
5
+ readonly icon: import("@angular/core").InputSignal<string>;
6
+ readonly primaryLabel: import("@angular/core").InputSignal<string>;
7
+ readonly primaryIcon: import("@angular/core").InputSignal<string>;
8
+ readonly secondaryLabel: import("@angular/core").InputSignal<string>;
9
+ readonly primary: import("@angular/core").OutputEmitterRef<MouseEvent>;
10
+ readonly secondary: import("@angular/core").OutputEmitterRef<MouseEvent>;
11
+ static ɵfac: i0.ɵɵFactoryDeclaration<EmptyStateComponent, never>;
12
+ static ɵcmp: i0.ɵɵComponentDeclaration<EmptyStateComponent, "empty-state", never, { "title": { "alias": "title"; "required": false; "isSignal": true; }; "message": { "alias": "message"; "required": false; "isSignal": true; }; "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "primaryLabel": { "alias": "primaryLabel"; "required": false; "isSignal": true; }; "primaryIcon": { "alias": "primaryIcon"; "required": false; "isSignal": true; }; "secondaryLabel": { "alias": "secondaryLabel"; "required": false; "isSignal": true; }; }, { "primary": "primary"; "secondary": "secondary"; }, never, ["*"], true, never>;
13
+ }
package/lib/index.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ export { SmartPaginationComponent, type PaginationData, } from './smart-pagination/smart-pagination.component';
2
+ export { EmptyStateComponent } from './empty-state/empty-state.component';
3
+ export { LoadErrorComponent } from './load-error/load-error.component';
4
+ export { SectionHeaderComponent } from './section-header/section-header.component';
5
+ export { KeyValueListComponent, type KeyValueItem, } from './key-value-list/key-value-list.component';
@@ -0,0 +1,11 @@
1
+ import * as i0 from "@angular/core";
2
+ export interface KeyValueItem {
3
+ label: string;
4
+ value: string | number | null | undefined;
5
+ }
6
+ export declare class KeyValueListComponent {
7
+ readonly items: import("@angular/core").InputSignal<KeyValueItem[]>;
8
+ readonly emptyValue: import("@angular/core").InputSignal<string>;
9
+ static ɵfac: i0.ɵɵFactoryDeclaration<KeyValueListComponent, never>;
10
+ static ɵcmp: i0.ɵɵComponentDeclaration<KeyValueListComponent, "key-value-list", never, { "items": { "alias": "items"; "required": false; "isSignal": true; }; "emptyValue": { "alias": "emptyValue"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
11
+ }
@@ -0,0 +1,10 @@
1
+ import * as i0 from "@angular/core";
2
+ export declare class LoadErrorComponent {
3
+ readonly title: import("@angular/core").InputSignal<string>;
4
+ readonly message: import("@angular/core").InputSignal<string>;
5
+ readonly icon: import("@angular/core").InputSignal<string>;
6
+ readonly retryLabel: import("@angular/core").InputSignal<string>;
7
+ readonly retry: import("@angular/core").OutputEmitterRef<MouseEvent>;
8
+ static ɵfac: i0.ɵɵFactoryDeclaration<LoadErrorComponent, never>;
9
+ static ɵcmp: i0.ɵɵComponentDeclaration<LoadErrorComponent, "load-error", never, { "title": { "alias": "title"; "required": false; "isSignal": true; }; "message": { "alias": "message"; "required": false; "isSignal": true; }; "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "retryLabel": { "alias": "retryLabel"; "required": false; "isSignal": true; }; }, { "retry": "retry"; }, never, ["*"], true, never>;
10
+ }
@@ -0,0 +1,8 @@
1
+ import * as i0 from "@angular/core";
2
+ export declare class SectionHeaderComponent {
3
+ readonly title: import("@angular/core").InputSignal<string>;
4
+ readonly subtitle: import("@angular/core").InputSignal<string>;
5
+ readonly eyebrow: import("@angular/core").InputSignal<string>;
6
+ static ɵfac: i0.ɵɵFactoryDeclaration<SectionHeaderComponent, never>;
7
+ static ɵcmp: i0.ɵɵComponentDeclaration<SectionHeaderComponent, "section-header", never, { "title": { "alias": "title"; "required": true; "isSignal": true; }; "subtitle": { "alias": "subtitle"; "required": false; "isSignal": true; }; "eyebrow": { "alias": "eyebrow"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
8
+ }
@@ -0,0 +1,40 @@
1
+ import { OnInit, OnDestroy } from '@angular/core';
2
+ import * as i0 from "@angular/core";
3
+ export interface PaginationData {
4
+ total: number;
5
+ per_page: number;
6
+ current_page: number;
7
+ last_page: number;
8
+ from: number;
9
+ to: number;
10
+ }
11
+ export declare class SmartPaginationComponent implements OnInit, OnDestroy {
12
+ private router;
13
+ private route;
14
+ private injector;
15
+ resetOnChanges: boolean;
16
+ paramName: string;
17
+ /** Label when total === 0. */
18
+ emptyLabel: string;
19
+ /** Label when last_page === 1. */
20
+ singlePageLabel: string;
21
+ set data(value: PaginationData | null | undefined);
22
+ paginationData: import("@angular/core").WritableSignal<PaginationData>;
23
+ readonly currentPage: import("@angular/core").Signal<number>;
24
+ readonly lastPage: import("@angular/core").Signal<number>;
25
+ readonly pagesToDisplay: import("@angular/core").Signal<(string | number)[]>;
26
+ private queryParamsSub;
27
+ readonly queryParams: import("@angular/core").WritableSignal<{
28
+ [key: string]: string;
29
+ }>;
30
+ isPage(p: number | string): p is number;
31
+ goToPage(page: number): void;
32
+ generateHref(page: number): string;
33
+ updateFilter(params: {
34
+ [key: string]: unknown;
35
+ }): void;
36
+ ngOnInit(): void;
37
+ ngOnDestroy(): void;
38
+ static ɵfac: i0.ɵɵFactoryDeclaration<SmartPaginationComponent, never>;
39
+ static ɵcmp: i0.ɵɵComponentDeclaration<SmartPaginationComponent, "smart-pagination", never, { "resetOnChanges": { "alias": "resetOnChanges"; "required": false; }; "paramName": { "alias": "paramName"; "required": false; }; "emptyLabel": { "alias": "emptyLabel"; "required": false; }; "singlePageLabel": { "alias": "singlePageLabel"; "required": false; }; "data": { "alias": "data"; "required": false; }; }, {}, never, never, true, never>;
40
+ }
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@xosue-utils/helpers",
3
+ "version": "0.2.0",
4
+ "description": "Angular standalone helper UI (pagination, empty-state, load-error, section-header, key-value) with Cupertino aesthetic.",
5
+ "license": "UNLICENSED",
6
+ "private": false,
7
+ "sideEffects": false,
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/YOUR_ORG/xosue-utils.git"
11
+ },
12
+ "keywords": [
13
+ "angular",
14
+ "helpers",
15
+ "pagination",
16
+ "cupertino",
17
+ "xosue-utils"
18
+ ],
19
+ "peerDependencies": {
20
+ "@angular/common": ">=19.0.0 <21.0.0",
21
+ "@angular/core": ">=19.0.0 <21.0.0",
22
+ "@angular/router": ">=19.0.0 <21.0.0",
23
+ "@lucide/angular": ">=0.500.0 <2.0.0",
24
+ "@xosue-utils/actions": ">=0.2.0 <1.0.0",
25
+ "rxjs": ">=7.8.0 <8.0.0"
26
+ },
27
+ "dependencies": {
28
+ "tslib": "^2.6.0"
29
+ },
30
+ "publishConfig": {
31
+ "access": "public"
32
+ },
33
+ "module": "fesm2022/xosue-utils-helpers.mjs",
34
+ "typings": "index.d.ts",
35
+ "exports": {
36
+ "./package.json": {
37
+ "default": "./package.json"
38
+ },
39
+ ".": {
40
+ "types": "./index.d.ts",
41
+ "default": "./fesm2022/xosue-utils-helpers.mjs"
42
+ }
43
+ }
44
+ }
@@ -0,0 +1 @@
1
+ export * from './lib/index';