@elementor/editor-controls 1.5.0 → 3.32.0-21
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 +0 -22
- package/dist/index.d.mts +95 -25
- package/dist/index.d.ts +95 -25
- package/dist/index.js +2045 -1041
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1962 -964
- package/dist/index.mjs.map +1 -1
- package/package.json +18 -18
- package/src/components/control-toggle-button-group.tsx +78 -14
- package/src/components/floating-bar.tsx +45 -0
- package/src/components/{font-family-selector.tsx → item-selector.tsx} +62 -50
- package/src/components/repeater.tsx +1 -1
- package/src/components/restricted-link-infotip.tsx +76 -0
- package/src/components/size-control/size-input.tsx +8 -7
- package/src/components/size-control/text-field-inner-selection.tsx +60 -14
- package/src/components/text-field-popover.tsx +30 -7
- package/src/components/unstable-repeater/actions/add-item-action.tsx +50 -0
- package/src/components/unstable-repeater/actions/disable-item-action.tsx +39 -0
- package/src/components/unstable-repeater/actions/duplicate-item-action.tsx +32 -0
- package/src/components/unstable-repeater/actions/remove-item-action.tsx +27 -0
- package/src/components/unstable-repeater/context/repeater-context.tsx +137 -0
- package/src/components/unstable-repeater/header/header.tsx +23 -0
- package/src/components/unstable-repeater/index.ts +5 -0
- package/src/components/unstable-repeater/items/edit-item-popover.tsx +28 -0
- package/src/components/unstable-repeater/items/item.tsx +71 -0
- package/src/components/unstable-repeater/items/items-container.tsx +49 -0
- package/src/components/unstable-repeater/items/use-popover.tsx +26 -0
- package/src/{locations.ts → components/unstable-repeater/locations.ts} +9 -1
- package/src/components/unstable-repeater/types.ts +26 -0
- package/src/components/unstable-repeater/unstable-repeater.tsx +24 -0
- package/src/control-actions/control-actions.tsx +3 -20
- package/src/control-replacements.tsx +41 -0
- package/src/controls/background-control/background-control.tsx +1 -8
- package/src/controls/background-control/background-overlay/background-overlay-repeater-control.tsx +17 -16
- package/src/controls/equal-unequal-sizes-control.tsx +2 -9
- package/src/controls/filter-control/drop-shadow-item-content.tsx +4 -6
- package/src/controls/filter-control/drop-shadow-item-label.tsx +2 -2
- package/src/controls/filter-repeater-control.tsx +149 -110
- package/src/controls/font-family-control/font-family-control.tsx +22 -10
- package/src/controls/key-value-control.tsx +9 -6
- package/src/controls/link-control.tsx +8 -91
- package/src/controls/linked-dimensions-control.tsx +3 -16
- package/src/controls/number-control.tsx +10 -1
- package/src/controls/position-control.tsx +4 -16
- package/src/controls/repeatable-control.tsx +8 -5
- package/src/controls/select-control.tsx +7 -2
- package/src/controls/selection-size-control.tsx +74 -0
- package/src/controls/size-control.tsx +181 -126
- package/src/controls/stroke-control.tsx +2 -2
- package/src/controls/toggle-control.tsx +3 -2
- package/src/controls/transform-control/functions/axis-row.tsx +4 -2
- package/src/controls/transform-control/functions/move.tsx +2 -1
- package/src/controls/transform-control/functions/rotate.tsx +48 -0
- package/src/controls/transform-control/functions/scale-axis-row.tsx +32 -0
- package/src/controls/transform-control/functions/scale.tsx +45 -0
- package/src/controls/transform-control/functions/skew.tsx +43 -0
- package/src/controls/transform-control/transform-content.tsx +60 -23
- package/src/controls/transform-control/transform-icon.tsx +10 -2
- package/src/controls/transform-control/transform-label.tsx +39 -2
- package/src/controls/transform-control/transform-repeater-control.tsx +2 -10
- package/src/controls/transform-control/types.ts +58 -0
- package/src/controls/transform-control/use-transform-tabs-history.tsx +107 -0
- package/src/controls/transition-control/data.ts +34 -0
- package/src/controls/transition-control/transition-repeater-control.tsx +63 -0
- package/src/controls/transition-control/transition-selector.tsx +88 -0
- package/src/controls/unstable-transform-control/unstable-transform-repeater-control.tsx +35 -0
- package/src/hooks/use-filtered-items-list.ts +24 -0
- package/src/hooks/use-size-extended-options.ts +1 -6
- package/src/index.ts +13 -3
- package/src/utils/size-control.ts +12 -6
- package/src/hooks/use-filtered-font-families.ts +0 -24
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": "
|
|
4
|
+
"version": "3.32.0-21",
|
|
5
5
|
"private": false,
|
|
6
6
|
"author": "Elementor Team",
|
|
7
7
|
"homepage": "https://elementor.com/",
|
|
@@ -19,11 +19,11 @@
|
|
|
19
19
|
},
|
|
20
20
|
"repository": {
|
|
21
21
|
"type": "git",
|
|
22
|
-
"url": "git+https://github.com/elementor/elementor
|
|
22
|
+
"url": "git+https://github.com/elementor/elementor.git",
|
|
23
23
|
"directory": "packages/libs/editor-controls"
|
|
24
24
|
},
|
|
25
25
|
"bugs": {
|
|
26
|
-
"url": "https://github.com/elementor/elementor
|
|
26
|
+
"url": "https://github.com/elementor/elementor/issues"
|
|
27
27
|
},
|
|
28
28
|
"publishConfig": {
|
|
29
29
|
"access": "public"
|
|
@@ -40,21 +40,21 @@
|
|
|
40
40
|
"dev": "tsup --config=../../tsup.dev.ts"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@elementor/editor-current-user": "
|
|
44
|
-
"@elementor/editor-elements": "
|
|
45
|
-
"@elementor/editor-props": "
|
|
46
|
-
"@elementor/editor-responsive": "
|
|
47
|
-
"@elementor/editor-ui": "
|
|
48
|
-
"@elementor/editor-v1-adapters": "
|
|
49
|
-
"@elementor/env": "
|
|
50
|
-
"@elementor/http-client": "
|
|
51
|
-
"@elementor/icons": "1.
|
|
52
|
-
"@elementor/locations": "
|
|
53
|
-
"@elementor/query": "
|
|
54
|
-
"@elementor/session": "
|
|
55
|
-
"@elementor/ui": "1.36.
|
|
56
|
-
"@elementor/utils": "
|
|
57
|
-
"@elementor/wp-media": "
|
|
43
|
+
"@elementor/editor-current-user": "3.32.0-21",
|
|
44
|
+
"@elementor/editor-elements": "3.32.0-21",
|
|
45
|
+
"@elementor/editor-props": "3.32.0-21",
|
|
46
|
+
"@elementor/editor-responsive": "3.32.0-21",
|
|
47
|
+
"@elementor/editor-ui": "3.32.0-21",
|
|
48
|
+
"@elementor/editor-v1-adapters": "3.32.0-21",
|
|
49
|
+
"@elementor/env": "3.32.0-21",
|
|
50
|
+
"@elementor/http-client": "3.32.0-21",
|
|
51
|
+
"@elementor/icons": "^1.51.1",
|
|
52
|
+
"@elementor/locations": "3.32.0-21",
|
|
53
|
+
"@elementor/query": "3.32.0-21",
|
|
54
|
+
"@elementor/session": "3.32.0-21",
|
|
55
|
+
"@elementor/ui": "1.36.2",
|
|
56
|
+
"@elementor/utils": "3.32.0-21",
|
|
57
|
+
"@elementor/wp-media": "3.32.0-21",
|
|
58
58
|
"@wordpress/i18n": "^5.13.0"
|
|
59
59
|
},
|
|
60
60
|
"devDependencies": {
|
|
@@ -42,6 +42,21 @@ const StyledToggleButtonGroup = styled( ToggleButtonGroup )`
|
|
|
42
42
|
}
|
|
43
43
|
`;
|
|
44
44
|
|
|
45
|
+
const StyledToggleButton = styled( ToggleButton, {
|
|
46
|
+
shouldForwardProp: ( prop ) => prop !== 'isPlaceholder',
|
|
47
|
+
} )< { isPlaceholder: boolean } >`
|
|
48
|
+
${ ( { theme, isPlaceholder } ) =>
|
|
49
|
+
isPlaceholder &&
|
|
50
|
+
`
|
|
51
|
+
color: ${ theme.palette.text.tertiary };
|
|
52
|
+
background-color: ${ theme.palette.mode === 'dark' ? 'rgba(255,255,255,0.04)' : 'rgba(0,0,0,0.02)' };
|
|
53
|
+
|
|
54
|
+
&:hover {
|
|
55
|
+
background-color: ${ theme.palette.mode === 'dark' ? 'rgba(255,255,255,0.08)' : 'rgba(0,0,0,0.04)' };
|
|
56
|
+
}
|
|
57
|
+
` }
|
|
58
|
+
`;
|
|
59
|
+
|
|
45
60
|
type ExclusiveValue< TValue > = TValue;
|
|
46
61
|
type NonExclusiveValue< TValue > = TValue[];
|
|
47
62
|
|
|
@@ -52,6 +67,7 @@ type Props< TValue > = {
|
|
|
52
67
|
items: ToggleButtonGroupItem< TValue | null >[];
|
|
53
68
|
maxItems?: number;
|
|
54
69
|
fullWidth?: boolean;
|
|
70
|
+
placeholder?: TValue | TValue[];
|
|
55
71
|
} & (
|
|
56
72
|
| {
|
|
57
73
|
exclusive?: false;
|
|
@@ -75,12 +91,15 @@ export const ControlToggleButtonGroup = < TValue, >( {
|
|
|
75
91
|
exclusive = false,
|
|
76
92
|
fullWidth = false,
|
|
77
93
|
disabled,
|
|
94
|
+
placeholder,
|
|
78
95
|
}: Props< TValue > ) => {
|
|
79
96
|
const shouldSliceItems = exclusive && maxItems !== undefined && items.length > maxItems;
|
|
80
97
|
const menuItems = shouldSliceItems ? items.slice( maxItems - 1 ) : [];
|
|
81
98
|
const fixedItems = shouldSliceItems ? items.slice( 0, maxItems - 1 ) : items;
|
|
82
99
|
|
|
83
|
-
const
|
|
100
|
+
const theme = useTheme();
|
|
101
|
+
const isRtl = 'rtl' === theme.direction;
|
|
102
|
+
|
|
84
103
|
const handleChange = (
|
|
85
104
|
_: React.MouseEvent< HTMLElement >,
|
|
86
105
|
newValue: typeof exclusive extends true ? ExclusiveValue< TValue > : NonExclusiveValue< TValue >
|
|
@@ -92,10 +111,42 @@ export const ControlToggleButtonGroup = < TValue, >( {
|
|
|
92
111
|
const isOffLimits = menuItems?.length;
|
|
93
112
|
const itemsCount = isOffLimits ? fixedItems.length + 1 : fixedItems.length;
|
|
94
113
|
const templateColumnsSuffix = isOffLimits ? 'auto' : '';
|
|
95
|
-
|
|
96
114
|
return `repeat(${ itemsCount }, minmax(0, 25%)) ${ templateColumnsSuffix }`;
|
|
97
115
|
}, [ menuItems?.length, fixedItems.length ] );
|
|
98
116
|
|
|
117
|
+
const shouldShowExclusivePlaceholder = exclusive && ( value === null || value === undefined || value === '' );
|
|
118
|
+
|
|
119
|
+
const nonExclusiveSelectedValues =
|
|
120
|
+
! exclusive && Array.isArray( value )
|
|
121
|
+
? value
|
|
122
|
+
.map( ( v ) => ( typeof v === 'string' ? v : '' ) )
|
|
123
|
+
.join( ' ' )
|
|
124
|
+
.trim()
|
|
125
|
+
.split( /\s+/ )
|
|
126
|
+
.filter( Boolean )
|
|
127
|
+
: [];
|
|
128
|
+
|
|
129
|
+
const shouldShowNonExclusivePlaceholder = ! exclusive && nonExclusiveSelectedValues.length === 0;
|
|
130
|
+
|
|
131
|
+
const getPlaceholderArray = ( placeholderValue: TValue | TValue[] | undefined ): string[] => {
|
|
132
|
+
if ( Array.isArray( placeholderValue ) ) {
|
|
133
|
+
return placeholderValue.flatMap( ( p ) => {
|
|
134
|
+
if ( typeof p === 'string' ) {
|
|
135
|
+
return p.trim().split( /\s+/ ).filter( Boolean );
|
|
136
|
+
}
|
|
137
|
+
return [];
|
|
138
|
+
} );
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if ( typeof placeholderValue === 'string' ) {
|
|
142
|
+
return placeholderValue.trim().split( /\s+/ ).filter( Boolean );
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return [];
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
const placeholderArray = getPlaceholderArray( placeholder );
|
|
149
|
+
|
|
99
150
|
return (
|
|
100
151
|
<ControlActions>
|
|
101
152
|
<StyledToggleButtonGroup
|
|
@@ -111,17 +162,30 @@ export const ControlToggleButtonGroup = < TValue, >( {
|
|
|
111
162
|
width: `100%`,
|
|
112
163
|
} }
|
|
113
164
|
>
|
|
114
|
-
{ fixedItems.map( ( { label, value: buttonValue, renderContent: Content, showTooltip } ) =>
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
165
|
+
{ fixedItems.map( ( { label, value: buttonValue, renderContent: Content, showTooltip } ) => {
|
|
166
|
+
const isPlaceholder =
|
|
167
|
+
placeholderArray.length > 0 &&
|
|
168
|
+
placeholderArray.includes( buttonValue as string ) &&
|
|
169
|
+
( shouldShowExclusivePlaceholder || shouldShowNonExclusivePlaceholder );
|
|
170
|
+
|
|
171
|
+
return (
|
|
172
|
+
<ConditionalTooltip
|
|
173
|
+
key={ buttonValue as string }
|
|
174
|
+
label={ label }
|
|
175
|
+
showTooltip={ showTooltip || false }
|
|
176
|
+
>
|
|
177
|
+
<StyledToggleButton
|
|
178
|
+
value={ buttonValue }
|
|
179
|
+
aria-label={ label }
|
|
180
|
+
size={ size }
|
|
181
|
+
fullWidth={ fullWidth }
|
|
182
|
+
isPlaceholder={ isPlaceholder }
|
|
183
|
+
>
|
|
184
|
+
<Content size={ size } />
|
|
185
|
+
</StyledToggleButton>
|
|
186
|
+
</ConditionalTooltip>
|
|
187
|
+
);
|
|
188
|
+
} ) }
|
|
125
189
|
|
|
126
190
|
{ menuItems.length && exclusive && (
|
|
127
191
|
<SplitButtonGroup
|
|
@@ -194,7 +258,7 @@ const SplitButtonGroup = < TValue, >( {
|
|
|
194
258
|
aria-pressed={ undefined }
|
|
195
259
|
onClick={ onMenuToggle }
|
|
196
260
|
ref={ menuButtonRef }
|
|
197
|
-
value=
|
|
261
|
+
value="__chevron-icon-button__"
|
|
198
262
|
>
|
|
199
263
|
<ChevronDownIcon fontSize={ size } />
|
|
200
264
|
</ToggleButton>
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { createContext, type PropsWithChildren, type ReactElement, useContext, useState } from 'react';
|
|
3
|
+
import { styled, UnstableFloatingActionBar } from '@elementor/ui';
|
|
4
|
+
|
|
5
|
+
// CSS hack to hide empty floating bars.
|
|
6
|
+
const FloatingBarContainer = styled( 'span' )`
|
|
7
|
+
display: contents;
|
|
8
|
+
|
|
9
|
+
.MuiFloatingActionBar-popper:has( .MuiFloatingActionBar-actions:empty ) {
|
|
10
|
+
display: none;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.MuiFloatingActionBar-popper {
|
|
14
|
+
z-index: 1000;
|
|
15
|
+
}
|
|
16
|
+
`;
|
|
17
|
+
|
|
18
|
+
const FloatingActionsContext = createContext< null | {
|
|
19
|
+
open: boolean;
|
|
20
|
+
setOpen: React.Dispatch< React.SetStateAction< boolean > >;
|
|
21
|
+
} >( null );
|
|
22
|
+
|
|
23
|
+
export function FloatingActionsBar( { actions, children }: PropsWithChildren< { actions: ReactElement[] } > ) {
|
|
24
|
+
const [ open, setOpen ] = useState< boolean >( false );
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<FloatingActionsContext.Provider value={ { open, setOpen } }>
|
|
28
|
+
<FloatingBarContainer>
|
|
29
|
+
<UnstableFloatingActionBar actions={ actions } open={ open || undefined }>
|
|
30
|
+
{ children as ReactElement }
|
|
31
|
+
</UnstableFloatingActionBar>
|
|
32
|
+
</FloatingBarContainer>
|
|
33
|
+
</FloatingActionsContext.Provider>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function useFloatingActionsBar() {
|
|
38
|
+
const context = useContext( FloatingActionsContext );
|
|
39
|
+
|
|
40
|
+
if ( ! context ) {
|
|
41
|
+
throw new Error( 'useFloatingActions must be used within a FloatingActionsBar' );
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return context;
|
|
45
|
+
}
|
|
@@ -1,35 +1,45 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { useEffect, useState } from 'react';
|
|
2
|
+
import { useCallback, useEffect, useState } from 'react';
|
|
3
3
|
import { PopoverBody, PopoverHeader, PopoverMenuList, PopoverSearch } from '@elementor/editor-ui';
|
|
4
|
-
import { TextIcon } from '@elementor/icons';
|
|
5
4
|
import { Box, Divider, Link, Stack, Typography } from '@elementor/ui';
|
|
6
5
|
import { debounce } from '@elementor/utils';
|
|
7
6
|
import { __ } from '@wordpress/i18n';
|
|
8
7
|
|
|
9
|
-
import {
|
|
10
|
-
import { type FontCategory } from '../controls/font-family-control/font-family-control';
|
|
11
|
-
import { type FontListItem, useFilteredFontFamilies } from '../hooks/use-filtered-font-families';
|
|
8
|
+
import { type SelectableItem, useFilteredItemsList } from '../hooks/use-filtered-items-list';
|
|
12
9
|
|
|
13
|
-
|
|
10
|
+
export type Category = {
|
|
11
|
+
label: string;
|
|
12
|
+
items: string[];
|
|
13
|
+
};
|
|
14
14
|
|
|
15
|
-
type
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
type ItemSelectorProps = {
|
|
16
|
+
itemsList: Category[];
|
|
17
|
+
selectedItem: string | null;
|
|
18
|
+
onItemChange: ( item: string ) => void;
|
|
19
19
|
onClose: () => void;
|
|
20
20
|
sectionWidth: number;
|
|
21
|
+
title: string;
|
|
22
|
+
itemStyle?: ( item: SelectableItem ) => React.CSSProperties;
|
|
23
|
+
onDebounce?: ( name: string ) => void;
|
|
24
|
+
icon: React.ElementType< { fontSize: string } >;
|
|
21
25
|
};
|
|
22
26
|
|
|
23
|
-
export const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
+
export const ItemSelector = ( {
|
|
28
|
+
itemsList,
|
|
29
|
+
selectedItem,
|
|
30
|
+
onItemChange,
|
|
27
31
|
onClose,
|
|
28
32
|
sectionWidth,
|
|
29
|
-
|
|
33
|
+
title,
|
|
34
|
+
itemStyle = () => ( {} ),
|
|
35
|
+
onDebounce = () => {},
|
|
36
|
+
icon,
|
|
37
|
+
}: ItemSelectorProps ) => {
|
|
30
38
|
const [ searchValue, setSearchValue ] = useState( '' );
|
|
31
39
|
|
|
32
|
-
const
|
|
40
|
+
const filteredItemsList = useFilteredItemsList( itemsList, searchValue );
|
|
41
|
+
|
|
42
|
+
const IconComponent = icon;
|
|
33
43
|
|
|
34
44
|
const handleSearch = ( value: string ) => {
|
|
35
45
|
setSearchValue( value );
|
|
@@ -42,12 +52,7 @@ export const FontFamilySelector = ( {
|
|
|
42
52
|
|
|
43
53
|
return (
|
|
44
54
|
<PopoverBody width={ sectionWidth }>
|
|
45
|
-
<PopoverHeader
|
|
46
|
-
title={ __( 'Font Family', 'elementor' ) }
|
|
47
|
-
onClose={ handleClose }
|
|
48
|
-
icon={ <TextIcon fontSize={ SIZE } /> }
|
|
49
|
-
/>
|
|
50
|
-
|
|
55
|
+
<PopoverHeader title={ title } onClose={ handleClose } icon={ <IconComponent fontSize="tiny" /> } />
|
|
51
56
|
<PopoverSearch
|
|
52
57
|
value={ searchValue }
|
|
53
58
|
onSearch={ handleSearch }
|
|
@@ -56,12 +61,14 @@ export const FontFamilySelector = ( {
|
|
|
56
61
|
|
|
57
62
|
<Divider />
|
|
58
63
|
|
|
59
|
-
{
|
|
60
|
-
<
|
|
61
|
-
|
|
62
|
-
|
|
64
|
+
{ filteredItemsList.length > 0 ? (
|
|
65
|
+
<ItemList
|
|
66
|
+
itemListItems={ filteredItemsList }
|
|
67
|
+
setSelectedItem={ onItemChange }
|
|
63
68
|
handleClose={ handleClose }
|
|
64
|
-
|
|
69
|
+
selectedItem={ selectedItem }
|
|
70
|
+
itemStyle={ itemStyle }
|
|
71
|
+
onDebounce={ onDebounce }
|
|
65
72
|
/>
|
|
66
73
|
) : (
|
|
67
74
|
<Stack
|
|
@@ -70,9 +77,9 @@ export const FontFamilySelector = ( {
|
|
|
70
77
|
height="100%"
|
|
71
78
|
p={ 2.5 }
|
|
72
79
|
gap={ 1.5 }
|
|
73
|
-
overflow=
|
|
80
|
+
overflow="hidden"
|
|
74
81
|
>
|
|
75
|
-
<
|
|
82
|
+
<IconComponent fontSize="large" />
|
|
76
83
|
<Box sx={ { maxWidth: 160, overflow: 'hidden' } }>
|
|
77
84
|
<Typography align="center" variant="subtitle2" color="text.secondary">
|
|
78
85
|
{ __( 'Sorry, nothing matched', 'elementor' ) }
|
|
@@ -80,11 +87,7 @@ export const FontFamilySelector = ( {
|
|
|
80
87
|
<Typography
|
|
81
88
|
variant="subtitle2"
|
|
82
89
|
color="text.secondary"
|
|
83
|
-
sx={ {
|
|
84
|
-
display: 'flex',
|
|
85
|
-
width: '100%',
|
|
86
|
-
justifyContent: 'center',
|
|
87
|
-
} }
|
|
90
|
+
sx={ { display: 'flex', width: '100%', justifyContent: 'center' } }
|
|
88
91
|
>
|
|
89
92
|
<span>“</span>
|
|
90
93
|
<span style={ { maxWidth: '80%', overflow: 'hidden', textOverflow: 'ellipsis' } }>
|
|
@@ -115,42 +118,51 @@ export const FontFamilySelector = ( {
|
|
|
115
118
|
);
|
|
116
119
|
};
|
|
117
120
|
|
|
118
|
-
type
|
|
119
|
-
|
|
120
|
-
|
|
121
|
+
type ItemListProps = {
|
|
122
|
+
itemListItems: SelectableItem[];
|
|
123
|
+
setSelectedItem: ( item: string ) => void;
|
|
121
124
|
handleClose: () => void;
|
|
122
|
-
|
|
125
|
+
selectedItem: string | null;
|
|
126
|
+
itemStyle?: ( item: SelectableItem ) => React.CSSProperties;
|
|
127
|
+
onDebounce?: ( name: string ) => void;
|
|
123
128
|
};
|
|
124
129
|
|
|
125
|
-
const
|
|
126
|
-
|
|
130
|
+
const ItemList = ( {
|
|
131
|
+
itemListItems,
|
|
132
|
+
setSelectedItem,
|
|
133
|
+
handleClose,
|
|
134
|
+
selectedItem,
|
|
135
|
+
itemStyle = () => ( {} ),
|
|
136
|
+
onDebounce = () => {},
|
|
137
|
+
}: ItemListProps ) => {
|
|
138
|
+
const selectedItemFound = itemListItems.find( ( item ) => item.value === selectedItem );
|
|
127
139
|
|
|
128
140
|
const debouncedVirtualizeChange = useDebounce( ( { getVirtualIndexes }: { getVirtualIndexes: () => number[] } ) => {
|
|
129
141
|
getVirtualIndexes().forEach( ( index ) => {
|
|
130
|
-
const item =
|
|
131
|
-
if ( item && item.type === '
|
|
132
|
-
|
|
142
|
+
const item = itemListItems[ index ];
|
|
143
|
+
if ( item && item.type === 'item' ) {
|
|
144
|
+
onDebounce( item.value );
|
|
133
145
|
}
|
|
134
146
|
} );
|
|
135
147
|
}, 100 );
|
|
136
148
|
|
|
149
|
+
const memoizedItemStyle = useCallback( ( item: SelectableItem ) => itemStyle( item ), [ itemStyle ] );
|
|
150
|
+
|
|
137
151
|
return (
|
|
138
152
|
<PopoverMenuList
|
|
139
|
-
items={
|
|
140
|
-
selectedValue={
|
|
153
|
+
items={ itemListItems }
|
|
154
|
+
selectedValue={ selectedItemFound?.value }
|
|
141
155
|
onChange={ debouncedVirtualizeChange }
|
|
142
|
-
onSelect={
|
|
156
|
+
onSelect={ setSelectedItem }
|
|
143
157
|
onClose={ handleClose }
|
|
144
|
-
itemStyle={
|
|
145
|
-
data-testid="
|
|
158
|
+
itemStyle={ memoizedItemStyle }
|
|
159
|
+
data-testid="item-list"
|
|
146
160
|
/>
|
|
147
161
|
);
|
|
148
162
|
};
|
|
149
163
|
|
|
150
164
|
const useDebounce = < TArgs extends unknown[] >( fn: ( ...args: TArgs ) => void, delay: number ) => {
|
|
151
165
|
const [ debouncedFn ] = useState( () => debounce( fn, delay ) );
|
|
152
|
-
|
|
153
166
|
useEffect( () => () => debouncedFn.cancel(), [ debouncedFn ] );
|
|
154
|
-
|
|
155
167
|
return debouncedFn;
|
|
156
168
|
};
|
|
@@ -19,9 +19,9 @@ import { __ } from '@wordpress/i18n';
|
|
|
19
19
|
|
|
20
20
|
import { ControlAdornments } from '../control-adornments/control-adornments';
|
|
21
21
|
import { useSyncExternalState } from '../hooks/use-sync-external-state';
|
|
22
|
-
import { RepeaterItemIconSlot, RepeaterItemLabelSlot } from '../locations';
|
|
23
22
|
import { SectionContent } from './section-content';
|
|
24
23
|
import { SortableItem, SortableProvider } from './sortable';
|
|
24
|
+
import { RepeaterItemIconSlot, RepeaterItemLabelSlot } from './unstable-repeater/locations';
|
|
25
25
|
|
|
26
26
|
const SIZE = 'tiny';
|
|
27
27
|
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { type PropsWithChildren } from 'react';
|
|
3
|
+
import { type LinkInLinkRestriction, selectElement } from '@elementor/editor-elements';
|
|
4
|
+
import { InfoCircleFilledIcon } from '@elementor/icons';
|
|
5
|
+
import { Alert, AlertAction, AlertTitle, Box, Infotip, Link } from '@elementor/ui';
|
|
6
|
+
import { __ } from '@wordpress/i18n';
|
|
7
|
+
|
|
8
|
+
const learnMoreButton = {
|
|
9
|
+
label: __( 'Learn More', 'elementor' ),
|
|
10
|
+
href: 'https://go.elementor.com/element-link-inside-link-infotip',
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const INFOTIP_CONTENT = {
|
|
14
|
+
descendant: __(
|
|
15
|
+
'To add a link to this element, first remove the link from the elements inside of it.',
|
|
16
|
+
'elementor'
|
|
17
|
+
),
|
|
18
|
+
ancestor: __( 'To add a link to this element, first remove the link from its parent container.', 'elementor' ),
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
type RestrictedLinkInfotipProps = PropsWithChildren< {
|
|
22
|
+
linkInLinkRestriction: LinkInLinkRestriction;
|
|
23
|
+
isVisible: boolean;
|
|
24
|
+
} >;
|
|
25
|
+
|
|
26
|
+
export const RestrictedLinkInfotip: React.FC< RestrictedLinkInfotipProps > = ( {
|
|
27
|
+
linkInLinkRestriction,
|
|
28
|
+
isVisible,
|
|
29
|
+
children,
|
|
30
|
+
} ) => {
|
|
31
|
+
const { shouldRestrict, reason, elementId } = linkInLinkRestriction;
|
|
32
|
+
|
|
33
|
+
const handleTakeMeClick = () => {
|
|
34
|
+
if ( elementId ) {
|
|
35
|
+
selectElement( elementId );
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const content = (
|
|
40
|
+
<Alert
|
|
41
|
+
severity="secondary"
|
|
42
|
+
icon={ <InfoCircleFilledIcon /> }
|
|
43
|
+
action={
|
|
44
|
+
<AlertAction
|
|
45
|
+
sx={ { width: 'fit-content' } }
|
|
46
|
+
variant="contained"
|
|
47
|
+
color="secondary"
|
|
48
|
+
onClick={ handleTakeMeClick }
|
|
49
|
+
>
|
|
50
|
+
{ __( 'Take me there', 'elementor' ) }
|
|
51
|
+
</AlertAction>
|
|
52
|
+
}
|
|
53
|
+
>
|
|
54
|
+
<AlertTitle>{ __( 'Nested links', 'elementor' ) }</AlertTitle>
|
|
55
|
+
<Box component="span">
|
|
56
|
+
{ INFOTIP_CONTENT[ reason ?? 'descendant' ] }{ ' ' }
|
|
57
|
+
<Link href={ learnMoreButton.href } target="_blank" color="info.main">
|
|
58
|
+
{ learnMoreButton.label }
|
|
59
|
+
</Link>
|
|
60
|
+
</Box>
|
|
61
|
+
</Alert>
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
return shouldRestrict && isVisible ? (
|
|
65
|
+
<Infotip
|
|
66
|
+
placement="right"
|
|
67
|
+
content={ content }
|
|
68
|
+
color="secondary"
|
|
69
|
+
slotProps={ { popper: { sx: { width: 300 } } } }
|
|
70
|
+
>
|
|
71
|
+
<Box>{ children }</Box>
|
|
72
|
+
</Infotip>
|
|
73
|
+
) : (
|
|
74
|
+
<>{ children }</>
|
|
75
|
+
);
|
|
76
|
+
};
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { useRef } from 'react';
|
|
3
|
-
import {
|
|
3
|
+
import { MathFunctionIcon } from '@elementor/icons';
|
|
4
4
|
import { Box, InputAdornment, type PopupState } from '@elementor/ui';
|
|
5
5
|
|
|
6
6
|
import ControlActions from '../../control-actions/control-actions';
|
|
7
|
-
import { type
|
|
7
|
+
import { type ExtendedOption, isUnitExtendedOption, type Unit } from '../../utils/size-control';
|
|
8
8
|
import { SelectionEndAdornment, TextFieldInnerSelection } from '../size-control/text-field-inner-selection';
|
|
9
9
|
|
|
10
10
|
type SizeInputProps = {
|
|
11
|
-
unit: Unit |
|
|
11
|
+
unit: Unit | ExtendedOption;
|
|
12
12
|
size: number | string;
|
|
13
13
|
placeholder?: string;
|
|
14
14
|
startIcon?: React.ReactNode;
|
|
15
|
-
units: ( Unit |
|
|
15
|
+
units: ( Unit | ExtendedOption )[];
|
|
16
16
|
onBlur?: ( event: React.FocusEvent< HTMLInputElement > ) => void;
|
|
17
17
|
onFocus?: ( event: React.FocusEvent< HTMLInputElement > ) => void;
|
|
18
18
|
onClick?: ( event: React.MouseEvent< HTMLInputElement > ) => void;
|
|
19
|
-
handleUnitChange: ( unit: Unit |
|
|
19
|
+
handleUnitChange: ( unit: Unit | ExtendedOption ) => void;
|
|
20
20
|
handleSizeChange: ( event: React.ChangeEvent< HTMLInputElement > ) => void;
|
|
21
21
|
popupState: PopupState;
|
|
22
22
|
disabled?: boolean;
|
|
@@ -72,6 +72,7 @@ export const SizeInput = ( {
|
|
|
72
72
|
|
|
73
73
|
const inputProps = {
|
|
74
74
|
...popupAttributes,
|
|
75
|
+
readOnly: isUnitExtendedOption( unit ),
|
|
75
76
|
autoComplete: 'off',
|
|
76
77
|
onClick,
|
|
77
78
|
onFocus,
|
|
@@ -87,7 +88,7 @@ export const SizeInput = ( {
|
|
|
87
88
|
onClick={ handleUnitChange }
|
|
88
89
|
value={ unit }
|
|
89
90
|
alternativeOptionLabels={ {
|
|
90
|
-
custom: <
|
|
91
|
+
custom: <MathFunctionIcon fontSize="tiny" />,
|
|
91
92
|
} }
|
|
92
93
|
menuItemsAttributes={
|
|
93
94
|
units.includes( 'custom' )
|
|
@@ -116,8 +117,8 @@ export const SizeInput = ( {
|
|
|
116
117
|
} }
|
|
117
118
|
onKeyUp={ handleKeyUp }
|
|
118
119
|
onBlur={ onBlur }
|
|
119
|
-
shouldBlockInput={ isUnitExtendedOption( unit ) }
|
|
120
120
|
inputProps={ inputProps }
|
|
121
|
+
isPopoverOpen={ popupState.isOpen }
|
|
121
122
|
/>
|
|
122
123
|
</Box>
|
|
123
124
|
</ControlActions>
|