@contentful/experiences-visual-editor-react 3.7.0-dev-20250919T1400-8d76591.0 → 3.7.0-prerelease-20250917T1034-42f0486.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 +377 -574
- package/dist/index.js.map +1 -1
- package/dist/renderApp.js +8851 -9598
- package/dist/renderApp.js.map +1 -1
- package/package.json +5 -4
package/dist/index.js
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
import styleInject from 'style-inject';
|
|
2
2
|
import React, { useState, useEffect, useCallback, forwardRef, useMemo, useLayoutEffect, useRef } from 'react';
|
|
3
3
|
import { z } from 'zod';
|
|
4
|
-
import cloneDeep from 'lodash
|
|
4
|
+
import { cloneDeep, omit, isArray, isEqual, get as get$2, debounce } from 'lodash-es';
|
|
5
5
|
import md5 from 'md5';
|
|
6
6
|
import { BLOCKS } from '@contentful/rich-text-types';
|
|
7
7
|
import { create, useStore } from 'zustand';
|
|
8
8
|
import { produce } from 'immer';
|
|
9
|
-
import { isEqual, get as get$2, debounce } from 'lodash-es';
|
|
10
9
|
import '@contentful/rich-text-react-renderer';
|
|
11
10
|
|
|
12
|
-
var css_248z$
|
|
13
|
-
styleInject(css_248z$
|
|
11
|
+
var css_248z$a = "html,\nbody {\n margin: 0;\n padding: 0;\n}\n\n/*\n * All of these variables are tokens from Forma-36 and should not be adjusted as these\n * are global variables that may affect multiple places.\n * As our customers may use other design libraries, we try to avoid overlapping global\n * variables by always using the prefix `--exp-builder-` inside this SDK.\n */\n\n:root {\n /* Color tokens from Forma 36: https://f36.contentful.com/tokens/color-system */\n --exp-builder-blue100: #e8f5ff;\n --exp-builder-blue200: #ceecff;\n --exp-builder-blue300: #98cbff;\n --exp-builder-blue400: #40a0ff;\n --exp-builder-blue500: #036fe3;\n --exp-builder-blue600: #0059c8;\n --exp-builder-blue700: #0041ab;\n --exp-builder-blue800: #003298;\n --exp-builder-blue900: #002a8e;\n --exp-builder-gray100: #f7f9fa;\n --exp-builder-gray200: #e7ebee;\n --exp-builder-gray300: #cfd9e0;\n --exp-builder-gray400: #aec1cc;\n --exp-builder-gray500: #67728a;\n --exp-builder-gray600: #5a657c;\n --exp-builder-gray700: #414d63;\n --exp-builder-gray800: #1b273a;\n --exp-builder-gray900: #111b2b;\n --exp-builder-purple600: #6c3ecf;\n --exp-builder-red200: #ffe0e0;\n --exp-builder-red800: #7f0010;\n --exp-builder-color-white: #ffffff;\n --exp-builder-glow-primary: 0px 0px 0px 3px #e8f5ff;\n\n /* RGB colors for applying opacity */\n --exp-builder-blue100-rgb: 232, 245, 255;\n --exp-builder-blue300-rgb: 152, 203, 255;\n\n /* Spacing tokens from Forma 36: https://f36.contentful.com/tokens/spacing */\n --exp-builder-spacing-s: 0.75rem;\n --exp-builder-spacing-2xs: 0.25rem;\n\n /* Typography tokens from Forma 36: https://f36.contentful.com/tokens/typography */\n --exp-builder-font-size-l: 1rem;\n --exp-builder-font-size-m: 0.875rem;\n --exp-builder-font-stack-primary: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial,\n sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol;\n --exp-builder-line-height-condensed: 1.25;\n}\n";
|
|
12
|
+
styleInject(css_248z$a);
|
|
14
13
|
|
|
15
14
|
/** @deprecated will be removed when dropping backward compatibility for old DND */
|
|
16
15
|
const INCOMING_EVENTS$1 = {
|
|
@@ -34,9 +33,7 @@ const INCOMING_EVENTS$1 = {
|
|
|
34
33
|
/** @deprecated will be removed when dropping backward compatibility for old DND */
|
|
35
34
|
HoverComponent: 'hoverComponent',
|
|
36
35
|
UpdatedEntity: 'updatedEntity',
|
|
37
|
-
/** @deprecated not needed after `patternResolution` was introduced. Will be removed in the next major version. */
|
|
38
36
|
AssembliesAdded: 'assembliesAdded',
|
|
39
|
-
/** @deprecated not needed after `patternResolution` was introduced. Will be removed in the next major version. */
|
|
40
37
|
AssembliesRegistered: 'assembliesRegistered',
|
|
41
38
|
/** @deprecated will be removed when dropping backward compatibility for old DND */
|
|
42
39
|
MouseMove: 'mouseMove',
|
|
@@ -99,7 +96,6 @@ const CONTENTFUL_COMPONENTS$1 = {
|
|
|
99
96
|
name: 'Carousel',
|
|
100
97
|
},
|
|
101
98
|
};
|
|
102
|
-
const ASSEMBLY_DEFAULT_CATEGORY = 'Assemblies';
|
|
103
99
|
const CF_STYLE_ATTRIBUTES = [
|
|
104
100
|
'cfVisibility',
|
|
105
101
|
'cfHorizontalAlignment',
|
|
@@ -758,7 +754,7 @@ const BreakpointSchema$1 = z
|
|
|
758
754
|
id: propertyKeySchema$1,
|
|
759
755
|
// Can be replace with z.templateLiteral when upgrading to zod v4
|
|
760
756
|
query: z.string().refine((s) => BREAKPOINT_QUERY_REGEX$1.test(s)),
|
|
761
|
-
previewSize: z.string()
|
|
757
|
+
previewSize: z.string(),
|
|
762
758
|
displayName: z.string(),
|
|
763
759
|
displayIcon: z.enum(['desktop', 'tablet', 'mobile']).optional(),
|
|
764
760
|
})
|
|
@@ -795,6 +791,19 @@ const ComponentVariableSchema$1 = z.object({
|
|
|
795
791
|
});
|
|
796
792
|
const ComponentTreeNodeSchema$1 = BaseComponentTreeNodeSchema$1.extend({
|
|
797
793
|
children: z.lazy(() => ComponentTreeNodeSchema$1.array()),
|
|
794
|
+
}).superRefine(({ id, prebindingId, parameters }, ctx) => {
|
|
795
|
+
if (prebindingId && !parameters) {
|
|
796
|
+
ctx.addIssue({
|
|
797
|
+
code: z.ZodIssueCode.custom,
|
|
798
|
+
message: `Found "prebindingId" but no "parameters" for node with id: "${id}"`,
|
|
799
|
+
});
|
|
800
|
+
}
|
|
801
|
+
if (parameters && !prebindingId) {
|
|
802
|
+
ctx.addIssue({
|
|
803
|
+
code: z.ZodIssueCode.custom,
|
|
804
|
+
message: `Found "parameters" but no "prebindingId" for node with id: "${id}"`,
|
|
805
|
+
});
|
|
806
|
+
}
|
|
798
807
|
});
|
|
799
808
|
const ComponentTreeSchema$1 = z
|
|
800
809
|
.object({
|
|
@@ -812,25 +821,6 @@ z.object({
|
|
|
812
821
|
usedComponents: localeWrapper$1(UsedComponentsSchema$1).optional(),
|
|
813
822
|
});
|
|
814
823
|
|
|
815
|
-
function treeVisit$1$1(initialNode, onNode) {
|
|
816
|
-
const _treeVisit = (currentNode) => {
|
|
817
|
-
const children = [...currentNode.children];
|
|
818
|
-
onNode(currentNode);
|
|
819
|
-
for (const child of children) {
|
|
820
|
-
_treeVisit(child);
|
|
821
|
-
}
|
|
822
|
-
};
|
|
823
|
-
if (Array.isArray(initialNode)) {
|
|
824
|
-
for (const node of initialNode) {
|
|
825
|
-
_treeVisit(node);
|
|
826
|
-
}
|
|
827
|
-
}
|
|
828
|
-
else {
|
|
829
|
-
_treeVisit(initialNode);
|
|
830
|
-
}
|
|
831
|
-
}
|
|
832
|
-
|
|
833
|
-
const MAX_ALLOWED_PATHS$1 = 200;
|
|
834
824
|
const THUMBNAIL_IDS$1 = [
|
|
835
825
|
'columns',
|
|
836
826
|
'columnsPlusRight',
|
|
@@ -861,17 +851,7 @@ const THUMBNAIL_IDS$1 = [
|
|
|
861
851
|
const VariableMappingSchema$1 = z.object({
|
|
862
852
|
parameterId: propertyKeySchema$1,
|
|
863
853
|
type: z.literal('ContentTypeMapping'),
|
|
864
|
-
pathsByContentType: z
|
|
865
|
-
.record(z.string(), z.object({ path: z.string() }))
|
|
866
|
-
.superRefine((paths, ctx) => {
|
|
867
|
-
const variableId = ctx.path[ctx.path.length - 2];
|
|
868
|
-
if (Object.keys(paths).length > MAX_ALLOWED_PATHS$1) {
|
|
869
|
-
ctx.addIssue({
|
|
870
|
-
code: z.ZodIssueCode.custom,
|
|
871
|
-
message: `Too many paths defined for variable mapping with id "${variableId}", maximum allowed is ${MAX_ALLOWED_PATHS$1}`,
|
|
872
|
-
});
|
|
873
|
-
}
|
|
874
|
-
}),
|
|
854
|
+
pathsByContentType: z.record(z.string(), z.object({ path: z.string() })),
|
|
875
855
|
});
|
|
876
856
|
const PassToNodeSchema$1 = z
|
|
877
857
|
.object({
|
|
@@ -895,10 +875,7 @@ const ParameterDefinitionSchema$1 = z.object({
|
|
|
895
875
|
})
|
|
896
876
|
.optional(),
|
|
897
877
|
contentTypes: z.array(z.string()).min(1),
|
|
898
|
-
passToNodes: z
|
|
899
|
-
.array(PassToNodeSchema$1)
|
|
900
|
-
.max(1, 'At most one "passToNodes" element is allowed per parameter definition.')
|
|
901
|
-
.optional(), // we might change this to be empty array for native parameter definitions, that's why we don't use .length(1)
|
|
878
|
+
passToNodes: z.array(PassToNodeSchema$1).optional(),
|
|
902
879
|
});
|
|
903
880
|
const ParameterDefinitionsSchema$1 = z.record(propertyKeySchema$1, ParameterDefinitionSchema$1);
|
|
904
881
|
const VariableMappingsSchema$1 = z.record(propertyKeySchema$1, VariableMappingSchema$1);
|
|
@@ -919,108 +896,14 @@ const ComponentSettingsSchema$1 = z
|
|
|
919
896
|
category: z.string().max(50, 'Category must contain at most 50 characters').optional(),
|
|
920
897
|
prebindingDefinitions: z.array(PrebindingDefinitionSchema$1).length(1).optional(),
|
|
921
898
|
})
|
|
922
|
-
.strict()
|
|
923
|
-
|
|
924
|
-
const { variableDefinitions, prebindingDefinitions } = componentSettings;
|
|
925
|
-
if (!prebindingDefinitions || prebindingDefinitions.length === 0) {
|
|
926
|
-
return;
|
|
927
|
-
}
|
|
928
|
-
const { parameterDefinitions, variableMappings, allowedVariableOverrides } = prebindingDefinitions[0];
|
|
929
|
-
validateAtMostOneNativeParameterDefinition$1(parameterDefinitions, ctx);
|
|
930
|
-
validateNoOverlapBetweenMappingAndOverrides$1(variableMappings, allowedVariableOverrides, ctx);
|
|
931
|
-
validateMappingsAgainstVariableDefinitions$1(variableMappings, allowedVariableOverrides, variableDefinitions, ctx);
|
|
932
|
-
validateMappingsAgainstParameterDefinitions$1(variableMappings, parameterDefinitions, ctx);
|
|
933
|
-
});
|
|
934
|
-
z
|
|
935
|
-
.object({
|
|
899
|
+
.strict();
|
|
900
|
+
z.object({
|
|
936
901
|
componentTree: localeWrapper$1(ComponentTreeSchema$1),
|
|
937
902
|
dataSource: localeWrapper$1(DataSourceSchema$1),
|
|
938
903
|
unboundValues: localeWrapper$1(UnboundValuesSchema$1),
|
|
939
904
|
usedComponents: localeWrapper$1(UsedComponentsSchema$1).optional(),
|
|
940
905
|
componentSettings: localeWrapper$1(ComponentSettingsSchema$1),
|
|
941
|
-
})
|
|
942
|
-
.superRefine((patternFields, ctx) => {
|
|
943
|
-
const { componentTree, componentSettings } = patternFields;
|
|
944
|
-
// values at this point are wrapped under locale code
|
|
945
|
-
const nonLocalisedComponentTree = Object.values(componentTree)[0];
|
|
946
|
-
const nonLocalisedComponentSettings = Object.values(componentSettings)[0];
|
|
947
|
-
if (!nonLocalisedComponentSettings || !nonLocalisedComponentTree) {
|
|
948
|
-
return;
|
|
949
|
-
}
|
|
950
|
-
validatePassToNodes$1(nonLocalisedComponentTree.children || [], nonLocalisedComponentSettings || {}, ctx);
|
|
951
906
|
});
|
|
952
|
-
const validateAtMostOneNativeParameterDefinition$1 = (parameterDefinitions, ctx) => {
|
|
953
|
-
const nativeParamDefinitions = Object.values(parameterDefinitions).filter((paramDefinition) => !(paramDefinition.passToNodes && paramDefinition.passToNodes.length > 0));
|
|
954
|
-
if (nativeParamDefinitions.length > 1) {
|
|
955
|
-
ctx.addIssue({
|
|
956
|
-
code: z.ZodIssueCode.custom,
|
|
957
|
-
message: `Only one native parameter definition (parameter definition without passToNodes) is allowed per prebinding definition.`,
|
|
958
|
-
});
|
|
959
|
-
}
|
|
960
|
-
};
|
|
961
|
-
const validateNoOverlapBetweenMappingAndOverrides$1 = (variableMappings, allowedVariableOverrides, ctx) => {
|
|
962
|
-
const variableMappingKeys = Object.keys(variableMappings || {});
|
|
963
|
-
const overridesSet = new Set(allowedVariableOverrides || []);
|
|
964
|
-
const overlap = variableMappingKeys.filter((key) => overridesSet.has(key));
|
|
965
|
-
if (overlap.length > 0) {
|
|
966
|
-
ctx.addIssue({
|
|
967
|
-
code: z.ZodIssueCode.custom,
|
|
968
|
-
message: `Found both variable mapping and allowed override for the following keys: ${overlap.map((key) => `"${key}"`).join(', ')}.`,
|
|
969
|
-
});
|
|
970
|
-
}
|
|
971
|
-
};
|
|
972
|
-
const validateMappingsAgainstVariableDefinitions$1 = (variableMappings, allowedVariableOverrides, variableDefinitions, ctx) => {
|
|
973
|
-
const nonDesignVariableDefinitionKeys = Object.entries(variableDefinitions)
|
|
974
|
-
.filter(([_, def]) => def.group !== 'style')
|
|
975
|
-
.map(([key]) => key);
|
|
976
|
-
const variableMappingKeys = Object.keys(variableMappings || {});
|
|
977
|
-
const allKeys = [...variableMappingKeys, ...(allowedVariableOverrides || [])];
|
|
978
|
-
const invalidMappings = allKeys.filter((key) => !nonDesignVariableDefinitionKeys.includes(key));
|
|
979
|
-
if (invalidMappings.length > 0) {
|
|
980
|
-
ctx.addIssue({
|
|
981
|
-
code: z.ZodIssueCode.custom,
|
|
982
|
-
message: `The following variable mappings or overrides are missing from the variable definitions: ${invalidMappings.map((key) => `"${key}"`).join(', ')}.`,
|
|
983
|
-
});
|
|
984
|
-
}
|
|
985
|
-
};
|
|
986
|
-
const validateMappingsAgainstParameterDefinitions$1 = (variableMappings, parameterDefinitions, ctx) => {
|
|
987
|
-
const parameterDefinitionKeys = Object.keys(parameterDefinitions || {});
|
|
988
|
-
for (const [mappingKey, mappingValue] of Object.entries(variableMappings || {})) {
|
|
989
|
-
if (!parameterDefinitionKeys.includes(mappingValue.parameterId)) {
|
|
990
|
-
ctx.addIssue({
|
|
991
|
-
code: z.ZodIssueCode.custom,
|
|
992
|
-
message: `The variable mapping with id "${mappingKey}" references a non-existing parameterId "${mappingValue.parameterId}".`,
|
|
993
|
-
});
|
|
994
|
-
}
|
|
995
|
-
}
|
|
996
|
-
};
|
|
997
|
-
const validatePassToNodes$1 = (rootChildren, componentSettings, ctx) => {
|
|
998
|
-
if (!componentSettings.prebindingDefinitions ||
|
|
999
|
-
componentSettings.prebindingDefinitions.length === 0) {
|
|
1000
|
-
return;
|
|
1001
|
-
}
|
|
1002
|
-
const { parameterDefinitions } = componentSettings.prebindingDefinitions[0];
|
|
1003
|
-
let nodeIds = new Set();
|
|
1004
|
-
for (const paramDef of Object.values(parameterDefinitions || {})) {
|
|
1005
|
-
paramDef.passToNodes?.forEach((n) => nodeIds.add(n.nodeId));
|
|
1006
|
-
}
|
|
1007
|
-
treeVisit$1$1(rootChildren, (node) => {
|
|
1008
|
-
if (!node.id)
|
|
1009
|
-
return;
|
|
1010
|
-
if (nodeIds.has(node.id)) {
|
|
1011
|
-
nodeIds.delete(node.id);
|
|
1012
|
-
}
|
|
1013
|
-
});
|
|
1014
|
-
if (nodeIds.size > 0) {
|
|
1015
|
-
const stringifiedNodeIds = Array.from(nodeIds)
|
|
1016
|
-
.map((id) => `"${id}"`)
|
|
1017
|
-
.join(', ');
|
|
1018
|
-
ctx.addIssue({
|
|
1019
|
-
code: z.ZodIssueCode.custom,
|
|
1020
|
-
message: `The following node IDs referenced in passToNodes are not present in the component tree: ${stringifiedNodeIds}.`,
|
|
1021
|
-
});
|
|
1022
|
-
}
|
|
1023
|
-
};
|
|
1024
907
|
|
|
1025
908
|
z
|
|
1026
909
|
.object({
|
|
@@ -1687,9 +1570,7 @@ const stylesToRemove = CF_STYLE_ATTRIBUTES.filter((style) => !stylesToKeep.inclu
|
|
|
1687
1570
|
// cfWrapColumns & cfWrapColumnsCount are no real style attributes as they are handled on the editor side
|
|
1688
1571
|
const propsToRemove = ['cfSsrClassName', 'cfWrapColumns', 'cfWrapColumnsCount'];
|
|
1689
1572
|
const sanitizeNodeProps = (nodeProps) => {
|
|
1690
|
-
|
|
1691
|
-
const sanitizedProps = Object.fromEntries(Object.entries(nodeProps).filter(([key]) => !keysToRemove.includes(key)));
|
|
1692
|
-
return sanitizedProps;
|
|
1573
|
+
return omit(nodeProps, stylesToRemove, propsToRemove);
|
|
1693
1574
|
};
|
|
1694
1575
|
|
|
1695
1576
|
/** Turn the visibility value into a style object that can be used for inline styles in React */
|
|
@@ -2168,7 +2049,7 @@ function getArrayValue(entryOrAsset, path, entityStore) {
|
|
|
2168
2049
|
}
|
|
2169
2050
|
const fieldName = path.split('/').slice(2, -1);
|
|
2170
2051
|
const arrayValue = get$1(entryOrAsset, fieldName);
|
|
2171
|
-
if (!
|
|
2052
|
+
if (!isArray(arrayValue)) {
|
|
2172
2053
|
debug$1.warn(`[experiences-core::getArrayValue] A field '${fieldName}' of an entity was bound to an Array variable. Expected value of that field to be an array, but got: ${JSON.stringify(arrayValue)}`, { entity: entryOrAsset });
|
|
2173
2054
|
return;
|
|
2174
2055
|
}
|
|
@@ -2285,19 +2166,6 @@ function getTargetValueInPixels(targetWidthObject) {
|
|
|
2285
2166
|
return targetWidthObject.value;
|
|
2286
2167
|
}
|
|
2287
2168
|
}
|
|
2288
|
-
/**
|
|
2289
|
-
* Creates a component definition for an assembly. As all assemblies use the same definition in the SDK,
|
|
2290
|
-
* all should be registered via this function.
|
|
2291
|
-
*/
|
|
2292
|
-
const createAssemblyDefinition = (definitionId) => {
|
|
2293
|
-
return {
|
|
2294
|
-
id: definitionId,
|
|
2295
|
-
name: 'Component',
|
|
2296
|
-
variables: {},
|
|
2297
|
-
children: true,
|
|
2298
|
-
category: ASSEMBLY_DEFAULT_CATEGORY,
|
|
2299
|
-
};
|
|
2300
|
-
};
|
|
2301
2169
|
|
|
2302
2170
|
class ParseError extends Error {
|
|
2303
2171
|
constructor(message) {
|
|
@@ -2933,9 +2801,6 @@ class DeepReference {
|
|
|
2933
2801
|
return new DeepReference(opt);
|
|
2934
2802
|
}
|
|
2935
2803
|
}
|
|
2936
|
-
/**
|
|
2937
|
-
* used in editor mode. for delivery mode see `gatherDeepReferencesFromExperienceEntry`
|
|
2938
|
-
*/
|
|
2939
2804
|
function gatherDeepReferencesFromTree(startingNode, dataSource, getEntityFromLink) {
|
|
2940
2805
|
const deepReferences = [];
|
|
2941
2806
|
treeVisit(startingNode, (node) => {
|
|
@@ -3206,9 +3071,7 @@ const INCOMING_EVENTS = {
|
|
|
3206
3071
|
/** @deprecated will be removed when dropping backward compatibility for old DND */
|
|
3207
3072
|
HoverComponent: 'hoverComponent',
|
|
3208
3073
|
UpdatedEntity: 'updatedEntity',
|
|
3209
|
-
/** @deprecated not needed after `patternResolution` was introduced. Will be removed in the next major version. */
|
|
3210
3074
|
AssembliesAdded: 'assembliesAdded',
|
|
3211
|
-
/** @deprecated not needed after `patternResolution` was introduced. Will be removed in the next major version. */
|
|
3212
3075
|
AssembliesRegistered: 'assembliesRegistered',
|
|
3213
3076
|
/** @deprecated will be removed when dropping backward compatibility for old DND */
|
|
3214
3077
|
MouseMove: 'mouseMove',
|
|
@@ -3233,6 +3096,7 @@ var StudioCanvasMode$2;
|
|
|
3233
3096
|
StudioCanvasMode["NONE"] = "none";
|
|
3234
3097
|
})(StudioCanvasMode$2 || (StudioCanvasMode$2 = {}));
|
|
3235
3098
|
const ASSEMBLY_NODE_TYPE = 'assembly';
|
|
3099
|
+
const ASSEMBLY_DEFAULT_CATEGORY = 'Assemblies';
|
|
3236
3100
|
const ASSEMBLY_BLOCK_NODE_TYPE = 'assemblyBlock';
|
|
3237
3101
|
const EMPTY_CONTAINER_SIZE = '80px';
|
|
3238
3102
|
const HYPERLINK_DEFAULT_PATTERN = `/{locale}/{entry.fields.slug}/`;
|
|
@@ -3391,7 +3255,34 @@ const useBreakpoints = (breakpoints) => {
|
|
|
3391
3255
|
return { resolveDesignValue };
|
|
3392
3256
|
};
|
|
3393
3257
|
|
|
3258
|
+
// Note: During development, the hot reloading might empty this and it
|
|
3259
|
+
// stays empty leading to not rendering assemblies. Ideally, this is
|
|
3260
|
+
// integrated into the state machine to keep track of its state.
|
|
3261
|
+
const assembliesRegistry = new Map([]);
|
|
3262
|
+
const setAssemblies = (assemblies) => {
|
|
3263
|
+
for (const assembly of assemblies) {
|
|
3264
|
+
assembliesRegistry.set(assembly.sys.id, assembly);
|
|
3265
|
+
}
|
|
3266
|
+
};
|
|
3394
3267
|
const componentRegistry = new Map();
|
|
3268
|
+
const addComponentRegistration = (componentRegistration) => {
|
|
3269
|
+
componentRegistry.set(componentRegistration.definition.id, componentRegistration);
|
|
3270
|
+
};
|
|
3271
|
+
const createAssemblyRegistration = ({ definitionId, definitionName, component, }) => {
|
|
3272
|
+
const componentRegistration = componentRegistry.get(definitionId);
|
|
3273
|
+
if (componentRegistration) {
|
|
3274
|
+
return componentRegistration;
|
|
3275
|
+
}
|
|
3276
|
+
const definition = {
|
|
3277
|
+
id: definitionId,
|
|
3278
|
+
name: definitionName || 'Component',
|
|
3279
|
+
variables: {},
|
|
3280
|
+
children: true,
|
|
3281
|
+
category: ASSEMBLY_DEFAULT_CATEGORY,
|
|
3282
|
+
};
|
|
3283
|
+
addComponentRegistration({ component, definition });
|
|
3284
|
+
return componentRegistry.get(definitionId);
|
|
3285
|
+
};
|
|
3395
3286
|
|
|
3396
3287
|
const useEditorStore = create((set, get) => ({
|
|
3397
3288
|
dataSource: {},
|
|
@@ -3431,278 +3322,6 @@ const useEditorStore = create((set, get) => ({
|
|
|
3431
3322
|
},
|
|
3432
3323
|
}));
|
|
3433
3324
|
|
|
3434
|
-
function useEditorSubscriber(inMemoryEntitiesStore) {
|
|
3435
|
-
const entityStore = inMemoryEntitiesStore((state) => state.entityStore);
|
|
3436
|
-
const areEntitiesFetched = inMemoryEntitiesStore((state) => state.areEntitiesFetched);
|
|
3437
|
-
const setEntitiesFetched = inMemoryEntitiesStore((state) => state.setEntitiesFetched);
|
|
3438
|
-
const resetEntityStore = inMemoryEntitiesStore((state) => state.resetEntityStore);
|
|
3439
|
-
const { updateTree, updateNodesByUpdatedEntity } = useTreeStore((state) => ({
|
|
3440
|
-
updateTree: state.updateTree,
|
|
3441
|
-
updateNodesByUpdatedEntity: state.updateNodesByUpdatedEntity,
|
|
3442
|
-
}));
|
|
3443
|
-
const unboundValues = useEditorStore((state) => state.unboundValues);
|
|
3444
|
-
const dataSource = useEditorStore((state) => state.dataSource);
|
|
3445
|
-
const setLocale = useEditorStore((state) => state.setLocale);
|
|
3446
|
-
const setUnboundValues = useEditorStore((state) => state.setUnboundValues);
|
|
3447
|
-
const setDataSource = useEditorStore((state) => state.setDataSource);
|
|
3448
|
-
const reloadApp = () => {
|
|
3449
|
-
sendMessage(OUTGOING_EVENTS.CanvasReload, undefined);
|
|
3450
|
-
// Wait a moment to ensure that the message was sent
|
|
3451
|
-
setTimeout(() => {
|
|
3452
|
-
// Received a hot reload message from webpack dev server -> reload the canvas
|
|
3453
|
-
window.location.reload();
|
|
3454
|
-
}, 50);
|
|
3455
|
-
};
|
|
3456
|
-
useEffect(() => {
|
|
3457
|
-
sendMessage(OUTGOING_EVENTS.RequestComponentTreeUpdate, undefined);
|
|
3458
|
-
}, []);
|
|
3459
|
-
/**
|
|
3460
|
-
* Fills up entityStore with entities from newDataSource and from the tree.
|
|
3461
|
-
* Also manages "entity status" variables (areEntitiesFetched, isFetchingEntities)
|
|
3462
|
-
*/
|
|
3463
|
-
const fetchMissingEntities = useCallback(async (entityStore, newDataSource, tree) => {
|
|
3464
|
-
// if we realize that there's nothing missing and nothing to fill-fetch before we do any async call,
|
|
3465
|
-
// then we can simply return and not lock the EntityStore at all.
|
|
3466
|
-
const startFetching = () => {
|
|
3467
|
-
setEntitiesFetched(false);
|
|
3468
|
-
};
|
|
3469
|
-
const endFetching = () => {
|
|
3470
|
-
setEntitiesFetched(true);
|
|
3471
|
-
};
|
|
3472
|
-
// Prepare L1 entities and deepReferences
|
|
3473
|
-
const entityLinksL1 = Object.values(newDataSource);
|
|
3474
|
-
/**
|
|
3475
|
-
* Checks only for _missing_ L1 entities
|
|
3476
|
-
* WARNING: Does NOT check for entity staleness/versions. If an entity is stale, it will NOT be considered missing.
|
|
3477
|
-
* If ExperienceBuilder wants to update stale entities, it should post `▼UPDATED_ENTITY` message to SDK.
|
|
3478
|
-
*/
|
|
3479
|
-
const isMissingL1Entities = (entityLinks) => {
|
|
3480
|
-
const { missingAssetIds, missingEntryIds } = entityStore.getMissingEntityIds(entityLinks);
|
|
3481
|
-
return Boolean(missingAssetIds.length) || Boolean(missingEntryIds.length);
|
|
3482
|
-
};
|
|
3483
|
-
/**
|
|
3484
|
-
* PRECONDITION: all L1 entities are fetched
|
|
3485
|
-
*/
|
|
3486
|
-
const isMissingL2Entities = (deepReferences) => {
|
|
3487
|
-
const referentLinks = deepReferences
|
|
3488
|
-
.map((deepReference) => deepReference.extractReferent(entityStore))
|
|
3489
|
-
.filter(isLink$1);
|
|
3490
|
-
const { missingAssetIds, missingEntryIds } = entityStore.getMissingEntityIds(referentLinks);
|
|
3491
|
-
return Boolean(missingAssetIds.length) || Boolean(missingEntryIds.length);
|
|
3492
|
-
};
|
|
3493
|
-
/**
|
|
3494
|
-
* POST_CONDITION: entityStore is has all L1 entities (aka headEntities)
|
|
3495
|
-
*/
|
|
3496
|
-
const fillupL1 = async ({ entityLinksL1, }) => {
|
|
3497
|
-
const { missingAssetIds, missingEntryIds } = entityStore.getMissingEntityIds(entityLinksL1);
|
|
3498
|
-
await entityStore.fetchEntities({ missingAssetIds, missingEntryIds });
|
|
3499
|
-
};
|
|
3500
|
-
/**
|
|
3501
|
-
* PRECONDITION: all L1 entites are fetched
|
|
3502
|
-
*/
|
|
3503
|
-
const fillupL2 = async ({ deepReferences }) => {
|
|
3504
|
-
const referentLinks = deepReferences
|
|
3505
|
-
.map((deepReference) => deepReference.extractReferent(entityStore))
|
|
3506
|
-
.filter(isLink$1);
|
|
3507
|
-
const { missingAssetIds, missingEntryIds } = entityStore.getMissingEntityIds(referentLinks);
|
|
3508
|
-
await entityStore.fetchEntities({ missingAssetIds, missingEntryIds });
|
|
3509
|
-
};
|
|
3510
|
-
try {
|
|
3511
|
-
if (isMissingL1Entities(entityLinksL1)) {
|
|
3512
|
-
startFetching();
|
|
3513
|
-
await fillupL1({ entityLinksL1 });
|
|
3514
|
-
}
|
|
3515
|
-
const deepReferences = gatherDeepReferencesFromTree(tree.root, newDataSource, entityStore.getEntityFromLink.bind(entityStore));
|
|
3516
|
-
if (isMissingL2Entities(deepReferences)) {
|
|
3517
|
-
startFetching();
|
|
3518
|
-
await fillupL2({ deepReferences });
|
|
3519
|
-
}
|
|
3520
|
-
}
|
|
3521
|
-
catch (error) {
|
|
3522
|
-
debug$1.error('[experiences-visual-editor-react::useEditorSubscriber] Failed fetching entities', { error });
|
|
3523
|
-
throw error; // TODO: The original catch didn't let's rethrow; for the moment throw to see if we have any errors
|
|
3524
|
-
}
|
|
3525
|
-
finally {
|
|
3526
|
-
endFetching();
|
|
3527
|
-
}
|
|
3528
|
-
}, [setEntitiesFetched]);
|
|
3529
|
-
useEffect(() => {
|
|
3530
|
-
const onMessage = async (event) => {
|
|
3531
|
-
let reason;
|
|
3532
|
-
if ((reason = doesMismatchMessageSchema(event))) {
|
|
3533
|
-
if (event.origin.startsWith('http://localhost') &&
|
|
3534
|
-
`${event.data}`.includes('webpackHotUpdate')) {
|
|
3535
|
-
reloadApp();
|
|
3536
|
-
}
|
|
3537
|
-
else {
|
|
3538
|
-
debug$1.warn(`[experiences-visual-editor-react::onMessage] Ignoring alien incoming message from origin [${event.origin}], due to: [${reason}]`, event);
|
|
3539
|
-
}
|
|
3540
|
-
return;
|
|
3541
|
-
}
|
|
3542
|
-
const eventData = tryParseMessage(event);
|
|
3543
|
-
debug$1.debug(`[experiences-visual-editor-react::onMessage] Received message [${eventData.eventType}]`, eventData);
|
|
3544
|
-
if (eventData.eventType === PostMessageMethods$2.REQUESTED_ENTITIES) {
|
|
3545
|
-
// Expected message: This message is handled in the EntityStore to store fetched entities
|
|
3546
|
-
return;
|
|
3547
|
-
}
|
|
3548
|
-
switch (eventData.eventType) {
|
|
3549
|
-
case INCOMING_EVENTS.ExperienceUpdated: {
|
|
3550
|
-
const { tree, locale, changedNode, changedValueType } = eventData.payload;
|
|
3551
|
-
let newEntityStore = entityStore;
|
|
3552
|
-
if (entityStore.locale !== locale) {
|
|
3553
|
-
newEntityStore = new EditorModeEntityStore({ locale, entities: [] });
|
|
3554
|
-
setLocale(locale);
|
|
3555
|
-
resetEntityStore(newEntityStore);
|
|
3556
|
-
}
|
|
3557
|
-
// Below are mutually exclusive cases
|
|
3558
|
-
if (changedNode) {
|
|
3559
|
-
/**
|
|
3560
|
-
* On single node updates, we want to skip the process of getting the data (datasource and unbound values)
|
|
3561
|
-
* from tree. Since we know the updated node, we can skip that recursion everytime the tree updates and
|
|
3562
|
-
* just update the relevant data we need from the relevant node.
|
|
3563
|
-
*
|
|
3564
|
-
* We still update the tree here so we don't have a stale "tree"
|
|
3565
|
-
*/
|
|
3566
|
-
if (changedValueType === 'BoundValue') {
|
|
3567
|
-
const newDataSource = { ...dataSource, ...changedNode.data.dataSource };
|
|
3568
|
-
setDataSource(newDataSource);
|
|
3569
|
-
await fetchMissingEntities(newEntityStore, newDataSource, tree);
|
|
3570
|
-
}
|
|
3571
|
-
else if (changedValueType === 'UnboundValue') {
|
|
3572
|
-
setUnboundValues({
|
|
3573
|
-
...unboundValues,
|
|
3574
|
-
...changedNode.data.unboundValues,
|
|
3575
|
-
});
|
|
3576
|
-
}
|
|
3577
|
-
}
|
|
3578
|
-
else {
|
|
3579
|
-
const { dataSource, unboundValues } = getDataFromTree(tree);
|
|
3580
|
-
setDataSource(dataSource);
|
|
3581
|
-
setUnboundValues(unboundValues);
|
|
3582
|
-
await fetchMissingEntities(newEntityStore, dataSource, tree);
|
|
3583
|
-
}
|
|
3584
|
-
// Update the tree when all necessary data is fetched and ready for rendering.
|
|
3585
|
-
updateTree(tree);
|
|
3586
|
-
break;
|
|
3587
|
-
}
|
|
3588
|
-
case INCOMING_EVENTS.AssembliesRegistered: {
|
|
3589
|
-
// Not necessary anymore since `patternResolution` which was introduced in 2024.
|
|
3590
|
-
break;
|
|
3591
|
-
}
|
|
3592
|
-
case INCOMING_EVENTS.AssembliesAdded: {
|
|
3593
|
-
// Not necessary anymore since `patternResolution` which was introduced in 2024.
|
|
3594
|
-
break;
|
|
3595
|
-
}
|
|
3596
|
-
case INCOMING_EVENTS.UpdatedEntity: {
|
|
3597
|
-
const { entity: updatedEntity, shouldRerender } = eventData.payload;
|
|
3598
|
-
if (updatedEntity) {
|
|
3599
|
-
const storedEntity = entityStore.entities.find((entity) => entity.sys.id === updatedEntity.sys.id);
|
|
3600
|
-
const didEntityChange = storedEntity?.sys.version !== updatedEntity.sys.version;
|
|
3601
|
-
entityStore.updateEntity(updatedEntity);
|
|
3602
|
-
// We traverse the whole tree, so this is a opt-in feature to only use it when required.
|
|
3603
|
-
if (shouldRerender && didEntityChange) {
|
|
3604
|
-
updateNodesByUpdatedEntity(updatedEntity.sys.id);
|
|
3605
|
-
}
|
|
3606
|
-
}
|
|
3607
|
-
break;
|
|
3608
|
-
}
|
|
3609
|
-
case INCOMING_EVENTS.RequestEditorMode: {
|
|
3610
|
-
break;
|
|
3611
|
-
}
|
|
3612
|
-
default: {
|
|
3613
|
-
const knownEvents = Object.values(INCOMING_EVENTS);
|
|
3614
|
-
const isDeprecatedMessage = knownEvents.includes(eventData.eventType);
|
|
3615
|
-
if (!isDeprecatedMessage) {
|
|
3616
|
-
debug$1.error(`[experiences-visual-editor-react::onMessage] Logic error, unsupported eventType: [${eventData.eventType}]`);
|
|
3617
|
-
}
|
|
3618
|
-
}
|
|
3619
|
-
}
|
|
3620
|
-
};
|
|
3621
|
-
window.addEventListener('message', onMessage);
|
|
3622
|
-
return () => {
|
|
3623
|
-
window.removeEventListener('message', onMessage);
|
|
3624
|
-
};
|
|
3625
|
-
}, [
|
|
3626
|
-
entityStore,
|
|
3627
|
-
setDataSource,
|
|
3628
|
-
setLocale,
|
|
3629
|
-
dataSource,
|
|
3630
|
-
areEntitiesFetched,
|
|
3631
|
-
fetchMissingEntities,
|
|
3632
|
-
setUnboundValues,
|
|
3633
|
-
unboundValues,
|
|
3634
|
-
updateTree,
|
|
3635
|
-
updateNodesByUpdatedEntity,
|
|
3636
|
-
resetEntityStore,
|
|
3637
|
-
]);
|
|
3638
|
-
}
|
|
3639
|
-
|
|
3640
|
-
const CircularDependencyErrorPlaceholder = ({ wrappingPatternIds, ...props }) => {
|
|
3641
|
-
return (React.createElement("div", { ...props, "data-cf-node-error": "circular-pattern-dependency", style: {
|
|
3642
|
-
border: '1px solid red',
|
|
3643
|
-
background: 'rgba(255, 0, 0, 0.1)',
|
|
3644
|
-
padding: '1rem 1rem 0 1rem',
|
|
3645
|
-
width: '100%',
|
|
3646
|
-
height: '100%',
|
|
3647
|
-
} },
|
|
3648
|
-
"Circular usage of patterns detected:",
|
|
3649
|
-
React.createElement("ul", null, Array.from(wrappingPatternIds).map((patternId) => {
|
|
3650
|
-
const entryLink = { sys: { type: 'Link', linkType: 'Entry', id: patternId } };
|
|
3651
|
-
const entry = inMemoryEntities.maybeResolveLink(entryLink);
|
|
3652
|
-
const entryTitle = entry?.fields?.title;
|
|
3653
|
-
const text = entryTitle ? `${entryTitle} (${patternId})` : patternId;
|
|
3654
|
-
return React.createElement("li", { key: patternId }, text);
|
|
3655
|
-
}))));
|
|
3656
|
-
};
|
|
3657
|
-
|
|
3658
|
-
class ImportedComponentError extends Error {
|
|
3659
|
-
constructor(message) {
|
|
3660
|
-
super(message);
|
|
3661
|
-
this.name = 'ImportedComponentError';
|
|
3662
|
-
}
|
|
3663
|
-
}
|
|
3664
|
-
class ExperienceSDKError extends Error {
|
|
3665
|
-
constructor(message) {
|
|
3666
|
-
super(message);
|
|
3667
|
-
this.name = 'ExperienceSDKError';
|
|
3668
|
-
}
|
|
3669
|
-
}
|
|
3670
|
-
class ImportedComponentErrorBoundary extends React.Component {
|
|
3671
|
-
componentDidCatch(error, _errorInfo) {
|
|
3672
|
-
if (error.name === 'ImportedComponentError' || error.name === 'ExperienceSDKError') {
|
|
3673
|
-
// This error was already handled by a nested error boundary and should be passed upwards
|
|
3674
|
-
// We have to do this as we wrap every component on every layer with this error boundary and
|
|
3675
|
-
// thus an error deep in the tree bubbles through many layers of error boundaries.
|
|
3676
|
-
throw error;
|
|
3677
|
-
}
|
|
3678
|
-
// Differentiate between custom and SDK-provided components for error tracking
|
|
3679
|
-
const ErrorClass = isContentfulComponent(this.props.componentId)
|
|
3680
|
-
? ExperienceSDKError
|
|
3681
|
-
: ImportedComponentError;
|
|
3682
|
-
const err = new ErrorClass(error.message);
|
|
3683
|
-
err.stack = error.stack;
|
|
3684
|
-
throw err;
|
|
3685
|
-
}
|
|
3686
|
-
render() {
|
|
3687
|
-
return this.props.children;
|
|
3688
|
-
}
|
|
3689
|
-
}
|
|
3690
|
-
|
|
3691
|
-
const MissingComponentPlaceholder = ({ blockId }) => {
|
|
3692
|
-
return (React.createElement("div", { style: {
|
|
3693
|
-
border: '1px solid red',
|
|
3694
|
-
width: '100%',
|
|
3695
|
-
height: '100%',
|
|
3696
|
-
} },
|
|
3697
|
-
"Missing component '",
|
|
3698
|
-
blockId,
|
|
3699
|
-
"'"));
|
|
3700
|
-
};
|
|
3701
|
-
|
|
3702
|
-
var css_248z$a = ".EditorBlock-module_emptySlot__za-Bi {\n min-height: 80px;\n min-width: 80px;\n}\n";
|
|
3703
|
-
var styles$1 = {"emptySlot":"EditorBlock-module_emptySlot__za-Bi"};
|
|
3704
|
-
styleInject(css_248z$a);
|
|
3705
|
-
|
|
3706
3325
|
var css_248z$8 = ":root{--cf-color-white:#fff;--cf-color-black:#000;--cf-color-gray100:#f7f9fa;--cf-color-gray400:#aec1cc;--cf-color-gray400-rgb:174,193,204;--cf-spacing-0:0rem;--cf-spacing-1:0.125rem;--cf-spacing-2:0.25rem;--cf-spacing-3:0.375rem;--cf-spacing-4:0.5rem;--cf-spacing-5:0.625rem;--cf-spacing-6:0.75rem;--cf-spacing-7:0.875rem;--cf-spacing-8:1rem;--cf-spacing-9:1.25rem;--cf-spacing-10:1.5rem;--cf-spacing-11:1.75rem;--cf-spacing-12:2rem;--cf-spacing-13:2.25rem;--cf-text-xs:0.75rem;--cf-text-sm:0.875rem;--cf-text-base:1rem;--cf-text-lg:1.125rem;--cf-text-xl:1.25rem;--cf-text-2xl:1.5rem;--cf-text-3xl:2rem;--cf-text-4xl:2.75rem;--cf-font-light:300;--cf-font-normal:400;--cf-font-medium:500;--cf-font-semibold:600;--cf-font-bold:700;--cf-font-extra-bold:800;--cf-font-black:900;--cf-border-radius-none:0px;--cf-border-radius-sm:0.125rem;--cf-border-radius:0.25rem;--cf-border-radius-md:0.375rem;--cf-border-radius-lg:0.5rem;--cf-border-radius-xl:0.75rem;--cf-border-radius-2xl:1rem;--cf-border-radius-3xl:1.5rem;--cf-border-radius-full:9999px;--cf-max-width-full:100%;--cf-button-bg:var(--cf-color-black);--cf-button-color:var(--cf-color-white);--cf-text-color:var(--cf-color-black)}*{box-sizing:border-box}";
|
|
3707
3326
|
styleInject(css_248z$8);
|
|
3708
3327
|
|
|
@@ -3996,7 +3615,7 @@ const BreakpointSchema = z
|
|
|
3996
3615
|
id: propertyKeySchema,
|
|
3997
3616
|
// Can be replace with z.templateLiteral when upgrading to zod v4
|
|
3998
3617
|
query: z.string().refine((s) => BREAKPOINT_QUERY_REGEX.test(s)),
|
|
3999
|
-
previewSize: z.string()
|
|
3618
|
+
previewSize: z.string(),
|
|
4000
3619
|
displayName: z.string(),
|
|
4001
3620
|
displayIcon: z.enum(['desktop', 'tablet', 'mobile']).optional(),
|
|
4002
3621
|
})
|
|
@@ -4033,6 +3652,19 @@ const ComponentVariableSchema = z.object({
|
|
|
4033
3652
|
});
|
|
4034
3653
|
const ComponentTreeNodeSchema = BaseComponentTreeNodeSchema.extend({
|
|
4035
3654
|
children: z.lazy(() => ComponentTreeNodeSchema.array()),
|
|
3655
|
+
}).superRefine(({ id, prebindingId, parameters }, ctx) => {
|
|
3656
|
+
if (prebindingId && !parameters) {
|
|
3657
|
+
ctx.addIssue({
|
|
3658
|
+
code: z.ZodIssueCode.custom,
|
|
3659
|
+
message: `Found "prebindingId" but no "parameters" for node with id: "${id}"`,
|
|
3660
|
+
});
|
|
3661
|
+
}
|
|
3662
|
+
if (parameters && !prebindingId) {
|
|
3663
|
+
ctx.addIssue({
|
|
3664
|
+
code: z.ZodIssueCode.custom,
|
|
3665
|
+
message: `Found "parameters" but no "prebindingId" for node with id: "${id}"`,
|
|
3666
|
+
});
|
|
3667
|
+
}
|
|
4036
3668
|
});
|
|
4037
3669
|
const ComponentTreeSchema = z
|
|
4038
3670
|
.object({
|
|
@@ -4050,25 +3682,6 @@ z.object({
|
|
|
4050
3682
|
usedComponents: localeWrapper(UsedComponentsSchema).optional(),
|
|
4051
3683
|
});
|
|
4052
3684
|
|
|
4053
|
-
function treeVisit$1(initialNode, onNode) {
|
|
4054
|
-
const _treeVisit = (currentNode) => {
|
|
4055
|
-
const children = [...currentNode.children];
|
|
4056
|
-
onNode(currentNode);
|
|
4057
|
-
for (const child of children) {
|
|
4058
|
-
_treeVisit(child);
|
|
4059
|
-
}
|
|
4060
|
-
};
|
|
4061
|
-
if (Array.isArray(initialNode)) {
|
|
4062
|
-
for (const node of initialNode) {
|
|
4063
|
-
_treeVisit(node);
|
|
4064
|
-
}
|
|
4065
|
-
}
|
|
4066
|
-
else {
|
|
4067
|
-
_treeVisit(initialNode);
|
|
4068
|
-
}
|
|
4069
|
-
}
|
|
4070
|
-
|
|
4071
|
-
const MAX_ALLOWED_PATHS = 200;
|
|
4072
3685
|
const THUMBNAIL_IDS = [
|
|
4073
3686
|
'columns',
|
|
4074
3687
|
'columnsPlusRight',
|
|
@@ -4099,17 +3712,7 @@ const THUMBNAIL_IDS = [
|
|
|
4099
3712
|
const VariableMappingSchema = z.object({
|
|
4100
3713
|
parameterId: propertyKeySchema,
|
|
4101
3714
|
type: z.literal('ContentTypeMapping'),
|
|
4102
|
-
pathsByContentType: z
|
|
4103
|
-
.record(z.string(), z.object({ path: z.string() }))
|
|
4104
|
-
.superRefine((paths, ctx) => {
|
|
4105
|
-
const variableId = ctx.path[ctx.path.length - 2];
|
|
4106
|
-
if (Object.keys(paths).length > MAX_ALLOWED_PATHS) {
|
|
4107
|
-
ctx.addIssue({
|
|
4108
|
-
code: z.ZodIssueCode.custom,
|
|
4109
|
-
message: `Too many paths defined for variable mapping with id "${variableId}", maximum allowed is ${MAX_ALLOWED_PATHS}`,
|
|
4110
|
-
});
|
|
4111
|
-
}
|
|
4112
|
-
}),
|
|
3715
|
+
pathsByContentType: z.record(z.string(), z.object({ path: z.string() })),
|
|
4113
3716
|
});
|
|
4114
3717
|
const PassToNodeSchema = z
|
|
4115
3718
|
.object({
|
|
@@ -4133,10 +3736,7 @@ const ParameterDefinitionSchema = z.object({
|
|
|
4133
3736
|
})
|
|
4134
3737
|
.optional(),
|
|
4135
3738
|
contentTypes: z.array(z.string()).min(1),
|
|
4136
|
-
passToNodes: z
|
|
4137
|
-
.array(PassToNodeSchema)
|
|
4138
|
-
.max(1, 'At most one "passToNodes" element is allowed per parameter definition.')
|
|
4139
|
-
.optional(), // we might change this to be empty array for native parameter definitions, that's why we don't use .length(1)
|
|
3739
|
+
passToNodes: z.array(PassToNodeSchema).optional(),
|
|
4140
3740
|
});
|
|
4141
3741
|
const ParameterDefinitionsSchema = z.record(propertyKeySchema, ParameterDefinitionSchema);
|
|
4142
3742
|
const VariableMappingsSchema = z.record(propertyKeySchema, VariableMappingSchema);
|
|
@@ -4157,108 +3757,14 @@ const ComponentSettingsSchema = z
|
|
|
4157
3757
|
category: z.string().max(50, 'Category must contain at most 50 characters').optional(),
|
|
4158
3758
|
prebindingDefinitions: z.array(PrebindingDefinitionSchema).length(1).optional(),
|
|
4159
3759
|
})
|
|
4160
|
-
.strict()
|
|
4161
|
-
|
|
4162
|
-
const { variableDefinitions, prebindingDefinitions } = componentSettings;
|
|
4163
|
-
if (!prebindingDefinitions || prebindingDefinitions.length === 0) {
|
|
4164
|
-
return;
|
|
4165
|
-
}
|
|
4166
|
-
const { parameterDefinitions, variableMappings, allowedVariableOverrides } = prebindingDefinitions[0];
|
|
4167
|
-
validateAtMostOneNativeParameterDefinition(parameterDefinitions, ctx);
|
|
4168
|
-
validateNoOverlapBetweenMappingAndOverrides(variableMappings, allowedVariableOverrides, ctx);
|
|
4169
|
-
validateMappingsAgainstVariableDefinitions(variableMappings, allowedVariableOverrides, variableDefinitions, ctx);
|
|
4170
|
-
validateMappingsAgainstParameterDefinitions(variableMappings, parameterDefinitions, ctx);
|
|
4171
|
-
});
|
|
4172
|
-
z
|
|
4173
|
-
.object({
|
|
3760
|
+
.strict();
|
|
3761
|
+
z.object({
|
|
4174
3762
|
componentTree: localeWrapper(ComponentTreeSchema),
|
|
4175
3763
|
dataSource: localeWrapper(DataSourceSchema),
|
|
4176
3764
|
unboundValues: localeWrapper(UnboundValuesSchema),
|
|
4177
3765
|
usedComponents: localeWrapper(UsedComponentsSchema).optional(),
|
|
4178
3766
|
componentSettings: localeWrapper(ComponentSettingsSchema),
|
|
4179
|
-
})
|
|
4180
|
-
.superRefine((patternFields, ctx) => {
|
|
4181
|
-
const { componentTree, componentSettings } = patternFields;
|
|
4182
|
-
// values at this point are wrapped under locale code
|
|
4183
|
-
const nonLocalisedComponentTree = Object.values(componentTree)[0];
|
|
4184
|
-
const nonLocalisedComponentSettings = Object.values(componentSettings)[0];
|
|
4185
|
-
if (!nonLocalisedComponentSettings || !nonLocalisedComponentTree) {
|
|
4186
|
-
return;
|
|
4187
|
-
}
|
|
4188
|
-
validatePassToNodes(nonLocalisedComponentTree.children || [], nonLocalisedComponentSettings || {}, ctx);
|
|
4189
3767
|
});
|
|
4190
|
-
const validateAtMostOneNativeParameterDefinition = (parameterDefinitions, ctx) => {
|
|
4191
|
-
const nativeParamDefinitions = Object.values(parameterDefinitions).filter((paramDefinition) => !(paramDefinition.passToNodes && paramDefinition.passToNodes.length > 0));
|
|
4192
|
-
if (nativeParamDefinitions.length > 1) {
|
|
4193
|
-
ctx.addIssue({
|
|
4194
|
-
code: z.ZodIssueCode.custom,
|
|
4195
|
-
message: `Only one native parameter definition (parameter definition without passToNodes) is allowed per prebinding definition.`,
|
|
4196
|
-
});
|
|
4197
|
-
}
|
|
4198
|
-
};
|
|
4199
|
-
const validateNoOverlapBetweenMappingAndOverrides = (variableMappings, allowedVariableOverrides, ctx) => {
|
|
4200
|
-
const variableMappingKeys = Object.keys(variableMappings || {});
|
|
4201
|
-
const overridesSet = new Set(allowedVariableOverrides || []);
|
|
4202
|
-
const overlap = variableMappingKeys.filter((key) => overridesSet.has(key));
|
|
4203
|
-
if (overlap.length > 0) {
|
|
4204
|
-
ctx.addIssue({
|
|
4205
|
-
code: z.ZodIssueCode.custom,
|
|
4206
|
-
message: `Found both variable mapping and allowed override for the following keys: ${overlap.map((key) => `"${key}"`).join(', ')}.`,
|
|
4207
|
-
});
|
|
4208
|
-
}
|
|
4209
|
-
};
|
|
4210
|
-
const validateMappingsAgainstVariableDefinitions = (variableMappings, allowedVariableOverrides, variableDefinitions, ctx) => {
|
|
4211
|
-
const nonDesignVariableDefinitionKeys = Object.entries(variableDefinitions)
|
|
4212
|
-
.filter(([_, def]) => def.group !== 'style')
|
|
4213
|
-
.map(([key]) => key);
|
|
4214
|
-
const variableMappingKeys = Object.keys(variableMappings || {});
|
|
4215
|
-
const allKeys = [...variableMappingKeys, ...(allowedVariableOverrides || [])];
|
|
4216
|
-
const invalidMappings = allKeys.filter((key) => !nonDesignVariableDefinitionKeys.includes(key));
|
|
4217
|
-
if (invalidMappings.length > 0) {
|
|
4218
|
-
ctx.addIssue({
|
|
4219
|
-
code: z.ZodIssueCode.custom,
|
|
4220
|
-
message: `The following variable mappings or overrides are missing from the variable definitions: ${invalidMappings.map((key) => `"${key}"`).join(', ')}.`,
|
|
4221
|
-
});
|
|
4222
|
-
}
|
|
4223
|
-
};
|
|
4224
|
-
const validateMappingsAgainstParameterDefinitions = (variableMappings, parameterDefinitions, ctx) => {
|
|
4225
|
-
const parameterDefinitionKeys = Object.keys(parameterDefinitions || {});
|
|
4226
|
-
for (const [mappingKey, mappingValue] of Object.entries(variableMappings || {})) {
|
|
4227
|
-
if (!parameterDefinitionKeys.includes(mappingValue.parameterId)) {
|
|
4228
|
-
ctx.addIssue({
|
|
4229
|
-
code: z.ZodIssueCode.custom,
|
|
4230
|
-
message: `The variable mapping with id "${mappingKey}" references a non-existing parameterId "${mappingValue.parameterId}".`,
|
|
4231
|
-
});
|
|
4232
|
-
}
|
|
4233
|
-
}
|
|
4234
|
-
};
|
|
4235
|
-
const validatePassToNodes = (rootChildren, componentSettings, ctx) => {
|
|
4236
|
-
if (!componentSettings.prebindingDefinitions ||
|
|
4237
|
-
componentSettings.prebindingDefinitions.length === 0) {
|
|
4238
|
-
return;
|
|
4239
|
-
}
|
|
4240
|
-
const { parameterDefinitions } = componentSettings.prebindingDefinitions[0];
|
|
4241
|
-
let nodeIds = new Set();
|
|
4242
|
-
for (const paramDef of Object.values(parameterDefinitions || {})) {
|
|
4243
|
-
paramDef.passToNodes?.forEach((n) => nodeIds.add(n.nodeId));
|
|
4244
|
-
}
|
|
4245
|
-
treeVisit$1(rootChildren, (node) => {
|
|
4246
|
-
if (!node.id)
|
|
4247
|
-
return;
|
|
4248
|
-
if (nodeIds.has(node.id)) {
|
|
4249
|
-
nodeIds.delete(node.id);
|
|
4250
|
-
}
|
|
4251
|
-
});
|
|
4252
|
-
if (nodeIds.size > 0) {
|
|
4253
|
-
const stringifiedNodeIds = Array.from(nodeIds)
|
|
4254
|
-
.map((id) => `"${id}"`)
|
|
4255
|
-
.join(', ');
|
|
4256
|
-
ctx.addIssue({
|
|
4257
|
-
code: z.ZodIssueCode.custom,
|
|
4258
|
-
message: `The following node IDs referenced in passToNodes are not present in the component tree: ${stringifiedNodeIds}.`,
|
|
4259
|
-
});
|
|
4260
|
-
}
|
|
4261
|
-
};
|
|
4262
3768
|
|
|
4263
3769
|
z
|
|
4264
3770
|
.object({
|
|
@@ -4910,8 +4416,8 @@ var VisualEditorMode;
|
|
|
4910
4416
|
VisualEditorMode["InjectScript"] = "injectScript";
|
|
4911
4417
|
})(VisualEditorMode || (VisualEditorMode = {}));
|
|
4912
4418
|
|
|
4913
|
-
var css_248z$2 = ".contentful-container{display:flex;pointer-events:all;position:relative}.contentful-container::-webkit-scrollbar{display:none}.cf-container-wrapper{position:relative;width:100%}.contentful-container:after{align-items:center;bottom:0;color:var(--exp-builder-gray400);content:\"\";display:block;display:flex;font-family:var(--exp-builder-font-stack-primary);font-size:12px;justify-content:center;left:0;overflow-x:clip;pointer-events:none;position:absolute;right:0;top:0;z-index:1}.contentful-section-label:after{content:\"Section\"}.contentful-container-label:after{content:\"Container\"}.contentful-container-link,.contentful-container-link:active,.contentful-container-link:focus-visible,.contentful-container-link:hover,.contentful-container-link:read-write,.contentful-container-link:visited{color:inherit;outline:unset;text-decoration:unset}";
|
|
4914
|
-
styleInject(css_248z$2);
|
|
4419
|
+
var css_248z$2$1 = ".contentful-container{display:flex;pointer-events:all;position:relative}.contentful-container::-webkit-scrollbar{display:none}.cf-container-wrapper{position:relative;width:100%}.contentful-container:after{align-items:center;bottom:0;color:var(--exp-builder-gray400);content:\"\";display:block;display:flex;font-family:var(--exp-builder-font-stack-primary);font-size:12px;justify-content:center;left:0;overflow-x:clip;pointer-events:none;position:absolute;right:0;top:0;z-index:1}.contentful-section-label:after{content:\"Section\"}.contentful-container-label:after{content:\"Container\"}.contentful-container-link,.contentful-container-link:active,.contentful-container-link:focus-visible,.contentful-container-link:hover,.contentful-container-link:read-write,.contentful-container-link:visited{color:inherit;outline:unset;text-decoration:unset}";
|
|
4420
|
+
styleInject(css_248z$2$1);
|
|
4915
4421
|
|
|
4916
4422
|
const Flex = forwardRef(({ id, children, onMouseEnter, onMouseUp, onMouseLeave, onMouseDown, onClick, flex, flexBasis, flexShrink, flexDirection, gap, justifyContent, justifyItems, justifySelf, alignItems, alignSelf, alignContent, order, flexWrap, flexGrow, className, cssStyles, ...props }, ref) => {
|
|
4917
4423
|
return (React.createElement("div", { id: id, ref: ref, style: {
|
|
@@ -4948,16 +4454,315 @@ const Assembly = (props) => {
|
|
|
4948
4454
|
return React.createElement("div", { "data-test-id": "assembly", ...props, style: assemblyStyle });
|
|
4949
4455
|
};
|
|
4950
4456
|
|
|
4457
|
+
function useEditorSubscriber(inMemoryEntitiesStore) {
|
|
4458
|
+
const entityStore = inMemoryEntitiesStore((state) => state.entityStore);
|
|
4459
|
+
const areEntitiesFetched = inMemoryEntitiesStore((state) => state.areEntitiesFetched);
|
|
4460
|
+
const setEntitiesFetched = inMemoryEntitiesStore((state) => state.setEntitiesFetched);
|
|
4461
|
+
const resetEntityStore = inMemoryEntitiesStore((state) => state.resetEntityStore);
|
|
4462
|
+
const { updateTree, updateNodesByUpdatedEntity } = useTreeStore((state) => ({
|
|
4463
|
+
updateTree: state.updateTree,
|
|
4464
|
+
updateNodesByUpdatedEntity: state.updateNodesByUpdatedEntity,
|
|
4465
|
+
}));
|
|
4466
|
+
const unboundValues = useEditorStore((state) => state.unboundValues);
|
|
4467
|
+
const dataSource = useEditorStore((state) => state.dataSource);
|
|
4468
|
+
const setLocale = useEditorStore((state) => state.setLocale);
|
|
4469
|
+
const setUnboundValues = useEditorStore((state) => state.setUnboundValues);
|
|
4470
|
+
const setDataSource = useEditorStore((state) => state.setDataSource);
|
|
4471
|
+
const reloadApp = () => {
|
|
4472
|
+
sendMessage(OUTGOING_EVENTS.CanvasReload, undefined);
|
|
4473
|
+
// Wait a moment to ensure that the message was sent
|
|
4474
|
+
setTimeout(() => {
|
|
4475
|
+
// Received a hot reload message from webpack dev server -> reload the canvas
|
|
4476
|
+
window.location.reload();
|
|
4477
|
+
}, 50);
|
|
4478
|
+
};
|
|
4479
|
+
useEffect(() => {
|
|
4480
|
+
sendMessage(OUTGOING_EVENTS.RequestComponentTreeUpdate, undefined);
|
|
4481
|
+
}, []);
|
|
4482
|
+
/**
|
|
4483
|
+
* Fills up entityStore with entities from newDataSource and from the tree.
|
|
4484
|
+
* Also manages "entity status" variables (areEntitiesFetched, isFetchingEntities)
|
|
4485
|
+
*/
|
|
4486
|
+
const fetchMissingEntities = useCallback(async (entityStore, newDataSource, tree) => {
|
|
4487
|
+
// if we realize that there's nothing missing and nothing to fill-fetch before we do any async call,
|
|
4488
|
+
// then we can simply return and not lock the EntityStore at all.
|
|
4489
|
+
const startFetching = () => {
|
|
4490
|
+
setEntitiesFetched(false);
|
|
4491
|
+
};
|
|
4492
|
+
const endFetching = () => {
|
|
4493
|
+
setEntitiesFetched(true);
|
|
4494
|
+
};
|
|
4495
|
+
// Prepare L1 entities and deepReferences
|
|
4496
|
+
const entityLinksL1 = [
|
|
4497
|
+
...Object.values(newDataSource),
|
|
4498
|
+
...assembliesRegistry.values(), // we count assemblies here as "L1 entities", for convenience. Even though they're not headEntities.
|
|
4499
|
+
];
|
|
4500
|
+
/**
|
|
4501
|
+
* Checks only for _missing_ L1 entities
|
|
4502
|
+
* WARNING: Does NOT check for entity staleness/versions. If an entity is stale, it will NOT be considered missing.
|
|
4503
|
+
* If ExperienceBuilder wants to update stale entities, it should post `▼UPDATED_ENTITY` message to SDK.
|
|
4504
|
+
*/
|
|
4505
|
+
const isMissingL1Entities = (entityLinks) => {
|
|
4506
|
+
const { missingAssetIds, missingEntryIds } = entityStore.getMissingEntityIds(entityLinks);
|
|
4507
|
+
return Boolean(missingAssetIds.length) || Boolean(missingEntryIds.length);
|
|
4508
|
+
};
|
|
4509
|
+
/**
|
|
4510
|
+
* PRECONDITION: all L1 entities are fetched
|
|
4511
|
+
*/
|
|
4512
|
+
const isMissingL2Entities = (deepReferences) => {
|
|
4513
|
+
const referentLinks = deepReferences
|
|
4514
|
+
.map((deepReference) => deepReference.extractReferent(entityStore))
|
|
4515
|
+
.filter(isLink$1);
|
|
4516
|
+
const { missingAssetIds, missingEntryIds } = entityStore.getMissingEntityIds(referentLinks);
|
|
4517
|
+
return Boolean(missingAssetIds.length) || Boolean(missingEntryIds.length);
|
|
4518
|
+
};
|
|
4519
|
+
/**
|
|
4520
|
+
* POST_CONDITION: entityStore is has all L1 entities (aka headEntities)
|
|
4521
|
+
*/
|
|
4522
|
+
const fillupL1 = async ({ entityLinksL1, }) => {
|
|
4523
|
+
const { missingAssetIds, missingEntryIds } = entityStore.getMissingEntityIds(entityLinksL1);
|
|
4524
|
+
await entityStore.fetchEntities({ missingAssetIds, missingEntryIds });
|
|
4525
|
+
};
|
|
4526
|
+
/**
|
|
4527
|
+
* PRECONDITION: all L1 entites are fetched
|
|
4528
|
+
*/
|
|
4529
|
+
const fillupL2 = async ({ deepReferences }) => {
|
|
4530
|
+
const referentLinks = deepReferences
|
|
4531
|
+
.map((deepReference) => deepReference.extractReferent(entityStore))
|
|
4532
|
+
.filter(isLink$1);
|
|
4533
|
+
const { missingAssetIds, missingEntryIds } = entityStore.getMissingEntityIds(referentLinks);
|
|
4534
|
+
await entityStore.fetchEntities({ missingAssetIds, missingEntryIds });
|
|
4535
|
+
};
|
|
4536
|
+
try {
|
|
4537
|
+
if (isMissingL1Entities(entityLinksL1)) {
|
|
4538
|
+
startFetching();
|
|
4539
|
+
await fillupL1({ entityLinksL1 });
|
|
4540
|
+
}
|
|
4541
|
+
const deepReferences = gatherDeepReferencesFromTree(tree.root, newDataSource, entityStore.getEntityFromLink.bind(entityStore));
|
|
4542
|
+
if (isMissingL2Entities(deepReferences)) {
|
|
4543
|
+
startFetching();
|
|
4544
|
+
await fillupL2({ deepReferences });
|
|
4545
|
+
}
|
|
4546
|
+
}
|
|
4547
|
+
catch (error) {
|
|
4548
|
+
debug$1.error('[experiences-visual-editor-react::useEditorSubscriber] Failed fetching entities', { error });
|
|
4549
|
+
throw error; // TODO: The original catch didn't let's rethrow; for the moment throw to see if we have any errors
|
|
4550
|
+
}
|
|
4551
|
+
finally {
|
|
4552
|
+
endFetching();
|
|
4553
|
+
}
|
|
4554
|
+
}, [setEntitiesFetched /* setFetchingEntities, assembliesRegistry */]);
|
|
4555
|
+
useEffect(() => {
|
|
4556
|
+
const onMessage = async (event) => {
|
|
4557
|
+
let reason;
|
|
4558
|
+
if ((reason = doesMismatchMessageSchema(event))) {
|
|
4559
|
+
if (event.origin.startsWith('http://localhost') &&
|
|
4560
|
+
`${event.data}`.includes('webpackHotUpdate')) {
|
|
4561
|
+
reloadApp();
|
|
4562
|
+
}
|
|
4563
|
+
else {
|
|
4564
|
+
debug$1.warn(`[experiences-visual-editor-react::onMessage] Ignoring alien incoming message from origin [${event.origin}], due to: [${reason}]`, event);
|
|
4565
|
+
}
|
|
4566
|
+
return;
|
|
4567
|
+
}
|
|
4568
|
+
const eventData = tryParseMessage(event);
|
|
4569
|
+
debug$1.debug(`[experiences-visual-editor-react::onMessage] Received message [${eventData.eventType}]`, eventData);
|
|
4570
|
+
if (eventData.eventType === PostMessageMethods$2.REQUESTED_ENTITIES) {
|
|
4571
|
+
// Expected message: This message is handled in the EntityStore to store fetched entities
|
|
4572
|
+
return;
|
|
4573
|
+
}
|
|
4574
|
+
switch (eventData.eventType) {
|
|
4575
|
+
case INCOMING_EVENTS.ExperienceUpdated: {
|
|
4576
|
+
const { tree, locale, changedNode, changedValueType, assemblies } = eventData.payload;
|
|
4577
|
+
// Make sure to first store the assemblies before setting the tree and thus triggering a rerender
|
|
4578
|
+
if (assemblies) {
|
|
4579
|
+
setAssemblies(assemblies);
|
|
4580
|
+
// If the assemblyEntry is not yet fetched, this will be done below by
|
|
4581
|
+
// the imperative calls to fetchMissingEntities.
|
|
4582
|
+
}
|
|
4583
|
+
let newEntityStore = entityStore;
|
|
4584
|
+
if (entityStore.locale !== locale) {
|
|
4585
|
+
newEntityStore = new EditorModeEntityStore({ locale, entities: [] });
|
|
4586
|
+
setLocale(locale);
|
|
4587
|
+
resetEntityStore(newEntityStore);
|
|
4588
|
+
}
|
|
4589
|
+
// Below are mutually exclusive cases
|
|
4590
|
+
if (changedNode) {
|
|
4591
|
+
/**
|
|
4592
|
+
* On single node updates, we want to skip the process of getting the data (datasource and unbound values)
|
|
4593
|
+
* from tree. Since we know the updated node, we can skip that recursion everytime the tree updates and
|
|
4594
|
+
* just update the relevant data we need from the relevant node.
|
|
4595
|
+
*
|
|
4596
|
+
* We still update the tree here so we don't have a stale "tree"
|
|
4597
|
+
*/
|
|
4598
|
+
if (changedValueType === 'BoundValue') {
|
|
4599
|
+
const newDataSource = { ...dataSource, ...changedNode.data.dataSource };
|
|
4600
|
+
setDataSource(newDataSource);
|
|
4601
|
+
await fetchMissingEntities(newEntityStore, newDataSource, tree);
|
|
4602
|
+
}
|
|
4603
|
+
else if (changedValueType === 'UnboundValue') {
|
|
4604
|
+
setUnboundValues({
|
|
4605
|
+
...unboundValues,
|
|
4606
|
+
...changedNode.data.unboundValues,
|
|
4607
|
+
});
|
|
4608
|
+
}
|
|
4609
|
+
}
|
|
4610
|
+
else {
|
|
4611
|
+
const { dataSource, unboundValues } = getDataFromTree(tree);
|
|
4612
|
+
setDataSource(dataSource);
|
|
4613
|
+
setUnboundValues(unboundValues);
|
|
4614
|
+
await fetchMissingEntities(newEntityStore, dataSource, tree);
|
|
4615
|
+
}
|
|
4616
|
+
// Update the tree when all necessary data is fetched and ready for rendering.
|
|
4617
|
+
updateTree(tree);
|
|
4618
|
+
break;
|
|
4619
|
+
}
|
|
4620
|
+
case INCOMING_EVENTS.AssembliesRegistered: {
|
|
4621
|
+
const { assemblies } = eventData.payload;
|
|
4622
|
+
assemblies.forEach((definition) => {
|
|
4623
|
+
addComponentRegistration({
|
|
4624
|
+
component: Assembly,
|
|
4625
|
+
definition,
|
|
4626
|
+
});
|
|
4627
|
+
});
|
|
4628
|
+
break;
|
|
4629
|
+
}
|
|
4630
|
+
case INCOMING_EVENTS.AssembliesAdded: {
|
|
4631
|
+
const { assembly, assemblyDefinition, } = eventData.payload;
|
|
4632
|
+
entityStore.updateEntity(assembly);
|
|
4633
|
+
// Using a Map here to avoid setting state and rerending all existing assemblies when a new assembly is added
|
|
4634
|
+
// TODO: Figure out if we can extend this love to data source and unbound values. Maybe that'll solve the blink
|
|
4635
|
+
// of all bound and unbound values when new values are added
|
|
4636
|
+
assembliesRegistry.set(assembly.sys.id, {
|
|
4637
|
+
sys: { id: assembly.sys.id, linkType: 'Entry', type: 'Link' },
|
|
4638
|
+
});
|
|
4639
|
+
if (assemblyDefinition) {
|
|
4640
|
+
addComponentRegistration({
|
|
4641
|
+
component: Assembly,
|
|
4642
|
+
definition: assemblyDefinition,
|
|
4643
|
+
});
|
|
4644
|
+
}
|
|
4645
|
+
break;
|
|
4646
|
+
}
|
|
4647
|
+
case INCOMING_EVENTS.UpdatedEntity: {
|
|
4648
|
+
const { entity: updatedEntity, shouldRerender } = eventData.payload;
|
|
4649
|
+
if (updatedEntity) {
|
|
4650
|
+
const storedEntity = entityStore.entities.find((entity) => entity.sys.id === updatedEntity.sys.id);
|
|
4651
|
+
const didEntityChange = storedEntity?.sys.version !== updatedEntity.sys.version;
|
|
4652
|
+
entityStore.updateEntity(updatedEntity);
|
|
4653
|
+
// We traverse the whole tree, so this is a opt-in feature to only use it when required.
|
|
4654
|
+
if (shouldRerender && didEntityChange) {
|
|
4655
|
+
updateNodesByUpdatedEntity(updatedEntity.sys.id);
|
|
4656
|
+
}
|
|
4657
|
+
}
|
|
4658
|
+
break;
|
|
4659
|
+
}
|
|
4660
|
+
case INCOMING_EVENTS.RequestEditorMode: {
|
|
4661
|
+
break;
|
|
4662
|
+
}
|
|
4663
|
+
default: {
|
|
4664
|
+
const knownEvents = Object.values(INCOMING_EVENTS);
|
|
4665
|
+
const isDeprecatedMessage = knownEvents.includes(eventData.eventType);
|
|
4666
|
+
if (!isDeprecatedMessage) {
|
|
4667
|
+
debug$1.error(`[experiences-visual-editor-react::onMessage] Logic error, unsupported eventType: [${eventData.eventType}]`);
|
|
4668
|
+
}
|
|
4669
|
+
}
|
|
4670
|
+
}
|
|
4671
|
+
};
|
|
4672
|
+
window.addEventListener('message', onMessage);
|
|
4673
|
+
return () => {
|
|
4674
|
+
window.removeEventListener('message', onMessage);
|
|
4675
|
+
};
|
|
4676
|
+
}, [
|
|
4677
|
+
entityStore,
|
|
4678
|
+
setDataSource,
|
|
4679
|
+
setLocale,
|
|
4680
|
+
dataSource,
|
|
4681
|
+
areEntitiesFetched,
|
|
4682
|
+
fetchMissingEntities,
|
|
4683
|
+
setUnboundValues,
|
|
4684
|
+
unboundValues,
|
|
4685
|
+
updateTree,
|
|
4686
|
+
updateNodesByUpdatedEntity,
|
|
4687
|
+
resetEntityStore,
|
|
4688
|
+
]);
|
|
4689
|
+
}
|
|
4690
|
+
|
|
4691
|
+
const CircularDependencyErrorPlaceholder = ({ wrappingPatternIds, ...props }) => {
|
|
4692
|
+
return (React.createElement("div", { ...props, "data-cf-node-error": "circular-pattern-dependency", style: {
|
|
4693
|
+
border: '1px solid red',
|
|
4694
|
+
background: 'rgba(255, 0, 0, 0.1)',
|
|
4695
|
+
padding: '1rem 1rem 0 1rem',
|
|
4696
|
+
width: '100%',
|
|
4697
|
+
height: '100%',
|
|
4698
|
+
} },
|
|
4699
|
+
"Circular usage of patterns detected:",
|
|
4700
|
+
React.createElement("ul", null, Array.from(wrappingPatternIds).map((patternId) => {
|
|
4701
|
+
const entryLink = { sys: { type: 'Link', linkType: 'Entry', id: patternId } };
|
|
4702
|
+
const entry = inMemoryEntities.maybeResolveLink(entryLink);
|
|
4703
|
+
const entryTitle = entry?.fields?.title;
|
|
4704
|
+
const text = entryTitle ? `${entryTitle} (${patternId})` : patternId;
|
|
4705
|
+
return React.createElement("li", { key: patternId }, text);
|
|
4706
|
+
}))));
|
|
4707
|
+
};
|
|
4708
|
+
|
|
4709
|
+
class ImportedComponentError extends Error {
|
|
4710
|
+
constructor(message) {
|
|
4711
|
+
super(message);
|
|
4712
|
+
this.name = 'ImportedComponentError';
|
|
4713
|
+
}
|
|
4714
|
+
}
|
|
4715
|
+
class ExperienceSDKError extends Error {
|
|
4716
|
+
constructor(message) {
|
|
4717
|
+
super(message);
|
|
4718
|
+
this.name = 'ExperienceSDKError';
|
|
4719
|
+
}
|
|
4720
|
+
}
|
|
4721
|
+
class ImportedComponentErrorBoundary extends React.Component {
|
|
4722
|
+
componentDidCatch(error, _errorInfo) {
|
|
4723
|
+
if (error.name === 'ImportedComponentError' || error.name === 'ExperienceSDKError') {
|
|
4724
|
+
// This error was already handled by a nested error boundary and should be passed upwards
|
|
4725
|
+
// We have to do this as we wrap every component on every layer with this error boundary and
|
|
4726
|
+
// thus an error deep in the tree bubbles through many layers of error boundaries.
|
|
4727
|
+
throw error;
|
|
4728
|
+
}
|
|
4729
|
+
// Differentiate between custom and SDK-provided components for error tracking
|
|
4730
|
+
const ErrorClass = isContentfulComponent(this.props.componentId)
|
|
4731
|
+
? ExperienceSDKError
|
|
4732
|
+
: ImportedComponentError;
|
|
4733
|
+
const err = new ErrorClass(error.message);
|
|
4734
|
+
err.stack = error.stack;
|
|
4735
|
+
throw err;
|
|
4736
|
+
}
|
|
4737
|
+
render() {
|
|
4738
|
+
return this.props.children;
|
|
4739
|
+
}
|
|
4740
|
+
}
|
|
4741
|
+
|
|
4742
|
+
const MissingComponentPlaceholder = ({ blockId }) => {
|
|
4743
|
+
return (React.createElement("div", { style: {
|
|
4744
|
+
border: '1px solid red',
|
|
4745
|
+
width: '100%',
|
|
4746
|
+
height: '100%',
|
|
4747
|
+
} },
|
|
4748
|
+
"Missing component '",
|
|
4749
|
+
blockId,
|
|
4750
|
+
"'"));
|
|
4751
|
+
};
|
|
4752
|
+
|
|
4753
|
+
var css_248z$2 = ".EditorBlock-module_emptySlot__za-Bi {\n min-height: 80px;\n min-width: 80px;\n}\n";
|
|
4754
|
+
var styles$1 = {"emptySlot":"EditorBlock-module_emptySlot__za-Bi"};
|
|
4755
|
+
styleInject(css_248z$2);
|
|
4756
|
+
|
|
4951
4757
|
const useComponentRegistration = (node) => {
|
|
4952
4758
|
return useMemo(() => {
|
|
4953
|
-
|
|
4954
|
-
|
|
4955
|
-
|
|
4759
|
+
let registration = componentRegistry.get(node.data.blockId);
|
|
4760
|
+
if (node.type === ASSEMBLY_NODE_TYPE && !registration) {
|
|
4761
|
+
registration = createAssemblyRegistration({
|
|
4762
|
+
definitionId: node.data.blockId,
|
|
4956
4763
|
component: Assembly,
|
|
4957
|
-
|
|
4958
|
-
};
|
|
4764
|
+
});
|
|
4959
4765
|
}
|
|
4960
|
-
const registration = componentRegistry.get(node.data.blockId);
|
|
4961
4766
|
if (!registration) {
|
|
4962
4767
|
debug$1.warn(`[experiences-visual-editor-react::useComponentRegistration] Component registration not found for component with id: "${node.data.blockId}". The registered component might have been removed from the code. To proceed, remove the component manually from the layers tab.`);
|
|
4963
4768
|
return undefined;
|
|
@@ -5049,9 +4854,7 @@ const checkIsNodeVisible = (node, resolveDesignValue) => {
|
|
|
5049
4854
|
return node.children.some((childNode) => checkIsNodeVisible(childNode, resolveDesignValue));
|
|
5050
4855
|
}
|
|
5051
4856
|
// Check if the current node is visible (`cfVisibility` is enforced on all nodes)
|
|
5052
|
-
|
|
5053
|
-
return (resolveDesignValue(node.data.props['cfVisibility'].valuesByBreakpoint) !==
|
|
5054
|
-
false);
|
|
4857
|
+
return !!resolveDesignValue(node.data.props['cfVisibility'].valuesByBreakpoint);
|
|
5055
4858
|
};
|
|
5056
4859
|
|
|
5057
4860
|
const useComponentProps = ({ node, entityStore, areEntitiesFetched, resolveDesignValue, definition, options, }) => {
|