@elementor/editor-components 3.35.0-448 → 3.35.0-450

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@elementor/editor-components",
3
3
  "description": "Elementor editor components",
4
- "version": "3.35.0-448",
4
+ "version": "3.35.0-450",
5
5
  "private": false,
6
6
  "author": "Elementor Team",
7
7
  "homepage": "https://elementor.com/",
@@ -40,30 +40,30 @@
40
40
  "dev": "tsup --config=../../tsup.dev.ts"
41
41
  },
42
42
  "dependencies": {
43
- "@elementor/editor": "3.35.0-448",
44
- "@elementor/editor-canvas": "3.35.0-448",
45
- "@elementor/editor-controls": "3.35.0-448",
46
- "@elementor/editor-documents": "3.35.0-448",
47
- "@elementor/editor-editing-panel": "3.35.0-448",
48
- "@elementor/editor-elements": "3.35.0-448",
49
- "@elementor/editor-elements-panel": "3.35.0-448",
50
- "@elementor/editor-mcp": "3.35.0-448",
51
- "@elementor/editor-panels": "3.35.0-448",
52
- "@elementor/editor-props": "3.35.0-448",
53
- "@elementor/editor-styles-repository": "3.35.0-448",
54
- "@elementor/editor-ui": "3.35.0-448",
55
- "@elementor/editor-v1-adapters": "3.35.0-448",
56
- "@elementor/http-client": "3.35.0-448",
43
+ "@elementor/editor": "3.35.0-450",
44
+ "@elementor/editor-canvas": "3.35.0-450",
45
+ "@elementor/editor-controls": "3.35.0-450",
46
+ "@elementor/editor-documents": "3.35.0-450",
47
+ "@elementor/editor-editing-panel": "3.35.0-450",
48
+ "@elementor/editor-elements": "3.35.0-450",
49
+ "@elementor/editor-elements-panel": "3.35.0-450",
50
+ "@elementor/editor-mcp": "3.35.0-450",
51
+ "@elementor/editor-panels": "3.35.0-450",
52
+ "@elementor/editor-props": "3.35.0-450",
53
+ "@elementor/editor-styles-repository": "3.35.0-450",
54
+ "@elementor/editor-ui": "3.35.0-450",
55
+ "@elementor/editor-v1-adapters": "3.35.0-450",
56
+ "@elementor/http-client": "3.35.0-450",
57
57
  "@elementor/icons": "^1.63.0",
58
- "@elementor/mixpanel": "3.35.0-448",
59
- "@elementor/query": "3.35.0-448",
60
- "@elementor/schema": "3.35.0-448",
61
- "@elementor/store": "3.35.0-448",
58
+ "@elementor/mixpanel": "3.35.0-450",
59
+ "@elementor/query": "3.35.0-450",
60
+ "@elementor/schema": "3.35.0-450",
61
+ "@elementor/store": "3.35.0-450",
62
62
  "@elementor/ui": "1.36.17",
63
- "@elementor/utils": "3.35.0-448",
63
+ "@elementor/utils": "3.35.0-450",
64
64
  "@wordpress/i18n": "^5.13.0",
65
- "@elementor/editor-notifications": "3.35.0-448",
66
- "@elementor/editor-current-user": "3.35.0-448"
65
+ "@elementor/editor-notifications": "3.35.0-450",
66
+ "@elementor/editor-current-user": "3.35.0-450"
67
67
  },
