feat: support src collaborators (#5114)

* feat: support avatarURLfor collaborators

* fix

* better avatars :)

* use position fixed for tooltips so it renders correctly when offsets updated

* update docs

* Update src/excalidraw-app/collab/CollabWrapper.tsx

* rename avatarUrl to src
This commit is contained in:
Aakansha Doshi 2022-05-02 15:15:24 +05:30 committed by GitHub
parent 22b2e10ddb
commit 9e6d5fdbcb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 69 additions and 18 deletions

View File

@ -1,4 +1,4 @@
import { getClientColors, getClientInitials } from "../clients"; import { getClientColors } from "../clients";
import { Avatar } from "../components/Avatar"; import { Avatar } from "../components/Avatar";
import { centerScrollOn } from "../scene/scroll"; import { centerScrollOn } from "../scene/scroll";
import { Collaborator } from "../types"; import { Collaborator } from "../types";
@ -43,16 +43,15 @@ export const actionGoToCollaborator = register({
} }
const { background, stroke } = getClientColors(clientId, appState); const { background, stroke } = getClientColors(clientId, appState);
const shortName = getClientInitials(collaborator.username);
return ( return (
<Avatar <Avatar
color={background} color={background}
border={stroke} border={stroke}
onClick={() => updateData(collaborator.pointer)} onClick={() => updateData(collaborator.pointer)}
> name={collaborator.username || ""}
{shortName} src={collaborator.src}
</Avatar> />
); );
}, },
}); });

View File

@ -12,5 +12,11 @@
cursor: pointer; cursor: pointer;
font-size: 0.8rem; font-size: 0.8rem;
font-weight: 500; font-weight: 500;
&-img {
width: 100%;
height: 100%;
border-radius: 100%;
}
} }
} }

View File

