@contentful/experiences-visual-editor-react 3.0.0 → 3.1.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
@@ -2869,6 +2869,7 @@ const OUTGOING_EVENTS = {
2869
2869
  SDKFeatures: 'sdkFeatures',
2870
2870
  RequestEntities: 'REQUEST_ENTITIES',
2871
2871
  CanvasGeometryUpdated: 'canvasGeometryUpdated',
2872
+ CanvasPan: 'canvasPan',
2872
2873
  };
2873
2874
  const INCOMING_EVENTS = {
2874
2875
  RequestEditorMode: 'requestEditorMode',
@@ -4813,7 +4814,6 @@ const EmptyCanvasMessage = () => {
4813
4814
  */
4814
4815
  const sendCanvasGeometryUpdatedMessage = async (tree, sourceEvent) => {
4815
4816
  const nodeToCoordinatesMap = {};
4816
- await waitForAllImagesToBeLoaded();
4817
4817
  collectNodeCoordinates(tree.root, nodeToCoordinatesMap);
4818
4818
  sendMessage(OUTGOING_EVENTS.CanvasGeometryUpdated, {
4819
4819
  size: {
@@ -4842,30 +4842,26 @@ const collectNodeCoordinates = (node, nodeToCoordinatesMap) => {
4842
4842
  }
4843
4843
  node.children.forEach((child) => collectNodeCoordinates(child, nodeToCoordinatesMap));
4844
4844
  };
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
- };
4845
+ function waitForImageToBeLoaded(imageNode) {
4846
+ if (imageNode.complete) {
4847
+ return Promise.resolve();
4848
+ }
4849
+ return new Promise((resolve, reject) => {
4850
+ const handleImageLoad = (event) => {
4851
+ imageNode.removeEventListener('load', handleImageLoad);
4852
+ imageNode.removeEventListener('error', handleImageLoad);
4853
+ if (event.type === 'error') {
4854
+ console.warn('Image failed to load:', imageNode);
4855
+ reject();
4856
+ }
4857
+ else {
4858
+ resolve();
4859
+ }
4860
+ };
4861
+ imageNode.addEventListener('load', handleImageLoad);
4862
+ imageNode.addEventListener('error', handleImageLoad);
4863
+ });
4864
+ }
4869
4865
 
4870
4866
  const useCanvasGeometryUpdates = ({ tree }) => {
4871
4867
  const debouncedUpdateGeometry = useMemo(() => debounce((tree, sourceEvent) => {
@@ -4880,6 +4876,9 @@ const useCanvasGeometryUpdates = ({ tree }) => {
4880
4876
  // yet show the need for this. So we might be able to drop this later to boost performance.
4881
4877
  trailing: true,
4882
4878
  }), []);
4879
+ const debouncedCollectImages = useMemo(() => debounce(() => {
4880
+ return Array.from(document.querySelectorAll('img'));
4881
+ }, 300, { leading: true, trailing: true }), []);
4883
4882
  // Store tree in a ref to avoid the need to deactivate & reactivate the mutation observer
4884
4883
  // when the tree changes. This is important to avoid missing out on some mutation events.
4885
4884
  const treeRef = useRef(tree);
@@ -4892,9 +4891,19 @@ const useCanvasGeometryUpdates = ({ tree }) => {
4892
4891
  window.addEventListener('resize', resizeEventListener);
4893
4892
  return () => window.removeEventListener('resize', resizeEventListener);
4894
4893
  }, [debouncedUpdateGeometry]);
4894
+ const [{ allImages, loadedImages }, setImages] = useState(() => {
4895
+ const allImages = debouncedCollectImages();
4896
+ const loadedImages = new WeakSet();
4897
+ return { allImages, loadedImages };
4898
+ });
4895
4899
  // Handling DOM mutations
4896
4900
  useEffect(() => {
4897
- const observer = new MutationObserver(() => debouncedUpdateGeometry(treeRef.current, 'mutation'));
4901
+ const observer = new MutationObserver(() => {
4902
+ debouncedUpdateGeometry(treeRef.current, 'mutation');
4903
+ // find all images on any DOM change
4904
+ const allImages = debouncedCollectImages();
4905
+ setImages((prevState) => ({ ...prevState, allImages }));
4906
+ });
4898
4907
  // send initial geometry in case the tree is empty
4899
4908
  debouncedUpdateGeometry(treeRef.current, 'mutation');
4900
4909
  observer.observe(document.documentElement, {
@@ -4903,7 +4912,42 @@ const useCanvasGeometryUpdates = ({ tree }) => {
4903
4912
  attributes: true,
4904
4913
  });
4905
4914
  return () => observer.disconnect();
4906
- }, [debouncedUpdateGeometry]);
4915
+ }, [debouncedCollectImages, debouncedUpdateGeometry]);
4916
+ // Handling image loading separately,
4917
+ // as each image can load at a different time, some might be hidden or lazy loaded
4918
+ useEffect(() => {
4919
+ let isCurrent = true;
4920
+ allImages.forEach(async (imageNode) => {
4921
+ if (loadedImages.has(imageNode)) {
4922
+ return;
4923
+ }
4924
+ // update the geometry after each image is loaded, as it can shift the layout
4925
+ await waitForImageToBeLoaded(imageNode);
4926
+ if (isCurrent) {
4927
+ loadedImages.add(imageNode);
4928
+ debouncedUpdateGeometry(treeRef.current, 'imageLoad');
4929
+ }
4930
+ });
4931
+ return () => {
4932
+ isCurrent = false;
4933
+ };
4934
+ }, [allImages, loadedImages, debouncedUpdateGeometry]);
4935
+ // Delegate scrolling to the canvas
4936
+ useEffect(() => {
4937
+ const onWheel = (e) => {
4938
+ e.preventDefault();
4939
+ sendMessage(OUTGOING_EVENTS.CanvasPan, {
4940
+ ctrlKey: e.ctrlKey,
4941
+ metaKey: e.metaKey,
4942
+ clientX: e.clientX,
4943
+ clientY: e.clientY,
4944
+ deltaX: e.deltaX,
4945
+ deltaY: e.deltaY,
4946
+ });
4947
+ };
4948
+ document.addEventListener('wheel', onWheel, { passive: false });
4949
+ return () => document.removeEventListener('wheel', onWheel);
4950
+ }, []);
4907
4951
  };
4908
4952
 
4909
4953
  const RootRenderer = ({ inMemoryEntitiesStore }) => {