@intlayer/editor-react 9.6.0 → 9.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/dist/cjs/ConfigurationContext.cjs +2 -3
  2. package/dist/cjs/DictionariesRecordContext.cjs +2 -3
  3. package/dist/cjs/EditedContentContext.cjs +8 -5
  4. package/dist/cjs/EditedContentContext.cjs.map +1 -1
  5. package/dist/cjs/EditorEnabledContext.cjs +3 -4
  6. package/dist/cjs/EditorEnabledContext.cjs.map +1 -1
  7. package/dist/cjs/EditorProvider.cjs +2 -3
  8. package/dist/cjs/EditorStateContext.cjs +1 -2
  9. package/dist/cjs/FocusDictionaryContext.cjs +2 -3
  10. package/dist/cjs/index.cjs +21 -14
  11. package/dist/cjs/useCrossFrameState.cjs +2 -3
  12. package/dist/cjs/useCrossURLPathState.cjs +2 -3
  13. package/dist/cjs/useEditedContentPersistence.cjs +2 -3
  14. package/dist/cjs/useEditorLocale.cjs +2 -3
  15. package/dist/cjs/useFocusUnmergedDictionary.cjs +4 -5
  16. package/dist/cjs/useFrameConnectionStatus.cjs +43 -0
  17. package/dist/cjs/useFrameConnectionStatus.cjs.map +1 -0
  18. package/dist/cjs/useFrameReconnection.cjs +62 -0
  19. package/dist/cjs/useFrameReconnection.cjs.map +1 -0
  20. package/dist/esm/ConfigurationContext.mjs +1 -2
  21. package/dist/esm/DictionariesRecordContext.mjs +1 -2
  22. package/dist/esm/EditedContentContext.mjs +8 -5
  23. package/dist/esm/EditedContentContext.mjs.map +1 -1
  24. package/dist/esm/EditorEnabledContext.mjs +3 -4
  25. package/dist/esm/EditorEnabledContext.mjs.map +1 -1
  26. package/dist/esm/EditorProvider.mjs +1 -2
  27. package/dist/esm/EditorStateContext.mjs +1 -2
  28. package/dist/esm/FocusDictionaryContext.mjs +1 -2
  29. package/dist/esm/index.mjs +9 -7
  30. package/dist/esm/useCrossFrameState.mjs +1 -2
  31. package/dist/esm/useCrossURLPathState.mjs +1 -2
  32. package/dist/esm/useEditedContentPersistence.mjs +1 -2
  33. package/dist/esm/useEditorLocale.mjs +1 -2
  34. package/dist/esm/useFocusUnmergedDictionary.mjs +2 -3
  35. package/dist/esm/useFrameConnectionStatus.mjs +41 -0
  36. package/dist/esm/useFrameConnectionStatus.mjs.map +1 -0
  37. package/dist/esm/useFrameReconnection.mjs +59 -0
  38. package/dist/esm/useFrameReconnection.mjs.map +1 -0
  39. package/dist/types/ConfigurationContext.d.ts +0 -2
  40. package/dist/types/ConfigurationContext.d.ts.map +1 -1
  41. package/dist/types/DictionariesRecordContext.d.ts +0 -2
  42. package/dist/types/DictionariesRecordContext.d.ts.map +1 -1
  43. package/dist/types/EditedContentContext.d.ts +4 -2
  44. package/dist/types/EditedContentContext.d.ts.map +1 -1
  45. package/dist/types/EditorEnabledContext.d.ts +0 -2
  46. package/dist/types/EditorEnabledContext.d.ts.map +1 -1
  47. package/dist/types/EditorProvider.d.ts +0 -2
  48. package/dist/types/EditorProvider.d.ts.map +1 -1
  49. package/dist/types/EditorStateContext.d.ts +0 -2
  50. package/dist/types/EditorStateContext.d.ts.map +1 -1
  51. package/dist/types/FocusDictionaryContext.d.ts +0 -2
  52. package/dist/types/FocusDictionaryContext.d.ts.map +1 -1
  53. package/dist/types/index.d.ts +4 -2
  54. package/dist/types/useCrossFrameState.d.ts +0 -2
  55. package/dist/types/useCrossFrameState.d.ts.map +1 -1
  56. package/dist/types/useCrossURLPathState.d.ts +0 -2
  57. package/dist/types/useCrossURLPathState.d.ts.map +1 -1
  58. package/dist/types/useEditedContentPersistence.d.ts +0 -2
  59. package/dist/types/useEditedContentPersistence.d.ts.map +1 -1
  60. package/dist/types/useEditorLocale.d.ts +0 -2
  61. package/dist/types/useEditorLocale.d.ts.map +1 -1
  62. package/dist/types/useFocusUnmergedDictionary.d.ts +0 -2
  63. package/dist/types/useFocusUnmergedDictionary.d.ts.map +1 -1
  64. package/dist/types/useFrameConnectionStatus.d.ts +22 -0
  65. package/dist/types/useFrameConnectionStatus.d.ts.map +1 -0
  66. package/dist/types/useFrameReconnection.d.ts +23 -0
  67. package/dist/types/useFrameReconnection.d.ts.map +1 -0
  68. package/package.json +8 -8
@@ -3,7 +3,7 @@
3
3
  import { createContext, useContext } from "react";
4
4
  import { jsx } from "react/jsx-runtime";
5
5
 
6
- //#region src/EditorStateContext.tsx
6
+ // src/EditorStateContext.tsx
7
7
  const EditorStateContext = createContext(null);
8
8
  const EditorStateProvider = ({ children, manager }) => /* @__PURE__ */ jsx(EditorStateContext.Provider, {
9
9
  value: manager,
@@ -11,6 +11,5 @@ const EditorStateProvider = ({ children, manager }) => /* @__PURE__ */ jsx(Edito
11
11
  });
12
12
  const useEditorStateManager = () => useContext(EditorStateContext);
13
13
 
14
- //#endregion
15
14
  export { EditorStateProvider, useEditorStateManager };
16
15
  //# sourceMappingURL=EditorStateContext.mjs.map
@@ -3,7 +3,7 @@
3
3
  import { useEditorStateManager } from "./EditorStateContext.mjs";
4
4
  import { useCallback, useEffect, useState } from "react";
5
5
 
6
- //#region src/FocusDictionaryContext.tsx
6
+ // src/FocusDictionaryContext.tsx
7
7
  /**
8
8
  * Returns the content focused in the editor, shared with the client frame.
9
9
  */
@@ -23,6 +23,5 @@ const useFocusDictionary = () => {
23
23
  };
24
24
  };
