base zone components and type refinements

This commit is contained in:
Julian Krauser 2024-11-02 12:47:07 +01:00
parent 9d96e3a6dc
commit 5c56af0dad
38 changed files with 323 additions and 54 deletions

View file

@ -1,5 +1,25 @@
<template>
<p>Content Builder</p>
<SharedHero v-if="hero" :data="hero" />
<div class="container mx-auto py-12 min-h-[50vh]">
<div v-for="item in content" class="contents">
<DynamicZoneColumnImageText v-if="item.__component == 'dynamic-zone.column-image-text'" :data="item" />
<DynamicZoneDualColumnText v-else-if="item.__component == 'dynamic-zone.dual-column-text'" :data="item" />
<DynamicZoneEmphasiseArticle v-else-if="item.__component == 'dynamic-zone.emphasise-article'" :data="item" />
<DynamicZoneFullImage v-else-if="item.__component == 'dynamic-zone.full-image'" :data="item" />
<DynamicZoneFullText v-else-if="item.__component == 'dynamic-zone.full-text'" :data="item" />
<DynamicZoneGallery v-else-if="item.__component == 'dynamic-zone.gallery'" :data="item" />
<SharedList v-else-if="item.__component == 'shared.list'" :data="item" />
</div>
</div>
</template>
<script setup lang="ts"></script>
<script setup lang="ts">
import type { PropType } from "vue";
import type SharedHero from "../types/component/sharedHero";
import type { ComponentTypes } from "../types/component/baseComponent";
defineProps({
hero: Object as PropType<SharedHero>,
content: Array<ComponentTypes>,
});
</script>

View file

@ -16,5 +16,5 @@ import type Global from "../types/single/global";
const { findOne } = useStrapi();
const { data: global } = await useAsyncData("global", () => findOne<Global>("global"));
const { footer } = global.value?.data as unknown as Global;
const { footer } = global.value?.data ?? ({} as Global);
</script>

View file

