feat: add `renderTopRight` prop & remove GH corner from core (#3539)

* feat: add `renderTopRight` prop & remove GH corner from core

* reuse `--space-factor` var

* update readme & changelog
pull/3540/head
David Luzar 4 years ago committed by GitHub
parent f28f7ffb6e
commit bac76778ce
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -452,6 +452,7 @@ class App extends React.Component<AppProps, AppState> {
const { const {
onCollabButtonClick, onCollabButtonClick,
onExportToBackend, onExportToBackend,
renderTopRight,
renderFooter, renderFooter,
renderCustomStats, renderCustomStats,
} = this.props; } = this.props;
@ -492,6 +493,7 @@ class App extends React.Component<AppProps, AppState> {
langCode={getLanguage().code} langCode={getLanguage().code}
isCollaborating={this.props.isCollaborating || false} isCollaborating={this.props.isCollaborating || false}
onExportToBackend={onExportToBackend} onExportToBackend={onExportToBackend}
renderTopRight={renderTopRight}
renderCustomFooter={renderFooter} renderCustomFooter={renderFooter}
viewModeEnabled={viewModeEnabled} viewModeEnabled={viewModeEnabled}
showExitZenModeBtn={ showExitZenModeBtn={

@ -1,6 +1,5 @@
.excalidraw { .excalidraw {
.FixedSideContainer { .FixedSideContainer {
--margin: 0.25rem;
position: absolute; position: absolute;
pointer-events: none; pointer-events: none;
} }
@ -10,9 +9,9 @@
} }
.FixedSideContainer_side_top { .FixedSideContainer_side_top {
left: var(--margin); left: var(--space-factor);
top: var(--margin); top: var(--space-factor);
right: var(--margin); right: var(--space-factor);
z-index: 2; z-index: 2;
} }
@ -23,16 +22,16 @@
/* TODO: if these are used, make sure to implement RTL support /* TODO: if these are used, make sure to implement RTL support
.FixedSideContainer_side_left { .FixedSideContainer_side_left {
left: var(--margin); left: var(--space-factor);
top: var(--margin); top: var(--space-factor);
bottom: var(--margin); bottom: var(--space-factor);
z-index: 1; z-index: 1;
} }
.FixedSideContainer_side_right { .FixedSideContainer_side_right {
right: var(--margin); right: var(--space-factor);
top: var(--margin); top: var(--space-factor);
bottom: var(--margin); bottom: var(--space-factor);
z-index: 3; z-index: 3;
} }
*/ */

@ -55,19 +55,8 @@
} }
} }
&__github-corner { &__top-right {
top: 0; display: flex;
:root[dir="ltr"] & {
right: 0;
}
:root[dir="rtl"] & {
left: 0;
}
position: absolute;
width: 40px;
} }
&__footer { &__footer {

@ -30,7 +30,6 @@ import CollabButton from "./CollabButton";
import { ErrorDialog } from "./ErrorDialog"; import { ErrorDialog } from "./ErrorDialog";
import { ExportCB, ExportDialog } from "./ExportDialog"; import { ExportCB, ExportDialog } from "./ExportDialog";
import { FixedSideContainer } from "./FixedSideContainer"; import { FixedSideContainer } from "./FixedSideContainer";
import { GitHubCorner } from "./GitHubCorner";
import { HintViewer } from "./HintViewer"; import { HintViewer } from "./HintViewer";
import { exportFile, load, shield, trash } from "./icons"; import { exportFile, load, shield, trash } from "./icons";
import { Island } from "./Island"; import { Island } from "./Island";
@ -68,6 +67,7 @@ interface LayerUIProps {
appState: AppState, appState: AppState,
canvas: HTMLCanvasElement | null, canvas: HTMLCanvasElement | null,
) => void; ) => void;
renderTopRight?: (isMobile: boolean, appState: AppState) => JSX.Element;
renderCustomFooter?: (isMobile: boolean) => JSX.Element; renderCustomFooter?: (isMobile: boolean) => JSX.Element;
viewModeEnabled: boolean; viewModeEnabled: boolean;
libraryReturnUrl: ExcalidrawProps["libraryReturnUrl"]; libraryReturnUrl: ExcalidrawProps["libraryReturnUrl"];
@ -371,6 +371,7 @@ const LayerUI = ({
toggleZenMode, toggleZenMode,
isCollaborating, isCollaborating,
onExportToBackend, onExportToBackend,
renderTopRight,
renderCustomFooter, renderCustomFooter,
viewModeEnabled, viewModeEnabled,
libraryReturnUrl, libraryReturnUrl,
@ -604,11 +605,15 @@ const LayerUI = ({
)} )}
</Section> </Section>
)} )}
<UserList <div
className={clsx("zen-mode-transition", { className={clsx(
"layer-ui__wrapper__top-right zen-mode-transition",
{
"transition-right": zenModeEnabled, "transition-right": zenModeEnabled,
})} },
)}
> >
<UserList>
{appState.collaborators.size > 0 && {appState.collaborators.size > 0 &&
Array.from(appState.collaborators) Array.from(appState.collaborators)
// Collaborator is either not initialized or is actually the current user. // Collaborator is either not initialized or is actually the current user.
@ -622,6 +627,8 @@ const LayerUI = ({
</Tooltip> </Tooltip>
))} ))}
</UserList> </UserList>
{renderTopRight?.(isMobile, appState)}
</div>
</div> </div>
</FixedSideContainer> </FixedSideContainer>
); );
@ -649,20 +656,6 @@ const LayerUI = ({
); );
}; };
const renderGitHubCorner = () => {
return (
<aside
className={clsx(
"layer-ui__wrapper__github-corner zen-mode-transition",
{
"transition-right": zenModeEnabled,
},
)}
>
<GitHubCorner theme={appState.theme} />
</aside>
);
};
const renderFooter = () => ( const renderFooter = () => (
<footer role="contentinfo" className="layer-ui__wrapper__footer"> <footer role="contentinfo" className="layer-ui__wrapper__footer">
<div <div
@ -746,7 +739,6 @@ const LayerUI = ({
{dialogs} {dialogs}
{renderFixedSideContainer()} {renderFixedSideContainer()}
{renderBottomAppMenu()} {renderBottomAppMenu()}
{renderGitHubCorner()}
{renderFooter()} {renderFooter()}
{appState.scrolledOutside && ( {appState.scrolledOutside && (
<button <button

@ -2,7 +2,8 @@
.UserList { .UserList {
pointer-events: none; pointer-events: none;
/*github corner*/ /*github corner*/
padding: var(--space-factor) 40px var(--space-factor) var(--space-factor); padding: var(--space-factor) var(--space-factor) var(--space-factor)
var(--space-factor);
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
justify-content: flex-end; justify-content: flex-end;

@ -500,20 +500,6 @@
} }
} }
.github-corner {
position: absolute;
top: 0;
z-index: 2;
:root[dir="ltr"] & {
right: 0;
}
:root[dir="rtl"] & {
left: 0;
}
}
.zen-mode-visibility { .zen-mode-visibility {
visibility: visible; visibility: visible;
opacity: 1; opacity: 1;

@ -14,6 +14,7 @@
--focus-highlight-color: #{$oc-blue-2}; --focus-highlight-color: #{$oc-blue-2};
--icon-fill-color: #{$oc-black}; --icon-fill-color: #{$oc-black};
--icon-green-fill-color: #{$oc-green-9}; --icon-green-fill-color: #{$oc-green-9};
--default-bg-color: #{$oc-white};
--input-bg-color: #{$oc-white}; --input-bg-color: #{$oc-white};
--input-border-color: #{$oc-gray-3}; --input-border-color: #{$oc-gray-3};
--input-hover-bg-color: #{$oc-gray-1}; --input-hover-bg-color: #{$oc-gray-1};
@ -56,6 +57,7 @@
--focus-highlight-color: #{$oc-blue-6}; --focus-highlight-color: #{$oc-blue-6};
--icon-fill-color: #{$oc-gray-4}; --icon-fill-color: #{$oc-gray-4};
--icon-green-fill-color: #{$oc-green-4}; --icon-green-fill-color: #{$oc-green-4};
--default-bg-color: #121212;
--input-bg-color: #121212; --input-bg-color: #121212;
--input-border-color: #2e2e2e; --input-border-color: #2e2e2e;
--input-hover-bg-color: #181818; --input-hover-bg-color: #181818;

@ -3,13 +3,19 @@ import React from "react";
// https://github.com/tholman/github-corners // https://github.com/tholman/github-corners
export const GitHubCorner = React.memo( export const GitHubCorner = React.memo(
({ theme }: { theme: "light" | "dark" }) => ( ({ theme, dir }: { theme: "light" | "dark"; dir: string }) => (
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
width="40" width="40"
height="40" height="40"
viewBox="0 0 250 250" viewBox="0 0 250 250"
className="github-corner rtl-mirror" className="rtl-mirror"
style={{
marginTop: "calc(var(--space-factor) * -1)",
[dir === "rtl"
? "marginLeft"
: "marginRight"]: "calc(var(--space-factor) * -1)",
}}
> >
<a <a
href="https://github.com/excalidraw/excalidraw" href="https://github.com/excalidraw/excalidraw"
@ -19,18 +25,18 @@ export const GitHubCorner = React.memo(
> >
<path <path
d="M0 0l115 115h15l12 27 108 108V0z" d="M0 0l115 115h15l12 27 108 108V0z"
fill={theme === "light" ? oc.gray[6] : oc.gray[8]} fill={theme === "light" ? oc.gray[6] : oc.gray[7]}
/> />
<path <path
className="octo-arm" className="octo-arm"
d="M128 109c-15-9-9-19-9-19 3-7 2-11 2-11-1-7 3-2 3-2 4 5 2 11 2 11-3 10 5 15 9 16" d="M128 109c-15-9-9-19-9-19 3-7 2-11 2-11-1-7 3-2 3-2 4 5 2 11 2 11-3 10 5 15 9 16"
style={{ transformOrigin: "130px 106px" }} style={{ transformOrigin: "130px 106px" }}
fill={theme === "light" ? oc.white : oc.black} fill={theme === "light" ? oc.white : "var(--default-bg-color)"}
/> />
<path <path
className="octo-body" className="octo-body"
d="M115 115s4 2 5 0l14-14c3-2 6-3 8-3-8-11-15-24 2-41 5-5 10-7 16-7 1-2 3-7 12-11 0 0 5 3 7 16 4 2 8 5 12 9s7 8 9 12c14 3 17 7 17 7-4 8-9 11-11 11 0 6-2 11-7 16-16 16-30 10-41 2 0 3-1 7-5 11l-12 11c-1 1 1 5 1 5z" d="M115 115s4 2 5 0l14-14c3-2 6-3 8-3-8-11-15-24 2-41 5-5 10-7 16-7 1-2 3-7 12-11 0 0 5 3 7 16 4 2 8 5 12 9s7 8 9 12c14 3 17 7 17 7-4 8-9 11-11 11 0 6-2 11-7 16-16 16-30 10-41 2 0 3-1 7-5 11l-12 11c-1 1 1 5 1 5z"
fill={theme === "light" ? oc.white : oc.black} fill={theme === "light" ? oc.white : "var(--default-bg-color)"}
/> />
</a> </a>
</svg> </svg>

@ -52,6 +52,7 @@ import {
} from "./data/localStorage"; } from "./data/localStorage";
import CustomStats from "./CustomStats"; import CustomStats from "./CustomStats";
import { RestoredDataState } from "../data/restore"; import { RestoredDataState } from "../data/restore";
import { GitHubCorner } from "./components/GitHubCorner";
const languageDetector = new LanguageDetector(); const languageDetector = new LanguageDetector();
languageDetector.init({ languageDetector.init({
@ -290,6 +291,17 @@ const ExcalidrawWrapper = () => {
} }
}; };
const renderTopRight = useCallback(
(isMobile: boolean, appState: AppState) => {
return (
<div>
<GitHubCorner theme={appState.theme} dir={document.dir} />
</div>
);
},
[],
);
const renderFooter = useCallback( const renderFooter = useCallback(
(isMobile: boolean) => { (isMobile: boolean) => {
const renderLanguageList = () => ( const renderLanguageList = () => (
@ -331,6 +343,7 @@ const ExcalidrawWrapper = () => {
const serializedItems = JSON.stringify(items); const serializedItems = JSON.stringify(items);
localStorage.setItem(STORAGE_KEYS.LOCAL_STORAGE_LIBRARY, serializedItems); localStorage.setItem(STORAGE_KEYS.LOCAL_STORAGE_LIBRARY, serializedItems);
}; };
return ( return (
<> <>
<Excalidraw <Excalidraw
@ -341,6 +354,7 @@ const ExcalidrawWrapper = () => {
isCollaborating={collabAPI?.isCollaborating()} isCollaborating={collabAPI?.isCollaborating()}
onPointerUpdate={collabAPI?.onPointerUpdate} onPointerUpdate={collabAPI?.onPointerUpdate}
onExportToBackend={onExportToBackend} onExportToBackend={onExportToBackend}
renderTopRight={renderTopRight}
renderFooter={renderFooter} renderFooter={renderFooter}
langCode={langCode} langCode={langCode}
renderCustomStats={renderCustomStats} renderCustomStats={renderCustomStats}

@ -11,6 +11,14 @@ The change should be grouped under one of the below section and must contain PR
Please add the latest change on the top under the correct section. Please add the latest change on the top under the correct section.
--> -->
## Unreleased
## Excalidraw API
- Add support to render custom UI in the top right corner via [`renderTopRight`](https://github.com/excalidraw/excalidraw/blob/master/src/packages/excalidraw/README.md#renderTopRight) prop [#3539](https://github.com/excalidraw/excalidraw/pull/3539).
This also removes the GitHub icon, keeping it local to the https://excalidraw.com app.
## 0.7.0 (2021-04-26) ## 0.7.0 (2021-04-26)
## Excalidraw API ## Excalidraw API

@ -355,6 +355,7 @@ To view the full example visit :point_down:
| [`onPointerUpdate`](#onPointerUpdate) | Function | | Callback triggered when mouse pointer is updated. | | [`onPointerUpdate`](#onPointerUpdate) | Function | | Callback triggered when mouse pointer is updated. |
| [`onExportToBackend`](#onExportToBackend) | Function | | Callback triggered when link button is clicked on export dialog | | [`onExportToBackend`](#onExportToBackend) | Function | | Callback triggered when link button is clicked on export dialog |
| [`langCode`](#langCode) | string | `en` | Language code string | | [`langCode`](#langCode) | string | `en` | Language code string |
| [`renderTopRight `](#renderTopRight) | Function | | Function that renders custom UI in top right corner |
| [`renderFooter `](#renderFooter) | Function | | Function that renders custom UI footer | | [`renderFooter `](#renderFooter) | Function | | Function that renders custom UI footer |
| [`renderCustomStats`](#renderCustomStats) | Function | | Function that can be used to render custom stats on the stats dialog. | | [`renderCustomStats`](#renderCustomStats) | Function | | Function that can be used to render custom stats on the stats dialog. |
| [`viewModeEnabled`](#viewModeEnabled) | boolean | | This implies if the app is in view mode. | | [`viewModeEnabled`](#viewModeEnabled) | boolean | | This implies if the app is in view mode. |
@ -502,9 +503,13 @@ import { defaultLang, languages } from "@excalidraw/excalidraw";
| defaultLang | string | | defaultLang | string |
| languages | [Language[]](https://github.com/excalidraw/excalidraw/blob/master/src/i18n.ts#L8) | | languages | [Language[]](https://github.com/excalidraw/excalidraw/blob/master/src/i18n.ts#L8) |
#### `renderTopRight`
A function returning JSX to render custom UI in the top right corner of the app.
#### `renderFooter` #### `renderFooter`
A function that renders (returns JSX) custom UI footer. For example, you can use this to render a language picker that was previously being rendered by Excalidraw itself (for now, you'll need to implement your own language picker). A function returning JSX to render custom UI footer. For example, you can use this to render a language picker that was previously being rendered by Excalidraw itself (for now, you'll need to implement your own language picker).
#### `renderCustomStats` #### `renderCustomStats`

@ -20,6 +20,7 @@ const Excalidraw = (props: ExcalidrawProps) => {
isCollaborating, isCollaborating,
onPointerUpdate, onPointerUpdate,
onExportToBackend, onExportToBackend,
renderTopRight,
renderFooter, renderFooter,
langCode = defaultLang.code, langCode = defaultLang.code,
viewModeEnabled, viewModeEnabled,
@ -72,6 +73,7 @@ const Excalidraw = (props: ExcalidrawProps) => {
isCollaborating={isCollaborating} isCollaborating={isCollaborating}
onPointerUpdate={onPointerUpdate} onPointerUpdate={onPointerUpdate}
onExportToBackend={onExportToBackend} onExportToBackend={onExportToBackend}
renderTopRight={renderTopRight}
renderFooter={renderFooter} renderFooter={renderFooter}
langCode={langCode} langCode={langCode}
viewModeEnabled={viewModeEnabled} viewModeEnabled={viewModeEnabled}

@ -182,6 +182,7 @@ export interface ExcalidrawProps {
data: ClipboardData, data: ClipboardData,
event: ClipboardEvent | null, event: ClipboardEvent | null,
) => Promise<boolean> | boolean; ) => Promise<boolean> | boolean;
renderTopRight?: (isMobile: boolean, appState: AppState) => JSX.Element;
renderFooter?: (isMobile: boolean) => JSX.Element; renderFooter?: (isMobile: boolean) => JSX.Element;
langCode?: Language["code"]; langCode?: Language["code"];
viewModeEnabled?: boolean; viewModeEnabled?: boolean;

Loading…
Cancel
Save