Created
November 30, 2023 19:06
-
-
Save KairuDeibisu/6dca738d0fb67cac4084602680f904af to your computer and use it in GitHub Desktop.
Proof of concept hook API for Excalidraw
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
commented
Dec 7, 2023
- 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