@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,8 +1,8 @@
1
1
  import * as React from 'react';
2
- import { type ComponentType, useMemo, useRef } from 'react';
2
+ import { type ComponentType, useMemo } from 'react';
3
3
  import { PopoverContent } from '@elementor/editor-controls';
4
4
  import { type PropValue } from '@elementor/editor-props';
5
- import { Box, Divider, Grid } from '@elementor/ui';
5
+ import { Divider, Grid } from '@elementor/ui';
6
6
  import { __ } from '@wordpress/i18n';
7
7
 
8
8
  import { getInteractionsControl } from '../interactions-controls-registry';
@@ -35,6 +35,8 @@ export const DEFAULT_VALUES = {
35
35
  replay: false,
36
36
  easing: 'easeIn',
37
37
  relativeTo: 'viewport',
38
+ repeat: '',
39
+ times: 1,
38
40
  start: 85,
39
41
  end: 15,
40
42
  };
@@ -53,7 +55,9 @@ type InteractionsControlType =
53
55
  | 'relativeTo'
54
56
  | 'start'
55
57
  | 'end'
56
- | 'customEffects';
58
+ | 'customEffects'
59
+ | 'repeat'
60
+ | 'times';
57
61
 
58
62
  type InteractionValues = {
59
63
  trigger: string;
@@ -68,6 +72,8 @@ type InteractionValues = {
68
72
  start: SizeStringValue;
69
73
  end: SizeStringValue;
70
74
  customEffects?: PropValue;
75
+ repeat: string;
76
+ times: number;
71
77
  };
72
78
 
73
79
  type ControlVisibilityConfig = {
@@ -82,6 +88,8 @@ const controlVisibilityConfig: ControlVisibilityConfig = {
82
88
  relativeTo: ( values ) => values.trigger === 'scrollOn',
83
89
  start: ( values ) => values.trigger === 'scrollOn',
84
90
  end: ( values ) => values.trigger === 'scrollOn',
91
+ repeat: ( values ) => values.trigger !== 'scrollOn',
92
+ times: ( values ) => values.trigger !== 'scrollOn' && values.repeat === 'times',
85
93
 
86
94
  duration: ( values ) => {
87
95
  const isRelativeToVisible = values.trigger === 'scrollOn';
@@ -93,6 +101,14 @@ const controlVisibilityConfig: ControlVisibilityConfig = {
93
101
  },
94
102
  };
95
103
 
104
+ function normalizeTimesValue( value: unknown, fallback: number ): number {
105
+ const numericValue = Number( value );
106
+ if ( ! Number.isFinite( numericValue ) ) {
107
+ return fallback;
108
+ }
109
+ return Math.max( 1, Math.floor( numericValue ) );
110
+ }
111
+
96
112
  function useControlComponent( controlName: InteractionsControlType, isVisible: boolean = true ) {
97
113
  return useMemo( () => {
98
114
  if ( ! isVisible ) {
@@ -113,6 +129,9 @@ export const InteractionDetails = ( { interaction, onChange, onPlayInteraction }
113
129
  const replay = extractBoolean( interaction.animation.value.config?.value.replay, DEFAULT_VALUES.replay );
114
130
  const easing = extractString( interaction.animation.value.config?.value.easing, DEFAULT_VALUES.easing );
115
131
  const relativeTo = extractString( interaction.animation.value.config?.value.relativeTo, DEFAULT_VALUES.relativeTo );
132
+ const configValue = interaction.animation.value.config?.value;
133
+ const repeat = extractString( configValue?.repeat, DEFAULT_VALUES.repeat );
134
+ const times = normalizeTimesValue( configValue?.times?.value, DEFAULT_VALUES.times );
116
135
 
117
136
  const start = extractSize( interaction.animation.value.config?.value.start, DEFAULT_VALUES.start );
118
137
  const end = extractSize( interaction.animation.value.config?.value.end, DEFAULT_VALUES.end );
@@ -127,6 +146,8 @@ export const InteractionDetails = ( { interaction, onChange, onPlayInteraction }
127
146
  easing,
128
147
  replay,
129
148
  relativeTo,
149
+ repeat,
150
+ times,
130
151
  start,
131
152
  end,
132
153
  customEffects,
@@ -151,10 +172,16 @@ export const InteractionDetails = ( { interaction, onChange, onPlayInteraction }
151
172
  controlVisibilityConfig.effectType( interactionValues )
152
173
  );
153
174
  const DirectionControl = useControlComponent( 'direction', controlVisibilityConfig.direction( interactionValues ) );
175
+ const RepeatControl = useControlComponent(
176
+ 'repeat',
177
+ controlVisibilityConfig.repeat( interactionValues )
178
+ ) as ComponentType< FieldProps< string > >;
179
+ const TimesControl = useControlComponent(
180
+ 'times',
181
+ controlVisibilityConfig.times( interactionValues )
182
+ ) as ComponentType< FieldProps< number > >;
154
183
  const EasingControl = useControlComponent( 'easing' );
155
184
 
156
- const containerRef = useRef< HTMLDivElement >( null );
157
-
158
185
  const updateInteraction = (
159
186
  updates: Partial< {
160
187
  trigger: string;
@@ -166,6 +193,8 @@ export const InteractionDetails = ( { interaction, onChange, onPlayInteraction }
166
193
  replay: boolean;
167
194
  easing?: string;
168
195
  relativeTo: string;
196
+ repeat: string;
197
+ times?: number;
169
198
  start: SizeStringValue;
170
199
  end: SizeStringValue;
171
200
  customEffects?: PropValue;
@@ -192,6 +221,8 @@ export const InteractionDetails = ( { interaction, onChange, onPlayInteraction }
192
221
  replay: updates.replay ?? replay,
193
222
  easing: updates.easing ?? easing,
194
223
  relativeTo: updates.relativeTo ?? relativeTo,
224
+ repeat: updates.repeat ?? repeat,
225
+ times: updates.times ?? times,
195
226
  start: updates.start ?? start,
196
227
  end: updates.end ?? end,
197
228
  customEffects: updates.customEffects ?? customEffects,
@@ -208,131 +239,142 @@ export const InteractionDetails = ( { interaction, onChange, onPlayInteraction }
208
239
  };
209
240
 
210
241
  return (
211
- <Box ref={ containerRef }>
212
- <PopoverContent p={ 1.5 }>
213
- <Grid container spacing={ 1.5 }>
214
- { TriggerControl && (
215
- <Field label={ __( 'Trigger', 'elementor' ) }>
216
- <TriggerControl
217
- value={ trigger }
218
- onChange={ ( v ) => updateInteraction( { trigger: v } ) }
219
- />
220
- </Field>
221
- ) }
222
-
223
- { ReplayControl && (
224
- <Field label={ __( 'Replay', 'elementor' ) }>
225
- <ReplayControl
226
- value={ replay }
227
- onChange={ ( v ) => updateInteraction( { replay: v } ) }
228
- disabled={ true }
229
- anchorRef={ containerRef }
230
- />
231
- </Field>
232
- ) }
233
- </Grid>
242
+ <PopoverContent p={ 1.5 }>
243
+ <Grid container spacing={ 1.5 }>
244
+ { TriggerControl && (
245
+ <Field label={ __( 'Trigger', 'elementor' ) }>
246
+ <TriggerControl value={ trigger } onChange={ ( v ) => updateInteraction( { trigger: v } ) } />
247
+ </Field>
248
+ ) }
234
249
 
235
- <Divider />
250
+ { ReplayControl && (
251
+ <Field label={ __( 'Replay', 'elementor' ) }>
252
+ <ReplayControl
253
+ value={ replay }
254
+ onChange={ ( v ) => updateInteraction( { replay: v } ) }
255
+ disabled={ true }
256
+ />
257
+ </Field>
258
+ ) }
259
+ </Grid>
236
260
 
237
- <Grid container spacing={ 1.5 }>
238
- { EffectControl && (
239
- <Field label={ __( 'Effect', 'elementor' ) }>
240
- <EffectControl value={ effect } onChange={ ( v ) => updateInteraction( { effect: v } ) } />
241
- </Field>
242
- ) }
261
+ <Divider />
243
262
 
244
- { CustomEffectControl && (
245
- <Field label={ __( 'Custom Effect', 'elementor' ) }>
246
- <CustomEffectControl
247
- value={ customEffects }
248
- onChange={ ( v: PropValue ) => updateInteraction( { customEffects: v } ) }
249
- />
250
- </Field>
251
- ) }
263
+ <Grid container spacing={ 1.5 }>
264
+ { EffectControl && (
265
+ <Field label={ __( 'Effect', 'elementor' ) }>
266
+ <EffectControl value={ effect } onChange={ ( v ) => updateInteraction( { effect: v } ) } />
267
+ </Field>
268
+ ) }
252
269
 
253
- { EffectTypeControl && (
254
- <Field label={ __( 'Type', 'elementor' ) }>
255
- <EffectTypeControl value={ type } onChange={ ( v ) => updateInteraction( { type: v } ) } />
256
- </Field>
257
- ) }
258
-
259
- { DirectionControl && (
260
- <Field label={ __( 'Direction', 'elementor' ) }>
261
- <DirectionControl
262
- value={ direction }
263
- onChange={ ( v ) => updateInteraction( { direction: v } ) }
264
- interactionType={ type }
265
- />
266
- </Field>
267
- ) }
268
-
269
- { controlVisibilityConfig.duration( interactionValues ) && (
270
- <Field label={ __( 'Duration', 'elementor' ) }>
271
- <TimeFrameIndicator
272
- value={ String( duration ) }
273
- onChange={ ( v ) => updateInteraction( { duration: v as SizeStringValue } ) }
274
- defaultValue={ DEFAULT_VALUES.duration as SizeStringValue }
275
- />
276
- </Field>
277
- ) }
278
-
279
- { controlVisibilityConfig.delay( interactionValues ) && (
280
- <Field label={ __( 'Delay', 'elementor' ) }>
281
- <TimeFrameIndicator
282
- value={ String( delay ) }
283
- onChange={ ( v ) => updateInteraction( { delay: v as SizeStringValue } ) }
284
- defaultValue={ DEFAULT_VALUES.delay as SizeStringValue }
285
- />
286
- </Field>
287
- ) }
288
- </Grid>
270
+ { CustomEffectControl && (
271
+ <Field label={ __( 'Custom Effect', 'elementor' ) }>
272
+ <CustomEffectControl
273
+ value={ customEffects }
274
+ onChange={ ( v: PropValue ) => updateInteraction( { customEffects: v } ) }
275
+ />
276
+ </Field>
277
+ ) }
289
278
 
290
- { controlVisibilityConfig.relativeTo( interactionValues ) && RelativeToControl && (
291
- <>
292
- <Divider />
293
- <Grid container spacing={ 1.5 }>
294
- { StartControl && (
295
- <Field label={ __( 'Start', 'elementor' ) }>
296
- <StartControl
297
- value={ parseSizeValue( start, [ '%' ] ).size?.toString() ?? '' }
298
- onChange={ ( v: string ) =>
299
- updateInteraction( { start: v as SizeStringValue } )
300
- }
301
- />
302
- </Field>
303
- ) }
304
- { EndControl && (
305
- <Field label={ __( 'End', 'elementor' ) }>
306
- <EndControl
307
- value={ parseSizeValue( end, [ '%' ] ).size?.toString() ?? '' }
308
- onChange={ ( v: string ) => updateInteraction( { end: v as SizeStringValue } ) }
309
- />
310
- </Field>
311
- ) }
312
- <Field label={ __( 'Relative To', 'elementor' ) }>
313
- <RelativeToControl
314
- value={ relativeTo }
315
- onChange={ ( v ) => updateInteraction( { relativeTo: v } ) }
316
- />
317
- </Field>
318
- </Grid>
319
- <Divider />
320
- </>
279
+ { EffectTypeControl && (
280
+ <Field label={ __( 'Type', 'elementor' ) }>
281
+ <EffectTypeControl value={ type } onChange={ ( v ) => updateInteraction( { type: v } ) } />
282
+ </Field>
283
+ ) }
284
+
285
+ { DirectionControl && (
286
+ <Field label={ __( 'Direction', 'elementor' ) }>
287
+ <DirectionControl
288
+ value={ direction }
289
+ onChange={ ( v ) => updateInteraction( { direction: v } ) }
290
+ interactionType={ type }
291
+ />
292
+ </Field>
293
+ ) }
294
+
295
+ { RepeatControl && (
296
+ <Field label={ __( 'Repeat', 'elementor' ) }>
297
+ <RepeatControl value={ repeat } onChange={ ( v ) => updateInteraction( { repeat: v } ) } />
298
+ </Field>
299
+ ) }
300
+
301
+ { TimesControl && (
302
+ <Field label={ __( 'Times', 'elementor' ) }>
303
+ <TimesControl
304
+ value={ times }
305
+ onChange={ ( v ) =>
306
+ updateInteraction( {
307
+ times: normalizeTimesValue( v, DEFAULT_VALUES.times ),
308
+ } )
309
+ }
310
+ />
311
+ </Field>
312
+ ) }
313
+
314
+ { controlVisibilityConfig.duration( interactionValues ) && (
315
+ <Field label={ __( 'Duration', 'elementor' ) }>
316
+ <TimeFrameIndicator
317
+ value={ String( duration ) }
318
+ onChange={ ( v ) => updateInteraction( { duration: v as SizeStringValue } ) }
319
+ defaultValue={ DEFAULT_VALUES.duration as SizeStringValue }
320
+ />
321
+ </Field>
322
+ ) }
323
+
324
+ { controlVisibilityConfig.delay( interactionValues ) && (
325
+ <Field label={ __( 'Delay', 'elementor' ) }>
326
+ <TimeFrameIndicator
327
+ value={ String( delay ) }
328
+ onChange={ ( v ) => updateInteraction( { delay: v as SizeStringValue } ) }
329
+ defaultValue={ DEFAULT_VALUES.delay as SizeStringValue }
330
+ />
331
+ </Field>
321
332
  ) }
333
+ </Grid>
322
334
 
323
- { EasingControl && (
335
+ { controlVisibilityConfig.relativeTo( interactionValues ) && RelativeToControl && (
336
+ <>
337
+ <Divider />
324
338
  <Grid container spacing={ 1.5 }>
325
- <Field label={ __( 'Easing', 'elementor' ) }>
326
- <EasingControl
327
- value={ easing }
328
- onChange={ ( v ) => {
329
- updateInteraction( { easing: v } );
330
- } }
339
+ { StartControl && (
340
+ <Field label={ __( 'Start', 'elementor' ) }>
341
+ <StartControl
342
+ value={ parseSizeValue( start, [ '%' ] ).size?.toString() ?? '' }
343
+ onChange={ ( v: string ) => updateInteraction( { start: v as SizeStringValue } ) }
344
+ />
345
+ </Field>
346
+ ) }
347
+ { EndControl && (
348
+ <Field label={ __( 'End', 'elementor' ) }>
349
+ <EndControl
350
+ value={ parseSizeValue( end, [ '%' ] ).size?.toString() ?? '' }
351
+ onChange={ ( v: string ) => updateInteraction( { end: v as SizeStringValue } ) }
352
+ />
353
+ </Field>
354
+ ) }
355
+ <Field label={ __( 'Relative To', 'elementor' ) }>
356
+ <RelativeToControl
357
+ value={ relativeTo }
358
+ onChange={ ( v ) => updateInteraction( { relativeTo: v } ) }
331
359
  />
332
360
  </Field>
333
361
  </Grid>
334
- ) }
335
- </PopoverContent>
336
- </Box>
362
+ <Divider />
363
+ </>
364
+ ) }
365
+
366
+ { EasingControl && (
367
+ <Grid container spacing={ 1.5 }>
368
+ <Field label={ __( 'Easing', 'elementor' ) }>
369
+ <EasingControl
370
+ value={ easing }
371
+ onChange={ ( v ) => {
372
+ updateInteraction( { easing: v } );
373
+ } }
374
+ />
375
+ </Field>
376
+ </Grid>
377
+ ) }
378
+ </PopoverContent>
337
379
  );
338
380
  };
@@ -1,13 +1,15 @@
1
1
  import * as React from 'react';
2
2
  import { useCallback, useEffect, useMemo, useRef } from 'react';
3
- import { Repeater } from '@elementor/editor-controls';
3
+ import { Repeater, type SetRepeaterValuesMeta } from '@elementor/editor-controls';
4
4
  import { InfoCircleFilledIcon, PlayerPlayIcon } from '@elementor/icons';
5
5
  import { Alert, AlertTitle, Box, IconButton, Tooltip } from '@elementor/ui';
6
6
  import { __ } from '@wordpress/i18n';
7
7
 
8
+ import { useInteractionsContext } from '../contexts/interactions-context';
8
9
  import { InteractionItemContextProvider } from '../contexts/interactions-item-context';
9
10
  import type { ElementInteractions, InteractionItemPropValue, InteractionItemValue } from '../types';
10
11
  import { buildDisplayLabel, createDefaultInteractionItem, extractString } from '../utils/prop-value-utils';
12
+ import { trackInteractionCreated } from '../utils/tracking';
11
13
  import { InteractionsListItem } from './interactions-list-item';
12
14
  export const MAX_NUMBER_OF_INTERACTIONS = 5;
13
15
 
@@ -20,6 +22,7 @@ export type InteractionListProps = {
20
22
 
21
23
  export function InteractionsList( props: InteractionListProps ) {
22
24
  const { interactions, onSelectInteractions, onPlayInteraction, triggerCreateOnShowEmpty } = props;
25
+ const { elementId } = useInteractionsContext();
23
26
 
24
27
  const hasInitializedRef = useRef( false );
25
28
 
@@ -62,13 +65,23 @@ export function InteractionsList( props: InteractionListProps ) {
62
65
  ) : undefined;
63
66
 
64
67
  const handleRepeaterChange = useCallback(
65
- ( newItems: ElementInteractions[ 'items' ] ) => {
68
+ (
69
+ newItems: ElementInteractions[ 'items' ],
70
+ _: unknown,
71
+ meta?: SetRepeaterValuesMeta< InteractionItemPropValue >
72
+ ) => {
66
73
  handleUpdateInteractions( {
67
74
  ...interactions,
68
75
  items: newItems,
69
76
  } );
77
+ if ( meta?.action?.type === 'add' ) {
78
+ const addedItem = meta.action.payload[ 0 ]?.item;
79
+ if ( addedItem ) {
80
+ trackInteractionCreated( elementId, addedItem );
81
+ }
82
+ }
70
83
  },
71
- [ interactions, handleUpdateInteractions ]
84
+ [ interactions, handleUpdateInteractions, elementId ]
72
85
  );
73
86
 
74
87
  const handleInteractionChange = useCallback(
@@ -1,12 +1,14 @@
1
1
  import * as React from 'react';
2
2
  import { useCallback, useState } from 'react';
3
- import { useElementInteractions } from '@elementor/editor-elements';
4
3
  import { SessionStorageProvider } from '@elementor/session';
5
4
  import { Stack } from '@elementor/ui';
6
5
 
7
6
  import { InteractionsProvider, useInteractionsContext } from '../contexts/interactions-context';
8
7
  import { PopupStateProvider } from '../contexts/popup-state-context';
8
+ import { useElementInteractions } from '../hooks/use-element-interactions';
9
9
  import type { ElementInteractions } from '../types';
10
+ import { createDefaultInteractionItem } from '../utils/prop-value-utils';
11
+ import { trackInteractionCreated } from '../utils/tracking';
10
12
  import { EmptyState } from './empty-state';
11
13
  import { InteractionsList } from './interactions-list';
12
14
 
@@ -33,6 +35,7 @@ function InteractionsTabContent( { elementId }: { elementId: string } ) {
33
35
  <EmptyState
34
36
  onCreateInteraction={ () => {
35
37
  firstInteractionState[ 1 ]( true );
38
+ trackInteractionCreated( elementId, createDefaultInteractionItem() );
36
39
  } }
37
40
  />
38
41
  ) }
@@ -4,10 +4,12 @@ import {
4
4
  type ElementInteractions,
5
5
  playElementInteractions,
6
6
  updateElementInteractions,
7
- useElementInteractions,
8
7
  } from '@elementor/editor-elements';
9
8
 
9
+ import { useElementInteractions } from '../hooks/use-element-interactions';
10
+
10
11
  type InteractionsContextValue = {
12
+ elementId: string;
11
13
  interactions: ElementInteractions;
12
14
  setInteractions: ( value: ElementInteractions | undefined ) => void;
13
15
  playInteractions: ( interactionId: string ) => void;
@@ -44,6 +46,7 @@ export const InteractionsProvider = ( { children, elementId }: { children: React
44
46
  };
45
47
 
46
48
  const contextValue: InteractionsContextValue = {
49
+ elementId,
47
50
  interactions,
48
51
  setInteractions,
49
52
  playInteractions,
@@ -0,0 +1,26 @@
1
+ import { useState } from 'react';
2
+ import { type ElementID, type ElementInteractions, getElementInteractions } from '@elementor/editor-elements';
3
+ import { __privateUseListenTo as useListenTo, windowEvent } from '@elementor/editor-v1-adapters';
4
+
5
+ import { filterInteractions } from '../utils/filter-interactions';
6
+
7
+ export const useElementInteractions = ( elementId: ElementID ) => {
8
+ const [ interactions, setInteractions ] = useState< ElementInteractions >( () => {
9
+ const initial = getElementInteractions( elementId );
10
+ const filteredInteractions = filterInteractions( initial?.items ?? [] );
11
+
12
+ return { version: initial?.version ?? 1, items: filteredInteractions };
13
+ } );
14
+
15
+ useListenTo(
16
+ windowEvent( 'elementor/element/update_interactions' ),
17
+ () => {
18
+ const newInteractions = getElementInteractions( elementId );
19
+ const filteredInteractions = filterInteractions( newInteractions?.items ?? [] );
20
+ setInteractions( { version: newInteractions?.version ?? 1, items: filteredInteractions } );
21
+ },
22
+ [ elementId ]
23
+ );
24
+
25
+ return interactions;
26
+ };
package/src/index.ts CHANGED
@@ -16,3 +16,29 @@ export { TRIGGER_OPTIONS, BASE_TRIGGERS } from './components/controls/trigger';
16
16
  export { EASING_OPTIONS, BASE_EASINGS } from './components/controls/easing';
17
17
  export { REPLAY_OPTIONS, BASE_REPLAY } from './components/controls/replay';
18
18
  export { EFFECT_OPTIONS, BASE_EFFECTS } from './components/controls/effect';
19
+ export { REPEAT_OPTIONS, REPEAT_TOOLTIPS } from './components/controls/repeat';
20
+
21
+ export {
22
+ createString,
23
+ createNumber,
24
+ createBoolean,
25
+ createTimingConfig,
26
+ createConfig,
27
+ createExcludedBreakpoints,
28
+ createInteractionBreakpoints,
29
+ createAnimationPreset,
30
+ createInteractionItem,
31
+ createDefaultInteractionItem,
32
+ createDefaultInteractions,
33
+ extractString,
34
+ extractBoolean,
35
+ extractSize,
36
+ extractExcludedBreakpoints,
37
+ buildDisplayLabel,
38
+ } from './utils/prop-value-utils';
39
+
40
+ export { generateTempInteractionId, isTempId } from './utils/temp-id-utils';
41
+ export { resolveDirection } from './utils/resolve-direction';
42
+ export { convertTimeUnit } from './utils/time-conversion';
43
+ export { parseSizeValue, formatSizeValue } from './utils/size-transform-utils';
44
+ export { useElementInteractions } from './hooks/use-element-interactions';
package/src/init.ts CHANGED
@@ -1,13 +1,17 @@
1
+ import { getMCPByDomain } from '@elementor/editor-mcp';
2
+
3
+ import { initPasteInteractionsCommand } from './commands/paste-interactions';
1
4
  import { Direction } from './components/controls/direction';
2
5
  import { Easing } from './components/controls/easing';
3
6
  import { Effect } from './components/controls/effect';
4
7
  import { EffectType } from './components/controls/effect-type';
8
+ import { Repeat } from './components/controls/repeat';
5
9
  import { Replay } from './components/controls/replay';
6
10
  import { Trigger } from './components/controls/trigger';
7
11
  import { initCleanInteractionIdsOnDuplicate } from './hooks/on-duplicate';
8
12
  import { registerInteractionsControl } from './interactions-controls-registry';
9
13
  import { interactionsRepository } from './interactions-repository';
10
- import { initMcpInteractions } from './mcp';
14
+ import { EDITOR_INTERACTIONS_MCP_INSTRUCTIONS, initMcpInteractions } from './mcp';
11
15
  import { documentElementsInteractionsProvider } from './providers/document-elements-interactions-provider';
12
16
 
13
17
  export function init() {
@@ -15,6 +19,7 @@ export function init() {
15
19
  interactionsRepository.register( documentElementsInteractionsProvider );
16
20
 
17
21
  initCleanInteractionIdsOnDuplicate();
22
+ initPasteInteractionsCommand();
18
23
 
19
24
  registerInteractionsControl( {
20
25
  type: 'trigger',
@@ -31,7 +36,7 @@ export function init() {
31
36
  registerInteractionsControl( {
32
37
  type: 'replay',
33
38
  component: Replay,
34
- options: [ 'true', 'false' ],
39
+ options: [ 'no' ],
35
40
  } );
36
41
 
37
42
  registerInteractionsControl( {
@@ -52,7 +57,12 @@ export function init() {
52
57
  options: [ 'fade', 'slide', 'scale' ],
53
58
  } );
54
59
 
55
- initMcpInteractions();
60
+ registerInteractionsControl( {
61
+ type: 'repeat',
62
+ component: Repeat,
63
+ } );
64
+
65
+ initMcpInteractions( getMCPByDomain( 'interactions', { instructions: EDITOR_INTERACTIONS_MCP_INSTRUCTIONS } ) );
56
66
  } catch ( error ) {
57
67
  throw error;
58
68
  }
@@ -1,9 +1,15 @@
1
1
  import { type ComponentType } from 'react';
2
2
  import { type PropValue } from '@elementor/editor-props';
3
3
 
4
- import { type DirectionFieldProps, type FieldProps, type ReplayFieldProps } from './types';
4
+ import {
5
+ type DirectionFieldProps,
6
+ type FieldProps,
7
+ type RepeatFieldProps,
8
+ type ReplayFieldProps,
9
+ type TimesFieldProps,
10
+ } from './types';
5
11
 
6
- type InteractionsControlType =
12
+ export type InteractionsControlType =
7
13
  | 'trigger'
8
14
  | 'effect'
9
15
  | 'effectType'
@@ -11,6 +17,8 @@ type InteractionsControlType =
11
17
  | 'duration'
12
18
  | 'delay'
13
19
  | 'replay'
20
+ | 'repeat'
21
+ | 'times'
14
22
  | 'easing'
15
23
  | 'relativeTo'
16
24
  | 'start'
@@ -26,6 +34,8 @@ type InteractionsControlPropsMap = {
26
34
  duration: FieldProps;
27
35
  delay: FieldProps;
28
36
  replay: ReplayFieldProps;
37
+ repeat: RepeatFieldProps;
38
+ times: TimesFieldProps;
29
39
  easing: FieldProps;
30
40
  relativeTo: FieldProps;
31
41
  start: FieldProps;
@@ -40,7 +50,9 @@ type ControlOptions< T extends InteractionsControlType > = {
40
50
 
41
51
  type StoredControlOptions = {
42
52
  type: InteractionsControlType;
43
- component: ComponentType< FieldProps | DirectionFieldProps | ReplayFieldProps >;
53
+ component: ComponentType<
54
+ FieldProps | DirectionFieldProps | ReplayFieldProps | RepeatFieldProps | TimesFieldProps
55
+ >;
44
56
  options?: string[];
45
57
  };
46
58