angular-intlayer 6.1.6 → 7.0.0-canary.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.
Files changed (237) hide show
  1. package/dist/cjs/UI/ContentSelector.component.cjs +94 -121
  2. package/dist/cjs/UI/ContentSelector.component.cjs.map +1 -1
  3. package/dist/cjs/_virtual/rolldown_runtime.cjs +25 -0
  4. package/dist/cjs/client/index.cjs +19 -35
  5. package/dist/cjs/client/installIntlayer.cjs +39 -68
  6. package/dist/cjs/client/installIntlayer.cjs.map +1 -1
  7. package/dist/cjs/client/useDictionary.cjs +13 -36
  8. package/dist/cjs/client/useDictionary.cjs.map +1 -1
  9. package/dist/cjs/client/useDictionaryAsync.cjs +23 -48
  10. package/dist/cjs/client/useDictionaryAsync.cjs.map +1 -1
  11. package/dist/cjs/client/useDictionaryDynamic.cjs +24 -52
  12. package/dist/cjs/client/useDictionaryDynamic.cjs.map +1 -1
  13. package/dist/cjs/client/useIntlayer.cjs +16 -36
  14. package/dist/cjs/client/useIntlayer.cjs.map +1 -1
  15. package/dist/cjs/client/useLoadDynamic.cjs +26 -42
  16. package/dist/cjs/client/useLoadDynamic.cjs.map +1 -1
  17. package/dist/cjs/client/useLocale.cjs +35 -67
  18. package/dist/cjs/client/useLocale.cjs.map +1 -1
  19. package/dist/cjs/client/useLocaleStorage.cjs +61 -0
  20. package/dist/cjs/client/useLocaleStorage.cjs.map +1 -0
  21. package/dist/cjs/editor/ContentSelectorWrapper.component.cjs +41 -63
  22. package/dist/cjs/editor/ContentSelectorWrapper.component.cjs.map +1 -1
  23. package/dist/cjs/editor/EditedContentRenderer.component.cjs +44 -75
  24. package/dist/cjs/editor/EditedContentRenderer.component.cjs.map +1 -1
  25. package/dist/cjs/editor/EditorSelectorRenderer.component.cjs +25 -45
  26. package/dist/cjs/editor/EditorSelectorRenderer.component.cjs.map +1 -1
  27. package/dist/cjs/editor/communicator.cjs +55 -73
  28. package/dist/cjs/editor/communicator.cjs.map +1 -1
  29. package/dist/cjs/editor/configuration.cjs +21 -54
  30. package/dist/cjs/editor/configuration.cjs.map +1 -1
  31. package/dist/cjs/editor/createSharedComposable.cjs +46 -63
  32. package/dist/cjs/editor/createSharedComposable.cjs.map +1 -1
  33. package/dist/cjs/editor/dictionariesRecord.cjs +47 -77
  34. package/dist/cjs/editor/dictionariesRecord.cjs.map +1 -1
  35. package/dist/cjs/editor/editedContent.cjs +58 -71
  36. package/dist/cjs/editor/editedContent.cjs.map +1 -1
  37. package/dist/cjs/editor/editorEnabled.cjs +45 -70
  38. package/dist/cjs/editor/editorEnabled.cjs.map +1 -1
  39. package/dist/cjs/editor/editorLocale.cjs +21 -45
  40. package/dist/cjs/editor/editorLocale.cjs.map +1 -1
  41. package/dist/cjs/editor/focusDictionary.cjs +58 -74
  42. package/dist/cjs/editor/focusDictionary.cjs.map +1 -1
  43. package/dist/cjs/editor/index.cjs +9 -38
  44. package/dist/cjs/editor/installIntlayerEditor.cjs +46 -64
  45. package/dist/cjs/editor/installIntlayerEditor.cjs.map +1 -1
  46. package/dist/cjs/editor/useCrossFrameMessageListener.cjs +63 -72
  47. package/dist/cjs/editor/useCrossFrameMessageListener.cjs.map +1 -1
  48. package/dist/cjs/editor/useCrossFrameState.cjs +108 -88
  49. package/dist/cjs/editor/useCrossFrameState.cjs.map +1 -1
  50. package/dist/cjs/editor/useCrossURLPathState.cjs +64 -69
  51. package/dist/cjs/editor/useCrossURLPathState.cjs.map +1 -1
  52. package/dist/cjs/editor/useEditedContentRenderer.cjs +25 -54
  53. package/dist/cjs/editor/useEditedContentRenderer.cjs.map +1 -1
  54. package/dist/cjs/editor/useEditor.cjs +22 -44
  55. package/dist/cjs/editor/useEditor.cjs.map +1 -1
  56. package/dist/cjs/editor/useIframeClickInterceptor.cjs +38 -59
  57. package/dist/cjs/editor/useIframeClickInterceptor.cjs.map +1 -1
  58. package/dist/cjs/getDictionary.cjs +14 -39
  59. package/dist/cjs/getDictionary.cjs.map +1 -1
  60. package/dist/cjs/getIntlayer.cjs +14 -35
  61. package/dist/cjs/getIntlayer.cjs.map +1 -1
  62. package/dist/cjs/index.cjs +32 -31
  63. package/dist/cjs/markdown/index.cjs +6 -23
  64. package/dist/cjs/markdown/installIntlayerMarkdown.cjs +33 -60
  65. package/dist/cjs/markdown/installIntlayerMarkdown.cjs.map +1 -1
  66. package/dist/cjs/plugins.cjs +77 -108
  67. package/dist/cjs/plugins.cjs.map +1 -1
  68. package/dist/cjs/renderIntlayerNode.cjs +16 -56
  69. package/dist/cjs/renderIntlayerNode.cjs.map +1 -1
  70. package/dist/cjs/webpack/index.cjs +3 -23
  71. package/dist/cjs/webpack/mergeConfig.cjs +33 -68
  72. package/dist/cjs/webpack/mergeConfig.cjs.map +1 -1
  73. package/dist/esm/UI/ContentSelector.component.mjs +89 -104
  74. package/dist/esm/UI/ContentSelector.component.mjs.map +1 -1
  75. package/dist/esm/client/index.mjs +9 -8
  76. package/dist/esm/client/installIntlayer.mjs +29 -27
  77. package/dist/esm/client/installIntlayer.mjs.map +1 -1
  78. package/dist/esm/client/useDictionary.mjs +10 -11
  79. package/dist/esm/client/useDictionary.mjs.map +1 -1
  80. package/dist/esm/client/useDictionaryAsync.mjs +18 -12
  81. package/dist/esm/client/useDictionaryAsync.mjs.map +1 -1
  82. package/dist/esm/client/useDictionaryDynamic.mjs +18 -15
  83. package/dist/esm/client/useDictionaryDynamic.mjs.map +1 -1
  84. package/dist/esm/client/useIntlayer.mjs +12 -10
  85. package/dist/esm/client/useIntlayer.mjs.map +1 -1
  86. package/dist/esm/client/useLoadDynamic.mjs +23 -17
  87. package/dist/esm/client/useLoadDynamic.mjs.map +1 -1
  88. package/dist/esm/client/useLocale.mjs +30 -31
  89. package/dist/esm/client/useLocale.mjs.map +1 -1
  90. package/dist/esm/client/useLocaleStorage.mjs +54 -0
  91. package/dist/esm/client/useLocaleStorage.mjs.map +1 -0
  92. package/dist/esm/editor/ContentSelectorWrapper.component.mjs +32 -34
  93. package/dist/esm/editor/ContentSelectorWrapper.component.mjs.map +1 -1
  94. package/dist/esm/editor/EditedContentRenderer.component.mjs +38 -49
  95. package/dist/esm/editor/EditedContentRenderer.component.mjs.map +1 -1
  96. package/dist/esm/editor/EditorSelectorRenderer.component.mjs +19 -18
  97. package/dist/esm/editor/EditorSelectorRenderer.component.mjs.map +1 -1
  98. package/dist/esm/editor/communicator.mjs +48 -35
  99. package/dist/esm/editor/communicator.mjs.map +1 -1
  100. package/dist/esm/editor/configuration.mjs +15 -18
  101. package/dist/esm/editor/configuration.mjs.map +1 -1
  102. package/dist/esm/editor/createSharedComposable.mjs +42 -37
  103. package/dist/esm/editor/createSharedComposable.mjs.map +1 -1
  104. package/dist/esm/editor/dictionariesRecord.mjs +38 -38
  105. package/dist/esm/editor/dictionariesRecord.mjs.map +1 -1
  106. package/dist/esm/editor/editedContent.mjs +49 -42
  107. package/dist/esm/editor/editedContent.mjs.map +1 -1
  108. package/dist/esm/editor/editorEnabled.mjs +37 -41
  109. package/dist/esm/editor/editorEnabled.mjs.map +1 -1
  110. package/dist/esm/editor/editorLocale.mjs +15 -18
  111. package/dist/esm/editor/editorLocale.mjs.map +1 -1
  112. package/dist/esm/editor/focusDictionary.mjs +50 -45
  113. package/dist/esm/editor/focusDictionary.mjs.map +1 -1
  114. package/dist/esm/editor/index.mjs +3 -7
  115. package/dist/esm/editor/installIntlayerEditor.mjs +40 -26
  116. package/dist/esm/editor/installIntlayerEditor.mjs.map +1 -1
  117. package/dist/esm/editor/useCrossFrameMessageListener.mjs +58 -46
  118. package/dist/esm/editor/useCrossFrameMessageListener.mjs.map +1 -1
  119. package/dist/esm/editor/useCrossFrameState.mjs +104 -62
  120. package/dist/esm/editor/useCrossFrameState.mjs.map +1 -1
  121. package/dist/esm/editor/useCrossURLPathState.mjs +57 -41
  122. package/dist/esm/editor/useCrossURLPathState.mjs.map +1 -1
  123. package/dist/esm/editor/useEditedContentRenderer.mjs +21 -33
  124. package/dist/esm/editor/useEditedContentRenderer.mjs.map +1 -1
  125. package/dist/esm/editor/useEditor.mjs +16 -14
  126. package/dist/esm/editor/useEditor.mjs.map +1 -1
  127. package/dist/esm/editor/useIframeClickInterceptor.mjs +30 -30
  128. package/dist/esm/editor/useIframeClickInterceptor.mjs.map +1 -1
  129. package/dist/esm/getDictionary.mjs +12 -20
  130. package/dist/esm/getDictionary.mjs.map +1 -1
  131. package/dist/esm/getIntlayer.mjs +12 -16
  132. package/dist/esm/getIntlayer.mjs.map +1 -1
  133. package/dist/esm/index.mjs +13 -6
  134. package/dist/esm/markdown/index.mjs +3 -2
  135. package/dist/esm/markdown/installIntlayerMarkdown.mjs +27 -32
  136. package/dist/esm/markdown/installIntlayerMarkdown.mjs.map +1 -1
  137. package/dist/esm/plugins.mjs +70 -83
  138. package/dist/esm/plugins.mjs.map +1 -1
  139. package/dist/esm/renderIntlayerNode.mjs +15 -32
  140. package/dist/esm/renderIntlayerNode.mjs.map +1 -1
  141. package/dist/esm/webpack/index.mjs +3 -2
  142. package/dist/esm/webpack/mergeConfig.mjs +25 -31
  143. package/dist/esm/webpack/mergeConfig.mjs.map +1 -1
  144. package/dist/types/UI/ContentSelector.component.d.ts +32 -30
  145. package/dist/types/UI/ContentSelector.component.d.ts.map +1 -1
  146. package/dist/types/client/index.d.ts +8 -8
  147. package/dist/types/client/installIntlayer.d.ts +15 -9
  148. package/dist/types/client/installIntlayer.d.ts.map +1 -1
  149. package/dist/types/client/useDictionary.d.ts +7 -4
  150. package/dist/types/client/useDictionary.d.ts.map +1 -1
  151. package/dist/types/client/useDictionaryAsync.d.ts +7 -3
  152. package/dist/types/client/useDictionaryAsync.d.ts.map +1 -1
  153. package/dist/types/client/useDictionaryDynamic.d.ts +9 -3
  154. package/dist/types/client/useDictionaryDynamic.d.ts.map +1 -1
  155. package/dist/types/client/useIntlayer.d.ts +9 -7
  156. package/dist/types/client/useIntlayer.d.ts.map +1 -1
  157. package/dist/types/client/useLoadDynamic.d.ts +4 -1
  158. package/dist/types/client/useLoadDynamic.d.ts.map +1 -1
  159. package/dist/types/client/useLocale.d.ts +17 -8
  160. package/dist/types/client/useLocale.d.ts.map +1 -1
  161. package/dist/types/client/useLocaleStorage.d.ts +49 -0
  162. package/dist/types/client/useLocaleStorage.d.ts.map +1 -0
  163. package/dist/types/editor/ContentSelectorWrapper.component.d.ts +18 -12
  164. package/dist/types/editor/ContentSelectorWrapper.component.d.ts.map +1 -1
  165. package/dist/types/editor/EditedContentRenderer.component.d.ts +20 -16
  166. package/dist/types/editor/EditedContentRenderer.component.d.ts.map +1 -1
  167. package/dist/types/editor/EditorSelectorRenderer.component.d.ts +11 -6
  168. package/dist/types/editor/EditorSelectorRenderer.component.d.ts.map +1 -1
  169. package/dist/types/editor/communicator.d.ts +14 -9
  170. package/dist/types/editor/communicator.d.ts.map +1 -1
  171. package/dist/types/editor/configuration.d.ts +4 -1
  172. package/dist/types/editor/configuration.d.ts.map +1 -1
  173. package/dist/types/editor/createSharedComposable.d.ts +6 -4
  174. package/dist/types/editor/createSharedComposable.d.ts.map +1 -1
  175. package/dist/types/editor/dictionariesRecord.d.ts +13 -10
  176. package/dist/types/editor/dictionariesRecord.d.ts.map +1 -1
  177. package/dist/types/editor/editedContent.d.ts +13 -10
  178. package/dist/types/editor/editedContent.d.ts.map +1 -1
  179. package/dist/types/editor/editorEnabled.d.ts +12 -8
  180. package/dist/types/editor/editorEnabled.d.ts.map +1 -1
  181. package/dist/types/editor/editorLocale.d.ts +6 -2
  182. package/dist/types/editor/editorLocale.d.ts.map +1 -1
  183. package/dist/types/editor/focusDictionary.d.ts +16 -13
  184. package/dist/types/editor/focusDictionary.d.ts.map +1 -1
  185. package/dist/types/editor/index.d.ts +5 -5
  186. package/dist/types/editor/installIntlayerEditor.d.ts +7 -2
  187. package/dist/types/editor/installIntlayerEditor.d.ts.map +1 -1
  188. package/dist/types/editor/useCrossFrameMessageListener.d.ts +7 -2
  189. package/dist/types/editor/useCrossFrameMessageListener.d.ts.map +1 -1
  190. package/dist/types/editor/useCrossFrameState.d.ts +12 -8
  191. package/dist/types/editor/useCrossFrameState.d.ts.map +1 -1
  192. package/dist/types/editor/useCrossURLPathState.d.ts +9 -3
  193. package/dist/types/editor/useCrossURLPathState.d.ts.map +1 -1
  194. package/dist/types/editor/useEditedContentRenderer.d.ts +15 -11
  195. package/dist/types/editor/useEditedContentRenderer.d.ts.map +1 -1
  196. package/dist/types/editor/useEditor.d.ts +4 -1
  197. package/dist/types/editor/useEditor.d.ts.map +1 -1
  198. package/dist/types/editor/useIframeClickInterceptor.d.ts +5 -2
  199. package/dist/types/editor/useIframeClickInterceptor.d.ts.map +1 -1
  200. package/dist/types/getDictionary.d.ts +8 -4
  201. package/dist/types/getDictionary.d.ts.map +1 -1
  202. package/dist/types/getIntlayer.d.ts +8 -4
  203. package/dist/types/getIntlayer.d.ts.map +1 -1
  204. package/dist/types/index.d.ts +16 -8
  205. package/dist/types/index.d.ts.map +1 -1
  206. package/dist/types/markdown/index.d.ts +2 -2
  207. package/dist/types/markdown/installIntlayerMarkdown.d.ts +15 -12
  208. package/dist/types/markdown/installIntlayerMarkdown.d.ts.map +1 -1
  209. package/dist/types/plugins.d.ts +29 -23
  210. package/dist/types/plugins.d.ts.map +1 -1
  211. package/dist/types/renderIntlayerNode.d.ts +16 -10
  212. package/dist/types/renderIntlayerNode.d.ts.map +1 -1
  213. package/dist/types/webpack/index.d.ts +2 -2
  214. package/dist/types/webpack/mergeConfig.d.ts +5 -2
  215. package/dist/types/webpack/mergeConfig.d.ts.map +1 -1
  216. package/package.json +43 -49
  217. package/LICENSE +0 -202
  218. package/dist/cjs/client/index.cjs.map +0 -1
  219. package/dist/cjs/client/useLocaleCookie.cjs +0 -60
  220. package/dist/cjs/client/useLocaleCookie.cjs.map +0 -1
  221. package/dist/cjs/editor/index.cjs.map +0 -1
  222. package/dist/cjs/index.cjs.map +0 -1
  223. package/dist/cjs/markdown/index.cjs.map +0 -1
  224. package/dist/cjs/webpack/index.cjs.map +0 -1
  225. package/dist/esm/client/index.mjs.map +0 -1
  226. package/dist/esm/client/useLocaleCookie.mjs +0 -24
  227. package/dist/esm/client/useLocaleCookie.mjs.map +0 -1
  228. package/dist/esm/editor/index.mjs.map +0 -1
  229. package/dist/esm/index.mjs.map +0 -1
  230. package/dist/esm/markdown/index.mjs.map +0 -1
  231. package/dist/esm/webpack/index.mjs.map +0 -1
  232. package/dist/types/client/index.d.ts.map +0 -1
  233. package/dist/types/client/useLocaleCookie.d.ts +0 -17
  234. package/dist/types/client/useLocaleCookie.d.ts.map +0 -1
  235. package/dist/types/editor/index.d.ts.map +0 -1
  236. package/dist/types/markdown/index.d.ts.map +0 -1
  237. package/dist/types/webpack/index.d.ts.map +0 -1
