4.0 KB129 lines
Blame
1/**
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
8import type {ReactNode} from 'react';
9
10import {ErrorBoundary} from 'isl-components/ErrorNotice';
11import {ThemedComponentsRoot} from 'isl-components/ThemedComponentsRoot';
12import {ViewportOverlayRoot} from 'isl-components/ViewportOverlay';
13import {Provider, useAtomValue, useStore} from 'jotai';
14import React from 'react';
15import {ContextMenus} from 'shared/ContextMenu';
16import {ISLCommandContext} from './ISLShortcuts';
17import {SuspenseBoundary} from './SuspenseBoundary';
18import {TopLevelToast} from './TopLevelToast';
19import {enableReactTools, enableReduxTools} from './atoms/debugToolAtoms';
20import {I18nSupport} from './i18n';
21import {setJotaiStore} from './jotaiUtils';
22import platform from './platform';
23import {DEFAULT_RESET_CSS} from './resetStyle';
24import {zoomUISettingAtom} from './responsive';
25import {themeState} from './theme';
26import {ModalContainer} from './useModal';
27import {usePromise} from './usePromise';
28import {isDev, isTest} from './utils';
29
30export function AllProviders({children}: {children: ReactNode}) {
31 return (
32 <React.StrictMode>
33 <ResetStyle />
34 <I18nSupport>
35 <MaybeWithJotaiRoot>
36 <ISLRoot>
37 <ISLCommandContext>
38 <ErrorBoundary>
39 {children}
40 <ViewportOverlayRoot />
41 <ModalContainer />
42 <ContextMenus />
43 <TopLevelToast />
44 </ErrorBoundary>
45 </ISLCommandContext>
46 </ISLRoot>
47 </MaybeWithJotaiRoot>
48 </I18nSupport>
49 </React.StrictMode>
50 );
51}
52
53function ResetStyle() {
54 const resetCSS = platform.theme?.resetCSS ?? DEFAULT_RESET_CSS;
55 return resetCSS.length > 0 ? <style>{resetCSS}</style> : null;
56}
57
58function ISLRoot({children}: {children: ReactNode}) {
59 const theme = useAtomValue(themeState);
60 useAtomValue(zoomUISettingAtom);
61 return (
62 <div onDragEnter={handleDragAndDrop} onDragOver={handleDragAndDrop}>
63 <ThemedComponentsRoot className="isl-root" theme={theme}>
64 {children}
65 </ThemedComponentsRoot>
66 </div>
67 );
68}
69
70function handleDragAndDrop(e: React.DragEvent<HTMLDivElement>) {
71 // VS Code tries to capture drag & drop events to open files. But if you're dragging
72 // on ISL, you probably want to do an ImageUpload. Prevent this event from propagating to vscode.
73 if (e.dataTransfer?.types?.some(t => t === 'Files')) {
74 e.stopPropagation();
75 e.preventDefault();
76 e.dataTransfer.dropEffect = 'copy';
77 }
78}
79
80function MaybeWithJotaiRoot({children}: {children: JSX.Element}) {
81 if (isTest) {
82 // Use a new store when re-mounting so each test (that calls `render(<App />)`)
83 // starts with a clean state.
84 return (
85 <Provider>
86 <AccessJotaiRoot />
87 {children}
88 </Provider>
89 );
90 } else if (isDev) {
91 return <MaybeJotaiDebugTools>{children}</MaybeJotaiDebugTools>;
92 } else {
93 // Such scoped Provider or store complexity is not needed outside tests or dev.
94 return children;
95 }
96}
97const jotaiDevtools = import('./third-party/jotai-devtools/utils');
98
99function MaybeJotaiDebugTools({children}: {children: JSX.Element}) {
100 const enabledRedux = useAtomValue(enableReduxTools);
101 const enabledReact = useAtomValue(enableReactTools);
102 return enabledRedux || enabledReact ? (
103 <SuspenseBoundary>
104 {enabledRedux ? <AtomsDevtools>{children}</AtomsDevtools> : children}
105 {enabledReact && <DebugAtoms />}
106 </SuspenseBoundary>
107 ) : (
108 children
109 );
110}
111
112function AtomsDevtools({children}: {children: JSX.Element}) {
113 const {useAtomsDevtools} = usePromise(jotaiDevtools);
114 useAtomsDevtools('jotai');
115 return children;
116}
117
118function DebugAtoms() {
119 const {useAtomsDebugValue} = usePromise(jotaiDevtools);
120 useAtomsDebugValue();
121 return null;
122}
123
124function AccessJotaiRoot() {
125 const store = useStore();
126 setJotaiStore(store);
127 return null;
128}
129