Skip to content

Commit

Permalink
feat: Implement Vue Web Component, add nested Vue Web Component into …
Browse files Browse the repository at this point in the history
…Vanilla TS app
  • Loading branch information
WaldemarLehner committed Apr 23, 2024
1 parent 7e2ffe3 commit 81d9894
Show file tree
Hide file tree
Showing 7 changed files with 56 additions and 28 deletions.
1 change: 0 additions & 1 deletion webcomponent-react/src/main.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.tsx'
import './index.css'

ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
Expand Down
36 changes: 36 additions & 0 deletions webcomponent-vanilla/src/Frontend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,16 @@ const template = `
<p>
Diese Komponent ist mit Vanilla TS / Vite implementiert
</p>
<p>Als Demonstration, dass Verschachtelung auch möglich ist, wurde hier die Vite+Vue3 Komponente verschachtelt einbunden und an den <b>lokalen</b> Zustand angebunden </p>
<div id="nested-frontend"> Loading... </div>
</div>
`



export default function createFrontend(parent: HTMLElement, initialCount: number, setCount: (count: number) => void) {
parent.innerHTML = template

Expand All @@ -30,6 +37,7 @@ export default function createFrontend(parent: HTMLElement, initialCount: number

const counterElement = parent.querySelector<HTMLButtonElement>("#counter")!
const localCounterElement = parent.querySelector<HTMLButtonElement>("#local-counter")!
let nestedFrontendElement: Element | undefined = undefined

const updateCounter = (newCount: number) => {
count = newCount
Expand All @@ -39,6 +47,12 @@ export default function createFrontend(parent: HTMLElement, initialCount: number
const updateLocalCounter = (newCount: number) => {
localCount = newCount
localCounterElement.innerHTML = `Lokaler Zustand ${newCount}`

if(!!nestedFrontendElement) {
// @ts-ignore
nestedFrontendElement.count = localCount
}

}

counterElement.addEventListener("click", () => setCount(count + 1))
Expand All @@ -47,5 +61,27 @@ export default function createFrontend(parent: HTMLElement, initialCount: number
updateCounter(count)
updateLocalCounter(localCount)

const nestedFrontendRoot = parent.querySelector<HTMLDivElement>("#nested-frontend")!

insertNestedFrontend(nestedFrontendRoot, updateLocalCounter).then( e => nestedFrontendElement = e )

return updateCounter
}

async function insertNestedFrontend(element: HTMLDivElement, newStateCallback: (count: number) => void) {
// @ts-ignore
(await import("http://localhost:5002/webcomponent.js")).default("webcomponent-vue") // redundant, but here anyways
// for consistency

element.innerHTML = `<webcomponent-vue count='0' class="${styles.nested}"></webcomponent-vue>`

const nestedFrontend = element.querySelector("webcomponent-vue")!


nestedFrontend.addEventListener("count", (ev) => {
const [count] = (ev as any).detail as [number]
newStateCallback(count)
})

return nestedFrontend
}
4 changes: 4 additions & 0 deletions webcomponent-vanilla/src/index.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,7 @@
}
}

.nested {
border: wheat 1px solid;
display: flow-root;
}
2 changes: 1 addition & 1 deletion webcomponent-vue/src/App.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script setup lang="ts">
import { ref } from "vue";
import Frontend from "./components/Frontend.vue"
import Frontend from "./components/Frontend.ce.vue"
const count = ref(0)
Expand Down
1 change: 1 addition & 0 deletions webcomponent-vue/src/components/Frontend.ce.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const localCount = ref(0)
<template>
<div class="root">
<div>

<a href="https://vitejs.dev" target="_blank">
<img src="/vite.svg" class="logo" alt="Vite logo" />
</a>
Expand Down
38 changes: 14 additions & 24 deletions webcomponent-vue/src/webcomponent.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// The web-component build process uses this as the export
// This defines a web-component which starts the Web-App that was defined here.
import { Ref, createApp, ref } from "vue"
import Frontend from "./components/Frontend.vue"
import { defineCustomElement } from "vue"
import Frontend from "./components/Frontend.ce.vue"


export default function register(name: string) {
Expand All @@ -14,14 +14,15 @@ export default function register(name: string) {
customElements.define(name, MicrofrontendWebComponent)
}

class MicrofrontendWebComponent extends HTMLElement {

const VueWebComponent = defineCustomElement(Frontend)

// See also: https://vuejs.org/guide/extras/web-components.html
class MicrofrontendWebComponent extends VueWebComponent {
declare count: number
declare styleAddress?: string
#root!: ShadowRoot

#state = ref(0)
render() {
this.#state.value = this.count
// Vue's defineCustomElement already does everything for us :)
}

private emitCountUpdate(newCount: number) {
Expand All @@ -31,25 +32,14 @@ class MicrofrontendWebComponent extends HTMLElement {
}

connectedCallback() {
this.#root = this.attachShadow({mode: "open"})
const mount = document.createElement("div")

const styleAddress = this.styleAddress ?? import.meta.env.BASE_URL + "style.css"

const style = document.createElement("link");
style.rel = "stylesheet"
style.href = styleAddress
this.#root.appendChild(style)
super.connectedCallback()

this.#root.appendChild(mount)
// Register update listener
super.addEventListener("count", (ev) => {
const [count] = (ev as any).detail as [number]
this.emitCountUpdate(count)
})

createApp(Frontend, {
count: this.#state,
onCount: (count: Ref<number>) => {
debugger
this.emitCountUpdate(count.value)
}
}).mount(mount);
}
}

Expand Down
2 changes: 0 additions & 2 deletions webcomponent-vue/vite-webcomponent.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,13 @@ import {resolve} from "node:path"
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
define: {
"process.env": {}
},
base: "http://localhost:5002/",
build: {
minify: false,
lib: {
entry: resolve(__dirname, "src/webcomponent.ts"),
formats: [
Expand Down

0 comments on commit 81d9894

Please sign in to comment.