Skip to content

Instantly share code, notes, and snippets.

@rachmadideni
Forked from KairuDeibisu/App.tsx
Created February 2, 2025 11:19
Show Gist options
  • Select an option

  • Save rachmadideni/7081942e8412cb59767d424606ac0dbc to your computer and use it in GitHub Desktop.

Select an option

Save rachmadideni/7081942e8412cb59767d424606ac0dbc 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;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment