@live-change/frontend-base 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,46 @@
1
+ <template>
2
+ <loading-zone suspense>
3
+ <template v-slot:loading>
4
+ <div class="fixed w-full h-full flex align-items-center justify-content-center top-0 left-0">
5
+ <ProgressSpinner animationDuration=".5s"/>
6
+ </div>
7
+ </template>
8
+ <template v-slot:default="{ isLoading }">
9
+ <working-zone>
10
+ <template v-slot:working>
11
+ <div class="fixed w-full h-full flex align-items-center justify-content-center top-0 left-0">
12
+ <ProgressSpinner animationDuration=".5s"/>
13
+ </div>
14
+ </template>
15
+ <template v-slot:default="{ isWorking }">
16
+ <ProgressBar v-if="isLoading || isWorking" mode="indeterminate" class="absolute w-full" style="height: .2em" />
17
+ <component :is="component" v-bind="props" @update:modelValue="value => data = value" @close="handleClose" />
18
+ </template>
19
+ </working-zone>
20
+ </template>
21
+ </loading-zone>
22
+ </template>
23
+
24
+ <script setup>
25
+
26
+ import { inject, ref } from 'vue'
27
+
28
+ import ProgressSpinner from 'primevue/progressspinner'
29
+ import ProgressBar from 'primevue/progressspinner'
30
+
31
+
32
+ const data = ref()
33
+
34
+ const dialogRef = inject('dialogRef')
35
+
36
+ const { component, props } = dialogRef.value.data
37
+
38
+ function handleClose() {
39
+ dialogRef.value.close(data)
40
+ }
41
+
42
+ </script>
43
+
44
+ <style scoped>
45
+
46
+ </style>
package/Page.vue ADDED
@@ -0,0 +1,49 @@
1
+ <template>
2
+ <div class="min-h-screen flex flex-column surface-ground">
3
+ <slot name="navbar"></slot>
4
+ <ConfirmPopup v-if="isMounted" />
5
+ <Toast v-if="isMounted" />
6
+ <DynamicDialog v-if="isMounted" />
7
+
8
+ <div class="relative h-0 w-full">
9
+ <ProgressBar v-if="loading || working" mode="indeterminate" class="absolute w-full" style="height: .2em" />
10
+ </div>
11
+ <div v-if="pageType == 'simple'" class="p-5 flex flex-column flex-auto align-items-center">
12
+ <slot></slot>
13
+ </div>
14
+ <div v-if="pageType == 'wide'">
15
+ <slot></slot>
16
+ </div>
17
+ </div>
18
+ </template>
19
+
20
+ <script setup>
21
+
22
+ import ProgressBar from "primevue/progressbar"
23
+
24
+ import ConfirmPopup from 'primevue/confirmpopup'
25
+ import Toast from 'primevue/toast'
26
+ import DynamicDialog from 'primevue/dynamicdialog';
27
+
28
+ import { onMounted, ref } from 'vue'
29
+ const isMounted = ref(false)
30
+ onMounted(() => isMounted.value = true)
31
+
32
+ const { working, loading } = defineProps({
33
+ working: {
34
+ type: Boolean
35
+ },
36
+ loading: {
37
+ type: Boolean
38
+ }
39
+ })
40
+
41
+ console.log("SETUP PAGE!!!")
42
+
43
+ import { computed } from 'vue'
44
+ import { useRoute } from 'vue-router'
45
+ const route = useRoute()
46
+
47
+ const pageType = computed(() => route.meta.pageType ?? 'simple' )
48
+
49
+ </script>
package/ViewRoot.vue ADDED
@@ -0,0 +1,79 @@
1
+ <template>
2
+ <router-view v-slot="{ route, Component }">
3
+ <template v-if="route?.meta?.raw">
4
+ <suspense>
5
+ <component :is="Component" />
6
+ </suspense>
7
+ </template>
8
+ <loading-zone v-else suspense @isLoading="l => loading = l">
9
+ <template v-slot:loading>
10
+ <div class="fixed w-full h-full flex align-items-center justify-content-center top-0 left-0">
11
+ <ProgressSpinner animationDuration=".5s"/>
12
+ </div>
13
+ </template>
14
+ <template v-slot:default="{ isLoading }">
15
+ <page :loading="loading" :working="working">
16
+ <template #navbar>
17
+ <slot name="navbar"></slot>
18
+ </template>
19
+ <working-zone @isWorking="w => working = w">
20
+ <template v-slot:working>
21
+ <div class="fixed w-full h-full flex align-items-center justify-content-center top-0 left-0">
22
+ <ProgressSpinner animationDuration=".5s"/>
23
+ </div>
24
+ </template>
25
+ <template v-slot:default="{ isWorking }">
26
+ <component :is="Component"
27
+ :style="isWorking || isLoading ? 'filter: blur(4px)' : ''"
28
+ class="working-blur" />
29
+ </template>
30
+ </working-zone>
31
+ </page>
32
+ </template>
33
+ </loading-zone>
34
+ </router-view>
35
+ </template>
36
+
37
+ <script setup>
38
+
39
+ import 'primevue/resources/primevue.min.css'
40
+ import 'primeflex/primeflex.css'
41
+ import 'primeicons/primeicons.css'
42
+
43
+ import ProgressSpinner from 'primevue/progressspinner'
44
+
45
+ import { useMeta } from 'vue-meta'
46
+ import Page from "./Page.vue"
47
+
48
+ const { meta } = useMeta({
49
+ meta: [
50
+ { charset: 'utf-8' },
51
+ { name: 'viewport',
52
+ content: "user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1," +
53
+ " width=device-width, viewport-fit=cover" }
54
+ ]
55
+ })
56
+
57
+ import { ref } from 'vue'
58
+ const working = ref(false)
59
+ const loading = ref(false)
60
+
61
+ </script>
62
+
63
+ <style>
64
+
65
+ body {
66
+ margin: 0;
67
+ height: 100%;
68
+ overflow-x: hidden;
69
+ overflow-y: auto;
70
+ background-color: var(--surface-a);
71
+ font-family: var(--font-family);
72
+ font-weight: 400;
73
+ color: var(--text-color);
74
+ }
75
+ .working-blur {
76
+ transition: filter 0.3s;
77
+ }
78
+
79
+ </style>
@@ -0,0 +1,32 @@
1
+ import { clientApi } from '@live-change/vue3-ssr/clientApi.js'
2
+
3
+ import {
4
+ createSharedElementDirective,
5
+ SharedElementRouteGuard
6
+ } from 'v-shared-element'
7
+
8
+ import { createApp } from "./main.js"
9
+
10
+ export function clientEntry(App, createRouter) {
11
+
12
+ const windowId = window.__WINDOW_ID__
13
+ console.error("CLIENT WINDOW ID", windowId)
14
+
15
+ window.api = clientApi({
16
+ use: [],
17
+ windowId
18
+ })
19
+
20
+ const { app, router } = createApp(api, App, createRouter)
21
+
22
+ app.use(createSharedElementDirective())
23
+ router.beforeEach(SharedElementRouteGuard)
24
+ window.process = window.process || null
25
+
26
+ // wait until router is ready before mounting to ensure hydration match
27
+ router.isReady().then(() => {
28
+ const instance = app.mount('#app', true)
29
+ app._container._vnode = instance.$.vnode
30
+ })
31
+
32
+ }
package/index.js ADDED
@@ -0,0 +1,3 @@
1
+ import ComponentDialog from "./ComponentDialog.vue"
2
+
3
+ export { ComponentDialog }
package/main.js ADDED
@@ -0,0 +1,62 @@
1
+ import { createSSRApp } from 'vue'
2
+ import { createMetaManager } from 'vue-meta'
3
+
4
+ import { registerComponents } from '@live-change/vue3-components'
5
+ import ReactiveDaoVue from '@live-change/dao-vue3'
6
+
7
+ import PrimeVue from 'primevue/config'
8
+ import ConfirmationService from 'primevue/confirmationservice'
9
+ import { PrimeVueConfirmSymbol } from 'primevue/useconfirm'
10
+ import ToastService from 'primevue/toastservice'
11
+ import { PrimeVueToastSymbol } from 'primevue/usetoast'
12
+ import DialogService from 'primevue/dialogservice'
13
+ import { PrimeVueDialogSymbol } from 'primevue/usedialog'
14
+ import StyleClass from 'primevue/styleclass'
15
+ import Ripple from 'primevue/ripple'
16
+ import BadgeDirective from 'primevue/badgedirective'
17
+
18
+ import emailValidator from "@live-change/email-service/clientEmailValidator.js"
19
+ import passwordValidator from "@live-change/password-authentication-service/clientPasswordValidator.js"
20
+
21
+ // SSR requires a fresh app instance per request, therefore we export a function
22
+ // that creates a fresh app instance. If using Vuex, we'd also be creating a
23
+ // fresh store here.
24
+ export function createApp(api, App, createRouter) {
25
+ api.validators.email = emailValidator
26
+ api.validators.password = passwordValidator
27
+
28
+ const app = createSSRApp(App)
29
+ app.config.devtools = true
30
+
31
+ api.installInstanceProperties(app.config.globalProperties)
32
+
33
+ registerComponents(app)
34
+ app.use(ReactiveDaoVue, { dao: api })
35
+
36
+ const router = createRouter(app)
37
+ app.use(router)
38
+
39
+ app.use(PrimeVue, {
40
+ ripple: true
41
+ })
42
+
43
+ app.use(ConfirmationService)
44
+ app.provide(PrimeVueConfirmSymbol, app.config.globalProperties.$confirm)
45
+
46
+ app.use(ToastService)
47
+ app.provide(PrimeVueToastSymbol, app.config.globalProperties.$toast)
48
+
49
+ app.use(DialogService)
50
+ app.provide(PrimeVueDialogSymbol, app.config.globalProperties.$dialog)
51
+
52
+ app.directive('styleclass', StyleClass)
53
+ app.directive('ripple', Ripple)
54
+ app.directive('badge', BadgeDirective)
55
+
56
+ const meta = createMetaManager({
57
+ isSSR: import.meta.env.SSR
58
+ })
59
+ app.use(meta)
60
+
61
+ return { app, router }
62
+ }
package/package.json ADDED
@@ -0,0 +1,73 @@
1
+ {
2
+ "name": "@live-change/frontend-base",
3
+ "version": "0.0.3",
4
+ "scripts": {
5
+ "memDev": "lcli memDev --enableSessions --initScript ./init.js --templatePath ../../base-frontend/index.html",
6
+ "localDevInit": "rm tmp.db; lcli localDev --enableSessions --initScript ./init.js",
7
+ "localDev": "lcli localDev --enableSessions",
8
+ "dev": "lcli dev --enableSessions",
9
+ "ssrDev": "lcli ssrDev --enableSessions",
10
+ "serveAllMem": "cross-env NODE_ENV=production lcli ssrServer --withApi --withServices --updateServices --enableSessions --withDb --dbBackend mem --createDb",
11
+ "serveAll": "cross-env NODE_ENV=production lcli ssrServer --withApi --withServices --updateServices --enableSessions",
12
+ "serve": "cross-env NODE_ENV=production lcli ssrServer --enableSessions",
13
+ "apiServer": "lcli apiServer --enableSessions",
14
+ "devApiServer": "lcli devApiServer --enableSessions",
15
+ "memApiServer": "lcli memApiServer --enableSessions",
16
+ "build": "cd front; yarn build:client && yarn build:server",
17
+ "build:client": "cd front; vite build --ssrManifest --outDir dist/client",
18
+ "build:server": "cd front; vite build --ssr src/entry-server.js --outDir dist/server",
19
+ "generate": "vite build --ssrManifest --outDir dist/static && yarn build:server && node prerender",
20
+ "debug": "node --inspect-brk server"
21
+ },
22
+ "dependencies": {
23
+ "@live-change/framework": "0.6.5",
24
+ "@live-change/cli": "0.6.5",
25
+ "@live-change/dao": "0.4.13",
26
+ "@live-change/dao-vue3": "0.4.13",
27
+ "@live-change/dao-websocket": "0.4.13",
28
+ "@live-change/vue3-ssr": "0.2.14",
29
+ "@live-change/vue3-components": "0.2.12",
30
+ "@live-change/session-service": "0.2.39",
31
+ "@live-change/user-service": "0.2.37",
32
+ "@live-change/password-authentication-service": "0.2.39",
33
+ "@live-change/secret-link-service": "0.2.39",
34
+ "@live-change/secret-code-service": "0.2.39",
35
+ "@live-change/security-frontend": "0.0.3",
36
+ "@vitejs/plugin-vue": "^2.3.1",
37
+ "@vitejs/plugin-vue-jsx": "^1.3.10",
38
+ "@vue/compiler-sfc": "^3.2.33",
39
+ "@vueuse/core": "^8.3.1",
40
+ "v-shared-element": "3.1.0",
41
+ "vue3-scroll-border": "0.1.2",
42
+ "codeceptjs-assert": "^0.0.5",
43
+ "compression": "^1.7.4",
44
+ "cross-env": "^7.0.3",
45
+ "get-port-sync": "1.0.1",
46
+ "primeicons": "^5.0.0",
47
+ "primevue": "^3.15.0",
48
+ "primeflex": "^3.2.1",
49
+ "rollup-plugin-node-builtins": "^2.1.2",
50
+ "serialize-javascript": "^6.0.0",
51
+ "serve-static": "^1.15.0",
52
+ "vite": "^2.9.6",
53
+ "vue": "^3.2.33",
54
+ "vue-meta": "^3.0.0-alpha.9",
55
+ "vue-router": "^4.0.14",
56
+ "vite-plugin-compression": "0.5.1",
57
+ "vite-plugin-vue-images": "^0.6.1",
58
+ "rollup-plugin-visualizer": "5.6.0"
59
+ },
60
+ "devDependencies": {
61
+ "@live-change/codeceptjs-helper": "0.6.5",
62
+ "@wdio/selenium-standalone-service": "^7.19.5",
63
+ "codeceptjs": "^3.3.1",
64
+ "playwright": "^1.21.1",
65
+ "random-profile-generator": "^2.3.0",
66
+ "txtgen": "^3.0.1",
67
+ "generate-password": "1.7.0",
68
+ "webdriverio": "^7.19.5"
69
+ },
70
+ "author": "",
71
+ "license": "ISC",
72
+ "description": ""
73
+ }
@@ -0,0 +1,64 @@
1
+ import { renderToString } from 'vue/server-renderer'
2
+ import { renderMetaToString } from 'vue-meta/ssr'
3
+
4
+ import { serverApi } from '@live-change/vue3-ssr/serverApi.js'
5
+
6
+ import { createApp } from "./main.js"
7
+
8
+ function escapeHtml(unsafe) {
9
+ return unsafe
10
+ .replace(/&/g, "&amp;")
11
+ .replace(/</g, "&lt;")
12
+ .replace(/>/g, "&gt;")
13
+ .replace(/"/g, "&quot;")
14
+ .replace(/'/g, "&#039;");
15
+ }
16
+
17
+ export function serverEntry(App, createRouter) {
18
+ return async function({ url, dao, windowId }) {
19
+ const api = await serverApi(dao, {
20
+ use: [],
21
+ windowId
22
+ })
23
+
24
+ const { app, router } = createApp(api, App, createRouter)
25
+
26
+ app.directive('shared-element', {})
27
+
28
+ // set the router to the desired URL before rendering
29
+ router.push(url)
30
+ await router.isReady()
31
+
32
+ // prefetch data
33
+ await api.preFetchRoute(router.currentRoute, router)
34
+
35
+ // passing SSR context object which will be available via useSSRContext()
36
+ // @vitejs/plugin-vue injects code into a component's setup() that registers
37
+ // itself on ctx.modules. After the render, ctx.modules would contain all the
38
+ // components that have been instantiated during this render call.
39
+ const ctx = {}
40
+ const html = await renderToString(app, ctx)
41
+ await renderMetaToString(app, ctx)
42
+
43
+ const data = api.prerenderCache.cacheData()
44
+ //console.log("PRERENDER CACHE", Array.from(api.prerenderCache.cache.keys()))
45
+ //console.log("PRERENDER CACHE EXTENDED", Array.from(api.prerenderCache.extendedCache.keys()))
46
+
47
+ // the SSR manifest generated by Vite contains module -> chunk/asset mapping
48
+ // which we can then use to determine what files need to be preloaded for this
49
+ // request.
50
+
51
+ const metaManager = app.config.globalProperties.$metaManager
52
+ const activeMeta = metaManager.target.context.active
53
+ //console.log("ACTIVE META", activeMeta)
54
+ //console.log("TELEPORTS", ctx.teleports)
55
+ ctx.teleports.head = [
56
+ ...(activeMeta.title ? [`<title data-vm-ssr="true">${escapeHtml(activeMeta.title)}</title>`] : []),
57
+ ...((activeMeta.meta || []).map(meta => `<meta ${Object.keys(meta).map(
58
+ key => `${escapeHtml(key)}="${escapeHtml(meta[key])}"`
59
+ ).join(' ')}>`))
60
+ ].join('\n')
61
+
62
+ return {html, data, meta: ctx.teleports, modules: ctx.modules}
63
+ }
64
+ }
package/vite-config.js ADDED
@@ -0,0 +1,108 @@
1
+ const { findFreePorts } = require("find-free-ports")
2
+ const path = require('path')
3
+ const vuePlugin = require('@vitejs/plugin-vue')
4
+ const { visualizer } = require('rollup-plugin-visualizer')
5
+ const viteImages = require('vite-plugin-vue-images')
6
+ const viteCompression = require('vite-plugin-compression')
7
+ const { searchForWorkspaceRoot } = require('vite')
8
+
9
+ const ssrTransformCustomDir = () => {
10
+ return {
11
+ props: [],
12
+ needRuntime: true
13
+ }
14
+ }
15
+
16
+ module.exports = async ({ command, mode }) => ({
17
+ define: {
18
+ ENV_BASE_HREF: JSON.stringify(process.env.BASE_HREF || 'http://localhost:8001')
19
+ },
20
+ server: {
21
+ hmr: {
22
+ port: (await findFreePorts())[0]
23
+ },
24
+ fs: {
25
+ allow: [
26
+ searchForWorkspaceRoot(process.cwd()),
27
+ path.dirname(require.resolve('primeicons/package.json')),
28
+ path.dirname(require.resolve('@fortawesome/fontawesome-free/package.json'))
29
+ ]
30
+ }
31
+ },
32
+ plugins: [
33
+ vuePlugin({
34
+ template: {
35
+ compilerOptions: {
36
+ // whitespace: "preserve",
37
+ directiveTransforms: {
38
+ 'ripple': ssrTransformCustomDir,
39
+ 'styleclass': ssrTransformCustomDir,
40
+ 'badge': ssrTransformCustomDir,
41
+ 'shared-element': ssrTransformCustomDir
42
+ }
43
+ }
44
+ },
45
+ }),
46
+ viteImages({ extensions: ['jpg', 'jpeg', 'png', 'svg', 'webp'] }),
47
+ viteCompression({ algorithm: 'brotliCompress', ext: '.br' }),
48
+ viteCompression({ algorithm: 'gzip', ext: '.gz' }),
49
+ viteCompression({ algorithm: 'deflate', ext: '.zz' }),
50
+ visualizer({
51
+ filename: '../stats.html'
52
+ }),
53
+ ],
54
+ build: {
55
+ minify: false,
56
+ commonjsOptions: {
57
+ transformMixedEsModules: true,
58
+ include: [
59
+ /node_modules/,
60
+ /live-change-framework\/framework\//,
61
+ /live-change-framework\/uid\//,
62
+ /live-change-dao\/dao\//,
63
+ /live-change-dao\/dao-sockjs\//,
64
+ /live-change-dao\/dao-websocket\//,
65
+ ]
66
+ },
67
+ },
68
+ ssr: {
69
+ external: [
70
+ '@live-change/dao',
71
+ '@live-change/uid',
72
+ '@live-change/framework',
73
+ '@live-change/framework/lib/utils/validators.js',
74
+ 'debug',
75
+ 'vite'
76
+ ],
77
+ noExternal: [
78
+ 'vue-meta',
79
+ '@live-change/vue3-components',
80
+ '@live-change/dao-vue3',
81
+ '@live-change/vue3-ssr',
82
+ '@live-change/email-service',
83
+ '@live-change/password-authentication-service',
84
+ '@live-change/db-admin',
85
+ 'vue3-scroll-border',
86
+ ]
87
+ },
88
+ optimizeDeps: {
89
+ include: [
90
+ '@live-change/dao',
91
+ '@live-change/dao-sockjs',
92
+ '@live-change/dao-websocket',
93
+ '@live-change/uid',
94
+ '@live-change/framework',
95
+ '@live-change/framework/lib/utils/validators.js',
96
+ 'debug'
97
+ ]
98
+ },
99
+
100
+ resolve: {
101
+ alias: [
102
+ { find: 'debug', replacement: 'debug/src/browser.js' },
103
+ { find: 'universal-websocket-client', replacement: 'universal-websocket-client/browser.js' },
104
+ { find: 'sockjs-client', replacement: 'sockjs-client/dist/sockjs.min.js' }
105
+ ],
106
+ }
107
+ })
108
+