refactor: Move footer to its own component (#5609)
This commit is contained in:
parent
f1ae37c84b
commit
ba2c86fe1b
@ -26,6 +26,8 @@ import { ToolButton } from "./ToolButton";
|
|||||||
import { hasStrokeColor } from "../scene/comparisons";
|
import { hasStrokeColor } from "../scene/comparisons";
|
||||||
import { trackEvent } from "../analytics";
|
import { trackEvent } from "../analytics";
|
||||||
import { hasBoundTextElement, isBoundToContainer } from "../element/typeChecks";
|
import { hasBoundTextElement, isBoundToContainer } from "../element/typeChecks";
|
||||||
|
import clsx from "clsx";
|
||||||
|
import { actionToggleZenMode } from "../actions";
|
||||||
|
|
||||||
export const SelectedShapeActions = ({
|
export const SelectedShapeActions = ({
|
||||||
appState,
|
appState,
|
||||||
@ -269,3 +271,45 @@ export const ZoomActions = ({
|
|||||||
</Stack.Row>
|
</Stack.Row>
|
||||||
</Stack.Col>
|
</Stack.Col>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const UndoRedoActions = ({
|
||||||
|
renderAction,
|
||||||
|
className,
|
||||||
|
}: {
|
||||||
|
renderAction: ActionManager["renderAction"];
|
||||||
|
className?: string;
|
||||||
|
}) => (
|
||||||
|
<div className={`undo-redo-buttons ${className}`}>
|
||||||
|
{renderAction("undo", { size: "small" })}
|
||||||
|
{renderAction("redo", { size: "small" })}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const ExitZenModeAction = ({
|
||||||
|
executeAction,
|
||||||
|
showExitZenModeBtn,
|
||||||
|
}: {
|
||||||
|
executeAction: ActionManager["executeAction"];
|
||||||
|
showExitZenModeBtn: boolean;
|
||||||
|
}) => (
|
||||||
|
<button
|
||||||
|
className={clsx("disable-zen-mode", {
|
||||||
|
"disable-zen-mode--visible": showExitZenModeBtn,
|
||||||
|
})}
|
||||||
|
onClick={() => executeAction(actionToggleZenMode)}
|
||||||
|
>
|
||||||
|
{t("buttons.exitZenMode")}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const FinalizeAction = ({
|
||||||
|
renderAction,
|
||||||
|
className,
|
||||||
|
}: {
|
||||||
|
renderAction: ActionManager["renderAction"];
|
||||||
|
className?: string;
|
||||||
|
}) => (
|
||||||
|
<div className={`finalize-button ${className}`}>
|
||||||
|
{renderAction("finalize", { size: "small" })}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
99
src/components/Footer.tsx
Normal file
99
src/components/Footer.tsx
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
import clsx from "clsx";
|
||||||
|
import { ActionManager } from "../actions/manager";
|
||||||
|
import { AppState, ExcalidrawProps } from "../types";
|
||||||
|
import {
|
||||||
|
ExitZenModeAction,
|
||||||
|
FinalizeAction,
|
||||||
|
UndoRedoActions,
|
||||||
|
ZoomActions,
|
||||||
|
} from "./Actions";
|
||||||
|
import { useDevice } from "./App";
|
||||||
|
import { Island } from "./Island";
|
||||||
|
import { Section } from "./Section";
|
||||||
|
import Stack from "./Stack";
|
||||||
|
|
||||||
|
const Footer = ({
|
||||||
|
appState,
|
||||||
|
actionManager,
|
||||||
|
renderCustomFooter,
|
||||||
|
showExitZenModeBtn,
|
||||||
|
}: {
|
||||||
|
appState: AppState;
|
||||||
|
actionManager: ActionManager;
|
||||||
|
renderCustomFooter?: ExcalidrawProps["renderFooter"];
|
||||||
|
showExitZenModeBtn: boolean;
|
||||||
|
}) => {
|
||||||
|
const device = useDevice();
|
||||||
|
const showFinalize =
|
||||||
|
!appState.viewModeEnabled && appState.multiElement && device.isTouchScreen;
|
||||||
|
return (
|
||||||
|
<footer
|
||||||
|
role="contentinfo"
|
||||||
|
className="layer-ui__wrapper__footer App-menu App-menu_bottom"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={clsx("layer-ui__wrapper__footer-left zen-mode-transition", {
|
||||||
|
"layer-ui__wrapper__footer-left--transition-left":
|
||||||
|
appState.zenModeEnabled,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<Stack.Col gap={2}>
|
||||||
|
<Section heading="canvasActions">
|
||||||
|
<Island padding={1}>
|
||||||
|
<ZoomActions
|
||||||
|
renderAction={actionManager.renderAction}
|
||||||
|
zoom={appState.zoom}
|
||||||
|
/>
|
||||||
|
</Island>
|
||||||
|
{!appState.viewModeEnabled && (
|
||||||
|
<>
|
||||||
|
<UndoRedoActions
|
||||||
|
renderAction={actionManager.renderAction}
|
||||||
|
className={clsx("zen-mode-transition", {
|
||||||
|
"layer-ui__wrapper__footer-left--transition-bottom":
|
||||||
|
appState.zenModeEnabled,
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div
|
||||||
|
className={clsx("eraser-buttons zen-mode-transition", {
|
||||||
|
"layer-ui__wrapper__footer-left--transition-left":
|
||||||
|
appState.zenModeEnabled,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{actionManager.renderAction("eraser", { size: "small" })}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{showFinalize && (
|
||||||
|
<FinalizeAction
|
||||||
|
renderAction={actionManager.renderAction}
|
||||||
|
className={clsx("zen-mode-transition", {
|
||||||
|
"layer-ui__wrapper__footer-left--transition-left":
|
||||||
|
appState.zenModeEnabled,
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Section>
|
||||||
|
</Stack.Col>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className={clsx(
|
||||||
|
"layer-ui__wrapper__footer-center zen-mode-transition",
|
||||||
|
{
|
||||||
|
"layer-ui__wrapper__footer-left--transition-bottom":
|
||||||
|
appState.zenModeEnabled,
|
||||||
|
},
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{renderCustomFooter?.(false, appState)}
|
||||||
|
</div>
|
||||||
|
<ExitZenModeAction
|
||||||
|
executeAction={actionManager.executeAction}
|
||||||
|
showExitZenModeBtn={showExitZenModeBtn}
|
||||||
|
/>
|
||||||
|
</footer>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Footer;
|
@ -10,7 +10,7 @@ import { calculateScrollCenter, getSelectedElements } from "../scene";
|
|||||||
import { ExportType } from "../scene/types";
|
import { ExportType } from "../scene/types";
|
||||||
import { AppProps, AppState, ExcalidrawProps, BinaryFiles } from "../types";
|
import { AppProps, AppState, ExcalidrawProps, BinaryFiles } from "../types";
|
||||||
import { muteFSAbortError } from "../utils";
|
import { muteFSAbortError } from "../utils";
|
||||||
import { SelectedShapeActions, ShapesSwitcher, ZoomActions } from "./Actions";
|
import { SelectedShapeActions, ShapesSwitcher } from "./Actions";
|
||||||
import { BackgroundPickerAndDarkModeToggle } from "./BackgroundPickerAndDarkModeToggle";
|
import { BackgroundPickerAndDarkModeToggle } from "./BackgroundPickerAndDarkModeToggle";
|
||||||
import CollabButton from "./CollabButton";
|
import CollabButton from "./CollabButton";
|
||||||
import { ErrorDialog } from "./ErrorDialog";
|
import { ErrorDialog } from "./ErrorDialog";
|
||||||
@ -39,7 +39,7 @@ import { trackEvent } from "../analytics";
|
|||||||
import { useDevice } from "../components/App";
|
import { useDevice } from "../components/App";
|
||||||
import { Stats } from "./Stats";
|
import { Stats } from "./Stats";
|
||||||
import { actionToggleStats } from "../actions/actionToggleStats";
|
import { actionToggleStats } from "../actions/actionToggleStats";
|
||||||
import { actionToggleZenMode } from "../actions";
|
import Footer from "./Footer";
|
||||||
|
|
||||||
interface LayerUIProps {
|
interface LayerUIProps {
|
||||||
actionManager: ActionManager;
|
actionManager: ActionManager;
|
||||||
@ -381,99 +381,6 @@ const LayerUI = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderBottomAppMenu = () => {
|
|
||||||
return (
|
|
||||||
<footer
|
|
||||||
role="contentinfo"
|
|
||||||
className="layer-ui__wrapper__footer App-menu App-menu_bottom"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className={clsx(
|
|
||||||
"layer-ui__wrapper__footer-left zen-mode-transition",
|
|
||||||
{
|
|
||||||
"layer-ui__wrapper__footer-left--transition-left":
|
|
||||||
appState.zenModeEnabled,
|
|
||||||
},
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<Stack.Col gap={2}>
|
|
||||||
<Section heading="canvasActions">
|
|
||||||
<Island padding={1}>
|
|
||||||
<ZoomActions
|
|
||||||
renderAction={actionManager.renderAction}
|
|
||||||
zoom={appState.zoom}
|
|
||||||
/>
|
|
||||||
</Island>
|
|
||||||
{!appState.viewModeEnabled && (
|
|
||||||
<>
|
|
||||||
<div
|
|
||||||
className={clsx("undo-redo-buttons zen-mode-transition", {
|
|
||||||
"layer-ui__wrapper__footer-left--transition-bottom":
|
|
||||||
appState.zenModeEnabled,
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
{actionManager.renderAction("undo", { size: "small" })}
|
|
||||||
{actionManager.renderAction("redo", { size: "small" })}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
className={clsx("eraser-buttons zen-mode-transition", {
|
|
||||||
"layer-ui__wrapper__footer-left--transition-left":
|
|
||||||
appState.zenModeEnabled,
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
{actionManager.renderAction("eraser", { size: "small" })}
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{!appState.viewModeEnabled &&
|
|
||||||
appState.multiElement &&
|
|
||||||
device.isTouchScreen && (
|
|
||||||
<div
|
|
||||||
className={clsx("finalize-button zen-mode-transition", {
|
|
||||||
"layer-ui__wrapper__footer-left--transition-left":
|
|
||||||
appState.zenModeEnabled,
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
{actionManager.renderAction("finalize", { size: "small" })}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</Section>
|
|
||||||
</Stack.Col>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className={clsx(
|
|
||||||
"layer-ui__wrapper__footer-center zen-mode-transition",
|
|
||||||
{
|
|
||||||
"layer-ui__wrapper__footer-left--transition-bottom":
|
|
||||||
appState.zenModeEnabled,
|
|
||||||
},
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{renderCustomFooter?.(false, appState)}
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className={clsx(
|
|
||||||
"layer-ui__wrapper__footer-right zen-mode-transition",
|
|
||||||
{
|
|
||||||
"transition-right disable-pointerEvents": appState.zenModeEnabled,
|
|
||||||
},
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{actionManager.renderAction("toggleShortcuts")}
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
className={clsx("disable-zen-mode", {
|
|
||||||
"disable-zen-mode--visible": showExitZenModeBtn,
|
|
||||||
})}
|
|
||||||
onClick={() => actionManager.executeAction(actionToggleZenMode)}
|
|
||||||
>
|
|
||||||
{t("buttons.exitZenMode")}
|
|
||||||
</button>
|
|
||||||
</footer>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const dialogs = (
|
const dialogs = (
|
||||||
<>
|
<>
|
||||||
{appState.isLoading && <LoadingMessage delay={250} />}
|
{appState.isLoading && <LoadingMessage delay={250} />}
|
||||||
@ -565,7 +472,12 @@ const LayerUI = ({
|
|||||||
>
|
>
|
||||||
{dialogs}
|
{dialogs}
|
||||||
{renderFixedSideContainer()}
|
{renderFixedSideContainer()}
|
||||||
{renderBottomAppMenu()}
|
<Footer
|
||||||
|
appState={appState}
|
||||||
|
actionManager={actionManager}
|
||||||
|
renderCustomFooter={renderCustomFooter}
|
||||||
|
showExitZenModeBtn={showExitZenModeBtn}
|
||||||
|
/>
|
||||||
{renderStats()}
|
{renderStats()}
|
||||||
{appState.scrolledOutside && (
|
{appState.scrolledOutside && (
|
||||||
<button
|
<button
|
||||||
|
Loading…
x
Reference in New Issue
Block a user