rimelight-components 2.1.78 → 2.1.79

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.1.78",
3
+ "version": "2.1.79",
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.1.78";
7
+ const version = "2.1.79";
8
8
  const homepage = "https://rimelight.com/tools/rimelight-components";
9
9
 
10
10
  const defaultOptions = {
@@ -61,7 +61,6 @@ defineExpose({ undo, redo, canUndo, canRedo });
61
61
  <span class="text-sm text-dimmed mb-2">Append new block to page</span>
62
62
  <UDropdown
63
63
  :items="dropdownItems"
64
- :popper="{ placement: 'bottom' }"
65
64
  >
66
65
  <UButton
67
66
  color="white"
@@ -9,7 +9,7 @@ const menuItems = computed(() => {
9
9
  acc[groupId].push({
10
10
  label: action.label,
11
11
  icon: action.icon,
12
- click: action.onSelect
12
+ onSelect: action.onSelect
13
13
  });
14
14
  return acc;
15
15
  }, {});
@@ -8,6 +8,8 @@ export interface PageEditorProps {
8
8
  pageDefinitions: Record<string, PageDefinition>;
9
9
  onCreatePage?: (page: Partial<Page>) => Promise<void>;
10
10
  onDeletePage?: (id: string) => Promise<void>;
11
+ onFetchPages?: () => Promise<Pick<Page, 'title' | 'slug'>[]>;
12
+ onNavigateToPage?: (slug: string) => void;
11
13
  rc?: {
12
14
  header?: string;
13
15
  headerGroup?: string;
@@ -13,6 +13,8 @@ const {
13
13
  resolvePage,
14
14
  onCreatePage,
15
15
  onDeletePage,
16
+ onFetchPages,
17
+ onNavigateToPage,
16
18
  rc: rcProp
17
19
  } = defineProps({
18
20
  isSaving: { type: Boolean, required: true },
@@ -23,6 +25,8 @@ const {
23
25
  pageDefinitions: { type: Object, required: true },
24
26
  onCreatePage: { type: Function, required: false },
25
27
  onDeletePage: { type: Function, required: false },
28
+ onFetchPages: { type: Function, required: false },
29
+ onNavigateToPage: { type: Function, required: false },
26
30
  rc: { type: Object, required: false }
27
31
  });
28
32
  const page = defineModel({ type: null, ...{ required: true } });
@@ -147,6 +151,26 @@ const handleDeleteConfirm = async () => {
147
151
  isDeleting.value = false;
148
152
  }
149
153
  };
154
+ const isPageTreeModalOpen = ref(false);
155
+ const isFetchingTree = ref(false);
156
+ const treePages = ref([]);
157
+ const handleOpenTree = async () => {
158
+ if (!onFetchPages) return;
159
+ isFetchingTree.value = true;
160
+ try {
161
+ treePages.value = await onFetchPages();
162
+ isPageTreeModalOpen.value = true;
163
+ } catch (e) {
164
+ console.error("Failed to fetch pages for tree", e);
165
+ } finally {
166
+ isFetchingTree.value = false;
167
+ }
168
+ };
169
+ const handleTreeNavigate = (slug) => {
170
+ if (onNavigateToPage) {
171
+ onNavigateToPage(slug);
172
+ }
173
+ };
150
174
  </script>
151
175
 
152
176
  <template>
@@ -191,6 +215,21 @@ const handleDeleteConfirm = async () => {
191
215
  :loading="isSaving"
192
216
  @click="handleSave"
193
217
  />
218
+ <UButton
219
+ icon="lucide:list-tree"
220
+ variant="outline"
221
+ color="neutral"
222
+ size="xs"
223
+ :loading="isFetchingTree"
224
+ :disabled="!onFetchPages"
225
+ @click="handleOpenTree"
226
+ />
227
+ <RCPageTreeModal
228
+ v-model:open="isPageTreeModalOpen"
229
+ :pages="treePages"
230
+ :loading="isFetchingTree"
231
+ @navigate="handleTreeNavigate"
232
+ />
194
233
  <RCCreatePageModal
195
234
  :is-open="isCreateModalOpen"
196
235
  :definitions="pageDefinitions"
@@ -8,6 +8,8 @@ export interface PageEditorProps {
8
8
  pageDefinitions: Record<string, PageDefinition>;
9
9
  onCreatePage?: (page: Partial<Page>) => Promise<void>;
10
10
  onDeletePage?: (id: string) => Promise<void>;
11
+ onFetchPages?: () => Promise<Pick<Page, 'title' | 'slug'>[]>;
12
+ onNavigateToPage?: (slug: string) => void;
11
13
  rc?: {
12
14
  header?: string;
13
15
  headerGroup?: string;
@@ -0,0 +1,32 @@
1
+ import type { Page } from "../../../types/index.js";
2
+ export interface PageTreeModalProps {
3
+ loading?: boolean;
4
+ pages?: Pick<Page, "title" | "slug">[];
5
+ rc?: {
6
+ header?: string;
7
+ headerTitle?: string;
8
+ closeButton?: string;
9
+ body?: string;
10
+ footer?: string;
11
+ };
12
+ }
13
+ type __VLS_Props = PageTreeModalProps;
14
+ export interface PageTreeModalEmits {
15
+ close: [];
16
+ navigate: [slug: string];
17
+ }
18
+ type __VLS_ModelProps = {
19
+ "open"?: boolean;
20
+ };
21
+ type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
22
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
23
+ navigate: (slug: string) => any;
24
+ close: () => any;
25
+ "update:open": (value: boolean) => any;
26
+ }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
27
+ onNavigate?: ((slug: string) => any) | undefined;
28
+ onClose?: (() => any) | undefined;
29
+ "onUpdate:open"?: ((value: boolean) => any) | undefined;
30
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
31
+ declare const _default: typeof __VLS_export;
32
+ export default _default;
@@ -0,0 +1,137 @@
1
+ <script setup>
2
+ import { computed, ref, watch } from "vue";
3
+ import { useRC } from "../../../composables";
4
+ import { tv } from "../../../internal/tv";
5
+ import { getLocalizedContent } from "../../../utils";
6
+ import { useI18n } from "vue-i18n";
7
+ const open = defineModel("open", { type: Boolean, ...{ default: false } });
8
+ const { loading, pages = [], rc: rcProp } = defineProps({
9
+ loading: { type: Boolean, required: false },
10
+ pages: { type: Array, required: false },
11
+ rc: { type: Object, required: false }
12
+ });
13
+ const emit = defineEmits(["close", "navigate"]);
14
+ const { rc } = useRC("PageTreeModal", rcProp);
15
+ const { t, locale } = useI18n();
16
+ const pageTreeModalStyles = tv({
17
+ slots: {
18
+ header: "flex items-center justify-between",
19
+ headerTitle: "text-base font-semibold leading-6",
20
+ closeButton: "-my-1",
21
+ body: "p-0 min-h-[300px] max-h-[60vh] overflow-y-auto",
22
+ footer: "flex justify-end gap-2"
23
+ }
24
+ });
25
+ const { header: headerClass, headerTitle, closeButton, body, footer } = pageTreeModalStyles();
26
+ const treeItems = computed(() => {
27
+ if (!pages || pages.length === 0) return [];
28
+ const nodeMap = /* @__PURE__ */ new Map();
29
+ const rootNodes = [];
30
+ const getNode = (path, partLabel, pageObj) => {
31
+ if (!nodeMap.has(path)) {
32
+ const label = pageObj ? getLocalizedContent(pageObj.title, locale.value) || partLabel : partLabel.charAt(0).toUpperCase() + partLabel.slice(1);
33
+ const newNode = {
34
+ label,
35
+ slug: pageObj ? pageObj.slug : void 0,
36
+ icon: pageObj ? "i-lucide-file" : "i-lucide-folder",
37
+ children: []
38
+ };
39
+ nodeMap.set(path, newNode);
40
+ } else if (pageObj) {
41
+ const node = nodeMap.get(path);
42
+ node.label = getLocalizedContent(pageObj.title, locale.value) || node.label;
43
+ node.slug = pageObj.slug;
44
+ node.icon = "i-lucide-file-text";
45
+ }
46
+ return nodeMap.get(path);
47
+ };
48
+ pages.forEach((page) => {
49
+ const parts = page.slug.split("/").filter(Boolean);
50
+ let currentPath = "";
51
+ let parent = null;
52
+ parts.forEach((part, index) => {
53
+ currentPath = currentPath ? `${currentPath}/${part}` : part;
54
+ const isLast = index === parts.length - 1;
55
+ const pageObj = isLast ? page : void 0;
56
+ const node = getNode(currentPath, part, pageObj);
57
+ if (index === 0) {
58
+ if (!rootNodes.includes(node)) {
59
+ rootNodes.push(node);
60
+ }
61
+ } else {
62
+ if (parent && !parent.children?.includes(node)) {
63
+ if (!parent.children) parent.children = [];
64
+ parent.children.push(node);
65
+ }
66
+ }
67
+ parent = node;
68
+ });
69
+ });
70
+ const sortNodes = (nodes) => {
71
+ nodes.sort((a, b) => a.label.localeCompare(b.label));
72
+ nodes.forEach((n) => {
73
+ if (n.children && n.children.length > 0) {
74
+ sortNodes(n.children);
75
+ }
76
+ });
77
+ };
78
+ sortNodes(rootNodes);
79
+ return rootNodes;
80
+ });
81
+ const handleSelect = (node) => {
82
+ if (node.slug) {
83
+ emit("navigate", node.slug);
84
+ open.value = false;
85
+ }
86
+ };
87
+ const getKey = (item) => item.slug || item.label;
88
+ </script>
89
+
90
+ <template>
91
+ <UModal v-model:open="open">
92
+ <UCard :ui="{ body: 'p-0 sm:p-0' }">
93
+ <template #header>
94
+ <div :class="headerClass({ class: rc.header })">
95
+ <h3 :class="headerTitle({ class: rc.headerTitle })">Page Hierarchy</h3>
96
+ <UButton
97
+ color="neutral"
98
+ variant="ghost"
99
+ icon="i-heroicons-x-mark-20-solid"
100
+ :class="closeButton({ class: rc.closeButton })"
101
+ @click="open = false"
102
+ />
103
+ </div>
104
+ </template>
105
+
106
+ <div :class="body({ class: rc.body })">
107
+ <div v-if="loading" class="p-4 flex justify-center">
108
+ <UIcon name="i-heroicons-arrow-path" class="w-5 h-5 animate-spin" />
109
+ </div>
110
+ <div v-else-if="treeItems.length === 0" class="p-4 text-center text-gray-500">
111
+ No pages found directly.
112
+ </div>
113
+ <UTree
114
+ v-else
115
+ :items="treeItems"
116
+ :ui="{ item: 'cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-800' }"
117
+ >
118
+ <template #item="{ item }">
119
+ <div class="flex items-center gap-2 py-1 w-full" @click="handleSelect(item)">
120
+ <UIcon :name="item.icon || (item.children?.length ? 'i-lucide-folder' : 'i-lucide-file')" class="w-4 h-4 text-gray-400" />
121
+ <span :class="{ 'text-gray-900 dark:text-gray-100': item.slug, 'text-gray-500 font-medium': !item.slug }">
122
+ {{ item.label }}
123
+ </span>
124
+ <UIcon v-if="item.slug" name="i-heroicons-arrow-right-20-solid" class="w-3 h-3 ml-auto text-gray-300 opacity-0 group-hover:opacity-100" />
125
+ </div>
126
+ </template>
127
+ </UTree>
128
+ </div>
129
+
130
+ <template #footer>
131
+ <div :class="footer({ class: rc.footer })">
132
+ <UButton color="neutral" variant="ghost" label="Close" @click="open = false" />
133
+ </div>
134
+ </template>
135
+ </UCard>
136
+ </UModal>
137
+ </template>
@@ -0,0 +1,32 @@
1
+ import type { Page } from "../../../types/index.js";
2
+ export interface PageTreeModalProps {
3
+ loading?: boolean;
4
+ pages?: Pick<Page, "title" | "slug">[];
5
+ rc?: {
6
+ header?: string;
7
+ headerTitle?: string;
8
+ closeButton?: string;
9
+ body?: string;
10
+ footer?: string;
11
+ };
12
+ }
13
+ type __VLS_Props = PageTreeModalProps;
14
+ export interface PageTreeModalEmits {
15
+ close: [];
16
+ navigate: [slug: string];
17
+ }
18
+ type __VLS_ModelProps = {
19
+ "open"?: boolean;
20
+ };
21
+ type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
22
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
23
+ navigate: (slug: string) => any;
24
+ close: () => any;
25
+ "update:open": (value: boolean) => any;
26
+ }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
27
+ onNavigate?: ((slug: string) => any) | undefined;
28
+ onClose?: (() => any) | undefined;
29
+ "onUpdate:open"?: ((value: boolean) => any) | undefined;
30
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
31
+ declare const _default: typeof __VLS_export;
32
+ export default _default;
@@ -1,2 +1,3 @@
1
1
  export { default as CreatePageModal } from './CreatePageModal.vue.js';
2
2
  export { default as DeletePageModal } from './DeletePageModal.vue.js';
3
+ export { default as PageTreeModal } from './PageTreeModal.vue.js';
@@ -1,2 +1,3 @@
1
1
  export { default as CreatePageModal } from "./CreatePageModal.vue";
2
2
  export { default as DeletePageModal } from "./DeletePageModal.vue";
3
+ export { default as PageTreeModal } from "./PageTreeModal.vue";
@@ -1,2 +1,3 @@
1
1
  export { default as CreatePageModal } from "./CreatePageModal.vue";
2
2
  export { default as DeletePageModal } from "./DeletePageModal.vue";
3
+ export { default as PageTreeModal } from "./PageTreeModal.vue";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rimelight-components",
3
- "version": "2.1.78",
3
+ "version": "2.1.79",
4
4
  "description": "A component library by Rimelight Entertainment.",
5
5
  "keywords": [
6
6
  "nuxt",