shirkasoft-ui-components 1.1.1 → 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.
- package/README.md +216 -0
- package/fesm2022/shirkasoft-ui-components.mjs +1476 -192
- package/fesm2022/shirkasoft-ui-components.mjs.map +1 -1
- package/package.json +1 -1
- package/src/theme.css +264 -63
- package/types/shirkasoft-ui-components.d.ts +396 -15
|
@@ -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,
|
|
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('
|
|
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,
|
|
@@ -2230,12 +2500,32 @@ function getIcon$5(name) {
|
|
|
2230
2500
|
return icons$3[name] ?? LucidePlus;
|
|
2231
2501
|
}
|
|
2232
2502
|
class TableComponent {
|
|
2503
|
+
getColumnWidth(col) {
|
|
2504
|
+
return col.width || this.DEFAULT_COLUMN_WIDTH;
|
|
2505
|
+
}
|
|
2233
2506
|
constructor() {
|
|
2234
2507
|
this.fb = inject(FormBuilder);
|
|
2235
2508
|
this.cdr = inject(ChangeDetectorRef);
|
|
2236
2509
|
this.transloco = inject(TranslocoService);
|
|
2510
|
+
this.destroyRef = inject(DestroyRef);
|
|
2237
2511
|
this.getIcon = getIcon$5;
|
|
2238
|
-
|
|
2512
|
+
/**
|
|
2513
|
+
* FIX responsive: ancho mínimo que toma cada columna (excepto 'actions',
|
|
2514
|
+
* que ya tiene su propio ancho calculado) SOLO en pantallas angostas
|
|
2515
|
+
* (ver el media query en `styles`). En pantallas anchas esta variable no
|
|
2516
|
+
* se usa: las columnas se estiran normalmente al 100% del contenedor,
|
|
2517
|
+
* igual que antes. Ajustable por columna vía `col.width` si alguna
|
|
2518
|
+
* necesita más o menos espacio en mobile.
|
|
2519
|
+
*/
|
|
2520
|
+
this.DEFAULT_COLUMN_WIDTH = '140px';
|
|
2521
|
+
this.actionsHeaderLabel = toSignal(this.transloco.selectTranslate('common.actions'), {
|
|
2522
|
+
initialValue: 'Acciones',
|
|
2523
|
+
});
|
|
2524
|
+
// FIX #14: idioma como señal para que los textos traducidos de forma
|
|
2525
|
+
// imperativa (transloco.translate) se recalculen al cambiar de idioma.
|
|
2526
|
+
this.currentLang = toSignal(this.transloco.langChanges$, {
|
|
2527
|
+
initialValue: this.transloco.getActiveLang(),
|
|
2528
|
+
});
|
|
2239
2529
|
this.data = input([], ...(ngDevMode ? [{ debugName: "data" }] : /* istanbul ignore next */ []));
|
|
2240
2530
|
this.columns = input([], ...(ngDevMode ? [{ debugName: "columns" }] : /* istanbul ignore next */ []));
|
|
2241
2531
|
this.rowsPerPage = input(10, ...(ngDevMode ? [{ debugName: "rowsPerPage" }] : /* istanbul ignore next */ []));
|
|
@@ -2273,18 +2563,21 @@ class TableComponent {
|
|
|
2273
2563
|
this.rowsPerPageLocal = signal(10, ...(ngDevMode ? [{ debugName: "rowsPerPageLocal" }] : /* istanbul ignore next */ []));
|
|
2274
2564
|
this.totalPages = computed(() => Math.max(1, Math.ceil(this.effectiveTotalRecords() / this.rowsPerPageLocal())), ...(ngDevMode ? [{ debugName: "totalPages" }] : /* istanbul ignore next */ []));
|
|
2275
2565
|
this.currentPage = computed(() => Math.floor(this.first() / this.rowsPerPageLocal()) + 1, ...(ngDevMode ? [{ debugName: "currentPage" }] : /* istanbul ignore next */ []));
|
|
2566
|
+
// FIX #4 (medio): debounce también para el buscador global en modo servidor.
|
|
2276
2567
|
this.filterSubject = new Subject();
|
|
2568
|
+
this.searchSubject = new Subject();
|
|
2569
|
+
// FIX #3/#crítico: para evitar reconstruir el FormGroup (y perder lo escrito
|
|
2570
|
+
// por el usuario) cuando `columns()` cambia de referencia pero el set de
|
|
2571
|
+
// campos filtrables es el mismo.
|
|
2572
|
+
this.lastFilterFieldsSignature = '';
|
|
2573
|
+
this.columnFilterSubscriptions = [];
|
|
2277
2574
|
this.columnsWithActions = computed(() => {
|
|
2278
2575
|
const columnsArray = this.columns();
|
|
2279
2576
|
const showAction = this.showActionRow();
|
|
2280
2577
|
const rowActionsArray = this.rowActions();
|
|
2281
2578
|
const translatedHeader = this.actionsHeaderLabel();
|
|
2282
2579
|
if (showAction && rowActionsArray.length > 0) {
|
|
2283
|
-
const actionsWidth = rowActionsArray.length <= 2
|
|
2284
|
-
? '110px'
|
|
2285
|
-
: rowActionsArray.length <= 3
|
|
2286
|
-
? '130px'
|
|
2287
|
-
: '170px';
|
|
2580
|
+
const actionsWidth = rowActionsArray.length <= 2 ? '110px' : rowActionsArray.length <= 3 ? '130px' : '170px';
|
|
2288
2581
|
return [
|
|
2289
2582
|
{
|
|
2290
2583
|
field: 'actions',
|
|
@@ -2303,25 +2596,35 @@ class TableComponent {
|
|
|
2303
2596
|
this.effectiveDisplayedData = computed(() => {
|
|
2304
2597
|
return this.serverSide() ? this.data() : this.displayedData();
|
|
2305
2598
|
}, ...(ngDevMode ? [{ debugName: "effectiveDisplayedData" }] : /* istanbul ignore next */ []));
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
const
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2599
|
+
// FIX #14: pageReport ahora es un computed reactivo (incluye idioma como
|
|
2600
|
+
// dependencia) en vez de un método imperativo llamado desde el template.
|
|
2601
|
+
this.pageReport = computed(() => {
|
|
2602
|
+
this.currentLang(); // fuerza recálculo al cambiar de idioma
|
|
2603
|
+
const total = this.effectiveTotalRecords();
|
|
2604
|
+
if (total === 0) {
|
|
2605
|
+
return this.transloco.translate('table.pageReport', {
|
|
2606
|
+
from: 0,
|
|
2607
|
+
to: 0,
|
|
2608
|
+
total: 0,
|
|
2609
|
+
});
|
|
2610
|
+
}
|
|
2611
|
+
const from = this.first() + 1;
|
|
2612
|
+
const to = Math.min(this.first() + this.rowsPerPageLocal(), total);
|
|
2613
|
+
return this.transloco.translate('table.pageReport', { from, to, total });
|
|
2614
|
+
}, ...(ngDevMode ? [{ debugName: "pageReport" }] : /* istanbul ignore next */ []));
|
|
2319
2615
|
this.columnFiltersForm = this.fb.group({});
|
|
2320
|
-
this.filterSubject
|
|
2616
|
+
this.filterSubject
|
|
2617
|
+
.pipe(debounceTime(300), takeUntilDestroyed(this.destroyRef))
|
|
2618
|
+
.subscribe(() => {
|
|
2321
2619
|
if (this.serverSide()) {
|
|
2322
2620
|
this.emitFilterChange();
|
|
2323
2621
|
}
|
|
2324
2622
|
});
|
|
2623
|
+
this.searchSubject
|
|
2624
|
+
.pipe(debounceTime(300), takeUntilDestroyed(this.destroyRef))
|
|
2625
|
+
.subscribe((value) => {
|
|
2626
|
+
this.searchChange.emit(value);
|
|
2627
|
+
});
|
|
2325
2628
|
effect(() => {
|
|
2326
2629
|
const newData = this.data();
|
|
2327
2630
|
untracked(() => {
|
|
@@ -2351,14 +2654,6 @@ class TableComponent {
|
|
|
2351
2654
|
});
|
|
2352
2655
|
}
|
|
2353
2656
|
});
|
|
2354
|
-
effect(() => {
|
|
2355
|
-
const activeFilters = this.activeFilters();
|
|
2356
|
-
untracked(() => {
|
|
2357
|
-
if (!this.serverSide()) {
|
|
2358
|
-
this.applyFilters();
|
|
2359
|
-
}
|
|
2360
|
-
});
|
|
2361
|
-
});
|
|
2362
2657
|
effect(() => {
|
|
2363
2658
|
const query = this.searchQuery();
|
|
2364
2659
|
untracked(() => {
|
|
@@ -2371,19 +2666,50 @@ class TableComponent {
|
|
|
2371
2666
|
this.rowsPerPageLocal.set(this.rowsPerPage());
|
|
2372
2667
|
});
|
|
2373
2668
|
}
|
|
2669
|
+
/**
|
|
2670
|
+
* FIX crítico: antes se reconstruía el FormGroup completo (perdiendo el
|
|
2671
|
+
* texto escrito por el usuario en los filtros y el estado de visibilidad
|
|
2672
|
+
* de cada filtro) cada vez que `columns()` cambiaba de referencia, aunque
|
|
2673
|
+
* el conjunto de campos filtrables fuera idéntico. Ahora comparamos una
|
|
2674
|
+
* "firma" del set de campos+tipo de filtro y solo reconstruimos si
|
|
2675
|
+
* realmente cambió. Además, las suscripciones previas se limpian antes de
|
|
2676
|
+
* crear las nuevas para no acumular fugas de memoria.
|
|
2677
|
+
*/
|
|
2374
2678
|
setupColumnFilters(cols) {
|
|
2679
|
+
const filterableCols = cols.filter((c) => c.filter);
|
|
2680
|
+
const signature = filterableCols.map((c) => `${c.field}:${c.filterType ?? 'text'}`).join('|');
|
|
2681
|
+
if (signature === this.lastFilterFieldsSignature && this.columnFiltersForm) {
|
|
2682
|
+
return;
|
|
2683
|
+
}
|
|
2684
|
+
this.lastFilterFieldsSignature = signature;
|
|
2685
|
+
// Limpiar suscripciones anteriores antes de reconstruir.
|
|
2686
|
+
this.columnFilterSubscriptions.forEach((sub) => sub.unsubscribe());
|
|
2687
|
+
this.columnFilterSubscriptions = [];
|
|
2688
|
+
const previousValues = {};
|
|
2689
|
+
if (this.columnFiltersForm) {
|
|
2690
|
+
Object.keys(this.columnFiltersForm.controls).forEach((field) => {
|
|
2691
|
+
previousValues[field] = this.columnFiltersForm.get(field)?.value;
|
|
2692
|
+
});
|
|
2693
|
+
}
|
|
2375
2694
|
const filterControls = {};
|
|
2376
|
-
const filterVisibility = {};
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2695
|
+
const filterVisibility = { ...this.showFilterInput() };
|
|
2696
|
+
filterableCols.forEach((col) => {
|
|
2697
|
+
const defaultValue = col.filterType === 'select' ? null : '';
|
|
2698
|
+
const preserved = previousValues.hasOwnProperty(col.field)
|
|
2699
|
+
? previousValues[col.field]
|
|
2700
|
+
: defaultValue;
|
|
2701
|
+
filterControls[col.field] = [preserved];
|
|
2702
|
+
if (!(col.field in filterVisibility)) {
|
|
2380
2703
|
filterVisibility[col.field] = false;
|
|
2381
2704
|
}
|
|
2382
2705
|
});
|
|
2383
2706
|
this.columnFiltersForm = this.fb.group(filterControls);
|
|
2384
2707
|
this.showFilterInput.set(filterVisibility);
|
|
2385
2708
|
Object.keys(this.columnFiltersForm.controls).forEach((field) => {
|
|
2386
|
-
this.columnFiltersForm
|
|
2709
|
+
const sub = this.columnFiltersForm
|
|
2710
|
+
.get(field)
|
|
2711
|
+
?.valueChanges.pipe(takeUntilDestroyed(this.destroyRef))
|
|
2712
|
+
.subscribe(() => {
|
|
2387
2713
|
this.first.set(0);
|
|
2388
2714
|
if (this.serverSide()) {
|
|
2389
2715
|
this.filterSubject.next();
|
|
@@ -2393,6 +2719,9 @@ class TableComponent {
|
|
|
2393
2719
|
}
|
|
2394
2720
|
this.cdr.markForCheck();
|
|
2395
2721
|
});
|
|
2722
|
+
if (sub) {
|
|
2723
|
+
this.columnFilterSubscriptions.push(sub);
|
|
2724
|
+
}
|
|
2396
2725
|
});
|
|
2397
2726
|
}
|
|
2398
2727
|
emitFilterChange() {
|
|
@@ -2476,6 +2805,13 @@ class TableComponent {
|
|
|
2476
2805
|
this.updateDisplayedData();
|
|
2477
2806
|
}
|
|
2478
2807
|
}
|
|
2808
|
+
/**
|
|
2809
|
+
* FIX: se documenta explícitamente el criterio de valores null/undefined.
|
|
2810
|
+
* Se mantienen SIEMPRE al final de la lista, sin importar la dirección del
|
|
2811
|
+
* orden (comportamiento común e intencional en tablas de datos). Si en tu
|
|
2812
|
+
* caso de uso prefieres que "sigan" la dirección del sort, multiplica
|
|
2813
|
+
* ambos returns por `order`.
|
|
2814
|
+
*/
|
|
2479
2815
|
applySort() {
|
|
2480
2816
|
const field = this.sortField();
|
|
2481
2817
|
const order = this.sortOrder();
|
|
@@ -2485,8 +2821,10 @@ class TableComponent {
|
|
|
2485
2821
|
data.sort((a, b) => {
|
|
2486
2822
|
const va = a[field];
|
|
2487
2823
|
const vb = b[field];
|
|
2824
|
+
if (va == null && vb == null)
|
|
2825
|
+
return 0;
|
|
2488
2826
|
if (va == null)
|
|
2489
|
-
return 1;
|
|
2827
|
+
return 1; // nulls siempre al final
|
|
2490
2828
|
if (vb == null)
|
|
2491
2829
|
return -1;
|
|
2492
2830
|
if (va < vb)
|
|
@@ -2501,7 +2839,7 @@ class TableComponent {
|
|
|
2501
2839
|
this.searchQuery.set(value);
|
|
2502
2840
|
this.first.set(0);
|
|
2503
2841
|
if (this.serverSide()) {
|
|
2504
|
-
this.
|
|
2842
|
+
this.searchSubject.next(value);
|
|
2505
2843
|
}
|
|
2506
2844
|
}
|
|
2507
2845
|
clearFilter(field) {
|
|
@@ -2517,17 +2855,18 @@ class TableComponent {
|
|
|
2517
2855
|
this.showFilterInput.set({ ...visibility, [field]: !visibility[field] });
|
|
2518
2856
|
this.cdr.markForCheck();
|
|
2519
2857
|
}
|
|
2858
|
+
/**
|
|
2859
|
+
* FIX #5 (falsy zero): antes el template usaba `columnFiltersForm.get(field)?.value`
|
|
2860
|
+
* directamente en un `@if`/`[class]`, lo cual falla cuando el valor es `0`
|
|
2861
|
+
* (número), ya que `0` es falsy en JS. Este helper compara explícitamente
|
|
2862
|
+
* contra null/undefined/''.
|
|
2863
|
+
*/
|
|
2864
|
+
hasFilterValue(field) {
|
|
2865
|
+
const value = this.columnFiltersForm.get(field)?.value;
|
|
2866
|
+
return value !== null && value !== undefined && value !== '';
|
|
2867
|
+
}
|
|
2520
2868
|
getPageReport() {
|
|
2521
|
-
|
|
2522
|
-
if (total === 0)
|
|
2523
|
-
return this.transloco.translate('table.pageReport', {
|
|
2524
|
-
from: 0,
|
|
2525
|
-
to: 0,
|
|
2526
|
-
total: 0,
|
|
2527
|
-
});
|
|
2528
|
-
const from = this.first() + 1;
|
|
2529
|
-
const to = Math.min(this.first() + this.rowsPerPageLocal(), total);
|
|
2530
|
-
return this.transloco.translate('table.pageReport', { from, to, total });
|
|
2869
|
+
return this.pageReport();
|
|
2531
2870
|
}
|
|
2532
2871
|
goToFirst() {
|
|
2533
2872
|
const rows = this.rowsPerPageLocal();
|
|
@@ -2545,9 +2884,7 @@ class TableComponent {
|
|
|
2545
2884
|
}
|
|
2546
2885
|
}
|
|
2547
2886
|
goToLast() {
|
|
2548
|
-
const total = this.serverSide()
|
|
2549
|
-
? this.totalRecords()
|
|
2550
|
-
: this.filteredData().length;
|
|
2887
|
+
const total = this.serverSide() ? this.totalRecords() : this.filteredData().length;
|
|
2551
2888
|
const rows = this.rowsPerPageLocal();
|
|
2552
2889
|
const lastFirst = Math.max(0, Math.ceil(total / rows) * rows - rows);
|
|
2553
2890
|
this.first.set(lastFirst);
|
|
@@ -2591,9 +2928,7 @@ class TableComponent {
|
|
|
2591
2928
|
return this.first() + this.rowsPerPageLocal() >= total;
|
|
2592
2929
|
}
|
|
2593
2930
|
next() {
|
|
2594
|
-
const total = this.serverSide()
|
|
2595
|
-
? this.totalRecords()
|
|
2596
|
-
: this.filteredData().length;
|
|
2931
|
+
const total = this.serverSide() ? this.totalRecords() : this.filteredData().length;
|
|
2597
2932
|
const rows = this.rowsPerPageLocal();
|
|
2598
2933
|
let first = this.first() + rows;
|
|
2599
2934
|
if (first >= total) {
|
|
@@ -2616,9 +2951,7 @@ class TableComponent {
|
|
|
2616
2951
|
prev() {
|
|
2617
2952
|
const rows = this.rowsPerPageLocal();
|
|
2618
2953
|
let first = this.first() - rows;
|
|
2619
|
-
const total = this.serverSide()
|
|
2620
|
-
? this.totalRecords()
|
|
2621
|
-
: this.filteredData().length;
|
|
2954
|
+
const total = this.serverSide() ? this.totalRecords() : this.filteredData().length;
|
|
2622
2955
|
if (first < 0) {
|
|
2623
2956
|
first = 0;
|
|
2624
2957
|
}
|
|
@@ -2640,21 +2973,15 @@ class TableComponent {
|
|
|
2640
2973
|
this.goToFirst();
|
|
2641
2974
|
}
|
|
2642
2975
|
getRowActionLabel(action, rowData) {
|
|
2643
|
-
return typeof action.label === 'function'
|
|
2644
|
-
? action.label(rowData)
|
|
2645
|
-
: action.label;
|
|
2976
|
+
return typeof action.label === 'function' ? action.label(rowData) : action.label;
|
|
2646
2977
|
}
|
|
2647
2978
|
getRowActionIconName(action, rowData) {
|
|
2648
|
-
return typeof action.icon === 'function'
|
|
2649
|
-
? action.icon(rowData)
|
|
2650
|
-
: action.icon;
|
|
2979
|
+
return typeof action.icon === 'function' ? action.icon(rowData) : action.icon;
|
|
2651
2980
|
}
|
|
2652
2981
|
getRowActionClass(action, rowData) {
|
|
2653
2982
|
if (!action.class)
|
|
2654
2983
|
return '';
|
|
2655
|
-
return typeof action.class === 'function'
|
|
2656
|
-
? action.class(rowData)
|
|
2657
|
-
: action.class;
|
|
2984
|
+
return typeof action.class === 'function' ? action.class(rowData) : action.class;
|
|
2658
2985
|
}
|
|
2659
2986
|
isHeaderActionDisabled(action) {
|
|
2660
2987
|
return action.isDisabled ? action.isDisabled() : false;
|
|
@@ -2674,22 +3001,54 @@ class TableComponent {
|
|
|
2674
3001
|
const str = String(value);
|
|
2675
3002
|
return str.length > limit ? str.slice(0, limit) + '\u2026' : str;
|
|
2676
3003
|
}
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
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) || {} : {};
|
|
3024
|
+
}
|
|
3025
|
+
/**
|
|
3026
|
+
* FIX #7 (medio): trackBy real por identidad de fila en vez de `$index`.
|
|
3027
|
+
* Si tu dato no tiene `id`/`_id`, cae de vuelta al índice (comportamiento
|
|
3028
|
+
* anterior), pero se recomienda pasar filas con un identificador único.
|
|
3029
|
+
*/
|
|
3030
|
+
trackByRow(row, index) {
|
|
3031
|
+
return row?.id ?? row?._id ?? index;
|
|
3032
|
+
}
|
|
3033
|
+
/** FIX accesibilidad: soporte de teclado (Enter/Espacio) para ordenar columnas. */
|
|
3034
|
+
onHeaderKeydown(event, col) {
|
|
3035
|
+
if (col.field === 'actions' || !col.sortable)
|
|
3036
|
+
return;
|
|
3037
|
+
if (event.key === 'Enter' || event.key === ' ') {
|
|
3038
|
+
event.preventDefault();
|
|
3039
|
+
this.toggleSort(col.field);
|
|
2689
3040
|
}
|
|
2690
3041
|
}
|
|
3042
|
+
/** FIX accesibilidad: valor aria-sort para el <th> correspondiente. */
|
|
3043
|
+
getAriaSort(col) {
|
|
3044
|
+
if (col.field === 'actions' || !col.sortable)
|
|
3045
|
+
return 'none';
|
|
3046
|
+
if (this.sortField() !== col.field)
|
|
3047
|
+
return 'none';
|
|
3048
|
+
return this.sortOrder() === 1 ? 'ascending' : 'descending';
|
|
3049
|
+
}
|
|
2691
3050
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: TableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2692
|
-
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\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\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 <div class=\"absolute inset-y-0 left-0 pl-2.5 flex items-center pointer-events-none\">\n <svg [lucideIcon]=\"getIcon('search')\" class=\"w-3.5 h-3.5 _shk-text-surface-400 dark:[color:var(--shk-surface-500)]\"></svg>\n </div>\n <input\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\n _shk-bg-surface-50 dark:[background-color:var(--shk-surface-100)] placeholder:[color:var(--shk-surface-400)] dark:placeholder:[color:var(--shk-surface-500)]\n _shk-text-surface-700 dark:[color:var(--shk-surface-700)]\n focus:outline-none focus:bg-white dark:focus:[background-color:var(--shk-surface-800)] focus:[border-color:var(--shk-primary-500)] focus:ring-1\n focus:[--tw-ring-color:var(--shk-primary-500)]\"\n />\n </div>\n }\n\n @if (filters().length > 0) {\n <div class=\"flex _shk-bg-surface-100 dark:[background-color:var(--shk-surface-100)] p-0.5 rounded-md ml-auto\">\n @for (f of filters(); track f.value) {\n <button\n type=\"button\"\n (click)=\"onFilterClick(f.value)\"\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]=\"f.value === activeFilter() ? 'bg-white shadow-sm dark:[background-color:var(--shk-surface-700)]' : ''\"\n >\n {{ f.label }}\n </button>\n }\n </div>\n }\n </div>\n\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]=\"action.class || '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 [disabled]=\"isHeaderActionDisabled(action)\"\n (click)=\"action.onClick()\"\n >\n @if (action.icon) {\n <svg\n [lucideIcon]=\"getIcon(action.icon)\"\n class=\"w-3.5 h-3.5\"\n ></svg>\n }\n {{ action.label }}\n </button>\n }\n }\n </div>\n }\n\n </div>\n }\n\n <div\n class=\"card p-0 overflow-hidden\"\n [class.shadow-lg]=\"hasShadow()\"\n >\n\n <div class=\"table-scroll-wrapper _shk-scrollbar\">\n <table class=\"w-full\">\n <thead>\n <tr>\n @for (col of columnsWithActions(); track col.field) {\n <th\n (click)=\"col.field !== 'actions' && col.sortable && toggleSort(col.field)\"\n [style.cursor]=\"col.field !== 'actions' && col.sortable ? 'pointer' : 'default'\"\n [style.width]=\"col.width || 'auto'\"\n [style.min-width]=\"col.field === 'actions' ? '130px' : 'auto'\"\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\n _shk-bg-surface-50 dark:[background-color:var(--shk-surface-100)] border-b _shk-border dark:[border-color:var(--shk-surface-200)]\"\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 [lucideIcon]=\"getIcon(sortOrder() === 1 ? 'arrow-up' : 'arrow-down')\"\n class=\"w-3.5 h-3.5 _shk-text-primary\"></svg>\n } @else {\n <svg [lucideIcon]=\"getIcon('arrow-up-down')\"\n class=\"w-3.5 h-3.5 _shk-text-surface-400 dark:[color:var(--shk-surface-500)]\"></svg>\n }\n <span 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 {{ '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 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]=\"columnFiltersForm.get(col.field)?.value\"\n ></svg>\n <span 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 {{ 'table.tooltips.filter' | transloco }}\n </span>\n </button>\n </div>\n }\n </div>\n </div>\n\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 <input\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)]\n focus:[border-color:var(--shk-primary-500)] focus:ring-1 focus:[--tw-ring-color:var(--shk-primary-500)]\n placeholder:[color:var(--shk-surface-400)] dark:placeholder:[color:var(--shk-surface-500)]\"\n [placeholder]=\"col.filterPlaceholder || ('common.search_placeholder' | transloco)\"\n />\n }\n @if (col.filterType === 'select') {\n <select\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\">A/D</option>\n @for (opt of col.filterOptions || []; track opt.value) {\n <option [ngValue]=\"opt.value\">{{ opt.label }}</option>\n }\n </select>\n }\n @if (columnFiltersForm.get(col.field)?.value && 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\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 class=\"flex items-center justify-center\">\n <svg [lucideIcon]=\"getIcon('loader')\" class=\"w-6 h-6 animate-spin _shk-text-primary\"></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 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 <svg [lucideIcon]=\"getIcon('inbox')\" class=\"w-8 h-8 _shk-text-surface-400 dark:[color:var(--shk-surface-500)]\"></svg>\n </div>\n <p class=\"text-lg font-semibold _shk-text-surface-600 dark:[color:var(--shk-surface-300)]\">\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 $index) {\n <tr 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 @for (col of columnsWithActions(); track col.field) {\n <td\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 >\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]=\"getTagClass(col.tagSeverity ? col.tagSeverity(rowData) : undefined)\"\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]=\"'table-action-btn hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)] rounded-md ' + getRowActionClass(action, rowData)\"\n [disabled]=\"isRowActionDisabled(action, rowData) || false\"\n >\n <svg\n [lucideIcon]=\"getIcon(getRowActionIconName(action, rowData))\"\n class=\"w-4 h-4\"\n ></svg>\n </button>\n <span 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 {{ getRowActionLabel(action, rowData) }}\n </span>\n </div>\n }\n }\n </div>\n }\n </td>\n }\n </tr>\n }\n </tbody>\n }\n </table>\n\n <ng-template #defaultTemplate let-rowData let-field=\"field\">\n {{ truncate(rowData[field], 30) }}\n </ng-template>\n </div>\n\n <div 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 <div class=\"text-xs _shk-text-surface-600 dark:[color:var(--shk-surface-400)]\">{{ getPageReport() }}</div>\n <div class=\"flex items-center gap-0.5\">\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 class=\"px-2 text-xs font-medium _shk-text-surface-700 dark:[color:var(--shk-surface-700)]\">{{ currentPage() }} / {{ totalPages() }}</span>\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 class=\"flex items-center gap-1.5 text-xs _shk-text-surface-600 dark:[color:var(--shk-surface-400)]\">\n <span>{{ 'table.rows' | transloco }}</span>\n <select\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\n</div>\n", styles: ["input:focus{outline:none}input[type=text]{font-weight:400!important}table th{white-space:nowrap}table td{white-space:nowrap;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;-webkit-overflow-scrolling:touch}.table-scroll-wrapper table{table-layout:fixed;width:100%}\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 }); }
|
|
2693
3052
|
}
|
|
2694
3053
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: TableComponent, decorators: [{
|
|
2695
3054
|
type: Component,
|
|
@@ -2698,118 +3057,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImpo
|
|
|
2698
3057
|
ReactiveFormsModule,
|
|
2699
3058
|
TranslocoModule,
|
|
2700
3059
|
LucideDynamicIcon,
|
|
2701
|
-
], template: "<div class=\"space-y-2 min-[1440px]:space-y-3 mt-2 min-[1440px]:mt-3 animate-fade-in pb-8\">\n\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\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 <div class=\"absolute inset-y-0 left-0 pl-2.5 flex items-center pointer-events-none\">\n <svg [lucideIcon]=\"getIcon('search')\" class=\"w-3.5 h-3.5 _shk-text-surface-400 dark:[color:var(--shk-surface-500)]\"></svg>\n </div>\n <input\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\n _shk-bg-surface-50 dark:[background-color:var(--shk-surface-100)] placeholder:[color:var(--shk-surface-400)] dark:placeholder:[color:var(--shk-surface-500)]\n _shk-text-surface-700 dark:[color:var(--shk-surface-700)]\n focus:outline-none focus:bg-white dark:focus:[background-color:var(--shk-surface-800)] focus:[border-color:var(--shk-primary-500)] focus:ring-1\n focus:[--tw-ring-color:var(--shk-primary-500)]\"\n />\n </div>\n }\n\n @if (filters().length > 0) {\n <div class=\"flex _shk-bg-surface-100 dark:[background-color:var(--shk-surface-100)] p-0.5 rounded-md ml-auto\">\n @for (f of filters(); track f.value) {\n <button\n type=\"button\"\n (click)=\"onFilterClick(f.value)\"\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]=\"f.value === activeFilter() ? 'bg-white shadow-sm dark:[background-color:var(--shk-surface-700)]' : ''\"\n >\n {{ f.label }}\n </button>\n }\n </div>\n }\n </div>\n\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]=\"action.class || '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 [disabled]=\"isHeaderActionDisabled(action)\"\n (click)=\"action.onClick()\"\n >\n @if (action.icon) {\n <svg\n [lucideIcon]=\"getIcon(action.icon)\"\n class=\"w-3.5 h-3.5\"\n ></svg>\n }\n {{ action.label }}\n </button>\n }\n }\n </div>\n }\n\n </div>\n }\n\n <div\n class=\"card p-0 overflow-hidden\"\n [class.shadow-lg]=\"hasShadow()\"\n >\n\n <div class=\"table-scroll-wrapper _shk-scrollbar\">\n <table class=\"w-full\">\n <thead>\n <tr>\n @for (col of columnsWithActions(); track col.field) {\n <th\n (click)=\"col.field !== 'actions' && col.sortable && toggleSort(col.field)\"\n [style.cursor]=\"col.field !== 'actions' && col.sortable ? 'pointer' : 'default'\"\n [style.width]=\"col.width || 'auto'\"\n [style.min-width]=\"col.field === 'actions' ? '130px' : 'auto'\"\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\n _shk-bg-surface-50 dark:[background-color:var(--shk-surface-100)] border-b _shk-border dark:[border-color:var(--shk-surface-200)]\"\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 [lucideIcon]=\"getIcon(sortOrder() === 1 ? 'arrow-up' : 'arrow-down')\"\n class=\"w-3.5 h-3.5 _shk-text-primary\"></svg>\n } @else {\n <svg [lucideIcon]=\"getIcon('arrow-up-down')\"\n class=\"w-3.5 h-3.5 _shk-text-surface-400 dark:[color:var(--shk-surface-500)]\"></svg>\n }\n <span 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 {{ '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 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]=\"columnFiltersForm.get(col.field)?.value\"\n ></svg>\n <span 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 {{ 'table.tooltips.filter' | transloco }}\n </span>\n </button>\n </div>\n }\n </div>\n </div>\n\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 <input\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)]\n focus:[border-color:var(--shk-primary-500)] focus:ring-1 focus:[--tw-ring-color:var(--shk-primary-500)]\n placeholder:[color:var(--shk-surface-400)] dark:placeholder:[color:var(--shk-surface-500)]\"\n [placeholder]=\"col.filterPlaceholder || ('common.search_placeholder' | transloco)\"\n />\n }\n @if (col.filterType === 'select') {\n <select\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\">A/D</option>\n @for (opt of col.filterOptions || []; track opt.value) {\n <option [ngValue]=\"opt.value\">{{ opt.label }}</option>\n }\n </select>\n }\n @if (columnFiltersForm.get(col.field)?.value && 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\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 class=\"flex items-center justify-center\">\n <svg [lucideIcon]=\"getIcon('loader')\" class=\"w-6 h-6 animate-spin _shk-text-primary\"></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 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 <svg [lucideIcon]=\"getIcon('inbox')\" class=\"w-8 h-8 _shk-text-surface-400 dark:[color:var(--shk-surface-500)]\"></svg>\n </div>\n <p class=\"text-lg font-semibold _shk-text-surface-600 dark:[color:var(--shk-surface-300)]\">\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 $index) {\n <tr 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 @for (col of columnsWithActions(); track col.field) {\n <td\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 >\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]=\"getTagClass(col.tagSeverity ? col.tagSeverity(rowData) : undefined)\"\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]=\"'table-action-btn hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)] rounded-md ' + getRowActionClass(action, rowData)\"\n [disabled]=\"isRowActionDisabled(action, rowData) || false\"\n >\n <svg\n [lucideIcon]=\"getIcon(getRowActionIconName(action, rowData))\"\n class=\"w-4 h-4\"\n ></svg>\n </button>\n <span 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 {{ getRowActionLabel(action, rowData) }}\n </span>\n </div>\n }\n }\n </div>\n }\n </td>\n }\n </tr>\n }\n </tbody>\n }\n </table>\n\n <ng-template #defaultTemplate let-rowData let-field=\"field\">\n {{ truncate(rowData[field], 30) }}\n </ng-template>\n </div>\n\n <div 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 <div class=\"text-xs _shk-text-surface-600 dark:[color:var(--shk-surface-400)]\">{{ getPageReport() }}</div>\n <div class=\"flex items-center gap-0.5\">\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 class=\"px-2 text-xs font-medium _shk-text-surface-700 dark:[color:var(--shk-surface-700)]\">{{ currentPage() }} / {{ totalPages() }}</span>\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 class=\"flex items-center gap-1.5 text-xs _shk-text-surface-600 dark:[color:var(--shk-surface-400)]\">\n <span>{{ 'table.rows' | transloco }}</span>\n <select\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\n</div>\n", styles: ["input:focus{outline:none}input[type=text]{font-weight:400!important}table th{white-space:nowrap}table td{white-space:nowrap;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;-webkit-overflow-scrolling:touch}.table-scroll-wrapper table{table-layout:fixed;width:100%}\n"] }]
|
|
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"] }]
|
|
2702
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"] }] } });
|
|
2703
3064
|
|
|
2704
|
-
class TooltipComponent {
|
|
2705
|
-
constructor() {
|
|
2706
|
-
this.text = input('', ...(ngDevMode ? [{ debugName: "text" }] : /* istanbul ignore next */ []));
|
|
2707
|
-
this.position = input('bottom', ...(ngDevMode ? [{ debugName: "position" }] : /* istanbul ignore next */ []));
|
|
2708
|
-
this.mode = input('hover', ...(ngDevMode ? [{ debugName: "mode" }] : /* istanbul ignore next */ []));
|
|
2709
|
-
this.offset = input(8, ...(ngDevMode ? [{ debugName: "offset" }] : /* istanbul ignore next */ []));
|
|
2710
|
-
this.elementRef = inject(ElementRef);
|
|
2711
|
-
this.tooltipX = signal(0, ...(ngDevMode ? [{ debugName: "tooltipX" }] : /* istanbul ignore next */ []));
|
|
2712
|
-
this.tooltipY = signal(0, ...(ngDevMode ? [{ debugName: "tooltipY" }] : /* istanbul ignore next */ []));
|
|
2713
|
-
}
|
|
2714
|
-
onMouseEnter() {
|
|
2715
|
-
if (this.mode() !== 'fixed')
|
|
2716
|
-
return;
|
|
2717
|
-
const host = this.elementRef.nativeElement;
|
|
2718
|
-
const container = host.querySelector('.group');
|
|
2719
|
-
if (!container)
|
|
2720
|
-
return;
|
|
2721
|
-
const rect = container.getBoundingClientRect();
|
|
2722
|
-
const off = this.offset();
|
|
2723
|
-
if (this.position() === 'right') {
|
|
2724
|
-
this.tooltipX.set(rect.right + off);
|
|
2725
|
-
this.tooltipY.set(rect.top + rect.height / 2);
|
|
2726
|
-
}
|
|
2727
|
-
else if (this.position() === 'bottom') {
|
|
2728
|
-
this.tooltipX.set(rect.left + rect.width / 2);
|
|
2729
|
-
this.tooltipY.set(rect.bottom + off);
|
|
2730
|
-
}
|
|
2731
|
-
else {
|
|
2732
|
-
this.tooltipX.set(rect.left + rect.width / 2);
|
|
2733
|
-
this.tooltipY.set(rect.top - off);
|
|
2734
|
-
}
|
|
2735
|
-
}
|
|
2736
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: TooltipComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2737
|
-
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: `
|
|
2738
|
-
<div class="relative group inline-flex">
|
|
2739
|
-
<ng-content></ng-content>
|
|
2740
|
-
@if (text() && mode() === 'hover') {
|
|
2741
|
-
<span
|
|
2742
|
-
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"
|
|
2743
|
-
[class.top-full]="position() === 'bottom'"
|
|
2744
|
-
[class.bottom-full]="position() === 'top'"
|
|
2745
|
-
[class.left-full]="position() === 'right'"
|
|
2746
|
-
[class.mt-1]="position() === 'bottom'"
|
|
2747
|
-
[class.mb-1]="position() === 'top'"
|
|
2748
|
-
[class.ml-1]="position() === 'right'"
|
|
2749
|
-
[class.left-1/2]="position() !== 'right'"
|
|
2750
|
-
[class.-translate-x-1/2]="position() !== 'right'"
|
|
2751
|
-
[class.top-1/2]="position() === 'right'"
|
|
2752
|
-
[class.-translate-y-1/2]="position() === 'right'"
|
|
2753
|
-
>
|
|
2754
|
-
{{ text() }}
|
|
2755
|
-
</span>
|
|
2756
|
-
}
|
|
2757
|
-
@if (text() && mode() === 'fixed') {
|
|
2758
|
-
<span
|
|
2759
|
-
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"
|
|
2760
|
-
[style.top.px]="tooltipY()"
|
|
2761
|
-
[style.left.px]="tooltipX()"
|
|
2762
|
-
[style.transform]="position() === 'right' ? 'translateY(-50%)' : ''"
|
|
2763
|
-
>
|
|
2764
|
-
{{ text() }}
|
|
2765
|
-
</span>
|
|
2766
|
-
}
|
|
2767
|
-
</div>
|
|
2768
|
-
`, isInline: true }); }
|
|
2769
|
-
}
|
|
2770
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: TooltipComponent, decorators: [{
|
|
2771
|
-
type: Component,
|
|
2772
|
-
args: [{
|
|
2773
|
-
selector: 'shk-tooltip',
|
|
2774
|
-
standalone: true,
|
|
2775
|
-
template: `
|
|
2776
|
-
<div class="relative group inline-flex">
|
|
2777
|
-
<ng-content></ng-content>
|
|
2778
|
-
@if (text() && mode() === 'hover') {
|
|
2779
|
-
<span
|
|
2780
|
-
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"
|
|
2781
|
-
[class.top-full]="position() === 'bottom'"
|
|
2782
|
-
[class.bottom-full]="position() === 'top'"
|
|
2783
|
-
[class.left-full]="position() === 'right'"
|
|
2784
|
-
[class.mt-1]="position() === 'bottom'"
|
|
2785
|
-
[class.mb-1]="position() === 'top'"
|
|
2786
|
-
[class.ml-1]="position() === 'right'"
|
|
2787
|
-
[class.left-1/2]="position() !== 'right'"
|
|
2788
|
-
[class.-translate-x-1/2]="position() !== 'right'"
|
|
2789
|
-
[class.top-1/2]="position() === 'right'"
|
|
2790
|
-
[class.-translate-y-1/2]="position() === 'right'"
|
|
2791
|
-
>
|
|
2792
|
-
{{ text() }}
|
|
2793
|
-
</span>
|
|
2794
|
-
}
|
|
2795
|
-
@if (text() && mode() === 'fixed') {
|
|
2796
|
-
<span
|
|
2797
|
-
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"
|
|
2798
|
-
[style.top.px]="tooltipY()"
|
|
2799
|
-
[style.left.px]="tooltipX()"
|
|
2800
|
-
[style.transform]="position() === 'right' ? 'translateY(-50%)' : ''"
|
|
2801
|
-
>
|
|
2802
|
-
{{ text() }}
|
|
2803
|
-
</span>
|
|
2804
|
-
}
|
|
2805
|
-
</div>
|
|
2806
|
-
`,
|
|
2807
|
-
}]
|
|
2808
|
-
}], 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: [{
|
|
2809
|
-
type: HostListener,
|
|
2810
|
-
args: ['mouseenter']
|
|
2811
|
-
}] } });
|
|
2812
|
-
|
|
2813
3065
|
const ICONS$1 = {
|
|
2814
3066
|
house: LucideHouse,
|
|
2815
3067
|
home: LucideHome,
|
|
@@ -3752,6 +4004,1038 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImpo
|
|
|
3752
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"] }]
|
|
3753
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 }] }] } });
|
|
3754
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">></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">></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
|
+
|
|
3755
5039
|
/* Theme — import theme.css in your global styles.css:
|
|
3756
5040
|
@import '@shirkasoft/ui-components/theme.css';
|
|
3757
5041
|
Override --shk-* CSS variables at :root or .dark to theme.
|
|
@@ -3761,5 +5045,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImpo
|
|
|
3761
5045
|
* Generated bundle index. Do not edit.
|
|
3762
5046
|
*/
|
|
3763
5047
|
|
|
3764
|
-
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 };
|
|
3765
5049
|
//# sourceMappingURL=shirkasoft-ui-components.mjs.map
|