personalize-connect-sdk 1.3.2 → 1.3.4
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.d.mts +5 -6
- package/dist/index.d.ts +5 -6
- package/dist/index.js +156 -34
- package/dist/index.mjs +157 -35
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -140,15 +140,14 @@ declare function resolveContent(options: ResolveContentOptions): Promise<Resolve
|
|
|
140
140
|
type GetConfigFromProps<P> = (props: P) => PersonalizeConnectConfig | undefined;
|
|
141
141
|
/**
|
|
142
142
|
* HOC that wraps any JSS component.
|
|
143
|
-
*
|
|
143
|
+
*
|
|
144
|
+
* Config lookup order:
|
|
144
145
|
* 1. props.rendering.personalizeConnect (inline on layout data)
|
|
145
146
|
* 2. context.configs map (loaded from content tree via Edge)
|
|
146
147
|
*
|
|
147
|
-
*
|
|
148
|
-
*
|
|
149
|
-
*
|
|
150
|
-
* In Page Builder, renders a visual indicator (border + badge) on
|
|
151
|
-
* components that have personalization configured.
|
|
148
|
+
* Live site: renders with defaultKey first, calls Personalize async, re-renders.
|
|
149
|
+
* Page Builder: shows a preview bar above the component to switch between
|
|
150
|
+
* content variants without running actual personalization.
|
|
152
151
|
*/
|
|
153
152
|
declare function withPersonalizeConnect<P extends object>(WrappedComponent: ComponentType<P>, getConfig?: GetConfigFromProps<P>): ComponentType<P>;
|
|
154
153
|
|
package/dist/index.d.ts
CHANGED
|
@@ -140,15 +140,14 @@ declare function resolveContent(options: ResolveContentOptions): Promise<Resolve
|
|
|
140
140
|
type GetConfigFromProps<P> = (props: P) => PersonalizeConnectConfig | undefined;
|
|
141
141
|
/**
|
|
142
142
|
* HOC that wraps any JSS component.
|
|
143
|
-
*
|
|
143
|
+
*
|
|
144
|
+
* Config lookup order:
|
|
144
145
|
* 1. props.rendering.personalizeConnect (inline on layout data)
|
|
145
146
|
* 2. context.configs map (loaded from content tree via Edge)
|
|
146
147
|
*
|
|
147
|
-
*
|
|
148
|
-
*
|
|
149
|
-
*
|
|
150
|
-
* In Page Builder, renders a visual indicator (border + badge) on
|
|
151
|
-
* components that have personalization configured.
|
|
148
|
+
* Live site: renders with defaultKey first, calls Personalize async, re-renders.
|
|
149
|
+
* Page Builder: shows a preview bar above the component to switch between
|
|
150
|
+
* content variants without running actual personalization.
|
|
152
151
|
*/
|
|
153
152
|
declare function withPersonalizeConnect<P extends object>(WrappedComponent: ComponentType<P>, getConfig?: GetConfigFromProps<P>): ComponentType<P>;
|
|
154
153
|
|
package/dist/index.js
CHANGED
|
@@ -164,6 +164,11 @@ var ITEM_QUERY = `
|
|
|
164
164
|
}
|
|
165
165
|
}
|
|
166
166
|
`;
|
|
167
|
+
function formatGuidForEdge(id) {
|
|
168
|
+
const clean = id.replace(/[{}\-\s]/g, "").toLowerCase();
|
|
169
|
+
if (clean.length !== 32 || !/^[0-9a-f]+$/.test(clean)) return id;
|
|
170
|
+
return `{${clean.slice(0, 8)}-${clean.slice(8, 12)}-${clean.slice(12, 16)}-${clean.slice(16, 20)}-${clean.slice(20)}}`;
|
|
171
|
+
}
|
|
167
172
|
function mapFields(fields) {
|
|
168
173
|
const result = {};
|
|
169
174
|
for (const field of fields) {
|
|
@@ -174,13 +179,14 @@ function mapFields(fields) {
|
|
|
174
179
|
return result;
|
|
175
180
|
}
|
|
176
181
|
async function queryEdge(url, headers, datasourceId, language) {
|
|
177
|
-
|
|
182
|
+
const formattedId = formatGuidForEdge(datasourceId);
|
|
183
|
+
log("Edge GraphQL request:", { url, datasourceId, formattedId, language });
|
|
178
184
|
const res = await fetch(url, {
|
|
179
185
|
method: "POST",
|
|
180
186
|
headers: { "Content-Type": "application/json", ...headers },
|
|
181
187
|
body: JSON.stringify({
|
|
182
188
|
query: ITEM_QUERY,
|
|
183
|
-
variables: { itemId:
|
|
189
|
+
variables: { itemId: formattedId, language }
|
|
184
190
|
})
|
|
185
191
|
});
|
|
186
192
|
log("Edge GraphQL response status:", res.status);
|
|
@@ -312,9 +318,12 @@ function parseConfigJson(json, renderingId) {
|
|
|
312
318
|
if (!contentMap || typeof contentMap !== "object" || !friendlyId) return null;
|
|
313
319
|
const keys = Object.keys(contentMap);
|
|
314
320
|
return {
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
321
|
+
config: {
|
|
322
|
+
friendlyId,
|
|
323
|
+
contentMap,
|
|
324
|
+
defaultKey: raw.defaultKey ?? keys[0] ?? ""
|
|
325
|
+
},
|
|
326
|
+
instanceId: raw.instanceId ?? void 0
|
|
318
327
|
};
|
|
319
328
|
} catch {
|
|
320
329
|
warn("Failed to parse config JSON for rendering", renderingId);
|
|
@@ -388,8 +397,13 @@ async function loadPageConfigs(edgeUrl, pageItemId, language, headers = {}, site
|
|
|
388
397
|
const normalizedRid = normalizeGuid(renderingId);
|
|
389
398
|
const parsed = parseConfigJson(configJson, normalizedRid);
|
|
390
399
|
if (parsed) {
|
|
391
|
-
configs.set(normalizedRid, parsed);
|
|
392
|
-
log("Config loader:
|
|
400
|
+
configs.set(normalizedRid, parsed.config);
|
|
401
|
+
log("Config loader: stored under renderingId", normalizedRid, "\u2192", parsed.config.friendlyId);
|
|
402
|
+
if (parsed.instanceId) {
|
|
403
|
+
const normalizedIid = normalizeGuid(parsed.instanceId);
|
|
404
|
+
configs.set(normalizedIid, parsed.config);
|
|
405
|
+
log("Config loader: also stored under instanceId", normalizedIid);
|
|
406
|
+
}
|
|
393
407
|
}
|
|
394
408
|
}
|
|
395
409
|
log("Config loader: total configs loaded:", configs.size);
|
|
@@ -736,35 +750,100 @@ function getRenderingUid(props) {
|
|
|
736
750
|
const rendering = props.rendering;
|
|
737
751
|
return rendering?.uid;
|
|
738
752
|
}
|
|
739
|
-
var
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
753
|
+
var BAR_STYLE = {
|
|
754
|
+
display: "flex",
|
|
755
|
+
alignItems: "center",
|
|
756
|
+
gap: "6px",
|
|
757
|
+
padding: "6px 12px",
|
|
758
|
+
background: "linear-gradient(135deg, #6B5CE7 0%, #8B7CF7 100%)",
|
|
759
|
+
color: "#fff",
|
|
760
|
+
fontSize: "12px",
|
|
761
|
+
fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif",
|
|
762
|
+
fontWeight: 500,
|
|
763
|
+
borderRadius: "6px 6px 0 0",
|
|
748
764
|
zIndex: 9999,
|
|
765
|
+
flexWrap: "wrap"
|
|
766
|
+
};
|
|
767
|
+
var LABEL_STYLE = {
|
|
749
768
|
display: "flex",
|
|
750
769
|
alignItems: "center",
|
|
751
770
|
gap: "4px",
|
|
752
|
-
|
|
771
|
+
marginRight: "6px",
|
|
772
|
+
fontSize: "11px",
|
|
773
|
+
opacity: 0.85,
|
|
774
|
+
whiteSpace: "nowrap"
|
|
775
|
+
};
|
|
776
|
+
var TAB_BASE = {
|
|
777
|
+
padding: "3px 10px",
|
|
753
778
|
fontSize: "11px",
|
|
754
779
|
fontWeight: 600,
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
780
|
+
border: "1px solid rgba(255,255,255,0.4)",
|
|
781
|
+
borderRadius: "12px",
|
|
782
|
+
cursor: "pointer",
|
|
783
|
+
transition: "all 0.15s ease",
|
|
759
784
|
whiteSpace: "nowrap",
|
|
760
|
-
|
|
785
|
+
lineHeight: "16px"
|
|
786
|
+
};
|
|
787
|
+
var TAB_ACTIVE = {
|
|
788
|
+
...TAB_BASE,
|
|
789
|
+
background: "#fff",
|
|
790
|
+
color: "#6B5CE7",
|
|
791
|
+
border: "1px solid #fff"
|
|
792
|
+
};
|
|
793
|
+
var TAB_INACTIVE = {
|
|
794
|
+
...TAB_BASE,
|
|
795
|
+
background: "rgba(255,255,255,0.15)",
|
|
796
|
+
color: "#fff"
|
|
761
797
|
};
|
|
798
|
+
var LOADING_STYLE = {
|
|
799
|
+
marginLeft: "auto",
|
|
800
|
+
fontSize: "10px",
|
|
801
|
+
opacity: 0.7
|
|
802
|
+
};
|
|
803
|
+
function PreviewBar({
|
|
804
|
+
config,
|
|
805
|
+
activeKey,
|
|
806
|
+
isLoading,
|
|
807
|
+
onSelect
|
|
808
|
+
}) {
|
|
809
|
+
const keys = Object.keys(config.contentMap);
|
|
810
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: BAR_STYLE, "data-personalize-connect-bar": config.friendlyId, children: [
|
|
811
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { style: LABEL_STYLE, children: [
|
|
812
|
+
"\u26A1 ",
|
|
813
|
+
config.friendlyId
|
|
814
|
+
] }),
|
|
815
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
816
|
+
"button",
|
|
817
|
+
{
|
|
818
|
+
type: "button",
|
|
819
|
+
style: activeKey === null ? TAB_ACTIVE : TAB_INACTIVE,
|
|
820
|
+
onClick: () => onSelect(null),
|
|
821
|
+
children: "Original"
|
|
822
|
+
}
|
|
823
|
+
),
|
|
824
|
+
keys.map((key) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
825
|
+
"button",
|
|
826
|
+
{
|
|
827
|
+
type: "button",
|
|
828
|
+
style: activeKey === key ? TAB_ACTIVE : TAB_INACTIVE,
|
|
829
|
+
onClick: () => onSelect(key),
|
|
830
|
+
children: key
|
|
831
|
+
},
|
|
832
|
+
key
|
|
833
|
+
)),
|
|
834
|
+
isLoading && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: LOADING_STYLE, children: "loading..." })
|
|
835
|
+
] });
|
|
836
|
+
}
|
|
762
837
|
function withPersonalizeConnect(WrappedComponent, getConfig = DEFAULT_GET_CONFIG) {
|
|
763
838
|
const componentName = WrappedComponent.displayName ?? WrappedComponent.name ?? "Component";
|
|
764
839
|
function PersonalizeConnectWrapper(props) {
|
|
765
840
|
const context = usePersonalizeContext();
|
|
766
841
|
const [resolvedFields, setResolvedFields] = (0, import_react2.useState)(null);
|
|
842
|
+
const [previewKey, setPreviewKey] = (0, import_react2.useState)(null);
|
|
843
|
+
const [previewFields, setPreviewFields] = (0, import_react2.useState)(null);
|
|
844
|
+
const [previewLoading, setPreviewLoading] = (0, import_react2.useState)(false);
|
|
767
845
|
const mountedRef = (0, import_react2.useRef)(true);
|
|
846
|
+
const previewCacheRef = (0, import_react2.useRef)(/* @__PURE__ */ new Map());
|
|
768
847
|
let config = getConfig(props);
|
|
769
848
|
if (!config && context) {
|
|
770
849
|
const uid = getRenderingUid(props);
|
|
@@ -793,8 +872,43 @@ function withPersonalizeConnect(WrappedComponent, getConfig = DEFAULT_GET_CONFIG
|
|
|
793
872
|
if (config) {
|
|
794
873
|
log(`[${componentName}] Config active:`, { friendlyId: config.friendlyId, defaultKey: config.defaultKey, keys: Object.keys(config.contentMap) });
|
|
795
874
|
}
|
|
796
|
-
const
|
|
875
|
+
const isEditing = context?.isEditing ?? false;
|
|
876
|
+
const handlePreviewSelect = (0, import_react2.useCallback)(async (key) => {
|
|
797
877
|
if (!config || !context) return;
|
|
878
|
+
setPreviewKey(key);
|
|
879
|
+
if (key === null) {
|
|
880
|
+
setPreviewFields(null);
|
|
881
|
+
return;
|
|
882
|
+
}
|
|
883
|
+
const cached = previewCacheRef.current.get(key);
|
|
884
|
+
if (cached) {
|
|
885
|
+
setPreviewFields(cached);
|
|
886
|
+
return;
|
|
887
|
+
}
|
|
888
|
+
const datasourceId = config.contentMap[key];
|
|
889
|
+
if (!datasourceId) {
|
|
890
|
+
warn(`[${componentName}] Preview: no datasource for key "${key}"`);
|
|
891
|
+
setPreviewFields(null);
|
|
892
|
+
return;
|
|
893
|
+
}
|
|
894
|
+
setPreviewLoading(true);
|
|
895
|
+
log(`[${componentName}] Preview: resolving datasource for key "${key}" \u2192`, datasourceId);
|
|
896
|
+
try {
|
|
897
|
+
const fields = await context.resolveDatasource(datasourceId);
|
|
898
|
+
if (mountedRef.current) {
|
|
899
|
+
previewCacheRef.current.set(key, fields);
|
|
900
|
+
setPreviewFields(fields);
|
|
901
|
+
log(`[${componentName}] Preview: resolved fields for "${key}":`, Object.keys(fields));
|
|
902
|
+
}
|
|
903
|
+
} catch (e) {
|
|
904
|
+
warn(`[${componentName}] Preview: failed to resolve datasource for "${key}"`, e);
|
|
905
|
+
if (mountedRef.current) setPreviewFields(null);
|
|
906
|
+
} finally {
|
|
907
|
+
if (mountedRef.current) setPreviewLoading(false);
|
|
908
|
+
}
|
|
909
|
+
}, [config, context]);
|
|
910
|
+
const runPersonalization = (0, import_react2.useCallback)(async () => {
|
|
911
|
+
if (!config || !context || isEditing) return;
|
|
798
912
|
group(`[${componentName}] personalization flow`);
|
|
799
913
|
log("Calling Personalize for experience:", config.friendlyId);
|
|
800
914
|
const contentKey = await callPersonalize({ config, context });
|
|
@@ -822,29 +936,37 @@ function withPersonalizeConnect(WrappedComponent, getConfig = DEFAULT_GET_CONFIG
|
|
|
822
936
|
warn(`[${componentName}] Content resolution returned null \u2014 component keeps original fields`);
|
|
823
937
|
}
|
|
824
938
|
groupEnd();
|
|
825
|
-
}, [config, context]);
|
|
939
|
+
}, [config, context, isEditing]);
|
|
826
940
|
(0, import_react2.useEffect)(() => {
|
|
827
941
|
mountedRef.current = true;
|
|
828
|
-
if (config && context && context.browserId) {
|
|
942
|
+
if (config && context && context.browserId && !isEditing) {
|
|
829
943
|
runPersonalization();
|
|
830
944
|
}
|
|
831
945
|
return () => {
|
|
832
946
|
mountedRef.current = false;
|
|
833
947
|
};
|
|
834
|
-
}, [config?.friendlyId, context?.browserId, context?.configsLoaded, runPersonalization]);
|
|
948
|
+
}, [config?.friendlyId, context?.browserId, context?.configsLoaded, isEditing, runPersonalization]);
|
|
835
949
|
if (!config || !context) {
|
|
836
950
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(WrappedComponent, { ...props });
|
|
837
951
|
}
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
952
|
+
if (isEditing) {
|
|
953
|
+
const editingFields = previewKey !== null ? previewFields : null;
|
|
954
|
+
const editingProps = editingFields ? { ...props, fields: editingFields } : props;
|
|
955
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react2.Fragment, { children: [
|
|
956
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
957
|
+
PreviewBar,
|
|
958
|
+
{
|
|
959
|
+
config,
|
|
960
|
+
activeKey: previewKey,
|
|
961
|
+
isLoading: previewLoading,
|
|
962
|
+
onSelect: handlePreviewSelect
|
|
963
|
+
}
|
|
964
|
+
),
|
|
965
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(WrappedComponent, { ...editingProps })
|
|
845
966
|
] });
|
|
846
967
|
}
|
|
847
|
-
|
|
968
|
+
const mergedProps = resolvedFields ? { ...props, fields: resolvedFields } : props;
|
|
969
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(WrappedComponent, { ...mergedProps });
|
|
848
970
|
}
|
|
849
971
|
PersonalizeConnectWrapper.displayName = `WithPersonalizeConnect(${componentName})`;
|
|
850
972
|
return PersonalizeConnectWrapper;
|
package/dist/index.mjs
CHANGED
|
@@ -123,6 +123,11 @@ var ITEM_QUERY = `
|
|
|
123
123
|
}
|
|
124
124
|
}
|
|
125
125
|
`;
|
|
126
|
+
function formatGuidForEdge(id) {
|
|
127
|
+
const clean = id.replace(/[{}\-\s]/g, "").toLowerCase();
|
|
128
|
+
if (clean.length !== 32 || !/^[0-9a-f]+$/.test(clean)) return id;
|
|
129
|
+
return `{${clean.slice(0, 8)}-${clean.slice(8, 12)}-${clean.slice(12, 16)}-${clean.slice(16, 20)}-${clean.slice(20)}}`;
|
|
130
|
+
}
|
|
126
131
|
function mapFields(fields) {
|
|
127
132
|
const result = {};
|
|
128
133
|
for (const field of fields) {
|
|
@@ -133,13 +138,14 @@ function mapFields(fields) {
|
|
|
133
138
|
return result;
|
|
134
139
|
}
|
|
135
140
|
async function queryEdge(url, headers, datasourceId, language) {
|
|
136
|
-
|
|
141
|
+
const formattedId = formatGuidForEdge(datasourceId);
|
|
142
|
+
log("Edge GraphQL request:", { url, datasourceId, formattedId, language });
|
|
137
143
|
const res = await fetch(url, {
|
|
138
144
|
method: "POST",
|
|
139
145
|
headers: { "Content-Type": "application/json", ...headers },
|
|
140
146
|
body: JSON.stringify({
|
|
141
147
|
query: ITEM_QUERY,
|
|
142
|
-
variables: { itemId:
|
|
148
|
+
variables: { itemId: formattedId, language }
|
|
143
149
|
})
|
|
144
150
|
});
|
|
145
151
|
log("Edge GraphQL response status:", res.status);
|
|
@@ -271,9 +277,12 @@ function parseConfigJson(json, renderingId) {
|
|
|
271
277
|
if (!contentMap || typeof contentMap !== "object" || !friendlyId) return null;
|
|
272
278
|
const keys = Object.keys(contentMap);
|
|
273
279
|
return {
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
280
|
+
config: {
|
|
281
|
+
friendlyId,
|
|
282
|
+
contentMap,
|
|
283
|
+
defaultKey: raw.defaultKey ?? keys[0] ?? ""
|
|
284
|
+
},
|
|
285
|
+
instanceId: raw.instanceId ?? void 0
|
|
277
286
|
};
|
|
278
287
|
} catch {
|
|
279
288
|
warn("Failed to parse config JSON for rendering", renderingId);
|
|
@@ -347,8 +356,13 @@ async function loadPageConfigs(edgeUrl, pageItemId, language, headers = {}, site
|
|
|
347
356
|
const normalizedRid = normalizeGuid(renderingId);
|
|
348
357
|
const parsed = parseConfigJson(configJson, normalizedRid);
|
|
349
358
|
if (parsed) {
|
|
350
|
-
configs.set(normalizedRid, parsed);
|
|
351
|
-
log("Config loader:
|
|
359
|
+
configs.set(normalizedRid, parsed.config);
|
|
360
|
+
log("Config loader: stored under renderingId", normalizedRid, "\u2192", parsed.config.friendlyId);
|
|
361
|
+
if (parsed.instanceId) {
|
|
362
|
+
const normalizedIid = normalizeGuid(parsed.instanceId);
|
|
363
|
+
configs.set(normalizedIid, parsed.config);
|
|
364
|
+
log("Config loader: also stored under instanceId", normalizedIid);
|
|
365
|
+
}
|
|
352
366
|
}
|
|
353
367
|
}
|
|
354
368
|
log("Config loader: total configs loaded:", configs.size);
|
|
@@ -685,7 +699,7 @@ async function resolveContent(options) {
|
|
|
685
699
|
}
|
|
686
700
|
|
|
687
701
|
// src/withPersonalizeConnect.tsx
|
|
688
|
-
import { useCallback as useCallback2, useEffect as useEffect2, useRef, useState as useState2 } from "react";
|
|
702
|
+
import { Fragment, useCallback as useCallback2, useEffect as useEffect2, useRef, useState as useState2 } from "react";
|
|
689
703
|
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
690
704
|
var DEFAULT_GET_CONFIG = (props) => props.rendering?.personalizeConnect;
|
|
691
705
|
function normalizeGuid2(id) {
|
|
@@ -695,35 +709,100 @@ function getRenderingUid(props) {
|
|
|
695
709
|
const rendering = props.rendering;
|
|
696
710
|
return rendering?.uid;
|
|
697
711
|
}
|
|
698
|
-
var
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
712
|
+
var BAR_STYLE = {
|
|
713
|
+
display: "flex",
|
|
714
|
+
alignItems: "center",
|
|
715
|
+
gap: "6px",
|
|
716
|
+
padding: "6px 12px",
|
|
717
|
+
background: "linear-gradient(135deg, #6B5CE7 0%, #8B7CF7 100%)",
|
|
718
|
+
color: "#fff",
|
|
719
|
+
fontSize: "12px",
|
|
720
|
+
fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif",
|
|
721
|
+
fontWeight: 500,
|
|
722
|
+
borderRadius: "6px 6px 0 0",
|
|
707
723
|
zIndex: 9999,
|
|
724
|
+
flexWrap: "wrap"
|
|
725
|
+
};
|
|
726
|
+
var LABEL_STYLE = {
|
|
708
727
|
display: "flex",
|
|
709
728
|
alignItems: "center",
|
|
710
729
|
gap: "4px",
|
|
711
|
-
|
|
730
|
+
marginRight: "6px",
|
|
731
|
+
fontSize: "11px",
|
|
732
|
+
opacity: 0.85,
|
|
733
|
+
whiteSpace: "nowrap"
|
|
734
|
+
};
|
|
735
|
+
var TAB_BASE = {
|
|
736
|
+
padding: "3px 10px",
|
|
712
737
|
fontSize: "11px",
|
|
713
738
|
fontWeight: 600,
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
739
|
+
border: "1px solid rgba(255,255,255,0.4)",
|
|
740
|
+
borderRadius: "12px",
|
|
741
|
+
cursor: "pointer",
|
|
742
|
+
transition: "all 0.15s ease",
|
|
718
743
|
whiteSpace: "nowrap",
|
|
719
|
-
|
|
744
|
+
lineHeight: "16px"
|
|
745
|
+
};
|
|
746
|
+
var TAB_ACTIVE = {
|
|
747
|
+
...TAB_BASE,
|
|
748
|
+
background: "#fff",
|
|
749
|
+
color: "#6B5CE7",
|
|
750
|
+
border: "1px solid #fff"
|
|
751
|
+
};
|
|
752
|
+
var TAB_INACTIVE = {
|
|
753
|
+
...TAB_BASE,
|
|
754
|
+
background: "rgba(255,255,255,0.15)",
|
|
755
|
+
color: "#fff"
|
|
720
756
|
};
|
|
757
|
+
var LOADING_STYLE = {
|
|
758
|
+
marginLeft: "auto",
|
|
759
|
+
fontSize: "10px",
|
|
760
|
+
opacity: 0.7
|
|
761
|
+
};
|
|
762
|
+
function PreviewBar({
|
|
763
|
+
config,
|
|
764
|
+
activeKey,
|
|
765
|
+
isLoading,
|
|
766
|
+
onSelect
|
|
767
|
+
}) {
|
|
768
|
+
const keys = Object.keys(config.contentMap);
|
|
769
|
+
return /* @__PURE__ */ jsxs("div", { style: BAR_STYLE, "data-personalize-connect-bar": config.friendlyId, children: [
|
|
770
|
+
/* @__PURE__ */ jsxs("span", { style: LABEL_STYLE, children: [
|
|
771
|
+
"\u26A1 ",
|
|
772
|
+
config.friendlyId
|
|
773
|
+
] }),
|
|
774
|
+
/* @__PURE__ */ jsx2(
|
|
775
|
+
"button",
|
|
776
|
+
{
|
|
777
|
+
type: "button",
|
|
778
|
+
style: activeKey === null ? TAB_ACTIVE : TAB_INACTIVE,
|
|
779
|
+
onClick: () => onSelect(null),
|
|
780
|
+
children: "Original"
|
|
781
|
+
}
|
|
782
|
+
),
|
|
783
|
+
keys.map((key) => /* @__PURE__ */ jsx2(
|
|
784
|
+
"button",
|
|
785
|
+
{
|
|
786
|
+
type: "button",
|
|
787
|
+
style: activeKey === key ? TAB_ACTIVE : TAB_INACTIVE,
|
|
788
|
+
onClick: () => onSelect(key),
|
|
789
|
+
children: key
|
|
790
|
+
},
|
|
791
|
+
key
|
|
792
|
+
)),
|
|
793
|
+
isLoading && /* @__PURE__ */ jsx2("span", { style: LOADING_STYLE, children: "loading..." })
|
|
794
|
+
] });
|
|
795
|
+
}
|
|
721
796
|
function withPersonalizeConnect(WrappedComponent, getConfig = DEFAULT_GET_CONFIG) {
|
|
722
797
|
const componentName = WrappedComponent.displayName ?? WrappedComponent.name ?? "Component";
|
|
723
798
|
function PersonalizeConnectWrapper(props) {
|
|
724
799
|
const context = usePersonalizeContext();
|
|
725
800
|
const [resolvedFields, setResolvedFields] = useState2(null);
|
|
801
|
+
const [previewKey, setPreviewKey] = useState2(null);
|
|
802
|
+
const [previewFields, setPreviewFields] = useState2(null);
|
|
803
|
+
const [previewLoading, setPreviewLoading] = useState2(false);
|
|
726
804
|
const mountedRef = useRef(true);
|
|
805
|
+
const previewCacheRef = useRef(/* @__PURE__ */ new Map());
|
|
727
806
|
let config = getConfig(props);
|
|
728
807
|
if (!config && context) {
|
|
729
808
|
const uid = getRenderingUid(props);
|
|
@@ -752,8 +831,43 @@ function withPersonalizeConnect(WrappedComponent, getConfig = DEFAULT_GET_CONFIG
|
|
|
752
831
|
if (config) {
|
|
753
832
|
log(`[${componentName}] Config active:`, { friendlyId: config.friendlyId, defaultKey: config.defaultKey, keys: Object.keys(config.contentMap) });
|
|
754
833
|
}
|
|
755
|
-
const
|
|
834
|
+
const isEditing = context?.isEditing ?? false;
|
|
835
|
+
const handlePreviewSelect = useCallback2(async (key) => {
|
|
756
836
|
if (!config || !context) return;
|
|
837
|
+
setPreviewKey(key);
|
|
838
|
+
if (key === null) {
|
|
839
|
+
setPreviewFields(null);
|
|
840
|
+
return;
|
|
841
|
+
}
|
|
842
|
+
const cached = previewCacheRef.current.get(key);
|
|
843
|
+
if (cached) {
|
|
844
|
+
setPreviewFields(cached);
|
|
845
|
+
return;
|
|
846
|
+
}
|
|
847
|
+
const datasourceId = config.contentMap[key];
|
|
848
|
+
if (!datasourceId) {
|
|
849
|
+
warn(`[${componentName}] Preview: no datasource for key "${key}"`);
|
|
850
|
+
setPreviewFields(null);
|
|
851
|
+
return;
|
|
852
|
+
}
|
|
853
|
+
setPreviewLoading(true);
|
|
854
|
+
log(`[${componentName}] Preview: resolving datasource for key "${key}" \u2192`, datasourceId);
|
|
855
|
+
try {
|
|
856
|
+
const fields = await context.resolveDatasource(datasourceId);
|
|
857
|
+
if (mountedRef.current) {
|
|
858
|
+
previewCacheRef.current.set(key, fields);
|
|
859
|
+
setPreviewFields(fields);
|
|
860
|
+
log(`[${componentName}] Preview: resolved fields for "${key}":`, Object.keys(fields));
|
|
861
|
+
}
|
|
862
|
+
} catch (e) {
|
|
863
|
+
warn(`[${componentName}] Preview: failed to resolve datasource for "${key}"`, e);
|
|
864
|
+
if (mountedRef.current) setPreviewFields(null);
|
|
865
|
+
} finally {
|
|
866
|
+
if (mountedRef.current) setPreviewLoading(false);
|
|
867
|
+
}
|
|
868
|
+
}, [config, context]);
|
|
869
|
+
const runPersonalization = useCallback2(async () => {
|
|
870
|
+
if (!config || !context || isEditing) return;
|
|
757
871
|
group(`[${componentName}] personalization flow`);
|
|
758
872
|
log("Calling Personalize for experience:", config.friendlyId);
|
|
759
873
|
const contentKey = await callPersonalize({ config, context });
|
|
@@ -781,29 +895,37 @@ function withPersonalizeConnect(WrappedComponent, getConfig = DEFAULT_GET_CONFIG
|
|
|
781
895
|
warn(`[${componentName}] Content resolution returned null \u2014 component keeps original fields`);
|
|
782
896
|
}
|
|
783
897
|
groupEnd();
|
|
784
|
-
}, [config, context]);
|
|
898
|
+
}, [config, context, isEditing]);
|
|
785
899
|
useEffect2(() => {
|
|
786
900
|
mountedRef.current = true;
|
|
787
|
-
if (config && context && context.browserId) {
|
|
901
|
+
if (config && context && context.browserId && !isEditing) {
|
|
788
902
|
runPersonalization();
|
|
789
903
|
}
|
|
790
904
|
return () => {
|
|
791
905
|
mountedRef.current = false;
|
|
792
906
|
};
|
|
793
|
-
}, [config?.friendlyId, context?.browserId, context?.configsLoaded, runPersonalization]);
|
|
907
|
+
}, [config?.friendlyId, context?.browserId, context?.configsLoaded, isEditing, runPersonalization]);
|
|
794
908
|
if (!config || !context) {
|
|
795
909
|
return /* @__PURE__ */ jsx2(WrappedComponent, { ...props });
|
|
796
910
|
}
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
911
|
+
if (isEditing) {
|
|
912
|
+
const editingFields = previewKey !== null ? previewFields : null;
|
|
913
|
+
const editingProps = editingFields ? { ...props, fields: editingFields } : props;
|
|
914
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
915
|
+
/* @__PURE__ */ jsx2(
|
|
916
|
+
PreviewBar,
|
|
917
|
+
{
|
|
918
|
+
config,
|
|
919
|
+
activeKey: previewKey,
|
|
920
|
+
isLoading: previewLoading,
|
|
921
|
+
onSelect: handlePreviewSelect
|
|
922
|
+
}
|
|
923
|
+
),
|
|
924
|
+
/* @__PURE__ */ jsx2(WrappedComponent, { ...editingProps })
|
|
804
925
|
] });
|
|
805
926
|
}
|
|
806
|
-
|
|
927
|
+
const mergedProps = resolvedFields ? { ...props, fields: resolvedFields } : props;
|
|
928
|
+
return /* @__PURE__ */ jsx2(WrappedComponent, { ...mergedProps });
|
|
807
929
|
}
|
|
808
930
|
PersonalizeConnectWrapper.displayName = `WithPersonalizeConnect(${componentName})`;
|
|
809
931
|
return PersonalizeConnectWrapper;
|
package/package.json
CHANGED