mirror of
https://github.com/IT4Change/Ocelot-Social.git
synced 2026-02-06 09:55:50 +00:00
Merge pull request #13 from IT4Change/boilerplate-function-layout
feat(frontend): boilerplate function & layout
This commit is contained in:
commit
55604e5907
3
.env.dist
Normal file
3
.env.dist
Normal file
@ -0,0 +1,3 @@
|
||||
# META
|
||||
PUBLIC_ENV__META__DEFAULT_TITLE="IT4C"
|
||||
PUBLIC_ENV__META__DEFAULT_DESCRIPTION="IT4C Frontend Boilerplate"
|
||||
@ -72,7 +72,7 @@
|
||||
"import/no-relative-packages": "error",
|
||||
"import/no-relative-parent-imports": [
|
||||
"error",
|
||||
{ "ignore": ["#[src,root,components,pages,assets,plugins,context,types]/*"] }
|
||||
{ "ignore": ["#[src,root,components,pages,assets,layouts,stores,plugins,context,types]/*"] }
|
||||
],
|
||||
"import/no-self-import": "error",
|
||||
"import/no-unresolved": "error",
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@ -5,4 +5,5 @@ coverage/
|
||||
!.vuepress/
|
||||
.vuepress/.temp/
|
||||
.vuepress/.cache/
|
||||
build-storybook.log
|
||||
build-storybook.log
|
||||
.env
|
||||
@ -26,7 +26,7 @@ The project uses `vite` as builder, `vike` to do the SSR. The design framework i
|
||||
|
||||
Testing is done with `vitest` and code style is enforced with `eslint`, `remark-cli` and `stylelint`.
|
||||
|
||||
This projects utilizes `storybook` to develop frontend components and `vuepress` for static documentation generation.
|
||||
This projects utilizes `storybook` and `chromatic` to develop, document & test frontend components and `vuepress` for static documentation generation.
|
||||
|
||||
## Commands
|
||||
|
||||
@ -97,8 +97,8 @@ The following endpoints are provided given the right command is executed or all
|
||||
|
||||
## TODO
|
||||
|
||||
- [ ] figma
|
||||
- [ ] feature zähler -> pinia tore
|
||||
- [ ] tests
|
||||
- [ ] stories
|
||||
|
||||
## Known Problems
|
||||
|
||||
|
||||
9
package-lock.json
generated
9
package-lock.json
generated
@ -21,6 +21,7 @@
|
||||
"cross-env": "^7.0.3",
|
||||
"express": "^4.18.2",
|
||||
"pinia": "^2.1.7",
|
||||
"pinia-plugin-persistedstate": "^3.2.0",
|
||||
"sass": "^1.69.5",
|
||||
"sass-loader": "^13.3.2",
|
||||
"sirv": "^2.0.3",
|
||||
@ -22520,6 +22521,14 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/pinia-plugin-persistedstate": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/pinia-plugin-persistedstate/-/pinia-plugin-persistedstate-3.2.0.tgz",
|
||||
"integrity": "sha512-tZbNGf2vjAQcIm7alK40sE51Qu/m9oWr+rEgNm/2AWr1huFxj72CjvpQcIQzMknDBJEkQznCLAGtJTIcLKrKdw==",
|
||||
"peerDependencies": {
|
||||
"pinia": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/pinia/node_modules/vue-demi": {
|
||||
"version": "0.14.6",
|
||||
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.6.tgz",
|
||||
|
||||
21
package.json
21
package.json
@ -9,20 +9,22 @@
|
||||
"url": "git+https://github.com/IT4Change/boilerplate-frontend.git"
|
||||
},
|
||||
"keywords": [
|
||||
"npm",
|
||||
"nodejs",
|
||||
"vite",
|
||||
"vike",
|
||||
"npm",
|
||||
"docker",
|
||||
"jq",
|
||||
"vue",
|
||||
"vike",
|
||||
"vuetify",
|
||||
"pinia",
|
||||
"storybook",
|
||||
"eslint",
|
||||
"vitest",
|
||||
"vue-i18n",
|
||||
"eslint",
|
||||
"remark-cli",
|
||||
"stylelint",
|
||||
"vitest",
|
||||
"storybook",
|
||||
"vuepress",
|
||||
"docker",
|
||||
"remark-cli"
|
||||
"chromatic"
|
||||
],
|
||||
"author": {
|
||||
"name": "Ulf Gebhardt"
|
||||
@ -67,6 +69,7 @@
|
||||
"cross-env": "^7.0.3",
|
||||
"express": "^4.18.2",
|
||||
"pinia": "^2.1.7",
|
||||
"pinia-plugin-persistedstate": "^3.2.0",
|
||||
"sass": "^1.69.5",
|
||||
"sass-loader": "^13.3.2",
|
||||
"sirv": "^2.0.3",
|
||||
@ -133,6 +136,8 @@
|
||||
"#components/*": "./src/components/*",
|
||||
"#pages/*": "./src/pages/*",
|
||||
"#assets/*": "./src/assets/*",
|
||||
"#layouts/*": "./src/layouts/*",
|
||||
"#stores/*": "./src/stores/*",
|
||||
"#plugins/*": "./renderer/plugins/*",
|
||||
"#context/*": "./renderer/context/*",
|
||||
"#types/*": "./types/*"
|
||||
|
||||
@ -1,17 +1,30 @@
|
||||
import { createApp } from './app'
|
||||
|
||||
import type { PageContextClient } from '#types/PageContext'
|
||||
import type { PageContext, VikePageContext } from '#types/PageContext'
|
||||
|
||||
// This render() hook only supports SSR, see https://vike.dev/render-modes for how to modify render() to support SPA
|
||||
async function render(pageContext: PageContextClient) {
|
||||
const { Page, pageProps } = pageContext
|
||||
if (!Page) throw new Error('Client-side render() hook expects pageContext.Page to be defined')
|
||||
const app = createApp(Page, pageProps, pageContext)
|
||||
app.mount('#app')
|
||||
let app: ReturnType<typeof createApp>
|
||||
async function render(pageContext: VikePageContext & PageContext) {
|
||||
if (!app) {
|
||||
app = createApp(pageContext)
|
||||
app.mount('#app')
|
||||
} else {
|
||||
app.changePage(pageContext)
|
||||
}
|
||||
}
|
||||
|
||||
/* To enable Client-side Routing:
|
||||
export const clientRouting = true
|
||||
// !! WARNING !! Before doing so, read https://vike.dev/clientRouting */
|
||||
function onHydrationEnd() {
|
||||
// console.log('Hydration finished; page is now interactive.')
|
||||
}
|
||||
function onPageTransitionStart() {
|
||||
// console.log('Page transition start')
|
||||
}
|
||||
function onPageTransitionEnd() {
|
||||
// console.log('Page transition end')
|
||||
}
|
||||
|
||||
export const clientRouting = true
|
||||
export const prefetchStaticAssets = 'viewport'
|
||||
export { render }
|
||||
export { onHydrationEnd }
|
||||
export { onPageTransitionStart }
|
||||
export { onPageTransitionEnd }
|
||||
|
||||
@ -2,27 +2,25 @@ import { renderToString as renderToString_ } from '@vue/server-renderer'
|
||||
import { escapeInject, dangerouslySkipEscape } from 'vike/server'
|
||||
|
||||
import logoUrl from '#assets/favicon.ico'
|
||||
import { META } from '#src/env'
|
||||
|
||||
import { createApp } from './app'
|
||||
|
||||
import type { PageContextServer } from '#types/PageContext'
|
||||
import type { PageContextServer, PageContext } from '#types/PageContext'
|
||||
import type { App } from 'vue'
|
||||
|
||||
// See https://vike.dev/data-fetching
|
||||
export const passToClient = ['pageProps', 'urlPathname']
|
||||
export const passToClient = ['pageProps', /* 'urlPathname', */ 'routeParams']
|
||||
|
||||
async function render(pageContext: PageContextServer) {
|
||||
const { Page, pageProps } = pageContext
|
||||
// This render() hook only supports SSR, see https://vike.dev/render-modes for how to modify render() to support SPA
|
||||
if (!Page) throw new Error('My render() hook expects pageContext.Page to be defined')
|
||||
const app = createApp(Page, pageProps, pageContext)
|
||||
async function render(pageContext: PageContextServer & PageContext) {
|
||||
const app = createApp(pageContext, false)
|
||||
|
||||
const appHtml = await renderToString(app)
|
||||
|
||||
// See https://vike.dev/head
|
||||
const { documentProps } = pageContext.exports
|
||||
const title = (documentProps && documentProps.title) || 'Vite SSR app'
|
||||
const desc = (documentProps && documentProps.description) || 'App using Vite + Vike'
|
||||
const title = (documentProps && documentProps.title) || META.DEFAULT_TITLE
|
||||
const desc = (documentProps && documentProps.description) || META.DEFAULT_DESCRIPTION
|
||||
|
||||
const documentHtml = escapeInject`<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
@ -1,39 +1,75 @@
|
||||
import { createSSRApp, defineComponent, h } from 'vue'
|
||||
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
|
||||
import { createSSRApp, defineComponent, h, markRaw, reactive } from 'vue'
|
||||
|
||||
import PageShell from '#components/PageShell.vue'
|
||||
import { setPageContext } from '#context/usePageContext'
|
||||
import i18n from '#plugins/i18n'
|
||||
import pinia from '#plugins/pinia'
|
||||
import CreateVuetify from '#plugins/vuetify'
|
||||
import { Page } from '#types/Page'
|
||||
import { PageProps } from '#types/PageProps'
|
||||
|
||||
import type { PageContext } from '#types/PageContext'
|
||||
import type { Component } from '#types/Component'
|
||||
import type { PageContext, VikePageContext } from '#types/PageContext'
|
||||
|
||||
function createApp(Page: Page, pageProps: PageProps | undefined, pageContext: PageContext) {
|
||||
const PageWithLayout = defineComponent({
|
||||
const vuetify = CreateVuetify(i18n)
|
||||
|
||||
function createApp(pageContext: VikePageContext & PageContext, isClient = true) {
|
||||
let rootComponent: Component
|
||||
const PageWithWrapper = defineComponent({
|
||||
data: () => ({
|
||||
Page: markRaw(pageContext.Page),
|
||||
pageProps: markRaw(pageContext.pageProps || {}),
|
||||
isClient,
|
||||
}),
|
||||
created() {
|
||||
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||
rootComponent = this
|
||||
},
|
||||
render() {
|
||||
return h(
|
||||
PageShell,
|
||||
{},
|
||||
{
|
||||
default() {
|
||||
return h(Page, pageProps || {})
|
||||
default: () => {
|
||||
return h(this.Page, this.pageProps)
|
||||
},
|
||||
},
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
const app = createSSRApp(PageWithLayout)
|
||||
if (isClient) {
|
||||
pinia.use(piniaPluginPersistedstate)
|
||||
}
|
||||
|
||||
const app = createSSRApp(PageWithWrapper)
|
||||
app.use(pinia)
|
||||
app.use(i18n)
|
||||
app.use(CreateVuetify(i18n))
|
||||
app.use(vuetify)
|
||||
|
||||
objectAssign(app, {
|
||||
changePage: (pageContext: VikePageContext & PageContext) => {
|
||||
Object.assign(pageContextReactive, pageContext)
|
||||
rootComponent.Page = markRaw(pageContext.Page)
|
||||
rootComponent.pageProps = markRaw(pageContext.pageProps || {})
|
||||
},
|
||||
})
|
||||
|
||||
// When doing Client Routing, we mutate pageContext (see usage of `app.changePage()` in `_default.page.client.js`).
|
||||
// We therefore use a reactive pageContext.
|
||||
const pageContextReactive = reactive(pageContext)
|
||||
|
||||
// Make pageContext available from any Vue component
|
||||
setPageContext(app, pageContext)
|
||||
setPageContext(app, pageContextReactive)
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
// Same as `Object.assign()` but with type inference
|
||||
function objectAssign<Obj extends object, ObjAddendum>(
|
||||
obj: Obj,
|
||||
objAddendum: ObjAddendum,
|
||||
): asserts obj is Obj & ObjAddendum {
|
||||
Object.assign(obj, objAddendum)
|
||||
}
|
||||
|
||||
export { createApp }
|
||||
|
||||
@ -3,11 +3,11 @@
|
||||
|
||||
import { inject } from 'vue'
|
||||
|
||||
import { PageContext } from '#types/PageContext'
|
||||
import { PageContext, VikePageContext } from '#types/PageContext'
|
||||
|
||||
import type { App, InjectionKey } from 'vue'
|
||||
|
||||
const key: InjectionKey<PageContext> = Symbol(undefined)
|
||||
const key: InjectionKey<VikePageContext & PageContext> = Symbol(undefined)
|
||||
|
||||
function usePageContext() {
|
||||
const pageContext = inject(key)
|
||||
@ -15,7 +15,7 @@ function usePageContext() {
|
||||
return pageContext
|
||||
}
|
||||
|
||||
function setPageContext(app: App, pageContext: PageContext) {
|
||||
function setPageContext(app: App, pageContext: VikePageContext & PageContext) {
|
||||
app.provide(key, pageContext)
|
||||
}
|
||||
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { createPinia } from 'pinia'
|
||||
|
||||
export default createPinia()
|
||||
const pinia = createPinia()
|
||||
export default pinia
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { mount, config } from '@vue/test-utils'
|
||||
import { describe, it, expect } from 'vitest'
|
||||
|
||||
import ClickCounter from './ClickCounter.vue'
|
||||
import ClickCounter from './ClickCounter.delete.vue'
|
||||
|
||||
describe('clickCounter', () => {
|
||||
const wrapper = mount(ClickCounter)
|
||||
13
src/components/ClientOnly.vue
Normal file
13
src/components/ClientOnly.vue
Normal file
@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<template v-if="isMounted"><slot /></template>
|
||||
<template v-else><slot name="placeholder" /></template>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
|
||||
const isMounted = ref(false)
|
||||
onMounted(() => {
|
||||
isMounted.value = true
|
||||
})
|
||||
</script>
|
||||
@ -1,66 +1,5 @@
|
||||
<template>
|
||||
<v-app>
|
||||
<v-container>
|
||||
<div class="layout">
|
||||
<div class="navigation">
|
||||
<a href="/" class="logo">
|
||||
<img :src="Logo" height="64" width="64" alt="logo" />
|
||||
</a>
|
||||
<VikeLink href="/">Home</VikeLink>
|
||||
<VikeLink href="/about">About</VikeLink>
|
||||
</div>
|
||||
<div class="content"><slot /></div>
|
||||
</div>
|
||||
</v-container>
|
||||
<slot />
|
||||
</v-app>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import Logo from '#assets/it4c-logo2-clean-bg_alpha-128x128.png'
|
||||
|
||||
import VikeLink from './VikeLink.vue'
|
||||
</script>
|
||||
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style scoped>
|
||||
.layout {
|
||||
display: flex;
|
||||
max-width: 900px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.content {
|
||||
min-height: 100vh;
|
||||
padding: 20px;
|
||||
padding-bottom: 50px;
|
||||
border-left: 2px solid #eee;
|
||||
}
|
||||
|
||||
.navigation {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-shrink: 0;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
line-height: 1.8em;
|
||||
}
|
||||
|
||||
.logo {
|
||||
margin-top: 20px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
</style>
|
||||
|
||||
17
src/components/VikeBtn.vue
Normal file
17
src/components/VikeBtn.vue
Normal file
@ -0,0 +1,17 @@
|
||||
<template>
|
||||
<v-btn :variant="isRouteSelected($attrs.href as string) ? 'tonal' : 'flat'">
|
||||
<slot />
|
||||
</v-btn>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { usePageContext } from '#context/usePageContext'
|
||||
|
||||
const pageContext = usePageContext()
|
||||
|
||||
const isRouteSelected = (href: string) => {
|
||||
if (href === '/app') {
|
||||
return pageContext.urlPathname.indexOf(href) === 0
|
||||
}
|
||||
return pageContext.urlPathname === href
|
||||
}
|
||||
</script>
|
||||
@ -1,19 +0,0 @@
|
||||
<template>
|
||||
<a :class="{ active: pageContext.urlPathname === $attrs.href }">
|
||||
<slot />
|
||||
</a>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { usePageContext } from '#context/usePageContext'
|
||||
|
||||
const pageContext = usePageContext()
|
||||
</script>
|
||||
<style scoped>
|
||||
a {
|
||||
padding: 3px 10px;
|
||||
}
|
||||
|
||||
a.active {
|
||||
background-color: #eee;
|
||||
}
|
||||
</style>
|
||||
7
src/components/menu/LogoAvatar.vue
Normal file
7
src/components/menu/LogoAvatar.vue
Normal file
@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<v-avatar color="#333" class="ma-2 pa-1" :image="Logo" size="48" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import Logo from '#assets/it4c-logo2-clean-bg_alpha-128x128.png'
|
||||
</script>
|
||||
16
src/components/menu/TopMenu.vue
Normal file
16
src/components/menu/TopMenu.vue
Normal file
@ -0,0 +1,16 @@
|
||||
<template>
|
||||
<v-app-bar flat>
|
||||
<LogoAvatar />
|
||||
<v-container class="mx-auto d-flex align-center justify-center">
|
||||
<VikeBtn href="/">Home</VikeBtn>
|
||||
<VikeBtn href="/app">App</VikeBtn>
|
||||
<VikeBtn href="/about">About</VikeBtn>
|
||||
</v-container>
|
||||
</v-app-bar>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import VikeBtn from '#components/VikeBtn.vue'
|
||||
|
||||
import LogoAvatar from './LogoAvatar.vue'
|
||||
</script>
|
||||
7
src/env.ts
Normal file
7
src/env.ts
Normal file
@ -0,0 +1,7 @@
|
||||
const META = {
|
||||
DEFAULT_TITLE: import.meta.env.PUBLIC_ENV__META__DEFAULT_TITLE ?? 'IT4C',
|
||||
DEFAULT_DESCRIPTION:
|
||||
import.meta.env.PUBLIC_ENV__META__DEFAULT_DESCRIPTION ?? 'IT4C Frontend Boilerplate',
|
||||
}
|
||||
|
||||
export { META }
|
||||
28
src/layouts/DefaultLayout.vue
Normal file
28
src/layouts/DefaultLayout.vue
Normal file
@ -0,0 +1,28 @@
|
||||
<template>
|
||||
<TopMenu />
|
||||
<v-main class="bg-grey-lighten-3">
|
||||
<v-container>
|
||||
<v-row>
|
||||
<v-col v-if="slots.sidemenu" cols="2">
|
||||
<v-sheet rounded>
|
||||
<slot name="sidemenu"></slot>
|
||||
</v-sheet>
|
||||
</v-col>
|
||||
|
||||
<v-col>
|
||||
<v-sheet rounded class="pa-3">
|
||||
<slot />
|
||||
</v-sheet>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</v-main>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useSlots } from 'vue'
|
||||
|
||||
import TopMenu from '#components/menu/TopMenu.vue'
|
||||
|
||||
const slots = useSlots()
|
||||
</script>
|
||||
@ -1,13 +1,20 @@
|
||||
<template>
|
||||
<h1>About</h1>
|
||||
<p>Example of app using Vike.</p>
|
||||
<DefaultLayout>
|
||||
<h1>About</h1>
|
||||
<p>
|
||||
To find out more about this boilerplate you can look at the sources:
|
||||
<a href="https://github.com/IT4Change/boilerplate-frontend/" target="_blank"
|
||||
>github.com/IT4Change/boilerplate-frontend</a
|
||||
>.
|
||||
</p>
|
||||
<br />
|
||||
<p>
|
||||
Want to get in touch? Find out how on our
|
||||
<a href="https://it4c.dev" target="_blank">website</a>
|
||||
</p>
|
||||
</DefaultLayout>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
code {
|
||||
padding: 3px 5px;
|
||||
font-family: monospace;
|
||||
background-color: #eaeaea;
|
||||
border-radius: 4px;
|
||||
}
|
||||
</style>
|
||||
<script lang="ts" setup>
|
||||
import DefaultLayout from '#layouts/DefaultLayout.vue'
|
||||
</script>
|
||||
|
||||
18
src/pages/app/index.page.route.ts
Normal file
18
src/pages/app/index.page.route.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { resolveRoute } from 'vike/routing'
|
||||
|
||||
import { PageContext } from '#types/PageContext'
|
||||
|
||||
export default (pageContext: PageContext) => {
|
||||
{
|
||||
const result = resolveRoute('/app', pageContext.urlPathname)
|
||||
if (result.match) {
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
const result = resolveRoute('/app/@page', pageContext.urlPathname)
|
||||
if (!['inc', 'reset'].includes(result.routeParams.page)) {
|
||||
return false
|
||||
}
|
||||
return result
|
||||
}
|
||||
13
src/pages/app/index.page.server.ts
Normal file
13
src/pages/app/index.page.server.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import type { PageContextBuiltInServer } from 'vike/types'
|
||||
|
||||
export { onBeforeRender }
|
||||
|
||||
async function onBeforeRender(pageContext: PageContextBuiltInServer) {
|
||||
const { page } = pageContext.routeParams
|
||||
const pageProps = { page }
|
||||
return {
|
||||
pageContext: {
|
||||
pageProps,
|
||||
},
|
||||
}
|
||||
}
|
||||
46
src/pages/app/index.page.vue
Normal file
46
src/pages/app/index.page.vue
Normal file
@ -0,0 +1,46 @@
|
||||
<template>
|
||||
<DefaultLayout>
|
||||
<template #sidemenu>
|
||||
<v-list rounded>
|
||||
<v-list-item link title="Value" :active="page === null" href="/app"></v-list-item>
|
||||
<v-list-item link title="Increase" :active="page === 'inc'" href="/app/inc"></v-list-item>
|
||||
|
||||
<v-divider class="my-2"></v-divider>
|
||||
<v-list-item link title="Reset" :active="page === 'reset'" href="/app/reset"></v-list-item>
|
||||
</v-list>
|
||||
</template>
|
||||
|
||||
<template #default>
|
||||
<div v-if="page === null">
|
||||
<h1>The Counter</h1>
|
||||
<p>
|
||||
The current value of the counter is:
|
||||
<ClientOnly
|
||||
><b>{{ counter.count }}</b></ClientOnly
|
||||
>
|
||||
</p>
|
||||
</div>
|
||||
<div v-else-if="page === 'inc'">
|
||||
<h1>Increase the Counter</h1>
|
||||
<ClientOnly>
|
||||
<v-btn elevation="2" @click="counter.increment()">{{ counter.count }}</v-btn>
|
||||
</ClientOnly>
|
||||
</div>
|
||||
<div v-else-if="page === 'reset'">
|
||||
<h1>Reset the Counter</h1>
|
||||
<ClientOnly>
|
||||
<v-btn elevation="2" @click="counter.reset()">{{ counter.count }}</v-btn>
|
||||
</ClientOnly>
|
||||
</div>
|
||||
</template>
|
||||
</DefaultLayout>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import ClientOnly from '#components/ClientOnly.vue'
|
||||
import DefaultLayout from '#layouts/DefaultLayout.vue'
|
||||
import { useCounterStore } from '#stores/counter'
|
||||
|
||||
const counter = useCounterStore()
|
||||
defineProps({ page: { type: String, default: null } })
|
||||
</script>
|
||||
@ -1,12 +1,19 @@
|
||||
<template>
|
||||
<h1>Welcome</h1>
|
||||
This page is:
|
||||
<ul>
|
||||
<li>Rendered to HTML.</li>
|
||||
<li>Interactive. <ClickCounter /></li>
|
||||
</ul>
|
||||
<DefaultLayout>
|
||||
<h1>IT4C Frontend Boilerplate</h1>
|
||||
<p>Welcome to this minimal starter for frontends.</p>
|
||||
<br />
|
||||
<p>This is just a basic example to demonstrate things - nothing fancy.</p>
|
||||
<br />
|
||||
<p>In the App Section you will find a counter example utilizing the local storage.</p>
|
||||
<br />
|
||||
<p>Happy Coding <v-icon icon="mdi-heart" color="red" /></p>
|
||||
<br />
|
||||
<p>Sincerly</p>
|
||||
<p>Your IT Team For Change</p>
|
||||
</DefaultLayout>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import ClickCounter from '#components/ClickCounter.vue'
|
||||
import DefaultLayout from '#layouts/DefaultLayout.vue'
|
||||
</script>
|
||||
|
||||
14
src/stores/counter.ts
Normal file
14
src/stores/counter.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { defineStore } from 'pinia'
|
||||
|
||||
export const useCounterStore = defineStore('counter', {
|
||||
state: () => ({ count: 0 }),
|
||||
actions: {
|
||||
increment() {
|
||||
this.count++
|
||||
},
|
||||
reset() {
|
||||
this.count = 0
|
||||
},
|
||||
},
|
||||
persist: true,
|
||||
})
|
||||
@ -16,6 +16,8 @@
|
||||
"#components/*": ["./src/components/*"],
|
||||
"#pages/*": ["./src/pages/*"],
|
||||
"#assets/*": ["./src/assets/*"],
|
||||
"#layouts/*": ["./src/layouts/*"],
|
||||
"#stores/*": ["./src/stores/*"],
|
||||
"#plugins/*": ["./renderer/plugins/*"],
|
||||
"#context/*": ["./renderer/context/*"],
|
||||
"#types/*": ["./types/*"]
|
||||
|
||||
2
types/Component.ts
Normal file
2
types/Component.ts
Normal file
@ -0,0 +1,2 @@
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export type Component = any
|
||||
@ -3,31 +3,31 @@ import { PageProps } from '#types/PageProps'
|
||||
|
||||
export type {
|
||||
PageContextServer,
|
||||
/*
|
||||
// When using Client Routing https://vike.dev/clientRouting
|
||||
PageContextClient,
|
||||
PageContext,
|
||||
/ */
|
||||
|
||||
// When using Client Routing https://vike.dev/clientRouting
|
||||
PageContextClient,
|
||||
PageContext as VikePageContext,
|
||||
PageContextBuiltInClientWithClientRouting as PageContextBuiltInClient,
|
||||
// When using Server Routing
|
||||
/*
|
||||
PageContextClientWithServerRouting as PageContextClient,
|
||||
PageContextWithServerRouting as PageContext,
|
||||
*/
|
||||
//* /
|
||||
} from 'vike/types'
|
||||
|
||||
// https://vike.dev/pageContext#typescript
|
||||
declare global {
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
namespace Vike {
|
||||
interface PageContext {
|
||||
Page: Page
|
||||
pageProps?: PageProps
|
||||
urlPathname: string
|
||||
exports: {
|
||||
documentProps?: {
|
||||
title?: string
|
||||
description?: string
|
||||
}
|
||||
}
|
||||
export type PageContext = {
|
||||
Page: Page
|
||||
pageProps?: PageProps
|
||||
urlPathname: string
|
||||
exports: {
|
||||
documentProps?: {
|
||||
title?: string
|
||||
description?: string
|
||||
}
|
||||
}
|
||||
documentProps?: {
|
||||
title: string
|
||||
description?: string
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,6 +27,8 @@ const config: UserConfig = {
|
||||
'#components': path.join(__dirname, '/src/components'),
|
||||
'#pages': path.join(__dirname, '/src/pages'),
|
||||
'#assets': path.join(__dirname, '/src/assets'),
|
||||
'#layouts': path.join(__dirname, '/src/layouts'),
|
||||
'#stores': path.join(__dirname, '/src/stores'),
|
||||
'#plugins': path.join(__dirname, '/renderer/plugins'),
|
||||
'#context': path.join(__dirname, '/renderer/context'),
|
||||
'#types': path.join(__dirname, '/types'),
|
||||
|
||||
@ -12,10 +12,10 @@ export default mergeConfig(
|
||||
coverage: {
|
||||
all: true,
|
||||
include: ['src/**/*.{js,jsx,ts,tsx,vue}'],
|
||||
lines: 2,
|
||||
lines: 1,
|
||||
functions: 0,
|
||||
branches: 8,
|
||||
statements: 2,
|
||||
branches: 4,
|
||||
statements: 1,
|
||||
// 100: true,
|
||||
},
|
||||
},
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user