@elementor/editor-components 3.33.0-299 → 3.33.0-301

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.33.0-299",
4
+ "version": "3.33.0-301",
5
5
  "private": false,
6
6
  "author": "Elementor Team",
7
7
  "homepage": "https://elementor.com/",
@@ -40,23 +40,25 @@
40
40
  "dev": "tsup --config=../../tsup.dev.ts"
41
41
  },
42
42
  "dependencies": {
43
- "@elementor/editor": "3.33.0-299",
44
- "@elementor/editor-canvas": "3.33.0-299",
45
- "@elementor/editor-documents": "3.33.0-299",
46
- "@elementor/editor-elements": "3.33.0-299",
47
- "@elementor/editor-elements-panel": "3.33.0-299",
48
- "@elementor/editor-props": "3.33.0-299",
49
- "@elementor/editor-styles-repository": "3.33.0-299",
50
- "@elementor/editor-ui": "3.33.0-299",
51
- "@elementor/editor-v1-adapters": "3.33.0-299",
52
- "@elementor/http-client": "3.33.0-299",
43
+ "@elementor/editor": "3.33.0-301",
44
+ "@elementor/editor-canvas": "3.33.0-301",
45
+ "@elementor/editor-controls": "3.33.0-301",
46
+ "@elementor/editor-documents": "3.33.0-301",
47
+ "@elementor/editor-editing-panel": "3.33.0-301",
48
+ "@elementor/editor-elements": "3.33.0-301",
49
+ "@elementor/editor-elements-panel": "3.33.0-301",
50
+ "@elementor/editor-props": "3.33.0-301",
51
+ "@elementor/editor-styles-repository": "3.33.0-301",
52
+ "@elementor/editor-ui": "3.33.0-301",
53
+ "@elementor/editor-v1-adapters": "3.33.0-301",
54
+ "@elementor/http-client": "3.33.0-301",
53
55
  "@elementor/icons": "^1.61.0",
54
- "@elementor/mixpanel": "3.33.0-299",
55
- "@elementor/query": "3.33.0-299",
56
- "@elementor/schema": "3.33.0-299",
57
- "@elementor/store": "3.33.0-299",
56
+ "@elementor/mixpanel": "3.33.0-301",
57
+ "@elementor/query": "3.33.0-301",
58
+ "@elementor/schema": "3.33.0-301",
59
+ "@elementor/store": "3.33.0-301",
58
60
  "@elementor/ui": "1.36.17",
59
- "@elementor/utils": "3.33.0-299",
61
+ "@elementor/utils": "3.33.0-301",
60
62
  "@wordpress/i18n": "^5.13.0"
61
63
  },
