types.ts 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. import {
  2. PointerType,
  3. ExcalidrawLinearElement,
  4. NonDeletedExcalidrawElement,
  5. NonDeleted,
  6. TextAlign,
  7. ExcalidrawElement,
  8. GroupId,
  9. ExcalidrawBindableElement,
  10. Arrowhead,
  11. ChartType,
  12. FontFamilyValues,
  13. Theme,
  14. } from "./element/types";
  15. import { SHAPES } from "./shapes";
  16. import { Point as RoughPoint } from "roughjs/bin/geometry";
  17. import { LinearElementEditor } from "./element/linearElementEditor";
  18. import { SuggestedBinding } from "./element/binding";
  19. import { ImportedDataState } from "./data/types";
  20. import type App from "./components/App";
  21. import type { ResolvablePromise } from "./utils";
  22. import { Spreadsheet } from "./charts";
  23. import { Language } from "./i18n";
  24. import { ClipboardData } from "./clipboard";
  25. import { isOverScrollBars } from "./scene";
  26. import { MaybeTransformHandleType } from "./element/transformHandles";
  27. import { FileSystemHandle } from "./data/filesystem";
  28. export type Point = Readonly<RoughPoint>;
  29. export type Collaborator = {
  30. pointer?: {
  31. x: number;
  32. y: number;
  33. };
  34. button?: "up" | "down";
  35. selectedElementIds?: AppState["selectedElementIds"];
  36. username?: string | null;
  37. userState?: UserIdleState;
  38. color?: {
  39. background: string;
  40. stroke: string;
  41. };
  42. };
  43. export type AppState = {
  44. isLoading: boolean;
  45. errorMessage: string | null;
  46. draggingElement: NonDeletedExcalidrawElement | null;
  47. resizingElement: NonDeletedExcalidrawElement | null;
  48. multiElement: NonDeleted<ExcalidrawLinearElement> | null;
  49. selectionElement: NonDeletedExcalidrawElement | null;
  50. isBindingEnabled: boolean;
  51. startBoundElement: NonDeleted<ExcalidrawBindableElement> | null;
  52. suggestedBindings: SuggestedBinding[];
  53. // element being edited, but not necessarily added to elements array yet
  54. // (e.g. text element when typing into the input)
  55. editingElement: NonDeletedExcalidrawElement | null;
  56. editingLinearElement: LinearElementEditor | null;
  57. elementType: typeof SHAPES[number]["value"];
  58. elementLocked: boolean;
  59. exportBackground: boolean;
  60. exportEmbedScene: boolean;
  61. exportWithDarkMode: boolean;
  62. exportScale: number;
  63. currentItemStrokeColor: string;
  64. currentItemBackgroundColor: string;
  65. currentItemFillStyle: ExcalidrawElement["fillStyle"];
  66. currentItemStrokeWidth: number;
  67. currentItemStrokeStyle: ExcalidrawElement["strokeStyle"];
  68. currentItemRoughness: number;
  69. currentItemOpacity: number;
  70. currentItemFontFamily: FontFamilyValues;
  71. currentItemFontSize: number;
  72. currentItemTextAlign: TextAlign;
  73. currentItemStrokeSharpness: ExcalidrawElement["strokeSharpness"];
  74. currentItemStartArrowhead: Arrowhead | null;
  75. currentItemEndArrowhead: Arrowhead | null;
  76. currentItemLinearStrokeSharpness: ExcalidrawElement["strokeSharpness"];
  77. viewBackgroundColor: string;
  78. scrollX: number;
  79. scrollY: number;
  80. cursorButton: "up" | "down";
  81. scrolledOutside: boolean;
  82. name: string;
  83. isResizing: boolean;
  84. isRotating: boolean;
  85. zoom: Zoom;
  86. openMenu: "canvas" | "shape" | null;
  87. openPopup:
  88. | "canvasColorPicker"
  89. | "backgroundColorPicker"
  90. | "strokeColorPicker"
  91. | null;
  92. lastPointerDownWith: PointerType;
  93. selectedElementIds: { [id: string]: boolean };
  94. previousSelectedElementIds: { [id: string]: boolean };
  95. shouldCacheIgnoreZoom: boolean;
  96. showHelpDialog: boolean;
  97. toastMessage: string | null;
  98. zenModeEnabled: boolean;
  99. theme: Theme;
  100. gridSize: number | null;
  101. viewModeEnabled: boolean;
  102. /** top-most selected groups (i.e. does not include nested groups) */
  103. selectedGroupIds: { [groupId: string]: boolean };
  104. /** group being edited when you drill down to its constituent element
  105. (e.g. when you double-click on a group's element) */
  106. editingGroupId: GroupId | null;
  107. width: number;
  108. height: number;
  109. offsetTop: number;
  110. offsetLeft: number;
  111. isLibraryOpen: boolean;
  112. fileHandle: FileSystemHandle | null;
  113. collaborators: Map<string, Collaborator>;
  114. showStats: boolean;
  115. currentChartType: ChartType;
  116. pasteDialog:
  117. | {
  118. shown: false;
  119. data: null;
  120. }
  121. | {
  122. shown: true;
  123. data: Spreadsheet;
  124. };
  125. };
  126. export type NormalizedZoomValue = number & { _brand: "normalizedZoom" };
  127. export type Zoom = Readonly<{
  128. value: NormalizedZoomValue;
  129. translation: Readonly<{
  130. x: number;
  131. y: number;
  132. }>;
  133. }>;
  134. export type PointerCoords = Readonly<{
  135. x: number;
  136. y: number;
  137. }>;
  138. export type Gesture = {
  139. pointers: Map<number, PointerCoords>;
  140. lastCenter: { x: number; y: number } | null;
  141. initialDistance: number | null;
  142. initialScale: number | null;
  143. };
  144. export declare class GestureEvent extends UIEvent {
  145. readonly rotation: number;
  146. readonly scale: number;
  147. }
  148. export type LibraryItem = readonly NonDeleted<ExcalidrawElement>[];
  149. export type LibraryItems = readonly LibraryItem[];
  150. // NOTE ready/readyPromise props are optional for host apps' sake (our own
  151. // implem guarantees existence)
  152. export type ExcalidrawAPIRefValue =
  153. | ExcalidrawImperativeAPI
  154. | {
  155. readyPromise?: ResolvablePromise<ExcalidrawImperativeAPI>;
  156. ready?: false;
  157. };
  158. export interface ExcalidrawProps {
  159. onChange?: (
  160. elements: readonly ExcalidrawElement[],
  161. appState: AppState,
  162. ) => void;
  163. initialData?: ImportedDataState | null | Promise<ImportedDataState | null>;
  164. excalidrawRef?: ForwardRef<ExcalidrawAPIRefValue>;
  165. onCollabButtonClick?: () => void;
  166. isCollaborating?: boolean;
  167. onPointerUpdate?: (payload: {
  168. pointer: { x: number; y: number };
  169. button: "down" | "up";
  170. pointersMap: Gesture["pointers"];
  171. }) => void;
  172. onPaste?: (
  173. data: ClipboardData,
  174. event: ClipboardEvent | null,
  175. ) => Promise<boolean> | boolean;
  176. renderTopRightUI?: (isMobile: boolean, appState: AppState) => JSX.Element;
  177. renderFooter?: (isMobile: boolean, appState: AppState) => JSX.Element;
  178. langCode?: Language["code"];
  179. viewModeEnabled?: boolean;
  180. zenModeEnabled?: boolean;
  181. gridModeEnabled?: boolean;
  182. libraryReturnUrl?: string;
  183. theme?: Theme;
  184. name?: string;
  185. renderCustomStats?: (
  186. elements: readonly NonDeletedExcalidrawElement[],
  187. appState: AppState,
  188. ) => JSX.Element;
  189. UIOptions?: UIOptions;
  190. detectScroll?: boolean;
  191. handleKeyboardGlobally?: boolean;
  192. onLibraryChange?: (libraryItems: LibraryItems) => void | Promise<any>;
  193. autoFocus?: boolean;
  194. }
  195. export type SceneData = {
  196. elements?: ImportedDataState["elements"];
  197. appState?: ImportedDataState["appState"];
  198. collaborators?: Map<string, Collaborator>;
  199. commitToHistory?: boolean;
  200. };
  201. export enum UserIdleState {
  202. ACTIVE = "active",
  203. AWAY = "away",
  204. IDLE = "idle",
  205. }
  206. export type ExportOpts = {
  207. saveFileToDisk?: boolean;
  208. onExportToBackend?: (
  209. exportedElements: readonly NonDeletedExcalidrawElement[],
  210. appState: AppState,
  211. canvas: HTMLCanvasElement | null,
  212. ) => void;
  213. renderCustomUI?: (
  214. exportedElements: readonly NonDeletedExcalidrawElement[],
  215. appState: AppState,
  216. canvas: HTMLCanvasElement | null,
  217. ) => JSX.Element;
  218. };
  219. type CanvasActions = {
  220. changeViewBackgroundColor?: boolean;
  221. clearCanvas?: boolean;
  222. export?: false | ExportOpts;
  223. loadScene?: boolean;
  224. saveToActiveFile?: boolean;
  225. theme?: boolean;
  226. saveAsImage?: boolean;
  227. };
  228. export type UIOptions = {
  229. canvasActions?: CanvasActions;
  230. };
  231. export type AppProps = ExcalidrawProps & {
  232. UIOptions: {
  233. canvasActions: Required<CanvasActions> & { export: ExportOpts };
  234. };
  235. detectScroll: boolean;
  236. handleKeyboardGlobally: boolean;
  237. };
  238. export type PointerDownState = Readonly<{
  239. // The first position at which pointerDown happened
  240. origin: Readonly<{ x: number; y: number }>;
  241. // Same as "origin" but snapped to the grid, if grid is on
  242. originInGrid: Readonly<{ x: number; y: number }>;
  243. // Scrollbar checks
  244. scrollbars: ReturnType<typeof isOverScrollBars>;
  245. // The previous pointer position
  246. lastCoords: { x: number; y: number };
  247. // map of original elements data
  248. originalElements: Map<string, NonDeleted<ExcalidrawElement>>;
  249. resize: {
  250. // Handle when resizing, might change during the pointer interaction
  251. handleType: MaybeTransformHandleType;
  252. // This is determined on the initial pointer down event
  253. isResizing: boolean;
  254. // This is determined on the initial pointer down event
  255. offset: { x: number; y: number };
  256. // This is determined on the initial pointer down event
  257. arrowDirection: "origin" | "end";
  258. // This is a center point of selected elements determined on the initial pointer down event (for rotation only)
  259. center: { x: number; y: number };
  260. };
  261. hit: {
  262. // The element the pointer is "hitting", is determined on the initial
  263. // pointer down event
  264. element: NonDeleted<ExcalidrawElement> | null;
  265. // The elements the pointer is "hitting", is determined on the initial
  266. // pointer down event
  267. allHitElements: NonDeleted<ExcalidrawElement>[];
  268. // This is determined on the initial pointer down event
  269. wasAddedToSelection: boolean;
  270. // Whether selected element(s) were duplicated, might change during the
  271. // pointer interaction
  272. hasBeenDuplicated: boolean;
  273. hasHitCommonBoundingBoxOfSelectedElements: boolean;
  274. };
  275. withCmdOrCtrl: boolean;
  276. drag: {
  277. // Might change during the pointer interation
  278. hasOccurred: boolean;
  279. // Might change during the pointer interation
  280. offset: { x: number; y: number } | null;
  281. };
  282. // We need to have these in the state so that we can unsubscribe them
  283. eventListeners: {
  284. // It's defined on the initial pointer down event
  285. onMove: null | ((event: PointerEvent) => void);
  286. // It's defined on the initial pointer down event
  287. onUp: null | ((event: PointerEvent) => void);
  288. // It's defined on the initial pointer down event
  289. onKeyDown: null | ((event: KeyboardEvent) => void);
  290. // It's defined on the initial pointer down event
  291. onKeyUp: null | ((event: KeyboardEvent) => void);
  292. };
  293. }>;
  294. export type ExcalidrawImperativeAPI = {
  295. updateScene: InstanceType<typeof App>["updateScene"];
  296. resetScene: InstanceType<typeof App>["resetScene"];
  297. getSceneElementsIncludingDeleted: InstanceType<
  298. typeof App
  299. >["getSceneElementsIncludingDeleted"];
  300. history: {
  301. clear: InstanceType<typeof App>["resetHistory"];
  302. };
  303. scrollToContent: InstanceType<typeof App>["scrollToContent"];
  304. getSceneElements: InstanceType<typeof App>["getSceneElements"];
  305. getAppState: () => InstanceType<typeof App>["state"];
  306. refresh: InstanceType<typeof App>["refresh"];
  307. importLibrary: InstanceType<typeof App>["importLibraryFromUrl"];
  308. setToastMessage: InstanceType<typeof App>["setToastMessage"];
  309. readyPromise: ResolvablePromise<ExcalidrawImperativeAPI>;
  310. ready: true;
  311. id: string;
  312. };