@@ -1,10 +1,6 @@
1
1
  import { ContentSelectorWrapperComponent } from "./ContentSelectorWrapper.component.mjs";
2
2
  import { EditedContentRendererComponent } from "./EditedContentRenderer.component.mjs";
3
3
  import { EditorSelectorRendererComponent } from "./EditorSelectorRenderer.component.mjs";
4
- export * from "./installIntlayerEditor.mjs";
5
- export {
6
- ContentSelectorWrapperComponent,
7
- EditedContentRendererComponent,
8
- EditorSelectorRendererComponent
9
- };
10
- //# sourceMappingURL=index.mjs.map
4
+ import { installIntlayerEditor } from "./installIntlayerEditor.mjs";
5
+
6
+ export { ContentSelectorWrapperComponent, EditedContentRendererComponent, EditorSelectorRendererComponent, installIntlayerEditor };
@@ -1,36 +1,50 @@
1
- import configuration from "@intlayer/config/built";
2
1
  import { installCommunicator } from "./communicator.mjs";
3
- import { installDictionariesRecord } from "./dictionariesRecord.mjs";
4
- import { installEditedContent } from "./editedContent.mjs";
5
2
  import { installEditorEnabled } from "./editorEnabled.mjs";
6
3
  import { installFocusDictionary } from "./focusDictionary.mjs";
4
+ import { installDictionariesRecord } from "./dictionariesRecord.mjs";
5
+ import { installEditedContent } from "./editedContent.mjs";
6
+ import configuration from "@intlayer/config/built";
7
+
8
+ //#region src/editor/installIntlayerEditor.ts
7
9
  const { editor } = configuration;
8
10
  const { applicationURL, editorURL, cmsURL } = editor ?? {};
9
11
  const postMessage = (data) => {
10
- if (typeof window === "undefined") return;
11
- const isInIframe = window.self !== window.top;
12
- if (!isInIframe) return;
13
- if (editor.applicationURL.length > 0) {
14
- window.postMessage(data, editor.applicationURL);
15
- }
16
- if (editor.editorURL.length > 0) {
17
- window.parent.postMessage(data, editor.editorURL);
18
- }
19
- if (editor.cmsURL.length > 0) {
20
- window.parent.postMessage(data, editor.cmsURL);
21
- }
12
+ if (typeof window === "undefined") return;
13
+ if (!(window.self !== window.top)) return;
14
+ if (editor.applicationURL.length > 0) window.postMessage(data, editor.applicationURL);
15
+ if (editor.editorURL.length > 0) window.parent.postMessage(data, editor.editorURL);
16
+ if (editor.cmsURL.length > 0) window.parent.postMessage(data, editor.cmsURL);
22
17
  };