62
64
  "peerDependencies": {
@@ -0,0 +1,81 @@
1
+ import * as React from 'react';
2
+ import { forwardRef } from 'react';
3
+ import { CheckIcon, PlusIcon } from '@elementor/icons';
4
+ import { type bindTrigger, Box, styled } from '@elementor/ui';
5
+ import { __ } from '@wordpress/i18n';
6
+
7
+ const SIZE = 'tiny';
8
+
9
+ const IconContainer = styled( Box )`
10
+ pointer-events: none;
11
+ opacity: 0;
12
+ transition: opacity 0.2s ease-in-out;
13
+
14
+ & > svg {
15
+ position: absolute;
16
+ top: 50%;
17
+ left: 50%;
18
+ transform: translate( -50%, -50% );
19
+ width: 10px;
20
+ height: 10px;
21
+ fill: ${ ( { theme } ) => theme.palette.primary.contrastText };
22
+ stroke: ${ ( { theme } ) => theme.palette.primary.contrastText };
23
+ stroke-width: 2px;
24
+ }
25
+ `;
26
+
27
+ const Content = styled( Box )`
28
+ position: relative;
29
+ display: flex;
30
+ align-items: center;
31
+ justify-content: center;
32
+ cursor: pointer;
33
+ width: 16px;
34
+ height: 16px;
35
+ margin-inline: ${ ( { theme } ) => theme.spacing( 0.5 ) };
36
+
37
+ &:before {
38
+ content: '';
39
+ display: block;
40
+ position: absolute;
41
+ top: 50%;
42
+ left: 50%;
43
+ transform: translate( -50%, -50% ) rotate( 45deg );
44
+ width: 5px;
45
+ height: 5px;
46
+ border-radius: 1px;
47
+ background-color: ${ ( { theme } ) => theme.palette.primary.main };
48
+ transition: all 0.1s ease-in-out;
49
+ }
50
+
51
+ &:hover,
52
+ &.enlarged {
53
+ &:before {
54
+ width: 12px;
55
+ height: 12px;
56
+ border-radius: 2px;
57
+ }
58
+
59
+ .icon {
60
+ opacity: 1;
61
+ }
62
+ }
63
+ `;
64
+
65
+ type Props = {
66
+ isOverridable: boolean;
67
+ triggerProps: ReturnType< typeof bindTrigger >;
68
+ isOpen: boolean;
69
+ };
70
+ export const Indicator = forwardRef< HTMLDivElement, Props >( ( { triggerProps, isOpen, isOverridable }, ref ) => (
71
+ <Content ref={ ref } { ...triggerProps } className={ isOpen || isOverridable ? 'enlarged' : '' }>
72
+ <IconContainer
73
+ className="icon"
74
+ aria-label={
75
+ isOverridable ? __( 'Overridable property', 'elementor' ) : __( 'Make prop overridable', 'elementor' )
76
+ }
77
+ >
78
+ { isOverridable ? <CheckIcon fontSize={ SIZE } /> : <PlusIcon fontSize={ SIZE } /> }
79
+ </IconContainer>
80
+ </Content>
81
+ ) );
@@ -0,0 +1,95 @@
1
+ import * as React from 'react';
2
+ import { useBoundProp } from '@elementor/editor-controls';
3
+ import { getV1CurrentDocument } from '@elementor/editor-documents';
4
+ import { useElement } from '@elementor/editor-editing-panel';
5
+ import { __getState as getState } from '@elementor/store';
6
+ import { bindPopover, bindTrigger, Popover, Tooltip, usePopupState } from '@elementor/ui';
7
+ import { __ } from '@wordpress/i18n';
8
+
9
+ import { componentOverridablePropTypeUtil } from '../../prop-types/component-overridable-prop-type';
10
+ import { selectOverridableProps } from '../../store/store';
11
+ import { type OverridableProps } from '../../types';
12
+ import { COMPONENT_DOCUMENT_TYPE } from '../consts';
13
+ import { Indicator } from './indicator';
14
+
15
+ const FORBIDDEN_KEYS = [ '_cssid', 'attributes' ];
16
+
17
+ export function OverridablePropIndicator() {
18
+ const { bind, value } = useBoundProp();
19
+ const currentDocument = getV1CurrentDocument();
20
+
21
+ if ( currentDocument.config.type !== COMPONENT_DOCUMENT_TYPE || ! currentDocument.id ) {
22
+ return null;
23
+ }
24
+
25
+ if ( ! isPropAllowed( bind ) ) {
26
+ return null;
27
+ }
28
+
29
+ const overridableProps = selectOverridableProps( getState(), currentDocument.id );
30
+ const isOverridable = componentOverridablePropTypeUtil.isValid( value );
31
+
32
+ return (
33
+ <Content
34
+ componentId={ currentDocument.id }
35
+ isOverridable={ isOverridable }
36
+ overridableProps={ overridableProps }
37
+ />
38
+ );
39
+ }
40
+
41
+ type Props = {
42
+ componentId: number;
43
+ isOverridable: boolean;
44
+ overridableProps?: OverridableProps;
45
+ };
46
+ export function Content( { isOverridable, overridableProps }: Props ) {
47
+ const {
48
+ element: { id: elementId },
49
+ } = useElement();
50
+ const { bind } = useBoundProp();
51
+
52
+ const popupState = usePopupState( {
53
+ variant: 'popover',
54
+ } );
55
+
56
+ const triggerProps = bindTrigger( popupState );
57
+ const popoverProps = bindPopover( popupState );
58
+
59
+ const overridableConfig = Object.values( overridableProps?.props ?? {} ).find(
60
+ ( prop ) => prop.elementId === elementId && prop.propKey === bind
61
+ );
62
+
63
+ return (
64
+ <>
65
+ <Tooltip placement="top" title={ __( 'Override Property', 'elementor' ) }>
66
+ <Indicator
67
+ triggerProps={ triggerProps }
68
+ isOpen={ !! popoverProps.open }
69
+ isOverridable={ isOverridable }
70
+ />
71
+ </Tooltip>
72
+ <Popover
73
+ disableScrollLock
74
+ anchorOrigin={ {
75
+ vertical: 'bottom',
76
+ horizontal: 'right',
77
+ } }
78
+ transformOrigin={ {
79
+ vertical: 'top',
80
+ horizontal: 'right',
81
+ } }
82
+ PaperProps={ {
83
+ sx: { my: 2.5 },
84
+ } }
85
+ { ...popoverProps }
86
+ >
87
+ { JSON.stringify( overridableConfig ) /** TODO: replace with actual form */ }
88
+ </Popover>
89
+ </>
90
+ );
91
+ }
92
+
93
+ function isPropAllowed( bind: string ) {
94
+ return ! FORBIDDEN_KEYS.includes( bind );
95
+ }
package/src/init.ts CHANGED
@@ -5,6 +5,7 @@ import {
5
5
  settingsTransformersRegistry,
6
6
  } from '@elementor/editor-canvas';
7
7
  import { getV1CurrentDocument } from '@elementor/editor-documents';
8
+ import { FIELD_TYPE, registerFieldIndicator } from '@elementor/editor-editing-panel';
8
9
  import { type V1ElementData } from '@elementor/editor-elements';
9
10
  import { injectTab } from '@elementor/editor-elements-panel';
10
11
  import { stylesRepository } from '@elementor/editor-styles-repository';
@@ -18,6 +19,7 @@ import { Components } from './components/components-tab/components';
18
19
  import { CreateComponentForm } from './components/create-component-form/create-component-form';
19
20
  import { EditComponent } from './components/edit-component/edit-component';
20
21
  import { openEditModeDialog } from './components/in-edit-mode';
22
+ import { OverridablePropIndicator } from './components/overridable-props/overridable-prop-indicator';
21
23
  import { createComponentType, TYPE } from './create-component-type';
22
24
  import { PopulateStore } from './populate-store';
23
25
  import { componentsStylesProvider } from './store/components-styles-provider';
@@ -82,6 +84,13 @@ export function init() {
82
84
  loadComponentsStyles( ( config?.elements as V1ElementData[] ) ?? [] );
83
85
  } );
