@payload-onpage/react 0.0.0-stage → 0.2.0

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 (198) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +13 -2
  3. package/dist/OnPageClient.d.ts +18 -0
  4. package/dist/OnPageClient.d.ts.map +1 -0
  5. package/dist/OnPageClient.js +51 -0
  6. package/dist/OnPageClient.js.map +1 -0
  7. package/dist/annotations.d.ts +55 -0
  8. package/dist/annotations.d.ts.map +1 -0
  9. package/dist/annotations.js +60 -0
  10. package/dist/annotations.js.map +1 -0
  11. package/dist/context.d.ts +7 -0
  12. package/dist/context.d.ts.map +1 -0
  13. package/dist/context.js +19 -0
  14. package/dist/context.js.map +1 -0
  15. package/dist/dom.d.ts +42 -0
  16. package/dist/dom.d.ts.map +1 -0
  17. package/dist/dom.js +95 -0
  18. package/dist/dom.js.map +1 -0
  19. package/dist/editor/FieldHost.d.ts +5 -0
  20. package/dist/editor/FieldHost.d.ts.map +1 -0
  21. package/dist/editor/FieldHost.js +36 -0
  22. package/dist/editor/FieldHost.js.map +1 -0
  23. package/dist/editor/Ghosts.d.ts +6 -0
  24. package/dist/editor/Ghosts.d.ts.map +1 -0
  25. package/dist/editor/Ghosts.js +37 -0
  26. package/dist/editor/Ghosts.js.map +1 -0
  27. package/dist/editor/OnPageEditor.d.ts +25 -0
  28. package/dist/editor/OnPageEditor.d.ts.map +1 -0
  29. package/dist/editor/OnPageEditor.js +211 -0
  30. package/dist/editor/OnPageEditor.js.map +1 -0
  31. package/dist/editor/useGhosts.d.ts +24 -0
  32. package/dist/editor/useGhosts.d.ts.map +1 -0
  33. package/dist/editor/useGhosts.js +34 -0
  34. package/dist/editor/useGhosts.js.map +1 -0
  35. package/dist/editor/usePageEvents.d.ts +3 -0
  36. package/dist/editor/usePageEvents.d.ts.map +1 -0
  37. package/dist/editor/usePageEvents.js +164 -0
  38. package/dist/editor/usePageEvents.js.map +1 -0
  39. package/dist/editor-kit.d.ts +19 -0
  40. package/dist/editor-kit.d.ts.map +1 -0
  41. package/dist/editor-kit.js +17 -0
  42. package/dist/editor-kit.js.map +1 -0
  43. package/dist/editors/CheckboxEditor.d.ts +3 -0
  44. package/dist/editors/CheckboxEditor.d.ts.map +1 -0
  45. package/dist/editors/CheckboxEditor.js +10 -0
  46. package/dist/editors/CheckboxEditor.js.map +1 -0
  47. package/dist/editors/ColorEditor.d.ts +4 -0
  48. package/dist/editors/ColorEditor.d.ts.map +1 -0
  49. package/dist/editors/ColorEditor.js +21 -0
  50. package/dist/editors/ColorEditor.js.map +1 -0
  51. package/dist/editors/DateEditor.d.ts +3 -0
  52. package/dist/editors/DateEditor.d.ts.map +1 -0
  53. package/dist/editors/DateEditor.js +28 -0
  54. package/dist/editors/DateEditor.js.map +1 -0
  55. package/dist/editors/InlineText.d.ts +4 -0
  56. package/dist/editors/InlineText.d.ts.map +1 -0
  57. package/dist/editors/InlineText.js +77 -0
  58. package/dist/editors/InlineText.js.map +1 -0
  59. package/dist/editors/NumberEditor.d.ts +4 -0
  60. package/dist/editors/NumberEditor.d.ts.map +1 -0
  61. package/dist/editors/NumberEditor.js +23 -0
  62. package/dist/editors/NumberEditor.js.map +1 -0
  63. package/dist/editors/RelationshipEditor.d.ts +3 -0
  64. package/dist/editors/RelationshipEditor.d.ts.map +1 -0
  65. package/dist/editors/RelationshipEditor.js +60 -0
  66. package/dist/editors/RelationshipEditor.js.map +1 -0
  67. package/dist/editors/SelectEditor.d.ts +4 -0
  68. package/dist/editors/SelectEditor.d.ts.map +1 -0
  69. package/dist/editors/SelectEditor.js +36 -0
  70. package/dist/editors/SelectEditor.js.map +1 -0
  71. package/dist/editors/UploadEditor.d.ts +3 -0
  72. package/dist/editors/UploadEditor.d.ts.map +1 -0
  73. package/dist/editors/UploadEditor.js +48 -0
  74. package/dist/editors/UploadEditor.js.map +1 -0
  75. package/dist/editors/index.d.ts +13 -0
  76. package/dist/editors/index.d.ts.map +1 -0
  77. package/dist/editors/index.js +23 -0
  78. package/dist/editors/index.js.map +1 -0
  79. package/dist/editors/useDebouncedSearch.d.ts +17 -0
  80. package/dist/editors/useDebouncedSearch.d.ts.map +1 -0
  81. package/dist/editors/useDebouncedSearch.js +50 -0
  82. package/dist/editors/useDebouncedSearch.js.map +1 -0
  83. package/dist/hub.d.ts +14 -0
  84. package/dist/hub.d.ts.map +1 -0
  85. package/dist/hub.js +20 -0
  86. package/dist/hub.js.map +1 -0
  87. package/dist/index.d.ts +20 -0
  88. package/dist/index.d.ts.map +1 -0
  89. package/dist/index.js +19 -0
  90. package/dist/index.js.map +1 -0
  91. package/dist/layout.d.ts +12 -0
  92. package/dist/layout.d.ts.map +1 -0
  93. package/dist/layout.js +83 -0
  94. package/dist/layout.js.map +1 -0
  95. package/dist/locators.d.ts +19 -0
  96. package/dist/locators.d.ts.map +1 -0
  97. package/dist/locators.js +215 -0
  98. package/dist/locators.js.map +1 -0
  99. package/dist/messages.d.ts +100 -0
  100. package/dist/messages.d.ts.map +1 -0
  101. package/dist/messages.js +112 -0
  102. package/dist/messages.js.map +1 -0
  103. package/dist/registry.d.ts +15 -0
  104. package/dist/registry.d.ts.map +1 -0
  105. package/dist/registry.js +29 -0
  106. package/dist/registry.js.map +1 -0
  107. package/dist/renderers.d.ts +7 -0
  108. package/dist/renderers.d.ts.map +1 -0
  109. package/dist/renderers.js +12 -0
  110. package/dist/renderers.js.map +1 -0
  111. package/dist/sanitize.d.ts +2 -0
  112. package/dist/sanitize.d.ts.map +1 -0
  113. package/dist/sanitize.js +49 -0
  114. package/dist/sanitize.js.map +1 -0
  115. package/dist/styles.d.ts +8 -0
  116. package/dist/styles.d.ts.map +1 -0
  117. package/dist/styles.js +122 -0
  118. package/dist/styles.js.map +1 -0
  119. package/dist/targets.d.ts +18 -0
  120. package/dist/targets.d.ts.map +1 -0
  121. package/dist/targets.js +86 -0
  122. package/dist/targets.js.map +1 -0
  123. package/dist/types.d.ts +131 -0
  124. package/dist/types.d.ts.map +1 -0
  125. package/dist/types.js +2 -0
  126. package/dist/types.js.map +1 -0
  127. package/dist/ui/BlockPicker.d.ts +3 -0
  128. package/dist/ui/BlockPicker.d.ts.map +1 -0
  129. package/dist/ui/BlockPicker.js +27 -0
  130. package/dist/ui/BlockPicker.js.map +1 -0
  131. package/dist/ui/Dialog.d.ts +22 -0
  132. package/dist/ui/Dialog.d.ts.map +1 -0
  133. package/dist/ui/Dialog.js +83 -0
  134. package/dist/ui/Dialog.js.map +1 -0
  135. package/dist/ui/Dock.d.ts +7 -0
  136. package/dist/ui/Dock.d.ts.map +1 -0
  137. package/dist/ui/Dock.js +104 -0
  138. package/dist/ui/Dock.js.map +1 -0
  139. package/dist/ui/Overlay.d.ts +2 -0
  140. package/dist/ui/Overlay.d.ts.map +1 -0
  141. package/dist/ui/Overlay.js +150 -0
  142. package/dist/ui/Overlay.js.map +1 -0
  143. package/dist/ui/PostButton.d.ts +10 -0
  144. package/dist/ui/PostButton.d.ts.map +1 -0
  145. package/dist/ui/PostButton.js +7 -0
  146. package/dist/ui/PostButton.js.map +1 -0
  147. package/dist/ui/PreviewToolbar.d.ts +15 -0
  148. package/dist/ui/PreviewToolbar.d.ts.map +1 -0
  149. package/dist/ui/PreviewToolbar.js +45 -0
  150. package/dist/ui/PreviewToolbar.js.map +1 -0
  151. package/dist/ui/SettingsPanel.d.ts +11 -0
  152. package/dist/ui/SettingsPanel.d.ts.map +1 -0
  153. package/dist/ui/SettingsPanel.js +130 -0
  154. package/dist/ui/SettingsPanel.js.map +1 -0
  155. package/dist/useEvent.d.ts +7 -0
  156. package/dist/useEvent.d.ts.map +1 -0
  157. package/dist/useEvent.js +15 -0
  158. package/dist/useEvent.js.map +1 -0
  159. package/package.json +71 -4
  160. package/src/OnPageClient.tsx +67 -0
  161. package/src/annotations.ts +83 -0
  162. package/src/context.tsx +23 -0
  163. package/src/dom.ts +118 -0
  164. package/src/editor/FieldHost.tsx +66 -0
  165. package/src/editor/Ghosts.tsx +55 -0
  166. package/src/editor/OnPageEditor.tsx +315 -0
  167. package/src/editor/useGhosts.ts +64 -0
  168. package/src/editor/usePageEvents.ts +161 -0
  169. package/src/editor-kit.ts +27 -0
  170. package/src/editors/CheckboxEditor.tsx +31 -0
  171. package/src/editors/ColorEditor.tsx +64 -0
  172. package/src/editors/DateEditor.tsx +64 -0
  173. package/src/editors/InlineText.tsx +106 -0
  174. package/src/editors/NumberEditor.tsx +69 -0
  175. package/src/editors/RelationshipEditor.tsx +153 -0
  176. package/src/editors/SelectEditor.tsx +74 -0
  177. package/src/editors/UploadEditor.tsx +130 -0
  178. package/src/editors/index.ts +35 -0
  179. package/src/editors/useDebouncedSearch.ts +54 -0
  180. package/src/hub.ts +32 -0
  181. package/src/index.ts +55 -0
  182. package/src/layout.ts +83 -0
  183. package/src/locators.ts +210 -0
  184. package/src/messages.ts +120 -0
  185. package/src/registry.ts +43 -0
  186. package/src/renderers.ts +15 -0
  187. package/src/sanitize.ts +52 -0
  188. package/src/styles.ts +122 -0
  189. package/src/targets.ts +101 -0
  190. package/src/types.ts +132 -0
  191. package/src/ui/BlockPicker.tsx +49 -0
  192. package/src/ui/Dialog.tsx +159 -0
  193. package/src/ui/Dock.tsx +240 -0
  194. package/src/ui/Overlay.tsx +324 -0
  195. package/src/ui/PostButton.tsx +26 -0
  196. package/src/ui/PreviewToolbar.tsx +123 -0
  197. package/src/ui/SettingsPanel.tsx +279 -0
  198. package/src/useEvent.ts +16 -0
