@elementor/editor-interactions 4.0.0-manual → 4.0.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/dist/index.d.mts +123 -5
- package/dist/index.d.ts +123 -5
- package/dist/index.js +861 -334
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +823 -312
- package/dist/index.mjs.map +1 -1
- package/package.json +12 -11
- package/src/commands/get-clipboard-elements.ts +11 -0
- package/src/commands/paste-interactions.ts +136 -0
- package/src/components/controls/easing.tsx +3 -0
- package/src/components/controls/effect.tsx +3 -0
- package/src/components/controls/repeat.tsx +57 -0
- package/src/components/controls/replay.tsx +15 -13
- package/src/components/controls/trigger.tsx +3 -0
- package/src/components/interaction-details.tsx +162 -120
- package/src/components/interactions-list.tsx +16 -3
- package/src/components/interactions-tab.tsx +4 -1
- package/src/contexts/interactions-context.tsx +4 -1
- package/src/hooks/use-element-interactions.ts +26 -0
- package/src/index.ts +26 -0
- package/src/init.ts +13 -3
- package/src/interactions-controls-registry.ts +15 -3
- package/src/mcp/constants.ts +58 -0
- package/src/mcp/index.ts +15 -69
- package/src/mcp/tools/manage-element-interaction-tool.ts +40 -5
- package/src/mcp/tools/schema.ts +1 -1
- package/src/types.ts +6 -1
- package/src/ui/interactions-promotion-chip.tsx +14 -6
- package/src/ui/promotion-overlay-layout.tsx +22 -0
- package/src/ui/promotion-select.tsx +4 -0
- package/src/utils/custom-effect-to-prop-value.ts +145 -0
- package/src/utils/filter-interactions.ts +9 -0
- package/src/utils/get-interactions-config.ts +1 -11
- package/src/utils/is-supported-interaction-item.ts +39 -0
- package/src/utils/prop-value-utils.ts +59 -34
- package/src/utils/tracking.ts +42 -0
package/src/mcp/constants.ts
CHANGED
|
@@ -1 +1,59 @@
|
|
|
1
1
|
export const MAX_INTERACTIONS_PER_ELEMENT = 5;
|
|
2
|
+
export const EDITOR_INTERACTIONS_MCP_INSTRUCTIONS = `MCP server for managing element interactions and animations. Use this to add, modify, or remove animations and motion effects triggered by user events such as page load or scroll-into-view.
|
|
3
|
+
** IMPORTANT **
|
|
4
|
+
Use the "interactions-schema" resource to get the schema of the interactions.
|
|
5
|
+
Actions:
|
|
6
|
+
- get: Read the current interactions on the element.
|
|
7
|
+
- add: Add a new interaction (max ${ MAX_INTERACTIONS_PER_ELEMENT } per element).
|
|
8
|
+
- update: Update an existing interaction by its interactionId.
|
|
9
|
+
- delete: Remove a specific interaction by its interactionId.
|
|
10
|
+
- clear: Remove all interactions from the element.
|
|
11
|
+
|
|
12
|
+
For add/update, provide: trigger, effect, effectType, direction (required for slide effect), duration, delay, easing.
|
|
13
|
+
Use excludedBreakpoints to disable the animation on specific responsive breakpoints (e.g. ["mobile", "tablet"]).
|
|
14
|
+
Example Get Request:
|
|
15
|
+
{
|
|
16
|
+
"elementId": "123",
|
|
17
|
+
"action": "get",
|
|
18
|
+
"interactionId": "123",
|
|
19
|
+
"animationData": {
|
|
20
|
+
"trigger": "click",
|
|
21
|
+
"effect": "fade",
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
Example Add Request:
|
|
25
|
+
{
|
|
26
|
+
"elementId": "123",
|
|
27
|
+
"action": "add",
|
|
28
|
+
"animationData": {
|
|
29
|
+
"effectType": "in",
|
|
30
|
+
"direction": "top",
|
|
31
|
+
"trigger": "click",
|
|
32
|
+
"effect": "fade",
|
|
33
|
+
"duration": 1000,
|
|
34
|
+
"delay": 0,
|
|
35
|
+
"easing": "easeIn",
|
|
36
|
+
"excludedBreakpoints": ["mobile", "tablet"],
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
Example Update Request:
|
|
40
|
+
{
|
|
41
|
+
"elementId": "123",
|
|
42
|
+
"action": "update",
|
|
43
|
+
"interactionId": "123",
|
|
44
|
+
"animationData": {
|
|
45
|
+
"trigger": "click",
|
|
46
|
+
"effect": "fade",
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
Example Delete Request:
|
|
50
|
+
{
|
|
51
|
+
"elementId": "123",
|
|
52
|
+
"action": "delete",
|
|
53
|
+
"interactionId": "123",
|
|
54
|
+
}
|
|
55
|
+
Example Clear Request:
|
|
56
|
+
{
|
|
57
|
+
"elementId": "123",
|
|
58
|
+
"action": "clear",
|
|
59
|
+
}`;
|
package/src/mcp/index.ts
CHANGED
|
@@ -1,74 +1,20 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { type MCPRegistryEntry } from '@elementor/editor-mcp';
|
|
2
2
|
|
|
3
|
-
import { MAX_INTERACTIONS_PER_ELEMENT } from './constants';
|
|
4
3
|
import { initInteractionsSchemaResource } from './resources/interactions-schema-resource';
|
|
5
4
|
import { initManageElementInteractionTool } from './tools/manage-element-interaction-tool';
|
|
6
5
|
|
|
7
|
-
export
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
Use excludedBreakpoints to disable the animation on specific responsive breakpoints (e.g. ["mobile", "tablet"]).
|
|
22
|
-
Example Get Request:
|
|
23
|
-
{
|
|
24
|
-
"elementId": "123",
|
|
25
|
-
"action": "get",
|
|
26
|
-
"interactionId": "123",
|
|
27
|
-
"animationData": {
|
|
28
|
-
"trigger": "click",
|
|
29
|
-
"effect": "fade",
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
Example Add Request:
|
|
33
|
-
{
|
|
34
|
-
"elementId": "123",
|
|
35
|
-
"action": "add",
|
|
36
|
-
"animationData": {
|
|
37
|
-
"effectType": "in",
|
|
38
|
-
"direction": "top",
|
|
39
|
-
"trigger": "click",
|
|
40
|
-
"effect": "fade",
|
|
41
|
-
"duration": 1000,
|
|
42
|
-
"delay": 0,
|
|
43
|
-
"easing": "easeIn",
|
|
44
|
-
"excludedBreakpoints": ["mobile", "tablet"],
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
Example Update Request:
|
|
48
|
-
{
|
|
49
|
-
"elementId": "123",
|
|
50
|
-
"action": "update",
|
|
51
|
-
"interactionId": "123",
|
|
52
|
-
"animationData": {
|
|
53
|
-
"trigger": "click",
|
|
54
|
-
"effect": "fade",
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
Example Delete Request:
|
|
58
|
-
{
|
|
59
|
-
"elementId": "123",
|
|
60
|
-
"action": "delete",
|
|
61
|
-
"interactionId": "123",
|
|
62
|
-
}
|
|
63
|
-
Example Clear Request:
|
|
64
|
-
{
|
|
65
|
-
"elementId": "123",
|
|
66
|
-
"action": "clear",
|
|
67
|
-
}
|
|
68
|
-
`,
|
|
69
|
-
} );
|
|
70
|
-
reg.waitForReady().then( () => {
|
|
71
|
-
initInteractionsSchemaResource( reg );
|
|
72
|
-
initManageElementInteractionTool( reg );
|
|
73
|
-
} );
|
|
6
|
+
export * from './constants';
|
|
7
|
+
|
|
8
|
+
export const initMcpInteractions = ( reg: MCPRegistryEntry ) => {
|
|
9
|
+
const { setMCPDescription } = reg;
|
|
10
|
+
setMCPDescription(
|
|
11
|
+
`Everything related to V4 ( Atomic ) interactions.
|
|
12
|
+
# Interactions
|
|
13
|
+
- Create/update/delete interactions
|
|
14
|
+
- Get list of interactions
|
|
15
|
+
- Get details of an interaction
|
|
16
|
+
`
|
|
17
|
+
);
|
|
18
|
+
initInteractionsSchemaResource( reg );
|
|
19
|
+
initManageElementInteractionTool( reg );
|
|
74
20
|
};
|
|
@@ -5,7 +5,12 @@ import { isProActive } from '@elementor/utils';
|
|
|
5
5
|
|
|
6
6
|
import { interactionsRepository } from '../../interactions-repository';
|
|
7
7
|
import { type ElementInteractions } from '../../types';
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
createInteractionItem,
|
|
10
|
+
extractExcludedBreakpoints,
|
|
11
|
+
extractSize,
|
|
12
|
+
extractString,
|
|
13
|
+
} from '../../utils/prop-value-utils';
|
|
9
14
|
import { generateTempInteractionId } from '../../utils/temp-id-utils';
|
|
10
15
|
import { MAX_INTERACTIONS_PER_ELEMENT } from '../constants';
|
|
11
16
|
import { INTERACTIONS_SCHEMA_URI } from '../resources/interactions-schema-resource';
|
|
@@ -16,6 +21,8 @@ const EMPTY_INTERACTIONS: ElementInteractions = {
|
|
|
16
21
|
items: [],
|
|
17
22
|
};
|
|
18
23
|
|
|
24
|
+
const EFFECTS_WITHOUT_TYPE = [ 'custom' ];
|
|
25
|
+
|
|
19
26
|
export const initManageElementInteractionTool = ( reg: MCPRegistryEntry ) => {
|
|
20
27
|
const { addTool } = reg;
|
|
21
28
|
const extendedSchema = isProActive() ? { ...baseSchema, ...proSchema } : baseSchema;
|
|
@@ -55,18 +62,44 @@ export const initManageElementInteractionTool = ( reg: MCPRegistryEntry ) => {
|
|
|
55
62
|
[ key: string ]: unknown;
|
|
56
63
|
} ) => {
|
|
57
64
|
const { elementId, action, interactionId, ...animationData } = input;
|
|
65
|
+
const { effectType, ...restAnimationData } = animationData as {
|
|
66
|
+
effectType?: string;
|
|
67
|
+
[ key: string ]: unknown;
|
|
68
|
+
};
|
|
69
|
+
const effect = restAnimationData.effect as string | undefined;
|
|
70
|
+
const resolvedType =
|
|
71
|
+
effectType ?? ( effect && ! EFFECTS_WITHOUT_TYPE.includes( effect ) ? 'in' : undefined );
|
|
58
72
|
|
|
59
73
|
const allInteractions = interactionsRepository.all();
|
|
60
74
|
const elementData = allInteractions.find( ( data ) => data.elementId === elementId );
|
|
61
75
|
const currentInteractions: ElementInteractions = elementData?.interactions ?? EMPTY_INTERACTIONS;
|
|
62
76
|
|
|
63
77
|
if ( action === 'get' ) {
|
|
78
|
+
const summary = currentInteractions.items.map( ( item ) => {
|
|
79
|
+
const { value } = item;
|
|
80
|
+
const animValue = value.animation.value;
|
|
81
|
+
const timingValue = animValue.timing_config.value;
|
|
82
|
+
const configValue = animValue.config.value;
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
id: extractString( value.interaction_id ),
|
|
86
|
+
trigger: extractString( value.trigger ),
|
|
87
|
+
effect: extractString( animValue.effect ),
|
|
88
|
+
effectType: extractString( animValue.type ),
|
|
89
|
+
direction: extractString( animValue.direction ),
|
|
90
|
+
duration: extractSize( timingValue.duration ),
|
|
91
|
+
delay: extractSize( timingValue.delay ),
|
|
92
|
+
easing: extractString( configValue.easing ),
|
|
93
|
+
excludedBreakpoints: extractExcludedBreakpoints( value.breakpoints ),
|
|
94
|
+
};
|
|
95
|
+
} );
|
|
96
|
+
|
|
64
97
|
return {
|
|
65
98
|
success: true,
|
|
66
99
|
elementId,
|
|
67
100
|
action,
|
|
68
|
-
interactions:
|
|
69
|
-
count:
|
|
101
|
+
interactions: summary,
|
|
102
|
+
count: summary.length,
|
|
70
103
|
};
|
|
71
104
|
}
|
|
72
105
|
|
|
@@ -82,7 +115,8 @@ export const initManageElementInteractionTool = ( reg: MCPRegistryEntry ) => {
|
|
|
82
115
|
|
|
83
116
|
const newItem = createInteractionItem( {
|
|
84
117
|
interactionId: generateTempInteractionId(),
|
|
85
|
-
...
|
|
118
|
+
...restAnimationData,
|
|
119
|
+
type: resolvedType,
|
|
86
120
|
} );
|
|
87
121
|
|
|
88
122
|
updatedItems = [ ...updatedItems, newItem ];
|
|
@@ -106,7 +140,8 @@ export const initManageElementInteractionTool = ( reg: MCPRegistryEntry ) => {
|
|
|
106
140
|
|
|
107
141
|
const updatedItem = createInteractionItem( {
|
|
108
142
|
interactionId,
|
|
109
|
-
...
|
|
143
|
+
...restAnimationData,
|
|
144
|
+
type: resolvedType,
|
|
110
145
|
} );
|
|
111
146
|
|
|
112
147
|
updatedItems = [
|
package/src/mcp/tools/schema.ts
CHANGED
|
@@ -25,7 +25,7 @@ export const proSchema = {
|
|
|
25
25
|
.optional()
|
|
26
26
|
.describe( 'Event that triggers the animation' ),
|
|
27
27
|
effect: z.enum( [ 'fade', 'slide', 'scale', 'custom' ] ).optional().describe( 'Animation effect type' ),
|
|
28
|
-
|
|
28
|
+
customEffects: z
|
|
29
29
|
.object( {
|
|
30
30
|
keyframes: z
|
|
31
31
|
.array(
|
package/src/types.ts
CHANGED
|
@@ -31,7 +31,10 @@ export type InteractionConstants = {
|
|
|
31
31
|
defaultDelay: number;
|
|
32
32
|
slideDistance: number;
|
|
33
33
|
scaleStart: number;
|
|
34
|
-
|
|
34
|
+
defaultEasing: string;
|
|
35
|
+
relativeTo: string;
|
|
36
|
+
start: number;
|
|
37
|
+
end: number;
|
|
35
38
|
};
|
|
36
39
|
|
|
37
40
|
export type InteractionsConfig = {
|
|
@@ -47,6 +50,8 @@ export type FieldProps< T = string > = {
|
|
|
47
50
|
};
|
|
48
51
|
|
|
49
52
|
export type ReplayFieldProps = FieldProps< boolean >;
|
|
53
|
+
export type RepeatFieldProps = FieldProps< string >;
|
|
54
|
+
export type TimesFieldProps = FieldProps< number >;
|
|
50
55
|
export type DirectionFieldProps = FieldProps< string > & {
|
|
51
56
|
interactionType: string;
|
|
52
57
|
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { forwardRef, type MouseEvent, type RefObject, useImperativeHandle, useState } from 'react';
|
|
2
|
+
import { forwardRef, type MouseEvent, type RefObject, useCallback, useImperativeHandle, useState } from 'react';
|
|
3
|
+
import { type PromotionTrackingData, trackUpgradePromotionClick, trackViewPromotion } from '@elementor/editor-controls';
|
|
3
4
|
import { PromotionChip, PromotionPopover, useCanvasClickHandler } from '@elementor/editor-ui';
|
|
4
5
|
import { Box } from '@elementor/ui';
|
|
5
6
|
import { __ } from '@wordpress/i18n';
|
|
@@ -8,6 +9,7 @@ export type InteractionsPromotionChipProps = {
|
|
|
8
9
|
content: string;
|
|
9
10
|
upgradeUrl: string;
|
|
10
11
|
anchorRef?: RefObject< HTMLElement | null >;
|
|
12
|
+
trackingData: PromotionTrackingData;
|
|
11
13
|
};
|
|
12
14
|
|
|
13
15
|
export type InteractionsPromotionChipRef = {
|
|
@@ -15,14 +17,21 @@ export type InteractionsPromotionChipRef = {
|
|
|
15
17
|
};
|
|
16
18
|
|
|
17
19
|
export const InteractionsPromotionChip = forwardRef< InteractionsPromotionChipRef, InteractionsPromotionChipProps >(
|
|
18
|
-
( { content, upgradeUrl, anchorRef }, ref ) => {
|
|
20
|
+
( { content, upgradeUrl, anchorRef, trackingData }, ref ) => {
|
|
19
21
|
const [ isOpen, setIsOpen ] = useState( false );
|
|
20
22
|
|
|
21
23
|
useCanvasClickHandler( isOpen, () => setIsOpen( false ) );
|
|
22
24
|
|
|
23
|
-
const toggle = (
|
|
25
|
+
const toggle = useCallback( () => {
|
|
26
|
+
setIsOpen( ( prev ) => {
|
|
27
|
+
if ( ! prev ) {
|
|
28
|
+
trackViewPromotion( trackingData );
|
|
29
|
+
}
|
|
30
|
+
return ! prev;
|
|
31
|
+
} );
|
|
32
|
+
}, [ trackingData ] );
|
|
24
33
|
|
|
25
|
-
useImperativeHandle( ref, () => ( { toggle } ), [] );
|
|
34
|
+
useImperativeHandle( ref, () => ( { toggle } ), [ toggle ] );
|
|
26
35
|
|
|
27
36
|
const handleToggle = ( e: MouseEvent ) => {
|
|
28
37
|
e.stopPropagation();
|
|
@@ -42,6 +51,7 @@ export const InteractionsPromotionChip = forwardRef< InteractionsPromotionChipRe
|
|
|
42
51
|
e.stopPropagation();
|
|
43
52
|
setIsOpen( false );
|
|
44
53
|
} }
|
|
54
|
+
onCtaClick={ () => trackUpgradePromotionClick( trackingData ) }
|
|
45
55
|
>
|
|
46
56
|
<Box
|
|
47
57
|
onMouseDown={ ( e: MouseEvent ) => e.stopPropagation() }
|
|
@@ -54,5 +64,3 @@ export const InteractionsPromotionChip = forwardRef< InteractionsPromotionChipRe
|
|
|
54
64
|
);
|
|
55
65
|
}
|
|
56
66
|
);
|
|
57
|
-
|
|
58
|
-
InteractionsPromotionChip.displayName = 'InteractionsPromotionChip';
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { forwardRef, type ReactNode } from 'react';
|
|
3
|
+
import { Box } from '@elementor/ui';
|
|
4
|
+
|
|
5
|
+
const OVERLAY_GRID = '1 / 1';
|
|
6
|
+
const CHIP_OFFSET = '50%';
|
|
7
|
+
|
|
8
|
+
type PromotionOverlayLayoutProps = {
|
|
9
|
+
children: ReactNode;
|
|
10
|
+
promotionChip: ReactNode;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const PromotionOverlayLayout = forwardRef< HTMLDivElement, PromotionOverlayLayoutProps >(
|
|
14
|
+
( { children, promotionChip }, ref ) => (
|
|
15
|
+
<Box ref={ ref } sx={ { display: 'grid', alignItems: 'center' } }>
|
|
16
|
+
<Box sx={ { gridArea: OVERLAY_GRID } }>{ children }</Box>
|
|
17
|
+
<Box sx={ { gridArea: OVERLAY_GRID, marginInlineEnd: CHIP_OFFSET, justifySelf: 'end' } }>
|
|
18
|
+
{ promotionChip }
|
|
19
|
+
</Box>
|
|
20
|
+
</Box>
|
|
21
|
+
)
|
|
22
|
+
);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { type MouseEvent, useRef } from 'react';
|
|
3
|
+
import { type PromotionTrackingData } from '@elementor/editor-controls';
|
|
3
4
|
import { MenuListItem } from '@elementor/editor-ui';
|
|
4
5
|
import { MenuSubheader, Select, type SelectChangeEvent } from '@elementor/ui';
|
|
5
6
|
import { __ } from '@wordpress/i18n';
|
|
@@ -14,6 +15,7 @@ type PromotionSelectProps = {
|
|
|
14
15
|
promotionLabel?: string;
|
|
15
16
|
promotionContent: string;
|
|
16
17
|
upgradeUrl: string;
|
|
18
|
+
trackingData: PromotionTrackingData;
|
|
17
19
|
};
|
|
18
20
|
|
|
19
21
|
export function PromotionSelect( {
|
|
@@ -24,6 +26,7 @@ export function PromotionSelect( {
|
|
|
24
26
|
promotionLabel,
|
|
25
27
|
promotionContent,
|
|
26
28
|
upgradeUrl,
|
|
29
|
+
trackingData,
|
|
27
30
|
}: PromotionSelectProps ) {
|
|
28
31
|
const promotionRef = useRef< InteractionsPromotionChipRef >( null );
|
|
29
32
|
const anchorRef = useRef< HTMLElement >( null );
|
|
@@ -63,6 +66,7 @@ export function PromotionSelect( {
|
|
|
63
66
|
upgradeUrl={ upgradeUrl }
|
|
64
67
|
ref={ promotionRef }
|
|
65
68
|
anchorRef={ anchorRef }
|
|
69
|
+
trackingData={ trackingData }
|
|
66
70
|
/>
|
|
67
71
|
</MenuSubheader>
|
|
68
72
|
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { type PropValue } from '@elementor/editor-props';
|
|
2
|
+
|
|
3
|
+
const CUSTOM_EFFECT_TYPE = 'custom-effect';
|
|
4
|
+
const KEYFRAMES_TYPE = 'keyframes';
|
|
5
|
+
const KEYFRAME_STOP_TYPE = 'keyframe-stop';
|
|
6
|
+
const KEYFRAME_STOP_SETTINGS_TYPE = 'keyframe-stop-settings';
|
|
7
|
+
const SIZE_TYPE = 'size';
|
|
8
|
+
const NUMBER_TYPE = 'number';
|
|
9
|
+
const TRANSFORM_SCALE_TYPE = 'transform-scale';
|
|
10
|
+
const TRANSFORM_ROTATE_TYPE = 'transform-rotate';
|
|
11
|
+
const TRANSFORM_MOVE_TYPE = 'transform-move';
|
|
12
|
+
const TRANSFORM_SKEW_TYPE = 'transform-skew';
|
|
13
|
+
const UNIT_PERCENT = '%';
|
|
14
|
+
const UNIT_DEG = 'deg';
|
|
15
|
+
const UNIT_PX = 'px';
|
|
16
|
+
|
|
17
|
+
export type PlainKeyframeValue = {
|
|
18
|
+
opacity?: number;
|
|
19
|
+
scale?: { x: number; y: number };
|
|
20
|
+
rotate?: { x: number; y: number; z: number };
|
|
21
|
+
move?: { x: number; y: number; z: number };
|
|
22
|
+
skew?: { x: number; y: number };
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export type PlainKeyframe = { stop: number; value: PlainKeyframeValue };
|
|
26
|
+
|
|
27
|
+
export type PlainCustomEffect = { keyframes: PlainKeyframe[] };
|
|
28
|
+
|
|
29
|
+
const isPlainCustomEffect = ( v: unknown ): v is PlainCustomEffect =>
|
|
30
|
+
typeof v === 'object' &&
|
|
31
|
+
v !== null &&
|
|
32
|
+
'keyframes' in v &&
|
|
33
|
+
Array.isArray( ( v as PlainCustomEffect ).keyframes ) &&
|
|
34
|
+
! ( '$$type' in v );
|
|
35
|
+
|
|
36
|
+
const toSizePropValue = ( size: number, unit: string = UNIT_PERCENT ): PropValue => ( {
|
|
37
|
+
$$type: SIZE_TYPE,
|
|
38
|
+
value: { size, unit },
|
|
39
|
+
} );
|
|
40
|
+
|
|
41
|
+
const toNumberPropValue = ( n: number ): PropValue => ( {
|
|
42
|
+
$$type: NUMBER_TYPE,
|
|
43
|
+
value: n,
|
|
44
|
+
} );
|
|
45
|
+
|
|
46
|
+
const toDimensionalNumberPropValue = (
|
|
47
|
+
type: string,
|
|
48
|
+
plain: { x?: number; y?: number; z?: number },
|
|
49
|
+
defaults: { x: number; y: number; z: number }
|
|
50
|
+
): PropValue => ( {
|
|
51
|
+
$$type: type,
|
|
52
|
+
value: {
|
|
53
|
+
x: toNumberPropValue( plain.x ?? defaults.x ),
|
|
54
|
+
y: toNumberPropValue( plain.y ?? defaults.y ),
|
|
55
|
+
z: toNumberPropValue( plain.z ?? defaults.z ),
|
|
56
|
+
},
|
|
57
|
+
} );
|
|
58
|
+
|
|
59
|
+
const toDimensionalSizePropValue = (
|
|
60
|
+
type: string,
|
|
61
|
+
plain: { x?: number; y?: number; z?: number },
|
|
62
|
+
defaults: { x: number; y: number; z: number },
|
|
63
|
+
unit: string
|
|
64
|
+
): PropValue => ( {
|
|
65
|
+
$$type: type,
|
|
66
|
+
value: {
|
|
67
|
+
x: toSizePropValue( plain.x ?? defaults.x, unit ),
|
|
68
|
+
y: toSizePropValue( plain.y ?? defaults.y, unit ),
|
|
69
|
+
z: toSizePropValue( plain.z ?? defaults.z, unit ),
|
|
70
|
+
},
|
|
71
|
+
} );
|
|
72
|
+
|
|
73
|
+
const toSkewPropValue = ( plain: { x?: number; y?: number } ): PropValue => ( {
|
|
74
|
+
$$type: TRANSFORM_SKEW_TYPE,
|
|
75
|
+
value: {
|
|
76
|
+
x: toSizePropValue( plain.x ?? 0, UNIT_DEG ),
|
|
77
|
+
y: toSizePropValue( plain.y ?? 0, UNIT_DEG ),
|
|
78
|
+
},
|
|
79
|
+
} );
|
|
80
|
+
|
|
81
|
+
const toKeyframeStopSettingsPropValue = ( plain: PlainKeyframeValue ): PropValue => {
|
|
82
|
+
const value: Record< string, PropValue > = {};
|
|
83
|
+
if ( plain.opacity !== undefined ) {
|
|
84
|
+
const percent = plain.opacity <= 1 ? Math.round( plain.opacity * 100 ) : plain.opacity;
|
|
85
|
+
value.opacity = toSizePropValue( percent );
|
|
86
|
+
}
|
|
87
|
+
if ( plain.scale !== undefined ) {
|
|
88
|
+
value.scale = toDimensionalNumberPropValue( TRANSFORM_SCALE_TYPE, plain.scale, { x: 1, y: 1, z: 1 } );
|
|
89
|
+
}
|
|
90
|
+
if ( plain.rotate !== undefined ) {
|
|
91
|
+
value.rotate = toDimensionalSizePropValue(
|
|
92
|
+
TRANSFORM_ROTATE_TYPE,
|
|
93
|
+
plain.rotate,
|
|
94
|
+
{ x: 0, y: 0, z: 0 },
|
|
95
|
+
UNIT_DEG
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
if ( plain.move !== undefined ) {
|
|
99
|
+
value.move = toDimensionalSizePropValue( TRANSFORM_MOVE_TYPE, plain.move, { x: 0, y: 0, z: 0 }, UNIT_PX );
|
|
100
|
+
}
|
|
101
|
+
if ( plain.skew !== undefined ) {
|
|
102
|
+
value.skew = toSkewPropValue( plain.skew );
|
|
103
|
+
}
|
|
104
|
+
return { $$type: KEYFRAME_STOP_SETTINGS_TYPE, value };
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const isPlainKeyframe = ( v: unknown ): v is PlainKeyframe =>
|
|
108
|
+
typeof v === 'object' && v !== null && 'stop' in v && 'value' in v && ! ( '$$type' in v );
|
|
109
|
+
|
|
110
|
+
const toKeyframeStopPropValue = ( item: PlainKeyframe | PropValue ): PropValue => {
|
|
111
|
+
if ( ! isPlainKeyframe( item ) ) {
|
|
112
|
+
return item as PropValue;
|
|
113
|
+
}
|
|
114
|
+
return {
|
|
115
|
+
$$type: KEYFRAME_STOP_TYPE,
|
|
116
|
+
value: {
|
|
117
|
+
stop: toSizePropValue( item.stop ),
|
|
118
|
+
settings: toKeyframeStopSettingsPropValue( item.value ),
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
const toKeyframesPropValue = ( keyframes: ( PlainKeyframe | PropValue )[] ): PropValue => ( {
|
|
124
|
+
$$type: KEYFRAMES_TYPE,
|
|
125
|
+
value: keyframes.map( toKeyframeStopPropValue ),
|
|
126
|
+
} );
|
|
127
|
+
|
|
128
|
+
const plainCustomEffectToPropValue = ( plain: PlainCustomEffect ): PropValue => ( {
|
|
129
|
+
$$type: CUSTOM_EFFECT_TYPE,
|
|
130
|
+
value: {
|
|
131
|
+
keyframes: toKeyframesPropValue( plain.keyframes as ( PlainKeyframe | PropValue )[] ),
|
|
132
|
+
},
|
|
133
|
+
} );
|
|
134
|
+
|
|
135
|
+
export const toCustomEffectPropValue = (
|
|
136
|
+
customEffects: PropValue | PlainCustomEffect | undefined
|
|
137
|
+
): PropValue | undefined => {
|
|
138
|
+
if ( customEffects === undefined ) {
|
|
139
|
+
return undefined;
|
|
140
|
+
}
|
|
141
|
+
if ( isPlainCustomEffect( customEffects ) ) {
|
|
142
|
+
return plainCustomEffectToPropValue( customEffects );
|
|
143
|
+
}
|
|
144
|
+
return customEffects as PropValue;
|
|
145
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type InteractionItemPropValue } from '@elementor/editor-elements';
|
|
2
|
+
|
|
3
|
+
import { isSupportedInteractionItem } from './is-supported-interaction-item';
|
|
4
|
+
|
|
5
|
+
export const filterInteractions = ( interactions: InteractionItemPropValue[] ) => {
|
|
6
|
+
return interactions.filter( ( interaction ) => {
|
|
7
|
+
return isSupportedInteractionItem( interaction );
|
|
8
|
+
} );
|
|
9
|
+
};
|
|
@@ -1,15 +1,5 @@
|
|
|
1
1
|
import { type InteractionsConfig } from '../types';
|
|
2
2
|
|
|
3
|
-
const DEFAULT_CONFIG: InteractionsConfig = {
|
|
4
|
-
constants: {
|
|
5
|
-
defaultDuration: 300,
|
|
6
|
-
defaultDelay: 0,
|
|
7
|
-
slideDistance: 100,
|
|
8
|
-
scaleStart: 0.5,
|
|
9
|
-
easing: 'linear',
|
|
10
|
-
},
|
|
11
|
-
};
|
|
12
|
-
|
|
13
3
|
export function getInteractionsConfig(): InteractionsConfig {
|
|
14
|
-
return window.ElementorInteractionsConfig
|
|
4
|
+
return window.ElementorInteractionsConfig ?? ( {} as InteractionsConfig );
|
|
15
5
|
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { getInteractionsControlOptions, type InteractionsControlType } from '../interactions-controls-registry';
|
|
2
|
+
import { type InteractionItemPropValue } from '../types';
|
|
3
|
+
import { extractBoolean, extractString } from '../utils/prop-value-utils';
|
|
4
|
+
|
|
5
|
+
export function isSupportedInteractionItem( interaction: InteractionItemPropValue ): boolean {
|
|
6
|
+
const value = interaction.value;
|
|
7
|
+
|
|
8
|
+
const replay = extractBoolean( value.animation.value.config?.value.replay );
|
|
9
|
+
if ( true === replay ) {
|
|
10
|
+
return hasSupport( 'replay', 'yes' );
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const trigger = extractString( value.trigger );
|
|
14
|
+
const easing = extractString( value.animation.value.config?.value.easing );
|
|
15
|
+
const effect = extractString( value.animation.value.effect );
|
|
16
|
+
|
|
17
|
+
const checks: Array< [ string, string ] > = [
|
|
18
|
+
[ 'trigger', trigger ],
|
|
19
|
+
[ 'easing', easing ],
|
|
20
|
+
[ 'effect', effect ],
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
return checks.every( ( [ controlType, controlValue ] ) => {
|
|
24
|
+
if ( controlValue === '' || controlValue === null ) {
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
return hasSupport( controlType, controlValue );
|
|
28
|
+
} );
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function hasSupport( controlType: string, controlValue: string ) {
|
|
32
|
+
const supportedOptions = getInteractionsControlOptions( controlType as InteractionsControlType );
|
|
33
|
+
|
|
34
|
+
if ( 1 > supportedOptions.length ) {
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return supportedOptions.includes( controlValue );
|
|
39
|
+
}
|