diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/intranet-main.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/intranet-main.tt
index 85ba090b50..a597af7964 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/modules/intranet-main.tt
+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/intranet-main.tt
@@ -13,7 +13,7 @@
[% END %]
[% Asset.css("css/mainpage.css") | $raw %]
-[% Asset.js("js/vue/dist/islands.js") | $raw %]
+[% Asset.js("js/vue/dist/islands.js", "defer" => 1, async => "1") | $raw %]
[% INCLUDE 'doc-head-close.inc' %]
@@ -335,7 +335,7 @@
🚧 Static Page with Vue Components 🚧
-
+
@@ -348,7 +348,8 @@
});
setTimeout(() => {
- document.getElementById("hello-islands").dataset.props = JSON.stringify({ message: "This is a delayed message! Changes to data-props currently unfortunately reset the internal state." });
+ const [firstHelloIslandsElement] = document.getElementsByTagName("hello-islands");
+ firstHelloIslandsElement.message = "This is a delayed update to the message attribute of the web commponent.";
}, 2000);
});
diff --git a/koha-tmpl/intranet-tmpl/prog/js/vue/components/HelloIslands.vue b/koha-tmpl/intranet-tmpl/prog/js/vue/components/HelloIslands.vue
index 685e9f23d5..6890ece726 100644
--- a/koha-tmpl/intranet-tmpl/prog/js/vue/components/HelloIslands.vue
+++ b/koha-tmpl/intranet-tmpl/prog/js/vue/components/HelloIslands.vue
@@ -1,5 +1,5 @@
- Hello from Islands!
+ Hello from Islands!
This component is rendered as an island in a static HTML page.
@@ -20,7 +20,11 @@ export default {
props: {
message: {
type: String,
- default: "",
+ default: "No content",
+ },
+ color: {
+ type: String,
+ default: "crimson",
},
},
data() {
diff --git a/koha-tmpl/intranet-tmpl/prog/js/vue/modules/islands.ts b/koha-tmpl/intranet-tmpl/prog/js/vue/modules/islands.ts
index 379c1c954a..db8dcb5d92 100644
--- a/koha-tmpl/intranet-tmpl/prog/js/vue/modules/islands.ts
+++ b/koha-tmpl/intranet-tmpl/prog/js/vue/modules/islands.ts
@@ -1,93 +1,20 @@
-import { createApp, Component, App } from "vue";
+import { Component, defineCustomElement } from "vue";
+import HelloIslands from "../components/HelloIslands.vue";
/**
* A registry for Vue components.
- * @type {Map}
+ * @type {Map}
*/
-export const componentRegistry: Map = new Map<
- string,
- Component
->();
+export const componentRegistry: Map Promise> = new Map(
+ [["hello-islands", HelloIslands]]
+);
-/**
- * Registers a Vue component with a name.
- * @param {string} name - The name of the component.
- * @param {Component} component - The Vue component to register.
- * @returns {void}
- */
-export function registerComponent(name: string, component: Component): void {
- componentRegistry.set(name, component);
-}
-
-/**
- * Mounts Vue components to DOM elements based on the `data-component` attribute.
- * Components are created with props parsed from the `data-props` attribute.
- * Watches for changes in props and updates the component accordingly.
- * @returns {void}
- */
-export function mountComponents(): void {
- console.log("Mounting components");
-
- const elements: NodeListOf =
- document.querySelectorAll("[data-component]");
- elements.forEach((element: Element) => {
- const componentName: string | null =
- element.getAttribute("data-component");
- if (!componentName) {
- console.warn("No data-component attribute found.");
- return;
- }
-
- const component: Component | undefined =
- componentRegistry.get(componentName);
- if (!component) {
- console.warn(`Component ${componentName} not found.`);
- return;
- }
-
- const props: string | null = element.getAttribute("data-props");
- const parsedProps: Record = props ? JSON.parse(props) : {};
-
- // Create and mount the Vue component
- const app: App = createApp(component, parsedProps);
- app.mount(element);
-
- // Watch for updates to props
- watchProps(element, app, component);
+// Register and define custom elements
+window.requestIdleCallback(() => {
+ componentRegistry.forEach((component, name) => {
+ customElements.define(
+ name,
+ defineCustomElement(component as any, { shadowRoot: false })
+ );
});
-}
-
-/**
- * Watches for changes in props and updates the component accordingly.
- * @param {Element} element - The DOM element where the component is mounted.
- * @param {App} app - The Vue application instance.
- * @param {Component} component - The Vue component.
- * @returns {void}
- */
-function watchProps(element: Element, app: App, component: Component): void {
- const propsAttr: string | null = element.getAttribute("data-props");
- let prevProps: Record = propsAttr ? JSON.parse(propsAttr) : {};
-
- const observer = new MutationObserver(() => {
- const newPropsAttr: string | null = element.getAttribute("data-props");
- if (newPropsAttr) {
- const newProps: Record = JSON.parse(newPropsAttr);
- if (JSON.stringify(newProps) !== JSON.stringify(prevProps)) {
- prevProps = newProps;
- app.unmount(); // Unmount existing component
- createApp(component, newProps).mount(element); // Mount with new props
- }
- }
- });
-
- observer.observe(element, {
- attributes: true,
- attributeFilter: ["data-props"],
- });
-}
-
-import HelloIslands from "../components/HelloIslands.vue";
-registerComponent("HelloIslands", HelloIslands);
-
-// Automatically mount components when the DOM is fully loaded
-document.addEventListener("DOMContentLoaded", mountComponents);
+});
diff --git a/package.json b/package.json
index 7d3f59253b..15d97d2408 100644
--- a/package.json
+++ b/package.json
@@ -38,7 +38,7 @@
"pinia": "^2.0.13",
"sass": "^1.58.1",
"style-loader": "^3.3.1",
- "vue": "^3.2.31",
+ "vue": "^3.5.4",
"vue-flatpickr-component": "^9",
"vue-router": "^4.0.14",
"vue-select": "4.0.0-beta.3",
@@ -69,8 +69,6 @@
"author": "",
"license": "GPL-3.0",
"devDependencies": {
- "@babel/core": "^7.26.8",
- "@koha-community/prettier-plugin-template-toolkit": "^2.0.2",
"@rspack/cli": "^1.0.1",
"@rspack/core": "^1.0.1",
"@vue/compiler-sfc": "^3.2.31",
@@ -101,4 +99,4 @@
"webpack-cli": "^4.9.2",
"webpack-dev-server": "^4.7.4"
}
-}
+}
\ No newline at end of file
diff --git a/rspack.config.js b/rspack.config.js
index 78802b7e1f..f8152c1d0b 100644
--- a/rspack.config.js
+++ b/rspack.config.js
@@ -88,6 +88,7 @@ module.exports = {
new rspack.DefinePlugin({
__VUE_OPTIONS_API__: true,
__VUE_PROD_DEVTOOLS__: false,
+ __VUE_PROD_HYDRATION_MISMATCH_DETAILS__: false,
}),
],
externals: {