@contentful/experiences-visual-editor-react 1.39.0-alpha-20250528T1342-e28bc3d.0 → 1.39.0-alpha-20250603T1404-5a5eb4e.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +377 -222
- package/dist/index.js.map +1 -1
- package/dist/renderApp.js +687 -233
- package/dist/renderApp.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import styleInject from 'style-inject';
|
|
|
2
2
|
import React, { useState, useEffect, useCallback, forwardRef, useMemo, useLayoutEffect, useRef } from 'react';
|
|
3
3
|
import { create } from 'zustand';
|
|
4
4
|
import { produce } from 'immer';
|
|
5
|
-
import { isEqual, omit, isArray, get as get$1 } from 'lodash-es';
|
|
5
|
+
import { isEqual, omit, isArray, get as get$1, debounce } from 'lodash-es';
|
|
6
6
|
import { z } from 'zod';
|
|
7
7
|
import md5 from 'md5';
|
|
8
8
|
import { BLOCKS } from '@contentful/rich-text-types';
|
|
@@ -228,6 +228,7 @@ const OUTGOING_EVENTS = {
|
|
|
228
228
|
OutsideCanvasClick: 'outsideCanvasClick',
|
|
229
229
|
SDKFeatures: 'sdkFeatures',
|
|
230
230
|
RequestEntities: 'REQUEST_ENTITIES',
|
|
231
|
+
CanvasGeometryUpdated: 'canvasGeometryUpdated',
|
|
231
232
|
};
|
|
232
233
|
const INCOMING_EVENTS$1 = {
|
|
233
234
|
RequestEditorMode: 'requestEditorMode',
|
|
@@ -867,17 +868,16 @@ const PrimitiveValueSchema$1 = z.union([
|
|
|
867
868
|
z.record(z.any(), z.any()),
|
|
868
869
|
z.undefined(),
|
|
869
870
|
]);
|
|
871
|
+
const UsedComponentsSchema$1 = z.array(z.object({
|
|
872
|
+
sys: z.object({
|
|
873
|
+
type: z.literal('Link'),
|
|
874
|
+
id: z.string(),
|
|
875
|
+
linkType: z.literal('Entry'),
|
|
876
|
+
}),
|
|
877
|
+
}));
|
|
870
878
|
const uuidKeySchema$1 = z
|
|
871
879
|
.string()
|
|
872
880
|
.regex(/^[a-zA-Z0-9-_]{1,21}$/, { message: 'Does not match /^[a-zA-Z0-9-_]{1,21}$/' });
|
|
873
|
-
/**
|
|
874
|
-
* Property keys for imported components have a limit of 32 characters (to be implemented) while
|
|
875
|
-
* property keys for patterns have a limit of 54 characters (<32-char-variabl-name>_<21-char-nanoid-id>).
|
|
876
|
-
* Because we cannot distinguish between the two in the componentTree, we will use the larger limit for both.
|
|
877
|
-
*/
|
|
878
|
-
const propertyKeySchema$1 = z
|
|
879
|
-
.string()
|
|
880
|
-
.regex(/^[a-zA-Z0-9-_]{1,54}$/, { message: 'Does not match /^[a-zA-Z0-9-_]{1,54}$/' });
|
|
881
881
|
const DataSourceSchema$1 = z.record(uuidKeySchema$1, z.object({
|
|
882
882
|
sys: z.object({
|
|
883
883
|
type: z.literal('Link'),
|
|
@@ -885,7 +885,62 @@ const DataSourceSchema$1 = z.record(uuidKeySchema$1, z.object({
|
|
|
885
885
|
linkType: z.enum(['Entry', 'Asset']),
|
|
886
886
|
}),
|
|
887
887
|
}));
|
|
888
|
+
const UnboundValuesSchema$1 = z.record(uuidKeySchema$1, z.object({
|
|
889
|
+
value: PrimitiveValueSchema$1,
|
|
890
|
+
}));
|
|
891
|
+
/**
|
|
892
|
+
* Property keys for imported components have a limit of 32 characters (to be implemented) while
|
|
893
|
+
* property keys for patterns have a limit of 54 characters (<32-char-variable-name>_<21-char-nanoid-id>).
|
|
894
|
+
* Because we cannot distinguish between the two in the componentTree, we will use the larger limit for both.
|
|
895
|
+
*/
|
|
896
|
+
const propertyKeySchema$1 = z
|
|
897
|
+
.string()
|
|
898
|
+
.regex(/^[a-zA-Z0-9-_]{1,54}$/, { message: 'Does not match /^[a-zA-Z0-9-_]{1,54}$/' });
|
|
899
|
+
const ComponentTreeNodeIdSchema$1 = z
|
|
900
|
+
.string()
|
|
901
|
+
.regex(/^[a-zA-Z0-9]{1,8}$/, { message: 'Does not match /^[a-zA-Z0-9]{1,8}$/' });
|
|
902
|
+
const breakpointsRefinement$1 = (value, ctx) => {
|
|
903
|
+
if (!value.length || value[0].query !== '*') {
|
|
904
|
+
ctx.addIssue({
|
|
905
|
+
code: z.ZodIssueCode.custom,
|
|
906
|
+
message: `The first breakpoint should include the following attributes: { "query": "*" }`,
|
|
907
|
+
});
|
|
908
|
+
}
|
|
909
|
+
const hasDuplicateIds = value.some((currentBreakpoint, currentBreakpointIndex) => {
|
|
910
|
+
// check if the current breakpoint id is found in the rest of the array
|
|
911
|
+
const breakpointIndex = value.findIndex((breakpoint) => breakpoint.id === currentBreakpoint.id);
|
|
912
|
+
return breakpointIndex !== currentBreakpointIndex;
|
|
913
|
+
});
|
|
914
|
+
if (hasDuplicateIds) {
|
|
915
|
+
ctx.addIssue({
|
|
916
|
+
code: z.ZodIssueCode.custom,
|
|
917
|
+
message: `Breakpoint IDs must be unique`,
|
|
918
|
+
});
|
|
919
|
+
}
|
|
920
|
+
// Extract the queries boundary by removing the special characters around it
|
|
921
|
+
const queries = value.map((bp) => bp.query === '*' ? bp.query : parseInt(bp.query.replace(/px|<|>/, '')));
|
|
922
|
+
// sort updates queries array in place so we need to create a copy
|
|
923
|
+
const originalQueries = [...queries];
|
|
924
|
+
queries.sort((q1, q2) => {
|
|
925
|
+
if (q1 === '*') {
|
|
926
|
+
return -1;
|
|
927
|
+
}
|
|
928
|
+
if (q2 === '*') {
|
|
929
|
+
return 1;
|
|
930
|
+
}
|
|
931
|
+
return q1 > q2 ? -1 : 1;
|
|
932
|
+
});
|
|
933
|
+
if (originalQueries.join('') !== queries.join('')) {
|
|
934
|
+
ctx.addIssue({
|
|
935
|
+
code: z.ZodIssueCode.custom,
|
|
936
|
+
message: `Breakpoints should be ordered from largest to smallest pixel value`,
|
|
937
|
+
});
|
|
938
|
+
}
|
|
939
|
+
};
|
|
888
940
|
const ValuesByBreakpointSchema$1 = z.record(z.lazy(() => PrimitiveValueSchema$1));
|
|
941
|
+
const BindingSourceTypeEnumSchema$1 = z
|
|
942
|
+
.array(z.enum(['entry', 'asset', 'manual', 'experience']))
|
|
943
|
+
.nonempty();
|
|
889
944
|
const DesignValueSchema$1 = z
|
|
890
945
|
.object({
|
|
891
946
|
type: z.literal('DesignValue'),
|
|
@@ -918,8 +973,6 @@ const ComponentValueSchema$1 = z
|
|
|
918
973
|
key: z.string(),
|
|
919
974
|
})
|
|
920
975
|
.strict();
|
|
921
|
-
// TODO: finalize schema structure before release
|
|
922
|
-
// https://contentful.atlassian.net/browse/LUMOS-523
|
|
923
976
|
const NoValueSchema$1 = z.object({ type: z.literal('NoValue') }).strict();
|
|
924
977
|
const ComponentPropertyValueSchema$1 = z.discriminatedUnion('type', [
|
|
925
978
|
DesignValueSchema$1,
|
|
@@ -931,41 +984,12 @@ const ComponentPropertyValueSchema$1 = z.discriminatedUnion('type', [
|
|
|
931
984
|
]);
|
|
932
985
|
// TODO: finalize schema structure before release
|
|
933
986
|
// https://contentful.atlassian.net/browse/LUMOS-523
|
|
934
|
-
const VariableMappingSchema$1 = z.object({
|
|
935
|
-
patternPropertyDefinitionId: propertyKeySchema$1,
|
|
936
|
-
type: z.literal('ContentTypeMapping'),
|
|
937
|
-
pathsByContentType: z.record(z.string(), z.object({ path: z.string() })),
|
|
938
|
-
});
|
|
939
|
-
const VariableMappingsSchema$1 = z.record(propertyKeySchema$1, VariableMappingSchema$1);
|
|
940
|
-
// TODO: finalize schema structure before release
|
|
941
|
-
// https://contentful.atlassian.net/browse/LUMOS-523
|
|
942
|
-
const PatternPropertyDefinitionSchema$1 = z.object({
|
|
943
|
-
defaultValue: z
|
|
944
|
-
.record(z.string(), z.object({
|
|
945
|
-
sys: z.object({
|
|
946
|
-
type: z.literal('Link'),
|
|
947
|
-
id: z.string(),
|
|
948
|
-
linkType: z.enum(['Entry']),
|
|
949
|
-
}),
|
|
950
|
-
}))
|
|
951
|
-
.optional(),
|
|
952
|
-
contentTypes: z.record(z.string(), z.object({
|
|
953
|
-
sys: z.object({
|
|
954
|
-
type: z.literal('Link'),
|
|
955
|
-
id: z.string(),
|
|
956
|
-
linkType: z.enum(['ContentType']),
|
|
957
|
-
}),
|
|
958
|
-
})),
|
|
959
|
-
});
|
|
960
|
-
const PatternPropertyDefinitionsSchema$1 = z.record(propertyKeySchema$1, PatternPropertyDefinitionSchema$1);
|
|
961
|
-
// TODO: finalize schema structure before release
|
|
962
|
-
// https://contentful.atlassian.net/browse/LUMOS-523
|
|
963
987
|
const PatternPropertySchema$1 = z.object({
|
|
964
988
|
type: z.literal('BoundValue'),
|
|
965
989
|
path: z.string(),
|
|
966
990
|
contentType: z.string(),
|
|
967
991
|
});
|
|
968
|
-
const
|
|
992
|
+
const PatternPropertiesSchema$1 = z.record(propertyKeySchema$1, PatternPropertySchema$1);
|
|
969
993
|
const BreakpointSchema$1 = z
|
|
970
994
|
.object({
|
|
971
995
|
id: propertyKeySchema$1,
|
|
@@ -975,12 +999,6 @@ const BreakpointSchema$1 = z
|
|
|
975
999
|
displayIcon: z.enum(['desktop', 'tablet', 'mobile']).optional(),
|
|
976
1000
|
})
|
|
977
1001
|
.strict();
|
|
978
|
-
const UnboundValuesSchema$1 = z.record(uuidKeySchema$1, z.object({
|
|
979
|
-
value: PrimitiveValueSchema$1,
|
|
980
|
-
}));
|
|
981
|
-
const ComponentTreeNodeIdSchema$1 = z
|
|
982
|
-
.string()
|
|
983
|
-
.regex(/^[a-zA-Z0-9]{1,8}$/, { message: 'Does not match /^[a-zA-Z0-9]{1,8}$/' });
|
|
984
1002
|
// Use helper schema to define a recursive schema with its type correctly below
|
|
985
1003
|
const BaseComponentTreeNodeSchema$1 = z.object({
|
|
986
1004
|
id: ComponentTreeNodeIdSchema$1.optional(),
|
|
@@ -988,14 +1006,8 @@ const BaseComponentTreeNodeSchema$1 = z.object({
|
|
|
988
1006
|
displayName: z.string().optional(),
|
|
989
1007
|
slotId: z.string().optional(),
|
|
990
1008
|
variables: z.record(propertyKeySchema$1, ComponentPropertyValueSchema$1),
|
|
991
|
-
patternProperties:
|
|
1009
|
+
patternProperties: PatternPropertiesSchema$1.optional(),
|
|
992
1010
|
});
|
|
993
|
-
const ComponentTreeNodeSchema$1 = BaseComponentTreeNodeSchema$1.extend({
|
|
994
|
-
children: z.lazy(() => ComponentTreeNodeSchema$1.array()),
|
|
995
|
-
});
|
|
996
|
-
const BindingSourceTypeEnumSchema$1 = z
|
|
997
|
-
.array(z.enum(['entry', 'asset', 'manual', 'experience']))
|
|
998
|
-
.nonempty();
|
|
999
1011
|
const ComponentVariableSchema$1 = z.object({
|
|
1000
1012
|
displayName: z.string().optional(),
|
|
1001
1013
|
type: DefinitionPropertyTypeSchema$1,
|
|
@@ -1016,8 +1028,25 @@ const ComponentVariableSchema$1 = z.object({
|
|
|
1016
1028
|
})
|
|
1017
1029
|
.optional(),
|
|
1018
1030
|
});
|
|
1019
|
-
const
|
|
1020
|
-
|
|
1031
|
+
const ComponentTreeNodeSchema$1 = BaseComponentTreeNodeSchema$1.extend({
|
|
1032
|
+
children: z.lazy(() => ComponentTreeNodeSchema$1.array()),
|
|
1033
|
+
});
|
|
1034
|
+
const ComponentTreeSchema$1 = z
|
|
1035
|
+
.object({
|
|
1036
|
+
breakpoints: z.array(BreakpointSchema$1).superRefine(breakpointsRefinement$1),
|
|
1037
|
+
children: z.array(ComponentTreeNodeSchema$1),
|
|
1038
|
+
schemaVersion: SchemaVersions$1,
|
|
1039
|
+
})
|
|
1040
|
+
.strict();
|
|
1041
|
+
const localeWrapper$1 = (fieldSchema) => z.record(z.string(), fieldSchema);
|
|
1042
|
+
|
|
1043
|
+
z.object({
|
|
1044
|
+
componentTree: localeWrapper$1(ComponentTreeSchema$1),
|
|
1045
|
+
dataSource: localeWrapper$1(DataSourceSchema$1),
|
|
1046
|
+
unboundValues: localeWrapper$1(UnboundValuesSchema$1),
|
|
1047
|
+
usedComponents: localeWrapper$1(UsedComponentsSchema$1).optional(),
|
|
1048
|
+
});
|
|
1049
|
+
|
|
1021
1050
|
const THUMBNAIL_IDS$1 = [
|
|
1022
1051
|
'columns',
|
|
1023
1052
|
'columnsPlusRight',
|
|
@@ -1043,6 +1072,37 @@ const THUMBNAIL_IDS$1 = [
|
|
|
1043
1072
|
'textColumns',
|
|
1044
1073
|
'duplex',
|
|
1045
1074
|
];
|
|
1075
|
+
// TODO: finalize schema structure before release
|
|
1076
|
+
// https://contentful.atlassian.net/browse/LUMOS-523
|
|
1077
|
+
const VariableMappingSchema$1 = z.object({
|
|
1078
|
+
patternPropertyDefinitionId: propertyKeySchema$1,
|
|
1079
|
+
type: z.literal('ContentTypeMapping'),
|
|
1080
|
+
pathsByContentType: z.record(z.string(), z.object({ path: z.string() })),
|
|
1081
|
+
});
|
|
1082
|
+
// TODO: finalize schema structure before release
|
|
1083
|
+
// https://contentful.atlassian.net/browse/LUMOS-523
|
|
1084
|
+
const PatternPropertyDefinitionSchema$1 = z.object({
|
|
1085
|
+
defaultValue: z
|
|
1086
|
+
.record(z.string(), z.object({
|
|
1087
|
+
sys: z.object({
|
|
1088
|
+
type: z.literal('Link'),
|
|
1089
|
+
id: z.string(),
|
|
1090
|
+
linkType: z.enum(['Entry']),
|
|
1091
|
+
}),
|
|
1092
|
+
}))
|
|
1093
|
+
.optional(),
|
|
1094
|
+
contentTypes: z.record(z.string(), z.object({
|
|
1095
|
+
sys: z.object({
|
|
1096
|
+
type: z.literal('Link'),
|
|
1097
|
+
id: z.string(),
|
|
1098
|
+
linkType: z.enum(['ContentType']),
|
|
1099
|
+
}),
|
|
1100
|
+
})),
|
|
1101
|
+
});
|
|
1102
|
+
const PatternPropertyDefinitionsSchema$1 = z.record(propertyKeySchema$1, PatternPropertyDefinitionSchema$1);
|
|
1103
|
+
const VariableMappingsSchema$1 = z.record(propertyKeySchema$1, VariableMappingSchema$1);
|
|
1104
|
+
const ComponentVariablesSchema$1 = z.record(z.string().regex(/^[a-zA-Z0-9-_]{1,54}$/), // Here the key is <variableName>_<nanoidId> so we need to allow for a longer length
|
|
1105
|
+
ComponentVariableSchema$1);
|
|
1046
1106
|
const ComponentSettingsSchema$1 = z.object({
|
|
1047
1107
|
variableDefinitions: ComponentVariablesSchema$1,
|
|
1048
1108
|
thumbnailId: z.enum(THUMBNAIL_IDS$1).optional(),
|
|
@@ -1050,65 +1110,12 @@ const ComponentSettingsSchema$1 = z.object({
|
|
|
1050
1110
|
variableMappings: VariableMappingsSchema$1.optional(),
|
|
1051
1111
|
patternPropertyDefinitions: PatternPropertyDefinitionsSchema$1.optional(),
|
|
1052
1112
|
});
|
|
1053
|
-
const UsedComponentsSchema$1 = z.array(z.object({
|
|
1054
|
-
sys: z.object({
|
|
1055
|
-
type: z.literal('Link'),
|
|
1056
|
-
id: z.string(),
|
|
1057
|
-
linkType: z.literal('Entry'),
|
|
1058
|
-
}),
|
|
1059
|
-
}));
|
|
1060
|
-
const breakpointsRefinement$1 = (value, ctx) => {
|
|
1061
|
-
if (!value.length || value[0].query !== '*') {
|
|
1062
|
-
ctx.addIssue({
|
|
1063
|
-
code: z.ZodIssueCode.custom,
|
|
1064
|
-
message: `The first breakpoint should include the following attributes: { "query": "*" }`,
|
|
1065
|
-
});
|
|
1066
|
-
}
|
|
1067
|
-
const hasDuplicateIds = value.some((currentBreakpoint, currentBreakpointIndex) => {
|
|
1068
|
-
// check if the current breakpoint id is found in the rest of the array
|
|
1069
|
-
const breakpointIndex = value.findIndex((breakpoint) => breakpoint.id === currentBreakpoint.id);
|
|
1070
|
-
return breakpointIndex !== currentBreakpointIndex;
|
|
1071
|
-
});
|
|
1072
|
-
if (hasDuplicateIds) {
|
|
1073
|
-
ctx.addIssue({
|
|
1074
|
-
code: z.ZodIssueCode.custom,
|
|
1075
|
-
message: `Breakpoint IDs must be unique`,
|
|
1076
|
-
});
|
|
1077
|
-
}
|
|
1078
|
-
// Extract the queries boundary by removing the special characters around it
|
|
1079
|
-
const queries = value.map((bp) => bp.query === '*' ? bp.query : parseInt(bp.query.replace(/px|<|>/, '')));
|
|
1080
|
-
// sort updates queries array in place so we need to create a copy
|
|
1081
|
-
const originalQueries = [...queries];
|
|
1082
|
-
queries.sort((q1, q2) => {
|
|
1083
|
-
if (q1 === '*') {
|
|
1084
|
-
return -1;
|
|
1085
|
-
}
|
|
1086
|
-
if (q2 === '*') {
|
|
1087
|
-
return 1;
|
|
1088
|
-
}
|
|
1089
|
-
return q1 > q2 ? -1 : 1;
|
|
1090
|
-
});
|
|
1091
|
-
if (originalQueries.join('') !== queries.join('')) {
|
|
1092
|
-
ctx.addIssue({
|
|
1093
|
-
code: z.ZodIssueCode.custom,
|
|
1094
|
-
message: `Breakpoints should be ordered from largest to smallest pixel value`,
|
|
1095
|
-
});
|
|
1096
|
-
}
|
|
1097
|
-
};
|
|
1098
|
-
const ComponentTreeSchema$1 = z
|
|
1099
|
-
.object({
|
|
1100
|
-
breakpoints: z.array(BreakpointSchema$1).superRefine(breakpointsRefinement$1),
|
|
1101
|
-
children: z.array(ComponentTreeNodeSchema$1),
|
|
1102
|
-
schemaVersion: SchemaVersions$1,
|
|
1103
|
-
})
|
|
1104
|
-
.strict();
|
|
1105
|
-
const localeWrapper$1 = (fieldSchema) => z.record(z.string(), fieldSchema);
|
|
1106
1113
|
z.object({
|
|
1107
1114
|
componentTree: localeWrapper$1(ComponentTreeSchema$1),
|
|
1108
1115
|
dataSource: localeWrapper$1(DataSourceSchema$1),
|
|
1109
1116
|
unboundValues: localeWrapper$1(UnboundValuesSchema$1),
|
|
1110
1117
|
usedComponents: localeWrapper$1(UsedComponentsSchema$1).optional(),
|
|
1111
|
-
componentSettings: localeWrapper$1(ComponentSettingsSchema$1)
|
|
1118
|
+
componentSettings: localeWrapper$1(ComponentSettingsSchema$1),
|
|
1112
1119
|
});
|
|
1113
1120
|
|
|
1114
1121
|
z.object({
|
|
@@ -1392,6 +1399,51 @@ let DebugLogger$1 = class DebugLogger {
|
|
|
1392
1399
|
DebugLogger$1.instance = null;
|
|
1393
1400
|
DebugLogger$1.getInstance();
|
|
1394
1401
|
|
|
1402
|
+
const findOutermostCoordinates = (first, second) => {
|
|
1403
|
+
return {
|
|
1404
|
+
top: Math.min(first.top, second.top),
|
|
1405
|
+
right: Math.max(first.right, second.right),
|
|
1406
|
+
bottom: Math.max(first.bottom, second.bottom),
|
|
1407
|
+
left: Math.min(first.left, second.left),
|
|
1408
|
+
};
|
|
1409
|
+
};
|
|
1410
|
+
const getElementCoordinates = (element) => {
|
|
1411
|
+
const rect = element.getBoundingClientRect();
|
|
1412
|
+
/**
|
|
1413
|
+
* If element does not have children, or element has it's own width or height,
|
|
1414
|
+
* return the element's coordinates.
|
|
1415
|
+
*/
|
|
1416
|
+
if (element.children.length === 0 || rect.width !== 0 || rect.height !== 0) {
|
|
1417
|
+
return rect;
|
|
1418
|
+
}
|
|
1419
|
+
const rects = [];
|
|
1420
|
+
/**
|
|
1421
|
+
* If element has children, or element does not have it's own width and height,
|
|
1422
|
+
* we find the cordinates of the children, and assume the outermost coordinates of the children
|
|
1423
|
+
* as the coordinate of the element.
|
|
1424
|
+
*
|
|
1425
|
+
* E.g child1 => {top: 2, bottom: 3, left: 4, right: 6} & child2 => {top: 1, bottom: 8, left: 12, right: 24}
|
|
1426
|
+
* The final assumed coordinates of the element would be => { top: 1, right: 24, bottom: 8, left: 4 }
|
|
1427
|
+
*/
|
|
1428
|
+
for (const child of element.children) {
|
|
1429
|
+
const childRect = getElementCoordinates(child);
|
|
1430
|
+
if (childRect.width !== 0 || childRect.height !== 0) {
|
|
1431
|
+
const { top, right, bottom, left } = childRect;
|
|
1432
|
+
rects.push({ top, right, bottom, left });
|
|
1433
|
+
}
|
|
1434
|
+
}
|
|
1435
|
+
if (rects.length === 0) {
|
|
1436
|
+
return rect;
|
|
1437
|
+
}
|
|
1438
|
+
const { top, right, bottom, left } = rects.reduce(findOutermostCoordinates);
|
|
1439
|
+
return DOMRect.fromRect({
|
|
1440
|
+
x: left,
|
|
1441
|
+
y: top,
|
|
1442
|
+
height: bottom - top,
|
|
1443
|
+
width: right - left,
|
|
1444
|
+
});
|
|
1445
|
+
};
|
|
1446
|
+
|
|
1395
1447
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1396
1448
|
const isLinkToAsset = (variable) => {
|
|
1397
1449
|
if (!variable)
|
|
@@ -3026,17 +3078,16 @@ const PrimitiveValueSchema = z.union([
|
|
|
3026
3078
|
z.record(z.any(), z.any()),
|
|
3027
3079
|
z.undefined(),
|
|
3028
3080
|
]);
|
|
3081
|
+
const UsedComponentsSchema = z.array(z.object({
|
|
3082
|
+
sys: z.object({
|
|
3083
|
+
type: z.literal('Link'),
|
|
3084
|
+
id: z.string(),
|
|
3085
|
+
linkType: z.literal('Entry'),
|
|
3086
|
+
}),
|
|
3087
|
+
}));
|
|
3029
3088
|
const uuidKeySchema = z
|
|
3030
3089
|
.string()
|
|
3031
3090
|
.regex(/^[a-zA-Z0-9-_]{1,21}$/, { message: 'Does not match /^[a-zA-Z0-9-_]{1,21}$/' });
|
|
3032
|
-
/**
|
|
3033
|
-
* Property keys for imported components have a limit of 32 characters (to be implemented) while
|
|
3034
|
-
* property keys for patterns have a limit of 54 characters (<32-char-variabl-name>_<21-char-nanoid-id>).
|
|
3035
|
-
* Because we cannot distinguish between the two in the componentTree, we will use the larger limit for both.
|
|
3036
|
-
*/
|
|
3037
|
-
const propertyKeySchema = z
|
|
3038
|
-
.string()
|
|
3039
|
-
.regex(/^[a-zA-Z0-9-_]{1,54}$/, { message: 'Does not match /^[a-zA-Z0-9-_]{1,54}$/' });
|
|
3040
3091
|
const DataSourceSchema = z.record(uuidKeySchema, z.object({
|
|
3041
3092
|
sys: z.object({
|
|
3042
3093
|
type: z.literal('Link'),
|
|
@@ -3044,7 +3095,62 @@ const DataSourceSchema = z.record(uuidKeySchema, z.object({
|
|
|
3044
3095
|
linkType: z.enum(['Entry', 'Asset']),
|
|
3045
3096
|
}),
|
|
3046
3097
|
}));
|
|
3098
|
+
const UnboundValuesSchema = z.record(uuidKeySchema, z.object({
|
|
3099
|
+
value: PrimitiveValueSchema,
|
|
3100
|
+
}));
|
|
3101
|
+
/**
|
|
3102
|
+
* Property keys for imported components have a limit of 32 characters (to be implemented) while
|
|
3103
|
+
* property keys for patterns have a limit of 54 characters (<32-char-variable-name>_<21-char-nanoid-id>).
|
|
3104
|
+
* Because we cannot distinguish between the two in the componentTree, we will use the larger limit for both.
|
|
3105
|
+
*/
|
|
3106
|
+
const propertyKeySchema = z
|
|
3107
|
+
.string()
|
|
3108
|
+
.regex(/^[a-zA-Z0-9-_]{1,54}$/, { message: 'Does not match /^[a-zA-Z0-9-_]{1,54}$/' });
|
|
3109
|
+
const ComponentTreeNodeIdSchema = z
|
|
3110
|
+
.string()
|
|
3111
|
+
.regex(/^[a-zA-Z0-9]{1,8}$/, { message: 'Does not match /^[a-zA-Z0-9]{1,8}$/' });
|
|
3112
|
+
const breakpointsRefinement = (value, ctx) => {
|
|
3113
|
+
if (!value.length || value[0].query !== '*') {
|
|
3114
|
+
ctx.addIssue({
|
|
3115
|
+
code: z.ZodIssueCode.custom,
|
|
3116
|
+
message: `The first breakpoint should include the following attributes: { "query": "*" }`,
|
|
3117
|
+
});
|
|
3118
|
+
}
|
|
3119
|
+
const hasDuplicateIds = value.some((currentBreakpoint, currentBreakpointIndex) => {
|
|
3120
|
+
// check if the current breakpoint id is found in the rest of the array
|
|
3121
|
+
const breakpointIndex = value.findIndex((breakpoint) => breakpoint.id === currentBreakpoint.id);
|
|
3122
|
+
return breakpointIndex !== currentBreakpointIndex;
|
|
3123
|
+
});
|
|
3124
|
+
if (hasDuplicateIds) {
|
|
3125
|
+
ctx.addIssue({
|
|
3126
|
+
code: z.ZodIssueCode.custom,
|
|
3127
|
+
message: `Breakpoint IDs must be unique`,
|
|
3128
|
+
});
|
|
3129
|
+
}
|
|
3130
|
+
// Extract the queries boundary by removing the special characters around it
|
|
3131
|
+
const queries = value.map((bp) => bp.query === '*' ? bp.query : parseInt(bp.query.replace(/px|<|>/, '')));
|
|
3132
|
+
// sort updates queries array in place so we need to create a copy
|
|
3133
|
+
const originalQueries = [...queries];
|
|
3134
|
+
queries.sort((q1, q2) => {
|
|
3135
|
+
if (q1 === '*') {
|
|
3136
|
+
return -1;
|
|
3137
|
+
}
|
|
3138
|
+
if (q2 === '*') {
|
|
3139
|
+
return 1;
|
|
3140
|
+
}
|
|
3141
|
+
return q1 > q2 ? -1 : 1;
|
|
3142
|
+
});
|
|
3143
|
+
if (originalQueries.join('') !== queries.join('')) {
|
|
3144
|
+
ctx.addIssue({
|
|
3145
|
+
code: z.ZodIssueCode.custom,
|
|
3146
|
+
message: `Breakpoints should be ordered from largest to smallest pixel value`,
|
|
3147
|
+
});
|
|
3148
|
+
}
|
|
3149
|
+
};
|
|
3047
3150
|
const ValuesByBreakpointSchema = z.record(z.lazy(() => PrimitiveValueSchema));
|
|
3151
|
+
const BindingSourceTypeEnumSchema = z
|
|
3152
|
+
.array(z.enum(['entry', 'asset', 'manual', 'experience']))
|
|
3153
|
+
.nonempty();
|
|
3048
3154
|
const DesignValueSchema = z
|
|
3049
3155
|
.object({
|
|
3050
3156
|
type: z.literal('DesignValue'),
|
|
@@ -3077,8 +3183,6 @@ const ComponentValueSchema = z
|
|
|
3077
3183
|
key: z.string(),
|
|
3078
3184
|
})
|
|
3079
3185
|
.strict();
|
|
3080
|
-
// TODO: finalize schema structure before release
|
|
3081
|
-
// https://contentful.atlassian.net/browse/LUMOS-523
|
|
3082
3186
|
const NoValueSchema = z.object({ type: z.literal('NoValue') }).strict();
|
|
3083
3187
|
const ComponentPropertyValueSchema = z.discriminatedUnion('type', [
|
|
3084
3188
|
DesignValueSchema,
|
|
@@ -3090,41 +3194,12 @@ const ComponentPropertyValueSchema = z.discriminatedUnion('type', [
|
|
|
3090
3194
|
]);
|
|
3091
3195
|
// TODO: finalize schema structure before release
|
|
3092
3196
|
// https://contentful.atlassian.net/browse/LUMOS-523
|
|
3093
|
-
const VariableMappingSchema = z.object({
|
|
3094
|
-
patternPropertyDefinitionId: propertyKeySchema,
|
|
3095
|
-
type: z.literal('ContentTypeMapping'),
|
|
3096
|
-
pathsByContentType: z.record(z.string(), z.object({ path: z.string() })),
|
|
3097
|
-
});
|
|
3098
|
-
const VariableMappingsSchema = z.record(propertyKeySchema, VariableMappingSchema);
|
|
3099
|
-
// TODO: finalize schema structure before release
|
|
3100
|
-
// https://contentful.atlassian.net/browse/LUMOS-523
|
|
3101
|
-
const PatternPropertyDefinitionSchema = z.object({
|
|
3102
|
-
defaultValue: z
|
|
3103
|
-
.record(z.string(), z.object({
|
|
3104
|
-
sys: z.object({
|
|
3105
|
-
type: z.literal('Link'),
|
|
3106
|
-
id: z.string(),
|
|
3107
|
-
linkType: z.enum(['Entry']),
|
|
3108
|
-
}),
|
|
3109
|
-
}))
|
|
3110
|
-
.optional(),
|
|
3111
|
-
contentTypes: z.record(z.string(), z.object({
|
|
3112
|
-
sys: z.object({
|
|
3113
|
-
type: z.literal('Link'),
|
|
3114
|
-
id: z.string(),
|
|
3115
|
-
linkType: z.enum(['ContentType']),
|
|
3116
|
-
}),
|
|
3117
|
-
})),
|
|
3118
|
-
});
|
|
3119
|
-
const PatternPropertyDefinitionsSchema = z.record(propertyKeySchema, PatternPropertyDefinitionSchema);
|
|
3120
|
-
// TODO: finalize schema structure before release
|
|
3121
|
-
// https://contentful.atlassian.net/browse/LUMOS-523
|
|
3122
3197
|
const PatternPropertySchema = z.object({
|
|
3123
3198
|
type: z.literal('BoundValue'),
|
|
3124
3199
|
path: z.string(),
|
|
3125
3200
|
contentType: z.string(),
|
|
3126
3201
|
});
|
|
3127
|
-
const
|
|
3202
|
+
const PatternPropertiesSchema = z.record(propertyKeySchema, PatternPropertySchema);
|
|
3128
3203
|
const BreakpointSchema = z
|
|
3129
3204
|
.object({
|
|
3130
3205
|
id: propertyKeySchema,
|
|
@@ -3134,12 +3209,6 @@ const BreakpointSchema = z
|
|
|
3134
3209
|
displayIcon: z.enum(['desktop', 'tablet', 'mobile']).optional(),
|
|
3135
3210
|
})
|
|
3136
3211
|
.strict();
|
|
3137
|
-
const UnboundValuesSchema = z.record(uuidKeySchema, z.object({
|
|
3138
|
-
value: PrimitiveValueSchema,
|
|
3139
|
-
}));
|
|
3140
|
-
const ComponentTreeNodeIdSchema = z
|
|
3141
|
-
.string()
|
|
3142
|
-
.regex(/^[a-zA-Z0-9]{1,8}$/, { message: 'Does not match /^[a-zA-Z0-9]{1,8}$/' });
|
|
3143
3212
|
// Use helper schema to define a recursive schema with its type correctly below
|
|
3144
3213
|
const BaseComponentTreeNodeSchema = z.object({
|
|
3145
3214
|
id: ComponentTreeNodeIdSchema.optional(),
|
|
@@ -3147,14 +3216,8 @@ const BaseComponentTreeNodeSchema = z.object({
|
|
|
3147
3216
|
displayName: z.string().optional(),
|
|
3148
3217
|
slotId: z.string().optional(),
|
|
3149
3218
|
variables: z.record(propertyKeySchema, ComponentPropertyValueSchema),
|
|
3150
|
-
patternProperties:
|
|
3219
|
+
patternProperties: PatternPropertiesSchema.optional(),
|
|
3151
3220
|
});
|
|
3152
|
-
const ComponentTreeNodeSchema = BaseComponentTreeNodeSchema.extend({
|
|
3153
|
-
children: z.lazy(() => ComponentTreeNodeSchema.array()),
|
|
3154
|
-
});
|
|
3155
|
-
const BindingSourceTypeEnumSchema = z
|
|
3156
|
-
.array(z.enum(['entry', 'asset', 'manual', 'experience']))
|
|
3157
|
-
.nonempty();
|
|
3158
3221
|
const ComponentVariableSchema = z.object({
|
|
3159
3222
|
displayName: z.string().optional(),
|
|
3160
3223
|
type: DefinitionPropertyTypeSchema,
|
|
@@ -3175,8 +3238,25 @@ const ComponentVariableSchema = z.object({
|
|
|
3175
3238
|
})
|
|
3176
3239
|
.optional(),
|
|
3177
3240
|
});
|
|
3178
|
-
const
|
|
3179
|
-
|
|
3241
|
+
const ComponentTreeNodeSchema = BaseComponentTreeNodeSchema.extend({
|
|
3242
|
+
children: z.lazy(() => ComponentTreeNodeSchema.array()),
|
|
3243
|
+
});
|
|
3244
|
+
const ComponentTreeSchema = z
|
|
3245
|
+
.object({
|
|
3246
|
+
breakpoints: z.array(BreakpointSchema).superRefine(breakpointsRefinement),
|
|
3247
|
+
children: z.array(ComponentTreeNodeSchema),
|
|
3248
|
+
schemaVersion: SchemaVersions,
|
|
3249
|
+
})
|
|
3250
|
+
.strict();
|
|
3251
|
+
const localeWrapper = (fieldSchema) => z.record(z.string(), fieldSchema);
|
|
3252
|
+
|
|
3253
|
+
z.object({
|
|
3254
|
+
componentTree: localeWrapper(ComponentTreeSchema),
|
|
3255
|
+
dataSource: localeWrapper(DataSourceSchema),
|
|
3256
|
+
unboundValues: localeWrapper(UnboundValuesSchema),
|
|
3257
|
+
usedComponents: localeWrapper(UsedComponentsSchema).optional(),
|
|
3258
|
+
});
|
|
3259
|
+
|
|
3180
3260
|
const THUMBNAIL_IDS = [
|
|
3181
3261
|
'columns',
|
|
3182
3262
|
'columnsPlusRight',
|
|
@@ -3202,6 +3282,37 @@ const THUMBNAIL_IDS = [
|
|
|
3202
3282
|
'textColumns',
|
|
3203
3283
|
'duplex',
|
|
3204
3284
|
];
|
|
3285
|
+
// TODO: finalize schema structure before release
|
|
3286
|
+
// https://contentful.atlassian.net/browse/LUMOS-523
|
|
3287
|
+
const VariableMappingSchema = z.object({
|
|
3288
|
+
patternPropertyDefinitionId: propertyKeySchema,
|
|
3289
|
+
type: z.literal('ContentTypeMapping'),
|
|
3290
|
+
pathsByContentType: z.record(z.string(), z.object({ path: z.string() })),
|
|
3291
|
+
});
|
|
3292
|
+
// TODO: finalize schema structure before release
|
|
3293
|
+
// https://contentful.atlassian.net/browse/LUMOS-523
|
|
3294
|
+
const PatternPropertyDefinitionSchema = z.object({
|
|
3295
|
+
defaultValue: z
|
|
3296
|
+
.record(z.string(), z.object({
|
|
3297
|
+
sys: z.object({
|
|
3298
|
+
type: z.literal('Link'),
|
|
3299
|
+
id: z.string(),
|
|
3300
|
+
linkType: z.enum(['Entry']),
|
|
3301
|
+
}),
|
|
3302
|
+
}))
|
|
3303
|
+
.optional(),
|
|
3304
|
+
contentTypes: z.record(z.string(), z.object({
|
|
3305
|
+
sys: z.object({
|
|
3306
|
+
type: z.literal('Link'),
|
|
3307
|
+
id: z.string(),
|
|
3308
|
+
linkType: z.enum(['ContentType']),
|
|
3309
|
+
}),
|
|
3310
|
+
})),
|
|
3311
|
+
});
|
|
3312
|
+
const PatternPropertyDefinitionsSchema = z.record(propertyKeySchema, PatternPropertyDefinitionSchema);
|
|
3313
|
+
const VariableMappingsSchema = z.record(propertyKeySchema, VariableMappingSchema);
|
|
3314
|
+
const ComponentVariablesSchema = z.record(z.string().regex(/^[a-zA-Z0-9-_]{1,54}$/), // Here the key is <variableName>_<nanoidId> so we need to allow for a longer length
|
|
3315
|
+
ComponentVariableSchema);
|
|
3205
3316
|
const ComponentSettingsSchema = z.object({
|
|
3206
3317
|
variableDefinitions: ComponentVariablesSchema,
|
|
3207
3318
|
thumbnailId: z.enum(THUMBNAIL_IDS).optional(),
|
|
@@ -3209,65 +3320,12 @@ const ComponentSettingsSchema = z.object({
|
|
|
3209
3320
|
variableMappings: VariableMappingsSchema.optional(),
|
|
3210
3321
|
patternPropertyDefinitions: PatternPropertyDefinitionsSchema.optional(),
|
|
3211
3322
|
});
|
|
3212
|
-
const UsedComponentsSchema = z.array(z.object({
|
|
3213
|
-
sys: z.object({
|
|
3214
|
-
type: z.literal('Link'),
|
|
3215
|
-
id: z.string(),
|
|
3216
|
-
linkType: z.literal('Entry'),
|
|
3217
|
-
}),
|
|
3218
|
-
}));
|
|
3219
|
-
const breakpointsRefinement = (value, ctx) => {
|
|
3220
|
-
if (!value.length || value[0].query !== '*') {
|
|
3221
|
-
ctx.addIssue({
|
|
3222
|
-
code: z.ZodIssueCode.custom,
|
|
3223
|
-
message: `The first breakpoint should include the following attributes: { "query": "*" }`,
|
|
3224
|
-
});
|
|
3225
|
-
}
|
|
3226
|
-
const hasDuplicateIds = value.some((currentBreakpoint, currentBreakpointIndex) => {
|
|
3227
|
-
// check if the current breakpoint id is found in the rest of the array
|
|
3228
|
-
const breakpointIndex = value.findIndex((breakpoint) => breakpoint.id === currentBreakpoint.id);
|
|
3229
|
-
return breakpointIndex !== currentBreakpointIndex;
|
|
3230
|
-
});
|
|
3231
|
-
if (hasDuplicateIds) {
|
|
3232
|
-
ctx.addIssue({
|
|
3233
|
-
code: z.ZodIssueCode.custom,
|
|
3234
|
-
message: `Breakpoint IDs must be unique`,
|
|
3235
|
-
});
|
|
3236
|
-
}
|
|
3237
|
-
// Extract the queries boundary by removing the special characters around it
|
|
3238
|
-
const queries = value.map((bp) => bp.query === '*' ? bp.query : parseInt(bp.query.replace(/px|<|>/, '')));
|
|
3239
|
-
// sort updates queries array in place so we need to create a copy
|
|
3240
|
-
const originalQueries = [...queries];
|
|
3241
|
-
queries.sort((q1, q2) => {
|
|
3242
|
-
if (q1 === '*') {
|
|
3243
|
-
return -1;
|
|
3244
|
-
}
|
|
3245
|
-
if (q2 === '*') {
|
|
3246
|
-
return 1;
|
|
3247
|
-
}
|
|
3248
|
-
return q1 > q2 ? -1 : 1;
|
|
3249
|
-
});
|
|
3250
|
-
if (originalQueries.join('') !== queries.join('')) {
|
|
3251
|
-
ctx.addIssue({
|
|
3252
|
-
code: z.ZodIssueCode.custom,
|
|
3253
|
-
message: `Breakpoints should be ordered from largest to smallest pixel value`,
|
|
3254
|
-
});
|
|
3255
|
-
}
|
|
3256
|
-
};
|
|
3257
|
-
const ComponentTreeSchema = z
|
|
3258
|
-
.object({
|
|
3259
|
-
breakpoints: z.array(BreakpointSchema).superRefine(breakpointsRefinement),
|
|
3260
|
-
children: z.array(ComponentTreeNodeSchema),
|
|
3261
|
-
schemaVersion: SchemaVersions,
|
|
3262
|
-
})
|
|
3263
|
-
.strict();
|
|
3264
|
-
const localeWrapper = (fieldSchema) => z.record(z.string(), fieldSchema);
|
|
3265
3323
|
z.object({
|
|
3266
3324
|
componentTree: localeWrapper(ComponentTreeSchema),
|
|
3267
3325
|
dataSource: localeWrapper(DataSourceSchema),
|
|
3268
3326
|
unboundValues: localeWrapper(UnboundValuesSchema),
|
|
3269
3327
|
usedComponents: localeWrapper(UsedComponentsSchema).optional(),
|
|
3270
|
-
componentSettings: localeWrapper(ComponentSettingsSchema)
|
|
3328
|
+
componentSettings: localeWrapper(ComponentSettingsSchema),
|
|
3271
3329
|
});
|
|
3272
3330
|
|
|
3273
3331
|
z.object({
|
|
@@ -4061,18 +4119,115 @@ const EmptyCanvasMessage = () => {
|
|
|
4061
4119
|
React.createElement("span", { className: styles['empty-canvas-label'] }, "Add components to begin")));
|
|
4062
4120
|
};
|
|
4063
4121
|
|
|
4122
|
+
/**
|
|
4123
|
+
* This function gets the element co-ordinates of a specified component in the DOM and its parent
|
|
4124
|
+
* and sends the DOM Rect to the client app.
|
|
4125
|
+
*/
|
|
4126
|
+
const sendCanvasGeometryUpdatedMessage = async (tree, sourceEvent) => {
|
|
4127
|
+
const nodeToCoordinatesMap = {};
|
|
4128
|
+
await waitForAllImagesToBeLoaded();
|
|
4129
|
+
collectNodeCoordinates(tree.root, nodeToCoordinatesMap);
|
|
4130
|
+
sendMessage(OUTGOING_EVENTS.CanvasGeometryUpdated, {
|
|
4131
|
+
size: {
|
|
4132
|
+
width: document.documentElement.scrollWidth,
|
|
4133
|
+
height: document.documentElement.scrollHeight,
|
|
4134
|
+
},
|
|
4135
|
+
nodes: nodeToCoordinatesMap,
|
|
4136
|
+
sourceEvent,
|
|
4137
|
+
});
|
|
4138
|
+
};
|
|
4139
|
+
const collectNodeCoordinates = (node, nodeToCoordinatesMap) => {
|
|
4140
|
+
const selectedElement = document.querySelector(`[data-cf-node-id="${node.data.id}"]`);
|
|
4141
|
+
if (selectedElement) {
|
|
4142
|
+
const rect = getElementCoordinates(selectedElement);
|
|
4143
|
+
nodeToCoordinatesMap[node.data.id] = {
|
|
4144
|
+
coordinates: {
|
|
4145
|
+
x: rect.x + window.scrollX,
|
|
4146
|
+
y: rect.y + window.scrollY,
|
|
4147
|
+
width: rect.width,
|
|
4148
|
+
height: rect.height,
|
|
4149
|
+
},
|
|
4150
|
+
};
|
|
4151
|
+
}
|
|
4152
|
+
node.children.forEach((child) => collectNodeCoordinates(child, nodeToCoordinatesMap));
|
|
4153
|
+
};
|
|
4154
|
+
const waitForAllImagesToBeLoaded = () => {
|
|
4155
|
+
// If the document contains an image, wait for this image to be loaded before collecting & sending all geometry data.
|
|
4156
|
+
const allImageNodes = document.querySelectorAll('img');
|
|
4157
|
+
return Promise.all(Array.from(allImageNodes).map((imageNode) => {
|
|
4158
|
+
if (imageNode.complete) {
|
|
4159
|
+
return Promise.resolve();
|
|
4160
|
+
}
|
|
4161
|
+
return new Promise((resolve, reject) => {
|
|
4162
|
+
const handleImageLoad = (event) => {
|
|
4163
|
+
imageNode.removeEventListener('load', handleImageLoad);
|
|
4164
|
+
imageNode.removeEventListener('error', handleImageLoad);
|
|
4165
|
+
if (event.type === 'error') {
|
|
4166
|
+
console.warn('Image failed to load:', imageNode);
|
|
4167
|
+
reject();
|
|
4168
|
+
}
|
|
4169
|
+
else {
|
|
4170
|
+
resolve();
|
|
4171
|
+
}
|
|
4172
|
+
};
|
|
4173
|
+
imageNode.addEventListener('load', handleImageLoad);
|
|
4174
|
+
imageNode.addEventListener('error', handleImageLoad);
|
|
4175
|
+
});
|
|
4176
|
+
}));
|
|
4177
|
+
};
|
|
4178
|
+
|
|
4179
|
+
const useCanvasGeometryUpdates = ({ tree, rootContainerRef, }) => {
|
|
4180
|
+
const debouncedUpdateGeometry = useMemo(() => debounce((tree, sourceEvent) => {
|
|
4181
|
+
// When the DOM changed, we still need to wait for the next frame to ensure that
|
|
4182
|
+
// rendering is complete (e.g. this is required when deleting a node).
|
|
4183
|
+
window.requestAnimationFrame(() => {
|
|
4184
|
+
sendCanvasGeometryUpdatedMessage(tree, sourceEvent);
|
|
4185
|
+
});
|
|
4186
|
+
}, 100, {
|
|
4187
|
+
leading: true,
|
|
4188
|
+
// To be sure, we recalculate it at the end of the frame again. Though, we couldn't
|
|
4189
|
+
// yet show the need for this. So we might be able to drop this later to boost performance.
|
|
4190
|
+
trailing: true,
|
|
4191
|
+
}), []);
|
|
4192
|
+
// Store tree in a ref to avoid the need to deactivate & reactivate the mutation observer
|
|
4193
|
+
// when the tree changes. This is important to avoid missing out on some mutation events.
|
|
4194
|
+
const treeRef = useRef(tree);
|
|
4195
|
+
useEffect(() => {
|
|
4196
|
+
treeRef.current = tree;
|
|
4197
|
+
}, [tree]);
|
|
4198
|
+
// Handling window resize events
|
|
4199
|
+
useEffect(() => {
|
|
4200
|
+
const resizeEventListener = () => debouncedUpdateGeometry(treeRef.current, 'resize');
|
|
4201
|
+
window.addEventListener('resize', resizeEventListener);
|
|
4202
|
+
return () => window.removeEventListener('resize', resizeEventListener);
|
|
4203
|
+
}, [debouncedUpdateGeometry]);
|
|
4204
|
+
// Handling DOM mutations
|
|
4205
|
+
useEffect(() => {
|
|
4206
|
+
if (!rootContainerRef.current)
|
|
4207
|
+
return;
|
|
4208
|
+
const observer = new MutationObserver(() => debouncedUpdateGeometry(treeRef.current, 'mutation'));
|
|
4209
|
+
observer.observe(rootContainerRef.current, {
|
|
4210
|
+
childList: true,
|
|
4211
|
+
subtree: true,
|
|
4212
|
+
attributes: true,
|
|
4213
|
+
});
|
|
4214
|
+
return () => observer.disconnect();
|
|
4215
|
+
}, [debouncedUpdateGeometry, rootContainerRef]);
|
|
4216
|
+
};
|
|
4217
|
+
|
|
4064
4218
|
const RootRenderer = () => {
|
|
4219
|
+
const rootContainerRef = useRef(null);
|
|
4220
|
+
const tree = useTreeStore((state) => state.tree);
|
|
4221
|
+
useCanvasGeometryUpdates({ tree, rootContainerRef });
|
|
4065
4222
|
useEditorSubscriber();
|
|
4066
4223
|
const breakpoints = useTreeStore((state) => state.breakpoints);
|
|
4067
|
-
const containerRef = useRef(null);
|
|
4068
4224
|
const { resolveDesignValue } = useBreakpoints(breakpoints);
|
|
4069
|
-
const tree = useTreeStore((state) => state.tree);
|
|
4070
4225
|
// If the root blockId is defined but not the default string, it is the entry ID
|
|
4071
4226
|
// of the experience/ pattern to properly detect circular dependencies.
|
|
4072
4227
|
const rootBlockId = tree.root.data.blockId ?? ROOT_ID;
|
|
4073
4228
|
const wrappingPatternIds = rootBlockId !== ROOT_ID ? new Set([rootBlockId]) : new Set();
|
|
4074
4229
|
return (React.createElement(React.Fragment, null,
|
|
4075
|
-
React.createElement("div", { "data-ctfl-root": true, className: styles$2.rootContainer, ref:
|
|
4230
|
+
React.createElement("div", { "data-ctfl-root": true, className: styles$2.rootContainer, ref: rootContainerRef }, !tree.root.children.length ? (React.createElement(EmptyCanvasMessage, null)) : (tree.root.children.map((topLevelChildNode) => (React.createElement(EditorBlock, { key: topLevelChildNode.data.id, node: topLevelChildNode, resolveDesignValue: resolveDesignValue, wrappingPatternIds: wrappingPatternIds })))))));
|
|
4076
4231
|
};
|
|
4077
4232
|
|
|
4078
4233
|
const useInitializeEditor = () => {
|