@elementor/editor-controls 3.33.0-99 → 3.35.0-325
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/dist/index.d.mts +276 -85
- package/dist/index.d.ts +276 -85
- package/dist/index.js +2491 -1783
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2304 -1592
- package/dist/index.mjs.map +1 -1
- package/package.json +31 -17
- package/src/bound-prop-context/prop-context.tsx +7 -1
- package/src/bound-prop-context/use-bound-prop.ts +19 -5
- package/src/components/autocomplete.tsx +34 -3
- package/src/components/conditional-control-infotip.tsx +64 -0
- package/src/components/{unstable-repeater → control-repeater}/actions/disable-item-action.tsx +2 -2
- package/src/components/{unstable-repeater → control-repeater}/actions/duplicate-item-action.tsx +10 -4
- package/src/components/{unstable-repeater → control-repeater}/actions/remove-item-action.tsx +2 -2
- package/src/components/control-repeater/context/item-context.tsx +8 -0
- package/src/components/{unstable-repeater → control-repeater}/context/repeater-context.tsx +24 -15
- package/src/components/control-repeater/control-repeater.tsx +29 -0
- package/src/components/{unstable-repeater → control-repeater}/index.ts +1 -2
- package/src/components/{unstable-repeater → control-repeater}/items/edit-item-popover.tsx +6 -20
- package/src/components/control-repeater/items/item.tsx +75 -0
- package/src/components/{unstable-repeater → control-repeater}/items/items-container.tsx +8 -13
- package/src/components/{unstable-repeater → control-repeater}/locations.ts +0 -4
- package/src/components/{unstable-repeater → control-repeater}/types.ts +1 -2
- package/src/components/control-toggle-button-group.tsx +79 -69
- package/src/components/enable-unfiltered-modal.tsx +1 -26
- package/src/components/icon-buttons/clear-icon-button.tsx +23 -0
- package/src/components/inline-editor-toolbar.tsx +137 -0
- package/src/components/inline-editor.tsx +111 -0
- package/src/components/item-selector.tsx +10 -4
- package/src/components/{unstable-repeater/header/header.tsx → repeater/repeater-header.tsx} +4 -12
- package/src/components/repeater/repeater-popover.tsx +19 -0
- package/src/components/repeater/repeater-tag.tsx +16 -0
- package/src/components/repeater/repeater.tsx +405 -0
- package/src/components/{sortable.tsx → repeater/sortable.tsx} +1 -1
- package/src/components/size-control/size-input.tsx +20 -14
- package/src/components/size-control/text-field-inner-selection.tsx +15 -2
- package/src/control-adornments/control-adornments-context.tsx +5 -4
- package/src/control-replacements.tsx +12 -47
- package/src/controls/background-control/background-control.tsx +43 -12
- package/src/controls/background-control/background-gradient-color-control.tsx +5 -8
- package/src/controls/background-control/background-overlay/background-image-overlay/background-image-overlay-position.tsx +18 -13
- package/src/controls/background-control/background-overlay/background-overlay-repeater-control.tsx +25 -16
- package/src/controls/box-shadow-repeater-control.tsx +38 -21
- package/src/controls/color-control.tsx +3 -1
- package/src/controls/date-time-control.tsx +108 -0
- package/src/controls/filter-control/drop-shadow/drop-shadow-item-content.tsx +1 -0
- package/src/controls/filter-control/drop-shadow/drop-shadow-item-label.tsx +10 -6
- package/src/controls/filter-control/filter-content.tsx +1 -1
- package/src/controls/filter-control/filter-repeater-control.tsx +24 -21
- package/src/controls/filter-control/single-size/single-size-item-content.tsx +1 -1
- package/src/controls/filter-control/single-size/single-size-item-label.tsx +2 -1
- package/src/controls/font-family-control/font-family-control.tsx +66 -55
- package/src/controls/html-tag-control.tsx +90 -0
- package/src/controls/image-media-control.tsx +2 -2
- package/src/controls/inline-editing-control.tsx +18 -0
- package/src/controls/key-value-control.tsx +8 -2
- package/src/controls/link-control.tsx +23 -123
- package/src/controls/query-control.tsx +168 -0
- package/src/controls/repeatable-control.tsx +62 -27
- package/src/controls/select-control-wrapper.tsx +57 -0
- package/src/controls/select-control.tsx +9 -5
- package/src/controls/selection-size-control.tsx +13 -2
- package/src/controls/size-control.tsx +43 -25
- package/src/controls/svg-media-control.tsx +33 -10
- package/src/controls/text-area-control.tsx +5 -1
- package/src/controls/text-control.tsx +5 -0
- package/src/controls/toggle-control.tsx +11 -2
- package/src/controls/transform-control/functions/axis-row.tsx +1 -0
- package/src/controls/transform-control/transform-icon.tsx +2 -2
- package/src/controls/transform-control/transform-label.tsx +15 -32
- package/src/controls/transform-control/transform-repeater-control.tsx +42 -36
- package/src/controls/transform-control/{transform-base-control.tsx → transform-settings-control.tsx} +2 -2
- package/src/controls/transform-control/use-transform-tabs-history.tsx +1 -1
- package/src/controls/transition-control/data.ts +16 -1
- package/src/controls/transition-control/trainsition-events.ts +2 -2
- package/src/controls/transition-control/transition-repeater-control.tsx +137 -13
- package/src/controls/transition-control/transition-selector.tsx +37 -14
- package/src/controls/url-control.tsx +21 -16
- package/src/create-control.tsx +3 -2
- package/src/hooks/use-filtered-items-list.ts +3 -2
- package/src/hooks/use-repeatable-control-context.ts +3 -0
- package/src/hooks/use-sync-external-state.tsx +0 -1
- package/src/index.ts +21 -5
- package/src/utils/convert-toggle-options-to-atomic.tsx +33 -0
- package/src/utils/escape-html-attr.ts +11 -0
- package/src/components/css-code-editor/css-editor.styles.ts +0 -52
- package/src/components/css-code-editor/css-editor.tsx +0 -142
- package/src/components/css-code-editor/css-validation.ts +0 -75
- package/src/components/css-code-editor/resize-handle.tsx +0 -55
- package/src/components/css-code-editor/visual-content-change-protection.ts +0 -69
- package/src/components/repeater.tsx +0 -343
- package/src/components/unstable-repeater/items/item.tsx +0 -77
- package/src/components/unstable-repeater/unstable-repeater.tsx +0 -26
- /package/src/components/{unstable-repeater → control-repeater}/actions/tooltip-add-item-action.tsx +0 -0
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
|
|
3
|
-
import { SortableItem, SortableProvider } from '../../sortable';
|
|
3
|
+
import { SortableItem, SortableProvider } from '../../repeater/sortable';
|
|
4
|
+
import { ItemContext } from '../context/item-context';
|
|
4
5
|
import { useRepeaterContext } from '../context/repeater-context';
|
|
5
|
-
import { type Item, type
|
|
6
|
+
import { type Item, type RepeatablePropValue } from '../types';
|
|
6
7
|
|
|
7
8
|
export const ItemsContainer = < T extends RepeatablePropValue >( {
|
|
8
|
-
itemTemplate,
|
|
9
9
|
isSortable = true,
|
|
10
10
|
children,
|
|
11
|
-
}: React.PropsWithChildren< {
|
|
11
|
+
}: React.PropsWithChildren< {
|
|
12
|
+
isSortable?: boolean;
|
|
13
|
+
} > ) => {
|
|
12
14
|
const { items, setItems } = useRepeaterContext();
|
|
13
15
|
const keys = items.map( ( { key } ) => key );
|
|
14
16
|
|
|
15
|
-
if ( !
|
|
17
|
+
if ( ! children ) {
|
|
16
18
|
return null;
|
|
17
19
|
}
|
|
18
20
|
|
|
@@ -34,14 +36,7 @@ export const ItemsContainer = < T extends RepeatablePropValue >( {
|
|
|
34
36
|
|
|
35
37
|
return (
|
|
36
38
|
<SortableItem id={ key } key={ `sortable-${ key }` } disabled={ ! isSortable }>
|
|
37
|
-
{
|
|
38
|
-
? React.cloneElement( itemTemplate, {
|
|
39
|
-
key,
|
|
40
|
-
value,
|
|
41
|
-
index,
|
|
42
|
-
children,
|
|
43
|
-
} )
|
|
44
|
-
: null }
|
|
39
|
+
<ItemContext.Provider value={ { index, value } }>{ children }</ItemContext.Provider>
|
|
45
40
|
</SortableItem>
|
|
46
41
|
);
|
|
47
42
|
} ) }
|
|
@@ -10,10 +10,6 @@ export const { Slot: RepeaterItemLabelSlot, inject: injectIntoRepeaterItemLabel
|
|
|
10
10
|
value: PropValue;
|
|
11
11
|
} >();
|
|
12
12
|
|
|
13
|
-
export const { Slot: RepeaterHeaderActionsSlot, inject: injectIntoRepeaterHeaderActions } = createLocation< {
|
|
14
|
-
value: PropValue;
|
|
15
|
-
} >();
|
|
16
|
-
|
|
17
13
|
export const { Slot: RepeaterItemActionsSlot, inject: injectIntoRepeaterItemActions } = createLocation< {
|
|
18
14
|
index: number;
|
|
19
15
|
} >();
|
|
@@ -11,6 +11,5 @@ export type RepeatablePropValue = TransformablePropValue< PropKey, PropValue >;
|
|
|
11
11
|
export type ItemProps< T > = {
|
|
12
12
|
Label: React.ComponentType< { value: T } >;
|
|
13
13
|
Icon: React.ComponentType< { value: T } >;
|
|
14
|
-
|
|
15
|
-
index?: number;
|
|
14
|
+
actions?: React.ReactNode;
|
|
16
15
|
};
|
|
@@ -80,76 +80,79 @@ type Props< TValue > = {
|
|
|
80
80
|
onChange: ( value: ExclusiveValue< TValue > ) => void;
|
|
81
81
|
}
|
|
82
82
|
);
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
const fixedItems = shouldSliceItems ? items.slice( 0, maxItems - 1 ) : items;
|
|
99
|
-
|
|
100
|
-
const theme = useTheme();
|
|
101
|
-
const isRtl = 'rtl' === theme.direction;
|
|
102
|
-
|
|
103
|
-
const handleChange = (
|
|
104
|
-
_: React.MouseEvent< HTMLElement >,
|
|
105
|
-
newValue: typeof exclusive extends true ? ExclusiveValue< TValue > : NonExclusiveValue< TValue >
|
|
83
|
+
export const ToggleButtonGroupUi = React.forwardRef(
|
|
84
|
+
< TValue, >(
|
|
85
|
+
{
|
|
86
|
+
justify = 'end',
|
|
87
|
+
size = 'tiny',
|
|
88
|
+
value,
|
|
89
|
+
onChange,
|
|
90
|
+
items,
|
|
91
|
+
maxItems,
|
|
92
|
+
exclusive = false,
|
|
93
|
+
fullWidth = false,
|
|
94
|
+
disabled,
|
|
95
|
+
placeholder,
|
|
96
|
+
}: Props< TValue >,
|
|
97
|
+
ref: React.Ref< HTMLDivElement >
|
|
106
98
|
) => {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
const
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
99
|
+
const shouldSliceItems = exclusive && maxItems !== undefined && items.length > maxItems;
|
|
100
|
+
const menuItems = shouldSliceItems ? items.slice( maxItems - 1 ) : [];
|
|
101
|
+
const fixedItems = shouldSliceItems ? items.slice( 0, maxItems - 1 ) : items;
|
|
102
|
+
|
|
103
|
+
const theme = useTheme();
|
|
104
|
+
const isRtl = 'rtl' === theme.direction;
|
|
105
|
+
|
|
106
|
+
const handleChange = (
|
|
107
|
+
_: React.MouseEvent< HTMLElement >,
|
|
108
|
+
newValue: typeof exclusive extends true ? ExclusiveValue< TValue > : NonExclusiveValue< TValue >
|
|
109
|
+
) => {
|
|
110
|
+
onChange( newValue as never );
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const getGridTemplateColumns = useMemo( () => {
|
|
114
|
+
const isOffLimits = menuItems?.length;
|
|
115
|
+
const itemsCount = isOffLimits ? fixedItems.length + 1 : fixedItems.length;
|
|
116
|
+
const templateColumnsSuffix = isOffLimits ? 'auto' : '';
|
|
117
|
+
return `repeat(${ itemsCount }, minmax(0, 25%)) ${ templateColumnsSuffix }`;
|
|
118
|
+
}, [ menuItems?.length, fixedItems.length ] );
|
|
119
|
+
|
|
120
|
+
const shouldShowExclusivePlaceholder = exclusive && ( value === null || value === undefined || value === '' );
|
|
121
|
+
|
|
122
|
+
const nonExclusiveSelectedValues =
|
|
123
|
+
! exclusive && Array.isArray( value )
|
|
124
|
+
? value
|
|
125
|
+
.map( ( v ) => ( typeof v === 'string' ? v : '' ) )
|
|
126
|
+
.join( ' ' )
|
|
127
|
+
.trim()
|
|
128
|
+
.split( /\s+/ )
|
|
129
|
+
.filter( Boolean )
|
|
130
|
+
: [];
|
|
131
|
+
|
|
132
|
+
const shouldShowNonExclusivePlaceholder = ! exclusive && nonExclusiveSelectedValues.length === 0;
|
|
133
|
+
|
|
134
|
+
const getPlaceholderArray = ( placeholderValue: TValue | TValue[] | undefined ): string[] => {
|
|
135
|
+
if ( Array.isArray( placeholderValue ) ) {
|
|
136
|
+
return placeholderValue.flatMap( ( p ) => {
|
|
137
|
+
if ( typeof p === 'string' ) {
|
|
138
|
+
return p.trim().split( /\s+/ ).filter( Boolean );
|
|
139
|
+
}
|
|
140
|
+
return [];
|
|
141
|
+
} );
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if ( typeof placeholderValue === 'string' ) {
|
|
145
|
+
return placeholderValue.trim().split( /\s+/ ).filter( Boolean );
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return [];
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
const placeholderArray = getPlaceholderArray( placeholder );
|
|
152
|
+
|
|
153
|
+
return (
|
|
152
154
|
<StyledToggleButtonGroup
|
|
155
|
+
ref={ ref }
|
|
153
156
|
justify={ justify }
|
|
154
157
|
value={ value }
|
|
155
158
|
onChange={ handleChange }
|
|
@@ -197,6 +200,14 @@ export const ControlToggleButtonGroup = < TValue, >( {
|
|
|
197
200
|
/>
|
|
198
201
|
) }
|
|
199
202
|
</StyledToggleButtonGroup>
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
) as < TValue >( props: Props< TValue > & { ref?: React.Ref< HTMLDivElement > } ) => React.ReactElement;
|
|
206
|
+
|
|
207
|
+
export const ControlToggleButtonGroup = < TValue, >( props: Props< TValue > ) => {
|
|
208
|
+
return (
|
|
209
|
+
<ControlActions>
|
|
210
|
+
<ToggleButtonGroupUi { ...props } />
|
|
200
211
|
</ControlActions>
|
|
201
212
|
);
|
|
202
213
|
};
|
|
@@ -247,7 +258,6 @@ const SplitButtonGroup = < TValue, >( {
|
|
|
247
258
|
ev.preventDefault();
|
|
248
259
|
onMenuItemClick( previewButton.value );
|
|
249
260
|
} }
|
|
250
|
-
ref={ menuButtonRef }
|
|
251
261
|
>
|
|
252
262
|
{ previewButton.renderContent( { size } ) }
|
|
253
263
|
</ToggleButton>
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { useState } from 'react';
|
|
3
|
-
import { useCurrentUserCapabilities } from '@elementor/editor-current-user';
|
|
4
3
|
import {
|
|
5
4
|
Button,
|
|
6
5
|
CircularProgress,
|
|
@@ -34,11 +33,6 @@ const ADMIN_CONTENT_TEXT = __(
|
|
|
34
33
|
'Before you enable unfiltered files upload, note that such files include a security risk. Elementor does run a process to remove possible malicious code, but there is still risk involved when using such files.',
|
|
35
34
|
'elementor'
|
|
36
35
|
);
|
|
37
|
-
const NON_ADMIN_TITLE_TEXT = __( "Sorry, you can't upload that file yet", 'elementor' );
|
|
38
|
-
const NON_ADMIN_CONTENT_TEXT = __(
|
|
39
|
-
'This is because this file type may pose a security risk. To upload them anyway, ask the site administrator to enable unfiltered file uploads.',
|
|
40
|
-
'elementor'
|
|
41
|
-
);
|
|
42
36
|
|
|
43
37
|
const ADMIN_FAILED_CONTENT_TEXT_PT1 = __( 'Failed to enable unfiltered files upload.', 'elementor' );
|
|
44
38
|
|
|
@@ -51,9 +45,7 @@ const WAIT_FOR_CLOSE_TIMEOUT_MS = 300;
|
|
|
51
45
|
|
|
52
46
|
export const EnableUnfilteredModal = ( props: EnableUnfilteredModalProps ) => {
|
|
53
47
|
const { mutateAsync, isPending } = useUpdateUnfilteredFilesUpload();
|
|
54
|
-
const { canUser } = useCurrentUserCapabilities();
|
|
55
48
|
const [ isError, setIsError ] = useState( false );
|
|
56
|
-
const canManageOptions = canUser( 'manage_options' );
|
|
57
49
|
|
|
58
50
|
const onClose = ( enabled: boolean ) => {
|
|
59
51
|
props.onClose( enabled );
|
|
@@ -75,7 +67,7 @@ export const EnableUnfilteredModal = ( props: EnableUnfilteredModalProps ) => {
|
|
|
75
67
|
|
|
76
68
|
const dialogProps = { ...props, isPending, handleEnable, isError, onClose };
|
|
77
69
|
|
|
78
|
-
return
|
|
70
|
+
return <AdminDialog { ...dialogProps } />;
|
|
79
71
|
};
|
|
80
72
|
|
|
81
73
|
const AdminDialog = ( { open, onClose, handleEnable, isPending, isError }: LocalModalProps ) => (
|
|
@@ -111,20 +103,3 @@ const AdminDialog = ( { open, onClose, handleEnable, isPending, isError }: Local
|
|
|
111
103
|
</DialogActions>
|
|
112
104
|
</Dialog>
|
|
113
105
|
);
|
|
114
|
-
|
|
115
|
-
const NonAdminDialog = ( { open, onClose }: LocalModalProps ) => (
|
|
116
|
-
<Dialog open={ open } maxWidth={ 'sm' } onClose={ () => onClose( false ) }>
|
|
117
|
-
<DialogHeader logo={ false }>
|
|
118
|
-
<DialogTitle>{ NON_ADMIN_TITLE_TEXT }</DialogTitle>
|
|
119
|
-
</DialogHeader>
|
|
120
|
-
<Divider />
|
|
121
|
-
<DialogContent>
|
|
122
|
-
<DialogContentText>{ NON_ADMIN_CONTENT_TEXT }</DialogContentText>
|
|
123
|
-
</DialogContent>
|
|
124
|
-
<DialogActions>
|
|
125
|
-
<Button size={ 'medium' } onClick={ () => onClose( false ) } variant="contained" color="primary">
|
|
126
|
-
{ __( 'Got it', 'elementor' ) }
|
|
127
|
-
</Button>
|
|
128
|
-
</DialogActions>
|
|
129
|
-
</Dialog>
|
|
130
|
-
);
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { BrushBigIcon } from '@elementor/icons';
|
|
3
|
+
import { IconButton, styled, Tooltip } from '@elementor/ui';
|
|
4
|
+
|
|
5
|
+
type ClearIconButtonProps = {
|
|
6
|
+
onClick?: () => void;
|
|
7
|
+
tooltipText: React.ReactNode;
|
|
8
|
+
disabled?: boolean;
|
|
9
|
+
size?: 'tiny' | 'small' | 'medium' | 'large';
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const CustomIconButton = styled( IconButton )( ( { theme } ) => ( {
|
|
13
|
+
width: theme.spacing( 2.5 ),
|
|
14
|
+
height: theme.spacing( 2.5 ),
|
|
15
|
+
} ) );
|
|
16
|
+
|
|
17
|
+
export const ClearIconButton = ( { tooltipText, onClick, disabled, size = 'tiny' }: ClearIconButtonProps ) => (
|
|
18
|
+
<Tooltip title={ tooltipText } placement="top" disableInteractive>
|
|
19
|
+
<CustomIconButton aria-label={ tooltipText } size={ size } onClick={ onClick } disabled={ disabled }>
|
|
20
|
+
<BrushBigIcon fontSize={ size } />
|
|
21
|
+
</CustomIconButton>
|
|
22
|
+
</Tooltip>
|
|
23
|
+
);
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { useMemo } from 'react';
|
|
3
|
+
import {
|
|
4
|
+
BoldIcon,
|
|
5
|
+
ItalicIcon,
|
|
6
|
+
MinusIcon,
|
|
7
|
+
StrikethroughIcon,
|
|
8
|
+
SubscriptIcon,
|
|
9
|
+
SuperscriptIcon,
|
|
10
|
+
UnderlineIcon,
|
|
11
|
+
} from '@elementor/icons';
|
|
12
|
+
import { Box, IconButton, ToggleButton, ToggleButtonGroup, Tooltip } from '@elementor/ui';
|
|
13
|
+
import { type Editor, useEditorState } from '@tiptap/react';
|
|
14
|
+
import { __ } from '@wordpress/i18n';
|
|
15
|
+
|
|
16
|
+
type InlineEditorToolbarProps = {
|
|
17
|
+
editor: Editor;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const toolbarButtons = {
|
|
21
|
+
clear: {
|
|
22
|
+
label: __( 'Clear', 'elementor' ),
|
|
23
|
+
icon: <MinusIcon fontSize="tiny" />,
|
|
24
|
+
action: 'clear',
|
|
25
|
+
method: ( editor: Editor ) => {
|
|
26
|
+
editor.chain().focus().clearNodes().unsetAllMarks().run();
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
bold: {
|
|
30
|
+
label: __( 'Bold', 'elementor' ),
|
|
31
|
+
icon: <BoldIcon fontSize="tiny" />,
|
|
32
|
+
action: 'bold',
|
|
33
|
+
method: ( editor: Editor ) => {
|
|
34
|
+
editor.chain().focus().toggleBold().run();
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
italic: {
|
|
38
|
+
label: __( 'Italic', 'elementor' ),
|
|
39
|
+
icon: <ItalicIcon fontSize="tiny" />,
|
|
40
|
+
action: 'italic',
|
|
41
|
+
method: ( editor: Editor ) => {
|
|
42
|
+
editor.chain().focus().toggleItalic().run();
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
underline: {
|
|
46
|
+
label: __( 'Underline', 'elementor' ),
|
|
47
|
+
icon: <UnderlineIcon fontSize="tiny" />,
|
|
48
|
+
action: 'underline',
|
|
49
|
+
method: ( editor: Editor ) => {
|
|
50
|
+
editor.chain().focus().toggleUnderline().run();
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
strike: {
|
|
54
|
+
label: __( 'Strikethrough', 'elementor' ),
|
|
55
|
+
icon: <StrikethroughIcon fontSize="tiny" />,
|
|
56
|
+
action: 'strike',
|
|
57
|
+
method: ( editor: Editor ) => {
|
|
58
|
+
editor.chain().focus().toggleStrike().run();
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
superscript: {
|
|
62
|
+
label: __( 'Superscript', 'elementor' ),
|
|
63
|
+
icon: <SuperscriptIcon fontSize="tiny" />,
|
|
64
|
+
action: 'superscript',
|
|
65
|
+
method: ( editor: Editor ) => {
|
|
66
|
+
editor.chain().focus().toggleSuperscript().run();
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
subscript: {
|
|
70
|
+
label: __( 'Subscript', 'elementor' ),
|
|
71
|
+
icon: <SubscriptIcon fontSize="tiny" />,
|
|
72
|
+
action: 'subscript',
|
|
73
|
+
method: ( editor: Editor ) => {
|
|
74
|
+
editor.chain().focus().toggleSubscript().run();
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
} as const;
|
|
78
|
+
|
|
79
|
+
type ToolbarButtonKeys = keyof typeof toolbarButtons;
|
|
80
|
+
|
|
81
|
+
type FormatAction = Omit< ToolbarButtonKeys, 'clear' >;
|
|
82
|
+
|
|
83
|
+
const { clear: clearButton, ...formatButtons } = toolbarButtons;
|
|
84
|
+
|
|
85
|
+
const possibleFormats: FormatAction[] = Object.keys( formatButtons ) as FormatAction[];
|
|
86
|
+
|
|
87
|
+
export const InlineEditorToolbar = ( { editor }: InlineEditorToolbarProps ) => {
|
|
88
|
+
const editorState = useEditorState( {
|
|
89
|
+
editor,
|
|
90
|
+
selector: ( ctx ) => possibleFormats.filter( ( format ) => ctx.editor.isActive( format ) ),
|
|
91
|
+
} );
|
|
92
|
+
|
|
93
|
+
const formatButtonsList = useMemo( () => Object.values( formatButtons ), [] );
|
|
94
|
+
|
|
95
|
+
return (
|
|
96
|
+
<Box
|
|
97
|
+
sx={ {
|
|
98
|
+
position: 'absolute',
|
|
99
|
+
top: -40,
|
|
100
|
+
display: 'inline-flex',
|
|
101
|
+
gap: 0.5,
|
|
102
|
+
padding: 0.5,
|
|
103
|
+
borderRadius: 1,
|
|
104
|
+
backgroundColor: 'background.paper',
|
|
105
|
+
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.2)',
|
|
106
|
+
alignItems: 'center',
|
|
107
|
+
} }
|
|
108
|
+
>
|
|
109
|
+
<Tooltip title={ clearButton.label } placement="top">
|
|
110
|
+
<IconButton aria-label={ clearButton.label } onClick={ () => clearButton.method( editor ) } size="tiny">
|
|
111
|
+
{ clearButton.icon }
|
|
112
|
+
</IconButton>
|
|
113
|
+
</Tooltip>
|
|
114
|
+
<ToggleButtonGroup
|
|
115
|
+
value={ editorState }
|
|
116
|
+
size="tiny"
|
|
117
|
+
sx={ {
|
|
118
|
+
display: 'flex',
|
|
119
|
+
gap: 0.5,
|
|
120
|
+
} }
|
|
121
|
+
>
|
|
122
|
+
{ formatButtonsList.map( ( button ) => (
|
|
123
|
+
<Tooltip title={ button.label } key={ button.action } placement="top">
|
|
124
|
+
<ToggleButton
|
|
125
|
+
value={ button.action }
|
|
126
|
+
aria-label={ button.label }
|
|
127
|
+
size="tiny"
|
|
128
|
+
onClick={ () => button.method( editor ) }
|
|
129
|
+
>
|
|
130
|
+
{ button.icon }
|
|
131
|
+
</ToggleButton>
|
|
132
|
+
</Tooltip>
|
|
133
|
+
) ) }
|
|
134
|
+
</ToggleButtonGroup>
|
|
135
|
+
</Box>
|
|
136
|
+
);
|
|
137
|
+
};
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { type DependencyList, type ForwardedRef, useEffect, useRef } from 'react';
|
|
3
|
+
import { Box, type SxProps, type Theme } from '@elementor/ui';
|
|
4
|
+
import Bold from '@tiptap/extension-bold';
|
|
5
|
+
import Document from '@tiptap/extension-document';
|
|
6
|
+
import HardBreak from '@tiptap/extension-hard-break';
|
|
7
|
+
import Italic from '@tiptap/extension-italic';
|
|
8
|
+
import Strike from '@tiptap/extension-strike';
|
|
9
|
+
import Subscript from '@tiptap/extension-subscript';
|
|
10
|
+
import Superscript from '@tiptap/extension-superscript';
|
|
11
|
+
import Text from '@tiptap/extension-text';
|
|
12
|
+
import Underline from '@tiptap/extension-underline';
|
|
13
|
+
import { type AnyExtension, EditorContent, useEditor } from '@tiptap/react';
|
|
14
|
+
|
|
15
|
+
import { InlineEditorToolbar } from './inline-editor-toolbar';
|
|
16
|
+
|
|
17
|
+
type InlineEditorProps = {
|
|
18
|
+
value: string;
|
|
19
|
+
setValue: ( value: string ) => void;
|
|
20
|
+
attributes?: Record< string, string >;
|
|
21
|
+
sx?: SxProps< Theme >;
|
|
22
|
+
showToolbar?: boolean;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const useOnUpdate = ( callback: () => void, dependencies: DependencyList ): void => {
|
|
26
|
+
const hasMounted = useRef( false );
|
|
27
|
+
|
|
28
|
+
useEffect( () => {
|
|
29
|
+
if ( hasMounted.current ) {
|
|
30
|
+
callback();
|
|
31
|
+
} else {
|
|
32
|
+
hasMounted.current = true;
|
|
33
|
+
}
|
|
34
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
35
|
+
}, dependencies );
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export const InlineEditor = React.forwardRef(
|
|
39
|
+
(
|
|
40
|
+
{ value, setValue, attributes = {}, showToolbar = false, sx }: InlineEditorProps,
|
|
41
|
+
ref: ForwardedRef< HTMLDivElement >
|
|
42
|
+
) => {
|
|
43
|
+
const editor = useEditor( {
|
|
44
|
+
extensions: [
|
|
45
|
+
Document.extend( {
|
|
46
|
+
content: 'inline*',
|
|
47
|
+
} ),
|
|
48
|
+
Text,
|
|
49
|
+
Bold,
|
|
50
|
+
Italic,
|
|
51
|
+
Strike,
|
|
52
|
+
Underline,
|
|
53
|
+
Superscript,
|
|
54
|
+
Subscript,
|
|
55
|
+
HardBreak.extend( {
|
|
56
|
+
addKeyboardShortcuts() {
|
|
57
|
+
return {
|
|
58
|
+
Enter: () => this.editor.commands.setHardBreak(),
|
|
59
|
+
};
|
|
60
|
+
},
|
|
61
|
+
} ),
|
|
62
|
+
] as AnyExtension[],
|
|
63
|
+
content: value,
|
|
64
|
+
onUpdate: ( { editor: updatedEditor } ) => setValue( updatedEditor.getHTML() ),
|
|
65
|
+
} );
|
|
66
|
+
|
|
67
|
+
useOnUpdate( () => {
|
|
68
|
+
if ( ! editor ) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const currentContent = editor.getHTML();
|
|
73
|
+
|
|
74
|
+
if ( currentContent !== value ) {
|
|
75
|
+
editor.commands.setContent( value, { emitUpdate: false } );
|
|
76
|
+
}
|
|
77
|
+
}, [ editor, value ] );
|
|
78
|
+
|
|
79
|
+
return (
|
|
80
|
+
<Box
|
|
81
|
+
ref={ ref }
|
|
82
|
+
sx={ {
|
|
83
|
+
p: 0.8,
|
|
84
|
+
border: '1px solid',
|
|
85
|
+
borderColor: 'grey.200',
|
|
86
|
+
borderRadius: '8px',
|
|
87
|
+
transition: 'border-color .2s ease, box-shadow .2s ease',
|
|
88
|
+
'&:hover': {
|
|
89
|
+
borderColor: 'black',
|
|
90
|
+
},
|
|
91
|
+
'&:focus-within': {
|
|
92
|
+
borderColor: 'black',
|
|
93
|
+
boxShadow: '0 0 0 1px black',
|
|
94
|
+
},
|
|
95
|
+
'& .ProseMirror:focus': {
|
|
96
|
+
outline: 'none',
|
|
97
|
+
},
|
|
98
|
+
'& .ProseMirror': {
|
|
99
|
+
minHeight: '70px',
|
|
100
|
+
fontSize: '12px',
|
|
101
|
+
},
|
|
102
|
+
...sx,
|
|
103
|
+
} }
|
|
104
|
+
{ ...attributes }
|
|
105
|
+
>
|
|
106
|
+
{ showToolbar && <InlineEditorToolbar editor={ editor } /> }
|
|
107
|
+
<EditorContent editor={ editor } />
|
|
108
|
+
</Box>
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { useCallback, useEffect, useState } from 'react';
|
|
3
|
-
import { PopoverBody, PopoverHeader, PopoverMenuList,
|
|
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';
|
|
6
6
|
import { __ } from '@wordpress/i18n';
|
|
@@ -22,6 +22,8 @@ type ItemSelectorProps = {
|
|
|
22
22
|
itemStyle?: ( item: SelectableItem ) => React.CSSProperties;
|
|
23
23
|
onDebounce?: ( name: string ) => void;
|
|
24
24
|
icon: React.ElementType< { fontSize: string } >;
|
|
25
|
+
disabledItems?: string[];
|
|
26
|
+
id?: string;
|
|
25
27
|
};
|
|
26
28
|
|
|
27
29
|
export const ItemSelector = ( {
|
|
@@ -34,10 +36,12 @@ export const ItemSelector = ( {
|
|
|
34
36
|
itemStyle = () => ( {} ),
|
|
35
37
|
onDebounce = () => {},
|
|
36
38
|
icon,
|
|
39
|
+
disabledItems,
|
|
40
|
+
id = 'item-selector',
|
|
37
41
|
}: ItemSelectorProps ) => {
|
|
38
42
|
const [ searchValue, setSearchValue ] = useState( '' );
|
|
39
43
|
|
|
40
|
-
const filteredItemsList = useFilteredItemsList( itemsList, searchValue );
|
|
44
|
+
const filteredItemsList = useFilteredItemsList( itemsList, searchValue, disabledItems );
|
|
41
45
|
|
|
42
46
|
const IconComponent = icon;
|
|
43
47
|
|
|
@@ -51,12 +55,13 @@ export const ItemSelector = ( {
|
|
|
51
55
|
};
|
|
52
56
|
|
|
53
57
|
return (
|
|
54
|
-
<PopoverBody width={ sectionWidth }>
|
|
58
|
+
<PopoverBody width={ sectionWidth } id={ id }>
|
|
55
59
|
<PopoverHeader title={ title } onClose={ handleClose } icon={ <IconComponent fontSize="tiny" /> } />
|
|
56
|
-
<
|
|
60
|
+
<SearchField
|
|
57
61
|
value={ searchValue }
|
|
58
62
|
onSearch={ handleSearch }
|
|
59
63
|
placeholder={ __( 'Search', 'elementor' ) }
|
|
64
|
+
id={ id + '-search' }
|
|
60
65
|
/>
|
|
61
66
|
|
|
62
67
|
<Divider />
|
|
@@ -128,6 +133,7 @@ type ItemListProps = {
|
|
|
128
133
|
selectedItem: string | null;
|
|
129
134
|
itemStyle?: ( item: SelectableItem ) => React.CSSProperties;
|
|
130
135
|
onDebounce?: ( name: string ) => void;
|
|
136
|
+
disabledItems?: string[];
|
|
131
137
|
};
|
|
132
138
|
|
|
133
139
|
const ItemList = ( {
|
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
+
import { forwardRef } from 'react';
|
|
2
3
|
import { Box, Stack, Typography } from '@elementor/ui';
|
|
3
4
|
|
|
4
|
-
import {
|
|
5
|
-
import { ControlAdornments } from '../../../control-adornments/control-adornments';
|
|
6
|
-
import { SlotChildren } from '../../../control-replacements';
|
|
7
|
-
import { TooltipAddItemAction } from '../actions/tooltip-add-item-action';
|
|
8
|
-
import { RepeaterHeaderActionsSlot } from '../locations';
|
|
5
|
+
import { ControlAdornments } from '../../control-adornments/control-adornments';
|
|
9
6
|
|
|
10
|
-
export const
|
|
7
|
+
export const RepeaterHeader = forwardRef(
|
|
11
8
|
(
|
|
12
9
|
{
|
|
13
10
|
label,
|
|
@@ -19,8 +16,6 @@ export const Header = React.forwardRef(
|
|
|
19
16
|
} >,
|
|
20
17
|
ref
|
|
21
18
|
) => {
|
|
22
|
-
const { value } = useBoundProp();
|
|
23
|
-
|
|
24
19
|
return (
|
|
25
20
|
<Stack
|
|
26
21
|
direction="row"
|
|
@@ -35,10 +30,7 @@ export const Header = React.forwardRef(
|
|
|
35
30
|
</Typography>
|
|
36
31
|
<Adornment />
|
|
37
32
|
</Box>
|
|
38
|
-
|
|
39
|
-
<SlotChildren whitelist={ [ TooltipAddItemAction ] as React.FC[] } sorted>
|
|
40
|
-
{ children }
|
|
41
|
-
</SlotChildren>
|
|
33
|
+
{ children }
|
|
42
34
|
</Stack>
|
|
43
35
|
);
|
|
44
36
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { Popover, type PopoverProps } from '@elementor/ui';
|
|
3
|
+
|
|
4
|
+
export const RepeaterPopover = ( { children, width, ...props }: PopoverProps & { width?: number } ) => {
|
|
5
|
+
return (
|
|
6
|
+
<Popover
|
|
7
|
+
disablePortal
|
|
8
|
+
anchorOrigin={ { vertical: 'bottom', horizontal: 'left' } }
|
|
9
|
+
slotProps={ {
|
|
10
|
+
paper: {
|
|
11
|
+
sx: { marginBlockStart: 0.5, width, overflow: 'visible' },
|
|
12
|
+
},
|
|
13
|
+
} }
|
|
14
|
+
{ ...props }
|
|
15
|
+
>
|
|
16
|
+
{ children }
|
|
17
|
+
</Popover>
|
|
18
|
+
);
|
|
19
|
+
};
|