shirkasoft-ui-components 1.1.2 → 1.1.3

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.
@@ -1,5 +1,5 @@
1
1
  import * as i1 from '@angular/common';
2
- import { CommonModule, isPlatformBrowser } from '@angular/common';
2
+ import { CommonModule, DOCUMENT, isPlatformBrowser } from '@angular/common';
3
3
  import * as i0 from '@angular/core';
4
4
  import { inject, ChangeDetectorRef, input, output, viewChild, signal, computed, DestroyRef, effect, forwardRef, ChangeDetectionStrategy, Component, Injectable, model, ElementRef, HostListener, ApplicationRef, EnvironmentInjector, createComponent, ViewContainerRef, untracked, PLATFORM_ID, Inject, Directive, contentChildren } from '@angular/core';
5
5
  import * as i2 from '@angular/forms';
@@ -7,9 +7,9 @@ import { ReactiveFormsModule, NG_VALUE_ACCESSOR, FormsModule, FormControl, FormG
7
7
  import { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop';
8
8
  import * as i1$1 from '@jsverse/transloco';
9
9
  import { TranslocoService, TranslocoModule } from '@jsverse/transloco';
10
- import { LucideEyeOff, LucideEye, LucideSearch, LucideDynamicIcon, LucideX, LucideTriangleAlert, LucideInfo, LucideCircleX, LucideCheck, LucideLoader, LucideTrash2, LucideLoaderCircle, LucideMinimize2, LucideMaximize2, LucideChevronRight, LucideChevronLeft, LucideChevronDown, LucideFile, LucideUpload, LucideList, LucideWarehouse, LucideUserCog, LucideShieldCheck, LucideCalculator, LucidePercent, LucideDollarSign, LucideLineChart, LucideBot, LucideReceipt, LucideUsers, LucideTruck, LucideTag, LucideTags, LucideBox, LucidePackage, LucideLandmark, LucideBuilding, LucideUserPlus, LucideUserRoundX, LucideUserCheck, LucideUser, LucideLogOut, LucideSettings, LucideClock, LucideStar, LucideVideo, LucidePlay, LucideShare2, LucideCopy, LucideCircleCheck, LucideArchive, LucideFileText, LucidePresentation, LucideFileSpreadsheet, LucideRefreshCw, LucideCirclePlus, LucidePlus, LucideDownload, LucideToggleLeft, LucideToggleRight, LucideUserPen, LucideKey, LucidePencil, LucideFunnel, LucideInbox, LucideArrowUpDown, LucideArrowDown, LucideArrowUp, LucideChevronsRight, LucideChevronsLeft, LucideBarChart3, LucideMenu, LucideBuilding2, LucideLayoutDashboard, LucideHome, LucideHouse, LucideBanknote, LucidePanelRight, LucideGlobe, LucideCalendarCheck, LucideCalendar, LucideSliders, LucideWallet, LucideBriefcase, LucideIdCard, LucideCreditCard, LucideChartLine, LucideChartPie, LucidePuzzle, LucideLayers, LucideShield, LucideUserRound, LucideMapPin, LucideUtensils, LucideShip, LucideBus, LucideMountain, LucideCar, LucideTicket, LucideHotel, LucideCalendarDays, LucideCircleHelp, LucideCircleAlert, LucideStickyNote, LucideCoins, LucideMap, LucideClipboardCheck, LucideScrollText, LucideArrowLeft } from '@lucide/angular';
11
- import { Router } from '@angular/router';
12
- import { Subject, debounceTime } from 'rxjs';
10
+ import { LucideEyeOff, LucideEye, LucideSearch, LucideDynamicIcon, LucideX, LucideTriangleAlert, LucideInfo, LucideCircleX, LucideCheck, LucideLoader, LucideTrash2, LucideLoaderCircle, LucideMinimize2, LucideMaximize2, LucideChevronRight, LucideChevronLeft, LucideChevronDown, LucideFile, LucideUpload, LucideArrowDown, LucideArrowUp, LucideArrowLeft, LucideArrowRight, LucideFilter, LucideCalculator, LucideMoreHorizontal, LucideMenu, LucideCircleCheck, LucideGlobe, LucideWallet, LucidePercent, LucideDollarSign, LucideCreditCard, LucideTag, LucideTruck, LucidePackage, LucideShoppingCart, LucideFolderOpen, LucideFileText, LucideHome, LucideMapPin, LucidePhone, LucideMail, LucideLock, LucideChevronUp, LucideSparkles, LucideCopy, LucideRocket, LucideRefreshCw, LucideDownload, LucideAlertCircle, LucideAlertTriangle, LucideSettings, LucideClock, LucideCalendarCheck, LucideCalendar, LucideStar, LucideUsers, LucideUser, LucideSave, LucidePencil, LucidePlus, LucideCircleHelp, LucideList, LucideWarehouse, LucideUserCog, LucideShieldCheck, LucideLineChart, LucideBot, LucideReceipt, LucideTags, LucideBox, LucideLandmark, LucideBuilding, LucideUserPlus, LucideUserRoundX, LucideUserCheck, LucideLogOut, LucideVideo, LucidePlay, LucideShare2, LucideArchive, LucidePresentation, LucideFileSpreadsheet, LucideCirclePlus, LucideToggleLeft, LucideToggleRight, LucideUserPen, LucideKey, LucideFunnel, LucideInbox, LucideArrowUpDown, LucideChevronsRight, LucideChevronsLeft, LucideBarChart3, LucideBuilding2, LucideLayoutDashboard, LucideHouse, LucideBanknote, LucidePanelRight, LucideSliders, LucideBriefcase, LucideIdCard, LucideChartLine, LucideChartPie, LucidePuzzle, LucideLayers, LucideShield, LucideUserRound, LucideUtensils, LucideShip, LucideBus, LucideMountain, LucideCar, LucideTicket, LucideHotel, LucideCalendarDays, LucideCircleAlert, LucideStickyNote, LucideCoins, LucideMap, LucideClipboardCheck, LucideScrollText } from '@lucide/angular';
11
+ import { Router, RouterLink } from '@angular/router';
12
+ import { Subject, debounceTime, BehaviorSubject } from 'rxjs';
13
13
  import { Chart, registerables } from 'chart.js';
14
14
 
15
15
  const icons$9 = {
@@ -38,7 +38,7 @@ class TextFieldComponent {
38
38
  this.textAreaHeight = input('h-28', ...(ngDevMode ? [{ debugName: "textAreaHeight" }] : /* istanbul ignore next */ []));
39
39
  this.searchMode = input(false, ...(ngDevMode ? [{ debugName: "searchMode" }] : /* istanbul ignore next */ []));
40
40
  this.displayValue = input('', ...(ngDevMode ? [{ debugName: "displayValue" }] : /* istanbul ignore next */ []));
41
- this.autocomplete = input('new-password', ...(ngDevMode ? [{ debugName: "autocomplete" }] : /* istanbul ignore next */ []));
41
+ this.autocomplete = input('', ...(ngDevMode ? [{ debugName: "autocomplete" }] : /* istanbul ignore next */ []));
42
42
  this.searchButtonClick = output();
43
43
  this.getIcon = (name) => icons$9[name] ?? LucideEye;
44
44
  this.inputElement = viewChild('input', ...(ngDevMode ? [{ debugName: "inputElement" }] : /* istanbul ignore next */ []));
@@ -2149,6 +2149,276 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImpo
2149
2149
  args: [{ selector: 'shk-file-upload', standalone: true, imports: [CommonModule, LucideDynamicIcon], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"flex flex-col gap-2\">\n @if (label()) {\n <label class=\"text-sm font-medium _shk-text-surface-700 dark:[color:var(--shk-surface-300)]\">\n {{ label() }}\n </label>\n }\n\n @if (canAddMore()) {\n <div\n class=\"relative border-2 border-dashed _shk-border dark:[border-color:var(--shk-surface-700)] rounded-lg p-4 transition-colors hover:[border-color:var(--shk-primary-400)] dark:hover:[border-color:var(--shk-primary-500)]\"\n [class.border-red-400]=\"errorMessage()\"\n (dragover)=\"onDragOver($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\"\n >\n <input\n #fileInput\n type=\"file\"\n [accept]=\"accept()\"\n [multiple]=\"multiple()\"\n (change)=\"onFileChange($event)\"\n class=\"hidden\"\n />\n\n <div\n class=\"flex flex-col items-center gap-2 cursor-pointer\"\n (click)=\"fileInput.click()\"\n [class.drag-over]=\"isDragOver()\"\n >\n <svg [lucideIcon]=\"getIcon('upload')\" class=\"w-8 h-8 _shk-icon-subtle dark:[color:var(--shk-surface-600)]\"></svg>\n <span class=\"text-sm _shk-text-surface-700 dark:[color:var(--shk-surface-300)]\">\n {{ fileUploadText() }}\n </span>\n @if (fileRecommendation()) {\n <span class=\"text-xs _shk-text-surface-400 dark:[color:var(--shk-surface-500)]\">\n {{ fileRecommendation() }}\n </span>\n }\n </div>\n </div>\n }\n\n @if (maxReached()) {\n <div class=\"text-xs _shk-text-surface-400 dark:[color:var(--shk-surface-500)] text-center py-2\">\n M\u00E1ximo {{ maxFiles() }} archivos alcanzado\n </div>\n }\n\n @if (selectedFiles().length > 0) {\n <div class=\"flex flex-wrap gap-3\">\n @for (file of selectedFiles(); track file.name + file.size + file.lastModified) {\n <div class=\"relative group\">\n @if (isImageFile(file)) {\n <img\n [src]=\"previewUrl(file)\"\n [alt]=\"file.name\"\n class=\"w-20 h-20 object-cover rounded-lg _shk-border dark:[border-color:var(--shk-surface-700)]\"\n />\n } @else {\n <div class=\"w-20 h-20 rounded-lg _shk-border dark:[border-color:var(--shk-surface-700)] flex flex-col items-center justify-center _shk-bg-surface-50 dark:[background-color:var(--shk-surface-800)] gap-1\">\n <svg [lucideIcon]=\"getIcon('file')\" class=\"w-6 h-6 _shk-text-surface-400\"></svg>\n <span class=\"text-[10px] _shk-text-surface-400 truncate max-w-[70px] px-1 text-center\">{{ file.name }}</span>\n </div>\n }\n <button\n type=\"button\"\n (click)=\"removeFile(file)\"\n class=\"absolute -top-1.5 -right-1.5 w-5 h-5 bg-red-500 text-white rounded-full text-xs flex items-center justify-center opacity-0 group-hover:opacity-100 transition shadow\"\n >\n <svg [lucideIcon]=\"getIcon('x')\" class=\"w-3 h-3\"></svg>\n </button>\n </div>\n }\n </div>\n @if (!multiple()) {\n <button\n type=\"button\"\n (click)=\"changeFiles()\"\n class=\"text-xs _shk-text-primary hover:[color:var(--shk-primary-700)] dark:[color:var(--shk-primary-400)] dark:hover:[color:var(--shk-primary-300)] underline cursor-pointer bg-transparent border-0\"\n >\n {{ changeFilesText() }}\n </button>\n }\n }\n\n @if (errorMessage()) {\n <p class=\"text-xs _shk-text-red-500\">{{ errorMessage() }}</p>\n }\n</div>" }]
2150
2150
  }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], accept: [{ type: i0.Input, args: [{ isSignal: true, alias: "accept", required: false }] }], maxFileSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxFileSize", required: false }] }], maxFiles: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxFiles", required: false }] }], fileUploadText: [{ type: i0.Input, args: [{ isSignal: true, alias: "fileUploadText", required: false }] }], fileRecommendation: [{ type: i0.Input, args: [{ isSignal: true, alias: "fileRecommendation", required: false }] }], changeFilesText: [{ type: i0.Input, args: [{ isSignal: true, alias: "changeFilesText", required: false }] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], fileSelected: [{ type: i0.Output, args: ["fileSelected"] }], fileRemoved: [{ type: i0.Output, args: ["fileRemoved"] }], fileError: [{ type: i0.Output, args: ["fileError"] }], fileWarning: [{ type: i0.Output, args: ["fileWarning"] }] } });
2151
2151
 
