{"version":3,"file":"page_navigation_controller.js","sources":["../../../assets/controllers/customizer/page_navigation_controller.ts"],"sourcesContent":["import { Controller } from '@hotwired/stimulus';\n\n/**\n * PageNavigationController — Multi-page navigation for the Fabric.js customizer.\n *\n * Listens for `fabric-customizer:pageChanged` events dispatched by the\n * fabric-customizer controller and updates:\n *  - prev/next arrow buttons (disabled state + visibility)\n *  - page thumbnail strip with small canvas snapshots\n *  - active page indicator badge\n *\n * Dispatches page-change requests back to the fabric-customizer controller\n * via `page-navigation:requestPage` custom events.\n *\n * Targets:\n *  - prevBtn   : Previous page button\n *  - nextBtn   : Next page button\n *  - thumbnails: Container for page thumbnail buttons\n *  - pageLabel : Optional element showing \"Page X / Y\"\n */\n\ninterface PageInfo {\n    index: number;\n    pageKey: string;\n    thumbnailDataUrl: string | null;\n}\n\ntype StimulusApp = {\n    getControllerForElementAndIdentifier(el: HTMLElement, id: string): unknown;\n};\n\nexport default class extends Controller {\n    static targets = ['prevBtn', 'nextBtn', 'thumbnails', 'pageLabel'];\n\n    declare readonly prevBtnTarget: HTMLButtonElement;\n    declare readonly nextBtnTarget: HTMLButtonElement;\n    declare readonly thumbnailsTarget: HTMLElement;\n    declare readonly hasPageLabelTarget: boolean;\n    declare readonly pageLabelTarget: HTMLElement;\n\n    private totalPages = 0;\n    private currentPage = 0;\n    private pages: PageInfo[] = [];\n\n    // Bound event listeners (assigned in connect for correct `this` binding)\n    private _onPageChanged!: EventListener;\n    private _onRequestPage!: EventListener;\n\n    connect(): void {\n        this._onPageChanged = this.onPageChanged.bind(this);\n        this._onRequestPage = this.onRequestPage.bind(this);\n\n        document.addEventListener('fabric-customizer:pageChanged', this._onPageChanged);\n        document.addEventListener('page-navigation:requestPage', this._onRequestPage);\n    }\n\n    disconnect(): void {\n        document.removeEventListener('fabric-customizer:pageChanged', this._onPageChanged);\n        document.removeEventListener('page-navigation:requestPage', this._onRequestPage);\n    }\n\n    // =========================================================================\n    // Event handlers\n    // =========================================================================\n\n    private onPageChanged(event: Event): void {\n        const e = event as CustomEvent<{\n            currentPage: number;\n            totalPages: number;\n            pages: PageInfo[];\n            thumbnailDataUrl: string | null;\n        }>;\n\n        const { currentPage, totalPages, pages, thumbnailDataUrl } = e.detail;\n\n        this.totalPages = totalPages;\n        this.currentPage = currentPage;\n        this.pages = pages.map(p => ({ ...p }));\n\n        // Store the fresh thumbnail for the page that just became active\n        if (thumbnailDataUrl) {\n            const activePage = this.pages[currentPage];\n            if (activePage) {\n                activePage.thumbnailDataUrl = thumbnailDataUrl;\n            }\n        }\n\n        this.updateNavigationButtons();\n        this.renderThumbnails();\n        this.updatePageLabel();\n    }\n\n    private onRequestPage(event: Event): void {\n        const e = event as CustomEvent<{ index: number }>;\n\n        // Forward the page-selection request to the fabric-customizer controller\n        const customizerEl = document.querySelector(\n            '[data-controller*=\"customizer--fabric-customizer\"]',\n        ) as HTMLElement | null;\n\n        if (!customizerEl) return;\n\n        const app = (this as unknown as { application: StimulusApp }).application;\n        const controller = app?.getControllerForElementAndIdentifier(\n            customizerEl,\n            'customizer--fabric-customizer',\n        ) as { selectPage?(index: number): Promise<void> } | null;\n\n        if (controller?.selectPage) {\n            void controller.selectPage(e.detail.index);\n        }\n    }\n\n    // =========================================================================\n    // Action methods (triggered by data-action attributes in the template)\n    // =========================================================================\n\n    previous(): void {\n        if (this.currentPage <= 0) return;\n        this.requestPage(this.currentPage - 1);\n    }\n\n    next(): void {\n        if (this.currentPage >= this.totalPages - 1) return;\n        this.requestPage(this.currentPage + 1);\n    }\n\n    // =========================================================================\n    // Private helpers\n    // =========================================================================\n\n    private requestPage(index: number): void {\n        document.dispatchEvent(\n            new CustomEvent('page-navigation:requestPage', {\n                bubbles: true,\n                detail: { index },\n            }),\n        );\n    }\n\n    private updateNavigationButtons(): void {\n        this.prevBtnTarget.disabled = this.currentPage <= 0;\n        this.nextBtnTarget.disabled = this.currentPage >= this.totalPages - 1;\n\n        if (this.totalPages <= 1) {\n            this.prevBtnTarget.classList.add('invisible');\n            this.nextBtnTarget.classList.add('invisible');\n        } else {\n            this.prevBtnTarget.classList.remove('invisible');\n            this.nextBtnTarget.classList.remove('invisible');\n        }\n    }\n\n    private renderThumbnails(): void {\n        this.thumbnailsTarget.innerHTML = '';\n\n        this.pages.forEach((page, i) => {\n            const isActive = i === this.currentPage;\n\n            const btn = document.createElement('button');\n            btn.type = 'button';\n            btn.className = [\n                'flex flex-col items-center gap-0.5 p-1 rounded border text-xs transition-all',\n                isActive\n                    ? 'border-primary bg-primary/10 ring-1 ring-primary'\n                    : 'border-base-300 bg-base-200 hover:bg-base-300',\n            ].join(' ');\n            btn.title = page.pageKey;\n            btn.setAttribute('aria-label', `Page ${i + 1}: ${page.pageKey}`);\n            btn.setAttribute('aria-pressed', isActive ? 'true' : 'false');\n\n            if (page.thumbnailDataUrl) {\n                const img = document.createElement('img');\n                img.src = page.thumbnailDataUrl;\n                img.className = 'w-12 rounded';\n                img.alt = page.pageKey;\n                btn.appendChild(img);\n            } else {\n                const placeholder = document.createElement('div');\n                placeholder.className =\n                    'w-12 h-16 bg-base-300 rounded flex items-center justify-center text-base-content/40 text-xs';\n                placeholder.textContent = String(i + 1);\n                btn.appendChild(placeholder);\n            }\n\n            const label = document.createElement('span');\n            label.className = 'truncate max-w-[3rem] text-[10px]';\n            label.textContent = page.pageKey;\n            btn.appendChild(label);\n\n            btn.addEventListener('click', () => {\n                if (i !== this.currentPage) {\n                    this.requestPage(i);\n                }\n            });\n\n            this.thumbnailsTarget.appendChild(btn);\n        });\n    }\n\n    private updatePageLabel(): void {\n        if (!this.hasPageLabelTarget) return;\n        this.pageLabelTarget.textContent = `${this.currentPage + 1} / ${this.totalPages}`;\n    }\n}\n"],"names":[],"mappings":";AA+BA,MAAA,mCAA6B,WAAW;AAAA,EACpC,OAAO,UAAU,CAAC,WAAW,WAAW,cAAc,WAAW;AAAA,EAQzD,aAAa;AAAA,EACb,cAAc;AAAA,EACd,QAAoB,CAAA;AAAA;AAAA,EAGpB;AAAA,EACA;AAAA,EAER,UAAgB;AACZ,SAAK,iBAAiB,KAAK,cAAc,KAAK,IAAI;AAClD,SAAK,iBAAiB,KAAK,cAAc,KAAK,IAAI;AAElD,aAAS,iBAAiB,iCAAiC,KAAK,cAAc;AAC9E,aAAS,iBAAiB,+BAA+B,KAAK,cAAc;AAAA,EAChF;AAAA,EAEA,aAAmB;AACf,aAAS,oBAAoB,iCAAiC,KAAK,cAAc;AACjF,aAAS,oBAAoB,+BAA+B,KAAK,cAAc;AAAA,EACnF;AAAA;AAAA;AAAA;AAAA,EAMQ,cAAc,OAAoB;AACtC,UAAM,IAAI;AAOV,UAAM,EAAE,aAAa,YAAY,OAAO,iBAAA,IAAqB,EAAE;AAE/D,SAAK,aAAa;AAClB,SAAK,cAAc;AACnB,SAAK,QAAQ,MAAM,IAAI,QAAM,EAAE,GAAG,IAAI;AAGtC,QAAI,kBAAkB;AAClB,YAAM,aAAa,KAAK,MAAM,WAAW;AACzC,UAAI,YAAY;AACZ,mBAAW,mBAAmB;AAAA,MAClC;AAAA,IACJ;AAEA,SAAK,wBAAA;AACL,SAAK,iBAAA;AACL,SAAK,gBAAA;AAAA,EACT;AAAA,EAEQ,cAAc,OAAoB;AACtC,UAAM,IAAI;AAGV,UAAM,eAAe,SAAS;AAAA,MAC1B;AAAA,IAAA;AAGJ,QAAI,CAAC,aAAc;AAEnB,UAAM,MAAO,KAAiD;AAC9D,UAAM,aAAa,KAAK;AAAA,MACpB;AAAA,MACA;AAAA,IAAA;AAGJ,QAAI,YAAY,YAAY;AACxB,WAAK,WAAW,WAAW,EAAE,OAAO,KAAK;AAAA,IAC7C;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA,EAMA,WAAiB;AACb,QAAI,KAAK,eAAe,EAAG;AAC3B,SAAK,YAAY,KAAK,cAAc,CAAC;AAAA,EACzC;AAAA,EAEA,OAAa;AACT,QAAI,KAAK,eAAe,KAAK,aAAa,EAAG;AAC7C,SAAK,YAAY,KAAK,cAAc,CAAC;AAAA,EACzC;AAAA;AAAA;AAAA;AAAA,EAMQ,YAAY,OAAqB;AACrC,aAAS;AAAA,MACL,IAAI,YAAY,+BAA+B;AAAA,QAC3C,SAAS;AAAA,QACT,QAAQ,EAAE,MAAA;AAAA,MAAM,CACnB;AAAA,IAAA;AAAA,EAET;AAAA,EAEQ,0BAAgC;AACpC,SAAK,cAAc,WAAW,KAAK,eAAe;AAClD,SAAK,cAAc,WAAW,KAAK,eAAe,KAAK,aAAa;AAEpE,QAAI,KAAK,cAAc,GAAG;AACtB,WAAK,cAAc,UAAU,IAAI,WAAW;AAC5C,WAAK,cAAc,UAAU,IAAI,WAAW;AAAA,IAChD,OAAO;AACH,WAAK,cAAc,UAAU,OAAO,WAAW;AAC/C,WAAK,cAAc,UAAU,OAAO,WAAW;AAAA,IACnD;AAAA,EACJ;AAAA,EAEQ,mBAAyB;AAC7B,SAAK,iBAAiB,YAAY;AAElC,SAAK,MAAM,QAAQ,CAAC,MAAM,MAAM;AAC5B,YAAM,WAAW,MAAM,KAAK;AAE5B,YAAM,MAAM,SAAS,cAAc,QAAQ;AAC3C,UAAI,OAAO;AACX,UAAI,YAAY;AAAA,QACZ;AAAA,QACA,WACM,qDACA;AAAA,MAAA,EACR,KAAK,GAAG;AACV,UAAI,QAAQ,KAAK;AACjB,UAAI,aAAa,cAAc,QAAQ,IAAI,CAAC,KAAK,KAAK,OAAO,EAAE;AAC/D,UAAI,aAAa,gBAAgB,WAAW,SAAS,OAAO;AAE5D,UAAI,KAAK,kBAAkB;AACvB,cAAM,MAAM,SAAS,cAAc,KAAK;AACxC,YAAI,MAAM,KAAK;AACf,YAAI,YAAY;AAChB,YAAI,MAAM,KAAK;AACf,YAAI,YAAY,GAAG;AAAA,MACvB,OAAO;AACH,cAAM,cAAc,SAAS,cAAc,KAAK;AAChD,oBAAY,YACR;AACJ,oBAAY,cAAc,OAAO,IAAI,CAAC;AACtC,YAAI,YAAY,WAAW;AAAA,MAC/B;AAEA,YAAM,QAAQ,SAAS,cAAc,MAAM;AAC3C,YAAM,YAAY;AAClB,YAAM,cAAc,KAAK;AACzB,UAAI,YAAY,KAAK;AAErB,UAAI,iBAAiB,SAAS,MAAM;AAChC,YAAI,MAAM,KAAK,aAAa;AACxB,eAAK,YAAY,CAAC;AAAA,QACtB;AAAA,MACJ,CAAC;AAED,WAAK,iBAAiB,YAAY,GAAG;AAAA,IACzC,CAAC;AAAA,EACL;AAAA,EAEQ,kBAAwB;AAC5B,QAAI,CAAC,KAAK,mBAAoB;AAC9B,SAAK,gBAAgB,cAAc,GAAG,KAAK,cAAc,CAAC,MAAM,KAAK,UAAU;AAAA,EACnF;AACJ;"}