@elementor/editor-interactions 4.0.0-manual → 4.0.1

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.
Files changed (36) hide show
  1. package/dist/index.d.mts +123 -5
  2. package/dist/index.d.ts +123 -5
  3. package/dist/index.js +861 -334
  4. package/dist/index.js.map +1 -1
  5. package/dist/index.mjs +823 -312
  6. package/dist/index.mjs.map +1 -1
  7. package/package.json +12 -11
  8. package/src/commands/get-clipboard-elements.ts +11 -0
  9. package/src/commands/paste-interactions.ts +136 -0
  10. package/src/components/controls/easing.tsx +3 -0
  11. package/src/components/controls/effect.tsx +3 -0
  12. package/src/components/controls/repeat.tsx +57 -0
  13. package/src/components/controls/replay.tsx +15 -13
  14. package/src/components/controls/trigger.tsx +3 -0
  15. package/src/components/interaction-details.tsx +162 -120
  16. package/src/components/interactions-list.tsx +16 -3
  17. package/src/components/interactions-tab.tsx +4 -1
  18. package/src/contexts/interactions-context.tsx +4 -1
  19. package/src/hooks/use-element-interactions.ts +26 -0
  20. package/src/index.ts +26 -0
  21. package/src/init.ts +13 -3
  22. package/src/interactions-controls-registry.ts +15 -3
  23. package/src/mcp/constants.ts +58 -0
  24. package/src/mcp/index.ts +15 -69
  25. package/src/mcp/tools/manage-element-interaction-tool.ts +40 -5
  26. package/src/mcp/tools/schema.ts +1 -1
  27. package/src/types.ts +6 -1
  28. package/src/ui/interactions-promotion-chip.tsx +14 -6
  29. package/src/ui/promotion-overlay-layout.tsx +22 -0
  30. package/src/ui/promotion-select.tsx +4 -0
  31. package/src/utils/custom-effect-to-prop-value.ts +145 -0
  32. package/src/utils/filter-interactions.ts +9 -0
  33. package/src/utils/get-interactions-config.ts +1 -11
  34. package/src/utils/is-supported-interaction-item.ts +39 -0
  35. package/src/utils/prop-value-utils.ts +59 -34
  36. package/src/utils/tracking.ts +42 -0
@@ -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 { getMCPByDomain } from '@elementor/editor-mcp';
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 const initMcpInteractions = () => {
8
- const reg = getMCPByDomain( 'interactions', {
9
- instructions: `
10
- 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.
11
- ** IMPORTANT **
12
- Use the "interactions-schema" resource to get the schema of the interactions.
13
- Actions:
14
- - get: Read the current interactions on the element.
15
- - add: Add a new interaction (max ${ MAX_INTERACTIONS_PER_ELEMENT } per element).
16
- - update: Update an existing interaction by its interactionId.
17
- - delete: Remove a specific interaction by its interactionId.
18
- - clear: Remove all interactions from the element.
19
-
20
- For add/update, provide: trigger, effect, effectType, direction (required for slide effect), duration, delay, easing.
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 { createInteractionItem, extractString } from '../../utils/prop-value-utils';
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: currentInteractions.items,
69
- count: currentInteractions.items.length,
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
- ...animationData,
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
- ...animationData,
143
+ ...restAnimationData,
144
+ type: resolvedType,
110
145
  } );
111
146
 
112
147
  updatedItems = [
@@ -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
- customEffect: z
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
- easing: string;
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 = () => setIsOpen( ( prev ) => ! prev );
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 || DEFAULT_CONFIG;
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
+ }