@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.
- package/dist/index.js +1482 -562
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1421 -485
- package/dist/index.mjs.map +1 -1
- package/package.json +22 -21
- package/src/components/component-panel-header/component-badge.tsx +30 -22
- package/src/components/component-panel-header/component-panel-header.tsx +21 -1
- package/src/components/component-properties-panel/component-properties-panel-content.tsx +165 -0
- package/src/components/component-properties-panel/component-properties-panel.tsx +51 -0
- package/src/components/component-properties-panel/properties-empty-state.tsx +44 -0
- package/src/components/component-properties-panel/properties-group.tsx +191 -0
- package/src/components/component-properties-panel/property-item.tsx +121 -0
- package/src/components/component-properties-panel/sortable.tsx +92 -0
- package/src/components/component-properties-panel/use-current-editable-item.ts +74 -0
- package/src/components/component-properties-panel/utils/generate-unique-label.ts +21 -0
- package/src/components/component-properties-panel/utils/validate-group-label.ts +24 -0
- package/src/components/components-tab/component-introduction.tsx +68 -0
- package/src/components/instance-editing-panel/instance-editing-panel.tsx +1 -1
- package/src/components/overridable-props/overridable-prop-form.tsx +7 -4
- package/src/init.ts +3 -0
- package/src/store/actions/add-overridable-group.ts +47 -0
- package/src/store/actions/delete-overridable-group.ts +38 -0
- package/src/store/actions/delete-overridable-prop.ts +56 -0
- package/src/store/actions/rename-overridable-group.ts +39 -0
- package/src/store/actions/reorder-group-props.ts +43 -0
- package/src/store/actions/reorder-overridable-groups.ts +30 -0
- package/src/store/actions/set-overridable-prop.ts +21 -126
- package/src/store/actions/update-overridable-prop.ts +58 -0
- 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-
|
|
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-
|
|
44
|
-
"@elementor/editor-canvas": "3.35.0-
|
|
45
|
-
"@elementor/editor-controls": "3.35.0-
|
|
46
|
-
"@elementor/editor-documents": "3.35.0-
|
|
47
|
-
"@elementor/editor-editing-panel": "3.35.0-
|
|
48
|
-
"@elementor/editor-elements": "3.35.0-
|
|
49
|
-
"@elementor/editor-elements-panel": "3.35.0-
|
|
50
|
-
"@elementor/editor-mcp": "3.35.0-
|
|
51
|
-
"@elementor/editor-panels": "3.35.0-
|
|
52
|
-
"@elementor/editor-props": "3.35.0-
|
|
53
|
-
"@elementor/editor-styles-repository": "3.35.0-
|
|
54
|
-
"@elementor/editor-ui": "3.35.0-
|
|
55
|
-
"@elementor/editor-v1-adapters": "3.35.0-
|
|
56
|
-
"@elementor/http-client": "3.35.0-
|
|
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-
|
|
59
|
-
"@elementor/query": "3.35.0-
|
|
60
|
-
"@elementor/schema": "3.35.0-
|
|
61
|
-
"@elementor/store": "3.35.0-
|
|
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-
|
|
63
|
+
"@elementor/utils": "3.35.0-380",
|
|
64
64
|
"@wordpress/i18n": "^5.13.0",
|
|
65
|
-
"@elementor/editor-notifications": "3.35.0-
|
|
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 =
|
|
8
|
-
|
|
7
|
+
export const ComponentsBadge = React.forwardRef< HTMLDivElement, { overridesCount: number; onClick: () => void } >(
|
|
8
|
+
( { overridesCount, onClick }, ref ) => {
|
|
9
|
+
const prevCount = usePrevious( overridesCount );
|
|
9
10
|
|
|
10
|
-
|
|
11
|
+
const isFirstOverride = prevCount === 0 && overridesCount === 1;
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
{
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
<
|
|
27
|
-
|
|
28
|
-
|
|
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
|
+
}
|