@elementor/editor-canvas 0.17.0 → 0.18.1
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/.turbo/turbo-build.log +10 -10
- package/CHANGELOG.md +29 -0
- package/dist/index.d.mts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +87 -38
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +69 -21
- package/dist/index.mjs.map +1 -1
- package/package.json +7 -7
- package/src/components/__tests__/__snapshots__/style-renderer.test.tsx.snap +30 -0
- package/src/components/__tests__/style-renderer.test.tsx +16 -14
- package/src/components/style-renderer.tsx +12 -9
- package/src/hooks/__tests__/__snapshots__/use-documents-css-links.test.tsx.snap +30 -0
- package/src/hooks/__tests__/use-documents-css-links.test.tsx +84 -0
- package/src/hooks/__tests__/use-style-items.test.ts +29 -32
- package/src/hooks/use-documents-css-links.ts +58 -0
- package/src/hooks/use-floating-on-element.ts +2 -2
- package/src/hooks/use-style-items.ts +2 -2
- package/src/index.ts +1 -3
- package/src/style-commands/__tests__/paste-style.test.ts +5 -5
- package/src/style-commands/__tests__/reset-style.test.ts +2 -2
- package/src/style-commands/undoable-actions/paste-element-style.ts +3 -3
- package/src/style-commands/undoable-actions/reset-element-style.ts +2 -2
- package/src/sync/{get-canvas-iframe-head.ts → get-canvas-iframe-document.ts} +2 -2
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
+
|
|
3
|
+
exports[`useDocumentsCssLinks should return an array of links attrs and remove them from the dom 1`] = `
|
|
4
|
+
<head>
|
|
5
|
+
<link
|
|
6
|
+
href="10.css"
|
|
7
|
+
id="elementor-post-10-css"
|
|
8
|
+
rel="stylesheet"
|
|
9
|
+
/>
|
|
10
|
+
<link
|
|
11
|
+
href="not-elementor.css"
|
|
12
|
+
id="not-elementor-css"
|
|
13
|
+
rel="stylesheet"
|
|
14
|
+
/>
|
|
15
|
+
<link
|
|
16
|
+
data-e-removed="true"
|
|
17
|
+
data-from-hook="true"
|
|
18
|
+
href="2.css"
|
|
19
|
+
id="elementor-post-2-css"
|
|
20
|
+
rel="stylesheet"
|
|
21
|
+
/>
|
|
22
|
+
<link
|
|
23
|
+
data-e-removed="true"
|
|
24
|
+
data-from-hook="true"
|
|
25
|
+
href="5.css"
|
|
26
|
+
id="elementor-post-5-css"
|
|
27
|
+
rel="stylesheet"
|
|
28
|
+
/>
|
|
29
|
+
</head>
|
|
30
|
+
`;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { createPortal } from 'react-dom';
|
|
3
|
+
import { createDOMElement } from 'test-utils';
|
|
4
|
+
import { render, renderHook } from '@testing-library/react';
|
|
5
|
+
|
|
6
|
+
import { getCanvasIframeDocument } from '../../sync/get-canvas-iframe-document';
|
|
7
|
+
import { useDocumentsCssLinks } from '../use-documents-css-links';
|
|
8
|
+
|
|
9
|
+
jest.mock( '../../sync/get-canvas-iframe-document', () => ( {
|
|
10
|
+
getCanvasIframeDocument: jest.fn(),
|
|
11
|
+
} ) );
|
|
12
|
+
|
|
13
|
+
describe( 'useDocumentsCssLinks', () => {
|
|
14
|
+
const head = createDOMElement( {
|
|
15
|
+
tag: 'head',
|
|
16
|
+
children: [
|
|
17
|
+
createDOMElement( {
|
|
18
|
+
tag: 'link',
|
|
19
|
+
attrs: { href: '2.css', rel: 'stylesheet', id: 'elementor-post-2-css' },
|
|
20
|
+
} ),
|
|
21
|
+
createDOMElement( {
|
|
22
|
+
tag: 'link',
|
|
23
|
+
attrs: { href: '10.css', rel: 'stylesheet', id: 'elementor-post-10-css' },
|
|
24
|
+
} ),
|
|
25
|
+
createDOMElement( {
|
|
26
|
+
tag: 'link',
|
|
27
|
+
attrs: { href: '5.css', rel: 'stylesheet', id: 'elementor-post-5-css' },
|
|
28
|
+
} ),
|
|
29
|
+
createDOMElement( {
|
|
30
|
+
tag: 'link',
|
|
31
|
+
attrs: { href: 'not-elementor.css', rel: 'stylesheet', id: 'not-elementor-css' },
|
|
32
|
+
} ),
|
|
33
|
+
],
|
|
34
|
+
} );
|
|
35
|
+
|
|
36
|
+
const body = createDOMElement( {
|
|
37
|
+
tag: 'body',
|
|
38
|
+
children: [
|
|
39
|
+
createDOMElement( { tag: 'div', attrs: { 'data-elementor-id': '2' } } ),
|
|
40
|
+
createDOMElement( { tag: 'div', attrs: { 'data-elementor-id': '4' } } ),
|
|
41
|
+
createDOMElement( {
|
|
42
|
+
tag: 'div',
|
|
43
|
+
children: [ createDOMElement( { tag: 'div', attrs: { 'data-elementor-id': '5' } } ) ],
|
|
44
|
+
} ),
|
|
45
|
+
],
|
|
46
|
+
} );
|
|
47
|
+
|
|
48
|
+
const Component = () => {
|
|
49
|
+
const links = useDocumentsCssLinks();
|
|
50
|
+
|
|
51
|
+
return createPortal(
|
|
52
|
+
<>
|
|
53
|
+
{ links.map( ( link ) => (
|
|
54
|
+
<link key={ link.id } { ...link } data-from-hook />
|
|
55
|
+
) ) }
|
|
56
|
+
</>,
|
|
57
|
+
head
|
|
58
|
+
);
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
it( 'should return an array of links attrs and remove them from the dom', () => {
|
|
62
|
+
// Arrange.
|
|
63
|
+
const document = { body, head };
|
|
64
|
+
|
|
65
|
+
jest.mocked( getCanvasIframeDocument ).mockReturnValue( document as never );
|
|
66
|
+
|
|
67
|
+
// Act.
|
|
68
|
+
render( <Component /> );
|
|
69
|
+
|
|
70
|
+
// Assert.
|
|
71
|
+
expect( head ).toMatchSnapshot();
|
|
72
|
+
} );
|
|
73
|
+
|
|
74
|
+
it( 'should return empty array when iframe document is not available', () => {
|
|
75
|
+
// Arrange.
|
|
76
|
+
jest.mocked( getCanvasIframeDocument ).mockReturnValue( null );
|
|
77
|
+
|
|
78
|
+
// Act.
|
|
79
|
+
const { result } = renderHook( () => useDocumentsCssLinks(), { initialProps: {} } );
|
|
80
|
+
|
|
81
|
+
// Assert.
|
|
82
|
+
expect( result.current ).toEqual( [] );
|
|
83
|
+
} );
|
|
84
|
+
} );
|
|
@@ -7,6 +7,7 @@ import { useStyleItems } from '../use-style-items';
|
|
|
7
7
|
import { useStyleRenderer } from '../use-style-renderer';
|
|
8
8
|
|
|
9
9
|
jest.mock( '@elementor/editor-styles-repository', () => ( {
|
|
10
|
+
...jest.requireActual( '@elementor/editor-styles-repository' ),
|
|
10
11
|
stylesRepository: {
|
|
11
12
|
getProviders: jest.fn(),
|
|
12
13
|
},
|
|
@@ -36,23 +37,21 @@ describe( 'useStyleItems', () => {
|
|
|
36
37
|
|
|
37
38
|
it( 'should return style items from providers when subscribed', async () => {
|
|
38
39
|
// Arrange.
|
|
39
|
-
const mockProvider1 = createMockStylesProvider(
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
} );
|
|
40
|
+
const mockProvider1 = createMockStylesProvider(
|
|
41
|
+
{
|
|
42
|
+
key: 'provider1',
|
|
43
|
+
priority: 2,
|
|
44
|
+
},
|
|
45
|
+
[ createMockStyleDefinition( { id: 'style1' } ), createMockStyleDefinition( { id: 'style2' } ) ]
|
|
46
|
+
);
|
|
47
47
|
|
|
48
|
-
const mockProvider2 = createMockStylesProvider(
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
} );
|
|
48
|
+
const mockProvider2 = createMockStylesProvider(
|
|
49
|
+
{
|
|
50
|
+
key: 'provider2',
|
|
51
|
+
priority: 1,
|
|
52
|
+
},
|
|
53
|
+
[ createMockStyleDefinition( { id: 'style3' } ), createMockStyleDefinition( { id: 'style4' } ) ]
|
|
54
|
+
);
|
|
56
55
|
|
|
57
56
|
jest.mocked( stylesRepository ).getProviders.mockReturnValue( [ mockProvider1, mockProvider2 ] );
|
|
58
57
|
|
|
@@ -89,23 +88,21 @@ describe( 'useStyleItems', () => {
|
|
|
89
88
|
|
|
90
89
|
it( 'should return style items when attach-preview command is triggered', async () => {
|
|
91
90
|
// Arrange.
|
|
92
|
-
const mockProvider1 = createMockStylesProvider(
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
} );
|
|
91
|
+
const mockProvider1 = createMockStylesProvider(
|
|
92
|
+
{
|
|
93
|
+
key: 'provider1',
|
|
94
|
+
priority: 2,
|
|
95
|
+
},
|
|
96
|
+
[ createMockStyleDefinition( { id: 'style1' } ), createMockStyleDefinition( { id: 'style2' } ) ]
|
|
97
|
+
);
|
|
100
98
|
|
|
101
|
-
const mockProvider2 = createMockStylesProvider(
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
} );
|
|
99
|
+
const mockProvider2 = createMockStylesProvider(
|
|
100
|
+
{
|
|
101
|
+
key: 'provider2',
|
|
102
|
+
priority: 1,
|
|
103
|
+
},
|
|
104
|
+
[ createMockStyleDefinition( { id: 'style3' } ), createMockStyleDefinition( { id: 'style4' } ) ]
|
|
105
|
+
);
|
|
109
106
|
|
|
110
107
|
jest.mocked( stylesRepository ).getProviders.mockReturnValue( [ mockProvider1, mockProvider2 ] );
|
|
111
108
|
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { __privateUseListenTo as useListenTo, commandEndEvent } from '@elementor/editor-v1-adapters';
|
|
2
|
+
|
|
3
|
+
import { getCanvasIframeDocument } from '../sync/get-canvas-iframe-document';
|
|
4
|
+
|
|
5
|
+
const REMOVED_ATTR = 'data-e-removed';
|
|
6
|
+
const DOCUMENT_WRAPPER_ATTR = 'data-elementor-id';
|
|
7
|
+
const CSS_LINK_ID_PREFIX = 'elementor-post-';
|
|
8
|
+
const CSS_LINK_ID_SUFFIX = '-css';
|
|
9
|
+
|
|
10
|
+
export function useDocumentsCssLinks() {
|
|
11
|
+
return useListenTo( commandEndEvent( 'editor/documents/attach-preview' ), () => {
|
|
12
|
+
const iframeDocument = getCanvasIframeDocument();
|
|
13
|
+
|
|
14
|
+
if ( ! iframeDocument ) {
|
|
15
|
+
return [];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const relevantLinkIds = getDocumentsIdsInCanvas( iframeDocument ).map(
|
|
19
|
+
( id ) => `${ CSS_LINK_ID_PREFIX }${ id }${ CSS_LINK_ID_SUFFIX }`
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
const links = getDocumentsCssLinks( iframeDocument ).filter( ( link ) =>
|
|
23
|
+
relevantLinkIds.includes( link.getAttribute( 'id' ) ?? '' )
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
links.forEach( ( link ) => {
|
|
27
|
+
if ( ! link.hasAttribute( REMOVED_ATTR ) ) {
|
|
28
|
+
link.remove();
|
|
29
|
+
}
|
|
30
|
+
} );
|
|
31
|
+
|
|
32
|
+
return links.map( ( link ) => ( {
|
|
33
|
+
...getLinkAttrs( link ),
|
|
34
|
+
id: link.getAttribute( 'id' ) ?? '',
|
|
35
|
+
[ REMOVED_ATTR ]: true,
|
|
36
|
+
} ) );
|
|
37
|
+
} );
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function getDocumentsIdsInCanvas( document: Document ) {
|
|
41
|
+
return [ ...( document.body.querySelectorAll< HTMLElement >( `[${ DOCUMENT_WRAPPER_ATTR }]` ) ?? [] ) ].map(
|
|
42
|
+
( el ) => el.getAttribute( DOCUMENT_WRAPPER_ATTR ) || ''
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function getDocumentsCssLinks( document: Document ) {
|
|
47
|
+
return [
|
|
48
|
+
...( document.head.querySelectorAll< HTMLLinkElement >(
|
|
49
|
+
`link[rel="stylesheet"][id^=${ CSS_LINK_ID_PREFIX }][id$=${ CSS_LINK_ID_SUFFIX }]`
|
|
50
|
+
) ?? [] ),
|
|
51
|
+
];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function getLinkAttrs( el: HTMLLinkElement ) {
|
|
55
|
+
const entries = [ ...el.attributes ].map( ( attr ) => [ attr.name, attr.value ] as const );
|
|
56
|
+
|
|
57
|
+
return Object.fromEntries( entries );
|
|
58
|
+
}
|
|
@@ -21,8 +21,8 @@ export function useFloatingOnElement( { element, isSelected }: Options ) {
|
|
|
21
21
|
size( {
|
|
22
22
|
apply( { elements, rects } ) {
|
|
23
23
|
Object.assign( elements.floating.style, {
|
|
24
|
-
width: `${ rects.reference.width }px`,
|
|
25
|
-
height: `${ rects.reference.height }px`,
|
|
24
|
+
width: `${ rects.reference.width + 2 }px`,
|
|
25
|
+
height: `${ rects.reference.height + 2 }px`,
|
|
26
26
|
} );
|
|
27
27
|
},
|
|
28
28
|
} ),
|
|
@@ -66,11 +66,11 @@ type CreateProviderSubscriberArgs = {
|
|
|
66
66
|
function createProviderSubscriber( { provider, renderStyles, setStyleItems }: CreateProviderSubscriberArgs ) {
|
|
67
67
|
return abortPreviousRuns( ( abortController ) =>
|
|
68
68
|
signalizedProcess( abortController.signal )
|
|
69
|
-
.then( ( _, signal ) => renderStyles( { styles: provider.actions.
|
|
69
|
+
.then( ( _, signal ) => renderStyles( { styles: provider.actions.all(), signal } ) )
|
|
70
70
|
.then( ( items ) => {
|
|
71
71
|
setStyleItems( ( prev ) => ( {
|
|
72
72
|
...prev,
|
|
73
|
-
[ provider.
|
|
73
|
+
[ provider.getKey() ]: { provider, items },
|
|
74
74
|
} ) );
|
|
75
75
|
} )
|
|
76
76
|
.execute()
|
package/src/index.ts
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
export { init } from './init';
|
|
2
2
|
|
|
3
3
|
export { styleTransformersRegistry } from './style-transformers-registry';
|
|
4
4
|
export { settingsTransformersRegistry } from './settings-transformers-registry';
|
|
5
5
|
export { createTransformer } from './transformers/create-transformer';
|
|
6
|
-
|
|
7
|
-
init();
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
updateElementStyle,
|
|
12
12
|
} from '@elementor/editor-elements';
|
|
13
13
|
import { type StyleDefinition } from '@elementor/editor-styles';
|
|
14
|
-
import {
|
|
14
|
+
import { ELEMENTS_STYLES_RESERVED_LABEL } from '@elementor/editor-styles-repository';
|
|
15
15
|
|
|
16
16
|
import { initPasteStyleCommand } from '../paste-style';
|
|
17
17
|
import { getClassesProp, getClipboardElements, isAtomicWidget } from '../utils';
|
|
@@ -132,7 +132,7 @@ describe( 'pasteStyles', () => {
|
|
|
132
132
|
expect( createElementStyle ).toHaveBeenCalledWith( {
|
|
133
133
|
elementId: 'test-container',
|
|
134
134
|
styleId: 's-1',
|
|
135
|
-
label:
|
|
135
|
+
label: ELEMENTS_STYLES_RESERVED_LABEL,
|
|
136
136
|
classesProp: 'classes',
|
|
137
137
|
meta: {
|
|
138
138
|
breakpoint: null,
|
|
@@ -195,7 +195,7 @@ describe( 'pasteStyles', () => {
|
|
|
195
195
|
// Assert.
|
|
196
196
|
expect( createElementStyle ).toHaveBeenCalledWith( {
|
|
197
197
|
elementId: 'test-container',
|
|
198
|
-
label:
|
|
198
|
+
label: ELEMENTS_STYLES_RESERVED_LABEL,
|
|
199
199
|
classesProp: 'classes',
|
|
200
200
|
meta: {
|
|
201
201
|
breakpoint: null,
|
|
@@ -461,7 +461,7 @@ describe( 'pasteStyles', () => {
|
|
|
461
461
|
// Assert.
|
|
462
462
|
expect( createElementStyle ).toHaveBeenCalledWith( {
|
|
463
463
|
elementId: 'test-container-1',
|
|
464
|
-
label:
|
|
464
|
+
label: ELEMENTS_STYLES_RESERVED_LABEL,
|
|
465
465
|
classesProp: 'classes',
|
|
466
466
|
meta: {
|
|
467
467
|
breakpoint: null,
|
|
@@ -522,7 +522,7 @@ describe( 'pasteStyles', () => {
|
|
|
522
522
|
|
|
523
523
|
expect( createElementStyle ).toHaveBeenCalledWith( {
|
|
524
524
|
elementId: 'test-container-2',
|
|
525
|
-
label:
|
|
525
|
+
label: ELEMENTS_STYLES_RESERVED_LABEL,
|
|
526
526
|
classesProp: 'classes',
|
|
527
527
|
styleId: 's-1',
|
|
528
528
|
meta: {
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
mockHistoryManager,
|
|
6
6
|
} from 'test-utils';
|
|
7
7
|
import { createElementStyle, deleteElementStyle, getElementStyles } from '@elementor/editor-elements';
|
|
8
|
-
import {
|
|
8
|
+
import { ELEMENTS_STYLES_RESERVED_LABEL } from '@elementor/editor-styles-repository';
|
|
9
9
|
|
|
10
10
|
import { initResetStyleCommand } from '../reset-style';
|
|
11
11
|
import { getClassesProp, hasAtomicWidgets, isAtomicWidget } from '../utils';
|
|
@@ -80,7 +80,7 @@ describe( 'resetStyles', () => {
|
|
|
80
80
|
// Assert.
|
|
81
81
|
expect( createElementStyle ).toHaveBeenCalledWith( {
|
|
82
82
|
elementId: 'test-container',
|
|
83
|
-
label:
|
|
83
|
+
label: ELEMENTS_STYLES_RESERVED_LABEL,
|
|
84
84
|
classesProp: 'classes',
|
|
85
85
|
meta: {
|
|
86
86
|
breakpoint: null,
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
type V1Element,
|
|
7
7
|
} from '@elementor/editor-elements';
|
|
8
8
|
import { type StyleDefinition } from '@elementor/editor-styles';
|
|
9
|
-
import {
|
|
9
|
+
import { ELEMENTS_STYLES_RESERVED_LABEL } from '@elementor/editor-styles-repository';
|
|
10
10
|
import { undoable } from '@elementor/editor-v1-adapters';
|
|
11
11
|
import { __ } from '@wordpress/i18n';
|
|
12
12
|
|
|
@@ -55,7 +55,7 @@ export const undoablePasteElementStyle = () =>
|
|
|
55
55
|
revertData.styleId = createElementStyle( {
|
|
56
56
|
elementId,
|
|
57
57
|
classesProp,
|
|
58
|
-
label:
|
|
58
|
+
label: ELEMENTS_STYLES_RESERVED_LABEL,
|
|
59
59
|
...firstVariant,
|
|
60
60
|
additionalVariants,
|
|
61
61
|
} );
|
|
@@ -92,7 +92,7 @@ export const undoablePasteElementStyle = () =>
|
|
|
92
92
|
createElementStyle( {
|
|
93
93
|
elementId: container.id,
|
|
94
94
|
classesProp,
|
|
95
|
-
label:
|
|
95
|
+
label: ELEMENTS_STYLES_RESERVED_LABEL,
|
|
96
96
|
styleId: revertData.styleId,
|
|
97
97
|
...firstVariant,
|
|
98
98
|
additionalVariants,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createElementStyle, deleteElementStyle, getElementStyles, type V1Element } from '@elementor/editor-elements';
|
|
2
|
-
import {
|
|
2
|
+
import { ELEMENTS_STYLES_RESERVED_LABEL } from '@elementor/editor-styles-repository';
|
|
3
3
|
import { undoable } from '@elementor/editor-v1-adapters';
|
|
4
4
|
import { __ } from '@wordpress/i18n';
|
|
5
5
|
|
|
@@ -45,7 +45,7 @@ export const undoableResetElementStyle = () =>
|
|
|
45
45
|
elementId,
|
|
46
46
|
classesProp,
|
|
47
47
|
styleId,
|
|
48
|
-
label:
|
|
48
|
+
label: ELEMENTS_STYLES_RESERVED_LABEL,
|
|
49
49
|
...firstVariant,
|
|
50
50
|
additionalVariants,
|
|
51
51
|
} );
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { CanvasExtendedWindow } from './types';
|
|
2
2
|
|
|
3
|
-
export function
|
|
3
|
+
export function getCanvasIframeDocument() {
|
|
4
4
|
const extendedWindow = window as unknown as CanvasExtendedWindow;
|
|
5
5
|
|
|
6
|
-
return extendedWindow.elementor?.$preview?.[ 0 ]?.contentDocument
|
|
6
|
+
return extendedWindow.elementor?.$preview?.[ 0 ]?.contentDocument;
|
|
7
7
|
}
|