@intlayer/editor-react 8.3.1 → 8.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/EditorProvider.cjs +1 -1
- package/dist/cjs/EditorProvider.cjs.map +1 -1
- package/dist/cjs/useCrossFrameState.cjs +1 -1
- package/dist/cjs/useCrossFrameState.cjs.map +1 -1
- package/dist/esm/EditorProvider.mjs +1 -1
- package/dist/esm/EditorProvider.mjs.map +1 -1
- package/dist/esm/useCrossFrameState.mjs +1 -1
- package/dist/esm/useCrossFrameState.mjs.map +1 -1
- package/dist/types/EditorProvider.d.ts.map +1 -1
- package/package.json +8 -8
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use client";Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`}),require(`./_virtual/_rolldown/runtime.cjs`);const e=require(`./CommunicatorContext.cjs`),t=require(`./FocusDictionaryContext.cjs`),n=require(`./DictionariesRecordContext.cjs`),r=require(`./ConfigurationContext.cjs`),i=require(`./EditedContentContext.cjs`),a=require(`./EditorEnabledContext.cjs`);let o=require(`react/jsx-runtime`),s=require(`react`);const c=({children:e})=>{let r=i.useGetEditedContentState();return(0,s.useEffect)(()=>{r()},[]),(0,o.jsx)(n.DictionariesRecordProvider,{children:(0,o.jsx)(i.EditedContentProvider,{children:(0,o.jsx)(t.FocusDictionaryProvider,{children:e})})})},l=({children:e,fallback:t})=>{let n=a.useGetEditorEnabledState(),{enabled:r}=a.useEditorEnabled();return(0,s.useEffect)(()=>{r||n()},[r]),r?e:t},u=({children:e,fallback:t})=>{let[n,r]=(0,s.useState)(!1);return(0,s.useEffect)(()=>{r(window.self!==window.top)},[]),n?e:t},d=({children:
|
|
1
|
+
"use client";Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`}),require(`./_virtual/_rolldown/runtime.cjs`);const e=require(`./CommunicatorContext.cjs`),t=require(`./FocusDictionaryContext.cjs`),n=require(`./DictionariesRecordContext.cjs`),r=require(`./ConfigurationContext.cjs`),i=require(`./EditedContentContext.cjs`),a=require(`./EditorEnabledContext.cjs`);let o=require(`react/jsx-runtime`),s=require(`react`);const c=({children:e})=>{let r=i.useGetEditedContentState();return(0,s.useEffect)(()=>{r()},[]),(0,o.jsx)(n.DictionariesRecordProvider,{children:(0,o.jsx)(i.EditedContentProvider,{children:(0,o.jsx)(t.FocusDictionaryProvider,{children:e})})})},l=({children:e,fallback:t})=>{let n=a.useGetEditorEnabledState(),{enabled:r}=a.useEditorEnabled();return(0,s.useEffect)(()=>{r||n()},[r]),r?e:t},u=({children:e,fallback:t})=>{let[n,r]=(0,s.useState)(!1);return(0,s.useEffect)(()=>{r(window.self!==window.top)},[]),n?e:t},d=({children:e,fallback:t})=>(0,o.jsx)(a.EditorEnabledProvider,{children:(0,o.jsx)(u,{fallback:t,children:(0,o.jsx)(l,{fallback:t,children:(0,o.jsx)(c,{children:e})})})}),f=({children:t,configuration:n,postMessage:i,allowedOrigins:a,mode:s})=>(0,o.jsx)(r.ConfigurationProvider,{configuration:n,children:(0,o.jsx)(e.CommunicatorProvider,{postMessage:i,allowedOrigins:a,children:s===`editor`?(0,o.jsx)(c,{children:t}):(0,o.jsx)(d,{fallback:t,children:t})})});exports.EditorProvider=f;
|
|
2
2
|
//# sourceMappingURL=EditorProvider.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EditorProvider.cjs","names":["useGetEditedContentState","DictionariesRecordProvider","EditedContentProvider","FocusDictionaryProvider","useGetEditorEnabledState","useEditorEnabled","EditorEnabledProvider","ConfigurationProvider","CommunicatorProvider"],"sources":["../../src/EditorProvider.tsx"],"sourcesContent":["'use client';\n\nimport {\n type FC,\n type PropsWithChildren,\n type ReactNode,\n useEffect,\n useState,\n} from 'react';\nimport {\n CommunicatorProvider,\n type CommunicatorProviderProps,\n} from './CommunicatorContext';\nimport {\n ConfigurationProvider,\n type ConfigurationProviderProps,\n} from './ConfigurationContext';\nimport { DictionariesRecordProvider } from './DictionariesRecordContext';\nimport {\n EditedContentProvider,\n useGetEditedContentState,\n} from './EditedContentContext';\nimport {\n EditorEnabledProvider,\n useEditorEnabled,\n useGetEditorEnabledState,\n} from './EditorEnabledContext';\nimport { FocusDictionaryProvider } from './FocusDictionaryContext';\n\n/**\n * This component add all the providers needed by the editor.\n * It is used to wrap the application, or the editor to work together.\n */\nconst EditorProvidersWrapper: FC<PropsWithChildren> = ({ children }) => {\n const getEditedContentState = useGetEditedContentState();\n\n useEffect(() => {\n getEditedContentState();\n }, []);\n\n return (\n <DictionariesRecordProvider>\n <EditedContentProvider>\n <FocusDictionaryProvider>{children}</FocusDictionaryProvider>\n </EditedContentProvider>\n </DictionariesRecordProvider>\n );\n};\n\ntype FallbackProps = {\n fallback: ReactNode;\n};\n\n/**\n * This component check if the editor is enabled to render the editor providers.\n */\nconst EditorEnabledCheckRenderer: FC<PropsWithChildren<FallbackProps>> = ({\n children,\n fallback,\n}) => {\n const getEditorEnabled = useGetEditorEnabledState();\n\n const { enabled } = useEditorEnabled();\n\n useEffect(() => {\n if (enabled) return;\n\n // Check if the editor is wrapping the application\n getEditorEnabled();\n }, [enabled]);\n\n return enabled ? children : fallback;\n};\n\n/**\n * This component is used to check if the editor is wrapping the application.\n * It avoid to send window.postMessage to the application if the editor is not wrapping the application.\n */\nconst IframeCheckRenderer: FC<PropsWithChildren<FallbackProps>> = ({\n children,\n fallback,\n}) => {\n const [isInIframe, setIsInIframe] = useState(false);\n\n useEffect(() => {\n setIsInIframe(window.self !== window.top);\n }, []);\n\n return isInIframe ? children : fallback;\n};\n\nexport type EditorProviderProps = CommunicatorProviderProps &\n ConfigurationProviderProps & {\n mode: 'editor' | 'client';\n };\n\
|
|
1
|
+
{"version":3,"file":"EditorProvider.cjs","names":["useGetEditedContentState","DictionariesRecordProvider","EditedContentProvider","FocusDictionaryProvider","useGetEditorEnabledState","useEditorEnabled","EditorEnabledProvider","ConfigurationProvider","CommunicatorProvider"],"sources":["../../src/EditorProvider.tsx"],"sourcesContent":["'use client';\n\nimport {\n type FC,\n type PropsWithChildren,\n type ReactNode,\n useEffect,\n useState,\n} from 'react';\nimport {\n CommunicatorProvider,\n type CommunicatorProviderProps,\n} from './CommunicatorContext';\nimport {\n ConfigurationProvider,\n type ConfigurationProviderProps,\n} from './ConfigurationContext';\nimport { DictionariesRecordProvider } from './DictionariesRecordContext';\nimport {\n EditedContentProvider,\n useGetEditedContentState,\n} from './EditedContentContext';\nimport {\n EditorEnabledProvider,\n useEditorEnabled,\n useGetEditorEnabledState,\n} from './EditorEnabledContext';\nimport { FocusDictionaryProvider } from './FocusDictionaryContext';\n\n/**\n * This component add all the providers needed by the editor.\n * It is used to wrap the application, or the editor to work together.\n */\nconst EditorProvidersWrapper: FC<PropsWithChildren> = ({ children }) => {\n const getEditedContentState = useGetEditedContentState();\n\n useEffect(() => {\n getEditedContentState();\n }, []);\n\n return (\n <DictionariesRecordProvider>\n <EditedContentProvider>\n <FocusDictionaryProvider>{children}</FocusDictionaryProvider>\n </EditedContentProvider>\n </DictionariesRecordProvider>\n );\n};\n\ntype FallbackProps = {\n fallback: ReactNode;\n};\n\n/**\n * This component check if the editor is enabled to render the editor providers.\n */\nconst EditorEnabledCheckRenderer: FC<PropsWithChildren<FallbackProps>> = ({\n children,\n fallback,\n}) => {\n const getEditorEnabled = useGetEditorEnabledState();\n\n const { enabled } = useEditorEnabled();\n\n useEffect(() => {\n if (enabled) return;\n\n // Check if the editor is wrapping the application\n getEditorEnabled();\n }, [enabled]);\n\n return enabled ? children : fallback;\n};\n\n/**\n * This component is used to check if the editor is wrapping the application.\n * It avoid to send window.postMessage to the application if the editor is not wrapping the application.\n */\nconst IframeCheckRenderer: FC<PropsWithChildren<FallbackProps>> = ({\n children,\n fallback,\n}) => {\n const [isInIframe, setIsInIframe] = useState(false);\n\n useEffect(() => {\n setIsInIframe(window.self !== window.top);\n }, []);\n\n return isInIframe ? children : fallback;\n};\n\nexport type EditorProviderProps = CommunicatorProviderProps &\n ConfigurationProviderProps & {\n mode: 'editor' | 'client';\n };\n\n/**\n * Inner component for client mode with communicator context available\n */\nconst ClientModeContent: FC<PropsWithChildren<FallbackProps>> = ({\n children,\n fallback,\n}) => (\n <EditorEnabledProvider>\n <IframeCheckRenderer fallback={fallback}>\n <EditorEnabledCheckRenderer fallback={fallback}>\n <EditorProvidersWrapper>{children}</EditorProvidersWrapper>\n </EditorEnabledCheckRenderer>\n </IframeCheckRenderer>\n </EditorEnabledProvider>\n);\n\nexport const EditorProvider: FC<PropsWithChildren<EditorProviderProps>> = ({\n children,\n configuration,\n postMessage,\n allowedOrigins,\n mode,\n}) => (\n <ConfigurationProvider configuration={configuration}>\n <CommunicatorProvider postMessage={postMessage} allowedOrigins={allowedOrigins}>\n {mode === 'editor' ? (\n <EditorProvidersWrapper>{children}</EditorProvidersWrapper>\n ) : (\n <ClientModeContent fallback={children}>\n {children}\n </ClientModeContent>\n )}\n </CommunicatorProvider>\n </ConfigurationProvider>\n);\n"],"mappings":"8aAiCA,MAAM,GAAiD,CAAE,cAAe,CACtE,IAAM,EAAwBA,EAAAA,0BAA0B,CAMxD,OAJA,EAAA,EAAA,eAAgB,CACd,GAAuB,EACtB,EAAE,CAAC,EAGJ,EAAA,EAAA,KAACC,EAAAA,2BAAD,CAAA,UACE,EAAA,EAAA,KAACC,EAAAA,sBAAD,CAAA,UACE,EAAA,EAAA,KAACC,EAAAA,wBAAD,CAA0B,WAAmC,CAAA,CACvC,CAAA,CACG,CAAA,EAW3B,GAAoE,CACxE,WACA,cACI,CACJ,IAAM,EAAmBC,EAAAA,0BAA0B,CAE7C,CAAE,WAAYC,EAAAA,kBAAkB,CAStC,OAPA,EAAA,EAAA,eAAgB,CACV,GAGJ,GAAkB,EACjB,CAAC,EAAQ,CAAC,CAEN,EAAU,EAAW,GAOxB,GAA6D,CACjE,WACA,cACI,CACJ,GAAM,CAAC,EAAY,IAAA,EAAA,EAAA,UAA0B,GAAM,CAMnD,OAJA,EAAA,EAAA,eAAgB,CACd,EAAc,OAAO,OAAS,OAAO,IAAI,EACxC,EAAE,CAAC,CAEC,EAAa,EAAW,GAW3B,GAA2D,CAC/D,WACA,eAEA,EAAA,EAAA,KAACC,EAAAA,sBAAD,CAAA,UACE,EAAA,EAAA,KAAC,EAAD,CAA+B,qBAC7B,EAAA,EAAA,KAAC,EAAD,CAAsC,qBACpC,EAAA,EAAA,KAAC,EAAD,CAAyB,WAAkC,CAAA,CAChC,CAAA,CACT,CAAA,CACA,CAAA,CAGb,GAA8D,CACzE,WACA,gBACA,cACA,iBACA,WAEA,EAAA,EAAA,KAACC,EAAAA,sBAAD,CAAsC,0BACpC,EAAA,EAAA,KAACC,EAAAA,qBAAD,CAAmC,cAA6B,0BAC7D,IAAS,UACR,EAAA,EAAA,KAAC,EAAD,CAAyB,WAAkC,CAAA,EAE3D,EAAA,EAAA,KAAC,EAAD,CAAmB,SAAU,EAC1B,WACiB,CAAA,CAED,CAAA,CACD,CAAA"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use client";Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`}),require(`./_virtual/_rolldown/runtime.cjs`);const e=require(`./CommunicatorContext.cjs`),t=require(`./useCrossFrameMessageListener.cjs`);let n=require(`react`);const r=(e,t)=>typeof e==`function`?e(t):e,i=(i,a,o)=>{let{postMessage:s,senderId:c}=e.useCommunicator(),{emit:l,receive:u}=o??{emit:!0,receive:!0},d=(e,t)=>{let n=r(e,t);return l&&typeof s==`function`&&n!==void 0&&s({type:`${i}/post`,data:n,senderId:c}),n},f=()=>{typeof s==`function`&&s({type:`${i}/post`,data:p,senderId:c})},[p,m]=(0,n.useState)(()=>d(a));return t.useCrossFrameMessageListener(`${i}/post`,u?e=>{m(e)}:void 0),t.useCrossFrameMessageListener(`${i}/get`,l?(e,t)=>{l&&typeof s==`function`&&t!==c&&p!==void 0&&s({type:`${i}/post`,data:p,senderId:c})}:void 0,p),(0,n.useEffect)(()=>{u&&typeof s==`function`&&p===void 0&&s({type:`${i}/get`,senderId:c})},[]),[p,e=>m(t=>d(e,t)),f]};exports.useCrossFrameState=i;
|
|
1
|
+
"use client";Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`}),require(`./_virtual/_rolldown/runtime.cjs`);const e=require(`./CommunicatorContext.cjs`),t=require(`./useCrossFrameMessageListener.cjs`);let n=require(`react`);const r=(e,t)=>typeof e==`function`?e(t):e,i=(i,a,o)=>{let{postMessage:s,senderId:c}=e.useCommunicator()??{},{emit:l,receive:u}=o??{emit:!0,receive:!0},d=(e,t)=>{let n=r(e,t);return l&&typeof s==`function`&&n!==void 0&&s({type:`${i}/post`,data:n,senderId:c}),n},f=()=>{typeof s==`function`&&s({type:`${i}/post`,data:p,senderId:c})},[p,m]=(0,n.useState)(()=>d(a));return t.useCrossFrameMessageListener(`${i}/post`,u?e=>{m(e)}:void 0),t.useCrossFrameMessageListener(`${i}/get`,l?(e,t)=>{l&&typeof s==`function`&&t!==c&&p!==void 0&&s({type:`${i}/post`,data:p,senderId:c})}:void 0,p),(0,n.useEffect)(()=>{u&&typeof s==`function`&&p===void 0&&s({type:`${i}/get`,senderId:c})},[]),[p,e=>m(t=>d(e,t)),f]};exports.useCrossFrameState=i;
|
|
2
2
|
//# sourceMappingURL=useCrossFrameState.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useCrossFrameState.cjs","names":["useCommunicator"],"sources":["../../src/useCrossFrameState.tsx"],"sourcesContent":["'use client';\n\nimport type { MessageKey } from '@intlayer/editor';\nimport { type Dispatch, type SetStateAction, useEffect, useState } from 'react';\nimport { useCommunicator } from './CommunicatorContext';\nimport { useCrossFrameMessageListener } from './useCrossFrameMessageListener';\n\nexport type CrossFrameStateOptions = {\n emit?: boolean;\n receive?: boolean;\n};\n\nconst resolveState = <S,>(state?: SetStateAction<S>, prevState?: S): S =>\n typeof state === 'function'\n ? (state as (prevState?: S) => S)(prevState)\n : (state as S);\n\n/**\n * Configuration options for `useCrossFrameState`.\n * @typedef {Object} CrossFrameStateOptions\n * @property {boolean} [emit=true] - Whether to broadcast state changes to other instances.\n * @property {boolean} [receive=true] - Whether to listen for state updates from other instances.\n */\n\n/**\n * useCrossFrameState\n *\n * This React hook synchronizes state across multiple instances (e.g., different iframes or windows).\n * It uses the `postMessage` API to communicate state changes and updates between instances.\n *\n * @template S - The type of the state.\n * @param key - A unique identifier for the state to synchronize.\n * @param initialState - The initial state value or a function to compute it lazily.\n * @param options - Configuration options to control emitting and receiving messages.\n * - `emit` (default: true): Whether to broadcast state changes to other instances.\n * - `receive` (default: true): Whether to listen for state updates from other instances.\n *\n * @returns {[S, Dispatch<SetStateAction<S>>]} An array containing the current state and a setter function.\n */\nexport const useCrossFrameState = <S,>(\n key: `${MessageKey}`,\n initialState?: S | (() => S),\n options?: CrossFrameStateOptions\n): [S, Dispatch<SetStateAction<S>>, typeof postState] => {\n const { postMessage, senderId } = useCommunicator();\n\n const { emit, receive } = options ?? { emit: true, receive: true };\n\n const handleStateChange = (state?: SetStateAction<S>, prevState?: S) => {\n // Initialize state from the provided initial value, if defined\n const resolvedState: S = resolveState(state, prevState);\n\n // Emit the initial state if `emit` is enabled and initial state is defined\n if (\n emit &&\n typeof postMessage === 'function' &&\n typeof resolvedState !== 'undefined'\n ) {\n postMessage({ type: `${key}/post`, data: resolvedState, senderId });\n }\n\n return resolvedState;\n };\n\n const postState = () => {\n if (typeof postMessage !== 'function') return;\n postMessage({ type: `${key}/post`, data: state, senderId });\n };\n\n const [state, setState] = useState<S>(() => handleStateChange(initialState));\n\n /**\n * A wrapper function around the `setState` function to handle messaging efficiently.\n *\n * This approach has several advantages over using an additional `useEffect`:\n * - **Avoid Redundant Re-renders:** By emitting the message directly within the `setState` logic,\n * it prevents the extra render cycle that would be triggered when using `useEffect`.\n * - **Consistency:** Ensures the message is emitted immediately when the state is updated,\n * avoiding potential delays caused by the asynchronous nature of `useEffect`.\n *\n * This function keeps the same API as `setState` and is memoized using `useCallback`\n * to prevent unnecessary re-renders of dependent components.\n *\n * @template S - The type of the state.\n * @param {SetStateAction<S>} valueOrUpdater - The new state or a function to produce it.\n * @returns {void}\n */\n const setStateWrapper: Dispatch<SetStateAction<S>> = (valueOrUpdater) =>\n setState((prevState) => handleStateChange(valueOrUpdater, prevState));\n\n /**\n * Listen for messages with the specified key and update the state accordingly.\n */\n useCrossFrameMessageListener<S>(\n `${key}/post`,\n // Only activate the state listener if the `receive` option is true\n receive\n ? (data) => {\n setState(data);\n }\n : undefined\n );\n\n const onGetMessage = (_: unknown, originSenderId?: string) => {\n if (!emit) return;\n if (typeof postMessage !== 'function') return;\n if (originSenderId === senderId) return;\n if (typeof state === 'undefined') return;\n\n postMessage({ type: `${key}/post`, data: state, senderId });\n };\n\n /**\n * Listen for messages request to get the state content and send it back.\n */\n useCrossFrameMessageListener<S>(\n `${key}/get`,\n // Only activate the state listener if the `emit` option is true\n emit ? onGetMessage : undefined,\n state // Revalidate the listener if the state changes\n );\n\n useEffect(() => {\n // If the component is mounted and the hook in receive mode,\n // Request the state from the other instance\n if (\n receive &&\n typeof postMessage === 'function' &&\n typeof state === 'undefined'\n ) {\n postMessage({ type: `${key}/get`, senderId });\n }\n }, []);\n\n // Return the useState state and setter\n return [state, setStateWrapper, postState];\n};\n"],"mappings":"gPAYA,MAAM,GAAoB,EAA2B,IACnD,OAAO,GAAU,WACZ,EAA+B,EAAU,CACzC,EAwBM,GACX,EACA,EACA,IACuD,CACvD,GAAM,CAAE,cAAa,YAAaA,EAAAA,iBAAiB,
|
|
1
|
+
{"version":3,"file":"useCrossFrameState.cjs","names":["useCommunicator"],"sources":["../../src/useCrossFrameState.tsx"],"sourcesContent":["'use client';\n\nimport type { MessageKey } from '@intlayer/editor';\nimport { type Dispatch, type SetStateAction, useEffect, useState } from 'react';\nimport { useCommunicator } from './CommunicatorContext';\nimport { useCrossFrameMessageListener } from './useCrossFrameMessageListener';\n\nexport type CrossFrameStateOptions = {\n emit?: boolean;\n receive?: boolean;\n};\n\nconst resolveState = <S,>(state?: SetStateAction<S>, prevState?: S): S =>\n typeof state === 'function'\n ? (state as (prevState?: S) => S)(prevState)\n : (state as S);\n\n/**\n * Configuration options for `useCrossFrameState`.\n * @typedef {Object} CrossFrameStateOptions\n * @property {boolean} [emit=true] - Whether to broadcast state changes to other instances.\n * @property {boolean} [receive=true] - Whether to listen for state updates from other instances.\n */\n\n/**\n * useCrossFrameState\n *\n * This React hook synchronizes state across multiple instances (e.g., different iframes or windows).\n * It uses the `postMessage` API to communicate state changes and updates between instances.\n *\n * @template S - The type of the state.\n * @param key - A unique identifier for the state to synchronize.\n * @param initialState - The initial state value or a function to compute it lazily.\n * @param options - Configuration options to control emitting and receiving messages.\n * - `emit` (default: true): Whether to broadcast state changes to other instances.\n * - `receive` (default: true): Whether to listen for state updates from other instances.\n *\n * @returns {[S, Dispatch<SetStateAction<S>>]} An array containing the current state and a setter function.\n */\nexport const useCrossFrameState = <S,>(\n key: `${MessageKey}`,\n initialState?: S | (() => S),\n options?: CrossFrameStateOptions\n): [S, Dispatch<SetStateAction<S>>, typeof postState] => {\n const { postMessage, senderId } = useCommunicator() ?? {};\n\n const { emit, receive } = options ?? { emit: true, receive: true };\n\n const handleStateChange = (state?: SetStateAction<S>, prevState?: S) => {\n // Initialize state from the provided initial value, if defined\n const resolvedState: S = resolveState(state, prevState);\n\n // Emit the initial state if `emit` is enabled and initial state is defined\n if (\n emit &&\n typeof postMessage === 'function' &&\n typeof resolvedState !== 'undefined'\n ) {\n postMessage({ type: `${key}/post`, data: resolvedState, senderId });\n }\n\n return resolvedState;\n };\n\n const postState = () => {\n if (typeof postMessage !== 'function') return;\n postMessage({ type: `${key}/post`, data: state, senderId });\n };\n\n const [state, setState] = useState<S>(() => handleStateChange(initialState));\n\n /**\n * A wrapper function around the `setState` function to handle messaging efficiently.\n *\n * This approach has several advantages over using an additional `useEffect`:\n * - **Avoid Redundant Re-renders:** By emitting the message directly within the `setState` logic,\n * it prevents the extra render cycle that would be triggered when using `useEffect`.\n * - **Consistency:** Ensures the message is emitted immediately when the state is updated,\n * avoiding potential delays caused by the asynchronous nature of `useEffect`.\n *\n * This function keeps the same API as `setState` and is memoized using `useCallback`\n * to prevent unnecessary re-renders of dependent components.\n *\n * @template S - The type of the state.\n * @param {SetStateAction<S>} valueOrUpdater - The new state or a function to produce it.\n * @returns {void}\n */\n const setStateWrapper: Dispatch<SetStateAction<S>> = (valueOrUpdater) =>\n setState((prevState) => handleStateChange(valueOrUpdater, prevState));\n\n /**\n * Listen for messages with the specified key and update the state accordingly.\n */\n useCrossFrameMessageListener<S>(\n `${key}/post`,\n // Only activate the state listener if the `receive` option is true\n receive\n ? (data) => {\n setState(data);\n }\n : undefined\n );\n\n const onGetMessage = (_: unknown, originSenderId?: string) => {\n if (!emit) return;\n if (typeof postMessage !== 'function') return;\n if (originSenderId === senderId) return;\n if (typeof state === 'undefined') return;\n\n postMessage({ type: `${key}/post`, data: state, senderId });\n };\n\n /**\n * Listen for messages request to get the state content and send it back.\n */\n useCrossFrameMessageListener<S>(\n `${key}/get`,\n // Only activate the state listener if the `emit` option is true\n emit ? onGetMessage : undefined,\n state // Revalidate the listener if the state changes\n );\n\n useEffect(() => {\n // If the component is mounted and the hook in receive mode,\n // Request the state from the other instance\n if (\n receive &&\n typeof postMessage === 'function' &&\n typeof state === 'undefined'\n ) {\n postMessage({ type: `${key}/get`, senderId });\n }\n }, []);\n\n // Return the useState state and setter\n return [state, setStateWrapper, postState];\n};\n"],"mappings":"gPAYA,MAAM,GAAoB,EAA2B,IACnD,OAAO,GAAU,WACZ,EAA+B,EAAU,CACzC,EAwBM,GACX,EACA,EACA,IACuD,CACvD,GAAM,CAAE,cAAa,YAAaA,EAAAA,iBAAiB,EAAI,EAAE,CAEnD,CAAE,OAAM,WAAY,GAAW,CAAE,KAAM,GAAM,QAAS,GAAM,CAE5D,GAAqB,EAA2B,IAAkB,CAEtE,IAAM,EAAmB,EAAa,EAAO,EAAU,CAWvD,OAPE,GACA,OAAO,GAAgB,YAChB,IAAkB,QAEzB,EAAY,CAAE,KAAM,GAAG,EAAI,OAAQ,KAAM,EAAe,WAAU,CAAC,CAG9D,GAGH,MAAkB,CAClB,OAAO,GAAgB,YAC3B,EAAY,CAAE,KAAM,GAAG,EAAI,OAAQ,KAAM,EAAO,WAAU,CAAC,EAGvD,CAAC,EAAO,IAAA,EAAA,EAAA,cAA8B,EAAkB,EAAa,CAAC,CAkE5E,OA1CA,EAAA,6BACE,GAAG,EAAI,OAEP,EACK,GAAS,CACR,EAAS,EAAK,EAEhB,IAAA,GACL,CAcD,EAAA,6BACE,GAAG,EAAI,MAEP,GAfoB,EAAY,IAA4B,CACvD,GACD,OAAO,GAAgB,YACvB,IAAmB,GACZ,IAAU,QAErB,EAAY,CAAE,KAAM,GAAG,EAAI,OAAQ,KAAM,EAAO,WAAU,CAAC,EASrC,IAAA,GACtB,EACD,EAED,EAAA,EAAA,eAAgB,CAIZ,GACA,OAAO,GAAgB,YAChB,IAAU,QAEjB,EAAY,CAAE,KAAM,GAAG,EAAI,MAAO,WAAU,CAAC,EAE9C,EAAE,CAAC,CAGC,CAAC,EAhD8C,GACpD,EAAU,GAAc,EAAkB,EAAgB,EAAU,CAAC,CA+CvC,EAAU"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use client";import{CommunicatorProvider as e}from"./CommunicatorContext.mjs";import{FocusDictionaryProvider as t}from"./FocusDictionaryContext.mjs";import{DictionariesRecordProvider as n}from"./DictionariesRecordContext.mjs";import{ConfigurationProvider as r}from"./ConfigurationContext.mjs";import{EditedContentProvider as i,useGetEditedContentState as a}from"./EditedContentContext.mjs";import{EditorEnabledProvider as o,useEditorEnabled as s,useGetEditorEnabledState as c}from"./EditorEnabledContext.mjs";import{jsx as l}from"react/jsx-runtime";import{useEffect as u,useState as d}from"react";const f=({children:e})=>{let r=a();return u(()=>{r()},[]),l(n,{children:l(i,{children:l(t,{children:e})})})},p=({children:e,fallback:t})=>{let n=c(),{enabled:r}=s();return u(()=>{r||n()},[r]),r?e:t},m=({children:e,fallback:t})=>{let[n,r]=d(!1);return u(()=>{r(window.self!==window.top)},[]),n?e:t},h=({children:
|
|
1
|
+
"use client";import{CommunicatorProvider as e}from"./CommunicatorContext.mjs";import{FocusDictionaryProvider as t}from"./FocusDictionaryContext.mjs";import{DictionariesRecordProvider as n}from"./DictionariesRecordContext.mjs";import{ConfigurationProvider as r}from"./ConfigurationContext.mjs";import{EditedContentProvider as i,useGetEditedContentState as a}from"./EditedContentContext.mjs";import{EditorEnabledProvider as o,useEditorEnabled as s,useGetEditorEnabledState as c}from"./EditorEnabledContext.mjs";import{jsx as l}from"react/jsx-runtime";import{useEffect as u,useState as d}from"react";const f=({children:e})=>{let r=a();return u(()=>{r()},[]),l(n,{children:l(i,{children:l(t,{children:e})})})},p=({children:e,fallback:t})=>{let n=c(),{enabled:r}=s();return u(()=>{r||n()},[r]),r?e:t},m=({children:e,fallback:t})=>{let[n,r]=d(!1);return u(()=>{r(window.self!==window.top)},[]),n?e:t},h=({children:e,fallback:t})=>l(o,{children:l(m,{fallback:t,children:l(p,{fallback:t,children:l(f,{children:e})})})}),g=({children:t,configuration:n,postMessage:i,allowedOrigins:a,mode:o})=>l(r,{configuration:n,children:l(e,{postMessage:i,allowedOrigins:a,children:o===`editor`?l(f,{children:t}):l(h,{fallback:t,children:t})})});export{g as EditorProvider};
|
|
2
2
|
//# sourceMappingURL=EditorProvider.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EditorProvider.mjs","names":[],"sources":["../../src/EditorProvider.tsx"],"sourcesContent":["'use client';\n\nimport {\n type FC,\n type PropsWithChildren,\n type ReactNode,\n useEffect,\n useState,\n} from 'react';\nimport {\n CommunicatorProvider,\n type CommunicatorProviderProps,\n} from './CommunicatorContext';\nimport {\n ConfigurationProvider,\n type ConfigurationProviderProps,\n} from './ConfigurationContext';\nimport { DictionariesRecordProvider } from './DictionariesRecordContext';\nimport {\n EditedContentProvider,\n useGetEditedContentState,\n} from './EditedContentContext';\nimport {\n EditorEnabledProvider,\n useEditorEnabled,\n useGetEditorEnabledState,\n} from './EditorEnabledContext';\nimport { FocusDictionaryProvider } from './FocusDictionaryContext';\n\n/**\n * This component add all the providers needed by the editor.\n * It is used to wrap the application, or the editor to work together.\n */\nconst EditorProvidersWrapper: FC<PropsWithChildren> = ({ children }) => {\n const getEditedContentState = useGetEditedContentState();\n\n useEffect(() => {\n getEditedContentState();\n }, []);\n\n return (\n <DictionariesRecordProvider>\n <EditedContentProvider>\n <FocusDictionaryProvider>{children}</FocusDictionaryProvider>\n </EditedContentProvider>\n </DictionariesRecordProvider>\n );\n};\n\ntype FallbackProps = {\n fallback: ReactNode;\n};\n\n/**\n * This component check if the editor is enabled to render the editor providers.\n */\nconst EditorEnabledCheckRenderer: FC<PropsWithChildren<FallbackProps>> = ({\n children,\n fallback,\n}) => {\n const getEditorEnabled = useGetEditorEnabledState();\n\n const { enabled } = useEditorEnabled();\n\n useEffect(() => {\n if (enabled) return;\n\n // Check if the editor is wrapping the application\n getEditorEnabled();\n }, [enabled]);\n\n return enabled ? children : fallback;\n};\n\n/**\n * This component is used to check if the editor is wrapping the application.\n * It avoid to send window.postMessage to the application if the editor is not wrapping the application.\n */\nconst IframeCheckRenderer: FC<PropsWithChildren<FallbackProps>> = ({\n children,\n fallback,\n}) => {\n const [isInIframe, setIsInIframe] = useState(false);\n\n useEffect(() => {\n setIsInIframe(window.self !== window.top);\n }, []);\n\n return isInIframe ? children : fallback;\n};\n\nexport type EditorProviderProps = CommunicatorProviderProps &\n ConfigurationProviderProps & {\n mode: 'editor' | 'client';\n };\n\
|
|
1
|
+
{"version":3,"file":"EditorProvider.mjs","names":[],"sources":["../../src/EditorProvider.tsx"],"sourcesContent":["'use client';\n\nimport {\n type FC,\n type PropsWithChildren,\n type ReactNode,\n useEffect,\n useState,\n} from 'react';\nimport {\n CommunicatorProvider,\n type CommunicatorProviderProps,\n} from './CommunicatorContext';\nimport {\n ConfigurationProvider,\n type ConfigurationProviderProps,\n} from './ConfigurationContext';\nimport { DictionariesRecordProvider } from './DictionariesRecordContext';\nimport {\n EditedContentProvider,\n useGetEditedContentState,\n} from './EditedContentContext';\nimport {\n EditorEnabledProvider,\n useEditorEnabled,\n useGetEditorEnabledState,\n} from './EditorEnabledContext';\nimport { FocusDictionaryProvider } from './FocusDictionaryContext';\n\n/**\n * This component add all the providers needed by the editor.\n * It is used to wrap the application, or the editor to work together.\n */\nconst EditorProvidersWrapper: FC<PropsWithChildren> = ({ children }) => {\n const getEditedContentState = useGetEditedContentState();\n\n useEffect(() => {\n getEditedContentState();\n }, []);\n\n return (\n <DictionariesRecordProvider>\n <EditedContentProvider>\n <FocusDictionaryProvider>{children}</FocusDictionaryProvider>\n </EditedContentProvider>\n </DictionariesRecordProvider>\n );\n};\n\ntype FallbackProps = {\n fallback: ReactNode;\n};\n\n/**\n * This component check if the editor is enabled to render the editor providers.\n */\nconst EditorEnabledCheckRenderer: FC<PropsWithChildren<FallbackProps>> = ({\n children,\n fallback,\n}) => {\n const getEditorEnabled = useGetEditorEnabledState();\n\n const { enabled } = useEditorEnabled();\n\n useEffect(() => {\n if (enabled) return;\n\n // Check if the editor is wrapping the application\n getEditorEnabled();\n }, [enabled]);\n\n return enabled ? children : fallback;\n};\n\n/**\n * This component is used to check if the editor is wrapping the application.\n * It avoid to send window.postMessage to the application if the editor is not wrapping the application.\n */\nconst IframeCheckRenderer: FC<PropsWithChildren<FallbackProps>> = ({\n children,\n fallback,\n}) => {\n const [isInIframe, setIsInIframe] = useState(false);\n\n useEffect(() => {\n setIsInIframe(window.self !== window.top);\n }, []);\n\n return isInIframe ? children : fallback;\n};\n\nexport type EditorProviderProps = CommunicatorProviderProps &\n ConfigurationProviderProps & {\n mode: 'editor' | 'client';\n };\n\n/**\n * Inner component for client mode with communicator context available\n */\nconst ClientModeContent: FC<PropsWithChildren<FallbackProps>> = ({\n children,\n fallback,\n}) => (\n <EditorEnabledProvider>\n <IframeCheckRenderer fallback={fallback}>\n <EditorEnabledCheckRenderer fallback={fallback}>\n <EditorProvidersWrapper>{children}</EditorProvidersWrapper>\n </EditorEnabledCheckRenderer>\n </IframeCheckRenderer>\n </EditorEnabledProvider>\n);\n\nexport const EditorProvider: FC<PropsWithChildren<EditorProviderProps>> = ({\n children,\n configuration,\n postMessage,\n allowedOrigins,\n mode,\n}) => (\n <ConfigurationProvider configuration={configuration}>\n <CommunicatorProvider postMessage={postMessage} allowedOrigins={allowedOrigins}>\n {mode === 'editor' ? (\n <EditorProvidersWrapper>{children}</EditorProvidersWrapper>\n ) : (\n <ClientModeContent fallback={children}>\n {children}\n </ClientModeContent>\n )}\n </CommunicatorProvider>\n </ConfigurationProvider>\n);\n"],"mappings":"qlBAiCA,MAAM,GAAiD,CAAE,cAAe,CACtE,IAAM,EAAwB,GAA0B,CAMxD,OAJA,MAAgB,CACd,GAAuB,EACtB,EAAE,CAAC,CAGJ,EAAC,EAAD,CAAA,SACE,EAAC,EAAD,CAAA,SACE,EAAC,EAAD,CAA0B,WAAmC,CAAA,CACvC,CAAA,CACG,CAAA,EAW3B,GAAoE,CACxE,WACA,cACI,CACJ,IAAM,EAAmB,GAA0B,CAE7C,CAAE,WAAY,GAAkB,CAStC,OAPA,MAAgB,CACV,GAGJ,GAAkB,EACjB,CAAC,EAAQ,CAAC,CAEN,EAAU,EAAW,GAOxB,GAA6D,CACjE,WACA,cACI,CACJ,GAAM,CAAC,EAAY,GAAiB,EAAS,GAAM,CAMnD,OAJA,MAAgB,CACd,EAAc,OAAO,OAAS,OAAO,IAAI,EACxC,EAAE,CAAC,CAEC,EAAa,EAAW,GAW3B,GAA2D,CAC/D,WACA,cAEA,EAAC,EAAD,CAAA,SACE,EAAC,EAAD,CAA+B,oBAC7B,EAAC,EAAD,CAAsC,oBACpC,EAAC,EAAD,CAAyB,WAAkC,CAAA,CAChC,CAAA,CACT,CAAA,CACA,CAAA,CAGb,GAA8D,CACzE,WACA,gBACA,cACA,iBACA,UAEA,EAAC,EAAD,CAAsC,yBACpC,EAAC,EAAD,CAAmC,cAA6B,0BAC7D,IAAS,SACR,EAAC,EAAD,CAAyB,WAAkC,CAAA,CAE3D,EAAC,EAAD,CAAmB,SAAU,EAC1B,WACiB,CAAA,CAED,CAAA,CACD,CAAA"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use client";import{useCommunicator as e}from"./CommunicatorContext.mjs";import{useCrossFrameMessageListener as t}from"./useCrossFrameMessageListener.mjs";import{useEffect as n,useState as r}from"react";const i=(e,t)=>typeof e==`function`?e(t):e,a=(a,o,s)=>{let{postMessage:c,senderId:l}=e(),{emit:u,receive:d}=s??{emit:!0,receive:!0},f=(e,t)=>{let n=i(e,t);return u&&typeof c==`function`&&n!==void 0&&c({type:`${a}/post`,data:n,senderId:l}),n},p=()=>{typeof c==`function`&&c({type:`${a}/post`,data:m,senderId:l})},[m,h]=r(()=>f(o));return t(`${a}/post`,d?e=>{h(e)}:void 0),t(`${a}/get`,u?(e,t)=>{u&&typeof c==`function`&&t!==l&&m!==void 0&&c({type:`${a}/post`,data:m,senderId:l})}:void 0,m),n(()=>{d&&typeof c==`function`&&m===void 0&&c({type:`${a}/get`,senderId:l})},[]),[m,e=>h(t=>f(e,t)),p]};export{a as useCrossFrameState};
|
|
1
|
+
"use client";import{useCommunicator as e}from"./CommunicatorContext.mjs";import{useCrossFrameMessageListener as t}from"./useCrossFrameMessageListener.mjs";import{useEffect as n,useState as r}from"react";const i=(e,t)=>typeof e==`function`?e(t):e,a=(a,o,s)=>{let{postMessage:c,senderId:l}=e()??{},{emit:u,receive:d}=s??{emit:!0,receive:!0},f=(e,t)=>{let n=i(e,t);return u&&typeof c==`function`&&n!==void 0&&c({type:`${a}/post`,data:n,senderId:l}),n},p=()=>{typeof c==`function`&&c({type:`${a}/post`,data:m,senderId:l})},[m,h]=r(()=>f(o));return t(`${a}/post`,d?e=>{h(e)}:void 0),t(`${a}/get`,u?(e,t)=>{u&&typeof c==`function`&&t!==l&&m!==void 0&&c({type:`${a}/post`,data:m,senderId:l})}:void 0,m),n(()=>{d&&typeof c==`function`&&m===void 0&&c({type:`${a}/get`,senderId:l})},[]),[m,e=>h(t=>f(e,t)),p]};export{a as useCrossFrameState};
|
|
2
2
|
//# sourceMappingURL=useCrossFrameState.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useCrossFrameState.mjs","names":[],"sources":["../../src/useCrossFrameState.tsx"],"sourcesContent":["'use client';\n\nimport type { MessageKey } from '@intlayer/editor';\nimport { type Dispatch, type SetStateAction, useEffect, useState } from 'react';\nimport { useCommunicator } from './CommunicatorContext';\nimport { useCrossFrameMessageListener } from './useCrossFrameMessageListener';\n\nexport type CrossFrameStateOptions = {\n emit?: boolean;\n receive?: boolean;\n};\n\nconst resolveState = <S,>(state?: SetStateAction<S>, prevState?: S): S =>\n typeof state === 'function'\n ? (state as (prevState?: S) => S)(prevState)\n : (state as S);\n\n/**\n * Configuration options for `useCrossFrameState`.\n * @typedef {Object} CrossFrameStateOptions\n * @property {boolean} [emit=true] - Whether to broadcast state changes to other instances.\n * @property {boolean} [receive=true] - Whether to listen for state updates from other instances.\n */\n\n/**\n * useCrossFrameState\n *\n * This React hook synchronizes state across multiple instances (e.g., different iframes or windows).\n * It uses the `postMessage` API to communicate state changes and updates between instances.\n *\n * @template S - The type of the state.\n * @param key - A unique identifier for the state to synchronize.\n * @param initialState - The initial state value or a function to compute it lazily.\n * @param options - Configuration options to control emitting and receiving messages.\n * - `emit` (default: true): Whether to broadcast state changes to other instances.\n * - `receive` (default: true): Whether to listen for state updates from other instances.\n *\n * @returns {[S, Dispatch<SetStateAction<S>>]} An array containing the current state and a setter function.\n */\nexport const useCrossFrameState = <S,>(\n key: `${MessageKey}`,\n initialState?: S | (() => S),\n options?: CrossFrameStateOptions\n): [S, Dispatch<SetStateAction<S>>, typeof postState] => {\n const { postMessage, senderId } = useCommunicator();\n\n const { emit, receive } = options ?? { emit: true, receive: true };\n\n const handleStateChange = (state?: SetStateAction<S>, prevState?: S) => {\n // Initialize state from the provided initial value, if defined\n const resolvedState: S = resolveState(state, prevState);\n\n // Emit the initial state if `emit` is enabled and initial state is defined\n if (\n emit &&\n typeof postMessage === 'function' &&\n typeof resolvedState !== 'undefined'\n ) {\n postMessage({ type: `${key}/post`, data: resolvedState, senderId });\n }\n\n return resolvedState;\n };\n\n const postState = () => {\n if (typeof postMessage !== 'function') return;\n postMessage({ type: `${key}/post`, data: state, senderId });\n };\n\n const [state, setState] = useState<S>(() => handleStateChange(initialState));\n\n /**\n * A wrapper function around the `setState` function to handle messaging efficiently.\n *\n * This approach has several advantages over using an additional `useEffect`:\n * - **Avoid Redundant Re-renders:** By emitting the message directly within the `setState` logic,\n * it prevents the extra render cycle that would be triggered when using `useEffect`.\n * - **Consistency:** Ensures the message is emitted immediately when the state is updated,\n * avoiding potential delays caused by the asynchronous nature of `useEffect`.\n *\n * This function keeps the same API as `setState` and is memoized using `useCallback`\n * to prevent unnecessary re-renders of dependent components.\n *\n * @template S - The type of the state.\n * @param {SetStateAction<S>} valueOrUpdater - The new state or a function to produce it.\n * @returns {void}\n */\n const setStateWrapper: Dispatch<SetStateAction<S>> = (valueOrUpdater) =>\n setState((prevState) => handleStateChange(valueOrUpdater, prevState));\n\n /**\n * Listen for messages with the specified key and update the state accordingly.\n */\n useCrossFrameMessageListener<S>(\n `${key}/post`,\n // Only activate the state listener if the `receive` option is true\n receive\n ? (data) => {\n setState(data);\n }\n : undefined\n );\n\n const onGetMessage = (_: unknown, originSenderId?: string) => {\n if (!emit) return;\n if (typeof postMessage !== 'function') return;\n if (originSenderId === senderId) return;\n if (typeof state === 'undefined') return;\n\n postMessage({ type: `${key}/post`, data: state, senderId });\n };\n\n /**\n * Listen for messages request to get the state content and send it back.\n */\n useCrossFrameMessageListener<S>(\n `${key}/get`,\n // Only activate the state listener if the `emit` option is true\n emit ? onGetMessage : undefined,\n state // Revalidate the listener if the state changes\n );\n\n useEffect(() => {\n // If the component is mounted and the hook in receive mode,\n // Request the state from the other instance\n if (\n receive &&\n typeof postMessage === 'function' &&\n typeof state === 'undefined'\n ) {\n postMessage({ type: `${key}/get`, senderId });\n }\n }, []);\n\n // Return the useState state and setter\n return [state, setStateWrapper, postState];\n};\n"],"mappings":"2MAYA,MAAM,GAAoB,EAA2B,IACnD,OAAO,GAAU,WACZ,EAA+B,EAAU,CACzC,EAwBM,GACX,EACA,EACA,IACuD,CACvD,GAAM,CAAE,cAAa,YAAa,GAAiB,
|
|
1
|
+
{"version":3,"file":"useCrossFrameState.mjs","names":[],"sources":["../../src/useCrossFrameState.tsx"],"sourcesContent":["'use client';\n\nimport type { MessageKey } from '@intlayer/editor';\nimport { type Dispatch, type SetStateAction, useEffect, useState } from 'react';\nimport { useCommunicator } from './CommunicatorContext';\nimport { useCrossFrameMessageListener } from './useCrossFrameMessageListener';\n\nexport type CrossFrameStateOptions = {\n emit?: boolean;\n receive?: boolean;\n};\n\nconst resolveState = <S,>(state?: SetStateAction<S>, prevState?: S): S =>\n typeof state === 'function'\n ? (state as (prevState?: S) => S)(prevState)\n : (state as S);\n\n/**\n * Configuration options for `useCrossFrameState`.\n * @typedef {Object} CrossFrameStateOptions\n * @property {boolean} [emit=true] - Whether to broadcast state changes to other instances.\n * @property {boolean} [receive=true] - Whether to listen for state updates from other instances.\n */\n\n/**\n * useCrossFrameState\n *\n * This React hook synchronizes state across multiple instances (e.g., different iframes or windows).\n * It uses the `postMessage` API to communicate state changes and updates between instances.\n *\n * @template S - The type of the state.\n * @param key - A unique identifier for the state to synchronize.\n * @param initialState - The initial state value or a function to compute it lazily.\n * @param options - Configuration options to control emitting and receiving messages.\n * - `emit` (default: true): Whether to broadcast state changes to other instances.\n * - `receive` (default: true): Whether to listen for state updates from other instances.\n *\n * @returns {[S, Dispatch<SetStateAction<S>>]} An array containing the current state and a setter function.\n */\nexport const useCrossFrameState = <S,>(\n key: `${MessageKey}`,\n initialState?: S | (() => S),\n options?: CrossFrameStateOptions\n): [S, Dispatch<SetStateAction<S>>, typeof postState] => {\n const { postMessage, senderId } = useCommunicator() ?? {};\n\n const { emit, receive } = options ?? { emit: true, receive: true };\n\n const handleStateChange = (state?: SetStateAction<S>, prevState?: S) => {\n // Initialize state from the provided initial value, if defined\n const resolvedState: S = resolveState(state, prevState);\n\n // Emit the initial state if `emit` is enabled and initial state is defined\n if (\n emit &&\n typeof postMessage === 'function' &&\n typeof resolvedState !== 'undefined'\n ) {\n postMessage({ type: `${key}/post`, data: resolvedState, senderId });\n }\n\n return resolvedState;\n };\n\n const postState = () => {\n if (typeof postMessage !== 'function') return;\n postMessage({ type: `${key}/post`, data: state, senderId });\n };\n\n const [state, setState] = useState<S>(() => handleStateChange(initialState));\n\n /**\n * A wrapper function around the `setState` function to handle messaging efficiently.\n *\n * This approach has several advantages over using an additional `useEffect`:\n * - **Avoid Redundant Re-renders:** By emitting the message directly within the `setState` logic,\n * it prevents the extra render cycle that would be triggered when using `useEffect`.\n * - **Consistency:** Ensures the message is emitted immediately when the state is updated,\n * avoiding potential delays caused by the asynchronous nature of `useEffect`.\n *\n * This function keeps the same API as `setState` and is memoized using `useCallback`\n * to prevent unnecessary re-renders of dependent components.\n *\n * @template S - The type of the state.\n * @param {SetStateAction<S>} valueOrUpdater - The new state or a function to produce it.\n * @returns {void}\n */\n const setStateWrapper: Dispatch<SetStateAction<S>> = (valueOrUpdater) =>\n setState((prevState) => handleStateChange(valueOrUpdater, prevState));\n\n /**\n * Listen for messages with the specified key and update the state accordingly.\n */\n useCrossFrameMessageListener<S>(\n `${key}/post`,\n // Only activate the state listener if the `receive` option is true\n receive\n ? (data) => {\n setState(data);\n }\n : undefined\n );\n\n const onGetMessage = (_: unknown, originSenderId?: string) => {\n if (!emit) return;\n if (typeof postMessage !== 'function') return;\n if (originSenderId === senderId) return;\n if (typeof state === 'undefined') return;\n\n postMessage({ type: `${key}/post`, data: state, senderId });\n };\n\n /**\n * Listen for messages request to get the state content and send it back.\n */\n useCrossFrameMessageListener<S>(\n `${key}/get`,\n // Only activate the state listener if the `emit` option is true\n emit ? onGetMessage : undefined,\n state // Revalidate the listener if the state changes\n );\n\n useEffect(() => {\n // If the component is mounted and the hook in receive mode,\n // Request the state from the other instance\n if (\n receive &&\n typeof postMessage === 'function' &&\n typeof state === 'undefined'\n ) {\n postMessage({ type: `${key}/get`, senderId });\n }\n }, []);\n\n // Return the useState state and setter\n return [state, setStateWrapper, postState];\n};\n"],"mappings":"2MAYA,MAAM,GAAoB,EAA2B,IACnD,OAAO,GAAU,WACZ,EAA+B,EAAU,CACzC,EAwBM,GACX,EACA,EACA,IACuD,CACvD,GAAM,CAAE,cAAa,YAAa,GAAiB,EAAI,EAAE,CAEnD,CAAE,OAAM,WAAY,GAAW,CAAE,KAAM,GAAM,QAAS,GAAM,CAE5D,GAAqB,EAA2B,IAAkB,CAEtE,IAAM,EAAmB,EAAa,EAAO,EAAU,CAWvD,OAPE,GACA,OAAO,GAAgB,YAChB,IAAkB,QAEzB,EAAY,CAAE,KAAM,GAAG,EAAI,OAAQ,KAAM,EAAe,WAAU,CAAC,CAG9D,GAGH,MAAkB,CAClB,OAAO,GAAgB,YAC3B,EAAY,CAAE,KAAM,GAAG,EAAI,OAAQ,KAAM,EAAO,WAAU,CAAC,EAGvD,CAAC,EAAO,GAAY,MAAkB,EAAkB,EAAa,CAAC,CAkE5E,OA1CA,EACE,GAAG,EAAI,OAEP,EACK,GAAS,CACR,EAAS,EAAK,EAEhB,IAAA,GACL,CAcD,EACE,GAAG,EAAI,MAEP,GAfoB,EAAY,IAA4B,CACvD,GACD,OAAO,GAAgB,YACvB,IAAmB,GACZ,IAAU,QAErB,EAAY,CAAE,KAAM,GAAG,EAAI,OAAQ,KAAM,EAAO,WAAU,CAAC,EASrC,IAAA,GACtB,EACD,CAED,MAAgB,CAIZ,GACA,OAAO,GAAgB,YAChB,IAAU,QAEjB,EAAY,CAAE,KAAM,GAAG,EAAI,MAAO,WAAU,CAAC,EAE9C,EAAE,CAAC,CAGC,CAAC,EAhD8C,GACpD,EAAU,GAAc,EAAkB,EAAgB,EAAU,CAAC,CA+CvC,EAAU"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EditorProvider.d.ts","names":[],"sources":["../../src/EditorProvider.tsx"],"mappings":";;;;;KA2FY,mBAAA,GAAsB,yBAAA,GAChC,0BAAA;EACE,IAAA;AAAA;AAAA,
|
|
1
|
+
{"version":3,"file":"EditorProvider.d.ts","names":[],"sources":["../../src/EditorProvider.tsx"],"mappings":";;;;;KA2FY,mBAAA,GAAsB,yBAAA,GAChC,0BAAA;EACE,IAAA;AAAA;AAAA,cAmBS,cAAA,EAAgB,EAAA,CAAG,iBAAA,CAAkB,mBAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@intlayer/editor-react",
|
|
3
|
-
"version": "8.3.
|
|
3
|
+
"version": "8.3.2",
|
|
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": [
|
|
@@ -70,14 +70,14 @@
|
|
|
70
70
|
"typecheck": "tsc --noEmit --project tsconfig.types.json"
|
|
71
71
|
},
|
|
72
72
|
"dependencies": {
|
|
73
|
-
"@intlayer/config": "8.3.
|
|
74
|
-
"@intlayer/core": "8.3.
|
|
75
|
-
"@intlayer/editor": "8.3.
|
|
76
|
-
"@intlayer/types": "8.3.
|
|
77
|
-
"@intlayer/unmerged-dictionaries-entry": "8.3.
|
|
73
|
+
"@intlayer/config": "8.3.2",
|
|
74
|
+
"@intlayer/core": "8.3.2",
|
|
75
|
+
"@intlayer/editor": "8.3.2",
|
|
76
|
+
"@intlayer/types": "8.3.2",
|
|
77
|
+
"@intlayer/unmerged-dictionaries-entry": "8.3.2"
|
|
78
78
|
},
|
|
79
79
|
"devDependencies": {
|
|
80
|
-
"@types/node": "25.
|
|
80
|
+
"@types/node": "25.5.0",
|
|
81
81
|
"@types/react": ">=16.0.0",
|
|
82
82
|
"@types/react-dom": ">=16.0.0",
|
|
83
83
|
"@utils/ts-config": "1.0.4",
|
|
@@ -88,7 +88,7 @@
|
|
|
88
88
|
"rimraf": "6.1.3",
|
|
89
89
|
"tsdown": "0.21.2",
|
|
90
90
|
"typescript": "5.9.3",
|
|
91
|
-
"vitest": "4.0
|
|
91
|
+
"vitest": "4.1.0"
|
|
92
92
|
},
|
|
93
93
|
"peerDependencies": {
|
|
94
94
|
"react": ">=16.0.0",
|