瀏覽代碼

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
David Luzar 4 年之前
父節點
當前提交
bac76778ce

+ 2 - 0
src/components/App.tsx

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

+ 9 - 10
src/components/FixedSideContainer.scss

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

+ 2 - 13
src/components/LayerUI.scss

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

+ 25 - 33
src/components/LayerUI.tsx

@@ -30,7 +30,6 @@ import CollabButton from "./CollabButton";
 import { ErrorDialog } from "./ErrorDialog";
 import { ExportCB, ExportDialog } from "./ExportDialog";
 import { FixedSideContainer } from "./FixedSideContainer";
-import { GitHubCorner } from "./GitHubCorner";
 import { HintViewer } from "./HintViewer";
 import { exportFile, load, shield, trash } from "./icons";
 import { Island } from "./Island";
@@ -68,6 +67,7 @@ interface LayerUIProps {
     appState: AppState,
     canvas: HTMLCanvasElement | null,
   ) => void;
+  renderTopRight?: (isMobile: boolean, appState: AppState) => JSX.Element;
   renderCustomFooter?: (isMobile: boolean) => JSX.Element;
   viewModeEnabled: boolean;
   libraryReturnUrl: ExcalidrawProps["libraryReturnUrl"];
@@ -371,6 +371,7 @@ const LayerUI = ({
   toggleZenMode,
   isCollaborating,
   onExportToBackend,
+  renderTopRight,
   renderCustomFooter,
   viewModeEnabled,
   libraryReturnUrl,
@@ -604,24 +605,30 @@ const LayerUI = ({
               )}
             </Section>
           )}
-          <UserList
-            className={clsx("zen-mode-transition", {
-              "transition-right": zenModeEnabled,
-            })}
+          <div
+            className={clsx(
+              "layer-ui__wrapper__top-right zen-mode-transition",
+              {
+                "transition-right": zenModeEnabled,
+              },
+            )}
           >
-            {appState.collaborators.size > 0 &&
-              Array.from(appState.collaborators)
-                // Collaborator is either not initialized or is actually the current user.
-                .filter(([_, client]) => Object.keys(client).length !== 0)
-                .map(([clientId, client]) => (
-                  <Tooltip
-                    label={client.username || "Unknown user"}
-                    key={clientId}
-                  >
-                    {actionManager.renderAction("goToCollaborator", clientId)}
-                  </Tooltip>
-                ))}
-          </UserList>
+            <UserList>
+              {appState.collaborators.size > 0 &&
+                Array.from(appState.collaborators)
+                  // Collaborator is either not initialized or is actually the current user.
+                  .filter(([_, client]) => Object.keys(client).length !== 0)
+                  .map(([clientId, client]) => (
+                    <Tooltip
+                      label={client.username || "Unknown user"}
+                      key={clientId}
+                    >
+                      {actionManager.renderAction("goToCollaborator", clientId)}
+                    </Tooltip>
+                  ))}
+            </UserList>
+            {renderTopRight?.(isMobile, appState)}
+          </div>
         </div>
       </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 = () => (
     <footer role="contentinfo" className="layer-ui__wrapper__footer">
       <div
@@ -746,7 +739,6 @@ const LayerUI = ({
       {dialogs}
       {renderFixedSideContainer()}
       {renderBottomAppMenu()}
-      {renderGitHubCorner()}
       {renderFooter()}
       {appState.scrolledOutside && (
         <button

+ 2 - 1
src/components/UserList.scss

@@ -2,7 +2,8 @@
   .UserList {
     pointer-events: none;
     /*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;
     flex-wrap: wrap;
     justify-content: flex-end;

+ 0 - 14
src/css/styles.scss

@@ -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 {
     visibility: visible;
     opacity: 1;

+ 2 - 0
src/css/theme.scss

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

+ 11 - 5
src/components/GitHubCorner.tsx → src/excalidraw-app/components/GitHubCorner.tsx

@@ -3,13 +3,19 @@ import React from "react";
 
 // https://github.com/tholman/github-corners
 export const GitHubCorner = React.memo(
-  ({ theme }: { theme: "light" | "dark" }) => (
+  ({ theme, dir }: { theme: "light" | "dark"; dir: string }) => (
     <svg
       xmlns="http://www.w3.org/2000/svg"
       width="40"
       height="40"
       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
         href="https://github.com/excalidraw/excalidraw"
@@ -19,18 +25,18 @@ export const GitHubCorner = React.memo(
       >
         <path
           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
           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"
           style={{ transformOrigin: "130px 106px" }}
-          fill={theme === "light" ? oc.white : oc.black}
+          fill={theme === "light" ? oc.white : "var(--default-bg-color)"}
         />
         <path
           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"
-          fill={theme === "light" ? oc.white : oc.black}
+          fill={theme === "light" ? oc.white : "var(--default-bg-color)"}
         />
       </a>
     </svg>

+ 14 - 0
src/excalidraw-app/index.tsx

@@ -52,6 +52,7 @@ import {
 } from "./data/localStorage";
 import CustomStats from "./CustomStats";
 import { RestoredDataState } from "../data/restore";
+import { GitHubCorner } from "./components/GitHubCorner";
 
 const languageDetector = new LanguageDetector();
 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(
     (isMobile: boolean) => {
       const renderLanguageList = () => (
@@ -331,6 +343,7 @@ const ExcalidrawWrapper = () => {
     const serializedItems = JSON.stringify(items);
     localStorage.setItem(STORAGE_KEYS.LOCAL_STORAGE_LIBRARY, serializedItems);
   };
+
   return (
     <>
       <Excalidraw
@@ -341,6 +354,7 @@ const ExcalidrawWrapper = () => {
         isCollaborating={collabAPI?.isCollaborating()}
         onPointerUpdate={collabAPI?.onPointerUpdate}
         onExportToBackend={onExportToBackend}
+        renderTopRight={renderTopRight}
         renderFooter={renderFooter}
         langCode={langCode}
         renderCustomStats={renderCustomStats}

+ 8 - 0
src/packages/excalidraw/CHANGELOG.md

@@ -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.
 -->
 
+## 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)
 
 ## Excalidraw API

+ 6 - 1
src/packages/excalidraw/README_NEXT.md

@@ -355,6 +355,7 @@ To view the full example visit :point_down:
 | [`onPointerUpdate`](#onPointerUpdate) | Function |  | Callback triggered when mouse pointer is updated. |
 | [`onExportToBackend`](#onExportToBackend) | Function |  | Callback triggered when link button is clicked on export dialog |
 | [`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 |
 | [`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. |
@@ -502,9 +503,13 @@ import { defaultLang, languages } from "@excalidraw/excalidraw";
 | defaultLang | string |
 | 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`
 
-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`
 

+ 2 - 0
src/packages/excalidraw/index.tsx

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

+ 1 - 0
src/types.ts

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