25
25
 
26
- //#endregion
27
26
  export { useFocusDictionary };
28
27
  //# sourceMappingURL=FocusDictionaryContext.mjs.map
@@ -1,15 +1,17 @@
1
- import { EditorStateProvider, useEditorStateManager } from "./EditorStateContext.mjs";
1
+ import { useConfiguration } from "./ConfigurationContext.mjs";
2
2
  import { useDictionariesRecord, useDictionariesRecordActions } from "./DictionariesRecordContext.mjs";
3
- import { useCrossFrameState } from "./useCrossFrameState.mjs";
4
- import { useCrossURLPathState } from "./useCrossURLPathState.mjs";
5
3
  import { useEditedContent, useEditedContentActions } from "./EditedContentContext.mjs";
6
- import { useEditedContentPersistence } from "./useEditedContentPersistence.mjs";
7
- import { useEditorLocale } from "./useEditorLocale.mjs";
8
- import { useConfiguration } from "./ConfigurationContext.mjs";
9
4
  import { useEditorEnabled, useEditorPingClient } from "./EditorEnabledContext.mjs";
10
5
  import { EditorProvider } from "./EditorProvider.mjs";
6
+ import { EditorStateProvider, useEditorStateManager } from "./EditorStateContext.mjs";
11
7
  import { useFocusDictionary } from "./FocusDictionaryContext.mjs";
8
+ import { useCrossFrameState } from "./useCrossFrameState.mjs";
9
+ import { useCrossURLPathState } from "./useCrossURLPathState.mjs";
10
+ import { useEditedContentPersistence } from "./useEditedContentPersistence.mjs";
11
+ import { useEditorLocale } from "./useEditorLocale.mjs";
12
12
  import { useFocusUnmergedDictionary } from "./useFocusUnmergedDictionary.mjs";
13
+ import { FRAME_CONNECTION_TIMEOUT_MS, useFrameConnectionStatus } from "./useFrameConnectionStatus.mjs";
14
+ import { FRAME_RECONNECTION_INTERVAL_MS, isApplicationReachable, useFrameReconnection } from "./useFrameReconnection.mjs";
13
15
  import { MessageKey } from "@intlayer/editor";
14
16
 
15
- export { EditorProvider, EditorStateProvider, MessageKey, useConfiguration, useCrossFrameState, useCrossURLPathState, useDictionariesRecord, useDictionariesRecordActions, useEditedContent, useEditedContentActions, useEditedContentPersistence, useEditorEnabled, useEditorLocale, useEditorPingClient, useEditorStateManager, useFocusDictionary, useFocusUnmergedDictionary };
17
+ export { EditorProvider, EditorStateProvider, FRAME_CONNECTION_TIMEOUT_MS, FRAME_RECONNECTION_INTERVAL_MS, MessageKey, isApplicationReachable, useConfiguration, useCrossFrameState, useCrossURLPathState, useDictionariesRecord, useDictionariesRecordActions, useEditedContent, useEditedContentActions, useEditedContentPersistence, useEditorEnabled, useEditorLocale, useEditorPingClient, useEditorStateManager, useFocusDictionary, useFocusUnmergedDictionary, useFrameConnectionStatus, useFrameReconnection };
@@ -3,7 +3,7 @@
3
3
  import { useEditorStateManager } from "./EditorStateContext.mjs";
4
4
  import { useEffect, useState } from "react";
5
5
 
6
- //#region src/useCrossFrameState.tsx
6
+ // src/useCrossFrameState.tsx
7
7
  /**
8
8
  * Returns the latest value another frame posted under `key`.
9
9
  */
@@ -14,6 +14,5 @@ const useCrossFrameState = (key, initialValue) => {
14
14
  return value;
15
15
  };
16
16
 
17
- //#endregion
18
17
  export { useCrossFrameState };
19
18
  //# sourceMappingURL=useCrossFrameState.mjs.map
@@ -3,12 +3,11 @@
3
3
  import { useCrossFrameState } from "./useCrossFrameState.mjs";
4
4
  import { MessageKey } from "@intlayer/editor";
5
5
 
6
- //#region src/useCrossURLPathState.tsx
6
+ // src/useCrossURLPathState.tsx
7
7
  /**
8
8
  * Returns the path currently displayed by the client application.
9
9
  */
10
10
  const useCrossURLPathState = () => useCrossFrameState(MessageKey.INTLAYER_URL_CHANGE, void 0);
11
11
 
12
- //#endregion
13
12
  export { useCrossURLPathState };
14
13
  //# sourceMappingURL=useCrossURLPathState.mjs.map
@@ -3,7 +3,7 @@
3
3
  import { useEditedContent } from "./EditedContentContext.mjs";
4
4
  import { useEffect } from "react";
5
5
 
6
- //#region src/useEditedContentPersistence.tsx
6
+ // src/useEditedContentPersistence.tsx
7
7
  const EDITED_CONTENT_STORAGE_KEY = "INTLAYER_EDITED_CONTENT_CHANGED";
