@rimelight/ui 0.0.43 → 0.0.44

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.
Files changed (153) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +14 -12
  3. package/package.json +11 -14
  4. package/src/components/avatar/RLAAvatar.astro +4 -12
  5. package/src/components/badge/RLABadge.astro +2 -1
  6. package/src/components/badge/badge.theme.ts +0 -4
  7. package/src/components/banner/RLABanner.astro +1 -1
  8. package/src/components/banner/banner.theme.ts +1 -1
  9. package/src/components/breadcrumb/RLABreadcrumb.astro +2 -2
  10. package/src/components/button/RLAButton.astro +4 -1
  11. package/src/components/button/button.theme.ts +1 -5
  12. package/src/components/callout/RLACallout.astro +7 -5
  13. package/src/components/callout/callout.theme.ts +1 -5
  14. package/src/components/card/card.theme.ts +3 -3
  15. package/src/components/carousel/RLACarousel.astro +2 -2
  16. package/src/components/carousel/carousel.theme.ts +2 -2
  17. package/src/components/chart/RLAChart.astro +1 -1
  18. package/src/components/checkbox/RLACheckbox.astro +2 -1
  19. package/src/components/checkbox/checkbox.theme.ts +0 -4
  20. package/src/components/chip/RLAChip.astro +2 -1
  21. package/src/components/chip/chip.theme.ts +0 -4
  22. package/src/components/collapsible/RLACollapsible.astro +1 -1
  23. package/src/components/color-area/RLAColorArea.astro +1 -0
  24. package/src/components/color-area/color-area.theme.ts +1 -5
  25. package/src/components/color-field/RLAColorField.astro +1 -0
  26. package/src/components/color-field/color-field.theme.ts +1 -5
  27. package/src/components/color-slider/RLAColorSlider.astro +1 -0
  28. package/src/components/color-slider/color-slider.theme.ts +1 -5
  29. package/src/components/dashboard-group/RLADashboardGroup.astro +5 -7
  30. package/src/components/dashboard-group/dashboard-group.theme.ts +1 -1
  31. package/src/components/dashboard-group/dashboard-group.ts +1 -1
  32. package/src/components/dashboard-navbar/RLADashboardNavbar.astro +4 -4
  33. package/src/components/dashboard-navbar/dashboard-navbar.theme.ts +3 -3
  34. package/src/components/dashboard-navbar/dashboard-navbar.ts +1 -1
  35. package/src/components/dashboard-panel/RLADashboardPanel.astro +9 -8
  36. package/src/components/dashboard-panel/dashboard-panel.theme.ts +1 -1
  37. package/src/components/dashboard-sidebar/RLADashboardSidebar.astro +7 -4
  38. package/src/components/dashboard-sidebar/dashboard-sidebar.theme.ts +6 -5
  39. package/src/components/dashboard-sidebar-collapse/RLADashboardSidebarCollapse.astro +1 -0
  40. package/src/components/drawer/RLADrawer.astro +1 -1
  41. package/src/components/dropdown-menu/dropdown-menu.theme.ts +2 -2
  42. package/src/components/footer/RLAFooter.astro +2 -0
  43. package/src/components/footer/footer.theme.ts +0 -8
  44. package/src/components/gamepad/RLAGamepad.astro +1 -1
  45. package/src/components/head/UIHead.astro +1 -2
  46. package/src/components/header/header.theme.ts +1 -1
  47. package/src/components/icon/RLAIcon.astro +2 -5
  48. package/src/components/icon/RLSIcon.tsx +2 -2
  49. package/src/components/icon/RLVIcon.vue +18 -6
  50. package/src/components/icon/icon.theme.ts +1 -1
  51. package/src/components/image/image.theme.ts +1 -1
  52. package/src/components/input/RLAInput.astro +2 -1
  53. package/src/components/input/input.theme.ts +0 -4
  54. package/src/components/input-menu/RLAInputMenu.astro +2 -2
  55. package/src/components/input-menu/input-menu.theme.ts +1 -1
  56. package/src/components/input-rating/input-rating.theme.ts +2 -2
  57. package/src/components/kbd/RLAKbd.astro +2 -1
  58. package/src/components/kbd/kbd.theme.ts +0 -4
  59. package/src/components/keyboard/RLAKeyboard.astro +1 -1
  60. package/src/components/link/RLALink.astro +1 -0
  61. package/src/components/link/link.theme.ts +0 -4
  62. package/src/components/link-group/RLALinkGroup.astro +1 -1
  63. package/src/components/link-group/link-group.theme.ts +1 -10
  64. package/src/components/locale-selector/RLALocaleSelector.astro +4 -36
  65. package/src/components/locale-selector/locale-selector.theme.ts +3 -11
  66. package/src/components/logo/RLALogo.astro +1 -2
  67. package/src/components/modal/RLAModal.astro +1 -1
  68. package/src/components/modal/RLSModal.tsx +2 -2
  69. package/src/components/navigation-menu/RLANavigationMenu.astro +17 -21
  70. package/src/components/navigation-menu/navigation-menu.theme.ts +3 -7
  71. package/src/components/pagination/RLAPagination.astro +2 -2
  72. package/src/components/pagination/pagination.theme.ts +1 -1
  73. package/src/components/popover/RLAPopover.astro +39 -0
  74. package/src/components/progress/RLAProgress.astro +2 -0
  75. package/src/components/progress/progress.theme.ts +0 -4
  76. package/src/components/scroll-area/scroll-area.theme.ts +1 -1
  77. package/src/components/scroll-to-top/RLAScrollToTop.astro +6 -5
  78. package/src/components/scroll-to-top/RLVScrollToTop.vue +7 -7
  79. package/src/components/scroll-to-top/scroll-to-top.theme.ts +3 -7
  80. package/src/components/search/RLASearch.astro +3 -3
  81. package/src/components/select/RLASelect.astro +96 -528
  82. package/src/components/select/RLSSelect.tsx +68 -14
  83. package/src/components/select/RLVSelect.vue +57 -0
  84. package/src/components/select/select.theme.ts +11 -96
  85. package/src/components/shortcuts/RLAShortcuts.astro +9 -9
  86. package/src/components/sidebar/RLASidebar.astro +3 -3
  87. package/src/components/slideover/RLASlideover.astro +1 -1
  88. package/src/components/slider/RLASlider.astro +2 -1
  89. package/src/components/slider/slider.theme.ts +1 -5
  90. package/src/components/spinner/RLASpinner.astro +2 -1
  91. package/src/components/spinner/spinner.theme.ts +1 -5
  92. package/src/components/splitter/splitter.theme.ts +1 -1
  93. package/src/components/stepper/RLAStepper.astro +8 -8
  94. package/src/components/stepper/stepper.theme.ts +6 -7
  95. package/src/components/steps/RLASteps.astro +3 -3
  96. package/src/components/switch/RLASwitch.astro +2 -1
  97. package/src/components/switch/switch.theme.ts +0 -4
  98. package/src/components/table/table.theme.ts +1 -1
  99. package/src/components/table-of-contents/RLATableOfContents.astro +5 -5
  100. package/src/components/theme-selector/RLAThemeSelector.astro +16 -14
  101. package/src/components/theme-selector/theme-selector.theme.ts +2 -2
  102. package/src/components/toast/RLAToast.astro +6 -6
  103. package/src/components/toast/store.ts +1 -1
  104. package/src/components/tooltip/tooltip.theme.ts +1 -1
  105. package/src/icons.raw.ts +67 -0
  106. package/src/icons.ts +31 -64
  107. package/src/index.ts +19 -18
  108. package/src/preset.ts +110 -108
  109. package/src/resolver.ts +127 -15
  110. package/src/shortcuts.ts +103 -84
  111. package/src/stores.ts +22 -0
  112. package/src/types.ts +27 -9
  113. package/src/virtual.d.ts +4 -0
  114. package/src/components/editor/PageEditor.vue +0 -459
  115. package/src/components/editor/PageMention.vue +0 -87
  116. package/src/components/editor/PagePropertiesEditor.vue +0 -358
  117. package/src/components/editor/PagePropertiesRenderer.vue +0 -285
  118. package/src/components/editor/PageRenderer.vue +0 -155
  119. package/src/components/editor/PageSurround.vue +0 -130
  120. package/src/components/editor/PageVersionSelector.vue +0 -371
  121. package/src/components/editor/blocks/AddBlockModal.vue +0 -196
  122. package/src/components/editor/blocks/Block.vue +0 -142
  123. package/src/components/editor/blocks/BlockEditRenderer.vue +0 -26
  124. package/src/components/editor/blocks/BlockEditor.vue +0 -141
  125. package/src/components/editor/blocks/BlockViewRenderer.vue +0 -71
  126. package/src/components/editor/blocks/TextRenderer.vue +0 -31
  127. package/src/components/editor/blocks/editor/CalloutBlockEditor.vue +0 -53
  128. package/src/components/editor/blocks/editor/ImageBlockEditor.vue +0 -55
  129. package/src/components/editor/blocks/editor/ParagraphBlockEditor.vue +0 -31
  130. package/src/components/editor/blocks/editor/SectionBlockEditor.vue +0 -65
  131. package/src/components/editor/blocks/nodes/LinkNode.vue +0 -28
  132. package/src/components/editor/blocks/nodes/TextNode.vue +0 -22
  133. package/src/components/editor/blocks/renderer/CalloutBlockRenderer.vue +0 -65
  134. package/src/components/editor/blocks/renderer/ImageBlockRenderer.vue +0 -20
  135. package/src/components/editor/blocks/renderer/ParagraphBlockRenderer.vue +0 -21
  136. package/src/components/editor/blocks/renderer/SectionBlockRenderer.vue +0 -55
  137. package/src/components/editor/composables/injectionKeys.ts +0 -3
  138. package/src/components/editor/composables/useBlockEditor.ts +0 -261
  139. package/src/components/editor/composables/usePageEditor.ts +0 -97
  140. package/src/components/editor/modals/CreatePageModal.vue +0 -160
  141. package/src/components/editor/modals/DeletePageModal.vue +0 -116
  142. package/src/components/editor/modals/PageTreeModal.vue +0 -275
  143. package/src/components/editor/types/blocks.ts +0 -100
  144. package/src/components/editor/types/pages.ts +0 -89
  145. package/src/components/editor/types/schemas.ts +0 -36
  146. package/src/components/editor/utils/blocks.ts +0 -42
  147. package/src/components/editor/utils/editorHelpers.ts +0 -24
  148. package/src/components/editor/utils/page.ts +0 -219
  149. package/src/components/editor/utils/richTextHelpers.ts +0 -35
  150. package/src/components/editor/views/PageEditView.vue +0 -195
  151. package/src/components/editor/views/PageLiveView.vue +0 -90
  152. package/src/components/editor/views/PageReviewView.vue +0 -308
  153. package/src/components/icon/resolver.ts +0 -23
