@elementor/editor-controls 3.33.0-149 → 3.33.0-151

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@elementor/editor-controls",
3
3
  "description": "This package contains the controls model and utils for the Elementor editor",
4
- "version": "3.33.0-149",
4
+ "version": "3.33.0-151",
5
5
  "private": false,
6
6
  "author": "Elementor Team",
7
7
  "homepage": "https://elementor.com/",
@@ -40,22 +40,22 @@
40
40
  "dev": "tsup --config=../../tsup.dev.ts"
41
41
  },
42
42
  "dependencies": {
43
- "@elementor/editor-current-user": "3.33.0-149",
44
- "@elementor/editor-elements": "3.33.0-149",
45
- "@elementor/editor-props": "3.33.0-149",
46
- "@elementor/editor-responsive": "3.33.0-149",
47
- "@elementor/editor-ui": "3.33.0-149",
48
- "@elementor/editor-v1-adapters": "3.33.0-149",
49
- "@elementor/env": "3.33.0-149",
50
- "@elementor/http-client": "3.33.0-149",
43
+ "@elementor/editor-current-user": "3.33.0-151",
44
+ "@elementor/editor-elements": "3.33.0-151",
45
+ "@elementor/editor-props": "3.33.0-151",
46
+ "@elementor/editor-responsive": "3.33.0-151",
47
+ "@elementor/editor-ui": "3.33.0-151",
48
+ "@elementor/editor-v1-adapters": "3.33.0-151",
49
+ "@elementor/env": "3.33.0-151",
50
+ "@elementor/http-client": "3.33.0-151",
51
51
  "@elementor/icons": "^1.51.1",
52
- "@elementor/locations": "3.33.0-149",
53
- "@elementor/mixpanel": "3.33.0-149",
54
- "@elementor/query": "3.33.0-149",
55
- "@elementor/session": "3.33.0-149",
52
+ "@elementor/locations": "3.33.0-151",
53
+ "@elementor/mixpanel": "3.33.0-151",
54
+ "@elementor/query": "3.33.0-151",
55
+ "@elementor/session": "3.33.0-151",
56
56
  "@elementor/ui": "1.36.12",
57
- "@elementor/utils": "3.33.0-149",
58
- "@elementor/wp-media": "3.33.0-149",
57
+ "@elementor/utils": "3.33.0-151",
58
+ "@elementor/wp-media": "3.33.0-151",
59
59
  "@wordpress/i18n": "^5.13.0",
60
60
  "@monaco-editor/react": "^4.7.0"
61
61
  },
@@ -0,0 +1,64 @@
1
+ import * as React from 'react';
2
+ import { InfoAlert } from '@elementor/editor-ui';
3
+ import { type AlertProps, AlertTitle, Box, Infotip, type InfotipProps, useTheme } from '@elementor/ui';
4
+ import { DirectionProvider } from '@elementor/ui';
5
+
6
+ type Props = {
7
+ infotipProps?: Partial< InfotipProps >;
8
+ alertProps?: Partial< AlertProps >;
9
+ title?: string;
10
+ description?: React.ReactNode | string;
11
+ isEnabled?: boolean;
12
+ };
13
+
14
+ const DEFAULT_COLOR = 'secondary';
15
+
16
+ export const ConditionalControlInfotip = React.forwardRef(
17
+ ( { children, title, description, alertProps, infotipProps, ...props }: React.PropsWithChildren< Props >, ref ) => {
18
+ const theme = useTheme();
19
+ const isUiRtl = 'rtl' === theme.direction;
20
+ const isEnabled = props.isEnabled && ( title || description );
21
+
22
+ return (
23
+ <Box ref={ ref }>
24
+ { isEnabled ? (
25
+ <DirectionProvider rtl={ isUiRtl }>
26
+ <Infotip
27
+ placement={ 'right' }
28
+ color={ DEFAULT_COLOR }
29
+ slotProps={ {
30
+ popper: {
31
+ modifiers: [
32
+ {
33
+ name: 'offset',
34
+ options: {
35
+ offset: [ 0, 10 ],
36
+ },
37
+ },
38
+ ],
39
+ },
40
+ } }
41
+ { ...infotipProps }
42
+ content={
43
+ <InfoAlert
44
+ color={ DEFAULT_COLOR }
45
+ sx={ { width: 300, px: 1.5, py: 2 } }
46
+ { ...alertProps }
47
+ >
48
+ <Box sx={ { flexDirection: 'column', display: 'flex', gap: 0.5 } }>
49
+ <AlertTitle>{ title }</AlertTitle>
50
+ <Box>{ description }</Box>
51
+ </Box>
52
+ </InfoAlert>
53
+ }
54
+ >
55
+ { children }
56
+ </Infotip>
57
+ </DirectionProvider>
58
+ ) : (
59
+ children
60
+ ) }
61
+ </Box>
62
+ );
63
+ }
64
+ );
@@ -1,6 +1,5 @@
1
1
  import * as React from 'react';
