@rimelight/ui 0.0.43 → 0.0.45

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 (154) hide show
  1. package/README.md +14 -12
  2. package/package.json +8 -10
  3. package/src/components/avatar/RLAAvatar.astro +4 -12
  4. package/src/components/badge/RLABadge.astro +2 -1
  5. package/src/components/badge/badge.theme.ts +0 -4
  6. package/src/components/banner/RLABanner.astro +1 -1
  7. package/src/components/banner/banner.theme.ts +1 -1
  8. package/src/components/breadcrumb/RLABreadcrumb.astro +2 -2
  9. package/src/components/button/RLAButton.astro +4 -1
  10. package/src/components/button/RLSButton.tsx +1 -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 +52 -20
  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 +8 -5
  68. package/src/components/modal/RLSModal.tsx +17 -12
  69. package/src/components/modal/modal.theme.ts +5 -4
  70. package/src/components/navigation-menu/RLANavigationMenu.astro +17 -21
  71. package/src/components/navigation-menu/navigation-menu.theme.ts +3 -7
  72. package/src/components/pagination/RLAPagination.astro +2 -2
  73. package/src/components/pagination/pagination.theme.ts +1 -1
  74. package/src/components/popover/RLAPopover.astro +39 -0
  75. package/src/components/progress/RLAProgress.astro +2 -0
  76. package/src/components/progress/progress.theme.ts +0 -4
  77. package/src/components/scroll-area/scroll-area.theme.ts +1 -1
  78. package/src/components/scroll-to-top/RLAScrollToTop.astro +6 -5
  79. package/src/components/scroll-to-top/RLVScrollToTop.vue +7 -7
  80. package/src/components/scroll-to-top/scroll-to-top.theme.ts +3 -7
  81. package/src/components/search/RLASearch.astro +3 -3
  82. package/src/components/select/RLASelect.astro +96 -528
  83. package/src/components/select/RLSSelect.tsx +68 -14
  84. package/src/components/select/RLVSelect.vue +57 -0
  85. package/src/components/select/select.theme.ts +11 -96
  86. package/src/components/shortcuts/RLAShortcuts.astro +9 -9
  87. package/src/components/sidebar/RLASidebar.astro +3 -3
  88. package/src/components/slideover/RLASlideover.astro +1 -1
  89. package/src/components/slider/RLASlider.astro +2 -1
  90. package/src/components/slider/slider.theme.ts +1 -5
  91. package/src/components/spinner/RLASpinner.astro +2 -1
  92. package/src/components/spinner/spinner.theme.ts +1 -5
  93. package/src/components/splitter/splitter.theme.ts +1 -1
  94. package/src/components/stepper/RLAStepper.astro +8 -8
  95. package/src/components/stepper/stepper.theme.ts +6 -7
  96. package/src/components/steps/RLASteps.astro +3 -3
  97. package/src/components/switch/RLASwitch.astro +2 -1
  98. package/src/components/switch/switch.theme.ts +0 -4
  99. package/src/components/table/table.theme.ts +1 -1
  100. package/src/components/table-of-contents/RLATableOfContents.astro +5 -5
  101. package/src/components/theme-selector/RLAThemeSelector.astro +16 -14
  102. package/src/components/theme-selector/theme-selector.theme.ts +2 -2
  103. package/src/components/toast/RLAToast.astro +6 -6
  104. package/src/components/toast/store.ts +1 -1
  105. package/src/components/tooltip/tooltip.theme.ts +1 -1
  106. package/src/icons.raw.ts +67 -0
  107. package/src/icons.ts +31 -64
  108. package/src/index.ts +19 -18
  109. package/src/preset.ts +110 -108
  110. package/src/resolver.ts +127 -15
  111. package/src/shortcuts.ts +103 -84
  112. package/src/stores.ts +22 -0
  113. package/src/types.ts +27 -9
  114. package/src/virtual.d.ts +4 -0
  115. package/src/components/editor/PageEditor.vue +0 -459
  116. package/src/components/editor/PageMention.vue +0 -87
  117. package/src/components/editor/PagePropertiesEditor.vue +0 -358
  118. package/src/components/editor/PagePropertiesRenderer.vue +0 -285
  119. package/src/components/editor/PageRenderer.vue +0 -155
  120. package/src/components/editor/PageSurround.vue +0 -130
  121. package/src/components/editor/PageVersionSelector.vue +0 -371
  122. package/src/components/editor/blocks/AddBlockModal.vue +0 -196
  123. package/src/components/editor/blocks/Block.vue +0 -142
  124. package/src/components/editor/blocks/BlockEditRenderer.vue +0 -26
  125. package/src/components/editor/blocks/BlockEditor.vue +0 -141
  126. package/src/components/editor/blocks/BlockViewRenderer.vue +0 -71
  127. package/src/components/editor/blocks/TextRenderer.vue +0 -31
  128. package/src/components/editor/blocks/editor/CalloutBlockEditor.vue +0 -53
  129. package/src/components/editor/blocks/editor/ImageBlockEditor.vue +0 -55
  130. package/src/components/editor/blocks/editor/ParagraphBlockEditor.vue +0 -31
  131. package/src/components/editor/blocks/editor/SectionBlockEditor.vue +0 -65
  132. package/src/components/editor/blocks/nodes/LinkNode.vue +0 -28
  133. package/src/components/editor/blocks/nodes/TextNode.vue +0 -22
  134. package/src/components/editor/blocks/renderer/CalloutBlockRenderer.vue +0 -65
  135. package/src/components/editor/blocks/renderer/ImageBlockRenderer.vue +0 -20
  136. package/src/components/editor/blocks/renderer/ParagraphBlockRenderer.vue +0 -21
  137. package/src/components/editor/blocks/renderer/SectionBlockRenderer.vue +0 -55
  138. package/src/components/editor/composables/injectionKeys.ts +0 -3
  139. package/src/components/editor/composables/useBlockEditor.ts +0 -261
  140. package/src/components/editor/composables/usePageEditor.ts +0 -97
  141. package/src/components/editor/modals/CreatePageModal.vue +0 -160
  142. package/src/components/editor/modals/DeletePageModal.vue +0 -116
  143. package/src/components/editor/modals/PageTreeModal.vue +0 -275
  144. package/src/components/editor/types/blocks.ts +0 -100
  145. package/src/components/editor/types/pages.ts +0 -89
  146. package/src/components/editor/types/schemas.ts +0 -36
  147. package/src/components/editor/utils/blocks.ts +0 -42
  148. package/src/components/editor/utils/editorHelpers.ts +0 -24
  149. package/src/components/editor/utils/page.ts +0 -219
  150. package/src/components/editor/utils/richTextHelpers.ts +0 -35
  151. package/src/components/editor/views/PageEditView.vue +0 -195
  152. package/src/components/editor/views/PageLiveView.vue +0 -90
  153. package/src/components/editor/views/PageReviewView.vue +0 -308
  154. package/src/components/icon/resolver.ts +0 -23