@@ -1,195 +0,0 @@
1
- <script setup lang="ts">
2
- import { computed, ref, watch, nextTick } from "vue"
3
- import type { Page, PageVersion, PageDefinition } from "../types/pages"
4
- import { getPageResolutionPath } from "../utils/page"
5
- import PageEditor from "../PageEditor.vue"
6
-
7
- export interface PageEditViewProps {
8
- lookupPath?: string
9
- fetchUrl?: string
10
- cacheKey?: string
11
- pageDefinitions: Record<string, PageDefinition>
12
- baseUrl: string
13
- hierarchyPath?: string
14
- isAdmin?: boolean
15
- backUrl?: string
16
- }
17
-
18
- const {
19
- lookupPath,
20
- fetchUrl,
21
- cacheKey,
22
- pageDefinitions,
23
- baseUrl,
24
- hierarchyPath,
25
- isAdmin = false,
26
- backUrl
27
- } = defineProps<PageEditViewProps>()
28
-
29
- const resolvedFetchUrl = computed(() => getPageResolutionPath(lookupPath || fetchUrl || ""))
30
- const isSaving = ref(false)
31
- const currentVersionId = ref<string | null>(null)
32
- const editorRef = ref<{
33
- resetHistory: () => void
34
- } | null>(null)
35
- const localPage = ref<Page | null>(null)
36
- const pageData = ref<Page | null>(null)
37
- const pageStatus = ref<"idle" | "pending" | "success" | "error">("idle")
38
-
39
- const fetchPageDetails = async () => {
40
- if (!resolvedFetchUrl.value) return
41
- pageStatus.value = "pending"
42
- try {
43
- const res = await fetch(resolvedFetchUrl.value)
44
- if (!res.ok) throw new Error("Failed to fetch")
45
- const data = await res.json()
46
- pageData.value = data
47
- localPage.value = JSON.parse(JSON.stringify(data))
48
- pageStatus.value = "success"
49
- } catch (err) {
50
- pageStatus.value = "error"
51
- }
52
- }
53
-
54
- watch(() => resolvedFetchUrl.value, fetchPageDetails, { immediate: true })
55
-
56
- const resolvePage = async (idOrSlug: string): Promise<Pick<Page, "title" | "icon" | "slug">> => {
57
- const url = getPageResolutionPath(idOrSlug)
58
- if (!url) throw new Error(`[Editor] Invalid page reference: ${idOrSlug}`)
59
- const res = await fetch(`${url}?select=title,icon,slug`)
60
- if (!res.ok) throw new Error(`[Editor] Page not found: ${idOrSlug}`)
61
- return await res.json()
62
- }
63
-
64
- const fetchPages = async () => {
65
- const types = Object.keys(pageDefinitions).join(",")
66
- const prefix = (hierarchyPath ?? baseUrl).replace(/^\/|\/$/g, "")
67
- const res = await fetch(
68
- `/api/pages/list?select=title,slug,type,id&types=${types}&prefix=${prefix}`
69
- )
70
- if (!res.ok) throw new Error("Failed to fetch page list")
71
- return await res.json()
72
- }
73
-
74
- const handleSave = async (updatedPage: Page): Promise<void> => {
75
- if (!updatedPage.id) return
76
- isSaving.value = true
77
- try {
78
- await fetch(`/api/pages/id/${updatedPage.id}`, {
79
- method: "PUT",
80
- headers: { "Content-Type": "application/json" },
81
- body: JSON.stringify(updatedPage)
82
- })
83
- await fetchPageDetails()
84
- await nextTick()
85
- editorRef.value?.resetHistory()
86
- } catch (e) {
87
- console.error(e)
88
- } finally {
89
- isSaving.value = false
90
- }
91
- }
92
-
93
- const handlePublish = async (updatedPage: Page): Promise<void> => {
94
- if (!updatedPage.id) return
95
- isSaving.value = true
96
- try {
97
- await fetch(`/api/pages/id/${updatedPage.id}/publish`, {
98
- method: "POST"
99
- })
100
- const base = baseUrl.replace(/\/$/, "")
101
- const cleanBase = base.replace(/^\//, "")
102
- const slugToUse = updatedPage.slug.startsWith(cleanBase)
103
- ? updatedPage.slug
104
- : `${cleanBase}/${updatedPage.slug}`
105
- if (typeof window !== "undefined") {
106
- window.location.href = `/${slugToUse}`
107
- }
108
- } catch (e) {
109
- console.error(e)
110
- } finally {
111
- isSaving.value = false
112
- }
113
- }
114
-
115
- const handleUnpublish = async (updatedPage: Page): Promise<void> => {
116
- if (!updatedPage.id) return
117
- isSaving.value = true
118
- try {
119
- await fetch(`/api/pages/id/${updatedPage.id}/unpublish`, {
120
- method: "POST"
121
- })
122
- await fetchPageDetails()
123
- } catch (e) {
124
- console.error(e)
125
- } finally {
126
- isSaving.value = false
127
- }
128
- }
129
-
130
- const handleDelete = async (id: string) => {
131
- try {
132
- await fetch(`/api/pages/id/${id}`, {
133
- method: "DELETE"
134
- })
135
- const dest = backUrl || baseUrl
136
- if (typeof window !== "undefined") {
137
- window.location.href = dest
138
- }
139
- } catch (e) {
140
- console.error(e)
141
- }
142
- }
143
-
144
- const handleVersionNavigate = (version: PageVersion) => {
145
- if (!version?.id || !localPage.value?.slug) return
146
- const base = baseUrl.replace(/\/$/, "")
147
- const cleanBase = base.replace(/^\//, "")
148
- const slugToUse = localPage.value.slug.startsWith(cleanBase)
149
- ? localPage.value.slug
150
- : `${cleanBase}/${localPage.value.slug}`
151
- if (typeof window !== "undefined") {
152
- window.location.href = `/${slugToUse}/review?version=${version.id}`
153
- }
154
- }
155
-
156
- const handleNavigateToPage = (slug: string) => {
157
- const base = baseUrl.replace(/\/$/, "")
158
- const cleanBase = base.replace(/^\//, "")
159
- const slugToUse = slug.startsWith(cleanBase) ? slug : `${cleanBase}/${slug}`
160
- if (typeof window !== "undefined") {
161
- window.location.href = `/${slugToUse}/edit`
162
- }
163
- }
164
-
165
- defineOptions({ name: "PageEditView" })
166
- </script>
167
-
168
- <template>
169
- <div v-if="pageStatus === 'pending'" class="h-full w-full flex items-center justify-center p-12">
170
- <span class="i-lucide-loader-2 size-12 animate-spin text-neutral-400" />
171
- </div>
172
- <div v-else-if="pageStatus === 'error' || !localPage" class="p-8 text-center">
173
- <h3 class="text-lg font-bold text-red-600">Page Not Found</h3>
174
- <p class="text-neutral-500 mt-2">The requested page could not be loaded.</p>
175
- </div>
176
- <template v-else-if="localPage && localPage.id">
177
- <PageEditor
178
- ref="editorRef"
179
- v-model="localPage"
180
- v-model:current-version-id="currentVersionId"
181
- :is-saving="isSaving"
182
- :is-admin="isAdmin"
183
- :base-path="(hierarchyPath ?? baseUrl).replace(/^\/|\/$/g, '')"
184
- :page-definitions="pageDefinitions"
185
- :resolve-page="resolvePage"
186
- :on-fetch-pages="fetchPages"
187
- :on-delete-page="handleDelete"
188
- :on-navigate-to-page="handleNavigateToPage"
189
- @save="handleSave"
190
- @publish="handlePublish"
191
- @unpublish="handleUnpublish"
192
- @version-navigate="handleVersionNavigate"
193
- />
194
- </template>
195
- </template>
@@ -1,90 +0,0 @@
1
- <script setup lang="ts">
2
- import { computed, ref, watch } from "vue"
3
- import type { Page } from "../types/pages"
4
- import { getPageResolutionPath } from "../utils/page"
5
- import PageRenderer from "../PageRenderer.vue"
6
-
7
- export interface PageLiveViewProps {
8
- lookupPath?: string
9
- fetchUrl?: string
10
- cacheKey?: string
11
- baseUrl: string
12
- canEdit?: boolean
13
- }
14
- const {
15
- lookupPath,
16
- fetchUrl,
17
- cacheKey,
18
- baseUrl,
19
- canEdit = false
20
- } = defineProps<PageLiveViewProps>()
21
-
22
- const resolvedFetchUrl = computed(() => getPageResolutionPath(lookupPath || fetchUrl || ""))
23
- const page = ref<Page | null>(null)
24
- const pageStatus = ref<"idle" | "pending" | "success" | "error">("idle")
25
-
26
- const fetchPage = async () => {
27
- if (!resolvedFetchUrl.value) return
28
- pageStatus.value = "pending"
29
- try {
30
- const res = await fetch(resolvedFetchUrl.value)
31
- if (!res.ok) throw new Error("Failed to fetch page")
32
- const data = await res.json()
33
- page.value = data
34
- pageStatus.value = "success"
35
- } catch (err) {
36
- pageStatus.value = "error"
37
- }
38
- }
39
-
40
- watch(() => resolvedFetchUrl.value, fetchPage, { immediate: true })
41
-
42
- const resolvePage = async (idOrSlug: string) => {
43
- if (!idOrSlug) return null
44
- const clean = idOrSlug.replace(/^\//, "")
45
- try {
46
- const isUuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(clean)
47
- const url = isUuid ? `/api/pages/id/${clean}` : `/api/pages/find/${clean}`
48
- const res = await fetch(url)
49
- if (res.ok) return await res.json()
50
- const res2 = await fetch(`/api/pages/find/${clean}`)
51
- if (res2.ok) return await res2.json()
52
- return null
53
- } catch (err) {
54
- console.warn("[PageLiveView resolvePage Error]:", err)
55
- return null
56
- }
57
- }
58
-
59
- const editUrl = computed(() => {
60
- const base = baseUrl.replace(/\/$/, "")
61
- if (page.value?.slug) {
62
- const slug = page.value.slug
63
- const cleanBase = base.replace(/^\//, "")
64
- if (slug.startsWith(cleanBase)) {
65
- return `/${slug}/edit`
66
- }
67
- return `${base}/${slug}/edit`
68
- }
69
- return `${base}/edit`
70
- })
71
-
72
- defineOptions({ name: "PageLiveView" })
73
- </script>
74
-
75
- <template>
76
- <div v-if="pageStatus === 'pending'" class="h-full w-full flex items-center justify-center p-12">
77
- <span class="i-lucide-loader-2 size-12 animate-spin text-neutral-400" />
78
- </div>
79
- <div v-else-if="pageStatus === 'error' || !page" class="p-8 text-center">
80
- <h3 class="text-lg font-bold text-red-600">Page Not Found</h3>
81
- <p class="text-neutral-500 mt-2">The requested page could not be located.</p>
82
- </div>
83
- <PageRenderer
84
- v-else
85
- v-model="page"
86
- :resolve-page="resolvePage"
87
- :can-edit="canEdit"
88
- :edit-url="editUrl"
89
- />
90
- </template>
@@ -1,308 +0,0 @@
1
- <script setup lang="ts">
2
- import { ref, computed, watch } from "vue"
3
- import type { Page, PageVersion, PageDefinition } from "../types/pages"
4
- import {
5
- convertVersionToPage,
6
- syncPageWithDefinition,
7
- getPageResolutionPath,
8
- getLocalizedContent
9
- } from "../utils/page"
10
- import { useI18n } from "../utils/editorHelpers"
11
- import PageRenderer from "../PageRenderer.vue"
12
- import RLVButton from "../../button/RLVButton.vue"
13
- import PageVersionSelector from "../PageVersionSelector.vue"
14
-
15
- export interface PageReviewViewProps {
16
- lookupPath: string
17
- versionId?: string | null
18
- pageDefinitions: Record<string, PageDefinition>
19
- baseUrl: string
20
- isAdmin?: boolean
21
- }
22
-
23
- const {
24
- lookupPath,
25
- versionId,
26
- pageDefinitions,
27
- baseUrl,
28
- isAdmin = false
29
- } = defineProps<PageReviewViewProps>()
30
-
31
- export interface PageReviewViewEmits {
32
- approved: [version: PageVersion]
33
- rejected: [version: PageVersion]
34
- }
35
- const emit = defineEmits<PageReviewViewEmits>()
36
-
37
- const { t, locale } = useI18n()
38
-
39
- const page = ref<Page | null>(null)
40
- const pageStatus = ref<"idle" | "pending" | "success" | "error">("idle")
41
-
42
- const fetchBasePage = async () => {
43
- const url = getPageResolutionPath(lookupPath)
44
- if (!url) return
45
- pageStatus.value = "pending"
46
- try {
47
- const res = await fetch(url)
48
- if (!res.ok) throw new Error("Failed to fetch page")
49
- page.value = await res.json()
50
- pageStatus.value = "success"
51
- } catch (err) {
52
- pageStatus.value = "error"
53
- }
54
- }
55
-
56
- const versionData = ref<PageVersion | null>(null)
57
- const versionStatus = ref<"idle" | "pending" | "success" | "error">("idle")
58
-
59
- const fetchVersionData = async () => {
60
- if (!versionId) {
61
- versionData.value = null
62
- return
63
- }
64
- versionStatus.value = "pending"
65
- try {
66
- const res = await fetch(`/api/pages/versions/${versionId}`)
67
- if (!res.ok) throw new Error("Failed to fetch version")
68
- versionData.value = await res.json()
69
- versionStatus.value = "success"
70
- } catch (err) {
71
- versionStatus.value = "error"
72
- }
73
- }
74
-
75
- watch(() => lookupPath, fetchBasePage, { immediate: true })
76
- watch(() => versionId, fetchVersionData, { immediate: true })
77
-
78
- const localPage = ref<Page | null>(null)
79
- watch(
80
- [page, versionData],
81
- () => {
82
- if (versionId && versionData.value) {
83
- const converted = convertVersionToPage(versionData.value)
84
- const typeKey = Object.keys(pageDefinitions).find(
85
- (k) => k.toLowerCase() === converted.type?.toLowerCase()
86
- )
87
- const definition = typeKey ? pageDefinitions[typeKey] : null
88
- if (definition) {
89
- syncPageWithDefinition(converted, definition)
90
- }
91
- localPage.value = converted
92
- } else if (page.value) {
93
- const cloned = JSON.parse(JSON.stringify(page.value))
94
- const typeKey = Object.keys(pageDefinitions).find(
95
- (k) => k.toLowerCase() === cloned.type?.toLowerCase()
96
- )
97
- const definition = typeKey ? pageDefinitions[typeKey] : null
98
- if (definition) {
99
- syncPageWithDefinition(cloned, definition)
100
- }
101
- localPage.value = cloned
102
- }
103
- },
104
- { immediate: true }
105
- )
106
-
107
- const showCompare = ref(false)
108
- const leftWidth = ref(50)
109
- const isActioning = ref<string | null>(null)
110
-
111
- const resolvePage = async (idOrSlug: string): Promise<Pick<Page, "title" | "icon" | "slug">> => {
112
- const url = getPageResolutionPath(idOrSlug)
113
- if (!url) throw new Error(`[Review] Invalid page reference: ${idOrSlug}`)
114
- const res = await fetch(`${url}?select=title,icon,slug`)
115
- if (!res.ok) throw new Error(`[Review] Page not found: ${idOrSlug}`)
116
- return await res.json()
117
- }
118
-
119
- const navigateToView = () => {
120
- if (typeof window !== "undefined") {
121
- window.location.href = `/${relativeSlug.value}`
122
- }
123
- }
124
-
125
- const navigateToVersion = (v: PageVersion) => {
126
- if (typeof window !== "undefined") {
127
- window.location.search = `?version=${v.id}`
128
- }
129
- }
130
-
131
- const handleApprove = async () => {
132
- if (!versionId || !isAdmin) return
133
- isActioning.value = "approving"
134
- try {
135
- await fetch(`/api/pages/versions/${versionId}/approve`, { method: "POST" })
136
- await fetchBasePage()
137
- if (versionData.value) {
138
- emit("approved", versionData.value)
139
- }
140
- if (page.value?.slug) {
141
- const base = baseUrl.replace(/\/$/, "")
142
- const cleanBase = base.replace(/^\//, "")
143
- const slugToUse = page.value.slug.startsWith(cleanBase)
144
- ? page.value.slug
145
- : `${cleanBase}/${page.value.slug}`
146
- if (typeof window !== "undefined") {
147
- window.location.href = `/${slugToUse}`
148
- }
149
- }
150
- } catch (e) {
151
- console.error(e)
152
- } finally {
153
- isActioning.value = null
154
- }
155
- }
156
-
157
- const handleReject = async () => {
158
- if (!versionId || !isAdmin) return
159
- isActioning.value = "rejecting"
160
- try {
161
- await fetch(`/api/pages/versions/${versionId}/reject`, { method: "POST" })
162
- await fetchBasePage()
163
- if (versionData.value) {
164
- emit("rejected", versionData.value)
165
- }
166
- if (page.value?.slug) {
167
- const base = baseUrl.replace(/\/$/, "")
168
- const cleanBase = base.replace(/^\//, "")
169
- const slugToUse = page.value.slug.startsWith(cleanBase)
170
- ? page.value.slug
171
- : `${cleanBase}/${page.value.slug}`
172
- if (typeof window !== "undefined") {
173
- window.location.href = `/${slugToUse}/edit`
174
- }
175
- }
176
- } catch (e) {
177
- console.error(e)
178
- } finally {
179
- isActioning.value = null
180
- }
181
- }
182
-
183
- const relativeSlug = computed(() => {
184
- if (page.value?.slug) return page.value.slug
185
- const base = baseUrl.replace(/^\/|\/$/g, "")
186
- return lookupPath.replace(base, "").replace(/^\/|\/$/g, "")
187
- })
188
-
189
- defineOptions({ name: "PageReviewView" })
190
- </script>
191
-
192
- <template>
193
- <div
194
- v-if="pageStatus === 'pending' || (versionId && versionStatus === 'pending')"
195
- class="h-full w-full flex items-center justify-center p-12"
196
- >
197
- <span class="i-lucide-loader-2 size-12 animate-spin text-neutral-400" />
198
- </div>
199
-
200
- <div
201
- v-else-if="pageStatus === 'error' || versionStatus === 'error' || !localPage"
202
- class="p-8 text-center"
203
- >
204
- <h3 class="text-lg font-bold text-red-600">Version Not Found</h3>
205
- <p class="text-neutral-500 mt-2">The requested page version could not be located.</p>
206
- </div>
207
-
208
- <template v-else>
209
- <div
210
- class="h-12 w-full bg-neutral-100 dark:bg-neutral-800 flex items-center justify-between px-4 border-b border-default"
211
- >
212
- <div class="flex items-center gap-2">
213
- <span class="text-xs text-neutral-500 font-medium">
214
- Reviewing:
215
- <span class="text-neutral-900 dark:text-white font-semibold">
216
- {{ getLocalizedContent(localPage?.title, locale) }}
217
- </span>
218
- </span>
219
- <span class="w-px h-4 bg-neutral-200 dark:bg-neutral-700 mx-2" />
220
- <PageVersionSelector
221
- v-if="page?.id"
222
- :page-id="page.id"
223
- :current-version-id="versionId || undefined"
224
- :is-admin="isAdmin"
225
- @version-selected="navigateToVersion"
226
- />
227
- </div>
228
-
229
- <div class="flex items-center gap-2">
230
- <RLVButton
231
- color="neutral"
232
- variant="ghost"
233
- leading-leading-icon="i-lucide-external-link"
234
- :label="t('page_editor.view_page', 'View Page')"
235
- @click="navigateToView"
236
- />
237
- <RLVButton
238
- color="neutral"
239
- variant="ghost"
240
- :leading-leading-icon="showCompare ? 'i-lucide-eye-off' : 'i-lucide-eye'"
241
- :label="showCompare ? 'Hide' : 'Compare'"
242
- @click="showCompare = !showCompare"
243
- />
244
- <span class="w-px h-4 bg-neutral-200 dark:bg-neutral-700 mx-2" />
245
-
246
- <template v-if="isAdmin && versionId && versionData?.status === 'pending'">
247
- <RLVButton
248
- leading-leading-icon="i-lucide-x"
249
- :label="t('common.reject', 'Reject')"
250
- color="danger"
251
- variant="outline"
252
- :loading="isActioning === 'rejecting'"
253
- @click="handleReject"
254
- />
255
- <RLVButton
256
- leading-leading-icon="i-lucide-check"
257
- :label="t('common.approve', 'Approve Version')"
258
- color="primary"
259
- :loading="isActioning === 'approving'"
260
- @click="handleApprove"
261
- />
262
- </template>
263
-
264
- <template v-else-if="versionId && versionData?.status === 'approved'">
265
- <span
266
- class="text-xs bg-green-500/10 text-green-600 px-2.5 py-1 rounded-md font-medium border border-green-500/20"
267
- >
268
- Version Already Approved
269
- </span>
270
- </template>
271
-
272
- <template v-else-if="versionId && versionData?.status === 'rejected'">
273
- <span
274
- class="text-xs bg-red-500/10 text-red-600 px-2.5 py-1 rounded-md font-medium border border-green-500/20"
275
- >
276
- Version Rejected
277
- </span>
278
- </template>
279
- </div>
280
- </div>
281
-
282
- <div class="flex w-full min-h-0">
283
- <div :style="{ width: showCompare ? `${leftWidth}%` : '100%' }" class="overflow-y-auto">
284
- <PageRenderer
285
- v-if="localPage"
286
- :key="versionId || 'live'"
287
- v-model="localPage"
288
- :resolve-page="resolvePage"
289
- class="h-full"
290
- />
291
- </div>
292
-
293
- <div
294
- v-if="showCompare"
295
- :style="{ width: `${100 - leftWidth}%` }"
296
- class="overflow-y-auto border-l border-default bg-neutral-50/50 dark:bg-neutral-900/10"
297
- >
298
- <PageRenderer
299
- v-if="page"
300
- :key="'live-base-' + page.id"
301
- :model-value="page"
302
- :resolve-page="resolvePage"
303
- class="opacity-70 pointer-events-none select-none h-full"
304
- />
305
- </div>
306
- </div>
307
- </template>
308
- </template>
@@ -1,23 +0,0 @@
1
- import { DEFAULT_ICONS } from "../../icons"
2
- import { uiConfig } from "../../resolver"
3
-
4
- export interface ResolvedIcon {
5
- isClass: boolean
6
- content: string
7
- }
8
-
9
- const c = new Map<string, ResolvedIcon>(),
10
- isCls = (s: string) => s.startsWith("i-") || s.includes(":")
11
-
12
- export const resolveIcon = (k = "", custom?: string): ResolvedIcon => {
13
- if (!k) return { isClass: false, content: "" }
14
- const id = custom ? `${k}:${custom}` : k,
15
- cached = c.get(id)
16
- if (cached) return cached
17
- const raw = custom || uiConfig.icons?.[k] || k,
18
- res = isCls(raw)
19
- ? { isClass: true, content: raw }
20
- : { isClass: false, content: (DEFAULT_ICONS as any)[raw] || "" }
21
- c.set(id, res)
22
- return res
23
- }