2152
+ const ICONS$2 = {
2153
+ plus: LucidePlus,
2154
+ 'circle-plus': LucidePlus,
2155
+ pencil: LucidePencil,
2156
+ edit: LucidePencil,
2157
+ 'trash-2': LucideTrash2,
2158
+ delete: LucideTrash2,
2159
+ save: LucideSave,
2160
+ x: LucideX,
2161
+ close: LucideX,
2162
+ check: LucideCheck,
2163
+ search: LucideSearch,
2164
+ user: LucideUser,
2165
+ users: LucideUsers,
2166
+ star: LucideStar,
2167
+ calendar: LucideCalendar,
2168
+ 'calendar-check': LucideCalendarCheck,
2169
+ clock: LucideClock,
2170
+ settings: LucideSettings,
2171
+ 'alert-triangle': LucideAlertTriangle,
2172
+ warning: LucideAlertTriangle,
2173
+ 'alert-circle': LucideAlertCircle,
2174
+ info: LucideInfo,
2175
+ download: LucideDownload,
2176
+ upload: LucideUpload,
2177
+ refresh: LucideRefreshCw,
2178
+ rocket: LucideRocket,
2179
+ copy: LucideCopy,
2180
+ sparkles: LucideSparkles,
2181
+ 'chevron-down': LucideChevronDown,
2182
+ 'chevron-up': LucideChevronUp,
2183
+ 'chevron-left': LucideChevronLeft,
2184
+ 'chevron-right': LucideChevronRight,
2185
+ eye: LucideEye,
2186
+ 'eye-off': LucideEyeOff,
2187
+ lock: LucideLock,
2188
+ mail: LucideMail,
2189
+ phone: LucidePhone,
2190
+ 'map-pin': LucideMapPin,
2191
+ home: LucideHome,
2192
+ 'file-text': LucideFileText,
2193
+ 'folder-open': LucideFolderOpen,
2194
+ 'shopping-cart': LucideShoppingCart,
2195
+ package: LucidePackage,
2196
+ truck: LucideTruck,
2197
+ tag: LucideTag,
2198
+ 'credit-card': LucideCreditCard,
2199
+ 'dollar-sign': LucideDollarSign,
2200
+ percent: LucidePercent,
2201
+ wallet: LucideWallet,
2202
+ globe: LucideGlobe,
2203
+ 'circle-check': LucideCircleCheck,
2204
+ 'circle-x': LucideCircleX,
2205
+ menu: LucideMenu,
2206
+ 'more-horizontal': LucideMoreHorizontal,
2207
+ calculator: LucideCalculator,
2208
+ filter: LucideFilter,
2209
+ 'arrow-right': LucideArrowRight,
2210
+ 'arrow-left': LucideArrowLeft,
2211
+ 'arrow-up': LucideArrowUp,
2212
+ 'arrow-down': LucideArrowDown,
2213
+ };
2214
+ /** Resolves a lucide icon name to its component. Falls back to a help circle. */
2215
+ function getIcon$6(name) {
2216
+ if (!name)
2217
+ return null;
2218
+ return ICONS$2[name] ?? LucideCircleHelp;
2219
+ }
2220
+
2221
+ const SEVERITY_CLASS = {
2222
+ secondary: '_shk-tag-secondary',
2223
+ success: '_shk-tag-success',
2224
+ info: '_shk-tag-info',
2225
+ warning: '_shk-tag-warn',
2226
+ danger: '_shk-tag-danger',
2227
+ contrast: '_shk-tag-contrast',
2228
+ };
2229
+ /** Small label used to highlight a value, status or category. */
2230
+ class TagComponent {
2231
+ constructor() {
2232
+ /** Text shown inside the tag. */
2233
+ this.value = input('', ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
2234
+ /** Color variant. Defaults to 'secondary'. */
2235
+ this.severity = input('secondary', ...(ngDevMode ? [{ debugName: "severity" }] : /* istanbul ignore next */ []));
2236
+ /** Renders the tag with fully rounded corners (pill). */
2237
+ this.rounded = input(false, ...(ngDevMode ? [{ debugName: "rounded" }] : /* istanbul ignore next */ []));
2238
+ /** Optional leading lucide icon name. */
2239
+ this.icon = input('', ...(ngDevMode ? [{ debugName: "icon" }] : /* istanbul ignore next */ []));
2240
+ /** Inline styles applied to the tag. */
2241
+ this.style = input({}, ...(ngDevMode ? [{ debugName: "style" }] : /* istanbul ignore next */ []));
2242
+ /** Extra CSS classes. */
2243
+ this.styleClass = input('', ...(ngDevMode ? [{ debugName: "styleClass" }] : /* istanbul ignore next */ []));
2244
+ this.getIcon = getIcon$6;
2245
+ this.tagClass = computed(() => {
2246
+ const base = 'inline-flex items-center gap-1 px-2 py-0.5 text-xs font-medium';
2247
+ const severity = SEVERITY_CLASS[this.severity()] ?? SEVERITY_CLASS.secondary;
2248
+ const radius = this.rounded() ? 'rounded-full' : 'rounded-md';
2249
+ return `${base} ${radius} ${severity} ${this.styleClass()}`.trim();
2250
+ }, ...(ngDevMode ? [{ debugName: "tagClass" }] : /* istanbul ignore next */ []));
2251
+ }
2252
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: TagComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2253
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.18", type: TagComponent, isStandalone: true, selector: "shk-tag", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, severity: { classPropertyName: "severity", publicName: "severity", isSignal: true, isRequired: false, transformFunction: null }, rounded: { classPropertyName: "rounded", publicName: "rounded", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, style: { classPropertyName: "style", publicName: "style", isSignal: true, isRequired: false, transformFunction: null }, styleClass: { classPropertyName: "styleClass", publicName: "styleClass", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
2254
+ <span [class]="tagClass()" [style]="style()">
2255
+ @if (icon()) {
2256
+ <svg [lucideIcon]="getIcon(icon())" class="shk-tag-icon"></svg>
2257
+ }
2258
+ @if (value() !== undefined && value() !== null) {
2259
+ <span>{{ value() }}</span>
2260
+ }
2261
+ <ng-content></ng-content>
2262
+ </span>
2263
+ `, isInline: true, styles: [".shk-tag-icon{width:.875rem;height:.875rem;flex-shrink:0}\n"], dependencies: [{ kind: "component", type: LucideDynamicIcon, selector: "svg[lucideIcon]", inputs: ["lucideIcon"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2264
+ }
2265
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: TagComponent, decorators: [{
2266
+ type: Component,
2267
+ args: [{ selector: 'shk-tag', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [LucideDynamicIcon], template: `
2268
+ <span [class]="tagClass()" [style]="style()">
2269
+ @if (icon()) {
2270
+ <svg [lucideIcon]="getIcon(icon())" class="shk-tag-icon"></svg>
2271
+ }
2272
+ @if (value() !== undefined && value() !== null) {
2273
+ <span>{{ value() }}</span>
2274
+ }
2275
+ <ng-content></ng-content>
2276
+ </span>
2277
+ `, styles: [".shk-tag-icon{width:.875rem;height:.875rem;flex-shrink:0}\n"] }]
2278
+ }], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], severity: [{ type: i0.Input, args: [{ isSignal: true, alias: "severity", required: false }] }], rounded: [{ type: i0.Input, args: [{ isSignal: true, alias: "rounded", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], style: [{ type: i0.Input, args: [{ isSignal: true, alias: "style", required: false }] }], styleClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "styleClass", required: false }] }] } });
2279
+
2280
+ const TOOLTIP_Z_INDEX = '2147483647';
2281
+ /**
2282
+ * Tooltip que se renderiza en `document.body` con `position: fixed`, de modo
2283
+ * que siempre flota por encima de cualquier contenedor (tablas con
2284
+ * `overflow: hidden`, modales, scroll wrappers, etc.) y nunca queda
2285
+ * recortado ni pasa por debajo de su elemento ancla.
2286
+ */
2287
+ class TooltipComponent {
2288
+ constructor() {
2289
+ this.text = input('', ...(ngDevMode ? [{ debugName: "text" }] : /* istanbul ignore next */ []));
2290
+ this.position = input('bottom', ...(ngDevMode ? [{ debugName: "position" }] : /* istanbul ignore next */ []));
2291
+ this.mode = input('hover', ...(ngDevMode ? [{ debugName: "mode" }] : /* istanbul ignore next */ []));
2292
+ this.offset = input(8, ...(ngDevMode ? [{ debugName: "offset" }] : /* istanbul ignore next */ []));
2293
+ this.host = inject((ElementRef));
2294
+ this.document = inject(DOCUMENT);
2295
+ this.tooltipEl = null;
2296
+ this.cleanup = null;
2297
+ }
2298
+ onMouseEnter() {
2299
+ if (!this.text())
2300
+ return;
2301
+ const trigger = this.host.nativeElement.querySelector('.shk-tooltip-trigger');
2302
+ if (!trigger)
2303
+ return;
2304
+ this.show(trigger);
2305
+ }
2306
+ onMouseLeave() {
2307
+ this.hide();
2308
+ }
2309
+ show(trigger) {
2310
+ this.removeTooltip();
2311
+ const el = this.document.createElement('span');
2312
+ el.className = 'shk-tooltip-dynamic';
2313
+ el.textContent = this.text();
2314
+ Object.assign(el.style, {
2315
+ position: 'fixed',
2316
+ zIndex: TOOLTIP_Z_INDEX,
2317
+ backgroundColor: 'var(--shk-tooltip-bg, #27272a)',
2318
+ color: 'var(--shk-tooltip-text, #fff)',
2319
+ padding: '4px 8px',
2320
+ borderRadius: '6px',
2321
+ fontSize: '12px',
2322
+ lineHeight: '1.4',
2323
+ whiteSpace: 'normal',
2324
+ maxWidth: 'min(80vw, 22rem)',
2325
+ boxShadow: '0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)',
2326
+ pointerEvents: 'none',
2327
+ opacity: '0',
2328
+ transition: 'opacity 0.15s ease',
2329
+ });
2330
+ this.document.body.appendChild(el);
2331
+ this.tooltipEl = el;
2332
+ const place = () => {
2333
+ const rect = trigger.getBoundingClientRect();
2334
+ const w = el.offsetWidth;
2335
+ const h = el.offsetHeight;
2336
+ const { left, top } = this.computePosition(rect, w, h);
2337
+ el.style.left = `${left}px`;
2338
+ el.style.top = `${top}px`;
2339
+ };
2340
+ place();
2341
+ requestAnimationFrame(() => {
2342
+ if (this.tooltipEl === el) {
2343
+ el.style.opacity = '1';
2344
+ }
2345
+ });
2346
+ const onScrollOrResize = () => place();
2347
+ window.addEventListener('scroll', onScrollOrResize, true);
2348
+ window.addEventListener('resize', onScrollOrResize);
2349
+ this.cleanup = () => {
2350
+ window.removeEventListener('scroll', onScrollOrResize, true);
2351
+ window.removeEventListener('resize', onScrollOrResize);
2352
+ };
2353
+ }
2354
+ computePosition(rect, width, height) {
2355
+ const off = this.offset();
2356
+ const viewportW = this.document.defaultView?.innerWidth ?? 0;
2357
+ const viewportH = this.document.defaultView?.innerHeight ?? 0;
2358
+ let x;
2359
+ let y;
2360
+ if (this.position() === 'right') {
2361
+ x = rect.right + off;
2362
+ y = rect.top + rect.height / 2 - height / 2;
2363
+ }
2364
+ else if (this.position() === 'bottom') {
2365
+ x = rect.left + rect.width / 2 - width / 2;
2366
+ y = rect.bottom + off;
2367
+ }
2368
+ else {
2369
+ x = rect.left + rect.width / 2 - width / 2;
2370
+ y = rect.top - off - height;
2371
+ }
2372
+ const margin = 4;
2373
+ const left = Math.max(margin, Math.min(x, Math.max(margin, viewportW - width - margin)));
2374
+ const top = Math.max(margin, Math.min(y, Math.max(margin, viewportH - height - margin)));
2375
+ return { left, top };
2376
+ }
2377
+ hide() {
2378
+ if (!this.tooltipEl)
2379
+ return;
2380
+ const el = this.tooltipEl;
2381
+ el.style.opacity = '0';
2382
+ this.tooltipEl = null;
2383
+ setTimeout(() => {
2384
+ el.remove();
2385
+ }, 150);
2386
+ }
2387
+ removeTooltip() {
2388
+ this.cleanup?.();
2389
+ this.cleanup = null;
2390
+ this.tooltipEl?.remove();
2391
+ this.tooltipEl = null;
2392
+ }
2393
+ ngOnDestroy() {
2394
+ this.removeTooltip();
2395
+ }
2396
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: TooltipComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2397
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.18", type: TooltipComponent, isStandalone: true, selector: "shk-tooltip", inputs: { text: { classPropertyName: "text", publicName: "text", isSignal: true, isRequired: false, transformFunction: null }, position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, offset: { classPropertyName: "offset", publicName: "offset", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "mouseenter": "onMouseEnter()", "mouseleave": "onMouseLeave()" } }, ngImport: i0, template: `
2398
+ <div class="shk-tooltip-trigger inline-flex">
2399
+ <ng-content></ng-content>
2400
+ </div>
2401
+ `, isInline: true }); }
2402
+ }
2403
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: TooltipComponent, decorators: [{
2404
+ type: Component,
2405
+ args: [{
2406
+ selector: 'shk-tooltip',
2407
+ standalone: true,
2408
+ template: `
2409
+ <div class="shk-tooltip-trigger inline-flex">
2410
+ <ng-content></ng-content>
2411
+ </div>
2412
+ `,
2413
+ }]
2414
+ }], propDecorators: { text: [{ type: i0.Input, args: [{ isSignal: true, alias: "text", required: false }] }], position: [{ type: i0.Input, args: [{ isSignal: true, alias: "position", required: false }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], offset: [{ type: i0.Input, args: [{ isSignal: true, alias: "offset", required: false }] }], onMouseEnter: [{
2415
+ type: HostListener,
2416
+ args: ['mouseenter']
2417
+ }], onMouseLeave: [{
2418
+ type: HostListener,
2419
+ args: ['mouseleave']
2420
+ }] } });
2421
+
2152
2422
  const icons$3 = {
2153
2423
  'chevrons-left': LucideChevronsLeft,
2154
2424
  'chevrons-right': LucideChevronsRight,
@@ -2731,19 +3001,26 @@ class TableComponent {
2731
3001
  const str = String(value);
2732
3002
  return str.length > limit ? str.slice(0, limit) + '\u2026' : str;
2733
3003
  }
2734
- getTagClass(severity) {
2735
- switch (severity) {
2736
- case 'success':
2737
- return '_shk-bg-green-soft _shk-text-green-600';
2738
- case 'danger':
2739
- return '_shk-bg-red-soft _shk-text-red-600';
2740
- case 'warn':
2741
- return '_shk-bg-yellow-soft _shk-text-yellow-600';
2742
- case 'info':
2743
- return '_shk-bg-blue-soft _shk-text-blue-600';
2744
- default:
2745
- return '_shk-bg-surface-100 _shk-text-surface-700 dark:[background-color:var(--shk-surface-700)] dark:[color:var(--shk-surface-300)]';
2746
- }
3004
+ getTagValue(col, rowData) {
3005
+ return col.tagValue ? col.tagValue(rowData) : String(rowData[col.field] ?? '');
3006
+ }
3007
+ getTagSeverity(col, rowData) {
3008
+ const raw = col.tagSeverity ? col.tagSeverity(rowData) : 'secondary';
3009
+ const allowed = ['secondary', 'success', 'info', 'warning', 'danger', 'contrast'];
3010
+ return allowed.includes(raw) ? raw : 'secondary';
3011
+ }
3012
+ getTagRounded(col, rowData) {
3013
+ if (typeof col.tagRounded === 'function')
3014
+ return col.tagRounded(rowData);
3015
+ return col.tagRounded ?? false;
3016
+ }
3017
+ getTagIcon(col, rowData) {
3018
+ if (typeof col.tagIcon === 'function')
3019
+ return col.tagIcon(rowData);
3020
+ return col.tagIcon ?? '';
3021
+ }
3022
+ getTagStyle(col, rowData) {
3023
+ return col.tagStyle ? col.tagStyle(rowData) || {} : {};
2747
3024
  }
2748
3025
  /**
2749
3026
  * FIX #7 (medio): trackBy real por identidad de fila en vez de `$index`.
@@ -2771,122 +3048,20 @@ class TableComponent {
2771
3048
  return this.sortOrder() === 1 ? 'ascending' : 'descending';
2772
3049
  }
2773
3050
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: TableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2774
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.18", type: TableComponent, isStandalone: true, selector: "shk-table", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, rowsPerPage: { classPropertyName: "rowsPerPage", publicName: "rowsPerPage", isSignal: true, isRequired: false, transformFunction: null }, rowsPerPageOptions: { classPropertyName: "rowsPerPageOptions", publicName: "rowsPerPageOptions", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, showActionRow: { classPropertyName: "showActionRow", publicName: "showActionRow", isSignal: true, isRequired: false, transformFunction: null }, customTemplates: { classPropertyName: "customTemplates", publicName: "customTemplates", isSignal: true, isRequired: false, transformFunction: null }, headerActions: { classPropertyName: "headerActions", publicName: "headerActions", isSignal: true, isRequired: false, transformFunction: null }, rowActions: { classPropertyName: "rowActions", publicName: "rowActions", isSignal: true, isRequired: false, transformFunction: null }, hasShadow: { classPropertyName: "hasShadow", publicName: "hasShadow", isSignal: true, isRequired: false, transformFunction: null }, defaultSortField: { classPropertyName: "defaultSortField", publicName: "defaultSortField", isSignal: true, isRequired: false, transformFunction: null }, defaultSortOrder: { classPropertyName: "defaultSortOrder", publicName: "defaultSortOrder", isSignal: true, isRequired: false, transformFunction: null }, showSearch: { classPropertyName: "showSearch", publicName: "showSearch", isSignal: true, isRequired: false, transformFunction: null }, searchPlaceholder: { classPropertyName: "searchPlaceholder", publicName: "searchPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, emptyMessage: { classPropertyName: "emptyMessage", publicName: "emptyMessage", isSignal: true, isRequired: false, transformFunction: null }, serverSide: { classPropertyName: "serverSide", publicName: "serverSide", isSignal: true, isRequired: false, transformFunction: null }, totalRecords: { classPropertyName: "totalRecords", publicName: "totalRecords", isSignal: true, isRequired: false, transformFunction: null }, filters: { classPropertyName: "filters", publicName: "filters", isSignal: true, isRequired: false, transformFunction: null }, activeFilter: { classPropertyName: "activeFilter", publicName: "activeFilter", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { refresh: "refresh", pageChange: "pageChange", filterChange: "filterChange", searchChange: "searchChange", filterClick: "filterClick" }, ngImport: i0, template: "<div class=\"space-y-2 min-[1440px]:space-y-3 mt-2 min-[1440px]:mt-3 animate-fade-in pb-8\">\n @if (showSearch() || filters().length > 0 || headerActions().length > 0) {\n <div class=\"card p-2 min-[1440px]:p-2.5 flex flex-row items-center justify-between gap-2\">\n <div class=\"flex items-center gap-2 min-[1440px]:gap-3 flex-1 min-w-0\">\n @if (showSearch()) {\n <div class=\"relative w-full md:w-52 min-[1440px]:w-56\">\n <label [for]=\"'shk-table-search'\" class=\"sr-only\">{{\n searchPlaceholder() || ('common.search_placeholder' | transloco)\n }}</label>\n <div class=\"absolute inset-y-0 left-0 pl-2.5 flex items-center pointer-events-none\">\n <svg\n [lucideIcon]=\"getIcon('search')\"\n class=\"w-3.5 h-3.5 _shk-text-surface-400 dark:[color:var(--shk-surface-500)]\"\n ></svg>\n </div>\n <input\n id=\"shk-table-search\"\n type=\"text\"\n [value]=\"searchQuery()\"\n (input)=\"onSearchInput($any($event.target).value)\"\n [placeholder]=\"searchPlaceholder() || ('common.search_placeholder' | transloco)\"\n class=\"block w-full pl-8 pr-3 py-1.5 text-xs border _shk-border dark:[border-color:var(--shk-surface-200)] rounded-md leading-5 _shk-bg-surface-50 dark:[background-color:var(--shk-surface-100)] placeholder:[color:var(--shk-surface-400)] dark:placeholder:[color:var(--shk-surface-500)] _shk-text-surface-700 dark:[color:var(--shk-surface-700)] focus:outline-none focus:bg-white dark:focus:[background-color:var(--shk-surface-800)] focus:[border-color:var(--shk-primary-500)] focus:ring-1 focus:[--tw-ring-color:var(--shk-primary-500)]\"\n />\n </div>\n }\n @if (filters().length > 0) {\n <div\n class=\"flex _shk-bg-surface-100 dark:[background-color:var(--shk-surface-100)] p-0.5 rounded-md ml-auto\"\n role=\"group\"\n >\n @for (f of filters(); track f.value) {\n <button\n type=\"button\"\n (click)=\"onFilterClick(f.value)\"\n [attr.aria-pressed]=\"f.value === activeFilter()\"\n class=\"px-2.5 min-[1440px]:px-3 py-1 text-xs font-medium rounded _shk-text-surface-700 dark:[color:var(--shk-surface-700)] transition-all\"\n [class]=\"\n f.value === activeFilter()\n ? 'bg-white shadow-sm dark:[background-color:var(--shk-surface-700)]'\n : ''\n \"\n >\n {{ f.label }}\n </button>\n }\n </div>\n }\n </div>\n @if (headerActions().length > 0) {\n <div class=\"flex items-center gap-1.5 shrink-0\">\n @for (action of headerActions(); track $index) {\n @if (isHeaderActionVisible(action)) {\n <button\n type=\"button\"\n [class]=\"\n 'inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-md _shk-bg-primary text-white hover:[background-color:var(--shk-primary-700)] transition-colors disabled:opacity-50 disabled:cursor-not-allowed ' +\n (action.class || '')\n \"\n [disabled]=\"isHeaderActionDisabled(action)\"\n [attr.aria-label]=\"action.label\"\n (click)=\"action.onClick()\"\n >\n @if (action.icon) {\n <svg [lucideIcon]=\"getIcon(action.icon)\" class=\"w-3.5 h-3.5\"></svg>\n }\n {{ action.label }}\n </button>\n }\n }\n </div>\n }\n </div>\n }\n <div class=\"card p-0 overflow-hidden\" [class.shadow-lg]=\"hasShadow()\">\n <div class=\"table-scroll-wrapper _shk-scrollbar\">\n <table>\n <thead>\n <tr>\n @for (col of columnsWithActions(); track col.field) {\n <th\n scope=\"col\"\n (click)=\"col.field !== 'actions' && col.sortable && toggleSort(col.field)\"\n (keydown)=\"onHeaderKeydown($event, col)\"\n [tabindex]=\"col.field !== 'actions' && col.sortable ? 0 : -1\"\n [attr.role]=\"col.field !== 'actions' && col.sortable ? 'button' : null\"\n [attr.aria-sort]=\"getAriaSort(col)\"\n [attr.data-shk-actions]=\"col.field === 'actions' ? '' : null\"\n [style.cursor]=\"col.field !== 'actions' && col.sortable ? 'pointer' : 'default'\"\n [style.width]=\"col.field === 'actions' ? col.width : null\"\n [style.min-width]=\"col.field === 'actions' ? col.width : null\"\n [style.--shk-col-width]=\"col.field !== 'actions' ? getColumnWidth(col) : null\"\n class=\"px-2 min-[1440px]:px-3 py-2 min-[1440px]:py-2.5 text-left text-[10px] min-[1440px]:text-xs font-bold _shk-text-surface-500 dark:[color:var(--shk-surface-400)] uppercase tracking-wider _shk-bg-surface-50 dark:[background-color:var(--shk-surface-100)] border-b _shk-border dark:[border-color:var(--shk-surface-200)] focus:outline-none focus-visible:ring-1 focus-visible:[--tw-ring-color:var(--shk-primary-500)]\"\n >\n @if (col.field !== 'actions') {\n <div class=\"flex items-center justify-between gap-2\">\n <span>{{ col.header }}</span>\n <div class=\"flex items-center gap-1\">\n @if (col.sortable) {\n <div class=\"relative group\">\n @if (sortField() === col.field) {\n <svg\n [lucideIcon]=\"getIcon(sortOrder() === 1 ? 'arrow-up' : 'arrow-down')\"\n class=\"w-3.5 h-3.5 _shk-text-primary\"\n ></svg>\n } @else {\n <svg\n [lucideIcon]=\"getIcon('arrow-up-down')\"\n class=\"w-3.5 h-3.5 _shk-text-surface-400 dark:[color:var(--shk-surface-500)]\"\n ></svg>\n }\n <span\n class=\"absolute top-full left-1/2 -translate-x-1/2 mt-1 px-2 py-1 text-xs _shk-bg-tooltip _shk-text-tooltip rounded whitespace-nowrap opacity-0 group-hover:opacity-100 transition pointer-events-none z-[9999] shadow-lg\"\n >\n {{ 'table.tooltips.sort' | transloco }}\n </span>\n </div>\n }\n @if (col.filter) {\n <div class=\"relative group\">\n <button\n type=\"button\"\n (click)=\"$event.stopPropagation(); toggleFilter(col.field)\"\n [attr.aria-label]=\"'table.tooltips.filter' | transloco\"\n [attr.aria-expanded]=\"showFilterInput()[col.field] || false\"\n class=\"_shk-text-surface-400 dark:[color:var(--shk-surface-500)] hover:[color:var(--shk-surface-600)] dark:hover:[color:var(--shk-surface-300)] transition-colors\"\n >\n <svg\n [lucideIcon]=\"getIcon('filter')\"\n class=\"w-3.5 h-3.5\"\n [class._shk-text-primary]=\"hasFilterValue(col.field)\"\n ></svg>\n <span\n class=\"absolute top-full left-1/2 -translate-x-1/2 mt-1 px-2 py-1 text-xs _shk-bg-tooltip _shk-text-tooltip rounded whitespace-nowrap opacity-0 group-hover:opacity-100 transition pointer-events-none z-[9999] shadow-lg\"\n >\n {{ 'table.tooltips.filter' | transloco }}\n </span>\n </button>\n </div>\n }\n </div>\n </div>\n @if (col.filter && showFilterInput()[col.field]) {\n <div\n class=\"mt-2\"\n [formGroup]=\"columnFiltersForm\"\n (click)=\"$event.stopPropagation()\"\n >\n <div class=\"relative\">\n @if (col.filterType !== 'select') {\n <label [for]=\"'shk-filter-' + col.field\" class=\"sr-only\">{{\n col.filterPlaceholder || ('common.search_placeholder' | transloco)\n }}</label>\n <input\n [id]=\"'shk-filter-' + col.field\"\n type=\"text\"\n [formControlName]=\"col.field\"\n class=\"w-full px-3 py-1.5 text-sm rounded-md border _shk-border dark:[border-color:var(--shk-surface-200)] bg-white dark:[background-color:var(--shk-surface-100)] _shk-text-surface-700 dark:[color:var(--shk-surface-700)] focus:[border-color:var(--shk-primary-500)] focus:ring-1 focus:[--tw-ring-color:var(--shk-primary-500)] placeholder:[color:var(--shk-surface-400)] dark:placeholder:[color:var(--shk-surface-500)]\"\n [placeholder]=\"\n col.filterPlaceholder || ('common.search_placeholder' | transloco)\n \"\n />\n }\n @if (col.filterType === 'select') {\n <label [for]=\"'shk-filter-' + col.field\" class=\"sr-only\">{{\n col.filterPlaceholder || ('common.search_placeholder' | transloco)\n }}</label>\n <select\n [id]=\"'shk-filter-' + col.field\"\n [formControlName]=\"col.field\"\n class=\"w-full px-2 py-1 text-xs rounded-md border _shk-border dark:[border-color:var(--shk-surface-200)] bg-white dark:[background-color:var(--shk-surface-100)] _shk-text-surface-700 dark:[color:var(--shk-surface-700)] focus:[border-color:var(--shk-primary-500)] focus:ring-1 focus:[--tw-ring-color:var(--shk-primary-500)] appearance-none\"\n >\n <option [ngValue]=\"null\">{{ 'table.filterAll' | transloco }}</option>\n @for (opt of col.filterOptions || []; track opt.value) {\n <option [ngValue]=\"opt.value\">{{ opt.label }}</option>\n }\n </select>\n }\n @if (hasFilterValue(col.field) && col.filterType !== 'select') {\n <button\n type=\"button\"\n class=\"absolute right-2 top-1/2 -translate-y-1/2 _shk-text-surface-400 dark:[color:var(--shk-surface-500)] hover:[color:var(--shk-surface-600)] dark:hover:[color:var(--shk-surface-300)]\"\n (click)=\"clearFilter(col.field)\"\n >\n <svg [lucideIcon]=\"getIcon('x')\" class=\"w-3.5 h-3.5\"></svg>\n </button>\n }\n </div>\n </div>\n }\n } @else {\n <div class=\"flex items-center justify-center\">\n <span>{{ col.header }}</span>\n </div>\n }\n </th>\n }\n </tr>\n </thead>\n @if (loading()) {\n <tbody class=\"border-b _shk-border dark:[border-color:var(--shk-surface-100)]\">\n <tr>\n <td [attr.colspan]=\"columnsWithActions().length\" class=\"text-center py-12\">\n <div\n class=\"flex items-center justify-center\"\n role=\"status\"\n [attr.aria-label]=\"'common.loading' | transloco\"\n >\n <svg\n [lucideIcon]=\"getIcon('loader')\"\n class=\"w-6 h-6 animate-spin _shk-text-primary\"\n ></svg>\n </div>\n </td>\n </tr>\n </tbody>\n } @else if (effectiveDisplayedData().length === 0) {\n <tbody class=\"border-b _shk-border dark:[border-color:var(--shk-surface-100)]\">\n <tr>\n <td [attr.colspan]=\"columnsWithActions().length\" class=\"text-center py-12\">\n <div class=\"flex flex-col items-center\">\n <div\n class=\"w-16 h-16 _shk-bg-surface-100 dark:[background-color:var(--shk-surface-100)] rounded-full flex items-center justify-center mb-4\"\n >\n <svg\n [lucideIcon]=\"getIcon('inbox')\"\n class=\"w-8 h-8 _shk-text-surface-400 dark:[color:var(--shk-surface-500)]\"\n ></svg>\n </div>\n <p\n class=\"text-lg font-semibold _shk-text-surface-600 dark:[color:var(--shk-surface-300)]\"\n >\n {{ emptyMessage() || ('common.no_records' | transloco) }}\n </p>\n </div>\n </td>\n </tr>\n </tbody>\n } @else {\n <tbody>\n @for (rowData of effectiveDisplayedData(); track trackByRow(rowData, $index)) {\n <tr\n class=\"hover:[background-color:color-mix(in srgb,var(--shk-primary-50)30%,transparent)] dark:hover:[background-color:color-mix(in srgb,var(--shk-primary-900)10%,transparent)] transition-colors duration-200 border-b _shk-border dark:[border-color:var(--shk-surface-100)]\"\n >\n @for (col of columnsWithActions(); track col.field) {\n <td\n [attr.data-shk-actions]=\"col.field === 'actions' ? '' : null\"\n class=\"px-2 min-[1440px]:px-3 py-2 min-[1440px]:py-2.5 text-xs font-medium _shk-text-surface-700 dark:[color:var(--shk-surface-700)] whitespace-nowrap\"\n [style.overflow]=\"col.field === 'actions' ? 'visible' : 'hidden'\"\n [style.text-overflow]=\"col.field === 'actions' ? 'clip' : 'ellipsis'\"\n [style.--shk-col-width]=\"col.field !== 'actions' ? getColumnWidth(col) : null\"\n >\n @if (col.field !== 'actions') {\n @if (col.template === 'tag') {\n <span\n class=\"inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium\"\n [class]=\"\n getTagClass(col.tagSeverity ? col.tagSeverity(rowData) : undefined)\n \"\n [style]=\"col.tagStyle ? col.tagStyle(rowData) : undefined\"\n >\n {{ col.tagValue ? col.tagValue(rowData) : rowData[col.field] }}\n </span>\n } @else if (col.format) {\n <span>{{ col.format(rowData) }}</span>\n } @else {\n <ng-container\n *ngTemplateOutlet=\"\n customTemplates()[col.field] || defaultTemplate;\n context: { $implicit: rowData, field: col.field }\n \"\n ></ng-container>\n }\n } @else {\n <div class=\"flex items-center justify-start gap-px\">\n @for (action of rowActions(); track $index) {\n @if (isRowActionVisible(action, rowData)) {\n <div class=\"relative group\">\n <button\n type=\"button\"\n (click)=\"action.onClick(rowData)\"\n [class]=\"\n 'table-action-btn hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)] rounded-md ' +\n getRowActionClass(action, rowData)\n \"\n [disabled]=\"isRowActionDisabled(action, rowData) || false\"\n [attr.aria-label]=\"getRowActionLabel(action, rowData)\"\n >\n <svg\n [lucideIcon]=\"getIcon(getRowActionIconName(action, rowData))\"\n class=\"w-4 h-4\"\n ></svg>\n </button>\n <span\n class=\"absolute bottom-full left-1/2 -translate-x-1/2 mb-1 px-2 py-1 text-xs _shk-bg-tooltip _shk-text-tooltip rounded whitespace-nowrap opacity-0 group-hover:opacity-100 transition pointer-events-none z-[9999] shadow-lg\"\n >\n {{ getRowActionLabel(action, rowData) }}\n </span>\n </div>\n }\n }\n </div>\n }\n </td>\n }\n </tr>\n }\n </tbody>\n }\n </table>\n <ng-template #defaultTemplate let-rowData let-field=\"field\">\n {{ truncate(rowData[field], 30) }}\n </ng-template>\n </div>\n <div\n class=\"flex items-center justify-between gap-4 px-2 min-[1440px]:px-3 py-2 border-t _shk-border dark:[border-color:var(--shk-surface-100)]\"\n >\n <div class=\"text-xs _shk-text-surface-600 dark:[color:var(--shk-surface-400)]\">\n {{ pageReport() }}\n </div>\n <div\n class=\"flex items-center gap-0.5\"\n role=\"navigation\"\n >\n <button\n type=\"button\"\n (click)=\"goToFirst()\"\n [disabled]=\"isFirstPage()\"\n class=\"p-1 _shk-text-surface-500 hover:[color:var(--shk-surface-700)] dark:[color:var(--shk-surface-400)] dark:hover:[color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed rounded transition-colors\"\n >\n <svg [lucideIcon]=\"getIcon('chevrons-left')\" class=\"w-4 h-4\"></svg>\n </button>\n <button\n type=\"button\"\n (click)=\"prev()\"\n [disabled]=\"isFirstPage()\"\n class=\"p-1 _shk-text-surface-500 hover:[color:var(--shk-surface-700)] dark:[color:var(--shk-surface-400)] dark:hover:[color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed rounded transition-colors\"\n >\n <svg [lucideIcon]=\"getIcon('chevron-left')\" class=\"w-4 h-4\"></svg>\n </button>\n <span\n class=\"px-2 text-xs font-medium _shk-text-surface-700 dark:[color:var(--shk-surface-700)]\"\n >{{ currentPage() }} / {{ totalPages() }}</span\n >\n <button\n type=\"button\"\n (click)=\"next()\"\n [disabled]=\"isLastPage()\"\n class=\"p-1 _shk-text-surface-500 hover:[color:var(--shk-surface-700)] dark:[color:var(--shk-surface-400)] dark:hover:[color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed rounded transition-colors\"\n >\n <svg [lucideIcon]=\"getIcon('chevron-right')\" class=\"w-4 h-4\"></svg>\n </button>\n <button\n type=\"button\"\n (click)=\"goToLast()\"\n [disabled]=\"isLastPage()\"\n class=\"p-1 _shk-text-surface-500 hover:[color:var(--shk-surface-700)] dark:[color:var(--shk-surface-400)] dark:hover:[color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed rounded transition-colors\"\n >\n <svg [lucideIcon]=\"getIcon('chevrons-right')\" class=\"w-4 h-4\"></svg>\n </button>\n </div>\n <div\n class=\"flex items-center gap-1.5 text-xs _shk-text-surface-600 dark:[color:var(--shk-surface-400)]\"\n >\n <label [for]=\"'shk-rows-per-page'\">{{ 'table.rows' | transloco }}</label>\n <select\n id=\"shk-rows-per-page\"\n [value]=\"rowsPerPageLocal()\"\n (change)=\"onRowsPerPageChange($any($event.target).value)\"\n class=\"border _shk-border dark:[border-color:var(--shk-surface-200)] rounded px-2 py-1 text-xs bg-white dark:[background-color:var(--shk-surface-100)] _shk-text-surface-700 dark:[color:var(--shk-surface-700)] focus:outline-none focus:[border-color:var(--shk-primary-500)]\"\n >\n @for (opt of rowsPerPageOptions(); track opt) {\n <option [value]=\"opt\">{{ opt }}</option>\n }\n </select>\n </div>\n </div>\n </div>\n</div>\n", styles: ["input:focus{outline:none}input[type=text]{font-weight:400!important}table th,table td{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}td button svg{width:1.125rem!important;height:1.125rem!important}td button:disabled{opacity:.4;cursor:not-allowed}.table-action-btn svg{width:1rem!important;height:1rem!important;min-width:1rem!important;min-height:1rem!important}.table-action-btn{padding:.5rem!important;min-width:2rem!important;min-height:2rem!important;display:inline-flex!important;align-items:center!important;justify-content:center!important}.table-scroll-wrapper{width:100%;overflow-x:auto;overflow-y:hidden;border-radius:inherit;-webkit-overflow-scrolling:touch;container-type:inline-size}.table-scroll-wrapper table{table-layout:fixed;width:100%}@container (max-width: 768px){.table-scroll-wrapper{scroll-snap-type:x mandatory}.table-scroll-wrapper table{table-layout:auto;width:100%}.table-scroll-wrapper th:not([data-shk-actions]),.table-scroll-wrapper td:not([data-shk-actions]){min-width:var(--shk-col-width, 140px);max-width:var(--shk-col-width, 140px)}.table-scroll-wrapper th,.table-scroll-wrapper td{scroll-snap-align:start}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: TranslocoModule }, { kind: "component", type: LucideDynamicIcon, selector: "svg[lucideIcon]", inputs: ["lucideIcon"] }, { kind: "pipe", type: i1$1.TranslocoPipe, name: "transloco" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
3051
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.18", type: TableComponent, isStandalone: true, selector: "shk-table", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, rowsPerPage: { classPropertyName: "rowsPerPage", publicName: "rowsPerPage", isSignal: true, isRequired: false, transformFunction: null }, rowsPerPageOptions: { classPropertyName: "rowsPerPageOptions", publicName: "rowsPerPageOptions", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, showActionRow: { classPropertyName: "showActionRow", publicName: "showActionRow", isSignal: true, isRequired: false, transformFunction: null }, customTemplates: { classPropertyName: "customTemplates", publicName: "customTemplates", isSignal: true, isRequired: false, transformFunction: null }, headerActions: { classPropertyName: "headerActions", publicName: "headerActions", isSignal: true, isRequired: false, transformFunction: null }, rowActions: { classPropertyName: "rowActions", publicName: "rowActions", isSignal: true, isRequired: false, transformFunction: null }, hasShadow: { classPropertyName: "hasShadow", publicName: "hasShadow", isSignal: true, isRequired: false, transformFunction: null }, defaultSortField: { classPropertyName: "defaultSortField", publicName: "defaultSortField", isSignal: true, isRequired: false, transformFunction: null }, defaultSortOrder: { classPropertyName: "defaultSortOrder", publicName: "defaultSortOrder", isSignal: true, isRequired: false, transformFunction: null }, showSearch: { classPropertyName: "showSearch", publicName: "showSearch", isSignal: true, isRequired: false, transformFunction: null }, searchPlaceholder: { classPropertyName: "searchPlaceholder", publicName: "searchPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, emptyMessage: { classPropertyName: "emptyMessage", publicName: "emptyMessage", isSignal: true, isRequired: false, transformFunction: null }, serverSide: { classPropertyName: "serverSide", publicName: "serverSide", isSignal: true, isRequired: false, transformFunction: null }, totalRecords: { classPropertyName: "totalRecords", publicName: "totalRecords", isSignal: true, isRequired: false, transformFunction: null }, filters: { classPropertyName: "filters", publicName: "filters", isSignal: true, isRequired: false, transformFunction: null }, activeFilter: { classPropertyName: "activeFilter", publicName: "activeFilter", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { refresh: "refresh", pageChange: "pageChange", filterChange: "filterChange", searchChange: "searchChange", filterClick: "filterClick" }, ngImport: i0, template: "<div class=\"space-y-2 min-[1440px]:space-y-3 mt-2 min-[1440px]:mt-3 animate-fade-in pb-8\">\n @if (showSearch() || filters().length > 0 || headerActions().length > 0) {\n <div class=\"card p-2 min-[1440px]:p-2.5 flex flex-row items-center justify-between gap-2\">\n <div class=\"flex items-center gap-2 min-[1440px]:gap-3 flex-1 min-w-0\">\n @if (showSearch()) {\n <div class=\"relative w-full md:w-52 min-[1440px]:w-56\">\n <label [for]=\"'shk-table-search'\" class=\"sr-only\">{{\n searchPlaceholder() || ('common.search_placeholder' | transloco)\n }}</label>\n <div class=\"absolute inset-y-0 left-0 pl-2.5 flex items-center pointer-events-none\">\n <svg\n [lucideIcon]=\"getIcon('search')\"\n class=\"w-3.5 h-3.5 _shk-text-surface-400 dark:[color:var(--shk-surface-500)]\"\n ></svg>\n </div>\n <input\n id=\"shk-table-search\"\n type=\"text\"\n [value]=\"searchQuery()\"\n (input)=\"onSearchInput($any($event.target).value)\"\n [placeholder]=\"searchPlaceholder() || ('common.search_placeholder' | transloco)\"\n class=\"block w-full pl-8 pr-3 py-1.5 text-xs border _shk-border dark:[border-color:var(--shk-surface-200)] rounded-md leading-5 _shk-bg-surface-50 dark:[background-color:var(--shk-surface-100)] placeholder:[color:var(--shk-surface-400)] dark:placeholder:[color:var(--shk-surface-500)] _shk-text-surface-700 dark:[color:var(--shk-surface-700)] focus:outline-none focus:bg-white dark:focus:[background-color:var(--shk-surface-800)] focus:[border-color:var(--shk-primary-500)] focus:ring-1 focus:[--tw-ring-color:var(--shk-primary-500)]\"\n />\n </div>\n }\n @if (filters().length > 0) {\n <div\n class=\"flex _shk-bg-surface-100 dark:[background-color:var(--shk-surface-100)] p-0.5 rounded-md ml-auto\"\n role=\"group\"\n >\n @for (f of filters(); track f.value) {\n <button\n type=\"button\"\n (click)=\"onFilterClick(f.value)\"\n [attr.aria-pressed]=\"f.value === activeFilter()\"\n class=\"px-2.5 min-[1440px]:px-3 py-1 text-xs font-medium rounded _shk-text-surface-700 dark:[color:var(--shk-surface-700)] transition-all\"\n [class]=\"\n f.value === activeFilter()\n ? 'bg-white shadow-sm dark:[background-color:var(--shk-surface-700)]'\n : ''\n \"\n >\n {{ f.label }}\n </button>\n }\n </div>\n }\n </div>\n @if (headerActions().length > 0) {\n <div class=\"flex items-center gap-1.5 shrink-0\">\n @for (action of headerActions(); track $index) {\n @if (isHeaderActionVisible(action)) {\n <button\n type=\"button\"\n [class]=\"\n 'inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-md _shk-bg-primary text-white hover:[background-color:var(--shk-primary-700)] transition-colors disabled:opacity-50 disabled:cursor-not-allowed ' +\n (action.class || '')\n \"\n [disabled]=\"isHeaderActionDisabled(action)\"\n [attr.aria-label]=\"action.label\"\n (click)=\"action.onClick()\"\n >\n @if (action.icon) {\n <svg [lucideIcon]=\"getIcon(action.icon)\" class=\"w-3.5 h-3.5\"></svg>\n }\n {{ action.label }}\n </button>\n }\n }\n </div>\n }\n </div>\n }\n <div class=\"card p-0 overflow-hidden\" [class.shadow-lg]=\"hasShadow()\">\n <div class=\"table-scroll-wrapper _shk-scrollbar\">\n <table>\n <thead>\n <tr>\n @for (col of columnsWithActions(); track col.field) {\n <th\n scope=\"col\"\n (click)=\"col.field !== 'actions' && col.sortable && toggleSort(col.field)\"\n (keydown)=\"onHeaderKeydown($event, col)\"\n [tabindex]=\"col.field !== 'actions' && col.sortable ? 0 : -1\"\n [attr.role]=\"col.field !== 'actions' && col.sortable ? 'button' : null\"\n [attr.aria-sort]=\"getAriaSort(col)\"\n [attr.data-shk-actions]=\"col.field === 'actions' ? '' : null\"\n [style.cursor]=\"col.field !== 'actions' && col.sortable ? 'pointer' : 'default'\"\n [style.width]=\"col.field === 'actions' ? col.width : null\"\n [style.min-width]=\"col.field === 'actions' ? col.width : null\"\n [style.--shk-col-width]=\"col.field !== 'actions' ? getColumnWidth(col) : null\"\n class=\"px-2 min-[1440px]:px-3 py-2 min-[1440px]:py-2.5 text-left text-[10px] min-[1440px]:text-xs font-bold _shk-text-surface-500 dark:[color:var(--shk-surface-400)] uppercase tracking-wider _shk-bg-surface-50 dark:[background-color:var(--shk-surface-100)] border-b _shk-border dark:[border-color:var(--shk-surface-200)] focus:outline-none focus-visible:ring-1 focus-visible:[--tw-ring-color:var(--shk-primary-500)]\"\n >\n @if (col.field !== 'actions') {\n <div class=\"flex items-center justify-between gap-2\">\n <span>{{ col.header }}</span>\n <div class=\"flex items-center gap-1\">\n @if (col.sortable) {\n <shk-tooltip [text]=\"'table.tooltips.sort' | transloco\">\n @if (sortField() === col.field) {\n <svg\n [lucideIcon]=\"getIcon(sortOrder() === 1 ? 'arrow-up' : 'arrow-down')\"\n class=\"w-3.5 h-3.5 _shk-text-primary\"\n ></svg>\n } @else {\n <svg\n [lucideIcon]=\"getIcon('arrow-up-down')\"\n class=\"w-3.5 h-3.5 _shk-text-surface-400 dark:[color:var(--shk-surface-500)]\"\n ></svg>\n }\n </shk-tooltip>\n }\n @if (col.filter) {\n <shk-tooltip [text]=\"'table.tooltips.filter' | transloco\">\n <button\n type=\"button\"\n (click)=\"$event.stopPropagation(); toggleFilter(col.field)\"\n [attr.aria-label]=\"'table.tooltips.filter' | transloco\"\n [attr.aria-expanded]=\"showFilterInput()[col.field] || false\"\n class=\"_shk-text-surface-400 dark:[color:var(--shk-surface-500)] hover:[color:var(--shk-surface-600)] dark:hover:[color:var(--shk-surface-300)] transition-colors\"\n >\n <svg\n [lucideIcon]=\"getIcon('filter')\"\n class=\"w-3.5 h-3.5\"\n [class._shk-text-primary]=\"hasFilterValue(col.field)\"\n ></svg>\n </button>\n </shk-tooltip>\n }\n </div>\n </div>\n @if (col.filter && showFilterInput()[col.field]) {\n <div\n class=\"mt-2\"\n [formGroup]=\"columnFiltersForm\"\n (click)=\"$event.stopPropagation()\"\n >\n <div class=\"relative\">\n @if (col.filterType !== 'select') {\n <label [for]=\"'shk-filter-' + col.field\" class=\"sr-only\">{{\n col.filterPlaceholder || ('common.search_placeholder' | transloco)\n }}</label>\n <input\n [id]=\"'shk-filter-' + col.field\"\n type=\"text\"\n [formControlName]=\"col.field\"\n class=\"w-full px-3 py-1.5 text-sm rounded-md border _shk-border dark:[border-color:var(--shk-surface-200)] bg-white dark:[background-color:var(--shk-surface-100)] _shk-text-surface-700 dark:[color:var(--shk-surface-700)] focus:[border-color:var(--shk-primary-500)] focus:ring-1 focus:[--tw-ring-color:var(--shk-primary-500)] placeholder:[color:var(--shk-surface-400)] dark:placeholder:[color:var(--shk-surface-500)]\"\n [placeholder]=\"\n col.filterPlaceholder || ('common.search_placeholder' | transloco)\n \"\n />\n }\n @if (col.filterType === 'select') {\n <label [for]=\"'shk-filter-' + col.field\" class=\"sr-only\">{{\n col.filterPlaceholder || ('common.search_placeholder' | transloco)\n }}</label>\n <select\n [id]=\"'shk-filter-' + col.field\"\n [formControlName]=\"col.field\"\n class=\"w-full px-2 py-1 text-xs rounded-md border _shk-border dark:[border-color:var(--shk-surface-200)] bg-white dark:[background-color:var(--shk-surface-100)] _shk-text-surface-700 dark:[color:var(--shk-surface-700)] focus:[border-color:var(--shk-primary-500)] focus:ring-1 focus:[--tw-ring-color:var(--shk-primary-500)] appearance-none\"\n >\n <option [ngValue]=\"null\">{{ 'table.filterAll' | transloco }}</option>\n @for (opt of col.filterOptions || []; track opt.value) {\n <option [ngValue]=\"opt.value\">{{ opt.label }}</option>\n }\n </select>\n }\n @if (hasFilterValue(col.field) && col.filterType !== 'select') {\n <button\n type=\"button\"\n class=\"absolute right-2 top-1/2 -translate-y-1/2 _shk-text-surface-400 dark:[color:var(--shk-surface-500)] hover:[color:var(--shk-surface-600)] dark:hover:[color:var(--shk-surface-300)]\"\n (click)=\"clearFilter(col.field)\"\n >\n <svg [lucideIcon]=\"getIcon('x')\" class=\"w-3.5 h-3.5\"></svg>\n </button>\n }\n </div>\n </div>\n }\n } @else {\n <div class=\"flex items-center justify-center\">\n <span>{{ col.header }}</span>\n </div>\n }\n </th>\n }\n </tr>\n </thead>\n @if (loading()) {\n <tbody class=\"border-b _shk-border dark:[border-color:var(--shk-surface-100)]\">\n <tr>\n <td [attr.colspan]=\"columnsWithActions().length\" class=\"text-center py-12\">\n <div\n class=\"flex items-center justify-center\"\n role=\"status\"\n [attr.aria-label]=\"'common.loading' | transloco\"\n >\n <svg\n [lucideIcon]=\"getIcon('loader')\"\n class=\"w-6 h-6 animate-spin _shk-text-primary\"\n ></svg>\n </div>\n </td>\n </tr>\n </tbody>\n } @else if (effectiveDisplayedData().length === 0) {\n <tbody class=\"border-b _shk-border dark:[border-color:var(--shk-surface-100)]\">\n <tr>\n <td [attr.colspan]=\"columnsWithActions().length\" class=\"text-center py-12\">\n <div class=\"flex flex-col items-center\">\n <div\n class=\"w-16 h-16 _shk-bg-surface-100 dark:[background-color:var(--shk-surface-100)] rounded-full flex items-center justify-center mb-4\"\n >\n <svg\n [lucideIcon]=\"getIcon('inbox')\"\n class=\"w-8 h-8 _shk-text-surface-400 dark:[color:var(--shk-surface-500)]\"\n ></svg>\n </div>\n <p\n class=\"text-lg font-semibold _shk-text-surface-600 dark:[color:var(--shk-surface-300)]\"\n >\n {{ emptyMessage() || ('common.no_records' | transloco) }}\n </p>\n </div>\n </td>\n </tr>\n </tbody>\n } @else {\n <tbody>\n @for (rowData of effectiveDisplayedData(); track trackByRow(rowData, $index)) {\n <tr\n class=\"hover:[background-color:color-mix(in srgb,var(--shk-primary-50)30%,transparent)] dark:hover:[background-color:color-mix(in srgb,var(--shk-primary-900)10%,transparent)] transition-colors duration-200 border-b _shk-border dark:[border-color:var(--shk-surface-100)]\"\n >\n @for (col of columnsWithActions(); track col.field) {\n <td\n [attr.data-shk-actions]=\"col.field === 'actions' ? '' : null\"\n class=\"px-2 min-[1440px]:px-3 py-2 min-[1440px]:py-2.5 text-xs font-medium _shk-text-surface-700 dark:[color:var(--shk-surface-700)] whitespace-nowrap\"\n [style.overflow]=\"col.field === 'actions' ? 'visible' : 'hidden'\"\n [style.text-overflow]=\"col.field === 'actions' ? 'clip' : 'ellipsis'\"\n [style.--shk-col-width]=\"col.field !== 'actions' ? getColumnWidth(col) : null\"\n >\n @if (col.field !== 'actions') {\n @if (col.template === 'tag') {\n <shk-tag\n [value]=\"getTagValue(col, rowData)\"\n [severity]=\"getTagSeverity(col, rowData)\"\n [rounded]=\"getTagRounded(col, rowData)\"\n [icon]=\"getTagIcon(col, rowData)\"\n [style]=\"getTagStyle(col, rowData)\"\n />\n } @else if (col.format) {\n <span>{{ col.format(rowData) }}</span>\n } @else {\n <ng-container\n *ngTemplateOutlet=\"\n customTemplates()[col.field] || defaultTemplate;\n context: { $implicit: rowData, field: col.field }\n \"\n ></ng-container>\n }\n } @else {\n <div class=\"flex items-center justify-start gap-px\">\n @for (action of rowActions(); track $index) {\n @if (isRowActionVisible(action, rowData)) {\n <div class=\"relative group\">\n <shk-tooltip\n [text]=\"getRowActionLabel(action, rowData)\"\n position=\"top\"\n >\n <button\n type=\"button\"\n (click)=\"action.onClick(rowData)\"\n [class]=\"\n 'table-action-btn hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)] rounded-md ' +\n getRowActionClass(action, rowData)\n \"\n [disabled]=\"isRowActionDisabled(action, rowData) || false\"\n [attr.aria-label]=\"getRowActionLabel(action, rowData)\"\n >\n <svg\n [lucideIcon]=\"getIcon(getRowActionIconName(action, rowData))\"\n class=\"w-4 h-4\"\n ></svg>\n </button>\n </shk-tooltip>\n </div>\n }\n }\n </div>\n }\n </td>\n }\n </tr>\n }\n </tbody>\n }\n </table>\n <ng-template #defaultTemplate let-rowData let-field=\"field\">\n {{ truncate(rowData[field], 30) }}\n </ng-template>\n </div>\n <div\n class=\"flex items-center justify-between gap-4 px-2 min-[1440px]:px-3 py-2 border-t _shk-border dark:[border-color:var(--shk-surface-100)]\"\n >\n <div class=\"text-xs _shk-text-surface-600 dark:[color:var(--shk-surface-400)]\">\n {{ pageReport() }}\n </div>\n <div\n class=\"flex items-center gap-0.5\"\n role=\"navigation\"\n >\n <button\n type=\"button\"\n (click)=\"goToFirst()\"\n [disabled]=\"isFirstPage()\"\n class=\"p-1 _shk-text-surface-500 hover:[color:var(--shk-surface-700)] dark:[color:var(--shk-surface-400)] dark:hover:[color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed rounded transition-colors\"\n >\n <svg [lucideIcon]=\"getIcon('chevrons-left')\" class=\"w-4 h-4\"></svg>\n </button>\n <button\n type=\"button\"\n (click)=\"prev()\"\n [disabled]=\"isFirstPage()\"\n class=\"p-1 _shk-text-surface-500 hover:[color:var(--shk-surface-700)] dark:[color:var(--shk-surface-400)] dark:hover:[color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed rounded transition-colors\"\n >\n <svg [lucideIcon]=\"getIcon('chevron-left')\" class=\"w-4 h-4\"></svg>\n </button>\n <span\n class=\"px-2 text-xs font-medium _shk-text-surface-700 dark:[color:var(--shk-surface-700)]\"\n >{{ currentPage() }} / {{ totalPages() }}</span\n >\n <button\n type=\"button\"\n (click)=\"next()\"\n [disabled]=\"isLastPage()\"\n class=\"p-1 _shk-text-surface-500 hover:[color:var(--shk-surface-700)] dark:[color:var(--shk-surface-400)] dark:hover:[color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed rounded transition-colors\"\n >\n <svg [lucideIcon]=\"getIcon('chevron-right')\" class=\"w-4 h-4\"></svg>\n </button>\n <button\n type=\"button\"\n (click)=\"goToLast()\"\n [disabled]=\"isLastPage()\"\n class=\"p-1 _shk-text-surface-500 hover:[color:var(--shk-surface-700)] dark:[color:var(--shk-surface-400)] dark:hover:[color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed rounded transition-colors\"\n >\n <svg [lucideIcon]=\"getIcon('chevrons-right')\" class=\"w-4 h-4\"></svg>\n </button>\n </div>\n <div\n class=\"flex items-center gap-1.5 text-xs _shk-text-surface-600 dark:[color:var(--shk-surface-400)]\"\n >\n <label [for]=\"'shk-rows-per-page'\">{{ 'table.rows' | transloco }}</label>\n <select\n id=\"shk-rows-per-page\"\n [value]=\"rowsPerPageLocal()\"\n (change)=\"onRowsPerPageChange($any($event.target).value)\"\n class=\"border _shk-border dark:[border-color:var(--shk-surface-200)] rounded px-2 py-1 text-xs bg-white dark:[background-color:var(--shk-surface-100)] _shk-text-surface-700 dark:[color:var(--shk-surface-700)] focus:outline-none focus:[border-color:var(--shk-primary-500)]\"\n >\n @for (opt of rowsPerPageOptions(); track opt) {\n <option [value]=\"opt\">{{ opt }}</option>\n }\n </select>\n </div>\n </div>\n </div>\n</div>\n", styles: ["input:focus{outline:none}input[type=text]{font-weight:400!important}table th,table td{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}td button svg{width:1.125rem!important;height:1.125rem!important}td button:disabled{opacity:.4;cursor:not-allowed}.table-action-btn svg{width:1rem!important;height:1rem!important;min-width:1rem!important;min-height:1rem!important}.table-action-btn{padding:.5rem!important;min-width:2rem!important;min-height:2rem!important;display:inline-flex!important;align-items:center!important;justify-content:center!important}.table-scroll-wrapper{width:100%;overflow-x:auto;overflow-y:hidden;border-radius:inherit;-webkit-overflow-scrolling:touch;container-type:inline-size}.table-scroll-wrapper table{table-layout:fixed;width:100%}@container (max-width: 768px){.table-scroll-wrapper{scroll-snap-type:x mandatory}.table-scroll-wrapper table{table-layout:auto;width:100%}.table-scroll-wrapper th:not([data-shk-actions]),.table-scroll-wrapper td:not([data-shk-actions]){min-width:var(--shk-col-width, 140px);max-width:var(--shk-col-width, 140px)}.table-scroll-wrapper th,.table-scroll-wrapper td{scroll-snap-align:start}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: TranslocoModule }, { kind: "component", type: LucideDynamicIcon, selector: "svg[lucideIcon]", inputs: ["lucideIcon"] }, { kind: "component", type: TagComponent, selector: "shk-tag", inputs: ["value", "severity", "rounded", "icon", "style", "styleClass"] }, { kind: "component", type: TooltipComponent, selector: "shk-tooltip", inputs: ["text", "position", "mode", "offset"] }, { kind: "pipe", type: i1$1.TranslocoPipe, name: "transloco" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2775
3052
  }
2776
3053
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: TableComponent, decorators: [{
2777
3054
  type: Component,
2778
- args: [{ selector: 'shk-table', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [CommonModule, ReactiveFormsModule, TranslocoModule, LucideDynamicIcon], template: "<div class=\"space-y-2 min-[1440px]:space-y-3 mt-2 min-[1440px]:mt-3 animate-fade-in pb-8\">\n @if (showSearch() || filters().length > 0 || headerActions().length > 0) {\n <div class=\"card p-2 min-[1440px]:p-2.5 flex flex-row items-center justify-between gap-2\">\n <div class=\"flex items-center gap-2 min-[1440px]:gap-3 flex-1 min-w-0\">\n @if (showSearch()) {\n <div class=\"relative w-full md:w-52 min-[1440px]:w-56\">\n <label [for]=\"'shk-table-search'\" class=\"sr-only\">{{\n searchPlaceholder() || ('common.search_placeholder' | transloco)\n }}</label>\n <div class=\"absolute inset-y-0 left-0 pl-2.5 flex items-center pointer-events-none\">\n <svg\n [lucideIcon]=\"getIcon('search')\"\n class=\"w-3.5 h-3.5 _shk-text-surface-400 dark:[color:var(--shk-surface-500)]\"\n ></svg>\n </div>\n <input\n id=\"shk-table-search\"\n type=\"text\"\n [value]=\"searchQuery()\"\n (input)=\"onSearchInput($any($event.target).value)\"\n [placeholder]=\"searchPlaceholder() || ('common.search_placeholder' | transloco)\"\n class=\"block w-full pl-8 pr-3 py-1.5 text-xs border _shk-border dark:[border-color:var(--shk-surface-200)] rounded-md leading-5 _shk-bg-surface-50 dark:[background-color:var(--shk-surface-100)] placeholder:[color:var(--shk-surface-400)] dark:placeholder:[color:var(--shk-surface-500)] _shk-text-surface-700 dark:[color:var(--shk-surface-700)] focus:outline-none focus:bg-white dark:focus:[background-color:var(--shk-surface-800)] focus:[border-color:var(--shk-primary-500)] focus:ring-1 focus:[--tw-ring-color:var(--shk-primary-500)]\"\n />\n </div>\n }\n @if (filters().length > 0) {\n <div\n class=\"flex _shk-bg-surface-100 dark:[background-color:var(--shk-surface-100)] p-0.5 rounded-md ml-auto\"\n role=\"group\"\n >\n @for (f of filters(); track f.value) {\n <button\n type=\"button\"\n (click)=\"onFilterClick(f.value)\"\n [attr.aria-pressed]=\"f.value === activeFilter()\"\n class=\"px-2.5 min-[1440px]:px-3 py-1 text-xs font-medium rounded _shk-text-surface-700 dark:[color:var(--shk-surface-700)] transition-all\"\n [class]=\"\n f.value === activeFilter()\n ? 'bg-white shadow-sm dark:[background-color:var(--shk-surface-700)]'\n : ''\n \"\n >\n {{ f.label }}\n </button>\n }\n </div>\n }\n </div>\n @if (headerActions().length > 0) {\n <div class=\"flex items-center gap-1.5 shrink-0\">\n @for (action of headerActions(); track $index) {\n @if (isHeaderActionVisible(action)) {\n <button\n type=\"button\"\n [class]=\"\n 'inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-md _shk-bg-primary text-white hover:[background-color:var(--shk-primary-700)] transition-colors disabled:opacity-50 disabled:cursor-not-allowed ' +\n (action.class || '')\n \"\n [disabled]=\"isHeaderActionDisabled(action)\"\n [attr.aria-label]=\"action.label\"\n (click)=\"action.onClick()\"\n >\n @if (action.icon) {\n <svg [lucideIcon]=\"getIcon(action.icon)\" class=\"w-3.5 h-3.5\"></svg>\n }\n {{ action.label }}\n </button>\n }\n }\n </div>\n }\n </div>\n }\n <div class=\"card p-0 overflow-hidden\" [class.shadow-lg]=\"hasShadow()\">\n <div class=\"table-scroll-wrapper _shk-scrollbar\">\n <table>\n <thead>\n <tr>\n @for (col of columnsWithActions(); track col.field) {\n <th\n scope=\"col\"\n (click)=\"col.field !== 'actions' && col.sortable && toggleSort(col.field)\"\n (keydown)=\"onHeaderKeydown($event, col)\"\n [tabindex]=\"col.field !== 'actions' && col.sortable ? 0 : -1\"\n [attr.role]=\"col.field !== 'actions' && col.sortable ? 'button' : null\"\n [attr.aria-sort]=\"getAriaSort(col)\"\n [attr.data-shk-actions]=\"col.field === 'actions' ? '' : null\"\n [style.cursor]=\"col.field !== 'actions' && col.sortable ? 'pointer' : 'default'\"\n [style.width]=\"col.field === 'actions' ? col.width : null\"\n [style.min-width]=\"col.field === 'actions' ? col.width : null\"\n [style.--shk-col-width]=\"col.field !== 'actions' ? getColumnWidth(col) : null\"\n class=\"px-2 min-[1440px]:px-3 py-2 min-[1440px]:py-2.5 text-left text-[10px] min-[1440px]:text-xs font-bold _shk-text-surface-500 dark:[color:var(--shk-surface-400)] uppercase tracking-wider _shk-bg-surface-50 dark:[background-color:var(--shk-surface-100)] border-b _shk-border dark:[border-color:var(--shk-surface-200)] focus:outline-none focus-visible:ring-1 focus-visible:[--tw-ring-color:var(--shk-primary-500)]\"\n >\n @if (col.field !== 'actions') {\n <div class=\"flex items-center justify-between gap-2\">\n <span>{{ col.header }}</span>\n <div class=\"flex items-center gap-1\">\n @if (col.sortable) {\n <div class=\"relative group\">\n @if (sortField() === col.field) {\n <svg\n [lucideIcon]=\"getIcon(sortOrder() === 1 ? 'arrow-up' : 'arrow-down')\"\n class=\"w-3.5 h-3.5 _shk-text-primary\"\n ></svg>\n } @else {\n <svg\n [lucideIcon]=\"getIcon('arrow-up-down')\"\n class=\"w-3.5 h-3.5 _shk-text-surface-400 dark:[color:var(--shk-surface-500)]\"\n ></svg>\n }\n <span\n class=\"absolute top-full left-1/2 -translate-x-1/2 mt-1 px-2 py-1 text-xs _shk-bg-tooltip _shk-text-tooltip rounded whitespace-nowrap opacity-0 group-hover:opacity-100 transition pointer-events-none z-[9999] shadow-lg\"\n >\n {{ 'table.tooltips.sort' | transloco }}\n </span>\n </div>\n }\n @if (col.filter) {\n <div class=\"relative group\">\n <button\n type=\"button\"\n (click)=\"$event.stopPropagation(); toggleFilter(col.field)\"\n [attr.aria-label]=\"'table.tooltips.filter' | transloco\"\n [attr.aria-expanded]=\"showFilterInput()[col.field] || false\"\n class=\"_shk-text-surface-400 dark:[color:var(--shk-surface-500)] hover:[color:var(--shk-surface-600)] dark:hover:[color:var(--shk-surface-300)] transition-colors\"\n >\n <svg\n [lucideIcon]=\"getIcon('filter')\"\n class=\"w-3.5 h-3.5\"\n [class._shk-text-primary]=\"hasFilterValue(col.field)\"\n ></svg>\n <span\n class=\"absolute top-full left-1/2 -translate-x-1/2 mt-1 px-2 py-1 text-xs _shk-bg-tooltip _shk-text-tooltip rounded whitespace-nowrap opacity-0 group-hover:opacity-100 transition pointer-events-none z-[9999] shadow-lg\"\n >\n {{ 'table.tooltips.filter' | transloco }}\n </span>\n </button>\n </div>\n }\n </div>\n </div>\n @if (col.filter && showFilterInput()[col.field]) {\n <div\n class=\"mt-2\"\n [formGroup]=\"columnFiltersForm\"\n (click)=\"$event.stopPropagation()\"\n >\n <div class=\"relative\">\n @if (col.filterType !== 'select') {\n <label [for]=\"'shk-filter-' + col.field\" class=\"sr-only\">{{\n col.filterPlaceholder || ('common.search_placeholder' | transloco)\n }}</label>\n <input\n [id]=\"'shk-filter-' + col.field\"\n type=\"text\"\n [formControlName]=\"col.field\"\n class=\"w-full px-3 py-1.5 text-sm rounded-md border _shk-border dark:[border-color:var(--shk-surface-200)] bg-white dark:[background-color:var(--shk-surface-100)] _shk-text-surface-700 dark:[color:var(--shk-surface-700)] focus:[border-color:var(--shk-primary-500)] focus:ring-1 focus:[--tw-ring-color:var(--shk-primary-500)] placeholder:[color:var(--shk-surface-400)] dark:placeholder:[color:var(--shk-surface-500)]\"\n [placeholder]=\"\n col.filterPlaceholder || ('common.search_placeholder' | transloco)\n \"\n />\n }\n @if (col.filterType === 'select') {\n <label [for]=\"'shk-filter-' + col.field\" class=\"sr-only\">{{\n col.filterPlaceholder || ('common.search_placeholder' | transloco)\n }}</label>\n <select\n [id]=\"'shk-filter-' + col.field\"\n [formControlName]=\"col.field\"\n class=\"w-full px-2 py-1 text-xs rounded-md border _shk-border dark:[border-color:var(--shk-surface-200)] bg-white dark:[background-color:var(--shk-surface-100)] _shk-text-surface-700 dark:[color:var(--shk-surface-700)] focus:[border-color:var(--shk-primary-500)] focus:ring-1 focus:[--tw-ring-color:var(--shk-primary-500)] appearance-none\"\n >\n <option [ngValue]=\"null\">{{ 'table.filterAll' | transloco }}</option>\n @for (opt of col.filterOptions || []; track opt.value) {\n <option [ngValue]=\"opt.value\">{{ opt.label }}</option>\n }\n </select>\n }\n @if (hasFilterValue(col.field) && col.filterType !== 'select') {\n <button\n type=\"button\"\n class=\"absolute right-2 top-1/2 -translate-y-1/2 _shk-text-surface-400 dark:[color:var(--shk-surface-500)] hover:[color:var(--shk-surface-600)] dark:hover:[color:var(--shk-surface-300)]\"\n (click)=\"clearFilter(col.field)\"\n >\n <svg [lucideIcon]=\"getIcon('x')\" class=\"w-3.5 h-3.5\"></svg>\n </button>\n }\n </div>\n </div>\n }\n } @else {\n <div class=\"flex items-center justify-center\">\n <span>{{ col.header }}</span>\n </div>\n }\n </th>\n }\n </tr>\n </thead>\n @if (loading()) {\n <tbody class=\"border-b _shk-border dark:[border-color:var(--shk-surface-100)]\">\n <tr>\n <td [attr.colspan]=\"columnsWithActions().length\" class=\"text-center py-12\">\n <div\n class=\"flex items-center justify-center\"\n role=\"status\"\n [attr.aria-label]=\"'common.loading' | transloco\"\n >\n <svg\n [lucideIcon]=\"getIcon('loader')\"\n class=\"w-6 h-6 animate-spin _shk-text-primary\"\n ></svg>\n </div>\n </td>\n </tr>\n </tbody>\n } @else if (effectiveDisplayedData().length === 0) {\n <tbody class=\"border-b _shk-border dark:[border-color:var(--shk-surface-100)]\">\n <tr>\n <td [attr.colspan]=\"columnsWithActions().length\" class=\"text-center py-12\">\n <div class=\"flex flex-col items-center\">\n <div\n class=\"w-16 h-16 _shk-bg-surface-100 dark:[background-color:var(--shk-surface-100)] rounded-full flex items-center justify-center mb-4\"\n >\n <svg\n [lucideIcon]=\"getIcon('inbox')\"\n class=\"w-8 h-8 _shk-text-surface-400 dark:[color:var(--shk-surface-500)]\"\n ></svg>\n </div>\n <p\n class=\"text-lg font-semibold _shk-text-surface-600 dark:[color:var(--shk-surface-300)]\"\n >\n {{ emptyMessage() || ('common.no_records' | transloco) }}\n </p>\n </div>\n </td>\n </tr>\n </tbody>\n } @else {\n <tbody>\n @for (rowData of effectiveDisplayedData(); track trackByRow(rowData, $index)) {\n <tr\n class=\"hover:[background-color:color-mix(in srgb,var(--shk-primary-50)30%,transparent)] dark:hover:[background-color:color-mix(in srgb,var(--shk-primary-900)10%,transparent)] transition-colors duration-200 border-b _shk-border dark:[border-color:var(--shk-surface-100)]\"\n >\n @for (col of columnsWithActions(); track col.field) {\n <td\n [attr.data-shk-actions]=\"col.field === 'actions' ? '' : null\"\n class=\"px-2 min-[1440px]:px-3 py-2 min-[1440px]:py-2.5 text-xs font-medium _shk-text-surface-700 dark:[color:var(--shk-surface-700)] whitespace-nowrap\"\n [style.overflow]=\"col.field === 'actions' ? 'visible' : 'hidden'\"\n [style.text-overflow]=\"col.field === 'actions' ? 'clip' : 'ellipsis'\"\n [style.--shk-col-width]=\"col.field !== 'actions' ? getColumnWidth(col) : null\"\n >\n @if (col.field !== 'actions') {\n @if (col.template === 'tag') {\n <span\n class=\"inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium\"\n [class]=\"\n getTagClass(col.tagSeverity ? col.tagSeverity(rowData) : undefined)\n \"\n [style]=\"col.tagStyle ? col.tagStyle(rowData) : undefined\"\n >\n {{ col.tagValue ? col.tagValue(rowData) : rowData[col.field] }}\n </span>\n } @else if (col.format) {\n <span>{{ col.format(rowData) }}</span>\n } @else {\n <ng-container\n *ngTemplateOutlet=\"\n customTemplates()[col.field] || defaultTemplate;\n context: { $implicit: rowData, field: col.field }\n \"\n ></ng-container>\n }\n } @else {\n <div class=\"flex items-center justify-start gap-px\">\n @for (action of rowActions(); track $index) {\n @if (isRowActionVisible(action, rowData)) {\n <div class=\"relative group\">\n <button\n type=\"button\"\n (click)=\"action.onClick(rowData)\"\n [class]=\"\n 'table-action-btn hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)] rounded-md ' +\n getRowActionClass(action, rowData)\n \"\n [disabled]=\"isRowActionDisabled(action, rowData) || false\"\n [attr.aria-label]=\"getRowActionLabel(action, rowData)\"\n >\n <svg\n [lucideIcon]=\"getIcon(getRowActionIconName(action, rowData))\"\n class=\"w-4 h-4\"\n ></svg>\n </button>\n <span\n class=\"absolute bottom-full left-1/2 -translate-x-1/2 mb-1 px-2 py-1 text-xs _shk-bg-tooltip _shk-text-tooltip rounded whitespace-nowrap opacity-0 group-hover:opacity-100 transition pointer-events-none z-[9999] shadow-lg\"\n >\n {{ getRowActionLabel(action, rowData) }}\n </span>\n </div>\n }\n }\n </div>\n }\n </td>\n }\n </tr>\n }\n </tbody>\n }\n </table>\n <ng-template #defaultTemplate let-rowData let-field=\"field\">\n {{ truncate(rowData[field], 30) }}\n </ng-template>\n </div>\n <div\n class=\"flex items-center justify-between gap-4 px-2 min-[1440px]:px-3 py-2 border-t _shk-border dark:[border-color:var(--shk-surface-100)]\"\n >\n <div class=\"text-xs _shk-text-surface-600 dark:[color:var(--shk-surface-400)]\">\n {{ pageReport() }}\n </div>\n <div\n class=\"flex items-center gap-0.5\"\n role=\"navigation\"\n >\n <button\n type=\"button\"\n (click)=\"goToFirst()\"\n [disabled]=\"isFirstPage()\"\n class=\"p-1 _shk-text-surface-500 hover:[color:var(--shk-surface-700)] dark:[color:var(--shk-surface-400)] dark:hover:[color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed rounded transition-colors\"\n >\n <svg [lucideIcon]=\"getIcon('chevrons-left')\" class=\"w-4 h-4\"></svg>\n </button>\n <button\n type=\"button\"\n (click)=\"prev()\"\n [disabled]=\"isFirstPage()\"\n class=\"p-1 _shk-text-surface-500 hover:[color:var(--shk-surface-700)] dark:[color:var(--shk-surface-400)] dark:hover:[color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed rounded transition-colors\"\n >\n <svg [lucideIcon]=\"getIcon('chevron-left')\" class=\"w-4 h-4\"></svg>\n </button>\n <span\n class=\"px-2 text-xs font-medium _shk-text-surface-700 dark:[color:var(--shk-surface-700)]\"\n >{{ currentPage() }} / {{ totalPages() }}</span\n >\n <button\n type=\"button\"\n (click)=\"next()\"\n [disabled]=\"isLastPage()\"\n class=\"p-1 _shk-text-surface-500 hover:[color:var(--shk-surface-700)] dark:[color:var(--shk-surface-400)] dark:hover:[color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed rounded transition-colors\"\n >\n <svg [lucideIcon]=\"getIcon('chevron-right')\" class=\"w-4 h-4\"></svg>\n </button>\n <button\n type=\"button\"\n (click)=\"goToLast()\"\n [disabled]=\"isLastPage()\"\n class=\"p-1 _shk-text-surface-500 hover:[color:var(--shk-surface-700)] dark:[color:var(--shk-surface-400)] dark:hover:[color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed rounded transition-colors\"\n >\n <svg [lucideIcon]=\"getIcon('chevrons-right')\" class=\"w-4 h-4\"></svg>\n </button>\n </div>\n <div\n class=\"flex items-center gap-1.5 text-xs _shk-text-surface-600 dark:[color:var(--shk-surface-400)]\"\n >\n <label [for]=\"'shk-rows-per-page'\">{{ 'table.rows' | transloco }}</label>\n <select\n id=\"shk-rows-per-page\"\n [value]=\"rowsPerPageLocal()\"\n (change)=\"onRowsPerPageChange($any($event.target).value)\"\n class=\"border _shk-border dark:[border-color:var(--shk-surface-200)] rounded px-2 py-1 text-xs bg-white dark:[background-color:var(--shk-surface-100)] _shk-text-surface-700 dark:[color:var(--shk-surface-700)] focus:outline-none focus:[border-color:var(--shk-primary-500)]\"\n >\n @for (opt of rowsPerPageOptions(); track opt) {\n <option [value]=\"opt\">{{ opt }}</option>\n }\n </select>\n </div>\n </div>\n </div>\n</div>\n", styles: ["input:focus{outline:none}input[type=text]{font-weight:400!important}table th,table td{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}td button svg{width:1.125rem!important;height:1.125rem!important}td button:disabled{opacity:.4;cursor:not-allowed}.table-action-btn svg{width:1rem!important;height:1rem!important;min-width:1rem!important;min-height:1rem!important}.table-action-btn{padding:.5rem!important;min-width:2rem!important;min-height:2rem!important;display:inline-flex!important;align-items:center!important;justify-content:center!important}.table-scroll-wrapper{width:100%;overflow-x:auto;overflow-y:hidden;border-radius:inherit;-webkit-overflow-scrolling:touch;container-type:inline-size}.table-scroll-wrapper table{table-layout:fixed;width:100%}@container (max-width: 768px){.table-scroll-wrapper{scroll-snap-type:x mandatory}.table-scroll-wrapper table{table-layout:auto;width:100%}.table-scroll-wrapper th:not([data-shk-actions]),.table-scroll-wrapper td:not([data-shk-actions]){min-width:var(--shk-col-width, 140px);max-width:var(--shk-col-width, 140px)}.table-scroll-wrapper th,.table-scroll-wrapper td{scroll-snap-align:start}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\n"] }]
3055
+ args: [{ selector: 'shk-table', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [
3056
+ CommonModule,
3057
+ ReactiveFormsModule,
3058
+ TranslocoModule,
3059
+ LucideDynamicIcon,
3060
+ TagComponent,
3061
+ TooltipComponent,
3062
+ ], template: "<div class=\"space-y-2 min-[1440px]:space-y-3 mt-2 min-[1440px]:mt-3 animate-fade-in pb-8\">\n @if (showSearch() || filters().length > 0 || headerActions().length > 0) {\n <div class=\"card p-2 min-[1440px]:p-2.5 flex flex-row items-center justify-between gap-2\">\n <div class=\"flex items-center gap-2 min-[1440px]:gap-3 flex-1 min-w-0\">\n @if (showSearch()) {\n <div class=\"relative w-full md:w-52 min-[1440px]:w-56\">\n <label [for]=\"'shk-table-search'\" class=\"sr-only\">{{\n searchPlaceholder() || ('common.search_placeholder' | transloco)\n }}</label>\n <div class=\"absolute inset-y-0 left-0 pl-2.5 flex items-center pointer-events-none\">\n <svg\n [lucideIcon]=\"getIcon('search')\"\n class=\"w-3.5 h-3.5 _shk-text-surface-400 dark:[color:var(--shk-surface-500)]\"\n ></svg>\n </div>\n <input\n id=\"shk-table-search\"\n type=\"text\"\n [value]=\"searchQuery()\"\n (input)=\"onSearchInput($any($event.target).value)\"\n [placeholder]=\"searchPlaceholder() || ('common.search_placeholder' | transloco)\"\n class=\"block w-full pl-8 pr-3 py-1.5 text-xs border _shk-border dark:[border-color:var(--shk-surface-200)] rounded-md leading-5 _shk-bg-surface-50 dark:[background-color:var(--shk-surface-100)] placeholder:[color:var(--shk-surface-400)] dark:placeholder:[color:var(--shk-surface-500)] _shk-text-surface-700 dark:[color:var(--shk-surface-700)] focus:outline-none focus:bg-white dark:focus:[background-color:var(--shk-surface-800)] focus:[border-color:var(--shk-primary-500)] focus:ring-1 focus:[--tw-ring-color:var(--shk-primary-500)]\"\n />\n </div>\n }\n @if (filters().length > 0) {\n <div\n class=\"flex _shk-bg-surface-100 dark:[background-color:var(--shk-surface-100)] p-0.5 rounded-md ml-auto\"\n role=\"group\"\n >\n @for (f of filters(); track f.value) {\n <button\n type=\"button\"\n (click)=\"onFilterClick(f.value)\"\n [attr.aria-pressed]=\"f.value === activeFilter()\"\n class=\"px-2.5 min-[1440px]:px-3 py-1 text-xs font-medium rounded _shk-text-surface-700 dark:[color:var(--shk-surface-700)] transition-all\"\n [class]=\"\n f.value === activeFilter()\n ? 'bg-white shadow-sm dark:[background-color:var(--shk-surface-700)]'\n : ''\n \"\n >\n {{ f.label }}\n </button>\n }\n </div>\n }\n </div>\n @if (headerActions().length > 0) {\n <div class=\"flex items-center gap-1.5 shrink-0\">\n @for (action of headerActions(); track $index) {\n @if (isHeaderActionVisible(action)) {\n <button\n type=\"button\"\n [class]=\"\n 'inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-md _shk-bg-primary text-white hover:[background-color:var(--shk-primary-700)] transition-colors disabled:opacity-50 disabled:cursor-not-allowed ' +\n (action.class || '')\n \"\n [disabled]=\"isHeaderActionDisabled(action)\"\n [attr.aria-label]=\"action.label\"\n (click)=\"action.onClick()\"\n >\n @if (action.icon) {\n <svg [lucideIcon]=\"getIcon(action.icon)\" class=\"w-3.5 h-3.5\"></svg>\n }\n {{ action.label }}\n </button>\n }\n }\n </div>\n }\n </div>\n }\n <div class=\"card p-0 overflow-hidden\" [class.shadow-lg]=\"hasShadow()\">\n <div class=\"table-scroll-wrapper _shk-scrollbar\">\n <table>\n <thead>\n <tr>\n @for (col of columnsWithActions(); track col.field) {\n <th\n scope=\"col\"\n (click)=\"col.field !== 'actions' && col.sortable && toggleSort(col.field)\"\n (keydown)=\"onHeaderKeydown($event, col)\"\n [tabindex]=\"col.field !== 'actions' && col.sortable ? 0 : -1\"\n [attr.role]=\"col.field !== 'actions' && col.sortable ? 'button' : null\"\n [attr.aria-sort]=\"getAriaSort(col)\"\n [attr.data-shk-actions]=\"col.field === 'actions' ? '' : null\"\n [style.cursor]=\"col.field !== 'actions' && col.sortable ? 'pointer' : 'default'\"\n [style.width]=\"col.field === 'actions' ? col.width : null\"\n [style.min-width]=\"col.field === 'actions' ? col.width : null\"\n [style.--shk-col-width]=\"col.field !== 'actions' ? getColumnWidth(col) : null\"\n class=\"px-2 min-[1440px]:px-3 py-2 min-[1440px]:py-2.5 text-left text-[10px] min-[1440px]:text-xs font-bold _shk-text-surface-500 dark:[color:var(--shk-surface-400)] uppercase tracking-wider _shk-bg-surface-50 dark:[background-color:var(--shk-surface-100)] border-b _shk-border dark:[border-color:var(--shk-surface-200)] focus:outline-none focus-visible:ring-1 focus-visible:[--tw-ring-color:var(--shk-primary-500)]\"\n >\n @if (col.field !== 'actions') {\n <div class=\"flex items-center justify-between gap-2\">\n <span>{{ col.header }}</span>\n <div class=\"flex items-center gap-1\">\n @if (col.sortable) {\n <shk-tooltip [text]=\"'table.tooltips.sort' | transloco\">\n @if (sortField() === col.field) {\n <svg\n [lucideIcon]=\"getIcon(sortOrder() === 1 ? 'arrow-up' : 'arrow-down')\"\n class=\"w-3.5 h-3.5 _shk-text-primary\"\n ></svg>\n } @else {\n <svg\n [lucideIcon]=\"getIcon('arrow-up-down')\"\n class=\"w-3.5 h-3.5 _shk-text-surface-400 dark:[color:var(--shk-surface-500)]\"\n ></svg>\n }\n </shk-tooltip>\n }\n @if (col.filter) {\n <shk-tooltip [text]=\"'table.tooltips.filter' | transloco\">\n <button\n type=\"button\"\n (click)=\"$event.stopPropagation(); toggleFilter(col.field)\"\n [attr.aria-label]=\"'table.tooltips.filter' | transloco\"\n [attr.aria-expanded]=\"showFilterInput()[col.field] || false\"\n class=\"_shk-text-surface-400 dark:[color:var(--shk-surface-500)] hover:[color:var(--shk-surface-600)] dark:hover:[color:var(--shk-surface-300)] transition-colors\"\n >\n <svg\n [lucideIcon]=\"getIcon('filter')\"\n class=\"w-3.5 h-3.5\"\n [class._shk-text-primary]=\"hasFilterValue(col.field)\"\n ></svg>\n </button>\n </shk-tooltip>\n }\n </div>\n </div>\n @if (col.filter && showFilterInput()[col.field]) {\n <div\n class=\"mt-2\"\n [formGroup]=\"columnFiltersForm\"\n (click)=\"$event.stopPropagation()\"\n >\n <div class=\"relative\">\n @if (col.filterType !== 'select') {\n <label [for]=\"'shk-filter-' + col.field\" class=\"sr-only\">{{\n col.filterPlaceholder || ('common.search_placeholder' | transloco)\n }}</label>\n <input\n [id]=\"'shk-filter-' + col.field\"\n type=\"text\"\n [formControlName]=\"col.field\"\n class=\"w-full px-3 py-1.5 text-sm rounded-md border _shk-border dark:[border-color:var(--shk-surface-200)] bg-white dark:[background-color:var(--shk-surface-100)] _shk-text-surface-700 dark:[color:var(--shk-surface-700)] focus:[border-color:var(--shk-primary-500)] focus:ring-1 focus:[--tw-ring-color:var(--shk-primary-500)] placeholder:[color:var(--shk-surface-400)] dark:placeholder:[color:var(--shk-surface-500)]\"\n [placeholder]=\"\n col.filterPlaceholder || ('common.search_placeholder' | transloco)\n \"\n />\n }\n @if (col.filterType === 'select') {\n <label [for]=\"'shk-filter-' + col.field\" class=\"sr-only\">{{\n col.filterPlaceholder || ('common.search_placeholder' | transloco)\n }}</label>\n <select\n [id]=\"'shk-filter-' + col.field\"\n [formControlName]=\"col.field\"\n class=\"w-full px-2 py-1 text-xs rounded-md border _shk-border dark:[border-color:var(--shk-surface-200)] bg-white dark:[background-color:var(--shk-surface-100)] _shk-text-surface-700 dark:[color:var(--shk-surface-700)] focus:[border-color:var(--shk-primary-500)] focus:ring-1 focus:[--tw-ring-color:var(--shk-primary-500)] appearance-none\"\n >\n <option [ngValue]=\"null\">{{ 'table.filterAll' | transloco }}</option>\n @for (opt of col.filterOptions || []; track opt.value) {\n <option [ngValue]=\"opt.value\">{{ opt.label }}</option>\n }\n </select>\n }\n @if (hasFilterValue(col.field) && col.filterType !== 'select') {\n <button\n type=\"button\"\n class=\"absolute right-2 top-1/2 -translate-y-1/2 _shk-text-surface-400 dark:[color:var(--shk-surface-500)] hover:[color:var(--shk-surface-600)] dark:hover:[color:var(--shk-surface-300)]\"\n (click)=\"clearFilter(col.field)\"\n >\n <svg [lucideIcon]=\"getIcon('x')\" class=\"w-3.5 h-3.5\"></svg>\n </button>\n }\n </div>\n </div>\n }\n } @else {\n <div class=\"flex items-center justify-center\">\n <span>{{ col.header }}</span>\n </div>\n }\n </th>\n }\n </tr>\n </thead>\n @if (loading()) {\n <tbody class=\"border-b _shk-border dark:[border-color:var(--shk-surface-100)]\">\n <tr>\n <td [attr.colspan]=\"columnsWithActions().length\" class=\"text-center py-12\">\n <div\n class=\"flex items-center justify-center\"\n role=\"status\"\n [attr.aria-label]=\"'common.loading' | transloco\"\n >\n <svg\n [lucideIcon]=\"getIcon('loader')\"\n class=\"w-6 h-6 animate-spin _shk-text-primary\"\n ></svg>\n </div>\n </td>\n </tr>\n </tbody>\n } @else if (effectiveDisplayedData().length === 0) {\n <tbody class=\"border-b _shk-border dark:[border-color:var(--shk-surface-100)]\">\n <tr>\n <td [attr.colspan]=\"columnsWithActions().length\" class=\"text-center py-12\">\n <div class=\"flex flex-col items-center\">\n <div\n class=\"w-16 h-16 _shk-bg-surface-100 dark:[background-color:var(--shk-surface-100)] rounded-full flex items-center justify-center mb-4\"\n >\n <svg\n [lucideIcon]=\"getIcon('inbox')\"\n class=\"w-8 h-8 _shk-text-surface-400 dark:[color:var(--shk-surface-500)]\"\n ></svg>\n </div>\n <p\n class=\"text-lg font-semibold _shk-text-surface-600 dark:[color:var(--shk-surface-300)]\"\n >\n {{ emptyMessage() || ('common.no_records' | transloco) }}\n </p>\n </div>\n </td>\n </tr>\n </tbody>\n } @else {\n <tbody>\n @for (rowData of effectiveDisplayedData(); track trackByRow(rowData, $index)) {\n <tr\n class=\"hover:[background-color:color-mix(in srgb,var(--shk-primary-50)30%,transparent)] dark:hover:[background-color:color-mix(in srgb,var(--shk-primary-900)10%,transparent)] transition-colors duration-200 border-b _shk-border dark:[border-color:var(--shk-surface-100)]\"\n >\n @for (col of columnsWithActions(); track col.field) {\n <td\n [attr.data-shk-actions]=\"col.field === 'actions' ? '' : null\"\n class=\"px-2 min-[1440px]:px-3 py-2 min-[1440px]:py-2.5 text-xs font-medium _shk-text-surface-700 dark:[color:var(--shk-surface-700)] whitespace-nowrap\"\n [style.overflow]=\"col.field === 'actions' ? 'visible' : 'hidden'\"\n [style.text-overflow]=\"col.field === 'actions' ? 'clip' : 'ellipsis'\"\n [style.--shk-col-width]=\"col.field !== 'actions' ? getColumnWidth(col) : null\"\n >\n @if (col.field !== 'actions') {\n @if (col.template === 'tag') {\n <shk-tag\n [value]=\"getTagValue(col, rowData)\"\n [severity]=\"getTagSeverity(col, rowData)\"\n [rounded]=\"getTagRounded(col, rowData)\"\n [icon]=\"getTagIcon(col, rowData)\"\n [style]=\"getTagStyle(col, rowData)\"\n />\n } @else if (col.format) {\n <span>{{ col.format(rowData) }}</span>\n } @else {\n <ng-container\n *ngTemplateOutlet=\"\n customTemplates()[col.field] || defaultTemplate;\n context: { $implicit: rowData, field: col.field }\n \"\n ></ng-container>\n }\n } @else {\n <div class=\"flex items-center justify-start gap-px\">\n @for (action of rowActions(); track $index) {\n @if (isRowActionVisible(action, rowData)) {\n <div class=\"relative group\">\n <shk-tooltip\n [text]=\"getRowActionLabel(action, rowData)\"\n position=\"top\"\n >\n <button\n type=\"button\"\n (click)=\"action.onClick(rowData)\"\n [class]=\"\n 'table-action-btn hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)] rounded-md ' +\n getRowActionClass(action, rowData)\n \"\n [disabled]=\"isRowActionDisabled(action, rowData) || false\"\n [attr.aria-label]=\"getRowActionLabel(action, rowData)\"\n >\n <svg\n [lucideIcon]=\"getIcon(getRowActionIconName(action, rowData))\"\n class=\"w-4 h-4\"\n ></svg>\n </button>\n </shk-tooltip>\n </div>\n }\n }\n </div>\n }\n </td>\n }\n </tr>\n }\n </tbody>\n }\n </table>\n <ng-template #defaultTemplate let-rowData let-field=\"field\">\n {{ truncate(rowData[field], 30) }}\n </ng-template>\n </div>\n <div\n class=\"flex items-center justify-between gap-4 px-2 min-[1440px]:px-3 py-2 border-t _shk-border dark:[border-color:var(--shk-surface-100)]\"\n >\n <div class=\"text-xs _shk-text-surface-600 dark:[color:var(--shk-surface-400)]\">\n {{ pageReport() }}\n </div>\n <div\n class=\"flex items-center gap-0.5\"\n role=\"navigation\"\n >\n <button\n type=\"button\"\n (click)=\"goToFirst()\"\n [disabled]=\"isFirstPage()\"\n class=\"p-1 _shk-text-surface-500 hover:[color:var(--shk-surface-700)] dark:[color:var(--shk-surface-400)] dark:hover:[color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed rounded transition-colors\"\n >\n <svg [lucideIcon]=\"getIcon('chevrons-left')\" class=\"w-4 h-4\"></svg>\n </button>\n <button\n type=\"button\"\n (click)=\"prev()\"\n [disabled]=\"isFirstPage()\"\n class=\"p-1 _shk-text-surface-500 hover:[color:var(--shk-surface-700)] dark:[color:var(--shk-surface-400)] dark:hover:[color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed rounded transition-colors\"\n >\n <svg [lucideIcon]=\"getIcon('chevron-left')\" class=\"w-4 h-4\"></svg>\n </button>\n <span\n class=\"px-2 text-xs font-medium _shk-text-surface-700 dark:[color:var(--shk-surface-700)]\"\n >{{ currentPage() }} / {{ totalPages() }}</span\n >\n <button\n type=\"button\"\n (click)=\"next()\"\n [disabled]=\"isLastPage()\"\n class=\"p-1 _shk-text-surface-500 hover:[color:var(--shk-surface-700)] dark:[color:var(--shk-surface-400)] dark:hover:[color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed rounded transition-colors\"\n >\n <svg [lucideIcon]=\"getIcon('chevron-right')\" class=\"w-4 h-4\"></svg>\n </button>\n <button\n type=\"button\"\n (click)=\"goToLast()\"\n [disabled]=\"isLastPage()\"\n class=\"p-1 _shk-text-surface-500 hover:[color:var(--shk-surface-700)] dark:[color:var(--shk-surface-400)] dark:hover:[color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed rounded transition-colors\"\n >\n <svg [lucideIcon]=\"getIcon('chevrons-right')\" class=\"w-4 h-4\"></svg>\n </button>\n </div>\n <div\n class=\"flex items-center gap-1.5 text-xs _shk-text-surface-600 dark:[color:var(--shk-surface-400)]\"\n >\n <label [for]=\"'shk-rows-per-page'\">{{ 'table.rows' | transloco }}</label>\n <select\n id=\"shk-rows-per-page\"\n [value]=\"rowsPerPageLocal()\"\n (change)=\"onRowsPerPageChange($any($event.target).value)\"\n class=\"border _shk-border dark:[border-color:var(--shk-surface-200)] rounded px-2 py-1 text-xs bg-white dark:[background-color:var(--shk-surface-100)] _shk-text-surface-700 dark:[color:var(--shk-surface-700)] focus:outline-none focus:[border-color:var(--shk-primary-500)]\"\n >\n @for (opt of rowsPerPageOptions(); track opt) {\n <option [value]=\"opt\">{{ opt }}</option>\n }\n </select>\n </div>\n </div>\n </div>\n</div>\n", styles: ["input:focus{outline:none}input[type=text]{font-weight:400!important}table th,table td{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}td button svg{width:1.125rem!important;height:1.125rem!important}td button:disabled{opacity:.4;cursor:not-allowed}.table-action-btn svg{width:1rem!important;height:1rem!important;min-width:1rem!important;min-height:1rem!important}.table-action-btn{padding:.5rem!important;min-width:2rem!important;min-height:2rem!important;display:inline-flex!important;align-items:center!important;justify-content:center!important}.table-scroll-wrapper{width:100%;overflow-x:auto;overflow-y:hidden;border-radius:inherit;-webkit-overflow-scrolling:touch;container-type:inline-size}.table-scroll-wrapper table{table-layout:fixed;width:100%}@container (max-width: 768px){.table-scroll-wrapper{scroll-snap-type:x mandatory}.table-scroll-wrapper table{table-layout:auto;width:100%}.table-scroll-wrapper th:not([data-shk-actions]),.table-scroll-wrapper td:not([data-shk-actions]){min-width:var(--shk-col-width, 140px);max-width:var(--shk-col-width, 140px)}.table-scroll-wrapper th,.table-scroll-wrapper td{scroll-snap-align:start}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\n"] }]
2779
3063
  }], ctorParameters: () => [], propDecorators: { data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: false }] }], rowsPerPage: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowsPerPage", required: false }] }], rowsPerPageOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowsPerPageOptions", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], showActionRow: [{ type: i0.Input, args: [{ isSignal: true, alias: "showActionRow", required: false }] }], customTemplates: [{ type: i0.Input, args: [{ isSignal: true, alias: "customTemplates", required: false }] }], headerActions: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerActions", required: false }] }], rowActions: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowActions", required: false }] }], hasShadow: [{ type: i0.Input, args: [{ isSignal: true, alias: "hasShadow", required: false }] }], defaultSortField: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultSortField", required: false }] }], defaultSortOrder: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultSortOrder", required: false }] }], showSearch: [{ type: i0.Input, args: [{ isSignal: true, alias: "showSearch", required: false }] }], searchPlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchPlaceholder", required: false }] }], emptyMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyMessage", required: false }] }], serverSide: [{ type: i0.Input, args: [{ isSignal: true, alias: "serverSide", required: false }] }], totalRecords: [{ type: i0.Input, args: [{ isSignal: true, alias: "totalRecords", required: false }] }], filters: [{ type: i0.Input, args: [{ isSignal: true, alias: "filters", required: false }] }], activeFilter: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeFilter", required: false }] }], refresh: [{ type: i0.Output, args: ["refresh"] }], pageChange: [{ type: i0.Output, args: ["pageChange"] }], filterChange: [{ type: i0.Output, args: ["filterChange"] }], searchChange: [{ type: i0.Output, args: ["searchChange"] }], filterClick: [{ type: i0.Output, args: ["filterClick"] }] } });
2780
3064
 
2781
- class TooltipComponent {
2782
- constructor() {
2783
- this.text = input('', ...(ngDevMode ? [{ debugName: "text" }] : /* istanbul ignore next */ []));
2784
- this.position = input('bottom', ...(ngDevMode ? [{ debugName: "position" }] : /* istanbul ignore next */ []));
2785
- this.mode = input('hover', ...(ngDevMode ? [{ debugName: "mode" }] : /* istanbul ignore next */ []));
2786
- this.offset = input(8, ...(ngDevMode ? [{ debugName: "offset" }] : /* istanbul ignore next */ []));
2787
- this.elementRef = inject(ElementRef);
2788
- this.tooltipX = signal(0, ...(ngDevMode ? [{ debugName: "tooltipX" }] : /* istanbul ignore next */ []));
2789
- this.tooltipY = signal(0, ...(ngDevMode ? [{ debugName: "tooltipY" }] : /* istanbul ignore next */ []));
2790
- }
2791
- onMouseEnter() {
2792
- if (this.mode() !== 'fixed')
2793
- return;
2794
- const host = this.elementRef.nativeElement;
2795
- const container = host.querySelector('.group');
2796
- if (!container)
2797
- return;
2798
- const rect = container.getBoundingClientRect();
2799
- const off = this.offset();
2800
- if (this.position() === 'right') {
2801
- this.tooltipX.set(rect.right + off);
2802
- this.tooltipY.set(rect.top + rect.height / 2);
2803
- }
2804
- else if (this.position() === 'bottom') {
2805
- this.tooltipX.set(rect.left + rect.width / 2);
2806
- this.tooltipY.set(rect.bottom + off);
2807
- }
2808
- else {
2809
- this.tooltipX.set(rect.left + rect.width / 2);
2810
- this.tooltipY.set(rect.top - off);
2811
- }
2812
- }
2813
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: TooltipComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2814
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.18", type: TooltipComponent, isStandalone: true, selector: "shk-tooltip", inputs: { text: { classPropertyName: "text", publicName: "text", isSignal: true, isRequired: false, transformFunction: null }, position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, offset: { classPropertyName: "offset", publicName: "offset", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "mouseenter": "onMouseEnter()" } }, ngImport: i0, template: `
2815
- <div class="relative group inline-flex">
2816
- <ng-content></ng-content>
2817
- @if (text() && mode() === 'hover') {
2818
- <span
2819
- class="absolute px-2 py-1 text-xs _shk-bg-tooltip _shk-text-tooltip rounded whitespace-nowrap opacity-0 group-hover:opacity-100 transition pointer-events-none z-[9999] shadow-lg"
2820
- [class.top-full]="position() === 'bottom'"
2821
- [class.bottom-full]="position() === 'top'"
2822
- [class.left-full]="position() === 'right'"
2823
- [class.mt-1]="position() === 'bottom'"
2824
- [class.mb-1]="position() === 'top'"
2825
- [class.ml-1]="position() === 'right'"
2826
- [class.left-1/2]="position() !== 'right'"
2827
- [class.-translate-x-1/2]="position() !== 'right'"
2828
- [class.top-1/2]="position() === 'right'"
2829
- [class.-translate-y-1/2]="position() === 'right'"
2830
- >
2831
- {{ text() }}
2832
- </span>
2833
- }
2834
- @if (text() && mode() === 'fixed') {
2835
- <span
2836
- class="fixed px-2 py-1 text-xs _shk-bg-tooltip _shk-text-tooltip rounded whitespace-nowrap opacity-0 group-hover:opacity-100 transition pointer-events-none z-[9999] shadow-lg"
2837
- [style.top.px]="tooltipY()"
2838
- [style.left.px]="tooltipX()"
2839
- [style.transform]="position() === 'right' ? 'translateY(-50%)' : ''"
2840
- >
2841
- {{ text() }}
2842
- </span>
2843
- }
2844
- </div>
2845
- `, isInline: true }); }
2846
- }
2847
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: TooltipComponent, decorators: [{
2848
- type: Component,
2849
- args: [{
2850
- selector: 'shk-tooltip',
2851
- standalone: true,
2852
- template: `
2853
- <div class="relative group inline-flex">
2854
- <ng-content></ng-content>
2855
- @if (text() && mode() === 'hover') {
2856
- <span
2857
- class="absolute px-2 py-1 text-xs _shk-bg-tooltip _shk-text-tooltip rounded whitespace-nowrap opacity-0 group-hover:opacity-100 transition pointer-events-none z-[9999] shadow-lg"
2858
- [class.top-full]="position() === 'bottom'"
2859
- [class.bottom-full]="position() === 'top'"
2860
- [class.left-full]="position() === 'right'"
2861
- [class.mt-1]="position() === 'bottom'"
2862
- [class.mb-1]="position() === 'top'"
2863
- [class.ml-1]="position() === 'right'"
2864
- [class.left-1/2]="position() !== 'right'"
2865
- [class.-translate-x-1/2]="position() !== 'right'"
2866
- [class.top-1/2]="position() === 'right'"
2867
- [class.-translate-y-1/2]="position() === 'right'"
2868
- >
2869
- {{ text() }}
2870
- </span>
2871
- }
2872
- @if (text() && mode() === 'fixed') {
2873
- <span
2874
- class="fixed px-2 py-1 text-xs _shk-bg-tooltip _shk-text-tooltip rounded whitespace-nowrap opacity-0 group-hover:opacity-100 transition pointer-events-none z-[9999] shadow-lg"
2875
- [style.top.px]="tooltipY()"
2876
- [style.left.px]="tooltipX()"
2877
- [style.transform]="position() === 'right' ? 'translateY(-50%)' : ''"
2878
- >
2879
- {{ text() }}
2880
- </span>
2881
- }
2882
- </div>
2883
- `,
2884
- }]
2885
- }], propDecorators: { text: [{ type: i0.Input, args: [{ isSignal: true, alias: "text", required: false }] }], position: [{ type: i0.Input, args: [{ isSignal: true, alias: "position", required: false }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], offset: [{ type: i0.Input, args: [{ isSignal: true, alias: "offset", required: false }] }], onMouseEnter: [{
2886
- type: HostListener,
2887
- args: ['mouseenter']
2888
- }] } });
2889
-
2890
3065
  const ICONS$1 = {
2891
3066
  house: LucideHouse,
2892
3067
  home: LucideHome,
@@ -3829,6 +4004,1038 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImpo
3829
4004
  args: [{ selector: 'shk-wizard', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [CommonModule, LucideDynamicIcon], template: "<div class=\"min-h-full bg-transparent\">\n\n <!-- ============ Sticky Header + Stepper ============ -->\n @if (showHeader()) {\n <div\n class=\"sticky top-0 z-20 bg-white dark:[background-color:var(--shk-surface-50)] border-b _shk-border dark:[border-color:var(--shk-surface-200)] shadow-sm\"\n >\n <div class=\"w-full px-4 sm:px-6 lg:px-8 py-4\">\n <div class=\"flex items-center gap-4 mb-4\">\n @if (showBackButton()) {\n <button\n type=\"button\"\n (click)=\"back.emit()\"\n [attr.aria-label]=\"backTooltip()\"\n [attr.title]=\"backTooltip()\"\n class=\"shk-wizard-icon-btn shrink-0\"\n >\n <svg [lucideIcon]=\"getIcon('arrow-left')\" class=\"w-4 h-4\"></svg>\n </button>\n }\n <div class=\"flex-1 min-w-0 flex items-center gap-2.5\">\n @if (headerIcon()) {\n <span class=\"w-9 h-9 rounded-lg _shk-bg-primary-soft _shk-text-primary flex items-center justify-center shrink-0\">\n <svg [lucideIcon]=\"getIcon(headerIcon())\" class=\"w-4 h-4\"></svg>\n </span>\n }\n <div class=\"min-w-0\">\n <h1 class=\"text-xl font-bold _shk-text-surface-800 dark:[color:var(--shk-surface-800)] truncate\">\n {{ headerTitle() }}\n </h1>\n @if (headerSubtitle()) {\n <p class=\"text-xs _shk-text-surface-500 dark:[color:var(--shk-surface-500)] mt-0.5 truncate\">\n {{ headerSubtitle() }}\n </p>\n }\n </div>\n </div>\n <ng-content select=\"[shkWizardHeaderActions]\"></ng-content>\n </div>\n\n <!-- Stepper -->\n @if (showStepper() && steps().length > 1) {\n <div class=\"flex items-center gap-2 w-full max-w-3xl mx-auto\">\n @for (step of steps(); track step.id; let i = $index, last = $last) {\n <div class=\"flex items-center gap-3 shrink-0\">\n <button\n type=\"button\"\n (click)=\"goTo(step.id)\"\n [disabled]=\"!stepperClickable() || currentIndex() < i\"\n [attr.aria-label]=\"step.label\"\n class=\"shk-wizard-stepper-dot\"\n [class._shk-bg-primary]=\"currentIndex() > i || currentIndex() === i\"\n [class.text-white]=\"currentIndex() > i || currentIndex() === i\"\n [class._shk-bg-surface-200]=\"currentIndex() < i\"\n [class._shk-text-surface-500]=\"currentIndex() < i\"\n [class.cursor-pointer]=\"stepperClickable() && currentIndex() >= i\"\n [class.cursor-default]=\"!stepperClickable() || currentIndex() < i\"\n >\n @if (currentIndex() > i) {\n <svg [lucideIcon]=\"getIcon(step.icon || 'check')\" class=\"w-4 h-4\"></svg>\n } @else if (step.icon) {\n <svg [lucideIcon]=\"getIcon(step.icon)\" class=\"w-4 h-4\"></svg>\n } @else {\n {{ i + 1 }}\n }\n </button>\n <span\n class=\"text-sm font-semibold transition-colors whitespace-nowrap hidden sm:inline\"\n [class._shk-text-surface-800]=\"currentIndex() === i\"\n [class._shk-text-surface-500]=\"currentIndex() !== i\"\n [class.dark:[color:var(--shk-surface-800)]]=\"currentIndex() === i\"\n [class.dark:[color:var(--shk-surface-500)]]=\"currentIndex() !== i\"\n >\n {{ step.label }}\n </span>\n </div>\n\n @if (!last) {\n <div class=\"flex-1 h-0.5 _shk-bg-surface-200 dark:[background-color:var(--shk-surface-200)] relative overflow-hidden rounded-full\">\n <div\n class=\"absolute inset-y-0 left-0 _shk-bg-primary transition-all duration-300\"\n [class.w-full]=\"currentIndex() > i\"\n [class.w-0]=\"currentIndex() <= i\"\n ></div>\n </div>\n }\n }\n </div>\n }\n </div>\n </div>\n }\n\n <!-- ============ Active step content ============ -->\n <div class=\"w-full px-4 sm:px-6 lg:px-8 py-6 pb-28\">\n @if (currentIndex() >= 0) {\n @if (getStepTemplate(steps()[currentIndex()].id); as tpl) {\n <div class=\"shk-wizard-fade\" [style.view-transition-name]=\"'shk-wizard-step'\">\n <ng-container\n *ngTemplateOutlet=\"tpl; context: { step: currentStepObj(), index: currentIndex() }\"\n ></ng-container>\n </div>\n } @else {\n <p class=\"text-sm _shk-text-surface-400 dark:[color:var(--shk-surface-400)]\">\n No hay contenido definido para el paso \u00AB{{ steps()[currentIndex()].label }}\u00BB.\n </p>\n }\n }\n </div>\n\n <!-- ============ Sticky Footer ============ -->\n @if (showFooter() && steps().length > 0) {\n <div\n class=\"sticky bottom-0 z-20 bg-white dark:[background-color:var(--shk-surface-50)] border-t _shk-border dark:[border-color:var(--shk-surface-200)] shadow-[0_-4px_12px_rgba(0,0,0,0.04)] mt-8\"\n >\n <div class=\"w-full px-4 sm:px-6 lg:px-8 py-4 flex items-center justify-end gap-3\">\n @if (showStepCounter()) {\n <span class=\"text-sm _shk-text-surface-500 dark:[color:var(--shk-surface-500)] mr-auto\">\n {{ stepCounterPrefix() }} {{ currentIndex() + 1 }} / {{ steps().length }}\n </span>\n }\n\n @if (isFirstStep()) {\n @if (showCancel()) {\n <button type=\"button\" (click)=\"cancel.emit()\" class=\"shk-wizard-btn-secondary\">\n @if (cancelIcon()) {\n <svg [lucideIcon]=\"getIcon(cancelIcon())\" class=\"w-4 h-4\"></svg>\n }\n {{ cancelLabel() }}\n </button>\n }\n } @else {\n <button type=\"button\" (click)=\"onPrev()\" class=\"shk-wizard-btn-secondary\">\n <svg [lucideIcon]=\"getIcon(prevIcon())\" class=\"w-4 h-4\"></svg>\n {{ prevLabel() }}\n </button>\n }\n\n <button\n type=\"button\"\n (click)=\"onPrimary()\"\n [disabled]=\"primaryDisabled() || primaryLoading()\"\n class=\"shk-wizard-btn-primary\"\n >\n @if (primaryLoading()) {\n <svg [lucideIcon]=\"getIcon('loader-circle')\" class=\"w-4 h-4 shk-wizard-spin\"></svg>\n } @else {\n @if (primaryActionIcon()) {\n <svg [lucideIcon]=\"getIcon(primaryActionIcon())\" class=\"w-4 h-4\"></svg>\n }\n }\n {{ primaryActionLabel() }}\n </button>\n </div>\n </div>\n }\n</div>\n", styles: [":host{display:block}.shk-wizard-icon-btn{display:inline-flex;align-items:center;justify-content:center;width:2rem;height:2rem;border-radius:9999px;color:var(--shk-surface-600);transition:background-color .15s ease-in-out,color .15s ease-in-out}.shk-wizard-icon-btn:hover:not(:disabled){background-color:var(--shk-surface-100);color:var(--shk-surface-800)}.shk-wizard-icon-btn:disabled{cursor:default}.shk-wizard-icon-btn svg{width:1rem!important;height:1rem!important}.shk-wizard-stepper-dot{display:flex;align-items:center;justify-content:center;width:2.25rem;height:2.25rem;border-radius:9999px;font-size:.875rem;font-weight:700;flex-shrink:0;transition:background-color .15s ease-in-out,color .15s ease-in-out}.shk-wizard-stepper-dot svg{width:1rem!important;height:1rem!important}.shk-wizard-btn-secondary{display:inline-flex;align-items:center;gap:.5rem;padding:.5rem 1rem;border-radius:.5rem;font-size:.875rem;font-weight:500;color:var(--shk-surface-700);background-color:transparent;border:1px solid var(--shk-surface-200);transition:background-color .15s ease-in-out,border-color .15s ease-in-out,color .15s ease-in-out}.shk-wizard-btn-secondary:hover:not(:disabled){background-color:var(--shk-surface-100);border-color:var(--shk-surface-300);color:var(--shk-surface-900)}.shk-wizard-btn-secondary:disabled{opacity:.5;cursor:not-allowed}.shk-wizard-btn-secondary svg{width:1rem!important;height:1rem!important}.shk-wizard-btn-primary{display:inline-flex;align-items:center;gap:.5rem;padding:.5rem 1rem;border-radius:.5rem;font-size:.875rem;font-weight:500;color:#fff;background-color:var(--shk-primary-600);box-shadow:0 6px 20px -6px color-mix(in srgb,var(--shk-primary-600) 55%,transparent);transition:background-color .15s ease-in-out,opacity .15s ease-in-out}.shk-wizard-btn-primary:hover:not(:disabled){background-color:var(--shk-primary-700)}.shk-wizard-btn-primary:disabled{opacity:.5;cursor:not-allowed;box-shadow:none}.shk-wizard-btn-primary svg{width:1rem!important;height:1rem!important}.shk-wizard-spin{animation:shk-wizard-spin .8s linear infinite}@keyframes shk-wizard-spin{to{transform:rotate(360deg)}}.shk-wizard-fade{animation:shk-wizard-fade .25s ease-out}@keyframes shk-wizard-fade{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}\n"] }]
3830
4005
  }], propDecorators: { steps: [{ type: i0.Input, args: [{ isSignal: true, alias: "steps", required: false }] }], currentStep: [{ type: i0.Input, args: [{ isSignal: true, alias: "currentStep", required: false }] }, { type: i0.Output, args: ["currentStepChange"] }], showHeader: [{ type: i0.Input, args: [{ isSignal: true, alias: "showHeader", required: false }] }], headerTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerTitle", required: false }] }], headerSubtitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerSubtitle", required: false }] }], showBackButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "showBackButton", required: false }] }], backTooltip: [{ type: i0.Input, args: [{ isSignal: true, alias: "backTooltip", required: false }] }], headerIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerIcon", required: false }] }], showStepper: [{ type: i0.Input, args: [{ isSignal: true, alias: "showStepper", required: false }] }], stepperClickable: [{ type: i0.Input, args: [{ isSignal: true, alias: "stepperClickable", required: false }] }], showFooter: [{ type: i0.Input, args: [{ isSignal: true, alias: "showFooter", required: false }] }], showStepCounter: [{ type: i0.Input, args: [{ isSignal: true, alias: "showStepCounter", required: false }] }], stepCounterPrefix: [{ type: i0.Input, args: [{ isSignal: true, alias: "stepCounterPrefix", required: false }] }], showCancel: [{ type: i0.Input, args: [{ isSignal: true, alias: "showCancel", required: false }] }], advanceOnNext: [{ type: i0.Input, args: [{ isSignal: true, alias: "advanceOnNext", required: false }] }], prevLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "prevLabel", required: false }] }], prevIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "prevIcon", required: false }] }], cancelLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "cancelLabel", required: false }] }], cancelIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "cancelIcon", required: false }] }], nextLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "nextLabel", required: false }] }], nextIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "nextIcon", required: false }] }], finishLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "finishLabel", required: false }] }], finishIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "finishIcon", required: false }] }], primaryLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "primaryLabel", required: false }] }], primaryIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "primaryIcon", required: false }] }], primaryLoading: [{ type: i0.Input, args: [{ isSignal: true, alias: "primaryLoading", required: false }] }], primaryDisabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "primaryDisabled", required: false }] }], back: [{ type: i0.Output, args: ["back"] }], cancel: [{ type: i0.Output, args: ["cancel"] }], prev: [{ type: i0.Output, args: ["prev"] }], next: [{ type: i0.Output, args: ["next"] }], finish: [{ type: i0.Output, args: ["finish"] }], stepChange: [{ type: i0.Output, args: ["stepChange"] }], stepContents: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => ShkWizardStepContent), { isSignal: true }] }] } });
3831
4006
 
4007
+ /** Placeholder block shown while content is loading. */
4008
+ class SkeletonComponent {
4009
+ constructor() {
4010
+ /** Shape of the skeleton: rectangle (default) or circle. */
4011
+ this.shape = input('rectangle', ...(ngDevMode ? [{ debugName: "shape" }] : /* istanbul ignore next */ []));
4012
+ /** When shape is circle, sets both width and height. */
4013
+ this.size = input('', ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
4014
+ /** Width of the rectangle skeleton. */
4015
+ this.width = input('100%', ...(ngDevMode ? [{ debugName: "width" }] : /* istanbul ignore next */ []));
4016
+ /** Height of the rectangle skeleton. */
4017
+ this.height = input('1rem', ...(ngDevMode ? [{ debugName: "height" }] : /* istanbul ignore next */ []));
4018
+ /** Custom border radius (rectangle only). */
4019
+ this.borderRadius = input('', ...(ngDevMode ? [{ debugName: "borderRadius" }] : /* istanbul ignore next */ []));
4020
+ /** Animation effect: 'wave' (default), 'pulse' or 'none'. */
4021
+ this.animation = input('wave', ...(ngDevMode ? [{ debugName: "animation" }] : /* istanbul ignore next */ []));
4022
+ /** Inline styles applied to the skeleton block. */
4023
+ this.style = input({}, ...(ngDevMode ? [{ debugName: "style" }] : /* istanbul ignore next */ []));
4024
+ /** Extra CSS classes. */
4025
+ this.styleClass = input('', ...(ngDevMode ? [{ debugName: "styleClass" }] : /* istanbul ignore next */ []));
4026
+ this.rootStyle = computed(() => {
4027
+ const s = {};
4028
+ if (this.shape() === 'circle') {
4029
+ s['width'] = this.size() || this.width();
4030
+ s['height'] = this.size() || this.height();
4031
+ s['borderRadius'] = '9999px';
4032
+ }
4033
+ else {
4034
+ s['width'] = this.width();
4035
+ s['height'] = this.height();
4036
+ s['borderRadius'] = this.borderRadius() || '0.25rem';
4037
+ }
4038
+ return { ...s, ...this.style() };
4039
+ }, ...(ngDevMode ? [{ debugName: "rootStyle" }] : /* istanbul ignore next */ []));
4040
+ }
4041
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: SkeletonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4042
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.18", type: SkeletonComponent, isStandalone: true, selector: "shk-skeleton", inputs: { shape: { classPropertyName: "shape", publicName: "shape", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, borderRadius: { classPropertyName: "borderRadius", publicName: "borderRadius", isSignal: true, isRequired: false, transformFunction: null }, animation: { classPropertyName: "animation", publicName: "animation", isSignal: true, isRequired: false, transformFunction: null }, style: { classPropertyName: "style", publicName: "style", isSignal: true, isRequired: false, transformFunction: null }, styleClass: { classPropertyName: "styleClass", publicName: "styleClass", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
4043
+ <div
4044
+ [class]="'shk-skeleton ' + styleClass()"
4045
+ [class.shk-skeleton-circle]="shape() === 'circle'"
4046
+ [class.shk-skeleton-animate-wave]="animation() === 'wave'"
4047
+ [class.shk-skeleton-animate-pulse]="animation() === 'pulse'"
4048
+ [style]="rootStyle()"
4049
+ aria-hidden="true"
4050
+ ></div>
4051
+ `, isInline: true, styles: [":host{display:block}.shk-skeleton{position:relative;overflow:hidden;background-color:var(--shk-skeleton-bg)}.shk-skeleton-circle{border-radius:9999px}.shk-skeleton-animate-wave:after{content:\"\";position:absolute;inset:0;background-image:linear-gradient(90deg,transparent,var(--shk-skeleton-highlight) 50%,transparent);animation:shk-skeleton-wave 1.6s ease-in-out infinite}.shk-skeleton-animate-pulse{animation:shk-skeleton-pulse 2s ease-in-out infinite}@keyframes shk-skeleton-wave{0%{transform:translate(-100%)}to{transform:translate(100%)}}@keyframes shk-skeleton-pulse{0%,to{opacity:1}50%{opacity:.55}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
4052
+ }
4053
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: SkeletonComponent, decorators: [{
4054
+ type: Component,
4055
+ args: [{ selector: 'shk-skeleton', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: `
4056
+ <div
4057
+ [class]="'shk-skeleton ' + styleClass()"
4058
+ [class.shk-skeleton-circle]="shape() === 'circle'"
4059
+ [class.shk-skeleton-animate-wave]="animation() === 'wave'"
4060
+ [class.shk-skeleton-animate-pulse]="animation() === 'pulse'"
4061
+ [style]="rootStyle()"
4062
+ aria-hidden="true"
4063
+ ></div>
4064
+ `, styles: [":host{display:block}.shk-skeleton{position:relative;overflow:hidden;background-color:var(--shk-skeleton-bg)}.shk-skeleton-circle{border-radius:9999px}.shk-skeleton-animate-wave:after{content:\"\";position:absolute;inset:0;background-image:linear-gradient(90deg,transparent,var(--shk-skeleton-highlight) 50%,transparent);animation:shk-skeleton-wave 1.6s ease-in-out infinite}.shk-skeleton-animate-pulse{animation:shk-skeleton-pulse 2s ease-in-out infinite}@keyframes shk-skeleton-wave{0%{transform:translate(-100%)}to{transform:translate(100%)}}@keyframes shk-skeleton-pulse{0%,to{opacity:1}50%{opacity:.55}}\n"] }]
4065
+ }], propDecorators: { shape: [{ type: i0.Input, args: [{ isSignal: true, alias: "shape", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }], borderRadius: [{ type: i0.Input, args: [{ isSignal: true, alias: "borderRadius", required: false }] }], animation: [{ type: i0.Input, args: [{ isSignal: true, alias: "animation", required: false }] }], style: [{ type: i0.Input, args: [{ isSignal: true, alias: "style", required: false }] }], styleClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "styleClass", required: false }] }] } });
4066
+
4067
+ /** Generic button with icon, severity variants, loading state and sizes. */
4068
+ class ButtonComponent {
4069
+ constructor() {
4070
+ /** Text shown inside the button. */
4071
+ this.label = input('', ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
4072
+ /** Optional leading lucide icon name. */
4073
+ this.icon = input('', ...(ngDevMode ? [{ debugName: "icon" }] : /* istanbul ignore next */ []));
4074
+ /** Position of the icon relative to the label. */
4075
+ this.iconPos = input('left', ...(ngDevMode ? [{ debugName: "iconPos" }] : /* istanbul ignore next */ []));
4076
+ /** Color variant. Defaults to 'primary'. */
4077
+ this.severity = input('primary', ...(ngDevMode ? [{ debugName: "severity" }] : /* istanbul ignore next */ []));
4078
+ /** Transparent background with a colored border. */
4079
+ this.outlined = input(false, ...(ngDevMode ? [{ debugName: "outlined" }] : /* istanbul ignore next */ []));
4080
+ /** Borderless button with a colored label. */
4081
+ this.text = input(false, ...(ngDevMode ? [{ debugName: "text" }] : /* istanbul ignore next */ []));
4082
+ /** Adds a soft shadow. */
4083
+ this.raised = input(false, ...(ngDevMode ? [{ debugName: "raised" }] : /* istanbul ignore next */ []));
4084
+ /** Fully rounded corners (pill). */
4085
+ this.rounded = input(false, ...(ngDevMode ? [{ debugName: "rounded" }] : /* istanbul ignore next */ []));
4086
+ /** Size variant. */
4087
+ this.size = input(undefined, ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
4088
+ /** Disables the button. */
4089
+ this.disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
4090
+ /** Shows a spinner and disables the button. */
4091
+ this.loading = input(false, ...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
4092
+ /** Icon shown while loading. Defaults to 'refresh'. */
4093
+ this.loadingIcon = input('refresh', ...(ngDevMode ? [{ debugName: "loadingIcon" }] : /* istanbul ignore next */ []));
4094
+ /** Optional text shown instead of the label while loading. */
4095
+ this.loadingText = input('', ...(ngDevMode ? [{ debugName: "loadingText" }] : /* istanbul ignore next */ []));
4096
+ /** Native button type. */
4097
+ this.type = input('button', ...(ngDevMode ? [{ debugName: "type" }] : /* istanbul ignore next */ []));
4098
+ /** Accessible label when the button is icon-only. */
4099
+ this.ariaLabel = input('', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
4100
+ /** Native title tooltip. */
4101
+ this.title = input('', ...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
4102
+ /** Inline styles applied to the button. */
4103
+ this.style = input({}, ...(ngDevMode ? [{ debugName: "style" }] : /* istanbul ignore next */ []));
4104
+ /** Extra CSS classes. */
4105
+ this.styleClass = input('', ...(ngDevMode ? [{ debugName: "styleClass" }] : /* istanbul ignore next */ []));
4106
+ /** Emitted on click with the original MouseEvent. */
4107
+ this.onClick = output();
4108
+ this.getIcon = getIcon$6;
4109
+ this.iconOnly = computed(() => !this.label() && !!this.icon(), ...(ngDevMode ? [{ debugName: "iconOnly" }] : /* istanbul ignore next */ []));
4110
+ }
4111
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: ButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4112
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.18", type: ButtonComponent, isStandalone: true, selector: "shk-button", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, iconPos: { classPropertyName: "iconPos", publicName: "iconPos", isSignal: true, isRequired: false, transformFunction: null }, severity: { classPropertyName: "severity", publicName: "severity", isSignal: true, isRequired: false, transformFunction: null }, outlined: { classPropertyName: "outlined", publicName: "outlined", isSignal: true, isRequired: false, transformFunction: null }, text: { classPropertyName: "text", publicName: "text", isSignal: true, isRequired: false, transformFunction: null }, raised: { classPropertyName: "raised", publicName: "raised", isSignal: true, isRequired: false, transformFunction: null }, rounded: { classPropertyName: "rounded", publicName: "rounded", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, loadingIcon: { classPropertyName: "loadingIcon", publicName: "loadingIcon", isSignal: true, isRequired: false, transformFunction: null }, loadingText: { classPropertyName: "loadingText", publicName: "loadingText", isSignal: true, isRequired: false, transformFunction: null }, type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, style: { classPropertyName: "style", publicName: "style", isSignal: true, isRequired: false, transformFunction: null }, styleClass: { classPropertyName: "styleClass", publicName: "styleClass", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onClick: "onClick" }, ngImport: i0, template: `
4113
+ <button
4114
+ [attr.type]="type()"
4115
+ class="shk-button"
4116
+ [class.shk-button-primary]="severity() === 'primary' || !severity()"
4117
+ [class.shk-button-secondary]="severity() === 'secondary'"
4118
+ [class.shk-button-success]="severity() === 'success'"
4119
+ [class.shk-button-info]="severity() === 'info'"
4120
+ [class.shk-button-warning]="severity() === 'warning'"
4121
+ [class.shk-button-danger]="severity() === 'danger'"
4122
+ [class.shk-button-help]="severity() === 'help'"
4123
+ [class.shk-button-contrast]="severity() === 'contrast'"
4124
+ [class.shk-button-outlined]="outlined()"
4125
+ [class.shk-button-text]="text()"
4126
+ [class.shk-button-raised]="raised()"
4127
+ [class.shk-button-rounded]="rounded()"
4128
+ [class.shk-button-sm]="size() === 'small'"
4129
+ [class.shk-button-lg]="size() === 'large'"
4130
+ [class.shk-button-icon-right]="iconPos() === 'right'"
4131
+ [class.shk-button-icon-top]="iconPos() === 'top'"
4132
+ [class.shk-button-icon-bottom]="iconPos() === 'bottom'"
4133
+ [class.shk-button-icon-only]="iconOnly()"
4134
+ [class.shk-button-loading]="loading()"
4135
+ [class]="styleClass()"
4136
+ [style]="style()"
4137
+ [disabled]="disabled() || loading()"
4138
+ [attr.aria-label]="ariaLabel() || undefined"
4139
+ [attr.title]="title() || undefined"
4140
+ (click)="onClick.emit($event)"
4141
+ >
4142
+ @if (loading()) {
4143
+ <svg [lucideIcon]="getIcon(loadingIcon())" class="shk-button-icon shk-button-spin"></svg>
4144
+ } @else if (icon()) {
4145
+ <svg [lucideIcon]="getIcon(icon())" class="shk-button-icon"></svg>
4146
+ }
4147
+ @if (label()) {
4148
+ <span>{{ loading() && loadingText() ? loadingText() : label() }}</span>
4149
+ }
4150
+ <ng-content></ng-content>
4151
+ </button>
4152
+ `, isInline: true, styles: [":host{display:inline-block}.shk-button{display:inline-flex;align-items:center;justify-content:center;gap:.5rem;padding:.5rem 1rem;border-radius:.5rem;border:1px solid transparent;font-size:.875rem;font-weight:500;line-height:1.25rem;cursor:pointer;-webkit-user-select:none;user-select:none;white-space:nowrap;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,color .15s ease-in-out,box-shadow .15s ease-in-out,opacity .15s ease-in-out}.shk-button:focus-visible{outline:2px solid var(--shk-primary-400);outline-offset:2px}.shk-button:disabled{opacity:.55;cursor:not-allowed}.shk-button-icon{width:1rem;height:1rem;flex-shrink:0}.shk-button-primary{--shk-btn-bg: var(--shk-primary-600);--shk-btn-color: #ffffff;--shk-btn-border: var(--shk-primary-600)}.shk-button-secondary{--shk-btn-bg: var(--shk-surface-100);--shk-btn-color: var(--shk-surface-700);--shk-btn-border: var(--shk-surface-200)}.shk-button-success{--shk-btn-bg: var(--shk-success-600);--shk-btn-color: #ffffff;--shk-btn-border: var(--shk-success-600)}.shk-button-info{--shk-btn-bg: var(--shk-info-600);--shk-btn-color: #ffffff;--shk-btn-border: var(--shk-info-600)}.shk-button-warning{--shk-btn-bg: var(--shk-warning-600);--shk-btn-color: #ffffff;--shk-btn-border: var(--shk-warning-600)}.shk-button-danger{--shk-btn-bg: var(--shk-error-600);--shk-btn-color: #ffffff;--shk-btn-border: var(--shk-error-600)}.shk-button-help{--shk-btn-bg: var(--shk-primary-500);--shk-btn-color: #ffffff;--shk-btn-border: var(--shk-primary-500)}.shk-button-contrast{--shk-btn-bg: var(--shk-surface-900);--shk-btn-color: var(--shk-surface-50);--shk-btn-border: var(--shk-surface-900)}.shk-button{background-color:var(--shk-btn-bg);color:var(--shk-btn-color);border-color:var(--shk-btn-border)}.shk-button:hover:not(:disabled){background-color:color-mix(in srgb,var(--shk-btn-bg) 88%,#000000)}.shk-button-outlined,.shk-button-text{background-color:transparent}.shk-button-outlined{border-color:var(--shk-btn-color)}.shk-button-text{border-color:transparent}.shk-button-text:hover:not(:disabled),.shk-button-outlined:hover:not(:disabled){background-color:color-mix(in srgb,var(--shk-btn-color) 12%,transparent)}.shk-button-raised{box-shadow:0 1px 2px #0000000f,0 4px 8px #0000001a}.shk-button-rounded{border-radius:9999px}.shk-button-sm{padding:.375rem .75rem;font-size:.8125rem}.shk-button-sm .shk-button-icon{width:.875rem;height:.875rem}.shk-button-lg{padding:.75rem 1.5rem;font-size:1rem}.shk-button-lg .shk-button-icon{width:1.25rem;height:1.25rem}.shk-button-icon-only{padding:.5rem}.shk-button-sm.shk-button-icon-only{padding:.375rem}.shk-button-lg.shk-button-icon-only{padding:.75rem}.shk-button-icon-right{flex-direction:row-reverse}.shk-button-icon-top{flex-direction:column}.shk-button-icon-bottom{flex-direction:column-reverse}.shk-button-loading .shk-button-spin{animation:shk-button-spin 1s linear infinite}@keyframes shk-button-spin{to{transform:rotate(360deg)}}\n"], dependencies: [{ kind: "component", type: LucideDynamicIcon, selector: "svg[lucideIcon]", inputs: ["lucideIcon"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
4153
+ }
4154
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: ButtonComponent, decorators: [{
4155
+ type: Component,
4156
+ args: [{ selector: 'shk-button', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [LucideDynamicIcon], template: `
4157
+ <button
4158
+ [attr.type]="type()"
4159
+ class="shk-button"
4160
+ [class.shk-button-primary]="severity() === 'primary' || !severity()"
4161
+ [class.shk-button-secondary]="severity() === 'secondary'"
4162
+ [class.shk-button-success]="severity() === 'success'"
4163
+ [class.shk-button-info]="severity() === 'info'"
4164
+ [class.shk-button-warning]="severity() === 'warning'"
4165
+ [class.shk-button-danger]="severity() === 'danger'"
4166
+ [class.shk-button-help]="severity() === 'help'"
4167
+ [class.shk-button-contrast]="severity() === 'contrast'"
4168
+ [class.shk-button-outlined]="outlined()"
4169
+ [class.shk-button-text]="text()"
4170
+ [class.shk-button-raised]="raised()"
4171
+ [class.shk-button-rounded]="rounded()"
4172
+ [class.shk-button-sm]="size() === 'small'"
4173
+ [class.shk-button-lg]="size() === 'large'"
4174
+ [class.shk-button-icon-right]="iconPos() === 'right'"
4175
+ [class.shk-button-icon-top]="iconPos() === 'top'"
4176
+ [class.shk-button-icon-bottom]="iconPos() === 'bottom'"
4177
+ [class.shk-button-icon-only]="iconOnly()"
4178
+ [class.shk-button-loading]="loading()"
4179
+ [class]="styleClass()"
4180
+ [style]="style()"
4181
+ [disabled]="disabled() || loading()"
4182
+ [attr.aria-label]="ariaLabel() || undefined"
4183
+ [attr.title]="title() || undefined"
4184
+ (click)="onClick.emit($event)"
4185
+ >
4186
+ @if (loading()) {
4187
+ <svg [lucideIcon]="getIcon(loadingIcon())" class="shk-button-icon shk-button-spin"></svg>
4188
+ } @else if (icon()) {
4189
+ <svg [lucideIcon]="getIcon(icon())" class="shk-button-icon"></svg>
4190
+ }
4191
+ @if (label()) {
4192
+ <span>{{ loading() && loadingText() ? loadingText() : label() }}</span>
4193
+ }
4194
+ <ng-content></ng-content>
4195
+ </button>
4196
+ `, styles: [":host{display:inline-block}.shk-button{display:inline-flex;align-items:center;justify-content:center;gap:.5rem;padding:.5rem 1rem;border-radius:.5rem;border:1px solid transparent;font-size:.875rem;font-weight:500;line-height:1.25rem;cursor:pointer;-webkit-user-select:none;user-select:none;white-space:nowrap;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,color .15s ease-in-out,box-shadow .15s ease-in-out,opacity .15s ease-in-out}.shk-button:focus-visible{outline:2px solid var(--shk-primary-400);outline-offset:2px}.shk-button:disabled{opacity:.55;cursor:not-allowed}.shk-button-icon{width:1rem;height:1rem;flex-shrink:0}.shk-button-primary{--shk-btn-bg: var(--shk-primary-600);--shk-btn-color: #ffffff;--shk-btn-border: var(--shk-primary-600)}.shk-button-secondary{--shk-btn-bg: var(--shk-surface-100);--shk-btn-color: var(--shk-surface-700);--shk-btn-border: var(--shk-surface-200)}.shk-button-success{--shk-btn-bg: var(--shk-success-600);--shk-btn-color: #ffffff;--shk-btn-border: var(--shk-success-600)}.shk-button-info{--shk-btn-bg: var(--shk-info-600);--shk-btn-color: #ffffff;--shk-btn-border: var(--shk-info-600)}.shk-button-warning{--shk-btn-bg: var(--shk-warning-600);--shk-btn-color: #ffffff;--shk-btn-border: var(--shk-warning-600)}.shk-button-danger{--shk-btn-bg: var(--shk-error-600);--shk-btn-color: #ffffff;--shk-btn-border: var(--shk-error-600)}.shk-button-help{--shk-btn-bg: var(--shk-primary-500);--shk-btn-color: #ffffff;--shk-btn-border: var(--shk-primary-500)}.shk-button-contrast{--shk-btn-bg: var(--shk-surface-900);--shk-btn-color: var(--shk-surface-50);--shk-btn-border: var(--shk-surface-900)}.shk-button{background-color:var(--shk-btn-bg);color:var(--shk-btn-color);border-color:var(--shk-btn-border)}.shk-button:hover:not(:disabled){background-color:color-mix(in srgb,var(--shk-btn-bg) 88%,#000000)}.shk-button-outlined,.shk-button-text{background-color:transparent}.shk-button-outlined{border-color:var(--shk-btn-color)}.shk-button-text{border-color:transparent}.shk-button-text:hover:not(:disabled),.shk-button-outlined:hover:not(:disabled){background-color:color-mix(in srgb,var(--shk-btn-color) 12%,transparent)}.shk-button-raised{box-shadow:0 1px 2px #0000000f,0 4px 8px #0000001a}.shk-button-rounded{border-radius:9999px}.shk-button-sm{padding:.375rem .75rem;font-size:.8125rem}.shk-button-sm .shk-button-icon{width:.875rem;height:.875rem}.shk-button-lg{padding:.75rem 1.5rem;font-size:1rem}.shk-button-lg .shk-button-icon{width:1.25rem;height:1.25rem}.shk-button-icon-only{padding:.5rem}.shk-button-sm.shk-button-icon-only{padding:.375rem}.shk-button-lg.shk-button-icon-only{padding:.75rem}.shk-button-icon-right{flex-direction:row-reverse}.shk-button-icon-top{flex-direction:column}.shk-button-icon-bottom{flex-direction:column-reverse}.shk-button-loading .shk-button-spin{animation:shk-button-spin 1s linear infinite}@keyframes shk-button-spin{to{transform:rotate(360deg)}}\n"] }]
4197
+ }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], iconPos: [{ type: i0.Input, args: [{ isSignal: true, alias: "iconPos", required: false }] }], severity: [{ type: i0.Input, args: [{ isSignal: true, alias: "severity", required: false }] }], outlined: [{ type: i0.Input, args: [{ isSignal: true, alias: "outlined", required: false }] }], text: [{ type: i0.Input, args: [{ isSignal: true, alias: "text", required: false }] }], raised: [{ type: i0.Input, args: [{ isSignal: true, alias: "raised", required: false }] }], rounded: [{ type: i0.Input, args: [{ isSignal: true, alias: "rounded", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], loadingIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "loadingIcon", required: false }] }], loadingText: [{ type: i0.Input, args: [{ isSignal: true, alias: "loadingText", required: false }] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], style: [{ type: i0.Input, args: [{ isSignal: true, alias: "style", required: false }] }], styleClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "styleClass", required: false }] }], onClick: [{ type: i0.Output, args: ["onClick"] }] } });
4198
+
4199
+ /** SVG radial knob with drag and keyboard support. Implements ControlValueAccessor. */
4200
+ class KnobComponent {
4201
+ constructor() {
4202
+ /** Current value. Two-way bindable with [(value)]. */
4203
+ this.value = model(0, ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
4204
+ /** Minimum value. */
4205
+ this.min = input(0, ...(ngDevMode ? [{ debugName: "min" }] : /* istanbul ignore next */ []));
4206
+ /** Maximum value. */
4207
+ this.max = input(100, ...(ngDevMode ? [{ debugName: "max" }] : /* istanbul ignore next */ []));
4208
+ /** Increment used by arrows and pointer drag rounding. */
4209
+ this.step = input(1, ...(ngDevMode ? [{ debugName: "step" }] : /* istanbul ignore next */ []));
4210
+ /** Diameter of the knob in px. */
4211
+ this.size = input(100, ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
4212
+ /** Thickness of the arc stroke. */
4213
+ this.strokeWidth = input(14, ...(ngDevMode ? [{ debugName: "strokeWidth" }] : /* istanbul ignore next */ []));
4214
+ /** Color of the filled arc. */
4215
+ this.valueColor = input('var(--shk-primary-600)', ...(ngDevMode ? [{ debugName: "valueColor" }] : /* istanbul ignore next */ []));
4216
+ /** Color of the track arc. */
4217
+ this.rangeColor = input('var(--shk-surface-200)', ...(ngDevMode ? [{ debugName: "rangeColor" }] : /* istanbul ignore next */ []));
4218
+ /** Whether the current value is shown in the center. */
4219
+ this.showValue = input(true, ...(ngDevMode ? [{ debugName: "showValue" }] : /* istanbul ignore next */ []));
4220
+ /** Disables user interaction. */
4221
+ this.disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
4222
+ /** Blocks editing but keeps the control styled as enabled. */
4223
+ this.readonly = input(false, ...(ngDevMode ? [{ debugName: "readonly" }] : /* istanbul ignore next */ []));
4224
+ /** Accessible label. */
4225
+ this.ariaLabel = input('', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
4226
+ /** Emitted when the knob value changes. */
4227
+ this.change = output();
4228
+ /** Emitted when the user releases the pointer. */
4229
+ this.end = output();
4230
+ this.knobSvg = viewChild('knobSvg', ...(ngDevMode ? [{ debugName: "knobSvg" }] : /* istanbul ignore next */ []));
4231
+ this.onChange = () => { };
4232
+ this.onTouched = () => { };
4233
+ this.dragging = false;
4234
+ this.formDisabled = signal(false, ...(ngDevMode ? [{ debugName: "formDisabled" }] : /* istanbul ignore next */ []));
4235
+ /** Effective disabled state combining the `disabled` input and the form binding. */
4236
+ this.effectiveDisabled = computed(() => this.disabled() || this.formDisabled(), ...(ngDevMode ? [{ debugName: "effectiveDisabled" }] : /* istanbul ignore next */ []));
4237
+ this.r = computed(() => (this.size() - this.strokeWidth()) / 2, ...(ngDevMode ? [{ debugName: "r" }] : /* istanbul ignore next */ []));
4238
+ this.center = computed(() => this.size() / 2, ...(ngDevMode ? [{ debugName: "center" }] : /* istanbul ignore next */ []));
4239
+ this.fraction = computed(() => {
4240
+ const range = this.max() - this.min();
4241
+ if (range <= 0)
4242
+ return 0;
4243
+ return Math.max(0, Math.min(1, (this.value() - this.min()) / range));
4244
+ }, ...(ngDevMode ? [{ debugName: "fraction" }] : /* istanbul ignore next */ []));
4245
+ this.rangeArc = computed(() => this.getArcPath(135, 405), ...(ngDevMode ? [{ debugName: "rangeArc" }] : /* istanbul ignore next */ []));
4246
+ this.valueArc = computed(() => this.getArcPath(135, 135 + 270 * this.fraction()), ...(ngDevMode ? [{ debugName: "valueArc" }] : /* istanbul ignore next */ []));
4247
+ }
4248
+ writeValue(value) {
4249
+ this.value.set(this.clamp(Number(value) || this.min()));
4250
+ }
4251
+ registerOnChange(fn) {
4252
+ this.onChange = fn;
4253
+ }
4254
+ registerOnTouched(fn) {
4255
+ this.onTouched = fn;
4256
+ }
4257
+ setDisabledState(isDisabled) {
4258
+ this.formDisabled.set(isDisabled);
4259
+ }
4260
+ onPointerDown(event) {
4261
+ if (this.effectiveDisabled() || this.readonly())
4262
+ return;
4263
+ this.dragging = true;
4264
+ this.knobSvg()?.nativeElement.setPointerCapture(event.pointerId);
4265
+ this.applyEvent(event);
4266
+ }
4267
+ onPointerMove(event) {
4268
+ if (!this.dragging)
4269
+ return;
4270
+ this.applyEvent(event);
4271
+ }
4272
+ onPointerUp(event) {
4273
+ if (!this.dragging)
4274
+ return;
4275
+ this.dragging = false;
4276
+ try {
4277
+ this.knobSvg()?.nativeElement.releasePointerCapture(event.pointerId);
4278
+ }
4279
+ catch {
4280
+ /* noop */
4281
+ }
4282
+ this.change.emit(this.value());
4283
+ this.end.emit(this.value());
4284
+ }
4285
+ updateByKey(step) {
4286
+ if (this.effectiveDisabled() || this.readonly())
4287
+ return;
4288
+ this.setValue(this.value() + step);
4289
+ this.onTouched();
4290
+ }
4291
+ applyEvent(event) {
4292
+ const svg = this.knobSvg()?.nativeElement;
4293
+ if (!svg)
4294
+ return;
4295
+ const rect = svg.getBoundingClientRect();
4296
+ const cx = rect.left + rect.width / 2;
4297
+ const cy = rect.top + rect.height / 2;
4298
+ const angle = Math.atan2(event.clientY - cy, event.clientX - cx);
4299
+ const deg = ((angle * 180) / Math.PI + 360) % 360;
4300
+ let t = deg - 135;
4301
+ if (t < 0)
4302
+ t += 360;
4303
+ let fraction;
4304
+ if (t > 270) {
4305
+ fraction = 360 - t < t - 270 ? 0 : 1;
4306
+ }
4307
+ else {
4308
+ fraction = t / 270;
4309
+ }
4310
+ const raw = this.min() + fraction * (this.max() - this.min());
4311
+ this.setValue(raw);
4312
+ this.onTouched();
4313
+ }
4314
+ setValue(raw) {
4315
+ const rounded = Math.round(raw / this.step()) * this.step();
4316
+ const clamped = this.clamp(rounded);
4317
+ this.value.set(clamped);
4318
+ this.onChange(clamped);
4319
+ }
4320
+ clamp(value) {
4321
+ return Math.max(this.min(), Math.min(this.max(), value));
4322
+ }
4323
+ getArcPath(startDeg, endDeg) {
4324
+ const cx = this.center();
4325
+ const cy = this.center();
4326
+ const r = this.r();
4327
+ const start = this.anglePoint(cx, cy, r, startDeg);
4328
+ const end = this.anglePoint(cx, cy, r, endDeg);
4329
+ const largeArc = endDeg - startDeg > 180 ? '1' : '0';
4330
+ return `M ${start.x.toFixed(2)} ${start.y.toFixed(2)} A ${r.toFixed(2)} ${r.toFixed(2)} 0 ${largeArc} 1 ${end.x.toFixed(2)} ${end.y.toFixed(2)}`;
4331
+ }
4332
+ anglePoint(cx, cy, r, angleDeg) {
4333
+ const rad = (Math.PI * angleDeg) / 180;
4334
+ return {
4335
+ x: cx + r * Math.cos(rad),
4336
+ y: cy + r * Math.sin(rad),
4337
+ };
4338
+ }
4339
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: KnobComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4340
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.18", type: KnobComponent, isStandalone: true, selector: "shk-knob", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, step: { classPropertyName: "step", publicName: "step", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, strokeWidth: { classPropertyName: "strokeWidth", publicName: "strokeWidth", isSignal: true, isRequired: false, transformFunction: null }, valueColor: { classPropertyName: "valueColor", publicName: "valueColor", isSignal: true, isRequired: false, transformFunction: null }, rangeColor: { classPropertyName: "rangeColor", publicName: "rangeColor", isSignal: true, isRequired: false, transformFunction: null }, showValue: { classPropertyName: "showValue", publicName: "showValue", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", change: "change", end: "end" }, providers: [
4341
+ {
4342
+ provide: NG_VALUE_ACCESSOR,
4343
+ useExisting: forwardRef(() => KnobComponent),
4344
+ multi: true,
4345
+ },
4346
+ ], viewQueries: [{ propertyName: "knobSvg", first: true, predicate: ["knobSvg"], descendants: true, isSignal: true }], ngImport: i0, template: `
4347
+ <div
4348
+ class="shk-knob"
4349
+ [class.shk-knob-disabled]="effectiveDisabled()"
4350
+ [style]="{ width: size() + 'px', height: size() + 'px' }"
4351
+ >
4352
+ <svg
4353
+ #knobSvg
4354
+ class="shk-knob-svg"
4355
+ [attr.width]="size()"
4356
+ [attr.height]="size()"
4357
+ [attr.viewBox]="'0 0 ' + size() + ' ' + size()"
4358
+ role="slider"
4359
+ tabindex="0"
4360
+ [attr.aria-label]="ariaLabel() || 'Valor'"
4361
+ [attr.aria-valuemin]="min()"
4362
+ [attr.aria-valuemax]="max()"
4363
+ [attr.aria-valuenow]="value()"
4364
+ [class.shk-knob-svg-focusable]="!effectiveDisabled()"
4365
+ (pointerdown)="onPointerDown($event)"
4366
+ (pointermove)="onPointerMove($event)"
4367
+ (pointerup)="onPointerUp($event)"
4368
+ (pointercancel)="onPointerUp($event)"
4369
+ (keydown.arrowup)="updateByKey(step())"
4370
+ (keydown.arrowright)="updateByKey(step())"
4371
+ (keydown.arrowdown)="updateByKey(-step())"
4372
+ (keydown.arrowleft)="updateByKey(-step())"
4373
+ >
4374
+ <path
4375
+ [attr.d]="rangeArc()"
4376
+ fill="none"
4377
+ [style.stroke]="rangeColor()"
4378
+ [attr.stroke-width]="strokeWidth()"
4379
+ stroke-linecap="round"
4380
+ ></path>
4381
+ <path
4382
+ [attr.d]="valueArc()"
4383
+ fill="none"
4384
+ [style.stroke]="valueColor()"
4385
+ [attr.stroke-width]="strokeWidth()"
4386
+ stroke-linecap="round"
4387
+ ></path>
4388
+ </svg>
4389
+ @if (showValue()) {
4390
+ <div class="shk-knob-label">{{ value() }}</div>
4391
+ }
4392
+ </div>
4393
+ `, isInline: true, styles: [".shk-knob{position:relative;display:inline-block}.shk-knob-svg{display:block;touch-action:none}.shk-knob-svg-focusable{cursor:pointer;outline:none}.shk-knob-svg-focusable:focus-visible{border-radius:9999px;outline:2px solid var(--shk-primary-400);outline-offset:2px}.shk-knob-disabled{opacity:.55}.shk-knob-disabled .shk-knob-svg{cursor:not-allowed}.shk-knob-label{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);font-size:1.125rem;font-weight:600;color:var(--shk-surface-800);pointer-events:none;-webkit-user-select:none;user-select:none}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
4394
+ }
4395
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: KnobComponent, decorators: [{
4396
+ type: Component,
4397
+ args: [{ selector: 'shk-knob', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: `
4398
+ <div
4399
+ class="shk-knob"
4400
+ [class.shk-knob-disabled]="effectiveDisabled()"
4401
+ [style]="{ width: size() + 'px', height: size() + 'px' }"
4402
+ >
4403
+ <svg
4404
+ #knobSvg
4405
+ class="shk-knob-svg"
4406
+ [attr.width]="size()"
4407
+ [attr.height]="size()"
4408
+ [attr.viewBox]="'0 0 ' + size() + ' ' + size()"
4409
+ role="slider"
4410
+ tabindex="0"
4411
+ [attr.aria-label]="ariaLabel() || 'Valor'"
4412
+ [attr.aria-valuemin]="min()"
4413
+ [attr.aria-valuemax]="max()"
4414
+ [attr.aria-valuenow]="value()"
4415
+ [class.shk-knob-svg-focusable]="!effectiveDisabled()"
4416
+ (pointerdown)="onPointerDown($event)"
4417
+ (pointermove)="onPointerMove($event)"
4418
+ (pointerup)="onPointerUp($event)"
4419
+ (pointercancel)="onPointerUp($event)"
4420
+ (keydown.arrowup)="updateByKey(step())"
4421
+ (keydown.arrowright)="updateByKey(step())"
4422
+ (keydown.arrowdown)="updateByKey(-step())"
4423
+ (keydown.arrowleft)="updateByKey(-step())"
4424
+ >
4425
+ <path
4426
+ [attr.d]="rangeArc()"
4427
+ fill="none"
4428
+ [style.stroke]="rangeColor()"
4429
+ [attr.stroke-width]="strokeWidth()"
4430
+ stroke-linecap="round"
4431
+ ></path>
4432
+ <path
4433
+ [attr.d]="valueArc()"
4434
+ fill="none"
4435
+ [style.stroke]="valueColor()"
4436
+ [attr.stroke-width]="strokeWidth()"
4437
+ stroke-linecap="round"
4438
+ ></path>
4439
+ </svg>
4440
+ @if (showValue()) {
4441
+ <div class="shk-knob-label">{{ value() }}</div>
4442
+ }
4443
+ </div>
4444
+ `, providers: [
4445
+ {
4446
+ provide: NG_VALUE_ACCESSOR,
4447
+ useExisting: forwardRef(() => KnobComponent),
4448
+ multi: true,
4449
+ },
4450
+ ], styles: [".shk-knob{position:relative;display:inline-block}.shk-knob-svg{display:block;touch-action:none}.shk-knob-svg-focusable{cursor:pointer;outline:none}.shk-knob-svg-focusable:focus-visible{border-radius:9999px;outline:2px solid var(--shk-primary-400);outline-offset:2px}.shk-knob-disabled{opacity:.55}.shk-knob-disabled .shk-knob-svg{cursor:not-allowed}.shk-knob-label{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);font-size:1.125rem;font-weight:600;color:var(--shk-surface-800);pointer-events:none;-webkit-user-select:none;user-select:none}\n"] }]
4451
+ }], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], step: [{ type: i0.Input, args: [{ isSignal: true, alias: "step", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], strokeWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "strokeWidth", required: false }] }], valueColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "valueColor", required: false }] }], rangeColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "rangeColor", required: false }] }], showValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "showValue", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], change: [{ type: i0.Output, args: ["change"] }], end: [{ type: i0.Output, args: ["end"] }], knobSvg: [{ type: i0.ViewChild, args: ['knobSvg', { isSignal: true }] }] } });
4452
+
4453
+ /** Circular indeterminate spinner. */
4454
+ class LoadingSpinnerComponent {
4455
+ constructor() {
4456
+ /** Diameter of the spinner. */
4457
+ this.size = input('2rem', ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
4458
+ /** Border thickness in px. */
4459
+ this.thickness = input(3, ...(ngDevMode ? [{ debugName: "thickness" }] : /* istanbul ignore next */ []));
4460
+ /** Color of the moving arc. Defaults to the primary token. */
4461
+ this.color = input('', ...(ngDevMode ? [{ debugName: "color" }] : /* istanbul ignore next */ []));
4462
+ /** Optional text shown under the spinner. */
4463
+ this.label = input('', ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
4464
+ }
4465
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: LoadingSpinnerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4466
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.18", type: LoadingSpinnerComponent, isStandalone: true, selector: "shk-loading-spinner", inputs: { size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, thickness: { classPropertyName: "thickness", publicName: "thickness", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
4467
+ <div
4468
+ class="shk-loading-spinner"
4469
+ [style]="{
4470
+ width: size(),
4471
+ height: size(),
4472
+ }"
4473
+ role="status"
4474
+ aria-label="Cargando"
4475
+ >
4476
+ <div
4477
+ class="shk-loading-spinner-circle"
4478
+ [style]="{
4479
+ borderWidth: thickness(),
4480
+ borderTopColor: color() || undefined,
4481
+ }"
4482
+ ></div>
4483
+ </div>
4484
+ @if (label()) {
4485
+ <span class="shk-loading-spinner-label">{{ label() }}</span>
4486
+ }
4487
+ `, isInline: true, styles: [":host{display:inline-flex;flex-direction:column;align-items:center;justify-content:center;gap:.5rem}.shk-loading-spinner{display:inline-block}.shk-loading-spinner-circle{width:100%;height:100%;border-radius:9999px;border-style:solid;border-color:color-mix(in srgb,var(--shk-primary-600) 20%,transparent);border-top-color:var(--shk-primary-600);animation:shk-loading-spin .8s linear infinite}.shk-loading-spinner-label{font-size:.8125rem;color:var(--shk-surface-500)}@keyframes shk-loading-spin{to{transform:rotate(360deg)}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
4488
+ }
4489
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: LoadingSpinnerComponent, decorators: [{
4490
+ type: Component,
4491
+ args: [{ selector: 'shk-loading-spinner', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: `
4492
+ <div
4493
+ class="shk-loading-spinner"
4494
+ [style]="{
4495
+ width: size(),
4496
+ height: size(),
4497
+ }"
4498
+ role="status"
4499
+ aria-label="Cargando"
4500
+ >
4501
+ <div
4502
+ class="shk-loading-spinner-circle"
4503
+ [style]="{
4504
+ borderWidth: thickness(),
4505
+ borderTopColor: color() || undefined,
4506
+ }"
4507
+ ></div>
4508
+ </div>
4509
+ @if (label()) {
4510
+ <span class="shk-loading-spinner-label">{{ label() }}</span>
4511
+ }
4512
+ `, styles: [":host{display:inline-flex;flex-direction:column;align-items:center;justify-content:center;gap:.5rem}.shk-loading-spinner{display:inline-block}.shk-loading-spinner-circle{width:100%;height:100%;border-radius:9999px;border-style:solid;border-color:color-mix(in srgb,var(--shk-primary-600) 20%,transparent);border-top-color:var(--shk-primary-600);animation:shk-loading-spin .8s linear infinite}.shk-loading-spinner-label{font-size:.8125rem;color:var(--shk-surface-500)}@keyframes shk-loading-spin{to{transform:rotate(360deg)}}\n"] }]
4513
+ }], propDecorators: { size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], thickness: [{ type: i0.Input, args: [{ isSignal: true, alias: "thickness", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }] } });
4514
+
4515
+ /** Progress bar with an indeterminate sweep or a determinate percentage. */
4516
+ class LoadingBarComponent {
4517
+ constructor() {
4518
+ /** Height of the bar. */
4519
+ this.height = input('0.25rem', ...(ngDevMode ? [{ debugName: "height" }] : /* istanbul ignore next */ []));
4520
+ /** When true the bar animates without a known percentage. */
4521
+ this.indeterminate = input(true, ...(ngDevMode ? [{ debugName: "indeterminate" }] : /* istanbul ignore next */ []));
4522
+ /** Current progress 0-100 when `indeterminate` is false. */
4523
+ this.progress = input(0, ...(ngDevMode ? [{ debugName: "progress" }] : /* istanbul ignore next */ []));
4524
+ /** Fill color. Defaults to the primary token. */
4525
+ this.color = input('', ...(ngDevMode ? [{ debugName: "color" }] : /* istanbul ignore next */ []));
4526
+ /** Renders fully rounded ends. */
4527
+ this.rounded = input(true, ...(ngDevMode ? [{ debugName: "rounded" }] : /* istanbul ignore next */ []));
4528
+ this.fillStyle = computed(() => {
4529
+ if (this.indeterminate())
4530
+ return {};
4531
+ return { width: `${Math.max(0, Math.min(100, this.progress()))}%` };
4532
+ }, ...(ngDevMode ? [{ debugName: "fillStyle" }] : /* istanbul ignore next */ []));
4533
+ }
4534
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: LoadingBarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4535
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.18", type: LoadingBarComponent, isStandalone: true, selector: "shk-loading-bar", inputs: { height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, indeterminate: { classPropertyName: "indeterminate", publicName: "indeterminate", isSignal: true, isRequired: false, transformFunction: null }, progress: { classPropertyName: "progress", publicName: "progress", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, rounded: { classPropertyName: "rounded", publicName: "rounded", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
4536
+ <div
4537
+ class="shk-loading-bar"
4538
+ [class.shk-loading-bar-indeterminate]="indeterminate()"
4539
+ [style]="{ height: height(), borderRadius: rounded() ? '9999px' : undefined }"
4540
+ role="progressbar"
4541
+ [attr.aria-valuenow]="indeterminate() ? undefined : progress()"
4542
+ aria-label="Progreso"
4543
+ >
4544
+ <div
4545
+ class="shk-loading-bar-fill"
4546
+ [style]="fillStyle()"
4547
+ [style.background-color]="color() || undefined"
4548
+ [style.border-radius]="rounded() ? '9999px' : undefined"
4549
+ ></div>
4550
+ </div>
4551
+ `, isInline: true, styles: [".shk-loading-bar{width:100%;position:relative;overflow:hidden;background-color:var(--shk-surface-200)}.shk-loading-bar-fill{height:100%;background-color:var(--shk-primary-600);transition:width .3s ease-in-out}.shk-loading-bar-indeterminate .shk-loading-bar-fill{width:40%;animation:shk-loading-bar-sweep 1.2s ease-in-out infinite}@keyframes shk-loading-bar-sweep{0%{transform:translate(-100%)}to{transform:translate(350%)}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
4552
+ }
4553
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: LoadingBarComponent, decorators: [{
4554
+ type: Component,
4555
+ args: [{ selector: 'shk-loading-bar', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: `
4556
+ <div
4557
+ class="shk-loading-bar"
4558
+ [class.shk-loading-bar-indeterminate]="indeterminate()"
4559
+ [style]="{ height: height(), borderRadius: rounded() ? '9999px' : undefined }"
4560
+ role="progressbar"
4561
+ [attr.aria-valuenow]="indeterminate() ? undefined : progress()"
4562
+ aria-label="Progreso"
4563
+ >
4564
+ <div
4565
+ class="shk-loading-bar-fill"
4566
+ [style]="fillStyle()"
4567
+ [style.background-color]="color() || undefined"
4568
+ [style.border-radius]="rounded() ? '9999px' : undefined"
4569
+ ></div>
4570
+ </div>
4571
+ `, styles: [".shk-loading-bar{width:100%;position:relative;overflow:hidden;background-color:var(--shk-surface-200)}.shk-loading-bar-fill{height:100%;background-color:var(--shk-primary-600);transition:width .3s ease-in-out}.shk-loading-bar-indeterminate .shk-loading-bar-fill{width:40%;animation:shk-loading-bar-sweep 1.2s ease-in-out infinite}@keyframes shk-loading-bar-sweep{0%{transform:translate(-100%)}to{transform:translate(350%)}}\n"] }]
4572
+ }], propDecorators: { height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }], indeterminate: [{ type: i0.Input, args: [{ isSignal: true, alias: "indeterminate", required: false }] }], progress: [{ type: i0.Input, args: [{ isSignal: true, alias: "progress", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], rounded: [{ type: i0.Input, args: [{ isSignal: true, alias: "rounded", required: false }] }] } });
4573
+
4574
+ /** Wraps content and shows a centered spinner over it while `visible` is true. */
4575
+ class LoadingOverlayComponent {
4576
+ constructor() {
4577
+ /** Controls whether the spinner layer is shown. */
4578
+ this.visible = input(false, ...(ngDevMode ? [{ debugName: "visible" }] : /* istanbul ignore next */ []));
4579
+ /** Optional text under the spinner. */
4580
+ this.message = input('', ...(ngDevMode ? [{ debugName: "message" }] : /* istanbul ignore next */ []));
4581
+ /** Diameter of the spinner. */
4582
+ this.spinnerSize = input('2.5rem', ...(ngDevMode ? [{ debugName: "spinnerSize" }] : /* istanbul ignore next */ []));
4583
+ /** Spinner color. Defaults to the primary token. */
4584
+ this.color = input('', ...(ngDevMode ? [{ debugName: "color" }] : /* istanbul ignore next */ []));
4585
+ /** Backdrop behind the spinner. */
4586
+ this.backdrop = input('transparent', ...(ngDevMode ? [{ debugName: "backdrop" }] : /* istanbul ignore next */ []));
4587
+ }
4588
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: LoadingOverlayComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4589
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.18", type: LoadingOverlayComponent, isStandalone: true, selector: "shk-loading-overlay", inputs: { visible: { classPropertyName: "visible", publicName: "visible", isSignal: true, isRequired: false, transformFunction: null }, message: { classPropertyName: "message", publicName: "message", isSignal: true, isRequired: false, transformFunction: null }, spinnerSize: { classPropertyName: "spinnerSize", publicName: "spinnerSize", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, backdrop: { classPropertyName: "backdrop", publicName: "backdrop", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
4590
+ <div
4591
+ class="shk-loading-overlay"
4592
+ [class.shk-loading-overlay-light]="backdrop() === 'light'"
4593
+ [class.shk-loading-overlay-dark]="backdrop() === 'dark'"
4594
+ >
4595
+ <ng-content></ng-content>
4596
+ @if (visible()) {
4597
+ <div class="shk-loading-overlay-layer">
4598
+ <shk-loading-spinner [size]="spinnerSize()" [color]="color()" [label]="message()" />
4599
+ </div>
4600
+ }
4601
+ </div>
4602
+ `, isInline: true, styles: [":host{display:block}.shk-loading-overlay{position:relative;min-height:1.5rem}.shk-loading-overlay-layer{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;z-index:10}.shk-loading-overlay-light .shk-loading-overlay-layer{background-color:#ffffffa6}.dark .shk-loading-overlay-light .shk-loading-overlay-layer{background-color:#0000008c}.shk-loading-overlay-dark .shk-loading-overlay-layer{background-color:#0006}\n"], dependencies: [{ kind: "component", type: LoadingSpinnerComponent, selector: "shk-loading-spinner", inputs: ["size", "thickness", "color", "label"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
4603
+ }
4604
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: LoadingOverlayComponent, decorators: [{
4605
+ type: Component,
4606
+ args: [{ selector: 'shk-loading-overlay', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [LoadingSpinnerComponent], template: `
4607
+ <div
4608
+ class="shk-loading-overlay"
4609
+ [class.shk-loading-overlay-light]="backdrop() === 'light'"
4610
+ [class.shk-loading-overlay-dark]="backdrop() === 'dark'"
4611
+ >
4612
+ <ng-content></ng-content>
4613
+ @if (visible()) {
4614
+ <div class="shk-loading-overlay-layer">
4615
+ <shk-loading-spinner [size]="spinnerSize()" [color]="color()" [label]="message()" />
4616
+ </div>
4617
+ }
4618
+ </div>
4619
+ `, styles: [":host{display:block}.shk-loading-overlay{position:relative;min-height:1.5rem}.shk-loading-overlay-layer{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;z-index:10}.shk-loading-overlay-light .shk-loading-overlay-layer{background-color:#ffffffa6}.dark .shk-loading-overlay-light .shk-loading-overlay-layer{background-color:#0000008c}.shk-loading-overlay-dark .shk-loading-overlay-layer{background-color:#0006}\n"] }]
4620
+ }], propDecorators: { visible: [{ type: i0.Input, args: [{ isSignal: true, alias: "visible", required: false }] }], message: [{ type: i0.Input, args: [{ isSignal: true, alias: "message", required: false }] }], spinnerSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "spinnerSize", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], backdrop: [{ type: i0.Input, args: [{ isSignal: true, alias: "backdrop", required: false }] }] } });
4621
+
4622
+ /** Bouncing dots loading indicator. */
4623
+ class LoadingDotsComponent {
4624
+ constructor() {
4625
+ /** Diameter of each dot. */
4626
+ this.size = input('0.5rem', ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
4627
+ /** Dot color. Defaults to the primary token. */
4628
+ this.color = input('', ...(ngDevMode ? [{ debugName: "color" }] : /* istanbul ignore next */ []));
4629
+ /** Number of dots. */
4630
+ this.count = input(3, ...(ngDevMode ? [{ debugName: "count" }] : /* istanbul ignore next */ []));
4631
+ /** Optional text shown next to the dots. */
4632
+ this.label = input('', ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
4633
+ this.dots = computed(() => Array.from({ length: Math.max(1, this.count()) }), ...(ngDevMode ? [{ debugName: "dots" }] : /* istanbul ignore next */ []));
4634
+ }
4635
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: LoadingDotsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4636
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.18", type: LoadingDotsComponent, isStandalone: true, selector: "shk-loading-dots", inputs: { size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, count: { classPropertyName: "count", publicName: "count", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
4637
+ <span class="shk-loading-dots" role="status" aria-label="Cargando">
4638
+ @for (dot of dots(); track $index) {
4639
+ <span
4640
+ class="shk-loading-dot"
4641
+ [style]="{
4642
+ width: size(),
4643
+ height: size(),
4644
+ backgroundColor: color() || undefined,
4645
+ animationDelay: $index * 0.12 + 's',
4646
+ }"
4647
+ ></span>
4648
+ }
4649
+ </span>
4650
+ @if (label()) {
4651
+ <span class="shk-loading-dots-label">{{ label() }}</span>
4652
+ }
4653
+ `, isInline: true, styles: [":host{display:inline-flex;align-items:center;gap:.5rem}.shk-loading-dots{display:inline-flex;align-items:center;gap:.25rem}.shk-loading-dot{border-radius:9999px;background-color:var(--shk-primary-600);animation:shk-loading-dots-bounce 1.4s ease-in-out infinite}.shk-loading-dots-label{font-size:.8125rem;color:var(--shk-surface-500)}@keyframes shk-loading-dots-bounce{0%,80%,to{transform:scale(.6);opacity:.5}40%{transform:scale(1);opacity:1}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
4654
+ }
4655
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: LoadingDotsComponent, decorators: [{
4656
+ type: Component,
4657
+ args: [{ selector: 'shk-loading-dots', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: `
4658
+ <span class="shk-loading-dots" role="status" aria-label="Cargando">
4659
+ @for (dot of dots(); track $index) {
4660
+ <span
4661
+ class="shk-loading-dot"
4662
+ [style]="{
4663
+ width: size(),
4664
+ height: size(),
4665
+ backgroundColor: color() || undefined,
4666
+ animationDelay: $index * 0.12 + 's',
4667
+ }"
4668
+ ></span>
4669
+ }
4670
+ </span>
4671
+ @if (label()) {
4672
+ <span class="shk-loading-dots-label">{{ label() }}</span>
4673
+ }
4674
+ `, styles: [":host{display:inline-flex;align-items:center;gap:.5rem}.shk-loading-dots{display:inline-flex;align-items:center;gap:.25rem}.shk-loading-dot{border-radius:9999px;background-color:var(--shk-primary-600);animation:shk-loading-dots-bounce 1.4s ease-in-out infinite}.shk-loading-dots-label{font-size:.8125rem;color:var(--shk-surface-500)}@keyframes shk-loading-dots-bounce{0%,80%,to{transform:scale(.6);opacity:.5}40%{transform:scale(1);opacity:1}}\n"] }]
4675
+ }], propDecorators: { size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], count: [{ type: i0.Input, args: [{ isSignal: true, alias: "count", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }] } });
4676
+
4677
+ /** Shared state between shk-tabs and its projected tab/tabpanel children. */
4678
+ class TabsService {
4679
+ constructor() {
4680
+ this.active = signal(undefined, ...(ngDevMode ? [{ debugName: "active" }] : /* istanbul ignore next */ []));
4681
+ }
4682
+ select(value) {
4683
+ this.active.set(value);
4684
+ }
4685
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: TabsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
4686
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: TabsService }); }
4687
+ }
4688
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: TabsService, decorators: [{
4689
+ type: Injectable
4690
+ }] });
4691
+
4692
+ /** Container for tab panels. Renders its projected shk-tablist/shh-tab content. */
4693
+ class TabsComponent {
4694
+ constructor() {
4695
+ /** Currently active tab value. Two-way bindable with [(value)]. */
4696
+ this.value = model(undefined, ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
4697
+ /** Allows the tab header to scroll horizontally when it overflows. */
4698
+ this.scrollable = input(false, ...(ngDevMode ? [{ debugName: "scrollable" }] : /* istanbul ignore next */ []));
4699
+ this.tabsSvc = inject(TabsService);
4700
+ effect(() => {
4701
+ this.tabsSvc.active.set(this.value());
4702
+ });
4703
+ effect(() => {
4704
+ const active = this.tabsSvc.active();
4705
+ if (active !== this.value()) {
4706
+ this.value.set(active);
4707
+ }
4708
+ });
4709
+ }
4710
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: TabsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4711
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.18", type: TabsComponent, isStandalone: true, selector: "shk-tabs", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, scrollable: { classPropertyName: "scrollable", publicName: "scrollable", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, providers: [TabsService], ngImport: i0, template: `
4712
+ <div class="shk-tabs">
4713
+ <ng-content></ng-content>
4714
+ </div>
4715
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
4716
+ }
4717
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: TabsComponent, decorators: [{
4718
+ type: Component,
4719
+ args: [{
4720
+ selector: 'shk-tabs',
4721
+ standalone: true,
4722
+ changeDetection: ChangeDetectionStrategy.OnPush,
4723
+ providers: [TabsService],
4724
+ template: `
4725
+ <div class="shk-tabs">
4726
+ <ng-content></ng-content>
4727
+ </div>
4728
+ `,
4729
+ }]
4730
+ }], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], scrollable: [{ type: i0.Input, args: [{ isSignal: true, alias: "scrollable", required: false }] }] } });
4731
+
4732
+ /** Horizontal bar that contains the shk-tab headers. */
4733
+ class TablistComponent {
4734
+ constructor() {
4735
+ /** Allows the header to scroll horizontally when it overflows. */
4736
+ this.scrollable = input(false, ...(ngDevMode ? [{ debugName: "scrollable" }] : /* istanbul ignore next */ []));
4737
+ /** Extra CSS classes. */
4738
+ this.styleClass = input('', ...(ngDevMode ? [{ debugName: "styleClass" }] : /* istanbul ignore next */ []));
4739
+ }
4740
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: TablistComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4741
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.18", type: TablistComponent, isStandalone: true, selector: "shk-tablist", inputs: { scrollable: { classPropertyName: "scrollable", publicName: "scrollable", isSignal: true, isRequired: false, transformFunction: null }, styleClass: { classPropertyName: "styleClass", publicName: "styleClass", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
4742
+ <div
4743
+ class="shk-tablist"
4744
+ [class.shk-tablist-scrollable]="scrollable()"
4745
+ [class]="styleClass()"
4746
+ role="tablist"
4747
+ aria-orientation="horizontal"
4748
+ >
4749
+ <ng-content></ng-content>
4750
+ </div>
4751
+ `, isInline: true, styles: [".shk-tablist{display:flex;align-items:center;gap:.25rem;flex-wrap:wrap;border-bottom:1px solid var(--shk-surface-200)}.dark .shk-tablist{border-bottom-color:var(--shk-surface-200)}.shk-tablist-scrollable{flex-wrap:nowrap;overflow-x:auto;scrollbar-width:thin}.shk-tablist-scrollable::-webkit-scrollbar{height:6px}.shk-tablist-scrollable::-webkit-scrollbar-thumb{background:var(--shk-surface-300);border-radius:9999px}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
4752
+ }
4753
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: TablistComponent, decorators: [{
4754
+ type: Component,
4755
+ args: [{ selector: 'shk-tablist', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: `
4756
+ <div
4757
+ class="shk-tablist"
4758
+ [class.shk-tablist-scrollable]="scrollable()"
4759
+ [class]="styleClass()"
4760
+ role="tablist"
4761
+ aria-orientation="horizontal"
4762
+ >
4763
+ <ng-content></ng-content>
4764
+ </div>
4765
+ `, styles: [".shk-tablist{display:flex;align-items:center;gap:.25rem;flex-wrap:wrap;border-bottom:1px solid var(--shk-surface-200)}.dark .shk-tablist{border-bottom-color:var(--shk-surface-200)}.shk-tablist-scrollable{flex-wrap:nowrap;overflow-x:auto;scrollbar-width:thin}.shk-tablist-scrollable::-webkit-scrollbar{height:6px}.shk-tablist-scrollable::-webkit-scrollbar-thumb{background:var(--shk-surface-300);border-radius:9999px}\n"] }]
4766
+ }], propDecorators: { scrollable: [{ type: i0.Input, args: [{ isSignal: true, alias: "scrollable", required: false }] }], styleClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "styleClass", required: false }] }] } });
4767
+
4768
+ /** A single tab header. Clicking it activates the matching panel. */
4769
+ class TabComponent {
4770
+ constructor() {
4771
+ /** Value that links this tab to its panel. */
4772
+ this.value = input(undefined, ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
4773
+ /** Disables the tab. */
4774
+ this.disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
4775
+ /** Extra CSS classes. */
4776
+ this.styleClass = input('', ...(ngDevMode ? [{ debugName: "styleClass" }] : /* istanbul ignore next */ []));
4777
+ this.tabsSvc = inject(TabsService);
4778
+ this.active = computed(() => this.tabsSvc.active() === this.value(), ...(ngDevMode ? [{ debugName: "active" }] : /* istanbul ignore next */ []));
4779
+ }
4780
+ select() {
4781
+ if (this.disabled())
4782
+ return;
4783
+ this.tabsSvc.select(this.value());
4784
+ }
4785
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: TabComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4786
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.18", type: TabComponent, isStandalone: true, selector: "shk-tab", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, styleClass: { classPropertyName: "styleClass", publicName: "styleClass", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
4787
+ <button
4788
+ type="button"
4789
+ class="shk-tab"
4790
+ [class]="styleClass()"
4791
+ [class.shk-tab-active]="active()"
4792
+ [class.shk-tab-disabled]="disabled()"
4793
+ role="tab"
4794
+ [attr.aria-selected]="active()"
4795
+ [disabled]="disabled()"
4796
+ (click)="select()"
4797
+ >
4798
+ <ng-content></ng-content>
4799
+ </button>
4800
+ `, isInline: true, styles: [".shk-tab{display:inline-flex;align-items:center;gap:.375rem;padding:.625rem 1rem;font-size:.875rem;font-weight:500;color:var(--shk-surface-500);background:transparent;border:none;border-bottom:2px solid transparent;margin-bottom:-1px;cursor:pointer;white-space:nowrap;transition:color .15s ease-in-out,border-color .15s ease-in-out,background-color .15s ease-in-out}.shk-tab:hover:not(:disabled):not(.shk-tab-active){color:var(--shk-surface-800)}.shk-tab-active{color:var(--shk-primary-600);border-bottom-color:var(--shk-primary-600);font-weight:600}.dark .shk-tab-active{color:var(--shk-primary-400);border-bottom-color:var(--shk-primary-400)}.shk-tab:disabled{opacity:.5;cursor:not-allowed}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
4801
+ }
4802
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: TabComponent, decorators: [{
4803
+ type: Component,
4804
+ args: [{ selector: 'shk-tab', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: `
4805
+ <button
4806
+ type="button"
4807
+ class="shk-tab"
4808
+ [class]="styleClass()"
4809
+ [class.shk-tab-active]="active()"
4810
+ [class.shk-tab-disabled]="disabled()"
4811
+ role="tab"
4812
+ [attr.aria-selected]="active()"
4813
+ [disabled]="disabled()"
4814
+ (click)="select()"
4815
+ >
4816
+ <ng-content></ng-content>
4817
+ </button>
4818
+ `, styles: [".shk-tab{display:inline-flex;align-items:center;gap:.375rem;padding:.625rem 1rem;font-size:.875rem;font-weight:500;color:var(--shk-surface-500);background:transparent;border:none;border-bottom:2px solid transparent;margin-bottom:-1px;cursor:pointer;white-space:nowrap;transition:color .15s ease-in-out,border-color .15s ease-in-out,background-color .15s ease-in-out}.shk-tab:hover:not(:disabled):not(.shk-tab-active){color:var(--shk-surface-800)}.shk-tab-active{color:var(--shk-primary-600);border-bottom-color:var(--shk-primary-600);font-weight:600}.dark .shk-tab-active{color:var(--shk-primary-400);border-bottom-color:var(--shk-primary-400)}.shk-tab:disabled{opacity:.5;cursor:not-allowed}\n"] }]
4819
+ }], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], styleClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "styleClass", required: false }] }] } });
4820
+
4821
+ /** Container that groups the shk-tabpanel content sections. */
4822
+ class TabpanelsComponent {
4823
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: TabpanelsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4824
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.18", type: TabpanelsComponent, isStandalone: true, selector: "shk-tabpanels", ngImport: i0, template: `
4825
+ <div class="shk-tabpanels">
4826
+ <ng-content></ng-content>
4827
+ </div>
4828
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
4829
+ }
4830
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: TabpanelsComponent, decorators: [{
4831
+ type: Component,
4832
+ args: [{
4833
+ selector: 'shk-tabpanels',
4834
+ standalone: true,
4835
+ changeDetection: ChangeDetectionStrategy.OnPush,
4836
+ template: `
4837
+ <div class="shk-tabpanels">
4838
+ <ng-content></ng-content>
4839
+ </div>
4840
+ `,
4841
+ }]
4842
+ }] });
4843
+
4844
+ /** Content section shown only while its value is the active tab. */
4845
+ class TabPanelComponent {
4846
+ constructor() {
4847
+ /** Value that links this panel to its tab. */
4848
+ this.value = input(undefined, ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
4849
+ this.tabsSvc = inject(TabsService);
4850
+ this.active = computed(() => this.tabsSvc.active() === this.value(), ...(ngDevMode ? [{ debugName: "active" }] : /* istanbul ignore next */ []));
4851
+ }
4852
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: TabPanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4853
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.18", type: TabPanelComponent, isStandalone: true, selector: "shk-tabpanel", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
4854
+ @if (active()) {
4855
+ <div class="shk-tabpanel" role="tabpanel">
4856
+ <ng-content></ng-content>
4857
+ </div>
4858
+ }
4859
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
4860
+ }
4861
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: TabPanelComponent, decorators: [{
4862
+ type: Component,
4863
+ args: [{
4864
+ selector: 'shk-tabpanel',
4865
+ standalone: true,
4866
+ changeDetection: ChangeDetectionStrategy.OnPush,
4867
+ template: `
4868
+ @if (active()) {
4869
+ <div class="shk-tabpanel" role="tabpanel">
4870
+ <ng-content></ng-content>
4871
+ </div>
4872
+ }
4873
+ `,
4874
+ }]
4875
+ }], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }] } });
4876
+
4877
+ class BreadcrumbService {
4878
+ constructor() {
4879
+ this.breadcrumbsSubject = new BehaviorSubject([]);
4880
+ this.breadcrumbs$ = this.breadcrumbsSubject.asObservable();
4881
+ }
4882
+ set(items) {
4883
+ this.breadcrumbsSubject.next(items);
4884
+ }
4885
+ clear() {
4886
+ this.breadcrumbsSubject.next([]);
4887
+ }
4888
+ getCurrent() {
4889
+ return this.breadcrumbsSubject.getValue();
4890
+ }
4891
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: BreadcrumbService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
4892
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: BreadcrumbService, providedIn: 'root' }); }
4893
+ }
4894
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: BreadcrumbService, decorators: [{
4895
+ type: Injectable,
4896
+ args: [{
4897
+ providedIn: 'root',
4898
+ }]
4899
+ }] });
4900
+
4901
+ class BreadcrumbComponent {
4902
+ constructor() {
4903
+ this.breadcrumbService = inject(BreadcrumbService);
4904
+ this.items = input([], ...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
4905
+ this.separator = input('chevron', ...(ngDevMode ? [{ debugName: "separator" }] : /* istanbul ignore next */ []));
4906
+ this.useService = input(true, ...(ngDevMode ? [{ debugName: "useService" }] : /* istanbul ignore next */ []));
4907
+ this.serviceItems = signal([], ...(ngDevMode ? [{ debugName: "serviceItems" }] : /* istanbul ignore next */ []));
4908
+ this.resolvedItems = computed(() => {
4909
+ const staticItems = this.items();
4910
+ if (staticItems.length > 0)
4911
+ return staticItems;
4912
+ if (this.useService())
4913
+ return this.serviceItems();
4914
+ return [];
4915
+ }, ...(ngDevMode ? [{ debugName: "resolvedItems" }] : /* istanbul ignore next */ []));
4916
+ this.serviceSub = null;
4917
+ effect(() => {
4918
+ this.resolvedItems();
4919
+ });
4920
+ }
4921
+ ngOnInit() {
4922
+ if (this.useService()) {
4923
+ this.serviceSub = this.breadcrumbService.breadcrumbs$.subscribe((items) => {
4924
+ this.serviceItems.set(items);
4925
+ });
4926
+ }
4927
+ }
4928
+ ngOnDestroy() {
4929
+ this.serviceSub?.unsubscribe();
4930
+ }
4931
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: BreadcrumbComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4932
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.18", type: BreadcrumbComponent, isStandalone: true, selector: "shk-breadcrumb", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, separator: { classPropertyName: "separator", publicName: "separator", isSignal: true, isRequired: false, transformFunction: null }, useService: { classPropertyName: "useService", publicName: "useService", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
4933
+ <nav class="shk-breadcrumb" aria-label="Breadcrumb">
4934
+ <ol class="shk-breadcrumb-list">
4935
+ @for (item of resolvedItems(); track $index; let last = $last) {
4936
+ <li class="shk-breadcrumb-item">
4937
+ @if (!last && item.route) {
4938
+ <a
4939
+ [routerLink]="item.route"
4940
+ class="shk-breadcrumb-link"
4941
+ [attr.aria-current]="null"
4942
+ >
4943
+ {{ item.label }}
4944
+ </a>
4945
+ } @else {
4946
+ <span
4947
+ class="shk-breadcrumb-current"
4948
+ [attr.aria-current]="last ? 'page' : null"
4949
+ >
4950
+ {{ item.label }}
4951
+ </span>
4952
+ }
4953
+ @if (!last) {
4954
+ <span class="shk-breadcrumb-separator" aria-hidden="true">
4955
+ @switch (separator()) {
4956
+ @case ('slash') {
4957
+ <span class="shk-breadcrumb-separator-text">/</span>
4958
+ }
4959
+ @case ('arrow') {
4960
+ <span class="shk-breadcrumb-separator-text">&gt;</span>
4961
+ }
4962
+ @default {
4963
+ <svg
4964
+ class="shk-breadcrumb-separator-icon"
4965
+ viewBox="0 0 24 24"
4966
+ fill="none"
4967
+ stroke="currentColor"
4968
+ stroke-width="2"
4969
+ stroke-linecap="round"
4970
+ stroke-linejoin="round"
4971
+ >
4972
+ <polyline points="9 18 15 12 9 6" />
4973
+ </svg>
4974
+ }
4975
+ }
4976
+ </span>
4977
+ }
4978
+ </li>
4979
+ }
4980
+ </ol>
4981
+ </nav>
4982
+ `, isInline: true, styles: [":host{display:block}.shk-breadcrumb-list{display:flex;align-items:center;flex-wrap:wrap;gap:.375rem;list-style:none;margin:0;padding:0;font-size:.9375rem;line-height:1.25rem}.shk-breadcrumb-item{display:inline-flex;align-items:center;gap:.375rem;min-width:0}.shk-breadcrumb-link{color:var(--shk-surface-500);text-decoration:none;transition:color .15s ease;cursor:pointer}.shk-breadcrumb-link:hover{color:var(--shk-primary-600)}.shk-breadcrumb-current{color:var(--shk-primary-700);font-weight:600}.shk-breadcrumb-separator{display:inline-flex;align-items:center;justify-content:center;color:var(--shk-surface-300);-webkit-user-select:none;user-select:none;margin:0 .25rem}.shk-breadcrumb-separator-text{font-size:.875rem}.shk-breadcrumb-separator-icon{width:.875rem;height:.875rem}\n"], dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
4983
+ }
4984
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: BreadcrumbComponent, decorators: [{
4985
+ type: Component,
4986
+ args: [{ selector: 'shk-breadcrumb', standalone: true, imports: [RouterLink], changeDetection: ChangeDetectionStrategy.OnPush, template: `
4987
+ <nav class="shk-breadcrumb" aria-label="Breadcrumb">
4988
+ <ol class="shk-breadcrumb-list">
4989
+ @for (item of resolvedItems(); track $index; let last = $last) {
4990
+ <li class="shk-breadcrumb-item">
4991
+ @if (!last && item.route) {
4992
+ <a
4993
+ [routerLink]="item.route"
4994
+ class="shk-breadcrumb-link"
4995
+ [attr.aria-current]="null"
4996
+ >
4997
+ {{ item.label }}
4998
+ </a>
4999
+ } @else {
5000
+ <span
5001
+ class="shk-breadcrumb-current"
5002
+ [attr.aria-current]="last ? 'page' : null"
5003
+ >
5004
+ {{ item.label }}
5005
+ </span>
5006
+ }
5007
+ @if (!last) {
5008
+ <span class="shk-breadcrumb-separator" aria-hidden="true">
5009
+ @switch (separator()) {
5010
+ @case ('slash') {
5011
+ <span class="shk-breadcrumb-separator-text">/</span>
5012
+ }
5013
+ @case ('arrow') {
5014
+ <span class="shk-breadcrumb-separator-text">&gt;</span>
5015
+ }
5016
+ @default {
5017
+ <svg
5018
+ class="shk-breadcrumb-separator-icon"
5019
+ viewBox="0 0 24 24"
5020
+ fill="none"
5021
+ stroke="currentColor"
5022
+ stroke-width="2"
5023
+ stroke-linecap="round"
5024
+ stroke-linejoin="round"
5025
+ >
5026
+ <polyline points="9 18 15 12 9 6" />
5027
+ </svg>
5028
+ }
5029
+ }
5030
+ </span>
5031
+ }
5032
+ </li>
5033
+ }
5034
+ </ol>
5035
+ </nav>
5036
+ `, styles: [":host{display:block}.shk-breadcrumb-list{display:flex;align-items:center;flex-wrap:wrap;gap:.375rem;list-style:none;margin:0;padding:0;font-size:.9375rem;line-height:1.25rem}.shk-breadcrumb-item{display:inline-flex;align-items:center;gap:.375rem;min-width:0}.shk-breadcrumb-link{color:var(--shk-surface-500);text-decoration:none;transition:color .15s ease;cursor:pointer}.shk-breadcrumb-link:hover{color:var(--shk-primary-600)}.shk-breadcrumb-current{color:var(--shk-primary-700);font-weight:600}.shk-breadcrumb-separator{display:inline-flex;align-items:center;justify-content:center;color:var(--shk-surface-300);-webkit-user-select:none;user-select:none;margin:0 .25rem}.shk-breadcrumb-separator-text{font-size:.875rem}.shk-breadcrumb-separator-icon{width:.875rem;height:.875rem}\n"] }]
5037
+ }], ctorParameters: () => [], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }], separator: [{ type: i0.Input, args: [{ isSignal: true, alias: "separator", required: false }] }], useService: [{ type: i0.Input, args: [{ isSignal: true, alias: "useService", required: false }] }] } });
5038
+
3832
5039
  /* Theme — import theme.css in your global styles.css:
3833
5040
  @import '@shirkasoft/ui-components/theme.css';
3834
5041
  Override --shk-* CSS variables at :root or .dark to theme.
@@ -3838,5 +5045,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImpo
3838
5045
  * Generated bundle index. Do not edit.
3839
5046
  */
3840
5047
 
3841
- export { COLOR_PALETTE, ChartComponent, ConfirmDialogComponent, ConfirmDialogService, DEFAULT_MARKER_GRADIENTS, DEFAULT_SUPPLIER_ICON_COLORS, DEFAULT_SUPPLIER_TYPE_ICONS, DatePickerComponent, FieldsetComponent, FileUploadComponent, ModalComponent, ModalService, NotificationComponent, NotificationService, PriceInputComponent, RailSidebarComponent, SelectComponent, ShkWizardStepContent, SidebarComponent, TableComponent, TextFieldComponent, TimelineComponent, ToggleComponent, TooltipComponent, WizardComponent, readThemeColors };
5048
+ export { BreadcrumbComponent, BreadcrumbService, ButtonComponent, COLOR_PALETTE, ChartComponent, ConfirmDialogComponent, ConfirmDialogService, DEFAULT_MARKER_GRADIENTS, DEFAULT_SUPPLIER_ICON_COLORS, DEFAULT_SUPPLIER_TYPE_ICONS, DatePickerComponent, FieldsetComponent, FileUploadComponent, KnobComponent, LoadingBarComponent, LoadingDotsComponent, LoadingOverlayComponent, LoadingSpinnerComponent, ModalComponent, ModalService, NotificationComponent, NotificationService, PriceInputComponent, RailSidebarComponent, SelectComponent, ShkWizardStepContent, SidebarComponent, SkeletonComponent, TabComponent, TabPanelComponent, TableComponent, TablistComponent, TabpanelsComponent, TabsComponent, TagComponent, TextFieldComponent, TimelineComponent, ToggleComponent, TooltipComponent, WizardComponent, readThemeColors };
3842
5049
  //# sourceMappingURL=shirkasoft-ui-components.mjs.map