@elementor/editor-controls 0.21.0 → 0.24.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/CHANGELOG.md +36 -0
- package/dist/index.js +91 -42
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +94 -45
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
- package/src/components/repeater.tsx +9 -6
- package/src/components/sortable.tsx +6 -6
- package/src/components/text-field-inner-selection.tsx +3 -3
- package/src/controls/background-control/background-overlay/background-overlay-repeater-control.tsx +12 -1
- package/src/controls/font-family-control/font-family-control.tsx +2 -0
- package/src/controls/link-control.tsx +95 -23
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": "0.
|
|
4
|
+
"version": "0.24.0",
|
|
5
5
|
"private": false,
|
|
6
6
|
"author": "Elementor Team",
|
|
7
7
|
"homepage": "https://elementor.com/",
|
|
@@ -41,9 +41,9 @@
|
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"@elementor/editor-current-user": "0.3.0",
|
|
44
|
-
"@elementor/editor-elements": "0.
|
|
44
|
+
"@elementor/editor-elements": "0.8.0",
|
|
45
45
|
"@elementor/editor-props": "0.11.1",
|
|
46
|
-
"@elementor/editor-ui": "0.
|
|
46
|
+
"@elementor/editor-ui": "0.7.0",
|
|
47
47
|
"@elementor/env": "0.3.5",
|
|
48
48
|
"@elementor/http": "0.1.4",
|
|
49
49
|
"@elementor/icons": "1.37.0",
|
|
@@ -48,6 +48,8 @@ type RepeaterProps< T > = {
|
|
|
48
48
|
};
|
|
49
49
|
};
|
|
50
50
|
|
|
51
|
+
const EMPTY_OPEN_ITEM = -1;
|
|
52
|
+
|
|
51
53
|
export const Repeater = < T, >( {
|
|
52
54
|
label,
|
|
53
55
|
itemSettings,
|
|
@@ -56,7 +58,7 @@ export const Repeater = < T, >( {
|
|
|
56
58
|
values: repeaterValues = [],
|
|
57
59
|
setValues: setRepeaterValues,
|
|
58
60
|
}: RepeaterProps< Item< T > > ) => {
|
|
59
|
-
const [ openItem, setOpenItem ] = useState(
|
|
61
|
+
const [ openItem, setOpenItem ] = useState( EMPTY_OPEN_ITEM );
|
|
60
62
|
|
|
61
63
|
const [ items, setItems ] = useSyncExternalState( {
|
|
62
64
|
external: repeaterValues,
|
|
@@ -166,7 +168,6 @@ export const Repeater = < T, >( {
|
|
|
166
168
|
return (
|
|
167
169
|
<SortableItem id={ key } key={ `sortable-${ key }` }>
|
|
168
170
|
<RepeaterItem
|
|
169
|
-
bind={ String( index ) }
|
|
170
171
|
disabled={ value?.disabled }
|
|
171
172
|
label={ <itemSettings.Label value={ value } /> }
|
|
172
173
|
startIcon={ <itemSettings.Icon value={ value } /> }
|
|
@@ -174,6 +175,7 @@ export const Repeater = < T, >( {
|
|
|
174
175
|
duplicateItem={ () => duplicateRepeaterItem( index ) }
|
|
175
176
|
toggleDisableItem={ () => toggleDisableRepeaterItem( index ) }
|
|
176
177
|
openOnMount={ openOnAdd && openItem === key }
|
|
178
|
+
onOpen={ () => setOpenItem( EMPTY_OPEN_ITEM ) }
|
|
177
179
|
>
|
|
178
180
|
{ ( props ) => (
|
|
179
181
|
<itemSettings.Content { ...props } value={ value } bind={ String( index ) } />
|
|
@@ -190,7 +192,6 @@ export const Repeater = < T, >( {
|
|
|
190
192
|
|
|
191
193
|
type RepeaterItemProps = {
|
|
192
194
|
label: React.ReactNode;
|
|
193
|
-
bind: string;
|
|
194
195
|
disabled?: boolean;
|
|
195
196
|
startIcon: UnstableTagProps[ 'startIcon' ];
|
|
196
197
|
removeItem: () => void;
|
|
@@ -198,6 +199,7 @@ type RepeaterItemProps = {
|
|
|
198
199
|
toggleDisableItem: () => void;
|
|
199
200
|
children: ( { anchorEl }: { anchorEl: AnchorEl } ) => React.ReactNode;
|
|
200
201
|
openOnMount: boolean;
|
|
202
|
+
onOpen: () => void;
|
|
201
203
|
};
|
|
202
204
|
|
|
203
205
|
const RepeaterItem = ( {
|
|
@@ -209,9 +211,10 @@ const RepeaterItem = ( {
|
|
|
209
211
|
duplicateItem,
|
|
210
212
|
toggleDisableItem,
|
|
211
213
|
openOnMount,
|
|
214
|
+
onOpen,
|
|
212
215
|
}: RepeaterItemProps ) => {
|
|
213
216
|
const [ anchorEl, setAnchorEl ] = useState< AnchorEl >( null );
|
|
214
|
-
const { popoverState, popoverProps, ref, setRef } = usePopover( openOnMount );
|
|
217
|
+
const { popoverState, popoverProps, ref, setRef } = usePopover( openOnMount, onOpen );
|
|
215
218
|
|
|
216
219
|
const duplicateLabel = __( 'Duplicate', 'elementor' );
|
|
217
220
|
const toggleLabel = disabled ? __( 'Show', 'elementor' ) : __( 'Hide', 'elementor' );
|
|
@@ -247,7 +250,6 @@ const RepeaterItem = ( {
|
|
|
247
250
|
</Tooltip>
|
|
248
251
|
</>
|
|
249
252
|
}
|
|
250
|
-
sx={ { backgroundColor: 'background.paper' } }
|
|
251
253
|
/>
|
|
252
254
|
<Popover
|
|
253
255
|
disablePortal
|
|
@@ -267,7 +269,7 @@ const RepeaterItem = ( {
|
|
|
267
269
|
);
|
|
268
270
|
};
|
|
269
271
|
|
|
270
|
-
const usePopover = ( openOnMount: boolean ) => {
|
|
272
|
+
const usePopover = ( openOnMount: boolean, onOpen: () => void ) => {
|
|
271
273
|
const [ ref, setRef ] = useState< HTMLElement | null >( null );
|
|
272
274
|
|
|
273
275
|
const popoverState = usePopupState( { variant: 'popover' } );
|
|
@@ -277,6 +279,7 @@ const usePopover = ( openOnMount: boolean ) => {
|
|
|
277
279
|
useEffect( () => {
|
|
278
280
|
if ( openOnMount && ref ) {
|
|
279
281
|
popoverState.open( ref );
|
|
282
|
+
onOpen?.();
|
|
280
283
|
}
|
|
281
284
|
// eslint-disable-next-line react-compiler/react-compiler
|
|
282
285
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
@@ -34,16 +34,11 @@ export const SortableItem = ( { id, children }: SortableItemProps ): React.React
|
|
|
34
34
|
triggerProps,
|
|
35
35
|
itemStyle,
|
|
36
36
|
triggerStyle,
|
|
37
|
-
isDragOverlay,
|
|
38
37
|
showDropIndication,
|
|
39
38
|
dropIndicationStyle,
|
|
40
39
|
}: UnstableSortableItemRenderProps ) => {
|
|
41
40
|
return (
|
|
42
|
-
<StyledListItem
|
|
43
|
-
{ ...itemProps }
|
|
44
|
-
style={ itemStyle }
|
|
45
|
-
sx={ { backgroundColor: isDragOverlay ? 'background.paper' : undefined } }
|
|
46
|
-
>
|
|
41
|
+
<StyledListItem { ...itemProps } style={ itemStyle }>
|
|
47
42
|
<SortableTrigger { ...triggerProps } style={ triggerStyle } />
|
|
48
43
|
{ children }
|
|
49
44
|
{ showDropIndication && <StyledDivider style={ dropIndicationStyle } /> }
|
|
@@ -72,6 +67,11 @@ const StyledListItem = styled( ListItem )`
|
|
|
72
67
|
transform: translate( -75%, -50% );
|
|
73
68
|
}
|
|
74
69
|
|
|
70
|
+
&[aria-describedby=''] > .MuiTag-root {
|
|
71
|
+
background-color: ${ ( { theme } ) => theme.palette.background.paper };
|
|
72
|
+
box-shadow: ${ ( { theme } ) => theme.shadows[ 3 ] };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
75
|
&:hover {
|
|
76
76
|
& .class-item-sortable-trigger {
|
|
77
77
|
visibility: visible;
|
|
@@ -58,11 +58,11 @@ export const SelectionEndAdornment = < T extends string >( {
|
|
|
58
58
|
<InputAdornment position="end">
|
|
59
59
|
<Button
|
|
60
60
|
size="small"
|
|
61
|
-
color="
|
|
62
|
-
sx={ { font: 'inherit', minWidth: 'initial' } }
|
|
61
|
+
color="secondary"
|
|
62
|
+
sx={ { font: 'inherit', minWidth: 'initial', textTransform: 'uppercase' } }
|
|
63
63
|
{ ...bindTrigger( popupState ) }
|
|
64
64
|
>
|
|
65
|
-
{ value
|
|
65
|
+
{ value }
|
|
66
66
|
</Button>
|
|
67
67
|
|
|
68
68
|
<Menu MenuListProps={ { dense: true } } { ...bindMenu( popupState ) }>
|
package/src/controls/background-control/background-overlay/background-overlay-repeater-control.tsx
CHANGED
|
@@ -271,7 +271,7 @@ const useImage = ( image: BackgroundImageOverlay ) => {
|
|
|
271
271
|
const { data: attachment } = useWpMediaAttachment( imageSrc.id?.value || null );
|
|
272
272
|
|
|
273
273
|
if ( imageSrc.id ) {
|
|
274
|
-
const imageFileTypeExtension = attachment?.
|
|
274
|
+
const imageFileTypeExtension = getFileExtensionFromFilename( attachment?.filename );
|
|
275
275
|
imageTitle = `${ attachment?.title }${ imageFileTypeExtension }` || null;
|
|
276
276
|
imageUrl = attachment?.url || null;
|
|
277
277
|
} else if ( imageSrc.url ) {
|
|
@@ -282,6 +282,17 @@ const useImage = ( image: BackgroundImageOverlay ) => {
|
|
|
282
282
|
return { imageTitle, imageUrl };
|
|
283
283
|
};
|
|
284
284
|
|
|
285
|
+
const getFileExtensionFromFilename = ( filename?: string ) => {
|
|
286
|
+
if ( ! filename ) {
|
|
287
|
+
return '';
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// get the substring after the last . in the filename
|
|
291
|
+
const extension = filename.substring( filename.lastIndexOf( '.' ) + 1 );
|
|
292
|
+
|
|
293
|
+
return `.${ extension }`;
|
|
294
|
+
};
|
|
295
|
+
|
|
285
296
|
const getGradientValue = ( value: BackgroundOverlayItemPropValue ) => {
|
|
286
297
|
const gradient = value.value;
|
|
287
298
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { useMemo, useState } from 'react';
|
|
3
|
-
import {
|
|
2
|
+
import { type PropsWithChildren, useMemo, useState } from 'react';
|
|
3
|
+
import { getLinkInLinkRestriction, type LinkInLinkRestriction, selectElement } from '@elementor/editor-elements';
|
|
4
4
|
import {
|
|
5
5
|
booleanPropTypeUtil,
|
|
6
6
|
linkPropTypeUtil,
|
|
@@ -9,10 +9,11 @@ import {
|
|
|
9
9
|
stringPropTypeUtil,
|
|
10
10
|
urlPropTypeUtil,
|
|
11
11
|
} from '@elementor/editor-props';
|
|
12
|
+
import { InfoTipCard } from '@elementor/editor-ui';
|
|
12
13
|
import { type HttpResponse, httpService } from '@elementor/http';
|
|
13
|
-
import { MinusIcon, PlusIcon } from '@elementor/icons';
|
|
14
|
+
import { AlertTriangleIcon, MinusIcon, PlusIcon } from '@elementor/icons';
|
|
14
15
|
import { useSessionStorage } from '@elementor/session';
|
|
15
|
-
import { Collapse, Divider, Grid, IconButton, Stack, Switch } from '@elementor/ui';
|
|
16
|
+
import { Box, Collapse, Divider, Grid, IconButton, Infotip, Stack, Switch } from '@elementor/ui';
|
|
16
17
|
import { debounce } from '@elementor/utils';
|
|
17
18
|
import { __ } from '@wordpress/i18n';
|
|
18
19
|
|
|
@@ -49,11 +50,15 @@ type LinkSessionValue = {
|
|
|
49
50
|
type Response = HttpResponse< { value: FlatOption[] | CategorizedOption[] } >;
|
|
50
51
|
|
|
51
52
|
const SIZE = 'tiny';
|
|
53
|
+
const learnMoreButton = {
|
|
54
|
+
label: __( 'Learn More', 'elementor' ),
|
|
55
|
+
href: 'https://go.elementor.com/element-link-inside-link-infotip',
|
|
56
|
+
};
|
|
52
57
|
|
|
53
58
|
export const LinkControl = createControl( ( props: Props ) => {
|
|
54
59
|
const { value, path, setValue, ...propContext } = useBoundProp( linkPropTypeUtil );
|
|
55
60
|
const [ linkSessionValue, setLinkSessionValue ] = useSessionStorage< LinkSessionValue >( path.join( '/' ) );
|
|
56
|
-
const [
|
|
61
|
+
const [ isActive, setIsActive ] = useState( !! value );
|
|
57
62
|
|
|
58
63
|
const {
|
|
59
64
|
allowCustomValues,
|
|
@@ -63,20 +68,22 @@ export const LinkControl = createControl( ( props: Props ) => {
|
|
|
63
68
|
context: { elementId },
|
|
64
69
|
} = props || {};
|
|
65
70
|
|
|
71
|
+
const [ linkInLinkRestriction, setLinkInLinkRestriction ] = useState( getLinkInLinkRestriction( elementId ) );
|
|
66
72
|
const [ options, setOptions ] = useState< FlatOption[] | CategorizedOption[] >(
|
|
67
73
|
generateFirstLoadedOption( value )
|
|
68
74
|
);
|
|
75
|
+
const shouldDisableAddingLink = ! isActive && linkInLinkRestriction.shouldRestrict;
|
|
69
76
|
|
|
70
77
|
const onEnabledChange = () => {
|
|
71
|
-
|
|
78
|
+
setLinkInLinkRestriction( getLinkInLinkRestriction( elementId ) );
|
|
72
79
|
|
|
73
|
-
if ( shouldRestrict && !
|
|
80
|
+
if ( linkInLinkRestriction.shouldRestrict && ! isActive ) {
|
|
74
81
|
return;
|
|
75
82
|
}
|
|
76
83
|
|
|
77
|
-
|
|
78
|
-
setValue(
|
|
79
|
-
setLinkSessionValue( { value, meta: { isEnabled: !
|
|
84
|
+
setIsActive( ( prevState ) => ! prevState );
|
|
85
|
+
setValue( isActive ? null : linkSessionValue?.value ?? null );
|
|
86
|
+
setLinkSessionValue( { value, meta: { isEnabled: ! isActive } } );
|
|
80
87
|
};
|
|
81
88
|
|
|
82
89
|
const onOptionChange = ( newValue: number | null ) => {
|
|
@@ -145,13 +152,16 @@ export const LinkControl = createControl( ( props: Props ) => {
|
|
|
145
152
|
} }
|
|
146
153
|
>
|
|
147
154
|
<ControlFormLabel>{ __( 'Link', 'elementor' ) }</ControlFormLabel>
|
|
148
|
-
<
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
155
|
+
<ConditionalInfoTip isVisible={ ! isActive } linkInLinkRestriction={ linkInLinkRestriction }>
|
|
156
|
+
<ToggleIconControl
|
|
157
|
+
disabled={ shouldDisableAddingLink }
|
|
158
|
+
active={ isActive }
|
|
159
|
+
onIconClick={ onEnabledChange }
|
|
160
|
+
label={ __( 'Toggle link', 'elementor' ) }
|
|
161
|
+
/>
|
|
162
|
+
</ConditionalInfoTip>
|
|
153
163
|
</Stack>
|
|
154
|
-
<Collapse in={
|
|
164
|
+
<Collapse in={ isActive } timeout="auto" unmountOnExit>
|
|
155
165
|
<Stack gap={ 1.5 }>
|
|
156
166
|
<PropKeyProvider bind={ 'destination' }>
|
|
157
167
|
<ControlActions>
|
|
@@ -167,7 +177,7 @@ export const LinkControl = createControl( ( props: Props ) => {
|
|
|
167
177
|
</ControlActions>
|
|
168
178
|
</PropKeyProvider>
|
|
169
179
|
<PropKeyProvider bind={ 'isTargetBlank' }>
|
|
170
|
-
<SwitchControl />
|
|
180
|
+
<SwitchControl disabled={ ! value } />
|
|
171
181
|
</PropKeyProvider>
|
|
172
182
|
</Stack>
|
|
173
183
|
</Collapse>
|
|
@@ -177,34 +187,43 @@ export const LinkControl = createControl( ( props: Props ) => {
|
|
|
177
187
|
} );
|
|
178
188
|
|
|
179
189
|
type ToggleIconControlProps = {
|
|
180
|
-
|
|
190
|
+
disabled: boolean;
|
|
191
|
+
active: boolean;
|
|
181
192
|
onIconClick: () => void;
|
|
182
193
|
label?: string;
|
|
183
194
|
};
|
|
184
195
|
|
|
185
|
-
const ToggleIconControl = ( {
|
|
196
|
+
const ToggleIconControl = ( { disabled, active, onIconClick, label }: ToggleIconControlProps ) => {
|
|
186
197
|
return (
|
|
187
|
-
<IconButton size={ SIZE } onClick={ onIconClick } aria-label={ label }>
|
|
188
|
-
{
|
|
198
|
+
<IconButton size={ SIZE } onClick={ onIconClick } aria-label={ label } disabled={ disabled }>
|
|
199
|
+
{ active ? <MinusIcon fontSize={ SIZE } /> : <PlusIcon fontSize={ SIZE } /> }
|
|
189
200
|
</IconButton>
|
|
190
201
|
);
|
|
191
202
|
};
|
|
192
203
|
|
|
193
204
|
// @TODO Should be refactored in ED-16323
|
|
194
|
-
const SwitchControl = () => {
|
|
205
|
+
const SwitchControl = ( { disabled }: { disabled: boolean } ) => {
|
|
195
206
|
const { value = false, setValue } = useBoundProp( booleanPropTypeUtil );
|
|
196
207
|
|
|
197
208
|
const onClick = () => {
|
|
198
209
|
setValue( ! value );
|
|
199
210
|
};
|
|
200
211
|
|
|
212
|
+
const inputProps = disabled
|
|
213
|
+
? {
|
|
214
|
+
style: {
|
|
215
|
+
opacity: 0,
|
|
216
|
+
},
|
|
217
|
+
}
|
|
218
|
+
: {};
|
|
219
|
+
|
|
201
220
|
return (
|
|
202
221
|
<Grid container alignItems="center" flexWrap="nowrap" justifyContent="space-between">
|
|
203
222
|
<Grid item>
|
|
204
223
|
<ControlFormLabel>{ __( 'Open in a new tab', 'elementor' ) }</ControlFormLabel>
|
|
205
224
|
</Grid>
|
|
206
225
|
<Grid item>
|
|
207
|
-
<Switch checked={ value } onClick={ onClick } />
|
|
226
|
+
<Switch checked={ value } onClick={ onClick } disabled={ disabled } inputProps={ inputProps } />
|
|
208
227
|
</Grid>
|
|
209
228
|
</Grid>
|
|
210
229
|
);
|
|
@@ -248,3 +267,56 @@ function generateFirstLoadedOption( unionValue: LinkPropValue[ 'value' ] | null
|
|
|
248
267
|
]
|
|
249
268
|
: [];
|
|
250
269
|
}
|
|
270
|
+
|
|
271
|
+
interface ConditionalInfoTipType extends PropsWithChildren {
|
|
272
|
+
linkInLinkRestriction: LinkInLinkRestriction;
|
|
273
|
+
isVisible: boolean;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
const ConditionalInfoTip: React.FC< ConditionalInfoTipType > = ( { linkInLinkRestriction, isVisible, children } ) => {
|
|
277
|
+
const { shouldRestrict, reason, elementId } = linkInLinkRestriction;
|
|
278
|
+
|
|
279
|
+
const handleTakeMeClick = () => {
|
|
280
|
+
if ( elementId ) {
|
|
281
|
+
selectElement( elementId );
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
return shouldRestrict && isVisible ? (
|
|
286
|
+
<Infotip
|
|
287
|
+
placement="right"
|
|
288
|
+
content={
|
|
289
|
+
<InfoTipCard
|
|
290
|
+
content={ INFOTIP_CONTENT[ reason ] }
|
|
291
|
+
svgIcon={ <AlertTriangleIcon /> }
|
|
292
|
+
learnMoreButton={ learnMoreButton }
|
|
293
|
+
ctaButton={ {
|
|
294
|
+
label: __( 'Take me there', 'elementor' ),
|
|
295
|
+
onClick: handleTakeMeClick,
|
|
296
|
+
} }
|
|
297
|
+
/>
|
|
298
|
+
}
|
|
299
|
+
>
|
|
300
|
+
<Box>{ children }</Box>
|
|
301
|
+
</Infotip>
|
|
302
|
+
) : (
|
|
303
|
+
<>{ children }</>
|
|
304
|
+
);
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
const INFOTIP_CONTENT = {
|
|
308
|
+
descendant: (
|
|
309
|
+
<>
|
|
310
|
+
{ __( 'To add a link to this container,', 'elementor' ) }
|
|
311
|
+
<br />
|
|
312
|
+
{ __( 'first remove the link from the elements inside of it.', 'elementor' ) }
|
|
313
|
+
</>
|
|
314
|
+
),
|
|
315
|
+
ancestor: (
|
|
316
|
+
<>
|
|
317
|
+
{ __( 'To add a link to this element,', 'elementor' ) }
|
|
318
|
+
<br />
|
|
319
|
+
{ __( 'first remove the link from its parent container.', 'elementor' ) }
|
|
320
|
+
</>
|
|
321
|
+
),
|
|
322
|
+
};
|