8
8
  /**
9
9
  * Persists the edited content in localStorage, and restores it on mount so
@@ -27,6 +27,5 @@ const useEditedContentPersistence = () => {
27
27
  }, [editedContent]);
28
28
  };
29
29
 
30
- //#endregion
31
30
  export { useEditedContentPersistence };
32
31
  //# sourceMappingURL=useEditedContentPersistence.mjs.map
@@ -3,7 +3,7 @@
3
3
  import { useEditorStateManager } from "./EditorStateContext.mjs";
4
4
  import { useEffect, useState } from "react";
5
5
 
6
- //#region src/useEditorLocale.tsx
6
+ // src/useEditorLocale.tsx
7
7
  const useEditorLocale = () => {
8
8
  const manager = useEditorStateManager();
9
9
  const [locale, setLocale] = useState(manager?.currentLocale.value);
@@ -16,6 +16,5 @@ const useEditorLocale = () => {
16
16
  return locale;
17
17
  };
18
18
 
19
- //#endregion
20
19
  export { useEditorLocale };
21
20
  //# sourceMappingURL=useEditorLocale.mjs.map
@@ -1,12 +1,12 @@
1
1
  'use client';
2
2
 
3
3
  import { useDictionariesRecord } from "./DictionariesRecordContext.mjs";
4
- import { useEditorLocale } from "./useEditorLocale.mjs";
5
4
  import { useFocusDictionary } from "./FocusDictionaryContext.mjs";
5
+ import { useEditorLocale } from "./useEditorLocale.mjs";
6
6
  import { useMemo } from "react";
7
7
  import { getContentNodeByKeyPath } from "@intlayer/core/dictionaryManipulator";
8
8
 
9
- //#region src/useFocusUnmergedDictionary.tsx
9
+ // src/useFocusUnmergedDictionary.tsx
10
10
  /**
11
11
  * Converts merged keypath format to unmerged format.
12
12
  * Merged: [{type: "translation", key: "fr"}, {type: "object", key: "title"}]
@@ -45,6 +45,5 @@ const useFocusUnmergedDictionary = () => {
45
45
  };
46
46
  };
47
47
 
48
- //#endregion
49
48
  export { useFocusUnmergedDictionary };
50
49
  //# sourceMappingURL=useFocusUnmergedDictionary.mjs.map
@@ -0,0 +1,41 @@
1
+ 'use client';
2
+
3
+ import { useEditorEnabled } from "./EditorEnabledContext.mjs";
4
+ import { useCallback, useEffect, useState } from "react";
5
+
6
+ // src/useFrameConnectionStatus.ts
7
+ /** Delay after a frame load before an unanswered handshake counts as failed. */
8
+ const FRAME_CONNECTION_TIMEOUT_MS = 5e3;
9
+ /**
10
+ * Detects an application frame that never connects to the editor.
11
+ * A frame blocked by the application's framing policy still fires `load` on
12
+ * an empty error page that the parent cannot inspect, so the missing
13
+ * handshake is the only signal available.
14
+ */
15
+ const useFrameConnectionStatus = (timeoutMs = FRAME_CONNECTION_TIMEOUT_MS) => {
16
+ const { enabled } = useEditorEnabled();
17
+ const [loadCount, setLoadCount] = useState(0);
18
+ const [isTimedOut, setIsTimedOut] = useState(false);
19
+ useEffect(() => {
20
+ if (enabled || loadCount === 0) return;
21
+ const timeout = setTimeout(() => setIsTimedOut(true), timeoutMs);
22
+ return () => clearTimeout(timeout);
23
+ }, [
24
+ enabled,
25
+ loadCount,
26
+ timeoutMs
27
+ ]);
28
+ const handleFrameLoad = useCallback(() => {
29
+ setIsTimedOut(false);
30
+ setLoadCount((count) => count + 1);
31
+ }, []);
32
+ const dismiss = useCallback(() => setIsTimedOut(false), []);
33
+ return {
34
+ isConnectionFailed: isTimedOut && !enabled,
35
+ handleFrameLoad,
36
+ dismiss
37
+ };
38
+ };
39
+
40
+ export { FRAME_CONNECTION_TIMEOUT_MS, useFrameConnectionStatus };
41
+ //# sourceMappingURL=useFrameConnectionStatus.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useFrameConnectionStatus.mjs","names":[],"sources":["../../src/useFrameConnectionStatus.ts"],"sourcesContent":["'use client';\n\nimport { useCallback, useEffect, useState } from 'react';\nimport { useEditorEnabled } from './EditorEnabledContext';\n\n/** Delay after a frame load before an unanswered handshake counts as failed. */\nexport const FRAME_CONNECTION_TIMEOUT_MS = 5_000;\n\nexport type FrameConnectionStatus = {\n /**\n * The frame loaded but its client never answered the handshake: the page\n * refused to be framed (CSP `frame-ancestors`, `X-Frame-Options`), or it\n * does not run the editor client.\n */\n isConnectionFailed: boolean;\n /** To call on every `load` event of the application frame. */\n handleFrameLoad: () => void;\n /** Hides the failure until the next load. */\n dismiss: () => void;\n};\n\n/**\n * Detects an application frame that never connects to the editor.\n * A frame blocked by the application's framing policy still fires `load` on\n * an empty error page that the parent cannot inspect, so the missing\n * handshake is the only signal available.\n */\nexport const useFrameConnectionStatus = (\n timeoutMs: number = FRAME_CONNECTION_TIMEOUT_MS\n): FrameConnectionStatus => {\n const { enabled } = useEditorEnabled();\n const [loadCount, setLoadCount] = useState(0);\n const [isTimedOut, setIsTimedOut] = useState(false);\n\n useEffect(() => {\n if (enabled || loadCount === 0) return;\n\n const timeout = setTimeout(() => setIsTimedOut(true), timeoutMs);\n\n return () => clearTimeout(timeout);\n }, [enabled, loadCount, timeoutMs]);\n\n const handleFrameLoad = useCallback(() => {\n setIsTimedOut(false);\n setLoadCount((count) => count + 1);\n }, []);\n\n const dismiss = useCallback(() => setIsTimedOut(false), []);\n\n return {\n isConnectionFailed: isTimedOut && !enabled,\n handleFrameLoad,\n dismiss,\n };\n};\n"],"mappings":";;;;;;;AAMA,MAAa,8BAA8B;;;;;;;AAqB3C,MAAa,4BACX,YAAoB,gCACM;CAC1B,MAAM,EAAE,YAAY,iBAAiB;CACrC,MAAM,CAAC,WAAW,gBAAgB,SAAS,CAAC;CAC5C,MAAM,CAAC,YAAY,iBAAiB,SAAS,KAAK;CAElD,gBAAgB;EACd,IAAI,WAAW,cAAc,GAAG;EAEhC,MAAM,UAAU,iBAAiB,cAAc,IAAI,GAAG,SAAS;EAE/D,aAAa,aAAa,OAAO;CACnC,GAAG;EAAC;EAAS;EAAW;CAAS,CAAC;CAElC,MAAM,kBAAkB,kBAAkB;EACxC,cAAc,KAAK;EACnB,cAAc,UAAU,QAAQ,CAAC;CACnC,GAAG,CAAC,CAAC;CAEL,MAAM,UAAU,kBAAkB,cAAc,KAAK,GAAG,CAAC,CAAC;CAE1D,OAAO;EACL,oBAAoB,cAAc,CAAC;EACnC;EACA;CACF;AACF"}
@@ -0,0 +1,59 @@
1
+ 'use client';
2
+
3
+ import { useEffect, useRef } from "react";
4
+
5
+ // src/useFrameReconnection.ts
6
+ /** Delay between two probes of a disconnected application. */
7
+ const FRAME_RECONNECTION_INTERVAL_MS = 3e3;
8
+ /**
9
+ * Whether the application answers. `no-cors` resolves on any HTTP answer and
10
+ * only rejects on a network failure: the editor origin is rarely in the
11
+ * application's CORS allowlist.
12
+ */
13
+ const isApplicationReachable = (applicationURL) => fetch(applicationURL, {
14
+ method: "HEAD",
15
+ mode: "no-cors",
16
+ cache: "no-store"
17
+ }).then(() => true).catch(() => false);
18
+ /**
19
+ * Reconnects a disconnected application frame: the frame is reloaded once
20
+ * when the application still answers (e.g. it reloaded while its dev server
21
+ * restarted), then each time it answers again after being unreachable.
22
+ */
23
+ const useFrameReconnection = ({ applicationURL, isDisconnected, reloadFrame, intervalMs = FRAME_RECONNECTION_INTERVAL_MS }) => {
24
+ const hasReloadedRef = useRef(false);
25
+ const reloadFrameRef = useRef(reloadFrame);
26
+ reloadFrameRef.current = reloadFrame;
27
+ useEffect(() => {
28
+ if (!isDisconnected) {
29
+ hasReloadedRef.current = false;
30
+ return;
31
+ }
32
+ if (!applicationURL) return;
33
+ let isCancelled = false;
34
+ const probe = async () => {
35
+ const isReachable = await isApplicationReachable(applicationURL);
36
+ if (isCancelled) return;
37
+ if (!isReachable) {
38
+ hasReloadedRef.current = false;
39
+ return;
40
+ }
41
+ if (hasReloadedRef.current) return;
42
+ hasReloadedRef.current = true;
43
+ reloadFrameRef.current();
44
+ };
45
+ probe();
46
+ const interval = setInterval(() => void probe(), intervalMs);
47
+ return () => {
48
+ isCancelled = true;
49
+ clearInterval(interval);
50
+ };
51
+ }, [
52
+ applicationURL,
53
+ isDisconnected,
54
+ intervalMs
55
+ ]);
56
+ };
57
+
58
+ export { FRAME_RECONNECTION_INTERVAL_MS, isApplicationReachable, useFrameReconnection };
59
+ //# sourceMappingURL=useFrameReconnection.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useFrameReconnection.mjs","names":[],"sources":["../../src/useFrameReconnection.ts"],"sourcesContent":["'use client';\n\nimport { useEffect, useRef } from 'react';\n\n/** Delay between two probes of a disconnected application. */\nexport const FRAME_RECONNECTION_INTERVAL_MS = 3_000;\n\n/**\n * Whether the application answers. `no-cors` resolves on any HTTP answer and\n * only rejects on a network failure: the editor origin is rarely in the\n * application's CORS allowlist.\n */\nexport const isApplicationReachable = (\n applicationURL: string\n): Promise<boolean> =>\n fetch(applicationURL, { method: 'HEAD', mode: 'no-cors', cache: 'no-store' })\n .then(() => true)\n .catch(() => false);\n\nexport type FrameReconnectionOptions = {\n applicationURL: string | undefined;\n /** The frame lost (or never got) its connection to the editor. */\n isDisconnected: boolean;\n /** Reloads the application frame. */\n reloadFrame: () => void;\n intervalMs?: number;\n};\n\n/**\n * Reconnects a disconnected application frame: the frame is reloaded once\n * when the application still answers (e.g. it reloaded while its dev server\n * restarted), then each time it answers again after being unreachable.\n */\nexport const useFrameReconnection = ({\n applicationURL,\n isDisconnected,\n reloadFrame,\n intervalMs = FRAME_RECONNECTION_INTERVAL_MS,\n}: FrameReconnectionOptions): void => {\n // Kept across probes: one reload per reachable period, so a frame that\n // never connects (framing refused, editor disabled) is not reloaded forever\n const hasReloadedRef = useRef(false);\n const reloadFrameRef = useRef(reloadFrame);\n reloadFrameRef.current = reloadFrame;\n\n useEffect(() => {\n if (!isDisconnected) {\n hasReloadedRef.current = false;\n return;\n }\n\n if (!applicationURL) return;\n\n let isCancelled = false;\n\n const probe = async () => {\n const isReachable = await isApplicationReachable(applicationURL);\n\n if (isCancelled) return;\n\n if (!isReachable) {\n hasReloadedRef.current = false;\n return;\n }\n\n if (hasReloadedRef.current) return;\n\n hasReloadedRef.current = true;\n reloadFrameRef.current();\n };\n\n void probe();\n const interval = setInterval(() => void probe(), intervalMs);\n\n return () => {\n isCancelled = true;\n clearInterval(interval);\n };\n }, [applicationURL, isDisconnected, intervalMs]);\n};\n"],"mappings":";;;;;;AAKA,MAAa,iCAAiC;;;;;;AAO9C,MAAa,0BACX,mBAEA,MAAM,gBAAgB;CAAE,QAAQ;CAAQ,MAAM;CAAW,OAAO;AAAW,CAAC,CAAC,CAC1E,WAAW,IAAI,CAAC,CAChB,YAAY,KAAK;;;;;;AAgBtB,MAAa,wBAAwB,EACnC,gBACA,gBACA,aACA,aAAa,qCACuB;CAGpC,MAAM,iBAAiB,OAAO,KAAK;CACnC,MAAM,iBAAiB,OAAO,WAAW;CACzC,eAAe,UAAU;CAEzB,gBAAgB;EACd,IAAI,CAAC,gBAAgB;GACnB,eAAe,UAAU;GACzB;EACF;EAEA,IAAI,CAAC,gBAAgB;EAErB,IAAI,cAAc;EAElB,MAAM,QAAQ,YAAY;GACxB,MAAM,cAAc,MAAM,uBAAuB,cAAc;GAE/D,IAAI,aAAa;GAEjB,IAAI,CAAC,aAAa;IAChB,eAAe,UAAU;IACzB;GACF;GAEA,IAAI,eAAe,SAAS;GAE5B,eAAe,UAAU;GACzB,eAAe,QAAQ;EACzB;EAEA,AAAK,MAAM;EACX,MAAM,WAAW,kBAAkB,KAAK,MAAM,GAAG,UAAU;EAE3D,aAAa;GACX,cAAc;GACd,cAAc,QAAQ;EACxB;CACF,GAAG;EAAC;EAAgB;EAAgB;CAAU,CAAC;AACjD"}
@@ -1,8 +1,6 @@
1
1
  import { IntlayerConfig } from "@intlayer/types/config";
