@orion-studios/payload-studio 0.5.0-beta.4 → 0.5.0-beta.41

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 (46) hide show
  1. package/dist/admin/client.js +68 -7
  2. package/dist/admin/client.mjs +68 -7
  3. package/dist/admin/index.d.mts +1 -1
  4. package/dist/admin/index.d.ts +1 -1
  5. package/dist/admin/index.js +37 -0
  6. package/dist/admin/index.mjs +3 -1
  7. package/dist/admin-app/client.d.mts +4 -0
  8. package/dist/admin-app/client.d.ts +4 -0
  9. package/dist/admin-app/client.js +705 -2
  10. package/dist/admin-app/client.mjs +701 -1
  11. package/dist/admin-app/index.d.mts +1 -1
  12. package/dist/admin-app/index.d.ts +1 -1
  13. package/dist/admin-app/index.js +167 -0
  14. package/dist/admin-app/index.mjs +13 -1
  15. package/dist/admin-app/styles.css +127 -0
  16. package/dist/blocks/index.js +457 -5
  17. package/dist/blocks/index.mjs +2 -2
  18. package/dist/{chunk-J7W5EE3B.mjs → chunk-7IGLXLUB.mjs} +37 -0
  19. package/dist/{chunk-ZLLNO5FM.mjs → chunk-GNYOAC5N.mjs} +44 -13
  20. package/dist/{chunk-UJFU323N.mjs → chunk-LB72FZZ3.mjs} +33 -9
  21. package/dist/{chunk-ETRRXURT.mjs → chunk-SIL2J5MF.mjs} +14 -0
  22. package/dist/{chunk-PC5622T7.mjs → chunk-XQYJXB46.mjs} +444 -6
  23. package/dist/chunk-XVH5SCBD.mjs +234 -0
  24. package/dist/index-BBvk9b9i.d.mts +97 -0
  25. package/dist/index-BBvk9b9i.d.ts +97 -0
  26. package/dist/{index-DbH0Ljwp.d.mts → index-CpG3UHcS.d.mts} +1 -0
  27. package/dist/{index-DbH0Ljwp.d.ts → index-CpG3UHcS.d.ts} +1 -0
  28. package/dist/{index-DJFhANvJ.d.ts → index-Dj21uD_B.d.mts} +5 -2
  29. package/dist/{index-DJFhANvJ.d.mts → index-Dj21uD_B.d.ts} +5 -2
  30. package/dist/index.d.mts +3 -3
  31. package/dist/index.d.ts +3 -3
  32. package/dist/index.js +904 -204
  33. package/dist/index.mjs +12 -12
  34. package/dist/nextjs/index.d.mts +1 -1
  35. package/dist/nextjs/index.d.ts +1 -1
  36. package/dist/nextjs/index.js +290 -13
  37. package/dist/nextjs/index.mjs +4 -1
  38. package/dist/studio-pages/builder.css +25 -1
  39. package/dist/studio-pages/client.js +3593 -1279
  40. package/dist/studio-pages/client.mjs +3593 -1279
  41. package/dist/studio-pages/index.js +33 -8
  42. package/dist/studio-pages/index.mjs +2 -2
  43. package/package.json +24 -12
  44. package/dist/chunk-AAOHJDNS.mjs +0 -67
  45. package/dist/index-BallJs-K.d.mts +0 -43
  46. package/dist/index-BallJs-K.d.ts +0 -43
@@ -105,6 +105,706 @@ function AdminShellClient({
105
105
  /* @__PURE__ */ jsx("main", { className: "orion-admin-main", children })
106
106
  ] });
107
107
  }
