vue-wswg-editor 0.0.9 → 0.0.11
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/README.md +493 -20
- package/dist/style.css +1 -1
- package/dist/vue-wswg-editor.es.js +2431 -1895
- package/package.json +15 -8
- package/src/assets/styles/_mixins.scss +12 -17
- package/src/components/AddBlockItem/AddBlockItem.vue +5 -5
- package/src/components/BlockBrowser/BlockBrowser.vue +33 -3
- package/src/components/BlockEditorFieldNode/BlockEditorFieldNode.vue +79 -34
- package/src/components/BlockEditorFields/BlockEditorFields.vue +15 -6
- package/src/components/BlockImageFieldNode/BlockImageNode.vue +373 -0
- package/src/components/BlockMarginFieldNode/BlockMarginNode.vue +6 -4
- package/src/components/BlockRepeaterFieldNode/BlockRepeaterNode.vue +4 -2
- package/src/components/BrowserNavigation/BrowserNavigation.vue +2 -2
- package/src/components/EmptyState/EmptyState.vue +1 -1
- package/src/components/PageBlockList/PageBlockList.vue +1 -9
- package/src/components/PageBuilderSidebar/PageBuilderSidebar.vue +29 -10
- package/src/components/PageBuilderToolbar/PageBuilderToolbar.vue +4 -4
- package/src/components/PageRenderer/PageRenderer.vue +76 -24
- package/src/components/PageRenderer/blockModules.ts +32 -3
- package/src/components/PageRenderer/layoutModules.ts +32 -0
- package/src/components/PageSettings/PageSettings.vue +19 -11
- package/src/components/ResizeHandle/ResizeHandle.vue +10 -10
- package/src/components/WswgJsonEditor/WswgJsonEditor.vue +323 -117
- package/src/index.ts +2 -2
- package/src/style.css +10 -3
- package/src/types/Block.d.ts +1 -1
- package/src/util/fieldConfig.ts +29 -0
- package/src/util/registry.ts +30 -23
- package/src/util/validation.ts +178 -23
- package/src/vite-plugin.ts +27 -2
- package/types/vue-wswg-editor.d.ts +161 -0
- package/dist/types/components/AddBlockItem/AddBlockItem.vue.d.ts +0 -6
- package/dist/types/components/BlockBrowser/BlockBrowser.vue.d.ts +0 -2
- package/dist/types/components/BlockComponent/BlockComponent.vue.d.ts +0 -15
- package/dist/types/components/BlockEditorFieldNode/BlockEditorFieldNode.vue.d.ts +0 -15
- package/dist/types/components/BlockEditorFields/BlockEditorFields.vue.d.ts +0 -15
- package/dist/types/components/BlockMarginFieldNode/BlockMarginNode.vue.d.ts +0 -23
- package/dist/types/components/BlockRepeaterFieldNode/BlockRepeaterNode.vue.d.ts +0 -15
- package/dist/types/components/BrowserNavigation/BrowserNavigation.vue.d.ts +0 -5
- package/dist/types/components/EmptyState/EmptyState.vue.d.ts +0 -15
- package/dist/types/components/PageBlockList/PageBlockList.vue.d.ts +0 -19
- package/dist/types/components/PageBuilderSidebar/PageBuilderSidebar.vue.d.ts +0 -30
- package/dist/types/components/PageBuilderToolbar/PageBuilderToolbar.vue.d.ts +0 -28
- package/dist/types/components/PageRenderer/PageRenderer.vue.d.ts +0 -6
- package/dist/types/components/PageRenderer/blockModules.d.ts +0 -1
- package/dist/types/components/PageSettings/PageSettings.vue.d.ts +0 -15
- package/dist/types/components/ResizeHandle/ResizeHandle.vue.d.ts +0 -6
- package/dist/types/components/WswgJsonEditor/WswgJsonEditor.test.d.ts +0 -1
- package/dist/types/components/WswgJsonEditor/WswgJsonEditor.vue.d.ts +0 -40
- package/dist/types/index.d.ts +0 -7
- package/dist/types/util/fieldConfig.d.ts +0 -82
- package/dist/types/util/helpers.d.ts +0 -28
- package/dist/types/util/registry.d.ts +0 -20
- package/dist/types/util/validation.d.ts +0 -15
- package/dist/types/vite-plugin.d.ts +0 -9
- package/dist/vite-plugin.js +0 -53
|
@@ -1,44 +1,89 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div id="page-
|
|
3
|
-
<
|
|
4
|
-
<component :is="
|
|
5
|
-
|
|
2
|
+
<div id="page-viewport" class="page-renderer-wrapper relative">
|
|
3
|
+
<template v-if="isReady">
|
|
4
|
+
<component :is="layoutComponent" v-if="withLayout && layoutComponent" v-bind="settings">
|
|
5
|
+
<template #default>
|
|
6
|
+
<div id="page-blocks-wrapper">
|
|
7
|
+
<div
|
|
8
|
+
v-for="block in blocks"
|
|
9
|
+
:key="block.id"
|
|
10
|
+
class="block-wrapper"
|
|
11
|
+
:class="{ [getMarginClass(block)]: true }"
|
|
12
|
+
>
|
|
13
|
+
<component :is="getBlock(block.type)" v-bind="block" :key="`block-${block.id}`" />
|
|
14
|
+
</div>
|
|
15
|
+
</div>
|
|
16
|
+
</template>
|
|
17
|
+
</component>
|
|
18
|
+
<div v-else id="page-blocks-wrapper">
|
|
19
|
+
<div
|
|
20
|
+
v-for="block in blocks"
|
|
21
|
+
:key="block.id"
|
|
22
|
+
class="block-wrapper"
|
|
23
|
+
:class="{ [getMarginClass(block)]: true }"
|
|
24
|
+
>
|
|
25
|
+
<component :is="getBlock(block.type)" v-bind="block" :key="`block-${block.id}`" />
|
|
26
|
+
</div>
|
|
27
|
+
<pre>{{ blocks }}</pre>
|
|
28
|
+
</div>
|
|
29
|
+
</template>
|
|
6
30
|
</div>
|
|
7
31
|
</template>
|
|
8
32
|
|
|
9
33
|
<script setup lang="ts">
|
|
10
|
-
import { type Component } from "vue";
|
|
34
|
+
import { type Component, computed, withDefaults, onBeforeMount, ref } from "vue";
|
|
11
35
|
import { generateNameVariations } from "../../util/helpers";
|
|
12
|
-
import {
|
|
36
|
+
import { getBlockModule, loadBlockModules } from "./blockModules";
|
|
37
|
+
import { loadLayoutModules, getLayoutModule } from "./layoutModules";
|
|
13
38
|
import type { Block } from "../../types/Block";
|
|
14
39
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
40
|
+
const props = withDefaults(
|
|
41
|
+
defineProps<{
|
|
42
|
+
blocks: Block[];
|
|
43
|
+
layout?: string;
|
|
44
|
+
settings?: Record<string, any>;
|
|
45
|
+
withLayout?: boolean;
|
|
46
|
+
}>(),
|
|
47
|
+
{
|
|
48
|
+
layout: "default",
|
|
49
|
+
settings: () => ({}),
|
|
50
|
+
withLayout: false,
|
|
51
|
+
}
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
const isReady = ref(false);
|
|
18
55
|
|
|
19
56
|
function getBlock(blockType: string): Component | undefined {
|
|
20
|
-
// Generate name variations and try to find a match
|
|
57
|
+
// Generate name variations and try to find a match
|
|
21
58
|
const nameVariations = generateNameVariations(blockType);
|
|
22
59
|
|
|
23
|
-
//
|
|
24
|
-
for (const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
60
|
+
// Try each variation to find the block component
|
|
61
|
+
for (const variation of nameVariations) {
|
|
62
|
+
const module = getBlockModule(variation);
|
|
63
|
+
if (module) return module;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function getLayout(layoutName: string): Component | undefined {
|
|
70
|
+
// Generate name variations and try to find a match in layoutModules keys (file paths)
|
|
71
|
+
const nameVariations = generateNameVariations(layoutName);
|
|
72
|
+
|
|
73
|
+
// Try each variation to find the block component
|
|
74
|
+
for (const variation of nameVariations) {
|
|
75
|
+
const module = getLayoutModule(variation);
|
|
76
|
+
if (module) return module;
|
|
37
77
|
}
|
|
38
78
|
|
|
39
79
|
return undefined;
|
|
40
80
|
}
|
|
41
81
|
|
|
82
|
+
// Get the layout component based on the layout prop
|
|
83
|
+
const layoutComponent = computed(() => {
|
|
84
|
+
return getLayout(props.layout);
|
|
85
|
+
});
|
|
86
|
+
|
|
42
87
|
// Get the margin class for the block
|
|
43
88
|
// Margin is an object with top and bottom properties
|
|
44
89
|
// margin classses are formatted as `margin-<direction>-<size>`
|
|
@@ -54,6 +99,13 @@ function getMarginClass(block: Block): string {
|
|
|
54
99
|
|
|
55
100
|
return [getClass(top, "top"), getClass(bottom, "bottom")].join(" ");
|
|
56
101
|
}
|
|
102
|
+
|
|
103
|
+
onBeforeMount(async () => {
|
|
104
|
+
isReady.value = false;
|
|
105
|
+
await loadBlockModules();
|
|
106
|
+
await loadLayoutModules();
|
|
107
|
+
isReady.value = true;
|
|
108
|
+
});
|
|
57
109
|
</script>
|
|
58
110
|
|
|
59
111
|
<style scoped lang="scss">
|
|
@@ -1,3 +1,32 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
|
|
1
|
+
// Import from virtual module created by vue-wswg-editor vite plugin
|
|
2
|
+
// Use lazy initialization to avoid circular dependency issues with createField
|
|
3
|
+
// The import is deferred until first access, ensuring createField is available when blocks load
|
|
4
|
+
import { type Component } from "vue";
|
|
5
|
+
import { getModuleDefault } from "../../util/registry";
|
|
6
|
+
import { markRaw } from "vue";
|
|
7
|
+
let _blockModules: Record<string, Component> | null = null;
|
|
8
|
+
|
|
9
|
+
export async function loadBlockModules(): Promise<Record<string, Component> | null> {
|
|
10
|
+
if (!_blockModules) {
|
|
11
|
+
_blockModules = {};
|
|
12
|
+
// Lazy load virtual modules to prevent initialization order issues
|
|
13
|
+
const { modules: discoveredModules } = await import("vue-wswg-editor:blocks");
|
|
14
|
+
for (const [, module] of Object.entries(discoveredModules)) {
|
|
15
|
+
let resolvedModule = module;
|
|
16
|
+
// If module is a function (lazy-loaded), call it to get the actual module
|
|
17
|
+
if (typeof module === "function") {
|
|
18
|
+
resolvedModule = await module();
|
|
19
|
+
}
|
|
20
|
+
const component = getModuleDefault(resolvedModule);
|
|
21
|
+
if (component && component.__name) {
|
|
22
|
+
const blockType = component.type || component.__name;
|
|
23
|
+
_blockModules[blockType] = markRaw(component);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return _blockModules;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function getBlockModule(type: string): Component | undefined {
|
|
31
|
+
return _blockModules?.[type];
|
|
32
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// Import from virtual module created by vue-wswg-editor vite plugin
|
|
2
|
+
// Use lazy initialization to avoid circular dependency issues with createField
|
|
3
|
+
// The import is deferred until first access, ensuring createField is available when blocks load
|
|
4
|
+
import { type Component } from "vue";
|
|
5
|
+
import { getModuleDefault } from "../../util/registry";
|
|
6
|
+
import { markRaw } from "vue";
|
|
7
|
+
let _layoutModules: Record<string, Component> | null = null;
|
|
8
|
+
|
|
9
|
+
export async function loadLayoutModules(): Promise<Record<string, Component> | null> {
|
|
10
|
+
if (!_layoutModules) {
|
|
11
|
+
_layoutModules = {};
|
|
12
|
+
// Lazy load virtual modules to prevent initialization order issues
|
|
13
|
+
const { modules: discoveredModules } = await import("vue-wswg-editor:layouts");
|
|
14
|
+
for (const [, module] of Object.entries(discoveredModules)) {
|
|
15
|
+
let resolvedModule = module;
|
|
16
|
+
// If module is a function (lazy-loaded), call it to get the actual module
|
|
17
|
+
if (typeof module === "function") {
|
|
18
|
+
resolvedModule = await module();
|
|
19
|
+
}
|
|
20
|
+
const component = getModuleDefault(resolvedModule);
|
|
21
|
+
if (component && component.__name) {
|
|
22
|
+
const layoutType = component.type || component.__name;
|
|
23
|
+
_layoutModules[layoutType] = markRaw(component);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return _layoutModules;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function getLayoutModule(type: string): Component | undefined {
|
|
31
|
+
return _layoutModules?.[type];
|
|
32
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="page-settings">
|
|
3
|
-
<div class="flex items-start justify-between border-b bg-white p-5">
|
|
3
|
+
<div class="flex items-start justify-between border-b border-gray-300 bg-white p-5">
|
|
4
4
|
<div>
|
|
5
5
|
<button
|
|
6
6
|
class="cursor-pointer text-sm text-zinc-500 hover:text-zinc-900 hover:underline"
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
<h4 class="mt-1 text-lg font-bold">Page settings</h4>
|
|
12
12
|
</div>
|
|
13
13
|
</div>
|
|
14
|
-
<div class="border-b p-5">
|
|
14
|
+
<div class="border-b border-gray-300 p-5">
|
|
15
15
|
<!-- Page layout -->
|
|
16
16
|
<div class="editor-field-node">
|
|
17
17
|
<!-- Label -->
|
|
@@ -19,7 +19,11 @@
|
|
|
19
19
|
<label class="mr-auto font-medium first-letter:uppercase">Page layout</label>
|
|
20
20
|
</div>
|
|
21
21
|
|
|
22
|
-
<select
|
|
22
|
+
<select
|
|
23
|
+
v-model="pageData[settingsKey].layout"
|
|
24
|
+
class="form-control w-full rounded-md border border-gray-300 p-2"
|
|
25
|
+
@change="getLayoutSettings"
|
|
26
|
+
>
|
|
23
27
|
<option v-for="layout in availableLayouts" :key="`layout-${layout.__name}`" :value="layout.__name">
|
|
24
28
|
{{ layout.label }}
|
|
25
29
|
</option>
|
|
@@ -29,7 +33,7 @@
|
|
|
29
33
|
<!-- Page settings -->
|
|
30
34
|
<div class="editor-field-node">
|
|
31
35
|
<BlockEditorFields
|
|
32
|
-
v-model="pageData
|
|
36
|
+
v-model="pageData[settingsKey]"
|
|
33
37
|
:fields="pageSettingsFields"
|
|
34
38
|
:editable="true"
|
|
35
39
|
:isLayoutBlock="true"
|
|
@@ -50,21 +54,25 @@ const emit = defineEmits<{
|
|
|
50
54
|
const pageData = defineModel<any>();
|
|
51
55
|
const pageSettingsFields = ref<any>({});
|
|
52
56
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
+
const props = withDefaults(
|
|
58
|
+
defineProps<{
|
|
59
|
+
settingsKey?: string;
|
|
60
|
+
}>(),
|
|
61
|
+
{
|
|
62
|
+
settingsKey: "settings",
|
|
63
|
+
}
|
|
64
|
+
);
|
|
57
65
|
const availableLayouts = computed(() => {
|
|
58
66
|
return getLayouts();
|
|
59
67
|
});
|
|
60
68
|
|
|
61
69
|
function getLayoutSettings() {
|
|
62
|
-
if (!pageData.value.
|
|
63
|
-
pageSettingsFields.value = getLayoutFields(pageData.value.
|
|
70
|
+
if (!pageData.value[props.settingsKey].layout) return;
|
|
71
|
+
pageSettingsFields.value = getLayoutFields(pageData.value[props.settingsKey].layout) || null;
|
|
64
72
|
}
|
|
65
73
|
|
|
66
74
|
onBeforeMount(() => {
|
|
67
|
-
if (!pageData.value.
|
|
75
|
+
if (!pageData.value[props.settingsKey]) {
|
|
68
76
|
pageData.value.settings = null;
|
|
69
77
|
}
|
|
70
78
|
});
|
|
@@ -55,25 +55,25 @@ function startResize(event: MouseEvent) {
|
|
|
55
55
|
<style scoped lang="scss">
|
|
56
56
|
/* Resize handle styles */
|
|
57
57
|
.resize-handle {
|
|
58
|
-
position:
|
|
58
|
+
position: sticky;
|
|
59
59
|
top: 0;
|
|
60
60
|
right: 0;
|
|
61
|
-
z-index:
|
|
61
|
+
z-index: 14;
|
|
62
62
|
width: 3px;
|
|
63
|
-
height:
|
|
63
|
+
height: var(--editor-height);
|
|
64
64
|
cursor: col-resize;
|
|
65
|
-
background-color:
|
|
65
|
+
background-color: #dbdee0;
|
|
66
66
|
transition: all 0.2s ease-in-out;
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
.resize-handle:hover {
|
|
70
70
|
width: 3px;
|
|
71
|
-
background-color:
|
|
71
|
+
background-color: #fcd34d;
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
.resize-handle:active {
|
|
75
75
|
width: 3px;
|
|
76
|
-
background-color:
|
|
76
|
+
background-color: #fbbf24;
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
/* Add a subtle indicator when resizing */
|
|
@@ -81,12 +81,12 @@ function startResize(event: MouseEvent) {
|
|
|
81
81
|
position: absolute;
|
|
82
82
|
top: 50%;
|
|
83
83
|
left: -6px;
|
|
84
|
-
z-index:
|
|
84
|
+
z-index: 50;
|
|
85
85
|
width: 14px;
|
|
86
86
|
height: 40px;
|
|
87
87
|
content: "";
|
|
88
|
-
background-color:
|
|
89
|
-
border-radius:
|
|
88
|
+
background-color: #fcd34d;
|
|
89
|
+
border-radius: 8px;
|
|
90
90
|
opacity: 0;
|
|
91
91
|
transform: translateY(-50%);
|
|
92
92
|
transition:
|
|
@@ -99,7 +99,7 @@ function startResize(event: MouseEvent) {
|
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
.resize-handle:active::before {
|
|
102
|
-
background-color:
|
|
102
|
+
background-color: #fbbf24;
|
|
103
103
|
opacity: 1;
|
|
104
104
|
}
|
|
105
105
|
</style>
|