@ -1,20 +1,28 @@
import "./Avatar.scss"; import "./Avatar.scss";
import React from "react"; import React from "react";
import { getClientInitials } from "../clients";
type AvatarProps = { type AvatarProps = {
children: string;
onClick: (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => void; onClick: (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => void;
color: string; color: string;
border: string; border: string;
name: string;
src?: string;
}; };
export const Avatar = ({ children, color, border, onClick }: AvatarProps) => ( export const Avatar = ({ color, border, onClick, name, src }: AvatarProps) => {
<div const shortName = getClientInitials(name);
className="Avatar" const style = src
style={{ background: color, border: `1px solid ${border}` }} ? undefined
onClick={onClick} : { background: color, border: `1px solid ${border}` };
> return (
{children} <div className="Avatar" style={style} onClick={onClick}>
</div> {src ? (
); <img className="Avatar-img" src={src} alt={shortName} />
) : (
shortName
)}
</div>
);
};

View File

@ -2,7 +2,7 @@
// container in body where the actual tooltip is appended to // container in body where the actual tooltip is appended to
.excalidraw-tooltip { .excalidraw-tooltip {
position: absolute; position: fixed;
z-index: 1000; z-index: 1000;
padding: 8px; padding: 8px;

View File

@ -17,6 +17,7 @@ Please add the latest change on the top under the correct section.
#### Features #### Features
- Support [`src`](https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L50) for collaborators. Now onwards host can pass `src` to render the customized avatar for collaborators [#5114](https://github.com/excalidraw/excalidraw/pull/5114).
- Support `libraryItems` argument in `initialData.libraryItems` and `updateScene({ libraryItems })` to be a Promise resolving to `LibraryItems`, and support functional update of `libraryItems` in [`updateScene({ libraryItems })`](https://github.com/excalidraw/excalidraw/blob/master/src/packages/excalidraw/README.md#updateScene). [#5101](https://github.com/excalidraw/excalidraw/pull/5101). - Support `libraryItems` argument in `initialData.libraryItems` and `updateScene({ libraryItems })` to be a Promise resolving to `LibraryItems`, and support functional update of `libraryItems` in [`updateScene({ libraryItems })`](https://github.com/excalidraw/excalidraw/blob/master/src/packages/excalidraw/README.md#updateScene). [#5101](https://github.com/excalidraw/excalidraw/pull/5101).
- Expose util [`mergeLibraryItems`](https://github.com/excalidraw/excalidraw/blob/master/src/packages/excalidraw/README.md#mergeLibraryItems) [#5101](https://github.com/excalidraw/excalidraw/pull/5101). - Expose util [`mergeLibraryItems`](https://github.com/excalidraw/excalidraw/blob/master/src/packages/excalidraw/README.md#mergeLibraryItems) [#5101](https://github.com/excalidraw/excalidraw/pull/5101).
- Expose util [`exportToClipboard`](https://github.com/excalidraw/excalidraw/blob/master/src/packages/excalidraw/README.md#exportToClipboard) which allows to copy the scene contents to clipboard as `svg`, `png` or `json` [#5103](https://github.com/excalidraw/excalidraw/pull/5103). - Expose util [`exportToClipboard`](https://github.com/excalidraw/excalidraw/blob/master/src/packages/excalidraw/README.md#exportToClipboard) which allows to copy the scene contents to clipboard as `svg`, `png` or `json` [#5103](https://github.com/excalidraw/excalidraw/pull/5103).

View File

@ -512,7 +512,7 @@ You can use this function to update the scene with the sceneData. It accepts the
| --- | --- | --- | | --- | --- | --- |
| `elements` | [`ImportedDataState["elements"]`](https://github.com/excalidraw/excalidraw/blob/master/src/data/types.ts#L17) | The `elements` to be updated in the scene | | `elements` | [`ImportedDataState["elements"]`](https://github.com/excalidraw/excalidraw/blob/master/src/data/types.ts#L17) | The `elements` to be updated in the scene |
| `appState` | [`ImportedDataState["appState"]`](https://github.com/excalidraw/excalidraw/blob/master/src/data/types.ts#L18) | The `appState` to be updated in the scene. | | `appState` | [`ImportedDataState["appState"]`](https://github.com/excalidraw/excalidraw/blob/master/src/data/types.ts#L18) | The `appState` to be updated in the scene. |
| `collaborators` | <pre>Map<string, <a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L29">Collaborator></a></pre> | The list of collaborators to be updated in the scene. | | `collaborators` | <pre>Map<string, <a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L35">Collaborator></a></pre> | The list of collaborators to be updated in the scene. |
| `commitToHistory` | `boolean` | Implies if the `history (undo/redo)` should be recorded. Defaults to `false`. | | `commitToHistory` | `boolean` | Implies if the `history (undo/redo)` should be recorded. Defaults to `false`. |
| `libraryItems` | [LibraryItems](https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L200) &#124; Promise<[LibraryItems](https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L200)> &#124; ((currentItems: [LibraryItems](https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L200)>) => [LibraryItems](https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L200) &#124; Promise<[LibraryItems](https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L200)>) | The `libraryItems` to be update in the scene. | | `libraryItems` | [LibraryItems](https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L200) &#124; Promise<[LibraryItems](https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L200)> &#124; ((currentItems: [LibraryItems](https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L200)>) => [LibraryItems](https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L200) &#124; Promise<[LibraryItems](https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L200)>) | The `libraryItems` to be update in the scene. |

View File

@ -31,7 +31,10 @@ const resolvablePromise = () => {
const renderTopRightUI = () => { const renderTopRightUI = () => {
return ( return (
<button onClick={() => alert("This is dummy top right UI")}> <button
onClick={() => alert("This is dummy top right UI")}
style={{ height: "2.5rem" }}
>
{" "} {" "}
Click me{" "} Click me{" "}
</button> </button>
@ -58,6 +61,7 @@ export default function App() {
const [exportWithDarkMode, setExportWithDarkMode] = useState(false); const [exportWithDarkMode, setExportWithDarkMode] = useState(false);
const [exportEmbedScene, setExportEmbedScene] = useState(false); const [exportEmbedScene, setExportEmbedScene] = useState(false);
const [theme, setTheme] = useState("light"); const [theme, setTheme] = useState("light");
const [isCollaborating, setIsCollaborating] = useState(false);
const initialStatePromiseRef = useRef({ promise: null }); const initialStatePromiseRef = useRef({ promise: null });
if (!initialStatePromiseRef.current.promise) { if (!initialStatePromiseRef.current.promise) {
@ -228,6 +232,36 @@ export default function App() {
/> />
Switch to Dark Theme Switch to Dark Theme
</label> </label>
<label>
<input
type="checkbox"
checked={isCollaborating}
onChange={() => {
if (!isCollaborating) {
const collaborators = new Map();
collaborators.set("id1", {
username: "Doremon",
src: "doremon.png",
});
collaborators.set("id2", {
username: "Excalibot",
src: "https://avatars.githubusercontent.com/excalibot",
});
collaborators.set("id3", {
username: "Pika",
src: "pika.jpeg",
});
excalidrawRef.current.updateScene({ collaborators });
} else {
excalidrawRef.current.updateScene({
collaborators: new Map(),
});
}
setIsCollaborating(!isCollaborating);
}}
/>
Show collaborators
</label>
<div> <div>
<button onClick={onCopy.bind(null, "png")}> <button onClick={onCopy.bind(null, "png")}>
Copy to Clipboard as PNG Copy to Clipboard as PNG

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

View File

@ -45,6 +45,9 @@ export type Collaborator = {
background: string; background: string;
stroke: string; stroke: string;
}; };
// The url of the collaborator's avatar, defaults to username intials
// if not present
src?: string;
}; };
export type DataURL = string & { _brand: "DataURL" }; export type DataURL = string & { _brand: "DataURL" };