108
+
109
+ // src/admin-app/components/HeaderNavItemsEditor.tsx
110
+ import { useEffect as useEffect2, useMemo, useState as useState2 } from "react";
111
+
112
+ // src/admin-app/navigationLinks.ts
113
+ var normalizeAdminNavInputs = (rows, pageOptions) => {
114
+ const allowedLinks = new Map(pageOptions.map((option) => [option.href, option.title]));
115
+ const deduped = [];
116
+ const seen = /* @__PURE__ */ new Set();
117
+ for (const row of rows) {
118
+ const href = typeof row.href === "string" ? row.href.trim() : "";
119
+ const defaultLabel = allowedLinks.get(href);
120
+ const explicitLabel = typeof row.label === "string" ? row.label.trim() : "";
121
+ const parentHref = typeof row.parentHref === "string" ? row.parentHref.trim() : "";
122
+ if (!href || !defaultLabel || seen.has(href)) {
123
+ continue;
124
+ }
125
+ seen.add(href);
126
+ deduped.push({
127
+ href,
128
+ label: explicitLabel.length > 0 ? explicitLabel : defaultLabel,
129
+ ...parentHref.length > 0 ? { parentHref } : {}
130
+ });
131
+ }
132
+ const hrefs = new Set(deduped.map((item) => item.href));
133
+ const parentByHref = new Map(deduped.map((item) => [item.href, item.parentHref || ""]));
134
+ const createsCycle = (href, parentHref) => {
135
+ const visited = /* @__PURE__ */ new Set([href]);
136
+ let current = parentHref;
137
+ while (current) {
138
+ if (visited.has(current)) {
139
+ return true;
140
+ }
141
+ visited.add(current);
142
+ current = parentByHref.get(current) || "";
143
+ }
144
+ return false;
145
+ };
146
+ return deduped.map((item) => {
147
+ const parentHref = item.parentHref;
148
+ if (!parentHref || parentHref === item.href || !hrefs.has(parentHref) || createsCycle(item.href, parentHref)) {
149
+ return {
150
+ href: item.href,
151
+ label: item.label
152
+ };
153
+ }
154
+ return item;
155
+ });
156
+ };
157
+
158
+ // src/admin-app/components/HeaderNavItemsEditor.tsx
159
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
160
+ var toRow = (item, index) => ({
161
+ id: `row-${index}-${item.href || "empty"}`,
162
+ href: item.href || "",
163
+ label: item.label || "",
164
+ parentHref: item.parentHref || ""
165
+ });
166
+ var moveRow = (rows, fromIndex, toIndex) => {
167
+ if (fromIndex === toIndex || fromIndex < 0 || toIndex < 0 || fromIndex >= rows.length || toIndex >= rows.length) {
168
+ return rows;
169
+ }
170
+ const next = [...rows];
171
+ const [moved] = next.splice(fromIndex, 1);
172
+ if (!moved) {
173
+ return rows;
174
+ }
175
+ next.splice(toIndex, 0, moved);
176
+ return next;
177
+ };
178
+ function HeaderNavItemsEditor({ initialItems, pageOptions, onItemsChange }) {
179
+ const [rows, setRows] = useState2(() => initialItems.map(toRow));
180
+ const [nextRowID, setNextRowID] = useState2(initialItems.length);
181
+ const [draggingRowID, setDraggingRowID] = useState2(null);
182
+ const [dragOverRowID, setDragOverRowID] = useState2(null);
183
+ const pageOptionByHref = useMemo(() => new Map(pageOptions.map((option) => [option.href, option])), [pageOptions]);
184
+ const serializedState = useMemo(
185
+ () => JSON.stringify(
186
+ rows.map((row) => ({
187
+ href: row.href.trim(),
188
+ label: row.label.trim(),
189
+ parentHref: row.parentHref.trim() || void 0
190
+ }))
191
+ ),
192
+ [rows]
193
+ );
194
+ const normalizedItems = useMemo(() => {
195
+ const inputs = rows.map((row) => ({
196
+ href: row.href,
197
+ label: row.label,
198
+ parentHref: row.parentHref
199
+ }));
200
+ return normalizeAdminNavInputs(inputs, pageOptions);
201
+ }, [rows, pageOptions]);
202
+ useEffect2(() => {
203
+ onItemsChange?.(normalizedItems);
204
+ }, [normalizedItems, onItemsChange]);
205
+ const setRowValue = (rowID, changes) => {
206
+ setRows(
207
+ (currentRows) => currentRows.map((row) => {
208
+ if (row.id !== rowID) {
209
+ return row;
210
+ }
211
+ const nextRow = { ...row, ...changes };
212
+ if (nextRow.parentHref === nextRow.href) {
213
+ nextRow.parentHref = "";
214
+ }
215
+ return nextRow;
216
+ })
217
+ );
218
+ };
219
+ const removeRow = (rowID) => {
220
+ setRows((currentRows) => {
221
+ const removedRow = currentRows.find((candidate) => candidate.id === rowID);
222
+ const removedHref = removedRow?.href || "";
223
+ return currentRows.filter((row) => row.id !== rowID).map((row) => removedHref && row.parentHref === removedHref ? { ...row, parentHref: "" } : row);
224
+ });
225
+ };
226
+ const addRow = () => {
227
+ setRows((currentRows) => [
228
+ ...currentRows,
229
+ {
230
+ id: `row-new-${nextRowID}`,
231
+ href: "",
232
+ label: "",
233
+ parentHref: ""
234
+ }
235
+ ]);
236
+ setNextRowID((current) => current + 1);
237
+ };
238
+ return /* @__PURE__ */ jsxs2("div", { className: "orion-admin-nav-editor", children: [
239
+ /* @__PURE__ */ jsx2("input", { name: "navItemsState", readOnly: true, type: "hidden", value: serializedState }),
240
+ /* @__PURE__ */ jsxs2("div", { className: "orion-admin-nav-editor-head", children: [
241
+ /* @__PURE__ */ jsx2("div", { className: "orion-admin-nav-editor-title", children: "Navigation Items" }),
242
+ /* @__PURE__ */ jsx2("button", { className: "orion-admin-nav-editor-add", onClick: addRow, type: "button", children: "Add Item" })
243
+ ] }),
244
+ /* @__PURE__ */ jsx2("div", { className: "orion-admin-nav-editor-help", children: "Add only links you want in the menu. Drag rows to reorder. Set a parent to create dropdown items." }),
245
+ rows.length === 0 ? /* @__PURE__ */ jsx2("div", { className: "orion-admin-nav-editor-empty", children: 'No navigation items yet. Click "Add Item" to start.' }) : null,
246
+ /* @__PURE__ */ jsx2("div", { className: "orion-admin-nav-editor-list", children: rows.map((row, rowIndex) => {
247
+ const parentCandidates = rows.filter((candidate) => candidate.id !== row.id && candidate.href.trim().length > 0).map((candidate) => {
248
+ const resolved = pageOptionByHref.get(candidate.href);
249
+ return {
250
+ href: candidate.href,
251
+ label: candidate.label.trim() || resolved?.title || candidate.href
252
+ };
253
+ });
254
+ const selectedPage = pageOptionByHref.get(row.href);
255
+ const labelPlaceholder = selectedPage?.title || "Navigation Label";
256
+ return /* @__PURE__ */ jsxs2(
257
+ "div",
258
+ {
259
+ className: `orion-admin-nav-editor-row${draggingRowID === row.id ? " is-dragging" : ""}${dragOverRowID === row.id && draggingRowID !== row.id ? " is-drop-target" : ""}`,
260
+ draggable: true,
261
+ onDragEnd: () => {
262
+ setDraggingRowID(null);
263
+ setDragOverRowID(null);
264
+ },
265
+ onDragEnter: () => {
266
+ if (draggingRowID && draggingRowID !== row.id) {
267
+ setDragOverRowID(row.id);
268
+ }
269
+ },
270
+ onDragLeave: () => {
271
+ if (dragOverRowID === row.id) {
272
+ setDragOverRowID(null);
273
+ }
274
+ },
275
+ onDragOver: (event) => {
276
+ event.preventDefault();
277
+ if (draggingRowID && draggingRowID !== row.id && dragOverRowID !== row.id) {
278
+ setDragOverRowID(row.id);
279
+ }
280
+ },
281
+ onDragStart: (event) => {
282
+ setDraggingRowID(row.id);
283
+ event.dataTransfer.effectAllowed = "move";
284
+ },
285
+ onDrop: (event) => {
286
+ event.preventDefault();
287
+ if (!draggingRowID || draggingRowID === row.id) {
288
+ return;
289
+ }
290
+ setRows((currentRows) => {
291
+ const fromIndex = currentRows.findIndex((entry) => entry.id === draggingRowID);
292
+ const toIndex = currentRows.findIndex((entry) => entry.id === row.id);
293
+ return moveRow(currentRows, fromIndex, toIndex);
294
+ });
295
+ setDraggingRowID(null);
296
+ setDragOverRowID(null);
297
+ },
298
+ children: [
299
+ /* @__PURE__ */ jsxs2("div", { className: "orion-admin-nav-editor-row-head", children: [
300
+ /* @__PURE__ */ jsx2("span", { className: "orion-admin-nav-editor-drag", children: "Drag" }),
301
+ /* @__PURE__ */ jsxs2("span", { className: "orion-admin-nav-editor-row-index", children: [
302
+ "#",
303
+ rowIndex + 1
304
+ ] }),
305
+ /* @__PURE__ */ jsx2("button", { className: "orion-admin-nav-editor-remove", onClick: () => removeRow(row.id), type: "button", children: "Remove" })
306
+ ] }),
307
+ /* @__PURE__ */ jsxs2("div", { className: "orion-admin-nav-editor-row-grid", children: [
308
+ /* @__PURE__ */ jsxs2("label", { children: [
309
+ "Label",
310
+ /* @__PURE__ */ jsx2(
311
+ "input",
312
+ {
313
+ name: `navLabel_${rowIndex}`,
314
+ onChange: (event) => setRowValue(row.id, { label: event.target.value }),
315
+ placeholder: labelPlaceholder,
316
+ type: "text",
317
+ value: row.label
318
+ }
319
+ )
320
+ ] }),
321
+ /* @__PURE__ */ jsxs2("label", { children: [
322
+ "Page",
323
+ /* @__PURE__ */ jsxs2(
324
+ "select",
325
+ {
326
+ name: `navPage_${rowIndex}`,
327
+ onChange: (event) => {
328
+ const nextHref = event.target.value;
329
+ const nextParent = row.parentHref && row.parentHref === nextHref ? "" : row.parentHref;
330
+ setRowValue(row.id, { href: nextHref, parentHref: nextParent });
331
+ },
332
+ value: row.href,
333
+ children: [
334
+ /* @__PURE__ */ jsx2("option", { value: "", children: "Select page..." }),
335
+ pageOptions.map((pageOption) => /* @__PURE__ */ jsx2("option", { value: pageOption.href, children: pageOption.label }, `${pageOption.href}-${pageOption.title}`))
336
+ ]
337
+ }
338
+ )
339
+ ] }),
340
+ /* @__PURE__ */ jsxs2("label", { children: [
341
+ "Parent (dropdown under)",
342
+ /* @__PURE__ */ jsxs2(
343
+ "select",
344
+ {
345
+ name: `navParentHref_${rowIndex}`,
346
+ onChange: (event) => setRowValue(row.id, { parentHref: event.target.value }),
347
+ value: row.parentHref,
348
+ children: [
349
+ /* @__PURE__ */ jsx2("option", { value: "", children: "Top-level item" }),
350
+ parentCandidates.map((candidate) => /* @__PURE__ */ jsx2("option", { value: candidate.href, children: candidate.label }, `${row.id}-parent-${candidate.href}`))
351
+ ]
352
+ }
353
+ )
354
+ ] })
355
+ ] })
356
+ ]
357
+ },
358
+ row.id
359
+ );
360
+ }) })
361
+ ] });
362
+ }
363
+
364
+ // src/admin-app/components/HeaderNavEditorWithPreview.tsx
365
+ import { useMemo as useMemo2, useState as useState3 } from "react";
366
+
367
+ // src/admin-app/nestedNavigation.ts
368
+ var normalizeNestedNavItems = (items) => {
369
+ const deduped = [];
370
+ const seen = /* @__PURE__ */ new Set();
371
+ for (const item of items) {
372
+ const href = typeof item.href === "string" ? item.href.trim() : "";
373
+ const label = typeof item.label === "string" ? item.label.trim() : "";
374
+ const parentHref = typeof item.parentHref === "string" ? item.parentHref.trim() : "";
375
+ if (!href || !label || seen.has(href)) {
376
+ continue;
377
+ }
378
+ seen.add(href);
379
+ deduped.push({
380
+ href,
381
+ label,
382
+ ...parentHref ? { parentHref } : {}
383
+ });
384
+ }
385
+ const hrefs = new Set(deduped.map((item) => item.href));
386
+ return deduped.map((item) => ({
387
+ href: item.href,
388
+ label: item.label,
389
+ ...item.parentHref && item.parentHref !== item.href && hrefs.has(item.parentHref) ? { parentHref: item.parentHref } : {}
390
+ }));
391
+ };
392
+ var buildNestedNavTree = (items) => {
393
+ const childrenByParent = /* @__PURE__ */ new Map();
394
+ const topLevel = [];
395
+ for (const item of items) {
396
+ if (!item.parentHref) {
397
+ topLevel.push(item);
398
+ continue;
399
+ }
400
+ const children = childrenByParent.get(item.parentHref) || [];
401
+ children.push(item);
402
+ childrenByParent.set(item.parentHref, children);
403
+ }
404
+ if (topLevel.length === 0 && items.length > 0) {
405
+ return {
406
+ topLevel: items.map((item) => ({ href: item.href, label: item.label })),
407
+ childrenByParent: /* @__PURE__ */ new Map()
408
+ };
409
+ }
410
+ return { childrenByParent, topLevel };
411
+ };
412
+
413
+ // src/admin-app/components/HeaderNavEditorWithPreview.tsx
414
+ import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
415
+ var fallbackNav = [{ href: "/", label: "Home" }];
416
+ function HeaderNavEditorWithPreview({
417
+ brandName,
418
+ initialItems,
419
+ logoUrl,
420
+ pageOptions,
421
+ tagline = ""
422
+ }) {
423
+ const [liveItems, setLiveItems] = useState3(initialItems);
424
+ const previewItems = useMemo2(() => {
425
+ const normalized = normalizeNestedNavItems(liveItems);
426
+ const tree = buildNestedNavTree(normalized);
427
+ return tree.topLevel.length > 0 ? tree : buildNestedNavTree(fallbackNav);
428
+ }, [liveItems]);
429
+ return /* @__PURE__ */ jsxs3(Fragment2, { children: [
430
+ /* @__PURE__ */ jsx3(HeaderNavItemsEditor, { initialItems, onItemsChange: setLiveItems, pageOptions }),
431
+ /* @__PURE__ */ jsx3("div", { style: { color: "#5d6664", fontSize: "0.88rem", fontWeight: 700 }, children: "Header Preview" }),
432
+ /* @__PURE__ */ jsx3(
433
+ "div",
434
+ {
435
+ style: {
436
+ border: "1px solid #dce2e0",
437
+ borderRadius: 14,
438
+ overflow: "hidden"
439
+ },
440
+ children: /* @__PURE__ */ jsx3("header", { className: "site-header", style: { position: "relative", top: "auto" }, children: /* @__PURE__ */ jsxs3("div", { className: "container container-content header-wrap", children: [
441
+ /* @__PURE__ */ jsxs3("a", { className: "brand", href: "#", children: [
442
+ logoUrl ? /* @__PURE__ */ jsx3("img", { alt: `${brandName} logo`, src: logoUrl }) : null,
443
+ /* @__PURE__ */ jsxs3("div", { children: [
444
+ /* @__PURE__ */ jsx3("div", { className: "brand-title", children: brandName }),
445
+ tagline ? /* @__PURE__ */ jsx3("div", { className: "brand-subtitle", children: tagline }) : null
446
+ ] })
447
+ ] }),
448
+ /* @__PURE__ */ jsx3("nav", { className: "site-nav open", children: previewItems.topLevel.map((item, index) => {
449
+ const children = previewItems.childrenByParent.get(item.href) || [];
450
+ return /* @__PURE__ */ jsxs3(
451
+ "div",
452
+ {
453
+ className: children.length > 0 ? "site-nav-item has-children" : "site-nav-item",
454
+ children: [
455
+ /* @__PURE__ */ jsx3("a", { className: index === 0 ? "active" : "", href: item.href, children: item.label }),
456
+ children.length > 0 ? /* @__PURE__ */ jsx3("div", { className: "site-subnav", children: children.map((child) => /* @__PURE__ */ jsx3("a", { href: child.href, children: child.label }, `${item.href}-${child.href}`)) }) : null
457
+ ]
458
+ },
459
+ `${item.href}-${index}`
460
+ );
461
+ }) })
462
+ ] }) })
463
+ }
464
+ )
465
+ ] });
466
+ }
467
+
468
+ // src/admin-app/components/PageEditorFrame.tsx
469
+ import { useEffect as useEffect3, useRef, useState as useState4 } from "react";
470
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
471
+ function PageEditorFrame({ src }) {
472
+ const iframeRef = useRef(null);
473
+ const dirtyCheckTimerRef = useRef(null);
474
+ const [saving, setSaving] = useState4(null);
475
+ const [message, setMessage] = useState4("");
476
+ const [error, setError] = useState4("");
477
+ const [hasUnsavedChanges, setHasUnsavedChanges] = useState4(false);
478
+ const [awaitingDirtyCheck, setAwaitingDirtyCheck] = useState4(false);
479
+ const [pendingNavigationURL, setPendingNavigationURL] = useState4(null);
480
+ const [showUnsavedDialog, setShowUnsavedDialog] = useState4(false);
481
+ const [canUndo, setCanUndo] = useState4(false);
482
+ const [canRedo, setCanRedo] = useState4(false);
483
+ const clearDirtyCheckTimer = () => {
484
+ if (dirtyCheckTimerRef.current) {
485
+ window.clearTimeout(dirtyCheckTimerRef.current);
486
+ dirtyCheckTimerRef.current = null;
487
+ }
488
+ };
489
+ const continueNavigation = (nextURL) => {
490
+ setPendingNavigationURL(null);
491
+ setShowUnsavedDialog(false);
492
+ setAwaitingDirtyCheck(false);
493
+ clearDirtyCheckTimer();
494
+ window.location.assign(nextURL);
495
+ };
496
+ const sendSave = (status) => {
497
+ const iframe = iframeRef.current;
498
+ if (!iframe?.contentWindow) {
499
+ setError("Editor is still loading. Try again in a moment.");
500
+ return;
501
+ }
502
+ setSaving(status);
503
+ setError("");
504
+ setMessage("");
505
+ iframe.contentWindow.postMessage(
506
+ {
507
+ source: "payload-visual-builder-parent",
508
+ type: "save",
509
+ status
510
+ },
511
+ "*"
512
+ );
513
+ };
514
+ const sendHistory = (type) => {
515
+ const iframe = iframeRef.current;
516
+ if (!iframe?.contentWindow) {
517
+ setError("Editor is still loading. Try again in a moment.");
518
+ return;
519
+ }
520
+ iframe.contentWindow.postMessage(
521
+ {
522
+ source: "payload-visual-builder-parent",
523
+ type
524
+ },
525
+ "*"
526
+ );
527
+ };
528
+ useEffect3(() => {
529
+ const onMessage = (event) => {
530
+ const data = event.data;
531
+ if (!data || data.source !== "payload-visual-builder-child") {
532
+ return;
533
+ }
534
+ if (data.type === "dirty-state") {
535
+ const dirty = Boolean(data.dirty);
536
+ setHasUnsavedChanges(dirty);
537
+ if (awaitingDirtyCheck && pendingNavigationURL) {
538
+ clearDirtyCheckTimer();
539
+ setAwaitingDirtyCheck(false);
540
+ if (dirty) {
541
+ setShowUnsavedDialog(true);
542
+ } else {
543
+ continueNavigation(pendingNavigationURL);
544
+ }
545
+ }
546
+ return;
547
+ }
548
+ if (data.type === "history-state") {
549
+ setCanUndo(Boolean(data.canUndo));
550
+ setCanRedo(Boolean(data.canRedo));
551
+ return;
552
+ }
553
+ if (data.type !== "save-result") {
554
+ return;
555
+ }
556
+ setSaving(null);
557
+ if (data.ok) {
558
+ setMessage(typeof data.message === "string" ? data.message : "Saved.");
559
+ setError("");
560
+ setHasUnsavedChanges(false);
561
+ } else {
562
+ setError(typeof data.message === "string" ? data.message : "Could not save changes.");
563
+ setMessage("");
564
+ }
565
+ };
566
+ window.addEventListener("message", onMessage);
567
+ return () => window.removeEventListener("message", onMessage);
568
+ }, [awaitingDirtyCheck, pendingNavigationURL]);
569
+ useEffect3(() => {
570
+ const onDocumentClick = (event) => {
571
+ if (!hasUnsavedChanges) {
572
+ return;
573
+ }
574
+ const target = event.target;
575
+ if (target?.closest('[data-orion-unsaved-dialog="true"]')) {
576
+ return;
577
+ }
578
+ const link = target?.closest("a[href]");
579
+ if (!link) {
580
+ return;
581
+ }
582
+ if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) {
583
+ return;
584
+ }
585
+ if (link.target && link.target !== "_self") {
586
+ return;
587
+ }
588
+ const href = link.getAttribute("href");
589
+ if (!href || href.startsWith("#") || href.startsWith("mailto:") || href.startsWith("tel:")) {
590
+ return;
591
+ }
592
+ const destination = new URL(href, window.location.href);
593
+ const current = new URL(window.location.href);
594
+ const isSameDestination = destination.pathname === current.pathname && destination.search === current.search && destination.hash === current.hash;
595
+ if (destination.origin !== current.origin || isSameDestination) {
596
+ return;
597
+ }
598
+ event.preventDefault();
599
+ const nextURL = `${destination.pathname}${destination.search}${destination.hash}`;
600
+ setPendingNavigationURL(nextURL);
601
+ const iframe = iframeRef.current;
602
+ if (!iframe?.contentWindow) {
603
+ if (hasUnsavedChanges) {
604
+ setShowUnsavedDialog(true);
605
+ } else {
606
+ continueNavigation(nextURL);
607
+ }
608
+ return;
609
+ }
610
+ setAwaitingDirtyCheck(true);
611
+ setShowUnsavedDialog(false);
612
+ clearDirtyCheckTimer();
613
+ iframe.contentWindow.postMessage(
614
+ {
615
+ source: "payload-visual-builder-parent",
616
+ type: "dirty-check-request"
617
+ },
618
+ "*"
619
+ );
620
+ dirtyCheckTimerRef.current = window.setTimeout(() => {
621
+ setAwaitingDirtyCheck(false);
622
+ if (hasUnsavedChanges) {
623
+ setShowUnsavedDialog(true);
624
+ } else {
625
+ continueNavigation(nextURL);
626
+ }
627
+ }, 250);
628
+ };
629
+ document.addEventListener("click", onDocumentClick, true);
630
+ return () => document.removeEventListener("click", onDocumentClick, true);
631
+ }, [hasUnsavedChanges]);
632
+ useEffect3(() => {
633
+ if (!hasUnsavedChanges) {
634
+ return;
635
+ }
636
+ const onBeforeUnload = (event) => {
637
+ event.preventDefault();
638
+ event.returnValue = "";
639
+ };
640
+ window.addEventListener("beforeunload", onBeforeUnload);
641
+ return () => window.removeEventListener("beforeunload", onBeforeUnload);
642
+ }, [hasUnsavedChanges]);
643
+ useEffect3(
644
+ () => () => {
645
+ clearDirtyCheckTimer();
646
+ },
647
+ []
648
+ );
649
+ return /* @__PURE__ */ jsxs4("div", { style: { display: "grid", gap: "0.8rem" }, children: [
650
+ /* @__PURE__ */ jsxs4(
651
+ "div",
652
+ {
653
+ style: {
654
+ alignItems: "center",
655
+ background: "var(--orion-admin-card-bg)",
656
+ border: "1px solid var(--orion-admin-card-border)",
657
+ borderRadius: 12,
658
+ display: "flex",
659
+ flexWrap: "wrap",
660
+ gap: "0.6rem",
661
+ justifyContent: "space-between",
662
+ padding: "0.7rem 0.8rem"
663
+ },
664
+ children: [
665
+ /* @__PURE__ */ jsx4("div", { style: { color: "var(--orion-admin-muted)", fontSize: "0.9rem" }, children: "Save changes to update the page layout and content." }),
666
+ /* @__PURE__ */ jsxs4("div", { style: { alignItems: "center", display: "flex", flexWrap: "wrap", gap: "0.5rem" }, children: [
667
+ /* @__PURE__ */ jsx4(
668
+ "button",
669
+ {
670
+ className: "orion-admin-action-button",
671
+ disabled: !canUndo,
672
+ onClick: () => sendHistory("undo"),
673
+ style: { background: "transparent", border: "1px solid var(--orion-admin-card-border)", color: "var(--orion-admin-text)" },
674
+ type: "button",
675
+ children: "Undo"
676
+ }
677
+ ),
678
+ /* @__PURE__ */ jsx4(
679
+ "button",
680
+ {
681
+ className: "orion-admin-action-button",
682
+ disabled: !canRedo,
683
+ onClick: () => sendHistory("redo"),
684
+ style: { background: "transparent", border: "1px solid var(--orion-admin-card-border)", color: "var(--orion-admin-text)" },
685
+ type: "button",
686
+ children: "Redo"
687
+ }
688
+ ),
689
+ /* @__PURE__ */ jsx4(
690
+ "button",
691
+ {
692
+ className: "orion-admin-action-button",
693
+ disabled: saving !== null,
694
+ onClick: () => sendSave("draft"),
695
+ type: "button",
696
+ children: saving === "draft" ? "Saving..." : "Save Draft"
697
+ }
698
+ ),
699
+ /* @__PURE__ */ jsx4(
700
+ "button",
701
+ {
702
+ className: "orion-admin-action-button",
703
+ disabled: saving !== null,
704
+ onClick: () => sendSave("published"),
705
+ style: { background: "#0f7d52" },
706
+ type: "button",
707
+ children: saving === "published" ? "Publishing..." : "Publish"
708
+ }
709
+ )
710
+ ] })
711
+ ]
712
+ }
713
+ ),
714
+ message ? /* @__PURE__ */ jsx4("div", { style: { color: "#0f7d52", fontSize: "0.9rem", fontWeight: 700 }, children: message }) : null,
715
+ error ? /* @__PURE__ */ jsx4("div", { className: "orion-admin-error", children: error }) : null,
716
+ /* @__PURE__ */ jsx4(
717
+ "iframe",
718
+ {
719
+ ref: iframeRef,
720
+ src,
721
+ style: {
722
+ border: "1px solid var(--orion-admin-card-border)",
723
+ borderRadius: 12,
724
+ height: "calc(100vh - 280px)",
725
+ width: "100%"
726
+ },
727
+ title: "Page Builder",
728
+ onLoad: () => {
729
+ const iframe = iframeRef.current;
730
+ if (!iframe?.contentWindow) return;
731
+ iframe.contentWindow.postMessage({ source: "payload-visual-builder-parent", type: "dirty-check-request" }, "*");
732
+ iframe.contentWindow.postMessage({ source: "payload-visual-builder-parent", type: "history-check-request" }, "*");
733
+ }
734
+ }
735
+ ),
736
+ pendingNavigationURL && showUnsavedDialog ? /* @__PURE__ */ jsx4(
737
+ "div",
738
+ {
739
+ "data-orion-unsaved-dialog": "true",
740
+ style: {
741
+ alignItems: "center",
742
+ background: "rgba(0, 0, 0, 0.35)",
743
+ display: "grid",
744
+ inset: 0,
745
+ justifyItems: "center",
746
+ padding: "1rem",
747
+ position: "fixed",
748
+ zIndex: 2e3
749
+ },
750
+ children: /* @__PURE__ */ jsxs4(
751
+ "div",
752
+ {
753
+ style: {
754
+ background: "#ffffff",
755
+ border: "1px solid var(--orion-admin-card-border)",
756
+ borderRadius: 14,
757
+ boxShadow: "0 20px 42px rgba(13, 74, 55, 0.28)",
758
+ display: "grid",
759
+ gap: "0.9rem",
760
+ maxWidth: 420,
761
+ padding: "1rem",
762
+ width: "100%"
763
+ },
764
+ children: [
765
+ /* @__PURE__ */ jsxs4("div", { children: [
766
+ /* @__PURE__ */ jsx4("div", { style: { color: "var(--orion-admin-text)", fontSize: "1rem", fontWeight: 700 }, children: "Unsaved changes" }),
767
+ /* @__PURE__ */ jsx4("div", { style: { color: "var(--orion-admin-muted)", fontSize: "0.9rem", marginTop: "0.4rem" }, children: "You have unsaved edits in the page builder. Save before leaving this page?" })
768
+ ] }),
769
+ /* @__PURE__ */ jsxs4("div", { style: { display: "flex", gap: "0.6rem", justifyContent: "flex-end" }, children: [
770
+ /* @__PURE__ */ jsx4(
771
+ "button",
772
+ {
773
+ className: "orion-admin-action-button",
774
+ onClick: () => {
775
+ setPendingNavigationURL(null);
776
+ setShowUnsavedDialog(false);
777
+ },
778
+ type: "button",
779
+ children: "Stay"
780
+ }
781
+ ),
782
+ /* @__PURE__ */ jsx4(
783
+ "button",
784
+ {
785
+ className: "orion-admin-action-button",
786
+ onClick: () => {
787
+ const next = pendingNavigationURL;
788
+ if (next) {
789
+ continueNavigation(next);
790
+ }
791
+ },
792
+ style: { background: "#8d1d1d" },
793
+ type: "button",
794
+ children: "Leave Without Saving"
795
+ }
796
+ )
797
+ ] })
798
+ ]
799
+ }
800
+ )
801
+ }
802
+ ) : null
803
+ ] });
804
+ }
108
805
  export {
109
- AdminShellClient
806
+ AdminShellClient,
807
+ HeaderNavEditorWithPreview,
808
+ HeaderNavItemsEditor,
809
+ PageEditorFrame
110
810
  };