// If you want to use Phoenix channels, run `mix help phx.gen.channel` // to get started and then uncomment the line below. // import "./user_socket.js" // You can include dependencies in two ways. // // The simplest option is to put them in assets/vendor and // import them using relative paths: // // import "../vendor/some-package.js" // // Alternatively, you can `npm install some-package --prefix assets` and import // them using a path starting with the package name: // // import "some-package" // // If you have dependencies that try to import CSS, esbuild will generate a separate `app.css` file. // To load it, simply add a second `` to your `root.html.heex` file. // Include phoenix_html to handle method=PUT/DELETE in forms and buttons. import "phoenix_html" // Establish Phoenix Socket and LiveView configuration. import {Socket} from "phoenix" import {LiveSocket} from "phoenix_live_view" import {hooks as colocatedHooks} from "phoenix-colocated/simpleshop_theme" import topbar from "../vendor/topbar" // Hook to sync color picker and text input const ColorSync = { mounted() { const picker = this.el.querySelector('input[type="color"]') const text = this.el.querySelector('input[type="text"]') if (picker && text) { picker.addEventListener('input', (e) => { text.value = e.target.value }) text.addEventListener('input', (e) => { picker.value = e.target.value }) } } } // Hook for PDP image lightbox const Lightbox = { mounted() { const dialog = this.el const lightboxImage = dialog.querySelector('#lightbox-image') const lightboxCounter = dialog.querySelector('#lightbox-counter') // Get images from data attribute const getImages = () => { try { return JSON.parse(dialog.dataset.images || '[]') } catch { return [] } } const getCurrentIndex = () => parseInt(dialog.dataset.currentIndex || '0', 10) const setCurrentIndex = (idx) => { dialog.dataset.currentIndex = idx.toString() } const updateImage = () => { const images = getImages() const idx = getCurrentIndex() if (images.length > 0 && lightboxImage) { lightboxImage.src = images[idx] if (lightboxCounter) { lightboxCounter.textContent = `${idx + 1} / ${images.length}` } } } const nextImage = () => { const images = getImages() const newIdx = (getCurrentIndex() + 1) % images.length setCurrentIndex(newIdx) updateImage() } const prevImage = () => { const images = getImages() const newIdx = (getCurrentIndex() - 1 + images.length) % images.length setCurrentIndex(newIdx) updateImage() } const openLightbox = () => { updateImage() dialog.showModal() } const closeLightbox = () => { dialog.close() } // Event listeners for custom events dispatched from LiveView.JS dialog.addEventListener('pdp:open-lightbox', openLightbox) dialog.addEventListener('pdp:close-lightbox', closeLightbox) dialog.addEventListener('pdp:next-image', nextImage) dialog.addEventListener('pdp:prev-image', prevImage) // Close on clicking backdrop dialog.addEventListener('click', (e) => { if (e.target === dialog) { closeLightbox() } }) // Keyboard navigation dialog.addEventListener('keydown', (e) => { if (e.key === 'ArrowRight') { nextImage() } else if (e.key === 'ArrowLeft') { prevImage() } else if (e.key === 'Escape') { closeLightbox() } }) // Store cleanup function this.cleanup = () => { dialog.removeEventListener('pdp:open-lightbox', openLightbox) dialog.removeEventListener('pdp:close-lightbox', closeLightbox) dialog.removeEventListener('pdp:next-image', nextImage) dialog.removeEventListener('pdp:prev-image', prevImage) } }, destroyed() { if (this.cleanup) { this.cleanup() } } } const csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content") const liveSocket = new LiveSocket("/live", Socket, { longPollFallbackMs: 2500, params: {_csrf_token: csrfToken}, hooks: {...colocatedHooks, ColorSync, Lightbox}, }) // Show progress bar on live navigation and form submits topbar.config({barColors: {0: "#29d"}, shadowColor: "rgba(0, 0, 0, .3)"}) window.addEventListener("phx:page-loading-start", _info => topbar.show(300)) window.addEventListener("phx:page-loading-stop", _info => topbar.hide()) // Scroll preview frame to top when changing pages window.addEventListener("phx:scroll-preview-top", (e) => { const previewFrame = document.querySelector('.preview-frame') if (previewFrame) { previewFrame.scrollTop = 0 } }) // connect if there are any LiveViews on the page liveSocket.connect() // expose liveSocket on window for web console debug logs and latency simulation: // >> liveSocket.enableDebug() // >> liveSocket.enableLatencySim(1000) // enabled for duration of browser session // >> liveSocket.disableLatencySim() window.liveSocket = liveSocket // The lines below enable quality of life phoenix_live_reload // development features: // // 1. stream server logs to the browser console // 2. click on elements to jump to their definitions in your code editor // if (process.env.NODE_ENV === "development") { window.addEventListener("phx:live_reload:attached", ({detail: reloader}) => { // Enable server log streaming to client. // Disable with reloader.disableServerLogs() reloader.enableServerLogs() // Open configured PLUG_EDITOR at file:line of the clicked element's HEEx component // // * click with "c" key pressed to open at caller location // * click with "d" key pressed to open at function component definition location let keyDown window.addEventListener("keydown", e => keyDown = e.key) window.addEventListener("keyup", e => keyDown = null) window.addEventListener("click", e => { if(keyDown === "c"){ e.preventDefault() e.stopImmediatePropagation() reloader.openEditorAtCaller(e.target) } else if(keyDown === "d"){ e.preventDefault() e.stopImmediatePropagation() reloader.openEditorAtDef(e.target) } }, true) window.liveReloader = reloader }) }