@elementor/editor-components 4.0.0-607 → 4.0.0-609

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.
Files changed (26) hide show
  1. package/dist/index.js +2341 -2192
  2. package/dist/index.js.map +1 -1
  3. package/dist/index.mjs +2814 -2672
  4. package/dist/index.mjs.map +1 -1
  5. package/package.json +23 -23
  6. package/src/components/components-tab/components-item.tsx +84 -215
  7. package/src/components/components-tab/components-list.tsx +3 -3
  8. package/src/components/components-tab/components.tsx +1 -2
  9. package/src/components/instance-editing-panel/empty-state.tsx +2 -2
  10. package/src/components/instance-editing-panel/instance-editing-panel.tsx +19 -78
  11. package/src/components/instance-editing-panel/instance-panel-body.tsx +36 -0
  12. package/src/components/instance-editing-panel/instance-panel-header.tsx +40 -0
  13. package/src/components/instance-editing-panel/use-instance-panel-data.ts +57 -0
  14. package/src/extended/components/components-tab/component-item.tsx +180 -0
  15. package/src/extended/components/components-tab/components.tsx +58 -0
  16. package/src/extended/components/create-component-form/create-component-form.tsx +1 -1
  17. package/src/extended/components/instance-editing-panel/instance-editing-panel.tsx +60 -0
  18. package/src/extended/init.ts +22 -1
  19. package/src/{store → extended/store}/actions/archive-component.ts +1 -1
  20. package/src/{store → extended/store}/actions/rename-component.ts +2 -2
  21. package/src/{utils → extended/utils}/component-name-validation.ts +1 -1
  22. package/src/extended/utils/replace-element-with-component.ts +1 -1
  23. /package/src/{components → extended/components}/components-tab/delete-confirmation-dialog.tsx +0 -0
  24. /package/src/{utils → extended/utils}/component-form-schema.ts +0 -0
  25. /package/src/{utils → extended/utils}/create-component-model.ts +0 -0
  26. /package/src/{utils → extended/utils}/get-container-for-new-element.ts +0 -0
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@elementor/editor-components",
3
3
  "description": "Elementor editor components",
4
- "version": "4.0.0-607",
4
+ "version": "4.0.0-609",
5
5
  "private": false,
6
6
  "author": "Elementor Team",
7
7
  "homepage": "https://elementor.com/",
@@ -40,31 +40,31 @@
40
40
  "dev": "tsup --config=../../tsup.dev.ts"
41
41
  },
42
42
  "dependencies": {
43
- "@elementor/editor": "4.0.0-607",
44
- "@elementor/editor-canvas": "4.0.0-607",
45
- "@elementor/editor-controls": "4.0.0-607",
46
- "@elementor/editor-documents": "4.0.0-607",
47
- "@elementor/editor-editing-panel": "4.0.0-607",
48
- "@elementor/editor-elements": "4.0.0-607",
49
- "@elementor/editor-elements-panel": "4.0.0-607",
50
- "@elementor/editor-mcp": "4.0.0-607",
51
- "@elementor/editor-templates": "4.0.0-607",
52
- "@elementor/editor-panels": "4.0.0-607",
53
- "@elementor/editor-props": "4.0.0-607",
54
- "@elementor/editor-styles-repository": "4.0.0-607",
55
- "@elementor/editor-ui": "4.0.0-607",
56
- "@elementor/editor-v1-adapters": "4.0.0-607",
57
- "@elementor/http-client": "4.0.0-607",
43
+ "@elementor/editor": "4.0.0-609",
44
+ "@elementor/editor-canvas": "4.0.0-609",
45
+ "@elementor/editor-controls": "4.0.0-609",
46
+ "@elementor/editor-documents": "4.0.0-609",
47
+ "@elementor/editor-editing-panel": "4.0.0-609",
48
+ "@elementor/editor-elements": "4.0.0-609",
49
+ "@elementor/editor-elements-panel": "4.0.0-609",
50
+ "@elementor/editor-mcp": "4.0.0-609",
51
+ "@elementor/editor-templates": "4.0.0-609",
52
+ "@elementor/editor-panels": "4.0.0-609",
53
+ "@elementor/editor-props": "4.0.0-609",
54
+ "@elementor/editor-styles-repository": "4.0.0-609",
55
+ "@elementor/editor-ui": "4.0.0-609",
56
+ "@elementor/editor-v1-adapters": "4.0.0-609",
57
+ "@elementor/http-client": "4.0.0-609",
58
58
  "@elementor/icons": "^1.68.0",
59
- "@elementor/events": "4.0.0-607",
60
- "@elementor/query": "4.0.0-607",
61
- "@elementor/schema": "4.0.0-607",
62
- "@elementor/store": "4.0.0-607",
59
+ "@elementor/events": "4.0.0-609",
60
+ "@elementor/query": "4.0.0-609",
61
+ "@elementor/schema": "4.0.0-609",
62
+ "@elementor/store": "4.0.0-609",
63
63
  "@elementor/ui": "1.36.17",
64
- "@elementor/utils": "4.0.0-607",
64
+ "@elementor/utils": "4.0.0-609",
65
65
  "@wordpress/i18n": "^5.13.0",
66
- "@elementor/editor-notifications": "4.0.0-607",
67
- "@elementor/editor-current-user": "4.0.0-607"
66
+ "@elementor/editor-notifications": "4.0.0-609",
67
+ "@elementor/editor-current-user": "4.0.0-609"
68
68
  },