84
86
 
87
+ registerFieldIndicator( {
88
+ fieldType: FIELD_TYPE.SETTINGS,
89
+ id: 'component-overridable-prop',
90
+ priority: 1,
91
+ indicator: OverridablePropIndicator,
92
+ } );
93
+
85
94
  settingsTransformersRegistry.register( 'component-instance', componentInstanceTransformer );
86
95
  settingsTransformersRegistry.register( 'overridable', componentOverridableTransformer );
87
96
  }
@@ -0,0 +1,15 @@
1
+ import { createPropUtils } from '@elementor/editor-props';
2
+ import { z } from '@elementor/schema';
3
+
4
+ export const componentOverridablePropTypeUtil = createPropUtils(
5
+ 'overridable',
6
+ z.object( {
7
+ override_key: z.string(),
8
+ default_value: z
9
+ .object( {
10
+ $$type: z.string(),
11
+ value: z.unknown(),
12
+ } )
13
+ .nullable(),
14
+ } )
15
+ );
@@ -88,6 +88,8 @@ const selectLoadStatus = ( state: ComponentsSlice ) => state[ SLICE_NAME ].loadS
88
88
  const selectStylesDefinitions = ( state: ComponentsSlice ) => state[ SLICE_NAME ].styles ?? {};
89
89
  const selectUnpublishedData = ( state: ComponentsSlice ) => state[ SLICE_NAME ].unpublishedData;
90
90
  const getCreatedThisSession = ( state: ComponentsSlice ) => state[ SLICE_NAME ].createdThisSession;
91
+ const selectComponent = ( state: ComponentsSlice, componentId: ComponentId ) =>
92
+ state[ SLICE_NAME ].data.find( ( component ) => component.id === componentId );
91
93
 
92
94
  export const selectComponents = createSelector(
93
95
  selectData,
@@ -109,3 +111,7 @@ export const selectCreatedThisSession = createSelector(
109
111
  getCreatedThisSession,
110
112
  ( createdThisSession ) => createdThisSession
111
113
  );
114
+ export const selectOverridableProps = createSelector(
115
+ selectComponent,
116
+ ( component: PublishedComponent | undefined ) => component?.overridableProps
117
+ );
package/src/types.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { type V1ElementData } from '@elementor/editor-elements';
2
- import { type TransformablePropValue } from '@elementor/editor-props';
2
+ import { type PropValue, type TransformablePropValue } from '@elementor/editor-props';
3
3
  import type { StyleDefinition } from '@elementor/editor-styles';
4
4
 
5
5
  export type ComponentFormValues = {
@@ -20,9 +20,35 @@ export type UnpublishedComponent = BaseComponent & {
20
20
  elements: V1ElementData[];
21
21
  };
22
22
 
23
+ export type OverridableProp = {
24
+ overrideKey: string;
25
+ label: string;
26
+ elementId: string;
27
+ propKey: string;
28
+ elType: string;
29
+ widgetType: string;
30
+ defaultValue: PropValue;
31
+ groupId: string;
32
+ };
33
+
34
+ export type OverridablePropGroup = {
35
+ id: string;
36
+ label: string;
37
+ props: string[];
38
+ };
39
+
40
+ export type OverridableProps = {
41
+ props: Record< string, OverridableProp >;
42
+ groups: {
43
+ items: Record< string, OverridablePropGroup >;
44
+ order: string[];
45
+ };
46
+ };
47
+
23
48
  type BaseComponent = {
24
49
  uid: string;
25
50
  name: string;
51
+ overridableProps?: OverridableProps;
26
52
  };
27
53
 
28
54
  export type DocumentStatus = 'publish' | 'draft';