@elementor/editor-components 3.33.0-252 → 3.33.0-254

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.
@@ -25,7 +25,7 @@ export function ComponentsList() {
25
25
  return (
26
26
  <List sx={ { display: 'flex', flexDirection: 'column', gap: 1, px: 2 } }>
27
27
  { components.map( ( component ) => (
28
- <ComponentItem key={ component.id } component={ component } />
28
+ <ComponentItem key={ component.uid } component={ component } />
29
29
  ) ) }
30
30
  </List>
31
31
  );
@@ -0,0 +1 @@
1
+ export const COMPONENT_DOCUMENT_TYPE = 'elementor_component';
@@ -1,10 +1,11 @@
1
1
  import * as React from 'react';
2
2
  import { useEffect, useMemo, useState } from 'react';
3
- import { getElementLabel, type V1Element } from '@elementor/editor-elements';
3
+ import { getElementLabel, type V1Element, type V1ElementData } from '@elementor/editor-elements';
4
4
  import { ThemeProvider } from '@elementor/editor-ui';
5
5
  import { StarIcon } from '@elementor/icons';
6
6
  import { __useDispatch as useDispatch } from '@elementor/store';
7
7
  import { Alert, Button, FormLabel, Grid, Popover, Snackbar, Stack, TextField, Typography } from '@elementor/ui';
8
+ import { generateUniqueId } from '@elementor/utils';
8
9
  import { __ } from '@wordpress/i18n';
9
10
 
10
11
  import { useComponents } from '../../hooks/use-components';
@@ -52,33 +53,32 @@ export function CreateComponentForm() {
52
53
  };
53
54
  }, [] );
54
55
 
