types.ts 10 KB

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