shirkasoft-ui-components 1.1.2 → 1.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +216 -0
- package/fesm2022/shirkasoft-ui-components.mjs +1337 -130
- 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 +347 -12
|
@@ -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,
|
|
@@ -2731,19 +3001,26 @@ class TableComponent {
|
|
|
2731
3001
|
const str = String(value);
|
|
2732
3002
|
return str.length > limit ? str.slice(0, limit) + '\u2026' : str;
|
|
2733
3003
|
}
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
3004
|
+
getTagValue(col, rowData) {
|
|
3005
|
+
return col.tagValue ? col.tagValue(rowData) : String(rowData[col.field] ?? '');
|
|
3006
|
+
}
|
|
3007
|
+
getTagSeverity(col, rowData) {
|
|
3008
|
+
const raw = col.tagSeverity ? col.tagSeverity(rowData) : 'secondary';
|
|
3009
|
+
const allowed = ['secondary', 'success', 'info', 'warning', 'danger', 'contrast'];
|
|
3010
|
+
return allowed.includes(raw) ? raw : 'secondary';
|
|
3011
|
+
}
|
|
3012
|
+
getTagRounded(col, rowData) {
|
|
3013
|
+
if (typeof col.tagRounded === 'function')
|
|
3014
|
+
return col.tagRounded(rowData);
|
|
3015
|
+
return col.tagRounded ?? false;
|
|
3016
|
+
}
|
|
3017
|
+
getTagIcon(col, rowData) {
|
|
3018
|
+
if (typeof col.tagIcon === 'function')
|
|
3019
|
+
return col.tagIcon(rowData);
|
|
3020
|
+
return col.tagIcon ?? '';
|
|
3021
|
+
}
|
|
3022
|
+
getTagStyle(col, rowData) {
|
|
3023
|
+
return col.tagStyle ? col.tagStyle(rowData) || {} : {};
|
|
2747
3024
|
}
|
|
2748
3025
|
/**
|
|
2749
3026
|
* FIX #7 (medio): trackBy real por identidad de fila en vez de `$index`.
|
|
@@ -2771,122 +3048,20 @@ class TableComponent {
|
|
|
2771
3048
|
return this.sortOrder() === 1 ? 'ascending' : 'descending';
|
|
2772
3049
|
}
|
|
2773
3050
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: TableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2774
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.18", type: TableComponent, isStandalone: true, selector: "shk-table", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, rowsPerPage: { classPropertyName: "rowsPerPage", publicName: "rowsPerPage", isSignal: true, isRequired: false, transformFunction: null }, rowsPerPageOptions: { classPropertyName: "rowsPerPageOptions", publicName: "rowsPerPageOptions", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, showActionRow: { classPropertyName: "showActionRow", publicName: "showActionRow", isSignal: true, isRequired: false, transformFunction: null }, customTemplates: { classPropertyName: "customTemplates", publicName: "customTemplates", isSignal: true, isRequired: false, transformFunction: null }, headerActions: { classPropertyName: "headerActions", publicName: "headerActions", isSignal: true, isRequired: false, transformFunction: null }, rowActions: { classPropertyName: "rowActions", publicName: "rowActions", isSignal: true, isRequired: false, transformFunction: null }, hasShadow: { classPropertyName: "hasShadow", publicName: "hasShadow", isSignal: true, isRequired: false, transformFunction: null }, defaultSortField: { classPropertyName: "defaultSortField", publicName: "defaultSortField", isSignal: true, isRequired: false, transformFunction: null }, defaultSortOrder: { classPropertyName: "defaultSortOrder", publicName: "defaultSortOrder", isSignal: true, isRequired: false, transformFunction: null }, showSearch: { classPropertyName: "showSearch", publicName: "showSearch", isSignal: true, isRequired: false, transformFunction: null }, searchPlaceholder: { classPropertyName: "searchPlaceholder", publicName: "searchPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, emptyMessage: { classPropertyName: "emptyMessage", publicName: "emptyMessage", isSignal: true, isRequired: false, transformFunction: null }, serverSide: { classPropertyName: "serverSide", publicName: "serverSide", isSignal: true, isRequired: false, transformFunction: null }, totalRecords: { classPropertyName: "totalRecords", publicName: "totalRecords", isSignal: true, isRequired: false, transformFunction: null }, filters: { classPropertyName: "filters", publicName: "filters", isSignal: true, isRequired: false, transformFunction: null }, activeFilter: { classPropertyName: "activeFilter", publicName: "activeFilter", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { refresh: "refresh", pageChange: "pageChange", filterChange: "filterChange", searchChange: "searchChange", filterClick: "filterClick" }, ngImport: i0, template: "<div class=\"space-y-2 min-[1440px]:space-y-3 mt-2 min-[1440px]:mt-3 animate-fade-in pb-8\">\n @if (showSearch() || filters().length > 0 || headerActions().length > 0) {\n <div class=\"card p-2 min-[1440px]:p-2.5 flex flex-row items-center justify-between gap-2\">\n <div class=\"flex items-center gap-2 min-[1440px]:gap-3 flex-1 min-w-0\">\n @if (showSearch()) {\n <div class=\"relative w-full md:w-52 min-[1440px]:w-56\">\n <label [for]=\"'shk-table-search'\" class=\"sr-only\">{{\n searchPlaceholder() || ('common.search_placeholder' | transloco)\n }}</label>\n <div class=\"absolute inset-y-0 left-0 pl-2.5 flex items-center pointer-events-none\">\n <svg\n [lucideIcon]=\"getIcon('search')\"\n class=\"w-3.5 h-3.5 _shk-text-surface-400 dark:[color:var(--shk-surface-500)]\"\n ></svg>\n </div>\n <input\n id=\"shk-table-search\"\n type=\"text\"\n [value]=\"searchQuery()\"\n (input)=\"onSearchInput($any($event.target).value)\"\n [placeholder]=\"searchPlaceholder() || ('common.search_placeholder' | transloco)\"\n class=\"block w-full pl-8 pr-3 py-1.5 text-xs border _shk-border dark:[border-color:var(--shk-surface-200)] rounded-md leading-5 _shk-bg-surface-50 dark:[background-color:var(--shk-surface-100)] placeholder:[color:var(--shk-surface-400)] dark:placeholder:[color:var(--shk-surface-500)] _shk-text-surface-700 dark:[color:var(--shk-surface-700)] focus:outline-none focus:bg-white dark:focus:[background-color:var(--shk-surface-800)] focus:[border-color:var(--shk-primary-500)] focus:ring-1 focus:[--tw-ring-color:var(--shk-primary-500)]\"\n />\n </div>\n }\n @if (filters().length > 0) {\n <div\n class=\"flex _shk-bg-surface-100 dark:[background-color:var(--shk-surface-100)] p-0.5 rounded-md ml-auto\"\n role=\"group\"\n >\n @for (f of filters(); track f.value) {\n <button\n type=\"button\"\n (click)=\"onFilterClick(f.value)\"\n [attr.aria-pressed]=\"f.value === activeFilter()\"\n class=\"px-2.5 min-[1440px]:px-3 py-1 text-xs font-medium rounded _shk-text-surface-700 dark:[color:var(--shk-surface-700)] transition-all\"\n [class]=\"\n f.value === activeFilter()\n ? 'bg-white shadow-sm dark:[background-color:var(--shk-surface-700)]'\n : ''\n \"\n >\n {{ f.label }}\n </button>\n }\n </div>\n }\n </div>\n @if (headerActions().length > 0) {\n <div class=\"flex items-center gap-1.5 shrink-0\">\n @for (action of headerActions(); track $index) {\n @if (isHeaderActionVisible(action)) {\n <button\n type=\"button\"\n [class]=\"\n 'inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-md _shk-bg-primary text-white hover:[background-color:var(--shk-primary-700)] transition-colors disabled:opacity-50 disabled:cursor-not-allowed ' +\n (action.class || '')\n \"\n [disabled]=\"isHeaderActionDisabled(action)\"\n [attr.aria-label]=\"action.label\"\n (click)=\"action.onClick()\"\n >\n @if (action.icon) {\n <svg [lucideIcon]=\"getIcon(action.icon)\" class=\"w-3.5 h-3.5\"></svg>\n }\n {{ action.label }}\n </button>\n }\n }\n </div>\n }\n </div>\n }\n <div class=\"card p-0 overflow-hidden\" [class.shadow-lg]=\"hasShadow()\">\n <div class=\"table-scroll-wrapper _shk-scrollbar\">\n <table>\n <thead>\n <tr>\n @for (col of columnsWithActions(); track col.field) {\n <th\n scope=\"col\"\n (click)=\"col.field !== 'actions' && col.sortable && toggleSort(col.field)\"\n (keydown)=\"onHeaderKeydown($event, col)\"\n [tabindex]=\"col.field !== 'actions' && col.sortable ? 0 : -1\"\n [attr.role]=\"col.field !== 'actions' && col.sortable ? 'button' : null\"\n [attr.aria-sort]=\"getAriaSort(col)\"\n [attr.data-shk-actions]=\"col.field === 'actions' ? '' : null\"\n [style.cursor]=\"col.field !== 'actions' && col.sortable ? 'pointer' : 'default'\"\n [style.width]=\"col.field === 'actions' ? col.width : null\"\n [style.min-width]=\"col.field === 'actions' ? col.width : null\"\n [style.--shk-col-width]=\"col.field !== 'actions' ? getColumnWidth(col) : null\"\n class=\"px-2 min-[1440px]:px-3 py-2 min-[1440px]:py-2.5 text-left text-[10px] min-[1440px]:text-xs font-bold _shk-text-surface-500 dark:[color:var(--shk-surface-400)] uppercase tracking-wider _shk-bg-surface-50 dark:[background-color:var(--shk-surface-100)] border-b _shk-border dark:[border-color:var(--shk-surface-200)] focus:outline-none focus-visible:ring-1 focus-visible:[--tw-ring-color:var(--shk-primary-500)]\"\n >\n @if (col.field !== 'actions') {\n <div class=\"flex items-center justify-between gap-2\">\n <span>{{ col.header }}</span>\n <div class=\"flex items-center gap-1\">\n @if (col.sortable) {\n <div class=\"relative group\">\n @if (sortField() === col.field) {\n <svg\n [lucideIcon]=\"getIcon(sortOrder() === 1 ? 'arrow-up' : 'arrow-down')\"\n class=\"w-3.5 h-3.5 _shk-text-primary\"\n ></svg>\n } @else {\n <svg\n [lucideIcon]=\"getIcon('arrow-up-down')\"\n class=\"w-3.5 h-3.5 _shk-text-surface-400 dark:[color:var(--shk-surface-500)]\"\n ></svg>\n }\n <span\n class=\"absolute top-full left-1/2 -translate-x-1/2 mt-1 px-2 py-1 text-xs _shk-bg-tooltip _shk-text-tooltip rounded whitespace-nowrap opacity-0 group-hover:opacity-100 transition pointer-events-none z-[9999] shadow-lg\"\n >\n {{ 'table.tooltips.sort' | transloco }}\n </span>\n </div>\n }\n @if (col.filter) {\n <div class=\"relative group\">\n <button\n type=\"button\"\n (click)=\"$event.stopPropagation(); toggleFilter(col.field)\"\n [attr.aria-label]=\"'table.tooltips.filter' | transloco\"\n [attr.aria-expanded]=\"showFilterInput()[col.field] || false\"\n class=\"_shk-text-surface-400 dark:[color:var(--shk-surface-500)] hover:[color:var(--shk-surface-600)] dark:hover:[color:var(--shk-surface-300)] transition-colors\"\n >\n <svg\n [lucideIcon]=\"getIcon('filter')\"\n class=\"w-3.5 h-3.5\"\n [class._shk-text-primary]=\"hasFilterValue(col.field)\"\n ></svg>\n <span\n class=\"absolute top-full left-1/2 -translate-x-1/2 mt-1 px-2 py-1 text-xs _shk-bg-tooltip _shk-text-tooltip rounded whitespace-nowrap opacity-0 group-hover:opacity-100 transition pointer-events-none z-[9999] shadow-lg\"\n >\n {{ 'table.tooltips.filter' | transloco }}\n </span>\n </button>\n </div>\n }\n </div>\n </div>\n @if (col.filter && showFilterInput()[col.field]) {\n <div\n class=\"mt-2\"\n [formGroup]=\"columnFiltersForm\"\n (click)=\"$event.stopPropagation()\"\n >\n <div class=\"relative\">\n @if (col.filterType !== 'select') {\n <label [for]=\"'shk-filter-' + col.field\" class=\"sr-only\">{{\n col.filterPlaceholder || ('common.search_placeholder' | transloco)\n }}</label>\n <input\n [id]=\"'shk-filter-' + col.field\"\n type=\"text\"\n [formControlName]=\"col.field\"\n class=\"w-full px-3 py-1.5 text-sm rounded-md border _shk-border dark:[border-color:var(--shk-surface-200)] bg-white dark:[background-color:var(--shk-surface-100)] _shk-text-surface-700 dark:[color:var(--shk-surface-700)] focus:[border-color:var(--shk-primary-500)] focus:ring-1 focus:[--tw-ring-color:var(--shk-primary-500)] placeholder:[color:var(--shk-surface-400)] dark:placeholder:[color:var(--shk-surface-500)]\"\n [placeholder]=\"\n col.filterPlaceholder || ('common.search_placeholder' | transloco)\n \"\n />\n }\n @if (col.filterType === 'select') {\n <label [for]=\"'shk-filter-' + col.field\" class=\"sr-only\">{{\n col.filterPlaceholder || ('common.search_placeholder' | transloco)\n }}</label>\n <select\n [id]=\"'shk-filter-' + col.field\"\n [formControlName]=\"col.field\"\n class=\"w-full px-2 py-1 text-xs rounded-md border _shk-border dark:[border-color:var(--shk-surface-200)] bg-white dark:[background-color:var(--shk-surface-100)] _shk-text-surface-700 dark:[color:var(--shk-surface-700)] focus:[border-color:var(--shk-primary-500)] focus:ring-1 focus:[--tw-ring-color:var(--shk-primary-500)] appearance-none\"\n >\n <option [ngValue]=\"null\">{{ 'table.filterAll' | transloco }}</option>\n @for (opt of col.filterOptions || []; track opt.value) {\n <option [ngValue]=\"opt.value\">{{ opt.label }}</option>\n }\n </select>\n }\n @if (hasFilterValue(col.field) && col.filterType !== 'select') {\n <button\n type=\"button\"\n class=\"absolute right-2 top-1/2 -translate-y-1/2 _shk-text-surface-400 dark:[color:var(--shk-surface-500)] hover:[color:var(--shk-surface-600)] dark:hover:[color:var(--shk-surface-300)]\"\n (click)=\"clearFilter(col.field)\"\n >\n <svg [lucideIcon]=\"getIcon('x')\" class=\"w-3.5 h-3.5\"></svg>\n </button>\n }\n </div>\n </div>\n }\n } @else {\n <div class=\"flex items-center justify-center\">\n <span>{{ col.header }}</span>\n </div>\n }\n </th>\n }\n </tr>\n </thead>\n @if (loading()) {\n <tbody class=\"border-b _shk-border dark:[border-color:var(--shk-surface-100)]\">\n <tr>\n <td [attr.colspan]=\"columnsWithActions().length\" class=\"text-center py-12\">\n <div\n class=\"flex items-center justify-center\"\n role=\"status\"\n [attr.aria-label]=\"'common.loading' | transloco\"\n >\n <svg\n [lucideIcon]=\"getIcon('loader')\"\n class=\"w-6 h-6 animate-spin _shk-text-primary\"\n ></svg>\n </div>\n </td>\n </tr>\n </tbody>\n } @else if (effectiveDisplayedData().length === 0) {\n <tbody class=\"border-b _shk-border dark:[border-color:var(--shk-surface-100)]\">\n <tr>\n <td [attr.colspan]=\"columnsWithActions().length\" class=\"text-center py-12\">\n <div class=\"flex flex-col items-center\">\n <div\n class=\"w-16 h-16 _shk-bg-surface-100 dark:[background-color:var(--shk-surface-100)] rounded-full flex items-center justify-center mb-4\"\n >\n <svg\n [lucideIcon]=\"getIcon('inbox')\"\n class=\"w-8 h-8 _shk-text-surface-400 dark:[color:var(--shk-surface-500)]\"\n ></svg>\n </div>\n <p\n class=\"text-lg font-semibold _shk-text-surface-600 dark:[color:var(--shk-surface-300)]\"\n >\n {{ emptyMessage() || ('common.no_records' | transloco) }}\n </p>\n </div>\n </td>\n </tr>\n </tbody>\n } @else {\n <tbody>\n @for (rowData of effectiveDisplayedData(); track trackByRow(rowData, $index)) {\n <tr\n class=\"hover:[background-color:color-mix(in srgb,var(--shk-primary-50)30%,transparent)] dark:hover:[background-color:color-mix(in srgb,var(--shk-primary-900)10%,transparent)] transition-colors duration-200 border-b _shk-border dark:[border-color:var(--shk-surface-100)]\"\n >\n @for (col of columnsWithActions(); track col.field) {\n <td\n [attr.data-shk-actions]=\"col.field === 'actions' ? '' : null\"\n class=\"px-2 min-[1440px]:px-3 py-2 min-[1440px]:py-2.5 text-xs font-medium _shk-text-surface-700 dark:[color:var(--shk-surface-700)] whitespace-nowrap\"\n [style.overflow]=\"col.field === 'actions' ? 'visible' : 'hidden'\"\n [style.text-overflow]=\"col.field === 'actions' ? 'clip' : 'ellipsis'\"\n [style.--shk-col-width]=\"col.field !== 'actions' ? getColumnWidth(col) : null\"\n >\n @if (col.field !== 'actions') {\n @if (col.template === 'tag') {\n <span\n class=\"inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium\"\n [class]=\"\n getTagClass(col.tagSeverity ? col.tagSeverity(rowData) : undefined)\n \"\n [style]=\"col.tagStyle ? col.tagStyle(rowData) : undefined\"\n >\n {{ col.tagValue ? col.tagValue(rowData) : rowData[col.field] }}\n </span>\n } @else if (col.format) {\n <span>{{ col.format(rowData) }}</span>\n } @else {\n <ng-container\n *ngTemplateOutlet=\"\n customTemplates()[col.field] || defaultTemplate;\n context: { $implicit: rowData, field: col.field }\n \"\n ></ng-container>\n }\n } @else {\n <div class=\"flex items-center justify-start gap-px\">\n @for (action of rowActions(); track $index) {\n @if (isRowActionVisible(action, rowData)) {\n <div class=\"relative group\">\n <button\n type=\"button\"\n (click)=\"action.onClick(rowData)\"\n [class]=\"\n 'table-action-btn hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)] rounded-md ' +\n getRowActionClass(action, rowData)\n \"\n [disabled]=\"isRowActionDisabled(action, rowData) || false\"\n [attr.aria-label]=\"getRowActionLabel(action, rowData)\"\n >\n <svg\n [lucideIcon]=\"getIcon(getRowActionIconName(action, rowData))\"\n class=\"w-4 h-4\"\n ></svg>\n </button>\n <span\n class=\"absolute bottom-full left-1/2 -translate-x-1/2 mb-1 px-2 py-1 text-xs _shk-bg-tooltip _shk-text-tooltip rounded whitespace-nowrap opacity-0 group-hover:opacity-100 transition pointer-events-none z-[9999] shadow-lg\"\n >\n {{ getRowActionLabel(action, rowData) }}\n </span>\n </div>\n }\n }\n </div>\n }\n </td>\n }\n </tr>\n }\n </tbody>\n }\n </table>\n <ng-template #defaultTemplate let-rowData let-field=\"field\">\n {{ truncate(rowData[field], 30) }}\n </ng-template>\n </div>\n <div\n class=\"flex items-center justify-between gap-4 px-2 min-[1440px]:px-3 py-2 border-t _shk-border dark:[border-color:var(--shk-surface-100)]\"\n >\n <div class=\"text-xs _shk-text-surface-600 dark:[color:var(--shk-surface-400)]\">\n {{ pageReport() }}\n </div>\n <div\n class=\"flex items-center gap-0.5\"\n role=\"navigation\"\n >\n <button\n type=\"button\"\n (click)=\"goToFirst()\"\n [disabled]=\"isFirstPage()\"\n class=\"p-1 _shk-text-surface-500 hover:[color:var(--shk-surface-700)] dark:[color:var(--shk-surface-400)] dark:hover:[color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed rounded transition-colors\"\n >\n <svg [lucideIcon]=\"getIcon('chevrons-left')\" class=\"w-4 h-4\"></svg>\n </button>\n <button\n type=\"button\"\n (click)=\"prev()\"\n [disabled]=\"isFirstPage()\"\n class=\"p-1 _shk-text-surface-500 hover:[color:var(--shk-surface-700)] dark:[color:var(--shk-surface-400)] dark:hover:[color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed rounded transition-colors\"\n >\n <svg [lucideIcon]=\"getIcon('chevron-left')\" class=\"w-4 h-4\"></svg>\n </button>\n <span\n class=\"px-2 text-xs font-medium _shk-text-surface-700 dark:[color:var(--shk-surface-700)]\"\n >{{ currentPage() }} / {{ totalPages() }}</span\n >\n <button\n type=\"button\"\n (click)=\"next()\"\n [disabled]=\"isLastPage()\"\n class=\"p-1 _shk-text-surface-500 hover:[color:var(--shk-surface-700)] dark:[color:var(--shk-surface-400)] dark:hover:[color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed rounded transition-colors\"\n >\n <svg [lucideIcon]=\"getIcon('chevron-right')\" class=\"w-4 h-4\"></svg>\n </button>\n <button\n type=\"button\"\n (click)=\"goToLast()\"\n [disabled]=\"isLastPage()\"\n class=\"p-1 _shk-text-surface-500 hover:[color:var(--shk-surface-700)] dark:[color:var(--shk-surface-400)] dark:hover:[color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed rounded transition-colors\"\n >\n <svg [lucideIcon]=\"getIcon('chevrons-right')\" class=\"w-4 h-4\"></svg>\n </button>\n </div>\n <div\n class=\"flex items-center gap-1.5 text-xs _shk-text-surface-600 dark:[color:var(--shk-surface-400)]\"\n >\n <label [for]=\"'shk-rows-per-page'\">{{ 'table.rows' | transloco }}</label>\n <select\n id=\"shk-rows-per-page\"\n [value]=\"rowsPerPageLocal()\"\n (change)=\"onRowsPerPageChange($any($event.target).value)\"\n class=\"border _shk-border dark:[border-color:var(--shk-surface-200)] rounded px-2 py-1 text-xs bg-white dark:[background-color:var(--shk-surface-100)] _shk-text-surface-700 dark:[color:var(--shk-surface-700)] focus:outline-none focus:[border-color:var(--shk-primary-500)]\"\n >\n @for (opt of rowsPerPageOptions(); track opt) {\n <option [value]=\"opt\">{{ opt }}</option>\n }\n </select>\n </div>\n </div>\n </div>\n</div>\n", styles: ["input:focus{outline:none}input[type=text]{font-weight:400!important}table th,table td{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}td button svg{width:1.125rem!important;height:1.125rem!important}td button:disabled{opacity:.4;cursor:not-allowed}.table-action-btn svg{width:1rem!important;height:1rem!important;min-width:1rem!important;min-height:1rem!important}.table-action-btn{padding:.5rem!important;min-width:2rem!important;min-height:2rem!important;display:inline-flex!important;align-items:center!important;justify-content:center!important}.table-scroll-wrapper{width:100%;overflow-x:auto;overflow-y:hidden;border-radius:inherit;-webkit-overflow-scrolling:touch;container-type:inline-size}.table-scroll-wrapper table{table-layout:fixed;width:100%}@container (max-width: 768px){.table-scroll-wrapper{scroll-snap-type:x mandatory}.table-scroll-wrapper table{table-layout:auto;width:100%}.table-scroll-wrapper th:not([data-shk-actions]),.table-scroll-wrapper td:not([data-shk-actions]){min-width:var(--shk-col-width, 140px);max-width:var(--shk-col-width, 140px)}.table-scroll-wrapper th,.table-scroll-wrapper td{scroll-snap-align:start}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: TranslocoModule }, { kind: "component", type: LucideDynamicIcon, selector: "svg[lucideIcon]", inputs: ["lucideIcon"] }, { kind: "pipe", type: i1$1.TranslocoPipe, name: "transloco" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
3051
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.18", type: TableComponent, isStandalone: true, selector: "shk-table", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, rowsPerPage: { classPropertyName: "rowsPerPage", publicName: "rowsPerPage", isSignal: true, isRequired: false, transformFunction: null }, rowsPerPageOptions: { classPropertyName: "rowsPerPageOptions", publicName: "rowsPerPageOptions", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, showActionRow: { classPropertyName: "showActionRow", publicName: "showActionRow", isSignal: true, isRequired: false, transformFunction: null }, customTemplates: { classPropertyName: "customTemplates", publicName: "customTemplates", isSignal: true, isRequired: false, transformFunction: null }, headerActions: { classPropertyName: "headerActions", publicName: "headerActions", isSignal: true, isRequired: false, transformFunction: null }, rowActions: { classPropertyName: "rowActions", publicName: "rowActions", isSignal: true, isRequired: false, transformFunction: null }, hasShadow: { classPropertyName: "hasShadow", publicName: "hasShadow", isSignal: true, isRequired: false, transformFunction: null }, defaultSortField: { classPropertyName: "defaultSortField", publicName: "defaultSortField", isSignal: true, isRequired: false, transformFunction: null }, defaultSortOrder: { classPropertyName: "defaultSortOrder", publicName: "defaultSortOrder", isSignal: true, isRequired: false, transformFunction: null }, showSearch: { classPropertyName: "showSearch", publicName: "showSearch", isSignal: true, isRequired: false, transformFunction: null }, searchPlaceholder: { classPropertyName: "searchPlaceholder", publicName: "searchPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, emptyMessage: { classPropertyName: "emptyMessage", publicName: "emptyMessage", isSignal: true, isRequired: false, transformFunction: null }, serverSide: { classPropertyName: "serverSide", publicName: "serverSide", isSignal: true, isRequired: false, transformFunction: null }, totalRecords: { classPropertyName: "totalRecords", publicName: "totalRecords", isSignal: true, isRequired: false, transformFunction: null }, filters: { classPropertyName: "filters", publicName: "filters", isSignal: true, isRequired: false, transformFunction: null }, activeFilter: { classPropertyName: "activeFilter", publicName: "activeFilter", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { refresh: "refresh", pageChange: "pageChange", filterChange: "filterChange", searchChange: "searchChange", filterClick: "filterClick" }, ngImport: i0, template: "<div class=\"space-y-2 min-[1440px]:space-y-3 mt-2 min-[1440px]:mt-3 animate-fade-in pb-8\">\n @if (showSearch() || filters().length > 0 || headerActions().length > 0) {\n <div class=\"card p-2 min-[1440px]:p-2.5 flex flex-row items-center justify-between gap-2\">\n <div class=\"flex items-center gap-2 min-[1440px]:gap-3 flex-1 min-w-0\">\n @if (showSearch()) {\n <div class=\"relative w-full md:w-52 min-[1440px]:w-56\">\n <label [for]=\"'shk-table-search'\" class=\"sr-only\">{{\n searchPlaceholder() || ('common.search_placeholder' | transloco)\n }}</label>\n <div class=\"absolute inset-y-0 left-0 pl-2.5 flex items-center pointer-events-none\">\n <svg\n [lucideIcon]=\"getIcon('search')\"\n class=\"w-3.5 h-3.5 _shk-text-surface-400 dark:[color:var(--shk-surface-500)]\"\n ></svg>\n </div>\n <input\n id=\"shk-table-search\"\n type=\"text\"\n [value]=\"searchQuery()\"\n (input)=\"onSearchInput($any($event.target).value)\"\n [placeholder]=\"searchPlaceholder() || ('common.search_placeholder' | transloco)\"\n class=\"block w-full pl-8 pr-3 py-1.5 text-xs border _shk-border dark:[border-color:var(--shk-surface-200)] rounded-md leading-5 _shk-bg-surface-50 dark:[background-color:var(--shk-surface-100)] placeholder:[color:var(--shk-surface-400)] dark:placeholder:[color:var(--shk-surface-500)] _shk-text-surface-700 dark:[color:var(--shk-surface-700)] focus:outline-none focus:bg-white dark:focus:[background-color:var(--shk-surface-800)] focus:[border-color:var(--shk-primary-500)] focus:ring-1 focus:[--tw-ring-color:var(--shk-primary-500)]\"\n />\n </div>\n }\n @if (filters().length > 0) {\n <div\n class=\"flex _shk-bg-surface-100 dark:[background-color:var(--shk-surface-100)] p-0.5 rounded-md ml-auto\"\n role=\"group\"\n >\n @for (f of filters(); track f.value) {\n <button\n type=\"button\"\n (click)=\"onFilterClick(f.value)\"\n [attr.aria-pressed]=\"f.value === activeFilter()\"\n class=\"px-2.5 min-[1440px]:px-3 py-1 text-xs font-medium rounded _shk-text-surface-700 dark:[color:var(--shk-surface-700)] transition-all\"\n [class]=\"\n f.value === activeFilter()\n ? 'bg-white shadow-sm dark:[background-color:var(--shk-surface-700)]'\n : ''\n \"\n >\n {{ f.label }}\n </button>\n }\n </div>\n }\n </div>\n @if (headerActions().length > 0) {\n <div class=\"flex items-center gap-1.5 shrink-0\">\n @for (action of headerActions(); track $index) {\n @if (isHeaderActionVisible(action)) {\n <button\n type=\"button\"\n [class]=\"\n 'inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-md _shk-bg-primary text-white hover:[background-color:var(--shk-primary-700)] transition-colors disabled:opacity-50 disabled:cursor-not-allowed ' +\n (action.class || '')\n \"\n [disabled]=\"isHeaderActionDisabled(action)\"\n [attr.aria-label]=\"action.label\"\n (click)=\"action.onClick()\"\n >\n @if (action.icon) {\n <svg [lucideIcon]=\"getIcon(action.icon)\" class=\"w-3.5 h-3.5\"></svg>\n }\n {{ action.label }}\n </button>\n }\n }\n </div>\n }\n </div>\n }\n <div class=\"card p-0 overflow-hidden\" [class.shadow-lg]=\"hasShadow()\">\n <div class=\"table-scroll-wrapper _shk-scrollbar\">\n <table>\n <thead>\n <tr>\n @for (col of columnsWithActions(); track col.field) {\n <th\n scope=\"col\"\n (click)=\"col.field !== 'actions' && col.sortable && toggleSort(col.field)\"\n (keydown)=\"onHeaderKeydown($event, col)\"\n [tabindex]=\"col.field !== 'actions' && col.sortable ? 0 : -1\"\n [attr.role]=\"col.field !== 'actions' && col.sortable ? 'button' : null\"\n [attr.aria-sort]=\"getAriaSort(col)\"\n [attr.data-shk-actions]=\"col.field === 'actions' ? '' : null\"\n [style.cursor]=\"col.field !== 'actions' && col.sortable ? 'pointer' : 'default'\"\n [style.width]=\"col.field === 'actions' ? col.width : null\"\n [style.min-width]=\"col.field === 'actions' ? col.width : null\"\n [style.--shk-col-width]=\"col.field !== 'actions' ? getColumnWidth(col) : null\"\n class=\"px-2 min-[1440px]:px-3 py-2 min-[1440px]:py-2.5 text-left text-[10px] min-[1440px]:text-xs font-bold _shk-text-surface-500 dark:[color:var(--shk-surface-400)] uppercase tracking-wider _shk-bg-surface-50 dark:[background-color:var(--shk-surface-100)] border-b _shk-border dark:[border-color:var(--shk-surface-200)] focus:outline-none focus-visible:ring-1 focus-visible:[--tw-ring-color:var(--shk-primary-500)]\"\n >\n @if (col.field !== 'actions') {\n <div class=\"flex items-center justify-between gap-2\">\n <span>{{ col.header }}</span>\n <div class=\"flex items-center gap-1\">\n @if (col.sortable) {\n <shk-tooltip [text]=\"'table.tooltips.sort' | transloco\">\n @if (sortField() === col.field) {\n <svg\n [lucideIcon]=\"getIcon(sortOrder() === 1 ? 'arrow-up' : 'arrow-down')\"\n class=\"w-3.5 h-3.5 _shk-text-primary\"\n ></svg>\n } @else {\n <svg\n [lucideIcon]=\"getIcon('arrow-up-down')\"\n class=\"w-3.5 h-3.5 _shk-text-surface-400 dark:[color:var(--shk-surface-500)]\"\n ></svg>\n }\n </shk-tooltip>\n }\n @if (col.filter) {\n <shk-tooltip [text]=\"'table.tooltips.filter' | transloco\">\n <button\n type=\"button\"\n (click)=\"$event.stopPropagation(); toggleFilter(col.field)\"\n [attr.aria-label]=\"'table.tooltips.filter' | transloco\"\n [attr.aria-expanded]=\"showFilterInput()[col.field] || false\"\n class=\"_shk-text-surface-400 dark:[color:var(--shk-surface-500)] hover:[color:var(--shk-surface-600)] dark:hover:[color:var(--shk-surface-300)] transition-colors\"\n >\n <svg\n [lucideIcon]=\"getIcon('filter')\"\n class=\"w-3.5 h-3.5\"\n [class._shk-text-primary]=\"hasFilterValue(col.field)\"\n ></svg>\n </button>\n </shk-tooltip>\n }\n </div>\n </div>\n @if (col.filter && showFilterInput()[col.field]) {\n <div\n class=\"mt-2\"\n [formGroup]=\"columnFiltersForm\"\n (click)=\"$event.stopPropagation()\"\n >\n <div class=\"relative\">\n @if (col.filterType !== 'select') {\n <label [for]=\"'shk-filter-' + col.field\" class=\"sr-only\">{{\n col.filterPlaceholder || ('common.search_placeholder' | transloco)\n }}</label>\n <input\n [id]=\"'shk-filter-' + col.field\"\n type=\"text\"\n [formControlName]=\"col.field\"\n class=\"w-full px-3 py-1.5 text-sm rounded-md border _shk-border dark:[border-color:var(--shk-surface-200)] bg-white dark:[background-color:var(--shk-surface-100)] _shk-text-surface-700 dark:[color:var(--shk-surface-700)] focus:[border-color:var(--shk-primary-500)] focus:ring-1 focus:[--tw-ring-color:var(--shk-primary-500)] placeholder:[color:var(--shk-surface-400)] dark:placeholder:[color:var(--shk-surface-500)]\"\n [placeholder]=\"\n col.filterPlaceholder || ('common.search_placeholder' | transloco)\n \"\n />\n }\n @if (col.filterType === 'select') {\n <label [for]=\"'shk-filter-' + col.field\" class=\"sr-only\">{{\n col.filterPlaceholder || ('common.search_placeholder' | transloco)\n }}</label>\n <select\n [id]=\"'shk-filter-' + col.field\"\n [formControlName]=\"col.field\"\n class=\"w-full px-2 py-1 text-xs rounded-md border _shk-border dark:[border-color:var(--shk-surface-200)] bg-white dark:[background-color:var(--shk-surface-100)] _shk-text-surface-700 dark:[color:var(--shk-surface-700)] focus:[border-color:var(--shk-primary-500)] focus:ring-1 focus:[--tw-ring-color:var(--shk-primary-500)] appearance-none\"\n >\n <option [ngValue]=\"null\">{{ 'table.filterAll' | transloco }}</option>\n @for (opt of col.filterOptions || []; track opt.value) {\n <option [ngValue]=\"opt.value\">{{ opt.label }}</option>\n }\n </select>\n }\n @if (hasFilterValue(col.field) && col.filterType !== 'select') {\n <button\n type=\"button\"\n class=\"absolute right-2 top-1/2 -translate-y-1/2 _shk-text-surface-400 dark:[color:var(--shk-surface-500)] hover:[color:var(--shk-surface-600)] dark:hover:[color:var(--shk-surface-300)]\"\n (click)=\"clearFilter(col.field)\"\n >\n <svg [lucideIcon]=\"getIcon('x')\" class=\"w-3.5 h-3.5\"></svg>\n </button>\n }\n </div>\n </div>\n }\n } @else {\n <div class=\"flex items-center justify-center\">\n <span>{{ col.header }}</span>\n </div>\n }\n </th>\n }\n </tr>\n </thead>\n @if (loading()) {\n <tbody class=\"border-b _shk-border dark:[border-color:var(--shk-surface-100)]\">\n <tr>\n <td [attr.colspan]=\"columnsWithActions().length\" class=\"text-center py-12\">\n <div\n class=\"flex items-center justify-center\"\n role=\"status\"\n [attr.aria-label]=\"'common.loading' | transloco\"\n >\n <svg\n [lucideIcon]=\"getIcon('loader')\"\n class=\"w-6 h-6 animate-spin _shk-text-primary\"\n ></svg>\n </div>\n </td>\n </tr>\n </tbody>\n } @else if (effectiveDisplayedData().length === 0) {\n <tbody class=\"border-b _shk-border dark:[border-color:var(--shk-surface-100)]\">\n <tr>\n <td [attr.colspan]=\"columnsWithActions().length\" class=\"text-center py-12\">\n <div class=\"flex flex-col items-center\">\n <div\n class=\"w-16 h-16 _shk-bg-surface-100 dark:[background-color:var(--shk-surface-100)] rounded-full flex items-center justify-center mb-4\"\n >\n <svg\n [lucideIcon]=\"getIcon('inbox')\"\n class=\"w-8 h-8 _shk-text-surface-400 dark:[color:var(--shk-surface-500)]\"\n ></svg>\n </div>\n <p\n class=\"text-lg font-semibold _shk-text-surface-600 dark:[color:var(--shk-surface-300)]\"\n >\n {{ emptyMessage() || ('common.no_records' | transloco) }}\n </p>\n </div>\n </td>\n </tr>\n </tbody>\n } @else {\n <tbody>\n @for (rowData of effectiveDisplayedData(); track trackByRow(rowData, $index)) {\n <tr\n class=\"hover:[background-color:color-mix(in srgb,var(--shk-primary-50)30%,transparent)] dark:hover:[background-color:color-mix(in srgb,var(--shk-primary-900)10%,transparent)] transition-colors duration-200 border-b _shk-border dark:[border-color:var(--shk-surface-100)]\"\n >\n @for (col of columnsWithActions(); track col.field) {\n <td\n [attr.data-shk-actions]=\"col.field === 'actions' ? '' : null\"\n class=\"px-2 min-[1440px]:px-3 py-2 min-[1440px]:py-2.5 text-xs font-medium _shk-text-surface-700 dark:[color:var(--shk-surface-700)] whitespace-nowrap\"\n [style.overflow]=\"col.field === 'actions' ? 'visible' : 'hidden'\"\n [style.text-overflow]=\"col.field === 'actions' ? 'clip' : 'ellipsis'\"\n [style.--shk-col-width]=\"col.field !== 'actions' ? getColumnWidth(col) : null\"\n >\n @if (col.field !== 'actions') {\n @if (col.template === 'tag') {\n <shk-tag\n [value]=\"getTagValue(col, rowData)\"\n [severity]=\"getTagSeverity(col, rowData)\"\n [rounded]=\"getTagRounded(col, rowData)\"\n [icon]=\"getTagIcon(col, rowData)\"\n [style]=\"getTagStyle(col, rowData)\"\n />\n } @else if (col.format) {\n <span>{{ col.format(rowData) }}</span>\n } @else {\n <ng-container\n *ngTemplateOutlet=\"\n customTemplates()[col.field] || defaultTemplate;\n context: { $implicit: rowData, field: col.field }\n \"\n ></ng-container>\n }\n } @else {\n <div class=\"flex items-center justify-start gap-px\">\n @for (action of rowActions(); track $index) {\n @if (isRowActionVisible(action, rowData)) {\n <div class=\"relative group\">\n <shk-tooltip\n [text]=\"getRowActionLabel(action, rowData)\"\n position=\"top\"\n >\n <button\n type=\"button\"\n (click)=\"action.onClick(rowData)\"\n [class]=\"\n 'table-action-btn hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)] rounded-md ' +\n getRowActionClass(action, rowData)\n \"\n [disabled]=\"isRowActionDisabled(action, rowData) || false\"\n [attr.aria-label]=\"getRowActionLabel(action, rowData)\"\n >\n <svg\n [lucideIcon]=\"getIcon(getRowActionIconName(action, rowData))\"\n class=\"w-4 h-4\"\n ></svg>\n </button>\n </shk-tooltip>\n </div>\n }\n }\n </div>\n }\n </td>\n }\n </tr>\n }\n </tbody>\n }\n </table>\n <ng-template #defaultTemplate let-rowData let-field=\"field\">\n {{ truncate(rowData[field], 30) }}\n </ng-template>\n </div>\n <div\n class=\"flex items-center justify-between gap-4 px-2 min-[1440px]:px-3 py-2 border-t _shk-border dark:[border-color:var(--shk-surface-100)]\"\n >\n <div class=\"text-xs _shk-text-surface-600 dark:[color:var(--shk-surface-400)]\">\n {{ pageReport() }}\n </div>\n <div\n class=\"flex items-center gap-0.5\"\n role=\"navigation\"\n >\n <button\n type=\"button\"\n (click)=\"goToFirst()\"\n [disabled]=\"isFirstPage()\"\n class=\"p-1 _shk-text-surface-500 hover:[color:var(--shk-surface-700)] dark:[color:var(--shk-surface-400)] dark:hover:[color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed rounded transition-colors\"\n >\n <svg [lucideIcon]=\"getIcon('chevrons-left')\" class=\"w-4 h-4\"></svg>\n </button>\n <button\n type=\"button\"\n (click)=\"prev()\"\n [disabled]=\"isFirstPage()\"\n class=\"p-1 _shk-text-surface-500 hover:[color:var(--shk-surface-700)] dark:[color:var(--shk-surface-400)] dark:hover:[color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed rounded transition-colors\"\n >\n <svg [lucideIcon]=\"getIcon('chevron-left')\" class=\"w-4 h-4\"></svg>\n </button>\n <span\n class=\"px-2 text-xs font-medium _shk-text-surface-700 dark:[color:var(--shk-surface-700)]\"\n >{{ currentPage() }} / {{ totalPages() }}</span\n >\n <button\n type=\"button\"\n (click)=\"next()\"\n [disabled]=\"isLastPage()\"\n class=\"p-1 _shk-text-surface-500 hover:[color:var(--shk-surface-700)] dark:[color:var(--shk-surface-400)] dark:hover:[color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed rounded transition-colors\"\n >\n <svg [lucideIcon]=\"getIcon('chevron-right')\" class=\"w-4 h-4\"></svg>\n </button>\n <button\n type=\"button\"\n (click)=\"goToLast()\"\n [disabled]=\"isLastPage()\"\n class=\"p-1 _shk-text-surface-500 hover:[color:var(--shk-surface-700)] dark:[color:var(--shk-surface-400)] dark:hover:[color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed rounded transition-colors\"\n >\n <svg [lucideIcon]=\"getIcon('chevrons-right')\" class=\"w-4 h-4\"></svg>\n </button>\n </div>\n <div\n class=\"flex items-center gap-1.5 text-xs _shk-text-surface-600 dark:[color:var(--shk-surface-400)]\"\n >\n <label [for]=\"'shk-rows-per-page'\">{{ 'table.rows' | transloco }}</label>\n <select\n id=\"shk-rows-per-page\"\n [value]=\"rowsPerPageLocal()\"\n (change)=\"onRowsPerPageChange($any($event.target).value)\"\n class=\"border _shk-border dark:[border-color:var(--shk-surface-200)] rounded px-2 py-1 text-xs bg-white dark:[background-color:var(--shk-surface-100)] _shk-text-surface-700 dark:[color:var(--shk-surface-700)] focus:outline-none focus:[border-color:var(--shk-primary-500)]\"\n >\n @for (opt of rowsPerPageOptions(); track opt) {\n <option [value]=\"opt\">{{ opt }}</option>\n }\n </select>\n </div>\n </div>\n </div>\n</div>\n", styles: ["input:focus{outline:none}input[type=text]{font-weight:400!important}table th,table td{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}td button svg{width:1.125rem!important;height:1.125rem!important}td button:disabled{opacity:.4;cursor:not-allowed}.table-action-btn svg{width:1rem!important;height:1rem!important;min-width:1rem!important;min-height:1rem!important}.table-action-btn{padding:.5rem!important;min-width:2rem!important;min-height:2rem!important;display:inline-flex!important;align-items:center!important;justify-content:center!important}.table-scroll-wrapper{width:100%;overflow-x:auto;overflow-y:hidden;border-radius:inherit;-webkit-overflow-scrolling:touch;container-type:inline-size}.table-scroll-wrapper table{table-layout:fixed;width:100%}@container (max-width: 768px){.table-scroll-wrapper{scroll-snap-type:x mandatory}.table-scroll-wrapper table{table-layout:auto;width:100%}.table-scroll-wrapper th:not([data-shk-actions]),.table-scroll-wrapper td:not([data-shk-actions]){min-width:var(--shk-col-width, 140px);max-width:var(--shk-col-width, 140px)}.table-scroll-wrapper th,.table-scroll-wrapper td{scroll-snap-align:start}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: TranslocoModule }, { kind: "component", type: LucideDynamicIcon, selector: "svg[lucideIcon]", inputs: ["lucideIcon"] }, { kind: "component", type: TagComponent, selector: "shk-tag", inputs: ["value", "severity", "rounded", "icon", "style", "styleClass"] }, { kind: "component", type: TooltipComponent, selector: "shk-tooltip", inputs: ["text", "position", "mode", "offset"] }, { kind: "pipe", type: i1$1.TranslocoPipe, name: "transloco" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
2775
3052
|
}
|
|
2776
3053
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: TableComponent, decorators: [{
|
|
2777
3054
|
type: Component,
|
|
2778
|
-
args: [{ selector: 'shk-table', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [CommonModule, ReactiveFormsModule, TranslocoModule, LucideDynamicIcon], template: "<div class=\"space-y-2 min-[1440px]:space-y-3 mt-2 min-[1440px]:mt-3 animate-fade-in pb-8\">\n @if (showSearch() || filters().length > 0 || headerActions().length > 0) {\n <div class=\"card p-2 min-[1440px]:p-2.5 flex flex-row items-center justify-between gap-2\">\n <div class=\"flex items-center gap-2 min-[1440px]:gap-3 flex-1 min-w-0\">\n @if (showSearch()) {\n <div class=\"relative w-full md:w-52 min-[1440px]:w-56\">\n <label [for]=\"'shk-table-search'\" class=\"sr-only\">{{\n searchPlaceholder() || ('common.search_placeholder' | transloco)\n }}</label>\n <div class=\"absolute inset-y-0 left-0 pl-2.5 flex items-center pointer-events-none\">\n <svg\n [lucideIcon]=\"getIcon('search')\"\n class=\"w-3.5 h-3.5 _shk-text-surface-400 dark:[color:var(--shk-surface-500)]\"\n ></svg>\n </div>\n <input\n id=\"shk-table-search\"\n type=\"text\"\n [value]=\"searchQuery()\"\n (input)=\"onSearchInput($any($event.target).value)\"\n [placeholder]=\"searchPlaceholder() || ('common.search_placeholder' | transloco)\"\n class=\"block w-full pl-8 pr-3 py-1.5 text-xs border _shk-border dark:[border-color:var(--shk-surface-200)] rounded-md leading-5 _shk-bg-surface-50 dark:[background-color:var(--shk-surface-100)] placeholder:[color:var(--shk-surface-400)] dark:placeholder:[color:var(--shk-surface-500)] _shk-text-surface-700 dark:[color:var(--shk-surface-700)] focus:outline-none focus:bg-white dark:focus:[background-color:var(--shk-surface-800)] focus:[border-color:var(--shk-primary-500)] focus:ring-1 focus:[--tw-ring-color:var(--shk-primary-500)]\"\n />\n </div>\n }\n @if (filters().length > 0) {\n <div\n class=\"flex _shk-bg-surface-100 dark:[background-color:var(--shk-surface-100)] p-0.5 rounded-md ml-auto\"\n role=\"group\"\n >\n @for (f of filters(); track f.value) {\n <button\n type=\"button\"\n (click)=\"onFilterClick(f.value)\"\n [attr.aria-pressed]=\"f.value === activeFilter()\"\n class=\"px-2.5 min-[1440px]:px-3 py-1 text-xs font-medium rounded _shk-text-surface-700 dark:[color:var(--shk-surface-700)] transition-all\"\n [class]=\"\n f.value === activeFilter()\n ? 'bg-white shadow-sm dark:[background-color:var(--shk-surface-700)]'\n : ''\n \"\n >\n {{ f.label }}\n </button>\n }\n </div>\n }\n </div>\n @if (headerActions().length > 0) {\n <div class=\"flex items-center gap-1.5 shrink-0\">\n @for (action of headerActions(); track $index) {\n @if (isHeaderActionVisible(action)) {\n <button\n type=\"button\"\n [class]=\"\n 'inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-md _shk-bg-primary text-white hover:[background-color:var(--shk-primary-700)] transition-colors disabled:opacity-50 disabled:cursor-not-allowed ' +\n (action.class || '')\n \"\n [disabled]=\"isHeaderActionDisabled(action)\"\n [attr.aria-label]=\"action.label\"\n (click)=\"action.onClick()\"\n >\n @if (action.icon) {\n <svg [lucideIcon]=\"getIcon(action.icon)\" class=\"w-3.5 h-3.5\"></svg>\n }\n {{ action.label }}\n </button>\n }\n }\n </div>\n }\n </div>\n }\n <div class=\"card p-0 overflow-hidden\" [class.shadow-lg]=\"hasShadow()\">\n <div class=\"table-scroll-wrapper _shk-scrollbar\">\n <table>\n <thead>\n <tr>\n @for (col of columnsWithActions(); track col.field) {\n <th\n scope=\"col\"\n (click)=\"col.field !== 'actions' && col.sortable && toggleSort(col.field)\"\n (keydown)=\"onHeaderKeydown($event, col)\"\n [tabindex]=\"col.field !== 'actions' && col.sortable ? 0 : -1\"\n [attr.role]=\"col.field !== 'actions' && col.sortable ? 'button' : null\"\n [attr.aria-sort]=\"getAriaSort(col)\"\n [attr.data-shk-actions]=\"col.field === 'actions' ? '' : null\"\n [style.cursor]=\"col.field !== 'actions' && col.sortable ? 'pointer' : 'default'\"\n [style.width]=\"col.field === 'actions' ? col.width : null\"\n [style.min-width]=\"col.field === 'actions' ? col.width : null\"\n [style.--shk-col-width]=\"col.field !== 'actions' ? getColumnWidth(col) : null\"\n class=\"px-2 min-[1440px]:px-3 py-2 min-[1440px]:py-2.5 text-left text-[10px] min-[1440px]:text-xs font-bold _shk-text-surface-500 dark:[color:var(--shk-surface-400)] uppercase tracking-wider _shk-bg-surface-50 dark:[background-color:var(--shk-surface-100)] border-b _shk-border dark:[border-color:var(--shk-surface-200)] focus:outline-none focus-visible:ring-1 focus-visible:[--tw-ring-color:var(--shk-primary-500)]\"\n >\n @if (col.field !== 'actions') {\n <div class=\"flex items-center justify-between gap-2\">\n <span>{{ col.header }}</span>\n <div class=\"flex items-center gap-1\">\n @if (col.sortable) {\n <div class=\"relative group\">\n @if (sortField() === col.field) {\n <svg\n [lucideIcon]=\"getIcon(sortOrder() === 1 ? 'arrow-up' : 'arrow-down')\"\n class=\"w-3.5 h-3.5 _shk-text-primary\"\n ></svg>\n } @else {\n <svg\n [lucideIcon]=\"getIcon('arrow-up-down')\"\n class=\"w-3.5 h-3.5 _shk-text-surface-400 dark:[color:var(--shk-surface-500)]\"\n ></svg>\n }\n <span\n class=\"absolute top-full left-1/2 -translate-x-1/2 mt-1 px-2 py-1 text-xs _shk-bg-tooltip _shk-text-tooltip rounded whitespace-nowrap opacity-0 group-hover:opacity-100 transition pointer-events-none z-[9999] shadow-lg\"\n >\n {{ 'table.tooltips.sort' | transloco }}\n </span>\n </div>\n }\n @if (col.filter) {\n <div class=\"relative group\">\n <button\n type=\"button\"\n (click)=\"$event.stopPropagation(); toggleFilter(col.field)\"\n [attr.aria-label]=\"'table.tooltips.filter' | transloco\"\n [attr.aria-expanded]=\"showFilterInput()[col.field] || false\"\n class=\"_shk-text-surface-400 dark:[color:var(--shk-surface-500)] hover:[color:var(--shk-surface-600)] dark:hover:[color:var(--shk-surface-300)] transition-colors\"\n >\n <svg\n [lucideIcon]=\"getIcon('filter')\"\n class=\"w-3.5 h-3.5\"\n [class._shk-text-primary]=\"hasFilterValue(col.field)\"\n ></svg>\n <span\n class=\"absolute top-full left-1/2 -translate-x-1/2 mt-1 px-2 py-1 text-xs _shk-bg-tooltip _shk-text-tooltip rounded whitespace-nowrap opacity-0 group-hover:opacity-100 transition pointer-events-none z-[9999] shadow-lg\"\n >\n {{ 'table.tooltips.filter' | transloco }}\n </span>\n </button>\n </div>\n }\n </div>\n </div>\n @if (col.filter && showFilterInput()[col.field]) {\n <div\n class=\"mt-2\"\n [formGroup]=\"columnFiltersForm\"\n (click)=\"$event.stopPropagation()\"\n >\n <div class=\"relative\">\n @if (col.filterType !== 'select') {\n <label [for]=\"'shk-filter-' + col.field\" class=\"sr-only\">{{\n col.filterPlaceholder || ('common.search_placeholder' | transloco)\n }}</label>\n <input\n [id]=\"'shk-filter-' + col.field\"\n type=\"text\"\n [formControlName]=\"col.field\"\n class=\"w-full px-3 py-1.5 text-sm rounded-md border _shk-border dark:[border-color:var(--shk-surface-200)] bg-white dark:[background-color:var(--shk-surface-100)] _shk-text-surface-700 dark:[color:var(--shk-surface-700)] focus:[border-color:var(--shk-primary-500)] focus:ring-1 focus:[--tw-ring-color:var(--shk-primary-500)] placeholder:[color:var(--shk-surface-400)] dark:placeholder:[color:var(--shk-surface-500)]\"\n [placeholder]=\"\n col.filterPlaceholder || ('common.search_placeholder' | transloco)\n \"\n />\n }\n @if (col.filterType === 'select') {\n <label [for]=\"'shk-filter-' + col.field\" class=\"sr-only\">{{\n col.filterPlaceholder || ('common.search_placeholder' | transloco)\n }}</label>\n <select\n [id]=\"'shk-filter-' + col.field\"\n [formControlName]=\"col.field\"\n class=\"w-full px-2 py-1 text-xs rounded-md border _shk-border dark:[border-color:var(--shk-surface-200)] bg-white dark:[background-color:var(--shk-surface-100)] _shk-text-surface-700 dark:[color:var(--shk-surface-700)] focus:[border-color:var(--shk-primary-500)] focus:ring-1 focus:[--tw-ring-color:var(--shk-primary-500)] appearance-none\"\n >\n <option [ngValue]=\"null\">{{ 'table.filterAll' | transloco }}</option>\n @for (opt of col.filterOptions || []; track opt.value) {\n <option [ngValue]=\"opt.value\">{{ opt.label }}</option>\n }\n </select>\n }\n @if (hasFilterValue(col.field) && col.filterType !== 'select') {\n <button\n type=\"button\"\n class=\"absolute right-2 top-1/2 -translate-y-1/2 _shk-text-surface-400 dark:[color:var(--shk-surface-500)] hover:[color:var(--shk-surface-600)] dark:hover:[color:var(--shk-surface-300)]\"\n (click)=\"clearFilter(col.field)\"\n >\n <svg [lucideIcon]=\"getIcon('x')\" class=\"w-3.5 h-3.5\"></svg>\n </button>\n }\n </div>\n </div>\n }\n } @else {\n <div class=\"flex items-center justify-center\">\n <span>{{ col.header }}</span>\n </div>\n }\n </th>\n }\n </tr>\n </thead>\n @if (loading()) {\n <tbody class=\"border-b _shk-border dark:[border-color:var(--shk-surface-100)]\">\n <tr>\n <td [attr.colspan]=\"columnsWithActions().length\" class=\"text-center py-12\">\n <div\n class=\"flex items-center justify-center\"\n role=\"status\"\n [attr.aria-label]=\"'common.loading' | transloco\"\n >\n <svg\n [lucideIcon]=\"getIcon('loader')\"\n class=\"w-6 h-6 animate-spin _shk-text-primary\"\n ></svg>\n </div>\n </td>\n </tr>\n </tbody>\n } @else if (effectiveDisplayedData().length === 0) {\n <tbody class=\"border-b _shk-border dark:[border-color:var(--shk-surface-100)]\">\n <tr>\n <td [attr.colspan]=\"columnsWithActions().length\" class=\"text-center py-12\">\n <div class=\"flex flex-col items-center\">\n <div\n class=\"w-16 h-16 _shk-bg-surface-100 dark:[background-color:var(--shk-surface-100)] rounded-full flex items-center justify-center mb-4\"\n >\n <svg\n [lucideIcon]=\"getIcon('inbox')\"\n class=\"w-8 h-8 _shk-text-surface-400 dark:[color:var(--shk-surface-500)]\"\n ></svg>\n </div>\n <p\n class=\"text-lg font-semibold _shk-text-surface-600 dark:[color:var(--shk-surface-300)]\"\n >\n {{ emptyMessage() || ('common.no_records' | transloco) }}\n </p>\n </div>\n </td>\n </tr>\n </tbody>\n } @else {\n <tbody>\n @for (rowData of effectiveDisplayedData(); track trackByRow(rowData, $index)) {\n <tr\n class=\"hover:[background-color:color-mix(in srgb,var(--shk-primary-50)30%,transparent)] dark:hover:[background-color:color-mix(in srgb,var(--shk-primary-900)10%,transparent)] transition-colors duration-200 border-b _shk-border dark:[border-color:var(--shk-surface-100)]\"\n >\n @for (col of columnsWithActions(); track col.field) {\n <td\n [attr.data-shk-actions]=\"col.field === 'actions' ? '' : null\"\n class=\"px-2 min-[1440px]:px-3 py-2 min-[1440px]:py-2.5 text-xs font-medium _shk-text-surface-700 dark:[color:var(--shk-surface-700)] whitespace-nowrap\"\n [style.overflow]=\"col.field === 'actions' ? 'visible' : 'hidden'\"\n [style.text-overflow]=\"col.field === 'actions' ? 'clip' : 'ellipsis'\"\n [style.--shk-col-width]=\"col.field !== 'actions' ? getColumnWidth(col) : null\"\n >\n @if (col.field !== 'actions') {\n @if (col.template === 'tag') {\n <span\n class=\"inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium\"\n [class]=\"\n getTagClass(col.tagSeverity ? col.tagSeverity(rowData) : undefined)\n \"\n [style]=\"col.tagStyle ? col.tagStyle(rowData) : undefined\"\n >\n {{ col.tagValue ? col.tagValue(rowData) : rowData[col.field] }}\n </span>\n } @else if (col.format) {\n <span>{{ col.format(rowData) }}</span>\n } @else {\n <ng-container\n *ngTemplateOutlet=\"\n customTemplates()[col.field] || defaultTemplate;\n context: { $implicit: rowData, field: col.field }\n \"\n ></ng-container>\n }\n } @else {\n <div class=\"flex items-center justify-start gap-px\">\n @for (action of rowActions(); track $index) {\n @if (isRowActionVisible(action, rowData)) {\n <div class=\"relative group\">\n <button\n type=\"button\"\n (click)=\"action.onClick(rowData)\"\n [class]=\"\n 'table-action-btn hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)] rounded-md ' +\n getRowActionClass(action, rowData)\n \"\n [disabled]=\"isRowActionDisabled(action, rowData) || false\"\n [attr.aria-label]=\"getRowActionLabel(action, rowData)\"\n >\n <svg\n [lucideIcon]=\"getIcon(getRowActionIconName(action, rowData))\"\n class=\"w-4 h-4\"\n ></svg>\n </button>\n <span\n class=\"absolute bottom-full left-1/2 -translate-x-1/2 mb-1 px-2 py-1 text-xs _shk-bg-tooltip _shk-text-tooltip rounded whitespace-nowrap opacity-0 group-hover:opacity-100 transition pointer-events-none z-[9999] shadow-lg\"\n >\n {{ getRowActionLabel(action, rowData) }}\n </span>\n </div>\n }\n }\n </div>\n }\n </td>\n }\n </tr>\n }\n </tbody>\n }\n </table>\n <ng-template #defaultTemplate let-rowData let-field=\"field\">\n {{ truncate(rowData[field], 30) }}\n </ng-template>\n </div>\n <div\n class=\"flex items-center justify-between gap-4 px-2 min-[1440px]:px-3 py-2 border-t _shk-border dark:[border-color:var(--shk-surface-100)]\"\n >\n <div class=\"text-xs _shk-text-surface-600 dark:[color:var(--shk-surface-400)]\">\n {{ pageReport() }}\n </div>\n <div\n class=\"flex items-center gap-0.5\"\n role=\"navigation\"\n >\n <button\n type=\"button\"\n (click)=\"goToFirst()\"\n [disabled]=\"isFirstPage()\"\n class=\"p-1 _shk-text-surface-500 hover:[color:var(--shk-surface-700)] dark:[color:var(--shk-surface-400)] dark:hover:[color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed rounded transition-colors\"\n >\n <svg [lucideIcon]=\"getIcon('chevrons-left')\" class=\"w-4 h-4\"></svg>\n </button>\n <button\n type=\"button\"\n (click)=\"prev()\"\n [disabled]=\"isFirstPage()\"\n class=\"p-1 _shk-text-surface-500 hover:[color:var(--shk-surface-700)] dark:[color:var(--shk-surface-400)] dark:hover:[color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed rounded transition-colors\"\n >\n <svg [lucideIcon]=\"getIcon('chevron-left')\" class=\"w-4 h-4\"></svg>\n </button>\n <span\n class=\"px-2 text-xs font-medium _shk-text-surface-700 dark:[color:var(--shk-surface-700)]\"\n >{{ currentPage() }} / {{ totalPages() }}</span\n >\n <button\n type=\"button\"\n (click)=\"next()\"\n [disabled]=\"isLastPage()\"\n class=\"p-1 _shk-text-surface-500 hover:[color:var(--shk-surface-700)] dark:[color:var(--shk-surface-400)] dark:hover:[color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed rounded transition-colors\"\n >\n <svg [lucideIcon]=\"getIcon('chevron-right')\" class=\"w-4 h-4\"></svg>\n </button>\n <button\n type=\"button\"\n (click)=\"goToLast()\"\n [disabled]=\"isLastPage()\"\n class=\"p-1 _shk-text-surface-500 hover:[color:var(--shk-surface-700)] dark:[color:var(--shk-surface-400)] dark:hover:[color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed rounded transition-colors\"\n >\n <svg [lucideIcon]=\"getIcon('chevrons-right')\" class=\"w-4 h-4\"></svg>\n </button>\n </div>\n <div\n class=\"flex items-center gap-1.5 text-xs _shk-text-surface-600 dark:[color:var(--shk-surface-400)]\"\n >\n <label [for]=\"'shk-rows-per-page'\">{{ 'table.rows' | transloco }}</label>\n <select\n id=\"shk-rows-per-page\"\n [value]=\"rowsPerPageLocal()\"\n (change)=\"onRowsPerPageChange($any($event.target).value)\"\n class=\"border _shk-border dark:[border-color:var(--shk-surface-200)] rounded px-2 py-1 text-xs bg-white dark:[background-color:var(--shk-surface-100)] _shk-text-surface-700 dark:[color:var(--shk-surface-700)] focus:outline-none focus:[border-color:var(--shk-primary-500)]\"\n >\n @for (opt of rowsPerPageOptions(); track opt) {\n <option [value]=\"opt\">{{ opt }}</option>\n }\n </select>\n </div>\n </div>\n </div>\n</div>\n", styles: ["input:focus{outline:none}input[type=text]{font-weight:400!important}table th,table td{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}td button svg{width:1.125rem!important;height:1.125rem!important}td button:disabled{opacity:.4;cursor:not-allowed}.table-action-btn svg{width:1rem!important;height:1rem!important;min-width:1rem!important;min-height:1rem!important}.table-action-btn{padding:.5rem!important;min-width:2rem!important;min-height:2rem!important;display:inline-flex!important;align-items:center!important;justify-content:center!important}.table-scroll-wrapper{width:100%;overflow-x:auto;overflow-y:hidden;border-radius:inherit;-webkit-overflow-scrolling:touch;container-type:inline-size}.table-scroll-wrapper table{table-layout:fixed;width:100%}@container (max-width: 768px){.table-scroll-wrapper{scroll-snap-type:x mandatory}.table-scroll-wrapper table{table-layout:auto;width:100%}.table-scroll-wrapper th:not([data-shk-actions]),.table-scroll-wrapper td:not([data-shk-actions]){min-width:var(--shk-col-width, 140px);max-width:var(--shk-col-width, 140px)}.table-scroll-wrapper th,.table-scroll-wrapper td{scroll-snap-align:start}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\n"] }]
|
|
3055
|
+
args: [{ selector: 'shk-table', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [
|
|
3056
|
+
CommonModule,
|
|
3057
|
+
ReactiveFormsModule,
|
|
3058
|
+
TranslocoModule,
|
|
3059
|
+
LucideDynamicIcon,
|
|
3060
|
+
TagComponent,
|
|
3061
|
+
TooltipComponent,
|
|
3062
|
+
], template: "<div class=\"space-y-2 min-[1440px]:space-y-3 mt-2 min-[1440px]:mt-3 animate-fade-in pb-8\">\n @if (showSearch() || filters().length > 0 || headerActions().length > 0) {\n <div class=\"card p-2 min-[1440px]:p-2.5 flex flex-row items-center justify-between gap-2\">\n <div class=\"flex items-center gap-2 min-[1440px]:gap-3 flex-1 min-w-0\">\n @if (showSearch()) {\n <div class=\"relative w-full md:w-52 min-[1440px]:w-56\">\n <label [for]=\"'shk-table-search'\" class=\"sr-only\">{{\n searchPlaceholder() || ('common.search_placeholder' | transloco)\n }}</label>\n <div class=\"absolute inset-y-0 left-0 pl-2.5 flex items-center pointer-events-none\">\n <svg\n [lucideIcon]=\"getIcon('search')\"\n class=\"w-3.5 h-3.5 _shk-text-surface-400 dark:[color:var(--shk-surface-500)]\"\n ></svg>\n </div>\n <input\n id=\"shk-table-search\"\n type=\"text\"\n [value]=\"searchQuery()\"\n (input)=\"onSearchInput($any($event.target).value)\"\n [placeholder]=\"searchPlaceholder() || ('common.search_placeholder' | transloco)\"\n class=\"block w-full pl-8 pr-3 py-1.5 text-xs border _shk-border dark:[border-color:var(--shk-surface-200)] rounded-md leading-5 _shk-bg-surface-50 dark:[background-color:var(--shk-surface-100)] placeholder:[color:var(--shk-surface-400)] dark:placeholder:[color:var(--shk-surface-500)] _shk-text-surface-700 dark:[color:var(--shk-surface-700)] focus:outline-none focus:bg-white dark:focus:[background-color:var(--shk-surface-800)] focus:[border-color:var(--shk-primary-500)] focus:ring-1 focus:[--tw-ring-color:var(--shk-primary-500)]\"\n />\n </div>\n }\n @if (filters().length > 0) {\n <div\n class=\"flex _shk-bg-surface-100 dark:[background-color:var(--shk-surface-100)] p-0.5 rounded-md ml-auto\"\n role=\"group\"\n >\n @for (f of filters(); track f.value) {\n <button\n type=\"button\"\n (click)=\"onFilterClick(f.value)\"\n [attr.aria-pressed]=\"f.value === activeFilter()\"\n class=\"px-2.5 min-[1440px]:px-3 py-1 text-xs font-medium rounded _shk-text-surface-700 dark:[color:var(--shk-surface-700)] transition-all\"\n [class]=\"\n f.value === activeFilter()\n ? 'bg-white shadow-sm dark:[background-color:var(--shk-surface-700)]'\n : ''\n \"\n >\n {{ f.label }}\n </button>\n }\n </div>\n }\n </div>\n @if (headerActions().length > 0) {\n <div class=\"flex items-center gap-1.5 shrink-0\">\n @for (action of headerActions(); track $index) {\n @if (isHeaderActionVisible(action)) {\n <button\n type=\"button\"\n [class]=\"\n 'inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-md _shk-bg-primary text-white hover:[background-color:var(--shk-primary-700)] transition-colors disabled:opacity-50 disabled:cursor-not-allowed ' +\n (action.class || '')\n \"\n [disabled]=\"isHeaderActionDisabled(action)\"\n [attr.aria-label]=\"action.label\"\n (click)=\"action.onClick()\"\n >\n @if (action.icon) {\n <svg [lucideIcon]=\"getIcon(action.icon)\" class=\"w-3.5 h-3.5\"></svg>\n }\n {{ action.label }}\n </button>\n }\n }\n </div>\n }\n </div>\n }\n <div class=\"card p-0 overflow-hidden\" [class.shadow-lg]=\"hasShadow()\">\n <div class=\"table-scroll-wrapper _shk-scrollbar\">\n <table>\n <thead>\n <tr>\n @for (col of columnsWithActions(); track col.field) {\n <th\n scope=\"col\"\n (click)=\"col.field !== 'actions' && col.sortable && toggleSort(col.field)\"\n (keydown)=\"onHeaderKeydown($event, col)\"\n [tabindex]=\"col.field !== 'actions' && col.sortable ? 0 : -1\"\n [attr.role]=\"col.field !== 'actions' && col.sortable ? 'button' : null\"\n [attr.aria-sort]=\"getAriaSort(col)\"\n [attr.data-shk-actions]=\"col.field === 'actions' ? '' : null\"\n [style.cursor]=\"col.field !== 'actions' && col.sortable ? 'pointer' : 'default'\"\n [style.width]=\"col.field === 'actions' ? col.width : null\"\n [style.min-width]=\"col.field === 'actions' ? col.width : null\"\n [style.--shk-col-width]=\"col.field !== 'actions' ? getColumnWidth(col) : null\"\n class=\"px-2 min-[1440px]:px-3 py-2 min-[1440px]:py-2.5 text-left text-[10px] min-[1440px]:text-xs font-bold _shk-text-surface-500 dark:[color:var(--shk-surface-400)] uppercase tracking-wider _shk-bg-surface-50 dark:[background-color:var(--shk-surface-100)] border-b _shk-border dark:[border-color:var(--shk-surface-200)] focus:outline-none focus-visible:ring-1 focus-visible:[--tw-ring-color:var(--shk-primary-500)]\"\n >\n @if (col.field !== 'actions') {\n <div class=\"flex items-center justify-between gap-2\">\n <span>{{ col.header }}</span>\n <div class=\"flex items-center gap-1\">\n @if (col.sortable) {\n <shk-tooltip [text]=\"'table.tooltips.sort' | transloco\">\n @if (sortField() === col.field) {\n <svg\n [lucideIcon]=\"getIcon(sortOrder() === 1 ? 'arrow-up' : 'arrow-down')\"\n class=\"w-3.5 h-3.5 _shk-text-primary\"\n ></svg>\n } @else {\n <svg\n [lucideIcon]=\"getIcon('arrow-up-down')\"\n class=\"w-3.5 h-3.5 _shk-text-surface-400 dark:[color:var(--shk-surface-500)]\"\n ></svg>\n }\n </shk-tooltip>\n }\n @if (col.filter) {\n <shk-tooltip [text]=\"'table.tooltips.filter' | transloco\">\n <button\n type=\"button\"\n (click)=\"$event.stopPropagation(); toggleFilter(col.field)\"\n [attr.aria-label]=\"'table.tooltips.filter' | transloco\"\n [attr.aria-expanded]=\"showFilterInput()[col.field] || false\"\n class=\"_shk-text-surface-400 dark:[color:var(--shk-surface-500)] hover:[color:var(--shk-surface-600)] dark:hover:[color:var(--shk-surface-300)] transition-colors\"\n >\n <svg\n [lucideIcon]=\"getIcon('filter')\"\n class=\"w-3.5 h-3.5\"\n [class._shk-text-primary]=\"hasFilterValue(col.field)\"\n ></svg>\n </button>\n </shk-tooltip>\n }\n </div>\n </div>\n @if (col.filter && showFilterInput()[col.field]) {\n <div\n class=\"mt-2\"\n [formGroup]=\"columnFiltersForm\"\n (click)=\"$event.stopPropagation()\"\n >\n <div class=\"relative\">\n @if (col.filterType !== 'select') {\n <label [for]=\"'shk-filter-' + col.field\" class=\"sr-only\">{{\n col.filterPlaceholder || ('common.search_placeholder' | transloco)\n }}</label>\n <input\n [id]=\"'shk-filter-' + col.field\"\n type=\"text\"\n [formControlName]=\"col.field\"\n class=\"w-full px-3 py-1.5 text-sm rounded-md border _shk-border dark:[border-color:var(--shk-surface-200)] bg-white dark:[background-color:var(--shk-surface-100)] _shk-text-surface-700 dark:[color:var(--shk-surface-700)] focus:[border-color:var(--shk-primary-500)] focus:ring-1 focus:[--tw-ring-color:var(--shk-primary-500)] placeholder:[color:var(--shk-surface-400)] dark:placeholder:[color:var(--shk-surface-500)]\"\n [placeholder]=\"\n col.filterPlaceholder || ('common.search_placeholder' | transloco)\n \"\n />\n }\n @if (col.filterType === 'select') {\n <label [for]=\"'shk-filter-' + col.field\" class=\"sr-only\">{{\n col.filterPlaceholder || ('common.search_placeholder' | transloco)\n }}</label>\n <select\n [id]=\"'shk-filter-' + col.field\"\n [formControlName]=\"col.field\"\n class=\"w-full px-2 py-1 text-xs rounded-md border _shk-border dark:[border-color:var(--shk-surface-200)] bg-white dark:[background-color:var(--shk-surface-100)] _shk-text-surface-700 dark:[color:var(--shk-surface-700)] focus:[border-color:var(--shk-primary-500)] focus:ring-1 focus:[--tw-ring-color:var(--shk-primary-500)] appearance-none\"\n >\n <option [ngValue]=\"null\">{{ 'table.filterAll' | transloco }}</option>\n @for (opt of col.filterOptions || []; track opt.value) {\n <option [ngValue]=\"opt.value\">{{ opt.label }}</option>\n }\n </select>\n }\n @if (hasFilterValue(col.field) && col.filterType !== 'select') {\n <button\n type=\"button\"\n class=\"absolute right-2 top-1/2 -translate-y-1/2 _shk-text-surface-400 dark:[color:var(--shk-surface-500)] hover:[color:var(--shk-surface-600)] dark:hover:[color:var(--shk-surface-300)]\"\n (click)=\"clearFilter(col.field)\"\n >\n <svg [lucideIcon]=\"getIcon('x')\" class=\"w-3.5 h-3.5\"></svg>\n </button>\n }\n </div>\n </div>\n }\n } @else {\n <div class=\"flex items-center justify-center\">\n <span>{{ col.header }}</span>\n </div>\n }\n </th>\n }\n </tr>\n </thead>\n @if (loading()) {\n <tbody class=\"border-b _shk-border dark:[border-color:var(--shk-surface-100)]\">\n <tr>\n <td [attr.colspan]=\"columnsWithActions().length\" class=\"text-center py-12\">\n <div\n class=\"flex items-center justify-center\"\n role=\"status\"\n [attr.aria-label]=\"'common.loading' | transloco\"\n >\n <svg\n [lucideIcon]=\"getIcon('loader')\"\n class=\"w-6 h-6 animate-spin _shk-text-primary\"\n ></svg>\n </div>\n </td>\n </tr>\n </tbody>\n } @else if (effectiveDisplayedData().length === 0) {\n <tbody class=\"border-b _shk-border dark:[border-color:var(--shk-surface-100)]\">\n <tr>\n <td [attr.colspan]=\"columnsWithActions().length\" class=\"text-center py-12\">\n <div class=\"flex flex-col items-center\">\n <div\n class=\"w-16 h-16 _shk-bg-surface-100 dark:[background-color:var(--shk-surface-100)] rounded-full flex items-center justify-center mb-4\"\n >\n <svg\n [lucideIcon]=\"getIcon('inbox')\"\n class=\"w-8 h-8 _shk-text-surface-400 dark:[color:var(--shk-surface-500)]\"\n ></svg>\n </div>\n <p\n class=\"text-lg font-semibold _shk-text-surface-600 dark:[color:var(--shk-surface-300)]\"\n >\n {{ emptyMessage() || ('common.no_records' | transloco) }}\n </p>\n </div>\n </td>\n </tr>\n </tbody>\n } @else {\n <tbody>\n @for (rowData of effectiveDisplayedData(); track trackByRow(rowData, $index)) {\n <tr\n class=\"hover:[background-color:color-mix(in srgb,var(--shk-primary-50)30%,transparent)] dark:hover:[background-color:color-mix(in srgb,var(--shk-primary-900)10%,transparent)] transition-colors duration-200 border-b _shk-border dark:[border-color:var(--shk-surface-100)]\"\n >\n @for (col of columnsWithActions(); track col.field) {\n <td\n [attr.data-shk-actions]=\"col.field === 'actions' ? '' : null\"\n class=\"px-2 min-[1440px]:px-3 py-2 min-[1440px]:py-2.5 text-xs font-medium _shk-text-surface-700 dark:[color:var(--shk-surface-700)] whitespace-nowrap\"\n [style.overflow]=\"col.field === 'actions' ? 'visible' : 'hidden'\"\n [style.text-overflow]=\"col.field === 'actions' ? 'clip' : 'ellipsis'\"\n [style.--shk-col-width]=\"col.field !== 'actions' ? getColumnWidth(col) : null\"\n >\n @if (col.field !== 'actions') {\n @if (col.template === 'tag') {\n <shk-tag\n [value]=\"getTagValue(col, rowData)\"\n [severity]=\"getTagSeverity(col, rowData)\"\n [rounded]=\"getTagRounded(col, rowData)\"\n [icon]=\"getTagIcon(col, rowData)\"\n [style]=\"getTagStyle(col, rowData)\"\n />\n } @else if (col.format) {\n <span>{{ col.format(rowData) }}</span>\n } @else {\n <ng-container\n *ngTemplateOutlet=\"\n customTemplates()[col.field] || defaultTemplate;\n context: { $implicit: rowData, field: col.field }\n \"\n ></ng-container>\n }\n } @else {\n <div class=\"flex items-center justify-start gap-px\">\n @for (action of rowActions(); track $index) {\n @if (isRowActionVisible(action, rowData)) {\n <div class=\"relative group\">\n <shk-tooltip\n [text]=\"getRowActionLabel(action, rowData)\"\n position=\"top\"\n >\n <button\n type=\"button\"\n (click)=\"action.onClick(rowData)\"\n [class]=\"\n 'table-action-btn hover:[background-color:var(--shk-surface-100)] dark:hover:[background-color:var(--shk-surface-200)] rounded-md ' +\n getRowActionClass(action, rowData)\n \"\n [disabled]=\"isRowActionDisabled(action, rowData) || false\"\n [attr.aria-label]=\"getRowActionLabel(action, rowData)\"\n >\n <svg\n [lucideIcon]=\"getIcon(getRowActionIconName(action, rowData))\"\n class=\"w-4 h-4\"\n ></svg>\n </button>\n </shk-tooltip>\n </div>\n }\n }\n </div>\n }\n </td>\n }\n </tr>\n }\n </tbody>\n }\n </table>\n <ng-template #defaultTemplate let-rowData let-field=\"field\">\n {{ truncate(rowData[field], 30) }}\n </ng-template>\n </div>\n <div\n class=\"flex items-center justify-between gap-4 px-2 min-[1440px]:px-3 py-2 border-t _shk-border dark:[border-color:var(--shk-surface-100)]\"\n >\n <div class=\"text-xs _shk-text-surface-600 dark:[color:var(--shk-surface-400)]\">\n {{ pageReport() }}\n </div>\n <div\n class=\"flex items-center gap-0.5\"\n role=\"navigation\"\n >\n <button\n type=\"button\"\n (click)=\"goToFirst()\"\n [disabled]=\"isFirstPage()\"\n class=\"p-1 _shk-text-surface-500 hover:[color:var(--shk-surface-700)] dark:[color:var(--shk-surface-400)] dark:hover:[color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed rounded transition-colors\"\n >\n <svg [lucideIcon]=\"getIcon('chevrons-left')\" class=\"w-4 h-4\"></svg>\n </button>\n <button\n type=\"button\"\n (click)=\"prev()\"\n [disabled]=\"isFirstPage()\"\n class=\"p-1 _shk-text-surface-500 hover:[color:var(--shk-surface-700)] dark:[color:var(--shk-surface-400)] dark:hover:[color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed rounded transition-colors\"\n >\n <svg [lucideIcon]=\"getIcon('chevron-left')\" class=\"w-4 h-4\"></svg>\n </button>\n <span\n class=\"px-2 text-xs font-medium _shk-text-surface-700 dark:[color:var(--shk-surface-700)]\"\n >{{ currentPage() }} / {{ totalPages() }}</span\n >\n <button\n type=\"button\"\n (click)=\"next()\"\n [disabled]=\"isLastPage()\"\n class=\"p-1 _shk-text-surface-500 hover:[color:var(--shk-surface-700)] dark:[color:var(--shk-surface-400)] dark:hover:[color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed rounded transition-colors\"\n >\n <svg [lucideIcon]=\"getIcon('chevron-right')\" class=\"w-4 h-4\"></svg>\n </button>\n <button\n type=\"button\"\n (click)=\"goToLast()\"\n [disabled]=\"isLastPage()\"\n class=\"p-1 _shk-text-surface-500 hover:[color:var(--shk-surface-700)] dark:[color:var(--shk-surface-400)] dark:hover:[color:var(--shk-surface-200)] disabled:opacity-30 disabled:cursor-not-allowed rounded transition-colors\"\n >\n <svg [lucideIcon]=\"getIcon('chevrons-right')\" class=\"w-4 h-4\"></svg>\n </button>\n </div>\n <div\n class=\"flex items-center gap-1.5 text-xs _shk-text-surface-600 dark:[color:var(--shk-surface-400)]\"\n >\n <label [for]=\"'shk-rows-per-page'\">{{ 'table.rows' | transloco }}</label>\n <select\n id=\"shk-rows-per-page\"\n [value]=\"rowsPerPageLocal()\"\n (change)=\"onRowsPerPageChange($any($event.target).value)\"\n class=\"border _shk-border dark:[border-color:var(--shk-surface-200)] rounded px-2 py-1 text-xs bg-white dark:[background-color:var(--shk-surface-100)] _shk-text-surface-700 dark:[color:var(--shk-surface-700)] focus:outline-none focus:[border-color:var(--shk-primary-500)]\"\n >\n @for (opt of rowsPerPageOptions(); track opt) {\n <option [value]=\"opt\">{{ opt }}</option>\n }\n </select>\n </div>\n </div>\n </div>\n</div>\n", styles: ["input:focus{outline:none}input[type=text]{font-weight:400!important}table th,table td{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}td button svg{width:1.125rem!important;height:1.125rem!important}td button:disabled{opacity:.4;cursor:not-allowed}.table-action-btn svg{width:1rem!important;height:1rem!important;min-width:1rem!important;min-height:1rem!important}.table-action-btn{padding:.5rem!important;min-width:2rem!important;min-height:2rem!important;display:inline-flex!important;align-items:center!important;justify-content:center!important}.table-scroll-wrapper{width:100%;overflow-x:auto;overflow-y:hidden;border-radius:inherit;-webkit-overflow-scrolling:touch;container-type:inline-size}.table-scroll-wrapper table{table-layout:fixed;width:100%}@container (max-width: 768px){.table-scroll-wrapper{scroll-snap-type:x mandatory}.table-scroll-wrapper table{table-layout:auto;width:100%}.table-scroll-wrapper th:not([data-shk-actions]),.table-scroll-wrapper td:not([data-shk-actions]){min-width:var(--shk-col-width, 140px);max-width:var(--shk-col-width, 140px)}.table-scroll-wrapper th,.table-scroll-wrapper td{scroll-snap-align:start}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\n"] }]
|
|
2779
3063
|
}], ctorParameters: () => [], propDecorators: { data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: false }] }], rowsPerPage: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowsPerPage", required: false }] }], rowsPerPageOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowsPerPageOptions", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], showActionRow: [{ type: i0.Input, args: [{ isSignal: true, alias: "showActionRow", required: false }] }], customTemplates: [{ type: i0.Input, args: [{ isSignal: true, alias: "customTemplates", required: false }] }], headerActions: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerActions", required: false }] }], rowActions: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowActions", required: false }] }], hasShadow: [{ type: i0.Input, args: [{ isSignal: true, alias: "hasShadow", required: false }] }], defaultSortField: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultSortField", required: false }] }], defaultSortOrder: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultSortOrder", required: false }] }], showSearch: [{ type: i0.Input, args: [{ isSignal: true, alias: "showSearch", required: false }] }], searchPlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchPlaceholder", required: false }] }], emptyMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyMessage", required: false }] }], serverSide: [{ type: i0.Input, args: [{ isSignal: true, alias: "serverSide", required: false }] }], totalRecords: [{ type: i0.Input, args: [{ isSignal: true, alias: "totalRecords", required: false }] }], filters: [{ type: i0.Input, args: [{ isSignal: true, alias: "filters", required: false }] }], activeFilter: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeFilter", required: false }] }], refresh: [{ type: i0.Output, args: ["refresh"] }], pageChange: [{ type: i0.Output, args: ["pageChange"] }], filterChange: [{ type: i0.Output, args: ["filterChange"] }], searchChange: [{ type: i0.Output, args: ["searchChange"] }], filterClick: [{ type: i0.Output, args: ["filterClick"] }] } });
|
|
2780
3064
|
|
|
2781
|
-
class TooltipComponent {
|
|
2782
|
-
constructor() {
|
|
2783
|
-
this.text = input('', ...(ngDevMode ? [{ debugName: "text" }] : /* istanbul ignore next */ []));
|
|
2784
|
-
this.position = input('bottom', ...(ngDevMode ? [{ debugName: "position" }] : /* istanbul ignore next */ []));
|
|
2785
|
-
this.mode = input('hover', ...(ngDevMode ? [{ debugName: "mode" }] : /* istanbul ignore next */ []));
|
|
2786
|
-
this.offset = input(8, ...(ngDevMode ? [{ debugName: "offset" }] : /* istanbul ignore next */ []));
|
|
2787
|
-
this.elementRef = inject(ElementRef);
|
|
2788
|
-
this.tooltipX = signal(0, ...(ngDevMode ? [{ debugName: "tooltipX" }] : /* istanbul ignore next */ []));
|
|
2789
|
-
this.tooltipY = signal(0, ...(ngDevMode ? [{ debugName: "tooltipY" }] : /* istanbul ignore next */ []));
|
|
2790
|
-
}
|
|
2791
|
-
onMouseEnter() {
|
|
2792
|
-
if (this.mode() !== 'fixed')
|
|
2793
|
-
return;
|
|
2794
|
-
const host = this.elementRef.nativeElement;
|
|
2795
|
-
const container = host.querySelector('.group');
|
|
2796
|
-
if (!container)
|
|
2797
|
-
return;
|
|
2798
|
-
const rect = container.getBoundingClientRect();
|
|
2799
|
-
const off = this.offset();
|
|
2800
|
-
if (this.position() === 'right') {
|
|
2801
|
-
this.tooltipX.set(rect.right + off);
|
|
2802
|
-
this.tooltipY.set(rect.top + rect.height / 2);
|
|
2803
|
-
}
|
|
2804
|
-
else if (this.position() === 'bottom') {
|
|
2805
|
-
this.tooltipX.set(rect.left + rect.width / 2);
|
|
2806
|
-
this.tooltipY.set(rect.bottom + off);
|
|
2807
|
-
}
|
|
2808
|
-
else {
|
|
2809
|
-
this.tooltipX.set(rect.left + rect.width / 2);
|
|
2810
|
-
this.tooltipY.set(rect.top - off);
|
|
2811
|
-
}
|
|
2812
|
-
}
|
|
2813
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: TooltipComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2814
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.18", type: TooltipComponent, isStandalone: true, selector: "shk-tooltip", inputs: { text: { classPropertyName: "text", publicName: "text", isSignal: true, isRequired: false, transformFunction: null }, position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, offset: { classPropertyName: "offset", publicName: "offset", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "mouseenter": "onMouseEnter()" } }, ngImport: i0, template: `
|
|
2815
|
-
<div class="relative group inline-flex">
|
|
2816
|
-
<ng-content></ng-content>
|
|
2817
|
-
@if (text() && mode() === 'hover') {
|
|
2818
|
-
<span
|
|
2819
|
-
class="absolute px-2 py-1 text-xs _shk-bg-tooltip _shk-text-tooltip rounded whitespace-nowrap opacity-0 group-hover:opacity-100 transition pointer-events-none z-[9999] shadow-lg"
|
|
2820
|
-
[class.top-full]="position() === 'bottom'"
|
|
2821
|
-
[class.bottom-full]="position() === 'top'"
|
|
2822
|
-
[class.left-full]="position() === 'right'"
|
|
2823
|
-
[class.mt-1]="position() === 'bottom'"
|
|
2824
|
-
[class.mb-1]="position() === 'top'"
|
|
2825
|
-
[class.ml-1]="position() === 'right'"
|
|
2826
|
-
[class.left-1/2]="position() !== 'right'"
|
|
2827
|
-
[class.-translate-x-1/2]="position() !== 'right'"
|
|
2828
|
-
[class.top-1/2]="position() === 'right'"
|
|
2829
|
-
[class.-translate-y-1/2]="position() === 'right'"
|
|
2830
|
-
>
|
|
2831
|
-
{{ text() }}
|
|
2832
|
-
</span>
|
|
2833
|
-
}
|
|
2834
|
-
@if (text() && mode() === 'fixed') {
|
|
2835
|
-
<span
|
|
2836
|
-
class="fixed px-2 py-1 text-xs _shk-bg-tooltip _shk-text-tooltip rounded whitespace-nowrap opacity-0 group-hover:opacity-100 transition pointer-events-none z-[9999] shadow-lg"
|
|
2837
|
-
[style.top.px]="tooltipY()"
|
|
2838
|
-
[style.left.px]="tooltipX()"
|
|
2839
|
-
[style.transform]="position() === 'right' ? 'translateY(-50%)' : ''"
|
|
2840
|
-
>
|
|
2841
|
-
{{ text() }}
|
|
2842
|
-
</span>
|
|
2843
|
-
}
|
|
2844
|
-
</div>
|
|
2845
|
-
`, isInline: true }); }
|
|
2846
|
-
}
|
|
2847
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: TooltipComponent, decorators: [{
|
|
2848
|
-
type: Component,
|
|
2849
|
-
args: [{
|
|
2850
|
-
selector: 'shk-tooltip',
|
|
2851
|
-
standalone: true,
|
|
2852
|
-
template: `
|
|
2853
|
-
<div class="relative group inline-flex">
|
|
2854
|
-
<ng-content></ng-content>
|
|
2855
|
-
@if (text() && mode() === 'hover') {
|
|
2856
|
-
<span
|
|
2857
|
-
class="absolute px-2 py-1 text-xs _shk-bg-tooltip _shk-text-tooltip rounded whitespace-nowrap opacity-0 group-hover:opacity-100 transition pointer-events-none z-[9999] shadow-lg"
|
|
2858
|
-
[class.top-full]="position() === 'bottom'"
|
|
2859
|
-
[class.bottom-full]="position() === 'top'"
|
|
2860
|
-
[class.left-full]="position() === 'right'"
|
|
2861
|
-
[class.mt-1]="position() === 'bottom'"
|
|
2862
|
-
[class.mb-1]="position() === 'top'"
|
|
2863
|
-
[class.ml-1]="position() === 'right'"
|
|
2864
|
-
[class.left-1/2]="position() !== 'right'"
|
|
2865
|
-
[class.-translate-x-1/2]="position() !== 'right'"
|
|
2866
|
-
[class.top-1/2]="position() === 'right'"
|
|
2867
|
-
[class.-translate-y-1/2]="position() === 'right'"
|
|
2868
|
-
>
|
|
2869
|
-
{{ text() }}
|
|
2870
|
-
</span>
|
|
2871
|
-
}
|
|
2872
|
-
@if (text() && mode() === 'fixed') {
|
|
2873
|
-
<span
|
|
2874
|
-
class="fixed px-2 py-1 text-xs _shk-bg-tooltip _shk-text-tooltip rounded whitespace-nowrap opacity-0 group-hover:opacity-100 transition pointer-events-none z-[9999] shadow-lg"
|
|
2875
|
-
[style.top.px]="tooltipY()"
|
|
2876
|
-
[style.left.px]="tooltipX()"
|
|
2877
|
-
[style.transform]="position() === 'right' ? 'translateY(-50%)' : ''"
|
|
2878
|
-
>
|
|
2879
|
-
{{ text() }}
|
|
2880
|
-
</span>
|
|
2881
|
-
}
|
|
2882
|
-
</div>
|
|
2883
|
-
`,
|
|
2884
|
-
}]
|
|
2885
|
-
}], propDecorators: { text: [{ type: i0.Input, args: [{ isSignal: true, alias: "text", required: false }] }], position: [{ type: i0.Input, args: [{ isSignal: true, alias: "position", required: false }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], offset: [{ type: i0.Input, args: [{ isSignal: true, alias: "offset", required: false }] }], onMouseEnter: [{
|
|
2886
|
-
type: HostListener,
|
|
2887
|
-
args: ['mouseenter']
|
|
2888
|
-
}] } });
|
|
2889
|
-
|
|
2890
3065
|
const ICONS$1 = {
|
|
2891
3066
|
house: LucideHouse,
|
|
2892
3067
|
home: LucideHome,
|
|
@@ -3829,6 +4004,1038 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImpo
|
|
|
3829
4004
|
args: [{ selector: 'shk-wizard', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [CommonModule, LucideDynamicIcon], template: "<div class=\"min-h-full bg-transparent\">\n\n <!-- ============ Sticky Header + Stepper ============ -->\n @if (showHeader()) {\n <div\n class=\"sticky top-0 z-20 bg-white dark:[background-color:var(--shk-surface-50)] border-b _shk-border dark:[border-color:var(--shk-surface-200)] shadow-sm\"\n >\n <div class=\"w-full px-4 sm:px-6 lg:px-8 py-4\">\n <div class=\"flex items-center gap-4 mb-4\">\n @if (showBackButton()) {\n <button\n type=\"button\"\n (click)=\"back.emit()\"\n [attr.aria-label]=\"backTooltip()\"\n [attr.title]=\"backTooltip()\"\n class=\"shk-wizard-icon-btn shrink-0\"\n >\n <svg [lucideIcon]=\"getIcon('arrow-left')\" class=\"w-4 h-4\"></svg>\n </button>\n }\n <div class=\"flex-1 min-w-0 flex items-center gap-2.5\">\n @if (headerIcon()) {\n <span class=\"w-9 h-9 rounded-lg _shk-bg-primary-soft _shk-text-primary flex items-center justify-center shrink-0\">\n <svg [lucideIcon]=\"getIcon(headerIcon())\" class=\"w-4 h-4\"></svg>\n </span>\n }\n <div class=\"min-w-0\">\n <h1 class=\"text-xl font-bold _shk-text-surface-800 dark:[color:var(--shk-surface-800)] truncate\">\n {{ headerTitle() }}\n </h1>\n @if (headerSubtitle()) {\n <p class=\"text-xs _shk-text-surface-500 dark:[color:var(--shk-surface-500)] mt-0.5 truncate\">\n {{ headerSubtitle() }}\n </p>\n }\n </div>\n </div>\n <ng-content select=\"[shkWizardHeaderActions]\"></ng-content>\n </div>\n\n <!-- Stepper -->\n @if (showStepper() && steps().length > 1) {\n <div class=\"flex items-center gap-2 w-full max-w-3xl mx-auto\">\n @for (step of steps(); track step.id; let i = $index, last = $last) {\n <div class=\"flex items-center gap-3 shrink-0\">\n <button\n type=\"button\"\n (click)=\"goTo(step.id)\"\n [disabled]=\"!stepperClickable() || currentIndex() < i\"\n [attr.aria-label]=\"step.label\"\n class=\"shk-wizard-stepper-dot\"\n [class._shk-bg-primary]=\"currentIndex() > i || currentIndex() === i\"\n [class.text-white]=\"currentIndex() > i || currentIndex() === i\"\n [class._shk-bg-surface-200]=\"currentIndex() < i\"\n [class._shk-text-surface-500]=\"currentIndex() < i\"\n [class.cursor-pointer]=\"stepperClickable() && currentIndex() >= i\"\n [class.cursor-default]=\"!stepperClickable() || currentIndex() < i\"\n >\n @if (currentIndex() > i) {\n <svg [lucideIcon]=\"getIcon(step.icon || 'check')\" class=\"w-4 h-4\"></svg>\n } @else if (step.icon) {\n <svg [lucideIcon]=\"getIcon(step.icon)\" class=\"w-4 h-4\"></svg>\n } @else {\n {{ i + 1 }}\n }\n </button>\n <span\n class=\"text-sm font-semibold transition-colors whitespace-nowrap hidden sm:inline\"\n [class._shk-text-surface-800]=\"currentIndex() === i\"\n [class._shk-text-surface-500]=\"currentIndex() !== i\"\n [class.dark:[color:var(--shk-surface-800)]]=\"currentIndex() === i\"\n [class.dark:[color:var(--shk-surface-500)]]=\"currentIndex() !== i\"\n >\n {{ step.label }}\n </span>\n </div>\n\n @if (!last) {\n <div class=\"flex-1 h-0.5 _shk-bg-surface-200 dark:[background-color:var(--shk-surface-200)] relative overflow-hidden rounded-full\">\n <div\n class=\"absolute inset-y-0 left-0 _shk-bg-primary transition-all duration-300\"\n [class.w-full]=\"currentIndex() > i\"\n [class.w-0]=\"currentIndex() <= i\"\n ></div>\n </div>\n }\n }\n </div>\n }\n </div>\n </div>\n }\n\n <!-- ============ Active step content ============ -->\n <div class=\"w-full px-4 sm:px-6 lg:px-8 py-6 pb-28\">\n @if (currentIndex() >= 0) {\n @if (getStepTemplate(steps()[currentIndex()].id); as tpl) {\n <div class=\"shk-wizard-fade\" [style.view-transition-name]=\"'shk-wizard-step'\">\n <ng-container\n *ngTemplateOutlet=\"tpl; context: { step: currentStepObj(), index: currentIndex() }\"\n ></ng-container>\n </div>\n } @else {\n <p class=\"text-sm _shk-text-surface-400 dark:[color:var(--shk-surface-400)]\">\n No hay contenido definido para el paso \u00AB{{ steps()[currentIndex()].label }}\u00BB.\n </p>\n }\n }\n </div>\n\n <!-- ============ Sticky Footer ============ -->\n @if (showFooter() && steps().length > 0) {\n <div\n class=\"sticky bottom-0 z-20 bg-white dark:[background-color:var(--shk-surface-50)] border-t _shk-border dark:[border-color:var(--shk-surface-200)] shadow-[0_-4px_12px_rgba(0,0,0,0.04)] mt-8\"\n >\n <div class=\"w-full px-4 sm:px-6 lg:px-8 py-4 flex items-center justify-end gap-3\">\n @if (showStepCounter()) {\n <span class=\"text-sm _shk-text-surface-500 dark:[color:var(--shk-surface-500)] mr-auto\">\n {{ stepCounterPrefix() }} {{ currentIndex() + 1 }} / {{ steps().length }}\n </span>\n }\n\n @if (isFirstStep()) {\n @if (showCancel()) {\n <button type=\"button\" (click)=\"cancel.emit()\" class=\"shk-wizard-btn-secondary\">\n @if (cancelIcon()) {\n <svg [lucideIcon]=\"getIcon(cancelIcon())\" class=\"w-4 h-4\"></svg>\n }\n {{ cancelLabel() }}\n </button>\n }\n } @else {\n <button type=\"button\" (click)=\"onPrev()\" class=\"shk-wizard-btn-secondary\">\n <svg [lucideIcon]=\"getIcon(prevIcon())\" class=\"w-4 h-4\"></svg>\n {{ prevLabel() }}\n </button>\n }\n\n <button\n type=\"button\"\n (click)=\"onPrimary()\"\n [disabled]=\"primaryDisabled() || primaryLoading()\"\n class=\"shk-wizard-btn-primary\"\n >\n @if (primaryLoading()) {\n <svg [lucideIcon]=\"getIcon('loader-circle')\" class=\"w-4 h-4 shk-wizard-spin\"></svg>\n } @else {\n @if (primaryActionIcon()) {\n <svg [lucideIcon]=\"getIcon(primaryActionIcon())\" class=\"w-4 h-4\"></svg>\n }\n }\n {{ primaryActionLabel() }}\n </button>\n </div>\n </div>\n }\n</div>\n", styles: [":host{display:block}.shk-wizard-icon-btn{display:inline-flex;align-items:center;justify-content:center;width:2rem;height:2rem;border-radius:9999px;color:var(--shk-surface-600);transition:background-color .15s ease-in-out,color .15s ease-in-out}.shk-wizard-icon-btn:hover:not(:disabled){background-color:var(--shk-surface-100);color:var(--shk-surface-800)}.shk-wizard-icon-btn:disabled{cursor:default}.shk-wizard-icon-btn svg{width:1rem!important;height:1rem!important}.shk-wizard-stepper-dot{display:flex;align-items:center;justify-content:center;width:2.25rem;height:2.25rem;border-radius:9999px;font-size:.875rem;font-weight:700;flex-shrink:0;transition:background-color .15s ease-in-out,color .15s ease-in-out}.shk-wizard-stepper-dot svg{width:1rem!important;height:1rem!important}.shk-wizard-btn-secondary{display:inline-flex;align-items:center;gap:.5rem;padding:.5rem 1rem;border-radius:.5rem;font-size:.875rem;font-weight:500;color:var(--shk-surface-700);background-color:transparent;border:1px solid var(--shk-surface-200);transition:background-color .15s ease-in-out,border-color .15s ease-in-out,color .15s ease-in-out}.shk-wizard-btn-secondary:hover:not(:disabled){background-color:var(--shk-surface-100);border-color:var(--shk-surface-300);color:var(--shk-surface-900)}.shk-wizard-btn-secondary:disabled{opacity:.5;cursor:not-allowed}.shk-wizard-btn-secondary svg{width:1rem!important;height:1rem!important}.shk-wizard-btn-primary{display:inline-flex;align-items:center;gap:.5rem;padding:.5rem 1rem;border-radius:.5rem;font-size:.875rem;font-weight:500;color:#fff;background-color:var(--shk-primary-600);box-shadow:0 6px 20px -6px color-mix(in srgb,var(--shk-primary-600) 55%,transparent);transition:background-color .15s ease-in-out,opacity .15s ease-in-out}.shk-wizard-btn-primary:hover:not(:disabled){background-color:var(--shk-primary-700)}.shk-wizard-btn-primary:disabled{opacity:.5;cursor:not-allowed;box-shadow:none}.shk-wizard-btn-primary svg{width:1rem!important;height:1rem!important}.shk-wizard-spin{animation:shk-wizard-spin .8s linear infinite}@keyframes shk-wizard-spin{to{transform:rotate(360deg)}}.shk-wizard-fade{animation:shk-wizard-fade .25s ease-out}@keyframes shk-wizard-fade{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}\n"] }]
|
|
3830
4005
|
}], propDecorators: { steps: [{ type: i0.Input, args: [{ isSignal: true, alias: "steps", required: false }] }], currentStep: [{ type: i0.Input, args: [{ isSignal: true, alias: "currentStep", required: false }] }, { type: i0.Output, args: ["currentStepChange"] }], showHeader: [{ type: i0.Input, args: [{ isSignal: true, alias: "showHeader", required: false }] }], headerTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerTitle", required: false }] }], headerSubtitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerSubtitle", required: false }] }], showBackButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "showBackButton", required: false }] }], backTooltip: [{ type: i0.Input, args: [{ isSignal: true, alias: "backTooltip", required: false }] }], headerIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerIcon", required: false }] }], showStepper: [{ type: i0.Input, args: [{ isSignal: true, alias: "showStepper", required: false }] }], stepperClickable: [{ type: i0.Input, args: [{ isSignal: true, alias: "stepperClickable", required: false }] }], showFooter: [{ type: i0.Input, args: [{ isSignal: true, alias: "showFooter", required: false }] }], showStepCounter: [{ type: i0.Input, args: [{ isSignal: true, alias: "showStepCounter", required: false }] }], stepCounterPrefix: [{ type: i0.Input, args: [{ isSignal: true, alias: "stepCounterPrefix", required: false }] }], showCancel: [{ type: i0.Input, args: [{ isSignal: true, alias: "showCancel", required: false }] }], advanceOnNext: [{ type: i0.Input, args: [{ isSignal: true, alias: "advanceOnNext", required: false }] }], prevLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "prevLabel", required: false }] }], prevIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "prevIcon", required: false }] }], cancelLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "cancelLabel", required: false }] }], cancelIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "cancelIcon", required: false }] }], nextLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "nextLabel", required: false }] }], nextIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "nextIcon", required: false }] }], finishLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "finishLabel", required: false }] }], finishIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "finishIcon", required: false }] }], primaryLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "primaryLabel", required: false }] }], primaryIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "primaryIcon", required: false }] }], primaryLoading: [{ type: i0.Input, args: [{ isSignal: true, alias: "primaryLoading", required: false }] }], primaryDisabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "primaryDisabled", required: false }] }], back: [{ type: i0.Output, args: ["back"] }], cancel: [{ type: i0.Output, args: ["cancel"] }], prev: [{ type: i0.Output, args: ["prev"] }], next: [{ type: i0.Output, args: ["next"] }], finish: [{ type: i0.Output, args: ["finish"] }], stepChange: [{ type: i0.Output, args: ["stepChange"] }], stepContents: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => ShkWizardStepContent), { isSignal: true }] }] } });
|
|
3831
4006
|
|
|
4007
|
+
/** Placeholder block shown while content is loading. */
|
|
4008
|
+
class SkeletonComponent {
|
|
4009
|
+
constructor() {
|
|
4010
|
+
/** Shape of the skeleton: rectangle (default) or circle. */
|
|
4011
|
+
this.shape = input('rectangle', ...(ngDevMode ? [{ debugName: "shape" }] : /* istanbul ignore next */ []));
|
|
4012
|
+
/** When shape is circle, sets both width and height. */
|
|
4013
|
+
this.size = input('', ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
4014
|
+
/** Width of the rectangle skeleton. */
|
|
4015
|
+
this.width = input('100%', ...(ngDevMode ? [{ debugName: "width" }] : /* istanbul ignore next */ []));
|
|
4016
|
+
/** Height of the rectangle skeleton. */
|
|
4017
|
+
this.height = input('1rem', ...(ngDevMode ? [{ debugName: "height" }] : /* istanbul ignore next */ []));
|
|
4018
|
+
/** Custom border radius (rectangle only). */
|
|
4019
|
+
this.borderRadius = input('', ...(ngDevMode ? [{ debugName: "borderRadius" }] : /* istanbul ignore next */ []));
|
|
4020
|
+
/** Animation effect: 'wave' (default), 'pulse' or 'none'. */
|
|
4021
|
+
this.animation = input('wave', ...(ngDevMode ? [{ debugName: "animation" }] : /* istanbul ignore next */ []));
|
|
4022
|
+
/** Inline styles applied to the skeleton block. */
|
|
4023
|
+
this.style = input({}, ...(ngDevMode ? [{ debugName: "style" }] : /* istanbul ignore next */ []));
|
|
4024
|
+
/** Extra CSS classes. */
|
|
4025
|
+
this.styleClass = input('', ...(ngDevMode ? [{ debugName: "styleClass" }] : /* istanbul ignore next */ []));
|
|
4026
|
+
this.rootStyle = computed(() => {
|
|
4027
|
+
const s = {};
|
|
4028
|
+
if (this.shape() === 'circle') {
|
|
4029
|
+
s['width'] = this.size() || this.width();
|
|
4030
|
+
s['height'] = this.size() || this.height();
|
|
4031
|
+
s['borderRadius'] = '9999px';
|
|
4032
|
+
}
|
|
4033
|
+
else {
|
|
4034
|
+
s['width'] = this.width();
|
|
4035
|
+
s['height'] = this.height();
|
|
4036
|
+
s['borderRadius'] = this.borderRadius() || '0.25rem';
|
|
4037
|
+
}
|
|
4038
|
+
return { ...s, ...this.style() };
|
|
4039
|
+
}, ...(ngDevMode ? [{ debugName: "rootStyle" }] : /* istanbul ignore next */ []));
|
|
4040
|
+
}
|
|
4041
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: SkeletonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
4042
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.18", type: SkeletonComponent, isStandalone: true, selector: "shk-skeleton", inputs: { shape: { classPropertyName: "shape", publicName: "shape", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, borderRadius: { classPropertyName: "borderRadius", publicName: "borderRadius", isSignal: true, isRequired: false, transformFunction: null }, animation: { classPropertyName: "animation", publicName: "animation", isSignal: true, isRequired: false, transformFunction: null }, style: { classPropertyName: "style", publicName: "style", isSignal: true, isRequired: false, transformFunction: null }, styleClass: { classPropertyName: "styleClass", publicName: "styleClass", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
|
|
4043
|
+
<div
|
|
4044
|
+
[class]="'shk-skeleton ' + styleClass()"
|
|
4045
|
+
[class.shk-skeleton-circle]="shape() === 'circle'"
|
|
4046
|
+
[class.shk-skeleton-animate-wave]="animation() === 'wave'"
|
|
4047
|
+
[class.shk-skeleton-animate-pulse]="animation() === 'pulse'"
|
|
4048
|
+
[style]="rootStyle()"
|
|
4049
|
+
aria-hidden="true"
|
|
4050
|
+
></div>
|
|
4051
|
+
`, isInline: true, styles: [":host{display:block}.shk-skeleton{position:relative;overflow:hidden;background-color:var(--shk-skeleton-bg)}.shk-skeleton-circle{border-radius:9999px}.shk-skeleton-animate-wave:after{content:\"\";position:absolute;inset:0;background-image:linear-gradient(90deg,transparent,var(--shk-skeleton-highlight) 50%,transparent);animation:shk-skeleton-wave 1.6s ease-in-out infinite}.shk-skeleton-animate-pulse{animation:shk-skeleton-pulse 2s ease-in-out infinite}@keyframes shk-skeleton-wave{0%{transform:translate(-100%)}to{transform:translate(100%)}}@keyframes shk-skeleton-pulse{0%,to{opacity:1}50%{opacity:.55}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
4052
|
+
}
|
|
4053
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: SkeletonComponent, decorators: [{
|
|
4054
|
+
type: Component,
|
|
4055
|
+
args: [{ selector: 'shk-skeleton', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
4056
|
+
<div
|
|
4057
|
+
[class]="'shk-skeleton ' + styleClass()"
|
|
4058
|
+
[class.shk-skeleton-circle]="shape() === 'circle'"
|
|
4059
|
+
[class.shk-skeleton-animate-wave]="animation() === 'wave'"
|
|
4060
|
+
[class.shk-skeleton-animate-pulse]="animation() === 'pulse'"
|
|
4061
|
+
[style]="rootStyle()"
|
|
4062
|
+
aria-hidden="true"
|
|
4063
|
+
></div>
|
|
4064
|
+
`, styles: [":host{display:block}.shk-skeleton{position:relative;overflow:hidden;background-color:var(--shk-skeleton-bg)}.shk-skeleton-circle{border-radius:9999px}.shk-skeleton-animate-wave:after{content:\"\";position:absolute;inset:0;background-image:linear-gradient(90deg,transparent,var(--shk-skeleton-highlight) 50%,transparent);animation:shk-skeleton-wave 1.6s ease-in-out infinite}.shk-skeleton-animate-pulse{animation:shk-skeleton-pulse 2s ease-in-out infinite}@keyframes shk-skeleton-wave{0%{transform:translate(-100%)}to{transform:translate(100%)}}@keyframes shk-skeleton-pulse{0%,to{opacity:1}50%{opacity:.55}}\n"] }]
|
|
4065
|
+
}], propDecorators: { shape: [{ type: i0.Input, args: [{ isSignal: true, alias: "shape", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }], borderRadius: [{ type: i0.Input, args: [{ isSignal: true, alias: "borderRadius", required: false }] }], animation: [{ type: i0.Input, args: [{ isSignal: true, alias: "animation", required: false }] }], style: [{ type: i0.Input, args: [{ isSignal: true, alias: "style", required: false }] }], styleClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "styleClass", required: false }] }] } });
|
|
4066
|
+
|
|
4067
|
+
/** Generic button with icon, severity variants, loading state and sizes. */
|
|
4068
|
+
class ButtonComponent {
|
|
4069
|
+
constructor() {
|
|
4070
|
+
/** Text shown inside the button. */
|
|
4071
|
+
this.label = input('', ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
|
|
4072
|
+
/** Optional leading lucide icon name. */
|
|
4073
|
+
this.icon = input('', ...(ngDevMode ? [{ debugName: "icon" }] : /* istanbul ignore next */ []));
|
|
4074
|
+
/** Position of the icon relative to the label. */
|
|
4075
|
+
this.iconPos = input('left', ...(ngDevMode ? [{ debugName: "iconPos" }] : /* istanbul ignore next */ []));
|
|
4076
|
+
/** Color variant. Defaults to 'primary'. */
|
|
4077
|
+
this.severity = input('primary', ...(ngDevMode ? [{ debugName: "severity" }] : /* istanbul ignore next */ []));
|
|
4078
|
+
/** Transparent background with a colored border. */
|
|
4079
|
+
this.outlined = input(false, ...(ngDevMode ? [{ debugName: "outlined" }] : /* istanbul ignore next */ []));
|
|
4080
|
+
/** Borderless button with a colored label. */
|
|
4081
|
+
this.text = input(false, ...(ngDevMode ? [{ debugName: "text" }] : /* istanbul ignore next */ []));
|
|
4082
|
+
/** Adds a soft shadow. */
|
|
4083
|
+
this.raised = input(false, ...(ngDevMode ? [{ debugName: "raised" }] : /* istanbul ignore next */ []));
|
|
4084
|
+
/** Fully rounded corners (pill). */
|
|
4085
|
+
this.rounded = input(false, ...(ngDevMode ? [{ debugName: "rounded" }] : /* istanbul ignore next */ []));
|
|
4086
|
+
/** Size variant. */
|
|
4087
|
+
this.size = input(undefined, ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
4088
|
+
/** Disables the button. */
|
|
4089
|
+
this.disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
|
|
4090
|
+
/** Shows a spinner and disables the button. */
|
|
4091
|
+
this.loading = input(false, ...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
|
|
4092
|
+
/** Icon shown while loading. Defaults to 'refresh'. */
|
|
4093
|
+
this.loadingIcon = input('refresh', ...(ngDevMode ? [{ debugName: "loadingIcon" }] : /* istanbul ignore next */ []));
|
|
4094
|
+
/** Optional text shown instead of the label while loading. */
|
|
4095
|
+
this.loadingText = input('', ...(ngDevMode ? [{ debugName: "loadingText" }] : /* istanbul ignore next */ []));
|
|
4096
|
+
/** Native button type. */
|
|
4097
|
+
this.type = input('button', ...(ngDevMode ? [{ debugName: "type" }] : /* istanbul ignore next */ []));
|
|
4098
|
+
/** Accessible label when the button is icon-only. */
|
|
4099
|
+
this.ariaLabel = input('', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
|
|
4100
|
+
/** Native title tooltip. */
|
|
4101
|
+
this.title = input('', ...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
|
|
4102
|
+
/** Inline styles applied to the button. */
|
|
4103
|
+
this.style = input({}, ...(ngDevMode ? [{ debugName: "style" }] : /* istanbul ignore next */ []));
|
|
4104
|
+
/** Extra CSS classes. */
|
|
4105
|
+
this.styleClass = input('', ...(ngDevMode ? [{ debugName: "styleClass" }] : /* istanbul ignore next */ []));
|
|
4106
|
+
/** Emitted on click with the original MouseEvent. */
|
|
4107
|
+
this.onClick = output();
|
|
4108
|
+
this.getIcon = getIcon$6;
|
|
4109
|
+
this.iconOnly = computed(() => !this.label() && !!this.icon(), ...(ngDevMode ? [{ debugName: "iconOnly" }] : /* istanbul ignore next */ []));
|
|
4110
|
+
}
|
|
4111
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: ButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
4112
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.18", type: ButtonComponent, isStandalone: true, selector: "shk-button", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, iconPos: { classPropertyName: "iconPos", publicName: "iconPos", isSignal: true, isRequired: false, transformFunction: null }, severity: { classPropertyName: "severity", publicName: "severity", isSignal: true, isRequired: false, transformFunction: null }, outlined: { classPropertyName: "outlined", publicName: "outlined", isSignal: true, isRequired: false, transformFunction: null }, text: { classPropertyName: "text", publicName: "text", isSignal: true, isRequired: false, transformFunction: null }, raised: { classPropertyName: "raised", publicName: "raised", isSignal: true, isRequired: false, transformFunction: null }, rounded: { classPropertyName: "rounded", publicName: "rounded", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, loadingIcon: { classPropertyName: "loadingIcon", publicName: "loadingIcon", isSignal: true, isRequired: false, transformFunction: null }, loadingText: { classPropertyName: "loadingText", publicName: "loadingText", isSignal: true, isRequired: false, transformFunction: null }, type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, style: { classPropertyName: "style", publicName: "style", isSignal: true, isRequired: false, transformFunction: null }, styleClass: { classPropertyName: "styleClass", publicName: "styleClass", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onClick: "onClick" }, ngImport: i0, template: `
|
|
4113
|
+
<button
|
|
4114
|
+
[attr.type]="type()"
|
|
4115
|
+
class="shk-button"
|
|
4116
|
+
[class.shk-button-primary]="severity() === 'primary' || !severity()"
|
|
4117
|
+
[class.shk-button-secondary]="severity() === 'secondary'"
|
|
4118
|
+
[class.shk-button-success]="severity() === 'success'"
|
|
4119
|
+
[class.shk-button-info]="severity() === 'info'"
|
|
4120
|
+
[class.shk-button-warning]="severity() === 'warning'"
|
|
4121
|
+
[class.shk-button-danger]="severity() === 'danger'"
|
|
4122
|
+
[class.shk-button-help]="severity() === 'help'"
|
|
4123
|
+
[class.shk-button-contrast]="severity() === 'contrast'"
|
|
4124
|
+
[class.shk-button-outlined]="outlined()"
|
|
4125
|
+
[class.shk-button-text]="text()"
|
|
4126
|
+
[class.shk-button-raised]="raised()"
|
|
4127
|
+
[class.shk-button-rounded]="rounded()"
|
|
4128
|
+
[class.shk-button-sm]="size() === 'small'"
|
|
4129
|
+
[class.shk-button-lg]="size() === 'large'"
|
|
4130
|
+
[class.shk-button-icon-right]="iconPos() === 'right'"
|
|
4131
|
+
[class.shk-button-icon-top]="iconPos() === 'top'"
|
|
4132
|
+
[class.shk-button-icon-bottom]="iconPos() === 'bottom'"
|
|
4133
|
+
[class.shk-button-icon-only]="iconOnly()"
|
|
4134
|
+
[class.shk-button-loading]="loading()"
|
|
4135
|
+
[class]="styleClass()"
|
|
4136
|
+
[style]="style()"
|
|
4137
|
+
[disabled]="disabled() || loading()"
|
|
4138
|
+
[attr.aria-label]="ariaLabel() || undefined"
|
|
4139
|
+
[attr.title]="title() || undefined"
|
|
4140
|
+
(click)="onClick.emit($event)"
|
|
4141
|
+
>
|
|
4142
|
+
@if (loading()) {
|
|
4143
|
+
<svg [lucideIcon]="getIcon(loadingIcon())" class="shk-button-icon shk-button-spin"></svg>
|
|
4144
|
+
} @else if (icon()) {
|
|
4145
|
+
<svg [lucideIcon]="getIcon(icon())" class="shk-button-icon"></svg>
|
|
4146
|
+
}
|
|
4147
|
+
@if (label()) {
|
|
4148
|
+
<span>{{ loading() && loadingText() ? loadingText() : label() }}</span>
|
|
4149
|
+
}
|
|
4150
|
+
<ng-content></ng-content>
|
|
4151
|
+
</button>
|
|
4152
|
+
`, isInline: true, styles: [":host{display:inline-block}.shk-button{display:inline-flex;align-items:center;justify-content:center;gap:.5rem;padding:.5rem 1rem;border-radius:.5rem;border:1px solid transparent;font-size:.875rem;font-weight:500;line-height:1.25rem;cursor:pointer;-webkit-user-select:none;user-select:none;white-space:nowrap;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,color .15s ease-in-out,box-shadow .15s ease-in-out,opacity .15s ease-in-out}.shk-button:focus-visible{outline:2px solid var(--shk-primary-400);outline-offset:2px}.shk-button:disabled{opacity:.55;cursor:not-allowed}.shk-button-icon{width:1rem;height:1rem;flex-shrink:0}.shk-button-primary{--shk-btn-bg: var(--shk-primary-600);--shk-btn-color: #ffffff;--shk-btn-border: var(--shk-primary-600)}.shk-button-secondary{--shk-btn-bg: var(--shk-surface-100);--shk-btn-color: var(--shk-surface-700);--shk-btn-border: var(--shk-surface-200)}.shk-button-success{--shk-btn-bg: var(--shk-success-600);--shk-btn-color: #ffffff;--shk-btn-border: var(--shk-success-600)}.shk-button-info{--shk-btn-bg: var(--shk-info-600);--shk-btn-color: #ffffff;--shk-btn-border: var(--shk-info-600)}.shk-button-warning{--shk-btn-bg: var(--shk-warning-600);--shk-btn-color: #ffffff;--shk-btn-border: var(--shk-warning-600)}.shk-button-danger{--shk-btn-bg: var(--shk-error-600);--shk-btn-color: #ffffff;--shk-btn-border: var(--shk-error-600)}.shk-button-help{--shk-btn-bg: var(--shk-primary-500);--shk-btn-color: #ffffff;--shk-btn-border: var(--shk-primary-500)}.shk-button-contrast{--shk-btn-bg: var(--shk-surface-900);--shk-btn-color: var(--shk-surface-50);--shk-btn-border: var(--shk-surface-900)}.shk-button{background-color:var(--shk-btn-bg);color:var(--shk-btn-color);border-color:var(--shk-btn-border)}.shk-button:hover:not(:disabled){background-color:color-mix(in srgb,var(--shk-btn-bg) 88%,#000000)}.shk-button-outlined,.shk-button-text{background-color:transparent}.shk-button-outlined{border-color:var(--shk-btn-color)}.shk-button-text{border-color:transparent}.shk-button-text:hover:not(:disabled),.shk-button-outlined:hover:not(:disabled){background-color:color-mix(in srgb,var(--shk-btn-color) 12%,transparent)}.shk-button-raised{box-shadow:0 1px 2px #0000000f,0 4px 8px #0000001a}.shk-button-rounded{border-radius:9999px}.shk-button-sm{padding:.375rem .75rem;font-size:.8125rem}.shk-button-sm .shk-button-icon{width:.875rem;height:.875rem}.shk-button-lg{padding:.75rem 1.5rem;font-size:1rem}.shk-button-lg .shk-button-icon{width:1.25rem;height:1.25rem}.shk-button-icon-only{padding:.5rem}.shk-button-sm.shk-button-icon-only{padding:.375rem}.shk-button-lg.shk-button-icon-only{padding:.75rem}.shk-button-icon-right{flex-direction:row-reverse}.shk-button-icon-top{flex-direction:column}.shk-button-icon-bottom{flex-direction:column-reverse}.shk-button-loading .shk-button-spin{animation:shk-button-spin 1s linear infinite}@keyframes shk-button-spin{to{transform:rotate(360deg)}}\n"], dependencies: [{ kind: "component", type: LucideDynamicIcon, selector: "svg[lucideIcon]", inputs: ["lucideIcon"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
4153
|
+
}
|
|
4154
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: ButtonComponent, decorators: [{
|
|
4155
|
+
type: Component,
|
|
4156
|
+
args: [{ selector: 'shk-button', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [LucideDynamicIcon], template: `
|
|
4157
|
+
<button
|
|
4158
|
+
[attr.type]="type()"
|
|
4159
|
+
class="shk-button"
|
|
4160
|
+
[class.shk-button-primary]="severity() === 'primary' || !severity()"
|
|
4161
|
+
[class.shk-button-secondary]="severity() === 'secondary'"
|
|
4162
|
+
[class.shk-button-success]="severity() === 'success'"
|
|
4163
|
+
[class.shk-button-info]="severity() === 'info'"
|
|
4164
|
+
[class.shk-button-warning]="severity() === 'warning'"
|
|
4165
|
+
[class.shk-button-danger]="severity() === 'danger'"
|
|
4166
|
+
[class.shk-button-help]="severity() === 'help'"
|
|
4167
|
+
[class.shk-button-contrast]="severity() === 'contrast'"
|
|
4168
|
+
[class.shk-button-outlined]="outlined()"
|
|
4169
|
+
[class.shk-button-text]="text()"
|
|
4170
|
+
[class.shk-button-raised]="raised()"
|
|
4171
|
+
[class.shk-button-rounded]="rounded()"
|
|
4172
|
+
[class.shk-button-sm]="size() === 'small'"
|
|
4173
|
+
[class.shk-button-lg]="size() === 'large'"
|
|
4174
|
+
[class.shk-button-icon-right]="iconPos() === 'right'"
|
|
4175
|
+
[class.shk-button-icon-top]="iconPos() === 'top'"
|
|
4176
|
+
[class.shk-button-icon-bottom]="iconPos() === 'bottom'"
|
|
4177
|
+
[class.shk-button-icon-only]="iconOnly()"
|
|
4178
|
+
[class.shk-button-loading]="loading()"
|
|
4179
|
+
[class]="styleClass()"
|
|
4180
|
+
[style]="style()"
|
|
4181
|
+
[disabled]="disabled() || loading()"
|
|
4182
|
+
[attr.aria-label]="ariaLabel() || undefined"
|
|
4183
|
+
[attr.title]="title() || undefined"
|
|
4184
|
+
(click)="onClick.emit($event)"
|
|
4185
|
+
>
|
|
4186
|
+
@if (loading()) {
|
|
4187
|
+
<svg [lucideIcon]="getIcon(loadingIcon())" class="shk-button-icon shk-button-spin"></svg>
|
|
4188
|
+
} @else if (icon()) {
|
|
4189
|
+
<svg [lucideIcon]="getIcon(icon())" class="shk-button-icon"></svg>
|
|
4190
|
+
}
|
|
4191
|
+
@if (label()) {
|
|
4192
|
+
<span>{{ loading() && loadingText() ? loadingText() : label() }}</span>
|
|
4193
|
+
}
|
|
4194
|
+
<ng-content></ng-content>
|
|
4195
|
+
</button>
|
|
4196
|
+
`, styles: [":host{display:inline-block}.shk-button{display:inline-flex;align-items:center;justify-content:center;gap:.5rem;padding:.5rem 1rem;border-radius:.5rem;border:1px solid transparent;font-size:.875rem;font-weight:500;line-height:1.25rem;cursor:pointer;-webkit-user-select:none;user-select:none;white-space:nowrap;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,color .15s ease-in-out,box-shadow .15s ease-in-out,opacity .15s ease-in-out}.shk-button:focus-visible{outline:2px solid var(--shk-primary-400);outline-offset:2px}.shk-button:disabled{opacity:.55;cursor:not-allowed}.shk-button-icon{width:1rem;height:1rem;flex-shrink:0}.shk-button-primary{--shk-btn-bg: var(--shk-primary-600);--shk-btn-color: #ffffff;--shk-btn-border: var(--shk-primary-600)}.shk-button-secondary{--shk-btn-bg: var(--shk-surface-100);--shk-btn-color: var(--shk-surface-700);--shk-btn-border: var(--shk-surface-200)}.shk-button-success{--shk-btn-bg: var(--shk-success-600);--shk-btn-color: #ffffff;--shk-btn-border: var(--shk-success-600)}.shk-button-info{--shk-btn-bg: var(--shk-info-600);--shk-btn-color: #ffffff;--shk-btn-border: var(--shk-info-600)}.shk-button-warning{--shk-btn-bg: var(--shk-warning-600);--shk-btn-color: #ffffff;--shk-btn-border: var(--shk-warning-600)}.shk-button-danger{--shk-btn-bg: var(--shk-error-600);--shk-btn-color: #ffffff;--shk-btn-border: var(--shk-error-600)}.shk-button-help{--shk-btn-bg: var(--shk-primary-500);--shk-btn-color: #ffffff;--shk-btn-border: var(--shk-primary-500)}.shk-button-contrast{--shk-btn-bg: var(--shk-surface-900);--shk-btn-color: var(--shk-surface-50);--shk-btn-border: var(--shk-surface-900)}.shk-button{background-color:var(--shk-btn-bg);color:var(--shk-btn-color);border-color:var(--shk-btn-border)}.shk-button:hover:not(:disabled){background-color:color-mix(in srgb,var(--shk-btn-bg) 88%,#000000)}.shk-button-outlined,.shk-button-text{background-color:transparent}.shk-button-outlined{border-color:var(--shk-btn-color)}.shk-button-text{border-color:transparent}.shk-button-text:hover:not(:disabled),.shk-button-outlined:hover:not(:disabled){background-color:color-mix(in srgb,var(--shk-btn-color) 12%,transparent)}.shk-button-raised{box-shadow:0 1px 2px #0000000f,0 4px 8px #0000001a}.shk-button-rounded{border-radius:9999px}.shk-button-sm{padding:.375rem .75rem;font-size:.8125rem}.shk-button-sm .shk-button-icon{width:.875rem;height:.875rem}.shk-button-lg{padding:.75rem 1.5rem;font-size:1rem}.shk-button-lg .shk-button-icon{width:1.25rem;height:1.25rem}.shk-button-icon-only{padding:.5rem}.shk-button-sm.shk-button-icon-only{padding:.375rem}.shk-button-lg.shk-button-icon-only{padding:.75rem}.shk-button-icon-right{flex-direction:row-reverse}.shk-button-icon-top{flex-direction:column}.shk-button-icon-bottom{flex-direction:column-reverse}.shk-button-loading .shk-button-spin{animation:shk-button-spin 1s linear infinite}@keyframes shk-button-spin{to{transform:rotate(360deg)}}\n"] }]
|
|
4197
|
+
}], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], iconPos: [{ type: i0.Input, args: [{ isSignal: true, alias: "iconPos", required: false }] }], severity: [{ type: i0.Input, args: [{ isSignal: true, alias: "severity", required: false }] }], outlined: [{ type: i0.Input, args: [{ isSignal: true, alias: "outlined", required: false }] }], text: [{ type: i0.Input, args: [{ isSignal: true, alias: "text", required: false }] }], raised: [{ type: i0.Input, args: [{ isSignal: true, alias: "raised", required: false }] }], rounded: [{ type: i0.Input, args: [{ isSignal: true, alias: "rounded", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], loadingIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "loadingIcon", required: false }] }], loadingText: [{ type: i0.Input, args: [{ isSignal: true, alias: "loadingText", required: false }] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], style: [{ type: i0.Input, args: [{ isSignal: true, alias: "style", required: false }] }], styleClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "styleClass", required: false }] }], onClick: [{ type: i0.Output, args: ["onClick"] }] } });
|
|
4198
|
+
|
|
4199
|
+
/** SVG radial knob with drag and keyboard support. Implements ControlValueAccessor. */
|
|
4200
|
+
class KnobComponent {
|
|
4201
|
+
constructor() {
|
|
4202
|
+
/** Current value. Two-way bindable with [(value)]. */
|
|
4203
|
+
this.value = model(0, ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
|
|
4204
|
+
/** Minimum value. */
|
|
4205
|
+
this.min = input(0, ...(ngDevMode ? [{ debugName: "min" }] : /* istanbul ignore next */ []));
|
|
4206
|
+
/** Maximum value. */
|
|
4207
|
+
this.max = input(100, ...(ngDevMode ? [{ debugName: "max" }] : /* istanbul ignore next */ []));
|
|
4208
|
+
/** Increment used by arrows and pointer drag rounding. */
|
|
4209
|
+
this.step = input(1, ...(ngDevMode ? [{ debugName: "step" }] : /* istanbul ignore next */ []));
|
|
4210
|
+
/** Diameter of the knob in px. */
|
|
4211
|
+
this.size = input(100, ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
4212
|
+
/** Thickness of the arc stroke. */
|
|
4213
|
+
this.strokeWidth = input(14, ...(ngDevMode ? [{ debugName: "strokeWidth" }] : /* istanbul ignore next */ []));
|
|
4214
|
+
/** Color of the filled arc. */
|
|
4215
|
+
this.valueColor = input('var(--shk-primary-600)', ...(ngDevMode ? [{ debugName: "valueColor" }] : /* istanbul ignore next */ []));
|
|
4216
|
+
/** Color of the track arc. */
|
|
4217
|
+
this.rangeColor = input('var(--shk-surface-200)', ...(ngDevMode ? [{ debugName: "rangeColor" }] : /* istanbul ignore next */ []));
|
|
4218
|
+
/** Whether the current value is shown in the center. */
|
|
4219
|
+
this.showValue = input(true, ...(ngDevMode ? [{ debugName: "showValue" }] : /* istanbul ignore next */ []));
|
|
4220
|
+
/** Disables user interaction. */
|
|
4221
|
+
this.disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
|
|
4222
|
+
/** Blocks editing but keeps the control styled as enabled. */
|
|
4223
|
+
this.readonly = input(false, ...(ngDevMode ? [{ debugName: "readonly" }] : /* istanbul ignore next */ []));
|
|
4224
|
+
/** Accessible label. */
|
|
4225
|
+
this.ariaLabel = input('', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
|
|
4226
|
+
/** Emitted when the knob value changes. */
|
|
4227
|
+
this.change = output();
|
|
4228
|
+
/** Emitted when the user releases the pointer. */
|
|
4229
|
+
this.end = output();
|
|
4230
|
+
this.knobSvg = viewChild('knobSvg', ...(ngDevMode ? [{ debugName: "knobSvg" }] : /* istanbul ignore next */ []));
|
|
4231
|
+
this.onChange = () => { };
|
|
4232
|
+
this.onTouched = () => { };
|
|
4233
|
+
this.dragging = false;
|
|
4234
|
+
this.formDisabled = signal(false, ...(ngDevMode ? [{ debugName: "formDisabled" }] : /* istanbul ignore next */ []));
|
|
4235
|
+
/** Effective disabled state combining the `disabled` input and the form binding. */
|
|
4236
|
+
this.effectiveDisabled = computed(() => this.disabled() || this.formDisabled(), ...(ngDevMode ? [{ debugName: "effectiveDisabled" }] : /* istanbul ignore next */ []));
|
|
4237
|
+
this.r = computed(() => (this.size() - this.strokeWidth()) / 2, ...(ngDevMode ? [{ debugName: "r" }] : /* istanbul ignore next */ []));
|
|
4238
|
+
this.center = computed(() => this.size() / 2, ...(ngDevMode ? [{ debugName: "center" }] : /* istanbul ignore next */ []));
|
|
4239
|
+
this.fraction = computed(() => {
|
|
4240
|
+
const range = this.max() - this.min();
|
|
4241
|
+
if (range <= 0)
|
|
4242
|
+
return 0;
|
|
4243
|
+
return Math.max(0, Math.min(1, (this.value() - this.min()) / range));
|
|
4244
|
+
}, ...(ngDevMode ? [{ debugName: "fraction" }] : /* istanbul ignore next */ []));
|
|
4245
|
+
this.rangeArc = computed(() => this.getArcPath(135, 405), ...(ngDevMode ? [{ debugName: "rangeArc" }] : /* istanbul ignore next */ []));
|
|
4246
|
+
this.valueArc = computed(() => this.getArcPath(135, 135 + 270 * this.fraction()), ...(ngDevMode ? [{ debugName: "valueArc" }] : /* istanbul ignore next */ []));
|
|
4247
|
+
}
|
|
4248
|
+
writeValue(value) {
|
|
4249
|
+
this.value.set(this.clamp(Number(value) || this.min()));
|
|
4250
|
+
}
|
|
4251
|
+
registerOnChange(fn) {
|
|
4252
|
+
this.onChange = fn;
|
|
4253
|
+
}
|
|
4254
|
+
registerOnTouched(fn) {
|
|
4255
|
+
this.onTouched = fn;
|
|
4256
|
+
}
|
|
4257
|
+
setDisabledState(isDisabled) {
|
|
4258
|
+
this.formDisabled.set(isDisabled);
|
|
4259
|
+
}
|
|
4260
|
+
onPointerDown(event) {
|
|
4261
|
+
if (this.effectiveDisabled() || this.readonly())
|
|
4262
|
+
return;
|
|
4263
|
+
this.dragging = true;
|
|
4264
|
+
this.knobSvg()?.nativeElement.setPointerCapture(event.pointerId);
|
|
4265
|
+
this.applyEvent(event);
|
|
4266
|
+
}
|
|
4267
|
+
onPointerMove(event) {
|
|
4268
|
+
if (!this.dragging)
|
|
4269
|
+
return;
|
|
4270
|
+
this.applyEvent(event);
|
|
4271
|
+
}
|
|
4272
|
+
onPointerUp(event) {
|
|
4273
|
+
if (!this.dragging)
|
|
4274
|
+
return;
|
|
4275
|
+
this.dragging = false;
|
|
4276
|
+
try {
|
|
4277
|
+
this.knobSvg()?.nativeElement.releasePointerCapture(event.pointerId);
|
|
4278
|
+
}
|
|
4279
|
+
catch {
|
|
4280
|
+
/* noop */
|
|
4281
|
+
}
|
|
4282
|
+
this.change.emit(this.value());
|
|
4283
|
+
this.end.emit(this.value());
|
|
4284
|
+
}
|
|
4285
|
+
updateByKey(step) {
|
|
4286
|
+
if (this.effectiveDisabled() || this.readonly())
|
|
4287
|
+
return;
|
|
4288
|
+
this.setValue(this.value() + step);
|
|
4289
|
+
this.onTouched();
|
|
4290
|
+
}
|
|
4291
|
+
applyEvent(event) {
|
|
4292
|
+
const svg = this.knobSvg()?.nativeElement;
|
|
4293
|
+
if (!svg)
|
|
4294
|
+
return;
|
|
4295
|
+
const rect = svg.getBoundingClientRect();
|
|
4296
|
+
const cx = rect.left + rect.width / 2;
|
|
4297
|
+
const cy = rect.top + rect.height / 2;
|
|
4298
|
+
const angle = Math.atan2(event.clientY - cy, event.clientX - cx);
|
|
4299
|
+
const deg = ((angle * 180) / Math.PI + 360) % 360;
|
|
4300
|
+
let t = deg - 135;
|
|
4301
|
+
if (t < 0)
|
|
4302
|
+
t += 360;
|
|
4303
|
+
let fraction;
|
|
4304
|
+
if (t > 270) {
|
|
4305
|
+
fraction = 360 - t < t - 270 ? 0 : 1;
|
|
4306
|
+
}
|
|
4307
|
+
else {
|
|
4308
|
+
fraction = t / 270;
|
|
4309
|
+
}
|
|
4310
|
+
const raw = this.min() + fraction * (this.max() - this.min());
|
|
4311
|
+
this.setValue(raw);
|
|
4312
|
+
this.onTouched();
|
|
4313
|
+
}
|
|
4314
|
+
setValue(raw) {
|
|
4315
|
+
const rounded = Math.round(raw / this.step()) * this.step();
|
|
4316
|
+
const clamped = this.clamp(rounded);
|
|
4317
|
+
this.value.set(clamped);
|
|
4318
|
+
this.onChange(clamped);
|
|
4319
|
+
}
|
|
4320
|
+
clamp(value) {
|
|
4321
|
+
return Math.max(this.min(), Math.min(this.max(), value));
|
|
4322
|
+
}
|
|
4323
|
+
getArcPath(startDeg, endDeg) {
|
|
4324
|
+
const cx = this.center();
|
|
4325
|
+
const cy = this.center();
|
|
4326
|
+
const r = this.r();
|
|
4327
|
+
const start = this.anglePoint(cx, cy, r, startDeg);
|
|
4328
|
+
const end = this.anglePoint(cx, cy, r, endDeg);
|
|
4329
|
+
const largeArc = endDeg - startDeg > 180 ? '1' : '0';
|
|
4330
|
+
return `M ${start.x.toFixed(2)} ${start.y.toFixed(2)} A ${r.toFixed(2)} ${r.toFixed(2)} 0 ${largeArc} 1 ${end.x.toFixed(2)} ${end.y.toFixed(2)}`;
|
|
4331
|
+
}
|
|
4332
|
+
anglePoint(cx, cy, r, angleDeg) {
|
|
4333
|
+
const rad = (Math.PI * angleDeg) / 180;
|
|
4334
|
+
return {
|
|
4335
|
+
x: cx + r * Math.cos(rad),
|
|
4336
|
+
y: cy + r * Math.sin(rad),
|
|
4337
|
+
};
|
|
4338
|
+
}
|
|
4339
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: KnobComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
4340
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.18", type: KnobComponent, isStandalone: true, selector: "shk-knob", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, step: { classPropertyName: "step", publicName: "step", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, strokeWidth: { classPropertyName: "strokeWidth", publicName: "strokeWidth", isSignal: true, isRequired: false, transformFunction: null }, valueColor: { classPropertyName: "valueColor", publicName: "valueColor", isSignal: true, isRequired: false, transformFunction: null }, rangeColor: { classPropertyName: "rangeColor", publicName: "rangeColor", isSignal: true, isRequired: false, transformFunction: null }, showValue: { classPropertyName: "showValue", publicName: "showValue", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", change: "change", end: "end" }, providers: [
|
|
4341
|
+
{
|
|
4342
|
+
provide: NG_VALUE_ACCESSOR,
|
|
4343
|
+
useExisting: forwardRef(() => KnobComponent),
|
|
4344
|
+
multi: true,
|
|
4345
|
+
},
|
|
4346
|
+
], viewQueries: [{ propertyName: "knobSvg", first: true, predicate: ["knobSvg"], descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
4347
|
+
<div
|
|
4348
|
+
class="shk-knob"
|
|
4349
|
+
[class.shk-knob-disabled]="effectiveDisabled()"
|
|
4350
|
+
[style]="{ width: size() + 'px', height: size() + 'px' }"
|
|
4351
|
+
>
|
|
4352
|
+
<svg
|
|
4353
|
+
#knobSvg
|
|
4354
|
+
class="shk-knob-svg"
|
|
4355
|
+
[attr.width]="size()"
|
|
4356
|
+
[attr.height]="size()"
|
|
4357
|
+
[attr.viewBox]="'0 0 ' + size() + ' ' + size()"
|
|
4358
|
+
role="slider"
|
|
4359
|
+
tabindex="0"
|
|
4360
|
+
[attr.aria-label]="ariaLabel() || 'Valor'"
|
|
4361
|
+
[attr.aria-valuemin]="min()"
|
|
4362
|
+
[attr.aria-valuemax]="max()"
|
|
4363
|
+
[attr.aria-valuenow]="value()"
|
|
4364
|
+
[class.shk-knob-svg-focusable]="!effectiveDisabled()"
|
|
4365
|
+
(pointerdown)="onPointerDown($event)"
|
|
4366
|
+
(pointermove)="onPointerMove($event)"
|
|
4367
|
+
(pointerup)="onPointerUp($event)"
|
|
4368
|
+
(pointercancel)="onPointerUp($event)"
|
|
4369
|
+
(keydown.arrowup)="updateByKey(step())"
|
|
4370
|
+
(keydown.arrowright)="updateByKey(step())"
|
|
4371
|
+
(keydown.arrowdown)="updateByKey(-step())"
|
|
4372
|
+
(keydown.arrowleft)="updateByKey(-step())"
|
|
4373
|
+
>
|
|
4374
|
+
<path
|
|
4375
|
+
[attr.d]="rangeArc()"
|
|
4376
|
+
fill="none"
|
|
4377
|
+
[style.stroke]="rangeColor()"
|
|
4378
|
+
[attr.stroke-width]="strokeWidth()"
|
|
4379
|
+
stroke-linecap="round"
|
|
4380
|
+
></path>
|
|
4381
|
+
<path
|
|
4382
|
+
[attr.d]="valueArc()"
|
|
4383
|
+
fill="none"
|
|
4384
|
+
[style.stroke]="valueColor()"
|
|
4385
|
+
[attr.stroke-width]="strokeWidth()"
|
|
4386
|
+
stroke-linecap="round"
|
|
4387
|
+
></path>
|
|
4388
|
+
</svg>
|
|
4389
|
+
@if (showValue()) {
|
|
4390
|
+
<div class="shk-knob-label">{{ value() }}</div>
|
|
4391
|
+
}
|
|
4392
|
+
</div>
|
|
4393
|
+
`, isInline: true, styles: [".shk-knob{position:relative;display:inline-block}.shk-knob-svg{display:block;touch-action:none}.shk-knob-svg-focusable{cursor:pointer;outline:none}.shk-knob-svg-focusable:focus-visible{border-radius:9999px;outline:2px solid var(--shk-primary-400);outline-offset:2px}.shk-knob-disabled{opacity:.55}.shk-knob-disabled .shk-knob-svg{cursor:not-allowed}.shk-knob-label{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);font-size:1.125rem;font-weight:600;color:var(--shk-surface-800);pointer-events:none;-webkit-user-select:none;user-select:none}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
4394
|
+
}
|
|
4395
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: KnobComponent, decorators: [{
|
|
4396
|
+
type: Component,
|
|
4397
|
+
args: [{ selector: 'shk-knob', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
4398
|
+
<div
|
|
4399
|
+
class="shk-knob"
|
|
4400
|
+
[class.shk-knob-disabled]="effectiveDisabled()"
|
|
4401
|
+
[style]="{ width: size() + 'px', height: size() + 'px' }"
|
|
4402
|
+
>
|
|
4403
|
+
<svg
|
|
4404
|
+
#knobSvg
|
|
4405
|
+
class="shk-knob-svg"
|
|
4406
|
+
[attr.width]="size()"
|
|
4407
|
+
[attr.height]="size()"
|
|
4408
|
+
[attr.viewBox]="'0 0 ' + size() + ' ' + size()"
|
|
4409
|
+
role="slider"
|
|
4410
|
+
tabindex="0"
|
|
4411
|
+
[attr.aria-label]="ariaLabel() || 'Valor'"
|
|
4412
|
+
[attr.aria-valuemin]="min()"
|
|
4413
|
+
[attr.aria-valuemax]="max()"
|
|
4414
|
+
[attr.aria-valuenow]="value()"
|
|
4415
|
+
[class.shk-knob-svg-focusable]="!effectiveDisabled()"
|
|
4416
|
+
(pointerdown)="onPointerDown($event)"
|
|
4417
|
+
(pointermove)="onPointerMove($event)"
|
|
4418
|
+
(pointerup)="onPointerUp($event)"
|
|
4419
|
+
(pointercancel)="onPointerUp($event)"
|
|
4420
|
+
(keydown.arrowup)="updateByKey(step())"
|
|
4421
|
+
(keydown.arrowright)="updateByKey(step())"
|
|
4422
|
+
(keydown.arrowdown)="updateByKey(-step())"
|
|
4423
|
+
(keydown.arrowleft)="updateByKey(-step())"
|
|
4424
|
+
>
|
|
4425
|
+
<path
|
|
4426
|
+
[attr.d]="rangeArc()"
|
|
4427
|
+
fill="none"
|
|
4428
|
+
[style.stroke]="rangeColor()"
|
|
4429
|
+
[attr.stroke-width]="strokeWidth()"
|
|
4430
|
+
stroke-linecap="round"
|
|
4431
|
+
></path>
|
|
4432
|
+
<path
|
|
4433
|
+
[attr.d]="valueArc()"
|
|
4434
|
+
fill="none"
|
|
4435
|
+
[style.stroke]="valueColor()"
|
|
4436
|
+
[attr.stroke-width]="strokeWidth()"
|
|
4437
|
+
stroke-linecap="round"
|
|
4438
|
+
></path>
|
|
4439
|
+
</svg>
|
|
4440
|
+
@if (showValue()) {
|
|
4441
|
+
<div class="shk-knob-label">{{ value() }}</div>
|
|
4442
|
+
}
|
|
4443
|
+
</div>
|
|
4444
|
+
`, providers: [
|
|
4445
|
+
{
|
|
4446
|
+
provide: NG_VALUE_ACCESSOR,
|
|
4447
|
+
useExisting: forwardRef(() => KnobComponent),
|
|
4448
|
+
multi: true,
|
|
4449
|
+
},
|
|
4450
|
+
], styles: [".shk-knob{position:relative;display:inline-block}.shk-knob-svg{display:block;touch-action:none}.shk-knob-svg-focusable{cursor:pointer;outline:none}.shk-knob-svg-focusable:focus-visible{border-radius:9999px;outline:2px solid var(--shk-primary-400);outline-offset:2px}.shk-knob-disabled{opacity:.55}.shk-knob-disabled .shk-knob-svg{cursor:not-allowed}.shk-knob-label{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);font-size:1.125rem;font-weight:600;color:var(--shk-surface-800);pointer-events:none;-webkit-user-select:none;user-select:none}\n"] }]
|
|
4451
|
+
}], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], step: [{ type: i0.Input, args: [{ isSignal: true, alias: "step", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], strokeWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "strokeWidth", required: false }] }], valueColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "valueColor", required: false }] }], rangeColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "rangeColor", required: false }] }], showValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "showValue", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], change: [{ type: i0.Output, args: ["change"] }], end: [{ type: i0.Output, args: ["end"] }], knobSvg: [{ type: i0.ViewChild, args: ['knobSvg', { isSignal: true }] }] } });
|
|
4452
|
+
|
|
4453
|
+
/** Circular indeterminate spinner. */
|
|
4454
|
+
class LoadingSpinnerComponent {
|
|
4455
|
+
constructor() {
|
|
4456
|
+
/** Diameter of the spinner. */
|
|
4457
|
+
this.size = input('2rem', ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
4458
|
+
/** Border thickness in px. */
|
|
4459
|
+
this.thickness = input(3, ...(ngDevMode ? [{ debugName: "thickness" }] : /* istanbul ignore next */ []));
|
|
4460
|
+
/** Color of the moving arc. Defaults to the primary token. */
|
|
4461
|
+
this.color = input('', ...(ngDevMode ? [{ debugName: "color" }] : /* istanbul ignore next */ []));
|
|
4462
|
+
/** Optional text shown under the spinner. */
|
|
4463
|
+
this.label = input('', ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
|
|
4464
|
+
}
|
|
4465
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: LoadingSpinnerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
4466
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.18", type: LoadingSpinnerComponent, isStandalone: true, selector: "shk-loading-spinner", inputs: { size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, thickness: { classPropertyName: "thickness", publicName: "thickness", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
|
|
4467
|
+
<div
|
|
4468
|
+
class="shk-loading-spinner"
|
|
4469
|
+
[style]="{
|
|
4470
|
+
width: size(),
|
|
4471
|
+
height: size(),
|
|
4472
|
+
}"
|
|
4473
|
+
role="status"
|
|
4474
|
+
aria-label="Cargando"
|
|
4475
|
+
>
|
|
4476
|
+
<div
|
|
4477
|
+
class="shk-loading-spinner-circle"
|
|
4478
|
+
[style]="{
|
|
4479
|
+
borderWidth: thickness(),
|
|
4480
|
+
borderTopColor: color() || undefined,
|
|
4481
|
+
}"
|
|
4482
|
+
></div>
|
|
4483
|
+
</div>
|
|
4484
|
+
@if (label()) {
|
|
4485
|
+
<span class="shk-loading-spinner-label">{{ label() }}</span>
|
|
4486
|
+
}
|
|
4487
|
+
`, isInline: true, styles: [":host{display:inline-flex;flex-direction:column;align-items:center;justify-content:center;gap:.5rem}.shk-loading-spinner{display:inline-block}.shk-loading-spinner-circle{width:100%;height:100%;border-radius:9999px;border-style:solid;border-color:color-mix(in srgb,var(--shk-primary-600) 20%,transparent);border-top-color:var(--shk-primary-600);animation:shk-loading-spin .8s linear infinite}.shk-loading-spinner-label{font-size:.8125rem;color:var(--shk-surface-500)}@keyframes shk-loading-spin{to{transform:rotate(360deg)}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
4488
|
+
}
|
|
4489
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: LoadingSpinnerComponent, decorators: [{
|
|
4490
|
+
type: Component,
|
|
4491
|
+
args: [{ selector: 'shk-loading-spinner', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
4492
|
+
<div
|
|
4493
|
+
class="shk-loading-spinner"
|
|
4494
|
+
[style]="{
|
|
4495
|
+
width: size(),
|
|
4496
|
+
height: size(),
|
|
4497
|
+
}"
|
|
4498
|
+
role="status"
|
|
4499
|
+
aria-label="Cargando"
|
|
4500
|
+
>
|
|
4501
|
+
<div
|
|
4502
|
+
class="shk-loading-spinner-circle"
|
|
4503
|
+
[style]="{
|
|
4504
|
+
borderWidth: thickness(),
|
|
4505
|
+
borderTopColor: color() || undefined,
|
|
4506
|
+
}"
|
|
4507
|
+
></div>
|
|
4508
|
+
</div>
|
|
4509
|
+
@if (label()) {
|
|
4510
|
+
<span class="shk-loading-spinner-label">{{ label() }}</span>
|
|
4511
|
+
}
|
|
4512
|
+
`, styles: [":host{display:inline-flex;flex-direction:column;align-items:center;justify-content:center;gap:.5rem}.shk-loading-spinner{display:inline-block}.shk-loading-spinner-circle{width:100%;height:100%;border-radius:9999px;border-style:solid;border-color:color-mix(in srgb,var(--shk-primary-600) 20%,transparent);border-top-color:var(--shk-primary-600);animation:shk-loading-spin .8s linear infinite}.shk-loading-spinner-label{font-size:.8125rem;color:var(--shk-surface-500)}@keyframes shk-loading-spin{to{transform:rotate(360deg)}}\n"] }]
|
|
4513
|
+
}], propDecorators: { size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], thickness: [{ type: i0.Input, args: [{ isSignal: true, alias: "thickness", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }] } });
|
|
4514
|
+
|
|
4515
|
+
/** Progress bar with an indeterminate sweep or a determinate percentage. */
|
|
4516
|
+
class LoadingBarComponent {
|
|
4517
|
+
constructor() {
|
|
4518
|
+
/** Height of the bar. */
|
|
4519
|
+
this.height = input('0.25rem', ...(ngDevMode ? [{ debugName: "height" }] : /* istanbul ignore next */ []));
|
|
4520
|
+
/** When true the bar animates without a known percentage. */
|
|
4521
|
+
this.indeterminate = input(true, ...(ngDevMode ? [{ debugName: "indeterminate" }] : /* istanbul ignore next */ []));
|
|
4522
|
+
/** Current progress 0-100 when `indeterminate` is false. */
|
|
4523
|
+
this.progress = input(0, ...(ngDevMode ? [{ debugName: "progress" }] : /* istanbul ignore next */ []));
|
|
4524
|
+
/** Fill color. Defaults to the primary token. */
|
|
4525
|
+
this.color = input('', ...(ngDevMode ? [{ debugName: "color" }] : /* istanbul ignore next */ []));
|
|
4526
|
+
/** Renders fully rounded ends. */
|
|
4527
|
+
this.rounded = input(true, ...(ngDevMode ? [{ debugName: "rounded" }] : /* istanbul ignore next */ []));
|
|
4528
|
+
this.fillStyle = computed(() => {
|
|
4529
|
+
if (this.indeterminate())
|
|
4530
|
+
return {};
|
|
4531
|
+
return { width: `${Math.max(0, Math.min(100, this.progress()))}%` };
|
|
4532
|
+
}, ...(ngDevMode ? [{ debugName: "fillStyle" }] : /* istanbul ignore next */ []));
|
|
4533
|
+
}
|
|
4534
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: LoadingBarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
4535
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.18", type: LoadingBarComponent, isStandalone: true, selector: "shk-loading-bar", inputs: { height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, indeterminate: { classPropertyName: "indeterminate", publicName: "indeterminate", isSignal: true, isRequired: false, transformFunction: null }, progress: { classPropertyName: "progress", publicName: "progress", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, rounded: { classPropertyName: "rounded", publicName: "rounded", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
|
|
4536
|
+
<div
|
|
4537
|
+
class="shk-loading-bar"
|
|
4538
|
+
[class.shk-loading-bar-indeterminate]="indeterminate()"
|
|
4539
|
+
[style]="{ height: height(), borderRadius: rounded() ? '9999px' : undefined }"
|
|
4540
|
+
role="progressbar"
|
|
4541
|
+
[attr.aria-valuenow]="indeterminate() ? undefined : progress()"
|
|
4542
|
+
aria-label="Progreso"
|
|
4543
|
+
>
|
|
4544
|
+
<div
|
|
4545
|
+
class="shk-loading-bar-fill"
|
|
4546
|
+
[style]="fillStyle()"
|
|
4547
|
+
[style.background-color]="color() || undefined"
|
|
4548
|
+
[style.border-radius]="rounded() ? '9999px' : undefined"
|
|
4549
|
+
></div>
|
|
4550
|
+
</div>
|
|
4551
|
+
`, isInline: true, styles: [".shk-loading-bar{width:100%;position:relative;overflow:hidden;background-color:var(--shk-surface-200)}.shk-loading-bar-fill{height:100%;background-color:var(--shk-primary-600);transition:width .3s ease-in-out}.shk-loading-bar-indeterminate .shk-loading-bar-fill{width:40%;animation:shk-loading-bar-sweep 1.2s ease-in-out infinite}@keyframes shk-loading-bar-sweep{0%{transform:translate(-100%)}to{transform:translate(350%)}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
4552
|
+
}
|
|
4553
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: LoadingBarComponent, decorators: [{
|
|
4554
|
+
type: Component,
|
|
4555
|
+
args: [{ selector: 'shk-loading-bar', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
4556
|
+
<div
|
|
4557
|
+
class="shk-loading-bar"
|
|
4558
|
+
[class.shk-loading-bar-indeterminate]="indeterminate()"
|
|
4559
|
+
[style]="{ height: height(), borderRadius: rounded() ? '9999px' : undefined }"
|
|
4560
|
+
role="progressbar"
|
|
4561
|
+
[attr.aria-valuenow]="indeterminate() ? undefined : progress()"
|
|
4562
|
+
aria-label="Progreso"
|
|
4563
|
+
>
|
|
4564
|
+
<div
|
|
4565
|
+
class="shk-loading-bar-fill"
|
|
4566
|
+
[style]="fillStyle()"
|
|
4567
|
+
[style.background-color]="color() || undefined"
|
|
4568
|
+
[style.border-radius]="rounded() ? '9999px' : undefined"
|
|
4569
|
+
></div>
|
|
4570
|
+
</div>
|
|
4571
|
+
`, styles: [".shk-loading-bar{width:100%;position:relative;overflow:hidden;background-color:var(--shk-surface-200)}.shk-loading-bar-fill{height:100%;background-color:var(--shk-primary-600);transition:width .3s ease-in-out}.shk-loading-bar-indeterminate .shk-loading-bar-fill{width:40%;animation:shk-loading-bar-sweep 1.2s ease-in-out infinite}@keyframes shk-loading-bar-sweep{0%{transform:translate(-100%)}to{transform:translate(350%)}}\n"] }]
|
|
4572
|
+
}], propDecorators: { height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }], indeterminate: [{ type: i0.Input, args: [{ isSignal: true, alias: "indeterminate", required: false }] }], progress: [{ type: i0.Input, args: [{ isSignal: true, alias: "progress", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], rounded: [{ type: i0.Input, args: [{ isSignal: true, alias: "rounded", required: false }] }] } });
|
|
4573
|
+
|
|
4574
|
+
/** Wraps content and shows a centered spinner over it while `visible` is true. */
|
|
4575
|
+
class LoadingOverlayComponent {
|
|
4576
|
+
constructor() {
|
|
4577
|
+
/** Controls whether the spinner layer is shown. */
|
|
4578
|
+
this.visible = input(false, ...(ngDevMode ? [{ debugName: "visible" }] : /* istanbul ignore next */ []));
|
|
4579
|
+
/** Optional text under the spinner. */
|
|
4580
|
+
this.message = input('', ...(ngDevMode ? [{ debugName: "message" }] : /* istanbul ignore next */ []));
|
|
4581
|
+
/** Diameter of the spinner. */
|
|
4582
|
+
this.spinnerSize = input('2.5rem', ...(ngDevMode ? [{ debugName: "spinnerSize" }] : /* istanbul ignore next */ []));
|
|
4583
|
+
/** Spinner color. Defaults to the primary token. */
|
|
4584
|
+
this.color = input('', ...(ngDevMode ? [{ debugName: "color" }] : /* istanbul ignore next */ []));
|
|
4585
|
+
/** Backdrop behind the spinner. */
|
|
4586
|
+
this.backdrop = input('transparent', ...(ngDevMode ? [{ debugName: "backdrop" }] : /* istanbul ignore next */ []));
|
|
4587
|
+
}
|
|
4588
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: LoadingOverlayComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
4589
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.18", type: LoadingOverlayComponent, isStandalone: true, selector: "shk-loading-overlay", inputs: { visible: { classPropertyName: "visible", publicName: "visible", isSignal: true, isRequired: false, transformFunction: null }, message: { classPropertyName: "message", publicName: "message", isSignal: true, isRequired: false, transformFunction: null }, spinnerSize: { classPropertyName: "spinnerSize", publicName: "spinnerSize", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, backdrop: { classPropertyName: "backdrop", publicName: "backdrop", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
|
|
4590
|
+
<div
|
|
4591
|
+
class="shk-loading-overlay"
|
|
4592
|
+
[class.shk-loading-overlay-light]="backdrop() === 'light'"
|
|
4593
|
+
[class.shk-loading-overlay-dark]="backdrop() === 'dark'"
|
|
4594
|
+
>
|
|
4595
|
+
<ng-content></ng-content>
|
|
4596
|
+
@if (visible()) {
|
|
4597
|
+
<div class="shk-loading-overlay-layer">
|
|
4598
|
+
<shk-loading-spinner [size]="spinnerSize()" [color]="color()" [label]="message()" />
|
|
4599
|
+
</div>
|
|
4600
|
+
}
|
|
4601
|
+
</div>
|
|
4602
|
+
`, isInline: true, styles: [":host{display:block}.shk-loading-overlay{position:relative;min-height:1.5rem}.shk-loading-overlay-layer{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;z-index:10}.shk-loading-overlay-light .shk-loading-overlay-layer{background-color:#ffffffa6}.dark .shk-loading-overlay-light .shk-loading-overlay-layer{background-color:#0000008c}.shk-loading-overlay-dark .shk-loading-overlay-layer{background-color:#0006}\n"], dependencies: [{ kind: "component", type: LoadingSpinnerComponent, selector: "shk-loading-spinner", inputs: ["size", "thickness", "color", "label"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
4603
|
+
}
|
|
4604
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: LoadingOverlayComponent, decorators: [{
|
|
4605
|
+
type: Component,
|
|
4606
|
+
args: [{ selector: 'shk-loading-overlay', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [LoadingSpinnerComponent], template: `
|
|
4607
|
+
<div
|
|
4608
|
+
class="shk-loading-overlay"
|
|
4609
|
+
[class.shk-loading-overlay-light]="backdrop() === 'light'"
|
|
4610
|
+
[class.shk-loading-overlay-dark]="backdrop() === 'dark'"
|
|
4611
|
+
>
|
|
4612
|
+
<ng-content></ng-content>
|
|
4613
|
+
@if (visible()) {
|
|
4614
|
+
<div class="shk-loading-overlay-layer">
|
|
4615
|
+
<shk-loading-spinner [size]="spinnerSize()" [color]="color()" [label]="message()" />
|
|
4616
|
+
</div>
|
|
4617
|
+
}
|
|
4618
|
+
</div>
|
|
4619
|
+
`, styles: [":host{display:block}.shk-loading-overlay{position:relative;min-height:1.5rem}.shk-loading-overlay-layer{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;z-index:10}.shk-loading-overlay-light .shk-loading-overlay-layer{background-color:#ffffffa6}.dark .shk-loading-overlay-light .shk-loading-overlay-layer{background-color:#0000008c}.shk-loading-overlay-dark .shk-loading-overlay-layer{background-color:#0006}\n"] }]
|
|
4620
|
+
}], propDecorators: { visible: [{ type: i0.Input, args: [{ isSignal: true, alias: "visible", required: false }] }], message: [{ type: i0.Input, args: [{ isSignal: true, alias: "message", required: false }] }], spinnerSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "spinnerSize", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], backdrop: [{ type: i0.Input, args: [{ isSignal: true, alias: "backdrop", required: false }] }] } });
|
|
4621
|
+
|
|
4622
|
+
/** Bouncing dots loading indicator. */
|
|
4623
|
+
class LoadingDotsComponent {
|
|
4624
|
+
constructor() {
|
|
4625
|
+
/** Diameter of each dot. */
|
|
4626
|
+
this.size = input('0.5rem', ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
4627
|
+
/** Dot color. Defaults to the primary token. */
|
|
4628
|
+
this.color = input('', ...(ngDevMode ? [{ debugName: "color" }] : /* istanbul ignore next */ []));
|
|
4629
|
+
/** Number of dots. */
|
|
4630
|
+
this.count = input(3, ...(ngDevMode ? [{ debugName: "count" }] : /* istanbul ignore next */ []));
|
|
4631
|
+
/** Optional text shown next to the dots. */
|
|
4632
|
+
this.label = input('', ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
|
|
4633
|
+
this.dots = computed(() => Array.from({ length: Math.max(1, this.count()) }), ...(ngDevMode ? [{ debugName: "dots" }] : /* istanbul ignore next */ []));
|
|
4634
|
+
}
|
|
4635
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: LoadingDotsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
4636
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.18", type: LoadingDotsComponent, isStandalone: true, selector: "shk-loading-dots", inputs: { size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, count: { classPropertyName: "count", publicName: "count", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
|
|
4637
|
+
<span class="shk-loading-dots" role="status" aria-label="Cargando">
|
|
4638
|
+
@for (dot of dots(); track $index) {
|
|
4639
|
+
<span
|
|
4640
|
+
class="shk-loading-dot"
|
|
4641
|
+
[style]="{
|
|
4642
|
+
width: size(),
|
|
4643
|
+
height: size(),
|
|
4644
|
+
backgroundColor: color() || undefined,
|
|
4645
|
+
animationDelay: $index * 0.12 + 's',
|
|
4646
|
+
}"
|
|
4647
|
+
></span>
|
|
4648
|
+
}
|
|
4649
|
+
</span>
|
|
4650
|
+
@if (label()) {
|
|
4651
|
+
<span class="shk-loading-dots-label">{{ label() }}</span>
|
|
4652
|
+
}
|
|
4653
|
+
`, isInline: true, styles: [":host{display:inline-flex;align-items:center;gap:.5rem}.shk-loading-dots{display:inline-flex;align-items:center;gap:.25rem}.shk-loading-dot{border-radius:9999px;background-color:var(--shk-primary-600);animation:shk-loading-dots-bounce 1.4s ease-in-out infinite}.shk-loading-dots-label{font-size:.8125rem;color:var(--shk-surface-500)}@keyframes shk-loading-dots-bounce{0%,80%,to{transform:scale(.6);opacity:.5}40%{transform:scale(1);opacity:1}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
4654
|
+
}
|
|
4655
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: LoadingDotsComponent, decorators: [{
|
|
4656
|
+
type: Component,
|
|
4657
|
+
args: [{ selector: 'shk-loading-dots', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
4658
|
+
<span class="shk-loading-dots" role="status" aria-label="Cargando">
|
|
4659
|
+
@for (dot of dots(); track $index) {
|
|
4660
|
+
<span
|
|
4661
|
+
class="shk-loading-dot"
|
|
4662
|
+
[style]="{
|
|
4663
|
+
width: size(),
|
|
4664
|
+
height: size(),
|
|
4665
|
+
backgroundColor: color() || undefined,
|
|
4666
|
+
animationDelay: $index * 0.12 + 's',
|
|
4667
|
+
}"
|
|
4668
|
+
></span>
|
|
4669
|
+
}
|
|
4670
|
+
</span>
|
|
4671
|
+
@if (label()) {
|
|
4672
|
+
<span class="shk-loading-dots-label">{{ label() }}</span>
|
|
4673
|
+
}
|
|
4674
|
+
`, styles: [":host{display:inline-flex;align-items:center;gap:.5rem}.shk-loading-dots{display:inline-flex;align-items:center;gap:.25rem}.shk-loading-dot{border-radius:9999px;background-color:var(--shk-primary-600);animation:shk-loading-dots-bounce 1.4s ease-in-out infinite}.shk-loading-dots-label{font-size:.8125rem;color:var(--shk-surface-500)}@keyframes shk-loading-dots-bounce{0%,80%,to{transform:scale(.6);opacity:.5}40%{transform:scale(1);opacity:1}}\n"] }]
|
|
4675
|
+
}], propDecorators: { size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], count: [{ type: i0.Input, args: [{ isSignal: true, alias: "count", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }] } });
|
|
4676
|
+
|
|
4677
|
+
/** Shared state between shk-tabs and its projected tab/tabpanel children. */
|
|
4678
|
+
class TabsService {
|
|
4679
|
+
constructor() {
|
|
4680
|
+
this.active = signal(undefined, ...(ngDevMode ? [{ debugName: "active" }] : /* istanbul ignore next */ []));
|
|
4681
|
+
}
|
|
4682
|
+
select(value) {
|
|
4683
|
+
this.active.set(value);
|
|
4684
|
+
}
|
|
4685
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: TabsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
4686
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: TabsService }); }
|
|
4687
|
+
}
|
|
4688
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: TabsService, decorators: [{
|
|
4689
|
+
type: Injectable
|
|
4690
|
+
}] });
|
|
4691
|
+
|
|
4692
|
+
/** Container for tab panels. Renders its projected shk-tablist/shh-tab content. */
|
|
4693
|
+
class TabsComponent {
|
|
4694
|
+
constructor() {
|
|
4695
|
+
/** Currently active tab value. Two-way bindable with [(value)]. */
|
|
4696
|
+
this.value = model(undefined, ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
|
|
4697
|
+
/** Allows the tab header to scroll horizontally when it overflows. */
|
|
4698
|
+
this.scrollable = input(false, ...(ngDevMode ? [{ debugName: "scrollable" }] : /* istanbul ignore next */ []));
|
|
4699
|
+
this.tabsSvc = inject(TabsService);
|
|
4700
|
+
effect(() => {
|
|
4701
|
+
this.tabsSvc.active.set(this.value());
|
|
4702
|
+
});
|
|
4703
|
+
effect(() => {
|
|
4704
|
+
const active = this.tabsSvc.active();
|
|
4705
|
+
if (active !== this.value()) {
|
|
4706
|
+
this.value.set(active);
|
|
4707
|
+
}
|
|
4708
|
+
});
|
|
4709
|
+
}
|
|
4710
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: TabsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
4711
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.18", type: TabsComponent, isStandalone: true, selector: "shk-tabs", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, scrollable: { classPropertyName: "scrollable", publicName: "scrollable", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, providers: [TabsService], ngImport: i0, template: `
|
|
4712
|
+
<div class="shk-tabs">
|
|
4713
|
+
<ng-content></ng-content>
|
|
4714
|
+
</div>
|
|
4715
|
+
`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
4716
|
+
}
|
|
4717
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: TabsComponent, decorators: [{
|
|
4718
|
+
type: Component,
|
|
4719
|
+
args: [{
|
|
4720
|
+
selector: 'shk-tabs',
|
|
4721
|
+
standalone: true,
|
|
4722
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
4723
|
+
providers: [TabsService],
|
|
4724
|
+
template: `
|
|
4725
|
+
<div class="shk-tabs">
|
|
4726
|
+
<ng-content></ng-content>
|
|
4727
|
+
</div>
|
|
4728
|
+
`,
|
|
4729
|
+
}]
|
|
4730
|
+
}], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], scrollable: [{ type: i0.Input, args: [{ isSignal: true, alias: "scrollable", required: false }] }] } });
|
|
4731
|
+
|
|
4732
|
+
/** Horizontal bar that contains the shk-tab headers. */
|
|
4733
|
+
class TablistComponent {
|
|
4734
|
+
constructor() {
|
|
4735
|
+
/** Allows the header to scroll horizontally when it overflows. */
|
|
4736
|
+
this.scrollable = input(false, ...(ngDevMode ? [{ debugName: "scrollable" }] : /* istanbul ignore next */ []));
|
|
4737
|
+
/** Extra CSS classes. */
|
|
4738
|
+
this.styleClass = input('', ...(ngDevMode ? [{ debugName: "styleClass" }] : /* istanbul ignore next */ []));
|
|
4739
|
+
}
|
|
4740
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: TablistComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
4741
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.18", type: TablistComponent, isStandalone: true, selector: "shk-tablist", inputs: { scrollable: { classPropertyName: "scrollable", publicName: "scrollable", isSignal: true, isRequired: false, transformFunction: null }, styleClass: { classPropertyName: "styleClass", publicName: "styleClass", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
|
|
4742
|
+
<div
|
|
4743
|
+
class="shk-tablist"
|
|
4744
|
+
[class.shk-tablist-scrollable]="scrollable()"
|
|
4745
|
+
[class]="styleClass()"
|
|
4746
|
+
role="tablist"
|
|
4747
|
+
aria-orientation="horizontal"
|
|
4748
|
+
>
|
|
4749
|
+
<ng-content></ng-content>
|
|
4750
|
+
</div>
|
|
4751
|
+
`, isInline: true, styles: [".shk-tablist{display:flex;align-items:center;gap:.25rem;flex-wrap:wrap;border-bottom:1px solid var(--shk-surface-200)}.dark .shk-tablist{border-bottom-color:var(--shk-surface-200)}.shk-tablist-scrollable{flex-wrap:nowrap;overflow-x:auto;scrollbar-width:thin}.shk-tablist-scrollable::-webkit-scrollbar{height:6px}.shk-tablist-scrollable::-webkit-scrollbar-thumb{background:var(--shk-surface-300);border-radius:9999px}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
4752
|
+
}
|
|
4753
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: TablistComponent, decorators: [{
|
|
4754
|
+
type: Component,
|
|
4755
|
+
args: [{ selector: 'shk-tablist', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
4756
|
+
<div
|
|
4757
|
+
class="shk-tablist"
|
|
4758
|
+
[class.shk-tablist-scrollable]="scrollable()"
|
|
4759
|
+
[class]="styleClass()"
|
|
4760
|
+
role="tablist"
|
|
4761
|
+
aria-orientation="horizontal"
|
|
4762
|
+
>
|
|
4763
|
+
<ng-content></ng-content>
|
|
4764
|
+
</div>
|
|
4765
|
+
`, styles: [".shk-tablist{display:flex;align-items:center;gap:.25rem;flex-wrap:wrap;border-bottom:1px solid var(--shk-surface-200)}.dark .shk-tablist{border-bottom-color:var(--shk-surface-200)}.shk-tablist-scrollable{flex-wrap:nowrap;overflow-x:auto;scrollbar-width:thin}.shk-tablist-scrollable::-webkit-scrollbar{height:6px}.shk-tablist-scrollable::-webkit-scrollbar-thumb{background:var(--shk-surface-300);border-radius:9999px}\n"] }]
|
|
4766
|
+
}], propDecorators: { scrollable: [{ type: i0.Input, args: [{ isSignal: true, alias: "scrollable", required: false }] }], styleClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "styleClass", required: false }] }] } });
|
|
4767
|
+
|
|
4768
|
+
/** A single tab header. Clicking it activates the matching panel. */
|
|
4769
|
+
class TabComponent {
|
|
4770
|
+
constructor() {
|
|
4771
|
+
/** Value that links this tab to its panel. */
|
|
4772
|
+
this.value = input(undefined, ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
|
|
4773
|
+
/** Disables the tab. */
|
|
4774
|
+
this.disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
|
|
4775
|
+
/** Extra CSS classes. */
|
|
4776
|
+
this.styleClass = input('', ...(ngDevMode ? [{ debugName: "styleClass" }] : /* istanbul ignore next */ []));
|
|
4777
|
+
this.tabsSvc = inject(TabsService);
|
|
4778
|
+
this.active = computed(() => this.tabsSvc.active() === this.value(), ...(ngDevMode ? [{ debugName: "active" }] : /* istanbul ignore next */ []));
|
|
4779
|
+
}
|
|
4780
|
+
select() {
|
|
4781
|
+
if (this.disabled())
|
|
4782
|
+
return;
|
|
4783
|
+
this.tabsSvc.select(this.value());
|
|
4784
|
+
}
|
|
4785
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: TabComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
4786
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.18", type: TabComponent, isStandalone: true, selector: "shk-tab", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, styleClass: { classPropertyName: "styleClass", publicName: "styleClass", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
|
|
4787
|
+
<button
|
|
4788
|
+
type="button"
|
|
4789
|
+
class="shk-tab"
|
|
4790
|
+
[class]="styleClass()"
|
|
4791
|
+
[class.shk-tab-active]="active()"
|
|
4792
|
+
[class.shk-tab-disabled]="disabled()"
|
|
4793
|
+
role="tab"
|
|
4794
|
+
[attr.aria-selected]="active()"
|
|
4795
|
+
[disabled]="disabled()"
|
|
4796
|
+
(click)="select()"
|
|
4797
|
+
>
|
|
4798
|
+
<ng-content></ng-content>
|
|
4799
|
+
</button>
|
|
4800
|
+
`, isInline: true, styles: [".shk-tab{display:inline-flex;align-items:center;gap:.375rem;padding:.625rem 1rem;font-size:.875rem;font-weight:500;color:var(--shk-surface-500);background:transparent;border:none;border-bottom:2px solid transparent;margin-bottom:-1px;cursor:pointer;white-space:nowrap;transition:color .15s ease-in-out,border-color .15s ease-in-out,background-color .15s ease-in-out}.shk-tab:hover:not(:disabled):not(.shk-tab-active){color:var(--shk-surface-800)}.shk-tab-active{color:var(--shk-primary-600);border-bottom-color:var(--shk-primary-600);font-weight:600}.dark .shk-tab-active{color:var(--shk-primary-400);border-bottom-color:var(--shk-primary-400)}.shk-tab:disabled{opacity:.5;cursor:not-allowed}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
4801
|
+
}
|
|
4802
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: TabComponent, decorators: [{
|
|
4803
|
+
type: Component,
|
|
4804
|
+
args: [{ selector: 'shk-tab', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
4805
|
+
<button
|
|
4806
|
+
type="button"
|
|
4807
|
+
class="shk-tab"
|
|
4808
|
+
[class]="styleClass()"
|
|
4809
|
+
[class.shk-tab-active]="active()"
|
|
4810
|
+
[class.shk-tab-disabled]="disabled()"
|
|
4811
|
+
role="tab"
|
|
4812
|
+
[attr.aria-selected]="active()"
|
|
4813
|
+
[disabled]="disabled()"
|
|
4814
|
+
(click)="select()"
|
|
4815
|
+
>
|
|
4816
|
+
<ng-content></ng-content>
|
|
4817
|
+
</button>
|
|
4818
|
+
`, styles: [".shk-tab{display:inline-flex;align-items:center;gap:.375rem;padding:.625rem 1rem;font-size:.875rem;font-weight:500;color:var(--shk-surface-500);background:transparent;border:none;border-bottom:2px solid transparent;margin-bottom:-1px;cursor:pointer;white-space:nowrap;transition:color .15s ease-in-out,border-color .15s ease-in-out,background-color .15s ease-in-out}.shk-tab:hover:not(:disabled):not(.shk-tab-active){color:var(--shk-surface-800)}.shk-tab-active{color:var(--shk-primary-600);border-bottom-color:var(--shk-primary-600);font-weight:600}.dark .shk-tab-active{color:var(--shk-primary-400);border-bottom-color:var(--shk-primary-400)}.shk-tab:disabled{opacity:.5;cursor:not-allowed}\n"] }]
|
|
4819
|
+
}], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], styleClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "styleClass", required: false }] }] } });
|
|
4820
|
+
|
|
4821
|
+
/** Container that groups the shk-tabpanel content sections. */
|
|
4822
|
+
class TabpanelsComponent {
|
|
4823
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: TabpanelsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
4824
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.18", type: TabpanelsComponent, isStandalone: true, selector: "shk-tabpanels", ngImport: i0, template: `
|
|
4825
|
+
<div class="shk-tabpanels">
|
|
4826
|
+
<ng-content></ng-content>
|
|
4827
|
+
</div>
|
|
4828
|
+
`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
4829
|
+
}
|
|
4830
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: TabpanelsComponent, decorators: [{
|
|
4831
|
+
type: Component,
|
|
4832
|
+
args: [{
|
|
4833
|
+
selector: 'shk-tabpanels',
|
|
4834
|
+
standalone: true,
|
|
4835
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
4836
|
+
template: `
|
|
4837
|
+
<div class="shk-tabpanels">
|
|
4838
|
+
<ng-content></ng-content>
|
|
4839
|
+
</div>
|
|
4840
|
+
`,
|
|
4841
|
+
}]
|
|
4842
|
+
}] });
|
|
4843
|
+
|
|
4844
|
+
/** Content section shown only while its value is the active tab. */
|
|
4845
|
+
class TabPanelComponent {
|
|
4846
|
+
constructor() {
|
|
4847
|
+
/** Value that links this panel to its tab. */
|
|
4848
|
+
this.value = input(undefined, ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
|
|
4849
|
+
this.tabsSvc = inject(TabsService);
|
|
4850
|
+
this.active = computed(() => this.tabsSvc.active() === this.value(), ...(ngDevMode ? [{ debugName: "active" }] : /* istanbul ignore next */ []));
|
|
4851
|
+
}
|
|
4852
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: TabPanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
4853
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.18", type: TabPanelComponent, isStandalone: true, selector: "shk-tabpanel", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
|
|
4854
|
+
@if (active()) {
|
|
4855
|
+
<div class="shk-tabpanel" role="tabpanel">
|
|
4856
|
+
<ng-content></ng-content>
|
|
4857
|
+
</div>
|
|
4858
|
+
}
|
|
4859
|
+
`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
4860
|
+
}
|
|
4861
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: TabPanelComponent, decorators: [{
|
|
4862
|
+
type: Component,
|
|
4863
|
+
args: [{
|
|
4864
|
+
selector: 'shk-tabpanel',
|
|
4865
|
+
standalone: true,
|
|
4866
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
4867
|
+
template: `
|
|
4868
|
+
@if (active()) {
|
|
4869
|
+
<div class="shk-tabpanel" role="tabpanel">
|
|
4870
|
+
<ng-content></ng-content>
|
|
4871
|
+
</div>
|
|
4872
|
+
}
|
|
4873
|
+
`,
|
|
4874
|
+
}]
|
|
4875
|
+
}], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }] } });
|
|
4876
|
+
|
|
4877
|
+
class BreadcrumbService {
|
|
4878
|
+
constructor() {
|
|
4879
|
+
this.breadcrumbsSubject = new BehaviorSubject([]);
|
|
4880
|
+
this.breadcrumbs$ = this.breadcrumbsSubject.asObservable();
|
|
4881
|
+
}
|
|
4882
|
+
set(items) {
|
|
4883
|
+
this.breadcrumbsSubject.next(items);
|
|
4884
|
+
}
|
|
4885
|
+
clear() {
|
|
4886
|
+
this.breadcrumbsSubject.next([]);
|
|
4887
|
+
}
|
|
4888
|
+
getCurrent() {
|
|
4889
|
+
return this.breadcrumbsSubject.getValue();
|
|
4890
|
+
}
|
|
4891
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: BreadcrumbService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
4892
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: BreadcrumbService, providedIn: 'root' }); }
|
|
4893
|
+
}
|
|
4894
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: BreadcrumbService, decorators: [{
|
|
4895
|
+
type: Injectable,
|
|
4896
|
+
args: [{
|
|
4897
|
+
providedIn: 'root',
|
|
4898
|
+
}]
|
|
4899
|
+
}] });
|
|
4900
|
+
|
|
4901
|
+
class BreadcrumbComponent {
|
|
4902
|
+
constructor() {
|
|
4903
|
+
this.breadcrumbService = inject(BreadcrumbService);
|
|
4904
|
+
this.items = input([], ...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
|
|
4905
|
+
this.separator = input('chevron', ...(ngDevMode ? [{ debugName: "separator" }] : /* istanbul ignore next */ []));
|
|
4906
|
+
this.useService = input(true, ...(ngDevMode ? [{ debugName: "useService" }] : /* istanbul ignore next */ []));
|
|
4907
|
+
this.serviceItems = signal([], ...(ngDevMode ? [{ debugName: "serviceItems" }] : /* istanbul ignore next */ []));
|
|
4908
|
+
this.resolvedItems = computed(() => {
|
|
4909
|
+
const staticItems = this.items();
|
|
4910
|
+
if (staticItems.length > 0)
|
|
4911
|
+
return staticItems;
|
|
4912
|
+
if (this.useService())
|
|
4913
|
+
return this.serviceItems();
|
|
4914
|
+
return [];
|
|
4915
|
+
}, ...(ngDevMode ? [{ debugName: "resolvedItems" }] : /* istanbul ignore next */ []));
|
|
4916
|
+
this.serviceSub = null;
|
|
4917
|
+
effect(() => {
|
|
4918
|
+
this.resolvedItems();
|
|
4919
|
+
});
|
|
4920
|
+
}
|
|
4921
|
+
ngOnInit() {
|
|
4922
|
+
if (this.useService()) {
|
|
4923
|
+
this.serviceSub = this.breadcrumbService.breadcrumbs$.subscribe((items) => {
|
|
4924
|
+
this.serviceItems.set(items);
|
|
4925
|
+
});
|
|
4926
|
+
}
|
|
4927
|
+
}
|
|
4928
|
+
ngOnDestroy() {
|
|
4929
|
+
this.serviceSub?.unsubscribe();
|
|
4930
|
+
}
|
|
4931
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: BreadcrumbComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
4932
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.18", type: BreadcrumbComponent, isStandalone: true, selector: "shk-breadcrumb", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, separator: { classPropertyName: "separator", publicName: "separator", isSignal: true, isRequired: false, transformFunction: null }, useService: { classPropertyName: "useService", publicName: "useService", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
|
|
4933
|
+
<nav class="shk-breadcrumb" aria-label="Breadcrumb">
|
|
4934
|
+
<ol class="shk-breadcrumb-list">
|
|
4935
|
+
@for (item of resolvedItems(); track $index; let last = $last) {
|
|
4936
|
+
<li class="shk-breadcrumb-item">
|
|
4937
|
+
@if (!last && item.route) {
|
|
4938
|
+
<a
|
|
4939
|
+
[routerLink]="item.route"
|
|
4940
|
+
class="shk-breadcrumb-link"
|
|
4941
|
+
[attr.aria-current]="null"
|
|
4942
|
+
>
|
|
4943
|
+
{{ item.label }}
|
|
4944
|
+
</a>
|
|
4945
|
+
} @else {
|
|
4946
|
+
<span
|
|
4947
|
+
class="shk-breadcrumb-current"
|
|
4948
|
+
[attr.aria-current]="last ? 'page' : null"
|
|
4949
|
+
>
|
|
4950
|
+
{{ item.label }}
|
|
4951
|
+
</span>
|
|
4952
|
+
}
|
|
4953
|
+
@if (!last) {
|
|
4954
|
+
<span class="shk-breadcrumb-separator" aria-hidden="true">
|
|
4955
|
+
@switch (separator()) {
|
|
4956
|
+
@case ('slash') {
|
|
4957
|
+
<span class="shk-breadcrumb-separator-text">/</span>
|
|
4958
|
+
}
|
|
4959
|
+
@case ('arrow') {
|
|
4960
|
+
<span class="shk-breadcrumb-separator-text">></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
|
+
|
|
3832
5039
|
/* Theme — import theme.css in your global styles.css:
|
|
3833
5040
|
@import '@shirkasoft/ui-components/theme.css';
|
|
3834
5041
|
Override --shk-* CSS variables at :root or .dark to theme.
|
|
@@ -3838,5 +5045,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImpo
|
|
|
3838
5045
|
* Generated bundle index. Do not edit.
|
|
3839
5046
|
*/
|
|
3840
5047
|
|
|
3841
|
-
export { COLOR_PALETTE, ChartComponent, ConfirmDialogComponent, ConfirmDialogService, DEFAULT_MARKER_GRADIENTS, DEFAULT_SUPPLIER_ICON_COLORS, DEFAULT_SUPPLIER_TYPE_ICONS, DatePickerComponent, FieldsetComponent, FileUploadComponent, ModalComponent, ModalService, NotificationComponent, NotificationService, PriceInputComponent, RailSidebarComponent, SelectComponent, ShkWizardStepContent, SidebarComponent, TableComponent, TextFieldComponent, TimelineComponent, ToggleComponent, TooltipComponent, WizardComponent, readThemeColors };
|
|
5048
|
+
export { BreadcrumbComponent, BreadcrumbService, ButtonComponent, COLOR_PALETTE, ChartComponent, ConfirmDialogComponent, ConfirmDialogService, DEFAULT_MARKER_GRADIENTS, DEFAULT_SUPPLIER_ICON_COLORS, DEFAULT_SUPPLIER_TYPE_ICONS, DatePickerComponent, FieldsetComponent, FileUploadComponent, KnobComponent, LoadingBarComponent, LoadingDotsComponent, LoadingOverlayComponent, LoadingSpinnerComponent, ModalComponent, ModalService, NotificationComponent, NotificationService, PriceInputComponent, RailSidebarComponent, SelectComponent, ShkWizardStepContent, SidebarComponent, SkeletonComponent, TabComponent, TabPanelComponent, TableComponent, TablistComponent, TabpanelsComponent, TabsComponent, TagComponent, TextFieldComponent, TimelineComponent, ToggleComponent, TooltipComponent, WizardComponent, readThemeColors };
|
|
3842
5049
|
//# sourceMappingURL=shirkasoft-ui-components.mjs.map
|