mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-05-03 10:00:07 -04:00
feat: rewrite public UI component rendering using tunnels (#6117)
* feat: rewrite public UI component rendering using tunnels * factor out into components * comments * fix variable naming * fix not hiding welcomeScreen * factor out AppFooter and memoize components * remove `UIOptions.welcomeScreen` and render only from host app * factor out tunnels into own file * update changelog. Keep `UIOptions.welcomeScreen` as deprecated * update changelog * lint --------- Co-authored-by: Aakansha Doshi <aakansha1216@gmail.com>
This commit is contained in:
parent
3a141ca77a
commit
e6de1fe4a4
22 changed files with 417 additions and 372 deletions
|
@ -8,15 +8,8 @@ import { NonDeletedExcalidrawElement } from "../element/types";
|
|||
import { Language, t } from "../i18n";
|
||||
import { calculateScrollCenter } from "../scene";
|
||||
import { ExportType } from "../scene/types";
|
||||
import {
|
||||
AppProps,
|
||||
AppState,
|
||||
ExcalidrawProps,
|
||||
BinaryFiles,
|
||||
UIChildrenComponents,
|
||||
UIWelcomeScreenComponents,
|
||||
} from "../types";
|
||||
import { isShallowEqual, muteFSAbortError, getReactChildren } from "../utils";
|
||||
import { AppProps, AppState, ExcalidrawProps, BinaryFiles } from "../types";
|
||||
import { isShallowEqual, muteFSAbortError } from "../utils";
|
||||
import { SelectedShapeActions, ShapesSwitcher } from "./Actions";
|
||||
import { ErrorDialog } from "./ErrorDialog";
|
||||
import { ExportCB, ImageExportDialog } from "./ImageExportDialog";
|
||||
|
@ -45,7 +38,6 @@ import { useDevice } from "../components/App";
|
|||
import { Stats } from "./Stats";
|
||||
import { actionToggleStats } from "../actions/actionToggleStats";
|
||||
import Footer from "./footer/Footer";
|
||||
import WelcomeScreen from "./welcome-screen/WelcomeScreen";
|
||||
import { hostSidebarCountersAtom } from "./Sidebar/Sidebar";
|
||||
import { jotaiScope } from "../jotai";
|
||||
import { useAtom } from "jotai";
|
||||
|
@ -53,6 +45,12 @@ import MainMenu from "./main-menu/MainMenu";
|
|||
import { ActiveConfirmDialog } from "./ActiveConfirmDialog";
|
||||
import { HandButton } from "./HandButton";
|
||||
import { isHandToolActive } from "../appState";
|
||||
import {
|
||||
mainMenuTunnel,
|
||||
welcomeScreenMenuHintTunnel,
|
||||
welcomeScreenToolbarHintTunnel,
|
||||
welcomeScreenCenterTunnel,
|
||||
} from "./tunnels";
|
||||
|
||||
interface LayerUIProps {
|
||||
actionManager: ActionManager;
|
||||
|
@ -67,7 +65,6 @@ interface LayerUIProps {
|
|||
onInsertElements: (elements: readonly NonDeletedExcalidrawElement[]) => void;
|
||||
showExitZenModeBtn: boolean;
|
||||
langCode: Language["code"];
|
||||
isCollaborating: boolean;
|
||||
renderTopRightUI?: ExcalidrawProps["renderTopRightUI"];
|
||||
renderCustomStats?: ExcalidrawProps["renderCustomStats"];
|
||||
renderCustomSidebar?: ExcalidrawProps["renderSidebar"];
|
||||
|
@ -81,6 +78,32 @@ interface LayerUIProps {
|
|||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
const DefaultMainMenu: React.FC<{
|
||||
UIOptions: AppProps["UIOptions"];
|
||||
}> = ({ UIOptions }) => {
|
||||
return (
|
||||
<MainMenu __fallback>
|
||||
<MainMenu.DefaultItems.LoadScene />
|
||||
<MainMenu.DefaultItems.SaveToActiveFile />
|
||||
{/* FIXME we should to test for this inside the item itself */}
|
||||
{UIOptions.canvasActions.export && <MainMenu.DefaultItems.Export />}
|
||||
{/* FIXME we should to test for this inside the item itself */}
|
||||
{UIOptions.canvasActions.saveAsImage && (
|
||||
<MainMenu.DefaultItems.SaveAsImage />
|
||||
)}
|
||||
<MainMenu.DefaultItems.Help />
|
||||
<MainMenu.DefaultItems.ClearCanvas />
|
||||
<MainMenu.Separator />
|
||||
<MainMenu.Group title="Excalidraw links">
|
||||
<MainMenu.DefaultItems.Socials />
|
||||
</MainMenu.Group>
|
||||
<MainMenu.Separator />
|
||||
<MainMenu.DefaultItems.ToggleTheme />
|
||||
<MainMenu.DefaultItems.ChangeCanvasBackground />
|
||||
</MainMenu>
|
||||
);
|
||||
};
|
||||
|
||||
const LayerUI = ({
|
||||
actionManager,
|
||||
appState,
|
||||
|
@ -93,7 +116,6 @@ const LayerUI = ({
|
|||
onPenModeToggle,
|
||||
onInsertElements,
|
||||
showExitZenModeBtn,
|
||||
isCollaborating,
|
||||
renderTopRightUI,
|
||||
renderCustomStats,
|
||||
renderCustomSidebar,
|
||||
|
@ -108,28 +130,6 @@ const LayerUI = ({
|
|||
}: LayerUIProps) => {
|
||||
const device = useDevice();
|
||||
|
||||
const [childrenComponents, restChildren] =
|
||||
getReactChildren<UIChildrenComponents>(children, {
|
||||
Menu: true,
|
||||
FooterCenter: true,
|
||||
WelcomeScreen: true,
|
||||
});
|
||||
|
||||
const [WelcomeScreenComponents] = getReactChildren<UIWelcomeScreenComponents>(
|
||||
renderWelcomeScreen
|
||||
? (
|
||||
childrenComponents?.WelcomeScreen ?? (
|
||||
<WelcomeScreen>
|
||||
<WelcomeScreen.Center />
|
||||
<WelcomeScreen.Hints.MenuHint />
|
||||
<WelcomeScreen.Hints.ToolbarHint />
|
||||
<WelcomeScreen.Hints.HelpHint />
|
||||
</WelcomeScreen>
|
||||
)
|
||||
)?.props?.children
|
||||
: null,
|
||||
);
|
||||
|
||||
const renderJSONExportDialog = () => {
|
||||
if (!UIOptions.canvasActions.export) {
|
||||
return null;
|
||||
|
@ -197,37 +197,12 @@ const LayerUI = ({
|
|||
);
|
||||
};
|
||||
|
||||
const renderMenu = () => {
|
||||
return (
|
||||
childrenComponents.Menu || (
|
||||
<MainMenu>
|
||||
<MainMenu.DefaultItems.LoadScene />
|
||||
<MainMenu.DefaultItems.SaveToActiveFile />
|
||||
{/* FIXME we should to test for this inside the item itself */}
|
||||
{UIOptions.canvasActions.export && <MainMenu.DefaultItems.Export />}
|
||||
{/* FIXME we should to test for this inside the item itself */}
|
||||
{UIOptions.canvasActions.saveAsImage && (
|
||||
<MainMenu.DefaultItems.SaveAsImage />
|
||||
)}
|
||||
<MainMenu.DefaultItems.Help />
|
||||
<MainMenu.DefaultItems.ClearCanvas />
|
||||
<MainMenu.Separator />
|
||||
<MainMenu.Group title="Excalidraw links">
|
||||
<MainMenu.DefaultItems.Socials />
|
||||
</MainMenu.Group>
|
||||
<MainMenu.Separator />
|
||||
<MainMenu.DefaultItems.ToggleTheme />
|
||||
<MainMenu.DefaultItems.ChangeCanvasBackground />
|
||||
</MainMenu>
|
||||
)
|
||||
);
|
||||
};
|
||||
const renderCanvasActions = () => (
|
||||
<div style={{ position: "relative" }}>
|
||||
{WelcomeScreenComponents.MenuHint}
|
||||
{/* wrapping to Fragment stops React from occasionally complaining
|
||||
about identical Keys */}
|
||||
<>{renderMenu()}</>
|
||||
<mainMenuTunnel.Out />
|
||||
{renderWelcomeScreen && <welcomeScreenMenuHintTunnel.Out />}
|
||||
</div>
|
||||
);
|
||||
|
||||
|
@ -264,7 +239,6 @@ const LayerUI = ({
|
|||
|
||||
return (
|
||||
<FixedSideContainer side="top">
|
||||
{WelcomeScreenComponents.Center}
|
||||
<div className="App-menu App-menu_top">
|
||||
<Stack.Col
|
||||
gap={6}
|
||||
|
@ -279,7 +253,9 @@ const LayerUI = ({
|
|||
<Section heading="shapes" className="shapes-section">
|
||||
{(heading: React.ReactNode) => (
|
||||
<div style={{ position: "relative" }}>
|
||||
{WelcomeScreenComponents.ToolbarHint}
|
||||
{renderWelcomeScreen && (
|
||||
<welcomeScreenToolbarHintTunnel.Out />
|
||||
)}
|
||||
<Stack.Col gap={4} align="start">
|
||||
<Stack.Row
|
||||
gap={1}
|
||||
|
@ -380,7 +356,16 @@ const LayerUI = ({
|
|||
|
||||
return (
|
||||
<>
|
||||
{restChildren}
|
||||
{/* ------------------------- tunneled UI ---------------------------- */}
|
||||
{/* make sure we render host app components first so that we can detect
|
||||
them first on initial render to optimize layout shift */}
|
||||
{children}
|
||||
{/* render component fallbacks. Can be rendered anywhere as they'll be
|
||||
tunneled away. We only render tunneled components that actually
|
||||
have defaults when host do not render anything. */}
|
||||
<DefaultMainMenu UIOptions={UIOptions} />
|
||||
{/* ------------------------------------------------------------------ */}
|
||||
|
||||
{appState.isLoading && <LoadingMessage delay={250} />}
|
||||
{appState.errorMessage && (
|
||||
<ErrorDialog
|
||||
|
@ -427,8 +412,6 @@ const LayerUI = ({
|
|||
renderCustomStats={renderCustomStats}
|
||||
renderSidebars={renderSidebars}
|
||||
device={device}
|
||||
renderMenu={renderMenu}
|
||||
welcomeScreenCenter={WelcomeScreenComponents.Center}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
@ -451,13 +434,13 @@ const LayerUI = ({
|
|||
: {}
|
||||
}
|
||||
>
|
||||
{renderWelcomeScreen && <welcomeScreenCenterTunnel.Out />}
|
||||
{renderFixedSideContainer()}
|
||||
<Footer
|
||||
appState={appState}
|
||||
actionManager={actionManager}
|
||||
showExitZenModeBtn={showExitZenModeBtn}
|
||||
footerCenter={childrenComponents.FooterCenter}
|
||||
welcomeScreenHelp={WelcomeScreenComponents.HelpHint}
|
||||
renderWelcomeScreen={renderWelcomeScreen}
|
||||
/>
|
||||
{appState.showStats && (
|
||||
<Stats
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue