@paulpaulstudio/strapi-render 0.4.1 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -4,29 +4,25 @@
4
4
  var react = require('react');
5
5
  var jsxRuntime = require('react/jsx-runtime');
6
6
 
7
+ var EDIT_SESSION_KEY = "__pp_edit_session";
7
8
  var noop = () => {
8
9
  };
9
10
  var EditModeContext = react.createContext({
10
11
  enabled: false,
11
12
  token: null,
12
- changes: /* @__PURE__ */ new Map(),
13
- isDirty: false,
14
- isCommitting: false,
15
- lastCommitError: null,
16
- setChange: noop,
17
- clearChange: noop,
18
- commit: noop,
13
+ publishChange: noop,
19
14
  openMediaPicker: noop,
20
15
  onMediaPicked: () => noop
21
16
  });
17
+ var ContentScopeContext = react.createContext(null);
22
18
  function useStrapiEditMode() {
23
19
  return react.useContext(EditModeContext);
24
20
  }
21
+ function useContentScope() {
22
+ return react.useContext(ContentScopeContext);
23
+ }
25
24
  function StrapiEditModeProvider({ children, enabled: enabledOverride }) {
26
25
  const [urlState, setUrlState] = react.useState({ enabled: false, token: null });
27
- const [changes, setChanges] = react.useState(/* @__PURE__ */ new Map());
28
- const [isCommitting, setIsCommitting] = react.useState(false);
29
- const [lastCommitError, setLastCommitError] = react.useState(null);
30
26
  const mediaPickListeners = react.useRef(/* @__PURE__ */ new Map());
31
27
  react.useEffect(() => {
32
28
  if (typeof window === "undefined") return;
@@ -36,9 +32,15 @@ function StrapiEditModeProvider({ children, enabled: enabledOverride }) {
36
32
  }
37
33
  try {
38
34
  const params = new URLSearchParams(window.location.search);
39
- const edit = params.get("__pp_edit") === "1";
40
- const token = params.get("__pp_token");
41
- setUrlState({ enabled: edit, token });
35
+ const urlEdit = params.get("__pp_edit") === "1";
36
+ const sessionFlag = window.sessionStorage.getItem(EDIT_SESSION_KEY) === "1";
37
+ const enabled = urlEdit || sessionFlag;
38
+ if (urlEdit) {
39
+ window.sessionStorage.setItem(EDIT_SESSION_KEY, "1");
40
+ }
41
+ const token = params.get("__pp_token") ?? window.sessionStorage.getItem("__pp_edit_token");
42
+ if (token) window.sessionStorage.setItem("__pp_edit_token", token);
43
+ setUrlState({ enabled, token });
42
44
  } catch {
43
45
  }
44
46
  }, [enabledOverride]);
@@ -65,14 +67,9 @@ function StrapiEditModeProvider({ children, enabled: enabledOverride }) {
65
67
  window.location.reload();
66
68
  return;
67
69
  }
68
- if (d.type === "pp:edit:commit-result") {
69
- setIsCommitting(false);
70
- if (d.ok) {
71
- setChanges(/* @__PURE__ */ new Map());
72
- setLastCommitError(null);
73
- } else {
74
- setLastCommitError(d.error || "Speichern fehlgeschlagen");
75
- }
70
+ if (d.type === "pp:edit:exit") {
71
+ window.sessionStorage.removeItem(EDIT_SESSION_KEY);
72
+ window.location.reload();
76
73
  return;
77
74
  }
78
75
  if (d.type === "pp:edit:media-picked") {
@@ -87,58 +84,12 @@ function StrapiEditModeProvider({ children, enabled: enabledOverride }) {
87
84
  window.addEventListener("message", onMsg);
88
85
  return () => window.removeEventListener("message", onMsg);
89
86
  }, [urlState.enabled]);
90
- const setChange = react.useCallback((path, fieldType, value2) => {
91
- setChanges((prev) => {
92
- const next = new Map(prev);
93
- next.set(path, { path, fieldType, value: value2 });
94
- return next;
95
- });
96
- setLastCommitError(null);
97
- if (typeof window !== "undefined" && window.parent !== window) {
98
- try {
99
- window.parent.postMessage({ type: "pp:edit:change", path, fieldType, value: value2 }, "*");
100
- } catch {
101
- }
102
- }
103
- }, []);
104
- const clearChange = react.useCallback((path) => {
105
- setChanges((prev) => {
106
- const next = new Map(prev);
107
- next.delete(path);
108
- return next;
109
- });
87
+ const publishChange = react.useCallback((_path, _fieldType, _value) => {
110
88
  }, []);
111
- const commit = react.useCallback(() => {
112
- if (changes.size === 0) return;
113
- if (typeof window === "undefined" || window.parent === window) return;
114
- setIsCommitting(true);
115
- setLastCommitError(null);
116
- try {
117
- window.parent.postMessage(
118
- {
119
- type: "pp:edit:commit-request",
120
- changes: Array.from(changes.values())
121
- },
122
- "*"
123
- );
124
- } catch (err) {
125
- setIsCommitting(false);
126
- setLastCommitError(err instanceof Error ? err.message : String(err));
127
- }
128
- }, [changes]);
129
89
  const openMediaPicker = react.useCallback((path, multiple, allowedTypes, currentValue) => {
130
90
  if (typeof window === "undefined" || window.parent === window) return;
131
91
  try {
132
- window.parent.postMessage(
133
- {
134
- type: "pp:edit:open-media-picker",
135
- path,
136
- multiple,
137
- allowedTypes,
138
- currentValue
139
- },
140
- "*"
141
- );
92
+ window.parent.postMessage({ type: "pp:edit:open-media-picker", path, multiple, allowedTypes, currentValue }, "*");
142
93
  } catch {
143
94
  }
144
95
  }, []);
@@ -151,98 +102,34 @@ function StrapiEditModeProvider({ children, enabled: enabledOverride }) {
151
102
  const value = react.useMemo(() => ({
152
103
  enabled: urlState.enabled,
153
104
  token: urlState.token,
154
- changes,
155
- isDirty: changes.size > 0,
156
- isCommitting,
157
- lastCommitError,
158
- setChange,
159
- clearChange,
160
- commit,
105
+ publishChange,
161
106
  openMediaPicker,
162
107
  onMediaPicked
163
- }), [urlState, changes, isCommitting, lastCommitError, setChange, clearChange, commit, openMediaPicker, onMediaPicked]);
164
- return /* @__PURE__ */ jsxRuntime.jsxs(EditModeContext.Provider, { value, children: [
165
- children,
166
- urlState.enabled && /* @__PURE__ */ jsxRuntime.jsx(SaveBar, {})
167
- ] });
108
+ }), [urlState, publishChange, openMediaPicker, onMediaPicked]);
109
+ return /* @__PURE__ */ jsxRuntime.jsx(EditModeContext.Provider, { value, children });
168
110
  }
169
- function SaveBar() {
170
- const { isDirty, isCommitting, lastCommitError, changes, commit } = useStrapiEditMode();
171
- if (!isDirty && !lastCommitError && !isCommitting) return null;
172
- return /* @__PURE__ */ jsxRuntime.jsxs(
173
- "div",
174
- {
175
- style: {
176
- position: "fixed",
177
- bottom: 24,
178
- right: 24,
179
- zIndex: 999999,
180
- fontFamily: "system-ui, -apple-system, sans-serif"
181
- },
182
- children: [
183
- lastCommitError && /* @__PURE__ */ jsxRuntime.jsx("div", { style: {
184
- background: "#FA501E",
185
- color: "#000",
186
- padding: "8px 14px",
187
- marginBottom: 8,
188
- fontSize: 13,
189
- fontWeight: 600,
190
- maxWidth: 320
191
- }, children: lastCommitError }),
192
- /* @__PURE__ */ jsxRuntime.jsx(
193
- "button",
194
- {
195
- type: "button",
196
- onClick: commit,
197
- disabled: isCommitting || !isDirty,
198
- style: {
199
- background: isCommitting ? "#666" : "#000",
200
- color: "#EBC6DF",
201
- border: "2px solid #000",
202
- padding: "14px 22px",
203
- fontSize: 13,
204
- fontWeight: 700,
205
- textTransform: "uppercase",
206
- letterSpacing: "0.2em",
207
- cursor: isCommitting ? "wait" : "pointer",
208
- display: "flex",
209
- alignItems: "center",
210
- gap: 10,
211
- boxShadow: "0 4px 12px rgba(0,0,0,0.3)",
212
- transition: "background 0.15s ease"
213
- },
214
- onMouseEnter: (e) => {
215
- if (!isCommitting && isDirty) {
216
- e.currentTarget.style.background = "#FA501E";
217
- e.currentTarget.style.color = "#000";
218
- }
219
- },
220
- onMouseLeave: (e) => {
221
- if (!isCommitting && isDirty) {
222
- e.currentTarget.style.background = "#000";
223
- e.currentTarget.style.color = "#EBC6DF";
224
- }
225
- },
226
- children: isCommitting ? /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Speichere\u2026" }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
227
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: {
228
- background: "#FA501E",
229
- color: "#000",
230
- width: 22,
231
- height: 22,
232
- borderRadius: "50%",
233
- display: "inline-flex",
234
- alignItems: "center",
235
- justifyContent: "center",
236
- fontSize: 11,
237
- fontWeight: 700
238
- }, children: changes.size }),
239
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Speichern" })
240
- ] })
241
- }
242
- )
243
- ]
244
- }
245
- );
111
+ function StrapiContent({ uid, kind, documentId, children }) {
112
+ const scope = react.useMemo(() => ({ uid, kind, documentId }), [uid, kind, documentId]);
113
+ return /* @__PURE__ */ jsxRuntime.jsx(ContentScopeContext.Provider, { value: scope, children });
114
+ }
115
+ function postEditChange(scope, path, fieldType, value) {
116
+ if (typeof window === "undefined" || window.parent === window) return;
117
+ if (!scope) {
118
+ console.warn("[strapi-render] Feld ohne <StrapiContent>-Scope \u2014 Save wird nicht funktionieren. Wickle deine Seite mit <StrapiContent uid='...' kind='...'> ein.");
119
+ return;
120
+ }
121
+ try {
122
+ window.parent.postMessage({
123
+ type: "pp:edit:change",
124
+ uid: scope.uid,
125
+ kind: scope.kind,
126
+ documentId: scope.documentId,
127
+ path,
128
+ fieldType,
129
+ value
130
+ }, "*");
131
+ } catch {
132
+ }
246
133
  }
