No impure getters pt3 (#2203)

Follow up to #2189 and #2202 

### Change Type

- [x] `patch` — Bug fix

[^1]: publishes a `patch` release, for devDependencies use `internal`
[^2]: will not publish a new version
This commit is contained in:
David Sheldrick 2023-11-13 14:31:27 +00:00 committed by GitHub
parent 2ca2f81f2a
commit 7ffda2335c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
52 changed files with 309 additions and 209 deletions

View file

@ -24,7 +24,7 @@ test.describe.skip('clipboard tests', () => {
await page.mouse.up()
expect(await page.evaluate(() => editor.currentPageShapes.length)).toBe(1)
expect(await page.evaluate(() => editor.selectedShapes.length)).toBe(1)
expect(await page.evaluate(() => editor.getSelectedShapes().length)).toBe(1)
await page.keyboard.down('Control')
await page.keyboard.press('KeyC')
@ -33,7 +33,7 @@ test.describe.skip('clipboard tests', () => {
await page.keyboard.up('Control')
expect(await page.evaluate(() => editor.currentPageShapes.length)).toBe(2)
expect(await page.evaluate(() => editor.selectedShapes.length)).toBe(1)
expect(await page.evaluate(() => editor.getSelectedShapes().length)).toBe(1)
})
test('copy and paste from main menu', async ({ page }) => {
@ -43,7 +43,7 @@ test.describe.skip('clipboard tests', () => {
await page.mouse.up()
expect(await page.evaluate(() => editor.currentPageShapes.length)).toBe(1)
expect(await page.evaluate(() => editor.selectedShapes.length)).toBe(1)
expect(await page.evaluate(() => editor.getSelectedShapes().length)).toBe(1)
await page.getByTestId('main.menu').click()
await page.getByTestId('menu-item.edit').click()
@ -54,7 +54,7 @@ test.describe.skip('clipboard tests', () => {
await page.getByTestId('menu-item.paste').click()
expect(await page.evaluate(() => editor.currentPageShapes.length)).toBe(2)
expect(await page.evaluate(() => editor.selectedShapes.length)).toBe(1)
expect(await page.evaluate(() => editor.getSelectedShapes().length)).toBe(1)
})
test('copy and paste from context menu', async ({ page }) => {
@ -64,7 +64,7 @@ test.describe.skip('clipboard tests', () => {
await page.mouse.up()
expect(await page.evaluate(() => editor.currentPageShapes.length)).toBe(1)
expect(await page.evaluate(() => editor.selectedShapes.length)).toBe(1)
expect(await page.evaluate(() => editor.getSelectedShapes().length)).toBe(1)
await page.mouse.click(100, 100, { button: 'right' })
await page.getByTestId('menu-item.copy').click()
@ -74,6 +74,6 @@ test.describe.skip('clipboard tests', () => {
await page.getByTestId('menu-item.paste').click()
expect(await page.evaluate(() => editor.currentPageShapes.length)).toBe(2)
expect(await page.evaluate(() => editor.selectedShapes.length)).toBe(1)
expect(await page.evaluate(() => editor.getSelectedShapes().length)).toBe(1)
})
})

View file

@ -65,7 +65,7 @@ test.describe('smoke tests', () => {
expect(await getAllShapeTypes(page)).toEqual(['geo'])
const getSelectedShapeColor = async () =>
await page.evaluate(() => (editor.selectedShapes[0] as TLGeoShape).props.color)
await page.evaluate(() => (editor.getSelectedShapes()[0] as TLGeoShape).props.color)
// change style
expect(await getSelectedShapeColor()).toBe('black')

View file

@ -24,7 +24,7 @@ type ShapeWithMyMeta = TLShape & { meta: { label: string } }
export const ShapeLabelUiWithHelper = track(function ShapeLabelUiWithHelper() {
const editor = useEditor()
const onlySelectedShape = editor.onlySelectedShape as ShapeWithMyMeta | null
const onlySelectedShape = editor.getOnlySelectedShape() as ShapeWithMyMeta | null
if (!onlySelectedShape) {
return null

View file

@ -103,7 +103,7 @@ class PointingState extends StateNode {
override onPointerMove: TLEventHandlers['onPointerUp'] = () => {
if (this.editor.inputs.isDragging) {
this.parent.transition('dragging', { shapes: [...this.editor.selectedShapes] })
this.parent.transition('dragging', { shapes: [...this.editor.getSelectedShapes()] })
}
}
}

View file

@ -689,6 +689,7 @@ export class Editor extends EventEmitter<TLEventMap> {
getInitialMetaForShape(_shape: TLShape): JsonObject;
getInstanceState(): TLInstance;
getIsMenuOpen(): boolean;
getOnlySelectedShape(): null | TLShape;
getOpenMenus(): string[];
getOutermostSelectableShape(shape: TLShape | TLShapeId, filter?: (shape: TLShape) => boolean): TLShape;
getPage(page: TLPage | TLPageId): TLPage | undefined;
@ -698,6 +699,7 @@ export class Editor extends EventEmitter<TLEventMap> {
getPointInShapeSpace(shape: TLShape | TLShapeId, point: VecLike): Vec2d;
getSelectedShapeAtPoint(point: VecLike): TLShape | undefined;
getSelectedShapeIds(): TLShapeId[];
getSelectedShapes(): TLShape[];
getShape<T extends TLShape = TLShape>(shape: TLParentId | TLShape): T | undefined;
getShapeAncestors(shape: TLShape | TLShapeId, acc?: TLShape[]): TLShape[];
getShapeAndDescendantIds(ids: TLShapeId[]): Set<TLShapeId>;
@ -791,6 +793,7 @@ export class Editor extends EventEmitter<TLEventMap> {
mark(markId?: string, onUndo?: boolean, onRedo?: boolean): this;
moveShapesToPage(shapes: TLShape[] | TLShapeId[], pageId: TLPageId): this;
nudgeShapes(shapes: TLShape[] | TLShapeId[], offset: VecLike, historyOptions?: TLCommandHistoryOptions): this;
// @deprecated (undocumented)
get onlySelectedShape(): null | TLShape;
// @deprecated (undocumented)
get openMenus(): string[];
@ -849,6 +852,7 @@ export class Editor extends EventEmitter<TLEventMap> {
selectAll(): this;
// @deprecated (undocumented)
get selectedShapeIds(): TLShapeId[];
// @deprecated (undocumented)
get selectedShapes(): TLShape[];
get selectionPageBounds(): Box2d | null;
get selectionRotatedPageBounds(): Box2d | undefined;

View file

@ -9068,7 +9068,7 @@
{
"kind": "Method",
"canonicalReference": "@tldraw/editor!Editor#duplicateShapes:member(1)",
"docComment": "/**\n * Duplicate shapes.\n *\n * @param shapes - The shapes (or shape ids) to duplicate.\n *\n * @param offset - The offset (in pixels) to apply to the duplicated shapes.\n *\n * @example\n * ```ts\n * editor.duplicateShapes(['box1', 'box2'], { x: 8, y: 8 })\n * editor.duplicateShapes(editor.selectedShapes, { x: 8, y: 8 })\n * ```\n *\n * @public\n */\n",
"docComment": "/**\n * Duplicate shapes.\n *\n * @param shapes - The shapes (or shape ids) to duplicate.\n *\n * @param offset - The offset (in pixels) to apply to the duplicated shapes.\n *\n * @example\n * ```ts\n * editor.duplicateShapes(['box1', 'box2'], { x: 8, y: 8 })\n * editor.duplicateShapes(editor.getSelectedShapes(), { x: 8, y: 8 })\n * ```\n *\n * @public\n */\n",
"excerptTokens": [
{
"kind": "Content",
@ -10517,6 +10517,42 @@
"isAbstract": false,
"name": "getIsMenuOpen"
},
{
"kind": "Method",
"canonicalReference": "@tldraw/editor!Editor#getOnlySelectedShape:member(1)",
"docComment": "/**\n * The app's only selected shape.\n *\n * @returns Null if there is no shape or more than one selected shape, otherwise the selected shape.\n *\n * @public @readonly\n */\n",
"excerptTokens": [
{
"kind": "Content",
"text": "getOnlySelectedShape(): "
},
{
"kind": "Content",
"text": "null | "
},
{
"kind": "Reference",
"text": "TLShape",
"canonicalReference": "@tldraw/tlschema!TLShape:type"
},
{
"kind": "Content",
"text": ";"
}
],
"isStatic": false,
"returnTypeTokenRange": {
"startIndex": 1,
"endIndex": 3
},
"releaseTag": "Public",
"isProtected": false,
"overloadIndex": 1,
"parameters": [],
"isOptional": false,
"isAbstract": false,
"name": "getOnlySelectedShape"
},
{
"kind": "Method",
"canonicalReference": "@tldraw/editor!Editor#getOpenMenus:member(1)",
@ -11045,6 +11081,42 @@
"isAbstract": false,
"name": "getSelectedShapeIds"
},
{
"kind": "Method",
"canonicalReference": "@tldraw/editor!Editor#getSelectedShapes:member(1)",
"docComment": "/**\n * An array containing all of the currently selected shapes.\n *\n * @public @readonly\n */\n",
"excerptTokens": [
{
"kind": "Content",
"text": "getSelectedShapes(): "
},
{
"kind": "Reference",
"text": "TLShape",
"canonicalReference": "@tldraw/tlschema!TLShape:type"
},
{
"kind": "Content",
"text": "[]"
},
{
"kind": "Content",
"text": ";"
}
],
"isStatic": false,
"returnTypeTokenRange": {
"startIndex": 1,
"endIndex": 3
},
"releaseTag": "Public",
"isProtected": false,
"overloadIndex": 1,
"parameters": [],
"isOptional": false,
"isAbstract": false,
"name": "getSelectedShapes"
},
{
"kind": "Method",
"canonicalReference": "@tldraw/editor!Editor#getShape:member(1)",
@ -14029,7 +14101,7 @@
{
"kind": "Method",
"canonicalReference": "@tldraw/editor!Editor#nudgeShapes:member(1)",
"docComment": "/**\n * Move shapes by a delta.\n *\n * @param shapes - The shapes (or shape ids) to move.\n *\n * @param direction - The direction in which to move the shapes.\n *\n * @param historyOptions - The history options for the change.\n *\n * @example\n * ```ts\n * editor.nudgeShapes(['box1', 'box2'], { x: 8, y: 8 })\n * editor.nudgeShapes(editor.selectedShapes, { x: 8, y: 8 }, { squashing: true })\n * ```\n *\n */\n",
"docComment": "/**\n * Move shapes by a delta.\n *\n * @param shapes - The shapes (or shape ids) to move.\n *\n * @param direction - The direction in which to move the shapes.\n *\n * @param historyOptions - The history options for the change.\n *\n * @example\n * ```ts\n * editor.nudgeShapes(['box1', 'box2'], { x: 8, y: 8 })\n * editor.nudgeShapes(editor.getSelectedShapes(), { x: 8, y: 8 }, { squashing: true })\n * ```\n *\n */\n",
"excerptTokens": [
{
"kind": "Content",
@ -14125,7 +14197,7 @@
{
"kind": "Property",
"canonicalReference": "@tldraw/editor!Editor#onlySelectedShape:member",
"docComment": "/**\n * The app's only selected shape.\n *\n * @returns Null if there is no shape or more than one selected shape, otherwise the selected shape.\n *\n * @example\n * ```ts\n * editor.onlySelectedShape\n * ```\n *\n * @public @readonly\n */\n",
"docComment": "/**\n * @deprecated\n *\n * Use `getOnlySelectedShape` instead.\n */\n",
"excerptTokens": [
{
"kind": "Content",
@ -15775,7 +15847,7 @@
{
"kind": "Property",
"canonicalReference": "@tldraw/editor!Editor#selectedShapes:member",
"docComment": "/**\n * An array containing all of the currently selected shapes.\n *\n * @example\n * ```ts\n * editor.selectedShapes\n * ```\n *\n * @public @readonly\n */\n",
"docComment": "/**\n * @deprecated\n *\n * Use `getSelectedShapes` instead.\n */\n",
"excerptTokens": [
{
"kind": "Content",

View file

@ -214,7 +214,9 @@ function HandlesWrapper() {
const isCoarse = useValue('coarse pointer', () => editor.getInstanceState().isCoarsePointer, [
editor,
])
const onlySelectedShape = useValue('onlySelectedShape', () => editor.onlySelectedShape, [editor])
const onlySelectedShape = useValue('onlySelectedShape', () => editor.getOnlySelectedShape(), [
editor,
])
const isChangingStyle = useValue(
'isChangingStyle',
() => editor.getInstanceState().isChangingStyle,
@ -225,13 +227,24 @@ function HandlesWrapper() {
])
const handles = useValue(
'handles',
() => (editor.onlySelectedShape ? editor.getShapeHandles(editor.onlySelectedShape) : undefined),
() => {
const onlySelectedShape = editor.getOnlySelectedShape()
if (onlySelectedShape) {
return editor.getShapeHandles(onlySelectedShape)
}
return undefined
},
[editor]
)
const transform = useValue(
'transform',
() =>
editor.onlySelectedShape ? editor.getShapePageTransform(editor.onlySelectedShape) : undefined,
() => {
const onlySelectedShape = editor.getOnlySelectedShape()
if (onlySelectedShape) {
return editor.getShapePageTransform(onlySelectedShape)
}
return undefined
},
[editor]
)

View file

@ -20,7 +20,7 @@ export const DefaultSelectionForeground: TLSelectionForegroundComponent = ({
const editor = useEditor()
const rSvg = useRef<SVGSVGElement>(null)
const onlyShape = useValue('only selected shape', () => editor.onlySelectedShape, [editor])
const onlyShape = useValue('only selected shape', () => editor.getOnlySelectedShape(), [editor])
// if all shapes have an expandBy for the selection outline, we can expand by the l
const expandOutlineBy = onlyShape

View file

@ -1002,7 +1002,7 @@ export class Editor extends EventEmitter<TLEventMap> {
},
extras: {
activeStateNode: this.root.path.get(),
selectedShapes: this.selectedShapes,
selectedShapes: this.getSelectedShapes(),
editingShape: this.editingShapeId ? this.getShape(this.editingShapeId) : undefined,
inputs: this.inputs,
},
@ -1481,19 +1481,21 @@ export class Editor extends EventEmitter<TLEventMap> {
/**
* An array containing all of the currently selected shapes.
*
* @example
* ```ts
* editor.selectedShapes
* ```
*
* @public
* @readonly
*/
@computed get selectedShapes(): TLShape[] {
@computed getSelectedShapes(): TLShape[] {
const { selectedShapeIds } = this.getCurrentPageState()
return compact(selectedShapeIds.map((id) => this.store.get(id)))
}
/**
* @deprecated Use `getSelectedShapes` instead.
*/
get selectedShapes() {
return this.getSelectedShapes()
}
/**
* Select one or more shapes.
*
@ -1652,22 +1654,24 @@ export class Editor extends EventEmitter<TLEventMap> {
/**
* The app's only selected shape.
*
* @example
* ```ts
* editor.onlySelectedShape
* ```
*
* @returns Null if there is no shape or more than one selected shape, otherwise the selected
* shape.
*
* @public
* @readonly
*/
@computed get onlySelectedShape(): TLShape | null {
const { selectedShapes } = this
@computed getOnlySelectedShape(): TLShape | null {
const selectedShapes = this.getSelectedShapes()
return selectedShapes.length === 1 ? selectedShapes[0] : null
}
/**
* @deprecated Use `getOnlySelectedShape` instead.
*/
get onlySelectedShape() {
return this.getOnlySelectedShape()
}
/**
* The current page bounds of all the selected shapes. If the
* selection is rotated, then these bounds are the axis-aligned
@ -5138,7 +5142,7 @@ export class Editor extends EventEmitter<TLEventMap> {
* @example
* ```ts
* editor.nudgeShapes(['box1', 'box2'], { x: 8, y: 8 })
* editor.nudgeShapes(editor.selectedShapes, { x: 8, y: 8 }, { squashing: true })
* editor.nudgeShapes(editor.getSelectedShapes(), { x: 8, y: 8 }, { squashing: true })
* ```
*
* @param shapes - The shapes (or shape ids) to move.
@ -5201,7 +5205,7 @@ export class Editor extends EventEmitter<TLEventMap> {
* @example
* ```ts
* editor.duplicateShapes(['box1', 'box2'], { x: 8, y: 8 })
* editor.duplicateShapes(editor.selectedShapes, { x: 8, y: 8 })
* editor.duplicateShapes(editor.getSelectedShapes(), { x: 8, y: 8 })
* ```
*
* @param shapes - The shapes (or shape ids) to duplicate.
@ -7288,7 +7292,7 @@ export class Editor extends EventEmitter<TLEventMap> {
private _selectionSharedStyles = computed<ReadonlySharedStyleMap>(
'_selectionSharedStyles',
() => {
const { selectedShapes } = this
const selectedShapes = this.getSelectedShapes()
const sharedStyles = new SharedStyleMap()
for (const selectedShape of selectedShapes) {
@ -7418,7 +7422,7 @@ export class Editor extends EventEmitter<TLEventMap> {
* @param historyOptions - The history options for the change.
*/
setOpacityForSelectedShapes(opacity: number, historyOptions?: TLCommandHistoryOptions): this {
const { selectedShapes } = this
const selectedShapes = this.getSelectedShapes()
if (selectedShapes.length > 0) {
const shapesToUpdate: TLShape[] = []
@ -7505,7 +7509,7 @@ export class Editor extends EventEmitter<TLEventMap> {
value: T,
historyOptions?: TLCommandHistoryOptions
): this {
const { selectedShapes } = this
const selectedShapes = this.getSelectedShapes()
if (selectedShapes.length > 0) {
const updates: {
@ -7847,7 +7851,7 @@ export class Editor extends EventEmitter<TLEventMap> {
let lowestAncestors: TLShape[] = []
// Among the selected shapes, find the shape with the fewest ancestors and use its first ancestor.
for (const shape of this.selectedShapes) {
for (const shape of this.getSelectedShapes()) {
if (lowestDepth === 0) break
const isFrame = this.isShapeOfType<TLFrameShape>(shape, 'frame')

View file

@ -286,7 +286,7 @@ export class SnapManager {
// This needs to be external from any expensive work
@computed get currentCommonAncestor() {
return this.editor.findCommonAncestor(this.editor.selectedShapes)
return this.editor.findCommonAncestor(this.editor.getSelectedShapes())
}
// Points which belong to snappable shapes

View file

@ -7,11 +7,11 @@ import { Vec2d } from '../primitives/Vec2d'
/** @internal */
export function getRotationSnapshot({ editor }: { editor: Editor }): TLRotationSnapshot | null {
const selectedShapes = editor.getSelectedShapes()
const {
selectionRotation,
selectionRotatedPageBounds: selectionBounds,
inputs: { originPagePoint },
selectedShapes,
} = editor
// todo: this assumes we're rotating the selected shapes

View file

@ -37,8 +37,8 @@ export const TldrawSelectionForeground: TLSelectionForegroundComponent = track(
!editor.getIsMenuOpen() && editor.getInstanceState().cursor.type === 'default'
const isCoarsePointer = editor.getInstanceState().isCoarsePointer
const shapes = editor.selectedShapes
const onlyShape = editor.onlySelectedShape
const shapes = editor.getSelectedShapes()
const onlyShape = editor.getOnlySelectedShape()
const isLockedShape = onlyShape && editor.isShapeOrAncestorLocked(onlyShape)
// if all shapes have an expandBy for the selection outline, we can expand by the l

View file

@ -476,7 +476,7 @@ function centerSelecitonAroundPoint(editor: Editor, position: VecLike) {
const offset = selectionPageBounds!.center.sub(position)
editor.updateShapes(
editor.selectedShapes.map((shape) => {
editor.getSelectedShapes().map((shape) => {
const localRotation = editor.getShapeParentTransform(shape).decompose().rotation
const localDelta = Vec2d.Rot(offset, -localRotation)
return {

View file

@ -488,24 +488,24 @@ describe("an arrow's parents", () => {
editor.setCurrentTool('frame')
editor.pointerDown(0, 0).pointerMove(100, 100).pointerUp()
frameId = editor.onlySelectedShape!.id
frameId = editor.getOnlySelectedShape()!.id
editor.setCurrentTool('geo')
editor.pointerDown(10, 10).pointerMove(20, 20).pointerUp()
boxAid = editor.onlySelectedShape!.id
boxAid = editor.getOnlySelectedShape()!.id
editor.setCurrentTool('geo')
editor.pointerDown(10, 80).pointerMove(20, 90).pointerUp()
boxBid = editor.onlySelectedShape!.id
boxBid = editor.getOnlySelectedShape()!.id
editor.setCurrentTool('geo')
editor.pointerDown(110, 10).pointerMove(120, 20).pointerUp()
boxCid = editor.onlySelectedShape!.id
boxCid = editor.getOnlySelectedShape()!.id
})
it("are updated when the arrow's bound shapes change", () => {
// draw arrow from a to empty space within frame, but don't pointer up yet
editor.setCurrentTool('arrow')
editor.pointerDown(15, 15).pointerMove(50, 50)
const arrowId = editor.onlySelectedShape!.id
const arrowId = editor.getOnlySelectedShape()!.id
expect(editor.getShape(arrowId)).toMatchObject({
props: {
@ -540,7 +540,7 @@ describe("an arrow's parents", () => {
// draw arrow from a to b
editor.setCurrentTool('arrow')
editor.pointerDown(15, 15).pointerMove(15, 85).pointerUp()
const arrowId = editor.onlySelectedShape!.id
const arrowId = editor.getOnlySelectedShape()!.id
expect(editor.getShape(arrowId)).toMatchObject({
parentId: frameId,
@ -564,7 +564,7 @@ describe("an arrow's parents", () => {
// draw arrow from a to c
editor.setCurrentTool('arrow')
editor.pointerDown(15, 15).pointerMove(115, 15).pointerUp()
const arrowId = editor.onlySelectedShape!.id
const arrowId = editor.getOnlySelectedShape()!.id
expect(editor.getShape(arrowId)).toMatchObject({
parentId: editor.currentPageId,
props: {

View file

@ -487,7 +487,7 @@ export class ArrowShapeUtil extends ShapeUtil<TLArrowShape> {
// Not a class component, but eslint can't tell that :(
// eslint-disable-next-line react-hooks/rules-of-hooks
const theme = useDefaultColorTheme()
const onlySelectedShape = this.editor.onlySelectedShape
const onlySelectedShape = this.editor.getOnlySelectedShape()
const shouldDisplayHandles =
this.editor.isInAny(
'select.idle',

View file

@ -18,7 +18,7 @@ export class Idle extends StateNode {
override onKeyUp: TLEventHandlers['onKeyUp'] = (info) => {
if (info.key === 'Enter') {
if (this.editor.getInstanceState().isReadonly) return null
const { onlySelectedShape } = this.editor
const onlySelectedShape = this.editor.getOnlySelectedShape()
// If the only selected shape is editable, start editing it
if (
onlySelectedShape &&

View file

@ -103,7 +103,7 @@ describe('When in the idle state', () => {
expect(editor.currentPageShapes.length).toBe(2)
editor.selectAll()
expect(editor.selectedShapes.length).toBe(2)
expect(editor.getSelectedShapes().length).toBe(2)
editor.keyUp('Enter')
editor.expectPathToBe('root.select.idle')

View file

@ -15,7 +15,7 @@ export class Idle extends StateNode {
if (info.key === 'Enter') {
if (this.editor.getInstanceState().isReadonly) return null
const { onlySelectedShape } = this.editor
const onlySelectedShape = this.editor.getOnlySelectedShape()
// If the only selected shape is editable, start editing it
if (
onlySelectedShape &&

View file

@ -86,7 +86,7 @@ export class ImageShapeUtil extends BaseBoxShapeUtil<TLImageShape> {
const isSelected = useValue(
'onlySelectedShape',
() => shape.id === this.editor.onlySelectedShape?.id,
() => shape.id === this.editor.getOnlySelectedShape()?.id,
[this.editor]
)

View file

@ -24,7 +24,7 @@ export class Idle extends StateNode {
override onKeyDown: TLEventHandlers['onKeyDown'] = (info) => {
if (info.key === 'Enter') {
if (this.editor.getInstanceState().isReadonly) return null
const { onlySelectedShape } = this.editor
const onlySelectedShape = this.editor.getOnlySelectedShape()
// If the only selected shape is editable, start editing it
if (
onlySelectedShape &&

View file

@ -11,7 +11,7 @@ export class Idle extends StateNode {
{ ephemeral: true }
)
const { onlySelectedShape } = this.editor
const onlySelectedShape = this.editor.getOnlySelectedShape()
// well this fucking sucks. what the fuck.
// it's possible for a user to enter cropping, then undo

View file

@ -88,7 +88,7 @@ export class TranslatingCrop extends StateNode {
}
private createSnapshot() {
const shape = this.editor.onlySelectedShape as ShapeWithCrop
const shape = this.editor.getOnlySelectedShape() as ShapeWithCrop
return { shape }
}

View file

@ -60,7 +60,7 @@ export class Cropping extends StateNode {
}
private updateCursor() {
const selectedShape = this.editor.selectedShapes[0]
const selectedShape = this.editor.getSelectedShapes()[0]
if (!selectedShape) return
const cursorType = CursorTypeMap[this.info.handle!]
@ -229,7 +229,7 @@ export class Cropping extends StateNode {
inputs: { originPagePoint },
} = this.editor
const shape = this.editor.onlySelectedShape as TLImageShape
const shape = this.editor.getOnlySelectedShape() as TLImageShape
const selectionBounds = this.editor.selectionRotatedPageBounds!

View file

@ -67,8 +67,8 @@ export class Idle extends StateNode {
}
const selectedShapeIds = this.editor.getSelectedShapeIds()
const onlySelectedShape = this.editor.getOnlySelectedShape()
const {
onlySelectedShape,
inputs: { currentPagePoint },
} = this.editor
@ -224,7 +224,7 @@ export class Idle extends StateNode {
case 'selection': {
if (this.editor.getInstanceState().isReadonly) break
const { onlySelectedShape } = this.editor
const onlySelectedShape = this.editor.getOnlySelectedShape()
if (onlySelectedShape) {
const util = this.editor.getShapeUtil(onlySelectedShape)
@ -346,8 +346,8 @@ export class Idle extends StateNode {
}
const selectedShapeIds = this.editor.getSelectedShapeIds()
const onlySelectedShape = this.editor.getOnlySelectedShape()
const {
onlySelectedShape,
inputs: { currentPagePoint },
} = this.editor
@ -425,7 +425,7 @@ export class Idle extends StateNode {
override onKeyUp = (info: TLKeyboardEventInfo) => {
switch (info.code) {
case 'Enter': {
const { selectedShapes } = this.editor
const selectedShapes = this.editor.getSelectedShapes()
// On enter, if every selected shape is a group, then select all of the children of the groups
if (
@ -438,7 +438,7 @@ export class Idle extends StateNode {
}
// If the only selected shape is editable, then begin editing it
const { onlySelectedShape } = this.editor
const onlySelectedShape = this.editor.getOnlySelectedShape()
if (onlySelectedShape && this.shouldStartEditingShape(onlySelectedShape)) {
this.startEditingShape(onlySelectedShape, {
...info,
@ -457,7 +457,9 @@ export class Idle extends StateNode {
}
}
private shouldStartEditingShape(shape: TLShape | null = this.editor.onlySelectedShape): boolean {
private shouldStartEditingShape(
shape: TLShape | null = this.editor.getOnlySelectedShape()
): boolean {
if (!shape) return false
if (this.editor.isShapeOrAncestorLocked(shape) && shape.type !== 'embed') return false
if (!this.canInteractWithShapeInReadOnly(shape)) return false

View file

@ -25,7 +25,7 @@ export class PointingCropHandle extends StateNode {
override onEnter = (info: TLPointingCropHandleInfo) => {
this.info = info
this.parent.currentToolIdMask = info.onInteractionEnd
const selectedShape = this.editor.selectedShapes[0]
const selectedShape = this.editor.getSelectedShapes()[0]
if (!selectedShape) return
this.updateCursor(selectedShape)

View file

@ -32,7 +32,7 @@ export class PointingResizeHandle extends StateNode {
private info = {} as PointingResizeHandleInfo
private updateCursor() {
const selected = this.editor.selectedShapes
const selected = this.editor.getSelectedShapes()
const cursorType = CursorTypeMap[this.info.handle!]
this.editor.updateInstanceState({
cursor: { type: cursorType, rotation: selected.length === 1 ? selected[0].rotation : 0 },

View file

@ -63,7 +63,9 @@ export class Resizing extends StateNode {
}
this.snapshot = this._createSnapshot()
this.markId = isCreating ? `creating:${this.editor.onlySelectedShape!.id}` : 'starting resizing'
this.markId = isCreating
? `creating:${this.editor.getOnlySelectedShape()!.id}`
: 'starting resizing'
if (!isCreating) this.editor.mark(this.markId)
@ -107,8 +109,9 @@ export class Resizing extends StateNode {
private complete() {
this.handleResizeEnd()
if (this.editAfterComplete && this.editor.onlySelectedShape) {
this.editor.setEditingShape(this.editor.onlySelectedShape.id)
const onlySelectedShape = this.editor.getOnlySelectedShape()
if (this.editAfterComplete && onlySelectedShape) {
this.editor.setEditingShape(onlySelectedShape.id)
this.editor.setCurrentTool('select.editing_shape')
return
}

View file

@ -53,7 +53,7 @@ export class Translating extends StateNode {
this.isCreating = isCreating
this.editAfterComplete = editAfterComplete
this.markId = isCreating ? `creating:${this.editor.onlySelectedShape!.id}` : 'translating'
this.markId = isCreating ? `creating:${this.editor.getOnlySelectedShape()!.id}` : 'translating'
this.editor.mark(this.markId)
this.isCloning = false
this.info = info
@ -166,7 +166,7 @@ export class Translating extends StateNode {
this.editor.setCurrentTool(this.info.onInteractionEnd)
} else {
if (this.editAfterComplete) {
const onlySelected = this.editor.onlySelectedShape
const onlySelected = this.editor.getOnlySelectedShape()
if (onlySelected) {
this.editor.setEditingShape(onlySelected.id)
this.editor.setCurrentTool('select.editing_shape')

View file

@ -1,7 +1,7 @@
import { Editor } from '@tldraw/editor'
export function getShouldEnterCropMode(editor: Editor): boolean {
const { onlySelectedShape } = editor
const onlySelectedShape = editor.getOnlySelectedShape()
return !!(
onlySelectedShape &&
!editor.isShapeOrAncestorLocked(onlySelectedShape) &&

View file

@ -27,7 +27,7 @@ export const ContextMenu = function ContextMenu({ children }: { children: any })
const cb = useCallback(
(isOpen: boolean) => {
if (!isOpen) {
const { onlySelectedShape } = editor
const onlySelectedShape = editor.getOnlySelectedShape()
if (onlySelectedShape && editor.isShapeOrAncestorLocked(onlySelectedShape)) {
editor.setSelectedShapes([])
@ -35,8 +35,8 @@ export const ContextMenu = function ContextMenu({ children }: { children: any })
} else {
// Weird route: selecting locked shapes on long press
if (editor.getInstanceState().isCoarsePointer) {
const selectedShapes = editor.getSelectedShapes()
const {
selectedShapes,
inputs: { currentPagePoint },
} = editor
@ -45,7 +45,7 @@ export const ContextMenu = function ContextMenu({ children }: { children: any })
if (
// if there are no selected shapes
!editor.selectedShapes.length ||
!editor.getSelectedShapes().length ||
// OR if none of the shapes at the point include the selected shape
!shapesAtPoint.some((s) => selectedShapes.includes(s))
) {

View file

@ -175,7 +175,7 @@ const DebugMenuContent = track(function DebugMenuContent({
return count
}
const { selectedShapes } = editor
const selectedShapes = editor.getSelectedShapes()
const shapes = selectedShapes.length === 0 ? editor.renderingShapes : selectedShapes

View file

@ -23,7 +23,7 @@ type ShapeWithUrl = TLBaseShape<string, { url: string }>
export const EditLinkDialog = track(function EditLinkDialog({ onClose }: TLUiDialogProps) {
const editor = useEditor()
const selectedShape = editor.onlySelectedShape
const selectedShape = editor.getOnlySelectedShape()
if (
!(selectedShape && 'url' in selectedShape.props && typeof selectedShape.props.url === 'string')
@ -89,7 +89,7 @@ export const EditLinkDialogInner = track(function EditLinkDialogInner({
}, [])
const handleClear = useCallback(() => {
const { onlySelectedShape } = editor
const onlySelectedShape = editor.getOnlySelectedShape()
if (!onlySelectedShape) return
editor.updateShapes([
{ id: onlySelectedShape.id, type: onlySelectedShape.type, props: { url: '' } },
@ -98,7 +98,7 @@ export const EditLinkDialogInner = track(function EditLinkDialogInner({
}, [editor, onClose])
const handleComplete = useCallback(() => {
const { onlySelectedShape } = editor
const onlySelectedShape = editor.getOnlySelectedShape()
if (!onlySelectedShape) return

View file

@ -233,7 +233,8 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
trackEvent('toggle-auto-size', { source })
editor.mark('toggling auto size')
editor.updateShapes(
editor.selectedShapes
editor
.getSelectedShapes()
.filter(
(shape): shape is TLTextShape =>
editor.isShapeOfType<TLTextShape>(shape, 'text') && shape.props.autoSize === false
@ -299,7 +300,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
editor.batch(() => {
trackEvent('convert-to-bookmark', { source })
const shapes = editor.selectedShapes
const shapes = editor.getSelectedShapes()
const createList: TLShapePartial[] = []
const deleteList: TLShapeId[] = []
@ -441,7 +442,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
if (mustGoBackToSelectToolFirst()) return
trackEvent('group-shapes', { source })
const { onlySelectedShape } = editor
const onlySelectedShape = editor.getOnlySelectedShape()
if (onlySelectedShape && editor.isShapeOfType<TLGroupShape>(onlySelectedShape, 'group')) {
editor.mark('ungroup')
editor.ungroupShapes(editor.getSelectedShapeIds())

View file

@ -76,7 +76,7 @@ export const TLUiContextMenuSchemaProvider = track(function TLUiContextMenuSchem
const allowUngroup = useAllowUngroup()
const hasClipboardWrite = Boolean(window.navigator.clipboard?.write)
const showEditLink = useHasLinkShapeSelected()
const { onlySelectedShape } = editor
const onlySelectedShape = editor.getOnlySelectedShape()
const isShapeLocked = onlySelectedShape && editor.isShapeOrAncestorLocked(onlySelectedShape)
const contextTLUiMenuSchema = useMemo<TLUiMenuSchema>(() => {

View file

@ -5,7 +5,7 @@ export function useHasLinkShapeSelected() {
return useValue(
'hasLinkShapeSelected',
() => {
const { selectedShapes } = editor
const selectedShapes = editor.getSelectedShapes()
return (
selectedShapes.length === 1 &&
'url' in selectedShapes[0].props &&

View file

@ -84,7 +84,7 @@ export function TLUiMenuSchemaProvider({ overrides, children }: TLUiMenuSchemaPr
const oneEmbedSelected = useValue(
'oneEmbedSelected',
() => {
const { onlySelectedShape } = editor
const onlySelectedShape = editor.getOnlySelectedShape()
if (!onlySelectedShape) return false
return !!(
editor.isShapeOfType<TLEmbedShape>(onlySelectedShape, 'embed') &&
@ -98,7 +98,7 @@ export function TLUiMenuSchemaProvider({ overrides, children }: TLUiMenuSchemaPr
const oneEmbeddableBookmarkSelected = useValue(
'oneEmbeddableBookmarkSelected',
() => {
const { onlySelectedShape } = editor
const onlySelectedShape = editor.getOnlySelectedShape()
if (!onlySelectedShape) return false
return !!(
editor.isShapeOfType<TLBookmarkShape>(onlySelectedShape, 'bookmark') &&

View file

@ -12,7 +12,7 @@ export function useOnlyFlippableShape() {
return useValue(
'onlyFlippableShape',
() => {
const { selectedShapes } = editor
const selectedShapes = editor.getSelectedShapes()
return (
selectedShapes.length === 1 &&
selectedShapes.every(

View file

@ -5,7 +5,7 @@ export function useShowAutoSizeToggle() {
return useValue(
'showAutoSizeToggle',
() => {
const { selectedShapes } = editor
const selectedShapes = editor.getSelectedShapes()
return (
selectedShapes.length === 1 &&
editor.isShapeOfType<TLTextShape>(selectedShapes[0], 'text') &&

View file

@ -415,7 +415,7 @@ describe('When clicking and dragging', () => {
describe('Does not erase hollow shapes on click', () => {
it('Returns to select on cancel', () => {
editor.selectAll().deleteShapes(editor.selectedShapes)
editor.selectAll().deleteShapes(editor.getSelectedShapes())
expect(editor.currentPageShapes.length).toBe(0)
editor.createShape({
id: createShapeId(),

View file

@ -19,6 +19,6 @@ describe('Highlight shape', () => {
editor.setCurrentTool('highlight').pointerDown(60, 60).pointerUp()
editor.setCurrentTool('select').pointerDown(70, 70).pointerUp()
expect(editor.selectedShapes).toHaveLength(1)
expect(editor.getSelectedShapes()).toHaveLength(1)
})
})

View file

@ -94,7 +94,7 @@ describe('TLSelectTool.Translating', () => {
editor.pointerDown(150, 150, { target: 'shape', shape })
editor.pointerMove(150, 250)
editor.pointerUp()
const box2Id = editor.onlySelectedShape!.id
const box2Id = editor.getOnlySelectedShape()!.id
expect(editor.currentPageShapes.length).toStrictEqual(2)
expect(ids.box1).not.toEqual(box2Id)
@ -318,11 +318,11 @@ describe('When editing shapes', () => {
// start editing the geo shape
editor.doubleClick(50, 50, { target: 'shape', shape: editor.getShape(ids.geo1) })
expect(editor.editingShapeId).toBe(ids.geo1)
expect(editor.onlySelectedShape?.id).toBe(ids.geo1)
expect(editor.getOnlySelectedShape()?.id).toBe(ids.geo1)
// point the text shape
editor.pointerDown(50, 50, { target: 'shape', shape: editor.getShape(ids.text1) })
expect(editor.editingShapeId).toBe(null)
expect(editor.onlySelectedShape?.id).toBe(ids.text1)
expect(editor.getOnlySelectedShape()?.id).toBe(ids.text1)
})
// The behavior described here will only work end to end, not with the library,
@ -334,12 +334,12 @@ describe('When editing shapes', () => {
// start editing the geo shape
editor.doubleClick(50, 50, { target: 'shape', shape: editor.getShape(ids.geo1) })
expect(editor.editingShapeId).toBe(ids.geo1)
expect(editor.onlySelectedShape?.id).toBe(ids.geo1)
expect(editor.getOnlySelectedShape()?.id).toBe(ids.geo1)
// point the other geo shape
editor.pointerDown(50, 50, { target: 'shape', shape: editor.getShape(ids.geo2) })
// that other shape should now be editing and selected!
expect(editor.editingShapeId).toBe(ids.geo2)
expect(editor.onlySelectedShape?.id).toBe(ids.geo2)
expect(editor.getOnlySelectedShape()?.id).toBe(ids.geo2)
})
// This works but only end to end — the logic had to move to React
@ -352,7 +352,7 @@ describe('When editing shapes', () => {
editor.pointerDown(50, 50, { target: 'shape', shape: editor.getShape(ids.text2) })
// that other shape should now be editing and selected!
expect(editor.editingShapeId).toBe(ids.text2)
expect(editor.onlySelectedShape?.id).toBe(ids.text2)
expect(editor.getOnlySelectedShape()?.id).toBe(ids.text2)
})
it('Double clicking the canvas creates a new text shape', () => {

View file

@ -18,12 +18,12 @@ describe('arrowBindingsIndex', () => {
editor.selectNone()
editor.setCurrentTool('arrow')
editor.pointerDown(50, 50)
expect(editor.onlySelectedShape).toBe(null)
expect(editor.getOnlySelectedShape()).toBe(null)
expect(editor.getArrowsBoundTo(ids.box1)).toEqual([])
editor.pointerMove(50, 55)
expect(editor.onlySelectedShape).not.toBe(null)
const arrow = editor.onlySelectedShape!
expect(editor.getOnlySelectedShape()).not.toBe(null)
const arrow = editor.getOnlySelectedShape()!
expect(arrow.type).toBe('arrow')
expect(editor.getArrowsBoundTo(ids.box1)).toEqual([
{ arrowId: arrow.id, handleId: 'start' },
@ -48,7 +48,7 @@ describe('arrowBindingsIndex', () => {
expect(editor.getArrowsBoundTo(ids.box1)).toEqual([])
editor.pointerMove(250, 50)
const arrow1 = editor.onlySelectedShape!
const arrow1 = editor.getOnlySelectedShape()!
expect(arrow1.type).toBe('arrow')
expect(editor.getArrowsBoundTo(ids.box1)).toEqual([{ arrowId: arrow1.id, handleId: 'start' }])
@ -62,7 +62,7 @@ describe('arrowBindingsIndex', () => {
// start at box 1 and end on the page
editor.setCurrentTool('arrow')
editor.pointerDown(50, 50).pointerMove(50, -50).pointerUp(50, -50)
const arrow2 = editor.onlySelectedShape!
const arrow2 = editor.getOnlySelectedShape()!
expect(arrow2.type).toBe('arrow')
expect(editor.getArrowsBoundTo(ids.box1)).toEqual([
@ -73,7 +73,7 @@ describe('arrowBindingsIndex', () => {
// start outside box 1 and end in box 1
editor.setCurrentTool('arrow')
editor.pointerDown(0, -50).pointerMove(50, 50).pointerUp(50, 50)
const arrow3 = editor.onlySelectedShape!
const arrow3 = editor.getOnlySelectedShape()!
expect(arrow3.type).toBe('arrow')
expect(editor.getArrowsBoundTo(ids.box1)).toEqual([
@ -91,7 +91,7 @@ describe('arrowBindingsIndex', () => {
editor.expectToBeIn('arrow.pointing')
editor.pointerMove(250, -50)
editor.expectToBeIn('select.dragging_handle')
const arrow4 = editor.onlySelectedShape!
const arrow4 = editor.getOnlySelectedShape()!
expect(editor.getArrowsBoundTo(ids.box2)).toEqual([
{ arrowId: arrow1.id, handleId: 'end' },
@ -110,7 +110,7 @@ describe('arrowBindingsIndex', () => {
// start outside box 2 and enter in box 2
editor.setCurrentTool('arrow')
editor.pointerDown(250, -50).pointerMove(250, 50).pointerUp(250, 50)
const arrow5 = editor.onlySelectedShape!
const arrow5 = editor.getOnlySelectedShape()!
expect(arrow5.type).toBe('arrow')
expect(editor.getArrowsBoundTo(ids.box1)).toEqual([
@ -150,23 +150,23 @@ describe('arrowBindingsIndex', () => {
// span both boxes
editor.setCurrentTool('arrow')
editor.pointerDown(50, 50).pointerMove(250, 50).pointerUp(250, 50)
arrowAId = editor.onlySelectedShape!.id
arrowAId = editor.getOnlySelectedShape()!.id
// start at box 1 and leave
editor.setCurrentTool('arrow')
editor.pointerDown(50, 50).pointerMove(50, -50).pointerUp(50, -50)
arrowBId = editor.onlySelectedShape!.id
arrowBId = editor.getOnlySelectedShape()!.id
// start outside box 1 and enter
editor.setCurrentTool('arrow')
editor.pointerDown(50, -50).pointerMove(50, 50).pointerUp(50, 50)
arrowCId = editor.onlySelectedShape!.id
arrowCId = editor.getOnlySelectedShape()!.id
// start at box 2 and leave
editor.setCurrentTool('arrow')
editor.pointerDown(250, 50).pointerMove(250, -50).pointerUp(250, -50)
arrowDId = editor.onlySelectedShape!.id
arrowDId = editor.getOnlySelectedShape()!.id
// start outside box 2 and enter
editor.setCurrentTool('arrow')
editor.pointerDown(250, -50).pointerMove(250, 50).pointerUp(250, 50)
arrowEId = editor.onlySelectedShape!.id
arrowEId = editor.getOnlySelectedShape()!.id
})
it('deletes the entry if you delete the bound shapes', () => {
expect(editor.getArrowsBoundTo(ids.box2)).toHaveLength(3)
@ -236,7 +236,8 @@ describe('arrowBindingsIndex', () => {
editor.selectAll()
editor.duplicateShapes(editor.getSelectedShapeIds())
const [box1Clone, box2Clone] = editor.selectedShapes
const [box1Clone, box2Clone] = editor
.getSelectedShapes()
.filter((shape) => editor.isShapeOfType<TLGeoShape>(shape, 'geo'))
.sort((a, b) => a.x - b.x)

View file

@ -18,7 +18,7 @@ const ids = {
arrow3: createShapeId('arrow3'),
}
const arrow = () => editor.onlySelectedShape as TLArrowShape
const arrow = () => editor.getOnlySelectedShape() as TLArrowShape
beforeEach(() => {
editor = new TestEditor()

View file

@ -10,12 +10,12 @@ beforeEach(() => {
it('Sets shape meta by default to an empty object', () => {
const id = createShapeId()
editor.createShapes([{ id, type: 'geo' }]).select(id)
expect(editor.onlySelectedShape!.meta).toStrictEqual({})
expect(editor.getOnlySelectedShape()!.meta).toStrictEqual({})
})
it('Sets shape meta', () => {
editor.getInitialMetaForShape = (shape) => ({ firstThreeCharactersOfId: shape.id.slice(0, 3) })
const id = createShapeId()
editor.createShapes([{ id, type: 'geo' }]).select(id)
expect(editor.onlySelectedShape!.meta).toStrictEqual({ firstThreeCharactersOfId: 'sha' })
expect(editor.getOnlySelectedShape()!.meta).toStrictEqual({ firstThreeCharactersOfId: 'sha' })
})

View file

@ -189,9 +189,9 @@ describe('arrows', () => {
editor.pointerMove(255, 255)
editor.pointerMove(450, 450)
editor.pointerUp(450, 450)
const arrow = editor.onlySelectedShape!
const arrow = editor.getOnlySelectedShape()!
expect(editor.getShapePageBounds(editor.onlySelectedShape!)).toCloselyMatchObject({
expect(editor.getShapePageBounds(editor.getOnlySelectedShape()!)).toCloselyMatchObject({
// exiting at the bottom right corner of the first box
x: 300,
y: 300,
@ -224,7 +224,7 @@ describe('arrows', () => {
.pointerMove(255, 255)
.pointerMove(450, 450)
.pointerUp(450, 450)
const arrow = editor.onlySelectedShape!
const arrow = editor.getOnlySelectedShape()!
expect(editor.getArrowsBoundTo(firstBox.id).length).toBe(1)
expect(editor.getArrowsBoundTo(secondBox.id).length).toBe(1)

View file

@ -50,7 +50,7 @@ describe('when multiple shapes are selected', () => {
it('stretches horizontally and preserves aspect ratio', () => {
const videoA = createVideoShape()
editor.selectAll()
expect(editor.selectedShapes.length).toBe(4)
expect(editor.getSelectedShapes().length).toBe(4)
editor.stretchShapes(editor.getSelectedShapeIds(), 'horizontal')
jest.advanceTimersByTime(1000)
const newHeight = (500 * 9) / 16
@ -76,7 +76,7 @@ describe('when multiple shapes are selected', () => {
it('stretches vertically and preserves aspect ratio', () => {
const videoA = createVideoShape()
editor.selectAll()
expect(editor.selectedShapes.length).toBe(4)
expect(editor.getSelectedShapes().length).toBe(4)
editor.stretchShapes(editor.getSelectedShapeIds(), 'vertical')
jest.advanceTimersByTime(1000)
const newWidth = (500 * 16) / 9

View file

@ -297,7 +297,7 @@ describe('When one shape is selected', () => {
editor.selectAll()
editor.groupShapes(editor.getSelectedShapeIds()) // this will also select the new group
const groupBefore = editor.selectedShapes[0]
const groupBefore = editor.getSelectedShapes()[0]
editor.on('change', fn)
editor.flipShapes(editor.getSelectedShapeIds(), 'horizontal')

View file

@ -20,8 +20,8 @@ describe('creating frames', () => {
it('can be done', () => {
editor.setCurrentTool('frame')
editor.pointerDown(100, 100).pointerMove(200, 200).pointerUp(200, 200)
expect(editor.onlySelectedShape?.type).toBe('frame')
expect(editor.getShapePageBounds(editor.onlySelectedShape!)).toMatchObject({
expect(editor.getOnlySelectedShape()?.type).toBe('frame')
expect(editor.getShapePageBounds(editor.getOnlySelectedShape()!)).toMatchObject({
x: 100,
y: 100,
w: 100,
@ -31,9 +31,9 @@ describe('creating frames', () => {
it('will create with a default size if no dragging happens', () => {
editor.setCurrentTool('frame')
editor.pointerDown(100, 100).pointerUp(100, 100)
expect(editor.onlySelectedShape?.type).toBe('frame')
expect(editor.getOnlySelectedShape()?.type).toBe('frame')
const { w, h } = editor.getShapeUtil<TLFrameShape>('frame').getDefaultProps()
expect(editor.getShapePageBounds(editor.onlySelectedShape!)).toMatchObject({
expect(editor.getShapePageBounds(editor.getOnlySelectedShape()!)).toMatchObject({
x: 100 - w / 2,
y: 100 - h / 2,
w,
@ -43,7 +43,7 @@ describe('creating frames', () => {
it('can be canceled while pointing', () => {
editor.setCurrentTool('frame')
editor.pointerDown(100, 100).cancel().pointerUp(100, 100)
expect(editor.onlySelectedShape?.type).toBe(undefined)
expect(editor.getOnlySelectedShape()?.type).toBe(undefined)
expect(editor.currentPageShapes).toHaveLength(0)
})
it('can be canceled while dragging', () => {
@ -52,35 +52,35 @@ describe('creating frames', () => {
editor.expectPathToBe('root.select.resizing')
editor.cancel()
editor.pointerUp()
expect(editor.onlySelectedShape?.type).toBe(undefined)
expect(editor.getOnlySelectedShape()?.type).toBe(undefined)
expect(editor.currentPageShapes).toHaveLength(0)
})
it('can be undone', () => {
editor.setCurrentTool('frame')
editor.pointerDown(100, 100).pointerMove(200, 200).pointerUp(200, 200)
expect(editor.onlySelectedShape?.type).toBe('frame')
expect(editor.getOnlySelectedShape()?.type).toBe('frame')
expect(editor.currentPageShapes).toHaveLength(1)
editor.undo()
expect(editor.onlySelectedShape?.type).toBe(undefined)
expect(editor.getOnlySelectedShape()?.type).toBe(undefined)
expect(editor.currentPageShapes).toHaveLength(0)
})
it('can be done inside other frames', () => {
editor.setCurrentTool('frame')
editor.pointerDown(100, 100).pointerMove(200, 200).pointerUp(200, 200)
const frameAId = editor.onlySelectedShape!.id
const frameAId = editor.getOnlySelectedShape()!.id
editor.setCurrentTool('frame')
editor.pointerDown(125, 125).pointerMove(175, 175).pointerUp(175, 175)
expect(editor.currentPageShapes).toHaveLength(2)
expect(editor.onlySelectedShape?.parentId).toEqual(frameAId)
expect(editor.getOnlySelectedShape()?.parentId).toEqual(frameAId)
expect(editor.getShapePageBounds(editor.onlySelectedShape!)).toMatchObject({
expect(editor.getShapePageBounds(editor.getOnlySelectedShape()!)).toMatchObject({
x: 125,
y: 125,
w: 50,
@ -91,7 +91,7 @@ describe('creating frames', () => {
editor.setCurrentTool('frame')
editor.pointerDown(100, 100).pointerMove(200, 200).pointerUp(200, 200)
const frameAId = editor.onlySelectedShape!.id
const frameAId = editor.getOnlySelectedShape()!.id
editor.rotateSelection(Math.PI / 2)
@ -100,9 +100,9 @@ describe('creating frames', () => {
expect(editor.currentPageShapes).toHaveLength(2)
expect(editor.onlySelectedShape?.parentId).toEqual(frameAId)
expect(editor.getOnlySelectedShape()?.parentId).toEqual(frameAId)
expect(editor.getShapePageBounds(editor.onlySelectedShape!)).toCloselyMatchObject({
expect(editor.getShapePageBounds(editor.getOnlySelectedShape()!)).toCloselyMatchObject({
x: 125,
y: 125,
w: 50,
@ -118,7 +118,7 @@ describe('creating frames', () => {
editor.setCurrentTool('frame')
editor.pointerDown(100, 100).pointerMove(49, 149)
expect(editor.getShapePageBounds(editor.onlySelectedShape!)).toMatchObject({
expect(editor.getShapePageBounds(editor.getOnlySelectedShape()!)).toMatchObject({
x: 49,
y: 100,
w: 51,
@ -128,7 +128,7 @@ describe('creating frames', () => {
// x should snap
editor.keyDown('Control')
expect(editor.snaps.lines).toHaveLength(1)
expect(editor.getShapePageBounds(editor.onlySelectedShape!)).toMatchObject({
expect(editor.getShapePageBounds(editor.getOnlySelectedShape()!)).toMatchObject({
x: 50,
y: 100,
w: 50,
@ -184,7 +184,7 @@ describe('frame shapes', () => {
editor.pointerDown(100, 100).pointerMove(200, 200).pointerUp(200, 200)
editor.resizeSelection({ scaleX: 0.5, scaleY: 0.5 }, 'bottom_right')
expect(editor.getShapePageBounds(editor.onlySelectedShape!)).toCloselyMatchObject({
expect(editor.getShapePageBounds(editor.getOnlySelectedShape()!)).toCloselyMatchObject({
x: 100,
y: 100,
w: 50,
@ -198,7 +198,7 @@ describe('frame shapes', () => {
editor.setCurrentTool('frame')
editor.pointerDown(100, 100).pointerMove(200, 200).pointerUp(200, 200)
editor.resizeSelection({ scaleX: 0.5, scaleY: 0.5 }, 'bottom_right', { altKey: true })
expect(editor.getShapePageBounds(editor.onlySelectedShape!)).toCloselyMatchObject({
expect(editor.getShapePageBounds(editor.getOnlySelectedShape()!)).toCloselyMatchObject({
x: 125,
y: 125,
w: 50,
@ -210,12 +210,12 @@ describe('frame shapes', () => {
editor.setCurrentTool('frame')
editor.pointerDown(100, 100).pointerMove(200, 200).pointerUp(200, 200)
const frameId = editor.onlySelectedShape!.id
const frameId = editor.getOnlySelectedShape()!.id
editor.setCurrentTool('geo')
editor.pointerDown(125, 125).pointerMove(175, 175).pointerUp(175, 175)
const boxId = editor.onlySelectedShape!.id
const boxId = editor.getOnlySelectedShape()!.id
editor.select(frameId)
@ -239,42 +239,42 @@ describe('frame shapes', () => {
editor.setCurrentTool('frame')
editor.pointerDown(100, 100).pointerMove(200, 200).pointerUp(200, 200)
const frameId = editor.onlySelectedShape!.id
const frameId = editor.getOnlySelectedShape()!.id
editor.createShapes([
{ type: 'geo', id: ids.boxA, x: 250, y: 250, props: { w: 50, h: 50, fill: 'solid' } },
])
expect(editor.onlySelectedShape!.parentId).toBe(editor.currentPageId)
expect(editor.getOnlySelectedShape()!.parentId).toBe(editor.currentPageId)
editor.setCurrentTool('select')
editor.pointerDown(275, 275).pointerMove(150, 150)
jest.advanceTimersByTime(300)
expect(editor.onlySelectedShape!.id).toBe(ids.boxA)
expect(editor.onlySelectedShape!.parentId).toBe(frameId)
expect(editor.getOnlySelectedShape()!.id).toBe(ids.boxA)
expect(editor.getOnlySelectedShape()!.parentId).toBe(frameId)
editor.pointerMove(275, 275)
jest.advanceTimersByTime(250)
expect(editor.onlySelectedShape!.parentId).toBe(editor.currentPageId)
expect(editor.getOnlySelectedShape()!.parentId).toBe(editor.currentPageId)
editor.pointerMove(150, 150)
jest.advanceTimersByTime(250)
expect(editor.onlySelectedShape!.parentId).toBe(frameId)
expect(editor.getOnlySelectedShape()!.parentId).toBe(frameId)
editor.pointerUp(150, 150)
expect(editor.onlySelectedShape!.parentId).toBe(frameId)
expect(editor.getOnlySelectedShape()!.parentId).toBe(frameId)
})
it('can have shapes dragged on top and dropped before the timeout fires', () => {
editor.setCurrentTool('frame')
editor.pointerDown(100, 100).pointerMove(200, 200).pointerUp(200, 200)
const frameId = editor.onlySelectedShape!.id
const frameId = editor.getOnlySelectedShape()!.id
// Create a new shape off of the frame
editor.createShapes([
@ -282,37 +282,37 @@ describe('frame shapes', () => {
])
// It should be a child of the page
expect(editor.onlySelectedShape!.parentId).toBe(editor.currentPageId)
expect(editor.getOnlySelectedShape()!.parentId).toBe(editor.currentPageId)
// Drag the shape on top of the frame
editor.setCurrentTool('select')
editor.pointerDown(275, 275, ids.boxA).pointerMove(150, 150)
// The timeout has not fired yet, so the shape is still a child of the current page
expect(editor.onlySelectedShape!.parentId).toBe(editor.currentPageId)
expect(editor.getOnlySelectedShape()!.parentId).toBe(editor.currentPageId)
// On pointer up, the shape should be dropped into the frame
editor.pointerUp()
expect(editor.onlySelectedShape!.parentId).toBe(frameId)
expect(editor.getOnlySelectedShape()!.parentId).toBe(frameId)
})
it('does not reparent shapes that are being dragged from within the frame', () => {
editor.setCurrentTool('frame')
editor.pointerDown(100, 100).pointerMove(200, 200).pointerUp(200, 200)
const frameId = editor.onlySelectedShape!.id
const frameId = editor.getOnlySelectedShape()!.id
// create a box within the frame
editor.setCurrentTool('geo')
editor.pointerDown(125, 125).pointerMove(175, 175).pointerUp(175, 175)
expect(editor.onlySelectedShape!.parentId).toBe(frameId)
const boxAid = editor.onlySelectedShape!.id
expect(editor.getOnlySelectedShape()!.parentId).toBe(frameId)
const boxAid = editor.getOnlySelectedShape()!.id
// create another box within the frame
editor.setCurrentTool('geo')
editor.pointerDown(130, 130).pointerMove(180, 180).pointerUp(180, 180)
expect(editor.onlySelectedShape!.parentId).toBe(frameId)
const boxBid = editor.onlySelectedShape!.id
expect(editor.getOnlySelectedShape()!.parentId).toBe(frameId)
const boxBid = editor.getOnlySelectedShape()!.id
// dragging box A around should not cause the index to change or the frame to be highlighted
@ -325,8 +325,8 @@ describe('frame shapes', () => {
jest.advanceTimersByTime(2500)
expect(editor.onlySelectedShape!.id).toBe(boxAid)
expect(editor.onlySelectedShape!.parentId).toBe(frameId)
expect(editor.getOnlySelectedShape()!.id).toBe(boxAid)
expect(editor.getOnlySelectedShape()!.parentId).toBe(frameId)
expect(editor.hintingShapeIds).toHaveLength(0)
// box A should still be beneath box B
expect(editor.getShape(boxAid)!.index.localeCompare(editor.getShape(boxBid)!.index)).toBe(-1)
@ -355,12 +355,12 @@ describe('frame shapes', () => {
editor.setCurrentTool('geo')
editor.pointerDown(125, 125).pointerMove(175, 175).pointerUp(175, 175)
const innerBoxId = editor.onlySelectedShape!.id
const innerBoxId = editor.getOnlySelectedShape()!.id
// make a shape outside the frame
editor.setCurrentTool('geo')
editor.pointerDown(275, 125).pointerMove(280, 130).pointerUp(280, 130)
expect(editor.getShapePageBounds(editor.onlySelectedShape!)).toMatchObject({
expect(editor.getShapePageBounds(editor.getOnlySelectedShape()!)).toMatchObject({
x: 275,
y: 125,
w: 5,
@ -370,13 +370,13 @@ describe('frame shapes', () => {
// drag it a pixel up, it should not snap even though it's at the same y as the box inside the frame
editor.setCurrentTool('select')
editor
.pointerDown(277.5, 127.5, editor.onlySelectedShape!.id)
.pointerDown(277.5, 127.5, editor.getOnlySelectedShape()!.id)
.pointerMove(287.5, 126.5)
.pointerMove(277.5, 126.5)
// now try to snap
editor.keyDown('Control')
expect(editor.getShapePageBounds(editor.onlySelectedShape!)).toMatchObject({
expect(editor.getShapePageBounds(editor.getOnlySelectedShape()!)).toMatchObject({
x: 275,
y: 124,
w: 5,
@ -388,7 +388,7 @@ describe('frame shapes', () => {
editor.pointerMove(287.5, 126.5).pointerMove(277.5, 126.5)
expect(editor.snaps.lines).toHaveLength(1)
expect(editor.getShapePageBounds(editor.onlySelectedShape!)).toMatchObject({
expect(editor.getShapePageBounds(editor.getOnlySelectedShape()!)).toMatchObject({
x: 275,
y: 125,
w: 5,
@ -399,17 +399,17 @@ describe('frame shapes', () => {
it('children of a frame will not snap to shapes outside the frame', () => {
editor.setCurrentTool('frame')
editor.pointerDown(100, 100).pointerMove(200, 200).pointerUp(200, 200)
const frameId = editor.onlySelectedShape!.id
const frameId = editor.getOnlySelectedShape()!.id
// make a shape inside the frame
editor.setCurrentTool('geo')
editor.pointerDown(125, 125).pointerMove(175, 175).pointerUp(175, 175)
const innerBoxId = editor.onlySelectedShape!.id
const innerBoxId = editor.getOnlySelectedShape()!.id
// make a shape outside the frame
editor.setCurrentTool('geo')
editor.pointerDown(275, 125).pointerMove(280, 130).pointerUp(280, 130)
const outerBoxId = editor.onlySelectedShape!.id
const outerBoxId = editor.getOnlySelectedShape()!.id
editor.setCurrentTool('select')
editor.pointerDown(150, 150, innerBoxId).pointerMove(150, 50).pointerMove(150, 148)
@ -429,7 +429,7 @@ describe('frame shapes', () => {
editor.setCurrentTool('geo')
editor.pointerDown(150, 150).pointerMove(250, 250).pointerUp(250, 250)
expect(editor.getShapePageBounds(editor.onlySelectedShape!)).toMatchObject({
expect(editor.getShapePageBounds(editor.getOnlySelectedShape()!)).toMatchObject({
x: 150,
y: 150,
w: 100,
@ -437,13 +437,13 @@ describe('frame shapes', () => {
})
// mask should be a 50px box around the top left corner
expect(editor.getShapeClipPath(editor.onlySelectedShape!.id)).toMatchInlineSnapshot(
expect(editor.getShapeClipPath(editor.getOnlySelectedShape()!.id)).toMatchInlineSnapshot(
`"polygon(-50px -50px,50px -50px,50px 50px,-50px 50px)"`
)
editor.reparentShapes([editor.onlySelectedShape!.id], editor.currentPageId)
editor.reparentShapes([editor.getOnlySelectedShape()!.id], editor.currentPageId)
expect(editor.getShapeClipPath(editor.onlySelectedShape!.id)).toBeUndefined()
expect(editor.getShapeClipPath(editor.getOnlySelectedShape()!.id)).toBeUndefined()
})
it('masks its nested children', () => {
@ -453,12 +453,12 @@ describe('frame shapes', () => {
editor.setCurrentTool('frame')
editor.pointerDown(150, 150).pointerMove(250, 250).pointerUp(250, 250)
const innerFrameId = editor.onlySelectedShape!.id
const innerFrameId = editor.getOnlySelectedShape()!.id
editor.setCurrentTool('geo')
editor.pointerDown(100, 100).pointerMove(250, 250).pointerUp(250, 250)
const boxId = editor.onlySelectedShape!.id
const boxId = editor.getOnlySelectedShape()!.id
editor.reparentShapes([boxId], innerFrameId)
@ -471,12 +471,12 @@ describe('frame shapes', () => {
it('arrows started within the frame will bind to it and have the page as their parent', () => {
editor.setCurrentTool('frame')
editor.pointerDown(100, 100).pointerMove(200, 200).pointerUp(200, 200)
const frameId = editor.onlySelectedShape!.id
const frameId = editor.getOnlySelectedShape()!.id
editor.setCurrentTool('arrow')
editor.pointerDown(150, 150).pointerMove(250, 250).pointerUp(250, 250)
const arrow = editor.onlySelectedShape! as TLArrowShape
const arrow = editor.getOnlySelectedShape()! as TLArrowShape
expect(arrow.props.start).toMatchObject({ boundShapeId: frameId })
expect(arrow.props.end).toMatchObject({ type: 'point' })
@ -487,7 +487,7 @@ describe('frame shapes', () => {
it('arrows started within the frame can bind to a shape within the frame ', () => {
editor.setCurrentTool('frame')
editor.pointerDown(100, 100).pointerMove(200, 200).pointerUp(200, 200)
const frameId = editor.onlySelectedShape!.id
const frameId = editor.getOnlySelectedShape()!.id
editor.setCurrentTool('geo')
editor
@ -496,12 +496,12 @@ describe('frame shapes', () => {
.pointerUp(175, 175)
.setStyleForSelectedShapes(DefaultFillStyle, 'solid')
.setStyleForNextShapes(DefaultFillStyle, 'solid')
const boxId = editor.onlySelectedShape!.id
const boxId = editor.getOnlySelectedShape()!.id
editor.setCurrentTool('arrow')
editor.pointerDown(150, 150).pointerMove(190, 190).pointerUp(190, 190)
const arrow = editor.onlySelectedShape! as TLArrowShape
const arrow = editor.getOnlySelectedShape()! as TLArrowShape
expect(arrow.props.start).toMatchObject({ boundShapeId: boxId })
expect(arrow.props.end).toMatchObject({ boundShapeId: frameId })
@ -513,7 +513,7 @@ describe('frame shapes', () => {
editor.setCurrentTool('frame')
editor.pointerDown(100, 100).pointerMove(200, 200).pointerUp(200, 200)
const frameId = editor.onlySelectedShape!.id
const frameId = editor.getOnlySelectedShape()!.id
expect(editor.getSelectedShapeIds()[0]).toBe(frameId)
expect(editor.getCurrentPageState().editingShapeId).toBe(null)
@ -529,7 +529,7 @@ describe('frame shapes', () => {
it('can be selected with box brushing only if the whole frame is selected', () => {
editor.setCurrentTool('frame')
editor.pointerDown(100, 100).pointerMove(200, 200).pointerUp(200, 200)
const frameId = editor.onlySelectedShape!.id
const frameId = editor.getOnlySelectedShape()!.id
// select from outside the frame
editor.setCurrentTool('select')
@ -541,7 +541,7 @@ describe('frame shapes', () => {
editor.pointerMove(250, 250)
expect(editor.getSelectedShapeIds()).toHaveLength(1)
expect(editor.onlySelectedShape!.id).toBe(frameId)
expect(editor.getOnlySelectedShape()!.id).toBe(frameId)
})
it('can be selected with scribble brushing only if the drag starts outside the frame', () => {
@ -561,12 +561,12 @@ describe('frame shapes', () => {
it('children of a frame will not be selected from outside of the frame', () => {
editor.setCurrentTool('frame')
editor.pointerDown(100, 100).pointerMove(200, 200).pointerUp(200, 200)
editor.onlySelectedShape!.id
editor.getOnlySelectedShape()!.id
// make a shape inside the frame that extends out of the frame
editor.setCurrentTool('geo')
editor.pointerDown(150, 150).pointerMove(400, 400).pointerUp(400, 400)
const innerBoxId = editor.onlySelectedShape!.id
const innerBoxId = editor.getOnlySelectedShape()!.id
// select from outside the frame via box brushing
editor.setCurrentTool('select')
@ -581,7 +581,7 @@ describe('frame shapes', () => {
// Check if the inner box was selected
expect(editor.getSelectedShapeIds()).toHaveLength(1)
expect(editor.onlySelectedShape!.id).toBe(innerBoxId)
expect(editor.getOnlySelectedShape()!.id).toBe(innerBoxId)
// Deselect everything
editor.deselect()
@ -600,7 +600,7 @@ describe('frame shapes', () => {
it('arrows will not bind to parts of shapes outside the frame', () => {
editor.setCurrentTool('frame')
editor.pointerDown(100, 100).pointerMove(200, 200).pointerUp(200, 200)
editor.onlySelectedShape!.id
editor.getOnlySelectedShape()!.id
// make a shape inside the frame that extends out of the frame
editor.setCurrentTool('geo')
@ -610,14 +610,14 @@ describe('frame shapes', () => {
.pointerUp(400, 400)
.setStyleForSelectedShapes(DefaultFillStyle, 'solid')
.setStyleForNextShapes(DefaultFillStyle, 'solid')
const innerBoxId = editor.onlySelectedShape!.id
const innerBoxId = editor.getOnlySelectedShape()!.id
// Make an arrow that binds to the inner box's bottom right corner
editor.setCurrentTool('arrow')
editor.pointerDown(500, 500).pointerMove(375, 375)
// Check if the arrow's handles remain points
let arrow = editor.onlySelectedShape! as TLArrowShape
let arrow = editor.getOnlySelectedShape()! as TLArrowShape
expect(arrow.props.start).toMatchObject({
type: 'point',
x: 0,
@ -633,7 +633,7 @@ describe('frame shapes', () => {
editor.pointerMove(175, 175).pointerUp(175, 175)
// Check if arrow's end handle is bound to the inner box
arrow = editor.onlySelectedShape! as TLArrowShape
arrow = editor.getOnlySelectedShape()! as TLArrowShape
expect(arrow.props.end).toMatchObject({ boundShapeId: innerBoxId })
})
})
@ -655,7 +655,7 @@ test('arrows bound to a shape within a group within a frame are reparented if th
// └──────────────────────────┘
editor.setCurrentTool('frame')
editor.pointerDown(100, 100).pointerMove(200, 200).pointerUp(200, 200)
const frameId = editor.onlySelectedShape!.id
const frameId = editor.getOnlySelectedShape()!.id
editor.setCurrentTool('geo')
editor
@ -664,7 +664,7 @@ test('arrows bound to a shape within a group within a frame are reparented if th
.pointerUp(120, 120)
.setStyleForSelectedShapes(DefaultFillStyle, 'solid')
.setStyleForNextShapes(DefaultFillStyle, 'solid')
const boxAId = editor.onlySelectedShape!.id
const boxAId = editor.getOnlySelectedShape()!.id
editor.setCurrentTool('geo')
editor
@ -673,7 +673,7 @@ test('arrows bound to a shape within a group within a frame are reparented if th
.pointerUp(190, 120)
.setStyleForSelectedShapes(DefaultFillStyle, 'solid')
.setStyleForNextShapes(DefaultFillStyle, 'solid')
const boxBId = editor.onlySelectedShape!.id
const boxBId = editor.getOnlySelectedShape()!.id
editor.setCurrentTool('geo')
editor
@ -682,16 +682,16 @@ test('arrows bound to a shape within a group within a frame are reparented if th
.pointerUp(170, 170)
.setStyleForSelectedShapes(DefaultFillStyle, 'solid')
.setStyleForNextShapes(DefaultFillStyle, 'solid')
const boxCId = editor.onlySelectedShape!.id
const boxCId = editor.getOnlySelectedShape()!.id
editor.setCurrentTool('select')
editor.select(boxBId, boxCId)
editor.groupShapes(editor.getSelectedShapeIds())
const groupId = editor.onlySelectedShape!.id
const groupId = editor.getOnlySelectedShape()!.id
editor.setCurrentTool('arrow')
editor.pointerDown(115, 115).pointerMove(185, 115).pointerUp(185, 115)
const arrowId = editor.onlySelectedShape!.id
const arrowId = editor.getOnlySelectedShape()!.id
expect(editor.getArrowsBoundTo(boxAId)).toHaveLength(1)
expect(editor.getArrowsBoundTo(boxBId)).toHaveLength(1)
@ -740,7 +740,7 @@ describe('When dragging a shape inside a group inside a frame', () => {
editor.pointerMove(100, 100).click().click()
expect(editor.onlySelectedShape?.id).toBe(ids.box1)
expect(editor.getOnlySelectedShape()?.id).toBe(ids.box1)
editor.pointerMove(150, 150).pointerDown().pointerMove(140, 140)
@ -760,7 +760,7 @@ describe('When dragging a shape inside a group inside a frame', () => {
editor.pointerMove(100, 100).click().click()
expect(editor.onlySelectedShape?.id).toBe(ids.box1)
expect(editor.getOnlySelectedShape()?.id).toBe(ids.box1)
expect(editor.focusedGroupId).toBe(ids.group1)
editor

View file

@ -475,9 +475,9 @@ describe('When pasting into frames...', () => {
editor.setCamera({ x: -editor.viewportScreenBounds.w, y: -editor.viewportScreenBounds.h, z: 1 })
// paste the box
editor.paste()
const boxId = editor.onlySelectedShape!.id
const boxId = editor.getOnlySelectedShape()!.id
// it should be a child of the frame
expect(editor.onlySelectedShape?.parentId).toBe(ids.frame1)
expect(editor.getOnlySelectedShape()?.parentId).toBe(ids.frame1)
// it should have pageBounds of 10x10 because it is not rotated relative to the viewport
expect(editor.getShapePageBounds(boxId)).toMatchObject({ w: 10, h: 10 })
// it should be in the middle of the frame

View file

@ -15,7 +15,7 @@ describe(SelectTool, () => {
describe('pointer down while shape is being edited', () => {
it('captures the pointer down event if it is on the shape', () => {
editor.setCurrentTool('geo').pointerDown(0, 0).pointerMove(100, 100).pointerUp(100, 100)
const shapeId = editor.onlySelectedShape?.id
const shapeId = editor.getOnlySelectedShape()?.id
editor._transformPointerDownSpy.mockRestore()
editor._transformPointerUpSpy.mockRestore()
editor.setCurrentTool('select')
@ -42,7 +42,7 @@ describe(SelectTool, () => {
})
it('does not allow pressing undo to end up in the editing state', () => {
editor.setCurrentTool('geo').pointerDown(0, 0).pointerMove(100, 100).pointerUp(100, 100)
const shapeId = editor.onlySelectedShape?.id
const shapeId = editor.getOnlySelectedShape()?.id
editor._transformPointerDownSpy.mockRestore()
editor._transformPointerUpSpy.mockRestore()
editor.setCurrentTool('select')

View file

@ -75,9 +75,9 @@ describe('Hovering shapes', () => {
editor.pointerMove(50, 50)
editor.pointerDown()
expect(editor.isIn('select.pointing_shape')).toBe(true)
expect(editor.selectedShapes.length).toBe(1)
expect(editor.getSelectedShapes().length).toBe(1)
editor.pointerUp()
expect(editor.selectedShapes.length).toBe(1)
expect(editor.getSelectedShapes().length).toBe(1)
expect(editor.isIn('select.idle')).toBe(true)
})
@ -85,10 +85,10 @@ describe('Hovering shapes', () => {
editor.pointerMove(50, 50)
editor.pointerDown()
expect(editor.isIn('select.pointing_canvas')).toBe(true)
expect(editor.selectedShapes.length).toBe(0)
expect(editor.getSelectedShapes().length).toBe(0)
editor.pointerUp()
expect(editor.isIn('select.idle')).toBe(true)
expect(editor.selectedShapes.length).toBe(1)
expect(editor.getSelectedShapes().length).toBe(1)
})
it('hovers the margins or inside of filled shapes', () => {

View file

@ -182,7 +182,7 @@ describe('When cloning...', () => {
// Start cloning!
editor.keyDown('Alt')
expect(editor.currentPageShapeIds.size).toBe(4)
const newShape = editor.selectedShapes[0]
const newShape = editor.getSelectedShapes()[0]
expect(newShape.id).not.toBe(ids.box1)
editor
@ -1792,19 +1792,19 @@ it('clones a single shape simply', () => {
.pointerMove(50, 50)
.click()
expect(editor.onlySelectedShape).toBe(editor.currentPageShapes[0])
expect(editor.getOnlySelectedShape()).toBe(editor.currentPageShapes[0])
expect(editor.hoveredShape).toBe(editor.currentPageShapes[0])
// click on the canvas to deselect
editor.pointerMove(200, 50).click()
expect(editor.onlySelectedShape).toBe(null)
expect(editor.getOnlySelectedShape()).toBe(null)
expect(editor.hoveredShape).toBe(undefined)
// move back over the the shape
editor.pointerMove(50, 50)
expect(editor.onlySelectedShape).toBe(null)
expect(editor.getOnlySelectedShape()).toBe(null)
expect(editor.hoveredShape).toBe(editor.currentPageShapes[0])
// start dragging the shape
@ -1818,7 +1818,7 @@ it('clones a single shape simply', () => {
expect(editor.currentPageShapes).toHaveLength(2)
const [, sticky2] = editor.currentPageShapes
expect(editor.onlySelectedShape).toBe(sticky2)
expect(editor.getOnlySelectedShape()).toBe(sticky2)
expect(editor.editingShape).toBe(undefined)
expect(editor.hoveredShape).toBe(sticky2)
})