@elementor/editor-controls 1.0.0 → 1.2.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 +60 -0
- package/dist/index.d.mts +78 -41
- package/dist/index.d.ts +78 -41
- package/dist/index.js +875 -617
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +713 -467
- package/dist/index.mjs.map +1 -1
- package/package.json +11 -11
- package/src/components/font-family-selector.tsx +50 -174
- package/src/components/popover-content.tsx +3 -11
- package/src/components/repeater.tsx +27 -11
- package/src/components/text-field-popover.tsx +3 -3
- package/src/controls/aspect-ratio-control.tsx +20 -2
- package/src/controls/background-control/background-overlay/background-image-overlay/background-image-overlay-position.tsx +2 -2
- package/src/controls/background-control/background-overlay/background-image-overlay/background-image-overlay-size.tsx +2 -2
- package/src/controls/background-control/background-overlay/background-overlay-repeater-control.tsx +9 -4
- package/src/controls/box-shadow-repeater-control.tsx +2 -2
- package/src/controls/equal-unequal-sizes-control.tsx +3 -9
- package/src/controls/filter-repeater-control.tsx +186 -0
- package/src/controls/font-family-control/font-family-control.tsx +6 -2
- package/src/controls/gap-control.tsx +3 -3
- package/src/controls/image-control.tsx +22 -35
- package/src/controls/key-value-control.tsx +119 -0
- package/src/controls/link-control.tsx +3 -1
- package/src/controls/linked-dimensions-control.tsx +3 -3
- package/src/controls/number-control.tsx +3 -3
- package/src/controls/position-control.tsx +109 -0
- package/src/controls/repeatable-control.tsx +119 -0
- package/src/controls/size-control.tsx +11 -9
- package/src/controls/stroke-control.tsx +2 -2
- package/src/controls/svg-media-control.tsx +0 -2
- package/src/hooks/use-repeatable-control-context.ts +24 -0
- package/src/index.ts +6 -1
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { useMemo } from 'react';
|
|
3
|
+
import { createArrayPropUtils, type PropKey } from '@elementor/editor-props';
|
|
4
|
+
import { Box } from '@elementor/ui';
|
|
5
|
+
|
|
6
|
+
/* eslint-disable */
|
|
7
|
+
import { PropKeyProvider, PropProvider, useBoundProp } from '../bound-prop-context';
|
|
8
|
+
import { PopoverContent } from '../components/popover-content';
|
|
9
|
+
import { PopoverGridContainer } from '../components/popover-grid-container';
|
|
10
|
+
import { Repeater } from '../components/repeater';
|
|
11
|
+
import { createControl } from '../create-control';
|
|
12
|
+
import {
|
|
13
|
+
type ChildControlConfig,
|
|
14
|
+
RepeatableControlContext,
|
|
15
|
+
useRepeatableControlContext,
|
|
16
|
+
} from '../hooks/use-repeatable-control-context';
|
|
17
|
+
|
|
18
|
+
type RepeatableControlProps = {
|
|
19
|
+
label: string;
|
|
20
|
+
repeaterLabel: string;
|
|
21
|
+
childControlConfig: ChildControlConfig;
|
|
22
|
+
showDuplicate?: boolean;
|
|
23
|
+
showToggle?: boolean;
|
|
24
|
+
initialValues?: object;
|
|
25
|
+
patternLabel?: string;
|
|
26
|
+
placeholder?: string;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const RepeatableControl = createControl(
|
|
30
|
+
( {
|
|
31
|
+
repeaterLabel,
|
|
32
|
+
childControlConfig,
|
|
33
|
+
showDuplicate,
|
|
34
|
+
showToggle,
|
|
35
|
+
initialValues,
|
|
36
|
+
patternLabel,
|
|
37
|
+
placeholder,
|
|
38
|
+
}: RepeatableControlProps ) => {
|
|
39
|
+
const { propTypeUtil: childPropTypeUtil } = childControlConfig;
|
|
40
|
+
|
|
41
|
+
if ( ! childPropTypeUtil ) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const childArrayPropTypeUtil = useMemo(
|
|
46
|
+
() => createArrayPropUtils( childPropTypeUtil.key, childPropTypeUtil.schema ),
|
|
47
|
+
[ childPropTypeUtil.key, childPropTypeUtil.schema ]
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
const { propType, value, setValue } = useBoundProp( childArrayPropTypeUtil );
|
|
51
|
+
const ItemLabel = ( { value: itemValue }: { value: any } ) => {
|
|
52
|
+
const pattern = patternLabel || '';
|
|
53
|
+
const finalLabel = interpolate( pattern, itemValue.value );
|
|
54
|
+
|
|
55
|
+
if ( finalLabel ) {
|
|
56
|
+
return <span>{ finalLabel }</span>;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<Box component="span" color="text.tertiary">
|
|
61
|
+
{ placeholder }
|
|
62
|
+
</Box>
|
|
63
|
+
);
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
return (
|
|
67
|
+
<PropProvider propType={ propType } value={ value } setValue={ setValue }>
|
|
68
|
+
<RepeatableControlContext.Provider value={ childControlConfig }>
|
|
69
|
+
<Repeater
|
|
70
|
+
openOnAdd
|
|
71
|
+
values={ value ?? [] }
|
|
72
|
+
setValues={ setValue }
|
|
73
|
+
label={ repeaterLabel }
|
|
74
|
+
isSortable={ false }
|
|
75
|
+
itemSettings={ {
|
|
76
|
+
Icon: ItemIcon,
|
|
77
|
+
Label: ItemLabel,
|
|
78
|
+
Content: ItemContent,
|
|
79
|
+
initialValues: childPropTypeUtil.create( initialValues || null ),
|
|
80
|
+
} }
|
|
81
|
+
showDuplicate={ showDuplicate }
|
|
82
|
+
showToggle={ showToggle }
|
|
83
|
+
/>
|
|
84
|
+
</RepeatableControlContext.Provider>
|
|
85
|
+
</PropProvider>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
const ItemContent = ( { bind }: { bind: PropKey } ) => {
|
|
91
|
+
return (
|
|
92
|
+
<PropKeyProvider bind={ bind }>
|
|
93
|
+
<Content />
|
|
94
|
+
</PropKeyProvider>
|
|
95
|
+
);
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
// TODO: Configurable icon probably can be somehow part of the injected control and bubbled up to the repeater
|
|
99
|
+
const ItemIcon = () => <></>;
|
|
100
|
+
|
|
101
|
+
const Content = () => {
|
|
102
|
+
const { component: ChildControl, props = {} } = useRepeatableControlContext();
|
|
103
|
+
|
|
104
|
+
return (
|
|
105
|
+
<PopoverContent p={ 1.5 }>
|
|
106
|
+
<PopoverGridContainer>
|
|
107
|
+
<ChildControl { ...props } />
|
|
108
|
+
</PopoverGridContainer>
|
|
109
|
+
</PopoverContent>
|
|
110
|
+
);
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const interpolate = ( template: string, data: object ) => {
|
|
114
|
+
//remove it to be generic
|
|
115
|
+
if ( Object.values( data ).some( ( value ) => value.value === '' || value === '' ) ) {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
return new Function( ...Object.keys( data ), `return \`${ template }\`;` )( ...Object.values( data ) );
|
|
119
|
+
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { type
|
|
2
|
+
import { type RefObject, useEffect, useState } from 'react';
|
|
3
3
|
import { sizePropTypeUtil, type SizePropValue } from '@elementor/editor-props';
|
|
4
4
|
import { useActiveBreakpoint } from '@elementor/editor-responsive';
|
|
5
5
|
import { usePopupState } from '@elementor/ui';
|
|
@@ -23,7 +23,8 @@ type SizeControlProps = {
|
|
|
23
23
|
units?: Unit[];
|
|
24
24
|
extendedOptions?: ExtendedOption[];
|
|
25
25
|
disableCustom?: boolean;
|
|
26
|
-
anchorRef?:
|
|
26
|
+
anchorRef?: RefObject< HTMLDivElement | null >;
|
|
27
|
+
defaultUnit?: Unit;
|
|
27
28
|
};
|
|
28
29
|
|
|
29
30
|
type State = {
|
|
@@ -33,9 +34,10 @@ type State = {
|
|
|
33
34
|
};
|
|
34
35
|
|
|
35
36
|
export const SizeControl = createControl( ( props: SizeControlProps ) => {
|
|
37
|
+
const defaultUnit = props.defaultUnit ?? DEFAULT_UNIT;
|
|
36
38
|
const { units = [ ...defaultUnits ], placeholder, startIcon, anchorRef } = props;
|
|
37
39
|
const { value: sizeValue, setValue: setSizeValue, disabled, restoreValue } = useBoundProp( sizePropTypeUtil );
|
|
38
|
-
const [ internalState, setInternalState ] = useState( createStateFromSizeProp( sizeValue ) );
|
|
40
|
+
const [ internalState, setInternalState ] = useState( createStateFromSizeProp( sizeValue, defaultUnit ) );
|
|
39
41
|
const activeBreakpoint = useActiveBreakpoint();
|
|
40
42
|
|
|
41
43
|
const extendedOptions = useSizeExtendedOptions( props.extendedOptions || [], props.disableCustom ?? false );
|
|
@@ -56,7 +58,7 @@ export const SizeControl = createControl( ( props: SizeControlProps ) => {
|
|
|
56
58
|
return !! newState?.numeric || newState?.numeric === 0;
|
|
57
59
|
},
|
|
58
60
|
fallback: ( newState ) => ( {
|
|
59
|
-
unit: newState?.unit ?? DEFAULT_UNIT,
|
|
61
|
+
unit: newState?.unit ?? props.defaultUnit ?? DEFAULT_UNIT,
|
|
60
62
|
numeric: newState?.numeric ?? DEFAULT_SIZE,
|
|
61
63
|
custom: newState?.custom ?? '',
|
|
62
64
|
} ),
|
|
@@ -101,7 +103,7 @@ export const SizeControl = createControl( ( props: SizeControlProps ) => {
|
|
|
101
103
|
};
|
|
102
104
|
|
|
103
105
|
useEffect( () => {
|
|
104
|
-
const newState = createStateFromSizeProp( sizeValue );
|
|
106
|
+
const newState = createStateFromSizeProp( sizeValue, defaultUnit );
|
|
105
107
|
const currentUnit = isUnitExtendedOption( state.unit ) ? 'custom' : 'numeric';
|
|
106
108
|
const mergedStates = { ...state, [ currentUnit ]: newState[ currentUnit ] };
|
|
107
109
|
|
|
@@ -120,7 +122,7 @@ export const SizeControl = createControl( ( props: SizeControlProps ) => {
|
|
|
120
122
|
}, [ sizeValue ] );
|
|
121
123
|
|
|
122
124
|
useEffect( () => {
|
|
123
|
-
const newState = createStateFromSizeProp( sizeValue );
|
|
125
|
+
const newState = createStateFromSizeProp( sizeValue, defaultUnit );
|
|
124
126
|
|
|
125
127
|
if ( activeBreakpoint && ! areStatesEqual( newState, state ) ) {
|
|
126
128
|
setState( newState );
|
|
@@ -147,7 +149,7 @@ export const SizeControl = createControl( ( props: SizeControlProps ) => {
|
|
|
147
149
|
{ anchorRef?.current && (
|
|
148
150
|
<TextFieldPopover
|
|
149
151
|
popupState={ popupState }
|
|
150
|
-
anchorRef={ anchorRef
|
|
152
|
+
anchorRef={ anchorRef }
|
|
151
153
|
restoreValue={ restoreValue }
|
|
152
154
|
value={ controlSize as string }
|
|
153
155
|
onChange={ handleSizeChange }
|
|
@@ -165,8 +167,8 @@ function formatSize< TSize extends string | number >( size: TSize, unit: Unit |
|
|
|
165
167
|
return size || size === 0 ? ( Number( size ) as TSize ) : ( NaN as TSize );
|
|
166
168
|
}
|
|
167
169
|
|
|
168
|
-
function createStateFromSizeProp( sizeValue: SizeValue | null ): State {
|
|
169
|
-
const unit = sizeValue?.unit ??
|
|
170
|
+
function createStateFromSizeProp( sizeValue: SizeValue | null, defaultUnit: Unit ): State {
|
|
171
|
+
const unit = sizeValue?.unit ?? defaultUnit;
|
|
170
172
|
const size = sizeValue?.size ?? '';
|
|
171
173
|
|
|
172
174
|
return {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { forwardRef,
|
|
2
|
+
import { forwardRef, useRef } from 'react';
|
|
3
3
|
import { strokePropTypeUtil } from '@elementor/editor-props';
|
|
4
4
|
import { Grid } from '@elementor/ui';
|
|
5
5
|
import { __ } from '@wordpress/i18n';
|
|
@@ -22,7 +22,7 @@ const units: Unit[] = [ 'px', 'em', 'rem' ];
|
|
|
22
22
|
|
|
23
23
|
export const StrokeControl = createControl( () => {
|
|
24
24
|
const propContext = useBoundProp( strokePropTypeUtil );
|
|
25
|
-
const rowRef
|
|
25
|
+
const rowRef = useRef< HTMLDivElement >( null );
|
|
26
26
|
|
|
27
27
|
return (
|
|
28
28
|
<PropProvider { ...propContext }>
|
|
@@ -7,7 +7,6 @@ import { type OpenOptions, useWpMediaAttachment, useWpMediaFrame } from '@elemen
|
|
|
7
7
|
import { __ } from '@wordpress/i18n';
|
|
8
8
|
|
|
9
9
|
import { useBoundProp } from '../bound-prop-context';
|
|
10
|
-
import { ControlFormLabel } from '../components/control-form-label';
|
|
11
10
|
import { EnableUnfilteredModal } from '../components/enable-unfiltered-modal';
|
|
12
11
|
import ControlActions from '../control-actions/control-actions';
|
|
13
12
|
import { createControl } from '../create-control';
|
|
@@ -83,7 +82,6 @@ export const SvgMediaControl = createControl( () => {
|
|
|
83
82
|
return (
|
|
84
83
|
<Stack gap={ 1 }>
|
|
85
84
|
<EnableUnfilteredModal open={ unfilteredModalOpenState } onClose={ onCloseUnfilteredModal } />
|
|
86
|
-
<ControlFormLabel> { __( 'SVG', 'elementor' ) } </ControlFormLabel>
|
|
87
85
|
<ControlActions>
|
|
88
86
|
<StyledCard variant="outlined">
|
|
89
87
|
<StyledCardMediaContainer>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { createContext, useContext } from 'react';
|
|
2
|
+
import { type PropTypeUtil } from '@elementor/editor-props';
|
|
3
|
+
|
|
4
|
+
export type ChildControlConfig = {
|
|
5
|
+
component: React.ComponentType;
|
|
6
|
+
props?: Record< string, unknown >;
|
|
7
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
8
|
+
propTypeUtil: PropTypeUtil< string, any >;
|
|
9
|
+
label?: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const RepeatableControlContext = createContext< ChildControlConfig | undefined >( undefined );
|
|
13
|
+
|
|
14
|
+
const useRepeatableControlContext = () => {
|
|
15
|
+
const context = useContext( RepeatableControlContext );
|
|
16
|
+
|
|
17
|
+
if ( ! context ) {
|
|
18
|
+
throw new Error( 'useRepeatableControlContext must be used within RepeatableControl' );
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return context;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export { RepeatableControlContext, useRepeatableControlContext };
|
package/src/index.ts
CHANGED
|
@@ -5,6 +5,7 @@ export { TextAreaControl } from './controls/text-area-control';
|
|
|
5
5
|
export { SizeControl } from './controls/size-control';
|
|
6
6
|
export { StrokeControl } from './controls/stroke-control';
|
|
7
7
|
export { BoxShadowRepeaterControl } from './controls/box-shadow-repeater-control';
|
|
8
|
+
export { FilterRepeaterControl } from './controls/filter-repeater-control';
|
|
8
9
|
export { SelectControl } from './controls/select-control';
|
|
9
10
|
export { ColorControl } from './controls/color-control';
|
|
10
11
|
export { ToggleControl } from './controls/toggle-control';
|
|
@@ -19,6 +20,10 @@ export { AspectRatioControl } from './controls/aspect-ratio-control';
|
|
|
19
20
|
export { SvgMediaControl } from './controls/svg-media-control';
|
|
20
21
|
export { BackgroundControl } from './controls/background-control/background-control';
|
|
21
22
|
export { SwitchControl } from './controls/switch-control';
|
|
23
|
+
export { RepeatableControl } from './controls/repeatable-control';
|
|
24
|
+
export { KeyValueControl } from './controls/key-value-control';
|
|
25
|
+
export { PositionControl } from './controls/position-control';
|
|
26
|
+
export { PopoverContent } from './components/popover-content';
|
|
22
27
|
|
|
23
28
|
// components
|
|
24
29
|
export { ControlFormLabel } from './components/control-form-label';
|
|
@@ -32,7 +37,7 @@ export type { EqualUnequalItems } from './controls/equal-unequal-sizes-control';
|
|
|
32
37
|
export type { ControlActionsItems } from './control-actions/control-actions-context';
|
|
33
38
|
export type { PropProviderProps } from './bound-prop-context';
|
|
34
39
|
export type { SetValue } from './bound-prop-context/prop-context';
|
|
35
|
-
export type { ExtendedOption } from './utils/size-control';
|
|
40
|
+
export type { ExtendedOption, Unit } from './utils/size-control';
|
|
36
41
|
export type { ToggleControlProps } from './controls/toggle-control';
|
|
37
42
|
export type { FontCategory } from './controls/font-family-control/font-family-control';
|
|
38
43
|
|