kmcom-nuxt-layers 1.6.18 → 1.6.21
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.
- package/layers/content/app/components/Blog/Article.vue +9 -5
- package/layers/content/app/components/Blog/Card.vue +11 -8
- package/layers/content/app/components/Blog/List.vue +12 -11
- package/layers/content/app/components/Gallery/Card.vue +6 -1
- package/layers/content/app/components/Gallery/Detail.vue +10 -6
- package/layers/content/app/components/Gallery/ImageDetail.vue +9 -3
- package/layers/content/app/components/NuxtContent/Detail.vue +7 -3
- package/layers/content/app/components/NuxtContent/Toc.vue +3 -1
- package/layers/content/app/components/Portfolio/Card.vue +5 -1
- package/layers/content/app/components/Portfolio/Detail.vue +32 -11
- package/layers/content/app/components/Portfolio/List.vue +4 -2
- package/layers/content/app/composables/useCollectionItem.ts +3 -1
- package/layers/content/app/composables/useCollectionSurround.ts +4 -6
- package/layers/content/app/types/content.ts +1 -1
- package/layers/content/package.json +1 -0
- package/layers/core/app/app.vue +1 -0
- package/layers/core/app/composables/useCache.ts +7 -8
- package/layers/core/app/composables/useScrollGuard.ts +0 -12
- package/layers/core/app/plugins/init.ts +4 -3
- package/layers/core/package.json +1 -0
- package/layers/core/tsconfig.json +2 -1
- package/layers/forms/app/components/Form/Contact.vue +1 -5
- package/layers/forms/app/components/Form/Field.vue +34 -22
- package/layers/forms/package.json +1 -0
- package/layers/layout/app/components/Layout/Page/Container.vue +7 -1
- package/layers/layout/app/components/Layout/Page/index.vue +4 -1
- package/layers/layout/package.json +1 -0
- package/layers/motion/app/components/Motion/Marquee.vue +12 -16
- package/layers/motion/app/components/Motion/Parallax.vue +1 -1
- package/layers/motion/app/components/Motion/Staggered.vue +1 -1
- package/layers/motion/app/components/Motion/Transition.vue +0 -13
- package/layers/motion/app/composables/useSmoothScroll.ts +1 -1
- package/layers/motion/package.json +1 -0
- package/layers/routing/app/middleware/02.governance.global.ts +2 -0
- package/layers/routing/app/plugins/feature-flags.client.ts +1 -0
- package/layers/routing/nuxt.config.ts +8 -4
- package/layers/routing/package.json +4 -1
- package/layers/shader/app/composables/useShader.ts +1 -1
- package/layers/shader/app/utils/tsl/noise.ts +0 -5
- package/layers/shader/nuxt.config.ts +1 -1
- package/layers/shader/package.json +3 -0
- package/layers/theme/package.json +1 -0
- package/layers/ui/app/app.config.ts +37 -6
- package/layers/ui/app/components/Accent/Scene.vue +14 -1
- package/layers/ui/app/components/Mast/NavModal.vue +3 -2
- package/layers/ui/app/components/Progress/Bar.vue +11 -1
- package/layers/ui/app/components/Typography/CodeBlock.vue +5 -4
- package/layers/ui/app/components/Typography/Headline.vue +11 -3
- package/layers/ui/app/components/Typography/QuoteBlock.vue +5 -1
- package/layers/ui/app/components/Typography/TextStroke.vue +0 -2
- package/layers/ui/app/components/Typography/index.vue +1 -1
- package/layers/ui/app/composables/gradient.ts +1 -1
- package/layers/ui/app/composables/mastNav.ts +0 -1
- package/layers/ui/app/composables/toast.ts +4 -4
- package/layers/ui/app/composables/useSite.ts +6 -6
- package/layers/ui/app/types/accent.ts +1 -1
- package/layers/ui/app/types/app-config.d.ts +5 -0
- package/layers/ui/app/utils/createModal.ts +4 -4
- package/layers/ui/package.json +1 -0
- package/package.json +8 -8
|
@@ -1,25 +1,29 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
import type { BlogCollectionItem } from '@nuxt/content'
|
|
3
|
+
|
|
2
4
|
const { slug } = defineProps<{
|
|
3
5
|
slug: string
|
|
4
6
|
}>()
|
|
7
|
+
|
|
8
|
+
const asBlog = (item: unknown) => item as BlogCollectionItem
|
|
5
9
|
</script>
|
|
6
10
|
|
|
7
11
|
<template>
|
|
8
12
|
<NuxtContentDetail collection="blog" :slug not-found-message="Blog post not found">
|
|
9
13
|
<template #headline="{ item }">
|
|
10
14
|
<div class="flex items-center gap-3 text-sm text-muted">
|
|
11
|
-
<time v-if="item.date">{{ new Date(item.date).toLocaleDateString() }}</time>
|
|
12
|
-
<UBadge v-if="item.badge" color="primary" variant="subtle">
|
|
13
|
-
{{ item.badge }}
|
|
15
|
+
<time v-if="asBlog(item).date">{{ new Date(asBlog(item).date).toLocaleDateString() }}</time>
|
|
16
|
+
<UBadge v-if="asBlog(item).badge" color="primary" variant="subtle">
|
|
17
|
+
{{ asBlog(item).badge }}
|
|
14
18
|
</UBadge>
|
|
15
19
|
</div>
|
|
16
20
|
</template>
|
|
17
21
|
|
|
18
22
|
<template #after-content="{ item }">
|
|
19
|
-
<template v-if="item.tags?.length">
|
|
23
|
+
<template v-if="asBlog(item).tags?.length">
|
|
20
24
|
<USeparator class="my-8" />
|
|
21
25
|
<div class="flex flex-wrap gap-2">
|
|
22
|
-
<UBadge v-for="tag in item.tags" :key="tag" color="neutral" variant="subtle">
|
|
26
|
+
<UBadge v-for="tag in asBlog(item).tags" :key="tag" color="neutral" variant="subtle">
|
|
23
27
|
{{ tag }}
|
|
24
28
|
</UBadge>
|
|
25
29
|
</div>
|
|
@@ -17,24 +17,27 @@ const {
|
|
|
17
17
|
authors?: { name: string; avatar?: string; url?: string }[]
|
|
18
18
|
to?: string
|
|
19
19
|
}>()
|
|
20
|
+
|
|
21
|
+
const postProps = computed(() => ({
|
|
22
|
+
...(description !== undefined && { description }),
|
|
23
|
+
...(date !== undefined && { date }),
|
|
24
|
+
...(image !== undefined && { image }),
|
|
25
|
+
...(badge !== undefined && { badge }),
|
|
26
|
+
...(to !== undefined && { to }),
|
|
27
|
+
}))
|
|
20
28
|
</script>
|
|
21
29
|
|
|
22
30
|
<template>
|
|
23
31
|
<UBlogPost
|
|
24
32
|
:title
|
|
25
|
-
|
|
26
|
-
:date
|
|
27
|
-
:image
|
|
28
|
-
:badge
|
|
33
|
+
v-bind="postProps"
|
|
29
34
|
:authors="
|
|
30
35
|
authors.map((a) => ({
|
|
31
36
|
name: a.name,
|
|
32
|
-
|
|
33
|
-
to: a.url,
|
|
34
|
-
target: a.url ? '_blank' : undefined,
|
|
37
|
+
...(a.avatar && { avatar: { src: a.avatar } }),
|
|
38
|
+
...(a.url && { to: a.url, target: '_blank' as const }),
|
|
35
39
|
}))
|
|
36
40
|
"
|
|
37
|
-
:to
|
|
38
41
|
variant="outline"
|
|
39
42
|
/>
|
|
40
43
|
</template>
|
|
@@ -19,20 +19,21 @@ const { data: posts, status } = await useBlogPosts(options)
|
|
|
19
19
|
v-for="post in posts"
|
|
20
20
|
:key="post.id"
|
|
21
21
|
:title="post.title"
|
|
22
|
-
:description="post.description"
|
|
23
22
|
:date="post.date"
|
|
24
|
-
:image="post.image"
|
|
25
|
-
:badge="post.badge"
|
|
26
|
-
:authors="
|
|
27
|
-
post.authors?.map((a) => ({
|
|
28
|
-
name: a.name,
|
|
29
|
-
avatar: a.avatar ? { src: a.avatar } : undefined,
|
|
30
|
-
to: a.url,
|
|
31
|
-
target: a.url ? '_blank' : undefined,
|
|
32
|
-
}))
|
|
33
|
-
"
|
|
34
23
|
:to="post.path"
|
|
35
24
|
variant="outline"
|
|
25
|
+
v-bind="{
|
|
26
|
+
...(post.description !== undefined && { description: post.description }),
|
|
27
|
+
...(post.image !== undefined && { image: post.image }),
|
|
28
|
+
...(post.badge !== undefined && { badge: post.badge }),
|
|
29
|
+
...(post.authors && {
|
|
30
|
+
authors: post.authors.map((a) => ({
|
|
31
|
+
name: a.name,
|
|
32
|
+
...(a.avatar && { avatar: { src: a.avatar } }),
|
|
33
|
+
...(a.url && { to: a.url, target: '_blank' as const }),
|
|
34
|
+
})),
|
|
35
|
+
}),
|
|
36
|
+
}"
|
|
36
37
|
/>
|
|
37
38
|
</UBlogPosts>
|
|
38
39
|
</NuxtContentList>
|
|
@@ -15,10 +15,15 @@ const {
|
|
|
15
15
|
tags?: string[]
|
|
16
16
|
to?: string
|
|
17
17
|
}>()
|
|
18
|
+
|
|
19
|
+
const cardProps = computed(() => ({
|
|
20
|
+
...(description !== undefined && { description }),
|
|
21
|
+
...(to !== undefined && { to }),
|
|
22
|
+
}))
|
|
18
23
|
</script>
|
|
19
24
|
|
|
20
25
|
<template>
|
|
21
|
-
<UPageCard :title
|
|
26
|
+
<UPageCard :title v-bind="cardProps" variant="outline">
|
|
22
27
|
<img
|
|
23
28
|
v-if="coverImage"
|
|
24
29
|
:src="coverImage.src"
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
import type { GalleryCollectionItem } from '@nuxt/content'
|
|
3
|
+
|
|
2
4
|
const { slug } = defineProps<{
|
|
3
5
|
slug: string
|
|
4
6
|
}>()
|
|
@@ -7,6 +9,8 @@ const lightboxOpen = ref(false)
|
|
|
7
9
|
const lightboxIndex = ref(0)
|
|
8
10
|
const lightboxImages = ref<{ src: string; alt: string; width?: number; height?: number }[]>([])
|
|
9
11
|
|
|
12
|
+
const asGallery = (item: unknown) => item as GalleryCollectionItem
|
|
13
|
+
|
|
10
14
|
function openLightbox(
|
|
11
15
|
images: { src: string; alt: string; width?: number; height?: number }[],
|
|
12
16
|
index: number
|
|
@@ -21,21 +25,21 @@ function openLightbox(
|
|
|
21
25
|
<NuxtContentDetail collection="gallery" :slug not-found-message="Gallery not found">
|
|
22
26
|
<template #headline="{ item }">
|
|
23
27
|
<div class="flex flex-wrap items-center gap-2">
|
|
24
|
-
<span v-if="item.images?.length" class="text-sm text-muted">
|
|
25
|
-
{{ item.images
|
|
28
|
+
<span v-if="asGallery(item).images?.length" class="text-sm text-muted">
|
|
29
|
+
{{ asGallery(item).images!.length }} image{{ asGallery(item).images!.length === 1 ? '' : 's' }}
|
|
26
30
|
</span>
|
|
27
|
-
<UBadge v-for="tag in item.tags" :key="tag" color="neutral" variant="subtle" size="xs">
|
|
31
|
+
<UBadge v-for="tag in asGallery(item).tags" :key="tag" color="neutral" variant="subtle" size="xs">
|
|
28
32
|
{{ tag }}
|
|
29
33
|
</UBadge>
|
|
30
34
|
</div>
|
|
31
35
|
</template>
|
|
32
36
|
|
|
33
37
|
<template #after-content="{ item }">
|
|
34
|
-
<template v-if="item.images?.length">
|
|
38
|
+
<template v-if="asGallery(item).images?.length">
|
|
35
39
|
<USeparator class="my-8" />
|
|
36
40
|
<div class="grid grid-cols-2 md:grid-cols-3 gap-4">
|
|
37
41
|
<div
|
|
38
|
-
v-for="(img, idx) in item.images"
|
|
42
|
+
v-for="(img, idx) in asGallery(item).images"
|
|
39
43
|
:key="img.src"
|
|
40
44
|
class="overflow-hidden rounded-lg group relative"
|
|
41
45
|
>
|
|
@@ -55,7 +59,7 @@ function openLightbox(
|
|
|
55
59
|
</NuxtLink>
|
|
56
60
|
<button
|
|
57
61
|
class="absolute top-2 right-2 z-10 opacity-0 group-hover:opacity-100 transition-opacity duration-300 bg-black/50 hover:bg-black/70 text-white rounded-full p-1.5 cursor-pointer"
|
|
58
|
-
@click.stop="openLightbox(item.images
|
|
62
|
+
@click.stop="openLightbox(asGallery(item).images!, idx)"
|
|
59
63
|
>
|
|
60
64
|
<UIcon name="i-lucide-expand" class="size-4" />
|
|
61
65
|
</button>
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
import type { GalleryCollectionItem } from '@nuxt/content'
|
|
3
|
+
|
|
2
4
|
const { slug, index } = defineProps<{
|
|
3
5
|
slug: string
|
|
4
6
|
index: number
|
|
5
7
|
}>()
|
|
6
8
|
|
|
7
|
-
const { data:
|
|
9
|
+
const { data: rawItem } = await useCollectionItem('gallery', slug)
|
|
10
|
+
const item = computed(() => rawItem.value as GalleryCollectionItem | null)
|
|
8
11
|
|
|
9
12
|
const image = computed(() => item.value?.images?.[index])
|
|
10
13
|
const totalImages = computed(() => item.value?.images?.length ?? 0)
|
|
@@ -19,8 +22,11 @@ useSeoMeta({
|
|
|
19
22
|
</script>
|
|
20
23
|
|
|
21
24
|
<template>
|
|
22
|
-
<div v-if="item && image">
|
|
23
|
-
<UPageHeader
|
|
25
|
+
<div v-if="item && item.images && image">
|
|
26
|
+
<UPageHeader
|
|
27
|
+
:title="image.title || image.alt"
|
|
28
|
+
v-bind="image.caption ? { description: image.caption } : {}"
|
|
29
|
+
>
|
|
24
30
|
<template #headline>
|
|
25
31
|
<UBreadcrumb
|
|
26
32
|
:items="[
|
|
@@ -15,10 +15,14 @@ const {
|
|
|
15
15
|
const { data: item, status } = await useCollectionItem(collection, slug)
|
|
16
16
|
const { data: surround } = await useCollectionSurround(collection, slug)
|
|
17
17
|
|
|
18
|
+
const itemImage = computed(
|
|
19
|
+
() => (item.value as { image?: string } | null)?.image
|
|
20
|
+
)
|
|
21
|
+
|
|
18
22
|
useSeoMeta({
|
|
19
23
|
title: item.value?.title,
|
|
20
24
|
description: item.value?.description,
|
|
21
|
-
ogImage:
|
|
25
|
+
ogImage: itemImage,
|
|
22
26
|
})
|
|
23
27
|
</script>
|
|
24
28
|
|
|
@@ -26,7 +30,7 @@ useSeoMeta({
|
|
|
26
30
|
<div>
|
|
27
31
|
<USkeleton v-if="status === 'pending'" class="h-96 w-full" />
|
|
28
32
|
<UPage v-else-if="item">
|
|
29
|
-
<UPageHeader :title="item.title"
|
|
33
|
+
<UPageHeader :title="item.title" v-bind="item.description ? { description: item.description } : {}">
|
|
30
34
|
<template v-if="$slots.headline" #headline>
|
|
31
35
|
<slot name="headline" :item="item" />
|
|
32
36
|
</template>
|
|
@@ -45,7 +49,7 @@ useSeoMeta({
|
|
|
45
49
|
</UPageBody>
|
|
46
50
|
|
|
47
51
|
<template v-if="!hideToc" #right>
|
|
48
|
-
<NuxtContentToc
|
|
52
|
+
<NuxtContentToc v-bind="item.body?.toc?.links ? { links: item.body.toc.links } : {}" />
|
|
49
53
|
</template>
|
|
50
54
|
</UPage>
|
|
51
55
|
<div v-else class="text-muted text-center py-12">{{ notFoundMessage }}</div>
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
<!-- eslint-disable vue/require-default-prop -->
|
|
2
2
|
<script setup lang="ts">
|
|
3
|
+
import type { TocLink } from '@nuxt/content'
|
|
4
|
+
|
|
3
5
|
const { links, title = 'Table of Contents' } = defineProps<{
|
|
4
|
-
links?:
|
|
6
|
+
links?: TocLink[]
|
|
5
7
|
title?: string
|
|
6
8
|
}>()
|
|
7
9
|
</script>
|
|
@@ -20,7 +20,11 @@ const {
|
|
|
20
20
|
</script>
|
|
21
21
|
|
|
22
22
|
<template>
|
|
23
|
-
<UPageCard
|
|
23
|
+
<UPageCard
|
|
24
|
+
:title
|
|
25
|
+
variant="outline"
|
|
26
|
+
v-bind="{ ...(description !== undefined && { description }), ...(to !== undefined && { to }) }"
|
|
27
|
+
>
|
|
24
28
|
<img v-if="image" :src="image" :alt="title" class="w-full h-48 object-cover rounded" />
|
|
25
29
|
<template #footer>
|
|
26
30
|
<div class="flex flex-wrap gap-1.5">
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
import type { PortfolioCollectionItem } from '@nuxt/content'
|
|
3
|
+
|
|
2
4
|
const { slug } = defineProps<{
|
|
3
5
|
slug: string
|
|
4
6
|
}>()
|
|
7
|
+
|
|
8
|
+
const asPortfolio = (item: unknown) => item as PortfolioCollectionItem
|
|
5
9
|
</script>
|
|
6
10
|
|
|
7
11
|
<template>
|
|
@@ -13,13 +17,19 @@ const { slug } = defineProps<{
|
|
|
13
17
|
>
|
|
14
18
|
<template #headline="{ item }">
|
|
15
19
|
<div class="flex flex-wrap items-center gap-2">
|
|
16
|
-
<UBadge v-if="item.client" color="primary" variant="subtle">
|
|
17
|
-
{{ item.client }}
|
|
20
|
+
<UBadge v-if="asPortfolio(item).client" color="primary" variant="subtle">
|
|
21
|
+
{{ asPortfolio(item).client }}
|
|
18
22
|
</UBadge>
|
|
19
|
-
<UBadge v-if="item.year" color="neutral" variant="subtle">
|
|
20
|
-
{{ item.year }}
|
|
23
|
+
<UBadge v-if="asPortfolio(item).year" color="neutral" variant="subtle">
|
|
24
|
+
{{ asPortfolio(item).year }}
|
|
21
25
|
</UBadge>
|
|
22
|
-
<UBadge
|
|
26
|
+
<UBadge
|
|
27
|
+
v-for="tag in asPortfolio(item).tags"
|
|
28
|
+
:key="tag"
|
|
29
|
+
color="neutral"
|
|
30
|
+
variant="subtle"
|
|
31
|
+
size="xs"
|
|
32
|
+
>
|
|
23
33
|
{{ tag }}
|
|
24
34
|
</UBadge>
|
|
25
35
|
</div>
|
|
@@ -27,8 +37,8 @@ const { slug } = defineProps<{
|
|
|
27
37
|
|
|
28
38
|
<template #links="{ item }">
|
|
29
39
|
<UButton
|
|
30
|
-
v-if="item.url"
|
|
31
|
-
:to="item.url"
|
|
40
|
+
v-if="asPortfolio(item).url"
|
|
41
|
+
:to="asPortfolio(item).url"
|
|
32
42
|
target="_blank"
|
|
33
43
|
icon="i-lucide-external-link"
|
|
34
44
|
variant="outline"
|
|
@@ -38,15 +48,26 @@ const { slug } = defineProps<{
|
|
|
38
48
|
</template>
|
|
39
49
|
|
|
40
50
|
<template #before-content="{ item }">
|
|
41
|
-
<img
|
|
51
|
+
<img
|
|
52
|
+
v-if="asPortfolio(item).image"
|
|
53
|
+
:src="asPortfolio(item).image"
|
|
54
|
+
:alt="item.title"
|
|
55
|
+
class="w-full rounded-lg mb-8"
|
|
56
|
+
/>
|
|
42
57
|
</template>
|
|
43
58
|
|
|
44
59
|
<template #after-content="{ item }">
|
|
45
|
-
<template v-if="item.colors?.length || item.typography?.length">
|
|
60
|
+
<template v-if="asPortfolio(item).colors?.length || asPortfolio(item).typography?.length">
|
|
46
61
|
<USeparator class="my-8" />
|
|
47
62
|
<div class="space-y-8">
|
|
48
|
-
<PortfolioColorPalette
|
|
49
|
-
|
|
63
|
+
<PortfolioColorPalette
|
|
64
|
+
v-if="asPortfolio(item).colors?.length"
|
|
65
|
+
:colors="asPortfolio(item).colors!"
|
|
66
|
+
/>
|
|
67
|
+
<PortfolioTypography
|
|
68
|
+
v-if="asPortfolio(item).typography?.length"
|
|
69
|
+
:typography="asPortfolio(item).typography!"
|
|
70
|
+
/>
|
|
50
71
|
</div>
|
|
51
72
|
</template>
|
|
52
73
|
</template>
|
|
@@ -19,9 +19,11 @@ const { data: items, status } = await usePortfolioItems(options)
|
|
|
19
19
|
v-for="item in items"
|
|
20
20
|
:key="item.id"
|
|
21
21
|
:title="item.title"
|
|
22
|
-
:description="item.description"
|
|
23
|
-
:to="item.path"
|
|
24
22
|
variant="outline"
|
|
23
|
+
v-bind="{
|
|
24
|
+
...(item.description !== undefined && { description: item.description }),
|
|
25
|
+
to: item.path,
|
|
26
|
+
}"
|
|
25
27
|
>
|
|
26
28
|
<img
|
|
27
29
|
v-if="item.image"
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import type { Collections } from '@nuxt/content'
|
|
2
|
+
|
|
1
3
|
export function useCollectionItem(collection: string, slug: string) {
|
|
2
4
|
return useContentData(`${collection}-${slug}`, () =>
|
|
3
|
-
queryCollection(collection).path(`/${collection}/${slug}`).first()
|
|
5
|
+
queryCollection(collection as keyof Collections).path(`/${collection}/${slug}`).first()
|
|
4
6
|
)
|
|
5
7
|
}
|
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
fields: string[] = ['description']
|
|
5
|
-
) {
|
|
1
|
+
import type { PageCollections } from '@nuxt/content'
|
|
2
|
+
|
|
3
|
+
export function useCollectionSurround(collection: string, slug: string) {
|
|
6
4
|
return useContentData(`${collection}-${slug}-surround`, () =>
|
|
7
|
-
queryCollectionItemSurroundings(collection, `/${collection}/${slug}
|
|
5
|
+
queryCollectionItemSurroundings(collection as keyof PageCollections, `/${collection}/${slug}`)
|
|
8
6
|
)
|
|
9
7
|
}
|
package/layers/core/app/app.vue
CHANGED
|
@@ -78,15 +78,14 @@ export function useCache() {
|
|
|
78
78
|
|
|
79
79
|
try {
|
|
80
80
|
const cacheNames = await caches.keys()
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
: ''
|
|
81
|
+
await Promise.all(cacheNames.map((name) => caches.delete(name)))
|
|
82
|
+
if (process.env.NODE_ENV === 'development') {
|
|
83
|
+
console.log('[useCache] All caches cleared')
|
|
84
|
+
}
|
|
86
85
|
} catch (error) {
|
|
87
|
-
process.env.NODE_ENV === 'development'
|
|
88
|
-
|
|
89
|
-
|
|
86
|
+
if (process.env.NODE_ENV === 'development') {
|
|
87
|
+
console.error('[useCache] Failed to clear cache:', error)
|
|
88
|
+
}
|
|
90
89
|
}
|
|
91
90
|
}
|
|
92
91
|
|
|
@@ -77,18 +77,6 @@ function clampElement(el: HTMLElement) {
|
|
|
77
77
|
clampedCount.value++
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
function restoreElement(el: HTMLElement) {
|
|
81
|
-
const saved = clampedElements.get(el)
|
|
82
|
-
if (!saved) return
|
|
83
|
-
|
|
84
|
-
el.style.transition = saved.transition
|
|
85
|
-
el.style.maxWidth = saved.maxWidth
|
|
86
|
-
el.style.boxSizing = saved.boxSizing
|
|
87
|
-
el.style.overflowX = saved.overflowX
|
|
88
|
-
|
|
89
|
-
clampedElements.delete(el)
|
|
90
|
-
clampedSet.delete(el)
|
|
91
|
-
}
|
|
92
80
|
|
|
93
81
|
function guard() {
|
|
94
82
|
if (!isEnabled.value || !opts) return
|
|
@@ -130,13 +130,14 @@ export default defineNuxtPlugin((nuxtApp) => {
|
|
|
130
130
|
}
|
|
131
131
|
|
|
132
132
|
// Test environment access (works on both server and client)
|
|
133
|
-
const env = useEnv()
|
|
133
|
+
const env = useEnv() as unknown as Record<string, unknown>
|
|
134
134
|
|
|
135
135
|
if (isDev) {
|
|
136
|
+
const publicConfig = env.public as Record<string, unknown> | undefined
|
|
136
137
|
// eslint-disable-next-line no-console
|
|
137
138
|
console.log('[Core Layer] Environment config loaded:', {
|
|
138
|
-
hasPublicConfig: !!
|
|
139
|
-
publicKeys: Object.keys(
|
|
139
|
+
hasPublicConfig: !!publicConfig,
|
|
140
|
+
publicKeys: Object.keys(publicConfig ?? {}),
|
|
140
141
|
})
|
|
141
142
|
}
|
|
142
143
|
} catch (error) {
|
package/layers/core/package.json
CHANGED
|
@@ -17,11 +17,7 @@ const schema = z.object({
|
|
|
17
17
|
|
|
18
18
|
type FormState = z.infer<typeof schema>
|
|
19
19
|
|
|
20
|
-
const state = reactive
|
|
21
|
-
name: undefined,
|
|
22
|
-
email: undefined,
|
|
23
|
-
message: undefined,
|
|
24
|
-
})
|
|
20
|
+
const state = reactive({ name: '', email: '', message: '' })
|
|
25
21
|
|
|
26
22
|
const toast = useToast()
|
|
27
23
|
const isLoading = ref(false)
|
|
@@ -41,6 +41,36 @@ const resolvedIcon = computed(() => icon ?? config.value.icon)
|
|
|
41
41
|
const isTextarea = computed(() => config.value.component === 'UTextarea')
|
|
42
42
|
const isNumber = computed(() => config.value.component === 'UInputNumber')
|
|
43
43
|
|
|
44
|
+
const formFieldProps = computed(() => ({
|
|
45
|
+
name,
|
|
46
|
+
required,
|
|
47
|
+
size,
|
|
48
|
+
...(label !== undefined && { label }),
|
|
49
|
+
...(className !== undefined && { class: className }),
|
|
50
|
+
}))
|
|
51
|
+
|
|
52
|
+
const baseInputProps = computed(() => ({
|
|
53
|
+
size,
|
|
54
|
+
...(resolvedPlaceholder.value !== undefined && { placeholder: resolvedPlaceholder.value }),
|
|
55
|
+
...(resolvedIcon.value !== undefined && { leadingIcon: resolvedIcon.value }),
|
|
56
|
+
}))
|
|
57
|
+
|
|
58
|
+
const numberInputProps = computed(() => ({
|
|
59
|
+
size,
|
|
60
|
+
...(resolvedPlaceholder.value !== undefined && { placeholder: resolvedPlaceholder.value }),
|
|
61
|
+
...(resolvedIcon.value !== undefined && { leadingIcon: resolvedIcon.value }),
|
|
62
|
+
...(currencyOptions.value !== undefined && { formatOptions: currencyOptions.value }),
|
|
63
|
+
}))
|
|
64
|
+
|
|
65
|
+
const textInputProps = computed(() => ({
|
|
66
|
+
type: config.value.inputType,
|
|
67
|
+
size,
|
|
68
|
+
...(config.value.inputMode !== undefined && { inputmode: config.value.inputMode }),
|
|
69
|
+
...(config.value.autocomplete !== undefined && { autocomplete: config.value.autocomplete }),
|
|
70
|
+
...(resolvedPlaceholder.value !== undefined && { placeholder: resolvedPlaceholder.value }),
|
|
71
|
+
...(resolvedIcon.value !== undefined && { leadingIcon: resolvedIcon.value }),
|
|
72
|
+
}))
|
|
73
|
+
|
|
44
74
|
const currencyOptions = computed((): Intl.NumberFormatOptions | undefined => {
|
|
45
75
|
if (config.value.format === 'currency') {
|
|
46
76
|
return {
|
|
@@ -53,33 +83,15 @@ const currencyOptions = computed((): Intl.NumberFormatOptions | undefined => {
|
|
|
53
83
|
</script>
|
|
54
84
|
|
|
55
85
|
<template>
|
|
56
|
-
<UFormField
|
|
57
|
-
<UTextarea
|
|
58
|
-
v-if="isTextarea"
|
|
59
|
-
v-model="model as string"
|
|
60
|
-
:placeholder="resolvedPlaceholder"
|
|
61
|
-
:size
|
|
62
|
-
autoresize
|
|
63
|
-
/>
|
|
86
|
+
<UFormField v-bind="formFieldProps">
|
|
87
|
+
<UTextarea v-if="isTextarea" v-model="model as string" autoresize v-bind="baseInputProps" />
|
|
64
88
|
|
|
65
89
|
<UInputNumber
|
|
66
90
|
v-else-if="isNumber"
|
|
67
91
|
v-model="model as number"
|
|
68
|
-
|
|
69
|
-
:leading-icon="resolvedIcon"
|
|
70
|
-
:size
|
|
71
|
-
:format-options="currencyOptions"
|
|
92
|
+
v-bind="numberInputProps"
|
|
72
93
|
/>
|
|
73
94
|
|
|
74
|
-
<UInput
|
|
75
|
-
v-else
|
|
76
|
-
v-model="model as string"
|
|
77
|
-
:type="config.inputType"
|
|
78
|
-
:inputmode="config.inputMode"
|
|
79
|
-
:autocomplete="config.autocomplete"
|
|
80
|
-
:placeholder="resolvedPlaceholder"
|
|
81
|
-
:leading-icon="resolvedIcon"
|
|
82
|
-
:size
|
|
83
|
-
/>
|
|
95
|
+
<UInput v-else v-model="model as string" v-bind="textInputProps" />
|
|
84
96
|
</UFormField>
|
|
85
97
|
</template>
|
|
@@ -78,7 +78,13 @@ provide('pageTitle', title)
|
|
|
78
78
|
<!-- Optional visible header -->
|
|
79
79
|
<LayoutSection v-if="showHeader">
|
|
80
80
|
<LayoutGridItem :preset="headerPreset">
|
|
81
|
-
<LayoutPageHeader
|
|
81
|
+
<LayoutPageHeader
|
|
82
|
+
:title
|
|
83
|
+
v-bind="{
|
|
84
|
+
...(description !== undefined && { description }),
|
|
85
|
+
...(back !== undefined && { back }),
|
|
86
|
+
}"
|
|
87
|
+
/>
|
|
82
88
|
</LayoutGridItem>
|
|
83
89
|
</LayoutSection>
|
|
84
90
|
|
|
@@ -60,7 +60,10 @@ provide('pageTitle', title)
|
|
|
60
60
|
<!-- Optional visible page header — rendered as a grid section -->
|
|
61
61
|
<LayoutSection v-if="showHeader">
|
|
62
62
|
<LayoutGridItem preset="centered">
|
|
63
|
-
<LayoutPageHeader
|
|
63
|
+
<LayoutPageHeader
|
|
64
|
+
:title
|
|
65
|
+
v-bind="description !== undefined ? { description } : {}"
|
|
66
|
+
/>
|
|
64
67
|
</LayoutGridItem>
|
|
65
68
|
</LayoutSection>
|
|
66
69
|
|