You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

types.ts 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654
  1. /* -------------------------------------------------- */
  2. /* Client State */
  3. /* -------------------------------------------------- */
  4. export interface Data {
  5. isReadOnly: boolean
  6. settings: {
  7. fontSize: number
  8. isDarkMode: boolean
  9. isCodeOpen: boolean
  10. isTestMode: boolean
  11. isDebugOpen: boolean
  12. isDebugMode: boolean
  13. isStyleOpen: boolean
  14. nudgeDistanceSmall: number
  15. nudgeDistanceLarge: number
  16. isToolLocked: boolean
  17. isPenLocked: boolean
  18. }
  19. room?: {
  20. id: string
  21. status: string
  22. peers: Record<string, CoopPresence>
  23. }
  24. currentStyle: ShapeStyles
  25. activeTool: ShapeType | 'select'
  26. brush?: Bounds
  27. boundsRotation: number
  28. pointedId?: string
  29. hoveredId?: string
  30. editingId?: string
  31. currentPageId: string
  32. currentParentId: string
  33. currentCodeFileId: string
  34. codeControls: Record<string, CodeControl>
  35. document: TLDocument
  36. pageStates: Record<string, PageState>
  37. }
  38. /* -------------------------------------------------- */
  39. /* Document */
  40. /* -------------------------------------------------- */
  41. export type CoopPresence = {
  42. id: string
  43. bufferedXs: number[]
  44. bufferedYs: number[]
  45. times: number[]
  46. duration: number
  47. pageId: string
  48. }
  49. export interface TLDocument {
  50. id: string
  51. name: string
  52. pages: Record<string, Page>
  53. code: Record<string, CodeFile>
  54. }
  55. export interface Page {
  56. id: string
  57. type: 'page'
  58. childIndex: number
  59. name: string
  60. shapes: Record<string, Shape>
  61. }
  62. export interface PageState {
  63. id: string
  64. selectedIds: string[]
  65. camera: {
  66. point: number[]
  67. zoom: number
  68. }
  69. }
  70. /* ----------------- Start Copy Here ---------------- */
  71. export enum ShapeType {
  72. Dot = 'dot',
  73. Ellipse = 'ellipse',
  74. Line = 'line',
  75. Ray = 'ray',
  76. Polyline = 'polyline',
  77. Rectangle = 'rectangle',
  78. Draw = 'draw',
  79. Arrow = 'arrow',
  80. Text = 'text',
  81. Group = 'group',
  82. }
  83. export enum ColorStyle {
  84. White = 'White',
  85. LightGray = 'LightGray',
  86. Gray = 'Gray',
  87. Black = 'Black',
  88. Green = 'Green',
  89. Cyan = 'Cyan',
  90. Blue = 'Blue',
  91. Indigo = 'Indigo',
  92. Violet = 'Violet',
  93. Red = 'Red',
  94. Orange = 'Orange',
  95. Yellow = 'Yellow',
  96. }
  97. export enum SizeStyle {
  98. Small = 'Small',
  99. Medium = 'Medium',
  100. Large = 'Large',
  101. }
  102. export enum DashStyle {
  103. Draw = 'Draw',
  104. Solid = 'Solid',
  105. Dashed = 'Dashed',
  106. Dotted = 'Dotted',
  107. }
  108. export enum FontSize {
  109. Small = 'Small',
  110. Medium = 'Medium',
  111. Large = 'Large',
  112. ExtraLarge = 'ExtraLarge',
  113. }
  114. export type Theme = 'dark' | 'light'
  115. export type ShapeStyles = {
  116. color: ColorStyle
  117. size: SizeStyle
  118. dash: DashStyle
  119. isFilled: boolean
  120. }
  121. export interface BaseShape {
  122. id: string
  123. type: ShapeType
  124. parentId: string
  125. childIndex: number
  126. name: string
  127. point: number[]
  128. style: ShapeStyles
  129. rotation: number
  130. children?: string[]
  131. bindings?: Record<string, ShapeBinding>
  132. handles?: Record<string, ShapeHandle>
  133. isLocked?: boolean
  134. isHidden?: boolean
  135. isEditing?: boolean
  136. isGenerated?: boolean
  137. isAspectRatioLocked?: boolean
  138. }
  139. export interface DotShape extends BaseShape {
  140. type: ShapeType.Dot
  141. }
  142. export interface EllipseShape extends BaseShape {
  143. type: ShapeType.Ellipse
  144. radiusX: number
  145. radiusY: number
  146. }
  147. export interface LineShape extends BaseShape {
  148. type: ShapeType.Line
  149. direction: number[]
  150. }
  151. export interface RayShape extends BaseShape {
  152. type: ShapeType.Ray
  153. direction: number[]
  154. }
  155. export interface PolylineShape extends BaseShape {
  156. type: ShapeType.Polyline
  157. points: number[][]
  158. }
  159. export interface RectangleShape extends BaseShape {
  160. type: ShapeType.Rectangle
  161. size: number[]
  162. radius: number
  163. }
  164. export interface DrawShape extends BaseShape {
  165. type: ShapeType.Draw
  166. points: number[][]
  167. }
  168. export interface ArrowShape extends BaseShape {
  169. type: ShapeType.Arrow
  170. handles: Record<string, ShapeHandle>
  171. bend: number
  172. decorations?: {
  173. start: Decoration
  174. end: Decoration
  175. middle: Decoration
  176. }
  177. }
  178. export interface TextShape extends BaseShape {
  179. type: ShapeType.Text
  180. text: string
  181. scale: number
  182. }
  183. export interface GroupShape extends BaseShape {
  184. type: ShapeType.Group
  185. children: string[]
  186. size: number[]
  187. }
  188. export type ShapeProps<T extends Shape> = {
  189. [P in keyof T]?: P extends 'style' ? Partial<T[P]> : T[P]
  190. }
  191. export interface MutableShapes {
  192. [ShapeType.Dot]: DotShape
  193. [ShapeType.Ellipse]: EllipseShape
  194. [ShapeType.Line]: LineShape
  195. [ShapeType.Ray]: RayShape
  196. [ShapeType.Polyline]: PolylineShape
  197. [ShapeType.Draw]: DrawShape
  198. [ShapeType.Rectangle]: RectangleShape
  199. [ShapeType.Arrow]: ArrowShape
  200. [ShapeType.Text]: TextShape
  201. [ShapeType.Group]: GroupShape
  202. }
  203. export type MutableShape = MutableShapes[keyof MutableShapes]
  204. export type Shapes = { [K in keyof MutableShapes]: Readonly<MutableShapes[K]> }
  205. export type Shape = Readonly<MutableShape>
  206. export type ShapeByType<T extends ShapeType> = Shapes[T]
  207. export type IsParent<T> = 'children' extends RequiredKeys<T> ? T : never
  208. export type ParentShape = {
  209. [K in keyof MutableShapes]: IsParent<MutableShapes[K]>
  210. }[keyof MutableShapes]
  211. export type ParentTypes = ParentShape['type'] & 'page'
  212. export enum Decoration {
  213. Arrow = 'Arrow',
  214. }
  215. export interface ShapeBinding {
  216. id: string
  217. index: number
  218. point: number[]
  219. }
  220. export interface ShapeHandle {
  221. id: string
  222. index: number
  223. point: number[]
  224. }
  225. export interface CodeFile {
  226. id: string
  227. name: string
  228. code: string
  229. }
  230. export interface CodeError {
  231. message: string
  232. line: number
  233. column: number
  234. }
  235. export interface CodeResult {
  236. shapes: Shape[]
  237. controls: CodeControl[]
  238. error: CodeError
  239. }
  240. /* -------------------------------------------------- */
  241. /* Editor UI */
  242. /* -------------------------------------------------- */
  243. export interface PointerInfo {
  244. target: string
  245. pointerId: number
  246. origin: number[]
  247. point: number[]
  248. pressure: number
  249. shiftKey: boolean
  250. ctrlKey: boolean
  251. metaKey: boolean
  252. altKey: boolean
  253. }
  254. export interface KeyboardInfo {
  255. key: string
  256. keys: string[]
  257. shiftKey: boolean
  258. ctrlKey: boolean
  259. metaKey: boolean
  260. altKey: boolean
  261. }
  262. export enum Edge {
  263. Top = 'top_edge',
  264. Right = 'right_edge',
  265. Bottom = 'bottom_edge',
  266. Left = 'left_edge',
  267. }
  268. export enum Corner {
  269. TopLeft = 'top_left_corner',
  270. TopRight = 'top_right_corner',
  271. BottomRight = 'bottom_right_corner',
  272. BottomLeft = 'bottom_left_corner',
  273. }
  274. export interface Bounds {
  275. minX: number
  276. minY: number
  277. maxX: number
  278. maxY: number
  279. width: number
  280. height: number
  281. rotation?: number
  282. }
  283. export interface RotatedBounds extends Bounds {
  284. rotation: number
  285. }
  286. export interface ShapeBounds extends Bounds {
  287. id: string
  288. }
  289. export interface PointSnapshot extends Bounds {
  290. nx: number
  291. nmx: number
  292. ny: number
  293. nmy: number
  294. }
  295. export interface BoundsSnapshot extends PointSnapshot {
  296. nw: number
  297. nh: number
  298. }
  299. export type ShapeSpecificProps<T extends Shape> = Pick<
  300. T,
  301. Difference<keyof T, keyof BaseShape>
  302. >
  303. export type ShapeIndicatorProps<T extends Shape> = ShapeSpecificProps<T>
  304. export type ShapeUtil<K extends Shape> = {
  305. create(props: Partial<K>): K
  306. getBounds(shape: K): Bounds
  307. hitTest(shape: K, test: number[]): boolean
  308. hitTestBounds(shape: K, bounds: Bounds): boolean
  309. rotate(shape: K): K
  310. translate(shape: K, delta: number[]): K
  311. scale(shape: K, scale: number): K
  312. stretch(shape: K, scaleX: number, scaleY: number): K
  313. render(shape: K): JSX.Element
  314. }
  315. export enum MoveType {
  316. Backward,
  317. Forward,
  318. ToFront,
  319. ToBack,
  320. }
  321. export enum AlignType {
  322. Top,
  323. CenterVertical,
  324. Bottom,
  325. Left,
  326. CenterHorizontal,
  327. Right,
  328. }
  329. export enum StretchType {
  330. Horizontal,
  331. Vertical,
  332. }
  333. export enum DistributeType {
  334. Horizontal,
  335. Vertical,
  336. }
  337. export interface BezierCurveSegment {
  338. start: number[]
  339. tangentStart: number[]
  340. normalStart: number[]
  341. pressureStart: number
  342. end: number[]
  343. tangentEnd: number[]
  344. normalEnd: number[]
  345. pressureEnd: number
  346. }
  347. /* -------------------------------------------------- */
  348. /* Code Editor */
  349. /* -------------------------------------------------- */
  350. export enum ControlType {
  351. Number = 'number',
  352. Vector = 'vector',
  353. Text = 'text',
  354. Select = 'select',
  355. }
  356. export interface BaseCodeControl {
  357. id: string
  358. type: ControlType
  359. label: string
  360. }
  361. export interface NumberCodeControl extends BaseCodeControl {
  362. type: ControlType.Number
  363. value: number
  364. min?: number
  365. max?: number
  366. step?: number
  367. format?: (value: number) => number
  368. }
  369. export interface VectorCodeControl extends BaseCodeControl {
  370. type: ControlType.Vector
  371. value: number[]
  372. min?: number
  373. max?: number
  374. step?: number
  375. isNormalized?: boolean
  376. format?: (value: number[]) => number[]
  377. }
  378. export interface TextCodeControl extends BaseCodeControl {
  379. type: ControlType.Text
  380. value: string
  381. format?: (value: string) => string
  382. }
  383. export interface SelectCodeControl<T extends string = ''>
  384. extends BaseCodeControl {
  385. type: ControlType.Select
  386. value: T
  387. options: T[]
  388. format?: (string: T) => string
  389. }
  390. export type CodeControl =
  391. | NumberCodeControl
  392. | VectorCodeControl
  393. | TextCodeControl
  394. export type PropsOfType<T extends Record<string, unknown>> = {
  395. [K in keyof T]: T[K] extends boolean ? K : never
  396. }[keyof T]
  397. export type Mutable<T extends Shape> = { -readonly [K in keyof T]: T[K] }
  398. export interface ShapeUtility<K extends Shape> {
  399. // Default properties when creating a new shape
  400. defaultProps: K
  401. // A cache for the computed bounds of this kind of shape.
  402. boundsCache: WeakMap<K, Bounds>
  403. // Whether to show transform controls when this shape is selected.
  404. canTransform: boolean
  405. // Whether the shape's aspect ratio can change.
  406. canChangeAspectRatio: boolean
  407. // Whether the shape's style can be filled.
  408. canStyleFill: boolean
  409. // Whether the shape may be edited in an editing mode
  410. canEdit: boolean
  411. // Whether the shape is a foreign object.
  412. isForeignObject: boolean
  413. // Whether the shape can contain other shapes.
  414. isParent: boolean
  415. // Whether the shape is only shown when on hovered.
  416. isShy: boolean
  417. // Create a new shape.
  418. create(this: ShapeUtility<K>, props: Partial<K>): K
  419. // Update a shape's styles
  420. applyStyles(
  421. this: ShapeUtility<K>,
  422. shape: Mutable<K>,
  423. style: Partial<ShapeStyles>
  424. ): ShapeUtility<K>
  425. translateBy(
  426. this: ShapeUtility<K>,
  427. shape: Mutable<K>,
  428. point: number[]
  429. ): ShapeUtility<K>
  430. translateTo(
  431. this: ShapeUtility<K>,
  432. shape: Mutable<K>,
  433. point: number[]
  434. ): ShapeUtility<K>
  435. rotateBy(
  436. this: ShapeUtility<K>,
  437. shape: Mutable<K>,
  438. rotation: number
  439. ): ShapeUtility<K>
  440. rotateTo(
  441. this: ShapeUtility<K>,
  442. shape: Mutable<K>,
  443. rotation: number,
  444. delta: number
  445. ): ShapeUtility<K>
  446. // Transform to fit a new bounding box when more than one shape is selected.
  447. transform(
  448. this: ShapeUtility<K>,
  449. shape: Mutable<K>,
  450. bounds: Bounds,
  451. info: {
  452. type: Edge | Corner
  453. initialShape: K
  454. scaleX: number
  455. scaleY: number
  456. transformOrigin: number[]
  457. }
  458. ): ShapeUtility<K>
  459. // Transform a single shape to fit a new bounding box.
  460. transformSingle(
  461. this: ShapeUtility<K>,
  462. shape: Mutable<K>,
  463. bounds: Bounds,
  464. info: {
  465. type: Edge | Corner
  466. initialShape: K
  467. scaleX: number
  468. scaleY: number
  469. transformOrigin: number[]
  470. }
  471. ): ShapeUtility<K>
  472. setProperty<P extends keyof K>(
  473. this: ShapeUtility<K>,
  474. shape: Mutable<K>,
  475. prop: P,
  476. value: K[P]
  477. ): ShapeUtility<K>
  478. // Respond when any child of this shape changes.
  479. onChildrenChange(
  480. this: ShapeUtility<K>,
  481. shape: Mutable<K>,
  482. children: Shape[]
  483. ): ShapeUtility<K>
  484. // Respond when a user moves one of the shape's bound elements.
  485. onBindingChange(
  486. this: ShapeUtility<K>,
  487. shape: Mutable<K>,
  488. bindings: Record<string, ShapeBinding>
  489. ): ShapeUtility<K>
  490. // Respond when a user moves one of the shape's handles.
  491. onHandleChange(
  492. this: ShapeUtility<K>,
  493. shape: Mutable<K>,
  494. handle: Partial<K['handles']>,
  495. info?: Partial<{
  496. delta: number[]
  497. shiftKey: boolean
  498. altKey: boolean
  499. metaKey: boolean
  500. }>
  501. ): ShapeUtility<K>
  502. onDoublePointHandle(
  503. this: ShapeUtility<K>,
  504. shape: Mutable<K>,
  505. handle: keyof K['handles'],
  506. info: PointerInfo
  507. ): ShapeUtility<K>
  508. // Respond when a user double clicks the shape's bounds.
  509. onBoundsReset(this: ShapeUtility<K>, shape: Mutable<K>): ShapeUtility<K>
  510. // Respond when a user double clicks the center of the shape.
  511. onDoubleFocus(this: ShapeUtility<K>, shape: Mutable<K>): ShapeUtility<K>
  512. // Clean up changes when a session ends.
  513. onSessionComplete(this: ShapeUtility<K>, shape: Mutable<K>): ShapeUtility<K>
  514. // Render a shape to JSX.
  515. render(
  516. this: ShapeUtility<K>,
  517. shape: K,
  518. info?: {
  519. isEditing?: boolean
  520. isHovered?: boolean
  521. isSelected?: boolean
  522. isCurrentParent?: boolean
  523. isDarkMode?: boolean
  524. ref?: React.MutableRefObject<HTMLTextAreaElement>
  525. }
  526. ): JSX.Element
  527. invalidate(this: ShapeUtility<K>, shape: K): ShapeUtility<K>
  528. // Get the bounds of the a shape.
  529. getBounds(this: ShapeUtility<K>, shape: K): Bounds
  530. // Get the routated bounds of the a shape.
  531. getRotatedBounds(this: ShapeUtility<K>, shape: K): Bounds
  532. // Get the center of the shape
  533. getCenter(this: ShapeUtility<K>, shape: K): number[]
  534. // Test whether a point lies within a shape.
  535. hitTest(this: ShapeUtility<K>, shape: K, test: number[]): boolean
  536. // Test whether bounds collide with or contain a shape.
  537. hitTestBounds(this: ShapeUtility<K>, shape: K, bounds: Bounds): boolean
  538. // Get whether the shape should delete
  539. shouldDelete(this: ShapeUtility<K>, shape: K): boolean
  540. // Get whether the shape should render
  541. shouldRender(this: ShapeUtility<K>, shape: K, previous: K): boolean
  542. }
  543. /* -------------------------------------------------- */
  544. /* Utilities */
  545. /* -------------------------------------------------- */
  546. export type Difference<A, B> = A extends B ? never : A
  547. export type RequiredKeys<T> = {
  548. [K in keyof T]-?: Record<string, unknown> extends Pick<T, K> ? never : K
  549. }[keyof T]