@elementor/editor-interactions 4.0.0-manual → 4.1.0-684
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 +85 -5
- package/dist/index.d.ts +85 -5
- package/dist/index.js +563 -217
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +515 -183
- 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/replay.tsx +3 -0
- package/src/components/controls/trigger.tsx +3 -0
- package/src/components/interaction-details.tsx +53 -1
- 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 +25 -0
- package/src/init.ts +3 -1
- package/src/interactions-controls-registry.ts +15 -3
- package/src/mcp/tools/manage-element-interaction-tool.ts +40 -5
- package/src/types.ts +6 -1
- package/src/ui/interactions-promotion-chip.tsx +14 -4
- package/src/ui/promotion-select.tsx +4 -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 +35 -14
- package/src/utils/tracking.ts +42 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elementor/editor-interactions",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.1.0-684",
|
|
4
4
|
"private": false,
|
|
5
5
|
"author": "Elementor Team",
|
|
6
6
|
"homepage": "https://elementor.com/",
|
|
@@ -39,18 +39,19 @@
|
|
|
39
39
|
"dev": "tsup --config=../../tsup.dev.ts"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@elementor/editor-controls": "4.
|
|
43
|
-
"@elementor/editor-elements": "4.
|
|
44
|
-
"@elementor/editor-mcp": "4.
|
|
45
|
-
"@elementor/editor-props": "4.
|
|
46
|
-
"@elementor/editor-responsive": "4.
|
|
47
|
-
"@elementor/editor-ui": "4.
|
|
48
|
-
"@elementor/editor-v1-adapters": "4.
|
|
42
|
+
"@elementor/editor-controls": "4.1.0-684",
|
|
43
|
+
"@elementor/editor-elements": "4.1.0-684",
|
|
44
|
+
"@elementor/editor-mcp": "4.1.0-684",
|
|
45
|
+
"@elementor/editor-props": "4.1.0-684",
|
|
46
|
+
"@elementor/editor-responsive": "4.1.0-684",
|
|
47
|
+
"@elementor/editor-ui": "4.1.0-684",
|
|
48
|
+
"@elementor/editor-v1-adapters": "4.1.0-684",
|
|
49
49
|
"@elementor/icons": "^1.68.0",
|
|
50
|
-
"@elementor/schema": "4.
|
|
51
|
-
"@elementor/session": "4.
|
|
50
|
+
"@elementor/schema": "4.1.0-684",
|
|
51
|
+
"@elementor/session": "4.1.0-684",
|
|
52
52
|
"@elementor/ui": "1.36.17",
|
|
53
|
-
"@elementor/utils": "4.
|
|
53
|
+
"@elementor/utils": "4.1.0-684",
|
|
54
|
+
"@elementor/events": "4.1.0-684",
|
|
54
55
|
"@wordpress/i18n": "^5.13.0"
|
|
55
56
|
},
|
|
56
57
|
"peerDependencies": {
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { V1ElementModelProps } from '@elementor/editor-elements';
|
|
2
|
+
|
|
3
|
+
export function getClipboardElements( storageKey: string = 'clipboard' ): V1ElementModelProps[] | undefined {
|
|
4
|
+
try {
|
|
5
|
+
const storedData = JSON.parse( localStorage.getItem( 'elementor' ) ?? '{}' );
|
|
6
|
+
|
|
7
|
+
return storedData[ storageKey ]?.elements as V1ElementModelProps[] | undefined;
|
|
8
|
+
} catch {
|
|
9
|
+
return undefined;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getContainer,
|
|
3
|
+
getElementInteractions,
|
|
4
|
+
getElementLabel,
|
|
5
|
+
getWidgetsCache,
|
|
6
|
+
updateElementInteractions,
|
|
7
|
+
type V1Element,
|
|
8
|
+
type V1ElementModelProps,
|
|
9
|
+
} from '@elementor/editor-elements';
|
|
10
|
+
import {
|
|
11
|
+
__privateListenTo as listenTo,
|
|
12
|
+
type CommandEvent,
|
|
13
|
+
commandStartEvent,
|
|
14
|
+
undoable,
|
|
15
|
+
} from '@elementor/editor-v1-adapters';
|
|
16
|
+
import { __ } from '@wordpress/i18n';
|
|
17
|
+
|
|
18
|
+
import type { ElementInteractions } from '../types';
|
|
19
|
+
import { createString } from '../utils/prop-value-utils';
|
|
20
|
+
import { generateTempInteractionId } from '../utils/temp-id-utils';
|
|
21
|
+
import { getClipboardElements } from './get-clipboard-elements';
|
|
22
|
+
|
|
23
|
+
function isAtomicContainer( container: V1Element ): boolean {
|
|
24
|
+
const type = container?.model.get( 'widgetType' ) || container?.model.get( 'elType' );
|
|
25
|
+
const widgetsCache = getWidgetsCache();
|
|
26
|
+
const elementConfig = widgetsCache?.[ type ];
|
|
27
|
+
|
|
28
|
+
return Boolean( elementConfig?.atomic_props_schema );
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
type PasteInteractionsCommandArgs = {
|
|
32
|
+
container?: V1Element;
|
|
33
|
+
containers?: V1Element[];
|
|
34
|
+
storageKey?: string;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
type PasteInteractionsPayload = {
|
|
38
|
+
containers: V1Element[];
|
|
39
|
+
newInteractions: ElementInteractions;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
function getTitleForContainers( containers: V1Element[] ): string {
|
|
43
|
+
return containers.length > 1 ? __( 'Elements', 'elementor' ) : getElementLabel( containers[ 0 ].id );
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function normalizeClipboardInteractions( raw: V1ElementModelProps[ 'interactions' ] ): ElementInteractions | null {
|
|
47
|
+
if ( ! raw ) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const parsed: ElementInteractions = typeof raw === 'string' ? ( JSON.parse( raw ) as ElementInteractions ) : raw;
|
|
52
|
+
|
|
53
|
+
if ( ! parsed?.items?.length ) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return { version: parsed.version ?? 1, items: parsed.items };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function regenerateInteractionIds( interactions: ElementInteractions ): ElementInteractions {
|
|
61
|
+
const cloned = structuredClone( interactions ) as ElementInteractions;
|
|
62
|
+
|
|
63
|
+
cloned.items?.forEach( ( item ) => {
|
|
64
|
+
if ( item.$$type === 'interaction-item' && item.value ) {
|
|
65
|
+
item.value.interaction_id = createString( generateTempInteractionId() );
|
|
66
|
+
}
|
|
67
|
+
} );
|
|
68
|
+
|
|
69
|
+
return cloned;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function initPasteInteractionsCommand() {
|
|
73
|
+
const undoablePasteInteractions = undoable(
|
|
74
|
+
{
|
|
75
|
+
do: ( { containers, newInteractions }: PasteInteractionsPayload ) => {
|
|
76
|
+
const pasted = regenerateInteractionIds( newInteractions );
|
|
77
|
+
|
|
78
|
+
return containers.map( ( container ) => {
|
|
79
|
+
const elementId = container.id;
|
|
80
|
+
const previous = getElementInteractions( elementId );
|
|
81
|
+
|
|
82
|
+
updateElementInteractions( {
|
|
83
|
+
elementId,
|
|
84
|
+
interactions: pasted,
|
|
85
|
+
} );
|
|
86
|
+
|
|
87
|
+
return { elementId, previous: previous ?? { version: 1, items: [] } };
|
|
88
|
+
} );
|
|
89
|
+
},
|
|
90
|
+
undo: ( _: PasteInteractionsPayload, revertData ) => {
|
|
91
|
+
revertData.forEach( ( { elementId, previous } ) => {
|
|
92
|
+
updateElementInteractions( {
|
|
93
|
+
elementId,
|
|
94
|
+
interactions: previous.items?.length ? previous : undefined,
|
|
95
|
+
} );
|
|
96
|
+
} );
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
title: ( { containers } ) => getTitleForContainers( containers ),
|
|
101
|
+
subtitle: __( 'Interactions Pasted', 'elementor' ),
|
|
102
|
+
}
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
listenTo( commandStartEvent( 'document/elements/paste-interactions' ), ( e ) => {
|
|
106
|
+
const args = ( e as CommandEvent ).args as PasteInteractionsCommandArgs;
|
|
107
|
+
const containers = args.containers ?? ( args.container ? [ args.container ] : [] );
|
|
108
|
+
const storageKey = args.storageKey ?? 'clipboard';
|
|
109
|
+
|
|
110
|
+
if ( ! containers.length ) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const clipboardElements = getClipboardElements( storageKey );
|
|
115
|
+
const [ clipboardElement ] = clipboardElements ?? [];
|
|
116
|
+
|
|
117
|
+
if ( ! clipboardElement ) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const newInteractions = normalizeClipboardInteractions( clipboardElement.interactions );
|
|
122
|
+
|
|
123
|
+
if ( ! newInteractions ) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const existingContainers = containers.filter( ( c ) => getContainer( c.id ) ) as V1Element[];
|
|
128
|
+
const validContainers = existingContainers.filter( isAtomicContainer );
|
|
129
|
+
|
|
130
|
+
if ( ! validContainers.length ) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
undoablePasteInteractions( { containers: validContainers, newInteractions } );
|
|
135
|
+
} );
|
|
136
|
+
}
|
|
@@ -5,6 +5,8 @@ import { type FieldProps } from '../../types';
|
|
|
5
5
|
import { PromotionSelect } from '../../ui/promotion-select';
|
|
6
6
|
import { DEFAULT_VALUES } from '../interaction-details';
|
|
7
7
|
|
|
8
|
+
const TRACKING_DATA = { target_name: 'interactions_easing', location_l2: 'interactions' } as const;
|
|
9
|
+
|
|
8
10
|
export const EASING_OPTIONS = {
|
|
9
11
|
easeIn: __( 'Ease In', 'elementor' ),
|
|
10
12
|
easeInOut: __( 'Ease In Out', 'elementor' ),
|
|
@@ -33,6 +35,7 @@ export function Easing( {}: FieldProps ) {
|
|
|
33
35
|
disabledOptions={ disabledOptions }
|
|
34
36
|
promotionContent={ __( 'Upgrade to control the smoothness of the interaction.', 'elementor' ) }
|
|
35
37
|
upgradeUrl="https://go.elementor.com/go-pro-interactions-easing-modal/"
|
|
38
|
+
trackingData={ TRACKING_DATA }
|
|
36
39
|
/>
|
|
37
40
|
);
|
|
38
41
|
}
|
|
@@ -5,6 +5,8 @@ import { type FieldProps } from '../../types';
|
|
|
5
5
|
import { PromotionSelect } from '../../ui/promotion-select';
|
|
6
6
|
import { DEFAULT_VALUES } from '../interaction-details';
|
|
7
7
|
|
|
8
|
+
const TRACKING_DATA = { target_name: 'interactions_effect', location_l2: 'interactions' } as const;
|
|
9
|
+
|
|
8
10
|
export const EFFECT_OPTIONS = {
|
|
9
11
|
fade: __( 'Fade', 'elementor' ),
|
|
10
12
|
slide: __( 'Slide', 'elementor' ),
|
|
@@ -35,6 +37,7 @@ export function Effect( { value, onChange }: FieldProps ) {
|
|
|
35
37
|
'elementor'
|
|
36
38
|
) }
|
|
37
39
|
upgradeUrl="https://go.elementor.com/go-pro-interactions-custom-effect-modal/"
|
|
40
|
+
trackingData={ TRACKING_DATA }
|
|
38
41
|
/>
|
|
39
42
|
);
|
|
40
43
|
}
|
|
@@ -7,6 +7,8 @@ import { __ } from '@wordpress/i18n';
|
|
|
7
7
|
import { type ReplayFieldProps } from '../../types';
|
|
8
8
|
import { InteractionsPromotionChip } from '../../ui/interactions-promotion-chip';
|
|
9
9
|
|
|
10
|
+
const TRACKING_DATA = { target_name: 'interactions_replay', location_l2: 'interactions' } as const;
|
|
11
|
+
|
|
10
12
|
export const REPLAY_OPTIONS = {
|
|
11
13
|
no: __( 'No', 'elementor' ),
|
|
12
14
|
yes: __( 'Yes', 'elementor' ),
|
|
@@ -45,6 +47,7 @@ export function Replay( { onChange, anchorRef }: ReplayFieldProps ) {
|
|
|
45
47
|
content={ __( 'Upgrade to run the animation every time its trigger occurs.', 'elementor' ) }
|
|
46
48
|
upgradeUrl={ 'https://go.elementor.com/go-pro-interactions-replay-modal/' }
|
|
47
49
|
anchorRef={ anchorRef }
|
|
50
|
+
trackingData={ TRACKING_DATA }
|
|
48
51
|
/>
|
|
49
52
|
</Box>
|
|
50
53
|
</Box>
|
|
@@ -5,6 +5,8 @@ import { type FieldProps } from '../../types';
|
|
|
5
5
|
import { PromotionSelect } from '../../ui/promotion-select';
|
|
6
6
|
import { DEFAULT_VALUES } from '../interaction-details';
|
|
7
7
|
|
|
8
|
+
const TRACKING_DATA = { target_name: 'interactions_trigger', location_l2: 'interactions' } as const;
|
|
9
|
+
|
|
8
10
|
export const TRIGGER_OPTIONS = {
|
|
9
11
|
load: __( 'Page load', 'elementor' ),
|
|
10
12
|
scrollIn: __( 'Scroll into view', 'elementor' ),
|
|
@@ -33,6 +35,7 @@ export function Trigger( { value, onChange }: FieldProps ) {
|
|
|
33
35
|
promotionLabel={ __( 'PRO triggers', 'elementor' ) }
|
|
34
36
|
promotionContent={ __( 'Upgrade to unlock more interactions triggers.', 'elementor' ) }
|
|
35
37
|
upgradeUrl="https://go.elementor.com/go-pro-interactions-triggers-modal/"
|
|
38
|
+
trackingData={ TRACKING_DATA }
|
|
36
39
|
/>
|
|
37
40
|
);
|
|
38
41
|
}
|
|
@@ -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,6 +172,14 @@ 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
185
|
const containerRef = useRef< HTMLDivElement >( null );
|
|
@@ -166,6 +195,8 @@ export const InteractionDetails = ( { interaction, onChange, onPlayInteraction }
|
|
|
166
195
|
replay: boolean;
|
|
167
196
|
easing?: string;
|
|
168
197
|
relativeTo: string;
|
|
198
|
+
repeat: string;
|
|
199
|
+
times?: number;
|
|
169
200
|
start: SizeStringValue;
|
|
170
201
|
end: SizeStringValue;
|
|
171
202
|
customEffects?: PropValue;
|
|
@@ -192,6 +223,8 @@ export const InteractionDetails = ( { interaction, onChange, onPlayInteraction }
|
|
|
192
223
|
replay: updates.replay ?? replay,
|
|
193
224
|
easing: updates.easing ?? easing,
|
|
194
225
|
relativeTo: updates.relativeTo ?? relativeTo,
|
|
226
|
+
repeat: updates.repeat ?? repeat,
|
|
227
|
+
times: updates.times ?? times,
|
|
195
228
|
start: updates.start ?? start,
|
|
196
229
|
end: updates.end ?? end,
|
|
197
230
|
customEffects: updates.customEffects ?? customEffects,
|
|
@@ -266,6 +299,25 @@ export const InteractionDetails = ( { interaction, onChange, onPlayInteraction }
|
|
|
266
299
|
</Field>
|
|
267
300
|
) }
|
|
268
301
|
|
|
302
|
+
{ RepeatControl && (
|
|
303
|
+
<Field label={ __( 'Repeat', 'elementor' ) }>
|
|
304
|
+
<RepeatControl value={ repeat } onChange={ ( v ) => updateInteraction( { repeat: v } ) } />
|
|
305
|
+
</Field>
|
|
306
|
+
) }
|
|
307
|
+
|
|
308
|
+
{ TimesControl && (
|
|
309
|
+
<Field label={ __( 'Times', 'elementor' ) }>
|
|
310
|
+
<TimesControl
|
|
311
|
+
value={ times }
|
|
312
|
+
onChange={ ( v ) =>
|
|
313
|
+
updateInteraction( {
|
|
314
|
+
times: normalizeTimesValue( v, DEFAULT_VALUES.times ),
|
|
315
|
+
} )
|
|
316
|
+
}
|
|
317
|
+
/>
|
|
318
|
+
</Field>
|
|
319
|
+
) }
|
|
320
|
+
|
|
269
321
|
{ controlVisibilityConfig.duration( interactionValues ) && (
|
|
270
322
|
<Field label={ __( 'Duration', 'elementor' ) }>
|
|
271
323
|
<TimeFrameIndicator
|
|
@@ -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
|
-
(
|
|
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,28 @@ 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
|
+
|
|
20
|
+
export {
|
|
21
|
+
createString,
|
|
22
|
+
createNumber,
|
|
23
|
+
createBoolean,
|
|
24
|
+
createTimingConfig,
|
|
25
|
+
createConfig,
|
|
26
|
+
createExcludedBreakpoints,
|
|
27
|
+
createInteractionBreakpoints,
|
|
28
|
+
createAnimationPreset,
|
|
29
|
+
createInteractionItem,
|
|
30
|
+
createDefaultInteractionItem,
|
|
31
|
+
createDefaultInteractions,
|
|
32
|
+
extractString,
|
|
33
|
+
extractBoolean,
|
|
34
|
+
extractSize,
|
|
35
|
+
extractExcludedBreakpoints,
|
|
36
|
+
buildDisplayLabel,
|
|
37
|
+
} from './utils/prop-value-utils';
|
|
38
|
+
|
|
39
|
+
export { generateTempInteractionId, isTempId } from './utils/temp-id-utils';
|
|
40
|
+
export { resolveDirection } from './utils/resolve-direction';
|
|
41
|
+
export { convertTimeUnit } from './utils/time-conversion';
|
|
42
|
+
export { parseSizeValue, formatSizeValue } from './utils/size-transform-utils';
|
|
43
|
+
export { useElementInteractions } from './hooks/use-element-interactions';
|
package/src/init.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { initPasteInteractionsCommand } from './commands/paste-interactions';
|
|
1
2
|
import { Direction } from './components/controls/direction';
|
|
2
3
|
import { Easing } from './components/controls/easing';
|
|
3
4
|
import { Effect } from './components/controls/effect';
|
|
@@ -15,6 +16,7 @@ export function init() {
|
|
|
15
16
|
interactionsRepository.register( documentElementsInteractionsProvider );
|
|
16
17
|
|
|
17
18
|
initCleanInteractionIdsOnDuplicate();
|
|
19
|
+
initPasteInteractionsCommand();
|
|
18
20
|
|
|
19
21
|
registerInteractionsControl( {
|
|
20
22
|
type: 'trigger',
|
|
@@ -31,7 +33,7 @@ export function init() {
|
|
|
31
33
|
registerInteractionsControl( {
|
|
32
34
|
type: 'replay',
|
|
33
35
|
component: Replay,
|
|
34
|
-
options: [ '
|
|
36
|
+
options: [ 'no' ],
|
|
35
37
|
} );
|
|
36
38
|
|
|
37
39
|
registerInteractionsControl( {
|
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
import { type ComponentType } from 'react';
|
|
2
2
|
import { type PropValue } from '@elementor/editor-props';
|
|
3
3
|
|
|
4
|
-
import {
|
|
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<
|
|
53
|
+
component: ComponentType<
|
|
54
|
+
FieldProps | DirectionFieldProps | ReplayFieldProps | RepeatFieldProps | TimesFieldProps
|
|
55
|
+
>;
|
|
44
56
|
options?: string[];
|
|
45
57
|
};
|
|
46
58
|
|