@elementor/editor-controls 3.35.0-487 → 3.35.0-488

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@elementor/editor-controls",
3
3
  "description": "This package contains the controls model and utils for the Elementor editor",
4
- "version": "3.35.0-487",
4
+ "version": "3.35.0-488",
5
5
  "private": false,
6
6
  "author": "Elementor Team",
7
7
  "homepage": "https://elementor.com/",
@@ -40,22 +40,22 @@
40
40
  "dev": "tsup --config=../../tsup.dev.ts"
41
41
  },
42
42
  "dependencies": {
43
- "@elementor/editor-current-user": "3.35.0-487",
44
- "@elementor/editor-elements": "3.35.0-487",
45
- "@elementor/editor-props": "3.35.0-487",
46
- "@elementor/editor-responsive": "3.35.0-487",
47
- "@elementor/editor-ui": "3.35.0-487",
48
- "@elementor/editor-v1-adapters": "3.35.0-487",
49
- "@elementor/env": "3.35.0-487",
50
- "@elementor/http-client": "3.35.0-487",
43
+ "@elementor/editor-current-user": "3.35.0-488",
44
+ "@elementor/editor-elements": "3.35.0-488",
45
+ "@elementor/editor-props": "3.35.0-488",
46
+ "@elementor/editor-responsive": "3.35.0-488",
47
+ "@elementor/editor-ui": "3.35.0-488",
48
+ "@elementor/editor-v1-adapters": "3.35.0-488",
49
+ "@elementor/env": "3.35.0-488",
50
+ "@elementor/http-client": "3.35.0-488",
51
51
  "@elementor/icons": "^1.63.0",
52
- "@elementor/locations": "3.35.0-487",
53
- "@elementor/mixpanel": "3.35.0-487",
54
- "@elementor/query": "3.35.0-487",
55
- "@elementor/session": "3.35.0-487",
52
+ "@elementor/locations": "3.35.0-488",
53
+ "@elementor/mixpanel": "3.35.0-488",
54
+ "@elementor/query": "3.35.0-488",
55
+ "@elementor/session": "3.35.0-488",
56
56
  "@elementor/ui": "1.36.17",
57
- "@elementor/utils": "3.35.0-487",
58
- "@elementor/wp-media": "3.35.0-487",
57
+ "@elementor/utils": "3.35.0-488",
58
+ "@elementor/wp-media": "3.35.0-488",
59
59
  "@wordpress/i18n": "^5.13.0",
60
60
  "@monaco-editor/react": "^4.7.0",
61
61
  "dayjs": "^1.11.18",
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- import { useCallback, useEffect, useState } from 'react';
2
+ import { type ReactNode, useCallback, useEffect, useState } from 'react';
3
3
  import { PopoverBody, PopoverHeader, PopoverMenuList, SearchField } from '@elementor/editor-ui';
4
4
  import { Box, Divider, Link, Stack, Typography } from '@elementor/ui';
5
5
  import { debounce } from '@elementor/utils';
@@ -24,6 +24,8 @@ type ItemSelectorProps = {
24
24
  icon: React.ElementType< { fontSize: string } >;
25
25
  disabledItems?: string[];
26
26
  id?: string;
27
+ footer?: ReactNode;
28
+ categoryItemContentTemplate?: ( item: SelectableItem ) => ReactNode;
27
29
  };
28
30
 
29
31
  export const ItemSelector = ( {
@@ -38,6 +40,8 @@ export const ItemSelector = ( {
38
40
  icon,
39
41
  disabledItems,
40
42
  id = 'item-selector',
43
+ footer,
44
+ categoryItemContentTemplate,
41
45
  }: ItemSelectorProps ) => {
42
46
  const [ searchValue, setSearchValue ] = useState( '' );
43
47
 
@@ -66,62 +70,66 @@ export const ItemSelector = ( {
66
70
 
67
71
  <Divider />
68
72
 
69
- { filteredItemsList.length > 0 ? (
70
- <ItemList
71
- itemListItems={ filteredItemsList }
72
- setSelectedItem={ onItemChange }
73
- handleClose={ handleClose }
74
- selectedItem={ selectedItem }
75
- itemStyle={ itemStyle }
76
- onDebounce={ onDebounce }
77
- />
78
- ) : (
79
- <Stack
80
- alignItems="center"
81
- justifyContent="center"
82
- height="100%"
83
- p={ 2.5 }
84
- gap={ 1.5 }
85
- overflow="hidden"
86
- >
87
- <IconComponent fontSize="large" />
88
- <Box sx={ { maxWidth: 160, overflow: 'hidden' } }>
89
- <Typography align="center" variant="subtitle2" color="text.secondary">
90
- { __( 'Sorry, nothing matched', 'elementor' ) }
91
- </Typography>
73
+ <Box sx={ { flex: 1, overflow: 'auto', minHeight: 0 } }>
74
+ { filteredItemsList.length > 0 ? (
75
+ <ItemList
76
+ itemListItems={ filteredItemsList }
77
+ setSelectedItem={ onItemChange }
78
+ handleClose={ handleClose }
79
+ selectedItem={ selectedItem }
80
+ itemStyle={ itemStyle }
81
+ onDebounce={ onDebounce }
82
+ categoryItemContentTemplate={ categoryItemContentTemplate }
83
+ />
84
+ ) : (
85
+ <Stack
86
+ alignItems="center"
87
+ justifyContent="center"
88
+ height="100%"
89
+ p={ 2.5 }
90
+ gap={ 1.5 }
91
+ overflow="hidden"
92
+ >
93
+ <IconComponent fontSize="large" />
94
+ <Box sx={ { maxWidth: 160, overflow: 'hidden' } }>
95
+ <Typography align="center" variant="subtitle2" color="text.secondary">
96
+ { __( 'Sorry, nothing matched', 'elementor' ) }
97
+ </Typography>
98
+ <Typography
99
+ variant="subtitle2"
100
+ color="text.secondary"
101
+ sx={ { display: 'flex', width: '100%', justifyContent: 'center' } }
102
+ >
103
+ <span>&ldquo;</span>
104
+ <Box
105
+ component="span"
106
+ sx={ { maxWidth: '80%', overflow: 'hidden', textOverflow: 'ellipsis' } }
107
+ >
108
+ { searchValue }
109
+ </Box>
110
+ <span>&rdquo;.</span>
111
+ </Typography>
112
+ </Box>
92
113
  <Typography
93
- variant="subtitle2"
114
+ align="center"
115
+ variant="caption"
94
116
  color="text.secondary"
95
- sx={ { display: 'flex', width: '100%', justifyContent: 'center' } }
117
+ sx={ { display: 'flex', flexDirection: 'column' } }
96
118
  >
97
- <span>&ldquo;</span>
98
- <Box
99
- component="span"
100
- sx={ { maxWidth: '80%', overflow: 'hidden', textOverflow: 'ellipsis' } }
119
+ { __( 'Try something else.', 'elementor' ) }
120
+ <Link
121
+ color="secondary"
122
+ variant="caption"
123
+ component="button"
124
+ onClick={ () => setSearchValue( '' ) }
101
125
  >
102
- { searchValue }
103
- </Box>
104
- <span>&rdquo;.</span>
126
+ { __( 'Clear & try again', 'elementor' ) }
127
+ </Link>
105
128
  </Typography>
106
- </Box>
107
- <Typography
108
- align="center"
109
- variant="caption"
110
- color="text.secondary"
111
- sx={ { display: 'flex', flexDirection: 'column' } }
112
- >
113
- { __( 'Try something else.', 'elementor' ) }
114
- <Link
115
- color="secondary"
116
- variant="caption"
117
- component="button"
118
- onClick={ () => setSearchValue( '' ) }
119
- >
120
- { __( 'Clear & try again', 'elementor' ) }
121
- </Link>
122
- </Typography>
123
- </Stack>
124
- ) }
129
+ </Stack>
130
+ ) }
131
+ </Box>
132
+ { footer }
125
133
  </PopoverBody>
126
134
  );
127
135
  };
@@ -134,6 +142,7 @@ type ItemListProps = {
134
142
  itemStyle?: ( item: SelectableItem ) => React.CSSProperties;
135
143
  onDebounce?: ( name: string ) => void;
136
144
  disabledItems?: string[];
145
+ categoryItemContentTemplate?: ( item: SelectableItem ) => ReactNode;
137
146
  };
138
147
 
139
148
  const ItemList = ( {
@@ -143,6 +152,7 @@ const ItemList = ( {
143
152
  selectedItem,
144
153
  itemStyle = () => ( {} ),
145
154
  onDebounce = () => {},
155
+ categoryItemContentTemplate,
146
156
  }: ItemListProps ) => {
147
157
  const selectedItemFound = itemListItems.find( ( item ) => item.value === selectedItem );
148
158
 
@@ -165,6 +175,7 @@ const ItemList = ( {
165
175
  onClose={ handleClose }
166
176
  itemStyle={ memoizedItemStyle }
167
177
  data-testid="item-list"
178
+ categoryItemContentTemplate={ categoryItemContentTemplate }
168
179
  />
169
180
  );
170
181
  };
@@ -1,4 +1,5 @@
1
1
  import { type KeyValuePropValue, type SizePropValue } from '@elementor/editor-props';
2
+ import { isVersionGreaterOrEqual } from '@elementor/utils';
2
3
  import { __ } from '@wordpress/i18n';
3
4
 
4
5
  export type TransitionProperty = {
@@ -38,13 +39,134 @@ export const initialTransitionValue: TransitionValue = {
38
39
  size: { $$type: 'size', value: { size: 200, unit: 'ms' } },
39
40
  };
40
41
 
41
- export const transitionProperties: TransitionCategory[] = [
42
- {
43
- label: __( 'Default', 'elementor' ),
44
- type: 'category',
45
- properties: [ { label: __( 'All properties', 'elementor' ), value: 'all' } ],
46
- },
47
- ];
42
+ const MIN_PRO_VERSION = '3.35';
43
+
44
+ // TODO: Remove this after version 4.01 is released
45
+ const shouldExtendTransitionProperties = (): boolean => {
46
+ const hasProInstalled = !! window.elementorPro;
47
+
48
+ if ( ! hasProInstalled ) {
49
+ return true;
50
+ }
51
+
52
+ const proVersion = window.elementorPro?.config?.version;
53
+
54
+ if ( ! proVersion ) {
55
+ return false;
56
+ }
57
+
58
+ return isVersionGreaterOrEqual( proVersion, MIN_PRO_VERSION );
59
+ };
60
+
61
+ const createTransitionPropertiesList = (): TransitionCategory[] => {
62
+ const baseProperties: TransitionCategory[] = [
63
+ {
64
+ label: __( 'Default', 'elementor' ),
65
+ type: 'category',
66
+ properties: [ { label: __( 'All properties', 'elementor' ), value: 'all' } ],
67
+ },
68
+ {
69
+ label: __( 'Margin', 'elementor' ),
70
+ type: 'category',
71
+ properties: [
72
+ { label: __( 'Margin (all)', 'elementor' ), value: 'margin', isDisabled: true },
73
+ { label: __( 'Margin bottom', 'elementor' ), value: 'margin-block-end', isDisabled: true },
74
+ { label: __( 'Margin left', 'elementor' ), value: 'margin-inline-start', isDisabled: true },
75
+ { label: __( 'Margin right', 'elementor' ), value: 'margin-inline-end', isDisabled: true },
76
+ { label: __( 'Margin top', 'elementor' ), value: 'margin-block-start', isDisabled: true },
77
+ ],
78
+ },
79
+ {
80
+ label: __( 'Padding', 'elementor' ),
81
+ type: 'category',
82
+ properties: [
83
+ { label: __( 'Padding (all)', 'elementor' ), value: 'padding', isDisabled: true },
84
+ { label: __( 'Padding bottom', 'elementor' ), value: 'padding-block-end', isDisabled: true },
85
+ { label: __( 'Padding left', 'elementor' ), value: 'padding-inline-start', isDisabled: true },
86
+ { label: __( 'Padding right', 'elementor' ), value: 'padding-inline-end', isDisabled: true },
87
+ { label: __( 'Padding top', 'elementor' ), value: 'padding-block-start', isDisabled: true },
88
+ ],
89
+ },
90
+ {
91
+ label: __( 'Flex', 'elementor' ),
92
+ type: 'category',
93
+ properties: [
94
+ { label: __( 'Flex (all)', 'elementor' ), value: 'flex', isDisabled: true },
95
+ { label: __( 'Flex grow', 'elementor' ), value: 'flex-grow', isDisabled: true },
96
+ { label: __( 'Flex shrink', 'elementor' ), value: 'flex-shrink', isDisabled: true },
97
+ { label: __( 'Flex basis', 'elementor' ), value: 'flex-basis', isDisabled: true },
98
+ ],
99
+ },
100
+ {
101
+ label: __( 'Size', 'elementor' ),
102
+ type: 'category',
103
+ properties: [
104
+ { label: __( 'Width', 'elementor' ), value: 'width', isDisabled: true },
105
+ { label: __( 'Height', 'elementor' ), value: 'height', isDisabled: true },
106
+ { label: __( 'Max height', 'elementor' ), value: 'max-height', isDisabled: true },
107
+ { label: __( 'Max width', 'elementor' ), value: 'max-width', isDisabled: true },
108
+ { label: __( 'Min height', 'elementor' ), value: 'min-height', isDisabled: true },
109
+ { label: __( 'Min width', 'elementor' ), value: 'min-width', isDisabled: true },
110
+ ],
111
+ },
112
+ {
113
+ label: __( 'Position', 'elementor' ),
114
+ type: 'category',
115
+ properties: [
116
+ { label: __( 'Top', 'elementor' ), value: 'inset-block-start', isDisabled: true },
117
+ { label: __( 'Left', 'elementor' ), value: 'inset-inline-start', isDisabled: true },
118
+ { label: __( 'Right', 'elementor' ), value: 'inset-inline-end', isDisabled: true },
119
+ { label: __( 'Bottom', 'elementor' ), value: 'inset-block-end', isDisabled: true },
120
+ { label: __( 'Z-index', 'elementor' ), value: 'z-index', isDisabled: true },
121
+ ],
122
+ },
123
+ {
124
+ label: __( 'Typography', 'elementor' ),
125
+ type: 'category',
126
+ properties: [
127
+ { label: __( 'Font color', 'elementor' ), value: 'color', isDisabled: true },
128
+ { label: __( 'Font size', 'elementor' ), value: 'font-size', isDisabled: true },
129
+ { label: __( 'Line height', 'elementor' ), value: 'line-height', isDisabled: true },
130
+ { label: __( 'Letter spacing', 'elementor' ), value: 'letter-spacing', isDisabled: true },
131
+ { label: __( 'Word spacing', 'elementor' ), value: 'word-spacing', isDisabled: true },
132
+ { label: __( 'Font variations', 'elementor' ), value: 'font-variation-settings', isDisabled: true },
133
+ { label: __( 'Text stroke color', 'elementor' ), value: '-webkit-text-stroke-color', isDisabled: true },
134
+ ],
135
+ },
136
+ {
137
+ label: __( 'Background', 'elementor' ),
138
+ type: 'category',
139
+ properties: [
140
+ { label: __( 'Background color', 'elementor' ), value: 'background-color', isDisabled: true },
141
+ { label: __( 'Background position', 'elementor' ), value: 'background-position', isDisabled: true },
142
+ { label: __( 'Box shadow', 'elementor' ), value: 'box-shadow', isDisabled: true },
143
+ ],
144
+ },
145
+ {
146
+ label: __( 'Border', 'elementor' ),
147
+ type: 'category',
148
+ properties: [
149
+ { label: __( 'Border (all)', 'elementor' ), value: 'border', isDisabled: true },
150
+ { label: __( 'Border radius', 'elementor' ), value: 'border-radius', isDisabled: true },
151
+ { label: __( 'Border color', 'elementor' ), value: 'border-color', isDisabled: true },
152
+ { label: __( 'Border width', 'elementor' ), value: 'border-width', isDisabled: true },
153
+ ],
154
+ },
155
+ {
156
+ label: __( 'Effects', 'elementor' ),
157
+ type: 'category',
158
+ properties: [
159
+ { label: __( 'Opacity', 'elementor' ), value: 'opacity', isDisabled: true },
160
+ { label: __( 'Transform (all)', 'elementor' ), value: 'transform', isDisabled: true },
161
+ { label: __( 'Filter (all)', 'elementor' ), value: 'filter', isDisabled: true },
162
+ ],
163
+ },
164
+ ];
165
+
166
+ return shouldExtendTransitionProperties() ? baseProperties : [ baseProperties[ 0 ] ];
167
+ };
168
+
169
+ export const transitionProperties: TransitionCategory[] = createTransitionPropertiesList();
48
170
 
49
171
  export const transitionsItemsList = transitionProperties.map( ( category ) => ( {
50
172
  label: category.label,
@@ -59,7 +59,7 @@ const areAllPropertiesUsed = ( value: SelectionSizePropValue[] = [] ) => {
59
59
 
60
60
  // this config needs to be loaded at runtime/render since it's the transitionProperties object will be mutated by the pro plugin.
61
61
  // See: https://elementor.atlassian.net/browse/ED-20285
62
- const getSelectionSizeProps = ( recentlyUsedList: string[], disabledItems?: string[] ) => {
62
+ const getSelectionSizeProps = ( recentlyUsedList: string[], disabledItems?: string[], showPromotion?: boolean ) => {
63
63
  return {
64
64
  selectionLabel: __( 'Type', 'elementor' ),
65
65
  sizeLabel: __( 'Duration', 'elementor' ),
@@ -68,6 +68,7 @@ const getSelectionSizeProps = ( recentlyUsedList: string[], disabledItems?: stri
68
68
  props: {
69
69
  recentlyUsedList,
70
70
  disabledItems,
71
+ showPromotion,
71
72
  },
72
73
  },
73
74
  sizeConfigMap: {
@@ -90,11 +91,11 @@ const isItemDisabled = ( item: TransitionItem[ 'value' ] ) => {
90
91
  return ! property ? false : !! property.isDisabled;
91
92
  };
92
93
 
93
- const getChildControlConfig = ( recentlyUsedList: string[], disabledItems?: string[] ) => {
94
+ const getChildControlConfig = ( recentlyUsedList: string[], disabledItems?: string[], showPromotion?: boolean ) => {
94
95
  return {
95
96
  propTypeUtil: selectionSizePropTypeUtil,
96
97
  component: SelectionSizeControl as unknown as React.ComponentType< Record< string, unknown > >,
97
- props: getSelectionSizeProps( recentlyUsedList, disabledItems ),
98
+ props: getSelectionSizeProps( recentlyUsedList, disabledItems, showPromotion ),
98
99
  isItemDisabled: isItemDisabled as ( item: Item< RepeatablePropValue > ) => boolean,
99
100
  };
100
101
  };
@@ -106,19 +107,23 @@ const isPropertyUsed = ( value: SelectionSizePropValue[], property: TransitionPr
106
107
  };
107
108
 
108
109
  const getDisabledItemLabels = ( values: SelectionSizePropValue[] = [] ) => {
109
- const disabledLabels: string[] = ( values || [] ).map(
110
+ const selectedLabels: string[] = ( values || [] ).map(
110
111
  ( item ) => ( item.value?.selection as KeyValuePropValue )?.value?.key?.value
111
112
  );
112
113
 
114
+ const proDisabledLabels: string[] = [];
113
115
  transitionProperties.forEach( ( category ) => {
114
116
  const disabledProperties = category.properties
115
- .filter( ( property ) => property.isDisabled && ! disabledLabels.includes( property.label ) )
117
+ .filter( ( property ) => property.isDisabled && ! selectedLabels.includes( property.label ) )
116
118
  .map( ( property ) => property.label );
117
119
 
118
- disabledLabels.push( ...disabledProperties );
120
+ proDisabledLabels.push( ...disabledProperties );
119
121
  } );
120
122
 
121
- return disabledLabels;
123
+ return {
124
+ allDisabled: [ ...selectedLabels, ...proDisabledLabels ],
125
+ proDisabled: proDisabledLabels,
126
+ };
122
127
  };
123
128
 
124
129
  const getInitialValue = ( values: SelectionSizePropValue[] = [] ): TransitionValue => {
@@ -178,7 +183,10 @@ export const TransitionRepeaterControl = createControl(
178
183
  const [ recentlyUsedList, setRecentlyUsedList ] = useState< string[] >( [] );
179
184
 
180
185
  const { value, setValue } = useBoundProp( childArrayPropTypeUtil );
181
- const disabledItems = useMemo( () => getDisabledItemLabels( value ), [ value ] );
186
+ const { allDisabled: disabledItems, proDisabled: proDisabledItems } = useMemo(
187
+ () => getDisabledItemLabels( value ),
188
+ [ value ]
189
+ );
182
190
 
183
191
  const allowedTransitionSet = useMemo( () => {
184
192
  const set = new Set< string >();
@@ -220,7 +228,11 @@ export const TransitionRepeaterControl = createControl(
220
228
  showDuplicate={ false }
221
229
  showToggle={ true }
222
230
  initialValues={ getInitialValue( value ) }
223
- childControlConfig={ getChildControlConfig( recentlyUsedList, disabledItems ) }
231
+ childControlConfig={ getChildControlConfig(
232
+ recentlyUsedList,
233
+ disabledItems,
234
+ proDisabledItems.length > 0
235
+ ) }
224
236
  propKey="transition"
225
237
  addItemTooltipProps={ {
226
238
  disabled: isAddItemDisabled,
@@ -1,8 +1,9 @@
1
1
  import * as React from 'react';
2
- import { useRef } from 'react';
2
+ import { useMemo, useRef } from 'react';
3
3
  import { keyValuePropTypeUtil, type KeyValuePropValue, type StringPropValue } from '@elementor/editor-props';
4
- import { ChevronDownIcon, VariationsIcon } from '@elementor/icons';
5
- import { bindPopover, bindTrigger, Box, Popover, UnstableTag, usePopupState } from '@elementor/ui';
4
+ import { PromotionChip } from '@elementor/editor-ui';
5
+ import { ChevronDownIcon, CrownFilledIcon, VariationsIcon } from '@elementor/icons';
6
+ import { Alert, bindPopover, bindTrigger, Box, Popover, UnstableTag, usePopupState } from '@elementor/ui';
6
7
  import { __ } from '@wordpress/i18n';
7
8
 
8
9
  import { useBoundProp } from '../../bound-prop-context';
@@ -46,12 +47,16 @@ const includeCurrentValueInOptions = ( value: KeyValuePropValue[ 'value' ], disa
46
47
  } );
47
48
  };
48
49
 
50
+ const PRO_UPGRADE_URL = 'https://go.elementor.com/go-pro-transitions-modal/';
51
+
49
52
  export const TransitionSelector = ( {
50
53
  recentlyUsedList = [],
51
54
  disabledItems = [],
55
+ showPromotion = false,
52
56
  }: {
53
57
  recentlyUsedList: string[];
54
58
  disabledItems?: string[];
59
+ showPromotion?: boolean;
55
60
  } ) => {
56
61
  const { value, setValue } = useBoundProp( keyValuePropTypeUtil );
57
62
  const {
@@ -60,6 +65,14 @@ export const TransitionSelector = ( {
60
65
  const defaultRef = useRef< HTMLDivElement >( null );
61
66
  const popoverState = usePopupState( { variant: 'popover' } );
62
67
 
68
+ const disabledCategories = useMemo( () => {
69
+ return new Set(
70
+ transitionProperties
71
+ .filter( ( cat ) => cat.properties.some( ( prop ) => prop.isDisabled ) )
72
+ .map( ( cat ) => cat.label )
73
+ );
74
+ }, [] );
75
+
63
76
  const getItemList = () => {
64
77
  const recentItems = recentlyUsedList
65
78
  .map( ( item ) => getTransitionPropertyByValue( { value: item, $$type: 'string' } )?.label )
@@ -136,6 +149,49 @@ export const TransitionSelector = ( {
136
149
  title={ __( 'Transition Property', 'elementor' ) }
137
150
  icon={ VariationsIcon as React.ElementType< { fontSize: string } > }
138
151
  disabledItems={ includeCurrentValueInOptions( value, disabledItems ) }
152
+ categoryItemContentTemplate={ ( item ) => (
153
+ <Box
154
+ sx={ {
155
+ display: 'flex',
156
+ alignItems: 'center',
157
+ justifyContent: 'space-between',
158
+ width: '100%',
159
+ } }
160
+ >
161
+ <span>{ item.value }</span>
162
+ { showPromotion && disabledCategories.has( item.value ) && <PromotionChip /> }
163
+ </Box>
164
+ ) }
165
+ footer={
166
+ showPromotion ? (
167
+ <Alert
168
+ variant="standard"
169
+ color="promotion"
170
+ icon={ false }
171
+ role="dialog"
172
+ aria-label="promotion-alert"
173
+ size="small"
174
+ sx={ { m: 1.5, mt: 0 } }
175
+ >
176
+ { __( 'Upgrade to customize transition properties and control effects.', 'elementor' ) }
177
+ <Box
178
+ component="a"
179
+ href={ PRO_UPGRADE_URL }
180
+ target="_blank"
181
+ rel="noopener noreferrer"
182
+ sx={ {
183
+ display: 'flex',
184
+ alignItems: 'center',
185
+ gap: 0.5,
186
+ color: 'promotion.main',
187
+ } }
188
+ >
189
+ <CrownFilledIcon fontSize="tiny" />
190
+ { __( 'Upgrade now', 'elementor' ) }
191
+ </Box>
192
+ </Alert>
193
+ ) : null
194
+ }
139
195
  />
140
196
  </Popover>
141
197
  </Box>