@contentful/experiences-visual-editor-react 3.0.0 → 3.0.1-dev-20250807T0745-c928267.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
@@ -4813,7 +4813,6 @@ const EmptyCanvasMessage = () => {
4813
4813
  */
4814
4814
  const sendCanvasGeometryUpdatedMessage = async (tree, sourceEvent) => {
4815
4815
  const nodeToCoordinatesMap = {};
4816
- await waitForAllImagesToBeLoaded();
4817
4816
  collectNodeCoordinates(tree.root, nodeToCoordinatesMap);
4818
4817
  sendMessage(OUTGOING_EVENTS.CanvasGeometryUpdated, {
4819
4818
  size: {
@@ -4842,30 +4841,26 @@ const collectNodeCoordinates = (node, nodeToCoordinatesMap) => {
4842
4841
  }
4843
4842
  node.children.forEach((child) => collectNodeCoordinates(child, nodeToCoordinatesMap));
4844
4843
  };
4845
- const waitForAllImagesToBeLoaded = () => {
4846
- // If the document contains an image, wait for this image to be loaded before collecting & sending all geometry data.
4847
- const allImageNodes = document.querySelectorAll('img');
4848
- return Promise.all(Array.from(allImageNodes).map((imageNode) => {
4849
- if (imageNode.complete) {
4850
- return Promise.resolve();
4851
- }
4852
- return new Promise((resolve, reject) => {
4853
- const handleImageLoad = (event) => {
4854
- imageNode.removeEventListener('load', handleImageLoad);
4855
- imageNode.removeEventListener('error', handleImageLoad);
4856
- if (event.type === 'error') {
4857
- console.warn('Image failed to load:', imageNode);
4858
- reject();
4859
- }
4860
- else {
4861
- resolve();
4862
- }
4863
- };
4864
- imageNode.addEventListener('load', handleImageLoad);
4865
- imageNode.addEventListener('error', handleImageLoad);
4866
- });
4867
- }));
4868
- };
4844
+ function waitForImageToBeLoaded(imageNode) {
4845
+ if (imageNode.complete) {
4846
+ return Promise.resolve();
4847
+ }
4848
+ return new Promise((resolve, reject) => {
4849
+ const handleImageLoad = (event) => {
4850
+ imageNode.removeEventListener('load', handleImageLoad);
4851
+ imageNode.removeEventListener('error', handleImageLoad);
4852
+ if (event.type === 'error') {
4853
+ console.warn('Image failed to load:', imageNode);
4854
+ reject();
4855
+ }
4856
+ else {
4857
+ resolve();
4858
+ }
4859
+ };
4860
+ imageNode.addEventListener('load', handleImageLoad);
4861
+ imageNode.addEventListener('error', handleImageLoad);
4862
+ });
4863
+ }
4869
4864
 
4870
4865
  const useCanvasGeometryUpdates = ({ tree }) => {
4871
4866
  const debouncedUpdateGeometry = useMemo(() => debounce((tree, sourceEvent) => {
@@ -4880,6 +4875,9 @@ const useCanvasGeometryUpdates = ({ tree }) => {
4880
4875
  // yet show the need for this. So we might be able to drop this later to boost performance.
4881
4876
  trailing: true,
4882
4877
  }), []);
4878
+ const debouncedCollectImages = useMemo(() => debounce(() => {
4879
+ return Array.from(document.querySelectorAll('img'));
4880
+ }, 300, { leading: true, trailing: true }), []);
4883
4881
  // Store tree in a ref to avoid the need to deactivate & reactivate the mutation observer
4884
4882
  // when the tree changes. This is important to avoid missing out on some mutation events.
4885
4883
  const treeRef = useRef(tree);
@@ -4892,9 +4890,19 @@ const useCanvasGeometryUpdates = ({ tree }) => {
4892
4890
  window.addEventListener('resize', resizeEventListener);
4893
4891
  return () => window.removeEventListener('resize', resizeEventListener);
4894
4892
  }, [debouncedUpdateGeometry]);
4893
+ const [{ allImages, loadedImages }, setImages] = useState(() => {
4894
+ const allImages = debouncedCollectImages();
4895
+ const loadedImages = new WeakSet();
4896
+ return { allImages, loadedImages };
4897
+ });
4895
4898
  // Handling DOM mutations
4896
4899
  useEffect(() => {
4897
- const observer = new MutationObserver(() => debouncedUpdateGeometry(treeRef.current, 'mutation'));
4900
+ const observer = new MutationObserver(() => {
4901
+ debouncedUpdateGeometry(treeRef.current, 'mutation');
4902
+ // find all images on any DOM change
4903
+ const allImages = debouncedCollectImages();
4904
+ setImages((prevState) => ({ ...prevState, allImages }));
4905
+ });
4898
4906
  // send initial geometry in case the tree is empty
4899
4907
  debouncedUpdateGeometry(treeRef.current, 'mutation');
4900
4908
  observer.observe(document.documentElement, {
@@ -4903,7 +4911,26 @@ const useCanvasGeometryUpdates = ({ tree }) => {
4903
4911
  attributes: true,
4904
4912
  });
4905
4913
  return () => observer.disconnect();
4906
- }, [debouncedUpdateGeometry]);
4914
+ }, [debouncedCollectImages, debouncedUpdateGeometry]);
4915
+ // Handling image loading separately,
4916
+ // as each image can load at a different time, some might be hidden or lazy loaded
4917
+ useEffect(() => {
4918
+ let isCurrent = true;
4919
+ allImages.forEach(async (imageNode) => {
4920
+ if (loadedImages.has(imageNode)) {
4921
+ return;
4922
+ }
4923
+ // update the geometry after each image is loaded, as it can shift the layout
4924
+ await waitForImageToBeLoaded(imageNode);
4925
+ if (isCurrent) {
4926
+ loadedImages.add(imageNode);
4927
+ debouncedUpdateGeometry(treeRef.current, 'imageLoad');
4928
+ }
4929
+ });
4930
+ return () => {
4931
+ isCurrent = false;
4932
+ };
4933
+ }, [allImages, loadedImages, debouncedUpdateGeometry]);
4907
4934
  };
4908
4935
 
4909
4936
  const RootRenderer = ({ inMemoryEntitiesStore }) => {