@elementor/editor-editing-panel 0.14.2 → 0.15.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 +18 -0
- package/dist/index.d.mts +29 -1
- package/dist/index.d.ts +29 -1
- package/dist/index.js +327 -263
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +309 -259
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
- package/src/components/editing-panel.tsx +1 -1
- package/src/components/settings-tab.tsx +6 -17
- package/src/components/style-sections/size-section.tsx +5 -6
- package/src/components/style-sections/typography-section/font-size-control.tsx +16 -0
- package/src/components/style-sections/typography-section/font-weight-control.tsx +24 -0
- package/src/{controls/control-types → components/style-sections/typography-section}/text-style-control.tsx +16 -14
- package/src/components/style-sections/typography-section/typography-section.tsx +20 -0
- package/src/components/style-tab.tsx +1 -1
- package/src/contexts/element-context.tsx +5 -3
- package/src/controls/components/control-container.tsx +18 -0
- package/src/controls/control-replacement.ts +26 -0
- package/src/controls/control-types/image-control.tsx +3 -18
- package/src/controls/control-types/size-control.tsx +4 -2
- package/src/controls/control-types/text-area-control.tsx +1 -1
- package/src/controls/control.tsx +50 -0
- package/src/controls/{get-control-by-type.ts → controls-registry.tsx} +13 -9
- package/src/controls/settings-control.tsx +8 -21
- package/src/hooks/use-dynamic-tags-config.ts +35 -0
- package/src/hooks/use-element-type.ts +5 -0
- package/src/index.ts +3 -0
- package/src/sync/get-atomic-dynamic-tags.ts +14 -0
- package/src/sync/get-elementor-config.ts +7 -0
- package/src/sync/types.ts +15 -1
- package/src/types.ts +14 -0
- package/LICENSE +0 -674
- package/src/components/style-sections/typography-section.tsx +0 -15
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { Stack, StackProps, styled } from '@elementor/ui';
|
|
3
|
+
|
|
4
|
+
const StyledStack = styled( Stack )( ( { theme, gap, direction } ) => ( {
|
|
5
|
+
'> :only-child': {
|
|
6
|
+
width: '100%',
|
|
7
|
+
},
|
|
8
|
+
'&:where( :has( > :nth-child( 2 ):last-child ) ) > :where( * )': {
|
|
9
|
+
width: direction === 'column' ? '100%' : `calc( 50% - ${ theme.spacing( gap / 2 ) })`,
|
|
10
|
+
},
|
|
11
|
+
'&:where( :has( > :nth-child( 3 ):last-child ) ) > :where( * )': {
|
|
12
|
+
width: direction === 'column' ? '100%' : `calc( 33.3333% - ${ theme.spacing( gap * 2 ) } / 3)`,
|
|
13
|
+
},
|
|
14
|
+
} ) );
|
|
15
|
+
|
|
16
|
+
export const ControlContainer = ( props: StackProps ) => (
|
|
17
|
+
<StyledStack gap={ 1 } direction="row" alignItems="center" justifyContent="space-between" { ...props } />
|
|
18
|
+
);
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { PropValue } from '../types';
|
|
2
|
+
|
|
3
|
+
type ReplaceWhenParams = {
|
|
4
|
+
value: PropValue;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
type ControlReplacement = {
|
|
8
|
+
component: React.ComponentType;
|
|
9
|
+
condition: ( { value }: ReplaceWhenParams ) => boolean;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
let controlReplacement: ControlReplacement | undefined;
|
|
13
|
+
|
|
14
|
+
export const replaceControl = ( { component, condition }: ControlReplacement ) => {
|
|
15
|
+
controlReplacement = { component, condition };
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const getControlReplacement = ( { value }: ReplaceWhenParams ) => {
|
|
19
|
+
let shouldReplace = false;
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
shouldReplace = !! controlReplacement?.condition( { value } );
|
|
23
|
+
} catch {}
|
|
24
|
+
|
|
25
|
+
return shouldReplace ? controlReplacement?.component : undefined;
|
|
26
|
+
};
|
|
@@ -18,25 +18,10 @@ type Image = {
|
|
|
18
18
|
};
|
|
19
19
|
};
|
|
20
20
|
|
|
21
|
-
// TODO: Use schema to get default image.
|
|
22
|
-
const defaultState: Image = {
|
|
23
|
-
$$type: 'image',
|
|
24
|
-
value: {
|
|
25
|
-
url: '/wp-content/plugins/elementor/assets/images/placeholder.png',
|
|
26
|
-
},
|
|
27
|
-
};
|
|
28
|
-
|
|
29
21
|
export const ImageControl = () => {
|
|
30
|
-
const { value, setValue } = useControl< Image >(
|
|
22
|
+
const { value, setValue } = useControl< Image >();
|
|
31
23
|
const { data: attachment } = useWpMediaAttachment( value?.value?.attachmentId );
|
|
32
|
-
|
|
33
|
-
const getImageSrc = () => {
|
|
34
|
-
if ( attachment?.url ) {
|
|
35
|
-
return attachment.url;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
return value?.value?.url ?? defaultState.value.url;
|
|
39
|
-
};
|
|
24
|
+
const src = attachment?.url ?? value?.value?.url;
|
|
40
25
|
|
|
41
26
|
const { open } = useWpMediaFrame( {
|
|
42
27
|
types: [ 'image' ],
|
|
@@ -54,7 +39,7 @@ export const ImageControl = () => {
|
|
|
54
39
|
|
|
55
40
|
return (
|
|
56
41
|
<Card variant="outlined">
|
|
57
|
-
<CardMedia image={
|
|
42
|
+
<CardMedia image={ src } sx={ { height: 150 } } />
|
|
58
43
|
<CardOverlay>
|
|
59
44
|
<Button
|
|
60
45
|
color="inherit"
|
|
@@ -5,15 +5,17 @@ import { useControl } from '../control-context';
|
|
|
5
5
|
import { useSyncExternalState } from '../hooks/use-sync-external-state';
|
|
6
6
|
|
|
7
7
|
export type SizeControlProps = {
|
|
8
|
-
units
|
|
8
|
+
units?: Unit[];
|
|
9
9
|
placeholder?: string;
|
|
10
10
|
};
|
|
11
11
|
|
|
12
12
|
export type Unit = 'px' | '%' | 'em' | 'rem' | 'vw';
|
|
13
13
|
|
|
14
|
+
const defaultUnits: Unit[] = [ 'px', '%', 'em', 'rem', 'vw' ];
|
|
15
|
+
|
|
14
16
|
export type SizeControlValue = TransformablePropValue< { unit: Unit; size: number } >;
|
|
15
17
|
|
|
16
|
-
export const SizeControl = ( { units, placeholder }: SizeControlProps ) => {
|
|
18
|
+
export const SizeControl = ( { units = defaultUnits, placeholder }: SizeControlProps ) => {
|
|
17
19
|
const { value, setValue } = useControl< SizeControlValue >();
|
|
18
20
|
|
|
19
21
|
const [ state, setState ] = useSyncExternalState< SizeControlValue >( {
|
|
@@ -7,7 +7,7 @@ type Props = {
|
|
|
7
7
|
};
|
|
8
8
|
|
|
9
9
|
export const TextAreaControl = ( { placeholder }: Props ) => {
|
|
10
|
-
const { value, setValue } = useControl< string >(
|
|
10
|
+
const { value, setValue } = useControl< string >();
|
|
11
11
|
|
|
12
12
|
const handleChange = ( event: React.ChangeEvent< HTMLInputElement > ) => {
|
|
13
13
|
setValue( event.target.value );
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type { ComponentProps } from 'react';
|
|
3
|
+
import { getControlReplacement } from './control-replacement';
|
|
4
|
+
import { useControl } from './control-context';
|
|
5
|
+
import { createError } from '@elementor/utils';
|
|
6
|
+
import { ControlType, ControlTypes, getControlByType } from './controls-registry';
|
|
7
|
+
|
|
8
|
+
export type ControlTypeErrorContext = {
|
|
9
|
+
type: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const ControlTypeError = createError< ControlTypeErrorContext >( {
|
|
13
|
+
code: 'CONTROL_TYPE_NOT_FOUND',
|
|
14
|
+
message: `Control type not found.`,
|
|
15
|
+
} );
|
|
16
|
+
|
|
17
|
+
type IsRequired< T, K extends keyof T > = object extends Pick< T, K > ? false : true;
|
|
18
|
+
|
|
19
|
+
type AnyPropertyRequired< T > = {
|
|
20
|
+
[ K in keyof T ]: IsRequired< T, K >;
|
|
21
|
+
}[ keyof T ] extends true
|
|
22
|
+
? true
|
|
23
|
+
: false;
|
|
24
|
+
|
|
25
|
+
type ControlProps< T extends ControlType > = AnyPropertyRequired< ComponentProps< ControlTypes[ T ] > > extends true
|
|
26
|
+
? {
|
|
27
|
+
props: ComponentProps< ControlTypes[ T ] >;
|
|
28
|
+
type: T;
|
|
29
|
+
}
|
|
30
|
+
: {
|
|
31
|
+
props?: ComponentProps< ControlTypes[ T ] >;
|
|
32
|
+
type: T;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export const Control = < T extends ControlType >( { props, type }: ControlProps< T > ) => {
|
|
36
|
+
const { value } = useControl();
|
|
37
|
+
|
|
38
|
+
const ControlByType = getControlByType( type );
|
|
39
|
+
|
|
40
|
+
if ( ! ControlByType ) {
|
|
41
|
+
throw new ControlTypeError( {
|
|
42
|
+
context: { type },
|
|
43
|
+
} );
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const ControlComponent = getControlReplacement( { value } ) || ControlByType;
|
|
47
|
+
|
|
48
|
+
// @ts-expect-error ControlComponent props are inferred from the type (T).
|
|
49
|
+
return <ControlComponent { ...props } />;
|
|
50
|
+
};
|
|
@@ -1,15 +1,19 @@
|
|
|
1
|
-
import { SelectControl } from './control-types/select-control';
|
|
2
|
-
import { TextAreaControl } from './control-types/text-area-control';
|
|
3
|
-
import { TextControl } from './control-types/text-control';
|
|
4
1
|
import { ImageControl } from './control-types/image-control';
|
|
2
|
+
import { TextControl } from './control-types/text-control';
|
|
3
|
+
import { TextAreaControl } from './control-types/text-area-control';
|
|
4
|
+
import { SizeControl } from './control-types/size-control';
|
|
5
|
+
import { SelectControl } from './control-types/select-control';
|
|
5
6
|
|
|
6
|
-
const controlTypes = {
|
|
7
|
+
export const controlTypes = {
|
|
7
8
|
image: ImageControl,
|
|
8
|
-
select: SelectControl,
|
|
9
9
|
text: TextControl,
|
|
10
10
|
textarea: TextAreaControl,
|
|
11
|
-
|
|
11
|
+
size: SizeControl,
|
|
12
|
+
select: SelectControl,
|
|
13
|
+
} as const;
|
|
14
|
+
|
|
15
|
+
export type ControlTypes = typeof controlTypes;
|
|
16
|
+
|
|
17
|
+
export type ControlType = keyof ControlTypes;
|
|
12
18
|
|
|
13
|
-
export const getControlByType = ( type:
|
|
14
|
-
return controlTypes[ type as keyof typeof controlTypes ] ?? null;
|
|
15
|
-
};
|
|
19
|
+
export const getControlByType = ( type: ControlType ) => controlTypes[ type ];
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { ControlContext } from '
|
|
3
|
-
import { Stack, styled } from '@elementor/ui';
|
|
2
|
+
import { ControlContext } from './control-context';
|
|
4
3
|
import { PropKey, PropValue } from '../types';
|
|
5
4
|
import { useElementContext } from '../contexts/element-context';
|
|
6
5
|
import { useWidgetSettings } from '../hooks/use-widget-settings';
|
|
7
6
|
import { updateSettings } from '../sync/update-settings';
|
|
8
7
|
import { ControlLabel } from '../components/control-label';
|
|
8
|
+
import { ControlContainer } from './components/control-container';
|
|
9
9
|
|
|
10
10
|
type Props = {
|
|
11
11
|
bind: PropKey;
|
|
@@ -13,8 +13,11 @@ type Props = {
|
|
|
13
13
|
};
|
|
14
14
|
|
|
15
15
|
export const SettingsControlProvider = ( { bind, children }: Props ) => {
|
|
16
|
-
const { element } = useElementContext();
|
|
17
|
-
|
|
16
|
+
const { element, elementType } = useElementContext();
|
|
17
|
+
|
|
18
|
+
const defaultValue = elementType.propsSchema[ bind ]?.default;
|
|
19
|
+
const settingsValue = useWidgetSettings( { id: element.id, bind } );
|
|
20
|
+
const value = settingsValue ?? defaultValue ?? null;
|
|
18
21
|
|
|
19
22
|
const setValue = ( newValue: PropValue ) => {
|
|
20
23
|
updateSettings( {
|
|
@@ -30,26 +33,10 @@ export const SettingsControlProvider = ( { bind, children }: Props ) => {
|
|
|
30
33
|
|
|
31
34
|
const SettingsControl = ( { children, bind }: Props ) => (
|
|
32
35
|
<SettingsControlProvider bind={ bind }>
|
|
33
|
-
<
|
|
34
|
-
{ children }
|
|
35
|
-
</StyledStack>
|
|
36
|
+
<ControlContainer flexWrap="wrap">{ children }</ControlContainer>
|
|
36
37
|
</SettingsControlProvider>
|
|
37
38
|
);
|
|
38
39
|
|
|
39
|
-
const StyledStack = styled( Stack )( ( { theme } ) => {
|
|
40
|
-
const gap = theme.spacing( 1 );
|
|
41
|
-
|
|
42
|
-
return {
|
|
43
|
-
gap,
|
|
44
|
-
'& > *': {
|
|
45
|
-
width: `calc(50% - ${ gap } / 2)`,
|
|
46
|
-
},
|
|
47
|
-
'& > label': {
|
|
48
|
-
flexShrink: 0,
|
|
49
|
-
},
|
|
50
|
-
};
|
|
51
|
-
} );
|
|
52
|
-
|
|
53
40
|
// TODO: When we start using useControl inside the label component, we should create a new component for it,
|
|
54
41
|
// and keep ControlLabel as a simple label component without context.
|
|
55
42
|
SettingsControl.Label = ControlLabel;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { useMemo } from 'react';
|
|
2
|
+
import { useElementContext } from '../contexts/element-context';
|
|
3
|
+
import { PropKey } from '../types';
|
|
4
|
+
import { getAtomicDynamicTags } from '../sync/get-atomic-dynamic-tags';
|
|
5
|
+
|
|
6
|
+
export const useDynamicTagsConfig = ( propName: PropKey ) => {
|
|
7
|
+
let categories: string[] = [];
|
|
8
|
+
|
|
9
|
+
const { elementType } = useElementContext();
|
|
10
|
+
|
|
11
|
+
const propSchema = elementType.propsSchema?.[ propName ];
|
|
12
|
+
|
|
13
|
+
if ( propSchema ) {
|
|
14
|
+
const dynamicTags = getAtomicDynamicTags();
|
|
15
|
+
|
|
16
|
+
categories = dynamicTags?.propTypesToDynamic?.[ propSchema.type ] || [];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
20
|
+
return useMemo( () => getDynamicTagsByCategories( categories ), [ categories.join() ] );
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const getDynamicTagsByCategories = ( categories: string[] ) => {
|
|
24
|
+
const dynamicTags = getAtomicDynamicTags();
|
|
25
|
+
|
|
26
|
+
if ( ! categories.length || ! dynamicTags?.tags ) {
|
|
27
|
+
return [];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const _categories = new Set( categories );
|
|
31
|
+
|
|
32
|
+
return Object.values( dynamicTags.tags ).filter( ( dynamicTag ) =>
|
|
33
|
+
dynamicTag.categories.some( ( category ) => _categories.has( category ) )
|
|
34
|
+
);
|
|
35
|
+
};
|
|
@@ -17,9 +17,14 @@ export default function useElementType( type?: string ) {
|
|
|
17
17
|
return null;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
if ( ! elementType?.atomic_props_schema ) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
|
|
20
24
|
return {
|
|
21
25
|
key: type,
|
|
22
26
|
controls: elementType.atomic_controls,
|
|
27
|
+
propsSchema: elementType.atomic_props_schema,
|
|
23
28
|
title: elementType.title,
|
|
24
29
|
};
|
|
25
30
|
},
|
package/src/index.ts
CHANGED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { getElementorConfig } from './get-elementor-config';
|
|
2
|
+
|
|
3
|
+
export const getAtomicDynamicTags = () => {
|
|
4
|
+
const { atomicDynamicTags } = getElementorConfig();
|
|
5
|
+
|
|
6
|
+
if ( ! atomicDynamicTags ) {
|
|
7
|
+
return null;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
return {
|
|
11
|
+
tags: atomicDynamicTags.tags,
|
|
12
|
+
propTypesToDynamic: atomicDynamicTags.prop_types_to_dynamic,
|
|
13
|
+
};
|
|
14
|
+
};
|
package/src/sync/types.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ControlItem, PropValue } from '../types';
|
|
1
|
+
import { ControlItem, PropsSchema, PropValue } from '../types';
|
|
2
2
|
import { StyleDefinition, StyleDefinitionID } from '@elementor/editor-style';
|
|
3
3
|
|
|
4
4
|
export type ExtendedWindow = Window & {
|
|
@@ -10,14 +10,28 @@ export type ExtendedWindow = Window & {
|
|
|
10
10
|
string,
|
|
11
11
|
{
|
|
12
12
|
atomic_controls?: ControlItem[];
|
|
13
|
+
atomic_props_schema?: PropsSchema;
|
|
13
14
|
controls: object;
|
|
14
15
|
title: string;
|
|
15
16
|
}
|
|
16
17
|
>;
|
|
17
18
|
getContainer?: ( id: string ) => V1Element;
|
|
19
|
+
config?: {
|
|
20
|
+
atomicDynamicTags?: {
|
|
21
|
+
tags: DynamicTags;
|
|
22
|
+
prop_types_to_dynamic: Record< string, string[] >;
|
|
23
|
+
};
|
|
24
|
+
};
|
|
18
25
|
};
|
|
19
26
|
};
|
|
20
27
|
|
|
28
|
+
export type DynamicTags = Record< DynamicTag[ 'name' ], DynamicTag >;
|
|
29
|
+
|
|
30
|
+
export type DynamicTag = {
|
|
31
|
+
name: string;
|
|
32
|
+
categories: string[];
|
|
33
|
+
};
|
|
34
|
+
|
|
21
35
|
export type V1Element = {
|
|
22
36
|
model: V1Model< V1ElementModelProps >;
|
|
23
37
|
settings?: V1Model< V1ElementSettingsProps >;
|
package/src/types.ts
CHANGED
|
@@ -8,6 +8,7 @@ export type Element = {
|
|
|
8
8
|
export type ElementType = {
|
|
9
9
|
key: string;
|
|
10
10
|
controls: ControlItem[];
|
|
11
|
+
propsSchema: PropsSchema;
|
|
11
12
|
title: string;
|
|
12
13
|
};
|
|
13
14
|
|
|
@@ -33,6 +34,19 @@ export type Control = {
|
|
|
33
34
|
|
|
34
35
|
export type ControlItem = ControlsSection | Control;
|
|
35
36
|
|
|
37
|
+
export type Constraint = {
|
|
38
|
+
type: string;
|
|
39
|
+
value: unknown;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export type PropDefinition = {
|
|
43
|
+
type: string;
|
|
44
|
+
default: PropValue;
|
|
45
|
+
constraints?: Constraint[];
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export type PropsSchema = Record< Control[ 'value' ][ 'bind' ], PropDefinition >;
|
|
49
|
+
|
|
36
50
|
type MaybeArray< T > = T | T[];
|
|
37
51
|
|
|
38
52
|
export type TransformablePropValue< T = unknown > = {
|