@@ -0,0 +1,7 @@
1
+ /** Framework-free Renderer adapters. Next.js's router.refresh() renderer lives in @payload-onpage/next. */
2
+ import type { Renderer } from '@payload-onpage/core';
3
+ /** The page renders from editor state (OnPageClient everywhere): nothing to do after a save. */
4
+ export declare const clientState: () => Renderer;
5
+ /** Reload the whole page after each save. A last resort for frameworks without a soft refresh. */
6
+ export declare const fullReload: () => Renderer;
7
+ //# sourceMappingURL=renderers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"renderers.d.ts","sourceRoot":"","sources":["../src/renderers.ts"],"names":[],"mappings":"AAAA,2GAA2G;AAC3G,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AAEpD,gGAAgG;AAChG,eAAO,MAAM,WAAW,QAAO,QAA4D,CAAA;AAE3F,kGAAkG;AAClG,eAAO,MAAM,UAAU,QAAO,QAO5B,CAAA"}
@@ -0,0 +1,12 @@
1
+ /** The page renders from editor state (OnPageClient everywhere): nothing to do after a save. */
2
+ export const clientState = () => ({ name: 'client', live: true, afterSave() { } });
3
+ /** Reload the whole page after each save. A last resort for frameworks without a soft refresh. */
4
+ export const fullReload = () => ({
5
+ name: 'reload',
6
+ live: false,
7
+ afterSave() {
8
+ window.location.reload();
9
+ return new Promise(() => { }); // never resolves: the page goes away
10
+ },
11
+ });
12
+ //# sourceMappingURL=renderers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"renderers.js","sourceRoot":"","sources":["../src/renderers.ts"],"names":[],"mappings":"AAGA,gGAAgG;AAChG,MAAM,CAAC,MAAM,WAAW,GAAG,GAAa,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,KAAI,CAAC,EAAE,CAAC,CAAA;AAE3F,kGAAkG;AAClG,MAAM,CAAC,MAAM,UAAU,GAAG,GAAa,EAAE,CAAC,CAAC;IACzC,IAAI,EAAE,QAAQ;IACd,IAAI,EAAE,KAAK;IACX,SAAS;QACP,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAA;QACxB,OAAO,IAAI,OAAO,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA,CAAC,qCAAqC;IACpE,CAAC;CACF,CAAC,CAAA"}
@@ -0,0 +1,2 @@
1
+ export declare function sanitizeHtml(html: string): string;
2
+ //# sourceMappingURL=sanitize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sanitize.d.ts","sourceRoot":"","sources":["../src/sanitize.ts"],"names":[],"mappings":"AA2BA,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAwBjD"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Minimal HTML sanitizer for editor-generated markup shown in ghosts (the
3
+ * stand-in content kept on screen until a server refresh lands). Drops active
4
+ * elements, event handlers and unsafe URLs.
5
+ */
6
+ import { safeLinkUrl } from '@payload-onpage/core';
7
+ const DROP = new Set([
8
+ 'SCRIPT',
9
+ 'STYLE',
10
+ 'IFRAME',
11
+ 'OBJECT',
12
+ 'EMBED',
13
+ 'LINK',
14
+ 'META',
15
+ 'BASE',
16
+ 'FORM',
17
+ 'INPUT',
18
+ 'TEXTAREA',
19
+ 'BUTTON',
20
+ 'SELECT',
21
+ 'TEMPLATE',
22
+ 'SVG',
23
+ 'MATH',
24
+ ]);
25
+ const URL_ATTRS = new Set(['href', 'src', 'xlink:href', 'action', 'formaction', 'srcset', 'poster']);
26
+ export function sanitizeHtml(html) {
27
+ const tpl = document.createElement('template');
28
+ tpl.innerHTML = html;
29
+ const walk = (node) => {
30
+ for (const el of Array.from(node.children)) {
31
+ if (DROP.has(el.tagName.toUpperCase())) {
32
+ el.remove();
33
+ continue;
34
+ }
35
+ for (const attr of Array.from(el.attributes)) {
36
+ const name = attr.name.toLowerCase();
37
+ if (name.startsWith('on') ||
38
+ name === 'style' ||
39
+ (URL_ATTRS.has(name) && !safeLinkUrl(attr.value))) {
40
+ el.removeAttribute(attr.name);
41
+ }
42
+ }
43
+ walk(el);
44
+ }
45
+ };
46
+ walk(tpl.content);
47
+ return tpl.innerHTML;
48
+ }
49
+ //# sourceMappingURL=sanitize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sanitize.js","sourceRoot":"","sources":["../src/sanitize.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAA;AAElD,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC;IACnB,QAAQ;IACR,OAAO;IACP,QAAQ;IACR,QAAQ;IACR,OAAO;IACP,MAAM;IACN,MAAM;IACN,MAAM;IACN,MAAM;IACN,OAAO;IACP,UAAU;IACV,QAAQ;IACR,QAAQ;IACR,UAAU;IACV,KAAK;IACL,MAAM;CACP,CAAC,CAAA;AACF,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,YAAY,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAA;AAEpG,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,MAAM,GAAG,GAAG,QAAQ,CAAC,aAAa,CAAC,UAAU,CAAC,CAAA;IAC9C,GAAG,CAAC,SAAS,GAAG,IAAI,CAAA;IACpB,MAAM,IAAI,GAAG,CAAC,IAAgB,EAAE,EAAE;QAChC,KAAK,MAAM,EAAE,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC3C,IAAI,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC;gBACvC,EAAE,CAAC,MAAM,EAAE,CAAA;gBACX,SAAQ;YACV,CAAC;YACD,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,EAAE,CAAC;gBAC7C,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAA;gBACpC,IACE,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;oBACrB,IAAI,KAAK,OAAO;oBAChB,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EACjD,CAAC;oBACD,EAAE,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;gBAC/B,CAAC;YACH,CAAC;YACD,IAAI,CAAC,EAAE,CAAC,CAAA;QACV,CAAC;IACH,CAAC,CAAA;IACD,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;IACjB,OAAO,GAAG,CAAC,SAAS,CAAA;AACtB,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * The editor's own styles, injected once. Everything is prefixed `po-` and set
3
+ * explicitly, so the host site's CSS (and its absence) doesn't matter.
4
+ */
5
+ export declare const PO_Z = 2147483000;
6
+ export declare const PO_CSS = "\n.po-layer{position:fixed;inset:0;pointer-events:none;z-index:2147483000;font:13px/1.35 system-ui,-apple-system,\"Segoe UI\",sans-serif;color:#0f172a}\n.po-layer *{box-sizing:border-box}\n.po-outline{position:fixed;border:2px solid #4f7cff;border-radius:4px;pointer-events:none;transition:all .06s ease-out}\n.po-outline.is-block{border-style:dashed;border-color:#4f7cff99}\n.po-outline.is-item{border-color:#22a06b}\n.po-chip{position:fixed;transform:translateY(-100%);background:#3b63e0;color:#fff;font-size:11px;font-weight:600;padding:2px 6px;border-radius:4px 4px 0 0;white-space:nowrap;pointer-events:none}\n.po-chip.is-item{background:#167a45}\n.po-bar{position:fixed;display:flex;gap:2px;padding:3px;background:#0f172a;border-radius:8px;box-shadow:0 6px 24px #0f172a40;pointer-events:auto}\n.po-bar.is-item{background:#14532d}\n.po-btn{all:unset;display:inline-flex;align-items:center;justify-content:center;gap:6px;min-width:28px;height:28px;padding:0 8px;border-radius:6px;color:#fff;font:600 12px/1 system-ui,-apple-system,sans-serif;cursor:pointer;white-space:nowrap}\n.po-btn:hover{background:#ffffff1f}\n.po-btn:focus-visible{outline:2px solid #93c5fd;outline-offset:1px}\n.po-btn[disabled]{opacity:.35;cursor:default;background:none}\n.po-btn.is-danger:hover{background:#ef444455}\n.po-add{position:fixed;transform:translate(-50%,-50%);pointer-events:auto}\n.po-add .po-btn{background:#3b63e0;box-shadow:0 2px 10px #4f7cff66;height:24px;font-size:11px}\n.po-add .po-btn:hover{background:#2f5bd9}\n.po-add.is-item .po-btn{background:#167a45;box-shadow:0 2px 10px #167a4555}\n.po-panel{position:fixed;width:320px;max-height:min(70vh,560px);overflow:auto;background:#fff;color:#0f172a;border-radius:12px;box-shadow:0 20px 60px #0f172a40,0 0 0 1px #0f172a14;pointer-events:auto;padding:14px}\n.po-panel .po-h3{margin:0 0 10px;font:700 13px/1.2 system-ui,sans-serif}\n.po-panel .po-h4{margin:14px 0 6px;font:600 11px/1.2 system-ui,sans-serif;text-transform:uppercase;letter-spacing:.06em;color:#64748b}\n.po-row{display:flex;flex-direction:column;gap:4px;margin:0 0 10px}\n.po-row label{font-weight:600;font-size:12px}\n.po-row small{color:#64748b;font-size:11px}\n.po-input,.po-select{all:unset;box-sizing:border-box;width:100%;height:32px;padding:0 10px;border:1px solid #cbd5e1;border-radius:8px;background:#fff;font:13px system-ui,sans-serif;color:#0f172a}\n.po-select{appearance:auto;-webkit-appearance:auto}\n.po-input:focus,.po-select:focus{border-color:#4f7cff;box-shadow:0 0 0 3px #4f7cff33}\n.po-check{display:flex;align-items:center;gap:8px;font-size:12px;font-weight:600}\n.po-seg{display:flex;flex-wrap:wrap;gap:4px}\n.po-seg button{all:unset;padding:5px 9px;border:1px solid #cbd5e1;border-radius:7px;font:12px system-ui,sans-serif;cursor:pointer;background:#fff}\n.po-seg button[aria-pressed=\"true\"]{background:#0f172a;color:#fff;border-color:#0f172a}\n.po-modal-bg{position:fixed;inset:0;background:#0f172a66;pointer-events:auto;display:flex;align-items:center;justify-content:center;padding:24px}\n.po-modal{width:min(880px,100%);max-height:85vh;display:flex;flex-direction:column;background:#fff;border-radius:14px;box-shadow:0 30px 80px #0f172a55;overflow:hidden}\n.po-modal header{display:flex;align-items:center;gap:10px;padding:14px 16px;border-bottom:1px solid #e2e8f0}\n.po-modal header .po-h3{margin:0;flex:1;font:700 14px system-ui,sans-serif}\n.po-modal .po-body{padding:14px 16px;overflow:auto}\n.po-modal .po-btn{color:#0f172a}\n.po-modal .po-btn:hover{background:#f1f5f9}\n.po-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:10px}\n.po-tile{all:unset;display:flex;flex-direction:column;gap:6px;cursor:pointer;border-radius:10px;padding:6px;border:2px solid transparent}\n.po-tile:hover,.po-tile:focus-visible{border-color:#4f7cff;background:#f8fafc}\n.po-tile img{width:100%;aspect-ratio:4/3;object-fit:cover;border-radius:6px;background:#f1f5f9}\n.po-tile span{font-size:11px;color:#475569;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.po-blocks{display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:10px}\n.po-blocks button{all:unset;padding:14px;border:1px solid #e2e8f0;border-radius:10px;cursor:pointer;font:600 13px system-ui,sans-serif}\n.po-blocks button:hover,.po-blocks button:focus-visible{border-color:#4f7cff;background:#f5f8ff}\n.po-dock{position:fixed;left:50%;bottom:16px;transform:translateX(-50%);display:flex;align-items:center;gap:4px;padding:6px;background:#0f172a;color:#fff;border-radius:12px;box-shadow:0 12px 40px #0f172a55;pointer-events:auto;max-width:calc(100vw - 24px);flex-wrap:wrap}\n.po-dock .po-status{padding:0 10px;font-size:12px;opacity:.85;white-space:nowrap}\n.po-dock .po-status.is-bad{color:#fca5a5;opacity:1}\n.po-dock .po-sep{width:1px;height:20px;background:#ffffff26;margin:0 4px}\n.po-dock .po-primary{background:#3b63e0}\n.po-dock .po-primary:hover{background:#2f5bd9}\n.po-chip-locale{padding:2px 7px;border-radius:999px;background:#ffffff1f;font-size:11px;font-weight:700;text-transform:uppercase}\n.po-notice{position:fixed;left:50%;bottom:72px;transform:translateX(-50%);max-width:min(560px,calc(100vw - 24px));padding:8px 12px;border-radius:10px;background:#fffbeb;color:#78350f;border:1px solid #fcd34d;font-size:12px;pointer-events:auto}\n.po-doc-layer{position:absolute;top:0;left:0;width:0;height:0;overflow:visible;z-index:2147483000;pointer-events:none;font:13px/1.35 system-ui,-apple-system,\"Segoe UI\",sans-serif}\n.po-inline{position:absolute;pointer-events:auto;outline:2px solid #4f7cff;outline-offset:2px;border-radius:2px;background:transparent;white-space:pre-wrap;word-break:break-word;caret-color:currentColor;cursor:text}\n.po-inline:focus{outline-color:#4f7cff}\n.po-rt{position:absolute;pointer-events:auto}\n.po-rt-surface{outline:2px solid #4f7cff;outline-offset:6px;border-radius:2px;min-height:1.5em}\n.po-rt-surface:focus{outline:2px solid #4f7cff}\n.po-rt-toolbar{position:absolute;left:0;top:-48px;display:flex;gap:2px;padding:3px;background:#0f172a;border-radius:8px;box-shadow:0 6px 24px #0f172a40}\n.po-rt-toolbar .po-btn[aria-pressed=\"true\"]{background:#ffffff2e}\n.po-pop{position:fixed;min-width:220px;max-width:340px;background:#fff;color:#0f172a;border-radius:10px;box-shadow:0 14px 40px #0f172a40,0 0 0 1px #0f172a14;pointer-events:auto;padding:10px}\n.po-pop .po-row{margin-bottom:8px}\n.po-pop-list{display:flex;flex-direction:column;gap:2px;max-height:260px;overflow:auto}\n.po-pop-list button{all:unset;padding:7px 9px;border-radius:7px;cursor:pointer;font:13px system-ui,sans-serif}\n.po-pop-list button:hover,.po-pop-list button:focus-visible,.po-pop-list button[aria-selected=\"true\"]{background:#eef2ff}\n.po-ghost{position:absolute;pointer-events:none;white-space:pre-wrap;word-break:break-word}\n.po-docs{font-size:11px;padding:2px 7px;border-radius:999px;background:#ffffff1f}\n.po-frame-bg{position:fixed;inset:0;background:#0f172acc;pointer-events:auto;display:flex;flex-direction:column;align-items:center;padding:56px 16px 16px}\n.po-frame-bg iframe{background:#fff;border:0;border-radius:12px;box-shadow:0 30px 80px #0008;height:100%;max-width:100%}\n.po-badge{padding:2px 8px;border-radius:999px;background:#f59e0b;color:#111;font-weight:700;font-size:11px;text-transform:uppercase;letter-spacing:.04em}\n.po-b{font-weight:700}.po-i{font-style:italic}.po-u{text-decoration:underline}\n.po-opaque{display:inline-flex;align-items:center;gap:6px;margin:6px 0;padding:6px 10px;border:1px dashed #94a3b8;border-radius:8px;background:#f8fafc;color:#475569;font:12px system-ui,sans-serif;user-select:none}\nhtml[data-onpage-editing] [data-onpage-field]{cursor:pointer}\n[data-onpage-hidden=\"true\"]{visibility:hidden!important}\n.po-preview{position:fixed;left:50%;bottom:16px;transform:translateX(-50%);display:flex;align-items:center;gap:6px;padding:6px;background:#0f172a;color:#fff;border-radius:12px;box-shadow:0 12px 40px #0f172a55;pointer-events:auto;max-width:calc(100vw - 24px);flex-wrap:wrap}\n.po-preview .po-status{padding:0 8px;font-size:12px;opacity:.85}\n.po-rel-item{display:flex;align-items:center;gap:6px;padding:4px 6px;border-radius:6px;background:#f1f5f9;font-size:12px;margin:0 0 4px}\n.po-rel-item button{all:unset;cursor:pointer;margin-left:auto;padding:0 4px;color:#64748b}\n\n.po-dialog-head{display:flex;align-items:center;gap:8px;margin:0 0 10px}\n.po-modal .po-dialog-head{margin:0;padding:14px 16px;border-bottom:1px solid #e2e8f0}\n.po-dialog-head .po-h3{flex:1;margin:0;font:700 14px/1.2 system-ui,sans-serif}\n.po-close{color:#0f172a;min-width:24px;height:24px}\n.po-close:hover{background:#f1f5f9}\n.po-bar-label{cursor:default;opacity:.8}\n.po-muted{color:#64748b;margin:6px 0}\n.po-error{color:#b91c1c;margin:6px 0}\n.po-center{text-align:center;margin-top:12px}\n.po-actions{display:flex;gap:6px}\n.po-inline-row{flex-direction:row;align-items:center}\n.po-search{width:240px}\n.po-picker{cursor:pointer;display:flex;align-items:center}\n.po-group{border:0;margin:0;padding:0}\n.po-primary,.po-pop .po-primary,.po-panel .po-primary{background:#3b63e0;color:#fff}\n.po-primary:hover{background:#2f5bd9}\n.po-pop .po-btn,.po-panel .po-btn{color:#0f172a}\n.po-pop .po-btn.po-primary,.po-panel .po-btn.po-primary{color:#fff}\n.po-frame-close{all:unset;position:absolute;inset:0;cursor:pointer}\n.po-frame-bg iframe{position:relative}\n.po-input[aria-invalid=\"true\"]{border-color:#dc2626}\nhtml[data-onpage-editing] [data-onpage-focusable]:focus-visible{outline:2px solid #4f7cff;outline-offset:2px}\n@media (prefers-reduced-motion:reduce){.po-outline{transition:none}}\n";
7
+ export declare function injectStyles(): void;
8
+ //# sourceMappingURL=styles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../src/styles.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,eAAO,MAAM,IAAI,aAAa,CAAA;AAE9B,eAAO,MAAM,MAAM,s5SAyGlB,CAAA;AAGD,wBAAgB,YAAY,SAO3B"}
package/dist/styles.js ADDED
@@ -0,0 +1,122 @@
1
+ /**
2
+ * The editor's own styles, injected once. Everything is prefixed `po-` and set
3
+ * explicitly, so the host site's CSS (and its absence) doesn't matter.
4
+ */
5
+ export const PO_Z = 2147483000;
6
+ export const PO_CSS = `
7
+ .po-layer{position:fixed;inset:0;pointer-events:none;z-index:${PO_Z};font:13px/1.35 system-ui,-apple-system,"Segoe UI",sans-serif;color:#0f172a}
8
+ .po-layer *{box-sizing:border-box}
9
+ .po-outline{position:fixed;border:2px solid #4f7cff;border-radius:4px;pointer-events:none;transition:all .06s ease-out}
10
+ .po-outline.is-block{border-style:dashed;border-color:#4f7cff99}
11
+ .po-outline.is-item{border-color:#22a06b}
12
+ .po-chip{position:fixed;transform:translateY(-100%);background:#3b63e0;color:#fff;font-size:11px;font-weight:600;padding:2px 6px;border-radius:4px 4px 0 0;white-space:nowrap;pointer-events:none}
13
+ .po-chip.is-item{background:#167a45}
14
+ .po-bar{position:fixed;display:flex;gap:2px;padding:3px;background:#0f172a;border-radius:8px;box-shadow:0 6px 24px #0f172a40;pointer-events:auto}
15
+ .po-bar.is-item{background:#14532d}
16
+ .po-btn{all:unset;display:inline-flex;align-items:center;justify-content:center;gap:6px;min-width:28px;height:28px;padding:0 8px;border-radius:6px;color:#fff;font:600 12px/1 system-ui,-apple-system,sans-serif;cursor:pointer;white-space:nowrap}
17
+ .po-btn:hover{background:#ffffff1f}
18
+ .po-btn:focus-visible{outline:2px solid #93c5fd;outline-offset:1px}
19
+ .po-btn[disabled]{opacity:.35;cursor:default;background:none}
20
+ .po-btn.is-danger:hover{background:#ef444455}
21
+ .po-add{position:fixed;transform:translate(-50%,-50%);pointer-events:auto}
22
+ .po-add .po-btn{background:#3b63e0;box-shadow:0 2px 10px #4f7cff66;height:24px;font-size:11px}
23
+ .po-add .po-btn:hover{background:#2f5bd9}
24
+ .po-add.is-item .po-btn{background:#167a45;box-shadow:0 2px 10px #167a4555}
25
+ .po-panel{position:fixed;width:320px;max-height:min(70vh,560px);overflow:auto;background:#fff;color:#0f172a;border-radius:12px;box-shadow:0 20px 60px #0f172a40,0 0 0 1px #0f172a14;pointer-events:auto;padding:14px}
26
+ .po-panel .po-h3{margin:0 0 10px;font:700 13px/1.2 system-ui,sans-serif}
27
+ .po-panel .po-h4{margin:14px 0 6px;font:600 11px/1.2 system-ui,sans-serif;text-transform:uppercase;letter-spacing:.06em;color:#64748b}
28
+ .po-row{display:flex;flex-direction:column;gap:4px;margin:0 0 10px}
29
+ .po-row label{font-weight:600;font-size:12px}
30
+ .po-row small{color:#64748b;font-size:11px}
31
+ .po-input,.po-select{all:unset;box-sizing:border-box;width:100%;height:32px;padding:0 10px;border:1px solid #cbd5e1;border-radius:8px;background:#fff;font:13px system-ui,sans-serif;color:#0f172a}
32
+ .po-select{appearance:auto;-webkit-appearance:auto}
33
+ .po-input:focus,.po-select:focus{border-color:#4f7cff;box-shadow:0 0 0 3px #4f7cff33}
34
+ .po-check{display:flex;align-items:center;gap:8px;font-size:12px;font-weight:600}
35
+ .po-seg{display:flex;flex-wrap:wrap;gap:4px}
36
+ .po-seg button{all:unset;padding:5px 9px;border:1px solid #cbd5e1;border-radius:7px;font:12px system-ui,sans-serif;cursor:pointer;background:#fff}
37
+ .po-seg button[aria-pressed="true"]{background:#0f172a;color:#fff;border-color:#0f172a}
38
+ .po-modal-bg{position:fixed;inset:0;background:#0f172a66;pointer-events:auto;display:flex;align-items:center;justify-content:center;padding:24px}
39
+ .po-modal{width:min(880px,100%);max-height:85vh;display:flex;flex-direction:column;background:#fff;border-radius:14px;box-shadow:0 30px 80px #0f172a55;overflow:hidden}
40
+ .po-modal header{display:flex;align-items:center;gap:10px;padding:14px 16px;border-bottom:1px solid #e2e8f0}
41
+ .po-modal header .po-h3{margin:0;flex:1;font:700 14px system-ui,sans-serif}
42
+ .po-modal .po-body{padding:14px 16px;overflow:auto}
43
+ .po-modal .po-btn{color:#0f172a}
44
+ .po-modal .po-btn:hover{background:#f1f5f9}
45
+ .po-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:10px}
46
+ .po-tile{all:unset;display:flex;flex-direction:column;gap:6px;cursor:pointer;border-radius:10px;padding:6px;border:2px solid transparent}
47
+ .po-tile:hover,.po-tile:focus-visible{border-color:#4f7cff;background:#f8fafc}
48
+ .po-tile img{width:100%;aspect-ratio:4/3;object-fit:cover;border-radius:6px;background:#f1f5f9}
49
+ .po-tile span{font-size:11px;color:#475569;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
50
+ .po-blocks{display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:10px}
51
+ .po-blocks button{all:unset;padding:14px;border:1px solid #e2e8f0;border-radius:10px;cursor:pointer;font:600 13px system-ui,sans-serif}
52
+ .po-blocks button:hover,.po-blocks button:focus-visible{border-color:#4f7cff;background:#f5f8ff}
53
+ .po-dock{position:fixed;left:50%;bottom:16px;transform:translateX(-50%);display:flex;align-items:center;gap:4px;padding:6px;background:#0f172a;color:#fff;border-radius:12px;box-shadow:0 12px 40px #0f172a55;pointer-events:auto;max-width:calc(100vw - 24px);flex-wrap:wrap}
54
+ .po-dock .po-status{padding:0 10px;font-size:12px;opacity:.85;white-space:nowrap}
55
+ .po-dock .po-status.is-bad{color:#fca5a5;opacity:1}
56
+ .po-dock .po-sep{width:1px;height:20px;background:#ffffff26;margin:0 4px}
57
+ .po-dock .po-primary{background:#3b63e0}
58
+ .po-dock .po-primary:hover{background:#2f5bd9}
59
+ .po-chip-locale{padding:2px 7px;border-radius:999px;background:#ffffff1f;font-size:11px;font-weight:700;text-transform:uppercase}
60
+ .po-notice{position:fixed;left:50%;bottom:72px;transform:translateX(-50%);max-width:min(560px,calc(100vw - 24px));padding:8px 12px;border-radius:10px;background:#fffbeb;color:#78350f;border:1px solid #fcd34d;font-size:12px;pointer-events:auto}
61
+ .po-doc-layer{position:absolute;top:0;left:0;width:0;height:0;overflow:visible;z-index:${PO_Z};pointer-events:none;font:13px/1.35 system-ui,-apple-system,"Segoe UI",sans-serif}
62
+ .po-inline{position:absolute;pointer-events:auto;outline:2px solid #4f7cff;outline-offset:2px;border-radius:2px;background:transparent;white-space:pre-wrap;word-break:break-word;caret-color:currentColor;cursor:text}
63
+ .po-inline:focus{outline-color:#4f7cff}
64
+ .po-rt{position:absolute;pointer-events:auto}
65
+ .po-rt-surface{outline:2px solid #4f7cff;outline-offset:6px;border-radius:2px;min-height:1.5em}
66
+ .po-rt-surface:focus{outline:2px solid #4f7cff}
67
+ .po-rt-toolbar{position:absolute;left:0;top:-48px;display:flex;gap:2px;padding:3px;background:#0f172a;border-radius:8px;box-shadow:0 6px 24px #0f172a40}
68
+ .po-rt-toolbar .po-btn[aria-pressed="true"]{background:#ffffff2e}
69
+ .po-pop{position:fixed;min-width:220px;max-width:340px;background:#fff;color:#0f172a;border-radius:10px;box-shadow:0 14px 40px #0f172a40,0 0 0 1px #0f172a14;pointer-events:auto;padding:10px}
70
+ .po-pop .po-row{margin-bottom:8px}
71
+ .po-pop-list{display:flex;flex-direction:column;gap:2px;max-height:260px;overflow:auto}
72
+ .po-pop-list button{all:unset;padding:7px 9px;border-radius:7px;cursor:pointer;font:13px system-ui,sans-serif}
73
+ .po-pop-list button:hover,.po-pop-list button:focus-visible,.po-pop-list button[aria-selected="true"]{background:#eef2ff}
74
+ .po-ghost{position:absolute;pointer-events:none;white-space:pre-wrap;word-break:break-word}
75
+ .po-docs{font-size:11px;padding:2px 7px;border-radius:999px;background:#ffffff1f}
76
+ .po-frame-bg{position:fixed;inset:0;background:#0f172acc;pointer-events:auto;display:flex;flex-direction:column;align-items:center;padding:56px 16px 16px}
77
+ .po-frame-bg iframe{background:#fff;border:0;border-radius:12px;box-shadow:0 30px 80px #0008;height:100%;max-width:100%}
78
+ .po-badge{padding:2px 8px;border-radius:999px;background:#f59e0b;color:#111;font-weight:700;font-size:11px;text-transform:uppercase;letter-spacing:.04em}
79
+ .po-b{font-weight:700}.po-i{font-style:italic}.po-u{text-decoration:underline}
80
+ .po-opaque{display:inline-flex;align-items:center;gap:6px;margin:6px 0;padding:6px 10px;border:1px dashed #94a3b8;border-radius:8px;background:#f8fafc;color:#475569;font:12px system-ui,sans-serif;user-select:none}
81
+ html[data-onpage-editing] [data-onpage-field]{cursor:pointer}
82
+ [data-onpage-hidden="true"]{visibility:hidden!important}
83
+ .po-preview{position:fixed;left:50%;bottom:16px;transform:translateX(-50%);display:flex;align-items:center;gap:6px;padding:6px;background:#0f172a;color:#fff;border-radius:12px;box-shadow:0 12px 40px #0f172a55;pointer-events:auto;max-width:calc(100vw - 24px);flex-wrap:wrap}
84
+ .po-preview .po-status{padding:0 8px;font-size:12px;opacity:.85}
85
+ .po-rel-item{display:flex;align-items:center;gap:6px;padding:4px 6px;border-radius:6px;background:#f1f5f9;font-size:12px;margin:0 0 4px}
86
+ .po-rel-item button{all:unset;cursor:pointer;margin-left:auto;padding:0 4px;color:#64748b}
87
+
88
+ .po-dialog-head{display:flex;align-items:center;gap:8px;margin:0 0 10px}
89
+ .po-modal .po-dialog-head{margin:0;padding:14px 16px;border-bottom:1px solid #e2e8f0}
90
+ .po-dialog-head .po-h3{flex:1;margin:0;font:700 14px/1.2 system-ui,sans-serif}
91
+ .po-close{color:#0f172a;min-width:24px;height:24px}
92
+ .po-close:hover{background:#f1f5f9}
93
+ .po-bar-label{cursor:default;opacity:.8}
94
+ .po-muted{color:#64748b;margin:6px 0}
95
+ .po-error{color:#b91c1c;margin:6px 0}
96
+ .po-center{text-align:center;margin-top:12px}
97
+ .po-actions{display:flex;gap:6px}
98
+ .po-inline-row{flex-direction:row;align-items:center}
99
+ .po-search{width:240px}
100
+ .po-picker{cursor:pointer;display:flex;align-items:center}
101
+ .po-group{border:0;margin:0;padding:0}
102
+ .po-primary,.po-pop .po-primary,.po-panel .po-primary{background:#3b63e0;color:#fff}
103
+ .po-primary:hover{background:#2f5bd9}
104
+ .po-pop .po-btn,.po-panel .po-btn{color:#0f172a}
105
+ .po-pop .po-btn.po-primary,.po-panel .po-btn.po-primary{color:#fff}
106
+ .po-frame-close{all:unset;position:absolute;inset:0;cursor:pointer}
107
+ .po-frame-bg iframe{position:relative}
108
+ .po-input[aria-invalid="true"]{border-color:#dc2626}
109
+ html[data-onpage-editing] [data-onpage-focusable]:focus-visible{outline:2px solid #4f7cff;outline-offset:2px}
110
+ @media (prefers-reduced-motion:reduce){.po-outline{transition:none}}
111
+ `;
112
+ let injected = false;
113
+ export function injectStyles() {
114
+ if (injected || typeof document === 'undefined')
115
+ return;
116
+ injected = true;
117
+ const el = document.createElement('style');
118
+ el.setAttribute('data-onpage-styles', '');
119
+ el.textContent = PO_CSS;
120
+ document.head.appendChild(el);
121
+ }
122
+ //# sourceMappingURL=styles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"styles.js","sourceRoot":"","sources":["../src/styles.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,CAAC,MAAM,IAAI,GAAG,UAAU,CAAA;AAE9B,MAAM,CAAC,MAAM,MAAM,GAAG;+DACyC,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;yFAsDsB,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkD5F,CAAA;AAED,IAAI,QAAQ,GAAG,KAAK,CAAA;AACpB,MAAM,UAAU,YAAY;IAC1B,IAAI,QAAQ,IAAI,OAAO,QAAQ,KAAK,WAAW;QAAE,OAAM;IACvD,QAAQ,GAAG,IAAI,CAAA;IACf,MAAM,EAAE,GAAG,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,CAAA;IAC1C,EAAE,CAAC,YAAY,CAAC,oBAAoB,EAAE,EAAE,CAAC,CAAA;IACzC,EAAE,CAAC,WAAW,GAAG,MAAM,CAAA;IACvB,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC,CAAA;AAC/B,CAAC"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Keeps an index of the page's targets current: scans with the locator on
3
+ * mount and again whenever the page's DOM changes (a server refresh, a client
4
+ * re-render, lazy content), ignoring the editor's own UI. Rescans are
5
+ * debounced and run when the browser is idle.
6
+ */
7
+ import { type Locator, type Target } from '@payload-onpage/core';
8
+ import type { TargetIndex } from './types.ts';
9
+ export declare const isFieldTarget: (t: Target) => boolean;
10
+ export declare const isRowTarget: (t: Target) => boolean;
11
+ export declare function buildIndex(all: Target[]): TargetIndex;
12
+ /** The innermost field target and all row targets (innermost first) at or above `el`. */
13
+ export declare function targetsAt(index: TargetIndex, el: Element | null): {
14
+ field?: Target;
15
+ rows: Target[];
16
+ };
17
+ export declare function useTargets(locator: Locator, root: () => ParentNode | null): TargetIndex;
18
+ //# sourceMappingURL=targets.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"targets.d.ts","sourceRoot":"","sources":["../src/targets.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,OAAO,EAAa,KAAK,OAAO,EAAE,KAAK,MAAM,EAAmB,MAAM,sBAAsB,CAAA;AAI5F,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAG7C,eAAO,MAAM,aAAa,GAAI,GAAG,MAAM,KAAG,OAAkC,CAAA;AAC5E,eAAO,MAAM,WAAW,GAAI,GAAG,MAAM,KAAG,OAA4B,CAAA;AAEpE,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,WAAW,CAoBrD;AAED,yFAAyF;AACzF,wBAAgB,SAAS,CACvB,KAAK,EAAE,WAAW,EAClB,EAAE,EAAE,OAAO,GAAG,IAAI,GACjB;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAA;CAAE,CAUpC;AAYD,wBAAgB,UAAU,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,GAAG,IAAI,GAAG,WAAW,CAkCvF"}
@@ -0,0 +1,86 @@
1
+ 'use client';
2
+ /**
3
+ * Keeps an index of the page's targets current: scans with the locator on
4
+ * mount and again whenever the page's DOM changes (a server refresh, a client
5
+ * re-render, lazy content), ignoring the editor's own UI. Rescans are
6
+ * debounced and run when the browser is idle.
7
+ */
8
+ import { targetKey } from '@payload-onpage/core';
9
+ import { useEffect, useState } from 'react';
10
+ import { ATTR } from "./annotations.js";
11
+ import { isOurs } from "./dom.js";
12
+ const FIELD_KINDS = new Set(['text', 'textarea', 'richtext', 'media', 'field']);
13
+ export const isFieldTarget = (t) => FIELD_KINDS.has(t.kind);
14
+ export const isRowTarget = (t) => t.kind === 'item';
15
+ export function buildIndex(all) {
16
+ const byEl = new Map();
17
+ const byPath = new Map();
18
+ // Annotations first: they are precise, inferred targets are a fallback.
19
+ const sorted = [...all].sort((a, b) => Number(a.source !== 'annotation') - Number(b.source !== 'annotation'));
20
+ for (const t of sorted) {
21
+ byEl.set(t.el, [...(byEl.get(t.el) ?? []), t]);
22
+ const k = targetKey(t.ref, t.path);
23
+ byPath.set(k, [...(byPath.get(k) ?? []), t]);
24
+ }
25
+ return {
26
+ all: sorted,
27
+ at: (el) => byEl.get(el) ?? [],
28
+ find: (key, path, kinds) => (byPath.get(targetKey(key, path)) ?? []).find((t) => t.el.isConnected && (!kinds || kinds.includes(t.kind))),
29
+ };
30
+ }
31
+ /** The innermost field target and all row targets (innermost first) at or above `el`. */
32
+ export function targetsAt(index, el) {
33
+ let field;
34
+ const rows = [];
35
+ for (let node = el; node; node = node.parentElement) {
36
+ for (const t of index.at(node)) {
37
+ if (!field && isFieldTarget(t))
38
+ field = t;
39
+ if (isRowTarget(t))
40
+ rows.push(t);
41
+ }
42
+ }
43
+ return { field, rows };
44
+ }
45
+ const WATCHED = [ATTR.ref, ATTR.field, ATTR.row, ATTR.list, 'alt'];
46
+ const idle = (fn) => {
47
+ if (typeof requestIdleCallback === 'function') {
48
+ const h = requestIdleCallback(fn, { timeout: 300 });
49
+ return () => cancelIdleCallback(h);
50
+ }
51
+ const h = setTimeout(fn, 50);
52
+ return () => clearTimeout(h);
53
+ };
54
+ export function useTargets(locator, root) {
55
+ const [index, setIndex] = useState(() => buildIndex([]));
56
+ useEffect(() => {
57
+ const r = root();
58
+ if (!r)
59
+ return;
60
+ let debounce;
61
+ let cancelIdle;
62
+ const scan = () => setIndex(buildIndex(locator.scan(r)));
63
+ scan();
64
+ const observer = new MutationObserver((records) => {
65
+ if (records.every((m) => isOurs(m.target instanceof Element ? m.target : m.target.parentElement)))
66
+ return;
67
+ clearTimeout(debounce);
68
+ cancelIdle?.();
69
+ debounce = setTimeout(() => (cancelIdle = idle(scan)), 120);
70
+ });
71
+ observer.observe(r, {
72
+ subtree: true,
73
+ childList: true,
74
+ characterData: true,
75
+ attributes: true,
76
+ attributeFilter: WATCHED,
77
+ });
78
+ return () => {
79
+ observer.disconnect();
80
+ clearTimeout(debounce);
81
+ cancelIdle?.();
82
+ };
83
+ }, [locator, root]);
84
+ return index;
85
+ }
86
+ //# sourceMappingURL=targets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"targets.js","sourceRoot":"","sources":["../src/targets.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,131 @@
1
+ /** Public types of the editor UI. */
2
+ import type { ComponentType } from 'react';
3
+ import type { DocRef, EditorSession, MediaLibrary, Renderer, SchemaField, SessionSnapshot, Target, TargetKind } from '@payload-onpage/core';
4
+ import type { MessageKey, Translate } from './messages.ts';
5
+ export interface EditingState {
6
+ /** refKey of the document */
7
+ key: string;
8
+ path: string;
9
+ /** Editor id from the registry ("text", "richText", …). */
10
+ editor: string;
11
+ point?: {
12
+ x: number;
13
+ y: number;
14
+ };
15
+ /** The element that was clicked (several elements may show one field). */
16
+ el?: Element;
17
+ }
18
+ export type Panel = {
19
+ kind: 'settings';
20
+ key: string;
21
+ path: string;
22
+ title: string;
23
+ } | {
24
+ kind: 'blocks';
25
+ key: string;
26
+ listPath: string;
27
+ index: number;
28
+ } | null;
29
+ export interface Selection {
30
+ key: string;
31
+ path: string;
32
+ }
33
+ /** Props every field editor receives. */
34
+ export interface FieldEditorProps {
35
+ docKey: string;
36
+ path: string;
37
+ field: SchemaField;
38
+ /** The element showing the field (re-resolved after each refresh). */
39
+ element: () => Element | null;
40
+ value: unknown;
41
+ /** Commit a value (one undo step, autosaved). */
42
+ commit: (value: unknown) => void;
43
+ /** Live preview while editing (no history entry, not saved). */
44
+ preview: (value: unknown) => void;
45
+ /** Close the editor (after commit, or to cancel: transient previews are reverted). */
46
+ close: () => void;
47
+ /** Leave a stand-in showing the new content until the page re-renders (refresh mode). */
48
+ ghost: (g: {
49
+ html: string;
50
+ className?: string;
51
+ style?: Partial<CSSStyleDeclaration>;
52
+ }) => void;
53
+ /** True when the page renders this document from editor state (instant); false = server refresh. */
54
+ live: boolean;
55
+ point?: {
56
+ x: number;
57
+ y: number;
58
+ };
59
+ /** UI strings. */
60
+ t: Translate;
61
+ }
62
+ /**
63
+ * A field editor (the FieldEditor port). `inline` editors replace the element
64
+ * in place (text, rich text); `popover` editors open beside it or as a dialog
65
+ * (select, date, relationship, image…). Register your own by id and refer to it
66
+ * from a field with `admin: { custom: { onpage: { editor: 'my-id' } } }`, or by
67
+ * field type ("json", "code"…) to replace or add a default.
68
+ */
69
+ export interface FieldEditorDef {
70
+ mode: 'inline' | 'popover';
71
+ component: ComponentType<FieldEditorProps>;
72
+ }
73
+ export type EditorRegistry = Record<string, FieldEditorDef>;
74
+ /** Navigation targets. `exit` and `mode` are endpoints the editor POSTs to. */
75
+ export interface Links {
76
+ exit: string;
77
+ admin?: (ref: DocRef) => string;
78
+ mode?: (mode: 'edit' | 'view') => string;
79
+ }
80
+ export interface TargetIndex {
81
+ all: Target[];
82
+ /** Targets whose element is exactly `el`. */
83
+ at: (el: Element) => Target[];
84
+ find: (key: string, path: string, kinds?: TargetKind[]) => Target | undefined;
85
+ }
86
+ /** What extensions and field editors can use from the editor (`useOnPage()`). */
87
+ export interface OnPageContextValue {
88
+ session: EditorSession;
89
+ snap: SessionSnapshot;
90
+ renderer: Renderer;
91
+ media?: MediaLibrary;
92
+ editors: EditorRegistry;
93
+ targets: TargetIndex;
94
+ links: Links;
95
+ t: Translate;
96
+ panel: Panel;
97
+ setPanel: (p: Panel) => void;
98
+ editing: EditingState | null;
99
+ setEditing: (e: EditingState | null) => void;
100
+ selected: Selection | null;
101
+ setSelected: (s: Selection | null) => void;
102
+ /** Documents shown on the current page (the session may hold others from earlier pages). */
103
+ docKeys: string[];
104
+ /** Is this document rendered from session state (client mode)? */
105
+ isLive: (key: string) => boolean;
106
+ /** Client-mode documents register here (they don't need a server refresh). */
107
+ registerClientDoc: (key: string) => () => void;
108
+ /** The page is re-rendering after a save (refresh mode). */
109
+ rendering: boolean;
110
+ /** Open the right editor for a field (registry resolution); false if none applies. */
111
+ edit: (key: string, path: string, opts?: {
112
+ point?: {
113
+ x: number;
114
+ y: number;
115
+ };
116
+ kind?: TargetKind;
117
+ anchor?: Element;
118
+ }) => boolean;
119
+ /** Re-render documents after an operation outside autosave (publish, reload). */
120
+ rerender: (refs: DocRef[]) => Promise<void>;
121
+ /** Show a short message in the dock. */
122
+ notify: (key: MessageKey | (string & {}), vars?: Record<string, string | number>, bad?: boolean) => void;
123
+ addGhost: (g: {
124
+ key: string;
125
+ path: string;
126
+ html: string;
127
+ className?: string;
128
+ style?: Partial<CSSStyleDeclaration>;
129
+ }) => void;
130
+ }
131
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,3 @@
1
+ /** Choose a block type to insert at a position in a blocks field. */
2
+ export declare function BlockPicker(): import("react").JSX.Element | null;
3
+ //# sourceMappingURL=BlockPicker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BlockPicker.d.ts","sourceRoot":"","sources":["../../src/ui/BlockPicker.tsx"],"names":[],"mappings":"AAMA,qEAAqE;AACrE,wBAAgB,WAAW,uCAyC1B"}
@@ -0,0 +1,27 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { fieldAtPath, insertItem, newBlockRow, updateArray } from '@payload-onpage/core';
4
+ import { useOnPage } from "../context.js";
5
+ import { Dialog } from "./Dialog.js";
6
+ /** Choose a block type to insert at a position in a blocks field. */
7
+ export function BlockPicker() {
8
+ const { panel, setPanel, snap, session, setSelected, t } = useOnPage();
9
+ if (panel?.kind !== 'blocks')
10
+ return null;
11
+ const state = snap.docs[panel.key];
12
+ if (!state?.schema || !state.doc)
13
+ return null;
14
+ const { key, listPath, index } = panel;
15
+ const field = fieldAtPath(state.schema.fields, state.doc, listPath)?.field;
16
+ const blocks = field?.type === 'blocks' ? (field.blocks ?? []) : [];
17
+ const choose = (slug) => {
18
+ const block = blocks.find((b) => b.slug === slug);
19
+ if (!block)
20
+ return;
21
+ session.apply(key, (d) => updateArray(d, listPath, (a) => insertItem(a, index, newBlockRow(block))));
22
+ setPanel(null);
23
+ setSelected({ key, path: `${listPath}.${index}` });
24
+ };
25
+ return (_jsx(Dialog, { title: t('blocks.title'), onClose: () => setPanel(null), t: t, width: 640, testId: "onpage-block-picker", children: _jsx("div", { className: "po-blocks", children: blocks.map((b) => (_jsx("button", { type: "button", onClick: () => choose(b.slug), "data-testid": `onpage-block-${b.slug}`, children: b.labels.singular }, b.slug))) }) }));
26
+ }
27
+ //# sourceMappingURL=BlockPicker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BlockPicker.js","sourceRoot":"","sources":["../../src/ui/BlockPicker.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAA;AACxF,OAAO,EAAE,SAAS,EAAE,MAAM,eAAgB,CAAA;AAC1C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAc,CAAA;AAErC,qEAAqE;AACrE,MAAM,UAAU,WAAW;IACzB,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC,EAAE,GAAG,SAAS,EAAE,CAAA;IACtE,IAAI,KAAK,EAAE,IAAI,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAA;IACzC,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IAClC,IAAI,CAAC,KAAK,EAAE,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG;QAAE,OAAO,IAAI,CAAA;IAC7C,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,KAAK,CAAA;IACtC,MAAM,KAAK,GAAG,WAAW,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,GAAG,EAAE,QAAQ,CAAC,EAAE,KAAK,CAAA;IAC1E,MAAM,MAAM,GAAG,KAAK,EAAE,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;IAEnE,MAAM,MAAM,GAAG,CAAC,IAAY,EAAE,EAAE;QAC9B,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAA;QACjD,IAAI,CAAC,KAAK;YAAE,OAAM;QAClB,OAAO,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,CACvB,WAAW,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,KAAK,EAAE,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,CAC1E,CAAA;QACD,QAAQ,CAAC,IAAI,CAAC,CAAA;QACd,WAAW,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,QAAQ,IAAI,KAAK,EAAE,EAAE,CAAC,CAAA;IACpD,CAAC,CAAA;IAED,OAAO,CACL,KAAC,MAAM,IACL,KAAK,EAAE,CAAC,CAAC,cAAc,CAAC,EACxB,OAAO,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,EAC7B,CAAC,EAAE,CAAC,EACJ,KAAK,EAAE,GAAG,EACV,MAAM,EAAC,qBAAqB,YAE5B,cAAK,SAAS,EAAC,WAAW,YACvB,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CACjB,iBAEE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,iBAChB,gBAAgB,CAAC,CAAC,IAAI,EAAE,YAEpC,CAAC,CAAC,MAAM,CAAC,QAAQ,IALb,CAAC,CAAC,IAAI,CAMJ,CACV,CAAC,GACE,GACC,CACV,CAAA;AACH,CAAC"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * The one dialog primitive: labelled, focus moves in on open and back on
3
+ * close, Tab stays inside (modal), Escape closes. `variant="popover"` is
4
+ * anchored next to an element and closes on an outside click.
5
+ */
6
+ import { type ReactNode } from 'react';
7
+ import type { Translate } from '../messages.ts';
8
+ export interface DialogProps {
9
+ title: string;
10
+ onClose: () => void;
11
+ children: ReactNode;
12
+ t: Translate;
13
+ /** modal: centered with a backdrop. panel: fixed side panel. popover: next to `anchor`. */
14
+ variant?: 'modal' | 'panel' | 'popover';
15
+ anchor?: () => Element | null;
16
+ width?: number;
17
+ testId?: string;
18
+ /** Extra header content (search box, actions). */
19
+ actions?: ReactNode;
20
+ }
21
+ export declare function Dialog({ title, onClose, children, t, variant, anchor, width, testId, actions, }: DialogProps): import("react").JSX.Element;
22
+ //# sourceMappingURL=Dialog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Dialog.d.ts","sourceRoot":"","sources":["../../src/ui/Dialog.tsx"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,OAAO,EAA0D,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAG9F,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAE/C,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB,QAAQ,EAAE,SAAS,CAAA;IACnB,CAAC,EAAE,SAAS,CAAA;IACZ,2FAA2F;IAC3F,OAAO,CAAC,EAAE,OAAO,GAAG,OAAO,GAAG,SAAS,CAAA;IACvC,MAAM,CAAC,EAAE,MAAM,OAAO,GAAG,IAAI,CAAA;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,kDAAkD;IAClD,OAAO,CAAC,EAAE,SAAS,CAAA;CACpB;AAED,wBAAgB,MAAM,CAAC,EACrB,KAAK,EACL,OAAO,EACP,QAAQ,EACR,CAAC,EACD,OAAiB,EACjB,MAAM,EACN,KAAK,EACL,MAAM,EACN,OAAO,GACR,EAAE,WAAW,+BA0Hb"}