vue-wswg-editor 0.0.9 → 0.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/style.css +1 -1
- package/dist/types/components/PageBuilderSidebar/PageBuilderSidebar.vue.d.ts +9 -4
- package/dist/types/components/PageRenderer/PageRenderer.vue.d.ts +8 -1
- package/dist/types/components/PageRenderer/layoutModules.d.ts +1 -0
- package/dist/types/components/PageSettings/PageSettings.vue.d.ts +4 -2
- package/dist/types/components/WswgJsonEditor/WswgJsonEditor.vue.d.ts +5 -9
- package/dist/types/index.d.ts +1 -0
- package/dist/types/util/fieldConfig.d.ts +2 -1
- package/dist/vite-plugin.js +32 -9
- package/dist/vue-wswg-editor.es.js +1486 -1417
- package/package.json +1 -1
- package/src/components/AddBlockItem/AddBlockItem.vue +5 -5
- package/src/components/BlockBrowser/BlockBrowser.vue +33 -3
- package/src/components/BlockEditorFieldNode/BlockEditorFieldNode.vue +40 -30
- package/src/components/BlockEditorFields/BlockEditorFields.vue +4 -4
- 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 +32 -9
- package/src/components/PageBuilderToolbar/PageBuilderToolbar.vue +4 -4
- package/src/components/PageRenderer/PageRenderer.vue +58 -5
- package/src/components/PageRenderer/layoutModules.ts +3 -0
- package/src/components/PageSettings/PageSettings.vue +19 -11
- package/src/components/ResizeHandle/ResizeHandle.vue +10 -10
- package/src/components/WswgJsonEditor/WswgJsonEditor.vue +103 -65
- package/src/index.ts +1 -0
- package/src/types/Block.d.ts +1 -1
- package/src/util/fieldConfig.ts +7 -0
- package/src/util/helpers.ts +1 -1
- package/src/vite-plugin.ts +27 -2
|
@@ -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>
|
|
@@ -1,42 +1,41 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="wswg-json-editor">
|
|
3
|
-
<div class="wswg-json-editor-header">
|
|
4
|
-
<!-- header slot for custom control elements -->
|
|
5
|
-
<slot name="header">
|
|
6
|
-
<!-- no default header content -->
|
|
7
|
-
</slot>
|
|
8
|
-
<!-- wswg toolbar-->
|
|
9
|
-
<PageBuilderToolbar
|
|
10
|
-
v-model:editorViewport="editorViewport"
|
|
11
|
-
v-model:showPageSettings="showPageSettings"
|
|
12
|
-
v-model:activeBlock="activeBlock"
|
|
13
|
-
:hasPageSettings="hasPageSettings"
|
|
14
|
-
>
|
|
15
|
-
<slot name="toolbar">
|
|
16
|
-
<!-- no default toolbar content -->
|
|
17
|
-
</slot>
|
|
18
|
-
</PageBuilderToolbar>
|
|
19
|
-
</div>
|
|
20
3
|
<slot v-if="loading" name="loading">
|
|
21
|
-
<div class="wswg-json-editor-loading">
|
|
22
|
-
<
|
|
4
|
+
<div class="wswg-json-editor-loading flex h-full flex-col items-center justify-center gap-4">
|
|
5
|
+
<svg
|
|
6
|
+
class="size-9 animate-[spin_2000ms_linear_infinite]"
|
|
7
|
+
viewBox="0 0 24 24"
|
|
8
|
+
fill="none"
|
|
9
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
10
|
+
>
|
|
11
|
+
<path
|
|
12
|
+
d="M13 2a1 1 0 0 0-2 0v4.167a1 1 0 1 0 2 0V2ZM13 17.833a1 1 0 0 0-2 0V22a1 1 0 1 0 2 0v-4.167ZM16.834 12a1 1 0 0 1 1-1H22a1 1 0 0 1 0 2h-4.166a1 1 0 0 1-1-1ZM2 11a1 1 0 0 0 0 2h4.167a1 1 0 1 0 0-2H2ZM19.916 4.085a1 1 0 0 1 0 1.414l-2.917 2.917A1 1 0 1 1 15.585 7l2.917-2.916a1 1 0 0 1 1.414 0ZM8.415 16.999a1 1 0 0 0-1.414-1.414L4.084 18.5A1 1 0 1 0 5.5 19.916l2.916-2.917ZM15.585 15.585a1 1 0 0 1 1.414 0l2.917 2.916a1 1 0 1 1-1.414 1.415l-2.917-2.917a1 1 0 0 1 0-1.414ZM5.499 4.085a1 1 0 0 0-1.415 1.414l2.917 2.917A1 1 0 0 0 8.415 7L5.5 4.085Z"
|
|
13
|
+
fill="#000000"
|
|
14
|
+
/>
|
|
15
|
+
</svg>
|
|
16
|
+
<span>Loading</span>
|
|
23
17
|
</div>
|
|
24
18
|
</slot>
|
|
25
19
|
<!-- WYSIWYG editor -->
|
|
26
|
-
<div v-else class="wswg-json-editor-
|
|
20
|
+
<div v-else class="wswg-json-editor-body">
|
|
27
21
|
<!-- Page preview -->
|
|
28
|
-
<div class="wswg-json-editor-
|
|
22
|
+
<div class="wswg-json-editor-preview">
|
|
29
23
|
<div
|
|
30
|
-
class="mx-auto h-full
|
|
24
|
+
class="mx-auto flex h-full flex-col transition-all duration-300"
|
|
31
25
|
:class="{ 'w-full': editorViewport === 'desktop', 'w-96': editorViewport === 'mobile' }"
|
|
32
26
|
>
|
|
33
|
-
<BrowserNavigation v-if="showBrowserBar" :url="url" />
|
|
34
|
-
<div
|
|
27
|
+
<BrowserNavigation v-if="showBrowserBar" class="browser-navigation-bar" :url="url" />
|
|
28
|
+
<div
|
|
29
|
+
v-if="pageLayout"
|
|
30
|
+
id="page-preview-viewport"
|
|
31
|
+
class="overflow-hidden rounded-b-lg bg-white"
|
|
32
|
+
:class="{ 'rounded-t-lg': !showBrowserBar }"
|
|
33
|
+
>
|
|
35
34
|
<component :is="pageLayout">
|
|
36
35
|
<template #default>
|
|
37
36
|
<!-- No blocks found -->
|
|
38
37
|
<EmptyState
|
|
39
|
-
v-if="!pageData[blocksKey]?.length"
|
|
38
|
+
v-if="!pageData?.[blocksKey]?.length"
|
|
40
39
|
v-model:showAddBlockMenu="showAddBlockMenu"
|
|
41
40
|
:editable="editable"
|
|
42
41
|
@block-added="handleAddBlock"
|
|
@@ -66,24 +65,36 @@
|
|
|
66
65
|
<ResizeHandle @sidebar-width="handleSidebarWidth" />
|
|
67
66
|
|
|
68
67
|
<!-- Sidebar -->
|
|
69
|
-
<
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
68
|
+
<div class="page-builder-sidebar-wrapper bg-white" :style="{ width: sidebarWidth + 'px' }">
|
|
69
|
+
<PageBuilderSidebar
|
|
70
|
+
v-model="pageData"
|
|
71
|
+
v-model:activeBlock="activeBlock"
|
|
72
|
+
v-model:hoveredBlockId="hoveredBlockId"
|
|
73
|
+
v-model:showPageSettings="showPageSettings"
|
|
74
|
+
v-model:showAddBlockMenu="showAddBlockMenu"
|
|
75
|
+
v-model:editorViewport="editorViewport"
|
|
76
|
+
:hasPageSettings="hasPageSettings"
|
|
77
|
+
:editable="editable"
|
|
78
|
+
:blocksKey="blocksKey"
|
|
79
|
+
:settingsKey="settingsKey"
|
|
80
|
+
/>
|
|
81
|
+
</div>
|
|
80
82
|
</div>
|
|
81
83
|
</div>
|
|
82
84
|
</template>
|
|
83
85
|
|
|
84
86
|
<script setup lang="ts">
|
|
85
|
-
import {
|
|
86
|
-
|
|
87
|
+
import {
|
|
88
|
+
ref,
|
|
89
|
+
shallowRef,
|
|
90
|
+
withDefaults,
|
|
91
|
+
watch,
|
|
92
|
+
type Component,
|
|
93
|
+
onBeforeMount,
|
|
94
|
+
nextTick,
|
|
95
|
+
computed,
|
|
96
|
+
onMounted,
|
|
97
|
+
} from "vue";
|
|
87
98
|
import ResizeHandle from "../ResizeHandle/ResizeHandle.vue";
|
|
88
99
|
import PageBuilderSidebar from "../PageBuilderSidebar/PageBuilderSidebar.vue";
|
|
89
100
|
import BrowserNavigation from "../BrowserNavigation/BrowserNavigation.vue";
|
|
@@ -123,7 +134,7 @@ const hoveredBlockId = ref<string | null>(null);
|
|
|
123
134
|
const sidebarWidth = ref(380); // Default sidebar width (380px)
|
|
124
135
|
|
|
125
136
|
// Model value for the JSON page data
|
|
126
|
-
const pageData = defineModel<any
|
|
137
|
+
const pageData = defineModel<Record<string, any>>();
|
|
127
138
|
|
|
128
139
|
// Layout component - dynamically imported from page-builder directory
|
|
129
140
|
// Using shallowRef to avoid making the component reactive (performance optimization)
|
|
@@ -177,6 +188,8 @@ function handleBlockClick(block: Block | null) {
|
|
|
177
188
|
}
|
|
178
189
|
|
|
179
190
|
function handleAddBlock(blockType: string, insertIndex?: number) {
|
|
191
|
+
if (!pageData.value) return;
|
|
192
|
+
|
|
180
193
|
// Ensure blocks array exists
|
|
181
194
|
if (!pageData.value[props.blocksKey]) {
|
|
182
195
|
pageData.value[props.blocksKey] = [];
|
|
@@ -240,7 +253,7 @@ watch(
|
|
|
240
253
|
(layoutName) => {
|
|
241
254
|
loadLayout(layoutName);
|
|
242
255
|
},
|
|
243
|
-
{ immediate:
|
|
256
|
+
{ immediate: false }
|
|
244
257
|
);
|
|
245
258
|
|
|
246
259
|
// Watch for activeBlock changes and scroll to the corresponding block in the preview
|
|
@@ -309,6 +322,10 @@ function initSortable() {
|
|
|
309
322
|
}
|
|
310
323
|
|
|
311
324
|
onBeforeMount(() => {
|
|
325
|
+
if (!pageData.value) {
|
|
326
|
+
pageData.value = {};
|
|
327
|
+
}
|
|
328
|
+
|
|
312
329
|
if (!pageData.value?.[props.settingsKey]) {
|
|
313
330
|
if (pageData.value) {
|
|
314
331
|
pageData.value[props.settingsKey] = {};
|
|
@@ -340,22 +357,23 @@ onBeforeMount(() => {
|
|
|
340
357
|
}
|
|
341
358
|
}
|
|
342
359
|
});
|
|
360
|
+
|
|
361
|
+
onMounted(() => {
|
|
362
|
+
loadLayout(pageData.value?.[props.settingsKey]?.layout);
|
|
363
|
+
});
|
|
343
364
|
</script>
|
|
344
365
|
|
|
345
366
|
<style lang="scss">
|
|
367
|
+
$editor-background-color: #6a6a6a;
|
|
368
|
+
|
|
346
369
|
.wswg-json-editor {
|
|
347
|
-
|
|
348
|
-
flex-direction: column;
|
|
349
|
-
width: 100%;
|
|
350
|
-
max-width: 100%;
|
|
351
|
-
height: 100vh;
|
|
370
|
+
--editor-height: calc(100vh);
|
|
352
371
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
}
|
|
372
|
+
position: relative;
|
|
373
|
+
width: 100%;
|
|
374
|
+
max-width: 100vw;
|
|
375
|
+
height: var(--editor-height);
|
|
376
|
+
overflow-y: auto;
|
|
359
377
|
|
|
360
378
|
&-loading {
|
|
361
379
|
display: flex;
|
|
@@ -363,27 +381,47 @@ onBeforeMount(() => {
|
|
|
363
381
|
justify-content: center;
|
|
364
382
|
}
|
|
365
383
|
|
|
366
|
-
&-
|
|
384
|
+
&-body {
|
|
385
|
+
display: flex;
|
|
386
|
+
width: 100%;
|
|
387
|
+
background-color: $editor-background-color;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
&-preview {
|
|
391
|
+
position: relative;
|
|
367
392
|
display: flex;
|
|
368
393
|
flex: 1;
|
|
369
394
|
flex-grow: 1;
|
|
370
395
|
flex-shrink: 0;
|
|
396
|
+
flex-direction: column;
|
|
371
397
|
height: 100%;
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
398
|
+
min-height: 0;
|
|
399
|
+
padding: 1.5rem;
|
|
400
|
+
|
|
401
|
+
.browser-navigation-bar {
|
|
402
|
+
position: sticky;
|
|
403
|
+
top: 1.5rem;
|
|
404
|
+
z-index: 12;
|
|
405
|
+
|
|
406
|
+
&::before {
|
|
407
|
+
position: absolute;
|
|
408
|
+
top: -1.5rem;
|
|
409
|
+
left: 0;
|
|
410
|
+
z-index: -1;
|
|
411
|
+
width: 100%;
|
|
412
|
+
height: 100%;
|
|
413
|
+
content: "";
|
|
414
|
+
background-color: $editor-background-color;
|
|
415
|
+
}
|
|
380
416
|
}
|
|
417
|
+
}
|
|
381
418
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
419
|
+
.page-builder-sidebar-wrapper {
|
|
420
|
+
position: sticky;
|
|
421
|
+
top: 0;
|
|
422
|
+
z-index: 12;
|
|
423
|
+
height: var(--editor-height);
|
|
424
|
+
overflow-y: auto;
|
|
387
425
|
}
|
|
388
426
|
}
|
|
389
427
|
</style>
|
package/src/index.ts
CHANGED
|
@@ -4,6 +4,7 @@ export { createField } from "./util/fieldConfig";
|
|
|
4
4
|
export type { EditorFieldConfig, ValidatorFunction } from "./util/fieldConfig";
|
|
5
5
|
export { getLayouts } from "./util/registry";
|
|
6
6
|
export { validateField, validateAllFields } from "./util/validation";
|
|
7
|
+
export type { ValidationResult } from "./util/validation";
|
|
7
8
|
|
|
8
9
|
// Export components (component exports don't cause side effects until used)
|
|
9
10
|
export { default as WswgJsonEditor } from "./components/WswgJsonEditor/WswgJsonEditor.vue";
|
package/src/types/Block.d.ts
CHANGED
package/src/util/fieldConfig.ts
CHANGED
|
@@ -25,6 +25,7 @@ export type EditorFieldType =
|
|
|
25
25
|
| "range" // ✅
|
|
26
26
|
| "repeater" // ✅
|
|
27
27
|
| "margin" // ✅
|
|
28
|
+
| "info" // ✅
|
|
28
29
|
| "custom"; // 🔌 (image, json, video, richtext, etc)
|
|
29
30
|
|
|
30
31
|
/**
|
|
@@ -170,4 +171,10 @@ export const createField = {
|
|
|
170
171
|
label: "Margin",
|
|
171
172
|
...config,
|
|
172
173
|
}),
|
|
174
|
+
|
|
175
|
+
// Display an info message
|
|
176
|
+
info: (config: Partial<EditorFieldConfig> = {}): EditorFieldConfig => ({
|
|
177
|
+
type: "info",
|
|
178
|
+
...config,
|
|
179
|
+
}),
|
|
173
180
|
};
|
package/src/util/helpers.ts
CHANGED
|
@@ -148,7 +148,7 @@ export function toCamelCase(input: string): string {
|
|
|
148
148
|
if (parts.length === 0) return "";
|
|
149
149
|
|
|
150
150
|
return (
|
|
151
|
-
parts[0]
|
|
151
|
+
parts?.[0]?.toLowerCase() +
|
|
152
152
|
parts
|
|
153
153
|
.slice(1)
|
|
154
154
|
.map((p) => p.charAt(0).toUpperCase() + p.slice(1).toLowerCase())
|
package/src/vite-plugin.ts
CHANGED
|
@@ -34,13 +34,38 @@ export function vueWswgEditorPlugin(options: VueWswgEditorPluginOptions): Plugin
|
|
|
34
34
|
} else {
|
|
35
35
|
exclude = [];
|
|
36
36
|
}
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
// Exclude the package and virtual modules
|
|
38
|
+
const itemsToExclude = [
|
|
39
|
+
"vue-wswg-editor",
|
|
40
|
+
"vue-wswg-editor:layouts",
|
|
41
|
+
"vue-wswg-editor:blocks",
|
|
42
|
+
"vue-wswg-editor:fields",
|
|
43
|
+
"vue-wswg-editor:thumbnails",
|
|
44
|
+
];
|
|
45
|
+
for (const item of itemsToExclude) {
|
|
46
|
+
if (!exclude.includes(item)) {
|
|
47
|
+
exclude.push(item);
|
|
48
|
+
}
|
|
39
49
|
}
|
|
40
50
|
return {
|
|
41
51
|
optimizeDeps: {
|
|
42
52
|
...optimizeDeps,
|
|
43
53
|
exclude,
|
|
54
|
+
esbuildOptions: {
|
|
55
|
+
...optimizeDeps.esbuildOptions,
|
|
56
|
+
plugins: [
|
|
57
|
+
...(optimizeDeps.esbuildOptions?.plugins || []),
|
|
58
|
+
{
|
|
59
|
+
name: "vue-wswg-editor-virtual-modules",
|
|
60
|
+
setup(build) {
|
|
61
|
+
// Mark virtual modules as external during esbuild optimization
|
|
62
|
+
build.onResolve({ filter: /^vue-wswg-editor:/ }, () => {
|
|
63
|
+
return { external: true };
|
|
64
|
+
});
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
},
|
|
44
69
|
},
|
|
45
70
|
};
|
|
46
71
|
},
|