2
- //#region src/ConfigurationContext.d.ts
3
2
  /**
4
3
  * Returns the Intlayer configuration held by the editor state manager.
5
4
  */
6
5
  export declare const useConfiguration: () => IntlayerConfig | undefined;
7
- //#endregion
8
6
  //# sourceMappingURL=ConfigurationContext.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ConfigurationContext.d.ts","names":[],"sources":["../../src/ConfigurationContext.tsx"],"mappings":";;;;;qBASa,wBAAuB"}
1
+ {"version":3,"file":"ConfigurationContext.d.ts","names":[],"sources":["../../src/ConfigurationContext.tsx"],"mappings":";;;;qBASa,wBAAuB"}
@@ -1,6 +1,5 @@
1
1
  import { DictionaryContent, DictionaryContent as DictionaryContent$1 } from "@intlayer/editor";
2
2
  import { Dictionary } from "@intlayer/types/dictionary";
3
- //#region src/DictionariesRecordContext.d.ts
4
3
  type DictionariesRecordStatesContextType = {
5
4
  localeDictionaries: DictionaryContent$1;
6
5
  };
@@ -10,6 +9,5 @@ type DictionariesRecordActionsContextType = {
10
9
  };
11
10
  export declare const useDictionariesRecord: () => DictionariesRecordStatesContextType & DictionariesRecordActionsContextType;
