@elementor/editor-components 4.0.0-607 → 4.0.0-619
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 +4 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.js +2380 -2216
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2835 -2680
- 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/override-prop-control.tsx +5 -3
- package/src/components/instance-editing-panel/use-instance-panel-data.ts +51 -0
- package/src/create-component-type.ts +15 -13
- package/src/extended/components/component-properties-panel/component-properties-panel.tsx +9 -17
- 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/index.ts +1 -0
- package/src/utils/is-pro-user.ts +6 -21
- /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
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { useElement } from '@elementor/editor-editing-panel';
|
|
2
|
+
|
|
3
|
+
import { useSanitizeOverridableProps } from '../../hooks/use-sanitize-overridable-props';
|
|
4
|
+
import { type ComponentInstanceOverridesPropValue } from '../../prop-types/component-instance-overrides-prop-type';
|
|
5
|
+
import { componentInstancePropTypeUtil } from '../../prop-types/component-instance-prop-type';
|
|
6
|
+
import { useComponent } from '../../store/store';
|
|
7
|
+
import { type Component, type OverridablePropsGroup } from '../../types';
|
|
8
|
+
|
|
9
|
+
type InstancePanelData = {
|
|
10
|
+
componentId: number;
|
|
11
|
+
component: Component;
|
|
12
|
+
overrides: ComponentInstanceOverridesPropValue;
|
|
13
|
+
overridableProps: NonNullable< ReturnType< typeof useSanitizeOverridableProps > >;
|
|
14
|
+
groups: OverridablePropsGroup[];
|
|
15
|
+
isEmpty: boolean;
|
|
16
|
+
componentInstanceId: string | undefined;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export function useInstancePanelData(): InstancePanelData | null {
|
|
20
|
+
const { element, settings } = useComponentInstanceSettings();
|
|
21
|
+
|
|
22
|
+
const componentId = settings?.component_id?.value;
|
|
23
|
+
|
|
24
|
+
const overrides = settings?.overrides?.value;
|
|
25
|
+
|
|
26
|
+
const component = useComponent( componentId ?? null );
|
|
27
|
+
|
|
28
|
+
const componentInstanceId = element?.id;
|
|
29
|
+
|
|
30
|
+
const overridableProps = useSanitizeOverridableProps( componentId ?? null, componentInstanceId );
|
|
31
|
+
|
|
32
|
+
if ( ! componentId || ! overridableProps || ! component ) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const isNonEmptyGroup = ( group: OverridablePropsGroup | null ) => group !== null && group.props.length > 0;
|
|
37
|
+
|
|
38
|
+
const groups = overridableProps.groups.order
|
|
39
|
+
.map( ( groupId ) => overridableProps.groups.items[ groupId ] ?? null )
|
|
40
|
+
.filter( isNonEmptyGroup );
|
|
41
|
+
|
|
42
|
+
const isEmpty = groups.length === 0 || Object.keys( overridableProps.props ).length === 0;
|
|
43
|
+
|
|
44
|
+
return { componentId, component, overrides, overridableProps, groups, isEmpty, componentInstanceId };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function useComponentInstanceSettings() {
|
|
48
|
+
const { element, settings } = useElement();
|
|
49
|
+
|
|
50
|
+
return { element, settings: componentInstancePropTypeUtil.extract( settings.component_instance ) };
|
|
51
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
type BackboneModel,
|
|
3
3
|
type BackboneModelConstructor,
|
|
4
|
+
type ContextMenuAction,
|
|
4
5
|
type CreateTemplatedElementTypeOptions,
|
|
5
6
|
createTemplatedElementView,
|
|
6
7
|
type ElementModel,
|
|
@@ -20,19 +21,12 @@ import { type ComponentInstanceProp } from './prop-types/component-instance-prop
|
|
|
20
21
|
import { type ComponentsSlice, selectComponentByUid } from './store/store';
|
|
21
22
|
import { type ComponentRenderContext, type ExtendedWindow } from './types';
|
|
22
23
|
import { formatComponentElementsId } from './utils/format-component-elements-id';
|
|
24
|
+
import { hasProInstalled } from './utils/is-pro-user';
|
|
23
25
|
import { switchToComponent } from './utils/switch-to-component';
|
|
24
26
|
import { trackComponentEvent } from './utils/tracking';
|
|
25
27
|
|
|
26
28
|
type ContextMenuEventData = { location: string; secondaryLocation: string; trigger: string };
|
|
27
29
|
|
|
28
|
-
export type ContextMenuAction = {
|
|
29
|
-
name: string;
|
|
30
|
-
icon: string;
|
|
31
|
-
title: string | ( () => string );
|
|
32
|
-
isEnabled: () => boolean;
|
|
33
|
-
callback: ( _: unknown, eventData: ContextMenuEventData ) => void;
|
|
34
|
-
};
|
|
35
|
-
|
|
36
30
|
type ContextMenuGroupConfig = {
|
|
37
31
|
disable: Record< string, string[] >;
|
|
38
32
|
add: Record< string, { index: number; action: ContextMenuAction } >;
|
|
@@ -58,6 +52,8 @@ type ComponentModelInstance = BackboneModel< ComponentModel > & {
|
|
|
58
52
|
|
|
59
53
|
export const COMPONENT_WIDGET_TYPE = 'e-component';
|
|
60
54
|
|
|
55
|
+
const EDIT_COMPONENT_UPGRADE_URL = 'https://go.elementor.com/go-pro-components-edit/';
|
|
56
|
+
|
|
61
57
|
const updateGroups = ( groups: ContextMenuGroup[], config: ContextMenuGroupConfig ): ContextMenuGroup[] => {
|
|
62
58
|
const disableMap = new Map( Object.entries( config.disable ?? {} ) );
|
|
63
59
|
const addMap = new Map( Object.entries( config.add ?? {} ) );
|
|
@@ -218,6 +214,11 @@ function createComponentView(
|
|
|
218
214
|
|
|
219
215
|
_getContextMenuConfig() {
|
|
220
216
|
const isAdministrator = isUserAdministrator();
|
|
217
|
+
const hasPro = hasProInstalled();
|
|
218
|
+
|
|
219
|
+
const proLabel = __( 'PRO', 'elementor' );
|
|
220
|
+
const badgeClass = 'elementor-context-menu-list__item__shortcut__new-badge';
|
|
221
|
+
const proBadge = `<a href="${ EDIT_COMPONENT_UPGRADE_URL }" target="_blank" onclick="event.stopPropagation()" class="${ badgeClass }">${ proLabel }</a>`;
|
|
221
222
|
|
|
222
223
|
const addedGroup = {
|
|
223
224
|
general: {
|
|
@@ -226,7 +227,8 @@ function createComponentView(
|
|
|
226
227
|
name: 'edit component',
|
|
227
228
|
icon: 'eicon-edit',
|
|
228
229
|
title: () => __( 'Edit Component', 'elementor' ),
|
|
229
|
-
|
|
230
|
+
...( ! hasPro && { shortcut: proBadge, hasShortcutAction: true } ),
|
|
231
|
+
isEnabled: () => hasPro,
|
|
230
232
|
callback: ( _: unknown, eventData: ContextMenuEventData ) => this.editComponent( eventData ),
|
|
231
233
|
},
|
|
232
234
|
},
|
|
@@ -253,7 +255,9 @@ function createComponentView(
|
|
|
253
255
|
}
|
|
254
256
|
|
|
255
257
|
editComponent( { trigger, location, secondaryLocation }: ContextMenuEventData ) {
|
|
256
|
-
|
|
258
|
+
const hasPro = hasProInstalled();
|
|
259
|
+
|
|
260
|
+
if ( ! hasPro || this.isComponentCurrentlyEdited() ) {
|
|
257
261
|
return;
|
|
258
262
|
}
|
|
259
263
|
|
|
@@ -275,9 +279,7 @@ function createComponentView(
|
|
|
275
279
|
handleDblClick( e: MouseEvent ) {
|
|
276
280
|
e.stopPropagation();
|
|
277
281
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
if ( ! isAdministrator ) {
|
|
282
|
+
if ( ! isUserAdministrator() || ! hasProInstalled() ) {
|
|
281
283
|
return;
|
|
282
284
|
}
|
|
283
285
|
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import {
|
|
3
|
-
import { useSelectedElement } from '@elementor/editor-elements';
|
|
2
|
+
import { usePanelActions as useEditingPanelActions } from '@elementor/editor-editing-panel';
|
|
4
3
|
import { __createPanel as createPanel, Panel } from '@elementor/editor-panels';
|
|
5
4
|
import { ThemeProvider } from '@elementor/editor-ui';
|
|
6
5
|
import { Alert, Box, ErrorBoundary } from '@elementor/ui';
|
|
@@ -16,27 +15,20 @@ export const { panel, usePanelActions } = createPanel( {
|
|
|
16
15
|
} );
|
|
17
16
|
|
|
18
17
|
function ComponentPropertiesPanel() {
|
|
19
|
-
const { element, elementType } = useSelectedElement();
|
|
20
18
|
const { close: closePanel } = usePanelActions();
|
|
21
19
|
const { open: openEditingPanel } = useEditingPanelActions();
|
|
22
20
|
|
|
23
|
-
if ( ! element || ! elementType ) {
|
|
24
|
-
return null;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
21
|
return (
|
|
28
22
|
<ThemeProvider>
|
|
29
23
|
<ErrorBoundary fallback={ <ErrorBoundaryFallback /> }>
|
|
30
|
-
<
|
|
31
|
-
<
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
</Panel>
|
|
39
|
-
</ElementProvider>
|
|
24
|
+
<Panel>
|
|
25
|
+
<ComponentPropertiesPanelContent
|
|
26
|
+
onClose={ () => {
|
|
27
|
+
closePanel();
|
|
28
|
+
openEditingPanel();
|
|
29
|
+
} }
|
|
30
|
+
/>
|
|
31
|
+
</Panel>
|
|
40
32
|
</ErrorBoundary>
|
|
41
33
|
</ThemeProvider>
|
|
42
34
|
);
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { useRef, useState } from 'react';
|
|
3
|
+
import { endDragElementFromPanel, startDragElementFromPanel } from '@elementor/editor-canvas';
|
|
4
|
+
import { dropElement, type DropElementParams, type V1ElementData } from '@elementor/editor-elements';
|
|
5
|
+
import { MenuListItem, useEditable, WarningInfotip } from '@elementor/editor-ui';
|
|
6
|
+
import { DotsVerticalIcon } from '@elementor/icons';
|
|
7
|
+
import { bindMenu, bindTrigger, IconButton, Menu, Stack, usePopupState } from '@elementor/ui';
|
|
8
|
+
import { __ } from '@wordpress/i18n';
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
ComponentItem as CoreComponentItem,
|
|
12
|
+
type ComponentItemProps,
|
|
13
|
+
ComponentName,
|
|
14
|
+
} from '../../../components/components-tab/components-item';
|
|
15
|
+
import { useComponentsPermissions } from '../../../hooks/use-components-permissions';
|
|
16
|
+
import { loadComponentsAssets } from '../../../store/actions/load-components-assets';
|
|
17
|
+
import { archiveComponent } from '../../store/actions/archive-component';
|
|
18
|
+
import { renameComponent } from '../../store/actions/rename-component';
|
|
19
|
+
import { validateComponentName } from '../../utils/component-name-validation';
|
|
20
|
+
import { createComponentModel } from '../../utils/create-component-model';
|
|
21
|
+
import { getContainerForNewElement } from '../../utils/get-container-for-new-element';
|
|
22
|
+
import { DeleteConfirmationDialog } from './delete-confirmation-dialog';
|
|
23
|
+
|
|
24
|
+
export function ComponentItem( { component }: ComponentItemProps ) {
|
|
25
|
+
const itemRef = useRef< HTMLElement >( null );
|
|
26
|
+
const [ isDeleteDialogOpen, setIsDeleteDialogOpen ] = useState( false );
|
|
27
|
+
const { canRename, canDelete } = useComponentsPermissions();
|
|
28
|
+
|
|
29
|
+
const shouldShowActions = canRename || canDelete;
|
|
30
|
+
|
|
31
|
+
const {
|
|
32
|
+
ref: editableRef,
|
|
33
|
+
isEditing,
|
|
34
|
+
openEditMode,
|
|
35
|
+
error,
|
|
36
|
+
getProps: getEditableProps,
|
|
37
|
+
} = useEditable( {
|
|
38
|
+
value: component.name,
|
|
39
|
+
onSubmit: ( newName: string ) => renameComponent( component.uid, newName ),
|
|
40
|
+
validation: validateComponentTitle,
|
|
41
|
+
} );
|
|
42
|
+
|
|
43
|
+
const componentModel = createComponentModel( component );
|
|
44
|
+
|
|
45
|
+
const popupState = usePopupState( {
|
|
46
|
+
variant: 'popover',
|
|
47
|
+
disableAutoFocus: true,
|
|
48
|
+
} );
|
|
49
|
+
|
|
50
|
+
const handleClick = () => {
|
|
51
|
+
addComponentToPage( componentModel );
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const handleDragEnd = () => {
|
|
55
|
+
loadComponentsAssets( [ componentModel as V1ElementData ] );
|
|
56
|
+
|
|
57
|
+
endDragElementFromPanel();
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const handleDeleteClick = () => {
|
|
61
|
+
setIsDeleteDialogOpen( true );
|
|
62
|
+
popupState.close();
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const handleDeleteConfirm = () => {
|
|
66
|
+
if ( ! component.id ) {
|
|
67
|
+
throw new Error( 'Component ID is required' );
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
setIsDeleteDialogOpen( false );
|
|
71
|
+
archiveComponent( component.id, component.name );
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const handleDeleteDialogClose = () => {
|
|
75
|
+
setIsDeleteDialogOpen( false );
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
return (
|
|
79
|
+
<Stack>
|
|
80
|
+
<WarningInfotip
|
|
81
|
+
open={ Boolean( error ) }
|
|
82
|
+
text={ error ?? '' }
|
|
83
|
+
placement="bottom"
|
|
84
|
+
width={ itemRef.current?.getBoundingClientRect().width }
|
|
85
|
+
offset={ [ 0, -15 ] }
|
|
86
|
+
>
|
|
87
|
+
<CoreComponentItem
|
|
88
|
+
ref={ itemRef }
|
|
89
|
+
component={ component }
|
|
90
|
+
disabled={ false }
|
|
91
|
+
draggable
|
|
92
|
+
onDragStart={ ( event: React.DragEvent ) => startDragElementFromPanel( componentModel, event ) }
|
|
93
|
+
onDragEnd={ handleDragEnd }
|
|
94
|
+
onClick={ handleClick }
|
|
95
|
+
isEditing={ isEditing }
|
|
96
|
+
error={ error }
|
|
97
|
+
nameSlot={
|
|
98
|
+
<ComponentName
|
|
99
|
+
name={ component.name }
|
|
100
|
+
editable={ { ref: editableRef, isEditing, getProps: getEditableProps } }
|
|
101
|
+
/>
|
|
102
|
+
}
|
|
103
|
+
endSlot={
|
|
104
|
+
shouldShowActions ? (
|
|
105
|
+
<IconButton size="tiny" { ...bindTrigger( popupState ) } aria-label="More actions">
|
|
106
|
+
<DotsVerticalIcon fontSize="tiny" />
|
|
107
|
+
</IconButton>
|
|
108
|
+
) : undefined
|
|
109
|
+
}
|
|
110
|
+
/>
|
|
111
|
+
</WarningInfotip>
|
|
112
|
+
{ shouldShowActions && (
|
|
113
|
+
<Menu
|
|
114
|
+
{ ...bindMenu( popupState ) }
|
|
115
|
+
anchorOrigin={ {
|
|
116
|
+
vertical: 'bottom',
|
|
117
|
+
horizontal: 'right',
|
|
118
|
+
} }
|
|
119
|
+
transformOrigin={ {
|
|
120
|
+
vertical: 'top',
|
|
121
|
+
horizontal: 'right',
|
|
122
|
+
} }
|
|
123
|
+
>
|
|
124
|
+
{ canRename && (
|
|
125
|
+
<MenuListItem
|
|
126
|
+
sx={ { minWidth: '160px' } }
|
|
127
|
+
primaryTypographyProps={ { variant: 'caption', color: 'text.primary' } }
|
|
128
|
+
onClick={ () => {
|
|
129
|
+
popupState.close();
|
|
130
|
+
openEditMode();
|
|
131
|
+
} }
|
|
132
|
+
>
|
|
133
|
+
{ __( 'Rename', 'elementor' ) }
|
|
134
|
+
</MenuListItem>
|
|
135
|
+
) }
|
|
136
|
+
{ canDelete && (
|
|
137
|
+
<MenuListItem
|
|
138
|
+
sx={ { minWidth: '160px' } }
|
|
139
|
+
primaryTypographyProps={ { variant: 'caption', color: 'error.light' } }
|
|
140
|
+
onClick={ handleDeleteClick }
|
|
141
|
+
>
|
|
142
|
+
{ __( 'Delete', 'elementor' ) }
|
|
143
|
+
</MenuListItem>
|
|
144
|
+
) }
|
|
145
|
+
</Menu>
|
|
146
|
+
) }
|
|
147
|
+
<DeleteConfirmationDialog
|
|
148
|
+
open={ isDeleteDialogOpen }
|
|
149
|
+
onClose={ handleDeleteDialogClose }
|
|
150
|
+
onConfirm={ handleDeleteConfirm }
|
|
151
|
+
/>
|
|
152
|
+
</Stack>
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const addComponentToPage = ( model: DropElementParams[ 'model' ] ) => {
|
|
157
|
+
const { container, options } = getContainerForNewElement();
|
|
158
|
+
|
|
159
|
+
if ( ! container ) {
|
|
160
|
+
throw new Error( `Can't find container to drop new component instance at` );
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
loadComponentsAssets( [ model as V1ElementData ] );
|
|
164
|
+
|
|
165
|
+
dropElement( {
|
|
166
|
+
containerId: container.id,
|
|
167
|
+
model,
|
|
168
|
+
options: { ...options, useHistory: false, scrollIntoView: true },
|
|
169
|
+
} );
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
const validateComponentTitle = ( newTitle: string ) => {
|
|
173
|
+
const result = validateComponentName( newTitle );
|
|
174
|
+
|
|
175
|
+
if ( ! result.errorMessage ) {
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return result.errorMessage;
|
|
180
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { ThemeProvider } from '@elementor/editor-ui';
|
|
3
|
+
import { List } from '@elementor/ui';
|
|
4
|
+
|
|
5
|
+
import { ComponentSearch } from '../../../components/components-tab/component-search';
|
|
6
|
+
import {
|
|
7
|
+
EmptySearchResult,
|
|
8
|
+
EmptyState,
|
|
9
|
+
useFilteredComponents,
|
|
10
|
+
} from '../../../components/components-tab/components-list';
|
|
11
|
+
import { LoadingComponents } from '../../../components/components-tab/loading-components';
|
|
12
|
+
import { SearchProvider } from '../../../components/components-tab/search-provider';
|
|
13
|
+
import { useComponents } from '../../../hooks/use-components';
|
|
14
|
+
import { ComponentItem } from './component-item';
|
|
15
|
+
|
|
16
|
+
const ExtendedComponentsList = () => {
|
|
17
|
+
const { components, isLoading, searchValue } = useFilteredComponents();
|
|
18
|
+
|
|
19
|
+
if ( isLoading ) {
|
|
20
|
+
return <LoadingComponents />;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const isEmpty = ! components?.length;
|
|
24
|
+
|
|
25
|
+
if ( isEmpty ) {
|
|
26
|
+
return searchValue.length ? <EmptySearchResult /> : <EmptyState />;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<List sx={ { display: 'flex', flexDirection: 'column', gap: 1, px: 2 } }>
|
|
31
|
+
{ components.map( ( component ) => (
|
|
32
|
+
<ComponentItem key={ component.uid } component={ component } />
|
|
33
|
+
) ) }
|
|
34
|
+
</List>
|
|
35
|
+
);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const ExtendedComponentsContent = () => {
|
|
39
|
+
const { components, isLoading } = useComponents();
|
|
40
|
+
const hasComponents = ! isLoading && components.length > 0;
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<>
|
|
44
|
+
{ hasComponents && <ComponentSearch /> }
|
|
45
|
+
<ExtendedComponentsList />
|
|
46
|
+
</>
|
|
47
|
+
);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export const ExtendedComponents = () => {
|
|
51
|
+
return (
|
|
52
|
+
<ThemeProvider>
|
|
53
|
+
<SearchProvider localStorageKey="elementor-components-search">
|
|
54
|
+
<ExtendedComponentsContent />
|
|
55
|
+
</SearchProvider>
|
|
56
|
+
</ThemeProvider>
|
|
57
|
+
);
|
|
58
|
+
};
|
|
@@ -11,11 +11,11 @@ import { __ } from '@wordpress/i18n';
|
|
|
11
11
|
import { useComponents } from '../../../hooks/use-components';
|
|
12
12
|
import { selectComponentByUid } from '../../../store/store';
|
|
13
13
|
import { type ComponentFormValues, type PublishedComponent } from '../../../types';
|
|
14
|
-
import { createBaseComponentSchema, createSubmitComponentSchema } from '../../../utils/component-form-schema';
|
|
15
14
|
import { switchToComponent } from '../../../utils/switch-to-component';
|
|
16
15
|
import { trackComponentEvent } from '../../../utils/tracking';
|
|
17
16
|
import { createUnpublishedComponent } from '../../store/actions/create-unpublished-component';
|
|
18
17
|
import { findNonAtomicElementsInElement } from '../../sync/prevent-non-atomic-nesting';
|
|
18
|
+
import { createBaseComponentSchema, createSubmitComponentSchema } from '../../utils/component-form-schema';
|
|
19
19
|
import { useForm } from './hooks/use-form';
|
|
20
20
|
import {
|
|
21
21
|
type ComponentEventData,
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { PencilIcon } from '@elementor/icons';
|
|
3
|
+
import { Box } from '@elementor/ui';
|
|
4
|
+
import { __ } from '@wordpress/i18n';
|
|
5
|
+
|
|
6
|
+
import { EmptyState } from '../../../components/instance-editing-panel/empty-state';
|
|
7
|
+
import { InstancePanelBody } from '../../../components/instance-editing-panel/instance-panel-body';
|
|
8
|
+
import {
|
|
9
|
+
EditComponentAction,
|
|
10
|
+
InstancePanelHeader,
|
|
11
|
+
} from '../../../components/instance-editing-panel/instance-panel-header';
|
|
12
|
+
import { useInstancePanelData } from '../../../components/instance-editing-panel/use-instance-panel-data';
|
|
13
|
+
import { useComponentsPermissions } from '../../../hooks/use-components-permissions';
|
|
14
|
+
import { ComponentInstanceProvider } from '../../../provider/component-instance-context';
|
|
15
|
+
import { switchToComponent } from '../../../utils/switch-to-component';
|
|
16
|
+
|
|
17
|
+
export function ExtendedInstanceEditingPanel() {
|
|
18
|
+
const { canEdit } = useComponentsPermissions();
|
|
19
|
+
const data = useInstancePanelData();
|
|
20
|
+
|
|
21
|
+
if ( ! data ) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const { componentId, component, overrides, overridableProps, groups, isEmpty, componentInstanceId } = data;
|
|
26
|
+
|
|
27
|
+
/* translators: %s: component name. */
|
|
28
|
+
const panelTitle = __( 'Edit %s', 'elementor' ).replace( '%s', component.name );
|
|
29
|
+
|
|
30
|
+
const handleEditComponent = () => switchToComponent( componentId, componentInstanceId );
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<Box data-testid="instance-editing-panel">
|
|
34
|
+
<ComponentInstanceProvider
|
|
35
|
+
componentId={ componentId }
|
|
36
|
+
overrides={ overrides }
|
|
37
|
+
overridableProps={ overridableProps }
|
|
38
|
+
>
|
|
39
|
+
<InstancePanelHeader
|
|
40
|
+
componentName={ component.name }
|
|
41
|
+
actions={
|
|
42
|
+
canEdit ? (
|
|
43
|
+
<EditComponentAction
|
|
44
|
+
label={ panelTitle }
|
|
45
|
+
onClick={ handleEditComponent }
|
|
46
|
+
icon={ PencilIcon }
|
|
47
|
+
/>
|
|
48
|
+
) : undefined
|
|
49
|
+
}
|
|
50
|
+
/>
|
|
51
|
+
<InstancePanelBody
|
|
52
|
+
groups={ groups }
|
|
53
|
+
isEmpty={ isEmpty }
|
|
54
|
+
emptyState={ <EmptyState onEditComponent={ canEdit ? handleEditComponent : undefined } /> }
|
|
55
|
+
componentInstanceId={ componentInstanceId }
|
|
56
|
+
/>
|
|
57
|
+
</ComponentInstanceProvider>
|
|
58
|
+
</Box>
|
|
59
|
+
);
|
|
60
|
+
}
|
package/src/extended/init.ts
CHANGED
|
@@ -1,17 +1,26 @@
|
|
|
1
1
|
import { injectIntoLogic, injectIntoTop } from '@elementor/editor';
|
|
2
2
|
import { registerControlReplacement } from '@elementor/editor-controls';
|
|
3
3
|
import { getV1CurrentDocument } from '@elementor/editor-documents';
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
FIELD_TYPE,
|
|
6
|
+
injectIntoPanelHeaderTop,
|
|
7
|
+
registerEditingPanelReplacement,
|
|
8
|
+
registerFieldIndicator,
|
|
9
|
+
} from '@elementor/editor-editing-panel';
|
|
10
|
+
import { registerTab } from '@elementor/editor-elements-panel';
|
|
5
11
|
import { __registerPanel as registerPanel } from '@elementor/editor-panels';
|
|
6
12
|
import { registerDataHook } from '@elementor/editor-v1-adapters';
|
|
13
|
+
import { __ } from '@wordpress/i18n';
|
|
7
14
|
|
|
8
15
|
import { componentOverridablePropTypeUtil } from '../prop-types/component-overridable-prop-type';
|
|
9
16
|
import { type ExtendedWindow } from '../types';
|
|
10
17
|
import { onElementDrop } from '../utils/tracking';
|
|
11
18
|
import { ComponentPanelHeader } from './components/component-panel-header/component-panel-header';
|
|
12
19
|
import { panel as componentPropertiesPanel } from './components/component-properties-panel/component-properties-panel';
|
|
20
|
+
import { ExtendedComponents } from './components/components-tab/components';
|
|
13
21
|
import { CreateComponentForm } from './components/create-component-form/create-component-form';
|
|
14
22
|
import { EditComponent } from './components/edit-component/edit-component';
|
|
23
|
+
import { ExtendedInstanceEditingPanel } from './components/instance-editing-panel/instance-editing-panel';
|
|
15
24
|
import { OverridablePropControl } from './components/overridable-props/overridable-prop-control';
|
|
16
25
|
import { OverridablePropIndicator } from './components/overridable-props/overridable-prop-indicator';
|
|
17
26
|
import { COMPONENT_DOCUMENT_TYPE, OVERRIDABLE_PROP_REPLACEMENT_ID } from './consts';
|
|
@@ -24,6 +33,18 @@ import { initRevertOverridablesOnCopyOrDuplicate } from './sync/revert-overridab
|
|
|
24
33
|
import { SanitizeOverridableProps } from './sync/sanitize-overridable-props';
|
|
25
34
|
|
|
26
35
|
export function initExtended() {
|
|
36
|
+
registerEditingPanelReplacement( {
|
|
37
|
+
id: 'component-instance-edit-panel',
|
|
38
|
+
condition: ( _, elementType ) => elementType.key === 'e-component',
|
|
39
|
+
component: ExtendedInstanceEditingPanel,
|
|
40
|
+
} );
|
|
41
|
+
|
|
42
|
+
registerTab( {
|
|
43
|
+
id: 'components',
|
|
44
|
+
label: __( 'Components', 'elementor' ),
|
|
45
|
+
component: ExtendedComponents,
|
|
46
|
+
} );
|
|
47
|
+
|
|
27
48
|
registerPanel( componentPropertiesPanel );
|
|
28
49
|
|
|
29
50
|
registerDataHook( 'dependency', 'editor/documents/close', ( args ) => {
|
|
@@ -3,7 +3,7 @@ import { type NotificationData, notify } from '@elementor/editor-notifications';
|
|
|
3
3
|
import { __dispatch as dispatch } from '@elementor/store';
|
|
4
4
|
import { __ } from '@wordpress/i18n';
|
|
5
5
|
|
|
6
|
-
import { slice } from '
|
|
6
|
+
import { slice } from '../../../store/store';
|
|
7
7
|
|
|
8
8
|
const successNotification = ( componentId: number, componentName: string ): NotificationData => ( {
|
|
9
9
|
type: 'success',
|
|
@@ -2,8 +2,8 @@ import { getV1DocumentsManager, setDocumentModifiedStatus } from '@elementor/edi
|
|
|
2
2
|
import { getAllDescendants, type V1Element } from '@elementor/editor-elements';
|
|
3
3
|
import { __dispatch as dispatch } from '@elementor/store';
|
|
4
4
|
|
|
5
|
-
import { COMPONENT_WIDGET_TYPE } from '
|
|
6
|
-
import { slice } from '
|
|
5
|
+
import { COMPONENT_WIDGET_TYPE } from '../../../create-component-type';
|
|
6
|
+
import { slice } from '../../../store/store';
|
|
7
7
|
|
|
8
8
|
const TITLE_EXTERNAL_CHANGE_COMMAND = 'title_external_change';
|
|
9
9
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { __getState as getState } from '@elementor/store';
|
|
2
2
|
|
|
3
|
-
import { selectComponents } from '
|
|
3
|
+
import { selectComponents } from '../../store/store';
|
|
4
4
|
import { createSubmitComponentSchema } from './component-form-schema';
|
|
5
5
|
|
|
6
6
|
type ValidationResult = { isValid: true; errorMessage: null } | { isValid: false; errorMessage: string };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { replaceElement, type V1ElementData } from '@elementor/editor-elements';
|
|
2
2
|
|
|
3
|
-
import { type ComponentInstanceParams, createComponentModel } from '
|
|
3
|
+
import { type ComponentInstanceParams, createComponentModel } from './create-component-model';
|
|
4
4
|
|
|
5
5
|
export const replaceElementWithComponent = async ( element: V1ElementData, component: ComponentInstanceParams ) => {
|
|
6
6
|
return await replaceElement( {
|
package/src/index.ts
CHANGED
package/src/utils/is-pro-user.ts
CHANGED
|
@@ -1,26 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
elementor
|
|
3
|
-
|
|
4
|
-
hasPro?: () => boolean;
|
|
5
|
-
};
|
|
6
|
-
};
|
|
7
|
-
elementorPro?: {
|
|
8
|
-
config?: {
|
|
9
|
-
isActive?: boolean;
|
|
10
|
-
};
|
|
11
|
-
};
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
export function isProUser(): boolean {
|
|
15
|
-
const extendedWindow = window as unknown as ExtendedWindow;
|
|
16
|
-
|
|
17
|
-
const hasPro = extendedWindow.elementor?.helpers?.hasPro?.() ?? false;
|
|
1
|
+
export function hasProInstalled(): boolean {
|
|
2
|
+
return window.elementor?.helpers?.hasPro?.() ?? false;
|
|
3
|
+
}
|
|
18
4
|
|
|
19
|
-
|
|
5
|
+
export function isProActive(): boolean {
|
|
6
|
+
if ( ! hasProInstalled() ) {
|
|
20
7
|
return false;
|
|
21
8
|
}
|
|
22
9
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
return isProActive;
|
|
10
|
+
return window.elementorPro?.config?.isActive ?? false;
|
|
26
11
|
}
|
/package/src/{components → extended/components}/components-tab/delete-confirmation-dialog.tsx
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|