feat: use nuxt 4 structure
This commit is contained in:
18 files changed
+7
-3
No files matched your search
+29
@@ -0,0 +1,29 @@
|
||||
<script setup lang="ts">
|
||||
import { appName } from '~/constants'
|
||||
|
||||
useHead({
|
||||
title: appName,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VitePwaManifest />
|
||||
<NuxtLayout>
|
||||
<NuxtPage />
|
||||
</NuxtLayout>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
html,
|
||||
body,
|
||||
#__nuxt {
|
||||
height: 100vh;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
html.dark {
|
||||
background: #222;
|
||||
color: white;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,17 @@
|
||||
<script setup lang='ts'>
|
||||
const { count, inc, dec } = useCount()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div inline-flex m="y-3">
|
||||
<button rounded-full p-2 btn @click="dec()">
|
||||
<div i-carbon-subtract />
|
||||
</button>
|
||||
<div font="mono" w="15" m-auto inline-block>
|
||||
{{ count }}
|
||||
</div>
|
||||
<button rounded-full p-2 btn @click="inc()">
|
||||
<div i-carbon-add />
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,21 @@
|
||||
<script setup lang="ts">
|
||||
const color = useColorMode()
|
||||
|
||||
useHead({
|
||||
meta: [{
|
||||
id: 'theme-color',
|
||||
name: 'theme-color',
|
||||
content: () => color.value === 'dark' ? '#222222' : '#ffffff',
|
||||
}],
|
||||
})
|
||||
|
||||
function toggleDark() {
|
||||
color.preference = color.value === 'dark' ? 'light' : 'dark'
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<button class="!outline-none" @click="toggleDark">
|
||||
<div class="i-carbon-sun dark:i-carbon-moon" />
|
||||
</button>
|
||||
</template>
|
||||
@@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<div text="xl gray4" m-5 flex="~ gap3" justify-center>
|
||||
<NuxtLink i-carbon-campsite to="/" />
|
||||
<a i-carbon-logo-github href="https://github.com/antfu/vitesse-nuxt3" target="_blank" />
|
||||
<DarkToggle />
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,34 @@
|
||||
<script setup lang="ts">
|
||||
const name = ref('')
|
||||
|
||||
const router = useRouter()
|
||||
function go() {
|
||||
if (name.value)
|
||||
router.push(`/hi/${encodeURIComponent(name.value)}`)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<input
|
||||
id="input"
|
||||
v-model="name"
|
||||
placeholder="What's your name?"
|
||||
type="text" autocomplete="off"
|
||||
p="x-4 y-2" m="t-5" w="250px"
|
||||
text="center" bg="transparent"
|
||||
border="~ rounded gray-200 dark:gray-700"
|
||||
outline="none active:none"
|
||||
@keydown.enter="go"
|
||||
>
|
||||
<div>
|
||||
<button
|
||||
m-3 text-sm btn
|
||||
:disabled="!name"
|
||||
@click="go"
|
||||
>
|
||||
GO
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,17 @@
|
||||
<template>
|
||||
<div inline-flex cursor-default text-2xl font-300>
|
||||
<div flex flex-col children:mx-auto>
|
||||
<img inline-block h-18 w-18 src="/nuxt.svg">
|
||||
<span mt--2 text-green5>Nuxt 3</span>
|
||||
</div>
|
||||
<div
|
||||
text="3xl gray4"
|
||||
m="x-4 y-auto"
|
||||
i-carbon-add transform transition-all-500 hover:rotate-135
|
||||
/>
|
||||
<div flex flex-col children:mx-auto>
|
||||
<img inline-block h-18 w-18 src="/vite.png">
|
||||
<span mt--2 text-purple5>Vitesse</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
const { data } = await useFetch('/api/pageview')
|
||||
|
||||
const time = useTimeAgo(() => data.value?.startAt || 0)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div text-gray:80>
|
||||
<span text-gray font-500>{{ data?.pageview }}</span>
|
||||
page views since
|
||||
<span text-gray>{{ time }}</span>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,16 @@
|
||||
export function useCount() {
|
||||
const count = useState('count', () => Math.round(Math.random() * 20))
|
||||
|
||||
function inc() {
|
||||
count.value += 1
|
||||
}
|
||||
function dec() {
|
||||
count.value -= 1
|
||||
}
|
||||
|
||||
return {
|
||||
count,
|
||||
inc,
|
||||
dec,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import { acceptHMRUpdate, defineStore } from 'pinia'
|
||||
|
||||
export const useUserStore = defineStore('user', () => {
|
||||
/**
|
||||
* Current named of the user.
|
||||
*/
|
||||
const savedName = ref('')
|
||||
const previousNames = ref(new Set<string>())
|
||||
|
||||
const usedNames = computed(() => Array.from(previousNames.value))
|
||||
const otherNames = computed(() => usedNames.value.filter(name => name !== savedName.value))
|
||||
|
||||
/**
|
||||
* Changes the current name of the user and saves the one that was used
|
||||
* before.
|
||||
*
|
||||
* @param name - new name to set
|
||||
*/
|
||||
function setNewName(name: string) {
|
||||
if (savedName.value)
|
||||
previousNames.value.add(savedName.value)
|
||||
|
||||
savedName.value = name
|
||||
}
|
||||
|
||||
return {
|
||||
setNewName,
|
||||
otherNames,
|
||||
savedName,
|
||||
}
|
||||
})
|
||||
|
||||
if (import.meta.hot)
|
||||
import.meta.hot.accept(acceptHMRUpdate(useUserStore, import.meta.hot))
|
||||
@@ -0,0 +1,79 @@
|
||||
import process from 'node:process'
|
||||
import type { ModuleOptions } from '@vite-pwa/nuxt'
|
||||
import { appDescription, appName } from '../constants/index'
|
||||
|
||||
const scope = '/'
|
||||
|
||||
export const pwa: ModuleOptions = {
|
||||
registerType: 'autoUpdate',
|
||||
scope,
|
||||
base: scope,
|
||||
manifest: {
|
||||
id: scope,
|
||||
scope,
|
||||
name: appName,
|
||||
short_name: appName,
|
||||
description: appDescription,
|
||||
theme_color: '#ffffff',
|
||||
icons: [
|
||||
{
|
||||
src: 'pwa-192x192.png',
|
||||
sizes: '192x192',
|
||||
type: 'image/png',
|
||||
},
|
||||
{
|
||||
src: 'pwa-512x512.png',
|
||||
sizes: '512x512',
|
||||
type: 'image/png',
|
||||
},
|
||||
{
|
||||
src: 'maskable-icon.png',
|
||||
sizes: '512x512',
|
||||
type: 'image/png',
|
||||
purpose: 'any maskable',
|
||||
},
|
||||
],
|
||||
},
|
||||
workbox: {
|
||||
globPatterns: ['**/*.{js,css,html,txt,png,ico,svg}'],
|
||||
navigateFallbackDenylist: [/^\/api\//],
|
||||
navigateFallback: '/',
|
||||
cleanupOutdatedCaches: true,
|
||||
runtimeCaching: [
|
||||
{
|
||||
urlPattern: /^https:\/\/fonts.googleapis.com\/.*/i,
|
||||
handler: 'CacheFirst',
|
||||
options: {
|
||||
cacheName: 'google-fonts-cache',
|
||||
expiration: {
|
||||
maxEntries: 10,
|
||||
maxAgeSeconds: 60 * 60 * 24 * 365, // <== 365 days
|
||||
},
|
||||
cacheableResponse: {
|
||||
statuses: [0, 200],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
urlPattern: /^https:\/\/fonts.gstatic.com\/.*/i,
|
||||
handler: 'CacheFirst',
|
||||
options: {
|
||||
cacheName: 'gstatic-fonts-cache',
|
||||
expiration: {
|
||||
maxEntries: 10,
|
||||
maxAgeSeconds: 60 * 60 * 24 * 365, // <== 365 days
|
||||
},
|
||||
cacheableResponse: {
|
||||
statuses: [0, 200],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
registerWebManifestInRouteRules: true,
|
||||
writePlugin: true,
|
||||
devOptions: {
|
||||
enabled: process.env.VITE_PLUGIN_PWA === 'true',
|
||||
navigateFallback: scope,
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
export const appName = 'Vitesse for Nuxt 3'
|
||||
export const appDescription = 'Vitesse for Nuxt 3'
|
||||
@@ -0,0 +1,15 @@
|
||||
## Layouts
|
||||
|
||||
Vue components in this dir are used as layouts.
|
||||
|
||||
By default, `default.vue` will be used unless an alternative is specified in the route meta.
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
layout: 'home',
|
||||
})
|
||||
</script>
|
||||
```
|
||||
|
||||
Learn more on https://nuxt.com/docs/guide/directory-structure/layouts
|
||||
@@ -0,0 +1,9 @@
|
||||
<template>
|
||||
<main class="px-10 py-20 text-center">
|
||||
<slot />
|
||||
<Footer />
|
||||
<div class="mx-auto mt-5 text-center text-sm opacity-25">
|
||||
[Default Layout]
|
||||
</div>
|
||||
</main>
|
||||
</template>
|
||||
@@ -0,0 +1,9 @@
|
||||
<template>
|
||||
<main class="px-10 py-20 text-center">
|
||||
<slot />
|
||||
<Footer />
|
||||
<div class="mx-auto mt-5 text-center text-sm opacity-25">
|
||||
[Home Layout]
|
||||
</div>
|
||||
</main>
|
||||
</template>
|
||||
@@ -0,0 +1,17 @@
|
||||
<script setup lang="ts">
|
||||
const router = useRouter()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<main p="x4 y10" text="center teal-700 dark:gray-200">
|
||||
<div text-4xl>
|
||||
<div i-carbon-warning inline-block />
|
||||
</div>
|
||||
<div>Not found</div>
|
||||
<div>
|
||||
<button text-sm btn m="3 t8" @click="router.back()">
|
||||
Back
|
||||
</button>
|
||||
</div>
|
||||
</main>
|
||||
</template>
|
||||
@@ -0,0 +1,49 @@
|
||||
<script setup lang="ts">
|
||||
const route = useRoute<'hi-id'>()
|
||||
const user = useUserStore()
|
||||
const name = route.params.id
|
||||
|
||||
watchEffect(() => {
|
||||
user.setNewName(route.params.id as string)
|
||||
})
|
||||
|
||||
definePageMeta({
|
||||
layout: 'home',
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div i-twemoji:waving-hand inline-block animate-shake-x animate-duration-5000 text-4xl />
|
||||
<h3 text-2xl font-500>
|
||||
Hi,
|
||||
</h3>
|
||||
<div text-xl>
|
||||
{{ name }}!
|
||||
</div>
|
||||
|
||||
<template v-if="user.otherNames.length">
|
||||
<p my-4 text-sm>
|
||||
<span op-50>Also as known as:</span>
|
||||
<ul>
|
||||
<li v-for="otherName in user.otherNames" :key="otherName">
|
||||
<router-link :to="`/hi/${otherName}`" replace>
|
||||
{{ otherName }}
|
||||
</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
</p>
|
||||
</template>
|
||||
|
||||
<Counter />
|
||||
|
||||
<div>
|
||||
<NuxtLink
|
||||
class="m-3 text-sm btn"
|
||||
to="/"
|
||||
>
|
||||
Back
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,27 @@
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
layout: 'home',
|
||||
})
|
||||
|
||||
const online = useOnline()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<Logos mb-6 />
|
||||
<Suspense>
|
||||
<ClientOnly>
|
||||
<PageView v-if="online" />
|
||||
<div v-else text-gray:80>
|
||||
You're offline
|
||||
</div>
|
||||
</ClientOnly>
|
||||
<template #fallback>
|
||||
<div italic op50>
|
||||
<span animate-pulse>Loading...</span>
|
||||
</div>
|
||||
</template>
|
||||
</Suspense>
|
||||
<InputEntry />
|
||||
</div>
|
||||
</template>
|
||||
Reference in new issue
Block a user