@elementor/editor-editing-panel 1.17.0 → 1.18.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 +70 -0
- package/dist/index.js +692 -501
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +610 -419
- package/dist/index.mjs.map +1 -1
- package/package.json +13 -12
- package/src/components/css-classes/css-class-item.tsx +32 -10
- package/src/components/css-classes/css-class-menu.tsx +25 -9
- package/src/components/css-classes/css-class-selector.tsx +13 -7
- package/src/components/editing-panel-hooks.tsx +0 -2
- package/src/components/editing-panel.tsx +2 -2
- package/src/components/multi-combobox.tsx +9 -4
- package/src/components/style-sections/border-section/border-radius-field.tsx +6 -5
- package/src/components/style-sections/position-section/dimensions-field.tsx +2 -2
- package/src/components/style-sections/spacing-section/spacing-section.tsx +4 -4
- package/src/components/style-sections/typography-section/font-family-field.tsx +2 -46
- package/src/components/style-sections/typography-section/font-style-field.tsx +1 -1
- package/src/components/style-sections/typography-section/hooks/use-font-families.ts +52 -0
- package/src/components/style-sections/typography-section/text-decoration-field.tsx +40 -89
- package/src/components/style-tab.tsx +34 -33
- package/src/contexts/styles-inheritance-context.tsx +65 -0
- package/src/controls-registry/control.tsx +3 -1
- package/src/dynamics/components/dynamic-selection.tsx +111 -74
- package/src/init.ts +6 -0
- package/src/sync/types.ts +1 -1
- package/src/hooks/use-close-editor-panel.ts +0 -23
|
@@ -1,98 +1,49 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { ControlLabel } from '@elementor/editor-controls';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { Grid, ToggleButton as ToggleButtonBase, ToggleButtonGroup, type ToggleButtonProps } from '@elementor/ui';
|
|
2
|
+
import { ControlLabel, ToggleControl, type ToggleControlProps } from '@elementor/editor-controls';
|
|
3
|
+
import { MinusIcon, OverlineIcon, StrikethroughIcon, UnderlineIcon } from '@elementor/icons';
|
|
4
|
+
import { Grid } from '@elementor/ui';
|
|
6
5
|
import { __ } from '@wordpress/i18n';
|
|
7
6
|
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
|
|
7
|
+
import { StylesField } from '../../../controls-registry/styles-field';
|
|
8
|
+
|
|
9
|
+
type Decoration = 'none' | 'underline' | 'line-through' | 'overline';
|
|
10
|
+
|
|
11
|
+
const options: ToggleControlProps< Decoration >[ 'options' ] = [
|
|
12
|
+
{
|
|
13
|
+
value: 'none',
|
|
14
|
+
label: __( 'None', 'elementor' ),
|
|
15
|
+
renderContent: ( { size } ) => <MinusIcon fontSize={ size } />,
|
|
16
|
+
showTooltip: true,
|
|
17
|
+
exclusive: true,
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
value: 'underline',
|
|
21
|
+
label: __( 'Underline', 'elementor' ),
|
|
22
|
+
renderContent: ( { size } ) => <UnderlineIcon fontSize={ size } />,
|
|
23
|
+
showTooltip: true,
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
value: 'line-through',
|
|
27
|
+
label: __( 'Line-through', 'elementor' ),
|
|
28
|
+
renderContent: ( { size } ) => <StrikethroughIcon fontSize={ size } />,
|
|
29
|
+
showTooltip: true,
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
value: 'overline',
|
|
33
|
+
label: __( 'Overline', 'elementor' ),
|
|
34
|
+
renderContent: ( { size } ) => <OverlineIcon fontSize={ size } />,
|
|
35
|
+
showTooltip: true,
|
|
36
|
+
},
|
|
37
|
+
];
|
|
38
|
+
export const TextDecorationField = () => (
|
|
39
|
+
<StylesField bind={ 'text-decoration' }>
|
|
28
40
|
<Grid container gap={ 2 } alignItems="center" flexWrap="nowrap">
|
|
29
41
|
<Grid item xs={ 6 }>
|
|
30
|
-
<ControlLabel>{ __( '
|
|
42
|
+
<ControlLabel>{ __( 'Line decoration', 'elementor' ) }</ControlLabel>
|
|
31
43
|
</Grid>
|
|
32
44
|
<Grid item xs={ 6 } display="flex" justifyContent="end">
|
|
33
|
-
<
|
|
34
|
-
<ShorthandControl
|
|
35
|
-
value="line-through"
|
|
36
|
-
currentValues={ textDecoration?.value || '' }
|
|
37
|
-
updateValues={ handleSetTextDecoration }
|
|
38
|
-
aria-label="line-through"
|
|
39
|
-
>
|
|
40
|
-
<StrikethroughIcon fontSize={ buttonSize } />
|
|
41
|
-
</ShorthandControl>
|
|
42
|
-
<ShorthandControl
|
|
43
|
-
value="underline"
|
|
44
|
-
currentValues={ textDecoration?.value || '' }
|
|
45
|
-
updateValues={ handleSetTextDecoration }
|
|
46
|
-
aria-label="underline"
|
|
47
|
-
>
|
|
48
|
-
<UnderlineIcon fontSize={ buttonSize } />
|
|
49
|
-
</ShorthandControl>
|
|
50
|
-
</ToggleButtonGroup>
|
|
45
|
+
<ToggleControl options={ options } exclusive={ false } />
|
|
51
46
|
</Grid>
|
|
52
47
|
</Grid>
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
type ShorthandControlProps = React.PropsWithChildren< {
|
|
57
|
-
value: string;
|
|
58
|
-
currentValues: string;
|
|
59
|
-
updateValues: ( newValue: string | null ) => void;
|
|
60
|
-
'aria-label': string;
|
|
61
|
-
} >;
|
|
62
|
-
|
|
63
|
-
export const ShorthandControl = ( {
|
|
64
|
-
children,
|
|
65
|
-
value,
|
|
66
|
-
currentValues,
|
|
67
|
-
updateValues,
|
|
68
|
-
'aria-label': ariaLabel,
|
|
69
|
-
}: ShorthandControlProps ) => {
|
|
70
|
-
const valuesArr = currentValues.split( ' ' ).filter( Boolean );
|
|
71
|
-
const selected = valuesArr.includes( value );
|
|
72
|
-
|
|
73
|
-
const toggleValue = ( newValue: string ) => {
|
|
74
|
-
if ( selected ) {
|
|
75
|
-
updateValues( valuesArr.filter( ( v ) => v !== newValue ).join( ' ' ) || null );
|
|
76
|
-
} else {
|
|
77
|
-
updateValues( [ ...valuesArr, newValue ].join( ' ' ) );
|
|
78
|
-
}
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
return (
|
|
82
|
-
<ToggleButton value={ value } onChange={ toggleValue } selected={ selected } aria-label={ ariaLabel }>
|
|
83
|
-
{ children }
|
|
84
|
-
</ToggleButton>
|
|
85
|
-
);
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
type ControlToggleButtonProps = Omit< ToggleButtonProps, 'onChange' > & {
|
|
89
|
-
onChange: ( newValue: string ) => void;
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
const ToggleButton = ( { onChange, ...props }: ControlToggleButtonProps ) => {
|
|
93
|
-
const handleChange = ( _e: React.MouseEvent< HTMLElement >, newValue: string ) => {
|
|
94
|
-
onChange( newValue );
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
return <ToggleButtonBase { ...props } onChange={ handleChange } size={ buttonSize } />;
|
|
98
|
-
};
|
|
48
|
+
</StylesField>
|
|
49
|
+
);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { useState } from 'react';
|
|
3
|
-
import {
|
|
4
|
-
import { type ClassesPropValue, type PropKey } from '@elementor/editor-props';
|
|
3
|
+
import { getElementStyles, useElementSetting } from '@elementor/editor-elements';
|
|
4
|
+
import { CLASSES_PROP_KEY, type ClassesPropValue, type PropKey } from '@elementor/editor-props';
|
|
5
5
|
import { useActiveBreakpoint } from '@elementor/editor-responsive';
|
|
6
6
|
import { type StyleDefinitionID, type StyleDefinitionState } from '@elementor/editor-styles';
|
|
7
7
|
import { SessionStorageProvider } from '@elementor/session';
|
|
@@ -11,6 +11,7 @@ import { __ } from '@wordpress/i18n';
|
|
|
11
11
|
import { ClassesPropProvider } from '../contexts/classes-prop-context';
|
|
12
12
|
import { useElement } from '../contexts/element-context';
|
|
13
13
|
import { StyleProvider } from '../contexts/style-context';
|
|
14
|
+
import { StyleInheritanceProvider } from '../contexts/styles-inheritance-context';
|
|
14
15
|
import { CssClassSelector } from './css-classes/css-class-selector';
|
|
15
16
|
import { Section } from './section';
|
|
16
17
|
import { SectionsList } from './sections-list';
|
|
@@ -23,8 +24,6 @@ import { SizeSection } from './style-sections/size-section/size-section';
|
|
|
23
24
|
import { SpacingSection } from './style-sections/spacing-section/spacing-section';
|
|
24
25
|
import { TypographySection } from './style-sections/typography-section/typography-section';
|
|
25
26
|
|
|
26
|
-
const CLASSES_PROP_KEY = 'classes';
|
|
27
|
-
|
|
28
27
|
export const StyleTab = () => {
|
|
29
28
|
const currentClassesProp = useCurrentClassesProp();
|
|
30
29
|
const [ activeStyleDefId, setActiveStyleDefId ] = useActiveStyleDefId( currentClassesProp );
|
|
@@ -43,34 +42,36 @@ export const StyleTab = () => {
|
|
|
43
42
|
setMetaState={ setActiveStyleState }
|
|
44
43
|
>
|
|
45
44
|
<SessionStorageProvider prefix={ activeStyleDefId ?? '' }>
|
|
46
|
-
<
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
<
|
|
50
|
-
<
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
<
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
<
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
<
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
<
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
<
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
<
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
<
|
|
72
|
-
|
|
73
|
-
|
|
45
|
+
<StyleInheritanceProvider>
|
|
46
|
+
<CssClassSelector />
|
|
47
|
+
<Divider />
|
|
48
|
+
<SectionsList>
|
|
49
|
+
<Section title={ __( 'Layout', 'elementor' ) }>
|
|
50
|
+
<LayoutSection />
|
|
51
|
+
</Section>
|
|
52
|
+
<Section title={ __( 'Spacing', 'elementor' ) }>
|
|
53
|
+
<SpacingSection />
|
|
54
|
+
</Section>
|
|
55
|
+
<Section title={ __( 'Size', 'elementor' ) }>
|
|
56
|
+
<SizeSection />
|
|
57
|
+
</Section>
|
|
58
|
+
<Section title={ __( 'Position', 'elementor' ) }>
|
|
59
|
+
<PositionSection />
|
|
60
|
+
</Section>
|
|
61
|
+
<Section title={ __( 'Typography', 'elementor' ) }>
|
|
62
|
+
<TypographySection />
|
|
63
|
+
</Section>
|
|
64
|
+
<Section title={ __( 'Background', 'elementor' ) }>
|
|
65
|
+
<BackgroundSection />
|
|
66
|
+
</Section>
|
|
67
|
+
<Section title={ __( 'Border', 'elementor' ) }>
|
|
68
|
+
<BorderSection />
|
|
69
|
+
</Section>
|
|
70
|
+
<Section title={ __( 'Effects', 'elementor' ) }>
|
|
71
|
+
<EffectsSection />
|
|
72
|
+
</Section>
|
|
73
|
+
</SectionsList>
|
|
74
|
+
</StyleInheritanceProvider>
|
|
74
75
|
</SessionStorageProvider>
|
|
75
76
|
</StyleProvider>
|
|
76
77
|
</ClassesPropProvider>
|
|
@@ -89,7 +90,7 @@ function useFirstElementStyleDef( currentClassesProp: PropKey ) {
|
|
|
89
90
|
const { element } = useElement();
|
|
90
91
|
|
|
91
92
|
const classesIds = useElementSetting< ClassesPropValue >( element.id, currentClassesProp )?.value || [];
|
|
92
|
-
const stylesDefs =
|
|
93
|
+
const stylesDefs = getElementStyles( element.id ) ?? {};
|
|
93
94
|
|
|
94
95
|
return Object.values( stylesDefs ).find( ( styleDef ) => classesIds.includes( styleDef.id ) );
|
|
95
96
|
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { createContext, type PropsWithChildren, useContext } from 'react';
|
|
3
|
+
import { useElementSetting } from '@elementor/editor-elements';
|
|
4
|
+
import { classesPropTypeUtil, type ClassesPropValue } from '@elementor/editor-props';
|
|
5
|
+
import { getBreakpointsTree } from '@elementor/editor-responsive';
|
|
6
|
+
import { stylesRepository } from '@elementor/editor-styles-repository';
|
|
7
|
+
|
|
8
|
+
import { createStylesInheritance } from '../styles-inheritance/create-styles-inheritance';
|
|
9
|
+
import { type SnapshotPropValue, type StylesInheritanceSnapshotGetter } from '../styles-inheritance/types';
|
|
10
|
+
import { useClassesProp } from './classes-prop-context';
|
|
11
|
+
import { useElement } from './element-context';
|
|
12
|
+
import { useStyle } from './style-context';
|
|
13
|
+
|
|
14
|
+
type ContextValue = {
|
|
15
|
+
getSnapshot: StylesInheritanceSnapshotGetter;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const Context = createContext< ContextValue | null >( null );
|
|
19
|
+
|
|
20
|
+
export function StyleInheritanceProvider( { children }: PropsWithChildren ) {
|
|
21
|
+
const styleDefs = useAppliedStyles();
|
|
22
|
+
|
|
23
|
+
const breakpointsTree = getBreakpointsTree();
|
|
24
|
+
|
|
25
|
+
const getSnapshot = createStylesInheritance( styleDefs, breakpointsTree );
|
|
26
|
+
|
|
27
|
+
return <Context.Provider value={ { getSnapshot } }>{ children }</Context.Provider>;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function useStylesInheritanceFields< T extends readonly string[] >(
|
|
31
|
+
fields: T
|
|
32
|
+
): { [ K in T[ number ] ]: SnapshotPropValue[] } | null {
|
|
33
|
+
const context = useContext( Context );
|
|
34
|
+
const { meta } = useStyle();
|
|
35
|
+
|
|
36
|
+
if ( ! context ) {
|
|
37
|
+
throw new Error( 'useStylesInheritanceFields must be used within a StyleInheritanceProvider' );
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if ( ! meta ) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const snapshot = context.getSnapshot( meta );
|
|
45
|
+
|
|
46
|
+
return fields.reduce(
|
|
47
|
+
( acc, key: T[ number ] ) => ( { ...acc, [ key ]: snapshot?.[ key ] ?? [] } ),
|
|
48
|
+
{} as { [ K in T[ number ] ]: SnapshotPropValue[] }
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function useStylesInheritanceField( field: string ): SnapshotPropValue[] {
|
|
53
|
+
return useStylesInheritanceFields( [ field ] )?.[ field ] ?? [];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const useAppliedStyles = () => {
|
|
57
|
+
const { element } = useElement();
|
|
58
|
+
const currentClassesProp = useClassesProp();
|
|
59
|
+
const classesProp = useElementSetting< ClassesPropValue >( element.id, currentClassesProp );
|
|
60
|
+
|
|
61
|
+
const appliedStyles = classesPropTypeUtil.extract( classesProp );
|
|
62
|
+
const allStyles = stylesRepository.all();
|
|
63
|
+
|
|
64
|
+
return allStyles.filter( ( style ) => appliedStyles?.includes( style.id ) );
|
|
65
|
+
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import type { ComponentProps } from 'react';
|
|
3
3
|
|
|
4
|
+
import { useElement } from '../contexts/element-context';
|
|
4
5
|
import { ControlTypeNotFoundError } from '../errors';
|
|
5
6
|
import { type ControlType, type ControlTypes, getControlByType } from './controls-registry';
|
|
6
7
|
|
|
@@ -24,6 +25,7 @@ type ControlProps< T extends ControlType > = AnyPropertyRequired< ComponentProps
|
|
|
24
25
|
|
|
25
26
|
export const Control = < T extends ControlType >( { props, type }: ControlProps< T > ) => {
|
|
26
27
|
const ControlByType = getControlByType( type );
|
|
28
|
+
const { element } = useElement();
|
|
27
29
|
|
|
28
30
|
if ( ! ControlByType ) {
|
|
29
31
|
throw new ControlTypeNotFoundError( {
|
|
@@ -32,5 +34,5 @@ export const Control = < T extends ControlType >( { props, type }: ControlProps<
|
|
|
32
34
|
}
|
|
33
35
|
|
|
34
36
|
// @ts-expect-error ControlComponent props are inferred from the type (T).
|
|
35
|
-
return <ControlByType { ...props } />;
|
|
37
|
+
return <ControlByType { ...props } context={ { elementId: element.id } } />;
|
|
36
38
|
};
|
|
@@ -34,6 +34,11 @@ type DynamicSelectionProps = {
|
|
|
34
34
|
onSelect?: () => void;
|
|
35
35
|
};
|
|
36
36
|
|
|
37
|
+
type NoResultsProps = {
|
|
38
|
+
searchValue: string;
|
|
39
|
+
onClear?: () => void;
|
|
40
|
+
};
|
|
41
|
+
|
|
37
42
|
export const DynamicSelection = ( { onSelect }: DynamicSelectionProps ) => {
|
|
38
43
|
const [ searchValue, setSearchValue ] = useState( '' );
|
|
39
44
|
const { groups: dynamicGroups } = getAtomicDynamicTags() || {};
|
|
@@ -47,6 +52,8 @@ export const DynamicSelection = ( { onSelect }: DynamicSelectionProps ) => {
|
|
|
47
52
|
|
|
48
53
|
const options = useFilteredOptions( searchValue );
|
|
49
54
|
|
|
55
|
+
const hasNoDynamicTags = ! options.length && ! searchValue.trim();
|
|
56
|
+
|
|
50
57
|
const handleSearch = ( event: React.ChangeEvent< HTMLInputElement > ) => {
|
|
51
58
|
setSearchValue( event.target.value );
|
|
52
59
|
};
|
|
@@ -63,85 +70,115 @@ export const DynamicSelection = ( { onSelect }: DynamicSelectionProps ) => {
|
|
|
63
70
|
|
|
64
71
|
return (
|
|
65
72
|
<Stack>
|
|
66
|
-
|
|
67
|
-
<
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
return (
|
|
95
|
-
<MenuItem
|
|
96
|
-
key={ value }
|
|
97
|
-
selected={ isSelected }
|
|
98
|
-
// eslint-disable-next-line jsx-a11y/no-autofocus
|
|
99
|
-
autoFocus={ isSelected }
|
|
100
|
-
sx={ { px: 1.5, typography: 'caption' } }
|
|
101
|
-
onClick={ () => handleSetDynamicTag( value ) }
|
|
73
|
+
{ hasNoDynamicTags ? (
|
|
74
|
+
<NoDynamicTags />
|
|
75
|
+
) : (
|
|
76
|
+
<Fragment>
|
|
77
|
+
<Box px={ 1.5 } pb={ 1 }>
|
|
78
|
+
<TextField
|
|
79
|
+
fullWidth
|
|
80
|
+
size={ SIZE }
|
|
81
|
+
value={ searchValue }
|
|
82
|
+
onChange={ handleSearch }
|
|
83
|
+
placeholder={ __( 'Search dynamic tags…', 'elementor' ) }
|
|
84
|
+
InputProps={ {
|
|
85
|
+
startAdornment: (
|
|
86
|
+
<InputAdornment position="start">
|
|
87
|
+
<SearchIcon fontSize={ SIZE } />
|
|
88
|
+
</InputAdornment>
|
|
89
|
+
),
|
|
90
|
+
} }
|
|
91
|
+
/>
|
|
92
|
+
</Box>
|
|
93
|
+
<Divider />
|
|
94
|
+
<Box sx={ { overflowY: 'auto', height: 260, width: 220 } }>
|
|
95
|
+
{ options.length > 0 ? (
|
|
96
|
+
<MenuList role="listbox" tabIndex={ 0 }>
|
|
97
|
+
{ options.map( ( [ category, items ], index ) => (
|
|
98
|
+
<Fragment key={ index }>
|
|
99
|
+
<ListSubheader
|
|
100
|
+
sx={ { px: 1.5, typography: 'caption', color: 'text.tertiary' } }
|
|
102
101
|
>
|
|
103
|
-
{
|
|
104
|
-
</
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
color="text.secondary"
|
|
131
|
-
variant="caption"
|
|
132
|
-
component="button"
|
|
133
|
-
onClick={ () => setSearchValue( '' ) }
|
|
134
|
-
>
|
|
135
|
-
{ __( 'Clear & try again', 'elementor' ) }
|
|
136
|
-
</Link>
|
|
137
|
-
</Typography>
|
|
138
|
-
</Stack>
|
|
139
|
-
) }
|
|
140
|
-
</Box>
|
|
102
|
+
{ dynamicGroups?.[ category ]?.title || category }
|
|
103
|
+
</ListSubheader>
|
|
104
|
+
{ items.map( ( { value, label: tagLabel } ) => {
|
|
105
|
+
const isSelected = isCurrentValueDynamic && value === dynamicValue?.name;
|
|
106
|
+
|
|
107
|
+
return (
|
|
108
|
+
<MenuItem
|
|
109
|
+
key={ value }
|
|
110
|
+
selected={ isSelected }
|
|
111
|
+
// eslint-disable-next-line jsx-a11y/no-autofocus
|
|
112
|
+
autoFocus={ isSelected }
|
|
113
|
+
sx={ { px: 1.5, typography: 'caption' } }
|
|
114
|
+
onClick={ () => handleSetDynamicTag( value ) }
|
|
115
|
+
>
|
|
116
|
+
{ tagLabel }
|
|
117
|
+
</MenuItem>
|
|
118
|
+
);
|
|
119
|
+
} ) }
|
|
120
|
+
</Fragment>
|
|
121
|
+
) ) }
|
|
122
|
+
</MenuList>
|
|
123
|
+
) : (
|
|
124
|
+
<NoResults searchValue={ searchValue } onClear={ () => setSearchValue( '' ) } />
|
|
125
|
+
) }
|
|
126
|
+
</Box>
|
|
127
|
+
</Fragment>
|
|
128
|
+
) }
|
|
141
129
|
</Stack>
|
|
142
130
|
);
|
|
143
131
|
};
|
|
144
132
|
|
|
133
|
+
const NoResults = ( { searchValue, onClear }: NoResultsProps ) => (
|
|
134
|
+
<Stack
|
|
135
|
+
gap={ 1 }
|
|
136
|
+
alignItems="center"
|
|
137
|
+
justifyContent="center"
|
|
138
|
+
height="100%"
|
|
139
|
+
p={ 2.5 }
|
|
140
|
+
color="text.secondary"
|
|
141
|
+
sx={ { pb: 3.5 } }
|
|
142
|
+
>
|
|
143
|
+
<DatabaseIcon fontSize="large" />
|
|
144
|
+
<Typography align="center" variant="subtitle2">
|
|
145
|
+
{ __( 'Sorry, nothing matched', 'elementor' ) }
|
|
146
|
+
<br />
|
|
147
|
+
“{ searchValue }”.
|
|
148
|
+
</Typography>
|
|
149
|
+
<Typography align="center" variant="caption">
|
|
150
|
+
{ __( 'Try something else.', 'elementor' ) }
|
|
151
|
+
|
|
152
|
+
<Link color="text.secondary" variant="caption" component="button" onClick={ onClear }>
|
|
153
|
+
{ __( 'Clear & try again', 'elementor' ) }
|
|
154
|
+
</Link>
|
|
155
|
+
</Typography>
|
|
156
|
+
</Stack>
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
const NoDynamicTags = () => (
|
|
160
|
+
<Box sx={ { overflowY: 'hidden', height: 297, width: 220 } }>
|
|
161
|
+
<Divider />
|
|
162
|
+
<Stack
|
|
163
|
+
gap={ 1 }
|
|
164
|
+
alignItems="center"
|
|
165
|
+
justifyContent="center"
|
|
166
|
+
height="100%"
|
|
167
|
+
p={ 2.5 }
|
|
168
|
+
color="text.secondary"
|
|
169
|
+
sx={ { pb: 3.5 } }
|
|
170
|
+
>
|
|
171
|
+
<DatabaseIcon fontSize="large" />
|
|
172
|
+
<Typography align="center" variant="subtitle2">
|
|
173
|
+
{ __( 'Streamline your workflow with dynamic tags', 'elementor' ) }
|
|
174
|
+
</Typography>
|
|
175
|
+
<Typography align="center" variant="caption">
|
|
176
|
+
{ __( 'You’ll need Elementor Pro to use this feature.', 'elementor' ) }
|
|
177
|
+
</Typography>
|
|
178
|
+
</Stack>
|
|
179
|
+
</Box>
|
|
180
|
+
);
|
|
181
|
+
|
|
145
182
|
const useFilteredOptions = ( searchValue: string ): OptionEntry[] => {
|
|
146
183
|
const dynamicTags = usePropDynamicTags();
|
|
147
184
|
|
package/src/init.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { injectIntoLogic } from '@elementor/editor';
|
|
2
|
+
import { PrefetchUserData } from '@elementor/editor-current-user';
|
|
2
3
|
import { __registerPanel as registerPanel } from '@elementor/editor-panels';
|
|
3
4
|
import { blockCommand } from '@elementor/editor-v1-adapters';
|
|
4
5
|
|
|
@@ -16,6 +17,11 @@ export default function init() {
|
|
|
16
17
|
component: EditingPanelHooks,
|
|
17
18
|
} );
|
|
18
19
|
|
|
20
|
+
injectIntoLogic( {
|
|
21
|
+
id: 'current-user-data',
|
|
22
|
+
component: PrefetchUserData,
|
|
23
|
+
} );
|
|
24
|
+
|
|
19
25
|
// TODO: Move it from here once we have dynamic package.
|
|
20
26
|
initDynamics();
|
|
21
27
|
}
|
package/src/sync/types.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type ControlItem, type V1Element } from '@elementor/editor-elements';
|
|
2
2
|
import { type PropsSchema } from '@elementor/editor-props';
|
|
3
3
|
|
|
4
|
-
type SupportedFonts = 'system' | 'googlefonts' | '
|
|
4
|
+
export type SupportedFonts = 'system' | 'googlefonts' | 'custom';
|
|
5
5
|
|
|
6
6
|
type EnqueueFont = ( fontFamily: string, context?: 'preview' | 'editor' ) => void;
|
|
7
7
|
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { useEffect } from 'react';
|
|
2
|
-
import { getSelectedElements, isElementInContainer, type V1Element } from '@elementor/editor-elements';
|
|
3
|
-
import { __privateListenTo as listenTo, type CommandEvent, commandStartEvent } from '@elementor/editor-v1-adapters';
|
|
4
|
-
|
|
5
|
-
import { usePanelActions } from '../panel';
|
|
6
|
-
import { isAtomicWidgetSelected } from '../sync/is-atomic-widget-selected';
|
|
7
|
-
|
|
8
|
-
export const useCloseEditorPanel = () => {
|
|
9
|
-
const { close } = usePanelActions();
|
|
10
|
-
|
|
11
|
-
return useEffect( () => {
|
|
12
|
-
return listenTo( commandStartEvent( 'document/elements/delete' ), ( e ) => {
|
|
13
|
-
const selectedElement = getSelectedElements()[ 0 ];
|
|
14
|
-
const { container: deletedContainer } = ( e as CommandEvent< { container: V1Element } > )?.args;
|
|
15
|
-
const isSelectedElementInDeletedContainer =
|
|
16
|
-
deletedContainer && selectedElement && isElementInContainer( selectedElement, deletedContainer );
|
|
17
|
-
|
|
18
|
-
if ( isSelectedElementInDeletedContainer && isAtomicWidgetSelected() ) {
|
|
19
|
-
close();
|
|
20
|
-
}
|
|
21
|
-
} );
|
|
22
|
-
}, [] ); // eslint-disable-line react-hooks/exhaustive-deps
|
|
23
|
-
};
|