@elementor/editor-canvas 0.6.1 → 0.7.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.
@@ -0,0 +1,6 @@
1
+ import { createError } from '@elementor/utils';
2
+
3
+ export const UnknownStyleTypeError = createError< { type: string } >( {
4
+ code: 'unknown_style_type',
5
+ message: 'Unknown style type',
6
+ } );
@@ -1,9 +1,13 @@
1
1
  import { type Props } from '@elementor/editor-props';
2
- import { type Breakpoint, type BreakpointId, type BreakpointsMap } from '@elementor/editor-responsive';
3
- import { type StyleDefinition, type StyleVariant } from '@elementor/editor-styles';
4
- import { ensureError } from '@elementor/utils';
5
-
6
- import { getStylesSchema } from './get-styles-schema';
2
+ import { type Breakpoint, type BreakpointsMap } from '@elementor/editor-responsive';
3
+ import {
4
+ getStylesSchema,
5
+ type StyleDefinition,
6
+ type StyleDefinitionState,
7
+ type StyleDefinitionType,
8
+ } from '@elementor/editor-styles';
9
+
10
+ import { UnknownStyleTypeError } from './errors';
7
11
  import { resolve } from './resolve';
8
12
  import { type TransformersMap } from './types';
9
13
 
@@ -11,81 +15,87 @@ type RenderParams = {
11
15
  transformers: TransformersMap;
12
16
  styles: StyleDefinition[];
13
17
  breakpoints: BreakpointsMap;
18
+ selectorPrefix?: string;
19
+ signal?: AbortSignal;
14
20
  };
15
21
 
