@elementor/editor-components 4.0.0-manual → 4.0.1
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.d.mts +1422 -1
- package/dist/index.d.ts +1422 -1
- package/dist/index.js +2096 -4814
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2028 -4837
- package/dist/index.mjs.map +1 -1
- package/package.json +23 -23
- package/src/components/components-tab/components-list.tsx +92 -4
- package/src/components/components-tab/components-pro-notification.tsx +9 -15
- package/src/components/components-tab/components-update-notification.tsx +13 -0
- package/src/components/components-tab/components.tsx +52 -3
- package/src/components/components-tab/loading-components.tsx +26 -14
- package/src/components/components-update-alert.tsx +40 -0
- package/src/components/components-upgrade-alert.tsx +39 -0
- package/src/components/detach-instance-confirmation-dialog.tsx +50 -0
- package/src/components/instance-editing-panel/detach-action.tsx +76 -0
- package/src/components/instance-editing-panel/empty-state.tsx +9 -2
- package/src/components/instance-editing-panel/instance-editing-panel.tsx +34 -6
- package/src/components/instance-editing-panel/override-prop-control.tsx +14 -6
- package/src/components/instance-editing-panel/use-instance-panel-data.ts +2 -2
- package/src/components/instance-editing-panel/utils/correct-exposed-empty-override.ts +28 -0
- package/src/consts.ts +1 -0
- package/src/create-component-type.ts +130 -29
- package/src/index.ts +92 -0
- package/src/init.ts +6 -4
- package/src/store/actions/update-overridable-prop.ts +4 -10
- package/src/store/dispatchers.ts +63 -0
- package/src/store/extensible-slice.ts +168 -0
- package/src/store/selectors.ts +53 -0
- package/src/store/store-types.ts +48 -0
- package/src/store/store.ts +7 -169
- package/src/sync/publish-draft-components-in-page-before-save.ts +42 -1
- package/src/types.ts +1 -1
- package/src/utils/detach-component-instance/detach-component-instance.ts +172 -0
- package/src/utils/detach-component-instance/index.ts +1 -0
- package/src/utils/detach-component-instance/regenerate-local-style-ids.ts +53 -0
- package/src/utils/detach-component-instance/resolve-detached-instance.ts +94 -0
- package/src/utils/detach-component-instance/resolve-overridable-settings.ts +121 -0
- package/src/utils/is-component-instance.ts +1 -1
- package/src/utils/is-pro-components-supported.ts +11 -0
- package/src/utils/tracking.ts +2 -1
- package/src/extended/components/component-introduction.tsx +0 -77
- package/src/extended/components/component-panel-header/component-badge.tsx +0 -73
- package/src/extended/components/component-panel-header/component-panel-header.tsx +0 -98
- package/src/extended/components/component-properties-panel/component-properties-panel-content.tsx +0 -176
- package/src/extended/components/component-properties-panel/component-properties-panel.tsx +0 -43
- package/src/extended/components/component-properties-panel/properties-empty-state.tsx +0 -51
- package/src/extended/components/component-properties-panel/properties-group.tsx +0 -196
- package/src/extended/components/component-properties-panel/property-item.tsx +0 -124
- package/src/extended/components/component-properties-panel/sortable.tsx +0 -92
- package/src/extended/components/component-properties-panel/use-current-editable-item.ts +0 -73
- package/src/extended/components/component-properties-panel/utils/generate-unique-label.ts +0 -21
- package/src/extended/components/component-properties-panel/utils/validate-group-label.ts +0 -24
- package/src/extended/components/components-tab/component-item.tsx +0 -180
- package/src/extended/components/components-tab/components.tsx +0 -58
- package/src/extended/components/components-tab/delete-confirmation-dialog.tsx +0 -26
- package/src/extended/components/create-component-form/create-component-form.tsx +0 -282
- package/src/extended/components/create-component-form/hooks/use-form.ts +0 -72
- package/src/extended/components/create-component-form/utils/get-component-event-data.ts +0 -54
- package/src/extended/components/edit-component/component-modal.tsx +0 -133
- package/src/extended/components/edit-component/edit-component.tsx +0 -166
- package/src/extended/components/edit-component/use-canvas-document.ts +0 -9
- package/src/extended/components/edit-component/use-element-rect.ts +0 -81
- package/src/extended/components/instance-editing-panel/instance-editing-panel.tsx +0 -60
- package/src/extended/components/overridable-props/indicator.tsx +0 -83
- package/src/extended/components/overridable-props/overridable-prop-control.tsx +0 -127
- package/src/extended/components/overridable-props/overridable-prop-form.tsx +0 -135
- package/src/extended/components/overridable-props/overridable-prop-indicator.tsx +0 -138
- package/src/extended/components/overridable-props/utils/validate-prop-label.ts +0 -38
- package/src/extended/consts.ts +0 -3
- package/src/extended/hooks/use-navigate-back.ts +0 -24
- package/src/extended/init.ts +0 -104
- package/src/extended/mcp/index.ts +0 -14
- package/src/extended/mcp/save-as-component-tool.ts +0 -436
- package/src/extended/store/actions/add-overridable-group.ts +0 -59
- package/src/extended/store/actions/archive-component.ts +0 -19
- package/src/extended/store/actions/create-unpublished-component.ts +0 -102
- package/src/extended/store/actions/delete-overridable-group.ts +0 -38
- package/src/extended/store/actions/delete-overridable-prop.ts +0 -70
- package/src/extended/store/actions/rename-component.ts +0 -49
- package/src/extended/store/actions/rename-overridable-group.ts +0 -39
- package/src/extended/store/actions/reorder-group-props.ts +0 -43
- package/src/extended/store/actions/reorder-overridable-groups.ts +0 -30
- package/src/extended/store/actions/reset-sanitized-components.ts +0 -7
- package/src/extended/store/actions/set-overridable-prop.ts +0 -117
- package/src/extended/store/actions/update-component-sanitized-attribute.ts +0 -8
- package/src/extended/store/actions/update-current-component.ts +0 -21
- package/src/extended/store/actions/update-overridable-prop-params.ts +0 -58
- package/src/extended/store/utils/groups-transformers.ts +0 -187
- package/src/extended/sync/before-save.ts +0 -52
- package/src/extended/sync/cleanup-overridable-props-on-delete.ts +0 -85
- package/src/extended/sync/create-components-before-save.ts +0 -113
- package/src/extended/sync/handle-component-edit-mode-container.ts +0 -114
- package/src/extended/sync/prevent-non-atomic-nesting.ts +0 -198
- package/src/extended/sync/revert-overridables-on-copy-or-duplicate.ts +0 -66
- package/src/extended/sync/sanitize-overridable-props.ts +0 -32
- package/src/extended/sync/set-component-overridable-props-settings-before-save.ts +0 -23
- package/src/extended/sync/update-archived-component-before-save.ts +0 -32
- package/src/extended/sync/update-component-title-before-save.ts +0 -19
- package/src/extended/utils/component-form-schema.ts +0 -32
- package/src/extended/utils/component-name-validation.ts +0 -27
- package/src/extended/utils/create-component-model.ts +0 -28
- package/src/extended/utils/get-container-for-new-element.ts +0 -49
- package/src/extended/utils/is-editing-component.ts +0 -13
- package/src/extended/utils/replace-element-with-component.ts +0 -11
- package/src/extended/utils/revert-overridable-settings.ts +0 -207
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { generateElementId, type V1ElementData, type V1ElementSettingsProps } from '@elementor/editor-elements';
|
|
2
|
+
import { classesPropTypeUtil, type ClassesPropValue, type PropValue } from '@elementor/editor-props';
|
|
3
|
+
import { type StyleDefinition, type StyleDefinitionID } from '@elementor/editor-styles';
|
|
4
|
+
|
|
5
|
+
// Ts version for atomic-widgets/assets/js/editor/utils/regenerate-local-style-ids.js
|
|
6
|
+
export function regenerateLocalStyleIds( element: V1ElementData ): {
|
|
7
|
+
styles: Record< StyleDefinitionID, StyleDefinition > | undefined;
|
|
8
|
+
settings: V1ElementSettingsProps | undefined;
|
|
9
|
+
} {
|
|
10
|
+
const originalStyles = element.styles;
|
|
11
|
+
|
|
12
|
+
if ( ! originalStyles || Object.keys( originalStyles ).length === 0 ) {
|
|
13
|
+
return { styles: undefined, settings: undefined };
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const newStyles: Record< string, StyleDefinition > = {};
|
|
17
|
+
const styleIdMapping: Record< string, string > = {};
|
|
18
|
+
|
|
19
|
+
for ( const [ originalStyleId, style ] of Object.entries( originalStyles ) ) {
|
|
20
|
+
const newStyleId = generateLocalStyleId( element.id );
|
|
21
|
+
|
|
22
|
+
newStyles[ newStyleId ] = { ...style, id: newStyleId };
|
|
23
|
+
styleIdMapping[ originalStyleId ] = newStyleId;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const settings = element.settings;
|
|
27
|
+
if ( ! settings || Object.keys( settings ).length === 0 ) {
|
|
28
|
+
return { styles: newStyles, settings: undefined };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const updatedSettings = { ...settings };
|
|
32
|
+
|
|
33
|
+
for ( const [ propKey, propValue ] of Object.entries( updatedSettings ) ) {
|
|
34
|
+
if ( isClassesProp( propValue ) && propValue.value.length > 0 ) {
|
|
35
|
+
const updatedClasses = propValue.value.map( ( classId ) => styleIdMapping[ classId ] ?? classId );
|
|
36
|
+
|
|
37
|
+
updatedSettings[ propKey ] = classesPropTypeUtil.create( updatedClasses );
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
styles: newStyles,
|
|
43
|
+
settings: updatedSettings,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function isClassesProp( prop: PropValue ): prop is ClassesPropValue {
|
|
48
|
+
return classesPropTypeUtil.isValid( prop );
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function generateLocalStyleId( elementId: string ): string {
|
|
52
|
+
return `e-${ elementId }-${ generateElementId() }`;
|
|
53
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { generateElementId, type V1ElementData } from '@elementor/editor-elements';
|
|
2
|
+
import { type PropValue } from '@elementor/editor-props';
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
type ComponentInstanceOverrideProp,
|
|
6
|
+
componentInstanceOverridePropTypeUtil,
|
|
7
|
+
} from '../../prop-types/component-instance-override-prop-type';
|
|
8
|
+
import { type ComponentInstanceOverride } from '../../prop-types/component-instance-overrides-prop-type';
|
|
9
|
+
import { componentOverridablePropTypeUtil } from '../../prop-types/component-overridable-prop-type';
|
|
10
|
+
import { regenerateLocalStyleIds } from './regenerate-local-style-ids';
|
|
11
|
+
import { resolveOverridableSettings } from './resolve-overridable-settings';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Creates detached element from component instance
|
|
15
|
+
* by applying overrides, reverting overridables, and regenerating IDs.
|
|
16
|
+
* This is used when detaching a component instance from its origin component.
|
|
17
|
+
*
|
|
18
|
+
* The function goes through all nested elements recursively and:
|
|
19
|
+
* - Regenerates element IDs and local style IDs
|
|
20
|
+
* - Resolves overridable settings - applies all instance overrides to element settings,
|
|
21
|
+
* or replaces overridable with origin_value when no matching override exists.
|
|
22
|
+
*
|
|
23
|
+
* @param element - The component's root element
|
|
24
|
+
* @param overrides - Array of overrides from the component instance
|
|
25
|
+
* @return A new element data with all overrides applied and new IDs
|
|
26
|
+
*/
|
|
27
|
+
export function resolveDetachedInstance(
|
|
28
|
+
element: V1ElementData,
|
|
29
|
+
overrides: ComponentInstanceOverride[]
|
|
30
|
+
): V1ElementData {
|
|
31
|
+
const overrideMap = createOverrideMap( overrides );
|
|
32
|
+
|
|
33
|
+
return resolveElementRecursive( structuredClone( element ), overrideMap );
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function resolveElementRecursive(
|
|
37
|
+
element: V1ElementData,
|
|
38
|
+
overrideMap: Map< string, ComponentInstanceOverrideProp >
|
|
39
|
+
): V1ElementData {
|
|
40
|
+
element.id = generateElementId();
|
|
41
|
+
|
|
42
|
+
if ( element.styles ) {
|
|
43
|
+
const { styles, settings } = regenerateLocalStyleIds( element );
|
|
44
|
+
|
|
45
|
+
element.styles = styles;
|
|
46
|
+
if ( settings ) {
|
|
47
|
+
element.settings = { ...element.settings, ...settings };
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if ( element.settings ) {
|
|
52
|
+
element.settings = resolveOverridableSettings( element, overrideMap );
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if ( element.elements?.length ) {
|
|
56
|
+
element.elements = element.elements.map( ( child ) => resolveElementRecursive( child, overrideMap ) );
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return element;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function createOverrideMap( overrides: ComponentInstanceOverride[] ): Map< string, ComponentInstanceOverrideProp > {
|
|
63
|
+
const map = new Map< string, ComponentInstanceOverrideProp >();
|
|
64
|
+
|
|
65
|
+
overrides.forEach( ( item ) => {
|
|
66
|
+
let override: ComponentInstanceOverrideProp | null = null;
|
|
67
|
+
|
|
68
|
+
if ( componentInstanceOverridePropTypeUtil.isValid( item ) ) {
|
|
69
|
+
override = item;
|
|
70
|
+
} else if ( componentOverridablePropTypeUtil.isValid( item ) ) {
|
|
71
|
+
override = getOverridableOverride( item );
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if ( override ) {
|
|
75
|
+
const overrideKey = override.value.override_key;
|
|
76
|
+
map.set( overrideKey, override );
|
|
77
|
+
}
|
|
78
|
+
} );
|
|
79
|
+
|
|
80
|
+
return map;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function getOverridableOverride( propValue: PropValue ): ComponentInstanceOverrideProp | null {
|
|
84
|
+
if ( ! componentOverridablePropTypeUtil.isValid( propValue ) ) {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const originValue = componentOverridablePropTypeUtil.extract( propValue )?.origin_value;
|
|
89
|
+
if ( ! componentInstanceOverridePropTypeUtil.isValid( originValue ) ) {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return originValue;
|
|
94
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { type V1ElementData, type V1ElementSettingsProps } from '@elementor/editor-elements';
|
|
2
|
+
import { type PropValue } from '@elementor/editor-props';
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
type ComponentInstanceOverrideProp,
|
|
6
|
+
componentInstanceOverridePropTypeUtil,
|
|
7
|
+
} from '../../prop-types/component-instance-override-prop-type';
|
|
8
|
+
import {
|
|
9
|
+
type ComponentInstanceProp,
|
|
10
|
+
componentInstancePropTypeUtil,
|
|
11
|
+
} from '../../prop-types/component-instance-prop-type';
|
|
12
|
+
import { componentOverridablePropTypeUtil } from '../../prop-types/component-overridable-prop-type';
|
|
13
|
+
import { isComponentInstance } from '../is-component-instance';
|
|
14
|
+
|
|
15
|
+
export function resolveOverridableSettings(
|
|
16
|
+
element: V1ElementData,
|
|
17
|
+
overrideMap: Map< string, ComponentInstanceOverrideProp >
|
|
18
|
+
): V1ElementSettingsProps {
|
|
19
|
+
if ( isComponentInstance( { widgetType: element.widgetType, elType: element.elType } ) ) {
|
|
20
|
+
return resolveOverridableSettingsForComponentInstance( element, overrideMap );
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return resolveOverridableSettingsForElement( element, overrideMap );
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function resolveOverridableSettingsForElement(
|
|
27
|
+
element: V1ElementData,
|
|
28
|
+
overrideMap: Map< string, ComponentInstanceOverrideProp >
|
|
29
|
+
): V1ElementSettingsProps {
|
|
30
|
+
const updatedSettings = element.settings ? { ...element.settings } : {};
|
|
31
|
+
|
|
32
|
+
for ( const [ settingKey, settingValue ] of Object.entries( element.settings ?? {} ) ) {
|
|
33
|
+
updatedSettings[ settingKey ] = resolvePropValue( settingValue, overrideMap );
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return updatedSettings;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function resolveOverridableSettingsForComponentInstance(
|
|
40
|
+
element: V1ElementData,
|
|
41
|
+
overrideMap: Map< string, ComponentInstanceOverrideProp >
|
|
42
|
+
): V1ElementSettingsProps {
|
|
43
|
+
const componentInstance = element.settings?.component_instance as ComponentInstanceProp | undefined;
|
|
44
|
+
|
|
45
|
+
if ( ! componentInstancePropTypeUtil.isValid( componentInstance ) ) {
|
|
46
|
+
return element.settings ?? {};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const instanceOverrides = componentInstance.value.overrides?.value;
|
|
50
|
+
|
|
51
|
+
if ( ! instanceOverrides?.length ) {
|
|
52
|
+
return element.settings ?? {};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const updatedOverrides = instanceOverrides.map( ( item ) =>
|
|
56
|
+
resolvePropValue( item, overrideMap, { isOverridableOverride: true } )
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
...element.settings,
|
|
61
|
+
component_instance: {
|
|
62
|
+
...componentInstance,
|
|
63
|
+
value: {
|
|
64
|
+
...componentInstance.value,
|
|
65
|
+
overrides: {
|
|
66
|
+
...componentInstance.value.overrides,
|
|
67
|
+
value: updatedOverrides,
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function resolvePropValue(
|
|
75
|
+
propValue: PropValue,
|
|
76
|
+
overrideMap: Map< string, ComponentInstanceOverrideProp >,
|
|
77
|
+
options?: { isOverridableOverride?: boolean }
|
|
78
|
+
): PropValue | null {
|
|
79
|
+
const { isOverridableOverride = false } = options ?? {};
|
|
80
|
+
|
|
81
|
+
// if it's not an overridable, return the prop value as is
|
|
82
|
+
if ( ! componentOverridablePropTypeUtil.isValid( propValue ) ) {
|
|
83
|
+
return propValue;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const overridableKey = propValue.value.override_key;
|
|
87
|
+
const matchingOverride = overrideMap.get( overridableKey );
|
|
88
|
+
|
|
89
|
+
const originValue = componentOverridablePropTypeUtil.extract( propValue )?.origin_value;
|
|
90
|
+
|
|
91
|
+
// if no matching override, return the overridable's origin value
|
|
92
|
+
if ( ! matchingOverride ) {
|
|
93
|
+
return originValue;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if ( isOverridableOverride ) {
|
|
97
|
+
return resolveOverridableOverride( matchingOverride, originValue );
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// for regular props, when there's a matching override, return the matching override value
|
|
101
|
+
const matchingOverrideValue = componentInstanceOverridePropTypeUtil.extract( matchingOverride )
|
|
102
|
+
?.override_value as PropValue | null;
|
|
103
|
+
return matchingOverrideValue;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function resolveOverridableOverride(
|
|
107
|
+
matchingOverride: ComponentInstanceOverrideProp,
|
|
108
|
+
originValue: PropValue
|
|
109
|
+
): ComponentInstanceOverrideProp | null {
|
|
110
|
+
if ( ! originValue || ! componentInstanceOverridePropTypeUtil.isValid( originValue ) ) {
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// for overridable overrides, we should create a new override with the matching override value
|
|
115
|
+
// but keep the origin value's override key and schema source, so they'll match the inner component.
|
|
116
|
+
return componentInstanceOverridePropTypeUtil.create( {
|
|
117
|
+
override_value: matchingOverride.value.override_value,
|
|
118
|
+
override_key: originValue.value.override_key,
|
|
119
|
+
schema_source: originValue.value.schema_source,
|
|
120
|
+
} );
|
|
121
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type V1ElementModelProps } from '@elementor/editor-elements';
|
|
2
2
|
|
|
3
|
-
import { COMPONENT_WIDGET_TYPE } from '../
|
|
3
|
+
import { COMPONENT_WIDGET_TYPE } from '../consts';
|
|
4
4
|
|
|
5
5
|
export function isComponentInstance( elementModel: Partial< V1ElementModelProps > ) {
|
|
6
6
|
return [ elementModel.widgetType, elementModel.elType ].includes( COMPONENT_WIDGET_TYPE );
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { hasProInstalled, isProAtLeast } from '@elementor/utils';
|
|
2
|
+
|
|
3
|
+
const MIN_PRO_VERSION_FOR_COMPONENTS = '4.0';
|
|
4
|
+
|
|
5
|
+
export function isProComponentsSupported(): boolean {
|
|
6
|
+
return hasProInstalled() && isProAtLeast( MIN_PRO_VERSION_FOR_COMPONENTS );
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function isProOutdatedForComponents(): boolean {
|
|
10
|
+
return hasProInstalled() && ! isProAtLeast( MIN_PRO_VERSION_FOR_COMPONENTS );
|
|
11
|
+
}
|
package/src/utils/tracking.ts
CHANGED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import * as React from 'react';
|
|
2
|
-
import { PopoverContent } from '@elementor/editor-controls';
|
|
3
|
-
import { PopoverHeader } from '@elementor/editor-ui';
|
|
4
|
-
import { Box, Button, Image, Link, Popover, Stack, Typography } from '@elementor/ui';
|
|
5
|
-
import { __ } from '@wordpress/i18n';
|
|
6
|
-
|
|
7
|
-
export const ComponentIntroduction = ( {
|
|
8
|
-
anchorRef,
|
|
9
|
-
shouldShowIntroduction,
|
|
10
|
-
onClose,
|
|
11
|
-
}: {
|
|
12
|
-
anchorRef: React.RefObject< HTMLElement >;
|
|
13
|
-
shouldShowIntroduction: boolean;
|
|
14
|
-
onClose: () => void;
|
|
15
|
-
} ) => {
|
|
16
|
-
if ( ! anchorRef.current || ! shouldShowIntroduction ) {
|
|
17
|
-
return null;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
return (
|
|
21
|
-
<Popover
|
|
22
|
-
anchorEl={ anchorRef.current }
|
|
23
|
-
open={ shouldShowIntroduction }
|
|
24
|
-
anchorOrigin={ {
|
|
25
|
-
vertical: 'top',
|
|
26
|
-
horizontal: 'right',
|
|
27
|
-
} }
|
|
28
|
-
transformOrigin={ {
|
|
29
|
-
vertical: 'top',
|
|
30
|
-
horizontal: -30,
|
|
31
|
-
} }
|
|
32
|
-
onClose={ onClose }
|
|
33
|
-
>
|
|
34
|
-
<Box sx={ { width: '296px' } }>
|
|
35
|
-
<PopoverHeader title={ __( 'Add your first property', 'elementor' ) } onClose={ onClose } />
|
|
36
|
-
<Image
|
|
37
|
-
sx={ { width: '296px', height: '160px' } }
|
|
38
|
-
src={ 'https://assets.elementor.com/packages/v1/images/components-properties-intro.png' }
|
|
39
|
-
alt={ '' }
|
|
40
|
-
/>
|
|
41
|
-
<PopoverContent>
|
|
42
|
-
<Stack sx={ { p: 2 } }>
|
|
43
|
-
<Typography variant={ 'body2' }>
|
|
44
|
-
{ __( 'Properties make instances flexible.', 'elementor' ) }
|
|
45
|
-
</Typography>
|
|
46
|
-
<Typography variant={ 'body2' }>
|
|
47
|
-
{ __(
|
|
48
|
-
'Select any Element, then in the General tab, click next to any setting you want users to customize - like text, images, or links.',
|
|
49
|
-
'elementor'
|
|
50
|
-
) }
|
|
51
|
-
</Typography>
|
|
52
|
-
<Typography variant={ 'body2' } sx={ { mt: 2 } }>
|
|
53
|
-
{ __(
|
|
54
|
-
'Your properties will appear in the Properties panel, where you can organize and manage them anytime.',
|
|
55
|
-
'elementor'
|
|
56
|
-
) }
|
|
57
|
-
</Typography>
|
|
58
|
-
<Link
|
|
59
|
-
href="http://go.elementor.com/components-guide"
|
|
60
|
-
target="_blank"
|
|
61
|
-
sx={ { mt: 2 } }
|
|
62
|
-
color="info.main"
|
|
63
|
-
variant="body2"
|
|
64
|
-
>
|
|
65
|
-
{ __( 'Learn more', 'elementor' ) }
|
|
66
|
-
</Link>
|
|
67
|
-
<Stack direction="row" alignItems="center" justifyContent="flex-end" sx={ { pt: 1 } }>
|
|
68
|
-
<Button size="medium" variant="contained" onClick={ onClose }>
|
|
69
|
-
{ __( 'Got it', 'elementor' ) }
|
|
70
|
-
</Button>
|
|
71
|
-
</Stack>
|
|
72
|
-
</Stack>
|
|
73
|
-
</PopoverContent>
|
|
74
|
-
</Box>
|
|
75
|
-
</Popover>
|
|
76
|
-
);
|
|
77
|
-
};
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import * as React from 'react';
|
|
2
|
-
import { useEffect, useRef } from 'react';
|
|
3
|
-
import { ComponentPropListIcon } from '@elementor/icons';
|
|
4
|
-
import { Badge, Box, keyframes, styled, ToggleButton, Tooltip } from '@elementor/ui';
|
|
5
|
-
import { __ } from '@wordpress/i18n';
|
|
6
|
-
|
|
7
|
-
export const ComponentsBadge = React.forwardRef<
|
|
8
|
-
HTMLDivElement,
|
|
9
|
-
{ overridablePropsCount: number; onClick: () => void }
|
|
10
|
-
>( ( { overridablePropsCount, onClick }, ref ) => {
|
|
11
|
-
const prevCount = usePrevious( overridablePropsCount );
|
|
12
|
-
|
|
13
|
-
const isFirstExposedProperty = prevCount === 0 && overridablePropsCount === 1;
|
|
14
|
-
|
|
15
|
-
return (
|
|
16
|
-
<StyledBadge
|
|
17
|
-
ref={ ref }
|
|
18
|
-
color="primary"
|
|
19
|
-
key={ overridablePropsCount }
|
|
20
|
-
invisible={ overridablePropsCount === 0 }
|
|
21
|
-
animate={ isFirstExposedProperty }
|
|
22
|
-
anchorOrigin={ { vertical: 'top', horizontal: 'right' } }
|
|
23
|
-
badgeContent={
|
|
24
|
-
<Box sx={ { animation: ! isFirstExposedProperty ? `${ slideUp } 300ms ease-out` : 'none' } }>
|
|
25
|
-
{ overridablePropsCount }
|
|
26
|
-
</Box>
|
|
27
|
-
}
|
|
28
|
-
>
|
|
29
|
-
<Tooltip title={ __( 'Component properties', 'elementor' ) }>
|
|
30
|
-
<ToggleButton
|
|
31
|
-
value="exposed properties"
|
|
32
|
-
size="tiny"
|
|
33
|
-
onClick={ onClick }
|
|
34
|
-
aria-label={ __( 'Component properties', 'elementor' ) }
|
|
35
|
-
>
|
|
36
|
-
<ComponentPropListIcon fontSize="tiny" />
|
|
37
|
-
</ToggleButton>
|
|
38
|
-
</Tooltip>
|
|
39
|
-
</StyledBadge>
|
|
40
|
-
);
|
|
41
|
-
} );
|
|
42
|
-
|
|
43
|
-
const StyledBadge = styled( Badge, { shouldForwardProp: ( prop ) => prop !== 'animate' } )(
|
|
44
|
-
( { theme, animate } ) => ( {
|
|
45
|
-
'& .MuiBadge-badge': {
|
|
46
|
-
minWidth: theme.spacing( 2 ),
|
|
47
|
-
height: theme.spacing( 2 ),
|
|
48
|
-
minHeight: theme.spacing( 2 ),
|
|
49
|
-
maxWidth: theme.spacing( 2 ),
|
|
50
|
-
fontSize: theme.typography.caption.fontSize as string,
|
|
51
|
-
animation: animate ? `${ bounceIn } 300ms ease-out` : 'none',
|
|
52
|
-
},
|
|
53
|
-
} )
|
|
54
|
-
);
|
|
55
|
-
|
|
56
|
-
function usePrevious< T >( value: T ) {
|
|
57
|
-
const ref = useRef< T >( value );
|
|
58
|
-
useEffect( () => {
|
|
59
|
-
ref.current = value;
|
|
60
|
-
}, [ value ] );
|
|
61
|
-
return ref.current;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
const bounceIn = keyframes`
|
|
65
|
-
0% { transform: scale(0) translate(50%, 50%); opacity: 0; }
|
|
66
|
-
70% { transform: scale(1.1) translate(50%, -50%); opacity: 1; }
|
|
67
|
-
100% { transform: scale(1) translate(50%, -50%); opacity: 1; }
|
|
68
|
-
`;
|
|
69
|
-
|
|
70
|
-
const slideUp = keyframes`
|
|
71
|
-
from { transform: translateY(100%); opacity: 0; }
|
|
72
|
-
to { transform: translateY(0); opacity: 1; }
|
|
73
|
-
`;
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import * as React from 'react';
|
|
2
|
-
import { useSuppressedMessage } from '@elementor/editor-current-user';
|
|
3
|
-
import { getV1DocumentsManager } from '@elementor/editor-documents';
|
|
4
|
-
import { PanelHeader } from '@elementor/editor-panels';
|
|
5
|
-
import { EllipsisWithTooltip } from '@elementor/editor-ui';
|
|
6
|
-
import { ArrowLeftIcon, ComponentsFilledIcon } from '@elementor/icons';
|
|
7
|
-
import { __getState as getState } from '@elementor/store';
|
|
8
|
-
import { Box, Divider, IconButton, Tooltip, Typography } from '@elementor/ui';
|
|
9
|
-
import { __ } from '@wordpress/i18n';
|
|
10
|
-
|
|
11
|
-
import { useSanitizeOverridableProps } from '../../../hooks/use-sanitize-overridable-props';
|
|
12
|
-
import { type ComponentsSlice, SLICE_NAME, useCurrentComponent } from '../../../store/store';
|
|
13
|
-
import { trackComponentEvent } from '../../../utils/tracking';
|
|
14
|
-
import { useNavigateBack } from '../../hooks/use-navigate-back';
|
|
15
|
-
import { ComponentIntroduction } from '../component-introduction';
|
|
16
|
-
import { usePanelActions } from '../component-properties-panel/component-properties-panel';
|
|
17
|
-
import { ComponentsBadge } from './component-badge';
|
|
18
|
-
|
|
19
|
-
const MESSAGE_KEY = 'components-properties-introduction';
|
|
20
|
-
|
|
21
|
-
export const ComponentPanelHeader = () => {
|
|
22
|
-
const { id: currentComponentId, uid: componentUid } = useCurrentComponent() ?? { id: null, uid: null };
|
|
23
|
-
const overridableProps = useSanitizeOverridableProps( currentComponentId );
|
|
24
|
-
const onBack = useNavigateBack();
|
|
25
|
-
const componentName = getComponentName();
|
|
26
|
-
const [ isMessageSuppressed, suppressMessage ] = useSuppressedMessage( MESSAGE_KEY );
|
|
27
|
-
const [ shouldShowIntroduction, setShouldShowIntroduction ] = React.useState( ! isMessageSuppressed );
|
|
28
|
-
|
|
29
|
-
const { open: openPropertiesPanel } = usePanelActions();
|
|
30
|
-
|
|
31
|
-
const overridablePropsCount = overridableProps ? Object.keys( overridableProps.props ).length : 0;
|
|
32
|
-
const anchorRef = React.useRef< HTMLDivElement >( null );
|
|
33
|
-
|
|
34
|
-
if ( ! currentComponentId ) {
|
|
35
|
-
return null;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const handleCloseIntroduction = () => {
|
|
39
|
-
suppressMessage();
|
|
40
|
-
setShouldShowIntroduction( false );
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
const handleOpenPropertiesPanel = () => {
|
|
44
|
-
openPropertiesPanel();
|
|
45
|
-
|
|
46
|
-
trackComponentEvent( {
|
|
47
|
-
action: 'propertiesPanelOpened',
|
|
48
|
-
source: 'user',
|
|
49
|
-
component_uid: componentUid,
|
|
50
|
-
properties_count: overridablePropsCount,
|
|
51
|
-
} );
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
return (
|
|
55
|
-
<Box data-testid="component-panel-header">
|
|
56
|
-
<PanelHeader sx={ { justifyContent: 'start', px: 2 } }>
|
|
57
|
-
<Tooltip title={ __( 'Back', 'elementor' ) }>
|
|
58
|
-
<IconButton size="tiny" onClick={ onBack } aria-label={ __( 'Back', 'elementor' ) }>
|
|
59
|
-
<ArrowLeftIcon fontSize="tiny" />
|
|
60
|
-
</IconButton>
|
|
61
|
-
</Tooltip>
|
|
62
|
-
<ComponentsFilledIcon fontSize="tiny" stroke="currentColor" />
|
|
63
|
-
<EllipsisWithTooltip
|
|
64
|
-
title={ componentName }
|
|
65
|
-
as={ Typography }
|
|
66
|
-
variant="caption"
|
|
67
|
-
sx={ { fontWeight: 500, flexGrow: 1 } }
|
|
68
|
-
/>
|
|
69
|
-
<ComponentsBadge
|
|
70
|
-
overridablePropsCount={ overridablePropsCount }
|
|
71
|
-
ref={ anchorRef }
|
|
72
|
-
onClick={ handleOpenPropertiesPanel }
|
|
73
|
-
/>
|
|
74
|
-
</PanelHeader>
|
|
75
|
-
<Divider />
|
|
76
|
-
<ComponentIntroduction
|
|
77
|
-
anchorRef={ anchorRef }
|
|
78
|
-
shouldShowIntroduction={ shouldShowIntroduction }
|
|
79
|
-
onClose={ handleCloseIntroduction }
|
|
80
|
-
/>
|
|
81
|
-
</Box>
|
|
82
|
-
);
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
function getComponentName(): string {
|
|
86
|
-
const state = getState() as ComponentsSlice;
|
|
87
|
-
const path = state[ SLICE_NAME ].path;
|
|
88
|
-
const { instanceTitle } = path.at( -1 ) ?? {};
|
|
89
|
-
|
|
90
|
-
if ( instanceTitle ) {
|
|
91
|
-
return instanceTitle;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
const documentsManager = getV1DocumentsManager();
|
|
95
|
-
const currentDocument = documentsManager.getCurrent();
|
|
96
|
-
|
|
97
|
-
return currentDocument?.container?.settings?.get( 'post_title' ) ?? '';
|
|
98
|
-
}
|