diff --git a/src/components/App.tsx b/src/components/App.tsx index 03c7b5a336bb..86bc561517ce 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -418,7 +418,12 @@ class App extends React.Component { zenModeEnabled, } = this.state; - const { onCollabButtonClick, onExportToBackend, renderFooter } = this.props; + const { + onCollabButtonClick, + onExportToBackend, + renderFooter, + renderTopRight, + } = this.props; const DEFAULT_PASTE_X = canvasDOMWidth / 2; const DEFAULT_PASTE_Y = canvasDOMHeight / 2; @@ -458,6 +463,7 @@ class App extends React.Component { isCollaborating={this.props.isCollaborating || false} onExportToBackend={onExportToBackend} renderCustomFooter={renderFooter} + renderTopRight={renderTopRight} viewModeEnabled={viewModeEnabled} showExitZenModeBtn={ typeof this.props?.zenModeEnabled === "undefined" && zenModeEnabled diff --git a/src/components/LayerUI.tsx b/src/components/LayerUI.tsx index cc838b3f2ac8..8a606488068c 100644 --- a/src/components/LayerUI.tsx +++ b/src/components/LayerUI.tsx @@ -60,6 +60,7 @@ interface LayerUIProps { appState: AppState, canvas: HTMLCanvasElement | null, ) => void; + renderTopRight?: (isMobile: boolean) => JSX.Element; renderCustomFooter?: (isMobile: boolean) => JSX.Element; viewModeEnabled: boolean; onHomeButtonClick?: () => void; @@ -318,6 +319,7 @@ const LayerUI = ({ isCollaborating, onExportToBackend, renderCustomFooter, + renderTopRight, viewModeEnabled, onHomeButtonClick, }: LayerUIProps) => { @@ -516,10 +518,22 @@ const LayerUI = ({ )} )} +
+ {renderTopRight?.(isMobile)} +
{appState.collaborators.size > 0 && Array.from(appState.collaborators) @@ -670,6 +684,8 @@ const areEqual = (prev: LayerUIProps, next: LayerUIProps) => { const keys = Object.keys(prevAppState) as (keyof Partial)[]; return ( + prev.renderCustomFooter === next.renderCustomFooter && + prev.renderTopRight === next.renderTopRight && prev.langCode === next.langCode && prev.elements === next.elements && keys.every((key) => prevAppState[key] === nextAppState[key]) diff --git a/src/components/MobileMenu.tsx b/src/components/MobileMenu.tsx index fb6f63fda5b9..26000a4e129c 100644 --- a/src/components/MobileMenu.tsx +++ b/src/components/MobileMenu.tsx @@ -160,7 +160,11 @@ export const MobileMenu = ({ {appState.collaborators.size > 0 && (
{t("labels.collaborators")} - + {Array.from(appState.collaborators) // Collaborator is either not initialized or is actually the current user. .filter( diff --git a/src/components/UserList.scss b/src/components/UserList.scss index 91d53544bfea..a028534d995f 100644 --- a/src/components/UserList.scss +++ b/src/components/UserList.scss @@ -1,16 +1,46 @@ .excalidraw { + $marginTop: 60px; + // eye-balled + $bottomOffset: 60px; + .UserList { pointer-events: none; /*github corner*/ padding: var(--space-factor); display: flex; - flex-wrap: wrap; justify-content: flex-end; + + overflow: hidden; + border-radius: 60px; + + &.layout-vertical { + grid-column: 3; + flex-direction: column-reverse; + position: absolute; + top: $marginTop; + right: var(--space-factor); + max-height: calc( + 100vh - var(--space-factor) - #{$marginTop} - #{$bottomOffset} + + var(--itemOffset) + ); + + .Avatar { + width: 2.4rem; + height: 2.4rem; + } + + padding-bottom: max(calc(var(--itemOffset) * -1), 0px); + } } .UserList > * { pointer-events: all; - margin: 0 0 var(--space-factor) var(--space-factor); + } + .UserList.layout-vertical > * { + margin-bottom: var(--itemOffset); + } + .UserList.layout-horizontal > * { + margin-right: var(--itemOffset); } .UserList_mobile { diff --git a/src/components/UserList.tsx b/src/components/UserList.tsx index 7cd1e12483b2..a159a1172c19 100644 --- a/src/components/UserList.tsx +++ b/src/components/UserList.tsx @@ -2,16 +2,37 @@ import "./UserList.scss"; import React from "react"; import clsx from "clsx"; +import { AppState } from "../types"; type UserListProps = { children: React.ReactNode; className?: string; mobile?: boolean; + collaborators: AppState["collaborators"]; + layout: "vertical" | "horizontal"; }; -export const UserList = ({ children, className, mobile }: UserListProps) => { +export const UserList = ({ + children, + className, + mobile, + collaborators = new Map(), + layout, +}: UserListProps) => { + const threshold = layout === "vertical" ? 6 : 3; + const offset = + collaborators.size > threshold + ? Math.min(collaborators.size - threshold, 15) * -2 + : 4; return ( -
+
{children}
); diff --git a/src/packages/excalidraw/index.tsx b/src/packages/excalidraw/index.tsx index 410db1b26bab..755b0dc9e777 100644 --- a/src/packages/excalidraw/index.tsx +++ b/src/packages/excalidraw/index.tsx @@ -29,6 +29,7 @@ const Excalidraw = (props: ExcalidrawProps) => { zenModeEnabled, gridModeEnabled, onHomeButtonClick, + renderTopRight, } = props; useEffect(() => { @@ -69,6 +70,7 @@ const Excalidraw = (props: ExcalidrawProps) => { zenModeEnabled={zenModeEnabled} gridModeEnabled={gridModeEnabled} onHomeButtonClick={onHomeButtonClick} + renderTopRight={renderTopRight} /> diff --git a/src/types.ts b/src/types.ts index d217ee087bf5..efe1be4e943e 100644 --- a/src/types.ts +++ b/src/types.ts @@ -191,6 +191,7 @@ export interface ExcalidrawProps { appState: AppState, canvas: HTMLCanvasElement | null, ) => void; + renderTopRight?: (isMobile: boolean) => JSX.Element; renderFooter?: (isMobile: boolean) => JSX.Element; langCode?: Language["code"]; viewModeEnabled?: boolean;