ngx-kit 7.4.0 → 8.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +0,0 @@
1
- {"version":3,"file":"ngx-kit-drawer-menu.mjs","sources":["../../../projects/libs/drawer-menu/contracts/drawer-menu-types.ts","../../../projects/libs/drawer-menu/components/drawer-menu.component.ts","../../../projects/libs/drawer-menu/components/drawer-menu.component.html","../../../projects/libs/drawer-menu/ngx-kit-drawer-menu.ts"],"sourcesContent":["/** Logical drawer placement. `start` is right in RTL and left in LTR. */\nexport type NgxDrawerSide = 'start' | 'end';\n\n/** Visual physics used by the drawer while opening/closing. */\nexport type NgxDrawerEffect = 'none' | 'fabric' | 'slide' | 'push' | 'scale';\n\nexport type NgxDrawerBehavior = 'dock' | 'overlay';\n\nexport type NgxDrawerResponsiveMode = 'auto' | 'desktop' | 'mobile';\n\nexport interface NgxDrawerResponsive {\n /**\n * auto:\n * breakpoint determines desktop/mobile mode\n *\n * desktop:\n * always desktop\n *\n * mobile:\n * always mobile\n */\n mode?: NgxDrawerResponsiveMode;\n\n /**\n * Width at which the drawer switches\n * between desktop and mobile.\n */\n breakpoint?: number;\n\n /**\n * Initial/default state on desktop.\n */\n desktopOpen?: boolean;\n\n /**\n * Initial/default state on mobile.\n */\n mobileOpen?: boolean;\n\n /**\n * Desktop drawer behavior.\n */\n desktopBehavior?: NgxDrawerBehavior;\n\n /**\n * Mobile drawer behavior.\n */\n mobileBehavior?: NgxDrawerBehavior;\n\n /**\n * If true, pinned state is preserved\n * when switching between desktop/mobile.\n */\n respectPinned?: boolean;\n}\n\nexport const DEFAULT_DRAWER_RESPONSIVE: Required<NgxDrawerResponsive> = {\n mode: 'auto',\n breakpoint: 960,\n desktopOpen: true,\n mobileOpen: false,\n desktopBehavior: 'dock',\n mobileBehavior: 'overlay',\n respectPinned: true,\n};\n","import {\n ChangeDetectionStrategy,\n Component,\n DestroyRef,\n ElementRef,\n HostBinding,\n PLATFORM_ID,\n Renderer2,\n afterNextRender,\n computed,\n effect,\n inject,\n input,\n model,\n signal,\n} from '@angular/core';\n\nimport { isPlatformBrowser } from '@angular/common';\nimport {\n DEFAULT_DRAWER_RESPONSIVE,\n NgxDrawerBehavior,\n NgxDrawerEffect,\n NgxDrawerResponsive,\n NgxDrawerSide,\n} from '../contracts/drawer-menu-types';\n\n@Component({\n selector: 'ngx-drawer-menu',\n standalone: true,\n templateUrl: './drawer-menu.component.html',\n styleUrl: './drawer-menu.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class NgxDrawerMenuComponent {\n private readonly elementRef = inject(ElementRef<HTMLElement>);\n private readonly renderer = inject(Renderer2);\n private readonly destroyRef = inject(DestroyRef);\n private readonly platformId = inject(PLATFORM_ID);\n\n // ---------------------------------------------------------------------------\n // Inputs\n // ---------------------------------------------------------------------------\n\n readonly side = input<NgxDrawerSide>('start');\n\n readonly effect = input<NgxDrawerEffect>('fabric');\n\n readonly width = input<number | string>(300);\n\n /**\n * Main drawer state.\n *\n * Usage:\n *\n * [(open)]=\"sidebarOpen\"\n */\n readonly open = model<boolean>(false);\n\n /**\n * Pinned state.\n *\n * Useful for desktop layouts.\n *\n * [(pinned)]=\"sidebarPinned\"\n */\n readonly pinned = model<boolean>(false);\n\n readonly responsive = input<NgxDrawerResponsive>(DEFAULT_DRAWER_RESPONSIVE);\n\n /**\n * Whether clicking backdrop closes drawer.\n */\n readonly closeOnBackdrop = input(true);\n\n /**\n * Whether Escape closes drawer.\n */\n readonly closeOnEscape = input(true);\n\n /**\n * Whether body scrolling should be locked\n * while an overlay drawer is open.\n */\n readonly lockBodyScroll = input(true);\n\n /**\n * Animation duration in milliseconds.\n */\n readonly animationDuration = input(280);\n\n /**\n * z-index.\n */\n readonly zIndex = input(1000);\n\n // ---------------------------------------------------------------------------\n // Internal state\n // ---------------------------------------------------------------------------\n\n private readonly isBrowser = isPlatformBrowser(this.platformId);\n\n readonly isMobile = signal(false);\n\n readonly initialized = signal(false);\n\n /**\n * Used to avoid applying responsive defaults repeatedly.\n */\n private previousResponsiveMode: 'desktop' | 'mobile' | null = null;\n\n private mediaQuery?: MediaQueryList;\n\n private removeMediaQueryListener?: () => void;\n\n // ---------------------------------------------------------------------------\n // Computed\n // ---------------------------------------------------------------------------\n\n readonly responsiveConfig = computed(() => {\n const config = this.responsive();\n\n return {\n ...DEFAULT_DRAWER_RESPONSIVE,\n ...config,\n };\n });\n\n readonly currentMode = computed<'desktop' | 'mobile'>(() => {\n const config = this.responsiveConfig();\n\n if (config.mode === 'desktop') {\n return 'desktop';\n }\n\n if (config.mode === 'mobile') {\n return 'mobile';\n }\n\n return this.isMobile() ? 'mobile' : 'desktop';\n });\n\n readonly currentBehavior = computed<NgxDrawerBehavior>(() => {\n const config = this.responsiveConfig();\n\n return this.currentMode() === 'mobile' ? config.mobileBehavior : config.desktopBehavior;\n });\n\n readonly isOverlay = computed(() => {\n return this.currentBehavior() === 'overlay';\n });\n\n readonly isDocked = computed(() => {\n return this.currentBehavior() === 'dock';\n });\n\n readonly shouldShowBackdrop = computed(() => {\n return this.isOverlay() && this.open();\n });\n\n readonly drawerWidth = computed(() => {\n const width = this.width();\n\n if (typeof width === 'number') {\n return `${width}px`;\n }\n\n return width;\n });\n\n readonly drawerVisible = computed(() => {\n return this.open();\n });\n\n readonly shouldPreservePinned = computed(() => {\n return this.responsiveConfig().respectPinned;\n });\n\n readonly isPinnedAndDesktop = computed(() => {\n return this.currentMode() === 'desktop' && this.pinned() && this.shouldPreservePinned();\n });\n\n // ---------------------------------------------------------------------------\n // Host bindings\n // ---------------------------------------------------------------------------\n\n @HostBinding('class.drawer-start')\n get drawerStart(): boolean {\n return this.side() === 'start';\n }\n\n @HostBinding('class.drawer-end')\n get drawerEnd(): boolean {\n return this.side() === 'end';\n }\n\n @HostBinding('class.drawer-open')\n get drawerOpen(): boolean {\n return this.open();\n }\n\n @HostBinding('class.drawer-mobile')\n get drawerMobile(): boolean {\n return this.currentMode() === 'mobile';\n }\n\n @HostBinding('class.drawer-desktop')\n get drawerDesktop(): boolean {\n return this.currentMode() === 'desktop';\n }\n\n @HostBinding('class.drawer-overlay')\n get drawerOverlay(): boolean {\n return this.isOverlay();\n }\n\n @HostBinding('class.drawer-dock')\n get drawerDock(): boolean {\n return this.isDocked();\n }\n\n @HostBinding('style.--ngx-drawer-width')\n get hostWidth(): string {\n return this.drawerWidth();\n }\n\n @HostBinding('style.--ngx-drawer-duration')\n get hostDuration(): string {\n return `${this.animationDuration()}ms`;\n }\n\n @HostBinding('style.--ngx-drawer-z-index')\n get hostZIndex(): number {\n return this.zIndex();\n }\n\n @HostBinding('class.effect-none')\n get effectNone(): boolean {\n return this.effect() === 'none';\n }\n\n @HostBinding('class.effect-fabric')\n get effectFabric(): boolean {\n return this.effect() === 'fabric';\n }\n\n @HostBinding('class.effect-slide')\n get effectSlide(): boolean {\n return this.effect() === 'slide';\n }\n\n @HostBinding('class.effect-push')\n get effectPush(): boolean {\n return this.effect() === 'push';\n }\n\n @HostBinding('class.effect-scale')\n get effectScale(): boolean {\n return this.effect() === 'scale';\n }\n\n // ---------------------------------------------------------------------------\n // Constructor\n // ---------------------------------------------------------------------------\n\n constructor() {\n this.setupResponsive();\n\n effect(() => {\n const mode = this.currentMode();\n\n if (!this.initialized()) {\n return;\n }\n\n this.handleResponsiveModeChange(mode);\n });\n\n effect(() => {\n const shouldLock = this.lockBodyScroll();\n const shouldLockNow = this.isOverlay() && this.open();\n\n if (!shouldLock) {\n return;\n }\n\n if (shouldLockNow) {\n this.lockScroll();\n } else {\n this.unlockScroll();\n }\n });\n\n this.destroyRef.onDestroy(() => {\n this.destroyResponsive();\n\n this.unlockScroll();\n });\n }\n\n // ---------------------------------------------------------------------------\n // Responsive\n // ---------------------------------------------------------------------------\n\n private setupResponsive(): void {\n if (!this.isBrowser) {\n return;\n }\n\n afterNextRender(() => {\n this.setupMediaQuery();\n\n this.initialized.set(true);\n\n const mode = this.currentMode();\n\n this.previousResponsiveMode = mode;\n\n this.applyInitialResponsiveState(mode);\n });\n }\n\n private setupMediaQuery(): void {\n const config = this.responsiveConfig();\n\n if (config.mode !== 'auto' || this.isBrowser) {\n this.isMobile.set(config.mode === 'mobile');\n\n return;\n }\n\n const breakpoint = config.breakpoint;\n\n this.mediaQuery = window.matchMedia(`(max-width: ${breakpoint - 0.02}px)`);\n\n this.updateMobileState();\n\n const listener = () => {\n this.updateMobileState();\n };\n\n this.mediaQuery.addEventListener('change', listener);\n\n this.removeMediaQueryListener = () => {\n this.mediaQuery?.removeEventListener('change', listener);\n };\n }\n\n private updateMobileState(): void {\n if (!this.mediaQuery) {\n return;\n }\n\n this.isMobile.set(this.mediaQuery.matches);\n }\n\n private handleResponsiveModeChange(mode: 'desktop' | 'mobile'): void {\n if (this.previousResponsiveMode === null) {\n this.previousResponsiveMode = mode;\n return;\n }\n\n if (this.previousResponsiveMode === mode) {\n return;\n }\n\n const previousMode = this.previousResponsiveMode;\n\n this.previousResponsiveMode = mode;\n\n const config = this.responsiveConfig();\n\n /**\n * If pinned should be respected,\n * don't overwrite the current open state.\n */\n if (config.respectPinned && this.pinned()) {\n return;\n }\n\n if (mode === 'desktop') {\n this.open.set(config.desktopOpen);\n } else {\n this.open.set(config.mobileOpen);\n }\n\n this.renderer.setAttribute(this.elementRef.nativeElement, 'data-previous-mode', previousMode);\n }\n\n private applyInitialResponsiveState(mode: 'desktop' | 'mobile'): void {\n const config = this.responsiveConfig();\n\n if (config.respectPinned && this.pinned()) {\n return;\n }\n\n /**\n * Don't blindly overwrite an explicitly bound\n * [(open)] value after initialization.\n *\n * The default is only applied when the model\n * has not already been initialized.\n */\n if (mode === 'desktop') {\n this.open.set(config.desktopOpen);\n } else {\n this.open.set(config.mobileOpen);\n }\n }\n\n private destroyResponsive(): void {\n this.removeMediaQueryListener?.();\n\n this.mediaQuery = undefined;\n }\n\n // ---------------------------------------------------------------------------\n // Public API\n // ---------------------------------------------------------------------------\n\n toggle(): void {\n if (this.open()) {\n this.close();\n } else {\n this.openDrawer();\n }\n }\n\n openDrawer(): void {\n this.open.set(true);\n }\n\n close(): void {\n if (this.pinned() && this.isPinnedAndDesktop()) {\n return;\n }\n\n this.open.set(false);\n }\n\n pin(): void {\n this.pinned.set(true);\n this.open.set(true);\n }\n\n unpin(): void {\n this.pinned.set(false);\n }\n\n togglePinned(): void {\n if (this.pinned()) {\n this.unpin();\n } else {\n this.pin();\n }\n }\n\n // ---------------------------------------------------------------------------\n // Events\n // ---------------------------------------------------------------------------\n\n onBackdropClick(): void {\n if (!this.closeOnBackdrop()) {\n return;\n }\n\n this.close();\n }\n\n onKeyDown(event: KeyboardEvent): void {\n if (event.key !== 'Escape') {\n return;\n }\n\n if (!this.closeOnEscape()) {\n return;\n }\n\n if (!this.open()) {\n return;\n }\n\n this.close();\n }\n\n // ---------------------------------------------------------------------------\n // Scroll locking\n // ---------------------------------------------------------------------------\n\n private lockScroll(): void {\n if (!this.isBrowser) {\n return;\n }\n\n if (!document.body) {\n return;\n }\n\n if (document.body.dataset['ngxDrawerScrollLocked'] === 'true') {\n return;\n }\n\n this.renderer.setAttribute(document.body, 'data-ngx-drawer-scroll-locked', 'true');\n\n this.renderer.setStyle(document.body, 'overflow', 'hidden');\n }\n\n private unlockScroll(): void {\n if (!this.isBrowser) {\n return;\n }\n\n if (!document.body) {\n return;\n }\n\n if (document.body.dataset['ngxDrawerScrollLocked'] !== 'true') {\n return;\n }\n\n this.renderer.removeAttribute(document.body, 'data-ngx-drawer-scroll-locked');\n\n this.renderer.removeStyle(document.body, 'overflow');\n }\n}\n","<div\n class=\"ngx-drawer-root\"\n [class.is-open]=\"drawerVisible()\"\n [class.is-closed]=\"!drawerVisible()\"\n [class.is-overlay]=\"isOverlay()\"\n [class.is-dock]=\"isDocked()\"\n [class.is-pinned]=\"pinned()\"\n [class.is-mobile]=\"currentMode() === 'mobile'\"\n [class.is-desktop]=\"currentMode() === 'desktop'\"\n (keydown)=\"onKeyDown($event)\"\n tabindex=\"-1\">\n <!-- ===================================================================== -->\n <!-- Backdrop -->\n <!-- ===================================================================== -->\n\n @if (shouldShowBackdrop()) {\n <div class=\"ngx-drawer-backdrop\" aria-hidden=\"true\" (click)=\"onBackdropClick()\"></div>\n }\n\n <!-- ===================================================================== -->\n <!-- Drawer -->\n <!-- ===================================================================== -->\n\n <aside\n class=\"ngx-drawer\"\n [attr.aria-hidden]=\"!drawerVisible()\"\n [attr.data-side]=\"side()\"\n [attr.data-mode]=\"currentMode()\"\n [attr.data-behavior]=\"currentBehavior()\"\n [attr.data-effect]=\"effect()\"\n [attr.data-pinned]=\"pinned()\">\n <div class=\"ngx-drawer-surface\">\n <!-- Drawer Content -->\n <div class=\"ngx-drawer-content\">\n <ng-content select=\"[drawerContent]\"></ng-content>\n </div>\n </div>\n </aside>\n\n <!-- ===================================================================== -->\n <!-- Main Content -->\n <!-- ===================================================================== -->\n\n <main class=\"ngx-drawer-main\">\n <div class=\"ngx-drawer-main-content\">\n <ng-content></ng-content>\n </div>\n </main>\n</div>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;;;AAwDO,MAAM,yBAAyB,GAAkC;AACtE,IAAA,IAAI,EAAE,MAAM;AACZ,IAAA,UAAU,EAAE,GAAG;AACf,IAAA,WAAW,EAAE,IAAI;AACjB,IAAA,UAAU,EAAE,KAAK;AACjB,IAAA,eAAe,EAAE,MAAM;AACvB,IAAA,cAAc,EAAE,SAAS;AACzB,IAAA,aAAa,EAAE,IAAI;;;MC9BR,sBAAsB,CAAA;AAChB,IAAA,UAAU,GAAG,MAAM,EAAC,UAAuB,EAAC;AAC5C,IAAA,QAAQ,GAAG,MAAM,CAAC,SAAS,CAAC;AAC5B,IAAA,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC;AAC/B,IAAA,UAAU,GAAG,MAAM,CAAC,WAAW,CAAC;;;;IAMxC,IAAI,GAAG,KAAK,CAAgB,OAAO;6EAAC;IAEpC,MAAM,GAAG,KAAK,CAAkB,QAAQ;+EAAC;IAEzC,KAAK,GAAG,KAAK,CAAkB,GAAG;8EAAC;AAE5C;;;;;;AAMG;IACM,IAAI,GAAG,KAAK,CAAU,KAAK;6EAAC;AAErC;;;;;;AAMG;IACM,MAAM,GAAG,KAAK,CAAU,KAAK;+EAAC;IAE9B,UAAU,GAAG,KAAK,CAAsB,yBAAyB;mFAAC;AAE3E;;AAEG;IACM,eAAe,GAAG,KAAK,CAAC,IAAI;wFAAC;AAEtC;;AAEG;IACM,aAAa,GAAG,KAAK,CAAC,IAAI;sFAAC;AAEpC;;;AAGG;IACM,cAAc,GAAG,KAAK,CAAC,IAAI;uFAAC;AAErC;;AAEG;IACM,iBAAiB,GAAG,KAAK,CAAC,GAAG;0FAAC;AAEvC;;AAEG;IACM,MAAM,GAAG,KAAK,CAAC,IAAI;+EAAC;;;;AAMZ,IAAA,SAAS,GAAG,iBAAiB,CAAC,IAAI,CAAC,UAAU,CAAC;IAEtD,QAAQ,GAAG,MAAM,CAAC,KAAK;iFAAC;IAExB,WAAW,GAAG,MAAM,CAAC,KAAK;oFAAC;AAEpC;;AAEG;IACK,sBAAsB,GAAgC,IAAI;AAE1D,IAAA,UAAU;AAEV,IAAA,wBAAwB;;;;AAMvB,IAAA,gBAAgB,GAAG,QAAQ,CAAC,MAAK;AACxC,QAAA,MAAM,MAAM,GAAG,IAAI,CAAC,UAAU,EAAE;QAEhC,OAAO;AACL,YAAA,GAAG,yBAAyB;AAC5B,YAAA,GAAG,MAAM;SACV;IACH,CAAC;yFAAC;AAEO,IAAA,WAAW,GAAG,QAAQ,CAAuB,MAAK;AACzD,QAAA,MAAM,MAAM,GAAG,IAAI,CAAC,gBAAgB,EAAE;AAEtC,QAAA,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS,EAAE;AAC7B,YAAA,OAAO,SAAS;QAClB;AAEA,QAAA,IAAI,MAAM,CAAC,IAAI,KAAK,QAAQ,EAAE;AAC5B,YAAA,OAAO,QAAQ;QACjB;AAEA,QAAA,OAAO,IAAI,CAAC,QAAQ,EAAE,GAAG,QAAQ,GAAG,SAAS;IAC/C,CAAC;oFAAC;AAEO,IAAA,eAAe,GAAG,QAAQ,CAAoB,MAAK;AAC1D,QAAA,MAAM,MAAM,GAAG,IAAI,CAAC,gBAAgB,EAAE;AAEtC,QAAA,OAAO,IAAI,CAAC,WAAW,EAAE,KAAK,QAAQ,GAAG,MAAM,CAAC,cAAc,GAAG,MAAM,CAAC,eAAe;IACzF,CAAC;wFAAC;AAEO,IAAA,SAAS,GAAG,QAAQ,CAAC,MAAK;AACjC,QAAA,OAAO,IAAI,CAAC,eAAe,EAAE,KAAK,SAAS;IAC7C,CAAC;kFAAC;AAEO,IAAA,QAAQ,GAAG,QAAQ,CAAC,MAAK;AAChC,QAAA,OAAO,IAAI,CAAC,eAAe,EAAE,KAAK,MAAM;IAC1C,CAAC;iFAAC;AAEO,IAAA,kBAAkB,GAAG,QAAQ,CAAC,MAAK;QAC1C,OAAO,IAAI,CAAC,SAAS,EAAE,IAAI,IAAI,CAAC,IAAI,EAAE;IACxC,CAAC;2FAAC;AAEO,IAAA,WAAW,GAAG,QAAQ,CAAC,MAAK;AACnC,QAAA,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,EAAE;AAE1B,QAAA,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE;YAC7B,OAAO,CAAA,EAAG,KAAK,CAAA,EAAA,CAAI;QACrB;AAEA,QAAA,OAAO,KAAK;IACd,CAAC;oFAAC;AAEO,IAAA,aAAa,GAAG,QAAQ,CAAC,MAAK;AACrC,QAAA,OAAO,IAAI,CAAC,IAAI,EAAE;IACpB,CAAC;sFAAC;AAEO,IAAA,oBAAoB,GAAG,QAAQ,CAAC,MAAK;AAC5C,QAAA,OAAO,IAAI,CAAC,gBAAgB,EAAE,CAAC,aAAa;IAC9C,CAAC;6FAAC;AAEO,IAAA,kBAAkB,GAAG,QAAQ,CAAC,MAAK;AAC1C,QAAA,OAAO,IAAI,CAAC,WAAW,EAAE,KAAK,SAAS,IAAI,IAAI,CAAC,MAAM,EAAE,IAAI,IAAI,CAAC,oBAAoB,EAAE;IACzF,CAAC;2FAAC;;;;AAMF,IAAA,IACI,WAAW,GAAA;AACb,QAAA,OAAO,IAAI,CAAC,IAAI,EAAE,KAAK,OAAO;IAChC;AAEA,IAAA,IACI,SAAS,GAAA;AACX,QAAA,OAAO,IAAI,CAAC,IAAI,EAAE,KAAK,KAAK;IAC9B;AAEA,IAAA,IACI,UAAU,GAAA;AACZ,QAAA,OAAO,IAAI,CAAC,IAAI,EAAE;IACpB;AAEA,IAAA,IACI,YAAY,GAAA;AACd,QAAA,OAAO,IAAI,CAAC,WAAW,EAAE,KAAK,QAAQ;IACxC;AAEA,IAAA,IACI,aAAa,GAAA;AACf,QAAA,OAAO,IAAI,CAAC,WAAW,EAAE,KAAK,SAAS;IACzC;AAEA,IAAA,IACI,aAAa,GAAA;AACf,QAAA,OAAO,IAAI,CAAC,SAAS,EAAE;IACzB;AAEA,IAAA,IACI,UAAU,GAAA;AACZ,QAAA,OAAO,IAAI,CAAC,QAAQ,EAAE;IACxB;AAEA,IAAA,IACI,SAAS,GAAA;AACX,QAAA,OAAO,IAAI,CAAC,WAAW,EAAE;IAC3B;AAEA,IAAA,IACI,YAAY,GAAA;AACd,QAAA,OAAO,GAAG,IAAI,CAAC,iBAAiB,EAAE,IAAI;IACxC;AAEA,IAAA,IACI,UAAU,GAAA;AACZ,QAAA,OAAO,IAAI,CAAC,MAAM,EAAE;IACtB;AAEA,IAAA,IACI,UAAU,GAAA;AACZ,QAAA,OAAO,IAAI,CAAC,MAAM,EAAE,KAAK,MAAM;IACjC;AAEA,IAAA,IACI,YAAY,GAAA;AACd,QAAA,OAAO,IAAI,CAAC,MAAM,EAAE,KAAK,QAAQ;IACnC;AAEA,IAAA,IACI,WAAW,GAAA;AACb,QAAA,OAAO,IAAI,CAAC,MAAM,EAAE,KAAK,OAAO;IAClC;AAEA,IAAA,IACI,UAAU,GAAA;AACZ,QAAA,OAAO,IAAI,CAAC,MAAM,EAAE,KAAK,MAAM;IACjC;AAEA,IAAA,IACI,WAAW,GAAA;AACb,QAAA,OAAO,IAAI,CAAC,MAAM,EAAE,KAAK,OAAO;IAClC;;;;AAMA,IAAA,WAAA,GAAA;QACE,IAAI,CAAC,eAAe,EAAE;QAEtB,MAAM,CAAC,MAAK;AACV,YAAA,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,EAAE;AAE/B,YAAA,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE;gBACvB;YACF;AAEA,YAAA,IAAI,CAAC,0BAA0B,CAAC,IAAI,CAAC;AACvC,QAAA,CAAC,CAAC;QAEF,MAAM,CAAC,MAAK;AACV,YAAA,MAAM,UAAU,GAAG,IAAI,CAAC,cAAc,EAAE;YACxC,MAAM,aAAa,GAAG,IAAI,CAAC,SAAS,EAAE,IAAI,IAAI,CAAC,IAAI,EAAE;YAErD,IAAI,CAAC,UAAU,EAAE;gBACf;YACF;YAEA,IAAI,aAAa,EAAE;gBACjB,IAAI,CAAC,UAAU,EAAE;YACnB;iBAAO;gBACL,IAAI,CAAC,YAAY,EAAE;YACrB;AACF,QAAA,CAAC,CAAC;AAEF,QAAA,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,MAAK;YAC7B,IAAI,CAAC,iBAAiB,EAAE;YAExB,IAAI,CAAC,YAAY,EAAE;AACrB,QAAA,CAAC,CAAC;IACJ;;;;IAMQ,eAAe,GAAA;AACrB,QAAA,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE;YACnB;QACF;QAEA,eAAe,CAAC,MAAK;YACnB,IAAI,CAAC,eAAe,EAAE;AAEtB,YAAA,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC;AAE1B,YAAA,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,EAAE;AAE/B,YAAA,IAAI,CAAC,sBAAsB,GAAG,IAAI;AAElC,YAAA,IAAI,CAAC,2BAA2B,CAAC,IAAI,CAAC;AACxC,QAAA,CAAC,CAAC;IACJ;IAEQ,eAAe,GAAA;AACrB,QAAA,MAAM,MAAM,GAAG,IAAI,CAAC,gBAAgB,EAAE;QAEtC,IAAI,MAAM,CAAC,IAAI,KAAK,MAAM,IAAI,IAAI,CAAC,SAAS,EAAE;YAC5C,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,KAAK,QAAQ,CAAC;YAE3C;QACF;AAEA,QAAA,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU;AAEpC,QAAA,IAAI,CAAC,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC,CAAA,YAAA,EAAe,UAAU,GAAG,IAAI,CAAA,GAAA,CAAK,CAAC;QAE1E,IAAI,CAAC,iBAAiB,EAAE;QAExB,MAAM,QAAQ,GAAG,MAAK;YACpB,IAAI,CAAC,iBAAiB,EAAE;AAC1B,QAAA,CAAC;QAED,IAAI,CAAC,UAAU,CAAC,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,CAAC;AAEpD,QAAA,IAAI,CAAC,wBAAwB,GAAG,MAAK;YACnC,IAAI,CAAC,UAAU,EAAE,mBAAmB,CAAC,QAAQ,EAAE,QAAQ,CAAC;AAC1D,QAAA,CAAC;IACH;IAEQ,iBAAiB,GAAA;AACvB,QAAA,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE;YACpB;QACF;QAEA,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC;IAC5C;AAEQ,IAAA,0BAA0B,CAAC,IAA0B,EAAA;AAC3D,QAAA,IAAI,IAAI,CAAC,sBAAsB,KAAK,IAAI,EAAE;AACxC,YAAA,IAAI,CAAC,sBAAsB,GAAG,IAAI;YAClC;QACF;AAEA,QAAA,IAAI,IAAI,CAAC,sBAAsB,KAAK,IAAI,EAAE;YACxC;QACF;AAEA,QAAA,MAAM,YAAY,GAAG,IAAI,CAAC,sBAAsB;AAEhD,QAAA,IAAI,CAAC,sBAAsB,GAAG,IAAI;AAElC,QAAA,MAAM,MAAM,GAAG,IAAI,CAAC,gBAAgB,EAAE;AAEtC;;;AAGG;QACH,IAAI,MAAM,CAAC,aAAa,IAAI,IAAI,CAAC,MAAM,EAAE,EAAE;YACzC;QACF;AAEA,QAAA,IAAI,IAAI,KAAK,SAAS,EAAE;YACtB,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC;QACnC;aAAO;YACL,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC;QAClC;AAEA,QAAA,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC,aAAa,EAAE,oBAAoB,EAAE,YAAY,CAAC;IAC/F;AAEQ,IAAA,2BAA2B,CAAC,IAA0B,EAAA;AAC5D,QAAA,MAAM,MAAM,GAAG,IAAI,CAAC,gBAAgB,EAAE;QAEtC,IAAI,MAAM,CAAC,aAAa,IAAI,IAAI,CAAC,MAAM,EAAE,EAAE;YACzC;QACF;AAEA;;;;;;AAMG;AACH,QAAA,IAAI,IAAI,KAAK,SAAS,EAAE;YACtB,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC;QACnC;aAAO;YACL,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC;QAClC;IACF;IAEQ,iBAAiB,GAAA;AACvB,QAAA,IAAI,CAAC,wBAAwB,IAAI;AAEjC,QAAA,IAAI,CAAC,UAAU,GAAG,SAAS;IAC7B;;;;IAMA,MAAM,GAAA;AACJ,QAAA,IAAI,IAAI,CAAC,IAAI,EAAE,EAAE;YACf,IAAI,CAAC,KAAK,EAAE;QACd;aAAO;YACL,IAAI,CAAC,UAAU,EAAE;QACnB;IACF;IAEA,UAAU,GAAA;AACR,QAAA,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC;IACrB;IAEA,KAAK,GAAA;QACH,IAAI,IAAI,CAAC,MAAM,EAAE,IAAI,IAAI,CAAC,kBAAkB,EAAE,EAAE;YAC9C;QACF;AAEA,QAAA,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC;IACtB;IAEA,GAAG,GAAA;AACD,QAAA,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC;AACrB,QAAA,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC;IACrB;IAEA,KAAK,GAAA;AACH,QAAA,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC;IACxB;IAEA,YAAY,GAAA;AACV,QAAA,IAAI,IAAI,CAAC,MAAM,EAAE,EAAE;YACjB,IAAI,CAAC,KAAK,EAAE;QACd;aAAO;YACL,IAAI,CAAC,GAAG,EAAE;QACZ;IACF;;;;IAMA,eAAe,GAAA;AACb,QAAA,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,EAAE;YAC3B;QACF;QAEA,IAAI,CAAC,KAAK,EAAE;IACd;AAEA,IAAA,SAAS,CAAC,KAAoB,EAAA;AAC5B,QAAA,IAAI,KAAK,CAAC,GAAG,KAAK,QAAQ,EAAE;YAC1B;QACF;AAEA,QAAA,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,EAAE;YACzB;QACF;AAEA,QAAA,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE;YAChB;QACF;QAEA,IAAI,CAAC,KAAK,EAAE;IACd;;;;IAMQ,UAAU,GAAA;AAChB,QAAA,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE;YACnB;QACF;AAEA,QAAA,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE;YAClB;QACF;QAEA,IAAI,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,uBAAuB,CAAC,KAAK,MAAM,EAAE;YAC7D;QACF;AAEA,QAAA,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,QAAQ,CAAC,IAAI,EAAE,+BAA+B,EAAE,MAAM,CAAC;AAElF,QAAA,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,UAAU,EAAE,QAAQ,CAAC;IAC7D;IAEQ,YAAY,GAAA;AAClB,QAAA,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE;YACnB;QACF;AAEA,QAAA,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE;YAClB;QACF;QAEA,IAAI,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,uBAAuB,CAAC,KAAK,MAAM,EAAE;YAC7D;QACF;QAEA,IAAI,CAAC,QAAQ,CAAC,eAAe,CAAC,QAAQ,CAAC,IAAI,EAAE,+BAA+B,CAAC;QAE7E,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IACtD;uGAzeW,sBAAsB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAtB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,sBAAsB,ouECjCnC,k3DAiDA,EAAA,MAAA,EAAA,CAAA,0+JAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;2FDhBa,sBAAsB,EAAA,UAAA,EAAA,CAAA;kBAPlC,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,iBAAiB,EAAA,UAAA,EACf,IAAI,EAAA,eAAA,EAGC,uBAAuB,CAAC,MAAM,EAAA,QAAA,EAAA,k3DAAA,EAAA,MAAA,EAAA,CAAA,0+JAAA,CAAA,EAAA;;sBA0J9C,WAAW;uBAAC,oBAAoB;;sBAKhC,WAAW;uBAAC,kBAAkB;;sBAK9B,WAAW;uBAAC,mBAAmB;;sBAK/B,WAAW;uBAAC,qBAAqB;;sBAKjC,WAAW;uBAAC,sBAAsB;;sBAKlC,WAAW;uBAAC,sBAAsB;;sBAKlC,WAAW;uBAAC,mBAAmB;;sBAK/B,WAAW;uBAAC,0BAA0B;;sBAKtC,WAAW;uBAAC,6BAA6B;;sBAKzC,WAAW;uBAAC,4BAA4B;;sBAKxC,WAAW;uBAAC,mBAAmB;;sBAK/B,WAAW;uBAAC,qBAAqB;;sBAKjC,WAAW;uBAAC,oBAAoB;;sBAKhC,WAAW;uBAAC,mBAAmB;;sBAK/B,WAAW;uBAAC,oBAAoB;;;AE/PnC;;AAEG;;;;"}
@@ -1,419 +0,0 @@
1
- import * as i0 from '@angular/core';
2
- import { input, output, signal, computed, viewChild, inject, ElementRef, DestroyRef, effect, HostListener, ChangeDetectionStrategy, Component, ApplicationRef, EnvironmentInjector, createComponent, Injectable } from '@angular/core';
3
- import { startDragSession } from 'ngx-kit/core';
4
-
5
- const NGX_IMAGE_VIEWER_DEFAULT_TOOLBAR = {
6
- prevNext: true,
7
- counter: true,
8
- zoomIn: true,
9
- zoomOut: true,
10
- resetZoom: true,
11
- rotateLeft: true,
12
- rotateRight: true,
13
- download: true,
14
- print: true,
15
- fullscreen: true,
16
- close: false,
17
- };
18
-
19
- /** دانلود تصویر با fetch→blob (نام‌گذاریِ درست فایل رو تضمین می‌کنه)؛ اگه CORS اجازه نداد، در تب جدید باز می‌شه */
20
- async function downloadImage(src, filename) {
21
- try {
22
- const response = await fetch(src, { mode: 'cors' });
23
- if (!response.ok)
24
- throw new Error('fetch failed');
25
- const blob = await response.blob();
26
- const url = URL.createObjectURL(blob);
27
- const a = document.createElement('a');
28
- a.href = url;
29
- a.download = filename || guessFileName(src);
30
- document.body.appendChild(a);
31
- a.click();
32
- a.remove();
33
- URL.revokeObjectURL(url);
34
- }
35
- catch {
36
- window.open(src, '_blank', 'noopener,noreferrer');
37
- }
38
- }
39
- function guessFileName(src) {
40
- try {
41
- const url = new URL(src, typeof window !== 'undefined' ? window.location.href : undefined);
42
- const last = url.pathname.split('/').pop();
43
- return last || 'image';
44
- }
45
- catch {
46
- return 'image';
47
- }
48
- }
49
- /** یه پنجره‌ی جدید فقط با همون تصویر باز می‌کنه و بعد از لود، print می‌زنه؛ با DOM API (نه innerHTML) پس در برابر src مخرب امنه */
50
- function printImage(src) {
51
- const win = window.open('', '_blank', 'noopener,noreferrer,width=800,height=600');
52
- if (!win)
53
- return;
54
- win.document.title = 'Print';
55
- const style = win.document.createElement('style');
56
- style.textContent =
57
- 'body{margin:0;display:flex;align-items:center;justify-content:center;min-height:100vh;background:#fff;}img{max-width:100%;max-height:100vh;}';
58
- win.document.head.appendChild(style);
59
- const img = win.document.createElement('img');
60
- img.onload = () => {
61
- win.focus();
62
- win.print();
63
- };
64
- img.src = src;
65
- win.document.body.appendChild(img);
66
- }
67
-
68
- /**
69
- * با تغییر zoom، pan رو طوری تنظیم می‌کنه که نقطه‌ی زیر cursor/انگشت‌ها
70
- * (نسبت به مرکز کانتینر) سرجاش بمونه — یعنی zoom-to-cursor به‌جای
71
- * zoom-to-center (که تجربه‌ی خیلی بهتری داره، دقیقاً مثل هر ویوئر حرفه‌ای).
72
- *
73
- * ریاضیِ پشتش: با transform="translate(panX,panY) scale(zoom)"، موقعیتِ
74
- * صفحه‌ایِ یه نقطه‌ی محلیِ p (نسبت به مرکز) برابره با
75
- * center + pan + p*zoom. اول p رو از روی وضعیت فعلی حساب می‌کنیم، بعد pan
76
- * جدید رو طوری می‌سازیم که با zoom تازه، همون p به همون مختصات صفحه برسه.
77
- */
78
- function zoomAroundPoint(current, newZoom, cursorX, cursorY, containerWidth, containerHeight) {
79
- const centerX = containerWidth / 2;
80
- const centerY = containerHeight / 2;
81
- const localX = (cursorX - centerX - current.panX) / current.zoom;
82
- const localY = (cursorY - centerY - current.panY) / current.zoom;
83
- return {
84
- zoom: newZoom,
85
- panX: cursorX - centerX - localX * newZoom,
86
- panY: cursorY - centerY - localY * newZoom,
87
- };
88
- }
89
- function distanceBetweenTouches(a, b) {
90
- return Math.hypot(a.clientX - b.clientX, a.clientY - b.clientY);
91
- }
92
- function midpointOfTouches(a, b) {
93
- return { x: (a.clientX + b.clientX) / 2, y: (a.clientY + b.clientY) / 2 };
94
- }
95
- function clamp(value, min, max) {
96
- return Math.min(max, Math.max(min, value));
97
- }
98
-
99
- /**
100
- * ویوئر حرفه‌ایِ تصویر: یه لیست تصویر می‌گیره، اولی رو نشون می‌ده، با
101
- * دکمه‌های چپ/راست (مثل کاروسل) بین‌شون می‌ره، و یه نوار ابزار کاملاً
102
- * قابل‌تنظیم (هر دکمه جدا فعال/غیرفعال می‌شه) داره: زوم این/اوت/ریست،
103
- * چرخش، دانلود، پرینت، تمام‌صفحه.
104
- *
105
- * تعامل‌ها:
106
- * - اسکرول موس روی تصویر = زوم (نسبت به همون نقطه‌ی زیر cursor، نه مرکز)
107
- * - دو انگشتی روی تاچ = پینچ زوم
108
- * - وقتی زوم‌شده، درگ با موس یا تک‌انگشتی = پن
109
- * - کیبورد: ← → برای قبلی/بعدی، +/- برای زوم، Esc برای closed
110
- *
111
- * چون wheel/touchmove باید preventDefault بشن (وگرنه صفحه اسکرول/زوم
112
- * می‌شه) و انگیولار این ایونت‌ها رو به‌صورت پیش‌فرض passive رجیستر می‌کنه
113
- * (که توش preventDefault اثر نداره)، این‌ها با addEventListener دستی و
114
- * {passive:false} وصل می‌شن، نه با بایندینگ تمپلیت.
115
- *
116
- * می‌تونه هم مستقیم توی صفحه جا بگیره، هم توسط مصرف‌کننده داخل
117
- * ngx-kit/dialog (یا هر overlay دیگه) قرار بگیره — خودش به هیچ‌کدوم وابسته
118
- * نیست، فقط با closed خروجی به بیرون خبر می‌ده که «ببندم».
119
- */
120
- class NgxImageViewerComponent {
121
- images = input.required(/* @ts-ignore */
122
- ...(ngDevMode ? [{ debugName: "images" }] : /* istanbul ignore next */ []));
123
- startIndex = input(0, /* @ts-ignore */
124
- ...(ngDevMode ? [{ debugName: "startIndex" }] : /* istanbul ignore next */ []));
125
- toolbar = input({}, /* @ts-ignore */
126
- ...(ngDevMode ? [{ debugName: "toolbar" }] : /* istanbul ignore next */ []));
127
- minZoom = input(1, /* @ts-ignore */
128
- ...(ngDevMode ? [{ debugName: "minZoom" }] : /* istanbul ignore next */ []));
129
- maxZoom = input(6, /* @ts-ignore */
130
- ...(ngDevMode ? [{ debugName: "maxZoom" }] : /* istanbul ignore next */ []));
131
- zoomStep = input(0.4, /* @ts-ignore */
132
- ...(ngDevMode ? [{ debugName: "zoomStep" }] : /* istanbul ignore next */ []));
133
- indexChange = output();
134
- closed = output();
135
- activeIndex = signal(0, /* @ts-ignore */
136
- ...(ngDevMode ? [{ debugName: "activeIndex" }] : /* istanbul ignore next */ []));
137
- zoom = signal(1, /* @ts-ignore */
138
- ...(ngDevMode ? [{ debugName: "zoom" }] : /* istanbul ignore next */ []));
139
- panX = signal(0, /* @ts-ignore */
140
- ...(ngDevMode ? [{ debugName: "panX" }] : /* istanbul ignore next */ []));
141
- panY = signal(0, /* @ts-ignore */
142
- ...(ngDevMode ? [{ debugName: "panY" }] : /* istanbul ignore next */ []));
143
- rotation = signal(0, /* @ts-ignore */
144
- ...(ngDevMode ? [{ debugName: "rotation" }] : /* istanbul ignore next */ []));
145
- activeImage = computed(() => this.images()[this.activeIndex()], /* @ts-ignore */
146
- ...(ngDevMode ? [{ debugName: "activeImage" }] : /* istanbul ignore next */ []));
147
- toolbarConfig = computed(() => ({
148
- ...NGX_IMAGE_VIEWER_DEFAULT_TOOLBAR,
149
- ...this.toolbar(),
150
- }), /* @ts-ignore */
151
- ...(ngDevMode ? [{ debugName: "toolbarConfig" }] : /* istanbul ignore next */ []));
152
- isZoomed = computed(() => this.zoom() > 1, /* @ts-ignore */
153
- ...(ngDevMode ? [{ debugName: "isZoomed" }] : /* istanbul ignore next */ []));
154
- stageTransform = computed(() => `translate(${this.panX()}px, ${this.panY()}px) scale(${this.zoom()}) rotate(${this.rotation()}deg)`, /* @ts-ignore */
155
- ...(ngDevMode ? [{ debugName: "stageTransform" }] : /* istanbul ignore next */ []));
156
- stageEl = viewChild('stage', /* @ts-ignore */
157
- ...(ngDevMode ? [{ debugName: "stageEl" }] : /* istanbul ignore next */ []));
158
- hostElRef = inject((ElementRef));
159
- destroyRef = inject(DestroyRef);
160
- pinchStartDistance = 0;
161
- pinchStartZoom = 1;
162
- stopPanDrag;
163
- constructor() {
164
- this.activeIndex.set(this.startIndex());
165
- // wheel/touch با addEventListener دستی + passive:false وصل می‌شن (دلیلش
166
- // بالای کلاس توضیح داده شده)؛ effect به‌صورت خودکار وقتی stageEl از
167
- // undefined به یه ElementRef واقعی تغییر کنه (یعنی بعد از اولین رندر)
168
- // دوباره اجرا می‌شه، پس نیازی به ngAfterViewInit نیست.
169
- effect((onCleanup) => {
170
- const stage = this.stageEl()?.nativeElement;
171
- if (!stage)
172
- return;
173
- const onWheel = (ev) => {
174
- ev.preventDefault();
175
- const rect = stage.getBoundingClientRect();
176
- this.applyZoomAt(this.zoom() + (ev.deltaY < 0 ? 1 : -1) * this.zoomStep(), ev.clientX - rect.left, ev.clientY - rect.top, rect.width, rect.height);
177
- };
178
- const onTouchStart = (ev) => {
179
- if (ev.touches.length === 2) {
180
- this.stopPanDrag?.();
181
- this.pinchStartDistance = distanceBetweenTouches(ev.touches[0], ev.touches[1]);
182
- this.pinchStartZoom = this.zoom();
183
- }
184
- else if (ev.touches.length === 1 && this.isZoomed()) {
185
- this.beginPanDrag(ev);
186
- }
187
- };
188
- const onTouchMove = (ev) => {
189
- if (ev.touches.length === 2 && this.pinchStartDistance > 0) {
190
- ev.preventDefault();
191
- const dist = distanceBetweenTouches(ev.touches[0], ev.touches[1]);
192
- const newZoom = (dist / this.pinchStartDistance) * this.pinchStartZoom;
193
- const rect = stage.getBoundingClientRect();
194
- const mid = midpointOfTouches(ev.touches[0], ev.touches[1]);
195
- this.applyZoomAt(newZoom, mid.x - rect.left, mid.y - rect.top, rect.width, rect.height);
196
- }
197
- };
198
- const onTouchEnd = () => {
199
- this.pinchStartDistance = 0;
200
- };
201
- stage.addEventListener('wheel', onWheel, { passive: false });
202
- stage.addEventListener('touchstart', onTouchStart, { passive: false });
203
- stage.addEventListener('touchmove', onTouchMove, { passive: false });
204
- stage.addEventListener('touchend', onTouchEnd);
205
- stage.addEventListener('touchcancel', onTouchEnd);
206
- onCleanup(() => {
207
- stage.removeEventListener('wheel', onWheel);
208
- stage.removeEventListener('touchstart', onTouchStart);
209
- stage.removeEventListener('touchmove', onTouchMove);
210
- stage.removeEventListener('touchend', onTouchEnd);
211
- stage.removeEventListener('touchcancel', onTouchEnd);
212
- });
213
- });
214
- this.destroyRef.onDestroy(() => this.stopPanDrag?.());
215
- }
216
- onKeydown(ev) {
217
- if (ev.key === 'ArrowLeft')
218
- this.prev();
219
- else if (ev.key === 'ArrowRight')
220
- this.next();
221
- else if (ev.key === '+' || ev.key === '=')
222
- this.zoomIn();
223
- else if (ev.key === '-')
224
- this.zoomOut();
225
- else if (ev.key === 'Escape')
226
- this.closed.emit();
227
- else
228
- return;
229
- ev.preventDefault();
230
- }
231
- next() {
232
- const len = this.images().length;
233
- if (len)
234
- this.goTo((this.activeIndex() + 1) % len);
235
- }
236
- prev() {
237
- const len = this.images().length;
238
- if (len)
239
- this.goTo((this.activeIndex() - 1 + len) % len);
240
- }
241
- goTo(index) {
242
- this.activeIndex.set(index);
243
- this.resetView();
244
- this.indexChange.emit(index);
245
- }
246
- zoomIn() {
247
- this.setZoomAtCenter(this.zoom() + this.zoomStep());
248
- }
249
- zoomOut() {
250
- this.setZoomAtCenter(this.zoom() - this.zoomStep());
251
- }
252
- resetView() {
253
- this.zoom.set(1);
254
- this.panX.set(0);
255
- this.panY.set(0);
256
- this.rotation.set(0);
257
- }
258
- rotateLeft() {
259
- this.rotation.update((r) => r - 90);
260
- }
261
- rotateRight() {
262
- this.rotation.update((r) => r + 90);
263
- }
264
- async download() {
265
- const img = this.activeImage();
266
- if (img)
267
- await downloadImage(img.src, img.downloadFileName);
268
- }
269
- print() {
270
- const img = this.activeImage();
271
- if (img)
272
- printImage(img.src);
273
- }
274
- toggleFullscreen() {
275
- const el = this.hostElRef.nativeElement;
276
- if (document.fullscreenElement) {
277
- document.exitFullscreen();
278
- }
279
- else {
280
- el.requestFullscreen?.();
281
- }
282
- }
283
- close() {
284
- this.closed.emit();
285
- }
286
- onPanPointerDown(ev) {
287
- if (!this.isZoomed())
288
- return;
289
- this.beginPanDrag(ev);
290
- }
291
- beginPanDrag(ev) {
292
- ev.preventDefault();
293
- const startPanX = this.panX();
294
- const startPanY = this.panY();
295
- const startX = this.clientXOf(ev);
296
- const startY = this.clientYOf(ev);
297
- this.stopPanDrag?.();
298
- this.stopPanDrag = startDragSession({
299
- onMove: (moveEv) => {
300
- this.panX.set(startPanX + (this.clientXOf(moveEv) - startX));
301
- this.panY.set(startPanY + (this.clientYOf(moveEv) - startY));
302
- },
303
- onEnd: () => {
304
- this.stopPanDrag = undefined;
305
- },
306
- });
307
- }
308
- setZoomAtCenter(newZoom) {
309
- const stage = this.stageEl()?.nativeElement;
310
- if (!stage) {
311
- this.zoom.set(clamp(newZoom, this.minZoom(), this.maxZoom()));
312
- return;
313
- }
314
- const rect = stage.getBoundingClientRect();
315
- this.applyZoomAt(newZoom, rect.width / 2, rect.height / 2, rect.width, rect.height);
316
- }
317
- applyZoomAt(rawZoom, x, y, containerWidth, containerHeight) {
318
- const newZoom = clamp(rawZoom, this.minZoom(), this.maxZoom());
319
- if (newZoom === this.zoom())
320
- return;
321
- const result = zoomAroundPoint({ zoom: this.zoom(), panX: this.panX(), panY: this.panY() }, newZoom, x, y, containerWidth, containerHeight);
322
- this.zoom.set(result.zoom);
323
- this.panX.set(result.panX);
324
- this.panY.set(result.panY);
325
- }
326
- clientXOf(ev) {
327
- return 'touches' in ev
328
- ? (ev.touches[0]?.clientX ?? ev.changedTouches[0]?.clientX ?? 0)
329
- : ev.clientX;
330
- }
331
- clientYOf(ev) {
332
- return 'touches' in ev
333
- ? (ev.touches[0]?.clientY ?? ev.changedTouches[0]?.clientY ?? 0)
334
- : ev.clientY;
335
- }
336
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: NgxImageViewerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
337
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.4", type: NgxImageViewerComponent, isStandalone: true, selector: "ngx-image-viewer", inputs: { images: { classPropertyName: "images", publicName: "images", isSignal: true, isRequired: true, transformFunction: null }, startIndex: { classPropertyName: "startIndex", publicName: "startIndex", isSignal: true, isRequired: false, transformFunction: null }, toolbar: { classPropertyName: "toolbar", publicName: "toolbar", isSignal: true, isRequired: false, transformFunction: null }, minZoom: { classPropertyName: "minZoom", publicName: "minZoom", isSignal: true, isRequired: false, transformFunction: null }, maxZoom: { classPropertyName: "maxZoom", publicName: "maxZoom", isSignal: true, isRequired: false, transformFunction: null }, zoomStep: { classPropertyName: "zoomStep", publicName: "zoomStep", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { indexChange: "indexChange", closed: "closed" }, host: { attributes: { "tabindex": "0" }, listeners: { "keydown": "onKeydown($event)" }, classAttribute: "ngx-image-viewer-host" }, viewQueries: [{ propertyName: "stageEl", first: true, predicate: ["stage"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"ngx-iv-stage\" #stage [class.zoomed]=\"isZoomed()\">\n @if (toolbarConfig().close) {\n <button type=\"button\" class=\"ngx-iv-close\" (click)=\"close()\" aria-label=\"Close\">\u2715</button>\n }\n\n @if (activeImage(); as img) {\n <img\n class=\"ngx-iv-image\"\n [class.grab]=\"isZoomed()\"\n [src]=\"img.src\"\n [alt]=\"img.alt || ''\"\n draggable=\"false\"\n [style.transform]=\"stageTransform()\"\n (mousedown)=\"onPanPointerDown($event)\" />\n }\n\n @if (toolbarConfig().prevNext && images().length > 1) {\n <button\n type=\"button\"\n class=\"ngx-iv-nav ngx-iv-nav--prev\"\n (click)=\"prev()\"\n aria-label=\"Previous\">\n \u2039\n </button>\n <button type=\"button\" class=\"ngx-iv-nav ngx-iv-nav--next\" (click)=\"next()\" aria-label=\"Next\">\n \u203A\n </button>\n }\n\n @if (toolbarConfig().counter && images().length > 1) {\n <div class=\"ngx-iv-counter\">{{ activeIndex() + 1 }} / {{ images().length }}</div>\n }\n</div>\n\n@if (activeImage()?.caption) {\n <div class=\"ngx-iv-caption\">{{ activeImage()?.caption }}</div>\n}\n\n<div class=\"ngx-iv-toolbar\">\n @if (toolbarConfig().zoomOut) {\n <button type=\"button\" (click)=\"zoomOut()\" title=\"Zoom out\">\uFF0D</button>\n }\n @if (toolbarConfig().zoomIn) {\n <button type=\"button\" (click)=\"zoomIn()\" title=\"Zoom in\">\uFF0B</button>\n }\n @if (toolbarConfig().resetZoom) {\n <button type=\"button\" (click)=\"resetView()\" title=\"Reset zoom\">\u2922</button>\n }\n @if (toolbarConfig().rotateLeft) {\n <button type=\"button\" (click)=\"rotateLeft()\" title=\"Rotate left\">\u27F2</button>\n }\n @if (toolbarConfig().rotateRight) {\n <button type=\"button\" (click)=\"rotateRight()\" title=\"Rotate right\">\u27F3</button>\n }\n @if (toolbarConfig().download) {\n <button type=\"button\" (click)=\"download()\" title=\"Download\">\u2B73</button>\n }\n @if (toolbarConfig().print) {\n <button type=\"button\" (click)=\"print()\" title=\"Print\">\uD83D\uDDB6</button>\n }\n @if (toolbarConfig().fullscreen) {\n <button type=\"button\" (click)=\"toggleFullscreen()\" title=\"Fullscreen\">\u26F6</button>\n }\n</div>\n", styles: [":host{display:flex;flex-direction:column;gap:.5rem;outline:none}.ngx-iv-stage{position:relative;block-size:420px;background:light-dark(#f3f4f6,#0b0b0d);border-radius:10px;overflow:hidden;touch-action:none;-webkit-user-select:none;user-select:none}.ngx-iv-image{position:absolute;inset:0;margin:auto;max-inline-size:100%;max-block-size:100%;inline-size:auto;block-size:auto;object-fit:contain;cursor:default;transition:transform 60ms linear}.ngx-iv-image.grab{cursor:grab}.ngx-iv-image.grab:active{cursor:grabbing}.ngx-iv-nav{position:absolute;inset-block-start:50%;translate:0 -50%;inline-size:40px;block-size:40px;border-radius:50%;border:none;background:#00000073;color:#fff;font-size:1.5rem;line-height:1;cursor:pointer;display:flex;align-items:center;justify-content:center}.ngx-iv-nav:hover{background:#000000a6}.ngx-iv-nav--prev{inset-inline-start:.6rem}.ngx-iv-nav--next{inset-inline-end:.6rem}.ngx-iv-counter{position:absolute;inset-block-end:.6rem;inset-inline-start:50%;translate:-50% 0;padding:.2rem .7rem;border-radius:999px;background:#00000080;color:#fff;font-size:.75rem}.ngx-iv-close{position:absolute;inset-block-start:.6rem;inset-inline-end:.6rem;inline-size:32px;block-size:32px;border-radius:50%;border:none;background:#00000073;color:#fff;cursor:pointer;z-index:2}.ngx-iv-caption{font-size:.85rem;color:light-dark(#4b5563,#a1a1aa);text-align:center}.ngx-iv-toolbar{display:flex;justify-content:center;flex-wrap:wrap;gap:.4rem}.ngx-iv-toolbar button{appearance:none;border:1px solid light-dark(#d1d5db,#3f3f46);background:light-dark(#ffffff,#232327);color:light-dark(#374151,#d4d4d8);border-radius:8px;inline-size:36px;block-size:36px;font-size:1rem;cursor:pointer}.ngx-iv-toolbar button:hover{background:light-dark(#f3f4f6,#2c2c31)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
338
- }
339
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: NgxImageViewerComponent, decorators: [{
340
- type: Component,
341
- args: [{ selector: 'ngx-image-viewer', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, host: { class: 'ngx-image-viewer-host', tabindex: '0' }, template: "<div class=\"ngx-iv-stage\" #stage [class.zoomed]=\"isZoomed()\">\n @if (toolbarConfig().close) {\n <button type=\"button\" class=\"ngx-iv-close\" (click)=\"close()\" aria-label=\"Close\">\u2715</button>\n }\n\n @if (activeImage(); as img) {\n <img\n class=\"ngx-iv-image\"\n [class.grab]=\"isZoomed()\"\n [src]=\"img.src\"\n [alt]=\"img.alt || ''\"\n draggable=\"false\"\n [style.transform]=\"stageTransform()\"\n (mousedown)=\"onPanPointerDown($event)\" />\n }\n\n @if (toolbarConfig().prevNext && images().length > 1) {\n <button\n type=\"button\"\n class=\"ngx-iv-nav ngx-iv-nav--prev\"\n (click)=\"prev()\"\n aria-label=\"Previous\">\n \u2039\n </button>\n <button type=\"button\" class=\"ngx-iv-nav ngx-iv-nav--next\" (click)=\"next()\" aria-label=\"Next\">\n \u203A\n </button>\n }\n\n @if (toolbarConfig().counter && images().length > 1) {\n <div class=\"ngx-iv-counter\">{{ activeIndex() + 1 }} / {{ images().length }}</div>\n }\n</div>\n\n@if (activeImage()?.caption) {\n <div class=\"ngx-iv-caption\">{{ activeImage()?.caption }}</div>\n}\n\n<div class=\"ngx-iv-toolbar\">\n @if (toolbarConfig().zoomOut) {\n <button type=\"button\" (click)=\"zoomOut()\" title=\"Zoom out\">\uFF0D</button>\n }\n @if (toolbarConfig().zoomIn) {\n <button type=\"button\" (click)=\"zoomIn()\" title=\"Zoom in\">\uFF0B</button>\n }\n @if (toolbarConfig().resetZoom) {\n <button type=\"button\" (click)=\"resetView()\" title=\"Reset zoom\">\u2922</button>\n }\n @if (toolbarConfig().rotateLeft) {\n <button type=\"button\" (click)=\"rotateLeft()\" title=\"Rotate left\">\u27F2</button>\n }\n @if (toolbarConfig().rotateRight) {\n <button type=\"button\" (click)=\"rotateRight()\" title=\"Rotate right\">\u27F3</button>\n }\n @if (toolbarConfig().download) {\n <button type=\"button\" (click)=\"download()\" title=\"Download\">\u2B73</button>\n }\n @if (toolbarConfig().print) {\n <button type=\"button\" (click)=\"print()\" title=\"Print\">\uD83D\uDDB6</button>\n }\n @if (toolbarConfig().fullscreen) {\n <button type=\"button\" (click)=\"toggleFullscreen()\" title=\"Fullscreen\">\u26F6</button>\n }\n</div>\n", styles: [":host{display:flex;flex-direction:column;gap:.5rem;outline:none}.ngx-iv-stage{position:relative;block-size:420px;background:light-dark(#f3f4f6,#0b0b0d);border-radius:10px;overflow:hidden;touch-action:none;-webkit-user-select:none;user-select:none}.ngx-iv-image{position:absolute;inset:0;margin:auto;max-inline-size:100%;max-block-size:100%;inline-size:auto;block-size:auto;object-fit:contain;cursor:default;transition:transform 60ms linear}.ngx-iv-image.grab{cursor:grab}.ngx-iv-image.grab:active{cursor:grabbing}.ngx-iv-nav{position:absolute;inset-block-start:50%;translate:0 -50%;inline-size:40px;block-size:40px;border-radius:50%;border:none;background:#00000073;color:#fff;font-size:1.5rem;line-height:1;cursor:pointer;display:flex;align-items:center;justify-content:center}.ngx-iv-nav:hover{background:#000000a6}.ngx-iv-nav--prev{inset-inline-start:.6rem}.ngx-iv-nav--next{inset-inline-end:.6rem}.ngx-iv-counter{position:absolute;inset-block-end:.6rem;inset-inline-start:50%;translate:-50% 0;padding:.2rem .7rem;border-radius:999px;background:#00000080;color:#fff;font-size:.75rem}.ngx-iv-close{position:absolute;inset-block-start:.6rem;inset-inline-end:.6rem;inline-size:32px;block-size:32px;border-radius:50%;border:none;background:#00000073;color:#fff;cursor:pointer;z-index:2}.ngx-iv-caption{font-size:.85rem;color:light-dark(#4b5563,#a1a1aa);text-align:center}.ngx-iv-toolbar{display:flex;justify-content:center;flex-wrap:wrap;gap:.4rem}.ngx-iv-toolbar button{appearance:none;border:1px solid light-dark(#d1d5db,#3f3f46);background:light-dark(#ffffff,#232327);color:light-dark(#374151,#d4d4d8);border-radius:8px;inline-size:36px;block-size:36px;font-size:1rem;cursor:pointer}.ngx-iv-toolbar button:hover{background:light-dark(#f3f4f6,#2c2c31)}\n"] }]
342
- }], ctorParameters: () => [], propDecorators: { images: [{ type: i0.Input, args: [{ isSignal: true, alias: "images", required: true }] }], startIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "startIndex", required: false }] }], toolbar: [{ type: i0.Input, args: [{ isSignal: true, alias: "toolbar", required: false }] }], minZoom: [{ type: i0.Input, args: [{ isSignal: true, alias: "minZoom", required: false }] }], maxZoom: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxZoom", required: false }] }], zoomStep: [{ type: i0.Input, args: [{ isSignal: true, alias: "zoomStep", required: false }] }], indexChange: [{ type: i0.Output, args: ["indexChange"] }], closed: [{ type: i0.Output, args: ["closed"] }], stageEl: [{ type: i0.ViewChild, args: ['stage', { isSignal: true }] }], onKeydown: [{
343
- type: HostListener,
344
- args: ['keydown', ['$event']]
345
- }] } });
346
-
347
- /**
348
- * راه میان‌بر برای وقتی که کاربر می‌خواد ویوئر رو داخل یه دیالوگِ تمام‌صفحه
349
- * باز کنه، بدون این‌که خودش دستی wire کنه. اگه کسی می‌خواد داخل دیالوگِ
350
- * خودش (مثلاً ngx-kit/dialog) بذارتش، کافیه مستقیم از NgxImageViewerComponent
351
- * استفاده کنه — این سرویس صرفاً یه میان‌بَره، وابستگی اجباری نیست.
352
- *
353
- * دقیقاً همون الگوی امن‌شده‌ی NgxMessageService: با Map<id, ComponentRef> و
354
- * detachView قبل از destroy، تا اون نشتی حافظه‌ای که قبلاً توی message
355
- * service پیدا و رفع شد، اینجا از اول تکرار نشه.
356
- */
357
- class NgxImageViewerService {
358
- appRef = inject(ApplicationRef);
359
- envInjector = inject(EnvironmentInjector);
360
- nextId = 0;
361
- openInstances = new Map();
362
- open(images, options = {}) {
363
- const componentRef = createComponent(NgxImageViewerComponent, {
364
- environmentInjector: this.envInjector,
365
- });
366
- componentRef.setInput('images', images);
367
- if (options.startIndex != null)
368
- componentRef.setInput('startIndex', options.startIndex);
369
- componentRef.setInput('toolbar', { close: true, ...options.toolbar });
370
- if (options.minZoom != null)
371
- componentRef.setInput('minZoom', options.minZoom);
372
- if (options.maxZoom != null)
373
- componentRef.setInput('maxZoom', options.maxZoom);
374
- if (options.zoomStep != null)
375
- componentRef.setInput('zoomStep', options.zoomStep);
376
- this.appRef.attachView(componentRef.hostView);
377
- const domElem = componentRef.hostView.rootNodes[0];
378
- const dialog = document.createElement('dialog');
379
- dialog.className = 'ngx-image-viewer-dialog';
380
- dialog.appendChild(domElem);
381
- document.body.appendChild(dialog);
382
- dialog.showModal();
383
- domElem.focus?.();
384
- const id = this.nextId++;
385
- this.openInstances.set(id, componentRef);
386
- const close = () => {
387
- const ref = this.openInstances.get(id);
388
- if (!ref)
389
- return;
390
- try {
391
- if (dialog.open)
392
- dialog.close();
393
- }
394
- catch {
395
- /* noop */
396
- }
397
- this.appRef.detachView(ref.hostView);
398
- ref.destroy();
399
- dialog.remove();
400
- this.openInstances.delete(id);
401
- };
402
- componentRef.instance.closed.subscribe(close);
403
- dialog.addEventListener('cancel', close, { once: true });
404
- return { close };
405
- }
406
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: NgxImageViewerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
407
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: NgxImageViewerService, providedIn: 'root' });
408
- }
409
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: NgxImageViewerService, decorators: [{
410
- type: Injectable,
411
- args: [{ providedIn: 'root' }]
412
- }] });
413
-
414
- /**
415
- * Generated bundle index. Do not edit.
416
- */
417
-
418
- export { NGX_IMAGE_VIEWER_DEFAULT_TOOLBAR, NgxImageViewerComponent, NgxImageViewerService, clamp, distanceBetweenTouches, downloadImage, midpointOfTouches, printImage, zoomAroundPoint };
419
- //# sourceMappingURL=ngx-kit-gallery.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ngx-kit-gallery.mjs","sources":["../../../projects/libs/gallery/contracts/image-viewer-types.ts","../../../projects/libs/gallery/utils/download-print.ts","../../../projects/libs/gallery/utils/zoom-math.ts","../../../projects/libs/gallery/components/image-viewer.component.ts","../../../projects/libs/gallery/components/image-viewer.component.html","../../../projects/libs/gallery/services/image-viewer.service.ts","../../../projects/libs/gallery/ngx-kit-gallery.ts"],"sourcesContent":["export interface NgxImageViewerItem {\n src: string;\n alt?: string;\n caption?: string;\n /** نام فایل هنگام دانلود؛ نبود یعنی از روی src حدس زده می‌شه */\n downloadFileName?: string;\n}\n\n/**\n * هر دکمه‌ی نوار ابزار جدا فعال/غیرفعال می‌شه — دقیقاً طبق خواسته: «این\n * دکمه‌های کانفیگی باشه که هر کدوم خواست فعال کنه».\n */\nexport interface NgxImageViewerToolbarConfig {\n prevNext?: boolean;\n counter?: boolean;\n zoomIn?: boolean;\n zoomOut?: boolean;\n resetZoom?: boolean;\n rotateLeft?: boolean;\n rotateRight?: boolean;\n download?: boolean;\n print?: boolean;\n fullscreen?: boolean;\n close?: boolean;\n}\n\nexport const NGX_IMAGE_VIEWER_DEFAULT_TOOLBAR: Required<NgxImageViewerToolbarConfig> = {\n prevNext: true,\n counter: true,\n zoomIn: true,\n zoomOut: true,\n resetZoom: true,\n rotateLeft: true,\n rotateRight: true,\n download: true,\n print: true,\n fullscreen: true,\n close: false,\n};\n","/** دانلود تصویر با fetch→blob (نام‌گذاریِ درست فایل رو تضمین می‌کنه)؛ اگه CORS اجازه نداد، در تب جدید باز می‌شه */\nexport async function downloadImage(src: string, filename?: string): Promise<void> {\n try {\n const response = await fetch(src, { mode: 'cors' });\n if (!response.ok) throw new Error('fetch failed');\n const blob = await response.blob();\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = filename || guessFileName(src);\n document.body.appendChild(a);\n a.click();\n a.remove();\n URL.revokeObjectURL(url);\n } catch {\n window.open(src, '_blank', 'noopener,noreferrer');\n }\n}\n\nfunction guessFileName(src: string): string {\n try {\n const url = new URL(src, typeof window !== 'undefined' ? window.location.href : undefined);\n const last = url.pathname.split('/').pop();\n return last || 'image';\n } catch {\n return 'image';\n }\n}\n\n/** یه پنجره‌ی جدید فقط با همون تصویر باز می‌کنه و بعد از لود، print می‌زنه؛ با DOM API (نه innerHTML) پس در برابر src مخرب امنه */\nexport function printImage(src: string): void {\n const win = window.open('', '_blank', 'noopener,noreferrer,width=800,height=600');\n if (!win) return;\n\n win.document.title = 'Print';\n const style = win.document.createElement('style');\n style.textContent =\n 'body{margin:0;display:flex;align-items:center;justify-content:center;min-height:100vh;background:#fff;}img{max-width:100%;max-height:100vh;}';\n win.document.head.appendChild(style);\n\n const img = win.document.createElement('img');\n img.onload = () => {\n win.focus();\n win.print();\n };\n img.src = src;\n win.document.body.appendChild(img);\n}\n","export interface PanZoomState {\n zoom: number;\n panX: number;\n panY: number;\n}\n\n/**\n * با تغییر zoom، pan رو طوری تنظیم می‌کنه که نقطه‌ی زیر cursor/انگشت‌ها\n * (نسبت به مرکز کانتینر) سرجاش بمونه — یعنی zoom-to-cursor به‌جای\n * zoom-to-center (که تجربه‌ی خیلی بهتری داره، دقیقاً مثل هر ویوئر حرفه‌ای).\n *\n * ریاضیِ پشتش: با transform=\"translate(panX,panY) scale(zoom)\"، موقعیتِ\n * صفحه‌ایِ یه نقطه‌ی محلیِ p (نسبت به مرکز) برابره با\n * center + pan + p*zoom. اول p رو از روی وضعیت فعلی حساب می‌کنیم، بعد pan\n * جدید رو طوری می‌سازیم که با zoom تازه، همون p به همون مختصات صفحه برسه.\n */\nexport function zoomAroundPoint(\n current: PanZoomState,\n newZoom: number,\n cursorX: number,\n cursorY: number,\n containerWidth: number,\n containerHeight: number,\n): PanZoomState {\n const centerX = containerWidth / 2;\n const centerY = containerHeight / 2;\n const localX = (cursorX - centerX - current.panX) / current.zoom;\n const localY = (cursorY - centerY - current.panY) / current.zoom;\n return {\n zoom: newZoom,\n panX: cursorX - centerX - localX * newZoom,\n panY: cursorY - centerY - localY * newZoom,\n };\n}\n\nexport function distanceBetweenTouches(a: Touch, b: Touch): number {\n return Math.hypot(a.clientX - b.clientX, a.clientY - b.clientY);\n}\n\nexport function midpointOfTouches(a: Touch, b: Touch): { x: number; y: number } {\n return { x: (a.clientX + b.clientX) / 2, y: (a.clientY + b.clientY) / 2 };\n}\n\nexport function clamp(value: number, min: number, max: number): number {\n return Math.min(max, Math.max(min, value));\n}\n","import {\n ChangeDetectionStrategy,\n Component,\n DestroyRef,\n ElementRef,\n HostListener,\n computed,\n effect,\n inject,\n input,\n output,\n signal,\n viewChild,\n} from '@angular/core';\nimport { startDragSession } from 'ngx-kit/core';\nimport {\n NGX_IMAGE_VIEWER_DEFAULT_TOOLBAR,\n NgxImageViewerItem,\n NgxImageViewerToolbarConfig,\n} from '../contracts/image-viewer-types';\nimport { downloadImage, printImage } from '../utils/download-print';\nimport {\n clamp,\n distanceBetweenTouches,\n midpointOfTouches,\n zoomAroundPoint,\n} from '../utils/zoom-math';\n\n/**\n * ویوئر حرفه‌ایِ تصویر: یه لیست تصویر می‌گیره، اولی رو نشون می‌ده، با\n * دکمه‌های چپ/راست (مثل کاروسل) بین‌شون می‌ره، و یه نوار ابزار کاملاً\n * قابل‌تنظیم (هر دکمه جدا فعال/غیرفعال می‌شه) داره: زوم این/اوت/ریست،\n * چرخش، دانلود، پرینت، تمام‌صفحه.\n *\n * تعامل‌ها:\n * - اسکرول موس روی تصویر = زوم (نسبت به همون نقطه‌ی زیر cursor، نه مرکز)\n * - دو انگشتی روی تاچ = پینچ زوم\n * - وقتی زوم‌شده، درگ با موس یا تک‌انگشتی = پن\n * - کیبورد: ← → برای قبلی/بعدی، +/- برای زوم، Esc برای closed\n *\n * چون wheel/touchmove باید preventDefault بشن (وگرنه صفحه اسکرول/زوم\n * می‌شه) و انگیولار این ایونت‌ها رو به‌صورت پیش‌فرض passive رجیستر می‌کنه\n * (که توش preventDefault اثر نداره)، این‌ها با addEventListener دستی و\n * {passive:false} وصل می‌شن، نه با بایندینگ تمپلیت.\n *\n * می‌تونه هم مستقیم توی صفحه جا بگیره، هم توسط مصرف‌کننده داخل\n * ngx-kit/dialog (یا هر overlay دیگه) قرار بگیره — خودش به هیچ‌کدوم وابسته\n * نیست، فقط با closed خروجی به بیرون خبر می‌ده که «ببندم».\n */\n@Component({\n selector: 'ngx-image-viewer',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n templateUrl: './image-viewer.component.html',\n styleUrl: './image-viewer.component.scss',\n host: { class: 'ngx-image-viewer-host', tabindex: '0' },\n})\nexport class NgxImageViewerComponent {\n images = input.required<NgxImageViewerItem[]>();\n startIndex = input<number>(0);\n toolbar = input<NgxImageViewerToolbarConfig>({});\n minZoom = input<number>(1);\n maxZoom = input<number>(6);\n zoomStep = input<number>(0.4);\n\n indexChange = output<number>();\n closed = output<void>();\n\n protected readonly activeIndex = signal(0);\n protected readonly zoom = signal(1);\n protected readonly panX = signal(0);\n protected readonly panY = signal(0);\n protected readonly rotation = signal(0);\n\n protected readonly activeImage = computed<NgxImageViewerItem | undefined>(\n () => this.images()[this.activeIndex()],\n );\n\n protected readonly toolbarConfig = computed(() => ({\n ...NGX_IMAGE_VIEWER_DEFAULT_TOOLBAR,\n ...this.toolbar(),\n }));\n\n protected readonly isZoomed = computed(() => this.zoom() > 1);\n\n protected readonly stageTransform = computed(\n () =>\n `translate(${this.panX()}px, ${this.panY()}px) scale(${this.zoom()}) rotate(${this.rotation()}deg)`,\n );\n\n private readonly stageEl = viewChild<ElementRef<HTMLElement>>('stage');\n private readonly hostElRef = inject(ElementRef<HTMLElement>);\n private readonly destroyRef = inject(DestroyRef);\n\n private pinchStartDistance = 0;\n private pinchStartZoom = 1;\n private stopPanDrag?: () => void;\n\n constructor() {\n this.activeIndex.set(this.startIndex());\n\n // wheel/touch با addEventListener دستی + passive:false وصل می‌شن (دلیلش\n // بالای کلاس توضیح داده شده)؛ effect به‌صورت خودکار وقتی stageEl از\n // undefined به یه ElementRef واقعی تغییر کنه (یعنی بعد از اولین رندر)\n // دوباره اجرا می‌شه، پس نیازی به ngAfterViewInit نیست.\n effect((onCleanup) => {\n const stage = this.stageEl()?.nativeElement;\n if (!stage) return;\n\n const onWheel = (ev: WheelEvent) => {\n ev.preventDefault();\n const rect = stage.getBoundingClientRect();\n this.applyZoomAt(\n this.zoom() + (ev.deltaY < 0 ? 1 : -1) * this.zoomStep(),\n ev.clientX - rect.left,\n ev.clientY - rect.top,\n rect.width,\n rect.height,\n );\n };\n\n const onTouchStart = (ev: TouchEvent) => {\n if (ev.touches.length === 2) {\n this.stopPanDrag?.();\n this.pinchStartDistance = distanceBetweenTouches(ev.touches[0], ev.touches[1]);\n this.pinchStartZoom = this.zoom();\n } else if (ev.touches.length === 1 && this.isZoomed()) {\n this.beginPanDrag(ev);\n }\n };\n\n const onTouchMove = (ev: TouchEvent) => {\n if (ev.touches.length === 2 && this.pinchStartDistance > 0) {\n ev.preventDefault();\n const dist = distanceBetweenTouches(ev.touches[0], ev.touches[1]);\n const newZoom = (dist / this.pinchStartDistance) * this.pinchStartZoom;\n const rect = stage.getBoundingClientRect();\n const mid = midpointOfTouches(ev.touches[0], ev.touches[1]);\n this.applyZoomAt(newZoom, mid.x - rect.left, mid.y - rect.top, rect.width, rect.height);\n }\n };\n\n const onTouchEnd = () => {\n this.pinchStartDistance = 0;\n };\n\n stage.addEventListener('wheel', onWheel, { passive: false });\n stage.addEventListener('touchstart', onTouchStart, { passive: false });\n stage.addEventListener('touchmove', onTouchMove, { passive: false });\n stage.addEventListener('touchend', onTouchEnd);\n stage.addEventListener('touchcancel', onTouchEnd);\n\n onCleanup(() => {\n stage.removeEventListener('wheel', onWheel);\n stage.removeEventListener('touchstart', onTouchStart);\n stage.removeEventListener('touchmove', onTouchMove);\n stage.removeEventListener('touchend', onTouchEnd);\n stage.removeEventListener('touchcancel', onTouchEnd);\n });\n });\n\n this.destroyRef.onDestroy(() => this.stopPanDrag?.());\n }\n\n @HostListener('keydown', ['$event'])\n protected onKeydown(ev: KeyboardEvent): void {\n if (ev.key === 'ArrowLeft') this.prev();\n else if (ev.key === 'ArrowRight') this.next();\n else if (ev.key === '+' || ev.key === '=') this.zoomIn();\n else if (ev.key === '-') this.zoomOut();\n else if (ev.key === 'Escape') this.closed.emit();\n else return;\n ev.preventDefault();\n }\n\n protected next(): void {\n const len = this.images().length;\n if (len) this.goTo((this.activeIndex() + 1) % len);\n }\n\n protected prev(): void {\n const len = this.images().length;\n if (len) this.goTo((this.activeIndex() - 1 + len) % len);\n }\n\n protected goTo(index: number): void {\n this.activeIndex.set(index);\n this.resetView();\n this.indexChange.emit(index);\n }\n\n protected zoomIn(): void {\n this.setZoomAtCenter(this.zoom() + this.zoomStep());\n }\n\n protected zoomOut(): void {\n this.setZoomAtCenter(this.zoom() - this.zoomStep());\n }\n\n protected resetView(): void {\n this.zoom.set(1);\n this.panX.set(0);\n this.panY.set(0);\n this.rotation.set(0);\n }\n\n protected rotateLeft(): void {\n this.rotation.update((r) => r - 90);\n }\n\n protected rotateRight(): void {\n this.rotation.update((r) => r + 90);\n }\n\n protected async download(): Promise<void> {\n const img = this.activeImage();\n if (img) await downloadImage(img.src, img.downloadFileName);\n }\n\n protected print(): void {\n const img = this.activeImage();\n if (img) printImage(img.src);\n }\n\n protected toggleFullscreen(): void {\n const el = this.hostElRef.nativeElement;\n if (document.fullscreenElement) {\n document.exitFullscreen();\n } else {\n el.requestFullscreen?.();\n }\n }\n\n protected close(): void {\n this.closed.emit();\n }\n\n protected onPanPointerDown(ev: MouseEvent): void {\n if (!this.isZoomed()) return;\n this.beginPanDrag(ev);\n }\n\n private beginPanDrag(ev: MouseEvent | TouchEvent): void {\n ev.preventDefault();\n const startPanX = this.panX();\n const startPanY = this.panY();\n const startX = this.clientXOf(ev);\n const startY = this.clientYOf(ev);\n this.stopPanDrag?.();\n this.stopPanDrag = startDragSession({\n onMove: (moveEv) => {\n this.panX.set(startPanX + (this.clientXOf(moveEv) - startX));\n this.panY.set(startPanY + (this.clientYOf(moveEv) - startY));\n },\n onEnd: () => {\n this.stopPanDrag = undefined;\n },\n });\n }\n\n private setZoomAtCenter(newZoom: number): void {\n const stage = this.stageEl()?.nativeElement;\n if (!stage) {\n this.zoom.set(clamp(newZoom, this.minZoom(), this.maxZoom()));\n return;\n }\n const rect = stage.getBoundingClientRect();\n this.applyZoomAt(newZoom, rect.width / 2, rect.height / 2, rect.width, rect.height);\n }\n\n private applyZoomAt(\n rawZoom: number,\n x: number,\n y: number,\n containerWidth: number,\n containerHeight: number,\n ): void {\n const newZoom = clamp(rawZoom, this.minZoom(), this.maxZoom());\n if (newZoom === this.zoom()) return;\n const result = zoomAroundPoint(\n { zoom: this.zoom(), panX: this.panX(), panY: this.panY() },\n newZoom,\n x,\n y,\n containerWidth,\n containerHeight,\n );\n this.zoom.set(result.zoom);\n this.panX.set(result.panX);\n this.panY.set(result.panY);\n }\n\n private clientXOf(ev: MouseEvent | TouchEvent): number {\n return 'touches' in ev\n ? (ev.touches[0]?.clientX ?? ev.changedTouches[0]?.clientX ?? 0)\n : ev.clientX;\n }\n\n private clientYOf(ev: MouseEvent | TouchEvent): number {\n return 'touches' in ev\n ? (ev.touches[0]?.clientY ?? ev.changedTouches[0]?.clientY ?? 0)\n : ev.clientY;\n }\n}\n","<div class=\"ngx-iv-stage\" #stage [class.zoomed]=\"isZoomed()\">\n @if (toolbarConfig().close) {\n <button type=\"button\" class=\"ngx-iv-close\" (click)=\"close()\" aria-label=\"Close\">✕</button>\n }\n\n @if (activeImage(); as img) {\n <img\n class=\"ngx-iv-image\"\n [class.grab]=\"isZoomed()\"\n [src]=\"img.src\"\n [alt]=\"img.alt || ''\"\n draggable=\"false\"\n [style.transform]=\"stageTransform()\"\n (mousedown)=\"onPanPointerDown($event)\" />\n }\n\n @if (toolbarConfig().prevNext && images().length > 1) {\n <button\n type=\"button\"\n class=\"ngx-iv-nav ngx-iv-nav--prev\"\n (click)=\"prev()\"\n aria-label=\"Previous\">\n ‹\n </button>\n <button type=\"button\" class=\"ngx-iv-nav ngx-iv-nav--next\" (click)=\"next()\" aria-label=\"Next\">\n ›\n </button>\n }\n\n @if (toolbarConfig().counter && images().length > 1) {\n <div class=\"ngx-iv-counter\">{{ activeIndex() + 1 }} / {{ images().length }}</div>\n }\n</div>\n\n@if (activeImage()?.caption) {\n <div class=\"ngx-iv-caption\">{{ activeImage()?.caption }}</div>\n}\n\n<div class=\"ngx-iv-toolbar\">\n @if (toolbarConfig().zoomOut) {\n <button type=\"button\" (click)=\"zoomOut()\" title=\"Zoom out\">-</button>\n }\n @if (toolbarConfig().zoomIn) {\n <button type=\"button\" (click)=\"zoomIn()\" title=\"Zoom in\">+</button>\n }\n @if (toolbarConfig().resetZoom) {\n <button type=\"button\" (click)=\"resetView()\" title=\"Reset zoom\">⤢</button>\n }\n @if (toolbarConfig().rotateLeft) {\n <button type=\"button\" (click)=\"rotateLeft()\" title=\"Rotate left\">⟲</button>\n }\n @if (toolbarConfig().rotateRight) {\n <button type=\"button\" (click)=\"rotateRight()\" title=\"Rotate right\">⟳</button>\n }\n @if (toolbarConfig().download) {\n <button type=\"button\" (click)=\"download()\" title=\"Download\">⭳</button>\n }\n @if (toolbarConfig().print) {\n <button type=\"button\" (click)=\"print()\" title=\"Print\">🖶</button>\n }\n @if (toolbarConfig().fullscreen) {\n <button type=\"button\" (click)=\"toggleFullscreen()\" title=\"Fullscreen\">⛶</button>\n }\n</div>\n","import {\n ApplicationRef,\n ComponentRef,\n createComponent,\n EmbeddedViewRef,\n EnvironmentInjector,\n Injectable,\n inject,\n} from '@angular/core';\nimport { NgxImageViewerComponent } from '../components/image-viewer.component';\nimport { NgxImageViewerItem, NgxImageViewerToolbarConfig } from '../contracts/image-viewer-types';\n\nexport interface NgxImageViewerOpenOptions {\n startIndex?: number;\n toolbar?: NgxImageViewerToolbarConfig;\n minZoom?: number;\n maxZoom?: number;\n zoomStep?: number;\n}\n\n/**\n * راه میان‌بر برای وقتی که کاربر می‌خواد ویوئر رو داخل یه دیالوگِ تمام‌صفحه\n * باز کنه، بدون این‌که خودش دستی wire کنه. اگه کسی می‌خواد داخل دیالوگِ\n * خودش (مثلاً ngx-kit/dialog) بذارتش، کافیه مستقیم از NgxImageViewerComponent\n * استفاده کنه — این سرویس صرفاً یه میان‌بَره، وابستگی اجباری نیست.\n *\n * دقیقاً همون الگوی امن‌شده‌ی NgxMessageService: با Map<id, ComponentRef> و\n * detachView قبل از destroy، تا اون نشتی حافظه‌ای که قبلاً توی message\n * service پیدا و رفع شد، اینجا از اول تکرار نشه.\n */\n@Injectable({ providedIn: 'root' })\nexport class NgxImageViewerService {\n private readonly appRef = inject(ApplicationRef);\n private readonly envInjector = inject(EnvironmentInjector);\n private nextId = 0;\n private readonly openInstances = new Map<number, ComponentRef<NgxImageViewerComponent>>();\n\n open(\n images: NgxImageViewerItem[],\n options: NgxImageViewerOpenOptions = {},\n ): { close: () => void } {\n const componentRef = createComponent(NgxImageViewerComponent, {\n environmentInjector: this.envInjector,\n });\n\n componentRef.setInput('images', images);\n if (options.startIndex != null) componentRef.setInput('startIndex', options.startIndex);\n componentRef.setInput('toolbar', { close: true, ...options.toolbar });\n if (options.minZoom != null) componentRef.setInput('minZoom', options.minZoom);\n if (options.maxZoom != null) componentRef.setInput('maxZoom', options.maxZoom);\n if (options.zoomStep != null) componentRef.setInput('zoomStep', options.zoomStep);\n\n this.appRef.attachView(componentRef.hostView);\n const domElem = (componentRef.hostView as EmbeddedViewRef<unknown>).rootNodes[0] as HTMLElement;\n\n const dialog = document.createElement('dialog');\n dialog.className = 'ngx-image-viewer-dialog';\n dialog.appendChild(domElem);\n document.body.appendChild(dialog);\n dialog.showModal();\n domElem.focus?.();\n\n const id = this.nextId++;\n this.openInstances.set(id, componentRef);\n\n const close = () => {\n const ref = this.openInstances.get(id);\n if (!ref) return;\n try {\n if (dialog.open) dialog.close();\n } catch {\n /* noop */\n }\n this.appRef.detachView(ref.hostView);\n ref.destroy();\n dialog.remove();\n this.openInstances.delete(id);\n };\n\n componentRef.instance.closed.subscribe(close);\n dialog.addEventListener('cancel', close, { once: true });\n\n return { close };\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}