Browse Source

feat: add comments/presi eplus promos for discoveribility (#10294)

David Luzar 1 month ago
parent
commit
5bcd8280c9

+ 12 - 0
excalidraw-app/App.tsx

@@ -138,6 +138,9 @@ import { ExcalidrawPlusIframeExport } from "./ExcalidrawPlusIframeExport";
 
 import "./index.scss";
 
+import { ExcalidrawPlusPromoBanner } from "./components/ExcalidrawPlusPromoBanner";
+import { AppSidebar } from "./components/AppSidebar";
+
 import type { CollabAPI } from "./collab/Collab";
 
 polyfill();
@@ -851,8 +854,15 @@ const ExcalidrawWrapper = () => {
           if (isMobile || !collabAPI || isCollabDisabled) {
             return null;
           }
+
           return (
             <div className="excalidraw-ui-top-right">
+              {excalidrawAPI?.getEditorInterface().formFactor === "desktop" && (
+                <ExcalidrawPlusPromoBanner
+                  isSignedIn={isExcalidrawPlusSignedUser}
+                />
+              )}
+
               {collabError.message && <CollabError collabError={collabError} />}
               <LiveCollaborationTrigger
                 isCollaborating={isCollaborating}
@@ -945,6 +955,8 @@ const ExcalidrawWrapper = () => {
           }}
         />
 
+        <AppSidebar />
+
         {errorMessage && (
           <ErrorDialog onClose={() => setErrorMessage("")}>
             {errorMessage}

+ 1 - 6
excalidraw-app/components/AppFooter.tsx

@@ -5,7 +5,6 @@ import { isExcalidrawPlusSignedUser } from "../app_constants";
 
 import { DebugFooter, isVisualDebuggerEnabled } from "./DebugCanvas";
 import { EncryptedIcon } from "./EncryptedIcon";
-import { ExcalidrawPlusAppLink } from "./ExcalidrawPlusAppLink";
 
 export const AppFooter = React.memo(
   ({ onChange }: { onChange: () => void }) => {
@@ -19,11 +18,7 @@ export const AppFooter = React.memo(
           }}
         >
           {isVisualDebuggerEnabled() && <DebugFooter onChange={onChange} />}
-          {isExcalidrawPlusSignedUser ? (
-            <ExcalidrawPlusAppLink />
-          ) : (
-            <EncryptedIcon />
-          )}
+          {!isExcalidrawPlusSignedUser && <EncryptedIcon />}
         </div>
       </Footer>
     );

+ 36 - 0
excalidraw-app/components/AppSidebar.scss

@@ -0,0 +1,36 @@
+.excalidraw {
+  .app-sidebar-promo-container {
+    padding: 0.75rem;
+    display: flex;
+    flex-direction: column;
+    text-align: center;
+    gap: 1rem;
+    flex: 1 1 auto;
+  }
+
+  .app-sidebar-promo-image {
+    margin: 1rem 0;
+
+    height: 16.25rem;
+    background-size: contain;
+    background-position: center;
+    background-repeat: no-repeat;
+
+    background-image: radial-gradient(
+        circle,
+        transparent 60%,
+        var(--sidebar-bg-color) 100%
+      ),
+      var(--image-source);
+
+    display: flex;
+  }
+
+  .app-sidebar-promo-text {
+    padding: 0 2rem;
+  }
+
+  .link-button {
+    margin: 0 auto;
+  }
+}

+ 79 - 0
excalidraw-app/components/AppSidebar.tsx

@@ -0,0 +1,79 @@
+import { DefaultSidebar, Sidebar, THEME } from "@excalidraw/excalidraw";
+import {
+  messageCircleIcon,
+  presentationIcon,
+} from "@excalidraw/excalidraw/components/icons";
+import { LinkButton } from "@excalidraw/excalidraw/components/LinkButton";
+import { useUIAppState } from "@excalidraw/excalidraw/context/ui-appState";
+
+import "./AppSidebar.scss";
+
+export const AppSidebar = () => {
+  const { theme, openSidebar } = useUIAppState();
+
+  return (
+    <DefaultSidebar>
+      <DefaultSidebar.TabTriggers>
+        <Sidebar.TabTrigger
+          tab="comments"
+          style={{ opacity: openSidebar?.tab === "comments" ? 1 : 0.4 }}
+        >
+          {messageCircleIcon}
+        </Sidebar.TabTrigger>
+        <Sidebar.TabTrigger
+          tab="presentation"
+          style={{ opacity: openSidebar?.tab === "presentation" ? 1 : 0.4 }}
+        >
+          {presentationIcon}
+        </Sidebar.TabTrigger>
+      </DefaultSidebar.TabTriggers>
+      <Sidebar.Tab tab="comments">
+        <div className="app-sidebar-promo-container">
+          <div
+            className="app-sidebar-promo-image"
+            style={{
+              ["--image-source" as any]: `url(/oss_promo_comments_${
+                theme === THEME.DARK ? "dark" : "light"
+              }.jpg)`,
+              opacity: 0.7,
+            }}
+          />
+          <div className="app-sidebar-promo-text">
+            Make comments with Excalidraw+
+          </div>
+          <LinkButton
+            href={`${
+              import.meta.env.VITE_APP_PLUS_LP
+            }/plus?utm_source=excalidraw&utm_medium=app&utm_content=comments_promo#excalidraw-redirect`}
+          >
+            Sign up now
+          </LinkButton>
+        </div>
+      </Sidebar.Tab>
+      <Sidebar.Tab tab="presentation" className="px-3">
+        <div className="app-sidebar-promo-container">
+          <div
+            className="app-sidebar-promo-image"
+            style={{
+              ["--image-source" as any]: `url(/oss_promo_presentations_${
+                theme === THEME.DARK ? "dark" : "light"
+              }.svg)`,
+              backgroundSize: "60%",
+              opacity: 0.4,
+            }}
+          />
+          <div className="app-sidebar-promo-text">
+            Create presentations with Excalidraw+
+          </div>
+          <LinkButton
+            href={`${
+              import.meta.env.VITE_APP_PLUS_LP
+            }/plus?utm_source=excalidraw&utm_medium=app&utm_content=presentations_promo#excalidraw-redirect`}
+          >
+            Sign up now
+          </LinkButton>
+        </div>
+      </Sidebar.Tab>
+    </DefaultSidebar>
+  );
+};

+ 0 - 19
excalidraw-app/components/ExcalidrawPlusAppLink.tsx

@@ -1,19 +0,0 @@
-import { isExcalidrawPlusSignedUser } from "../app_constants";
-
-export const ExcalidrawPlusAppLink = () => {
-  if (!isExcalidrawPlusSignedUser) {
-    return null;
-  }
-  return (
-    <a
-      href={`${
-        import.meta.env.VITE_APP_PLUS_APP
-      }?utm_source=excalidraw&utm_medium=app&utm_content=signedInUserRedirectButton#excalidraw-redirect`}
-      target="_blank"
-      rel="noopener"
-      className="plus-button"
-    >
-      Go to Excalidraw+
-    </a>
-  );
-};

+ 22 - 0
excalidraw-app/components/ExcalidrawPlusPromoBanner.tsx

@@ -0,0 +1,22 @@
+export const ExcalidrawPlusPromoBanner = ({
+  isSignedIn,
+}: {
+  isSignedIn: boolean;
+}) => {
+  return (
+    <a
+      href={`${
+        isSignedIn
+          ? import.meta.env.VITE_APP_PLUS_LP
+          : import.meta.env.VITE_APP_PLUS_APP
+      }/plus?utm_source=excalidraw&utm_medium=app&utm_content=${
+        isSignedIn ? "signedInBanner" : "guestBanner"
+      }#excalidraw-redirect`}
+      target="_blank"
+      rel="noopener"
+      className="plus-banner"
+    >
+      Excalidraw+
+    </a>
+  );
+};

+ 16 - 5
excalidraw-app/index.scss

@@ -1,3 +1,5 @@
+@import "../packages/excalidraw/css/variables.module.scss";
+
 .excalidraw {
   --color-primary-contrast-offset: #625ee0; // to offset Chubb illusion
 
@@ -84,22 +86,31 @@
   }
 }
 
-.plus-button {
+.plus-banner {
   display: flex;
   justify-content: center;
   cursor: pointer;
   align-items: center;
   border: 1px solid var(--color-primary);
-  padding: 0.5rem 0.75rem;
+  padding: 0.5rem 0.875rem;
   border-radius: var(--border-radius-lg);
   background-color: var(--island-bg-color);
-  color: var(--color-primary) !important;
   text-decoration: none !important;
 
-  font-size: 0.75rem;
+  font-family: var(--ui-font);
+  font-size: 0.8333rem;
   box-sizing: border-box;
   height: var(--lg-button-size);
 
+  border: none;
+  box-shadow: 0 0 0 1px var(--color-surface-lowest);
+  background-color: var(--color-surface-low);
+  color: var(--button-color, var(--color-on-surface)) !important;
+
+  &:active {
+    box-shadow: 0 0 0 1px var(--color-brand-active);
+  }
+
   &:hover {
     background-color: var(--color-primary);
     color: white !important;
@@ -111,7 +122,7 @@
 }
 
 .theme--dark {
-  .plus-button {
+  .plus-banner {
     &:hover {
       color: black !important;
     }

+ 1 - 1
packages/excalidraw/components/FilledButton.tsx

@@ -20,7 +20,7 @@ export type ButtonColor =
 export type ButtonSize = "medium" | "large";
 
 export type FilledButtonProps = {
-  label: string;
+  label?: string;
 
   children?: React.ReactNode;
   onClick?: (event: React.MouseEvent) => void;

+ 3 - 8
packages/excalidraw/components/LayerUI.tsx

@@ -4,7 +4,6 @@ import React from "react";
 import {
   CLASSES,
   DEFAULT_SIDEBAR,
-  MQ_MIN_WIDTH_DESKTOP,
   TOOL_TYPE,
   arrayToMap,
   capitalizeString,
@@ -48,7 +47,7 @@ import MainMenu from "./main-menu/MainMenu";
 import { ActiveConfirmDialog } from "./ActiveConfirmDialog";
 import { useEditorInterface, useStylesPanelMode } from "./App";
 import { OverwriteConfirmDialog } from "./OverwriteConfirm/OverwriteConfirm";
-import { LibraryIcon } from "./icons";
+import { sidebarRightIcon } from "./icons";
 import { DefaultSidebar } from "./DefaultSidebar";
 import { TTDDialog } from "./TTDDialog/TTDDialog";
 import { Stats } from "./Stats";
@@ -473,7 +472,7 @@ const LayerUI = ({
       <DefaultMainMenu UIOptions={UIOptions} />
       <DefaultSidebar.Trigger
         __fallback
-        icon={LibraryIcon}
+        icon={sidebarRightIcon}
         title={capitalizeString(t("toolBar.library"))}
         onToggle={(open) => {
           if (open) {
@@ -487,11 +486,7 @@ const LayerUI = ({
           }
         }}
         tab={DEFAULT_SIDEBAR.defaultTab}
-      >
-        {stylesPanelMode === "full" &&
-          appState.width >= MQ_MIN_WIDTH_DESKTOP &&
-          t("toolBar.library")}
-      </DefaultSidebar.Trigger>
+      />
       <DefaultOverwriteConfirmDialog />
       {appState.openDialog?.name === "ttd" && <TTDDialog __fallback />}
       {/* ------------------------------------------------------------------ */}

+ 15 - 0
packages/excalidraw/components/LinkButton.tsx

@@ -0,0 +1,15 @@
+import { FilledButton } from "./FilledButton";
+
+export const LinkButton = ({
+  children,
+  href,
+}: {
+  href: string;
+  children: React.ReactNode;
+}) => {
+  return (
+    <a href={href} target="_blank" rel="noopener" className="link-button">
+      <FilledButton>{children}</FilledButton>
+    </a>
+  );
+};

+ 38 - 0
packages/excalidraw/components/icons.tsx

@@ -2335,3 +2335,41 @@ export const pencilIcon = createIcon(
   </g>,
   tablerIconProps,
 );
+
+export const chevronLeftIcon = createIcon(
+  <g strokeWidth={1}>
+    <path stroke="none" d="M0 0h24v24H0z" fill="none" />
+    <path d="M11 7l-5 5l5 5" />
+    <path d="M17 7l-5 5l5 5" />
+  </g>,
+  tablerIconProps,
+);
+
+export const sidebarRightIcon = createIcon(
+  <g strokeWidth="1.75">
+    <path stroke="none" d="M0 0h24v24H0z" fill="none" />
+    <path d="M4 4m0 2a2 2 0 0 1 2 -2h12a2 2 0 0 1 2 2v12a2 2 0 0 1 -2 2h-12a2 2 0 0 1 -2 -2z" />
+    <path d="M15 4l0 16" />
+  </g>,
+  tablerIconProps,
+);
+
+export const messageCircleIcon = createIcon(
+  <g strokeWidth="1.25">
+    <path stroke="none" d="M0 0h24v24H0z" fill="none" />
+    <path d="M3 20l1.3 -3.9c-2.324 -3.437 -1.426 -7.872 2.1 -10.374c3.526 -2.501 8.59 -2.296 11.845 .48c3.255 2.777 3.695 7.266 1.029 10.501c-2.666 3.235 -7.615 4.215 -11.574 2.293l-4.7 1" />
+  </g>,
+  tablerIconProps,
+);
+
+export const presentationIcon = createIcon(
+  <g strokeWidth="1.25">
+    <path stroke="none" d="M0 0h24v24H0z" fill="none" />
+    <path d="M3 4l18 0" />
+    <path d="M4 4v10a2 2 0 0 0 2 2h12a2 2 0 0 0 2 -2v-10" />
+    <path d="M12 16l0 4" />
+    <path d="M9 20l6 0" />
+    <path d="M8 12l3 -3l2 2l3 -3" />
+  </g>,
+  tablerIconProps,
+);

+ 10 - 0
packages/excalidraw/css/styles.scss

@@ -50,6 +50,11 @@ body.excalidraw-cursor-resize * {
   height: 100%;
   width: 100%;
 
+  button {
+    // browser default. Looks kinda good on low-dpi.
+    font-size: 0.8333rem;
+  }
+
   button,
   label {
     @include buttonNoHighlight;
@@ -708,6 +713,11 @@ body.excalidraw-cursor-resize * {
       margin-top: 0rem;
     }
   }
+
+  .link-button {
+    display: flex;
+    text-decoration: none !important;
+  }
 }
 
 .ErrorSplash.excalidraw {

BIN
public/oss_promo_comments_dark.jpg


BIN
public/oss_promo_comments_light.jpg


File diff suppressed because it is too large
+ 0 - 0
public/oss_promo_presentations_dark.svg


File diff suppressed because it is too large
+ 0 - 0
public/oss_promo_presentations_light.svg


Some files were not shown because too many files changed in this diff