12
11
  export declare const useDictionariesRecordActions: () => DictionariesRecordActionsContextType;
13
- //#endregion
14
12
  export type { DictionaryContent };
15
13
  //# sourceMappingURL=DictionariesRecordContext.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"DictionariesRecordContext.d.ts","names":[],"sources":["../../src/DictionariesRecordContext.tsx"],"mappings":";;;KASK;EACH,oBAAoB;;KAEjB;EACH,wBAAwB,OAAO;EAC/B,sBAAsB,YAAY;;qBAGvB,6BAA4B,sCACvC;qBAgCW,oCACP"}
1
+ {"version":3,"file":"DictionariesRecordContext.d.ts","names":["DictionaryContent"],"sources":["../../src/DictionariesRecordContext.tsx"],"mappings":";;KASK;EACH,oBAAoBA;;KAEjB;EACH,wBAAwB,OAAOA;EAC/B,sBAAsB,YAAY;;qBAGvB,6BAA4B,sCACvC;qBAgCW,oCACP"}
@@ -1,7 +1,6 @@
1
1
  import { DictionaryContent, DictionaryContent as DictionaryContent$1 } from "@intlayer/editor";
2
2
  import { ContentNode, Dictionary, LocalDictionaryId } from "@intlayer/types/dictionary";
3
3
  import { KeyPath } from "@intlayer/types/keyPath";