2
2
  import { useState } from 'react';
3
- import { useCurrentUserCapabilities } from '@elementor/editor-current-user';
4
3
  import {
5
4
  Button,
6
5
  CircularProgress,
@@ -34,11 +33,6 @@ const ADMIN_CONTENT_TEXT = __(
34
33
  'Before you enable unfiltered files upload, note that such files include a security risk. Elementor does run a process to remove possible malicious code, but there is still risk involved when using such files.',
35
34
  'elementor'
36
35
  );
37
- const NON_ADMIN_TITLE_TEXT = __( "Sorry, you can't upload that file yet", 'elementor' );
38
- const NON_ADMIN_CONTENT_TEXT = __(
39
- 'This is because this file type may pose a security risk. To upload them anyway, ask the site administrator to enable unfiltered file uploads.',
40
- 'elementor'
41
- );
42
36
 
43
37
  const ADMIN_FAILED_CONTENT_TEXT_PT1 = __( 'Failed to enable unfiltered files upload.', 'elementor' );
44
38
 
@@ -51,9 +45,7 @@ const WAIT_FOR_CLOSE_TIMEOUT_MS = 300;
51
45
 
52
46
  export const EnableUnfilteredModal = ( props: EnableUnfilteredModalProps ) => {
53
47
  const { mutateAsync, isPending } = useUpdateUnfilteredFilesUpload();
54
- const { canUser } = useCurrentUserCapabilities();
55
48
  const [ isError, setIsError ] = useState( false );
56
- const canManageOptions = canUser( 'manage_options' );
57
49
 
58
50
  const onClose = ( enabled: boolean ) => {
59
51
  props.onClose( enabled );
@@ -75,7 +67,7 @@ export const EnableUnfilteredModal = ( props: EnableUnfilteredModalProps ) => {
75
67
 
76
68
  const dialogProps = { ...props, isPending, handleEnable, isError, onClose };
77
69
 
78
- return canManageOptions ? <AdminDialog { ...dialogProps } /> : <NonAdminDialog { ...dialogProps } />;
70
+ return <AdminDialog { ...dialogProps } />;
79
71
  };
80
72
 
81
73
  const AdminDialog = ( { open, onClose, handleEnable, isPending, isError }: LocalModalProps ) => (
@@ -111,20 +103,3 @@ const AdminDialog = ( { open, onClose, handleEnable, isPending, isError }: Local
111
103
  </DialogActions>
112
104
  </Dialog>
113
105
  );
114
-
115
- const NonAdminDialog = ( { open, onClose }: LocalModalProps ) => (
116
- <Dialog open={ open } maxWidth={ 'sm' } onClose={ () => onClose( false ) }>
117
- <DialogHeader logo={ false }>
118
- <DialogTitle>{ NON_ADMIN_TITLE_TEXT }</DialogTitle>
119
- </DialogHeader>
120
- <Divider />
121
- <DialogContent>
122
- <DialogContentText>{ NON_ADMIN_CONTENT_TEXT }</DialogContentText>
123
- </DialogContent>
124
- <DialogActions>
125
- <Button size={ 'medium' } onClick={ () => onClose( false ) } variant="contained" color="primary">
126
- { __( 'Got it', 'elementor' ) }
127
- </Button>
128
- </DialogActions>
129
- </Dialog>
130
- );
@@ -0,0 +1,90 @@
1
+ import * as React from 'react';
2
+ import { getElementLabel } from '@elementor/editor-elements';
3
+ import { stringPropTypeUtil, type StringPropValue } from '@elementor/editor-props';
4
+ import { MenuListItem } from '@elementor/editor-ui';
5
+ import { Select, type SelectChangeEvent, styled, Typography } from '@elementor/ui';
6
+ import { __ } from '@wordpress/i18n';
7
+
8
+ import { useBoundProp } from '../bound-prop-context';
9
+ import { ConditionalControlInfotip } from '../components/conditional-control-infotip';
10
+ import ControlActions from '../control-actions/control-actions';
11
+ import { createControl } from '../create-control';
12
+
13
+ export type SelectOption = {
14
+ label: string;
15
+ value: StringPropValue[ 'value' ];
16
+ disabled?: boolean;
17
+ };
18
+
19
+ type Props = {
20
+ options: SelectOption[];
21
+ onChange?: ( newValue: string | null, previousValue: string | null | undefined ) => void;
22
+ fallbackLabels?: Record< string, string >;
23
+ };
24
+
25
+ const StyledSelect = styled( Select )( () => ( { '.MuiSelect-select.Mui-disabled': { cursor: 'not-allowed' } } ) );
26
+
27
+ export const HtmlTagControl = createControl( ( { options, onChange, fallbackLabels = {} }: Props ) => {
28
+ const { value, setValue, disabled, placeholder } = useBoundProp( stringPropTypeUtil );
29
+ const handleChange = ( event: SelectChangeEvent< StringPropValue[ 'value' ] > ) => {
30
+ const newValue = event.target.value || null;
31
+
32
+ onChange?.( newValue, value );
33
+ setValue( newValue );
34
+ };
35
+
36
+ const elementLabel = getElementLabel() ?? 'element';
37
+ const infoTipProps = {
38
+ title: __( 'HTML Tag', 'elementor' ),
39
+ /* translators: %s is the element name. */
40
+ description: __(
41
+ `The tag is locked to 'a' tag because this %s has a link. To pick a different tag, remove the link first.`,
42
+ 'elementor'
43
+ ).replace( '%s', elementLabel ),
44
+ isEnabled: !! disabled,
45
+ };
46
+
47
+ const renderValue = ( selectedValue: string | null ) => {
48
+ if ( selectedValue ) {
49
+ return findOptionByValue( selectedValue )?.label || fallbackLabels[ selectedValue ] || selectedValue;
50
+ }
51
+
52
+ if ( ! placeholder ) {
53
+ return '';
54
+ }
55
+
56
+ const placeholderOption = findOptionByValue( placeholder );
57
+ const displayText = placeholderOption?.label || placeholder;
58
+
59
+ return (
60
+ <Typography component="span" variant="caption" color="text.tertiary">
61
+ { displayText }
62
+ </Typography>
63
+ );
64
+ };
65
+
66
+ const findOptionByValue = ( searchValue: string | null ) => options.find( ( opt ) => opt.value === searchValue );
67
+
68
+ return (
69
+ <ControlActions>
70
+ <ConditionalControlInfotip { ...infoTipProps }>
71
+ <StyledSelect
72
+ sx={ { overflow: 'hidden', cursor: disabled ? 'not-allowed' : undefined } }
73
+ displayEmpty
74
+ size="tiny"
75
+ renderValue={ renderValue }
76
+ value={ value ?? '' }
77
+ onChange={ handleChange }
78
+ disabled={ disabled }
79
+ fullWidth
80
+ >
81
+ { options.map( ( { label, ...props } ) => (
82
+ <MenuListItem key={ props.value } { ...props } value={ props.value ?? '' }>
83
+ { label }
84
+ </MenuListItem>
85
+ ) ) }
86
+ </StyledSelect>
87
+ </ConditionalControlInfotip>
88
+ </ControlActions>
89
+ );
90
+ } );
@@ -67,6 +67,13 @@ export const QueryControl = createControl( ( props: Props ) => {
67
67
  };
68
68
 
69
69
  const onTextChange = ( newValue: string | null ) => {
70
+ if ( ! newValue ) {
71
+ setValue( null );
72
+ onSetValue?.( null );
73
+
74
+ return;
75
+ }
76
+
70
77
  const newLinkValue = newValue?.trim() || '';
71
78
  const valueToSave = newLinkValue ? urlPropTypeUtil.create( newLinkValue ) : null;
72
79
 
@@ -1,12 +1,14 @@
1
1
  import * as React from 'react';
2
2
  import { useState } from 'react';
3
+ import { useCurrentUserCapabilities } from '@elementor/editor-current-user';
3
4
  import { imageSrcPropTypeUtil } from '@elementor/editor-props';
4
5
  import { UploadIcon } from '@elementor/icons';
5
- import { Button, Card, CardMedia, CardOverlay, CircularProgress, Stack, styled } from '@elementor/ui';
6
+ import { Button, Card, CardMedia, CardOverlay, CircularProgress, Stack, styled, ThemeProvider } from '@elementor/ui';
6
7
  import { type OpenOptions, useWpMediaAttachment, useWpMediaFrame } from '@elementor/wp-media';
7
8
  import { __ } from '@wordpress/i18n';
8
9
 
9
10
  import { useBoundProp } from '../bound-prop-context';
11
+ import { ConditionalControlInfotip } from '../components/conditional-control-infotip';
10
12
  import { EnableUnfilteredModal } from '../components/enable-unfiltered-modal';
11
13
  import ControlActions from '../control-actions/control-actions';
12
14
  import { createControl } from '../create-control';
@@ -47,6 +49,8 @@ export const SvgMediaControl = createControl( () => {
47
49
  const src = attachment?.url ?? url?.value ?? null;
48
50
  const { data: allowSvgUpload } = useUnfilteredFilesUpload();
49
51
  const [ unfilteredModalOpenState, setUnfilteredModalOpenState ] = useState( false );
52
+ const { canUser } = useCurrentUserCapabilities();
53
+ const canManageOptions = canUser( 'manage_options' );
50
54
 
51
55
  const { open } = useWpMediaFrame( {
52
56
  mediaTypes: [ 'svg' ],
@@ -79,6 +83,18 @@ export const SvgMediaControl = createControl( () => {
79
83
  }
80
84
  };
81
85
 
86
+ const infotipProps = {
87
+ title: __( "Sorry, you can't upload that file yet.", 'elementor' ),
88
+ description: (
89
+ <>
90
+ { __( 'To upload them anyway, ask the site administrator to enable unfiltered', 'elementor' ) }
91
+ <br />
92
+ { __( 'file uploads.', 'elementor' ) }
93
+ </>
94
+ ),
95
+ isEnabled: ! canManageOptions,
96
+ };
97
+
82
98
  return (
83
99
  <Stack gap={ 1 }>
84
100
  <EnableUnfilteredModal open={ unfilteredModalOpenState } onClose={ onCloseUnfilteredModal } />
@@ -112,15 +128,22 @@ export const SvgMediaControl = createControl( () => {
112
128
  >
113
129
  { __( 'Select SVG', 'elementor' ) }
114
130
  </Button>
115
- <Button
116
- size="tiny"
117
- variant="text"
118
- color="inherit"
119
- startIcon={ <UploadIcon /> }
120
- onClick={ () => handleClick( MODE_UPLOAD ) }
121
- >
122
- { __( 'Upload', 'elementor' ) }
123
- </Button>
131
+ <ConditionalControlInfotip { ...infotipProps }>
132
+ <span>
133
+ <ThemeProvider colorScheme={ canManageOptions ? 'light' : 'dark' }>
134
+ <Button
135
+ size="tiny"
136
+ variant="text"
137
+ color="inherit"
138
+ startIcon={ <UploadIcon /> }
139
+ disabled={ canManageOptions ? false : true }
140
+ onClick={ () => canManageOptions && handleClick( MODE_UPLOAD ) }
141
+ >
142
+ { __( 'Upload', 'elementor' ) }
143
+ </Button>
144
+ </ThemeProvider>
145
+ </span>
146
+ </ConditionalControlInfotip>
124
147
  </Stack>
125
148
  </CardOverlay>
126
149
  </StyledCard>
package/src/index.ts CHANGED
@@ -17,6 +17,7 @@ export { FontFamilyControl } from './controls/font-family-control/font-family-co
17
17
  export { ItemSelector } from './components/item-selector';
18
18
  export { UrlControl } from './controls/url-control';
19
19
  export { LinkControl } from './controls/link-control';
20
+ export { HtmlTagControl } from './controls/html-tag-control';
20
21
  export { QueryControl } from './controls/query-control';
21
22
  export { GapControl } from './controls/gap-control';
22
23
  export { AspectRatioControl } from './controls/aspect-ratio-control';