@@ -1,196 +0,0 @@
1
- <script setup lang="ts">
2
- import { ref, computed, inject } from "vue"
3
- import { BLOCK_DEFINITIONS, CATEGORY_ORDER, type BlockDefinition } from "../utils/blocks"
4
- import { useI18n } from "../utils/editorHelpers"
5
- import { scv, cx } from "css-variants"
6
- import { SECTION_LEVEL_KEY } from "../composables/injectionKeys"
7
- import RLVButton from "../../button/RLVButton.vue"
8
- import RLVInput from "../../input/RLVInput.vue"
9
-
10
- export interface AddBlockModalProps {}
11
- defineProps<AddBlockModalProps>()
12
-
13
- export interface AddBlockModalEmits {
14
- select: [definition: BlockDefinition]
15
- }
16
- const emit = defineEmits<AddBlockModalEmits>()
17
-
18
- const addBlockModalStyles = scv({
19
- slots: [
20
- "header",
21
- "headerTitle",
22
- "closeButton",
23
- "body",
24
- "search",
25
- "categoryList",
26
- "categoryHeader",
27
- "blockGrid",
28
- "blockItem",
29
- "blockIcon",
30
- "blockLabel",
31
- "blockDescription",
32
- "footerClass",
33
- "typeBadge",
34
- "emptyState"
35
- ],
36
- base: {
37
- header: "flex items-center justify-between px-4 py-3 border-b border-default",
38
- headerTitle: "text-base font-semibold leading-6",
39
- closeButton: "-my-1",
40
- body: "p-0 max-h-[70vh] flex flex-col overflow-hidden",
41
- search: "p-4 border-b border-default",
42
- categoryList: "overflow-y-auto flex-1 p-4 space-y-6 max-h-[50vh]",
43
- categoryHeader: "text-xs font-bold text-dimmed uppercase tracking-wider mb-3",
44
- blockGrid: "flex flex-col gap-2",
45
- blockItem:
46
- "flex items-start gap-3 p-3 rounded-lg border border-default hover:border-primary hover:bg-primary-50 dark:hover:bg-primary-950/20 transition-all cursor-pointer text-left w-full",
47
- blockIcon:
48
- "w-10 h-10 flex items-center justify-center rounded-md bg-neutral-100 dark:bg-neutral-900 text-neutral-500 group-hover:text-primary shrink-0",
49
- blockLabel: "font-medium text-sm block text-default",
50
- blockDescription: "text-xs text-dimmed block mt-0.5",
51
- footerClass: "flex justify-end gap-2 p-4 border-t border-default",
52
- typeBadge:
53
- "text-[10px] font-mono text-dimmed px-1.5 py-0.5 bg-neutral-100 dark:bg-neutral-900 rounded shrink-0",
54
- emptyState: "flex flex-col items-center justify-center py-12 text-center"
55
- }
56
- })
57
- const styles = computed(() => addBlockModalStyles())
58
-
59
- const open = defineModel<boolean>("open", { default: false })
60
- const { t } = useI18n()
61
- const searchQuery = ref("")
62
- const sectionLevel = inject(
63
- SECTION_LEVEL_KEY,
64
- computed(() => 1)
65
- )
66
- const filteredBlocks = computed(() => {
67
- let blocks = BLOCK_DEFINITIONS
68
- if (sectionLevel.value >= 6) {
69
- blocks = blocks.filter((b) => b.type !== "SectionBlock")
70
- }
71
- if (!searchQuery.value) return blocks
72
- const query = searchQuery.value.toLowerCase()
73
- return blocks.filter(
74
- (block) =>
75
- t(block.label).toLowerCase().includes(query) ||
76
- block.type.toLowerCase().includes(query) ||
77
- block.category.toLowerCase().includes(query) ||
78
- (block.description && t(block.description).toLowerCase().includes(query))
79
- )
80
- })
81
- const groupedBlocks = computed(() => {
82
- const groups: Record<string, BlockDefinition[]> = {}
83
- filteredBlocks.value.forEach((block) => {
84
- const category = block.category
85
- if (!groups[category]) {
86
- groups[category] = []
87
- }
88
- groups[category]!.push(block)
89
- })
90
- const sortedGroups: Record<string, BlockDefinition[]> = {}
91
- CATEGORY_ORDER.forEach((category) => {
92
- if (groups[category]) {
93
- sortedGroups[category] = groups[category]!
94
- }
95
- })
96
- Object.keys(groups).forEach((category) => {
97
- if (!sortedGroups[category]) {
98
- sortedGroups[category] = groups[category]!
99
- }
100
- })
101
- return sortedGroups
102
- })
103
-
104
- const handleSelect = (block: BlockDefinition) => {
105
- emit("select", block)
106
- open.value = false
107
- }
108
-
109
- defineOptions({ name: "AddBlockModal" })
110
- </script>
111
-
112
- <template>
113
- <div
114
- v-if="open"
115
- class="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/60 backdrop-blur-sm"
116
- @click.self="open = false"
117
- >
118
- <div
119
- class="bg-white dark:bg-neutral-900 border border-default rounded-2xl w-full max-w-lg shadow-xl overflow-hidden flex flex-col"
120
- >
121
- <div :class="styles.header">
122
- <h3 :class="styles.headerTitle">
123
- {{ t("page_editor.add_block", "Add Block") }}
124
- </h3>
125
- <RLVButton
126
- color="neutral"
127
- variant="ghost"
128
- leading-leading-icon="i-lucide-x"
129
- class="!p-1.5"
130
- :class="styles.closeButton"
131
- @click="open = false"
132
- />
133
- </div>
134
- <div :class="styles.body">
135
- <div :class="styles.search">
136
- <RLVInput
137
- v-model="searchQuery"
138
- leading-icon="i-lucide-search"
139
- :placeholder="t('page_editor.search_blocks', 'Search blocks...')"
140
- autofocus
141
- color="neutral"
142
- class="w-full"
143
- />
144
- </div>
145
- <div :class="styles.categoryList">
146
- <template v-if="Object.keys(groupedBlocks).length > 0">
147
- <div v-for="(blocksList, category) in groupedBlocks" :key="category" class="space-y-3">
148
- <h4 :class="styles.categoryHeader">
149
- {{ category }}
150
- </h4>
151
- <div :class="styles.blockGrid">
152
- <button
153
- v-for="block in blocksList"
154
- :key="block.type"
155
- :class="styles.blockItem"
156
- @click="handleSelect(block)"
157
- >
158
- <span :class="styles.blockIcon">
159
- <span :class="block.icon" class="size-5 shrink-0" />
160
- </span>
161
- <span class="flex-1 text-left">
162
- <span class="flex items-center justify-between gap-2">
163
- <span :class="styles.blockLabel">
164
- {{ t(block.label, block.type) }}
165
- </span>
166
- <span :class="styles.typeBadge">
167
- {{ block.type }}
168
- </span>
169
- </span>
170
- <span v-if="block.description" :class="styles.blockDescription">
171
- {{ t(block.description, "") }}
172
- </span>
173
- </span>
174
- </button>
175
- </div>
176
- </div>
177
- </template>
178
- <div v-else :class="styles.emptyState">
179
- <span class="i-lucide-search-slash size-10 text-neutral-400 mb-4" />
180
- <p class="text-dimmed">
181
- {{ t("page_editor.no_blocks_found", "No blocks found") }}
182
- </p>
183
- </div>
184
- </div>
185
- </div>
186
- <div :class="styles.footerClass">
187
- <RLVButton
188
- color="neutral"
189
- variant="ghost"
190
- :label="t('common.cancel', 'Cancel')"
191
- @click="open = false"
192
- />
193
- </div>
194
- </div>
195
- </div>
196
- </template>
@@ -1,142 +0,0 @@
1
- <script setup lang="ts">
2
- import { inject, defineAsyncComponent } from "vue"
3
- import type { Block as BlockType } from "../types/blocks"
4
- import ParagraphBlockEditor from "./editor/ParagraphBlockEditor.vue"
5
- import ImageBlockEditor from "./editor/ImageBlockEditor.vue"
6
- import RLVButton from "../../button/RLVButton.vue"
7
-
8
- const SectionBlockEditor = defineAsyncComponent(() => import("./editor/SectionBlockEditor.vue"))
9
- const CalloutBlockEditor = defineAsyncComponent(() => import("./editor/CalloutBlockEditor.vue"))
10
-
11
- export interface BlockProps {
12
- block: BlockType
13
- index: number
14
- total: number
15
- containerId?: string | null
16
- }
17
-
18
- const props = defineProps<BlockProps>()
19
-
20
- const editorApi = inject<any>("block-editor-api")
21
-
22
- const updateProps = (newProps: Record<string, any>) => {
23
- if (editorApi) {
24
- editorApi.updateBlockProps(props.block.id, newProps)
25
- }
26
- }
27
-
28
- const handleRemove = () => {
29
- if (editorApi) editorApi.removeBlock(props.block.id)
30
- }
31
-
32
- const handleMoveUp = () => {
33
- if (editorApi) editorApi.moveBlock(props.block.id, -1)
34
- }
35
-
36
- const handleMoveDown = () => {
37
- if (editorApi) editorApi.moveBlock(props.block.id, 1)
38
- }
39
-
40
- const handleDuplicate = () => {
41
- if (editorApi) editorApi.duplicateBlock(props.block.id)
42
- }
43
-
44
- const handleAddBefore = () => {
45
- if (editorApi) editorApi.openAddBlockModal(props.block.id, "before")
46
- }
47
-
48
- const handleAddAfter = () => {
49
- if (editorApi) editorApi.openAddBlockModal(props.block.id, "after")
50
- }
51
-
52
- defineOptions({ name: "Block" })
53
- </script>
54
-
55
- <template>
56
- <div class="relative group/block my-2 w-full">
57
- <div
58
- class="absolute -top-3 left-1/2 -translate-x-1/2 z-10 opacity-0 group-hover/block:opacity-100 transition-opacity flex gap-1 bg-white dark:bg-neutral-800 border border-default rounded-full px-2 py-0.5 shadow-md"
59
- >
60
- <RLVButton
61
- leading-icon="i-lucide-plus"
62
- variant="ghost"
63
- color="neutral"
64
- class="!p-1 text-xs"
65
- title="Add block before"
66
- @click="handleAddBefore"
67
- />
68
- <RLVButton
69
- leading-icon="i-lucide-arrow-up"
70
- variant="ghost"
71
- color="neutral"
72
- class="!p-1 text-xs"
73
- :disabled="index === 0"
74
- title="Move up"
75
- @click="handleMoveUp"
76
- />
77
- <RLVButton
78
- leading-icon="i-lucide-arrow-down"
79
- variant="ghost"
80
- color="neutral"
81
- class="!p-1 text-xs"
82
- :disabled="index === total - 1"
83
- title="Move down"
84
- @click="handleMoveDown"
85
- />
86
- <RLVButton
87
- leading-icon="i-lucide-copy"
88
- variant="ghost"
89
- color="neutral"
90
- class="!p-1 text-xs"
91
- title="Duplicate"
92
- @click="handleDuplicate"
93
- />
94
- <RLVButton
95
- leading-icon="i-lucide-trash"
96
- variant="ghost"
97
- color="danger"
98
- class="!p-1 text-xs"
99
- title="Delete"
100
- @click="handleRemove"
101
- />
102
- </div>
103
-
104
- <div class="w-full transition-all rounded-xl hover:ring-1 hover:ring-primary/40">
105
- <SectionBlockEditor
106
- v-if="block.type === 'SectionBlock'"
107
- :props="block.props as any"
108
- :container-id="block.id"
109
- @update:props="updateProps"
110
- />
111
- <ParagraphBlockEditor
112
- v-else-if="block.type === 'ParagraphBlock'"
113
- :props="block.props as any"
114
- @update:props="updateProps"
115
- />
116
- <CalloutBlockEditor
117
- v-else-if="block.type === 'CalloutBlock'"
118
- :props="block.props as any"
119
- :container-id="block.id"
120
- @update:props="updateProps"
121
- />
122
- <ImageBlockEditor
123
- v-else-if="block.type === 'ImageBlock'"
124
- :props="block.props as any"
125
- @update:props="updateProps"
126
- />
127
- </div>
128
-
129
- <div
130
- class="absolute -bottom-3 left-1/2 -translate-x-1/2 z-10 opacity-0 group-hover/block:opacity-100 transition-opacity flex gap-1 bg-white dark:bg-neutral-800 border border-default rounded-full px-2 py-0.5 shadow-md"
131
- >
132
- <RLVButton
133
- leading-icon="i-lucide-plus"
134
- variant="ghost"
135
- color="neutral"
136
- class="!p-1 text-xs"
137
- title="Add block after"
138
- @click="handleAddAfter"
139
- />
140
- </div>
141
- </div>
142
- </template>
@@ -1,26 +0,0 @@
1
- <script setup lang="ts">
2
- import type { Block as BlockType } from "../types/blocks"
3
- import Block from "./Block.vue"
4
-
5
- export interface BlockEditRendererProps {
6
- blocks: BlockType[]
7
- containerId?: string | null
8
- }
9
-
10
- defineProps<BlockEditRendererProps>()
11
- defineEmits<{
12
- start: []
13
- end: []
14
- change: [any]
15
- }>()
16
-
17
- defineOptions({ name: "BlockEditRenderer" })
18
- </script>
19
-
20
- <template>
21
- <div class="flex flex-col gap-4 w-full">
22
- <template v-for="(block, index) in blocks" :key="block.id">
23
- <Block :block="block" :index="index" :total="blocks.length" :container-id="containerId" />
24
- </template>
25
- </div>
26
- </template>
@@ -1,141 +0,0 @@
1
- <script setup lang="ts">
2
- import { provide, ref, computed } from "vue"
3
- import type { Block } from "../types/blocks"
4
- import { useBlockEditor } from "../composables/useBlockEditor"
5
- import { type BlockDefinition } from "../utils/blocks"
6
- import { useI18n } from "../utils/editorHelpers"
7
- import { scv, cx } from "css-variants"
8
- import BlockEditRenderer from "./BlockEditRenderer.vue"
9
- import AddBlockModal from "./AddBlockModal.vue"
10
- import RLVButton from "../../button/RLVButton.vue"
11
-
12
- export interface BlockEditorProps {
13
- historyLimit?: number
14
- containerId?: string | null
15
- }
16
- const { historyLimit, containerId = null } = defineProps<BlockEditorProps>()
17
-
18
- export interface BlockEditorEmits {
19
- save: []
20
- mutation: []
21
- start: []
22
- end: []
23
- change: [any]
24
- }
25
- const emit = defineEmits<BlockEditorEmits>()
26
-
27
- const blockEditorStyles = scv({
28
- slots: ["root", "footerClass"],
29
- base: {
30
- root: "flex flex-col gap-8 w-full",
31
- footerClass: "flex flex-col items-center justify-center gap-4 p-4"
32
- }
33
- })
34
- const styles = computed(() => blockEditorStyles())
35
-
36
- const blocks = defineModel<Block[]>({ required: true })
37
- const { t } = useI18n()
38
- const {
39
- removeBlock,
40
- moveBlock,
41
- duplicateBlock,
42
- updateBlockProps,
43
- insertBlock,
44
- relocateBlock,
45
- undo,
46
- redo,
47
- canUndo,
48
- canRedo
49
- } = useBlockEditor(blocks, { maxHistorySize: historyLimit, onMutation: () => emit("mutation") })
50
-
51
- const isAddBlockModalOpen = ref(false)
52
- const addBlockTarget = ref<{ id: string | null; position: "before" | "after" }>({
53
- id: null,
54
- position: "after"
55
- })
56
-
57
- provide("block-editor-api", {
58
- removeBlock,
59
- moveBlock,
60
- duplicateBlock,
61
- updateBlockProps,
62
- insertBlock,
63
- relocateBlock,
64
- canUndo,
65
- canRedo,
66
- undo,
67
- redo,
68
- openAddBlockModal
69
- })
70
-
71
- defineExpose({ undo, redo, canUndo, canRedo })
72
-
73
- function uuidv7(): string {
74
- if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
75
- return crypto.randomUUID()
76
- }
77
- return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15)
78
- }
79
-
80
- function regenerateIds(block: Block): void {
81
- block.id = uuidv7()
82
- if (block.props && "children" in block.props && Array.isArray(block.props.children)) {
83
- block.props.children.forEach((child: Block) => regenerateIds(child))
84
- }
85
- }
86
-
87
- function openAddBlockModal(targetId: string | null = null, position: "before" | "after" = "after") {
88
- addBlockTarget.value = { id: targetId, position }
89
- isAddBlockModalOpen.value = true
90
- }
91
-
92
- const handleBlockSelect = (definition: BlockDefinition) => {
93
- insertBlock(definition.type, addBlockTarget.value.id, addBlockTarget.value.position)
94
- }
95
-
96
- const handleDragStart = () => {
97
- emit("start")
98
- }
99
-
100
- const handleDragEnd = async () => {
101
- emit("end")
102
- emit("mutation")
103
- }
104
-
105
- const handleBlockChange = (event: any) => {
106
- if (event.added) {
107
- const block = event.added.element
108
- if (block) {
109
- regenerateIds(block)
110
- }
111
- }
112
- emit("change", event)
113
- if (event.added || event.removed || event.moved) {
114
- emit("mutation")
115
- }
116
- }
117
-
118
- defineOptions({ name: "BlockEditor" })
119
- </script>
120
-
121
- <template>
122
- <div :class="styles.root">
123
- <BlockEditRenderer
124
- v-model:blocks="blocks"
125
- :container-id="containerId"
126
- @start="handleDragStart"
127
- @end="handleDragEnd"
128
- @change="handleBlockChange"
129
- />
130
- <div v-if="blocks && blocks.length > 0" :class="styles.footerClass">
131
- <RLVButton
132
- color="neutral"
133
- :label="t('page_editor.add_block', 'Add Block')"
134
- variant="outline"
135
- leading-leading-icon="i-lucide-plus"
136
- @click="openAddBlockModal()"
137
- />
138
- </div>
139
- <AddBlockModal v-model:open="isAddBlockModalOpen" @select="handleBlockSelect" />
140
- </div>
141
- </template>
@@ -1,71 +0,0 @@
1
- <script setup lang="ts">
2
- import { defineAsyncComponent } from "vue"
3
- import ParagraphBlockRenderer from "./renderer/ParagraphBlockRenderer.vue"
4
- import ImageBlockRenderer from "./renderer/ImageBlockRenderer.vue"
5
-
6
- const SectionBlockRenderer = defineAsyncComponent(
7
- () => import("./renderer/SectionBlockRenderer.vue")
8
- )
9
- const CalloutBlockRenderer = defineAsyncComponent(
10
- () => import("./renderer/CalloutBlockRenderer.vue")
11
- )
12
-
13
- export interface BlockViewRendererProps {
14
- blocks: any[]
15
- }
16
-
17
- defineProps<BlockViewRendererProps>()
18
-
19
- function normalizeBlockType(type: string = ""): string {
20
- if (!type) return ""
21
- return type.toLowerCase().replace(/block$/, "")
22
- }
23
-
24
- function normalizeProps(block: any): any {
25
- if (!block) return {}
26
- const merged = {
27
- ...block.attrs,
28
- ...block.props
29
- }
30
- if (!merged.text && merged.content) {
31
- merged.text = merged.content
32
- }
33
- if (!merged.children) {
34
- if (Array.isArray(block.children)) {
35
- merged.children = block.children
36
- } else if (Array.isArray(block.blocks)) {
37
- merged.children = block.blocks
38
- } else if (block.slots?.default && Array.isArray(block.slots.default)) {
39
- merged.children = block.slots.default
40
- } else if (block.slots?.main && Array.isArray(block.slots.main)) {
41
- merged.children = block.slots.main
42
- }
43
- }
44
- return merged
45
- }
46
-
47
- defineOptions({ name: "BlockViewRenderer" })
48
- </script>
49
-
50
- <template>
51
- <div class="flex flex-col gap-2 w-full">
52
- <template v-for="(block, index) in blocks" :key="block.id || index">
53
- <SectionBlockRenderer
54
- v-if="normalizeBlockType(block.type) === 'section'"
55
- :props="normalizeProps(block)"
56
- />
57
- <ParagraphBlockRenderer
58
- v-else-if="normalizeBlockType(block.type) === 'paragraph'"
59
- :props="normalizeProps(block)"
60
- />
61
- <CalloutBlockRenderer
62
- v-else-if="normalizeBlockType(block.type) === 'callout'"
63
- :props="normalizeProps(block)"
64
- />
65
- <ImageBlockRenderer
66
- v-else-if="normalizeBlockType(block.type) === 'image'"
67
- :props="normalizeProps(block)"
68
- />
69
- </template>
70
- </div>
71
- </template>
@@ -1,31 +0,0 @@
1
- <script setup lang="ts">
2
- import { useI18n } from "../utils/editorHelpers"
3
- import { getLocalizedContent } from "../utils/page"
4
- import TextNode from "./nodes/TextNode.vue"
5
- import LinkNode from "./nodes/LinkNode.vue"
6
- import PageMention from "../PageMention.vue"
7
-
8
- export interface TextRendererProps {
9
- content: any
10
- }
11
-
12
- defineProps<TextRendererProps>()
13
- const { locale } = useI18n()
14
- defineOptions({ name: "TextRenderer" })
15
- </script>
16
-
17
- <template>
18
- <span v-if="Array.isArray(content)">
19
- <template v-for="(item, index) in content" :key="item.id || index">
20
- <TextNode v-if="item.type === 'text'" :node="item" />
21
- <LinkNode v-else-if="item.type === 'link'" :node="item" />
22
- <PageMention
23
- v-else-if="item.type === 'mention'"
24
- :page-id="item.props?.pageId || item.pageId"
25
- />
26
- <span v-else>{{ getLocalizedContent(item, locale) }}</span>
27
- </template>
28
- </span>
29
- <span v-else-if="typeof content === 'string'">{{ content }}</span>
30
- <span v-else-if="content">{{ getLocalizedContent(content, locale) }}</span>
31
- </template>
@@ -1,53 +0,0 @@
1
- <script setup lang="ts">
2
- import { computed } from "vue"
3
- import type { CalloutBlockProps, CalloutVariant } from "../../types/blocks"
4
-
5
- import BlockEditRenderer from "../BlockEditRenderer.vue"
6
-
7
- const props = defineProps<{ props: CalloutBlockProps; containerId?: string | null }>()
8
- const emit = defineEmits<{ "update:props": [value: CalloutBlockProps] }>()
9
-
10
- const variant = computed({
11
- get: () => props.props.variant || "info",
12
- set: (val: CalloutVariant) => emit("update:props", { ...props.props, variant: val })
13
- })
14
-
15
- const children = computed({
16
- get: () => props.props.children || [],
17
- set: (val: any[]) => emit("update:props", { ...props.props, children: val })
18
- })
19
-
20
- const variants: CalloutVariant[] = [
21
- "info",
22
- "success",
23
- "warning",
24
- "error",
25
- "commentary",
26
- "ideation",
27
- "source"
28
- ]
29
-
30
- defineOptions({ name: "CalloutBlockEditor" })
31
- </script>
32
-
33
- <template>
34
- <div
35
- class="w-full flex flex-col gap-3 border border-default rounded-xl p-4 bg-white dark:bg-neutral-900"
36
- >
37
- <div class="flex items-center gap-2">
38
- <label class="text-xs font-bold uppercase tracking-wider text-dimmed">Callout Type:</label>
39
- <select
40
- v-model="variant"
41
- class="bg-neutral-100 dark:bg-neutral-800 border border-default rounded px-2 py-1 text-xs font-medium focus:outline-none"
42
- >
43
- <option v-for="v in variants" :key="v" :value="v">
44
- {{ v.toUpperCase() }}
45
- </option>
46
- </select>
47
- </div>
48
-
49
- <div class="p-3 rounded-lg border border-dashed border-default">
50
- <BlockEditRenderer v-model:blocks="children" :container-id="containerId" />
51
- </div>
52
- </div>
53
- </template>