@elementor/editor-components 3.33.0-170 → 3.33.0-172

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.
@@ -6,9 +6,8 @@ import { StarIcon } from '@elementor/icons';
6
6
  import { Alert, Button, FormLabel, Grid, Popover, Snackbar, Stack, TextField, Typography } from '@elementor/ui';
7
7
  import { __ } from '@wordpress/i18n';
8
8
 
9
- import { type CreateComponentResponse } from '../../api';
10
9
  import { useComponents } from '../../hooks/use-components';
11
- import { useCreateComponentMutation } from '../../hooks/use-create-component';
10
+ import { useCreateComponent } from '../../hooks/use-create-component';
12
11
  import { type ComponentFormValues } from '../../types';
13
12
  import { useForm } from './hooks/use-form';
14
13
  import { createBaseComponentSchema, createSubmitComponentSchema } from './utils/component-form-schema';
@@ -35,8 +34,7 @@ export function CreateComponentForm() {
35
34
 
36
35
  const [ resultNotification, setResultNotification ] = useState< ResultNotification | null >( null );
37
36
 
38
- const { mutate: createComponent, isPending } = useCreateComponentMutation();
39
-
37
+ const { createComponent, isPending } = useCreateComponent();
40
38
  useEffect( () => {
41
39
  const OPEN_SAVE_AS_COMPONENT_FORM_EVENT = 'elementor/editor/open-save-as-component-form';
42
40
 
@@ -57,43 +55,39 @@ export function CreateComponentForm() {
57
55
  throw new Error( `Can't save element as component: element not found` );
58
56
  }
59
57
 
60
- createComponent(
61
- {
58
+ try {
59
+ const result = await createComponent( {
62
60
  name: values.componentName,
63
61
  content: [ element.element.model.toJSON( { remove: [ 'default' ] } ) ],
64
- },
65
- {
66
- onSuccess: ( result: CreateComponentResponse ) => {
67
- if ( ! element ) {
68
- throw new Error( `Can't replace element with component: element not found` );
69
- }
70
-
71
- replaceElementWithComponent( element.element, {
72
- id: result.component_id,
73
- name: values.componentName,
74
- } );
75
-
76
- setResultNotification( {
77
- show: true,
78
- // Translators: %1$s: Component name, %2$s: Component ID
79
- message: __( 'Component saved successfully as: %1$s (ID: %2$s)', 'elementor' )
80
- .replace( '%1$s', values.componentName )
81
- .replace( '%2$s', result.component_id.toString() ),
82
- type: 'success',
83
- } );
84
-
85
- resetAndClosePopup();
86
- },
87
- onError: () => {
88
- const errorMessage = __( 'Failed to save component. Please try again.', 'elementor' );
89
- setResultNotification( {
90
- show: true,
91
- message: errorMessage,
92
- type: 'error',
93
- } );
94
- },
62
+ } );
63
+
64
+ if ( ! element ) {
65
+ throw new Error( `Can't replace element with component: element not found` );
95
66
  }
96
- );
67
+
68
+ replaceElementWithComponent( element.element, {
69
+ id: result.component_id,
70
+ name: values.componentName,
71
+ } );
72
+
73
+ setResultNotification( {
74
+ show: true,
75
+ // Translators: %1$s: Component name, %2$s: Component ID
76
+ message: __( 'Component saved successfully as: %1$s (ID: %2$s)', 'elementor' )
77
+ .replace( '%1$s', values.componentName )
78
+ .replace( '%2$s', result.component_id.toString() ),
79
+ type: 'success',
80
+ } );
81
+
82
+ resetAndClosePopup();
83
+ } catch {
84
+ const errorMessage = __( 'Failed to save component. Please try again.', 'elementor' );
85
+ setResultNotification( {
86
+ show: true,
87
+ message: errorMessage,
88
+ type: 'error',
89
+ } );
90
+ }
97
91
  };
98
92
 
99
93
  const resetAndClosePopup = () => {
@@ -146,7 +140,7 @@ const Form = ( {
146
140
  } ) => {
147
141
  const { values, errors, isValid, handleChange, validateForm } = useForm< ComponentFormValues >( initialValues );
148
142
 
149
- const { data: components } = useComponents();
143
+ const { components } = useComponents();
150
144
 
151
145
  const existingComponentNames = useMemo( () => {
152
146
  return components?.map( ( component ) => component.name ) ?? [];
@@ -1,13 +1,10 @@
1
- import { useQuery } from '@elementor/query';
1
+ import { __useSelector as useSelector } from '@elementor/store';
2
2
 
3
- import { apiClient } from '../api';
4
-
5
- export const COMPONENTS_QUERY_KEY = 'components';
3
+ import { selectComponents, selectLoadIsPending } from '../store';
6
4
 
7
5
  export const useComponents = () => {
8
- return useQuery( {
9
- queryKey: [ COMPONENTS_QUERY_KEY ],
10
- queryFn: apiClient.get,
11
- staleTime: Infinity,
12
- } );
6
+ const components = useSelector( selectComponents );
7
+ const isLoading = useSelector( selectLoadIsPending );
8
+
9
+ return { components, isLoading };
13
10
  };
@@ -1,13 +1,22 @@
1
- import { useMutation, useQueryClient } from '@elementor/query';
1
+ import { __useDispatch as useDispatch, __useSelector as useSelector, type AnyAction } from '@elementor/store';
2
2
 
3
- import { apiClient } from '../api';
4
- import { COMPONENTS_QUERY_KEY } from './use-components';
3
+ import { type CreateComponentPayload } from '../api';
4
+ import { selectCreateIsError, selectCreateIsPending } from '../store';
5
+ import { createComponent } from '../thunks';
5
6
 
6
- export const useCreateComponentMutation = () => {
7
- const queryClient = useQueryClient();
7
+ export const useCreateComponent = () => {
8
+ const dispatch = useDispatch();
9
+ const isPending = useSelector( selectCreateIsPending );
10
+ const isError = useSelector( selectCreateIsError );
8
11
 
9
- return useMutation( {
10
- mutationFn: apiClient.create,
11
- onSuccess: () => queryClient.invalidateQueries( { queryKey: [ COMPONENTS_QUERY_KEY ] } ),
12
- } );
12
+ const createComponentAction = async ( payload: CreateComponentPayload ) => {
13
+ const result = await dispatch( createComponent( payload ) as unknown as AnyAction );
14
+ return result.payload;
15
+ };
16
+
17
+ return {
18
+ createComponent: createComponentAction,
19
+ isPending,
20
+ isError,
21
+ };
13
22
  };
package/src/init.ts CHANGED
@@ -1,11 +1,16 @@
1
- import { injectIntoTop } from '@elementor/editor';
1
+ import { injectIntoLogic, injectIntoTop } from '@elementor/editor';
2
2
  import { injectTab } from '@elementor/editor-elements-panel';
3
+ import { __registerSlice as registerSlice } from '@elementor/store';
3
4
  import { __ } from '@wordpress/i18n';
4
5
 
5
6
  import { Components } from './components/components-tab/components';
6
7
  import { CreateComponentForm } from './components/create-component-form/create-component-form';
8
+ import { PopulateStore } from './populate-store';
9
+ import { slice } from './store';
7
10
 
8
11
  export function init() {
12
+ registerSlice( slice );
13
+
9
14
  injectTab( {
10
15
  id: 'components',
11
16
  label: __( 'Components', 'elementor' ),
@@ -16,4 +21,9 @@ export function init() {
16
21
  id: 'create-component-popup',
17
22
  component: CreateComponentForm,
18
23
  } );
24
+
25
+ injectIntoLogic( {
26
+ id: 'components-populate-store',
27
+ component: PopulateStore,
28
+ } );
19
29
  }
@@ -0,0 +1,12 @@
1
+ import { useEffect } from 'react';
2
+ import { __dispatch as dispatch } from '@elementor/store';
3
+
4
+ import { loadComponents } from './thunks';
5
+
6
+ export function PopulateStore() {
7
+ useEffect( () => {
8
+ dispatch( loadComponents() );
9
+ }, [] );
10
+
11
+ return null;
12
+ }
package/src/store.ts ADDED
@@ -0,0 +1,73 @@
1
+ import {
2
+ __createSelector as createSelector,
3
+ __createSlice as createSlice,
4
+ type PayloadAction,
5
+ type SliceState,
6
+ } from '@elementor/store';
7
+
8
+ import { createComponent, loadComponents } from './thunks';
9
+ import { type Component } from './types';
10
+
11
+ type GetComponentResponse = Component[];
12
+
13
+ type Status = 'idle' | 'pending' | 'error';
14
+ export type ComponentsState = {
15
+ data: Component[];
16
+ loadStatus: Status;
17
+ createStatus: Status;
18
+ };
19
+
20
+ export const initialState: ComponentsState = {
21
+ data: [],
22
+ loadStatus: 'idle',
23
+ createStatus: 'idle',
24
+ };
25
+
26
+ const SLICE_NAME = 'components';
27
+ export const slice = createSlice( {
28
+ name: SLICE_NAME,
29
+ initialState,
30
+ reducers: {
31
+ add: ( state, { payload } ) => {
32
+ state.data.push( payload );
33
+ },
34
+ load: ( state, { payload } ) => {
35
+ state.data = payload;
36
+ },
37
+ },
38
+ extraReducers: ( builder ) => {
39
+ builder.addCase( loadComponents.fulfilled, ( state, { payload }: PayloadAction< GetComponentResponse > ) => {
40
+ state.data = payload;
41
+ state.loadStatus = 'idle';
42
+ } );
43
+ builder.addCase( loadComponents.pending, ( state ) => {
44
+ state.loadStatus = 'pending';
45
+ } );
46
+ builder.addCase( loadComponents.rejected, ( state ) => {
47
+ state.loadStatus = 'error';
48
+ } );
49
+ builder.addCase( createComponent.fulfilled, ( state, { payload, meta } ) => {
50
+ state.createStatus = 'idle';
51
+ state.data.push( {
52
+ id: payload.component_id,
53
+ name: meta.arg.name,
54
+ } );
55
+ } );
56
+ builder.addCase( createComponent.pending, ( state ) => {
57
+ state.createStatus = 'pending';
58
+ } );
59
+ builder.addCase( createComponent.rejected, ( state ) => {
60
+ state.createStatus = 'error';
61
+ } );
62
+ },
63
+ } );
64
+
65
+ const selectData = ( state: SliceState< typeof slice > ) => state[ SLICE_NAME ].data;
66
+ const selectLoadStatus = ( state: SliceState< typeof slice > ) => state[ SLICE_NAME ].loadStatus;
67
+ const selectCreateStatus = ( state: SliceState< typeof slice > ) => state[ SLICE_NAME ].createStatus;
68
+
69
+ export const selectComponents = createSelector( selectData, ( data: Component[] ) => data );
70
+ export const selectLoadIsPending = createSelector( selectLoadStatus, ( status ) => status === 'pending' );
71
+ export const selectLoadIsError = createSelector( selectLoadStatus, ( status ) => status === 'error' );
72
+ export const selectCreateIsPending = createSelector( selectCreateStatus, ( status ) => status === 'pending' );
73
+ export const selectCreateIsError = createSelector( selectCreateStatus, ( status ) => status === 'error' );
package/src/thunks.ts ADDED
@@ -0,0 +1,18 @@
1
+ import { __createAsyncThunk as createAsyncThunk } from '@elementor/store';
2
+
3
+ import { apiClient, type CreateComponentPayload, type CreateComponentResponse } from './api';
4
+
5
+ const createComponent = createAsyncThunk< CreateComponentResponse, CreateComponentPayload >(
6
+ 'components/create',
7
+ async ( payload: CreateComponentPayload ) => {
8
+ const response = await apiClient.create( payload );
9
+ return { ...response, name: payload.name };
10
+ }
11
+ );
12
+
13
+ const loadComponents = createAsyncThunk( 'components/load', async () => {
14
+ const response = await apiClient.get();
15
+ return response;
16
+ } );
17
+
18
+ export { createComponent, loadComponents };