69
69
  "peerDependencies": {
70
70
  "react": "^18.3.1",
@@ -1,230 +1,80 @@
1
1
  import * as React from 'react';
2
- import { useRef, useState } from 'react';
3
- import { endDragElementFromPanel, startDragElementFromPanel } from '@elementor/editor-canvas';
4
- import { dropElement, type DropElementParams, type V1ElementData } from '@elementor/editor-elements';
5
- import { EditableField, EllipsisWithTooltip, MenuListItem, useEditable, WarningInfotip } from '@elementor/editor-ui';
6
- import { ComponentsIcon, DotsVerticalIcon } from '@elementor/icons';
7
- import {
8
- bindMenu,
9
- bindTrigger,
10
- Box,
11
- IconButton,
12
- ListItemButton,
13
- ListItemIcon,
14
- Menu,
15
- Stack,
16
- styled,
17
- type Theme,
18
- Typography,
19
- usePopupState,
20
- } from '@elementor/ui';
21
- import { __ } from '@wordpress/i18n';
2
+ import { forwardRef } from 'react';
3
+ import { EditableField, EllipsisWithTooltip } from '@elementor/editor-ui';
4
+ import { ComponentsIcon } from '@elementor/icons';
5
+ import { Box, ListItemButton, ListItemIcon, styled, type Theme, Typography } from '@elementor/ui';
22
6
 
23
- import { useComponentsPermissions } from '../../hooks/use-components-permissions';
24
- import { archiveComponent } from '../../store/actions/archive-component';
25
- import { loadComponentsAssets } from '../../store/actions/load-components-assets';
26
- import { renameComponent } from '../../store/actions/rename-component';
27
7
  import { type Component } from '../../types';
28
- import { validateComponentName } from '../../utils/component-name-validation';
29
- import { createComponentModel } from '../../utils/create-component-model';
30
- import { getContainerForNewElement } from '../../utils/get-container-for-new-element';
31
- import { DeleteConfirmationDialog } from './delete-confirmation-dialog';
32
8
 
33
- type ComponentItemProps = {
9
+ export type ComponentItemProps = {
34
10
  component: Omit< Component, 'id' > & { id?: number };
11
+ disabled?: boolean;
12
+ draggable?: boolean;
13
+ onDragStart?: React.DragEventHandler;
14
+ onDragEnd?: React.DragEventHandler;
15
+ onClick?: () => void;
16
+ isEditing?: boolean;
17
+ error?: string | null;
18
+ nameSlot?: React.ReactNode;
19
+ endSlot?: React.ReactNode;
35
20
  };
36
21
 
37
- export const ComponentItem = ( { component }: ComponentItemProps ) => {
38
- const itemRef = useRef< HTMLElement >( null );
39
- const [ isDeleteDialogOpen, setIsDeleteDialogOpen ] = useState( false );
40
- const { canRename, canDelete } = useComponentsPermissions();
41
-
42
- const shouldShowActions = canRename || canDelete;
43
-
44
- const {
45
- ref: editableRef,
46
- isEditing,
47
- openEditMode,
48
- error,
49
- getProps: getEditableProps,
50
- } = useEditable( {
51
- value: component.name,
52
- onSubmit: ( newName: string ) => renameComponent( component.uid, newName ),
53
- validation: validateComponentTitle,
54
- } );
55
-
56
- const componentModel = createComponentModel( component );
57
-
58
- const popupState = usePopupState( {
59
- variant: 'popover',
60
- disableAutoFocus: true,
61
- } );
62
-
63
- const handleClick = () => {
64
- addComponentToPage( componentModel );
65
- };
66
-
67
- const handleDragEnd = () => {
68
- loadComponentsAssets( [ componentModel as V1ElementData ] );
69
-
70
- endDragElementFromPanel();
71
- };
72
-
73
- const handleDeleteClick = () => {
74
- setIsDeleteDialogOpen( true );
75
- popupState.close();
76
- };
77
-
78
- const handleDeleteConfirm = () => {
79
- if ( ! component.id ) {
80
- throw new Error( 'Component ID is required' );
81
- }
82
-
83
- setIsDeleteDialogOpen( false );
84
- archiveComponent( component.id, component.name );
85
- };
86
-
87
- const handleDeleteDialogClose = () => {
88
- setIsDeleteDialogOpen( false );
89
- };
90
-
91
- return (
92
- <Stack>
93
- <WarningInfotip
94
- open={ Boolean( error ) }
95
- text={ error ?? '' }
96
- placement="bottom"
97
- width={ itemRef.current?.getBoundingClientRect().width }
98
- offset={ [ 0, -15 ] }
22
+ export const ComponentItem = forwardRef< HTMLElement, ComponentItemProps >(
23
+ (
24
+ {
25
+ component,
26
+ disabled = true,
27
+ draggable,
28
+ onDragStart,
29
+ onDragEnd,
30
+ onClick,
31
+ isEditing = false,
32
+ error = null,
33
+ nameSlot,
34
+ endSlot,
35
+ ...props
36
+ },
37
+ ref
38
+ ) => {
39
+ return (
40
+ <ListItemButton
41
+ disabled={ disabled }
42
+ draggable={ draggable }
43
+ onDragStart={ onDragStart }
44
+ onDragEnd={ onDragEnd }
45
+ shape="rounded"
46
+ ref={ ref }
47
+ sx={ {
48
+ border: 'solid 1px',
49
+ borderColor: 'divider',
50
+ py: 0.5,
51
+ px: 1,
52
+ display: 'flex',
53
+ width: '100%',
54
+ alignItems: 'center',
55
+ gap: 1,
56
+ } }
57
+ { ...props }
99
58
  >
100
- <ListItemButton
101
- draggable
102
- onDragStart={ ( event: React.DragEvent ) => startDragElementFromPanel( componentModel, event ) }
103
- onDragEnd={ handleDragEnd }
104
- shape="rounded"
105
- ref={ itemRef }
106
- sx={ {
107
- border: 'solid 1px',
108
- borderColor: 'divider',
109
- py: 0.5,
110
- px: 1,
111
- display: 'flex',
112
- width: '100%',
113
- alignItems: 'center',
114
- gap: 1,
115
- } }
116
- >
117
- <Box
118
- display="flex"
119
- alignItems="center"
120
- gap={ 1 }
121
- minWidth={ 0 }
122
- flexGrow={ 1 }
123
- onClick={ handleClick }
124
- >
125
- <ListItemIcon size="tiny">
126
- <ComponentsIcon fontSize="tiny" />
127
- </ListItemIcon>
128
- <Indicator isActive={ isEditing } isError={ !! error }>
129
- <Box display="flex" flex={ 1 } minWidth={ 0 } flexGrow={ 1 }>
130
- { isEditing ? (
131
- <EditableField
132
- ref={ editableRef }
133
- as={ Typography }
134
- variant="caption"
135
- { ...getEditableProps() }
136
- />
137
- ) : (
138
- <EllipsisWithTooltip
139
- title={ component.name }
140
- as={ Typography }
141
- variant="caption"
142
- color="text.primary"
143
- />
144
- ) }
145
- </Box>
146
- </Indicator>
147
- </Box>
148
- { shouldShowActions && (
149
- <IconButton size="tiny" { ...bindTrigger( popupState ) } aria-label="More actions">
150
- <DotsVerticalIcon fontSize="tiny" />
151
- </IconButton>
152
- ) }
153
- </ListItemButton>
154
- </WarningInfotip>
155
- { shouldShowActions && (
156
- <Menu
157
- { ...bindMenu( popupState ) }
158
- anchorOrigin={ {
159
- vertical: 'bottom',
160
- horizontal: 'right',
161
- } }
162
- transformOrigin={ {
163
- vertical: 'top',
164
- horizontal: 'right',
165
- } }
166
- >
167
- { canRename && (
168
- <MenuListItem
169
- sx={ { minWidth: '160px' } }
170
- primaryTypographyProps={ { variant: 'caption', color: 'text.primary' } }
171
- onClick={ () => {
172
- popupState.close();
173
- openEditMode();
174
- } }
175
- >
176
- { __( 'Rename', 'elementor' ) }
177
- </MenuListItem>
178
- ) }
179
- { canDelete && (
180
- <MenuListItem
181
- sx={ { minWidth: '160px' } }
182
- primaryTypographyProps={ { variant: 'caption', color: 'error.light' } }
183
- onClick={ handleDeleteClick }
184
- >
185
- { __( 'Delete', 'elementor' ) }
186
- </MenuListItem>
187
- ) }
188
- </Menu>
189
- ) }
190
- <DeleteConfirmationDialog
191
- open={ isDeleteDialogOpen }
192
- onClose={ handleDeleteDialogClose }
193
- onConfirm={ handleDeleteConfirm }
194
- />
195
- </Stack>
196
- );
197
- };
198
-
199
- const addComponentToPage = ( model: DropElementParams[ 'model' ] ) => {
200
- const { container, options } = getContainerForNewElement();
201
-
202
- if ( ! container ) {
203
- throw new Error( `Can't find container to drop new component instance at` );
59
+ <Box display="flex" alignItems="center" gap={ 1 } minWidth={ 0 } flexGrow={ 1 } onClick={ onClick }>
60
+ <ListItemIcon size="tiny">
61
+ <ComponentsIcon fontSize="tiny" />
62
+ </ListItemIcon>
63
+ <Indicator isActive={ isEditing } isError={ !! error }>
64
+ <Box display="flex" flex={ 1 } minWidth={ 0 } flexGrow={ 1 }>
65
+ { nameSlot ?? <ComponentName name={ component.name } /> }
66
+ </Box>
67
+ </Indicator>
68
+ </Box>
69
+ { endSlot }
70
+ </ListItemButton>
71
+ );
204
72
  }
205
-
206
- loadComponentsAssets( [ model as V1ElementData ] );
207
-
208
- dropElement( {
209
- containerId: container.id,
210
- model,
211
- options: { ...options, useHistory: false, scrollIntoView: true },
212
- } );
213
- };
214
-
215
- const validateComponentTitle = ( newTitle: string ) => {
216
- const result = validateComponentName( newTitle );
217
-
218
- if ( ! result.errorMessage ) {
219
- return null;
220
- }
221
-
222
- return result.errorMessage;
223
- };
73
+ );
224
74
 
225
75
  const Indicator = styled( Box, {
226
76
  shouldForwardProp: ( prop ) => prop !== 'isActive' && prop !== 'isError',
227
- } )( ( { theme, isActive, isError } ) => ( {
77
+ } )( ( { theme, isActive, isError }: { theme: Theme; isActive: boolean; isError: boolean } ) => ( {
228
78
  display: 'flex',
229
79
  width: '100%',
230
80
  flexGrow: 1,
@@ -246,3 +96,22 @@ const getIndicatorBorder = ( { isActive, isError, theme }: { isActive: boolean;
246
96
 
247
97
  return 'none';
248
98
  };
99
+
100
+ type EditableConfig = {
101
+ ref: React.Ref< HTMLElement >;
102
+ isEditing: boolean;
103
+ getProps: () => Record< string, unknown >;
104
+ };
105
+
106
+ export type ComponentNameProps = {
107
+ name: string;
108
+ editable?: EditableConfig;
109
+ };
110
+
111
+ export function ComponentName( { name, editable }: ComponentNameProps ) {
112
+ if ( editable?.isEditing ) {
113
+ return <EditableField ref={ editable.ref } as={ Typography } variant="caption" { ...editable.getProps() } />;
114
+ }
115
+
116
+ return <EllipsisWithTooltip title={ name } as={ Typography } variant="caption" color="text.primary" />;
117
+ }
@@ -40,7 +40,7 @@ export function ComponentsList() {
40
40
  );
41
41
  }
42
42
 
43
- const EmptyState = () => {
43
+ export const EmptyState = () => {
44
44
  const { canCreate } = useComponentsPermissions();
45
45
 
46
46
  return (
@@ -106,7 +106,7 @@ const EmptyState = () => {
106
106
  );
107
107
  };
108
108
 
109
- const EmptySearchResult = () => {
109
+ export const EmptySearchResult = () => {
110
110
  const { searchValue, clearSearch } = useSearch();
111
111
  return (
112
112
  <Stack
@@ -163,7 +163,7 @@ const EmptySearchResult = () => {
163
163
  );
164
164
  };
165
165
 
166
- const useFilteredComponents = () => {
166
+ export const useFilteredComponents = () => {
167
167
  const { components, isLoading } = useComponents();
168
168
  const { searchValue } = useSearch();
169
169
 
@@ -2,7 +2,6 @@ import * as React from 'react';
2
2
  import { ThemeProvider } from '@elementor/editor-ui';
3
3
 
4
4
  import { useComponents } from '../../hooks/use-components';
5
- import { isProUser } from '../../utils/is-pro-user';
6
5
  import { ComponentSearch } from './component-search';
7
6
  import { ComponentsList } from './components-list';
8
7
  import { ComponentsProNotification } from './components-pro-notification';
@@ -15,7 +14,7 @@ const ComponentsContent = () => {
15
14
  return (
16
15
  <>
17
16
  { hasComponents && <ComponentSearch /> }
18
- { hasComponents && ! isProUser() && <ComponentsProNotification /> }
17
+ { hasComponents && <ComponentsProNotification /> }
19
18
  <ComponentsList />
20
19
  </>
21
20
  );
@@ -5,7 +5,7 @@ import { __ } from '@wordpress/i18n';
5
5
 
6
6
  import { useComponentsPermissions } from '../../hooks/use-components-permissions';
7
7
 
8
- export const EmptyState = ( { onEditComponent }: { onEditComponent: () => void } ) => {
8
+ export const EmptyState = ( { onEditComponent }: { onEditComponent?: () => void } ) => {
9
9
  const { canEdit } = useComponentsPermissions();
10
10
 
11
11
  const message = canEdit
@@ -34,7 +34,7 @@ export const EmptyState = ( { onEditComponent }: { onEditComponent: () => void }
34
34
  <Typography align="center" variant="caption" maxWidth="170px">
35
35
  { message }
36
36
  </Typography>
37
- { canEdit && (
37
+ { canEdit && !! onEditComponent && (
38
38
  <Button variant="outlined" color="secondary" size="small" sx={ { mt: 1 } } onClick={ onEditComponent }>
39
39
  <PencilIcon fontSize="small" />
40
40
  { __( 'Edit component', 'elementor' ) }
@@ -1,58 +1,26 @@
1
1
  import * as React from 'react';
2
- import { ControlAdornmentsProvider } from '@elementor/editor-controls';
3
- import { getFieldIndicators, useElement } from '@elementor/editor-editing-panel';
4
- import { useElementSetting, useSelectedElement } from '@elementor/editor-elements';
5
- import { PanelBody, PanelHeader, PanelHeaderTitle } from '@elementor/editor-panels';
6
- import { EllipsisWithTooltip } from '@elementor/editor-ui';
7
- import { ComponentsIcon, PencilIcon } from '@elementor/icons';
8
- import { Box, Divider, IconButton, Stack, Tooltip } from '@elementor/ui';
2
+ import { PencilIcon } from '@elementor/icons';
3
+ import { Box } from '@elementor/ui';
9
4
  import { __ } from '@wordpress/i18n';
10
5
 
11
- import { useComponentsPermissions } from '../../hooks/use-components-permissions';
12
- import { useSanitizeOverridableProps } from '../../hooks/use-sanitize-overridable-props';
13
- import {
14
- componentInstancePropTypeUtil,
15
- type ComponentInstancePropValue,
16
- } from '../../prop-types/component-instance-prop-type';
17
6
  import { ComponentInstanceProvider } from '../../provider/component-instance-context';
18
- import { useComponent } from '../../store/store';
19
- import { type OverridablePropsGroup } from '../../types';
20
- import { switchToComponent } from '../../utils/switch-to-component';
21
7
  import { EmptyState } from './empty-state';
22
- import { OverridePropsGroup } from './override-props-group';
8
+ import { InstancePanelBody } from './instance-panel-body';
9
+ import { EditComponentAction, InstancePanelHeader } from './instance-panel-header';
10
+ import { useInstancePanelData } from './use-instance-panel-data';
23
11
 
24
12
  export function InstanceEditingPanel() {
25
- const { canEdit } = useComponentsPermissions();
13
+ const data = useInstancePanelData();
26
14
 
27
- const settings = useComponentInstanceSettings();
28
-
29
- const componentId = settings?.component_id?.value;
30
-
31
- const overrides = settings?.overrides?.value;
32
-
33
- const component = useComponent( componentId ?? null );
34
-
35
- const componentInstanceId = useSelectedElement()?.element?.id;
36
-
37
- const overridableProps = useSanitizeOverridableProps( componentId ?? null, componentInstanceId );
38
-
39
- if ( ! componentId || ! overridableProps || ! component ) {
15
+ if ( ! data ) {
40
16
  return null;
41
17
  }
42
18
 
19
+ const { componentId, component, overrides, overridableProps, groups, isEmpty, componentInstanceId } = data;
20
+
43
21
  /* translators: %s: component name. */
44
22
  const panelTitle = __( 'Edit %s', 'elementor' ).replace( '%s', component.name );
45
23
 
46
- const handleEditComponent = () => switchToComponent( componentId, componentInstanceId );
47
-
48
- const isNonEmptyGroup = ( group: OverridablePropsGroup | null ) => group !== null && group.props.length > 0;
49
-
50
- const groups = overridableProps.groups.order
51
- .map( ( groupId ) => overridableProps.groups.items[ groupId ] ?? null )
52
- .filter( isNonEmptyGroup );
53
-
54
- const isEmpty = groups.length === 0 || Object.keys( overridableProps.props ).length === 0;
55
-
56
24
  return (
57
25
  <Box data-testid="instance-editing-panel">
58
26
  <ComponentInstanceProvider
@@ -60,44 +28,17 @@ export function InstanceEditingPanel() {
60
28
  overrides={ overrides }
61
29
  overridableProps={ overridableProps }
62
30
  >
63
- <PanelHeader sx={ { justifyContent: 'start', px: 2 } }>
64
- <Stack direction="row" alignItems="center" flexGrow={ 1 } gap={ 1 } maxWidth="100%">
65
- <ComponentsIcon fontSize="small" sx={ { color: 'text.tertiary' } } />
66
- <EllipsisWithTooltip title={ component.name } as={ PanelHeaderTitle } sx={ { flexGrow: 1 } } />
67
- { canEdit && (
68
- <Tooltip title={ panelTitle }>
69
- <IconButton size="tiny" onClick={ handleEditComponent } aria-label={ panelTitle }>
70
- <PencilIcon fontSize="tiny" />
71
- </IconButton>
72
- </Tooltip>
73
- ) }
74
- </Stack>
75
- </PanelHeader>
76
- <PanelBody>
77
- <ControlAdornmentsProvider items={ getFieldIndicators( 'settings' ) }>
78
- { isEmpty ? (
79
- <EmptyState onEditComponent={ handleEditComponent } />
80
- ) : (
81
- <Stack direction="column" alignItems="stretch">
82
- { groups.map( ( group ) => (
83
- <React.Fragment key={ group.id + componentInstanceId }>
84
- <OverridePropsGroup group={ group } />
85
- <Divider />
86
- </React.Fragment>
87
- ) ) }
88
- </Stack>
89
- ) }
90
- </ControlAdornmentsProvider>
91
- </PanelBody>
31
+ <InstancePanelHeader
32
+ componentName={ component.name }
33
+ actions={ <EditComponentAction disabled label={ panelTitle } icon={ PencilIcon } /> }
34
+ />
35
+ <InstancePanelBody
36
+ groups={ groups }
37
+ isEmpty={ isEmpty }
38
+ emptyState={ <EmptyState /> }
39
+ componentInstanceId={ componentInstanceId }
40
+ />
92
41
  </ComponentInstanceProvider>
93
42
  </Box>
94
43
  );
95
44
  }
96
-
97
- function useComponentInstanceSettings() {
98
- const { element } = useElement();
99
-
100
- const settings = useElementSetting< ComponentInstancePropValue >( element.id, 'component_instance' );
101
-
102
- return componentInstancePropTypeUtil.extract( settings );
103
- }
@@ -0,0 +1,36 @@
1
+ import * as React from 'react';
2
+ import { ControlAdornmentsProvider } from '@elementor/editor-controls';
3
+ import { getFieldIndicators } from '@elementor/editor-editing-panel';
4
+ import { PanelBody } from '@elementor/editor-panels';
5
+ import { Divider, Stack } from '@elementor/ui';
6
+
7
+ import { type OverridablePropsGroup } from '../../types';
8
+ import { OverridePropsGroup } from './override-props-group';
9
+
10
+ type InstancePanelBodyProps = {
11
+ groups: OverridablePropsGroup[];
12
+ isEmpty: boolean;
13
+ emptyState: React.ReactNode;
14
+ componentInstanceId?: string;
15
+ };
16
+
17
+ export function InstancePanelBody( { groups, isEmpty, emptyState, componentInstanceId }: InstancePanelBodyProps ) {
18
+ return (
19
+ <PanelBody>
20
+ <ControlAdornmentsProvider items={ getFieldIndicators( 'settings' ) }>
21
+ { isEmpty ? (
22
+ emptyState
23
+ ) : (
24
+ <Stack direction="column" alignItems="stretch">
25
+ { groups.map( ( group ) => (
26
+ <React.Fragment key={ group.id + componentInstanceId }>
27
+ <OverridePropsGroup group={ group } />
28
+ <Divider />
29
+ </React.Fragment>
30
+ ) ) }
31
+ </Stack>
32
+ ) }
33
+ </ControlAdornmentsProvider>
34
+ </PanelBody>
35
+ );
36
+ }
@@ -0,0 +1,40 @@
1
+ import * as React from 'react';
2
+ import { type ElementType } from 'react';
3
+ import { PanelHeader, PanelHeaderTitle } from '@elementor/editor-panels';
4
+ import { EllipsisWithTooltip } from '@elementor/editor-ui';
5
+ import { ComponentsIcon } from '@elementor/icons';
6
+ import { IconButton, Stack, Tooltip } from '@elementor/ui';
7
+
8
+ type InstancePanelHeaderProps = {
9
+ componentName: string;
10
+ actions?: React.ReactNode;
11
+ };
12
+
13
+ export function InstancePanelHeader( { componentName, actions }: InstancePanelHeaderProps ) {
14
+ return (
15
+ <PanelHeader sx={ { justifyContent: 'start', px: 2 } }>
16
+ <Stack direction="row" alignItems="center" flexGrow={ 1 } gap={ 1 } maxWidth="100%">
17
+ <ComponentsIcon fontSize="small" sx={ { color: 'text.tertiary' } } />
18
+ <EllipsisWithTooltip title={ componentName } as={ PanelHeaderTitle } sx={ { flexGrow: 1 } } />
19
+ { actions }
20
+ </Stack>
21
+ </PanelHeader>
22
+ );
23
+ }
24
+
25
+ type EditComponentActionProps = {
26
+ label: string;
27
+ onClick?: () => void;
28
+ disabled?: boolean;
29
+ icon: ElementType;
30
+ };
31
+
32
+ export function EditComponentAction( { label, onClick, disabled = false, icon: Icon }: EditComponentActionProps ) {
33
+ return (
34
+ <Tooltip title={ label }>
35
+ <IconButton size="tiny" onClick={ onClick } aria-label={ label } disabled={ disabled }>
36
+ <Icon fontSize="tiny" />
37
+ </IconButton>
38
+ </Tooltip>
39
+ );
40
+ }