55
- const handleSave = async ( values: ComponentFormValues ) => {
56
+ const handleSave = ( values: ComponentFormValues ) => {
56
57
  try {
57
58
  if ( ! element ) {
58
59
  throw new Error( `Can't save element as component: element not found` );
59
60
  }
60
61
 
61
- const tempId = generateTempId();
62
+ const uid = generateUniqueId( 'component' );
62
63
 
63
64
  dispatch(
64
65
  slice.actions.addUnpublished( {
65
- id: tempId,
66
+ uid,
66
67
  name: values.componentName,
67
- elements: [ element.element.model.toJSON( { remove: [ 'default' ] } ) ],
68
+ elements: [ element.element.model.toJSON( { remove: [ 'default' ] } ) as V1ElementData ],
68
69
  } )
69
70
  );
70
71
 
71
- replaceElementWithComponent( element.element, {
72
- id: tempId,
73
- name: values.componentName,
74
- } );
72
+ dispatch( slice.actions.addCreatedThisSession( uid ) );
73
+
74
+ replaceElementWithComponent( element.element, { uid, name: values.componentName } );
75
75
 
76
76
  setResultNotification( {
77
77
  show: true,
78
- // Translators: %1$s: Component name, %2$s: Component temp ID
79
- message: __( 'Component saved successfully as: %1$s (temp ID: %2$s)', 'elementor' )
78
+ // Translators: %1$s: Component name, %2$s: Component UID
79
+ message: __( 'Component saved successfully as: %1$s (UID: %2$s)', 'elementor' )
80
80
  .replace( '%1$s', values.componentName )
81
- .replace( '%2$s', tempId.toString() ),
81
+ .replace( '%2$s', uid ),
82
82
  type: 'success',
83
83
  } );
84
84
 
@@ -98,11 +98,15 @@ export function CreateComponentForm() {
98
98
  setAnchorPosition( undefined );
99
99
  };
100
100
 
101
+ const cancelSave = () => {
102
+ resetAndClosePopup();
103
+ };
104
+
101
105
  return (
102
106
  <ThemeProvider>
103
107
  <Popover
104
108
  open={ element !== null }
105
- onClose={ resetAndClosePopup }
109
+ onClose={ cancelSave }
106
110
  anchorReference="anchorPosition"
107
111
  anchorPosition={ anchorPosition }
108
112
  >
@@ -110,7 +114,7 @@ export function CreateComponentForm() {
110
114
  <Form
111
115
  initialValues={ { componentName: element.elementLabel } }
112
116
  handleSave={ handleSave }
113
- closePopup={ resetAndClosePopup }
117
+ closePopup={ cancelSave }
114
118
  />
115
119
  ) }
116
120
  </Popover>
@@ -215,7 +219,3 @@ const Form = ( {
215
219
  </Stack>
216
220
  );
217
221
  };
218
-
219
- export const generateTempId = () => {
220
- return Date.now() + Math.floor( Math.random() * 1000000 );
221
- };
@@ -1,8 +1,12 @@
1
- import { replaceElement, type V1Element } from '@elementor/editor-elements';
1
+ import { replaceElement, type V1Element, type V1ElementModelProps } from '@elementor/editor-elements';
2
2
 
3
- import { type Component } from '../../../types';
3
+ type ComponentInstanceParams = {
4
+ id?: number;
5
+ name: string;
6
+ uid: string;
7
+ };
4
8
 
5
- export const replaceElementWithComponent = ( element: V1Element, component: Component ) => {
9
+ export const replaceElementWithComponent = ( element: V1Element, component: ComponentInstanceParams ) => {
6
10
  replaceElement( {
7
11
  currentElement: element,
8
12
  newElement: createComponentModel( component ),
@@ -10,20 +14,19 @@ export const replaceElementWithComponent = ( element: V1Element, component: Comp
10
14
  } );
11
15
  };
12
16
 
13
- export const createComponentModel = (
14
- component: Component
15
- ): Parameters< typeof replaceElement >[ 0 ][ 'newElement' ] => {
17
+ export const createComponentModel = ( component: ComponentInstanceParams ): Omit< V1ElementModelProps, 'id' > => {
16
18
  return {
17
19
  elType: 'widget',
18
20
  widgetType: 'e-component',
19
21
  settings: {
20
22
  component: {
21
23
  $$type: 'component-id',
22
- value: component.id,
24
+ value: component.id ?? component.uid,
23
25
  },
24
26
  },
25
27
  editor_settings: {
26
28
  title: component.name,
29
+ component_uid: component.uid,
27
30
  },
28
31
  };
29
32
  };
@@ -7,10 +7,9 @@ import {
7
7
  __privateRunCommand as runCommand,
8
8
  commandEndEvent,
9
9
  } from '@elementor/editor-v1-adapters';
10
- import { __useSelector as useSelector } from '@elementor/store';
11
10
 
12
11
  import { apiClient } from '../../api';
13
- import { selectComponentsObject } from '../../store/store';
12
+ import { COMPONENT_DOCUMENT_TYPE } from '../consts';
14
13
  import { ComponentModal } from './component-modal';
15
14
 
16
15
  type ComponentsPathItem = {
@@ -41,7 +40,6 @@ function useHandleDocumentSwitches(
41
40
  path: ComponentsPathItem[],
42
41
  setPath: Dispatch< SetStateAction< ComponentsPathItem[] > >
43
42
  ) {
44
- const components = useSelector( selectComponentsObject );
45
43
  const documentsManager = getV1DocumentsManager();
46
44
 
47
45
  useEffect(
@@ -59,7 +57,7 @@ function useHandleDocumentSwitches(
59
57
  apiClient.unlockComponent( currentComponentId );
60
58
  }
61
59
 
62
- const isComponent = !! components?.[ nextDocument.id ];
60
+ const isComponent = nextDocument.config.type === COMPONENT_DOCUMENT_TYPE;
63
61
 
64
62
  if ( ! isComponent ) {
65
63
  setPath( [] );
@@ -69,7 +67,7 @@ function useHandleDocumentSwitches(
69
67
 
70
68
  setPath( getUpdatedComponentPath( path, nextDocument ) );
71
69
  } ),
72
- [ path, setPath, components, documentsManager ]
70
+ [ path, setPath, documentsManager ]
73
71
  );
74
72
  }
75
73
 
@@ -1,4 +1,3 @@
1
- import { type V1ElementData } from '@elementor/editor-elements';
2
1
  import {
3
2
  __createSelector as createSelector,
4
3
  __createSlice as createSlice,
@@ -6,22 +5,25 @@ import {
6
5
  type SliceState,
7
6
  } from '@elementor/store';
8
7
 
9
- import { type Component, type ComponentId, type StylesDefinition } from '../types';
8
+ import {
9
+ type Component,
10
+ type ComponentId,
11
+ type PublishedComponent,
12
+ type StylesDefinition,
13
+ type UnpublishedComponent,
14
+ } from '../types';
10
15
  import { loadComponents } from './thunks';
11
16
 
12
- type GetComponentResponse = Component[];
13
-
14
- export type UnpublishedComponent = Component & {
15
- elements: V1ElementData[];
16
- };
17
+ type GetComponentResponse = PublishedComponent[];
17
18
 
18
19
  type Status = 'idle' | 'pending' | 'error';
19
20
 
20
21
  type ComponentsState = {
21
- data: Component[];
22
+ data: PublishedComponent[];
22
23
  unpublishedData: UnpublishedComponent[];
23
24
  loadStatus: Status;
24
25
  styles: StylesDefinition;
26
+ createdThisSession: Component[ 'uid' ][];
25
27
  };
26
28
 
27
29
  type ComponentsSlice = SliceState< typeof slice >;
@@ -31,6 +33,7 @@ export const initialState: ComponentsState = {
31
33
  unpublishedData: [],
32
34
  loadStatus: 'idle',
33
35
  styles: {},
36
+ createdThisSession: [],
34
37
  };
35
38
 
36
39
  export const SLICE_NAME = 'components';
@@ -38,17 +41,17 @@ export const slice = createSlice( {
38
41
  name: SLICE_NAME,
39
42
  initialState,
40
43
  reducers: {
41
- add: ( state, { payload }: PayloadAction< Component | Component[] > ) => {
44
+ add: ( state, { payload }: PayloadAction< PublishedComponent | PublishedComponent[] > ) => {
42
45
  if ( Array.isArray( payload ) ) {
43
46
  state.data = [ ...state.data, ...payload ];
44
47
  } else {
45
48
  state.data.unshift( payload );
46
49
  }
47
50
  },
48
- load: ( state, { payload }: PayloadAction< Component[] > ) => {
51
+ load: ( state, { payload }: PayloadAction< PublishedComponent[] > ) => {
49
52
  state.data = payload;
50
53
  },
51
- addUnpublished: ( state, { payload } ) => {
54
+ addUnpublished: ( state, { payload }: PayloadAction< UnpublishedComponent > ) => {
52
55
  state.unpublishedData.unshift( payload );
53
56
  },
54
57
  resetUnpublished: ( state ) => {
@@ -62,6 +65,9 @@ export const slice = createSlice( {
62
65
  addStyles: ( state, { payload } ) => {
63
66
  state.styles = { ...state.styles, ...payload };
64
67
  },
68
+ addCreatedThisSession: ( state, { payload }: PayloadAction< string > ) => {
69
+ state.createdThisSession.push( payload );
70
+ },
65
71
  },
66
72
  extraReducers: ( builder ) => {
67
73
  builder.addCase( loadComponents.fulfilled, ( state, { payload }: PayloadAction< GetComponentResponse > ) => {
@@ -81,12 +87,13 @@ const selectData = ( state: ComponentsSlice ) => state[ SLICE_NAME ].data;
81
87
  const selectLoadStatus = ( state: ComponentsSlice ) => state[ SLICE_NAME ].loadStatus;
82
88
  const selectStylesDefinitions = ( state: ComponentsSlice ) => state[ SLICE_NAME ].styles ?? {};
83
89
  const selectUnpublishedData = ( state: ComponentsSlice ) => state[ SLICE_NAME ].unpublishedData;
90
+ const getCreatedThisSession = ( state: ComponentsSlice ) => state[ SLICE_NAME ].createdThisSession;
84
91
 
85
92
  export const selectComponents = createSelector(
86
93
  selectData,
87
94
  selectUnpublishedData,
88
- ( data: Component[], unpublishedData: UnpublishedComponent[] ) => [
89
- ...unpublishedData.map( ( item ) => ( { id: item.id, name: item.name } ) ),
95
+ ( data: PublishedComponent[], unpublishedData: UnpublishedComponent[] ) => [
96
+ ...unpublishedData.map( ( item ) => ( { uid: item.uid, name: item.name } ) ),
90
97
  ...data,
91
98
  ]
92
99
  );
@@ -94,16 +101,11 @@ export const selectUnpublishedComponents = createSelector(
94
101
  selectUnpublishedData,
95
102
  ( unpublishedData: UnpublishedComponent[] ) => unpublishedData
96
103
  );
97
- export const selectComponentsObject = createSelector(
98
- selectData,
99
- selectUnpublishedData,
100
- ( data: Component[], unpublishedData: UnpublishedComponent[] ) =>
101
- data.concat( unpublishedData ).reduce< Record< ComponentId, Component > >( ( acc, component ) => {
102
- acc[ component.id ] = component;
103
- return acc;
104
- }, {} )
105
- );
106
104
  export const selectLoadIsPending = createSelector( selectLoadStatus, ( status ) => status === 'pending' );
107
105
  export const selectLoadIsError = createSelector( selectLoadStatus, ( status ) => status === 'error' );
108
106
  export const selectStyles = ( state: ComponentsSlice ) => state[ SLICE_NAME ].styles ?? {};
109
107
  export const selectFlatStyles = createSelector( selectStylesDefinitions, ( data ) => Object.values( data ).flat() );
108
+ export const selectCreatedThisSession = createSelector(
109
+ getCreatedThisSession,
110
+ ( createdThisSession ) => createdThisSession
111
+ );
@@ -3,8 +3,8 @@ import { type TransformablePropValue } from '@elementor/editor-props';
3
3
  import { __dispatch as dispatch, __getState as getState } from '@elementor/store';
4
4
 
5
5
  import { apiClient } from '../api';
6
- import { selectUnpublishedComponents, slice, type UnpublishedComponent } from '../store/store';
7
- import { type Container, type DocumentSaveStatus } from '../types';
6
+ import { selectUnpublishedComponents, slice } from '../store/store';
7
+ import { type Container, type DocumentSaveStatus, type UnpublishedComponent } from '../types';
8
8
 
9
9
  export async function createComponentsBeforeSave( {
10
10
  container,
@@ -20,16 +20,17 @@ export async function createComponentsBeforeSave( {
20
20
  }
21
21
 
22
22
  try {
23
- const tempIdToComponentId = await createComponents( unpublishedComponents, status );
23
+ const uidToComponentId = await createComponents( unpublishedComponents, status );
24
24
 
25
25
  const elements = container.model.get( 'elements' ).toJSON();
26
- updateComponentInstances( elements, tempIdToComponentId );
26
+ updateComponentInstances( elements, uidToComponentId );
27
27
 
28
28
  dispatch(
29
29
  slice.actions.add(
30
30
  unpublishedComponents.map( ( component ) => ( {
31
- id: tempIdToComponentId.get( component.id ) as number,
31
+ id: uidToComponentId.get( component.uid ) as number,
32
32
  name: component.name,
33
+ uid: component.uid,
33
34
  } ) )
34
35
  )
35
36
  );
@@ -42,47 +43,47 @@ export async function createComponentsBeforeSave( {
42
43
  async function createComponents(
43
44
  components: UnpublishedComponent[],
44
45
  status: DocumentSaveStatus
45
- ): Promise< Map< number, number > > {
46
+ ): Promise< Map< string, number > > {
46
47
  const response = await apiClient.create( {
47
48
  status,
48
49
  items: components.map( ( component ) => ( {
49
- temp_id: component.id,
50
+ uid: component.uid,
50
51
  title: component.name,
51
52
  elements: component.elements,
52
53
  } ) ),
53
54
  } );
54
55
 
55
- const map = new Map< number, number >();
56
+ const map = new Map< string, number >();
56
57
 
57
58
  Object.entries( response ).forEach( ( [ key, value ] ) => {
58
- map.set( Number( key ), value );
59
+ map.set( key, value );
59
60
  } );
60
61
 
61
62
  return map;
62
63
  }
63
64
 
64
- function updateComponentInstances( elements: V1ElementData[], tempIdToComponentId: Map< number, number > ): void {
65
+ function updateComponentInstances( elements: V1ElementData[], uidToComponentId: Map< string, number > ): void {
65
66
  elements.forEach( ( element ) => {
66
- const { shouldUpdate, newComponentId } = shouldUpdateElement( element, tempIdToComponentId );
67
+ const { shouldUpdate, newComponentId } = shouldUpdateElement( element, uidToComponentId );
67
68
  if ( shouldUpdate ) {
68
69
  updateElementComponentId( element.id, newComponentId );
69
70
  }
70
71
 
71
72
  if ( element.elements ) {
72
- updateComponentInstances( element.elements, tempIdToComponentId );
73
+ updateComponentInstances( element.elements, uidToComponentId );
73
74
  }
74
75
  } );
75
76
  }
76
77
 
77
78
  function shouldUpdateElement(
78
79
  element: V1ElementData,
79
- tempIdToComponentId: Map< number, number >
80
+ uidToComponentId: Map< string, number >
80
81
  ): { shouldUpdate: true; newComponentId: number } | { shouldUpdate: false; newComponentId: null } {
81
82
  if ( element.widgetType === 'e-component' ) {
82
- const currentComponentId = ( element.settings?.component as TransformablePropValue< 'component-id', number > )
83
+ const currentComponentId = ( element.settings?.component as TransformablePropValue< 'component-id', string > )
83
84
  ?.value;
84
- if ( currentComponentId && tempIdToComponentId.has( currentComponentId ) ) {
85
- return { shouldUpdate: true, newComponentId: tempIdToComponentId.get( currentComponentId ) as number };
85
+ if ( currentComponentId && uidToComponentId.has( currentComponentId ) ) {
86
+ return { shouldUpdate: true, newComponentId: uidToComponentId.get( currentComponentId ) as number };
86
87
  }
87
88
  }
88
89
  return { shouldUpdate: false, newComponentId: null };
package/src/types.ts CHANGED
@@ -9,8 +9,18 @@ export type ComponentId = number;
9
9
 
10
10
  export type StylesDefinition = Record< ComponentId, StyleDefinition[] >;
11
11
 
12
- export type Component = {
12
+ export type Component = PublishedComponent | UnpublishedComponent;
13
+
14
+ export type PublishedComponent = BaseComponent & {
13
15
  id: number;
16
+ };
17
+
18
+ export type UnpublishedComponent = BaseComponent & {
19
+ elements: V1ElementData[];
20
+ };
21
+
22
+ type BaseComponent = {
23
+ uid: string;
14
24
  name: string;
15
25
  };
16
26
 
@@ -18,7 +28,15 @@ export type DocumentStatus = 'publish' | 'draft';
18
28
  export type DocumentSaveStatus = DocumentStatus | 'autosave';
19
29
 
20
30
  export type ExtendedWindow = Window & {
21
- elementorCommon: Record< string, unknown >;
31
+ elementorCommon: Record< string, unknown > & {
32
+ eventsManager: {
33
+ config: {
34
+ locations: Record< string, string >;
35
+ secondaryLocations: Record< string, string >;
36
+ triggers: Record< string, string >;
37
+ };
38
+ };
39
+ };
22
40
  };
23
41
 
24
42
  export type Container = {