@elementor/editor-components 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.js +2225 -128
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2236 -111
- package/dist/index.mjs.map +1 -1
- package/package.json +23 -12
- package/src/api.ts +71 -11
- package/src/component-instance-transformer.ts +24 -0
- package/src/component-overridable-transformer.ts +28 -0
- package/src/components/component-panel-header/component-badge.tsx +62 -0
- package/src/components/component-panel-header/component-panel-header.tsx +58 -0
- package/src/components/component-panel-header/use-overridable-props.ts +14 -0
- package/src/components/components-tab/component-search.tsx +32 -0
- package/src/components/components-tab/components-item.tsx +115 -0
- package/src/components/components-tab/components-list.tsx +141 -0
- package/src/components/components-tab/components.tsx +17 -0
- package/src/components/components-tab/loading-components.tsx +43 -0
- package/src/components/components-tab/search-provider.tsx +38 -0
- package/src/components/consts.ts +1 -0
- package/src/components/create-component-form/create-component-form.tsx +109 -100
- package/src/components/create-component-form/utils/get-component-event-data.ts +54 -0
- package/src/components/create-component-form/utils/replace-element-with-component.ts +28 -10
- package/src/components/edit-component/component-modal.tsx +134 -0
- package/src/components/edit-component/edit-component.tsx +96 -0
- package/src/components/in-edit-mode.tsx +43 -0
- package/src/components/overridable-props/indicator.tsx +80 -0
- package/src/components/overridable-props/overridable-prop-control.tsx +67 -0
- package/src/components/overridable-props/overridable-prop-form.tsx +98 -0
- package/src/components/overridable-props/overridable-prop-indicator.tsx +124 -0
- package/src/components/overridable-props/utils/get-overridable-prop.ts +20 -0
- package/src/create-component-type.ts +194 -0
- package/src/hooks/use-canvas-document.ts +6 -0
- package/src/hooks/use-components.ts +6 -9
- package/src/hooks/use-element-rect.ts +81 -0
- package/src/hooks/use-navigate-back.ts +34 -0
- package/src/init.ts +100 -3
- package/src/mcp/index.ts +14 -0
- package/src/mcp/save-as-component-tool.ts +92 -0
- package/src/populate-store.ts +12 -0
- package/src/prop-types/component-overridable-prop-type.ts +17 -0
- package/src/store/actions/archive-component.ts +16 -0
- package/src/store/actions/create-unpublished-component.ts +40 -0
- package/src/store/actions/load-components-assets.ts +29 -0
- package/src/store/actions/load-components-overridable-props.ts +33 -0
- package/src/store/actions/load-components-styles.ts +44 -0
- package/src/store/actions/remove-component-styles.ts +9 -0
- package/src/store/actions/set-overridable-prop.ts +200 -0
- package/src/store/actions/update-current-component.ts +33 -0
- package/src/store/actions/update-overridable-prop-origin-value.ts +37 -0
- package/src/store/components-styles-provider.ts +24 -0
- package/src/store/store.ts +193 -0
- package/src/store/thunks.ts +10 -0
- package/src/sync/before-save.ts +31 -0
- package/src/sync/create-components-before-save.ts +102 -0
- package/src/sync/set-component-overridable-props-settings-before-save.ts +23 -0
- package/src/sync/update-archived-component-before-save.ts +44 -0
- package/src/sync/update-components-before-save.ts +35 -0
- package/src/types.ts +83 -0
- package/src/utils/component-document-data.ts +19 -0
- package/src/utils/get-component-ids.ts +36 -0
- package/src/utils/get-container-for-new-element.ts +49 -0
- package/src/utils/tracking.ts +47 -0
- package/src/components/components-tab.tsx +0 -6
- package/src/hooks/use-create-component.ts +0 -13
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { setDocumentModifiedStatus, type V1Document } from '@elementor/editor-documents';
|
|
2
|
+
import { __getStore as getStore } from '@elementor/store';
|
|
3
|
+
|
|
4
|
+
import { type ComponentsPathItem, slice } from '../store';
|
|
5
|
+
|
|
6
|
+
export function updateCurrentComponent( {
|
|
7
|
+
path,
|
|
8
|
+
currentComponentId,
|
|
9
|
+
}: {
|
|
10
|
+
path: ComponentsPathItem[];
|
|
11
|
+
currentComponentId: V1Document[ 'id' ] | null;
|
|
12
|
+
} ) {
|
|
13
|
+
const dispatch = getStore()?.dispatch;
|
|
14
|
+
|
|
15
|
+
if ( ! dispatch ) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
dispatch( slice.actions.setPath( path ) );
|
|
20
|
+
dispatch( slice.actions.setCurrentComponentId( currentComponentId ) );
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const archiveComponent = ( componentId: number ) => {
|
|
24
|
+
const store = getStore();
|
|
25
|
+
const dispatch = store?.dispatch;
|
|
26
|
+
|
|
27
|
+
if ( ! dispatch ) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
dispatch( slice.actions.archive( componentId ) );
|
|
32
|
+
setDocumentModifiedStatus( true );
|
|
33
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { __dispatch as dispatch, __getState as getState } from '@elementor/store';
|
|
2
|
+
|
|
3
|
+
import { type ComponentOverridablePropValue } from '../../prop-types/component-overridable-prop-type';
|
|
4
|
+
import { type OverridableProps } from '../../types';
|
|
5
|
+
import { selectOverridableProps, slice } from '../store';
|
|
6
|
+
|
|
7
|
+
export function updateOverridablePropOriginValue( componentId: number, propValue: ComponentOverridablePropValue ) {
|
|
8
|
+
const overridableProps = selectOverridableProps( getState(), componentId );
|
|
9
|
+
|
|
10
|
+
if ( ! overridableProps ) {
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const existingOverridableProp = overridableProps.props[ propValue.override_key ];
|
|
15
|
+
|
|
16
|
+
if ( ! existingOverridableProp ) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const newOverridableProps = {
|
|
21
|
+
...overridableProps,
|
|
22
|
+
props: {
|
|
23
|
+
...overridableProps.props,
|
|
24
|
+
[ existingOverridableProp.overrideKey ]: {
|
|
25
|
+
...existingOverridableProp,
|
|
26
|
+
originValue: propValue.origin_value,
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
} satisfies OverridableProps;
|
|
30
|
+
|
|
31
|
+
dispatch(
|
|
32
|
+
slice.actions.setOverridableProps( {
|
|
33
|
+
componentId,
|
|
34
|
+
overridableProps: newOverridableProps,
|
|
35
|
+
} )
|
|
36
|
+
);
|
|
37
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { createStylesProvider } from '@elementor/editor-styles-repository';
|
|
2
|
+
import { __getState as getState, __subscribeWithSelector as subscribeWithSelector } from '@elementor/store';
|
|
3
|
+
|
|
4
|
+
import { selectFlatStyles, SLICE_NAME } from './store';
|
|
5
|
+
|
|
6
|
+
export const componentsStylesProvider = createStylesProvider( {
|
|
7
|
+
key: 'components-styles',
|
|
8
|
+
priority: 100,
|
|
9
|
+
subscribe: ( cb ) =>
|
|
10
|
+
subscribeWithSelector(
|
|
11
|
+
( state ) => state[ SLICE_NAME ],
|
|
12
|
+
() => {
|
|
13
|
+
cb();
|
|
14
|
+
}
|
|
15
|
+
),
|
|
16
|
+
actions: {
|
|
17
|
+
all: () => {
|
|
18
|
+
return selectFlatStyles( getState() );
|
|
19
|
+
},
|
|
20
|
+
get: ( id ) => {
|
|
21
|
+
return selectFlatStyles( getState() ).find( ( style ) => style.id === id ) ?? null;
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
} );
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { type V1Document } from '@elementor/editor-documents';
|
|
2
|
+
import {
|
|
3
|
+
__createSelector as createSelector,
|
|
4
|
+
__createSlice as createSlice,
|
|
5
|
+
type PayloadAction,
|
|
6
|
+
type SliceState,
|
|
7
|
+
} from '@elementor/store';
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
type Component,
|
|
11
|
+
type ComponentId,
|
|
12
|
+
type OverridableProps,
|
|
13
|
+
type PublishedComponent,
|
|
14
|
+
type StylesDefinition,
|
|
15
|
+
type UnpublishedComponent,
|
|
16
|
+
} from '../types';
|
|
17
|
+
import { loadComponents } from './thunks';
|
|
18
|
+
|
|
19
|
+
type GetComponentResponse = PublishedComponent[];
|
|
20
|
+
|
|
21
|
+
type Status = 'idle' | 'pending' | 'error';
|
|
22
|
+
|
|
23
|
+
type ComponentsState = {
|
|
24
|
+
data: PublishedComponent[];
|
|
25
|
+
unpublishedData: UnpublishedComponent[];
|
|
26
|
+
loadStatus: Status;
|
|
27
|
+
styles: StylesDefinition;
|
|
28
|
+
createdThisSession: Component[ 'uid' ][];
|
|
29
|
+
archivedData: PublishedComponent[];
|
|
30
|
+
path: ComponentsPathItem[];
|
|
31
|
+
currentComponentId: V1Document[ 'id' ] | null;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export type ComponentsSlice = SliceState< typeof slice >;
|
|
35
|
+
|
|
36
|
+
export type ComponentsPathItem = {
|
|
37
|
+
instanceId?: string;
|
|
38
|
+
componentId: V1Document[ 'id' ];
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export const initialState: ComponentsState = {
|
|
42
|
+
data: [],
|
|
43
|
+
unpublishedData: [],
|
|
44
|
+
loadStatus: 'idle',
|
|
45
|
+
styles: {},
|
|
46
|
+
createdThisSession: [],
|
|
47
|
+
archivedData: [],
|
|
48
|
+
path: [],
|
|
49
|
+
currentComponentId: null,
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export const SLICE_NAME = 'components';
|
|
53
|
+
|
|
54
|
+
export const slice = createSlice( {
|
|
55
|
+
name: SLICE_NAME,
|
|
56
|
+
initialState,
|
|
57
|
+
reducers: {
|
|
58
|
+
add: ( state, { payload }: PayloadAction< PublishedComponent | PublishedComponent[] > ) => {
|
|
59
|
+
if ( Array.isArray( payload ) ) {
|
|
60
|
+
state.data = [ ...state.data, ...payload ];
|
|
61
|
+
} else {
|
|
62
|
+
state.data.unshift( payload );
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
load: ( state, { payload }: PayloadAction< PublishedComponent[] > ) => {
|
|
66
|
+
state.data = payload;
|
|
67
|
+
},
|
|
68
|
+
addUnpublished: ( state, { payload }: PayloadAction< UnpublishedComponent > ) => {
|
|
69
|
+
state.unpublishedData.unshift( payload );
|
|
70
|
+
},
|
|
71
|
+
resetUnpublished: ( state ) => {
|
|
72
|
+
state.unpublishedData = [];
|
|
73
|
+
},
|
|
74
|
+
removeStyles( state, { payload }: PayloadAction< { id: ComponentId } > ) {
|
|
75
|
+
const { [ payload.id ]: _, ...rest } = state.styles;
|
|
76
|
+
|
|
77
|
+
state.styles = rest;
|
|
78
|
+
},
|
|
79
|
+
addStyles: ( state, { payload } ) => {
|
|
80
|
+
state.styles = { ...state.styles, ...payload };
|
|
81
|
+
},
|
|
82
|
+
addCreatedThisSession: ( state, { payload }: PayloadAction< string > ) => {
|
|
83
|
+
state.createdThisSession.push( payload );
|
|
84
|
+
},
|
|
85
|
+
archive: ( state, { payload }: PayloadAction< number > ) => {
|
|
86
|
+
state.data = state.data.filter( ( component ) => {
|
|
87
|
+
const isArchived = component.id === payload;
|
|
88
|
+
if ( isArchived ) {
|
|
89
|
+
state.archivedData.push( component );
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return ! isArchived;
|
|
93
|
+
} );
|
|
94
|
+
},
|
|
95
|
+
setCurrentComponentId: ( state, { payload }: PayloadAction< V1Document[ 'id' ] | null > ) => {
|
|
96
|
+
state.currentComponentId = payload;
|
|
97
|
+
},
|
|
98
|
+
setPath: ( state, { payload }: PayloadAction< ComponentsPathItem[] > ) => {
|
|
99
|
+
state.path = payload;
|
|
100
|
+
},
|
|
101
|
+
setOverridableProps: (
|
|
102
|
+
state,
|
|
103
|
+
{ payload }: PayloadAction< { componentId: ComponentId; overridableProps: OverridableProps } >
|
|
104
|
+
) => {
|
|
105
|
+
const component = state.data.find( ( comp ) => comp.id === payload.componentId );
|
|
106
|
+
|
|
107
|
+
if ( ! component ) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
component.overridableProps = payload.overridableProps;
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
extraReducers: ( builder ) => {
|
|
115
|
+
builder.addCase( loadComponents.fulfilled, ( state, { payload }: PayloadAction< GetComponentResponse > ) => {
|
|
116
|
+
state.data = payload;
|
|
117
|
+
state.loadStatus = 'idle';
|
|
118
|
+
} );
|
|
119
|
+
builder.addCase( loadComponents.pending, ( state ) => {
|
|
120
|
+
state.loadStatus = 'pending';
|
|
121
|
+
} );
|
|
122
|
+
builder.addCase( loadComponents.rejected, ( state ) => {
|
|
123
|
+
state.loadStatus = 'error';
|
|
124
|
+
} );
|
|
125
|
+
},
|
|
126
|
+
} );
|
|
127
|
+
|
|
128
|
+
const selectData = ( state: ComponentsSlice ) => state[ SLICE_NAME ].data;
|
|
129
|
+
const selectArchivedData = ( state: ComponentsSlice ) => state[ SLICE_NAME ].archivedData;
|
|
130
|
+
const selectLoadStatus = ( state: ComponentsSlice ) => state[ SLICE_NAME ].loadStatus;
|
|
131
|
+
const selectStylesDefinitions = ( state: ComponentsSlice ) => state[ SLICE_NAME ].styles ?? {};
|
|
132
|
+
const selectUnpublishedData = ( state: ComponentsSlice ) => state[ SLICE_NAME ].unpublishedData;
|
|
133
|
+
const getCreatedThisSession = ( state: ComponentsSlice ) => state[ SLICE_NAME ].createdThisSession;
|
|
134
|
+
const getPath = ( state: ComponentsSlice ) => state[ SLICE_NAME ].path;
|
|
135
|
+
const getCurrentComponentId = ( state: ComponentsSlice ) => state[ SLICE_NAME ].currentComponentId;
|
|
136
|
+
const selectComponent = ( state: ComponentsSlice, componentId: ComponentId ) =>
|
|
137
|
+
state[ SLICE_NAME ].data.find( ( component ) => component.id === componentId );
|
|
138
|
+
|
|
139
|
+
export const selectComponents = createSelector(
|
|
140
|
+
selectData,
|
|
141
|
+
selectUnpublishedData,
|
|
142
|
+
( data: PublishedComponent[], unpublishedData: UnpublishedComponent[] ) => [
|
|
143
|
+
...unpublishedData.map( ( item ) => ( { uid: item.uid, name: item.name } ) ),
|
|
144
|
+
...data,
|
|
145
|
+
]
|
|
146
|
+
);
|
|
147
|
+
export const selectUnpublishedComponents = createSelector(
|
|
148
|
+
selectUnpublishedData,
|
|
149
|
+
( unpublishedData: UnpublishedComponent[] ) => unpublishedData
|
|
150
|
+
);
|
|
151
|
+
export const selectLoadIsPending = createSelector( selectLoadStatus, ( status ) => status === 'pending' );
|
|
152
|
+
export const selectLoadIsError = createSelector( selectLoadStatus, ( status ) => status === 'error' );
|
|
153
|
+
export const selectStyles = ( state: ComponentsSlice ) => state[ SLICE_NAME ].styles ?? {};
|
|
154
|
+
export const selectFlatStyles = createSelector( selectStylesDefinitions, ( data ) => Object.values( data ).flat() );
|
|
155
|
+
export const selectCreatedThisSession = createSelector(
|
|
156
|
+
getCreatedThisSession,
|
|
157
|
+
( createdThisSession ) => createdThisSession
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
const DEFAULT_OVERRIDABLE_PROPS: OverridableProps = {
|
|
161
|
+
props: {},
|
|
162
|
+
groups: {
|
|
163
|
+
items: {},
|
|
164
|
+
order: [],
|
|
165
|
+
},
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
export const selectOverridableProps = createSelector(
|
|
169
|
+
selectComponent,
|
|
170
|
+
( component: PublishedComponent | undefined ) => {
|
|
171
|
+
if ( ! component ) {
|
|
172
|
+
return undefined;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return component.overridableProps ?? DEFAULT_OVERRIDABLE_PROPS;
|
|
176
|
+
}
|
|
177
|
+
);
|
|
178
|
+
export const selectIsOverridablePropsLoaded = createSelector(
|
|
179
|
+
selectComponent,
|
|
180
|
+
( component: PublishedComponent | undefined ) => {
|
|
181
|
+
return !! component?.overridableProps;
|
|
182
|
+
}
|
|
183
|
+
);
|
|
184
|
+
export const selectPath = createSelector( getPath, ( path ) => path );
|
|
185
|
+
export const selectCurrentComponentId = createSelector(
|
|
186
|
+
getCurrentComponentId,
|
|
187
|
+
( currentComponentId ) => currentComponentId
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
export const selectArchivedComponents = createSelector(
|
|
191
|
+
selectArchivedData,
|
|
192
|
+
( archivedData: PublishedComponent[] ) => archivedData
|
|
193
|
+
);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { __createAsyncThunk as createAsyncThunk } from '@elementor/store';
|
|
2
|
+
|
|
3
|
+
import { apiClient } from '../api';
|
|
4
|
+
|
|
5
|
+
const loadComponents = createAsyncThunk( 'components/load', async () => {
|
|
6
|
+
const response = await apiClient.get();
|
|
7
|
+
return response;
|
|
8
|
+
} );
|
|
9
|
+
|
|
10
|
+
export { loadComponents };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { type V1Document } from '@elementor/editor-documents';
|
|
2
|
+
import { type V1Element, type V1ElementData } from '@elementor/editor-elements';
|
|
3
|
+
|
|
4
|
+
import { type DocumentSaveStatus } from '../types';
|
|
5
|
+
import { createComponentsBeforeSave } from './create-components-before-save';
|
|
6
|
+
import { setComponentOverridablePropsSettingsBeforeSave } from './set-component-overridable-props-settings-before-save';
|
|
7
|
+
import { updateArchivedComponentBeforeSave } from './update-archived-component-before-save';
|
|
8
|
+
import { updateComponentsBeforeSave } from './update-components-before-save';
|
|
9
|
+
|
|
10
|
+
type Options = {
|
|
11
|
+
container: V1Element & {
|
|
12
|
+
document: V1Document;
|
|
13
|
+
model: {
|
|
14
|
+
get: ( key: 'elements' ) => {
|
|
15
|
+
toJSON: () => V1ElementData[];
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
status: DocumentSaveStatus;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export const beforeSave = ( { container, status }: Options ) => {
|
|
23
|
+
const elements = container.model.get( 'elements' )?.toJSON() ?? [];
|
|
24
|
+
|
|
25
|
+
return Promise.all( [
|
|
26
|
+
updateArchivedComponentBeforeSave(),
|
|
27
|
+
createComponentsBeforeSave( { elements, status } ),
|
|
28
|
+
updateComponentsBeforeSave( { elements, status } ),
|
|
29
|
+
setComponentOverridablePropsSettingsBeforeSave( { container } ),
|
|
30
|
+
] );
|
|
31
|
+
};
|
|
@@ -0,0 +1,102 @@
|
|
|
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 { selectUnpublishedComponents, slice } from '../store/store';
|
|
6
|
+
import { type ComponentInstancePropValue, type DocumentSaveStatus, type UnpublishedComponent } from '../types';
|
|
7
|
+
|
|
8
|
+
export async function createComponentsBeforeSave( {
|
|
9
|
+
elements,
|
|
10
|
+
status,
|
|
11
|
+
}: {
|
|
12
|
+
elements: V1ElementData[];
|
|
13
|
+
status: DocumentSaveStatus;
|
|
14
|
+
} ) {
|
|
15
|
+
const unpublishedComponents = selectUnpublishedComponents( getState() );
|
|
16
|
+
|
|
17
|
+
if ( ! unpublishedComponents.length ) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
const uidToComponentId = await createComponents( unpublishedComponents, status );
|
|
23
|
+
|
|
24
|
+
updateComponentInstances( elements, uidToComponentId );
|
|
25
|
+
|
|
26
|
+
dispatch(
|
|
27
|
+
slice.actions.add(
|
|
28
|
+
unpublishedComponents.map( ( component ) => ( {
|
|
29
|
+
id: uidToComponentId.get( component.uid ) as number,
|
|
30
|
+
name: component.name,
|
|
31
|
+
uid: component.uid,
|
|
32
|
+
} ) )
|
|
33
|
+
)
|
|
34
|
+
);
|
|
35
|
+
dispatch( slice.actions.resetUnpublished() );
|
|
36
|
+
} catch ( error ) {
|
|
37
|
+
throw new Error( `Failed to publish components and update component instances: ${ error }` );
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async function createComponents(
|
|
42
|
+
components: UnpublishedComponent[],
|
|
43
|
+
status: DocumentSaveStatus
|
|
44
|
+
): Promise< Map< string, number > > {
|
|
45
|
+
const response = await apiClient.create( {
|
|
46
|
+
status,
|
|
47
|
+
items: components.map( ( component ) => ( {
|
|
48
|
+
uid: component.uid,
|
|
49
|
+
title: component.name,
|
|
50
|
+
elements: component.elements,
|
|
51
|
+
} ) ),
|
|
52
|
+
} );
|
|
53
|
+
|
|
54
|
+
const map = new Map< string, number >();
|
|
55
|
+
|
|
56
|
+
Object.entries( response ).forEach( ( [ key, value ] ) => {
|
|
57
|
+
map.set( key, value );
|
|
58
|
+
} );
|
|
59
|
+
|
|
60
|
+
return map;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function updateComponentInstances( elements: V1ElementData[], uidToComponentId: Map< string, number > ): void {
|
|
64
|
+
elements.forEach( ( element ) => {
|
|
65
|
+
const { shouldUpdate, newComponentId } = shouldUpdateElement( element, uidToComponentId );
|
|
66
|
+
if ( shouldUpdate ) {
|
|
67
|
+
updateElementComponentId( element.id, newComponentId );
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if ( element.elements ) {
|
|
71
|
+
updateComponentInstances( element.elements, uidToComponentId );
|
|
72
|
+
}
|
|
73
|
+
} );
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function shouldUpdateElement(
|
|
77
|
+
element: V1ElementData,
|
|
78
|
+
uidToComponentId: Map< string, number >
|
|
79
|
+
): { shouldUpdate: true; newComponentId: number } | { shouldUpdate: false; newComponentId: null } {
|
|
80
|
+
if ( element.widgetType === 'e-component' ) {
|
|
81
|
+
const currentComponentId = ( element.settings?.component_instance as ComponentInstancePropValue< string > )
|
|
82
|
+
?.value?.component_id;
|
|
83
|
+
|
|
84
|
+
if ( currentComponentId && uidToComponentId.has( currentComponentId ) ) {
|
|
85
|
+
return { shouldUpdate: true, newComponentId: uidToComponentId.get( currentComponentId ) as number };
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return { shouldUpdate: false, newComponentId: null };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function updateElementComponentId( elementId: string, componentId: number ): void {
|
|
92
|
+
updateElementSettings( {
|
|
93
|
+
id: elementId,
|
|
94
|
+
props: {
|
|
95
|
+
component_instance: {
|
|
96
|
+
$$type: 'component-instance',
|
|
97
|
+
value: { component_id: componentId },
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
withHistory: false,
|
|
101
|
+
} );
|
|
102
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
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 { COMPONENT_DOCUMENT_TYPE } from '../components/consts';
|
|
6
|
+
import { selectOverridableProps } from '../store/store';
|
|
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
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
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 { selectArchivedComponents } from '../store/store';
|
|
6
|
+
|
|
7
|
+
const failedNotification = ( message: string ): NotificationData => ( {
|
|
8
|
+
type: 'error',
|
|
9
|
+
message: `Failed to archive components: ${ message }`,
|
|
10
|
+
id: 'failed-archived-components-notification',
|
|
11
|
+
} );
|
|
12
|
+
|
|
13
|
+
const successNotification = ( message: string ): NotificationData => ( {
|
|
14
|
+
type: 'success',
|
|
15
|
+
message: `Successfully archived components: ${ message }`,
|
|
16
|
+
id: 'success-archived-components-notification',
|
|
17
|
+
} );
|
|
18
|
+
|
|
19
|
+
export const updateArchivedComponentBeforeSave = async () => {
|
|
20
|
+
try {
|
|
21
|
+
const archivedComponents = selectArchivedComponents( getState() );
|
|
22
|
+
|
|
23
|
+
if ( ! archivedComponents.length ) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const result = await apiClient.updateArchivedComponents(
|
|
28
|
+
archivedComponents.map( ( component ) => component.id )
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
const failedIds = result.failedIds.join( ', ' );
|
|
32
|
+
const successIds = result.successIds.join( ', ' );
|
|
33
|
+
|
|
34
|
+
if ( failedIds ) {
|
|
35
|
+
notify( failedNotification( failedIds ) );
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if ( successIds ) {
|
|
39
|
+
notify( successNotification( successIds ) );
|
|
40
|
+
}
|
|
41
|
+
} catch ( error ) {
|
|
42
|
+
throw new Error( `Failed to update archived components: ${ error }` );
|
|
43
|
+
}
|
|
44
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { isDocumentDirty } from '@elementor/editor-documents';
|
|
2
|
+
import { type V1ElementData } from '@elementor/editor-elements';
|
|
3
|
+
|
|
4
|
+
import { apiClient } from '../api';
|
|
5
|
+
import { type DocumentSaveStatus } from '../types';
|
|
6
|
+
import { getComponentDocumentData, invalidateComponentDocumentData } from '../utils/component-document-data';
|
|
7
|
+
import { getComponentIds } from '../utils/get-component-ids';
|
|
8
|
+
|
|
9
|
+
type Options = {
|
|
10
|
+
status: DocumentSaveStatus;
|
|
11
|
+
elements: V1ElementData[];
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export async function updateComponentsBeforeSave( { status, elements }: Options ) {
|
|
15
|
+
if ( status !== 'publish' ) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const componentIds = await getComponentIds( elements );
|
|
20
|
+
|
|
21
|
+
const componentDocumentData = await Promise.all( componentIds.map( getComponentDocumentData ) );
|
|
22
|
+
|
|
23
|
+
const draftIds = componentDocumentData
|
|
24
|
+
.filter( ( document ) => !! document )
|
|
25
|
+
.filter( isDocumentDirty )
|
|
26
|
+
.map( ( document ) => document.id );
|
|
27
|
+
|
|
28
|
+
if ( draftIds.length === 0 ) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
await apiClient.updateStatuses( draftIds, 'publish' );
|
|
33
|
+
|
|
34
|
+
draftIds.forEach( ( id ) => invalidateComponentDocumentData( id ) );
|
|
35
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -1,3 +1,86 @@
|
|
|
1
|
+
import { type V1ElementData } from '@elementor/editor-elements';
|
|
2
|
+
import { type PropValue, type TransformablePropValue } from '@elementor/editor-props';
|
|
3
|
+
import type { StyleDefinition } from '@elementor/editor-styles';
|
|
4
|
+
|
|
1
5
|
export type ComponentFormValues = {
|
|
2
6
|
componentName: string;
|
|
3
7
|
};
|
|
8
|
+
|
|
9
|
+
export type ComponentId = number;
|
|
10
|
+
|
|
11
|
+
export type StylesDefinition = Record< ComponentId, StyleDefinition[] >;
|
|
12
|
+
|
|
13
|
+
export type Component = PublishedComponent | UnpublishedComponent;
|
|
14
|
+
|
|
15
|
+
export type PublishedComponent = BaseComponent & {
|
|
16
|
+
id: number;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export type UnpublishedComponent = BaseComponent & {
|
|
20
|
+
elements: V1ElementData[];
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export type OverridableProp = {
|
|
24
|
+
overrideKey: string;
|
|
25
|
+
label: string;
|
|
26
|
+
elementId: string;
|
|
27
|
+
propKey: string;
|
|
28
|
+
elType: string;
|
|
29
|
+
widgetType: string;
|
|
30
|
+
originValue: PropValue;
|
|
31
|
+
groupId: string;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export type OverridablePropsGroup = {
|
|
35
|
+
id: string;
|
|
36
|
+
label: string;
|
|
37
|
+
props: string[];
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export type OverridableProps = {
|
|
41
|
+
props: Record< string, OverridableProp >;
|
|
42
|
+
groups: {
|
|
43
|
+
items: Record< string, OverridablePropsGroup >;
|
|
44
|
+
order: string[];
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
type BaseComponent = {
|
|
49
|
+
uid: string;
|
|
50
|
+
name: string;
|
|
51
|
+
overridableProps?: OverridableProps;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export type DocumentStatus = 'publish' | 'draft';
|
|
55
|
+
export type DocumentSaveStatus = DocumentStatus | 'autosave';
|
|
56
|
+
|
|
57
|
+
export type ExtendedWindow = Window & {
|
|
58
|
+
elementorCommon: Record< string, unknown > & {
|
|
59
|
+
eventsManager: {
|
|
60
|
+
config: {
|
|
61
|
+
locations: Record< string, string >;
|
|
62
|
+
secondaryLocations: Record< string, string >;
|
|
63
|
+
triggers: Record< string, string >;
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
export type ComponentInstancePropValue< TComponentId extends number | string = number | string > =
|
|
70
|
+
TransformablePropValue<
|
|
71
|
+
'component-instance',
|
|
72
|
+
{
|
|
73
|
+
component_id: TComponentId;
|
|
74
|
+
overrides?: ComponentOverride[];
|
|
75
|
+
}
|
|
76
|
+
>;
|
|
77
|
+
|
|
78
|
+
type ComponentOverride = {
|
|
79
|
+
override_key: string;
|
|
80
|
+
value: TransformablePropValue< string >;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
export type ComponentOverridable = {
|
|
84
|
+
override_key: string;
|
|
85
|
+
origin_value: TransformablePropValue< string >;
|
|
86
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type Document, getV1DocumentsManager } from '@elementor/editor-documents';
|
|
2
|
+
|
|
3
|
+
type ComponentDocumentData = Document;
|
|
4
|
+
|
|
5
|
+
export const getComponentDocumentData = async ( id: number ) => {
|
|
6
|
+
const documentManager = getV1DocumentsManager();
|
|
7
|
+
|
|
8
|
+
try {
|
|
9
|
+
return await documentManager.request< ComponentDocumentData >( id );
|
|
10
|
+
} catch {
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const invalidateComponentDocumentData = ( id: number ) => {
|
|
16
|
+
const documentManager = getV1DocumentsManager();
|
|
17
|
+
|
|
18
|
+
documentManager.invalidateCache( id );
|
|
19
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type V1ElementData } from '@elementor/editor-elements';
|
|
2
|
+
|
|
3
|
+
import { TYPE } from '../create-component-type';
|
|
4
|
+
import { type ComponentInstancePropValue } from '../types';
|
|
5
|
+
import { getComponentDocumentData } from './component-document-data';
|
|
6
|
+
|
|
7
|
+
export const getComponentIds = async ( elements: V1ElementData[] ) => {
|
|
8
|
+
const components = elements.map( async ( { widgetType, elType, elements: childElements, settings } ) => {
|
|
9
|
+
const ids: number[] = [];
|
|
10
|
+
|
|
11
|
+
const isComponent = [ widgetType, elType ].includes( TYPE );
|
|
12
|
+
|
|
13
|
+
if ( isComponent ) {
|
|
14
|
+
const componentId = ( settings?.component_instance as ComponentInstancePropValue< number > )?.value
|
|
15
|
+
?.component_id;
|
|
16
|
+
|
|
17
|
+
const document = await getComponentDocumentData( componentId );
|
|
18
|
+
|
|
19
|
+
childElements = document?.elements;
|
|
20
|
+
|
|
21
|
+
if ( Boolean( componentId ) ) {
|
|
22
|
+
ids.push( componentId );
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if ( !! childElements?.length ) {
|
|
27
|
+
ids.push( ...( await getComponentIds( childElements ) ) );
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return ids;
|
|
31
|
+
} );
|
|
32
|
+
|
|
33
|
+
const result = ( await Promise.all( components ) ).flat();
|
|
34
|
+
|
|
35
|
+
return Array.from( new Set( result ) );
|
|
36
|
+
};
|