@elementor/editor-components 3.35.0-378 → 3.35.0-380

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 (29) hide show
  1. package/dist/index.js +1482 -562
  2. package/dist/index.js.map +1 -1
  3. package/dist/index.mjs +1421 -485
  4. package/dist/index.mjs.map +1 -1
  5. package/package.json +22 -21
  6. package/src/components/component-panel-header/component-badge.tsx +30 -22
  7. package/src/components/component-panel-header/component-panel-header.tsx +21 -1
  8. package/src/components/component-properties-panel/component-properties-panel-content.tsx +165 -0
  9. package/src/components/component-properties-panel/component-properties-panel.tsx +51 -0
  10. package/src/components/component-properties-panel/properties-empty-state.tsx +44 -0
  11. package/src/components/component-properties-panel/properties-group.tsx +191 -0
  12. package/src/components/component-properties-panel/property-item.tsx +121 -0
  13. package/src/components/component-properties-panel/sortable.tsx +92 -0
  14. package/src/components/component-properties-panel/use-current-editable-item.ts +74 -0
  15. package/src/components/component-properties-panel/utils/generate-unique-label.ts +21 -0
  16. package/src/components/component-properties-panel/utils/validate-group-label.ts +24 -0
  17. package/src/components/components-tab/component-introduction.tsx +68 -0
  18. package/src/components/instance-editing-panel/instance-editing-panel.tsx +1 -1
  19. package/src/components/overridable-props/overridable-prop-form.tsx +7 -4
  20. package/src/init.ts +3 -0
  21. package/src/store/actions/add-overridable-group.ts +47 -0
  22. package/src/store/actions/delete-overridable-group.ts +38 -0
  23. package/src/store/actions/delete-overridable-prop.ts +56 -0
  24. package/src/store/actions/rename-overridable-group.ts +39 -0
  25. package/src/store/actions/reorder-group-props.ts +43 -0
  26. package/src/store/actions/reorder-overridable-groups.ts +30 -0
  27. package/src/store/actions/set-overridable-prop.ts +21 -126
  28. package/src/store/actions/update-overridable-prop.ts +58 -0
  29. package/src/store/utils/groups-transformers.ts +185 -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": "3.35.0-378",
4
+ "version": "3.35.0-380",
5
5
  "private": false,
6
6
  "author": "Elementor Team",
7
7
  "homepage": "https://elementor.com/",
@@ -40,29 +40,30 @@
40
40
  "dev": "tsup --config=../../tsup.dev.ts"
41
41
  },
42
42
  "dependencies": {
43
- "@elementor/editor": "3.35.0-378",
44
- "@elementor/editor-canvas": "3.35.0-378",
45
- "@elementor/editor-controls": "3.35.0-378",
46
- "@elementor/editor-documents": "3.35.0-378",
47
- "@elementor/editor-editing-panel": "3.35.0-378",
48
- "@elementor/editor-elements": "3.35.0-378",
49
- "@elementor/editor-elements-panel": "3.35.0-378",
50
- "@elementor/editor-mcp": "3.35.0-378",
51
- "@elementor/editor-panels": "3.35.0-378",
52
- "@elementor/editor-props": "3.35.0-378",
53
- "@elementor/editor-styles-repository": "3.35.0-378",
54
- "@elementor/editor-ui": "3.35.0-378",
55
- "@elementor/editor-v1-adapters": "3.35.0-378",
56
- "@elementor/http-client": "3.35.0-378",
43
+ "@elementor/editor": "3.35.0-380",
44
+ "@elementor/editor-canvas": "3.35.0-380",
45
+ "@elementor/editor-controls": "3.35.0-380",
46
+ "@elementor/editor-documents": "3.35.0-380",
47
+ "@elementor/editor-editing-panel": "3.35.0-380",
48
+ "@elementor/editor-elements": "3.35.0-380",
49
+ "@elementor/editor-elements-panel": "3.35.0-380",
50
+ "@elementor/editor-mcp": "3.35.0-380",
51
+ "@elementor/editor-panels": "3.35.0-380",
52
+ "@elementor/editor-props": "3.35.0-380",
53
+ "@elementor/editor-styles-repository": "3.35.0-380",
54
+ "@elementor/editor-ui": "3.35.0-380",
55
+ "@elementor/editor-v1-adapters": "3.35.0-380",
56
+ "@elementor/http-client": "3.35.0-380",
57
57
  "@elementor/icons": "^1.63.0",
58
- "@elementor/mixpanel": "3.35.0-378",
59
- "@elementor/query": "3.35.0-378",
60
- "@elementor/schema": "3.35.0-378",
61
- "@elementor/store": "3.35.0-378",
58
+ "@elementor/mixpanel": "3.35.0-380",
59
+ "@elementor/query": "3.35.0-380",
60
+ "@elementor/schema": "3.35.0-380",
61
+ "@elementor/store": "3.35.0-380",
62
62
  "@elementor/ui": "1.36.17",
63
- "@elementor/utils": "3.35.0-378",
63
+ "@elementor/utils": "3.35.0-380",
64
64
  "@wordpress/i18n": "^5.13.0",
65
- "@elementor/editor-notifications": "3.35.0-378"
65
+ "@elementor/editor-notifications": "3.35.0-380",
66
+ "@elementor/editor-current-user": "3.35.0-380"
66
67
  },