18
+ /**
19
+ * Angular-side replacement for the former <IntlayerEditorProvider> React component.
20
+ *
21
+ * Call **once** in your Angular application's bootstrap function or main module:
22
+ *
23
+ * ```typescript
24
+ * import { bootstrapApplication } from '@angular/platform-browser';
25
+ * import { AppComponent } from './app/app.component';
26
+ * import { installIntlayerEditor } from 'angular-intlayer';
27
+ *
28
+ * bootstrapApplication(AppComponent).then(appRef => {
29
+ * installIntlayerEditor(appRef.injector);
30
+ * });
31
+ * ```
32
+ */
23
33
  const installIntlayerEditor = (injector) => {
24
- installCommunicator(injector, {
25
- postMessage,
26
- allowedOrigins: [applicationURL, editorURL, cmsURL]
27
- });
28
- installEditorEnabled(injector);
29
- installDictionariesRecord(injector);
30
- installEditedContent(injector);
31
- installFocusDictionary(injector);
32
- };
33
- export {
34
- installIntlayerEditor
34
+ installCommunicator(injector, {
35
+ postMessage,
36
+ allowedOrigins: [
37
+ applicationURL,
38
+ editorURL,
39
+ cmsURL
40
+ ]
41
+ });
42
+ installEditorEnabled(injector);
43
+ installDictionariesRecord(injector);
44
+ installEditedContent(injector);
45
+ installFocusDictionary(injector);
35
46
  };
47
+
48
+ //#endregion
49
+ export { installIntlayerEditor };
36
50
  //# sourceMappingURL=installIntlayerEditor.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/editor/installIntlayerEditor.ts"],"sourcesContent":["import { Injector } from '@angular/core';\nimport configuration from '@intlayer/config/built';\nimport { installCommunicator } from './communicator';\nimport { installDictionariesRecord } from './dictionariesRecord';\nimport { installEditedContent } from './editedContent';\nimport { installEditorEnabled } from './editorEnabled';\nimport { installFocusDictionary } from './focusDictionary';\n\nconst { editor } = configuration;\nconst { applicationURL, editorURL, cmsURL } = editor ?? {};\n\nconst postMessage = (data: any) => {\n if (typeof window === 'undefined') return;\n\n const isInIframe = window.self !== window.top;\n\n if (!isInIframe) return;\n\n if (editor.applicationURL.length > 0) {\n window.postMessage(data, editor.applicationURL);\n }\n\n if (editor.editorURL.length > 0) {\n window.parent.postMessage(data, editor.editorURL);\n }\n\n if (editor.cmsURL.length > 0) {\n window.parent.postMessage(data, editor.cmsURL);\n }\n};\n\n/**\n * Angular-side replacement for the former <IntlayerEditorProvider> React component.\n *\n * Call **once** in your Angular application's bootstrap function or main module:\n *\n * ```typescript\n * import { bootstrapApplication } from '@angular/platform-browser';\n * import { AppComponent } from './app/app.component';\n * import { installIntlayerEditor } from 'angular-intlayer';\n *\n * bootstrapApplication(AppComponent).then(appRef => {\n * installIntlayerEditor(appRef.injector);\n * });\n * ```\n */\nexport const installIntlayerEditor = (injector: Injector): void => {\n /* ---------------------------------------------------------------------\n * 1. Base providers – always on\n * -------------------------------------------------------------------*/\n\n installCommunicator(injector, {\n postMessage,\n allowedOrigins: [applicationURL, editorURL, cmsURL] as string[],\n });\n installEditorEnabled(injector);\n installDictionariesRecord(injector);\n installEditedContent(injector);\n installFocusDictionary(injector);\n};\n"],"mappings":"AACA,OAAO,mBAAmB;AAC1B,SAAS,2BAA2B;AACpC,SAAS,iCAAiC;AAC1C,SAAS,4BAA4B;AACrC,SAAS,4BAA4B;AACrC,SAAS,8BAA8B;AAEvC,MAAM,EAAE,OAAO,IAAI;AACnB,MAAM,EAAE,gBAAgB,WAAW,OAAO,IAAI,UAAU,CAAC;AAEzD,MAAM,cAAc,CAAC,SAAc;AACjC,MAAI,OAAO,WAAW,YAAa;AAEnC,QAAM,aAAa,OAAO,SAAS,OAAO;AAE1C,MAAI,CAAC,WAAY;AAEjB,MAAI,OAAO,eAAe,SAAS,GAAG;AACpC,WAAO,YAAY,MAAM,OAAO,cAAc;AAAA,EAChD;AAEA,MAAI,OAAO,UAAU,SAAS,GAAG;AAC/B,WAAO,OAAO,YAAY,MAAM,OAAO,SAAS;AAAA,EAClD;AAEA,MAAI,OAAO,OAAO,SAAS,GAAG;AAC5B,WAAO,OAAO,YAAY,MAAM,OAAO,MAAM;AAAA,EAC/C;AACF;AAiBO,MAAM,wBAAwB,CAAC,aAA6B;AAKjE,sBAAoB,UAAU;AAAA,IAC5B;AAAA,IACA,gBAAgB,CAAC,gBAAgB,WAAW,MAAM;AAAA,EACpD,CAAC;AACD,uBAAqB,QAAQ;AAC7B,4BAA0B,QAAQ;AAClC,uBAAqB,QAAQ;AAC7B,yBAAuB,QAAQ;AACjC;","names":[]}
1
+ {"version":3,"file":"installIntlayerEditor.mjs","names":[],"sources":["../../../src/editor/installIntlayerEditor.ts"],"sourcesContent":["import type { Injector } from '@angular/core';\nimport configuration from '@intlayer/config/built';\nimport { installCommunicator } from './communicator';\nimport { installDictionariesRecord } from './dictionariesRecord';\nimport { installEditedContent } from './editedContent';\nimport { installEditorEnabled } from './editorEnabled';\nimport { installFocusDictionary } from './focusDictionary';\n\nconst { editor } = configuration;\nconst { applicationURL, editorURL, cmsURL } = editor ?? {};\n\nconst postMessage = (data: any) => {\n if (typeof window === 'undefined') return;\n\n const isInIframe = window.self !== window.top;\n\n if (!isInIframe) return;\n\n if (editor.applicationURL.length > 0) {\n window.postMessage(data, editor.applicationURL);\n }\n\n if (editor.editorURL.length > 0) {\n window.parent.postMessage(data, editor.editorURL);\n }\n\n if (editor.cmsURL.length > 0) {\n window.parent.postMessage(data, editor.cmsURL);\n }\n};\n\n/**\n * Angular-side replacement for the former <IntlayerEditorProvider> React component.\n *\n * Call **once** in your Angular application's bootstrap function or main module:\n *\n * ```typescript\n * import { bootstrapApplication } from '@angular/platform-browser';\n * import { AppComponent } from './app/app.component';\n * import { installIntlayerEditor } from 'angular-intlayer';\n *\n * bootstrapApplication(AppComponent).then(appRef => {\n * installIntlayerEditor(appRef.injector);\n * });\n * ```\n */\nexport const installIntlayerEditor = (injector: Injector): void => {\n /* ---------------------------------------------------------------------\n * 1. Base providers – always on\n * -------------------------------------------------------------------*/\n\n installCommunicator(injector, {\n postMessage,\n allowedOrigins: [applicationURL, editorURL, cmsURL] as string[],\n });\n installEditorEnabled(injector);\n installDictionariesRecord(injector);\n installEditedContent(injector);\n installFocusDictionary(injector);\n};\n"],"mappings":";;;;;;;;AAQA,MAAM,EAAE,WAAW;AACnB,MAAM,EAAE,gBAAgB,WAAW,WAAW,UAAU,EAAE;AAE1D,MAAM,eAAe,SAAc;AACjC,KAAI,OAAO,WAAW,YAAa;AAInC,KAAI,EAFe,OAAO,SAAS,OAAO,KAEzB;AAEjB,KAAI,OAAO,eAAe,SAAS,EACjC,QAAO,YAAY,MAAM,OAAO,eAAe;AAGjD,KAAI,OAAO,UAAU,SAAS,EAC5B,QAAO,OAAO,YAAY,MAAM,OAAO,UAAU;AAGnD,KAAI,OAAO,OAAO,SAAS,EACzB,QAAO,OAAO,YAAY,MAAM,OAAO,OAAO;;;;;;;;;;;;;;;;;AAmBlD,MAAa,yBAAyB,aAA6B;AAKjE,qBAAoB,UAAU;EAC5B;EACA,gBAAgB;GAAC;GAAgB;GAAW;GAAO;EACpD,CAAC;AACF,sBAAqB,SAAS;AAC9B,2BAA0B,SAAS;AACnC,sBAAqB,SAAS;AAC9B,wBAAuB,SAAS"}
@@ -1,59 +1,71 @@
1
- "use client";
1
+ 'use client';
2
+
3
+
4
+ import { useCommunicator } from "./communicator.mjs";
2
5
  import { DestroyRef, inject } from "@angular/core";
3
6
  import { compareUrls } from "@intlayer/editor";
4
- import { useCommunicator } from "./communicator.mjs";
7
+
8
+ //#region src/editor/useCrossFrameMessageListener.ts
9
+ /** Map<key, Set<callback>> */
5
10
  const subscribers = /* @__PURE__ */ new Map();
11
+ /** True once we've attached the single window listener */
6
12
  let windowListenerAttached = false;
13
+ /** Helper to add/remove a callback for a key */
7
14
  function addSubscriber(key, cb) {
8
- let set = subscribers.get(key);
9
- if (!set) {
10
- set = /* @__PURE__ */ new Set();
11
- subscribers.set(key, set);
12
- }
13
- set.add(cb);
15
+ let set = subscribers.get(key);
16
+ if (!set) {
17
+ set = /* @__PURE__ */ new Set();
18
+ subscribers.set(key, set);
19
+ }
20
+ set.add(cb);
14
21
  }
15
22
  function removeSubscriber(key, cb) {
16
- const set = subscribers.get(key);
17
- if (!set) return;
18
- set.delete(cb);
19
- if (set.size === 0) subscribers.delete(key);
23
+ const set = subscribers.get(key);
24
+ if (!set) return;
25
+ set.delete(cb);
26
+ if (set.size === 0) subscribers.delete(key);
20
27
  }
28
+ /** The one global window listener */
21
29
  function ensureGlobalListener(allowedOrigins, selfId) {
22
- if (windowListenerAttached) return;
23
- window.addEventListener("message", (event) => {
24
- const { type, data, senderId } = event.data ?? {};
25
- if (!type) return;
26
- if (senderId === selfId) return;
27
- if (!allowedOrigins || allowedOrigins.includes("*") || allowedOrigins.some((o) => compareUrls(o, event.origin))) {
28
- subscribers.get(type)?.forEach((cb) => cb(data));
29
- }
30
- });
31
- windowListenerAttached = true;
30
+ if (windowListenerAttached) return;
31
+ window.addEventListener("message", (event) => {
32
+ const { type, data, senderId } = event.data ?? {};
33
+ if (!type) return;
34
+ if (senderId === selfId) return;
35
+ if (!allowedOrigins || allowedOrigins.includes("*") || allowedOrigins.some((o) => compareUrls(o, event.origin))) subscribers.get(type)?.forEach((cb) => cb(data));
36
+ });
37
+ windowListenerAttached = true;
32
38
  }
