'use client';
import type {
ComponentChildren,
FunctionalComponent,
RenderableProps,
} from 'preact';
import { useEffect, useState } from 'preact/hooks';
import {
CommunicatorProvider,
type CommunicatorProviderProps,
} from './CommunicatorContext';
import {
ConfigurationProvider,
type ConfigurationProviderProps,
} from './ConfigurationContext';
import { DictionariesRecordProvider } from './DictionariesRecordContext';
import {
EditedContentProvider,
useGetEditedContentState,
} from './EditedContentContext';
import {
EditorEnabledProvider,
useEditorEnabled,
useGetEditorEnabledState,
} from './EditorEnabledContext';
import { FocusDictionaryProvider } from './FocusDictionaryContext';
/**
* This component add all the providers needed by the editor.
* It is used to wrap the application, or the editor to work together.
*/
const EditorProvidersWrapper: FunctionalComponent<RenderableProps<{}>> = ({
children,
}) => {
const getEditedContentState = useGetEditedContentState();
useEffect(() => {
getEditedContentState();
}, []);
return (
<DictionariesRecordProvider>
<EditedContentProvider>
<FocusDictionaryProvider>{children}</FocusDictionaryProvider>
</EditedContentProvider>
</DictionariesRecordProvider>
);
};
type FallbackProps = {
fallback: ComponentChildren;
};
/**
* This component check if the editor is enabled to render the editor providers.
*/
const EditorEnabledCheckRenderer: FunctionalComponent<
RenderableProps<FallbackProps>
> = ({ children, fallback }) => {
const getEditorEnabled = useGetEditorEnabledState();
const { enabled } = useEditorEnabled();
useEffect(() => {
if (enabled) return;
// Check if the editor is wrapping the application
getEditorEnabled();
}, [enabled]);
return enabled ? children : fallback;
};
/**
* This component is used to check if the editor is wrapping the application.
* It avoid to send window.postMessage to the application if the editor is not wrapping the application.
*/
const IframeCheckRenderer: FunctionalComponent<
RenderableProps<FallbackProps>
> = ({ children, fallback }) => {
const [isInIframe, setIsInIframe] = useState(false);
useEffect(() => {
setIsInIframe(window.self !== window.top);
}, []);
return isInIframe ? children : fallback;
};
export type EditorProviderProps = CommunicatorProviderProps &
ConfigurationProviderProps;
export const EditorProvider: FunctionalComponent<
RenderableProps<EditorProviderProps>
> = ({ children, configuration, ...props }) => (
<EditorEnabledProvider>
<ConfigurationProvider configuration={configuration}>
<IframeCheckRenderer fallback={children}>
<CommunicatorProvider {...props}>
<EditorEnabledCheckRenderer fallback={children}>
<EditorProvidersWrapper>{children}</EditorProvidersWrapper>
</EditorEnabledCheckRenderer>
</CommunicatorProvider>
</IframeCheckRenderer>
</ConfigurationProvider>
</EditorEnabledProvider>
);