@elementor/editor-canvas 3.33.0-221 → 3.33.0-223

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
@@ -137,6 +137,7 @@ var import_react3 = require("react");
137
137
  var import_react4 = require("@floating-ui/react");
138
138
  function useFloatingOnElement({ element, isSelected }) {
139
139
  const [isOpen, setIsOpen] = (0, import_react3.useState)(false);
140
+ const sizeModifier = 2;
140
141
  const { refs, floatingStyles, context } = (0, import_react4.useFloating)({
141
142
  // Must be controlled for interactions (like hover) to work.
142
143
  open: isOpen || isSelected,
@@ -144,13 +145,15 @@ function useFloatingOnElement({ element, isSelected }) {
144
145
  whileElementsMounted: import_react4.autoUpdate,
145
146
  middleware: [
146
147
  // Match the floating element's size to the reference element.
147
- (0, import_react4.size)({
148
- apply({ elements, rects }) {
149
- Object.assign(elements.floating.style, {
150
- width: `${rects.reference.width + 2}px`,
151
- height: `${rects.reference.height + 2}px`
152
- });
153
- }
148
+ (0, import_react4.size)(() => {
149
+ return {
150
+ apply({ elements, rects }) {
151
+ Object.assign(elements.floating.style, {
152
+ width: `${rects.reference.width + sizeModifier}px`,
153
+ height: `${rects.reference.height + sizeModifier}px`
154
+ });
155
+ }
156
+ };
154
157
  }),
155
158
  // Center the floating element on the reference element.
156
159
  (0, import_react4.offset)(({ rects }) => -rects.reference.height / 2 - rects.floating.height / 2)
@@ -170,6 +173,23 @@ function useFloatingOnElement({ element, isSelected }) {
170
173
  };
171
174
  }
172
175
 
176
+ // src/hooks/use-has-overlapping.ts
177
+ var possibleOverlappingSelectors = [".e-off-canvas"];
178
+ var useHasOverlapping = () => {
179
+ const preview = window.elementor?.$preview?.[0];
180
+ if (!preview) {
181
+ return false;
182
+ }
183
+ const hasOverlapping = possibleOverlappingSelectors.map((selector) => Array.from(preview?.contentWindow?.document.body.querySelectorAll(selector) ?? [])).flat().some(
184
+ (elem) => elem.checkVisibility({
185
+ opacityProperty: true,
186
+ visibilityProperty: true,
187
+ contentVisibilityAuto: true
188
+ })
189
+ );
190
+ return hasOverlapping;
191
+ };
192
+
173
193
  // src/components/element-overlay.tsx
174
194
  var CANVAS_WRAPPER_ID = "elementor-preview-responsive-wrapper";
175
195
  var OverlayBox = (0, import_ui.styled)(import_ui.Box, {
@@ -182,9 +202,10 @@ var OverlayBox = (0, import_ui.styled)(import_ui.Box, {
182
202
  function ElementOverlay({ element, isSelected, id }) {
183
203
  const { context, floating, isVisible } = useFloatingOnElement({ element, isSelected });
184
204
  const { getFloatingProps, getReferenceProps } = (0, import_react5.useInteractions)([(0, import_react5.useHover)(context)]);
205
+ const hasOverlapping = useHasOverlapping();
185
206
  useBindReactPropsToElement(element, getReferenceProps);
186
207
  const isSmallerOffset = element.offsetHeight <= 1;
187
- return isVisible && /* @__PURE__ */ React.createElement(import_react5.FloatingPortal, { id: CANVAS_WRAPPER_ID }, /* @__PURE__ */ React.createElement(
208
+ return isVisible && !hasOverlapping && /* @__PURE__ */ React.createElement(import_react5.FloatingPortal, { id: CANVAS_WRAPPER_ID }, /* @__PURE__ */ React.createElement(
188
209
  OverlayBox,
189
210
  {
190
211
  ref: floating.setRef,
package/dist/index.mjs CHANGED
@@ -97,6 +97,7 @@ import { useEffect as useEffect3, useState } from "react";
97
97
  import { autoUpdate, offset, size, useFloating } from "@floating-ui/react";
98
98
  function useFloatingOnElement({ element, isSelected }) {
99
99
  const [isOpen, setIsOpen] = useState(false);
100
+ const sizeModifier = 2;
100
101
  const { refs, floatingStyles, context } = useFloating({
101
102
  // Must be controlled for interactions (like hover) to work.
102
103
  open: isOpen || isSelected,
@@ -104,13 +105,15 @@ function useFloatingOnElement({ element, isSelected }) {
104
105
  whileElementsMounted: autoUpdate,
105
106
  middleware: [
106
107
  // Match the floating element's size to the reference element.
107
- size({
108
- apply({ elements, rects }) {
109
- Object.assign(elements.floating.style, {
110
- width: `${rects.reference.width + 2}px`,
111
- height: `${rects.reference.height + 2}px`
112
- });
113
- }
108
+ size(() => {
109
+ return {
110
+ apply({ elements, rects }) {
111
+ Object.assign(elements.floating.style, {
112
+ width: `${rects.reference.width + sizeModifier}px`,
113
+ height: `${rects.reference.height + sizeModifier}px`
114
+ });
115
+ }
116
+ };
114
117
  }),
115
118
  // Center the floating element on the reference element.
116
119
  offset(({ rects }) => -rects.reference.height / 2 - rects.floating.height / 2)
@@ -130,6 +133,23 @@ function useFloatingOnElement({ element, isSelected }) {
130
133
  };
131
134
  }
132
135
 
136
+ // src/hooks/use-has-overlapping.ts
137
+ var possibleOverlappingSelectors = [".e-off-canvas"];
138
+ var useHasOverlapping = () => {
139
+ const preview = window.elementor?.$preview?.[0];
140
+ if (!preview) {
141
+ return false;
142
+ }
143
+ const hasOverlapping = possibleOverlappingSelectors.map((selector) => Array.from(preview?.contentWindow?.document.body.querySelectorAll(selector) ?? [])).flat().some(
144
+ (elem) => elem.checkVisibility({
145
+ opacityProperty: true,
146
+ visibilityProperty: true,
147
+ contentVisibilityAuto: true
148
+ })
149
+ );
150
+ return hasOverlapping;
151
+ };
152
+
133
153
  // src/components/element-overlay.tsx
134
154
  var CANVAS_WRAPPER_ID = "elementor-preview-responsive-wrapper";
135
155
  var OverlayBox = styled(Box, {
@@ -142,9 +162,10 @@ var OverlayBox = styled(Box, {
142
162
  function ElementOverlay({ element, isSelected, id }) {
143
163
  const { context, floating, isVisible } = useFloatingOnElement({ element, isSelected });
144
164
  const { getFloatingProps, getReferenceProps } = useInteractions([useHover(context)]);
165
+ const hasOverlapping = useHasOverlapping();
145
166
  useBindReactPropsToElement(element, getReferenceProps);
146
167
  const isSmallerOffset = element.offsetHeight <= 1;
147
- return isVisible && /* @__PURE__ */ React.createElement(FloatingPortal, { id: CANVAS_WRAPPER_ID }, /* @__PURE__ */ React.createElement(
168
+ return isVisible && !hasOverlapping && /* @__PURE__ */ React.createElement(FloatingPortal, { id: CANVAS_WRAPPER_ID }, /* @__PURE__ */ React.createElement(
148
169
  OverlayBox,
149
170
  {
150
171
  ref: floating.setRef,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@elementor/editor-canvas",
3
3
  "description": "Elementor Editor Canvas",
4
- "version": "3.33.0-221",
4
+ "version": "3.33.0-223",
5
5
  "private": false,
6
6
  "author": "Elementor Team",
7
7
  "homepage": "https://elementor.com/",
@@ -37,19 +37,19 @@
37
37
  "react-dom": "^18.3.1"
38
38
  },
39
39
  "dependencies": {
40
- "@elementor/editor": "3.33.0-221",
41
- "@elementor/editor-notifications": "3.33.0-221",
42
- "@elementor/editor-documents": "3.33.0-221",
43
- "@elementor/editor-elements": "3.33.0-221",
44
- "@elementor/editor-props": "3.33.0-221",
45
- "@elementor/editor-responsive": "3.33.0-221",
46
- "@elementor/editor-styles": "3.33.0-221",
47
- "@elementor/editor-styles-repository": "3.33.0-221",
48
- "@elementor/editor-v1-adapters": "3.33.0-221",
49
- "@elementor/twing": "3.33.0-221",
40
+ "@elementor/editor": "3.33.0-223",
41
+ "@elementor/editor-notifications": "3.33.0-223",
42
+ "@elementor/editor-documents": "3.33.0-223",
43
+ "@elementor/editor-elements": "3.33.0-223",
44
+ "@elementor/editor-props": "3.33.0-223",
45
+ "@elementor/editor-responsive": "3.33.0-223",
46
+ "@elementor/editor-styles": "3.33.0-223",
47
+ "@elementor/editor-styles-repository": "3.33.0-223",
48
+ "@elementor/editor-v1-adapters": "3.33.0-223",
49
+ "@elementor/twing": "3.33.0-223",
50
50
  "@elementor/ui": "1.36.12",
51
- "@elementor/utils": "3.33.0-221",
52
- "@elementor/wp-media": "3.33.0-221",
51
+ "@elementor/utils": "3.33.0-223",
52
+ "@elementor/wp-media": "3.33.0-223",
53
53
  "@floating-ui/react": "^0.27.5",
54
54
  "@wordpress/i18n": "^5.13.0"
55
55
  },
@@ -4,6 +4,7 @@ import { FloatingPortal, useHover, useInteractions } from '@floating-ui/react';
4
4
 
5
5
  import { useBindReactPropsToElement } from '../hooks/use-bind-react-props-to-element';
6
6
  import { useFloatingOnElement } from '../hooks/use-floating-on-element';
7
+ import { useHasOverlapping } from '../hooks/use-has-overlapping';
7
8
 
8
9
  export const CANVAS_WRAPPER_ID = 'elementor-preview-responsive-wrapper';
9
10
 
@@ -25,12 +26,14 @@ const OverlayBox = styled( Box, {
25
26
  export function ElementOverlay( { element, isSelected, id }: Props ) {
26
27
  const { context, floating, isVisible } = useFloatingOnElement( { element, isSelected } );
27
28
  const { getFloatingProps, getReferenceProps } = useInteractions( [ useHover( context ) ] );
29
+ const hasOverlapping = useHasOverlapping();
28
30
 
29
31
  useBindReactPropsToElement( element, getReferenceProps );
30
32
  const isSmallerOffset = element.offsetHeight <= 1;
31
33
 
32
34
  return (
33
- isVisible && (
35
+ isVisible &&
36
+ ! hasOverlapping && (
34
37
  <FloatingPortal id={ CANVAS_WRAPPER_ID }>
35
38
  <OverlayBox
36
39
  ref={ floating.setRef }
@@ -0,0 +1,187 @@
1
+ import { createDOMElement } from 'test-utils';
2
+ import { renderHook } from '@testing-library/react';
3
+
4
+ import { type CanvasExtendedWindow } from '../../sync/types';
5
+ import { useHasOverlapping } from '../use-has-overlapping';
6
+
7
+ const OFF_CANVAS_CLASS = 'e-off-canvas';
8
+
9
+ describe( 'useHasOverlapping', () => {
10
+ let mockPreviewFrame: HTMLIFrameElement;
11
+ let mockDocument: Document;
12
+
13
+ const setupPreviewFrame = ( hasOffCanvas: boolean, isVisible: boolean = true ) => {
14
+ // Arrange - Create mock iframe and document
15
+ mockPreviewFrame = createDOMElement( { tag: 'iframe' } ) as HTMLIFrameElement;
16
+ mockDocument = document.implementation.createHTMLDocument( 'Preview' );
17
+
18
+ if ( hasOffCanvas ) {
19
+ const offCanvasElement = createDOMElement( {
20
+ tag: 'div',
21
+ attrs: { class: OFF_CANVAS_CLASS },
22
+ } );
23
+
24
+ // Mock checkVisibility method
25
+ offCanvasElement.checkVisibility = jest.fn().mockReturnValue( isVisible );
26
+
27
+ mockDocument.body.appendChild( offCanvasElement );
28
+ }
29
+
30
+ // Setup the content window with the mock document
31
+ Object.defineProperty( mockPreviewFrame, 'contentWindow', {
32
+ value: {
33
+ document: mockDocument,
34
+ },
35
+ writable: true,
36
+ } );
37
+
38
+ // Setup window.elementor.$preview
39
+ ( window as unknown as CanvasExtendedWindow ).elementor = {
40
+ $preview: [ mockPreviewFrame ],
41
+ };
42
+ };
43
+
44
+ const cleanupPreviewFrame = () => {
45
+ delete ( window as unknown as CanvasExtendedWindow ).elementor;
46
+ };
47
+
48
+ afterEach( () => {
49
+ cleanupPreviewFrame();
50
+ } );
51
+
52
+ it( 'should return false when preview frame is not available', () => {
53
+ // Arrange - No preview frame setup
54
+
55
+ // Act
56
+ const { result } = renderHook( () => useHasOverlapping() );
57
+
58
+ // Assert
59
+ expect( result.current ).toBe( false );
60
+ } );
61
+
62
+ it( 'should return false when off-canvas element does not exist', () => {
63
+ // Arrange
64
+ setupPreviewFrame( false );
65
+
66
+ // Act
67
+ const { result } = renderHook( () => useHasOverlapping() );
68
+
69
+ // Assert
70
+ expect( result.current ).toBe( false );
71
+ } );
72
+
73
+ it( 'should return true when off-canvas element exists and is visible', () => {
74
+ // Arrange
75
+ setupPreviewFrame( true, true );
76
+
77
+ // Act
78
+ const { result } = renderHook( () => useHasOverlapping() );
79
+
80
+ // Assert
81
+ expect( result.current ).toBe( true );
82
+ } );
83
+
84
+ it( 'should return false when off-canvas element exists but is not visible', () => {
85
+ // Arrange
86
+ setupPreviewFrame( true, false );
87
+
88
+ // Act
89
+ const { result } = renderHook( () => useHasOverlapping() );
90
+
91
+ // Assert
92
+ expect( result.current ).toBe( false );
93
+ } );
94
+
95
+ it( 'should check visibility with correct options', () => {
96
+ // Arrange
97
+ setupPreviewFrame( true, true );
98
+
99
+ // Act
100
+ renderHook( () => useHasOverlapping() );
101
+
102
+ // Assert
103
+ // eslint-disable-next-line testing-library/no-node-access
104
+ const offCanvasElement = mockDocument.querySelector( `.${ OFF_CANVAS_CLASS }` );
105
+ expect( offCanvasElement?.checkVisibility ).toHaveBeenCalledWith( {
106
+ opacityProperty: true,
107
+ visibilityProperty: true,
108
+ contentVisibilityAuto: true,
109
+ } );
110
+ } );
111
+
112
+ it( 'should return true when multiple off-canvas elements exist and at least one is visible', () => {
113
+ // Arrange
114
+ mockPreviewFrame = createDOMElement( { tag: 'iframe' } ) as HTMLIFrameElement;
115
+ mockDocument = document.implementation.createHTMLDocument( 'Preview' );
116
+
117
+ const offCanvasElement1 = createDOMElement( {
118
+ tag: 'div',
119
+ attrs: { class: OFF_CANVAS_CLASS },
120
+ } );
121
+ offCanvasElement1.checkVisibility = jest.fn().mockReturnValue( false );
122
+
123
+ const offCanvasElement2 = createDOMElement( {
124
+ tag: 'div',
125
+ attrs: { class: OFF_CANVAS_CLASS },
126
+ } );
127
+ offCanvasElement2.checkVisibility = jest.fn().mockReturnValue( true );
128
+
129
+ mockDocument.body.appendChild( offCanvasElement1 );
130
+ mockDocument.body.appendChild( offCanvasElement2 );
131
+
132
+ Object.defineProperty( mockPreviewFrame, 'contentWindow', {
133
+ value: {
134
+ document: mockDocument,
135
+ },
136
+ writable: true,
137
+ } );
138
+
139
+ ( window as unknown as CanvasExtendedWindow ).elementor = {
140
+ $preview: [ mockPreviewFrame ],
141
+ };
142
+
143
+ // Act
144
+ const { result } = renderHook( () => useHasOverlapping() );
145
+
146
+ // Assert
147
+ expect( result.current ).toBe( true );
148
+ } );
149
+
150
+ it( 'should return false when multiple off-canvas elements exist but none are visible', () => {
151
+ // Arrange
152
+ mockPreviewFrame = createDOMElement( { tag: 'iframe' } ) as HTMLIFrameElement;
153
+ mockDocument = document.implementation.createHTMLDocument( 'Preview' );
154
+
155
+ const offCanvasElement1 = createDOMElement( {
156
+ tag: 'div',
157
+ attrs: { class: OFF_CANVAS_CLASS },
158
+ } );
159
+ offCanvasElement1.checkVisibility = jest.fn().mockReturnValue( false );
160
+
161
+ const offCanvasElement2 = createDOMElement( {
162
+ tag: 'div',
163
+ attrs: { class: OFF_CANVAS_CLASS },
164
+ } );
165
+ offCanvasElement2.checkVisibility = jest.fn().mockReturnValue( false );
166
+
167
+ mockDocument.body.appendChild( offCanvasElement1 );
168
+ mockDocument.body.appendChild( offCanvasElement2 );
169
+
170
+ Object.defineProperty( mockPreviewFrame, 'contentWindow', {
171
+ value: {
172
+ document: mockDocument,
173
+ },
174
+ writable: true,
175
+ } );
176
+
177
+ ( window as unknown as CanvasExtendedWindow ).elementor = {
178
+ $preview: [ mockPreviewFrame ],
179
+ };
180
+
181
+ // Act
182
+ const { result } = renderHook( () => useHasOverlapping() );
183
+
184
+ // Assert
185
+ expect( result.current ).toBe( false );
186
+ } );
187
+ } );
@@ -8,6 +8,7 @@ type Options = {
8
8
 
9
9
  export function useFloatingOnElement( { element, isSelected }: Options ) {
10
10
  const [ isOpen, setIsOpen ] = useState( false );
11
+ const sizeModifier = 2;
11
12
 
12
13
  const { refs, floatingStyles, context } = useFloating( {
13
14
  // Must be controlled for interactions (like hover) to work.
@@ -18,15 +19,17 @@ export function useFloatingOnElement( { element, isSelected }: Options ) {
18
19
 
19
20
  middleware: [
20
21
  // Match the floating element's size to the reference element.
21
- size( {
22
- apply( { elements, rects } ) {
23
- Object.assign( elements.floating.style, {
24
- width: `${ rects.reference.width + 2 }px`,
25
- height: `${ rects.reference.height + 2 }px`,
26
- } );
27
- },
28
- } ),
29
22
 
23
+ size( () => {
24
+ return {
25
+ apply( { elements, rects } ) {
26
+ Object.assign( elements.floating.style, {
27
+ width: `${ rects.reference.width + sizeModifier }px`,
28
+ height: `${ rects.reference.height + sizeModifier }px`,
29
+ } );
30
+ },
31
+ };
32
+ } ),
30
33
  // Center the floating element on the reference element.
31
34
  offset( ( { rects } ) => -rects.reference.height / 2 - rects.floating.height / 2 ),
32
35
  ],
@@ -0,0 +1,21 @@
1
+ import { type CanvasExtendedWindow } from '../sync/types';
2
+
3
+ const possibleOverlappingSelectors = [ '.e-off-canvas' ]; // can add more selectors here if needed, make sure to loop through them to check classList
4
+
5
+ export const useHasOverlapping = () => {
6
+ const preview = ( window as unknown as CanvasExtendedWindow ).elementor?.$preview?.[ 0 ];
7
+ if ( ! preview ) {
8
+ return false;
9
+ }
10
+ const hasOverlapping = possibleOverlappingSelectors
11
+ .map( ( selector ) => Array.from( preview?.contentWindow?.document.body.querySelectorAll( selector ) ?? [] ) )
12
+ .flat()
13
+ .some( ( elem ) =>
14
+ elem.checkVisibility( {
15
+ opacityProperty: true,
16
+ visibilityProperty: true,
17
+ contentVisibilityAuto: true,
18
+ } )
19
+ );
20
+ return hasOverlapping;
21
+ };