|
Lines 1-93
Link Here
|
| 1 |
import { createApp, Component, App } from "vue"; |
1 |
import { Component, defineCustomElement } from "vue"; |
|
|
2 |
import HelloIslands from "../components/HelloIslands.vue"; |
| 2 |
|
3 |
|
| 3 |
/** |
4 |
/** |
| 4 |
* A registry for Vue components. |
5 |
* A registry for Vue components. |
| 5 |
* @type {Map<string, Component>} |
6 |
* @type {Map<string, string>} |
| 6 |
*/ |
|
|
| 7 |
export const componentRegistry: Map<string, Component> = new Map< |
| 8 |
string, |
| 9 |
Component |
| 10 |
>(); |
| 11 |
|
| 12 |
/** |
| 13 |
* Registers a Vue component with a name. |
| 14 |
* @param {string} name - The name of the component. |
| 15 |
* @param {Component} component - The Vue component to register. |
| 16 |
* @returns {void} |
| 17 |
*/ |
| 18 |
export function registerComponent(name: string, component: Component): void { |
| 19 |
componentRegistry.set(name, component); |
| 20 |
} |
| 21 |
|
| 22 |
/** |
| 23 |
* Mounts Vue components to DOM elements based on the `data-component` attribute. |
| 24 |
* Components are created with props parsed from the `data-props` attribute. |
| 25 |
* Watches for changes in props and updates the component accordingly. |
| 26 |
* @returns {void} |
| 27 |
*/ |
| 28 |
export function mountComponents(): void { |
| 29 |
console.log("Mounting components"); |
| 30 |
|
| 31 |
const elements: NodeListOf<Element> = |
| 32 |
document.querySelectorAll("[data-component]"); |
| 33 |
elements.forEach((element: Element) => { |
| 34 |
const componentName: string | null = |
| 35 |
element.getAttribute("data-component"); |
| 36 |
if (!componentName) { |
| 37 |
console.warn("No data-component attribute found."); |
| 38 |
return; |
| 39 |
} |
| 40 |
|
| 41 |
const component: Component | undefined = |
| 42 |
componentRegistry.get(componentName); |
| 43 |
if (!component) { |
| 44 |
console.warn(`Component ${componentName} not found.`); |
| 45 |
return; |
| 46 |
} |
| 47 |
|
| 48 |
const props: string | null = element.getAttribute("data-props"); |
| 49 |
const parsedProps: Record<string, any> = props ? JSON.parse(props) : {}; |
| 50 |
|
| 51 |
// Create and mount the Vue component |
| 52 |
const app: App = createApp(component, parsedProps); |
| 53 |
app.mount(element); |
| 54 |
|
| 55 |
// Watch for updates to props |
| 56 |
watchProps(element, app, component); |
| 57 |
}); |
| 58 |
} |
| 59 |
|
| 60 |
/** |
| 61 |
* Watches for changes in props and updates the component accordingly. |
| 62 |
* @param {Element} element - The DOM element where the component is mounted. |
| 63 |
* @param {App} app - The Vue application instance. |
| 64 |
* @param {Component} component - The Vue component. |
| 65 |
* @returns {void} |
| 66 |
*/ |
7 |
*/ |
| 67 |
function watchProps(element: Element, app: App, component: Component): void { |
8 |
export const componentRegistry: Map<string, () => Promise<Component>> = new Map( |
| 68 |
const propsAttr: string | null = element.getAttribute("data-props"); |
9 |
[["hello-islands", HelloIslands]] |
| 69 |
let prevProps: Record<string, any> = propsAttr ? JSON.parse(propsAttr) : {}; |
10 |
); |
| 70 |
|
11 |
|
| 71 |
const observer = new MutationObserver(() => { |
12 |
// Register and define custom elements |
| 72 |
const newPropsAttr: string | null = element.getAttribute("data-props"); |
13 |
window.requestIdleCallback(() => { |
| 73 |
if (newPropsAttr) { |
14 |
componentRegistry.forEach((component, name) => { |
| 74 |
const newProps: Record<string, any> = JSON.parse(newPropsAttr); |
15 |
customElements.define( |
| 75 |
if (JSON.stringify(newProps) !== JSON.stringify(prevProps)) { |
16 |
name, |
| 76 |
prevProps = newProps; |
17 |
defineCustomElement(component as any, { shadowRoot: false }) |
| 77 |
app.unmount(); // Unmount existing component |
18 |
); |
| 78 |
createApp(component, newProps).mount(element); // Mount with new props |
|
|
| 79 |
} |
| 80 |
} |
| 81 |
}); |
19 |
}); |
| 82 |
|
20 |
}); |
| 83 |
observer.observe(element, { |
|
|
| 84 |
attributes: true, |
| 85 |
attributeFilter: ["data-props"], |
| 86 |
}); |
| 87 |
} |
| 88 |
|
| 89 |
import HelloIslands from "../components/HelloIslands.vue"; |
| 90 |
registerComponent("HelloIslands", HelloIslands); |
| 91 |
|
| 92 |
// Automatically mount components when the DOM is fully loaded |
| 93 |
document.addEventListener("DOMContentLoaded", mountComponents); |