67
68
  "peerDependencies": {
68
69
  "react": "^18.3.1",
@@ -4,30 +4,38 @@ import { ComponentPropListIcon } from '@elementor/icons';
4
4
  import { Badge, Box, keyframes, styled, ToggleButton } from '@elementor/ui';
5
5
  import { __ } from '@wordpress/i18n';
6
6
 
7
- export const ComponentsBadge = ( { overridesCount }: { overridesCount: number } ) => {
8
- const prevCount = usePrevious( overridesCount );
7
+ export const ComponentsBadge = React.forwardRef< HTMLDivElement, { overridesCount: number; onClick: () => void } >(
8
+ ( { overridesCount, onClick }, ref ) => {
9
+ const prevCount = usePrevious( overridesCount );
9
10
 
10
- const isFirstOverride = prevCount === 0 && overridesCount === 1;
11
+ const isFirstOverride = prevCount === 0 && overridesCount === 1;
11
12
 
12
- return (
13
- <StyledBadge
14
- color="primary"
15
- key={ overridesCount }
16
- invisible={ overridesCount === 0 }
17
- animate={ isFirstOverride }
18
- anchorOrigin={ { vertical: 'top', horizontal: 'right' } }
19
- badgeContent={
20
- <Box sx={ { animation: ! isFirstOverride ? `${ slideUp } 300ms ease-out` : 'none' } }>
21
- { overridesCount }
22
- </Box>
23
- }
24
- >
25
- <ToggleButton value="overrides" size="tiny" aria-label={ __( 'View overrides', 'elementor' ) }>
26
- <ComponentPropListIcon fontSize="tiny" />
27
- </ToggleButton>
28
- </StyledBadge>
29
- );
30
- };
13
+ return (
14
+ <StyledBadge
15
+ ref={ ref }
16
+ color="primary"
17
+ key={ overridesCount }
18
+ invisible={ overridesCount === 0 }
19
+ animate={ isFirstOverride }
20
+ anchorOrigin={ { vertical: 'top', horizontal: 'right' } }
21
+ badgeContent={
22
+ <Box sx={ { animation: ! isFirstOverride ? `${ slideUp } 300ms ease-out` : 'none' } }>
23
+ { overridesCount }
24
+ </Box>
25
+ }
26
+ >
27
+ <ToggleButton
28
+ value="overrides"
29
+ size="tiny"
30
+ onClick={ onClick }
31
+ aria-label={ __( 'View overrides', 'elementor' ) }
32
+ >
33
+ <ComponentPropListIcon fontSize="tiny" />
34
+ </ToggleButton>
35
+ </StyledBadge>
36
+ );
37
+ }
38
+ );
31
39
 
32
40
  const StyledBadge = styled( Badge, { shouldForwardProp: ( prop ) => prop !== 'animate' } )(
33
41
  ( { theme, animate } ) => ( {
@@ -1,4 +1,5 @@
1
1
  import * as React from 'react';
2
+ import { useSuppressedMessage } from '@elementor/editor-current-user';
2
3
  import { getV1DocumentsManager } from '@elementor/editor-documents';
3
4
  import { ArrowLeftIcon, ComponentsFilledIcon } from '@elementor/icons';
4
5
  import { Box, Divider, IconButton, Stack, Tooltip, Typography } from '@elementor/ui';
@@ -6,21 +7,35 @@ import { __ } from '@wordpress/i18n';
6
7
 
7
8
  import { useNavigateBack } from '../../hooks/use-navigate-back';
8
9
  import { useCurrentComponentId } from '../../store/store';
10
+ import { usePanelActions } from '../component-properties-panel/component-properties-panel';
11
+ import { ComponentIntroduction } from '../components-tab/component-introduction';
9
12
  import { ComponentsBadge } from './component-badge';
10
13
  import { useOverridableProps } from './use-overridable-props';
11
14
 
15
+ const MESSAGE_KEY = 'components-properties-introduction';
16
+
12
17
  export const ComponentPanelHeader = () => {
13
18
  const currentComponentId = useCurrentComponentId();
14
19
  const overridableProps = useOverridableProps( currentComponentId );
15
20
  const onBack = useNavigateBack();
16
21
  const componentName = getComponentName();
22
+ const [ isMessageSuppressed, suppressMessage ] = useSuppressedMessage( MESSAGE_KEY );
23
+ const [ shouldShowIntroduction, setShouldShowIntroduction ] = React.useState( ! isMessageSuppressed );
24
+
25
+ const { open: openPropertiesPanel } = usePanelActions();
17
26
 
18
27
  const overridesCount = overridableProps ? Object.keys( overridableProps.props ).length : 0;
28
+ const anchorRef = React.useRef< HTMLDivElement >( null );
19
29
 
20
30
  if ( ! currentComponentId ) {
21
31
  return null;
22
32
  }
23
33
 
34
+ const handleCloseIntroduction = () => {
35
+ suppressMessage();
36
+ setShouldShowIntroduction( false );
37
+ };
38
+
24
39
  return (
25
40
  <Box>
26
41
  <Stack
@@ -42,9 +57,14 @@ export const ComponentPanelHeader = () => {
42
57
  </Typography>
43
58
  </Stack>
44
59
  </Stack>
45
- <ComponentsBadge overridesCount={ overridesCount } />
60
+ <ComponentsBadge overridesCount={ overridesCount } ref={ anchorRef } onClick={ openPropertiesPanel } />
46
61
  </Stack>
47
62
  <Divider />
63
+ <ComponentIntroduction
64
+ anchorRef={ anchorRef }
65
+ shouldShowIntroduction={ shouldShowIntroduction }
66
+ onClose={ handleCloseIntroduction }
67
+ />
48
68
  </Box>
49
69
  );
50
70
  };
@@ -0,0 +1,165 @@
1
+ import * as React from 'react';
2
+ import { useMemo, useState } from 'react';
3
+ import { setDocumentModifiedStatus } from '@elementor/editor-documents';
4
+ import { PanelBody, PanelHeader, PanelHeaderTitle } from '@elementor/editor-panels';
5
+ import { ComponentPropListIcon, FolderPlusIcon, XIcon } from '@elementor/icons';
6
+ import { Divider, IconButton, List, Stack, Tooltip } from '@elementor/ui';
7
+ import { generateUniqueId } from '@elementor/utils';
8
+ import { __ } from '@wordpress/i18n';
9
+
10
+ import { addOverridableGroup } from '../../store/actions/add-overridable-group';
11
+ import { deleteOverridableGroup } from '../../store/actions/delete-overridable-group';
12
+ import { deleteOverridableProp } from '../../store/actions/delete-overridable-prop';
13
+ import { reorderGroupProps } from '../../store/actions/reorder-group-props';
14
+ import { reorderOverridableGroups } from '../../store/actions/reorder-overridable-groups';
15
+ import { updateOverridableProp } from '../../store/actions/update-overridable-prop';
16
+ import { useCurrentComponentId } from '../../store/store';
17
+ import { useOverridableProps } from '../component-panel-header/use-overridable-props';
18
+ import { PropertiesEmptyState } from './properties-empty-state';
19
+ import { PropertiesGroup } from './properties-group';
20
+ import { SortableItem, SortableProvider } from './sortable';
21
+ import { useCurrentEditableItem } from './use-current-editable-item';
22
+ import { generateUniqueLabel } from './utils/generate-unique-label';
23
+
24
+ type Props = {
25
+ onClose: () => void;
26
+ };
27
+
28
+ export function ComponentPropertiesPanelContent( { onClose }: Props ) {
29
+ const currentComponentId = useCurrentComponentId();
30
+ const overridableProps = useOverridableProps( currentComponentId );
31
+ const [ isAddingGroup, setIsAddingGroup ] = useState( false );
32
+ const groupLabelEditable = useCurrentEditableItem();
33
+
34
+ const groups = useMemo( () => {
35
+ if ( ! overridableProps ) {
36
+ return [];
37
+ }
38
+
39
+ return overridableProps.groups.order
40
+ .map( ( groupId ) => overridableProps.groups.items[ groupId ] ?? null )
41
+ .filter( Boolean );
42
+ }, [ overridableProps ] );
43
+
44
+ const allGroupsForSelect = useMemo(
45
+ () => groups.map( ( group ) => ( { value: group.id, label: group.label } ) ),
46
+ [ groups ]
47
+ );
48
+
49
+ if ( ! currentComponentId || ! overridableProps ) {
50
+ return null;
51
+ }
52
+
53
+ const hasGroups = groups.length > 0;
54
+ const showEmptyState = ! hasGroups && ! isAddingGroup;
55
+ const groupIds = overridableProps.groups.order;
56
+
57
+ const handleAddGroupClick = () => {
58
+ if ( isAddingGroup ) {
59
+ return;
60
+ }
61
+
62
+ const newGroupId = generateUniqueId( 'group' );
63
+ const newLabel = generateUniqueLabel( groups );
64
+
65
+ addOverridableGroup( { componentId: currentComponentId, groupId: newGroupId, label: newLabel } );
66
+ setDocumentModifiedStatus( true );
67
+ setIsAddingGroup( false );
68
+
69
+ groupLabelEditable.setEditingGroupId( newGroupId );
70
+ };
71
+
72
+ const handleGroupsReorder = ( newOrder: string[] ) => {
73
+ reorderOverridableGroups( { componentId: currentComponentId, newOrder } );
74
+ setDocumentModifiedStatus( true );
75
+ };
76
+
77
+ const handlePropsReorder = ( groupId: string, newPropsOrder: string[] ) => {
78
+ reorderGroupProps( { componentId: currentComponentId, groupId, newPropsOrder } );
79
+ setDocumentModifiedStatus( true );
80
+ };
81
+
82
+ const handlePropertyDelete = ( propKey: string ) => {
83
+ deleteOverridableProp( { componentId: currentComponentId, propKey } );
84
+ setDocumentModifiedStatus( true );
85
+ };
86
+
87
+ const handlePropertyUpdate = ( propKey: string, data: { label: string; group: string | null } ) => {
88
+ updateOverridableProp( {
89
+ componentId: currentComponentId,
90
+ propKey,
91
+ label: data.label,
92
+ groupId: data.group,
93
+ } );
94
+ setDocumentModifiedStatus( true );
95
+ };
96
+
97
+ const handleGroupDelete = ( groupId: string ) => {
98
+ deleteOverridableGroup( { componentId: currentComponentId, groupId } );
99
+ setDocumentModifiedStatus( true );
100
+ };
101
+
102
+ return (
103
+ <>
104
+ <PanelHeader sx={ { justifyContent: 'start', pl: 1.5, pr: 1, py: 1 } }>
105
+ <Stack direction="row" alignItems="center" gap={ 0.5 } flexGrow={ 1 }>
106
+ <ComponentPropListIcon fontSize="tiny" />
107
+ <PanelHeaderTitle variant="subtitle2">
108
+ { __( 'Component properties', 'elementor' ) }
109
+ </PanelHeaderTitle>
110
+ </Stack>
111
+
112
+ { ! showEmptyState && (
113
+ <Tooltip title={ __( 'Add new group', 'elementor' ) }>
114
+ <IconButton
115
+ size="tiny"
116
+ aria-label={ __( 'Add new group', 'elementor' ) }
117
+ onClick={ handleAddGroupClick }
118
+ >
119
+ <FolderPlusIcon fontSize="tiny" />
120
+ </IconButton>
121
+ </Tooltip>
122
+ ) }
123
+
124
+ <Tooltip title={ __( 'Close panel', 'elementor' ) }>
125
+ <IconButton size="tiny" aria-label={ __( 'Close panel', 'elementor' ) } onClick={ onClose }>
126
+ <XIcon fontSize="tiny" />
127
+ </IconButton>
128
+ </Tooltip>
129
+ </PanelHeader>
130
+
131
+ <Divider />
132
+
133
+ <PanelBody>
134
+ { showEmptyState ? (
135
+ <PropertiesEmptyState />
136
+ ) : (
137
+ <List sx={ { p: 2, display: 'flex', flexDirection: 'column', gap: 2 } }>
138
+ <SortableProvider value={ groupIds } onChange={ handleGroupsReorder }>
139
+ { groups.map( ( group ) => (
140
+ <SortableItem key={ group.id } id={ group.id }>
141
+ { ( { triggerProps, triggerStyle, isDragPlaceholder } ) => (
142
+ <PropertiesGroup
143
+ group={ group }
144
+ props={ overridableProps.props }
145
+ allGroups={ allGroupsForSelect }
146
+ allGroupsRecord={ overridableProps.groups.items }
147
+ sortableTriggerProps={ { ...triggerProps, style: triggerStyle } }
148
+ isDragPlaceholder={ isDragPlaceholder }
149
+ setIsAddingGroup={ setIsAddingGroup }
150
+ onPropsReorder={ ( newOrder ) => handlePropsReorder( group.id, newOrder ) }
151
+ onPropertyDelete={ handlePropertyDelete }
152
+ onPropertyUpdate={ handlePropertyUpdate }
153
+ editableLabelProps={ groupLabelEditable }
154
+ onGroupDelete={ handleGroupDelete }
155
+ />
156
+ ) }
157
+ </SortableItem>
158
+ ) ) }
159
+ </SortableProvider>
160
+ </List>
161
+ ) }
162
+ </PanelBody>
163
+ </>
164
+ );
165
+ }
@@ -0,0 +1,51 @@
1
+ import * as React from 'react';
2
+ import { ElementProvider, usePanelActions as useEditingPanelActions } from '@elementor/editor-editing-panel';
3
+ import { useSelectedElement } from '@elementor/editor-elements';
4
+ import { __createPanel as createPanel, Panel } from '@elementor/editor-panels';
5
+ import { ThemeProvider } from '@elementor/editor-ui';
6
+ import { Alert, Box, ErrorBoundary } from '@elementor/ui';
7
+ import { __ } from '@wordpress/i18n';
8
+
9
+ import { ComponentPropertiesPanelContent } from './component-properties-panel-content';
10
+
11
+ const id = 'component-properties-panel';
12
+
13
+ export const { panel, usePanelActions } = createPanel( {
14
+ id,
15
+ component: ComponentPropertiesPanel,
16
+ } );
17
+
18
+ function ComponentPropertiesPanel() {
19
+ const { element, elementType } = useSelectedElement();
20
+ const { close: closePanel } = usePanelActions();
21
+ const { open: openEditingPanel } = useEditingPanelActions();
22
+
23
+ if ( ! element || ! elementType ) {
24
+ return null;
25
+ }
26
+
27
+ return (
28
+ <ThemeProvider>
29
+ <ErrorBoundary fallback={ <ErrorBoundaryFallback /> }>
30
+ <ElementProvider element={ element } elementType={ elementType }>
31
+ <Panel>
32
+ <ComponentPropertiesPanelContent
33
+ onClose={ () => {
34
+ closePanel();
35
+ openEditingPanel();
36
+ } }
37
+ />
38
+ </Panel>
39
+ </ElementProvider>
40
+ </ErrorBoundary>
41
+ </ThemeProvider>
42
+ );
43
+ }
44
+
45
+ const ErrorBoundaryFallback = () => (
46
+ <Box role="alert" sx={ { minHeight: '100%', p: 2 } }>
47
+ <Alert severity="error" sx={ { mb: 2, maxWidth: 400, textAlign: 'center' } }>
48
+ <strong>{ __( 'Something went wrong', 'elementor' ) }</strong>
49
+ </Alert>
50
+ </Box>
51
+ );
@@ -0,0 +1,44 @@
1
+ import * as React from 'react';
2
+ import { ComponentPropListIcon } from '@elementor/icons';
3
+ import { Link, Stack, Typography } from '@elementor/ui';
4
+ import { __ } from '@wordpress/i18n';
5
+
6
+ const LEARN_MORE_URL = 'https://go.elementor.com/tbd/';
7
+
8
+ export function PropertiesEmptyState() {
9
+ return (
10
+ <Stack
11
+ alignItems="center"
12
+ justifyContent="flex-start"
13
+ height="100%"
14
+ color="text.secondary"
15
+ sx={ { px: 2.5, pt: 10, pb: 5.5 } }
16
+ gap={ 1 }
17
+ >
18
+ <ComponentPropListIcon fontSize="large" />
19
+
20
+ <Typography align="center" variant="subtitle2">
21
+ { __( 'Add your first property', 'elementor' ) }
22
+ </Typography>
23
+
24
+ <Typography align="center" variant="caption">
25
+ { __( 'Make instances flexible while keeping design synced.', 'elementor' ) }
26
+ </Typography>
27
+
28
+ <Typography align="center" variant="caption">
29
+ { __( 'Select any element, then click + next to a setting to expose it.', 'elementor' ) }
30
+ </Typography>
31
+
32
+ <Link
33
+ variant="caption"
34
+ color="secondary"
35
+ href={ LEARN_MORE_URL }
36
+ target="_blank"
37
+ rel="noopener noreferrer"
38
+ sx={ { textDecorationLine: 'underline' } }
39
+ >
40
+ { __( 'Learn more', 'elementor' ) }
41
+ </Link>
42
+ </Stack>
43
+ );
44
+ }
@@ -0,0 +1,191 @@
1
+ import * as React from 'react';
2
+ import { EditableField, MenuListItem } from '@elementor/editor-ui';
3
+ import { DotsVerticalIcon } from '@elementor/icons';
4
+ import {
5
+ bindMenu,
6
+ bindTrigger,
7
+ Box,
8
+ IconButton,
9
+ List,
10
+ Menu,
11
+ Stack,
12
+ Tooltip,
13
+ Typography,
14
+ usePopupState,
15
+ } from '@elementor/ui';
16
+ import { __ } from '@wordpress/i18n';
17
+
18
+ import { type OverridableProp, type OverridablePropsGroup } from '../../types';
19
+ import { PropertyItem } from './property-item';
20
+ import { SortableItem, SortableProvider, SortableTrigger, type SortableTriggerProps } from './sortable';
21
+ import { type GroupLabelEditableState } from './use-current-editable-item';
22
+
23
+ type Props = {
24
+ group: OverridablePropsGroup;
25
+ props: Record< string, OverridableProp >;
26
+ allGroups: { value: string; label: string }[];
27
+ allGroupsRecord: Record< string, OverridablePropsGroup >;
28
+ sortableTriggerProps: SortableTriggerProps;
29
+ isDragPlaceholder?: boolean;
30
+ setIsAddingGroup: ( isAddingGroup: boolean ) => void;
31
+ onPropsReorder: ( newOrder: string[] ) => void;
32
+ onPropertyDelete: ( propKey: string ) => void;
33
+ onPropertyUpdate: ( propKey: string, data: { label: string; group: string | null } ) => void;
34
+ onGroupDelete: ( groupId: string ) => void;
35
+ editableLabelProps: GroupLabelEditableState;
36
+ };
37
+
38
+ export function PropertiesGroup( {
39
+ group,
40
+ props,
41
+ allGroups,
42
+ sortableTriggerProps,
43
+ isDragPlaceholder,
44
+ onPropsReorder,
45
+ onPropertyDelete,
46
+ onPropertyUpdate,
47
+ onGroupDelete,
48
+ editableLabelProps,
49
+ }: Props ) {
50
+ const groupProps = group.props
51
+ .map( ( propId ) => props[ propId ] )
52
+ .filter( ( prop ): prop is OverridableProp => Boolean( prop ) );
53
+
54
+ const popupState = usePopupState( {
55
+ variant: 'popover',
56
+ disableAutoFocus: true,
57
+ } );
58
+
59
+ const { editableRef, isEditing, error, getEditableProps, setEditingGroupId, editingGroupId } = editableLabelProps;
60
+
61
+ const hasProperties = group.props.length > 0;
62
+ const isThisGroupEditing = isEditing && editingGroupId === group.id;
63
+
64
+ const handleRenameClick = () => {
65
+ popupState.close();
66
+ setEditingGroupId( group.id );
67
+ };
68
+
69
+ const handleDeleteClick = () => {
70
+ popupState.close();
71
+ onGroupDelete( group.id );
72
+ };
73
+
74
+ return (
75
+ <Box
76
+ sx={ {
77
+ opacity: isDragPlaceholder ? 0.5 : 1,
78
+ } }
79
+ >
80
+ <Stack gap={ 1 }>
81
+ <Box
82
+ className="group-header"
83
+ sx={ {
84
+ position: 'relative',
85
+ '&:hover .group-sortable-trigger': {
86
+ visibility: 'visible',
87
+ },
88
+ '& .group-sortable-trigger': {
89
+ visibility: 'hidden',
90
+ },
91
+ '&:hover .group-menu': {
92
+ visibility: 'visible',
93
+ },
94
+ '& .group-menu': {
95
+ visibility: 'hidden',
96
+ },
97
+ } }
98
+ >
99
+ <SortableTrigger triggerClassName="group-sortable-trigger" { ...sortableTriggerProps } />
100
+ <Stack direction="row" alignItems="center" justifyContent="space-between" gap={ 2 }>
101
+ { isThisGroupEditing ? (
102
+ <Box
103
+ sx={ {
104
+ flex: 1,
105
+ height: 28,
106
+ display: 'flex',
107
+ alignItems: 'center',
108
+ border: 2,
109
+ borderColor: 'text.secondary',
110
+ borderRadius: 1,
111
+ pl: 0.5,
112
+ } }
113
+ >
114
+ <EditableField
115
+ ref={ editableRef }
116
+ as={ Typography }
117
+ variant="caption"
118
+ error={ error ?? undefined }
119
+ sx={ { color: 'text.primary', fontWeight: 400, lineHeight: 1.66 } }
120
+ { ...getEditableProps() }
121
+ />
122
+ </Box>
123
+ ) : (
124
+ <Typography
125
+ variant="caption"
126
+ sx={ { color: 'text.primary', fontWeight: 400, lineHeight: 1.66 } }
127
+ >
128
+ { group.label }
129
+ </Typography>
130
+ ) }
131
+ <IconButton
132
+ className="group-menu"
133
+ size="tiny"
134
+ sx={ { p: 0.25, visibility: isThisGroupEditing ? 'visible' : undefined } }
135
+ aria-label={ __( 'Group actions', 'elementor' ) }
136
+ { ...bindTrigger( popupState ) }
137
+ >
138
+ <DotsVerticalIcon fontSize="tiny" />
139
+ </IconButton>
140
+ </Stack>
141
+ </Box>
142
+ <List sx={ { p: 0, display: 'flex', flexDirection: 'column', gap: 1 } }>
143
+ <SortableProvider value={ group.props } onChange={ onPropsReorder }>
144
+ { groupProps.map( ( prop ) => (
145
+ <SortableItem key={ prop.overrideKey } id={ prop.overrideKey }>
146
+ { ( { triggerProps, triggerStyle, isDragPlaceholder: isItemDragPlaceholder } ) => (
147
+ <PropertyItem
148
+ prop={ prop }
149
+ sortableTriggerProps={ { ...triggerProps, style: triggerStyle } }
150
+ isDragPlaceholder={ isItemDragPlaceholder }
151
+ groups={ allGroups }
152
+ onDelete={ onPropertyDelete }
153
+ onUpdate={ ( data ) => onPropertyUpdate( prop.overrideKey, data ) }
154
+ />
155
+ ) }
156
+ </SortableItem>
157
+ ) ) }
158
+ </SortableProvider>
159
+ </List>
160
+ </Stack>
161
+ <Menu
162
+ { ...bindMenu( popupState ) }
163
+ anchorOrigin={ { vertical: 'bottom', horizontal: 'right' } }
164
+ transformOrigin={ { vertical: 'top', horizontal: 'right' } }
165
+ >
166
+ <MenuListItem sx={ { minWidth: '160px' } } onClick={ handleRenameClick }>
167
+ <Typography variant="caption" sx={ { color: 'text.primary' } }>
168
+ { __( 'Rename', 'elementor' ) }
169
+ </Typography>
170
+ </MenuListItem>
171
+ <Tooltip
172
+ title={
173
+ hasProperties ? __( 'To delete the group, first remove all the properties', 'elementor' ) : ''
174
+ }
175
+ placement="right"
176
+ >
177
+ <span>
178
+ <MenuListItem onClick={ handleDeleteClick } disabled={ hasProperties }>
179
+ <Typography
180
+ variant="caption"
181
+ sx={ { color: hasProperties ? 'text.disabled' : 'error.light' } }
182
+ >
183
+ { __( 'Delete', 'elementor' ) }
184
+ </Typography>
185
+ </MenuListItem>
186
+ </span>
187
+ </Tooltip>
188
+ </Menu>
189
+ </Box>
190
+ );
191
+ }