Adds copy, fix bug on mutating bound shapes, adds binding indicator, adds binding to text
This commit is contained in:
parent
89dfd22bac
commit
f6934dedb8
10 changed files with 174 additions and 58 deletions
|
@ -10,7 +10,7 @@ export function Defs({ zoom }: DefProps): JSX.Element {
|
||||||
<circle id="dot" className="tl-counter-scaled tl-dot" r={4} />
|
<circle id="dot" className="tl-counter-scaled tl-dot" r={4} />
|
||||||
<circle id="handle-bg" className="tl-handle-bg" pointerEvents="all" r={12} />
|
<circle id="handle-bg" className="tl-handle-bg" pointerEvents="all" r={12} />
|
||||||
<circle id="handle" className="tl-counter-scaled tl-handle" pointerEvents="none" r={4} />
|
<circle id="handle" className="tl-counter-scaled tl-handle" pointerEvents="none" r={4} />
|
||||||
<g id="cross" className="tl-binding-indicator">
|
<g id="cross" className="tl-anchor-indicator">
|
||||||
<line x1={-6} y1={-6} x2={6} y2={6} />
|
<line x1={-6} y1={-6} x2={6} y2={6} />
|
||||||
<line x1={6} y1={-6} x2={-6} y2={6} />
|
<line x1={6} y1={-6} x2={-6} y2={6} />
|
||||||
</g>
|
</g>
|
||||||
|
|
|
@ -16,6 +16,7 @@ function addToShapeTree<T extends TLShape, M extends Record<string, unknown>>(
|
||||||
shapes: TLPage<T, TLBinding>['shapes'],
|
shapes: TLPage<T, TLBinding>['shapes'],
|
||||||
selectedIds: string[],
|
selectedIds: string[],
|
||||||
pageState: {
|
pageState: {
|
||||||
|
bindingTargetId?: string
|
||||||
bindingId?: string
|
bindingId?: string
|
||||||
hoveredId?: string
|
hoveredId?: string
|
||||||
currentParentId?: string
|
currentParentId?: string
|
||||||
|
@ -28,7 +29,7 @@ function addToShapeTree<T extends TLShape, M extends Record<string, unknown>>(
|
||||||
shape,
|
shape,
|
||||||
isCurrentParent: pageState.currentParentId === shape.id,
|
isCurrentParent: pageState.currentParentId === shape.id,
|
||||||
isEditing: pageState.editingId === shape.id,
|
isEditing: pageState.editingId === shape.id,
|
||||||
isBinding: pageState.bindingId === shape.id,
|
isBinding: pageState.bindingTargetId === shape.id,
|
||||||
meta,
|
meta,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,13 +103,17 @@ export function useShapeTree<T extends TLShape, M extends Record<string, unknown
|
||||||
rPreviousCount.current = shapesToRender.length
|
rPreviousCount.current = shapesToRender.length
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const bindingTargetId = pageState.bindingId ? page.bindings[pageState.bindingId].toId : undefined
|
||||||
|
|
||||||
// Populate the shape tree
|
// Populate the shape tree
|
||||||
|
|
||||||
const tree: IShapeTreeNode<M>[] = []
|
const tree: IShapeTreeNode<M>[] = []
|
||||||
|
|
||||||
shapesToRender
|
shapesToRender
|
||||||
.sort((a, b) => a.childIndex - b.childIndex)
|
.sort((a, b) => a.childIndex - b.childIndex)
|
||||||
.forEach((shape) => addToShapeTree(shape, tree, page.shapes, selectedIds, pageState, meta))
|
.forEach((shape) =>
|
||||||
|
addToShapeTree(shape, tree, page.shapes, selectedIds, { ...pageState, bindingTargetId }, meta)
|
||||||
|
)
|
||||||
|
|
||||||
return tree
|
return tree
|
||||||
}
|
}
|
||||||
|
|
|
@ -241,7 +241,7 @@ const tlcss = css`
|
||||||
}
|
}
|
||||||
.tl-binding-indicator {
|
.tl-binding-indicator {
|
||||||
stroke-width: calc(3px * var(--tl-scale));
|
stroke-width: calc(3px * var(--tl-scale));
|
||||||
fill: none;
|
fill: var(--tl-selectFill);
|
||||||
stroke: var(--tl-selected);
|
stroke: var(--tl-selected);
|
||||||
}
|
}
|
||||||
.tl-shape-group {
|
.tl-shape-group {
|
||||||
|
|
|
@ -101,12 +101,12 @@ export const ContextMenu = React.memo(({ children }: ContextMenuProps): JSX.Elem
|
||||||
tlstate.delete()
|
tlstate.delete()
|
||||||
}, [tlstate])
|
}, [tlstate])
|
||||||
|
|
||||||
const handleCopyAsJson = React.useCallback(() => {
|
const handlecopyJson = React.useCallback(() => {
|
||||||
tlstate.copyAsJson()
|
tlstate.copyJson()
|
||||||
}, [tlstate])
|
}, [tlstate])
|
||||||
|
|
||||||
const handleCopyAsSvg = React.useCallback(() => {
|
const handlecopySvg = React.useCallback(() => {
|
||||||
tlstate.copyAsSvg()
|
tlstate.copySvg()
|
||||||
}, [tlstate])
|
}, [tlstate])
|
||||||
|
|
||||||
const handleUndo = React.useCallback(() => {
|
const handleUndo = React.useCallback(() => {
|
||||||
|
@ -180,12 +180,12 @@ export const ContextMenu = React.memo(({ children }: ContextMenuProps): JSX.Elem
|
||||||
)}
|
)}
|
||||||
{/* <MoveToPageMenu /> */}
|
{/* <MoveToPageMenu /> */}
|
||||||
{isDebugMode && (
|
{isDebugMode && (
|
||||||
<ContextMenuButton onSelect={handleCopyAsJson}>
|
<ContextMenuButton onSelect={handlecopyJson}>
|
||||||
<span>Copy Data</span>
|
<span>Copy Data</span>
|
||||||
<Kbd variant="menu">#⇧C</Kbd>
|
<Kbd variant="menu">#⇧C</Kbd>
|
||||||
</ContextMenuButton>
|
</ContextMenuButton>
|
||||||
)}
|
)}
|
||||||
<ContextMenuButton onSelect={handleCopyAsSvg}>
|
<ContextMenuButton onSelect={handlecopySvg}>
|
||||||
<span>Copy to SVG</span>
|
<span>Copy to SVG</span>
|
||||||
<Kbd variant="menu">#⇧C</Kbd>
|
<Kbd variant="menu">#⇧C</Kbd>
|
||||||
</ContextMenuButton>
|
</ContextMenuButton>
|
||||||
|
|
|
@ -66,8 +66,8 @@ function SelectedShapeContent(): JSX.Element {
|
||||||
tlstate.paste()
|
tlstate.paste()
|
||||||
}, [tlstate])
|
}, [tlstate])
|
||||||
|
|
||||||
const handleCopyAsSvg = React.useCallback(() => {
|
const handlecopySvg = React.useCallback(() => {
|
||||||
tlstate.copyAsSvg()
|
tlstate.copySvg()
|
||||||
}, [tlstate])
|
}, [tlstate])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -88,7 +88,7 @@ function SelectedShapeContent(): JSX.Element {
|
||||||
<span>Paste</span>
|
<span>Paste</span>
|
||||||
{showKbds && <Kbd variant="menu">#V</Kbd>}
|
{showKbds && <Kbd variant="menu">#V</Kbd>}
|
||||||
</RowButton>
|
</RowButton>
|
||||||
<RowButton bp={breakpoints} onClick={handleCopyAsSvg}>
|
<RowButton bp={breakpoints} onClick={handlecopySvg}>
|
||||||
<span>Copy to SVG</span>
|
<span>Copy to SVG</span>
|
||||||
{showKbds && <Kbd variant="menu">⇧#C</Kbd>}
|
{showKbds && <Kbd variant="menu">⇧#C</Kbd>}
|
||||||
</RowButton>
|
</RowButton>
|
||||||
|
|
|
@ -11,6 +11,8 @@ import {
|
||||||
import styled from '~styles'
|
import styled from '~styles'
|
||||||
import TextAreaUtils from './text-utils'
|
import TextAreaUtils from './text-utils'
|
||||||
|
|
||||||
|
const LETTER_SPACING = -1.5
|
||||||
|
|
||||||
function normalizeText(text: string) {
|
function normalizeText(text: string) {
|
||||||
return text.replace(/\r?\n|\r/g, '\n')
|
return text.replace(/\r?\n|\r/g, '\n')
|
||||||
}
|
}
|
||||||
|
@ -31,7 +33,7 @@ function getMeasurementDiv() {
|
||||||
border: '1px solid red',
|
border: '1px solid red',
|
||||||
padding: '4px',
|
padding: '4px',
|
||||||
margin: '0px',
|
margin: '0px',
|
||||||
letterSpacing: '-2.5px',
|
letterSpacing: `${LETTER_SPACING}px`,
|
||||||
opacity: '0',
|
opacity: '0',
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
top: '-500px',
|
top: '-500px',
|
||||||
|
@ -93,6 +95,7 @@ export class Text extends TLDrawShapeUtil<TextShape> {
|
||||||
ref,
|
ref,
|
||||||
meta,
|
meta,
|
||||||
isEditing,
|
isEditing,
|
||||||
|
isBinding,
|
||||||
onTextBlur,
|
onTextBlur,
|
||||||
onTextChange,
|
onTextChange,
|
||||||
onTextFocus,
|
onTextFocus,
|
||||||
|
@ -162,15 +165,15 @@ export class Text extends TLDrawShapeUtil<TextShape> {
|
||||||
if (!isEditing) {
|
if (!isEditing) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{/* {isBinding && (
|
{isBinding && (
|
||||||
<BindingIndicator
|
<rect
|
||||||
as="rect"
|
className="tl-binding-indicator"
|
||||||
x={-32}
|
x={-16}
|
||||||
y={-32}
|
y={-16}
|
||||||
width={bounds.width + 64}
|
width={bounds.width + 32}
|
||||||
height={bounds.height + 64}
|
height={bounds.height + 32}
|
||||||
/>
|
/>
|
||||||
)} */}
|
)}
|
||||||
{text.split('\n').map((str, i) => (
|
{text.split('\n').map((str, i) => (
|
||||||
<text
|
<text
|
||||||
key={i}
|
key={i}
|
||||||
|
@ -179,7 +182,7 @@ export class Text extends TLDrawShapeUtil<TextShape> {
|
||||||
fontFamily="Caveat Brush"
|
fontFamily="Caveat Brush"
|
||||||
fontStyle="normal"
|
fontStyle="normal"
|
||||||
fontWeight="500"
|
fontWeight="500"
|
||||||
letterSpacing="-2.5"
|
letterSpacing={LETTER_SPACING}
|
||||||
fontSize={fontSize}
|
fontSize={fontSize}
|
||||||
width={bounds.width}
|
width={bounds.width}
|
||||||
height={bounds.height}
|
height={bounds.height}
|
||||||
|
@ -372,6 +375,81 @@ export class Text extends TLDrawShapeUtil<TextShape> {
|
||||||
return shape.text.trim().length === 0
|
return shape.text.trim().length === 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getBindingPoint(
|
||||||
|
shape: TextShape,
|
||||||
|
point: number[],
|
||||||
|
origin: number[],
|
||||||
|
direction: number[],
|
||||||
|
padding: number,
|
||||||
|
anywhere: boolean
|
||||||
|
) {
|
||||||
|
const bounds = this.getBounds(shape)
|
||||||
|
|
||||||
|
const expandedBounds = Utils.expandBounds(bounds, padding)
|
||||||
|
|
||||||
|
let bindingPoint: number[]
|
||||||
|
let distance: number
|
||||||
|
|
||||||
|
// The point must be inside of the expanded bounding box
|
||||||
|
if (!Utils.pointInBounds(point, expandedBounds)) return
|
||||||
|
|
||||||
|
// The point is inside of the shape, so we'll assume the user is
|
||||||
|
// indicating a specific point inside of the shape.
|
||||||
|
if (anywhere) {
|
||||||
|
if (Vec.dist(point, this.getCenter(shape)) < 12) {
|
||||||
|
bindingPoint = [0.5, 0.5]
|
||||||
|
} else {
|
||||||
|
bindingPoint = Vec.divV(Vec.sub(point, [expandedBounds.minX, expandedBounds.minY]), [
|
||||||
|
expandedBounds.width,
|
||||||
|
expandedBounds.height,
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
distance = 0
|
||||||
|
} else {
|
||||||
|
// Find furthest intersection between ray from
|
||||||
|
// origin through point and expanded bounds.
|
||||||
|
|
||||||
|
// TODO: Make this a ray vs rounded rect intersection
|
||||||
|
const intersection = Intersect.ray
|
||||||
|
.bounds(origin, direction, expandedBounds)
|
||||||
|
.filter((int) => int.didIntersect)
|
||||||
|
.map((int) => int.points[0])
|
||||||
|
.sort((a, b) => Vec.dist(b, origin) - Vec.dist(a, origin))[0]
|
||||||
|
|
||||||
|
// The anchor is a point between the handle and the intersection
|
||||||
|
const anchor = Vec.med(point, intersection)
|
||||||
|
|
||||||
|
// If we're close to the center, snap to the center
|
||||||
|
if (Vec.distanceToLineSegment(point, anchor, this.getCenter(shape)) < 12) {
|
||||||
|
bindingPoint = [0.5, 0.5]
|
||||||
|
} else {
|
||||||
|
// Or else calculate a normalized point
|
||||||
|
bindingPoint = Vec.divV(Vec.sub(anchor, [expandedBounds.minX, expandedBounds.minY]), [
|
||||||
|
expandedBounds.width,
|
||||||
|
expandedBounds.height,
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Utils.pointInBounds(point, bounds)) {
|
||||||
|
distance = 16
|
||||||
|
} else {
|
||||||
|
// If the binding point was close to the shape's center, snap to the center
|
||||||
|
// Find the distance between the point and the real bounds of the shape
|
||||||
|
distance = Math.max(
|
||||||
|
16,
|
||||||
|
Utils.getBoundsSides(bounds)
|
||||||
|
.map((side) => Vec.distanceToLineSegment(side[1][0], side[1][1], point))
|
||||||
|
.sort((a, b) => a - b)[0]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
point: Vec.clampV(bindingPoint, 0, 1),
|
||||||
|
distance,
|
||||||
|
}
|
||||||
|
}
|
||||||
// getBindingPoint(shape, point, origin, direction, expandDistance) {
|
// getBindingPoint(shape, point, origin, direction, expandDistance) {
|
||||||
// const bounds = this.getBounds(shape)
|
// const bounds = this.getBounds(shape)
|
||||||
|
|
||||||
|
@ -442,7 +520,7 @@ const StyledTextArea = styled('textarea', {
|
||||||
minHeight: 1,
|
minHeight: 1,
|
||||||
minWidth: 1,
|
minWidth: 1,
|
||||||
lineHeight: 1.4,
|
lineHeight: 1.4,
|
||||||
letterSpacing: -2.5,
|
letterSpacing: LETTER_SPACING,
|
||||||
outline: 0,
|
outline: 0,
|
||||||
fontWeight: '500',
|
fontWeight: '500',
|
||||||
backgroundColor: '$boundsBg',
|
backgroundColor: '$boundsBg',
|
||||||
|
|
|
@ -7,9 +7,7 @@ export function style(data: Data, ids: string[], changes: Partial<ShapeStyles>):
|
||||||
const { before, after } = TLDR.mutateShapes(
|
const { before, after } = TLDR.mutateShapes(
|
||||||
data,
|
data,
|
||||||
ids,
|
ids,
|
||||||
(shape) => {
|
(shape) => ({ style: { ...shape.style, ...changes } }),
|
||||||
return { style: { ...shape.style, ...changes } }
|
|
||||||
},
|
|
||||||
currentPageId
|
currentPageId
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -18,7 +16,9 @@ export function style(data: Data, ids: string[], changes: Partial<ShapeStyles>):
|
||||||
before: {
|
before: {
|
||||||
document: {
|
document: {
|
||||||
pages: {
|
pages: {
|
||||||
[currentPageId]: { shapes: before },
|
[currentPageId]: {
|
||||||
|
shapes: before,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
appState: {
|
appState: {
|
||||||
|
@ -28,7 +28,9 @@ export function style(data: Data, ids: string[], changes: Partial<ShapeStyles>):
|
||||||
after: {
|
after: {
|
||||||
document: {
|
document: {
|
||||||
pages: {
|
pages: {
|
||||||
[currentPageId]: { shapes: after },
|
[currentPageId]: {
|
||||||
|
shapes: after,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
appState: {
|
appState: {
|
||||||
|
|
|
@ -427,9 +427,19 @@ export class TLDR {
|
||||||
afterShapes[id] = change
|
afterShapes[id] = change
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const dataWithMutations = Utils.deepMerge(data, {
|
||||||
|
document: {
|
||||||
|
pages: {
|
||||||
|
[data.appState.currentPageId]: {
|
||||||
|
shapes: afterShapes,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
const dataWithChildrenChanges = ids.reduce<Data>((cData, id) => {
|
const dataWithChildrenChanges = ids.reduce<Data>((cData, id) => {
|
||||||
return this.recursivelyUpdateChildren(cData, id, beforeShapes, afterShapes, pageId)
|
return this.recursivelyUpdateChildren(cData, id, beforeShapes, afterShapes, pageId)
|
||||||
}, data)
|
}, dataWithMutations)
|
||||||
|
|
||||||
const dataWithParentChanges = ids.reduce<Data>((cData, id) => {
|
const dataWithParentChanges = ids.reduce<Data>((cData, id) => {
|
||||||
return this.recursivelyUpdateParents(cData, id, beforeShapes, afterShapes, pageId)
|
return this.recursivelyUpdateParents(cData, id, beforeShapes, afterShapes, pageId)
|
||||||
|
|
|
@ -228,10 +228,12 @@ describe('TLDrawState', () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('Copies to JSON', () => {
|
describe('Copies to JSON', () => {
|
||||||
// TODO
|
tlstate.selectAll()
|
||||||
|
tlstate.copyJson()
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('Copies to SVG', () => {
|
describe('Copies to SVG', () => {
|
||||||
// TODO
|
tlstate.selectAll()
|
||||||
|
tlstate.copySvg()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -717,7 +717,7 @@ export class TLDrawState extends StateManager<Data> {
|
||||||
* @param pageId The page from which to copy the shapes.
|
* @param pageId The page from which to copy the shapes.
|
||||||
* @returns A string containing the JSON.
|
* @returns A string containing the JSON.
|
||||||
*/
|
*/
|
||||||
copyAsSvg = (ids = this.selectedIds, pageId = this.currentPageId) => {
|
copySvg = (ids = this.selectedIds, pageId = this.currentPageId) => {
|
||||||
if (ids.length === 0) return
|
if (ids.length === 0) return
|
||||||
|
|
||||||
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg')
|
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg')
|
||||||
|
@ -767,7 +767,7 @@ export class TLDrawState extends StateManager<Data> {
|
||||||
* @param pageId The page from which to copy the shapes.
|
* @param pageId The page from which to copy the shapes.
|
||||||
* @returns A string containing the JSON.
|
* @returns A string containing the JSON.
|
||||||
*/
|
*/
|
||||||
copyAsJson = (ids = this.selectedIds, pageId = this.currentPageId) => {
|
copyJson = (ids = this.selectedIds, pageId = this.currentPageId) => {
|
||||||
const shapes = ids.map((id) => this.getShape(id, pageId))
|
const shapes = ids.map((id) => this.getShape(id, pageId))
|
||||||
const json = JSON.stringify(shapes, null, 2)
|
const json = JSON.stringify(shapes, null, 2)
|
||||||
TLDR.copyStringToClipboard(json)
|
TLDR.copyStringToClipboard(json)
|
||||||
|
@ -784,7 +784,10 @@ export class TLDrawState extends StateManager<Data> {
|
||||||
* @param args arguments of the session's start method.
|
* @param args arguments of the session's start method.
|
||||||
* @returns this
|
* @returns this
|
||||||
*/
|
*/
|
||||||
startSession<T extends Session>(session: T, ...args: ParametersExceptFirst<T['start']>): this {
|
startSession = <T extends Session>(
|
||||||
|
session: T,
|
||||||
|
...args: ParametersExceptFirst<T['start']>
|
||||||
|
): this => {
|
||||||
this.session = session
|
this.session = session
|
||||||
|
|
||||||
const result = session.start(this.state, ...args)
|
const result = session.start(this.state, ...args)
|
||||||
|
@ -813,7 +816,7 @@ export class TLDrawState extends StateManager<Data> {
|
||||||
* @param args The arguments of the current session's update method.
|
* @param args The arguments of the current session's update method.
|
||||||
* @returns this
|
* @returns this
|
||||||
*/
|
*/
|
||||||
updateSession<T extends Session>(...args: ParametersExceptFirst<T['update']>): this {
|
updateSession = <T extends Session>(...args: ParametersExceptFirst<T['update']>): this => {
|
||||||
const { session } = this
|
const { session } = this
|
||||||
if (!session) return this
|
if (!session) return this
|
||||||
const patch = session.update(this.state, ...args)
|
const patch = session.update(this.state, ...args)
|
||||||
|
@ -826,7 +829,7 @@ export class TLDrawState extends StateManager<Data> {
|
||||||
* @param args The arguments of the current session's cancel method.
|
* @param args The arguments of the current session's cancel method.
|
||||||
* @returns this
|
* @returns this
|
||||||
*/
|
*/
|
||||||
cancelSession<T extends Session>(...args: ParametersExceptFirst<T['cancel']>): this {
|
cancelSession = <T extends Session>(...args: ParametersExceptFirst<T['cancel']>): this => {
|
||||||
const { session } = this
|
const { session } = this
|
||||||
if (!session) return this
|
if (!session) return this
|
||||||
this.session = undefined
|
this.session = undefined
|
||||||
|
@ -882,7 +885,7 @@ export class TLDrawState extends StateManager<Data> {
|
||||||
* @param args The arguments of the current session's complete method.
|
* @param args The arguments of the current session's complete method.
|
||||||
* @returns this
|
* @returns this
|
||||||
*/
|
*/
|
||||||
completeSession<T extends Session>(...args: ParametersExceptFirst<T['complete']>) {
|
completeSession = <T extends Session>(...args: ParametersExceptFirst<T['complete']>): this => {
|
||||||
const { session } = this
|
const { session } = this
|
||||||
|
|
||||||
if (!session) return this
|
if (!session) return this
|
||||||
|
@ -1008,20 +1011,22 @@ export class TLDrawState extends StateManager<Data> {
|
||||||
/**
|
/**
|
||||||
* Clear the selection history (undo/redo stack for selection).
|
* Clear the selection history (undo/redo stack for selection).
|
||||||
*/
|
*/
|
||||||
private clearSelectHistory() {
|
private clearSelectHistory = (): this => {
|
||||||
this.selectHistory.pointer = 0
|
this.selectHistory.pointer = 0
|
||||||
this.selectHistory.stack = [this.selectedIds]
|
this.selectHistory.stack = [this.selectedIds]
|
||||||
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a selection to the selection history (undo/redo stack for selection).
|
* Adds a selection to the selection history (undo/redo stack for selection).
|
||||||
*/
|
*/
|
||||||
private addToSelectHistory(ids: string[]) {
|
private addToSelectHistory = (ids: string[]): this => {
|
||||||
if (this.selectHistory.pointer < this.selectHistory.stack.length) {
|
if (this.selectHistory.pointer < this.selectHistory.stack.length) {
|
||||||
this.selectHistory.stack = this.selectHistory.stack.slice(0, this.selectHistory.pointer + 1)
|
this.selectHistory.stack = this.selectHistory.stack.slice(0, this.selectHistory.pointer + 1)
|
||||||
}
|
}
|
||||||
this.selectHistory.pointer++
|
this.selectHistory.pointer++
|
||||||
this.selectHistory.stack.push(ids)
|
this.selectHistory.stack.push(ids)
|
||||||
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1030,7 +1035,7 @@ export class TLDrawState extends StateManager<Data> {
|
||||||
* @param push Whether to add the ids to the current selection instead.
|
* @param push Whether to add the ids to the current selection instead.
|
||||||
* @returns this
|
* @returns this
|
||||||
*/
|
*/
|
||||||
private setSelectedIds(ids: string[], push = false): this {
|
private setSelectedIds = (ids: string[], push = false): this => {
|
||||||
return this.patchState(
|
return this.patchState(
|
||||||
{
|
{
|
||||||
appState: {
|
appState: {
|
||||||
|
@ -1053,7 +1058,7 @@ export class TLDrawState extends StateManager<Data> {
|
||||||
* Undo the most recent selection.
|
* Undo the most recent selection.
|
||||||
* @returns this
|
* @returns this
|
||||||
*/
|
*/
|
||||||
undoSelect(): this {
|
undoSelect = (): this => {
|
||||||
if (this.selectHistory.pointer > 0) {
|
if (this.selectHistory.pointer > 0) {
|
||||||
this.selectHistory.pointer--
|
this.selectHistory.pointer--
|
||||||
this.setSelectedIds(this.selectHistory.stack[this.selectHistory.pointer])
|
this.setSelectedIds(this.selectHistory.stack[this.selectHistory.pointer])
|
||||||
|
@ -1065,7 +1070,7 @@ export class TLDrawState extends StateManager<Data> {
|
||||||
* Redo the previous selection.
|
* Redo the previous selection.
|
||||||
* @returns this
|
* @returns this
|
||||||
*/
|
*/
|
||||||
redoSelect(): this {
|
redoSelect = (): this => {
|
||||||
if (this.selectHistory.pointer < this.selectHistory.stack.length - 1) {
|
if (this.selectHistory.pointer < this.selectHistory.stack.length - 1) {
|
||||||
this.selectHistory.pointer++
|
this.selectHistory.pointer++
|
||||||
this.setSelectedIds(this.selectHistory.stack[this.selectHistory.pointer])
|
this.setSelectedIds(this.selectHistory.stack[this.selectHistory.pointer])
|
||||||
|
@ -1178,7 +1183,8 @@ export class TLDrawState extends StateManager<Data> {
|
||||||
* @param ids The ids of the shapes to change (defaults to selection).
|
* @param ids The ids of the shapes to change (defaults to selection).
|
||||||
* @returns this
|
* @returns this
|
||||||
*/
|
*/
|
||||||
style = (style: Partial<ShapeStyles>, ids = this.selectedIds) => {
|
style = (style: Partial<ShapeStyles>, ids = this.selectedIds): this => {
|
||||||
|
if (ids.length === 0) return this
|
||||||
return this.setState(Commands.style(this.state, ids, style))
|
return this.setState(Commands.style(this.state, ids, style))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1188,7 +1194,8 @@ export class TLDrawState extends StateManager<Data> {
|
||||||
* @param ids The ids of the shapes to change (defaults to selection).
|
* @param ids The ids of the shapes to change (defaults to selection).
|
||||||
* @returns this
|
* @returns this
|
||||||
*/
|
*/
|
||||||
align = (type: AlignType, ids = this.selectedIds) => {
|
align = (type: AlignType, ids = this.selectedIds): this => {
|
||||||
|
if (ids.length < 2) return this
|
||||||
return this.setState(Commands.align(this.state, ids, type))
|
return this.setState(Commands.align(this.state, ids, type))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1198,7 +1205,8 @@ export class TLDrawState extends StateManager<Data> {
|
||||||
* @param ids The ids of the shapes to change (defaults to selection).
|
* @param ids The ids of the shapes to change (defaults to selection).
|
||||||
* @returns this
|
* @returns this
|
||||||
*/
|
*/
|
||||||
distribute = (direction: DistributeType, ids = this.selectedIds) => {
|
distribute = (direction: DistributeType, ids = this.selectedIds): this => {
|
||||||
|
if (ids.length < 3) return this
|
||||||
return this.setState(Commands.distribute(this.state, ids, direction))
|
return this.setState(Commands.distribute(this.state, ids, direction))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1208,7 +1216,8 @@ export class TLDrawState extends StateManager<Data> {
|
||||||
* @param ids The ids of the shapes to change (defaults to selection).
|
* @param ids The ids of the shapes to change (defaults to selection).
|
||||||
* @returns this
|
* @returns this
|
||||||
*/
|
*/
|
||||||
stretch = (direction: StretchType, ids = this.selectedIds) => {
|
stretch = (direction: StretchType, ids = this.selectedIds): this => {
|
||||||
|
if (ids.length < 2) return this
|
||||||
return this.setState(Commands.stretch(this.state, ids, direction))
|
return this.setState(Commands.stretch(this.state, ids, direction))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1217,7 +1226,8 @@ export class TLDrawState extends StateManager<Data> {
|
||||||
* @param ids The ids of the shapes to change (defaults to selection).
|
* @param ids The ids of the shapes to change (defaults to selection).
|
||||||
* @returns this
|
* @returns this
|
||||||
*/
|
*/
|
||||||
flipHorizontal = (ids = this.selectedIds) => {
|
flipHorizontal = (ids = this.selectedIds): this => {
|
||||||
|
if (ids.length === 0) return this
|
||||||
return this.setState(Commands.flip(this.state, ids, FlipType.Horizontal))
|
return this.setState(Commands.flip(this.state, ids, FlipType.Horizontal))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1226,7 +1236,8 @@ export class TLDrawState extends StateManager<Data> {
|
||||||
* @param ids The ids of the shapes to change (defaults to selection).
|
* @param ids The ids of the shapes to change (defaults to selection).
|
||||||
* @returns this
|
* @returns this
|
||||||
*/
|
*/
|
||||||
flipVertical = (ids = this.selectedIds) => {
|
flipVertical = (ids = this.selectedIds): this => {
|
||||||
|
if (ids.length === 0) return this
|
||||||
return this.setState(Commands.flip(this.state, ids, FlipType.Vertical))
|
return this.setState(Commands.flip(this.state, ids, FlipType.Vertical))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1235,7 +1246,8 @@ export class TLDrawState extends StateManager<Data> {
|
||||||
* @param ids The ids of the shapes to change (defaults to selection).
|
* @param ids The ids of the shapes to change (defaults to selection).
|
||||||
* @returns this
|
* @returns this
|
||||||
*/
|
*/
|
||||||
moveToBack = (ids = this.selectedIds) => {
|
moveToBack = (ids = this.selectedIds): this => {
|
||||||
|
if (ids.length === 0) return this
|
||||||
return this.setState(Commands.move(this.state, ids, MoveType.ToBack))
|
return this.setState(Commands.move(this.state, ids, MoveType.ToBack))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1244,7 +1256,8 @@ export class TLDrawState extends StateManager<Data> {
|
||||||
* @param ids The ids of the shapes to change (defaults to selection).
|
* @param ids The ids of the shapes to change (defaults to selection).
|
||||||
* @returns this
|
* @returns this
|
||||||
*/
|
*/
|
||||||
moveBackward = (ids = this.selectedIds) => {
|
moveBackward = (ids = this.selectedIds): this => {
|
||||||
|
if (ids.length === 0) return this
|
||||||
return this.setState(Commands.move(this.state, ids, MoveType.Backward))
|
return this.setState(Commands.move(this.state, ids, MoveType.Backward))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1253,7 +1266,8 @@ export class TLDrawState extends StateManager<Data> {
|
||||||
* @param ids The ids of the shapes to change (defaults to selection).
|
* @param ids The ids of the shapes to change (defaults to selection).
|
||||||
* @returns this
|
* @returns this
|
||||||
*/
|
*/
|
||||||
moveForward = (ids = this.selectedIds) => {
|
moveForward = (ids = this.selectedIds): this => {
|
||||||
|
if (ids.length === 0) return this
|
||||||
return this.setState(Commands.move(this.state, ids, MoveType.Forward))
|
return this.setState(Commands.move(this.state, ids, MoveType.Forward))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1262,7 +1276,8 @@ export class TLDrawState extends StateManager<Data> {
|
||||||
* @param ids The ids of the shapes to change (defaults to selection).
|
* @param ids The ids of the shapes to change (defaults to selection).
|
||||||
* @returns this
|
* @returns this
|
||||||
*/
|
*/
|
||||||
moveToFront = (ids = this.selectedIds) => {
|
moveToFront = (ids = this.selectedIds): this => {
|
||||||
|
if (ids.length === 0) return this
|
||||||
return this.setState(Commands.move(this.state, ids, MoveType.ToFront))
|
return this.setState(Commands.move(this.state, ids, MoveType.ToFront))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1274,6 +1289,7 @@ export class TLDrawState extends StateManager<Data> {
|
||||||
* @returns this
|
* @returns this
|
||||||
*/
|
*/
|
||||||
nudge = (delta: number[], isMajor = false, ids = this.selectedIds): this => {
|
nudge = (delta: number[], isMajor = false, ids = this.selectedIds): this => {
|
||||||
|
if (ids.length === 0) return this
|
||||||
return this.setState(Commands.translate(this.state, ids, Vec.mul(delta, isMajor ? 10 : 1)))
|
return this.setState(Commands.translate(this.state, ids, Vec.mul(delta, isMajor ? 10 : 1)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1283,6 +1299,7 @@ export class TLDrawState extends StateManager<Data> {
|
||||||
* @returns this
|
* @returns this
|
||||||
*/
|
*/
|
||||||
duplicate = (ids = this.selectedIds): this => {
|
duplicate = (ids = this.selectedIds): this => {
|
||||||
|
if (ids.length === 0) return this
|
||||||
return this.setState(Commands.duplicate(this.state, ids))
|
return this.setState(Commands.duplicate(this.state, ids))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1292,6 +1309,7 @@ export class TLDrawState extends StateManager<Data> {
|
||||||
* @returns this
|
* @returns this
|
||||||
*/
|
*/
|
||||||
toggleHidden = (ids = this.selectedIds): this => {
|
toggleHidden = (ids = this.selectedIds): this => {
|
||||||
|
if (ids.length === 0) return this
|
||||||
return this.setState(Commands.toggle(this.state, ids, 'isHidden'))
|
return this.setState(Commands.toggle(this.state, ids, 'isHidden'))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1301,6 +1319,7 @@ export class TLDrawState extends StateManager<Data> {
|
||||||
* @returns this
|
* @returns this
|
||||||
*/
|
*/
|
||||||
toggleLocked = (ids = this.selectedIds): this => {
|
toggleLocked = (ids = this.selectedIds): this => {
|
||||||
|
if (ids.length === 0) return this
|
||||||
return this.setState(Commands.toggle(this.state, ids, 'isLocked'))
|
return this.setState(Commands.toggle(this.state, ids, 'isLocked'))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1310,6 +1329,7 @@ export class TLDrawState extends StateManager<Data> {
|
||||||
* @returns this
|
* @returns this
|
||||||
*/
|
*/
|
||||||
toggleAspectRatioLocked = (ids = this.selectedIds): this => {
|
toggleAspectRatioLocked = (ids = this.selectedIds): this => {
|
||||||
|
if (ids.length === 0) return this
|
||||||
return this.setState(Commands.toggle(this.state, ids, 'isAspectRatioLocked'))
|
return this.setState(Commands.toggle(this.state, ids, 'isAspectRatioLocked'))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1320,13 +1340,10 @@ export class TLDrawState extends StateManager<Data> {
|
||||||
* @returns this
|
* @returns this
|
||||||
*/
|
*/
|
||||||
toggleDecoration = (handleId: string, ids = this.selectedIds): this => {
|
toggleDecoration = (handleId: string, ids = this.selectedIds): this => {
|
||||||
if (handleId === 'start' || handleId === 'end') {
|
if (ids.length === 0 || !(handleId === 'start' || handleId === 'end')) return this
|
||||||
return this.setState(Commands.toggleDecoration(this.state, ids, handleId))
|
return this.setState(Commands.toggleDecoration(this.state, ids, handleId))
|
||||||
}
|
}
|
||||||
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rotate one or more shapes by a delta.
|
* Rotate one or more shapes by a delta.
|
||||||
* @param delta The delta in radians.
|
* @param delta The delta in radians.
|
||||||
|
@ -1334,6 +1351,7 @@ export class TLDrawState extends StateManager<Data> {
|
||||||
* @returns this
|
* @returns this
|
||||||
*/
|
*/
|
||||||
rotate = (delta = Math.PI * -0.5, ids = this.selectedIds): this => {
|
rotate = (delta = Math.PI * -0.5, ids = this.selectedIds): this => {
|
||||||
|
if (ids.length === 0) return this
|
||||||
return this.setState(Commands.rotate(this.state, ids, delta))
|
return this.setState(Commands.rotate(this.state, ids, delta))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1343,6 +1361,7 @@ export class TLDrawState extends StateManager<Data> {
|
||||||
* @todo
|
* @todo
|
||||||
*/
|
*/
|
||||||
group = (): this => {
|
group = (): this => {
|
||||||
|
// TODO
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue