@elementor/editor 0.2.0 → 0.3.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/CHANGELOG.md ADDED
@@ -0,0 +1,41 @@
1
+ # Change Log
2
+
3
+ All notable changes to this project will be documented in this file.
4
+ See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
+
6
+ # 0.3.0 (2023-05-21)
7
+
8
+
9
+ ### Features
10
+
11
+ * **editor-site-navigation:** change post without refresh [ED-10713] ([#39](https://github.com/elementor/elementor-packages/issues/39)) ([b72e82f](https://github.com/elementor/elementor-packages/commit/b72e82f9adb9c1237300cbf603f33f268f9f0400))
12
+ * **locations:** change api to support props for slots [ED-10730] ([#38](https://github.com/elementor/elementor-packages/issues/38)) ([44bec3c](https://github.com/elementor/elementor-packages/commit/44bec3cda1020037ba7105c6f05ce4baa8e3b376))
13
+
14
+
15
+
16
+ ## 0.2.1 (2023-05-11)
17
+
18
+
19
+ ### Bug Fixes
20
+
21
+ * **editor-app-bar:** make save options button disabled in site-settings [ED-10221] ([#30](https://github.com/elementor/elementor-packages/issues/30)) ([1991096](https://github.com/elementor/elementor-packages/commit/1991096115efeae7bc3648e4889899b85d7328d6))
22
+
23
+
24
+
25
+ # 0.2.0 (2023-05-09)
26
+
27
+
28
+ ### Features
29
+
30
+ * **env:** support consuming environment variables in packages [ED-10161] ([#26](https://github.com/elementor/elementor-packages/issues/26)) ([dd811ff](https://github.com/elementor/elementor-packages/commit/dd811ff71e43bf998ee07c6e1f380a379874a7fd))
31
+
32
+
33
+
34
+
35
+
36
+ ## [0.2.1](https://github.com/elementor/elementor-packages/compare/v0.2.0...v0.2.1) (2023-05-11)
37
+
38
+
39
+ ### Bug Fixes
40
+
41
+ * **editor-app-bar:** make save options button disabled in site-settings [ED-10221] ([#30](https://github.com/elementor/elementor-packages/issues/30)) ([1991096](https://github.com/elementor/elementor-packages/commit/1991096115efeae7bc3648e4889899b85d7328d6))
package/dist/index.d.ts CHANGED
@@ -1,11 +1,6 @@
1
1
  import * as _elementor_locations from '@elementor/locations';
2
2
 
3
- declare const injectIntoTop: ({ filler, name, options }: Omit<{
4
- location: string;
5
- filler: _elementor_locations.Filler;
6
- name: string;
7
- options?: _elementor_locations.InjectionOptions | undefined;
8
- }, "location">) => void;
3
+ declare const injectIntoTop: (args: _elementor_locations.InjectArgs<_elementor_locations.EmptyObject>) => void;
9
4
 
10
5
  declare function init(domElement: Element): void;
11
6
 
package/dist/index.js CHANGED
@@ -37,19 +37,57 @@ module.exports = __toCommonJS(src_exports);
37
37
 
38
38
  // src/locations.ts
39
39
  var import_locations = require("@elementor/locations");
40
- var LOCATION_TOP = "editor/top";
41
- var injectIntoTop = (0, import_locations.createInjectorFor)(LOCATION_TOP);
40
+ var {
41
+ Slot: TopSlot,
42
+ inject: injectIntoTop
43
+ } = (0, import_locations.createLocation)();
42
44
 
43
45
  // src/init.tsx
44
46
  var React3 = __toESM(require("react"));
45
47
  var ReactDOM = __toESM(require("react-dom"));
46
- var import_i18n = require("@wordpress/i18n");
48
+ var import_i18n2 = require("@wordpress/i18n");
47
49
 
48
50
  // src/components/shell.tsx
49
51
  var React = __toESM(require("react"));
50
- var import_locations2 = require("@elementor/locations");
52
+
53
+ // src/hooks/use-sync-document-title.ts
54
+ var import_react = require("react");
55
+ var import_editor_documents = require("@elementor/editor-documents");
56
+ var import_i18n = require("@wordpress/i18n");
57
+ function useSyncDocumentTitle() {
58
+ const activeDocument = (0, import_editor_documents.useActiveDocument)();
59
+ const hostDocument = (0, import_editor_documents.useHostDocument)();
60
+ const document = activeDocument && activeDocument.type.value !== "kit" ? activeDocument : hostDocument;
61
+ (0, import_react.useEffect)(() => {
62
+ if (!document?.title) {
63
+ return;
64
+ }
65
+ const title = (0, import_i18n.__)('Edit "%s" with Elementor', "elementor").replace("%s", document.title);
66
+ window.document.title = title;
67
+ }, [document?.title]);
68
+ }
69
+
70
+ // src/hooks/use-sync-document-query-params.ts
71
+ var import_react2 = require("react");
72
+ var import_editor_documents2 = require("@elementor/editor-documents");
73
+ function useSyncDocumentQueryParams() {
74
+ const hostDocument = (0, import_editor_documents2.useHostDocument)();
75
+ (0, import_react2.useEffect)(() => {
76
+ if (!hostDocument?.id) {
77
+ return;
78
+ }
79
+ const url = new URL(window.location.href);
80
+ url.searchParams.set("post", hostDocument.id.toString());
81
+ url.searchParams.delete("active-document");
82
+ history.replaceState({}, "", url);
83
+ }, [hostDocument?.id]);
84
+ }
85
+
86
+ // src/components/shell.tsx
51
87
  function Shell() {
52
- return /* @__PURE__ */ React.createElement(import_locations2.Slot, { location: LOCATION_TOP });
88
+ useSyncDocumentTitle();
89
+ useSyncDocumentQueryParams();
90
+ return /* @__PURE__ */ React.createElement(TopSlot, null);
53
91
  }
54
92
 
55
93
  // src/init.tsx
@@ -62,17 +100,17 @@ var React2 = __toESM(require("react"));
62
100
  var import_ui = require("@elementor/ui");
63
101
 
64
102
  // src/sync/use-color-scheme.ts
65
- var import_react = require("react");
103
+ var import_react3 = require("react");
66
104
  var import_editor_v1_adapters = require("@elementor/editor-v1-adapters");
67
105
  function useColorScheme() {
68
- const [colorScheme, setColorScheme] = (0, import_react.useState)(() => getV1ColorScheme());
69
- (0, import_react.useEffect)(() => {
106
+ const [colorScheme, setColorScheme] = (0, import_react3.useState)(() => getV1ColorScheme());
107
+ (0, import_react3.useEffect)(() => {
70
108
  return (0, import_editor_v1_adapters.listenTo)(
71
109
  (0, import_editor_v1_adapters.v1ReadyEvent)(),
72
110
  () => setColorScheme(getV1ColorScheme())
73
111
  );
74
112
  }, []);
75
- (0, import_react.useEffect)(() => {
113
+ (0, import_react3.useEffect)(() => {
76
114
  return (0, import_editor_v1_adapters.listenTo)(
77
115
  (0, import_editor_v1_adapters.commandEndEvent)("document/elements/settings"),
78
116
  (e) => {
@@ -100,7 +138,7 @@ function ThemeProvider({ children }) {
100
138
  function init(domElement) {
101
139
  const store = (0, import_store.createStore)();
102
140
  (0, import_editor_v1_adapters2.dispatchReadyEvent)();
103
- ReactDOM.render(/* @__PURE__ */ React3.createElement(import_store.StoreProvider, { store }, /* @__PURE__ */ React3.createElement(import_ui2.DirectionProvider, { rtl: (0, import_i18n.isRTL)() }, /* @__PURE__ */ React3.createElement(ThemeProvider, null, /* @__PURE__ */ React3.createElement(Shell, null)))), domElement);
141
+ ReactDOM.render(/* @__PURE__ */ React3.createElement(import_store.StoreProvider, { store }, /* @__PURE__ */ React3.createElement(import_ui2.DirectionProvider, { rtl: (0, import_i18n2.isRTL)() }, /* @__PURE__ */ React3.createElement(ThemeProvider, null, /* @__PURE__ */ React3.createElement(Shell, null)))), domElement);
104
142
  }
105
143
  // Annotate the CommonJS export names for ESM import in node:
106
144
  0 && (module.exports = {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/locations.ts","../src/init.tsx","../src/components/shell.tsx","../src/components/theme-provider.tsx","../src/sync/use-color-scheme.ts"],"sourcesContent":["export { injectIntoTop } from './locations';\nexport { default as init } from './init';\n","import { createInjectorFor } from '@elementor/locations';\n\nexport const LOCATION_TOP = 'editor/top';\n\nexport const injectIntoTop = createInjectorFor( LOCATION_TOP );\n","import * as React from 'react';\nimport * as ReactDOM from 'react-dom';\nimport { isRTL } from '@wordpress/i18n';\nimport Shell from './components/shell';\nimport { DirectionProvider } from '@elementor/ui';\nimport { StoreProvider, createStore } from '@elementor/store';\nimport { dispatchReadyEvent } from '@elementor/editor-v1-adapters';\nimport ThemeProvider from './components/theme-provider';\n\nexport default function init( domElement: Element ): void {\n\tconst store = createStore();\n\n\tdispatchReadyEvent();\n\n\tReactDOM.render( (\n\t\t<StoreProvider store={ store }>\n\t\t\t<DirectionProvider rtl={ isRTL() }>\n\t\t\t\t<ThemeProvider>\n\t\t\t\t\t<Shell />\n\t\t\t\t</ThemeProvider>\n\t\t\t</DirectionProvider>\n\t\t</StoreProvider>\n\t), domElement );\n}\n","import * as React from 'react';\nimport { Slot } from '@elementor/locations';\nimport { LOCATION_TOP } from '../locations';\n\nexport default function Shell() {\n\treturn (\n\t\t<Slot location={ LOCATION_TOP } />\n\t);\n}\n","import * as React from 'react';\nimport { ThemeProvider as ThemeProviderBase } from '@elementor/ui';\nimport { useColorScheme } from '../sync/use-color-scheme';\n\nexport default function ThemeProvider( { children }: { children: React.ReactNode } ) {\n\tconst colorScheme = useColorScheme();\n\n\treturn (\n\t\t<ThemeProviderBase colorScheme={ colorScheme }>\n\t\t\t{ children }\n\t\t</ThemeProviderBase>\n\t);\n}\n","import { useEffect, useState } from 'react';\nimport { commandEndEvent, CommandEvent, listenTo, v1ReadyEvent } from '@elementor/editor-v1-adapters';\n\nexport type ColorScheme = 'auto' | 'dark' | 'light';\n\nexport type ExtendedWindow = Window & {\n\telementor: {\n\t\tgetPreferences: ( key: 'ui_theme' ) => ColorScheme,\n\t}\n}\n\nexport function useColorScheme() {\n\tconst [ colorScheme, setColorScheme ] = useState<ColorScheme>( () => getV1ColorScheme() );\n\n\tuseEffect( () => {\n\t\treturn listenTo(\n\t\t\tv1ReadyEvent(),\n\t\t\t() => setColorScheme( getV1ColorScheme() )\n\t\t);\n\t}, [] );\n\n\tuseEffect( () => {\n\t\treturn listenTo(\n\t\t\tcommandEndEvent( 'document/elements/settings' ),\n\t\t\t( e ) => {\n\t\t\t\tconst event = e as CommandEvent<{\n\t\t\t\t\tsettings: {\n\t\t\t\t\t\tui_theme?: ColorScheme,\n\t\t\t\t\t},\n\t\t\t\t}>;\n\n\t\t\t\t// The User-Preferences settings object has a key named `ui_theme` that controls the color scheme.\n\t\t\t\tconst isColorScheme = event.args?.settings && 'ui_theme' in event.args.settings;\n\n\t\t\t\tif ( isColorScheme ) {\n\t\t\t\t\tsetColorScheme( getV1ColorScheme() );\n\t\t\t\t}\n\t\t\t}\n\t\t);\n\t}, [] );\n\n\treturn colorScheme;\n}\n\nfunction getV1ColorScheme() {\n\treturn ( window as unknown as ExtendedWindow ).elementor?.getPreferences?.( 'ui_theme' ) || 'auto';\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,uBAAkC;AAE3B,IAAM,eAAe;AAErB,IAAM,oBAAgB,oCAAmB,YAAa;;;ACJ7D,IAAAA,SAAuB;AACvB,eAA0B;AAC1B,kBAAsB;;;ACFtB,YAAuB;AACvB,IAAAC,oBAAqB;AAGN,SAAR,QAAyB;AAC/B,SACC,oCAAC,0BAAK,UAAW,cAAe;AAElC;;;ADJA,IAAAC,aAAkC;AAClC,mBAA2C;AAC3C,IAAAC,6BAAmC;;;AENnC,IAAAC,SAAuB;AACvB,gBAAmD;;;ACDnD,mBAAoC;AACpC,gCAAsE;AAU/D,SAAS,iBAAiB;AAChC,QAAM,CAAE,aAAa,cAAe,QAAI,uBAAuB,MAAM,iBAAiB,CAAE;AAExF,8BAAW,MAAM;AAChB,eAAO;AAAA,UACN,wCAAa;AAAA,MACb,MAAM,eAAgB,iBAAiB,CAAE;AAAA,IAC1C;AAAA,EACD,GAAG,CAAC,CAAE;AAEN,8BAAW,MAAM;AAChB,eAAO;AAAA,UACN,2CAAiB,4BAA6B;AAAA,MAC9C,CAAE,MAAO;AACR,cAAM,QAAQ;AAOd,cAAM,gBAAgB,MAAM,MAAM,YAAY,cAAc,MAAM,KAAK;AAEvE,YAAK,eAAgB;AACpB,yBAAgB,iBAAiB,CAAE;AAAA,QACpC;AAAA,MACD;AAAA,IACD;AAAA,EACD,GAAG,CAAC,CAAE;AAEN,SAAO;AACR;AAEA,SAAS,mBAAmB;AAC3B,SAAS,OAAsC,WAAW,iBAAkB,UAAW,KAAK;AAC7F;;;AD1Ce,SAAR,cAAgC,EAAE,SAAS,GAAmC;AACpF,QAAM,cAAc,eAAe;AAEnC,SACC,qCAAC,UAAAC,eAAA,EAAkB,eAChB,QACH;AAEF;;;AFHe,SAAR,KAAuB,YAA4B;AACzD,QAAM,YAAQ,0BAAY;AAE1B,qDAAmB;AAEnB,EAAS,gBACR,qCAAC,8BAAc,SACd,qCAAC,gCAAkB,SAAM,mBAAM,KAC9B,qCAAC,qBACA,qCAAC,WAAM,CACR,CACD,CACD,GACE,UAAW;AACf;","names":["React","import_locations","import_ui","import_editor_v1_adapters","React","ThemeProviderBase"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/locations.ts","../src/init.tsx","../src/components/shell.tsx","../src/hooks/use-sync-document-title.ts","../src/hooks/use-sync-document-query-params.ts","../src/components/theme-provider.tsx","../src/sync/use-color-scheme.ts"],"sourcesContent":["export { injectIntoTop } from './locations';\nexport { default as init } from './init';\n","import { createLocation } from '@elementor/locations';\n\nexport const {\n\tSlot: TopSlot,\n\tinject: injectIntoTop,\n} = createLocation();\n","import * as React from 'react';\nimport * as ReactDOM from 'react-dom';\nimport { isRTL } from '@wordpress/i18n';\nimport Shell from './components/shell';\nimport { DirectionProvider } from '@elementor/ui';\nimport { StoreProvider, createStore } from '@elementor/store';\nimport { dispatchReadyEvent } from '@elementor/editor-v1-adapters';\nimport ThemeProvider from './components/theme-provider';\n\nexport default function init( domElement: Element ): void {\n\tconst store = createStore();\n\n\tdispatchReadyEvent();\n\n\tReactDOM.render( (\n\t\t<StoreProvider store={ store }>\n\t\t\t<DirectionProvider rtl={ isRTL() }>\n\t\t\t\t<ThemeProvider>\n\t\t\t\t\t<Shell />\n\t\t\t\t</ThemeProvider>\n\t\t\t</DirectionProvider>\n\t\t</StoreProvider>\n\t), domElement );\n}\n","import * as React from 'react';\nimport useSyncDocumentTitle from '../hooks/use-sync-document-title';\nimport useSyncDocumentQueryParams from '../hooks/use-sync-document-query-params';\nimport { TopSlot } from '../locations';\n\nexport default function Shell() {\n\tuseSyncDocumentTitle();\n\tuseSyncDocumentQueryParams();\n\n\treturn (\n\t\t<TopSlot />\n\t);\n}\n","import { useEffect } from 'react';\nimport { useActiveDocument, useHostDocument } from '@elementor/editor-documents';\nimport { __ } from '@wordpress/i18n';\n\nexport default function useSyncDocumentTitle() {\n\tconst activeDocument = useActiveDocument();\n\tconst hostDocument = useHostDocument();\n\n\tconst document = activeDocument && activeDocument.type.value !== 'kit'\n\t\t? activeDocument\n\t\t: hostDocument;\n\n\tuseEffect( () => {\n\t\tif ( ! document?.title ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// translators: %s: Document title.\n\t\tconst title = __( 'Edit \"%s\" with Elementor', 'elementor' )\n\t\t\t.replace( '%s', document.title );\n\n\t\twindow.document.title = title;\n\t}, [ document?.title ] );\n}\n","import { useEffect } from 'react';\nimport { useHostDocument } from '@elementor/editor-documents';\n\nexport default function useSyncDocumentQueryParams() {\n\tconst hostDocument = useHostDocument();\n\n\tuseEffect( () => {\n\t\tif ( ! hostDocument?.id ) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst url = new URL( window.location.href );\n\n\t\turl.searchParams.set( 'post', hostDocument.id.toString() );\n\t\turl.searchParams.delete( 'active-document' );\n\n\t\thistory.replaceState( {}, '', url );\n\t}, [ hostDocument?.id ] );\n}\n","import * as React from 'react';\nimport { ThemeProvider as ThemeProviderBase } from '@elementor/ui';\nimport { useColorScheme } from '../sync/use-color-scheme';\n\nexport default function ThemeProvider( { children }: { children: React.ReactNode } ) {\n\tconst colorScheme = useColorScheme();\n\n\treturn (\n\t\t<ThemeProviderBase colorScheme={ colorScheme }>\n\t\t\t{ children }\n\t\t</ThemeProviderBase>\n\t);\n}\n","import { useEffect, useState } from 'react';\nimport { commandEndEvent, CommandEvent, listenTo, v1ReadyEvent } from '@elementor/editor-v1-adapters';\n\nexport type ColorScheme = 'auto' | 'dark' | 'light';\n\nexport type ExtendedWindow = Window & {\n\telementor: {\n\t\tgetPreferences: ( key: 'ui_theme' ) => ColorScheme,\n\t}\n}\n\nexport function useColorScheme() {\n\tconst [ colorScheme, setColorScheme ] = useState<ColorScheme>( () => getV1ColorScheme() );\n\n\tuseEffect( () => {\n\t\treturn listenTo(\n\t\t\tv1ReadyEvent(),\n\t\t\t() => setColorScheme( getV1ColorScheme() )\n\t\t);\n\t}, [] );\n\n\tuseEffect( () => {\n\t\treturn listenTo(\n\t\t\tcommandEndEvent( 'document/elements/settings' ),\n\t\t\t( e ) => {\n\t\t\t\tconst event = e as CommandEvent<{\n\t\t\t\t\tsettings: {\n\t\t\t\t\t\tui_theme?: ColorScheme,\n\t\t\t\t\t},\n\t\t\t\t}>;\n\n\t\t\t\t// The User-Preferences settings object has a key named `ui_theme` that controls the color scheme.\n\t\t\t\tconst isColorScheme = event.args?.settings && 'ui_theme' in event.args.settings;\n\n\t\t\t\tif ( isColorScheme ) {\n\t\t\t\t\tsetColorScheme( getV1ColorScheme() );\n\t\t\t\t}\n\t\t\t}\n\t\t);\n\t}, [] );\n\n\treturn colorScheme;\n}\n\nfunction getV1ColorScheme() {\n\treturn ( window as unknown as ExtendedWindow ).elementor?.getPreferences?.( 'ui_theme' ) || 'auto';\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,uBAA+B;AAExB,IAAM;AAAA,EACZ,MAAM;AAAA,EACN,QAAQ;AACT,QAAI,iCAAe;;;ACLnB,IAAAA,SAAuB;AACvB,eAA0B;AAC1B,IAAAC,eAAsB;;;ACFtB,YAAuB;;;ACAvB,mBAA0B;AAC1B,8BAAmD;AACnD,kBAAmB;AAEJ,SAAR,uBAAwC;AAC9C,QAAM,qBAAiB,2CAAkB;AACzC,QAAM,mBAAe,yCAAgB;AAErC,QAAM,WAAW,kBAAkB,eAAe,KAAK,UAAU,QAC9D,iBACA;AAEH,8BAAW,MAAM;AAChB,QAAK,CAAE,UAAU,OAAQ;AACxB;AAAA,IACD;AAGA,UAAM,YAAQ,gBAAI,4BAA4B,WAAY,EACxD,QAAS,MAAM,SAAS,KAAM;AAEhC,WAAO,SAAS,QAAQ;AAAA,EACzB,GAAG,CAAE,UAAU,KAAM,CAAE;AACxB;;;ACvBA,IAAAC,gBAA0B;AAC1B,IAAAC,2BAAgC;AAEjB,SAAR,6BAA8C;AACpD,QAAM,mBAAe,0CAAgB;AAErC,+BAAW,MAAM;AAChB,QAAK,CAAE,cAAc,IAAK;AACzB;AAAA,IACD;AAEA,UAAM,MAAM,IAAI,IAAK,OAAO,SAAS,IAAK;AAE1C,QAAI,aAAa,IAAK,QAAQ,aAAa,GAAG,SAAS,CAAE;AACzD,QAAI,aAAa,OAAQ,iBAAkB;AAE3C,YAAQ,aAAc,CAAC,GAAG,IAAI,GAAI;AAAA,EACnC,GAAG,CAAE,cAAc,EAAG,CAAE;AACzB;;;AFbe,SAAR,QAAyB;AAC/B,uBAAqB;AACrB,6BAA2B;AAE3B,SACC,oCAAC,aAAQ;AAEX;;;ADRA,IAAAC,aAAkC;AAClC,mBAA2C;AAC3C,IAAAC,6BAAmC;;;AINnC,IAAAC,SAAuB;AACvB,gBAAmD;;;ACDnD,IAAAC,gBAAoC;AACpC,gCAAsE;AAU/D,SAAS,iBAAiB;AAChC,QAAM,CAAE,aAAa,cAAe,QAAI,wBAAuB,MAAM,iBAAiB,CAAE;AAExF,+BAAW,MAAM;AAChB,eAAO;AAAA,UACN,wCAAa;AAAA,MACb,MAAM,eAAgB,iBAAiB,CAAE;AAAA,IAC1C;AAAA,EACD,GAAG,CAAC,CAAE;AAEN,+BAAW,MAAM;AAChB,eAAO;AAAA,UACN,2CAAiB,4BAA6B;AAAA,MAC9C,CAAE,MAAO;AACR,cAAM,QAAQ;AAOd,cAAM,gBAAgB,MAAM,MAAM,YAAY,cAAc,MAAM,KAAK;AAEvE,YAAK,eAAgB;AACpB,yBAAgB,iBAAiB,CAAE;AAAA,QACpC;AAAA,MACD;AAAA,IACD;AAAA,EACD,GAAG,CAAC,CAAE;AAEN,SAAO;AACR;AAEA,SAAS,mBAAmB;AAC3B,SAAS,OAAsC,WAAW,iBAAkB,UAAW,KAAK;AAC7F;;;AD1Ce,SAAR,cAAgC,EAAE,SAAS,GAAmC;AACpF,QAAM,cAAc,eAAe;AAEnC,SACC,qCAAC,UAAAC,eAAA,EAAkB,eAChB,QACH;AAEF;;;AJHe,SAAR,KAAuB,YAA4B;AACzD,QAAM,YAAQ,0BAAY;AAE1B,qDAAmB;AAEnB,EAAS,gBACR,qCAAC,8BAAc,SACd,qCAAC,gCAAkB,SAAM,oBAAM,KAC9B,qCAAC,qBACA,qCAAC,WAAM,CACR,CACD,CACD,GACE,UAAW;AACf;","names":["React","import_i18n","import_react","import_editor_documents","import_ui","import_editor_v1_adapters","React","import_react","ThemeProviderBase"]}
package/dist/index.mjs CHANGED
@@ -1,7 +1,9 @@
1
1
  // src/locations.ts
2
- import { createInjectorFor } from "@elementor/locations";
3
- var LOCATION_TOP = "editor/top";
4
- var injectIntoTop = createInjectorFor(LOCATION_TOP);
2
+ import { createLocation } from "@elementor/locations";
3
+ var {
4
+ Slot: TopSlot,
5
+ inject: injectIntoTop
6
+ } = createLocation();
5
7
 
6
8
  // src/init.tsx
7
9
  import * as React3 from "react";
@@ -10,9 +12,45 @@ import { isRTL } from "@wordpress/i18n";
10
12
 
11
13
  // src/components/shell.tsx
12
14
  import * as React from "react";
13
- import { Slot } from "@elementor/locations";
15
+
16
+ // src/hooks/use-sync-document-title.ts
17
+ import { useEffect } from "react";
18
+ import { useActiveDocument, useHostDocument } from "@elementor/editor-documents";
19
+ import { __ } from "@wordpress/i18n";
20
+ function useSyncDocumentTitle() {
21
+ const activeDocument = useActiveDocument();
22
+ const hostDocument = useHostDocument();
23
+ const document = activeDocument && activeDocument.type.value !== "kit" ? activeDocument : hostDocument;
24
+ useEffect(() => {
25
+ if (!document?.title) {
26
+ return;
27
+ }
28
+ const title = __('Edit "%s" with Elementor', "elementor").replace("%s", document.title);
29
+ window.document.title = title;
30
+ }, [document?.title]);
31
+ }
32
+
33
+ // src/hooks/use-sync-document-query-params.ts
34
+ import { useEffect as useEffect2 } from "react";
35
+ import { useHostDocument as useHostDocument2 } from "@elementor/editor-documents";
36
+ function useSyncDocumentQueryParams() {
37
+ const hostDocument = useHostDocument2();
38
+ useEffect2(() => {
39
+ if (!hostDocument?.id) {
40
+ return;
41
+ }
42
+ const url = new URL(window.location.href);
43
+ url.searchParams.set("post", hostDocument.id.toString());
44
+ url.searchParams.delete("active-document");
45
+ history.replaceState({}, "", url);
46
+ }, [hostDocument?.id]);
47
+ }
48
+
49
+ // src/components/shell.tsx
14
50
  function Shell() {
15
- return /* @__PURE__ */ React.createElement(Slot, { location: LOCATION_TOP });
51
+ useSyncDocumentTitle();
52
+ useSyncDocumentQueryParams();
53
+ return /* @__PURE__ */ React.createElement(TopSlot, null);
16
54
  }
17
55
 
18
56
  // src/init.tsx
@@ -25,17 +63,17 @@ import * as React2 from "react";
25
63
  import { ThemeProvider as ThemeProviderBase } from "@elementor/ui";
26
64
 
27
65
  // src/sync/use-color-scheme.ts
28
- import { useEffect, useState } from "react";
66
+ import { useEffect as useEffect3, useState } from "react";
29
67
  import { commandEndEvent, listenTo, v1ReadyEvent } from "@elementor/editor-v1-adapters";
30
68
  function useColorScheme() {
31
69
  const [colorScheme, setColorScheme] = useState(() => getV1ColorScheme());
32
- useEffect(() => {
70
+ useEffect3(() => {
33
71
  return listenTo(
34
72
  v1ReadyEvent(),
35
73
  () => setColorScheme(getV1ColorScheme())
36
74
  );
37
75
  }, []);
38
- useEffect(() => {
76
+ useEffect3(() => {
39
77
  return listenTo(
40
78
  commandEndEvent("document/elements/settings"),
41
79
  (e) => {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/locations.ts","../src/init.tsx","../src/components/shell.tsx","../src/components/theme-provider.tsx","../src/sync/use-color-scheme.ts"],"sourcesContent":["import { createInjectorFor } from '@elementor/locations';\n\nexport const LOCATION_TOP = 'editor/top';\n\nexport const injectIntoTop = createInjectorFor( LOCATION_TOP );\n","import * as React from 'react';\nimport * as ReactDOM from 'react-dom';\nimport { isRTL } from '@wordpress/i18n';\nimport Shell from './components/shell';\nimport { DirectionProvider } from '@elementor/ui';\nimport { StoreProvider, createStore } from '@elementor/store';\nimport { dispatchReadyEvent } from '@elementor/editor-v1-adapters';\nimport ThemeProvider from './components/theme-provider';\n\nexport default function init( domElement: Element ): void {\n\tconst store = createStore();\n\n\tdispatchReadyEvent();\n\n\tReactDOM.render( (\n\t\t<StoreProvider store={ store }>\n\t\t\t<DirectionProvider rtl={ isRTL() }>\n\t\t\t\t<ThemeProvider>\n\t\t\t\t\t<Shell />\n\t\t\t\t</ThemeProvider>\n\t\t\t</DirectionProvider>\n\t\t</StoreProvider>\n\t), domElement );\n}\n","import * as React from 'react';\nimport { Slot } from '@elementor/locations';\nimport { LOCATION_TOP } from '../locations';\n\nexport default function Shell() {\n\treturn (\n\t\t<Slot location={ LOCATION_TOP } />\n\t);\n}\n","import * as React from 'react';\nimport { ThemeProvider as ThemeProviderBase } from '@elementor/ui';\nimport { useColorScheme } from '../sync/use-color-scheme';\n\nexport default function ThemeProvider( { children }: { children: React.ReactNode } ) {\n\tconst colorScheme = useColorScheme();\n\n\treturn (\n\t\t<ThemeProviderBase colorScheme={ colorScheme }>\n\t\t\t{ children }\n\t\t</ThemeProviderBase>\n\t);\n}\n","import { useEffect, useState } from 'react';\nimport { commandEndEvent, CommandEvent, listenTo, v1ReadyEvent } from '@elementor/editor-v1-adapters';\n\nexport type ColorScheme = 'auto' | 'dark' | 'light';\n\nexport type ExtendedWindow = Window & {\n\telementor: {\n\t\tgetPreferences: ( key: 'ui_theme' ) => ColorScheme,\n\t}\n}\n\nexport function useColorScheme() {\n\tconst [ colorScheme, setColorScheme ] = useState<ColorScheme>( () => getV1ColorScheme() );\n\n\tuseEffect( () => {\n\t\treturn listenTo(\n\t\t\tv1ReadyEvent(),\n\t\t\t() => setColorScheme( getV1ColorScheme() )\n\t\t);\n\t}, [] );\n\n\tuseEffect( () => {\n\t\treturn listenTo(\n\t\t\tcommandEndEvent( 'document/elements/settings' ),\n\t\t\t( e ) => {\n\t\t\t\tconst event = e as CommandEvent<{\n\t\t\t\t\tsettings: {\n\t\t\t\t\t\tui_theme?: ColorScheme,\n\t\t\t\t\t},\n\t\t\t\t}>;\n\n\t\t\t\t// The User-Preferences settings object has a key named `ui_theme` that controls the color scheme.\n\t\t\t\tconst isColorScheme = event.args?.settings && 'ui_theme' in event.args.settings;\n\n\t\t\t\tif ( isColorScheme ) {\n\t\t\t\t\tsetColorScheme( getV1ColorScheme() );\n\t\t\t\t}\n\t\t\t}\n\t\t);\n\t}, [] );\n\n\treturn colorScheme;\n}\n\nfunction getV1ColorScheme() {\n\treturn ( window as unknown as ExtendedWindow ).elementor?.getPreferences?.( 'ui_theme' ) || 'auto';\n}\n"],"mappings":";AAAA,SAAS,yBAAyB;AAE3B,IAAM,eAAe;AAErB,IAAM,gBAAgB,kBAAmB,YAAa;;;ACJ7D,YAAYA,YAAW;AACvB,YAAY,cAAc;AAC1B,SAAS,aAAa;;;ACFtB,YAAY,WAAW;AACvB,SAAS,YAAY;AAGN,SAAR,QAAyB;AAC/B,SACC,oCAAC,QAAK,UAAW,cAAe;AAElC;;;ADJA,SAAS,yBAAyB;AAClC,SAAS,eAAe,mBAAmB;AAC3C,SAAS,0BAA0B;;;AENnC,YAAYC,YAAW;AACvB,SAAS,iBAAiB,yBAAyB;;;ACDnD,SAAS,WAAW,gBAAgB;AACpC,SAAS,iBAA+B,UAAU,oBAAoB;AAU/D,SAAS,iBAAiB;AAChC,QAAM,CAAE,aAAa,cAAe,IAAI,SAAuB,MAAM,iBAAiB,CAAE;AAExF,YAAW,MAAM;AAChB,WAAO;AAAA,MACN,aAAa;AAAA,MACb,MAAM,eAAgB,iBAAiB,CAAE;AAAA,IAC1C;AAAA,EACD,GAAG,CAAC,CAAE;AAEN,YAAW,MAAM;AAChB,WAAO;AAAA,MACN,gBAAiB,4BAA6B;AAAA,MAC9C,CAAE,MAAO;AACR,cAAM,QAAQ;AAOd,cAAM,gBAAgB,MAAM,MAAM,YAAY,cAAc,MAAM,KAAK;AAEvE,YAAK,eAAgB;AACpB,yBAAgB,iBAAiB,CAAE;AAAA,QACpC;AAAA,MACD;AAAA,IACD;AAAA,EACD,GAAG,CAAC,CAAE;AAEN,SAAO;AACR;AAEA,SAAS,mBAAmB;AAC3B,SAAS,OAAsC,WAAW,iBAAkB,UAAW,KAAK;AAC7F;;;AD1Ce,SAAR,cAAgC,EAAE,SAAS,GAAmC;AACpF,QAAM,cAAc,eAAe;AAEnC,SACC,qCAAC,qBAAkB,eAChB,QACH;AAEF;;;AFHe,SAAR,KAAuB,YAA4B;AACzD,QAAM,QAAQ,YAAY;AAE1B,qBAAmB;AAEnB,EAAS,gBACR,qCAAC,iBAAc,SACd,qCAAC,qBAAkB,KAAM,MAAM,KAC9B,qCAAC,qBACA,qCAAC,WAAM,CACR,CACD,CACD,GACE,UAAW;AACf;","names":["React","React"]}
1
+ {"version":3,"sources":["../src/locations.ts","../src/init.tsx","../src/components/shell.tsx","../src/hooks/use-sync-document-title.ts","../src/hooks/use-sync-document-query-params.ts","../src/components/theme-provider.tsx","../src/sync/use-color-scheme.ts"],"sourcesContent":["import { createLocation } from '@elementor/locations';\n\nexport const {\n\tSlot: TopSlot,\n\tinject: injectIntoTop,\n} = createLocation();\n","import * as React from 'react';\nimport * as ReactDOM from 'react-dom';\nimport { isRTL } from '@wordpress/i18n';\nimport Shell from './components/shell';\nimport { DirectionProvider } from '@elementor/ui';\nimport { StoreProvider, createStore } from '@elementor/store';\nimport { dispatchReadyEvent } from '@elementor/editor-v1-adapters';\nimport ThemeProvider from './components/theme-provider';\n\nexport default function init( domElement: Element ): void {\n\tconst store = createStore();\n\n\tdispatchReadyEvent();\n\n\tReactDOM.render( (\n\t\t<StoreProvider store={ store }>\n\t\t\t<DirectionProvider rtl={ isRTL() }>\n\t\t\t\t<ThemeProvider>\n\t\t\t\t\t<Shell />\n\t\t\t\t</ThemeProvider>\n\t\t\t</DirectionProvider>\n\t\t</StoreProvider>\n\t), domElement );\n}\n","import * as React from 'react';\nimport useSyncDocumentTitle from '../hooks/use-sync-document-title';\nimport useSyncDocumentQueryParams from '../hooks/use-sync-document-query-params';\nimport { TopSlot } from '../locations';\n\nexport default function Shell() {\n\tuseSyncDocumentTitle();\n\tuseSyncDocumentQueryParams();\n\n\treturn (\n\t\t<TopSlot />\n\t);\n}\n","import { useEffect } from 'react';\nimport { useActiveDocument, useHostDocument } from '@elementor/editor-documents';\nimport { __ } from '@wordpress/i18n';\n\nexport default function useSyncDocumentTitle() {\n\tconst activeDocument = useActiveDocument();\n\tconst hostDocument = useHostDocument();\n\n\tconst document = activeDocument && activeDocument.type.value !== 'kit'\n\t\t? activeDocument\n\t\t: hostDocument;\n\n\tuseEffect( () => {\n\t\tif ( ! document?.title ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// translators: %s: Document title.\n\t\tconst title = __( 'Edit \"%s\" with Elementor', 'elementor' )\n\t\t\t.replace( '%s', document.title );\n\n\t\twindow.document.title = title;\n\t}, [ document?.title ] );\n}\n","import { useEffect } from 'react';\nimport { useHostDocument } from '@elementor/editor-documents';\n\nexport default function useSyncDocumentQueryParams() {\n\tconst hostDocument = useHostDocument();\n\n\tuseEffect( () => {\n\t\tif ( ! hostDocument?.id ) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst url = new URL( window.location.href );\n\n\t\turl.searchParams.set( 'post', hostDocument.id.toString() );\n\t\turl.searchParams.delete( 'active-document' );\n\n\t\thistory.replaceState( {}, '', url );\n\t}, [ hostDocument?.id ] );\n}\n","import * as React from 'react';\nimport { ThemeProvider as ThemeProviderBase } from '@elementor/ui';\nimport { useColorScheme } from '../sync/use-color-scheme';\n\nexport default function ThemeProvider( { children }: { children: React.ReactNode } ) {\n\tconst colorScheme = useColorScheme();\n\n\treturn (\n\t\t<ThemeProviderBase colorScheme={ colorScheme }>\n\t\t\t{ children }\n\t\t</ThemeProviderBase>\n\t);\n}\n","import { useEffect, useState } from 'react';\nimport { commandEndEvent, CommandEvent, listenTo, v1ReadyEvent } from '@elementor/editor-v1-adapters';\n\nexport type ColorScheme = 'auto' | 'dark' | 'light';\n\nexport type ExtendedWindow = Window & {\n\telementor: {\n\t\tgetPreferences: ( key: 'ui_theme' ) => ColorScheme,\n\t}\n}\n\nexport function useColorScheme() {\n\tconst [ colorScheme, setColorScheme ] = useState<ColorScheme>( () => getV1ColorScheme() );\n\n\tuseEffect( () => {\n\t\treturn listenTo(\n\t\t\tv1ReadyEvent(),\n\t\t\t() => setColorScheme( getV1ColorScheme() )\n\t\t);\n\t}, [] );\n\n\tuseEffect( () => {\n\t\treturn listenTo(\n\t\t\tcommandEndEvent( 'document/elements/settings' ),\n\t\t\t( e ) => {\n\t\t\t\tconst event = e as CommandEvent<{\n\t\t\t\t\tsettings: {\n\t\t\t\t\t\tui_theme?: ColorScheme,\n\t\t\t\t\t},\n\t\t\t\t}>;\n\n\t\t\t\t// The User-Preferences settings object has a key named `ui_theme` that controls the color scheme.\n\t\t\t\tconst isColorScheme = event.args?.settings && 'ui_theme' in event.args.settings;\n\n\t\t\t\tif ( isColorScheme ) {\n\t\t\t\t\tsetColorScheme( getV1ColorScheme() );\n\t\t\t\t}\n\t\t\t}\n\t\t);\n\t}, [] );\n\n\treturn colorScheme;\n}\n\nfunction getV1ColorScheme() {\n\treturn ( window as unknown as ExtendedWindow ).elementor?.getPreferences?.( 'ui_theme' ) || 'auto';\n}\n"],"mappings":";AAAA,SAAS,sBAAsB;AAExB,IAAM;AAAA,EACZ,MAAM;AAAA,EACN,QAAQ;AACT,IAAI,eAAe;;;ACLnB,YAAYA,YAAW;AACvB,YAAY,cAAc;AAC1B,SAAS,aAAa;;;ACFtB,YAAY,WAAW;;;ACAvB,SAAS,iBAAiB;AAC1B,SAAS,mBAAmB,uBAAuB;AACnD,SAAS,UAAU;AAEJ,SAAR,uBAAwC;AAC9C,QAAM,iBAAiB,kBAAkB;AACzC,QAAM,eAAe,gBAAgB;AAErC,QAAM,WAAW,kBAAkB,eAAe,KAAK,UAAU,QAC9D,iBACA;AAEH,YAAW,MAAM;AAChB,QAAK,CAAE,UAAU,OAAQ;AACxB;AAAA,IACD;AAGA,UAAM,QAAQ,GAAI,4BAA4B,WAAY,EACxD,QAAS,MAAM,SAAS,KAAM;AAEhC,WAAO,SAAS,QAAQ;AAAA,EACzB,GAAG,CAAE,UAAU,KAAM,CAAE;AACxB;;;ACvBA,SAAS,aAAAC,kBAAiB;AAC1B,SAAS,mBAAAC,wBAAuB;AAEjB,SAAR,6BAA8C;AACpD,QAAM,eAAeA,iBAAgB;AAErC,EAAAD,WAAW,MAAM;AAChB,QAAK,CAAE,cAAc,IAAK;AACzB;AAAA,IACD;AAEA,UAAM,MAAM,IAAI,IAAK,OAAO,SAAS,IAAK;AAE1C,QAAI,aAAa,IAAK,QAAQ,aAAa,GAAG,SAAS,CAAE;AACzD,QAAI,aAAa,OAAQ,iBAAkB;AAE3C,YAAQ,aAAc,CAAC,GAAG,IAAI,GAAI;AAAA,EACnC,GAAG,CAAE,cAAc,EAAG,CAAE;AACzB;;;AFbe,SAAR,QAAyB;AAC/B,uBAAqB;AACrB,6BAA2B;AAE3B,SACC,oCAAC,aAAQ;AAEX;;;ADRA,SAAS,yBAAyB;AAClC,SAAS,eAAe,mBAAmB;AAC3C,SAAS,0BAA0B;;;AINnC,YAAYE,YAAW;AACvB,SAAS,iBAAiB,yBAAyB;;;ACDnD,SAAS,aAAAC,YAAW,gBAAgB;AACpC,SAAS,iBAA+B,UAAU,oBAAoB;AAU/D,SAAS,iBAAiB;AAChC,QAAM,CAAE,aAAa,cAAe,IAAI,SAAuB,MAAM,iBAAiB,CAAE;AAExF,EAAAA,WAAW,MAAM;AAChB,WAAO;AAAA,MACN,aAAa;AAAA,MACb,MAAM,eAAgB,iBAAiB,CAAE;AAAA,IAC1C;AAAA,EACD,GAAG,CAAC,CAAE;AAEN,EAAAA,WAAW,MAAM;AAChB,WAAO;AAAA,MACN,gBAAiB,4BAA6B;AAAA,MAC9C,CAAE,MAAO;AACR,cAAM,QAAQ;AAOd,cAAM,gBAAgB,MAAM,MAAM,YAAY,cAAc,MAAM,KAAK;AAEvE,YAAK,eAAgB;AACpB,yBAAgB,iBAAiB,CAAE;AAAA,QACpC;AAAA,MACD;AAAA,IACD;AAAA,EACD,GAAG,CAAC,CAAE;AAEN,SAAO;AACR;AAEA,SAAS,mBAAmB;AAC3B,SAAS,OAAsC,WAAW,iBAAkB,UAAW,KAAK;AAC7F;;;AD1Ce,SAAR,cAAgC,EAAE,SAAS,GAAmC;AACpF,QAAM,cAAc,eAAe;AAEnC,SACC,qCAAC,qBAAkB,eAChB,QACH;AAEF;;;AJHe,SAAR,KAAuB,YAA4B;AACzD,QAAM,QAAQ,YAAY;AAE1B,qBAAmB;AAEnB,EAAS,gBACR,qCAAC,iBAAc,SACd,qCAAC,qBAAkB,KAAM,MAAM,KAC9B,qCAAC,qBACA,qCAAC,WAAM,CACR,CACD,CACD,GACE,UAAW;AACf;","names":["React","useEffect","useHostDocument","React","useEffect"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elementor/editor",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "private": false,
5
5
  "author": "Elementor Team",
6
6
  "homepage": "https://elementor.com/",
@@ -32,10 +32,11 @@
32
32
  "dev": "tsup --config=../../tsup.dev.ts"
33
33
  },
34
34
  "dependencies": {
35
+ "@elementor/editor-documents": "^0.4.0",
35
36
  "@elementor/editor-v1-adapters": "^0.2.0",
36
- "@elementor/locations": "^0.2.0",
37
+ "@elementor/locations": "^0.3.0",
37
38
  "@elementor/store": "^0.2.0",
38
- "@elementor/ui": "^1.4.47",
39
+ "@elementor/ui": "^1.4.50",
39
40
  "@wordpress/i18n": "^4.31.0"
40
41
  },
41
42
  "peerDependencies": {
@@ -45,5 +46,5 @@
45
46
  "elementor": {
46
47
  "type": "app"
47
48
  },
48
- "gitHead": "f93a1ed5350b151f17d65b21b0b43398774dccc5"
49
+ "gitHead": "45afd449fda346eea0f8a431ebca7d6c1d518dd1"
49
50
  }
@@ -2,12 +2,45 @@ import * as React from 'react';
2
2
  import { render } from '@testing-library/react';
3
3
  import Shell from '../shell';
4
4
  import { injectIntoTop } from '../../locations';
5
+ import { useActiveDocument, useHostDocument } from '@elementor/editor-documents';
6
+ import { createMockDocument } from 'test-utils';
7
+
8
+ jest.mock( '@elementor/editor-documents', () => ( {
9
+ useActiveDocument: jest.fn(),
10
+ useHostDocument: jest.fn(),
11
+ } ) );
5
12
 
6
13
  describe( '@elementor/editor Shell component', () => {
14
+ const originalReplaceState = history.replaceState;
15
+ const originalLocation = window.location;
16
+
17
+ beforeEach( () => {
18
+ jest.resetAllMocks();
19
+
20
+ history.replaceState = jest.fn();
21
+
22
+ /**
23
+ * @see https://gist.github.com/the0neWhoKnocks/bdac1d09b93b8418d948558f7ab233d7#setting-props-on-windowlocation
24
+ */
25
+ Object.defineProperty( window, 'location', {
26
+ writable: true,
27
+ value: new URL( 'https://localhost/' ),
28
+ } );
29
+ } );
30
+
31
+ afterAll( () => {
32
+ history.replaceState = originalReplaceState;
33
+
34
+ Object.defineProperty( window, 'location', {
35
+ writable: false,
36
+ value: originalLocation,
37
+ } );
38
+ } );
39
+
7
40
  it( 'should render', () => {
8
41
  // Arrange.
9
42
  injectIntoTop( {
10
- name: 'test',
43
+ id: 'test',
11
44
  filler: () => <div>test</div>,
12
45
  } );
13
46
 
@@ -17,4 +50,123 @@ describe( '@elementor/editor Shell component', () => {
17
50
  // Assert.
18
51
  expect( queryByText( 'test' ) ).toBeTruthy();
19
52
  } );
53
+
54
+ it( 'should not modify the query params when there is no host document', () => {
55
+ // Arrange.
56
+ jest.mocked( useHostDocument ).mockReturnValue( null );
57
+
58
+ // Act.
59
+ render( <Shell /> );
60
+
61
+ // Assert.
62
+ expect( history.replaceState ).not.toHaveBeenCalled();
63
+ } );
64
+
65
+ it( 'should modify the query params when there is host document', () => {
66
+ // Arrange.
67
+ jest.mocked( useHostDocument ).mockReturnValue( createMockDocument( {
68
+ id: 1,
69
+ } ) );
70
+
71
+ // TS doesn't allow modifying the location object.
72
+ ( window as unknown as { location: URL } ).location = new URL( 'https://localhost/?post=2&active-document=3' );
73
+
74
+ // Act.
75
+ const { rerender } = render( <Shell /> );
76
+
77
+ // Assert.
78
+ expect( history.replaceState ).toHaveBeenCalledTimes( 1 );
79
+ expect( history.replaceState ).toHaveBeenCalledWith(
80
+ expect.anything(),
81
+ expect.anything(),
82
+ expect.objectContaining( { href: 'https://localhost/?post=1' } )
83
+ );
84
+
85
+ // Act - rerender with another host document.
86
+ jest.mocked( useHostDocument ).mockReturnValue( createMockDocument( {
87
+ id: 2,
88
+ } ) );
89
+
90
+ rerender( <Shell /> );
91
+
92
+ // Assert.
93
+ expect( history.replaceState ).toHaveBeenCalledTimes( 2 );
94
+ expect( history.replaceState ).toHaveBeenCalledWith(
95
+ expect.anything(),
96
+ expect.anything(),
97
+ expect.objectContaining( { href: 'https://localhost/?post=2' } )
98
+ );
99
+ } );
100
+
101
+ it( 'should set the document title to be the active document', () => {
102
+ // Arrange - set the initial document.
103
+ jest.mocked( useActiveDocument ).mockReturnValue( createMockDocument( {
104
+ type: {
105
+ value: 'page',
106
+ label: 'Page',
107
+ },
108
+ title: 'Initial Document',
109
+ } ) );
110
+
111
+ // Act.
112
+ const { rerender } = render( <Shell /> );
113
+
114
+ // Assert.
115
+ expect( window.document.title ).toBe( 'Edit "Initial Document" with Elementor' );
116
+
117
+ // Arrange - set the new document.
118
+ jest.mocked( useActiveDocument ).mockReturnValue( createMockDocument( {
119
+ type: {
120
+ value: 'page',
121
+ label: 'Page',
122
+ },
123
+ title: 'New Document',
124
+ } ) );
125
+
126
+ // Act.
127
+ rerender( <Shell /> );
128
+
129
+ // Assert.
130
+ expect( window.document.title ).toBe( 'Edit "New Document" with Elementor' );
131
+ } );
132
+
133
+ it( 'should use the host document title when the active document is a kit', () => {
134
+ // Arrange.
135
+ jest.mocked( useActiveDocument ).mockReturnValue( createMockDocument( {
136
+ type: {
137
+ value: 'kit',
138
+ label: 'Kit',
139
+ },
140
+ title: 'My Kit',
141
+ } ) );
142
+
143
+ jest.mocked( useHostDocument ).mockReturnValue( createMockDocument( {
144
+ type: {
145
+ value: 'page',
146
+ label: 'Page',
147
+ },
148
+ title: 'My Page',
149
+ } ) );
150
+
151
+ window.document.title = 'Old title';
152
+
153
+ // Act.
154
+ render( <Shell /> );
155
+
156
+ // Assert.
157
+ expect( window.document.title ).toBe( 'Edit "My Page" with Elementor' );
158
+ } );
159
+
160
+ it( 'should not modify the title when there is no active document', () => {
161
+ // Arrange.
162
+ jest.mocked( useActiveDocument ).mockReturnValue( null );
163
+
164
+ window.document.title = 'Old title';
165
+
166
+ // Act.
167
+ render( <Shell /> );
168
+
169
+ // Assert.
170
+ expect( window.document.title ).toBe( 'Old title' );
171
+ } );
20
172
  } );
@@ -1,9 +1,13 @@
1
1
  import * as React from 'react';
2
- import { Slot } from '@elementor/locations';
3
- import { LOCATION_TOP } from '../locations';
2
+ import useSyncDocumentTitle from '../hooks/use-sync-document-title';
3
+ import useSyncDocumentQueryParams from '../hooks/use-sync-document-query-params';
4
+ import { TopSlot } from '../locations';
4
5
 
5
6
  export default function Shell() {
7
+ useSyncDocumentTitle();
8
+ useSyncDocumentQueryParams();
9
+
6
10
  return (
7
- <Slot location={ LOCATION_TOP } />
11
+ <TopSlot />
8
12
  );
9
13
  }
@@ -0,0 +1,19 @@
1
+ import { useEffect } from 'react';
2
+ import { useHostDocument } from '@elementor/editor-documents';
3
+
4
+ export default function useSyncDocumentQueryParams() {
5
+ const hostDocument = useHostDocument();
6
+
7
+ useEffect( () => {
8
+ if ( ! hostDocument?.id ) {
9
+ return;
10
+ }
11
+
12
+ const url = new URL( window.location.href );
13
+
14
+ url.searchParams.set( 'post', hostDocument.id.toString() );
15
+ url.searchParams.delete( 'active-document' );
16
+
17
+ history.replaceState( {}, '', url );
18
+ }, [ hostDocument?.id ] );
19
+ }
@@ -0,0 +1,24 @@
1
+ import { useEffect } from 'react';
2
+ import { useActiveDocument, useHostDocument } from '@elementor/editor-documents';
3
+ import { __ } from '@wordpress/i18n';
4
+
5
+ export default function useSyncDocumentTitle() {
6
+ const activeDocument = useActiveDocument();
7
+ const hostDocument = useHostDocument();
8
+
9
+ const document = activeDocument && activeDocument.type.value !== 'kit'
10
+ ? activeDocument
11
+ : hostDocument;
12
+
13
+ useEffect( () => {
14
+ if ( ! document?.title ) {
15
+ return;
16
+ }
17
+
18
+ // translators: %s: Document title.
19
+ const title = __( 'Edit "%s" with Elementor', 'elementor' )
20
+ .replace( '%s', document.title );
21
+
22
+ window.document.title = title;
23
+ }, [ document?.title ] );
24
+ }
package/src/locations.ts CHANGED
@@ -1,5 +1,6 @@
1
- import { createInjectorFor } from '@elementor/locations';
1
+ import { createLocation } from '@elementor/locations';
2
2
 
3
- export const LOCATION_TOP = 'editor/top';
4
-
5
- export const injectIntoTop = createInjectorFor( LOCATION_TOP );
3
+ export const {
4
+ Slot: TopSlot,
5
+ inject: injectIntoTop,
6
+ } = createLocation();