@adminlte/angular 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.
@@ -0,0 +1,4360 @@
1
+ import * as i0 from '@angular/core';
2
+ import { signal, computed, effect, PLATFORM_ID, Inject, Injectable, inject, ChangeDetectionStrategy, Component, input, output, ElementRef, afterNextRender, viewChild, DestroyRef, model, contentChildren, forwardRef } from '@angular/core';
3
+ import { isPlatformBrowser, DOCUMENT } from '@angular/common';
4
+ import { RouterLink, Router } from '@angular/router';
5
+ import { NG_VALUE_ACCESSOR } from '@angular/forms';
6
+
7
+ const STORAGE_KEY$1 = 'lte-theme';
8
+ /**
9
+ * Inline script that should be added to `index.html` `<head>` to apply the
10
+ * persisted color mode before first paint (no flash of the wrong theme).
11
+ * Exposed so SSR/host apps can inject it. Keep in sync with {@link STORAGE_KEY}.
12
+ */
13
+ const COLOR_MODE_NO_FLASH_SCRIPT = `(function(){try{var m=localStorage.getItem('lte-theme')||'auto';var d=m==='dark'||(m==='auto'&&window.matchMedia('(prefers-color-scheme: dark)').matches);document.documentElement.setAttribute('data-bs-theme',d?'dark':'light');}catch(e){}})();`;
14
+ /**
15
+ * Signal-based color mode (light/dark/auto) service. Persists to localStorage,
16
+ * falls back to the system preference for `auto`, and reflects the resolved mode
17
+ * onto `<html data-bs-theme>` (Bootstrap 5.3 standard). SSR-safe: all DOM access
18
+ * is guarded by the platform check, so it no-ops on the server.
19
+ */
20
+ class ColorModeService {
21
+ doc;
22
+ isBrowser;
23
+ media = null;
24
+ /** True when a mode was restored from localStorage on init. */
25
+ hadPersisted = false;
26
+ /** The user's chosen mode: 'light' | 'dark' | 'auto'. */
27
+ colorMode = signal('auto', /* @ts-ignore */
28
+ ...(ngDevMode ? [{ debugName: "colorMode" }] : /* istanbul ignore next */ []));
29
+ systemDark = signal(false, /* @ts-ignore */
30
+ ...(ngDevMode ? [{ debugName: "systemDark" }] : /* istanbul ignore next */ []));
31
+ /** The concrete mode after `auto` is evaluated against system preference. */
32
+ resolvedMode = computed(() => {
33
+ if (this.colorMode() === 'auto')
34
+ return this.systemDark() ? 'dark' : 'light';
35
+ return this.colorMode();
36
+ }, /* @ts-ignore */
37
+ ...(ngDevMode ? [{ debugName: "resolvedMode" }] : /* istanbul ignore next */ []));
38
+ constructor(platformId, doc) {
39
+ this.doc = doc;
40
+ this.isBrowser = isPlatformBrowser(platformId);
41
+ if (this.isBrowser) {
42
+ const saved = this.readStorage();
43
+ if (saved) {
44
+ this.colorMode.set(saved);
45
+ this.hadPersisted = true;
46
+ }
47
+ this.media = this.doc.defaultView?.matchMedia('(prefers-color-scheme: dark)') ?? null;
48
+ this.systemDark.set(this.media?.matches ?? false);
49
+ this.media?.addEventListener('change', this.onSystemChange);
50
+ }
51
+ // Reflect resolved mode to <html> and persist the chosen mode.
52
+ effect(() => {
53
+ const resolved = this.resolvedMode();
54
+ const chosen = this.colorMode();
55
+ if (!this.isBrowser)
56
+ return;
57
+ this.doc.documentElement.setAttribute('data-bs-theme', resolved);
58
+ this.writeStorage(chosen);
59
+ });
60
+ }
61
+ /** Set the color mode. */
62
+ setColorMode(mode) {
63
+ this.colorMode.set(mode);
64
+ }
65
+ /**
66
+ * Apply an initial mode preference. Honored only when the user has no
67
+ * persisted choice — so a saved selection always wins over the layout default.
68
+ */
69
+ setInitialMode(mode) {
70
+ if (!this.hadPersisted)
71
+ this.colorMode.set(mode);
72
+ }
73
+ /** Cycle light → dark → auto → light. */
74
+ cycle() {
75
+ const order = ['light', 'dark', 'auto'];
76
+ const next = order[(order.indexOf(this.colorMode()) + 1) % order.length];
77
+ this.setColorMode(next);
78
+ }
79
+ onSystemChange = (e) => {
80
+ this.systemDark.set(e.matches);
81
+ };
82
+ readStorage() {
83
+ try {
84
+ const v = localStorage.getItem(STORAGE_KEY$1);
85
+ return v === 'light' || v === 'dark' || v === 'auto' ? v : null;
86
+ }
87
+ catch {
88
+ return null;
89
+ }
90
+ }
91
+ writeStorage(mode) {
92
+ try {
93
+ localStorage.setItem(STORAGE_KEY$1, mode);
94
+ }
95
+ catch {
96
+ /* storage unavailable (private mode / SSR) — ignore */
97
+ }
98
+ }
99
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: ColorModeService, deps: [{ token: PLATFORM_ID }, { token: DOCUMENT }], target: i0.ɵɵFactoryTarget.Injectable });
100
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: ColorModeService, providedIn: 'root' });
101
+ }
102
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: ColorModeService, decorators: [{
103
+ type: Injectable,
104
+ args: [{ providedIn: 'root' }]
105
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
106
+ type: Inject,
107
+ args: [PLATFORM_ID]
108
+ }] }, { type: Document, decorators: [{
109
+ type: Inject,
110
+ args: [DOCUMENT]
111
+ }] }] });
112
+
113
+ const STORAGE_KEY = 'lte.sidebar.state';
114
+ const BREAKPOINT_PX = {
115
+ sm: 576,
116
+ md: 768,
117
+ lg: 992,
118
+ xl: 1200,
119
+ xxl: 1400,
120
+ };
121
+ /**
122
+ * Signal-based sidebar state. Ports the responsive logic of AdminLTE's
123
+ * `push-menu.ts`: on desktop the toggle collapses the sidebar
124
+ * (`body.sidebar-collapse`); on mobile it opens an overlay (`body.sidebar-open`).
125
+ * Reflects state onto `<body>` imperatively (hydration-safe — `<body>` lives
126
+ * outside the Angular component tree). SSR-safe via the platform guard.
127
+ */
128
+ class SidebarService {
129
+ doc;
130
+ isBrowser;
131
+ isCollapsed = signal(false, /* @ts-ignore */
132
+ ...(ngDevMode ? [{ debugName: "isCollapsed" }] : /* istanbul ignore next */ []));
133
+ isMobileOpen = signal(false, /* @ts-ignore */
134
+ ...(ngDevMode ? [{ debugName: "isMobileOpen" }] : /* istanbul ignore next */ []));
135
+ isMiniMode = signal(false, /* @ts-ignore */
136
+ ...(ngDevMode ? [{ debugName: "isMiniMode" }] : /* istanbul ignore next */ []));
137
+ windowWidth = signal(9999, /* @ts-ignore */
138
+ ...(ngDevMode ? [{ debugName: "windowWidth" }] : /* istanbul ignore next */ []));
139
+ breakpoint = signal('lg', /* @ts-ignore */
140
+ ...(ngDevMode ? [{ debugName: "breakpoint" }] : /* istanbul ignore next */ []));
141
+ staticBodyClasses = signal('', /* @ts-ignore */
142
+ ...(ngDevMode ? [{ debugName: "staticBodyClasses" }] : /* istanbul ignore next */ []));
143
+ enablePersistence = false;
144
+ /** Reactive: is the viewport at/below the sidebar breakpoint? */
145
+ isMobile = computed(() => this.windowWidth() <= BREAKPOINT_PX[this.breakpoint()], /* @ts-ignore */
146
+ ...(ngDevMode ? [{ debugName: "isMobile" }] : /* istanbul ignore next */ []));
147
+ appliedBodyClasses = new Set();
148
+ constructor(platformId, doc) {
149
+ this.doc = doc;
150
+ this.isBrowser = isPlatformBrowser(platformId);
151
+ if (this.isBrowser) {
152
+ const win = this.doc.defaultView;
153
+ this.windowWidth.set(win?.innerWidth ?? 9999);
154
+ win?.addEventListener('resize', this.onResize);
155
+ }
156
+ // When growing back to desktop, close the mobile overlay.
157
+ effect(() => {
158
+ if (!this.isMobile() && this.isMobileOpen())
159
+ this.isMobileOpen.set(false);
160
+ });
161
+ // Persist collapse state.
162
+ effect(() => {
163
+ const collapsed = this.isCollapsed();
164
+ if (!this.enablePersistence || !this.isBrowser)
165
+ return;
166
+ try {
167
+ localStorage.setItem(STORAGE_KEY, JSON.stringify({ collapsed }));
168
+ }
169
+ catch {
170
+ /* ignore */
171
+ }
172
+ });
173
+ // Reflect state + static classes onto <body>, diffing the applied set.
174
+ effect(() => {
175
+ if (!this.isBrowser)
176
+ return;
177
+ const next = new Set();
178
+ if (this.isCollapsed())
179
+ next.add('sidebar-collapse');
180
+ if (this.isMobileOpen())
181
+ next.add('sidebar-open');
182
+ if (this.isMiniMode())
183
+ next.add('sidebar-mini');
184
+ for (const cls of this.staticBodyClasses().split(/\s+/).filter(Boolean))
185
+ next.add(cls);
186
+ const body = this.doc.body;
187
+ for (const cls of this.appliedBodyClasses)
188
+ if (!next.has(cls))
189
+ body.classList.remove(cls);
190
+ for (const cls of next)
191
+ body.classList.add(cls);
192
+ this.appliedBodyClasses = next;
193
+ });
194
+ }
195
+ /** Configure the sidebar — called once by the dashboard layout. */
196
+ configure(config) {
197
+ this.isMiniMode.set(config.sidebarMini ?? false);
198
+ this.enablePersistence = config.enablePersistence ?? false;
199
+ this.breakpoint.set(config.breakpoint ?? 'lg');
200
+ this.staticBodyClasses.set(config.staticBodyClasses ?? '');
201
+ if (this.isBrowser && this.enablePersistence) {
202
+ try {
203
+ const saved = localStorage.getItem(STORAGE_KEY);
204
+ if (saved)
205
+ this.isCollapsed.set(!!JSON.parse(saved).collapsed);
206
+ }
207
+ catch {
208
+ /* ignore */
209
+ }
210
+ }
211
+ }
212
+ /** On mobile toggles the overlay; on desktop toggles the collapse state. */
213
+ toggle() {
214
+ if (this.isMobile())
215
+ this.isMobileOpen.update((v) => !v);
216
+ else
217
+ this.isCollapsed.update((v) => !v);
218
+ }
219
+ collapse() {
220
+ this.isCollapsed.set(true);
221
+ this.isMobileOpen.set(false);
222
+ }
223
+ expand() {
224
+ this.isCollapsed.set(false);
225
+ this.isMobileOpen.set(false);
226
+ }
227
+ /** Tear down the body classes (e.g. when leaving the dashboard layout). */
228
+ reset() {
229
+ if (!this.isBrowser)
230
+ return;
231
+ for (const cls of this.appliedBodyClasses)
232
+ this.doc.body.classList.remove(cls);
233
+ this.appliedBodyClasses = new Set();
234
+ }
235
+ onResize = () => {
236
+ this.windowWidth.set(this.doc.defaultView?.innerWidth ?? 9999);
237
+ };
238
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: SidebarService, deps: [{ token: PLATFORM_ID }, { token: DOCUMENT }], target: i0.ɵɵFactoryTarget.Injectable });
239
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: SidebarService, providedIn: 'root' });
240
+ }
241
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: SidebarService, decorators: [{
242
+ type: Injectable,
243
+ args: [{ providedIn: 'root' }]
244
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
245
+ type: Inject,
246
+ args: [PLATFORM_ID]
247
+ }] }, { type: Document, decorators: [{
248
+ type: Inject,
249
+ args: [DOCUMENT]
250
+ }] }] });
251
+
252
+ /**
253
+ * Signal-based command palette (⌘K) state. Registers a global keydown listener:
254
+ * ⌘K / Ctrl+K toggles the palette, Escape closes it. SSR-safe.
255
+ */
256
+ class CommandPaletteService {
257
+ doc;
258
+ isOpen = signal(false, /* @ts-ignore */
259
+ ...(ngDevMode ? [{ debugName: "isOpen" }] : /* istanbul ignore next */ []));
260
+ constructor(platformId, doc) {
261
+ this.doc = doc;
262
+ if (isPlatformBrowser(platformId)) {
263
+ this.doc.addEventListener('keydown', this.onKeydown);
264
+ }
265
+ }
266
+ open() {
267
+ this.isOpen.set(true);
268
+ }
269
+ close() {
270
+ this.isOpen.set(false);
271
+ }
272
+ toggle() {
273
+ this.isOpen.update((v) => !v);
274
+ }
275
+ onKeydown = (e) => {
276
+ if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === 'k') {
277
+ e.preventDefault();
278
+ this.toggle();
279
+ }
280
+ else if (e.key === 'Escape' && this.isOpen()) {
281
+ this.close();
282
+ }
283
+ };
284
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: CommandPaletteService, deps: [{ token: PLATFORM_ID }, { token: DOCUMENT }], target: i0.ɵɵFactoryTarget.Injectable });
285
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: CommandPaletteService, providedIn: 'root' });
286
+ }
287
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: CommandPaletteService, decorators: [{
288
+ type: Injectable,
289
+ args: [{ providedIn: 'root' }]
290
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
291
+ type: Inject,
292
+ args: [PLATFORM_ID]
293
+ }] }, { type: Document, decorators: [{
294
+ type: Inject,
295
+ args: [DOCUMENT]
296
+ }] }] });
297
+
298
+ /**
299
+ * Signal-based wrapper around the Fullscreen API. Drives `isFullscreen` from the
300
+ * `fullscreenchange` event so the UI stays in sync even when the user exits with
301
+ * Esc. Ports AdminLTE's `fullscreen.ts`. SSR-safe.
302
+ */
303
+ class FullscreenService {
304
+ doc;
305
+ isFullscreen = signal(false, /* @ts-ignore */
306
+ ...(ngDevMode ? [{ debugName: "isFullscreen" }] : /* istanbul ignore next */ []));
307
+ constructor(platformId, doc) {
308
+ this.doc = doc;
309
+ if (isPlatformBrowser(platformId)) {
310
+ this.doc.addEventListener('fullscreenchange', this.onChange);
311
+ }
312
+ }
313
+ async enter() {
314
+ try {
315
+ await this.doc.documentElement.requestFullscreen();
316
+ }
317
+ catch (err) {
318
+ console.error('[adminlte-angular] failed to enter fullscreen:', err);
319
+ }
320
+ }
321
+ async exit() {
322
+ try {
323
+ if (this.doc.fullscreenElement)
324
+ await this.doc.exitFullscreen();
325
+ }
326
+ catch (err) {
327
+ console.error('[adminlte-angular] failed to exit fullscreen:', err);
328
+ }
329
+ }
330
+ async toggle() {
331
+ if (this.isFullscreen())
332
+ await this.exit();
333
+ else
334
+ await this.enter();
335
+ }
336
+ onChange = () => {
337
+ this.isFullscreen.set(!!this.doc.fullscreenElement);
338
+ };
339
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: FullscreenService, deps: [{ token: PLATFORM_ID }, { token: DOCUMENT }], target: i0.ɵɵFactoryTarget.Injectable });
340
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: FullscreenService, providedIn: 'root' });
341
+ }
342
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: FullscreenService, decorators: [{
343
+ type: Injectable,
344
+ args: [{ providedIn: 'root' }]
345
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
346
+ type: Inject,
347
+ args: [PLATFORM_ID]
348
+ }] }, { type: Document, decorators: [{
349
+ type: Inject,
350
+ args: [DOCUMENT]
351
+ }] }] });
352
+
353
+ /**
354
+ * Tracks open/closed state of sidebar treeview groups. In `accordion` mode only
355
+ * one group per parent stays open at a time. Provided at the SidebarNav level so
356
+ * each sidebar instance gets its own registry.
357
+ */
358
+ class TreeviewService {
359
+ /** When true, only one child group per parent may be open. */
360
+ accordion = signal(false, /* @ts-ignore */
361
+ ...(ngDevMode ? [{ debugName: "accordion" }] : /* istanbul ignore next */ []));
362
+ /** Maps `parentKey` -> the id of the currently open child group (or null). */
363
+ openByParent = signal({}, /* @ts-ignore */
364
+ ...(ngDevMode ? [{ debugName: "openByParent" }] : /* istanbul ignore next */ []));
365
+ /** Animation duration (ms) for the slide-down/up transition. */
366
+ animationSpeed = signal(300, /* @ts-ignore */
367
+ ...(ngDevMode ? [{ debugName: "animationSpeed" }] : /* istanbul ignore next */ []));
368
+ setAccordion(value) {
369
+ this.accordion.set(value);
370
+ }
371
+ isOpen(parentKey, id) {
372
+ return this.openByParent()[parentKey] === id;
373
+ }
374
+ setOpen(parentKey, id, open) {
375
+ this.openByParent.update((map) => ({
376
+ ...map,
377
+ [parentKey]: open ? id : map[parentKey] === id ? null : map[parentKey] ?? null,
378
+ }));
379
+ }
380
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: TreeviewService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
381
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: TreeviewService });
382
+ }
383
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: TreeviewService, decorators: [{
384
+ type: Injectable
385
+ }] });
386
+
387
+ /**
388
+ * Tiny className utility (alternative to clsx). Filters out falsy values and
389
+ * joins the rest with a single space.
390
+ */
391
+ function cn(...classes) {
392
+ return classes.filter(Boolean).join(' ');
393
+ }
394
+ /**
395
+ * Normalize a Bootstrap Icons class so callers may pass either `bi-speedometer`
396
+ * or `bi bi-speedometer`. Returns a string that always includes the base `bi`.
397
+ */
398
+ function biClass(icon) {
399
+ if (!icon)
400
+ return '';
401
+ return icon.includes('bi-') && !/\bbi\b/.test(icon) ? `bi ${icon}` : icon;
402
+ }
403
+
404
+ /**
405
+ * Resolve the navigable target of a menu item — prefers an Angular `route`,
406
+ * falls back to an external `href`.
407
+ */
408
+ function targetOf(node) {
409
+ return node.route ?? node.href;
410
+ }
411
+ /**
412
+ * Flatten a MenuNode tree into navigable commands for the command palette.
413
+ * Skips hidden items and placeholder links (`#`), and de-duplicates by target.
414
+ */
415
+ function flattenMenuToCommands(nodes) {
416
+ const out = [];
417
+ const walk = (list, group) => {
418
+ for (const node of list) {
419
+ if (node.type === 'header') {
420
+ group = node.text;
421
+ }
422
+ else if (node.type === 'item') {
423
+ if (node.visible === false)
424
+ continue;
425
+ const target = targetOf(node);
426
+ if (target && target !== '#') {
427
+ out.push({ label: node.text, href: target, icon: node.icon, group });
428
+ }
429
+ }
430
+ else {
431
+ if (node.visible === false)
432
+ continue;
433
+ walk(node.children, node.text);
434
+ }
435
+ }
436
+ };
437
+ walk(nodes);
438
+ const seen = new Set();
439
+ return out.filter((c) => (seen.has(c.href) ? false : (seen.add(c.href), true)));
440
+ }
441
+
442
+ /**
443
+ * Topbar dropdown to switch between Light / Dark / Auto color modes.
444
+ * The host element is the navbar `<li>` (selector matches `li[lte-color-mode-toggle]`),
445
+ * so it slots cleanly into the topbar's `<ul class="navbar-nav">`.
446
+ */
447
+ class ColorModeToggleComponent {
448
+ colorModeService = inject(ColorModeService);
449
+ colorMode = this.colorModeService.colorMode;
450
+ triggerIcon = computed(() => {
451
+ switch (this.colorMode()) {
452
+ case 'light':
453
+ return 'bi-sun-fill';
454
+ case 'dark':
455
+ return 'bi-moon-fill';
456
+ default:
457
+ return 'bi-circle-half';
458
+ }
459
+ }, /* @ts-ignore */
460
+ ...(ngDevMode ? [{ debugName: "triggerIcon" }] : /* istanbul ignore next */ []));
461
+ modes = [
462
+ { value: 'light', icon: 'bi-sun-fill', label: 'Light' },
463
+ { value: 'dark', icon: 'bi-moon-fill', label: 'Dark' },
464
+ { value: 'auto', icon: 'bi-circle-half', label: 'Auto' },
465
+ ];
466
+ select(mode) {
467
+ this.colorModeService.setColorMode(mode);
468
+ }
469
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: ColorModeToggleComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
470
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.3", type: ColorModeToggleComponent, isStandalone: true, selector: "li[lte-color-mode-toggle]", host: { classAttribute: "nav-item dropdown" }, ngImport: i0, template: `
471
+ <button
472
+ id="bd-theme"
473
+ class="nav-link"
474
+ type="button"
475
+ aria-label="Toggle color scheme"
476
+ data-bs-toggle="dropdown"
477
+ aria-expanded="false"
478
+ >
479
+ <i class="bi {{ triggerIcon() }}"></i>
480
+ </button>
481
+ <ul class="dropdown-menu dropdown-menu-end" aria-labelledby="bd-theme">
482
+ @for (mode of modes; track mode.value) {
483
+ <li>
484
+ <button
485
+ type="button"
486
+ class="dropdown-item d-flex align-items-center"
487
+ [class.active]="colorMode() === mode.value"
488
+ (click)="select(mode.value)"
489
+ >
490
+ <i class="bi {{ mode.icon }} me-2"></i>
491
+ {{ mode.label }}
492
+ @if (colorMode() === mode.value) {
493
+ <i class="bi bi-check-lg ms-auto"></i>
494
+ }
495
+ </button>
496
+ </li>
497
+ }
498
+ </ul>
499
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
500
+ }
501
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: ColorModeToggleComponent, decorators: [{
502
+ type: Component,
503
+ args: [{
504
+ selector: 'li[lte-color-mode-toggle]',
505
+ changeDetection: ChangeDetectionStrategy.OnPush,
506
+ host: { class: 'nav-item dropdown' },
507
+ template: `
508
+ <button
509
+ id="bd-theme"
510
+ class="nav-link"
511
+ type="button"
512
+ aria-label="Toggle color scheme"
513
+ data-bs-toggle="dropdown"
514
+ aria-expanded="false"
515
+ >
516
+ <i class="bi {{ triggerIcon() }}"></i>
517
+ </button>
518
+ <ul class="dropdown-menu dropdown-menu-end" aria-labelledby="bd-theme">
519
+ @for (mode of modes; track mode.value) {
520
+ <li>
521
+ <button
522
+ type="button"
523
+ class="dropdown-item d-flex align-items-center"
524
+ [class.active]="colorMode() === mode.value"
525
+ (click)="select(mode.value)"
526
+ >
527
+ <i class="bi {{ mode.icon }} me-2"></i>
528
+ {{ mode.label }}
529
+ @if (colorMode() === mode.value) {
530
+ <i class="bi bi-check-lg ms-auto"></i>
531
+ }
532
+ </button>
533
+ </li>
534
+ }
535
+ </ul>
536
+ `,
537
+ }]
538
+ }] });
539
+
540
+ /**
541
+ * Topbar button that toggles browser fullscreen. The host is the navbar `<li>`.
542
+ */
543
+ class FullscreenToggleComponent {
544
+ fullscreen = inject(FullscreenService);
545
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: FullscreenToggleComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
546
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.0.3", type: FullscreenToggleComponent, isStandalone: true, selector: "li[lte-fullscreen-toggle]", host: { classAttribute: "nav-item" }, ngImport: i0, template: `
547
+ <button
548
+ type="button"
549
+ class="nav-link"
550
+ [title]="fullscreen.isFullscreen() ? 'Exit fullscreen' : 'Fullscreen'"
551
+ (click)="fullscreen.toggle()"
552
+ >
553
+ <i class="bi" [class.bi-fullscreen-exit]="fullscreen.isFullscreen()" [class.bi-arrows-fullscreen]="!fullscreen.isFullscreen()"></i>
554
+ </button>
555
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
556
+ }
557
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: FullscreenToggleComponent, decorators: [{
558
+ type: Component,
559
+ args: [{
560
+ selector: 'li[lte-fullscreen-toggle]',
561
+ changeDetection: ChangeDetectionStrategy.OnPush,
562
+ host: { class: 'nav-item' },
563
+ template: `
564
+ <button
565
+ type="button"
566
+ class="nav-link"
567
+ [title]="fullscreen.isFullscreen() ? 'Exit fullscreen' : 'Fullscreen'"
568
+ (click)="fullscreen.toggle()"
569
+ >
570
+ <i class="bi" [class.bi-fullscreen-exit]="fullscreen.isFullscreen()" [class.bi-arrows-fullscreen]="!fullscreen.isFullscreen()"></i>
571
+ </button>
572
+ `,
573
+ }]
574
+ }] });
575
+
576
+ /**
577
+ * Application header (navbar): sidebar toggle, search trigger (⌘K), fullscreen +
578
+ * color-mode toggles, and a user dropdown. Project extra items into the
579
+ * `[topbar-start]` / `[topbar-end]` slots.
580
+ */
581
+ class TopbarComponent {
582
+ sidebar = inject(SidebarService);
583
+ palette = inject(CommandPaletteService);
584
+ user = input(/* @ts-ignore */
585
+ ...(ngDevMode ? [undefined, { debugName: "user" }] : /* istanbul ignore next */ []));
586
+ colorModeToggle = input(true, /* @ts-ignore */
587
+ ...(ngDevMode ? [{ debugName: "colorModeToggle" }] : /* istanbul ignore next */ []));
588
+ search = input(true, /* @ts-ignore */
589
+ ...(ngDevMode ? [{ debugName: "search" }] : /* istanbul ignore next */ []));
590
+ fullscreen = input(true, /* @ts-ignore */
591
+ ...(ngDevMode ? [{ debugName: "fullscreen" }] : /* istanbul ignore next */ []));
592
+ navbarClass = input('', /* @ts-ignore */
593
+ ...(ngDevMode ? [{ debugName: "navbarClass" }] : /* istanbul ignore next */ []));
594
+ logout = output();
595
+ profile = output();
596
+ navClass = computed(() => cn('app-header navbar navbar-expand bg-body', this.navbarClass()), /* @ts-ignore */
597
+ ...(ngDevMode ? [{ debugName: "navClass" }] : /* istanbul ignore next */ []));
598
+ resolvedUser = computed(() => this.user() ?? {
599
+ name: 'Alexander Pierce',
600
+ image: 'https://www.gravatar.com/avatar/?d=mp&s=160',
601
+ role: 'Web Developer',
602
+ memberSince: 'Nov. 2023',
603
+ }, /* @ts-ignore */
604
+ ...(ngDevMode ? [{ debugName: "resolvedUser" }] : /* istanbul ignore next */ []));
605
+ openSearch(e) {
606
+ e.preventDefault();
607
+ this.palette.open();
608
+ }
609
+ emitProfile(e) {
610
+ e.preventDefault();
611
+ this.profile.emit();
612
+ }
613
+ emitLogout(e) {
614
+ e.preventDefault();
615
+ this.logout.emit();
616
+ }
617
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: TopbarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
618
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.3", type: TopbarComponent, isStandalone: true, selector: "lte-topbar", inputs: { user: { classPropertyName: "user", publicName: "user", isSignal: true, isRequired: false, transformFunction: null }, colorModeToggle: { classPropertyName: "colorModeToggle", publicName: "colorModeToggle", isSignal: true, isRequired: false, transformFunction: null }, search: { classPropertyName: "search", publicName: "search", isSignal: true, isRequired: false, transformFunction: null }, fullscreen: { classPropertyName: "fullscreen", publicName: "fullscreen", isSignal: true, isRequired: false, transformFunction: null }, navbarClass: { classPropertyName: "navbarClass", publicName: "navbarClass", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { logout: "logout", profile: "profile" }, ngImport: i0, template: `
619
+ <nav [class]="navClass()">
620
+ <div class="container-fluid">
621
+ <ul class="navbar-nav">
622
+ <li class="nav-item">
623
+ <button type="button" class="nav-link" title="Toggle sidebar" (click)="sidebar.toggle()">
624
+ <i class="bi bi-list"></i>
625
+ </button>
626
+ </li>
627
+ <ng-content select="[topbar-start]" />
628
+ </ul>
629
+
630
+ <ul class="navbar-nav ms-auto">
631
+ @if (search()) {
632
+ <li class="nav-item">
633
+ <a class="nav-link" href="#" role="button" title="Search (⌘K)" (click)="openSearch($event)">
634
+ <i class="bi bi-search"></i>
635
+ </a>
636
+ </li>
637
+ }
638
+
639
+ <ng-content select="[topbar-end]" />
640
+
641
+ @if (fullscreen()) {
642
+ <li lte-fullscreen-toggle></li>
643
+ }
644
+ @if (colorModeToggle()) {
645
+ <li lte-color-mode-toggle></li>
646
+ }
647
+
648
+ <li class="nav-item dropdown user-menu">
649
+ <a href="#" class="nav-link dropdown-toggle" data-bs-toggle="dropdown" (click)="$event.preventDefault()">
650
+ <img [src]="resolvedUser().image" class="user-image rounded-circle shadow" [alt]="resolvedUser().name" />
651
+ <span class="d-none d-md-inline">{{ resolvedUser().name }}</span>
652
+ </a>
653
+ <ul class="dropdown-menu dropdown-menu-lg dropdown-menu-end">
654
+ <li class="user-header text-bg-primary">
655
+ <img [src]="resolvedUser().image" class="rounded-circle shadow" [alt]="resolvedUser().name" />
656
+ <p>
657
+ {{ resolvedUser().name }}@if (resolvedUser().role) { - {{ resolvedUser().role }} }
658
+ @if (resolvedUser().memberSince) {
659
+ <small>Member since {{ resolvedUser().memberSince }}</small>
660
+ }
661
+ </p>
662
+ </li>
663
+ <li class="user-body">
664
+ <div class="row">
665
+ <div class="col-4 text-center"><a href="#">Followers</a></div>
666
+ <div class="col-4 text-center"><a href="#">Sales</a></div>
667
+ <div class="col-4 text-center"><a href="#">Friends</a></div>
668
+ </div>
669
+ </li>
670
+ <li class="user-footer">
671
+ <a href="#" class="btn btn-outline-secondary" (click)="emitProfile($event)">Profile</a>
672
+ <a href="#" class="btn btn-outline-danger float-end" (click)="emitLogout($event)">Sign out</a>
673
+ </li>
674
+ </ul>
675
+ </li>
676
+ </ul>
677
+ </div>
678
+ </nav>
679
+ `, isInline: true, dependencies: [{ kind: "component", type: ColorModeToggleComponent, selector: "li[lte-color-mode-toggle]" }, { kind: "component", type: FullscreenToggleComponent, selector: "li[lte-fullscreen-toggle]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
680
+ }
681
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: TopbarComponent, decorators: [{
682
+ type: Component,
683
+ args: [{
684
+ selector: 'lte-topbar',
685
+ changeDetection: ChangeDetectionStrategy.OnPush,
686
+ imports: [ColorModeToggleComponent, FullscreenToggleComponent],
687
+ template: `
688
+ <nav [class]="navClass()">
689
+ <div class="container-fluid">
690
+ <ul class="navbar-nav">
691
+ <li class="nav-item">
692
+ <button type="button" class="nav-link" title="Toggle sidebar" (click)="sidebar.toggle()">
693
+ <i class="bi bi-list"></i>
694
+ </button>
695
+ </li>
696
+ <ng-content select="[topbar-start]" />
697
+ </ul>
698
+
699
+ <ul class="navbar-nav ms-auto">
700
+ @if (search()) {
701
+ <li class="nav-item">
702
+ <a class="nav-link" href="#" role="button" title="Search (⌘K)" (click)="openSearch($event)">
703
+ <i class="bi bi-search"></i>
704
+ </a>
705
+ </li>
706
+ }
707
+
708
+ <ng-content select="[topbar-end]" />
709
+
710
+ @if (fullscreen()) {
711
+ <li lte-fullscreen-toggle></li>
712
+ }
713
+ @if (colorModeToggle()) {
714
+ <li lte-color-mode-toggle></li>
715
+ }
716
+
717
+ <li class="nav-item dropdown user-menu">
718
+ <a href="#" class="nav-link dropdown-toggle" data-bs-toggle="dropdown" (click)="$event.preventDefault()">
719
+ <img [src]="resolvedUser().image" class="user-image rounded-circle shadow" [alt]="resolvedUser().name" />
720
+ <span class="d-none d-md-inline">{{ resolvedUser().name }}</span>
721
+ </a>
722
+ <ul class="dropdown-menu dropdown-menu-lg dropdown-menu-end">
723
+ <li class="user-header text-bg-primary">
724
+ <img [src]="resolvedUser().image" class="rounded-circle shadow" [alt]="resolvedUser().name" />
725
+ <p>
726
+ {{ resolvedUser().name }}@if (resolvedUser().role) { - {{ resolvedUser().role }} }
727
+ @if (resolvedUser().memberSince) {
728
+ <small>Member since {{ resolvedUser().memberSince }}</small>
729
+ }
730
+ </p>
731
+ </li>
732
+ <li class="user-body">
733
+ <div class="row">
734
+ <div class="col-4 text-center"><a href="#">Followers</a></div>
735
+ <div class="col-4 text-center"><a href="#">Sales</a></div>
736
+ <div class="col-4 text-center"><a href="#">Friends</a></div>
737
+ </div>
738
+ </li>
739
+ <li class="user-footer">
740
+ <a href="#" class="btn btn-outline-secondary" (click)="emitProfile($event)">Profile</a>
741
+ <a href="#" class="btn btn-outline-danger float-end" (click)="emitLogout($event)">Sign out</a>
742
+ </li>
743
+ </ul>
744
+ </li>
745
+ </ul>
746
+ </div>
747
+ </nav>
748
+ `,
749
+ }]
750
+ }], propDecorators: { user: [{ type: i0.Input, args: [{ isSignal: true, alias: "user", required: false }] }], colorModeToggle: [{ type: i0.Input, args: [{ isSignal: true, alias: "colorModeToggle", required: false }] }], search: [{ type: i0.Input, args: [{ isSignal: true, alias: "search", required: false }] }], fullscreen: [{ type: i0.Input, args: [{ isSignal: true, alias: "fullscreen", required: false }] }], navbarClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "navbarClass", required: false }] }], logout: [{ type: i0.Output, args: ["logout"] }], profile: [{ type: i0.Output, args: ["profile"] }] } });
751
+
752
+ /**
753
+ * Sidebar brand/logo header. Shows an image logo with brand text, or the
754
+ * default `<b>Admin</b>LTE` mark when no logo is supplied.
755
+ */
756
+ class SidebarBrandComponent {
757
+ /** Logo image src. When omitted, the default AdminLTE text mark is shown. */
758
+ logo = input(/* @ts-ignore */
759
+ ...(ngDevMode ? [undefined, { debugName: "logo" }] : /* istanbul ignore next */ []));
760
+ href = input('/', /* @ts-ignore */
761
+ ...(ngDevMode ? [{ debugName: "href" }] : /* istanbul ignore next */ []));
762
+ /** Brand text shown next to the logo image. */
763
+ brandText = input('AdminLTE 4', /* @ts-ignore */
764
+ ...(ngDevMode ? [{ debugName: "brandText" }] : /* istanbul ignore next */ []));
765
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: SidebarBrandComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
766
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.3", type: SidebarBrandComponent, isStandalone: true, selector: "lte-sidebar-brand", inputs: { logo: { classPropertyName: "logo", publicName: "logo", isSignal: true, isRequired: false, transformFunction: null }, href: { classPropertyName: "href", publicName: "href", isSignal: true, isRequired: false, transformFunction: null }, brandText: { classPropertyName: "brandText", publicName: "brandText", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "sidebar-brand" }, ngImport: i0, template: `
767
+ <a [routerLink]="href()" class="brand-link">
768
+ @if (logo()) {
769
+ <img [src]="logo()" alt="Logo" class="brand-image opacity-75 shadow" />
770
+ <span class="brand-text fw-light">{{ brandText() }}</span>
771
+ } @else {
772
+ <span class="brand-image opacity-75 shadow"></span>
773
+ <span class="brand-text fw-light"><b>Admin</b>LTE</span>
774
+ }
775
+ </a>
776
+ `, isInline: true, dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "browserUrl", "routerLink"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
777
+ }
778
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: SidebarBrandComponent, decorators: [{
779
+ type: Component,
780
+ args: [{
781
+ selector: 'lte-sidebar-brand',
782
+ changeDetection: ChangeDetectionStrategy.OnPush,
783
+ imports: [RouterLink],
784
+ host: { class: 'sidebar-brand' },
785
+ template: `
786
+ <a [routerLink]="href()" class="brand-link">
787
+ @if (logo()) {
788
+ <img [src]="logo()" alt="Logo" class="brand-image opacity-75 shadow" />
789
+ <span class="brand-text fw-light">{{ brandText() }}</span>
790
+ } @else {
791
+ <span class="brand-image opacity-75 shadow"></span>
792
+ <span class="brand-text fw-light"><b>Admin</b>LTE</span>
793
+ }
794
+ </a>
795
+ `,
796
+ }]
797
+ }], propDecorators: { logo: [{ type: i0.Input, args: [{ isSignal: true, alias: "logo", required: false }] }], href: [{ type: i0.Input, args: [{ isSignal: true, alias: "href", required: false }] }], brandText: [{ type: i0.Input, args: [{ isSignal: true, alias: "brandText", required: false }] }] } });
798
+
799
+ let uid = 0;
800
+ /**
801
+ * Renders a single sidebar node — a header, a leaf link, or a collapsible group
802
+ * (treeview) that recurses into its children. Active-link detection compares the
803
+ * item's route/href to `currentPath`. Groups auto-open when a descendant is
804
+ * active. The submenu slides open via a CSS `grid-template-rows` transition (no
805
+ * fixed heights, no animations package).
806
+ */
807
+ class SidebarNavItemComponent {
808
+ registry = inject(TreeviewService);
809
+ item = input.required(/* @ts-ignore */
810
+ ...(ngDevMode ? [{ debugName: "item" }] : /* istanbul ignore next */ []));
811
+ currentPath = input('/', /* @ts-ignore */
812
+ ...(ngDevMode ? [{ debugName: "currentPath" }] : /* istanbul ignore next */ []));
813
+ depth = input(0, /* @ts-ignore */
814
+ ...(ngDevMode ? [{ debugName: "depth" }] : /* istanbul ignore next */ []));
815
+ parentKey = input('root', /* @ts-ignore */
816
+ ...(ngDevMode ? [{ debugName: "parentKey" }] : /* istanbul ignore next */ []));
817
+ id = `tv-${uid++}`;
818
+ /** Discriminated accessors so the template stays strongly typed. */
819
+ header = computed(() => this.item(), /* @ts-ignore */
820
+ ...(ngDevMode ? [{ debugName: "header" }] : /* istanbul ignore next */ []));
821
+ leaf = computed(() => this.item(), /* @ts-ignore */
822
+ ...(ngDevMode ? [{ debugName: "leaf" }] : /* istanbul ignore next */ []));
823
+ group = computed(() => this.item(), /* @ts-ignore */
824
+ ...(ngDevMode ? [{ debugName: "group" }] : /* istanbul ignore next */ []));
825
+ animationSpeed = this.registry.animationSpeed;
826
+ localOpen = signal(false, /* @ts-ignore */
827
+ ...(ngDevMode ? [{ debugName: "localOpen" }] : /* istanbul ignore next */ []));
828
+ visibleChildren = computed(() => this.item().type === 'group'
829
+ ? this.item().children.filter((c) => c.type === 'header' || c.visible !== false)
830
+ : [], /* @ts-ignore */
831
+ ...(ngDevMode ? [{ debugName: "visibleChildren" }] : /* istanbul ignore next */ []));
832
+ isItemActive = computed(() => {
833
+ const node = this.item();
834
+ if (node.type !== 'item')
835
+ return false;
836
+ const target = node.route ?? node.href;
837
+ return target ? this.matches(target) : false;
838
+ }, /* @ts-ignore */
839
+ ...(ngDevMode ? [{ debugName: "isItemActive" }] : /* istanbul ignore next */ []));
840
+ groupActive = computed(() => this.item().type === 'group' && this.hasActiveDescendant(this.item()), /* @ts-ignore */
841
+ ...(ngDevMode ? [{ debugName: "groupActive" }] : /* istanbul ignore next */ []));
842
+ isOpen = computed(() => {
843
+ if (this.registry.accordion())
844
+ return this.registry.isOpen(this.parentKey(), this.id);
845
+ return this.localOpen();
846
+ }, /* @ts-ignore */
847
+ ...(ngDevMode ? [{ debugName: "isOpen" }] : /* istanbul ignore next */ []));
848
+ constructor() {
849
+ // Open the group when a descendant becomes active (e.g. on route change).
850
+ effect(() => {
851
+ if (!this.groupActive())
852
+ return;
853
+ if (this.registry.accordion())
854
+ this.registry.setOpen(this.parentKey(), this.id, true);
855
+ else
856
+ this.localOpen.set(true);
857
+ });
858
+ }
859
+ icon(value) {
860
+ return biClass(value);
861
+ }
862
+ toggle() {
863
+ if (this.registry.accordion()) {
864
+ this.registry.setOpen(this.parentKey(), this.id, !this.isOpen());
865
+ }
866
+ else {
867
+ this.localOpen.update((v) => !v);
868
+ }
869
+ }
870
+ trackChild(index, child) {
871
+ return child.type === 'item'
872
+ ? (child.route ?? child.href ?? child.text)
873
+ : `${child.type}:${child.text}:${index}`;
874
+ }
875
+ matches(target) {
876
+ const path = this.currentPath();
877
+ if (target.startsWith('http'))
878
+ return false;
879
+ if (target === '/')
880
+ return path === '/';
881
+ return path === target || path.startsWith(target + '/');
882
+ }
883
+ hasActiveDescendant(node) {
884
+ if (node.type === 'item') {
885
+ const target = node.route ?? node.href;
886
+ return target ? this.matches(target) : false;
887
+ }
888
+ if (node.type === 'group')
889
+ return node.children.some((c) => this.hasActiveDescendant(c));
890
+ return false;
891
+ }
892
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: SidebarNavItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
893
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.3", type: SidebarNavItemComponent, isStandalone: true, selector: "lte-sidebar-nav-item", inputs: { item: { classPropertyName: "item", publicName: "item", isSignal: true, isRequired: true, transformFunction: null }, currentPath: { classPropertyName: "currentPath", publicName: "currentPath", isSignal: true, isRequired: false, transformFunction: null }, depth: { classPropertyName: "depth", publicName: "depth", isSignal: true, isRequired: false, transformFunction: null }, parentKey: { classPropertyName: "parentKey", publicName: "parentKey", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
894
+ @switch (item().type) {
895
+ @case ('header') {
896
+ <li class="nav-header">{{ header().text }}</li>
897
+ }
898
+ @case ('item') {
899
+ <li class="nav-item" [class.active]="isItemActive()">
900
+ @if (leaf().route) {
901
+ <a [routerLink]="leaf().route" [attr.target]="leaf().target ?? null" class="nav-link" [class.active]="isItemActive()">
902
+ @if (leaf().icon) {
903
+ <i class="nav-icon {{ icon(leaf().icon) }}" [class]="leaf().iconColor ? 'text-' + leaf().iconColor : ''"></i>
904
+ }
905
+ <p>
906
+ {{ leaf().text }}
907
+ @if (leaf().badge != null) {
908
+ <span class="nav-badge badge text-bg-{{ leaf().badgeColor || 'secondary' }} ms-auto">{{ leaf().badge }}</span>
909
+ }
910
+ </p>
911
+ </a>
912
+ } @else {
913
+ <a [href]="leaf().href ?? '#'" [attr.target]="leaf().target ?? null" class="nav-link" [class.active]="isItemActive()">
914
+ @if (leaf().icon) {
915
+ <i class="nav-icon {{ icon(leaf().icon) }}" [class]="leaf().iconColor ? 'text-' + leaf().iconColor : ''"></i>
916
+ }
917
+ <p>
918
+ {{ leaf().text }}
919
+ @if (leaf().badge != null) {
920
+ <span class="nav-badge badge text-bg-{{ leaf().badgeColor || 'secondary' }} ms-auto">{{ leaf().badge }}</span>
921
+ }
922
+ </p>
923
+ </a>
924
+ }
925
+ </li>
926
+ }
927
+ @default {
928
+ <li class="nav-item" [class.menu-open]="isOpen()">
929
+ <button type="button" class="nav-link" [attr.aria-expanded]="isOpen()" (click)="toggle()">
930
+ @if (group().icon) {
931
+ <i class="nav-icon {{ icon(group().icon) }}"></i>
932
+ }
933
+ <p>
934
+ {{ group().text }}
935
+ <i class="nav-arrow bi bi-chevron-right"></i>
936
+ @if (group().badge != null) {
937
+ <span class="nav-badge badge text-bg-{{ group().badgeColor || 'secondary' }} ms-auto me-3">{{ group().badge }}</span>
938
+ }
939
+ </p>
940
+ </button>
941
+
942
+ <div class="treeview-wrap" [class.open]="isOpen()" [style.--lte-treeview-speed.ms]="animationSpeed()">
943
+ <ul class="nav nav-treeview">
944
+ @for (child of visibleChildren(); track trackChild($index, child)) {
945
+ <lte-sidebar-nav-item
946
+ [item]="child"
947
+ [currentPath]="currentPath()"
948
+ [depth]="depth() + 1"
949
+ [parentKey]="id"
950
+ />
951
+ }
952
+ </ul>
953
+ </div>
954
+ </li>
955
+ }
956
+ }
957
+ `, isInline: true, styles: [".treeview-wrap{display:grid;grid-template-rows:0fr;transition:grid-template-rows var(--lte-treeview-speed, .3s) ease}.treeview-wrap.open{grid-template-rows:1fr}.treeview-wrap>.nav-treeview{overflow:hidden;min-height:0}\n"], dependencies: [{ kind: "component", type: SidebarNavItemComponent, selector: "lte-sidebar-nav-item", inputs: ["item", "currentPath", "depth", "parentKey"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "browserUrl", "routerLink"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
958
+ }
959
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: SidebarNavItemComponent, decorators: [{
960
+ type: Component,
961
+ args: [{ selector: 'lte-sidebar-nav-item', changeDetection: ChangeDetectionStrategy.OnPush, imports: [RouterLink], template: `
962
+ @switch (item().type) {
963
+ @case ('header') {
964
+ <li class="nav-header">{{ header().text }}</li>
965
+ }
966
+ @case ('item') {
967
+ <li class="nav-item" [class.active]="isItemActive()">
968
+ @if (leaf().route) {
969
+ <a [routerLink]="leaf().route" [attr.target]="leaf().target ?? null" class="nav-link" [class.active]="isItemActive()">
970
+ @if (leaf().icon) {
971
+ <i class="nav-icon {{ icon(leaf().icon) }}" [class]="leaf().iconColor ? 'text-' + leaf().iconColor : ''"></i>
972
+ }
973
+ <p>
974
+ {{ leaf().text }}
975
+ @if (leaf().badge != null) {
976
+ <span class="nav-badge badge text-bg-{{ leaf().badgeColor || 'secondary' }} ms-auto">{{ leaf().badge }}</span>
977
+ }
978
+ </p>
979
+ </a>
980
+ } @else {
981
+ <a [href]="leaf().href ?? '#'" [attr.target]="leaf().target ?? null" class="nav-link" [class.active]="isItemActive()">
982
+ @if (leaf().icon) {
983
+ <i class="nav-icon {{ icon(leaf().icon) }}" [class]="leaf().iconColor ? 'text-' + leaf().iconColor : ''"></i>
984
+ }
985
+ <p>
986
+ {{ leaf().text }}
987
+ @if (leaf().badge != null) {
988
+ <span class="nav-badge badge text-bg-{{ leaf().badgeColor || 'secondary' }} ms-auto">{{ leaf().badge }}</span>
989
+ }
990
+ </p>
991
+ </a>
992
+ }
993
+ </li>
994
+ }
995
+ @default {
996
+ <li class="nav-item" [class.menu-open]="isOpen()">
997
+ <button type="button" class="nav-link" [attr.aria-expanded]="isOpen()" (click)="toggle()">
998
+ @if (group().icon) {
999
+ <i class="nav-icon {{ icon(group().icon) }}"></i>
1000
+ }
1001
+ <p>
1002
+ {{ group().text }}
1003
+ <i class="nav-arrow bi bi-chevron-right"></i>
1004
+ @if (group().badge != null) {
1005
+ <span class="nav-badge badge text-bg-{{ group().badgeColor || 'secondary' }} ms-auto me-3">{{ group().badge }}</span>
1006
+ }
1007
+ </p>
1008
+ </button>
1009
+
1010
+ <div class="treeview-wrap" [class.open]="isOpen()" [style.--lte-treeview-speed.ms]="animationSpeed()">
1011
+ <ul class="nav nav-treeview">
1012
+ @for (child of visibleChildren(); track trackChild($index, child)) {
1013
+ <lte-sidebar-nav-item
1014
+ [item]="child"
1015
+ [currentPath]="currentPath()"
1016
+ [depth]="depth() + 1"
1017
+ [parentKey]="id"
1018
+ />
1019
+ }
1020
+ </ul>
1021
+ </div>
1022
+ </li>
1023
+ }
1024
+ }
1025
+ `, styles: [".treeview-wrap{display:grid;grid-template-rows:0fr;transition:grid-template-rows var(--lte-treeview-speed, .3s) ease}.treeview-wrap.open{grid-template-rows:1fr}.treeview-wrap>.nav-treeview{overflow:hidden;min-height:0}\n"] }]
1026
+ }], ctorParameters: () => [], propDecorators: { item: [{ type: i0.Input, args: [{ isSignal: true, alias: "item", required: true }] }], currentPath: [{ type: i0.Input, args: [{ isSignal: true, alias: "currentPath", required: false }] }], depth: [{ type: i0.Input, args: [{ isSignal: true, alias: "depth", required: false }] }], parentKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "parentKey", required: false }] }] } });
1027
+
1028
+ /**
1029
+ * Renders the config-driven sidebar menu from a `MenuNode[]`. Owns a
1030
+ * {@link TreeviewService} registry so each sidebar instance manages its own
1031
+ * accordion state.
1032
+ */
1033
+ class SidebarNavComponent {
1034
+ registry = inject(TreeviewService);
1035
+ items = input([], /* @ts-ignore */
1036
+ ...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
1037
+ currentPath = input('/', /* @ts-ignore */
1038
+ ...(ngDevMode ? [{ debugName: "currentPath" }] : /* istanbul ignore next */ []));
1039
+ /** Accordion treeview — one open group per parent at a time. */
1040
+ accordion = input(false, /* @ts-ignore */
1041
+ ...(ngDevMode ? [{ debugName: "accordion" }] : /* istanbul ignore next */ []));
1042
+ animationSpeed = input(300, /* @ts-ignore */
1043
+ ...(ngDevMode ? [{ debugName: "animationSpeed" }] : /* istanbul ignore next */ []));
1044
+ visibleItems = computed(() => this.items().filter((i) => i.type === 'header' || i.visible !== false), /* @ts-ignore */
1045
+ ...(ngDevMode ? [{ debugName: "visibleItems" }] : /* istanbul ignore next */ []));
1046
+ constructor() {
1047
+ effect(() => this.registry.setAccordion(this.accordion()));
1048
+ effect(() => this.registry.animationSpeed.set(this.animationSpeed()));
1049
+ }
1050
+ trackItem(index, item) {
1051
+ return item.type === 'item'
1052
+ ? (item.route ?? item.href ?? item.text)
1053
+ : `${item.type}:${item.text}:${index}`;
1054
+ }
1055
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: SidebarNavComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1056
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.3", type: SidebarNavComponent, isStandalone: true, selector: "lte-sidebar-nav", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, currentPath: { classPropertyName: "currentPath", publicName: "currentPath", isSignal: true, isRequired: false, transformFunction: null }, accordion: { classPropertyName: "accordion", publicName: "accordion", isSignal: true, isRequired: false, transformFunction: null }, animationSpeed: { classPropertyName: "animationSpeed", publicName: "animationSpeed", isSignal: true, isRequired: false, transformFunction: null } }, providers: [TreeviewService], ngImport: i0, template: `
1057
+ <nav class="mt-2" aria-label="Main navigation">
1058
+ <ul
1059
+ id="navigation"
1060
+ class="nav sidebar-menu flex-column"
1061
+ data-lte-toggle="treeview"
1062
+ [attr.data-accordion]="accordion() ? 'true' : 'false'"
1063
+ >
1064
+ @for (item of visibleItems(); track trackItem($index, item)) {
1065
+ <lte-sidebar-nav-item [item]="item" [currentPath]="currentPath()" [depth]="0" parentKey="root" />
1066
+ }
1067
+ </ul>
1068
+ </nav>
1069
+ `, isInline: true, dependencies: [{ kind: "component", type: SidebarNavItemComponent, selector: "lte-sidebar-nav-item", inputs: ["item", "currentPath", "depth", "parentKey"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1070
+ }
1071
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: SidebarNavComponent, decorators: [{
1072
+ type: Component,
1073
+ args: [{
1074
+ selector: 'lte-sidebar-nav',
1075
+ changeDetection: ChangeDetectionStrategy.OnPush,
1076
+ imports: [SidebarNavItemComponent],
1077
+ providers: [TreeviewService],
1078
+ template: `
1079
+ <nav class="mt-2" aria-label="Main navigation">
1080
+ <ul
1081
+ id="navigation"
1082
+ class="nav sidebar-menu flex-column"
1083
+ data-lte-toggle="treeview"
1084
+ [attr.data-accordion]="accordion() ? 'true' : 'false'"
1085
+ >
1086
+ @for (item of visibleItems(); track trackItem($index, item)) {
1087
+ <lte-sidebar-nav-item [item]="item" [currentPath]="currentPath()" [depth]="0" parentKey="root" />
1088
+ }
1089
+ </ul>
1090
+ </nav>
1091
+ `,
1092
+ }]
1093
+ }], ctorParameters: () => [], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }], currentPath: [{ type: i0.Input, args: [{ isSignal: true, alias: "currentPath", required: false }] }], accordion: [{ type: i0.Input, args: [{ isSignal: true, alias: "accordion", required: false }] }], animationSpeed: [{ type: i0.Input, args: [{ isSignal: true, alias: "animationSpeed", required: false }] }] } });
1094
+
1095
+ /**
1096
+ * Click/touch backdrop shown behind the mobile off-canvas sidebar. Tapping it
1097
+ * collapses the sidebar.
1098
+ */
1099
+ class SidebarOverlayComponent {
1100
+ sidebar = inject(SidebarService);
1101
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: SidebarOverlayComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1102
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.0.3", type: SidebarOverlayComponent, isStandalone: true, selector: "lte-sidebar-overlay", host: { attributes: { "role": "presentation" }, listeners: { "click": "sidebar.collapse()", "touchend": "sidebar.collapse()" }, classAttribute: "sidebar-overlay" }, ngImport: i0, template: ``, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
1103
+ }
1104
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: SidebarOverlayComponent, decorators: [{
1105
+ type: Component,
1106
+ args: [{
1107
+ selector: 'lte-sidebar-overlay',
1108
+ changeDetection: ChangeDetectionStrategy.OnPush,
1109
+ host: {
1110
+ class: 'sidebar-overlay',
1111
+ role: 'presentation',
1112
+ '(click)': 'sidebar.collapse()',
1113
+ '(touchend)': 'sidebar.collapse()',
1114
+ },
1115
+ template: ``,
1116
+ }]
1117
+ }] });
1118
+
1119
+ /**
1120
+ * The off-canvas application sidebar: brand header + scrollable config-driven
1121
+ * menu, plus the mobile overlay. The `theme` controls the local `data-bs-theme`
1122
+ * so the sidebar can be dark while the page is light (and vice versa).
1123
+ */
1124
+ class SidebarComponent {
1125
+ items = input([], /* @ts-ignore */
1126
+ ...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
1127
+ logo = input(/* @ts-ignore */
1128
+ ...(ngDevMode ? [undefined, { debugName: "logo" }] : /* istanbul ignore next */ []));
1129
+ logoHref = input('/', /* @ts-ignore */
1130
+ ...(ngDevMode ? [{ debugName: "logoHref" }] : /* istanbul ignore next */ []));
1131
+ brandText = input('AdminLTE 4', /* @ts-ignore */
1132
+ ...(ngDevMode ? [{ debugName: "brandText" }] : /* istanbul ignore next */ []));
1133
+ theme = input('dark', /* @ts-ignore */
1134
+ ...(ngDevMode ? [{ debugName: "theme" }] : /* istanbul ignore next */ []));
1135
+ sidebarClass = input('bg-body-secondary shadow', /* @ts-ignore */
1136
+ ...(ngDevMode ? [{ debugName: "sidebarClass" }] : /* istanbul ignore next */ []));
1137
+ currentPath = input('/', /* @ts-ignore */
1138
+ ...(ngDevMode ? [{ debugName: "currentPath" }] : /* istanbul ignore next */ []));
1139
+ accordion = input(false, /* @ts-ignore */
1140
+ ...(ngDevMode ? [{ debugName: "accordion" }] : /* istanbul ignore next */ []));
1141
+ animationSpeed = input(300, /* @ts-ignore */
1142
+ ...(ngDevMode ? [{ debugName: "animationSpeed" }] : /* istanbul ignore next */ []));
1143
+ asideClass = () => cn('app-sidebar', this.sidebarClass());
1144
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: SidebarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1145
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.3", type: SidebarComponent, isStandalone: true, selector: "lte-sidebar", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, logo: { classPropertyName: "logo", publicName: "logo", isSignal: true, isRequired: false, transformFunction: null }, logoHref: { classPropertyName: "logoHref", publicName: "logoHref", isSignal: true, isRequired: false, transformFunction: null }, brandText: { classPropertyName: "brandText", publicName: "brandText", isSignal: true, isRequired: false, transformFunction: null }, theme: { classPropertyName: "theme", publicName: "theme", isSignal: true, isRequired: false, transformFunction: null }, sidebarClass: { classPropertyName: "sidebarClass", publicName: "sidebarClass", isSignal: true, isRequired: false, transformFunction: null }, currentPath: { classPropertyName: "currentPath", publicName: "currentPath", isSignal: true, isRequired: false, transformFunction: null }, accordion: { classPropertyName: "accordion", publicName: "accordion", isSignal: true, isRequired: false, transformFunction: null }, animationSpeed: { classPropertyName: "animationSpeed", publicName: "animationSpeed", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
1146
+ <aside [class]="asideClass()" [attr.data-bs-theme]="theme()">
1147
+ <lte-sidebar-brand [logo]="logo()" [href]="logoHref()" [brandText]="brandText()" />
1148
+ <div class="sidebar-wrapper">
1149
+ <lte-sidebar-nav
1150
+ [items]="items()"
1151
+ [currentPath]="currentPath()"
1152
+ [accordion]="accordion()"
1153
+ [animationSpeed]="animationSpeed()"
1154
+ />
1155
+ </div>
1156
+ </aside>
1157
+
1158
+ <lte-sidebar-overlay />
1159
+ `, isInline: true, dependencies: [{ kind: "component", type: SidebarBrandComponent, selector: "lte-sidebar-brand", inputs: ["logo", "href", "brandText"] }, { kind: "component", type: SidebarNavComponent, selector: "lte-sidebar-nav", inputs: ["items", "currentPath", "accordion", "animationSpeed"] }, { kind: "component", type: SidebarOverlayComponent, selector: "lte-sidebar-overlay" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1160
+ }
1161
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: SidebarComponent, decorators: [{
1162
+ type: Component,
1163
+ args: [{
1164
+ selector: 'lte-sidebar',
1165
+ changeDetection: ChangeDetectionStrategy.OnPush,
1166
+ imports: [SidebarBrandComponent, SidebarNavComponent, SidebarOverlayComponent],
1167
+ template: `
1168
+ <aside [class]="asideClass()" [attr.data-bs-theme]="theme()">
1169
+ <lte-sidebar-brand [logo]="logo()" [href]="logoHref()" [brandText]="brandText()" />
1170
+ <div class="sidebar-wrapper">
1171
+ <lte-sidebar-nav
1172
+ [items]="items()"
1173
+ [currentPath]="currentPath()"
1174
+ [accordion]="accordion()"
1175
+ [animationSpeed]="animationSpeed()"
1176
+ />
1177
+ </div>
1178
+ </aside>
1179
+
1180
+ <lte-sidebar-overlay />
1181
+ `,
1182
+ }]
1183
+ }], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }], logo: [{ type: i0.Input, args: [{ isSignal: true, alias: "logo", required: false }] }], logoHref: [{ type: i0.Input, args: [{ isSignal: true, alias: "logoHref", required: false }] }], brandText: [{ type: i0.Input, args: [{ isSignal: true, alias: "brandText", required: false }] }], theme: [{ type: i0.Input, args: [{ isSignal: true, alias: "theme", required: false }] }], sidebarClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "sidebarClass", required: false }] }], currentPath: [{ type: i0.Input, args: [{ isSignal: true, alias: "currentPath", required: false }] }], accordion: [{ type: i0.Input, args: [{ isSignal: true, alias: "accordion", required: false }] }], animationSpeed: [{ type: i0.Input, args: [{ isSignal: true, alias: "animationSpeed", required: false }] }] } });
1184
+
1185
+ /**
1186
+ * App footer. Projects custom content via the default slot; when none is
1187
+ * provided it falls back to the default AdminLTE copyright line. The host
1188
+ * element is the `<footer class="app-footer">`.
1189
+ */
1190
+ class FooterComponent {
1191
+ el = inject((ElementRef));
1192
+ /** Right-aligned text. Hidden on extra-small screens. */
1193
+ rightText = input('', /* @ts-ignore */
1194
+ ...(ngDevMode ? [{ debugName: "rightText" }] : /* istanbul ignore next */ []));
1195
+ year = input(new Date().getFullYear(), /* @ts-ignore */
1196
+ ...(ngDevMode ? [{ debugName: "year" }] : /* istanbul ignore next */ []));
1197
+ /** True when no content was projected — render the default copyright. */
1198
+ showDefault = signal(false, /* @ts-ignore */
1199
+ ...(ngDevMode ? [{ debugName: "showDefault" }] : /* istanbul ignore next */ []));
1200
+ constructor() {
1201
+ afterNextRender(() => {
1202
+ const projected = this.el.nativeElement.querySelector('span');
1203
+ this.showDefault.set(!projected || projected.textContent?.trim() === '');
1204
+ });
1205
+ }
1206
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: FooterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1207
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.3", type: FooterComponent, isStandalone: true, selector: "lte-footer", inputs: { rightText: { classPropertyName: "rightText", publicName: "rightText", isSignal: true, isRequired: false, transformFunction: null }, year: { classPropertyName: "year", publicName: "year", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "app-footer" }, ngImport: i0, template: `
1208
+ @if (rightText()) {
1209
+ <div class="float-end d-none d-sm-inline">{{ rightText() }}</div>
1210
+ }
1211
+ <span #projected><ng-content /></span>
1212
+ @if (showDefault()) {
1213
+ <strong>
1214
+ Copyright &copy; 2014-{{ year() }}&nbsp;
1215
+ <a href="https://adminlte.io" class="text-decoration-none">AdminLTE.io</a>.
1216
+ </strong>
1217
+ All rights reserved.
1218
+ }
1219
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
1220
+ }
1221
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: FooterComponent, decorators: [{
1222
+ type: Component,
1223
+ args: [{
1224
+ selector: 'lte-footer',
1225
+ changeDetection: ChangeDetectionStrategy.OnPush,
1226
+ host: { class: 'app-footer' },
1227
+ template: `
1228
+ @if (rightText()) {
1229
+ <div class="float-end d-none d-sm-inline">{{ rightText() }}</div>
1230
+ }
1231
+ <span #projected><ng-content /></span>
1232
+ @if (showDefault()) {
1233
+ <strong>
1234
+ Copyright &copy; 2014-{{ year() }}&nbsp;
1235
+ <a href="https://adminlte.io" class="text-decoration-none">AdminLTE.io</a>.
1236
+ </strong>
1237
+ All rights reserved.
1238
+ }
1239
+ `,
1240
+ }]
1241
+ }], ctorParameters: () => [], propDecorators: { rightText: [{ type: i0.Input, args: [{ isSignal: true, alias: "rightText", required: false }] }], year: [{ type: i0.Input, args: [{ isSignal: true, alias: "year", required: false }] }] } });
1242
+
1243
+ /**
1244
+ * ⌘K command palette. Flattens the sidebar menu into navigable commands, filters
1245
+ * them by a substring query, and supports full keyboard navigation
1246
+ * (↑/↓/Enter/Esc). Navigation goes through the Angular Router by default; pass a
1247
+ * `(navigate)` handler to override.
1248
+ */
1249
+ class CommandPaletteComponent {
1250
+ palette = inject(CommandPaletteService);
1251
+ router = inject(Router, { optional: true });
1252
+ /** Explicit command list. Takes precedence over `menuItems`. */
1253
+ items = input(/* @ts-ignore */
1254
+ ...(ngDevMode ? [undefined, { debugName: "items" }] : /* istanbul ignore next */ []));
1255
+ /** Menu tree to flatten into commands when `items` is not given. */
1256
+ menuItems = input([], /* @ts-ignore */
1257
+ ...(ngDevMode ? [{ debugName: "menuItems" }] : /* istanbul ignore next */ []));
1258
+ placeholder = input('Search pages…', /* @ts-ignore */
1259
+ ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
1260
+ /**
1261
+ * Optional navigation override. When provided it fully takes over routing
1262
+ * (e.g. a custom router push or analytics-wrapped navigation); otherwise the
1263
+ * palette uses the Angular Router (or `window.location` for external/`http` links).
1264
+ */
1265
+ navigate = input(/* @ts-ignore */
1266
+ ...(ngDevMode ? [undefined, { debugName: "navigate" }] : /* istanbul ignore next */ []));
1267
+ searchInput = viewChild('searchInput', /* @ts-ignore */
1268
+ ...(ngDevMode ? [{ debugName: "searchInput" }] : /* istanbul ignore next */ []));
1269
+ query = signal('', /* @ts-ignore */
1270
+ ...(ngDevMode ? [{ debugName: "query" }] : /* istanbul ignore next */ []));
1271
+ active = signal(0, /* @ts-ignore */
1272
+ ...(ngDevMode ? [{ debugName: "active" }] : /* istanbul ignore next */ []));
1273
+ listboxId = 'lte-cmd-listbox';
1274
+ allItems = computed(() => this.items() ?? flattenMenuToCommands(this.menuItems()), /* @ts-ignore */
1275
+ ...(ngDevMode ? [{ debugName: "allItems" }] : /* istanbul ignore next */ []));
1276
+ results = computed(() => {
1277
+ const q = this.query().trim().toLowerCase();
1278
+ if (!q)
1279
+ return this.allItems();
1280
+ return this.allItems().filter((i) => i.label.toLowerCase().includes(q) || (i.group?.toLowerCase().includes(q) ?? false));
1281
+ }, /* @ts-ignore */
1282
+ ...(ngDevMode ? [{ debugName: "results" }] : /* istanbul ignore next */ []));
1283
+ constructor() {
1284
+ // Reset + focus when the palette opens; reset active on query change.
1285
+ effect(() => {
1286
+ if (!this.palette.isOpen())
1287
+ return;
1288
+ this.query.set('');
1289
+ this.active.set(0);
1290
+ queueMicrotask(() => setTimeout(() => this.searchInput()?.nativeElement.focus(), 20));
1291
+ });
1292
+ effect(() => {
1293
+ this.query();
1294
+ this.active.set(0);
1295
+ });
1296
+ }
1297
+ optionId(idx) {
1298
+ return `${this.listboxId}-opt-${idx}`;
1299
+ }
1300
+ icon(value) {
1301
+ return biClass(value) || 'bi-arrow-return-right';
1302
+ }
1303
+ onQuery(e) {
1304
+ this.query.set(e.target.value);
1305
+ }
1306
+ onBackdrop(e) {
1307
+ if (e.target === e.currentTarget)
1308
+ this.palette.close();
1309
+ }
1310
+ onKeydown(e) {
1311
+ switch (e.key) {
1312
+ case 'ArrowDown':
1313
+ e.preventDefault();
1314
+ this.active.update((i) => Math.min(i + 1, this.results().length - 1));
1315
+ break;
1316
+ case 'ArrowUp':
1317
+ e.preventDefault();
1318
+ this.active.update((i) => Math.max(i - 1, 0));
1319
+ break;
1320
+ case 'Enter':
1321
+ e.preventDefault();
1322
+ this.go();
1323
+ break;
1324
+ case 'Escape':
1325
+ e.preventDefault();
1326
+ this.palette.close();
1327
+ break;
1328
+ }
1329
+ }
1330
+ go(item) {
1331
+ const target = item ?? this.results()[this.active()];
1332
+ if (!target)
1333
+ return;
1334
+ this.palette.close();
1335
+ const override = this.navigate();
1336
+ if (override) {
1337
+ override(target.href);
1338
+ }
1339
+ else if (target.href.startsWith('http')) {
1340
+ if (typeof window !== 'undefined')
1341
+ window.location.assign(target.href);
1342
+ }
1343
+ else if (this.router) {
1344
+ void this.router.navigateByUrl(target.href);
1345
+ }
1346
+ else if (typeof window !== 'undefined') {
1347
+ window.location.assign(target.href);
1348
+ }
1349
+ }
1350
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: CommandPaletteComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1351
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.3", type: CommandPaletteComponent, isStandalone: true, selector: "lte-command-palette", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, menuItems: { classPropertyName: "menuItems", publicName: "menuItems", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, navigate: { classPropertyName: "navigate", publicName: "navigate", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "searchInput", first: true, predicate: ["searchInput"], descendants: true, isSignal: true }], ngImport: i0, template: `
1352
+ @if (palette.isOpen()) {
1353
+ <div class="lte-cmd-overlay" (mousedown)="onBackdrop($event)">
1354
+ <div class="card shadow-lg lte-cmd-panel" role="dialog" aria-modal="true" aria-label="Command palette">
1355
+ <div class="input-group input-group-lg border-bottom">
1356
+ <span class="input-group-text bg-body border-0"><i class="bi bi-search"></i></span>
1357
+ <input
1358
+ #searchInput
1359
+ class="form-control border-0 shadow-none"
1360
+ [placeholder]="placeholder()"
1361
+ aria-label="Search"
1362
+ role="combobox"
1363
+ aria-expanded="true"
1364
+ aria-autocomplete="list"
1365
+ [attr.aria-controls]="listboxId"
1366
+ [attr.aria-activedescendant]="results().length ? optionId(active()) : null"
1367
+ [value]="query()"
1368
+ (input)="onQuery($event)"
1369
+ (keydown)="onKeydown($event)"
1370
+ />
1371
+ </div>
1372
+
1373
+ <div [id]="listboxId" role="listbox" class="list-group list-group-flush lte-cmd-list">
1374
+ @if (results().length === 0) {
1375
+ <div class="text-secondary text-center py-4 small">No results for "{{ query() }}"</div>
1376
+ }
1377
+ @for (item of results(); track item.href; let idx = $index) {
1378
+ <button
1379
+ [id]="optionId(idx)"
1380
+ type="button"
1381
+ role="option"
1382
+ [attr.aria-selected]="idx === active()"
1383
+ class="list-group-item list-group-item-action d-flex align-items-center gap-2"
1384
+ [class.active]="idx === active()"
1385
+ (mouseenter)="active.set(idx)"
1386
+ (click)="go(item)"
1387
+ >
1388
+ <i class="bi {{ icon(item.icon) }}"></i>
1389
+ <span class="flex-grow-1 text-start">{{ item.label }}</span>
1390
+ @if (item.group) {
1391
+ <span class="badge fw-normal" [class.text-bg-light]="idx === active()" [class.text-bg-secondary]="idx !== active()">{{ item.group }}</span>
1392
+ }
1393
+ </button>
1394
+ }
1395
+ </div>
1396
+
1397
+ <div class="card-footer d-flex justify-content-between align-items-center text-secondary small py-2">
1398
+ <span><kbd>&uarr;</kbd> <kbd>&darr;</kbd> navigate</span>
1399
+ <span><kbd>&crarr;</kbd> open &middot; <kbd>esc</kbd> close</span>
1400
+ </div>
1401
+ </div>
1402
+ </div>
1403
+ }
1404
+ `, isInline: true, styles: [".lte-cmd-overlay{position:fixed;inset:0;z-index:1080;display:flex;align-items:flex-start;justify-content:center;padding-top:12vh;background:#00000080}.lte-cmd-panel{width:min(640px,92vw);max-height:70vh;overflow:hidden;display:flex;flex-direction:column}.lte-cmd-list{overflow-y:auto}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1405
+ }
1406
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: CommandPaletteComponent, decorators: [{
1407
+ type: Component,
1408
+ args: [{ selector: 'lte-command-palette', changeDetection: ChangeDetectionStrategy.OnPush, template: `
1409
+ @if (palette.isOpen()) {
1410
+ <div class="lte-cmd-overlay" (mousedown)="onBackdrop($event)">
1411
+ <div class="card shadow-lg lte-cmd-panel" role="dialog" aria-modal="true" aria-label="Command palette">
1412
+ <div class="input-group input-group-lg border-bottom">
1413
+ <span class="input-group-text bg-body border-0"><i class="bi bi-search"></i></span>
1414
+ <input
1415
+ #searchInput
1416
+ class="form-control border-0 shadow-none"
1417
+ [placeholder]="placeholder()"
1418
+ aria-label="Search"
1419
+ role="combobox"
1420
+ aria-expanded="true"
1421
+ aria-autocomplete="list"
1422
+ [attr.aria-controls]="listboxId"
1423
+ [attr.aria-activedescendant]="results().length ? optionId(active()) : null"
1424
+ [value]="query()"
1425
+ (input)="onQuery($event)"
1426
+ (keydown)="onKeydown($event)"
1427
+ />
1428
+ </div>
1429
+
1430
+ <div [id]="listboxId" role="listbox" class="list-group list-group-flush lte-cmd-list">
1431
+ @if (results().length === 0) {
1432
+ <div class="text-secondary text-center py-4 small">No results for "{{ query() }}"</div>
1433
+ }
1434
+ @for (item of results(); track item.href; let idx = $index) {
1435
+ <button
1436
+ [id]="optionId(idx)"
1437
+ type="button"
1438
+ role="option"
1439
+ [attr.aria-selected]="idx === active()"
1440
+ class="list-group-item list-group-item-action d-flex align-items-center gap-2"
1441
+ [class.active]="idx === active()"
1442
+ (mouseenter)="active.set(idx)"
1443
+ (click)="go(item)"
1444
+ >
1445
+ <i class="bi {{ icon(item.icon) }}"></i>
1446
+ <span class="flex-grow-1 text-start">{{ item.label }}</span>
1447
+ @if (item.group) {
1448
+ <span class="badge fw-normal" [class.text-bg-light]="idx === active()" [class.text-bg-secondary]="idx !== active()">{{ item.group }}</span>
1449
+ }
1450
+ </button>
1451
+ }
1452
+ </div>
1453
+
1454
+ <div class="card-footer d-flex justify-content-between align-items-center text-secondary small py-2">
1455
+ <span><kbd>&uarr;</kbd> <kbd>&darr;</kbd> navigate</span>
1456
+ <span><kbd>&crarr;</kbd> open &middot; <kbd>esc</kbd> close</span>
1457
+ </div>
1458
+ </div>
1459
+ </div>
1460
+ }
1461
+ `, styles: [".lte-cmd-overlay{position:fixed;inset:0;z-index:1080;display:flex;align-items:flex-start;justify-content:center;padding-top:12vh;background:#00000080}.lte-cmd-panel{width:min(640px,92vw);max-height:70vh;overflow:hidden;display:flex;flex-direction:column}.lte-cmd-list{overflow-y:auto}\n"] }]
1462
+ }], ctorParameters: () => [], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }], menuItems: [{ type: i0.Input, args: [{ isSignal: true, alias: "menuItems", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], navigate: [{ type: i0.Input, args: [{ isSignal: true, alias: "navigate", required: false }] }], searchInput: [{ type: i0.ViewChild, args: ['searchInput', { isSignal: true }] }] } });
1463
+
1464
+ /**
1465
+ * The full dashboard shell: fixed topbar + off-canvas sidebar + main content
1466
+ * region + footer, with the ⌘K command palette wired in. Configures the shared
1467
+ * {@link SidebarService} (responsive state + body classes) and initializes the
1468
+ * {@link ColorModeService}. Project your page content into the default slot,
1469
+ * and use `[footer]` for footer content.
1470
+ */
1471
+ class DashboardLayoutComponent {
1472
+ sidebar = inject(SidebarService);
1473
+ colorMode = inject(ColorModeService);
1474
+ destroyRef = inject(DestroyRef);
1475
+ menuItems = input.required(/* @ts-ignore */
1476
+ ...(ngDevMode ? [{ debugName: "menuItems" }] : /* istanbul ignore next */ []));
1477
+ logo = input(/* @ts-ignore */
1478
+ ...(ngDevMode ? [undefined, { debugName: "logo" }] : /* istanbul ignore next */ []));
1479
+ logoHref = input('/', /* @ts-ignore */
1480
+ ...(ngDevMode ? [{ debugName: "logoHref" }] : /* istanbul ignore next */ []));
1481
+ brandText = input('AdminLTE 4', /* @ts-ignore */
1482
+ ...(ngDevMode ? [{ debugName: "brandText" }] : /* istanbul ignore next */ []));
1483
+ user = input(/* @ts-ignore */
1484
+ ...(ngDevMode ? [undefined, { debugName: "user" }] : /* istanbul ignore next */ []));
1485
+ sidebarTheme = input('dark', /* @ts-ignore */
1486
+ ...(ngDevMode ? [{ debugName: "sidebarTheme" }] : /* istanbul ignore next */ []));
1487
+ sidebarClass = input('bg-body-secondary shadow', /* @ts-ignore */
1488
+ ...(ngDevMode ? [{ debugName: "sidebarClass" }] : /* istanbul ignore next */ []));
1489
+ sidebarBreakpoint = input('lg', /* @ts-ignore */
1490
+ ...(ngDevMode ? [{ debugName: "sidebarBreakpoint" }] : /* istanbul ignore next */ []));
1491
+ sidebarMini = input(false, /* @ts-ignore */
1492
+ ...(ngDevMode ? [{ debugName: "sidebarMini" }] : /* istanbul ignore next */ []));
1493
+ fixedHeader = input(false, /* @ts-ignore */
1494
+ ...(ngDevMode ? [{ debugName: "fixedHeader" }] : /* istanbul ignore next */ []));
1495
+ fixedSidebar = input(false, /* @ts-ignore */
1496
+ ...(ngDevMode ? [{ debugName: "fixedSidebar" }] : /* istanbul ignore next */ []));
1497
+ fixedFooter = input(false, /* @ts-ignore */
1498
+ ...(ngDevMode ? [{ debugName: "fixedFooter" }] : /* istanbul ignore next */ []));
1499
+ layoutFixed = input(true, /* @ts-ignore */
1500
+ ...(ngDevMode ? [{ debugName: "layoutFixed" }] : /* istanbul ignore next */ []));
1501
+ colorModeToggle = input(true, /* @ts-ignore */
1502
+ ...(ngDevMode ? [{ debugName: "colorModeToggle" }] : /* istanbul ignore next */ []));
1503
+ initialColorMode = input('auto', /* @ts-ignore */
1504
+ ...(ngDevMode ? [{ debugName: "initialColorMode" }] : /* istanbul ignore next */ []));
1505
+ enableSidebarPersistence = input(false, /* @ts-ignore */
1506
+ ...(ngDevMode ? [{ debugName: "enableSidebarPersistence" }] : /* istanbul ignore next */ []));
1507
+ navbarClass = input('', /* @ts-ignore */
1508
+ ...(ngDevMode ? [{ debugName: "navbarClass" }] : /* istanbul ignore next */ []));
1509
+ bodyClass = input('', /* @ts-ignore */
1510
+ ...(ngDevMode ? [{ debugName: "bodyClass" }] : /* istanbul ignore next */ []));
1511
+ currentPath = input('/', /* @ts-ignore */
1512
+ ...(ngDevMode ? [{ debugName: "currentPath" }] : /* istanbul ignore next */ []));
1513
+ accordion = input(false, /* @ts-ignore */
1514
+ ...(ngDevMode ? [{ debugName: "accordion" }] : /* istanbul ignore next */ []));
1515
+ logout = output();
1516
+ profile = output();
1517
+ /**
1518
+ * Optional command-palette navigation override. When omitted the palette uses
1519
+ * the Angular Router. Provided as an input function (mirrors `LteCommandPalette`).
1520
+ */
1521
+ navigate = input(/* @ts-ignore */
1522
+ ...(ngDevMode ? [undefined, { debugName: "navigate" }] : /* istanbul ignore next */ []));
1523
+ staticBodyClasses = computed(() => cn(this.layoutFixed() && 'layout-fixed', `sidebar-expand-${this.sidebarBreakpoint()}`, this.fixedHeader() && 'fixed-header', this.fixedSidebar() && 'fixed-sidebar', this.fixedFooter() && 'fixed-footer', 'bg-body-tertiary', this.bodyClass()), /* @ts-ignore */
1524
+ ...(ngDevMode ? [{ debugName: "staticBodyClasses" }] : /* istanbul ignore next */ []));
1525
+ constructor() {
1526
+ // Seed the color mode from the layout's initial preference (a persisted
1527
+ // user choice always wins — see ColorModeService.setInitialMode).
1528
+ this.colorMode.setInitialMode(this.initialColorMode());
1529
+ // Keep the sidebar service configured with the current inputs.
1530
+ effect(() => {
1531
+ this.sidebar.configure({
1532
+ sidebarMini: this.sidebarMini(),
1533
+ enablePersistence: this.enableSidebarPersistence(),
1534
+ breakpoint: this.sidebarBreakpoint(),
1535
+ staticBodyClasses: this.staticBodyClasses(),
1536
+ });
1537
+ });
1538
+ // Clean up the body classes when the dashboard layout is destroyed so they
1539
+ // don't leak onto e.g. an auth page.
1540
+ this.destroyRef.onDestroy(() => this.sidebar.reset());
1541
+ }
1542
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: DashboardLayoutComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1543
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.3", type: DashboardLayoutComponent, isStandalone: true, selector: "lte-dashboard-layout", inputs: { menuItems: { classPropertyName: "menuItems", publicName: "menuItems", isSignal: true, isRequired: true, transformFunction: null }, logo: { classPropertyName: "logo", publicName: "logo", isSignal: true, isRequired: false, transformFunction: null }, logoHref: { classPropertyName: "logoHref", publicName: "logoHref", isSignal: true, isRequired: false, transformFunction: null }, brandText: { classPropertyName: "brandText", publicName: "brandText", isSignal: true, isRequired: false, transformFunction: null }, user: { classPropertyName: "user", publicName: "user", isSignal: true, isRequired: false, transformFunction: null }, sidebarTheme: { classPropertyName: "sidebarTheme", publicName: "sidebarTheme", isSignal: true, isRequired: false, transformFunction: null }, sidebarClass: { classPropertyName: "sidebarClass", publicName: "sidebarClass", isSignal: true, isRequired: false, transformFunction: null }, sidebarBreakpoint: { classPropertyName: "sidebarBreakpoint", publicName: "sidebarBreakpoint", isSignal: true, isRequired: false, transformFunction: null }, sidebarMini: { classPropertyName: "sidebarMini", publicName: "sidebarMini", isSignal: true, isRequired: false, transformFunction: null }, fixedHeader: { classPropertyName: "fixedHeader", publicName: "fixedHeader", isSignal: true, isRequired: false, transformFunction: null }, fixedSidebar: { classPropertyName: "fixedSidebar", publicName: "fixedSidebar", isSignal: true, isRequired: false, transformFunction: null }, fixedFooter: { classPropertyName: "fixedFooter", publicName: "fixedFooter", isSignal: true, isRequired: false, transformFunction: null }, layoutFixed: { classPropertyName: "layoutFixed", publicName: "layoutFixed", isSignal: true, isRequired: false, transformFunction: null }, colorModeToggle: { classPropertyName: "colorModeToggle", publicName: "colorModeToggle", isSignal: true, isRequired: false, transformFunction: null }, initialColorMode: { classPropertyName: "initialColorMode", publicName: "initialColorMode", isSignal: true, isRequired: false, transformFunction: null }, enableSidebarPersistence: { classPropertyName: "enableSidebarPersistence", publicName: "enableSidebarPersistence", isSignal: true, isRequired: false, transformFunction: null }, navbarClass: { classPropertyName: "navbarClass", publicName: "navbarClass", isSignal: true, isRequired: false, transformFunction: null }, bodyClass: { classPropertyName: "bodyClass", publicName: "bodyClass", isSignal: true, isRequired: false, transformFunction: null }, currentPath: { classPropertyName: "currentPath", publicName: "currentPath", isSignal: true, isRequired: false, transformFunction: null }, accordion: { classPropertyName: "accordion", publicName: "accordion", isSignal: true, isRequired: false, transformFunction: null }, navigate: { classPropertyName: "navigate", publicName: "navigate", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { logout: "logout", profile: "profile" }, host: { classAttribute: "app-wrapper" }, ngImport: i0, template: `
1544
+ <lte-topbar
1545
+ [user]="user()"
1546
+ [colorModeToggle]="colorModeToggle()"
1547
+ [navbarClass]="navbarClass()"
1548
+ (logout)="logout.emit()"
1549
+ (profile)="profile.emit()"
1550
+ >
1551
+ <ng-content select="[topbar-start]" topbar-start />
1552
+ <ng-content select="[topbar-end]" topbar-end />
1553
+ </lte-topbar>
1554
+
1555
+ <lte-sidebar
1556
+ [items]="menuItems()"
1557
+ [logo]="logo()"
1558
+ [logoHref]="logoHref()"
1559
+ [brandText]="brandText()"
1560
+ [theme]="sidebarTheme()"
1561
+ [sidebarClass]="sidebarClass()"
1562
+ [currentPath]="currentPath()"
1563
+ [accordion]="accordion()"
1564
+ />
1565
+
1566
+ <main class="app-main">
1567
+ <ng-content />
1568
+ </main>
1569
+
1570
+ <lte-footer>
1571
+ <ng-content select="[footer]" />
1572
+ </lte-footer>
1573
+
1574
+ <lte-command-palette [menuItems]="menuItems()" [navigate]="navigate()" />
1575
+ `, isInline: true, dependencies: [{ kind: "component", type: TopbarComponent, selector: "lte-topbar", inputs: ["user", "colorModeToggle", "search", "fullscreen", "navbarClass"], outputs: ["logout", "profile"] }, { kind: "component", type: SidebarComponent, selector: "lte-sidebar", inputs: ["items", "logo", "logoHref", "brandText", "theme", "sidebarClass", "currentPath", "accordion", "animationSpeed"] }, { kind: "component", type: FooterComponent, selector: "lte-footer", inputs: ["rightText", "year"] }, { kind: "component", type: CommandPaletteComponent, selector: "lte-command-palette", inputs: ["items", "menuItems", "placeholder", "navigate"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1576
+ }
1577
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: DashboardLayoutComponent, decorators: [{
1578
+ type: Component,
1579
+ args: [{
1580
+ selector: 'lte-dashboard-layout',
1581
+ changeDetection: ChangeDetectionStrategy.OnPush,
1582
+ imports: [TopbarComponent, SidebarComponent, FooterComponent, CommandPaletteComponent],
1583
+ host: { class: 'app-wrapper' },
1584
+ template: `
1585
+ <lte-topbar
1586
+ [user]="user()"
1587
+ [colorModeToggle]="colorModeToggle()"
1588
+ [navbarClass]="navbarClass()"
1589
+ (logout)="logout.emit()"
1590
+ (profile)="profile.emit()"
1591
+ >
1592
+ <ng-content select="[topbar-start]" topbar-start />
1593
+ <ng-content select="[topbar-end]" topbar-end />
1594
+ </lte-topbar>
1595
+
1596
+ <lte-sidebar
1597
+ [items]="menuItems()"
1598
+ [logo]="logo()"
1599
+ [logoHref]="logoHref()"
1600
+ [brandText]="brandText()"
1601
+ [theme]="sidebarTheme()"
1602
+ [sidebarClass]="sidebarClass()"
1603
+ [currentPath]="currentPath()"
1604
+ [accordion]="accordion()"
1605
+ />
1606
+
1607
+ <main class="app-main">
1608
+ <ng-content />
1609
+ </main>
1610
+
1611
+ <lte-footer>
1612
+ <ng-content select="[footer]" />
1613
+ </lte-footer>
1614
+
1615
+ <lte-command-palette [menuItems]="menuItems()" [navigate]="navigate()" />
1616
+ `,
1617
+ }]
1618
+ }], ctorParameters: () => [], propDecorators: { menuItems: [{ type: i0.Input, args: [{ isSignal: true, alias: "menuItems", required: true }] }], logo: [{ type: i0.Input, args: [{ isSignal: true, alias: "logo", required: false }] }], logoHref: [{ type: i0.Input, args: [{ isSignal: true, alias: "logoHref", required: false }] }], brandText: [{ type: i0.Input, args: [{ isSignal: true, alias: "brandText", required: false }] }], user: [{ type: i0.Input, args: [{ isSignal: true, alias: "user", required: false }] }], sidebarTheme: [{ type: i0.Input, args: [{ isSignal: true, alias: "sidebarTheme", required: false }] }], sidebarClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "sidebarClass", required: false }] }], sidebarBreakpoint: [{ type: i0.Input, args: [{ isSignal: true, alias: "sidebarBreakpoint", required: false }] }], sidebarMini: [{ type: i0.Input, args: [{ isSignal: true, alias: "sidebarMini", required: false }] }], fixedHeader: [{ type: i0.Input, args: [{ isSignal: true, alias: "fixedHeader", required: false }] }], fixedSidebar: [{ type: i0.Input, args: [{ isSignal: true, alias: "fixedSidebar", required: false }] }], fixedFooter: [{ type: i0.Input, args: [{ isSignal: true, alias: "fixedFooter", required: false }] }], layoutFixed: [{ type: i0.Input, args: [{ isSignal: true, alias: "layoutFixed", required: false }] }], colorModeToggle: [{ type: i0.Input, args: [{ isSignal: true, alias: "colorModeToggle", required: false }] }], initialColorMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "initialColorMode", required: false }] }], enableSidebarPersistence: [{ type: i0.Input, args: [{ isSignal: true, alias: "enableSidebarPersistence", required: false }] }], navbarClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "navbarClass", required: false }] }], bodyClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "bodyClass", required: false }] }], currentPath: [{ type: i0.Input, args: [{ isSignal: true, alias: "currentPath", required: false }] }], accordion: [{ type: i0.Input, args: [{ isSignal: true, alias: "accordion", required: false }] }], logout: [{ type: i0.Output, args: ["logout"] }], profile: [{ type: i0.Output, args: ["profile"] }], navigate: [{ type: i0.Input, args: [{ isSignal: true, alias: "navigate", required: false }] }] } });
1619
+
1620
+ /**
1621
+ * Centered card layout for login / register pages. Applies the page-level body
1622
+ * classes (`login-page` / `register-page` + `bg-body-secondary`) while mounted
1623
+ * and removes them on destroy. Project the form into the default slot, and
1624
+ * optionally a custom brand into `[logo]`.
1625
+ */
1626
+ class AuthLayoutComponent {
1627
+ doc;
1628
+ isBrowser;
1629
+ appliedClasses = [];
1630
+ authType = input('login', /* @ts-ignore */
1631
+ ...(ngDevMode ? [{ debugName: "authType" }] : /* istanbul ignore next */ []));
1632
+ variant = input('default', /* @ts-ignore */
1633
+ ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
1634
+ logo = input(/* @ts-ignore */
1635
+ ...(ngDevMode ? [undefined, { debugName: "logo" }] : /* istanbul ignore next */ []));
1636
+ logoHref = input('/', /* @ts-ignore */
1637
+ ...(ngDevMode ? [{ debugName: "logoHref" }] : /* istanbul ignore next */ []));
1638
+ bodyClasses = computed(() => [`${this.authType()}-page`, 'bg-body-secondary'], /* @ts-ignore */
1639
+ ...(ngDevMode ? [{ debugName: "bodyClasses" }] : /* istanbul ignore next */ []));
1640
+ constructor(platformId, doc, destroyRef) {
1641
+ this.doc = doc;
1642
+ this.isBrowser = isPlatformBrowser(platformId);
1643
+ effect(() => {
1644
+ if (!this.isBrowser)
1645
+ return;
1646
+ const next = this.bodyClasses();
1647
+ for (const c of this.appliedClasses)
1648
+ this.doc.body.classList.remove(c);
1649
+ for (const c of next)
1650
+ this.doc.body.classList.add(c);
1651
+ this.appliedClasses = next;
1652
+ });
1653
+ destroyRef.onDestroy(() => {
1654
+ if (!this.isBrowser)
1655
+ return;
1656
+ for (const c of this.appliedClasses)
1657
+ this.doc.body.classList.remove(c);
1658
+ this.appliedClasses = [];
1659
+ });
1660
+ }
1661
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: AuthLayoutComponent, deps: [{ token: PLATFORM_ID }, { token: DOCUMENT }, { token: i0.DestroyRef }], target: i0.ɵɵFactoryTarget.Component });
1662
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.3", type: AuthLayoutComponent, isStandalone: true, selector: "lte-auth-layout", inputs: { authType: { classPropertyName: "authType", publicName: "authType", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, logo: { classPropertyName: "logo", publicName: "logo", isSignal: true, isRequired: false, transformFunction: null }, logoHref: { classPropertyName: "logoHref", publicName: "logoHref", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
1663
+ <div [class]="authType() + '-box'">
1664
+ @if (variant() === 'v2') {
1665
+ <div class="card card-outline card-primary">
1666
+ <div class="card-header">
1667
+ <a [routerLink]="logoHref()" class="link-dark text-center link-offset-2 link-opacity-100 link-opacity-50-hover">
1668
+ @if (logo()) {
1669
+ <img [src]="logo()" alt="Logo" height="48" />
1670
+ } @else {
1671
+ <h1 class="mb-0"><b>Admin</b>LTE</h1>
1672
+ }
1673
+ <ng-content select="[logo]" />
1674
+ </a>
1675
+ </div>
1676
+ <div [class]="'card-body ' + authType() + '-card-body'">
1677
+ <ng-content />
1678
+ </div>
1679
+ </div>
1680
+ } @else {
1681
+ <div [class]="authType() + '-logo'">
1682
+ <a [routerLink]="logoHref()">
1683
+ @if (logo()) {
1684
+ <img [src]="logo()" alt="Logo" height="48" />
1685
+ } @else {
1686
+ <b>Admin</b>LTE
1687
+ }
1688
+ <ng-content select="[logo]" />
1689
+ </a>
1690
+ </div>
1691
+ <div class="card">
1692
+ <div [class]="'card-body ' + authType() + '-card-body'">
1693
+ <ng-content />
1694
+ </div>
1695
+ </div>
1696
+ }
1697
+ </div>
1698
+ `, isInline: true, dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "browserUrl", "routerLink"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1699
+ }
1700
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: AuthLayoutComponent, decorators: [{
1701
+ type: Component,
1702
+ args: [{
1703
+ selector: 'lte-auth-layout',
1704
+ changeDetection: ChangeDetectionStrategy.OnPush,
1705
+ imports: [RouterLink],
1706
+ template: `
1707
+ <div [class]="authType() + '-box'">
1708
+ @if (variant() === 'v2') {
1709
+ <div class="card card-outline card-primary">
1710
+ <div class="card-header">
1711
+ <a [routerLink]="logoHref()" class="link-dark text-center link-offset-2 link-opacity-100 link-opacity-50-hover">
1712
+ @if (logo()) {
1713
+ <img [src]="logo()" alt="Logo" height="48" />
1714
+ } @else {
1715
+ <h1 class="mb-0"><b>Admin</b>LTE</h1>
1716
+ }
1717
+ <ng-content select="[logo]" />
1718
+ </a>
1719
+ </div>
1720
+ <div [class]="'card-body ' + authType() + '-card-body'">
1721
+ <ng-content />
1722
+ </div>
1723
+ </div>
1724
+ } @else {
1725
+ <div [class]="authType() + '-logo'">
1726
+ <a [routerLink]="logoHref()">
1727
+ @if (logo()) {
1728
+ <img [src]="logo()" alt="Logo" height="48" />
1729
+ } @else {
1730
+ <b>Admin</b>LTE
1731
+ }
1732
+ <ng-content select="[logo]" />
1733
+ </a>
1734
+ </div>
1735
+ <div class="card">
1736
+ <div [class]="'card-body ' + authType() + '-card-body'">
1737
+ <ng-content />
1738
+ </div>
1739
+ </div>
1740
+ }
1741
+ </div>
1742
+ `,
1743
+ }]
1744
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
1745
+ type: Inject,
1746
+ args: [PLATFORM_ID]
1747
+ }] }, { type: Document, decorators: [{
1748
+ type: Inject,
1749
+ args: [DOCUMENT]
1750
+ }] }, { type: i0.DestroyRef }], propDecorators: { authType: [{ type: i0.Input, args: [{ isSignal: true, alias: "authType", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], logo: [{ type: i0.Input, args: [{ isSignal: true, alias: "logo", required: false }] }], logoHref: [{ type: i0.Input, args: [{ isSignal: true, alias: "logoHref", required: false }] }] } });
1751
+
1752
+ /**
1753
+ * Page content wrapper with an optional header row (title + breadcrumbs) above a
1754
+ * `container`/`container-fluid` body. Project the page body into the default slot.
1755
+ */
1756
+ class AppContentComponent {
1757
+ title = input(/* @ts-ignore */
1758
+ ...(ngDevMode ? [undefined, { debugName: "title" }] : /* istanbul ignore next */ []));
1759
+ breadcrumbs = input([], /* @ts-ignore */
1760
+ ...(ngDevMode ? [{ debugName: "breadcrumbs" }] : /* istanbul ignore next */ []));
1761
+ fluid = input(true, /* @ts-ignore */
1762
+ ...(ngDevMode ? [{ debugName: "fluid" }] : /* istanbul ignore next */ []));
1763
+ containerClass = computed(() => (this.fluid() ? 'container-fluid' : 'container-lg'), /* @ts-ignore */
1764
+ ...(ngDevMode ? [{ debugName: "containerClass" }] : /* istanbul ignore next */ []));
1765
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: AppContentComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1766
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.3", type: AppContentComponent, isStandalone: true, selector: "lte-app-content", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, breadcrumbs: { classPropertyName: "breadcrumbs", publicName: "breadcrumbs", isSignal: true, isRequired: false, transformFunction: null }, fluid: { classPropertyName: "fluid", publicName: "fluid", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
1767
+ @if (title() || breadcrumbs().length) {
1768
+ <div class="app-content-header">
1769
+ <div [class]="containerClass()">
1770
+ <div class="row">
1771
+ <div class="col-sm-6">
1772
+ @if (title()) {
1773
+ <h3 class="mb-0">{{ title() }}</h3>
1774
+ }
1775
+ </div>
1776
+ @if (breadcrumbs().length) {
1777
+ <div class="col-sm-6">
1778
+ <ol class="breadcrumb float-sm-end">
1779
+ @for (crumb of breadcrumbs(); track $index; let last = $last) {
1780
+ <li class="breadcrumb-item" [class.active]="last" [attr.aria-current]="last ? 'page' : null">
1781
+ @if ((crumb.route || crumb.href) && !last) {
1782
+ @if (crumb.route) {
1783
+ <a [routerLink]="crumb.route">{{ crumb.label }}</a>
1784
+ } @else {
1785
+ <a [href]="crumb.href">{{ crumb.label }}</a>
1786
+ }
1787
+ } @else {
1788
+ {{ crumb.label }}
1789
+ }
1790
+ </li>
1791
+ }
1792
+ </ol>
1793
+ </div>
1794
+ }
1795
+ </div>
1796
+ </div>
1797
+ </div>
1798
+ }
1799
+
1800
+ <div class="app-content">
1801
+ <div [class]="containerClass()">
1802
+ <ng-content />
1803
+ </div>
1804
+ </div>
1805
+ `, isInline: true, dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "browserUrl", "routerLink"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1806
+ }
1807
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: AppContentComponent, decorators: [{
1808
+ type: Component,
1809
+ args: [{
1810
+ selector: 'lte-app-content',
1811
+ changeDetection: ChangeDetectionStrategy.OnPush,
1812
+ imports: [RouterLink],
1813
+ template: `
1814
+ @if (title() || breadcrumbs().length) {
1815
+ <div class="app-content-header">
1816
+ <div [class]="containerClass()">
1817
+ <div class="row">
1818
+ <div class="col-sm-6">
1819
+ @if (title()) {
1820
+ <h3 class="mb-0">{{ title() }}</h3>
1821
+ }
1822
+ </div>
1823
+ @if (breadcrumbs().length) {
1824
+ <div class="col-sm-6">
1825
+ <ol class="breadcrumb float-sm-end">
1826
+ @for (crumb of breadcrumbs(); track $index; let last = $last) {
1827
+ <li class="breadcrumb-item" [class.active]="last" [attr.aria-current]="last ? 'page' : null">
1828
+ @if ((crumb.route || crumb.href) && !last) {
1829
+ @if (crumb.route) {
1830
+ <a [routerLink]="crumb.route">{{ crumb.label }}</a>
1831
+ } @else {
1832
+ <a [href]="crumb.href">{{ crumb.label }}</a>
1833
+ }
1834
+ } @else {
1835
+ {{ crumb.label }}
1836
+ }
1837
+ </li>
1838
+ }
1839
+ </ol>
1840
+ </div>
1841
+ }
1842
+ </div>
1843
+ </div>
1844
+ </div>
1845
+ }
1846
+
1847
+ <div class="app-content">
1848
+ <div [class]="containerClass()">
1849
+ <ng-content />
1850
+ </div>
1851
+ </div>
1852
+ `,
1853
+ }]
1854
+ }], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], breadcrumbs: [{ type: i0.Input, args: [{ isSignal: true, alias: "breadcrumbs", required: false }] }], fluid: [{ type: i0.Input, args: [{ isSignal: true, alias: "fluid", required: false }] }] } });
1855
+
1856
+ /**
1857
+ * AdminLTE card with optional collapse / maximize / remove tools. `variant`
1858
+ * controls the color treatment: `default` (colored header), `outline` (colored
1859
+ * top border) or `solid` (fully colored). Project the body into the default
1860
+ * slot, an optional footer into `[footer]`, and extra tools into `[tools]`.
1861
+ */
1862
+ class CardComponent {
1863
+ doc = inject(DOCUMENT);
1864
+ title = input(/* @ts-ignore */
1865
+ ...(ngDevMode ? [undefined, { debugName: "title" }] : /* istanbul ignore next */ []));
1866
+ icon = input(/* @ts-ignore */
1867
+ ...(ngDevMode ? [undefined, { debugName: "icon" }] : /* istanbul ignore next */ []));
1868
+ theme = input(/* @ts-ignore */
1869
+ ...(ngDevMode ? [undefined, { debugName: "theme" }] : /* istanbul ignore next */ []));
1870
+ variant = input('default', /* @ts-ignore */
1871
+ ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
1872
+ gradient = input(false, /* @ts-ignore */
1873
+ ...(ngDevMode ? [{ debugName: "gradient" }] : /* istanbul ignore next */ []));
1874
+ collapsible = input(false, /* @ts-ignore */
1875
+ ...(ngDevMode ? [{ debugName: "collapsible" }] : /* istanbul ignore next */ []));
1876
+ defaultCollapsed = input(false, /* @ts-ignore */
1877
+ ...(ngDevMode ? [{ debugName: "defaultCollapsed" }] : /* istanbul ignore next */ []));
1878
+ removable = input(false, /* @ts-ignore */
1879
+ ...(ngDevMode ? [{ debugName: "removable" }] : /* istanbul ignore next */ []));
1880
+ maximizable = input(false, /* @ts-ignore */
1881
+ ...(ngDevMode ? [{ debugName: "maximizable" }] : /* istanbul ignore next */ []));
1882
+ hasFooter = input(false, /* @ts-ignore */
1883
+ ...(ngDevMode ? [{ debugName: "hasFooter" }] : /* istanbul ignore next */ []));
1884
+ bodyClass = input('', /* @ts-ignore */
1885
+ ...(ngDevMode ? [{ debugName: "bodyClass" }] : /* istanbul ignore next */ []));
1886
+ headerClass = input('', /* @ts-ignore */
1887
+ ...(ngDevMode ? [{ debugName: "headerClass" }] : /* istanbul ignore next */ []));
1888
+ footerClass = input('', /* @ts-ignore */
1889
+ ...(ngDevMode ? [{ debugName: "footerClass" }] : /* istanbul ignore next */ []));
1890
+ isCollapsed = signal(false, /* @ts-ignore */
1891
+ ...(ngDevMode ? [{ debugName: "isCollapsed" }] : /* istanbul ignore next */ []));
1892
+ isMaximized = signal(false, /* @ts-ignore */
1893
+ ...(ngDevMode ? [{ debugName: "isMaximized" }] : /* istanbul ignore next */ []));
1894
+ isRemoved = signal(false, /* @ts-ignore */
1895
+ ...(ngDevMode ? [{ debugName: "isRemoved" }] : /* istanbul ignore next */ []));
1896
+ biClass = biClass;
1897
+ cn = cn;
1898
+ hasTools = computed(() => this.collapsible() || this.removable() || this.maximizable(), /* @ts-ignore */
1899
+ ...(ngDevMode ? [{ debugName: "hasTools" }] : /* istanbul ignore next */ []));
1900
+ cardClass = computed(() => {
1901
+ let base = 'card';
1902
+ const theme = this.theme();
1903
+ if (theme) {
1904
+ if (this.variant() === 'outline')
1905
+ base = `card card-outline card-${theme}`;
1906
+ else if (this.variant() === 'solid')
1907
+ base = `card text-bg-${theme}`;
1908
+ else
1909
+ base = `card card-${theme}`;
1910
+ }
1911
+ return cn(base, this.gradient() && theme && 'bg-gradient', this.isCollapsed() && 'collapsed-card', this.isMaximized() && 'maximized-card');
1912
+ }, /* @ts-ignore */
1913
+ ...(ngDevMode ? [{ debugName: "cardClass" }] : /* istanbul ignore next */ []));
1914
+ constructor() {
1915
+ queueMicrotask(() => {
1916
+ if (this.defaultCollapsed())
1917
+ this.isCollapsed.set(true);
1918
+ });
1919
+ }
1920
+ toggleCollapse() {
1921
+ this.isCollapsed.update((v) => !v);
1922
+ }
1923
+ toggleMaximize() {
1924
+ this.isMaximized.update((v) => {
1925
+ const next = !v;
1926
+ this.doc.documentElement.classList.toggle('maximized-card', next);
1927
+ return next;
1928
+ });
1929
+ }
1930
+ remove() {
1931
+ this.isRemoved.set(true);
1932
+ }
1933
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: CardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1934
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.3", type: CardComponent, isStandalone: true, selector: "lte-card", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, theme: { classPropertyName: "theme", publicName: "theme", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, gradient: { classPropertyName: "gradient", publicName: "gradient", isSignal: true, isRequired: false, transformFunction: null }, collapsible: { classPropertyName: "collapsible", publicName: "collapsible", isSignal: true, isRequired: false, transformFunction: null }, defaultCollapsed: { classPropertyName: "defaultCollapsed", publicName: "defaultCollapsed", isSignal: true, isRequired: false, transformFunction: null }, removable: { classPropertyName: "removable", publicName: "removable", isSignal: true, isRequired: false, transformFunction: null }, maximizable: { classPropertyName: "maximizable", publicName: "maximizable", isSignal: true, isRequired: false, transformFunction: null }, hasFooter: { classPropertyName: "hasFooter", publicName: "hasFooter", isSignal: true, isRequired: false, transformFunction: null }, bodyClass: { classPropertyName: "bodyClass", publicName: "bodyClass", isSignal: true, isRequired: false, transformFunction: null }, headerClass: { classPropertyName: "headerClass", publicName: "headerClass", isSignal: true, isRequired: false, transformFunction: null }, footerClass: { classPropertyName: "footerClass", publicName: "footerClass", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
1935
+ @if (!isRemoved()) {
1936
+ <div [class]="cardClass()">
1937
+ @if (title() || icon() || hasTools()) {
1938
+ <div [class]="cn('card-header', headerClass())">
1939
+ <h3 class="card-title">
1940
+ @if (icon()) {
1941
+ <i class="{{ biClass(icon()) }} me-1"></i>
1942
+ }
1943
+ {{ title() }}
1944
+ </h3>
1945
+ <div class="card-tools">
1946
+ <ng-content select="[tools]" />
1947
+ @if (collapsible()) {
1948
+ <button type="button" class="btn btn-tool" [title]="isCollapsed() ? 'Expand' : 'Collapse'" (click)="toggleCollapse()">
1949
+ <i class="bi" [class.bi-plus-lg]="isCollapsed()" [class.bi-dash-lg]="!isCollapsed()"></i>
1950
+ </button>
1951
+ }
1952
+ @if (maximizable()) {
1953
+ <button type="button" class="btn btn-tool" title="Maximize" (click)="toggleMaximize()">
1954
+ <i class="bi" [class.bi-fullscreen-exit]="isMaximized()" [class.bi-fullscreen]="!isMaximized()"></i>
1955
+ </button>
1956
+ }
1957
+ @if (removable()) {
1958
+ <button type="button" class="btn btn-tool" title="Remove" (click)="remove()">
1959
+ <i class="bi bi-x-lg"></i>
1960
+ </button>
1961
+ }
1962
+ </div>
1963
+ </div>
1964
+ }
1965
+
1966
+ @if (!isCollapsed()) {
1967
+ <div [class]="cn('card-body', bodyClass())">
1968
+ <ng-content />
1969
+ </div>
1970
+ <div [class]="cn('card-footer', footerClass())" [hidden]="!hasFooter()">
1971
+ <ng-content select="[footer]" />
1972
+ </div>
1973
+ }
1974
+ </div>
1975
+ }
1976
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
1977
+ }
1978
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: CardComponent, decorators: [{
1979
+ type: Component,
1980
+ args: [{
1981
+ selector: 'lte-card',
1982
+ changeDetection: ChangeDetectionStrategy.OnPush,
1983
+ template: `
1984
+ @if (!isRemoved()) {
1985
+ <div [class]="cardClass()">
1986
+ @if (title() || icon() || hasTools()) {
1987
+ <div [class]="cn('card-header', headerClass())">
1988
+ <h3 class="card-title">
1989
+ @if (icon()) {
1990
+ <i class="{{ biClass(icon()) }} me-1"></i>
1991
+ }
1992
+ {{ title() }}
1993
+ </h3>
1994
+ <div class="card-tools">
1995
+ <ng-content select="[tools]" />
1996
+ @if (collapsible()) {
1997
+ <button type="button" class="btn btn-tool" [title]="isCollapsed() ? 'Expand' : 'Collapse'" (click)="toggleCollapse()">
1998
+ <i class="bi" [class.bi-plus-lg]="isCollapsed()" [class.bi-dash-lg]="!isCollapsed()"></i>
1999
+ </button>
2000
+ }
2001
+ @if (maximizable()) {
2002
+ <button type="button" class="btn btn-tool" title="Maximize" (click)="toggleMaximize()">
2003
+ <i class="bi" [class.bi-fullscreen-exit]="isMaximized()" [class.bi-fullscreen]="!isMaximized()"></i>
2004
+ </button>
2005
+ }
2006
+ @if (removable()) {
2007
+ <button type="button" class="btn btn-tool" title="Remove" (click)="remove()">
2008
+ <i class="bi bi-x-lg"></i>
2009
+ </button>
2010
+ }
2011
+ </div>
2012
+ </div>
2013
+ }
2014
+
2015
+ @if (!isCollapsed()) {
2016
+ <div [class]="cn('card-body', bodyClass())">
2017
+ <ng-content />
2018
+ </div>
2019
+ <div [class]="cn('card-footer', footerClass())" [hidden]="!hasFooter()">
2020
+ <ng-content select="[footer]" />
2021
+ </div>
2022
+ }
2023
+ </div>
2024
+ }
2025
+ `,
2026
+ }]
2027
+ }], ctorParameters: () => [], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], theme: [{ type: i0.Input, args: [{ isSignal: true, alias: "theme", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], gradient: [{ type: i0.Input, args: [{ isSignal: true, alias: "gradient", required: false }] }], collapsible: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsible", required: false }] }], defaultCollapsed: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultCollapsed", required: false }] }], removable: [{ type: i0.Input, args: [{ isSignal: true, alias: "removable", required: false }] }], maximizable: [{ type: i0.Input, args: [{ isSignal: true, alias: "maximizable", required: false }] }], hasFooter: [{ type: i0.Input, args: [{ isSignal: true, alias: "hasFooter", required: false }] }], bodyClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "bodyClass", required: false }] }], headerClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerClass", required: false }] }], footerClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "footerClass", required: false }] }] } });
2028
+
2029
+ /**
2030
+ * Colored stat box (`small-box`) with a big number, label, icon and a "More
2031
+ * info" footer link. Light/warning backgrounds get a dark footer link for
2032
+ * contrast — matching the React/Vue ports.
2033
+ */
2034
+ class SmallBoxComponent {
2035
+ title = input(/* @ts-ignore */
2036
+ ...(ngDevMode ? [undefined, { debugName: "title" }] : /* istanbul ignore next */ []));
2037
+ text = input('', /* @ts-ignore */
2038
+ ...(ngDevMode ? [{ debugName: "text" }] : /* istanbul ignore next */ []));
2039
+ icon = input(/* @ts-ignore */
2040
+ ...(ngDevMode ? [undefined, { debugName: "icon" }] : /* istanbul ignore next */ []));
2041
+ theme = input('primary', /* @ts-ignore */
2042
+ ...(ngDevMode ? [{ debugName: "theme" }] : /* istanbul ignore next */ []));
2043
+ url = input(/* @ts-ignore */
2044
+ ...(ngDevMode ? [undefined, { debugName: "url" }] : /* istanbul ignore next */ []));
2045
+ urlText = input('More info', /* @ts-ignore */
2046
+ ...(ngDevMode ? [{ debugName: "urlText" }] : /* istanbul ignore next */ []));
2047
+ loading = input(false, /* @ts-ignore */
2048
+ ...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
2049
+ linkColor = computed(() => this.theme() === 'warning' || this.theme() === 'light' ? 'link-dark' : 'link-light', /* @ts-ignore */
2050
+ ...(ngDevMode ? [{ debugName: "linkColor" }] : /* istanbul ignore next */ []));
2051
+ footerClass = computed(() => `small-box-footer ${this.linkColor()} link-underline-opacity-0 link-underline-opacity-50-hover`, /* @ts-ignore */
2052
+ ...(ngDevMode ? [{ debugName: "footerClass" }] : /* istanbul ignore next */ []));
2053
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: SmallBoxComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2054
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.3", type: SmallBoxComponent, isStandalone: true, selector: "lte-small-box", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, text: { classPropertyName: "text", publicName: "text", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, theme: { classPropertyName: "theme", publicName: "theme", isSignal: true, isRequired: false, transformFunction: null }, url: { classPropertyName: "url", publicName: "url", isSignal: true, isRequired: false, transformFunction: null }, urlText: { classPropertyName: "urlText", publicName: "urlText", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
2055
+ <div [class]="'small-box text-bg-' + theme()">
2056
+ <div class="inner">
2057
+ <h3>{{ title() }}</h3>
2058
+ <p>{{ text() }}<ng-content /></p>
2059
+ </div>
2060
+ @if (icon()) {
2061
+ <i class="small-box-icon bi {{ icon() }}"></i>
2062
+ }
2063
+ @if (loading()) {
2064
+ <div class="overlay">
2065
+ <i class="bi bi-arrow-repeat spinner-border-sm"></i>
2066
+ </div>
2067
+ }
2068
+ @if (url()) {
2069
+ <a [href]="url()" [class]="footerClass()">
2070
+ {{ urlText() }} <i class="bi bi-link-45deg"></i>
2071
+ </a>
2072
+ }
2073
+ </div>
2074
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
2075
+ }
2076
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: SmallBoxComponent, decorators: [{
2077
+ type: Component,
2078
+ args: [{
2079
+ selector: 'lte-small-box',
2080
+ changeDetection: ChangeDetectionStrategy.OnPush,
2081
+ template: `
2082
+ <div [class]="'small-box text-bg-' + theme()">
2083
+ <div class="inner">
2084
+ <h3>{{ title() }}</h3>
2085
+ <p>{{ text() }}<ng-content /></p>
2086
+ </div>
2087
+ @if (icon()) {
2088
+ <i class="small-box-icon bi {{ icon() }}"></i>
2089
+ }
2090
+ @if (loading()) {
2091
+ <div class="overlay">
2092
+ <i class="bi bi-arrow-repeat spinner-border-sm"></i>
2093
+ </div>
2094
+ }
2095
+ @if (url()) {
2096
+ <a [href]="url()" [class]="footerClass()">
2097
+ {{ urlText() }} <i class="bi bi-link-45deg"></i>
2098
+ </a>
2099
+ }
2100
+ </div>
2101
+ `,
2102
+ }]
2103
+ }], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], text: [{ type: i0.Input, args: [{ isSignal: true, alias: "text", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], theme: [{ type: i0.Input, args: [{ isSignal: true, alias: "theme", required: false }] }], url: [{ type: i0.Input, args: [{ isSignal: true, alias: "url", required: false }] }], urlText: [{ type: i0.Input, args: [{ isSignal: true, alias: "urlText", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }] } });
2104
+
2105
+ /**
2106
+ * Compact info box with a colored icon chip, a label/number, and an optional
2107
+ * progress bar. `variant: 'solid'` colors the whole box.
2108
+ */
2109
+ class InfoBoxComponent {
2110
+ title = input(/* @ts-ignore */
2111
+ ...(ngDevMode ? [undefined, { debugName: "title" }] : /* istanbul ignore next */ []));
2112
+ text = input('', /* @ts-ignore */
2113
+ ...(ngDevMode ? [{ debugName: "text" }] : /* istanbul ignore next */ []));
2114
+ icon = input(/* @ts-ignore */
2115
+ ...(ngDevMode ? [undefined, { debugName: "icon" }] : /* istanbul ignore next */ []));
2116
+ theme = input('info', /* @ts-ignore */
2117
+ ...(ngDevMode ? [{ debugName: "theme" }] : /* istanbul ignore next */ []));
2118
+ iconTheme = input(/* @ts-ignore */
2119
+ ...(ngDevMode ? [undefined, { debugName: "iconTheme" }] : /* istanbul ignore next */ []));
2120
+ variant = input('default', /* @ts-ignore */
2121
+ ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
2122
+ gradient = input(false, /* @ts-ignore */
2123
+ ...(ngDevMode ? [{ debugName: "gradient" }] : /* istanbul ignore next */ []));
2124
+ boxClassExtra = input('', /* @ts-ignore */
2125
+ ...(ngDevMode ? [{ debugName: "boxClassExtra" }] : /* istanbul ignore next */ []));
2126
+ unit = input(/* @ts-ignore */
2127
+ ...(ngDevMode ? [undefined, { debugName: "unit" }] : /* istanbul ignore next */ []));
2128
+ progress = input(/* @ts-ignore */
2129
+ ...(ngDevMode ? [undefined, { debugName: "progress" }] : /* istanbul ignore next */ []));
2130
+ progressText = input(/* @ts-ignore */
2131
+ ...(ngDevMode ? [undefined, { debugName: "progressText" }] : /* istanbul ignore next */ []));
2132
+ biClass = biClass;
2133
+ boxClass = computed(() => cn('info-box', this.variant() === 'solid' && `text-bg-${this.theme()}`, this.variant() === 'solid' && this.gradient() && 'bg-gradient', this.boxClassExtra()), /* @ts-ignore */
2134
+ ...(ngDevMode ? [{ debugName: "boxClass" }] : /* istanbul ignore next */ []));
2135
+ iconClass = computed(() => this.variant() === 'solid'
2136
+ ? 'info-box-icon'
2137
+ : `info-box-icon text-bg-${this.iconTheme() ?? this.theme()} shadow-sm`, /* @ts-ignore */
2138
+ ...(ngDevMode ? [{ debugName: "iconClass" }] : /* istanbul ignore next */ []));
2139
+ barClass = computed(() => this.variant() === 'solid' ? 'progress-bar' : `progress-bar bg-${this.theme()}`, /* @ts-ignore */
2140
+ ...(ngDevMode ? [{ debugName: "barClass" }] : /* istanbul ignore next */ []));
2141
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: InfoBoxComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2142
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.3", type: InfoBoxComponent, isStandalone: true, selector: "lte-info-box", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, text: { classPropertyName: "text", publicName: "text", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, theme: { classPropertyName: "theme", publicName: "theme", isSignal: true, isRequired: false, transformFunction: null }, iconTheme: { classPropertyName: "iconTheme", publicName: "iconTheme", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, gradient: { classPropertyName: "gradient", publicName: "gradient", isSignal: true, isRequired: false, transformFunction: null }, boxClassExtra: { classPropertyName: "boxClassExtra", publicName: "boxClassExtra", isSignal: true, isRequired: false, transformFunction: null }, unit: { classPropertyName: "unit", publicName: "unit", isSignal: true, isRequired: false, transformFunction: null }, progress: { classPropertyName: "progress", publicName: "progress", isSignal: true, isRequired: false, transformFunction: null }, progressText: { classPropertyName: "progressText", publicName: "progressText", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
2143
+ <div [class]="boxClass()">
2144
+ @if (icon()) {
2145
+ <span [class]="iconClass()">
2146
+ <i class="{{ biClass(icon()) }}" aria-hidden="true"></i>
2147
+ </span>
2148
+ }
2149
+ <div class="info-box-content">
2150
+ @if (text()) {
2151
+ <span class="info-box-text">{{ text() }}</span>
2152
+ }
2153
+ @if (title() != null) {
2154
+ <span class="info-box-number">{{ title() }} @if (unit()) {<small>{{ unit() }}</small>}</span>
2155
+ }
2156
+ <ng-content />
2157
+ @if (progress() != null) {
2158
+ <div class="progress">
2159
+ <div [class]="barClass()" [style.width.%]="progress()"></div>
2160
+ </div>
2161
+ @if (progressText()) {
2162
+ <span class="progress-description">{{ progressText() }}</span>
2163
+ }
2164
+ }
2165
+ </div>
2166
+ </div>
2167
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
2168
+ }
2169
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: InfoBoxComponent, decorators: [{
2170
+ type: Component,
2171
+ args: [{
2172
+ selector: 'lte-info-box',
2173
+ changeDetection: ChangeDetectionStrategy.OnPush,
2174
+ template: `
2175
+ <div [class]="boxClass()">
2176
+ @if (icon()) {
2177
+ <span [class]="iconClass()">
2178
+ <i class="{{ biClass(icon()) }}" aria-hidden="true"></i>
2179
+ </span>
2180
+ }
2181
+ <div class="info-box-content">
2182
+ @if (text()) {
2183
+ <span class="info-box-text">{{ text() }}</span>
2184
+ }
2185
+ @if (title() != null) {
2186
+ <span class="info-box-number">{{ title() }} @if (unit()) {<small>{{ unit() }}</small>}</span>
2187
+ }
2188
+ <ng-content />
2189
+ @if (progress() != null) {
2190
+ <div class="progress">
2191
+ <div [class]="barClass()" [style.width.%]="progress()"></div>
2192
+ </div>
2193
+ @if (progressText()) {
2194
+ <span class="progress-description">{{ progressText() }}</span>
2195
+ }
2196
+ }
2197
+ </div>
2198
+ </div>
2199
+ `,
2200
+ }]
2201
+ }], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], text: [{ type: i0.Input, args: [{ isSignal: true, alias: "text", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], theme: [{ type: i0.Input, args: [{ isSignal: true, alias: "theme", required: false }] }], iconTheme: [{ type: i0.Input, args: [{ isSignal: true, alias: "iconTheme", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], gradient: [{ type: i0.Input, args: [{ isSignal: true, alias: "gradient", required: false }] }], boxClassExtra: [{ type: i0.Input, args: [{ isSignal: true, alias: "boxClassExtra", required: false }] }], unit: [{ type: i0.Input, args: [{ isSignal: true, alias: "unit", required: false }] }], progress: [{ type: i0.Input, args: [{ isSignal: true, alias: "progress", required: false }] }], progressText: [{ type: i0.Input, args: [{ isSignal: true, alias: "progressText", required: false }] }] } });
2202
+
2203
+ /**
2204
+ * Bootstrap alert with optional icon, title and dismiss button. `show` is a
2205
+ * two-way model so it can be controlled (`[(show)]`) or left uncontrolled.
2206
+ */
2207
+ class AlertComponent {
2208
+ theme = input('info', /* @ts-ignore */
2209
+ ...(ngDevMode ? [{ debugName: "theme" }] : /* istanbul ignore next */ []));
2210
+ icon = input(/* @ts-ignore */
2211
+ ...(ngDevMode ? [undefined, { debugName: "icon" }] : /* istanbul ignore next */ []));
2212
+ title = input(/* @ts-ignore */
2213
+ ...(ngDevMode ? [undefined, { debugName: "title" }] : /* istanbul ignore next */ []));
2214
+ dismissible = input(false, /* @ts-ignore */
2215
+ ...(ngDevMode ? [{ debugName: "dismissible" }] : /* istanbul ignore next */ []));
2216
+ show = model(true, /* @ts-ignore */
2217
+ ...(ngDevMode ? [{ debugName: "show" }] : /* istanbul ignore next */ []));
2218
+ biClass = biClass;
2219
+ alertClass = computed(() => cn('alert', `alert-${this.theme()}`, this.dismissible() && 'alert-dismissible'), /* @ts-ignore */
2220
+ ...(ngDevMode ? [{ debugName: "alertClass" }] : /* istanbul ignore next */ []));
2221
+ dismiss() {
2222
+ this.show.set(false);
2223
+ }
2224
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: AlertComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2225
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.3", type: AlertComponent, isStandalone: true, selector: "lte-alert", inputs: { theme: { classPropertyName: "theme", publicName: "theme", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, dismissible: { classPropertyName: "dismissible", publicName: "dismissible", isSignal: true, isRequired: false, transformFunction: null }, show: { classPropertyName: "show", publicName: "show", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { show: "showChange" }, ngImport: i0, template: `
2226
+ @if (show()) {
2227
+ <div [class]="alertClass()" role="alert">
2228
+ @if (title() || icon()) {
2229
+ <h5>
2230
+ @if (icon()) {
2231
+ <i class="{{ biClass(icon()) }} me-2"></i>
2232
+ }
2233
+ {{ title() }}
2234
+ </h5>
2235
+ }
2236
+ <ng-content />
2237
+ @if (dismissible()) {
2238
+ <button type="button" class="btn-close" aria-label="Close" (click)="dismiss()"></button>
2239
+ }
2240
+ </div>
2241
+ }
2242
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
2243
+ }
2244
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: AlertComponent, decorators: [{
2245
+ type: Component,
2246
+ args: [{
2247
+ selector: 'lte-alert',
2248
+ changeDetection: ChangeDetectionStrategy.OnPush,
2249
+ template: `
2250
+ @if (show()) {
2251
+ <div [class]="alertClass()" role="alert">
2252
+ @if (title() || icon()) {
2253
+ <h5>
2254
+ @if (icon()) {
2255
+ <i class="{{ biClass(icon()) }} me-2"></i>
2256
+ }
2257
+ {{ title() }}
2258
+ </h5>
2259
+ }
2260
+ <ng-content />
2261
+ @if (dismissible()) {
2262
+ <button type="button" class="btn-close" aria-label="Close" (click)="dismiss()"></button>
2263
+ }
2264
+ </div>
2265
+ }
2266
+ `,
2267
+ }]
2268
+ }], propDecorators: { theme: [{ type: i0.Input, args: [{ isSignal: true, alias: "theme", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], dismissible: [{ type: i0.Input, args: [{ isSignal: true, alias: "dismissible", required: false }] }], show: [{ type: i0.Input, args: [{ isSignal: true, alias: "show", required: false }] }, { type: i0.Output, args: ["showChange"] }] } });
2269
+
2270
+ /** AdminLTE callout box — a bordered, lightly-tinted notice. */
2271
+ class CalloutComponent {
2272
+ theme = input('info', /* @ts-ignore */
2273
+ ...(ngDevMode ? [{ debugName: "theme" }] : /* istanbul ignore next */ []));
2274
+ title = input(/* @ts-ignore */
2275
+ ...(ngDevMode ? [undefined, { debugName: "title" }] : /* istanbul ignore next */ []));
2276
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: CalloutComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2277
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.3", type: CalloutComponent, isStandalone: true, selector: "lte-callout", inputs: { theme: { classPropertyName: "theme", publicName: "theme", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
2278
+ <div [class]="'callout callout-' + theme()">
2279
+ @if (title()) {
2280
+ <h5>{{ title() }}</h5>
2281
+ }
2282
+ <ng-content />
2283
+ </div>
2284
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
2285
+ }
2286
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: CalloutComponent, decorators: [{
2287
+ type: Component,
2288
+ args: [{
2289
+ selector: 'lte-callout',
2290
+ changeDetection: ChangeDetectionStrategy.OnPush,
2291
+ template: `
2292
+ <div [class]="'callout callout-' + theme()">
2293
+ @if (title()) {
2294
+ <h5>{{ title() }}</h5>
2295
+ }
2296
+ <ng-content />
2297
+ </div>
2298
+ `,
2299
+ }]
2300
+ }], propDecorators: { theme: [{ type: i0.Input, args: [{ isSignal: true, alias: "theme", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }] } });
2301
+
2302
+ /** Bootstrap progress bar with theme, size, striped/animated and label options. */
2303
+ class ProgressComponent {
2304
+ value = input.required(/* @ts-ignore */
2305
+ ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
2306
+ max = input(100, /* @ts-ignore */
2307
+ ...(ngDevMode ? [{ debugName: "max" }] : /* istanbul ignore next */ []));
2308
+ theme = input('primary', /* @ts-ignore */
2309
+ ...(ngDevMode ? [{ debugName: "theme" }] : /* istanbul ignore next */ []));
2310
+ size = input(/* @ts-ignore */
2311
+ ...(ngDevMode ? [undefined, { debugName: "size" }] : /* istanbul ignore next */ []));
2312
+ striped = input(false, /* @ts-ignore */
2313
+ ...(ngDevMode ? [{ debugName: "striped" }] : /* istanbul ignore next */ []));
2314
+ animated = input(false, /* @ts-ignore */
2315
+ ...(ngDevMode ? [{ debugName: "animated" }] : /* istanbul ignore next */ []));
2316
+ showLabel = input(false, /* @ts-ignore */
2317
+ ...(ngDevMode ? [{ debugName: "showLabel" }] : /* istanbul ignore next */ []));
2318
+ height = input(/* @ts-ignore */
2319
+ ...(ngDevMode ? [undefined, { debugName: "height" }] : /* istanbul ignore next */ []));
2320
+ pct = computed(() => Math.round(Math.min(100, Math.max(0, (this.value() / this.max()) * 100))), /* @ts-ignore */
2321
+ ...(ngDevMode ? [{ debugName: "pct" }] : /* istanbul ignore next */ []));
2322
+ barClass = computed(() => cn('progress-bar', `bg-${this.theme()}`, this.striped() && 'progress-bar-striped', this.animated() && 'progress-bar-animated'), /* @ts-ignore */
2323
+ ...(ngDevMode ? [{ debugName: "barClass" }] : /* istanbul ignore next */ []));
2324
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: ProgressComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2325
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.3", type: ProgressComponent, isStandalone: true, selector: "lte-progress", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: true, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, theme: { classPropertyName: "theme", publicName: "theme", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, striped: { classPropertyName: "striped", publicName: "striped", isSignal: true, isRequired: false, transformFunction: null }, animated: { classPropertyName: "animated", publicName: "animated", isSignal: true, isRequired: false, transformFunction: null }, showLabel: { classPropertyName: "showLabel", publicName: "showLabel", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
2326
+ <div
2327
+ class="progress"
2328
+ [class.progress-sm]="size() === 'sm'"
2329
+ [class.progress-lg]="size() === 'lg'"
2330
+ [style.height]="height() || null"
2331
+ role="progressbar"
2332
+ [attr.aria-valuenow]="value()"
2333
+ aria-valuemin="0"
2334
+ [attr.aria-valuemax]="max()"
2335
+ >
2336
+ <div [class]="barClass()" [style.width.%]="pct()">
2337
+ @if (showLabel()) {
2338
+ {{ pct() }}%
2339
+ }
2340
+ </div>
2341
+ </div>
2342
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
2343
+ }
2344
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: ProgressComponent, decorators: [{
2345
+ type: Component,
2346
+ args: [{
2347
+ selector: 'lte-progress',
2348
+ changeDetection: ChangeDetectionStrategy.OnPush,
2349
+ template: `
2350
+ <div
2351
+ class="progress"
2352
+ [class.progress-sm]="size() === 'sm'"
2353
+ [class.progress-lg]="size() === 'lg'"
2354
+ [style.height]="height() || null"
2355
+ role="progressbar"
2356
+ [attr.aria-valuenow]="value()"
2357
+ aria-valuemin="0"
2358
+ [attr.aria-valuemax]="max()"
2359
+ >
2360
+ <div [class]="barClass()" [style.width.%]="pct()">
2361
+ @if (showLabel()) {
2362
+ {{ pct() }}%
2363
+ }
2364
+ </div>
2365
+ </div>
2366
+ `,
2367
+ }]
2368
+ }], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: true }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], theme: [{ type: i0.Input, args: [{ isSignal: true, alias: "theme", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], striped: [{ type: i0.Input, args: [{ isSignal: true, alias: "striped", required: false }] }], animated: [{ type: i0.Input, args: [{ isSignal: true, alias: "animated", required: false }] }], showLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "showLabel", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }] } });
2369
+
2370
+ /**
2371
+ * Vertical timeline of events. `body`/`footer` accept HTML strings, rendered via
2372
+ * `[innerHTML]` (Angular sanitizes them before insertion).
2373
+ */
2374
+ class TimelineComponent {
2375
+ items = input.required(/* @ts-ignore */
2376
+ ...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
2377
+ icon(value) {
2378
+ return biClass(value || 'bi-circle-fill');
2379
+ }
2380
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: TimelineComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2381
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.3", type: TimelineComponent, isStandalone: true, selector: "lte-timeline", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: `
2382
+ <div class="timeline">
2383
+ @for (item of items(); track $index) {
2384
+ <div>
2385
+ <i class="bi {{ icon(item.icon) }} bg-{{ item.iconTheme || 'primary' }}" aria-hidden="true"></i>
2386
+ <div class="timeline-item">
2387
+ <span class="time"><i class="bi bi-clock"></i> {{ item.time }}</span>
2388
+ <h3 class="timeline-header">
2389
+ @if (item.url) {
2390
+ <a [href]="item.url">{{ item.title }}</a>
2391
+ } @else {
2392
+ {{ item.title }}
2393
+ }
2394
+ </h3>
2395
+ @if (item.body) {
2396
+ <div class="timeline-body" [innerHTML]="item.body"></div>
2397
+ }
2398
+ @if (item.footer) {
2399
+ <div class="timeline-footer" [innerHTML]="item.footer"></div>
2400
+ }
2401
+ </div>
2402
+ </div>
2403
+ }
2404
+ </div>
2405
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
2406
+ }
2407
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: TimelineComponent, decorators: [{
2408
+ type: Component,
2409
+ args: [{
2410
+ selector: 'lte-timeline',
2411
+ changeDetection: ChangeDetectionStrategy.OnPush,
2412
+ template: `
2413
+ <div class="timeline">
2414
+ @for (item of items(); track $index) {
2415
+ <div>
2416
+ <i class="bi {{ icon(item.icon) }} bg-{{ item.iconTheme || 'primary' }}" aria-hidden="true"></i>
2417
+ <div class="timeline-item">
2418
+ <span class="time"><i class="bi bi-clock"></i> {{ item.time }}</span>
2419
+ <h3 class="timeline-header">
2420
+ @if (item.url) {
2421
+ <a [href]="item.url">{{ item.title }}</a>
2422
+ } @else {
2423
+ {{ item.title }}
2424
+ }
2425
+ </h3>
2426
+ @if (item.body) {
2427
+ <div class="timeline-body" [innerHTML]="item.body"></div>
2428
+ }
2429
+ @if (item.footer) {
2430
+ <div class="timeline-footer" [innerHTML]="item.footer"></div>
2431
+ }
2432
+ </div>
2433
+ </div>
2434
+ }
2435
+ </div>
2436
+ `,
2437
+ }]
2438
+ }], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: true }] }] } });
2439
+
2440
+ /**
2441
+ * User profile card: avatar, name, role, optional cover image and a list of
2442
+ * stats. Project extra content (e.g. a "Follow" button) into the default slot.
2443
+ */
2444
+ class ProfileCardComponent {
2445
+ name = input.required(/* @ts-ignore */
2446
+ ...(ngDevMode ? [{ debugName: "name" }] : /* istanbul ignore next */ []));
2447
+ image = input.required(/* @ts-ignore */
2448
+ ...(ngDevMode ? [{ debugName: "image" }] : /* istanbul ignore next */ []));
2449
+ role = input(/* @ts-ignore */
2450
+ ...(ngDevMode ? [undefined, { debugName: "role" }] : /* istanbul ignore next */ []));
2451
+ coverImage = input(/* @ts-ignore */
2452
+ ...(ngDevMode ? [undefined, { debugName: "coverImage" }] : /* istanbul ignore next */ []));
2453
+ stats = input([], /* @ts-ignore */
2454
+ ...(ngDevMode ? [{ debugName: "stats" }] : /* istanbul ignore next */ []));
2455
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: ProfileCardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2456
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.3", type: ProfileCardComponent, isStandalone: true, selector: "lte-profile-card", inputs: { name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: true, transformFunction: null }, image: { classPropertyName: "image", publicName: "image", isSignal: true, isRequired: true, transformFunction: null }, role: { classPropertyName: "role", publicName: "role", isSignal: true, isRequired: false, transformFunction: null }, coverImage: { classPropertyName: "coverImage", publicName: "coverImage", isSignal: true, isRequired: false, transformFunction: null }, stats: { classPropertyName: "stats", publicName: "stats", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
2457
+ <div class="card card-primary card-outline">
2458
+ <div class="card-body box-profile">
2459
+ @if (coverImage()) {
2460
+ <div class="rounded-top mb-3" [style.height.px]="120" [style.background]="'center/cover url(' + coverImage() + ')'"></div>
2461
+ }
2462
+ <div class="text-center">
2463
+ <img
2464
+ [src]="image()"
2465
+ [alt]="name()"
2466
+ class="profile-user-img img-fluid img-circle rounded-circle shadow"
2467
+ width="100"
2468
+ height="100"
2469
+ />
2470
+ </div>
2471
+ <h3 class="profile-username text-center">{{ name() }}</h3>
2472
+ @if (role()) {
2473
+ <p class="text-body-secondary text-center">{{ role() }}</p>
2474
+ }
2475
+ @if (stats().length) {
2476
+ <ul class="list-group list-group-unbordered mb-3">
2477
+ @for (stat of stats(); track $index) {
2478
+ <li class="list-group-item">
2479
+ <b>{{ stat.label }}</b> <span class="float-end">{{ stat.value }}</span>
2480
+ </li>
2481
+ }
2482
+ </ul>
2483
+ }
2484
+ <ng-content />
2485
+ </div>
2486
+ </div>
2487
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
2488
+ }
2489
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: ProfileCardComponent, decorators: [{
2490
+ type: Component,
2491
+ args: [{
2492
+ selector: 'lte-profile-card',
2493
+ changeDetection: ChangeDetectionStrategy.OnPush,
2494
+ template: `
2495
+ <div class="card card-primary card-outline">
2496
+ <div class="card-body box-profile">
2497
+ @if (coverImage()) {
2498
+ <div class="rounded-top mb-3" [style.height.px]="120" [style.background]="'center/cover url(' + coverImage() + ')'"></div>
2499
+ }
2500
+ <div class="text-center">
2501
+ <img
2502
+ [src]="image()"
2503
+ [alt]="name()"
2504
+ class="profile-user-img img-fluid img-circle rounded-circle shadow"
2505
+ width="100"
2506
+ height="100"
2507
+ />
2508
+ </div>
2509
+ <h3 class="profile-username text-center">{{ name() }}</h3>
2510
+ @if (role()) {
2511
+ <p class="text-body-secondary text-center">{{ role() }}</p>
2512
+ }
2513
+ @if (stats().length) {
2514
+ <ul class="list-group list-group-unbordered mb-3">
2515
+ @for (stat of stats(); track $index) {
2516
+ <li class="list-group-item">
2517
+ <b>{{ stat.label }}</b> <span class="float-end">{{ stat.value }}</span>
2518
+ </li>
2519
+ }
2520
+ </ul>
2521
+ }
2522
+ <ng-content />
2523
+ </div>
2524
+ </div>
2525
+ `,
2526
+ }]
2527
+ }], propDecorators: { name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: true }] }], image: [{ type: i0.Input, args: [{ isSignal: true, alias: "image", required: true }] }], role: [{ type: i0.Input, args: [{ isSignal: true, alias: "role", required: false }] }], coverImage: [{ type: i0.Input, args: [{ isSignal: true, alias: "coverImage", required: false }] }], stats: [{ type: i0.Input, args: [{ isSignal: true, alias: "stats", required: false }] }] } });
2528
+
2529
+ /**
2530
+ * Description block (used inside card footers): a big header value, a label, and
2531
+ * an optional trend percentage (green up / red down).
2532
+ */
2533
+ class DescriptionBlockComponent {
2534
+ header = input.required(/* @ts-ignore */
2535
+ ...(ngDevMode ? [{ debugName: "header" }] : /* istanbul ignore next */ []));
2536
+ text = input(/* @ts-ignore */
2537
+ ...(ngDevMode ? [undefined, { debugName: "text" }] : /* istanbul ignore next */ []));
2538
+ icon = input(/* @ts-ignore */
2539
+ ...(ngDevMode ? [undefined, { debugName: "icon" }] : /* istanbul ignore next */ []));
2540
+ iconTheme = input(/* @ts-ignore */
2541
+ ...(ngDevMode ? [undefined, { debugName: "iconTheme" }] : /* istanbul ignore next */ []));
2542
+ percentage = input(/* @ts-ignore */
2543
+ ...(ngDevMode ? [undefined, { debugName: "percentage" }] : /* istanbul ignore next */ []));
2544
+ biClass = biClass;
2545
+ positive = computed(() => (this.percentage() ?? 0) >= 0, /* @ts-ignore */
2546
+ ...(ngDevMode ? [{ debugName: "positive" }] : /* istanbul ignore next */ []));
2547
+ absPercentage = computed(() => Math.abs(this.percentage() ?? 0), /* @ts-ignore */
2548
+ ...(ngDevMode ? [{ debugName: "absPercentage" }] : /* istanbul ignore next */ []));
2549
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: DescriptionBlockComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2550
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.3", type: DescriptionBlockComponent, isStandalone: true, selector: "lte-description-block", inputs: { header: { classPropertyName: "header", publicName: "header", isSignal: true, isRequired: true, transformFunction: null }, text: { classPropertyName: "text", publicName: "text", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, iconTheme: { classPropertyName: "iconTheme", publicName: "iconTheme", isSignal: true, isRequired: false, transformFunction: null }, percentage: { classPropertyName: "percentage", publicName: "percentage", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
2551
+ <div class="description-block">
2552
+ @if (percentage() != null) {
2553
+ <span class="description-percentage" [class.text-success]="positive()" [class.text-danger]="!positive()">
2554
+ <i class="bi" [class.bi-caret-up-fill]="positive()" [class.bi-caret-down-fill]="!positive()"></i>
2555
+ {{ absPercentage() }}%
2556
+ </span>
2557
+ }
2558
+ <h5 class="description-header">
2559
+ @if (icon()) {
2560
+ <i class="{{ biClass(icon()) }} me-1" [class]="iconTheme() ? 'text-' + iconTheme() : ''"></i>
2561
+ }
2562
+ {{ header() }}
2563
+ </h5>
2564
+ <span class="description-text">{{ text() }}</span>
2565
+ <ng-content />
2566
+ </div>
2567
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
2568
+ }
2569
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: DescriptionBlockComponent, decorators: [{
2570
+ type: Component,
2571
+ args: [{
2572
+ selector: 'lte-description-block',
2573
+ changeDetection: ChangeDetectionStrategy.OnPush,
2574
+ template: `
2575
+ <div class="description-block">
2576
+ @if (percentage() != null) {
2577
+ <span class="description-percentage" [class.text-success]="positive()" [class.text-danger]="!positive()">
2578
+ <i class="bi" [class.bi-caret-up-fill]="positive()" [class.bi-caret-down-fill]="!positive()"></i>
2579
+ {{ absPercentage() }}%
2580
+ </span>
2581
+ }
2582
+ <h5 class="description-header">
2583
+ @if (icon()) {
2584
+ <i class="{{ biClass(icon()) }} me-1" [class]="iconTheme() ? 'text-' + iconTheme() : ''"></i>
2585
+ }
2586
+ {{ header() }}
2587
+ </h5>
2588
+ <span class="description-text">{{ text() }}</span>
2589
+ <ng-content />
2590
+ </div>
2591
+ `,
2592
+ }]
2593
+ }], propDecorators: { header: [{ type: i0.Input, args: [{ isSignal: true, alias: "header", required: true }] }], text: [{ type: i0.Input, args: [{ isSignal: true, alias: "text", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], iconTheme: [{ type: i0.Input, args: [{ isSignal: true, alias: "iconTheme", required: false }] }], percentage: [{ type: i0.Input, args: [{ isSignal: true, alias: "percentage", required: false }] }] } });
2594
+
2595
+ /** Standalone breadcrumb trail. The last item renders as the active page. */
2596
+ class BreadcrumbComponent {
2597
+ items = input.required(/* @ts-ignore */
2598
+ ...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
2599
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: BreadcrumbComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2600
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.3", type: BreadcrumbComponent, isStandalone: true, selector: "lte-breadcrumb", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: `
2601
+ <nav aria-label="breadcrumb">
2602
+ <ol class="breadcrumb">
2603
+ @for (crumb of items(); track $index; let last = $last) {
2604
+ <li class="breadcrumb-item" [class.active]="last" [attr.aria-current]="last ? 'page' : null">
2605
+ @if (!last && crumb.route) {
2606
+ <a [routerLink]="crumb.route">{{ crumb.label }}</a>
2607
+ } @else if (!last && crumb.href) {
2608
+ <a [href]="crumb.href">{{ crumb.label }}</a>
2609
+ } @else {
2610
+ {{ crumb.label }}
2611
+ }
2612
+ </li>
2613
+ }
2614
+ </ol>
2615
+ </nav>
2616
+ `, isInline: true, dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "browserUrl", "routerLink"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2617
+ }
2618
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: BreadcrumbComponent, decorators: [{
2619
+ type: Component,
2620
+ args: [{
2621
+ selector: 'lte-breadcrumb',
2622
+ changeDetection: ChangeDetectionStrategy.OnPush,
2623
+ imports: [RouterLink],
2624
+ template: `
2625
+ <nav aria-label="breadcrumb">
2626
+ <ol class="breadcrumb">
2627
+ @for (crumb of items(); track $index; let last = $last) {
2628
+ <li class="breadcrumb-item" [class.active]="last" [attr.aria-current]="last ? 'page' : null">
2629
+ @if (!last && crumb.route) {
2630
+ <a [routerLink]="crumb.route">{{ crumb.label }}</a>
2631
+ } @else if (!last && crumb.href) {
2632
+ <a [href]="crumb.href">{{ crumb.label }}</a>
2633
+ } @else {
2634
+ {{ crumb.label }}
2635
+ }
2636
+ </li>
2637
+ }
2638
+ </ol>
2639
+ </nav>
2640
+ `,
2641
+ }]
2642
+ }], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: true }] }] } });
2643
+
2644
+ /**
2645
+ * Thin ApexCharts wrapper. Lazily imports `apexcharts` (an optional peer dep) on
2646
+ * the browser only, renders into a div, re-renders when `options` change, and
2647
+ * destroys the chart on teardown.
2648
+ */
2649
+ class ApexChartComponent {
2650
+ host = viewChild.required('host');
2651
+ isBrowser;
2652
+ chart = null;
2653
+ /** ApexCharts options object (series, chart, xaxis, …). */
2654
+ options = input.required(/* @ts-ignore */
2655
+ ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
2656
+ constructor(platformId) {
2657
+ this.isBrowser = isPlatformBrowser(platformId);
2658
+ effect(() => {
2659
+ const opts = this.options();
2660
+ if (!this.isBrowser)
2661
+ return;
2662
+ void this.renderOrUpdate(opts);
2663
+ });
2664
+ inject(DestroyRef).onDestroy(() => {
2665
+ this.chart?.destroy();
2666
+ this.chart = null;
2667
+ });
2668
+ }
2669
+ async renderOrUpdate(opts) {
2670
+ try {
2671
+ if (this.chart) {
2672
+ this.chart.updateOptions(opts);
2673
+ return;
2674
+ }
2675
+ const mod = await import('apexcharts');
2676
+ const ApexCharts = (mod.default ?? mod);
2677
+ this.chart = new ApexCharts(this.host().nativeElement, opts);
2678
+ this.chart.render();
2679
+ }
2680
+ catch {
2681
+ console.warn('[adminlte-angular] apexcharts is not installed — <lte-apex-chart> is inert.');
2682
+ }
2683
+ }
2684
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: ApexChartComponent, deps: [{ token: PLATFORM_ID }], target: i0.ɵɵFactoryTarget.Component });
2685
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.0.3", type: ApexChartComponent, isStandalone: true, selector: "lte-apex-chart", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: true, transformFunction: null } }, viewQueries: [{ propertyName: "host", first: true, predicate: ["host"], descendants: true, isSignal: true }], ngImport: i0, template: `<div #host></div>`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
2686
+ }
2687
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: ApexChartComponent, decorators: [{
2688
+ type: Component,
2689
+ args: [{
2690
+ selector: 'lte-apex-chart',
2691
+ changeDetection: ChangeDetectionStrategy.OnPush,
2692
+ template: `<div #host></div>`,
2693
+ }]
2694
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
2695
+ type: Inject,
2696
+ args: [PLATFORM_ID]
2697
+ }] }], propDecorators: { host: [{ type: i0.ViewChild, args: ['host', { isSignal: true }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: true }] }] } });
2698
+
2699
+ /**
2700
+ * A self-contained Bootstrap modal that doesn't require Bootstrap's JS. Visibility
2701
+ * is driven by the `[(open)]` model; project the body into the default slot and a
2702
+ * footer into `[modal-footer]`.
2703
+ */
2704
+ class ModalComponent {
2705
+ title = input('', /* @ts-ignore */
2706
+ ...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
2707
+ size = input(/* @ts-ignore */
2708
+ ...(ngDevMode ? [undefined, { debugName: "size" }] : /* istanbul ignore next */ []));
2709
+ open = model(false, /* @ts-ignore */
2710
+ ...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
2711
+ closed = output();
2712
+ dialogClass = computed(() => this.size() ? `modal-dialog modal-${this.size()}` : 'modal-dialog', /* @ts-ignore */
2713
+ ...(ngDevMode ? [{ debugName: "dialogClass" }] : /* istanbul ignore next */ []));
2714
+ onBackdrop(e) {
2715
+ if (e.target.classList.contains('modal'))
2716
+ this.close();
2717
+ }
2718
+ close() {
2719
+ this.open.set(false);
2720
+ this.closed.emit();
2721
+ }
2722
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: ModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2723
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.3", type: ModalComponent, isStandalone: true, selector: "lte-modal", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { open: "openChange", closed: "closed" }, ngImport: i0, template: `
2724
+ @if (open()) {
2725
+ <div class="modal fade show d-block" tabindex="-1" role="dialog" (click)="onBackdrop($event)">
2726
+ <div [class]="dialogClass()" role="document">
2727
+ <div class="modal-content">
2728
+ <div class="modal-header">
2729
+ <h5 class="modal-title">{{ title() }}</h5>
2730
+ <button type="button" class="btn-close" aria-label="Close" (click)="close()"></button>
2731
+ </div>
2732
+ <div class="modal-body">
2733
+ <ng-content />
2734
+ </div>
2735
+ <div class="modal-footer">
2736
+ <ng-content select="[modal-footer]" />
2737
+ </div>
2738
+ </div>
2739
+ </div>
2740
+ </div>
2741
+ <div class="modal-backdrop fade show"></div>
2742
+ }
2743
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
2744
+ }
2745
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: ModalComponent, decorators: [{
2746
+ type: Component,
2747
+ args: [{
2748
+ selector: 'lte-modal',
2749
+ changeDetection: ChangeDetectionStrategy.OnPush,
2750
+ template: `
2751
+ @if (open()) {
2752
+ <div class="modal fade show d-block" tabindex="-1" role="dialog" (click)="onBackdrop($event)">
2753
+ <div [class]="dialogClass()" role="document">
2754
+ <div class="modal-content">
2755
+ <div class="modal-header">
2756
+ <h5 class="modal-title">{{ title() }}</h5>
2757
+ <button type="button" class="btn-close" aria-label="Close" (click)="close()"></button>
2758
+ </div>
2759
+ <div class="modal-body">
2760
+ <ng-content />
2761
+ </div>
2762
+ <div class="modal-footer">
2763
+ <ng-content select="[modal-footer]" />
2764
+ </div>
2765
+ </div>
2766
+ </div>
2767
+ </div>
2768
+ <div class="modal-backdrop fade show"></div>
2769
+ }
2770
+ `,
2771
+ }]
2772
+ }], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }, { type: i0.Output, args: ["openChange"] }], closed: [{ type: i0.Output, args: ["closed"] }] } });
2773
+
2774
+ /** A labeled `value/max` progress row, as used in AdminLTE stat cards. */
2775
+ class ProgressGroupComponent {
2776
+ label = input.required(/* @ts-ignore */
2777
+ ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
2778
+ value = input.required(/* @ts-ignore */
2779
+ ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
2780
+ max = input(100, /* @ts-ignore */
2781
+ ...(ngDevMode ? [{ debugName: "max" }] : /* istanbul ignore next */ []));
2782
+ theme = input('primary', /* @ts-ignore */
2783
+ ...(ngDevMode ? [{ debugName: "theme" }] : /* istanbul ignore next */ []));
2784
+ pct = computed(() => Math.min(100, Math.max(0, (this.value() / this.max()) * 100)), /* @ts-ignore */
2785
+ ...(ngDevMode ? [{ debugName: "pct" }] : /* istanbul ignore next */ []));
2786
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: ProgressGroupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2787
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.3", type: ProgressGroupComponent, isStandalone: true, selector: "lte-progress-group", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: true, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, theme: { classPropertyName: "theme", publicName: "theme", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
2788
+ <div class="progress-group">
2789
+ <span class="progress-text">{{ label() }}</span>
2790
+ <span class="float-end"><b>{{ value() }}</b>/{{ max() }}</span>
2791
+ <div class="progress progress-sm">
2792
+ <div class="progress-bar" [class]="'bg-' + theme()" [style.width.%]="pct()"></div>
2793
+ </div>
2794
+ </div>
2795
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
2796
+ }
2797
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: ProgressGroupComponent, decorators: [{
2798
+ type: Component,
2799
+ args: [{
2800
+ selector: 'lte-progress-group',
2801
+ changeDetection: ChangeDetectionStrategy.OnPush,
2802
+ template: `
2803
+ <div class="progress-group">
2804
+ <span class="progress-text">{{ label() }}</span>
2805
+ <span class="float-end"><b>{{ value() }}</b>/{{ max() }}</span>
2806
+ <div class="progress progress-sm">
2807
+ <div class="progress-bar" [class]="'bg-' + theme()" [style.width.%]="pct()"></div>
2808
+ </div>
2809
+ </div>
2810
+ `,
2811
+ }]
2812
+ }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: true }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], theme: [{ type: i0.Input, args: [{ isSignal: true, alias: "theme", required: false }] }] } });
2813
+
2814
+ /** Star rating display supporting half-stars and an optional `value/max` label. */
2815
+ class RatingsComponent {
2816
+ value = input.required(/* @ts-ignore */
2817
+ ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
2818
+ max = input(5, /* @ts-ignore */
2819
+ ...(ngDevMode ? [{ debugName: "max" }] : /* istanbul ignore next */ []));
2820
+ theme = input('warning', /* @ts-ignore */
2821
+ ...(ngDevMode ? [{ debugName: "theme" }] : /* istanbul ignore next */ []));
2822
+ showText = input(false, /* @ts-ignore */
2823
+ ...(ngDevMode ? [{ debugName: "showText" }] : /* istanbul ignore next */ []));
2824
+ stars = computed(() => Array.from({ length: this.max() }, (_, i) => {
2825
+ const n = i + 1;
2826
+ if (this.value() >= n)
2827
+ return 'bi-star-fill';
2828
+ if (this.value() >= n - 0.5)
2829
+ return 'bi-star-half';
2830
+ return 'bi-star';
2831
+ }), /* @ts-ignore */
2832
+ ...(ngDevMode ? [{ debugName: "stars" }] : /* istanbul ignore next */ []));
2833
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: RatingsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2834
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.3", type: RatingsComponent, isStandalone: true, selector: "lte-ratings", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: true, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, theme: { classPropertyName: "theme", publicName: "theme", isSignal: true, isRequired: false, transformFunction: null }, showText: { classPropertyName: "showText", publicName: "showText", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
2835
+ <div class="ratings d-inline-flex align-items-center gap-1">
2836
+ @for (star of stars(); track $index) {
2837
+ <i class="bi {{ star }} text-{{ theme() }}" aria-hidden="true"></i>
2838
+ }
2839
+ @if (showText()) {
2840
+ <span class="ms-2 text-secondary">{{ value() }}/{{ max() }}</span>
2841
+ }
2842
+ </div>
2843
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
2844
+ }
2845
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: RatingsComponent, decorators: [{
2846
+ type: Component,
2847
+ args: [{
2848
+ selector: 'lte-ratings',
2849
+ changeDetection: ChangeDetectionStrategy.OnPush,
2850
+ template: `
2851
+ <div class="ratings d-inline-flex align-items-center gap-1">
2852
+ @for (star of stars(); track $index) {
2853
+ <i class="bi {{ star }} text-{{ theme() }}" aria-hidden="true"></i>
2854
+ }
2855
+ @if (showText()) {
2856
+ <span class="ms-2 text-secondary">{{ value() }}/{{ max() }}</span>
2857
+ }
2858
+ </div>
2859
+ `,
2860
+ }]
2861
+ }], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: true }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], theme: [{ type: i0.Input, args: [{ isSignal: true, alias: "theme", required: false }] }], showText: [{ type: i0.Input, args: [{ isSignal: true, alias: "showText", required: false }] }] } });
2862
+
2863
+ /**
2864
+ * AdminLTE direct-chat card: a scrollable message thread with a slide-in contacts
2865
+ * pane. Pass `messages`/`contacts` data, or project your own markup into the
2866
+ * `[messages]` / `[contacts]` / `[footer]` slots.
2867
+ */
2868
+ class DirectChatComponent {
2869
+ title = input('', /* @ts-ignore */
2870
+ ...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
2871
+ theme = input('primary', /* @ts-ignore */
2872
+ ...(ngDevMode ? [{ debugName: "theme" }] : /* istanbul ignore next */ []));
2873
+ messages = input([], /* @ts-ignore */
2874
+ ...(ngDevMode ? [{ debugName: "messages" }] : /* istanbul ignore next */ []));
2875
+ contacts = input([], /* @ts-ignore */
2876
+ ...(ngDevMode ? [{ debugName: "contacts" }] : /* istanbul ignore next */ []));
2877
+ /** Render the `[footer]` slot wrapper. */
2878
+ hasFooter = input(false, /* @ts-ignore */
2879
+ ...(ngDevMode ? [{ debugName: "hasFooter" }] : /* istanbul ignore next */ []));
2880
+ contactsOpen = signal(false, /* @ts-ignore */
2881
+ ...(ngDevMode ? [{ debugName: "contactsOpen" }] : /* istanbul ignore next */ []));
2882
+ rootClass = computed(() => cn('card direct-chat', `direct-chat-${this.theme()}`, this.contactsOpen() && 'direct-chat-contacts-open'), /* @ts-ignore */
2883
+ ...(ngDevMode ? [{ debugName: "rootClass" }] : /* istanbul ignore next */ []));
2884
+ toggleContacts() {
2885
+ this.contactsOpen.update((v) => !v);
2886
+ }
2887
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: DirectChatComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2888
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.3", type: DirectChatComponent, isStandalone: true, selector: "lte-direct-chat", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, theme: { classPropertyName: "theme", publicName: "theme", isSignal: true, isRequired: false, transformFunction: null }, messages: { classPropertyName: "messages", publicName: "messages", isSignal: true, isRequired: false, transformFunction: null }, contacts: { classPropertyName: "contacts", publicName: "contacts", isSignal: true, isRequired: false, transformFunction: null }, hasFooter: { classPropertyName: "hasFooter", publicName: "hasFooter", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
2889
+ <div [class]="rootClass()">
2890
+ <div class="card-header">
2891
+ <h3 class="card-title">{{ title() }}</h3>
2892
+ <div class="card-tools">
2893
+ <button type="button" class="btn btn-tool" title="Contacts" (click)="toggleContacts()">
2894
+ <i class="bi bi-person-lines-fill"></i>
2895
+ </button>
2896
+ </div>
2897
+ </div>
2898
+
2899
+ <div class="card-body">
2900
+ <div class="direct-chat-messages">
2901
+ <ng-content select="[messages]" />
2902
+ @for (msg of messages(); track $index) {
2903
+ <div class="direct-chat-msg" [class.end]="msg.isOwn">
2904
+ <div class="direct-chat-infos clearfix">
2905
+ <span class="direct-chat-name" [class.float-end]="msg.isOwn" [class.float-start]="!msg.isOwn">{{ msg.from }}</span>
2906
+ <span class="direct-chat-timestamp" [class.float-start]="msg.isOwn" [class.float-end]="!msg.isOwn">{{ msg.timestamp }}</span>
2907
+ </div>
2908
+ <img class="direct-chat-img" [src]="msg.image" [alt]="msg.from" />
2909
+ <div class="direct-chat-text">{{ msg.text }}</div>
2910
+ </div>
2911
+ }
2912
+ </div>
2913
+
2914
+ <div class="direct-chat-contacts">
2915
+ <ng-content select="[contacts]" />
2916
+ @if (contacts().length) {
2917
+ <ul class="contacts-list">
2918
+ @for (c of contacts(); track $index) {
2919
+ <li>
2920
+ <a href="#">
2921
+ <img class="contacts-list-img" [src]="c.image" [alt]="c.name" />
2922
+ <div class="contacts-list-info">
2923
+ <span class="contacts-list-name">
2924
+ {{ c.name }}
2925
+ <small class="contacts-list-date float-end">{{ c.date }}</small>
2926
+ </span>
2927
+ <span class="contacts-list-msg">{{ c.preview }}</span>
2928
+ </div>
2929
+ </a>
2930
+ </li>
2931
+ }
2932
+ </ul>
2933
+ }
2934
+ </div>
2935
+ </div>
2936
+
2937
+ @if (hasFooter()) {
2938
+ <div class="card-footer">
2939
+ <ng-content select="[footer]" />
2940
+ </div>
2941
+ }
2942
+ </div>
2943
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
2944
+ }
2945
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: DirectChatComponent, decorators: [{
2946
+ type: Component,
2947
+ args: [{
2948
+ selector: 'lte-direct-chat',
2949
+ changeDetection: ChangeDetectionStrategy.OnPush,
2950
+ template: `
2951
+ <div [class]="rootClass()">
2952
+ <div class="card-header">
2953
+ <h3 class="card-title">{{ title() }}</h3>
2954
+ <div class="card-tools">
2955
+ <button type="button" class="btn btn-tool" title="Contacts" (click)="toggleContacts()">
2956
+ <i class="bi bi-person-lines-fill"></i>
2957
+ </button>
2958
+ </div>
2959
+ </div>
2960
+
2961
+ <div class="card-body">
2962
+ <div class="direct-chat-messages">
2963
+ <ng-content select="[messages]" />
2964
+ @for (msg of messages(); track $index) {
2965
+ <div class="direct-chat-msg" [class.end]="msg.isOwn">
2966
+ <div class="direct-chat-infos clearfix">
2967
+ <span class="direct-chat-name" [class.float-end]="msg.isOwn" [class.float-start]="!msg.isOwn">{{ msg.from }}</span>
2968
+ <span class="direct-chat-timestamp" [class.float-start]="msg.isOwn" [class.float-end]="!msg.isOwn">{{ msg.timestamp }}</span>
2969
+ </div>
2970
+ <img class="direct-chat-img" [src]="msg.image" [alt]="msg.from" />
2971
+ <div class="direct-chat-text">{{ msg.text }}</div>
2972
+ </div>
2973
+ }
2974
+ </div>
2975
+
2976
+ <div class="direct-chat-contacts">
2977
+ <ng-content select="[contacts]" />
2978
+ @if (contacts().length) {
2979
+ <ul class="contacts-list">
2980
+ @for (c of contacts(); track $index) {
2981
+ <li>
2982
+ <a href="#">
2983
+ <img class="contacts-list-img" [src]="c.image" [alt]="c.name" />
2984
+ <div class="contacts-list-info">
2985
+ <span class="contacts-list-name">
2986
+ {{ c.name }}
2987
+ <small class="contacts-list-date float-end">{{ c.date }}</small>
2988
+ </span>
2989
+ <span class="contacts-list-msg">{{ c.preview }}</span>
2990
+ </div>
2991
+ </a>
2992
+ </li>
2993
+ }
2994
+ </ul>
2995
+ }
2996
+ </div>
2997
+ </div>
2998
+
2999
+ @if (hasFooter()) {
3000
+ <div class="card-footer">
3001
+ <ng-content select="[footer]" />
3002
+ </div>
3003
+ }
3004
+ </div>
3005
+ `,
3006
+ }]
3007
+ }], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], theme: [{ type: i0.Input, args: [{ isSignal: true, alias: "theme", required: false }] }], messages: [{ type: i0.Input, args: [{ isSignal: true, alias: "messages", required: false }] }], contacts: [{ type: i0.Input, args: [{ isSignal: true, alias: "contacts", required: false }] }], hasFooter: [{ type: i0.Input, args: [{ isSignal: true, alias: "hasFooter", required: false }] }] } });
3008
+
3009
+ /**
3010
+ * Topbar messages dropdown (`<li class="nav-item dropdown">`). Drop it into the
3011
+ * topbar's `[topbar-end]` slot. Uses Bootstrap's dropdown JS (`data-bs-toggle`).
3012
+ */
3013
+ class NavMessagesComponent {
3014
+ messages = input.required(/* @ts-ignore */
3015
+ ...(ngDevMode ? [{ debugName: "messages" }] : /* istanbul ignore next */ []));
3016
+ badgeColor = input('danger', /* @ts-ignore */
3017
+ ...(ngDevMode ? [{ debugName: "badgeColor" }] : /* istanbul ignore next */ []));
3018
+ count = input(/* @ts-ignore */
3019
+ ...(ngDevMode ? [undefined, { debugName: "count" }] : /* istanbul ignore next */ []));
3020
+ seeAllUrl = input('#', /* @ts-ignore */
3021
+ ...(ngDevMode ? [{ debugName: "seeAllUrl" }] : /* istanbul ignore next */ []));
3022
+ seeAllText = input('See All Messages', /* @ts-ignore */
3023
+ ...(ngDevMode ? [{ debugName: "seeAllText" }] : /* istanbul ignore next */ []));
3024
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: NavMessagesComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3025
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.3", type: NavMessagesComponent, isStandalone: true, selector: "lte-nav-messages, [lte-nav-messages]", inputs: { messages: { classPropertyName: "messages", publicName: "messages", isSignal: true, isRequired: true, transformFunction: null }, badgeColor: { classPropertyName: "badgeColor", publicName: "badgeColor", isSignal: true, isRequired: false, transformFunction: null }, count: { classPropertyName: "count", publicName: "count", isSignal: true, isRequired: false, transformFunction: null }, seeAllUrl: { classPropertyName: "seeAllUrl", publicName: "seeAllUrl", isSignal: true, isRequired: false, transformFunction: null }, seeAllText: { classPropertyName: "seeAllText", publicName: "seeAllText", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "nav-item dropdown" }, ngImport: i0, template: `
3026
+ <a class="nav-link" data-bs-toggle="dropdown" href="#" (click)="$event.preventDefault()">
3027
+ <i class="bi bi-chat-text"></i>
3028
+ @if (messages().length) {
3029
+ <span class="navbar-badge badge text-bg-{{ badgeColor() }}">{{ count() ?? messages().length }}</span>
3030
+ }
3031
+ </a>
3032
+ <div class="dropdown-menu dropdown-menu-lg dropdown-menu-end">
3033
+ @for (msg of messages(); track $index) {
3034
+ <a [href]="msg.url || '#'" class="dropdown-item">
3035
+ <div class="d-flex">
3036
+ @if (msg.image) {
3037
+ <div class="flex-shrink-0">
3038
+ <img [src]="msg.image" alt="User Avatar" class="img-size-50 rounded-circle me-3" />
3039
+ </div>
3040
+ }
3041
+ <div class="flex-grow-1">
3042
+ <h3 class="dropdown-item-title">
3043
+ {{ msg.from }}
3044
+ @if (msg.star) {
3045
+ <span class="float-end fs-7 text-{{ msg.star }}"><i class="bi bi-star-fill"></i></span>
3046
+ }
3047
+ </h3>
3048
+ <p class="fs-7">{{ msg.text }}</p>
3049
+ @if (msg.time) {
3050
+ <p class="fs-7 text-secondary"><i class="bi bi-clock-fill me-1"></i> {{ msg.time }}</p>
3051
+ }
3052
+ </div>
3053
+ </div>
3054
+ </a>
3055
+ <div class="dropdown-divider"></div>
3056
+ }
3057
+ <a [href]="seeAllUrl()" class="dropdown-item dropdown-footer">{{ seeAllText() }}</a>
3058
+ </div>
3059
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
3060
+ }
3061
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: NavMessagesComponent, decorators: [{
3062
+ type: Component,
3063
+ args: [{
3064
+ selector: 'lte-nav-messages, [lte-nav-messages]',
3065
+ changeDetection: ChangeDetectionStrategy.OnPush,
3066
+ host: { class: 'nav-item dropdown' },
3067
+ template: `
3068
+ <a class="nav-link" data-bs-toggle="dropdown" href="#" (click)="$event.preventDefault()">
3069
+ <i class="bi bi-chat-text"></i>
3070
+ @if (messages().length) {
3071
+ <span class="navbar-badge badge text-bg-{{ badgeColor() }}">{{ count() ?? messages().length }}</span>
3072
+ }
3073
+ </a>
3074
+ <div class="dropdown-menu dropdown-menu-lg dropdown-menu-end">
3075
+ @for (msg of messages(); track $index) {
3076
+ <a [href]="msg.url || '#'" class="dropdown-item">
3077
+ <div class="d-flex">
3078
+ @if (msg.image) {
3079
+ <div class="flex-shrink-0">
3080
+ <img [src]="msg.image" alt="User Avatar" class="img-size-50 rounded-circle me-3" />
3081
+ </div>
3082
+ }
3083
+ <div class="flex-grow-1">
3084
+ <h3 class="dropdown-item-title">
3085
+ {{ msg.from }}
3086
+ @if (msg.star) {
3087
+ <span class="float-end fs-7 text-{{ msg.star }}"><i class="bi bi-star-fill"></i></span>
3088
+ }
3089
+ </h3>
3090
+ <p class="fs-7">{{ msg.text }}</p>
3091
+ @if (msg.time) {
3092
+ <p class="fs-7 text-secondary"><i class="bi bi-clock-fill me-1"></i> {{ msg.time }}</p>
3093
+ }
3094
+ </div>
3095
+ </div>
3096
+ </a>
3097
+ <div class="dropdown-divider"></div>
3098
+ }
3099
+ <a [href]="seeAllUrl()" class="dropdown-item dropdown-footer">{{ seeAllText() }}</a>
3100
+ </div>
3101
+ `,
3102
+ }]
3103
+ }], propDecorators: { messages: [{ type: i0.Input, args: [{ isSignal: true, alias: "messages", required: true }] }], badgeColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "badgeColor", required: false }] }], count: [{ type: i0.Input, args: [{ isSignal: true, alias: "count", required: false }] }], seeAllUrl: [{ type: i0.Input, args: [{ isSignal: true, alias: "seeAllUrl", required: false }] }], seeAllText: [{ type: i0.Input, args: [{ isSignal: true, alias: "seeAllText", required: false }] }] } });
3104
+
3105
+ /**
3106
+ * Topbar notifications dropdown (`<li class="nav-item dropdown">`). Drop it into
3107
+ * the topbar's `[topbar-end]` slot. Uses Bootstrap's dropdown JS.
3108
+ */
3109
+ class NavNotificationsComponent {
3110
+ notifications = input.required(/* @ts-ignore */
3111
+ ...(ngDevMode ? [{ debugName: "notifications" }] : /* istanbul ignore next */ []));
3112
+ badgeColor = input('warning', /* @ts-ignore */
3113
+ ...(ngDevMode ? [{ debugName: "badgeColor" }] : /* istanbul ignore next */ []));
3114
+ count = input(/* @ts-ignore */
3115
+ ...(ngDevMode ? [undefined, { debugName: "count" }] : /* istanbul ignore next */ []));
3116
+ seeAllUrl = input('#', /* @ts-ignore */
3117
+ ...(ngDevMode ? [{ debugName: "seeAllUrl" }] : /* istanbul ignore next */ []));
3118
+ seeAllText = input('See All Notifications', /* @ts-ignore */
3119
+ ...(ngDevMode ? [{ debugName: "seeAllText" }] : /* istanbul ignore next */ []));
3120
+ biClass = biClass;
3121
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: NavNotificationsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3122
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.3", type: NavNotificationsComponent, isStandalone: true, selector: "lte-nav-notifications, [lte-nav-notifications]", inputs: { notifications: { classPropertyName: "notifications", publicName: "notifications", isSignal: true, isRequired: true, transformFunction: null }, badgeColor: { classPropertyName: "badgeColor", publicName: "badgeColor", isSignal: true, isRequired: false, transformFunction: null }, count: { classPropertyName: "count", publicName: "count", isSignal: true, isRequired: false, transformFunction: null }, seeAllUrl: { classPropertyName: "seeAllUrl", publicName: "seeAllUrl", isSignal: true, isRequired: false, transformFunction: null }, seeAllText: { classPropertyName: "seeAllText", publicName: "seeAllText", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "nav-item dropdown" }, ngImport: i0, template: `
3123
+ <a class="nav-link" data-bs-toggle="dropdown" href="#" (click)="$event.preventDefault()">
3124
+ <i class="bi bi-bell-fill"></i>
3125
+ @if (notifications().length) {
3126
+ <span class="navbar-badge badge text-bg-{{ badgeColor() }}">{{ count() ?? notifications().length }}</span>
3127
+ }
3128
+ </a>
3129
+ <div class="dropdown-menu dropdown-menu-lg dropdown-menu-end">
3130
+ <span class="dropdown-item dropdown-header">{{ count() ?? notifications().length }} Notifications</span>
3131
+ @for (n of notifications(); track $index) {
3132
+ <div class="dropdown-divider"></div>
3133
+ <a [href]="n.url || '#'" class="dropdown-item">
3134
+ <i class="{{ biClass(n.icon || 'bi-info-circle') }} {{ n.iconTheme ? 'text-' + n.iconTheme : '' }} me-2"></i>
3135
+ {{ n.text }}
3136
+ @if (n.time) {
3137
+ <span class="float-end text-secondary fs-7">{{ n.time }}</span>
3138
+ }
3139
+ </a>
3140
+ }
3141
+ <div class="dropdown-divider"></div>
3142
+ <a [href]="seeAllUrl()" class="dropdown-item dropdown-footer">{{ seeAllText() }}</a>
3143
+ </div>
3144
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
3145
+ }
3146
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: NavNotificationsComponent, decorators: [{
3147
+ type: Component,
3148
+ args: [{
3149
+ selector: 'lte-nav-notifications, [lte-nav-notifications]',
3150
+ changeDetection: ChangeDetectionStrategy.OnPush,
3151
+ host: { class: 'nav-item dropdown' },
3152
+ template: `
3153
+ <a class="nav-link" data-bs-toggle="dropdown" href="#" (click)="$event.preventDefault()">
3154
+ <i class="bi bi-bell-fill"></i>
3155
+ @if (notifications().length) {
3156
+ <span class="navbar-badge badge text-bg-{{ badgeColor() }}">{{ count() ?? notifications().length }}</span>
3157
+ }
3158
+ </a>
3159
+ <div class="dropdown-menu dropdown-menu-lg dropdown-menu-end">
3160
+ <span class="dropdown-item dropdown-header">{{ count() ?? notifications().length }} Notifications</span>
3161
+ @for (n of notifications(); track $index) {
3162
+ <div class="dropdown-divider"></div>
3163
+ <a [href]="n.url || '#'" class="dropdown-item">
3164
+ <i class="{{ biClass(n.icon || 'bi-info-circle') }} {{ n.iconTheme ? 'text-' + n.iconTheme : '' }} me-2"></i>
3165
+ {{ n.text }}
3166
+ @if (n.time) {
3167
+ <span class="float-end text-secondary fs-7">{{ n.time }}</span>
3168
+ }
3169
+ </a>
3170
+ }
3171
+ <div class="dropdown-divider"></div>
3172
+ <a [href]="seeAllUrl()" class="dropdown-item dropdown-footer">{{ seeAllText() }}</a>
3173
+ </div>
3174
+ `,
3175
+ }]
3176
+ }], propDecorators: { notifications: [{ type: i0.Input, args: [{ isSignal: true, alias: "notifications", required: true }] }], badgeColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "badgeColor", required: false }] }], count: [{ type: i0.Input, args: [{ isSignal: true, alias: "count", required: false }] }], seeAllUrl: [{ type: i0.Input, args: [{ isSignal: true, alias: "seeAllUrl", required: false }] }], seeAllText: [{ type: i0.Input, args: [{ isSignal: true, alias: "seeAllText", required: false }] }] } });
3177
+
3178
+ /**
3179
+ * Topbar tasks dropdown (`<li class="nav-item dropdown">`) with per-task progress
3180
+ * bars. Drop it into the topbar's `[topbar-end]` slot. Uses Bootstrap's dropdown JS.
3181
+ */
3182
+ class NavTasksComponent {
3183
+ tasks = input.required(/* @ts-ignore */
3184
+ ...(ngDevMode ? [{ debugName: "tasks" }] : /* istanbul ignore next */ []));
3185
+ badgeColor = input('success', /* @ts-ignore */
3186
+ ...(ngDevMode ? [{ debugName: "badgeColor" }] : /* istanbul ignore next */ []));
3187
+ count = input(/* @ts-ignore */
3188
+ ...(ngDevMode ? [undefined, { debugName: "count" }] : /* istanbul ignore next */ []));
3189
+ seeAllUrl = input('#', /* @ts-ignore */
3190
+ ...(ngDevMode ? [{ debugName: "seeAllUrl" }] : /* istanbul ignore next */ []));
3191
+ seeAllText = input('View All Tasks', /* @ts-ignore */
3192
+ ...(ngDevMode ? [{ debugName: "seeAllText" }] : /* istanbul ignore next */ []));
3193
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: NavTasksComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3194
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.3", type: NavTasksComponent, isStandalone: true, selector: "lte-nav-tasks, [lte-nav-tasks]", inputs: { tasks: { classPropertyName: "tasks", publicName: "tasks", isSignal: true, isRequired: true, transformFunction: null }, badgeColor: { classPropertyName: "badgeColor", publicName: "badgeColor", isSignal: true, isRequired: false, transformFunction: null }, count: { classPropertyName: "count", publicName: "count", isSignal: true, isRequired: false, transformFunction: null }, seeAllUrl: { classPropertyName: "seeAllUrl", publicName: "seeAllUrl", isSignal: true, isRequired: false, transformFunction: null }, seeAllText: { classPropertyName: "seeAllText", publicName: "seeAllText", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "nav-item dropdown" }, ngImport: i0, template: `
3195
+ <a class="nav-link" data-bs-toggle="dropdown" href="#" (click)="$event.preventDefault()">
3196
+ <i class="bi bi-list-check"></i>
3197
+ @if (tasks().length) {
3198
+ <span class="navbar-badge badge text-bg-{{ badgeColor() }}">{{ count() ?? tasks().length }}</span>
3199
+ }
3200
+ </a>
3201
+ <div class="dropdown-menu dropdown-menu-lg dropdown-menu-end">
3202
+ <span class="dropdown-item dropdown-header">{{ tasks().length }} Tasks</span>
3203
+ @for (task of tasks(); track $index) {
3204
+ <div class="dropdown-divider"></div>
3205
+ <a [href]="task.url || '#'" class="dropdown-item">
3206
+ <h3 class="dropdown-item-title">
3207
+ {{ task.text }}
3208
+ <span class="float-end fs-7">{{ task.progress }}%</span>
3209
+ </h3>
3210
+ <div class="progress progress-sm">
3211
+ <div class="progress-bar" [class]="'bg-' + (task.theme || badgeColor())" [style.width.%]="task.progress"></div>
3212
+ </div>
3213
+ </a>
3214
+ }
3215
+ <div class="dropdown-divider"></div>
3216
+ <a [href]="seeAllUrl()" class="dropdown-item dropdown-footer">{{ seeAllText() }}</a>
3217
+ </div>
3218
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
3219
+ }
3220
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: NavTasksComponent, decorators: [{
3221
+ type: Component,
3222
+ args: [{
3223
+ selector: 'lte-nav-tasks, [lte-nav-tasks]',
3224
+ changeDetection: ChangeDetectionStrategy.OnPush,
3225
+ host: { class: 'nav-item dropdown' },
3226
+ template: `
3227
+ <a class="nav-link" data-bs-toggle="dropdown" href="#" (click)="$event.preventDefault()">
3228
+ <i class="bi bi-list-check"></i>
3229
+ @if (tasks().length) {
3230
+ <span class="navbar-badge badge text-bg-{{ badgeColor() }}">{{ count() ?? tasks().length }}</span>
3231
+ }
3232
+ </a>
3233
+ <div class="dropdown-menu dropdown-menu-lg dropdown-menu-end">
3234
+ <span class="dropdown-item dropdown-header">{{ tasks().length }} Tasks</span>
3235
+ @for (task of tasks(); track $index) {
3236
+ <div class="dropdown-divider"></div>
3237
+ <a [href]="task.url || '#'" class="dropdown-item">
3238
+ <h3 class="dropdown-item-title">
3239
+ {{ task.text }}
3240
+ <span class="float-end fs-7">{{ task.progress }}%</span>
3241
+ </h3>
3242
+ <div class="progress progress-sm">
3243
+ <div class="progress-bar" [class]="'bg-' + (task.theme || badgeColor())" [style.width.%]="task.progress"></div>
3244
+ </div>
3245
+ </a>
3246
+ }
3247
+ <div class="dropdown-divider"></div>
3248
+ <a [href]="seeAllUrl()" class="dropdown-item dropdown-footer">{{ seeAllText() }}</a>
3249
+ </div>
3250
+ `,
3251
+ }]
3252
+ }], propDecorators: { tasks: [{ type: i0.Input, args: [{ isSignal: true, alias: "tasks", required: true }] }], badgeColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "badgeColor", required: false }] }], count: [{ type: i0.Input, args: [{ isSignal: true, alias: "count", required: false }] }], seeAllUrl: [{ type: i0.Input, args: [{ isSignal: true, alias: "seeAllUrl", required: false }] }], seeAllText: [{ type: i0.Input, args: [{ isSignal: true, alias: "seeAllText", required: false }] }] } });
3253
+
3254
+ /**
3255
+ * A single tab panel. Must be a content child of {@link TabsComponent}. The host
3256
+ * carries the panel data (`tabId`, `title`, `icon`, `disabled`); project the panel
3257
+ * body into its default slot. Active state is driven by the parent.
3258
+ */
3259
+ class TabComponent {
3260
+ /** Unique id used to select this tab. */
3261
+ tabId = input.required(/* @ts-ignore */
3262
+ ...(ngDevMode ? [{ debugName: "tabId" }] : /* istanbul ignore next */ []));
3263
+ title = input.required(/* @ts-ignore */
3264
+ ...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
3265
+ icon = input(/* @ts-ignore */
3266
+ ...(ngDevMode ? [undefined, { debugName: "icon" }] : /* istanbul ignore next */ []));
3267
+ disabled = input(false, /* @ts-ignore */
3268
+ ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
3269
+ /** Active flag, toggled by the parent {@link TabsComponent}. */
3270
+ active = signal(false, /* @ts-ignore */
3271
+ ...(ngDevMode ? [{ debugName: "active" }] : /* istanbul ignore next */ []));
3272
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: TabComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3273
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.3", type: TabComponent, isStandalone: true, selector: "lte-tab", inputs: { tabId: { classPropertyName: "tabId", publicName: "tabId", isSignal: true, isRequired: true, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: true, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
3274
+ <div class="tab-pane fade" [class.show]="active()" [class.active]="active()" role="tabpanel" [hidden]="!active()">
3275
+ <ng-content />
3276
+ </div>
3277
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
3278
+ }
3279
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: TabComponent, decorators: [{
3280
+ type: Component,
3281
+ args: [{
3282
+ selector: 'lte-tab',
3283
+ changeDetection: ChangeDetectionStrategy.OnPush,
3284
+ template: `
3285
+ <div class="tab-pane fade" [class.show]="active()" [class.active]="active()" role="tabpanel" [hidden]="!active()">
3286
+ <ng-content />
3287
+ </div>
3288
+ `,
3289
+ }]
3290
+ }], propDecorators: { tabId: [{ type: i0.Input, args: [{ isSignal: true, alias: "tabId", required: true }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: true }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }] } });
3291
+
3292
+ /**
3293
+ * Tab container that renders a nav strip from its projected `<lte-tab>` children
3294
+ * and toggles their panels. `[(active)]` two-way binds the active tab id; when
3295
+ * unset it defaults to the first non-disabled tab.
3296
+ */
3297
+ class TabsComponent {
3298
+ pills = input(false, /* @ts-ignore */
3299
+ ...(ngDevMode ? [{ debugName: "pills" }] : /* istanbul ignore next */ []));
3300
+ justified = input(false, /* @ts-ignore */
3301
+ ...(ngDevMode ? [{ debugName: "justified" }] : /* istanbul ignore next */ []));
3302
+ /** Two-way bound active tab id. */
3303
+ active = model(/* @ts-ignore */
3304
+ ...(ngDevMode ? [undefined, { debugName: "active" }] : /* istanbul ignore next */ []));
3305
+ tabs = contentChildren(TabComponent, /* @ts-ignore */
3306
+ ...(ngDevMode ? [{ debugName: "tabs" }] : /* istanbul ignore next */ []));
3307
+ biClass = biClass;
3308
+ constructor() {
3309
+ // Keep each child panel's active flag in sync with the active id, defaulting
3310
+ // to the first non-disabled tab when none is selected.
3311
+ effect(() => {
3312
+ const tabs = this.tabs();
3313
+ if (tabs.length === 0)
3314
+ return;
3315
+ let current = this.active();
3316
+ const known = tabs.some((t) => t.tabId() === current);
3317
+ if (!current || !known) {
3318
+ current = (tabs.find((t) => !t.disabled()) ?? tabs[0]).tabId();
3319
+ }
3320
+ for (const tab of tabs)
3321
+ tab.active.set(tab.tabId() === current);
3322
+ });
3323
+ }
3324
+ select(id) {
3325
+ const tab = this.tabs().find((t) => t.tabId() === id);
3326
+ if (tab?.disabled())
3327
+ return;
3328
+ this.active.set(id);
3329
+ }
3330
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: TabsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3331
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.3", type: TabsComponent, isStandalone: true, selector: "lte-tabs", inputs: { pills: { classPropertyName: "pills", publicName: "pills", isSignal: true, isRequired: false, transformFunction: null }, justified: { classPropertyName: "justified", publicName: "justified", isSignal: true, isRequired: false, transformFunction: null }, active: { classPropertyName: "active", publicName: "active", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { active: "activeChange" }, queries: [{ propertyName: "tabs", predicate: TabComponent, isSignal: true }], ngImport: i0, template: `
3332
+ <ul class="nav" [class.nav-tabs]="!pills()" [class.nav-pills]="pills()" [class.nav-justified]="justified()" role="tablist">
3333
+ @for (tab of tabs(); track tab.tabId()) {
3334
+ <li class="nav-item" role="presentation">
3335
+ <button
3336
+ type="button"
3337
+ class="nav-link"
3338
+ [class.active]="tab.active()"
3339
+ [class.disabled]="tab.disabled()"
3340
+ role="tab"
3341
+ [attr.aria-selected]="tab.active()"
3342
+ [disabled]="tab.disabled()"
3343
+ (click)="select(tab.tabId())"
3344
+ >
3345
+ @if (tab.icon()) {
3346
+ <i class="{{ biClass(tab.icon()!) }} me-1"></i>
3347
+ }
3348
+ {{ tab.title() }}
3349
+ </button>
3350
+ </li>
3351
+ }
3352
+ </ul>
3353
+ <div class="tab-content pt-3">
3354
+ <ng-content />
3355
+ </div>
3356
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
3357
+ }
3358
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: TabsComponent, decorators: [{
3359
+ type: Component,
3360
+ args: [{
3361
+ selector: 'lte-tabs',
3362
+ changeDetection: ChangeDetectionStrategy.OnPush,
3363
+ template: `
3364
+ <ul class="nav" [class.nav-tabs]="!pills()" [class.nav-pills]="pills()" [class.nav-justified]="justified()" role="tablist">
3365
+ @for (tab of tabs(); track tab.tabId()) {
3366
+ <li class="nav-item" role="presentation">
3367
+ <button
3368
+ type="button"
3369
+ class="nav-link"
3370
+ [class.active]="tab.active()"
3371
+ [class.disabled]="tab.disabled()"
3372
+ role="tab"
3373
+ [attr.aria-selected]="tab.active()"
3374
+ [disabled]="tab.disabled()"
3375
+ (click)="select(tab.tabId())"
3376
+ >
3377
+ @if (tab.icon()) {
3378
+ <i class="{{ biClass(tab.icon()!) }} me-1"></i>
3379
+ }
3380
+ {{ tab.title() }}
3381
+ </button>
3382
+ </li>
3383
+ }
3384
+ </ul>
3385
+ <div class="tab-content pt-3">
3386
+ <ng-content />
3387
+ </div>
3388
+ `,
3389
+ }]
3390
+ }], ctorParameters: () => [], propDecorators: { pills: [{ type: i0.Input, args: [{ isSignal: true, alias: "pills", required: false }] }], justified: [{ type: i0.Input, args: [{ isSignal: true, alias: "justified", required: false }] }], active: [{ type: i0.Input, args: [{ isSignal: true, alias: "active", required: false }] }, { type: i0.Output, args: ["activeChange"] }], tabs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => TabComponent), { isSignal: true }] }] } });
3391
+
3392
+ /**
3393
+ * Bootstrap accordion container. Project `<lte-accordion-item>` children into the
3394
+ * default slot. By default only one item is open at a time; set `[alwaysOpen]` to
3395
+ * allow multiple. `[(active)]` two-way binds the open id(s): a single string when
3396
+ * `alwaysOpen` is false, a string array when true.
3397
+ */
3398
+ class AccordionComponent {
3399
+ flush = input(false, /* @ts-ignore */
3400
+ ...(ngDevMode ? [{ debugName: "flush" }] : /* istanbul ignore next */ []));
3401
+ alwaysOpen = input(false, /* @ts-ignore */
3402
+ ...(ngDevMode ? [{ debugName: "alwaysOpen" }] : /* istanbul ignore next */ []));
3403
+ /** Two-way bound open id(s). */
3404
+ active = model(/* @ts-ignore */
3405
+ ...(ngDevMode ? [undefined, { debugName: "active" }] : /* istanbul ignore next */ []));
3406
+ openIds = signal(new Set(), /* @ts-ignore */
3407
+ ...(ngDevMode ? [{ debugName: "openIds" }] : /* istanbul ignore next */ []));
3408
+ openList = computed(() => [...this.openIds()], /* @ts-ignore */
3409
+ ...(ngDevMode ? [{ debugName: "openList" }] : /* istanbul ignore next */ []));
3410
+ constructor() {
3411
+ // Sync the external model -> internal set.
3412
+ effect(() => {
3413
+ const value = this.active();
3414
+ const ids = value == null ? [] : Array.isArray(value) ? value : [value];
3415
+ this.openIds.set(new Set(ids));
3416
+ });
3417
+ }
3418
+ isOpen(id) {
3419
+ return this.openIds().has(id);
3420
+ }
3421
+ /** Called by a child whose `defaultOpen` is set, before any user interaction. */
3422
+ openInitial(id) {
3423
+ if (this.openIds().has(id))
3424
+ return;
3425
+ this.openIds.update((set) => {
3426
+ const next = this.alwaysOpen() ? new Set(set) : new Set();
3427
+ next.add(id);
3428
+ return next;
3429
+ });
3430
+ this.emit();
3431
+ }
3432
+ toggle(id) {
3433
+ this.openIds.update((set) => {
3434
+ const next = new Set(set);
3435
+ if (next.has(id)) {
3436
+ next.delete(id);
3437
+ }
3438
+ else {
3439
+ if (!this.alwaysOpen())
3440
+ next.clear();
3441
+ next.add(id);
3442
+ }
3443
+ return next;
3444
+ });
3445
+ this.emit();
3446
+ }
3447
+ emit() {
3448
+ const ids = [...this.openIds()];
3449
+ this.active.set(this.alwaysOpen() ? ids : (ids[0] ?? ''));
3450
+ }
3451
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: AccordionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3452
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.3", type: AccordionComponent, isStandalone: true, selector: "lte-accordion", inputs: { flush: { classPropertyName: "flush", publicName: "flush", isSignal: true, isRequired: false, transformFunction: null }, alwaysOpen: { classPropertyName: "alwaysOpen", publicName: "alwaysOpen", isSignal: true, isRequired: false, transformFunction: null }, active: { classPropertyName: "active", publicName: "active", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { active: "activeChange" }, ngImport: i0, template: `
3453
+ <div class="accordion" [class.accordion-flush]="flush()">
3454
+ <ng-content />
3455
+ </div>
3456
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
3457
+ }
3458
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: AccordionComponent, decorators: [{
3459
+ type: Component,
3460
+ args: [{
3461
+ selector: 'lte-accordion',
3462
+ changeDetection: ChangeDetectionStrategy.OnPush,
3463
+ template: `
3464
+ <div class="accordion" [class.accordion-flush]="flush()">
3465
+ <ng-content />
3466
+ </div>
3467
+ `,
3468
+ }]
3469
+ }], ctorParameters: () => [], propDecorators: { flush: [{ type: i0.Input, args: [{ isSignal: true, alias: "flush", required: false }] }], alwaysOpen: [{ type: i0.Input, args: [{ isSignal: true, alias: "alwaysOpen", required: false }] }], active: [{ type: i0.Input, args: [{ isSignal: true, alias: "active", required: false }] }, { type: i0.Output, args: ["activeChange"] }] } });
3470
+
3471
+ /**
3472
+ * A single collapsible panel inside an {@link AccordionComponent}. The host carries
3473
+ * the panel data (`itemId`, `title`, `defaultOpen`); project the body into its
3474
+ * default slot. Open state is read from the parent accordion.
3475
+ */
3476
+ class AccordionItemComponent {
3477
+ parent = inject(AccordionComponent);
3478
+ /** Unique id used to address this item. */
3479
+ itemId = input.required(/* @ts-ignore */
3480
+ ...(ngDevMode ? [{ debugName: "itemId" }] : /* istanbul ignore next */ []));
3481
+ title = input.required(/* @ts-ignore */
3482
+ ...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
3483
+ /** Whether the item starts open. */
3484
+ defaultOpen = input(false, /* @ts-ignore */
3485
+ ...(ngDevMode ? [{ debugName: "defaultOpen" }] : /* istanbul ignore next */ []));
3486
+ open = computed(() => this.parent.isOpen(this.itemId()), /* @ts-ignore */
3487
+ ...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
3488
+ constructor() {
3489
+ queueMicrotask(() => {
3490
+ if (this.defaultOpen())
3491
+ this.parent.openInitial(this.itemId());
3492
+ });
3493
+ }
3494
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: AccordionItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3495
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.3", type: AccordionItemComponent, isStandalone: true, selector: "lte-accordion-item", inputs: { itemId: { classPropertyName: "itemId", publicName: "itemId", isSignal: true, isRequired: true, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: true, transformFunction: null }, defaultOpen: { classPropertyName: "defaultOpen", publicName: "defaultOpen", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
3496
+ <div class="accordion-item">
3497
+ <h2 class="accordion-header">
3498
+ <button
3499
+ type="button"
3500
+ class="accordion-button"
3501
+ [class.collapsed]="!open()"
3502
+ [attr.aria-expanded]="open()"
3503
+ (click)="parent.toggle(itemId())"
3504
+ >
3505
+ {{ title() }}
3506
+ </button>
3507
+ </h2>
3508
+ <div class="accordion-collapse collapse" [class.show]="open()">
3509
+ <div class="accordion-body">
3510
+ <ng-content />
3511
+ </div>
3512
+ </div>
3513
+ </div>
3514
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
3515
+ }
3516
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: AccordionItemComponent, decorators: [{
3517
+ type: Component,
3518
+ args: [{
3519
+ selector: 'lte-accordion-item',
3520
+ changeDetection: ChangeDetectionStrategy.OnPush,
3521
+ template: `
3522
+ <div class="accordion-item">
3523
+ <h2 class="accordion-header">
3524
+ <button
3525
+ type="button"
3526
+ class="accordion-button"
3527
+ [class.collapsed]="!open()"
3528
+ [attr.aria-expanded]="open()"
3529
+ (click)="parent.toggle(itemId())"
3530
+ >
3531
+ {{ title() }}
3532
+ </button>
3533
+ </h2>
3534
+ <div class="accordion-collapse collapse" [class.show]="open()">
3535
+ <div class="accordion-body">
3536
+ <ng-content />
3537
+ </div>
3538
+ </div>
3539
+ </div>
3540
+ `,
3541
+ }]
3542
+ }], ctorParameters: () => [], propDecorators: { itemId: [{ type: i0.Input, args: [{ isSignal: true, alias: "itemId", required: true }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: true }] }], defaultOpen: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultOpen", required: false }] }] } });
3543
+
3544
+ /**
3545
+ * Sortable / searchable / paginated table. Renders a plain Bootstrap `<table>`
3546
+ * from `columns` + `data`, then progressively enhances it with
3547
+ * {@link https://github.com/fiduswriter/simple-datatables simple-datatables} (an
3548
+ * optional peer dep, lazily imported on the browser only). Without the library it
3549
+ * degrades to a static styled table.
3550
+ */
3551
+ class DatatableComponent {
3552
+ host = viewChild.required('host');
3553
+ isBrowser;
3554
+ table = null;
3555
+ columns = input.required(/* @ts-ignore */
3556
+ ...(ngDevMode ? [{ debugName: "columns" }] : /* istanbul ignore next */ []));
3557
+ data = input.required(/* @ts-ignore */
3558
+ ...(ngDevMode ? [{ debugName: "data" }] : /* istanbul ignore next */ []));
3559
+ hover = input(true, /* @ts-ignore */
3560
+ ...(ngDevMode ? [{ debugName: "hover" }] : /* istanbul ignore next */ []));
3561
+ /** Extra simple-datatables options merged over the defaults. */
3562
+ options = input({}, /* @ts-ignore */
3563
+ ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
3564
+ constructor(platformId) {
3565
+ this.isBrowser = isPlatformBrowser(platformId);
3566
+ // Enhance once, after the rows have rendered. Tracks `data`/`columns` so a
3567
+ // fresh dataset rebuilds the instance.
3568
+ effect(() => {
3569
+ this.columns();
3570
+ this.data();
3571
+ if (!this.isBrowser)
3572
+ return;
3573
+ void this.rebuild();
3574
+ });
3575
+ inject(DestroyRef).onDestroy(() => {
3576
+ this.table?.destroy();
3577
+ this.table = null;
3578
+ });
3579
+ }
3580
+ cell(row, key) {
3581
+ return row[key];
3582
+ }
3583
+ async rebuild() {
3584
+ try {
3585
+ const mod = await import('simple-datatables');
3586
+ const DataTable = mod.DataTable;
3587
+ if (!DataTable)
3588
+ return;
3589
+ this.table?.destroy();
3590
+ // Re-read the freshly rendered DOM on the next microtask so the new rows
3591
+ // are present before enhancement.
3592
+ queueMicrotask(() => {
3593
+ this.table = new DataTable(this.host().nativeElement, {
3594
+ searchable: true,
3595
+ perPage: 10,
3596
+ ...this.options(),
3597
+ });
3598
+ });
3599
+ }
3600
+ catch {
3601
+ console.warn('[adminlte-angular] simple-datatables is not installed — <lte-datatable> is a static table.');
3602
+ }
3603
+ }
3604
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: DatatableComponent, deps: [{ token: PLATFORM_ID }], target: i0.ɵɵFactoryTarget.Component });
3605
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.3", type: DatatableComponent, isStandalone: true, selector: "lte-datatable", inputs: { columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: true, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: true, transformFunction: null }, hover: { classPropertyName: "hover", publicName: "hover", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "host", first: true, predicate: ["host"], descendants: true, isSignal: true }], ngImport: i0, template: `
3606
+ <table #host class="table table-striped" [class.table-hover]="hover()">
3607
+ <thead>
3608
+ <tr>
3609
+ @for (col of columns(); track col.key) {
3610
+ <th [attr.data-sortable]="col.sortable === false ? 'false' : null">{{ col.label ?? col.key }}</th>
3611
+ }
3612
+ </tr>
3613
+ </thead>
3614
+ <tbody>
3615
+ @for (row of data(); track $index) {
3616
+ <tr>
3617
+ @for (col of columns(); track col.key) {
3618
+ <td>{{ cell(row, col.key) }}</td>
3619
+ }
3620
+ </tr>
3621
+ }
3622
+ </tbody>
3623
+ </table>
3624
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
3625
+ }
3626
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: DatatableComponent, decorators: [{
3627
+ type: Component,
3628
+ args: [{
3629
+ selector: 'lte-datatable',
3630
+ changeDetection: ChangeDetectionStrategy.OnPush,
3631
+ template: `
3632
+ <table #host class="table table-striped" [class.table-hover]="hover()">
3633
+ <thead>
3634
+ <tr>
3635
+ @for (col of columns(); track col.key) {
3636
+ <th [attr.data-sortable]="col.sortable === false ? 'false' : null">{{ col.label ?? col.key }}</th>
3637
+ }
3638
+ </tr>
3639
+ </thead>
3640
+ <tbody>
3641
+ @for (row of data(); track $index) {
3642
+ <tr>
3643
+ @for (col of columns(); track col.key) {
3644
+ <td>{{ cell(row, col.key) }}</td>
3645
+ }
3646
+ </tr>
3647
+ }
3648
+ </tbody>
3649
+ </table>
3650
+ `,
3651
+ }]
3652
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
3653
+ type: Inject,
3654
+ args: [PLATFORM_ID]
3655
+ }] }], propDecorators: { host: [{ type: i0.ViewChild, args: ['host', { isSignal: true }] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: true }] }], data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: true }] }], hover: [{ type: i0.Input, args: [{ isSignal: true, alias: "hover", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }] } });
3656
+
3657
+ /**
3658
+ * Bootstrap button with theme, size, outline and loading states. Renders a real
3659
+ * `<button>` so it participates in forms; project the label into the slot.
3660
+ */
3661
+ class ButtonComponent {
3662
+ theme = input('primary', /* @ts-ignore */
3663
+ ...(ngDevMode ? [{ debugName: "theme" }] : /* istanbul ignore next */ []));
3664
+ size = input(/* @ts-ignore */
3665
+ ...(ngDevMode ? [undefined, { debugName: "size" }] : /* istanbul ignore next */ []));
3666
+ outline = input(false, /* @ts-ignore */
3667
+ ...(ngDevMode ? [{ debugName: "outline" }] : /* istanbul ignore next */ []));
3668
+ block = input(false, /* @ts-ignore */
3669
+ ...(ngDevMode ? [{ debugName: "block" }] : /* istanbul ignore next */ []));
3670
+ icon = input(/* @ts-ignore */
3671
+ ...(ngDevMode ? [undefined, { debugName: "icon" }] : /* istanbul ignore next */ []));
3672
+ loading = input(false, /* @ts-ignore */
3673
+ ...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
3674
+ disabled = input(false, /* @ts-ignore */
3675
+ ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
3676
+ type = input('button', /* @ts-ignore */
3677
+ ...(ngDevMode ? [{ debugName: "type" }] : /* istanbul ignore next */ []));
3678
+ btnClass = computed(() => cn('btn', this.outline() ? `btn-outline-${this.theme()}` : `btn-${this.theme()}`, this.size() && `btn-${this.size()}`, this.block() && 'w-100'), /* @ts-ignore */
3679
+ ...(ngDevMode ? [{ debugName: "btnClass" }] : /* istanbul ignore next */ []));
3680
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: ButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3681
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.3", type: ButtonComponent, isStandalone: true, selector: "lte-button", inputs: { theme: { classPropertyName: "theme", publicName: "theme", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, outline: { classPropertyName: "outline", publicName: "outline", isSignal: true, isRequired: false, transformFunction: null }, block: { classPropertyName: "block", publicName: "block", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
3682
+ <button [type]="type()" [class]="btnClass()" [disabled]="disabled() || loading()">
3683
+ @if (loading()) {
3684
+ <span class="spinner-border spinner-border-sm me-1" role="status" aria-hidden="true"></span>
3685
+ } @else if (icon()) {
3686
+ <i class="bi {{ icon() }} me-1"></i>
3687
+ }
3688
+ <ng-content />
3689
+ </button>
3690
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
3691
+ }
3692
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: ButtonComponent, decorators: [{
3693
+ type: Component,
3694
+ args: [{
3695
+ selector: 'lte-button',
3696
+ changeDetection: ChangeDetectionStrategy.OnPush,
3697
+ template: `
3698
+ <button [type]="type()" [class]="btnClass()" [disabled]="disabled() || loading()">
3699
+ @if (loading()) {
3700
+ <span class="spinner-border spinner-border-sm me-1" role="status" aria-hidden="true"></span>
3701
+ } @else if (icon()) {
3702
+ <i class="bi {{ icon() }} me-1"></i>
3703
+ }
3704
+ <ng-content />
3705
+ </button>
3706
+ `,
3707
+ }]
3708
+ }], propDecorators: { theme: [{ type: i0.Input, args: [{ isSignal: true, alias: "theme", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], outline: [{ type: i0.Input, args: [{ isSignal: true, alias: "outline", required: false }] }], block: [{ type: i0.Input, args: [{ isSignal: true, alias: "block", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }] } });
3709
+
3710
+ /**
3711
+ * Labeled text input with an optional Bootstrap Icons prefix and validation
3712
+ * feedback. Implements ControlValueAccessor so it works with reactive,
3713
+ * template-driven and Signal Forms; `[(value)]` also works for simple cases.
3714
+ */
3715
+ class InputComponent {
3716
+ label = input(/* @ts-ignore */
3717
+ ...(ngDevMode ? [undefined, { debugName: "label" }] : /* istanbul ignore next */ []));
3718
+ type = input('text', /* @ts-ignore */
3719
+ ...(ngDevMode ? [{ debugName: "type" }] : /* istanbul ignore next */ []));
3720
+ placeholder = input('', /* @ts-ignore */
3721
+ ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
3722
+ icon = input(/* @ts-ignore */
3723
+ ...(ngDevMode ? [undefined, { debugName: "icon" }] : /* istanbul ignore next */ []));
3724
+ hint = input(/* @ts-ignore */
3725
+ ...(ngDevMode ? [undefined, { debugName: "hint" }] : /* istanbul ignore next */ []));
3726
+ error = input(/* @ts-ignore */
3727
+ ...(ngDevMode ? [undefined, { debugName: "error" }] : /* istanbul ignore next */ []));
3728
+ autocomplete = input(/* @ts-ignore */
3729
+ ...(ngDevMode ? [undefined, { debugName: "autocomplete" }] : /* istanbul ignore next */ []));
3730
+ id = input(`lte-input-${idCounter$5++}`, /* @ts-ignore */
3731
+ ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
3732
+ value = model('', /* @ts-ignore */
3733
+ ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
3734
+ disabled = signal(false, /* @ts-ignore */
3735
+ ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
3736
+ controlClass = computed(() => `form-control${this.error() ? ' is-invalid' : ''}`, /* @ts-ignore */
3737
+ ...(ngDevMode ? [{ debugName: "controlClass" }] : /* istanbul ignore next */ []));
3738
+ onChange = () => { };
3739
+ onTouched = () => { };
3740
+ onInput(e) {
3741
+ const v = e.target.value;
3742
+ this.value.set(v);
3743
+ this.onChange(v);
3744
+ }
3745
+ writeValue(value) {
3746
+ this.value.set(value ?? '');
3747
+ }
3748
+ registerOnChange(fn) {
3749
+ this.onChange = fn;
3750
+ }
3751
+ registerOnTouched(fn) {
3752
+ this.onTouched = fn;
3753
+ }
3754
+ setDisabledState(isDisabled) {
3755
+ this.disabled.set(isDisabled);
3756
+ }
3757
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: InputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3758
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.3", type: InputComponent, isStandalone: true, selector: "lte-input", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, autocomplete: { classPropertyName: "autocomplete", publicName: "autocomplete", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, providers: [
3759
+ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => InputComponent), multi: true },
3760
+ ], ngImport: i0, template: `
3761
+ @if (label()) {
3762
+ <label [for]="id()" class="form-label">{{ label() }}</label>
3763
+ }
3764
+ <div [class.input-group]="!!icon()">
3765
+ <input
3766
+ [id]="id()"
3767
+ [type]="type()"
3768
+ [class]="controlClass()"
3769
+ [placeholder]="placeholder()"
3770
+ [value]="value()"
3771
+ [disabled]="disabled()"
3772
+ [attr.autocomplete]="autocomplete()"
3773
+ (input)="onInput($event)"
3774
+ (blur)="onTouched()"
3775
+ />
3776
+ @if (icon()) {
3777
+ <span class="input-group-text"><i class="bi {{ icon() }}"></i></span>
3778
+ }
3779
+ </div>
3780
+ @if (error()) {
3781
+ <div class="invalid-feedback d-block">{{ error() }}</div>
3782
+ } @else if (hint()) {
3783
+ <div class="form-text">{{ hint() }}</div>
3784
+ }
3785
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
3786
+ }
3787
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: InputComponent, decorators: [{
3788
+ type: Component,
3789
+ args: [{
3790
+ selector: 'lte-input',
3791
+ changeDetection: ChangeDetectionStrategy.OnPush,
3792
+ providers: [
3793
+ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => InputComponent), multi: true },
3794
+ ],
3795
+ template: `
3796
+ @if (label()) {
3797
+ <label [for]="id()" class="form-label">{{ label() }}</label>
3798
+ }
3799
+ <div [class.input-group]="!!icon()">
3800
+ <input
3801
+ [id]="id()"
3802
+ [type]="type()"
3803
+ [class]="controlClass()"
3804
+ [placeholder]="placeholder()"
3805
+ [value]="value()"
3806
+ [disabled]="disabled()"
3807
+ [attr.autocomplete]="autocomplete()"
3808
+ (input)="onInput($event)"
3809
+ (blur)="onTouched()"
3810
+ />
3811
+ @if (icon()) {
3812
+ <span class="input-group-text"><i class="bi {{ icon() }}"></i></span>
3813
+ }
3814
+ </div>
3815
+ @if (error()) {
3816
+ <div class="invalid-feedback d-block">{{ error() }}</div>
3817
+ } @else if (hint()) {
3818
+ <div class="form-text">{{ hint() }}</div>
3819
+ }
3820
+ `,
3821
+ }]
3822
+ }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], autocomplete: [{ type: i0.Input, args: [{ isSignal: true, alias: "autocomplete", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }] } });
3823
+ let idCounter$5 = 0;
3824
+
3825
+ let idCounter$4 = 0;
3826
+ /**
3827
+ * Labeled textarea with validation feedback. ControlValueAccessor-backed, so it
3828
+ * works with reactive, template-driven and Signal Forms.
3829
+ */
3830
+ class TextareaComponent {
3831
+ label = input(/* @ts-ignore */
3832
+ ...(ngDevMode ? [undefined, { debugName: "label" }] : /* istanbul ignore next */ []));
3833
+ placeholder = input('', /* @ts-ignore */
3834
+ ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
3835
+ rows = input(3, /* @ts-ignore */
3836
+ ...(ngDevMode ? [{ debugName: "rows" }] : /* istanbul ignore next */ []));
3837
+ hint = input(/* @ts-ignore */
3838
+ ...(ngDevMode ? [undefined, { debugName: "hint" }] : /* istanbul ignore next */ []));
3839
+ error = input(/* @ts-ignore */
3840
+ ...(ngDevMode ? [undefined, { debugName: "error" }] : /* istanbul ignore next */ []));
3841
+ id = input(`lte-textarea-${idCounter$4++}`, /* @ts-ignore */
3842
+ ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
3843
+ value = model('', /* @ts-ignore */
3844
+ ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
3845
+ disabled = signal(false, /* @ts-ignore */
3846
+ ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
3847
+ controlClass = computed(() => `form-control${this.error() ? ' is-invalid' : ''}`, /* @ts-ignore */
3848
+ ...(ngDevMode ? [{ debugName: "controlClass" }] : /* istanbul ignore next */ []));
3849
+ onChange = () => { };
3850
+ onTouched = () => { };
3851
+ onInput(e) {
3852
+ const v = e.target.value;
3853
+ this.value.set(v);
3854
+ this.onChange(v);
3855
+ }
3856
+ writeValue(value) {
3857
+ this.value.set(value ?? '');
3858
+ }
3859
+ registerOnChange(fn) {
3860
+ this.onChange = fn;
3861
+ }
3862
+ registerOnTouched(fn) {
3863
+ this.onTouched = fn;
3864
+ }
3865
+ setDisabledState(isDisabled) {
3866
+ this.disabled.set(isDisabled);
3867
+ }
3868
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: TextareaComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3869
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.3", type: TextareaComponent, isStandalone: true, selector: "lte-textarea", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, providers: [
3870
+ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => TextareaComponent), multi: true },
3871
+ ], ngImport: i0, template: `
3872
+ @if (label()) {
3873
+ <label [for]="id()" class="form-label">{{ label() }}</label>
3874
+ }
3875
+ <textarea
3876
+ [id]="id()"
3877
+ [class]="controlClass()"
3878
+ [placeholder]="placeholder()"
3879
+ [rows]="rows()"
3880
+ [value]="value()"
3881
+ [disabled]="disabled()"
3882
+ (input)="onInput($event)"
3883
+ (blur)="onTouched()"
3884
+ ></textarea>
3885
+ @if (error()) {
3886
+ <div class="invalid-feedback d-block">{{ error() }}</div>
3887
+ } @else if (hint()) {
3888
+ <div class="form-text">{{ hint() }}</div>
3889
+ }
3890
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
3891
+ }
3892
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: TextareaComponent, decorators: [{
3893
+ type: Component,
3894
+ args: [{
3895
+ selector: 'lte-textarea',
3896
+ changeDetection: ChangeDetectionStrategy.OnPush,
3897
+ providers: [
3898
+ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => TextareaComponent), multi: true },
3899
+ ],
3900
+ template: `
3901
+ @if (label()) {
3902
+ <label [for]="id()" class="form-label">{{ label() }}</label>
3903
+ }
3904
+ <textarea
3905
+ [id]="id()"
3906
+ [class]="controlClass()"
3907
+ [placeholder]="placeholder()"
3908
+ [rows]="rows()"
3909
+ [value]="value()"
3910
+ [disabled]="disabled()"
3911
+ (input)="onInput($event)"
3912
+ (blur)="onTouched()"
3913
+ ></textarea>
3914
+ @if (error()) {
3915
+ <div class="invalid-feedback d-block">{{ error() }}</div>
3916
+ } @else if (hint()) {
3917
+ <div class="form-text">{{ hint() }}</div>
3918
+ }
3919
+ `,
3920
+ }]
3921
+ }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }] } });
3922
+
3923
+ let idCounter$3 = 0;
3924
+ /**
3925
+ * Labeled native select. Pass `options`, or project `<option>` elements into the
3926
+ * default slot. ControlValueAccessor-backed.
3927
+ */
3928
+ class SelectComponent {
3929
+ label = input(/* @ts-ignore */
3930
+ ...(ngDevMode ? [undefined, { debugName: "label" }] : /* istanbul ignore next */ []));
3931
+ placeholder = input('', /* @ts-ignore */
3932
+ ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
3933
+ options = input([], /* @ts-ignore */
3934
+ ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
3935
+ hint = input(/* @ts-ignore */
3936
+ ...(ngDevMode ? [undefined, { debugName: "hint" }] : /* istanbul ignore next */ []));
3937
+ error = input(/* @ts-ignore */
3938
+ ...(ngDevMode ? [undefined, { debugName: "error" }] : /* istanbul ignore next */ []));
3939
+ id = input(`lte-select-${idCounter$3++}`, /* @ts-ignore */
3940
+ ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
3941
+ value = model('', /* @ts-ignore */
3942
+ ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
3943
+ disabled = signal(false, /* @ts-ignore */
3944
+ ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
3945
+ controlClass = computed(() => `form-select${this.error() ? ' is-invalid' : ''}`, /* @ts-ignore */
3946
+ ...(ngDevMode ? [{ debugName: "controlClass" }] : /* istanbul ignore next */ []));
3947
+ onChange = () => { };
3948
+ onTouched = () => { };
3949
+ onSelect(e) {
3950
+ const v = e.target.value;
3951
+ this.value.set(v);
3952
+ this.onChange(v);
3953
+ }
3954
+ writeValue(value) {
3955
+ this.value.set(value ?? '');
3956
+ }
3957
+ registerOnChange(fn) {
3958
+ this.onChange = fn;
3959
+ }
3960
+ registerOnTouched(fn) {
3961
+ this.onTouched = fn;
3962
+ }
3963
+ setDisabledState(isDisabled) {
3964
+ this.disabled.set(isDisabled);
3965
+ }
3966
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: SelectComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3967
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.3", type: SelectComponent, isStandalone: true, selector: "lte-select", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, providers: [
3968
+ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => SelectComponent), multi: true },
3969
+ ], ngImport: i0, template: `
3970
+ @if (label()) {
3971
+ <label [for]="id()" class="form-label">{{ label() }}</label>
3972
+ }
3973
+ <select
3974
+ [id]="id()"
3975
+ [class]="controlClass()"
3976
+ [disabled]="disabled()"
3977
+ [value]="value()"
3978
+ (change)="onSelect($event)"
3979
+ (blur)="onTouched()"
3980
+ >
3981
+ @if (placeholder()) {
3982
+ <option value="" disabled>{{ placeholder() }}</option>
3983
+ }
3984
+ @for (opt of options(); track opt.value) {
3985
+ <option [value]="opt.value" [disabled]="opt.disabled ?? false">{{ opt.label }}</option>
3986
+ }
3987
+ <ng-content />
3988
+ </select>
3989
+ @if (error()) {
3990
+ <div class="invalid-feedback d-block">{{ error() }}</div>
3991
+ } @else if (hint()) {
3992
+ <div class="form-text">{{ hint() }}</div>
3993
+ }
3994
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
3995
+ }
3996
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: SelectComponent, decorators: [{
3997
+ type: Component,
3998
+ args: [{
3999
+ selector: 'lte-select',
4000
+ changeDetection: ChangeDetectionStrategy.OnPush,
4001
+ providers: [
4002
+ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => SelectComponent), multi: true },
4003
+ ],
4004
+ template: `
4005
+ @if (label()) {
4006
+ <label [for]="id()" class="form-label">{{ label() }}</label>
4007
+ }
4008
+ <select
4009
+ [id]="id()"
4010
+ [class]="controlClass()"
4011
+ [disabled]="disabled()"
4012
+ [value]="value()"
4013
+ (change)="onSelect($event)"
4014
+ (blur)="onTouched()"
4015
+ >
4016
+ @if (placeholder()) {
4017
+ <option value="" disabled>{{ placeholder() }}</option>
4018
+ }
4019
+ @for (opt of options(); track opt.value) {
4020
+ <option [value]="opt.value" [disabled]="opt.disabled ?? false">{{ opt.label }}</option>
4021
+ }
4022
+ <ng-content />
4023
+ </select>
4024
+ @if (error()) {
4025
+ <div class="invalid-feedback d-block">{{ error() }}</div>
4026
+ } @else if (hint()) {
4027
+ <div class="form-text">{{ hint() }}</div>
4028
+ }
4029
+ `,
4030
+ }]
4031
+ }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }] } });
4032
+
4033
+ let idCounter$2 = 0;
4034
+ /**
4035
+ * Bootstrap toggle switch (a styled checkbox). ControlValueAccessor-backed, with
4036
+ * a `[(checked)]` model for simple two-way binding.
4037
+ */
4038
+ class InputSwitchComponent {
4039
+ label = input(/* @ts-ignore */
4040
+ ...(ngDevMode ? [undefined, { debugName: "label" }] : /* istanbul ignore next */ []));
4041
+ id = input(`lte-switch-${idCounter$2++}`, /* @ts-ignore */
4042
+ ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
4043
+ checked = model(false, /* @ts-ignore */
4044
+ ...(ngDevMode ? [{ debugName: "checked" }] : /* istanbul ignore next */ []));
4045
+ disabled = signal(false, /* @ts-ignore */
4046
+ ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
4047
+ onChange = () => { };
4048
+ onTouched = () => { };
4049
+ onToggle(e) {
4050
+ const v = e.target.checked;
4051
+ this.checked.set(v);
4052
+ this.onChange(v);
4053
+ }
4054
+ writeValue(value) {
4055
+ this.checked.set(!!value);
4056
+ }
4057
+ registerOnChange(fn) {
4058
+ this.onChange = fn;
4059
+ }
4060
+ registerOnTouched(fn) {
4061
+ this.onTouched = fn;
4062
+ }
4063
+ setDisabledState(isDisabled) {
4064
+ this.disabled.set(isDisabled);
4065
+ }
4066
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: InputSwitchComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4067
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.3", type: InputSwitchComponent, isStandalone: true, selector: "lte-input-switch", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, checked: { classPropertyName: "checked", publicName: "checked", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { checked: "checkedChange" }, providers: [
4068
+ {
4069
+ provide: NG_VALUE_ACCESSOR,
4070
+ useExisting: forwardRef(() => InputSwitchComponent),
4071
+ multi: true,
4072
+ },
4073
+ ], ngImport: i0, template: `
4074
+ <div class="form-check form-switch">
4075
+ <input
4076
+ class="form-check-input"
4077
+ type="checkbox"
4078
+ role="switch"
4079
+ [id]="id()"
4080
+ [checked]="checked()"
4081
+ [disabled]="disabled()"
4082
+ (change)="onToggle($event)"
4083
+ (blur)="onTouched()"
4084
+ />
4085
+ @if (label()) {
4086
+ <label class="form-check-label" [for]="id()">{{ label() }}</label>
4087
+ }
4088
+ </div>
4089
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
4090
+ }
4091
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: InputSwitchComponent, decorators: [{
4092
+ type: Component,
4093
+ args: [{
4094
+ selector: 'lte-input-switch',
4095
+ changeDetection: ChangeDetectionStrategy.OnPush,
4096
+ providers: [
4097
+ {
4098
+ provide: NG_VALUE_ACCESSOR,
4099
+ useExisting: forwardRef(() => InputSwitchComponent),
4100
+ multi: true,
4101
+ },
4102
+ ],
4103
+ template: `
4104
+ <div class="form-check form-switch">
4105
+ <input
4106
+ class="form-check-input"
4107
+ type="checkbox"
4108
+ role="switch"
4109
+ [id]="id()"
4110
+ [checked]="checked()"
4111
+ [disabled]="disabled()"
4112
+ (change)="onToggle($event)"
4113
+ (blur)="onTouched()"
4114
+ />
4115
+ @if (label()) {
4116
+ <label class="form-check-label" [for]="id()">{{ label() }}</label>
4117
+ }
4118
+ </div>
4119
+ `,
4120
+ }]
4121
+ }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], checked: [{ type: i0.Input, args: [{ isSignal: true, alias: "checked", required: false }] }, { type: i0.Output, args: ["checkedChange"] }] } });
4122
+
4123
+ let idCounter$1 = 0;
4124
+ /**
4125
+ * Labeled date input backed by {@link https://flatpickr.js.org/ flatpickr} (an
4126
+ * optional peer dep, lazily imported on the browser only). Two-way bind the
4127
+ * selected date string with `[(value)]`; also a ControlValueAccessor.
4128
+ */
4129
+ class InputFlatpickrComponent {
4130
+ host = viewChild.required('host');
4131
+ isBrowser;
4132
+ picker = null;
4133
+ label = input(/* @ts-ignore */
4134
+ ...(ngDevMode ? [undefined, { debugName: "label" }] : /* istanbul ignore next */ []));
4135
+ placeholder = input('', /* @ts-ignore */
4136
+ ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
4137
+ fgroupClass = input('', /* @ts-ignore */
4138
+ ...(ngDevMode ? [{ debugName: "fgroupClass" }] : /* istanbul ignore next */ []));
4139
+ id = input(`lte-flatpickr-${idCounter$1++}`, /* @ts-ignore */
4140
+ ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
4141
+ /** Extra flatpickr options (mode, dateFormat, …). */
4142
+ options = input({}, /* @ts-ignore */
4143
+ ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
4144
+ value = model('', /* @ts-ignore */
4145
+ ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
4146
+ onChange = () => { };
4147
+ onTouched = () => { };
4148
+ constructor(platformId) {
4149
+ this.isBrowser = isPlatformBrowser(platformId);
4150
+ // Initialise once the view is ready, then keep the picker's date in sync
4151
+ // when `value` changes from the outside.
4152
+ effect(() => {
4153
+ const v = this.value();
4154
+ if (!this.isBrowser)
4155
+ return;
4156
+ if (!this.picker) {
4157
+ void this.init(v);
4158
+ }
4159
+ else if (v !== this.host().nativeElement.value) {
4160
+ this.picker.setDate(v, false);
4161
+ }
4162
+ });
4163
+ inject(DestroyRef).onDestroy(() => {
4164
+ this.picker?.destroy();
4165
+ this.picker = null;
4166
+ });
4167
+ }
4168
+ async init(initial) {
4169
+ try {
4170
+ const mod = await import('flatpickr');
4171
+ const flatpickr = (mod.default ?? mod);
4172
+ this.picker = flatpickr(this.host().nativeElement, {
4173
+ ...this.options(),
4174
+ defaultDate: initial || undefined,
4175
+ onChange: (_dates, dateStr) => {
4176
+ this.value.set(dateStr);
4177
+ this.onChange(dateStr);
4178
+ this.onTouched();
4179
+ },
4180
+ });
4181
+ }
4182
+ catch {
4183
+ console.warn('[adminlte-angular] flatpickr is not installed — <lte-input-flatpickr> is a plain text input.');
4184
+ }
4185
+ }
4186
+ writeValue(value) {
4187
+ this.value.set(value ?? '');
4188
+ }
4189
+ registerOnChange(fn) {
4190
+ this.onChange = fn;
4191
+ }
4192
+ registerOnTouched(fn) {
4193
+ this.onTouched = fn;
4194
+ }
4195
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: InputFlatpickrComponent, deps: [{ token: PLATFORM_ID }], target: i0.ɵɵFactoryTarget.Component });
4196
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.3", type: InputFlatpickrComponent, isStandalone: true, selector: "lte-input-flatpickr", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, fgroupClass: { classPropertyName: "fgroupClass", publicName: "fgroupClass", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, providers: [
4197
+ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => InputFlatpickrComponent), multi: true },
4198
+ ], viewQueries: [{ propertyName: "host", first: true, predicate: ["host"], descendants: true, isSignal: true }], ngImport: i0, template: `
4199
+ <div [class]="'mb-3 ' + fgroupClass()">
4200
+ @if (label()) {
4201
+ <label [for]="id()" class="form-label">{{ label() }}</label>
4202
+ }
4203
+ <input #host [id]="id()" type="text" class="form-control" [placeholder]="placeholder()" [value]="value()" />
4204
+ </div>
4205
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
4206
+ }
4207
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: InputFlatpickrComponent, decorators: [{
4208
+ type: Component,
4209
+ args: [{
4210
+ selector: 'lte-input-flatpickr',
4211
+ changeDetection: ChangeDetectionStrategy.OnPush,
4212
+ providers: [
4213
+ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => InputFlatpickrComponent), multi: true },
4214
+ ],
4215
+ template: `
4216
+ <div [class]="'mb-3 ' + fgroupClass()">
4217
+ @if (label()) {
4218
+ <label [for]="id()" class="form-label">{{ label() }}</label>
4219
+ }
4220
+ <input #host [id]="id()" type="text" class="form-control" [placeholder]="placeholder()" [value]="value()" />
4221
+ </div>
4222
+ `,
4223
+ }]
4224
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
4225
+ type: Inject,
4226
+ args: [PLATFORM_ID]
4227
+ }] }], propDecorators: { host: [{ type: i0.ViewChild, args: ['host', { isSignal: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], fgroupClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "fgroupClass", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }] } });
4228
+
4229
+ let idCounter = 0;
4230
+ /**
4231
+ * Labeled enhanced `<select>` backed by {@link https://tom-select.js.org/ Tom Select}
4232
+ * (an optional peer dep, lazily imported on the browser only). Supports single or
4233
+ * `[multiple]` selection. Two-way bind with `[(value)]`; also a ControlValueAccessor.
4234
+ */
4235
+ class InputTomSelectComponent {
4236
+ host = viewChild.required('host');
4237
+ isBrowser;
4238
+ instance = null;
4239
+ initialized = false;
4240
+ label = input(/* @ts-ignore */
4241
+ ...(ngDevMode ? [undefined, { debugName: "label" }] : /* istanbul ignore next */ []));
4242
+ placeholder = input('', /* @ts-ignore */
4243
+ ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
4244
+ fgroupClass = input('', /* @ts-ignore */
4245
+ ...(ngDevMode ? [{ debugName: "fgroupClass" }] : /* istanbul ignore next */ []));
4246
+ multiple = input(false, /* @ts-ignore */
4247
+ ...(ngDevMode ? [{ debugName: "multiple" }] : /* istanbul ignore next */ []));
4248
+ options = input([], /* @ts-ignore */
4249
+ ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
4250
+ id = input(`lte-tomselect-${idCounter++}`, /* @ts-ignore */
4251
+ ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
4252
+ /** Extra Tom Select settings merged over the defaults. */
4253
+ settings = input({}, /* @ts-ignore */
4254
+ ...(ngDevMode ? [{ debugName: "settings" }] : /* istanbul ignore next */ []));
4255
+ value = model('', /* @ts-ignore */
4256
+ ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
4257
+ onChange = () => { };
4258
+ onTouched = () => { };
4259
+ constructor(platformId) {
4260
+ this.isBrowser = isPlatformBrowser(platformId);
4261
+ effect(() => {
4262
+ const v = this.value();
4263
+ if (!this.isBrowser)
4264
+ return;
4265
+ if (!this.initialized) {
4266
+ this.initialized = true;
4267
+ void this.init(v);
4268
+ }
4269
+ else if (this.instance && v != null) {
4270
+ this.instance.setValue(v, true);
4271
+ }
4272
+ });
4273
+ inject(DestroyRef).onDestroy(() => {
4274
+ this.instance?.destroy();
4275
+ this.instance = null;
4276
+ });
4277
+ }
4278
+ async init(initial) {
4279
+ try {
4280
+ const mod = await import('tom-select');
4281
+ const TomSelect = (mod.default ?? mod);
4282
+ this.instance = new TomSelect(this.host().nativeElement, {
4283
+ options: this.options(),
4284
+ placeholder: this.placeholder() || undefined,
4285
+ ...this.settings(),
4286
+ onChange: (value) => {
4287
+ this.value.set(value);
4288
+ this.onChange(value);
4289
+ this.onTouched();
4290
+ },
4291
+ });
4292
+ if (initial != null && initial !== '')
4293
+ this.instance.setValue(initial, true);
4294
+ }
4295
+ catch {
4296
+ console.warn('[adminlte-angular] tom-select is not installed — <lte-input-tom-select> is a plain select.');
4297
+ }
4298
+ }
4299
+ writeValue(value) {
4300
+ this.value.set(value ?? '');
4301
+ }
4302
+ registerOnChange(fn) {
4303
+ this.onChange = fn;
4304
+ }
4305
+ registerOnTouched(fn) {
4306
+ this.onTouched = fn;
4307
+ }
4308
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: InputTomSelectComponent, deps: [{ token: PLATFORM_ID }], target: i0.ɵɵFactoryTarget.Component });
4309
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.3", type: InputTomSelectComponent, isStandalone: true, selector: "lte-input-tom-select", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, fgroupClass: { classPropertyName: "fgroupClass", publicName: "fgroupClass", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, settings: { classPropertyName: "settings", publicName: "settings", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, providers: [
4310
+ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => InputTomSelectComponent), multi: true },
4311
+ ], viewQueries: [{ propertyName: "host", first: true, predicate: ["host"], descendants: true, isSignal: true }], ngImport: i0, template: `
4312
+ <div [class]="'mb-3 ' + fgroupClass()">
4313
+ @if (label()) {
4314
+ <label [for]="id()" class="form-label">{{ label() }}</label>
4315
+ }
4316
+ <select #host [id]="id()" [multiple]="multiple()">
4317
+ @for (opt of options(); track opt.value) {
4318
+ <option [value]="opt.value">{{ opt.text }}</option>
4319
+ }
4320
+ </select>
4321
+ </div>
4322
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
4323
+ }
4324
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.3", ngImport: i0, type: InputTomSelectComponent, decorators: [{
4325
+ type: Component,
4326
+ args: [{
4327
+ selector: 'lte-input-tom-select',
4328
+ changeDetection: ChangeDetectionStrategy.OnPush,
4329
+ providers: [
4330
+ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => InputTomSelectComponent), multi: true },
4331
+ ],
4332
+ template: `
4333
+ <div [class]="'mb-3 ' + fgroupClass()">
4334
+ @if (label()) {
4335
+ <label [for]="id()" class="form-label">{{ label() }}</label>
4336
+ }
4337
+ <select #host [id]="id()" [multiple]="multiple()">
4338
+ @for (opt of options(); track opt.value) {
4339
+ <option [value]="opt.value">{{ opt.text }}</option>
4340
+ }
4341
+ </select>
4342
+ </div>
4343
+ `,
4344
+ }]
4345
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
4346
+ type: Inject,
4347
+ args: [PLATFORM_ID]
4348
+ }] }], propDecorators: { host: [{ type: i0.ViewChild, args: ['host', { isSignal: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], fgroupClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "fgroupClass", required: false }] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], settings: [{ type: i0.Input, args: [{ isSignal: true, alias: "settings", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }] } });
4349
+
4350
+ /*
4351
+ * Public API surface of @adminlte/angular.
4352
+ */
4353
+ // Services
4354
+
4355
+ /**
4356
+ * Generated bundle index. Do not edit.
4357
+ */
4358
+
4359
+ export { AccordionComponent, AccordionItemComponent, AlertComponent, ApexChartComponent, AppContentComponent, AuthLayoutComponent, BreadcrumbComponent, ButtonComponent, COLOR_MODE_NO_FLASH_SCRIPT, CalloutComponent, CardComponent, ColorModeService, ColorModeToggleComponent, CommandPaletteComponent, CommandPaletteService, DashboardLayoutComponent, DatatableComponent, DescriptionBlockComponent, DirectChatComponent, FooterComponent, FullscreenService, FullscreenToggleComponent, InfoBoxComponent, InputComponent, InputFlatpickrComponent, InputSwitchComponent, InputTomSelectComponent, ModalComponent, NavMessagesComponent, NavNotificationsComponent, NavTasksComponent, ProfileCardComponent, ProgressComponent, ProgressGroupComponent, RatingsComponent, SelectComponent, SidebarBrandComponent, SidebarComponent, SidebarNavComponent, SidebarNavItemComponent, SidebarOverlayComponent, SidebarService, SmallBoxComponent, TabComponent, TabsComponent, TextareaComponent, TimelineComponent, TopbarComponent, TreeviewService, biClass, cn, flattenMenuToCommands };
4360
+ //# sourceMappingURL=adminlte-angular.mjs.map