39
+ /**
40
+ * useCrossFrameMessageListener
41
+ *
42
+ * @template S - type of the message payload
43
+ * @param key message type we care about
44
+ * @param onEventTriggered optional callback when a matching message arrives
45
+ * @returns postMessage(data?) helper scoped to this key
46
+ */
33
47
  const useCrossFrameMessageListener = (key, onEventTriggered) => {
34
- const { allowedOrigins, postMessage, senderId } = useCommunicator();
35
- ensureGlobalListener(allowedOrigins, senderId);
36
- if (onEventTriggered) {
37
- addSubscriber(key, onEventTriggered);
38
- try {
39
- const destroyRef = inject(DestroyRef, { optional: true });
40
- if (destroyRef) {
41
- destroyRef.onDestroy(
42
- () => removeSubscriber(key, onEventTriggered)
43
- );
44
- }
45
- } catch {
46
- console.warn(
47
- "useCrossFrameMessageListener called outside injection context; cleanup may not be available."
48
- );
49
- }
50
- }
51
- const postMessageWrapper = (data) => {
52
- postMessage({ type: key, data, senderId });
53
- };
54
- return postMessageWrapper;
55
- };
56
- export {
57
- useCrossFrameMessageListener
48
+ const { allowedOrigins, postMessage, senderId } = useCommunicator();
49
+ ensureGlobalListener(allowedOrigins, senderId);
50
+ if (onEventTriggered) {
51
+ addSubscriber(key, onEventTriggered);
52
+ try {
53
+ const destroyRef = inject(DestroyRef, { optional: true });
54
+ if (destroyRef) destroyRef.onDestroy(() => removeSubscriber(key, onEventTriggered));
55
+ } catch {
56
+ console.warn("useCrossFrameMessageListener called outside injection context; cleanup may not be available.");
57
+ }
58
+ }
59
+ const postMessageWrapper = (data) => {
60
+ postMessage({
61
+ type: key,
62
+ data,
63
+ senderId
64
+ });
65
+ };
66
+ return postMessageWrapper;
58
67
  };
68
+
69
+ //#endregion
70
+ export { useCrossFrameMessageListener };
59
71
  //# sourceMappingURL=useCrossFrameMessageListener.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/editor/useCrossFrameMessageListener.ts"],"sourcesContent":["'use client';\n\nimport { DestroyRef, inject } from '@angular/core';\nimport { type MessageKey, compareUrls } from '@intlayer/editor';\nimport { useCommunicator } from './communicator';\n\n// ---------- module-level singletons ----------\ntype AnyFn = (data: unknown) => void;\n\n/** Map<key, Set<callback>> */\nconst subscribers = new Map<string, Set<AnyFn>>();\n\n/** True once we've attached the single window listener */\nlet windowListenerAttached = false;\n\n/** Helper to add/remove a callback for a key */\nfunction addSubscriber(key: string, cb: AnyFn) {\n let set = subscribers.get(key);\n if (!set) {\n set = new Set();\n subscribers.set(key, set);\n }\n set.add(cb);\n}\n\nfunction removeSubscriber(key: string, cb: AnyFn) {\n const set = subscribers.get(key);\n if (!set) return;\n set.delete(cb);\n if (set.size === 0) subscribers.delete(key);\n}\n\n/** The one global window listener */\nfunction ensureGlobalListener(\n allowedOrigins: string[] | undefined,\n selfId: string\n) {\n if (windowListenerAttached) return;\n window.addEventListener('message', (event) => {\n const { type, data, senderId } = event.data ?? {};\n if (!type) return; // guard malformed messages\n if (senderId === selfId) return; // ignore my own\n\n // origin check\n if (\n !allowedOrigins ||\n allowedOrigins.includes('*') ||\n allowedOrigins.some((o) => compareUrls(o, event.origin))\n ) {\n // broadcast to everyone interested in this key\n subscribers.get(type)?.forEach((cb) => cb(data));\n }\n });\n windowListenerAttached = true;\n}\n// ---------- end module-level code ----------\n\n/**\n * useCrossFrameMessageListener\n *\n * @template S - type of the message payload\n * @param key message type we care about\n * @param onEventTriggered optional callback when a matching message arrives\n * @returns postMessage(data?) helper scoped to this key\n */\nexport const useCrossFrameMessageListener = <S>(\n key: `${MessageKey}` | `${MessageKey}/post` | `${MessageKey}/get`,\n onEventTriggered?: (data: S) => void\n) => {\n // Communicator is the same for everyone, so it's fine to call every time.\n const { allowedOrigins, postMessage, senderId } = useCommunicator();\n\n // --- 1. make sure the global listener exists ----\n ensureGlobalListener(allowedOrigins, senderId);\n\n // --- 2. register this caller's callback (if any) ---\n if (onEventTriggered) {\n addSubscriber(key, onEventTriggered as AnyFn);\n\n // Use Angular's DestroyRef for cleanup instead of Vue's onScopeDispose\n try {\n const destroyRef = inject(DestroyRef, { optional: true });\n if (destroyRef) {\n destroyRef.onDestroy(() =>\n removeSubscriber(key, onEventTriggered as AnyFn)\n );\n }\n } catch {\n // If called outside injection context, no cleanup available\n console.warn(\n 'useCrossFrameMessageListener called outside injection context; ' +\n 'cleanup may not be available.'\n );\n }\n }\n\n // --- 3. return a wrapper that tags outgoing messages with our key ---\n const postMessageWrapper = (data?: S) => {\n postMessage({ type: key, data, senderId });\n };\n\n return postMessageWrapper;\n};\n"],"mappings":";AAEA,SAAS,YAAY,cAAc;AACnC,SAA0B,mBAAmB;AAC7C,SAAS,uBAAuB;AAMhC,MAAM,cAAc,oBAAI,IAAwB;AAGhD,IAAI,yBAAyB;AAG7B,SAAS,cAAc,KAAa,IAAW;AAC7C,MAAI,MAAM,YAAY,IAAI,GAAG;AAC7B,MAAI,CAAC,KAAK;AACR,UAAM,oBAAI,IAAI;AACd,gBAAY,IAAI,KAAK,GAAG;AAAA,EAC1B;AACA,MAAI,IAAI,EAAE;AACZ;AAEA,SAAS,iBAAiB,KAAa,IAAW;AAChD,QAAM,MAAM,YAAY,IAAI,GAAG;AAC/B,MAAI,CAAC,IAAK;AACV,MAAI,OAAO,EAAE;AACb,MAAI,IAAI,SAAS,EAAG,aAAY,OAAO,GAAG;AAC5C;AAGA,SAAS,qBACP,gBACA,QACA;AACA,MAAI,uBAAwB;AAC5B,SAAO,iBAAiB,WAAW,CAAC,UAAU;AAC5C,UAAM,EAAE,MAAM,MAAM,SAAS,IAAI,MAAM,QAAQ,CAAC;AAChD,QAAI,CAAC,KAAM;AACX,QAAI,aAAa,OAAQ;AAGzB,QACE,CAAC,kBACD,eAAe,SAAS,GAAG,KAC3B,eAAe,KAAK,CAAC,MAAM,YAAY,GAAG,MAAM,MAAM,CAAC,GACvD;AAEA,kBAAY,IAAI,IAAI,GAAG,QAAQ,CAAC,OAAO,GAAG,IAAI,CAAC;AAAA,IACjD;AAAA,EACF,CAAC;AACD,2BAAyB;AAC3B;AAWO,MAAM,+BAA+B,CAC1C,KACA,qBACG;AAEH,QAAM,EAAE,gBAAgB,aAAa,SAAS,IAAI,gBAAgB;AAGlE,uBAAqB,gBAAgB,QAAQ;AAG7C,MAAI,kBAAkB;AACpB,kBAAc,KAAK,gBAAyB;AAG5C,QAAI;AACF,YAAM,aAAa,OAAO,YAAY,EAAE,UAAU,KAAK,CAAC;AACxD,UAAI,YAAY;AACd,mBAAW;AAAA,UAAU,MACnB,iBAAiB,KAAK,gBAAyB;AAAA,QACjD;AAAA,MACF;AAAA,IACF,QAAQ;AAEN,cAAQ;AAAA,QACN;AAAA,MAEF;AAAA,IACF;AAAA,EACF;AAGA,QAAM,qBAAqB,CAAC,SAAa;AACvC,gBAAY,EAAE,MAAM,KAAK,MAAM,SAAS,CAAC;AAAA,EAC3C;AAEA,SAAO;AACT;","names":[]}
