@maro-tech/card 0.1.2 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import * as i1 from '@angular/common';
|
|
1
|
+
import * as i1$1 from '@angular/common';
|
|
2
2
|
import { CommonModule } from '@angular/common';
|
|
3
3
|
import * as i0 from '@angular/core';
|
|
4
|
-
import { input, inject, DestroyRef, signal, Component, computed, output, effect, HostListener, Input, InjectionToken, forwardRef, Injectable, ViewChild, untracked } from '@angular/core';
|
|
4
|
+
import { input, inject, DestroyRef, signal, Component, computed, viewChildren, output, effect, HostListener, Input, InjectionToken, forwardRef, Injectable, ViewChild, untracked } from '@angular/core';
|
|
5
5
|
import { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop';
|
|
6
6
|
import { EventBus, UiButtonComponent, UiToastService, UiModalComponent, UiConfirmService } from '@maro-tech/core';
|
|
7
7
|
import { DomSanitizer } from '@angular/platform-browser';
|
|
8
|
+
import * as i1 from 'lucide-angular';
|
|
9
|
+
import { Download, LucideAngularModule, Eye, FileText, Plus, Sparkles, Trash } from 'lucide-angular';
|
|
8
10
|
import { HttpClient, HttpParams, HttpHeaders } from '@angular/common/http';
|
|
9
|
-
import { map,
|
|
11
|
+
import { map, shareReplay, forkJoin, switchMap, timer, of } from 'rxjs';
|
|
10
12
|
import { UiTableComponent } from '@maro-tech/table';
|
|
11
13
|
import { ActivatedRoute, Router, RouterLink } from '@angular/router';
|
|
12
14
|
import { map as map$1, exhaustMap, catchError, takeWhile, takeUntil, finalize } from 'rxjs/operators';
|
|
13
|
-
import * as i1$2 from '
|
|
14
|
-
import { LucideAngularModule, Eye, FileText, Plus, Sparkles, Trash } from 'lucide-angular';
|
|
15
|
-
import * as i1$1 from '@angular/forms';
|
|
15
|
+
import * as i1$2 from '@angular/forms';
|
|
16
16
|
import { FormsModule } from '@angular/forms';
|
|
17
17
|
import { InputSelectComponent } from '@maro-tech/form';
|
|
18
18
|
|
|
@@ -273,20 +273,36 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImpo
|
|
|
273
273
|
}], propDecorators: { html: [{ type: i0.Input, args: [{ isSignal: true, alias: "html", required: false }] }] } });
|
|
274
274
|
|
|
275
275
|
class ImagesViewerComponent {
|
|
276
|
+
downloadIcon = Download;
|
|
276
277
|
urls = input([], ...(ngDevMode ? [{ debugName: "urls" }] : /* istanbul ignore next */ []));
|
|
277
278
|
initialIndex = input(0, ...(ngDevMode ? [{ debugName: "initialIndex" }] : /* istanbul ignore next */ []));
|
|
278
279
|
currentIndex = signal(0, ...(ngDevMode ? [{ debugName: "currentIndex" }] : /* istanbul ignore next */ []));
|
|
280
|
+
isOriginalVisible = signal(false, ...(ngDevMode ? [{ debugName: "isOriginalVisible" }] : /* istanbul ignore next */ []));
|
|
281
|
+
thumbnailButtons = viewChildren('thumbnailButton', ...(ngDevMode ? [{ debugName: "thumbnailButtons" }] : /* istanbul ignore next */ []));
|
|
279
282
|
closed = output();
|
|
283
|
+
touchStartX = 0;
|
|
284
|
+
touchStartY = 0;
|
|
285
|
+
lastImagesKey = '';
|
|
280
286
|
constructor() {
|
|
281
287
|
effect(() => {
|
|
282
288
|
const urls = this.urls();
|
|
289
|
+
const imagesKey = `${this.initialIndex()}|${urls.join('|')}`;
|
|
290
|
+
if (imagesKey === this.lastImagesKey)
|
|
291
|
+
return;
|
|
292
|
+
this.lastImagesKey = imagesKey;
|
|
283
293
|
if (!urls.length) {
|
|
284
294
|
this.currentIndex.set(0);
|
|
295
|
+
this.isOriginalVisible.set(false);
|
|
285
296
|
return;
|
|
286
297
|
}
|
|
287
298
|
const maxIndex = urls.length - 1;
|
|
288
299
|
const startIndex = Math.max(0, Math.min(this.initialIndex(), maxIndex));
|
|
289
300
|
this.currentIndex.set(startIndex);
|
|
301
|
+
this.isOriginalVisible.set(false);
|
|
302
|
+
});
|
|
303
|
+
effect(() => {
|
|
304
|
+
const thumbnail = this.thumbnailButtons()[this.currentIndex()];
|
|
305
|
+
thumbnail?.nativeElement.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'center' });
|
|
290
306
|
});
|
|
291
307
|
}
|
|
292
308
|
onEscape() {
|
|
@@ -301,17 +317,69 @@ class ImagesViewerComponent {
|
|
|
301
317
|
closeViewer() {
|
|
302
318
|
this.closed.emit();
|
|
303
319
|
}
|
|
320
|
+
onTouchStart(event) {
|
|
321
|
+
const touch = event.changedTouches[0];
|
|
322
|
+
if (!touch)
|
|
323
|
+
return;
|
|
324
|
+
this.touchStartX = touch.clientX;
|
|
325
|
+
this.touchStartY = touch.clientY;
|
|
326
|
+
}
|
|
327
|
+
onTouchEnd(event) {
|
|
328
|
+
const touch = event.changedTouches[0];
|
|
329
|
+
if (!touch)
|
|
330
|
+
return;
|
|
331
|
+
const deltaX = touch.clientX - this.touchStartX;
|
|
332
|
+
const deltaY = touch.clientY - this.touchStartY;
|
|
333
|
+
if (Math.abs(deltaX) < 50 || Math.abs(deltaX) <= Math.abs(deltaY))
|
|
334
|
+
return;
|
|
335
|
+
if (deltaX < 0) {
|
|
336
|
+
this.showNextImage();
|
|
337
|
+
}
|
|
338
|
+
else {
|
|
339
|
+
this.showPreviousImage();
|
|
340
|
+
}
|
|
341
|
+
}
|
|
304
342
|
showPreviousImage() {
|
|
305
343
|
const total = this.urls().length;
|
|
306
344
|
if (total <= 1)
|
|
307
345
|
return;
|
|
308
346
|
this.currentIndex.set((this.currentIndex() - 1 + total) % total);
|
|
347
|
+
this.blurActiveElement();
|
|
309
348
|
}
|
|
310
349
|
showNextImage() {
|
|
311
350
|
const total = this.urls().length;
|
|
312
351
|
if (total <= 1)
|
|
313
352
|
return;
|
|
314
353
|
this.currentIndex.set((this.currentIndex() + 1) % total);
|
|
354
|
+
this.blurActiveElement();
|
|
355
|
+
}
|
|
356
|
+
toggleOriginal() {
|
|
357
|
+
this.isOriginalVisible.update((visible) => !visible);
|
|
358
|
+
}
|
|
359
|
+
selectImage(index, event) {
|
|
360
|
+
this.currentIndex.set(index);
|
|
361
|
+
event.currentTarget?.blur();
|
|
362
|
+
}
|
|
363
|
+
blurActiveElement() {
|
|
364
|
+
if (typeof document === 'undefined')
|
|
365
|
+
return;
|
|
366
|
+
document.activeElement?.blur();
|
|
367
|
+
}
|
|
368
|
+
get currentUrl() {
|
|
369
|
+
return this.urls()[this.currentIndex()] || '';
|
|
370
|
+
}
|
|
371
|
+
get currentImageUrl() {
|
|
372
|
+
return this.isOriginalVisible() ? this.currentUrl : this.getThumbnailUrl(this.currentUrl);
|
|
373
|
+
}
|
|
374
|
+
isPreloadIndex(index) {
|
|
375
|
+
const total = this.urls().length;
|
|
376
|
+
if (total <= 2)
|
|
377
|
+
return true;
|
|
378
|
+
const distance = Math.abs(index - this.currentIndex());
|
|
379
|
+
return distance <= 1 || distance >= total - 1;
|
|
380
|
+
}
|
|
381
|
+
getImageUrl(url, index) {
|
|
382
|
+
return index === this.currentIndex() || this.isOriginalVisible() ? url : this.getThumbnailUrl(url);
|
|
315
383
|
}
|
|
316
384
|
get imageViewerTransform() {
|
|
317
385
|
return `translate3d(-${this.currentIndex() * 100}%, 0, 0)`;
|
|
@@ -324,12 +392,12 @@ class ImagesViewerComponent {
|
|
|
324
392
|
return match ? `${match[1]}/preview?scale=25` : url;
|
|
325
393
|
}
|
|
326
394
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: ImagesViewerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
327
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.20", type: ImagesViewerComponent, isStandalone: true, selector: "app-images-viewer", inputs: { urls: { classPropertyName: "urls", publicName: "urls", isSignal: true, isRequired: false, transformFunction: null }, initialIndex: { classPropertyName: "initialIndex", publicName: "initialIndex", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { closed: "closed" }, host: { listeners: { "document:keydown.escape": "onEscape()", "document:keydown.arrowleft": "onArrowLeft()", "document:keydown.arrowright": "onArrowRight()" } }, ngImport: i0, template: "<div class=\"fixed inset-0 z-[2300] flex items-center justify-center bg-black/80 p-3 md:p-4\" role=\"dialog\" aria-modal=\"true\" aria-label=\"\u041F\u0435\u0440\u0435\u0433\u043B\u044F\u0434 \u0444\u043E\u0442\u043E\">\n <button type=\"button\" class=\"absolute inset-0\" aria-label=\"\u0417\u0430\u043A\u0440\u0438\u0442\u0438 \u043F\u0435\u0440\u0435\u0433\u043B\u044F\u0434\" (click)=\"closeViewer()\"></button>\n <div class=\"relative z-[1] flex h-full w-full max-w-[1500px] flex-col items-center justify-center gap-4\" (click)=\"$event.stopPropagation()\">\n <button\n type=\"button\"\n class=\"absolute right-2 top-2 z-20 inline-flex h-11 w-11 items-center justify-center rounded-full bg-white/90 text-slate-900 shadow-[0_12px_28px_rgba(15,23,42,0.22)] transition hover:bg-white md:right-3 md:top-3\"\n aria-label=\"\u0417\u0430\u043A\u0440\u0438\u0442\u0438\"\n (click)=\"closeViewer()\">\n \u2715\n </button>\n\n <div
|
|
395
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.20", type: ImagesViewerComponent, isStandalone: true, selector: "app-images-viewer", inputs: { urls: { classPropertyName: "urls", publicName: "urls", isSignal: true, isRequired: false, transformFunction: null }, initialIndex: { classPropertyName: "initialIndex", publicName: "initialIndex", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { closed: "closed" }, host: { listeners: { "document:keydown.escape": "onEscape()", "document:keydown.arrowleft": "onArrowLeft()", "document:keydown.arrowright": "onArrowRight()" } }, viewQueries: [{ propertyName: "thumbnailButtons", predicate: ["thumbnailButton"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"fixed inset-0 z-[2300] flex items-center justify-center bg-black/80 p-3 md:p-4\" role=\"dialog\" aria-modal=\"true\" aria-label=\"\u041F\u0435\u0440\u0435\u0433\u043B\u044F\u0434 \u0444\u043E\u0442\u043E\">\n <button type=\"button\" class=\"absolute inset-0\" aria-label=\"\u0417\u0430\u043A\u0440\u0438\u0442\u0438 \u043F\u0435\u0440\u0435\u0433\u043B\u044F\u0434\" (click)=\"closeViewer()\"></button>\n <div class=\"relative z-[1] flex h-full w-full max-w-[1500px] flex-col items-center justify-center gap-4\" (click)=\"$event.stopPropagation()\">\n <button\n type=\"button\"\n class=\"absolute right-2 top-2 z-20 inline-flex h-11 w-11 items-center justify-center rounded-full bg-white/90 text-slate-900 shadow-[0_12px_28px_rgba(15,23,42,0.22)] transition hover:bg-white md:right-3 md:top-3\"\n aria-label=\"\u0417\u0430\u043A\u0440\u0438\u0442\u0438\"\n (click)=\"closeViewer()\">\n \u2715\n </button>\n\n <div\n class=\"relative flex min-h-0 w-full flex-1 items-center justify-center overflow-hidden rounded-[18px] touch-pan-y\"\n (touchstart)=\"onTouchStart($event)\"\n (touchend)=\"onTouchEnd($event)\">\n <div class=\"h-full w-full overflow-hidden rounded-[18px]\">\n <div\n class=\"flex h-full w-full\"\n style=\"will-change: transform; backface-visibility: hidden;\"\n [style.transition]=\"imageViewerTransitionStyle\"\n [style.transform]=\"imageViewerTransform\">\n @for (url of urls(); track $index; let idx = $index) {\n <div class=\"flex h-full w-full min-w-full shrink-0 items-center justify-center overflow-hidden\">\n @if (isPreloadIndex(idx)) {\n <img\n [src]=\"getImageUrl(url, idx)\"\n [attr.alt]=\"'\u0424\u043E\u0442\u043E ' + (idx + 1)\"\n [attr.loading]=\"idx === currentIndex() ? 'eager' : 'lazy'\"\n decoding=\"async\"\n class=\"block max-h-[74vh] max-w-[96vw] object-contain md:max-h-[78vh]\">\n }\n </div>\n }\n </div>\n </div>\n\n @if (urls().length > 1) {\n <button type=\"button\" class=\"absolute left-0 top-0 z-10 flex h-full w-1/4 min-w-12 items-center justify-start pl-2 md:pl-3\" aria-label=\"\u041F\u043E\u043F\u0435\u0440\u0435\u0434\u043D\u0454 \u0444\u043E\u0442\u043E\" (click)=\"showPreviousImage()\">\n <span class=\"inline-flex h-11 w-11 items-center justify-center rounded-full bg-white/90 text-slate-900 shadow-[0_12px_28px_rgba(15,23,42,0.22)] transition hover:bg-white\">\u2039</span>\n </button>\n <button type=\"button\" class=\"absolute right-0 top-0 z-10 flex h-full w-1/4 min-w-12 items-center justify-end pr-2 md:pr-3\" aria-label=\"\u041D\u0430\u0441\u0442\u0443\u043F\u043D\u0435 \u0444\u043E\u0442\u043E\" (click)=\"showNextImage()\">\n <span class=\"inline-flex h-11 w-11 items-center justify-center rounded-full bg-white/90 text-slate-900 shadow-[0_12px_28px_rgba(15,23,42,0.22)] transition hover:bg-white\">\u203A</span>\n </button>\n }\n\n @if (currentUrl) {\n <div class=\"absolute bottom-3 right-3 z-20 inline-flex max-w-[calc(100%-1.5rem)] items-center gap-1.5 rounded-2xl bg-black/55 p-1.5 text-white shadow-[0_12px_28px_rgba(15,23,42,0.24)] backdrop-blur-md md:bottom-4 md:right-4\">\n <button\n type=\"button\"\n class=\"inline-flex h-9 items-center gap-2 rounded-xl px-2.5 text-xs font-semibold transition hover:bg-white/10\"\n [class.bg-emerald-500\\/20]=\"isOriginalVisible()\"\n [class.text-emerald-100]=\"isOriginalVisible()\"\n [attr.aria-pressed]=\"isOriginalVisible()\"\n aria-label=\"\u041F\u0435\u0440\u0435\u043C\u043A\u043D\u0443\u0442\u0438 \u043C\u0456\u0436 \u043F\u0440\u0435\u0432\u2019\u044E \u0442\u0430 \u043E\u0440\u0438\u0433\u0456\u043D\u0430\u043B\u043E\u043C\"\n (click)=\"toggleOriginal()\">\n <span>\u041E\u0440\u0438\u0433\u0456\u043D\u0430\u043B</span>\n <span\n class=\"relative inline-flex h-5 w-9 items-center rounded-full border p-0.5 transition-colors\"\n [class.bg-emerald-500]=\"isOriginalVisible()\"\n [class.bg-white\\/15]=\"!isOriginalVisible()\"\n [class.border-emerald-200]=\"isOriginalVisible()\"\n [class.border-white\\/45]=\"!isOriginalVisible()\"\n [class.ring-2]=\"isOriginalVisible()\"\n [class.ring-emerald-300\\/70]=\"isOriginalVisible()\">\n <span\n class=\"h-4 w-4 rounded-full bg-white shadow-sm transition-transform duration-200\"\n [class.translate-x-4]=\"isOriginalVisible()\">\n </span>\n </span>\n </button>\n <a\n class=\"inline-flex h-9 w-9 items-center justify-center rounded-xl transition hover:bg-white/10\"\n [href]=\"currentUrl\"\n download\n aria-label=\"\u0421\u043A\u0430\u0447\u0430\u0442\u0438 \u043E\u0440\u0438\u0433\u0456\u043D\u0430\u043B \u0444\u043E\u0442\u043E\">\n <lucide-icon [img]=\"downloadIcon\" [size]=\"19\" [strokeWidth]=\"2.25\"></lucide-icon>\n </a>\n </div>\n }\n </div>\n\n @if (urls().length > 1) {\n <div class=\"flex w-full flex-col items-center gap-3 pb-1 md:pb-2\">\n <div class=\"flex w-full max-w-[92vw] flex-nowrap justify-start gap-2 overflow-x-auto overflow-y-hidden overscroll-x-contain px-2 py-3 md:max-w-[80vw]\">\n @for (url of urls(); track url; let idx = $index) {\n <button\n #thumbnailButton\n type=\"button\"\n class=\"h-14 w-14 shrink-0 overflow-hidden rounded-xl border-2 border-transparent bg-white p-1 transition-transform duration-200\"\n [class.border-slate-900]=\"idx === currentIndex()\"\n [class.mx-2]=\"idx === currentIndex()\"\n [style.transform]=\"idx === currentIndex() ? 'scale(1.4)' : 'scale(1)'\"\n [class.origin-center]=\"idx === currentIndex()\"\n [class.relative]=\"idx === currentIndex()\"\n [class.z-10]=\"idx === currentIndex()\"\n [attr.aria-current]=\"idx === currentIndex() ? 'true' : null\"\n [attr.aria-label]=\"'\u0424\u043E\u0442\u043E ' + (idx + 1)\"\n (click)=\"selectImage(idx, $event)\">\n <img class=\"block aspect-square w-full rounded-lg object-contain\" [src]=\"getThumbnailUrl(url)\" [attr.alt]=\"'\u041C\u0456\u043D\u0456\u0430\u0442\u044E\u0440\u0430 ' + (idx + 1)\" loading=\"lazy\" decoding=\"async\">\n </button>\n }\n </div>\n <div class=\"rounded-full bg-white/95 px-3 py-1.5 text-xs font-semibold text-slate-900 shadow-[0_12px_28px_rgba(15,23,42,0.2)]\">\n {{ currentIndex() + 1 }} / {{ urls().length }}\n </div>\n </div>\n }\n </div>\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: LucideAngularModule }, { kind: "component", type: i1.LucideAngularComponent, selector: "lucide-angular, lucide-icon, i-lucide, span-lucide", inputs: ["class", "name", "img", "color", "absoluteStrokeWidth", "size", "strokeWidth"] }] });
|
|
328
396
|
}
|
|
329
397
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: ImagesViewerComponent, decorators: [{
|
|
330
398
|
type: Component,
|
|
331
|
-
args: [{ selector: 'app-images-viewer', standalone: true, imports: [CommonModule], template: "<div class=\"fixed inset-0 z-[2300] flex items-center justify-center bg-black/80 p-3 md:p-4\" role=\"dialog\" aria-modal=\"true\" aria-label=\"\u041F\u0435\u0440\u0435\u0433\u043B\u044F\u0434 \u0444\u043E\u0442\u043E\">\n <button type=\"button\" class=\"absolute inset-0\" aria-label=\"\u0417\u0430\u043A\u0440\u0438\u0442\u0438 \u043F\u0435\u0440\u0435\u0433\u043B\u044F\u0434\" (click)=\"closeViewer()\"></button>\n <div class=\"relative z-[1] flex h-full w-full max-w-[1500px] flex-col items-center justify-center gap-4\" (click)=\"$event.stopPropagation()\">\n <button\n type=\"button\"\n class=\"absolute right-2 top-2 z-20 inline-flex h-11 w-11 items-center justify-center rounded-full bg-white/90 text-slate-900 shadow-[0_12px_28px_rgba(15,23,42,0.22)] transition hover:bg-white md:right-3 md:top-3\"\n aria-label=\"\u0417\u0430\u043A\u0440\u0438\u0442\u0438\"\n (click)=\"closeViewer()\">\n \u2715\n </button>\n\n <div
|
|
332
|
-
}], ctorParameters: () => [], propDecorators: { urls: [{ type: i0.Input, args: [{ isSignal: true, alias: "urls", required: false }] }], initialIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "initialIndex", required: false }] }], closed: [{ type: i0.Output, args: ["closed"] }], onEscape: [{
|
|
399
|
+
args: [{ selector: 'app-images-viewer', standalone: true, imports: [CommonModule, LucideAngularModule], template: "<div class=\"fixed inset-0 z-[2300] flex items-center justify-center bg-black/80 p-3 md:p-4\" role=\"dialog\" aria-modal=\"true\" aria-label=\"\u041F\u0435\u0440\u0435\u0433\u043B\u044F\u0434 \u0444\u043E\u0442\u043E\">\n <button type=\"button\" class=\"absolute inset-0\" aria-label=\"\u0417\u0430\u043A\u0440\u0438\u0442\u0438 \u043F\u0435\u0440\u0435\u0433\u043B\u044F\u0434\" (click)=\"closeViewer()\"></button>\n <div class=\"relative z-[1] flex h-full w-full max-w-[1500px] flex-col items-center justify-center gap-4\" (click)=\"$event.stopPropagation()\">\n <button\n type=\"button\"\n class=\"absolute right-2 top-2 z-20 inline-flex h-11 w-11 items-center justify-center rounded-full bg-white/90 text-slate-900 shadow-[0_12px_28px_rgba(15,23,42,0.22)] transition hover:bg-white md:right-3 md:top-3\"\n aria-label=\"\u0417\u0430\u043A\u0440\u0438\u0442\u0438\"\n (click)=\"closeViewer()\">\n \u2715\n </button>\n\n <div\n class=\"relative flex min-h-0 w-full flex-1 items-center justify-center overflow-hidden rounded-[18px] touch-pan-y\"\n (touchstart)=\"onTouchStart($event)\"\n (touchend)=\"onTouchEnd($event)\">\n <div class=\"h-full w-full overflow-hidden rounded-[18px]\">\n <div\n class=\"flex h-full w-full\"\n style=\"will-change: transform; backface-visibility: hidden;\"\n [style.transition]=\"imageViewerTransitionStyle\"\n [style.transform]=\"imageViewerTransform\">\n @for (url of urls(); track $index; let idx = $index) {\n <div class=\"flex h-full w-full min-w-full shrink-0 items-center justify-center overflow-hidden\">\n @if (isPreloadIndex(idx)) {\n <img\n [src]=\"getImageUrl(url, idx)\"\n [attr.alt]=\"'\u0424\u043E\u0442\u043E ' + (idx + 1)\"\n [attr.loading]=\"idx === currentIndex() ? 'eager' : 'lazy'\"\n decoding=\"async\"\n class=\"block max-h-[74vh] max-w-[96vw] object-contain md:max-h-[78vh]\">\n }\n </div>\n }\n </div>\n </div>\n\n @if (urls().length > 1) {\n <button type=\"button\" class=\"absolute left-0 top-0 z-10 flex h-full w-1/4 min-w-12 items-center justify-start pl-2 md:pl-3\" aria-label=\"\u041F\u043E\u043F\u0435\u0440\u0435\u0434\u043D\u0454 \u0444\u043E\u0442\u043E\" (click)=\"showPreviousImage()\">\n <span class=\"inline-flex h-11 w-11 items-center justify-center rounded-full bg-white/90 text-slate-900 shadow-[0_12px_28px_rgba(15,23,42,0.22)] transition hover:bg-white\">\u2039</span>\n </button>\n <button type=\"button\" class=\"absolute right-0 top-0 z-10 flex h-full w-1/4 min-w-12 items-center justify-end pr-2 md:pr-3\" aria-label=\"\u041D\u0430\u0441\u0442\u0443\u043F\u043D\u0435 \u0444\u043E\u0442\u043E\" (click)=\"showNextImage()\">\n <span class=\"inline-flex h-11 w-11 items-center justify-center rounded-full bg-white/90 text-slate-900 shadow-[0_12px_28px_rgba(15,23,42,0.22)] transition hover:bg-white\">\u203A</span>\n </button>\n }\n\n @if (currentUrl) {\n <div class=\"absolute bottom-3 right-3 z-20 inline-flex max-w-[calc(100%-1.5rem)] items-center gap-1.5 rounded-2xl bg-black/55 p-1.5 text-white shadow-[0_12px_28px_rgba(15,23,42,0.24)] backdrop-blur-md md:bottom-4 md:right-4\">\n <button\n type=\"button\"\n class=\"inline-flex h-9 items-center gap-2 rounded-xl px-2.5 text-xs font-semibold transition hover:bg-white/10\"\n [class.bg-emerald-500\\/20]=\"isOriginalVisible()\"\n [class.text-emerald-100]=\"isOriginalVisible()\"\n [attr.aria-pressed]=\"isOriginalVisible()\"\n aria-label=\"\u041F\u0435\u0440\u0435\u043C\u043A\u043D\u0443\u0442\u0438 \u043C\u0456\u0436 \u043F\u0440\u0435\u0432\u2019\u044E \u0442\u0430 \u043E\u0440\u0438\u0433\u0456\u043D\u0430\u043B\u043E\u043C\"\n (click)=\"toggleOriginal()\">\n <span>\u041E\u0440\u0438\u0433\u0456\u043D\u0430\u043B</span>\n <span\n class=\"relative inline-flex h-5 w-9 items-center rounded-full border p-0.5 transition-colors\"\n [class.bg-emerald-500]=\"isOriginalVisible()\"\n [class.bg-white\\/15]=\"!isOriginalVisible()\"\n [class.border-emerald-200]=\"isOriginalVisible()\"\n [class.border-white\\/45]=\"!isOriginalVisible()\"\n [class.ring-2]=\"isOriginalVisible()\"\n [class.ring-emerald-300\\/70]=\"isOriginalVisible()\">\n <span\n class=\"h-4 w-4 rounded-full bg-white shadow-sm transition-transform duration-200\"\n [class.translate-x-4]=\"isOriginalVisible()\">\n </span>\n </span>\n </button>\n <a\n class=\"inline-flex h-9 w-9 items-center justify-center rounded-xl transition hover:bg-white/10\"\n [href]=\"currentUrl\"\n download\n aria-label=\"\u0421\u043A\u0430\u0447\u0430\u0442\u0438 \u043E\u0440\u0438\u0433\u0456\u043D\u0430\u043B \u0444\u043E\u0442\u043E\">\n <lucide-icon [img]=\"downloadIcon\" [size]=\"19\" [strokeWidth]=\"2.25\"></lucide-icon>\n </a>\n </div>\n }\n </div>\n\n @if (urls().length > 1) {\n <div class=\"flex w-full flex-col items-center gap-3 pb-1 md:pb-2\">\n <div class=\"flex w-full max-w-[92vw] flex-nowrap justify-start gap-2 overflow-x-auto overflow-y-hidden overscroll-x-contain px-2 py-3 md:max-w-[80vw]\">\n @for (url of urls(); track url; let idx = $index) {\n <button\n #thumbnailButton\n type=\"button\"\n class=\"h-14 w-14 shrink-0 overflow-hidden rounded-xl border-2 border-transparent bg-white p-1 transition-transform duration-200\"\n [class.border-slate-900]=\"idx === currentIndex()\"\n [class.mx-2]=\"idx === currentIndex()\"\n [style.transform]=\"idx === currentIndex() ? 'scale(1.4)' : 'scale(1)'\"\n [class.origin-center]=\"idx === currentIndex()\"\n [class.relative]=\"idx === currentIndex()\"\n [class.z-10]=\"idx === currentIndex()\"\n [attr.aria-current]=\"idx === currentIndex() ? 'true' : null\"\n [attr.aria-label]=\"'\u0424\u043E\u0442\u043E ' + (idx + 1)\"\n (click)=\"selectImage(idx, $event)\">\n <img class=\"block aspect-square w-full rounded-lg object-contain\" [src]=\"getThumbnailUrl(url)\" [attr.alt]=\"'\u041C\u0456\u043D\u0456\u0430\u0442\u044E\u0440\u0430 ' + (idx + 1)\" loading=\"lazy\" decoding=\"async\">\n </button>\n }\n </div>\n <div class=\"rounded-full bg-white/95 px-3 py-1.5 text-xs font-semibold text-slate-900 shadow-[0_12px_28px_rgba(15,23,42,0.2)]\">\n {{ currentIndex() + 1 }} / {{ urls().length }}\n </div>\n </div>\n }\n </div>\n</div>\n" }]
|
|
400
|
+
}], ctorParameters: () => [], propDecorators: { urls: [{ type: i0.Input, args: [{ isSignal: true, alias: "urls", required: false }] }], initialIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "initialIndex", required: false }] }], thumbnailButtons: [{ type: i0.ViewChildren, args: ['thumbnailButton', { isSignal: true }] }], closed: [{ type: i0.Output, args: ["closed"] }], onEscape: [{
|
|
333
401
|
type: HostListener,
|
|
334
402
|
args: ['document:keydown.escape']
|
|
335
403
|
}], onArrowLeft: [{
|
|
@@ -453,7 +521,7 @@ class DetailsGridComponent {
|
|
|
453
521
|
: { day: '2-digit', month: '2-digit', year: 'numeric' }).format(date);
|
|
454
522
|
}
|
|
455
523
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: DetailsGridComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
456
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.20", type: DetailsGridComponent, isStandalone: true, selector: "app-details-grid", inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"grid gap-4\" [class.grid-cols-1]=\"!hasExplicitColumns()\" [class.sm:grid-cols-3]=\"!hasExplicitColumns()\" [class.sm:grid-cols-12]=\"hasExplicitColumns()\">\n @for (field of displayFields(); track $index) {\n <div class=\"rounded-2xl border border-slate-100 bg-slate-200 p-4\" [ngClass]=\"getFieldColumnClass(field)\" [class.flex]=\"field.type === 'image'\" [class.flex-col]=\"field.type === 'image'\" [class.min-h-64]=\"field.type === 'image'\">\n <p class=\"text-xs uppercase tracking-[0.18em] text-slate-500\">{{ field.label }}</p>\n @if (field.type === 'image') {\n @if (field.imageSrc) {\n <button type=\"button\" class=\"mt-2 block flex-1 cursor-zoom-in overflow-hidden rounded-xl border border-slate-200 bg-white\" [attr.aria-label]=\"'\u041F\u0435\u0440\u0435\u0433\u043B\u044F\u043D\u0443\u0442\u0438 ' + field.label\" (click)=\"openViewer(field.imageSrc)\">\n <img [src]=\"field.imageSrc\" [alt]=\"field.label\" class=\"h-full w-full object-cover\" />\n </button>\n } @else {\n <p class=\"mt-2 text-base font-semibold text-slate-900\">-</p>\n }\n } @else {\n <p class=\"mt-2 text-base font-semibold text-slate-900\">{{ field.displayValue ?? '-' }}</p>\n }\n </div>\n }\n</div>\n\n@if (selectedImageSrc(); as imageSrc) {\n <app-images-viewer [urls]=\"[imageSrc]\" (closed)=\"closeViewer()\" />\n}\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: ImagesViewerComponent, selector: "app-images-viewer", inputs: ["urls", "initialIndex"], outputs: ["closed"] }] });
|
|
524
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.20", type: DetailsGridComponent, isStandalone: true, selector: "app-details-grid", inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"grid gap-4\" [class.grid-cols-1]=\"!hasExplicitColumns()\" [class.sm:grid-cols-3]=\"!hasExplicitColumns()\" [class.sm:grid-cols-12]=\"hasExplicitColumns()\">\n @for (field of displayFields(); track $index) {\n <div class=\"rounded-2xl border border-slate-100 bg-slate-200 p-4\" [ngClass]=\"getFieldColumnClass(field)\" [class.flex]=\"field.type === 'image'\" [class.flex-col]=\"field.type === 'image'\" [class.min-h-64]=\"field.type === 'image'\">\n <p class=\"text-xs uppercase tracking-[0.18em] text-slate-500\">{{ field.label }}</p>\n @if (field.type === 'image') {\n @if (field.imageSrc) {\n <button type=\"button\" class=\"mt-2 block flex-1 cursor-zoom-in overflow-hidden rounded-xl border border-slate-200 bg-white\" [attr.aria-label]=\"'\u041F\u0435\u0440\u0435\u0433\u043B\u044F\u043D\u0443\u0442\u0438 ' + field.label\" (click)=\"openViewer(field.imageSrc)\">\n <img [src]=\"field.imageSrc\" [alt]=\"field.label\" class=\"h-full w-full object-cover\" />\n </button>\n } @else {\n <p class=\"mt-2 text-base font-semibold text-slate-900\">-</p>\n }\n } @else {\n <p class=\"mt-2 text-base font-semibold text-slate-900\">{{ field.displayValue ?? '-' }}</p>\n }\n </div>\n }\n</div>\n\n@if (selectedImageSrc(); as imageSrc) {\n <app-images-viewer [urls]=\"[imageSrc]\" (closed)=\"closeViewer()\" />\n}\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: ImagesViewerComponent, selector: "app-images-viewer", inputs: ["urls", "initialIndex"], outputs: ["closed"] }] });
|
|
457
525
|
}
|
|
458
526
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: DetailsGridComponent, decorators: [{
|
|
459
527
|
type: Component,
|
|
@@ -501,14 +569,14 @@ class ImagesComponent {
|
|
|
501
569
|
.filter(Boolean)));
|
|
502
570
|
}
|
|
503
571
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: ImagesComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
504
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.20", type: ImagesComponent, isStandalone: true, selector: "app-images", inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@if (resolvedImageUrls().length === 0) {\n <p class=\"text-sm text-slate-500\">\u0424\u043E\u0442\u043E \u0432\u0456\u0434\u0441\u0443\u0442\u043D\u0456</p>\n} @else {\n <div\n class=\"grid gap-3\"\n [ngClass]=\"resolvedImageUrls().length === 1 ? 'grid-cols-1' : 'sm:grid-cols-2 lg:grid-cols-4'\"\n >\n @for (image of resolvedImagePreviewUrls(); track image; let i = $index) {\n <button\n type=\"button\"\n class=\"group block overflow-hidden rounded-2xl border border-slate-200 bg-slate-50 text-left\"\n [attr.aria-label]=\"'\u041F\u0435\u0440\u0435\u0433\u043B\u044F\u043D\u0443\u0442\u0438 \u0444\u043E\u0442\u043E ' + (i + 1)\"\n (click)=\"openViewer(i)\"\n >\n <img\n [src]=\"image\"\n [alt]=\"'\u0424\u043E\u0442\u043E ' + (i + 1)\"\n loading=\"lazy\"\n decoding=\"async\"\n class=\"w-full object-cover transition duration-200 group-hover:scale-[1.02]\"\n [style.max-height.px]=\"resolvedImageMaxHeight()\"\n [ngClass]=\"resolvedImageUrls().length === 1 ? 'h-auto' : 'h-48'\"\n />\n </button>\n }\n </div>\n\n @if (isViewerOpen()) {\n <app-images-viewer\n [urls]=\"resolvedImageUrls()\"\n [initialIndex]=\"viewerStartIndex()\"\n (closed)=\"closeViewer()\"\n ></app-images-viewer>\n }\n}\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: ImagesViewerComponent, selector: "app-images-viewer", inputs: ["urls", "initialIndex"], outputs: ["closed"] }] });
|
|
572
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.20", type: ImagesComponent, isStandalone: true, selector: "app-images", inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@if (resolvedImageUrls().length === 0) {\n <p class=\"text-sm text-slate-500\">\u0424\u043E\u0442\u043E \u0432\u0456\u0434\u0441\u0443\u0442\u043D\u0456</p>\n} @else {\n <div\n class=\"grid gap-3\"\n [ngClass]=\"resolvedImageUrls().length === 1 ? 'grid-cols-1' : 'sm:grid-cols-2 lg:grid-cols-4'\"\n >\n @for (image of resolvedImagePreviewUrls(); track image; let i = $index) {\n <button\n type=\"button\"\n class=\"group block overflow-hidden rounded-2xl border border-slate-200 bg-slate-50 text-left\"\n [attr.aria-label]=\"'\u041F\u0435\u0440\u0435\u0433\u043B\u044F\u043D\u0443\u0442\u0438 \u0444\u043E\u0442\u043E ' + (i + 1)\"\n (click)=\"openViewer(i)\"\n >\n <img\n [src]=\"image\"\n [alt]=\"'\u0424\u043E\u0442\u043E ' + (i + 1)\"\n loading=\"lazy\"\n decoding=\"async\"\n class=\"w-full object-cover transition duration-200 group-hover:scale-[1.02]\"\n [style.max-height.px]=\"resolvedImageMaxHeight()\"\n [ngClass]=\"resolvedImageUrls().length === 1 ? 'h-auto' : 'h-48'\"\n />\n </button>\n }\n </div>\n\n @if (isViewerOpen()) {\n <app-images-viewer\n [urls]=\"resolvedImageUrls()\"\n [initialIndex]=\"viewerStartIndex()\"\n (closed)=\"closeViewer()\"\n ></app-images-viewer>\n }\n}\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: ImagesViewerComponent, selector: "app-images-viewer", inputs: ["urls", "initialIndex"], outputs: ["closed"] }] });
|
|
505
573
|
}
|
|
506
574
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: ImagesComponent, decorators: [{
|
|
507
575
|
type: Component,
|
|
508
576
|
args: [{ selector: 'app-images', standalone: true, imports: [CommonModule, ImagesViewerComponent], template: "@if (resolvedImageUrls().length === 0) {\n <p class=\"text-sm text-slate-500\">\u0424\u043E\u0442\u043E \u0432\u0456\u0434\u0441\u0443\u0442\u043D\u0456</p>\n} @else {\n <div\n class=\"grid gap-3\"\n [ngClass]=\"resolvedImageUrls().length === 1 ? 'grid-cols-1' : 'sm:grid-cols-2 lg:grid-cols-4'\"\n >\n @for (image of resolvedImagePreviewUrls(); track image; let i = $index) {\n <button\n type=\"button\"\n class=\"group block overflow-hidden rounded-2xl border border-slate-200 bg-slate-50 text-left\"\n [attr.aria-label]=\"'\u041F\u0435\u0440\u0435\u0433\u043B\u044F\u043D\u0443\u0442\u0438 \u0444\u043E\u0442\u043E ' + (i + 1)\"\n (click)=\"openViewer(i)\"\n >\n <img\n [src]=\"image\"\n [alt]=\"'\u0424\u043E\u0442\u043E ' + (i + 1)\"\n loading=\"lazy\"\n decoding=\"async\"\n class=\"w-full object-cover transition duration-200 group-hover:scale-[1.02]\"\n [style.max-height.px]=\"resolvedImageMaxHeight()\"\n [ngClass]=\"resolvedImageUrls().length === 1 ? 'h-auto' : 'h-48'\"\n />\n </button>\n }\n </div>\n\n @if (isViewerOpen()) {\n <app-images-viewer\n [urls]=\"resolvedImageUrls()\"\n [initialIndex]=\"viewerStartIndex()\"\n (closed)=\"closeViewer()\"\n ></app-images-viewer>\n }\n}\n" }]
|
|
509
577
|
}], propDecorators: { config: [{ type: i0.Input, args: [{ isSignal: true, alias: "config", required: false }] }], data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }] } });
|
|
510
578
|
|
|
511
|
-
class
|
|
579
|
+
class TableComponent {
|
|
512
580
|
config;
|
|
513
581
|
data = null;
|
|
514
582
|
http = inject(HttpClient);
|
|
@@ -516,10 +584,10 @@ class StandardTableComponent {
|
|
|
516
584
|
columnsSignal = signal([], ...(ngDevMode ? [{ debugName: "columnsSignal" }] : /* istanbul ignore next */ []));
|
|
517
585
|
rowsSignal = signal([], ...(ngDevMode ? [{ debugName: "rowsSignal" }] : /* istanbul ignore next */ []));
|
|
518
586
|
paginationSignal = signal({ page: 0, pageSize: 10, total: 0 }, ...(ngDevMode ? [{ debugName: "paginationSignal" }] : /* istanbul ignore next */ []));
|
|
587
|
+
pageSizeOptionsSignal = signal([10, 20, 30], ...(ngDevMode ? [{ debugName: "pageSizeOptionsSignal" }] : /* istanbul ignore next */ []));
|
|
519
588
|
headerActionsSignal = signal([], ...(ngDevMode ? [{ debugName: "headerActionsSignal" }] : /* istanbul ignore next */ []));
|
|
520
589
|
loadingSignal = signal(false, ...(ngDevMode ? [{ debugName: "loadingSignal" }] : /* istanbul ignore next */ []));
|
|
521
590
|
errorSignal = signal('', ...(ngDevMode ? [{ debugName: "errorSignal" }] : /* istanbul ignore next */ []));
|
|
522
|
-
editableOptionsSignal = signal({}, ...(ngDevMode ? [{ debugName: "editableOptionsSignal" }] : /* istanbul ignore next */ []));
|
|
523
591
|
columns = this.columnsSignal.asReadonly();
|
|
524
592
|
rows = this.rowsSignal.asReadonly();
|
|
525
593
|
headerActions = this.headerActionsSignal.asReadonly();
|
|
@@ -616,7 +684,8 @@ class StandardTableComponent {
|
|
|
616
684
|
this.setColumns();
|
|
617
685
|
this.headerActionsSignal.set(view.actions?.header?.custom || []);
|
|
618
686
|
this.rowsSignal.set(this.rowsFrom(view, sourceColumns));
|
|
619
|
-
|
|
687
|
+
if (view.pagination?.sizes?.length)
|
|
688
|
+
this.pageSizeOptionsSignal.set(view.pagination.sizes);
|
|
620
689
|
this.page = view.pagination?.page ?? this.page;
|
|
621
690
|
this.pageSize = view.pagination?.size ?? this.pageSize;
|
|
622
691
|
this.paginationSignal.set({
|
|
@@ -651,31 +720,13 @@ class StandardTableComponent {
|
|
|
651
720
|
editableCondition: String(this.config?.props?.['editableCondition'] || config?.['condition'] || ''),
|
|
652
721
|
editor: config?.editor,
|
|
653
722
|
bindKey: config?.bindKey || key,
|
|
654
|
-
options: config?.options ||
|
|
723
|
+
options: config?.options || [],
|
|
724
|
+
data: config?.data,
|
|
725
|
+
dataParams: config?.dataParams,
|
|
655
726
|
placeholder: config?.placeholder,
|
|
656
|
-
includeBlankOption: config?.includeBlankOption,
|
|
657
|
-
blankOptionLabel: config?.blankOptionLabel,
|
|
658
727
|
};
|
|
659
728
|
}));
|
|
660
729
|
}
|
|
661
|
-
loadEditableOptions() {
|
|
662
|
-
const sources = this.editableColumns().filter((column) => column.data && !column.options);
|
|
663
|
-
if (!sources.length)
|
|
664
|
-
return;
|
|
665
|
-
forkJoin(sources.map((column) => this.http.get(`/api/suggest/${encodeURIComponent(column.data)}`))).subscribe({
|
|
666
|
-
next: (responses) => {
|
|
667
|
-
const options = { ...this.editableOptionsSignal() };
|
|
668
|
-
sources.forEach((column, index) => {
|
|
669
|
-
options[column.key] = (responses[index] || []).map((item) => ({
|
|
670
|
-
value: item?.key == null ? null : String(item.key),
|
|
671
|
-
label: String(item?.label || item?.key || '').trim(),
|
|
672
|
-
}));
|
|
673
|
-
});
|
|
674
|
-
this.editableOptionsSignal.set(options);
|
|
675
|
-
this.setColumns();
|
|
676
|
-
},
|
|
677
|
-
});
|
|
678
|
-
}
|
|
679
730
|
rowsFrom(view, columns) {
|
|
680
731
|
const configured = view.actions?.row?.custom || [];
|
|
681
732
|
return (view.rows || []).map((raw) => {
|
|
@@ -688,10 +739,11 @@ class StandardTableComponent {
|
|
|
688
739
|
row['id'] = String(row['id'] || '');
|
|
689
740
|
if (!Array.isArray(row['actions'])) {
|
|
690
741
|
const actions = [...configured];
|
|
691
|
-
|
|
742
|
+
const actionIds = new Set(actions.map((action) => String(action.id || '').trim()).filter(Boolean));
|
|
743
|
+
if (view.viewActions?.edit !== false && view.actions?.row?.edit !== false && this.editEnabled() && !actionIds.has('edit')) {
|
|
692
744
|
actions.push({ id: 'edit', label: 'Редагувати', type: 'form', formId: this.tableName() });
|
|
693
745
|
}
|
|
694
|
-
if (view.viewActions?.delete !== false && view.actions?.row?.delete !== false) {
|
|
746
|
+
if (view.viewActions?.delete !== false && view.actions?.row?.delete !== false && !actionIds.has('delete')) {
|
|
695
747
|
actions.push({ id: 'delete', label: 'Видалити', type: 'submit', api: `/api/crud/${this.tableName()}/${row['id']}`, method: 'DELETE' });
|
|
696
748
|
}
|
|
697
749
|
if (actions.length)
|
|
@@ -745,12 +797,12 @@ class StandardTableComponent {
|
|
|
745
797
|
return this.resolveObject(value, row);
|
|
746
798
|
return value;
|
|
747
799
|
}
|
|
748
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type:
|
|
749
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.20", type:
|
|
800
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: TableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
801
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.20", type: TableComponent, isStandalone: true, selector: "app-card-table", inputs: { config: "config", data: "data" }, usesOnChanges: true, ngImport: i0, template: "<section class=\"space-y-4\">\n @if (headerActions().length) {\n <div class=\"flex justify-end gap-2\">\n @for (action of headerActions(); track action.id) {\n <button type=\"button\" class=\"rounded-lg border border-slate-200 px-3 py-1.5 text-sm\" (click)=\"onHeaderAction(action)\">\n {{ action.label }}\n </button>\n }\n </div>\n }\n\n <app-ui-table\n [data]=\"tableData()\"\n [columns]=\"columns()\"\n [pageSizeOptions]=\"pageSizeOptionsSignal()\"\n [isLoading]=\"isLoading()\"\n [disablePaging]=\"disablePaging\"\n (pageChange)=\"onPageChange($event)\"\n (actionClick)=\"onAction($event)\"\n (cellChange)=\"onCellChange($event)\"\n ></app-ui-table>\n</section>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: UiTableComponent, selector: "app-ui-table", inputs: ["pageSizeOptions", "columns", "isLoading", "data", "disablePaging", "rowClickable", "imageResolver"], outputs: ["pageChange", "rowClick", "actionClick", "cellChange"] }] });
|
|
750
802
|
}
|
|
751
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type:
|
|
803
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: TableComponent, decorators: [{
|
|
752
804
|
type: Component,
|
|
753
|
-
args: [{ selector: 'app-card-table', standalone: true, imports: [CommonModule, UiTableComponent], template: "<section class=\"space-y-4\">\n @if (headerActions().length) {\n <div class=\"flex justify-end gap-2\">\n @for (action of headerActions(); track action.id) {\n <button type=\"button\" class=\"rounded-lg border border-slate-200 px-3 py-1.5 text-sm\" (click)=\"onHeaderAction(action)\">\n {{ action.label }}\n </button>\n }\n </div>\n }\n\n <app-ui-table\n [data]=\"tableData()\"\n [columns]=\"columns()\"\n [isLoading]=\"isLoading()\"\n [disablePaging]=\"disablePaging\"\n (pageChange)=\"onPageChange($event)\"\n (actionClick)=\"onAction($event)\"\n (cellChange)=\"onCellChange($event)\"\n ></app-ui-table>\n</section>\n" }]
|
|
805
|
+
args: [{ selector: 'app-card-table', standalone: true, imports: [CommonModule, UiTableComponent], template: "<section class=\"space-y-4\">\n @if (headerActions().length) {\n <div class=\"flex justify-end gap-2\">\n @for (action of headerActions(); track action.id) {\n <button type=\"button\" class=\"rounded-lg border border-slate-200 px-3 py-1.5 text-sm\" (click)=\"onHeaderAction(action)\">\n {{ action.label }}\n </button>\n }\n </div>\n }\n\n <app-ui-table\n [data]=\"tableData()\"\n [columns]=\"columns()\"\n [pageSizeOptions]=\"pageSizeOptionsSignal()\"\n [isLoading]=\"isLoading()\"\n [disablePaging]=\"disablePaging\"\n (pageChange)=\"onPageChange($event)\"\n (actionClick)=\"onAction($event)\"\n (cellChange)=\"onCellChange($event)\"\n ></app-ui-table>\n</section>\n" }]
|
|
754
806
|
}], ctorParameters: () => [], propDecorators: { config: [{
|
|
755
807
|
type: Input,
|
|
756
808
|
args: [{ required: true }]
|
|
@@ -768,6 +820,7 @@ function normalizeTableResponse(response) {
|
|
|
768
820
|
size: raw['size'] ?? source.pagination?.size,
|
|
769
821
|
total: raw['total'] ?? source.pagination?.total,
|
|
770
822
|
totalElements: raw['total'] ?? source.pagination?.totalElements,
|
|
823
|
+
sizes: source.pagination?.sizes,
|
|
771
824
|
},
|
|
772
825
|
};
|
|
773
826
|
}
|
|
@@ -837,11 +890,11 @@ class UiCardPanelsLayoutComponent {
|
|
|
837
890
|
return found.resolveInputs(tab, this.data());
|
|
838
891
|
}
|
|
839
892
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: UiCardPanelsLayoutComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
840
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.20", type: UiCardPanelsLayoutComponent, isStandalone: true, selector: "app-ui-card-panels-layout", inputs: { tabs: { classPropertyName: "tabs", publicName: "tabs", isSignal: true, isRequired: false, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, embedded: { classPropertyName: "embedded", publicName: "embedded", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@if (tabs().length) {\n <div class=\"grid grid-cols-1 gap-4 md:grid-cols-12\">\n @for (tab of tabs(); track tab.id) {\n <section\n [class.rounded-3xl]=\"!embedded()\"\n [class.border]=\"!embedded()\"\n [class.border-slate-200]=\"!embedded()\"\n [class.bg-white]=\"!embedded()\"\n [class.p-6]=\"!embedded()\"\n [class.min-w-0]=\"embedded()\"\n [ngClass]=\"getColSpanClass(tab)\"\n [attr.data-card-panel-id]=\"tab.id\"\n >\n @if (shouldShowPanelHeader(tab)) {\n <app-card-section-header\n [data]=\"data()\"\n [title]=\"tab.title\"\n [actions]=\"getVisibleActions(tab)\"\n ></app-card-section-header>\n }\n\n @if (getNestedCardConfig(tab); as nestedCard) {\n <app-card [tabs]=\"nestedCard.tabs\" [data]=\"data()\" [layout]=\"nestedCard.layout\"></app-card>\n } @else if (getNestedPanels(tab); as nestedPanels) {\n <app-card [tabs]=\"nestedPanels\" [data]=\"data()\" layout=\"panels\" [embedded]=\"true\"></app-card>\n } @else if (tab.component === 'detailsGrid') {\n <app-details-grid [config]=\"tab\" [data]=\"data()\"></app-details-grid>\n } @else if (tab.component === 'table') {\n <app-card-table [config]=\"tab\" [data]=\"data()\"></app-card-table>\n } @else if (tab.component === 'images') {\n <app-images [config]=\"tab\" [data]=\"data()\"></app-images>\n } @else if (tab.component === 'html') {\n <app-card-html-tab [html]=\"tab.html || ''\"></app-card-html-tab>\n } @else if (getRegisteredComponent(tab); as customComponent) {\n <ng-container *ngComponentOutlet=\"customComponent; inputs: getRegisteredInputs(tab)\"></ng-container>\n }\n </section>\n }\n </div>\n}\n", dependencies: [{ kind: "ngmodule", type: i0.forwardRef(() => CommonModule) }, { kind: "directive", type: i0.forwardRef(() => i1.NgClass), selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i0.forwardRef(() => i1.NgComponentOutlet), selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }, { kind: "component", type: i0.forwardRef(() => CardComponent), selector: "app-card", inputs: ["tabs", "data", "layout", "embedded"] }, { kind: "component", type: i0.forwardRef(() => CardSectionHeaderComponent), selector: "app-card-section-header", inputs: ["variant", "title", "actions", "data"] }, { kind: "component", type: i0.forwardRef(() => CardHtmlTabComponent), selector: "app-card-html-tab", inputs: ["html"] }, { kind: "component", type: i0.forwardRef(() => DetailsGridComponent), selector: "app-details-grid", inputs: ["config", "data"] }, { kind: "component", type: i0.forwardRef(() => ImagesComponent), selector: "app-images", inputs: ["config", "data"] }, { kind: "component", type: i0.forwardRef(() =>
|
|
893
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.20", type: UiCardPanelsLayoutComponent, isStandalone: true, selector: "app-ui-card-panels-layout", inputs: { tabs: { classPropertyName: "tabs", publicName: "tabs", isSignal: true, isRequired: false, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, embedded: { classPropertyName: "embedded", publicName: "embedded", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@if (tabs().length) {\n <div class=\"grid grid-cols-1 gap-4 md:grid-cols-12\">\n @for (tab of tabs(); track tab.id) {\n <section\n [class.rounded-3xl]=\"!embedded()\"\n [class.border]=\"!embedded()\"\n [class.border-slate-200]=\"!embedded()\"\n [class.bg-white]=\"!embedded()\"\n [class.p-6]=\"!embedded()\"\n [class.min-w-0]=\"embedded()\"\n [ngClass]=\"getColSpanClass(tab)\"\n [attr.data-card-panel-id]=\"tab.id\"\n >\n @if (shouldShowPanelHeader(tab)) {\n <app-card-section-header\n [data]=\"data()\"\n [title]=\"tab.title\"\n [actions]=\"getVisibleActions(tab)\"\n ></app-card-section-header>\n }\n\n @if (getNestedCardConfig(tab); as nestedCard) {\n <app-card [tabs]=\"nestedCard.tabs\" [data]=\"data()\" [layout]=\"nestedCard.layout\"></app-card>\n } @else if (getNestedPanels(tab); as nestedPanels) {\n <app-card [tabs]=\"nestedPanels\" [data]=\"data()\" layout=\"panels\" [embedded]=\"true\"></app-card>\n } @else if (tab.component === 'detailsGrid') {\n <app-details-grid [config]=\"tab\" [data]=\"data()\"></app-details-grid>\n } @else if (tab.component === 'table') {\n <app-card-table [config]=\"tab\" [data]=\"data()\"></app-card-table>\n } @else if (tab.component === 'images') {\n <app-images [config]=\"tab\" [data]=\"data()\"></app-images>\n } @else if (tab.component === 'html') {\n <app-card-html-tab [html]=\"tab.html || ''\"></app-card-html-tab>\n } @else if (getRegisteredComponent(tab); as customComponent) {\n <ng-container *ngComponentOutlet=\"customComponent; inputs: getRegisteredInputs(tab)\"></ng-container>\n }\n </section>\n }\n </div>\n}\n", dependencies: [{ kind: "ngmodule", type: i0.forwardRef(() => CommonModule) }, { kind: "directive", type: i0.forwardRef(() => i1$1.NgClass), selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i0.forwardRef(() => i1$1.NgComponentOutlet), selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }, { kind: "component", type: i0.forwardRef(() => CardComponent), selector: "app-card", inputs: ["tabs", "data", "layout", "embedded"] }, { kind: "component", type: i0.forwardRef(() => CardSectionHeaderComponent), selector: "app-card-section-header", inputs: ["variant", "title", "actions", "data"] }, { kind: "component", type: i0.forwardRef(() => CardHtmlTabComponent), selector: "app-card-html-tab", inputs: ["html"] }, { kind: "component", type: i0.forwardRef(() => DetailsGridComponent), selector: "app-details-grid", inputs: ["config", "data"] }, { kind: "component", type: i0.forwardRef(() => ImagesComponent), selector: "app-images", inputs: ["config", "data"] }, { kind: "component", type: i0.forwardRef(() => TableComponent), selector: "app-card-table", inputs: ["config", "data"] }] });
|
|
841
894
|
}
|
|
842
895
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: UiCardPanelsLayoutComponent, decorators: [{
|
|
843
896
|
type: Component,
|
|
844
|
-
args: [{ selector: 'app-ui-card-panels-layout', standalone: true, imports: [CommonModule, forwardRef(() => CardComponent), CardSectionHeaderComponent, CardHtmlTabComponent, DetailsGridComponent, ImagesComponent,
|
|
897
|
+
args: [{ selector: 'app-ui-card-panels-layout', standalone: true, imports: [CommonModule, forwardRef(() => CardComponent), CardSectionHeaderComponent, CardHtmlTabComponent, DetailsGridComponent, ImagesComponent, TableComponent], template: "@if (tabs().length) {\n <div class=\"grid grid-cols-1 gap-4 md:grid-cols-12\">\n @for (tab of tabs(); track tab.id) {\n <section\n [class.rounded-3xl]=\"!embedded()\"\n [class.border]=\"!embedded()\"\n [class.border-slate-200]=\"!embedded()\"\n [class.bg-white]=\"!embedded()\"\n [class.p-6]=\"!embedded()\"\n [class.min-w-0]=\"embedded()\"\n [ngClass]=\"getColSpanClass(tab)\"\n [attr.data-card-panel-id]=\"tab.id\"\n >\n @if (shouldShowPanelHeader(tab)) {\n <app-card-section-header\n [data]=\"data()\"\n [title]=\"tab.title\"\n [actions]=\"getVisibleActions(tab)\"\n ></app-card-section-header>\n }\n\n @if (getNestedCardConfig(tab); as nestedCard) {\n <app-card [tabs]=\"nestedCard.tabs\" [data]=\"data()\" [layout]=\"nestedCard.layout\"></app-card>\n } @else if (getNestedPanels(tab); as nestedPanels) {\n <app-card [tabs]=\"nestedPanels\" [data]=\"data()\" layout=\"panels\" [embedded]=\"true\"></app-card>\n } @else if (tab.component === 'detailsGrid') {\n <app-details-grid [config]=\"tab\" [data]=\"data()\"></app-details-grid>\n } @else if (tab.component === 'table') {\n <app-card-table [config]=\"tab\" [data]=\"data()\"></app-card-table>\n } @else if (tab.component === 'images') {\n <app-images [config]=\"tab\" [data]=\"data()\"></app-images>\n } @else if (tab.component === 'html') {\n <app-card-html-tab [html]=\"tab.html || ''\"></app-card-html-tab>\n } @else if (getRegisteredComponent(tab); as customComponent) {\n <ng-container *ngComponentOutlet=\"customComponent; inputs: getRegisteredInputs(tab)\"></ng-container>\n }\n </section>\n }\n </div>\n}\n" }]
|
|
845
898
|
}], propDecorators: { tabs: [{ type: i0.Input, args: [{ isSignal: true, alias: "tabs", required: false }] }], data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], embedded: [{ type: i0.Input, args: [{ isSignal: true, alias: "embedded", required: false }] }] } });
|
|
846
899
|
|
|
847
900
|
class UiCardTabsLayoutComponent {
|
|
@@ -907,11 +960,11 @@ class UiCardTabsLayoutComponent {
|
|
|
907
960
|
return found.resolveInputs(tab, this.data());
|
|
908
961
|
}
|
|
909
962
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: UiCardTabsLayoutComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
910
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.20", type: UiCardTabsLayoutComponent, isStandalone: true, selector: "app-ui-card-tabs-layout", inputs: { tabs: { classPropertyName: "tabs", publicName: "tabs", isSignal: true, isRequired: false, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@if (tabs().length) {\n <section class=\"space-y-4\">\n <nav class=\"flex flex-wrap gap-2\" aria-label=\"Card tabs\">\n @for (tab of tabs(); track tab.id) {\n <button\n type=\"button\"\n class=\"rounded-xl border px-4 py-2 text-sm font-medium transition\"\n [class.border-slate-900]=\"activeTab()?.id === tab.id\"\n [class.bg-slate-900]=\"activeTab()?.id === tab.id\"\n [class.text-white]=\"activeTab()?.id === tab.id\"\n [class.border-slate-200]=\"activeTab()?.id !== tab.id\"\n [class.bg-white]=\"activeTab()?.id !== tab.id\"\n [class.text-slate-700]=\"activeTab()?.id !== tab.id\"\n (click)=\"setActiveTab(tab.id)\"\n >\n {{ tab.title }}\n </button>\n }\n </nav>\n\n @if (activeTab(); as tab) {\n <section\n [attr.data-card-tab-id]=\"tab.id\"\n >\n @if (getVisibleActions(tab).length) {\n <app-card-section-header\n variant=\"tabs\"\n [data]=\"data()\"\n [actions]=\"getVisibleActions(tab)\"\n ></app-card-section-header>\n }\n\n @if (tab.tabs?.length) {\n <app-card [tabs]=\"tab.tabs || []\" [data]=\"data()\" [layout]=\"tab.layout || 'panels'\"></app-card>\n } @else if (tab.component === 'images') {\n <app-images\n [config]=\"tab\"\n [data]=\"data()\"\n ></app-images>\n } @else if (tab.component === 'detailsGrid') {\n <app-details-grid [config]=\"tab\" [data]=\"data()\"></app-details-grid>\n } @else if (tab.component === 'table') {\n <app-card-table [config]=\"tab\" [data]=\"data()\"></app-card-table>\n } @else if (tab.component === 'html') {\n <app-card-html-tab [html]=\"tab.html || ''\"></app-card-html-tab>\n } @else if (getRegisteredComponent(tab); as customComponent) {\n <ng-container *ngComponentOutlet=\"customComponent; inputs: getRegisteredInputs(tab)\"></ng-container>\n }\n </section>\n }\n </section>\n}\n", dependencies: [{ kind: "ngmodule", type: i0.forwardRef(() => CommonModule) }, { kind: "directive", type: i0.forwardRef(() => i1.NgComponentOutlet), selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }, { kind: "component", type: i0.forwardRef(() => CardComponent), selector: "app-card", inputs: ["tabs", "data", "layout", "embedded"] }, { kind: "component", type: i0.forwardRef(() => CardSectionHeaderComponent), selector: "app-card-section-header", inputs: ["variant", "title", "actions", "data"] }, { kind: "component", type: i0.forwardRef(() => CardHtmlTabComponent), selector: "app-card-html-tab", inputs: ["html"] }, { kind: "component", type: i0.forwardRef(() => ImagesComponent), selector: "app-images", inputs: ["config", "data"] }, { kind: "component", type: i0.forwardRef(() => DetailsGridComponent), selector: "app-details-grid", inputs: ["config", "data"] }, { kind: "component", type: i0.forwardRef(() =>
|
|
963
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.20", type: UiCardTabsLayoutComponent, isStandalone: true, selector: "app-ui-card-tabs-layout", inputs: { tabs: { classPropertyName: "tabs", publicName: "tabs", isSignal: true, isRequired: false, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@if (tabs().length) {\n <section class=\"space-y-4\">\n <nav class=\"flex flex-wrap gap-2\" aria-label=\"Card tabs\">\n @for (tab of tabs(); track tab.id) {\n <button\n type=\"button\"\n class=\"rounded-xl border px-4 py-2 text-sm font-medium transition\"\n [class.border-slate-900]=\"activeTab()?.id === tab.id\"\n [class.bg-slate-900]=\"activeTab()?.id === tab.id\"\n [class.text-white]=\"activeTab()?.id === tab.id\"\n [class.border-slate-200]=\"activeTab()?.id !== tab.id\"\n [class.bg-white]=\"activeTab()?.id !== tab.id\"\n [class.text-slate-700]=\"activeTab()?.id !== tab.id\"\n (click)=\"setActiveTab(tab.id)\"\n >\n {{ tab.title }}\n </button>\n }\n </nav>\n\n @if (activeTab(); as tab) {\n <section\n [attr.data-card-tab-id]=\"tab.id\"\n >\n @if (getVisibleActions(tab).length) {\n <app-card-section-header\n variant=\"tabs\"\n [data]=\"data()\"\n [actions]=\"getVisibleActions(tab)\"\n ></app-card-section-header>\n }\n\n @if (tab.tabs?.length) {\n <app-card [tabs]=\"tab.tabs || []\" [data]=\"data()\" [layout]=\"tab.layout || 'panels'\"></app-card>\n } @else if (tab.component === 'images') {\n <app-images\n [config]=\"tab\"\n [data]=\"data()\"\n ></app-images>\n } @else if (tab.component === 'detailsGrid') {\n <app-details-grid [config]=\"tab\" [data]=\"data()\"></app-details-grid>\n } @else if (tab.component === 'table') {\n <app-card-table [config]=\"tab\" [data]=\"data()\"></app-card-table>\n } @else if (tab.component === 'html') {\n <app-card-html-tab [html]=\"tab.html || ''\"></app-card-html-tab>\n } @else if (getRegisteredComponent(tab); as customComponent) {\n <ng-container *ngComponentOutlet=\"customComponent; inputs: getRegisteredInputs(tab)\"></ng-container>\n }\n </section>\n }\n </section>\n}\n", dependencies: [{ kind: "ngmodule", type: i0.forwardRef(() => CommonModule) }, { kind: "directive", type: i0.forwardRef(() => i1$1.NgComponentOutlet), selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }, { kind: "component", type: i0.forwardRef(() => CardComponent), selector: "app-card", inputs: ["tabs", "data", "layout", "embedded"] }, { kind: "component", type: i0.forwardRef(() => CardSectionHeaderComponent), selector: "app-card-section-header", inputs: ["variant", "title", "actions", "data"] }, { kind: "component", type: i0.forwardRef(() => CardHtmlTabComponent), selector: "app-card-html-tab", inputs: ["html"] }, { kind: "component", type: i0.forwardRef(() => ImagesComponent), selector: "app-images", inputs: ["config", "data"] }, { kind: "component", type: i0.forwardRef(() => DetailsGridComponent), selector: "app-details-grid", inputs: ["config", "data"] }, { kind: "component", type: i0.forwardRef(() => TableComponent), selector: "app-card-table", inputs: ["config", "data"] }] });
|
|
911
964
|
}
|
|
912
965
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: UiCardTabsLayoutComponent, decorators: [{
|
|
913
966
|
type: Component,
|
|
914
|
-
args: [{ selector: 'app-ui-card-tabs-layout', standalone: true, imports: [CommonModule, forwardRef(() => CardComponent), CardSectionHeaderComponent, CardHtmlTabComponent, ImagesComponent, DetailsGridComponent,
|
|
967
|
+
args: [{ selector: 'app-ui-card-tabs-layout', standalone: true, imports: [CommonModule, forwardRef(() => CardComponent), CardSectionHeaderComponent, CardHtmlTabComponent, ImagesComponent, DetailsGridComponent, TableComponent], template: "@if (tabs().length) {\n <section class=\"space-y-4\">\n <nav class=\"flex flex-wrap gap-2\" aria-label=\"Card tabs\">\n @for (tab of tabs(); track tab.id) {\n <button\n type=\"button\"\n class=\"rounded-xl border px-4 py-2 text-sm font-medium transition\"\n [class.border-slate-900]=\"activeTab()?.id === tab.id\"\n [class.bg-slate-900]=\"activeTab()?.id === tab.id\"\n [class.text-white]=\"activeTab()?.id === tab.id\"\n [class.border-slate-200]=\"activeTab()?.id !== tab.id\"\n [class.bg-white]=\"activeTab()?.id !== tab.id\"\n [class.text-slate-700]=\"activeTab()?.id !== tab.id\"\n (click)=\"setActiveTab(tab.id)\"\n >\n {{ tab.title }}\n </button>\n }\n </nav>\n\n @if (activeTab(); as tab) {\n <section\n [attr.data-card-tab-id]=\"tab.id\"\n >\n @if (getVisibleActions(tab).length) {\n <app-card-section-header\n variant=\"tabs\"\n [data]=\"data()\"\n [actions]=\"getVisibleActions(tab)\"\n ></app-card-section-header>\n }\n\n @if (tab.tabs?.length) {\n <app-card [tabs]=\"tab.tabs || []\" [data]=\"data()\" [layout]=\"tab.layout || 'panels'\"></app-card>\n } @else if (tab.component === 'images') {\n <app-images\n [config]=\"tab\"\n [data]=\"data()\"\n ></app-images>\n } @else if (tab.component === 'detailsGrid') {\n <app-details-grid [config]=\"tab\" [data]=\"data()\"></app-details-grid>\n } @else if (tab.component === 'table') {\n <app-card-table [config]=\"tab\" [data]=\"data()\"></app-card-table>\n } @else if (tab.component === 'html') {\n <app-card-html-tab [html]=\"tab.html || ''\"></app-card-html-tab>\n } @else if (getRegisteredComponent(tab); as customComponent) {\n <ng-container *ngComponentOutlet=\"customComponent; inputs: getRegisteredInputs(tab)\"></ng-container>\n }\n </section>\n }\n </section>\n}\n" }]
|
|
915
968
|
}], propDecorators: { tabs: [{ type: i0.Input, args: [{ isSignal: true, alias: "tabs", required: false }] }], data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }] } });
|
|
916
969
|
|
|
917
970
|
class CardComponent {
|
|
@@ -1080,7 +1133,7 @@ class DocumentsUploadModalComponent {
|
|
|
1080
1133
|
}
|
|
1081
1134
|
}
|
|
1082
1135
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: DocumentsUploadModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1083
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.20", type: DocumentsUploadModalComponent, isStandalone: true, selector: "app-documents-upload-modal", inputs: { isOpen: { classPropertyName: "isOpen", publicName: "isOpen", isSignal: true, isRequired: false, transformFunction: null }, entity: { classPropertyName: "entity", publicName: "entity", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { closed: "closed", saved: "saved" }, viewQueries: [{ propertyName: "uploadFileInput", first: true, predicate: ["uploadFileInput"], descendants: true }], ngImport: i0, template: "<core-modal\n title=\"\u0414\u043E\u0434\u0430\u0442\u0438 \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442\"\n [isOpen]=\"isOpen()\"\n (closed)=\"close()\"\n>\n <div class=\"space-y-4\">\n <input-select\n [data]=\"'doc_type'\"\n name=\"uploadDocumentTypeId\"\n label=\"\u0422\u0438\u043F \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442\u0430\"\n hint=\"\u041D\u0435\u043E\u0431\u043E\u0432'\u044F\u0437\u043A\u043E\u0432\u043E\"\n [ngModel]=\"documentTypeId()\"\n (ngModelChange)=\"onDocumentTypeChange($event)\"\n placeholder=\"\u0422\u0438\u043F \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442\u0430\"\n [fullWidth]=\"true\"\n [searchable]=\"true\"\n ></input-select>\n\n <div class=\"space-y-2\">\n <label class=\"block text-sm font-medium text-slate-700\" for=\"document-files\">\u0424\u0430\u0439\u043B\u0438</label>\n <input\n #uploadFileInput\n id=\"document-files\"\n type=\"file\"\n class=\"block w-full rounded-xl border border-slate-200 bg-white px-3 py-2.5 text-sm text-slate-900 outline-none transition file:mr-3 file:rounded-lg file:border-0 file:bg-slate-900 file:px-3 file:py-2 file:text-sm file:font-medium file:text-white focus:border-slate-400 focus:ring-2 focus:ring-slate-200\"\n multiple\n (change)=\"onFilesSelected($event)\"\n />\n @if (fileNames().length) {\n <div class=\"rounded-xl border border-slate-200 bg-slate-50 px-3 py-2 text-sm text-slate-600\">\n @for (fileName of fileNames(); track fileName) {\n <div class=\"truncate\">{{ fileName }}</div>\n }\n </div>\n } @else {\n <p class=\"text-xs text-slate-500\">\u041C\u043E\u0436\u043D\u0430 \u043E\u0431\u0440\u0430\u0442\u0438 \u043E\u0434\u0438\u043D \u0430\u0431\u043E \u0434\u0435\u043A\u0456\u043B\u044C\u043A\u0430 \u0444\u0430\u0439\u043B\u0456\u0432.</p>\n }\n </div>\n </div>\n\n <div slot=\"footer\" class=\"flex justify-end gap-2\">\n <ui-button variant=\"ghost\" [disabled]=\"isUploading()\" (click)=\"close()\">\u0421\u043A\u0430\u0441\u0443\u0432\u0430\u0442\u0438</ui-button>\n <ui-button variant=\"primary\" [disabled]=\"isUploading()\" (click)=\"submitUpload()\">\n {{ isUploading() ? '\u0417\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0435\u043D\u043D\u044F...' : '\u0417\u0431\u0435\u0440\u0435\u0433\u0442\u0438' }}\n </ui-button>\n </div>\n</core-modal>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$
|
|
1136
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.20", type: DocumentsUploadModalComponent, isStandalone: true, selector: "app-documents-upload-modal", inputs: { isOpen: { classPropertyName: "isOpen", publicName: "isOpen", isSignal: true, isRequired: false, transformFunction: null }, entity: { classPropertyName: "entity", publicName: "entity", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { closed: "closed", saved: "saved" }, viewQueries: [{ propertyName: "uploadFileInput", first: true, predicate: ["uploadFileInput"], descendants: true }], ngImport: i0, template: "<core-modal\n title=\"\u0414\u043E\u0434\u0430\u0442\u0438 \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442\"\n [isOpen]=\"isOpen()\"\n (closed)=\"close()\"\n>\n <div class=\"space-y-4\">\n <input-select\n [data]=\"'doc_type'\"\n name=\"uploadDocumentTypeId\"\n label=\"\u0422\u0438\u043F \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442\u0430\"\n hint=\"\u041D\u0435\u043E\u0431\u043E\u0432'\u044F\u0437\u043A\u043E\u0432\u043E\"\n [ngModel]=\"documentTypeId()\"\n (ngModelChange)=\"onDocumentTypeChange($event)\"\n placeholder=\"\u0422\u0438\u043F \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442\u0430\"\n [fullWidth]=\"true\"\n [searchable]=\"true\"\n ></input-select>\n\n <div class=\"space-y-2\">\n <label class=\"block text-sm font-medium text-slate-700\" for=\"document-files\">\u0424\u0430\u0439\u043B\u0438</label>\n <input\n #uploadFileInput\n id=\"document-files\"\n type=\"file\"\n class=\"block w-full rounded-xl border border-slate-200 bg-white px-3 py-2.5 text-sm text-slate-900 outline-none transition file:mr-3 file:rounded-lg file:border-0 file:bg-slate-900 file:px-3 file:py-2 file:text-sm file:font-medium file:text-white focus:border-slate-400 focus:ring-2 focus:ring-slate-200\"\n multiple\n (change)=\"onFilesSelected($event)\"\n />\n @if (fileNames().length) {\n <div class=\"rounded-xl border border-slate-200 bg-slate-50 px-3 py-2 text-sm text-slate-600\">\n @for (fileName of fileNames(); track fileName) {\n <div class=\"truncate\">{{ fileName }}</div>\n }\n </div>\n } @else {\n <p class=\"text-xs text-slate-500\">\u041C\u043E\u0436\u043D\u0430 \u043E\u0431\u0440\u0430\u0442\u0438 \u043E\u0434\u0438\u043D \u0430\u0431\u043E \u0434\u0435\u043A\u0456\u043B\u044C\u043A\u0430 \u0444\u0430\u0439\u043B\u0456\u0432.</p>\n }\n </div>\n </div>\n\n <div slot=\"footer\" class=\"flex justify-end gap-2\">\n <ui-button variant=\"ghost\" [disabled]=\"isUploading()\" (click)=\"close()\">\u0421\u043A\u0430\u0441\u0443\u0432\u0430\u0442\u0438</ui-button>\n <ui-button variant=\"primary\" [disabled]=\"isUploading()\" (click)=\"submitUpload()\">\n {{ isUploading() ? '\u0417\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0435\u043D\u043D\u044F...' : '\u0417\u0431\u0435\u0440\u0435\u0433\u0442\u0438' }}\n </ui-button>\n </div>\n</core-modal>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: LucideAngularModule }, { kind: "component", type: UiButtonComponent, selector: "ui-button", inputs: ["variant", "size", "disabled", "fullWidth", "type", "withShadow", "icon", "iconOnly", "ariaLabel"] }, { kind: "component", type: UiModalComponent, selector: "core-modal", inputs: ["isOpen", "title", "size", "position", "closable", "closeOnBackdrop", "closeOnEscape"], outputs: ["closed"] }, { kind: "component", type: InputSelectComponent, selector: "input-select", inputs: ["label", "placeholder", "name", "data", "required", "fullWidth", "disabled", "multiple", "includeBlankOption", "blankOptionLabel", "emptyStateLabel", "searchable", "remoteSearch", "searchPlaceholder", "options", "error", "hint", "triggerClass", "panelClass"], outputs: ["valueChange", "searchChange"] }] });
|
|
1084
1137
|
}
|
|
1085
1138
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: DocumentsUploadModalComponent, decorators: [{
|
|
1086
1139
|
type: Component,
|
|
@@ -1428,7 +1481,7 @@ class DocumentsComponent {
|
|
|
1428
1481
|
.subscribe();
|
|
1429
1482
|
}
|
|
1430
1483
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: DocumentsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1431
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.20", type: DocumentsComponent, isStandalone: true, selector: "app-documents", inputs: { entity: { classPropertyName: "entity", publicName: "entity", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"space-y-4\">\n <div>\n @if (isLoading()) {\n <div class=\"rounded-2xl border border-dashed border-slate-200 bg-slate-50 px-4 py-8 text-center text-sm text-slate-500\">\n \u0417\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0435\u043D\u043D\u044F \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442\u0456\u0432...\n </div>\n } @else if (documents().length === 0) {\n <div class=\"rounded-2xl border border-dashed border-slate-200 bg-slate-50 px-4 py-8 text-center text-sm text-slate-500\">\n \u0414\u043E\u043A\u0443\u043C\u0435\u043D\u0442\u0438 \u0432\u0456\u0434\u0441\u0443\u0442\u043D\u0456\n </div>\n } @else {\n <div class=\"grid gap-3\">\n @for (document of documents(); track trackDocument($index, document)) {\n <article class=\"flex flex-col gap-4 rounded-2xl border border-slate-200 bg-white p-4 shadow-sm sm:flex-row sm:items-center sm:justify-between\">\n @if (isImage(document)) {\n <button\n type=\"button\"\n class=\"block shrink-0 overflow-hidden rounded-2xl border border-slate-200 bg-slate-50 transition hover:border-slate-300 hover:bg-slate-100 h-28 w-40\"\n (click)=\"openImagePreview(document)\"\n >\n <img [src]=\"getImagePreviewUrl(document)\" [alt]=\"document.name || '\u0414\u043E\u043A\u0443\u043C\u0435\u043D\u0442'\" loading=\"lazy\" decoding=\"async\" class=\"h-full w-full object-cover\">\n </button>\n } @else {\n <a\n class=\"block shrink-0 overflow-hidden rounded-2xl border border-slate-200 bg-slate-50 transition hover:border-slate-300 hover:bg-slate-100\"\n [href]=\"document.downloadUrl\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n [class.h-28]=\"isPdf(document)\"\n [class.w-40]=\"isPdf(document)\"\n [class.h-20]=\"!isPdf(document)\"\n [class.w-24]=\"!isPdf(document)\"\n >\n @if (isPdf(document)) {\n <div class=\"flex h-full w-full flex-col items-center justify-center gap-2 px-3 py-4 text-slate-700\">\n <div class=\"flex h-11 w-11 items-center justify-center rounded-2xl bg-white text-slate-900 shadow-sm\">\n <lucide-icon [img]=\"fileIcon\"></lucide-icon>\n </div>\n <span class=\"text-xs font-semibold uppercase tracking-[0.18em] text-slate-600\">PDF</span>\n </div>\n } @else {\n <div class=\"flex h-full w-full flex-col items-center justify-center gap-2 px-3 py-4 text-slate-700\">\n <div class=\"flex h-11 w-11 items-center justify-center rounded-2xl bg-white text-slate-900 shadow-sm\">\n <lucide-icon [img]=\"fileIcon\"></lucide-icon>\n </div>\n <span class=\"text-[10px] font-semibold uppercase tracking-[0.18em] text-slate-500\">\n {{ getFileBadge(document) }}\n </span>\n </div>\n }\n </a>\n }\n\n <div class=\"min-w-0 flex-1\">\n <div class=\"flex flex-col gap-2 lg:flex-row lg:items-start lg:justify-between\">\n <div class=\"min-w-0\">\n <div class=\"flex flex-wrap items-center gap-2\">\n <h4 class=\"truncate text-sm font-semibold text-slate-900\">\n {{ document.name || '\u0411\u0435\u0437 \u043D\u0430\u0437\u0432\u0438' }}\n </h4>\n @if (getDocumentTypeLabel(document)) {\n <span class=\"inline-flex shrink-0 items-center rounded-full border border-sky-200 bg-sky-50 px-2.5 py-1 text-[10px] font-semibold uppercase tracking-[0.18em] text-sky-700\">\n {{ getDocumentTypeLabel(document) }}\n </span>\n }\n </div>\n <p class=\"mt-1 text-xs text-slate-500\">\n {{ formatSize(document.size) }} \u00B7 {{ document.type }}\n </p>\n </div>\n\n <div class=\"flex flex-wrap items-center gap-2\">\n <ui-button variant=\"ghost\" size=\"sm\" (click)=\"openTextModal(document)\">\n <lucide-icon [img]=\"eyeIcon\"></lucide-icon>\n \u041F\u0435\u0440\u0435\u0433\u043B\u044F\u0434\n </ui-button>\n @if (isImage(document)) {\n <ui-button variant=\"ghost\" size=\"sm\" [disabled]=\"isDocumentRecognizing(document) || isRecognitionPending(document) || isRecognitionCompleted(document)\" (click)=\"recognizeText(document)\">\n <lucide-icon [img]=\"sparkleIcon\"></lucide-icon>\n {{ isDocumentRecognizing(document) ? '\u0420\u043E\u0437\u043F\u0456\u0437\u043D\u0430\u0432\u0430\u043D\u043D\u044F...' : '\u0420\u043E\u0437\u043F\u0456\u0437\u043D\u0430\u0442\u0438 \u0434\u0430\u043D\u0456' }}\n </ui-button>\n }\n @if (isAdmin()) {\n <ui-button variant=\"ghost\" size=\"sm\" [disabled]=\"deletingDocumentId() === document.id\" (click)=\"removeDocument(document)\">\n <lucide-icon [img]=\"deleteIcon\"></lucide-icon>\n {{ deletingDocumentId() === document.id ? '\u0412\u0438\u0434\u0430\u043B\u0435\u043D\u043D\u044F...' : '\u0412\u0438\u0434\u0430\u043B\u0438\u0442\u0438' }}\n </ui-button>\n }\n </div>\n </div>\n\n <div class=\"mt-3 flex flex-wrap items-center justify-between gap-2\">\n <a\n class=\"inline-flex items-center rounded-lg border border-slate-200 bg-white px-3 py-2 text-sm font-medium text-slate-700 transition hover:border-slate-300 hover:bg-slate-50\"\n [href]=\"document.downloadUrl\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n \u0417\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0438\u0442\u0438\n </a>\n @if (isImage(document) && getRecognitionStatusLabel(document)) {\n <span\n class=\"inline-flex items-center rounded-full border px-2.5 py-1 text-[10px] font-semibold uppercase tracking-[0.12em]\"\n [class.border-amber-200]=\"isRecognitionPending(document)\"\n [class.bg-amber-50]=\"isRecognitionPending(document)\"\n [class.text-amber-700]=\"isRecognitionPending(document)\"\n [class.border-rose-200]=\"document.recognitionStatus === 'FAILED'\"\n [class.bg-rose-50]=\"document.recognitionStatus === 'FAILED'\"\n [class.text-rose-700]=\"document.recognitionStatus === 'FAILED'\"\n [class.border-emerald-200]=\"document.recognitionStatus === 'COMPLETED'\"\n [class.bg-emerald-50]=\"document.recognitionStatus === 'COMPLETED'\"\n [class.text-emerald-700]=\"document.recognitionStatus === 'COMPLETED'\"\n >\n {{ getRecognitionStatusLabel(document) }}\n </span>\n }\n </div>\n </div>\n </article>\n }\n </div>\n }\n </div>\n\n <div class=\"flex flex-col gap-3 rounded-2xl border border-dashed border-slate-200 bg-slate-50 px-4 py-4\">\n <div>\n <h4 class=\"text-sm font-semibold text-slate-900\">\u0417\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0435\u043D\u043D\u044F \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442\u0456\u0432 \u0443 \u0444\u0430\u0439\u043B\u043E\u0432\u0435 \u0441\u0445\u043E\u0432\u0438\u0449\u0435</h4>\n <p class=\"mt-1 text-xs text-slate-500\">\u0414\u043E\u0434\u0430\u0439\u0442\u0435 \u043D\u043E\u0432\u0456 \u0444\u0430\u0439\u043B\u0438 \u0434\u043E \u043A\u0430\u0440\u0442\u043A\u0438 \u0431\u0435\u0437\u043F\u043E\u0441\u0435\u0440\u0435\u0434\u043D\u044C\u043E \u0442\u0443\u0442.</p>\n </div>\n\n <div class=\"flex w-full flex-col gap-2 sm:w-auto\">\n <ui-button variant=\"secondary\" size=\"sm\" (click)=\"openUploadModal()\">\n <lucide-icon [img]=\"plusIcon\"></lucide-icon>\n \u0417\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0438\u0442\u0438 \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442\n </ui-button>\n </div>\n </div>\n</div>\n\n<app-documents-text-modal\n [isOpen]=\"isTextOpen()\"\n [document]=\"textTarget()\"\n (closed)=\"closeTextModal()\"\n (recognizeRequested)=\"textTarget() && recognizeText(textTarget()!)\"\n></app-documents-text-modal>\n\n<app-documents-upload-modal\n [isOpen]=\"isUploadOpen()\"\n [entity]=\"entity()\"\n [id]=\"id()\"\n (closed)=\"closeUploadModal()\"\n (saved)=\"reloadDocuments()\"\n></app-documents-upload-modal>\n\n<app-documents-image-preview-modal\n [isOpen]=\"isImagePreviewOpen()\"\n [document]=\"imagePreviewTarget()\"\n (closed)=\"closeImagePreview()\"\n></app-documents-image-preview-modal>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: LucideAngularModule }, { kind: "component", type: i1$2.LucideAngularComponent, selector: "lucide-angular, lucide-icon, i-lucide, span-lucide", inputs: ["class", "name", "img", "color", "absoluteStrokeWidth", "size", "strokeWidth"] }, { kind: "component", type: UiButtonComponent, selector: "ui-button", inputs: ["variant", "size", "disabled", "fullWidth", "type", "withShadow", "icon", "iconOnly", "ariaLabel"] }, { kind: "component", type: DocumentsUploadModalComponent, selector: "app-documents-upload-modal", inputs: ["isOpen", "entity", "id"], outputs: ["closed", "saved"] }, { kind: "component", type: DocumentsTextModalComponent, selector: "app-documents-text-modal", inputs: ["isOpen", "document"], outputs: ["closed", "recognizeRequested"] }, { kind: "component", type: DocumentsImagePreviewModalComponent, selector: "app-documents-image-preview-modal", inputs: ["isOpen", "document"], outputs: ["closed"] }] });
|
|
1484
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.20", type: DocumentsComponent, isStandalone: true, selector: "app-documents", inputs: { entity: { classPropertyName: "entity", publicName: "entity", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"space-y-4\">\n <div>\n @if (isLoading()) {\n <div class=\"rounded-2xl border border-dashed border-slate-200 bg-slate-50 px-4 py-8 text-center text-sm text-slate-500\">\n \u0417\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0435\u043D\u043D\u044F \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442\u0456\u0432...\n </div>\n } @else if (documents().length === 0) {\n <div class=\"rounded-2xl border border-dashed border-slate-200 bg-slate-50 px-4 py-8 text-center text-sm text-slate-500\">\n \u0414\u043E\u043A\u0443\u043C\u0435\u043D\u0442\u0438 \u0432\u0456\u0434\u0441\u0443\u0442\u043D\u0456\n </div>\n } @else {\n <div class=\"grid gap-3\">\n @for (document of documents(); track trackDocument($index, document)) {\n <article class=\"flex flex-col gap-4 rounded-2xl border border-slate-200 bg-white p-4 shadow-sm sm:flex-row sm:items-center sm:justify-between\">\n @if (isImage(document)) {\n <button\n type=\"button\"\n class=\"block shrink-0 overflow-hidden rounded-2xl border border-slate-200 bg-slate-50 transition hover:border-slate-300 hover:bg-slate-100 h-28 w-40\"\n (click)=\"openImagePreview(document)\"\n >\n <img [src]=\"getImagePreviewUrl(document)\" [alt]=\"document.name || '\u0414\u043E\u043A\u0443\u043C\u0435\u043D\u0442'\" loading=\"lazy\" decoding=\"async\" class=\"h-full w-full object-cover\">\n </button>\n } @else {\n <a\n class=\"block shrink-0 overflow-hidden rounded-2xl border border-slate-200 bg-slate-50 transition hover:border-slate-300 hover:bg-slate-100\"\n [href]=\"document.downloadUrl\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n [class.h-28]=\"isPdf(document)\"\n [class.w-40]=\"isPdf(document)\"\n [class.h-20]=\"!isPdf(document)\"\n [class.w-24]=\"!isPdf(document)\"\n >\n @if (isPdf(document)) {\n <div class=\"flex h-full w-full flex-col items-center justify-center gap-2 px-3 py-4 text-slate-700\">\n <div class=\"flex h-11 w-11 items-center justify-center rounded-2xl bg-white text-slate-900 shadow-sm\">\n <lucide-icon [img]=\"fileIcon\"></lucide-icon>\n </div>\n <span class=\"text-xs font-semibold uppercase tracking-[0.18em] text-slate-600\">PDF</span>\n </div>\n } @else {\n <div class=\"flex h-full w-full flex-col items-center justify-center gap-2 px-3 py-4 text-slate-700\">\n <div class=\"flex h-11 w-11 items-center justify-center rounded-2xl bg-white text-slate-900 shadow-sm\">\n <lucide-icon [img]=\"fileIcon\"></lucide-icon>\n </div>\n <span class=\"text-[10px] font-semibold uppercase tracking-[0.18em] text-slate-500\">\n {{ getFileBadge(document) }}\n </span>\n </div>\n }\n </a>\n }\n\n <div class=\"min-w-0 flex-1\">\n <div class=\"flex flex-col gap-2 lg:flex-row lg:items-start lg:justify-between\">\n <div class=\"min-w-0\">\n <div class=\"flex flex-wrap items-center gap-2\">\n <h4 class=\"truncate text-sm font-semibold text-slate-900\">\n {{ document.name || '\u0411\u0435\u0437 \u043D\u0430\u0437\u0432\u0438' }}\n </h4>\n @if (getDocumentTypeLabel(document)) {\n <span class=\"inline-flex shrink-0 items-center rounded-full border border-sky-200 bg-sky-50 px-2.5 py-1 text-[10px] font-semibold uppercase tracking-[0.18em] text-sky-700\">\n {{ getDocumentTypeLabel(document) }}\n </span>\n }\n </div>\n <p class=\"mt-1 text-xs text-slate-500\">\n {{ formatSize(document.size) }} \u00B7 {{ document.type }}\n </p>\n </div>\n\n <div class=\"flex flex-wrap items-center gap-2\">\n <ui-button variant=\"ghost\" size=\"sm\" (click)=\"openTextModal(document)\">\n <lucide-icon [img]=\"eyeIcon\"></lucide-icon>\n \u041F\u0435\u0440\u0435\u0433\u043B\u044F\u0434\n </ui-button>\n @if (isImage(document)) {\n <ui-button variant=\"ghost\" size=\"sm\" [disabled]=\"isDocumentRecognizing(document) || isRecognitionPending(document) || isRecognitionCompleted(document)\" (click)=\"recognizeText(document)\">\n <lucide-icon [img]=\"sparkleIcon\"></lucide-icon>\n {{ isDocumentRecognizing(document) ? '\u0420\u043E\u0437\u043F\u0456\u0437\u043D\u0430\u0432\u0430\u043D\u043D\u044F...' : '\u0420\u043E\u0437\u043F\u0456\u0437\u043D\u0430\u0442\u0438 \u0434\u0430\u043D\u0456' }}\n </ui-button>\n }\n @if (isAdmin()) {\n <ui-button variant=\"ghost\" size=\"sm\" [disabled]=\"deletingDocumentId() === document.id\" (click)=\"removeDocument(document)\">\n <lucide-icon [img]=\"deleteIcon\"></lucide-icon>\n {{ deletingDocumentId() === document.id ? '\u0412\u0438\u0434\u0430\u043B\u0435\u043D\u043D\u044F...' : '\u0412\u0438\u0434\u0430\u043B\u0438\u0442\u0438' }}\n </ui-button>\n }\n </div>\n </div>\n\n <div class=\"mt-3 flex flex-wrap items-center justify-between gap-2\">\n <a\n class=\"inline-flex items-center rounded-lg border border-slate-200 bg-white px-3 py-2 text-sm font-medium text-slate-700 transition hover:border-slate-300 hover:bg-slate-50\"\n [href]=\"document.downloadUrl\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n \u0417\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0438\u0442\u0438\n </a>\n @if (isImage(document) && getRecognitionStatusLabel(document)) {\n <span\n class=\"inline-flex items-center rounded-full border px-2.5 py-1 text-[10px] font-semibold uppercase tracking-[0.12em]\"\n [class.border-amber-200]=\"isRecognitionPending(document)\"\n [class.bg-amber-50]=\"isRecognitionPending(document)\"\n [class.text-amber-700]=\"isRecognitionPending(document)\"\n [class.border-rose-200]=\"document.recognitionStatus === 'FAILED'\"\n [class.bg-rose-50]=\"document.recognitionStatus === 'FAILED'\"\n [class.text-rose-700]=\"document.recognitionStatus === 'FAILED'\"\n [class.border-emerald-200]=\"document.recognitionStatus === 'COMPLETED'\"\n [class.bg-emerald-50]=\"document.recognitionStatus === 'COMPLETED'\"\n [class.text-emerald-700]=\"document.recognitionStatus === 'COMPLETED'\"\n >\n {{ getRecognitionStatusLabel(document) }}\n </span>\n }\n </div>\n </div>\n </article>\n }\n </div>\n }\n </div>\n\n <div class=\"flex flex-col gap-3 rounded-2xl border border-dashed border-slate-200 bg-slate-50 px-4 py-4\">\n <div>\n <h4 class=\"text-sm font-semibold text-slate-900\">\u0417\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0435\u043D\u043D\u044F \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442\u0456\u0432 \u0443 \u0444\u0430\u0439\u043B\u043E\u0432\u0435 \u0441\u0445\u043E\u0432\u0438\u0449\u0435</h4>\n <p class=\"mt-1 text-xs text-slate-500\">\u0414\u043E\u0434\u0430\u0439\u0442\u0435 \u043D\u043E\u0432\u0456 \u0444\u0430\u0439\u043B\u0438 \u0434\u043E \u043A\u0430\u0440\u0442\u043A\u0438 \u0431\u0435\u0437\u043F\u043E\u0441\u0435\u0440\u0435\u0434\u043D\u044C\u043E \u0442\u0443\u0442.</p>\n </div>\n\n <div class=\"flex w-full flex-col gap-2 sm:w-auto\">\n <ui-button variant=\"secondary\" size=\"sm\" (click)=\"openUploadModal()\">\n <lucide-icon [img]=\"plusIcon\"></lucide-icon>\n \u0417\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0438\u0442\u0438 \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442\n </ui-button>\n </div>\n </div>\n</div>\n\n<app-documents-text-modal\n [isOpen]=\"isTextOpen()\"\n [document]=\"textTarget()\"\n (closed)=\"closeTextModal()\"\n (recognizeRequested)=\"textTarget() && recognizeText(textTarget()!)\"\n></app-documents-text-modal>\n\n<app-documents-upload-modal\n [isOpen]=\"isUploadOpen()\"\n [entity]=\"entity()\"\n [id]=\"id()\"\n (closed)=\"closeUploadModal()\"\n (saved)=\"reloadDocuments()\"\n></app-documents-upload-modal>\n\n<app-documents-image-preview-modal\n [isOpen]=\"isImagePreviewOpen()\"\n [document]=\"imagePreviewTarget()\"\n (closed)=\"closeImagePreview()\"\n></app-documents-image-preview-modal>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: LucideAngularModule }, { kind: "component", type: i1.LucideAngularComponent, selector: "lucide-angular, lucide-icon, i-lucide, span-lucide", inputs: ["class", "name", "img", "color", "absoluteStrokeWidth", "size", "strokeWidth"] }, { kind: "component", type: UiButtonComponent, selector: "ui-button", inputs: ["variant", "size", "disabled", "fullWidth", "type", "withShadow", "icon", "iconOnly", "ariaLabel"] }, { kind: "component", type: DocumentsUploadModalComponent, selector: "app-documents-upload-modal", inputs: ["isOpen", "entity", "id"], outputs: ["closed", "saved"] }, { kind: "component", type: DocumentsTextModalComponent, selector: "app-documents-text-modal", inputs: ["isOpen", "document"], outputs: ["closed", "recognizeRequested"] }, { kind: "component", type: DocumentsImagePreviewModalComponent, selector: "app-documents-image-preview-modal", inputs: ["isOpen", "document"], outputs: ["closed"] }] });
|
|
1432
1485
|
}
|
|
1433
1486
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: DocumentsComponent, decorators: [{
|
|
1434
1487
|
type: Component,
|
|
@@ -1591,7 +1644,7 @@ class HistoryTabComponent {
|
|
|
1591
1644
|
: operation.changes || [];
|
|
1592
1645
|
}
|
|
1593
1646
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: HistoryTabComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1594
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.20", type: HistoryTabComponent, isStandalone: true, selector: "app-history-tab", inputs: { entity: { classPropertyName: "entity", publicName: "entity", isSignal: true, isRequired: false, transformFunction: null }, primaryEntity: { classPropertyName: "primaryEntity", publicName: "primaryEntity", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, entityLabels: { classPropertyName: "entityLabels", publicName: "entityLabels", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<section class=\"space-y-4\">\n @if (isLoading()) {\n <div class=\"rounded-2xl border border-slate-200 bg-white p-6 text-sm text-slate-500\">\n \u0417\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0435\u043D\u043D\u044F \u0456\u0441\u0442\u043E\u0440\u0456\u0457\u2026\n </div>\n } @else if (hasError()) {\n <div class=\"rounded-2xl border border-red-200 bg-red-50 p-6 text-sm text-red-700\">\n \u041D\u0435 \u0432\u0434\u0430\u043B\u043E\u0441\u044F \u0437\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0438\u0442\u0438 \u0456\u0441\u0442\u043E\u0440\u0456\u044E \u0437\u043C\u0456\u043D.\n </div>\n } @else if (!operations().length) {\n <div class=\"rounded-2xl border border-slate-200 bg-white p-6 text-sm text-slate-500\">\n \u0406\u0441\u0442\u043E\u0440\u0456\u044F \u0437\u043C\u0456\u043D \u043F\u043E\u043A\u0438 \u043F\u043E\u0440\u043E\u0436\u043D\u044F.\n </div>\n } @else {\n <div class=\"space-y-3\">\n @for (operation of operations(); track operation.id) {\n <article class=\"rounded-2xl border border-slate-200 bg-white p-5 shadow-sm\">\n <div class=\"flex flex-wrap items-start justify-between gap-3\">\n <div>\n <h3 class=\"text-sm font-semibold text-slate-900\">{{ operation.description }}</h3>\n @if (operation.actorName) {\n <p class=\"mt-1 text-sm text-slate-500\">{{ operation.actorName }}</p>\n }\n </div>\n <time class=\"text-xs text-slate-400\" [dateTime]=\"operation.createdAt\">\n {{ operation.createdAt | date: 'dd.MM.yyyy HH:mm' }}\n </time>\n </div>\n\n @if (getPrimaryChanges(operation).length) {\n <div class=\"mt-4 space-y-2 border-t border-slate-100 pt-4\">\n @for (change of getPrimaryChanges(operation); track change.entityId + change.entityType) {\n @for (field of (change.changedFields | keyvalue); track field.key) {\n <div class=\"flex flex-wrap items-center gap-2 text-sm\">\n <span class=\"font-medium text-slate-700\">{{ field.value.label }}</span>\n @if (isImageField(field.value)) {\n @if (getImageUrls(field.value.from.value).length) {\n <div class=\"flex flex-wrap items-center gap-2\">\n @for (url of getImageUrls(field.value.from.value); track url; let imageIndex = $index) {\n <button type=\"button\" class=\"h-14 w-14 overflow-hidden rounded-lg border border-slate-200 bg-slate-100\" (click)=\"openImageViewer(getImageUrls(field.value.from.value))\" [attr.aria-label]=\"'\u0412\u0456\u0434\u043A\u0440\u0438\u0442\u0438 \u0441\u0442\u0430\u0440\u0435 \u0444\u043E\u0442\u043E ' + (imageIndex + 1)\">\n <img [src]=\"url\" [alt]=\"'\u0421\u0442\u0430\u0440\u0435 \u0444\u043E\u0442\u043E ' + (imageIndex + 1)\" class=\"h-full w-full object-cover\" />\n </button>\n }\n </div>\n } @else {\n <span class=\"font-medium text-slate-900\">\u041D\u0435 \u0437\u0430\u0434\u0430\u043D\u043E</span>\n }\n } @else if (isFileField(field.value)) {\n @if (field.value.from.url) {\n <a class=\"font-medium text-sky-700 underline\" [href]=\"field.value.from.url\" target=\"_blank\" rel=\"noopener noreferrer\">\u0417\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0438\u0442\u0438 \u0444\u0430\u0439\u043B</a>\n } @else {\n <span class=\"text-slate-400\">\u041D\u0435 \u0437\u0430\u0434\u0430\u043D\u043E</span>\n }\n } @else if (isHtmlField(field.value)) {\n @if (field.value.from.display !== '\u041D\u0435 \u0437\u0430\u0434\u0430\u043D\u043E') {\n <div class=\"prose prose-sm max-w-none text-slate-500\" [innerHTML]=\"field.value.from.display\"></div>\n } @else {\n <span class=\"text-slate-400\">\u041D\u0435 \u0437\u0430\u0434\u0430\u043D\u043E</span>\n }\n } @else {\n @if (field.value.from.url) {\n <a class=\"font-medium text-sky-700 underline\" [routerLink]=\"field.value.from.url\">{{ field.value.from.display }}</a>\n } @else {\n <span class=\"font-medium text-slate-900\">{{ field.value.from.display }}</span>\n }\n }\n <span class=\"text-slate-400\">\u2192</span>\n @if (isImageField(field.value)) {\n @if (getImageUrls(field.value.to.value).length) {\n <div class=\"flex flex-wrap items-center gap-2\">\n @for (url of getImageUrls(field.value.to.value); track url; let imageIndex = $index) {\n <button type=\"button\" class=\"h-14 w-14 overflow-hidden rounded-lg border border-slate-200 bg-slate-100\" (click)=\"openImageViewer(getImageUrls(field.value.to.value))\" [attr.aria-label]=\"'\u0412\u0456\u0434\u043A\u0440\u0438\u0442\u0438 \u043D\u043E\u0432\u0435 \u0444\u043E\u0442\u043E ' + (imageIndex + 1)\">\n <img [src]=\"url\" [alt]=\"'\u041D\u043E\u0432\u0435 \u0444\u043E\u0442\u043E ' + (imageIndex + 1)\" class=\"h-full w-full object-cover\" />\n </button>\n }\n </div>\n } @else {\n <span class=\"font-medium text-slate-900\">\u041D\u0435 \u0437\u0430\u0434\u0430\u043D\u043E</span>\n }\n } @else if (isFileField(field.value)) {\n @if (field.value.to.url) {\n <a class=\"font-medium text-sky-700 underline\" [href]=\"field.value.to.url\" target=\"_blank\" rel=\"noopener noreferrer\">\u0417\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0438\u0442\u0438 \u0444\u0430\u0439\u043B</a>\n } @else {\n <span class=\"font-medium text-slate-900\">\u041D\u0435 \u0437\u0430\u0434\u0430\u043D\u043E</span>\n }\n } @else if (isHtmlField(field.value)) {\n @if (field.value.to.display !== '\u041D\u0435 \u0437\u0430\u0434\u0430\u043D\u043E') {\n <div class=\"prose prose-sm max-w-none text-slate-900\" [innerHTML]=\"field.value.to.display\"></div>\n } @else {\n <span class=\"font-medium text-slate-900\">\u041D\u0435 \u0437\u0430\u0434\u0430\u043D\u043E</span>\n }\n } @else {\n @if (field.value.to.url) {\n <a class=\"font-medium text-sky-700 underline\" [routerLink]=\"field.value.to.url\">{{ field.value.to.display }}</a>\n } @else {\n <span class=\"font-medium text-slate-900\">{{ field.value.to.display }}</span>\n }\n }\n </div>\n }\n }\n </div>\n }\n\n @if (getRelatedChanges(operation).length) {\n <div class=\"mt-4 border-t border-slate-100 pt-4\">\n <button\n type=\"button\"\n class=\"flex w-full items-center justify-between gap-3 text-left text-sm text-slate-600\"\n [attr.aria-expanded]=\"isExpanded(operation.id)\"\n (click)=\"toggleOperation(operation.id)\"\n >\n <span>\n \u0414\u0435\u0442\u0430\u043B\u0456 \u0437\u043C\u0456\u043D:\n <span class=\"font-medium text-slate-900\">{{ getAffectedSummary(operation) }}</span>\n </span>\n <span class=\"text-lg leading-none text-slate-400\">{{ isExpanded(operation.id) ? '\u2212' : '+' }}</span>\n </button>\n\n @if (isExpanded(operation.id)) {\n <div class=\"mt-4 space-y-4\">\n @for (change of getRelatedChanges(operation); track change.entityId + change.entityType) {\n <div class=\"rounded-xl bg-slate-50 p-3\">\n <p class=\"mb-2 text-xs font-semibold uppercase tracking-[0.16em] text-slate-500\">\n {{ getEntityLabel(change.entityType) }} \u00B7 {{ getActionLabel(change.action) }}\n </p>\n @for (field of (change.changedFields | keyvalue); track field.key) {\n <div class=\"flex flex-wrap items-center gap-2 text-sm\">\n <span class=\"font-medium text-slate-700\">{{ field.value.label }}</span>\n @if (isImageField(field.value)) {\n @if (getImageUrls(field.value.from.value).length) {\n <div class=\"flex flex-wrap items-center gap-2\">\n @for (url of getImageUrls(field.value.from.value); track url; let imageIndex = $index) {\n <button type=\"button\" class=\"h-14 w-14 overflow-hidden rounded-lg border border-slate-200 bg-white\" (click)=\"openImageViewer(getImageUrls(field.value.from.value))\" [attr.aria-label]=\"'\u0412\u0456\u0434\u043A\u0440\u0438\u0442\u0438 \u0441\u0442\u0430\u0440\u0435 \u0444\u043E\u0442\u043E ' + (imageIndex + 1)\">\n <img [src]=\"url\" [alt]=\"'\u0421\u0442\u0430\u0440\u0435 \u0444\u043E\u0442\u043E ' + (imageIndex + 1)\" class=\"h-full w-full object-cover\" />\n </button>\n }\n </div>\n } @else {\n <span class=\"text-slate-400\">\u041D\u0435 \u0437\u0430\u0434\u0430\u043D\u043E</span>\n }\n } @else if (isFileField(field.value)) {\n @if (field.value.from.url) {\n <a class=\"text-sky-700 underline\" [href]=\"field.value.from.url\" target=\"_blank\" rel=\"noopener noreferrer\">\u0417\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0438\u0442\u0438 \u0444\u0430\u0439\u043B</a>\n } @else {\n <span class=\"text-slate-400\">\u041D\u0435 \u0437\u0430\u0434\u0430\u043D\u043E</span>\n }\n } @else if (isHtmlField(field.value)) {\n @if (field.value.from.display !== '\u041D\u0435 \u0437\u0430\u0434\u0430\u043D\u043E') {\n <div class=\"prose prose-sm max-w-none text-slate-500\" [innerHTML]=\"field.value.from.display\"></div>\n } @else {\n <span class=\"text-slate-400\">\u041D\u0435 \u0437\u0430\u0434\u0430\u043D\u043E</span>\n }\n } @else {\n @if (field.value.from.url) {\n <a class=\"text-sky-700 underline\" [routerLink]=\"field.value.from.url\">{{ field.value.from.display }}</a>\n } @else {\n <span class=\"text-slate-400\">{{ field.value.from.display }}</span>\n }\n }\n <span class=\"text-slate-400\">\u2192</span>\n @if (isImageField(field.value)) {\n @if (getImageUrls(field.value.to.value).length) {\n <div class=\"flex flex-wrap items-center gap-2\">\n @for (url of getImageUrls(field.value.to.value); track url; let imageIndex = $index) {\n <button type=\"button\" class=\"h-14 w-14 overflow-hidden rounded-lg border border-slate-200 bg-white\" (click)=\"openImageViewer(getImageUrls(field.value.to.value))\" [attr.aria-label]=\"'\u0412\u0456\u0434\u043A\u0440\u0438\u0442\u0438 \u043D\u043E\u0432\u0435 \u0444\u043E\u0442\u043E ' + (imageIndex + 1)\">\n <img [src]=\"url\" [alt]=\"'\u041D\u043E\u0432\u0435 \u0444\u043E\u0442\u043E ' + (imageIndex + 1)\" class=\"h-full w-full object-cover\" />\n </button>\n }\n </div>\n } @else {\n <span class=\"font-medium text-slate-900\">\u041D\u0435 \u0437\u0430\u0434\u0430\u043D\u043E</span>\n }\n } @else if (isFileField(field.value)) {\n @if (field.value.to.url) {\n <a class=\"font-medium text-sky-700 underline\" [href]=\"field.value.to.url\" target=\"_blank\" rel=\"noopener noreferrer\">\u0417\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0438\u0442\u0438 \u0444\u0430\u0439\u043B</a>\n } @else {\n <span class=\"font-medium text-slate-900\">\u041D\u0435 \u0437\u0430\u0434\u0430\u043D\u043E</span>\n }\n } @else if (isHtmlField(field.value)) {\n @if (field.value.to.display !== '\u041D\u0435 \u0437\u0430\u0434\u0430\u043D\u043E') {\n <div class=\"prose prose-sm max-w-none text-slate-900\" [innerHTML]=\"field.value.to.display\"></div>\n } @else {\n <span class=\"font-medium text-slate-900\">\u041D\u0435 \u0437\u0430\u0434\u0430\u043D\u043E</span>\n }\n } @else {\n @if (field.value.to.url) {\n <a class=\"font-medium text-sky-700 underline\" [routerLink]=\"field.value.to.url\">{{ field.value.to.display }}</a>\n } @else {\n <span class=\"font-medium text-slate-900\">{{ field.value.to.display }}</span>\n }\n }\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n }\n </article>\n }\n </div>\n }\n</section>\n\n@if (isViewerOpen()) {\n <app-images-viewer [urls]=\"viewerUrls()\" (closed)=\"closeImageViewer()\" />\n}\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: ImagesViewerComponent, selector: "app-images-viewer", inputs: ["urls", "initialIndex"], outputs: ["closed"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "pipe", type: i1.DatePipe, name: "date" }, { kind: "pipe", type: i1.KeyValuePipe, name: "keyvalue" }] });
|
|
1647
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.20", type: HistoryTabComponent, isStandalone: true, selector: "app-history-tab", inputs: { entity: { classPropertyName: "entity", publicName: "entity", isSignal: true, isRequired: false, transformFunction: null }, primaryEntity: { classPropertyName: "primaryEntity", publicName: "primaryEntity", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, entityLabels: { classPropertyName: "entityLabels", publicName: "entityLabels", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<section class=\"space-y-4\">\n @if (isLoading()) {\n <div class=\"rounded-2xl border border-slate-200 bg-white p-6 text-sm text-slate-500\">\n \u0417\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0435\u043D\u043D\u044F \u0456\u0441\u0442\u043E\u0440\u0456\u0457\u2026\n </div>\n } @else if (hasError()) {\n <div class=\"rounded-2xl border border-red-200 bg-red-50 p-6 text-sm text-red-700\">\n \u041D\u0435 \u0432\u0434\u0430\u043B\u043E\u0441\u044F \u0437\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0438\u0442\u0438 \u0456\u0441\u0442\u043E\u0440\u0456\u044E \u0437\u043C\u0456\u043D.\n </div>\n } @else if (!operations().length) {\n <div class=\"rounded-2xl border border-slate-200 bg-white p-6 text-sm text-slate-500\">\n \u0406\u0441\u0442\u043E\u0440\u0456\u044F \u0437\u043C\u0456\u043D \u043F\u043E\u043A\u0438 \u043F\u043E\u0440\u043E\u0436\u043D\u044F.\n </div>\n } @else {\n <div class=\"space-y-3\">\n @for (operation of operations(); track operation.id) {\n <article class=\"rounded-2xl border border-slate-200 bg-white p-5 shadow-sm\">\n <div class=\"flex flex-wrap items-start justify-between gap-3\">\n <div>\n <h3 class=\"text-sm font-semibold text-slate-900\">{{ operation.description }}</h3>\n @if (operation.actorName) {\n <p class=\"mt-1 text-sm text-slate-500\">{{ operation.actorName }}</p>\n }\n </div>\n <time class=\"text-xs text-slate-400\" [dateTime]=\"operation.createdAt\">\n {{ operation.createdAt | date: 'dd.MM.yyyy HH:mm' }}\n </time>\n </div>\n\n @if (getPrimaryChanges(operation).length) {\n <div class=\"mt-4 space-y-2 border-t border-slate-100 pt-4\">\n @for (change of getPrimaryChanges(operation); track change.entityId + change.entityType) {\n @for (field of (change.changedFields | keyvalue); track field.key) {\n <div class=\"flex flex-wrap items-center gap-2 text-sm\">\n <span class=\"font-medium text-slate-700\">{{ field.value.label }}</span>\n @if (isImageField(field.value)) {\n @if (getImageUrls(field.value.from.value).length) {\n <div class=\"flex flex-wrap items-center gap-2\">\n @for (url of getImageUrls(field.value.from.value); track url; let imageIndex = $index) {\n <button type=\"button\" class=\"h-14 w-14 overflow-hidden rounded-lg border border-slate-200 bg-slate-100\" (click)=\"openImageViewer(getImageUrls(field.value.from.value))\" [attr.aria-label]=\"'\u0412\u0456\u0434\u043A\u0440\u0438\u0442\u0438 \u0441\u0442\u0430\u0440\u0435 \u0444\u043E\u0442\u043E ' + (imageIndex + 1)\">\n <img [src]=\"url\" [alt]=\"'\u0421\u0442\u0430\u0440\u0435 \u0444\u043E\u0442\u043E ' + (imageIndex + 1)\" class=\"h-full w-full object-cover\" />\n </button>\n }\n </div>\n } @else {\n <span class=\"font-medium text-slate-900\">\u041D\u0435 \u0437\u0430\u0434\u0430\u043D\u043E</span>\n }\n } @else if (isFileField(field.value)) {\n @if (field.value.from.url) {\n <a class=\"font-medium text-sky-700 underline\" [href]=\"field.value.from.url\" target=\"_blank\" rel=\"noopener noreferrer\">\u0417\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0438\u0442\u0438 \u0444\u0430\u0439\u043B</a>\n } @else {\n <span class=\"text-slate-400\">\u041D\u0435 \u0437\u0430\u0434\u0430\u043D\u043E</span>\n }\n } @else if (isHtmlField(field.value)) {\n @if (field.value.from.display !== '\u041D\u0435 \u0437\u0430\u0434\u0430\u043D\u043E') {\n <div class=\"prose prose-sm max-w-none text-slate-500\" [innerHTML]=\"field.value.from.display\"></div>\n } @else {\n <span class=\"text-slate-400\">\u041D\u0435 \u0437\u0430\u0434\u0430\u043D\u043E</span>\n }\n } @else {\n @if (field.value.from.url) {\n <a class=\"font-medium text-sky-700 underline\" [routerLink]=\"field.value.from.url\">{{ field.value.from.display }}</a>\n } @else {\n <span class=\"font-medium text-slate-900\">{{ field.value.from.display }}</span>\n }\n }\n <span class=\"text-slate-400\">\u2192</span>\n @if (isImageField(field.value)) {\n @if (getImageUrls(field.value.to.value).length) {\n <div class=\"flex flex-wrap items-center gap-2\">\n @for (url of getImageUrls(field.value.to.value); track url; let imageIndex = $index) {\n <button type=\"button\" class=\"h-14 w-14 overflow-hidden rounded-lg border border-slate-200 bg-slate-100\" (click)=\"openImageViewer(getImageUrls(field.value.to.value))\" [attr.aria-label]=\"'\u0412\u0456\u0434\u043A\u0440\u0438\u0442\u0438 \u043D\u043E\u0432\u0435 \u0444\u043E\u0442\u043E ' + (imageIndex + 1)\">\n <img [src]=\"url\" [alt]=\"'\u041D\u043E\u0432\u0435 \u0444\u043E\u0442\u043E ' + (imageIndex + 1)\" class=\"h-full w-full object-cover\" />\n </button>\n }\n </div>\n } @else {\n <span class=\"font-medium text-slate-900\">\u041D\u0435 \u0437\u0430\u0434\u0430\u043D\u043E</span>\n }\n } @else if (isFileField(field.value)) {\n @if (field.value.to.url) {\n <a class=\"font-medium text-sky-700 underline\" [href]=\"field.value.to.url\" target=\"_blank\" rel=\"noopener noreferrer\">\u0417\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0438\u0442\u0438 \u0444\u0430\u0439\u043B</a>\n } @else {\n <span class=\"font-medium text-slate-900\">\u041D\u0435 \u0437\u0430\u0434\u0430\u043D\u043E</span>\n }\n } @else if (isHtmlField(field.value)) {\n @if (field.value.to.display !== '\u041D\u0435 \u0437\u0430\u0434\u0430\u043D\u043E') {\n <div class=\"prose prose-sm max-w-none text-slate-900\" [innerHTML]=\"field.value.to.display\"></div>\n } @else {\n <span class=\"font-medium text-slate-900\">\u041D\u0435 \u0437\u0430\u0434\u0430\u043D\u043E</span>\n }\n } @else {\n @if (field.value.to.url) {\n <a class=\"font-medium text-sky-700 underline\" [routerLink]=\"field.value.to.url\">{{ field.value.to.display }}</a>\n } @else {\n <span class=\"font-medium text-slate-900\">{{ field.value.to.display }}</span>\n }\n }\n </div>\n }\n }\n </div>\n }\n\n @if (getRelatedChanges(operation).length) {\n <div class=\"mt-4 border-t border-slate-100 pt-4\">\n <button\n type=\"button\"\n class=\"flex w-full items-center justify-between gap-3 text-left text-sm text-slate-600\"\n [attr.aria-expanded]=\"isExpanded(operation.id)\"\n (click)=\"toggleOperation(operation.id)\"\n >\n <span>\n \u0414\u0435\u0442\u0430\u043B\u0456 \u0437\u043C\u0456\u043D:\n <span class=\"font-medium text-slate-900\">{{ getAffectedSummary(operation) }}</span>\n </span>\n <span class=\"text-lg leading-none text-slate-400\">{{ isExpanded(operation.id) ? '\u2212' : '+' }}</span>\n </button>\n\n @if (isExpanded(operation.id)) {\n <div class=\"mt-4 space-y-4\">\n @for (change of getRelatedChanges(operation); track change.entityId + change.entityType) {\n <div class=\"rounded-xl bg-slate-50 p-3\">\n <p class=\"mb-2 text-xs font-semibold uppercase tracking-[0.16em] text-slate-500\">\n {{ getEntityLabel(change.entityType) }} \u00B7 {{ getActionLabel(change.action) }}\n </p>\n @for (field of (change.changedFields | keyvalue); track field.key) {\n <div class=\"flex flex-wrap items-center gap-2 text-sm\">\n <span class=\"font-medium text-slate-700\">{{ field.value.label }}</span>\n @if (isImageField(field.value)) {\n @if (getImageUrls(field.value.from.value).length) {\n <div class=\"flex flex-wrap items-center gap-2\">\n @for (url of getImageUrls(field.value.from.value); track url; let imageIndex = $index) {\n <button type=\"button\" class=\"h-14 w-14 overflow-hidden rounded-lg border border-slate-200 bg-white\" (click)=\"openImageViewer(getImageUrls(field.value.from.value))\" [attr.aria-label]=\"'\u0412\u0456\u0434\u043A\u0440\u0438\u0442\u0438 \u0441\u0442\u0430\u0440\u0435 \u0444\u043E\u0442\u043E ' + (imageIndex + 1)\">\n <img [src]=\"url\" [alt]=\"'\u0421\u0442\u0430\u0440\u0435 \u0444\u043E\u0442\u043E ' + (imageIndex + 1)\" class=\"h-full w-full object-cover\" />\n </button>\n }\n </div>\n } @else {\n <span class=\"text-slate-400\">\u041D\u0435 \u0437\u0430\u0434\u0430\u043D\u043E</span>\n }\n } @else if (isFileField(field.value)) {\n @if (field.value.from.url) {\n <a class=\"text-sky-700 underline\" [href]=\"field.value.from.url\" target=\"_blank\" rel=\"noopener noreferrer\">\u0417\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0438\u0442\u0438 \u0444\u0430\u0439\u043B</a>\n } @else {\n <span class=\"text-slate-400\">\u041D\u0435 \u0437\u0430\u0434\u0430\u043D\u043E</span>\n }\n } @else if (isHtmlField(field.value)) {\n @if (field.value.from.display !== '\u041D\u0435 \u0437\u0430\u0434\u0430\u043D\u043E') {\n <div class=\"prose prose-sm max-w-none text-slate-500\" [innerHTML]=\"field.value.from.display\"></div>\n } @else {\n <span class=\"text-slate-400\">\u041D\u0435 \u0437\u0430\u0434\u0430\u043D\u043E</span>\n }\n } @else {\n @if (field.value.from.url) {\n <a class=\"text-sky-700 underline\" [routerLink]=\"field.value.from.url\">{{ field.value.from.display }}</a>\n } @else {\n <span class=\"text-slate-400\">{{ field.value.from.display }}</span>\n }\n }\n <span class=\"text-slate-400\">\u2192</span>\n @if (isImageField(field.value)) {\n @if (getImageUrls(field.value.to.value).length) {\n <div class=\"flex flex-wrap items-center gap-2\">\n @for (url of getImageUrls(field.value.to.value); track url; let imageIndex = $index) {\n <button type=\"button\" class=\"h-14 w-14 overflow-hidden rounded-lg border border-slate-200 bg-white\" (click)=\"openImageViewer(getImageUrls(field.value.to.value))\" [attr.aria-label]=\"'\u0412\u0456\u0434\u043A\u0440\u0438\u0442\u0438 \u043D\u043E\u0432\u0435 \u0444\u043E\u0442\u043E ' + (imageIndex + 1)\">\n <img [src]=\"url\" [alt]=\"'\u041D\u043E\u0432\u0435 \u0444\u043E\u0442\u043E ' + (imageIndex + 1)\" class=\"h-full w-full object-cover\" />\n </button>\n }\n </div>\n } @else {\n <span class=\"font-medium text-slate-900\">\u041D\u0435 \u0437\u0430\u0434\u0430\u043D\u043E</span>\n }\n } @else if (isFileField(field.value)) {\n @if (field.value.to.url) {\n <a class=\"font-medium text-sky-700 underline\" [href]=\"field.value.to.url\" target=\"_blank\" rel=\"noopener noreferrer\">\u0417\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0438\u0442\u0438 \u0444\u0430\u0439\u043B</a>\n } @else {\n <span class=\"font-medium text-slate-900\">\u041D\u0435 \u0437\u0430\u0434\u0430\u043D\u043E</span>\n }\n } @else if (isHtmlField(field.value)) {\n @if (field.value.to.display !== '\u041D\u0435 \u0437\u0430\u0434\u0430\u043D\u043E') {\n <div class=\"prose prose-sm max-w-none text-slate-900\" [innerHTML]=\"field.value.to.display\"></div>\n } @else {\n <span class=\"font-medium text-slate-900\">\u041D\u0435 \u0437\u0430\u0434\u0430\u043D\u043E</span>\n }\n } @else {\n @if (field.value.to.url) {\n <a class=\"font-medium text-sky-700 underline\" [routerLink]=\"field.value.to.url\">{{ field.value.to.display }}</a>\n } @else {\n <span class=\"font-medium text-slate-900\">{{ field.value.to.display }}</span>\n }\n }\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n }\n </article>\n }\n </div>\n }\n</section>\n\n@if (isViewerOpen()) {\n <app-images-viewer [urls]=\"viewerUrls()\" (closed)=\"closeImageViewer()\" />\n}\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: ImagesViewerComponent, selector: "app-images-viewer", inputs: ["urls", "initialIndex"], outputs: ["closed"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "pipe", type: i1$1.DatePipe, name: "date" }, { kind: "pipe", type: i1$1.KeyValuePipe, name: "keyvalue" }] });
|
|
1595
1648
|
}
|
|
1596
1649
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: HistoryTabComponent, decorators: [{
|
|
1597
1650
|
type: Component,
|
|
@@ -1602,5 +1655,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImpo
|
|
|
1602
1655
|
* Generated bundle index. Do not edit.
|
|
1603
1656
|
*/
|
|
1604
1657
|
|
|
1605
|
-
export { CARD_ACTION_COMPLETED, CARD_ACTION_FINISHED, CARD_ACTION_REQUESTED, CARD_ACTION_STARTED, CardComponent, CardCurrentUserService, CardDictionaryService, CardImageService, CardSectionHeaderComponent, DocumentsComponent, ENTITY_UPDATED, HistoryTabComponent, ImagesViewerComponent,
|
|
1658
|
+
export { CARD_ACTION_COMPLETED, CARD_ACTION_FINISHED, CARD_ACTION_REQUESTED, CARD_ACTION_STARTED, CardComponent, CardCurrentUserService, CardDictionaryService, CardImageService, CardSectionHeaderComponent, DocumentsComponent, ENTITY_UPDATED, HistoryTabComponent, ImagesViewerComponent, TableComponent, UI_CARD_API_BASE_URL, UI_CARD_TAB_REGISTRY, UI_ENTITY_CARD_HOST, UiEntityDocumentsService, cardApiParams, cardApiResourceUrl, normalizeCardConfig, resolveCardConfig };
|
|
1606
1659
|
//# sourceMappingURL=maro-tech-card.mjs.map
|