Set up content layer
This commit is contained in:
parent
b90285a425
commit
2a1ba854ba
@ -1,5 +1,3 @@
|
||||
<template>
|
||||
<div>
|
||||
<HelloWorld />
|
||||
</div>
|
||||
<NuxtPage/>
|
||||
</template>
|
||||
|
||||
@ -0,0 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<LocaleSwitcher/>
|
||||
<br/>
|
||||
<VariantSwitcher/>
|
||||
</template>
|
||||
@ -2,5 +2,5 @@
|
||||
export default defineNuxtConfig({
|
||||
compatibilityDate: '2025-07-15',
|
||||
devtools: { enabled: true },
|
||||
extends: [ '@layers/base' ]
|
||||
extends: [ '@layers/content' ]
|
||||
})
|
||||
|
||||
@ -13,6 +13,6 @@
|
||||
"nuxt": "^4.1.2",
|
||||
"vue": "^3.5.22",
|
||||
"vue-router": "^4.5.1",
|
||||
"@layers/base": "workspace:0.0.0"
|
||||
"@layers/content": "workspace:0.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,14 +0,0 @@
|
||||
export default defineAppConfig({
|
||||
myLayer: {
|
||||
name: 'Hello from Nuxt layer'
|
||||
}
|
||||
})
|
||||
|
||||
declare module '@nuxt/schema' {
|
||||
interface AppConfigInput {
|
||||
myLayer?: {
|
||||
/** Project name */
|
||||
name?: string
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
const { myLayer } = useAppConfig()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<h1>Hello World!</h1>
|
||||
<pre>{{ myLayer }}</pre>
|
||||
</div>
|
||||
</template>
|
||||
@ -1,3 +1,3 @@
|
||||
<template>
|
||||
<HelloWorld />
|
||||
<NuxtPage/>
|
||||
</template>
|
||||
@ -0,0 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
const { locale, locales, variant, variants, c } = useContent()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<p>locale {{ locale }}</p>
|
||||
<p>locales {{ locales }}</p>
|
||||
<p>variant {{ variant }}</p>
|
||||
<p>variants {{ variants }}</p>
|
||||
<LocaleSwitcher/>
|
||||
<br/>
|
||||
<VariantSwitcher/>
|
||||
<p>{{ c.cookies.title }}</p>
|
||||
</template>
|
||||
24
packages/layers/content/app.config.ts
Normal file
24
packages/layers/content/app.config.ts
Normal file
@ -0,0 +1,24 @@
|
||||
export default defineAppConfig({
|
||||
content: {
|
||||
defaultLocale: 'de',
|
||||
locales: [
|
||||
{code: 'de', name: {'de': 'Deutsch', 'en': 'German'}},
|
||||
{code: 'en', name: {'de': 'Englisch', 'en': 'English'}},
|
||||
],
|
||||
defaultVariant: 'summer',
|
||||
variants: [
|
||||
{code: 'summer', name: {'de': 'Sommer', 'en': 'Summer'}, icon: 'i-lucide-sun'},
|
||||
{code: 'winter', name: {'de': 'Winter', 'en': 'Winter'}, icon: 'i-lucide-snowflake'}
|
||||
],
|
||||
cookieMaxAge: 60 * 60 * 24 * 365
|
||||
}
|
||||
})
|
||||
|
||||
declare module '@nuxt/schema' {
|
||||
interface AppConfigInput {
|
||||
myLayer?: {
|
||||
/** Project name */
|
||||
name?: string
|
||||
}
|
||||
}
|
||||
}
|
||||
7
packages/layers/content/assets/content.yaml
Normal file
7
packages/layers/content/assets/content.yaml
Normal file
@ -0,0 +1,7 @@
|
||||
cookies:
|
||||
title:
|
||||
de: "Ihre Privatsphäre ist uns wichtig"
|
||||
en: "We value your privacy"
|
||||
description:
|
||||
de: "Wir verwenden keine Tracking-Cookies. Genießen Sie Ihren Aufenthalt auf unserer Website!"
|
||||
en: "We do not use any tracking cookies. Enjoy your stay on our website!"
|
||||
10
packages/layers/content/components/LocaleSwitcher.vue
Normal file
10
packages/layers/content/components/LocaleSwitcher.vue
Normal file
@ -0,0 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
const { locale, locales, matrixPath } = useContent()
|
||||
const candidates = computed(() => locales.filter(l => l !== locale.value))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NuxtLink v-for="candidate in candidates" :key="candidate" :to="matrixPath({locale: candidate})">
|
||||
{{ candidate.toUpperCase() }}
|
||||
</NuxtLink>
|
||||
</template>
|
||||
10
packages/layers/content/components/VariantSwitcher.vue
Normal file
10
packages/layers/content/components/VariantSwitcher.vue
Normal file
@ -0,0 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
const { variant, variants, matrixPath } = useContent()
|
||||
const candidates = computed(() => variants.filter(v => v !== variant.value))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NuxtLink v-for="candidate in candidates" :key="candidate" :to="matrixPath({variant: candidate})">
|
||||
{{ candidate.toUpperCase() }}
|
||||
</NuxtLink>
|
||||
</template>
|
||||
171
packages/layers/content/composables/useContent.ts
Normal file
171
packages/layers/content/composables/useContent.ts
Normal file
@ -0,0 +1,171 @@
|
||||
import { computed } from 'vue'
|
||||
import { useRoute } from '#app'
|
||||
import content from '~/assets/content.yaml'
|
||||
|
||||
const specializations: Record<string, object> = {}
|
||||
|
||||
function buildSpecialization(locale: string, variant: string): object {
|
||||
const config = useAppConfig()
|
||||
const locales = config.content.locales.map(l => l.code)
|
||||
const variants = config.content.variants.map(v => v.code)
|
||||
const allTags = new Set([...locales, ...variants])
|
||||
|
||||
function split(key: string): {base?: string; tags: string[]} {
|
||||
const parts = key.split('@')
|
||||
|
||||
if (allTags.has(parts[0])) {
|
||||
return {base: undefined, tags: parts}
|
||||
}
|
||||
|
||||
return {base: parts[0], tags: parts.slice(1)}
|
||||
}
|
||||
|
||||
function relevant(tags: string[]): boolean {
|
||||
if (tags.length < 1) {
|
||||
return true
|
||||
}
|
||||
|
||||
for (const tag of tags) {
|
||||
if (tag !== locale && tag !== variant) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
function filterObject(object: any): any {
|
||||
if (Array.isArray(object)) {
|
||||
return object.map(e => filterObject(e)).filter(e => e !== undefined)
|
||||
}
|
||||
|
||||
if (typeof object !== 'object') {
|
||||
return object
|
||||
}
|
||||
|
||||
const copy: Record<string, any> = {}
|
||||
let anonymous: [number, any][] = []
|
||||
|
||||
for (const key in object) {
|
||||
const {base, tags} = split(key)
|
||||
|
||||
if (!relevant(tags)) {
|
||||
continue
|
||||
}
|
||||
|
||||
const value = filterObject(object[key])
|
||||
|
||||
if (base === undefined) {
|
||||
anonymous.push([tags.length, value])
|
||||
} else {
|
||||
copy[base] = value
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.keys(copy).length > 0 && anonymous.length > 0) {
|
||||
console.warn(`Invalid mixing of keys in object ${object}`)
|
||||
}
|
||||
|
||||
if (Object.keys(copy).length > 0) {
|
||||
return copy
|
||||
}
|
||||
|
||||
anonymous.sort((a, b) => b[0] - a[0])
|
||||
|
||||
if (anonymous[0][0] > 2) {
|
||||
console.warn(`More than two tags per key in object ${object}`)
|
||||
}
|
||||
|
||||
return anonymous[0][1]
|
||||
}
|
||||
|
||||
return filterObject(content)
|
||||
}
|
||||
|
||||
function getSpecialization(locale: string, variant: string): object {
|
||||
const key = locale + '/' + variant
|
||||
|
||||
if (!(key in specializations)) {
|
||||
specializations[key] = buildSpecialization(locale, variant)
|
||||
}
|
||||
|
||||
return specializations[key]
|
||||
}
|
||||
|
||||
export function useContent() {
|
||||
const config = useAppConfig()
|
||||
const route = useRoute()
|
||||
console.log("miau")
|
||||
console.log(route)
|
||||
|
||||
const locales = config.content.locales.map(l => l.code)
|
||||
const variants = config.content.variants.map(v => v.code)
|
||||
|
||||
const localeCookie = useCookie<string>('locale', {
|
||||
sameSite: 'lax',
|
||||
maxAge: config.content.cookieMaxAge
|
||||
})
|
||||
|
||||
const variantCookie = useCookie<string>('variant', {
|
||||
sameSite: 'lax',
|
||||
maxAge: config.content.cookieMaxAge
|
||||
})
|
||||
|
||||
const currentLocale = computed<string>(() => route.params.locale || localeCookie.value || config.content.defaultLocale)
|
||||
const currentVariant = computed<string>(() => route.params.variant || variantCookie.value || config.content.defaultVariant)
|
||||
|
||||
function preferLocale(locale: string) {
|
||||
if (locale in locales) {
|
||||
localeCookie.value = locale
|
||||
}
|
||||
}
|
||||
|
||||
function preferVariant(variant: string) {
|
||||
if (variant in variants) {
|
||||
variantCookie.value = variant
|
||||
}
|
||||
}
|
||||
|
||||
function matrixPath(options: { page?: string; anchor?: string; locale?: string; variant?: string }): string {
|
||||
let prefix: string[] = []
|
||||
|
||||
const showLocale = locales.length > 1
|
||||
const showVariant = variants.length > 1
|
||||
|
||||
if (showLocale) {
|
||||
prefix.push(options.locale ?? currentLocale.value)
|
||||
}
|
||||
|
||||
if (showVariant) {
|
||||
prefix.push(options.variant ?? currentVariant.value)
|
||||
}
|
||||
|
||||
// TODO preserve anchor when taking route.path
|
||||
let page = (options.page ?? route.path).split('/').filter(Boolean)
|
||||
|
||||
if (!options.page) {
|
||||
page = page.slice([showLocale, showVariant].filter(Boolean).length)
|
||||
}
|
||||
|
||||
const base = '/' + [...prefix, ...page].join('/')
|
||||
return options.anchor ? `${base}#${options.anchor}` : base
|
||||
}
|
||||
|
||||
function p(page: string, anchor?: string): string {
|
||||
return matrixPath({page: page, anchor: anchor})
|
||||
}
|
||||
|
||||
const specialization = computed<any>(() => getSpecialization(currentLocale.value, currentVariant.value))
|
||||
|
||||
return {
|
||||
locale: currentLocale,
|
||||
locales: locales,
|
||||
preferLocale: preferLocale,
|
||||
variant: currentVariant,
|
||||
variants: variants,
|
||||
preferVariant: preferVariant,
|
||||
matrixPath: matrixPath,
|
||||
p: p,
|
||||
c: specialization,
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,10 @@
|
||||
// https://nuxt.com/docs/api/configuration/nuxt-config
|
||||
|
||||
import yaml from '@rollup/plugin-yaml'
|
||||
|
||||
export default defineNuxtConfig({
|
||||
devtools: { enabled: true },
|
||||
vite: {
|
||||
plugins: [ yaml() ]
|
||||
}
|
||||
})
|
||||
@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "@layers/base",
|
||||
"name": "@layers/content",
|
||||
"type": "module",
|
||||
"version": "0.0.0",
|
||||
"main": "./nuxt.config.ts",
|
||||
@ -11,8 +11,12 @@
|
||||
"preview": "nuxt preview .playground",
|
||||
"lint": "eslint ."
|
||||
},
|
||||
"dependencies": {
|
||||
"vue-router": "^4.5.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nuxt/eslint": "latest",
|
||||
"@rollup/plugin-yaml": "^4.1.2",
|
||||
"eslint": "^9.36.0",
|
||||
"nuxt": "^4.1.2",
|
||||
"typescript": "^5.9.2",
|
||||
44
pnpm-lock.yaml
generated
44
pnpm-lock.yaml
generated
@ -20,9 +20,9 @@ importers:
|
||||
|
||||
apps/panoramablick-saalbach.at:
|
||||
dependencies:
|
||||
'@layers/base':
|
||||
'@layers/content':
|
||||
specifier: workspace:0.0.0
|
||||
version: link:../../packages/layers/base
|
||||
version: link:../../packages/layers/content
|
||||
nuxt:
|
||||
specifier: ^4.1.2
|
||||
version: 4.1.2(@parcel/watcher@2.5.1)(@types/node@22.15.3)(@vue/compiler-sfc@3.5.22)(db0@0.3.4)(eslint@9.37.0(jiti@2.6.1))(ioredis@5.8.0)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.52.4)(terser@5.44.0)(typescript@5.9.2)(vite@7.1.9(@types/node@22.15.3)(jiti@2.6.1)(terser@5.44.0)(yaml@2.8.1))(yaml@2.8.1)
|
||||
@ -33,11 +33,18 @@ importers:
|
||||
specifier: ^4.5.1
|
||||
version: 4.5.1(vue@3.5.22(typescript@5.9.2))
|
||||
|
||||
packages/layers/base:
|
||||
packages/layers/content:
|
||||
dependencies:
|
||||
vue-router:
|
||||
specifier: ^4.5.1
|
||||
version: 4.5.1(vue@3.5.22(typescript@5.9.2))
|
||||
devDependencies:
|
||||
'@nuxt/eslint':
|
||||
specifier: latest
|
||||
version: 1.9.0(@typescript-eslint/utils@8.40.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.2))(@vue/compiler-sfc@3.5.22)(eslint@9.37.0(jiti@2.6.1))(magicast@0.3.5)(typescript@5.9.2)(vite@7.1.9(@types/node@22.15.3)(jiti@2.6.1)(terser@5.44.0)(yaml@2.8.1))
|
||||
'@rollup/plugin-yaml':
|
||||
specifier: ^4.1.2
|
||||
version: 4.1.2(rollup@4.52.4)
|
||||
eslint:
|
||||
specifier: ^9.36.0
|
||||
version: 9.37.0(jiti@2.6.1)
|
||||
@ -1025,6 +1032,15 @@ packages:
|
||||
rollup:
|
||||
optional: true
|
||||
|
||||
'@rollup/plugin-yaml@4.1.2':
|
||||
resolution: {integrity: sha512-RpupciIeZMUqhgFE97ba0s98mOFS7CWzN3EJNhJkqSv9XLlWYtwVdtE6cDw6ASOF/sZVFS7kRJXftaqM2Vakdw==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
peerDependencies:
|
||||
rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0
|
||||
peerDependenciesMeta:
|
||||
rollup:
|
||||
optional: true
|
||||
|
||||
'@rollup/pluginutils@5.3.0':
|
||||
resolution: {integrity: sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
@ -3464,6 +3480,10 @@ packages:
|
||||
resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==}
|
||||
engines: {node: '>=0.6'}
|
||||
|
||||
tosource@2.0.0-alpha.3:
|
||||
resolution: {integrity: sha512-KAB2lrSS48y91MzFPFuDg4hLbvDiyTjOVgaK7Erw+5AmZXNq4sFRVn8r6yxSLuNs15PaokrDRpS61ERY9uZOug==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
totalist@3.0.1:
|
||||
resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==}
|
||||
engines: {node: '>=6'}
|
||||
@ -4516,7 +4536,7 @@ snapshots:
|
||||
|
||||
'@nuxt/eslint-plugin@1.9.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.2)':
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 8.40.0
|
||||
'@typescript-eslint/types': 8.45.0
|
||||
'@typescript-eslint/utils': 8.40.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.2)
|
||||
eslint: 9.37.0(jiti@2.6.1)
|
||||
transitivePeerDependencies:
|
||||
@ -4974,6 +4994,14 @@ snapshots:
|
||||
optionalDependencies:
|
||||
rollup: 4.52.4
|
||||
|
||||
'@rollup/plugin-yaml@4.1.2(rollup@4.52.4)':
|
||||
dependencies:
|
||||
'@rollup/pluginutils': 5.3.0(rollup@4.52.4)
|
||||
js-yaml: 4.1.0
|
||||
tosource: 2.0.0-alpha.3
|
||||
optionalDependencies:
|
||||
rollup: 4.52.4
|
||||
|
||||
'@rollup/pluginutils@5.3.0(rollup@4.52.4)':
|
||||
dependencies:
|
||||
'@types/estree': 1.0.8
|
||||
@ -5116,7 +5144,7 @@ snapshots:
|
||||
'@typescript-eslint/project-service@8.40.0(typescript@5.9.2)':
|
||||
dependencies:
|
||||
'@typescript-eslint/tsconfig-utils': 8.40.0(typescript@5.9.2)
|
||||
'@typescript-eslint/types': 8.40.0
|
||||
'@typescript-eslint/types': 8.45.0
|
||||
debug: 4.4.1
|
||||
typescript: 5.9.2
|
||||
transitivePeerDependencies:
|
||||
@ -5936,14 +5964,14 @@ snapshots:
|
||||
eslint-plugin-import-lite@0.3.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.2):
|
||||
dependencies:
|
||||
'@eslint-community/eslint-utils': 4.9.0(eslint@9.37.0(jiti@2.6.1))
|
||||
'@typescript-eslint/types': 8.40.0
|
||||
'@typescript-eslint/types': 8.45.0
|
||||
eslint: 9.37.0(jiti@2.6.1)
|
||||
optionalDependencies:
|
||||
typescript: 5.9.2
|
||||
|
||||
eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.40.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.2))(eslint@9.37.0(jiti@2.6.1)):
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 8.40.0
|
||||
'@typescript-eslint/types': 8.45.0
|
||||
comment-parser: 1.4.1
|
||||
debug: 4.4.1
|
||||
eslint: 9.37.0(jiti@2.6.1)
|
||||
@ -7647,6 +7675,8 @@ snapshots:
|
||||
|
||||
toidentifier@1.0.1: {}
|
||||
|
||||
tosource@2.0.0-alpha.3: {}
|
||||
|
||||
totalist@3.0.1: {}
|
||||
|
||||
tr46@0.0.3: {}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user