247
134
  var EDITABLE_TYPES = /* @__PURE__ */ new Set([
248
135
  "text",
@@ -254,6 +141,7 @@ var EDITABLE_TYPES = /* @__PURE__ */ new Set([
254
141
  var HOVER_OUTLINE_COLOR = "#FA501E";
255
142
  function StrapiField({ path, type, value, children, className }) {
256
143
  const ctx = useStrapiEditMode();
144
+ const scope = useContentScope();
257
145
  const ref = react.useRef(null);
258
146
  const initialText = react.useRef(void 0);
259
147
  react.useEffect(() => {
@@ -309,7 +197,7 @@ function StrapiField({ path, type, value, children, className }) {
309
197
  const original = typeof value === "string" ? value : initialText.current ?? "";
310
198
  if (newText !== original) {
311
199
  const newValue = type === "number" ? newText === "" ? null : Number(newText) : newText;
312
- ctx.setChange(path, type, newValue);
200
+ postEditChange(scope, path, type, newValue);
313
201
  }
314
202
  };
315
203
  props.onKeyDown = (e) => {
@@ -349,6 +237,7 @@ function resolveUrl(value, baseUrl) {
349
237
  var HOVER_OUTLINE_COLOR2 = "#FA501E";
350
238
  function StrapiImage({ path, value, baseUrl, alt, className, style, loading = "lazy", fallback, allowedTypes, multiple = false, width, height, draggable }) {
351
239
  const ctx = useStrapiEditMode();
240
+ const scope = useContentScope();
352
241
  const containerRef = react.useRef(null);
353
242
  const imgRef = react.useRef(null);
354
243
  const handleClick = (e) => {
@@ -356,7 +245,7 @@ function StrapiImage({ path, value, baseUrl, alt, className, style, loading = "l
356
245
  e.preventDefault();
357
246
  e.stopPropagation();
358
247
  const unsubscribe = ctx.onMediaPicked(path, (picked) => {
359
- ctx.setChange(path, "media", picked);
248
+ postEditChange(scope, path, "media", picked);
360
249
  unsubscribe();
361
250
  if (imgRef.current && picked && typeof picked === "object" && !Array.isArray(picked) && "url" in picked) {
362
251
  imgRef.current.src = resolveUrl(picked, baseUrl);
@@ -421,7 +310,16 @@ function StrapiImage({ path, value, baseUrl, alt, className, style, loading = "l
421
310
  ref: containerRef,
422
311
  "data-pp-edit": path,
423
312
  "data-pp-type": "media",
424
- style: { position: "relative", display: "inline-block", outline: "1px dashed transparent", outlineOffset: 2, transition: "outline-color 0.15s ease" },
313
+ style: {
314
+ position: "relative",
315
+ display: "block",
316
+ // Wrapper ist block-level damit das innere img mit className w-full / h-[60vh] etc.
317
+ // die korrekte Größe ausfüllt. Im Read-Mode (kein Wrapper) hatte das img direkt
318
+ // diese Größe — beim Wrap mit inline-block-span schrumpfte das.
319
+ outline: "1px dashed transparent",
320
+ outlineOffset: 2,
321
+ transition: "outline-color 0.15s ease"
322
+ },
425
323
  onMouseEnter: (e) => {
426
324
  e.currentTarget.style.outlineColor = HOVER_OUTLINE_COLOR2;
427
325
  const btn = e.currentTarget.querySelector("[data-pp-edit-btn]");
@@ -578,6 +476,7 @@ function parseBlocksFromDOM(root) {
578
476
  }
579
477
  function StrapiBlocks({ path, value, className }) {
580
478
  const ctx = useStrapiEditMode();
479
+ const scope = useContentScope();
581
480
  const containerRef = react.useRef(null);
582
481
  const initialValue = react.useRef(null);
583
482
  const blocks = Array.isArray(value) ? value : [];
@@ -618,7 +517,7 @@ function StrapiBlocks({ path, value, className }) {
618
517
  const before = JSON.stringify(initialValue.current);
619
518
  const after = JSON.stringify(parsed);
620
519
  if (before !== after) {
621
- ctx.setChange(path, "blocks", parsed);
520
+ postEditChange(scope, path, "blocks", parsed);
622
521
  }
623
522
  e.currentTarget.style.outlineColor = "transparent";
624
523
  e.currentTarget.style.outlineStyle = "dashed";
@@ -630,6 +529,7 @@ function StrapiBlocks({ path, value, className }) {
630
529
  var HOVER_OUTLINE2 = "#FA501E";
631
530
  function StrapiMarkdownField({ path, value, render, className, minRows = 12 }) {
632
531
  const ctx = useStrapiEditMode();
532
+ const scope = useContentScope();
633
533
  const [draft, setDraft] = react.useState(value ?? "");
634
534
  const initial = react.useRef(value ?? "");
635
535
  react.useEffect(() => {
@@ -647,7 +547,7 @@ function StrapiMarkdownField({ path, value, render, className, minRows = 12 }) {
647
547
  onChange: (e) => setDraft(e.target.value),
648
548
  onBlur: () => {
649
549
  if (draft !== initial.current) {
650
- ctx.setChange(path, "richText", draft);
550
+ postEditChange(scope, path, "richText", draft);
651
551
  }
652
552
  },
653
553
  onFocus: (e) => {
@@ -696,12 +596,14 @@ function StrapiMarkdownField({ path, value, render, className, minRows = 12 }) {
696
596
  }
697
597
 
698
598
  exports.StrapiBlocks = StrapiBlocks;
599
+ exports.StrapiContent = StrapiContent;
699
600
  exports.StrapiEditModeProvider = StrapiEditModeProvider;
700
601
  exports.StrapiField = StrapiField;
701
602
  exports.StrapiImage = StrapiImage;
702
603
  exports.StrapiList = StrapiList;
703
604
  exports.StrapiMarkdownField = StrapiMarkdownField;
704
605
  exports.StrapiText = StrapiText;
606
+ exports.useContentScope = useContentScope;
705
607
  exports.useStrapiEditMode = useStrapiEditMode;
706
608
  //# sourceMappingURL=index.cjs.map
707
609
  //# sourceMappingURL=index.cjs.map