View | Details | Raw Unified | Return to bug 37911
Collapse All | Expand All

(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/intranet-main.tt (-36 / +1 lines)
Lines 301-351 Link Here
301
            </div> <!-- /.col-sm-9 -->
301
            </div> <!-- /.col-sm-9 -->
302
302
303
        </div> <!-- /.row -->
303
        </div> <!-- /.row -->
304
        <div class="row w-25 m-auto p-4 border border-4 border-warning br-4 rounded text-center">
305
            <h1>🚧 Static Page with Vue Components 🚧</h1>
306
307
            <hello-islands message="This is an attribute on the web component, that's bound to a prop in the Vue component. Cool, right?" color="dodgerblue"></hello-islands>
308
        </div> <!-- /.row -->
309
        <div class="row w-25 m-auto p-4 border border-4 border-warning br-5 rounded text-center mt-4">
310
            <p>
311
                <span>Uncomment me in <span class="font-monospace">intranet-main.tt</span> 👇</span><br>
312
                <span class="font-monospace text-danger">-&lt;!-- &lt;hello-world&gt;&lt;/hello-world&gt; --&gt;</span><br>
313
                <span class="font-monospace text-success">+&lt;hello-world&gt;&lt;/hello-world&gt;</span>
314
            </p>
315
            <!-- <hello-world></hello-world> -->
316
        </div> <!-- /.row -->
317
318
        <div class="row w-25 m-auto p-4 border border-4 border-warning br-5 rounded text-center mt-4">
319
            <dialog-island id="surprise"></dialog-island>
320
            <button id="surprise-button" class="btn btn-primary">Click me for a surprise!</button>
321
        </div>
322
304
323
[% MACRO jsinclude BLOCK %]
305
[% MACRO jsinclude BLOCK %]
324
    [% INCLUDE 'calendar.inc' %]
306
    [% INCLUDE 'calendar.inc' %]
325
    [% Asset.js("js/vue/dist/islands.js", "init" => "1") | $raw %]
307
326
    <!--
327
    [% SET islands = Asset.js("js/vue/dist/islands.esm.js").match('(src="([^"]+)")').1 %]
328
    <script src="[% islands %]" type="module"></script>
329
    <script type="module">
330
        import { hydrate } from "[% islands %]";
331
        hydrate();
332
    </script>
333
    -->
334
    <script>
308
    <script>
335
        var MSG_CONFIRM_DELETE = _("Are you sure you want to delete this news item? This cannot be undone.");
309
        var MSG_CONFIRM_DELETE = _("Are you sure you want to delete this news item? This cannot be undone.");
336
        $(document).ready(function(){
310
        $(document).ready(function(){
337
            $(".news_delete").on("click", function(){
311
            $(".news_delete").on("click", function(){
338
                return confirmDelete(MSG_CONFIRM_DELETE);
312
                return confirmDelete(MSG_CONFIRM_DELETE);
339
            });
313
            });
340
341
            setTimeout(() => {
342
                const [firstHelloIslandsElement] = document.getElementsByTagName("hello-islands");
343
                firstHelloIslandsElement.message = 'This is a delayed update to the message attribute of the web commponent.'
344
            }, 2000);
345
346
            $("#surprise-button").on("click", () => {
347
                $("#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>`);
348
            });
349
        });
314
        });
350
    </script>
315
    </script>
351
[% END %]
316
[% END %]
(-)a/koha-tmpl/intranet-tmpl/prog/js/vue/components/Islands/DialogIsland.vue (-107 lines)
Lines 1-107 Link Here
1
<template>
2
    <Dialog />
3
</template>
4
5
<script>
6
import { inject, watch } from "vue"
7
import Dialog from "../Dialog.vue"
8
9
export default {
10
    props: {
11
        message: {
12
            type: String,
13
            default: null,
14
        },
15
        error: {
16
            type: String,
17
            default: null,
18
        },
19
        warning: {
20
            type: String,
21
            default: null,
22
        },
23
        confirmation: {
24
            type: Object,
25
            default: () => null,
26
        },
27
        isSubmitting: {
28
            type: Boolean,
29
            default: false,
30
        },
31
        isLoading: {
32
            type: Boolean,
33
            default: false,
34
        },
35
        acceptCallback: {
36
            type: Function,
37
            default: null,
38
        },
39
    },
40
    setup(props) {
41
        const mainStore = inject("mainStore")
42
        const {
43
            setMessage,
44
            setError,
45
            setWarning,
46
            setConfirmation,
47
            setSubmitting,
48
            setLoading,
49
            setAcceptCallback,
50
        } = mainStore
51
52
        watch(
53
            () => props.message,
54
            newVal => {
55
                setMessage(newVal)
56
            }
57
        )
58
59
        watch(
60
            () => props.error,
61
            newVal => {
62
                setError(newVal)
63
            }
64
        )
65
66
        watch(
67
            () => props.warning,
68
            newVal => {
69
                setWarning(newVal)
70
            }
71
        )
72
73
        watch(
74
            () => props.confirmation,
75
            newVal => {
76
                setConfirmation(newVal)
77
            }
78
        )
79
80
        watch(
81
            () => props.isSubmitting,
82
            newVal => {
83
                setSubmitting(newVal)
84
            }
85
        )
86
87
        watch(
88
            () => props.isLoading,
89
            newVal => {
90
                setLoading(newVal)
91
            }
92
        )
93
94
        watch(
95
            () => props.acceptCallback,
96
            newVal => {
97
                setAcceptCallback(newVal)
98
            }
99
        )
100
101
        return {}
102
    },
103
    components: {
104
        Dialog,
105
    },
106
}
107
</script>
(-)a/koha-tmpl/intranet-tmpl/prog/js/vue/components/Islands/HelloIslands.vue (-56 lines)
Lines 1-56 Link Here
1
<template>
2
    <h2 :style="{ color }">Hello from Islands!</h2>
3
    <p>This component is rendered as an island in a static HTML page.</p>
4
5
    <!-- Display message prop -->
6
    <p v-if="message">{{ message }}</p>
7
8
    <!-- Display store data -->
9
    <p v-if="stringFromStore">{{ stringFromStore }}</p>
10
    <p v-if="anotherStoreString">{{ anotherStoreString }}</p>
11
12
    <!-- Reactive counter example -->
13
    <p>Counter: {{ count }}</p>
14
    <!-- Koha's bootstrap works in here! -->
15
    <button @click="incrementCounter" class="btn btn-primary">
16
        Increment Counter
17
    </button>
18
</template>
19
20
<script>
21
import { ref } from "vue"
22
import { inject } from "vue"
23
24
export default {
25
    props: {
26
        message: {
27
            type: String,
28
            default: "No content",
29
        },
30
        color: {
31
            type: String,
32
            default: "crimson",
33
        },
34
    },
35
    setup() {
36
        const mainStore = inject("mainStore")
37
        const { stringFromStore } = mainStore
38
        const navigationStore = inject("navigationStore")
39
        const { anotherStoreString } = navigationStore
40
        return {
41
            stringFromStore,
42
            anotherStoreString,
43
        }
44
    },
45
    data() {
46
        return {
47
            count: 0,
48
        }
49
    },
50
    methods: {
51
        incrementCounter() {
52
            this.count++
53
        },
54
    },
55
}
56
</script>
(-)a/koha-tmpl/intranet-tmpl/prog/js/vue/components/Islands/HelloWorld.vue (-18 lines)
Lines 1-18 Link Here
1
<!--
2
Say Hello World with Vue!
3
-->
4
5
<script setup>
6
import { ref } from "vue"
7
8
// A "ref" is a reactive data source that stores a value.
9
// Technically, we don't need to wrap the string with ref()
10
// in order to display it, but we will see in the next
11
// example why it is needed if we ever intend to change
12
// the value.
13
const message = ref("Hello World!")
14
</script>
15
16
<template>
17
    <h1>{{ message }}</h1>
18
</template>
(-)a/koha-tmpl/intranet-tmpl/prog/js/vue/modules/islands.ts (-44 / +20 lines)
Lines 19-70 type WebComponentDynamicImport = { Link Here
19
/**
19
/**
20
 * A registry for Vue components.
20
 * A registry for Vue components.
21
 * @type {Map<string, WebComponentDynamicImport>}
21
 * @type {Map<string, WebComponentDynamicImport>}
22
 * @property {string} key - The name of the component.
23
 * @property {WebComponentDynamicImport} value - The configuration for the component. Includes the import function and optional configuration.
24
 * @example
25
 * //
26
 * [
27
 *     "hello-islands",
28
 *     {
29
 *         importFn: async () => {
30
 *             const module = await import(
31
 *                 /* webpackChunkName: "hello-islands" */
32
/**                "../components/Islands/HelloIslands.vue"
33
 *             );
34
 *             return module.default;
35
 *         },
36
 *         config: {
37
 *             stores: ["mainStore", "navigationStore"],
38
 *         },
39
 *     },
40
 * ],
22
 */
41
 */
23
export const componentRegistry: Map<string, WebComponentDynamicImport> =
42
export const componentRegistry: Map<string, WebComponentDynamicImport> =
24
    new Map([
43
    new Map([]);
25
        [
26
            "hello-islands",
27
            {
28
                importFn: async () => {
29
                    const module = await import(
30
                        /* webpackChunkName: "hello-islands" */
31
                        "../components/Islands/HelloIslands.vue"
32
                    );
33
                    return module.default;
34
                },
35
                config: {
36
                    stores: ["mainStore", "navigationStore"],
37
                },
38
            },
39
        ],
40
        [
41
            "hello-world",
42
            {
43
                importFn: async () => {
44
                    const module = await import(
45
                        /* webpackChunkName: "hello-world" */
46
                        "../components/Islands/HelloWorld.vue"
47
                    );
48
                    return module.default;
49
                },
50
            },
51
        ],
52
        [
53
            "dialog-island",
54
            {
55
                importFn: async () => {
56
                    const module = await import(
57
                        /* webpackChunkName: "vue-dialog" */
58
                        "../components/Islands/DialogIsland.vue"
59
                    );
60
                    return module.default;
61
                },
62
                config: {
63
                    stores: ["mainStore"],
64
                },
65
            },
66
        ],
67
    ]);
68
44
69
/**
45
/**
70
 * Hydrates custom elements by scanning the document and loading only necessary components.
46
 * Hydrates custom elements by scanning the document and loading only necessary components.
(-)a/koha-tmpl/intranet-tmpl/prog/js/vue/stores/main.js (-1 lines)
Lines 12-18 export const useMainStore = defineStore("main", { Link Here
12
        displayed_already: false,
12
        displayed_already: false,
13
        _is_submitting: false,
13
        _is_submitting: false,
14
        _is_loading: false,
14
        _is_loading: false,
15
        stringFromStore: "Hello from main store",
16
    }),
15
    }),
17
    actions: {
16
    actions: {
18
        setMessage(message, displayed = false) {
17
        setMessage(message, displayed = false) {
(-)a/koha-tmpl/intranet-tmpl/prog/js/vue/stores/navigation.js (-2 lines)
Lines 11-17 export const useNavigationStore = defineStore("navigation", { Link Here
11
        },
11
        },
12
        current: null,
12
        current: null,
13
        params: {},
13
        params: {},
14
        anotherStoreString: "Hello from nav store",
15
    }),
14
    }),
16
    actions: {
15
    actions: {
17
        setRoutes(routesDef) {
16
        setRoutes(routesDef) {
18
- 

Return to bug 37911