vue-wswg-editor 0.0.13 → 0.0.14

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vue-wswg-editor",
3
- "version": "0.0.13",
3
+ "version": "0.0.14",
4
4
  "type": "module",
5
5
  "main": "./dist/vue-wswg-editor.es.js",
6
6
  "module": "./dist/vue-wswg-editor.es.js",
@@ -106,6 +106,7 @@ export function generateIframeHTML(iframeAppModuleUrl?: string): string {
106
106
  const parentStylesheets = extractParentStylesheets();
107
107
  const parentCSSVariables = extractParentCSSVariables();
108
108
  const vueCdnUrl = "https://unpkg.com/vue@3/dist/vue.esm-browser.js";
109
+ const vueRouterCdnUrl = "https://unpkg.com/vue-router@4/dist/vue-router.esm-browser.js";
109
110
  // Get parent origin to use as base URL for relative asset paths
110
111
  const parentOrigin = typeof window !== "undefined" ? window.location.origin : "";
111
112
 
@@ -125,10 +126,15 @@ export function generateIframeHTML(iframeAppModuleUrl?: string): string {
125
126
  window.__VUE_PROD_HYDRATION_MISMATCH_DETAILS__ = false;
126
127
  </script>`;
127
128
 
128
- // Generate script that loads Vue and initializes the iframe app
129
+ // Generate script that loads Vue, vue-router, and initializes the iframe app
130
+ // The import map above allows vue-router to resolve "vue" as a bare module specifier
129
131
  const appScript = iframeAppModuleUrl
130
132
  ? `<script type="module">
131
- import { createApp } from '${vueCdnUrl}';
133
+ import { createApp } from 'vue';
134
+ import * as VueRouter from 'vue-router';
135
+ // Make vue-router available globally so it can be accessed in the iframe app
136
+ window.VueRouter = VueRouter;
137
+
132
138
  import { createIframeApp } from '${iframeAppModuleUrl}';
133
139
 
134
140
  const appEl = document.getElementById('app');
@@ -140,7 +146,10 @@ export function generateIframeHTML(iframeAppModuleUrl?: string): string {
140
146
  </script>`
141
147
  : `<script type="module">
142
148
  // Fallback: Wait for parent to send module URL via postMessage
143
- import { createApp } from '${vueCdnUrl}';
149
+ import { createApp } from 'vue';
150
+ import * as VueRouter from 'vue-router';
151
+ // Make vue-router available globally so it can be imported in the iframe app
152
+ window.VueRouter = VueRouter;
144
153
 
145
154
  let appInitialized = false;
146
155
 
@@ -179,6 +188,17 @@ export function generateIframeHTML(iframeAppModuleUrl?: string): string {
179
188
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
180
189
  <title>Page Preview</title>
181
190
  ${parentOrigin ? `<base href="${parentOrigin}/">` : ""}
191
+ <!-- Import map to allow vue-router to resolve "vue" -->
192
+ <!-- Stub for @vue/devtools-api to avoid loading devtools in iframe -->
193
+ <script type="importmap">
194
+ {
195
+ "imports": {
196
+ "vue": "${vueCdnUrl}",
197
+ "vue-router": "${vueRouterCdnUrl}",
198
+ "@vue/devtools-api": "data:text/javascript,export const setupDevtoolsPlugin=()=>{};export const on=()=>{};export const off=()=>{};export const once=()=>{};export const emit=()=>{};export const notifyComponentUpdate=()=>{};export const addTimelineLayer=()=>{};export const addCustomCommand=()=>{};export const addCustomTab=()=>{};"
199
+ }
200
+ }
201
+ </script>
182
202
  ${parentStylesheets}
183
203
  ${parentCSSVariables}
184
204
  <style>
@@ -57,8 +57,7 @@ export async function createIframeApp(container: HTMLElement): Promise<App> {
57
57
  function serializeForPostMessage(data: any): any {
58
58
  try {
59
59
  return JSON.parse(JSON.stringify(data));
60
- } catch (error) {
61
- console.warn("[iframe] Failed to serialize data for postMessage:", error);
60
+ } catch {
62
61
  return undefined;
63
62
  }
64
63
  }
@@ -69,7 +68,6 @@ export async function createIframeApp(container: HTMLElement): Promise<App> {
69
68
  // Serialize message to handle Vue reactive proxies
70
69
  const serializedMessage = serializeForPostMessage(message);
71
70
  if (!serializedMessage) {
72
- console.error("[iframe] Failed to serialize message for postMessage");
73
71
  return;
74
72
  }
75
73
  window.parent.postMessage(serializedMessage, "*");
@@ -193,15 +191,18 @@ export async function createIframeApp(container: HTMLElement): Promise<App> {
193
191
  },
194
192
  });
195
193
 
194
+ // Dynamic import helper - using Function constructor to create a truly dynamic import
195
+ // that Vite cannot analyze statically, preventing build-time resolution errors
196
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
197
+ // @ts-ignore - modules may not be available in all consuming apps
198
+ const dynamicImport = new Function("modulePath", "return import(modulePath)");
199
+
196
200
  // Try to install Unhead plugin if available
197
201
  // This is needed for layouts that use useHead from @vueuse/head
198
202
  try {
199
203
  // Dynamic import to check if @vueuse/head is available
200
204
  // This module is externalized in vite.config.ts so it won't be bundled
201
- // Using @vite-ignore to prevent static analysis
202
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
203
- // @ts-ignore - @vueuse/head may not be available in all consuming apps
204
- const headModule = await import(/* @vite-ignore */ "@vueuse/head");
205
+ const headModule = await dynamicImport("@vueuse/head");
205
206
  if (headModule && typeof headModule.createHead === "function") {
206
207
  const head = headModule.createHead();
207
208
  app.use(head);
@@ -211,6 +212,92 @@ export async function createIframeApp(container: HTMLElement): Promise<App> {
211
212
  // This is expected if the consuming app doesn't use @vueuse/head
212
213
  }
213
214
 
215
+ // Try to use vue-router - first try from CDN, but components use consuming app's vue-router
216
+ // The issue is that components from the consuming app use their own vue-router instance
217
+ // with different Symbol keys. We need to use the same instance they're using.
218
+ // Try to import vue-router dynamically - this will use the consuming app's vue-router
219
+ // if it's available in the module resolution context
220
+ let VueRouter: any = null;
221
+ let routerInstalled = false;
222
+
223
+ try {
224
+ // Try dynamic import - this should resolve to the consuming app's vue-router
225
+ // when the library is consumed as source code
226
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
227
+ // @ts-ignore - vue-router may not be available in all consuming apps
228
+ const routerModule = await import("vue-router");
229
+ VueRouter = routerModule;
230
+ } catch {
231
+ // Fallback to CDN version
232
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
233
+ // @ts-ignore
234
+ VueRouter = typeof window !== "undefined" ? (window as any).VueRouter : null;
235
+ }
236
+
237
+ if (VueRouter && typeof VueRouter.createRouter === "function") {
238
+ try {
239
+ // Create a minimal router instance
240
+ const router = VueRouter.createRouter({
241
+ history: VueRouter.createMemoryHistory(),
242
+ routes: [
243
+ {
244
+ path: "/",
245
+ component: { template: "<div></div>" },
246
+ },
247
+ {
248
+ path: "/:pathMatch(.*)*",
249
+ component: { template: "<div></div>" },
250
+ },
251
+ ],
252
+ });
253
+
254
+ // Store original resolve to call it first, then ensure matched array exists
255
+ const originalResolve = router.resolve.bind(router);
256
+ router.resolve = (to: any) => {
257
+ // Call original resolve to get proper route structure
258
+ const resolved = originalResolve(to);
259
+ // Ensure the resolved route has a matched array (useLink requires this)
260
+ if (resolved.route && (!resolved.route.matched || resolved.route.matched.length === 0)) {
261
+ resolved.route.matched = [
262
+ {
263
+ path: resolved.route.path || "/",
264
+ name: resolved.route.name,
265
+ meta: resolved.route.meta || {},
266
+ components: {},
267
+ children: [],
268
+ },
269
+ ];
270
+ }
271
+ return resolved;
272
+ };
273
+
274
+ // Install router BEFORE mounting - this provides the router injection
275
+ // The app.use() call should handle the Symbol keys automatically
276
+ app.use(router);
277
+
278
+ // Wait for router to be ready before mounting
279
+ await router.isReady();
280
+
281
+ // Ensure currentRoute has proper structure with matched array
282
+ // currentRoute.value is readonly, so we navigate to ensure route is properly resolved
283
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
284
+ // @ts-ignore
285
+ const currentRoute = router.currentRoute.value;
286
+ if (currentRoute && (!currentRoute.matched || currentRoute.matched.length === 0)) {
287
+ // Navigate to ensure route has proper matched array
288
+ await router.push("/");
289
+ }
290
+
291
+ routerInstalled = true;
292
+ } catch {
293
+ // Fall through to mount without router
294
+ }
295
+ }
296
+
297
+ if (!routerInstalled) {
298
+ // Router was not installed - RouterLink components will fail
299
+ console.warn("[iframe preview] Router was not installed - RouterLink components will fail");
300
+ }
214
301
  // Mount the app
215
302
  app.mount(container);
216
303
 
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <div id="page-viewport" class="page-renderer-wrapper relative">
3
3
  <template v-if="isReady">
4
- <component :is="layoutComponent" v-if="withLayout && layoutComponent" v-bind="settings">
4
+ <component :is="layoutComponent" v-if="withLayout && layoutComponent" v-bind="settings" :blocks="blocks">
5
5
  <template #default>
6
6
  <div id="page-blocks-wrapper">
7
7
  <div
@@ -54,7 +54,7 @@ const isReady = ref(false);
54
54
 
55
55
  // Get the layout component based on the layout prop
56
56
  const layoutComponent = computed(() => {
57
- return getLayout(props.layout);
57
+ return getLayout(props.layout || props.settings?.layout);
58
58
  });
59
59
 
60
60
  // Get the margin class for the block
@@ -84,10 +84,14 @@ onBeforeMount(async () => {
84
84
  });
85
85
  </script>
86
86
 
87
- <style scoped lang="scss">
87
+ <style lang="scss">
88
88
  @use "../../assets/styles/mixins" as *;
89
89
 
90
- .block-wrapper {
91
- @include block-margin-classes;
90
+ // Styles are scoped to the component using the root class selector instead of Vue's scoped attribute
91
+ // This ensures styles work correctly when the component is consumed from a library
92
+ .page-renderer-wrapper {
93
+ .block-wrapper {
94
+ @include block-margin-classes;
95
+ }
92
96
  }
93
97
  </style>
@@ -178,8 +178,8 @@ export async function initialiseLayoutRegistry(): Promise<void> {
178
178
  // Get the active theme
179
179
  const activeTheme = getActiveTheme();
180
180
 
181
- // Use virtual module to load all layouts (scans all themes at build time)
182
- // Then filter to only process layouts from the active theme
181
+ // Use virtual module to load all layouts (scans all theme directories at build time)
182
+ // Then filter to only process layouts from the active theme directory
183
183
  const { modules: layoutModules } = await import("vue-wswg-editor:layouts");
184
184
 
185
185
  // Filter to only layouts from the active theme