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.
Files changed (56) hide show
  1. package/README.md +493 -20
  2. package/dist/style.css +1 -1
  3. package/dist/vue-wswg-editor.es.js +2431 -1895
  4. package/package.json +15 -8
  5. package/src/assets/styles/_mixins.scss +12 -17
  6. package/src/components/AddBlockItem/AddBlockItem.vue +5 -5
  7. package/src/components/BlockBrowser/BlockBrowser.vue +33 -3
  8. package/src/components/BlockEditorFieldNode/BlockEditorFieldNode.vue +79 -34
  9. package/src/components/BlockEditorFields/BlockEditorFields.vue +15 -6
  10. package/src/components/BlockImageFieldNode/BlockImageNode.vue +373 -0
  11. package/src/components/BlockMarginFieldNode/BlockMarginNode.vue +6 -4
  12. package/src/components/BlockRepeaterFieldNode/BlockRepeaterNode.vue +4 -2
  13. package/src/components/BrowserNavigation/BrowserNavigation.vue +2 -2
  14. package/src/components/EmptyState/EmptyState.vue +1 -1
  15. package/src/components/PageBlockList/PageBlockList.vue +1 -9
  16. package/src/components/PageBuilderSidebar/PageBuilderSidebar.vue +29 -10
  17. package/src/components/PageBuilderToolbar/PageBuilderToolbar.vue +4 -4
  18. package/src/components/PageRenderer/PageRenderer.vue +76 -24
  19. package/src/components/PageRenderer/blockModules.ts +32 -3
  20. package/src/components/PageRenderer/layoutModules.ts +32 -0
  21. package/src/components/PageSettings/PageSettings.vue +19 -11
  22. package/src/components/ResizeHandle/ResizeHandle.vue +10 -10
  23. package/src/components/WswgJsonEditor/WswgJsonEditor.vue +323 -117
  24. package/src/index.ts +2 -2
  25. package/src/style.css +10 -3
  26. package/src/types/Block.d.ts +1 -1
  27. package/src/util/fieldConfig.ts +29 -0
  28. package/src/util/registry.ts +30 -23
  29. package/src/util/validation.ts +178 -23
  30. package/src/vite-plugin.ts +27 -2
  31. package/types/vue-wswg-editor.d.ts +161 -0
  32. package/dist/types/components/AddBlockItem/AddBlockItem.vue.d.ts +0 -6
  33. package/dist/types/components/BlockBrowser/BlockBrowser.vue.d.ts +0 -2
  34. package/dist/types/components/BlockComponent/BlockComponent.vue.d.ts +0 -15
  35. package/dist/types/components/BlockEditorFieldNode/BlockEditorFieldNode.vue.d.ts +0 -15
  36. package/dist/types/components/BlockEditorFields/BlockEditorFields.vue.d.ts +0 -15
  37. package/dist/types/components/BlockMarginFieldNode/BlockMarginNode.vue.d.ts +0 -23
  38. package/dist/types/components/BlockRepeaterFieldNode/BlockRepeaterNode.vue.d.ts +0 -15
  39. package/dist/types/components/BrowserNavigation/BrowserNavigation.vue.d.ts +0 -5
  40. package/dist/types/components/EmptyState/EmptyState.vue.d.ts +0 -15
  41. package/dist/types/components/PageBlockList/PageBlockList.vue.d.ts +0 -19
  42. package/dist/types/components/PageBuilderSidebar/PageBuilderSidebar.vue.d.ts +0 -30
  43. package/dist/types/components/PageBuilderToolbar/PageBuilderToolbar.vue.d.ts +0 -28
  44. package/dist/types/components/PageRenderer/PageRenderer.vue.d.ts +0 -6
  45. package/dist/types/components/PageRenderer/blockModules.d.ts +0 -1
  46. package/dist/types/components/PageSettings/PageSettings.vue.d.ts +0 -15
  47. package/dist/types/components/ResizeHandle/ResizeHandle.vue.d.ts +0 -6
  48. package/dist/types/components/WswgJsonEditor/WswgJsonEditor.test.d.ts +0 -1
  49. package/dist/types/components/WswgJsonEditor/WswgJsonEditor.vue.d.ts +0 -40
  50. package/dist/types/index.d.ts +0 -7
  51. package/dist/types/util/fieldConfig.d.ts +0 -82
  52. package/dist/types/util/helpers.d.ts +0 -28
  53. package/dist/types/util/registry.d.ts +0 -20
  54. package/dist/types/util/validation.d.ts +0 -15
  55. package/dist/types/vite-plugin.d.ts +0 -9
  56. package/dist/vite-plugin.js +0 -53
@@ -1,44 +1,89 @@
1
1
  <template>
2
- <div id="page-blocks-wrapper">
3
- <div v-for="block in blocks" :key="block.id" class="block-wrapper" :class="{ [getMarginClass(block)]: true }">
4
- <component :is="getBlock(block.type)" v-bind="block" :key="`block-${block.id}`" />
5
- </div>
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 { blockModules } from "./blockModules";
36
+ import { getBlockModule, loadBlockModules } from "./blockModules";
37
+ import { loadLayoutModules, getLayoutModule } from "./layoutModules";
13
38
  import type { Block } from "../../types/Block";
14
39
 
15
- defineProps<{
16
- blocks: Block[];
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 in blockModules keys (file paths)
57
+ // Generate name variations and try to find a match
21
58
  const nameVariations = generateNameVariations(blockType);
22
59
 
23
- // Iterate through all blockModules entries
24
- for (const [filePath, module] of Object.entries(blockModules)) {
25
- // Check if any variation matches the file path
26
- for (const variation of nameVariations) {
27
- // Check if the file path contains the variation followed by .vue
28
- // e.g., "hero-section" matches "blocks/hero-section/hero-section.vue"
29
- if (filePath.includes(`${variation}.vue`)) {
30
- // Extract the default export (the Vue component)
31
- const component = (module as any).default;
32
- if (component) {
33
- return component;
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
- // import.meta.glob must be at top level - it will be transformed by Vite plugin
2
- // This file is separate from the Vue component to ensure the transform hook can intercept it
3
- export const blockModules = import.meta.glob("@page-builder/blocks/**/*.vue", { eager: true });
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 v-model="pageData.settings.layout" class="form-control" @change="getLayoutSettings">
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.settings"
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
- defineProps<{
54
- title?: string;
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.settings.layout) return;
63
- pageSettingsFields.value = getLayoutFields(pageData.value.settings.layout) || null;
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.settings) {
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: relative;
58
+ position: sticky;
59
59
  top: 0;
60
60
  right: 0;
61
- z-index: 2;
61
+ z-index: 14;
62
62
  width: 3px;
63
- height: 100%;
63
+ height: var(--editor-height);
64
64
  cursor: col-resize;
65
- background-color: var(--grey-20);
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: var(--yellow-40);
71
+ background-color: #fcd34d;
72
72
  }
73
73
 
74
74
  .resize-handle:active {
75
75
  width: 3px;
76
- background-color: var(--yellow-50);
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: 20;
84
+ z-index: 50;
85
85
  width: 14px;
86
86
  height: 40px;
87
87
  content: "";
88
- background-color: var(--yellow-40);
89
- border-radius: var(--border-radius-16);
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: var(--yellow-50);
102
+ background-color: #fbbf24;
103
103
  opacity: 1;
104
104
  }
105
105
  </style>