@elementor/editor-controls 0.13.0 → 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 +36 -0
- package/dist/index.d.mts +13 -5
- package/dist/index.d.ts +13 -5
- package/dist/index.js +443 -244
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +468 -268
- package/dist/index.mjs.map +1 -1
- package/package.json +9 -7
- package/src/api.ts +16 -0
- package/src/components/repeater.tsx +21 -23
- package/src/controls/background-control/background-overlay/background-image-overlay/background-image-overlay-repeat.tsx +1 -1
- package/src/controls/background-control/background-overlay/background-image-overlay/background-image-overlay-size.tsx +8 -2
- package/src/controls/equal-unequal-sizes-control.tsx +15 -10
- package/src/controls/font-family-control/enqueue-font.tsx +15 -0
- package/src/controls/font-family-control/font-family-control.tsx +286 -0
- package/src/controls/gap-control.tsx +20 -12
- package/src/controls/image-control.tsx +7 -2
- package/src/controls/image-media-control.tsx +5 -7
- package/src/controls/link-control.tsx +2 -13
- package/src/controls/linked-dimensions-control.tsx +141 -89
- package/src/controls/size-control.tsx +91 -26
- package/src/controls/svg-media-control.tsx +17 -7
- package/src/hooks/use-filtered-font-families.ts +13 -26
- package/src/hooks/use-unfiltered-files-upload.ts +40 -0
- package/src/index.ts +2 -1
- package/src/controls/font-family-control.tsx +0 -157
|
@@ -1,117 +1,169 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { dimensionsPropTypeUtil, type PropKey, sizePropTypeUtil } from '@elementor/editor-props';
|
|
3
3
|
import { DetachIcon, LinkIcon, SideBottomIcon, SideLeftIcon, SideRightIcon, SideTopIcon } from '@elementor/icons';
|
|
4
|
-
import { Grid, Stack, ToggleButton } from '@elementor/ui';
|
|
4
|
+
import { Grid, Stack, ToggleButton, Tooltip } from '@elementor/ui';
|
|
5
5
|
import { __ } from '@wordpress/i18n';
|
|
6
6
|
|
|
7
7
|
import { PropKeyProvider, PropProvider, useBoundProp } from '../bound-prop-context';
|
|
8
8
|
import { ControlLabel } from '../components/control-label';
|
|
9
9
|
import { createControl } from '../create-control';
|
|
10
|
-
import { SizeControl } from './size-control';
|
|
10
|
+
import { type ExtendedValue, SizeControl } from './size-control';
|
|
11
11
|
|
|
12
|
-
export const LinkedDimensionsControl = createControl(
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
export const LinkedDimensionsControl = createControl(
|
|
13
|
+
( {
|
|
14
|
+
label,
|
|
15
|
+
isSiteRtl = false,
|
|
16
|
+
extendedValues,
|
|
17
|
+
}: {
|
|
18
|
+
label: string;
|
|
19
|
+
isSiteRtl?: boolean;
|
|
20
|
+
extendedValues?: ExtendedValue[];
|
|
21
|
+
} ) => {
|
|
22
|
+
const {
|
|
23
|
+
value: dimensionsValue,
|
|
24
|
+
setValue: setDimensionsValue,
|
|
25
|
+
propType,
|
|
26
|
+
} = useBoundProp( dimensionsPropTypeUtil );
|
|
27
|
+
const { value: sizeValue, setValue: setSizeValue } = useBoundProp( sizePropTypeUtil );
|
|
15
28
|
|
|
16
|
-
|
|
29
|
+
const isLinked = ! dimensionsValue && ! sizeValue ? true : !! sizeValue;
|
|
17
30
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
31
|
+
const onLinkToggle = () => {
|
|
32
|
+
if ( ! isLinked ) {
|
|
33
|
+
setSizeValue( dimensionsValue[ 'block-start' ]?.value );
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
23
36
|
|
|
24
|
-
|
|
37
|
+
const value = sizeValue ? sizePropTypeUtil.create( sizeValue ) : null;
|
|
25
38
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
39
|
+
setDimensionsValue( {
|
|
40
|
+
'block-start': value,
|
|
41
|
+
'block-end': value,
|
|
42
|
+
'inline-start': value,
|
|
43
|
+
'inline-end': value,
|
|
44
|
+
} );
|
|
45
|
+
};
|
|
33
46
|
|
|
34
|
-
|
|
47
|
+
const tooltipLabel = label.toLowerCase();
|
|
35
48
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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
|
-
isLinked={ isLinked }
|
|
73
|
-
/>
|
|
74
|
-
</Grid>
|
|
75
|
-
</Grid>
|
|
76
|
-
</Stack>
|
|
77
|
-
<Stack direction="row" gap={ 2 } flexWrap="nowrap">
|
|
78
|
-
<Grid container gap={ 1 } alignItems="center">
|
|
79
|
-
<Grid item xs={ 12 }>
|
|
80
|
-
<ControlLabel>{ __( 'Bottom', 'elementor' ) }</ControlLabel>
|
|
49
|
+
const LinkedIcon = isLinked ? LinkIcon : DetachIcon;
|
|
50
|
+
// translators: %s: Tooltip title.
|
|
51
|
+
const linkedLabel = __( 'Link %s', 'elementor' ).replace( '%s', tooltipLabel );
|
|
52
|
+
// translators: %s: Tooltip title.
|
|
53
|
+
const unlinkedLabel = __( 'Unlink %s', 'elementor' ).replace( '%s', tooltipLabel );
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<PropProvider propType={ propType } value={ dimensionsValue } setValue={ setDimensionsValue }>
|
|
57
|
+
<Stack direction="row" gap={ 2 } flexWrap="nowrap">
|
|
58
|
+
<ControlLabel>{ label }</ControlLabel>
|
|
59
|
+
<Tooltip title={ isLinked ? unlinkedLabel : linkedLabel } placement="top">
|
|
60
|
+
<ToggleButton
|
|
61
|
+
aria-label={ isLinked ? unlinkedLabel : linkedLabel }
|
|
62
|
+
size={ 'tiny' }
|
|
63
|
+
value={ 'check' }
|
|
64
|
+
selected={ isLinked }
|
|
65
|
+
sx={ { marginLeft: 'auto' } }
|
|
66
|
+
onChange={ onLinkToggle }
|
|
67
|
+
>
|
|
68
|
+
<LinkedIcon fontSize={ 'tiny' } />
|
|
69
|
+
</ToggleButton>
|
|
70
|
+
</Tooltip>
|
|
71
|
+
</Stack>
|
|
72
|
+
<Stack direction="row" gap={ 2 } flexWrap="nowrap">
|
|
73
|
+
<Grid container gap={ 1 } alignItems="center">
|
|
74
|
+
<Grid item xs={ 12 }>
|
|
75
|
+
<ControlLabel>{ __( 'Top', 'elementor' ) }</ControlLabel>
|
|
76
|
+
</Grid>
|
|
77
|
+
<Grid item xs={ 12 }>
|
|
78
|
+
<Control
|
|
79
|
+
bind={ 'block-start' }
|
|
80
|
+
startIcon={ <SideTopIcon fontSize={ 'tiny' } /> }
|
|
81
|
+
isLinked={ isLinked }
|
|
82
|
+
extendedValues={ extendedValues }
|
|
83
|
+
/>
|
|
84
|
+
</Grid>
|
|
81
85
|
</Grid>
|
|
82
|
-
<Grid
|
|
83
|
-
<
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
86
|
+
<Grid container gap={ 1 } alignItems="center">
|
|
87
|
+
<Grid item xs={ 12 }>
|
|
88
|
+
<ControlLabel>
|
|
89
|
+
{ isSiteRtl ? __( 'Left', 'elementor' ) : __( 'Right', 'elementor' ) }
|
|
90
|
+
</ControlLabel>
|
|
91
|
+
</Grid>
|
|
92
|
+
<Grid item xs={ 12 }>
|
|
93
|
+
<Control
|
|
94
|
+
bind={ 'inline-end' }
|
|
95
|
+
startIcon={
|
|
96
|
+
isSiteRtl ? (
|
|
97
|
+
<SideLeftIcon fontSize={ 'tiny' } />
|
|
98
|
+
) : (
|
|
99
|
+
<SideRightIcon fontSize={ 'tiny' } />
|
|
100
|
+
)
|
|
101
|
+
}
|
|
102
|
+
isLinked={ isLinked }
|
|
103
|
+
extendedValues={ extendedValues }
|
|
104
|
+
/>
|
|
105
|
+
</Grid>
|
|
88
106
|
</Grid>
|
|
89
|
-
</
|
|
90
|
-
<
|
|
91
|
-
<Grid
|
|
92
|
-
<
|
|
107
|
+
</Stack>
|
|
108
|
+
<Stack direction="row" gap={ 2 } flexWrap="nowrap">
|
|
109
|
+
<Grid container gap={ 1 } alignItems="center">
|
|
110
|
+
<Grid item xs={ 12 }>
|
|
111
|
+
<ControlLabel>{ __( 'Bottom', 'elementor' ) }</ControlLabel>
|
|
112
|
+
</Grid>
|
|
113
|
+
<Grid item xs={ 12 }>
|
|
114
|
+
<Control
|
|
115
|
+
bind={ 'block-end' }
|
|
116
|
+
startIcon={ <SideBottomIcon fontSize={ 'tiny' } /> }
|
|
117
|
+
isLinked={ isLinked }
|
|
118
|
+
extendedValues={ extendedValues }
|
|
119
|
+
/>
|
|
120
|
+
</Grid>
|
|
93
121
|
</Grid>
|
|
94
|
-
<Grid
|
|
95
|
-
<
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
122
|
+
<Grid container gap={ 1 } alignItems="center">
|
|
123
|
+
<Grid item xs={ 12 }>
|
|
124
|
+
<ControlLabel>
|
|
125
|
+
{ isSiteRtl ? __( 'Right', 'elementor' ) : __( 'Left', 'elementor' ) }
|
|
126
|
+
</ControlLabel>
|
|
127
|
+
</Grid>
|
|
128
|
+
<Grid item xs={ 12 }>
|
|
129
|
+
<Control
|
|
130
|
+
bind={ 'inline-start' }
|
|
131
|
+
startIcon={
|
|
132
|
+
isSiteRtl ? (
|
|
133
|
+
<SideRightIcon fontSize={ 'tiny' } />
|
|
134
|
+
) : (
|
|
135
|
+
<SideLeftIcon fontSize={ 'tiny' } />
|
|
136
|
+
)
|
|
137
|
+
}
|
|
138
|
+
isLinked={ isLinked }
|
|
139
|
+
extendedValues={ extendedValues }
|
|
140
|
+
/>
|
|
141
|
+
</Grid>
|
|
100
142
|
</Grid>
|
|
101
|
-
</
|
|
102
|
-
</
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
143
|
+
</Stack>
|
|
144
|
+
</PropProvider>
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
);
|
|
106
148
|
|
|
107
|
-
const Control = ( {
|
|
149
|
+
const Control = ( {
|
|
150
|
+
bind,
|
|
151
|
+
startIcon,
|
|
152
|
+
isLinked,
|
|
153
|
+
extendedValues,
|
|
154
|
+
}: {
|
|
155
|
+
bind: PropKey;
|
|
156
|
+
startIcon: React.ReactNode;
|
|
157
|
+
isLinked: boolean;
|
|
158
|
+
extendedValues?: ExtendedValue[];
|
|
159
|
+
} ) => {
|
|
108
160
|
if ( isLinked ) {
|
|
109
|
-
return <SizeControl startIcon={ startIcon } />;
|
|
161
|
+
return <SizeControl startIcon={ startIcon } extendedValues={ extendedValues } />;
|
|
110
162
|
}
|
|
111
163
|
|
|
112
164
|
return (
|
|
113
165
|
<PropKeyProvider bind={ bind }>
|
|
114
|
-
<SizeControl startIcon={ startIcon } />
|
|
166
|
+
<SizeControl startIcon={ startIcon } extendedValues={ extendedValues } />
|
|
115
167
|
</PropKeyProvider>
|
|
116
168
|
);
|
|
117
169
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { sizePropTypeUtil } from '@elementor/editor-props';
|
|
2
|
+
import { sizePropTypeUtil, stringPropTypeUtil } from '@elementor/editor-props';
|
|
3
3
|
import { InputAdornment } from '@elementor/ui';
|
|
4
4
|
|
|
5
5
|
import { useBoundProp } from '../bound-prop-context';
|
|
@@ -8,6 +8,7 @@ import ControlActions from '../control-actions/control-actions';
|
|
|
8
8
|
import { createControl } from '../create-control';
|
|
9
9
|
import { useSyncExternalState } from '../hooks/use-sync-external-state';
|
|
10
10
|
|
|
11
|
+
export type ExtendedValue = 'auto';
|
|
11
12
|
export type Unit = 'px' | '%' | 'em' | 'rem' | 'vw' | 'vh';
|
|
12
13
|
|
|
13
14
|
const defaultUnits: Unit[] = [ 'px', '%', 'em', 'rem', 'vw', 'vh' ];
|
|
@@ -19,34 +20,98 @@ type SizeControlProps = {
|
|
|
19
20
|
placeholder?: string;
|
|
20
21
|
startIcon?: React.ReactNode;
|
|
21
22
|
units?: Unit[];
|
|
23
|
+
extendedValues?: ExtendedValue[];
|
|
22
24
|
};
|
|
23
25
|
|
|
24
|
-
export const SizeControl = createControl(
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
26
|
+
export const SizeControl = createControl(
|
|
27
|
+
( { units = defaultUnits, extendedValues = [], placeholder, startIcon }: SizeControlProps ) => {
|
|
28
|
+
const { value: sizeValue, setValue: setSizeValue } = useBoundProp( sizePropTypeUtil );
|
|
29
|
+
|
|
30
|
+
const [ state, setState ] = useSyncExternalState( {
|
|
31
|
+
external: sizeValue,
|
|
32
|
+
setExternal: setSizeValue,
|
|
33
|
+
persistWhen: ( controlValue ) => !! controlValue?.size || controlValue?.size === 0,
|
|
34
|
+
fallback: ( controlValue ) => ( { unit: controlValue?.unit || defaultUnit, size: defaultSize } ),
|
|
35
|
+
} );
|
|
36
|
+
|
|
37
|
+
const handleUnitChange = ( unit: Unit ) => {
|
|
38
|
+
setState( ( prev ) => ( {
|
|
39
|
+
size: prev?.size ?? defaultSize,
|
|
40
|
+
unit,
|
|
41
|
+
} ) );
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const handleSizeChange = ( event: React.ChangeEvent< HTMLInputElement > ) => {
|
|
45
|
+
const { value: size } = event.target;
|
|
46
|
+
|
|
47
|
+
setState( ( prev ) => ( {
|
|
48
|
+
...prev,
|
|
49
|
+
size: size || size === '0' ? parseFloat( size ) : defaultSize,
|
|
50
|
+
} ) );
|
|
51
|
+
};
|
|
40
52
|
|
|
41
|
-
|
|
42
|
-
|
|
53
|
+
const inputProps = {
|
|
54
|
+
size: state.size,
|
|
55
|
+
unit: state.unit,
|
|
56
|
+
placeholder,
|
|
57
|
+
startIcon,
|
|
58
|
+
units,
|
|
59
|
+
extendedValues,
|
|
60
|
+
handleSizeChange,
|
|
61
|
+
handleUnitChange,
|
|
62
|
+
};
|
|
43
63
|
|
|
44
|
-
|
|
45
|
-
...
|
|
46
|
-
|
|
47
|
-
}
|
|
64
|
+
if ( extendedValues?.length ) {
|
|
65
|
+
return <ExtendedSizeInput { ...inputProps } />;
|
|
66
|
+
}
|
|
67
|
+
return <SizeInput { ...inputProps } />;
|
|
68
|
+
}
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
const ExtendedSizeInput = ( props: SizeInputProps ) => {
|
|
72
|
+
const { value: stringValue, setValue: setStringValue } = useBoundProp( stringPropTypeUtil );
|
|
73
|
+
const { extendedValues = [] } = props;
|
|
74
|
+
|
|
75
|
+
const unit = ( stringValue ?? props.unit ) as Unit;
|
|
76
|
+
|
|
77
|
+
const handleUnitChange = ( newUnit: Unit ) => {
|
|
78
|
+
if ( extendedValues.includes( newUnit as ExtendedValue ) ) {
|
|
79
|
+
setStringValue( newUnit );
|
|
80
|
+
} else {
|
|
81
|
+
props.handleUnitChange( newUnit );
|
|
82
|
+
}
|
|
48
83
|
};
|
|
49
84
|
|
|
85
|
+
return (
|
|
86
|
+
<SizeInput
|
|
87
|
+
{ ...props }
|
|
88
|
+
units={ [ ...props.units, ...( extendedValues as unknown as Unit[] ) ] }
|
|
89
|
+
handleUnitChange={ handleUnitChange }
|
|
90
|
+
unit={ unit }
|
|
91
|
+
/>
|
|
92
|
+
);
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
type SizeInputProps = {
|
|
96
|
+
unit: Unit;
|
|
97
|
+
size: number;
|
|
98
|
+
placeholder?: string;
|
|
99
|
+
startIcon?: React.ReactNode;
|
|
100
|
+
units: Unit[];
|
|
101
|
+
extendedValues?: ExtendedValue[];
|
|
102
|
+
handleUnitChange: ( unit: Unit ) => void;
|
|
103
|
+
handleSizeChange: ( event: React.ChangeEvent< HTMLInputElement > ) => void;
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const SizeInput = ( {
|
|
107
|
+
units,
|
|
108
|
+
handleUnitChange,
|
|
109
|
+
handleSizeChange,
|
|
110
|
+
placeholder,
|
|
111
|
+
startIcon,
|
|
112
|
+
size,
|
|
113
|
+
unit,
|
|
114
|
+
}: SizeInputProps ) => {
|
|
50
115
|
return (
|
|
51
116
|
<ControlActions>
|
|
52
117
|
<TextFieldInnerSelection
|
|
@@ -54,7 +119,7 @@ export const SizeControl = createControl( ( { units = defaultUnits, placeholder,
|
|
|
54
119
|
<SelectionEndAdornment
|
|
55
120
|
options={ units }
|
|
56
121
|
onClick={ handleUnitChange }
|
|
57
|
-
value={
|
|
122
|
+
value={ unit ?? defaultUnit }
|
|
58
123
|
/>
|
|
59
124
|
}
|
|
60
125
|
placeholder={ placeholder }
|
|
@@ -62,9 +127,9 @@ export const SizeControl = createControl( ( { units = defaultUnits, placeholder,
|
|
|
62
127
|
startIcon ? <InputAdornment position="start">{ startIcon }</InputAdornment> : undefined
|
|
63
128
|
}
|
|
64
129
|
type="number"
|
|
65
|
-
value={ Number.isNaN(
|
|
130
|
+
value={ Number.isNaN( size ) ? '' : size }
|
|
66
131
|
onChange={ handleSizeChange }
|
|
67
132
|
/>
|
|
68
133
|
</ControlActions>
|
|
69
134
|
);
|
|
70
|
-
}
|
|
135
|
+
};
|
|
@@ -2,13 +2,14 @@ import * as React from 'react';
|
|
|
2
2
|
import { imageSrcPropTypeUtil } from '@elementor/editor-props';
|
|
3
3
|
import { UploadIcon } from '@elementor/icons';
|
|
4
4
|
import { Button, Card, CardMedia, CardOverlay, CircularProgress, Stack, styled } from '@elementor/ui';
|
|
5
|
-
import { useWpMediaAttachment, useWpMediaFrame } from '@elementor/wp-media';
|
|
5
|
+
import { type OpenOptions, useWpMediaAttachment, useWpMediaFrame } from '@elementor/wp-media';
|
|
6
6
|
import { __ } from '@wordpress/i18n';
|
|
7
7
|
|
|
8
8
|
import { useBoundProp } from '../bound-prop-context';
|
|
9
9
|
import { ControlLabel } from '../components/control-label';
|
|
10
10
|
import ControlActions from '../control-actions/control-actions';
|
|
11
11
|
import { createControl } from '../create-control';
|
|
12
|
+
import { useUnfilteredFilesUpload } from '../hooks/use-unfiltered-files-upload';
|
|
12
13
|
|
|
13
14
|
const TILE_SIZE = 8;
|
|
14
15
|
const TILE_WHITE = 'transparent';
|
|
@@ -40,9 +41,10 @@ export const SvgMediaControl = createControl( () => {
|
|
|
40
41
|
const { id, url } = value ?? {};
|
|
41
42
|
const { data: attachment, isFetching } = useWpMediaAttachment( id?.value || null );
|
|
42
43
|
const src = attachment?.url ?? url?.value ?? null;
|
|
44
|
+
const { data: allowSvgUpload } = useUnfilteredFilesUpload();
|
|
45
|
+
|
|
43
46
|
const { open } = useWpMediaFrame( {
|
|
44
|
-
|
|
45
|
-
allowedExtensions: [ 'svg' ],
|
|
47
|
+
mediaTypes: [ 'svg' ],
|
|
46
48
|
multiple: false,
|
|
47
49
|
selected: id?.value || null,
|
|
48
50
|
onSelect: ( selectedAttachment ) => {
|
|
@@ -56,9 +58,17 @@ export const SvgMediaControl = createControl( () => {
|
|
|
56
58
|
},
|
|
57
59
|
} );
|
|
58
60
|
|
|
61
|
+
const handleClick = ( openOptions?: OpenOptions ) => {
|
|
62
|
+
if ( allowSvgUpload ) {
|
|
63
|
+
open( openOptions );
|
|
64
|
+
} else {
|
|
65
|
+
// TODO open upload SVG confirmation modal
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
|
|
59
69
|
return (
|
|
60
70
|
<Stack gap={ 1 }>
|
|
61
|
-
<ControlLabel> { __( '
|
|
71
|
+
<ControlLabel> { __( 'SVG', 'elementor' ) } </ControlLabel>
|
|
62
72
|
<ControlActions>
|
|
63
73
|
<StyledCard variant="outlined">
|
|
64
74
|
<StyledCardMediaContainer>
|
|
@@ -85,7 +95,7 @@ export const SvgMediaControl = createControl( () => {
|
|
|
85
95
|
size="tiny"
|
|
86
96
|
color="inherit"
|
|
87
97
|
variant="outlined"
|
|
88
|
-
onClick={ () =>
|
|
98
|
+
onClick={ () => handleClick( { mode: 'browse' } ) }
|
|
89
99
|
>
|
|
90
100
|
{ __( 'Select SVG', 'elementor' ) }
|
|
91
101
|
</Button>
|
|
@@ -94,9 +104,9 @@ export const SvgMediaControl = createControl( () => {
|
|
|
94
104
|
variant="text"
|
|
95
105
|
color="inherit"
|
|
96
106
|
startIcon={ <UploadIcon /> }
|
|
97
|
-
onClick={ () =>
|
|
107
|
+
onClick={ () => handleClick( { mode: 'upload' } ) }
|
|
98
108
|
>
|
|
99
|
-
{ __( 'Upload
|
|
109
|
+
{ __( 'Upload', 'elementor' ) }
|
|
100
110
|
</Button>
|
|
101
111
|
</Stack>
|
|
102
112
|
</CardOverlay>
|
|
@@ -1,37 +1,24 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const supportedCategories: Record< SupportedFonts, string > = {
|
|
6
|
-
system: __( 'System', 'elementor' ),
|
|
7
|
-
googlefonts: __( 'Google Fonts', 'elementor' ),
|
|
8
|
-
customfonts: __( 'Custom Fonts', 'elementor' ),
|
|
1
|
+
export type FontListItem = {
|
|
2
|
+
type: 'font' | 'category';
|
|
3
|
+
value: string;
|
|
9
4
|
};
|
|
10
5
|
|
|
11
|
-
export const useFilteredFontFamilies = ( fontFamilies: Record< string,
|
|
12
|
-
const filteredFontFamilies = Object.entries( fontFamilies ).reduce<
|
|
13
|
-
( acc, [
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
if ( ! isMatch ) {
|
|
17
|
-
return acc;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const categoryLabel = supportedCategories[ category as SupportedFonts ];
|
|
6
|
+
export const useFilteredFontFamilies = ( fontFamilies: Record< string, string[] >, searchValue: string ) => {
|
|
7
|
+
const filteredFontFamilies = Object.entries( fontFamilies ).reduce< FontListItem[] >(
|
|
8
|
+
( acc, [ category, fonts ] ) => {
|
|
9
|
+
const filteredFonts = fonts.filter( ( font ) => font.toLowerCase().includes( searchValue.toLowerCase() ) );
|
|
21
10
|
|
|
22
|
-
if (
|
|
23
|
-
|
|
11
|
+
if ( filteredFonts.length ) {
|
|
12
|
+
acc.push( { type: 'category', value: category } );
|
|
24
13
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
acc.set( categoryLabel, [ font ] );
|
|
29
|
-
}
|
|
14
|
+
filteredFonts.forEach( ( font ) => {
|
|
15
|
+
acc.push( { type: 'font', value: font } );
|
|
16
|
+
} );
|
|
30
17
|
}
|
|
31
18
|
|
|
32
19
|
return acc;
|
|
33
20
|
},
|
|
34
|
-
|
|
21
|
+
[]
|
|
35
22
|
);
|
|
36
23
|
|
|
37
24
|
return [ ...filteredFontFamilies ];
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { useMutation, useQuery, useQueryClient } from '@elementor/query';
|
|
2
|
+
|
|
3
|
+
import { apiClient } from '../api';
|
|
4
|
+
|
|
5
|
+
export const UNFILTERED_FILES_UPLOAD_KEY = 'elementor_unfiltered_files_upload';
|
|
6
|
+
|
|
7
|
+
const unfilteredFilesQueryKey = {
|
|
8
|
+
queryKey: [ UNFILTERED_FILES_UPLOAD_KEY ],
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
type Value = '0' | '1';
|
|
12
|
+
|
|
13
|
+
export const useUnfilteredFilesUpload = () =>
|
|
14
|
+
useQuery( {
|
|
15
|
+
...unfilteredFilesQueryKey,
|
|
16
|
+
queryFn: (): Promise< boolean > =>
|
|
17
|
+
apiClient.getElementorSetting< Value >( UNFILTERED_FILES_UPLOAD_KEY ).then( ( res ) => {
|
|
18
|
+
return formatResponse( res );
|
|
19
|
+
} ),
|
|
20
|
+
staleTime: Infinity,
|
|
21
|
+
} );
|
|
22
|
+
|
|
23
|
+
export function useUpdateUnfilteredFilesUpload() {
|
|
24
|
+
const queryClient = useQueryClient();
|
|
25
|
+
|
|
26
|
+
const mutate = useMutation( {
|
|
27
|
+
mutationFn: ( { allowUnfilteredFilesUpload }: { allowUnfilteredFilesUpload: boolean } ) =>
|
|
28
|
+
apiClient.updateElementorSetting< Value >(
|
|
29
|
+
UNFILTERED_FILES_UPLOAD_KEY,
|
|
30
|
+
allowUnfilteredFilesUpload ? '1' : '0'
|
|
31
|
+
),
|
|
32
|
+
onSuccess: () => queryClient.invalidateQueries( unfilteredFilesQueryKey ),
|
|
33
|
+
} );
|
|
34
|
+
|
|
35
|
+
return mutate;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const formatResponse = ( response: Value ): boolean => {
|
|
39
|
+
return Boolean( response === '1' );
|
|
40
|
+
};
|
package/src/index.ts
CHANGED
|
@@ -11,7 +11,7 @@ export { ToggleControl } from './controls/toggle-control';
|
|
|
11
11
|
export { NumberControl } from './controls/number-control';
|
|
12
12
|
export { EqualUnequalSizesControl } from './controls/equal-unequal-sizes-control';
|
|
13
13
|
export { LinkedDimensionsControl } from './controls/linked-dimensions-control';
|
|
14
|
-
export { FontFamilyControl } from './controls/font-family-control';
|
|
14
|
+
export { FontFamilyControl } from './controls/font-family-control/font-family-control';
|
|
15
15
|
export { UrlControl } from './controls/url-control';
|
|
16
16
|
export { LinkControl } from './controls/link-control';
|
|
17
17
|
export { GapControl } from './controls/gap-control';
|
|
@@ -29,6 +29,7 @@ export type { EqualUnequalItems } from './controls/equal-unequal-sizes-control';
|
|
|
29
29
|
export type { ControlActionsItems } from './control-actions/control-actions-context';
|
|
30
30
|
export type { PropProviderProps } from './bound-prop-context';
|
|
31
31
|
export type { SetValue } from './bound-prop-context/prop-context';
|
|
32
|
+
export type { ExtendedValue } from './controls/size-control';
|
|
32
33
|
|
|
33
34
|
// providers
|
|
34
35
|
export { createControlReplacement, ControlReplacementProvider } from './create-control-replacement';
|