From d9b311ca2ab75ea8f297df7fef51bf106528420c Mon Sep 17 00:00:00 2001 From: Wout De Puysseleir Date: Mon, 13 Mar 2023 13:40:59 -0700 Subject: [PATCH] Put hook in js import --- assets/js/app.js | 5 +- assets/js/hooks.js | 109 ------------------------------------- lib/live_component.ex | 2 +- package-lock.json | 33 ++++++++++++ package.json | 3 ++ priv/live_svelte.esm.js | 115 +++++++++++++++++++++++++++++++++++++++- 6 files changed, 153 insertions(+), 114 deletions(-) delete mode 100644 assets/js/hooks.js create mode 100644 package-lock.json diff --git a/assets/js/app.js b/assets/js/app.js index 423b964..42b3193 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -21,10 +21,11 @@ import "phoenix_html" import {Socket} from "phoenix" import {LiveSocket} from "phoenix_live_view" import topbar from "../vendor/topbar" -import hooks from './hooks' +import {getHooks} from 'live_svelte' +import * as Components from '../svelte/components/**/*' let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content") -let liveSocket = new LiveSocket("/live", Socket, {hooks, params: {_csrf_token: csrfToken}}) +let liveSocket = new LiveSocket("/live", Socket, {hooks: getHooks(Components), params: {_csrf_token: csrfToken}}) // Show progress bar on live navigation and form submits topbar.config({barColors: {0: "#29d"}, shadowColor: "rgba(0, 0, 0, .3)"}) diff --git a/assets/js/hooks.js b/assets/js/hooks.js deleted file mode 100644 index 16a52d8..0000000 --- a/assets/js/hooks.js +++ /dev/null @@ -1,109 +0,0 @@ -import * as Components from '../svelte/components/**/*' -import {exportSvelteComponents} from 'live_svelte' -import {detach, insert, noop} from 'svelte/internal' - -const components = exportSvelteComponents(Components) - -function base64ToElement(base64) { - let template = document.createElement('div') - template.innerHTML = atob(base64).trim() - return template -} - -function dataAttributeToJson(attributeName, el) { - const data = el.getAttribute(attributeName) - return data ? JSON.parse(data) : {} -} - -function createSlots(slots, ref) { - const createSlot = (slotName, ref) => { - let savedTarget, savedAnchor, savedElement - return () => { - return { - getElement() { - return base64ToElement(dataAttributeToJson('data-slots', ref.el)[slotName]) - }, - update() { - const element = this.getElement() - detach(savedElement) - insert(savedTarget, element, savedAnchor) - savedElement = element - }, - c: noop, - m(target, anchor) { - const element = this.getElement() - savedTarget = target - savedAnchor = anchor - savedElement = element - insert(target, element, anchor) - }, - d(detaching) { - if (detaching) detach(savedElement) - }, - l: noop, - } - } - } - - const svelteSlots = {} - - for (const slotName in slots) { - svelteSlots[slotName] = [createSlot(slotName, ref)] - } - - return svelteSlots -} - -function getProps(ref) { - return { - ...dataAttributeToJson('data-props', ref.el), - pushEvent: (event, data, callback) => ref.pushEvent(event, data, callback), - $$slots: createSlots(dataAttributeToJson('data-slots', ref.el), ref), - $$scope: {} - } -} - -function findSlotCtx(component) { - // The default slot always exists if there's a slot set - // even if no slot is set for the explicit default slot - return component.$$.ctx.find(ctxElement => ctxElement.default) -} - -const SvelteComponent = { - mounted() { - const componentName = this.el.getAttribute('data-name') - if (!componentName) { - throw new Error('Component name must be provided') - } - - const Component = components[componentName] - if (!Component) { - throw new Error(`Unable to find ${componentName} component.`) - } - - this._instance = new Component({ - target: this.el, - props: getProps(this), - hydrate: true - }) - }, - - updated() { - // Set the props - this._instance.$set(getProps(this)) - - // Set the slots - const slotCtx = findSlotCtx(this._instance) - for (const key in slotCtx) { - slotCtx[key][0]().update() - } - }, - - destroyed() { - this._instance?.$destroy() - } -} - -export default { - SvelteComponent -} diff --git a/lib/live_component.ex b/lib/live_component.ex index e63191c..eabc148 100644 --- a/lib/live_component.ex +++ b/lib/live_component.ex @@ -29,7 +29,7 @@ defmodule LiveSvelte do data-props={json(@props)} data-slots={Slots.base_encode_64(@slots) |> json} phx-update="ignore" - phx-hook="SvelteComponent" + phx-hook="SvelteHook" > <% end %> diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..aaddc6d --- /dev/null +++ b/package-lock.json @@ -0,0 +1,33 @@ +{ + "name": "live_svelte", + "version": "0.2.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "live_svelte", + "version": "0.2.0", + "license": "MIT", + "devDependencies": { + "svelte": "^3.55.1" + } + }, + "node_modules/svelte": { + "version": "3.56.0", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-3.56.0.tgz", + "integrity": "sha512-LvXiJbjdvJKwB/0CQyYpDX0q+hFqCyWmybzC2G6eK1tJJA/RSRCytTfNmjHv+RHlLuA70vWG7nXp6gbeErYvRA==", + "dev": true, + "engines": { + "node": ">= 8" + } + } + }, + "dependencies": { + "svelte": { + "version": "3.56.0", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-3.56.0.tgz", + "integrity": "sha512-LvXiJbjdvJKwB/0CQyYpDX0q+hFqCyWmybzC2G6eK1tJJA/RSRCytTfNmjHv+RHlLuA70vWG7nXp6gbeErYvRA==", + "dev": true + } + } +} diff --git a/package.json b/package.json index aec1aeb..dfd91c4 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,9 @@ "module": "./priv/live_svelte.esm.js", "main": "./priv/live_svelte.cjs.js", "author": "Wout De Puysseleir ", + "devDependencies": { + "svelte": "^3.55.1" + }, "exports": { "import": "./priv/live_svelte.esm.js", "require": "./priv/live_svelte.cjs.js" diff --git a/priv/live_svelte.esm.js b/priv/live_svelte.esm.js index 95b2ac0..e669558 100644 --- a/priv/live_svelte.esm.js +++ b/priv/live_svelte.esm.js @@ -1,3 +1,5 @@ +import {detach, insert, noop} from 'svelte/internal' + function exportSvelteComponents(components) { let { default: modules, filenames } = components @@ -8,6 +10,115 @@ function exportSvelteComponents(components) { return Object.assign({}, ...modules.map((m, index) => ({[filenames[index]]: m.default}))) } -module.exports = { - exportSvelteComponents, +function base64ToElement(base64) { + let template = document.createElement('div') + template.innerHTML = atob(base64).trim() + return template +} + +function dataAttributeToJson(attributeName, el) { + const data = el.getAttribute(attributeName) + return data ? JSON.parse(data) : {} +} + +function createSlots(slots, ref) { + const createSlot = (slotName, ref) => { + let savedTarget, savedAnchor, savedElement + return () => { + return { + getElement() { + return base64ToElement(dataAttributeToJson('data-slots', ref.el)[slotName]) + }, + update() { + const element = this.getElement() + detach(savedElement) + insert(savedTarget, element, savedAnchor) + savedElement = element + }, + c: noop, + m(target, anchor) { + const element = this.getElement() + savedTarget = target + savedAnchor = anchor + savedElement = element + insert(target, element, anchor) + }, + d(detaching) { + if (detaching) detach(savedElement) + }, + l: noop, + } + } + } + + const svelteSlots = {} + + for (const slotName in slots) { + svelteSlots[slotName] = [createSlot(slotName, ref)] + } + + return svelteSlots +} + +function getProps(ref) { + return { + ...dataAttributeToJson('data-props', ref.el), + pushEvent: (event, data, callback) => ref.pushEvent(event, data, callback), + $$slots: createSlots(dataAttributeToJson('data-slots', ref.el), ref), + $$scope: {} + } +} + +function findSlotCtx(component) { + // The default slot always exists if there's a slot set + // even if no slot is set for the explicit default slot + return component.$$.ctx.find(ctxElement => ctxElement.default) +} + +function getHooks(Components) { + const components = exportSvelteComponents(Components) + + const SvelteHook = { + mounted() { + const componentName = this.el.getAttribute('data-name') + if (!componentName) { + throw new Error('Component name must be provided') + } + + const Component = components[componentName] + if (!Component) { + throw new Error(`Unable to find ${componentName} component.`) + } + + this._instance = new Component({ + target: this.el, + props: getProps(this), + hydrate: true + }) + }, + + updated() { + // Set the props + this._instance.$set(getProps(this)) + + // Set the slots + const slotCtx = findSlotCtx(this._instance) + for (const key in slotCtx) { + slotCtx[key][0]().update() + } + }, + + destroyed() { + this._instance?.$destroy() + } + } + + return { + SvelteHook + } +} + +module.exports = { + exportSvelteComponents, + getHooks }