@orion-studios/payload-studio 0.5.0-beta.3 → 0.5.0-beta.31

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