4
- //#region src/EditedContentContext.d.ts
5
4
  type EditedContentActionsContextType = {
6
5
  setEditedContentState: (editedContent: DictionaryContent$1) => void;
7
6
  setEditedDictionary: (dict: Dictionary) => void;
@@ -12,6 +11,10 @@ type EditedContentActionsContextType = {
12
11
  restoreEditedContent: (localDictionaryId: LocalDictionaryId) => void;
13
12
  getEditedContentValue: (localDictionaryIdOrKey: LocalDictionaryId | Dictionary['key'] | string, keyPath: KeyPath[]) => ContentNode | undefined;
14
13
  };
14
+ /**
15
+ * Returns the edited content actions. Memoized on the manager so they can be
16
+ * used as effect dependencies without a compiler.
17
+ */
15
18
  export declare const useEditedContentActions: () => EditedContentActionsContextType;
16
19
  export declare const useEditedContent: () => {
17
20
  setEditedContentState: (editedContent: DictionaryContent$1) => void;
@@ -24,6 +27,5 @@ export declare const useEditedContent: () => {
24
27
  getEditedContentValue: (localDictionaryIdOrKey: LocalDictionaryId | Dictionary['key'] | string, keyPath: KeyPath[]) => ContentNode | undefined;
25
28
  editedContent: DictionaryContent$1;
26
29
  };
27
- //#endregion
28
30
  export type { DictionaryContent };
29
31
  //# sourceMappingURL=EditedContentContext.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"EditedContentContext.d.ts","names":[],"sources":["../../src/EditedContentContext.tsx"],"mappings":";;;;KAcK;EACH,wBAAwB,eAAe;EACvC,sBAAsB,MAAM;EAC5B,mBACE,mBAAmB,mBACnB,UAAU;EAEZ,mBACE,mBAAmB,mBACnB,UAAU,kBACV,UAAU,WACV;EAEF,sBACE,mBAAmB,mBACnB,QAAQ,gBACR,UAAU;EAEZ,sBACE,mBAAmB,mBACnB,SAAS;EAEX,uBAAuB,mBAAmB;EAC1C,wBACE,wBAAwB,oBAAoB,4BAC5C,SAAS,cACN;;qBAGM,+BAA8B;qBAwB9B;EApDY,wBAAC,eAAe;EAClB,sBAAC,MAAM;EACV,mBAChB,mBAAmB,mBACnB,UAAU;EAEM,mBAChB,mBAAmB,mBACnB,UAAU,kBACV,UAAU,WACV;EAEmB,sBACnB,mBAAmB,mBACnB,QAAQ,gBACR,UAAU;EAES,sBACnB,mBAAmB,mBACnB,SAAS;EAEW,uBAAC,mBAAmB;EACnB,wBACrB,wBAAwB,oBAAoB,4BAC5C,SAAS,cACN"}
1
+ {"version":3,"file":"EditedContentContext.d.ts","names":["DictionaryContent"],"sources":["../../src/EditedContentContext.tsx"],"mappings":";;;KAcK;EACH,wBAAwB,eAAeA;EACvC,sBAAsB,MAAM;EAC5B,mBACE,mBAAmB,mBACnB,UAAU;EAEZ,mBACE,mBAAmB,mBACnB,UAAU,kBACV,UAAU,WACV;EAEF,sBACE,mBAAmB,mBACnB,QAAQ,gBACR,UAAU;EAEZ,sBACE,mBAAmB,mBACnB,SAAS;EAEX,uBAAuB,mBAAmB;EAC1C,wBACE,wBAAwB,oBAAoB,4BAC5C,SAAS,cACN;;;;;;qBAOM,+BAA8B;qBA2B9B;EA3DY,wBAAC,eAAeA;EAClB,sBAAC,MAAM;EACV,mBAChB,mBAAmB,mBACnB,UAAU;EAEM,mBAChB,mBAAmB,mBACnB,UAAU,kBACV,UAAU,WACV;EAEmB,sBACnB,mBAAmB,mBACnB,QAAQ,gBACR,UAAU;EAES,sBACnB,mBAAmB,mBACnB,SAAS;EAEW,uBAAC,mBAAmB;EACnB,wBACrB,wBAAwB,oBAAoB,4BAC5C,SAAS,cACN"}
@@ -1,4 +1,3 @@
1
- //#region src/EditorEnabledContext.d.ts
2
1
  export type EditorEnabledStateProps = {
3
2
  enabled: boolean;
4
3
  };
@@ -12,5 +11,4 @@ export declare const useEditorEnabled: () => EditorEnabledStateProps;
12
11
  * Use this as the onClick for an "Enable Editor" / reconnect button.
13
12
  */
14
13
  export declare const useEditorPingClient: () => (() => void);
15
- //#endregion
16
14
  //# sourceMappingURL=EditorEnabledContext.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"EditorEnabledContext.d.ts","names":[],"sources":["../../src/EditorEnabledContext.tsx"],"mappings":";YAKY;EACV;;;;;;qBAOW,wBAAuB;;;;;qBAsBvB"}
1
+ {"version":3,"file":"EditorEnabledContext.d.ts","names":[],"sources":["../../src/EditorEnabledContext.tsx"],"mappings":"A,YAKY;EACV;;;;;;qBAOW,wBAAuB;;;;;qBAsBvB"}
@@ -1,6 +1,5 @@
1
1
  import { IntlayerConfig } from "@intlayer/types/config";
2
2
  import { FC, PropsWithChildren } from "react";
3
- //#region src/EditorProvider.d.ts
4
3
  export type EditorProviderProps = {
5
4
  configuration: Pick<IntlayerConfig, 'editor' | 'internationalization'>;
6
5
  postMessage: (data: any) => void;
@@ -11,5 +10,4 @@ export type EditorProviderProps = {
11
10
  * provides it to all descendants, and registers the Lit web components.
12
11
  */
13
12
  export declare const EditorProvider: FC<PropsWithChildren<EditorProviderProps>>;
14
- //#endregion
15
13
  //# sourceMappingURL=EditorProvider.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"EditorProvider.d.ts","names":[],"sources":["../../src/EditorProvider.tsx"],"mappings":";;;YAYY;EACV,eAAe,KAAK;EACpB,cAAc;EACd;;;;;;qBAOW,gBAAgB,GAAG,kBAAkB"}
1
+ {"version":3,"file":"EditorProvider.d.ts","names":[],"sources":["../../src/EditorProvider.tsx"],"mappings":";;YAYY;EACV,eAAe,KAAK;EACpB,cAAc;EACd;;;;;;qBAOW,gBAAgB,GAAG,kBAAkB"}
@@ -1,9 +1,7 @@
1
1
  import { EditorStateManager } from "@intlayer/editor";
2
2
  import { FC, PropsWithChildren } from "react";
3
- //#region src/EditorStateContext.d.ts
4
3
  export declare const EditorStateProvider: FC<PropsWithChildren<{
5
4
  manager: EditorStateManager;
6
5
  }>>;
7
6
  export declare const useEditorStateManager: () => EditorStateManager | null;
8
- //#endregion
9
7
  //# sourceMappingURL=EditorStateContext.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"EditorStateContext.d.ts","names":[],"sources":["../../src/EditorStateContext.tsx"],"mappings":";;;qBAYa,qBAAqB,GAChC;EAAoB,SAAS;;qBAOlB,6BAA4B"}
1
+ {"version":3,"file":"EditorStateContext.d.ts","names":[],"sources":["../../src/EditorStateContext.tsx"],"mappings":";;qBAYa,qBAAqB,GAChC;EAAoB,SAAS;;qBAOlB,6BAA4B"}
@@ -1,6 +1,5 @@
1
1
  import { CrossFrameStateAction, FileContent, FileContent as FileContent$1 } from "@intlayer/editor";
2
2
  import { KeyPath } from "@intlayer/types/keyPath";
3
- //#region src/FocusDictionaryContext.d.ts
4
3
  export type FocusDictionaryActions = {
5
4
  /** Accepts a value or an updater — `setFocusedContent(prev => …)`. */
6
5
  setFocusedContent: (value: CrossFrameStateAction<FileContent$1 | null>) => void;
@@ -12,6 +11,5 @@ export type FocusDictionaryActions = {
12
11
  export declare const useFocusDictionary: () => FocusDictionaryActions & {
13
12
  focusedContent: FileContent$1 | null;
14
13
  };
15
- //#endregion
16
14
  export type { FileContent };
17
15
  //# sourceMappingURL=FocusDictionaryContext.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"FocusDictionaryContext.d.ts","names":[],"sources":["../../src/FocusDictionaryContext.tsx"],"mappings":";;;YASY;;EAEV,oBAAoB,OAAO,sBAAsB;EACjD,2BAA2B,SAAS;;;;;qBAMzB,0BAAyB;EACpC,gBAAgB"}
1
+ {"version":3,"file":"FocusDictionaryContext.d.ts","names":["FileContent"],"sources":["../../src/FocusDictionaryContext.tsx"],"mappings":";;YASY;;EAEV,oBAAoB,OAAO,sBAAsBA;EACjD,2BAA2B,SAAS;;;;;qBAMzB,0BAAyB;EACpC,gBAAgBA"}
@@ -1,5 +1,5 @@
1
1
  import { useConfiguration } from "./ConfigurationContext.js";
2
- import { DictionaryContent as DictionaryContent$1, useDictionariesRecord, useDictionariesRecordActions } from "./DictionariesRecordContext.js";
2
+ import { DictionaryContent, useDictionariesRecord, useDictionariesRecordActions } from "./DictionariesRecordContext.js";
3
3
  import { useEditedContent, useEditedContentActions } from "./EditedContentContext.js";
4
4
  import { EditorEnabledStateProps, useEditorEnabled, useEditorPingClient } from "./EditorEnabledContext.js";
5
5
  import { EditorProvider, EditorProviderProps } from "./EditorProvider.js";
@@ -10,5 +10,7 @@ import { useCrossURLPathState } from "./useCrossURLPathState.js";
10
10
  import { useEditedContentPersistence } from "./useEditedContentPersistence.js";
11
11
  import { useEditorLocale } from "./useEditorLocale.js";
12
12
  import { useFocusUnmergedDictionary } from "./useFocusUnmergedDictionary.js";
13
+ import { FRAME_CONNECTION_TIMEOUT_MS, FrameConnectionStatus, useFrameConnectionStatus } from "./useFrameConnectionStatus.js";
14
+ import { FRAME_RECONNECTION_INTERVAL_MS, FrameReconnectionOptions, isApplicationReachable, useFrameReconnection } from "./useFrameReconnection.js";
13
15
  import { MessageKey } from "@intlayer/editor";
14
- export { EditorEnabledStateProps, EditorProvider, EditorProviderProps, EditorStateProvider, type FileContent, FocusDictionaryActions, MessageKey, useConfiguration, useCrossFrameState, useCrossURLPathState, useDictionariesRecord, useDictionariesRecordActions, useEditedContent, useEditedContentActions, useEditedContentPersistence, useEditorEnabled, useEditorLocale, useEditorPingClient, useEditorStateManager, useFocusDictionary, useFocusUnmergedDictionary };
16
+ export { type DictionaryContent, EditorEnabledStateProps, EditorProvider, EditorProviderProps, EditorStateProvider, FRAME_CONNECTION_TIMEOUT_MS, FRAME_RECONNECTION_INTERVAL_MS, type FileContent, FocusDictionaryActions, FrameConnectionStatus, FrameReconnectionOptions, MessageKey, isApplicationReachable, useConfiguration, useCrossFrameState, useCrossURLPathState, useDictionariesRecord, useDictionariesRecordActions, useEditedContent, useEditedContentActions, useEditedContentPersistence, useEditorEnabled, useEditorLocale, useEditorPingClient, useEditorStateManager, useFocusDictionary, useFocusUnmergedDictionary, useFrameConnectionStatus, useFrameReconnection };
@@ -1,8 +1,6 @@
1
1
  import { MessageKey } from "@intlayer/editor";
2
- //#region src/useCrossFrameState.d.ts
3
2
  /**
4
3
  * Returns the latest value another frame posted under `key`.
5
4
  */
6
5
  export declare const useCrossFrameState: <Value>(key: `${MessageKey}`, initialValue: Value) => Value;
7
- //#endregion
8
6
  //# sourceMappingURL=useCrossFrameState.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useCrossFrameState.d.ts","names":[],"sources":["../../src/useCrossFrameState.tsx"],"mappings":";;;;;qBASa,qBAAsB,OAAK,QAC9B,cAAY,cACN,UACb"}
1
+ {"version":3,"file":"useCrossFrameState.d.ts","names":[],"sources":["../../src/useCrossFrameState.tsx"],"mappings":";;;;qBASa,qBAAsB,OAAK,QAC9B,cAAY,cACN,UACb"}
@@ -1,7 +1,5 @@
1
- //#region src/useCrossURLPathState.d.ts
2
1
  /**
3
2
  * Returns the path currently displayed by the client application.
4
3
  */
5
4
  export declare const useCrossURLPathState: () => string | undefined;
6
- //#endregion
7
5
  //# sourceMappingURL=useCrossURLPathState.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useCrossURLPathState.d.ts","names":[],"sources":["../../src/useCrossURLPathState.tsx"],"mappings":";;;;qBAQa"}
1
+ {"version":3,"file":"useCrossURLPathState.d.ts","names":[],"sources":["../../src/useCrossURLPathState.tsx"],"mappings":";;;qBAQa"}
@@ -1,8 +1,6 @@
1
- //#region src/useEditedContentPersistence.d.ts
2
1
  /**
3
2
  * Persists the edited content in localStorage, and restores it on mount so
4
3
  * unsaved edits survive a page reload.
5
4
  */
6
5
  export declare const useEditedContentPersistence: () => void;
7
- //#endregion
8
6
  //# sourceMappingURL=useEditedContentPersistence.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useEditedContentPersistence.d.ts","names":[],"sources":["../../src/useEditedContentPersistence.tsx"],"mappings":";;;;;qBAWa"}
1
+ {"version":3,"file":"useEditedContentPersistence.d.ts","names":[],"sources":["../../src/useEditedContentPersistence.tsx"],"mappings":";;;;qBAWa"}
@@ -1,5 +1,3 @@
1
1
  import { Locale } from "@intlayer/types/allLocales";
2
- //#region src/useEditorLocale.d.ts
3
2
  export declare const useEditorLocale: () => Locale | undefined;
4
- //#endregion
5
3
  //# sourceMappingURL=useEditorLocale.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useEditorLocale.d.ts","names":[],"sources":["../../src/useEditorLocale.tsx"],"mappings":";;qBAMa,uBAAsB"}
1
+ {"version":3,"file":"useEditorLocale.d.ts","names":[],"sources":["../../src/useEditorLocale.tsx"],"mappings":";qBAMa,uBAAsB"}
@@ -1,10 +1,8 @@
1
1
  import { FileContent } from "./FocusDictionaryContext.js";
2
2
  import { KeyPath } from "@intlayer/types/keyPath";
3
- //#region src/useFocusUnmergedDictionary.d.ts
4
3
  export declare const useFocusUnmergedDictionary: () => {
5
4
  focusedContent: FileContent;
6
5
  setFocusedContent: (value: import("@intlayer/editor").CrossFrameStateAction<FileContent | null>) => void;
7
6
  setFocusedContentKeyPath: (keyPath: KeyPath[]) => void;
8
7
  };
9
- //#endregion
10
8
  //# sourceMappingURL=useFocusUnmergedDictionary.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useFocusUnmergedDictionary.d.ts","names":[],"sources":["../../src/useFocusUnmergedDictionary.tsx"],"mappings":";;;qBA8Ca"}
1
+ {"version":3,"file":"useFocusUnmergedDictionary.d.ts","names":[],"sources":["../../src/useFocusUnmergedDictionary.tsx"],"mappings":";;qBA8Ca"}
@@ -0,0 +1,22 @@
1
+ /** Delay after a frame load before an unanswered handshake counts as failed. */
2
+ export declare const FRAME_CONNECTION_TIMEOUT_MS = 5000;
3
+ export type FrameConnectionStatus = {
4
+ /**
5
+ * The frame loaded but its client never answered the handshake: the page
6
+ * refused to be framed (CSP `frame-ancestors`, `X-Frame-Options`), or it
7
+ * does not run the editor client.
8
+ */
9
+ isConnectionFailed: boolean;
10
+ /** To call on every `load` event of the application frame. */
11
+ handleFrameLoad: () => void;
12
+ /** Hides the failure until the next load. */
13
+ dismiss: () => void;
14
+ };
15
+ /**
16
+ * Detects an application frame that never connects to the editor.
17
+ * A frame blocked by the application's framing policy still fires `load` on
18
+ * an empty error page that the parent cannot inspect, so the missing
19
+ * handshake is the only signal available.
20
+ */
21
+ export declare const useFrameConnectionStatus: (timeoutMs?: number) => FrameConnectionStatus;
22
+ //# sourceMappingURL=useFrameConnectionStatus.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useFrameConnectionStatus.d.ts","names":[],"sources":["../../src/useFrameConnectionStatus.ts"],"mappings":";qBAMa;YAED;;;;;;EAMV;;EAEA;;EAEA;;;;;;;;qBASW,2BAAwB,uBAElC"}
@@ -0,0 +1,23 @@
1
+ /** Delay between two probes of a disconnected application. */
2
+ export declare const FRAME_RECONNECTION_INTERVAL_MS = 3000;
3
+ /**
4
+ * Whether the application answers. `no-cors` resolves on any HTTP answer and
5
+ * only rejects on a network failure: the editor origin is rarely in the
6
+ * application's CORS allowlist.
7
+ */
8
+ export declare const isApplicationReachable: (applicationURL: string) => Promise<boolean>;
9
+ export type FrameReconnectionOptions = {
10
+ applicationURL: string | undefined;
11
+ /** The frame lost (or never got) its connection to the editor. */
12
+ isDisconnected: boolean;
13
+ /** Reloads the application frame. */
14
+ reloadFrame: () => void;
15
+ intervalMs?: number;
16
+ };
17
+ /**
18
+ * Reconnects a disconnected application frame: the frame is reloaded once
19
+ * when the application still answers (e.g. it reloaded while its dev server
20
+ * restarted), then each time it answers again after being unreachable.
21
+ */
22
+ export declare const useFrameReconnection: ({ applicationURL, isDisconnected, reloadFrame, intervalMs }: FrameReconnectionOptions) => void;
23
+ //# sourceMappingURL=useFrameReconnection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useFrameReconnection.d.ts","names":[],"sources":["../../src/useFrameReconnection.ts"],"mappings":";qBAKa;;;;;;qBAOA,yBAAsB,2BAEhC;YAKS;EACV;;EAEA;;EAEA;EACA;;;;;;;qBAQW,yBAAoB,gBAAA,gBAAA,aAAA,cAK9B"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intlayer/editor-react",
3
- "version": "9.6.0",
3
+ "version": "9.7.1",
4
4
  "private": false,
5
5
  "description": "Provides the states, contexts, hooks and components to interact with the Intlayer editor for a React application",
6
6
  "keywords": [
@@ -71,13 +71,13 @@
71
71
  "typecheck": "tsc --noEmit --project tsconfig.types.json"
72
72
  },
73
73
  "dependencies": {
74
- "@intlayer/config": "9.6.0",
75
- "@intlayer/core": "9.6.0",
76
- "@intlayer/editor": "9.6.0",
77
- "@intlayer/types": "9.6.0"
74
+ "@intlayer/config": "9.7.1",
75
+ "@intlayer/core": "9.7.1",
76
+ "@intlayer/editor": "9.7.1",
77
+ "@intlayer/types": "9.7.1"
78
78
  },
79
79
  "devDependencies": {
80
- "@types/node": "^22",
80
+ "@types/node": "^26",
81
81
  "@types/react": ">=16.0.0",
82
82
  "@types/react-dom": ">=16.0.0",
83
83
  "@utils/ts-config": "1.0.4",
@@ -86,9 +86,9 @@
86
86
  "react": ">=16.0.0",
87
87
  "react-dom": ">=16.0.0",
88
88
  "rimraf": "6.1.3",
89
- "tsdown": "0.23.0",
89
+ "tsdown": "0.23.4",
90
90
  "typescript": "7.0.2",
91
- "vitest": "5.0.3"
91
+ "vitest": "5.1.0"
92
92
  },
93
93
  "peerDependencies": {
94
94
  "react": ">=16.0.0",