@contentful/experiences-visual-editor-react 3.2.1-dev-20250819T1408-2dcca7e.0 → 3.3.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1298,6 +1298,11 @@ const isLink$1 = (maybeLink) => {
1298
1298
  /**
1299
1299
  * This module encapsulates format of the path to a deep reference.
1300
1300
  */
1301
+ const isPreboundProp = (variable) => {
1302
+ return (variable.type === 'BoundValue' &&
1303
+ typeof variable.isPrebound === 'boolean' &&
1304
+ !!variable.pathsByContentType);
1305
+ };
1301
1306
  const parseDataSourcePathIntoFieldset$1 = (path) => {
1302
1307
  const parsedPath = parseDeepPath$1(path);
1303
1308
  if (null === parsedPath) {
@@ -1336,6 +1341,35 @@ const isDeepPath$1 = (deepPathCandidate) => {
1336
1341
  }
1337
1342
  return deepPathParsed.fields.length > 1;
1338
1343
  };
1344
+ const isDeepPrebinding = (boundValueProperty) => {
1345
+ if (!boundValueProperty?.path || boundValueProperty.type !== 'BoundValue') {
1346
+ return false;
1347
+ }
1348
+ if (!isPreboundProp(boundValueProperty)) {
1349
+ return false;
1350
+ }
1351
+ if (isDeepPath$1(boundValueProperty.path)) {
1352
+ return true;
1353
+ }
1354
+ const hasDeepPathByContentType = Object.values(boundValueProperty.pathsByContentType || {}).some((val) => isDeepPath$1(val.path));
1355
+ return hasDeepPathByContentType;
1356
+ };
1357
+ const getPrebindingPathBySourceEntry = (preboundValueProperty, getHeadEntityByDataSourceKey) => {
1358
+ if (!isPreboundProp(preboundValueProperty)) {
1359
+ return undefined;
1360
+ }
1361
+ // incomplete path due to several content types and not known default source
1362
+ const [, dataSourceKey] = preboundValueProperty.path.split('/');
1363
+ if (!dataSourceKey) {
1364
+ return undefined;
1365
+ }
1366
+ const headEntity = getHeadEntityByDataSourceKey(dataSourceKey);
1367
+ if (headEntity?.sys.type !== 'Entry') {
1368
+ return undefined;
1369
+ }
1370
+ const contentTypeId = headEntity.sys.contentType.sys.id;
1371
+ return preboundValueProperty.pathsByContentType?.[contentTypeId]?.path;
1372
+ };
1339
1373
  const parseDeepPath$1 = (deepPathCandidate) => {
1340
1374
  // ALGORITHM:
1341
1375
  // We start with deep path in form:
@@ -2672,7 +2706,7 @@ class DeepReference {
2672
2706
  return new DeepReference(opt);
2673
2707
  }
2674
2708
  }
2675
- function gatherDeepReferencesFromTree(startingNode, dataSource) {
2709
+ function gatherDeepReferencesFromTree(startingNode, dataSource, getEntityFromLink) {
2676
2710
  const deepReferences = [];
2677
2711
  treeVisit(startingNode, (node) => {
2678
2712
  if (!node.data.props)
@@ -2680,12 +2714,27 @@ function gatherDeepReferencesFromTree(startingNode, dataSource) {
2680
2714
  for (const [, variableMapping] of Object.entries(node.data.props)) {
2681
2715
  if (variableMapping.type !== 'BoundValue')
2682
2716
  continue;
2683
- if (!isDeepPath$1(variableMapping.path))
2684
- continue;
2685
- deepReferences.push(DeepReference.from({
2686
- path: variableMapping.path,
2687
- dataSource,
2688
- }));
2717
+ if (isDeepPath$1(variableMapping.path)) {
2718
+ deepReferences.push(DeepReference.from({
2719
+ path: variableMapping.path,
2720
+ dataSource,
2721
+ }));
2722
+ }
2723
+ else if (isPreboundProp(variableMapping) && isDeepPrebinding(variableMapping)) {
2724
+ const getEntityByDataSourceKey = (dataSourceKey) => {
2725
+ const entityLink = dataSource[dataSourceKey];
2726
+ if (!entityLink)
2727
+ return undefined;
2728
+ return getEntityFromLink(entityLink);
2729
+ };
2730
+ const deepPrebindingPath = getPrebindingPathBySourceEntry(variableMapping, getEntityByDataSourceKey);
2731
+ if (!deepPrebindingPath)
2732
+ continue;
2733
+ deepReferences.push(DeepReference.from({
2734
+ path: deepPrebindingPath,
2735
+ dataSource,
2736
+ }));
2737
+ }
2689
2738
  }
2690
2739
  });
2691
2740
  return deepReferences;
@@ -3813,10 +3862,6 @@ const isLink = (maybeLink) => {
3813
3862
  const link = maybeLink;
3814
3863
  return Boolean(link.sys?.id) && link.sys?.type === 'Link' && Boolean(link.sys?.linkType);
3815
3864
  };
3816
-
3817
- /**
3818
- * This module encapsulates format of the path to a deep reference.
3819
- */
3820
3865
  const parseDataSourcePathIntoFieldset = (path) => {
3821
3866
  const parsedPath = parseDeepPath(path);
3822
3867
  if (null === parsedPath) {
@@ -4305,7 +4350,6 @@ function useEditorSubscriber(inMemoryEntitiesStore) {
4305
4350
  ...Object.values(newDataSource),
4306
4351
  ...assembliesRegistry.values(), // we count assemblies here as "L1 entities", for convenience. Even though they're not headEntities.
4307
4352
  ];
4308
- const deepReferences = gatherDeepReferencesFromTree(tree.root, newDataSource);
4309
4353
  /**
4310
4354
  * Checks only for _missing_ L1 entities
4311
4355
  * WARNING: Does NOT check for entity staleness/versions. If an entity is stale, it will NOT be considered missing.
@@ -4347,6 +4391,7 @@ function useEditorSubscriber(inMemoryEntitiesStore) {
4347
4391
  startFetching();
4348
4392
  await fillupL1({ entityLinksL1 });
4349
4393
  }
4394
+ const deepReferences = gatherDeepReferencesFromTree(tree.root, newDataSource, entityStore.getEntityFromLink.bind(entityStore));
4350
4395
  if (isMissingL2Entities(deepReferences)) {
4351
4396
  startFetching();
4352
4397
  await fillupL2({ deepReferences });
@@ -4713,16 +4758,32 @@ const useComponentProps = ({ node, entityStore, areEntitiesFetched, resolveDesig
4713
4758
  };
4714
4759
  }
4715
4760
  else if (variableMapping.type === 'BoundValue') {
4716
- const [, uuid, path] = variableMapping.path.split('/');
4717
- const binding = dataSource[uuid];
4718
- const variableDefinition = definition.variables[variableName];
4719
- let boundValue = transformBoundContentValue(node.data.props, entityStore, binding, resolveDesignValue, variableName, variableDefinition.type, variableMapping.path);
4761
+ // maybePath, because prebound props would have the type 'BoundValue' but the path would be incomplete
4762
+ // eg: "/uuid" vs "/uuid/fields/[fileName]/~locale" as the regular BoundValue would have
4763
+ const [, uuid, maybePath] = variableMapping.path.split('/');
4764
+ const link = dataSource[uuid];
4765
+ let boundValue;
4766
+ // starting from here, if the prop is of type 'BoundValue', and has prebinding
4767
+ // we are going to resolve the incomplete path
4768
+ if (link && isPreboundProp(variableMapping) && variableMapping.isPrebound) {
4769
+ const prebindingPath = getPrebindingPathBySourceEntry(variableMapping, (dataSourceKey) => {
4770
+ const link = dataSource[dataSourceKey];
4771
+ return entityStore.getEntityFromLink(link);
4772
+ }) ?? variableMapping.path;
4773
+ // this allows us to resolve it regularly
4774
+ boundValue = transformBoundContentValue(node.data.props, entityStore, link, resolveDesignValue, variableName, variableDefinition.type, prebindingPath);
4775
+ }
4776
+ else {
4777
+ // here we resolve the regular bound value
4778
+ const variableDefinition = definition.variables[variableName];
4779
+ boundValue = transformBoundContentValue(node.data.props, entityStore, link, resolveDesignValue, variableName, variableDefinition.type, variableMapping.path);
4780
+ }
4720
4781
  // In some cases, there may be an asset linked in the path, so we need to consider this scenario:
4721
4782
  // If no 'boundValue' is found, we also attempt to extract the value associated with the second-to-last item in the path.
4722
4783
  // If successful, it means we have identified the linked asset.
4723
- if (!boundValue) {
4784
+ if (!boundValue && maybePath) {
4724
4785
  const maybeBoundAsset = areEntitiesFetched
4725
- ? entityStore.getValue(binding, path.split('/').slice(0, -2))
4786
+ ? entityStore.getValue(link, maybePath.split('/').slice(0, -2))
4726
4787
  : undefined;
4727
4788
  if (isLinkToAsset(maybeBoundAsset)) {
4728
4789
  boundValue = maybeBoundAsset;
@@ -4894,7 +4955,7 @@ const sendCanvasGeometryUpdatedMessage = async (tree, sourceEvent) => {
4894
4955
  const rootRect = document.documentElement.getBoundingClientRect();
4895
4956
  const bodyRect = document.body.getBoundingClientRect();
4896
4957
  const width = Math.max(document.documentElement.offsetWidth, rootRect.width, bodyRect.width);
4897
- const height = Math.max(document.documentElement.offsetHeight, rootRect.height, bodyRect.height);
4958
+ const height = Math.max(document.documentElement.offsetHeight, rootRect.height, bodyRect.height, measureBodyContentHeight());
4898
4959
  sendMessage(OUTGOING_EVENTS.CanvasGeometryUpdated, {
4899
4960
  size: {
4900
4961
  width,
@@ -4942,6 +5003,31 @@ function waitForImageToBeLoaded(imageNode) {
4942
5003
  imageNode.addEventListener('error', handleImageLoad);
4943
5004
  });
4944
5005
  }
5006
+ // calculates the content height by finding the deepest node in the first 2 levels of the body
5007
+ function measureBodyContentHeight(depth = 2, node = document.body) {
5008
+ if (depth <= 0)
5009
+ return 0;
5010
+ let height = 0;
5011
+ for (const element of node.children) {
5012
+ const rect = element.getBoundingClientRect();
5013
+ const style = window.getComputedStyle(element);
5014
+ const isHidden = (rect.width === 0 && rect.height === 0) ||
5015
+ style.display === 'none' ||
5016
+ style.visibility === 'hidden';
5017
+ // ignore relative positioned elements that are anchored to the bottom,
5018
+ // as this can cause infinite height
5019
+ const isBottomAnchored = (style.position === 'fixed' ||
5020
+ style.position === 'absolute' ||
5021
+ style.position === 'relative' ||
5022
+ style.position === 'sticky') &&
5023
+ parseFloat(style.bottom) < 0;
5024
+ if (isHidden || isBottomAnchored) {
5025
+ continue;
5026
+ }
5027
+ height = Math.max(height, Math.ceil(rect.bottom), measureBodyContentHeight(depth - 1, element));
5028
+ }
5029
+ return height;
5030
+ }
4945
5031
 
4946
5032
  const useCanvasGeometryUpdates = ({ tree, canvasMode }) => {
4947
5033
  const debouncedUpdateGeometry = useMemo(() => debounce((tree, sourceEvent) => {