chore: defines ClientFunction pattern
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import type { BaseEditor, BaseOperation } from 'slate'
|
||||
import type { BaseEditor, BaseOperation, Editor } from 'slate'
|
||||
import type { HistoryEditor } from 'slate-history'
|
||||
import type { ReactEditor } from 'slate-react'
|
||||
|
||||
@@ -13,12 +13,20 @@ import { withHistory } from 'slate-history'
|
||||
import { Editable, Slate, withReact } from 'slate-react'
|
||||
|
||||
import type { FormFieldBase } from '../../../ui/src/forms/fields/shared'
|
||||
import type { ElementNode, TextNode } from '../types'
|
||||
import type {
|
||||
ElementNode,
|
||||
RichTextCustomElement,
|
||||
RichTextPlugin,
|
||||
RichTextPluginComponent,
|
||||
TextNode,
|
||||
} from '../types'
|
||||
import type { EnabledFeatures } from './types'
|
||||
|
||||
import { withCondition } from '../../../ui/src/forms/withCondition'
|
||||
import { useClientFunctions } from '../../../ui/src/providers/ClientFunction'
|
||||
import { defaultRichTextValue } from '../data/defaultValue'
|
||||
import { richTextValidate } from '../data/validation'
|
||||
import { createFeatureMap } from './createFeatureMap'
|
||||
import listTypes from './elements/listTypes'
|
||||
import hotkeys from './hotkeys'
|
||||
import './index.scss'
|
||||
@@ -42,7 +50,10 @@ declare module 'slate' {
|
||||
|
||||
const RichText: React.FC<
|
||||
FormFieldBase & {
|
||||
elements: EnabledFeatures['elements']
|
||||
leaves: EnabledFeatures['leaves']
|
||||
name: string
|
||||
plugins: RichTextPlugin[]
|
||||
richTextComponentMap: Map<string, React.ReactNode>
|
||||
}
|
||||
> = (props) => {
|
||||
@@ -52,57 +63,18 @@ const RichText: React.FC<
|
||||
Error,
|
||||
Label,
|
||||
className,
|
||||
elements,
|
||||
leaves,
|
||||
path: pathFromProps,
|
||||
placeholder,
|
||||
plugins,
|
||||
readOnly,
|
||||
required,
|
||||
richTextComponentMap,
|
||||
style,
|
||||
validate = richTextValidate,
|
||||
width,
|
||||
} = props
|
||||
|
||||
const [{ elements, leaves }] = useState<EnabledFeatures>(() => {
|
||||
const features: EnabledFeatures = {
|
||||
elements: {},
|
||||
leaves: {},
|
||||
}
|
||||
|
||||
for (const [key, value] of richTextComponentMap) {
|
||||
if (key.startsWith('leaf.button.') || key.startsWith('leaf.component.')) {
|
||||
const leafName = key.replace('leaf.button.', '').replace('leaf.component.', '')
|
||||
|
||||
if (!features.leaves[leafName]) {
|
||||
features.leaves[leafName] = {
|
||||
name: leafName,
|
||||
Button: null,
|
||||
Leaf: null,
|
||||
}
|
||||
}
|
||||
|
||||
if (key.startsWith('leaf.button.')) features.leaves[leafName].Button = value
|
||||
if (key.startsWith('leaf.component.')) features.leaves[leafName].Leaf = value
|
||||
}
|
||||
|
||||
if (key.startsWith('element.button.') || key.startsWith('element.component.')) {
|
||||
const elementName = key.replace('element.button.', '').replace('element.component.', '')
|
||||
|
||||
if (!features.elements[elementName]) {
|
||||
features.elements[elementName] = {
|
||||
name: elementName,
|
||||
Button: null,
|
||||
Element: null,
|
||||
}
|
||||
}
|
||||
|
||||
if (key.startsWith('element.button.')) features.elements[elementName].Button = value
|
||||
if (key.startsWith('element.component.')) features.elements[elementName].Element = value
|
||||
}
|
||||
}
|
||||
|
||||
return features
|
||||
})
|
||||
|
||||
const { i18n } = useTranslation()
|
||||
const editorRef = useRef(null)
|
||||
const toolbarRef = useRef(null)
|
||||
@@ -124,6 +96,20 @@ const RichText: React.FC<
|
||||
validate: memoizedValidate,
|
||||
})
|
||||
|
||||
const editor = useMemo(() => {
|
||||
let CreatedEditor = withEnterBreakOut(withHistory(withReact(createEditor())))
|
||||
|
||||
CreatedEditor = withHTML(CreatedEditor)
|
||||
|
||||
if (plugins.length) {
|
||||
CreatedEditor = plugins.reduce((editorWithPlugins, plugin) => {
|
||||
return plugin(editorWithPlugins)
|
||||
}, CreatedEditor)
|
||||
}
|
||||
|
||||
return CreatedEditor
|
||||
}, [plugins])
|
||||
|
||||
const renderElement = useCallback(
|
||||
({ attributes, children, element }) => {
|
||||
// return <div {...attributes}>{children}</div>
|
||||
@@ -228,34 +214,13 @@ const RichText: React.FC<
|
||||
[path, props, schemaPath, leaves],
|
||||
)
|
||||
|
||||
const classes = [
|
||||
baseClass,
|
||||
'field-type',
|
||||
className,
|
||||
showError && 'error',
|
||||
readOnly && `${baseClass}--read-only`,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' ')
|
||||
|
||||
const editor = useMemo(() => {
|
||||
let CreatedEditor = withEnterBreakOut(withHistory(withReact(createEditor())))
|
||||
|
||||
CreatedEditor = withHTML(CreatedEditor)
|
||||
// CreatedEditor = enablePlugins(CreatedEditor, elements)
|
||||
// CreatedEditor = enablePlugins(CreatedEditor, leaves)
|
||||
|
||||
return CreatedEditor
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [path])
|
||||
|
||||
// All slate changes fire the onChange event
|
||||
// including selection changes
|
||||
// so we will filter the set_selection operations out
|
||||
// and only fire setValue when onChange is because of value
|
||||
const handleChange = useCallback(
|
||||
(val: unknown) => {
|
||||
const ops = editor.operations.filter((o: BaseOperation) => {
|
||||
const ops = editor?.operations.filter((o: BaseOperation) => {
|
||||
if (o) {
|
||||
return o.type !== 'set_selection'
|
||||
}
|
||||
@@ -268,7 +233,7 @@ const RichText: React.FC<
|
||||
}
|
||||
}
|
||||
},
|
||||
[editor.operations, readOnly, setValue, value],
|
||||
[editor?.operations, readOnly, setValue, value],
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
@@ -306,6 +271,16 @@ const RichText: React.FC<
|
||||
// }
|
||||
// }, [path, editor]);
|
||||
|
||||
const classes = [
|
||||
baseClass,
|
||||
'field-type',
|
||||
className,
|
||||
showError && 'error',
|
||||
readOnly && `${baseClass}--read-only`,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' ')
|
||||
|
||||
let valueToRender = value
|
||||
|
||||
if (typeof valueToRender === 'string') {
|
||||
@@ -345,7 +320,7 @@ const RichText: React.FC<
|
||||
ref={toolbarRef}
|
||||
>
|
||||
<div className={`${baseClass}__toolbar-wrap`}>
|
||||
{Object.values(elements).map((element, i) => {
|
||||
{Object.values(elements).map((element) => {
|
||||
const Button = element?.Button
|
||||
|
||||
if (Button) {
|
||||
@@ -363,7 +338,7 @@ const RichText: React.FC<
|
||||
|
||||
return null
|
||||
})}
|
||||
{Object.values(leaves).map((leaf, i) => {
|
||||
{Object.values(leaves).map((leaf) => {
|
||||
const Button = leaf?.Button
|
||||
|
||||
if (Button) {
|
||||
|
||||
49
packages/richtext-slate/src/field/createFeatureMap.ts
Normal file
49
packages/richtext-slate/src/field/createFeatureMap.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import type { EnabledFeatures } from './types'
|
||||
|
||||
export const createFeatureMap = (
|
||||
richTextComponentMap: Map<string, React.ReactNode>,
|
||||
): EnabledFeatures => {
|
||||
const features: EnabledFeatures = {
|
||||
elements: {},
|
||||
leaves: {},
|
||||
plugins: [],
|
||||
}
|
||||
|
||||
for (const [key, value] of richTextComponentMap) {
|
||||
if (key.startsWith('leaf.button') || key.startsWith('leaf.component.')) {
|
||||
const leafName = key.replace('leaf.button.', '').replace('leaf.component.', '')
|
||||
|
||||
if (!features.leaves[leafName]) {
|
||||
features.leaves[leafName] = {
|
||||
name: leafName,
|
||||
Button: null,
|
||||
Leaf: null,
|
||||
}
|
||||
}
|
||||
|
||||
if (key.startsWith('leaf.button.')) features.leaves[leafName].Button = value
|
||||
if (key.startsWith('leaf.component.')) features.leaves[leafName].Leaf = value
|
||||
}
|
||||
|
||||
if (key.startsWith('element.button.') || key.startsWith('element.component.')) {
|
||||
const elementName = key.replace('element.button.', '').replace('element.component.', '')
|
||||
|
||||
if (!features.elements[elementName]) {
|
||||
features.elements[elementName] = {
|
||||
name: elementName,
|
||||
Button: null,
|
||||
Element: null,
|
||||
}
|
||||
}
|
||||
|
||||
if (key.startsWith('element.button.')) features.elements[elementName].Button = value
|
||||
if (key.startsWith('element.component.')) features.elements[elementName].Element = value
|
||||
}
|
||||
|
||||
if (key.startsWith('leaf.plugin.') || key.startsWith('element.plugin.')) {
|
||||
features.plugins.push(value)
|
||||
}
|
||||
}
|
||||
|
||||
return features
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
'use client'
|
||||
|
||||
import type React from 'react'
|
||||
import type { Editor } from 'slate'
|
||||
|
||||
import { useSlatePlugin } from '../../../utilities/useSlatePlugin'
|
||||
|
||||
export const WithLinks: React.FC = () => {
|
||||
useSlatePlugin('withLinks', (incomingEditor: Editor): Editor => {
|
||||
const editor = incomingEditor
|
||||
const { isInline } = editor
|
||||
|
||||
editor.isInline = (element) => {
|
||||
if (element.type === 'link') {
|
||||
return true
|
||||
}
|
||||
|
||||
return isInline(element)
|
||||
}
|
||||
|
||||
return editor
|
||||
})
|
||||
return null
|
||||
}
|
||||
@@ -2,13 +2,13 @@ import type { RichTextCustomElement } from '../../..'
|
||||
|
||||
import { LinkButton } from './Button'
|
||||
import { LinkElement } from './Element'
|
||||
import { withLinks } from './utilities'
|
||||
import { WithLinks } from './WithLinks'
|
||||
|
||||
const link: RichTextCustomElement = {
|
||||
name: 'link',
|
||||
Button: LinkButton,
|
||||
Element: LinkElement,
|
||||
plugins: [withLinks],
|
||||
plugins: [WithLinks],
|
||||
}
|
||||
|
||||
export default link
|
||||
|
||||
@@ -30,21 +30,6 @@ export const wrapLink = (editor: Editor): void => {
|
||||
}
|
||||
}
|
||||
|
||||
export const withLinks = (incomingEditor: Editor): Editor => {
|
||||
const editor = incomingEditor
|
||||
const { isInline } = editor
|
||||
|
||||
editor.isInline = (element) => {
|
||||
if (element.type === 'link') {
|
||||
return true
|
||||
}
|
||||
|
||||
return isInline(element)
|
||||
}
|
||||
|
||||
return editor
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is run to enrich the basefields which every link has with potential, custom user-added fields.
|
||||
*/
|
||||
|
||||
@@ -1,16 +1,74 @@
|
||||
'use client'
|
||||
import { ShimmerEffect } from '@payloadcms/ui'
|
||||
import React, { Suspense, lazy } from 'react'
|
||||
import React, { Suspense, lazy, useEffect, useState } from 'react'
|
||||
|
||||
import type { FieldProps } from '../types'
|
||||
import type { FormFieldBase } from '../../../ui/src/forms/fields/shared'
|
||||
import type { RichTextPlugin } from '../types'
|
||||
import type { EnabledFeatures } from './types'
|
||||
|
||||
import { useFieldPath } from '../../../ui/src/forms/FieldPathProvider'
|
||||
import { useClientFunctions } from '../../../ui/src/providers/ClientFunction'
|
||||
import { createFeatureMap } from './createFeatureMap'
|
||||
|
||||
// @ts-expect-error Just TypeScript being broken // TODO: Open TypeScript issue
|
||||
const RichTextEditor = lazy(() => import('./RichText'))
|
||||
|
||||
const RichTextField: React.FC<FieldProps> = (props) => (
|
||||
<Suspense fallback={<ShimmerEffect height="35vh" />}>
|
||||
<RichTextEditor {...props} />
|
||||
</Suspense>
|
||||
)
|
||||
const RichTextField: React.FC<
|
||||
FormFieldBase & {
|
||||
name: string
|
||||
richTextComponentMap: Map<string, React.ReactNode>
|
||||
}
|
||||
> = (props) => {
|
||||
const { richTextComponentMap } = props
|
||||
|
||||
const { schemaPath } = useFieldPath()
|
||||
const clientFunctions = useClientFunctions()
|
||||
const [hasLoadedPlugins, setHasLoadedPlugins] = useState(false)
|
||||
|
||||
const [features] = useState<EnabledFeatures>(() => {
|
||||
return createFeatureMap(richTextComponentMap)
|
||||
})
|
||||
|
||||
const [plugins, setPlugins] = useState<RichTextPlugin[]>([])
|
||||
|
||||
useEffect(() => {
|
||||
if (!hasLoadedPlugins) {
|
||||
const plugins: RichTextPlugin[] = []
|
||||
|
||||
Object.entries(clientFunctions).forEach(([key, plugin]) => {
|
||||
if (key.startsWith(`slatePlugin.${schemaPath}.`)) {
|
||||
plugins.push(plugin)
|
||||
}
|
||||
})
|
||||
|
||||
if (plugins.length === features.plugins.length) {
|
||||
setPlugins(plugins)
|
||||
setHasLoadedPlugins(true)
|
||||
}
|
||||
}
|
||||
}, [hasLoadedPlugins, clientFunctions, schemaPath, features.plugins.length])
|
||||
|
||||
if (!hasLoadedPlugins) {
|
||||
return (
|
||||
<React.Fragment>
|
||||
{Array.isArray(features.plugins) &&
|
||||
features.plugins.map((Plugin, i) => {
|
||||
return <React.Fragment key={i}>{Plugin}</React.Fragment>
|
||||
})}
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Suspense fallback={<ShimmerEffect height="35vh" />}>
|
||||
<RichTextEditor
|
||||
{...props}
|
||||
elements={features.elements}
|
||||
leaves={features.leaves}
|
||||
plugins={plugins}
|
||||
/>
|
||||
</Suspense>
|
||||
)
|
||||
}
|
||||
|
||||
export default RichTextField
|
||||
|
||||
@@ -13,4 +13,5 @@ export type EnabledFeatures = {
|
||||
name: string
|
||||
}
|
||||
}
|
||||
plugins: React.ReactNode[]
|
||||
}
|
||||
|
||||
@@ -48,6 +48,12 @@ export function slateEditor(args: AdapterArguments): RichTextAdapter<any[], Adap
|
||||
|
||||
componentMap.set(`leaf.button.${leafObject.name}`, <LeafButton />)
|
||||
componentMap.set(`leaf.component.${leafObject.name}`, <LeafComponent />)
|
||||
|
||||
if (Array.isArray(leafObject.plugins)) {
|
||||
leafObject.plugins.forEach((Plugin, i) => {
|
||||
componentMap.set(`leaf.plugin.${leafObject.name}.${i}`, <Plugin />)
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
;(args?.admin?.elements || Object.values(elementTypes)).forEach((el) => {
|
||||
@@ -66,6 +72,12 @@ export function slateEditor(args: AdapterArguments): RichTextAdapter<any[], Adap
|
||||
if (ElementButton) componentMap.set(`element.button.${element.name}`, <ElementButton />)
|
||||
componentMap.set(`element.component.${element.name}`, <ElementComponent />)
|
||||
|
||||
if (Array.isArray(element.plugins)) {
|
||||
element.plugins.forEach((Plugin, i) => {
|
||||
componentMap.set(`element.plugin.${element.name}.${i}`, <Plugin />)
|
||||
})
|
||||
}
|
||||
|
||||
switch (element.name) {
|
||||
case 'link': {
|
||||
const linkFields = sanitizeFields({
|
||||
@@ -84,7 +96,7 @@ export function slateEditor(args: AdapterArguments): RichTextAdapter<any[], Adap
|
||||
|
||||
componentMap.set('link.fields', mappedFields)
|
||||
|
||||
return
|
||||
break
|
||||
}
|
||||
|
||||
case 'upload':
|
||||
|
||||
@@ -11,20 +11,21 @@ export function nodeIsTextNode(node: ElementNode | TextNode): node is TextNode {
|
||||
return 'text' in node
|
||||
}
|
||||
|
||||
type RichTextPlugin = (editor: Editor) => Editor
|
||||
export type RichTextPluginComponent = React.ComponentType
|
||||
export type RichTextPlugin = (editor: Editor) => Editor
|
||||
|
||||
export type RichTextCustomElement = {
|
||||
Button?: React.ComponentType<any>
|
||||
Element: React.ComponentType<any>
|
||||
name: string
|
||||
plugins?: RichTextPlugin[]
|
||||
plugins?: RichTextPluginComponent[]
|
||||
}
|
||||
|
||||
export type RichTextCustomLeaf = {
|
||||
Button: React.ComponentType<any>
|
||||
Leaf: React.ComponentType<any>
|
||||
name: string
|
||||
plugins?: RichTextPlugin[]
|
||||
plugins?: RichTextPluginComponent[]
|
||||
}
|
||||
|
||||
export type RichTextElement =
|
||||
|
||||
13
packages/richtext-slate/src/utilities/useSlatePlugin.tsx
Normal file
13
packages/richtext-slate/src/utilities/useSlatePlugin.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import type { Editor } from 'slate'
|
||||
|
||||
import { useAddClientFunction } from '@payloadcms/ui/providers'
|
||||
|
||||
import { useFieldPath } from '../../../ui/src/forms/FieldPathProvider'
|
||||
|
||||
type Plugin = (editor: Editor) => Editor
|
||||
|
||||
export const useSlatePlugin = (key: string, plugin: Plugin) => {
|
||||
const { schemaPath } = useFieldPath()
|
||||
|
||||
useAddClientFunction(`slatePlugin.${schemaPath}.${key}`, plugin)
|
||||
}
|
||||
@@ -17,3 +17,4 @@ export { CustomProvider } from '../providers/CustomProvider'
|
||||
export { useComponentMap } from '../providers/ComponentMapProvider'
|
||||
export type { IComponentMapContext } from '../providers/ComponentMapProvider'
|
||||
export { SetDocumentInfo } from '../providers/DocumentInfo/SetDocumentInfo'
|
||||
export { ClientFunctionProvider, useAddClientFunction } from '../providers/ClientFunction'
|
||||
|
||||
45
packages/ui/src/providers/ClientFunction/index.tsx
Normal file
45
packages/ui/src/providers/ClientFunction/index.tsx
Normal file
@@ -0,0 +1,45 @@
|
||||
'use client'
|
||||
import React from 'react'
|
||||
|
||||
type AddClientFunctionContextType = (func: any) => void
|
||||
type ClientFunctionsContextType = Record<string, any>
|
||||
|
||||
const AddClientFunctionContext = React.createContext<AddClientFunctionContextType>(() => null)
|
||||
const ClientFunctionsContext = React.createContext<ClientFunctionsContextType>({})
|
||||
|
||||
type AddFunctionArgs = { key: string; func: any }
|
||||
|
||||
export const ClientFunctionProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
||||
const [clientFunctions, setClientFunctions] = React.useState({})
|
||||
|
||||
const addClientFunction = React.useCallback((args: AddFunctionArgs) => {
|
||||
setClientFunctions((state) => {
|
||||
const newState = { ...state }
|
||||
newState[args.key] = args.func
|
||||
return newState
|
||||
})
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<AddClientFunctionContext.Provider value={addClientFunction}>
|
||||
<ClientFunctionsContext.Provider value={clientFunctions}>
|
||||
{children}
|
||||
</ClientFunctionsContext.Provider>
|
||||
</AddClientFunctionContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export const useAddClientFunction = (key: string, func: any) => {
|
||||
const addClientFunction = React.useContext(AddClientFunctionContext)
|
||||
|
||||
React.useEffect(() => {
|
||||
addClientFunction({
|
||||
key,
|
||||
func,
|
||||
})
|
||||
}, [func, key])
|
||||
}
|
||||
|
||||
export const useClientFunctions = () => {
|
||||
return React.useContext(ClientFunctionsContext)
|
||||
}
|
||||
@@ -23,6 +23,7 @@ import { ComponentMapProvider } from '../ComponentMapProvider'
|
||||
import { SearchParamsProvider } from '../SearchParams'
|
||||
import { ParamsProvider } from '../Params'
|
||||
import { DocumentInfoProvider } from '../DocumentInfo'
|
||||
import { ClientFunctionProvider } from '../ClientFunction'
|
||||
|
||||
type Props = {
|
||||
config: ClientConfig
|
||||
@@ -47,52 +48,54 @@ export const RootProvider: React.FC<Props> = ({
|
||||
<Fragment>
|
||||
<ConfigProvider config={config}>
|
||||
<ComponentMapProvider componentMap={componentMap}>
|
||||
<TranslationProvider
|
||||
lang={lang}
|
||||
translations={translations}
|
||||
fallbackLang={fallbackLang}
|
||||
languageOptions={languageOptions}
|
||||
>
|
||||
<WindowInfoProvider
|
||||
breakpoints={{
|
||||
l: '(max-width: 1440px)',
|
||||
m: '(max-width: 1024px)',
|
||||
s: '(max-width: 768px)',
|
||||
xs: '(max-width: 400px)',
|
||||
}}
|
||||
<ClientFunctionProvider>
|
||||
<TranslationProvider
|
||||
lang={lang}
|
||||
translations={translations}
|
||||
fallbackLang={fallbackLang}
|
||||
languageOptions={languageOptions}
|
||||
>
|
||||
<ScrollInfoProvider>
|
||||
<ModalProvider classPrefix="payload" transTime={0} zIndex="var(--z-modal)">
|
||||
<AuthProvider>
|
||||
<PreferencesProvider>
|
||||
<ThemeProvider>
|
||||
<ParamsProvider>
|
||||
<SearchParamsProvider>
|
||||
<LocaleProvider>
|
||||
<StepNavProvider>
|
||||
<LoadingOverlayProvider>
|
||||
<DocumentInfoProvider>
|
||||
<DocumentEventsProvider>
|
||||
<ActionsProvider>
|
||||
<NavProvider>
|
||||
<CustomProvider>{children}</CustomProvider>
|
||||
</NavProvider>
|
||||
</ActionsProvider>
|
||||
</DocumentEventsProvider>
|
||||
</DocumentInfoProvider>
|
||||
</LoadingOverlayProvider>
|
||||
</StepNavProvider>
|
||||
</LocaleProvider>
|
||||
</SearchParamsProvider>
|
||||
</ParamsProvider>
|
||||
</ThemeProvider>
|
||||
</PreferencesProvider>
|
||||
<ModalContainer />
|
||||
</AuthProvider>
|
||||
</ModalProvider>
|
||||
</ScrollInfoProvider>
|
||||
</WindowInfoProvider>
|
||||
</TranslationProvider>
|
||||
<WindowInfoProvider
|
||||
breakpoints={{
|
||||
l: '(max-width: 1440px)',
|
||||
m: '(max-width: 1024px)',
|
||||
s: '(max-width: 768px)',
|
||||
xs: '(max-width: 400px)',
|
||||
}}
|
||||
>
|
||||
<ScrollInfoProvider>
|
||||
<ModalProvider classPrefix="payload" transTime={0} zIndex="var(--z-modal)">
|
||||
<AuthProvider>
|
||||
<PreferencesProvider>
|
||||
<ThemeProvider>
|
||||
<ParamsProvider>
|
||||
<SearchParamsProvider>
|
||||
<LocaleProvider>
|
||||
<StepNavProvider>
|
||||
<LoadingOverlayProvider>
|
||||
<DocumentInfoProvider>
|
||||
<DocumentEventsProvider>
|
||||
<ActionsProvider>
|
||||
<NavProvider>
|
||||
<CustomProvider>{children}</CustomProvider>
|
||||
</NavProvider>
|
||||
</ActionsProvider>
|
||||
</DocumentEventsProvider>
|
||||
</DocumentInfoProvider>
|
||||
</LoadingOverlayProvider>
|
||||
</StepNavProvider>
|
||||
</LocaleProvider>
|
||||
</SearchParamsProvider>
|
||||
</ParamsProvider>
|
||||
</ThemeProvider>
|
||||
</PreferencesProvider>
|
||||
<ModalContainer />
|
||||
</AuthProvider>
|
||||
</ModalProvider>
|
||||
</ScrollInfoProvider>
|
||||
</WindowInfoProvider>
|
||||
</TranslationProvider>
|
||||
</ClientFunctionProvider>
|
||||
</ComponentMapProvider>
|
||||
</ConfigProvider>
|
||||
<ToastContainer icon={false} position="bottom-center" transition={Slide} />
|
||||
|
||||
Reference in New Issue
Block a user