@ -17,9 +17,9 @@
</div>
</div>
<div
v-if="navbar_sub_items?.length != 0"
v-if="navbar_sub_items && navbar_sub_items?.length != 0"
primary
class="h-12 min-h-fit w-full px-12 border-t-2 border-white justify-center items-center gap-5 flex"
class="h-fit min-h-fit w-full px-12 border-t-2 border-white justify-center items-center gap-5 flex"
>
<NuxtLink
primary-sublink
@ -44,7 +44,7 @@ const baseUrl = useStrapiUrl().replace("/api", "");
const { findOne } = useStrapi();
const { data: global } = await useAsyncData("global", () => findOne<Global>("global"));
const { navbar } = global.value?.data as unknown as Global;
const { navbar } = global.value?.data ?? ({} as Global);
const navbar_sub_items = computed(() => {
return navbar.navbar_items.find((ni) => ni.URL == params?.[0])?.navbar_sub_items;

View file

@ -0,0 +1,12 @@
<template>
<p>{{ data }}</p>
</template>
<script setup lang="ts">
import type { PropType } from "vue";
import type DynamicZoneColumnImageText from "../../types/component/dynamicZoneColumnImageText";
defineProps({
data: Object as PropType<DynamicZoneColumnImageText>,
});
</script>

View file

@ -0,0 +1,12 @@
<template>
<p>{{ data }}</p>
</template>
<script setup lang="ts">
import type { PropType } from "vue";
import type DynamicZoneDualColumnText from "../../types/component/dynamicZoneDualColumnText";
defineProps({
data: Object as PropType<DynamicZoneDualColumnText>,
});
</script>

View file

@ -0,0 +1,12 @@
<template>
<p>{{ data }}</p>
</template>
<script setup lang="ts">
import type { PropType } from "vue";
import type DynamicZoneEmphasiseArticle from "../../types/component/dynamicZoneEmphasiseArticle";
defineProps({
data: Object as PropType<DynamicZoneEmphasiseArticle>,
});
</script>

View file

@ -0,0 +1,12 @@
<template>
<p>{{ data }}</p>
</template>
<script setup lang="ts">
import type { PropType } from "vue";
import type DynamicZoneFullImage from "../../types/component/dynamicZoneFullImage";
defineProps({
data: Object as PropType<DynamicZoneFullImage>,
});
</script>

View file

@ -0,0 +1,12 @@
<template>
<p>{{ data }}</p>
</template>
<script setup lang="ts">
import type { PropType } from "vue";
import type DynamicZoneFullText from "../../types/component/dynamicZoneFullText";
defineProps({
data: Object as PropType<DynamicZoneFullText>,
});
</script>

View file

@ -0,0 +1,12 @@
<template>
<p>{{ data }}</p>
</template>
<script setup lang="ts">
import type { PropType } from "vue";
import type DynamicZoneGallery from "../../types/component/dynamicZoneGallery";
defineProps({
data: Object as PropType<DynamicZoneGallery>,
});
</script>

View file

@ -0,0 +1,25 @@
<template>
<div class="w-full max-h-60 h-60">
<NuxtPicture
preload
loading="lazy"
class="w-full h-full object-cover object-center"
:src="baseUrl + data?.banner.url"
:imgAttrs="{ class: 'w-full h-full object-cover object-center' }"
/>
<div primary class="h-12 w-full px-12 justify-center items-center gap-5 flex">
<p>{{ data?.title }}</p>
</div>
</div>
</template>
<script setup lang="ts">
import type { PropType } from "vue";
import type SharedHero from "../../types/component/sharedHero";
defineProps({
data: Object as PropType<SharedHero>,
});
const baseUrl = useStrapiUrl().replace("/api", "");
</script>

View file

@ -0,0 +1,12 @@
<template>
<p>{{ data }}</p>
</template>
<script setup lang="ts">
import type { PropType } from "vue";
import type SharedList from "../../types/component/sharedList";
defineProps({
data: Object as PropType<SharedList>,
});
</script>

View file

@ -1,6 +1,12 @@
<template>
<div class="relative h-[calc(100vh-6rem)] w-full">
<NuxtPicture preload loading="lazy" class="w-full h-full object-cover" :src="baseUrl + backdrop.url" />
<div class="relative h-[calc(100vh-6rem)] max-h-[calc(100vh-6rem)] w-full overflow-hidden">
<NuxtPicture
preload
loading="lazy"
class="w-full h-full object-cover object-center"
:src="baseUrl + backdrop.url"
:imgAttrs="{ class: 'w-full h-full object-cover object-center' }"
/>
<img class="absolute h-40 w-fit bottom-10 left-5" :src="baseUrl + navbar.logo.url" />
</div>
<Header />

View file

@ -33,5 +33,6 @@ export default defineNuxtConfig({
strapi: {
url: process.env.STRAPI_URL,
prefix: "/api",
version: "v5",
},
});
});

32
package-lock.json generated
View file

