@sdcorejs/angular 19.0.10 → 19.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +32 -23
- package/assets/STYLE-GUIDE.md +58 -72
- package/assets/scss/core/_inline-edit.scss +1 -1
- package/assets/scss/core/color.scss +56 -45
- package/assets/scss/core/form.scss +21 -9
- package/assets/scss/core/utilities/_base.scss +3 -3
- package/assets/scss/core/utilities/_border.scss +1 -1
- package/assets/scss/themes/default.scss +46 -32
- package/assets/scss/themes/material-theme.scss +8 -87
- package/components/badge/src/badge.component.d.ts +5 -16
- package/components/button/src/button.component.d.ts +6 -4
- package/components/form-generic/src/components/form-builder/components/group/attribute/group-attribute.component.d.ts +1 -0
- package/components/form-generic/src/components/form-builder/form-builder.component.d.ts +9 -1
- package/components/inform/src/inform.component.d.ts +2 -8
- package/components/modal/src/modal.component.d.ts +1 -1
- package/components/modal-resizable/src/modal-resizable.component.d.ts +1 -1
- package/components/query-bar/src/query-bar.component.d.ts +1 -0
- package/components/query-builder/src/query-builder.component.d.ts +6 -6
- package/components/query-builder/src/query-builder.model.d.ts +3 -3
- package/components/section/src/section.component.d.ts +1 -5
- package/components/side-drawer/src/side-drawer.component.d.ts +1 -1
- package/components/table/src/components/command/desktop-command.component.d.ts +0 -1
- package/components/table/src/models/table-command.model.d.ts +3 -3
- package/components/table/src/models/table-option-selector.model.d.ts +5 -5
- package/components/table/src/services/table-filter/table-filter.model.d.ts +0 -2
- package/components/tree/src/tree.model.d.ts +3 -2
- package/fesm2022/sdcorejs-angular-components-anchor.mjs +5 -6
- package/fesm2022/sdcorejs-angular-components-anchor.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-autoid-inspector.mjs +10 -11
- package/fesm2022/sdcorejs-angular-components-autoid-inspector.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-badge.mjs +15 -13
- package/fesm2022/sdcorejs-angular-components-badge.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-button.mjs +10 -11
- package/fesm2022/sdcorejs-angular-components-button.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-code-editor.mjs +4 -5
- package/fesm2022/sdcorejs-angular-components-code-editor.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-document-builder.mjs +3 -3
- package/fesm2022/sdcorejs-angular-components-document-builder.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-editor.mjs +4 -5
- package/fesm2022/sdcorejs-angular-components-editor.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-form-generic.mjs +117 -46
- package/fesm2022/sdcorejs-angular-components-form-generic.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-history.mjs +2 -2
- package/fesm2022/sdcorejs-angular-components-history.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-import-excel.mjs +5 -7
- package/fesm2022/sdcorejs-angular-components-import-excel.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-inform.mjs +5 -15
- package/fesm2022/sdcorejs-angular-components-inform.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-mini-editor.mjs +2 -2
- package/fesm2022/sdcorejs-angular-components-mini-editor.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-modal-resizable.mjs +5 -6
- package/fesm2022/sdcorejs-angular-components-modal-resizable.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-modal.mjs +25 -7
- package/fesm2022/sdcorejs-angular-components-modal.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-org-chart.mjs +4 -5
- package/fesm2022/sdcorejs-angular-components-org-chart.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-preview.mjs +7 -8
- package/fesm2022/sdcorejs-angular-components-preview.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-query-bar.mjs +27 -28
- package/fesm2022/sdcorejs-angular-components-query-bar.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-query-builder.mjs +5 -5
- package/fesm2022/sdcorejs-angular-components-query-builder.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-section.mjs +7 -12
- package/fesm2022/sdcorejs-angular-components-section.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-side-drawer.mjs +3 -4
- package/fesm2022/sdcorejs-angular-components-side-drawer.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-splitter.mjs +2 -2
- package/fesm2022/sdcorejs-angular-components-splitter.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-stepper.mjs +3 -4
- package/fesm2022/sdcorejs-angular-components-stepper.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-tab-router.mjs +5 -6
- package/fesm2022/sdcorejs-angular-components-tab-router.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-tab.mjs +3 -4
- package/fesm2022/sdcorejs-angular-components-tab.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-table.mjs +28 -32
- package/fesm2022/sdcorejs-angular-components-table.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-tree.mjs +6 -8
- package/fesm2022/sdcorejs-angular-components-tree.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-upload-file.mjs +8 -9
- package/fesm2022/sdcorejs-angular-components-upload-file.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-components-view.mjs +2 -2
- package/fesm2022/sdcorejs-angular-components-view.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-directives.mjs +1 -1
- package/fesm2022/sdcorejs-angular-directives.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-autocomplete.mjs +5 -7
- package/fesm2022/sdcorejs-angular-forms-autocomplete.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-checkbox.mjs +2 -2
- package/fesm2022/sdcorejs-angular-forms-checkbox.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-chip-calendar.mjs +7 -9
- package/fesm2022/sdcorejs-angular-forms-chip-calendar.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-chip.mjs +5 -7
- package/fesm2022/sdcorejs-angular-forms-chip.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-date-range.mjs +7 -9
- package/fesm2022/sdcorejs-angular-forms-date-range.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-date.mjs +6 -8
- package/fesm2022/sdcorejs-angular-forms-date.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-datetime.mjs +465 -343
- package/fesm2022/sdcorejs-angular-forms-datetime.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-inline-text.mjs +4 -5
- package/fesm2022/sdcorejs-angular-forms-inline-text.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-input-number.mjs +5 -7
- package/fesm2022/sdcorejs-angular-forms-input-number.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-input.mjs +5 -7
- package/fesm2022/sdcorejs-angular-forms-input.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-label.mjs +4 -5
- package/fesm2022/sdcorejs-angular-forms-label.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-radio.mjs +2 -4
- package/fesm2022/sdcorejs-angular-forms-radio.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-select.mjs +6 -8
- package/fesm2022/sdcorejs-angular-forms-select.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-switch.mjs +2 -2
- package/fesm2022/sdcorejs-angular-forms-switch.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-forms-textarea.mjs +5 -7
- package/fesm2022/sdcorejs-angular-forms-textarea.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-modules-icon.mjs +523 -0
- package/fesm2022/sdcorejs-angular-modules-icon.mjs.map +1 -0
- package/fesm2022/sdcorejs-angular-modules-layout.mjs +29 -29
- package/fesm2022/sdcorejs-angular-modules-layout.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-modules.mjs +1 -0
- package/fesm2022/sdcorejs-angular-modules.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-services-confirm.mjs +85 -5
- package/fesm2022/sdcorejs-angular-services-confirm.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-services-excel.mjs +22 -14
- package/fesm2022/sdcorejs-angular-services-excel.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-services-loading.mjs +1 -1
- package/fesm2022/sdcorejs-angular-services-loading.mjs.map +1 -1
- package/fesm2022/sdcorejs-angular-utilities-models.mjs +3 -2
- package/fesm2022/sdcorejs-angular-utilities-models.mjs.map +1 -1
- package/forms/datetime/src/datetime.component.d.ts +4 -4
- package/forms/datetime/src/material-datetime/index.d.ts +14 -0
- package/forms/datetime/src/material-datetime/lib/core/date-adapter.d.ts +10 -0
- package/forms/datetime/src/material-datetime/lib/core/date-formats.d.ts +18 -0
- package/forms/datetime/src/material-datetime/lib/core/default-options.d.ts +13 -0
- package/forms/datetime/src/material-datetime/lib/datetime-picker/datetime-picker-actions.component.d.ts +5 -0
- package/forms/datetime/src/material-datetime/lib/datetime-picker/datetime-picker-apply.directive.d.ts +7 -0
- package/forms/datetime/src/material-datetime/lib/datetime-picker/datetime-picker-cancel.directive.d.ts +7 -0
- package/forms/datetime/src/material-datetime/lib/datetime-picker/datetime-picker-input.directive.d.ts +24 -0
- package/forms/datetime/src/material-datetime/lib/datetime-picker/datetime-picker-now.directive.d.ts +7 -0
- package/forms/datetime/src/material-datetime/lib/datetime-picker/datetime-picker-toggle.directive.d.ts +8 -0
- package/forms/datetime/src/material-datetime/lib/datetime-picker/datetime-picker.component.d.ts +39 -0
- package/forms/datetime/src/material-datetime/lib/native/native-date-adapter.d.ts +15 -0
- package/forms/datetime/src/material-datetime/lib/native/native-date-formats.d.ts +2 -0
- package/forms/datetime/src/material-datetime/lib/native/provide-native.d.ts +3 -0
- package/forms/datetime/src/material-datetime/lib/time-spinner/time-spinner.component.d.ts +31 -0
- package/modules/icon/index.d.ts +1 -0
- package/modules/icon/src/icon.component.d.ts +101 -0
- package/modules/icon/src/icon.model.d.ts +59 -0
- package/modules/icon/src/icon.provider.d.ts +33 -0
- package/modules/icon/src/index.d.ts +3 -0
- package/modules/index.d.ts +1 -0
- package/package.json +10 -5
- package/services/confirm/src/lib/components/dialog-confirm/dialog-confirm.component.d.ts +17 -0
- package/services/confirm/src/lib/confirm.service.d.ts +32 -1
- package/services/excel/src/lib/spreadsheet-formula-injection.util.d.ts +5 -0
- package/utilities/models/src/icon.model.d.ts +4 -2
- package/forms/datetime/src/popup/sd-datetime-picker.component.d.ts +0 -37
- package/forms/datetime/src/popup/sd-time-spinner.component.d.ts +0 -41
|
@@ -3,9 +3,8 @@ import { CommonModule } from '@angular/common';
|
|
|
3
3
|
import * as i0 from '@angular/core';
|
|
4
4
|
import { inject, ElementRef, DestroyRef, input, computed, output, signal, effect, afterNextRender, HostListener, ChangeDetectionStrategy, Component, InjectionToken, viewChild, viewChildren } from '@angular/core';
|
|
5
5
|
import { Utilities } from '@sdcorejs/utils/fns';
|
|
6
|
-
import * as i2 from '@angular/material/icon';
|
|
7
|
-
import { MatIconModule } from '@angular/material/icon';
|
|
8
6
|
import { TranslatePipe } from '@sdcorejs/angular/i18n';
|
|
7
|
+
import { SdIcon } from '@sdcorejs/angular/modules/icon';
|
|
9
8
|
import { FormsModule } from '@angular/forms';
|
|
10
9
|
import * as pdfjsLib from 'pdfjs-dist';
|
|
11
10
|
|
|
@@ -639,12 +638,12 @@ class SdPreviewImage {
|
|
|
639
638
|
};
|
|
640
639
|
}
|
|
641
640
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: SdPreviewImage, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
642
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.21", type: SdPreviewImage, isStandalone: true, selector: "sd-preview-image", inputs: { autoIdInput: { classPropertyName: "autoIdInput", publicName: "autoId", isSignal: true, isRequired: false, transformFunction: null }, items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, thumbnailPosition: { classPropertyName: "thumbnailPosition", publicName: "thumbnailPosition", isSignal: true, isRequired: false, transformFunction: null }, showToolbar: { classPropertyName: "showToolbar", publicName: "showToolbar", isSignal: true, isRequired: false, transformFunction: null }, downloadable: { classPropertyName: "downloadable", publicName: "downloadable", isSignal: true, isRequired: false, transformFunction: null }, zoomable: { classPropertyName: "zoomable", publicName: "zoomable", isSignal: true, isRequired: false, transformFunction: null }, loop: { classPropertyName: "loop", publicName: "loop", isSignal: true, isRequired: false, transformFunction: null }, startIndex: { classPropertyName: "startIndex", publicName: "startIndex", isSignal: true, isRequired: false, transformFunction: null }, theme: { classPropertyName: "theme", publicName: "theme", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { close: "close", activeIndexChange: "activeIndexChange", download: "download", imageError: "imageError" }, host: { attributes: { "tabindex": "0" }, listeners: { "keydown": "onKeyDown($event)" }, properties: { "attr.data-theme": "theme()", "class.sd-preview-image-host--fullscreen": "isFullscreen()" }, classAttribute: "sd-preview-image-host" }, ngImport: i0, template: "<!--\n Self-contained viewer shell. Component sizes to its parent (width:100%;\n height:100%) \u2014 consumer decides whether to wrap in <sd-modal>,\n <sd-side-drawer>, a page section, or use inline.\n-->\n<div [ngClass]=\"shellClass()\" role=\"region\">\n @let _stage = stage();\n @let _images = images();\n @let _thumbnailPosition = thumbnailPosition();\n @let _activeIndex = activeIndex();\n @let _activeImage = activeImage();\n\n <!-- ============================================\n HEADER BAR\n ============================================ -->\n @if (_stage !== 'empty' && _activeImage; as img) {\n <div class=\"sd-preview-header\">\n <div class=\"sd-preview-header__left\">\n <mat-icon class=\"sd-preview-header__icon\" fontIcon=\"image\"></mat-icon>\n <div class=\"sd-preview-header__meta\">\n <div class=\"sd-preview-header__name T14M\" [title]=\"img.name\">{{ img.name }}</div>\n @if (img.size) {\n <div class=\"sd-preview-header__sub\">\n {{ formatBytes(img.size) }}\n </div>\n }\n </div>\n </div>\n\n @if (_images.length > 1) {\n <div class=\"sd-preview-header__counter\" aria-live=\"polite\">\n {{ _activeIndex + 1 }} / {{ _images.length }}\n </div>\n }\n </div>\n } @else if (title()) {\n <!-- Optional override title even on empty state -->\n <div class=\"sd-preview-header sd-preview-header--simple\">\n <div class=\"sd-preview-header__name T14M\">{{ title() }}</div>\n </div>\n }\n\n <!-- ============================================\n BODY (stage + thumbnails per layout)\n ============================================ -->\n <div class=\"sd-preview-body\">\n\n <!-- STAGE ............................................................... -->\n <div\n class=\"sd-preview-stage\"\n [class.sd-preview-stage--dragging]=\"zoom() > 1\"\n (wheel)=\"onWheel($event)\"\n (pointerdown)=\"onPointerDown($event)\"\n (pointermove)=\"onPointerMove($event)\"\n (pointerup)=\"onPointerUp($event)\"\n (pointercancel)=\"onPointerCancel($event)\"\n (dblclick)=\"onImageDblClick()\">\n\n @switch (_stage) {\n @case ('empty') {\n <div class=\"sd-preview-status\">\n <div class=\"sd-preview-status__tile sd-preview-status__tile--muted\">\n <mat-icon fontIcon=\"image_not_supported\"></mat-icon>\n </div>\n <div class=\"sd-preview-status__title\">\n {{ 'core.component.preview-image.no-image' | translate }}\n </div>\n <div class=\"sd-preview-status__hint\">\n {{ 'core.component.preview-image.no-image-hint' | translate }}\n </div>\n </div>\n }\n @case ('loading') {\n <div class=\"sd-preview-status\">\n <div class=\"sd-preview-spinner\" role=\"progressbar\"></div>\n <div class=\"sd-preview-status__hint\">\n {{ 'core.component.preview-image.loading' | translate }}\n </div>\n </div>\n }\n @default {\n @if (_activeImage; as img) {\n @if (img.loading) {\n <!-- Per-image loading (retry inline) \u2014 gi\u1EEF nav/dots/thumbs visible -->\n <div class=\"sd-preview-image-loading\" role=\"status\"\n [attr.aria-label]=\"'core.component.preview-image.loading' | translate\">\n <div class=\"sd-preview-spinner\"></div>\n <div class=\"sd-preview-status__hint\">\n {{ 'core.component.preview-image.loading' | translate }}\n </div>\n </div>\n } @else if (img.error) {\n <!-- Inline broken-image placeholder \u2014 gi\u1EEF nav/dots/thumbs visible -->\n <div class=\"sd-preview-image-broken\" role=\"img\"\n [attr.aria-label]=\"'core.component.preview-image.load-error' | translate\">\n <div class=\"sd-preview-status__tile sd-preview-status__tile--danger\">\n <mat-icon fontIcon=\"broken_image\"></mat-icon>\n </div>\n <div class=\"sd-preview-status__title\">\n {{ 'core.component.preview-image.load-error' | translate }}\n </div>\n <div class=\"sd-preview-status__hint\">\n {{ 'core.component.preview-image.load-error-hint' | translate }}\n </div>\n <button type=\"button\" class=\"sd-preview-btn-primary\" (click)=\"retryActive()\" [attr.data-autoId]=\"autoIdRetry()\">\n <mat-icon fontIcon=\"refresh\"></mat-icon>\n <span>{{ 'core.component.preview-image.retry' | translate }}</span>\n </button>\n </div>\n } @else {\n <img\n class=\"sd-preview-image\"\n [src]=\"img.blobUrl || img.url\"\n [alt]=\"img.alt || img.name\"\n [style.transform]=\"imageTransform()\"\n (error)=\"onImageError()\"\n draggable=\"false\" />\n\n @if (img.caption) {\n <div class=\"sd-preview-caption\">{{ img.caption }}</div>\n }\n }\n }\n }\n }\n\n <!-- KEYBOARD HINT ............................................. -->\n @if (_stage === 'ready' && _images.length > 1) {\n <div class=\"sd-preview-kbd-hint\" aria-hidden=\"true\">\n <span class=\"sd-preview-kbd\">\u2190</span>\n <span class=\"sd-preview-kbd\">\u2192</span>\n <span class=\"sd-preview-kbd-text\">{{ 'core.component.preview-image.kbd-hint-nav' | translate }}</span>\n </div>\n }\n\n <!-- NAV ARROWS ............................................. -->\n @if (_stage === 'ready' && _images.length > 1) {\n <button\n type=\"button\"\n class=\"sd-preview-nav sd-preview-nav--prev\"\n [disabled]=\"!hasPrev()\"\n [attr.aria-label]=\"'core.component.preview-image.prev' | translate\"\n [attr.data-autoId]=\"autoIdPrev()\"\n (click)=\"updateCurrentImage(-1)\">\n <mat-icon fontIcon=\"chevron_left\"></mat-icon>\n </button>\n <button\n type=\"button\"\n class=\"sd-preview-nav sd-preview-nav--next\"\n [disabled]=\"!hasNext()\"\n [attr.aria-label]=\"'core.component.preview-image.next' | translate\"\n [attr.data-autoId]=\"autoIdNext()\"\n (click)=\"updateCurrentImage(1)\">\n <mat-icon fontIcon=\"chevron_right\"></mat-icon>\n </button>\n }\n\n <!-- DOTS INDICATOR ......................................... -->\n @if (_thumbnailPosition === 'dots' && _stage === 'ready' && _images.length > 1) {\n <div class=\"sd-preview-dots\" role=\"tablist\">\n @for (img of _images; track img.id; let i = $index) {\n <button\n type=\"button\"\n class=\"sd-preview-dot\"\n [class.sd-preview-dot--active]=\"i === _activeIndex\"\n [attr.aria-label]=\"'core.component.preview-image.next' | translate\"\n [attr.data-autoId]=\"autoIdDot(i)\"\n (click)=\"onClickThumbnailImage(i)\"></button>\n }\n </div>\n }\n\n <!-- FLOATING TOOLBAR ....................................... -->\n @if (showToolbar() && _stage === 'ready') {\n <div class=\"sd-preview-toolbar\" role=\"toolbar\">\n @if (zoomable()) {\n <button\n type=\"button\"\n class=\"sd-preview-toolbar__btn\"\n [attr.aria-label]=\"'core.component.preview-image.zoom-out' | translate\"\n [attr.title]=\"'core.component.preview-image.zoom-out' | translate\"\n [attr.data-autoId]=\"autoIdZoomOut()\"\n (click)=\"zoomOut()\">\n <mat-icon fontIcon=\"zoom_out\"></mat-icon>\n </button>\n <span class=\"sd-preview-toolbar__readout\">{{ zoomPercent() }}%</span>\n <button\n type=\"button\"\n class=\"sd-preview-toolbar__btn\"\n [attr.aria-label]=\"'core.component.preview-image.zoom-in' | translate\"\n [attr.title]=\"'core.component.preview-image.zoom-in' | translate\"\n [attr.data-autoId]=\"autoIdZoomIn()\"\n (click)=\"zoomIn()\">\n <mat-icon fontIcon=\"zoom_in\"></mat-icon>\n </button>\n <span class=\"sd-preview-toolbar__divider\"></span>\n }\n <button\n type=\"button\"\n class=\"sd-preview-toolbar__btn sd-preview-toolbar__btn--with-label\"\n [attr.aria-label]=\"'core.component.preview-image.fit' | translate\"\n [attr.data-autoId]=\"autoIdFit()\"\n (click)=\"fitToScreen()\">\n <mat-icon fontIcon=\"fit_screen\"></mat-icon>\n <span class=\"sd-preview-toolbar__btn-label\">{{ 'core.component.preview-image.fit' | translate }}</span>\n </button>\n <button\n type=\"button\"\n class=\"sd-preview-toolbar__btn\"\n [attr.aria-label]=\"'core.component.preview-image.rotate' | translate\"\n [attr.title]=\"'core.component.preview-image.rotate' | translate\"\n [attr.data-autoId]=\"autoIdRotate()\"\n (click)=\"rotate('right')\">\n <mat-icon fontIcon=\"rotate_right\"></mat-icon>\n </button>\n <span class=\"sd-preview-toolbar__divider\"></span>\n @if (downloadable()) {\n <button\n type=\"button\"\n class=\"sd-preview-toolbar__btn\"\n [attr.aria-label]=\"'core.component.preview-image.download' | translate\"\n [attr.title]=\"'core.component.preview-image.download' | translate\"\n [attr.data-autoId]=\"autoIdDownload()\"\n (click)=\"downloadCurrent()\">\n <mat-icon fontIcon=\"download\"></mat-icon>\n </button>\n }\n <button\n type=\"button\"\n class=\"sd-preview-toolbar__btn\"\n [attr.aria-label]=\"'core.component.preview-image.fullscreen' | translate\"\n [attr.title]=\"'core.component.preview-image.fullscreen' | translate\"\n [attr.data-autoId]=\"autoIdFullscreen()\"\n (click)=\"toggleFullscreen()\">\n <mat-icon [fontIcon]=\"isFullscreen() ? 'fullscreen_exit' : 'fullscreen'\"></mat-icon>\n </button>\n </div>\n }\n </div>\n\n <!-- THUMBNAIL STRIP .................................................... -->\n @if (\n _stage === 'ready' &&\n _images.length > 1 &&\n _thumbnailPosition !== 'none' &&\n _thumbnailPosition !== 'dots'\n ) {\n <div\n class=\"sd-preview-thumbs\"\n [class.sd-preview-thumbs--vertical]=\"_thumbnailPosition === 'left' || _thumbnailPosition === 'right'\">\n @for (img of _images; track trackByImage($index, img); let i = $index) {\n <button\n type=\"button\"\n class=\"sd-preview-thumb\"\n [class.sd-preview-thumb--active]=\"i === _activeIndex\"\n [id]=\"'sd-preview-thumb-' + i\"\n [attr.aria-label]=\"img.name\"\n [attr.aria-current]=\"i === _activeIndex ? 'true' : null\"\n [attr.data-autoId]=\"autoIdThumb(i)\"\n (click)=\"onClickThumbnailImage(i)\">\n @if (img.error) {\n <div class=\"sd-preview-thumb__broken\">\n <mat-icon fontIcon=\"broken_image\"></mat-icon>\n </div>\n } @else {\n <img class=\"sd-preview-thumb__img\" [src]=\"img.blobUrl || img.url\" [alt]=\"img.alt || img.name\" />\n }\n <span class=\"sd-preview-thumb__badge\">{{ i + 1 }}</span>\n @if (_thumbnailPosition === 'right' || _thumbnailPosition === 'left') {\n <span class=\"sd-preview-thumb__name\" [title]=\"img.name\">{{ img.name }}</span>\n }\n </button>\n }\n </div>\n }\n </div>\n</div>\n", styles: [":host{--sd-preview-shell-bg: #0d0e10;--sd-preview-shell-bg-2: #17191c;--sd-preview-panel-bg: rgba(15, 17, 20, .92);--sd-preview-divider: rgba(255, 255, 255, .08);--sd-preview-fg: #f5f6f7;--sd-preview-fg-muted: rgba(255, 255, 255, .6);--sd-preview-fg-subtle: rgba(255, 255, 255, .4);--sd-preview-accent: var(--sd-primary, #3b82f6);--sd-preview-accent-soft: rgba(59, 130, 246, .2);--sd-preview-danger: var(--sd-error, #ef4444);--sd-preview-success: var(--sd-success, #22c55e);--sd-preview-danger-soft: rgba(239, 68, 68, .1);--sd-preview-danger-border: rgba(239, 68, 68, .3);--sd-preview-hover-bg: rgba(255, 255, 255, .1);display:block;width:100%;height:100%;min-height:320px;outline:none}:host([data-theme=light]){--sd-preview-shell-bg: #f7f8fa;--sd-preview-shell-bg-2: #ffffff;--sd-preview-panel-bg: rgba(255, 255, 255, .94);--sd-preview-divider: rgba(0, 0, 0, .08);--sd-preview-fg: #1a1c1f;--sd-preview-fg-muted: rgba(0, 0, 0, .62);--sd-preview-fg-subtle: rgba(0, 0, 0, .42);--sd-preview-accent: var(--sd-primary, #1f49bf);--sd-preview-accent-soft: rgba(31, 73, 191, .18);--sd-preview-danger: var(--sd-error, #ef4444);--sd-preview-success: var(--sd-success, #22c55e);--sd-preview-danger-soft: rgba(239, 68, 68, .08);--sd-preview-danger-border: rgba(239, 68, 68, .25);--sd-preview-hover-bg: rgba(0, 0, 0, .06)}.sd-preview-shell{position:relative;display:flex;flex-direction:column;width:100%;height:100%;background:var(--sd-preview-shell-bg);color:var(--sd-preview-fg);border-radius:12px;overflow:hidden;box-shadow:0 30px 80px #0000008c,0 0 0 1px #ffffff0a;font-family:inherit}.sd-preview-shell--fullscreen{height:100vh;width:100vw;border-radius:0}:host(.sd-preview-image-host--fullscreen){width:100vw;height:100vh;min-height:0}.sd-preview-header{position:relative;display:flex;align-items:center;height:68px;padding:0 16px;background:var(--sd-preview-panel-bg);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border-bottom:1px solid var(--sd-preview-divider);flex:0 0 auto;z-index:2}.sd-preview-header--simple{justify-content:space-between}.sd-preview-header__left{display:flex;align-items:center;gap:12px;min-width:0;flex:1 1 auto}.sd-preview-header__icon{color:var(--sd-preview-fg-muted);font-size:20px;width:20px;height:20px}.sd-preview-header__meta{display:flex;flex-direction:column;min-width:0}.sd-preview-header__name{color:var(--sd-preview-fg);max-width:360px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sd-preview-header__sub{font-size:12px;color:var(--sd-preview-fg-muted);margin-top:2px}.sd-preview-header__counter{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);font-size:13px;font-weight:500;color:var(--sd-preview-fg-muted);background:#ffffff14;padding:4px 12px;border-radius:999px;font-variant-numeric:tabular-nums}.sd-preview-body{flex:1 1 auto;min-height:0;display:flex;flex-direction:column;overflow:hidden}.sd-preview-shell--pos-bottom .sd-preview-body{flex-direction:column}.sd-preview-shell--pos-top .sd-preview-body{flex-direction:column-reverse}.sd-preview-shell--pos-right .sd-preview-body{flex-direction:row}.sd-preview-shell--pos-left .sd-preview-body{flex-direction:row-reverse}.sd-preview-shell--pos-dots .sd-preview-body,.sd-preview-shell--pos-none .sd-preview-body{flex-direction:column}.sd-preview-stage{position:relative;flex:1 1 auto;min-height:0;min-width:0;background:var(--sd-preview-shell-bg);background-image:linear-gradient(45deg,rgba(255,255,255,.02) 25%,transparent 25%),linear-gradient(-45deg,rgba(255,255,255,.02) 25%,transparent 25%),linear-gradient(45deg,transparent 75%,rgba(255,255,255,.02) 75%),linear-gradient(-45deg,transparent 75%,rgba(255,255,255,.02) 75%);background-size:24px 24px;background-position:0 0,0 12px,12px -12px,-12px 0;display:flex;align-items:center;justify-content:center;overflow:hidden;touch-action:none;-webkit-user-select:none;user-select:none}.sd-preview-stage--dragging{cursor:grab}:host([data-theme=light]) .sd-preview-stage{background-image:linear-gradient(45deg,rgba(0,0,0,.015) 25%,transparent 25%),linear-gradient(-45deg,rgba(0,0,0,.015) 25%,transparent 25%),linear-gradient(45deg,transparent 75%,rgba(0,0,0,.015) 75%),linear-gradient(-45deg,transparent 75%,rgba(0,0,0,.015) 75%)}.sd-preview-image{max-width:100%;max-height:100%;object-fit:contain;transform-origin:center center;transition:transform .2s ease;will-change:transform;pointer-events:none}.sd-preview-caption{position:absolute;bottom:96px;left:50%;transform:translate(-50%);max-width:70%;padding:6px 12px;background:#0f1114b3;color:var(--sd-preview-fg);border-radius:8px;font-size:13px;text-align:center}.sd-preview-image-broken,.sd-preview-image-loading{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:16px;width:100%;max-width:480px;padding:24px;text-align:center}.sd-preview-nav{position:absolute;top:50%;transform:translateY(-50%);width:48px;height:48px;border-radius:999px;border:none;background:#14141699;backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);color:var(--sd-preview-fg);display:flex;align-items:center;justify-content:center;cursor:pointer;box-shadow:0 4px 16px #0000004d;transition:background .12s;z-index:3}.sd-preview-nav mat-icon{font-size:28px;width:28px;height:28px}.sd-preview-nav:hover:not(:disabled){background:#ffffff2e}.sd-preview-nav:focus-visible{outline:2px solid var(--sd-preview-accent);outline-offset:2px}.sd-preview-nav:disabled{background:#ffffff0a;opacity:.35;cursor:not-allowed}.sd-preview-nav--prev{left:16px}.sd-preview-nav--next{right:16px}.sd-preview-toolbar{position:absolute;bottom:24px;left:50%;transform:translate(-50%);display:flex;align-items:center;gap:4px;padding:6px 8px;background:var(--sd-preview-panel-bg);backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px);border:1px solid var(--sd-preview-divider);border-radius:12px;box-shadow:0 8px 30px #0006;z-index:3}.sd-preview-toolbar__btn{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-width:36px;height:36px;padding:0 8px;background:transparent;border:none;color:var(--sd-preview-fg);border-radius:8px;cursor:pointer;transition:background .12s}.sd-preview-toolbar__btn mat-icon{font-size:20px;width:20px;height:20px}.sd-preview-toolbar__btn--with-label{padding:0 12px}.sd-preview-toolbar__btn:hover:not(:disabled){background:var(--sd-preview-hover-bg)}.sd-preview-toolbar__btn:focus-visible{outline:2px solid var(--sd-preview-accent);outline-offset:2px}.sd-preview-toolbar__btn-label{font-size:13px;font-weight:500}.sd-preview-toolbar__readout{min-width:56px;text-align:center;color:var(--sd-preview-fg);font-size:13px;font-weight:500;font-variant-numeric:tabular-nums}.sd-preview-toolbar__divider{width:1px;height:20px;background:var(--sd-preview-divider);margin:0 4px}.sd-preview-thumbs{display:flex;gap:8px;padding:12px;background:var(--sd-preview-shell-bg-2);overflow:auto;flex:0 0 auto;flex-direction:row;height:116px}.sd-preview-thumbs--vertical{flex-direction:column;width:152px;height:100%}.sd-preview-thumb{position:relative;flex:0 0 auto;width:120px;height:76px;padding:0;border:none;background:transparent;border-radius:8px;outline:1px solid var(--sd-preview-divider);outline-offset:-1px;opacity:.7;overflow:hidden;cursor:pointer;transition:opacity .12s,outline-color .12s}.sd-preview-thumb:hover{opacity:1}.sd-preview-thumb--active{opacity:1;outline:2px solid var(--sd-preview-accent);outline-offset:-2px;box-shadow:inset 0 0 0 2px var(--sd-preview-accent)}.sd-preview-thumb:focus-visible{outline:2px solid var(--sd-preview-accent);outline-offset:2px}.sd-preview-thumb__img{width:100%;height:100%;object-fit:cover;display:block;pointer-events:none}.sd-preview-thumb__broken{display:flex;align-items:center;justify-content:center;width:100%;height:100%;background:var(--sd-preview-danger-soft);color:var(--sd-preview-danger);pointer-events:none}.sd-preview-thumb__broken mat-icon{font-size:24px;width:24px;height:24px}.sd-preview-thumb__badge{position:absolute;top:4px;right:4px;background:#000000b3;color:#fff;font-size:10px;font-weight:600;padding:1px 5px;border-radius:4px;pointer-events:none}.sd-preview-thumb__name{position:absolute;left:0;right:0;bottom:0;padding:14px 6px 4px;color:var(--sd-preview-fg);font-size:11px;line-height:1.3;background:linear-gradient(to top,rgba(0,0,0,.85),transparent);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;pointer-events:none}.sd-preview-thumbs--vertical .sd-preview-thumb{width:128px}.sd-preview-dots{position:absolute;bottom:24px;left:50%;transform:translate(-50%);display:flex;align-items:center;gap:6px;z-index:3}.sd-preview-dot{width:8px;height:8px;border:none;background:#ffffff4d;border-radius:999px;padding:0;cursor:pointer;transition:width .18s,background .18s}.sd-preview-dot--active{width:24px;background:#f5f6f7}.sd-preview-dot:focus-visible{outline:2px solid var(--sd-preview-accent);outline-offset:2px}.sd-preview-status{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:16px;text-align:center;padding:24px}.sd-preview-status__tile{width:96px;height:96px;border-radius:24px;display:flex;align-items:center;justify-content:center;border:1px solid var(--sd-preview-divider);background:#ffffff0d}.sd-preview-status__tile mat-icon{font-size:48px;width:48px;height:48px;color:var(--sd-preview-fg-subtle)}.sd-preview-status__tile--muted{background:#ffffff0d;border-color:var(--sd-preview-divider)}.sd-preview-status__tile--danger{background:var(--sd-preview-danger-soft);border-color:var(--sd-preview-danger-border)}.sd-preview-status__tile--danger mat-icon{color:var(--sd-preview-danger)}.sd-preview-status__title{font-size:16px;font-weight:500;color:var(--sd-preview-fg)}.sd-preview-status__hint{font-size:13px;color:var(--sd-preview-fg-muted);max-width:320px}.sd-preview-spinner{width:56px;height:56px;border-radius:999px;border:3px solid rgba(255,255,255,.08);border-top-color:var(--sd-preview-accent);animation:sd-preview-spin .9s linear infinite}@keyframes sd-preview-spin{to{transform:rotate(360deg)}}.sd-preview-btn-primary{display:inline-flex;align-items:center;gap:6px;height:36px;padding:0 16px;background:var(--sd-preview-accent);color:#fff;border:none;border-radius:8px;cursor:pointer;font-size:13px;font-weight:500;transition:filter .12s}.sd-preview-btn-primary mat-icon{font-size:18px;width:18px;height:18px}.sd-preview-btn-primary:hover{filter:brightness(1.1)}.sd-preview-btn-primary:focus-visible{outline:2px solid #fff;outline-offset:2px}.sd-preview-kbd-hint{position:absolute;top:16px;left:16px;display:flex;align-items:center;gap:6px;z-index:2;pointer-events:none}.sd-preview-kbd{display:inline-flex;align-items:center;justify-content:center;min-width:20px;height:20px;padding:0 5px;border:1px solid var(--sd-preview-divider);background:#ffffff0a;color:var(--sd-preview-fg-muted);border-radius:4px;font:500 11px/1 ui-monospace,SFMono-Regular,Menlo,monospace}.sd-preview-kbd-text,.sd-preview-kbd-dot{font-size:11px;color:var(--sd-preview-fg-subtle)}.sd-preview-kbd-dot{margin:0 2px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
641
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.21", type: SdPreviewImage, isStandalone: true, selector: "sd-preview-image", inputs: { autoIdInput: { classPropertyName: "autoIdInput", publicName: "autoId", isSignal: true, isRequired: false, transformFunction: null }, items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, thumbnailPosition: { classPropertyName: "thumbnailPosition", publicName: "thumbnailPosition", isSignal: true, isRequired: false, transformFunction: null }, showToolbar: { classPropertyName: "showToolbar", publicName: "showToolbar", isSignal: true, isRequired: false, transformFunction: null }, downloadable: { classPropertyName: "downloadable", publicName: "downloadable", isSignal: true, isRequired: false, transformFunction: null }, zoomable: { classPropertyName: "zoomable", publicName: "zoomable", isSignal: true, isRequired: false, transformFunction: null }, loop: { classPropertyName: "loop", publicName: "loop", isSignal: true, isRequired: false, transformFunction: null }, startIndex: { classPropertyName: "startIndex", publicName: "startIndex", isSignal: true, isRequired: false, transformFunction: null }, theme: { classPropertyName: "theme", publicName: "theme", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { close: "close", activeIndexChange: "activeIndexChange", download: "download", imageError: "imageError" }, host: { attributes: { "tabindex": "0" }, listeners: { "keydown": "onKeyDown($event)" }, properties: { "attr.data-theme": "theme()", "class.sd-preview-image-host--fullscreen": "isFullscreen()" }, classAttribute: "sd-preview-image-host" }, ngImport: i0, template: "<!--\n Self-contained viewer shell. Component sizes to its parent (width:100%;\n height:100%) \u2014 consumer decides whether to wrap in <sd-modal>,\n <sd-side-drawer>, a page section, or use inline.\n-->\n<div [ngClass]=\"shellClass()\" role=\"region\">\n @let _stage = stage();\n @let _images = images();\n @let _thumbnailPosition = thumbnailPosition();\n @let _activeIndex = activeIndex();\n @let _activeImage = activeImage();\n\n <!-- ============================================\n HEADER BAR\n ============================================ -->\n @if (_stage !== 'empty' && _activeImage; as img) {\n <div class=\"sd-preview-header\">\n <div class=\"sd-preview-header__left\">\n <sd-icon class=\"sd-preview-header__icon\" fontIcon=\"image\"></sd-icon>\n <div class=\"sd-preview-header__meta\">\n <div class=\"sd-preview-header__name T14M\" [title]=\"img.name\">{{ img.name }}</div>\n @if (img.size) {\n <div class=\"sd-preview-header__sub\">\n {{ formatBytes(img.size) }}\n </div>\n }\n </div>\n </div>\n\n @if (_images.length > 1) {\n <div class=\"sd-preview-header__counter\" aria-live=\"polite\">\n {{ _activeIndex + 1 }} / {{ _images.length }}\n </div>\n }\n </div>\n } @else if (title()) {\n <!-- Optional override title even on empty state -->\n <div class=\"sd-preview-header sd-preview-header--simple\">\n <div class=\"sd-preview-header__name T14M\">{{ title() }}</div>\n </div>\n }\n\n <!-- ============================================\n BODY (stage + thumbnails per layout)\n ============================================ -->\n <div class=\"sd-preview-body\">\n\n <!-- STAGE ............................................................... -->\n <div\n class=\"sd-preview-stage\"\n [class.sd-preview-stage--dragging]=\"zoom() > 1\"\n (wheel)=\"onWheel($event)\"\n (pointerdown)=\"onPointerDown($event)\"\n (pointermove)=\"onPointerMove($event)\"\n (pointerup)=\"onPointerUp($event)\"\n (pointercancel)=\"onPointerCancel($event)\"\n (dblclick)=\"onImageDblClick()\">\n\n @switch (_stage) {\n @case ('empty') {\n <div class=\"sd-preview-status\">\n <div class=\"sd-preview-status__tile sd-preview-status__tile--muted\">\n <sd-icon fontIcon=\"image_not_supported\"></sd-icon>\n </div>\n <div class=\"sd-preview-status__title\">\n {{ 'core.component.preview-image.no-image' | translate }}\n </div>\n <div class=\"sd-preview-status__hint\">\n {{ 'core.component.preview-image.no-image-hint' | translate }}\n </div>\n </div>\n }\n @case ('loading') {\n <div class=\"sd-preview-status\">\n <div class=\"sd-preview-spinner\" role=\"progressbar\"></div>\n <div class=\"sd-preview-status__hint\">\n {{ 'core.component.preview-image.loading' | translate }}\n </div>\n </div>\n }\n @default {\n @if (_activeImage; as img) {\n @if (img.loading) {\n <!-- Per-image loading (retry inline) \u2014 gi\u1EEF nav/dots/thumbs visible -->\n <div class=\"sd-preview-image-loading\" role=\"status\"\n [attr.aria-label]=\"'core.component.preview-image.loading' | translate\">\n <div class=\"sd-preview-spinner\"></div>\n <div class=\"sd-preview-status__hint\">\n {{ 'core.component.preview-image.loading' | translate }}\n </div>\n </div>\n } @else if (img.error) {\n <!-- Inline broken-image placeholder \u2014 gi\u1EEF nav/dots/thumbs visible -->\n <div class=\"sd-preview-image-broken\" role=\"img\"\n [attr.aria-label]=\"'core.component.preview-image.load-error' | translate\">\n <div class=\"sd-preview-status__tile sd-preview-status__tile--danger\">\n <sd-icon fontIcon=\"broken_image\"></sd-icon>\n </div>\n <div class=\"sd-preview-status__title\">\n {{ 'core.component.preview-image.load-error' | translate }}\n </div>\n <div class=\"sd-preview-status__hint\">\n {{ 'core.component.preview-image.load-error-hint' | translate }}\n </div>\n <button type=\"button\" class=\"sd-preview-btn-primary\" (click)=\"retryActive()\" [attr.data-autoId]=\"autoIdRetry()\">\n <sd-icon fontIcon=\"refresh\"></sd-icon>\n <span>{{ 'core.component.preview-image.retry' | translate }}</span>\n </button>\n </div>\n } @else {\n <img\n class=\"sd-preview-image\"\n [src]=\"img.blobUrl || img.url\"\n [alt]=\"img.alt || img.name\"\n [style.transform]=\"imageTransform()\"\n (error)=\"onImageError()\"\n draggable=\"false\" />\n\n @if (img.caption) {\n <div class=\"sd-preview-caption\">{{ img.caption }}</div>\n }\n }\n }\n }\n }\n\n <!-- KEYBOARD HINT ............................................. -->\n @if (_stage === 'ready' && _images.length > 1) {\n <div class=\"sd-preview-kbd-hint\" aria-hidden=\"true\">\n <span class=\"sd-preview-kbd\">\u2190</span>\n <span class=\"sd-preview-kbd\">\u2192</span>\n <span class=\"sd-preview-kbd-text\">{{ 'core.component.preview-image.kbd-hint-nav' | translate }}</span>\n </div>\n }\n\n <!-- NAV ARROWS ............................................. -->\n @if (_stage === 'ready' && _images.length > 1) {\n <button\n type=\"button\"\n class=\"sd-preview-nav sd-preview-nav--prev\"\n [disabled]=\"!hasPrev()\"\n [attr.aria-label]=\"'core.component.preview-image.prev' | translate\"\n [attr.data-autoId]=\"autoIdPrev()\"\n (click)=\"updateCurrentImage(-1)\">\n <sd-icon fontIcon=\"chevron_left\"></sd-icon>\n </button>\n <button\n type=\"button\"\n class=\"sd-preview-nav sd-preview-nav--next\"\n [disabled]=\"!hasNext()\"\n [attr.aria-label]=\"'core.component.preview-image.next' | translate\"\n [attr.data-autoId]=\"autoIdNext()\"\n (click)=\"updateCurrentImage(1)\">\n <sd-icon fontIcon=\"chevron_right\"></sd-icon>\n </button>\n }\n\n <!-- DOTS INDICATOR ......................................... -->\n @if (_thumbnailPosition === 'dots' && _stage === 'ready' && _images.length > 1) {\n <div class=\"sd-preview-dots\" role=\"tablist\">\n @for (img of _images; track img.id; let i = $index) {\n <button\n type=\"button\"\n class=\"sd-preview-dot\"\n [class.sd-preview-dot--active]=\"i === _activeIndex\"\n [attr.aria-label]=\"'core.component.preview-image.next' | translate\"\n [attr.data-autoId]=\"autoIdDot(i)\"\n (click)=\"onClickThumbnailImage(i)\"></button>\n }\n </div>\n }\n\n <!-- FLOATING TOOLBAR ....................................... -->\n @if (showToolbar() && _stage === 'ready') {\n <div class=\"sd-preview-toolbar\" role=\"toolbar\">\n @if (zoomable()) {\n <button\n type=\"button\"\n class=\"sd-preview-toolbar__btn\"\n [attr.aria-label]=\"'core.component.preview-image.zoom-out' | translate\"\n [attr.title]=\"'core.component.preview-image.zoom-out' | translate\"\n [attr.data-autoId]=\"autoIdZoomOut()\"\n (click)=\"zoomOut()\">\n <sd-icon fontIcon=\"zoom_out\"></sd-icon>\n </button>\n <span class=\"sd-preview-toolbar__readout\">{{ zoomPercent() }}%</span>\n <button\n type=\"button\"\n class=\"sd-preview-toolbar__btn\"\n [attr.aria-label]=\"'core.component.preview-image.zoom-in' | translate\"\n [attr.title]=\"'core.component.preview-image.zoom-in' | translate\"\n [attr.data-autoId]=\"autoIdZoomIn()\"\n (click)=\"zoomIn()\">\n <sd-icon fontIcon=\"zoom_in\"></sd-icon>\n </button>\n <span class=\"sd-preview-toolbar__divider\"></span>\n }\n <button\n type=\"button\"\n class=\"sd-preview-toolbar__btn sd-preview-toolbar__btn--with-label\"\n [attr.aria-label]=\"'core.component.preview-image.fit' | translate\"\n [attr.data-autoId]=\"autoIdFit()\"\n (click)=\"fitToScreen()\">\n <sd-icon fontIcon=\"fit_screen\"></sd-icon>\n <span class=\"sd-preview-toolbar__btn-label\">{{ 'core.component.preview-image.fit' | translate }}</span>\n </button>\n <button\n type=\"button\"\n class=\"sd-preview-toolbar__btn\"\n [attr.aria-label]=\"'core.component.preview-image.rotate' | translate\"\n [attr.title]=\"'core.component.preview-image.rotate' | translate\"\n [attr.data-autoId]=\"autoIdRotate()\"\n (click)=\"rotate('right')\">\n <sd-icon fontIcon=\"rotate_right\"></sd-icon>\n </button>\n <span class=\"sd-preview-toolbar__divider\"></span>\n @if (downloadable()) {\n <button\n type=\"button\"\n class=\"sd-preview-toolbar__btn\"\n [attr.aria-label]=\"'core.component.preview-image.download' | translate\"\n [attr.title]=\"'core.component.preview-image.download' | translate\"\n [attr.data-autoId]=\"autoIdDownload()\"\n (click)=\"downloadCurrent()\">\n <sd-icon fontIcon=\"download\"></sd-icon>\n </button>\n }\n <button\n type=\"button\"\n class=\"sd-preview-toolbar__btn\"\n [attr.aria-label]=\"'core.component.preview-image.fullscreen' | translate\"\n [attr.title]=\"'core.component.preview-image.fullscreen' | translate\"\n [attr.data-autoId]=\"autoIdFullscreen()\"\n (click)=\"toggleFullscreen()\">\n <sd-icon [fontIcon]=\"isFullscreen() ? 'fullscreen_exit' : 'fullscreen'\"></sd-icon>\n </button>\n </div>\n }\n </div>\n\n <!-- THUMBNAIL STRIP .................................................... -->\n @if (\n _stage === 'ready' &&\n _images.length > 1 &&\n _thumbnailPosition !== 'none' &&\n _thumbnailPosition !== 'dots'\n ) {\n <div\n class=\"sd-preview-thumbs\"\n [class.sd-preview-thumbs--vertical]=\"_thumbnailPosition === 'left' || _thumbnailPosition === 'right'\">\n @for (img of _images; track trackByImage($index, img); let i = $index) {\n <button\n type=\"button\"\n class=\"sd-preview-thumb\"\n [class.sd-preview-thumb--active]=\"i === _activeIndex\"\n [id]=\"'sd-preview-thumb-' + i\"\n [attr.aria-label]=\"img.name\"\n [attr.aria-current]=\"i === _activeIndex ? 'true' : null\"\n [attr.data-autoId]=\"autoIdThumb(i)\"\n (click)=\"onClickThumbnailImage(i)\">\n @if (img.error) {\n <div class=\"sd-preview-thumb__broken\">\n <sd-icon fontIcon=\"broken_image\"></sd-icon>\n </div>\n } @else {\n <img class=\"sd-preview-thumb__img\" [src]=\"img.blobUrl || img.url\" [alt]=\"img.alt || img.name\" />\n }\n <span class=\"sd-preview-thumb__badge\">{{ i + 1 }}</span>\n @if (_thumbnailPosition === 'right' || _thumbnailPosition === 'left') {\n <span class=\"sd-preview-thumb__name\" [title]=\"img.name\">{{ img.name }}</span>\n }\n </button>\n }\n </div>\n }\n </div>\n</div>\n", styles: [":host{--sd-preview-shell-bg: #0d0e10;--sd-preview-shell-bg-2: #17191c;--sd-preview-panel-bg: rgba(15, 17, 20, .92);--sd-preview-divider: rgba(255, 255, 255, .08);--sd-preview-fg: #f5f6f7;--sd-preview-fg-muted: rgba(255, 255, 255, .6);--sd-preview-fg-subtle: rgba(255, 255, 255, .4);--sd-preview-primary: var(--sd-preview-accent, var(--sd-primary, #005cbb));--sd-preview-primary-soft: var(--sd-preview-accent-soft, rgba(0, 92, 187, .18));--sd-preview-danger: var(--sd-error, #ba1a1a);--sd-preview-success: var(--sd-success, #2e7d32);--sd-preview-danger-soft: rgba(186, 26, 26, .1);--sd-preview-danger-border: rgba(186, 26, 26, .3);--sd-preview-hover-bg: rgba(255, 255, 255, .1);display:block;width:100%;height:100%;min-height:320px;outline:none}:host([data-theme=light]){--sd-preview-shell-bg: #f7f8fa;--sd-preview-shell-bg-2: #ffffff;--sd-preview-panel-bg: rgba(255, 255, 255, .94);--sd-preview-divider: rgba(0, 0, 0, .08);--sd-preview-fg: #1a1c1f;--sd-preview-fg-muted: rgba(0, 0, 0, .62);--sd-preview-fg-subtle: rgba(0, 0, 0, .42);--sd-preview-primary: var(--sd-preview-accent, var(--sd-primary, #005cbb));--sd-preview-primary-soft: var(--sd-preview-accent-soft, rgba(0, 92, 187, .16));--sd-preview-danger: var(--sd-error, #ba1a1a);--sd-preview-success: var(--sd-success, #2e7d32);--sd-preview-danger-soft: rgba(186, 26, 26, .08);--sd-preview-danger-border: rgba(186, 26, 26, .25);--sd-preview-hover-bg: rgba(0, 0, 0, .06)}.sd-preview-shell{position:relative;display:flex;flex-direction:column;width:100%;height:100%;background:var(--sd-preview-shell-bg);color:var(--sd-preview-fg);border-radius:12px;overflow:hidden;box-shadow:0 30px 80px #0000008c,0 0 0 1px #ffffff0a;font-family:inherit}.sd-preview-shell--fullscreen{height:100vh;width:100vw;border-radius:0}:host(.sd-preview-image-host--fullscreen){width:100vw;height:100vh;min-height:0}.sd-preview-header{position:relative;display:flex;align-items:center;height:68px;padding:0 16px;background:var(--sd-preview-panel-bg);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border-bottom:1px solid var(--sd-preview-divider);flex:0 0 auto;z-index:2}.sd-preview-header--simple{justify-content:space-between}.sd-preview-header__left{display:flex;align-items:center;gap:12px;min-width:0;flex:1 1 auto}.sd-preview-header__icon{color:var(--sd-preview-fg-muted);font-size:20px;width:20px;height:20px}.sd-preview-header__meta{display:flex;flex-direction:column;min-width:0}.sd-preview-header__name{color:var(--sd-preview-fg);max-width:360px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sd-preview-header__sub{font-size:12px;color:var(--sd-preview-fg-muted);margin-top:2px}.sd-preview-header__counter{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);font-size:13px;font-weight:500;color:var(--sd-preview-fg-muted);background:#ffffff14;padding:4px 12px;border-radius:999px;font-variant-numeric:tabular-nums}.sd-preview-body{flex:1 1 auto;min-height:0;display:flex;flex-direction:column;overflow:hidden}.sd-preview-shell--pos-bottom .sd-preview-body{flex-direction:column}.sd-preview-shell--pos-top .sd-preview-body{flex-direction:column-reverse}.sd-preview-shell--pos-right .sd-preview-body{flex-direction:row}.sd-preview-shell--pos-left .sd-preview-body{flex-direction:row-reverse}.sd-preview-shell--pos-dots .sd-preview-body,.sd-preview-shell--pos-none .sd-preview-body{flex-direction:column}.sd-preview-stage{position:relative;flex:1 1 auto;min-height:0;min-width:0;background:var(--sd-preview-shell-bg);background-image:linear-gradient(45deg,rgba(255,255,255,.02) 25%,transparent 25%),linear-gradient(-45deg,rgba(255,255,255,.02) 25%,transparent 25%),linear-gradient(45deg,transparent 75%,rgba(255,255,255,.02) 75%),linear-gradient(-45deg,transparent 75%,rgba(255,255,255,.02) 75%);background-size:24px 24px;background-position:0 0,0 12px,12px -12px,-12px 0;display:flex;align-items:center;justify-content:center;overflow:hidden;touch-action:none;-webkit-user-select:none;user-select:none}.sd-preview-stage--dragging{cursor:grab}:host([data-theme=light]) .sd-preview-stage{background-image:linear-gradient(45deg,rgba(0,0,0,.015) 25%,transparent 25%),linear-gradient(-45deg,rgba(0,0,0,.015) 25%,transparent 25%),linear-gradient(45deg,transparent 75%,rgba(0,0,0,.015) 75%),linear-gradient(-45deg,transparent 75%,rgba(0,0,0,.015) 75%)}.sd-preview-image{max-width:100%;max-height:100%;object-fit:contain;transform-origin:center center;transition:transform .2s ease;will-change:transform;pointer-events:none}.sd-preview-caption{position:absolute;bottom:96px;left:50%;transform:translate(-50%);max-width:70%;padding:6px 12px;background:#0f1114b3;color:var(--sd-preview-fg);border-radius:8px;font-size:13px;text-align:center}.sd-preview-image-broken,.sd-preview-image-loading{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:16px;width:100%;max-width:480px;padding:24px;text-align:center}.sd-preview-nav{position:absolute;top:50%;transform:translateY(-50%);width:48px;height:48px;border-radius:999px;border:none;background:#14141699;backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);color:var(--sd-preview-fg);display:flex;align-items:center;justify-content:center;cursor:pointer;box-shadow:0 4px 16px #0000004d;transition:background .12s;z-index:3}.sd-preview-nav sd-icon{font-size:28px;width:28px;height:28px}.sd-preview-nav:hover:not(:disabled){background:#ffffff2e}.sd-preview-nav:focus-visible{outline:2px solid var(--sd-preview-primary);outline-offset:2px}.sd-preview-nav:disabled{background:#ffffff0a;opacity:.35;cursor:not-allowed}.sd-preview-nav--prev{left:16px}.sd-preview-nav--next{right:16px}.sd-preview-toolbar{position:absolute;bottom:24px;left:50%;transform:translate(-50%);display:flex;align-items:center;gap:4px;padding:6px 8px;background:var(--sd-preview-panel-bg);backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px);border:1px solid var(--sd-preview-divider);border-radius:12px;box-shadow:0 8px 30px #0006;z-index:3}.sd-preview-toolbar__btn{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-width:36px;height:36px;padding:0 8px;background:transparent;border:none;color:var(--sd-preview-fg);border-radius:8px;cursor:pointer;transition:background .12s}.sd-preview-toolbar__btn sd-icon{font-size:20px;width:20px;height:20px}.sd-preview-toolbar__btn--with-label{padding:0 12px}.sd-preview-toolbar__btn:hover:not(:disabled){background:var(--sd-preview-hover-bg)}.sd-preview-toolbar__btn:focus-visible{outline:2px solid var(--sd-preview-primary);outline-offset:2px}.sd-preview-toolbar__btn-label{font-size:13px;font-weight:500}.sd-preview-toolbar__readout{min-width:56px;text-align:center;color:var(--sd-preview-fg);font-size:13px;font-weight:500;font-variant-numeric:tabular-nums}.sd-preview-toolbar__divider{width:1px;height:20px;background:var(--sd-preview-divider);margin:0 4px}.sd-preview-thumbs{display:flex;gap:8px;padding:12px;background:var(--sd-preview-shell-bg-2);overflow:auto;flex:0 0 auto;flex-direction:row;height:116px}.sd-preview-thumbs--vertical{flex-direction:column;width:152px;height:100%}.sd-preview-thumb{position:relative;flex:0 0 auto;width:120px;height:76px;padding:0;border:none;background:transparent;border-radius:8px;outline:1px solid var(--sd-preview-divider);outline-offset:-1px;opacity:.7;overflow:hidden;cursor:pointer;transition:opacity .12s,outline-color .12s}.sd-preview-thumb:hover{opacity:1}.sd-preview-thumb--active{opacity:1;outline:2px solid var(--sd-preview-primary);outline-offset:-2px;box-shadow:inset 0 0 0 2px var(--sd-preview-primary)}.sd-preview-thumb:focus-visible{outline:2px solid var(--sd-preview-primary);outline-offset:2px}.sd-preview-thumb__img{width:100%;height:100%;object-fit:cover;display:block;pointer-events:none}.sd-preview-thumb__broken{display:flex;align-items:center;justify-content:center;width:100%;height:100%;background:var(--sd-preview-danger-soft);color:var(--sd-preview-danger);pointer-events:none}.sd-preview-thumb__broken sd-icon{font-size:24px;width:24px;height:24px}.sd-preview-thumb__badge{position:absolute;top:4px;right:4px;background:#000000b3;color:#fff;font-size:10px;font-weight:600;padding:1px 5px;border-radius:4px;pointer-events:none}.sd-preview-thumb__name{position:absolute;left:0;right:0;bottom:0;padding:14px 6px 4px;color:var(--sd-preview-fg);font-size:11px;line-height:1.3;background:linear-gradient(to top,rgba(0,0,0,.85),transparent);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;pointer-events:none}.sd-preview-thumbs--vertical .sd-preview-thumb{width:128px}.sd-preview-dots{position:absolute;bottom:24px;left:50%;transform:translate(-50%);display:flex;align-items:center;gap:6px;z-index:3}.sd-preview-dot{width:8px;height:8px;border:none;background:#ffffff4d;border-radius:999px;padding:0;cursor:pointer;transition:width .18s,background .18s}.sd-preview-dot--active{width:24px;background:#f5f6f7}.sd-preview-dot:focus-visible{outline:2px solid var(--sd-preview-primary);outline-offset:2px}.sd-preview-status{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:16px;text-align:center;padding:24px}.sd-preview-status__tile{width:96px;height:96px;border-radius:24px;display:flex;align-items:center;justify-content:center;border:1px solid var(--sd-preview-divider);background:#ffffff0d}.sd-preview-status__tile sd-icon{font-size:48px;width:48px;height:48px;color:var(--sd-preview-fg-subtle)}.sd-preview-status__tile--muted{background:#ffffff0d;border-color:var(--sd-preview-divider)}.sd-preview-status__tile--danger{background:var(--sd-preview-danger-soft);border-color:var(--sd-preview-danger-border)}.sd-preview-status__tile--danger sd-icon{color:var(--sd-preview-danger)}.sd-preview-status__title{font-size:16px;font-weight:500;color:var(--sd-preview-fg)}.sd-preview-status__hint{font-size:13px;color:var(--sd-preview-fg-muted);max-width:320px}.sd-preview-spinner{width:56px;height:56px;border-radius:999px;border:3px solid rgba(255,255,255,.08);border-top-color:var(--sd-preview-primary);animation:sd-preview-spin .9s linear infinite}@keyframes sd-preview-spin{to{transform:rotate(360deg)}}.sd-preview-btn-primary{display:inline-flex;align-items:center;gap:6px;height:36px;padding:0 16px;background:var(--sd-preview-primary);color:#fff;border:none;border-radius:8px;cursor:pointer;font-size:13px;font-weight:500;transition:filter .12s}.sd-preview-btn-primary sd-icon{font-size:18px;width:18px;height:18px}.sd-preview-btn-primary:hover{filter:brightness(1.1)}.sd-preview-btn-primary:focus-visible{outline:2px solid #fff;outline-offset:2px}.sd-preview-kbd-hint{position:absolute;top:16px;left:16px;display:flex;align-items:center;gap:6px;z-index:2;pointer-events:none}.sd-preview-kbd{display:inline-flex;align-items:center;justify-content:center;min-width:20px;height:20px;padding:0 5px;border:1px solid var(--sd-preview-divider);background:#ffffff0a;color:var(--sd-preview-fg-muted);border-radius:4px;font:500 11px/1 ui-monospace,SFMono-Regular,Menlo,monospace}.sd-preview-kbd-text,.sd-preview-kbd-dot{font-size:11px;color:var(--sd-preview-fg-subtle)}.sd-preview-kbd-dot{margin:0 2px}\n"], dependencies: [{ kind: "component", type: SdIcon, selector: "sd-icon", inputs: ["name", "fontIcon", "color", "set", "fontSet", "size", "strokeWidth", "absoluteStrokeWidth", "ariaLabel"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
643
642
|
}
|
|
644
643
|
_a$1 = SdPreviewImage;
|
|
645
644
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: SdPreviewImage, decorators: [{
|
|
646
645
|
type: Component,
|
|
647
|
-
args: [{ selector: 'sd-preview-image', standalone: true, imports: [
|
|
646
|
+
args: [{ selector: 'sd-preview-image', standalone: true, imports: [SdIcon, CommonModule, TranslatePipe], changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
648
647
|
tabindex: '0',
|
|
649
648
|
class: 'sd-preview-image-host',
|
|
650
649
|
// WHY data-theme attribute (not class): SCSS uses :host([data-theme="light"])
|
|
@@ -652,7 +651,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImpo
|
|
|
652
651
|
// visible in devtools without polluting the class list.
|
|
653
652
|
'[attr.data-theme]': 'theme()',
|
|
654
653
|
'[class.sd-preview-image-host--fullscreen]': 'isFullscreen()',
|
|
655
|
-
}, template: "<!--\n Self-contained viewer shell. Component sizes to its parent (width:100%;\n height:100%) \u2014 consumer decides whether to wrap in <sd-modal>,\n <sd-side-drawer>, a page section, or use inline.\n-->\n<div [ngClass]=\"shellClass()\" role=\"region\">\n @let _stage = stage();\n @let _images = images();\n @let _thumbnailPosition = thumbnailPosition();\n @let _activeIndex = activeIndex();\n @let _activeImage = activeImage();\n\n <!-- ============================================\n HEADER BAR\n ============================================ -->\n @if (_stage !== 'empty' && _activeImage; as img) {\n <div class=\"sd-preview-header\">\n <div class=\"sd-preview-header__left\">\n <mat-icon class=\"sd-preview-header__icon\" fontIcon=\"image\"></mat-icon>\n <div class=\"sd-preview-header__meta\">\n <div class=\"sd-preview-header__name T14M\" [title]=\"img.name\">{{ img.name }}</div>\n @if (img.size) {\n <div class=\"sd-preview-header__sub\">\n {{ formatBytes(img.size) }}\n </div>\n }\n </div>\n </div>\n\n @if (_images.length > 1) {\n <div class=\"sd-preview-header__counter\" aria-live=\"polite\">\n {{ _activeIndex + 1 }} / {{ _images.length }}\n </div>\n }\n </div>\n } @else if (title()) {\n <!-- Optional override title even on empty state -->\n <div class=\"sd-preview-header sd-preview-header--simple\">\n <div class=\"sd-preview-header__name T14M\">{{ title() }}</div>\n </div>\n }\n\n <!-- ============================================\n BODY (stage + thumbnails per layout)\n ============================================ -->\n <div class=\"sd-preview-body\">\n\n <!-- STAGE ............................................................... -->\n <div\n class=\"sd-preview-stage\"\n [class.sd-preview-stage--dragging]=\"zoom() > 1\"\n (wheel)=\"onWheel($event)\"\n (pointerdown)=\"onPointerDown($event)\"\n (pointermove)=\"onPointerMove($event)\"\n (pointerup)=\"onPointerUp($event)\"\n (pointercancel)=\"onPointerCancel($event)\"\n (dblclick)=\"onImageDblClick()\">\n\n @switch (_stage) {\n @case ('empty') {\n <div class=\"sd-preview-status\">\n <div class=\"sd-preview-status__tile sd-preview-status__tile--muted\">\n <mat-icon fontIcon=\"image_not_supported\"></mat-icon>\n </div>\n <div class=\"sd-preview-status__title\">\n {{ 'core.component.preview-image.no-image' | translate }}\n </div>\n <div class=\"sd-preview-status__hint\">\n {{ 'core.component.preview-image.no-image-hint' | translate }}\n </div>\n </div>\n }\n @case ('loading') {\n <div class=\"sd-preview-status\">\n <div class=\"sd-preview-spinner\" role=\"progressbar\"></div>\n <div class=\"sd-preview-status__hint\">\n {{ 'core.component.preview-image.loading' | translate }}\n </div>\n </div>\n }\n @default {\n @if (_activeImage; as img) {\n @if (img.loading) {\n <!-- Per-image loading (retry inline) \u2014 gi\u1EEF nav/dots/thumbs visible -->\n <div class=\"sd-preview-image-loading\" role=\"status\"\n [attr.aria-label]=\"'core.component.preview-image.loading' | translate\">\n <div class=\"sd-preview-spinner\"></div>\n <div class=\"sd-preview-status__hint\">\n {{ 'core.component.preview-image.loading' | translate }}\n </div>\n </div>\n } @else if (img.error) {\n <!-- Inline broken-image placeholder \u2014 gi\u1EEF nav/dots/thumbs visible -->\n <div class=\"sd-preview-image-broken\" role=\"img\"\n [attr.aria-label]=\"'core.component.preview-image.load-error' | translate\">\n <div class=\"sd-preview-status__tile sd-preview-status__tile--danger\">\n <mat-icon fontIcon=\"broken_image\"></mat-icon>\n </div>\n <div class=\"sd-preview-status__title\">\n {{ 'core.component.preview-image.load-error' | translate }}\n </div>\n <div class=\"sd-preview-status__hint\">\n {{ 'core.component.preview-image.load-error-hint' | translate }}\n </div>\n <button type=\"button\" class=\"sd-preview-btn-primary\" (click)=\"retryActive()\" [attr.data-autoId]=\"autoIdRetry()\">\n <mat-icon fontIcon=\"refresh\"></mat-icon>\n <span>{{ 'core.component.preview-image.retry' | translate }}</span>\n </button>\n </div>\n } @else {\n <img\n class=\"sd-preview-image\"\n [src]=\"img.blobUrl || img.url\"\n [alt]=\"img.alt || img.name\"\n [style.transform]=\"imageTransform()\"\n (error)=\"onImageError()\"\n draggable=\"false\" />\n\n @if (img.caption) {\n <div class=\"sd-preview-caption\">{{ img.caption }}</div>\n }\n }\n }\n }\n }\n\n <!-- KEYBOARD HINT ............................................. -->\n @if (_stage === 'ready' && _images.length > 1) {\n <div class=\"sd-preview-kbd-hint\" aria-hidden=\"true\">\n <span class=\"sd-preview-kbd\">\u2190</span>\n <span class=\"sd-preview-kbd\">\u2192</span>\n <span class=\"sd-preview-kbd-text\">{{ 'core.component.preview-image.kbd-hint-nav' | translate }}</span>\n </div>\n }\n\n <!-- NAV ARROWS ............................................. -->\n @if (_stage === 'ready' && _images.length > 1) {\n <button\n type=\"button\"\n class=\"sd-preview-nav sd-preview-nav--prev\"\n [disabled]=\"!hasPrev()\"\n [attr.aria-label]=\"'core.component.preview-image.prev' | translate\"\n [attr.data-autoId]=\"autoIdPrev()\"\n (click)=\"updateCurrentImage(-1)\">\n <mat-icon fontIcon=\"chevron_left\"></mat-icon>\n </button>\n <button\n type=\"button\"\n class=\"sd-preview-nav sd-preview-nav--next\"\n [disabled]=\"!hasNext()\"\n [attr.aria-label]=\"'core.component.preview-image.next' | translate\"\n [attr.data-autoId]=\"autoIdNext()\"\n (click)=\"updateCurrentImage(1)\">\n <mat-icon fontIcon=\"chevron_right\"></mat-icon>\n </button>\n }\n\n <!-- DOTS INDICATOR ......................................... -->\n @if (_thumbnailPosition === 'dots' && _stage === 'ready' && _images.length > 1) {\n <div class=\"sd-preview-dots\" role=\"tablist\">\n @for (img of _images; track img.id; let i = $index) {\n <button\n type=\"button\"\n class=\"sd-preview-dot\"\n [class.sd-preview-dot--active]=\"i === _activeIndex\"\n [attr.aria-label]=\"'core.component.preview-image.next' | translate\"\n [attr.data-autoId]=\"autoIdDot(i)\"\n (click)=\"onClickThumbnailImage(i)\"></button>\n }\n </div>\n }\n\n <!-- FLOATING TOOLBAR ....................................... -->\n @if (showToolbar() && _stage === 'ready') {\n <div class=\"sd-preview-toolbar\" role=\"toolbar\">\n @if (zoomable()) {\n <button\n type=\"button\"\n class=\"sd-preview-toolbar__btn\"\n [attr.aria-label]=\"'core.component.preview-image.zoom-out' | translate\"\n [attr.title]=\"'core.component.preview-image.zoom-out' | translate\"\n [attr.data-autoId]=\"autoIdZoomOut()\"\n (click)=\"zoomOut()\">\n <mat-icon fontIcon=\"zoom_out\"></mat-icon>\n </button>\n <span class=\"sd-preview-toolbar__readout\">{{ zoomPercent() }}%</span>\n <button\n type=\"button\"\n class=\"sd-preview-toolbar__btn\"\n [attr.aria-label]=\"'core.component.preview-image.zoom-in' | translate\"\n [attr.title]=\"'core.component.preview-image.zoom-in' | translate\"\n [attr.data-autoId]=\"autoIdZoomIn()\"\n (click)=\"zoomIn()\">\n <mat-icon fontIcon=\"zoom_in\"></mat-icon>\n </button>\n <span class=\"sd-preview-toolbar__divider\"></span>\n }\n <button\n type=\"button\"\n class=\"sd-preview-toolbar__btn sd-preview-toolbar__btn--with-label\"\n [attr.aria-label]=\"'core.component.preview-image.fit' | translate\"\n [attr.data-autoId]=\"autoIdFit()\"\n (click)=\"fitToScreen()\">\n <mat-icon fontIcon=\"fit_screen\"></mat-icon>\n <span class=\"sd-preview-toolbar__btn-label\">{{ 'core.component.preview-image.fit' | translate }}</span>\n </button>\n <button\n type=\"button\"\n class=\"sd-preview-toolbar__btn\"\n [attr.aria-label]=\"'core.component.preview-image.rotate' | translate\"\n [attr.title]=\"'core.component.preview-image.rotate' | translate\"\n [attr.data-autoId]=\"autoIdRotate()\"\n (click)=\"rotate('right')\">\n <mat-icon fontIcon=\"rotate_right\"></mat-icon>\n </button>\n <span class=\"sd-preview-toolbar__divider\"></span>\n @if (downloadable()) {\n <button\n type=\"button\"\n class=\"sd-preview-toolbar__btn\"\n [attr.aria-label]=\"'core.component.preview-image.download' | translate\"\n [attr.title]=\"'core.component.preview-image.download' | translate\"\n [attr.data-autoId]=\"autoIdDownload()\"\n (click)=\"downloadCurrent()\">\n <mat-icon fontIcon=\"download\"></mat-icon>\n </button>\n }\n <button\n type=\"button\"\n class=\"sd-preview-toolbar__btn\"\n [attr.aria-label]=\"'core.component.preview-image.fullscreen' | translate\"\n [attr.title]=\"'core.component.preview-image.fullscreen' | translate\"\n [attr.data-autoId]=\"autoIdFullscreen()\"\n (click)=\"toggleFullscreen()\">\n <mat-icon [fontIcon]=\"isFullscreen() ? 'fullscreen_exit' : 'fullscreen'\"></mat-icon>\n </button>\n </div>\n }\n </div>\n\n <!-- THUMBNAIL STRIP .................................................... -->\n @if (\n _stage === 'ready' &&\n _images.length > 1 &&\n _thumbnailPosition !== 'none' &&\n _thumbnailPosition !== 'dots'\n ) {\n <div\n class=\"sd-preview-thumbs\"\n [class.sd-preview-thumbs--vertical]=\"_thumbnailPosition === 'left' || _thumbnailPosition === 'right'\">\n @for (img of _images; track trackByImage($index, img); let i = $index) {\n <button\n type=\"button\"\n class=\"sd-preview-thumb\"\n [class.sd-preview-thumb--active]=\"i === _activeIndex\"\n [id]=\"'sd-preview-thumb-' + i\"\n [attr.aria-label]=\"img.name\"\n [attr.aria-current]=\"i === _activeIndex ? 'true' : null\"\n [attr.data-autoId]=\"autoIdThumb(i)\"\n (click)=\"onClickThumbnailImage(i)\">\n @if (img.error) {\n <div class=\"sd-preview-thumb__broken\">\n <mat-icon fontIcon=\"broken_image\"></mat-icon>\n </div>\n } @else {\n <img class=\"sd-preview-thumb__img\" [src]=\"img.blobUrl || img.url\" [alt]=\"img.alt || img.name\" />\n }\n <span class=\"sd-preview-thumb__badge\">{{ i + 1 }}</span>\n @if (_thumbnailPosition === 'right' || _thumbnailPosition === 'left') {\n <span class=\"sd-preview-thumb__name\" [title]=\"img.name\">{{ img.name }}</span>\n }\n </button>\n }\n </div>\n }\n </div>\n</div>\n", styles: [":host{--sd-preview-shell-bg: #0d0e10;--sd-preview-shell-bg-2: #17191c;--sd-preview-panel-bg: rgba(15, 17, 20, .92);--sd-preview-divider: rgba(255, 255, 255, .08);--sd-preview-fg: #f5f6f7;--sd-preview-fg-muted: rgba(255, 255, 255, .6);--sd-preview-fg-subtle: rgba(255, 255, 255, .4);--sd-preview-accent: var(--sd-primary, #3b82f6);--sd-preview-accent-soft: rgba(59, 130, 246, .2);--sd-preview-danger: var(--sd-error, #ef4444);--sd-preview-success: var(--sd-success, #22c55e);--sd-preview-danger-soft: rgba(239, 68, 68, .1);--sd-preview-danger-border: rgba(239, 68, 68, .3);--sd-preview-hover-bg: rgba(255, 255, 255, .1);display:block;width:100%;height:100%;min-height:320px;outline:none}:host([data-theme=light]){--sd-preview-shell-bg: #f7f8fa;--sd-preview-shell-bg-2: #ffffff;--sd-preview-panel-bg: rgba(255, 255, 255, .94);--sd-preview-divider: rgba(0, 0, 0, .08);--sd-preview-fg: #1a1c1f;--sd-preview-fg-muted: rgba(0, 0, 0, .62);--sd-preview-fg-subtle: rgba(0, 0, 0, .42);--sd-preview-accent: var(--sd-primary, #1f49bf);--sd-preview-accent-soft: rgba(31, 73, 191, .18);--sd-preview-danger: var(--sd-error, #ef4444);--sd-preview-success: var(--sd-success, #22c55e);--sd-preview-danger-soft: rgba(239, 68, 68, .08);--sd-preview-danger-border: rgba(239, 68, 68, .25);--sd-preview-hover-bg: rgba(0, 0, 0, .06)}.sd-preview-shell{position:relative;display:flex;flex-direction:column;width:100%;height:100%;background:var(--sd-preview-shell-bg);color:var(--sd-preview-fg);border-radius:12px;overflow:hidden;box-shadow:0 30px 80px #0000008c,0 0 0 1px #ffffff0a;font-family:inherit}.sd-preview-shell--fullscreen{height:100vh;width:100vw;border-radius:0}:host(.sd-preview-image-host--fullscreen){width:100vw;height:100vh;min-height:0}.sd-preview-header{position:relative;display:flex;align-items:center;height:68px;padding:0 16px;background:var(--sd-preview-panel-bg);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border-bottom:1px solid var(--sd-preview-divider);flex:0 0 auto;z-index:2}.sd-preview-header--simple{justify-content:space-between}.sd-preview-header__left{display:flex;align-items:center;gap:12px;min-width:0;flex:1 1 auto}.sd-preview-header__icon{color:var(--sd-preview-fg-muted);font-size:20px;width:20px;height:20px}.sd-preview-header__meta{display:flex;flex-direction:column;min-width:0}.sd-preview-header__name{color:var(--sd-preview-fg);max-width:360px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sd-preview-header__sub{font-size:12px;color:var(--sd-preview-fg-muted);margin-top:2px}.sd-preview-header__counter{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);font-size:13px;font-weight:500;color:var(--sd-preview-fg-muted);background:#ffffff14;padding:4px 12px;border-radius:999px;font-variant-numeric:tabular-nums}.sd-preview-body{flex:1 1 auto;min-height:0;display:flex;flex-direction:column;overflow:hidden}.sd-preview-shell--pos-bottom .sd-preview-body{flex-direction:column}.sd-preview-shell--pos-top .sd-preview-body{flex-direction:column-reverse}.sd-preview-shell--pos-right .sd-preview-body{flex-direction:row}.sd-preview-shell--pos-left .sd-preview-body{flex-direction:row-reverse}.sd-preview-shell--pos-dots .sd-preview-body,.sd-preview-shell--pos-none .sd-preview-body{flex-direction:column}.sd-preview-stage{position:relative;flex:1 1 auto;min-height:0;min-width:0;background:var(--sd-preview-shell-bg);background-image:linear-gradient(45deg,rgba(255,255,255,.02) 25%,transparent 25%),linear-gradient(-45deg,rgba(255,255,255,.02) 25%,transparent 25%),linear-gradient(45deg,transparent 75%,rgba(255,255,255,.02) 75%),linear-gradient(-45deg,transparent 75%,rgba(255,255,255,.02) 75%);background-size:24px 24px;background-position:0 0,0 12px,12px -12px,-12px 0;display:flex;align-items:center;justify-content:center;overflow:hidden;touch-action:none;-webkit-user-select:none;user-select:none}.sd-preview-stage--dragging{cursor:grab}:host([data-theme=light]) .sd-preview-stage{background-image:linear-gradient(45deg,rgba(0,0,0,.015) 25%,transparent 25%),linear-gradient(-45deg,rgba(0,0,0,.015) 25%,transparent 25%),linear-gradient(45deg,transparent 75%,rgba(0,0,0,.015) 75%),linear-gradient(-45deg,transparent 75%,rgba(0,0,0,.015) 75%)}.sd-preview-image{max-width:100%;max-height:100%;object-fit:contain;transform-origin:center center;transition:transform .2s ease;will-change:transform;pointer-events:none}.sd-preview-caption{position:absolute;bottom:96px;left:50%;transform:translate(-50%);max-width:70%;padding:6px 12px;background:#0f1114b3;color:var(--sd-preview-fg);border-radius:8px;font-size:13px;text-align:center}.sd-preview-image-broken,.sd-preview-image-loading{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:16px;width:100%;max-width:480px;padding:24px;text-align:center}.sd-preview-nav{position:absolute;top:50%;transform:translateY(-50%);width:48px;height:48px;border-radius:999px;border:none;background:#14141699;backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);color:var(--sd-preview-fg);display:flex;align-items:center;justify-content:center;cursor:pointer;box-shadow:0 4px 16px #0000004d;transition:background .12s;z-index:3}.sd-preview-nav mat-icon{font-size:28px;width:28px;height:28px}.sd-preview-nav:hover:not(:disabled){background:#ffffff2e}.sd-preview-nav:focus-visible{outline:2px solid var(--sd-preview-accent);outline-offset:2px}.sd-preview-nav:disabled{background:#ffffff0a;opacity:.35;cursor:not-allowed}.sd-preview-nav--prev{left:16px}.sd-preview-nav--next{right:16px}.sd-preview-toolbar{position:absolute;bottom:24px;left:50%;transform:translate(-50%);display:flex;align-items:center;gap:4px;padding:6px 8px;background:var(--sd-preview-panel-bg);backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px);border:1px solid var(--sd-preview-divider);border-radius:12px;box-shadow:0 8px 30px #0006;z-index:3}.sd-preview-toolbar__btn{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-width:36px;height:36px;padding:0 8px;background:transparent;border:none;color:var(--sd-preview-fg);border-radius:8px;cursor:pointer;transition:background .12s}.sd-preview-toolbar__btn mat-icon{font-size:20px;width:20px;height:20px}.sd-preview-toolbar__btn--with-label{padding:0 12px}.sd-preview-toolbar__btn:hover:not(:disabled){background:var(--sd-preview-hover-bg)}.sd-preview-toolbar__btn:focus-visible{outline:2px solid var(--sd-preview-accent);outline-offset:2px}.sd-preview-toolbar__btn-label{font-size:13px;font-weight:500}.sd-preview-toolbar__readout{min-width:56px;text-align:center;color:var(--sd-preview-fg);font-size:13px;font-weight:500;font-variant-numeric:tabular-nums}.sd-preview-toolbar__divider{width:1px;height:20px;background:var(--sd-preview-divider);margin:0 4px}.sd-preview-thumbs{display:flex;gap:8px;padding:12px;background:var(--sd-preview-shell-bg-2);overflow:auto;flex:0 0 auto;flex-direction:row;height:116px}.sd-preview-thumbs--vertical{flex-direction:column;width:152px;height:100%}.sd-preview-thumb{position:relative;flex:0 0 auto;width:120px;height:76px;padding:0;border:none;background:transparent;border-radius:8px;outline:1px solid var(--sd-preview-divider);outline-offset:-1px;opacity:.7;overflow:hidden;cursor:pointer;transition:opacity .12s,outline-color .12s}.sd-preview-thumb:hover{opacity:1}.sd-preview-thumb--active{opacity:1;outline:2px solid var(--sd-preview-accent);outline-offset:-2px;box-shadow:inset 0 0 0 2px var(--sd-preview-accent)}.sd-preview-thumb:focus-visible{outline:2px solid var(--sd-preview-accent);outline-offset:2px}.sd-preview-thumb__img{width:100%;height:100%;object-fit:cover;display:block;pointer-events:none}.sd-preview-thumb__broken{display:flex;align-items:center;justify-content:center;width:100%;height:100%;background:var(--sd-preview-danger-soft);color:var(--sd-preview-danger);pointer-events:none}.sd-preview-thumb__broken mat-icon{font-size:24px;width:24px;height:24px}.sd-preview-thumb__badge{position:absolute;top:4px;right:4px;background:#000000b3;color:#fff;font-size:10px;font-weight:600;padding:1px 5px;border-radius:4px;pointer-events:none}.sd-preview-thumb__name{position:absolute;left:0;right:0;bottom:0;padding:14px 6px 4px;color:var(--sd-preview-fg);font-size:11px;line-height:1.3;background:linear-gradient(to top,rgba(0,0,0,.85),transparent);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;pointer-events:none}.sd-preview-thumbs--vertical .sd-preview-thumb{width:128px}.sd-preview-dots{position:absolute;bottom:24px;left:50%;transform:translate(-50%);display:flex;align-items:center;gap:6px;z-index:3}.sd-preview-dot{width:8px;height:8px;border:none;background:#ffffff4d;border-radius:999px;padding:0;cursor:pointer;transition:width .18s,background .18s}.sd-preview-dot--active{width:24px;background:#f5f6f7}.sd-preview-dot:focus-visible{outline:2px solid var(--sd-preview-accent);outline-offset:2px}.sd-preview-status{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:16px;text-align:center;padding:24px}.sd-preview-status__tile{width:96px;height:96px;border-radius:24px;display:flex;align-items:center;justify-content:center;border:1px solid var(--sd-preview-divider);background:#ffffff0d}.sd-preview-status__tile mat-icon{font-size:48px;width:48px;height:48px;color:var(--sd-preview-fg-subtle)}.sd-preview-status__tile--muted{background:#ffffff0d;border-color:var(--sd-preview-divider)}.sd-preview-status__tile--danger{background:var(--sd-preview-danger-soft);border-color:var(--sd-preview-danger-border)}.sd-preview-status__tile--danger mat-icon{color:var(--sd-preview-danger)}.sd-preview-status__title{font-size:16px;font-weight:500;color:var(--sd-preview-fg)}.sd-preview-status__hint{font-size:13px;color:var(--sd-preview-fg-muted);max-width:320px}.sd-preview-spinner{width:56px;height:56px;border-radius:999px;border:3px solid rgba(255,255,255,.08);border-top-color:var(--sd-preview-accent);animation:sd-preview-spin .9s linear infinite}@keyframes sd-preview-spin{to{transform:rotate(360deg)}}.sd-preview-btn-primary{display:inline-flex;align-items:center;gap:6px;height:36px;padding:0 16px;background:var(--sd-preview-accent);color:#fff;border:none;border-radius:8px;cursor:pointer;font-size:13px;font-weight:500;transition:filter .12s}.sd-preview-btn-primary mat-icon{font-size:18px;width:18px;height:18px}.sd-preview-btn-primary:hover{filter:brightness(1.1)}.sd-preview-btn-primary:focus-visible{outline:2px solid #fff;outline-offset:2px}.sd-preview-kbd-hint{position:absolute;top:16px;left:16px;display:flex;align-items:center;gap:6px;z-index:2;pointer-events:none}.sd-preview-kbd{display:inline-flex;align-items:center;justify-content:center;min-width:20px;height:20px;padding:0 5px;border:1px solid var(--sd-preview-divider);background:#ffffff0a;color:var(--sd-preview-fg-muted);border-radius:4px;font:500 11px/1 ui-monospace,SFMono-Regular,Menlo,monospace}.sd-preview-kbd-text,.sd-preview-kbd-dot{font-size:11px;color:var(--sd-preview-fg-subtle)}.sd-preview-kbd-dot{margin:0 2px}\n"] }]
|
|
654
|
+
}, template: "<!--\n Self-contained viewer shell. Component sizes to its parent (width:100%;\n height:100%) \u2014 consumer decides whether to wrap in <sd-modal>,\n <sd-side-drawer>, a page section, or use inline.\n-->\n<div [ngClass]=\"shellClass()\" role=\"region\">\n @let _stage = stage();\n @let _images = images();\n @let _thumbnailPosition = thumbnailPosition();\n @let _activeIndex = activeIndex();\n @let _activeImage = activeImage();\n\n <!-- ============================================\n HEADER BAR\n ============================================ -->\n @if (_stage !== 'empty' && _activeImage; as img) {\n <div class=\"sd-preview-header\">\n <div class=\"sd-preview-header__left\">\n <sd-icon class=\"sd-preview-header__icon\" fontIcon=\"image\"></sd-icon>\n <div class=\"sd-preview-header__meta\">\n <div class=\"sd-preview-header__name T14M\" [title]=\"img.name\">{{ img.name }}</div>\n @if (img.size) {\n <div class=\"sd-preview-header__sub\">\n {{ formatBytes(img.size) }}\n </div>\n }\n </div>\n </div>\n\n @if (_images.length > 1) {\n <div class=\"sd-preview-header__counter\" aria-live=\"polite\">\n {{ _activeIndex + 1 }} / {{ _images.length }}\n </div>\n }\n </div>\n } @else if (title()) {\n <!-- Optional override title even on empty state -->\n <div class=\"sd-preview-header sd-preview-header--simple\">\n <div class=\"sd-preview-header__name T14M\">{{ title() }}</div>\n </div>\n }\n\n <!-- ============================================\n BODY (stage + thumbnails per layout)\n ============================================ -->\n <div class=\"sd-preview-body\">\n\n <!-- STAGE ............................................................... -->\n <div\n class=\"sd-preview-stage\"\n [class.sd-preview-stage--dragging]=\"zoom() > 1\"\n (wheel)=\"onWheel($event)\"\n (pointerdown)=\"onPointerDown($event)\"\n (pointermove)=\"onPointerMove($event)\"\n (pointerup)=\"onPointerUp($event)\"\n (pointercancel)=\"onPointerCancel($event)\"\n (dblclick)=\"onImageDblClick()\">\n\n @switch (_stage) {\n @case ('empty') {\n <div class=\"sd-preview-status\">\n <div class=\"sd-preview-status__tile sd-preview-status__tile--muted\">\n <sd-icon fontIcon=\"image_not_supported\"></sd-icon>\n </div>\n <div class=\"sd-preview-status__title\">\n {{ 'core.component.preview-image.no-image' | translate }}\n </div>\n <div class=\"sd-preview-status__hint\">\n {{ 'core.component.preview-image.no-image-hint' | translate }}\n </div>\n </div>\n }\n @case ('loading') {\n <div class=\"sd-preview-status\">\n <div class=\"sd-preview-spinner\" role=\"progressbar\"></div>\n <div class=\"sd-preview-status__hint\">\n {{ 'core.component.preview-image.loading' | translate }}\n </div>\n </div>\n }\n @default {\n @if (_activeImage; as img) {\n @if (img.loading) {\n <!-- Per-image loading (retry inline) \u2014 gi\u1EEF nav/dots/thumbs visible -->\n <div class=\"sd-preview-image-loading\" role=\"status\"\n [attr.aria-label]=\"'core.component.preview-image.loading' | translate\">\n <div class=\"sd-preview-spinner\"></div>\n <div class=\"sd-preview-status__hint\">\n {{ 'core.component.preview-image.loading' | translate }}\n </div>\n </div>\n } @else if (img.error) {\n <!-- Inline broken-image placeholder \u2014 gi\u1EEF nav/dots/thumbs visible -->\n <div class=\"sd-preview-image-broken\" role=\"img\"\n [attr.aria-label]=\"'core.component.preview-image.load-error' | translate\">\n <div class=\"sd-preview-status__tile sd-preview-status__tile--danger\">\n <sd-icon fontIcon=\"broken_image\"></sd-icon>\n </div>\n <div class=\"sd-preview-status__title\">\n {{ 'core.component.preview-image.load-error' | translate }}\n </div>\n <div class=\"sd-preview-status__hint\">\n {{ 'core.component.preview-image.load-error-hint' | translate }}\n </div>\n <button type=\"button\" class=\"sd-preview-btn-primary\" (click)=\"retryActive()\" [attr.data-autoId]=\"autoIdRetry()\">\n <sd-icon fontIcon=\"refresh\"></sd-icon>\n <span>{{ 'core.component.preview-image.retry' | translate }}</span>\n </button>\n </div>\n } @else {\n <img\n class=\"sd-preview-image\"\n [src]=\"img.blobUrl || img.url\"\n [alt]=\"img.alt || img.name\"\n [style.transform]=\"imageTransform()\"\n (error)=\"onImageError()\"\n draggable=\"false\" />\n\n @if (img.caption) {\n <div class=\"sd-preview-caption\">{{ img.caption }}</div>\n }\n }\n }\n }\n }\n\n <!-- KEYBOARD HINT ............................................. -->\n @if (_stage === 'ready' && _images.length > 1) {\n <div class=\"sd-preview-kbd-hint\" aria-hidden=\"true\">\n <span class=\"sd-preview-kbd\">\u2190</span>\n <span class=\"sd-preview-kbd\">\u2192</span>\n <span class=\"sd-preview-kbd-text\">{{ 'core.component.preview-image.kbd-hint-nav' | translate }}</span>\n </div>\n }\n\n <!-- NAV ARROWS ............................................. -->\n @if (_stage === 'ready' && _images.length > 1) {\n <button\n type=\"button\"\n class=\"sd-preview-nav sd-preview-nav--prev\"\n [disabled]=\"!hasPrev()\"\n [attr.aria-label]=\"'core.component.preview-image.prev' | translate\"\n [attr.data-autoId]=\"autoIdPrev()\"\n (click)=\"updateCurrentImage(-1)\">\n <sd-icon fontIcon=\"chevron_left\"></sd-icon>\n </button>\n <button\n type=\"button\"\n class=\"sd-preview-nav sd-preview-nav--next\"\n [disabled]=\"!hasNext()\"\n [attr.aria-label]=\"'core.component.preview-image.next' | translate\"\n [attr.data-autoId]=\"autoIdNext()\"\n (click)=\"updateCurrentImage(1)\">\n <sd-icon fontIcon=\"chevron_right\"></sd-icon>\n </button>\n }\n\n <!-- DOTS INDICATOR ......................................... -->\n @if (_thumbnailPosition === 'dots' && _stage === 'ready' && _images.length > 1) {\n <div class=\"sd-preview-dots\" role=\"tablist\">\n @for (img of _images; track img.id; let i = $index) {\n <button\n type=\"button\"\n class=\"sd-preview-dot\"\n [class.sd-preview-dot--active]=\"i === _activeIndex\"\n [attr.aria-label]=\"'core.component.preview-image.next' | translate\"\n [attr.data-autoId]=\"autoIdDot(i)\"\n (click)=\"onClickThumbnailImage(i)\"></button>\n }\n </div>\n }\n\n <!-- FLOATING TOOLBAR ....................................... -->\n @if (showToolbar() && _stage === 'ready') {\n <div class=\"sd-preview-toolbar\" role=\"toolbar\">\n @if (zoomable()) {\n <button\n type=\"button\"\n class=\"sd-preview-toolbar__btn\"\n [attr.aria-label]=\"'core.component.preview-image.zoom-out' | translate\"\n [attr.title]=\"'core.component.preview-image.zoom-out' | translate\"\n [attr.data-autoId]=\"autoIdZoomOut()\"\n (click)=\"zoomOut()\">\n <sd-icon fontIcon=\"zoom_out\"></sd-icon>\n </button>\n <span class=\"sd-preview-toolbar__readout\">{{ zoomPercent() }}%</span>\n <button\n type=\"button\"\n class=\"sd-preview-toolbar__btn\"\n [attr.aria-label]=\"'core.component.preview-image.zoom-in' | translate\"\n [attr.title]=\"'core.component.preview-image.zoom-in' | translate\"\n [attr.data-autoId]=\"autoIdZoomIn()\"\n (click)=\"zoomIn()\">\n <sd-icon fontIcon=\"zoom_in\"></sd-icon>\n </button>\n <span class=\"sd-preview-toolbar__divider\"></span>\n }\n <button\n type=\"button\"\n class=\"sd-preview-toolbar__btn sd-preview-toolbar__btn--with-label\"\n [attr.aria-label]=\"'core.component.preview-image.fit' | translate\"\n [attr.data-autoId]=\"autoIdFit()\"\n (click)=\"fitToScreen()\">\n <sd-icon fontIcon=\"fit_screen\"></sd-icon>\n <span class=\"sd-preview-toolbar__btn-label\">{{ 'core.component.preview-image.fit' | translate }}</span>\n </button>\n <button\n type=\"button\"\n class=\"sd-preview-toolbar__btn\"\n [attr.aria-label]=\"'core.component.preview-image.rotate' | translate\"\n [attr.title]=\"'core.component.preview-image.rotate' | translate\"\n [attr.data-autoId]=\"autoIdRotate()\"\n (click)=\"rotate('right')\">\n <sd-icon fontIcon=\"rotate_right\"></sd-icon>\n </button>\n <span class=\"sd-preview-toolbar__divider\"></span>\n @if (downloadable()) {\n <button\n type=\"button\"\n class=\"sd-preview-toolbar__btn\"\n [attr.aria-label]=\"'core.component.preview-image.download' | translate\"\n [attr.title]=\"'core.component.preview-image.download' | translate\"\n [attr.data-autoId]=\"autoIdDownload()\"\n (click)=\"downloadCurrent()\">\n <sd-icon fontIcon=\"download\"></sd-icon>\n </button>\n }\n <button\n type=\"button\"\n class=\"sd-preview-toolbar__btn\"\n [attr.aria-label]=\"'core.component.preview-image.fullscreen' | translate\"\n [attr.title]=\"'core.component.preview-image.fullscreen' | translate\"\n [attr.data-autoId]=\"autoIdFullscreen()\"\n (click)=\"toggleFullscreen()\">\n <sd-icon [fontIcon]=\"isFullscreen() ? 'fullscreen_exit' : 'fullscreen'\"></sd-icon>\n </button>\n </div>\n }\n </div>\n\n <!-- THUMBNAIL STRIP .................................................... -->\n @if (\n _stage === 'ready' &&\n _images.length > 1 &&\n _thumbnailPosition !== 'none' &&\n _thumbnailPosition !== 'dots'\n ) {\n <div\n class=\"sd-preview-thumbs\"\n [class.sd-preview-thumbs--vertical]=\"_thumbnailPosition === 'left' || _thumbnailPosition === 'right'\">\n @for (img of _images; track trackByImage($index, img); let i = $index) {\n <button\n type=\"button\"\n class=\"sd-preview-thumb\"\n [class.sd-preview-thumb--active]=\"i === _activeIndex\"\n [id]=\"'sd-preview-thumb-' + i\"\n [attr.aria-label]=\"img.name\"\n [attr.aria-current]=\"i === _activeIndex ? 'true' : null\"\n [attr.data-autoId]=\"autoIdThumb(i)\"\n (click)=\"onClickThumbnailImage(i)\">\n @if (img.error) {\n <div class=\"sd-preview-thumb__broken\">\n <sd-icon fontIcon=\"broken_image\"></sd-icon>\n </div>\n } @else {\n <img class=\"sd-preview-thumb__img\" [src]=\"img.blobUrl || img.url\" [alt]=\"img.alt || img.name\" />\n }\n <span class=\"sd-preview-thumb__badge\">{{ i + 1 }}</span>\n @if (_thumbnailPosition === 'right' || _thumbnailPosition === 'left') {\n <span class=\"sd-preview-thumb__name\" [title]=\"img.name\">{{ img.name }}</span>\n }\n </button>\n }\n </div>\n }\n </div>\n</div>\n", styles: [":host{--sd-preview-shell-bg: #0d0e10;--sd-preview-shell-bg-2: #17191c;--sd-preview-panel-bg: rgba(15, 17, 20, .92);--sd-preview-divider: rgba(255, 255, 255, .08);--sd-preview-fg: #f5f6f7;--sd-preview-fg-muted: rgba(255, 255, 255, .6);--sd-preview-fg-subtle: rgba(255, 255, 255, .4);--sd-preview-primary: var(--sd-preview-accent, var(--sd-primary, #005cbb));--sd-preview-primary-soft: var(--sd-preview-accent-soft, rgba(0, 92, 187, .18));--sd-preview-danger: var(--sd-error, #ba1a1a);--sd-preview-success: var(--sd-success, #2e7d32);--sd-preview-danger-soft: rgba(186, 26, 26, .1);--sd-preview-danger-border: rgba(186, 26, 26, .3);--sd-preview-hover-bg: rgba(255, 255, 255, .1);display:block;width:100%;height:100%;min-height:320px;outline:none}:host([data-theme=light]){--sd-preview-shell-bg: #f7f8fa;--sd-preview-shell-bg-2: #ffffff;--sd-preview-panel-bg: rgba(255, 255, 255, .94);--sd-preview-divider: rgba(0, 0, 0, .08);--sd-preview-fg: #1a1c1f;--sd-preview-fg-muted: rgba(0, 0, 0, .62);--sd-preview-fg-subtle: rgba(0, 0, 0, .42);--sd-preview-primary: var(--sd-preview-accent, var(--sd-primary, #005cbb));--sd-preview-primary-soft: var(--sd-preview-accent-soft, rgba(0, 92, 187, .16));--sd-preview-danger: var(--sd-error, #ba1a1a);--sd-preview-success: var(--sd-success, #2e7d32);--sd-preview-danger-soft: rgba(186, 26, 26, .08);--sd-preview-danger-border: rgba(186, 26, 26, .25);--sd-preview-hover-bg: rgba(0, 0, 0, .06)}.sd-preview-shell{position:relative;display:flex;flex-direction:column;width:100%;height:100%;background:var(--sd-preview-shell-bg);color:var(--sd-preview-fg);border-radius:12px;overflow:hidden;box-shadow:0 30px 80px #0000008c,0 0 0 1px #ffffff0a;font-family:inherit}.sd-preview-shell--fullscreen{height:100vh;width:100vw;border-radius:0}:host(.sd-preview-image-host--fullscreen){width:100vw;height:100vh;min-height:0}.sd-preview-header{position:relative;display:flex;align-items:center;height:68px;padding:0 16px;background:var(--sd-preview-panel-bg);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border-bottom:1px solid var(--sd-preview-divider);flex:0 0 auto;z-index:2}.sd-preview-header--simple{justify-content:space-between}.sd-preview-header__left{display:flex;align-items:center;gap:12px;min-width:0;flex:1 1 auto}.sd-preview-header__icon{color:var(--sd-preview-fg-muted);font-size:20px;width:20px;height:20px}.sd-preview-header__meta{display:flex;flex-direction:column;min-width:0}.sd-preview-header__name{color:var(--sd-preview-fg);max-width:360px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sd-preview-header__sub{font-size:12px;color:var(--sd-preview-fg-muted);margin-top:2px}.sd-preview-header__counter{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);font-size:13px;font-weight:500;color:var(--sd-preview-fg-muted);background:#ffffff14;padding:4px 12px;border-radius:999px;font-variant-numeric:tabular-nums}.sd-preview-body{flex:1 1 auto;min-height:0;display:flex;flex-direction:column;overflow:hidden}.sd-preview-shell--pos-bottom .sd-preview-body{flex-direction:column}.sd-preview-shell--pos-top .sd-preview-body{flex-direction:column-reverse}.sd-preview-shell--pos-right .sd-preview-body{flex-direction:row}.sd-preview-shell--pos-left .sd-preview-body{flex-direction:row-reverse}.sd-preview-shell--pos-dots .sd-preview-body,.sd-preview-shell--pos-none .sd-preview-body{flex-direction:column}.sd-preview-stage{position:relative;flex:1 1 auto;min-height:0;min-width:0;background:var(--sd-preview-shell-bg);background-image:linear-gradient(45deg,rgba(255,255,255,.02) 25%,transparent 25%),linear-gradient(-45deg,rgba(255,255,255,.02) 25%,transparent 25%),linear-gradient(45deg,transparent 75%,rgba(255,255,255,.02) 75%),linear-gradient(-45deg,transparent 75%,rgba(255,255,255,.02) 75%);background-size:24px 24px;background-position:0 0,0 12px,12px -12px,-12px 0;display:flex;align-items:center;justify-content:center;overflow:hidden;touch-action:none;-webkit-user-select:none;user-select:none}.sd-preview-stage--dragging{cursor:grab}:host([data-theme=light]) .sd-preview-stage{background-image:linear-gradient(45deg,rgba(0,0,0,.015) 25%,transparent 25%),linear-gradient(-45deg,rgba(0,0,0,.015) 25%,transparent 25%),linear-gradient(45deg,transparent 75%,rgba(0,0,0,.015) 75%),linear-gradient(-45deg,transparent 75%,rgba(0,0,0,.015) 75%)}.sd-preview-image{max-width:100%;max-height:100%;object-fit:contain;transform-origin:center center;transition:transform .2s ease;will-change:transform;pointer-events:none}.sd-preview-caption{position:absolute;bottom:96px;left:50%;transform:translate(-50%);max-width:70%;padding:6px 12px;background:#0f1114b3;color:var(--sd-preview-fg);border-radius:8px;font-size:13px;text-align:center}.sd-preview-image-broken,.sd-preview-image-loading{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:16px;width:100%;max-width:480px;padding:24px;text-align:center}.sd-preview-nav{position:absolute;top:50%;transform:translateY(-50%);width:48px;height:48px;border-radius:999px;border:none;background:#14141699;backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);color:var(--sd-preview-fg);display:flex;align-items:center;justify-content:center;cursor:pointer;box-shadow:0 4px 16px #0000004d;transition:background .12s;z-index:3}.sd-preview-nav sd-icon{font-size:28px;width:28px;height:28px}.sd-preview-nav:hover:not(:disabled){background:#ffffff2e}.sd-preview-nav:focus-visible{outline:2px solid var(--sd-preview-primary);outline-offset:2px}.sd-preview-nav:disabled{background:#ffffff0a;opacity:.35;cursor:not-allowed}.sd-preview-nav--prev{left:16px}.sd-preview-nav--next{right:16px}.sd-preview-toolbar{position:absolute;bottom:24px;left:50%;transform:translate(-50%);display:flex;align-items:center;gap:4px;padding:6px 8px;background:var(--sd-preview-panel-bg);backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px);border:1px solid var(--sd-preview-divider);border-radius:12px;box-shadow:0 8px 30px #0006;z-index:3}.sd-preview-toolbar__btn{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-width:36px;height:36px;padding:0 8px;background:transparent;border:none;color:var(--sd-preview-fg);border-radius:8px;cursor:pointer;transition:background .12s}.sd-preview-toolbar__btn sd-icon{font-size:20px;width:20px;height:20px}.sd-preview-toolbar__btn--with-label{padding:0 12px}.sd-preview-toolbar__btn:hover:not(:disabled){background:var(--sd-preview-hover-bg)}.sd-preview-toolbar__btn:focus-visible{outline:2px solid var(--sd-preview-primary);outline-offset:2px}.sd-preview-toolbar__btn-label{font-size:13px;font-weight:500}.sd-preview-toolbar__readout{min-width:56px;text-align:center;color:var(--sd-preview-fg);font-size:13px;font-weight:500;font-variant-numeric:tabular-nums}.sd-preview-toolbar__divider{width:1px;height:20px;background:var(--sd-preview-divider);margin:0 4px}.sd-preview-thumbs{display:flex;gap:8px;padding:12px;background:var(--sd-preview-shell-bg-2);overflow:auto;flex:0 0 auto;flex-direction:row;height:116px}.sd-preview-thumbs--vertical{flex-direction:column;width:152px;height:100%}.sd-preview-thumb{position:relative;flex:0 0 auto;width:120px;height:76px;padding:0;border:none;background:transparent;border-radius:8px;outline:1px solid var(--sd-preview-divider);outline-offset:-1px;opacity:.7;overflow:hidden;cursor:pointer;transition:opacity .12s,outline-color .12s}.sd-preview-thumb:hover{opacity:1}.sd-preview-thumb--active{opacity:1;outline:2px solid var(--sd-preview-primary);outline-offset:-2px;box-shadow:inset 0 0 0 2px var(--sd-preview-primary)}.sd-preview-thumb:focus-visible{outline:2px solid var(--sd-preview-primary);outline-offset:2px}.sd-preview-thumb__img{width:100%;height:100%;object-fit:cover;display:block;pointer-events:none}.sd-preview-thumb__broken{display:flex;align-items:center;justify-content:center;width:100%;height:100%;background:var(--sd-preview-danger-soft);color:var(--sd-preview-danger);pointer-events:none}.sd-preview-thumb__broken sd-icon{font-size:24px;width:24px;height:24px}.sd-preview-thumb__badge{position:absolute;top:4px;right:4px;background:#000000b3;color:#fff;font-size:10px;font-weight:600;padding:1px 5px;border-radius:4px;pointer-events:none}.sd-preview-thumb__name{position:absolute;left:0;right:0;bottom:0;padding:14px 6px 4px;color:var(--sd-preview-fg);font-size:11px;line-height:1.3;background:linear-gradient(to top,rgba(0,0,0,.85),transparent);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;pointer-events:none}.sd-preview-thumbs--vertical .sd-preview-thumb{width:128px}.sd-preview-dots{position:absolute;bottom:24px;left:50%;transform:translate(-50%);display:flex;align-items:center;gap:6px;z-index:3}.sd-preview-dot{width:8px;height:8px;border:none;background:#ffffff4d;border-radius:999px;padding:0;cursor:pointer;transition:width .18s,background .18s}.sd-preview-dot--active{width:24px;background:#f5f6f7}.sd-preview-dot:focus-visible{outline:2px solid var(--sd-preview-primary);outline-offset:2px}.sd-preview-status{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:16px;text-align:center;padding:24px}.sd-preview-status__tile{width:96px;height:96px;border-radius:24px;display:flex;align-items:center;justify-content:center;border:1px solid var(--sd-preview-divider);background:#ffffff0d}.sd-preview-status__tile sd-icon{font-size:48px;width:48px;height:48px;color:var(--sd-preview-fg-subtle)}.sd-preview-status__tile--muted{background:#ffffff0d;border-color:var(--sd-preview-divider)}.sd-preview-status__tile--danger{background:var(--sd-preview-danger-soft);border-color:var(--sd-preview-danger-border)}.sd-preview-status__tile--danger sd-icon{color:var(--sd-preview-danger)}.sd-preview-status__title{font-size:16px;font-weight:500;color:var(--sd-preview-fg)}.sd-preview-status__hint{font-size:13px;color:var(--sd-preview-fg-muted);max-width:320px}.sd-preview-spinner{width:56px;height:56px;border-radius:999px;border:3px solid rgba(255,255,255,.08);border-top-color:var(--sd-preview-primary);animation:sd-preview-spin .9s linear infinite}@keyframes sd-preview-spin{to{transform:rotate(360deg)}}.sd-preview-btn-primary{display:inline-flex;align-items:center;gap:6px;height:36px;padding:0 16px;background:var(--sd-preview-primary);color:#fff;border:none;border-radius:8px;cursor:pointer;font-size:13px;font-weight:500;transition:filter .12s}.sd-preview-btn-primary sd-icon{font-size:18px;width:18px;height:18px}.sd-preview-btn-primary:hover{filter:brightness(1.1)}.sd-preview-btn-primary:focus-visible{outline:2px solid #fff;outline-offset:2px}.sd-preview-kbd-hint{position:absolute;top:16px;left:16px;display:flex;align-items:center;gap:6px;z-index:2;pointer-events:none}.sd-preview-kbd{display:inline-flex;align-items:center;justify-content:center;min-width:20px;height:20px;padding:0 5px;border:1px solid var(--sd-preview-divider);background:#ffffff0a;color:var(--sd-preview-fg-muted);border-radius:4px;font:500 11px/1 ui-monospace,SFMono-Regular,Menlo,monospace}.sd-preview-kbd-text,.sd-preview-kbd-dot{font-size:11px;color:var(--sd-preview-fg-subtle)}.sd-preview-kbd-dot{margin:0 2px}\n"] }]
|
|
656
655
|
}], ctorParameters: () => [], propDecorators: { onKeyDown: [{
|
|
657
656
|
type: HostListener,
|
|
658
657
|
args: ['keydown', ['$event']]
|
|
@@ -1964,12 +1963,12 @@ class SdPreviewPdf {
|
|
|
1964
1963
|
img.src = dataUrl;
|
|
1965
1964
|
}
|
|
1966
1965
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: SdPreviewPdf, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1967
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.21", type: SdPreviewPdf, isStandalone: true, selector: "sd-preview-pdf", inputs: { autoIdInput: { classPropertyName: "autoIdInput", publicName: "autoId", isSignal: true, isRequired: false, transformFunction: null }, source: { classPropertyName: "source", publicName: "source", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, startPage: { classPropertyName: "startPage", publicName: "startPage", isSignal: true, isRequired: false, transformFunction: null }, initialZoom: { classPropertyName: "initialZoom", publicName: "initialZoom", isSignal: true, isRequired: false, transformFunction: null }, sidebar: { classPropertyName: "sidebar", publicName: "sidebar", isSignal: true, isRequired: false, transformFunction: null }, sidebarOpen: { classPropertyName: "sidebarOpen", publicName: "sidebarOpen", isSignal: true, isRequired: false, transformFunction: null }, scrollMode: { classPropertyName: "scrollMode", publicName: "scrollMode", isSignal: true, isRequired: false, transformFunction: null }, showToolbar: { classPropertyName: "showToolbar", publicName: "showToolbar", isSignal: true, isRequired: false, transformFunction: null }, downloadable: { classPropertyName: "downloadable", publicName: "downloadable", isSignal: true, isRequired: false, transformFunction: null }, password: { classPropertyName: "password", publicName: "password", isSignal: true, isRequired: false, transformFunction: null }, httpHeaders: { classPropertyName: "httpHeaders", publicName: "httpHeaders", isSignal: true, isRequired: false, transformFunction: null }, theme: { classPropertyName: "theme", publicName: "theme", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { close: "close", loaded: "loaded", pageChange: "pageChange", zoomChange: "zoomChange", download: "download", loadError: "loadError", searchChange: "searchChange" }, host: { attributes: { "tabindex": "0" }, listeners: { "keydown": "onKeyDown($event)" }, properties: { "attr.data-theme": "theme()", "class.sd-preview-pdf-host--fullscreen": "isFullscreen()" }, classAttribute: "sd-preview-pdf-host" }, viewQueries: [{ propertyName: "pageCanvasRef", first: true, predicate: ["pageCanvas"], descendants: true, isSignal: true }, { propertyName: "stageEl", first: true, predicate: ["stageRef"], descendants: true, isSignal: true }, { propertyName: "pageInputRef", first: true, predicate: ["pageInput"], descendants: true, isSignal: true }, { propertyName: "searchInputRef", first: true, predicate: ["searchInput"], descendants: true, isSignal: true }, { propertyName: "thumbCanvases", predicate: ["thumbCanvas"], descendants: true, isSignal: true }], ngImport: i0, template: "<!--\n sd-preview-pdf shell. Self-contained \u2014 sizes to its parent. Consumer wraps\n in <sd-modal>/<sd-side-drawer>/page section. Header + sidebar + stage +\n floating toolbar mirror Artboards A / C / E from the design handoff.\n-->\n<div class=\"sd-preview-pdf-shell\" role=\"region\">\n @let _stage = stage();\n @let _numPages = numPages();\n @let _activePage = activePage();\n @let _isSidebarOpen = isSidebarOpen();\n @let _searchBarOpen = searchBarOpen();\n @let _searchTerm = searchTerm();\n @let _searchTotal = searchTotal();\n\n <!-- ============================================\n HEADER BAR \u2014 hidden in empty + loading per design\n ============================================ -->\n @if (_stage === 'ready') {\n <div class=\"sd-preview-pdf-header\">\n <div class=\"sd-preview-pdf-header__left\">\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [class.sd-preview-pdf-iconbtn--active]=\"_isSidebarOpen\"\n [attr.aria-label]=\"'core.component.preview-pdf.toggle-sidebar' | translate\"\n [attr.title]=\"'core.component.preview-pdf.toggle-sidebar' | translate\"\n [attr.data-autoId]=\"autoIdSidebarToggle()\"\n (click)=\"toggleSidebar()\">\n <mat-icon fontIcon=\"menu\"></mat-icon>\n </button>\n <span class=\"sd-preview-pdf-header__divider\"></span>\n <div class=\"sd-preview-pdf-fileicon\" aria-hidden=\"true\">PDF</div>\n <div class=\"sd-preview-pdf-header__meta\">\n <div class=\"sd-preview-pdf-header__name\" [title]=\"title() || filename()\">\n {{ title() || filename() }}\n </div>\n <div class=\"sd-preview-pdf-header__sub\">\n {{ 'core.component.preview-pdf.pages-count' | translate: { count: _numPages } }}\n </div>\n </div>\n </div>\n\n <div class=\"sd-preview-pdf-header__right\">\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [class.sd-preview-pdf-iconbtn--active]=\"_searchBarOpen\"\n [attr.aria-label]=\"'core.component.preview-pdf.search' | translate\"\n [attr.title]=\"'core.component.preview-pdf.search' | translate\"\n [attr.data-autoId]=\"autoIdSearchToggle()\"\n (click)=\"searchBarOpen() ? closeSearch() : openSearch()\">\n <mat-icon fontIcon=\"search\"></mat-icon>\n </button>\n @if (downloadable()) {\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [attr.aria-label]=\"'core.component.preview-pdf.download' | translate\"\n [attr.title]=\"'core.component.preview-pdf.download' | translate\"\n [attr.data-autoId]=\"autoIdDownload()\"\n (click)=\"downloadFile()\">\n <mat-icon fontIcon=\"download\"></mat-icon>\n </button>\n }\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [attr.aria-label]=\"'core.component.preview-pdf.fullscreen' | translate\"\n [attr.title]=\"'core.component.preview-pdf.fullscreen' | translate\"\n [attr.data-autoId]=\"autoIdFullscreen()\"\n (click)=\"toggleFullscreen()\">\n <mat-icon [fontIcon]=\"isFullscreen() ? 'fullscreen_exit' : 'fullscreen'\"></mat-icon>\n </button>\n </div>\n </div>\n\n <!-- ============================================\n SEARCH BAR \u2014 between header + body, only when bar is open\n ============================================ -->\n @if (_searchBarOpen) {\n <div class=\"sd-preview-pdf-searchbar\" role=\"search\">\n <mat-icon class=\"sd-preview-pdf-searchbar__icon\" fontIcon=\"search\"></mat-icon>\n <input\n #searchInput\n type=\"text\"\n class=\"sd-preview-pdf-searchbar__input\"\n [attr.placeholder]=\"'core.component.preview-pdf.search-placeholder' | translate\"\n [value]=\"_searchTerm\"\n [attr.data-autoId]=\"autoIdSearchInput()\"\n (input)=\"onSearchInput($any($event.target).value)\" />\n <span class=\"sd-preview-pdf-searchbar__counter\" aria-live=\"polite\">\n {{ searchCurrent() }} / {{ _searchTotal }}\n </span>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [disabled]=\"_searchTotal === 0\"\n [attr.aria-label]=\"'core.component.preview-pdf.search-prev' | translate\"\n [attr.title]=\"'core.component.preview-pdf.search-prev' | translate\"\n [attr.data-autoId]=\"autoIdSearchPrev()\"\n (click)=\"searchPrev()\">\n <mat-icon fontIcon=\"keyboard_arrow_up\"></mat-icon>\n </button>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [disabled]=\"_searchTotal === 0\"\n [attr.aria-label]=\"'core.component.preview-pdf.search-next' | translate\"\n [attr.title]=\"'core.component.preview-pdf.search-next' | translate\"\n [attr.data-autoId]=\"autoIdSearchNext()\"\n (click)=\"searchNext()\">\n <mat-icon fontIcon=\"keyboard_arrow_down\"></mat-icon>\n </button>\n <span class=\"sd-preview-pdf-searchbar__divider\"></span>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [class.sd-preview-pdf-iconbtn--active]=\"searchCaseSensitive()\"\n [attr.aria-label]=\"'core.component.preview-pdf.search-case' | translate\"\n [attr.title]=\"'core.component.preview-pdf.search-case' | translate\"\n [attr.data-autoId]=\"autoIdSearchCase()\"\n (click)=\"toggleSearchCaseSensitive()\">\n <mat-icon fontIcon=\"match_case\"></mat-icon>\n </button>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [class.sd-preview-pdf-iconbtn--active]=\"searchWholeWord()\"\n [attr.aria-label]=\"'core.component.preview-pdf.search-whole' | translate\"\n [attr.title]=\"'core.component.preview-pdf.search-whole' | translate\"\n [attr.data-autoId]=\"autoIdSearchWhole()\"\n (click)=\"toggleSearchWholeWord()\">\n <mat-icon fontIcon=\"text_fields\"></mat-icon>\n </button>\n <span class=\"sd-preview-pdf-searchbar__spacer\"></span>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [attr.aria-label]=\"'core.component.preview-pdf.search-close' | translate\"\n [attr.title]=\"'core.component.preview-pdf.search-close' | translate\"\n [attr.data-autoId]=\"autoIdSearchClose()\"\n (click)=\"closeSearch()\">\n <mat-icon fontIcon=\"close\"></mat-icon>\n </button>\n </div>\n }\n }\n\n <!-- ============================================\n BODY \u2014 sidebar + stage\n ============================================ -->\n <div class=\"sd-preview-pdf-body\">\n\n <!-- SIDEBAR ............................................................. -->\n @if (_stage === 'ready' && _isSidebarOpen) {\n <aside class=\"sd-preview-pdf-sidebar\" role=\"complementary\">\n @let _sidebarMode = sidebarMode();\n <div class=\"sd-preview-pdf-sidebar__tabs\" role=\"tablist\">\n <button\n type=\"button\"\n class=\"sd-preview-pdf-sidebar__tab\"\n role=\"tab\"\n [class.sd-preview-pdf-sidebar__tab--active]=\"_sidebarMode === 'thumbnails'\"\n [attr.data-autoId]=\"autoIdTabThumbnails()\"\n (click)=\"setSidebarMode('thumbnails')\">\n <mat-icon fontIcon=\"view_carousel\"></mat-icon>\n <span>{{ 'core.component.preview-pdf.tab.thumbnails' | translate }}</span>\n </button>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-sidebar__tab\"\n role=\"tab\"\n [class.sd-preview-pdf-sidebar__tab--active]=\"_sidebarMode === 'outline'\"\n [attr.data-autoId]=\"autoIdTabOutline()\"\n (click)=\"setSidebarMode('outline')\">\n <mat-icon fontIcon=\"list\"></mat-icon>\n <span>{{ 'core.component.preview-pdf.tab.outline' | translate }}</span>\n </button>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-sidebar__tab\"\n role=\"tab\"\n [class.sd-preview-pdf-sidebar__tab--active]=\"_sidebarMode === 'search'\"\n [attr.data-autoId]=\"autoIdTabSearch()\"\n (click)=\"setSidebarMode('search')\">\n <mat-icon fontIcon=\"search\"></mat-icon>\n <span>{{ 'core.component.preview-pdf.tab.search' | translate }}</span>\n </button>\n </div>\n\n <div class=\"sd-preview-pdf-sidebar__content\">\n @switch (_sidebarMode) {\n @case ('thumbnails') {\n <ul class=\"sd-preview-pdf-thumb-list\">\n @for (p of pageNumbers(); track trackByPage($index, p); let pi = $index) {\n <li>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-thumb\"\n [class.sd-preview-pdf-thumb--active]=\"p === _activePage\"\n [attr.aria-current]=\"p === _activePage ? 'page' : null\"\n [attr.aria-label]=\"('core.component.preview-pdf.page-of' | translate: { current: p, total: _numPages })\"\n [attr.data-autoId]=\"autoIdThumb(pi)\"\n (click)=\"goToPage(p)\">\n <div class=\"sd-preview-pdf-thumb__page\">\n <canvas\n #thumbCanvas\n class=\"sd-preview-pdf-thumb__canvas\"\n [attr.data-page]=\"p\"\n aria-hidden=\"true\"></canvas>\n </div>\n <span class=\"sd-preview-pdf-thumb__label\">{{ p }}</span>\n </button>\n </li>\n }\n </ul>\n }\n @case ('outline') {\n <!-- TODO(preview-pdf): outline tree (pdfDoc.getOutline()) deferred to follow-up commit. -->\n <div class=\"sd-preview-pdf-sidebar__deferred\">\n <mat-icon fontIcon=\"list\"></mat-icon>\n <p>{{ 'core.component.preview-pdf.deferred.outline' | translate }}</p>\n </div>\n }\n @case ('search') {\n @if (!_searchTerm) {\n <div class=\"sd-preview-pdf-sidebar__deferred\">\n <mat-icon fontIcon=\"search\"></mat-icon>\n <p>{{ 'core.component.preview-pdf.search-placeholder' | translate }}</p>\n </div>\n } @else if (_searchTotal === 0) {\n <div class=\"sd-preview-pdf-sidebar__deferred\">\n <mat-icon fontIcon=\"search_off\"></mat-icon>\n <p>{{ 'core.component.preview-pdf.search-no-results' | translate }}</p>\n </div>\n } @else {\n <ul class=\"sd-preview-pdf-search-list\">\n @for (r of searchResults(); track $index; let i = $index) {\n <li>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-search-result\"\n [class.sd-preview-pdf-search-result--active]=\"i === searchActiveIndex()\"\n [attr.data-autoId]=\"autoIdResult(i)\"\n (click)=\"goToPage(r.page)\">\n <span class=\"sd-preview-pdf-search-result__snippet\">\n {{ r.before }}<mark>{{ r.term }}</mark>{{ r.after }}\n </span>\n <span class=\"sd-preview-pdf-search-result__page\">{{ r.page }}</span>\n </button>\n </li>\n }\n </ul>\n }\n }\n @default {\n <div class=\"sd-preview-pdf-sidebar__deferred\"></div>\n }\n }\n </div>\n </aside>\n }\n\n <!-- STAGE ............................................................... -->\n <div class=\"sd-preview-pdf-stage\" #stageRef (wheel)=\"onWheel($event)\">\n @switch (_stage) {\n @case ('empty') {\n <div class=\"sd-preview-pdf-status\">\n <div class=\"sd-preview-pdf-status__tile sd-preview-pdf-status__tile--muted\">\n <mat-icon fontIcon=\"picture_as_pdf\"></mat-icon>\n </div>\n <div class=\"sd-preview-pdf-status__title\">\n {{ 'core.component.preview-pdf.empty.title' | translate }}\n </div>\n <div class=\"sd-preview-pdf-status__hint\">\n {{ 'core.component.preview-pdf.empty.subtitle' | translate }}\n </div>\n </div>\n }\n @case ('loading') {\n @let _loadPercent = loadPercent();\n <div class=\"sd-preview-pdf-status\">\n <div class=\"sd-preview-pdf-spinner\" role=\"progressbar\"></div>\n <div class=\"sd-preview-pdf-status__title\">\n {{ 'core.component.preview-pdf.loading' | translate }}\n @if (_loadPercent > 0) {\n <span class=\"sd-preview-pdf-progress-pct\"> {{ _loadPercent }}%</span>\n }\n </div>\n <div class=\"sd-preview-pdf-progress\">\n <div class=\"sd-preview-pdf-progress__fill\" [style.width.%]=\"_loadPercent\"></div>\n </div>\n </div>\n }\n @case ('error') {\n @if (loadError$(); as err) {\n <div class=\"sd-preview-pdf-status\">\n <div class=\"sd-preview-pdf-status__tile sd-preview-pdf-status__tile--danger\">\n <mat-icon fontIcon=\"error_outline\"></mat-icon>\n </div>\n <div class=\"sd-preview-pdf-status__title\">\n {{ ('core.component.preview-pdf.error.' + err.reason + '.title') | translate }}\n </div>\n <div class=\"sd-preview-pdf-status__hint\">\n {{ ('core.component.preview-pdf.error.' + err.reason + '.body') | translate }}\n </div>\n <div class=\"sd-preview-pdf-status__actions\">\n <button type=\"button\" class=\"sd-preview-pdf-btn-primary\" (click)=\"retryLoad()\" [attr.data-autoId]=\"autoIdRetry()\">\n <mat-icon fontIcon=\"refresh\"></mat-icon>\n <span>{{ 'core.component.preview-pdf.error.retry' | translate }}</span>\n </button>\n @if (downloadable()) {\n <button type=\"button\" class=\"sd-preview-pdf-btn-secondary\" (click)=\"downloadFile()\">\n <mat-icon fontIcon=\"download\"></mat-icon>\n <span>{{ 'core.component.preview-pdf.error.download-raw' | translate }}</span>\n </button>\n }\n </div>\n </div>\n }\n }\n @default {\n <div class=\"sd-preview-pdf-page-wrap\">\n <canvas\n #pageCanvas\n class=\"sd-preview-pdf-page\"\n role=\"img\"\n [attr.aria-label]=\"('core.component.preview-pdf.page-of' | translate: { current: _activePage, total: _numPages })\">\n </canvas>\n </div>\n }\n }\n\n <!-- FLOATING TOOLBAR ........................................ -->\n @if (showToolbar() && _stage === 'ready') {\n @let _canPrev = canPrev();\n @let _canNext = canNext();\n @let _zoomMode = zoomMode();\n @let _scrollModeCurrent = scrollModeCurrent();\n <div class=\"sd-preview-pdf-toolbar\" role=\"toolbar\">\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn\"\n [disabled]=\"!_canPrev\"\n [attr.aria-label]=\"'core.component.preview-pdf.first-page' | translate\"\n [attr.title]=\"'core.component.preview-pdf.first-page' | translate\"\n [attr.data-autoId]=\"autoIdFirst()\"\n (click)=\"firstPage()\">\n <mat-icon fontIcon=\"first_page\"></mat-icon>\n </button>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn\"\n [disabled]=\"!_canPrev\"\n [attr.aria-label]=\"'core.component.preview-pdf.prev-page' | translate\"\n [attr.title]=\"'core.component.preview-pdf.prev-page' | translate\"\n [attr.data-autoId]=\"autoIdPrev()\"\n (click)=\"prevPage()\">\n <mat-icon fontIcon=\"chevron_left\"></mat-icon>\n </button>\n <span class=\"sd-preview-pdf-toolbar__page\">\n <input\n type=\"text\"\n inputmode=\"numeric\"\n class=\"sd-preview-pdf-toolbar__page-input\"\n [value]=\"_activePage\"\n #pageInput\n (keydown.enter)=\"onPageInputEnter($event)\"\n (blur)=\"onPageInputBlur($event)\"\n [attr.aria-label]=\"'core.component.preview-pdf.page-of' | translate: { current: _activePage, total: _numPages }\"\n [attr.data-autoId]=\"autoIdPageInput()\" />\n <span class=\"sd-preview-pdf-toolbar__page-total\">/ {{ _numPages }}</span>\n </span>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn\"\n [disabled]=\"!_canNext\"\n [attr.aria-label]=\"'core.component.preview-pdf.next-page' | translate\"\n [attr.title]=\"'core.component.preview-pdf.next-page' | translate\"\n [attr.data-autoId]=\"autoIdNext()\"\n (click)=\"nextPage()\">\n <mat-icon fontIcon=\"chevron_right\"></mat-icon>\n </button>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn\"\n [disabled]=\"!_canNext\"\n [attr.aria-label]=\"'core.component.preview-pdf.last-page' | translate\"\n [attr.title]=\"'core.component.preview-pdf.last-page' | translate\"\n [attr.data-autoId]=\"autoIdLast()\"\n (click)=\"lastPage()\">\n <mat-icon fontIcon=\"last_page\"></mat-icon>\n </button>\n\n <span class=\"sd-preview-pdf-toolbar__divider\"></span>\n\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn\"\n [attr.aria-label]=\"'core.component.preview-pdf.zoom-out' | translate\"\n [attr.title]=\"'core.component.preview-pdf.zoom-out' | translate\"\n [attr.data-autoId]=\"autoIdZoomOut()\"\n (click)=\"zoomOut()\">\n <mat-icon fontIcon=\"remove\"></mat-icon>\n </button>\n <span class=\"sd-preview-pdf-toolbar__zoom\" aria-live=\"polite\" [attr.data-autoId]=\"autoIdZoomReadout()\">{{ zoomPercent() }}%</span>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn\"\n [attr.aria-label]=\"'core.component.preview-pdf.zoom-in' | translate\"\n [attr.title]=\"'core.component.preview-pdf.zoom-in' | translate\"\n [attr.data-autoId]=\"autoIdZoomIn()\"\n (click)=\"zoomIn()\">\n <mat-icon fontIcon=\"add\"></mat-icon>\n </button>\n\n <span class=\"sd-preview-pdf-toolbar__divider\"></span>\n\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn sd-preview-pdf-toolbar__btn--with-label\"\n [class.sd-preview-pdf-toolbar__btn--active]=\"_zoomMode === 'page-fit'\"\n [attr.data-autoId]=\"autoIdFitPage()\"\n (click)=\"setZoom('page-fit')\">\n <mat-icon fontIcon=\"fit_screen\"></mat-icon>\n <span>{{ 'core.component.preview-pdf.fit-page' | translate }}</span>\n </button>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn sd-preview-pdf-toolbar__btn--with-label\"\n [class.sd-preview-pdf-toolbar__btn--active]=\"_zoomMode === 'page-width'\"\n [attr.data-autoId]=\"autoIdFitWidth()\"\n (click)=\"setZoom('page-width')\">\n <mat-icon fontIcon=\"swap_horiz\"></mat-icon>\n <span>{{ 'core.component.preview-pdf.fit-width' | translate }}</span>\n </button>\n\n <span class=\"sd-preview-pdf-toolbar__divider\"></span>\n\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn\"\n [attr.aria-label]=\"'core.component.preview-pdf.rotate' | translate\"\n [attr.title]=\"'core.component.preview-pdf.rotate' | translate\"\n [attr.data-autoId]=\"autoIdRotate()\"\n (click)=\"rotate('right')\">\n <mat-icon fontIcon=\"rotate_right\"></mat-icon>\n </button>\n\n <!-- TODO(preview-pdf): scroll mode toggle is signature-only;\n clicking switches the state but rendering stays single-page\n until the continuous scroll feature lands. -->\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn sd-preview-pdf-toolbar__btn--with-label\"\n [attr.data-autoId]=\"autoIdScrollMode()\"\n (click)=\"setScrollMode(_scrollModeCurrent === 'page' ? 'continuous' : 'page')\">\n <mat-icon [fontIcon]=\"_scrollModeCurrent === 'page' ? 'description' : 'view_day'\"></mat-icon>\n <span>\n {{ (_scrollModeCurrent === 'page'\n ? 'core.component.preview-pdf.scroll-page'\n : 'core.component.preview-pdf.scroll-continuous') | translate }}\n </span>\n </button>\n </div>\n }\n </div>\n </div>\n</div>\n", styles: [":host{--sd-pdf-shell-bg: #1a1c1f;--sd-pdf-sidebar-bg: #22252a;--sd-pdf-toolbar-bg: rgba(20, 22, 25, .94);--sd-pdf-page-bg: #ffffff;--sd-pdf-page-shadow: 0 4px 16px rgba(0, 0, 0, .4);--sd-pdf-divider: rgba(255, 255, 255, .08);--sd-pdf-fg: #f5f6f7;--sd-pdf-fg-muted: rgba(255, 255, 255, .6);--sd-pdf-fg-subtle: rgba(255, 255, 255, .4);--sd-pdf-accent: var(--sd-primary, #3b82f6);--sd-pdf-accent-soft: rgba(59, 130, 246, .2);--sd-pdf-danger: var(--sd-error, #ef4444);--sd-pdf-success: var(--sd-success, #22c55e);--sd-pdf-danger-soft: rgba(239, 68, 68, .1);--sd-pdf-danger-border: rgba(239, 68, 68, .3);--sd-pdf-danger-tint: rgba(239, 68, 68, .15);--sd-pdf-hover-bg: rgba(255, 255, 255, .1);--sd-pdf-search-hi: rgba(250, 204, 21, .55);--sd-pdf-search-hi-active: rgba(249, 115, 22, .85);display:block;width:100%;height:100%;min-height:320px;outline:none}:host([data-theme=light]){--sd-pdf-shell-bg: #f7f8fa;--sd-pdf-sidebar-bg: #ffffff;--sd-pdf-toolbar-bg: rgba(255, 255, 255, .94);--sd-pdf-page-bg: #ffffff;--sd-pdf-page-shadow: 0 4px 16px rgba(0, 0, 0, .12);--sd-pdf-divider: rgba(0, 0, 0, .08);--sd-pdf-fg: #1a1c1f;--sd-pdf-fg-muted: rgba(0, 0, 0, .62);--sd-pdf-fg-subtle: rgba(0, 0, 0, .42);--sd-pdf-accent: var(--sd-primary, #1f49bf);--sd-pdf-accent-soft: rgba(31, 73, 191, .18);--sd-pdf-danger: var(--sd-error, #ef4444);--sd-pdf-success: var(--sd-success, #22c55e);--sd-pdf-danger-soft: rgba(239, 68, 68, .08);--sd-pdf-danger-border: rgba(239, 68, 68, .25);--sd-pdf-danger-tint: rgba(239, 68, 68, .12);--sd-pdf-hover-bg: rgba(0, 0, 0, .06);--sd-pdf-search-hi: rgba(250, 204, 21, .55);--sd-pdf-search-hi-active: rgba(250, 204, 21, .9)}:host(.sd-preview-pdf-host--fullscreen){width:100vw;height:100vh;min-height:0}.sd-preview-pdf-shell{position:relative;display:flex;flex-direction:column;width:100%;height:100%;background:var(--sd-pdf-shell-bg);color:var(--sd-pdf-fg);border-radius:12px;overflow:hidden;box-shadow:0 30px 80px #0000008c,0 0 0 1px #ffffff0a;font-family:inherit}.sd-preview-pdf-header{display:flex;align-items:center;justify-content:space-between;gap:12px;height:57px;padding:0 12px;background:var(--sd-pdf-toolbar-bg);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border-bottom:1px solid var(--sd-pdf-divider);flex:0 0 auto;z-index:2}.sd-preview-pdf-header__left{display:flex;align-items:center;gap:8px;min-width:0;flex:1 1 auto}.sd-preview-pdf-header__right{display:flex;align-items:center;gap:4px;flex-shrink:0}.sd-preview-pdf-header__divider{display:inline-block;width:1px;height:20px;background:var(--sd-pdf-divider)}.sd-preview-pdf-header__meta{display:flex;flex-direction:column;min-width:0}.sd-preview-pdf-header__name{font-size:14px;font-weight:500;color:var(--sd-pdf-fg);max-width:320px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sd-preview-pdf-header__sub{font-size:11px;color:var(--sd-pdf-fg-muted);margin-top:2px}.sd-preview-pdf-fileicon{display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;border-radius:6px;background:var(--sd-pdf-danger-tint);border:1px solid var(--sd-pdf-danger-border);color:var(--sd-error-light, #f87171);font:700 10px/1 ui-monospace,SFMono-Regular,Menlo,monospace}.sd-preview-pdf-iconbtn{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-width:32px;height:32px;padding:0 8px;background:transparent;border:none;color:var(--sd-pdf-fg);border-radius:6px;cursor:pointer;transition:background .12s,color .12s;white-space:nowrap;flex-shrink:0}.sd-preview-pdf-iconbtn mat-icon{font-size:18px;width:18px;height:18px}.sd-preview-pdf-iconbtn__label{font-size:13px;font-weight:500}.sd-preview-pdf-iconbtn:hover:not(:disabled){background:var(--sd-pdf-hover-bg)}.sd-preview-pdf-iconbtn:focus-visible{outline:2px solid var(--sd-pdf-accent);outline-offset:2px}.sd-preview-pdf-iconbtn:disabled{opacity:.35;cursor:not-allowed}.sd-preview-pdf-iconbtn--active{background:var(--sd-pdf-accent-soft);color:var(--sd-pdf-accent)}.sd-preview-pdf-searchbar{display:flex;align-items:center;gap:6px;height:46px;padding:0 12px;background:var(--sd-pdf-toolbar-bg);border-bottom:1px solid var(--sd-pdf-divider);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);flex:0 0 auto;z-index:2}.sd-preview-pdf-searchbar__icon{color:var(--sd-pdf-fg-muted);font-size:18px;width:18px;height:18px;flex-shrink:0}.sd-preview-pdf-searchbar__input{flex:0 1 360px;min-width:160px;height:30px;background:#ffffff0f;border:1px solid rgba(255,255,255,.08);color:var(--sd-pdf-fg);border-radius:6px;padding:0 10px;font-size:13px;outline:none}.sd-preview-pdf-searchbar__input::placeholder{color:var(--sd-pdf-fg-subtle)}.sd-preview-pdf-searchbar__input:focus{border-color:var(--sd-pdf-accent);background:#ffffff17}.sd-preview-pdf-searchbar__counter{color:var(--sd-pdf-fg-muted);font-size:12px;font-variant-numeric:tabular-nums;min-width:48px;text-align:center;flex-shrink:0}.sd-preview-pdf-searchbar__divider{width:1px;height:20px;background:var(--sd-pdf-divider);flex-shrink:0}.sd-preview-pdf-searchbar__spacer{flex:1 1 auto}.sd-pdf-search-overlay{position:absolute;top:8px;left:50%;transform:translate(-50%);display:flex;flex-wrap:wrap;gap:4px;max-width:80%;pointer-events:none;z-index:5}.sd-pdf-search-hi{display:inline-block;padding:1px 4px;border-radius:2px;background:var(--sd-pdf-search-hi);color:#000;font-size:12px;font-weight:500}.sd-pdf-search-hi--active{background:var(--sd-pdf-search-hi-active);color:#fff;box-shadow:0 0 0 2px #fff6}.sd-preview-pdf-body{position:relative;flex:1 1 auto;min-height:0;display:flex;flex-direction:row;overflow:hidden}.sd-preview-pdf-sidebar{width:240px;display:flex;flex-direction:column;background:var(--sd-pdf-sidebar-bg);border-right:1px solid var(--sd-pdf-divider);flex-shrink:0;min-height:0}.sd-preview-pdf-sidebar__tabs{display:flex;gap:4px;padding:8px;border-bottom:1px solid var(--sd-pdf-divider)}.sd-preview-pdf-sidebar__tab{flex:1 1 0;display:inline-flex;align-items:center;justify-content:center;gap:4px;height:32px;padding:0 4px;background:transparent;border:none;color:var(--sd-pdf-fg-muted);border-radius:6px;cursor:pointer;font-size:12px;font-weight:500;white-space:nowrap;flex-shrink:0;transition:background .12s,color .12s}.sd-preview-pdf-sidebar__tab mat-icon{font-size:14px;width:14px;height:14px}.sd-preview-pdf-sidebar__tab:hover{background:#ffffff0f;color:var(--sd-pdf-fg)}.sd-preview-pdf-sidebar__tab--active{background:var(--sd-pdf-accent-soft);color:var(--sd-pdf-accent)}.sd-preview-pdf-sidebar__content{flex:1 1 auto;overflow:auto;padding:12px}.sd-preview-pdf-sidebar__deferred{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px;padding:32px 16px;text-align:center;color:var(--sd-pdf-fg-subtle)}.sd-preview-pdf-sidebar__deferred mat-icon{font-size:28px;width:28px;height:28px;opacity:.6}.sd-preview-pdf-sidebar__deferred p{margin:0;font-size:12px;line-height:1.5}.sd-preview-pdf-thumb-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:12px}.sd-preview-pdf-thumb{display:flex;flex-direction:column;align-items:center;gap:4px;width:100%;padding:0;background:transparent;border:none;cursor:pointer}.sd-preview-pdf-thumb__page{width:152px;aspect-ratio:612/792;background:var(--sd-pdf-page-bg);border-radius:2px;outline:1px solid rgba(255,255,255,.08);outline-offset:-1px;box-shadow:0 2px 6px #0000004d;display:flex;align-items:center;justify-content:center;overflow:hidden;-webkit-user-select:none;user-select:none;background-image:linear-gradient(90deg,#0000,#0000000a,#0000);background-size:200% 100%;animation:sd-preview-pdf-thumb-skel 1.6s ease-in-out infinite}.sd-preview-pdf-thumb__canvas{display:block;max-width:100%;max-height:100%;background:var(--sd-pdf-page-bg)}.sd-preview-pdf-thumb__label{font-size:11px;color:var(--sd-pdf-fg-muted);font-variant-numeric:tabular-nums}.sd-preview-pdf-thumb--active .sd-preview-pdf-thumb__page{outline:2px solid var(--sd-pdf-accent);outline-offset:-2px}.sd-preview-pdf-thumb--active .sd-preview-pdf-thumb__label{color:var(--sd-pdf-accent);font-weight:600}.sd-preview-pdf-thumb:focus-visible .sd-preview-pdf-thumb__page{outline:2px solid var(--sd-pdf-accent);outline-offset:2px}.sd-preview-pdf-stage{position:relative;flex:1 1 auto;min-width:0;min-height:0;background:var(--sd-pdf-shell-bg);background-image:linear-gradient(45deg,rgba(255,255,255,.015) 25%,transparent 25%),linear-gradient(-45deg,rgba(255,255,255,.015) 25%,transparent 25%),linear-gradient(45deg,transparent 75%,rgba(255,255,255,.015) 75%),linear-gradient(-45deg,transparent 75%,rgba(255,255,255,.015) 75%);background-size:24px 24px;background-position:0 0,0 12px,12px -12px,-12px 0;overflow:auto;padding:32px 24px 96px;display:flex;justify-content:center;align-items:flex-start}:host([data-theme=light]) .sd-preview-pdf-stage{background-image:linear-gradient(45deg,rgba(0,0,0,.015) 25%,transparent 25%),linear-gradient(-45deg,rgba(0,0,0,.015) 25%,transparent 25%),linear-gradient(45deg,transparent 75%,rgba(0,0,0,.015) 75%),linear-gradient(-45deg,transparent 75%,rgba(0,0,0,.015) 75%)}.sd-preview-pdf-page-wrap{display:inline-block;position:relative}.sd-preview-pdf-page{display:block;background:var(--sd-pdf-page-bg);border-radius:2px;box-shadow:var(--sd-pdf-page-shadow);max-width:100%}.sd-preview-pdf-toolbar{position:absolute;bottom:20px;left:50%;transform:translate(-50%);display:flex;align-items:center;gap:2px;padding:5px 6px;background:var(--sd-pdf-toolbar-bg);backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px);border:1px solid var(--sd-pdf-divider);border-radius:10px;box-shadow:0 8px 30px #0006;z-index:4;max-width:calc(100% - 32px);overflow-x:auto}.sd-preview-pdf-toolbar__btn{display:inline-flex;align-items:center;justify-content:center;gap:4px;min-width:30px;height:28px;padding:0 6px;background:transparent;border:none;color:var(--sd-pdf-fg);border-radius:6px;cursor:pointer;white-space:nowrap;flex-shrink:0;transition:background .12s,color .12s}.sd-preview-pdf-toolbar__btn mat-icon{font-size:18px;width:18px;height:18px}.sd-preview-pdf-toolbar__btn span{font-size:13px;font-weight:500}.sd-preview-pdf-toolbar__btn--with-label{padding:0 10px}.sd-preview-pdf-toolbar__btn--active{background:var(--sd-pdf-accent-soft);color:var(--sd-pdf-accent)}.sd-preview-pdf-toolbar__btn:hover:not(:disabled):not(.sd-preview-pdf-toolbar__btn--active){background:var(--sd-pdf-hover-bg)}.sd-preview-pdf-toolbar__btn:focus-visible{outline:2px solid var(--sd-pdf-accent);outline-offset:2px}.sd-preview-pdf-toolbar__btn:disabled{opacity:.35;cursor:not-allowed}.sd-preview-pdf-toolbar__page{display:inline-flex;align-items:center;gap:4px;padding:0 6px;flex-shrink:0}.sd-preview-pdf-toolbar__page-input{width:36px;height:24px;background:#ffffff14;border:1px solid rgba(255,255,255,.08);color:var(--sd-pdf-fg);border-radius:4px;text-align:center;font-size:13px;font-variant-numeric:tabular-nums;outline:none}.sd-preview-pdf-toolbar__page-input:focus{border-color:var(--sd-pdf-accent)}.sd-preview-pdf-toolbar__page-total{font-size:13px;color:var(--sd-pdf-fg-muted);font-variant-numeric:tabular-nums}.sd-preview-pdf-toolbar__zoom{min-width:56px;text-align:center;color:var(--sd-pdf-fg);font-size:13px;font-weight:500;font-variant-numeric:tabular-nums;background:#ffffff14;border-radius:4px;padding:2px 8px}.sd-preview-pdf-toolbar__divider{width:1px;height:20px;background:var(--sd-pdf-divider);margin:0 4px;flex-shrink:0}.sd-preview-pdf-status{margin:auto;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:16px;text-align:center;padding:32px;max-width:420px}.sd-preview-pdf-status__tile{width:96px;height:96px;border-radius:24px;display:flex;align-items:center;justify-content:center;border:1px solid var(--sd-pdf-divider)}.sd-preview-pdf-status__tile mat-icon{font-size:48px;width:48px;height:48px}.sd-preview-pdf-status__tile--muted{background:#ffffff0d}.sd-preview-pdf-status__tile--muted mat-icon{color:var(--sd-pdf-fg-subtle)}.sd-preview-pdf-status__tile--danger{background:var(--sd-pdf-danger-soft);border-color:var(--sd-pdf-danger-border)}.sd-preview-pdf-status__tile--danger mat-icon{color:var(--sd-pdf-danger)}.sd-preview-pdf-status__title{font-size:16px;font-weight:500;color:var(--sd-pdf-fg)}.sd-preview-pdf-status__hint{font-size:13px;color:var(--sd-pdf-fg-muted);line-height:1.5}.sd-preview-pdf-status__actions{display:flex;gap:8px;margin-top:4px}.sd-preview-pdf-progress-pct{font-variant-numeric:tabular-nums;color:var(--sd-pdf-fg-muted)}.sd-preview-pdf-progress{width:240px;height:4px;background:#ffffff14;border-radius:999px;overflow:hidden}.sd-preview-pdf-progress__fill{height:100%;background:var(--sd-pdf-accent);transition:width .12s linear}.sd-preview-pdf-spinner{width:56px;height:56px;border-radius:999px;border:3px solid rgba(255,255,255,.08);border-top-color:var(--sd-pdf-accent);animation:sd-preview-pdf-spin .9s linear infinite}@keyframes sd-preview-pdf-spin{to{transform:rotate(360deg)}}@keyframes sd-preview-pdf-thumb-skel{0%{background-position:200% 0}to{background-position:-200% 0}}.sd-preview-pdf-search-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:6px}.sd-preview-pdf-search-result{width:100%;display:flex;align-items:flex-start;gap:8px;padding:8px 10px;background:#ffffff0a;border:1px solid transparent;border-radius:6px;color:var(--sd-pdf-fg);cursor:pointer;text-align:left;transition:background .12s,border-color .12s}.sd-preview-pdf-search-result:hover{background:#ffffff12}.sd-preview-pdf-search-result--active{background:var(--sd-pdf-accent-soft);border-color:#3b82f673}.sd-preview-pdf-search-result__snippet{flex:1 1 auto;font-size:12px;line-height:1.4;color:var(--sd-pdf-fg);word-break:break-word}.sd-preview-pdf-search-result__snippet mark{background:var(--sd-pdf-search-hi);color:#000;padding:0 2px;border-radius:2px}.sd-preview-pdf-search-result__page{flex:0 0 auto;font-size:11px;font-variant-numeric:tabular-nums;color:var(--sd-pdf-fg-muted);background:#ffffff0f;padding:2px 6px;border-radius:4px}.sd-preview-pdf-btn-primary{display:inline-flex;align-items:center;gap:6px;height:36px;padding:0 16px;background:var(--sd-pdf-accent);color:#fff;border:none;border-radius:8px;cursor:pointer;font-size:13px;font-weight:500;transition:filter .12s}.sd-preview-pdf-btn-primary mat-icon{font-size:18px;width:18px;height:18px}.sd-preview-pdf-btn-primary:hover{filter:brightness(1.1)}.sd-preview-pdf-btn-primary:focus-visible{outline:2px solid #fff;outline-offset:2px}.sd-preview-pdf-btn-secondary{display:inline-flex;align-items:center;gap:6px;height:36px;padding:0 16px;background:transparent;color:var(--sd-pdf-fg);border:1px solid var(--sd-pdf-divider);border-radius:8px;cursor:pointer;font-size:13px;font-weight:500;transition:background .12s}.sd-preview-pdf-btn-secondary mat-icon{font-size:18px;width:18px;height:18px}.sd-preview-pdf-btn-secondary:hover{background:#ffffff0f}.sd-preview-pdf-btn-secondary:focus-visible{outline:2px solid var(--sd-pdf-accent);outline-offset:2px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1966
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.21", type: SdPreviewPdf, isStandalone: true, selector: "sd-preview-pdf", inputs: { autoIdInput: { classPropertyName: "autoIdInput", publicName: "autoId", isSignal: true, isRequired: false, transformFunction: null }, source: { classPropertyName: "source", publicName: "source", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, startPage: { classPropertyName: "startPage", publicName: "startPage", isSignal: true, isRequired: false, transformFunction: null }, initialZoom: { classPropertyName: "initialZoom", publicName: "initialZoom", isSignal: true, isRequired: false, transformFunction: null }, sidebar: { classPropertyName: "sidebar", publicName: "sidebar", isSignal: true, isRequired: false, transformFunction: null }, sidebarOpen: { classPropertyName: "sidebarOpen", publicName: "sidebarOpen", isSignal: true, isRequired: false, transformFunction: null }, scrollMode: { classPropertyName: "scrollMode", publicName: "scrollMode", isSignal: true, isRequired: false, transformFunction: null }, showToolbar: { classPropertyName: "showToolbar", publicName: "showToolbar", isSignal: true, isRequired: false, transformFunction: null }, downloadable: { classPropertyName: "downloadable", publicName: "downloadable", isSignal: true, isRequired: false, transformFunction: null }, password: { classPropertyName: "password", publicName: "password", isSignal: true, isRequired: false, transformFunction: null }, httpHeaders: { classPropertyName: "httpHeaders", publicName: "httpHeaders", isSignal: true, isRequired: false, transformFunction: null }, theme: { classPropertyName: "theme", publicName: "theme", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { close: "close", loaded: "loaded", pageChange: "pageChange", zoomChange: "zoomChange", download: "download", loadError: "loadError", searchChange: "searchChange" }, host: { attributes: { "tabindex": "0" }, listeners: { "keydown": "onKeyDown($event)" }, properties: { "attr.data-theme": "theme()", "class.sd-preview-pdf-host--fullscreen": "isFullscreen()" }, classAttribute: "sd-preview-pdf-host" }, viewQueries: [{ propertyName: "pageCanvasRef", first: true, predicate: ["pageCanvas"], descendants: true, isSignal: true }, { propertyName: "stageEl", first: true, predicate: ["stageRef"], descendants: true, isSignal: true }, { propertyName: "pageInputRef", first: true, predicate: ["pageInput"], descendants: true, isSignal: true }, { propertyName: "searchInputRef", first: true, predicate: ["searchInput"], descendants: true, isSignal: true }, { propertyName: "thumbCanvases", predicate: ["thumbCanvas"], descendants: true, isSignal: true }], ngImport: i0, template: "<!--\n sd-preview-pdf shell. Self-contained \u2014 sizes to its parent. Consumer wraps\n in <sd-modal>/<sd-side-drawer>/page section. Header + sidebar + stage +\n floating toolbar mirror Artboards A / C / E from the design handoff.\n-->\n<div class=\"sd-preview-pdf-shell\" role=\"region\">\n @let _stage = stage();\n @let _numPages = numPages();\n @let _activePage = activePage();\n @let _isSidebarOpen = isSidebarOpen();\n @let _searchBarOpen = searchBarOpen();\n @let _searchTerm = searchTerm();\n @let _searchTotal = searchTotal();\n\n <!-- ============================================\n HEADER BAR \u2014 hidden in empty + loading per design\n ============================================ -->\n @if (_stage === 'ready') {\n <div class=\"sd-preview-pdf-header\">\n <div class=\"sd-preview-pdf-header__left\">\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [class.sd-preview-pdf-iconbtn--active]=\"_isSidebarOpen\"\n [attr.aria-label]=\"'core.component.preview-pdf.toggle-sidebar' | translate\"\n [attr.title]=\"'core.component.preview-pdf.toggle-sidebar' | translate\"\n [attr.data-autoId]=\"autoIdSidebarToggle()\"\n (click)=\"toggleSidebar()\">\n <sd-icon fontIcon=\"menu\"></sd-icon>\n </button>\n <span class=\"sd-preview-pdf-header__divider\"></span>\n <div class=\"sd-preview-pdf-fileicon\" aria-hidden=\"true\">PDF</div>\n <div class=\"sd-preview-pdf-header__meta\">\n <div class=\"sd-preview-pdf-header__name\" [title]=\"title() || filename()\">\n {{ title() || filename() }}\n </div>\n <div class=\"sd-preview-pdf-header__sub\">\n {{ 'core.component.preview-pdf.pages-count' | translate: { count: _numPages } }}\n </div>\n </div>\n </div>\n\n <div class=\"sd-preview-pdf-header__right\">\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [class.sd-preview-pdf-iconbtn--active]=\"_searchBarOpen\"\n [attr.aria-label]=\"'core.component.preview-pdf.search' | translate\"\n [attr.title]=\"'core.component.preview-pdf.search' | translate\"\n [attr.data-autoId]=\"autoIdSearchToggle()\"\n (click)=\"searchBarOpen() ? closeSearch() : openSearch()\">\n <sd-icon fontIcon=\"search\"></sd-icon>\n </button>\n @if (downloadable()) {\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [attr.aria-label]=\"'core.component.preview-pdf.download' | translate\"\n [attr.title]=\"'core.component.preview-pdf.download' | translate\"\n [attr.data-autoId]=\"autoIdDownload()\"\n (click)=\"downloadFile()\">\n <sd-icon fontIcon=\"download\"></sd-icon>\n </button>\n }\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [attr.aria-label]=\"'core.component.preview-pdf.fullscreen' | translate\"\n [attr.title]=\"'core.component.preview-pdf.fullscreen' | translate\"\n [attr.data-autoId]=\"autoIdFullscreen()\"\n (click)=\"toggleFullscreen()\">\n <sd-icon [fontIcon]=\"isFullscreen() ? 'fullscreen_exit' : 'fullscreen'\"></sd-icon>\n </button>\n </div>\n </div>\n\n <!-- ============================================\n SEARCH BAR \u2014 between header + body, only when bar is open\n ============================================ -->\n @if (_searchBarOpen) {\n <div class=\"sd-preview-pdf-searchbar\" role=\"search\">\n <sd-icon class=\"sd-preview-pdf-searchbar__icon\" fontIcon=\"search\"></sd-icon>\n <input\n #searchInput\n type=\"text\"\n class=\"sd-preview-pdf-searchbar__input\"\n [attr.placeholder]=\"'core.component.preview-pdf.search-placeholder' | translate\"\n [value]=\"_searchTerm\"\n [attr.data-autoId]=\"autoIdSearchInput()\"\n (input)=\"onSearchInput($any($event.target).value)\" />\n <span class=\"sd-preview-pdf-searchbar__counter\" aria-live=\"polite\"> {{ searchCurrent() }} / {{ _searchTotal }} </span>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [disabled]=\"_searchTotal === 0\"\n [attr.aria-label]=\"'core.component.preview-pdf.search-prev' | translate\"\n [attr.title]=\"'core.component.preview-pdf.search-prev' | translate\"\n [attr.data-autoId]=\"autoIdSearchPrev()\"\n (click)=\"searchPrev()\">\n <sd-icon fontIcon=\"keyboard_arrow_up\"></sd-icon>\n </button>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [disabled]=\"_searchTotal === 0\"\n [attr.aria-label]=\"'core.component.preview-pdf.search-next' | translate\"\n [attr.title]=\"'core.component.preview-pdf.search-next' | translate\"\n [attr.data-autoId]=\"autoIdSearchNext()\"\n (click)=\"searchNext()\">\n <sd-icon fontIcon=\"keyboard_arrow_down\"></sd-icon>\n </button>\n <span class=\"sd-preview-pdf-searchbar__divider\"></span>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [class.sd-preview-pdf-iconbtn--active]=\"searchCaseSensitive()\"\n [attr.aria-label]=\"'core.component.preview-pdf.search-case' | translate\"\n [attr.title]=\"'core.component.preview-pdf.search-case' | translate\"\n [attr.data-autoId]=\"autoIdSearchCase()\"\n (click)=\"toggleSearchCaseSensitive()\">\n <sd-icon fontIcon=\"match_case\"></sd-icon>\n </button>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [class.sd-preview-pdf-iconbtn--active]=\"searchWholeWord()\"\n [attr.aria-label]=\"'core.component.preview-pdf.search-whole' | translate\"\n [attr.title]=\"'core.component.preview-pdf.search-whole' | translate\"\n [attr.data-autoId]=\"autoIdSearchWhole()\"\n (click)=\"toggleSearchWholeWord()\">\n <sd-icon fontIcon=\"text_fields\"></sd-icon>\n </button>\n <span class=\"sd-preview-pdf-searchbar__spacer\"></span>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [attr.aria-label]=\"'core.component.preview-pdf.search-close' | translate\"\n [attr.title]=\"'core.component.preview-pdf.search-close' | translate\"\n [attr.data-autoId]=\"autoIdSearchClose()\"\n (click)=\"closeSearch()\">\n <sd-icon fontIcon=\"close\"></sd-icon>\n </button>\n </div>\n }\n }\n\n <!-- ============================================\n BODY \u2014 sidebar + stage\n ============================================ -->\n <div class=\"sd-preview-pdf-body\">\n <!-- SIDEBAR ............................................................. -->\n @if (_stage === 'ready' && _isSidebarOpen) {\n <aside class=\"sd-preview-pdf-sidebar\" role=\"complementary\">\n @let _sidebarMode = sidebarMode();\n <div class=\"sd-preview-pdf-sidebar__tabs\" role=\"tablist\">\n <button\n type=\"button\"\n class=\"sd-preview-pdf-sidebar__tab\"\n role=\"tab\"\n [class.sd-preview-pdf-sidebar__tab--active]=\"_sidebarMode === 'thumbnails'\"\n [attr.data-autoId]=\"autoIdTabThumbnails()\"\n (click)=\"setSidebarMode('thumbnails')\">\n <sd-icon fontIcon=\"view_carousel\"></sd-icon>\n <span>{{ 'core.component.preview-pdf.tab.thumbnails' | translate }}</span>\n </button>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-sidebar__tab\"\n role=\"tab\"\n [class.sd-preview-pdf-sidebar__tab--active]=\"_sidebarMode === 'outline'\"\n [attr.data-autoId]=\"autoIdTabOutline()\"\n (click)=\"setSidebarMode('outline')\">\n <sd-icon fontIcon=\"list\"></sd-icon>\n <span>{{ 'core.component.preview-pdf.tab.outline' | translate }}</span>\n </button>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-sidebar__tab\"\n role=\"tab\"\n [class.sd-preview-pdf-sidebar__tab--active]=\"_sidebarMode === 'search'\"\n [attr.data-autoId]=\"autoIdTabSearch()\"\n (click)=\"setSidebarMode('search')\">\n <sd-icon fontIcon=\"search\"></sd-icon>\n <span>{{ 'core.component.preview-pdf.tab.search' | translate }}</span>\n </button>\n </div>\n\n <div class=\"sd-preview-pdf-sidebar__content\">\n @switch (_sidebarMode) {\n @case ('thumbnails') {\n <ul class=\"sd-preview-pdf-thumb-list\">\n @for (p of pageNumbers(); track trackByPage($index, p); let pi = $index) {\n <li>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-thumb\"\n [class.sd-preview-pdf-thumb--active]=\"p === _activePage\"\n [attr.aria-current]=\"p === _activePage ? 'page' : null\"\n [attr.aria-label]=\"'core.component.preview-pdf.page-of' | translate: { current: p, total: _numPages }\"\n [attr.data-autoId]=\"autoIdThumb(pi)\"\n (click)=\"goToPage(p)\">\n <div class=\"sd-preview-pdf-thumb__page\">\n <canvas #thumbCanvas class=\"sd-preview-pdf-thumb__canvas\" [attr.data-page]=\"p\" aria-hidden=\"true\"></canvas>\n </div>\n <span class=\"sd-preview-pdf-thumb__label\">{{ p }}</span>\n </button>\n </li>\n }\n </ul>\n }\n @case ('outline') {\n <!-- TODO(preview-pdf): outline tree (pdfDoc.getOutline()) deferred to follow-up commit. -->\n <div class=\"sd-preview-pdf-sidebar__deferred\">\n <sd-icon fontIcon=\"list\"></sd-icon>\n <p>{{ 'core.component.preview-pdf.deferred.outline' | translate }}</p>\n </div>\n }\n @case ('search') {\n @if (!_searchTerm) {\n <div class=\"sd-preview-pdf-sidebar__deferred\">\n <sd-icon fontIcon=\"search\"></sd-icon>\n <p>{{ 'core.component.preview-pdf.search-placeholder' | translate }}</p>\n </div>\n } @else if (_searchTotal === 0) {\n <div class=\"sd-preview-pdf-sidebar__deferred\">\n <sd-icon fontIcon=\"search_off\"></sd-icon>\n <p>{{ 'core.component.preview-pdf.search-no-results' | translate }}</p>\n </div>\n } @else {\n <ul class=\"sd-preview-pdf-search-list\">\n @for (r of searchResults(); track $index; let i = $index) {\n <li>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-search-result\"\n [class.sd-preview-pdf-search-result--active]=\"i === searchActiveIndex()\"\n [attr.data-autoId]=\"autoIdResult(i)\"\n (click)=\"goToPage(r.page)\">\n <span class=\"sd-preview-pdf-search-result__snippet\">\n {{ r.before }}<mark>{{ r.term }}</mark\n >{{ r.after }}\n </span>\n <span class=\"sd-preview-pdf-search-result__page\">{{ r.page }}</span>\n </button>\n </li>\n }\n </ul>\n }\n }\n @default {\n <div class=\"sd-preview-pdf-sidebar__deferred\"></div>\n }\n }\n </div>\n </aside>\n }\n\n <!-- STAGE ............................................................... -->\n <div class=\"sd-preview-pdf-stage\" #stageRef (wheel)=\"onWheel($event)\">\n @switch (_stage) {\n @case ('empty') {\n <div class=\"sd-preview-pdf-status\">\n <div class=\"sd-preview-pdf-status__tile sd-preview-pdf-status__tile--muted\">\n <sd-icon fontIcon=\"picture_as_pdf\"></sd-icon>\n </div>\n <div class=\"sd-preview-pdf-status__title\">\n {{ 'core.component.preview-pdf.empty.title' | translate }}\n </div>\n <div class=\"sd-preview-pdf-status__hint\">\n {{ 'core.component.preview-pdf.empty.subtitle' | translate }}\n </div>\n </div>\n }\n @case ('loading') {\n @let _loadPercent = loadPercent();\n <div class=\"sd-preview-pdf-status\">\n <div class=\"sd-preview-pdf-spinner\" role=\"progressbar\"></div>\n <div class=\"sd-preview-pdf-status__title\">\n {{ 'core.component.preview-pdf.loading' | translate }}\n @if (_loadPercent > 0) {\n <span class=\"sd-preview-pdf-progress-pct\"> {{ _loadPercent }}%</span>\n }\n </div>\n <div class=\"sd-preview-pdf-progress\">\n <div class=\"sd-preview-pdf-progress__fill\" [style.width.%]=\"_loadPercent\"></div>\n </div>\n </div>\n }\n @case ('error') {\n @if (loadError$(); as err) {\n <div class=\"sd-preview-pdf-status\">\n <div class=\"sd-preview-pdf-status__tile sd-preview-pdf-status__tile--danger\">\n <sd-icon fontIcon=\"error\"></sd-icon>\n </div>\n <div class=\"sd-preview-pdf-status__title\">\n {{ 'core.component.preview-pdf.error.' + err.reason + '.title' | translate }}\n </div>\n <div class=\"sd-preview-pdf-status__hint\">\n {{ 'core.component.preview-pdf.error.' + err.reason + '.body' | translate }}\n </div>\n <div class=\"sd-preview-pdf-status__actions\">\n <button type=\"button\" class=\"sd-preview-pdf-btn-primary\" (click)=\"retryLoad()\" [attr.data-autoId]=\"autoIdRetry()\">\n <sd-icon fontIcon=\"refresh\"></sd-icon>\n <span>{{ 'core.component.preview-pdf.error.retry' | translate }}</span>\n </button>\n @if (downloadable()) {\n <button type=\"button\" class=\"sd-preview-pdf-btn-secondary\" (click)=\"downloadFile()\">\n <sd-icon fontIcon=\"download\"></sd-icon>\n <span>{{ 'core.component.preview-pdf.error.download-raw' | translate }}</span>\n </button>\n }\n </div>\n </div>\n }\n }\n @default {\n <div class=\"sd-preview-pdf-page-wrap\">\n <canvas\n #pageCanvas\n class=\"sd-preview-pdf-page\"\n role=\"img\"\n [attr.aria-label]=\"'core.component.preview-pdf.page-of' | translate: { current: _activePage, total: _numPages }\">\n </canvas>\n </div>\n }\n }\n\n <!-- FLOATING TOOLBAR ........................................ -->\n @if (showToolbar() && _stage === 'ready') {\n @let _canPrev = canPrev();\n @let _canNext = canNext();\n @let _zoomMode = zoomMode();\n @let _scrollModeCurrent = scrollModeCurrent();\n <div class=\"sd-preview-pdf-toolbar\" role=\"toolbar\">\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn\"\n [disabled]=\"!_canPrev\"\n [attr.aria-label]=\"'core.component.preview-pdf.first-page' | translate\"\n [attr.title]=\"'core.component.preview-pdf.first-page' | translate\"\n [attr.data-autoId]=\"autoIdFirst()\"\n (click)=\"firstPage()\">\n <sd-icon fontIcon=\"first_page\"></sd-icon>\n </button>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn\"\n [disabled]=\"!_canPrev\"\n [attr.aria-label]=\"'core.component.preview-pdf.prev-page' | translate\"\n [attr.title]=\"'core.component.preview-pdf.prev-page' | translate\"\n [attr.data-autoId]=\"autoIdPrev()\"\n (click)=\"prevPage()\">\n <sd-icon fontIcon=\"chevron_left\"></sd-icon>\n </button>\n <span class=\"sd-preview-pdf-toolbar__page\">\n <input\n type=\"text\"\n inputmode=\"numeric\"\n class=\"sd-preview-pdf-toolbar__page-input\"\n [value]=\"_activePage\"\n #pageInput\n (keydown.enter)=\"onPageInputEnter($event)\"\n (blur)=\"onPageInputBlur($event)\"\n [attr.aria-label]=\"'core.component.preview-pdf.page-of' | translate: { current: _activePage, total: _numPages }\"\n [attr.data-autoId]=\"autoIdPageInput()\" />\n <span class=\"sd-preview-pdf-toolbar__page-total\">/ {{ _numPages }}</span>\n </span>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn\"\n [disabled]=\"!_canNext\"\n [attr.aria-label]=\"'core.component.preview-pdf.next-page' | translate\"\n [attr.title]=\"'core.component.preview-pdf.next-page' | translate\"\n [attr.data-autoId]=\"autoIdNext()\"\n (click)=\"nextPage()\">\n <sd-icon fontIcon=\"chevron_right\"></sd-icon>\n </button>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn\"\n [disabled]=\"!_canNext\"\n [attr.aria-label]=\"'core.component.preview-pdf.last-page' | translate\"\n [attr.title]=\"'core.component.preview-pdf.last-page' | translate\"\n [attr.data-autoId]=\"autoIdLast()\"\n (click)=\"lastPage()\">\n <sd-icon fontIcon=\"last_page\"></sd-icon>\n </button>\n\n <span class=\"sd-preview-pdf-toolbar__divider\"></span>\n\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn\"\n [attr.aria-label]=\"'core.component.preview-pdf.zoom-out' | translate\"\n [attr.title]=\"'core.component.preview-pdf.zoom-out' | translate\"\n [attr.data-autoId]=\"autoIdZoomOut()\"\n (click)=\"zoomOut()\">\n <sd-icon fontIcon=\"remove\"></sd-icon>\n </button>\n <span class=\"sd-preview-pdf-toolbar__zoom\" aria-live=\"polite\" [attr.data-autoId]=\"autoIdZoomReadout()\">{{ zoomPercent() }}%</span>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn\"\n [attr.aria-label]=\"'core.component.preview-pdf.zoom-in' | translate\"\n [attr.title]=\"'core.component.preview-pdf.zoom-in' | translate\"\n [attr.data-autoId]=\"autoIdZoomIn()\"\n (click)=\"zoomIn()\">\n <sd-icon fontIcon=\"add\"></sd-icon>\n </button>\n\n <span class=\"sd-preview-pdf-toolbar__divider\"></span>\n\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn sd-preview-pdf-toolbar__btn--with-label\"\n [class.sd-preview-pdf-toolbar__btn--active]=\"_zoomMode === 'page-fit'\"\n [attr.data-autoId]=\"autoIdFitPage()\"\n (click)=\"setZoom('page-fit')\">\n <sd-icon fontIcon=\"fit_screen\"></sd-icon>\n <span>{{ 'core.component.preview-pdf.fit-page' | translate }}</span>\n </button>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn sd-preview-pdf-toolbar__btn--with-label\"\n [class.sd-preview-pdf-toolbar__btn--active]=\"_zoomMode === 'page-width'\"\n [attr.data-autoId]=\"autoIdFitWidth()\"\n (click)=\"setZoom('page-width')\">\n <sd-icon fontIcon=\"swap_horiz\"></sd-icon>\n <span>{{ 'core.component.preview-pdf.fit-width' | translate }}</span>\n </button>\n\n <span class=\"sd-preview-pdf-toolbar__divider\"></span>\n\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn\"\n [attr.aria-label]=\"'core.component.preview-pdf.rotate' | translate\"\n [attr.title]=\"'core.component.preview-pdf.rotate' | translate\"\n [attr.data-autoId]=\"autoIdRotate()\"\n (click)=\"rotate('right')\">\n <sd-icon fontIcon=\"rotate_right\"></sd-icon>\n </button>\n\n <!-- TODO(preview-pdf): scroll mode toggle is signature-only;\n clicking switches the state but rendering stays single-page\n until the continuous scroll feature lands. -->\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn sd-preview-pdf-toolbar__btn--with-label\"\n [attr.data-autoId]=\"autoIdScrollMode()\"\n (click)=\"setScrollMode(_scrollModeCurrent === 'page' ? 'continuous' : 'page')\">\n <sd-icon [fontIcon]=\"_scrollModeCurrent === 'page' ? 'description' : 'view_day'\"></sd-icon>\n <span>\n {{\n (_scrollModeCurrent === 'page' ? 'core.component.preview-pdf.scroll-page' : 'core.component.preview-pdf.scroll-continuous')\n | translate\n }}\n </span>\n </button>\n </div>\n }\n </div>\n </div>\n</div>\n", styles: [":host{--sd-pdf-shell-bg: #1a1c1f;--sd-pdf-sidebar-bg: #22252a;--sd-pdf-toolbar-bg: rgba(20, 22, 25, .94);--sd-pdf-page-bg: #ffffff;--sd-pdf-page-shadow: 0 4px 16px rgba(0, 0, 0, .4);--sd-pdf-divider: rgba(255, 255, 255, .08);--sd-pdf-fg: #f5f6f7;--sd-pdf-fg-muted: rgba(255, 255, 255, .6);--sd-pdf-fg-subtle: rgba(255, 255, 255, .4);--sd-pdf-primary: var(--sd-pdf-accent, var(--sd-primary, #005cbb));--sd-pdf-primary-soft: var(--sd-pdf-accent-soft, rgba(0, 92, 187, .18));--sd-pdf-danger: var(--sd-error, #ba1a1a);--sd-pdf-success: var(--sd-success, #2e7d32);--sd-pdf-danger-soft: rgba(186, 26, 26, .1);--sd-pdf-danger-border: rgba(186, 26, 26, .3);--sd-pdf-danger-tint: rgba(186, 26, 26, .15);--sd-pdf-hover-bg: rgba(255, 255, 255, .1);--sd-pdf-search-hi: rgba(250, 204, 21, .55);--sd-pdf-search-hi-active: rgba(249, 115, 22, .85);display:block;width:100%;height:100%;min-height:320px;outline:none}:host([data-theme=light]){--sd-pdf-shell-bg: #f7f8fa;--sd-pdf-sidebar-bg: #ffffff;--sd-pdf-toolbar-bg: rgba(255, 255, 255, .94);--sd-pdf-page-bg: #ffffff;--sd-pdf-page-shadow: 0 4px 16px rgba(0, 0, 0, .12);--sd-pdf-divider: rgba(0, 0, 0, .08);--sd-pdf-fg: #1a1c1f;--sd-pdf-fg-muted: rgba(0, 0, 0, .62);--sd-pdf-fg-subtle: rgba(0, 0, 0, .42);--sd-pdf-primary: var(--sd-pdf-accent, var(--sd-primary, #005cbb));--sd-pdf-primary-soft: var(--sd-pdf-accent-soft, rgba(0, 92, 187, .16));--sd-pdf-danger: var(--sd-error, #ba1a1a);--sd-pdf-success: var(--sd-success, #2e7d32);--sd-pdf-danger-soft: rgba(186, 26, 26, .08);--sd-pdf-danger-border: rgba(186, 26, 26, .25);--sd-pdf-danger-tint: rgba(186, 26, 26, .12);--sd-pdf-hover-bg: rgba(0, 0, 0, .06);--sd-pdf-search-hi: rgba(250, 204, 21, .55);--sd-pdf-search-hi-active: rgba(250, 204, 21, .9)}:host(.sd-preview-pdf-host--fullscreen){width:100vw;height:100vh;min-height:0}.sd-preview-pdf-shell{position:relative;display:flex;flex-direction:column;width:100%;height:100%;background:var(--sd-pdf-shell-bg);color:var(--sd-pdf-fg);border-radius:12px;overflow:hidden;box-shadow:0 30px 80px #0000008c,0 0 0 1px #ffffff0a;font-family:inherit}.sd-preview-pdf-header{display:flex;align-items:center;justify-content:space-between;gap:12px;height:57px;padding:0 12px;background:var(--sd-pdf-toolbar-bg);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border-bottom:1px solid var(--sd-pdf-divider);flex:0 0 auto;z-index:2}.sd-preview-pdf-header__left{display:flex;align-items:center;gap:8px;min-width:0;flex:1 1 auto}.sd-preview-pdf-header__right{display:flex;align-items:center;gap:4px;flex-shrink:0}.sd-preview-pdf-header__divider{display:inline-block;width:1px;height:20px;background:var(--sd-pdf-divider)}.sd-preview-pdf-header__meta{display:flex;flex-direction:column;min-width:0}.sd-preview-pdf-header__name{font-size:14px;font-weight:500;color:var(--sd-pdf-fg);max-width:320px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sd-preview-pdf-header__sub{font-size:11px;color:var(--sd-pdf-fg-muted);margin-top:2px}.sd-preview-pdf-fileicon{display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;border-radius:6px;background:var(--sd-pdf-danger-tint);border:1px solid var(--sd-pdf-danger-border);color:var(--sd-error-light, #f87171);font:700 10px/1 ui-monospace,SFMono-Regular,Menlo,monospace}.sd-preview-pdf-iconbtn{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-width:32px;height:32px;padding:0 8px;background:transparent;border:none;color:var(--sd-pdf-fg);border-radius:6px;cursor:pointer;transition:background .12s,color .12s;white-space:nowrap;flex-shrink:0}.sd-preview-pdf-iconbtn sd-icon{font-size:18px;width:18px;height:18px}.sd-preview-pdf-iconbtn__label{font-size:13px;font-weight:500}.sd-preview-pdf-iconbtn:hover:not(:disabled){background:var(--sd-pdf-hover-bg)}.sd-preview-pdf-iconbtn:focus-visible{outline:2px solid var(--sd-pdf-primary);outline-offset:2px}.sd-preview-pdf-iconbtn:disabled{opacity:.35;cursor:not-allowed}.sd-preview-pdf-iconbtn--active{background:var(--sd-pdf-primary-soft);color:var(--sd-pdf-primary)}.sd-preview-pdf-searchbar{display:flex;align-items:center;gap:6px;height:46px;padding:0 12px;background:var(--sd-pdf-toolbar-bg);border-bottom:1px solid var(--sd-pdf-divider);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);flex:0 0 auto;z-index:2}.sd-preview-pdf-searchbar__icon{color:var(--sd-pdf-fg-muted);font-size:18px;width:18px;height:18px;flex-shrink:0}.sd-preview-pdf-searchbar__input{flex:0 1 360px;min-width:160px;height:30px;background:#ffffff0f;border:1px solid rgba(255,255,255,.08);color:var(--sd-pdf-fg);border-radius:6px;padding:0 10px;font-size:13px;outline:none}.sd-preview-pdf-searchbar__input::placeholder{color:var(--sd-pdf-fg-subtle)}.sd-preview-pdf-searchbar__input:focus{border-color:var(--sd-pdf-primary);background:#ffffff17}.sd-preview-pdf-searchbar__counter{color:var(--sd-pdf-fg-muted);font-size:12px;font-variant-numeric:tabular-nums;min-width:48px;text-align:center;flex-shrink:0}.sd-preview-pdf-searchbar__divider{width:1px;height:20px;background:var(--sd-pdf-divider);flex-shrink:0}.sd-preview-pdf-searchbar__spacer{flex:1 1 auto}.sd-pdf-search-overlay{position:absolute;top:8px;left:50%;transform:translate(-50%);display:flex;flex-wrap:wrap;gap:4px;max-width:80%;pointer-events:none;z-index:5}.sd-pdf-search-hi{display:inline-block;padding:1px 4px;border-radius:2px;background:var(--sd-pdf-search-hi);color:#000;font-size:12px;font-weight:500}.sd-pdf-search-hi--active{background:var(--sd-pdf-search-hi-active);color:#fff;box-shadow:0 0 0 2px #fff6}.sd-preview-pdf-body{position:relative;flex:1 1 auto;min-height:0;display:flex;flex-direction:row;overflow:hidden}.sd-preview-pdf-sidebar{width:240px;display:flex;flex-direction:column;background:var(--sd-pdf-sidebar-bg);border-right:1px solid var(--sd-pdf-divider);flex-shrink:0;min-height:0}.sd-preview-pdf-sidebar__tabs{display:flex;gap:4px;padding:8px;border-bottom:1px solid var(--sd-pdf-divider)}.sd-preview-pdf-sidebar__tab{flex:1 1 0;display:inline-flex;align-items:center;justify-content:center;gap:4px;height:32px;padding:0 4px;background:transparent;border:none;color:var(--sd-pdf-fg-muted);border-radius:6px;cursor:pointer;font-size:12px;font-weight:500;white-space:nowrap;flex-shrink:0;transition:background .12s,color .12s}.sd-preview-pdf-sidebar__tab sd-icon{font-size:14px;width:14px;height:14px}.sd-preview-pdf-sidebar__tab:hover{background:#ffffff0f;color:var(--sd-pdf-fg)}.sd-preview-pdf-sidebar__tab--active{background:var(--sd-pdf-primary-soft);color:var(--sd-pdf-primary)}.sd-preview-pdf-sidebar__content{flex:1 1 auto;overflow:auto;padding:12px}.sd-preview-pdf-sidebar__deferred{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px;padding:32px 16px;text-align:center;color:var(--sd-pdf-fg-subtle)}.sd-preview-pdf-sidebar__deferred sd-icon{font-size:28px;width:28px;height:28px;opacity:.6}.sd-preview-pdf-sidebar__deferred p{margin:0;font-size:12px;line-height:1.5}.sd-preview-pdf-thumb-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:12px}.sd-preview-pdf-thumb{display:flex;flex-direction:column;align-items:center;gap:4px;width:100%;padding:0;background:transparent;border:none;cursor:pointer}.sd-preview-pdf-thumb__page{width:152px;aspect-ratio:612/792;background:var(--sd-pdf-page-bg);border-radius:2px;outline:1px solid rgba(255,255,255,.08);outline-offset:-1px;box-shadow:0 2px 6px #0000004d;display:flex;align-items:center;justify-content:center;overflow:hidden;-webkit-user-select:none;user-select:none;background-image:linear-gradient(90deg,#0000,#0000000a,#0000);background-size:200% 100%;animation:sd-preview-pdf-thumb-skel 1.6s ease-in-out infinite}.sd-preview-pdf-thumb__canvas{display:block;max-width:100%;max-height:100%;background:var(--sd-pdf-page-bg)}.sd-preview-pdf-thumb__label{font-size:11px;color:var(--sd-pdf-fg-muted);font-variant-numeric:tabular-nums}.sd-preview-pdf-thumb--active .sd-preview-pdf-thumb__page{outline:2px solid var(--sd-pdf-primary);outline-offset:-2px}.sd-preview-pdf-thumb--active .sd-preview-pdf-thumb__label{color:var(--sd-pdf-primary);font-weight:600}.sd-preview-pdf-thumb:focus-visible .sd-preview-pdf-thumb__page{outline:2px solid var(--sd-pdf-primary);outline-offset:2px}.sd-preview-pdf-stage{position:relative;flex:1 1 auto;min-width:0;min-height:0;background:var(--sd-pdf-shell-bg);background-image:linear-gradient(45deg,rgba(255,255,255,.015) 25%,transparent 25%),linear-gradient(-45deg,rgba(255,255,255,.015) 25%,transparent 25%),linear-gradient(45deg,transparent 75%,rgba(255,255,255,.015) 75%),linear-gradient(-45deg,transparent 75%,rgba(255,255,255,.015) 75%);background-size:24px 24px;background-position:0 0,0 12px,12px -12px,-12px 0;overflow:auto;padding:32px 24px 96px;display:flex;justify-content:center;align-items:flex-start}:host([data-theme=light]) .sd-preview-pdf-stage{background-image:linear-gradient(45deg,rgba(0,0,0,.015) 25%,transparent 25%),linear-gradient(-45deg,rgba(0,0,0,.015) 25%,transparent 25%),linear-gradient(45deg,transparent 75%,rgba(0,0,0,.015) 75%),linear-gradient(-45deg,transparent 75%,rgba(0,0,0,.015) 75%)}.sd-preview-pdf-page-wrap{display:inline-block;position:relative}.sd-preview-pdf-page{display:block;background:var(--sd-pdf-page-bg);border-radius:2px;box-shadow:var(--sd-pdf-page-shadow);max-width:100%}.sd-preview-pdf-toolbar{position:absolute;bottom:20px;left:50%;transform:translate(-50%);display:flex;align-items:center;gap:2px;padding:5px 6px;background:var(--sd-pdf-toolbar-bg);backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px);border:1px solid var(--sd-pdf-divider);border-radius:10px;box-shadow:0 8px 30px #0006;z-index:4;max-width:calc(100% - 32px);overflow-x:auto}.sd-preview-pdf-toolbar__btn{display:inline-flex;align-items:center;justify-content:center;gap:4px;min-width:30px;height:28px;padding:0 6px;background:transparent;border:none;color:var(--sd-pdf-fg);border-radius:6px;cursor:pointer;white-space:nowrap;flex-shrink:0;transition:background .12s,color .12s}.sd-preview-pdf-toolbar__btn sd-icon{font-size:18px;width:18px;height:18px}.sd-preview-pdf-toolbar__btn span{font-size:13px;font-weight:500}.sd-preview-pdf-toolbar__btn--with-label{padding:0 10px}.sd-preview-pdf-toolbar__btn--active{background:var(--sd-pdf-primary-soft);color:var(--sd-pdf-primary)}.sd-preview-pdf-toolbar__btn:hover:not(:disabled):not(.sd-preview-pdf-toolbar__btn--active){background:var(--sd-pdf-hover-bg)}.sd-preview-pdf-toolbar__btn:focus-visible{outline:2px solid var(--sd-pdf-primary);outline-offset:2px}.sd-preview-pdf-toolbar__btn:disabled{opacity:.35;cursor:not-allowed}.sd-preview-pdf-toolbar__page{display:inline-flex;align-items:center;gap:4px;padding:0 6px;flex-shrink:0}.sd-preview-pdf-toolbar__page-input{width:36px;height:24px;background:#ffffff14;border:1px solid rgba(255,255,255,.08);color:var(--sd-pdf-fg);border-radius:4px;text-align:center;font-size:13px;font-variant-numeric:tabular-nums;outline:none}.sd-preview-pdf-toolbar__page-input:focus{border-color:var(--sd-pdf-primary)}.sd-preview-pdf-toolbar__page-total{font-size:13px;color:var(--sd-pdf-fg-muted);font-variant-numeric:tabular-nums}.sd-preview-pdf-toolbar__zoom{min-width:56px;text-align:center;color:var(--sd-pdf-fg);font-size:13px;font-weight:500;font-variant-numeric:tabular-nums;background:#ffffff14;border-radius:4px;padding:2px 8px}.sd-preview-pdf-toolbar__divider{width:1px;height:20px;background:var(--sd-pdf-divider);margin:0 4px;flex-shrink:0}.sd-preview-pdf-status{margin:auto;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:16px;text-align:center;padding:32px;max-width:420px}.sd-preview-pdf-status__tile{width:96px;height:96px;border-radius:24px;display:flex;align-items:center;justify-content:center;border:1px solid var(--sd-pdf-divider)}.sd-preview-pdf-status__tile sd-icon{font-size:48px;width:48px;height:48px}.sd-preview-pdf-status__tile--muted{background:#ffffff0d}.sd-preview-pdf-status__tile--muted sd-icon{color:var(--sd-pdf-fg-subtle)}.sd-preview-pdf-status__tile--danger{background:var(--sd-pdf-danger-soft);border-color:var(--sd-pdf-danger-border)}.sd-preview-pdf-status__tile--danger sd-icon{color:var(--sd-pdf-danger)}.sd-preview-pdf-status__title{font-size:16px;font-weight:500;color:var(--sd-pdf-fg)}.sd-preview-pdf-status__hint{font-size:13px;color:var(--sd-pdf-fg-muted);line-height:1.5}.sd-preview-pdf-status__actions{display:flex;gap:8px;margin-top:4px}.sd-preview-pdf-progress-pct{font-variant-numeric:tabular-nums;color:var(--sd-pdf-fg-muted)}.sd-preview-pdf-progress{width:240px;height:4px;background:#ffffff14;border-radius:999px;overflow:hidden}.sd-preview-pdf-progress__fill{height:100%;background:var(--sd-pdf-primary);transition:width .12s linear}.sd-preview-pdf-spinner{width:56px;height:56px;border-radius:999px;border:3px solid rgba(255,255,255,.08);border-top-color:var(--sd-pdf-primary);animation:sd-preview-pdf-spin .9s linear infinite}@keyframes sd-preview-pdf-spin{to{transform:rotate(360deg)}}@keyframes sd-preview-pdf-thumb-skel{0%{background-position:200% 0}to{background-position:-200% 0}}.sd-preview-pdf-search-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:6px}.sd-preview-pdf-search-result{width:100%;display:flex;align-items:flex-start;gap:8px;padding:8px 10px;background:#ffffff0a;border:1px solid transparent;border-radius:6px;color:var(--sd-pdf-fg);cursor:pointer;text-align:left;transition:background .12s,border-color .12s}.sd-preview-pdf-search-result:hover{background:#ffffff12}.sd-preview-pdf-search-result--active{background:var(--sd-pdf-primary-soft);border-color:#005cbb73}.sd-preview-pdf-search-result__snippet{flex:1 1 auto;font-size:12px;line-height:1.4;color:var(--sd-pdf-fg);word-break:break-word}.sd-preview-pdf-search-result__snippet mark{background:var(--sd-pdf-search-hi);color:#000;padding:0 2px;border-radius:2px}.sd-preview-pdf-search-result__page{flex:0 0 auto;font-size:11px;font-variant-numeric:tabular-nums;color:var(--sd-pdf-fg-muted);background:#ffffff0f;padding:2px 6px;border-radius:4px}.sd-preview-pdf-btn-primary{display:inline-flex;align-items:center;gap:6px;height:36px;padding:0 16px;background:var(--sd-pdf-primary);color:#fff;border:none;border-radius:8px;cursor:pointer;font-size:13px;font-weight:500;transition:filter .12s}.sd-preview-pdf-btn-primary sd-icon{font-size:18px;width:18px;height:18px}.sd-preview-pdf-btn-primary:hover{filter:brightness(1.1)}.sd-preview-pdf-btn-primary:focus-visible{outline:2px solid #fff;outline-offset:2px}.sd-preview-pdf-btn-secondary{display:inline-flex;align-items:center;gap:6px;height:36px;padding:0 16px;background:transparent;color:var(--sd-pdf-fg);border:1px solid var(--sd-pdf-divider);border-radius:8px;cursor:pointer;font-size:13px;font-weight:500;transition:background .12s}.sd-preview-pdf-btn-secondary sd-icon{font-size:18px;width:18px;height:18px}.sd-preview-pdf-btn-secondary:hover{background:#ffffff0f}.sd-preview-pdf-btn-secondary:focus-visible{outline:2px solid var(--sd-pdf-primary);outline-offset:2px}\n"], dependencies: [{ kind: "component", type: SdIcon, selector: "sd-icon", inputs: ["name", "fontIcon", "color", "set", "fontSet", "size", "strokeWidth", "absoluteStrokeWidth", "ariaLabel"] }, { kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1968
1967
|
}
|
|
1969
1968
|
_a = SdPreviewPdf;
|
|
1970
1969
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: SdPreviewPdf, decorators: [{
|
|
1971
1970
|
type: Component,
|
|
1972
|
-
args: [{ selector: 'sd-preview-pdf', standalone: true, imports: [CommonModule, FormsModule,
|
|
1971
|
+
args: [{ selector: 'sd-preview-pdf', standalone: true, imports: [SdIcon, CommonModule, FormsModule, TranslatePipe], changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
1973
1972
|
tabindex: '0',
|
|
1974
1973
|
class: 'sd-preview-pdf-host',
|
|
1975
1974
|
// WHY data-theme attribute: SCSS uses :host([data-theme="light"]) to swap
|
|
@@ -1977,7 +1976,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImpo
|
|
|
1977
1976
|
// collision with consumer styling.
|
|
1978
1977
|
'[attr.data-theme]': 'theme()',
|
|
1979
1978
|
'[class.sd-preview-pdf-host--fullscreen]': 'isFullscreen()',
|
|
1980
|
-
}, template: "<!--\n sd-preview-pdf shell. Self-contained \u2014 sizes to its parent. Consumer wraps\n in <sd-modal>/<sd-side-drawer>/page section. Header + sidebar + stage +\n floating toolbar mirror Artboards A / C / E from the design handoff.\n-->\n<div class=\"sd-preview-pdf-shell\" role=\"region\">\n @let _stage = stage();\n @let _numPages = numPages();\n @let _activePage = activePage();\n @let _isSidebarOpen = isSidebarOpen();\n @let _searchBarOpen = searchBarOpen();\n @let _searchTerm = searchTerm();\n @let _searchTotal = searchTotal();\n\n <!-- ============================================\n HEADER BAR \u2014 hidden in empty + loading per design\n ============================================ -->\n @if (_stage === 'ready') {\n <div class=\"sd-preview-pdf-header\">\n <div class=\"sd-preview-pdf-header__left\">\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [class.sd-preview-pdf-iconbtn--active]=\"_isSidebarOpen\"\n [attr.aria-label]=\"'core.component.preview-pdf.toggle-sidebar' | translate\"\n [attr.title]=\"'core.component.preview-pdf.toggle-sidebar' | translate\"\n [attr.data-autoId]=\"autoIdSidebarToggle()\"\n (click)=\"toggleSidebar()\">\n <mat-icon fontIcon=\"menu\"></mat-icon>\n </button>\n <span class=\"sd-preview-pdf-header__divider\"></span>\n <div class=\"sd-preview-pdf-fileicon\" aria-hidden=\"true\">PDF</div>\n <div class=\"sd-preview-pdf-header__meta\">\n <div class=\"sd-preview-pdf-header__name\" [title]=\"title() || filename()\">\n {{ title() || filename() }}\n </div>\n <div class=\"sd-preview-pdf-header__sub\">\n {{ 'core.component.preview-pdf.pages-count' | translate: { count: _numPages } }}\n </div>\n </div>\n </div>\n\n <div class=\"sd-preview-pdf-header__right\">\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [class.sd-preview-pdf-iconbtn--active]=\"_searchBarOpen\"\n [attr.aria-label]=\"'core.component.preview-pdf.search' | translate\"\n [attr.title]=\"'core.component.preview-pdf.search' | translate\"\n [attr.data-autoId]=\"autoIdSearchToggle()\"\n (click)=\"searchBarOpen() ? closeSearch() : openSearch()\">\n <mat-icon fontIcon=\"search\"></mat-icon>\n </button>\n @if (downloadable()) {\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [attr.aria-label]=\"'core.component.preview-pdf.download' | translate\"\n [attr.title]=\"'core.component.preview-pdf.download' | translate\"\n [attr.data-autoId]=\"autoIdDownload()\"\n (click)=\"downloadFile()\">\n <mat-icon fontIcon=\"download\"></mat-icon>\n </button>\n }\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [attr.aria-label]=\"'core.component.preview-pdf.fullscreen' | translate\"\n [attr.title]=\"'core.component.preview-pdf.fullscreen' | translate\"\n [attr.data-autoId]=\"autoIdFullscreen()\"\n (click)=\"toggleFullscreen()\">\n <mat-icon [fontIcon]=\"isFullscreen() ? 'fullscreen_exit' : 'fullscreen'\"></mat-icon>\n </button>\n </div>\n </div>\n\n <!-- ============================================\n SEARCH BAR \u2014 between header + body, only when bar is open\n ============================================ -->\n @if (_searchBarOpen) {\n <div class=\"sd-preview-pdf-searchbar\" role=\"search\">\n <mat-icon class=\"sd-preview-pdf-searchbar__icon\" fontIcon=\"search\"></mat-icon>\n <input\n #searchInput\n type=\"text\"\n class=\"sd-preview-pdf-searchbar__input\"\n [attr.placeholder]=\"'core.component.preview-pdf.search-placeholder' | translate\"\n [value]=\"_searchTerm\"\n [attr.data-autoId]=\"autoIdSearchInput()\"\n (input)=\"onSearchInput($any($event.target).value)\" />\n <span class=\"sd-preview-pdf-searchbar__counter\" aria-live=\"polite\">\n {{ searchCurrent() }} / {{ _searchTotal }}\n </span>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [disabled]=\"_searchTotal === 0\"\n [attr.aria-label]=\"'core.component.preview-pdf.search-prev' | translate\"\n [attr.title]=\"'core.component.preview-pdf.search-prev' | translate\"\n [attr.data-autoId]=\"autoIdSearchPrev()\"\n (click)=\"searchPrev()\">\n <mat-icon fontIcon=\"keyboard_arrow_up\"></mat-icon>\n </button>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [disabled]=\"_searchTotal === 0\"\n [attr.aria-label]=\"'core.component.preview-pdf.search-next' | translate\"\n [attr.title]=\"'core.component.preview-pdf.search-next' | translate\"\n [attr.data-autoId]=\"autoIdSearchNext()\"\n (click)=\"searchNext()\">\n <mat-icon fontIcon=\"keyboard_arrow_down\"></mat-icon>\n </button>\n <span class=\"sd-preview-pdf-searchbar__divider\"></span>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [class.sd-preview-pdf-iconbtn--active]=\"searchCaseSensitive()\"\n [attr.aria-label]=\"'core.component.preview-pdf.search-case' | translate\"\n [attr.title]=\"'core.component.preview-pdf.search-case' | translate\"\n [attr.data-autoId]=\"autoIdSearchCase()\"\n (click)=\"toggleSearchCaseSensitive()\">\n <mat-icon fontIcon=\"match_case\"></mat-icon>\n </button>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [class.sd-preview-pdf-iconbtn--active]=\"searchWholeWord()\"\n [attr.aria-label]=\"'core.component.preview-pdf.search-whole' | translate\"\n [attr.title]=\"'core.component.preview-pdf.search-whole' | translate\"\n [attr.data-autoId]=\"autoIdSearchWhole()\"\n (click)=\"toggleSearchWholeWord()\">\n <mat-icon fontIcon=\"text_fields\"></mat-icon>\n </button>\n <span class=\"sd-preview-pdf-searchbar__spacer\"></span>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [attr.aria-label]=\"'core.component.preview-pdf.search-close' | translate\"\n [attr.title]=\"'core.component.preview-pdf.search-close' | translate\"\n [attr.data-autoId]=\"autoIdSearchClose()\"\n (click)=\"closeSearch()\">\n <mat-icon fontIcon=\"close\"></mat-icon>\n </button>\n </div>\n }\n }\n\n <!-- ============================================\n BODY \u2014 sidebar + stage\n ============================================ -->\n <div class=\"sd-preview-pdf-body\">\n\n <!-- SIDEBAR ............................................................. -->\n @if (_stage === 'ready' && _isSidebarOpen) {\n <aside class=\"sd-preview-pdf-sidebar\" role=\"complementary\">\n @let _sidebarMode = sidebarMode();\n <div class=\"sd-preview-pdf-sidebar__tabs\" role=\"tablist\">\n <button\n type=\"button\"\n class=\"sd-preview-pdf-sidebar__tab\"\n role=\"tab\"\n [class.sd-preview-pdf-sidebar__tab--active]=\"_sidebarMode === 'thumbnails'\"\n [attr.data-autoId]=\"autoIdTabThumbnails()\"\n (click)=\"setSidebarMode('thumbnails')\">\n <mat-icon fontIcon=\"view_carousel\"></mat-icon>\n <span>{{ 'core.component.preview-pdf.tab.thumbnails' | translate }}</span>\n </button>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-sidebar__tab\"\n role=\"tab\"\n [class.sd-preview-pdf-sidebar__tab--active]=\"_sidebarMode === 'outline'\"\n [attr.data-autoId]=\"autoIdTabOutline()\"\n (click)=\"setSidebarMode('outline')\">\n <mat-icon fontIcon=\"list\"></mat-icon>\n <span>{{ 'core.component.preview-pdf.tab.outline' | translate }}</span>\n </button>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-sidebar__tab\"\n role=\"tab\"\n [class.sd-preview-pdf-sidebar__tab--active]=\"_sidebarMode === 'search'\"\n [attr.data-autoId]=\"autoIdTabSearch()\"\n (click)=\"setSidebarMode('search')\">\n <mat-icon fontIcon=\"search\"></mat-icon>\n <span>{{ 'core.component.preview-pdf.tab.search' | translate }}</span>\n </button>\n </div>\n\n <div class=\"sd-preview-pdf-sidebar__content\">\n @switch (_sidebarMode) {\n @case ('thumbnails') {\n <ul class=\"sd-preview-pdf-thumb-list\">\n @for (p of pageNumbers(); track trackByPage($index, p); let pi = $index) {\n <li>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-thumb\"\n [class.sd-preview-pdf-thumb--active]=\"p === _activePage\"\n [attr.aria-current]=\"p === _activePage ? 'page' : null\"\n [attr.aria-label]=\"('core.component.preview-pdf.page-of' | translate: { current: p, total: _numPages })\"\n [attr.data-autoId]=\"autoIdThumb(pi)\"\n (click)=\"goToPage(p)\">\n <div class=\"sd-preview-pdf-thumb__page\">\n <canvas\n #thumbCanvas\n class=\"sd-preview-pdf-thumb__canvas\"\n [attr.data-page]=\"p\"\n aria-hidden=\"true\"></canvas>\n </div>\n <span class=\"sd-preview-pdf-thumb__label\">{{ p }}</span>\n </button>\n </li>\n }\n </ul>\n }\n @case ('outline') {\n <!-- TODO(preview-pdf): outline tree (pdfDoc.getOutline()) deferred to follow-up commit. -->\n <div class=\"sd-preview-pdf-sidebar__deferred\">\n <mat-icon fontIcon=\"list\"></mat-icon>\n <p>{{ 'core.component.preview-pdf.deferred.outline' | translate }}</p>\n </div>\n }\n @case ('search') {\n @if (!_searchTerm) {\n <div class=\"sd-preview-pdf-sidebar__deferred\">\n <mat-icon fontIcon=\"search\"></mat-icon>\n <p>{{ 'core.component.preview-pdf.search-placeholder' | translate }}</p>\n </div>\n } @else if (_searchTotal === 0) {\n <div class=\"sd-preview-pdf-sidebar__deferred\">\n <mat-icon fontIcon=\"search_off\"></mat-icon>\n <p>{{ 'core.component.preview-pdf.search-no-results' | translate }}</p>\n </div>\n } @else {\n <ul class=\"sd-preview-pdf-search-list\">\n @for (r of searchResults(); track $index; let i = $index) {\n <li>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-search-result\"\n [class.sd-preview-pdf-search-result--active]=\"i === searchActiveIndex()\"\n [attr.data-autoId]=\"autoIdResult(i)\"\n (click)=\"goToPage(r.page)\">\n <span class=\"sd-preview-pdf-search-result__snippet\">\n {{ r.before }}<mark>{{ r.term }}</mark>{{ r.after }}\n </span>\n <span class=\"sd-preview-pdf-search-result__page\">{{ r.page }}</span>\n </button>\n </li>\n }\n </ul>\n }\n }\n @default {\n <div class=\"sd-preview-pdf-sidebar__deferred\"></div>\n }\n }\n </div>\n </aside>\n }\n\n <!-- STAGE ............................................................... -->\n <div class=\"sd-preview-pdf-stage\" #stageRef (wheel)=\"onWheel($event)\">\n @switch (_stage) {\n @case ('empty') {\n <div class=\"sd-preview-pdf-status\">\n <div class=\"sd-preview-pdf-status__tile sd-preview-pdf-status__tile--muted\">\n <mat-icon fontIcon=\"picture_as_pdf\"></mat-icon>\n </div>\n <div class=\"sd-preview-pdf-status__title\">\n {{ 'core.component.preview-pdf.empty.title' | translate }}\n </div>\n <div class=\"sd-preview-pdf-status__hint\">\n {{ 'core.component.preview-pdf.empty.subtitle' | translate }}\n </div>\n </div>\n }\n @case ('loading') {\n @let _loadPercent = loadPercent();\n <div class=\"sd-preview-pdf-status\">\n <div class=\"sd-preview-pdf-spinner\" role=\"progressbar\"></div>\n <div class=\"sd-preview-pdf-status__title\">\n {{ 'core.component.preview-pdf.loading' | translate }}\n @if (_loadPercent > 0) {\n <span class=\"sd-preview-pdf-progress-pct\"> {{ _loadPercent }}%</span>\n }\n </div>\n <div class=\"sd-preview-pdf-progress\">\n <div class=\"sd-preview-pdf-progress__fill\" [style.width.%]=\"_loadPercent\"></div>\n </div>\n </div>\n }\n @case ('error') {\n @if (loadError$(); as err) {\n <div class=\"sd-preview-pdf-status\">\n <div class=\"sd-preview-pdf-status__tile sd-preview-pdf-status__tile--danger\">\n <mat-icon fontIcon=\"error_outline\"></mat-icon>\n </div>\n <div class=\"sd-preview-pdf-status__title\">\n {{ ('core.component.preview-pdf.error.' + err.reason + '.title') | translate }}\n </div>\n <div class=\"sd-preview-pdf-status__hint\">\n {{ ('core.component.preview-pdf.error.' + err.reason + '.body') | translate }}\n </div>\n <div class=\"sd-preview-pdf-status__actions\">\n <button type=\"button\" class=\"sd-preview-pdf-btn-primary\" (click)=\"retryLoad()\" [attr.data-autoId]=\"autoIdRetry()\">\n <mat-icon fontIcon=\"refresh\"></mat-icon>\n <span>{{ 'core.component.preview-pdf.error.retry' | translate }}</span>\n </button>\n @if (downloadable()) {\n <button type=\"button\" class=\"sd-preview-pdf-btn-secondary\" (click)=\"downloadFile()\">\n <mat-icon fontIcon=\"download\"></mat-icon>\n <span>{{ 'core.component.preview-pdf.error.download-raw' | translate }}</span>\n </button>\n }\n </div>\n </div>\n }\n }\n @default {\n <div class=\"sd-preview-pdf-page-wrap\">\n <canvas\n #pageCanvas\n class=\"sd-preview-pdf-page\"\n role=\"img\"\n [attr.aria-label]=\"('core.component.preview-pdf.page-of' | translate: { current: _activePage, total: _numPages })\">\n </canvas>\n </div>\n }\n }\n\n <!-- FLOATING TOOLBAR ........................................ -->\n @if (showToolbar() && _stage === 'ready') {\n @let _canPrev = canPrev();\n @let _canNext = canNext();\n @let _zoomMode = zoomMode();\n @let _scrollModeCurrent = scrollModeCurrent();\n <div class=\"sd-preview-pdf-toolbar\" role=\"toolbar\">\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn\"\n [disabled]=\"!_canPrev\"\n [attr.aria-label]=\"'core.component.preview-pdf.first-page' | translate\"\n [attr.title]=\"'core.component.preview-pdf.first-page' | translate\"\n [attr.data-autoId]=\"autoIdFirst()\"\n (click)=\"firstPage()\">\n <mat-icon fontIcon=\"first_page\"></mat-icon>\n </button>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn\"\n [disabled]=\"!_canPrev\"\n [attr.aria-label]=\"'core.component.preview-pdf.prev-page' | translate\"\n [attr.title]=\"'core.component.preview-pdf.prev-page' | translate\"\n [attr.data-autoId]=\"autoIdPrev()\"\n (click)=\"prevPage()\">\n <mat-icon fontIcon=\"chevron_left\"></mat-icon>\n </button>\n <span class=\"sd-preview-pdf-toolbar__page\">\n <input\n type=\"text\"\n inputmode=\"numeric\"\n class=\"sd-preview-pdf-toolbar__page-input\"\n [value]=\"_activePage\"\n #pageInput\n (keydown.enter)=\"onPageInputEnter($event)\"\n (blur)=\"onPageInputBlur($event)\"\n [attr.aria-label]=\"'core.component.preview-pdf.page-of' | translate: { current: _activePage, total: _numPages }\"\n [attr.data-autoId]=\"autoIdPageInput()\" />\n <span class=\"sd-preview-pdf-toolbar__page-total\">/ {{ _numPages }}</span>\n </span>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn\"\n [disabled]=\"!_canNext\"\n [attr.aria-label]=\"'core.component.preview-pdf.next-page' | translate\"\n [attr.title]=\"'core.component.preview-pdf.next-page' | translate\"\n [attr.data-autoId]=\"autoIdNext()\"\n (click)=\"nextPage()\">\n <mat-icon fontIcon=\"chevron_right\"></mat-icon>\n </button>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn\"\n [disabled]=\"!_canNext\"\n [attr.aria-label]=\"'core.component.preview-pdf.last-page' | translate\"\n [attr.title]=\"'core.component.preview-pdf.last-page' | translate\"\n [attr.data-autoId]=\"autoIdLast()\"\n (click)=\"lastPage()\">\n <mat-icon fontIcon=\"last_page\"></mat-icon>\n </button>\n\n <span class=\"sd-preview-pdf-toolbar__divider\"></span>\n\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn\"\n [attr.aria-label]=\"'core.component.preview-pdf.zoom-out' | translate\"\n [attr.title]=\"'core.component.preview-pdf.zoom-out' | translate\"\n [attr.data-autoId]=\"autoIdZoomOut()\"\n (click)=\"zoomOut()\">\n <mat-icon fontIcon=\"remove\"></mat-icon>\n </button>\n <span class=\"sd-preview-pdf-toolbar__zoom\" aria-live=\"polite\" [attr.data-autoId]=\"autoIdZoomReadout()\">{{ zoomPercent() }}%</span>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn\"\n [attr.aria-label]=\"'core.component.preview-pdf.zoom-in' | translate\"\n [attr.title]=\"'core.component.preview-pdf.zoom-in' | translate\"\n [attr.data-autoId]=\"autoIdZoomIn()\"\n (click)=\"zoomIn()\">\n <mat-icon fontIcon=\"add\"></mat-icon>\n </button>\n\n <span class=\"sd-preview-pdf-toolbar__divider\"></span>\n\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn sd-preview-pdf-toolbar__btn--with-label\"\n [class.sd-preview-pdf-toolbar__btn--active]=\"_zoomMode === 'page-fit'\"\n [attr.data-autoId]=\"autoIdFitPage()\"\n (click)=\"setZoom('page-fit')\">\n <mat-icon fontIcon=\"fit_screen\"></mat-icon>\n <span>{{ 'core.component.preview-pdf.fit-page' | translate }}</span>\n </button>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn sd-preview-pdf-toolbar__btn--with-label\"\n [class.sd-preview-pdf-toolbar__btn--active]=\"_zoomMode === 'page-width'\"\n [attr.data-autoId]=\"autoIdFitWidth()\"\n (click)=\"setZoom('page-width')\">\n <mat-icon fontIcon=\"swap_horiz\"></mat-icon>\n <span>{{ 'core.component.preview-pdf.fit-width' | translate }}</span>\n </button>\n\n <span class=\"sd-preview-pdf-toolbar__divider\"></span>\n\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn\"\n [attr.aria-label]=\"'core.component.preview-pdf.rotate' | translate\"\n [attr.title]=\"'core.component.preview-pdf.rotate' | translate\"\n [attr.data-autoId]=\"autoIdRotate()\"\n (click)=\"rotate('right')\">\n <mat-icon fontIcon=\"rotate_right\"></mat-icon>\n </button>\n\n <!-- TODO(preview-pdf): scroll mode toggle is signature-only;\n clicking switches the state but rendering stays single-page\n until the continuous scroll feature lands. -->\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn sd-preview-pdf-toolbar__btn--with-label\"\n [attr.data-autoId]=\"autoIdScrollMode()\"\n (click)=\"setScrollMode(_scrollModeCurrent === 'page' ? 'continuous' : 'page')\">\n <mat-icon [fontIcon]=\"_scrollModeCurrent === 'page' ? 'description' : 'view_day'\"></mat-icon>\n <span>\n {{ (_scrollModeCurrent === 'page'\n ? 'core.component.preview-pdf.scroll-page'\n : 'core.component.preview-pdf.scroll-continuous') | translate }}\n </span>\n </button>\n </div>\n }\n </div>\n </div>\n</div>\n", styles: [":host{--sd-pdf-shell-bg: #1a1c1f;--sd-pdf-sidebar-bg: #22252a;--sd-pdf-toolbar-bg: rgba(20, 22, 25, .94);--sd-pdf-page-bg: #ffffff;--sd-pdf-page-shadow: 0 4px 16px rgba(0, 0, 0, .4);--sd-pdf-divider: rgba(255, 255, 255, .08);--sd-pdf-fg: #f5f6f7;--sd-pdf-fg-muted: rgba(255, 255, 255, .6);--sd-pdf-fg-subtle: rgba(255, 255, 255, .4);--sd-pdf-accent: var(--sd-primary, #3b82f6);--sd-pdf-accent-soft: rgba(59, 130, 246, .2);--sd-pdf-danger: var(--sd-error, #ef4444);--sd-pdf-success: var(--sd-success, #22c55e);--sd-pdf-danger-soft: rgba(239, 68, 68, .1);--sd-pdf-danger-border: rgba(239, 68, 68, .3);--sd-pdf-danger-tint: rgba(239, 68, 68, .15);--sd-pdf-hover-bg: rgba(255, 255, 255, .1);--sd-pdf-search-hi: rgba(250, 204, 21, .55);--sd-pdf-search-hi-active: rgba(249, 115, 22, .85);display:block;width:100%;height:100%;min-height:320px;outline:none}:host([data-theme=light]){--sd-pdf-shell-bg: #f7f8fa;--sd-pdf-sidebar-bg: #ffffff;--sd-pdf-toolbar-bg: rgba(255, 255, 255, .94);--sd-pdf-page-bg: #ffffff;--sd-pdf-page-shadow: 0 4px 16px rgba(0, 0, 0, .12);--sd-pdf-divider: rgba(0, 0, 0, .08);--sd-pdf-fg: #1a1c1f;--sd-pdf-fg-muted: rgba(0, 0, 0, .62);--sd-pdf-fg-subtle: rgba(0, 0, 0, .42);--sd-pdf-accent: var(--sd-primary, #1f49bf);--sd-pdf-accent-soft: rgba(31, 73, 191, .18);--sd-pdf-danger: var(--sd-error, #ef4444);--sd-pdf-success: var(--sd-success, #22c55e);--sd-pdf-danger-soft: rgba(239, 68, 68, .08);--sd-pdf-danger-border: rgba(239, 68, 68, .25);--sd-pdf-danger-tint: rgba(239, 68, 68, .12);--sd-pdf-hover-bg: rgba(0, 0, 0, .06);--sd-pdf-search-hi: rgba(250, 204, 21, .55);--sd-pdf-search-hi-active: rgba(250, 204, 21, .9)}:host(.sd-preview-pdf-host--fullscreen){width:100vw;height:100vh;min-height:0}.sd-preview-pdf-shell{position:relative;display:flex;flex-direction:column;width:100%;height:100%;background:var(--sd-pdf-shell-bg);color:var(--sd-pdf-fg);border-radius:12px;overflow:hidden;box-shadow:0 30px 80px #0000008c,0 0 0 1px #ffffff0a;font-family:inherit}.sd-preview-pdf-header{display:flex;align-items:center;justify-content:space-between;gap:12px;height:57px;padding:0 12px;background:var(--sd-pdf-toolbar-bg);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border-bottom:1px solid var(--sd-pdf-divider);flex:0 0 auto;z-index:2}.sd-preview-pdf-header__left{display:flex;align-items:center;gap:8px;min-width:0;flex:1 1 auto}.sd-preview-pdf-header__right{display:flex;align-items:center;gap:4px;flex-shrink:0}.sd-preview-pdf-header__divider{display:inline-block;width:1px;height:20px;background:var(--sd-pdf-divider)}.sd-preview-pdf-header__meta{display:flex;flex-direction:column;min-width:0}.sd-preview-pdf-header__name{font-size:14px;font-weight:500;color:var(--sd-pdf-fg);max-width:320px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sd-preview-pdf-header__sub{font-size:11px;color:var(--sd-pdf-fg-muted);margin-top:2px}.sd-preview-pdf-fileicon{display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;border-radius:6px;background:var(--sd-pdf-danger-tint);border:1px solid var(--sd-pdf-danger-border);color:var(--sd-error-light, #f87171);font:700 10px/1 ui-monospace,SFMono-Regular,Menlo,monospace}.sd-preview-pdf-iconbtn{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-width:32px;height:32px;padding:0 8px;background:transparent;border:none;color:var(--sd-pdf-fg);border-radius:6px;cursor:pointer;transition:background .12s,color .12s;white-space:nowrap;flex-shrink:0}.sd-preview-pdf-iconbtn mat-icon{font-size:18px;width:18px;height:18px}.sd-preview-pdf-iconbtn__label{font-size:13px;font-weight:500}.sd-preview-pdf-iconbtn:hover:not(:disabled){background:var(--sd-pdf-hover-bg)}.sd-preview-pdf-iconbtn:focus-visible{outline:2px solid var(--sd-pdf-accent);outline-offset:2px}.sd-preview-pdf-iconbtn:disabled{opacity:.35;cursor:not-allowed}.sd-preview-pdf-iconbtn--active{background:var(--sd-pdf-accent-soft);color:var(--sd-pdf-accent)}.sd-preview-pdf-searchbar{display:flex;align-items:center;gap:6px;height:46px;padding:0 12px;background:var(--sd-pdf-toolbar-bg);border-bottom:1px solid var(--sd-pdf-divider);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);flex:0 0 auto;z-index:2}.sd-preview-pdf-searchbar__icon{color:var(--sd-pdf-fg-muted);font-size:18px;width:18px;height:18px;flex-shrink:0}.sd-preview-pdf-searchbar__input{flex:0 1 360px;min-width:160px;height:30px;background:#ffffff0f;border:1px solid rgba(255,255,255,.08);color:var(--sd-pdf-fg);border-radius:6px;padding:0 10px;font-size:13px;outline:none}.sd-preview-pdf-searchbar__input::placeholder{color:var(--sd-pdf-fg-subtle)}.sd-preview-pdf-searchbar__input:focus{border-color:var(--sd-pdf-accent);background:#ffffff17}.sd-preview-pdf-searchbar__counter{color:var(--sd-pdf-fg-muted);font-size:12px;font-variant-numeric:tabular-nums;min-width:48px;text-align:center;flex-shrink:0}.sd-preview-pdf-searchbar__divider{width:1px;height:20px;background:var(--sd-pdf-divider);flex-shrink:0}.sd-preview-pdf-searchbar__spacer{flex:1 1 auto}.sd-pdf-search-overlay{position:absolute;top:8px;left:50%;transform:translate(-50%);display:flex;flex-wrap:wrap;gap:4px;max-width:80%;pointer-events:none;z-index:5}.sd-pdf-search-hi{display:inline-block;padding:1px 4px;border-radius:2px;background:var(--sd-pdf-search-hi);color:#000;font-size:12px;font-weight:500}.sd-pdf-search-hi--active{background:var(--sd-pdf-search-hi-active);color:#fff;box-shadow:0 0 0 2px #fff6}.sd-preview-pdf-body{position:relative;flex:1 1 auto;min-height:0;display:flex;flex-direction:row;overflow:hidden}.sd-preview-pdf-sidebar{width:240px;display:flex;flex-direction:column;background:var(--sd-pdf-sidebar-bg);border-right:1px solid var(--sd-pdf-divider);flex-shrink:0;min-height:0}.sd-preview-pdf-sidebar__tabs{display:flex;gap:4px;padding:8px;border-bottom:1px solid var(--sd-pdf-divider)}.sd-preview-pdf-sidebar__tab{flex:1 1 0;display:inline-flex;align-items:center;justify-content:center;gap:4px;height:32px;padding:0 4px;background:transparent;border:none;color:var(--sd-pdf-fg-muted);border-radius:6px;cursor:pointer;font-size:12px;font-weight:500;white-space:nowrap;flex-shrink:0;transition:background .12s,color .12s}.sd-preview-pdf-sidebar__tab mat-icon{font-size:14px;width:14px;height:14px}.sd-preview-pdf-sidebar__tab:hover{background:#ffffff0f;color:var(--sd-pdf-fg)}.sd-preview-pdf-sidebar__tab--active{background:var(--sd-pdf-accent-soft);color:var(--sd-pdf-accent)}.sd-preview-pdf-sidebar__content{flex:1 1 auto;overflow:auto;padding:12px}.sd-preview-pdf-sidebar__deferred{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px;padding:32px 16px;text-align:center;color:var(--sd-pdf-fg-subtle)}.sd-preview-pdf-sidebar__deferred mat-icon{font-size:28px;width:28px;height:28px;opacity:.6}.sd-preview-pdf-sidebar__deferred p{margin:0;font-size:12px;line-height:1.5}.sd-preview-pdf-thumb-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:12px}.sd-preview-pdf-thumb{display:flex;flex-direction:column;align-items:center;gap:4px;width:100%;padding:0;background:transparent;border:none;cursor:pointer}.sd-preview-pdf-thumb__page{width:152px;aspect-ratio:612/792;background:var(--sd-pdf-page-bg);border-radius:2px;outline:1px solid rgba(255,255,255,.08);outline-offset:-1px;box-shadow:0 2px 6px #0000004d;display:flex;align-items:center;justify-content:center;overflow:hidden;-webkit-user-select:none;user-select:none;background-image:linear-gradient(90deg,#0000,#0000000a,#0000);background-size:200% 100%;animation:sd-preview-pdf-thumb-skel 1.6s ease-in-out infinite}.sd-preview-pdf-thumb__canvas{display:block;max-width:100%;max-height:100%;background:var(--sd-pdf-page-bg)}.sd-preview-pdf-thumb__label{font-size:11px;color:var(--sd-pdf-fg-muted);font-variant-numeric:tabular-nums}.sd-preview-pdf-thumb--active .sd-preview-pdf-thumb__page{outline:2px solid var(--sd-pdf-accent);outline-offset:-2px}.sd-preview-pdf-thumb--active .sd-preview-pdf-thumb__label{color:var(--sd-pdf-accent);font-weight:600}.sd-preview-pdf-thumb:focus-visible .sd-preview-pdf-thumb__page{outline:2px solid var(--sd-pdf-accent);outline-offset:2px}.sd-preview-pdf-stage{position:relative;flex:1 1 auto;min-width:0;min-height:0;background:var(--sd-pdf-shell-bg);background-image:linear-gradient(45deg,rgba(255,255,255,.015) 25%,transparent 25%),linear-gradient(-45deg,rgba(255,255,255,.015) 25%,transparent 25%),linear-gradient(45deg,transparent 75%,rgba(255,255,255,.015) 75%),linear-gradient(-45deg,transparent 75%,rgba(255,255,255,.015) 75%);background-size:24px 24px;background-position:0 0,0 12px,12px -12px,-12px 0;overflow:auto;padding:32px 24px 96px;display:flex;justify-content:center;align-items:flex-start}:host([data-theme=light]) .sd-preview-pdf-stage{background-image:linear-gradient(45deg,rgba(0,0,0,.015) 25%,transparent 25%),linear-gradient(-45deg,rgba(0,0,0,.015) 25%,transparent 25%),linear-gradient(45deg,transparent 75%,rgba(0,0,0,.015) 75%),linear-gradient(-45deg,transparent 75%,rgba(0,0,0,.015) 75%)}.sd-preview-pdf-page-wrap{display:inline-block;position:relative}.sd-preview-pdf-page{display:block;background:var(--sd-pdf-page-bg);border-radius:2px;box-shadow:var(--sd-pdf-page-shadow);max-width:100%}.sd-preview-pdf-toolbar{position:absolute;bottom:20px;left:50%;transform:translate(-50%);display:flex;align-items:center;gap:2px;padding:5px 6px;background:var(--sd-pdf-toolbar-bg);backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px);border:1px solid var(--sd-pdf-divider);border-radius:10px;box-shadow:0 8px 30px #0006;z-index:4;max-width:calc(100% - 32px);overflow-x:auto}.sd-preview-pdf-toolbar__btn{display:inline-flex;align-items:center;justify-content:center;gap:4px;min-width:30px;height:28px;padding:0 6px;background:transparent;border:none;color:var(--sd-pdf-fg);border-radius:6px;cursor:pointer;white-space:nowrap;flex-shrink:0;transition:background .12s,color .12s}.sd-preview-pdf-toolbar__btn mat-icon{font-size:18px;width:18px;height:18px}.sd-preview-pdf-toolbar__btn span{font-size:13px;font-weight:500}.sd-preview-pdf-toolbar__btn--with-label{padding:0 10px}.sd-preview-pdf-toolbar__btn--active{background:var(--sd-pdf-accent-soft);color:var(--sd-pdf-accent)}.sd-preview-pdf-toolbar__btn:hover:not(:disabled):not(.sd-preview-pdf-toolbar__btn--active){background:var(--sd-pdf-hover-bg)}.sd-preview-pdf-toolbar__btn:focus-visible{outline:2px solid var(--sd-pdf-accent);outline-offset:2px}.sd-preview-pdf-toolbar__btn:disabled{opacity:.35;cursor:not-allowed}.sd-preview-pdf-toolbar__page{display:inline-flex;align-items:center;gap:4px;padding:0 6px;flex-shrink:0}.sd-preview-pdf-toolbar__page-input{width:36px;height:24px;background:#ffffff14;border:1px solid rgba(255,255,255,.08);color:var(--sd-pdf-fg);border-radius:4px;text-align:center;font-size:13px;font-variant-numeric:tabular-nums;outline:none}.sd-preview-pdf-toolbar__page-input:focus{border-color:var(--sd-pdf-accent)}.sd-preview-pdf-toolbar__page-total{font-size:13px;color:var(--sd-pdf-fg-muted);font-variant-numeric:tabular-nums}.sd-preview-pdf-toolbar__zoom{min-width:56px;text-align:center;color:var(--sd-pdf-fg);font-size:13px;font-weight:500;font-variant-numeric:tabular-nums;background:#ffffff14;border-radius:4px;padding:2px 8px}.sd-preview-pdf-toolbar__divider{width:1px;height:20px;background:var(--sd-pdf-divider);margin:0 4px;flex-shrink:0}.sd-preview-pdf-status{margin:auto;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:16px;text-align:center;padding:32px;max-width:420px}.sd-preview-pdf-status__tile{width:96px;height:96px;border-radius:24px;display:flex;align-items:center;justify-content:center;border:1px solid var(--sd-pdf-divider)}.sd-preview-pdf-status__tile mat-icon{font-size:48px;width:48px;height:48px}.sd-preview-pdf-status__tile--muted{background:#ffffff0d}.sd-preview-pdf-status__tile--muted mat-icon{color:var(--sd-pdf-fg-subtle)}.sd-preview-pdf-status__tile--danger{background:var(--sd-pdf-danger-soft);border-color:var(--sd-pdf-danger-border)}.sd-preview-pdf-status__tile--danger mat-icon{color:var(--sd-pdf-danger)}.sd-preview-pdf-status__title{font-size:16px;font-weight:500;color:var(--sd-pdf-fg)}.sd-preview-pdf-status__hint{font-size:13px;color:var(--sd-pdf-fg-muted);line-height:1.5}.sd-preview-pdf-status__actions{display:flex;gap:8px;margin-top:4px}.sd-preview-pdf-progress-pct{font-variant-numeric:tabular-nums;color:var(--sd-pdf-fg-muted)}.sd-preview-pdf-progress{width:240px;height:4px;background:#ffffff14;border-radius:999px;overflow:hidden}.sd-preview-pdf-progress__fill{height:100%;background:var(--sd-pdf-accent);transition:width .12s linear}.sd-preview-pdf-spinner{width:56px;height:56px;border-radius:999px;border:3px solid rgba(255,255,255,.08);border-top-color:var(--sd-pdf-accent);animation:sd-preview-pdf-spin .9s linear infinite}@keyframes sd-preview-pdf-spin{to{transform:rotate(360deg)}}@keyframes sd-preview-pdf-thumb-skel{0%{background-position:200% 0}to{background-position:-200% 0}}.sd-preview-pdf-search-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:6px}.sd-preview-pdf-search-result{width:100%;display:flex;align-items:flex-start;gap:8px;padding:8px 10px;background:#ffffff0a;border:1px solid transparent;border-radius:6px;color:var(--sd-pdf-fg);cursor:pointer;text-align:left;transition:background .12s,border-color .12s}.sd-preview-pdf-search-result:hover{background:#ffffff12}.sd-preview-pdf-search-result--active{background:var(--sd-pdf-accent-soft);border-color:#3b82f673}.sd-preview-pdf-search-result__snippet{flex:1 1 auto;font-size:12px;line-height:1.4;color:var(--sd-pdf-fg);word-break:break-word}.sd-preview-pdf-search-result__snippet mark{background:var(--sd-pdf-search-hi);color:#000;padding:0 2px;border-radius:2px}.sd-preview-pdf-search-result__page{flex:0 0 auto;font-size:11px;font-variant-numeric:tabular-nums;color:var(--sd-pdf-fg-muted);background:#ffffff0f;padding:2px 6px;border-radius:4px}.sd-preview-pdf-btn-primary{display:inline-flex;align-items:center;gap:6px;height:36px;padding:0 16px;background:var(--sd-pdf-accent);color:#fff;border:none;border-radius:8px;cursor:pointer;font-size:13px;font-weight:500;transition:filter .12s}.sd-preview-pdf-btn-primary mat-icon{font-size:18px;width:18px;height:18px}.sd-preview-pdf-btn-primary:hover{filter:brightness(1.1)}.sd-preview-pdf-btn-primary:focus-visible{outline:2px solid #fff;outline-offset:2px}.sd-preview-pdf-btn-secondary{display:inline-flex;align-items:center;gap:6px;height:36px;padding:0 16px;background:transparent;color:var(--sd-pdf-fg);border:1px solid var(--sd-pdf-divider);border-radius:8px;cursor:pointer;font-size:13px;font-weight:500;transition:background .12s}.sd-preview-pdf-btn-secondary mat-icon{font-size:18px;width:18px;height:18px}.sd-preview-pdf-btn-secondary:hover{background:#ffffff0f}.sd-preview-pdf-btn-secondary:focus-visible{outline:2px solid var(--sd-pdf-accent);outline-offset:2px}\n"] }]
|
|
1979
|
+
}, template: "<!--\n sd-preview-pdf shell. Self-contained \u2014 sizes to its parent. Consumer wraps\n in <sd-modal>/<sd-side-drawer>/page section. Header + sidebar + stage +\n floating toolbar mirror Artboards A / C / E from the design handoff.\n-->\n<div class=\"sd-preview-pdf-shell\" role=\"region\">\n @let _stage = stage();\n @let _numPages = numPages();\n @let _activePage = activePage();\n @let _isSidebarOpen = isSidebarOpen();\n @let _searchBarOpen = searchBarOpen();\n @let _searchTerm = searchTerm();\n @let _searchTotal = searchTotal();\n\n <!-- ============================================\n HEADER BAR \u2014 hidden in empty + loading per design\n ============================================ -->\n @if (_stage === 'ready') {\n <div class=\"sd-preview-pdf-header\">\n <div class=\"sd-preview-pdf-header__left\">\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [class.sd-preview-pdf-iconbtn--active]=\"_isSidebarOpen\"\n [attr.aria-label]=\"'core.component.preview-pdf.toggle-sidebar' | translate\"\n [attr.title]=\"'core.component.preview-pdf.toggle-sidebar' | translate\"\n [attr.data-autoId]=\"autoIdSidebarToggle()\"\n (click)=\"toggleSidebar()\">\n <sd-icon fontIcon=\"menu\"></sd-icon>\n </button>\n <span class=\"sd-preview-pdf-header__divider\"></span>\n <div class=\"sd-preview-pdf-fileicon\" aria-hidden=\"true\">PDF</div>\n <div class=\"sd-preview-pdf-header__meta\">\n <div class=\"sd-preview-pdf-header__name\" [title]=\"title() || filename()\">\n {{ title() || filename() }}\n </div>\n <div class=\"sd-preview-pdf-header__sub\">\n {{ 'core.component.preview-pdf.pages-count' | translate: { count: _numPages } }}\n </div>\n </div>\n </div>\n\n <div class=\"sd-preview-pdf-header__right\">\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [class.sd-preview-pdf-iconbtn--active]=\"_searchBarOpen\"\n [attr.aria-label]=\"'core.component.preview-pdf.search' | translate\"\n [attr.title]=\"'core.component.preview-pdf.search' | translate\"\n [attr.data-autoId]=\"autoIdSearchToggle()\"\n (click)=\"searchBarOpen() ? closeSearch() : openSearch()\">\n <sd-icon fontIcon=\"search\"></sd-icon>\n </button>\n @if (downloadable()) {\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [attr.aria-label]=\"'core.component.preview-pdf.download' | translate\"\n [attr.title]=\"'core.component.preview-pdf.download' | translate\"\n [attr.data-autoId]=\"autoIdDownload()\"\n (click)=\"downloadFile()\">\n <sd-icon fontIcon=\"download\"></sd-icon>\n </button>\n }\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [attr.aria-label]=\"'core.component.preview-pdf.fullscreen' | translate\"\n [attr.title]=\"'core.component.preview-pdf.fullscreen' | translate\"\n [attr.data-autoId]=\"autoIdFullscreen()\"\n (click)=\"toggleFullscreen()\">\n <sd-icon [fontIcon]=\"isFullscreen() ? 'fullscreen_exit' : 'fullscreen'\"></sd-icon>\n </button>\n </div>\n </div>\n\n <!-- ============================================\n SEARCH BAR \u2014 between header + body, only when bar is open\n ============================================ -->\n @if (_searchBarOpen) {\n <div class=\"sd-preview-pdf-searchbar\" role=\"search\">\n <sd-icon class=\"sd-preview-pdf-searchbar__icon\" fontIcon=\"search\"></sd-icon>\n <input\n #searchInput\n type=\"text\"\n class=\"sd-preview-pdf-searchbar__input\"\n [attr.placeholder]=\"'core.component.preview-pdf.search-placeholder' | translate\"\n [value]=\"_searchTerm\"\n [attr.data-autoId]=\"autoIdSearchInput()\"\n (input)=\"onSearchInput($any($event.target).value)\" />\n <span class=\"sd-preview-pdf-searchbar__counter\" aria-live=\"polite\"> {{ searchCurrent() }} / {{ _searchTotal }} </span>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [disabled]=\"_searchTotal === 0\"\n [attr.aria-label]=\"'core.component.preview-pdf.search-prev' | translate\"\n [attr.title]=\"'core.component.preview-pdf.search-prev' | translate\"\n [attr.data-autoId]=\"autoIdSearchPrev()\"\n (click)=\"searchPrev()\">\n <sd-icon fontIcon=\"keyboard_arrow_up\"></sd-icon>\n </button>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [disabled]=\"_searchTotal === 0\"\n [attr.aria-label]=\"'core.component.preview-pdf.search-next' | translate\"\n [attr.title]=\"'core.component.preview-pdf.search-next' | translate\"\n [attr.data-autoId]=\"autoIdSearchNext()\"\n (click)=\"searchNext()\">\n <sd-icon fontIcon=\"keyboard_arrow_down\"></sd-icon>\n </button>\n <span class=\"sd-preview-pdf-searchbar__divider\"></span>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [class.sd-preview-pdf-iconbtn--active]=\"searchCaseSensitive()\"\n [attr.aria-label]=\"'core.component.preview-pdf.search-case' | translate\"\n [attr.title]=\"'core.component.preview-pdf.search-case' | translate\"\n [attr.data-autoId]=\"autoIdSearchCase()\"\n (click)=\"toggleSearchCaseSensitive()\">\n <sd-icon fontIcon=\"match_case\"></sd-icon>\n </button>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [class.sd-preview-pdf-iconbtn--active]=\"searchWholeWord()\"\n [attr.aria-label]=\"'core.component.preview-pdf.search-whole' | translate\"\n [attr.title]=\"'core.component.preview-pdf.search-whole' | translate\"\n [attr.data-autoId]=\"autoIdSearchWhole()\"\n (click)=\"toggleSearchWholeWord()\">\n <sd-icon fontIcon=\"text_fields\"></sd-icon>\n </button>\n <span class=\"sd-preview-pdf-searchbar__spacer\"></span>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [attr.aria-label]=\"'core.component.preview-pdf.search-close' | translate\"\n [attr.title]=\"'core.component.preview-pdf.search-close' | translate\"\n [attr.data-autoId]=\"autoIdSearchClose()\"\n (click)=\"closeSearch()\">\n <sd-icon fontIcon=\"close\"></sd-icon>\n </button>\n </div>\n }\n }\n\n <!-- ============================================\n BODY \u2014 sidebar + stage\n ============================================ -->\n <div class=\"sd-preview-pdf-body\">\n <!-- SIDEBAR ............................................................. -->\n @if (_stage === 'ready' && _isSidebarOpen) {\n <aside class=\"sd-preview-pdf-sidebar\" role=\"complementary\">\n @let _sidebarMode = sidebarMode();\n <div class=\"sd-preview-pdf-sidebar__tabs\" role=\"tablist\">\n <button\n type=\"button\"\n class=\"sd-preview-pdf-sidebar__tab\"\n role=\"tab\"\n [class.sd-preview-pdf-sidebar__tab--active]=\"_sidebarMode === 'thumbnails'\"\n [attr.data-autoId]=\"autoIdTabThumbnails()\"\n (click)=\"setSidebarMode('thumbnails')\">\n <sd-icon fontIcon=\"view_carousel\"></sd-icon>\n <span>{{ 'core.component.preview-pdf.tab.thumbnails' | translate }}</span>\n </button>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-sidebar__tab\"\n role=\"tab\"\n [class.sd-preview-pdf-sidebar__tab--active]=\"_sidebarMode === 'outline'\"\n [attr.data-autoId]=\"autoIdTabOutline()\"\n (click)=\"setSidebarMode('outline')\">\n <sd-icon fontIcon=\"list\"></sd-icon>\n <span>{{ 'core.component.preview-pdf.tab.outline' | translate }}</span>\n </button>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-sidebar__tab\"\n role=\"tab\"\n [class.sd-preview-pdf-sidebar__tab--active]=\"_sidebarMode === 'search'\"\n [attr.data-autoId]=\"autoIdTabSearch()\"\n (click)=\"setSidebarMode('search')\">\n <sd-icon fontIcon=\"search\"></sd-icon>\n <span>{{ 'core.component.preview-pdf.tab.search' | translate }}</span>\n </button>\n </div>\n\n <div class=\"sd-preview-pdf-sidebar__content\">\n @switch (_sidebarMode) {\n @case ('thumbnails') {\n <ul class=\"sd-preview-pdf-thumb-list\">\n @for (p of pageNumbers(); track trackByPage($index, p); let pi = $index) {\n <li>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-thumb\"\n [class.sd-preview-pdf-thumb--active]=\"p === _activePage\"\n [attr.aria-current]=\"p === _activePage ? 'page' : null\"\n [attr.aria-label]=\"'core.component.preview-pdf.page-of' | translate: { current: p, total: _numPages }\"\n [attr.data-autoId]=\"autoIdThumb(pi)\"\n (click)=\"goToPage(p)\">\n <div class=\"sd-preview-pdf-thumb__page\">\n <canvas #thumbCanvas class=\"sd-preview-pdf-thumb__canvas\" [attr.data-page]=\"p\" aria-hidden=\"true\"></canvas>\n </div>\n <span class=\"sd-preview-pdf-thumb__label\">{{ p }}</span>\n </button>\n </li>\n }\n </ul>\n }\n @case ('outline') {\n <!-- TODO(preview-pdf): outline tree (pdfDoc.getOutline()) deferred to follow-up commit. -->\n <div class=\"sd-preview-pdf-sidebar__deferred\">\n <sd-icon fontIcon=\"list\"></sd-icon>\n <p>{{ 'core.component.preview-pdf.deferred.outline' | translate }}</p>\n </div>\n }\n @case ('search') {\n @if (!_searchTerm) {\n <div class=\"sd-preview-pdf-sidebar__deferred\">\n <sd-icon fontIcon=\"search\"></sd-icon>\n <p>{{ 'core.component.preview-pdf.search-placeholder' | translate }}</p>\n </div>\n } @else if (_searchTotal === 0) {\n <div class=\"sd-preview-pdf-sidebar__deferred\">\n <sd-icon fontIcon=\"search_off\"></sd-icon>\n <p>{{ 'core.component.preview-pdf.search-no-results' | translate }}</p>\n </div>\n } @else {\n <ul class=\"sd-preview-pdf-search-list\">\n @for (r of searchResults(); track $index; let i = $index) {\n <li>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-search-result\"\n [class.sd-preview-pdf-search-result--active]=\"i === searchActiveIndex()\"\n [attr.data-autoId]=\"autoIdResult(i)\"\n (click)=\"goToPage(r.page)\">\n <span class=\"sd-preview-pdf-search-result__snippet\">\n {{ r.before }}<mark>{{ r.term }}</mark\n >{{ r.after }}\n </span>\n <span class=\"sd-preview-pdf-search-result__page\">{{ r.page }}</span>\n </button>\n </li>\n }\n </ul>\n }\n }\n @default {\n <div class=\"sd-preview-pdf-sidebar__deferred\"></div>\n }\n }\n </div>\n </aside>\n }\n\n <!-- STAGE ............................................................... -->\n <div class=\"sd-preview-pdf-stage\" #stageRef (wheel)=\"onWheel($event)\">\n @switch (_stage) {\n @case ('empty') {\n <div class=\"sd-preview-pdf-status\">\n <div class=\"sd-preview-pdf-status__tile sd-preview-pdf-status__tile--muted\">\n <sd-icon fontIcon=\"picture_as_pdf\"></sd-icon>\n </div>\n <div class=\"sd-preview-pdf-status__title\">\n {{ 'core.component.preview-pdf.empty.title' | translate }}\n </div>\n <div class=\"sd-preview-pdf-status__hint\">\n {{ 'core.component.preview-pdf.empty.subtitle' | translate }}\n </div>\n </div>\n }\n @case ('loading') {\n @let _loadPercent = loadPercent();\n <div class=\"sd-preview-pdf-status\">\n <div class=\"sd-preview-pdf-spinner\" role=\"progressbar\"></div>\n <div class=\"sd-preview-pdf-status__title\">\n {{ 'core.component.preview-pdf.loading' | translate }}\n @if (_loadPercent > 0) {\n <span class=\"sd-preview-pdf-progress-pct\"> {{ _loadPercent }}%</span>\n }\n </div>\n <div class=\"sd-preview-pdf-progress\">\n <div class=\"sd-preview-pdf-progress__fill\" [style.width.%]=\"_loadPercent\"></div>\n </div>\n </div>\n }\n @case ('error') {\n @if (loadError$(); as err) {\n <div class=\"sd-preview-pdf-status\">\n <div class=\"sd-preview-pdf-status__tile sd-preview-pdf-status__tile--danger\">\n <sd-icon fontIcon=\"error\"></sd-icon>\n </div>\n <div class=\"sd-preview-pdf-status__title\">\n {{ 'core.component.preview-pdf.error.' + err.reason + '.title' | translate }}\n </div>\n <div class=\"sd-preview-pdf-status__hint\">\n {{ 'core.component.preview-pdf.error.' + err.reason + '.body' | translate }}\n </div>\n <div class=\"sd-preview-pdf-status__actions\">\n <button type=\"button\" class=\"sd-preview-pdf-btn-primary\" (click)=\"retryLoad()\" [attr.data-autoId]=\"autoIdRetry()\">\n <sd-icon fontIcon=\"refresh\"></sd-icon>\n <span>{{ 'core.component.preview-pdf.error.retry' | translate }}</span>\n </button>\n @if (downloadable()) {\n <button type=\"button\" class=\"sd-preview-pdf-btn-secondary\" (click)=\"downloadFile()\">\n <sd-icon fontIcon=\"download\"></sd-icon>\n <span>{{ 'core.component.preview-pdf.error.download-raw' | translate }}</span>\n </button>\n }\n </div>\n </div>\n }\n }\n @default {\n <div class=\"sd-preview-pdf-page-wrap\">\n <canvas\n #pageCanvas\n class=\"sd-preview-pdf-page\"\n role=\"img\"\n [attr.aria-label]=\"'core.component.preview-pdf.page-of' | translate: { current: _activePage, total: _numPages }\">\n </canvas>\n </div>\n }\n }\n\n <!-- FLOATING TOOLBAR ........................................ -->\n @if (showToolbar() && _stage === 'ready') {\n @let _canPrev = canPrev();\n @let _canNext = canNext();\n @let _zoomMode = zoomMode();\n @let _scrollModeCurrent = scrollModeCurrent();\n <div class=\"sd-preview-pdf-toolbar\" role=\"toolbar\">\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn\"\n [disabled]=\"!_canPrev\"\n [attr.aria-label]=\"'core.component.preview-pdf.first-page' | translate\"\n [attr.title]=\"'core.component.preview-pdf.first-page' | translate\"\n [attr.data-autoId]=\"autoIdFirst()\"\n (click)=\"firstPage()\">\n <sd-icon fontIcon=\"first_page\"></sd-icon>\n </button>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn\"\n [disabled]=\"!_canPrev\"\n [attr.aria-label]=\"'core.component.preview-pdf.prev-page' | translate\"\n [attr.title]=\"'core.component.preview-pdf.prev-page' | translate\"\n [attr.data-autoId]=\"autoIdPrev()\"\n (click)=\"prevPage()\">\n <sd-icon fontIcon=\"chevron_left\"></sd-icon>\n </button>\n <span class=\"sd-preview-pdf-toolbar__page\">\n <input\n type=\"text\"\n inputmode=\"numeric\"\n class=\"sd-preview-pdf-toolbar__page-input\"\n [value]=\"_activePage\"\n #pageInput\n (keydown.enter)=\"onPageInputEnter($event)\"\n (blur)=\"onPageInputBlur($event)\"\n [attr.aria-label]=\"'core.component.preview-pdf.page-of' | translate: { current: _activePage, total: _numPages }\"\n [attr.data-autoId]=\"autoIdPageInput()\" />\n <span class=\"sd-preview-pdf-toolbar__page-total\">/ {{ _numPages }}</span>\n </span>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn\"\n [disabled]=\"!_canNext\"\n [attr.aria-label]=\"'core.component.preview-pdf.next-page' | translate\"\n [attr.title]=\"'core.component.preview-pdf.next-page' | translate\"\n [attr.data-autoId]=\"autoIdNext()\"\n (click)=\"nextPage()\">\n <sd-icon fontIcon=\"chevron_right\"></sd-icon>\n </button>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn\"\n [disabled]=\"!_canNext\"\n [attr.aria-label]=\"'core.component.preview-pdf.last-page' | translate\"\n [attr.title]=\"'core.component.preview-pdf.last-page' | translate\"\n [attr.data-autoId]=\"autoIdLast()\"\n (click)=\"lastPage()\">\n <sd-icon fontIcon=\"last_page\"></sd-icon>\n </button>\n\n <span class=\"sd-preview-pdf-toolbar__divider\"></span>\n\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn\"\n [attr.aria-label]=\"'core.component.preview-pdf.zoom-out' | translate\"\n [attr.title]=\"'core.component.preview-pdf.zoom-out' | translate\"\n [attr.data-autoId]=\"autoIdZoomOut()\"\n (click)=\"zoomOut()\">\n <sd-icon fontIcon=\"remove\"></sd-icon>\n </button>\n <span class=\"sd-preview-pdf-toolbar__zoom\" aria-live=\"polite\" [attr.data-autoId]=\"autoIdZoomReadout()\">{{ zoomPercent() }}%</span>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn\"\n [attr.aria-label]=\"'core.component.preview-pdf.zoom-in' | translate\"\n [attr.title]=\"'core.component.preview-pdf.zoom-in' | translate\"\n [attr.data-autoId]=\"autoIdZoomIn()\"\n (click)=\"zoomIn()\">\n <sd-icon fontIcon=\"add\"></sd-icon>\n </button>\n\n <span class=\"sd-preview-pdf-toolbar__divider\"></span>\n\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn sd-preview-pdf-toolbar__btn--with-label\"\n [class.sd-preview-pdf-toolbar__btn--active]=\"_zoomMode === 'page-fit'\"\n [attr.data-autoId]=\"autoIdFitPage()\"\n (click)=\"setZoom('page-fit')\">\n <sd-icon fontIcon=\"fit_screen\"></sd-icon>\n <span>{{ 'core.component.preview-pdf.fit-page' | translate }}</span>\n </button>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn sd-preview-pdf-toolbar__btn--with-label\"\n [class.sd-preview-pdf-toolbar__btn--active]=\"_zoomMode === 'page-width'\"\n [attr.data-autoId]=\"autoIdFitWidth()\"\n (click)=\"setZoom('page-width')\">\n <sd-icon fontIcon=\"swap_horiz\"></sd-icon>\n <span>{{ 'core.component.preview-pdf.fit-width' | translate }}</span>\n </button>\n\n <span class=\"sd-preview-pdf-toolbar__divider\"></span>\n\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn\"\n [attr.aria-label]=\"'core.component.preview-pdf.rotate' | translate\"\n [attr.title]=\"'core.component.preview-pdf.rotate' | translate\"\n [attr.data-autoId]=\"autoIdRotate()\"\n (click)=\"rotate('right')\">\n <sd-icon fontIcon=\"rotate_right\"></sd-icon>\n </button>\n\n <!-- TODO(preview-pdf): scroll mode toggle is signature-only;\n clicking switches the state but rendering stays single-page\n until the continuous scroll feature lands. -->\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn sd-preview-pdf-toolbar__btn--with-label\"\n [attr.data-autoId]=\"autoIdScrollMode()\"\n (click)=\"setScrollMode(_scrollModeCurrent === 'page' ? 'continuous' : 'page')\">\n <sd-icon [fontIcon]=\"_scrollModeCurrent === 'page' ? 'description' : 'view_day'\"></sd-icon>\n <span>\n {{\n (_scrollModeCurrent === 'page' ? 'core.component.preview-pdf.scroll-page' : 'core.component.preview-pdf.scroll-continuous')\n | translate\n }}\n </span>\n </button>\n </div>\n }\n </div>\n </div>\n</div>\n", styles: [":host{--sd-pdf-shell-bg: #1a1c1f;--sd-pdf-sidebar-bg: #22252a;--sd-pdf-toolbar-bg: rgba(20, 22, 25, .94);--sd-pdf-page-bg: #ffffff;--sd-pdf-page-shadow: 0 4px 16px rgba(0, 0, 0, .4);--sd-pdf-divider: rgba(255, 255, 255, .08);--sd-pdf-fg: #f5f6f7;--sd-pdf-fg-muted: rgba(255, 255, 255, .6);--sd-pdf-fg-subtle: rgba(255, 255, 255, .4);--sd-pdf-primary: var(--sd-pdf-accent, var(--sd-primary, #005cbb));--sd-pdf-primary-soft: var(--sd-pdf-accent-soft, rgba(0, 92, 187, .18));--sd-pdf-danger: var(--sd-error, #ba1a1a);--sd-pdf-success: var(--sd-success, #2e7d32);--sd-pdf-danger-soft: rgba(186, 26, 26, .1);--sd-pdf-danger-border: rgba(186, 26, 26, .3);--sd-pdf-danger-tint: rgba(186, 26, 26, .15);--sd-pdf-hover-bg: rgba(255, 255, 255, .1);--sd-pdf-search-hi: rgba(250, 204, 21, .55);--sd-pdf-search-hi-active: rgba(249, 115, 22, .85);display:block;width:100%;height:100%;min-height:320px;outline:none}:host([data-theme=light]){--sd-pdf-shell-bg: #f7f8fa;--sd-pdf-sidebar-bg: #ffffff;--sd-pdf-toolbar-bg: rgba(255, 255, 255, .94);--sd-pdf-page-bg: #ffffff;--sd-pdf-page-shadow: 0 4px 16px rgba(0, 0, 0, .12);--sd-pdf-divider: rgba(0, 0, 0, .08);--sd-pdf-fg: #1a1c1f;--sd-pdf-fg-muted: rgba(0, 0, 0, .62);--sd-pdf-fg-subtle: rgba(0, 0, 0, .42);--sd-pdf-primary: var(--sd-pdf-accent, var(--sd-primary, #005cbb));--sd-pdf-primary-soft: var(--sd-pdf-accent-soft, rgba(0, 92, 187, .16));--sd-pdf-danger: var(--sd-error, #ba1a1a);--sd-pdf-success: var(--sd-success, #2e7d32);--sd-pdf-danger-soft: rgba(186, 26, 26, .08);--sd-pdf-danger-border: rgba(186, 26, 26, .25);--sd-pdf-danger-tint: rgba(186, 26, 26, .12);--sd-pdf-hover-bg: rgba(0, 0, 0, .06);--sd-pdf-search-hi: rgba(250, 204, 21, .55);--sd-pdf-search-hi-active: rgba(250, 204, 21, .9)}:host(.sd-preview-pdf-host--fullscreen){width:100vw;height:100vh;min-height:0}.sd-preview-pdf-shell{position:relative;display:flex;flex-direction:column;width:100%;height:100%;background:var(--sd-pdf-shell-bg);color:var(--sd-pdf-fg);border-radius:12px;overflow:hidden;box-shadow:0 30px 80px #0000008c,0 0 0 1px #ffffff0a;font-family:inherit}.sd-preview-pdf-header{display:flex;align-items:center;justify-content:space-between;gap:12px;height:57px;padding:0 12px;background:var(--sd-pdf-toolbar-bg);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border-bottom:1px solid var(--sd-pdf-divider);flex:0 0 auto;z-index:2}.sd-preview-pdf-header__left{display:flex;align-items:center;gap:8px;min-width:0;flex:1 1 auto}.sd-preview-pdf-header__right{display:flex;align-items:center;gap:4px;flex-shrink:0}.sd-preview-pdf-header__divider{display:inline-block;width:1px;height:20px;background:var(--sd-pdf-divider)}.sd-preview-pdf-header__meta{display:flex;flex-direction:column;min-width:0}.sd-preview-pdf-header__name{font-size:14px;font-weight:500;color:var(--sd-pdf-fg);max-width:320px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sd-preview-pdf-header__sub{font-size:11px;color:var(--sd-pdf-fg-muted);margin-top:2px}.sd-preview-pdf-fileicon{display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;border-radius:6px;background:var(--sd-pdf-danger-tint);border:1px solid var(--sd-pdf-danger-border);color:var(--sd-error-light, #f87171);font:700 10px/1 ui-monospace,SFMono-Regular,Menlo,monospace}.sd-preview-pdf-iconbtn{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-width:32px;height:32px;padding:0 8px;background:transparent;border:none;color:var(--sd-pdf-fg);border-radius:6px;cursor:pointer;transition:background .12s,color .12s;white-space:nowrap;flex-shrink:0}.sd-preview-pdf-iconbtn sd-icon{font-size:18px;width:18px;height:18px}.sd-preview-pdf-iconbtn__label{font-size:13px;font-weight:500}.sd-preview-pdf-iconbtn:hover:not(:disabled){background:var(--sd-pdf-hover-bg)}.sd-preview-pdf-iconbtn:focus-visible{outline:2px solid var(--sd-pdf-primary);outline-offset:2px}.sd-preview-pdf-iconbtn:disabled{opacity:.35;cursor:not-allowed}.sd-preview-pdf-iconbtn--active{background:var(--sd-pdf-primary-soft);color:var(--sd-pdf-primary)}.sd-preview-pdf-searchbar{display:flex;align-items:center;gap:6px;height:46px;padding:0 12px;background:var(--sd-pdf-toolbar-bg);border-bottom:1px solid var(--sd-pdf-divider);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);flex:0 0 auto;z-index:2}.sd-preview-pdf-searchbar__icon{color:var(--sd-pdf-fg-muted);font-size:18px;width:18px;height:18px;flex-shrink:0}.sd-preview-pdf-searchbar__input{flex:0 1 360px;min-width:160px;height:30px;background:#ffffff0f;border:1px solid rgba(255,255,255,.08);color:var(--sd-pdf-fg);border-radius:6px;padding:0 10px;font-size:13px;outline:none}.sd-preview-pdf-searchbar__input::placeholder{color:var(--sd-pdf-fg-subtle)}.sd-preview-pdf-searchbar__input:focus{border-color:var(--sd-pdf-primary);background:#ffffff17}.sd-preview-pdf-searchbar__counter{color:var(--sd-pdf-fg-muted);font-size:12px;font-variant-numeric:tabular-nums;min-width:48px;text-align:center;flex-shrink:0}.sd-preview-pdf-searchbar__divider{width:1px;height:20px;background:var(--sd-pdf-divider);flex-shrink:0}.sd-preview-pdf-searchbar__spacer{flex:1 1 auto}.sd-pdf-search-overlay{position:absolute;top:8px;left:50%;transform:translate(-50%);display:flex;flex-wrap:wrap;gap:4px;max-width:80%;pointer-events:none;z-index:5}.sd-pdf-search-hi{display:inline-block;padding:1px 4px;border-radius:2px;background:var(--sd-pdf-search-hi);color:#000;font-size:12px;font-weight:500}.sd-pdf-search-hi--active{background:var(--sd-pdf-search-hi-active);color:#fff;box-shadow:0 0 0 2px #fff6}.sd-preview-pdf-body{position:relative;flex:1 1 auto;min-height:0;display:flex;flex-direction:row;overflow:hidden}.sd-preview-pdf-sidebar{width:240px;display:flex;flex-direction:column;background:var(--sd-pdf-sidebar-bg);border-right:1px solid var(--sd-pdf-divider);flex-shrink:0;min-height:0}.sd-preview-pdf-sidebar__tabs{display:flex;gap:4px;padding:8px;border-bottom:1px solid var(--sd-pdf-divider)}.sd-preview-pdf-sidebar__tab{flex:1 1 0;display:inline-flex;align-items:center;justify-content:center;gap:4px;height:32px;padding:0 4px;background:transparent;border:none;color:var(--sd-pdf-fg-muted);border-radius:6px;cursor:pointer;font-size:12px;font-weight:500;white-space:nowrap;flex-shrink:0;transition:background .12s,color .12s}.sd-preview-pdf-sidebar__tab sd-icon{font-size:14px;width:14px;height:14px}.sd-preview-pdf-sidebar__tab:hover{background:#ffffff0f;color:var(--sd-pdf-fg)}.sd-preview-pdf-sidebar__tab--active{background:var(--sd-pdf-primary-soft);color:var(--sd-pdf-primary)}.sd-preview-pdf-sidebar__content{flex:1 1 auto;overflow:auto;padding:12px}.sd-preview-pdf-sidebar__deferred{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px;padding:32px 16px;text-align:center;color:var(--sd-pdf-fg-subtle)}.sd-preview-pdf-sidebar__deferred sd-icon{font-size:28px;width:28px;height:28px;opacity:.6}.sd-preview-pdf-sidebar__deferred p{margin:0;font-size:12px;line-height:1.5}.sd-preview-pdf-thumb-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:12px}.sd-preview-pdf-thumb{display:flex;flex-direction:column;align-items:center;gap:4px;width:100%;padding:0;background:transparent;border:none;cursor:pointer}.sd-preview-pdf-thumb__page{width:152px;aspect-ratio:612/792;background:var(--sd-pdf-page-bg);border-radius:2px;outline:1px solid rgba(255,255,255,.08);outline-offset:-1px;box-shadow:0 2px 6px #0000004d;display:flex;align-items:center;justify-content:center;overflow:hidden;-webkit-user-select:none;user-select:none;background-image:linear-gradient(90deg,#0000,#0000000a,#0000);background-size:200% 100%;animation:sd-preview-pdf-thumb-skel 1.6s ease-in-out infinite}.sd-preview-pdf-thumb__canvas{display:block;max-width:100%;max-height:100%;background:var(--sd-pdf-page-bg)}.sd-preview-pdf-thumb__label{font-size:11px;color:var(--sd-pdf-fg-muted);font-variant-numeric:tabular-nums}.sd-preview-pdf-thumb--active .sd-preview-pdf-thumb__page{outline:2px solid var(--sd-pdf-primary);outline-offset:-2px}.sd-preview-pdf-thumb--active .sd-preview-pdf-thumb__label{color:var(--sd-pdf-primary);font-weight:600}.sd-preview-pdf-thumb:focus-visible .sd-preview-pdf-thumb__page{outline:2px solid var(--sd-pdf-primary);outline-offset:2px}.sd-preview-pdf-stage{position:relative;flex:1 1 auto;min-width:0;min-height:0;background:var(--sd-pdf-shell-bg);background-image:linear-gradient(45deg,rgba(255,255,255,.015) 25%,transparent 25%),linear-gradient(-45deg,rgba(255,255,255,.015) 25%,transparent 25%),linear-gradient(45deg,transparent 75%,rgba(255,255,255,.015) 75%),linear-gradient(-45deg,transparent 75%,rgba(255,255,255,.015) 75%);background-size:24px 24px;background-position:0 0,0 12px,12px -12px,-12px 0;overflow:auto;padding:32px 24px 96px;display:flex;justify-content:center;align-items:flex-start}:host([data-theme=light]) .sd-preview-pdf-stage{background-image:linear-gradient(45deg,rgba(0,0,0,.015) 25%,transparent 25%),linear-gradient(-45deg,rgba(0,0,0,.015) 25%,transparent 25%),linear-gradient(45deg,transparent 75%,rgba(0,0,0,.015) 75%),linear-gradient(-45deg,transparent 75%,rgba(0,0,0,.015) 75%)}.sd-preview-pdf-page-wrap{display:inline-block;position:relative}.sd-preview-pdf-page{display:block;background:var(--sd-pdf-page-bg);border-radius:2px;box-shadow:var(--sd-pdf-page-shadow);max-width:100%}.sd-preview-pdf-toolbar{position:absolute;bottom:20px;left:50%;transform:translate(-50%);display:flex;align-items:center;gap:2px;padding:5px 6px;background:var(--sd-pdf-toolbar-bg);backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px);border:1px solid var(--sd-pdf-divider);border-radius:10px;box-shadow:0 8px 30px #0006;z-index:4;max-width:calc(100% - 32px);overflow-x:auto}.sd-preview-pdf-toolbar__btn{display:inline-flex;align-items:center;justify-content:center;gap:4px;min-width:30px;height:28px;padding:0 6px;background:transparent;border:none;color:var(--sd-pdf-fg);border-radius:6px;cursor:pointer;white-space:nowrap;flex-shrink:0;transition:background .12s,color .12s}.sd-preview-pdf-toolbar__btn sd-icon{font-size:18px;width:18px;height:18px}.sd-preview-pdf-toolbar__btn span{font-size:13px;font-weight:500}.sd-preview-pdf-toolbar__btn--with-label{padding:0 10px}.sd-preview-pdf-toolbar__btn--active{background:var(--sd-pdf-primary-soft);color:var(--sd-pdf-primary)}.sd-preview-pdf-toolbar__btn:hover:not(:disabled):not(.sd-preview-pdf-toolbar__btn--active){background:var(--sd-pdf-hover-bg)}.sd-preview-pdf-toolbar__btn:focus-visible{outline:2px solid var(--sd-pdf-primary);outline-offset:2px}.sd-preview-pdf-toolbar__btn:disabled{opacity:.35;cursor:not-allowed}.sd-preview-pdf-toolbar__page{display:inline-flex;align-items:center;gap:4px;padding:0 6px;flex-shrink:0}.sd-preview-pdf-toolbar__page-input{width:36px;height:24px;background:#ffffff14;border:1px solid rgba(255,255,255,.08);color:var(--sd-pdf-fg);border-radius:4px;text-align:center;font-size:13px;font-variant-numeric:tabular-nums;outline:none}.sd-preview-pdf-toolbar__page-input:focus{border-color:var(--sd-pdf-primary)}.sd-preview-pdf-toolbar__page-total{font-size:13px;color:var(--sd-pdf-fg-muted);font-variant-numeric:tabular-nums}.sd-preview-pdf-toolbar__zoom{min-width:56px;text-align:center;color:var(--sd-pdf-fg);font-size:13px;font-weight:500;font-variant-numeric:tabular-nums;background:#ffffff14;border-radius:4px;padding:2px 8px}.sd-preview-pdf-toolbar__divider{width:1px;height:20px;background:var(--sd-pdf-divider);margin:0 4px;flex-shrink:0}.sd-preview-pdf-status{margin:auto;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:16px;text-align:center;padding:32px;max-width:420px}.sd-preview-pdf-status__tile{width:96px;height:96px;border-radius:24px;display:flex;align-items:center;justify-content:center;border:1px solid var(--sd-pdf-divider)}.sd-preview-pdf-status__tile sd-icon{font-size:48px;width:48px;height:48px}.sd-preview-pdf-status__tile--muted{background:#ffffff0d}.sd-preview-pdf-status__tile--muted sd-icon{color:var(--sd-pdf-fg-subtle)}.sd-preview-pdf-status__tile--danger{background:var(--sd-pdf-danger-soft);border-color:var(--sd-pdf-danger-border)}.sd-preview-pdf-status__tile--danger sd-icon{color:var(--sd-pdf-danger)}.sd-preview-pdf-status__title{font-size:16px;font-weight:500;color:var(--sd-pdf-fg)}.sd-preview-pdf-status__hint{font-size:13px;color:var(--sd-pdf-fg-muted);line-height:1.5}.sd-preview-pdf-status__actions{display:flex;gap:8px;margin-top:4px}.sd-preview-pdf-progress-pct{font-variant-numeric:tabular-nums;color:var(--sd-pdf-fg-muted)}.sd-preview-pdf-progress{width:240px;height:4px;background:#ffffff14;border-radius:999px;overflow:hidden}.sd-preview-pdf-progress__fill{height:100%;background:var(--sd-pdf-primary);transition:width .12s linear}.sd-preview-pdf-spinner{width:56px;height:56px;border-radius:999px;border:3px solid rgba(255,255,255,.08);border-top-color:var(--sd-pdf-primary);animation:sd-preview-pdf-spin .9s linear infinite}@keyframes sd-preview-pdf-spin{to{transform:rotate(360deg)}}@keyframes sd-preview-pdf-thumb-skel{0%{background-position:200% 0}to{background-position:-200% 0}}.sd-preview-pdf-search-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:6px}.sd-preview-pdf-search-result{width:100%;display:flex;align-items:flex-start;gap:8px;padding:8px 10px;background:#ffffff0a;border:1px solid transparent;border-radius:6px;color:var(--sd-pdf-fg);cursor:pointer;text-align:left;transition:background .12s,border-color .12s}.sd-preview-pdf-search-result:hover{background:#ffffff12}.sd-preview-pdf-search-result--active{background:var(--sd-pdf-primary-soft);border-color:#005cbb73}.sd-preview-pdf-search-result__snippet{flex:1 1 auto;font-size:12px;line-height:1.4;color:var(--sd-pdf-fg);word-break:break-word}.sd-preview-pdf-search-result__snippet mark{background:var(--sd-pdf-search-hi);color:#000;padding:0 2px;border-radius:2px}.sd-preview-pdf-search-result__page{flex:0 0 auto;font-size:11px;font-variant-numeric:tabular-nums;color:var(--sd-pdf-fg-muted);background:#ffffff0f;padding:2px 6px;border-radius:4px}.sd-preview-pdf-btn-primary{display:inline-flex;align-items:center;gap:6px;height:36px;padding:0 16px;background:var(--sd-pdf-primary);color:#fff;border:none;border-radius:8px;cursor:pointer;font-size:13px;font-weight:500;transition:filter .12s}.sd-preview-pdf-btn-primary sd-icon{font-size:18px;width:18px;height:18px}.sd-preview-pdf-btn-primary:hover{filter:brightness(1.1)}.sd-preview-pdf-btn-primary:focus-visible{outline:2px solid #fff;outline-offset:2px}.sd-preview-pdf-btn-secondary{display:inline-flex;align-items:center;gap:6px;height:36px;padding:0 16px;background:transparent;color:var(--sd-pdf-fg);border:1px solid var(--sd-pdf-divider);border-radius:8px;cursor:pointer;font-size:13px;font-weight:500;transition:background .12s}.sd-preview-pdf-btn-secondary sd-icon{font-size:18px;width:18px;height:18px}.sd-preview-pdf-btn-secondary:hover{background:#ffffff0f}.sd-preview-pdf-btn-secondary:focus-visible{outline:2px solid var(--sd-pdf-primary);outline-offset:2px}\n"] }]
|
|
1981
1980
|
}], ctorParameters: () => [], propDecorators: { onKeyDown: [{
|
|
1982
1981
|
type: HostListener,
|
|
1983
1982
|
args: ['keydown', ['$event']]
|