{"version":3,"file":"swatch-journey.js","names":[],"sources":["../resources/javascript/swatch-journey.js"],"sourcesContent":["import Dialog from 'dialog';\nimport { removeItem } from './swatch/local-storage';\n\nconst MAX_SWATCHES = 6;\nconst STORAGE_KEY = 'swatches';\nconst BADGE_TICK =\n    '<svg viewBox=\"0 0 15 12\" width=\"12\" height=\"10\"><path d=\"M14.067.933a1 1 0 0 1 0 1.414l-8.13 ' +\n    '8.13a1 1 0 0 1-1.414 0L.933 6.887a1 1 0 1 1 1.414-1.414L5.523 8.65 ' +\n    '12.653.933a1 1 0 0 1 1.414 0Z\" fill=\"currentColor\"/></svg>';\n\n// DOM references\nconst basketSlots = document.querySelector('[data-swatch-slots]');\nconst basketCount = document.querySelector('[data-swatch-count]');\nconst basketForm = document.querySelector('[data-basket-form]');\nconst basketInput = document.querySelector('[data-swatch-panel-input]');\nconst continueBtn = document.querySelector('.swatch-journey-basket__continue');\n\nlet currentColourCode = null;\nlet selectedMaterials = new Set();\n\n// ── localStorage helpers ──\n\nconst getSwatches = () => {\n    try {\n        return JSON.parse(localStorage.getItem(STORAGE_KEY)) ?? [];\n    } catch {\n        return [];\n    }\n};\n\nconst saveSwatches = swatches => {\n    localStorage.setItem(STORAGE_KEY, JSON.stringify(swatches));\n};\n\nconst getSwatchColour = swatch => {\n    try {\n        return JSON.parse(swatch.value).colour;\n    } catch {\n        return null;\n    }\n};\n\nconst isSwatchInBasket = urlname => {\n    return getSwatches().some(s => getSwatchColour(s) === urlname);\n};\n\nconst removeSwatchByUrlname = urlname => {\n    const swatches = getSwatches().filter(s => getSwatchColour(s) !== urlname);\n    saveSwatches(swatches);\n};\n\nconst isComboInBasket = (colourCode, materialCode) => {\n    return getSwatches().some(\n        s => s.colourCode === colourCode && s.materialCode === materialCode\n    );\n};\n\nconst getRemainingSlots = () => {\n    return MAX_SWATCHES - getSwatches().length;\n};\n\n// ── Colour button badge helpers ──\n\nconst getCountsPerColour = () => {\n    const counts = {};\n    for (const swatch of getSwatches()) {\n        if (swatch.colourCode) {\n            counts[swatch.colourCode] = (counts[swatch.colourCode] || 0) + 1;\n        }\n    }\n    return counts;\n};\n\nconst updateColourBadges = () => {\n    const counts = getCountsPerColour();\n    document.querySelectorAll('.swatch-journey__colour').forEach(btn => {\n        const code = btn.dataset.colourCode;\n        const badge = btn.querySelector('[data-colour-badge]');\n        if (!badge) return;\n\n        // Don't touch the active colour (overlay open) — it shows a tick\n        if (btn.classList.contains('swatch-journey__colour--active')) return;\n\n        const count = counts[code] || 0;\n        if (count > 0) {\n            btn.classList.add('swatch-journey__colour--has-swatches');\n            badge.innerHTML = count === 1 ? BADGE_TICK : 'x' + count;\n        } else {\n            btn.classList.remove('swatch-journey__colour--has-swatches');\n            badge.innerHTML = '';\n        }\n    });\n};\n\n// ── Basket rendering ──\n\nconst renderBasket = () => {\n    let swatches = getSwatches();\n\n    for (const swatch of swatches) {\n        if ('imageHtml' in swatch === false) {\n            removeItem('swatches', swatch.value);\n        }\n    }\n\n    swatches = getSwatches();\n\n    const count = swatches.length;\n\n    basketCount.textContent = `Your free swatches (${count}/${MAX_SWATCHES})`;\n    continueBtn.disabled = count === 0;\n\n    const urlnames = swatches.map(s => getSwatchColour(s)).filter(Boolean);\n    basketInput.value = urlnames.join(',');\n\n    basketSlots.innerHTML = '';\n    for (let i = 0; i < MAX_SWATCHES; i++) {\n        const slot = document.createElement('div');\n        slot.className = 'swatch-journey-basket__slot';\n\n        if (swatches[i]) {\n            slot.style.cssText = swatches[i].backgroundCSS || '';\n            slot.classList.add('swatch-journey-basket__slot--filled');\n            slot.title = swatches[i].title || '';\n\n            if (swatches[i].imageHtml) {\n                slot.insertAdjacentHTML('afterbegin', swatches[i].imageHtml);\n                const img = slot.querySelector('img');\n                img?.classList.add('swatch-journey-basket__slot-image');\n            }\n\n            const urlname = getSwatchColour(swatches[i]);\n            slot.addEventListener('click', () => {\n                removeSwatchByUrlname(urlname);\n                renderBasket();\n            });\n        }\n\n        basketSlots.appendChild(slot);\n    }\n\n    updateColourBadges();\n};\n\n// ── Overlay helpers ──\n\nconst updateAddButton = () => {\n    const addBtn = document.querySelector('[data-overlay-add]');\n    if (!addBtn) return;\n    const count = selectedMaterials.size;\n    addBtn.textContent = `Add ${count} swatch${count !== 1 ? 'es' : ''}`;\n    addBtn.disabled = count === 0;\n};\n\n/**\n * Callback invoked after the D3R overlay opens with server-rendered\n * materials content. Binds material selection and basket logic.\n */\nconst initMaterialsOverlay = () => {\n    const root = document.querySelector('.swatch-journey-overlay');\n    if (!root) return;\n\n    const colourCode = root.dataset.colourCode;\n    currentColourCode = colourCode;\n    selectedMaterials.clear();\n\n    // Mark cards that are already in the basket\n    const cards = root.querySelectorAll('.swatch-journey__material-card');\n    for (const card of cards) {\n        const materialCode = card.dataset.materialCode;\n        const available = card.dataset.available === 'true';\n\n        if (available && isComboInBasket(colourCode, materialCode)) {\n            card.classList.add('swatch-journey__material-card--in-basket');\n            continue;\n        }\n\n        if (!available) continue;\n\n        // Bind selection toggle\n        const btn = card.querySelector('.swatch-journey__material-swatch');\n        btn?.addEventListener('click', () => {\n            if (\n                !card.classList.contains(\n                    'swatch-journey__material-card--selected'\n                )\n            ) {\n                card.classList.add('swatch-journey__material-card--selected');\n                selectedMaterials.add(materialCode);\n            } else {\n                card.classList.remove(\n                    'swatch-journey__material-card--selected'\n                );\n                selectedMaterials.delete(materialCode);\n            }\n            updateAddButton();\n        });\n    }\n\n    // Bind \"Add swatches\" button\n    const addBtn = root.querySelector('[data-overlay-add]');\n    addBtn?.addEventListener('click', addSelectedSwatches);\n};\n\nconst findColourButton = code =>\n    Array.from(document.querySelectorAll('.swatch-journey__colour')).find(\n        el => el.dataset.colourCode === code\n    ) ?? null;\n\nconst onOverlayClose = () => {\n    // Remove active indicator from colour button\n    if (currentColourCode) {\n        const activeBtn = findColourButton(currentColourCode);\n        if (activeBtn) {\n            activeBtn.classList.remove('swatch-journey__colour--active');\n        }\n    }\n\n    currentColourCode = null;\n    selectedMaterials.clear();\n    updateColourBadges();\n};\n\nconst openMaterialsOverlay = colourCode => {\n    // Show tick on the active colour button\n    const activeBtn = findColourButton(colourCode);\n    if (activeBtn) {\n        const badge = activeBtn.querySelector('[data-colour-badge]');\n        activeBtn.classList.remove('swatch-journey__colour--has-swatches');\n        activeBtn.classList.add('swatch-journey__colour--active');\n        if (badge) badge.innerHTML = BADGE_TICK;\n    }\n\n    Dialog.load(\n        '/swatches/materials-overlay?colour_global=' +\n            encodeURIComponent(colourCode),\n        {\n            modifiers: ['sidebar', 'overlay', 'swatch-journey'],\n            // Open immediately with a spinner, fill fabrics when the response lands.\n            loading: 'active',\n            loading_content: '<div class=\"lds-dual-ring\"></div>',\n            stack: true,\n            callback: initMaterialsOverlay,\n            close: onOverlayClose,\n        }\n    );\n};\n\n// ── Add swatches ──\n\nconst addSelectedSwatches = async () => {\n    const capturedColourCode = currentColourCode;\n    if (!capturedColourCode || selectedMaterials.size === 0) {\n        return;\n    }\n\n    const addBtn = document.querySelector('[data-overlay-add]');\n    if (addBtn) {\n        addBtn.disabled = true;\n        addBtn.textContent = 'Adding\\u2026';\n    }\n\n    const materialsToAdd = [...selectedMaterials];\n\n    const results = await Promise.allSettled(\n        materialsToAdd.map(async materialCode => {\n            const response = await fetch(\n                '/swatches/resolve-swatch?colour_global=' +\n                    encodeURIComponent(capturedColourCode) +\n                    '&material_global=' +\n                    encodeURIComponent(materialCode),\n                { headers: { 'X-Requested-With': 'XMLHttpRequest' } }\n            );\n            if (!response.ok) {\n                throw new Error('HTTP ' + response.status);\n            }\n            const data = await response.json();\n            return { materialCode, data };\n        })\n    );\n\n    const resolvedEntries = [];\n\n    for (const result of results) {\n        if (result.status !== 'fulfilled') continue;\n\n        const { materialCode, data } = result.value;\n        if (!data.result || !data.payload?.swatch) continue;\n\n        const swatch = data.payload.swatch;\n        if (isSwatchInBasket(swatch.urlname)) continue;\n\n        resolvedEntries.push({\n            value: JSON.stringify({ colour: swatch.urlname }),\n            title: swatch.title,\n            longTitle: swatch.long_title || swatch.title,\n            categoryName:\n                swatch.material_global_name ||\n                swatch.colour_category_name ||\n                '',\n            url: '/swatch/about?swatch=' + swatch.id,\n            backgroundCSS: swatch.hex_background_css || '',\n            categoryClass: swatch.colour_category_class || '',\n            hex: swatch.hex || '',\n            imageHtml: swatch.image_html || '',\n            prices: '',\n            leadTime: '',\n            colourCode: capturedColourCode,\n            materialCode: materialCode,\n        });\n    }\n\n    if (resolvedEntries.length === 0) {\n        if (addBtn) {\n            addBtn.disabled = false;\n            addBtn.textContent = 'Add 0 swatches';\n        }\n        return;\n    }\n\n    const added = [];\n    const needsReplace = [];\n\n    for (const entry of resolvedEntries) {\n        const current = getSwatches();\n        if (current.length < MAX_SWATCHES) {\n            current.push(entry);\n            saveSwatches(current);\n            added.push(entry);\n        } else {\n            needsReplace.push(entry);\n        }\n    }\n\n    if (needsReplace.length > 0) {\n        const { SwatchReplace } = await import('./swatch/SwatchReplace.js');\n        document.addEventListener(\n            'swatch-journey:replaced',\n            () => {\n                updateAddButton();\n                renderBasket();\n            },\n            { once: true }\n        );\n        new SwatchReplace(needsReplace, 'swatch-journey:replaced', () => {\n            Dialog.close(() => {\n                Dialog.close();\n            });\n        });\n    } else {\n        renderBasket();\n        Dialog.close();\n    }\n};\n\n// ── Event listeners ──\n\n// Only initialise if we're on the swatch journey page\nif (basketSlots && basketCount && basketForm && basketInput && continueBtn) {\n    // Colour swatch clicks\n    document.querySelectorAll('.swatch-journey__colour').forEach(btn => {\n        btn.addEventListener('click', () => {\n            openMaterialsOverlay(btn.dataset.colourCode);\n        });\n    });\n\n    // Basket form submit\n    basketForm.addEventListener('submit', e => {\n        if (getSwatches().length === 0) {\n            e.preventDefault();\n        }\n    });\n\n    // Initial render\n    renderBasket();\n}\n"],"mappings":"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"}