1
+ {"version":3,"file":"useCrossFrameMessageListener.mjs","names":[],"sources":["../../../src/editor/useCrossFrameMessageListener.ts"],"sourcesContent":["'use client';\n\nimport { DestroyRef, inject } from '@angular/core';\nimport { compareUrls, type MessageKey } from '@intlayer/editor';\nimport { useCommunicator } from './communicator';\n\n// ---------- module-level singletons ----------\ntype AnyFn = (data: unknown) => void;\n\n/** Map<key, Set<callback>> */\nconst subscribers = new Map<string, Set<AnyFn>>();\n\n/** True once we've attached the single window listener */\nlet windowListenerAttached = false;\n\n/** Helper to add/remove a callback for a key */\nfunction addSubscriber(key: string, cb: AnyFn) {\n let set = subscribers.get(key);\n if (!set) {\n set = new Set();\n subscribers.set(key, set);\n }\n set.add(cb);\n}\n\nfunction removeSubscriber(key: string, cb: AnyFn) {\n const set = subscribers.get(key);\n if (!set) return;\n set.delete(cb);\n if (set.size === 0) subscribers.delete(key);\n}\n\n/** The one global window listener */\nfunction ensureGlobalListener(\n allowedOrigins: string[] | undefined,\n selfId: string\n) {\n if (windowListenerAttached) return;\n window.addEventListener('message', (event) => {\n const { type, data, senderId } = event.data ?? {};\n if (!type) return; // guard malformed messages\n if (senderId === selfId) return; // ignore my own\n\n // origin check\n if (\n !allowedOrigins ||\n allowedOrigins.includes('*') ||\n allowedOrigins.some((o) => compareUrls(o, event.origin))\n ) {\n // broadcast to everyone interested in this key\n subscribers.get(type)?.forEach((cb) => cb(data));\n }\n });\n windowListenerAttached = true;\n}\n// ---------- end module-level code ----------\n\n/**\n * useCrossFrameMessageListener\n *\n * @template S - type of the message payload\n * @param key message type we care about\n * @param onEventTriggered optional callback when a matching message arrives\n * @returns postMessage(data?) helper scoped to this key\n */\nexport const useCrossFrameMessageListener = <S>(\n key: `${MessageKey}` | `${MessageKey}/post` | `${MessageKey}/get`,\n onEventTriggered?: (data: S) => void\n) => {\n // Communicator is the same for everyone, so it's fine to call every time.\n const { allowedOrigins, postMessage, senderId } = useCommunicator();\n\n // --- 1. make sure the global listener exists ----\n ensureGlobalListener(allowedOrigins, senderId);\n\n // --- 2. register this caller's callback (if any) ---\n if (onEventTriggered) {\n addSubscriber(key, onEventTriggered as AnyFn);\n\n // Use Angular's DestroyRef for cleanup instead of Vue's onScopeDispose\n try {\n const destroyRef = inject(DestroyRef, { optional: true });\n if (destroyRef) {\n destroyRef.onDestroy(() =>\n removeSubscriber(key, onEventTriggered as AnyFn)\n );\n }\n } catch {\n // If called outside injection context, no cleanup available\n console.warn(\n 'useCrossFrameMessageListener called outside injection context; ' +\n 'cleanup may not be available.'\n );\n }\n }\n\n // --- 3. return a wrapper that tags outgoing messages with our key ---\n const postMessageWrapper = (data?: S) => {\n postMessage({ type: key, data, senderId });\n };\n\n return postMessageWrapper;\n};\n"],"mappings":";;;;;;;;;AAUA,MAAM,8BAAc,IAAI,KAAyB;;AAGjD,IAAI,yBAAyB;;AAG7B,SAAS,cAAc,KAAa,IAAW;CAC7C,IAAI,MAAM,YAAY,IAAI,IAAI;AAC9B,KAAI,CAAC,KAAK;AACR,wBAAM,IAAI,KAAK;AACf,cAAY,IAAI,KAAK,IAAI;;AAE3B,KAAI,IAAI,GAAG;;AAGb,SAAS,iBAAiB,KAAa,IAAW;CAChD,MAAM,MAAM,YAAY,IAAI,IAAI;AAChC,KAAI,CAAC,IAAK;AACV,KAAI,OAAO,GAAG;AACd,KAAI,IAAI,SAAS,EAAG,aAAY,OAAO,IAAI;;;AAI7C,SAAS,qBACP,gBACA,QACA;AACA,KAAI,uBAAwB;AAC5B,QAAO,iBAAiB,YAAY,UAAU;EAC5C,MAAM,EAAE,MAAM,MAAM,aAAa,MAAM,QAAQ,EAAE;AACjD,MAAI,CAAC,KAAM;AACX,MAAI,aAAa,OAAQ;AAGzB,MACE,CAAC,kBACD,eAAe,SAAS,IAAI,IAC5B,eAAe,MAAM,MAAM,YAAY,GAAG,MAAM,OAAO,CAAC,CAGxD,aAAY,IAAI,KAAK,EAAE,SAAS,OAAO,GAAG,KAAK,CAAC;GAElD;AACF,0BAAyB;;;;;;;;;;AAY3B,MAAa,gCACX,KACA,qBACG;CAEH,MAAM,EAAE,gBAAgB,aAAa,aAAa,iBAAiB;AAGnE,sBAAqB,gBAAgB,SAAS;AAG9C,KAAI,kBAAkB;AACpB,gBAAc,KAAK,iBAA0B;AAG7C,MAAI;GACF,MAAM,aAAa,OAAO,YAAY,EAAE,UAAU,MAAM,CAAC;AACzD,OAAI,WACF,YAAW,gBACT,iBAAiB,KAAK,iBAA0B,CACjD;UAEG;AAEN,WAAQ,KACN,+FAED;;;CAKL,MAAM,sBAAsB,SAAa;AACvC,cAAY;GAAE,MAAM;GAAK;GAAM;GAAU,CAAC;;AAG5C,QAAO"}
@@ -1,71 +1,113 @@
1
- import { signal } from "@angular/core";
2
1
  import { useCommunicator } from "./communicator.mjs";
3
2
  import { useCrossFrameMessageListener } from "./useCrossFrameMessageListener.mjs";
3
+ import { signal } from "@angular/core";
4
+
5
+ //#region src/editor/useCrossFrameState.ts
4
6
  const crossFrameStateCache = /* @__PURE__ */ new Map();
7
+ /**
8
+ * Utility to resolve either a value or an updater function (mirrors React's `setState`).
9
+ */
5
10
  const resolveState = (state, prevState) => {
6
- if (typeof state === "function") {
7
- return state(prevState);
8
- }
9
- return state;
11
+ if (typeof state === "function") return state(prevState);
12
+ return state;
10
13
  };
14
+ /**
15
+ * Creates a plain object copy that can be safely serialized
16
+ * for postMessage communication
17
+ */
11
18
  const toSerializable = (obj) => {
12
- if (obj === null || obj === void 0) return obj;
13
- return JSON.parse(JSON.stringify(obj));
14
- };
15
- const useCrossFrameState = (key, initialState, options = { emit: true, receive: true }) => {
16
- if (crossFrameStateCache.has(key)) {
17
- const { state, setState: setState2, postState: postState2 } = crossFrameStateCache.get(key);
18
- return [state, setState2, postState2];
19
- }
20
- const { emit = true, receive = true } = options;
21
- const stateSignal = signal(resolveState(initialState));
22
- const { postMessage, senderId } = useCommunicator();
23
- const broadcastState = (value) => {
24
- if (!emit || typeof postMessage !== "function" || typeof value === "undefined")
25
- return;
26
- postMessage({
27
- type: `${key}/post`,
28
- data: value,
29
- senderId
30
- });
31
- };
32
- const setState = (valueOrUpdater) => {
33
- const next = resolveState(valueOrUpdater, stateSignal());
34
- const serialised = toSerializable(next);
35
- stateSignal.set(serialised);
36
- broadcastState(serialised);
37
- };
38
- const postState = () => {
39
- if (typeof postMessage !== "function") return;
40
- postMessage({
41
- type: `${key}/post`,
42
- data: stateSignal(),
43
- senderId
44
- });
45
- };
46
- broadcastState(stateSignal());
47
- if (receive && typeof postMessage === "function" && typeof stateSignal() === "undefined") {
48
- postMessage({ type: `${key}/get`, senderId });
49
- }
50
- useCrossFrameMessageListener(
51
- `${key}/post`,
52
- receive ? (data) => {
53
- stateSignal.set(data);
54
- } : void 0
55
- );
56
- const handleGetMessage = (_, originSenderId) => {
57
- if (!emit) return;
58
- if (originSenderId === senderId) return;
59
- broadcastState(stateSignal());
60
- };
61
- useCrossFrameMessageListener(
62
- `${key}/get`,
63
- emit ? handleGetMessage : void 0
64
- );
65
- crossFrameStateCache.set(key, { state: stateSignal, setState, postState });
66
- return [stateSignal, setState, postState];
19
+ if (obj === null || obj === void 0) return obj;
20
+ return JSON.parse(JSON.stringify(obj));
67
21
  };
