@elementor/editor-components 4.0.0-manual → 4.0.1
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 +1422 -1
- package/dist/index.d.ts +1422 -1
- package/dist/index.js +2096 -4814
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2028 -4837
- package/dist/index.mjs.map +1 -1
- package/package.json +23 -23
- package/src/components/components-tab/components-list.tsx +92 -4
- package/src/components/components-tab/components-pro-notification.tsx +9 -15
- package/src/components/components-tab/components-update-notification.tsx +13 -0
- package/src/components/components-tab/components.tsx +52 -3
- package/src/components/components-tab/loading-components.tsx +26 -14
- package/src/components/components-update-alert.tsx +40 -0
- package/src/components/components-upgrade-alert.tsx +39 -0
- package/src/components/detach-instance-confirmation-dialog.tsx +50 -0
- package/src/components/instance-editing-panel/detach-action.tsx +76 -0
- package/src/components/instance-editing-panel/empty-state.tsx +9 -2
- package/src/components/instance-editing-panel/instance-editing-panel.tsx +34 -6
- package/src/components/instance-editing-panel/override-prop-control.tsx +14 -6
- package/src/components/instance-editing-panel/use-instance-panel-data.ts +2 -2
- package/src/components/instance-editing-panel/utils/correct-exposed-empty-override.ts +28 -0
- package/src/consts.ts +1 -0
- package/src/create-component-type.ts +130 -29
- package/src/index.ts +92 -0
- package/src/init.ts +6 -4
- package/src/store/actions/update-overridable-prop.ts +4 -10
- package/src/store/dispatchers.ts +63 -0
- package/src/store/extensible-slice.ts +168 -0
- package/src/store/selectors.ts +53 -0
- package/src/store/store-types.ts +48 -0
- package/src/store/store.ts +7 -169
- package/src/sync/publish-draft-components-in-page-before-save.ts +42 -1
- package/src/types.ts +1 -1
- package/src/utils/detach-component-instance/detach-component-instance.ts +172 -0
- package/src/utils/detach-component-instance/index.ts +1 -0
- package/src/utils/detach-component-instance/regenerate-local-style-ids.ts +53 -0
- package/src/utils/detach-component-instance/resolve-detached-instance.ts +94 -0
- package/src/utils/detach-component-instance/resolve-overridable-settings.ts +121 -0
- package/src/utils/is-component-instance.ts +1 -1
- package/src/utils/is-pro-components-supported.ts +11 -0
- package/src/utils/tracking.ts +2 -1
- package/src/extended/components/component-introduction.tsx +0 -77
- package/src/extended/components/component-panel-header/component-badge.tsx +0 -73
- package/src/extended/components/component-panel-header/component-panel-header.tsx +0 -98
- package/src/extended/components/component-properties-panel/component-properties-panel-content.tsx +0 -176
- package/src/extended/components/component-properties-panel/component-properties-panel.tsx +0 -43
- package/src/extended/components/component-properties-panel/properties-empty-state.tsx +0 -51
- package/src/extended/components/component-properties-panel/properties-group.tsx +0 -196
- package/src/extended/components/component-properties-panel/property-item.tsx +0 -124
- package/src/extended/components/component-properties-panel/sortable.tsx +0 -92
- package/src/extended/components/component-properties-panel/use-current-editable-item.ts +0 -73
- package/src/extended/components/component-properties-panel/utils/generate-unique-label.ts +0 -21
- package/src/extended/components/component-properties-panel/utils/validate-group-label.ts +0 -24
- package/src/extended/components/components-tab/component-item.tsx +0 -180
- package/src/extended/components/components-tab/components.tsx +0 -58
- package/src/extended/components/components-tab/delete-confirmation-dialog.tsx +0 -26
- package/src/extended/components/create-component-form/create-component-form.tsx +0 -282
- package/src/extended/components/create-component-form/hooks/use-form.ts +0 -72
- package/src/extended/components/create-component-form/utils/get-component-event-data.ts +0 -54
- package/src/extended/components/edit-component/component-modal.tsx +0 -133
- package/src/extended/components/edit-component/edit-component.tsx +0 -166
- package/src/extended/components/edit-component/use-canvas-document.ts +0 -9
- package/src/extended/components/edit-component/use-element-rect.ts +0 -81
- package/src/extended/components/instance-editing-panel/instance-editing-panel.tsx +0 -60
- package/src/extended/components/overridable-props/indicator.tsx +0 -83
- package/src/extended/components/overridable-props/overridable-prop-control.tsx +0 -127
- package/src/extended/components/overridable-props/overridable-prop-form.tsx +0 -135
- package/src/extended/components/overridable-props/overridable-prop-indicator.tsx +0 -138
- package/src/extended/components/overridable-props/utils/validate-prop-label.ts +0 -38
- package/src/extended/consts.ts +0 -3
- package/src/extended/hooks/use-navigate-back.ts +0 -24
- package/src/extended/init.ts +0 -104
- package/src/extended/mcp/index.ts +0 -14
- package/src/extended/mcp/save-as-component-tool.ts +0 -436
- package/src/extended/store/actions/add-overridable-group.ts +0 -59
- package/src/extended/store/actions/archive-component.ts +0 -19
- package/src/extended/store/actions/create-unpublished-component.ts +0 -102
- package/src/extended/store/actions/delete-overridable-group.ts +0 -38
- package/src/extended/store/actions/delete-overridable-prop.ts +0 -70
- package/src/extended/store/actions/rename-component.ts +0 -49
- package/src/extended/store/actions/rename-overridable-group.ts +0 -39
- package/src/extended/store/actions/reorder-group-props.ts +0 -43
- package/src/extended/store/actions/reorder-overridable-groups.ts +0 -30
- package/src/extended/store/actions/reset-sanitized-components.ts +0 -7
- package/src/extended/store/actions/set-overridable-prop.ts +0 -117
- package/src/extended/store/actions/update-component-sanitized-attribute.ts +0 -8
- package/src/extended/store/actions/update-current-component.ts +0 -21
- package/src/extended/store/actions/update-overridable-prop-params.ts +0 -58
- package/src/extended/store/utils/groups-transformers.ts +0 -187
- package/src/extended/sync/before-save.ts +0 -52
- package/src/extended/sync/cleanup-overridable-props-on-delete.ts +0 -85
- package/src/extended/sync/create-components-before-save.ts +0 -113
- package/src/extended/sync/handle-component-edit-mode-container.ts +0 -114
- package/src/extended/sync/prevent-non-atomic-nesting.ts +0 -198
- package/src/extended/sync/revert-overridables-on-copy-or-duplicate.ts +0 -66
- package/src/extended/sync/sanitize-overridable-props.ts +0 -32
- package/src/extended/sync/set-component-overridable-props-settings-before-save.ts +0 -23
- package/src/extended/sync/update-archived-component-before-save.ts +0 -32
- package/src/extended/sync/update-component-title-before-save.ts +0 -19
- package/src/extended/utils/component-form-schema.ts +0 -32
- package/src/extended/utils/component-name-validation.ts +0 -27
- package/src/extended/utils/create-component-model.ts +0 -28
- package/src/extended/utils/get-container-for-new-element.ts +0 -49
- package/src/extended/utils/is-editing-component.ts +0 -13
- package/src/extended/utils/replace-element-with-component.ts +0 -11
- package/src/extended/utils/revert-overridable-settings.ts +0 -207
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
import { getAllDescendants, type V1Element } from '@elementor/editor-elements';
|
|
2
|
-
import { type HookOptions, registerDataHook } from '@elementor/editor-v1-adapters';
|
|
3
|
-
import { __getState as getState } from '@elementor/store';
|
|
4
|
-
|
|
5
|
-
import { type ComponentsSlice, selectCurrentComponentId, selectOverridableProps } from '../../store/store';
|
|
6
|
-
import { deleteOverridableProp } from '../store/actions/delete-overridable-prop';
|
|
7
|
-
|
|
8
|
-
type DeleteCommandArgs = {
|
|
9
|
-
container?: V1Element;
|
|
10
|
-
containers?: V1Element[];
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
export function initCleanupOverridablePropsOnDelete() {
|
|
14
|
-
// This hook is not a real dependency - it doesn't block the execution of the command in any case, only perform side effect.
|
|
15
|
-
// We use `dependency` and not `after` hook because the `after` hook doesn't include the children of a deleted container
|
|
16
|
-
// in the callback parameters (as they already were deleted).
|
|
17
|
-
registerDataHook( 'dependency', 'document/elements/delete', ( args: DeleteCommandArgs, options?: HookOptions ) => {
|
|
18
|
-
if ( isPartOfMoveCommand( options ) ) {
|
|
19
|
-
return true;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const state = getState() as ComponentsSlice | undefined;
|
|
23
|
-
|
|
24
|
-
if ( ! state ) {
|
|
25
|
-
return true;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const currentComponentId = selectCurrentComponentId( state );
|
|
29
|
-
|
|
30
|
-
if ( ! currentComponentId ) {
|
|
31
|
-
return true;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const overridableProps = selectOverridableProps( state, currentComponentId );
|
|
35
|
-
|
|
36
|
-
if ( ! overridableProps || Object.keys( overridableProps.props ).length === 0 ) {
|
|
37
|
-
return true;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const containers = args.containers ?? ( args.container ? [ args.container ] : [] );
|
|
41
|
-
|
|
42
|
-
if ( containers.length === 0 ) {
|
|
43
|
-
return true;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const deletedElementIds = collectDeletedElementIds( containers );
|
|
47
|
-
|
|
48
|
-
if ( deletedElementIds.length === 0 ) {
|
|
49
|
-
return true;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const propKeysToDelete = Object.entries( overridableProps.props )
|
|
53
|
-
.filter( ( [ , prop ] ) => deletedElementIds.includes( prop.elementId ) )
|
|
54
|
-
.map( ( [ propKey ] ) => propKey );
|
|
55
|
-
|
|
56
|
-
if ( propKeysToDelete.length === 0 ) {
|
|
57
|
-
return true;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
deleteOverridableProp( { componentId: currentComponentId, propKey: propKeysToDelete, source: 'system' } );
|
|
61
|
-
|
|
62
|
-
return true;
|
|
63
|
-
} );
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function collectDeletedElementIds( containers: V1Element[] ): string[] {
|
|
67
|
-
const elementIds = containers
|
|
68
|
-
.filter( Boolean )
|
|
69
|
-
.flatMap( ( container ) => [ container, ...getAllDescendants( container ) ] )
|
|
70
|
-
.map( ( element ) => element.model?.get?.( 'id' ) ?? element.id )
|
|
71
|
-
.filter( ( id ): id is string => Boolean( id ) );
|
|
72
|
-
|
|
73
|
-
return elementIds;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function isPartOfMoveCommand( options?: HookOptions ): boolean {
|
|
77
|
-
// Skip cleanup if this delete is part of a move command
|
|
78
|
-
// Move = delete + create, and we don't want to delete the overridable prop in this case.
|
|
79
|
-
// See assets/dev/js/editor/document/elements/commands/move.js
|
|
80
|
-
const isMoveCommandInTrace =
|
|
81
|
-
options?.commandsCurrentTrace?.includes( 'document/elements/move' ) ||
|
|
82
|
-
options?.commandsCurrentTrace?.includes( 'document/repeater/move' );
|
|
83
|
-
|
|
84
|
-
return Boolean( isMoveCommandInTrace );
|
|
85
|
-
}
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
import { updateElementSettings, type V1ElementData } from '@elementor/editor-elements';
|
|
2
|
-
import { __dispatch as dispatch, __getState as getState } from '@elementor/store';
|
|
3
|
-
|
|
4
|
-
import { apiClient } from '../../api';
|
|
5
|
-
import { type ComponentInstanceProp } from '../../prop-types/component-instance-prop-type';
|
|
6
|
-
import { selectUnpublishedComponents, slice } from '../../store/store';
|
|
7
|
-
import { type DocumentSaveStatus, type UnpublishedComponent } from '../../types';
|
|
8
|
-
|
|
9
|
-
export async function createComponentsBeforeSave( {
|
|
10
|
-
elements,
|
|
11
|
-
status,
|
|
12
|
-
}: {
|
|
13
|
-
elements: V1ElementData[];
|
|
14
|
-
status: DocumentSaveStatus;
|
|
15
|
-
} ) {
|
|
16
|
-
const unpublishedComponents = selectUnpublishedComponents( getState() );
|
|
17
|
-
|
|
18
|
-
if ( ! unpublishedComponents.length ) {
|
|
19
|
-
return;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
try {
|
|
23
|
-
const uidToComponentId = await createComponents( unpublishedComponents, status );
|
|
24
|
-
|
|
25
|
-
updateComponentInstances( elements, uidToComponentId );
|
|
26
|
-
|
|
27
|
-
dispatch(
|
|
28
|
-
slice.actions.add(
|
|
29
|
-
unpublishedComponents.map( ( component ) => ( {
|
|
30
|
-
id: uidToComponentId.get( component.uid ) as number,
|
|
31
|
-
name: component.name,
|
|
32
|
-
uid: component.uid,
|
|
33
|
-
overridableProps: component.overridableProps ? component.overridableProps : undefined,
|
|
34
|
-
} ) )
|
|
35
|
-
)
|
|
36
|
-
);
|
|
37
|
-
dispatch( slice.actions.resetUnpublished() );
|
|
38
|
-
} catch ( error ) {
|
|
39
|
-
const failedUids = unpublishedComponents.map( ( component ) => component.uid );
|
|
40
|
-
dispatch( slice.actions.removeUnpublished( failedUids ) );
|
|
41
|
-
|
|
42
|
-
throw new Error( `Failed to publish components: ${ error }` );
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
async function createComponents(
|
|
47
|
-
components: UnpublishedComponent[],
|
|
48
|
-
status: DocumentSaveStatus
|
|
49
|
-
): Promise< Map< string, number > > {
|
|
50
|
-
const response = await apiClient.create( {
|
|
51
|
-
status,
|
|
52
|
-
items: components.map( ( component ) => ( {
|
|
53
|
-
uid: component.uid,
|
|
54
|
-
title: component.name,
|
|
55
|
-
elements: component.elements,
|
|
56
|
-
settings: component.overridableProps ? { overridable_props: component.overridableProps } : undefined,
|
|
57
|
-
} ) ),
|
|
58
|
-
} );
|
|
59
|
-
|
|
60
|
-
const map = new Map< string, number >();
|
|
61
|
-
|
|
62
|
-
Object.entries( response ).forEach( ( [ key, value ] ) => {
|
|
63
|
-
map.set( key, value );
|
|
64
|
-
} );
|
|
65
|
-
|
|
66
|
-
return map;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function updateComponentInstances( elements: V1ElementData[], uidToComponentId: Map< string, number > ): void {
|
|
70
|
-
elements.forEach( ( element ) => {
|
|
71
|
-
const { shouldUpdate, newComponentId } = shouldUpdateElement( element, uidToComponentId );
|
|
72
|
-
if ( shouldUpdate ) {
|
|
73
|
-
updateElementComponentId( element.id, newComponentId );
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
if ( element.elements ) {
|
|
77
|
-
updateComponentInstances( element.elements, uidToComponentId );
|
|
78
|
-
}
|
|
79
|
-
} );
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
function shouldUpdateElement(
|
|
83
|
-
element: V1ElementData,
|
|
84
|
-
uidToComponentId: Map< string, number >
|
|
85
|
-
): { shouldUpdate: true; newComponentId: number } | { shouldUpdate: false; newComponentId: null } {
|
|
86
|
-
if ( element.widgetType === 'e-component' ) {
|
|
87
|
-
const currentComponentId = ( element.settings?.component_instance as ComponentInstanceProp )?.value
|
|
88
|
-
?.component_id.value;
|
|
89
|
-
|
|
90
|
-
if ( currentComponentId && uidToComponentId.has( currentComponentId.toString() ) ) {
|
|
91
|
-
return {
|
|
92
|
-
shouldUpdate: true,
|
|
93
|
-
newComponentId: uidToComponentId.get( currentComponentId.toString() ) as number,
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
return { shouldUpdate: false, newComponentId: null };
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
function updateElementComponentId( elementId: string, componentId: number ): void {
|
|
101
|
-
updateElementSettings( {
|
|
102
|
-
id: elementId,
|
|
103
|
-
props: {
|
|
104
|
-
component_instance: {
|
|
105
|
-
$$type: 'component-instance',
|
|
106
|
-
value: {
|
|
107
|
-
component_id: { $$type: 'number', value: componentId },
|
|
108
|
-
},
|
|
109
|
-
},
|
|
110
|
-
},
|
|
111
|
-
withHistory: false,
|
|
112
|
-
} );
|
|
113
|
-
}
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
import { type V1Document } from '@elementor/editor-documents';
|
|
2
|
-
import { createElement, selectElement, type V1Element } from '@elementor/editor-elements';
|
|
3
|
-
import { registerDataHook } from '@elementor/editor-v1-adapters';
|
|
4
|
-
|
|
5
|
-
import { COMPONENT_DOCUMENT_TYPE } from '../consts';
|
|
6
|
-
import { isEditingComponent } from '../utils/is-editing-component';
|
|
7
|
-
|
|
8
|
-
const V4_DEFAULT_CONTAINER_TYPE = 'e-flexbox';
|
|
9
|
-
|
|
10
|
-
type Container = Omit< V1Element, 'children' | 'parent' > & {
|
|
11
|
-
document?: V1Document;
|
|
12
|
-
parent?: Container;
|
|
13
|
-
children?: Container[];
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
export function initHandleComponentEditModeContainer() {
|
|
17
|
-
initRedirectDropIntoComponent();
|
|
18
|
-
initHandleTopLevelElementDelete();
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export type DeleteArgs = {
|
|
22
|
-
container?: Container;
|
|
23
|
-
containers?: Container[];
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
function initHandleTopLevelElementDelete() {
|
|
27
|
-
registerDataHook( 'after', 'document/elements/delete', ( args: DeleteArgs ) => {
|
|
28
|
-
if ( ! isEditingComponent() ) {
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const containers = args.containers ?? ( args.container ? [ args.container ] : [] );
|
|
33
|
-
|
|
34
|
-
for ( const container of containers ) {
|
|
35
|
-
if ( ! container.parent || ! isComponent( container.parent ) ) {
|
|
36
|
-
continue;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const component = container.parent;
|
|
40
|
-
const isComponentEmpty = component.children?.length === 0;
|
|
41
|
-
|
|
42
|
-
if ( isComponentEmpty ) {
|
|
43
|
-
createEmptyTopLevelContainer( container.parent );
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
} );
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
type DropArgs = {
|
|
50
|
-
container?: Container;
|
|
51
|
-
containers?: Container[];
|
|
52
|
-
model?: unknown;
|
|
53
|
-
options?: unknown;
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
function initRedirectDropIntoComponent() {
|
|
57
|
-
registerDataHook( 'dependency', 'preview/drop', ( args: DropArgs ) => {
|
|
58
|
-
if ( ! isEditingComponent() ) {
|
|
59
|
-
return true;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const containers = args.containers ?? ( args.container ? [ args.container ] : [] );
|
|
63
|
-
|
|
64
|
-
for ( const container of containers ) {
|
|
65
|
-
if ( ! isComponent( container ) ) {
|
|
66
|
-
continue;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const { shouldRedirect, container: redirectedContainer } = getComponentContainer( container );
|
|
70
|
-
|
|
71
|
-
if ( ! shouldRedirect ) {
|
|
72
|
-
continue;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
if ( args.containers ) {
|
|
76
|
-
const index = args.containers.indexOf( container );
|
|
77
|
-
args.containers[ index ] = redirectedContainer;
|
|
78
|
-
} else {
|
|
79
|
-
args.container = redirectedContainer;
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
return true;
|
|
84
|
-
} );
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
function createEmptyTopLevelContainer( container: Container ) {
|
|
88
|
-
const newContainer = createElement( {
|
|
89
|
-
container,
|
|
90
|
-
model: { elType: V4_DEFAULT_CONTAINER_TYPE },
|
|
91
|
-
} );
|
|
92
|
-
|
|
93
|
-
selectElement( newContainer.id );
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
function getComponentContainer( container: Container ): { shouldRedirect: boolean; container: Container } {
|
|
97
|
-
const topLevelElement = container.children?.[ 0 ];
|
|
98
|
-
|
|
99
|
-
if ( topLevelElement ) {
|
|
100
|
-
return { shouldRedirect: true, container: topLevelElement };
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
return { shouldRedirect: false, container };
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
function isComponent( container: Container ): boolean {
|
|
107
|
-
const isDocument = container.id === 'document';
|
|
108
|
-
|
|
109
|
-
if ( ! isDocument ) {
|
|
110
|
-
return false;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
return container.document?.config.type === COMPONENT_DOCUMENT_TYPE;
|
|
114
|
-
}
|
|
@@ -1,198 +0,0 @@
|
|
|
1
|
-
import { isAtomicWidget } from '@elementor/editor-canvas';
|
|
2
|
-
import { getAllDescendants, getElementType, type V1Element } from '@elementor/editor-elements';
|
|
3
|
-
import { type NotificationData, notify } from '@elementor/editor-notifications';
|
|
4
|
-
import { blockCommand } from '@elementor/editor-v1-adapters';
|
|
5
|
-
import { __ } from '@wordpress/i18n';
|
|
6
|
-
|
|
7
|
-
import { type ExtendedWindow } from '../../types';
|
|
8
|
-
import { isEditingComponent } from '../utils/is-editing-component';
|
|
9
|
-
|
|
10
|
-
type CreateArgs = {
|
|
11
|
-
container?: V1Element;
|
|
12
|
-
model?: {
|
|
13
|
-
elType?: string;
|
|
14
|
-
widgetType?: string;
|
|
15
|
-
};
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
type MoveArgs = {
|
|
19
|
-
containers?: V1Element[];
|
|
20
|
-
container?: V1Element;
|
|
21
|
-
target?: V1Element;
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
type PasteArgs = {
|
|
25
|
-
containers?: V1Element[];
|
|
26
|
-
container?: V1Element;
|
|
27
|
-
storageType?: string;
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
export type ClipboardElement = {
|
|
31
|
-
elType?: string;
|
|
32
|
-
widgetType?: string;
|
|
33
|
-
elements?: ClipboardElement[];
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
type StorageContent = {
|
|
37
|
-
clipboard?: {
|
|
38
|
-
elements?: ClipboardElement[];
|
|
39
|
-
};
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
const NON_ATOMIC_ELEMENT_ALERT: NotificationData = {
|
|
43
|
-
type: 'default',
|
|
44
|
-
message: __( "This widget isn't compatible with components. Use atomic elements instead.", 'elementor' ),
|
|
45
|
-
id: 'non-atomic-element-blocked',
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
export function initNonAtomicNestingPrevention() {
|
|
49
|
-
blockCommand( {
|
|
50
|
-
command: 'document/elements/create',
|
|
51
|
-
condition: blockNonAtomicCreate,
|
|
52
|
-
} );
|
|
53
|
-
|
|
54
|
-
blockCommand( {
|
|
55
|
-
command: 'document/elements/move',
|
|
56
|
-
condition: blockNonAtomicMove,
|
|
57
|
-
} );
|
|
58
|
-
|
|
59
|
-
blockCommand( {
|
|
60
|
-
command: 'document/elements/paste',
|
|
61
|
-
condition: blockNonAtomicPaste,
|
|
62
|
-
} );
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export function isElementAtomic( elementType: string ): boolean {
|
|
66
|
-
return getElementType( elementType ) !== null;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function blockNonAtomicCreate( args: CreateArgs ): boolean {
|
|
70
|
-
if ( ! isEditingComponent() ) {
|
|
71
|
-
return false;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const { model } = args;
|
|
75
|
-
const elementType = model?.widgetType || model?.elType;
|
|
76
|
-
|
|
77
|
-
if ( ! elementType ) {
|
|
78
|
-
return false;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
if ( isElementAtomic( elementType ) ) {
|
|
82
|
-
return false;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
notify( NON_ATOMIC_ELEMENT_ALERT );
|
|
86
|
-
return true;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
function blockNonAtomicMove( args: MoveArgs ): boolean {
|
|
90
|
-
if ( ! isEditingComponent() ) {
|
|
91
|
-
return false;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
const { containers = [ args.container ] } = args;
|
|
95
|
-
|
|
96
|
-
const hasNonAtomicElement = containers.some( ( container ) => {
|
|
97
|
-
if ( ! container ) {
|
|
98
|
-
return false;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
const allElements = getAllDescendants( container );
|
|
102
|
-
|
|
103
|
-
return allElements.some( ( element ) => ! isAtomicWidget( element ) );
|
|
104
|
-
} );
|
|
105
|
-
|
|
106
|
-
if ( hasNonAtomicElement ) {
|
|
107
|
-
notify( NON_ATOMIC_ELEMENT_ALERT );
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
return hasNonAtomicElement;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
function blockNonAtomicPaste( args: PasteArgs ): boolean {
|
|
114
|
-
if ( ! isEditingComponent() ) {
|
|
115
|
-
return false;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
const { storageType } = args;
|
|
119
|
-
|
|
120
|
-
if ( storageType !== 'localstorage' ) {
|
|
121
|
-
return false;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
const data = (
|
|
125
|
-
window as unknown as ExtendedWindow & { elementorCommon?: { storage?: { get: () => StorageContent } } }
|
|
126
|
-
)?.elementorCommon?.storage?.get();
|
|
127
|
-
|
|
128
|
-
if ( ! data?.clipboard?.elements ) {
|
|
129
|
-
return false;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
const hasNonAtomicElement = hasNonAtomicElementsInTree( data.clipboard.elements );
|
|
133
|
-
|
|
134
|
-
if ( hasNonAtomicElement ) {
|
|
135
|
-
notify( NON_ATOMIC_ELEMENT_ALERT );
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
return hasNonAtomicElement;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
export function hasNonAtomicElementsInTree( elements: ClipboardElement[] ): boolean {
|
|
142
|
-
for ( const element of elements ) {
|
|
143
|
-
const elementType = element.widgetType || element.elType;
|
|
144
|
-
|
|
145
|
-
if ( elementType && ! isElementAtomic( elementType ) ) {
|
|
146
|
-
return true;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
if ( element.elements?.length ) {
|
|
150
|
-
if ( hasNonAtomicElementsInTree( element.elements ) ) {
|
|
151
|
-
return true;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
return false;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
export function findNonAtomicElements( elements: ClipboardElement[] ): string[] {
|
|
160
|
-
const nonAtomicElements: string[] = [];
|
|
161
|
-
|
|
162
|
-
for ( const element of elements ) {
|
|
163
|
-
const elementType = element.widgetType || element.elType;
|
|
164
|
-
|
|
165
|
-
if ( elementType && ! isElementAtomic( elementType ) ) {
|
|
166
|
-
nonAtomicElements.push( elementType );
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
if ( element.elements?.length ) {
|
|
170
|
-
nonAtomicElements.push( ...findNonAtomicElements( element.elements ) );
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
return [ ...new Set( nonAtomicElements ) ];
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
type V1ElementLike = {
|
|
178
|
-
elType?: string;
|
|
179
|
-
widgetType?: string;
|
|
180
|
-
elements?: V1ElementLike[];
|
|
181
|
-
};
|
|
182
|
-
|
|
183
|
-
export function findNonAtomicElementsInElement( element: V1ElementLike ): string[] {
|
|
184
|
-
const nonAtomicElements: string[] = [];
|
|
185
|
-
const elementType = element.widgetType || element.elType;
|
|
186
|
-
|
|
187
|
-
if ( elementType && ! isElementAtomic( elementType ) ) {
|
|
188
|
-
nonAtomicElements.push( elementType );
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
if ( element.elements?.length ) {
|
|
192
|
-
for ( const child of element.elements ) {
|
|
193
|
-
nonAtomicElements.push( ...findNonAtomicElementsInElement( child ) );
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
return [ ...new Set( nonAtomicElements ) ];
|
|
198
|
-
}
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import { type V1Element, type V1ElementData } from '@elementor/editor-elements';
|
|
2
|
-
import { registerDataHook } from '@elementor/editor-v1-adapters';
|
|
3
|
-
|
|
4
|
-
import { type ExtendedWindow } from '../../types';
|
|
5
|
-
import { isEditingComponent } from '../utils/is-editing-component';
|
|
6
|
-
import {
|
|
7
|
-
revertAllOverridablesInContainer,
|
|
8
|
-
revertAllOverridablesInElementData,
|
|
9
|
-
} from '../utils/revert-overridable-settings';
|
|
10
|
-
|
|
11
|
-
type CopyArgs = {
|
|
12
|
-
storageKey?: string;
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
type ClipboardData = {
|
|
16
|
-
type: string;
|
|
17
|
-
siteurl: string;
|
|
18
|
-
elements: V1ElementData[];
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
export function initRevertOverridablesOnCopyOrDuplicate() {
|
|
22
|
-
registerDataHook( 'after', 'document/elements/duplicate', ( _args, result: V1Element | V1Element[] ) => {
|
|
23
|
-
if ( ! isEditingComponent() ) {
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
revertOverridablesForDuplicatedElements( result );
|
|
28
|
-
} );
|
|
29
|
-
|
|
30
|
-
registerDataHook( 'after', 'document/elements/copy', ( args: CopyArgs ) => {
|
|
31
|
-
if ( ! isEditingComponent() ) {
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
revertOverridablesInStorage( args.storageKey ?? 'clipboard' );
|
|
36
|
-
} );
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function revertOverridablesForDuplicatedElements( duplicatedElements: V1Element | V1Element[] ) {
|
|
40
|
-
const containers = Array.isArray( duplicatedElements ) ? duplicatedElements : [ duplicatedElements ];
|
|
41
|
-
|
|
42
|
-
containers.forEach( ( container ) => {
|
|
43
|
-
revertAllOverridablesInContainer( container );
|
|
44
|
-
} );
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function revertOverridablesInStorage( storageKey: string ) {
|
|
48
|
-
const storage = ( window as unknown as ExtendedWindow ).elementorCommon?.storage;
|
|
49
|
-
|
|
50
|
-
if ( ! storage ) {
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const storageData = storage.get< ClipboardData >( storageKey );
|
|
55
|
-
|
|
56
|
-
if ( ! storageData?.elements?.length ) {
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const elementsDataWithOverridablesReverted = storageData.elements.map( revertAllOverridablesInElementData );
|
|
61
|
-
|
|
62
|
-
storage.set( storageKey, {
|
|
63
|
-
...storageData,
|
|
64
|
-
elements: elementsDataWithOverridablesReverted,
|
|
65
|
-
} );
|
|
66
|
-
}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { useEffect } from 'react';
|
|
2
|
-
|
|
3
|
-
import { useCurrentComponentId, useIsSanitizedComponent, useOverridableProps } from '../../store/store';
|
|
4
|
-
import { filterValidOverridableProps } from '../../utils/filter-valid-overridable-props';
|
|
5
|
-
import { deleteOverridableProp } from '../store/actions/delete-overridable-prop';
|
|
6
|
-
import { updateComponentSanitizedAttribute } from '../store/actions/update-component-sanitized-attribute';
|
|
7
|
-
|
|
8
|
-
export function SanitizeOverridableProps() {
|
|
9
|
-
const currentComponentId = useCurrentComponentId();
|
|
10
|
-
const overridableProps = useOverridableProps( currentComponentId );
|
|
11
|
-
const isSanitized = useIsSanitizedComponent( currentComponentId, 'overridableProps' );
|
|
12
|
-
|
|
13
|
-
useEffect( () => {
|
|
14
|
-
if ( isSanitized || ! overridableProps || ! currentComponentId ) {
|
|
15
|
-
return;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const filtered = filterValidOverridableProps( overridableProps );
|
|
19
|
-
|
|
20
|
-
const propsToDelete = Object.keys( overridableProps.props ?? {} ).filter( ( key ) => ! filtered.props[ key ] );
|
|
21
|
-
|
|
22
|
-
if ( propsToDelete.length > 0 ) {
|
|
23
|
-
propsToDelete.forEach( ( key ) => {
|
|
24
|
-
deleteOverridableProp( { componentId: currentComponentId, propKey: key, source: 'system' } );
|
|
25
|
-
} );
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
updateComponentSanitizedAttribute( currentComponentId, 'overridableProps' );
|
|
29
|
-
}, [ currentComponentId, isSanitized, overridableProps ] );
|
|
30
|
-
|
|
31
|
-
return null;
|
|
32
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { type V1Document } from '@elementor/editor-documents';
|
|
2
|
-
import { type V1Element } from '@elementor/editor-elements';
|
|
3
|
-
import { __getState as getState } from '@elementor/store';
|
|
4
|
-
|
|
5
|
-
import { selectOverridableProps } from '../../store/store';
|
|
6
|
-
import { COMPONENT_DOCUMENT_TYPE } from '../consts';
|
|
7
|
-
|
|
8
|
-
export const setComponentOverridablePropsSettingsBeforeSave = ( {
|
|
9
|
-
container,
|
|
10
|
-
}: {
|
|
11
|
-
container: V1Element & { document: V1Document };
|
|
12
|
-
} ) => {
|
|
13
|
-
const currentDocument = container.document;
|
|
14
|
-
|
|
15
|
-
if ( ! currentDocument || currentDocument.config.type !== COMPONENT_DOCUMENT_TYPE ) {
|
|
16
|
-
return;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const overridableProps = selectOverridableProps( getState(), currentDocument.id );
|
|
20
|
-
if ( overridableProps ) {
|
|
21
|
-
container.settings.set( 'overridable_props', overridableProps );
|
|
22
|
-
}
|
|
23
|
-
};
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { type NotificationData, notify } from '@elementor/editor-notifications';
|
|
2
|
-
import { __getState as getState } from '@elementor/store';
|
|
3
|
-
|
|
4
|
-
import { apiClient } from '../../api';
|
|
5
|
-
import { selectArchivedThisSession } from '../../store/store';
|
|
6
|
-
import { type DocumentSaveStatus } from '../../types';
|
|
7
|
-
|
|
8
|
-
const failedNotification = ( message: string ): NotificationData => ( {
|
|
9
|
-
type: 'error',
|
|
10
|
-
message: `Failed to archive components: ${ message }`,
|
|
11
|
-
id: 'failed-archived-components-notification',
|
|
12
|
-
} );
|
|
13
|
-
|
|
14
|
-
export const updateArchivedComponentBeforeSave = async ( status: DocumentSaveStatus ) => {
|
|
15
|
-
try {
|
|
16
|
-
const archivedComponents = selectArchivedThisSession( getState() );
|
|
17
|
-
|
|
18
|
-
if ( ! archivedComponents.length ) {
|
|
19
|
-
return;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const result = await apiClient.updateArchivedComponents( archivedComponents, status );
|
|
23
|
-
|
|
24
|
-
const failedIds = result.failedIds.join( ', ' );
|
|
25
|
-
|
|
26
|
-
if ( failedIds ) {
|
|
27
|
-
notify( failedNotification( failedIds ) );
|
|
28
|
-
}
|
|
29
|
-
} catch ( error ) {
|
|
30
|
-
throw new Error( `Failed to update archived components: ${ error }` );
|
|
31
|
-
}
|
|
32
|
-
};
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { __dispatch as dispatch, __getState as getState } from '@elementor/store';
|
|
2
|
-
|
|
3
|
-
import { apiClient } from '../../api';
|
|
4
|
-
import { selectUpdatedComponentNames, slice } from '../../store/store';
|
|
5
|
-
import { type DocumentSaveStatus } from '../../types';
|
|
6
|
-
|
|
7
|
-
export const updateComponentTitleBeforeSave = async ( status: DocumentSaveStatus ) => {
|
|
8
|
-
const updatedComponentNames = selectUpdatedComponentNames( getState() );
|
|
9
|
-
|
|
10
|
-
if ( ! updatedComponentNames.length ) {
|
|
11
|
-
return;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const result = await apiClient.updateComponentTitle( updatedComponentNames, status );
|
|
15
|
-
|
|
16
|
-
if ( result.failedIds.length === 0 ) {
|
|
17
|
-
dispatch( slice.actions.cleanUpdatedComponentNames() );
|
|
18
|
-
}
|
|
19
|
-
};
|