feat: render footer as a component instead of render prop
This commit is contained in:
parent
a7153d9d1d
commit
67bc10f33b
@ -474,12 +474,8 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
this.scene.getNonDeletedElements(),
|
this.scene.getNonDeletedElements(),
|
||||||
this.state,
|
this.state,
|
||||||
);
|
);
|
||||||
const {
|
const { onCollabButtonClick, renderTopRightUI, renderCustomStats } =
|
||||||
onCollabButtonClick,
|
this.props;
|
||||||
renderTopRightUI,
|
|
||||||
renderFooter,
|
|
||||||
renderCustomStats,
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@ -520,7 +516,6 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
langCode={getLanguage().code}
|
langCode={getLanguage().code}
|
||||||
isCollaborating={this.props.isCollaborating}
|
isCollaborating={this.props.isCollaborating}
|
||||||
renderTopRightUI={renderTopRightUI}
|
renderTopRightUI={renderTopRightUI}
|
||||||
renderCustomFooter={renderFooter}
|
|
||||||
renderCustomStats={renderCustomStats}
|
renderCustomStats={renderCustomStats}
|
||||||
viewModeEnabled={viewModeEnabled}
|
viewModeEnabled={viewModeEnabled}
|
||||||
showExitZenModeBtn={
|
showExitZenModeBtn={
|
||||||
@ -537,7 +532,9 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
library={this.library}
|
library={this.library}
|
||||||
id={this.id}
|
id={this.id}
|
||||||
onImageAction={this.onImageAction}
|
onImageAction={this.onImageAction}
|
||||||
/>
|
>
|
||||||
|
{this.props.children}
|
||||||
|
</LayerUI>
|
||||||
<div className="excalidraw-textEditorContainer" />
|
<div className="excalidraw-textEditorContainer" />
|
||||||
<div className="excalidraw-contextMenuContainer" />
|
<div className="excalidraw-contextMenuContainer" />
|
||||||
{selectedElement.length === 1 && this.state.showHyperlinkPopup && (
|
{selectedElement.length === 1 && this.state.showHyperlinkPopup && (
|
||||||
|
@ -58,7 +58,6 @@ interface LayerUIProps {
|
|||||||
langCode: Language["code"];
|
langCode: Language["code"];
|
||||||
isCollaborating: boolean;
|
isCollaborating: boolean;
|
||||||
renderTopRightUI?: ExcalidrawProps["renderTopRightUI"];
|
renderTopRightUI?: ExcalidrawProps["renderTopRightUI"];
|
||||||
renderCustomFooter?: ExcalidrawProps["renderFooter"];
|
|
||||||
renderCustomStats?: ExcalidrawProps["renderCustomStats"];
|
renderCustomStats?: ExcalidrawProps["renderCustomStats"];
|
||||||
viewModeEnabled: boolean;
|
viewModeEnabled: boolean;
|
||||||
libraryReturnUrl: ExcalidrawProps["libraryReturnUrl"];
|
libraryReturnUrl: ExcalidrawProps["libraryReturnUrl"];
|
||||||
@ -67,6 +66,7 @@ interface LayerUIProps {
|
|||||||
library: Library;
|
library: Library;
|
||||||
id: string;
|
id: string;
|
||||||
onImageAction: (data: { insertOnCanvasDirectly: boolean }) => void;
|
onImageAction: (data: { insertOnCanvasDirectly: boolean }) => void;
|
||||||
|
children?: React.ReactNode;
|
||||||
}
|
}
|
||||||
const LayerUI = ({
|
const LayerUI = ({
|
||||||
actionManager,
|
actionManager,
|
||||||
@ -85,7 +85,6 @@ const LayerUI = ({
|
|||||||
toggleZenMode,
|
toggleZenMode,
|
||||||
isCollaborating,
|
isCollaborating,
|
||||||
renderTopRightUI,
|
renderTopRightUI,
|
||||||
renderCustomFooter,
|
|
||||||
renderCustomStats,
|
renderCustomStats,
|
||||||
viewModeEnabled,
|
viewModeEnabled,
|
||||||
libraryReturnUrl,
|
libraryReturnUrl,
|
||||||
@ -94,6 +93,7 @@ const LayerUI = ({
|
|||||||
library,
|
library,
|
||||||
id,
|
id,
|
||||||
onImageAction,
|
onImageAction,
|
||||||
|
children,
|
||||||
}: LayerUIProps) => {
|
}: LayerUIProps) => {
|
||||||
const device = useDevice();
|
const device = useDevice();
|
||||||
|
|
||||||
@ -454,7 +454,7 @@ const LayerUI = ({
|
|||||||
},
|
},
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{renderCustomFooter?.(false, appState)}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className={clsx(
|
className={clsx(
|
||||||
@ -542,7 +542,6 @@ const LayerUI = ({
|
|||||||
onPenModeToggle={onPenModeToggle}
|
onPenModeToggle={onPenModeToggle}
|
||||||
canvas={canvas}
|
canvas={canvas}
|
||||||
isCollaborating={isCollaborating}
|
isCollaborating={isCollaborating}
|
||||||
renderCustomFooter={renderCustomFooter}
|
|
||||||
viewModeEnabled={viewModeEnabled}
|
viewModeEnabled={viewModeEnabled}
|
||||||
showThemeBtn={showThemeBtn}
|
showThemeBtn={showThemeBtn}
|
||||||
onImageAction={onImageAction}
|
onImageAction={onImageAction}
|
||||||
@ -606,7 +605,6 @@ const areEqual = (prev: LayerUIProps, next: LayerUIProps) => {
|
|||||||
|
|
||||||
const keys = Object.keys(prevAppState) as (keyof Partial<AppState>)[];
|
const keys = Object.keys(prevAppState) as (keyof Partial<AppState>)[];
|
||||||
return (
|
return (
|
||||||
prev.renderCustomFooter === next.renderCustomFooter &&
|
|
||||||
prev.langCode === next.langCode &&
|
prev.langCode === next.langCode &&
|
||||||
prev.elements === next.elements &&
|
prev.elements === next.elements &&
|
||||||
prev.files === next.files &&
|
prev.files === next.files &&
|
||||||
|
79
src/excalidraw-app/components/Footer.tsx
Normal file
79
src/excalidraw-app/components/Footer.tsx
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
import { isExcalidrawPlusSignedUser, PlusAppLinkJSX, PlusLPLinkJSX } from "..";
|
||||||
|
import { shield } from "../../components/icons";
|
||||||
|
import { Tooltip } from "../../components/Tooltip";
|
||||||
|
import { t } from "../../i18n";
|
||||||
|
import { languages } from "../../packages/excalidraw/index";
|
||||||
|
import { LanguageList } from "./LanguageList";
|
||||||
|
|
||||||
|
const EncryptedIcon = () => (
|
||||||
|
<a
|
||||||
|
className="encrypted-icon tooltip"
|
||||||
|
href="https://blog.excalidraw.com/end-to-end-encryption/"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
aria-label={t("encrypted.link")}
|
||||||
|
>
|
||||||
|
<Tooltip label={t("encrypted.tooltip")} long={true}>
|
||||||
|
{shield}
|
||||||
|
</Tooltip>
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
|
||||||
|
const Footer = ({
|
||||||
|
isMobile,
|
||||||
|
langCode,
|
||||||
|
onLangChange,
|
||||||
|
}: {
|
||||||
|
isMobile: boolean;
|
||||||
|
langCode: string;
|
||||||
|
onLangChange: (langCode: string) => void;
|
||||||
|
}) => {
|
||||||
|
if (isMobile) {
|
||||||
|
const isTinyDevice = window.innerWidth < 362;
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: isTinyDevice ? "column" : "row",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<fieldset>
|
||||||
|
<legend>{t("labels.language")}</legend>
|
||||||
|
<LanguageList
|
||||||
|
onChange={onLangChange}
|
||||||
|
languages={languages}
|
||||||
|
currentLangCode={langCode}
|
||||||
|
/>
|
||||||
|
</fieldset>
|
||||||
|
{/* FIXME remove after 2021-05-20 */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
width: "24ch",
|
||||||
|
fontSize: "0.7em",
|
||||||
|
textAlign: "center",
|
||||||
|
marginTop: isTinyDevice ? 16 : undefined,
|
||||||
|
marginLeft: "auto",
|
||||||
|
marginRight: isTinyDevice ? "auto" : undefined,
|
||||||
|
padding: isExcalidrawPlusSignedUser ? undefined : "4px 2px",
|
||||||
|
border: isExcalidrawPlusSignedUser ? undefined : "1px dashed #aaa",
|
||||||
|
borderRadius: 12,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{isExcalidrawPlusSignedUser ? PlusAppLinkJSX : PlusLPLinkJSX}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<EncryptedIcon />
|
||||||
|
<LanguageList
|
||||||
|
onChange={onLangChange}
|
||||||
|
languages={languages}
|
||||||
|
currentLangCode={langCode}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Footer;
|
@ -19,11 +19,7 @@ import {
|
|||||||
} from "../element/types";
|
} from "../element/types";
|
||||||
import { useCallbackRefState } from "../hooks/useCallbackRefState";
|
import { useCallbackRefState } from "../hooks/useCallbackRefState";
|
||||||
import { t } from "../i18n";
|
import { t } from "../i18n";
|
||||||
import {
|
import { Excalidraw, defaultLang } from "../packages/excalidraw/index";
|
||||||
Excalidraw,
|
|
||||||
defaultLang,
|
|
||||||
languages,
|
|
||||||
} from "../packages/excalidraw/index";
|
|
||||||
import {
|
import {
|
||||||
AppState,
|
AppState,
|
||||||
LibraryItems,
|
LibraryItems,
|
||||||
@ -51,7 +47,6 @@ import Collab, {
|
|||||||
collabDialogShownAtom,
|
collabDialogShownAtom,
|
||||||
isCollaboratingAtom,
|
isCollaboratingAtom,
|
||||||
} from "./collab/Collab";
|
} from "./collab/Collab";
|
||||||
import { LanguageList } from "./components/LanguageList";
|
|
||||||
import {
|
import {
|
||||||
exportToBackend,
|
exportToBackend,
|
||||||
getCollaborationLinkData,
|
getCollaborationLinkData,
|
||||||
@ -65,8 +60,6 @@ import {
|
|||||||
} from "./data/localStorage";
|
} from "./data/localStorage";
|
||||||
import CustomStats from "./CustomStats";
|
import CustomStats from "./CustomStats";
|
||||||
import { restore, restoreAppState, RestoredDataState } from "../data/restore";
|
import { restore, restoreAppState, RestoredDataState } from "../data/restore";
|
||||||
import { Tooltip } from "../components/Tooltip";
|
|
||||||
import { shield } from "../components/icons";
|
|
||||||
|
|
||||||
import "./index.scss";
|
import "./index.scss";
|
||||||
import { ExportToExcalidrawPlus } from "./components/ExportToExcalidrawPlus";
|
import { ExportToExcalidrawPlus } from "./components/ExportToExcalidrawPlus";
|
||||||
@ -82,8 +75,9 @@ import { Provider, useAtom } from "jotai";
|
|||||||
import { jotaiStore, useAtomWithInitialValue } from "../jotai";
|
import { jotaiStore, useAtomWithInitialValue } from "../jotai";
|
||||||
import { reconcileElements } from "./collab/reconciliation";
|
import { reconcileElements } from "./collab/reconciliation";
|
||||||
import { parseLibraryTokensFromUrl, useHandleLibrary } from "../data/library";
|
import { parseLibraryTokensFromUrl, useHandleLibrary } from "../data/library";
|
||||||
|
import Footer from "./components/Footer";
|
||||||
|
|
||||||
const isExcalidrawPlusSignedUser = document.cookie.includes(
|
export const isExcalidrawPlusSignedUser = document.cookie.includes(
|
||||||
COOKIES.AUTH_STATE_COOKIE,
|
COOKIES.AUTH_STATE_COOKIE,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -197,7 +191,7 @@ const initializeScene = async (opts: {
|
|||||||
return { scene: null, isExternalScene: false };
|
return { scene: null, isExternalScene: false };
|
||||||
};
|
};
|
||||||
|
|
||||||
const PlusLPLinkJSX = (
|
export const PlusLPLinkJSX = (
|
||||||
<p style={{ direction: "ltr", unicodeBidi: "embed" }}>
|
<p style={{ direction: "ltr", unicodeBidi: "embed" }}>
|
||||||
Introducing Excalidraw+
|
Introducing Excalidraw+
|
||||||
<br />
|
<br />
|
||||||
@ -211,7 +205,7 @@ const PlusLPLinkJSX = (
|
|||||||
</p>
|
</p>
|
||||||
);
|
);
|
||||||
|
|
||||||
const PlusAppLinkJSX = (
|
export const PlusAppLinkJSX = (
|
||||||
<a
|
<a
|
||||||
href={`${process.env.REACT_APP_PLUS_APP}/#excalidraw-redirect`}
|
href={`${process.env.REACT_APP_PLUS_APP}/#excalidraw-redirect`}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
@ -588,73 +582,6 @@ const ExcalidrawWrapper = () => {
|
|||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
const renderFooter = useCallback(
|
|
||||||
(isMobile: boolean) => {
|
|
||||||
const renderEncryptedIcon = () => (
|
|
||||||
<a
|
|
||||||
className="encrypted-icon tooltip"
|
|
||||||
href="https://blog.excalidraw.com/end-to-end-encryption/"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
aria-label={t("encrypted.link")}
|
|
||||||
>
|
|
||||||
<Tooltip label={t("encrypted.tooltip")} long={true}>
|
|
||||||
{shield}
|
|
||||||
</Tooltip>
|
|
||||||
</a>
|
|
||||||
);
|
|
||||||
|
|
||||||
const renderLanguageList = () => (
|
|
||||||
<LanguageList
|
|
||||||
onChange={(langCode) => setLangCode(langCode)}
|
|
||||||
languages={languages}
|
|
||||||
currentLangCode={langCode}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
if (isMobile) {
|
|
||||||
const isTinyDevice = window.innerWidth < 362;
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: isTinyDevice ? "column" : "row",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<fieldset>
|
|
||||||
<legend>{t("labels.language")}</legend>
|
|
||||||
{renderLanguageList()}
|
|
||||||
</fieldset>
|
|
||||||
{/* FIXME remove after 2021-05-20 */}
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
width: "24ch",
|
|
||||||
fontSize: "0.7em",
|
|
||||||
textAlign: "center",
|
|
||||||
marginTop: isTinyDevice ? 16 : undefined,
|
|
||||||
marginLeft: "auto",
|
|
||||||
marginRight: isTinyDevice ? "auto" : undefined,
|
|
||||||
padding: isExcalidrawPlusSignedUser ? undefined : "4px 2px",
|
|
||||||
border: isExcalidrawPlusSignedUser
|
|
||||||
? undefined
|
|
||||||
: "1px dashed #aaa",
|
|
||||||
borderRadius: 12,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{isExcalidrawPlusSignedUser ? PlusAppLinkJSX : PlusLPLinkJSX}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{renderEncryptedIcon()}
|
|
||||||
{renderLanguageList()}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
[langCode],
|
|
||||||
);
|
|
||||||
|
|
||||||
const renderCustomStats = () => {
|
const renderCustomStats = () => {
|
||||||
return (
|
return (
|
||||||
<CustomStats
|
<CustomStats
|
||||||
@ -710,14 +637,19 @@ const ExcalidrawWrapper = () => {
|
|||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
renderTopRightUI={renderTopRightUI}
|
renderTopRightUI={renderTopRightUI}
|
||||||
renderFooter={renderFooter}
|
|
||||||
langCode={langCode}
|
langCode={langCode}
|
||||||
renderCustomStats={renderCustomStats}
|
renderCustomStats={renderCustomStats}
|
||||||
detectScroll={false}
|
detectScroll={false}
|
||||||
handleKeyboardGlobally={true}
|
handleKeyboardGlobally={true}
|
||||||
onLibraryChange={onLibraryChange}
|
onLibraryChange={onLibraryChange}
|
||||||
autoFocus={true}
|
autoFocus={true}
|
||||||
|
>
|
||||||
|
<Footer
|
||||||
|
isMobile={false}
|
||||||
|
langCode={langCode}
|
||||||
|
onLangChange={(langCode) => setLangCode(langCode)}
|
||||||
/>
|
/>
|
||||||
|
</Excalidraw>
|
||||||
{excalidrawAPI && <Collab excalidrawAPI={excalidrawAPI} />}
|
{excalidrawAPI && <Collab excalidrawAPI={excalidrawAPI} />}
|
||||||
{errorMessage && (
|
{errorMessage && (
|
||||||
<ErrorDialog
|
<ErrorDialog
|
||||||
|
@ -654,11 +654,12 @@ export default function App() {
|
|||||||
name="Custom name of drawing"
|
name="Custom name of drawing"
|
||||||
UIOptions={{ canvasActions: { loadScene: false } }}
|
UIOptions={{ canvasActions: { loadScene: false } }}
|
||||||
renderTopRightUI={renderTopRightUI}
|
renderTopRightUI={renderTopRightUI}
|
||||||
renderFooter={renderFooter}
|
|
||||||
onLinkOpen={onLinkOpen}
|
onLinkOpen={onLinkOpen}
|
||||||
onPointerDown={onPointerDown}
|
onPointerDown={onPointerDown}
|
||||||
onScrollChange={rerenderCommentIcons}
|
onScrollChange={rerenderCommentIcons}
|
||||||
/>
|
>
|
||||||
|
{renderFooter()}
|
||||||
|
</Excalidraw>
|
||||||
{Object.keys(commentIcons || []).length > 0 && renderCommentIcons()}
|
{Object.keys(commentIcons || []).length > 0 && renderCommentIcons()}
|
||||||
{comment && renderComment()}
|
{comment && renderComment()}
|
||||||
</div>
|
</div>
|
||||||
|
@ -21,7 +21,6 @@ const ExcalidrawBase = (props: ExcalidrawProps) => {
|
|||||||
isCollaborating = false,
|
isCollaborating = false,
|
||||||
onPointerUpdate,
|
onPointerUpdate,
|
||||||
renderTopRightUI,
|
renderTopRightUI,
|
||||||
renderFooter,
|
|
||||||
langCode = defaultLang.code,
|
langCode = defaultLang.code,
|
||||||
viewModeEnabled,
|
viewModeEnabled,
|
||||||
zenModeEnabled,
|
zenModeEnabled,
|
||||||
@ -39,6 +38,7 @@ const ExcalidrawBase = (props: ExcalidrawProps) => {
|
|||||||
onLinkOpen,
|
onLinkOpen,
|
||||||
onPointerDown,
|
onPointerDown,
|
||||||
onScrollChange,
|
onScrollChange,
|
||||||
|
children,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const canvasActions = props.UIOptions?.canvasActions;
|
const canvasActions = props.UIOptions?.canvasActions;
|
||||||
@ -86,7 +86,6 @@ const ExcalidrawBase = (props: ExcalidrawProps) => {
|
|||||||
isCollaborating={isCollaborating}
|
isCollaborating={isCollaborating}
|
||||||
onPointerUpdate={onPointerUpdate}
|
onPointerUpdate={onPointerUpdate}
|
||||||
renderTopRightUI={renderTopRightUI}
|
renderTopRightUI={renderTopRightUI}
|
||||||
renderFooter={renderFooter}
|
|
||||||
langCode={langCode}
|
langCode={langCode}
|
||||||
viewModeEnabled={viewModeEnabled}
|
viewModeEnabled={viewModeEnabled}
|
||||||
zenModeEnabled={zenModeEnabled}
|
zenModeEnabled={zenModeEnabled}
|
||||||
@ -105,7 +104,9 @@ const ExcalidrawBase = (props: ExcalidrawProps) => {
|
|||||||
onLinkOpen={onLinkOpen}
|
onLinkOpen={onLinkOpen}
|
||||||
onPointerDown={onPointerDown}
|
onPointerDown={onPointerDown}
|
||||||
onScrollChange={onScrollChange}
|
onScrollChange={onScrollChange}
|
||||||
/>
|
>
|
||||||
|
{children}
|
||||||
|
</App>
|
||||||
</Provider>
|
</Provider>
|
||||||
</InitializeApp>
|
</InitializeApp>
|
||||||
);
|
);
|
||||||
|
@ -280,7 +280,6 @@ export interface ExcalidrawProps {
|
|||||||
isMobile: boolean,
|
isMobile: boolean,
|
||||||
appState: AppState,
|
appState: AppState,
|
||||||
) => JSX.Element | null;
|
) => JSX.Element | null;
|
||||||
renderFooter?: (isMobile: boolean, appState: AppState) => JSX.Element | null;
|
|
||||||
langCode?: Language["code"];
|
langCode?: Language["code"];
|
||||||
viewModeEnabled?: boolean;
|
viewModeEnabled?: boolean;
|
||||||
zenModeEnabled?: boolean;
|
zenModeEnabled?: boolean;
|
||||||
@ -312,6 +311,7 @@ export interface ExcalidrawProps {
|
|||||||
pointerDownState: PointerDownState,
|
pointerDownState: PointerDownState,
|
||||||
) => void;
|
) => void;
|
||||||
onScrollChange?: (scrollX: number, scrollY: number) => void;
|
onScrollChange?: (scrollX: number, scrollY: number) => void;
|
||||||
|
children?: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SceneData = {
|
export type SceneData = {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user