@elementor/editor-controls 4.0.0-498 → 4.0.0-499

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": "4.0.0-498",
4
+ "version": "4.0.0-499",
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": "4.0.0-498",
44
- "@elementor/editor-elements": "4.0.0-498",
45
- "@elementor/editor-props": "4.0.0-498",
46
- "@elementor/editor-responsive": "4.0.0-498",
47
- "@elementor/editor-ui": "4.0.0-498",
48
- "@elementor/editor-v1-adapters": "4.0.0-498",
49
- "@elementor/env": "4.0.0-498",
50
- "@elementor/http-client": "4.0.0-498",
43
+ "@elementor/editor-current-user": "4.0.0-499",
44
+ "@elementor/editor-elements": "4.0.0-499",
45
+ "@elementor/editor-props": "4.0.0-499",
46
+ "@elementor/editor-responsive": "4.0.0-499",
47
+ "@elementor/editor-ui": "4.0.0-499",
48
+ "@elementor/editor-v1-adapters": "4.0.0-499",
49
+ "@elementor/env": "4.0.0-499",
50
+ "@elementor/http-client": "4.0.0-499",
51
51
  "@elementor/icons": "^1.63.0",
52
- "@elementor/locations": "4.0.0-498",
53
- "@elementor/mixpanel": "4.0.0-498",
54
- "@elementor/query": "4.0.0-498",
55
- "@elementor/session": "4.0.0-498",
52
+ "@elementor/locations": "4.0.0-499",
53
+ "@elementor/mixpanel": "4.0.0-499",
54
+ "@elementor/query": "4.0.0-499",
55
+ "@elementor/session": "4.0.0-499",
56
56
  "@elementor/ui": "1.36.17",
57
- "@elementor/utils": "4.0.0-498",
58
- "@elementor/wp-media": "4.0.0-498",
57
+ "@elementor/utils": "4.0.0-499",
58
+ "@elementor/wp-media": "4.0.0-499",
59
59
  "@wordpress/i18n": "^5.13.0",
60
60
  "@monaco-editor/react": "^4.7.0",
61
61
  "dayjs": "^1.11.18",
@@ -0,0 +1,69 @@
1
+ import * as React from 'react';
2
+ import { useId } from 'react';
3
+ import { type SizePropValue } from '@elementor/editor-props';
4
+ import { MenuListItem } from '@elementor/editor-ui';
5
+ import { bindMenu, bindTrigger, Button, Menu, styled, usePopupState } from '@elementor/ui';
6
+
7
+ type Props< T = SizePropValue[ 'value' ][ 'unit' ] > = {
8
+ options: T[];
9
+ value: T;
10
+ onClick: ( value: T ) => void;
11
+ showPrimaryColor: boolean;
12
+ };
13
+
14
+ const menuItemContentStyles = {
15
+ display: 'flex',
16
+ flexDirection: 'column',
17
+ justifyContent: 'center',
18
+ };
19
+
20
+ export const UnitSelect = ( { value, showPrimaryColor, onClick, options }: Props ) => {
21
+ const popupState = usePopupState( {
22
+ variant: 'popover',
23
+ popupId: useId(),
24
+ } );
25
+
26
+ const handleMenuItemClick = ( index: number ) => {
27
+ onClick( options[ index ] );
28
+
29
+ popupState.close();
30
+ };
31
+
32
+ return (
33
+ <>
34
+ <StyledButton isPrimaryColor={ showPrimaryColor } size="small" { ...bindTrigger( popupState ) }>
35
+ { value }
36
+ </StyledButton>
37
+
38
+ <Menu MenuListProps={ { dense: true } } { ...bindMenu( popupState ) }>
39
+ { options.map( ( option, index ) => (
40
+ <MenuListItem
41
+ key={ option }
42
+ onClick={ () => handleMenuItemClick( index ) }
43
+ primaryTypographyProps={ {
44
+ variant: 'caption',
45
+ sx: {
46
+ ...menuItemContentStyles,
47
+ lineHeight: '1',
48
+ },
49
+ } }
50
+ menuItemTextProps={ {
51
+ sx: menuItemContentStyles,
52
+ } }
53
+ >
54
+ { option.toUpperCase() }
55
+ </MenuListItem>
56
+ ) ) }
57
+ </Menu>
58
+ </>
59
+ );
60
+ };
61
+
62
+ const StyledButton = styled( Button, {
63
+ shouldForwardProp: ( prop ) => prop !== 'isPrimaryColor',
64
+ } )( ( { isPrimaryColor, theme } ) => ( {
65
+ color: isPrimaryColor ? theme.palette.text.primary : theme.palette.text.tertiary,
66
+ font: 'inherit',
67
+ minWidth: 'initial',
68
+ textTransform: 'uppercase',
69
+ } ) );
@@ -0,0 +1,63 @@
1
+ import * as React from 'react';
2
+ import type { PropValue, SizePropValue } from '@elementor/editor-props';
3
+ import { InputAdornment } from '@elementor/ui';
4
+
5
+ import { useSizeValue } from '../../hooks/use-size-value';
6
+ import { type Unit } from '../../utils/size-control';
7
+ import { UnitSelect } from './unit-select';
8
+ import { UnstableSizeInput } from './unstable-size-input';
9
+
10
+ type Props< TValue > = {
11
+ units: Unit[];
12
+ value: TValue;
13
+ defaultValue?: Partial< TValue >;
14
+ onChange: ( value: TValue ) => void;
15
+ onBlur?: ( event: React.FocusEvent< HTMLInputElement > ) => void;
16
+ };
17
+
18
+ const DEFAULT_VALUE: SizePropValue[ 'value' ] = {
19
+ unit: 'px',
20
+ size: 0,
21
+ };
22
+
23
+ export const UnstableSizeField = < T extends SizePropValue[ 'value' ] >( {
24
+ value,
25
+ defaultValue,
26
+ onChange,
27
+ onBlur,
28
+ units,
29
+ }: Props< T > ) => {
30
+ const { size, unit, setSize, setUnit } = useSizeValue< T >( value, onChange, {
31
+ ...DEFAULT_VALUE,
32
+ ...defaultValue,
33
+ } as T );
34
+
35
+ const shouldHighlightUnit = () => {
36
+ return hasValue( size );
37
+ };
38
+
39
+ return (
40
+ <UnstableSizeInput
41
+ type="number"
42
+ value={ size ?? '' }
43
+ onBlur={ onBlur }
44
+ onChange={ ( event ) => setSize( event.target.value ) }
45
+ InputProps={ {
46
+ endAdornment: (
47
+ <InputAdornment position="end">
48
+ <UnitSelect
49
+ options={ units }
50
+ value={ unit }
51
+ onClick={ setUnit }
52
+ showPrimaryColor={ shouldHighlightUnit() }
53
+ />
54
+ </InputAdornment>
55
+ ),
56
+ } }
57
+ />
58
+ );
59
+ };
60
+
61
+ const hasValue = ( value: PropValue ): boolean => {
62
+ return value !== null && value !== '';
63
+ };
@@ -0,0 +1,46 @@
1
+ import * as React from 'react';
2
+ import { forwardRef } from 'react';
3
+ import type { PropValue } from '@elementor/editor-props';
4
+ import type { TextFieldProps } from '@elementor/ui';
5
+
6
+ import { NumberInput } from '../number-input';
7
+
8
+ type Props = {
9
+ type: 'number' | 'text';
10
+ value: PropValue;
11
+ focused?: boolean;
12
+ disabled?: boolean;
13
+ onChange: ( event: React.ChangeEvent< HTMLInputElement > ) => void;
14
+ onKeyDown?: ( event: React.KeyboardEvent< HTMLInputElement > ) => void;
15
+ onKeyUp?: ( event: React.KeyboardEvent< HTMLInputElement > ) => void;
16
+ onBlur?: ( event: React.FocusEvent< HTMLInputElement > ) => void;
17
+ InputProps: TextFieldProps[ 'InputProps' ] & {
18
+ endAdornment: React.JSX.Element;
19
+ };
20
+ };
21
+
22
+ export const UnstableSizeInput = forwardRef(
23
+ ( { type, value, onChange, onKeyDown, onKeyUp, InputProps, onBlur, focused, disabled }: Props, ref ) => {
24
+ return (
25
+ <NumberInput
26
+ ref={ ref }
27
+ size="tiny"
28
+ fullWidth
29
+ type={ type }
30
+ value={ value }
31
+ onKeyUp={ onKeyUp }
32
+ focused={ focused }
33
+ disabled={ disabled }
34
+ onKeyDown={ onKeyDown }
35
+ onInput={ onChange }
36
+ onBlur={ onBlur }
37
+ InputProps={ InputProps }
38
+ sx={ getCursorStyle( InputProps?.readOnly ?? false ) }
39
+ />
40
+ );
41
+ }
42
+ );
43
+
44
+ const getCursorStyle = ( readOnly: boolean ) => ( {
45
+ input: { cursor: readOnly ? 'default !important' : undefined },
46
+ } );
@@ -0,0 +1,49 @@
1
+ import { type SizePropValue } from '@elementor/editor-props';
2
+
3
+ import { useSyncExternalState } from '../hooks/use-sync-external-state';
4
+
5
+ export const useSizeValue = < T extends SizePropValue[ 'value' ] >(
6
+ externalValue: T,
7
+ onChange: ( value: T ) => void,
8
+ defaultValue: T
9
+ ) => {
10
+ const [ sizeValue, setSizeValue ] = useSyncExternalState< T >( {
11
+ external: externalValue,
12
+ setExternal: ( newState ) => {
13
+ if ( newState !== null ) {
14
+ onChange( newState as T );
15
+ }
16
+ },
17
+ persistWhen: ( newState ) => differsFromExternal( newState as T, externalValue ),
18
+ fallback: () => defaultValue,
19
+ } );
20
+
21
+ const setSize = ( value: string ) => {
22
+ const newState = {
23
+ ...sizeValue,
24
+ size: value.trim() === '' ? null : Number( value ),
25
+ };
26
+
27
+ setSizeValue( newState );
28
+ };
29
+
30
+ const setUnit = ( unit: SizePropValue[ 'value' ][ 'unit' ] ) => {
31
+ const newState = {
32
+ ...sizeValue,
33
+ unit,
34
+ };
35
+
36
+ setSizeValue( newState );
37
+ };
38
+
39
+ return {
40
+ size: sizeValue.size,
41
+ unit: sizeValue.unit,
42
+ setSize,
43
+ setUnit,
44
+ };
45
+ };
46
+
47
+ const differsFromExternal = ( newState: SizePropValue[ 'value' ], externalState: SizePropValue[ 'value' ] ) => {
48
+ return newState.size !== externalState.size || newState.unit !== externalState.unit;
49
+ };
package/src/index.ts CHANGED
@@ -51,6 +51,7 @@ export { FloatingActionsBar } from './components/floating-bar';
51
51
  export { PopoverGridContainer } from './components/popover-grid-container';
52
52
  export { InlineEditor } from './components/inline-editor';
53
53
  export { InlineEditorToolbar } from './components/inline-editor-toolbar';
54
+ export { UnstableSizeField } from './components/size/unstable-size-field';
54
55
 
55
56
  // types
56
57
  export type { ControlComponent } from './create-control';