rimelight-components 2.0.98 → 2.1.1
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/README.md +44 -45
- package/dist/module.json +1 -1
- package/dist/module.mjs +8 -7
- package/dist/runtime/app.config.d.ts +4 -2
- package/dist/runtime/components/app/ScrollToTop.vue +1 -3
- package/dist/runtime/components/blocks/Block.vue +1 -3
- package/dist/runtime/components/blocks/TextRenderer.vue +3 -5
- package/dist/runtime/components/blocks/editor/ImageBlockEditor.vue +2 -6
- package/dist/runtime/components/blocks/editor/ParagraphBlockEditor.vue +6 -7
- package/dist/runtime/components/blocks/editor/SectionBlockEditor.d.vue.ts +1 -1
- package/dist/runtime/components/blocks/editor/SectionBlockEditor.vue +21 -24
- package/dist/runtime/components/blocks/editor/SectionBlockEditor.vue.d.ts +1 -1
- package/dist/runtime/components/blocks/renderer/SectionBlockRenderer.vue +1 -6
- package/dist/runtime/components/blocks/renderer/TestBlockRenderer.vue +1 -1
- package/dist/runtime/components/content/Callout.vue +1 -4
- package/dist/runtime/components/content/Section.vue +8 -10
- package/dist/runtime/components/page/PageEditor.d.vue.ts +2 -2
- package/dist/runtime/components/page/PageEditor.vue +64 -26
- package/dist/runtime/components/page/PageEditor.vue.d.ts +2 -2
- package/dist/runtime/components/page/PageMention.d.vue.ts +6 -0
- package/dist/runtime/components/page/PageMention.vue +42 -0
- package/dist/runtime/components/page/PageMention.vue.d.ts +6 -0
- package/dist/runtime/components/page/PagePropertiesEditor.d.vue.ts +1 -1
- package/dist/runtime/components/page/PagePropertiesEditor.vue +23 -37
- package/dist/runtime/components/page/PagePropertiesEditor.vue.d.ts +1 -1
- package/dist/runtime/components/page/PagePropertiesRenderer.d.vue.ts +12 -2
- package/dist/runtime/components/page/PagePropertiesRenderer.vue +215 -78
- package/dist/runtime/components/page/PagePropertiesRenderer.vue.d.ts +12 -2
- package/dist/runtime/components/page/PageRenderer.d.vue.ts +25 -0
- package/dist/runtime/components/page/PageRenderer.vue +123 -0
- package/dist/runtime/components/page/PageRenderer.vue.d.ts +25 -0
- package/dist/runtime/components/page/PageSurround.vue +9 -9
- package/dist/runtime/components/{blocks/TOC.d.vue.ts → page/PageTOC.d.vue.ts} +5 -8
- package/dist/runtime/components/page/PageTOC.vue +107 -0
- package/dist/runtime/components/{blocks/TOC.vue.d.ts → page/PageTOC.vue.d.ts} +5 -8
- package/dist/runtime/components/swatches/ColorSwatch.vue +1 -4
- package/dist/runtime/composables/index.d.ts +1 -0
- package/dist/runtime/composables/index.js +1 -0
- package/dist/runtime/composables/useBlockEditor.d.ts +173 -517
- package/dist/runtime/composables/usePageEditor.js +7 -3
- package/dist/runtime/composables/usePageRegistry.d.ts +33 -0
- package/dist/runtime/composables/usePageRegistry.js +16 -0
- package/dist/runtime/types/blocks.d.ts +1 -3
- package/dist/runtime/types/pages.d.ts +18 -9
- package/dist/runtime/types/schemas.d.ts +6 -0
- package/dist/runtime/types/schemas.js +3 -1
- package/dist/runtime/utils/page.d.ts +17 -3
- package/dist/runtime/utils/richTextHelpers.js +9 -1
- package/package.json +17 -17
- package/dist/runtime/components/blocks/TOC.vue +0 -95
- package/dist/runtime/components/nodes/MentionNode.d.vue.ts +0 -8
- package/dist/runtime/components/nodes/MentionNode.vue +0 -22
- package/dist/runtime/components/nodes/MentionNode.vue.d.ts +0 -8
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
-
import { computed } from "vue";
|
|
3
2
|
import { useI18n } from "vue-i18n";
|
|
3
|
+
import { usePageRegistry } from "../../composables";
|
|
4
|
+
import { getLocalizedContent } from "../../utils";
|
|
4
5
|
import {} from "../../types";
|
|
6
|
+
const { getTypeLabelKey } = usePageRegistry();
|
|
5
7
|
const page = defineModel({ type: null, ...{ required: true } });
|
|
6
|
-
const locale = useI18n()
|
|
7
|
-
const properties = computed(() => page.value.properties);
|
|
8
|
-
const groups = computed(() => {
|
|
9
|
-
return page.value.properties;
|
|
10
|
-
});
|
|
8
|
+
const { locale, t } = useI18n();
|
|
11
9
|
const isFieldVisible = (fieldSchema) => {
|
|
12
10
|
if (!fieldSchema.visibleIf) return true;
|
|
13
11
|
return fieldSchema.visibleIf(page.value.properties);
|
|
@@ -18,17 +16,18 @@ const getSortedFields = (fields) => {
|
|
|
18
16
|
</script>
|
|
19
17
|
|
|
20
18
|
<template>
|
|
21
|
-
<
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
19
|
+
<UCard
|
|
20
|
+
variant="soft"
|
|
21
|
+
:ui="{ root: 'divide-none', header: 'bg-elevated text-center', body: 'bg-muted' }"
|
|
22
|
+
>
|
|
23
|
+
<template #header>
|
|
24
|
+
<h3>
|
|
25
|
+
{{ getLocalizedContent(page.title, locale) }}
|
|
25
26
|
</h3>
|
|
26
|
-
<UBadge variant="subtle" size="sm" color="primary"
|
|
27
|
-
|
|
28
|
-
</UBadge>
|
|
29
|
-
</div>
|
|
27
|
+
<UBadge variant="subtle" size="sm" color="primary" :label="t(getTypeLabelKey(page.type))" />
|
|
28
|
+
</template>
|
|
30
29
|
|
|
31
|
-
<div v-for="(group, groupId) in
|
|
30
|
+
<div v-for="(group, groupId) in page.properties" :key="groupId">
|
|
32
31
|
<div class="flex items-center gap-3">
|
|
33
32
|
<span class="text-[10px] font-bold uppercase tracking-widest text-primary">
|
|
34
33
|
{{ group.label[locale] }}
|
|
@@ -38,36 +37,36 @@ const getSortedFields = (fields) => {
|
|
|
38
37
|
|
|
39
38
|
<div class="grid gap-y-4 px-1">
|
|
40
39
|
<template v-for="[fieldKey, schema] in getSortedFields(group.fields)" :key="fieldKey">
|
|
41
|
-
|
|
42
40
|
<UFormField
|
|
43
41
|
v-if="isFieldVisible(schema)"
|
|
44
|
-
:label="schema.label"
|
|
42
|
+
:label="getLocalizedContent(schema.label, locale)"
|
|
45
43
|
:name="fieldKey"
|
|
46
44
|
>
|
|
47
45
|
<UInput
|
|
48
46
|
v-if="schema.type === 'text'"
|
|
49
|
-
v-model="
|
|
47
|
+
v-model="schema.value[locale]"
|
|
50
48
|
variant="subtle"
|
|
51
49
|
placeholder="..."
|
|
52
50
|
/>
|
|
53
51
|
|
|
54
52
|
<UInput
|
|
55
53
|
v-else-if="schema.type === 'number'"
|
|
56
|
-
v-model.number="
|
|
54
|
+
v-model.number="schema.value"
|
|
57
55
|
type="number"
|
|
58
56
|
variant="subtle"
|
|
59
57
|
/>
|
|
60
58
|
|
|
61
59
|
<USelect
|
|
62
60
|
v-else-if="schema.type === 'enum'"
|
|
63
|
-
v-model="
|
|
61
|
+
v-model="schema.value"
|
|
64
62
|
:items="schema.options || []"
|
|
65
63
|
variant="subtle"
|
|
66
64
|
/>
|
|
67
65
|
|
|
68
66
|
<UInputMenu
|
|
69
67
|
v-else-if="schema.type === 'text-array'"
|
|
70
|
-
|
|
68
|
+
:model-value="schema.value.map((v) => v[locale])"
|
|
69
|
+
@update:model-value="(vals) => schema.value = vals.map((str) => ({ [locale]: str }))"
|
|
71
70
|
multiple
|
|
72
71
|
creatable
|
|
73
72
|
variant="subtle"
|
|
@@ -76,7 +75,7 @@ const getSortedFields = (fields) => {
|
|
|
76
75
|
|
|
77
76
|
<UInput
|
|
78
77
|
v-else-if="schema.type === 'page'"
|
|
79
|
-
v-model="
|
|
78
|
+
v-model="schema.value"
|
|
80
79
|
icon="lucide:link-2"
|
|
81
80
|
variant="subtle"
|
|
82
81
|
:placeholder="`Select ${schema.allowedPageTypes?.join('/')}`"
|
|
@@ -86,7 +85,7 @@ const getSortedFields = (fields) => {
|
|
|
86
85
|
</div>
|
|
87
86
|
</div>
|
|
88
87
|
|
|
89
|
-
<USeparator />
|
|
88
|
+
<USeparator class="my-6" />
|
|
90
89
|
|
|
91
90
|
<UFormField label="Global Search Tags">
|
|
92
91
|
<UInputMenu
|
|
@@ -98,18 +97,5 @@ const getSortedFields = (fields) => {
|
|
|
98
97
|
placeholder="Add tags..."
|
|
99
98
|
/>
|
|
100
99
|
</UFormField>
|
|
101
|
-
|
|
102
|
-
<div class="mt-auto pt-6 text-[9px] font-mono text-muted-foreground/60 space-y-1">
|
|
103
|
-
<div class="flex justify-between">
|
|
104
|
-
<span>ID:</span>
|
|
105
|
-
<span>{{ page.id }}</span>
|
|
106
|
-
</div>
|
|
107
|
-
<ClientOnly>
|
|
108
|
-
<div class="flex justify-between">
|
|
109
|
-
<span>Last Updated:</span>
|
|
110
|
-
<span>{{ page.updated_at }}</span>
|
|
111
|
-
</div>
|
|
112
|
-
</ClientOnly>
|
|
113
|
-
</div>
|
|
114
|
-
</div>
|
|
100
|
+
</UCard>
|
|
115
101
|
</template>
|
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
import { type Page } from '../../types/index.js';
|
|
2
|
-
type
|
|
2
|
+
type __VLS_ModelProps = {
|
|
3
3
|
modelValue: Page;
|
|
4
4
|
};
|
|
5
|
-
declare const __VLS_export: import("vue").DefineComponent<
|
|
5
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
6
|
+
"update:modelValue": (value: {
|
|
7
|
+
type: "Default";
|
|
8
|
+
properties: import("../../types/index.js").BasePageProperties;
|
|
9
|
+
} & import("../../types/index.js").BasePage) => any;
|
|
10
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_ModelProps> & Readonly<{
|
|
11
|
+
"onUpdate:modelValue"?: ((value: {
|
|
12
|
+
type: "Default";
|
|
13
|
+
properties: import("../../types/index.js").BasePageProperties;
|
|
14
|
+
} & import("../../types/index.js").BasePage) => any) | undefined;
|
|
15
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
6
16
|
declare const _default: typeof __VLS_export;
|
|
7
17
|
export default _default;
|
|
@@ -1,99 +1,236 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import { computed } from "vue";
|
|
3
|
+
import { getLocalizedContent } from "../../utils";
|
|
4
|
+
import { usePageRegistry } from "../../composables";
|
|
5
|
+
import { useToast } from "@nuxt/ui/composables";
|
|
6
|
+
import {} from "@nuxt/ui/components/Tabs.vue";
|
|
3
7
|
import { useI18n } from "vue-i18n";
|
|
4
|
-
import {
|
|
5
|
-
} from "../../types";
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
});
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
8
|
+
import { useShare, useClipboard } from "@vueuse/core";
|
|
9
|
+
import {} from "../../types";
|
|
10
|
+
const { getTypeLabelKey } = usePageRegistry();
|
|
11
|
+
const page = defineModel({ type: null, ...{ required: true } });
|
|
12
|
+
const { t, locale } = useI18n();
|
|
13
|
+
const { share } = useShare();
|
|
14
|
+
const { copy } = useClipboard();
|
|
15
|
+
const toast = useToast();
|
|
16
|
+
const shouldRenderField = (schema) => {
|
|
17
|
+
const isVisible = !schema.visibleIf || schema.visibleIf(page.value.properties);
|
|
18
|
+
if (!isVisible) return false;
|
|
19
|
+
const val = schema.value;
|
|
20
|
+
if (schema.type === "text") {
|
|
21
|
+
return !!val?.[locale.value];
|
|
22
|
+
}
|
|
23
|
+
if (schema.type === "text-array") {
|
|
24
|
+
return Array.isArray(val) && val.length > 0;
|
|
25
|
+
}
|
|
26
|
+
return val !== void 0 && val !== null && val !== "";
|
|
27
|
+
};
|
|
28
|
+
const shouldRenderGroup = (group) => {
|
|
29
|
+
return Object.values(group.fields || {}).some((schema) => shouldRenderField(schema));
|
|
30
|
+
};
|
|
31
|
+
const getSortedFields = (fields) => {
|
|
32
|
+
return Object.entries(fields).sort(([, a], [, b]) => (a.order ?? 0) - (b.order ?? 0));
|
|
33
|
+
};
|
|
34
|
+
const sharePage = async () => {
|
|
35
|
+
if (!page.value) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
try {
|
|
39
|
+
await share({
|
|
40
|
+
title: getLocalizedContent(page.value.title, locale),
|
|
41
|
+
text: getLocalizedContent(page.value.description, locale),
|
|
42
|
+
url: typeof location !== "undefined" ? location.href : ""
|
|
43
|
+
});
|
|
44
|
+
} catch {
|
|
45
|
+
toast.add({
|
|
46
|
+
color: "error",
|
|
47
|
+
title: "toast_share-page_error_title",
|
|
48
|
+
description: "toast_share-page_error_description"
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
const copyLink = async () => {
|
|
53
|
+
try {
|
|
54
|
+
await copy(typeof location !== "undefined" ? location.href : "");
|
|
55
|
+
toast.add({
|
|
56
|
+
color: "success",
|
|
57
|
+
title: "toast_copy-page-link_success_title",
|
|
58
|
+
description: typeof location !== "undefined" ? location.href : ""
|
|
59
|
+
});
|
|
60
|
+
} catch {
|
|
61
|
+
toast.add({
|
|
62
|
+
color: "error",
|
|
63
|
+
title: "toast_copy-page-link_error_title",
|
|
64
|
+
description: "toast_copy-page-link_error_description"
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
const imageTabs = computed(() => {
|
|
69
|
+
if (!page.value.images?.length) return [];
|
|
70
|
+
return page.value.images.map((img, index) => {
|
|
71
|
+
const localizedName = getLocalizedContent(img.name, locale.value);
|
|
20
72
|
return {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
73
|
+
label: localizedName || `${t("label_image")} ${index + 1}`,
|
|
74
|
+
value: `image-${index}`,
|
|
75
|
+
img
|
|
24
76
|
};
|
|
25
|
-
})
|
|
77
|
+
});
|
|
26
78
|
});
|
|
27
79
|
</script>
|
|
28
80
|
|
|
29
81
|
<template>
|
|
30
|
-
<
|
|
31
|
-
<div class="
|
|
32
|
-
<
|
|
33
|
-
|
|
34
|
-
</div>
|
|
35
|
-
<h2 class="text-xl font-serif font-bold italic text-foreground">
|
|
36
|
-
{{ properties.name?.value?.[locale] || props.modelValue.slug }}
|
|
37
|
-
</h2>
|
|
82
|
+
<aside class="flex flex-col gap-md">
|
|
83
|
+
<div class="flex flex-row flex-wrap gap-sm">
|
|
84
|
+
<UButton variant="soft" color="neutral" icon="lucide:share" size="sm" @click="sharePage()" />
|
|
85
|
+
<UButton variant="soft" color="neutral" icon="lucide:link" size="sm" @click="copyLink()" />
|
|
38
86
|
</div>
|
|
87
|
+
<UCard
|
|
88
|
+
variant="soft"
|
|
89
|
+
:ui="{ root: 'divide-none', header: 'bg-accented text-center', body: 'p-0 sm:p-0 bg-muted' }"
|
|
90
|
+
>
|
|
91
|
+
<template #header>
|
|
92
|
+
<div class="flex flex-col gap-xs items-center">
|
|
93
|
+
<NuxtImg
|
|
94
|
+
v-if="page.icon?.src"
|
|
95
|
+
:src="page.icon?.src"
|
|
96
|
+
:alt="page.icon?.alt"
|
|
97
|
+
class="rounded-full w-12 h-12 object-cover"
|
|
98
|
+
/>
|
|
99
|
+
|
|
100
|
+
<h3>
|
|
101
|
+
{{ getLocalizedContent(page.title, locale) }}
|
|
102
|
+
</h3>
|
|
39
103
|
|
|
40
|
-
|
|
41
|
-
<div class="bg-muted/30 px-3 py-1.5 text-[10px] font-black text-primary uppercase tracking-tighter border-b border-border/10">
|
|
42
|
-
{{ group.label[locale] }}
|
|
43
|
-
</div>
|
|
104
|
+
<span class="text-sm">{{ t(getTypeLabelKey(page.type)) }}</span>
|
|
44
105
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
106
|
+
<div v-if="page.tags?.length" class="flex flex-row flex-wrap gap-xs">
|
|
107
|
+
<UBadge
|
|
108
|
+
v-for="tag in page.tags"
|
|
109
|
+
:key="tag[locale]"
|
|
110
|
+
variant="soft"
|
|
111
|
+
size="xs"
|
|
112
|
+
color="neutral"
|
|
113
|
+
>
|
|
114
|
+
{{ tag[locale] }}
|
|
115
|
+
</UBadge>
|
|
116
|
+
</div>
|
|
54
117
|
|
|
55
|
-
<
|
|
56
|
-
<
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
118
|
+
<div v-if="page.images?.length" class="w-full">
|
|
119
|
+
<UTabs
|
|
120
|
+
v-if="page.images.length > 1"
|
|
121
|
+
:items="imageTabs"
|
|
122
|
+
default-value="image-0"
|
|
123
|
+
variant="link"
|
|
124
|
+
size="xs"
|
|
125
|
+
color="neutral"
|
|
126
|
+
class="w-full"
|
|
127
|
+
>
|
|
128
|
+
<template #content="{ item }">
|
|
129
|
+
<NuxtImg :src="item.img.src" :alt="item.img.alt" class="w-full object-cover" />
|
|
60
130
|
</template>
|
|
131
|
+
</UTabs>
|
|
132
|
+
|
|
133
|
+
<div v-else-if="page.images[0]">
|
|
134
|
+
<NuxtImg
|
|
135
|
+
:src="page.images[0].src"
|
|
136
|
+
:alt="page.images[0].alt"
|
|
137
|
+
class="w-full object-cover"
|
|
138
|
+
/>
|
|
61
139
|
</div>
|
|
140
|
+
</div>
|
|
141
|
+
</div>
|
|
142
|
+
</template>
|
|
62
143
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
144
|
+
<template #default>
|
|
145
|
+
<template v-for="(group, groupId) in page.properties" :key="groupId">
|
|
146
|
+
<UCollapsible v-if="shouldRenderGroup(group)" :default-open="group.defaultOpen">
|
|
147
|
+
<template #default>
|
|
148
|
+
<UButton
|
|
149
|
+
:label="getLocalizedContent(group.label, locale)"
|
|
150
|
+
variant="soft"
|
|
151
|
+
trailing-icon="lucide:chevron-down"
|
|
152
|
+
:ui="{
|
|
153
|
+
trailingIcon: 'group-data-[state=open]:rotate-180 transition-transform duration-200'
|
|
154
|
+
}"
|
|
155
|
+
block
|
|
156
|
+
class="group rounded-none bg-elevated text-default"
|
|
157
|
+
/>
|
|
158
|
+
</template>
|
|
66
159
|
|
|
67
|
-
<
|
|
68
|
-
|
|
69
|
-
|
|
160
|
+
<template #content>
|
|
161
|
+
<dl class="p-sm flex flex-col gap-xs">
|
|
162
|
+
<template
|
|
163
|
+
v-for="[fieldKey, schema] in getSortedFields(group.fields)"
|
|
164
|
+
:key="fieldKey"
|
|
165
|
+
>
|
|
166
|
+
<div
|
|
167
|
+
v-if="shouldRenderField(schema)"
|
|
168
|
+
class="grid grid-cols-3 gap-xs items-baseline"
|
|
169
|
+
>
|
|
170
|
+
<dt class="text-xs font-semibold text-dimmed">
|
|
171
|
+
{{ getLocalizedContent(schema.label, locale) }}
|
|
172
|
+
</dt>
|
|
70
173
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
174
|
+
<dd class="text-xs col-span-2">
|
|
175
|
+
<span v-if="schema.type === 'text'">
|
|
176
|
+
{{ getLocalizedContent(schema.value, locale) }}
|
|
177
|
+
</span>
|
|
178
|
+
<ul
|
|
179
|
+
v-else-if="schema.type === 'text-array'"
|
|
180
|
+
class="flex flex-wrap list-disc list-inside"
|
|
181
|
+
>
|
|
182
|
+
<li v-for="(item, index) in schema.value" :key="index">
|
|
183
|
+
<span class="font-medium">
|
|
184
|
+
{{ getLocalizedContent(item, locale) }}
|
|
185
|
+
</span>
|
|
186
|
+
</li>
|
|
187
|
+
</ul>
|
|
188
|
+
<RCPageMention
|
|
189
|
+
v-else-if="schema.type === 'page' && schema.value"
|
|
190
|
+
:page-id="schema.value"
|
|
191
|
+
/>
|
|
78
192
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
>
|
|
89
|
-
#{{ tag[locale] }}
|
|
90
|
-
</UBadge>
|
|
91
|
-
</div>
|
|
92
|
-
</div>
|
|
193
|
+
<ul
|
|
194
|
+
v-else-if="schema.type === 'page-array' && Array.isArray(schema.value)"
|
|
195
|
+
class="flex flex-col gap-y-1"
|
|
196
|
+
>
|
|
197
|
+
<li v-for="id in schema.value" :key="id" class="flex items-center gap-x-2">
|
|
198
|
+
<span
|
|
199
|
+
class="w-1 h-1 rounded-full bg-inverted shrink-0"
|
|
200
|
+
aria-hidden="true"
|
|
201
|
+
/>
|
|
93
202
|
|
|
94
|
-
|
|
95
|
-
</
|
|
203
|
+
<RCPageMention :page-id="id" />
|
|
204
|
+
</li>
|
|
205
|
+
</ul>
|
|
96
206
|
|
|
97
|
-
<
|
|
98
|
-
|
|
99
|
-
</
|
|
207
|
+
<span v-else>
|
|
208
|
+
{{ schema.value }}
|
|
209
|
+
</span>
|
|
210
|
+
</dd>
|
|
211
|
+
</div>
|
|
212
|
+
</template>
|
|
213
|
+
</dl>
|
|
214
|
+
</template>
|
|
215
|
+
</UCollapsible>
|
|
216
|
+
</template>
|
|
217
|
+
</template>
|
|
218
|
+
</UCard>
|
|
219
|
+
<div class="flex flex-col gap-xs">
|
|
220
|
+
<h6>Links</h6>
|
|
221
|
+
<UButton
|
|
222
|
+
v-for="(link, index) in page.links"
|
|
223
|
+
:key="index"
|
|
224
|
+
:label="link.label"
|
|
225
|
+
:icon="link.icon"
|
|
226
|
+
:to="link.to"
|
|
227
|
+
:target="link.to ? '_blank' : void 0"
|
|
228
|
+
:external="!!link.to"
|
|
229
|
+
:variant="link.variant || 'link'"
|
|
230
|
+
:color="link.color || 'neutral'"
|
|
231
|
+
size="sm"
|
|
232
|
+
:ui="{ base: 'pl-0' }"
|
|
233
|
+
/>
|
|
234
|
+
</div>
|
|
235
|
+
</aside>
|
|
236
|
+
</template>
|
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
import { type Page } from '../../types/index.js';
|
|
2
|
-
type
|
|
2
|
+
type __VLS_ModelProps = {
|
|
3
3
|
modelValue: Page;
|
|
4
4
|
};
|
|
5
|
-
declare const __VLS_export: import("vue").DefineComponent<
|
|
5
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
6
|
+
"update:modelValue": (value: {
|
|
7
|
+
type: "Default";
|
|
8
|
+
properties: import("../../types/index.js").BasePageProperties;
|
|
9
|
+
} & import("../../types/index.js").BasePage) => any;
|
|
10
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_ModelProps> & Readonly<{
|
|
11
|
+
"onUpdate:modelValue"?: ((value: {
|
|
12
|
+
type: "Default";
|
|
13
|
+
properties: import("../../types/index.js").BasePageProperties;
|
|
14
|
+
} & import("../../types/index.js").BasePage) => any) | undefined;
|
|
15
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
6
16
|
declare const _default: typeof __VLS_export;
|
|
7
17
|
export default _default;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type Page, type PageSurround } from "../../types/index.js";
|
|
2
|
+
interface PageRendererProps {
|
|
3
|
+
useSurround?: boolean;
|
|
4
|
+
surround?: PageSurround | null;
|
|
5
|
+
surroundStatus?: 'idle' | 'pending' | 'success' | 'error';
|
|
6
|
+
resolvePage?: (id: string) => Promise<Pick<Page, 'title' | 'icon' | 'slug'>>;
|
|
7
|
+
}
|
|
8
|
+
type __VLS_Props = PageRendererProps;
|
|
9
|
+
type __VLS_ModelProps = {
|
|
10
|
+
modelValue: Page;
|
|
11
|
+
};
|
|
12
|
+
type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
|
|
13
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
14
|
+
"update:modelValue": (value: {
|
|
15
|
+
type: "Default";
|
|
16
|
+
properties: import("../../types/index.js").BasePageProperties;
|
|
17
|
+
} & import("../../types/index.js").BasePage) => any;
|
|
18
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
19
|
+
"onUpdate:modelValue"?: ((value: {
|
|
20
|
+
type: "Default";
|
|
21
|
+
properties: import("../../types/index.js").BasePageProperties;
|
|
22
|
+
} & import("../../types/index.js").BasePage) => any) | undefined;
|
|
23
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
24
|
+
declare const _default: typeof __VLS_export;
|
|
25
|
+
export default _default;
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { computed, provide } from "vue";
|
|
3
|
+
import {} from "../../types";
|
|
4
|
+
import { getLocalizedContent } from "../../utils";
|
|
5
|
+
import { useI18n } from "vue-i18n";
|
|
6
|
+
import { usePageRegistry } from "../../composables";
|
|
7
|
+
const { getTypeLabelKey } = usePageRegistry();
|
|
8
|
+
const page = defineModel({ type: null, ...{ required: true } });
|
|
9
|
+
const { t, locale } = useI18n();
|
|
10
|
+
const { useSurround = false, surroundStatus = "idle", surround = null, resolvePage } = defineProps({
|
|
11
|
+
useSurround: { type: Boolean, required: false },
|
|
12
|
+
surround: { type: [Object, null], required: false },
|
|
13
|
+
surroundStatus: { type: String, required: false },
|
|
14
|
+
resolvePage: { type: Function, required: false }
|
|
15
|
+
});
|
|
16
|
+
const emit = defineEmits([]);
|
|
17
|
+
provide("page-resolver", resolvePage);
|
|
18
|
+
const previousPage = computed(() => surround?.previous);
|
|
19
|
+
const nextPage = computed(() => surround?.next);
|
|
20
|
+
const hasSurround = computed(() => !!(surround?.previous || surround?.next));
|
|
21
|
+
</script>
|
|
22
|
+
|
|
23
|
+
<template>
|
|
24
|
+
<UContainer class="flex flex-col py-16">
|
|
25
|
+
<div class="grid grid-cols-1 lg:grid-cols-24 gap-xl items-start">
|
|
26
|
+
<RCPageTOC
|
|
27
|
+
:page-blocks="page.blocks"
|
|
28
|
+
:levels="[2, 3, 4]"
|
|
29
|
+
class="hidden lg:flex lg:col-span-4 sticky top-32"
|
|
30
|
+
>
|
|
31
|
+
<template #bottom> </template>
|
|
32
|
+
</RCPageTOC>
|
|
33
|
+
<RCPagePropertiesRenderer v-model="page" class="order-1 lg:order-2 lg:col-span-6" />
|
|
34
|
+
<div class="order-2 lg:order-1 lg:col-span-14 flex flex-col gap-xl">
|
|
35
|
+
<NuxtImg
|
|
36
|
+
v-if="page.banner?.src"
|
|
37
|
+
:src="page.banner?.src"
|
|
38
|
+
:alt="page.banner?.alt"
|
|
39
|
+
class="rounded-xl w-full object-cover"
|
|
40
|
+
/>
|
|
41
|
+
<UPageHeader
|
|
42
|
+
:headline="t(getTypeLabelKey(page.type))"
|
|
43
|
+
:description="getLocalizedContent(page.description, 'en') ?? ''"
|
|
44
|
+
:ui="{ root: 'pt-0' }"
|
|
45
|
+
>
|
|
46
|
+
<template #title>
|
|
47
|
+
<div class="flex flex-row gap-sm">
|
|
48
|
+
<NuxtImg
|
|
49
|
+
v-if="page.icon?.src"
|
|
50
|
+
:src="page.icon?.src"
|
|
51
|
+
:alt="page.icon?.alt"
|
|
52
|
+
class="rounded-full w-12 h-12 object-cover"
|
|
53
|
+
/>
|
|
54
|
+
<h1>{{ getLocalizedContent(page.title, locale) }}</h1>
|
|
55
|
+
</div>
|
|
56
|
+
</template>
|
|
57
|
+
</UPageHeader>
|
|
58
|
+
<RCBlockViewRenderer :blocks="page.blocks" />
|
|
59
|
+
<template v-if="useSurround">
|
|
60
|
+
<div v-if="surroundStatus === 'pending'" class="grid grid-cols-1 gap-md sm:grid-cols-2">
|
|
61
|
+
<USkeleton class="h-48 w-full rounded-xl" />
|
|
62
|
+
<USkeleton class="h-48 w-full rounded-xl" />
|
|
63
|
+
</div>
|
|
64
|
+
|
|
65
|
+
<LazyRCPageSurround
|
|
66
|
+
v-else-if="surroundStatus === 'success' && hasSurround"
|
|
67
|
+
hydrate-on-visible
|
|
68
|
+
:pageType="getTypeLabelKey(page.type)"
|
|
69
|
+
:previousTitle="getLocalizedContent(previousPage?.title, locale)"
|
|
70
|
+
:previousDescription="getLocalizedContent(previousPage?.description, locale)"
|
|
71
|
+
:previousTo="`/${previousPage?.slug}`"
|
|
72
|
+
:nextTitle="getLocalizedContent(nextPage?.title, locale)"
|
|
73
|
+
:nextDescription="getLocalizedContent(nextPage?.description, locale)"
|
|
74
|
+
:nextTo="`/${nextPage?.slug}`"
|
|
75
|
+
/>
|
|
76
|
+
|
|
77
|
+
<USeparator />
|
|
78
|
+
|
|
79
|
+
<div class="flex flex-col gap-xs text-xs text-dimmed p-xl">
|
|
80
|
+
<h6>Metadata</h6>
|
|
81
|
+
<span>Page ID: {{ page.id }}</span>
|
|
82
|
+
<span
|
|
83
|
+
>Created At:
|
|
84
|
+
<NuxtTime
|
|
85
|
+
:datetime="page.created_at ?? ''"
|
|
86
|
+
year="numeric"
|
|
87
|
+
month="numeric"
|
|
88
|
+
day="numeric"
|
|
89
|
+
hour="numeric"
|
|
90
|
+
minute="numeric"
|
|
91
|
+
second="numeric"
|
|
92
|
+
time-zone-name="short"
|
|
93
|
+
/></span>
|
|
94
|
+
<span
|
|
95
|
+
>Posted At:
|
|
96
|
+
<NuxtTime
|
|
97
|
+
:datetime="page.created_at ?? ''"
|
|
98
|
+
year="numeric"
|
|
99
|
+
month="numeric"
|
|
100
|
+
day="numeric"
|
|
101
|
+
hour="numeric"
|
|
102
|
+
minute="numeric"
|
|
103
|
+
second="numeric"
|
|
104
|
+
time-zone-name="short"
|
|
105
|
+
/></span>
|
|
106
|
+
<span
|
|
107
|
+
>Updated At:
|
|
108
|
+
<NuxtTime
|
|
109
|
+
:datetime="page.created_at ?? ''"
|
|
110
|
+
year="numeric"
|
|
111
|
+
month="numeric"
|
|
112
|
+
day="numeric"
|
|
113
|
+
hour="numeric"
|
|
114
|
+
minute="numeric"
|
|
115
|
+
second="numeric"
|
|
116
|
+
time-zone-name="short"
|
|
117
|
+
/></span>
|
|
118
|
+
</div>
|
|
119
|
+
</template>
|
|
120
|
+
</div>
|
|
121
|
+
</div>
|
|
122
|
+
</UContainer>
|
|
123
|
+
</template>
|