@elementor/editor-controls 4.1.0-manual → 4.2.0-839
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/dist/index.d.mts +43 -21
- package/dist/index.d.ts +43 -21
- package/dist/index.js +675 -369
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +606 -305
- package/dist/index.mjs.map +1 -1
- package/package.json +16 -16
- package/src/components/control-repeater/context/repeater-context.tsx +13 -0
- package/src/components/repeater/repeater.tsx +6 -10
- package/src/controls/attachment-type-control.tsx +36 -0
- package/src/controls/date-range-control.tsx +83 -0
- package/src/controls/date-string-control.tsx +62 -0
- package/src/controls/email-form-action-control.tsx +21 -1
- package/src/controls/image-media-control.tsx +23 -1
- package/src/controls/mention-text-area-control.tsx +32 -11
- package/src/controls/number-control.tsx +4 -2
- package/src/controls/position-control.tsx +7 -0
- package/src/controls/select-control.tsx +1 -1
- package/src/controls/size-control/size-field.tsx +1 -1
- package/src/controls/time-range-control.tsx +49 -0
- package/src/controls/time-string-control.tsx +62 -0
- package/src/controls/transform-control/transform-base-controls/transform-origin-control.tsx +1 -1
- package/src/hooks/use-form-field-suggestions.ts +23 -2
- package/src/hooks/use-repeater-popover-dismiss.ts +53 -0
- package/src/index.ts +4 -0
- package/src/utils/date-time.ts +41 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type { Dayjs } from 'dayjs';
|
|
3
|
+
import { timeStringPropTypeUtil } from '@elementor/editor-props';
|
|
4
|
+
import { LocalizationProvider, TimePicker } from '@elementor/ui';
|
|
5
|
+
|
|
6
|
+
import { useBoundProp } from '../bound-prop-context';
|
|
7
|
+
import ControlActions from '../control-actions/control-actions';
|
|
8
|
+
import { createControl } from '../create-control';
|
|
9
|
+
import { isValidDayjs, parseTimeString, TIME_FORMAT } from '../utils/date-time';
|
|
10
|
+
|
|
11
|
+
type TimeStringControlProps = {
|
|
12
|
+
inputDisabled?: boolean;
|
|
13
|
+
ariaLabel?: string;
|
|
14
|
+
error?: boolean;
|
|
15
|
+
coerceInvalidToNull?: boolean;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const TimeStringControl = createControl(
|
|
19
|
+
( { inputDisabled, ariaLabel, error, coerceInvalidToNull = false }: TimeStringControlProps ) => {
|
|
20
|
+
const { value, setValue, disabled } = useBoundProp( timeStringPropTypeUtil );
|
|
21
|
+
|
|
22
|
+
const isDisabled = inputDisabled ?? disabled;
|
|
23
|
+
|
|
24
|
+
const slotProps = {
|
|
25
|
+
textField: {
|
|
26
|
+
size: 'tiny' as const,
|
|
27
|
+
fullWidth: true,
|
|
28
|
+
error,
|
|
29
|
+
inputProps: ariaLabel ? { 'aria-label': ariaLabel } : undefined,
|
|
30
|
+
},
|
|
31
|
+
openPickerButton: { size: 'tiny' as const },
|
|
32
|
+
openPickerIcon: { fontSize: 'tiny' as const },
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const handleChange = ( newValue: Dayjs | null, format: string ) => {
|
|
36
|
+
if ( ! newValue ) {
|
|
37
|
+
setValue( null );
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if ( coerceInvalidToNull && ! isValidDayjs( newValue ) ) {
|
|
42
|
+
setValue( null );
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
setValue( newValue.format( format ) );
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<LocalizationProvider>
|
|
51
|
+
<ControlActions>
|
|
52
|
+
<TimePicker
|
|
53
|
+
value={ parseTimeString( value ?? '' ) }
|
|
54
|
+
onChange={ ( newValue: Dayjs | null ) => handleChange( newValue, TIME_FORMAT ) }
|
|
55
|
+
disabled={ isDisabled }
|
|
56
|
+
slotProps={ slotProps }
|
|
57
|
+
/>
|
|
58
|
+
</ControlActions>
|
|
59
|
+
</LocalizationProvider>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
);
|
|
@@ -54,7 +54,7 @@ const ControlFields = ( { control }: { control: ( typeof TRANSFORM_ORIGIN_FIELDS
|
|
|
54
54
|
<ControlFormLabel>{ control.label }</ControlFormLabel>
|
|
55
55
|
</Grid>
|
|
56
56
|
<Grid item xs={ 6 }>
|
|
57
|
-
<SizeControl variant="length" units={ control.units } anchorRef={ rowRef }
|
|
57
|
+
<SizeControl variant="length" units={ control.units } anchorRef={ rowRef } />
|
|
58
58
|
</Grid>
|
|
59
59
|
</PopoverGridContainer>
|
|
60
60
|
</PropKeyProvider>
|
|
@@ -7,9 +7,21 @@ export type Suggestion = {
|
|
|
7
7
|
value: string;
|
|
8
8
|
};
|
|
9
9
|
|
|
10
|
-
const FORM_FIELD_WIDGET_TYPES = [
|
|
10
|
+
const FORM_FIELD_WIDGET_TYPES = [
|
|
11
|
+
'e-form-input',
|
|
12
|
+
'e-form-textarea',
|
|
13
|
+
'e-form-checkbox',
|
|
14
|
+
'e-form-radio-button',
|
|
15
|
+
'e-form-select',
|
|
16
|
+
'e-form-date-picker',
|
|
17
|
+
'e-form-time-picker',
|
|
18
|
+
];
|
|
11
19
|
|
|
12
|
-
|
|
20
|
+
type Options = {
|
|
21
|
+
inputType?: string;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export function useFormFieldSuggestions( options?: Options ): Suggestion[] {
|
|
13
25
|
return useListenTo(
|
|
14
26
|
[
|
|
15
27
|
v1ReadyEvent(),
|
|
@@ -40,6 +52,15 @@ export function useFormFieldSuggestions(): Suggestion[] {
|
|
|
40
52
|
return;
|
|
41
53
|
}
|
|
42
54
|
|
|
55
|
+
if ( options?.inputType ) {
|
|
56
|
+
const typeProp = child.settings.get( 'type' );
|
|
57
|
+
const typeValue = isTransformable( typeProp ) ? typeProp.value : typeProp;
|
|
58
|
+
|
|
59
|
+
if ( typeValue !== options.inputType ) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
43
64
|
const cssIdProp = child.settings.get( '_cssid' );
|
|
44
65
|
const fieldId = isTransformable( cssIdProp ) ? cssIdProp.value : cssIdProp;
|
|
45
66
|
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { useEffect, useMemo, useRef } from 'react';
|
|
2
|
+
import { type Breakpoint, useActiveBreakpoint, useBreakpoints } from '@elementor/editor-responsive';
|
|
3
|
+
|
|
4
|
+
const serializeBreakpoints = ( breakpoints: Breakpoint[] ) =>
|
|
5
|
+
breakpoints.map( ( b ) => [ b.id, b.width ?? '', b.type ?? '' ].join( ':' ) ).join( '|' );
|
|
6
|
+
|
|
7
|
+
export const usePopoverDismiss = ( { isOpen, onClose }: { isOpen: boolean; onClose: () => void } ) => {
|
|
8
|
+
const onCloseRef = useRef( onClose );
|
|
9
|
+
onCloseRef.current = onClose;
|
|
10
|
+
|
|
11
|
+
const activeBreakpoint = useActiveBreakpoint();
|
|
12
|
+
const breakpoints = useBreakpoints();
|
|
13
|
+
const breakpointsSignature = useMemo( () => serializeBreakpoints( breakpoints ), [ breakpoints ] );
|
|
14
|
+
|
|
15
|
+
const prevActiveBreakpointRef = useRef< typeof activeBreakpoint >( undefined );
|
|
16
|
+
const prevBreakpointsSignatureRef = useRef< string | null >( null );
|
|
17
|
+
|
|
18
|
+
useEffect( () => {
|
|
19
|
+
if ( ! isOpen ) {
|
|
20
|
+
prevActiveBreakpointRef.current = activeBreakpoint;
|
|
21
|
+
prevBreakpointsSignatureRef.current = breakpointsSignature;
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const previousBreakpoint = prevActiveBreakpointRef.current;
|
|
26
|
+
const previousSignature = prevBreakpointsSignatureRef.current;
|
|
27
|
+
|
|
28
|
+
const breakpointChanged = previousBreakpoint !== undefined && previousBreakpoint !== activeBreakpoint;
|
|
29
|
+
const breakpointsChanged = previousSignature !== null && previousSignature !== breakpointsSignature;
|
|
30
|
+
|
|
31
|
+
if ( breakpointChanged || breakpointsChanged ) {
|
|
32
|
+
onCloseRef.current();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
prevActiveBreakpointRef.current = activeBreakpoint;
|
|
36
|
+
prevBreakpointsSignatureRef.current = breakpointsSignature;
|
|
37
|
+
}, [ activeBreakpoint, breakpointsSignature, isOpen ] );
|
|
38
|
+
|
|
39
|
+
useEffect( () => {
|
|
40
|
+
if ( ! isOpen ) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const onKeyDown = ( event: KeyboardEvent ) => {
|
|
44
|
+
if ( event.key === 'Escape' && ! event.defaultPrevented ) {
|
|
45
|
+
onCloseRef.current();
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
document.addEventListener( 'keydown', onKeyDown );
|
|
49
|
+
return () => {
|
|
50
|
+
document.removeEventListener( 'keydown', onKeyDown );
|
|
51
|
+
};
|
|
52
|
+
}, [ isOpen ] );
|
|
53
|
+
};
|
package/src/index.ts
CHANGED
|
@@ -37,8 +37,12 @@ export { PopoverContent } from './components/popover-content';
|
|
|
37
37
|
export { enqueueFont } from './controls/font-family-control/enqueue-font';
|
|
38
38
|
export { transitionProperties, transitionsItemsList } from './controls/transition-control/data';
|
|
39
39
|
export { DateTimeControl } from './controls/date-time-control';
|
|
40
|
+
export { DateRangeControl } from './controls/date-range-control';
|
|
41
|
+
export { TimeStringControl } from './controls/time-string-control';
|
|
42
|
+
export { TimeRangeControl } from './controls/time-range-control';
|
|
40
43
|
export { InlineEditingControl } from './controls/inline-editing-control';
|
|
41
44
|
export { EmailFormActionControl } from './controls/email-form-action-control';
|
|
45
|
+
export { AttachmentTypeControl } from './controls/attachment-type-control';
|
|
42
46
|
export { UnstableSizeControl } from './controls/size-control/unstable-size-control';
|
|
43
47
|
|
|
44
48
|
// components
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { Dayjs } from 'dayjs';
|
|
2
|
+
import * as dayjs from 'dayjs';
|
|
3
|
+
|
|
4
|
+
export const DATE_FORMAT = 'YYYY-MM-DD';
|
|
5
|
+
export const TIME_FORMAT = 'HH:mm';
|
|
6
|
+
|
|
7
|
+
export function isValidDayjs( value: Dayjs | null ): value is Dayjs {
|
|
8
|
+
return !! value && typeof value.isValid === 'function' && value.isValid();
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function parseDateString( raw: string ): Dayjs | null {
|
|
12
|
+
if ( ! raw ) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const parsed = ( dayjs as unknown as { default: ( s?: string | number | Date ) => Dayjs } ).default( raw );
|
|
17
|
+
|
|
18
|
+
return isValidDayjs( parsed ) ? parsed : null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function parseTimeString( raw: string ): Dayjs | null {
|
|
22
|
+
if ( ! raw ) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const [ hours, minutes, seconds ] = raw.split( ':' );
|
|
27
|
+
const h = Number.parseInt( hours ?? '', 10 );
|
|
28
|
+
const m = Number.parseInt( minutes ?? '', 10 );
|
|
29
|
+
const s = Number.parseInt( seconds ?? '0', 10 );
|
|
30
|
+
|
|
31
|
+
if ( Number.isNaN( h ) || Number.isNaN( m ) ) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const base = ( dayjs as unknown as { default: () => Dayjs } ).default();
|
|
36
|
+
return base
|
|
37
|
+
.hour( h )
|
|
38
|
+
.minute( m )
|
|
39
|
+
.second( Number.isNaN( s ) ? 0 : s )
|
|
40
|
+
.millisecond( 0 );
|
|
41
|
+
}
|