Skip to content

Instantly share code, notes, and snippets.

@KairuDeibisu
Created November 30, 2023 19:06
Show Gist options
  • Save KairuDeibisu/6dca738d0fb67cac4084602680f904af to your computer and use it in GitHub Desktop.
Save KairuDeibisu/6dca738d0fb67cac4084602680f904af to your computer and use it in GitHub Desktop.
Proof of concept hook API for Excalidraw
import React, { useEffect, useState } from 'react';
import { Excalidraw } from "@excalidraw/excalidraw";
import type { ExcalidrawImperativeAPI } from '@excalidraw/excalidraw/types/types';
import Example from './Example';
import { ExcalidrawAPIProvider } from './hooks/useExcalidrawAPI';
function App() {
const [excalidrawAPI, setExcalidrawAPI] = useState<ExcalidrawImperativeAPI | null>(null);
return (
<>
<div style={{ height: "800px" }}>
<Excalidraw initialData={{}} excalidrawAPI={(api) => setExcalidrawAPI(api)} />
</div>
<ExcalidrawAPIProvider excalidrawImperativeAPI={excalidrawAPI}>
{<Example />}
</ExcalidrawAPIProvider>
</>
)
}
export default App;
import React, { useEffect } from 'react';
import useExcalidrawAPI from './hooks/useExcalidrawAPI';
import useSelectedElementsAreBeingDragged from './hooks/useSelectedElementsAreBeingDragged';
type ExampleProps = {};
const Example: React.FC<ExampleProps> = () => {
const { excalidrawAPI } = useExcalidrawAPI();
const [isDragging, selectedElements] = useSelectedElementsAreBeingDragged();
useEffect(() => {
// triggers only twice
// once when you start dragging
// once when you stop dragging
console.log(isDragging, selectedElements);
}, [excalidrawAPI, isDragging, selectedElements])
return null; // This component does not render anything itself
}
export default Example;
import type { ExcalidrawImperativeAPI } from "@excalidraw/excalidraw/types/types";
import { FC, HTMLAttributes, createContext, useContext, useEffect, useState } from "react";
const ExcalidrawAPIContext = createContext<{
excalidrawAPI: ExcalidrawImperativeAPI | null;
setExcalidrawAPI: React.Dispatch<React.SetStateAction<ExcalidrawImperativeAPI | null>>;
} | undefined>(undefined);
const useExcalidrawAPI = () => {
const context = useContext(ExcalidrawAPIContext);
if (context === undefined) {
throw new Error('useExcalidrawAPI must be used within an ExcalidrawAPIProvider');
}
return context;
};
interface ExcalidrawAPIProviderProps
extends HTMLAttributes<HTMLDivElement> {
excalidrawImperativeAPI: ExcalidrawImperativeAPI | null
}
const ExcalidrawAPIProvider: FC<ExcalidrawAPIProviderProps> = ({
children,
excalidrawImperativeAPI,
}) => {
const [excalidrawAPI, setExcalidrawAPI] = useState<ExcalidrawImperativeAPI | null>(
excalidrawImperativeAPI
);
useEffect(() => {
setExcalidrawAPI(excalidrawImperativeAPI);
}, [excalidrawImperativeAPI]);
return (
<ExcalidrawAPIContext.Provider value={{ excalidrawAPI, setExcalidrawAPI }}>
{children}
</ExcalidrawAPIContext.Provider>
);
};
ExcalidrawAPIProvider.displayName = 'ExcalidrawAPIProvider';
export default useExcalidrawAPI;
export { ExcalidrawAPIProvider };
import { ExcalidrawElement } from '@excalidraw/excalidraw/types/element/types';
import { useEffect, useState } from 'react';
import useExcalidrawAPI from './useExcalidrawAPI';
function useSelectedElementsAreBeingDragged() {
const { excalidrawAPI } = useExcalidrawAPI();
const [isDragging, setDragging] = useState<boolean>(false);
const [selectedElements, setSelectedElements] = useState<Array<ExcalidrawElement>>([]);
useEffect(() => {
if (!excalidrawAPI) {
console.warn('Excalidraw API is not available');
return;
}
return excalidrawAPI.onChange((_, state) => {
if (isDragging !== state.selectedElementsAreBeingDragged) {
setDragging(state.selectedElementsAreBeingDragged);
setSelectedElements(
excalidrawAPI.getSceneElements().filter(element =>
Object.keys(state.selectedElementIds).includes(element.id.toString())
)
);
}
});
}, [excalidrawAPI, isDragging]);
return [isDragging, selectedElements] as const;
}
export default useSelectedElementsAreBeingDragged;
@Love-Becca
Copy link

  • Hi,
  • I'm currently working on integrating Excalidraw into a React project, and I'm encountering some challenges, was wondering if you could provide some guidance or assistance with this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment