@elementor/editor-editing-panel 1.40.0 → 1.41.0
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/CHANGELOG.md +32 -0
- package/dist/index.d.mts +9 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +1177 -732
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1070 -614
- package/dist/index.mjs.map +1 -1
- package/package.json +7 -6
- package/src/action.tsx +26 -0
- package/src/components/add-or-remove-content.tsx +11 -3
- package/src/components/css-classes/css-class-item.tsx +3 -2
- package/src/components/css-classes/css-class-menu.tsx +15 -5
- package/src/components/css-classes/css-class-selector.tsx +1 -0
- package/src/components/css-classes/use-apply-and-unapply-class.ts +8 -4
- package/src/components/section-content.tsx +16 -6
- package/src/components/style-sections/background-section/background-section.tsx +6 -3
- package/src/components/style-sections/border-section/border-field.tsx +3 -0
- package/src/components/style-sections/layout-section/display-field.tsx +2 -1
- package/src/components/style-sections/layout-section/flex-order-field.tsx +5 -2
- package/src/components/style-sections/layout-section/flex-size-field.tsx +16 -12
- package/src/components/style-sections/typography-section/text-stroke-field.tsx +3 -0
- package/src/components/style-tab.tsx +1 -1
- package/src/contexts/style-context.tsx +11 -2
- package/src/contexts/styles-inheritance-context.tsx +9 -7
- package/src/controls-actions.ts +2 -0
- package/src/controls-registry/styles-field.tsx +3 -0
- package/src/init.ts +11 -1
- package/src/reset-style-props.tsx +31 -0
- package/src/styles-inheritance/components/action-icons.tsx +8 -0
- package/src/styles-inheritance/components/breakpoint-icon.tsx +47 -0
- package/src/styles-inheritance/components/index.ts +4 -0
- package/src/styles-inheritance/components/label-chip.tsx +43 -0
- package/src/styles-inheritance/components/value-component.tsx +25 -0
- package/src/styles-inheritance/consts.ts +17 -0
- package/src/styles-inheritance/create-styles-inheritance.ts +50 -12
- package/src/{hooks → styles-inheritance/hooks}/use-normalized-inheritance-chain-items.tsx +41 -11
- package/src/styles-inheritance/init-styles-inheritance-transformers.ts +38 -0
- package/src/styles-inheritance/init.ts +8 -0
- package/src/styles-inheritance/styles-inheritance-indicator.tsx +35 -32
- package/src/styles-inheritance/styles-inheritance-infotip.tsx +113 -19
- package/src/styles-inheritance/styles-inheritance-transformers-registry.tsx +3 -0
- package/src/styles-inheritance/transformers/background-color-overlay-transformer.tsx +27 -0
- package/src/styles-inheritance/transformers/background-gradient-overlay-transformer.tsx +50 -0
- package/src/styles-inheritance/transformers/background-image-overlay-transformer.tsx +79 -0
- package/src/styles-inheritance/transformers/background-overlay-transformer.tsx +20 -0
- package/src/styles-inheritance/types.ts +6 -2
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { type BreakpointId, useBreakpoints } from '@elementor/editor-responsive';
|
|
3
|
+
import {
|
|
4
|
+
DesktopIcon,
|
|
5
|
+
LaptopIcon,
|
|
6
|
+
MobileLandscapeIcon,
|
|
7
|
+
MobilePortraitIcon,
|
|
8
|
+
TabletLandscapeIcon,
|
|
9
|
+
TabletPortraitIcon,
|
|
10
|
+
WidescreenIcon,
|
|
11
|
+
} from '@elementor/icons';
|
|
12
|
+
import { Tooltip } from '@elementor/ui';
|
|
13
|
+
|
|
14
|
+
type Props = {
|
|
15
|
+
breakpoint?: BreakpointId | null;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const SIZE = 'tiny';
|
|
19
|
+
const DEFAULT_BREAKPOINT = 'desktop';
|
|
20
|
+
|
|
21
|
+
const breakpointIconMap: Record< string, React.ElementType > = {
|
|
22
|
+
widescreen: WidescreenIcon,
|
|
23
|
+
desktop: DesktopIcon,
|
|
24
|
+
laptop: LaptopIcon,
|
|
25
|
+
tablet_extra: TabletLandscapeIcon,
|
|
26
|
+
tablet: TabletPortraitIcon,
|
|
27
|
+
mobile_extra: MobileLandscapeIcon,
|
|
28
|
+
mobile: MobilePortraitIcon,
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export const BreakpointIcon = ( { breakpoint }: Props ) => {
|
|
32
|
+
const breakpoints = useBreakpoints();
|
|
33
|
+
const currentBreakpoint = breakpoint || DEFAULT_BREAKPOINT;
|
|
34
|
+
const IconComponent = breakpointIconMap[ currentBreakpoint ];
|
|
35
|
+
|
|
36
|
+
if ( ! IconComponent ) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const breakpointLabel = breakpoints.find( ( breakpointItem ) => breakpointItem.id === currentBreakpoint )?.label;
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<Tooltip title={ breakpointLabel } placement="top">
|
|
44
|
+
<IconComponent fontSize={ SIZE } sx={ { mt: '2px' } } />
|
|
45
|
+
</Tooltip>
|
|
46
|
+
);
|
|
47
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { ELEMENTS_BASE_STYLES_PROVIDER_KEY } from '@elementor/editor-styles-repository';
|
|
3
|
+
import { InfoCircleIcon } from '@elementor/icons';
|
|
4
|
+
import { Chip, type Theme, Tooltip } from '@elementor/ui';
|
|
5
|
+
import { __ } from '@wordpress/i18n';
|
|
6
|
+
|
|
7
|
+
type Props = {
|
|
8
|
+
displayLabel: string;
|
|
9
|
+
provider?: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const SIZE = 'tiny';
|
|
13
|
+
|
|
14
|
+
export const LabelChip = ( { displayLabel, provider }: Props ) => {
|
|
15
|
+
return (
|
|
16
|
+
<Chip
|
|
17
|
+
label={ displayLabel }
|
|
18
|
+
size={ SIZE }
|
|
19
|
+
color="global"
|
|
20
|
+
variant="standard"
|
|
21
|
+
state="enabled"
|
|
22
|
+
icon={
|
|
23
|
+
provider === ELEMENTS_BASE_STYLES_PROVIDER_KEY ? (
|
|
24
|
+
<Tooltip title={ __( 'Inherited from base styles', 'elementor' ) } placement="top">
|
|
25
|
+
<InfoCircleIcon fontSize={ SIZE } />
|
|
26
|
+
</Tooltip>
|
|
27
|
+
) : undefined
|
|
28
|
+
}
|
|
29
|
+
sx={ ( theme: Theme ) => ( {
|
|
30
|
+
lineHeight: 1,
|
|
31
|
+
flexWrap: 'nowrap',
|
|
32
|
+
alignItems: 'center',
|
|
33
|
+
borderRadius: `${ theme.shape.borderRadius * 0.75 }px`,
|
|
34
|
+
flexDirection: 'row-reverse',
|
|
35
|
+
'.MuiChip-label': {
|
|
36
|
+
overflow: 'hidden',
|
|
37
|
+
textOverflow: 'ellipsis',
|
|
38
|
+
whiteSpace: 'nowrap',
|
|
39
|
+
},
|
|
40
|
+
} ) }
|
|
41
|
+
/>
|
|
42
|
+
);
|
|
43
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { Typography } from '@elementor/ui';
|
|
3
|
+
|
|
4
|
+
type Props = {
|
|
5
|
+
index: number;
|
|
6
|
+
value: React.ReactNode;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export const ValueComponent = ( { index, value }: Props ) => {
|
|
10
|
+
return (
|
|
11
|
+
<Typography
|
|
12
|
+
variant="caption"
|
|
13
|
+
color="text.tertiary"
|
|
14
|
+
sx={ {
|
|
15
|
+
mt: '1px',
|
|
16
|
+
textDecoration: index === 0 ? 'none' : 'line-through',
|
|
17
|
+
overflow: 'hidden',
|
|
18
|
+
textOverflow: 'ellipsis',
|
|
19
|
+
whiteSpace: 'nowrap',
|
|
20
|
+
} }
|
|
21
|
+
>
|
|
22
|
+
{ value }
|
|
23
|
+
</Typography>
|
|
24
|
+
);
|
|
25
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { isExperimentActive } from '@elementor/editor-v1-adapters';
|
|
2
|
+
|
|
3
|
+
// the following prop types' style transformers would be ignored to provide alternative transformers for the styles inheritance popover
|
|
4
|
+
export const excludePropTypeTransformers = new Set( [
|
|
5
|
+
'background-color-overlay',
|
|
6
|
+
'background-image-overlay',
|
|
7
|
+
'background-gradient-overlay',
|
|
8
|
+
'gradient-color-stop',
|
|
9
|
+
'color-stop',
|
|
10
|
+
'background-image-position-offset',
|
|
11
|
+
'background-image-size-scale',
|
|
12
|
+
'image-src',
|
|
13
|
+
'image',
|
|
14
|
+
'background-overlay',
|
|
15
|
+
] );
|
|
16
|
+
|
|
17
|
+
export const isUsingIndicatorPopover = () => isExperimentActive( 'e_indications_popover' );
|
|
@@ -1,17 +1,21 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
isEmpty,
|
|
3
|
+
isTransformable,
|
|
4
|
+
type PropKey,
|
|
5
|
+
type PropType,
|
|
6
|
+
type PropValue,
|
|
7
|
+
type UnionPropType,
|
|
8
|
+
} from '@elementor/editor-props';
|
|
2
9
|
import { type BreakpointNode } from '@elementor/editor-responsive';
|
|
3
10
|
import { type StyleDefinition } from '@elementor/editor-styles';
|
|
4
11
|
|
|
5
12
|
import { getProviderByStyleId } from '../contexts/style-context';
|
|
6
13
|
import { createSnapshotsManager } from './create-snapshots-manager';
|
|
7
|
-
import {
|
|
8
|
-
type BreakpointsStatesStyles,
|
|
9
|
-
type StyleInheritanceMetaProps,
|
|
10
|
-
type StylesInheritanceAPI,
|
|
11
|
-
type StylesInheritanceSnapshot,
|
|
12
|
-
} from './types';
|
|
14
|
+
import { type BreakpointsStatesStyles, type StyleInheritanceMetaProps, type StylesInheritanceAPI } from './types';
|
|
13
15
|
import { getBreakpointKey, getStateKey } from './utils';
|
|
14
16
|
|
|
17
|
+
type ValidPropType = Exclude< PropType, UnionPropType >;
|
|
18
|
+
|
|
15
19
|
export function createStylesInheritance(
|
|
16
20
|
styleDefs: StyleDefinition[],
|
|
17
21
|
breakpointsRoot: BreakpointNode
|
|
@@ -23,16 +27,18 @@ export function createStylesInheritance(
|
|
|
23
27
|
|
|
24
28
|
return {
|
|
25
29
|
getSnapshot: createSnapshotsManager( getStyles, breakpointsRoot ),
|
|
26
|
-
getInheritanceChain: ( snapshot
|
|
30
|
+
getInheritanceChain: ( snapshot, path, topLevelPropType ) => {
|
|
27
31
|
const [ field, ...nextFields ] = path;
|
|
28
32
|
|
|
29
33
|
let inheritanceChain = snapshot[ field ] ?? [];
|
|
30
34
|
|
|
31
35
|
if ( nextFields.length > 0 ) {
|
|
36
|
+
const filterPropType = getFilterPropType( topLevelPropType, nextFields );
|
|
37
|
+
|
|
32
38
|
inheritanceChain = inheritanceChain
|
|
33
39
|
.map( ( { value: styleValue, ...rest } ) => ( {
|
|
34
40
|
...rest,
|
|
35
|
-
value: getValueByPath( styleValue, nextFields ),
|
|
41
|
+
value: getValueByPath( styleValue, nextFields, filterPropType ),
|
|
36
42
|
} ) )
|
|
37
43
|
.filter( ( { value: styleValue } ) => ! isEmpty( styleValue ) );
|
|
38
44
|
}
|
|
@@ -77,24 +83,56 @@ function buildStyleVariantsByMetaMapping( styleDefs: StyleDefinition[] ): Breakp
|
|
|
77
83
|
return breakpointStateSlots;
|
|
78
84
|
}
|
|
79
85
|
|
|
80
|
-
function getValueByPath( value: PropValue, path: PropKey[] ): PropValue {
|
|
86
|
+
function getValueByPath( value: PropValue, path: PropKey[], filterPropType: ValidPropType | null ): PropValue {
|
|
81
87
|
if ( ! value || typeof value !== 'object' ) {
|
|
82
88
|
return null;
|
|
83
89
|
}
|
|
84
90
|
|
|
91
|
+
if ( shouldUseOriginalValue( filterPropType, value ) ) {
|
|
92
|
+
return value;
|
|
93
|
+
}
|
|
94
|
+
|
|
85
95
|
return path.reduce( ( currentScope: PropValue, key: PropKey ): PropValue | null => {
|
|
86
96
|
if ( ! currentScope ) {
|
|
87
97
|
return null;
|
|
88
98
|
}
|
|
89
99
|
|
|
90
100
|
if ( isTransformable( currentScope ) ) {
|
|
91
|
-
return currentScope.value?.[ key ];
|
|
101
|
+
return currentScope.value?.[ key ] ?? null;
|
|
92
102
|
}
|
|
93
103
|
|
|
94
104
|
if ( typeof currentScope === 'object' ) {
|
|
95
|
-
return currentScope[ key as keyof typeof currentScope ];
|
|
105
|
+
return currentScope[ key as keyof typeof currentScope ] ?? null;
|
|
96
106
|
}
|
|
97
107
|
|
|
98
108
|
return null;
|
|
99
109
|
}, value );
|
|
100
110
|
}
|
|
111
|
+
|
|
112
|
+
function shouldUseOriginalValue( filterPropType: ValidPropType | null, value: PropValue ): boolean {
|
|
113
|
+
return !! filterPropType && isTransformable( value ) && filterPropType.key !== value.$$type;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const getFilterPropType = ( propType: PropType, path: string[] ): ValidPropType | null => {
|
|
117
|
+
if ( ! propType || propType.kind !== 'union' ) {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return (
|
|
122
|
+
Object.values( propType.prop_types ).find( ( type: PropType ) => {
|
|
123
|
+
return !! path.reduce( ( currentScope: PropType | null, key: string ) => {
|
|
124
|
+
if ( currentScope?.kind !== 'object' ) {
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const { shape } = currentScope;
|
|
129
|
+
|
|
130
|
+
if ( shape[ key ] ) {
|
|
131
|
+
return shape[ key ];
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return null;
|
|
135
|
+
}, type );
|
|
136
|
+
} ) ?? null
|
|
137
|
+
);
|
|
138
|
+
};
|
|
@@ -1,14 +1,17 @@
|
|
|
1
|
-
import { type ReactNode, useEffect, useState } from 'react';
|
|
1
|
+
import { isValidElement, type ReactNode, useEffect, useState } from 'react';
|
|
2
2
|
import { type PropsResolver } from '@elementor/editor-canvas';
|
|
3
3
|
import { type PropKey } from '@elementor/editor-props';
|
|
4
4
|
import { type StyleDefinitionVariant } from '@elementor/editor-styles';
|
|
5
|
+
import { ELEMENTS_BASE_STYLES_PROVIDER_KEY } from '@elementor/editor-styles-repository';
|
|
6
|
+
import { __ } from '@wordpress/i18n';
|
|
5
7
|
|
|
6
|
-
import { type SnapshotPropValue } from '../
|
|
8
|
+
import { type SnapshotPropValue } from '../types';
|
|
7
9
|
|
|
8
10
|
const MAXIMUM_ITEMS = 2;
|
|
9
11
|
|
|
10
12
|
type NormalizedItem = {
|
|
11
13
|
id: string | number;
|
|
14
|
+
provider: string;
|
|
12
15
|
breakpoint?: StyleDefinitionVariant[ 'meta' ][ 'breakpoint' ];
|
|
13
16
|
displayLabel: string;
|
|
14
17
|
value: ReactNode | string;
|
|
@@ -25,11 +28,20 @@ export const useNormalizedInheritanceChainItems = (
|
|
|
25
28
|
( async () => {
|
|
26
29
|
const normalizedItems = await Promise.all(
|
|
27
30
|
inheritanceChain
|
|
28
|
-
.filter( (
|
|
31
|
+
.filter( ( { style } ) => style )
|
|
29
32
|
.map( ( item, index ) => normalizeInheritanceItem( item, index, bind, resolve ) )
|
|
30
33
|
);
|
|
31
34
|
|
|
32
|
-
const validItems = normalizedItems
|
|
35
|
+
const validItems = normalizedItems
|
|
36
|
+
.map( ( item ) => ( {
|
|
37
|
+
...item,
|
|
38
|
+
displayLabel:
|
|
39
|
+
ELEMENTS_BASE_STYLES_PROVIDER_KEY !== item.provider
|
|
40
|
+
? item.displayLabel
|
|
41
|
+
: __( 'Base', 'elementor' ),
|
|
42
|
+
} ) )
|
|
43
|
+
.filter( ( item ) => ! item.value || item.displayLabel !== '' )
|
|
44
|
+
.slice( 0, MAXIMUM_ITEMS );
|
|
33
45
|
|
|
34
46
|
setItems( validItems );
|
|
35
47
|
} )();
|
|
@@ -38,19 +50,27 @@ export const useNormalizedInheritanceChainItems = (
|
|
|
38
50
|
return items;
|
|
39
51
|
};
|
|
40
52
|
|
|
53
|
+
const DEFAULT_BREAKPOINT = 'desktop';
|
|
54
|
+
|
|
41
55
|
export const normalizeInheritanceItem = async (
|
|
42
56
|
item: SnapshotPropValue,
|
|
43
57
|
index: number,
|
|
44
58
|
bind: PropKey,
|
|
45
59
|
resolve: PropsResolver
|
|
46
60
|
): Promise< NormalizedItem > => {
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
61
|
+
const {
|
|
62
|
+
variant: {
|
|
63
|
+
meta: { state, breakpoint },
|
|
64
|
+
},
|
|
65
|
+
style: { label, id },
|
|
66
|
+
} = item;
|
|
67
|
+
|
|
68
|
+
const displayLabel = `${ label }${ state ? ':' + state : '' }`;
|
|
50
69
|
|
|
51
70
|
return {
|
|
52
|
-
id:
|
|
53
|
-
|
|
71
|
+
id: id ? id + ( state ?? '' ) : index,
|
|
72
|
+
provider: item.provider || '',
|
|
73
|
+
breakpoint: breakpoint ?? DEFAULT_BREAKPOINT,
|
|
54
74
|
displayLabel,
|
|
55
75
|
value: await getTransformedValue( item, bind, resolve ),
|
|
56
76
|
};
|
|
@@ -60,7 +80,7 @@ const getTransformedValue = async (
|
|
|
60
80
|
item: SnapshotPropValue,
|
|
61
81
|
bind: PropKey,
|
|
62
82
|
resolve: PropsResolver
|
|
63
|
-
): Promise< string > => {
|
|
83
|
+
): Promise< ReactNode | string > => {
|
|
64
84
|
try {
|
|
65
85
|
const result = await resolve( {
|
|
66
86
|
props: {
|
|
@@ -68,7 +88,17 @@ const getTransformedValue = async (
|
|
|
68
88
|
},
|
|
69
89
|
} );
|
|
70
90
|
|
|
71
|
-
|
|
91
|
+
const value = result?.[ bind ] ?? result;
|
|
92
|
+
|
|
93
|
+
if ( isValidElement( value ) ) {
|
|
94
|
+
return value;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if ( typeof value === 'object' ) {
|
|
98
|
+
return JSON.stringify( value );
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return String( value );
|
|
72
102
|
} catch {
|
|
73
103
|
return '';
|
|
74
104
|
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { createTransformer, styleTransformersRegistry } from '@elementor/editor-canvas';
|
|
2
|
+
|
|
3
|
+
import { excludePropTypeTransformers } from './consts';
|
|
4
|
+
import { stylesInheritanceTransformersRegistry } from './styles-inheritance-transformers-registry';
|
|
5
|
+
import { backgroundColorOverlayTransformer } from './transformers/background-color-overlay-transformer';
|
|
6
|
+
import { backgroundGradientOverlayTransformer } from './transformers/background-gradient-overlay-transformer';
|
|
7
|
+
import { backgroundImageOverlayTransformer } from './transformers/background-image-overlay-transformer';
|
|
8
|
+
import { backgroundOverlayTransformer } from './transformers/background-overlay-transformer';
|
|
9
|
+
|
|
10
|
+
export function initStylesInheritanceTransformers() {
|
|
11
|
+
const originalStyleTransformers = styleTransformersRegistry.all();
|
|
12
|
+
|
|
13
|
+
Object.entries( originalStyleTransformers ).forEach( ( [ propType, transformer ] ) => {
|
|
14
|
+
if ( excludePropTypeTransformers.has( propType ) ) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
stylesInheritanceTransformersRegistry.register( propType, transformer );
|
|
19
|
+
} );
|
|
20
|
+
|
|
21
|
+
stylesInheritanceTransformersRegistry.registerFallback(
|
|
22
|
+
createTransformer( ( value: unknown ) => {
|
|
23
|
+
return value;
|
|
24
|
+
} )
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
registerCustomTransformers();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function registerCustomTransformers() {
|
|
31
|
+
stylesInheritanceTransformersRegistry.register( 'background-color-overlay', backgroundColorOverlayTransformer );
|
|
32
|
+
stylesInheritanceTransformersRegistry.register(
|
|
33
|
+
'background-gradient-overlay',
|
|
34
|
+
backgroundGradientOverlayTransformer
|
|
35
|
+
);
|
|
36
|
+
stylesInheritanceTransformersRegistry.register( 'background-image-overlay', backgroundImageOverlayTransformer );
|
|
37
|
+
stylesInheritanceTransformersRegistry.register( 'background-overlay', backgroundOverlayTransformer );
|
|
38
|
+
}
|
|
@@ -1,24 +1,23 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { useState } from 'react';
|
|
3
2
|
import { useBoundProp } from '@elementor/editor-controls';
|
|
4
3
|
import { isEmpty } from '@elementor/editor-props';
|
|
5
4
|
import { ELEMENTS_BASE_STYLES_PROVIDER_KEY, isElementsStylesProvider } from '@elementor/editor-styles-repository';
|
|
6
5
|
import { isExperimentActive } from '@elementor/editor-v1-adapters';
|
|
7
|
-
import {
|
|
6
|
+
import { Tooltip } from '@elementor/ui';
|
|
8
7
|
import { __ } from '@wordpress/i18n';
|
|
9
8
|
|
|
10
9
|
import { StyleIndicator } from '../components/style-indicator';
|
|
11
10
|
import { useStyle } from '../contexts/style-context';
|
|
12
11
|
import { useStylesInheritanceChain } from '../contexts/styles-inheritance-context';
|
|
12
|
+
import { EXPERIMENTAL_FEATURES } from '../sync/experiments-flags';
|
|
13
|
+
import { isUsingIndicatorPopover } from './consts';
|
|
13
14
|
import { StyleIndicatorInfotip } from './styles-inheritance-infotip';
|
|
14
15
|
|
|
15
16
|
export const StylesInheritanceIndicator = () => {
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
const { value, path, propType } = useBoundProp();
|
|
17
|
+
const { path, propType } = useBoundProp();
|
|
19
18
|
const { id: currentStyleId, provider: currentStyleProvider, meta: currentStyleMeta } = useStyle();
|
|
20
19
|
|
|
21
|
-
const isUsingNestedProps = isExperimentActive(
|
|
20
|
+
const isUsingNestedProps = isExperimentActive( EXPERIMENTAL_FEATURES.V_3_30 );
|
|
22
21
|
|
|
23
22
|
const finalPath = isUsingNestedProps ? path : path.slice( 0, 1 );
|
|
24
23
|
|
|
@@ -28,44 +27,48 @@ export const StylesInheritanceIndicator = () => {
|
|
|
28
27
|
return null;
|
|
29
28
|
}
|
|
30
29
|
|
|
31
|
-
const
|
|
30
|
+
const currentItem = inheritanceChain.find(
|
|
31
|
+
( {
|
|
32
|
+
style,
|
|
33
|
+
variant: {
|
|
34
|
+
meta: { breakpoint, state },
|
|
35
|
+
},
|
|
36
|
+
} ) =>
|
|
37
|
+
style.id === currentStyleId &&
|
|
38
|
+
breakpoint === currentStyleMeta.breakpoint &&
|
|
39
|
+
state === currentStyleMeta.state
|
|
40
|
+
);
|
|
32
41
|
|
|
33
|
-
|
|
34
|
-
return null;
|
|
35
|
-
}
|
|
42
|
+
const hasValue = ! isEmpty( currentItem?.value );
|
|
36
43
|
|
|
37
|
-
const
|
|
44
|
+
const [ actualStyle ] = inheritanceChain;
|
|
38
45
|
|
|
39
|
-
|
|
40
|
-
|
|
46
|
+
if ( actualStyle.provider === ELEMENTS_BASE_STYLES_PROVIDER_KEY ) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
41
49
|
|
|
42
|
-
const
|
|
50
|
+
const isFinalValue = currentItem === actualStyle;
|
|
43
51
|
|
|
44
52
|
const label = getLabel( { isFinalValue, hasValue } );
|
|
45
53
|
const variantType = getVariant( { isFinalValue, hasValue, currentStyleProvider } );
|
|
46
54
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
55
|
+
if ( ! isUsingIndicatorPopover() ) {
|
|
56
|
+
return (
|
|
57
|
+
<Tooltip title={ __( 'Style origin', 'elementor' ) } placement="top">
|
|
58
|
+
<StyleIndicator variant={ variantType } aria-label={ label } />
|
|
59
|
+
</Tooltip>
|
|
60
|
+
);
|
|
51
61
|
}
|
|
52
62
|
|
|
53
|
-
const toggleOpen = () => setOpen( ( prev ) => ! prev );
|
|
54
|
-
|
|
55
63
|
return (
|
|
56
|
-
<
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
61
|
-
open={ open }
|
|
62
|
-
onClose={ () => setOpen( false ) }
|
|
63
|
-
trigger="manual"
|
|
64
|
+
<StyleIndicatorInfotip
|
|
65
|
+
inheritanceChain={ inheritanceChain }
|
|
66
|
+
path={ finalPath }
|
|
67
|
+
propType={ propType }
|
|
68
|
+
label={ label }
|
|
64
69
|
>
|
|
65
|
-
<
|
|
66
|
-
|
|
67
|
-
</IconButton>
|
|
68
|
-
</Infotip>
|
|
70
|
+
<StyleIndicator variant={ variantType } />
|
|
71
|
+
</StyleIndicatorInfotip>
|
|
69
72
|
);
|
|
70
73
|
};
|
|
71
74
|
|
|
@@ -1,43 +1,137 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { useMemo } from 'react';
|
|
3
|
-
import { createPropsResolver, type PropsResolver
|
|
2
|
+
import { useMemo, useState } from 'react';
|
|
3
|
+
import { createPropsResolver, type PropsResolver } from '@elementor/editor-canvas';
|
|
4
4
|
import { type PropKey, type PropType } from '@elementor/editor-props';
|
|
5
|
-
import { Card, CardContent,
|
|
5
|
+
import { Box, Card, CardContent, CloseButton, IconButton, Infotip, Stack, Typography } from '@elementor/ui';
|
|
6
|
+
import { __ } from '@wordpress/i18n';
|
|
6
7
|
|
|
7
|
-
import {
|
|
8
|
+
import { useSectionContentRef } from '../components/section-content';
|
|
9
|
+
import { useDirection } from '../hooks/use-direction';
|
|
10
|
+
import { ActionIcons, BreakpointIcon, LabelChip, ValueComponent } from './components';
|
|
11
|
+
import { useNormalizedInheritanceChainItems } from './hooks/use-normalized-inheritance-chain-items';
|
|
12
|
+
import { stylesInheritanceTransformersRegistry } from './styles-inheritance-transformers-registry';
|
|
8
13
|
import { type SnapshotPropValue } from './types';
|
|
9
14
|
|
|
10
|
-
type
|
|
15
|
+
type Props = {
|
|
11
16
|
inheritanceChain: SnapshotPropValue[];
|
|
12
17
|
propType: PropType;
|
|
13
18
|
path: PropKey[];
|
|
19
|
+
label: string;
|
|
20
|
+
children: React.ReactNode;
|
|
14
21
|
};
|
|
15
22
|
|
|
16
|
-
|
|
23
|
+
const SIZE = 'tiny';
|
|
24
|
+
|
|
25
|
+
export const StyleIndicatorInfotip = ( { inheritanceChain, propType, path, label, children }: Props ) => {
|
|
26
|
+
const [ open, setOpen ] = useState( false );
|
|
27
|
+
const { isSiteRtl } = useDirection();
|
|
17
28
|
const key = path.join( '.' );
|
|
18
29
|
|
|
30
|
+
const sectionContentRef = useSectionContentRef();
|
|
31
|
+
const sectionContentWidth = sectionContentRef?.current?.offsetWidth ?? 320;
|
|
32
|
+
|
|
19
33
|
const resolve = useMemo< PropsResolver >( () => {
|
|
20
34
|
return createPropsResolver( {
|
|
21
|
-
transformers:
|
|
35
|
+
transformers: stylesInheritanceTransformersRegistry,
|
|
22
36
|
schema: { [ key ]: propType },
|
|
23
37
|
} );
|
|
24
38
|
}, [ key, propType ] );
|
|
25
39
|
|
|
26
40
|
const items = useNormalizedInheritanceChainItems( inheritanceChain, key, resolve );
|
|
27
41
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
42
|
+
const toggleOpen = () => setOpen( ( prev ) => ! prev );
|
|
43
|
+
const closeInfotip = () => {
|
|
44
|
+
setOpen( false );
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const forceInfotipAlignLeft = isSiteRtl ? 9999999 : -9999999;
|
|
48
|
+
|
|
49
|
+
const infotipContent = (
|
|
50
|
+
<Card
|
|
51
|
+
elevation={ 0 }
|
|
52
|
+
sx={ {
|
|
53
|
+
width: `${ sectionContentWidth }px`,
|
|
54
|
+
maxWidth: 500,
|
|
55
|
+
overflowX: 'hidden',
|
|
56
|
+
} }
|
|
57
|
+
>
|
|
58
|
+
<CardContent
|
|
59
|
+
sx={ {
|
|
60
|
+
display: 'flex',
|
|
61
|
+
gap: 0.5,
|
|
62
|
+
flexDirection: 'column',
|
|
63
|
+
p: 0,
|
|
64
|
+
'&:last-child': {
|
|
65
|
+
pb: 0,
|
|
66
|
+
},
|
|
67
|
+
} }
|
|
68
|
+
>
|
|
69
|
+
<Stack direction="row" alignItems="center" sx={ { pl: 1.5, pr: 0.5, minHeight: 36, py: 0.5 } }>
|
|
70
|
+
<Typography variant="subtitle2" color="secondary" sx={ { fontSize: 12, fontWeight: '500' } }>
|
|
71
|
+
{ __( 'Style origin', 'elementor' ) }
|
|
72
|
+
</Typography>
|
|
73
|
+
<CloseButton
|
|
74
|
+
slotProps={ { icon: { fontSize: SIZE } } }
|
|
75
|
+
sx={ { ml: 'auto' } }
|
|
76
|
+
onClick={ closeInfotip }
|
|
77
|
+
/>
|
|
78
|
+
</Stack>
|
|
79
|
+
<Stack gap={ 1.5 } sx={ { pl: 2, pr: 1, pb: 2, overflowX: 'hidden', overflowY: 'auto' } } role="list">
|
|
80
|
+
{ items.map( ( item, index ) => {
|
|
81
|
+
return (
|
|
82
|
+
<Box
|
|
83
|
+
key={ item.id }
|
|
84
|
+
display="flex"
|
|
85
|
+
gap={ 0.5 }
|
|
86
|
+
role="listitem"
|
|
87
|
+
/* translators: %s: Label of the inheritance item */
|
|
88
|
+
aria-label={ __( 'Inheritance item: %s', 'elementor' ).replace(
|
|
89
|
+
'%s',
|
|
90
|
+
item.displayLabel
|
|
91
|
+
) }
|
|
92
|
+
>
|
|
93
|
+
<Box display="flex" gap={ 0.5 } sx={ { flexWrap: 'wrap', width: '100%' } }>
|
|
94
|
+
<BreakpointIcon breakpoint={ item.breakpoint } />
|
|
95
|
+
<LabelChip displayLabel={ item.displayLabel } provider={ item.provider } />
|
|
96
|
+
<ValueComponent index={ index } value={ item.value } />
|
|
97
|
+
</Box>
|
|
98
|
+
<ActionIcons />
|
|
99
|
+
</Box>
|
|
100
|
+
);
|
|
101
|
+
} ) }
|
|
102
|
+
</Stack>
|
|
40
103
|
</CardContent>
|
|
41
104
|
</Card>
|
|
42
105
|
);
|
|
106
|
+
|
|
107
|
+
return (
|
|
108
|
+
<Infotip
|
|
109
|
+
placement="top"
|
|
110
|
+
content={ infotipContent }
|
|
111
|
+
open={ open }
|
|
112
|
+
onClose={ closeInfotip }
|
|
113
|
+
disableHoverListener={ true }
|
|
114
|
+
componentsProps={ {
|
|
115
|
+
tooltip: {
|
|
116
|
+
sx: {
|
|
117
|
+
mx: 2,
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
} }
|
|
121
|
+
slotProps={ {
|
|
122
|
+
popper: {
|
|
123
|
+
modifiers: [
|
|
124
|
+
{
|
|
125
|
+
name: 'offset',
|
|
126
|
+
options: { offset: [ forceInfotipAlignLeft, 0 ] },
|
|
127
|
+
},
|
|
128
|
+
],
|
|
129
|
+
},
|
|
130
|
+
} }
|
|
131
|
+
>
|
|
132
|
+
<IconButton onClick={ toggleOpen } aria-label={ label } sx={ { my: '-1px' } }>
|
|
133
|
+
{ children }
|
|
134
|
+
</IconButton>
|
|
135
|
+
</Infotip>
|
|
136
|
+
);
|
|
43
137
|
};
|