@ -8,7 +8,7 @@
"hasInstallScript": true,
"dependencies": {
"@nuxt/image": "^1.8.1",
"@nuxtjs/strapi": "^1.12.0",
"@nuxtjs/strapi": "npm:@nuxtjs/strapi-edge@1.12.0-28818224.f53bdf9",
"nuxt": "^3.13.2",
"vue": "latest",
"vue-router": "latest"
@ -1353,15 +1353,17 @@
}
},
"node_modules/@nuxtjs/strapi": {
"version": "1.12.0",
"resolved": "https://registry.npmjs.org/@nuxtjs/strapi/-/strapi-1.12.0.tgz",
"integrity": "sha512-pysxYW5SPQaB6H7G72wO2qz4Rl7DakuPUthNweEvL3ERK5O3qMZ97mIm+Pdwvb3/H0ntFDJSjx08YRWjjuV7hg==",
"name": "@nuxtjs/strapi-edge",
"version": "1.12.0-28818224.f53bdf9",
"resolved": "https://registry.npmjs.org/@nuxtjs/strapi-edge/-/strapi-edge-1.12.0-28818224.f53bdf9.tgz",
"integrity": "sha512-SdazV9/5qJr/b5F//fveU9DVxBFeYKfB6Gg7wyvZoNUW9z9BSPuQyoCfDdzRkPy+QqdaLhFXPjFJdPNeK/8agA==",
"license": "MIT",
"dependencies": {
"@nuxt/kit": "^3.11.1",
"@nuxt/kit": "^3.13.2",
"defu": "^6.1.4",
"graphql": "^16.8.1",
"qs": "^6.12.0",
"ufo": "^1.5.3"
"graphql": "^16.9.0",
"qs": "^6.13.0",
"ufo": "^1.5.4"
}
},
"node_modules/@parcel/watcher": {
@ -10920,15 +10922,15 @@
}
},
"@nuxtjs/strapi": {
"version": "1.12.0",
"resolved": "https://registry.npmjs.org/@nuxtjs/strapi/-/strapi-1.12.0.tgz",
"integrity": "sha512-pysxYW5SPQaB6H7G72wO2qz4Rl7DakuPUthNweEvL3ERK5O3qMZ97mIm+Pdwvb3/H0ntFDJSjx08YRWjjuV7hg==",
"version": "npm:@nuxtjs/strapi-edge@1.12.0-28818224.f53bdf9",
"resolved": "https://registry.npmjs.org/@nuxtjs/strapi-edge/-/strapi-edge-1.12.0-28818224.f53bdf9.tgz",
"integrity": "sha512-SdazV9/5qJr/b5F//fveU9DVxBFeYKfB6Gg7wyvZoNUW9z9BSPuQyoCfDdzRkPy+QqdaLhFXPjFJdPNeK/8agA==",
"requires": {
"@nuxt/kit": "^3.11.1",
"@nuxt/kit": "^3.13.2",
"defu": "^6.1.4",
"graphql": "^16.8.1",
"qs": "^6.12.0",
"ufo": "^1.5.3"
"graphql": "^16.9.0",
"qs": "^6.13.0",
"ufo": "^1.5.4"
}
},
"@parcel/watcher": {

View file

@ -11,7 +11,7 @@
},
"dependencies": {
"@nuxt/image": "^1.8.1",
"@nuxtjs/strapi": "^1.12.0",
"@nuxtjs/strapi": "npm:@nuxtjs/strapi-edge@1.12.0-28818224.f53bdf9",
"nuxt": "^3.13.2",
"vue": "latest",
"vue-router": "latest"

View file

@ -1,7 +1,7 @@
<template>
<NuxtLayout name="default">
<NotFound v-if="data.length == 0" />
<ContentBuilder v-else class="min-h-[calc(100vh-9rem)] w-full" />
<NotFound v-if="active_page_id == ''" />
<ContentBuilder v-else class="min-h-[calc(100vh-9rem)] w-full" :hero="hero" :content="content" />
</NuxtLayout>
</template>
@ -18,7 +18,7 @@ const { findOne } = useStrapi();
const { data: global } = await useAsyncData("global", () => findOne<Global>("global"));
const {
navbar: { navbar_items },
} = global.value?.data as unknown as Global;
} = global.value?.data ?? ({} as Global);
const navbar_sub_items = computed(() => {
return navbar_items.find((ni) => ni.URL == params[0])?.navbar_sub_items ?? [];
@ -31,14 +31,21 @@ const active_sub_item = computed(() => {
return navbar_sub_items.value.find((si) => si.URL == params[1])?.page;
});
const active_page_id = computed<string>(() => {
return active_sub_item.value?.slug ?? active_item.value?.slug ?? "";
return active_sub_item.value?.documentId ?? active_item.value?.documentId ?? "";
});
const { data: pages } = await useAsyncData("pages", () =>
findOne<Array<Page>>("pages", {
populate: "*",
filters: { slug: active_page_id.value },
findOne<Page>("pages", active_page_id.value, {
populate: {
populate: "*",
content: {
populate: "*",
},
hero: {
populate: "*",
},
},
})
);
const data = pages.value?.data as unknown as Array<Page>;
const { content, hero } = pages.value?.data ?? {};
</script>

View file

@ -1,6 +1,6 @@
<template>
<NuxtLayout name="landing">
<ContentBuilder class="min-h-[calc(100vh-9rem)] w-full" />
<ContentBuilder class="min-h-[calc(100vh-9rem)] w-full" :content="content" />
</NuxtLayout>
</template>
@ -10,5 +10,5 @@ import type Homepage from "../types/single/homepage";
const { findOne } = useStrapi();
const { data: homepage } = await useAsyncData("homepage", () => findOne<Homepage>("homepage"));
const { content } = homepage.value?.data as unknown as Homepage;
const { content } = homepage.value?.data ?? {};
</script>

View file

@ -0,0 +1,13 @@
export default interface Article {
id: number;
documentId: string;
title: string;
description: string;
slug: string;
content: Array<{ type: string; children: Array<{ type: string; text: string }>; level?: number }>;
date: string;
createdAt: string;
updatedAt: string;
publishedAt: string;
locale: string;
}

View file

@ -1,3 +1,6 @@
import type { ComponentTypes } from "../component/baseComponent";
import type SharedHero from "../component/sharedHero";
export default interface Page {
id: number;
documentId: string;
@ -7,7 +10,7 @@ export default interface Page {
publishedAt: string;
locale: string;
slug: string;
Hero: { id: number; titel: string };
content: { __component: string; id: number; list: string; enable_detail: boolean }[];
hero: SharedHero;
content: Array<ComponentTypes>;
localizations: any[];
}

View file

@ -0,0 +1,30 @@
import type DynamicZoneColumnImageText from "./dynamicZoneColumnImageText";
import type DynamicZoneDualColumnText from "./dynamicZoneDualColumnText";
import type DynamicZoneEmphasiseArticle from "./dynamicZoneEmphasiseArticle";
import type DynamicZoneFullImage from "./dynamicZoneFullImage";
import type DynamicZoneFullText from "./dynamicZoneFullText";
import type DynamicZoneGallery from "./dynamicZoneGallery";
import type SharedList from "./sharedList";
export default interface BaseComponent {
__component: ComponentNames;
id: number;
}
export type ComponentNames =
| "shared.list"
| "dynamic-zone.gallery"
| "dynamic-zone.full-text"
| "dynamic-zone.full-image"
| "dynamic-zone.emphasise-article"
| "dynamic-zone.dual-column-text"
| "dynamic-zone.column-image-text";
export type ComponentTypes =
| SharedList
| DynamicZoneGallery
| DynamicZoneFullText
| DynamicZoneFullImage
| DynamicZoneEmphasiseArticle
| DynamicZoneDualColumnText
| DynamicZoneColumnImageText;

View file

@ -1,4 +1,4 @@
export default interface Image {
export default interface BaseImage {
id: number;
documentId: string;
name: string;
@ -6,7 +6,7 @@ export default interface Image {
caption: string | null;
width: number;
height: number;
formats: any;
formats: Record<string, ImageFormat> | null;
hash: string;
ext: string;
mime: string;
@ -19,3 +19,16 @@ export default interface Image {
updatedAt: string;
publishedAt: string;
}
export interface ImageFormat {
name: string;
hash: string;
ext: string;
mime: string;
path: string | null;
width: number;
height: number;
size: number;
sizeInBytes: number;
url: string;
}

View file

@ -0,0 +1,9 @@
import type BaseComponent from "./baseComponent";
import type BaseImage from "./baseImage";
export default interface DynamicZoneColumnImageText extends BaseComponent {
__component: "dynamic-zone.column-image-text";
text: Array<{ type: string; children: Array<{ type: string; text: string }> }>;
image_left: boolean;
image: BaseImage;
}

View file

@ -0,0 +1,7 @@
import type BaseComponent from "./baseComponent";
export default interface DynamicZoneDualColumnText extends BaseComponent {
__component: "dynamic-zone.dual-column-text";
left_side: Array<{ type: string; children: Array<{ type: string; text: string }> }>;
right_side: Array<{ type: string; children: Array<{ type: string; text: string }> }>;
}

View file

@ -0,0 +1,9 @@
import type Article from "../collection/article";
import type BaseComponent from "./baseComponent";
export default interface DynamicZoneEmphasiseArticle extends BaseComponent {
__component: "dynamic-zone.emphasise-article";
titel: string;
description: string;
articles: Array<Article>;
}

View file

@ -0,0 +1,7 @@
import type BaseComponent from "./baseComponent";
import type BaseImage from "./baseImage";
export default interface DynamicZoneFullImage extends BaseComponent {
__component: "dynamic-zone.full-image";
image: BaseImage;
}

View file

@ -0,0 +1,6 @@
import type BaseComponent from "./baseComponent";
export default interface DynamicZoneFullText extends BaseComponent {
__component: "dynamic-zone.full-text";
text: Array<{ type: string; children: Array<{ type: string; text: string }> }>;
}

View file

@ -0,0 +1,7 @@
import type BaseComponent from "./baseComponent";
import type BaseImage from "./baseImage";
export default interface DynamicZoneGallery extends BaseComponent {
__component: "dynamic-zone.gallery";
images: Array<BaseImage>;
}

View file

@ -1,4 +1,4 @@
import type FooterLink from "./footerLink";
import type FooterLink from "./itemsFooterLink";
export default interface Footer {
id: number;

View file

@ -0,0 +1,8 @@
import type BaseImage from "./baseImage";
import type NavbarItem from "./itemsNavbarItem";
export default interface Navbar {
id: number;
logo: BaseImage;
navbar_items: NavbarItem[];
}

View file

@ -1,4 +1,4 @@
import type NavbarSubItem from "./navbarSubItem";
import type NavbarSubItem from "./itemsNavbarSubItem";
export default interface NavbarItem {
id: number;

View file

@ -1,8 +0,0 @@
import type Image from "./image";
import type NavbarItem from "./navbarItem";
export default interface Navbar {
id: number;
logo: Image;
navbar_items: NavbarItem[];
}

View file

@ -0,0 +1,7 @@
import type BaseImage from "./baseImage";
export default interface SharedHero {
id: number;
title: string;
banner: BaseImage;
}

View file

@ -0,0 +1,5 @@
export default interface SharedLink {
text: string;
URL: string;
target: "_blank" | "_self" | "_parent" | "_top";
}

View file

@ -0,0 +1,7 @@
import type BaseComponent from "./baseComponent";
export default interface SharedList extends BaseComponent {
__component: "shared.list";
list: string;
enable_detail: boolean;
}

View file

@ -1,5 +1,5 @@
import type Footer from "../component/footer";
import type Navbar from "../component/navbar";
import type Footer from "../component/globalFooter";
import type Navbar from "../component/globalNavbar";
export default interface Global {
id: number;

View file

@ -1,4 +1,5 @@
import type Image from "../component/image";
import type BaseImage from "../component/baseImage";
import type { ComponentTypes } from "../component/baseComponent";
export default interface Homepage {
id: number;
@ -7,6 +8,6 @@ export default interface Homepage {
updatedAt: string;
publishedAt: string;
locale: string;
backdrop: Image;
content: Array<any>;
backdrop: BaseImage;
content: Array<ComponentTypes>;
}