@elementor/editor-components 4.0.0-607 → 4.0.0-609
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.js +2341 -2192
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2814 -2672
- package/dist/index.mjs.map +1 -1
- package/package.json +23 -23
- package/src/components/components-tab/components-item.tsx +84 -215
- package/src/components/components-tab/components-list.tsx +3 -3
- package/src/components/components-tab/components.tsx +1 -2
- package/src/components/instance-editing-panel/empty-state.tsx +2 -2
- package/src/components/instance-editing-panel/instance-editing-panel.tsx +19 -78
- package/src/components/instance-editing-panel/instance-panel-body.tsx +36 -0
- package/src/components/instance-editing-panel/instance-panel-header.tsx +40 -0
- package/src/components/instance-editing-panel/use-instance-panel-data.ts +57 -0
- package/src/extended/components/components-tab/component-item.tsx +180 -0
- package/src/extended/components/components-tab/components.tsx +58 -0
- package/src/extended/components/create-component-form/create-component-form.tsx +1 -1
- package/src/extended/components/instance-editing-panel/instance-editing-panel.tsx +60 -0
- package/src/extended/init.ts +22 -1
- package/src/{store → extended/store}/actions/archive-component.ts +1 -1
- package/src/{store → extended/store}/actions/rename-component.ts +2 -2
- package/src/{utils → extended/utils}/component-name-validation.ts +1 -1
- package/src/extended/utils/replace-element-with-component.ts +1 -1
- /package/src/{components → extended/components}/components-tab/delete-confirmation-dialog.tsx +0 -0
- /package/src/{utils → extended/utils}/component-form-schema.ts +0 -0
- /package/src/{utils → extended/utils}/create-component-model.ts +0 -0
- /package/src/{utils → extended/utils}/get-container-for-new-element.ts +0 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elementor/editor-components",
|
|
3
3
|
"description": "Elementor editor components",
|
|
4
|
-
"version": "4.0.0-
|
|
4
|
+
"version": "4.0.0-609",
|
|
5
5
|
"private": false,
|
|
6
6
|
"author": "Elementor Team",
|
|
7
7
|
"homepage": "https://elementor.com/",
|
|
@@ -40,31 +40,31 @@
|
|
|
40
40
|
"dev": "tsup --config=../../tsup.dev.ts"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@elementor/editor": "4.0.0-
|
|
44
|
-
"@elementor/editor-canvas": "4.0.0-
|
|
45
|
-
"@elementor/editor-controls": "4.0.0-
|
|
46
|
-
"@elementor/editor-documents": "4.0.0-
|
|
47
|
-
"@elementor/editor-editing-panel": "4.0.0-
|
|
48
|
-
"@elementor/editor-elements": "4.0.0-
|
|
49
|
-
"@elementor/editor-elements-panel": "4.0.0-
|
|
50
|
-
"@elementor/editor-mcp": "4.0.0-
|
|
51
|
-
"@elementor/editor-templates": "4.0.0-
|
|
52
|
-
"@elementor/editor-panels": "4.0.0-
|
|
53
|
-
"@elementor/editor-props": "4.0.0-
|
|
54
|
-
"@elementor/editor-styles-repository": "4.0.0-
|
|
55
|
-
"@elementor/editor-ui": "4.0.0-
|
|
56
|
-
"@elementor/editor-v1-adapters": "4.0.0-
|
|
57
|
-
"@elementor/http-client": "4.0.0-
|
|
43
|
+
"@elementor/editor": "4.0.0-609",
|
|
44
|
+
"@elementor/editor-canvas": "4.0.0-609",
|
|
45
|
+
"@elementor/editor-controls": "4.0.0-609",
|
|
46
|
+
"@elementor/editor-documents": "4.0.0-609",
|
|
47
|
+
"@elementor/editor-editing-panel": "4.0.0-609",
|
|
48
|
+
"@elementor/editor-elements": "4.0.0-609",
|
|
49
|
+
"@elementor/editor-elements-panel": "4.0.0-609",
|
|
50
|
+
"@elementor/editor-mcp": "4.0.0-609",
|
|
51
|
+
"@elementor/editor-templates": "4.0.0-609",
|
|
52
|
+
"@elementor/editor-panels": "4.0.0-609",
|
|
53
|
+
"@elementor/editor-props": "4.0.0-609",
|
|
54
|
+
"@elementor/editor-styles-repository": "4.0.0-609",
|
|
55
|
+
"@elementor/editor-ui": "4.0.0-609",
|
|
56
|
+
"@elementor/editor-v1-adapters": "4.0.0-609",
|
|
57
|
+
"@elementor/http-client": "4.0.0-609",
|
|
58
58
|
"@elementor/icons": "^1.68.0",
|
|
59
|
-
"@elementor/events": "4.0.0-
|
|
60
|
-
"@elementor/query": "4.0.0-
|
|
61
|
-
"@elementor/schema": "4.0.0-
|
|
62
|
-
"@elementor/store": "4.0.0-
|
|
59
|
+
"@elementor/events": "4.0.0-609",
|
|
60
|
+
"@elementor/query": "4.0.0-609",
|
|
61
|
+
"@elementor/schema": "4.0.0-609",
|
|
62
|
+
"@elementor/store": "4.0.0-609",
|
|
63
63
|
"@elementor/ui": "1.36.17",
|
|
64
|
-
"@elementor/utils": "4.0.0-
|
|
64
|
+
"@elementor/utils": "4.0.0-609",
|
|
65
65
|
"@wordpress/i18n": "^5.13.0",
|
|
66
|
-
"@elementor/editor-notifications": "4.0.0-
|
|
67
|
-
"@elementor/editor-current-user": "4.0.0-
|
|
66
|
+
"@elementor/editor-notifications": "4.0.0-609",
|
|
67
|
+
"@elementor/editor-current-user": "4.0.0-609"
|
|
68
68
|
},
|
|
69
69
|
"peerDependencies": {
|
|
70
70
|
"react": "^18.3.1",
|
|
@@ -1,230 +1,80 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { ComponentsIcon, DotsVerticalIcon } from '@elementor/icons';
|
|
7
|
-
import {
|
|
8
|
-
bindMenu,
|
|
9
|
-
bindTrigger,
|
|
10
|
-
Box,
|
|
11
|
-
IconButton,
|
|
12
|
-
ListItemButton,
|
|
13
|
-
ListItemIcon,
|
|
14
|
-
Menu,
|
|
15
|
-
Stack,
|
|
16
|
-
styled,
|
|
17
|
-
type Theme,
|
|
18
|
-
Typography,
|
|
19
|
-
usePopupState,
|
|
20
|
-
} from '@elementor/ui';
|
|
21
|
-
import { __ } from '@wordpress/i18n';
|
|
2
|
+
import { forwardRef } from 'react';
|
|
3
|
+
import { EditableField, EllipsisWithTooltip } from '@elementor/editor-ui';
|
|
4
|
+
import { ComponentsIcon } from '@elementor/icons';
|
|
5
|
+
import { Box, ListItemButton, ListItemIcon, styled, type Theme, Typography } from '@elementor/ui';
|
|
22
6
|
|
|
23
|
-
import { useComponentsPermissions } from '../../hooks/use-components-permissions';
|
|
24
|
-
import { archiveComponent } from '../../store/actions/archive-component';
|
|
25
|
-
import { loadComponentsAssets } from '../../store/actions/load-components-assets';
|
|
26
|
-
import { renameComponent } from '../../store/actions/rename-component';
|
|
27
7
|
import { type Component } from '../../types';
|
|
28
|
-
import { validateComponentName } from '../../utils/component-name-validation';
|
|
29
|
-
import { createComponentModel } from '../../utils/create-component-model';
|
|
30
|
-
import { getContainerForNewElement } from '../../utils/get-container-for-new-element';
|
|
31
|
-
import { DeleteConfirmationDialog } from './delete-confirmation-dialog';
|
|
32
8
|
|
|
33
|
-
type ComponentItemProps = {
|
|
9
|
+
export type ComponentItemProps = {
|
|
34
10
|
component: Omit< Component, 'id' > & { id?: number };
|
|
11
|
+
disabled?: boolean;
|
|
12
|
+
draggable?: boolean;
|
|
13
|
+
onDragStart?: React.DragEventHandler;
|
|
14
|
+
onDragEnd?: React.DragEventHandler;
|
|
15
|
+
onClick?: () => void;
|
|
16
|
+
isEditing?: boolean;
|
|
17
|
+
error?: string | null;
|
|
18
|
+
nameSlot?: React.ReactNode;
|
|
19
|
+
endSlot?: React.ReactNode;
|
|
35
20
|
};
|
|
36
21
|
|
|
37
|
-
export const ComponentItem =
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
const handleDeleteClick = () => {
|
|
74
|
-
setIsDeleteDialogOpen( true );
|
|
75
|
-
popupState.close();
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
const handleDeleteConfirm = () => {
|
|
79
|
-
if ( ! component.id ) {
|
|
80
|
-
throw new Error( 'Component ID is required' );
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
setIsDeleteDialogOpen( false );
|
|
84
|
-
archiveComponent( component.id, component.name );
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
const handleDeleteDialogClose = () => {
|
|
88
|
-
setIsDeleteDialogOpen( false );
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
return (
|
|
92
|
-
<Stack>
|
|
93
|
-
<WarningInfotip
|
|
94
|
-
open={ Boolean( error ) }
|
|
95
|
-
text={ error ?? '' }
|
|
96
|
-
placement="bottom"
|
|
97
|
-
width={ itemRef.current?.getBoundingClientRect().width }
|
|
98
|
-
offset={ [ 0, -15 ] }
|
|
22
|
+
export const ComponentItem = forwardRef< HTMLElement, ComponentItemProps >(
|
|
23
|
+
(
|
|
24
|
+
{
|
|
25
|
+
component,
|
|
26
|
+
disabled = true,
|
|
27
|
+
draggable,
|
|
28
|
+
onDragStart,
|
|
29
|
+
onDragEnd,
|
|
30
|
+
onClick,
|
|
31
|
+
isEditing = false,
|
|
32
|
+
error = null,
|
|
33
|
+
nameSlot,
|
|
34
|
+
endSlot,
|
|
35
|
+
...props
|
|
36
|
+
},
|
|
37
|
+
ref
|
|
38
|
+
) => {
|
|
39
|
+
return (
|
|
40
|
+
<ListItemButton
|
|
41
|
+
disabled={ disabled }
|
|
42
|
+
draggable={ draggable }
|
|
43
|
+
onDragStart={ onDragStart }
|
|
44
|
+
onDragEnd={ onDragEnd }
|
|
45
|
+
shape="rounded"
|
|
46
|
+
ref={ ref }
|
|
47
|
+
sx={ {
|
|
48
|
+
border: 'solid 1px',
|
|
49
|
+
borderColor: 'divider',
|
|
50
|
+
py: 0.5,
|
|
51
|
+
px: 1,
|
|
52
|
+
display: 'flex',
|
|
53
|
+
width: '100%',
|
|
54
|
+
alignItems: 'center',
|
|
55
|
+
gap: 1,
|
|
56
|
+
} }
|
|
57
|
+
{ ...props }
|
|
99
58
|
>
|
|
100
|
-
<
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
alignItems: 'center',
|
|
114
|
-
gap: 1,
|
|
115
|
-
} }
|
|
116
|
-
>
|
|
117
|
-
<Box
|
|
118
|
-
display="flex"
|
|
119
|
-
alignItems="center"
|
|
120
|
-
gap={ 1 }
|
|
121
|
-
minWidth={ 0 }
|
|
122
|
-
flexGrow={ 1 }
|
|
123
|
-
onClick={ handleClick }
|
|
124
|
-
>
|
|
125
|
-
<ListItemIcon size="tiny">
|
|
126
|
-
<ComponentsIcon fontSize="tiny" />
|
|
127
|
-
</ListItemIcon>
|
|
128
|
-
<Indicator isActive={ isEditing } isError={ !! error }>
|
|
129
|
-
<Box display="flex" flex={ 1 } minWidth={ 0 } flexGrow={ 1 }>
|
|
130
|
-
{ isEditing ? (
|
|
131
|
-
<EditableField
|
|
132
|
-
ref={ editableRef }
|
|
133
|
-
as={ Typography }
|
|
134
|
-
variant="caption"
|
|
135
|
-
{ ...getEditableProps() }
|
|
136
|
-
/>
|
|
137
|
-
) : (
|
|
138
|
-
<EllipsisWithTooltip
|
|
139
|
-
title={ component.name }
|
|
140
|
-
as={ Typography }
|
|
141
|
-
variant="caption"
|
|
142
|
-
color="text.primary"
|
|
143
|
-
/>
|
|
144
|
-
) }
|
|
145
|
-
</Box>
|
|
146
|
-
</Indicator>
|
|
147
|
-
</Box>
|
|
148
|
-
{ shouldShowActions && (
|
|
149
|
-
<IconButton size="tiny" { ...bindTrigger( popupState ) } aria-label="More actions">
|
|
150
|
-
<DotsVerticalIcon fontSize="tiny" />
|
|
151
|
-
</IconButton>
|
|
152
|
-
) }
|
|
153
|
-
</ListItemButton>
|
|
154
|
-
</WarningInfotip>
|
|
155
|
-
{ shouldShowActions && (
|
|
156
|
-
<Menu
|
|
157
|
-
{ ...bindMenu( popupState ) }
|
|
158
|
-
anchorOrigin={ {
|
|
159
|
-
vertical: 'bottom',
|
|
160
|
-
horizontal: 'right',
|
|
161
|
-
} }
|
|
162
|
-
transformOrigin={ {
|
|
163
|
-
vertical: 'top',
|
|
164
|
-
horizontal: 'right',
|
|
165
|
-
} }
|
|
166
|
-
>
|
|
167
|
-
{ canRename && (
|
|
168
|
-
<MenuListItem
|
|
169
|
-
sx={ { minWidth: '160px' } }
|
|
170
|
-
primaryTypographyProps={ { variant: 'caption', color: 'text.primary' } }
|
|
171
|
-
onClick={ () => {
|
|
172
|
-
popupState.close();
|
|
173
|
-
openEditMode();
|
|
174
|
-
} }
|
|
175
|
-
>
|
|
176
|
-
{ __( 'Rename', 'elementor' ) }
|
|
177
|
-
</MenuListItem>
|
|
178
|
-
) }
|
|
179
|
-
{ canDelete && (
|
|
180
|
-
<MenuListItem
|
|
181
|
-
sx={ { minWidth: '160px' } }
|
|
182
|
-
primaryTypographyProps={ { variant: 'caption', color: 'error.light' } }
|
|
183
|
-
onClick={ handleDeleteClick }
|
|
184
|
-
>
|
|
185
|
-
{ __( 'Delete', 'elementor' ) }
|
|
186
|
-
</MenuListItem>
|
|
187
|
-
) }
|
|
188
|
-
</Menu>
|
|
189
|
-
) }
|
|
190
|
-
<DeleteConfirmationDialog
|
|
191
|
-
open={ isDeleteDialogOpen }
|
|
192
|
-
onClose={ handleDeleteDialogClose }
|
|
193
|
-
onConfirm={ handleDeleteConfirm }
|
|
194
|
-
/>
|
|
195
|
-
</Stack>
|
|
196
|
-
);
|
|
197
|
-
};
|
|
198
|
-
|
|
199
|
-
const addComponentToPage = ( model: DropElementParams[ 'model' ] ) => {
|
|
200
|
-
const { container, options } = getContainerForNewElement();
|
|
201
|
-
|
|
202
|
-
if ( ! container ) {
|
|
203
|
-
throw new Error( `Can't find container to drop new component instance at` );
|
|
59
|
+
<Box display="flex" alignItems="center" gap={ 1 } minWidth={ 0 } flexGrow={ 1 } onClick={ onClick }>
|
|
60
|
+
<ListItemIcon size="tiny">
|
|
61
|
+
<ComponentsIcon fontSize="tiny" />
|
|
62
|
+
</ListItemIcon>
|
|
63
|
+
<Indicator isActive={ isEditing } isError={ !! error }>
|
|
64
|
+
<Box display="flex" flex={ 1 } minWidth={ 0 } flexGrow={ 1 }>
|
|
65
|
+
{ nameSlot ?? <ComponentName name={ component.name } /> }
|
|
66
|
+
</Box>
|
|
67
|
+
</Indicator>
|
|
68
|
+
</Box>
|
|
69
|
+
{ endSlot }
|
|
70
|
+
</ListItemButton>
|
|
71
|
+
);
|
|
204
72
|
}
|
|
205
|
-
|
|
206
|
-
loadComponentsAssets( [ model as V1ElementData ] );
|
|
207
|
-
|
|
208
|
-
dropElement( {
|
|
209
|
-
containerId: container.id,
|
|
210
|
-
model,
|
|
211
|
-
options: { ...options, useHistory: false, scrollIntoView: true },
|
|
212
|
-
} );
|
|
213
|
-
};
|
|
214
|
-
|
|
215
|
-
const validateComponentTitle = ( newTitle: string ) => {
|
|
216
|
-
const result = validateComponentName( newTitle );
|
|
217
|
-
|
|
218
|
-
if ( ! result.errorMessage ) {
|
|
219
|
-
return null;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
return result.errorMessage;
|
|
223
|
-
};
|
|
73
|
+
);
|
|
224
74
|
|
|
225
75
|
const Indicator = styled( Box, {
|
|
226
76
|
shouldForwardProp: ( prop ) => prop !== 'isActive' && prop !== 'isError',
|
|
227
|
-
} )( ( { theme, isActive, isError } ) => ( {
|
|
77
|
+
} )( ( { theme, isActive, isError }: { theme: Theme; isActive: boolean; isError: boolean } ) => ( {
|
|
228
78
|
display: 'flex',
|
|
229
79
|
width: '100%',
|
|
230
80
|
flexGrow: 1,
|
|
@@ -246,3 +96,22 @@ const getIndicatorBorder = ( { isActive, isError, theme }: { isActive: boolean;
|
|
|
246
96
|
|
|
247
97
|
return 'none';
|
|
248
98
|
};
|
|
99
|
+
|
|
100
|
+
type EditableConfig = {
|
|
101
|
+
ref: React.Ref< HTMLElement >;
|
|
102
|
+
isEditing: boolean;
|
|
103
|
+
getProps: () => Record< string, unknown >;
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
export type ComponentNameProps = {
|
|
107
|
+
name: string;
|
|
108
|
+
editable?: EditableConfig;
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
export function ComponentName( { name, editable }: ComponentNameProps ) {
|
|
112
|
+
if ( editable?.isEditing ) {
|
|
113
|
+
return <EditableField ref={ editable.ref } as={ Typography } variant="caption" { ...editable.getProps() } />;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return <EllipsisWithTooltip title={ name } as={ Typography } variant="caption" color="text.primary" />;
|
|
117
|
+
}
|
|
@@ -40,7 +40,7 @@ export function ComponentsList() {
|
|
|
40
40
|
);
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
const EmptyState = () => {
|
|
43
|
+
export const EmptyState = () => {
|
|
44
44
|
const { canCreate } = useComponentsPermissions();
|
|
45
45
|
|
|
46
46
|
return (
|
|
@@ -106,7 +106,7 @@ const EmptyState = () => {
|
|
|
106
106
|
);
|
|
107
107
|
};
|
|
108
108
|
|
|
109
|
-
const EmptySearchResult = () => {
|
|
109
|
+
export const EmptySearchResult = () => {
|
|
110
110
|
const { searchValue, clearSearch } = useSearch();
|
|
111
111
|
return (
|
|
112
112
|
<Stack
|
|
@@ -163,7 +163,7 @@ const EmptySearchResult = () => {
|
|
|
163
163
|
);
|
|
164
164
|
};
|
|
165
165
|
|
|
166
|
-
const useFilteredComponents = () => {
|
|
166
|
+
export const useFilteredComponents = () => {
|
|
167
167
|
const { components, isLoading } = useComponents();
|
|
168
168
|
const { searchValue } = useSearch();
|
|
169
169
|
|
|
@@ -2,7 +2,6 @@ import * as React from 'react';
|
|
|
2
2
|
import { ThemeProvider } from '@elementor/editor-ui';
|
|
3
3
|
|
|
4
4
|
import { useComponents } from '../../hooks/use-components';
|
|
5
|
-
import { isProUser } from '../../utils/is-pro-user';
|
|
6
5
|
import { ComponentSearch } from './component-search';
|
|
7
6
|
import { ComponentsList } from './components-list';
|
|
8
7
|
import { ComponentsProNotification } from './components-pro-notification';
|
|
@@ -15,7 +14,7 @@ const ComponentsContent = () => {
|
|
|
15
14
|
return (
|
|
16
15
|
<>
|
|
17
16
|
{ hasComponents && <ComponentSearch /> }
|
|
18
|
-
{ hasComponents &&
|
|
17
|
+
{ hasComponents && <ComponentsProNotification /> }
|
|
19
18
|
<ComponentsList />
|
|
20
19
|
</>
|
|
21
20
|
);
|
|
@@ -5,7 +5,7 @@ import { __ } from '@wordpress/i18n';
|
|
|
5
5
|
|
|
6
6
|
import { useComponentsPermissions } from '../../hooks/use-components-permissions';
|
|
7
7
|
|
|
8
|
-
export const EmptyState = ( { onEditComponent }: { onEditComponent
|
|
8
|
+
export const EmptyState = ( { onEditComponent }: { onEditComponent?: () => void } ) => {
|
|
9
9
|
const { canEdit } = useComponentsPermissions();
|
|
10
10
|
|
|
11
11
|
const message = canEdit
|
|
@@ -34,7 +34,7 @@ export const EmptyState = ( { onEditComponent }: { onEditComponent: () => void }
|
|
|
34
34
|
<Typography align="center" variant="caption" maxWidth="170px">
|
|
35
35
|
{ message }
|
|
36
36
|
</Typography>
|
|
37
|
-
{ canEdit && (
|
|
37
|
+
{ canEdit && !! onEditComponent && (
|
|
38
38
|
<Button variant="outlined" color="secondary" size="small" sx={ { mt: 1 } } onClick={ onEditComponent }>
|
|
39
39
|
<PencilIcon fontSize="small" />
|
|
40
40
|
{ __( 'Edit component', 'elementor' ) }
|
|
@@ -1,58 +1,26 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { useElementSetting, useSelectedElement } from '@elementor/editor-elements';
|
|
5
|
-
import { PanelBody, PanelHeader, PanelHeaderTitle } from '@elementor/editor-panels';
|
|
6
|
-
import { EllipsisWithTooltip } from '@elementor/editor-ui';
|
|
7
|
-
import { ComponentsIcon, PencilIcon } from '@elementor/icons';
|
|
8
|
-
import { Box, Divider, IconButton, Stack, Tooltip } from '@elementor/ui';
|
|
2
|
+
import { PencilIcon } from '@elementor/icons';
|
|
3
|
+
import { Box } from '@elementor/ui';
|
|
9
4
|
import { __ } from '@wordpress/i18n';
|
|
10
5
|
|
|
11
|
-
import { useComponentsPermissions } from '../../hooks/use-components-permissions';
|
|
12
|
-
import { useSanitizeOverridableProps } from '../../hooks/use-sanitize-overridable-props';
|
|
13
|
-
import {
|
|
14
|
-
componentInstancePropTypeUtil,
|
|
15
|
-
type ComponentInstancePropValue,
|
|
16
|
-
} from '../../prop-types/component-instance-prop-type';
|
|
17
6
|
import { ComponentInstanceProvider } from '../../provider/component-instance-context';
|
|
18
|
-
import { useComponent } from '../../store/store';
|
|
19
|
-
import { type OverridablePropsGroup } from '../../types';
|
|
20
|
-
import { switchToComponent } from '../../utils/switch-to-component';
|
|
21
7
|
import { EmptyState } from './empty-state';
|
|
22
|
-
import {
|
|
8
|
+
import { InstancePanelBody } from './instance-panel-body';
|
|
9
|
+
import { EditComponentAction, InstancePanelHeader } from './instance-panel-header';
|
|
10
|
+
import { useInstancePanelData } from './use-instance-panel-data';
|
|
23
11
|
|
|
24
12
|
export function InstanceEditingPanel() {
|
|
25
|
-
const
|
|
13
|
+
const data = useInstancePanelData();
|
|
26
14
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const componentId = settings?.component_id?.value;
|
|
30
|
-
|
|
31
|
-
const overrides = settings?.overrides?.value;
|
|
32
|
-
|
|
33
|
-
const component = useComponent( componentId ?? null );
|
|
34
|
-
|
|
35
|
-
const componentInstanceId = useSelectedElement()?.element?.id;
|
|
36
|
-
|
|
37
|
-
const overridableProps = useSanitizeOverridableProps( componentId ?? null, componentInstanceId );
|
|
38
|
-
|
|
39
|
-
if ( ! componentId || ! overridableProps || ! component ) {
|
|
15
|
+
if ( ! data ) {
|
|
40
16
|
return null;
|
|
41
17
|
}
|
|
42
18
|
|
|
19
|
+
const { componentId, component, overrides, overridableProps, groups, isEmpty, componentInstanceId } = data;
|
|
20
|
+
|
|
43
21
|
/* translators: %s: component name. */
|
|
44
22
|
const panelTitle = __( 'Edit %s', 'elementor' ).replace( '%s', component.name );
|
|
45
23
|
|
|
46
|
-
const handleEditComponent = () => switchToComponent( componentId, componentInstanceId );
|
|
47
|
-
|
|
48
|
-
const isNonEmptyGroup = ( group: OverridablePropsGroup | null ) => group !== null && group.props.length > 0;
|
|
49
|
-
|
|
50
|
-
const groups = overridableProps.groups.order
|
|
51
|
-
.map( ( groupId ) => overridableProps.groups.items[ groupId ] ?? null )
|
|
52
|
-
.filter( isNonEmptyGroup );
|
|
53
|
-
|
|
54
|
-
const isEmpty = groups.length === 0 || Object.keys( overridableProps.props ).length === 0;
|
|
55
|
-
|
|
56
24
|
return (
|
|
57
25
|
<Box data-testid="instance-editing-panel">
|
|
58
26
|
<ComponentInstanceProvider
|
|
@@ -60,44 +28,17 @@ export function InstanceEditingPanel() {
|
|
|
60
28
|
overrides={ overrides }
|
|
61
29
|
overridableProps={ overridableProps }
|
|
62
30
|
>
|
|
63
|
-
<
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
) }
|
|
74
|
-
</Stack>
|
|
75
|
-
</PanelHeader>
|
|
76
|
-
<PanelBody>
|
|
77
|
-
<ControlAdornmentsProvider items={ getFieldIndicators( 'settings' ) }>
|
|
78
|
-
{ isEmpty ? (
|
|
79
|
-
<EmptyState onEditComponent={ handleEditComponent } />
|
|
80
|
-
) : (
|
|
81
|
-
<Stack direction="column" alignItems="stretch">
|
|
82
|
-
{ groups.map( ( group ) => (
|
|
83
|
-
<React.Fragment key={ group.id + componentInstanceId }>
|
|
84
|
-
<OverridePropsGroup group={ group } />
|
|
85
|
-
<Divider />
|
|
86
|
-
</React.Fragment>
|
|
87
|
-
) ) }
|
|
88
|
-
</Stack>
|
|
89
|
-
) }
|
|
90
|
-
</ControlAdornmentsProvider>
|
|
91
|
-
</PanelBody>
|
|
31
|
+
<InstancePanelHeader
|
|
32
|
+
componentName={ component.name }
|
|
33
|
+
actions={ <EditComponentAction disabled label={ panelTitle } icon={ PencilIcon } /> }
|
|
34
|
+
/>
|
|
35
|
+
<InstancePanelBody
|
|
36
|
+
groups={ groups }
|
|
37
|
+
isEmpty={ isEmpty }
|
|
38
|
+
emptyState={ <EmptyState /> }
|
|
39
|
+
componentInstanceId={ componentInstanceId }
|
|
40
|
+
/>
|
|
92
41
|
</ComponentInstanceProvider>
|
|
93
42
|
</Box>
|
|
94
43
|
);
|
|
95
44
|
}
|
|
96
|
-
|
|
97
|
-
function useComponentInstanceSettings() {
|
|
98
|
-
const { element } = useElement();
|
|
99
|
-
|
|
100
|
-
const settings = useElementSetting< ComponentInstancePropValue >( element.id, 'component_instance' );
|
|
101
|
-
|
|
102
|
-
return componentInstancePropTypeUtil.extract( settings );
|
|
103
|
-
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { ControlAdornmentsProvider } from '@elementor/editor-controls';
|
|
3
|
+
import { getFieldIndicators } from '@elementor/editor-editing-panel';
|
|
4
|
+
import { PanelBody } from '@elementor/editor-panels';
|
|
5
|
+
import { Divider, Stack } from '@elementor/ui';
|
|
6
|
+
|
|
7
|
+
import { type OverridablePropsGroup } from '../../types';
|
|
8
|
+
import { OverridePropsGroup } from './override-props-group';
|
|
9
|
+
|
|
10
|
+
type InstancePanelBodyProps = {
|
|
11
|
+
groups: OverridablePropsGroup[];
|
|
12
|
+
isEmpty: boolean;
|
|
13
|
+
emptyState: React.ReactNode;
|
|
14
|
+
componentInstanceId?: string;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export function InstancePanelBody( { groups, isEmpty, emptyState, componentInstanceId }: InstancePanelBodyProps ) {
|
|
18
|
+
return (
|
|
19
|
+
<PanelBody>
|
|
20
|
+
<ControlAdornmentsProvider items={ getFieldIndicators( 'settings' ) }>
|
|
21
|
+
{ isEmpty ? (
|
|
22
|
+
emptyState
|
|
23
|
+
) : (
|
|
24
|
+
<Stack direction="column" alignItems="stretch">
|
|
25
|
+
{ groups.map( ( group ) => (
|
|
26
|
+
<React.Fragment key={ group.id + componentInstanceId }>
|
|
27
|
+
<OverridePropsGroup group={ group } />
|
|
28
|
+
<Divider />
|
|
29
|
+
</React.Fragment>
|
|
30
|
+
) ) }
|
|
31
|
+
</Stack>
|
|
32
|
+
) }
|
|
33
|
+
</ControlAdornmentsProvider>
|
|
34
|
+
</PanelBody>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { type ElementType } from 'react';
|
|
3
|
+
import { PanelHeader, PanelHeaderTitle } from '@elementor/editor-panels';
|
|
4
|
+
import { EllipsisWithTooltip } from '@elementor/editor-ui';
|
|
5
|
+
import { ComponentsIcon } from '@elementor/icons';
|
|
6
|
+
import { IconButton, Stack, Tooltip } from '@elementor/ui';
|
|
7
|
+
|
|
8
|
+
type InstancePanelHeaderProps = {
|
|
9
|
+
componentName: string;
|
|
10
|
+
actions?: React.ReactNode;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export function InstancePanelHeader( { componentName, actions }: InstancePanelHeaderProps ) {
|
|
14
|
+
return (
|
|
15
|
+
<PanelHeader sx={ { justifyContent: 'start', px: 2 } }>
|
|
16
|
+
<Stack direction="row" alignItems="center" flexGrow={ 1 } gap={ 1 } maxWidth="100%">
|
|
17
|
+
<ComponentsIcon fontSize="small" sx={ { color: 'text.tertiary' } } />
|
|
18
|
+
<EllipsisWithTooltip title={ componentName } as={ PanelHeaderTitle } sx={ { flexGrow: 1 } } />
|
|
19
|
+
{ actions }
|
|
20
|
+
</Stack>
|
|
21
|
+
</PanelHeader>
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
type EditComponentActionProps = {
|
|
26
|
+
label: string;
|
|
27
|
+
onClick?: () => void;
|
|
28
|
+
disabled?: boolean;
|
|
29
|
+
icon: ElementType;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export function EditComponentAction( { label, onClick, disabled = false, icon: Icon }: EditComponentActionProps ) {
|
|
33
|
+
return (
|
|
34
|
+
<Tooltip title={ label }>
|
|
35
|
+
<IconButton size="tiny" onClick={ onClick } aria-label={ label } disabled={ disabled }>
|
|
36
|
+
<Icon fontSize="tiny" />
|
|
37
|
+
</IconButton>
|
|
38
|
+
</Tooltip>
|
|
39
|
+
);
|
|
40
|
+
}
|