@elementor/editor-v1-adapters 0.1.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/LICENSE +674 -0
- package/README.md +5 -0
- package/dist/index.d.ts +81 -0
- package/dist/index.js +334 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +288 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +38 -0
- package/src/__tests__/utils.ts +39 -0
- package/src/dispatchers/__tests__/index.test.ts +126 -0
- package/src/dispatchers/dispatchers.ts +38 -0
- package/src/dispatchers/index.ts +1 -0
- package/src/dispatchers/types.ts +10 -0
- package/src/dispatchers/utils.ts +18 -0
- package/src/hooks/__tests__/test-utils.ts +21 -0
- package/src/hooks/__tests__/use-is-preview-mode.test.ts +47 -0
- package/src/hooks/__tests__/use-is-route-active.test.ts +93 -0
- package/src/hooks/__tests__/use-route-status.test.ts +124 -0
- package/src/hooks/index.ts +4 -0
- package/src/hooks/use-is-preview-mode.ts +10 -0
- package/src/hooks/use-is-route-active.ts +14 -0
- package/src/hooks/use-listen-to.ts +21 -0
- package/src/hooks/use-route-status.ts +32 -0
- package/src/index.ts +4 -0
- package/src/listeners/__tests__/index.test.ts +343 -0
- package/src/listeners/event-creators.ts +48 -0
- package/src/listeners/index.ts +5 -0
- package/src/listeners/is-ready.ts +14 -0
- package/src/listeners/listeners.ts +124 -0
- package/src/listeners/types.ts +45 -0
- package/src/listeners/utils.ts +44 -0
- package/src/readers/__tests__/index.test.ts +71 -0
- package/src/readers/index.ts +13 -0
- package/src/readers/types.ts +16 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
// src/dispatchers/utils.ts
|
|
2
|
+
function isJQueryDeferred(value) {
|
|
3
|
+
return !!value && "object" === typeof value && Object.hasOwn(value, "promise") && Object.hasOwn(value, "then") && Object.hasOwn(value, "fail");
|
|
4
|
+
}
|
|
5
|
+
function promisifyJQueryDeferred(deferred) {
|
|
6
|
+
return new Promise((resolve, reject) => {
|
|
7
|
+
deferred.then(resolve, reject);
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// src/dispatchers/dispatchers.ts
|
|
12
|
+
function runCommand(command, args) {
|
|
13
|
+
const extendedWindow = window;
|
|
14
|
+
if (!extendedWindow.$e?.run) {
|
|
15
|
+
return Promise.reject("`$e.run()` is not available");
|
|
16
|
+
}
|
|
17
|
+
const result = extendedWindow.$e.run(command, args);
|
|
18
|
+
if (result instanceof Promise) {
|
|
19
|
+
return result;
|
|
20
|
+
}
|
|
21
|
+
if (isJQueryDeferred(result)) {
|
|
22
|
+
return promisifyJQueryDeferred(result);
|
|
23
|
+
}
|
|
24
|
+
return Promise.resolve(result);
|
|
25
|
+
}
|
|
26
|
+
function openRoute(route) {
|
|
27
|
+
const extendedWindow = window;
|
|
28
|
+
if (!extendedWindow.$e?.route) {
|
|
29
|
+
return Promise.reject("`$e.route()` is not available");
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
return Promise.resolve(
|
|
33
|
+
extendedWindow.$e.route(route)
|
|
34
|
+
);
|
|
35
|
+
} catch (e) {
|
|
36
|
+
return Promise.reject(e);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// src/hooks/use-listen-to.ts
|
|
41
|
+
import { useEffect, useState } from "react";
|
|
42
|
+
|
|
43
|
+
// src/listeners/event-creators.ts
|
|
44
|
+
var commandStartEvent = (command) => {
|
|
45
|
+
return {
|
|
46
|
+
type: "command",
|
|
47
|
+
name: command,
|
|
48
|
+
state: "before"
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
var commandEndEvent = (command) => {
|
|
52
|
+
return {
|
|
53
|
+
type: "command",
|
|
54
|
+
name: command,
|
|
55
|
+
state: "after"
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
var routeOpenEvent = (route) => {
|
|
59
|
+
return {
|
|
60
|
+
type: "route",
|
|
61
|
+
name: route,
|
|
62
|
+
state: "open"
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
var routeCloseEvent = (route) => {
|
|
66
|
+
return {
|
|
67
|
+
type: "route",
|
|
68
|
+
name: route,
|
|
69
|
+
state: "close"
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
var windowEvent = (event) => {
|
|
73
|
+
return {
|
|
74
|
+
type: "window-event",
|
|
75
|
+
name: event
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
var v1ReadyEvent = () => {
|
|
79
|
+
return windowEvent("elementor/initialized");
|
|
80
|
+
};
|
|
81
|
+
var editModeChangeEvent = () => {
|
|
82
|
+
return windowEvent("elementor/edit-mode/change");
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
// src/listeners/is-ready.ts
|
|
86
|
+
var ready = false;
|
|
87
|
+
function isReady() {
|
|
88
|
+
return ready;
|
|
89
|
+
}
|
|
90
|
+
function setReady(value) {
|
|
91
|
+
ready = value;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// src/listeners/utils.ts
|
|
95
|
+
function dispatchReadyEvent() {
|
|
96
|
+
return getV1LoadingPromise().then(() => {
|
|
97
|
+
setReady(true);
|
|
98
|
+
window.dispatchEvent(new CustomEvent("elementor/initialized"));
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
function getV1LoadingPromise() {
|
|
102
|
+
const v1LoadingPromise = window.__elementorEditorV1LoadingPromise;
|
|
103
|
+
if (!v1LoadingPromise) {
|
|
104
|
+
return Promise.reject("Elementor Editor V1 is not loaded");
|
|
105
|
+
}
|
|
106
|
+
return v1LoadingPromise;
|
|
107
|
+
}
|
|
108
|
+
function normalizeEvent(e) {
|
|
109
|
+
if (e instanceof CustomEvent && e.detail?.command) {
|
|
110
|
+
return {
|
|
111
|
+
type: "command",
|
|
112
|
+
command: e.detail.command,
|
|
113
|
+
args: e.detail.args,
|
|
114
|
+
originalEvent: e
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
if (e instanceof CustomEvent && e.detail?.route) {
|
|
118
|
+
return {
|
|
119
|
+
type: "route",
|
|
120
|
+
route: e.detail.route,
|
|
121
|
+
originalEvent: e
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
return {
|
|
125
|
+
type: "window-event",
|
|
126
|
+
event: e.type,
|
|
127
|
+
originalEvent: e
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// src/listeners/listeners.ts
|
|
132
|
+
var callbacksByEvent = /* @__PURE__ */ new Map();
|
|
133
|
+
var abortController = new AbortController();
|
|
134
|
+
function listenTo(eventDescriptors, callback) {
|
|
135
|
+
if (!Array.isArray(eventDescriptors)) {
|
|
136
|
+
eventDescriptors = [eventDescriptors];
|
|
137
|
+
}
|
|
138
|
+
const cleanups = eventDescriptors.map((event) => {
|
|
139
|
+
const { type, name } = event;
|
|
140
|
+
switch (type) {
|
|
141
|
+
case "command":
|
|
142
|
+
return registerCommandListener(name, event.state, callback);
|
|
143
|
+
case "route":
|
|
144
|
+
return registerRouteListener(name, event.state, callback);
|
|
145
|
+
case "window-event":
|
|
146
|
+
return registerWindowEventListener(name, callback);
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
return () => {
|
|
150
|
+
cleanups.forEach((cleanup) => cleanup());
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
function flushListeners() {
|
|
154
|
+
abortController.abort();
|
|
155
|
+
callbacksByEvent.clear();
|
|
156
|
+
setReady(false);
|
|
157
|
+
abortController = new AbortController();
|
|
158
|
+
}
|
|
159
|
+
function registerCommandListener(command, state, callback) {
|
|
160
|
+
return registerWindowEventListener(`elementor/commands/run/${state}`, (e) => {
|
|
161
|
+
const shouldRunCallback = e.type === "command" && e.command === command;
|
|
162
|
+
if (shouldRunCallback) {
|
|
163
|
+
callback(e);
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
function registerRouteListener(route, state, callback) {
|
|
168
|
+
return registerWindowEventListener(`elementor/routes/${state}`, (e) => {
|
|
169
|
+
const shouldRunCallback = e.type === "route" && e.route.startsWith(route);
|
|
170
|
+
if (shouldRunCallback) {
|
|
171
|
+
callback(e);
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
function registerWindowEventListener(event, callback) {
|
|
176
|
+
const isFirstListener = !callbacksByEvent.has(event);
|
|
177
|
+
if (isFirstListener) {
|
|
178
|
+
callbacksByEvent.set(event, []);
|
|
179
|
+
addListener(event);
|
|
180
|
+
}
|
|
181
|
+
callbacksByEvent.get(event)?.push(callback);
|
|
182
|
+
return () => {
|
|
183
|
+
const callbacks = callbacksByEvent.get(event);
|
|
184
|
+
if (!callbacks?.length) {
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
const filtered = callbacks.filter((cb) => cb !== callback);
|
|
188
|
+
callbacksByEvent.set(event, filtered);
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
function addListener(event) {
|
|
192
|
+
window.addEventListener(
|
|
193
|
+
event,
|
|
194
|
+
makeEventHandler(event),
|
|
195
|
+
{ signal: abortController.signal }
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
function makeEventHandler(event) {
|
|
199
|
+
return (e) => {
|
|
200
|
+
if (!isReady()) {
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
const normalizedEvent = normalizeEvent(e);
|
|
204
|
+
callbacksByEvent.get(event)?.forEach((callback) => {
|
|
205
|
+
callback(normalizedEvent);
|
|
206
|
+
});
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// src/hooks/use-listen-to.ts
|
|
211
|
+
function useListenTo(event, getSnapshot, deps = []) {
|
|
212
|
+
const [snapshot, setSnapshot] = useState(() => getSnapshot());
|
|
213
|
+
useEffect(() => {
|
|
214
|
+
const updateState = () => setSnapshot(getSnapshot());
|
|
215
|
+
updateState();
|
|
216
|
+
return listenTo(event, updateState);
|
|
217
|
+
}, deps);
|
|
218
|
+
return snapshot;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// src/readers/index.ts
|
|
222
|
+
function isRouteActive(route) {
|
|
223
|
+
const extendedWindow = window;
|
|
224
|
+
return !!extendedWindow.$e?.routes?.isPartOf(route);
|
|
225
|
+
}
|
|
226
|
+
function getCurrentEditMode() {
|
|
227
|
+
const extendedWindow = window;
|
|
228
|
+
return extendedWindow.elementor?.channels?.dataEditMode?.request?.("activeMode");
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// src/hooks/use-is-preview-mode.ts
|
|
232
|
+
function useIsPreviewMode() {
|
|
233
|
+
return useListenTo(
|
|
234
|
+
editModeChangeEvent(),
|
|
235
|
+
() => getCurrentEditMode() === "preview"
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// src/hooks/use-is-route-active.ts
|
|
240
|
+
function useIsRouteActive(route) {
|
|
241
|
+
return useListenTo(
|
|
242
|
+
[
|
|
243
|
+
routeOpenEvent(route),
|
|
244
|
+
routeCloseEvent(route)
|
|
245
|
+
],
|
|
246
|
+
() => isRouteActive(route),
|
|
247
|
+
[route]
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// src/hooks/use-route-status.ts
|
|
252
|
+
function useRouteStatus(route, {
|
|
253
|
+
blockOnKitRoutes = true,
|
|
254
|
+
blockOnPreviewMode = true
|
|
255
|
+
} = {}) {
|
|
256
|
+
const isRouteActive2 = useIsRouteActive(route);
|
|
257
|
+
const isKitRouteActive = useIsRouteActive("panel/global");
|
|
258
|
+
const isPreviewMode = useIsPreviewMode();
|
|
259
|
+
const isActive = isRouteActive2 && !(blockOnPreviewMode && isPreviewMode);
|
|
260
|
+
const isBlocked = blockOnPreviewMode && isPreviewMode || blockOnKitRoutes && isKitRouteActive;
|
|
261
|
+
return {
|
|
262
|
+
isActive,
|
|
263
|
+
isBlocked
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
export {
|
|
267
|
+
commandEndEvent,
|
|
268
|
+
commandStartEvent,
|
|
269
|
+
dispatchReadyEvent,
|
|
270
|
+
editModeChangeEvent,
|
|
271
|
+
flushListeners,
|
|
272
|
+
getCurrentEditMode,
|
|
273
|
+
isReady,
|
|
274
|
+
isRouteActive,
|
|
275
|
+
listenTo,
|
|
276
|
+
openRoute,
|
|
277
|
+
routeCloseEvent,
|
|
278
|
+
routeOpenEvent,
|
|
279
|
+
runCommand,
|
|
280
|
+
setReady,
|
|
281
|
+
useIsPreviewMode,
|
|
282
|
+
useIsRouteActive,
|
|
283
|
+
useListenTo,
|
|
284
|
+
useRouteStatus,
|
|
285
|
+
v1ReadyEvent,
|
|
286
|
+
windowEvent
|
|
287
|
+
};
|
|
288
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/dispatchers/utils.ts","../src/dispatchers/dispatchers.ts","../src/hooks/use-listen-to.ts","../src/listeners/event-creators.ts","../src/listeners/is-ready.ts","../src/listeners/utils.ts","../src/listeners/listeners.ts","../src/readers/index.ts","../src/hooks/use-is-preview-mode.ts","../src/hooks/use-is-route-active.ts","../src/hooks/use-route-status.ts"],"sourcesContent":["import { jQueryDeferred } from './types';\n\nexport function isJQueryDeferred<T>( value: unknown ): value is jQueryDeferred<T> {\n\t// TODO: Copied from:\n\t// https://github.com/elementor/elementor/blob/6a74fc9/modules/web-cli/assets/js/core/commands.js#L410\n\n\treturn ( !! value ) &&\n\t\t'object' === typeof value &&\n\t\tObject.hasOwn( value, 'promise' ) &&\n\t\tObject.hasOwn( value, 'then' ) &&\n\t\tObject.hasOwn( value, 'fail' );\n}\n\nexport function promisifyJQueryDeferred<T>( deferred: jQueryDeferred<T> ): Promise<T> {\n\treturn new Promise( ( resolve, reject ) => {\n\t\tdeferred.then( resolve, reject );\n\t} );\n}\n","import { ExtendedWindow } from './types';\nimport { isJQueryDeferred, promisifyJQueryDeferred } from './utils';\n\nexport function runCommand( command: string, args?: object ) {\n\tconst extendedWindow = window as unknown as ExtendedWindow;\n\n\tif ( ! extendedWindow.$e?.run ) {\n\t\treturn Promise.reject( '`$e.run()` is not available' );\n\t}\n\n\tconst result = extendedWindow.$e.run( command, args );\n\n\tif ( result instanceof Promise ) {\n\t\treturn result;\n\t}\n\n\tif ( isJQueryDeferred( result ) ) {\n\t\treturn promisifyJQueryDeferred( result );\n\t}\n\n\treturn Promise.resolve( result );\n}\n\nexport function openRoute( route: string ) {\n\tconst extendedWindow = window as unknown as ExtendedWindow;\n\n\tif ( ! extendedWindow.$e?.route ) {\n\t\treturn Promise.reject( '`$e.route()` is not available' );\n\t}\n\n\ttry {\n\t\treturn Promise.resolve(\n\t\t\textendedWindow.$e.route( route )\n\t\t);\n\t} catch ( e ) {\n\t\treturn Promise.reject( e );\n\t}\n}\n","import { useEffect, useState } from 'react';\nimport { EventDescriptor, listenTo } from '../listeners';\n\nexport default function useListenTo<T>(\n\tevent: EventDescriptor | EventDescriptor[],\n\tgetSnapshot: () => T,\n\tdeps: unknown[] = []\n) {\n\tconst [ snapshot, setSnapshot ] = useState( () => getSnapshot() );\n\n\tuseEffect( () => {\n\t\tconst updateState = () => setSnapshot( getSnapshot() );\n\n\t\t// Ensure the state is re-calculated when the dependencies have been changed.\n\t\tupdateState();\n\n\t\treturn listenTo( event, updateState );\n\t}, deps ); // eslint-disable-line react-hooks/exhaustive-deps\n\n\treturn snapshot;\n}\n","import { CommandEventDescriptor, RouteEventDescriptor, WindowEventDescriptor } from './types';\n\nexport const commandStartEvent = ( command: CommandEventDescriptor['name'] ): CommandEventDescriptor => {\n\treturn {\n\t\ttype: 'command',\n\t\tname: command,\n\t\tstate: 'before',\n\t};\n};\n\nexport const commandEndEvent = ( command: CommandEventDescriptor['name'] ): CommandEventDescriptor => {\n\treturn {\n\t\ttype: 'command',\n\t\tname: command,\n\t\tstate: 'after',\n\t};\n};\n\nexport const routeOpenEvent = ( route: RouteEventDescriptor['name'] ): RouteEventDescriptor => {\n\treturn {\n\t\ttype: 'route',\n\t\tname: route,\n\t\tstate: 'open',\n\t};\n};\n\nexport const routeCloseEvent = ( route: RouteEventDescriptor['name'] ): RouteEventDescriptor => {\n\treturn {\n\t\ttype: 'route',\n\t\tname: route,\n\t\tstate: 'close',\n\t};\n};\n\nexport const windowEvent = ( event: WindowEventDescriptor['name'] ): WindowEventDescriptor => {\n\treturn {\n\t\ttype: 'window-event',\n\t\tname: event,\n\t};\n};\n\nexport const v1ReadyEvent = () => {\n\treturn windowEvent( 'elementor/initialized' );\n};\n\nexport const editModeChangeEvent = () => {\n\treturn windowEvent( 'elementor/edit-mode/change' );\n};\n","/**\n * This file is used to store the state of the isReady variable, which is used to determine\n * if the adapter is ready to receive events (editor v1 and v2 are loaded).\n */\n\nlet ready = false;\n\nexport function isReady() {\n\treturn ready;\n}\n\nexport function setReady( value: boolean ) {\n\tready = value;\n}\n","import { ExtendedWindow, ListenerEvent } from './types';\nimport { setReady } from './is-ready';\n\nexport function dispatchReadyEvent() {\n\treturn getV1LoadingPromise().then( () => {\n\t\tsetReady( true );\n\t\twindow.dispatchEvent( new CustomEvent( 'elementor/initialized' ) );\n\t} );\n}\n\nfunction getV1LoadingPromise() {\n\tconst v1LoadingPromise = ( window as unknown as ExtendedWindow ).__elementorEditorV1LoadingPromise;\n\n\tif ( ! v1LoadingPromise ) {\n\t\treturn Promise.reject( 'Elementor Editor V1 is not loaded' );\n\t}\n\n\treturn v1LoadingPromise;\n}\n\nexport function normalizeEvent( e: ListenerEvent['originalEvent'] ): ListenerEvent {\n\tif ( e instanceof CustomEvent && e.detail?.command ) {\n\t\treturn {\n\t\t\ttype: 'command',\n\t\t\tcommand: e.detail.command,\n\t\t\targs: e.detail.args,\n\t\t\toriginalEvent: e,\n\t\t};\n\t}\n\n\tif ( e instanceof CustomEvent && e.detail?.route ) {\n\t\treturn {\n\t\t\ttype: 'route',\n\t\t\troute: e.detail.route,\n\t\t\toriginalEvent: e,\n\t\t};\n\t}\n\n\treturn {\n\t\ttype: 'window-event',\n\t\tevent: e.type,\n\t\toriginalEvent: e,\n\t};\n}\n","import { normalizeEvent } from './utils';\nimport {\n\tCommandEventDescriptor,\n\tEventDescriptor,\n\tListenerCallback,\n\tRouteEventDescriptor,\n\tWindowEventDescriptor,\n} from './types';\nimport { isReady, setReady } from './is-ready';\n\nconst callbacksByEvent = new Map<EventDescriptor['name'], ListenerCallback[]>();\nlet abortController = new AbortController();\n\nexport function listenTo(\n\teventDescriptors: EventDescriptor | EventDescriptor[],\n\tcallback: ListenerCallback\n) {\n\tif ( ! Array.isArray( eventDescriptors ) ) {\n\t\teventDescriptors = [ eventDescriptors ];\n\t}\n\n\t// @see https://github.com/typescript-eslint/typescript-eslint/issues/2841\n\t// eslint-disable-next-line array-callback-return -- Clashes with typescript.\n\tconst cleanups = eventDescriptors.map( ( event ) => {\n\t\tconst { type, name } = event;\n\n\t\tswitch ( type ) {\n\t\t\tcase 'command':\n\t\t\t\treturn registerCommandListener( name, event.state, callback );\n\n\t\t\tcase 'route':\n\t\t\t\treturn registerRouteListener( name, event.state, callback );\n\n\t\t\tcase 'window-event':\n\t\t\t\treturn registerWindowEventListener( name, callback );\n\t\t}\n\t} );\n\n\treturn () => {\n\t\tcleanups.forEach( ( cleanup ) => cleanup() );\n\t};\n}\n\nexport function flushListeners() {\n\tabortController.abort();\n\tcallbacksByEvent.clear();\n\tsetReady( false );\n\n\tabortController = new AbortController();\n}\n\nfunction registerCommandListener(\n\tcommand: CommandEventDescriptor['name'],\n\tstate: CommandEventDescriptor['state'],\n\tcallback: ListenerCallback\n) {\n\treturn registerWindowEventListener( `elementor/commands/run/${ state }`, ( e ) => {\n\t\tconst shouldRunCallback = e.type === 'command' && e.command === command;\n\n\t\tif ( shouldRunCallback ) {\n\t\t\tcallback( e );\n\t\t}\n\t} );\n}\n\nfunction registerRouteListener(\n\troute: RouteEventDescriptor['name'],\n\tstate: RouteEventDescriptor['state'],\n\tcallback: ListenerCallback\n) {\n\treturn registerWindowEventListener( `elementor/routes/${ state }`, ( e ) => {\n\t\tconst shouldRunCallback = e.type === 'route' && e.route.startsWith( route );\n\n\t\tif ( shouldRunCallback ) {\n\t\t\tcallback( e );\n\t\t}\n\t} );\n}\n\nfunction registerWindowEventListener( event: WindowEventDescriptor['name'], callback: ListenerCallback ) {\n\tconst isFirstListener = ! callbacksByEvent.has( event );\n\n\tif ( isFirstListener ) {\n\t\tcallbacksByEvent.set( event, [] );\n\n\t\taddListener( event );\n\t}\n\n\tcallbacksByEvent.get( event )?.push( callback );\n\n\treturn () => {\n\t\tconst callbacks = callbacksByEvent.get( event );\n\n\t\tif ( ! callbacks?.length ) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst filtered = callbacks.filter( ( cb ) => cb !== callback );\n\n\t\tcallbacksByEvent.set( event, filtered );\n\t};\n}\n\nfunction addListener( event: EventDescriptor['name'] ) {\n\twindow.addEventListener(\n\t\tevent,\n\t\tmakeEventHandler( event ),\n\t\t{ signal: abortController.signal }\n\t);\n}\n\nfunction makeEventHandler( event: EventDescriptor['name'] ): EventListener {\n\treturn ( e ) => {\n\t\tif ( ! isReady() ) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst normalizedEvent = normalizeEvent( e );\n\n\t\tcallbacksByEvent.get( event )?.forEach( ( callback ) => {\n\t\t\tcallback( normalizedEvent );\n\t\t} );\n\t};\n}\n","import { ExtendedWindow } from './types';\n\nexport function isRouteActive( route: string ) {\n\tconst extendedWindow = window as unknown as ExtendedWindow;\n\n\treturn !! extendedWindow.$e?.routes?.isPartOf( route );\n}\n\nexport function getCurrentEditMode() {\n\tconst extendedWindow = window as unknown as ExtendedWindow;\n\n\treturn extendedWindow.elementor?.channels?.dataEditMode?.request?.( 'activeMode' );\n}\n","import useListenTo from './use-listen-to';\nimport { getCurrentEditMode } from '../readers';\nimport { editModeChangeEvent } from '../listeners';\n\nexport default function useIsPreviewMode() {\n\treturn useListenTo(\n\t\teditModeChangeEvent(),\n\t\t() => getCurrentEditMode() === 'preview'\n\t);\n}\n","import useListenTo from './use-listen-to';\nimport { isRouteActive } from '../readers';\nimport { routeCloseEvent, routeOpenEvent, RouteEventDescriptor } from '../listeners';\n\nexport default function useIsRouteActive( route: RouteEventDescriptor['name'] ) {\n\treturn useListenTo(\n\t\t[\n\t\t\trouteOpenEvent( route ),\n\t\t\trouteCloseEvent( route ),\n\t\t],\n\t\t() => isRouteActive( route ),\n\t\t[ route ]\n\t);\n}\n","import useIsPreviewMode from './use-is-preview-mode';\nimport useIsRouteActive from './use-is-route-active';\nimport { RouteEventDescriptor } from '../listeners';\n\ntype Options = {\n\tblockOnKitRoutes?: boolean,\n\tblockOnPreviewMode?: boolean,\n}\n\nexport default function useRouteStatus(\n\troute: RouteEventDescriptor['name'],\n\t{\n\t\tblockOnKitRoutes = true,\n\t\tblockOnPreviewMode = true,\n\t}: Options = {}\n) {\n\tconst isRouteActive = useIsRouteActive( route );\n\tconst isKitRouteActive = useIsRouteActive( 'panel/global' );\n\tconst isPreviewMode = useIsPreviewMode();\n\n\tconst isActive = isRouteActive && ! ( blockOnPreviewMode && isPreviewMode );\n\n\tconst isBlocked = (\n\t\t( blockOnPreviewMode && isPreviewMode ) ||\n\t\t( blockOnKitRoutes && isKitRouteActive )\n\t);\n\n\treturn {\n\t\tisActive,\n\t\tisBlocked,\n\t};\n}\n"],"mappings":";AAEO,SAAS,iBAAqB,OAA6C;AAIjF,SAAS,CAAC,CAAE,SACX,aAAa,OAAO,SACpB,OAAO,OAAQ,OAAO,SAAU,KAChC,OAAO,OAAQ,OAAO,MAAO,KAC7B,OAAO,OAAQ,OAAO,MAAO;AAC/B;AAEO,SAAS,wBAA4B,UAA0C;AACrF,SAAO,IAAI,QAAS,CAAE,SAAS,WAAY;AAC1C,aAAS,KAAM,SAAS,MAAO;AAAA,EAChC,CAAE;AACH;;;ACdO,SAAS,WAAY,SAAiB,MAAgB;AAC5D,QAAM,iBAAiB;AAEvB,MAAK,CAAE,eAAe,IAAI,KAAM;AAC/B,WAAO,QAAQ,OAAQ,6BAA8B;AAAA,EACtD;AAEA,QAAM,SAAS,eAAe,GAAG,IAAK,SAAS,IAAK;AAEpD,MAAK,kBAAkB,SAAU;AAChC,WAAO;AAAA,EACR;AAEA,MAAK,iBAAkB,MAAO,GAAI;AACjC,WAAO,wBAAyB,MAAO;AAAA,EACxC;AAEA,SAAO,QAAQ,QAAS,MAAO;AAChC;AAEO,SAAS,UAAW,OAAgB;AAC1C,QAAM,iBAAiB;AAEvB,MAAK,CAAE,eAAe,IAAI,OAAQ;AACjC,WAAO,QAAQ,OAAQ,+BAAgC;AAAA,EACxD;AAEA,MAAI;AACH,WAAO,QAAQ;AAAA,MACd,eAAe,GAAG,MAAO,KAAM;AAAA,IAChC;AAAA,EACD,SAAU,GAAR;AACD,WAAO,QAAQ,OAAQ,CAAE;AAAA,EAC1B;AACD;;;ACrCA,SAAS,WAAW,gBAAgB;;;ACE7B,IAAM,oBAAoB,CAAE,YAAqE;AACvG,SAAO;AAAA,IACN,MAAM;AAAA,IACN,MAAM;AAAA,IACN,OAAO;AAAA,EACR;AACD;AAEO,IAAM,kBAAkB,CAAE,YAAqE;AACrG,SAAO;AAAA,IACN,MAAM;AAAA,IACN,MAAM;AAAA,IACN,OAAO;AAAA,EACR;AACD;AAEO,IAAM,iBAAiB,CAAE,UAA+D;AAC9F,SAAO;AAAA,IACN,MAAM;AAAA,IACN,MAAM;AAAA,IACN,OAAO;AAAA,EACR;AACD;AAEO,IAAM,kBAAkB,CAAE,UAA+D;AAC/F,SAAO;AAAA,IACN,MAAM;AAAA,IACN,MAAM;AAAA,IACN,OAAO;AAAA,EACR;AACD;AAEO,IAAM,cAAc,CAAE,UAAiE;AAC7F,SAAO;AAAA,IACN,MAAM;AAAA,IACN,MAAM;AAAA,EACP;AACD;AAEO,IAAM,eAAe,MAAM;AACjC,SAAO,YAAa,uBAAwB;AAC7C;AAEO,IAAM,sBAAsB,MAAM;AACxC,SAAO,YAAa,4BAA6B;AAClD;;;AC1CA,IAAI,QAAQ;AAEL,SAAS,UAAU;AACzB,SAAO;AACR;AAEO,SAAS,SAAU,OAAiB;AAC1C,UAAQ;AACT;;;ACVO,SAAS,qBAAqB;AACpC,SAAO,oBAAoB,EAAE,KAAM,MAAM;AACxC,aAAU,IAAK;AACf,WAAO,cAAe,IAAI,YAAa,uBAAwB,CAAE;AAAA,EAClE,CAAE;AACH;AAEA,SAAS,sBAAsB;AAC9B,QAAM,mBAAqB,OAAsC;AAEjE,MAAK,CAAE,kBAAmB;AACzB,WAAO,QAAQ,OAAQ,mCAAoC;AAAA,EAC5D;AAEA,SAAO;AACR;AAEO,SAAS,eAAgB,GAAmD;AAClF,MAAK,aAAa,eAAe,EAAE,QAAQ,SAAU;AACpD,WAAO;AAAA,MACN,MAAM;AAAA,MACN,SAAS,EAAE,OAAO;AAAA,MAClB,MAAM,EAAE,OAAO;AAAA,MACf,eAAe;AAAA,IAChB;AAAA,EACD;AAEA,MAAK,aAAa,eAAe,EAAE,QAAQ,OAAQ;AAClD,WAAO;AAAA,MACN,MAAM;AAAA,MACN,OAAO,EAAE,OAAO;AAAA,MAChB,eAAe;AAAA,IAChB;AAAA,EACD;AAEA,SAAO;AAAA,IACN,MAAM;AAAA,IACN,OAAO,EAAE;AAAA,IACT,eAAe;AAAA,EAChB;AACD;;;ACjCA,IAAM,mBAAmB,oBAAI,IAAiD;AAC9E,IAAI,kBAAkB,IAAI,gBAAgB;AAEnC,SAAS,SACf,kBACA,UACC;AACD,MAAK,CAAE,MAAM,QAAS,gBAAiB,GAAI;AAC1C,uBAAmB,CAAE,gBAAiB;AAAA,EACvC;AAIA,QAAM,WAAW,iBAAiB,IAAK,CAAE,UAAW;AACnD,UAAM,EAAE,MAAM,KAAK,IAAI;AAEvB,YAAS,MAAO;AAAA,MACf,KAAK;AACJ,eAAO,wBAAyB,MAAM,MAAM,OAAO,QAAS;AAAA,MAE7D,KAAK;AACJ,eAAO,sBAAuB,MAAM,MAAM,OAAO,QAAS;AAAA,MAE3D,KAAK;AACJ,eAAO,4BAA6B,MAAM,QAAS;AAAA,IACrD;AAAA,EACD,CAAE;AAEF,SAAO,MAAM;AACZ,aAAS,QAAS,CAAE,YAAa,QAAQ,CAAE;AAAA,EAC5C;AACD;AAEO,SAAS,iBAAiB;AAChC,kBAAgB,MAAM;AACtB,mBAAiB,MAAM;AACvB,WAAU,KAAM;AAEhB,oBAAkB,IAAI,gBAAgB;AACvC;AAEA,SAAS,wBACR,SACA,OACA,UACC;AACD,SAAO,4BAA6B,0BAA2B,SAAU,CAAE,MAAO;AACjF,UAAM,oBAAoB,EAAE,SAAS,aAAa,EAAE,YAAY;AAEhE,QAAK,mBAAoB;AACxB,eAAU,CAAE;AAAA,IACb;AAAA,EACD,CAAE;AACH;AAEA,SAAS,sBACR,OACA,OACA,UACC;AACD,SAAO,4BAA6B,oBAAqB,SAAU,CAAE,MAAO;AAC3E,UAAM,oBAAoB,EAAE,SAAS,WAAW,EAAE,MAAM,WAAY,KAAM;AAE1E,QAAK,mBAAoB;AACxB,eAAU,CAAE;AAAA,IACb;AAAA,EACD,CAAE;AACH;AAEA,SAAS,4BAA6B,OAAsC,UAA6B;AACxG,QAAM,kBAAkB,CAAE,iBAAiB,IAAK,KAAM;AAEtD,MAAK,iBAAkB;AACtB,qBAAiB,IAAK,OAAO,CAAC,CAAE;AAEhC,gBAAa,KAAM;AAAA,EACpB;AAEA,mBAAiB,IAAK,KAAM,GAAG,KAAM,QAAS;AAE9C,SAAO,MAAM;AACZ,UAAM,YAAY,iBAAiB,IAAK,KAAM;AAE9C,QAAK,CAAE,WAAW,QAAS;AAC1B;AAAA,IACD;AAEA,UAAM,WAAW,UAAU,OAAQ,CAAE,OAAQ,OAAO,QAAS;AAE7D,qBAAiB,IAAK,OAAO,QAAS;AAAA,EACvC;AACD;AAEA,SAAS,YAAa,OAAiC;AACtD,SAAO;AAAA,IACN;AAAA,IACA,iBAAkB,KAAM;AAAA,IACxB,EAAE,QAAQ,gBAAgB,OAAO;AAAA,EAClC;AACD;AAEA,SAAS,iBAAkB,OAAgD;AAC1E,SAAO,CAAE,MAAO;AACf,QAAK,CAAE,QAAQ,GAAI;AAClB;AAAA,IACD;AAEA,UAAM,kBAAkB,eAAgB,CAAE;AAE1C,qBAAiB,IAAK,KAAM,GAAG,QAAS,CAAE,aAAc;AACvD,eAAU,eAAgB;AAAA,IAC3B,CAAE;AAAA,EACH;AACD;;;AJxHe,SAAR,YACN,OACA,aACA,OAAkB,CAAC,GAClB;AACD,QAAM,CAAE,UAAU,WAAY,IAAI,SAAU,MAAM,YAAY,CAAE;AAEhE,YAAW,MAAM;AAChB,UAAM,cAAc,MAAM,YAAa,YAAY,CAAE;AAGrD,gBAAY;AAEZ,WAAO,SAAU,OAAO,WAAY;AAAA,EACrC,GAAG,IAAK;AAER,SAAO;AACR;;;AKlBO,SAAS,cAAe,OAAgB;AAC9C,QAAM,iBAAiB;AAEvB,SAAO,CAAC,CAAE,eAAe,IAAI,QAAQ,SAAU,KAAM;AACtD;AAEO,SAAS,qBAAqB;AACpC,QAAM,iBAAiB;AAEvB,SAAO,eAAe,WAAW,UAAU,cAAc,UAAW,YAAa;AAClF;;;ACRe,SAAR,mBAAoC;AAC1C,SAAO;AAAA,IACN,oBAAoB;AAAA,IACpB,MAAM,mBAAmB,MAAM;AAAA,EAChC;AACD;;;ACLe,SAAR,iBAAmC,OAAsC;AAC/E,SAAO;AAAA,IACN;AAAA,MACC,eAAgB,KAAM;AAAA,MACtB,gBAAiB,KAAM;AAAA,IACxB;AAAA,IACA,MAAM,cAAe,KAAM;AAAA,IAC3B,CAAE,KAAM;AAAA,EACT;AACD;;;ACJe,SAAR,eACN,OACA;AAAA,EACC,mBAAmB;AAAA,EACnB,qBAAqB;AACtB,IAAa,CAAC,GACb;AACD,QAAMA,iBAAgB,iBAAkB,KAAM;AAC9C,QAAM,mBAAmB,iBAAkB,cAAe;AAC1D,QAAM,gBAAgB,iBAAiB;AAEvC,QAAM,WAAWA,kBAAiB,EAAI,sBAAsB;AAE5D,QAAM,YACH,sBAAsB,iBACtB,oBAAoB;AAGvB,SAAO;AAAA,IACN;AAAA,IACA;AAAA,EACD;AACD;","names":["isRouteActive"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@elementor/editor-v1-adapters",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"author": "Elementor Team",
|
|
6
|
+
"homepage": "https://elementor.com/",
|
|
7
|
+
"license": "GPL-3.0-or-later",
|
|
8
|
+
"main": "dist/index.js",
|
|
9
|
+
"module": "dist/index.mjs",
|
|
10
|
+
"types": "dist/index.d.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"import": "./dist/index.mjs",
|
|
14
|
+
"require": "./dist/index.js",
|
|
15
|
+
"types": "./dist/index.d.ts"
|
|
16
|
+
},
|
|
17
|
+
"./package.json": "./package.json"
|
|
18
|
+
},
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "https://github.com/elementor/elementor-packages.git",
|
|
22
|
+
"directory": "packages/editor-v1-adapters"
|
|
23
|
+
},
|
|
24
|
+
"bugs": {
|
|
25
|
+
"url": "https://github.com/elementor/elementor-packages/issues"
|
|
26
|
+
},
|
|
27
|
+
"publishConfig": {
|
|
28
|
+
"access": "public"
|
|
29
|
+
},
|
|
30
|
+
"scripts": {
|
|
31
|
+
"build": "tsup --config=../../tsup.build.ts",
|
|
32
|
+
"dev": "tsup --config=../../tsup.dev.ts"
|
|
33
|
+
},
|
|
34
|
+
"peerDependencies": {
|
|
35
|
+
"react": "17.x"
|
|
36
|
+
},
|
|
37
|
+
"gitHead": "2ba9f13a9dbd085eb6ed8e6e303e9275ce626b8d"
|
|
38
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export function dispatchCommandBefore( command: string ) {
|
|
2
|
+
window.dispatchEvent( new CustomEvent( 'elementor/commands/run/before', {
|
|
3
|
+
detail: {
|
|
4
|
+
command,
|
|
5
|
+
},
|
|
6
|
+
} ) );
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function dispatchCommandAfter( command: string ) {
|
|
10
|
+
window.dispatchEvent( new CustomEvent( 'elementor/commands/run/after', {
|
|
11
|
+
detail: {
|
|
12
|
+
command,
|
|
13
|
+
},
|
|
14
|
+
} ) );
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function dispatchRouteOpen( route: string ) {
|
|
18
|
+
window.dispatchEvent( new CustomEvent( 'elementor/routes/open', {
|
|
19
|
+
detail: {
|
|
20
|
+
route,
|
|
21
|
+
},
|
|
22
|
+
} ) );
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function dispatchRouteClose( route: string ) {
|
|
26
|
+
window.dispatchEvent( new CustomEvent( 'elementor/routes/close', {
|
|
27
|
+
detail: {
|
|
28
|
+
route,
|
|
29
|
+
},
|
|
30
|
+
} ) );
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function dispatchEditModeChange() {
|
|
34
|
+
window.dispatchEvent( new CustomEvent( 'elementor/edit-mode/change' ) );
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function dispatchWindowEvent( event: string ) {
|
|
38
|
+
window.dispatchEvent( new CustomEvent( event ) );
|
|
39
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { openRoute, runCommand } from '../';
|
|
2
|
+
|
|
3
|
+
type ExtendedWindow = Window & {
|
|
4
|
+
$e: {
|
|
5
|
+
run: jest.Mock;
|
|
6
|
+
route: jest.Mock;
|
|
7
|
+
},
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
describe( '@elementor/editor-v1-adapters/dispatchers', () => {
|
|
11
|
+
let eRun: jest.Mock,
|
|
12
|
+
eRoute: jest.Mock;
|
|
13
|
+
|
|
14
|
+
beforeEach( () => {
|
|
15
|
+
const extendedWindow = window as unknown as ExtendedWindow;
|
|
16
|
+
|
|
17
|
+
extendedWindow.$e = {
|
|
18
|
+
run: jest.fn(),
|
|
19
|
+
route: jest.fn(),
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
eRun = extendedWindow.$e.run;
|
|
23
|
+
eRoute = extendedWindow.$e.route;
|
|
24
|
+
} );
|
|
25
|
+
|
|
26
|
+
it( 'should run a V1 command that returns Promise', () => {
|
|
27
|
+
// Arrange.
|
|
28
|
+
const command = 'editor/documents/open',
|
|
29
|
+
args = { test: 'arg' };
|
|
30
|
+
|
|
31
|
+
eRun.mockReturnValue( Promise.resolve( 'result' ) );
|
|
32
|
+
|
|
33
|
+
// Act.
|
|
34
|
+
const result = runCommand( command, args );
|
|
35
|
+
|
|
36
|
+
// Assert.
|
|
37
|
+
expect( eRun ).toHaveBeenCalledWith( command, args );
|
|
38
|
+
expect( result ).toEqual( Promise.resolve( 'result' ) );
|
|
39
|
+
} );
|
|
40
|
+
|
|
41
|
+
it( 'should run a V1 command that returns jQuery.Deferred object', () => {
|
|
42
|
+
// Arrange.
|
|
43
|
+
const command = 'editor/documents/open',
|
|
44
|
+
args = { test: 'arg' };
|
|
45
|
+
|
|
46
|
+
eRun.mockReturnValue( makeJQueryDeferred( 'result' ) );
|
|
47
|
+
|
|
48
|
+
// Act.
|
|
49
|
+
const result = runCommand( command, args );
|
|
50
|
+
|
|
51
|
+
// Assert.
|
|
52
|
+
expect( eRun ).toHaveBeenCalledWith( command, args );
|
|
53
|
+
expect( result ).toEqual( Promise.resolve( 'result' ) );
|
|
54
|
+
} );
|
|
55
|
+
|
|
56
|
+
it( 'should run a V1 command that returns a plain value', () => {
|
|
57
|
+
// Arrange.
|
|
58
|
+
const command = 'editor/documents/open',
|
|
59
|
+
args = { test: 'arg' };
|
|
60
|
+
|
|
61
|
+
eRun.mockReturnValue( 'result' );
|
|
62
|
+
|
|
63
|
+
// Act.
|
|
64
|
+
const result = runCommand( command, args );
|
|
65
|
+
|
|
66
|
+
// Assert.
|
|
67
|
+
expect( eRun ).toHaveBeenCalledWith( command, args );
|
|
68
|
+
expect( result ).toEqual( Promise.resolve( 'result' ) );
|
|
69
|
+
} );
|
|
70
|
+
|
|
71
|
+
it( 'should reject when trying to run a V1 command and `$e.run()` is unavailable', () => {
|
|
72
|
+
// Arrange.
|
|
73
|
+
delete ( window as { $e?: unknown } ).$e;
|
|
74
|
+
|
|
75
|
+
// Act & Assert.
|
|
76
|
+
expect( () => runCommand( 'editor/documents/open' ) )
|
|
77
|
+
.rejects
|
|
78
|
+
.toEqual( '`$e.run()` is not available' );
|
|
79
|
+
} );
|
|
80
|
+
|
|
81
|
+
it( 'should open a V1 route', () => {
|
|
82
|
+
// Arrange.
|
|
83
|
+
const route = 'test/route';
|
|
84
|
+
|
|
85
|
+
// Act.
|
|
86
|
+
const result = openRoute( route );
|
|
87
|
+
|
|
88
|
+
// Assert.
|
|
89
|
+
expect( eRoute ).toHaveBeenCalledWith( route );
|
|
90
|
+
expect( result ).toEqual( Promise.resolve() );
|
|
91
|
+
} );
|
|
92
|
+
|
|
93
|
+
it( 'should reject when failing to open a V1 route', () => {
|
|
94
|
+
// Arrange.
|
|
95
|
+
const route = 'test/route';
|
|
96
|
+
|
|
97
|
+
eRoute.mockImplementation( ( r: string ) => {
|
|
98
|
+
throw `Cannot find ${ r }`;
|
|
99
|
+
} );
|
|
100
|
+
|
|
101
|
+
// Act.
|
|
102
|
+
expect( () => openRoute( route ) )
|
|
103
|
+
.rejects
|
|
104
|
+
.toEqual( 'Cannot find test/route' );
|
|
105
|
+
} );
|
|
106
|
+
|
|
107
|
+
it( 'should reject when trying to open a V1 route and `$e.route()` is unavailable', () => {
|
|
108
|
+
// Arrange.
|
|
109
|
+
delete ( window as { $e?: unknown } ).$e;
|
|
110
|
+
|
|
111
|
+
// Act & Assert.
|
|
112
|
+
expect( () => openRoute( 'test/route' ) )
|
|
113
|
+
.rejects
|
|
114
|
+
.toEqual( '`$e.route()` is not available' );
|
|
115
|
+
} );
|
|
116
|
+
} );
|
|
117
|
+
|
|
118
|
+
function makeJQueryDeferred( value: unknown ) {
|
|
119
|
+
return {
|
|
120
|
+
then: () => value,
|
|
121
|
+
promise: () => value,
|
|
122
|
+
fail: () => {
|
|
123
|
+
throw 'Error';
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { ExtendedWindow } from './types';
|
|
2
|
+
import { isJQueryDeferred, promisifyJQueryDeferred } from './utils';
|
|
3
|
+
|
|
4
|
+
export function runCommand( command: string, args?: object ) {
|
|
5
|
+
const extendedWindow = window as unknown as ExtendedWindow;
|
|
6
|
+
|
|
7
|
+
if ( ! extendedWindow.$e?.run ) {
|
|
8
|
+
return Promise.reject( '`$e.run()` is not available' );
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const result = extendedWindow.$e.run( command, args );
|
|
12
|
+
|
|
13
|
+
if ( result instanceof Promise ) {
|
|
14
|
+
return result;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if ( isJQueryDeferred( result ) ) {
|
|
18
|
+
return promisifyJQueryDeferred( result );
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return Promise.resolve( result );
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function openRoute( route: string ) {
|
|
25
|
+
const extendedWindow = window as unknown as ExtendedWindow;
|
|
26
|
+
|
|
27
|
+
if ( ! extendedWindow.$e?.route ) {
|
|
28
|
+
return Promise.reject( '`$e.route()` is not available' );
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
return Promise.resolve(
|
|
33
|
+
extendedWindow.$e.route( route )
|
|
34
|
+
);
|
|
35
|
+
} catch ( e ) {
|
|
36
|
+
return Promise.reject( e );
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './dispatchers';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export type jQueryDeferred<T> = {
|
|
2
|
+
then<U>( onFulfill: ( value: T ) => U, onReject?: ( error: unknown ) => U ): jQueryDeferred<U>;
|
|
3
|
+
};
|
|
4
|
+
|
|
5
|
+
export type ExtendedWindow = Window & {
|
|
6
|
+
$e: {
|
|
7
|
+
run: ( command: string, args?: object ) => unknown;
|
|
8
|
+
route: ( route: string ) => void;
|
|
9
|
+
},
|
|
10
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { jQueryDeferred } from './types';
|
|
2
|
+
|
|
3
|
+
export function isJQueryDeferred<T>( value: unknown ): value is jQueryDeferred<T> {
|
|
4
|
+
// TODO: Copied from:
|
|
5
|
+
// https://github.com/elementor/elementor/blob/6a74fc9/modules/web-cli/assets/js/core/commands.js#L410
|
|
6
|
+
|
|
7
|
+
return ( !! value ) &&
|
|
8
|
+
'object' === typeof value &&
|
|
9
|
+
Object.hasOwn( value, 'promise' ) &&
|
|
10
|
+
Object.hasOwn( value, 'then' ) &&
|
|
11
|
+
Object.hasOwn( value, 'fail' );
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function promisifyJQueryDeferred<T>( deferred: jQueryDeferred<T> ): Promise<T> {
|
|
15
|
+
return new Promise( ( resolve, reject ) => {
|
|
16
|
+
deferred.then( resolve, reject );
|
|
17
|
+
} );
|
|
18
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { EditMode, ExtendedWindow } from '../../readers/types';
|
|
2
|
+
|
|
3
|
+
const extendedWindow = window as unknown as ExtendedWindow;
|
|
4
|
+
|
|
5
|
+
export function mockIsRouteActive( implementation: ( route: string ) => boolean ) {
|
|
6
|
+
extendedWindow.$e = {
|
|
7
|
+
routes: {
|
|
8
|
+
isPartOf: jest.fn( implementation ),
|
|
9
|
+
},
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function mockGetCurrentEditMode( implementation: () => EditMode ) {
|
|
14
|
+
extendedWindow.elementor = {
|
|
15
|
+
channels: {
|
|
16
|
+
dataEditMode: {
|
|
17
|
+
request: jest.fn( implementation ),
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { act, renderHook } from '@testing-library/react-hooks';
|
|
2
|
+
import { useIsPreviewMode } from '../../';
|
|
3
|
+
import { dispatchEditModeChange } from '../../__tests__/utils';
|
|
4
|
+
import { mockGetCurrentEditMode } from './test-utils';
|
|
5
|
+
|
|
6
|
+
describe( '@elementor/editor-v1-adapters - useIsPreviewMode', () => {
|
|
7
|
+
it.each( [
|
|
8
|
+
{ mode: 'preview', expected: true },
|
|
9
|
+
{ mode: 'edit', expected: false },
|
|
10
|
+
{ mode: 'picker', expected: false },
|
|
11
|
+
] as const )( 'should return $expected when the current edit mode is $mode', ( { mode, expected } ) => {
|
|
12
|
+
// Arrange.
|
|
13
|
+
mockGetCurrentEditMode( () => mode );
|
|
14
|
+
|
|
15
|
+
// Act.
|
|
16
|
+
const { result } = renderHook( () => useIsPreviewMode() );
|
|
17
|
+
|
|
18
|
+
// Assert.
|
|
19
|
+
expect( result.current ).toBe( expected );
|
|
20
|
+
} );
|
|
21
|
+
|
|
22
|
+
it( 'should update the state when the edit mode changes', () => {
|
|
23
|
+
// Arrange.
|
|
24
|
+
const { result } = renderHook( () => useIsPreviewMode() );
|
|
25
|
+
|
|
26
|
+
// Assert.
|
|
27
|
+
expect( result.current ).toBe( false );
|
|
28
|
+
|
|
29
|
+
// Act.
|
|
30
|
+
act( () => {
|
|
31
|
+
mockGetCurrentEditMode( () => 'preview' );
|
|
32
|
+
dispatchEditModeChange();
|
|
33
|
+
} );
|
|
34
|
+
|
|
35
|
+
// Assert.
|
|
36
|
+
expect( result.current ).toBe( true );
|
|
37
|
+
|
|
38
|
+
// Act.
|
|
39
|
+
act( () => {
|
|
40
|
+
mockGetCurrentEditMode( () => 'edit' );
|
|
41
|
+
dispatchEditModeChange();
|
|
42
|
+
} );
|
|
43
|
+
|
|
44
|
+
// Assert.
|
|
45
|
+
expect( result.current ).toBe( false );
|
|
46
|
+
} );
|
|
47
|
+
} );
|