@paulpaulstudio/strapi-render 0.1.0 → 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.
package/dist/index.js CHANGED
@@ -1,12 +1,31 @@
1
- import { createContext, useContext, useState, useEffect, useMemo, useCallback, Children, isValidElement, cloneElement, createElement, Fragment as Fragment$1 } from 'react';
2
- import { jsx, Fragment } from 'react/jsx-runtime';
1
+ "use client";
2
+ import { createContext, useContext, useState, useRef, useEffect, useCallback, useMemo, Children, isValidElement, cloneElement, createElement, Fragment as Fragment$1 } from 'react';
3
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
3
4
 
4
- var EditModeContext = createContext({ enabled: false, token: null });
5
+ var noop = () => {
6
+ };
7
+ var EditModeContext = createContext({
8
+ enabled: false,
9
+ token: null,
10
+ changes: /* @__PURE__ */ new Map(),
11
+ isDirty: false,
12
+ isCommitting: false,
13
+ lastCommitError: null,
14
+ setChange: noop,
15
+ clearChange: noop,
16
+ commit: noop,
17
+ openMediaPicker: noop,
18
+ onMediaPicked: () => noop
19
+ });
5
20
  function useStrapiEditMode() {
6
21
  return useContext(EditModeContext);
7
22
  }
8
23
  function StrapiEditModeProvider({ children, enabled: enabledOverride }) {
9
24
  const [urlState, setUrlState] = useState({ enabled: false, token: null });
25
+ const [changes, setChanges] = useState(/* @__PURE__ */ new Map());
26
+ const [isCommitting, setIsCommitting] = useState(false);
27
+ const [lastCommitError, setLastCommitError] = useState(null);
28
+ const mediaPickListeners = useRef(/* @__PURE__ */ new Map());
10
29
  useEffect(() => {
11
30
  if (typeof window === "undefined") return;
12
31
  if (enabledOverride !== void 0) {
@@ -30,10 +49,7 @@ function StrapiEditModeProvider({ children, enabled: enabledOverride }) {
30
49
  if (!urlState.enabled) return;
31
50
  if (window.parent === window) return;
32
51
  try {
33
- window.parent.postMessage(
34
- { type: "pp:edit:ready", href: window.location.href },
35
- "*"
36
- );
52
+ window.parent.postMessage({ type: "pp:edit:ready", href: window.location.href }, "*");
37
53
  } catch {
38
54
  }
39
55
  }, [urlState.enabled]);
@@ -42,91 +58,278 @@ function StrapiEditModeProvider({ children, enabled: enabledOverride }) {
42
58
  if (!urlState.enabled) return;
43
59
  function onMsg(e) {
44
60
  const d = e.data;
45
- if (d && typeof d === "object" && d.type === "pp:edit:reload") {
61
+ if (!d || typeof d !== "object") return;
62
+ if (d.type === "pp:edit:reload") {
46
63
  window.location.reload();
64
+ return;
65
+ }
66
+ if (d.type === "pp:edit:commit-result") {
67
+ setIsCommitting(false);
68
+ if (d.ok) {
69
+ setChanges(/* @__PURE__ */ new Map());
70
+ setLastCommitError(null);
71
+ } else {
72
+ setLastCommitError(d.error || "Speichern fehlgeschlagen");
73
+ }
74
+ return;
75
+ }
76
+ if (d.type === "pp:edit:media-picked") {
77
+ const listener = mediaPickListeners.current.get(d.path);
78
+ if (listener) {
79
+ listener(d.value);
80
+ mediaPickListeners.current.delete(d.path);
81
+ }
82
+ return;
47
83
  }
48
84
  }
49
85
  window.addEventListener("message", onMsg);
50
86
  return () => window.removeEventListener("message", onMsg);
51
87
  }, [urlState.enabled]);
52
- const value = useMemo(() => urlState, [urlState]);
53
- return /* @__PURE__ */ jsx(EditModeContext.Provider, { value, children });
54
- }
55
- var EDIT_ATTR_STYLE = {
56
- outline: "1px dashed transparent",
57
- outlineOffset: "2px",
58
- cursor: "pointer",
59
- transition: "outline-color 0.15s ease"
60
- };
61
- var EDIT_HOVER_STYLE = {
62
- outlineColor: "#FA501E"
63
- };
64
- function StrapiField({ path, type, value, children, className }) {
65
- const { enabled } = useStrapiEditMode();
66
- const onClick = useCallback(
67
- (e) => {
68
- e.preventDefault();
69
- e.stopPropagation();
70
- if (typeof window === "undefined" || window.parent === window) return;
71
- const target = e.currentTarget;
72
- const rect = target.getBoundingClientRect();
88
+ const setChange = useCallback((path, fieldType, value2) => {
89
+ setChanges((prev) => {
90
+ const next = new Map(prev);
91
+ next.set(path, { path, fieldType, value: value2 });
92
+ return next;
93
+ });
94
+ setLastCommitError(null);
95
+ if (typeof window !== "undefined" && window.parent !== window) {
73
96
  try {
74
- window.parent.postMessage(
75
- {
76
- type: "pp:edit:click",
77
- path,
78
- fieldType: type,
79
- rect: {
80
- top: rect.top,
81
- left: rect.left,
82
- width: rect.width,
83
- height: rect.height
84
- },
85
- currentValue: value
86
- },
87
- "*"
88
- );
97
+ window.parent.postMessage({ type: "pp:edit:change", path, fieldType, value: value2 }, "*");
89
98
  } catch {
90
99
  }
91
- },
92
- [path, type, value]
100
+ }
101
+ }, []);
102
+ const clearChange = useCallback((path) => {
103
+ setChanges((prev) => {
104
+ const next = new Map(prev);
105
+ next.delete(path);
106
+ return next;
107
+ });
108
+ }, []);
109
+ const commit = useCallback(() => {
110
+ if (changes.size === 0) return;
111
+ if (typeof window === "undefined" || window.parent === window) return;
112
+ setIsCommitting(true);
113
+ setLastCommitError(null);
114
+ try {
115
+ window.parent.postMessage(
116
+ {
117
+ type: "pp:edit:commit-request",
118
+ changes: Array.from(changes.values())
119
+ },
120
+ "*"
121
+ );
122
+ } catch (err) {
123
+ setIsCommitting(false);
124
+ setLastCommitError(err instanceof Error ? err.message : String(err));
125
+ }
126
+ }, [changes]);
127
+ const openMediaPicker = useCallback((path, multiple, allowedTypes, currentValue) => {
128
+ if (typeof window === "undefined" || window.parent === window) return;
129
+ try {
130
+ window.parent.postMessage(
131
+ {
132
+ type: "pp:edit:open-media-picker",
133
+ path,
134
+ multiple,
135
+ allowedTypes,
136
+ currentValue
137
+ },
138
+ "*"
139
+ );
140
+ } catch {
141
+ }
142
+ }, []);
143
+ const onMediaPicked = useCallback((path, listener) => {
144
+ mediaPickListeners.current.set(path, listener);
145
+ return () => {
146
+ mediaPickListeners.current.delete(path);
147
+ };
148
+ }, []);
149
+ const value = useMemo(() => ({
150
+ enabled: urlState.enabled,
151
+ token: urlState.token,
152
+ changes,
153
+ isDirty: changes.size > 0,
154
+ isCommitting,
155
+ lastCommitError,
156
+ setChange,
157
+ clearChange,
158
+ commit,
159
+ openMediaPicker,
160
+ onMediaPicked
161
+ }), [urlState, changes, isCommitting, lastCommitError, setChange, clearChange, commit, openMediaPicker, onMediaPicked]);
162
+ return /* @__PURE__ */ jsxs(EditModeContext.Provider, { value, children: [
163
+ children,
164
+ urlState.enabled && /* @__PURE__ */ jsx(SaveBar, {})
165
+ ] });
166
+ }
167
+ function SaveBar() {
168
+ const { isDirty, isCommitting, lastCommitError, changes, commit } = useStrapiEditMode();
169
+ if (!isDirty && !lastCommitError && !isCommitting) return null;
170
+ return /* @__PURE__ */ jsxs(
171
+ "div",
172
+ {
173
+ style: {
174
+ position: "fixed",
175
+ bottom: 24,
176
+ right: 24,
177
+ zIndex: 999999,
178
+ fontFamily: "system-ui, -apple-system, sans-serif"
179
+ },
180
+ children: [
181
+ lastCommitError && /* @__PURE__ */ jsx("div", { style: {
182
+ background: "#FA501E",
183
+ color: "#000",
184
+ padding: "8px 14px",
185
+ marginBottom: 8,
186
+ fontSize: 13,
187
+ fontWeight: 600,
188
+ maxWidth: 320
189
+ }, children: lastCommitError }),
190
+ /* @__PURE__ */ jsx(
191
+ "button",
192
+ {
193
+ type: "button",
194
+ onClick: commit,
195
+ disabled: isCommitting || !isDirty,
196
+ style: {
197
+ background: isCommitting ? "#666" : "#000",
198
+ color: "#EBC6DF",
199
+ border: "2px solid #000",
200
+ padding: "14px 22px",
201
+ fontSize: 13,
202
+ fontWeight: 700,
203
+ textTransform: "uppercase",
204
+ letterSpacing: "0.2em",
205
+ cursor: isCommitting ? "wait" : "pointer",
206
+ display: "flex",
207
+ alignItems: "center",
208
+ gap: 10,
209
+ boxShadow: "0 4px 12px rgba(0,0,0,0.3)",
210
+ transition: "background 0.15s ease"
211
+ },
212
+ onMouseEnter: (e) => {
213
+ if (!isCommitting && isDirty) {
214
+ e.currentTarget.style.background = "#FA501E";
215
+ e.currentTarget.style.color = "#000";
216
+ }
217
+ },
218
+ onMouseLeave: (e) => {
219
+ if (!isCommitting && isDirty) {
220
+ e.currentTarget.style.background = "#000";
221
+ e.currentTarget.style.color = "#EBC6DF";
222
+ }
223
+ },
224
+ children: isCommitting ? /* @__PURE__ */ jsx("span", { children: "Speichere\u2026" }) : /* @__PURE__ */ jsxs(Fragment, { children: [
225
+ /* @__PURE__ */ jsx("span", { style: {
226
+ background: "#FA501E",
227
+ color: "#000",
228
+ width: 22,
229
+ height: 22,
230
+ borderRadius: "50%",
231
+ display: "inline-flex",
232
+ alignItems: "center",
233
+ justifyContent: "center",
234
+ fontSize: 11,
235
+ fontWeight: 700
236
+ }, children: changes.size }),
237
+ /* @__PURE__ */ jsx("span", { children: "Speichern" })
238
+ ] })
239
+ }
240
+ )
241
+ ]
242
+ }
93
243
  );
94
- if (!enabled) {
244
+ }
245
+ var EDITABLE_TYPES = /* @__PURE__ */ new Set([
246
+ "text",
247
+ "textarea",
248
+ "richText",
249
+ "email",
250
+ "number"
251
+ ]);
252
+ var HOVER_OUTLINE_COLOR = "#FA501E";
253
+ function StrapiField({ path, type, value, children, className }) {
254
+ const ctx = useStrapiEditMode();
255
+ const ref = useRef(null);
256
+ const initialText = useRef(void 0);
257
+ useEffect(() => {
258
+ if (!ctx.enabled) return;
259
+ if (!EDITABLE_TYPES.has(type)) return;
260
+ if (initialText.current === void 0 && ref.current) {
261
+ initialText.current = ref.current.textContent ?? "";
262
+ }
263
+ }, [ctx.enabled, type]);
264
+ if (!ctx.enabled) {
95
265
  return /* @__PURE__ */ jsx(Fragment, { children });
96
266
  }
97
267
  const arr = Children.toArray(children);
268
+ const isTextual = EDITABLE_TYPES.has(type);
98
269
  if (arr.length === 1 && isValidElement(arr[0])) {
99
270
  const el = arr[0];
100
- const existing = el.props.style ?? {};
271
+ const existingStyle = el.props.style ?? {};
101
272
  const existingClass = el.props.className ?? "";
102
- return cloneElement(el, {
273
+ const inlineStyle = {
274
+ outline: "1px dashed transparent",
275
+ outlineOffset: 2,
276
+ transition: "outline-color 0.15s ease",
277
+ cursor: isTextual ? "text" : "pointer",
278
+ ...existingStyle
279
+ };
280
+ const props = {
103
281
  "data-pp-edit": path,
104
282
  "data-pp-type": type,
105
- onClick,
283
+ ref: (node) => {
284
+ ref.current = node;
285
+ },
286
+ className: [existingClass, className, "pp-edit-target"].filter(Boolean).join(" "),
287
+ style: inlineStyle,
106
288
  onMouseEnter: (e) => {
107
- e.currentTarget.style.outlineColor = EDIT_HOVER_STYLE.outlineColor;
289
+ e.currentTarget.style.outlineColor = HOVER_OUTLINE_COLOR;
108
290
  },
109
291
  onMouseLeave: (e) => {
110
292
  e.currentTarget.style.outlineColor = "transparent";
111
- },
112
- style: { ...EDIT_ATTR_STYLE, ...existing },
113
- className: [existingClass, className, "pp-edit-target"].filter(Boolean).join(" ")
114
- });
293
+ }
294
+ };
295
+ if (isTextual) {
296
+ props.contentEditable = "plaintext-only";
297
+ props.suppressContentEditableWarning = true;
298
+ props.onFocus = (e) => {
299
+ e.currentTarget.style.outlineColor = HOVER_OUTLINE_COLOR;
300
+ e.currentTarget.style.outlineStyle = "solid";
301
+ };
302
+ props.onBlur = (e) => {
303
+ const target = e.currentTarget;
304
+ target.style.outlineColor = "transparent";
305
+ target.style.outlineStyle = "dashed";
306
+ const newText = target.textContent ?? "";
307
+ const original = typeof value === "string" ? value : initialText.current ?? "";
308
+ if (newText !== original) {
309
+ const newValue = type === "number" ? newText === "" ? null : Number(newText) : newText;
310
+ ctx.setChange(path, type, newValue);
311
+ }
312
+ };
313
+ props.onKeyDown = (e) => {
314
+ if (e.key === "Enter" && !e.shiftKey && type === "text") {
315
+ e.preventDefault();
316
+ e.currentTarget.blur();
317
+ }
318
+ if (e.key === "Escape") {
319
+ e.preventDefault();
320
+ e.currentTarget.blur();
321
+ }
322
+ };
323
+ }
324
+ return cloneElement(el, props);
115
325
  }
116
326
  return /* @__PURE__ */ jsx(
117
327
  "span",
118
328
  {
119
329
  "data-pp-edit": path,
120
330
  "data-pp-type": type,
121
- onClick,
122
- onMouseEnter: (e) => {
123
- e.currentTarget.style.outlineColor = EDIT_HOVER_STYLE.outlineColor;
124
- },
125
- onMouseLeave: (e) => {
126
- e.currentTarget.style.outlineColor = "transparent";
127
- },
128
- style: EDIT_ATTR_STYLE,
129
331
  className: ["pp-edit-target", className].filter(Boolean).join(" "),
332
+ style: { outline: "1px dashed transparent", outlineOffset: 2 },
130
333
  children
131
334
  }
132
335
  );
@@ -137,18 +340,68 @@ function StrapiText({ path, value, fieldType = "text", as = "span", className, c
137
340
  return /* @__PURE__ */ jsx(StrapiField, { path, type: fieldType, value, children: el });
138
341
  }
139
342
  function resolveUrl(value, baseUrl) {
140
- const u = value.url;
141
- if (u.startsWith("http")) return u;
142
- if (baseUrl) return `${baseUrl.replace(/\/$/, "")}${u.startsWith("/") ? "" : "/"}${u}`;
143
- return u;
343
+ if (value.url.startsWith("http")) return value.url;
344
+ if (baseUrl) return `${baseUrl.replace(/\/$/, "")}${value.url.startsWith("/") ? "" : "/"}${value.url}`;
345
+ return value.url;
144
346
  }
145
- function StrapiImage({ path, value, baseUrl, alt, className, style, loading = "lazy", fallback }) {
347
+ var HOVER_OUTLINE_COLOR2 = "#FA501E";
348
+ function StrapiImage({ path, value, baseUrl, alt, className, style, loading = "lazy", fallback, allowedTypes, multiple = false }) {
349
+ const ctx = useStrapiEditMode();
350
+ const containerRef = useRef(null);
351
+ const imgRef = useRef(null);
352
+ const handleClick = (e) => {
353
+ if (!ctx.enabled) return;
354
+ e.preventDefault();
355
+ e.stopPropagation();
356
+ const unsubscribe = ctx.onMediaPicked(path, (picked) => {
357
+ ctx.setChange(path, "media", picked);
358
+ unsubscribe();
359
+ if (imgRef.current && picked && typeof picked === "object" && !Array.isArray(picked) && "url" in picked) {
360
+ imgRef.current.src = resolveUrl(picked, baseUrl);
361
+ }
362
+ });
363
+ ctx.openMediaPicker(path, multiple, allowedTypes, value);
364
+ };
146
365
  if (!value) {
147
- return /* @__PURE__ */ jsx(StrapiField, { path, type: "media", value: null, children: /* @__PURE__ */ jsx("span", { className, style, children: fallback ?? "" }) });
366
+ if (!ctx.enabled) return /* @__PURE__ */ jsx(Fragment, { children: fallback ?? null });
367
+ return /* @__PURE__ */ jsx(
368
+ "span",
369
+ {
370
+ ref: containerRef,
371
+ "data-pp-edit": path,
372
+ "data-pp-type": "media",
373
+ onClick: handleClick,
374
+ onMouseEnter: (e) => {
375
+ e.currentTarget.style.outlineColor = HOVER_OUTLINE_COLOR2;
376
+ },
377
+ onMouseLeave: (e) => {
378
+ e.currentTarget.style.outlineColor = "transparent";
379
+ },
380
+ style: {
381
+ display: "inline-flex",
382
+ alignItems: "center",
383
+ justifyContent: "center",
384
+ background: "rgba(0,0,0,0.05)",
385
+ border: "1px dashed rgba(0,0,0,0.3)",
386
+ outline: "1px dashed transparent",
387
+ outlineOffset: 2,
388
+ cursor: "pointer",
389
+ padding: "20px 28px",
390
+ fontSize: 12,
391
+ fontWeight: 700,
392
+ textTransform: "uppercase",
393
+ letterSpacing: "0.15em",
394
+ color: "#000",
395
+ transition: "outline-color 0.15s ease"
396
+ },
397
+ children: "+ Bild hinzuf\xFCgen"
398
+ }
399
+ );
148
400
  }
149
- return /* @__PURE__ */ jsx(StrapiField, { path, type: "media", value, children: /* @__PURE__ */ jsx(
401
+ const imgEl = /* @__PURE__ */ jsx(
150
402
  "img",
151
403
  {
404
+ ref: imgRef,
152
405
  src: resolveUrl(value, baseUrl),
153
406
  alt: alt ?? value.alternativeText ?? value.name ?? "",
154
407
  width: value.width,
@@ -157,7 +410,64 @@ function StrapiImage({ path, value, baseUrl, alt, className, style, loading = "l
157
410
  className,
158
411
  style
159
412
  }
160
- ) });
413
+ );
414
+ if (!ctx.enabled) return imgEl;
415
+ return /* @__PURE__ */ jsxs(
416
+ "span",
417
+ {
418
+ ref: containerRef,
419
+ "data-pp-edit": path,
420
+ "data-pp-type": "media",
421
+ style: { position: "relative", display: "inline-block", outline: "1px dashed transparent", outlineOffset: 2, transition: "outline-color 0.15s ease" },
422
+ onMouseEnter: (e) => {
423
+ e.currentTarget.style.outlineColor = HOVER_OUTLINE_COLOR2;
424
+ const btn = e.currentTarget.querySelector("[data-pp-edit-btn]");
425
+ if (btn) btn.style.opacity = "1";
426
+ },
427
+ onMouseLeave: (e) => {
428
+ e.currentTarget.style.outlineColor = "transparent";
429
+ const btn = e.currentTarget.querySelector("[data-pp-edit-btn]");
430
+ if (btn) btn.style.opacity = "0";
431
+ },
432
+ children: [
433
+ imgEl,
434
+ /* @__PURE__ */ jsx(
435
+ "button",
436
+ {
437
+ type: "button",
438
+ "data-pp-edit-btn": true,
439
+ onClick: handleClick,
440
+ style: {
441
+ position: "absolute",
442
+ top: 8,
443
+ right: 8,
444
+ background: "#000",
445
+ color: "#EBC6DF",
446
+ border: "2px solid #000",
447
+ padding: "6px 10px",
448
+ fontSize: 11,
449
+ fontWeight: 700,
450
+ textTransform: "uppercase",
451
+ letterSpacing: "0.15em",
452
+ cursor: "pointer",
453
+ opacity: 0,
454
+ transition: "opacity 0.15s ease, background 0.15s ease",
455
+ fontFamily: "system-ui, -apple-system, sans-serif"
456
+ },
457
+ onMouseEnter: (e) => {
458
+ e.currentTarget.style.background = "#FA501E";
459
+ e.currentTarget.style.color = "#000";
460
+ },
461
+ onMouseLeave: (e) => {
462
+ e.currentTarget.style.background = "#000";
463
+ e.currentTarget.style.color = "#EBC6DF";
464
+ },
465
+ children: "\xC4ndern"
466
+ }
467
+ )
468
+ ]
469
+ }
470
+ );
161
471
  }
162
472
  function StrapiList({ path, value, renderItem, fallback }) {
163
473
  if (!Array.isArray(value) || value.length === 0) {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/EditModeContext.tsx","../src/StrapiField.tsx","../src/StrapiText.tsx","../src/StrapiImage.tsx","../src/StrapiList.tsx"],"names":["jsx","Fragment"],"mappings":"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","file":"index.js","sourcesContent":["\"use client\";\n\nimport { createContext, useContext, useEffect, useMemo, useState } from \"react\";\n\ninterface EditModeState {\n enabled: boolean;\n token: string | null;\n}\n\nconst EditModeContext = createContext<EditModeState>({ enabled: false, token: null });\n\nexport function useStrapiEditMode(): EditModeState {\n return useContext(EditModeContext);\n}\n\ninterface ProviderProps {\n children: React.ReactNode;\n /**\n * Override automatic URL-based detection. If `true`/`false` ist gesetzt,\n * wird der URL-Parameter ignoriert.\n */\n enabled?: boolean;\n}\n\n/**\n * Provider, der den Edit-Mode aus `?__pp_edit=1&__pp_token=<jwt>` der URL\n * liest und an Kinder via Context weitergibt. Token wird sicherheitshalber\n * NICHT ans CSR exponiert über Props oder DOM — er bleibt nur im Context.\n *\n * Im Edit-Mode wird ausserdem eine kleine globale CSS-Klasse `pp-edit-active`\n * am `<html>` gesetzt, sodass Customer-Sites optional CSS-Hooks setzen können.\n */\nexport function StrapiEditModeProvider({ children, enabled: enabledOverride }: ProviderProps) {\n const [urlState, setUrlState] = useState<EditModeState>({ enabled: false, token: null });\n\n useEffect(() => {\n if (typeof window === \"undefined\") return;\n if (enabledOverride !== undefined) {\n setUrlState({ enabled: enabledOverride, token: null });\n return;\n }\n try {\n const params = new URLSearchParams(window.location.search);\n const edit = params.get(\"__pp_edit\") === \"1\";\n const token = params.get(\"__pp_token\");\n setUrlState({ enabled: edit, token });\n } catch {\n // ignore\n }\n }, [enabledOverride]);\n\n useEffect(() => {\n if (typeof document === \"undefined\") return;\n document.documentElement.classList.toggle(\"pp-edit-active\", urlState.enabled);\n }, [urlState.enabled]);\n\n // postMessage \"ready\" wenn Edit-Mode aktiv ist und Iframe geladen\n useEffect(() => {\n if (typeof window === \"undefined\") return;\n if (!urlState.enabled) return;\n if (window.parent === window) return; // nicht im Iframe\n try {\n window.parent.postMessage(\n { type: \"pp:edit:ready\", href: window.location.href },\n \"*\",\n );\n } catch {\n // ignore\n }\n }, [urlState.enabled]);\n\n // Reload-Listener vom Parent (nach Save)\n useEffect(() => {\n if (typeof window === \"undefined\") return;\n if (!urlState.enabled) return;\n function onMsg(e: MessageEvent) {\n const d = e.data;\n if (d && typeof d === \"object\" && d.type === \"pp:edit:reload\") {\n // Soft-Reload — re-loaded mit gleichem Suchparameter\n window.location.reload();\n }\n }\n window.addEventListener(\"message\", onMsg);\n return () => window.removeEventListener(\"message\", onMsg);\n }, [urlState.enabled]);\n\n const value = useMemo(() => urlState, [urlState]);\n return <EditModeContext.Provider value={value}>{children}</EditModeContext.Provider>;\n}\n","\"use client\";\n\nimport { Children, cloneElement, isValidElement, useCallback, type ReactElement, type ReactNode } from \"react\";\nimport { useStrapiEditMode } from \"./EditModeContext\";\nimport type { StrapiFieldType } from \"./types\";\n\ninterface StrapiFieldProps {\n /** Dot-notation path im Strapi-Document, z.B. \"hero.title\" oder \"features.0.image\" */\n path: string;\n /** Strapi field type — bestimmt welche Edit-UI im Parent geöffnet wird */\n type: StrapiFieldType;\n /** Aktueller Wert. Wird beim Click-Event mitgesendet, damit der Editor initial befüllt ist */\n value?: unknown;\n /** Inhalt (das echte gerenderte HTML). Wird im Edit-Mode mit data-attrs + Hover/Click annotiert */\n children: ReactNode;\n /** Optional: zusätzliche CSS-Klassen die im Edit-Mode angehängt werden */\n className?: string;\n}\n\nconst EDIT_ATTR_STYLE = {\n outline: \"1px dashed transparent\",\n outlineOffset: \"2px\",\n cursor: \"pointer\",\n transition: \"outline-color 0.15s ease\",\n} as const;\n\nconst EDIT_HOVER_STYLE = {\n outlineColor: \"#FA501E\",\n};\n\n/**\n * Universeller Wrapper für editierbare Strapi-Felder.\n *\n * Im normalen Mode rendert er nur die `children` — keine zusätzlichen DOM-\n * Nodes, kein Overhead. Im Edit-Mode wird das einzige Top-Level-Child mit\n * `data-pp-edit=\"<path>\"` annotiert + Click-Handler bekommt.\n *\n * Falls children mehrere Top-Level-Knoten haben, wird automatisch ein\n * `<span>` als Wrapper eingefügt.\n */\nexport function StrapiField({ path, type, value, children, className }: StrapiFieldProps) {\n const { enabled } = useStrapiEditMode();\n\n const onClick = useCallback(\n (e: React.MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n if (typeof window === \"undefined\" || window.parent === window) return;\n const target = e.currentTarget as HTMLElement;\n const rect = target.getBoundingClientRect();\n try {\n window.parent.postMessage(\n {\n type: \"pp:edit:click\",\n path,\n fieldType: type,\n rect: {\n top: rect.top,\n left: rect.left,\n width: rect.width,\n height: rect.height,\n },\n currentValue: value,\n },\n \"*\",\n );\n } catch {\n // ignore\n }\n },\n [path, type, value],\n );\n\n if (!enabled) {\n return <>{children}</>;\n }\n\n // Wenn genau ein React-Element drin ist, klonen wir es und reichen die Edit-Attrs durch.\n const arr = Children.toArray(children);\n if (arr.length === 1 && isValidElement(arr[0])) {\n const el = arr[0] as ReactElement<Record<string, unknown>>;\n const existing = (el.props.style as React.CSSProperties) ?? {};\n const existingClass = (el.props.className as string) ?? \"\";\n return cloneElement(el, {\n \"data-pp-edit\": path,\n \"data-pp-type\": type,\n onClick,\n onMouseEnter: (e: React.MouseEvent) => {\n (e.currentTarget as HTMLElement).style.outlineColor = EDIT_HOVER_STYLE.outlineColor;\n },\n onMouseLeave: (e: React.MouseEvent) => {\n (e.currentTarget as HTMLElement).style.outlineColor = \"transparent\";\n },\n style: { ...EDIT_ATTR_STYLE, ...existing },\n className: [existingClass, className, \"pp-edit-target\"].filter(Boolean).join(\" \"),\n });\n }\n\n // Mehrere Kinder: in einen span wrappen\n return (\n <span\n data-pp-edit={path}\n data-pp-type={type}\n onClick={onClick}\n onMouseEnter={(e) => { (e.currentTarget).style.outlineColor = EDIT_HOVER_STYLE.outlineColor; }}\n onMouseLeave={(e) => { (e.currentTarget).style.outlineColor = \"transparent\"; }}\n style={EDIT_ATTR_STYLE}\n className={[\"pp-edit-target\", className].filter(Boolean).join(\" \")}\n >\n {children}\n </span>\n );\n}\n","\"use client\";\n\nimport { createElement, type ElementType, type ReactNode } from \"react\";\nimport { StrapiField } from \"./StrapiField\";\nimport type { StrapiFieldType } from \"./types\";\n\ninterface StrapiTextProps {\n path: string;\n value: string | number | null | undefined;\n /** Welcher Strapi-Feldtyp ist das? Default \"text\". Für mehrzeilige Felder \"textarea\", für Markdown \"richText\". */\n fieldType?: Extract<StrapiFieldType, \"text\" | \"textarea\" | \"richText\" | \"email\" | \"number\">;\n /** HTML-Tag um den Wert zu rendern. Default \"span\". */\n as?: ElementType;\n className?: string;\n children?: ReactNode;\n}\n\n/**\n * Inline-Text wrapper. Im normalen Mode rendert es einfach `value` im\n * gewählten Tag — identisch zu einem rohen `<h1>{value}</h1>`. Im Edit-Mode\n * wird ein Click-Handler aktiviert.\n *\n * Wenn `children` mitgegeben sind, werden diese statt `value` als Inhalt\n * verwendet — nützlich für custom Formatierung. Der Click-Wert bleibt\n * `value`.\n */\nexport function StrapiText({ path, value, fieldType = \"text\", as = \"span\", className, children }: StrapiTextProps) {\n const content = children ?? (value === null || value === undefined ? \"\" : String(value));\n const el = createElement(as, { className }, content);\n return (\n <StrapiField path={path} type={fieldType} value={value}>\n {el}\n </StrapiField>\n );\n}\n","\"use client\";\n\nimport { StrapiField } from \"./StrapiField\";\nimport type { StrapiMediaValue } from \"./types\";\n\ninterface StrapiImageProps {\n path: string;\n value: StrapiMediaValue | null | undefined;\n /** Optional: Strapi-Basis-URL für relative URLs (z.B. \"https://cms.paulpaul.studio\") */\n baseUrl?: string;\n /** Override alt-Text (default: alternativeText vom Strapi-Objekt, dann name) */\n alt?: string;\n className?: string;\n style?: React.CSSProperties;\n loading?: \"lazy\" | \"eager\";\n /** Fallback wenn value null/undefined ist */\n fallback?: React.ReactNode;\n}\n\nfunction resolveUrl(value: StrapiMediaValue, baseUrl?: string): string {\n const u = value.url;\n if (u.startsWith(\"http\")) return u;\n if (baseUrl) return `${baseUrl.replace(/\\/$/, \"\")}${u.startsWith(\"/\") ? \"\" : \"/\"}${u}`;\n return u;\n}\n\n/**\n * Wrapper für ein einzelnes Strapi-Media-Feld. Rendert `<img>` mit der\n * abgeleiteten URL. Im Edit-Mode klickbar — der Editor öffnet einen\n * MediaPicker.\n *\n * Wenn `value` null ist, wird der `fallback` gerendert (oder ein leeres\n * `<span>`).\n */\nexport function StrapiImage({ path, value, baseUrl, alt, className, style, loading = \"lazy\", fallback }: StrapiImageProps) {\n if (!value) {\n return (\n <StrapiField path={path} type=\"media\" value={null}>\n <span className={className} style={style}>{fallback ?? \"\"}</span>\n </StrapiField>\n );\n }\n return (\n <StrapiField path={path} type=\"media\" value={value}>\n <img\n src={resolveUrl(value, baseUrl)}\n alt={alt ?? value.alternativeText ?? value.name ?? \"\"}\n width={value.width}\n height={value.height}\n loading={loading}\n className={className}\n style={style}\n />\n </StrapiField>\n );\n}\n","\"use client\";\n\nimport { Fragment, type ReactNode } from \"react\";\n\ninterface StrapiListProps<T> {\n /** Pfad zum Array-Feld, z.B. \"features\" */\n path: string;\n value: T[] | null | undefined;\n /**\n * Render-Funktion pro Item. Bekommt einen `itemPath` als zweites Argument,\n * der für nested-StrapiText/Image-Calls als path-Prefix dient.\n */\n renderItem: (item: T, itemPath: string, index: number) => ReactNode;\n /** Optional: Fallback wenn value null/leer ist */\n fallback?: ReactNode;\n}\n\n/**\n * Iteriert über ein Strapi-Repeatable-Field oder eine Relation-Liste. Generiert\n * pro Item den vollständigen Edit-Pfad (z.B. \"features.0\", \"features.1\"), den\n * der Caller in nested StrapiText/Image weitergibt.\n *\n * Beispiel:\n * <StrapiList path=\"features\" value={page.features} renderItem={(f, fp) => (\n * <div>\n * <StrapiText path={`${fp}.title`} value={f.title} as=\"h3\" />\n * <StrapiImage path={`${fp}.icon`} value={f.icon} />\n * </div>\n * )} />\n */\nexport function StrapiList<T>({ path, value, renderItem, fallback }: StrapiListProps<T>) {\n if (!Array.isArray(value) || value.length === 0) {\n return <>{fallback ?? null}</>;\n }\n return (\n <>\n {value.map((item, i) => (\n <Fragment key={i}>{renderItem(item, `${path}.${i}`, i)}</Fragment>\n ))}\n </>\n );\n}\n"]}
1
+ {"version":3,"sources":["../src/EditModeContext.tsx","../src/StrapiField.tsx","../src/StrapiText.tsx","../src/StrapiImage.tsx","../src/StrapiList.tsx"],"names":["value","useRef","useEffect","jsx","Fragment","HOVER_OUTLINE_COLOR","jsxs"],"mappings":"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","file":"index.js","sourcesContent":["\"use client\";\n\nimport { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from \"react\";\nimport type { StrapiFieldType } from \"./types\";\n\ninterface PendingChange {\n path: string;\n fieldType: StrapiFieldType;\n value: unknown;\n}\n\ninterface EditModeState {\n enabled: boolean;\n token: string | null;\n changes: Map<string, PendingChange>;\n isDirty: boolean;\n isCommitting: boolean;\n lastCommitError: string | null;\n setChange: (path: string, fieldType: StrapiFieldType, value: unknown) => void;\n clearChange: (path: string) => void;\n commit: () => void;\n openMediaPicker: (path: string, multiple: boolean, allowedTypes: string[] | undefined, currentValue: unknown) => void;\n onMediaPicked: (path: string, listener: (value: unknown) => void) => () => void;\n}\n\nconst noop = () => {};\nconst EditModeContext = createContext<EditModeState>({\n enabled: false,\n token: null,\n changes: new Map(),\n isDirty: false,\n isCommitting: false,\n lastCommitError: null,\n setChange: noop,\n clearChange: noop,\n commit: noop,\n openMediaPicker: noop,\n onMediaPicked: () => noop,\n});\n\nexport function useStrapiEditMode(): EditModeState {\n return useContext(EditModeContext);\n}\n\ninterface ProviderProps {\n children: React.ReactNode;\n enabled?: boolean;\n}\n\nexport function StrapiEditModeProvider({ children, enabled: enabledOverride }: ProviderProps) {\n const [urlState, setUrlState] = useState<{ enabled: boolean; token: string | null }>({ enabled: false, token: null });\n const [changes, setChanges] = useState<Map<string, PendingChange>>(new Map());\n const [isCommitting, setIsCommitting] = useState(false);\n const [lastCommitError, setLastCommitError] = useState<string | null>(null);\n const mediaPickListeners = useRef<Map<string, (v: unknown) => void>>(new Map());\n\n useEffect(() => {\n if (typeof window === \"undefined\") return;\n if (enabledOverride !== undefined) {\n setUrlState({ enabled: enabledOverride, token: null });\n return;\n }\n try {\n const params = new URLSearchParams(window.location.search);\n const edit = params.get(\"__pp_edit\") === \"1\";\n const token = params.get(\"__pp_token\");\n setUrlState({ enabled: edit, token });\n } catch {\n // ignore\n }\n }, [enabledOverride]);\n\n useEffect(() => {\n if (typeof document === \"undefined\") return;\n document.documentElement.classList.toggle(\"pp-edit-active\", urlState.enabled);\n }, [urlState.enabled]);\n\n // Ready ping to parent\n useEffect(() => {\n if (typeof window === \"undefined\") return;\n if (!urlState.enabled) return;\n if (window.parent === window) return;\n try {\n window.parent.postMessage({ type: \"pp:edit:ready\", href: window.location.href }, \"*\");\n } catch { /* ignore */ }\n }, [urlState.enabled]);\n\n // Listen for parent → iframe messages\n useEffect(() => {\n if (typeof window === \"undefined\") return;\n if (!urlState.enabled) return;\n function onMsg(e: MessageEvent) {\n const d = e.data;\n if (!d || typeof d !== \"object\") return;\n if (d.type === \"pp:edit:reload\") {\n window.location.reload();\n return;\n }\n if (d.type === \"pp:edit:commit-result\") {\n setIsCommitting(false);\n if (d.ok) {\n setChanges(new Map());\n setLastCommitError(null);\n } else {\n setLastCommitError(d.error || \"Speichern fehlgeschlagen\");\n }\n return;\n }\n if (d.type === \"pp:edit:media-picked\") {\n const listener = mediaPickListeners.current.get(d.path);\n if (listener) {\n listener(d.value);\n mediaPickListeners.current.delete(d.path);\n }\n return;\n }\n }\n window.addEventListener(\"message\", onMsg);\n return () => window.removeEventListener(\"message\", onMsg);\n }, [urlState.enabled]);\n\n const setChange = useCallback((path: string, fieldType: StrapiFieldType, value: unknown) => {\n setChanges((prev) => {\n const next = new Map(prev);\n next.set(path, { path, fieldType, value });\n return next;\n });\n setLastCommitError(null);\n // Optional: send live change event for future \"show preview on parent\"\n if (typeof window !== \"undefined\" && window.parent !== window) {\n try {\n window.parent.postMessage({ type: \"pp:edit:change\", path, fieldType, value }, \"*\");\n } catch { /* ignore */ }\n }\n }, []);\n\n const clearChange = useCallback((path: string) => {\n setChanges((prev) => {\n const next = new Map(prev);\n next.delete(path);\n return next;\n });\n }, []);\n\n const commit = useCallback(() => {\n if (changes.size === 0) return;\n if (typeof window === \"undefined\" || window.parent === window) return;\n setIsCommitting(true);\n setLastCommitError(null);\n try {\n window.parent.postMessage(\n {\n type: \"pp:edit:commit-request\",\n changes: Array.from(changes.values()),\n },\n \"*\",\n );\n } catch (err) {\n setIsCommitting(false);\n setLastCommitError(err instanceof Error ? err.message : String(err));\n }\n }, [changes]);\n\n const openMediaPicker = useCallback((path: string, multiple: boolean, allowedTypes: string[] | undefined, currentValue: unknown) => {\n if (typeof window === \"undefined\" || window.parent === window) return;\n try {\n window.parent.postMessage(\n {\n type: \"pp:edit:open-media-picker\",\n path, multiple, allowedTypes, currentValue,\n },\n \"*\",\n );\n } catch { /* ignore */ }\n }, []);\n\n const onMediaPicked = useCallback((path: string, listener: (v: unknown) => void) => {\n mediaPickListeners.current.set(path, listener);\n return () => { mediaPickListeners.current.delete(path); };\n }, []);\n\n const value = useMemo<EditModeState>(() => ({\n enabled: urlState.enabled,\n token: urlState.token,\n changes,\n isDirty: changes.size > 0,\n isCommitting,\n lastCommitError,\n setChange,\n clearChange,\n commit,\n openMediaPicker,\n onMediaPicked,\n }), [urlState, changes, isCommitting, lastCommitError, setChange, clearChange, commit, openMediaPicker, onMediaPicked]);\n\n return (\n <EditModeContext.Provider value={value}>\n {children}\n {urlState.enabled && <SaveBar />}\n </EditModeContext.Provider>\n );\n}\n\nfunction SaveBar() {\n const { isDirty, isCommitting, lastCommitError, changes, commit } = useStrapiEditMode();\n if (!isDirty && !lastCommitError && !isCommitting) return null;\n\n return (\n <div\n style={{\n position: \"fixed\",\n bottom: 24,\n right: 24,\n zIndex: 999999,\n fontFamily: \"system-ui, -apple-system, sans-serif\",\n }}\n >\n {lastCommitError && (\n <div style={{\n background: \"#FA501E\",\n color: \"#000\",\n padding: \"8px 14px\",\n marginBottom: 8,\n fontSize: 13,\n fontWeight: 600,\n maxWidth: 320,\n }}>\n {lastCommitError}\n </div>\n )}\n <button\n type=\"button\"\n onClick={commit}\n disabled={isCommitting || !isDirty}\n style={{\n background: isCommitting ? \"#666\" : \"#000\",\n color: \"#EBC6DF\",\n border: \"2px solid #000\",\n padding: \"14px 22px\",\n fontSize: 13,\n fontWeight: 700,\n textTransform: \"uppercase\",\n letterSpacing: \"0.2em\",\n cursor: isCommitting ? \"wait\" : \"pointer\",\n display: \"flex\",\n alignItems: \"center\",\n gap: 10,\n boxShadow: \"0 4px 12px rgba(0,0,0,0.3)\",\n transition: \"background 0.15s ease\",\n }}\n onMouseEnter={(e) => {\n if (!isCommitting && isDirty) {\n e.currentTarget.style.background = \"#FA501E\";\n e.currentTarget.style.color = \"#000\";\n }\n }}\n onMouseLeave={(e) => {\n if (!isCommitting && isDirty) {\n e.currentTarget.style.background = \"#000\";\n e.currentTarget.style.color = \"#EBC6DF\";\n }\n }}\n >\n {isCommitting ? (\n <span>Speichere…</span>\n ) : (\n <>\n <span style={{\n background: \"#FA501E\", color: \"#000\",\n width: 22, height: 22, borderRadius: \"50%\",\n display: \"inline-flex\", alignItems: \"center\", justifyContent: \"center\",\n fontSize: 11, fontWeight: 700,\n }}>{changes.size}</span>\n <span>Speichern</span>\n </>\n )}\n </button>\n </div>\n );\n}\n","\"use client\";\n\nimport { Children, cloneElement, isValidElement, useEffect, useRef, type ReactElement, type ReactNode } from \"react\";\nimport { useStrapiEditMode } from \"./EditModeContext\";\nimport type { StrapiFieldType } from \"./types\";\n\ninterface StrapiFieldProps {\n path: string;\n type: StrapiFieldType;\n value?: unknown;\n children: ReactNode;\n className?: string;\n}\n\nconst EDITABLE_TYPES = new Set<StrapiFieldType>([\n \"text\",\n \"textarea\",\n \"richText\",\n \"email\",\n \"number\",\n]);\n\nconst HOVER_OUTLINE_COLOR = \"#FA501E\";\n\n/**\n * Universeller Wrapper. Im Normal-Mode: rendert children unverändert.\n *\n * Im Edit-Mode:\n * - Für text/textarea/richText/email/number: macht das Top-Level-Child\n * `contenteditable`, mit dünnem orange Hover-Outline, sammelt Wert-Änderungen\n * in den globalen Edit-Context, der dann beim Klick auf \"Speichern\" alle\n * Änderungen in einem Roundtrip ans Parent schickt.\n * - Für andere Typen (media/select/checkbox): trägt ein Click-Handler bzw.\n * ein Overlay (Implementierung in jeweiligen Sugar-Komponenten).\n */\nexport function StrapiField({ path, type, value, children, className }: StrapiFieldProps) {\n const ctx = useStrapiEditMode();\n const ref = useRef<HTMLElement | null>(null);\n const initialText = useRef<string | undefined>(undefined);\n\n // Captured initial textContent on first mount in edit-mode (for revert-detection)\n useEffect(() => {\n if (!ctx.enabled) return;\n if (!EDITABLE_TYPES.has(type)) return;\n if (initialText.current === undefined && ref.current) {\n initialText.current = ref.current.textContent ?? \"\";\n }\n }, [ctx.enabled, type]);\n\n if (!ctx.enabled) {\n return <>{children}</>;\n }\n\n const arr = Children.toArray(children);\n const isTextual = EDITABLE_TYPES.has(type);\n\n if (arr.length === 1 && isValidElement(arr[0])) {\n const el = arr[0] as ReactElement<Record<string, unknown>>;\n const existingStyle = (el.props.style as React.CSSProperties) ?? {};\n const existingClass = (el.props.className as string) ?? \"\";\n\n const inlineStyle: React.CSSProperties = {\n outline: \"1px dashed transparent\",\n outlineOffset: 2,\n transition: \"outline-color 0.15s ease\",\n cursor: isTextual ? \"text\" : \"pointer\",\n ...existingStyle,\n };\n\n const props: Record<string, unknown> = {\n \"data-pp-edit\": path,\n \"data-pp-type\": type,\n ref: (node: HTMLElement | null) => { ref.current = node; },\n className: [existingClass, className, \"pp-edit-target\"].filter(Boolean).join(\" \"),\n style: inlineStyle,\n onMouseEnter: (e: React.MouseEvent) => {\n (e.currentTarget as HTMLElement).style.outlineColor = HOVER_OUTLINE_COLOR;\n },\n onMouseLeave: (e: React.MouseEvent) => {\n (e.currentTarget as HTMLElement).style.outlineColor = \"transparent\";\n },\n };\n\n if (isTextual) {\n props.contentEditable = \"plaintext-only\";\n props.suppressContentEditableWarning = true;\n props.onFocus = (e: React.FocusEvent) => {\n (e.currentTarget as HTMLElement).style.outlineColor = HOVER_OUTLINE_COLOR;\n (e.currentTarget as HTMLElement).style.outlineStyle = \"solid\";\n };\n props.onBlur = (e: React.FocusEvent) => {\n const target = e.currentTarget as HTMLElement;\n target.style.outlineColor = \"transparent\";\n target.style.outlineStyle = \"dashed\";\n const newText = target.textContent ?? \"\";\n const original = typeof value === \"string\" ? value : (initialText.current ?? \"\");\n if (newText !== original) {\n const newValue = type === \"number\" ? (newText === \"\" ? null : Number(newText)) : newText;\n ctx.setChange(path, type, newValue);\n }\n };\n props.onKeyDown = (e: React.KeyboardEvent) => {\n // Enter ohne Shift = blur (commit local). Für richText/textarea kann user mit Shift+Enter Zeilenumbruch erzeugen.\n if (e.key === \"Enter\" && !e.shiftKey && type === \"text\") {\n e.preventDefault();\n (e.currentTarget as HTMLElement).blur();\n }\n if (e.key === \"Escape\") {\n e.preventDefault();\n (e.currentTarget as HTMLElement).blur();\n }\n };\n } else {\n // Non-editable types: click handler wird von Sugar-Komponenten (StrapiImage etc.) gesetzt\n }\n\n return cloneElement(el, props);\n }\n\n // Fallback: span wrap\n return (\n <span\n data-pp-edit={path}\n data-pp-type={type}\n className={[\"pp-edit-target\", className].filter(Boolean).join(\" \")}\n style={{ outline: \"1px dashed transparent\", outlineOffset: 2 }}\n >\n {children}\n </span>\n );\n}\n","\"use client\";\n\nimport { createElement, type ElementType, type ReactNode } from \"react\";\nimport { StrapiField } from \"./StrapiField\";\nimport type { StrapiFieldType } from \"./types\";\n\ninterface StrapiTextProps {\n path: string;\n value: string | number | null | undefined;\n fieldType?: Extract<StrapiFieldType, \"text\" | \"textarea\" | \"richText\" | \"email\" | \"number\">;\n as?: ElementType;\n className?: string;\n children?: ReactNode;\n}\n\nexport function StrapiText({ path, value, fieldType = \"text\", as = \"span\", className, children }: StrapiTextProps) {\n const content = children ?? (value === null || value === undefined ? \"\" : String(value));\n const el = createElement(as, { className }, content);\n return (\n <StrapiField path={path} type={fieldType} value={value}>\n {el}\n </StrapiField>\n );\n}\n","\"use client\";\n\nimport { useRef } from \"react\";\nimport { useStrapiEditMode } from \"./EditModeContext\";\nimport type { StrapiMediaValue } from \"./types\";\n\ninterface StrapiImageProps {\n path: string;\n value: StrapiMediaValue | null | undefined;\n baseUrl?: string;\n alt?: string;\n className?: string;\n style?: React.CSSProperties;\n loading?: \"lazy\" | \"eager\";\n fallback?: React.ReactNode;\n allowedTypes?: string[];\n multiple?: boolean;\n}\n\nfunction resolveUrl(value: StrapiMediaValue, baseUrl?: string): string {\n if (value.url.startsWith(\"http\")) return value.url;\n if (baseUrl) return `${baseUrl.replace(/\\/$/, \"\")}${value.url.startsWith(\"/\") ? \"\" : \"/\"}${value.url}`;\n return value.url;\n}\n\nconst HOVER_OUTLINE_COLOR = \"#FA501E\";\n\nexport function StrapiImage({ path, value, baseUrl, alt, className, style, loading = \"lazy\", fallback, allowedTypes, multiple = false }: StrapiImageProps) {\n const ctx = useStrapiEditMode();\n const containerRef = useRef<HTMLSpanElement>(null);\n const imgRef = useRef<HTMLImageElement>(null);\n\n const handleClick = (e: React.MouseEvent) => {\n if (!ctx.enabled) return;\n e.preventDefault();\n e.stopPropagation();\n const unsubscribe = ctx.onMediaPicked(path, (picked) => {\n ctx.setChange(path, \"media\", picked);\n unsubscribe();\n // Optimistic UI: swap src in-place wenn picked = single file mit url\n if (imgRef.current && picked && typeof picked === \"object\" && !Array.isArray(picked) && \"url\" in (picked as Record<string, unknown>)) {\n imgRef.current.src = resolveUrl(picked as StrapiMediaValue, baseUrl);\n }\n });\n ctx.openMediaPicker(path, multiple, allowedTypes, value);\n };\n\n if (!value) {\n if (!ctx.enabled) return <>{fallback ?? null}</>;\n return (\n <span\n ref={containerRef}\n data-pp-edit={path}\n data-pp-type=\"media\"\n onClick={handleClick}\n onMouseEnter={(e) => { (e.currentTarget as HTMLElement).style.outlineColor = HOVER_OUTLINE_COLOR; }}\n onMouseLeave={(e) => { (e.currentTarget as HTMLElement).style.outlineColor = \"transparent\"; }}\n style={{\n display: \"inline-flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n background: \"rgba(0,0,0,0.05)\",\n border: \"1px dashed rgba(0,0,0,0.3)\",\n outline: \"1px dashed transparent\",\n outlineOffset: 2,\n cursor: \"pointer\",\n padding: \"20px 28px\",\n fontSize: 12,\n fontWeight: 700,\n textTransform: \"uppercase\",\n letterSpacing: \"0.15em\",\n color: \"#000\",\n transition: \"outline-color 0.15s ease\",\n }}\n >\n + Bild hinzufügen\n </span>\n );\n }\n\n const imgEl = (\n <img\n ref={imgRef}\n src={resolveUrl(value, baseUrl)}\n alt={alt ?? value.alternativeText ?? value.name ?? \"\"}\n width={value.width}\n height={value.height}\n loading={loading}\n className={className}\n style={style}\n />\n );\n\n if (!ctx.enabled) return imgEl;\n\n return (\n <span\n ref={containerRef}\n data-pp-edit={path}\n data-pp-type=\"media\"\n style={{ position: \"relative\", display: \"inline-block\", outline: \"1px dashed transparent\", outlineOffset: 2, transition: \"outline-color 0.15s ease\" }}\n onMouseEnter={(e) => {\n (e.currentTarget as HTMLElement).style.outlineColor = HOVER_OUTLINE_COLOR;\n const btn = (e.currentTarget as HTMLElement).querySelector(\"[data-pp-edit-btn]\") as HTMLElement | null;\n if (btn) btn.style.opacity = \"1\";\n }}\n onMouseLeave={(e) => {\n (e.currentTarget as HTMLElement).style.outlineColor = \"transparent\";\n const btn = (e.currentTarget as HTMLElement).querySelector(\"[data-pp-edit-btn]\") as HTMLElement | null;\n if (btn) btn.style.opacity = \"0\";\n }}\n >\n {imgEl}\n <button\n type=\"button\"\n data-pp-edit-btn\n onClick={handleClick}\n style={{\n position: \"absolute\",\n top: 8,\n right: 8,\n background: \"#000\",\n color: \"#EBC6DF\",\n border: \"2px solid #000\",\n padding: \"6px 10px\",\n fontSize: 11,\n fontWeight: 700,\n textTransform: \"uppercase\",\n letterSpacing: \"0.15em\",\n cursor: \"pointer\",\n opacity: 0,\n transition: \"opacity 0.15s ease, background 0.15s ease\",\n fontFamily: \"system-ui, -apple-system, sans-serif\",\n }}\n onMouseEnter={(e) => { e.currentTarget.style.background = \"#FA501E\"; e.currentTarget.style.color = \"#000\"; }}\n onMouseLeave={(e) => { e.currentTarget.style.background = \"#000\"; e.currentTarget.style.color = \"#EBC6DF\"; }}\n >\n Ändern\n </button>\n </span>\n );\n}\n","\"use client\";\n\nimport { Fragment, type ReactNode } from \"react\";\n\ninterface StrapiListProps<T> {\n /** Pfad zum Array-Feld, z.B. \"features\" */\n path: string;\n value: T[] | null | undefined;\n /**\n * Render-Funktion pro Item. Bekommt einen `itemPath` als zweites Argument,\n * der für nested-StrapiText/Image-Calls als path-Prefix dient.\n */\n renderItem: (item: T, itemPath: string, index: number) => ReactNode;\n /** Optional: Fallback wenn value null/leer ist */\n fallback?: ReactNode;\n}\n\n/**\n * Iteriert über ein Strapi-Repeatable-Field oder eine Relation-Liste. Generiert\n * pro Item den vollständigen Edit-Pfad (z.B. \"features.0\", \"features.1\"), den\n * der Caller in nested StrapiText/Image weitergibt.\n *\n * Beispiel:\n * <StrapiList path=\"features\" value={page.features} renderItem={(f, fp) => (\n * <div>\n * <StrapiText path={`${fp}.title`} value={f.title} as=\"h3\" />\n * <StrapiImage path={`${fp}.icon`} value={f.icon} />\n * </div>\n * )} />\n */\nexport function StrapiList<T>({ path, value, renderItem, fallback }: StrapiListProps<T>) {\n if (!Array.isArray(value) || value.length === 0) {\n return <>{fallback ?? null}</>;\n }\n return (\n <>\n {value.map((item, i) => (\n <Fragment key={i}>{renderItem(item, `${path}.${i}`, i)}</Fragment>\n ))}\n </>\n );\n}\n"]}