@elementor/editor-editing-panel 0.4.1 → 0.5.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 +12 -22
- package/dist/index.js +98 -88
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +97 -87
- package/dist/index.mjs.map +1 -1
- package/package.json +43 -43
- package/src/__tests__/utils.ts +2 -2
- package/src/components/__tests__/editing-panel.test.tsx +2 -6
- package/src/components/controls/__tests__/settings-control.test.tsx +19 -15
- package/src/components/controls/control-types/__tests__/select-control.test.tsx +1 -1
- package/src/components/controls/control-types/__tests__/{text-control.test.tsx → text-area-control.test.tsx} +4 -4
- package/src/components/controls/control-types/select-control.tsx +9 -9
- package/src/components/controls/control-types/text-area-control.tsx +25 -0
- package/src/components/controls/settings-control.tsx +12 -32
- package/src/components/editing-panel.tsx +23 -24
- package/src/contexts/control-context.tsx +45 -0
- package/src/contexts/{settings-controls.tsx → element-context.tsx} +4 -8
- package/src/hooks/__tests__/use-widget-settings.test.ts +3 -1
- package/src/hooks/use-open-editor-panel.ts +4 -7
- package/src/hooks/use-selected-elements.ts +1 -4
- package/src/panel.ts +1 -5
- package/src/sync/get-selected-elements.ts +1 -1
- package/src/sync/types.ts +20 -17
- package/src/sync/update-settings.ts +1 -1
- package/src/types.ts +17 -17
- package/src/components/controls/control-context.ts +0 -20
- package/src/components/controls/control-types/text-control.tsx +0 -19
- /package/src/{components/controls → contexts}/__tests__/control-context.test.tsx +0 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { TextareaAutosize } from '@elementor/ui';
|
|
3
|
+
import { useControl } from '../../../contexts/control-context';
|
|
4
|
+
|
|
5
|
+
type Props = {
|
|
6
|
+
placeholder?: string;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export const TextAreaControl = ( { placeholder }: Props ) => {
|
|
10
|
+
const { value, setValue } = useControl< string >( '' );
|
|
11
|
+
|
|
12
|
+
const handleChange = ( event: React.ChangeEvent< HTMLInputElement > ) => {
|
|
13
|
+
setValue( event.target.value );
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<TextareaAutosize
|
|
18
|
+
size="tiny"
|
|
19
|
+
minRows={ 3 }
|
|
20
|
+
value={ value }
|
|
21
|
+
onChange={ handleChange }
|
|
22
|
+
placeholder={ placeholder }
|
|
23
|
+
/>
|
|
24
|
+
);
|
|
25
|
+
};
|
|
@@ -1,40 +1,14 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { ControlContextProvider } from '../../contexts/control-context';
|
|
3
3
|
import { Stack, Typography } from '@elementor/ui';
|
|
4
|
-
import {
|
|
5
|
-
import { useWidgetSettings } from '../../hooks/use-widget-settings';
|
|
6
|
-
import { PropKey, PropValue } from '../../types';
|
|
4
|
+
import { PropKey } from '../../types';
|
|
7
5
|
|
|
8
6
|
type Props = {
|
|
9
7
|
bind: PropKey;
|
|
10
8
|
children: React.ReactNode;
|
|
11
|
-
elementID: Element['id'];
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const SettingsControl = ( { bind, children, elementID }: Props ) => {
|
|
15
|
-
const value = useWidgetSettings( { id: elementID, bind } );
|
|
16
|
-
|
|
17
|
-
const setValue = ( newValue: PropValue ) => {
|
|
18
|
-
updateSettings( {
|
|
19
|
-
id: elementID,
|
|
20
|
-
props: {
|
|
21
|
-
[ bind ]: newValue,
|
|
22
|
-
},
|
|
23
|
-
} );
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
return (
|
|
27
|
-
<ControlContext.Provider value={ { setValue, value, bind } }>
|
|
28
|
-
{ children }
|
|
29
|
-
</ControlContext.Provider>
|
|
30
|
-
);
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
const Label = ( { children }: { children: React.ReactNode } ) => {
|
|
34
|
-
return <Typography component="label" variant="caption">{ children }</Typography>;
|
|
35
9
|
};
|
|
36
10
|
|
|
37
|
-
const
|
|
11
|
+
const SettingsControl = ( { children, bind }: Props ) => (
|
|
38
12
|
<Stack
|
|
39
13
|
spacing={ 1 }
|
|
40
14
|
flexDirection="row"
|
|
@@ -43,11 +17,17 @@ const Container = ( { children }: { children: React.ReactNode} ) => (
|
|
|
43
17
|
flexWrap="wrap"
|
|
44
18
|
sx={ { px: 2 } }
|
|
45
19
|
>
|
|
46
|
-
{ children }
|
|
20
|
+
<ControlContextProvider bind={ bind }>{ children }</ControlContextProvider>
|
|
47
21
|
</Stack>
|
|
48
22
|
);
|
|
49
23
|
|
|
50
|
-
|
|
51
|
-
|
|
24
|
+
const Label = ( { children }: { children: React.ReactNode } ) => {
|
|
25
|
+
return (
|
|
26
|
+
<Typography component="label" variant="caption">
|
|
27
|
+
{ children }
|
|
28
|
+
</Typography>
|
|
29
|
+
);
|
|
30
|
+
};
|
|
52
31
|
|
|
32
|
+
SettingsControl.Label = Label;
|
|
53
33
|
export { SettingsControl };
|
|
@@ -4,20 +4,20 @@ import useSelectedElements from '../hooks/use-selected-elements';
|
|
|
4
4
|
import useElementType from '../hooks/use-element-type';
|
|
5
5
|
import { Panel, PanelBody, PanelHeader, PanelHeaderTitle } from '@elementor/editor-panels';
|
|
6
6
|
import { SelectControl } from './controls/control-types/select-control';
|
|
7
|
-
import {
|
|
7
|
+
import { TextAreaControl } from './controls/control-types/text-area-control';
|
|
8
8
|
import { SettingsControl } from '../components/controls/settings-control';
|
|
9
9
|
import { Stack } from '@elementor/ui';
|
|
10
|
-
import { ElementContext } from '../contexts/
|
|
10
|
+
import { ElementContext } from '../contexts/element-context';
|
|
11
11
|
|
|
12
12
|
const controlTypes = {
|
|
13
13
|
select: SelectControl,
|
|
14
|
-
|
|
14
|
+
textarea: TextAreaControl,
|
|
15
15
|
};
|
|
16
16
|
|
|
17
17
|
export const EditingPanel = () => {
|
|
18
18
|
const elements = useSelectedElements();
|
|
19
19
|
|
|
20
|
-
const selectedElement = elements
|
|
20
|
+
const [ selectedElement ] = elements;
|
|
21
21
|
|
|
22
22
|
const elementType = useElementType( selectedElement?.type );
|
|
23
23
|
|
|
@@ -36,29 +36,28 @@ export const EditingPanel = () => {
|
|
|
36
36
|
<PanelBody>
|
|
37
37
|
<ElementContext element={ selectedElement }>
|
|
38
38
|
<Stack spacing={ 2 }>
|
|
39
|
-
{
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
{ elementType.controls.map( ( control ) => {
|
|
40
|
+
if ( control.type === 'control' ) {
|
|
41
|
+
const ControlComponent =
|
|
42
|
+
controlTypes[ control.value.type as keyof typeof controlTypes ];
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
return (
|
|
49
|
-
<SettingsControl key={ control.value.bind } bind={ control.value.bind } elementID={ elements[ 0 ].id }>
|
|
50
|
-
<SettingsControl.Container>
|
|
51
|
-
<SettingsControl.Label>{ control.value.label }</SettingsControl.Label>
|
|
52
|
-
{ /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ }
|
|
53
|
-
<ControlComponent { ...control.value.props as any } />
|
|
54
|
-
</SettingsControl.Container>
|
|
55
|
-
</SettingsControl>
|
|
56
|
-
);
|
|
44
|
+
if ( ! ControlComponent ) {
|
|
45
|
+
return null;
|
|
57
46
|
}
|
|
58
47
|
|
|
59
|
-
return
|
|
60
|
-
|
|
61
|
-
|
|
48
|
+
return (
|
|
49
|
+
<SettingsControl key={ control.value.bind } bind={ control.value.bind }>
|
|
50
|
+
<SettingsControl.Label>{ control.value.label }</SettingsControl.Label>
|
|
51
|
+
<ControlComponent
|
|
52
|
+
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
|
|
53
|
+
{ ...( control.value.props as any ) }
|
|
54
|
+
/>
|
|
55
|
+
</SettingsControl>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return null;
|
|
60
|
+
} ) }
|
|
62
61
|
</Stack>
|
|
63
62
|
</ElementContext>
|
|
64
63
|
</PanelBody>
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { createContext, useContext } from 'react';
|
|
3
|
+
import { PropKey, PropValue } from '../types';
|
|
4
|
+
import { useElementContext } from './element-context';
|
|
5
|
+
import { useWidgetSettings } from '../hooks/use-widget-settings';
|
|
6
|
+
import { updateSettings } from '../sync/update-settings';
|
|
7
|
+
|
|
8
|
+
export type ControlContext< T extends PropValue > = null | {
|
|
9
|
+
bind: PropKey;
|
|
10
|
+
setValue: ( value: T ) => void;
|
|
11
|
+
value: T;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export const ControlContext = createContext< ControlContext< PropValue > >( null );
|
|
15
|
+
|
|
16
|
+
export function useControl< T extends PropValue >( defaultValue?: T ) {
|
|
17
|
+
const controlContext = useContext< ControlContext< T > >( ControlContext as never );
|
|
18
|
+
|
|
19
|
+
if ( ! controlContext ) {
|
|
20
|
+
throw new Error( 'useControl must be used within a ControlContext' );
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return { ...controlContext, value: controlContext.value ?? defaultValue };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
type Props = {
|
|
27
|
+
bind: PropKey;
|
|
28
|
+
children: React.ReactNode;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export const ControlContextProvider = ( { bind, children }: Props ) => {
|
|
32
|
+
const { element } = useElementContext();
|
|
33
|
+
const value = useWidgetSettings( { id: element.id, bind } );
|
|
34
|
+
|
|
35
|
+
const setValue = ( newValue: PropValue ) => {
|
|
36
|
+
updateSettings( {
|
|
37
|
+
id: element.id,
|
|
38
|
+
props: {
|
|
39
|
+
[ bind ]: newValue,
|
|
40
|
+
},
|
|
41
|
+
} );
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
return <ControlContext.Provider value={ { setValue, value, bind } }>{ children }</ControlContext.Provider>;
|
|
45
|
+
};
|
|
@@ -4,21 +4,17 @@ import { Element } from '../types';
|
|
|
4
4
|
|
|
5
5
|
type ContextValue = {
|
|
6
6
|
element: Element;
|
|
7
|
-
}
|
|
7
|
+
};
|
|
8
8
|
|
|
9
|
-
const Context = createContext<ContextValue | null>( null );
|
|
9
|
+
const Context = createContext< ContextValue | null >( null );
|
|
10
10
|
|
|
11
11
|
type Props = {
|
|
12
12
|
element: Element;
|
|
13
13
|
children?: ReactNode;
|
|
14
|
-
}
|
|
14
|
+
};
|
|
15
15
|
|
|
16
16
|
export function ElementContext( { children, element }: Props ) {
|
|
17
|
-
return
|
|
18
|
-
<Context.Provider value={ { element } }>
|
|
19
|
-
{ children }
|
|
20
|
-
</Context.Provider>
|
|
21
|
-
);
|
|
17
|
+
return <Context.Provider value={ { element } }>{ children }</Context.Provider>;
|
|
22
18
|
}
|
|
23
19
|
|
|
24
20
|
export function useElementContext() {
|
|
@@ -71,7 +71,9 @@ describe( 'useWidgetSettings', () => {
|
|
|
71
71
|
|
|
72
72
|
// Act.
|
|
73
73
|
act( () => {
|
|
74
|
-
jest.mocked( getContainer ).mockReturnValue(
|
|
74
|
+
jest.mocked( getContainer ).mockReturnValue(
|
|
75
|
+
mockV1Element( { settings: { [ bind ]: 'Goodbye, World!' } } )
|
|
76
|
+
);
|
|
75
77
|
dispatchCommandAfter( 'document/elements/settings' );
|
|
76
78
|
} );
|
|
77
79
|
|
|
@@ -7,13 +7,10 @@ export const useOpenEditorPanel = () => {
|
|
|
7
7
|
const { open } = usePanelActions();
|
|
8
8
|
|
|
9
9
|
useEffect( () => {
|
|
10
|
-
return listenTo(
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
if ( shouldUseV2Panel() ) {
|
|
14
|
-
open();
|
|
15
|
-
}
|
|
10
|
+
return listenTo( commandStartEvent( 'panel/editor/open' ), () => {
|
|
11
|
+
if ( shouldUseV2Panel() ) {
|
|
12
|
+
open();
|
|
16
13
|
}
|
|
17
|
-
);
|
|
14
|
+
} );
|
|
18
15
|
}, [] ); // eslint-disable-line react-hooks/exhaustive-deps
|
|
19
16
|
};
|
|
@@ -3,10 +3,7 @@ import getSelectedElements from '../sync/get-selected-elements';
|
|
|
3
3
|
|
|
4
4
|
export default function useSelectedElements() {
|
|
5
5
|
return useListenTo(
|
|
6
|
-
[
|
|
7
|
-
commandEndEvent( 'document/elements/select' ),
|
|
8
|
-
commandEndEvent( 'document/elements/deselect' ),
|
|
9
|
-
],
|
|
6
|
+
[ commandEndEvent( 'document/elements/select' ), commandEndEvent( 'document/elements/deselect' ) ],
|
|
10
7
|
() => getSelectedElements()
|
|
11
8
|
);
|
|
12
9
|
}
|
package/src/panel.ts
CHANGED
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
import { __createPanel as createPanel } from '@elementor/editor-panels';
|
|
2
2
|
import { EditingPanel } from './components/editing-panel';
|
|
3
3
|
|
|
4
|
-
export const {
|
|
5
|
-
panel,
|
|
6
|
-
usePanelActions,
|
|
7
|
-
usePanelStatus,
|
|
8
|
-
} = createPanel( {
|
|
4
|
+
export const { panel, usePanelActions, usePanelStatus } = createPanel( {
|
|
9
5
|
id: 'editing-panel',
|
|
10
6
|
component: EditingPanel,
|
|
11
7
|
} );
|
|
@@ -6,7 +6,7 @@ export default function getSelectedElements(): Element[] {
|
|
|
6
6
|
|
|
7
7
|
const selectedElements = extendedWindow.elementor?.selection?.getElements?.() ?? [];
|
|
8
8
|
|
|
9
|
-
return selectedElements.reduce<Element[]>( ( acc, el ) => {
|
|
9
|
+
return selectedElements.reduce< Element[] >( ( acc, el ) => {
|
|
10
10
|
const type = el.model.get( 'widgetType' ) || el.model.get( 'elType' );
|
|
11
11
|
|
|
12
12
|
if ( type ) {
|
package/src/sync/types.ts
CHANGED
|
@@ -4,29 +4,32 @@ export type ExtendedWindow = Window & {
|
|
|
4
4
|
elementor?: {
|
|
5
5
|
selection?: {
|
|
6
6
|
getElements: () => V1Element[];
|
|
7
|
-
}
|
|
8
|
-
widgetsCache?: Record<
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
7
|
+
};
|
|
8
|
+
widgetsCache?: Record<
|
|
9
|
+
string,
|
|
10
|
+
{
|
|
11
|
+
atomic_controls?: AtomicControl[];
|
|
12
|
+
controls: object;
|
|
13
|
+
title: string;
|
|
14
|
+
}
|
|
15
|
+
>;
|
|
16
|
+
getContainer?: ( id: string ) => V1Element;
|
|
17
|
+
};
|
|
18
|
+
};
|
|
16
19
|
|
|
17
20
|
export type V1Element = {
|
|
18
|
-
model: V1Model<V1ElementModelProps
|
|
19
|
-
settings?: V1Model<V1ElementSettingsProps
|
|
20
|
-
}
|
|
21
|
+
model: V1Model< V1ElementModelProps >;
|
|
22
|
+
settings?: V1Model< V1ElementSettingsProps >;
|
|
23
|
+
};
|
|
21
24
|
|
|
22
25
|
export type V1ElementModelProps = {
|
|
23
26
|
widgetType?: string;
|
|
24
27
|
elType: string;
|
|
25
28
|
id: string;
|
|
26
|
-
}
|
|
29
|
+
};
|
|
27
30
|
|
|
28
|
-
export type V1ElementSettingsProps = Record<string, PropValue>;
|
|
31
|
+
export type V1ElementSettingsProps = Record< string, PropValue >;
|
|
29
32
|
|
|
30
|
-
type V1Model<T> = {
|
|
31
|
-
get: <K extends keyof T>( key: K ) => T[K]
|
|
32
|
-
}
|
|
33
|
+
type V1Model< T > = {
|
|
34
|
+
get: < K extends keyof T >( key: K ) => T[ K ];
|
|
35
|
+
};
|
|
@@ -2,7 +2,7 @@ import { __privateRunCommand as runCommand } from '@elementor/editor-v1-adapters
|
|
|
2
2
|
import { Props } from '../types';
|
|
3
3
|
import getContainer from './get-container';
|
|
4
4
|
|
|
5
|
-
export const updateSettings = ( { id, props }: { id: string
|
|
5
|
+
export const updateSettings = ( { id, props }: { id: string; props: Props } ) => {
|
|
6
6
|
const container = getContainer( id );
|
|
7
7
|
|
|
8
8
|
runCommand( 'document/elements/settings', {
|
package/src/types.ts
CHANGED
|
@@ -1,38 +1,38 @@
|
|
|
1
1
|
export type Element = {
|
|
2
2
|
id: string;
|
|
3
3
|
type: string;
|
|
4
|
-
}
|
|
4
|
+
};
|
|
5
5
|
|
|
6
6
|
export type ElementType = {
|
|
7
7
|
key: string;
|
|
8
8
|
controls: AtomicControl[];
|
|
9
9
|
title: string;
|
|
10
|
-
}
|
|
10
|
+
};
|
|
11
11
|
|
|
12
12
|
export type AtomicControl = {
|
|
13
|
-
type: 'control'
|
|
13
|
+
type: 'control';
|
|
14
14
|
value: {
|
|
15
|
-
bind: string
|
|
16
|
-
label: string
|
|
17
|
-
description?: string
|
|
18
|
-
type: string
|
|
19
|
-
props: Record<string, unknown
|
|
20
|
-
}
|
|
21
|
-
}
|
|
15
|
+
bind: string;
|
|
16
|
+
label: string;
|
|
17
|
+
description?: string;
|
|
18
|
+
type: string;
|
|
19
|
+
props: Record< string, unknown >;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
22
|
|
|
23
|
-
type MaybeArray<T> = T | T[];
|
|
23
|
+
type MaybeArray< T > = T | T[];
|
|
24
24
|
|
|
25
25
|
type TransformablePropValue = {
|
|
26
|
-
$$type: string
|
|
27
|
-
value: unknown
|
|
28
|
-
}
|
|
26
|
+
$$type: string;
|
|
27
|
+
value: unknown;
|
|
28
|
+
};
|
|
29
29
|
|
|
30
|
-
export type PlainPropValue = MaybeArray<string | number | boolean | object | null | undefined>;
|
|
30
|
+
export type PlainPropValue = MaybeArray< string | number | boolean | object | null | undefined >;
|
|
31
31
|
|
|
32
32
|
export type PropValue = PlainPropValue | TransformablePropValue;
|
|
33
33
|
|
|
34
34
|
export type PropKey = string;
|
|
35
35
|
|
|
36
|
-
export type Props = Record<PropKey, PropValue>;
|
|
36
|
+
export type Props = Record< PropKey, PropValue >;
|
|
37
37
|
|
|
38
|
-
export type PlainProps = Record<PropKey, PlainPropValue>;
|
|
38
|
+
export type PlainProps = Record< PropKey, PlainPropValue >;
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { createContext, useContext } from 'react';
|
|
2
|
-
import { PropKey, PropValue } from '../../types';
|
|
3
|
-
|
|
4
|
-
export type ControlContext<T extends PropValue> = null | {
|
|
5
|
-
bind: PropKey;
|
|
6
|
-
setValue: ( value: T ) => void;
|
|
7
|
-
value: T;
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
export const ControlContext = createContext<ControlContext<PropValue>>( null );
|
|
11
|
-
|
|
12
|
-
export function useControl<T extends PropValue>( defaultValue?: T ) {
|
|
13
|
-
const controlContext = useContext<ControlContext<T>>( ControlContext as never );
|
|
14
|
-
|
|
15
|
-
if ( ! controlContext ) {
|
|
16
|
-
throw new Error( 'useControl must be used within a ControlContext' );
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
return { ...controlContext, value: controlContext.value ?? defaultValue };
|
|
20
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import * as React from 'react';
|
|
2
|
-
import { TextareaAutosize } from '@elementor/ui';
|
|
3
|
-
import { useControl } from '../control-context';
|
|
4
|
-
|
|
5
|
-
export type TextControlProps = {
|
|
6
|
-
placeholder?: string;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export const TextControl = ( { placeholder }: TextControlProps ) => {
|
|
10
|
-
const { value, setValue } = useControl<string>( '' );
|
|
11
|
-
|
|
12
|
-
const handleChange = ( event: React.ChangeEvent<HTMLInputElement> ) => {
|
|
13
|
-
setValue( event.target.value );
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
return (
|
|
17
|
-
<TextareaAutosize size="tiny" minRows={ 3 }value={ value } onChange={ handleChange } placeholder={ placeholder } />
|
|
18
|
-
);
|
|
19
|
-
};
|
|
File without changes
|