Bug 37911: (follow-up) Allow for initialisation of islands on demand
- Split the rspack config into a umd and an esm section.
- Don't hydrate by default.
- Instead check the "init" attribute for the currentScript when using the umd.
- Allow for manual import and call when using the esm.
- Add examples for both in intranet-main.tt (to be removed).
- Add a DialogIsland wrapper for the shared Dialog component (to map props to stored refs).
- Move DialogIsland as well as Demo components into an Islands directory under components.
- Add i18n initialisation to hydrate's idle callback.
Signed-off-by: Matt Blenkinsop <matt.blenkinsop@ptfs-europe.com>
Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>
Signed-off-by: Katrin Fischer <katrin.fischer@bsz-bw.de>
This commit is contained in:
parent
aa5283dd62
commit
bd15b1829e
|
|
@ -347,8 +347,23 @@
|
|||
</div>
|
||||
<!-- /.row -->
|
||||
|
||||
<div class="row w-25 m-auto p-4 border border-4 border-warning br-5 rounded text-center mt-4">
|
||||
<dialog-island id="surprise"></dialog-island>
|
||||
<button id="surprise-button" class="btn btn-primary">Click me for a surprise!</button>
|
||||
</div>
|
||||
|
||||
[% MACRO jsinclude BLOCK %]
|
||||
[% Asset.js("js/vue/dist/islands.js", "type" => "module") | $raw %]
|
||||
[% INCLUDE 'calendar.inc' %]
|
||||
[% Asset.js("js/vue/dist/islands.js", "init" => "1") | $raw %]
|
||||
<!--
|
||||
[% SET islands = Asset.js("js/vue/dist/islands.esm.js").match('(src="([^"]+)")').1 %]
|
||||
<script src="[% islands %]" type="module">
|
||||
</script>
|
||||
<script type="module">
|
||||
import { hydrate } from "[% islands %]";
|
||||
hydrate();
|
||||
</script>
|
||||
-->
|
||||
<script>
|
||||
var MSG_CONFIRM_DELETE = _("Are you sure you want to delete this news item? This cannot be undone.");
|
||||
$(document).ready(function () {
|
||||
|
|
@ -360,6 +375,10 @@
|
|||
const [firstHelloIslandsElement] = document.getElementsByTagName("hello-islands");
|
||||
firstHelloIslandsElement.message = "This is a delayed update to the message attribute of the web commponent.";
|
||||
}, 2000);
|
||||
|
||||
$("#surprise-button").on("click", () => {
|
||||
$("#surprise").attr("warning", `<span>Yes, this is <span class="font-monospace">Dialog.vue</span> (in a wrapper component)!</span><span class="d-none">${Math.random()}</span>`);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
[% END %]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,107 @@
|
|||
<template>
|
||||
<Dialog />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { inject, watch } from "vue";
|
||||
import Dialog from "../Dialog.vue";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
message: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
error: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
warning: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
confirmation: {
|
||||
type: Object,
|
||||
default: () => null,
|
||||
},
|
||||
isSubmitting: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
isLoading: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
acceptCallback: {
|
||||
type: Function,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const mainStore = inject("mainStore");
|
||||
const {
|
||||
setMessage,
|
||||
setError,
|
||||
setWarning,
|
||||
setConfirmation,
|
||||
setSubmitting,
|
||||
setLoading,
|
||||
setAcceptCallback,
|
||||
} = mainStore;
|
||||
|
||||
watch(
|
||||
() => props.message,
|
||||
newVal => {
|
||||
setMessage(newVal);
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.error,
|
||||
newVal => {
|
||||
setError(newVal);
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.warning,
|
||||
newVal => {
|
||||
setWarning(newVal);
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.confirmation,
|
||||
newVal => {
|
||||
setConfirmation(newVal);
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.isSubmitting,
|
||||
newVal => {
|
||||
setSubmitting(newVal);
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.isLoading,
|
||||
newVal => {
|
||||
setLoading(newVal);
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.acceptCallback,
|
||||
newVal => {
|
||||
setAcceptCallback(newVal);
|
||||
}
|
||||
);
|
||||
|
||||
return {};
|
||||
},
|
||||
components: {
|
||||
Dialog,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
import { Component, defineCustomElement } from "vue";
|
||||
import { createPinia } from "pinia";
|
||||
import { $__ } from "../i18n";
|
||||
import { useMainStore } from "../stores/main";
|
||||
import { useNavigationStore } from "../stores/navigation";
|
||||
|
||||
|
|
@ -27,7 +28,7 @@ export const componentRegistry: Map<string, WebComponentDynamicImport> =
|
|||
importFn: async () => {
|
||||
const module = await import(
|
||||
/* webpackChunkName: "hello-islands" */
|
||||
"../components/HelloIslands.vue"
|
||||
"../components/Islands/HelloIslands.vue"
|
||||
);
|
||||
return module.default;
|
||||
},
|
||||
|
|
@ -42,12 +43,27 @@ export const componentRegistry: Map<string, WebComponentDynamicImport> =
|
|||
importFn: async () => {
|
||||
const module = await import(
|
||||
/* webpackChunkName: "hello-world" */
|
||||
"../components/HelloWorld.vue"
|
||||
"../components/Islands/HelloWorld.vue"
|
||||
);
|
||||
return module.default;
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
"dialog-island",
|
||||
{
|
||||
importFn: async () => {
|
||||
const module = await import(
|
||||
/* webpackChunkName: "vue-dialog" */
|
||||
"../components/Islands/DialogIsland.vue"
|
||||
);
|
||||
return module.default;
|
||||
},
|
||||
config: {
|
||||
stores: ["mainStore"],
|
||||
},
|
||||
},
|
||||
],
|
||||
]);
|
||||
|
||||
/**
|
||||
|
|
@ -88,6 +104,7 @@ export function hydrate(): void {
|
|||
app.provide(store, storesMatrix[store]);
|
||||
});
|
||||
}
|
||||
app.config.globalProperties.$__ = $__;
|
||||
// Further config options can be added here as we expand this further
|
||||
},
|
||||
}),
|
||||
|
|
@ -97,4 +114,6 @@ export function hydrate(): void {
|
|||
});
|
||||
}
|
||||
|
||||
hydrate();
|
||||
if (parseInt(document?.currentScript?.getAttribute("init") ?? "0", 10)) {
|
||||
hydrate();
|
||||
}
|
||||
|
|
|
|||
222
rspack.config.js
222
rspack.config.js
|
|
@ -3,8 +3,9 @@ const { VueLoaderPlugin } = require("vue-loader");
|
|||
const path = require("path");
|
||||
const rspack = require("@rspack/core");
|
||||
|
||||
module.exports = {
|
||||
resolve: {
|
||||
module.exports = [
|
||||
{
|
||||
resolve: {
|
||||
alias: {
|
||||
"@fetch": path.resolve(
|
||||
__dirname,
|
||||
|
|
@ -13,90 +14,145 @@ module.exports = {
|
|||
},
|
||||
},
|
||||
experiments: {
|
||||
css: true,
|
||||
},
|
||||
entry: {
|
||||
erm: "./koha-tmpl/intranet-tmpl/prog/js/vue/modules/erm.ts",
|
||||
preservation:
|
||||
"./koha-tmpl/intranet-tmpl/prog/js/vue/modules/preservation.ts",
|
||||
"admin/record_sources":
|
||||
"./koha-tmpl/intranet-tmpl/prog/js/vue/modules/admin/record_sources.ts",
|
||||
islands: "./koha-tmpl/intranet-tmpl/prog/js/vue/modules/islands.ts",
|
||||
},
|
||||
output: {
|
||||
filename: "[name].js",
|
||||
path: path.resolve(
|
||||
__dirname,
|
||||
"koha-tmpl/intranet-tmpl/prog/js/vue/dist/"
|
||||
),
|
||||
chunkFilename: "[name].[contenthash].js",
|
||||
globalObject: "window",
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.vue$/,
|
||||
loader: "vue-loader",
|
||||
options: {
|
||||
experimentalInlineMatchResource: true,
|
||||
},
|
||||
exclude: [path.resolve(__dirname, "t/cypress/")],
|
||||
},
|
||||
{
|
||||
test: /\.ts$/,
|
||||
loader: "builtin:swc-loader",
|
||||
options: {
|
||||
jsc: {
|
||||
parser: {
|
||||
syntax: "typescript",
|
||||
},
|
||||
css: true,
|
||||
},
|
||||
entry: {
|
||||
erm: "./koha-tmpl/intranet-tmpl/prog/js/vue/modules/erm.ts",
|
||||
preservation:
|
||||
"./koha-tmpl/intranet-tmpl/prog/js/vue/modules/preservation.ts",
|
||||
"admin/record_sources":
|
||||
"./koha-tmpl/intranet-tmpl/prog/js/vue/modules/admin/record_sources.ts",
|
||||
islands: "./koha-tmpl/intranet-tmpl/prog/js/vue/modules/islands.ts",
|
||||
},
|
||||
output: {
|
||||
filename: "[name].js",
|
||||
path: path.resolve(
|
||||
__dirname,
|
||||
"koha-tmpl/intranet-tmpl/prog/js/vue/dist/"
|
||||
),
|
||||
chunkFilename: "[name].[contenthash].js",
|
||||
globalObject: "window",
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.vue$/,
|
||||
loader: "vue-loader",
|
||||
options: {
|
||||
experimentalInlineMatchResource: true,
|
||||
},
|
||||
appendTsSuffixTo: [/\.vue$/],
|
||||
exclude: [path.resolve(__dirname, "t/cypress/")],
|
||||
},
|
||||
exclude: [
|
||||
/node_modules/,
|
||||
path.resolve(__dirname, "t/cypress/"),
|
||||
],
|
||||
type: "javascript/auto",
|
||||
},
|
||||
{
|
||||
test: /\.css$/i,
|
||||
type: "javascript/auto",
|
||||
use: ["style-loader", "css-loader"],
|
||||
},
|
||||
{
|
||||
test: /\.ts$/,
|
||||
loader: "builtin:swc-loader",
|
||||
options: {
|
||||
jsc: {
|
||||
parser: {
|
||||
syntax: "typescript",
|
||||
},
|
||||
},
|
||||
appendTsSuffixTo: [/\.vue$/],
|
||||
},
|
||||
exclude: [
|
||||
/node_modules/,
|
||||
path.resolve(__dirname, "t/cypress/"),
|
||||
],
|
||||
type: "javascript/auto",
|
||||
},
|
||||
{
|
||||
test: /\.css$/i,
|
||||
type: "javascript/auto",
|
||||
use: ["style-loader", "css-loader"],
|
||||
},
|
||||
],
|
||||
},
|
||||
plugins: [
|
||||
new VueLoaderPlugin(),
|
||||
new rspack.DefinePlugin({
|
||||
__VUE_OPTIONS_API__: true,
|
||||
__VUE_PROD_DEVTOOLS__: false,
|
||||
__VUE_PROD_HYDRATION_MISMATCH_DETAILS__: false,
|
||||
}),
|
||||
],
|
||||
},
|
||||
/**
|
||||
optimization: {
|
||||
splitChunks: {
|
||||
chunks: "all",
|
||||
cacheGroups: {
|
||||
default: false, // Disable default cache groups to avoid affecting existing bundles
|
||||
vendors: false, // Disable vendor caching
|
||||
vue: {
|
||||
test: /[\\/]node_modules[\\/]vue[\\/]/,
|
||||
name: "vue",
|
||||
chunks: "all",
|
||||
enforce: true,
|
||||
},
|
||||
},
|
||||
externals: {
|
||||
jquery: "jQuery",
|
||||
"datatables.net": "DataTable",
|
||||
"datatables.net-buttons": "DataTable",
|
||||
"datatables.net-buttons/js/buttons.html5": "DataTable",
|
||||
"datatables.net-buttons/js/buttons.print": "DataTable",
|
||||
"datatables.net-buttons/js/buttons.colVis": "DataTable",
|
||||
},
|
||||
},
|
||||
*/
|
||||
plugins: [
|
||||
new VueLoaderPlugin(),
|
||||
new rspack.DefinePlugin({
|
||||
__VUE_OPTIONS_API__: true,
|
||||
__VUE_PROD_DEVTOOLS__: false,
|
||||
__VUE_PROD_HYDRATION_MISMATCH_DETAILS__: false,
|
||||
}),
|
||||
],
|
||||
externals: {
|
||||
jquery: "jQuery",
|
||||
"datatables.net": "DataTable",
|
||||
"datatables.net-buttons": "DataTable",
|
||||
"datatables.net-buttons/js/buttons.html5": "DataTable",
|
||||
"datatables.net-buttons/js/buttons.print": "DataTable",
|
||||
"datatables.net-buttons/js/buttons.colVis": "DataTable",
|
||||
{
|
||||
experiments: {
|
||||
css: true,
|
||||
outputModule: true,
|
||||
},
|
||||
entry: {
|
||||
islands: "./koha-tmpl/intranet-tmpl/prog/js/vue/modules/islands.ts",
|
||||
},
|
||||
output: {
|
||||
filename: "[name].esm.js",
|
||||
path: path.resolve(
|
||||
__dirname,
|
||||
"koha-tmpl/intranet-tmpl/prog/js/vue/dist/"
|
||||
),
|
||||
chunkFilename: "[name].[contenthash].esm.js",
|
||||
globalObject: "window",
|
||||
library: {
|
||||
type: "module",
|
||||
},
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.vue$/,
|
||||
loader: "vue-loader",
|
||||
options: {
|
||||
experimentalInlineMatchResource: true,
|
||||
},
|
||||
exclude: [path.resolve(__dirname, "t/cypress/")],
|
||||
},
|
||||
{
|
||||
test: /\.ts$/,
|
||||
loader: "builtin:swc-loader",
|
||||
options: {
|
||||
jsc: {
|
||||
parser: {
|
||||
syntax: "typescript",
|
||||
},
|
||||
},
|
||||
appendTsSuffixTo: [/\.vue$/],
|
||||
},
|
||||
exclude: [
|
||||
/node_modules/,
|
||||
path.resolve(__dirname, "t/cypress/"),
|
||||
],
|
||||
type: "javascript/auto",
|
||||
},
|
||||
{
|
||||
test: /\.css$/i,
|
||||
type: "javascript/auto",
|
||||
use: ["style-loader", "css-loader"],
|
||||
},
|
||||
],
|
||||
},
|
||||
plugins: [
|
||||
new VueLoaderPlugin(),
|
||||
new rspack.DefinePlugin({
|
||||
__VUE_OPTIONS_API__: true,
|
||||
__VUE_PROD_DEVTOOLS__: false,
|
||||
__VUE_PROD_HYDRATION_MISMATCH_DETAILS__: false,
|
||||
}),
|
||||
],
|
||||
externals: {
|
||||
jquery: "jQuery",
|
||||
"datatables.net": "DataTable",
|
||||
"datatables.net-buttons": "DataTable",
|
||||
"datatables.net-buttons/js/buttons.html5": "DataTable",
|
||||
"datatables.net-buttons/js/buttons.print": "DataTable",
|
||||
"datatables.net-buttons/js/buttons.colVis": "DataTable",
|
||||
},
|
||||
},
|
||||
};
|
||||
];
|
||||
|
|
|
|||
Loading…
Reference in New Issue