68
68
  "peerDependencies": {
69
69
  "react": "^18.3.1",
@@ -20,6 +20,7 @@ import {
20
20
  } from '@elementor/ui';
21
21
  import { __ } from '@wordpress/i18n';
22
22
 
23
+ import { useComponentsPermissions } from '../../hooks/use-components-permissions';
23
24
  import { archiveComponent } from '../../store/actions/archive-component';
24
25
  import { loadComponentsAssets } from '../../store/actions/load-components-assets';
25
26
  import { type Component } from '../../types';
@@ -36,6 +37,9 @@ type ComponentItemProps = {
36
37
  export const ComponentItem = ( { component, renameComponent }: ComponentItemProps ) => {
37
38
  const itemRef = useRef< HTMLElement >( null );
38
39
  const [ isDeleteDialogOpen, setIsDeleteDialogOpen ] = useState( false );
40
+ const { canRename, canDelete } = useComponentsPermissions();
41
+
42
+ const shouldShowActions = canRename || canDelete;
39
43
 
40
44
  const {
41
45
  ref: editableRef,
@@ -66,8 +70,8 @@ export const ComponentItem = ( { component, renameComponent }: ComponentItemProp
66
70
  };
67
71
 
68
72
  const handleDeleteClick = () => {
69
- popupState.close();
70
73
  setIsDeleteDialogOpen( true );
74
+ popupState.close();
71
75
  };
72
76
 
73
77
  const handleDeleteConfirm = () => {
@@ -140,37 +144,48 @@ export const ComponentItem = ( { component, renameComponent }: ComponentItemProp
140
144
  </Box>
141
145
  </Indicator>
142
146
  </Box>
143
- <IconButton size="tiny" { ...bindTrigger( popupState ) } aria-label="More actions">
144
- <DotsVerticalIcon fontSize="tiny" />
145
- </IconButton>
147
+ { shouldShowActions && (
148
+ <IconButton size="tiny" { ...bindTrigger( popupState ) } aria-label="More actions">
149
+ <DotsVerticalIcon fontSize="tiny" />
150
+ </IconButton>
151
+ ) }
146
152
  </ListItemButton>
147
153
  </WarningInfotip>
148
- <Menu
149
- { ...bindMenu( popupState ) }
150
- anchorOrigin={ {
151
- vertical: 'bottom',
152
- horizontal: 'right',
153
- } }
154
- transformOrigin={ {
155
- vertical: 'top',
156
- horizontal: 'right',
157
- } }
158
- >
159
- <MenuListItem
160
- sx={ { minWidth: '160px' } }
161
- onClick={ () => {
162
- popupState.close();
163
- openEditMode();
154
+ { shouldShowActions && (
155
+ <Menu
156
+ { ...bindMenu( popupState ) }
157
+ anchorOrigin={ {
158
+ vertical: 'bottom',
159
+ horizontal: 'right',
160
+ } }
161
+ transformOrigin={ {
162
+ vertical: 'top',
163
+ horizontal: 'right',
164
164
  } }
165
165
  >
166
- { __( 'Rename', 'elementor' ) }
167
- </MenuListItem>
168
- <MenuListItem sx={ { minWidth: '160px' } } onClick={ handleDeleteClick }>
169
- <Typography variant="caption" sx={ { color: 'error.light' } }>
170
- { __( 'Delete', 'elementor' ) }
171
- </Typography>
172
- </MenuListItem>
173
- </Menu>
166
+ { canRename && (
167
+ <MenuListItem
168
+ sx={ { minWidth: '160px' } }
169
+ primaryTypographyProps={ { variant: 'caption', color: 'text.primary' } }
170
+ onClick={ () => {
171
+ popupState.close();
172
+ openEditMode();
173
+ } }
174
+ >
175
+ { __( 'Rename', 'elementor' ) }
176
+ </MenuListItem>
177
+ ) }
178
+ { canDelete && (
179
+ <MenuListItem
180
+ sx={ { minWidth: '160px' } }
181
+ primaryTypographyProps={ { variant: 'caption', color: 'error.light' } }
182
+ onClick={ handleDeleteClick }
183
+ >
184
+ { __( 'Delete', 'elementor' ) }
185
+ </MenuListItem>
186
+ ) }
187
+ </Menu>
188
+ ) }
174
189
  <DeleteConfirmationDialog
175
190
  open={ isDeleteDialogOpen }
176
191
  onClose={ handleDeleteDialogClose }
@@ -6,6 +6,7 @@ import { Box, Button, Divider, Link, List, Stack, Typography } from '@elementor/
6
6
  import { __ } from '@wordpress/i18n';
7
7
 
8
8
  import { useComponents } from '../../hooks/use-components';
9
+ import { useComponentsPermissions } from '../../hooks/use-components-permissions';
9
10
  import { renameComponent } from '../../store/actions/rename-component';
10
11
  import { AngiePromotionModal } from './angie-promotion-modal';
11
12
  import { ComponentItem } from './components-item';
@@ -53,6 +54,8 @@ export function ComponentsList() {
53
54
  const EmptyState = () => {
54
55
  const [ isAngieModalOpen, setIsAngieModalOpen ] = useState( false );
55
56
 
57
+ const { canCreate } = useComponentsPermissions();
58
+
56
59
  const handleCreateWithAI = () => {
57
60
  const sdk = getAngieSdk();
58
61
 
@@ -88,9 +91,13 @@ const EmptyState = () => {
88
91
  <Typography align="center" variant="caption" color="secondary" sx={ { maxWidth: 200 } }>
89
92
  { __( 'Components are reusable blocks that sync across your site.', 'elementor' ) }
90
93
  <br />
91
- { __( 'Create once, use everywhere.', 'elementor' ) }
94
+ { canCreate
95
+ ? __( 'Create once, use everywhere.', 'elementor' )
96
+ : __(
97
+ 'With your current role, you cannot create components. Contact an administrator to create one.',
98
+ 'elementor'
99
+ ) }
92
100
  </Typography>
93
-
94
101
  <Link
95
102
  href={ LEARN_MORE_URL }
96
103
  target="_blank"
@@ -102,36 +109,44 @@ const EmptyState = () => {
102
109
  </Link>
103
110
  </Stack>
104
111
 
105
- <Divider sx={ { width: '100%' } } />
106
-
107
- <Stack alignItems="center" gap={ 1 } width="100%">
108
- <Typography align="center" variant="subtitle2" color="text.secondary" sx={ SUBTITLE_OVERRIDE_SX }>
109
- { __( 'Create your first one:', 'elementor' ) }
110
- </Typography>
111
-
112
- <Typography align="center" variant="caption" color="secondary" sx={ { maxWidth: 228 } }>
113
- { __(
114
- 'Right-click any div-block or flexbox on your canvas or structure and select "Create component"',
115
- 'elementor'
116
- ) }
117
- </Typography>
118
-
119
- <Typography align="center" variant="caption" color="secondary">
120
- { __( 'Or', 'elementor' ) }
121
- </Typography>
122
-
123
- <AngiePromotionModal open={ isAngieModalOpen } onClose={ () => setIsAngieModalOpen( false ) }>
124
- <Button
125
- color="secondary"
126
- variant="outlined"
127
- size="small"
128
- onClick={ handleCreateWithAI }
129
- endIcon={ <AIIcon /> }
130
- >
131
- { __( 'Create component with AI', 'elementor' ) }
132
- </Button>
133
- </AngiePromotionModal>
134
- </Stack>
112
+ { canCreate && (
113
+ <>
114
+ <Divider sx={ { width: '100%' } } />
115
+ <Stack alignItems="center" gap={ 1 } width="100%">
116
+ <Typography
117
+ align="center"
118
+ variant="subtitle2"
119
+ color="text.secondary"
120
+ sx={ SUBTITLE_OVERRIDE_SX }
121
+ >
122
+ { __( 'Create your first one:', 'elementor' ) }
123
+ </Typography>
124
+
125
+ <Typography align="center" variant="caption" color="secondary" sx={ { maxWidth: 228 } }>
126
+ { __(
127
+ 'Right-click any div-block or flexbox on your canvas or structure and select "Create component"',
128
+ 'elementor'
129
+ ) }
130
+ </Typography>
131
+
132
+ <Typography align="center" variant="caption" color="secondary">
133
+ { __( 'Or', 'elementor' ) }
134
+ </Typography>
135
+
136
+ <AngiePromotionModal open={ isAngieModalOpen } onClose={ () => setIsAngieModalOpen( false ) }>
137
+ <Button
138
+ color="secondary"
139
+ variant="outlined"
140
+ size="small"
141
+ onClick={ handleCreateWithAI }
142
+ endIcon={ <AIIcon /> }
143
+ >
144
+ { __( 'Create component with AI', 'elementor' ) }
145
+ </Button>
146
+ </AngiePromotionModal>
147
+ </Stack>
148
+ </>
149
+ ) }
135
150
  </Stack>
136
151
  );
137
152
  };
@@ -3,7 +3,21 @@ import { ComponentPropListIcon, PencilIcon } from '@elementor/icons';
3
3
  import { Button, Stack, Typography } from '@elementor/ui';
4
4
  import { __ } from '@wordpress/i18n';
5
5
 
6
+ import { useComponentsPermissions } from '../../hooks/use-components-permissions';
7
+
6
8
  export const EmptyState = ( { onEditComponent }: { onEditComponent: () => void } ) => {
9
+ const { canEdit } = useComponentsPermissions();
10
+
11
+ const message = canEdit
12
+ ? __(
13
+ 'Edit the component to add properties, manage them or update the design across all instances.',
14
+ 'elementor'
15
+ )
16
+ : __(
17
+ 'With your current role, you cannot edit this component. Contact an administrator to add properties.',
18
+ 'elementor'
19
+ );
20
+
7
21
  return (
8
22
  <Stack
9
23
  alignItems="center"
@@ -14,22 +28,18 @@ export const EmptyState = ( { onEditComponent }: { onEditComponent: () => void }
14
28
  gap={ 1.5 }
15
29
  >
16
30
  <ComponentPropListIcon fontSize="large" />
17
-
18
31
  <Typography align="center" variant="subtitle2">
19
32
  { __( 'No properties yet', 'elementor' ) }
20
33
  </Typography>
21
-
22
34
  <Typography align="center" variant="caption" maxWidth="170px">
23
- { __(
24
- 'Edit the component to add properties, manage them or update the design across all instances.',
25
- 'elementor'
26
- ) }
35
+ { message }
27
36
  </Typography>
28
-
29
- <Button variant="outlined" color="secondary" size="small" sx={ { mt: 1 } } onClick={ onEditComponent }>
30
- <PencilIcon fontSize="small" />
31
- { __( 'Edit component', 'elementor' ) }
32
- </Button>
37
+ { canEdit && (
38
+ <Button variant="outlined" color="secondary" size="small" sx={ { mt: 1 } } onClick={ onEditComponent }>
39
+ <PencilIcon fontSize="small" />
40
+ { __( 'Edit component', 'elementor' ) }
41
+ </Button>
42
+ ) }
33
43
  </Stack>
34
44
  );
35
45
  };
@@ -9,6 +9,7 @@ import { Divider, IconButton, Stack, Tooltip } from '@elementor/ui';
9
9
  import { __ } from '@wordpress/i18n';
10
10
 
11
11
  import { useComponentInstanceSettings } from '../../hooks/use-component-instance-settings';
12
+ import { useComponentsPermissions } from '../../hooks/use-components-permissions';
12
13
  import { useComponent, useOverridableProps } from '../../store/store';
13
14
  import { type OverridablePropsGroup } from '../../types';
14
15
  import { switchToComponent } from '../../utils/switch-to-component';
@@ -16,7 +17,10 @@ import { EmptyState } from './empty-state';
16
17
  import { OverridePropsGroup } from './override-props-group';
17
18
 
18
19
  export function InstanceEditingPanel() {
20
+ const { canEdit } = useComponentsPermissions();
21
+
19
22
  const settings = useComponentInstanceSettings();
23
+
20
24
  const componentId = settings?.component_id?.value;
21
25
 
22
26
  const overrides = settings?.overrides?.value;
@@ -49,11 +53,13 @@ export function InstanceEditingPanel() {
49
53
  <Stack direction="row" alignItems="center" flexGrow={ 1 } gap={ 1 } maxWidth="100%">
50
54
  <ComponentsIcon fontSize="small" sx={ { color: 'text.tertiary' } } />
51
55
  <EllipsisWithTooltip title={ component.name } as={ PanelHeaderTitle } />
52
- <Tooltip title={ panelTitle } sx={ { marginLeft: 'auto' } }>
53
- <IconButton size="tiny" onClick={ handleEditComponent } aria-label={ panelTitle }>
54
- <PencilIcon fontSize="tiny" />
55
- </IconButton>
56
- </Tooltip>
56
+ { canEdit && (
57
+ <Tooltip title={ panelTitle }>
58
+ <IconButton size="tiny" onClick={ handleEditComponent } aria-label={ panelTitle }>
59
+ <PencilIcon fontSize="tiny" />
60
+ </IconButton>
61
+ </Tooltip>
62
+ ) }
57
63
  </Stack>
58
64
  </PanelHeader>
59
65
  <PanelBody>
@@ -106,9 +106,10 @@ function createComponentView(
106
106
  showLockedByModal?: ( lockedBy: string ) => void;
107
107
  }
108
108
  ): typeof ElementView {
109
+ const legacyWindow = window as unknown as LegacyWindow & ExtendedWindow;
110
+
109
111
  return class extends createTemplatedElementView( options ) {
110
- legacyWindow = window as unknown as LegacyWindow & ExtendedWindow;
111
- eventsManagerConfig = this.legacyWindow.elementorCommon.eventsManager.config;
112
+ eventsManagerConfig = legacyWindow.elementorCommon.eventsManager.config;
112
113
  #componentRenderContext: ComponentRenderContext | undefined;
113
114
 
114
115
  isComponentCurrentlyEdited() {
@@ -160,9 +161,7 @@ function createComponentView(
160
161
  overrides: componentInstance.overrides ?? {},
161
162
  };
162
163
 
163
- this.collection = this.legacyWindow.elementor.createBackboneElementsCollection(
164
- componentInstance.elements
165
- );
164
+ this.collection = legacyWindow.elementor.createBackboneElementsCollection( componentInstance.elements );
166
165
 
167
166
  this.collection.models.forEach( setInactiveRecursively );
168
167
 
@@ -212,11 +211,7 @@ function createComponentView(
212
211
  }
213
212
 
214
213
  _getContextMenuConfig() {
215
- const legacyWindow = this.legacyWindow || ( window as unknown as LegacyWindow & ExtendedWindow );
216
- const elementorWithConfig = legacyWindow.elementor as typeof legacyWindow.elementor & {
217
- config?: { user?: { is_administrator?: boolean } };
218
- };
219
- const isAdministrator = elementorWithConfig.config?.user?.is_administrator ?? false;
214
+ const isAdministrator = isUserAdministrator();
220
215
 
221
216
  const addedGroup = {
222
217
  general: {
@@ -273,6 +268,12 @@ function createComponentView(
273
268
  handleDblClick( e: MouseEvent ) {
274
269
  e.stopPropagation();
275
270
 
271
+ const isAdministrator = isUserAdministrator();
272
+
273
+ if ( ! isAdministrator ) {
274
+ return;
275
+ }
276
+
276
277
  const { triggers, locations, secondaryLocations } = this.eventsManagerConfig;
277
278
 
278
279
  this.editComponent( {
@@ -314,6 +315,12 @@ function setInactiveRecursively( model: BackboneModel< ElementModel > ) {
314
315
  }
315
316
  }
316
317
 
318
+ function isUserAdministrator() {
319
+ const legacyWindow = window as unknown as LegacyWindow;
320
+
321
+ return legacyWindow.elementor.config?.user?.is_administrator ?? false;
322
+ }
323
+
317
324
  function createComponentModel(): BackboneModelConstructor< ComponentModel > {
318
325
  const legacyWindow = window as unknown as LegacyWindow;
319
326
  const WidgetType = legacyWindow.elementor.modules.elements.types.Widget;
@@ -0,0 +1,12 @@
1
+ import { useCurrentUserCapabilities } from '@elementor/editor-current-user';
2
+
3
+ export const useComponentsPermissions = () => {
4
+ const { isAdmin } = useCurrentUserCapabilities();
5
+
6
+ return {
7
+ canCreate: isAdmin,
8
+ canEdit: isAdmin,
9
+ canDelete: isAdmin,
10
+ canRename: isAdmin,
11
+ };
12
+ };