68
- export {
69
- useCrossFrameState
22
+ /**
23
+ * Angular replacement for Vue's cross-frame state composable.
24
+ * It synchronises a reactive value across frames/windows via the `postMessage` API.
25
+ *
26
+ * @template S The type of the state being synchronised.
27
+ * @param key Unique key identifying this state channel.
28
+ * @param initialState Initial value (or lazy factory) for the state.
29
+ * @param options Control flags for emitting/receiving updates.
30
+ *
31
+ * @returns `[stateSignal, setState, postState]`
32
+ * - `stateSignal` – Angular `Signal<S | undefined>` holding the current state.
33
+ * - `setState` – Setter with the same API as React's `setState`.
34
+ * - `postState` – Manually broadcast the current state (useful after mutations outside `setState`).
35
+ */
36
+ const useCrossFrameState = (key, initialState, options = {
37
+ emit: true,
38
+ receive: true
39
+ }) => {
40
+ if (crossFrameStateCache.has(key)) {
41
+ const { state, setState: setState$1, postState: postState$1 } = crossFrameStateCache.get(key);
42
+ return [
43
+ state,
44
+ setState$1,
45
+ postState$1
46
+ ];
47
+ }
48
+ const { emit = true, receive = true } = options;
49
+ /**
50
+ * Internal reactive state using Angular signals.
51
+ * We resolve the initial value here to avoid one extra render (same idea as in the React version).
52
+ */
53
+ const stateSignal = signal(resolveState(initialState));
54
+ const { postMessage, senderId } = useCommunicator();
55
+ /**
56
+ * Broadcast the given value if emitting is allowed and the communicator is ready.
57
+ */
58
+ const broadcastState = (value) => {
59
+ if (!emit || typeof postMessage !== "function" || typeof value === "undefined") return;
60
+ postMessage({
61
+ type: `${key}/post`,
62
+ data: value,
63
+ senderId
64
+ });
65
+ };
66
+ /**
67
+ * Setter that mirrors React's `setState` signature (supports value or updater fn).
68
+ */
69
+ const setState = (valueOrUpdater) => {
70
+ const serialised = toSerializable(resolveState(valueOrUpdater, stateSignal()));
71
+ stateSignal.set(serialised);
72
+ broadcastState(serialised);
73
+ };
74
+ /**
75
+ * Manually broadcast the current state to peers.
76
+ */
77
+ const postState = () => {
78
+ if (typeof postMessage !== "function") return;
79
+ postMessage({
80
+ type: `${key}/post`,
81
+ data: stateSignal(),
82
+ senderId
83
+ });
84
+ };
85
+ broadcastState(stateSignal());
86
+ if (receive && typeof postMessage === "function" && typeof stateSignal() === "undefined") postMessage({
87
+ type: `${key}/get`,
88
+ senderId
89
+ });
90
+ useCrossFrameMessageListener(`${key}/post`, receive ? (data) => {
91
+ stateSignal.set(data);
92
+ } : void 0);
93
+ const handleGetMessage = (_, originSenderId) => {
94
+ if (!emit) return;
95
+ if (originSenderId === senderId) return;
96
+ broadcastState(stateSignal());
97
+ };
98
+ useCrossFrameMessageListener(`${key}/get`, emit ? handleGetMessage : void 0);
99
+ crossFrameStateCache.set(key, {
100
+ state: stateSignal,
101
+ setState,
102
+ postState
103
+ });
104
+ return [
105
+ stateSignal,
106
+ setState,
107
+ postState
108
+ ];
70
109
  };
110
+
111
+ //#endregion
112
+ export { useCrossFrameState };
71
113
  //# sourceMappingURL=useCrossFrameState.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/editor/useCrossFrameState.ts"],"sourcesContent":["import { signal, Signal } from '@angular/core';\nimport { type MessageKey } from '@intlayer/editor';\nimport { useCommunicator } from './communicator';\nimport { useCrossFrameMessageListener } from './useCrossFrameMessageListener';\n\nexport type CrossFrameStateOptions = {\n /** Whether to broadcast state changes to other instances (default: true) */\n emit?: boolean;\n /** Whether to listen for state updates from other instances (default: true) */\n receive?: boolean;\n};\n\nconst crossFrameStateCache = new Map<\n string,\n {\n state: Signal<any>;\n setState: (v: any | ((prev: any) => any)) => void;\n postState: () => void;\n }\n>();\n\n/**\n * Utility to resolve either a value or an updater function (mirrors React's `setState`).\n */\nconst resolveState = <S>(\n state: S | ((prev?: S) => S) | undefined,\n prevState?: S\n): S | undefined => {\n if (typeof state === 'function') {\n return (state as (prev?: S) => S)(prevState);\n }\n return state as S;\n};\n\n/**\n * Creates a plain object copy that can be safely serialized\n * for postMessage communication\n */\nconst toSerializable = <T>(obj: T): T => {\n if (obj === null || obj === undefined) return obj;\n // Using parse/stringify for a quick deep clone to remove reactivity\n return JSON.parse(JSON.stringify(obj));\n};\n\n/**\n * Angular replacement for Vue's cross-frame state composable.\n * It synchronises a reactive value across frames/windows via the `postMessage` API.\n *\n * @template S The type of the state being synchronised.\n * @param key Unique key identifying this state channel.\n * @param initialState Initial value (or lazy factory) for the state.\n * @param options Control flags for emitting/receiving updates.\n *\n * @returns `[stateSignal, setState, postState]`\n * - `stateSignal` – Angular `Signal<S | undefined>` holding the current state.\n * - `setState` – Setter with the same API as React's `setState`.\n * - `postState` – Manually broadcast the current state (useful after mutations outside `setState`).\n */\nexport const useCrossFrameState = <S>(\n key: `${MessageKey}`,\n initialState?: S | (() => S),\n options: CrossFrameStateOptions = { emit: true, receive: true }\n): [\n Signal<S | undefined>,\n (v: S | ((prev: S | undefined) => S)) => void,\n () => void,\n] => {\n if (crossFrameStateCache.has(key)) {\n // Return the existing instance\n const { state, setState, postState } = crossFrameStateCache.get(key)!;\n return [state, setState, postState];\n }\n\n const { emit = true, receive = true } = options;\n\n /**\n * Internal reactive state using Angular signals.\n * We resolve the initial value here to avoid one extra render (same idea as in the React version).\n */\n const stateSignal = signal<S | undefined>(resolveState<S>(initialState));\n\n // Get communicator within injection context\n const { postMessage, senderId } = useCommunicator();\n\n /**\n * Broadcast the given value if emitting is allowed and the communicator is ready.\n */\n const broadcastState = (value: S | undefined) => {\n if (\n !emit ||\n typeof postMessage !== 'function' ||\n typeof value === 'undefined'\n )\n return;\n postMessage({\n type: `${key}/post`,\n data: value,\n senderId,\n });\n };\n\n /**\n * Setter that mirrors React's `setState` signature (supports value or updater fn).\n */\n const setState = (valueOrUpdater: S | ((prev: S | undefined) => S)) => {\n const next = resolveState<S>(valueOrUpdater as any, stateSignal());\n const serialised = toSerializable(next);\n stateSignal.set(serialised);\n broadcastState(serialised);\n };\n\n /**\n * Manually broadcast the current state to peers.\n */\n const postState = () => {\n if (typeof postMessage !== 'function') return;\n postMessage({\n type: `${key}/post`,\n data: stateSignal(),\n senderId,\n });\n };\n\n // Emit the initial state (if any) right away so that peers can pick it up.\n broadcastState(stateSignal());\n\n // If we are in receive mode but have no state yet, ask peers for theirs.\n if (\n receive &&\n typeof postMessage === 'function' &&\n typeof stateSignal() === 'undefined'\n ) {\n postMessage({ type: `${key}/get`, senderId });\n }\n\n /* ───────────────────── Incoming messages ───────────────────── */\n\n // 1. Updates posted by other frames\n useCrossFrameMessageListener<S>(\n `${key}/post`,\n receive\n ? (data) => {\n stateSignal.set(data);\n }\n : undefined\n );\n\n // 2. Requests from peers asking for our current value\n const handleGetMessage = (_: unknown, originSenderId?: string) => {\n if (!emit) return;\n if (originSenderId === senderId) return; // Don't respond to our own request\n broadcastState(stateSignal());\n };\n\n useCrossFrameMessageListener(\n `${key}/get`,\n emit ? handleGetMessage : undefined\n );\n\n // Cache this instance\n crossFrameStateCache.set(key, { state: stateSignal, setState, postState });\n\n return [stateSignal as Signal<S | undefined>, setState, postState];\n};\n"],"mappings":"AAAA,SAAS,cAAsB;AAE/B,SAAS,uBAAuB;AAChC,SAAS,oCAAoC;AAS7C,MAAM,uBAAuB,oBAAI,IAO/B;AAKF,MAAM,eAAe,CACnB,OACA,cACkB;AAClB,MAAI,OAAO,UAAU,YAAY;AAC/B,WAAQ,MAA0B,SAAS;AAAA,EAC7C;AACA,SAAO;AACT;AAMA,MAAM,iBAAiB,CAAI,QAAc;AACvC,MAAI,QAAQ,QAAQ,QAAQ,OAAW,QAAO;AAE9C,SAAO,KAAK,MAAM,KAAK,UAAU,GAAG,CAAC;AACvC;AAgBO,MAAM,qBAAqB,CAChC,KACA,cACA,UAAkC,EAAE,MAAM,MAAM,SAAS,KAAK,MAK3D;AACH,MAAI,qBAAqB,IAAI,GAAG,GAAG;AAEjC,UAAM,EAAE,OAAO,UAAAA,WAAU,WAAAC,WAAU,IAAI,qBAAqB,IAAI,GAAG;AACnE,WAAO,CAAC,OAAOD,WAAUC,UAAS;AAAA,EACpC;AAEA,QAAM,EAAE,OAAO,MAAM,UAAU,KAAK,IAAI;AAMxC,QAAM,cAAc,OAAsB,aAAgB,YAAY,CAAC;AAGvE,QAAM,EAAE,aAAa,SAAS,IAAI,gBAAgB;AAKlD,QAAM,iBAAiB,CAAC,UAAyB;AAC/C,QACE,CAAC,QACD,OAAO,gBAAgB,cACvB,OAAO,UAAU;AAEjB;AACF,gBAAY;AAAA,MACV,MAAM,GAAG,GAAG;AAAA,MACZ,MAAM;AAAA,MACN;AAAA,IACF,CAAC;AAAA,EACH;AAKA,QAAM,WAAW,CAAC,mBAAqD;AACrE,UAAM,OAAO,aAAgB,gBAAuB,YAAY,CAAC;AACjE,UAAM,aAAa,eAAe,IAAI;AACtC,gBAAY,IAAI,UAAU;AAC1B,mBAAe,UAAU;AAAA,EAC3B;AAKA,QAAM,YAAY,MAAM;AACtB,QAAI,OAAO,gBAAgB,WAAY;AACvC,gBAAY;AAAA,MACV,MAAM,GAAG,GAAG;AAAA,MACZ,MAAM,YAAY;AAAA,MAClB;AAAA,IACF,CAAC;AAAA,EACH;AAGA,iBAAe,YAAY,CAAC;AAG5B,MACE,WACA,OAAO,gBAAgB,cACvB,OAAO,YAAY,MAAM,aACzB;AACA,gBAAY,EAAE,MAAM,GAAG,GAAG,QAAQ,SAAS,CAAC;AAAA,EAC9C;AAKA;AAAA,IACE,GAAG,GAAG;AAAA,IACN,UACI,CAAC,SAAS;AACR,kBAAY,IAAI,IAAI;AAAA,IACtB,IACA;AAAA,EACN;AAGA,QAAM,mBAAmB,CAAC,GAAY,mBAA4B;AAChE,QAAI,CAAC,KAAM;AACX,QAAI,mBAAmB,SAAU;AACjC,mBAAe,YAAY,CAAC;AAAA,EAC9B;AAEA;AAAA,IACE,GAAG,GAAG;AAAA,IACN,OAAO,mBAAmB;AAAA,EAC5B;AAGA,uBAAqB,IAAI,KAAK,EAAE,OAAO,aAAa,UAAU,UAAU,CAAC;AAEzE,SAAO,CAAC,aAAsC,UAAU,SAAS;AACnE;","names":["setState","postState"]}
1
+ {"version":3,"file":"useCrossFrameState.mjs","names":["setState","postState"],"sources":["../../../src/editor/useCrossFrameState.ts"],"sourcesContent":["import { type Signal, signal } from '@angular/core';\nimport type { MessageKey } from '@intlayer/editor';\nimport { useCommunicator } from './communicator';\nimport { useCrossFrameMessageListener } from './useCrossFrameMessageListener';\n\nexport type CrossFrameStateOptions = {\n /** Whether to broadcast state changes to other instances (default: true) */\n emit?: boolean;\n /** Whether to listen for state updates from other instances (default: true) */\n receive?: boolean;\n};\n\nconst crossFrameStateCache = new Map<\n string,\n {\n state: Signal<any>;\n setState: (v: any | ((prev: any) => any)) => void;\n postState: () => void;\n }\n>();\n\n/**\n * Utility to resolve either a value or an updater function (mirrors React's `setState`).\n */\nconst resolveState = <S>(\n state: S | ((prev?: S) => S) | undefined,\n prevState?: S\n): S | undefined => {\n if (typeof state === 'function') {\n return (state as (prev?: S) => S)(prevState);\n }\n return state as S;\n};\n\n/**\n * Creates a plain object copy that can be safely serialized\n * for postMessage communication\n */\nconst toSerializable = <T>(obj: T): T => {\n if (obj === null || obj === undefined) return obj;\n // Using parse/stringify for a quick deep clone to remove reactivity\n return JSON.parse(JSON.stringify(obj));\n};\n\n/**\n * Angular replacement for Vue's cross-frame state composable.\n * It synchronises a reactive value across frames/windows via the `postMessage` API.\n *\n * @template S The type of the state being synchronised.\n * @param key Unique key identifying this state channel.\n * @param initialState Initial value (or lazy factory) for the state.\n * @param options Control flags for emitting/receiving updates.\n *\n * @returns `[stateSignal, setState, postState]`\n * - `stateSignal` – Angular `Signal<S | undefined>` holding the current state.\n * - `setState` – Setter with the same API as React's `setState`.\n * - `postState` – Manually broadcast the current state (useful after mutations outside `setState`).\n */\nexport const useCrossFrameState = <S>(\n key: `${MessageKey}`,\n initialState?: S | (() => S),\n options: CrossFrameStateOptions = { emit: true, receive: true }\n): [\n Signal<S | undefined>,\n (v: S | ((prev: S | undefined) => S)) => void,\n () => void,\n] => {\n if (crossFrameStateCache.has(key)) {\n // Return the existing instance\n const { state, setState, postState } = crossFrameStateCache.get(key)!;\n return [state, setState, postState];\n }\n\n const { emit = true, receive = true } = options;\n\n /**\n * Internal reactive state using Angular signals.\n * We resolve the initial value here to avoid one extra render (same idea as in the React version).\n */\n const stateSignal = signal<S | undefined>(resolveState<S>(initialState));\n\n // Get communicator within injection context\n const { postMessage, senderId } = useCommunicator();\n\n /**\n * Broadcast the given value if emitting is allowed and the communicator is ready.\n */\n const broadcastState = (value: S | undefined) => {\n if (\n !emit ||\n typeof postMessage !== 'function' ||\n typeof value === 'undefined'\n )\n return;\n postMessage({\n type: `${key}/post`,\n data: value,\n senderId,\n });\n };\n\n /**\n * Setter that mirrors React's `setState` signature (supports value or updater fn).\n */\n const setState = (valueOrUpdater: S | ((prev: S | undefined) => S)) => {\n const next = resolveState<S>(valueOrUpdater as any, stateSignal());\n const serialised = toSerializable(next);\n stateSignal.set(serialised);\n broadcastState(serialised);\n };\n\n /**\n * Manually broadcast the current state to peers.\n */\n const postState = () => {\n if (typeof postMessage !== 'function') return;\n postMessage({\n type: `${key}/post`,\n data: stateSignal(),\n senderId,\n });\n };\n\n // Emit the initial state (if any) right away so that peers can pick it up.\n broadcastState(stateSignal());\n\n // If we are in receive mode but have no state yet, ask peers for theirs.\n if (\n receive &&\n typeof postMessage === 'function' &&\n typeof stateSignal() === 'undefined'\n ) {\n postMessage({ type: `${key}/get`, senderId });\n }\n\n /* ───────────────────── Incoming messages ───────────────────── */\n\n // 1. Updates posted by other frames\n useCrossFrameMessageListener<S>(\n `${key}/post`,\n receive\n ? (data) => {\n stateSignal.set(data);\n }\n : undefined\n );\n\n // 2. Requests from peers asking for our current value\n const handleGetMessage = (_: unknown, originSenderId?: string) => {\n if (!emit) return;\n if (originSenderId === senderId) return; // Don't respond to our own request\n broadcastState(stateSignal());\n };\n\n useCrossFrameMessageListener(\n `${key}/get`,\n emit ? handleGetMessage : undefined\n );\n\n // Cache this instance\n crossFrameStateCache.set(key, { state: stateSignal, setState, postState });\n\n return [stateSignal as Signal<S | undefined>, setState, postState];\n};\n"],"mappings":";;;;;AAYA,MAAM,uCAAuB,IAAI,KAO9B;;;;AAKH,MAAM,gBACJ,OACA,cACkB;AAClB,KAAI,OAAO,UAAU,WACnB,QAAQ,MAA0B,UAAU;AAE9C,QAAO;;;;;;AAOT,MAAM,kBAAqB,QAAc;AACvC,KAAI,QAAQ,QAAQ,QAAQ,OAAW,QAAO;AAE9C,QAAO,KAAK,MAAM,KAAK,UAAU,IAAI,CAAC;;;;;;;;;;;;;;;;AAiBxC,MAAa,sBACX,KACA,cACA,UAAkC;CAAE,MAAM;CAAM,SAAS;CAAM,KAK5D;AACH,KAAI,qBAAqB,IAAI,IAAI,EAAE;EAEjC,MAAM,EAAE,OAAO,sBAAU,2BAAc,qBAAqB,IAAI,IAAI;AACpE,SAAO;GAAC;GAAOA;GAAUC;GAAU;;CAGrC,MAAM,EAAE,OAAO,MAAM,UAAU,SAAS;;;;;CAMxC,MAAM,cAAc,OAAsB,aAAgB,aAAa,CAAC;CAGxE,MAAM,EAAE,aAAa,aAAa,iBAAiB;;;;CAKnD,MAAM,kBAAkB,UAAyB;AAC/C,MACE,CAAC,QACD,OAAO,gBAAgB,cACvB,OAAO,UAAU,YAEjB;AACF,cAAY;GACV,MAAM,GAAG,IAAI;GACb,MAAM;GACN;GACD,CAAC;;;;;CAMJ,MAAM,YAAY,mBAAqD;EAErE,MAAM,aAAa,eADN,aAAgB,gBAAuB,aAAa,CAAC,CAC3B;AACvC,cAAY,IAAI,WAAW;AAC3B,iBAAe,WAAW;;;;;CAM5B,MAAM,kBAAkB;AACtB,MAAI,OAAO,gBAAgB,WAAY;AACvC,cAAY;GACV,MAAM,GAAG,IAAI;GACb,MAAM,aAAa;GACnB;GACD,CAAC;;AAIJ,gBAAe,aAAa,CAAC;AAG7B,KACE,WACA,OAAO,gBAAgB,cACvB,OAAO,aAAa,KAAK,YAEzB,aAAY;EAAE,MAAM,GAAG,IAAI;EAAO;EAAU,CAAC;AAM/C,8BACE,GAAG,IAAI,QACP,WACK,SAAS;AACR,cAAY,IAAI,KAAK;KAEvB,OACL;CAGD,MAAM,oBAAoB,GAAY,mBAA4B;AAChE,MAAI,CAAC,KAAM;AACX,MAAI,mBAAmB,SAAU;AACjC,iBAAe,aAAa,CAAC;;AAG/B,8BACE,GAAG,IAAI,OACP,OAAO,mBAAmB,OAC3B;AAGD,sBAAqB,IAAI,KAAK;EAAE,OAAO;EAAa;EAAU;EAAW,CAAC;AAE1E,QAAO;EAAC;EAAsC;EAAU;EAAU"}
@@ -1,47 +1,63 @@
1
+ import { useCrossFrameState } from "./useCrossFrameState.mjs";
1
2
  import { DestroyRef, inject } from "@angular/core";
2
3
  import { MessageKey } from "@intlayer/editor";
3
- import { useCrossFrameState } from "./useCrossFrameState.mjs";
4
+
5
+ //#region src/editor/useCrossURLPathState.ts
6
+ /**
7
+ * Hook to create and manage a cross-frame synchronized URL path state
8
+ * @param initial - The initial URL path
9
+ * @param opts - Options for controlling emit and receive behavior
10
+ * @returns A tuple containing [state signal, setState function, forceSync function]
11
+ */
4
12
  const useCrossURLPathState = (initial, opts) => useCrossFrameState(MessageKey.INTLAYER_URL_CHANGE, initial, opts);
13
+ /**
14
+ * Hook for host applications to push URL path changes into the shared state
15
+ * This also monkey patches history methods to capture navigation events
16
+ * @param initial - The initial URL path
17
+ * @returns A tuple containing [state signal, setState function]
18
+ */
5
19
  const useCrossURLPathSetter = (initial) => {
6
- const [state, setState] = useCrossURLPathState(initial, {
7
- emit: true,
8
- receive: false
9
- });
10
- let originalPushState;
11
- let originalReplaceState;
12
- const update = () => setState(window.location.pathname);
13
- try {
14
- const destroyRef = inject(DestroyRef, { optional: true });
15
- if (destroyRef && typeof window !== "undefined") {
16
- originalPushState = history.pushState;
17
- originalReplaceState = history.replaceState;
18
- const wrap = (fn) => (...args) => {
19
- fn.apply(history, args);
20
- window.dispatchEvent(new Event("locationchange"));
21
- };
22
- history.pushState = wrap(originalPushState);
23
- history.replaceState = wrap(originalReplaceState);
24
- window.addEventListener("locationchange", update);
25
- window.addEventListener("popstate", update);
26
- window.addEventListener("hashchange", update);
27
- update();
28
- destroyRef.onDestroy(() => {
29
- window.removeEventListener("locationchange", update);
30
- window.removeEventListener("popstate", update);
31
- window.removeEventListener("hashchange", update);
32
- if (originalPushState) history.pushState = originalPushState;
33
- if (originalReplaceState) history.replaceState = originalReplaceState;
34
- });
35
- }
36
- } catch {
37
- console.warn(
38
- "useCrossURLPathSetter called outside injection context; URL path synchronization may not be available."
39
- );
40
- }
41
- return [state, setState];
42
- };
43
- export {
44
- useCrossURLPathSetter,
45
- useCrossURLPathState
20
+ const [state, setState] = useCrossURLPathState(initial, {
21
+ emit: true,
22
+ receive: false
23
+ });
24
+ let originalPushState;
25
+ let originalReplaceState;
26
+ const update = () => setState(window.location.pathname);
27
+ try {
28
+ const destroyRef = inject(DestroyRef, { optional: true });
29
+ if (destroyRef && typeof window !== "undefined") {
30
+ originalPushState = history.pushState;
31
+ originalReplaceState = history.replaceState;
32
+ /**
33
+ * Wraps a history function to dispatch a custom event when called
34
+ * @param fn - The history function to wrap
35
+ * @returns The wrapped function
36
+ */
37
+ const wrap = (fn) => (...args) => {
38
+ fn.apply(history, args);
39
+ window.dispatchEvent(new Event("locationchange"));
40
+ };
41
+ history.pushState = wrap(originalPushState);
42
+ history.replaceState = wrap(originalReplaceState);
43
+ window.addEventListener("locationchange", update);
44
+ window.addEventListener("popstate", update);
45
+ window.addEventListener("hashchange", update);
46
+ update();
47
+ destroyRef.onDestroy(() => {
48
+ window.removeEventListener("locationchange", update);
49
+ window.removeEventListener("popstate", update);
50
+ window.removeEventListener("hashchange", update);
51
+ if (originalPushState) history.pushState = originalPushState;
52
+ if (originalReplaceState) history.replaceState = originalReplaceState;
53
+ });
54
+ }
55
+ } catch {
56
+ console.warn("useCrossURLPathSetter called outside injection context; URL path synchronization may not be available.");
57
+ }
58
+ return [state, setState];
46
59
  };
60
+
61
+ //#endregion
62
+ export { useCrossURLPathSetter, useCrossURLPathState };
47
63
  //# sourceMappingURL=useCrossURLPathState.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/editor/useCrossURLPathState.ts"],"sourcesContent":["import { DestroyRef, inject } from '@angular/core';\nimport { MessageKey } from '@intlayer/editor';\nimport { useCrossFrameState } from './useCrossFrameState';\n\n/**\n * Hook to create and manage a cross-frame synchronized URL path state\n * @param initial - The initial URL path\n * @param opts - Options for controlling emit and receive behavior\n * @returns A tuple containing [state signal, setState function, forceSync function]\n */\nexport const useCrossURLPathState = (\n initial?: string,\n opts?: Parameters<typeof useCrossFrameState>[2]\n) => useCrossFrameState<string>(MessageKey.INTLAYER_URL_CHANGE, initial, opts);\n\n/**\n * Hook for host applications to push URL path changes into the shared state\n * This also monkey patches history methods to capture navigation events\n * @param initial - The initial URL path\n * @returns A tuple containing [state signal, setState function]\n */\nexport const useCrossURLPathSetter = (initial?: string) => {\n const [state, setState] = useCrossURLPathState(initial, {\n emit: true,\n receive: false,\n });\n\n // Original history methods\n let originalPushState: typeof history.pushState;\n let originalReplaceState: typeof history.replaceState;\n\n // Function to update state with current pathname\n const update = () => setState(window.location.pathname);\n\n // Use Angular's DestroyRef for cleanup instead of Vue lifecycle hooks\n try {\n const destroyRef = inject(DestroyRef, { optional: true });\n\n if (destroyRef && typeof window !== 'undefined') {\n // Save original methods\n originalPushState = history.pushState;\n originalReplaceState = history.replaceState;\n\n /**\n * Wraps a history function to dispatch a custom event when called\n * @param fn - The history function to wrap\n * @returns The wrapped function\n */\n const wrap =\n (fn: typeof history.pushState) =>\n (...args: Parameters<typeof history.pushState>) => {\n fn.apply(history, args);\n window.dispatchEvent(new Event('locationchange'));\n };\n\n // Patch history methods\n history.pushState = wrap(originalPushState);\n history.replaceState = wrap(originalReplaceState);\n\n // Add event listeners\n window.addEventListener('locationchange', update);\n window.addEventListener('popstate', update);\n window.addEventListener('hashchange', update);\n\n // Initialize immediately\n update();\n\n // Clean up on destroy\n destroyRef.onDestroy(() => {\n window.removeEventListener('locationchange', update);\n window.removeEventListener('popstate', update);\n window.removeEventListener('hashchange', update);\n\n // Restore original history methods\n if (originalPushState) history.pushState = originalPushState;\n if (originalReplaceState) history.replaceState = originalReplaceState;\n });\n }\n } catch {\n console.warn(\n 'useCrossURLPathSetter called outside injection context; ' +\n 'URL path synchronization may not be available.'\n );\n }\n\n return [state, setState] as const;\n};\n"],"mappings":"AAAA,SAAS,YAAY,cAAc;AACnC,SAAS,kBAAkB;AAC3B,SAAS,0BAA0B;AAQ5B,MAAM,uBAAuB,CAClC,SACA,SACG,mBAA2B,WAAW,qBAAqB,SAAS,IAAI;AAQtE,MAAM,wBAAwB,CAAC,YAAqB;AACzD,QAAM,CAAC,OAAO,QAAQ,IAAI,qBAAqB,SAAS;AAAA,IACtD,MAAM;AAAA,IACN,SAAS;AAAA,EACX,CAAC;AAGD,MAAI;AACJ,MAAI;AAGJ,QAAM,SAAS,MAAM,SAAS,OAAO,SAAS,QAAQ;AAGtD,MAAI;AACF,UAAM,aAAa,OAAO,YAAY,EAAE,UAAU,KAAK,CAAC;AAExD,QAAI,cAAc,OAAO,WAAW,aAAa;AAE/C,0BAAoB,QAAQ;AAC5B,6BAAuB,QAAQ;AAO/B,YAAM,OACJ,CAAC,OACD,IAAI,SAA+C;AACjD,WAAG,MAAM,SAAS,IAAI;AACtB,eAAO,cAAc,IAAI,MAAM,gBAAgB,CAAC;AAAA,MAClD;AAGF,cAAQ,YAAY,KAAK,iBAAiB;AAC1C,cAAQ,eAAe,KAAK,oBAAoB;AAGhD,aAAO,iBAAiB,kBAAkB,MAAM;AAChD,aAAO,iBAAiB,YAAY,MAAM;AAC1C,aAAO,iBAAiB,cAAc,MAAM;AAG5C,aAAO;AAGP,iBAAW,UAAU,MAAM;AACzB,eAAO,oBAAoB,kBAAkB,MAAM;AACnD,eAAO,oBAAoB,YAAY,MAAM;AAC7C,eAAO,oBAAoB,cAAc,MAAM;AAG/C,YAAI,kBAAmB,SAAQ,YAAY;AAC3C,YAAI,qBAAsB,SAAQ,eAAe;AAAA,MACnD,CAAC;AAAA,IACH;AAAA,EACF,QAAQ;AACN,YAAQ;AAAA,MACN;AAAA,IAEF;AAAA,EACF;AAEA,SAAO,CAAC,OAAO,QAAQ;AACzB;","names":[]}
1
+ {"version":3,"file":"useCrossURLPathState.mjs","names":["originalPushState: typeof history.pushState","originalReplaceState: typeof history.replaceState"],"sources":["../../../src/editor/useCrossURLPathState.ts"],"sourcesContent":["import { DestroyRef, inject } from '@angular/core';\nimport { MessageKey } from '@intlayer/editor';\nimport { useCrossFrameState } from './useCrossFrameState';\n\n/**\n * Hook to create and manage a cross-frame synchronized URL path state\n * @param initial - The initial URL path\n * @param opts - Options for controlling emit and receive behavior\n * @returns A tuple containing [state signal, setState function, forceSync function]\n */\nexport const useCrossURLPathState = (\n initial?: string,\n opts?: Parameters<typeof useCrossFrameState>[2]\n) => useCrossFrameState<string>(MessageKey.INTLAYER_URL_CHANGE, initial, opts);\n\n/**\n * Hook for host applications to push URL path changes into the shared state\n * This also monkey patches history methods to capture navigation events\n * @param initial - The initial URL path\n * @returns A tuple containing [state signal, setState function]\n */\nexport const useCrossURLPathSetter = (initial?: string) => {\n const [state, setState] = useCrossURLPathState(initial, {\n emit: true,\n receive: false,\n });\n\n // Original history methods\n let originalPushState: typeof history.pushState;\n let originalReplaceState: typeof history.replaceState;\n\n // Function to update state with current pathname\n const update = () => setState(window.location.pathname);\n\n // Use Angular's DestroyRef for cleanup instead of Vue lifecycle hooks\n try {\n const destroyRef = inject(DestroyRef, { optional: true });\n\n if (destroyRef && typeof window !== 'undefined') {\n // Save original methods\n originalPushState = history.pushState;\n originalReplaceState = history.replaceState;\n\n /**\n * Wraps a history function to dispatch a custom event when called\n * @param fn - The history function to wrap\n * @returns The wrapped function\n */\n const wrap =\n (fn: typeof history.pushState) =>\n (...args: Parameters<typeof history.pushState>) => {\n fn.apply(history, args);\n window.dispatchEvent(new Event('locationchange'));\n };\n\n // Patch history methods\n history.pushState = wrap(originalPushState);\n history.replaceState = wrap(originalReplaceState);\n\n // Add event listeners\n window.addEventListener('locationchange', update);\n window.addEventListener('popstate', update);\n window.addEventListener('hashchange', update);\n\n // Initialize immediately\n update();\n\n // Clean up on destroy\n destroyRef.onDestroy(() => {\n window.removeEventListener('locationchange', update);\n window.removeEventListener('popstate', update);\n window.removeEventListener('hashchange', update);\n\n // Restore original history methods\n if (originalPushState) history.pushState = originalPushState;\n if (originalReplaceState) history.replaceState = originalReplaceState;\n });\n }\n } catch {\n console.warn(\n 'useCrossURLPathSetter called outside injection context; ' +\n 'URL path synchronization may not be available.'\n );\n }\n\n return [state, setState] as const;\n};\n"],"mappings":";;;;;;;;;;;AAUA,MAAa,wBACX,SACA,SACG,mBAA2B,WAAW,qBAAqB,SAAS,KAAK;;;;;;;AAQ9E,MAAa,yBAAyB,YAAqB;CACzD,MAAM,CAAC,OAAO,YAAY,qBAAqB,SAAS;EACtD,MAAM;EACN,SAAS;EACV,CAAC;CAGF,IAAIA;CACJ,IAAIC;CAGJ,MAAM,eAAe,SAAS,OAAO,SAAS,SAAS;AAGvD,KAAI;EACF,MAAM,aAAa,OAAO,YAAY,EAAE,UAAU,MAAM,CAAC;AAEzD,MAAI,cAAc,OAAO,WAAW,aAAa;AAE/C,uBAAoB,QAAQ;AAC5B,0BAAuB,QAAQ;;;;;;GAO/B,MAAM,QACH,QACA,GAAG,SAA+C;AACjD,OAAG,MAAM,SAAS,KAAK;AACvB,WAAO,cAAc,IAAI,MAAM,iBAAiB,CAAC;;AAIrD,WAAQ,YAAY,KAAK,kBAAkB;AAC3C,WAAQ,eAAe,KAAK,qBAAqB;AAGjD,UAAO,iBAAiB,kBAAkB,OAAO;AACjD,UAAO,iBAAiB,YAAY,OAAO;AAC3C,UAAO,iBAAiB,cAAc,OAAO;AAG7C,WAAQ;AAGR,cAAW,gBAAgB;AACzB,WAAO,oBAAoB,kBAAkB,OAAO;AACpD,WAAO,oBAAoB,YAAY,OAAO;AAC9C,WAAO,oBAAoB,cAAc,OAAO;AAGhD,QAAI,kBAAmB,SAAQ,YAAY;AAC3C,QAAI,qBAAsB,SAAQ,eAAe;KACjD;;SAEE;AACN,UAAQ,KACN,yGAED;;AAGH,QAAO,CAAC,OAAO,SAAS"}