kopia lustrzana https://github.com/Tldraw/Tldraw
146 wiersze
3.8 KiB
TypeScript
146 wiersze
3.8 KiB
TypeScript
import { Utils, TLBounds } from '@tldraw/core'
|
|
import { Vec } from '@tldraw/vec'
|
|
import { TLDrawSnapshot, Session, SessionType, TLDrawPatch, TLDrawStatus } from '~types'
|
|
import { TLDR } from '~state/TLDR'
|
|
|
|
export class BrushSession extends Session {
|
|
static type = SessionType.Brush
|
|
status = TLDrawStatus.Brushing
|
|
origin: number[]
|
|
snapshot: BrushSnapshot
|
|
|
|
constructor(data: TLDrawSnapshot, viewport: TLBounds, point: number[]) {
|
|
super(viewport)
|
|
this.origin = Vec.round(point)
|
|
this.snapshot = getBrushSnapshot(data)
|
|
}
|
|
|
|
start = () => void null
|
|
|
|
update = (
|
|
data: TLDrawSnapshot,
|
|
point: number[],
|
|
_shiftKey = false,
|
|
_altKey = false,
|
|
metaKey = false
|
|
): TLDrawPatch => {
|
|
const { snapshot, origin } = this
|
|
const { currentPageId } = data.appState
|
|
|
|
// Create a bounding box between the origin and the new point
|
|
const brush = Utils.getBoundsFromPoints([origin, point])
|
|
|
|
// Find ids of brushed shapes
|
|
const hits = new Set<string>()
|
|
const selectedIds = new Set(snapshot.selectedIds)
|
|
|
|
const page = TLDR.getPage(data, currentPageId)
|
|
|
|
snapshot.shapesToTest.forEach(({ id, util, selectId }) => {
|
|
if (selectedIds.has(id)) return
|
|
|
|
const shape = page.shapes[id]
|
|
|
|
if (!hits.has(selectId)) {
|
|
if (
|
|
metaKey
|
|
? Utils.boundsContain(brush, util.getBounds(shape))
|
|
: util.hitTestBounds(shape, brush)
|
|
) {
|
|
hits.add(selectId)
|
|
|
|
// When brushing a shape, select its top group parent.
|
|
if (!selectedIds.has(selectId)) {
|
|
selectedIds.add(selectId)
|
|
}
|
|
} else if (selectedIds.has(selectId)) {
|
|
selectedIds.delete(selectId)
|
|
}
|
|
}
|
|
})
|
|
|
|
const currentSelectedIds = data.document.pageStates[data.appState.currentPageId].selectedIds
|
|
|
|
const didChange =
|
|
selectedIds.size !== currentSelectedIds.length ||
|
|
currentSelectedIds.some((id) => !selectedIds.has(id))
|
|
|
|
const afterSelectedIds = didChange ? Array.from(selectedIds.values()) : currentSelectedIds
|
|
|
|
return {
|
|
document: {
|
|
pageStates: {
|
|
[currentPageId]: {
|
|
brush,
|
|
selectedIds: afterSelectedIds,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
cancel = (data: TLDrawSnapshot) => {
|
|
const { currentPageId } = data.appState
|
|
return {
|
|
document: {
|
|
pageStates: {
|
|
[currentPageId]: {
|
|
brush: null,
|
|
selectedIds: this.snapshot.selectedIds,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
complete = (data: TLDrawSnapshot) => {
|
|
const { currentPageId } = data.appState
|
|
const pageState = TLDR.getPageState(data, currentPageId)
|
|
|
|
return {
|
|
document: {
|
|
pageStates: {
|
|
[currentPageId]: {
|
|
brush: null,
|
|
selectedIds: [...pageState.selectedIds],
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get a snapshot of the current selected ids, for each shape that is
|
|
* not already selected, the shape's id and a test to see whether the
|
|
* brush will intersect that shape. For tests, start broad -> fine.
|
|
*/
|
|
export function getBrushSnapshot(data: TLDrawSnapshot) {
|
|
const { currentPageId } = data.appState
|
|
const selectedIds = [...TLDR.getSelectedIds(data, currentPageId)]
|
|
|
|
const shapesToTest = TLDR.getShapes(data, currentPageId)
|
|
.filter(
|
|
(shape) =>
|
|
!(
|
|
shape.isHidden ||
|
|
shape.children !== undefined ||
|
|
selectedIds.includes(shape.id) ||
|
|
selectedIds.includes(shape.parentId)
|
|
)
|
|
)
|
|
.map((shape) => ({
|
|
id: shape.id,
|
|
util: TLDR.getShapeUtils(shape),
|
|
bounds: TLDR.getShapeUtils(shape).getBounds(shape),
|
|
selectId: shape.id, //TLDR.getTopParentId(data, shape.id, currentPageId),
|
|
}))
|
|
|
|
return {
|
|
selectedIds,
|
|
shapesToTest,
|
|
}
|
|
}
|
|
|
|
export type BrushSnapshot = ReturnType<typeof getBrushSnapshot>
|