rimelight-components 2.0.98 → 2.0.99

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/dist/module.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rimelight-components",
3
- "version": "2.0.98",
3
+ "version": "2.0.99",
4
4
  "docs": "https://rimelight.com/tools/rimelight-components",
5
5
  "configKey": "rimelightComponents",
6
6
  "compatibility": {
package/dist/module.mjs CHANGED
@@ -4,7 +4,7 @@ import { readdirSync } from 'node:fs';
4
4
  import { basename } from 'node:path';
5
5
 
6
6
  const name = "rimelight-components";
7
- const version = "2.0.98";
7
+ const version = "2.0.99";
8
8
  const homepage = "https://rimelight.com/tools/rimelight-components";
9
9
 
10
10
  const defaultOptions = {
@@ -1,8 +1,8 @@
1
1
  import type { Page } from "../../types/index.js";
2
- interface Props {
2
+ interface PageEditorProps {
3
3
  isSaving: boolean;
4
4
  }
5
- type __VLS_Props = Props;
5
+ type __VLS_Props = PageEditorProps;
6
6
  type __VLS_ModelProps = {
7
7
  modelValue: Page;
8
8
  };
@@ -1,8 +1,12 @@
1
1
  <script setup>
2
2
  import { ref, computed, useTemplateRef } from "vue";
3
3
  import { usePageEditor } from "../../composables";
4
+ import { getLocalizedContent } from "../../utils";
4
5
  const page = defineModel({ type: null, ...{ required: true } });
5
6
  const { undo, redo, canUndo, canRedo, captureSnapshot } = usePageEditor(page);
7
+ const { isSaving } = defineProps({
8
+ isSaving: { type: Boolean, required: true }
9
+ });
6
10
  const emit = defineEmits(["save"]);
7
11
  const handleSave = () => {
8
12
  const dataToPersist = JSON.parse(JSON.stringify(page.value));
@@ -14,9 +18,6 @@ defineExpose({
14
18
  canUndo,
15
19
  canRedo
16
20
  });
17
- defineProps({
18
- isSaving: { type: Boolean, required: true }
19
- });
20
21
  const editorRef = useTemplateRef("editor");
21
22
  const showPreview = ref(false);
22
23
  const editorPanelClass = computed(() => ({
@@ -71,33 +72,58 @@ const editorPanelClass = computed(() => ({
71
72
  </div>
72
73
  </template>
73
74
  </UHeader>
74
-
75
- <div class="mt-24 grid gap-xl" :class="showPreview ? 'grid-cols-2' : 'grid-cols-1'">
76
- <UPage :class="editorPanelClass">
77
- <template #default>
78
- <UPageHeader />
75
+ <UContainer class="mt-24 grid gap-xl" :class="showPreview ? 'grid-cols-2 max-w-full' : 'grid-cols-1'">
76
+ <div :class="editorPanelClass" class="grid grid-cols-1 lg:grid-cols-4 gap-8">
77
+ <UPageAside class="order-1 lg:order-2 lg:col-span-1">
78
+ <RCPagePropertiesEditor v-model="page" />
79
+ </UPageAside>
80
+ <div class="order-2 lg:order-1 lg:col-span-3">
81
+ <UPageHeader :title="getLocalizedContent(page.title, 'en')" :description="getLocalizedContent(page.description, 'en') ?? ''" />
79
82
  <RCBlockEditor
80
83
  ref="editor"
81
84
  v-model="page.blocks"
82
85
  :class="editorPanelClass"
83
86
  @mutation="captureSnapshot"
84
87
  />
85
- </template>
86
- <template #right>
87
- <UPageAside>
88
- <RCPagePropertiesEditor v-model="page" />
89
- </UPageAside>
90
- </template>
91
- </UPage>
92
- <UPage v-if="showPreview">
93
- <template #default>
94
- <RCBlockViewRenderer :blocks="page.blocks" />
95
- </template>
96
- <template #right>
97
- <UPageAside>
98
- <RCPagePropertiesRenderer v-model="page"/>
99
- </UPageAside>
100
- </template>
101
- </UPage>
102
- </div>
88
+ <div class="flex flex-col gap-xs text-xs">
89
+ <h6>Metadata</h6>
90
+ <span>Page ID: {{ page.id }}</span>
91
+ <span>Created At: <NuxtTime
92
+ :datetime="page.created_at ?? ''"
93
+ year="numeric"
94
+ month="numeric"
95
+ day="numeric"
96
+ hour="numeric"
97
+ minute="numeric"
98
+ second="numeric"
99
+ time-zone-name="short"
100
+ /></span>
101
+ <span>Posted At: <NuxtTime
102
+ :datetime="page.created_at ?? ''"
103
+ year="numeric"
104
+ month="numeric"
105
+ day="numeric"
106
+ hour="numeric"
107
+ minute="numeric"
108
+ second="numeric"
109
+ time-zone-name="short"
110
+ /></span>
111
+ <span>Updated At: <NuxtTime
112
+ :datetime="page.created_at ?? ''"
113
+ year="numeric"
114
+ month="numeric"
115
+ day="numeric"
116
+ hour="numeric"
117
+ minute="numeric"
118
+ second="numeric"
119
+ time-zone-name="short"
120
+ /></span>
121
+ </div>
122
+ </div>
123
+ </div>
124
+ <div class="flex flex-row gap-xl">
125
+ <USeparator orientation="vertical" />
126
+ <RCPageRenderer v-if="showPreview" v-model="page"/>
127
+ </div>
128
+ </UContainer>
103
129
  </template>
@@ -1,8 +1,8 @@
1
1
  import type { Page } from "../../types/index.js";
2
- interface Props {
2
+ interface PageEditorProps {
3
3
  isSaving: boolean;
4
4
  }
5
- type __VLS_Props = Props;
5
+ type __VLS_Props = PageEditorProps;
6
6
  type __VLS_ModelProps = {
7
7
  modelValue: Page;
8
8
  };
@@ -1,13 +1,9 @@
1
1
  <script setup>
2
- import { computed } from "vue";
3
2
  import { useI18n } from "vue-i18n";
3
+ import { getLocalizedContent } from "../../utils";
4
4
  import {} from "../../types";
5
5
  const page = defineModel({ type: null, ...{ required: true } });
6
6
  const locale = useI18n().locale;
7
- const properties = computed(() => page.value.properties);
8
- const groups = computed(() => {
9
- return page.value.properties;
10
- });
11
7
  const isFieldVisible = (fieldSchema) => {
12
8
  if (!fieldSchema.visibleIf) return true;
13
9
  return fieldSchema.visibleIf(page.value.properties);
@@ -18,17 +14,18 @@ const getSortedFields = (fields) => {
18
14
  </script>
19
15
 
20
16
  <template>
21
- <div class="flex flex-col gap-y-8 pr-2 pb-12">
22
- <div class="flex items-center justify-between border-b border-border pb-4">
23
- <h3 class="text-xs font-black uppercase tracking-widest text-muted-foreground">
24
- {{ page.type }} Metadata
17
+ <UCard variant="soft" :ui="{ root: 'divide-none', header: 'bg-elevated text-center', body: 'bg-muted' }">
18
+ <template #header>
19
+ <h3>
20
+ {{ getLocalizedContent(page.title, locale) }}
25
21
  </h3>
26
- <UBadge variant="subtle" size="sm" color="primary">
27
- {{ page.slug }}
28
- </UBadge>
29
- </div>
22
+ <UBadge variant="subtle" size="sm" color="primary" :label="page.type"/>
23
+ </template>
30
24
 
31
- <div v-for="(group, groupId) in groups" :key="groupId" class="space-y-4">
25
+ <div
26
+ v-for="(group, groupId) in page.properties"
27
+ :key="groupId"
28
+ >
32
29
  <div class="flex items-center gap-3">
33
30
  <span class="text-[10px] font-bold uppercase tracking-widest text-primary">
34
31
  {{ group.label[locale] }}
@@ -40,76 +37,64 @@ const getSortedFields = (fields) => {
40
37
  <template v-for="[fieldKey, schema] in getSortedFields(group.fields)" :key="fieldKey">
41
38
 
42
39
  <UFormField
43
- v-if="isFieldVisible(schema)"
44
- :label="schema.label"
45
- :name="fieldKey"
40
+ v-if="isFieldVisible(schema)"
41
+ :label="getLocalizedContent(schema.label, locale)"
42
+ :name="fieldKey"
46
43
  >
47
44
  <UInput
48
- v-if="schema.type === 'text'"
49
- v-model="properties[groupId].fields[fieldKey].value[locale]"
50
- variant="subtle"
51
- placeholder="..."
45
+ v-if="schema.type === 'text'"
46
+ v-model="schema.value[locale]"
47
+ variant="subtle"
48
+ placeholder="..."
52
49
  />
53
50
 
54
51
  <UInput
55
- v-else-if="schema.type === 'number'"
56
- v-model.number="properties[groupId].fields[fieldKey].value"
57
- type="number"
58
- variant="subtle"
52
+ v-else-if="schema.type === 'number'"
53
+ v-model.number="schema.value"
54
+ type="number"
55
+ variant="subtle"
59
56
  />
60
57
 
61
58
  <USelect
62
- v-else-if="schema.type === 'enum'"
63
- v-model="properties[groupId].fields[fieldKey].value"
64
- :items="schema.options || []"
65
- variant="subtle"
59
+ v-else-if="schema.type === 'enum'"
60
+ v-model="schema.value"
61
+ :items="schema.options || []"
62
+ variant="subtle"
66
63
  />
67
64
 
68
65
  <UInputMenu
69
- v-else-if="schema.type === 'text-array'"
70
- v-model="properties[groupId].fields[fieldKey].value[locale]"
71
- multiple
72
- creatable
73
- variant="subtle"
74
- placeholder="Add item..."
66
+ v-else-if="schema.type === 'text-array'"
67
+ :model-value="schema.value.map((v) => v[locale])"
68
+ @update:model-value="(vals) => schema.value = vals.map((str) => ({ [locale]: str }))"
69
+ multiple
70
+ creatable
71
+ variant="subtle"
72
+ placeholder="Add item..."
75
73
  />
76
74
 
77
75
  <UInput
78
- v-else-if="schema.type === 'page'"
79
- v-model="properties[groupId].fields[fieldKey].value"
80
- icon="lucide:link-2"
81
- variant="subtle"
82
- :placeholder="`Select ${schema.allowedPageTypes?.join('/')}`"
76
+ v-else-if="schema.type === 'page'"
77
+ v-model="schema.value"
78
+ icon="lucide:link-2"
79
+ variant="subtle"
80
+ :placeholder="`Select ${schema.allowedPageTypes?.join('/')}`"
83
81
  />
84
82
  </UFormField>
85
83
  </template>
86
84
  </div>
87
85
  </div>
88
86
 
89
- <USeparator />
87
+ <USeparator class="my-6" />
90
88
 
91
89
  <UFormField label="Global Search Tags">
92
90
  <UInputMenu
93
- v-model="page.tags"
94
- multiple
95
- creatable
96
- icon="lucide:tag"
97
- variant="subtle"
98
- placeholder="Add tags..."
91
+ v-model="page.tags"
92
+ multiple
93
+ creatable
94
+ icon="lucide:tag"
95
+ variant="subtle"
96
+ placeholder="Add tags..."
99
97
  />
100
98
  </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>
99
+ </UCard>
115
100
  </template>
@@ -1,7 +1,17 @@
1
1
  import { type Page } from '../../types/index.js';
2
- type __VLS_Props = {
2
+ type __VLS_ModelProps = {
3
3
  modelValue: Page;
4
4
  };
5
- declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
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,102 @@
1
1
  <script setup>
2
- import { computed } from "vue";
2
+ import { getLocalizedContent } from "../../utils";
3
3
  import { useI18n } from "vue-i18n";
4
4
  import {
5
5
  } from "../../types";
6
- const props = defineProps({
7
- modelValue: { type: null, required: true }
8
- });
9
- const locale = useI18n().locale;
10
- const properties = computed(() => props.modelValue.properties);
11
- const displayGroups = computed(() => {
12
- return Object.entries(properties.value || {}).map(([groupId, group]) => {
13
- const visibleFields = Object.entries(group.fields || {}).filter(([fieldKey, schema]) => {
14
- const value = schema.type === "text" || schema.type === "text-array" ? schema.value?.[locale.value] : schema.value;
15
- const isVisible = !schema.visibleIf || schema.visibleIf(props.modelValue.properties);
16
- if (!isVisible) return false;
17
- if (Array.isArray(value)) return value.length > 0;
18
- return value !== void 0 && value !== null && value !== "";
19
- }).sort(([, a], [, b]) => (a.order ?? 0) - (b.order ?? 0));
20
- return {
21
- ...group,
22
- groupId,
23
- visibleFields
24
- };
25
- }).filter((group) => group.visibleFields.length > 0);
26
- });
6
+ const page = defineModel({ type: null, ...{ required: true } });
7
+ const { t, locale } = useI18n();
8
+ const shouldRenderField = (schema) => {
9
+ const isVisible = !schema.visibleIf || schema.visibleIf(page.value.properties);
10
+ if (!isVisible) return false;
11
+ const val = schema.value;
12
+ if (schema.type === "text") {
13
+ return !!val?.[locale.value];
14
+ }
15
+ if (schema.type === "text-array") {
16
+ return Array.isArray(val) && val.length > 0;
17
+ }
18
+ return val !== void 0 && val !== null && val !== "";
19
+ };
20
+ const shouldRenderGroup = (group) => {
21
+ return Object.values(group.fields || {}).some((schema) => shouldRenderField(schema));
22
+ };
23
+ const getSortedFields = (fields) => {
24
+ return Object.entries(fields).sort(([, a], [, b]) => (a.order ?? 0) - (b.order ?? 0));
25
+ };
27
26
  </script>
28
27
 
29
28
  <template>
30
- <div class="wiki-renderer flex flex-col border border-border rounded-lg bg-muted/20 overflow-hidden shadow-sm">
31
- <div class="bg-muted/50 p-4 border-b border-border text-center">
32
- <div class="text-[10px] font-bold uppercase tracking-widest text-muted-foreground/70 mb-1">
33
- {{ props.modelValue.type }}
34
- </div>
35
- <h2 class="text-xl font-serif font-bold italic text-foreground">
36
- {{ properties.name?.value?.[locale] || props.modelValue.slug }}
37
- </h2>
38
- </div>
39
-
40
- <div v-for="group in displayGroups" :key="group.groupId" class="border-b last:border-b-0 border-border/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>
44
-
45
- <dl class="p-3 space-y-2.5">
46
- <div
47
- v-for="[fieldKey, schema] in group.visibleFields"
48
- :key="fieldKey"
49
- class="grid grid-cols-3 gap-x-3 items-baseline"
50
- >
51
- <dt class="text-[11px] font-semibold text-muted-foreground leading-tight">
52
- {{ schema.label }}
53
- </dt>
54
-
55
- <dd class="text-xs col-span-2 text-foreground leading-snug">
56
- <div v-if="schema.type === 'text-array'" class="flex flex-wrap gap-1">
57
- <template v-for="(item, index) in properties[group.groupId].fields[fieldKey].value[locale]" :key="index">
58
- <span class="font-medium">{{ item }}</span>
59
- <span v-if="index < properties[group.groupId].fields[fieldKey].value[locale].length - 1" class="text-muted-foreground/50">, </span>
60
- </template>
61
- </div>
62
-
63
- <span v-else-if="schema.type === 'page'" class="text-primary font-bold hover:underline cursor-pointer">
64
- {{ properties[group.groupId].fields[fieldKey].value }}
65
- </span>
66
-
67
- <span v-else-if="schema.type === 'text'" class="font-medium">
68
- {{ properties[group.groupId].fields[fieldKey].value[locale] }}
69
- </span>
70
-
71
- <span v-else class="font-medium">
72
- {{ properties[group.groupId].fields[fieldKey].value }}
73
- </span>
74
- </dd>
29
+ <aside class="flex flex-col gap-xl">
30
+ <UCard variant="soft" :ui="{ root: 'divide-none', header: 'bg-accented text-center', body: 'p-0 sm:p-0 bg-muted' }">
31
+ <template #header>
32
+ <div class="flex flex-col gap-xs items-center">
33
+ <h3>
34
+ {{ getLocalizedContent(page.title, locale) }}
35
+ </h3>
36
+ <span class="text-xs">{{ page.type }}</span>
37
+ <div v-if="page.tags?.length" class="flex flex-row flex-wrap gap-xs">
38
+ <UBadge
39
+ v-for="tag in page.tags"
40
+ :key="tag[locale]"
41
+ variant="soft"
42
+ size="xs"
43
+ color="neutral"
44
+ >
45
+ {{ tag[locale] }}
46
+ </UBadge>
47
+ </div>
75
48
  </div>
76
- </dl>
77
- </div>
49
+ </template>
78
50
 
79
- <div v-if="props.modelValue.tags?.length" class="p-3 bg-background/50 border-t border-border">
80
- <div class="flex flex-wrap gap-1.5">
81
- <UBadge
82
- v-for="tag in props.modelValue.tags"
83
- :key="String(tag[locale])"
84
- variant="subtle"
85
- size="sm"
86
- color="neutral"
87
- class="text-[9px] font-bold uppercase tracking-tight"
88
- >
89
- #{{ tag[locale] }}
90
- </UBadge>
91
- </div>
92
- </div>
51
+ <template #default>
52
+ <template v-for="(group, groupId) in page.properties" :key="groupId">
53
+ <UCollapsible v-if="shouldRenderGroup(group)" :default-open="group.defaultOpen">
54
+ <template #default>
55
+ <UButton
56
+ :label="getLocalizedContent(group.label, locale)"
57
+ variant="soft"
58
+ trailing-icon="lucide:chevron-down"
59
+ :ui="{
60
+ trailingIcon: 'group-data-[state=open]:rotate-180 transition-transform duration-200'
61
+ }"
62
+ block
63
+ class="group rounded-none bg-elevated text-default"
64
+ />
65
+ </template>
93
66
 
94
- </div>
95
- </template>
67
+ <template #content>
68
+ <dl class="p-sm flex flex-col gap-xs">
69
+ <template v-for="[fieldKey, schema] in getSortedFields(group.fields)" :key="fieldKey">
70
+ <div
71
+ v-if="shouldRenderField(schema)"
72
+ class="grid grid-cols-3 gap-xs items-baseline"
73
+ >
74
+ <dt class="text-xs font-semibold text-dimmed">
75
+ {{ getLocalizedContent(schema.label, locale) }}
76
+ </dt>
96
77
 
97
- <style scoped>
98
- .font-serif{font-family:Georgia,Times New Roman,Times,serif}
99
- </style>
78
+ <dd class="text-xs col-span-2">
79
+ <ul v-if="schema.type === 'text-array'" class="flex flex-wrap">
80
+ <li v-for="(item, index) in schema.value" :key="index">
81
+ <span class="font-medium">
82
+ {{ getLocalizedContent(item, locale) }}
83
+ </span>
84
+ </li>
85
+ </ul>
86
+ <span v-else-if="schema.type === 'text'">
87
+ {{ getLocalizedContent(schema.value, locale) }}
88
+ </span>
89
+ <span v-else>
90
+ {{ schema.value }}
91
+ </span>
92
+ </dd>
93
+ </div>
94
+ </template>
95
+ </dl>
96
+ </template>
97
+ </UCollapsible>
98
+ </template>
99
+ </template>
100
+ </UCard>
101
+ </aside>
102
+ </template>
@@ -1,7 +1,17 @@
1
1
  import { type Page } from '../../types/index.js';
2
- type __VLS_Props = {
2
+ type __VLS_ModelProps = {
3
3
  modelValue: Page;
4
4
  };
5
- declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
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,21 @@
1
+ import type { Page } from "../../types/index.js";
2
+ interface PageRendererProps {
3
+ }
4
+ type __VLS_Props = PageRendererProps;
5
+ type __VLS_ModelProps = {
6
+ modelValue: Page;
7
+ };
8
+ type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
9
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
10
+ "update:modelValue": (value: {
11
+ type: "Default";
12
+ properties: import("#imports").BasePageProperties;
13
+ } & import("#imports").BasePage) => any;
14
+ }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
15
+ "onUpdate:modelValue"?: ((value: {
16
+ type: "Default";
17
+ properties: import("#imports").BasePageProperties;
18
+ } & import("#imports").BasePage) => any) | undefined;
19
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
20
+ declare const _default: typeof __VLS_export;
21
+ export default _default;
@@ -0,0 +1,57 @@
1
+ <script setup>
2
+ import { getLocalizedContent } from "../../utils";
3
+ import { useI18n } from "#imports";
4
+ const page = defineModel({ type: null, ...{ required: true } });
5
+ const { t, locale } = useI18n();
6
+ const {} = defineProps({});
7
+ const emit = defineEmits([]);
8
+ </script>
9
+
10
+ <template>
11
+ <UContainer class="flex flex-col">
12
+ <div class="grid grid-cols-1 lg:grid-cols-4 gap-xl p-xl bg-muted">
13
+ <RCPagePropertiesRenderer v-model="page" class="order-1 lg:order-2 lg:col-span-1" />
14
+ <div class="order-2 lg:order-1 lg:col-span-3 flex flex-col gap-xl">
15
+ <UPageHeader
16
+ :title="getLocalizedContent(page.title, 'en')"
17
+ :description="getLocalizedContent(page.description, 'en') ?? ''"
18
+ />
19
+ <RCBlockViewRenderer :blocks="page.blocks" />
20
+ </div>
21
+ </div>
22
+ <div class="flex flex-col gap-xs text-xs p-xl">
23
+ <h6>Metadata</h6>
24
+ <span>Page ID: {{ page.id }}</span>
25
+ <span>Created At: <NuxtTime
26
+ :datetime="page.created_at ?? ''"
27
+ year="numeric"
28
+ month="numeric"
29
+ day="numeric"
30
+ hour="numeric"
31
+ minute="numeric"
32
+ second="numeric"
33
+ time-zone-name="short"
34
+ /></span>
35
+ <span>Posted At: <NuxtTime
36
+ :datetime="page.created_at ?? ''"
37
+ year="numeric"
38
+ month="numeric"
39
+ day="numeric"
40
+ hour="numeric"
41
+ minute="numeric"
42
+ second="numeric"
43
+ time-zone-name="short"
44
+ /></span>
45
+ <span>Updated At: <NuxtTime
46
+ :datetime="page.created_at ?? ''"
47
+ year="numeric"
48
+ month="numeric"
49
+ day="numeric"
50
+ hour="numeric"
51
+ minute="numeric"
52
+ second="numeric"
53
+ time-zone-name="short"
54
+ /></span>
55
+ </div>
56
+ </UContainer>
57
+ </template>
@@ -0,0 +1,21 @@
1
+ import type { Page } from "../../types/index.js";
2
+ interface PageRendererProps {
3
+ }
4
+ type __VLS_Props = PageRendererProps;
5
+ type __VLS_ModelProps = {
6
+ modelValue: Page;
7
+ };
8
+ type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
9
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
10
+ "update:modelValue": (value: {
11
+ type: "Default";
12
+ properties: import("#imports").BasePageProperties;
13
+ } & import("#imports").BasePage) => any;
14
+ }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
15
+ "onUpdate:modelValue"?: ((value: {
16
+ type: "Default";
17
+ properties: import("#imports").BasePageProperties;
18
+ } & import("#imports").BasePage) => any) | undefined;
19
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
20
+ declare const _default: typeof __VLS_export;
21
+ export default _default;
@@ -6,8 +6,8 @@ declare global {
6
6
  }
7
7
  export type PageType = keyof RegisterPageTypes;
8
8
  export interface Property<T = any> {
9
- value: T;
10
- label: string;
9
+ value: T extends never[] ? Localized[] : T;
10
+ label: Localized<string>;
11
11
  type: "number" | "text" | "text-array" | "enum" | "page" | "page-array";
12
12
  options?: string[];
13
13
  allowedPageTypes?: PageType[];
@@ -19,6 +19,7 @@ export interface PropertyGroup {
19
19
  label: Localized<string>;
20
20
  order?: number;
21
21
  fields: Record<string, Property>;
22
+ defaultOpen: boolean;
22
23
  }
23
24
  /**
24
25
  * A PageTemplate is the single definition for a page's properties and initial blocks.
@@ -1,11 +1,24 @@
1
1
  import { type MaybeRefOrGetter } from 'vue';
2
- import type { Page, Localized, PageDefinition } from "../types/pages.js";
2
+ import { type Page, type Localized, type PageDefinition, type Property } from "../types/index.js";
3
3
  export declare const getLocalizedContent: <T = string>(field: Localized<T> | undefined, currentLocale: MaybeRefOrGetter<string>) => T | string;
4
+ type WidenProperty<T> = T extends string ? string : T extends number ? number : T extends never[] ? Localized[] : T extends object ? {
5
+ [K in keyof T]: WidenProperty<T[K]>;
6
+ } : T;
4
7
  /**
5
8
  * Helper to define a page with full type safety and literal preservation.
6
9
  * This is used by consuming apps to define their custom page types.
7
10
  */
8
- export declare function definePageDefinition<T extends PageDefinition>(def: T): T;
11
+ export declare function definePageDefinition<T extends PageDefinition>(def: T): {
12
+ [K in keyof T]: K extends 'properties' ? {
13
+ [G in keyof T['properties']]: {
14
+ label: Localized<string>;
15
+ defaultOpen: boolean;
16
+ fields: {
17
+ [F in keyof T['properties'][G]['fields']]: Property<WidenProperty<T['properties'][G]['fields'][F]['value']>>;
18
+ };
19
+ };
20
+ } : T[K];
21
+ };
9
22
  /**
10
23
  * Ensures a page strictly adheres to its PageDefinition.
11
24
  * - Adds missing properties and groups in the correct order.
@@ -14,3 +27,4 @@ export declare function definePageDefinition<T extends PageDefinition>(def: T):
14
27
  * - Removes templated blocks no longer in the definition.
15
28
  */
16
29
  export declare function syncPageWithDefinition(page: Page, definition?: PageDefinition): Page;
30
+ export {};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "rimelight-components",
3
3
  "description": "A component library by Rimelight Entertainment.",
4
- "version": "2.0.98",
4
+ "version": "2.0.99",
5
5
  "type": "module",
6
6
  "repository": {
7
7
  "type": "git",