16
- export default function render( { transformers, styles, breakpoints }: RenderParams ) {
17
- const cssStyle: string[] = [];
18
-
19
- try {
20
- styles.forEach( ( styleDef ) => {
21
- const style = renderStyle( styleDef, transformers, breakpoints );
22
+ const SELECTORS_MAP: Record< StyleDefinitionType, string > = {
23
+ class: '.',
24
+ };
22
25
 
23
- cssStyle.push( wrapWithStyleElement( styleDef.id, style ) );
26
+ export default async function render( {
27
+ transformers,
28
+ styles,
29
+ breakpoints,
30
+ selectorPrefix = '',
31
+ signal,
32
+ }: RenderParams ) {
33
+ const stylesCssPromises = styles.map( async ( style ) => {
34
+ const variantCssPromises = Object.values( style.variants ).map( async ( variant ) => {
35
+ const css = await propsToCss( variant.props, transformers, signal );
36
+
37
+ return createStyleWrapper()
38
+ .forStyle( style )
39
+ .withPrefix( selectorPrefix )
40
+ .withState( variant.meta.state )
41
+ .withMediaQuery( variant.meta.breakpoint ? breakpoints[ variant.meta.breakpoint ] : null )
42
+ .wrap( css );
24
43
  } );
25
- } catch ( error: unknown ) {
26
- // eslint-disable-next-line no-console
27
- console.error( `Cannot render style': ${ ensureError( error ).message }` );
28
- }
29
44
 
30
- return cssStyle.join( '' );
31
- }
45
+ const variantsCss = await Promise.all( variantCssPromises );
32
46
 
33
- function renderStyle( style: StyleDefinition, transformers: TransformersMap, breakpoints: BreakpointsMap ) {
34
- const baseSelector = getBaseSelector( style );
47
+ return wrapCssWithStyleElement( style.id, variantsCss.join( '' ) );
48
+ } );
35
49
 
36
- if ( ! baseSelector ) {
37
- return '';
38
- }
50
+ const stylesCss = await Promise.all( stylesCssPromises );
39
51
 
40
- const stylesheet: string[] = [];
52
+ return stylesCss.join( '' );
53
+ }
41
54
 
42
- Object.values( style.variants ).forEach( ( variant ) => {
43
- const styleDeclaration = variantToStyleDeclaration( baseSelector, variant, transformers, breakpoints );
55
+ function createStyleWrapper( value: string = '', wrapper?: ( css: string ) => string ) {
56
+ return {
57
+ forStyle: ( { id, type }: StyleDefinition ) => {
58
+ const symbol = SELECTORS_MAP[ type ];
44
59
 
45
- if ( styleDeclaration ) {
46
- stylesheet.push( styleDeclaration );
47
- }
48
- } );
60
+ if ( ! symbol ) {
61
+ throw new UnknownStyleTypeError( { context: { type } } );
62
+ }
49
63
 
50
- return stylesheet.join( '' );
51
- }
64
+ return createStyleWrapper( `${ value }${ symbol }${ id }`, wrapper );
65
+ },
52
66
 
53
- function getBaseSelector( styleDef: StyleDefinition ) {
54
- const map = {
55
- class: '.',
56
- };
67
+ withPrefix: ( prefix: string ) =>
68
+ createStyleWrapper( [ prefix, value ].filter( Boolean ).join( ' ' ), wrapper ),
57
69
 
58
- return `${ map[ styleDef.type ] }${ styleDef.id }`;
59
- }
70
+ withState: ( state: StyleDefinitionState ) =>
71
+ createStyleWrapper( state ? `${ value }:${ state }` : value, wrapper ),
60
72
 
61
- function variantToStyleDeclaration(
62
- baseSelector: string,
63
- variant: StyleVariant,
64
- transformers: TransformersMap,
65
- breakpoints: BreakpointsMap
66
- ) {
67
- const css = propsToCss( variant.props, transformers );
73
+ withMediaQuery: ( breakpoint: Breakpoint | null ) => {
74
+ if ( ! breakpoint?.type ) {
75
+ return createStyleWrapper( value, wrapper );
76
+ }
68
77
 
69
- if ( ! css ) {
70
- return '';
71
- }
78
+ const size = `${ breakpoint.type }:${ breakpoint.width }px`;
72
79
 
73
- const state = variant.meta.state ? `:${ variant.meta.state }` : '';
74
- const selector = `${ baseSelector }${ state }`;
80
+ return createStyleWrapper( value, ( css ) => `@media(${ size }){${ css }}` );
81
+ },
75
82
 
76
- let styleDeclaration = `${ selector }{${ css }}`;
83
+ wrap: ( css: string ) => {
84
+ const res = `${ value }{${ css }}`;
77
85
 
78
- if ( variant.meta.breakpoint ) {
79
- styleDeclaration = wrapWithMediaQuery( breakpoints, variant.meta.breakpoint, styleDeclaration );
80
- }
86
+ if ( ! wrapper ) {
87
+ return res;
88
+ }
81
89
 
82
- return styleDeclaration;
90
+ return wrapper( res );
91
+ },
92
+ };
83
93
  }
84
94
 
85
- function propsToCss( props: Props, transformers: TransformersMap ): string {
95
+ async function propsToCss( props: Props, transformers: TransformersMap, signal?: AbortSignal ) {
86
96
  const schema = getStylesSchema();
87
97
 
88
- const transformed = resolve( { props, schema, transformers } );
98
+ const transformed = await resolve( { props, schema, transformers, signal } );
89
99
 
90
100
  return Object.entries( transformed )
91
101
  .reduce< string[] >( ( acc, [ propName, propValue ] ) => {
@@ -96,15 +106,6 @@ function propsToCss( props: Props, transformers: TransformersMap ): string {
96
106
  .join( '' );
97
107
  }
98
108
 
99
- function wrapWithMediaQuery( breakpoints: BreakpointsMap, breakpoint: BreakpointId, css: string ) {
100
- const size = getBreakpointSize( breakpoints[ breakpoint ] );
101
- return size ? `@media(${ size }){${ css }}` : css;
102
- }
103
-
104
- function getBreakpointSize( breakpoint: Breakpoint ) {
105
- return breakpoint.type ? `${ breakpoint.type }:${ breakpoint.width }px` : null;
106
- }
107
-
108
- function wrapWithStyleElement( id: string, content: string ) {
109
- return `<style data-style-id="${ id }">${ content }</style>`;
109
+ function wrapCssWithStyleElement( id: string, css: string ) {
110
+ return `<style data-style-id="${ id }">${ css }</style>`;
110
111
  }
@@ -10,38 +10,39 @@ type ResolveArgs = {
10
10
  props: Props;
11
11
  schema: PropsSchema;
12
12
  transformers: TransformersMap;
13
+ signal?: AbortSignal;
13
14
  };
14
15
 
15
- export function resolve( { props, schema, transformers }: ResolveArgs ) {
16
- const resolved: Props = {};
16
+ export async function resolve( { props, schema, transformers, signal }: ResolveArgs ) {
17
+ const promises = Promise.all(
18
+ Object.entries( schema ).map( async ( [ propKey, propType ] ) => {
19
+ const value = props[ propKey ] ?? propType.default;
17
20
 
18
- Object.entries( schema ).forEach( ( [ key, propType ] ) => {
19
- const value = props[ key ] ?? propType.default;
21
+ const transformed = await transform( value, propKey, propType, transformers, 0, signal );
20
22
 
21
- const transformed = transform( value, key, propType, transformers );
23
+ if ( transformed === null ) {
24
+ return;
25
+ }
22
26
 
23
- if ( transformed === null ) {
24
- return;
25
- }
26
-
27
- if ( isMultiProps( transformed ) ) {
28
- Object.assign( resolved, getMultiPropsValue( transformed ) );
29
- return;
30
- }
27
+ if ( isMultiProps( transformed ) ) {
28
+ return getMultiPropsValue( transformed );
29
+ }
31
30
 
32
- resolved[ key ] = transformed;
33
- } );
31
+ return { [ propKey ]: transformed };
32
+ } )
33
+ );
34
34
 
35
- return resolved;
35
+ return Object.assign( {}, ...( await promises ).filter( Boolean ) );
36
36
  }
37
37
 
38
- function transform(
38
+ async function transform(
39
39
  value: PropValue,
40
40
  propKey: PropKey,
41
41
  propType: PropType,
42
42
  transformers: TransformersMap,
43
- depth: number = 0
44
- ) {
43
+ depth: number = 0,
44
+ signal?: AbortSignal
45
+ ): Promise< unknown > {
45
46
  if ( ! value && value !== 0 ) {
46
47
  return null;
47
48
  }
@@ -70,16 +71,19 @@ function transform(
70
71
  let resolvedValue = value.value;
71
72
 
72
73
  if ( propType.kind === 'object' ) {
73
- resolvedValue = resolve( {
74
+ resolvedValue = await resolve( {
74
75
  transformers,
75
76
  props: resolvedValue,
76
77
  schema: propType.shape,
78
+ signal,
77
79
  } );
78
80
  }
79
81
 
80
82
  if ( propType.kind === 'array' ) {
81
- resolvedValue = resolvedValue.map( ( item: PropValue ) =>
82
- transform( item, propKey, propType.item_prop_type, transformers, depth )
83
+ resolvedValue = await Promise.all(
84
+ resolvedValue.map( ( item: PropValue ) =>
85
+ transform( item, propKey, propType.item_prop_type, transformers, depth, signal )
86
+ )
83
87
  );
84
88
  }
85
89
 
@@ -90,9 +94,9 @@ function transform(
90
94
  }
91
95
 
92
96
  try {
93
- const transformed = transformer( resolvedValue, propKey );
97
+ const transformed = await transformer( resolvedValue, { key: propKey, signal } );
94
98
 
95
- return transform( transformed, propKey, propType, transformers, depth + 1 );
99
+ return transform( transformed, propKey, propType, transformers, depth + 1, signal );
96
100
  } catch {
97
101
  return null;
98
102
  }
@@ -2,10 +2,30 @@ import { type BackgroundImageOverlayPropValue } from '@elementor/editor-props';
2
2
 
3
3
  import { type Transformer } from '../types';
4
4
 
5
- export const mockBackgroundImageUrl = 'https://bit.ly/2rlzaXi';
5
+ export const defaultPositionValue = '0% 0%';
6
6
 
7
- const backgroundImageOverlayTransformer: Transformer< BackgroundImageOverlayPropValue > = () => {
8
- return `url("${ mockBackgroundImageUrl }")`;
7
+ const backgroundImageOverlayTransformer: Transformer< BackgroundImageOverlayPropValue[ 'value' ] > = ( value ) => {
8
+ const { 'image-src': imageSrc, size = null, position = null, repeat = null, attachment = null } = value;
9
+
10
+ let resultBackgroundStyle = imageSrc;
11
+
12
+ if ( repeat ) {
13
+ resultBackgroundStyle += ` ${ repeat }`;
14
+ }
15
+
16
+ if ( attachment ) {
17
+ resultBackgroundStyle += ` ${ attachment }`;
18
+ }
19
+
20
+ if ( position && ! size ) {
21
+ resultBackgroundStyle += ` ${ position }`;
22
+ }
23
+
24
+ if ( size ) {
25
+ resultBackgroundStyle += ` ${ position || defaultPositionValue } / ${ size }`;
26
+ }
27
+
28
+ return resultBackgroundStyle;
9
29
  };
10
30
 
11
31
  export default backgroundImageOverlayTransformer;
@@ -5,7 +5,7 @@ import { type Transformer } from '../types';
5
5
 
6
6
  const validKeys = [ 'top', 'right', 'bottom', 'left' ];
7
7
 
8
- const dimensions: Transformer< DimensionsPropValue[ 'value' ] > = ( value, key ) => {
8
+ const dimensions: Transformer< DimensionsPropValue[ 'value' ] > = ( value, { key } ) => {
9
9
  const parsed = Object.entries( value ).reduce< Props >( ( acc, [ dimensionKey, dimensionValue ] ) => {
10
10
  if ( dimensionValue && validKeys.includes( dimensionKey ) ) {
11
11
  acc[ `${ key }-${ dimensionKey }` ] = dimensionValue;
@@ -0,0 +1,14 @@
1
+ import { type ImageSrcPropValue } from '@elementor/editor-props';
2
+ import { getMediaAttachment } from '@elementor/wp-media';
3
+
4
+ import { type Transformer } from '../types';
5
+
6
+ export const imageAttachmentTransformer: Transformer< ImageSrcPropValue[ 'value' ][ 'id' ] > = async ( value ) => {
7
+ const attachment = await getMediaAttachment( { id: value } );
8
+
9
+ if ( ! attachment ) {
10
+ return null;
11
+ }
12
+
13
+ return attachment.url;
14
+ };
@@ -0,0 +1,11 @@
1
+ import { type ImageSrcPropValue } from '@elementor/editor-props';
2
+
3
+ import { type Transformer } from '../types';
4
+
5
+ export const imageSrcTransformer: Transformer< ImageSrcPropValue[ 'value' ] > = ( value ) => {
6
+ const url = value.id ?? value.url?.value;
7
+
8
+ if ( url ) {
9
+ return `url(${ url })`;
10
+ }
11
+ };
@@ -6,7 +6,9 @@ import { default as createCombineArrayTransformer } from './create-combine-array
6
6
  import createCornerSizesTransformer from './create-corner-sizes-transformer';
7
7
  import createEdgeSizesTransformer from './create-edge-sizes-transformer';
8
8
  import { default as dimensions } from './dimensions';
9
- import { default as gap } from './gap-transformer';
9
+ import { imageAttachmentTransformer } from './image-attachment';
10
+ import { imageSrcTransformer } from './image-src';
11
+ import { default as layoutDirection } from './layout-direction-transformer';
10
12
  import { primitiveTransformer } from './primitive-transformer';
11
13
  import { default as shadow } from './shadow-transformer';
12
14
  import { default as size } from './size-transformer';
@@ -16,7 +18,6 @@ export default {
16
18
  size,
17
19
  shadow,
18
20
  stroke,
19
- gap,
20
21
  background,
21
22
 
22
23
  color: primitiveTransformer,
@@ -32,4 +33,9 @@ export default {
32
33
 
33
34
  'border-width': createEdgeSizesTransformer( ( edgeKey ) => `border-${ edgeKey }-width` ),
34
35
  'border-radius': createCornerSizesTransformer( ( cornerKey ) => `border-${ cornerKey }-radius` ),
36
+
37
+ 'image-attachment-id': imageAttachmentTransformer,
38
+ 'image-src': imageSrcTransformer,
39
+
40
+ 'layout-direction': layoutDirection,
35
41
  } satisfies TransformersMap;
@@ -1,11 +1,11 @@
1
- import { type GapPropValue, type Props } from '@elementor/editor-props';
1
+ import { type LayoutDirectionPropValue, type Props } from '@elementor/editor-props';
2
2
 
3
3
  import { createMultiPropsValue } from '../multi-props';
4
4
  import { type Transformer } from '../types';
5
5
 
6
6
  const validKeys = [ 'row', 'column' ];
7
7
 
8
- const gapTransformer: Transformer< GapPropValue[ 'value' ] > = ( value, key ) => {
8
+ const layoutDirectionTransformer: Transformer< LayoutDirectionPropValue[ 'value' ] > = ( value, { key } ) => {
9
9
  const parsed = Object.entries( value ).reduce< Props >( ( acc, [ dimensionKey, dimensionValue ] ) => {
10
10
  if ( dimensionValue && validKeys.includes( dimensionKey ) ) {
11
11
  acc[ `${ dimensionKey }-${ key }` ] = dimensionValue;
@@ -17,4 +17,4 @@ const gapTransformer: Transformer< GapPropValue[ 'value' ] > = ( value, key ) =>
17
17
  return createMultiPropsValue( parsed );
18
18
  };
19
19
 
20
- export default gapTransformer;
20
+ export default layoutDirectionTransformer;
@@ -1,6 +1,16 @@
1
1
  import { type PropValue } from '@elementor/editor-props';
2
2
 
3
- export type Transformer< TValue extends PropValue > = ( value: TValue, key: string ) => PropValue;
3
+ type TransformerOptions = {
4
+ key: string;
5
+ signal?: AbortSignal;
6
+ };
7
+
8
+ type MaybePromise< T > = T | Promise< T >;
9
+
10
+ export type Transformer< TValue extends PropValue > = (
11
+ value: TValue,
12
+ options: TransformerOptions
13
+ ) => MaybePromise< PropValue >;
4
14
 
5
15
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
6
16
  export type TransformersMap = Record< string, Transformer< any > >;
@@ -1,17 +0,0 @@
1
- import { type PropKey, type PropType } from '@elementor/editor-props';
2
-
3
- type ExtendedWindow = Window & {
4
- elementor?: {
5
- config?: {
6
- atomic?: {
7
- styles_schema?: Record< PropKey, PropType >;
8
- };
9
- };
10
- };
11
- };
12
-
13
- export const getStylesSchema = () => {
14
- const extendedWindow = window as unknown as ExtendedWindow;
15
-
16
- return extendedWindow.elementor?.config?.atomic?.styles_schema ?? {};
17
- };