kopia lustrzana https://github.com/Tldraw/Tldraw
208 wiersze
6.2 KiB
TypeScript
208 wiersze
6.2 KiB
TypeScript
import {
|
|
ANIMATION_MEDIUM_MS,
|
|
Editor,
|
|
TLNoteShape,
|
|
TLShape,
|
|
Vec,
|
|
compact,
|
|
createShapeId,
|
|
} from '@tldraw/editor'
|
|
|
|
/** @internal */
|
|
export const ADJACENT_NOTE_MARGIN = 20
|
|
/** @internal */
|
|
export const CLONE_HANDLE_MARGIN = 0
|
|
/** @internal */
|
|
export const NOTE_SIZE = 200
|
|
/** @internal */
|
|
export const CENTER_OFFSET = { x: NOTE_SIZE / 2, y: NOTE_SIZE / 2 }
|
|
/** @internal */
|
|
export const NOTE_PIT_RADIUS = 10
|
|
/** @internal */
|
|
export type NotePit = Vec
|
|
|
|
const DEFAULT_PITS: NotePit[] = [
|
|
new Vec(NOTE_SIZE * 0.5, NOTE_SIZE * -0.5 - ADJACENT_NOTE_MARGIN), // t
|
|
new Vec(NOTE_SIZE * 1.5 + ADJACENT_NOTE_MARGIN, NOTE_SIZE * 0.5), // r
|
|
new Vec(NOTE_SIZE * 0.5, NOTE_SIZE * 1.5 + ADJACENT_NOTE_MARGIN), // b
|
|
new Vec(NOTE_SIZE * -0.5 - ADJACENT_NOTE_MARGIN, NOTE_SIZE * 0.5), // l
|
|
]
|
|
|
|
/**
|
|
* Get the adjacent positions for a particular note shape.
|
|
*
|
|
* @param pagePoint - The point of the note shape on the page.
|
|
* @param pageRotation - The rotation of the note shape on the page.
|
|
* @param growY - The growY of the note shape.
|
|
* @param extraHeight - The extra height to add to the top position above the note shape (ie the growY of the dragging shape).
|
|
*
|
|
* @internal */
|
|
export function getNoteAdjacentPositions(
|
|
pagePoint: Vec,
|
|
pageRotation: number,
|
|
growY: number,
|
|
extraHeight: number
|
|
) {
|
|
return DEFAULT_PITS.map((v, i) => {
|
|
const point = v.clone()
|
|
if (i === 0 && extraHeight) {
|
|
// apply top margin (the growY of the moving note shape)
|
|
point.y -= extraHeight
|
|
} else if (i === 2 && growY) {
|
|
// apply bottom margin (the growY of this note shape)
|
|
point.y += growY
|
|
}
|
|
return point.rot(pageRotation).add(pagePoint)
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Get all of the available note adjacent positions, excluding the selected shapes.
|
|
*
|
|
* @param editor - The editor instance.
|
|
* @param rotation - The rotation of the note shape.
|
|
* @param extraHeight - The extra height to add to the top position above the note shape (ie the growY of the dragging shape).
|
|
*
|
|
* @internal */
|
|
export function getAvailableNoteAdjacentPositions(
|
|
editor: Editor,
|
|
rotation: number,
|
|
extraHeight: number
|
|
) {
|
|
const selectedShapeIds = editor.getSelectedShapeIds()
|
|
const allUnselectedNoteShapes = editor
|
|
.getCurrentPageShapes()
|
|
.filter((shape) => shape.type === 'note' && !selectedShapeIds.includes(shape.id))
|
|
return compact(
|
|
allUnselectedNoteShapes.flatMap((shape) => {
|
|
if (!editor.isShapeOfType<TLNoteShape>(shape, 'note')) return
|
|
const transform = editor.getShapePageTransform(shape.id)!
|
|
const pageRotation = transform.rotation()
|
|
// We only want to create pits for notes with the specified rotation
|
|
if (rotation !== pageRotation) return
|
|
const pagePoint = transform.point()
|
|
return getNoteAdjacentPositions(pagePoint, pageRotation, shape.props.growY, extraHeight)
|
|
})
|
|
).filter((pit) => !allUnselectedNoteShapes.some((shape) => editor.isPointInShape(shape, pit)))
|
|
}
|
|
|
|
/**
|
|
* For a particular adjacent note position, get the shape in that position or create a new one.
|
|
*
|
|
* @param editor - The editor instance.
|
|
* @param shape - The note shape to create or select.
|
|
* @param center - The center of the note shape.
|
|
* @param pageRotation - The rotation of the note shape on the page.
|
|
* @param forceNew - Whether to force the creation of a new note shape.
|
|
*
|
|
* @internal */
|
|
export function getNoteShapeForAdjacentPosition(
|
|
editor: Editor,
|
|
shape: TLNoteShape,
|
|
center: Vec,
|
|
pageRotation: number,
|
|
forceNew = false
|
|
) {
|
|
// There might already be a note in that position! If there is, we'll
|
|
// select the next note and switch focus to it. If there's not, then
|
|
// we'll create a new note in that position.
|
|
|
|
let nextNote: TLShape | undefined
|
|
|
|
// Check the center of where a new note would be
|
|
// Start from the top of the stack, and work our way down
|
|
const allShapesOnPage = editor.getCurrentPageShapesSorted()
|
|
|
|
for (let i = allShapesOnPage.length - 1; i >= 0; i--) {
|
|
const otherNote = allShapesOnPage[i]
|
|
if (otherNote.type === 'note') {
|
|
if (otherNote.id === shape.id) continue
|
|
if (editor.isPointInShape(otherNote, center)) {
|
|
nextNote = otherNote
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
editor.complete()
|
|
editor.mark()
|
|
|
|
// If we didn't find any in that position, then create a new one
|
|
if (!nextNote || forceNew) {
|
|
const id = createShapeId()
|
|
|
|
// We create it at the center first, so that it becomes
|
|
// the child of whatever parent was at that center
|
|
editor.createShape({
|
|
id,
|
|
type: 'note',
|
|
x: center.x,
|
|
y: center.y,
|
|
rotation: pageRotation,
|
|
opacity: shape.opacity,
|
|
props: {
|
|
// Use the props of the shape we're cloning
|
|
...shape.props,
|
|
// ...except for these values, which should reset to their defaults
|
|
text: '',
|
|
growY: 0,
|
|
fontSizeAdjustment: 0,
|
|
url: '',
|
|
},
|
|
})
|
|
|
|
// Now we need to correct its location within its new parent
|
|
|
|
const createdShape = editor.getShape(id)!
|
|
|
|
// We need to put the page point in the same coordinate
|
|
// space as the newly created shape (i.e its parent's space)
|
|
const topLeft = editor.getPointInParentSpace(
|
|
createdShape,
|
|
Vec.Sub(center, Vec.Rot(CENTER_OFFSET, pageRotation))
|
|
)
|
|
|
|
editor.updateShape({
|
|
id,
|
|
type: 'note',
|
|
x: topLeft.x,
|
|
y: topLeft.y,
|
|
})
|
|
|
|
nextNote = editor.getShape(id)!
|
|
}
|
|
|
|
// Animate to the next sticky if it would be off screen
|
|
const selectionPageBounds = editor.getSelectionPageBounds()
|
|
const viewportPageBounds = editor.getViewportPageBounds()
|
|
if (selectionPageBounds && !viewportPageBounds.contains(selectionPageBounds)) {
|
|
editor.centerOnPoint(selectionPageBounds.center, {
|
|
duration: ANIMATION_MEDIUM_MS,
|
|
})
|
|
}
|
|
|
|
return nextNote
|
|
}
|
|
|
|
/** @internal */
|
|
export function startEditingNoteShape(editor: Editor, shape: TLShape) {
|
|
// Finish this sticky and start editing the next one
|
|
editor.select(shape)
|
|
editor.setEditingShape(shape)
|
|
editor.setCurrentTool('select.editing_shape', {
|
|
target: 'shape',
|
|
shape: shape,
|
|
})
|
|
|
|
// Select any text that's in the newly selected sticky
|
|
;(document.getElementById(`text-input-${shape.id}`) as HTMLTextAreaElement)?.select()
|
|
|
|
const selectionPageBounds = editor.getSelectionPageBounds()
|
|
const viewportPageBounds = editor.getViewportPageBounds()
|
|
if (selectionPageBounds && !viewportPageBounds.contains(selectionPageBounds)) {
|
|
editor.centerOnPoint(selectionPageBounds.center, {
|
|
duration: ANIMATION_MEDIUM_MS,
|
|
})
|
|
}
|
|
}
|