@contentful/experiences-visual-editor-react 3.7.0-prerelease-20250917T1034-42f0486.0 → 3.7.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 +1093 -896
- package/dist/index.js.map +1 -1
- package/dist/renderApp.js +9553 -8806
- package/dist/renderApp.js.map +1 -1
- package/package.json +4 -5
package/dist/index.js
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
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
|
|
4
|
+
import cloneDeep from 'lodash.clonedeep';
|
|
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';
|
|
9
10
|
import '@contentful/rich-text-react-renderer';
|
|
10
11
|
|
|
11
|
-
var css_248z$
|
|
12
|
-
styleInject(css_248z$
|
|
12
|
+
var css_248z$b = "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";
|
|
13
|
+
styleInject(css_248z$b);
|
|
13
14
|
|
|
14
15
|
/** @deprecated will be removed when dropping backward compatibility for old DND */
|
|
15
16
|
const INCOMING_EVENTS$1 = {
|
|
@@ -33,7 +34,9 @@ const INCOMING_EVENTS$1 = {
|
|
|
33
34
|
/** @deprecated will be removed when dropping backward compatibility for old DND */
|
|
34
35
|
HoverComponent: 'hoverComponent',
|
|
35
36
|
UpdatedEntity: 'updatedEntity',
|
|
37
|
+
/** @deprecated not needed after `patternResolution` was introduced. Will be removed in the next major version. */
|
|
36
38
|
AssembliesAdded: 'assembliesAdded',
|
|
39
|
+
/** @deprecated not needed after `patternResolution` was introduced. Will be removed in the next major version. */
|
|
37
40
|
AssembliesRegistered: 'assembliesRegistered',
|
|
38
41
|
/** @deprecated will be removed when dropping backward compatibility for old DND */
|
|
39
42
|
MouseMove: 'mouseMove',
|
|
@@ -96,6 +99,7 @@ const CONTENTFUL_COMPONENTS$1 = {
|
|
|
96
99
|
name: 'Carousel',
|
|
97
100
|
},
|
|
98
101
|
};
|
|
102
|
+
const ASSEMBLY_DEFAULT_CATEGORY = 'Assemblies';
|
|
99
103
|
const CF_STYLE_ATTRIBUTES = [
|
|
100
104
|
'cfVisibility',
|
|
101
105
|
'cfHorizontalAlignment',
|
|
@@ -754,7 +758,7 @@ const BreakpointSchema$1 = z
|
|
|
754
758
|
id: propertyKeySchema$1,
|
|
755
759
|
// Can be replace with z.templateLiteral when upgrading to zod v4
|
|
756
760
|
query: z.string().refine((s) => BREAKPOINT_QUERY_REGEX$1.test(s)),
|
|
757
|
-
previewSize: z.string(),
|
|
761
|
+
previewSize: z.string().optional(),
|
|
758
762
|
displayName: z.string(),
|
|
759
763
|
displayIcon: z.enum(['desktop', 'tablet', 'mobile']).optional(),
|
|
760
764
|
})
|
|
@@ -791,19 +795,6 @@ const ComponentVariableSchema$1 = z.object({
|
|
|
791
795
|
});
|
|
792
796
|
const ComponentTreeNodeSchema$1 = BaseComponentTreeNodeSchema$1.extend({
|
|
793
797
|
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
|
-
}
|
|
807
798
|
});
|
|
808
799
|
const ComponentTreeSchema$1 = z
|
|
809
800
|
.object({
|
|
@@ -821,6 +812,25 @@ z.object({
|
|
|
821
812
|
usedComponents: localeWrapper$1(UsedComponentsSchema$1).optional(),
|
|
822
813
|
});
|
|
823
814
|
|
|
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;
|
|
824
834
|
const THUMBNAIL_IDS$1 = [
|
|
825
835
|
'columns',
|
|
826
836
|
'columnsPlusRight',
|
|
@@ -851,7 +861,17 @@ const THUMBNAIL_IDS$1 = [
|
|
|
851
861
|
const VariableMappingSchema$1 = z.object({
|
|
852
862
|
parameterId: propertyKeySchema$1,
|
|
853
863
|
type: z.literal('ContentTypeMapping'),
|
|
854
|
-
pathsByContentType: z
|
|
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
|
+
}),
|
|
855
875
|
});
|
|
856
876
|
const PassToNodeSchema$1 = z
|
|
857
877
|
.object({
|
|
@@ -875,7 +895,10 @@ const ParameterDefinitionSchema$1 = z.object({
|
|
|
875
895
|
})
|
|
876
896
|
.optional(),
|
|
877
897
|
contentTypes: z.array(z.string()).min(1),
|
|
878
|
-
passToNodes: z
|
|
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)
|
|
879
902
|
});
|
|
880
903
|
const ParameterDefinitionsSchema$1 = z.record(propertyKeySchema$1, ParameterDefinitionSchema$1);
|
|
881
904
|
const VariableMappingsSchema$1 = z.record(propertyKeySchema$1, VariableMappingSchema$1);
|
|
@@ -896,14 +919,108 @@ const ComponentSettingsSchema$1 = z
|
|
|
896
919
|
category: z.string().max(50, 'Category must contain at most 50 characters').optional(),
|
|
897
920
|
prebindingDefinitions: z.array(PrebindingDefinitionSchema$1).length(1).optional(),
|
|
898
921
|
})
|
|
899
|
-
.strict()
|
|
900
|
-
|
|
922
|
+
.strict()
|
|
923
|
+
.superRefine((componentSettings, ctx) => {
|
|
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({
|
|
901
936
|
componentTree: localeWrapper$1(ComponentTreeSchema$1),
|
|
902
937
|
dataSource: localeWrapper$1(DataSourceSchema$1),
|
|
903
938
|
unboundValues: localeWrapper$1(UnboundValuesSchema$1),
|
|
904
939
|
usedComponents: localeWrapper$1(UsedComponentsSchema$1).optional(),
|
|
905
940
|
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);
|
|
906
951
|
});
|
|
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
|
+
};
|
|
907
1024
|
|
|
908
1025
|
z
|
|
909
1026
|
.object({
|
|
@@ -1570,7 +1687,9 @@ const stylesToRemove = CF_STYLE_ATTRIBUTES.filter((style) => !stylesToKeep.inclu
|
|
|
1570
1687
|
// cfWrapColumns & cfWrapColumnsCount are no real style attributes as they are handled on the editor side
|
|
1571
1688
|
const propsToRemove = ['cfSsrClassName', 'cfWrapColumns', 'cfWrapColumnsCount'];
|
|
1572
1689
|
const sanitizeNodeProps = (nodeProps) => {
|
|
1573
|
-
|
|
1690
|
+
const keysToRemove = [...stylesToRemove, ...propsToRemove];
|
|
1691
|
+
const sanitizedProps = Object.fromEntries(Object.entries(nodeProps).filter(([key]) => !keysToRemove.includes(key)));
|
|
1692
|
+
return sanitizedProps;
|
|
1574
1693
|
};
|
|
1575
1694
|
|
|
1576
1695
|
/** Turn the visibility value into a style object that can be used for inline styles in React */
|
|
@@ -2049,7 +2168,7 @@ function getArrayValue(entryOrAsset, path, entityStore) {
|
|
|
2049
2168
|
}
|
|
2050
2169
|
const fieldName = path.split('/').slice(2, -1);
|
|
2051
2170
|
const arrayValue = get$1(entryOrAsset, fieldName);
|
|
2052
|
-
if (!isArray(arrayValue)) {
|
|
2171
|
+
if (!Array.isArray(arrayValue)) {
|
|
2053
2172
|
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 });
|
|
2054
2173
|
return;
|
|
2055
2174
|
}
|
|
@@ -2166,6 +2285,19 @@ function getTargetValueInPixels(targetWidthObject) {
|
|
|
2166
2285
|
return targetWidthObject.value;
|
|
2167
2286
|
}
|
|
2168
2287
|
}
|
|
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
|
+
};
|
|
2169
2301
|
|
|
2170
2302
|
class ParseError extends Error {
|
|
2171
2303
|
constructor(message) {
|
|
@@ -2801,6 +2933,9 @@ class DeepReference {
|
|
|
2801
2933
|
return new DeepReference(opt);
|
|
2802
2934
|
}
|
|
2803
2935
|
}
|
|
2936
|
+
/**
|
|
2937
|
+
* used in editor mode. for delivery mode see `gatherDeepReferencesFromExperienceEntry`
|
|
2938
|
+
*/
|
|
2804
2939
|
function gatherDeepReferencesFromTree(startingNode, dataSource, getEntityFromLink) {
|
|
2805
2940
|
const deepReferences = [];
|
|
2806
2941
|
treeVisit(startingNode, (node) => {
|
|
@@ -3071,7 +3206,9 @@ const INCOMING_EVENTS = {
|
|
|
3071
3206
|
/** @deprecated will be removed when dropping backward compatibility for old DND */
|
|
3072
3207
|
HoverComponent: 'hoverComponent',
|
|
3073
3208
|
UpdatedEntity: 'updatedEntity',
|
|
3209
|
+
/** @deprecated not needed after `patternResolution` was introduced. Will be removed in the next major version. */
|
|
3074
3210
|
AssembliesAdded: 'assembliesAdded',
|
|
3211
|
+
/** @deprecated not needed after `patternResolution` was introduced. Will be removed in the next major version. */
|
|
3075
3212
|
AssembliesRegistered: 'assembliesRegistered',
|
|
3076
3213
|
/** @deprecated will be removed when dropping backward compatibility for old DND */
|
|
3077
3214
|
MouseMove: 'mouseMove',
|
|
@@ -3096,7 +3233,6 @@ var StudioCanvasMode$2;
|
|
|
3096
3233
|
StudioCanvasMode["NONE"] = "none";
|
|
3097
3234
|
})(StudioCanvasMode$2 || (StudioCanvasMode$2 = {}));
|
|
3098
3235
|
const ASSEMBLY_NODE_TYPE = 'assembly';
|
|
3099
|
-
const ASSEMBLY_DEFAULT_CATEGORY = 'Assemblies';
|
|
3100
3236
|
const ASSEMBLY_BLOCK_NODE_TYPE = 'assemblyBlock';
|
|
3101
3237
|
const EMPTY_CONTAINER_SIZE = '80px';
|
|
3102
3238
|
const HYPERLINK_DEFAULT_PATTERN = `/{locale}/{entry.fields.slug}/`;
|
|
@@ -3255,34 +3391,7 @@ const useBreakpoints = (breakpoints) => {
|
|
|
3255
3391
|
return { resolveDesignValue };
|
|
3256
3392
|
};
|
|
3257
3393
|
|
|
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
|
-
};
|
|
3267
3394
|
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
|
-
};
|
|
3286
3395
|
|
|
3287
3396
|
const useEditorStore = create((set, get) => ({
|
|
3288
3397
|
dataSource: {},
|
|
@@ -3322,173 +3431,445 @@ const useEditorStore = create((set, get) => ({
|
|
|
3322
3431
|
},
|
|
3323
3432
|
}));
|
|
3324
3433
|
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
const
|
|
3489
|
-
|
|
3490
|
-
|
|
3491
|
-
|
|
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
|
+
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
|
+
styleInject(css_248z$8);
|
|
3708
|
+
|
|
3709
|
+
/** @deprecated will be removed when dropping backward compatibility for old DND */
|
|
3710
|
+
/**
|
|
3711
|
+
* These modes are ONLY intended to be internally used within the context of
|
|
3712
|
+
* editing an experience inside of Contentful Studio. i.e. these modes
|
|
3713
|
+
* intentionally do not include preview/delivery modes.
|
|
3714
|
+
*/
|
|
3715
|
+
var StudioCanvasMode$1;
|
|
3716
|
+
(function (StudioCanvasMode) {
|
|
3717
|
+
StudioCanvasMode["READ_ONLY"] = "readOnlyMode";
|
|
3718
|
+
StudioCanvasMode["EDITOR"] = "editorMode";
|
|
3719
|
+
StudioCanvasMode["NONE"] = "none";
|
|
3720
|
+
})(StudioCanvasMode$1 || (StudioCanvasMode$1 = {}));
|
|
3721
|
+
var PostMessageMethods$1;
|
|
3722
|
+
(function (PostMessageMethods) {
|
|
3723
|
+
PostMessageMethods["REQUEST_ENTITIES"] = "REQUEST_ENTITIES";
|
|
3724
|
+
PostMessageMethods["REQUESTED_ENTITIES"] = "REQUESTED_ENTITIES";
|
|
3725
|
+
})(PostMessageMethods$1 || (PostMessageMethods$1 = {}));
|
|
3726
|
+
|
|
3727
|
+
var css_248z$7 = ".cf-button:empty:before{content:\"\";display:inline-block}";
|
|
3728
|
+
styleInject(css_248z$7);
|
|
3729
|
+
|
|
3730
|
+
var css_248z$6 = ".cf-heading{white-space:pre-line}";
|
|
3731
|
+
styleInject(css_248z$6);
|
|
3732
|
+
|
|
3733
|
+
var css_248z$5 = ".cf-richtext{white-space:pre-line}.cf-richtext>:first-child{margin-top:0}.cf-richtext>:last-child{margin-bottom:0}";
|
|
3734
|
+
styleInject(css_248z$5);
|
|
3735
|
+
|
|
3736
|
+
var css_248z$4 = ".cf-text{white-space:pre-line}.cf-text-link .cf-text{margin:0}";
|
|
3737
|
+
styleInject(css_248z$4);
|
|
3738
|
+
|
|
3739
|
+
var css_248z$3 = "div.cf-placeholder-wrapper{outline:2px solid rgba(var(--cf-color-gray400-rgb),.5);outline-offset:-2px;overflow:hidden;position:relative}img.cf-placeholder-image{background-color:var(--cf-color-gray100);height:100%;width:100%}svg.cf-placeholder-icon{height:var(--cf-text-3xl);left:50%;max-height:100%;max-width:100%;position:absolute;top:50%;transform:translate(-50%,-50%);width:var(--cf-text-3xl)}svg.cf-placeholder-icon path{fill:var(--cf-color-gray400)}";
|
|
3740
|
+
styleInject(css_248z$3);
|
|
3741
|
+
|
|
3742
|
+
/**
|
|
3743
|
+
* These modes are ONLY intended to be internally used within the context of
|
|
3744
|
+
* editing an experience inside of Contentful Studio. i.e. these modes
|
|
3745
|
+
* intentionally do not include preview/delivery modes.
|
|
3746
|
+
*/
|
|
3747
|
+
var StudioCanvasMode;
|
|
3748
|
+
(function (StudioCanvasMode) {
|
|
3749
|
+
StudioCanvasMode["READ_ONLY"] = "readOnlyMode";
|
|
3750
|
+
StudioCanvasMode["EDITOR"] = "editorMode";
|
|
3751
|
+
StudioCanvasMode["NONE"] = "none";
|
|
3752
|
+
})(StudioCanvasMode || (StudioCanvasMode = {}));
|
|
3753
|
+
const CONTENTFUL_COMPONENTS = {
|
|
3754
|
+
section: {
|
|
3755
|
+
id: 'contentful-section',
|
|
3756
|
+
name: 'Section',
|
|
3757
|
+
},
|
|
3758
|
+
container: {
|
|
3759
|
+
id: 'contentful-container',
|
|
3760
|
+
name: 'Container',
|
|
3761
|
+
},
|
|
3762
|
+
columns: {
|
|
3763
|
+
id: 'contentful-columns',
|
|
3764
|
+
name: 'Columns',
|
|
3765
|
+
},
|
|
3766
|
+
singleColumn: {
|
|
3767
|
+
id: 'contentful-single-column',
|
|
3768
|
+
name: 'Column',
|
|
3769
|
+
},
|
|
3770
|
+
button: {
|
|
3771
|
+
id: 'contentful-button',
|
|
3772
|
+
name: 'Button',
|
|
3773
|
+
},
|
|
3774
|
+
heading: {
|
|
3775
|
+
id: 'contentful-heading',
|
|
3776
|
+
name: 'Heading',
|
|
3777
|
+
},
|
|
3778
|
+
image: {
|
|
3779
|
+
id: 'contentful-image',
|
|
3780
|
+
name: 'Image',
|
|
3781
|
+
},
|
|
3782
|
+
richText: {
|
|
3783
|
+
id: 'contentful-richText',
|
|
3784
|
+
name: 'Rich Text',
|
|
3785
|
+
},
|
|
3786
|
+
text: {
|
|
3787
|
+
id: 'contentful-text',
|
|
3788
|
+
name: 'Text',
|
|
3789
|
+
},
|
|
3790
|
+
divider: {
|
|
3791
|
+
id: 'contentful-divider',
|
|
3792
|
+
name: 'Divider',
|
|
3793
|
+
},
|
|
3794
|
+
carousel: {
|
|
3795
|
+
id: 'contentful-carousel',
|
|
3796
|
+
name: 'Carousel',
|
|
3797
|
+
},
|
|
3798
|
+
};
|
|
3799
|
+
var PostMessageMethods;
|
|
3800
|
+
(function (PostMessageMethods) {
|
|
3801
|
+
PostMessageMethods["REQUEST_ENTITIES"] = "REQUEST_ENTITIES";
|
|
3802
|
+
PostMessageMethods["REQUESTED_ENTITIES"] = "REQUESTED_ENTITIES";
|
|
3803
|
+
})(PostMessageMethods || (PostMessageMethods = {}));
|
|
3804
|
+
new Set(Object.values(CONTENTFUL_COMPONENTS).map((component) => component.id));
|
|
3805
|
+
|
|
3806
|
+
// If more than one version is supported, use z.union
|
|
3807
|
+
const SchemaVersions = z.literal('2023-09-28');
|
|
3808
|
+
// Keep deprecated versions here just for reference
|
|
3809
|
+
z.union([
|
|
3810
|
+
z.literal('2023-08-23'),
|
|
3811
|
+
z.literal('2023-07-26'),
|
|
3812
|
+
z.literal('2023-06-27'),
|
|
3813
|
+
]);
|
|
3814
|
+
|
|
3815
|
+
const DefinitionPropertyTypeSchema = z.enum([
|
|
3816
|
+
'Text',
|
|
3817
|
+
'RichText',
|
|
3818
|
+
'Number',
|
|
3819
|
+
'Date',
|
|
3820
|
+
'Boolean',
|
|
3821
|
+
'Location',
|
|
3822
|
+
'Media',
|
|
3823
|
+
'Object',
|
|
3824
|
+
'Hyperlink',
|
|
3825
|
+
'Array',
|
|
3826
|
+
'Link',
|
|
3827
|
+
]);
|
|
3828
|
+
const DefinitionPropertyKeySchema = z
|
|
3829
|
+
.string()
|
|
3830
|
+
.regex(/^[a-zA-Z0-9-_]{1,32}$/, { message: 'Property needs to match: /^[a-zA-Z0-9-_]{1,32}$/' });
|
|
3831
|
+
const PrimitiveValueSchema = z.union([
|
|
3832
|
+
z.string(),
|
|
3833
|
+
z.boolean(),
|
|
3834
|
+
z.number(),
|
|
3835
|
+
z.record(z.any(), z.any()),
|
|
3836
|
+
z.undefined(),
|
|
3837
|
+
]);
|
|
3838
|
+
const UsedComponentsSchema = z.array(z.object({
|
|
3839
|
+
sys: z.object({
|
|
3840
|
+
type: z.literal('Link'),
|
|
3841
|
+
id: z.string(),
|
|
3842
|
+
linkType: z.literal('Entry'),
|
|
3843
|
+
}),
|
|
3844
|
+
}));
|
|
3845
|
+
const uuidKeySchema = z
|
|
3846
|
+
.string()
|
|
3847
|
+
.regex(/^[a-zA-Z0-9-_]{1,21}$/, { message: 'Does not match /^[a-zA-Z0-9-_]{1,21}$/' });
|
|
3848
|
+
const DataSourceSchema = z.record(uuidKeySchema, z.object({
|
|
3849
|
+
sys: z.object({
|
|
3850
|
+
type: z.literal('Link'),
|
|
3851
|
+
id: z.string(),
|
|
3852
|
+
linkType: z.enum(['Entry', 'Asset']),
|
|
3853
|
+
}),
|
|
3854
|
+
}));
|
|
3855
|
+
const UnboundValuesSchema = z.record(uuidKeySchema, z.object({
|
|
3856
|
+
value: PrimitiveValueSchema,
|
|
3857
|
+
}));
|
|
3858
|
+
/**
|
|
3859
|
+
* Property keys for imported components have a limit of 32 characters (to be implemented) while
|
|
3860
|
+
* property keys for patterns have a limit of 54 characters (<32-char-variable-name>_<21-char-nanoid-id>).
|
|
3861
|
+
* Because we cannot distinguish between the two in the componentTree, we will use the larger limit for both.
|
|
3862
|
+
*/
|
|
3863
|
+
const propertyKeySchema = z
|
|
3864
|
+
.string()
|
|
3865
|
+
.regex(/^[a-zA-Z0-9-_]{1,54}$/, { message: 'Does not match /^[a-zA-Z0-9-_]{1,54}$/' });
|
|
3866
|
+
const ComponentTreeNodeIdSchema = z
|
|
3867
|
+
.string()
|
|
3868
|
+
.regex(/^[a-zA-Z0-9]{1,8}$/, { message: 'Does not match /^[a-zA-Z0-9]{1,8}$/' });
|
|
3869
|
+
const breakpointsRefinement = (value, ctx) => {
|
|
3870
|
+
if (!value.length || value[0].query !== '*') {
|
|
3871
|
+
ctx.addIssue({
|
|
3872
|
+
code: z.ZodIssueCode.custom,
|
|
3492
3873
|
message: `The first breakpoint should include the following attributes: { "query": "*" }`,
|
|
3493
3874
|
});
|
|
3494
3875
|
return;
|
|
@@ -3615,7 +3996,7 @@ const BreakpointSchema = z
|
|
|
3615
3996
|
id: propertyKeySchema,
|
|
3616
3997
|
// Can be replace with z.templateLiteral when upgrading to zod v4
|
|
3617
3998
|
query: z.string().refine((s) => BREAKPOINT_QUERY_REGEX.test(s)),
|
|
3618
|
-
previewSize: z.string(),
|
|
3999
|
+
previewSize: z.string().optional(),
|
|
3619
4000
|
displayName: z.string(),
|
|
3620
4001
|
displayIcon: z.enum(['desktop', 'tablet', 'mobile']).optional(),
|
|
3621
4002
|
})
|
|
@@ -3652,19 +4033,6 @@ const ComponentVariableSchema = z.object({
|
|
|
3652
4033
|
});
|
|
3653
4034
|
const ComponentTreeNodeSchema = BaseComponentTreeNodeSchema.extend({
|
|
3654
4035
|
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
|
-
}
|
|
3668
4036
|
});
|
|
3669
4037
|
const ComponentTreeSchema = z
|
|
3670
4038
|
.object({
|
|
@@ -3682,6 +4050,25 @@ z.object({
|
|
|
3682
4050
|
usedComponents: localeWrapper(UsedComponentsSchema).optional(),
|
|
3683
4051
|
});
|
|
3684
4052
|
|
|
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;
|
|
3685
4072
|
const THUMBNAIL_IDS = [
|
|
3686
4073
|
'columns',
|
|
3687
4074
|
'columnsPlusRight',
|
|
@@ -3712,7 +4099,17 @@ const THUMBNAIL_IDS = [
|
|
|
3712
4099
|
const VariableMappingSchema = z.object({
|
|
3713
4100
|
parameterId: propertyKeySchema,
|
|
3714
4101
|
type: z.literal('ContentTypeMapping'),
|
|
3715
|
-
pathsByContentType: z
|
|
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
|
+
}),
|
|
3716
4113
|
});
|
|
3717
4114
|
const PassToNodeSchema = z
|
|
3718
4115
|
.object({
|
|
@@ -3736,7 +4133,10 @@ const ParameterDefinitionSchema = z.object({
|
|
|
3736
4133
|
})
|
|
3737
4134
|
.optional(),
|
|
3738
4135
|
contentTypes: z.array(z.string()).min(1),
|
|
3739
|
-
passToNodes: z
|
|
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)
|
|
3740
4140
|
});
|
|
3741
4141
|
const ParameterDefinitionsSchema = z.record(propertyKeySchema, ParameterDefinitionSchema);
|
|
3742
4142
|
const VariableMappingsSchema = z.record(propertyKeySchema, VariableMappingSchema);
|
|
@@ -3757,14 +4157,108 @@ const ComponentSettingsSchema = z
|
|
|
3757
4157
|
category: z.string().max(50, 'Category must contain at most 50 characters').optional(),
|
|
3758
4158
|
prebindingDefinitions: z.array(PrebindingDefinitionSchema).length(1).optional(),
|
|
3759
4159
|
})
|
|
3760
|
-
.strict()
|
|
3761
|
-
|
|
4160
|
+
.strict()
|
|
4161
|
+
.superRefine((componentSettings, ctx) => {
|
|
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({
|
|
3762
4174
|
componentTree: localeWrapper(ComponentTreeSchema),
|
|
3763
4175
|
dataSource: localeWrapper(DataSourceSchema),
|
|
3764
4176
|
unboundValues: localeWrapper(UnboundValuesSchema),
|
|
3765
4177
|
usedComponents: localeWrapper(UsedComponentsSchema).optional(),
|
|
3766
4178
|
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);
|
|
3767
4189
|
});
|
|
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
|
+
};
|
|
3768
4262
|
|
|
3769
4263
|
z
|
|
3770
4264
|
.object({
|
|
@@ -4062,707 +4556,408 @@ const parseDeepPath = (deepPathCandidate) => {
|
|
|
4062
4556
|
else if (chunks.length === 2) {
|
|
4063
4557
|
return null; // deep paths have at least 3 chunks
|
|
4064
4558
|
}
|
|
4065
|
-
// With 3+ chunks we can now check for deep path correctness
|
|
4066
|
-
const [initialChunk, ...fieldChunks] = chunks;
|
|
4067
|
-
if (!isValidInitialChunk(initialChunk)) {
|
|
4068
|
-
return null;
|
|
4069
|
-
}
|
|
4070
|
-
if (!fieldChunks.every(isValidFieldChunk)) {
|
|
4071
|
-
return null;
|
|
4072
|
-
}
|
|
4073
|
-
return {
|
|
4074
|
-
key: initialChunk[1], // pick uuid from initial chunk ['','uuid123'],
|
|
4075
|
-
fields: fieldChunks.map((fieldChunk) => fieldChunk[1]), // pick only fieldName eg. from ['fields','mainStory', '~locale'] we pick `mainStory`
|
|
4076
|
-
};
|
|
4077
|
-
};
|
|
4078
|
-
const chunkSegments = (segments, { startNextChunkOnElementEqualTo }) => {
|
|
4079
|
-
const chunks = [];
|
|
4080
|
-
let currentChunk = [];
|
|
4081
|
-
const isSegmentBeginningOfChunk = (segment) => segment === startNextChunkOnElementEqualTo;
|
|
4082
|
-
const excludeEmptyChunks = (chunk) => chunk.length > 0;
|
|
4083
|
-
for (let i = 0; i < segments.length; i++) {
|
|
4084
|
-
const isInitialElement = i === 0;
|
|
4085
|
-
const segment = segments[i];
|
|
4086
|
-
if (isInitialElement) {
|
|
4087
|
-
currentChunk = [segment];
|
|
4088
|
-
}
|
|
4089
|
-
else if (isSegmentBeginningOfChunk(segment)) {
|
|
4090
|
-
chunks.push(currentChunk);
|
|
4091
|
-
currentChunk = [segment];
|
|
4092
|
-
}
|
|
4093
|
-
else {
|
|
4094
|
-
currentChunk.push(segment);
|
|
4095
|
-
}
|
|
4096
|
-
}
|
|
4097
|
-
chunks.push(currentChunk);
|
|
4098
|
-
return chunks.filter(excludeEmptyChunks);
|
|
4099
|
-
};
|
|
4100
|
-
|
|
4101
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4102
|
-
function get(obj, path) {
|
|
4103
|
-
if (!path.length) {
|
|
4104
|
-
return obj;
|
|
4105
|
-
}
|
|
4106
|
-
try {
|
|
4107
|
-
const [currentPath, ...nextPath] = path;
|
|
4108
|
-
return get(obj[currentPath], nextPath);
|
|
4109
|
-
}
|
|
4110
|
-
catch (err) {
|
|
4111
|
-
return undefined;
|
|
4112
|
-
}
|
|
4113
|
-
}
|
|
4114
|
-
const isEntry = (value) => {
|
|
4115
|
-
return (null !== value &&
|
|
4116
|
-
typeof value === 'object' &&
|
|
4117
|
-
'sys' in value &&
|
|
4118
|
-
value.sys?.type === 'Entry');
|
|
4119
|
-
};
|
|
4120
|
-
const isAsset = (value) => {
|
|
4121
|
-
return (null !== value &&
|
|
4122
|
-
typeof value === 'object' &&
|
|
4123
|
-
'sys' in value &&
|
|
4124
|
-
value.sys?.type === 'Asset');
|
|
4125
|
-
};
|
|
4126
|
-
|
|
4127
|
-
function deepFreeze(obj) {
|
|
4128
|
-
const propNames = Object.getOwnPropertyNames(obj);
|
|
4129
|
-
for (const name of propNames) {
|
|
4130
|
-
const value = obj[name];
|
|
4131
|
-
if (value && typeof value === 'object') {
|
|
4132
|
-
deepFreeze(value);
|
|
4133
|
-
}
|
|
4134
|
-
}
|
|
4135
|
-
return Object.freeze(obj);
|
|
4136
|
-
}
|
|
4137
|
-
|
|
4138
|
-
/**
|
|
4139
|
-
* Base Store for entities
|
|
4140
|
-
* Can be extended for the different loading behaviours (editor, production, ..)
|
|
4141
|
-
*/
|
|
4142
|
-
class EntityStoreBase {
|
|
4143
|
-
constructor({ entities, locale }) {
|
|
4144
|
-
/* serialized */ this.entryMap = new Map();
|
|
4145
|
-
/* serialized */ this.assetMap = new Map();
|
|
4146
|
-
this.locale = locale;
|
|
4147
|
-
for (const entity of entities) {
|
|
4148
|
-
this.addEntity(entity);
|
|
4149
|
-
}
|
|
4150
|
-
}
|
|
4151
|
-
get entities() {
|
|
4152
|
-
return [...this.entryMap.values(), ...this.assetMap.values()];
|
|
4153
|
-
}
|
|
4154
|
-
updateEntity(entity) {
|
|
4155
|
-
this.addEntity(entity);
|
|
4156
|
-
}
|
|
4157
|
-
getEntryOrAsset(linkOrEntryOrAsset, path) {
|
|
4158
|
-
if (isDeepPath(path)) {
|
|
4159
|
-
return this.getDeepEntry(linkOrEntryOrAsset, path);
|
|
4160
|
-
}
|
|
4161
|
-
let entity;
|
|
4162
|
-
if (isLink(linkOrEntryOrAsset)) {
|
|
4163
|
-
const resolvedEntity = linkOrEntryOrAsset.sys.linkType === 'Entry'
|
|
4164
|
-
? this.entryMap.get(linkOrEntryOrAsset.sys.id)
|
|
4165
|
-
: this.assetMap.get(linkOrEntryOrAsset.sys.id);
|
|
4166
|
-
if (!resolvedEntity || resolvedEntity.sys.type !== linkOrEntryOrAsset.sys.linkType) {
|
|
4167
|
-
debug.warn(`[experiences-core::EntityStoreBase] Experience references unresolved entity: ${JSON.stringify(linkOrEntryOrAsset)}`);
|
|
4168
|
-
return;
|
|
4169
|
-
}
|
|
4170
|
-
entity = resolvedEntity;
|
|
4171
|
-
}
|
|
4172
|
-
else if (isAsset(linkOrEntryOrAsset) || isEntry(linkOrEntryOrAsset)) {
|
|
4173
|
-
// We already have the complete entity in preview & delivery (resolved by the CMA client)
|
|
4174
|
-
entity = linkOrEntryOrAsset;
|
|
4175
|
-
}
|
|
4176
|
-
else {
|
|
4177
|
-
throw new Error(`[experiences-core::EntityStoreBase] Unexpected object when resolving entity: ${JSON.stringify(linkOrEntryOrAsset)}`);
|
|
4178
|
-
}
|
|
4179
|
-
return entity;
|
|
4180
|
-
}
|
|
4181
|
-
/**
|
|
4182
|
-
* @deprecated in the base class this should be simply an abstract method
|
|
4183
|
-
* @param entityLink
|
|
4184
|
-
* @param path
|
|
4185
|
-
* @returns
|
|
4186
|
-
*/
|
|
4187
|
-
getValue(entityLink, path) {
|
|
4188
|
-
const entity = this.getEntity(entityLink.sys.linkType, entityLink.sys.id);
|
|
4189
|
-
if (!entity) {
|
|
4190
|
-
// TODO: move to `debug` utils once it is extracted
|
|
4191
|
-
debug.warn(`Unresolved entity reference: ${entityLink.sys.linkType} with ID ${entityLink.sys.id}`);
|
|
4192
|
-
return;
|
|
4193
|
-
}
|
|
4194
|
-
return get(entity, path);
|
|
4195
|
-
}
|
|
4196
|
-
getEntityFromLink(link) {
|
|
4197
|
-
const resolvedEntity = link.sys.linkType === 'Entry'
|
|
4198
|
-
? this.entryMap.get(link.sys.id)
|
|
4199
|
-
: this.assetMap.get(link.sys.id);
|
|
4200
|
-
if (!resolvedEntity || resolvedEntity.sys.type !== link.sys.linkType) {
|
|
4201
|
-
debug.warn(`[experiences-core::EntityStoreBase] Experience references unresolved entity: ${JSON.stringify(link)}`);
|
|
4202
|
-
return;
|
|
4203
|
-
}
|
|
4204
|
-
return resolvedEntity;
|
|
4205
|
-
}
|
|
4206
|
-
getAssetById(assetId) {
|
|
4207
|
-
const asset = this.assetMap.get(assetId);
|
|
4208
|
-
if (!asset) {
|
|
4209
|
-
debug.warn(`[experiences-core::EntityStoreBase] Asset with ID "${assetId}" is not found in the store`);
|
|
4210
|
-
return;
|
|
4211
|
-
}
|
|
4212
|
-
return asset;
|
|
4213
|
-
}
|
|
4214
|
-
getEntryById(entryId) {
|
|
4215
|
-
const entry = this.entryMap.get(entryId);
|
|
4216
|
-
if (!entry) {
|
|
4217
|
-
debug.warn(`[experiences-core::EntityStoreBase] Entry with ID "${entryId}" is not found in the store`);
|
|
4218
|
-
return;
|
|
4219
|
-
}
|
|
4220
|
-
return entry;
|
|
4221
|
-
}
|
|
4222
|
-
getEntitiesFromMap(type, ids) {
|
|
4223
|
-
const resolved = [];
|
|
4224
|
-
const missing = [];
|
|
4225
|
-
for (const id of ids) {
|
|
4226
|
-
const entity = this.getEntity(type, id);
|
|
4227
|
-
if (entity) {
|
|
4228
|
-
resolved.push(entity);
|
|
4229
|
-
}
|
|
4230
|
-
else {
|
|
4231
|
-
missing.push(id);
|
|
4232
|
-
}
|
|
4233
|
-
}
|
|
4234
|
-
return {
|
|
4235
|
-
resolved,
|
|
4236
|
-
missing,
|
|
4237
|
-
};
|
|
4238
|
-
}
|
|
4239
|
-
addEntity(entity) {
|
|
4240
|
-
if (isAsset(entity)) {
|
|
4241
|
-
// cloned and frozen
|
|
4242
|
-
this.assetMap.set(entity.sys.id, deepFreeze(cloneDeep(entity)));
|
|
4243
|
-
}
|
|
4244
|
-
else if (isEntry(entity)) {
|
|
4245
|
-
// cloned and frozen
|
|
4246
|
-
this.entryMap.set(entity.sys.id, deepFreeze(cloneDeep(entity)));
|
|
4247
|
-
}
|
|
4248
|
-
else {
|
|
4249
|
-
throw new Error(`Attempted to add an entity to the store that is neither Asset nor Entry: '${JSON.stringify(entity)}'`);
|
|
4250
|
-
}
|
|
4251
|
-
}
|
|
4252
|
-
async fetchAsset(id) {
|
|
4253
|
-
const { resolved, missing } = this.getEntitiesFromMap('Asset', [id]);
|
|
4254
|
-
if (missing.length) {
|
|
4255
|
-
// TODO: move to `debug` utils once it is extracted
|
|
4256
|
-
debug.warn(`[experiences-core::EntityStoreBase] Asset "${id}" is not in the store`);
|
|
4257
|
-
return;
|
|
4258
|
-
}
|
|
4259
|
-
return resolved[0];
|
|
4260
|
-
}
|
|
4261
|
-
async fetchAssets(ids) {
|
|
4262
|
-
const { resolved, missing } = this.getEntitiesFromMap('Asset', ids);
|
|
4263
|
-
if (missing.length) {
|
|
4264
|
-
throw new Error(`Missing assets in the store (${missing.join(',')})`);
|
|
4265
|
-
}
|
|
4266
|
-
return resolved;
|
|
4267
|
-
}
|
|
4268
|
-
async fetchEntry(id) {
|
|
4269
|
-
const { resolved, missing } = this.getEntitiesFromMap('Entry', [id]);
|
|
4270
|
-
if (missing.length) {
|
|
4271
|
-
// TODO: move to `debug` utils once it is extracted
|
|
4272
|
-
debug.warn(`[experiences-core::EntityStoreBase] Entry "${id}" is not in the store`);
|
|
4273
|
-
return;
|
|
4274
|
-
}
|
|
4275
|
-
return resolved[0];
|
|
4559
|
+
// With 3+ chunks we can now check for deep path correctness
|
|
4560
|
+
const [initialChunk, ...fieldChunks] = chunks;
|
|
4561
|
+
if (!isValidInitialChunk(initialChunk)) {
|
|
4562
|
+
return null;
|
|
4276
4563
|
}
|
|
4277
|
-
|
|
4278
|
-
|
|
4279
|
-
if (missing.length) {
|
|
4280
|
-
throw new Error(`Missing assets in the store (${missing.join(',')})`);
|
|
4281
|
-
}
|
|
4282
|
-
return resolved;
|
|
4564
|
+
if (!fieldChunks.every(isValidFieldChunk)) {
|
|
4565
|
+
return null;
|
|
4283
4566
|
}
|
|
4284
|
-
|
|
4285
|
-
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
|
|
4290
|
-
|
|
4291
|
-
|
|
4292
|
-
|
|
4293
|
-
|
|
4294
|
-
|
|
4295
|
-
|
|
4296
|
-
|
|
4297
|
-
|
|
4298
|
-
|
|
4299
|
-
const fieldValue = get(entityToResolveFieldsFrom, ['fields', field]);
|
|
4300
|
-
if (undefined === fieldValue) {
|
|
4301
|
-
return {
|
|
4302
|
-
resolvedFieldset,
|
|
4303
|
-
isFullyResolved: false,
|
|
4304
|
-
reason: `Cannot resolve field Link<${entityToResolveFieldsFrom.sys.type}>(sys.id=${entityToResolveFieldsFrom.sys.id}).fields[${field}] as field value is not defined`,
|
|
4305
|
-
};
|
|
4306
|
-
}
|
|
4307
|
-
else if (isLink(fieldValue)) {
|
|
4308
|
-
const entity = this.getEntityFromLink(fieldValue);
|
|
4309
|
-
if (entity === undefined) {
|
|
4310
|
-
return {
|
|
4311
|
-
resolvedFieldset,
|
|
4312
|
-
isFullyResolved: false,
|
|
4313
|
-
reason: `Field reference Link (sys.id=${fieldValue.sys.id}) not found in the EntityStore, waiting...`,
|
|
4314
|
-
};
|
|
4315
|
-
}
|
|
4316
|
-
resolvedFieldset.push([entityToResolveFieldsFrom, field, _localeQualifier]);
|
|
4317
|
-
entityToResolveFieldsFrom = entity; // we move up
|
|
4318
|
-
}
|
|
4319
|
-
else if (isAsset(fieldValue) || isEntry(fieldValue)) {
|
|
4320
|
-
resolvedFieldset.push([entityToResolveFieldsFrom, field, _localeQualifier]);
|
|
4321
|
-
entityToResolveFieldsFrom = fieldValue; // we move up
|
|
4322
|
-
}
|
|
4323
|
-
else {
|
|
4324
|
-
return {
|
|
4325
|
-
resolvedFieldset,
|
|
4326
|
-
isFullyResolved: false,
|
|
4327
|
-
reason: `Deep path points to an invalid field value of type '${typeof fieldValue}' (value=${fieldValue})`,
|
|
4328
|
-
};
|
|
4329
|
-
}
|
|
4330
|
-
}
|
|
4331
|
-
return {
|
|
4332
|
-
resolvedFieldset,
|
|
4333
|
-
isFullyResolved: true,
|
|
4334
|
-
};
|
|
4335
|
-
};
|
|
4336
|
-
const headEntity = isLink(linkOrEntryOrAsset)
|
|
4337
|
-
? this.getEntityFromLink(linkOrEntryOrAsset)
|
|
4338
|
-
: linkOrEntryOrAsset;
|
|
4339
|
-
if (undefined === headEntity) {
|
|
4340
|
-
return;
|
|
4567
|
+
return {
|
|
4568
|
+
key: initialChunk[1], // pick uuid from initial chunk ['','uuid123'],
|
|
4569
|
+
fields: fieldChunks.map((fieldChunk) => fieldChunk[1]), // pick only fieldName eg. from ['fields','mainStory', '~locale'] we pick `mainStory`
|
|
4570
|
+
};
|
|
4571
|
+
};
|
|
4572
|
+
const chunkSegments = (segments, { startNextChunkOnElementEqualTo }) => {
|
|
4573
|
+
const chunks = [];
|
|
4574
|
+
let currentChunk = [];
|
|
4575
|
+
const isSegmentBeginningOfChunk = (segment) => segment === startNextChunkOnElementEqualTo;
|
|
4576
|
+
const excludeEmptyChunks = (chunk) => chunk.length > 0;
|
|
4577
|
+
for (let i = 0; i < segments.length; i++) {
|
|
4578
|
+
const isInitialElement = i === 0;
|
|
4579
|
+
const segment = segments[i];
|
|
4580
|
+
if (isInitialElement) {
|
|
4581
|
+
currentChunk = [segment];
|
|
4341
4582
|
}
|
|
4342
|
-
|
|
4343
|
-
|
|
4344
|
-
|
|
4345
|
-
// in case we can't follow till the end, we should signal that there was null-reference in the path
|
|
4346
|
-
const { resolvedFieldset, isFullyResolved, reason } = resolveFieldset(unresolvedFieldset, headEntity);
|
|
4347
|
-
if (!isFullyResolved) {
|
|
4348
|
-
if (reason) {
|
|
4349
|
-
debug.log(`[experiences-core::EntityStoreBase] Deep path wasn't resolved till leaf node, falling back to undefined, because: ${reason}`);
|
|
4350
|
-
}
|
|
4351
|
-
return;
|
|
4583
|
+
else if (isSegmentBeginningOfChunk(segment)) {
|
|
4584
|
+
chunks.push(currentChunk);
|
|
4585
|
+
currentChunk = [segment];
|
|
4352
4586
|
}
|
|
4353
|
-
|
|
4354
|
-
|
|
4355
|
-
}
|
|
4356
|
-
getEntity(type, id) {
|
|
4357
|
-
if (type === 'Asset') {
|
|
4358
|
-
return this.assetMap.get(id);
|
|
4587
|
+
else {
|
|
4588
|
+
currentChunk.push(segment);
|
|
4359
4589
|
}
|
|
4360
|
-
return this.entryMap.get(id);
|
|
4361
4590
|
}
|
|
4362
|
-
|
|
4363
|
-
|
|
4364
|
-
|
|
4365
|
-
|
|
4366
|
-
|
|
4367
|
-
|
|
4591
|
+
chunks.push(currentChunk);
|
|
4592
|
+
return chunks.filter(excludeEmptyChunks);
|
|
4593
|
+
};
|
|
4594
|
+
|
|
4595
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4596
|
+
function get(obj, path) {
|
|
4597
|
+
if (!path.length) {
|
|
4598
|
+
return obj;
|
|
4599
|
+
}
|
|
4600
|
+
try {
|
|
4601
|
+
const [currentPath, ...nextPath] = path;
|
|
4602
|
+
return get(obj[currentPath], nextPath);
|
|
4603
|
+
}
|
|
4604
|
+
catch (err) {
|
|
4605
|
+
return undefined;
|
|
4368
4606
|
}
|
|
4369
4607
|
}
|
|
4608
|
+
const isEntry = (value) => {
|
|
4609
|
+
return (null !== value &&
|
|
4610
|
+
typeof value === 'object' &&
|
|
4611
|
+
'sys' in value &&
|
|
4612
|
+
value.sys?.type === 'Entry');
|
|
4613
|
+
};
|
|
4614
|
+
const isAsset = (value) => {
|
|
4615
|
+
return (null !== value &&
|
|
4616
|
+
typeof value === 'object' &&
|
|
4617
|
+
'sys' in value &&
|
|
4618
|
+
value.sys?.type === 'Asset');
|
|
4619
|
+
};
|
|
4370
4620
|
|
|
4371
|
-
|
|
4372
|
-
|
|
4373
|
-
|
|
4621
|
+
function deepFreeze(obj) {
|
|
4622
|
+
const propNames = Object.getOwnPropertyNames(obj);
|
|
4623
|
+
for (const name of propNames) {
|
|
4624
|
+
const value = obj[name];
|
|
4625
|
+
if (value && typeof value === 'object') {
|
|
4626
|
+
deepFreeze(value);
|
|
4627
|
+
}
|
|
4374
4628
|
}
|
|
4629
|
+
return Object.freeze(obj);
|
|
4375
4630
|
}
|
|
4376
4631
|
|
|
4377
|
-
|
|
4378
|
-
|
|
4379
|
-
|
|
4380
|
-
|
|
4381
|
-
|
|
4382
|
-
|
|
4383
|
-
|
|
4384
|
-
|
|
4385
|
-
|
|
4386
|
-
|
|
4387
|
-
|
|
4388
|
-
|
|
4389
|
-
|
|
4390
|
-
|
|
4391
|
-
return
|
|
4392
|
-
}
|
|
4393
|
-
|
|
4394
|
-
|
|
4395
|
-
|
|
4396
|
-
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
|
|
4400
|
-
|
|
4401
|
-
|
|
4402
|
-
|
|
4403
|
-
|
|
4404
|
-
|
|
4405
|
-
|
|
4406
|
-
|
|
4407
|
-
|
|
4408
|
-
|
|
4409
|
-
|
|
4410
|
-
|
|
4411
|
-
|
|
4412
|
-
|
|
4413
|
-
|
|
4414
|
-
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
}
|
|
4418
|
-
|
|
4419
|
-
|
|
4420
|
-
styleInject(css_248z$2$1);
|
|
4421
|
-
|
|
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) => {
|
|
4423
|
-
return (React.createElement("div", { id: id, ref: ref, style: {
|
|
4424
|
-
display: 'flex',
|
|
4425
|
-
flex,
|
|
4426
|
-
flexBasis,
|
|
4427
|
-
flexShrink,
|
|
4428
|
-
flexDirection,
|
|
4429
|
-
gap,
|
|
4430
|
-
justifyContent,
|
|
4431
|
-
justifyItems,
|
|
4432
|
-
justifySelf,
|
|
4433
|
-
alignItems,
|
|
4434
|
-
alignSelf,
|
|
4435
|
-
alignContent,
|
|
4436
|
-
order,
|
|
4437
|
-
flexWrap,
|
|
4438
|
-
flexGrow,
|
|
4439
|
-
...cssStyles,
|
|
4440
|
-
}, className: className, onMouseEnter: onMouseEnter, onMouseUp: onMouseUp, onMouseDown: onMouseDown, onMouseLeave: onMouseLeave, onClick: onClick, ...props }, children));
|
|
4441
|
-
});
|
|
4442
|
-
Flex.displayName = 'Flex';
|
|
4443
|
-
|
|
4444
|
-
var css_248z$1$1 = ".cf-divider{display:contents;height:100%;position:relative;width:100%}.cf-divider hr{border:none}";
|
|
4445
|
-
styleInject(css_248z$1$1);
|
|
4446
|
-
|
|
4447
|
-
var css_248z$9 = ".cf-columns{display:flex;flex-direction:column;gap:24px;grid-template-columns:repeat(12,1fr);min-height:0;min-width:0}@media (min-width:768px){.cf-columns{display:grid}}.cf-single-column-wrapper{position:relative}.cf-single-column-wrapper: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}.cf-single-column-label:after{content:\"Column\"}";
|
|
4448
|
-
styleInject(css_248z$9);
|
|
4449
|
-
|
|
4450
|
-
const assemblyStyle = { display: 'contents' };
|
|
4451
|
-
const Assembly = (props) => {
|
|
4452
|
-
// Using a display contents so assembly content/children
|
|
4453
|
-
// can appear as if they are direct children of the div wrapper's parent
|
|
4454
|
-
return React.createElement("div", { "data-test-id": "assembly", ...props, style: assemblyStyle });
|
|
4455
|
-
};
|
|
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
|
-
}, []);
|
|
4632
|
+
/**
|
|
4633
|
+
* Base Store for entities
|
|
4634
|
+
* Can be extended for the different loading behaviours (editor, production, ..)
|
|
4635
|
+
*/
|
|
4636
|
+
class EntityStoreBase {
|
|
4637
|
+
constructor({ entities, locale }) {
|
|
4638
|
+
/* serialized */ this.entryMap = new Map();
|
|
4639
|
+
/* serialized */ this.assetMap = new Map();
|
|
4640
|
+
this.locale = locale;
|
|
4641
|
+
for (const entity of entities) {
|
|
4642
|
+
this.addEntity(entity);
|
|
4643
|
+
}
|
|
4644
|
+
}
|
|
4645
|
+
get entities() {
|
|
4646
|
+
return [...this.entryMap.values(), ...this.assetMap.values()];
|
|
4647
|
+
}
|
|
4648
|
+
updateEntity(entity) {
|
|
4649
|
+
this.addEntity(entity);
|
|
4650
|
+
}
|
|
4651
|
+
getEntryOrAsset(linkOrEntryOrAsset, path) {
|
|
4652
|
+
if (isDeepPath(path)) {
|
|
4653
|
+
return this.getDeepEntry(linkOrEntryOrAsset, path);
|
|
4654
|
+
}
|
|
4655
|
+
let entity;
|
|
4656
|
+
if (isLink(linkOrEntryOrAsset)) {
|
|
4657
|
+
const resolvedEntity = linkOrEntryOrAsset.sys.linkType === 'Entry'
|
|
4658
|
+
? this.entryMap.get(linkOrEntryOrAsset.sys.id)
|
|
4659
|
+
: this.assetMap.get(linkOrEntryOrAsset.sys.id);
|
|
4660
|
+
if (!resolvedEntity || resolvedEntity.sys.type !== linkOrEntryOrAsset.sys.linkType) {
|
|
4661
|
+
debug.warn(`[experiences-core::EntityStoreBase] Experience references unresolved entity: ${JSON.stringify(linkOrEntryOrAsset)}`);
|
|
4662
|
+
return;
|
|
4663
|
+
}
|
|
4664
|
+
entity = resolvedEntity;
|
|
4665
|
+
}
|
|
4666
|
+
else if (isAsset(linkOrEntryOrAsset) || isEntry(linkOrEntryOrAsset)) {
|
|
4667
|
+
// We already have the complete entity in preview & delivery (resolved by the CMA client)
|
|
4668
|
+
entity = linkOrEntryOrAsset;
|
|
4669
|
+
}
|
|
4670
|
+
else {
|
|
4671
|
+
throw new Error(`[experiences-core::EntityStoreBase] Unexpected object when resolving entity: ${JSON.stringify(linkOrEntryOrAsset)}`);
|
|
4672
|
+
}
|
|
4673
|
+
return entity;
|
|
4674
|
+
}
|
|
4482
4675
|
/**
|
|
4483
|
-
*
|
|
4484
|
-
*
|
|
4676
|
+
* @deprecated in the base class this should be simply an abstract method
|
|
4677
|
+
* @param entityLink
|
|
4678
|
+
* @param path
|
|
4679
|
+
* @returns
|
|
4485
4680
|
*/
|
|
4486
|
-
|
|
4487
|
-
|
|
4488
|
-
|
|
4489
|
-
|
|
4490
|
-
|
|
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
|
-
}
|
|
4681
|
+
getValue(entityLink, path) {
|
|
4682
|
+
const entity = this.getEntity(entityLink.sys.linkType, entityLink.sys.id);
|
|
4683
|
+
if (!entity) {
|
|
4684
|
+
// TODO: move to `debug` utils once it is extracted
|
|
4685
|
+
debug.warn(`Unresolved entity reference: ${entityLink.sys.linkType} with ID ${entityLink.sys.id}`);
|
|
4686
|
+
return;
|
|
4546
4687
|
}
|
|
4547
|
-
|
|
4548
|
-
|
|
4549
|
-
|
|
4688
|
+
return get(entity, path);
|
|
4689
|
+
}
|
|
4690
|
+
getEntityFromLink(link) {
|
|
4691
|
+
const resolvedEntity = link.sys.linkType === 'Entry'
|
|
4692
|
+
? this.entryMap.get(link.sys.id)
|
|
4693
|
+
: this.assetMap.get(link.sys.id);
|
|
4694
|
+
if (!resolvedEntity || resolvedEntity.sys.type !== link.sys.linkType) {
|
|
4695
|
+
debug.warn(`[experiences-core::EntityStoreBase] Experience references unresolved entity: ${JSON.stringify(link)}`);
|
|
4696
|
+
return;
|
|
4550
4697
|
}
|
|
4551
|
-
|
|
4552
|
-
|
|
4698
|
+
return resolvedEntity;
|
|
4699
|
+
}
|
|
4700
|
+
getAssetById(assetId) {
|
|
4701
|
+
const asset = this.assetMap.get(assetId);
|
|
4702
|
+
if (!asset) {
|
|
4703
|
+
debug.warn(`[experiences-core::EntityStoreBase] Asset with ID "${assetId}" is not found in the store`);
|
|
4704
|
+
return;
|
|
4553
4705
|
}
|
|
4554
|
-
|
|
4555
|
-
|
|
4556
|
-
|
|
4557
|
-
|
|
4558
|
-
|
|
4559
|
-
|
|
4560
|
-
|
|
4561
|
-
|
|
4562
|
-
|
|
4563
|
-
|
|
4564
|
-
|
|
4565
|
-
|
|
4566
|
-
|
|
4706
|
+
return asset;
|
|
4707
|
+
}
|
|
4708
|
+
getEntryById(entryId) {
|
|
4709
|
+
const entry = this.entryMap.get(entryId);
|
|
4710
|
+
if (!entry) {
|
|
4711
|
+
debug.warn(`[experiences-core::EntityStoreBase] Entry with ID "${entryId}" is not found in the store`);
|
|
4712
|
+
return;
|
|
4713
|
+
}
|
|
4714
|
+
return entry;
|
|
4715
|
+
}
|
|
4716
|
+
getEntitiesFromMap(type, ids) {
|
|
4717
|
+
const resolved = [];
|
|
4718
|
+
const missing = [];
|
|
4719
|
+
for (const id of ids) {
|
|
4720
|
+
const entity = this.getEntity(type, id);
|
|
4721
|
+
if (entity) {
|
|
4722
|
+
resolved.push(entity);
|
|
4567
4723
|
}
|
|
4568
|
-
|
|
4569
|
-
|
|
4570
|
-
if (eventData.eventType === PostMessageMethods$2.REQUESTED_ENTITIES) {
|
|
4571
|
-
// Expected message: This message is handled in the EntityStore to store fetched entities
|
|
4572
|
-
return;
|
|
4724
|
+
else {
|
|
4725
|
+
missing.push(id);
|
|
4573
4726
|
}
|
|
4574
|
-
|
|
4575
|
-
|
|
4576
|
-
|
|
4577
|
-
|
|
4578
|
-
|
|
4579
|
-
|
|
4580
|
-
|
|
4581
|
-
|
|
4582
|
-
|
|
4583
|
-
|
|
4584
|
-
|
|
4585
|
-
|
|
4586
|
-
|
|
4587
|
-
|
|
4588
|
-
|
|
4589
|
-
|
|
4590
|
-
|
|
4591
|
-
|
|
4592
|
-
|
|
4593
|
-
|
|
4594
|
-
|
|
4595
|
-
|
|
4596
|
-
|
|
4597
|
-
|
|
4598
|
-
|
|
4599
|
-
|
|
4600
|
-
|
|
4601
|
-
|
|
4602
|
-
|
|
4603
|
-
|
|
4604
|
-
|
|
4605
|
-
|
|
4606
|
-
|
|
4607
|
-
|
|
4608
|
-
|
|
4609
|
-
|
|
4610
|
-
|
|
4611
|
-
|
|
4612
|
-
|
|
4613
|
-
|
|
4614
|
-
|
|
4615
|
-
|
|
4616
|
-
|
|
4617
|
-
|
|
4618
|
-
|
|
4727
|
+
}
|
|
4728
|
+
return {
|
|
4729
|
+
resolved,
|
|
4730
|
+
missing,
|
|
4731
|
+
};
|
|
4732
|
+
}
|
|
4733
|
+
addEntity(entity) {
|
|
4734
|
+
if (isAsset(entity)) {
|
|
4735
|
+
// cloned and frozen
|
|
4736
|
+
this.assetMap.set(entity.sys.id, deepFreeze(cloneDeep(entity)));
|
|
4737
|
+
}
|
|
4738
|
+
else if (isEntry(entity)) {
|
|
4739
|
+
// cloned and frozen
|
|
4740
|
+
this.entryMap.set(entity.sys.id, deepFreeze(cloneDeep(entity)));
|
|
4741
|
+
}
|
|
4742
|
+
else {
|
|
4743
|
+
throw new Error(`Attempted to add an entity to the store that is neither Asset nor Entry: '${JSON.stringify(entity)}'`);
|
|
4744
|
+
}
|
|
4745
|
+
}
|
|
4746
|
+
async fetchAsset(id) {
|
|
4747
|
+
const { resolved, missing } = this.getEntitiesFromMap('Asset', [id]);
|
|
4748
|
+
if (missing.length) {
|
|
4749
|
+
// TODO: move to `debug` utils once it is extracted
|
|
4750
|
+
debug.warn(`[experiences-core::EntityStoreBase] Asset "${id}" is not in the store`);
|
|
4751
|
+
return;
|
|
4752
|
+
}
|
|
4753
|
+
return resolved[0];
|
|
4754
|
+
}
|
|
4755
|
+
async fetchAssets(ids) {
|
|
4756
|
+
const { resolved, missing } = this.getEntitiesFromMap('Asset', ids);
|
|
4757
|
+
if (missing.length) {
|
|
4758
|
+
throw new Error(`Missing assets in the store (${missing.join(',')})`);
|
|
4759
|
+
}
|
|
4760
|
+
return resolved;
|
|
4761
|
+
}
|
|
4762
|
+
async fetchEntry(id) {
|
|
4763
|
+
const { resolved, missing } = this.getEntitiesFromMap('Entry', [id]);
|
|
4764
|
+
if (missing.length) {
|
|
4765
|
+
// TODO: move to `debug` utils once it is extracted
|
|
4766
|
+
debug.warn(`[experiences-core::EntityStoreBase] Entry "${id}" is not in the store`);
|
|
4767
|
+
return;
|
|
4768
|
+
}
|
|
4769
|
+
return resolved[0];
|
|
4770
|
+
}
|
|
4771
|
+
async fetchEntries(ids) {
|
|
4772
|
+
const { resolved, missing } = this.getEntitiesFromMap('Entry', ids);
|
|
4773
|
+
if (missing.length) {
|
|
4774
|
+
throw new Error(`Missing assets in the store (${missing.join(',')})`);
|
|
4775
|
+
}
|
|
4776
|
+
return resolved;
|
|
4777
|
+
}
|
|
4778
|
+
getDeepEntry(linkOrEntryOrAsset, path) {
|
|
4779
|
+
const resolveFieldset = (unresolvedFieldset, headEntry) => {
|
|
4780
|
+
const resolvedFieldset = [];
|
|
4781
|
+
let entityToResolveFieldsFrom = headEntry;
|
|
4782
|
+
for (let i = 0; i < unresolvedFieldset.length; i++) {
|
|
4783
|
+
const isLeaf = i === unresolvedFieldset.length - 1; // with last row, we are not expecting a link, but a value
|
|
4784
|
+
const row = unresolvedFieldset[i];
|
|
4785
|
+
const [, field, _localeQualifier] = row;
|
|
4786
|
+
if (!entityToResolveFieldsFrom) {
|
|
4787
|
+
throw new Error(`Logic Error: Cannot resolve field ${field} of a fieldset as there is no entity to resolve it from.`);
|
|
4619
4788
|
}
|
|
4620
|
-
|
|
4621
|
-
|
|
4622
|
-
assemblies.forEach((definition) => {
|
|
4623
|
-
addComponentRegistration({
|
|
4624
|
-
component: Assembly,
|
|
4625
|
-
definition,
|
|
4626
|
-
});
|
|
4627
|
-
});
|
|
4789
|
+
if (isLeaf) {
|
|
4790
|
+
resolvedFieldset.push([entityToResolveFieldsFrom, field, _localeQualifier]);
|
|
4628
4791
|
break;
|
|
4629
4792
|
}
|
|
4630
|
-
|
|
4631
|
-
|
|
4632
|
-
|
|
4633
|
-
|
|
4634
|
-
|
|
4635
|
-
|
|
4636
|
-
|
|
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;
|
|
4793
|
+
const fieldValue = get(entityToResolveFieldsFrom, ['fields', field]);
|
|
4794
|
+
if (undefined === fieldValue) {
|
|
4795
|
+
return {
|
|
4796
|
+
resolvedFieldset,
|
|
4797
|
+
isFullyResolved: false,
|
|
4798
|
+
reason: `Cannot resolve field Link<${entityToResolveFieldsFrom.sys.type}>(sys.id=${entityToResolveFieldsFrom.sys.id}).fields[${field}] as field value is not defined`,
|
|
4799
|
+
};
|
|
4646
4800
|
}
|
|
4647
|
-
|
|
4648
|
-
const
|
|
4649
|
-
if (
|
|
4650
|
-
|
|
4651
|
-
|
|
4652
|
-
|
|
4653
|
-
|
|
4654
|
-
|
|
4655
|
-
updateNodesByUpdatedEntity(updatedEntity.sys.id);
|
|
4656
|
-
}
|
|
4801
|
+
else if (isLink(fieldValue)) {
|
|
4802
|
+
const entity = this.getEntityFromLink(fieldValue);
|
|
4803
|
+
if (entity === undefined) {
|
|
4804
|
+
return {
|
|
4805
|
+
resolvedFieldset,
|
|
4806
|
+
isFullyResolved: false,
|
|
4807
|
+
reason: `Field reference Link (sys.id=${fieldValue.sys.id}) not found in the EntityStore, waiting...`,
|
|
4808
|
+
};
|
|
4657
4809
|
}
|
|
4658
|
-
|
|
4810
|
+
resolvedFieldset.push([entityToResolveFieldsFrom, field, _localeQualifier]);
|
|
4811
|
+
entityToResolveFieldsFrom = entity; // we move up
|
|
4659
4812
|
}
|
|
4660
|
-
|
|
4661
|
-
|
|
4813
|
+
else if (isAsset(fieldValue) || isEntry(fieldValue)) {
|
|
4814
|
+
resolvedFieldset.push([entityToResolveFieldsFrom, field, _localeQualifier]);
|
|
4815
|
+
entityToResolveFieldsFrom = fieldValue; // we move up
|
|
4662
4816
|
}
|
|
4663
|
-
|
|
4664
|
-
|
|
4665
|
-
|
|
4666
|
-
|
|
4667
|
-
|
|
4668
|
-
}
|
|
4817
|
+
else {
|
|
4818
|
+
return {
|
|
4819
|
+
resolvedFieldset,
|
|
4820
|
+
isFullyResolved: false,
|
|
4821
|
+
reason: `Deep path points to an invalid field value of type '${typeof fieldValue}' (value=${fieldValue})`,
|
|
4822
|
+
};
|
|
4669
4823
|
}
|
|
4670
4824
|
}
|
|
4825
|
+
return {
|
|
4826
|
+
resolvedFieldset,
|
|
4827
|
+
isFullyResolved: true,
|
|
4828
|
+
};
|
|
4671
4829
|
};
|
|
4672
|
-
|
|
4673
|
-
|
|
4674
|
-
|
|
4675
|
-
|
|
4676
|
-
|
|
4677
|
-
|
|
4678
|
-
|
|
4679
|
-
|
|
4680
|
-
|
|
4681
|
-
|
|
4682
|
-
|
|
4683
|
-
|
|
4684
|
-
|
|
4685
|
-
|
|
4686
|
-
|
|
4687
|
-
|
|
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';
|
|
4830
|
+
const headEntity = isLink(linkOrEntryOrAsset)
|
|
4831
|
+
? this.getEntityFromLink(linkOrEntryOrAsset)
|
|
4832
|
+
: linkOrEntryOrAsset;
|
|
4833
|
+
if (undefined === headEntity) {
|
|
4834
|
+
return;
|
|
4835
|
+
}
|
|
4836
|
+
const unresolvedFieldset = parseDataSourcePathIntoFieldset(path);
|
|
4837
|
+
// The purpose here is to take this intermediate representation of the deep-path
|
|
4838
|
+
// and to follow the links to the leaf-entity and field
|
|
4839
|
+
// in case we can't follow till the end, we should signal that there was null-reference in the path
|
|
4840
|
+
const { resolvedFieldset, isFullyResolved, reason } = resolveFieldset(unresolvedFieldset, headEntity);
|
|
4841
|
+
if (!isFullyResolved) {
|
|
4842
|
+
if (reason) {
|
|
4843
|
+
debug.log(`[experiences-core::EntityStoreBase] Deep path wasn't resolved till leaf node, falling back to undefined, because: ${reason}`);
|
|
4844
|
+
}
|
|
4845
|
+
return;
|
|
4846
|
+
}
|
|
4847
|
+
const [leafEntity] = resolvedFieldset[resolvedFieldset.length - 1];
|
|
4848
|
+
return leafEntity;
|
|
4719
4849
|
}
|
|
4720
|
-
|
|
4721
|
-
|
|
4722
|
-
|
|
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;
|
|
4850
|
+
getEntity(type, id) {
|
|
4851
|
+
if (type === 'Asset') {
|
|
4852
|
+
return this.assetMap.get(id);
|
|
4728
4853
|
}
|
|
4729
|
-
|
|
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;
|
|
4854
|
+
return this.entryMap.get(id);
|
|
4736
4855
|
}
|
|
4737
|
-
|
|
4738
|
-
return
|
|
4856
|
+
toJSON() {
|
|
4857
|
+
return {
|
|
4858
|
+
entryMap: Object.fromEntries(this.entryMap),
|
|
4859
|
+
assetMap: Object.fromEntries(this.assetMap),
|
|
4860
|
+
locale: this.locale,
|
|
4861
|
+
};
|
|
4739
4862
|
}
|
|
4740
4863
|
}
|
|
4741
4864
|
|
|
4742
|
-
|
|
4743
|
-
|
|
4744
|
-
|
|
4745
|
-
|
|
4746
|
-
|
|
4747
|
-
} },
|
|
4748
|
-
"Missing component '",
|
|
4749
|
-
blockId,
|
|
4750
|
-
"'"));
|
|
4751
|
-
};
|
|
4865
|
+
class UninitializedEntityStore extends EntityStoreBase {
|
|
4866
|
+
constructor() {
|
|
4867
|
+
super({ entities: [], locale: 'uninitialized-locale-in-uninitialized-entity-store' });
|
|
4868
|
+
}
|
|
4869
|
+
}
|
|
4752
4870
|
|
|
4753
|
-
|
|
4754
|
-
|
|
4871
|
+
create((set, get) => ({
|
|
4872
|
+
// The UninitializedEntityStore is a placeholder instance and is here to highlight the
|
|
4873
|
+
// // fact that it's not used by anything until during loading lifecycle it'sreplaced by real entity store:
|
|
4874
|
+
// - in Preview+Delivery mode: right after we fetch Expereince and it entities
|
|
4875
|
+
// - in EDITOR (VisualEditor) mode: right after the VisualEditor is async imported and initialize event happens
|
|
4876
|
+
entityStore: new UninitializedEntityStore(),
|
|
4877
|
+
areEntitiesFetched: false,
|
|
4878
|
+
setEntitiesFetched(fetched) {
|
|
4879
|
+
set({ areEntitiesFetched: fetched });
|
|
4880
|
+
},
|
|
4881
|
+
resolveAssetById(assetId) {
|
|
4882
|
+
if (!assetId)
|
|
4883
|
+
return undefined;
|
|
4884
|
+
const { entityStore } = get();
|
|
4885
|
+
return entityStore.getAssetById(assetId);
|
|
4886
|
+
},
|
|
4887
|
+
resolveEntryById(entryId) {
|
|
4888
|
+
if (!entryId)
|
|
4889
|
+
return undefined;
|
|
4890
|
+
const { entityStore } = get();
|
|
4891
|
+
return entityStore.getEntryById(entryId);
|
|
4892
|
+
},
|
|
4893
|
+
resolveEntity(link) {
|
|
4894
|
+
if (!link)
|
|
4895
|
+
return undefined;
|
|
4896
|
+
const { entityStore } = get();
|
|
4897
|
+
return entityStore.getEntityFromLink(link);
|
|
4898
|
+
},
|
|
4899
|
+
resetEntityStore(entityStore) {
|
|
4900
|
+
set({
|
|
4901
|
+
entityStore,
|
|
4902
|
+
areEntitiesFetched: false,
|
|
4903
|
+
});
|
|
4904
|
+
},
|
|
4905
|
+
}));
|
|
4906
|
+
|
|
4907
|
+
var VisualEditorMode;
|
|
4908
|
+
(function (VisualEditorMode) {
|
|
4909
|
+
VisualEditorMode["LazyLoad"] = "lazyLoad";
|
|
4910
|
+
VisualEditorMode["InjectScript"] = "injectScript";
|
|
4911
|
+
})(VisualEditorMode || (VisualEditorMode = {}));
|
|
4912
|
+
|
|
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}";
|
|
4755
4914
|
styleInject(css_248z$2);
|
|
4756
4915
|
|
|
4916
|
+
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
|
+
return (React.createElement("div", { id: id, ref: ref, style: {
|
|
4918
|
+
display: 'flex',
|
|
4919
|
+
flex,
|
|
4920
|
+
flexBasis,
|
|
4921
|
+
flexShrink,
|
|
4922
|
+
flexDirection,
|
|
4923
|
+
gap,
|
|
4924
|
+
justifyContent,
|
|
4925
|
+
justifyItems,
|
|
4926
|
+
justifySelf,
|
|
4927
|
+
alignItems,
|
|
4928
|
+
alignSelf,
|
|
4929
|
+
alignContent,
|
|
4930
|
+
order,
|
|
4931
|
+
flexWrap,
|
|
4932
|
+
flexGrow,
|
|
4933
|
+
...cssStyles,
|
|
4934
|
+
}, className: className, onMouseEnter: onMouseEnter, onMouseUp: onMouseUp, onMouseDown: onMouseDown, onMouseLeave: onMouseLeave, onClick: onClick, ...props }, children));
|
|
4935
|
+
});
|
|
4936
|
+
Flex.displayName = 'Flex';
|
|
4937
|
+
|
|
4938
|
+
var css_248z$1$1 = ".cf-divider{display:contents;height:100%;position:relative;width:100%}.cf-divider hr{border:none}";
|
|
4939
|
+
styleInject(css_248z$1$1);
|
|
4940
|
+
|
|
4941
|
+
var css_248z$9 = ".cf-columns{display:flex;flex-direction:column;gap:24px;grid-template-columns:repeat(12,1fr);min-height:0;min-width:0}@media (min-width:768px){.cf-columns{display:grid}}.cf-single-column-wrapper{position:relative}.cf-single-column-wrapper: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}.cf-single-column-label:after{content:\"Column\"}";
|
|
4942
|
+
styleInject(css_248z$9);
|
|
4943
|
+
|
|
4944
|
+
const assemblyStyle = { display: 'contents' };
|
|
4945
|
+
const Assembly = (props) => {
|
|
4946
|
+
// Using a display contents so assembly content/children
|
|
4947
|
+
// can appear as if they are direct children of the div wrapper's parent
|
|
4948
|
+
return React.createElement("div", { "data-test-id": "assembly", ...props, style: assemblyStyle });
|
|
4949
|
+
};
|
|
4950
|
+
|
|
4757
4951
|
const useComponentRegistration = (node) => {
|
|
4758
4952
|
return useMemo(() => {
|
|
4759
|
-
|
|
4760
|
-
|
|
4761
|
-
|
|
4762
|
-
definitionId: node.data.blockId,
|
|
4953
|
+
if (node.type === ASSEMBLY_NODE_TYPE) {
|
|
4954
|
+
// The definition and component are the same for all assemblies
|
|
4955
|
+
return {
|
|
4763
4956
|
component: Assembly,
|
|
4764
|
-
|
|
4957
|
+
definition: createAssemblyDefinition(node.data.blockId),
|
|
4958
|
+
};
|
|
4765
4959
|
}
|
|
4960
|
+
const registration = componentRegistry.get(node.data.blockId);
|
|
4766
4961
|
if (!registration) {
|
|
4767
4962
|
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.`);
|
|
4768
4963
|
return undefined;
|
|
@@ -4854,7 +5049,9 @@ const checkIsNodeVisible = (node, resolveDesignValue) => {
|
|
|
4854
5049
|
return node.children.some((childNode) => checkIsNodeVisible(childNode, resolveDesignValue));
|
|
4855
5050
|
}
|
|
4856
5051
|
// Check if the current node is visible (`cfVisibility` is enforced on all nodes)
|
|
4857
|
-
|
|
5052
|
+
// Check explicitly for false, as `undefined` is treated as `true`. It could become undefined when the breakpoint IDs changed.
|
|
5053
|
+
return (resolveDesignValue(node.data.props['cfVisibility'].valuesByBreakpoint) !==
|
|
5054
|
+
false);
|
|
4858
5055
|
};
|
|
4859
5056
|
|
|
4860
5057
|
const useComponentProps = ({ node, entityStore, areEntitiesFetched, resolveDesignValue, definition, options, }) => {
|