mirror of
https://github.com/woutdp/live_svelte
synced 2026-05-24 09:28:21 +00:00
Put hook in js import
This commit is contained in:
parent
f97c6647bf
commit
d9b311ca2a
6 changed files with 153 additions and 114 deletions
|
|
@ -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)"})
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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"
|
||||
>
|
||||
</div>
|
||||
<% end %>
|
||||
|
|
|
|||
33
package-lock.json
generated
Normal file
33
package-lock.json
generated
Normal file
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -7,6 +7,9 @@
|
|||
"module": "./priv/live_svelte.esm.js",
|
||||
"main": "./priv/live_svelte.cjs.js",
|
||||
"author": "Wout De Puysseleir <contact@wout.space>",
|
||||
"devDependencies": {
|
||||
"svelte": "^3.55.1"
|
||||
},
|
||||
"exports": {
|
||||
"import": "./priv/live_svelte.esm.js",
|
||||
"require": "./priv/live_svelte.cjs.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
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue