@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.
- package/LICENSE +21 -0
- package/README.md +14 -12
- package/package.json +11 -14
- package/src/components/avatar/RLAAvatar.astro +4 -12
- package/src/components/badge/RLABadge.astro +2 -1
- package/src/components/badge/badge.theme.ts +0 -4
- package/src/components/banner/RLABanner.astro +1 -1
- package/src/components/banner/banner.theme.ts +1 -1
- package/src/components/breadcrumb/RLABreadcrumb.astro +2 -2
- package/src/components/button/RLAButton.astro +4 -1
- package/src/components/button/button.theme.ts +1 -5
- package/src/components/callout/RLACallout.astro +7 -5
- package/src/components/callout/callout.theme.ts +1 -5
- package/src/components/card/card.theme.ts +3 -3
- package/src/components/carousel/RLACarousel.astro +2 -2
- package/src/components/carousel/carousel.theme.ts +2 -2
- package/src/components/chart/RLAChart.astro +1 -1
- package/src/components/checkbox/RLACheckbox.astro +2 -1
- package/src/components/checkbox/checkbox.theme.ts +0 -4
- package/src/components/chip/RLAChip.astro +2 -1
- package/src/components/chip/chip.theme.ts +0 -4
- package/src/components/collapsible/RLACollapsible.astro +1 -1
- package/src/components/color-area/RLAColorArea.astro +1 -0
- package/src/components/color-area/color-area.theme.ts +1 -5
- package/src/components/color-field/RLAColorField.astro +1 -0
- package/src/components/color-field/color-field.theme.ts +1 -5
- package/src/components/color-slider/RLAColorSlider.astro +1 -0
- package/src/components/color-slider/color-slider.theme.ts +1 -5
- package/src/components/dashboard-group/RLADashboardGroup.astro +5 -7
- package/src/components/dashboard-group/dashboard-group.theme.ts +1 -1
- package/src/components/dashboard-group/dashboard-group.ts +1 -1
- package/src/components/dashboard-navbar/RLADashboardNavbar.astro +4 -4
- package/src/components/dashboard-navbar/dashboard-navbar.theme.ts +3 -3
- package/src/components/dashboard-navbar/dashboard-navbar.ts +1 -1
- package/src/components/dashboard-panel/RLADashboardPanel.astro +9 -8
- package/src/components/dashboard-panel/dashboard-panel.theme.ts +1 -1
- package/src/components/dashboard-sidebar/RLADashboardSidebar.astro +7 -4
- package/src/components/dashboard-sidebar/dashboard-sidebar.theme.ts +6 -5
- package/src/components/dashboard-sidebar-collapse/RLADashboardSidebarCollapse.astro +1 -0
- package/src/components/drawer/RLADrawer.astro +1 -1
- package/src/components/dropdown-menu/dropdown-menu.theme.ts +2 -2
- package/src/components/footer/RLAFooter.astro +2 -0
- package/src/components/footer/footer.theme.ts +0 -8
- package/src/components/gamepad/RLAGamepad.astro +1 -1
- package/src/components/head/UIHead.astro +1 -2
- package/src/components/header/header.theme.ts +1 -1
- package/src/components/icon/RLAIcon.astro +2 -5
- package/src/components/icon/RLSIcon.tsx +2 -2
- package/src/components/icon/RLVIcon.vue +18 -6
- package/src/components/icon/icon.theme.ts +1 -1
- package/src/components/image/image.theme.ts +1 -1
- package/src/components/input/RLAInput.astro +2 -1
- package/src/components/input/input.theme.ts +0 -4
- package/src/components/input-menu/RLAInputMenu.astro +2 -2
- package/src/components/input-menu/input-menu.theme.ts +1 -1
- package/src/components/input-rating/input-rating.theme.ts +2 -2
- package/src/components/kbd/RLAKbd.astro +2 -1
- package/src/components/kbd/kbd.theme.ts +0 -4
- package/src/components/keyboard/RLAKeyboard.astro +1 -1
- package/src/components/link/RLALink.astro +1 -0
- package/src/components/link/link.theme.ts +0 -4
- package/src/components/link-group/RLALinkGroup.astro +1 -1
- package/src/components/link-group/link-group.theme.ts +1 -10
- package/src/components/locale-selector/RLALocaleSelector.astro +4 -36
- package/src/components/locale-selector/locale-selector.theme.ts +3 -11
- package/src/components/logo/RLALogo.astro +1 -2
- package/src/components/modal/RLAModal.astro +1 -1
- package/src/components/modal/RLSModal.tsx +2 -2
- package/src/components/navigation-menu/RLANavigationMenu.astro +17 -21
- package/src/components/navigation-menu/navigation-menu.theme.ts +3 -7
- package/src/components/pagination/RLAPagination.astro +2 -2
- package/src/components/pagination/pagination.theme.ts +1 -1
- package/src/components/popover/RLAPopover.astro +39 -0
- package/src/components/progress/RLAProgress.astro +2 -0
- package/src/components/progress/progress.theme.ts +0 -4
- package/src/components/scroll-area/scroll-area.theme.ts +1 -1
- package/src/components/scroll-to-top/RLAScrollToTop.astro +6 -5
- package/src/components/scroll-to-top/RLVScrollToTop.vue +7 -7
- package/src/components/scroll-to-top/scroll-to-top.theme.ts +3 -7
- package/src/components/search/RLASearch.astro +3 -3
- package/src/components/select/RLASelect.astro +96 -528
- package/src/components/select/RLSSelect.tsx +68 -14
- package/src/components/select/RLVSelect.vue +57 -0
- package/src/components/select/select.theme.ts +11 -96
- package/src/components/shortcuts/RLAShortcuts.astro +9 -9
- package/src/components/sidebar/RLASidebar.astro +3 -3
- package/src/components/slideover/RLASlideover.astro +1 -1
- package/src/components/slider/RLASlider.astro +2 -1
- package/src/components/slider/slider.theme.ts +1 -5
- package/src/components/spinner/RLASpinner.astro +2 -1
- package/src/components/spinner/spinner.theme.ts +1 -5
- package/src/components/splitter/splitter.theme.ts +1 -1
- package/src/components/stepper/RLAStepper.astro +8 -8
- package/src/components/stepper/stepper.theme.ts +6 -7
- package/src/components/steps/RLASteps.astro +3 -3
- package/src/components/switch/RLASwitch.astro +2 -1
- package/src/components/switch/switch.theme.ts +0 -4
- package/src/components/table/table.theme.ts +1 -1
- package/src/components/table-of-contents/RLATableOfContents.astro +5 -5
- package/src/components/theme-selector/RLAThemeSelector.astro +16 -14
- package/src/components/theme-selector/theme-selector.theme.ts +2 -2
- package/src/components/toast/RLAToast.astro +6 -6
- package/src/components/toast/store.ts +1 -1
- package/src/components/tooltip/tooltip.theme.ts +1 -1
- package/src/icons.raw.ts +67 -0
- package/src/icons.ts +31 -64
- package/src/index.ts +19 -18
- package/src/preset.ts +110 -108
- package/src/resolver.ts +127 -15
- package/src/shortcuts.ts +103 -84
- package/src/stores.ts +22 -0
- package/src/types.ts +27 -9
- package/src/virtual.d.ts +4 -0
- package/src/components/editor/PageEditor.vue +0 -459
- package/src/components/editor/PageMention.vue +0 -87
- package/src/components/editor/PagePropertiesEditor.vue +0 -358
- package/src/components/editor/PagePropertiesRenderer.vue +0 -285
- package/src/components/editor/PageRenderer.vue +0 -155
- package/src/components/editor/PageSurround.vue +0 -130
- package/src/components/editor/PageVersionSelector.vue +0 -371
- package/src/components/editor/blocks/AddBlockModal.vue +0 -196
- package/src/components/editor/blocks/Block.vue +0 -142
- package/src/components/editor/blocks/BlockEditRenderer.vue +0 -26
- package/src/components/editor/blocks/BlockEditor.vue +0 -141
- package/src/components/editor/blocks/BlockViewRenderer.vue +0 -71
- package/src/components/editor/blocks/TextRenderer.vue +0 -31
- package/src/components/editor/blocks/editor/CalloutBlockEditor.vue +0 -53
- package/src/components/editor/blocks/editor/ImageBlockEditor.vue +0 -55
- package/src/components/editor/blocks/editor/ParagraphBlockEditor.vue +0 -31
- package/src/components/editor/blocks/editor/SectionBlockEditor.vue +0 -65
- package/src/components/editor/blocks/nodes/LinkNode.vue +0 -28
- package/src/components/editor/blocks/nodes/TextNode.vue +0 -22
- package/src/components/editor/blocks/renderer/CalloutBlockRenderer.vue +0 -65
- package/src/components/editor/blocks/renderer/ImageBlockRenderer.vue +0 -20
- package/src/components/editor/blocks/renderer/ParagraphBlockRenderer.vue +0 -21
- package/src/components/editor/blocks/renderer/SectionBlockRenderer.vue +0 -55
- package/src/components/editor/composables/injectionKeys.ts +0 -3
- package/src/components/editor/composables/useBlockEditor.ts +0 -261
- package/src/components/editor/composables/usePageEditor.ts +0 -97
- package/src/components/editor/modals/CreatePageModal.vue +0 -160
- package/src/components/editor/modals/DeletePageModal.vue +0 -116
- package/src/components/editor/modals/PageTreeModal.vue +0 -275
- package/src/components/editor/types/blocks.ts +0 -100
- package/src/components/editor/types/pages.ts +0 -89
- package/src/components/editor/types/schemas.ts +0 -36
- package/src/components/editor/utils/blocks.ts +0 -42
- package/src/components/editor/utils/editorHelpers.ts +0 -24
- package/src/components/editor/utils/page.ts +0 -219
- package/src/components/editor/utils/richTextHelpers.ts +0 -35
- package/src/components/editor/views/PageEditView.vue +0 -195
- package/src/components/editor/views/PageLiveView.vue +0 -90
- package/src/components/editor/views/PageReviewView.vue +0 -308
- 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>
|