Skip to content

Commit

Permalink
alex/state-node-prototype-2: wip
Browse files Browse the repository at this point in the history
  • Loading branch information
SomeHats committed Feb 27, 2024
1 parent b2ce8ba commit fb5afbf
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Tldraw } from '@tldraw/tldraw'
import '@tldraw/tldraw/tldraw.css'
import { speechBubbleControl } from './SpeechBubble/SpeechBubbleHandle'
import { SpeechBubbleTool } from './SpeechBubble/SpeechBubbleTool'
import { SpeechBubbleUtil } from './SpeechBubble/SpeechBubbleUtil'
import { components, customAssetUrls, uiOverrides } from './SpeechBubble/ui-overrides'
Expand All @@ -16,6 +17,9 @@ export default function CustomShapeWithHandles() {
return (
<div style={{ position: 'absolute', inset: 0 }}>
<Tldraw
onMount={(editor) => {
editor.addControls(speechBubbleControl)
}}
shapeUtils={shapeUtils}
tools={tools}
overrides={uiOverrides}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Control, Editor } from '@tldraw/tldraw'
import { SpeechBubbleShape } from './SpeechBubbleUtil'

export function speechBubbleControl(editor: Editor) {
// we only show the control in select.idle
if (!editor.isIn('select.idle')) return null

// it's only relevant when we have a single speech bubble shape selected
const shape = editor.getOnlySelectedShape()
if (!shape || !editor.isShapeOfType<SpeechBubbleShape>(shape, 'speech-bubble')) {
return null
}

// return the control - this handles the actual interaction.
return new SpeechBubbleControl(shape)
}

class SpeechBubbleControl extends Control {
constructor(readonly shape: SpeechBubbleShape) {
super()
}
}
1 change: 1 addition & 0 deletions packages/editor/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ export { getArrowTerminalsInArrowSpace } from './lib/editor/shapes/shared/arrow/
export { resizeBox, type ResizeBoxOptions } from './lib/editor/shapes/shared/resizeBox'
export { BaseBoxShapeTool } from './lib/editor/tools/BaseBoxShapeTool/BaseBoxShapeTool'
export { StateNode, type TLStateNodeConstructor } from './lib/editor/tools/StateNode'
export { Control, type ControlFn } from './lib/editor/types/Control'
export { type SvgExportContext, type SvgExportDef } from './lib/editor/types/SvgExportContext'
export { type TLContent } from './lib/editor/types/clipboard-types'
export { type TLEventMap, type TLEventMapHandler } from './lib/editor/types/emit-types'
Expand Down
43 changes: 42 additions & 1 deletion packages/editor/src/lib/editor/Editor.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { EMPTY_ARRAY, atom, computed, transact } from '@tldraw/state'
import { Atom, Computed, EMPTY_ARRAY, atom, computed, transact } from '@tldraw/state'
import { ComputedCache, RecordType } from '@tldraw/store'
import {
CameraRecordType,
Expand Down Expand Up @@ -119,6 +119,7 @@ import { getArrowTerminalsInArrowSpace, getIsArrowStraight } from './shapes/shar
import { getStraightArrowInfo } from './shapes/shared/arrow/straight-arrow'
import { RootState } from './tools/RootState'
import { StateNode, TLStateNodeConstructor } from './tools/StateNode'
import { Control, ControlFn } from './types/Control'
import { SvgExportContext, SvgExportDef } from './types/SvgExportContext'
import { TLContent } from './types/clipboard-types'
import { TLEventMap } from './types/emit-types'
Expand Down Expand Up @@ -8928,6 +8929,46 @@ export class Editor extends EventEmitter<TLEventMap> {

return this
}

// controls
private controlsMap: Atom<ReadonlyMap<ControlFn, Computed<readonly Control[]>>> = atom(
'controls',
new Map()
)
addControls(controlFn: ControlFn) {
this.controlsMap.update((prevControls) => {
if (prevControls.has(controlFn)) return prevControls
const nextControls = new Map(prevControls)
nextControls.set(
controlFn,
computed('control', () => {
const result = controlFn(this)
if (!result) return EMPTY_ARRAY
if (Array.isArray(result)) return result
return [result]
})
)
return nextControls
})
}
removeControls(controlFn: ControlFn) {
this.controlsMap.update((prevControls) => {
if (!prevControls.has(controlFn)) return prevControls
const nextControls = new Map(prevControls)
nextControls.delete(controlFn)
return nextControls
})
}

@computed getControls(): readonly Control[] {
const results = []
for (const controls of this.controlsMap.get().values()) {
for (const control of controls.get()) {
results.push(control)
}
}
return results
}
}

function alertMaxShapes(editor: Editor, pageId = editor.getCurrentPageId()) {
Expand Down
12 changes: 12 additions & 0 deletions packages/editor/src/lib/editor/types/Control.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { ReactNode } from 'react'
import { Geometry2d } from '../../primitives/geometry/Geometry2d'
import { Editor } from '../Editor'

export type ControlFn = (editor: Editor) => null | Control | Control[]

export abstract class Control {
abstract getGeometry(): Geometry2d
component(): ReactNode {
return null
}
}

0 comments on commit fb5afbf

Please sign in to comment.