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:
parent
2ca2f81f2a
commit
7ffda2335c
52 changed files with 309 additions and 209 deletions
|
@ -24,7 +24,7 @@ test.describe.skip('clipboard tests', () => {
|
||||||
await page.mouse.up()
|
await page.mouse.up()
|
||||||
|
|
||||||
expect(await page.evaluate(() => editor.currentPageShapes.length)).toBe(1)
|
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.down('Control')
|
||||||
await page.keyboard.press('KeyC')
|
await page.keyboard.press('KeyC')
|
||||||
|
@ -33,7 +33,7 @@ test.describe.skip('clipboard tests', () => {
|
||||||
await page.keyboard.up('Control')
|
await page.keyboard.up('Control')
|
||||||
|
|
||||||
expect(await page.evaluate(() => editor.currentPageShapes.length)).toBe(2)
|
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 }) => {
|
test('copy and paste from main menu', async ({ page }) => {
|
||||||
|
@ -43,7 +43,7 @@ test.describe.skip('clipboard tests', () => {
|
||||||
await page.mouse.up()
|
await page.mouse.up()
|
||||||
|
|
||||||
expect(await page.evaluate(() => editor.currentPageShapes.length)).toBe(1)
|
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('main.menu').click()
|
||||||
await page.getByTestId('menu-item.edit').click()
|
await page.getByTestId('menu-item.edit').click()
|
||||||
|
@ -54,7 +54,7 @@ test.describe.skip('clipboard tests', () => {
|
||||||
await page.getByTestId('menu-item.paste').click()
|
await page.getByTestId('menu-item.paste').click()
|
||||||
|
|
||||||
expect(await page.evaluate(() => editor.currentPageShapes.length)).toBe(2)
|
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 }) => {
|
test('copy and paste from context menu', async ({ page }) => {
|
||||||
|
@ -64,7 +64,7 @@ test.describe.skip('clipboard tests', () => {
|
||||||
await page.mouse.up()
|
await page.mouse.up()
|
||||||
|
|
||||||
expect(await page.evaluate(() => editor.currentPageShapes.length)).toBe(1)
|
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.mouse.click(100, 100, { button: 'right' })
|
||||||
await page.getByTestId('menu-item.copy').click()
|
await page.getByTestId('menu-item.copy').click()
|
||||||
|
@ -74,6 +74,6 @@ test.describe.skip('clipboard tests', () => {
|
||||||
await page.getByTestId('menu-item.paste').click()
|
await page.getByTestId('menu-item.paste').click()
|
||||||
|
|
||||||
expect(await page.evaluate(() => editor.currentPageShapes.length)).toBe(2)
|
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)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -65,7 +65,7 @@ test.describe('smoke tests', () => {
|
||||||
expect(await getAllShapeTypes(page)).toEqual(['geo'])
|
expect(await getAllShapeTypes(page)).toEqual(['geo'])
|
||||||
|
|
||||||
const getSelectedShapeColor = async () =>
|
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
|
// change style
|
||||||
expect(await getSelectedShapeColor()).toBe('black')
|
expect(await getSelectedShapeColor()).toBe('black')
|
||||||
|
|
|
@ -24,7 +24,7 @@ type ShapeWithMyMeta = TLShape & { meta: { label: string } }
|
||||||
|
|
||||||
export const ShapeLabelUiWithHelper = track(function ShapeLabelUiWithHelper() {
|
export const ShapeLabelUiWithHelper = track(function ShapeLabelUiWithHelper() {
|
||||||
const editor = useEditor()
|
const editor = useEditor()
|
||||||
const onlySelectedShape = editor.onlySelectedShape as ShapeWithMyMeta | null
|
const onlySelectedShape = editor.getOnlySelectedShape() as ShapeWithMyMeta | null
|
||||||
|
|
||||||
if (!onlySelectedShape) {
|
if (!onlySelectedShape) {
|
||||||
return null
|
return null
|
||||||
|
|
|
@ -103,7 +103,7 @@ class PointingState extends StateNode {
|
||||||
|
|
||||||
override onPointerMove: TLEventHandlers['onPointerUp'] = () => {
|
override onPointerMove: TLEventHandlers['onPointerUp'] = () => {
|
||||||
if (this.editor.inputs.isDragging) {
|
if (this.editor.inputs.isDragging) {
|
||||||
this.parent.transition('dragging', { shapes: [...this.editor.selectedShapes] })
|
this.parent.transition('dragging', { shapes: [...this.editor.getSelectedShapes()] })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -689,6 +689,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
||||||
getInitialMetaForShape(_shape: TLShape): JsonObject;
|
getInitialMetaForShape(_shape: TLShape): JsonObject;
|
||||||
getInstanceState(): TLInstance;
|
getInstanceState(): TLInstance;
|
||||||
getIsMenuOpen(): boolean;
|
getIsMenuOpen(): boolean;
|
||||||
|
getOnlySelectedShape(): null | TLShape;
|
||||||
getOpenMenus(): string[];
|
getOpenMenus(): string[];
|
||||||
getOutermostSelectableShape(shape: TLShape | TLShapeId, filter?: (shape: TLShape) => boolean): TLShape;
|
getOutermostSelectableShape(shape: TLShape | TLShapeId, filter?: (shape: TLShape) => boolean): TLShape;
|
||||||
getPage(page: TLPage | TLPageId): TLPage | undefined;
|
getPage(page: TLPage | TLPageId): TLPage | undefined;
|
||||||
|
@ -698,6 +699,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
||||||
getPointInShapeSpace(shape: TLShape | TLShapeId, point: VecLike): Vec2d;
|
getPointInShapeSpace(shape: TLShape | TLShapeId, point: VecLike): Vec2d;
|
||||||
getSelectedShapeAtPoint(point: VecLike): TLShape | undefined;
|
getSelectedShapeAtPoint(point: VecLike): TLShape | undefined;
|
||||||
getSelectedShapeIds(): TLShapeId[];
|
getSelectedShapeIds(): TLShapeId[];
|
||||||
|
getSelectedShapes(): TLShape[];
|
||||||
getShape<T extends TLShape = TLShape>(shape: TLParentId | TLShape): T | undefined;
|
getShape<T extends TLShape = TLShape>(shape: TLParentId | TLShape): T | undefined;
|
||||||
getShapeAncestors(shape: TLShape | TLShapeId, acc?: TLShape[]): TLShape[];
|
getShapeAncestors(shape: TLShape | TLShapeId, acc?: TLShape[]): TLShape[];
|
||||||
getShapeAndDescendantIds(ids: TLShapeId[]): Set<TLShapeId>;
|
getShapeAndDescendantIds(ids: TLShapeId[]): Set<TLShapeId>;
|
||||||
|
@ -791,6 +793,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
||||||
mark(markId?: string, onUndo?: boolean, onRedo?: boolean): this;
|
mark(markId?: string, onUndo?: boolean, onRedo?: boolean): this;
|
||||||
moveShapesToPage(shapes: TLShape[] | TLShapeId[], pageId: TLPageId): this;
|
moveShapesToPage(shapes: TLShape[] | TLShapeId[], pageId: TLPageId): this;
|
||||||
nudgeShapes(shapes: TLShape[] | TLShapeId[], offset: VecLike, historyOptions?: TLCommandHistoryOptions): this;
|
nudgeShapes(shapes: TLShape[] | TLShapeId[], offset: VecLike, historyOptions?: TLCommandHistoryOptions): this;
|
||||||
|
// @deprecated (undocumented)
|
||||||
get onlySelectedShape(): null | TLShape;
|
get onlySelectedShape(): null | TLShape;
|
||||||
// @deprecated (undocumented)
|
// @deprecated (undocumented)
|
||||||
get openMenus(): string[];
|
get openMenus(): string[];
|
||||||
|
@ -849,6 +852,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
||||||
selectAll(): this;
|
selectAll(): this;
|
||||||
// @deprecated (undocumented)
|
// @deprecated (undocumented)
|
||||||
get selectedShapeIds(): TLShapeId[];
|
get selectedShapeIds(): TLShapeId[];
|
||||||
|
// @deprecated (undocumented)
|
||||||
get selectedShapes(): TLShape[];
|
get selectedShapes(): TLShape[];
|
||||||
get selectionPageBounds(): Box2d | null;
|
get selectionPageBounds(): Box2d | null;
|
||||||
get selectionRotatedPageBounds(): Box2d | undefined;
|
get selectionRotatedPageBounds(): Box2d | undefined;
|
||||||
|
|
|
@ -9068,7 +9068,7 @@
|
||||||
{
|
{
|
||||||
"kind": "Method",
|
"kind": "Method",
|
||||||
"canonicalReference": "@tldraw/editor!Editor#duplicateShapes:member(1)",
|
"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": [
|
"excerptTokens": [
|
||||||
{
|
{
|
||||||
"kind": "Content",
|
"kind": "Content",
|
||||||
|
@ -10517,6 +10517,42 @@
|
||||||
"isAbstract": false,
|
"isAbstract": false,
|
||||||
"name": "getIsMenuOpen"
|
"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",
|
"kind": "Method",
|
||||||
"canonicalReference": "@tldraw/editor!Editor#getOpenMenus:member(1)",
|
"canonicalReference": "@tldraw/editor!Editor#getOpenMenus:member(1)",
|
||||||
|
@ -11045,6 +11081,42 @@
|
||||||
"isAbstract": false,
|
"isAbstract": false,
|
||||||
"name": "getSelectedShapeIds"
|
"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",
|
"kind": "Method",
|
||||||
"canonicalReference": "@tldraw/editor!Editor#getShape:member(1)",
|
"canonicalReference": "@tldraw/editor!Editor#getShape:member(1)",
|
||||||
|
@ -14029,7 +14101,7 @@
|
||||||
{
|
{
|
||||||
"kind": "Method",
|
"kind": "Method",
|
||||||
"canonicalReference": "@tldraw/editor!Editor#nudgeShapes:member(1)",
|
"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": [
|
"excerptTokens": [
|
||||||
{
|
{
|
||||||
"kind": "Content",
|
"kind": "Content",
|
||||||
|
@ -14125,7 +14197,7 @@
|
||||||
{
|
{
|
||||||
"kind": "Property",
|
"kind": "Property",
|
||||||
"canonicalReference": "@tldraw/editor!Editor#onlySelectedShape:member",
|
"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": [
|
"excerptTokens": [
|
||||||
{
|
{
|
||||||
"kind": "Content",
|
"kind": "Content",
|
||||||
|
@ -15775,7 +15847,7 @@
|
||||||
{
|
{
|
||||||
"kind": "Property",
|
"kind": "Property",
|
||||||
"canonicalReference": "@tldraw/editor!Editor#selectedShapes:member",
|
"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": [
|
"excerptTokens": [
|
||||||
{
|
{
|
||||||
"kind": "Content",
|
"kind": "Content",
|
||||||
|
|
|
@ -214,7 +214,9 @@ function HandlesWrapper() {
|
||||||
const isCoarse = useValue('coarse pointer', () => editor.getInstanceState().isCoarsePointer, [
|
const isCoarse = useValue('coarse pointer', () => editor.getInstanceState().isCoarsePointer, [
|
||||||
editor,
|
editor,
|
||||||
])
|
])
|
||||||
const onlySelectedShape = useValue('onlySelectedShape', () => editor.onlySelectedShape, [editor])
|
const onlySelectedShape = useValue('onlySelectedShape', () => editor.getOnlySelectedShape(), [
|
||||||
|
editor,
|
||||||
|
])
|
||||||
const isChangingStyle = useValue(
|
const isChangingStyle = useValue(
|
||||||
'isChangingStyle',
|
'isChangingStyle',
|
||||||
() => editor.getInstanceState().isChangingStyle,
|
() => editor.getInstanceState().isChangingStyle,
|
||||||
|
@ -225,13 +227,24 @@ function HandlesWrapper() {
|
||||||
])
|
])
|
||||||
const handles = useValue(
|
const handles = useValue(
|
||||||
'handles',
|
'handles',
|
||||||
() => (editor.onlySelectedShape ? editor.getShapeHandles(editor.onlySelectedShape) : undefined),
|
() => {
|
||||||
|
const onlySelectedShape = editor.getOnlySelectedShape()
|
||||||
|
if (onlySelectedShape) {
|
||||||
|
return editor.getShapeHandles(onlySelectedShape)
|
||||||
|
}
|
||||||
|
return undefined
|
||||||
|
},
|
||||||
[editor]
|
[editor]
|
||||||
)
|
)
|
||||||
const transform = useValue(
|
const transform = useValue(
|
||||||
'transform',
|
'transform',
|
||||||
() =>
|
() => {
|
||||||
editor.onlySelectedShape ? editor.getShapePageTransform(editor.onlySelectedShape) : undefined,
|
const onlySelectedShape = editor.getOnlySelectedShape()
|
||||||
|
if (onlySelectedShape) {
|
||||||
|
return editor.getShapePageTransform(onlySelectedShape)
|
||||||
|
}
|
||||||
|
return undefined
|
||||||
|
},
|
||||||
[editor]
|
[editor]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ export const DefaultSelectionForeground: TLSelectionForegroundComponent = ({
|
||||||
const editor = useEditor()
|
const editor = useEditor()
|
||||||
const rSvg = useRef<SVGSVGElement>(null)
|
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
|
// if all shapes have an expandBy for the selection outline, we can expand by the l
|
||||||
const expandOutlineBy = onlyShape
|
const expandOutlineBy = onlyShape
|
||||||
|
|
|
@ -1002,7 +1002,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
||||||
},
|
},
|
||||||
extras: {
|
extras: {
|
||||||
activeStateNode: this.root.path.get(),
|
activeStateNode: this.root.path.get(),
|
||||||
selectedShapes: this.selectedShapes,
|
selectedShapes: this.getSelectedShapes(),
|
||||||
editingShape: this.editingShapeId ? this.getShape(this.editingShapeId) : undefined,
|
editingShape: this.editingShapeId ? this.getShape(this.editingShapeId) : undefined,
|
||||||
inputs: this.inputs,
|
inputs: this.inputs,
|
||||||
},
|
},
|
||||||
|
@ -1481,19 +1481,21 @@ export class Editor extends EventEmitter<TLEventMap> {
|
||||||
/**
|
/**
|
||||||
* An array containing all of the currently selected shapes.
|
* An array containing all of the currently selected shapes.
|
||||||
*
|
*
|
||||||
* @example
|
|
||||||
* ```ts
|
|
||||||
* editor.selectedShapes
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* @public
|
* @public
|
||||||
* @readonly
|
* @readonly
|
||||||
*/
|
*/
|
||||||
@computed get selectedShapes(): TLShape[] {
|
@computed getSelectedShapes(): TLShape[] {
|
||||||
const { selectedShapeIds } = this.getCurrentPageState()
|
const { selectedShapeIds } = this.getCurrentPageState()
|
||||||
return compact(selectedShapeIds.map((id) => this.store.get(id)))
|
return compact(selectedShapeIds.map((id) => this.store.get(id)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use `getSelectedShapes` instead.
|
||||||
|
*/
|
||||||
|
get selectedShapes() {
|
||||||
|
return this.getSelectedShapes()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Select one or more shapes.
|
* Select one or more shapes.
|
||||||
*
|
*
|
||||||
|
@ -1652,22 +1654,24 @@ export class Editor extends EventEmitter<TLEventMap> {
|
||||||
/**
|
/**
|
||||||
* The app's only selected shape.
|
* 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
|
* @returns Null if there is no shape or more than one selected shape, otherwise the selected
|
||||||
* shape.
|
* shape.
|
||||||
*
|
*
|
||||||
* @public
|
* @public
|
||||||
* @readonly
|
* @readonly
|
||||||
*/
|
*/
|
||||||
@computed get onlySelectedShape(): TLShape | null {
|
@computed getOnlySelectedShape(): TLShape | null {
|
||||||
const { selectedShapes } = this
|
const selectedShapes = this.getSelectedShapes()
|
||||||
return selectedShapes.length === 1 ? selectedShapes[0] : null
|
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
|
* The current page bounds of all the selected shapes. If the
|
||||||
* selection is rotated, then these bounds are the axis-aligned
|
* selection is rotated, then these bounds are the axis-aligned
|
||||||
|
@ -5138,7 +5142,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
||||||
* @example
|
* @example
|
||||||
* ```ts
|
* ```ts
|
||||||
* editor.nudgeShapes(['box1', 'box2'], { x: 8, y: 8 })
|
* 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.
|
* @param shapes - The shapes (or shape ids) to move.
|
||||||
|
@ -5201,7 +5205,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
||||||
* @example
|
* @example
|
||||||
* ```ts
|
* ```ts
|
||||||
* editor.duplicateShapes(['box1', 'box2'], { x: 8, y: 8 })
|
* 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.
|
* @param shapes - The shapes (or shape ids) to duplicate.
|
||||||
|
@ -7288,7 +7292,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
||||||
private _selectionSharedStyles = computed<ReadonlySharedStyleMap>(
|
private _selectionSharedStyles = computed<ReadonlySharedStyleMap>(
|
||||||
'_selectionSharedStyles',
|
'_selectionSharedStyles',
|
||||||
() => {
|
() => {
|
||||||
const { selectedShapes } = this
|
const selectedShapes = this.getSelectedShapes()
|
||||||
|
|
||||||
const sharedStyles = new SharedStyleMap()
|
const sharedStyles = new SharedStyleMap()
|
||||||
for (const selectedShape of selectedShapes) {
|
for (const selectedShape of selectedShapes) {
|
||||||
|
@ -7418,7 +7422,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
||||||
* @param historyOptions - The history options for the change.
|
* @param historyOptions - The history options for the change.
|
||||||
*/
|
*/
|
||||||
setOpacityForSelectedShapes(opacity: number, historyOptions?: TLCommandHistoryOptions): this {
|
setOpacityForSelectedShapes(opacity: number, historyOptions?: TLCommandHistoryOptions): this {
|
||||||
const { selectedShapes } = this
|
const selectedShapes = this.getSelectedShapes()
|
||||||
|
|
||||||
if (selectedShapes.length > 0) {
|
if (selectedShapes.length > 0) {
|
||||||
const shapesToUpdate: TLShape[] = []
|
const shapesToUpdate: TLShape[] = []
|
||||||
|
@ -7505,7 +7509,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
||||||
value: T,
|
value: T,
|
||||||
historyOptions?: TLCommandHistoryOptions
|
historyOptions?: TLCommandHistoryOptions
|
||||||
): this {
|
): this {
|
||||||
const { selectedShapes } = this
|
const selectedShapes = this.getSelectedShapes()
|
||||||
|
|
||||||
if (selectedShapes.length > 0) {
|
if (selectedShapes.length > 0) {
|
||||||
const updates: {
|
const updates: {
|
||||||
|
@ -7847,7 +7851,7 @@ export class Editor extends EventEmitter<TLEventMap> {
|
||||||
let lowestAncestors: TLShape[] = []
|
let lowestAncestors: TLShape[] = []
|
||||||
|
|
||||||
// Among the selected shapes, find the shape with the fewest ancestors and use its first ancestor.
|
// 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
|
if (lowestDepth === 0) break
|
||||||
|
|
||||||
const isFrame = this.isShapeOfType<TLFrameShape>(shape, 'frame')
|
const isFrame = this.isShapeOfType<TLFrameShape>(shape, 'frame')
|
||||||
|
|
|
@ -286,7 +286,7 @@ export class SnapManager {
|
||||||
|
|
||||||
// This needs to be external from any expensive work
|
// This needs to be external from any expensive work
|
||||||
@computed get currentCommonAncestor() {
|
@computed get currentCommonAncestor() {
|
||||||
return this.editor.findCommonAncestor(this.editor.selectedShapes)
|
return this.editor.findCommonAncestor(this.editor.getSelectedShapes())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Points which belong to snappable shapes
|
// Points which belong to snappable shapes
|
||||||
|
|
|
@ -7,11 +7,11 @@ import { Vec2d } from '../primitives/Vec2d'
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
export function getRotationSnapshot({ editor }: { editor: Editor }): TLRotationSnapshot | null {
|
export function getRotationSnapshot({ editor }: { editor: Editor }): TLRotationSnapshot | null {
|
||||||
|
const selectedShapes = editor.getSelectedShapes()
|
||||||
const {
|
const {
|
||||||
selectionRotation,
|
selectionRotation,
|
||||||
selectionRotatedPageBounds: selectionBounds,
|
selectionRotatedPageBounds: selectionBounds,
|
||||||
inputs: { originPagePoint },
|
inputs: { originPagePoint },
|
||||||
selectedShapes,
|
|
||||||
} = editor
|
} = editor
|
||||||
|
|
||||||
// todo: this assumes we're rotating the selected shapes
|
// todo: this assumes we're rotating the selected shapes
|
||||||
|
|
|
@ -37,8 +37,8 @@ export const TldrawSelectionForeground: TLSelectionForegroundComponent = track(
|
||||||
!editor.getIsMenuOpen() && editor.getInstanceState().cursor.type === 'default'
|
!editor.getIsMenuOpen() && editor.getInstanceState().cursor.type === 'default'
|
||||||
const isCoarsePointer = editor.getInstanceState().isCoarsePointer
|
const isCoarsePointer = editor.getInstanceState().isCoarsePointer
|
||||||
|
|
||||||
const shapes = editor.selectedShapes
|
const shapes = editor.getSelectedShapes()
|
||||||
const onlyShape = editor.onlySelectedShape
|
const onlyShape = editor.getOnlySelectedShape()
|
||||||
const isLockedShape = onlyShape && editor.isShapeOrAncestorLocked(onlyShape)
|
const isLockedShape = onlyShape && editor.isShapeOrAncestorLocked(onlyShape)
|
||||||
|
|
||||||
// if all shapes have an expandBy for the selection outline, we can expand by the l
|
// if all shapes have an expandBy for the selection outline, we can expand by the l
|
||||||
|
|
|
@ -476,7 +476,7 @@ function centerSelecitonAroundPoint(editor: Editor, position: VecLike) {
|
||||||
const offset = selectionPageBounds!.center.sub(position)
|
const offset = selectionPageBounds!.center.sub(position)
|
||||||
|
|
||||||
editor.updateShapes(
|
editor.updateShapes(
|
||||||
editor.selectedShapes.map((shape) => {
|
editor.getSelectedShapes().map((shape) => {
|
||||||
const localRotation = editor.getShapeParentTransform(shape).decompose().rotation
|
const localRotation = editor.getShapeParentTransform(shape).decompose().rotation
|
||||||
const localDelta = Vec2d.Rot(offset, -localRotation)
|
const localDelta = Vec2d.Rot(offset, -localRotation)
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -488,24 +488,24 @@ describe("an arrow's parents", () => {
|
||||||
|
|
||||||
editor.setCurrentTool('frame')
|
editor.setCurrentTool('frame')
|
||||||
editor.pointerDown(0, 0).pointerMove(100, 100).pointerUp()
|
editor.pointerDown(0, 0).pointerMove(100, 100).pointerUp()
|
||||||
frameId = editor.onlySelectedShape!.id
|
frameId = editor.getOnlySelectedShape()!.id
|
||||||
|
|
||||||
editor.setCurrentTool('geo')
|
editor.setCurrentTool('geo')
|
||||||
editor.pointerDown(10, 10).pointerMove(20, 20).pointerUp()
|
editor.pointerDown(10, 10).pointerMove(20, 20).pointerUp()
|
||||||
boxAid = editor.onlySelectedShape!.id
|
boxAid = editor.getOnlySelectedShape()!.id
|
||||||
editor.setCurrentTool('geo')
|
editor.setCurrentTool('geo')
|
||||||
editor.pointerDown(10, 80).pointerMove(20, 90).pointerUp()
|
editor.pointerDown(10, 80).pointerMove(20, 90).pointerUp()
|
||||||
boxBid = editor.onlySelectedShape!.id
|
boxBid = editor.getOnlySelectedShape()!.id
|
||||||
editor.setCurrentTool('geo')
|
editor.setCurrentTool('geo')
|
||||||
editor.pointerDown(110, 10).pointerMove(120, 20).pointerUp()
|
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", () => {
|
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
|
// draw arrow from a to empty space within frame, but don't pointer up yet
|
||||||
editor.setCurrentTool('arrow')
|
editor.setCurrentTool('arrow')
|
||||||
editor.pointerDown(15, 15).pointerMove(50, 50)
|
editor.pointerDown(15, 15).pointerMove(50, 50)
|
||||||
const arrowId = editor.onlySelectedShape!.id
|
const arrowId = editor.getOnlySelectedShape()!.id
|
||||||
|
|
||||||
expect(editor.getShape(arrowId)).toMatchObject({
|
expect(editor.getShape(arrowId)).toMatchObject({
|
||||||
props: {
|
props: {
|
||||||
|
@ -540,7 +540,7 @@ describe("an arrow's parents", () => {
|
||||||
// draw arrow from a to b
|
// draw arrow from a to b
|
||||||
editor.setCurrentTool('arrow')
|
editor.setCurrentTool('arrow')
|
||||||
editor.pointerDown(15, 15).pointerMove(15, 85).pointerUp()
|
editor.pointerDown(15, 15).pointerMove(15, 85).pointerUp()
|
||||||
const arrowId = editor.onlySelectedShape!.id
|
const arrowId = editor.getOnlySelectedShape()!.id
|
||||||
|
|
||||||
expect(editor.getShape(arrowId)).toMatchObject({
|
expect(editor.getShape(arrowId)).toMatchObject({
|
||||||
parentId: frameId,
|
parentId: frameId,
|
||||||
|
@ -564,7 +564,7 @@ describe("an arrow's parents", () => {
|
||||||
// draw arrow from a to c
|
// draw arrow from a to c
|
||||||
editor.setCurrentTool('arrow')
|
editor.setCurrentTool('arrow')
|
||||||
editor.pointerDown(15, 15).pointerMove(115, 15).pointerUp()
|
editor.pointerDown(15, 15).pointerMove(115, 15).pointerUp()
|
||||||
const arrowId = editor.onlySelectedShape!.id
|
const arrowId = editor.getOnlySelectedShape()!.id
|
||||||
expect(editor.getShape(arrowId)).toMatchObject({
|
expect(editor.getShape(arrowId)).toMatchObject({
|
||||||
parentId: editor.currentPageId,
|
parentId: editor.currentPageId,
|
||||||
props: {
|
props: {
|
||||||
|
|
|
@ -487,7 +487,7 @@ export class ArrowShapeUtil extends ShapeUtil<TLArrowShape> {
|
||||||
// Not a class component, but eslint can't tell that :(
|
// Not a class component, but eslint can't tell that :(
|
||||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||||
const theme = useDefaultColorTheme()
|
const theme = useDefaultColorTheme()
|
||||||
const onlySelectedShape = this.editor.onlySelectedShape
|
const onlySelectedShape = this.editor.getOnlySelectedShape()
|
||||||
const shouldDisplayHandles =
|
const shouldDisplayHandles =
|
||||||
this.editor.isInAny(
|
this.editor.isInAny(
|
||||||
'select.idle',
|
'select.idle',
|
||||||
|
|
|
@ -18,7 +18,7 @@ export class Idle extends StateNode {
|
||||||
override onKeyUp: TLEventHandlers['onKeyUp'] = (info) => {
|
override onKeyUp: TLEventHandlers['onKeyUp'] = (info) => {
|
||||||
if (info.key === 'Enter') {
|
if (info.key === 'Enter') {
|
||||||
if (this.editor.getInstanceState().isReadonly) return null
|
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 the only selected shape is editable, start editing it
|
||||||
if (
|
if (
|
||||||
onlySelectedShape &&
|
onlySelectedShape &&
|
||||||
|
|
|
@ -103,7 +103,7 @@ describe('When in the idle state', () => {
|
||||||
expect(editor.currentPageShapes.length).toBe(2)
|
expect(editor.currentPageShapes.length).toBe(2)
|
||||||
|
|
||||||
editor.selectAll()
|
editor.selectAll()
|
||||||
expect(editor.selectedShapes.length).toBe(2)
|
expect(editor.getSelectedShapes().length).toBe(2)
|
||||||
|
|
||||||
editor.keyUp('Enter')
|
editor.keyUp('Enter')
|
||||||
editor.expectPathToBe('root.select.idle')
|
editor.expectPathToBe('root.select.idle')
|
||||||
|
|
|
@ -15,7 +15,7 @@ export class Idle extends StateNode {
|
||||||
if (info.key === 'Enter') {
|
if (info.key === 'Enter') {
|
||||||
if (this.editor.getInstanceState().isReadonly) return null
|
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 the only selected shape is editable, start editing it
|
||||||
if (
|
if (
|
||||||
onlySelectedShape &&
|
onlySelectedShape &&
|
||||||
|
|
|
@ -86,7 +86,7 @@ export class ImageShapeUtil extends BaseBoxShapeUtil<TLImageShape> {
|
||||||
|
|
||||||
const isSelected = useValue(
|
const isSelected = useValue(
|
||||||
'onlySelectedShape',
|
'onlySelectedShape',
|
||||||
() => shape.id === this.editor.onlySelectedShape?.id,
|
() => shape.id === this.editor.getOnlySelectedShape()?.id,
|
||||||
[this.editor]
|
[this.editor]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ export class Idle extends StateNode {
|
||||||
override onKeyDown: TLEventHandlers['onKeyDown'] = (info) => {
|
override onKeyDown: TLEventHandlers['onKeyDown'] = (info) => {
|
||||||
if (info.key === 'Enter') {
|
if (info.key === 'Enter') {
|
||||||
if (this.editor.getInstanceState().isReadonly) return null
|
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 the only selected shape is editable, start editing it
|
||||||
if (
|
if (
|
||||||
onlySelectedShape &&
|
onlySelectedShape &&
|
||||||
|
|
|
@ -11,7 +11,7 @@ export class Idle extends StateNode {
|
||||||
{ ephemeral: true }
|
{ ephemeral: true }
|
||||||
)
|
)
|
||||||
|
|
||||||
const { onlySelectedShape } = this.editor
|
const onlySelectedShape = this.editor.getOnlySelectedShape()
|
||||||
|
|
||||||
// well this fucking sucks. what the fuck.
|
// well this fucking sucks. what the fuck.
|
||||||
// it's possible for a user to enter cropping, then undo
|
// it's possible for a user to enter cropping, then undo
|
||||||
|
|
|
@ -88,7 +88,7 @@ export class TranslatingCrop extends StateNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
private createSnapshot() {
|
private createSnapshot() {
|
||||||
const shape = this.editor.onlySelectedShape as ShapeWithCrop
|
const shape = this.editor.getOnlySelectedShape() as ShapeWithCrop
|
||||||
return { shape }
|
return { shape }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -60,7 +60,7 @@ export class Cropping extends StateNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateCursor() {
|
private updateCursor() {
|
||||||
const selectedShape = this.editor.selectedShapes[0]
|
const selectedShape = this.editor.getSelectedShapes()[0]
|
||||||
if (!selectedShape) return
|
if (!selectedShape) return
|
||||||
|
|
||||||
const cursorType = CursorTypeMap[this.info.handle!]
|
const cursorType = CursorTypeMap[this.info.handle!]
|
||||||
|
@ -229,7 +229,7 @@ export class Cropping extends StateNode {
|
||||||
inputs: { originPagePoint },
|
inputs: { originPagePoint },
|
||||||
} = this.editor
|
} = this.editor
|
||||||
|
|
||||||
const shape = this.editor.onlySelectedShape as TLImageShape
|
const shape = this.editor.getOnlySelectedShape() as TLImageShape
|
||||||
|
|
||||||
const selectionBounds = this.editor.selectionRotatedPageBounds!
|
const selectionBounds = this.editor.selectionRotatedPageBounds!
|
||||||
|
|
||||||
|
|
|
@ -67,8 +67,8 @@ export class Idle extends StateNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectedShapeIds = this.editor.getSelectedShapeIds()
|
const selectedShapeIds = this.editor.getSelectedShapeIds()
|
||||||
|
const onlySelectedShape = this.editor.getOnlySelectedShape()
|
||||||
const {
|
const {
|
||||||
onlySelectedShape,
|
|
||||||
inputs: { currentPagePoint },
|
inputs: { currentPagePoint },
|
||||||
} = this.editor
|
} = this.editor
|
||||||
|
|
||||||
|
@ -224,7 +224,7 @@ export class Idle extends StateNode {
|
||||||
case 'selection': {
|
case 'selection': {
|
||||||
if (this.editor.getInstanceState().isReadonly) break
|
if (this.editor.getInstanceState().isReadonly) break
|
||||||
|
|
||||||
const { onlySelectedShape } = this.editor
|
const onlySelectedShape = this.editor.getOnlySelectedShape()
|
||||||
|
|
||||||
if (onlySelectedShape) {
|
if (onlySelectedShape) {
|
||||||
const util = this.editor.getShapeUtil(onlySelectedShape)
|
const util = this.editor.getShapeUtil(onlySelectedShape)
|
||||||
|
@ -346,8 +346,8 @@ export class Idle extends StateNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectedShapeIds = this.editor.getSelectedShapeIds()
|
const selectedShapeIds = this.editor.getSelectedShapeIds()
|
||||||
|
const onlySelectedShape = this.editor.getOnlySelectedShape()
|
||||||
const {
|
const {
|
||||||
onlySelectedShape,
|
|
||||||
inputs: { currentPagePoint },
|
inputs: { currentPagePoint },
|
||||||
} = this.editor
|
} = this.editor
|
||||||
|
|
||||||
|
@ -425,7 +425,7 @@ export class Idle extends StateNode {
|
||||||
override onKeyUp = (info: TLKeyboardEventInfo) => {
|
override onKeyUp = (info: TLKeyboardEventInfo) => {
|
||||||
switch (info.code) {
|
switch (info.code) {
|
||||||
case 'Enter': {
|
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
|
// On enter, if every selected shape is a group, then select all of the children of the groups
|
||||||
if (
|
if (
|
||||||
|
@ -438,7 +438,7 @@ export class Idle extends StateNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the only selected shape is editable, then begin editing it
|
// 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)) {
|
if (onlySelectedShape && this.shouldStartEditingShape(onlySelectedShape)) {
|
||||||
this.startEditingShape(onlySelectedShape, {
|
this.startEditingShape(onlySelectedShape, {
|
||||||
...info,
|
...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 (!shape) return false
|
||||||
if (this.editor.isShapeOrAncestorLocked(shape) && shape.type !== 'embed') return false
|
if (this.editor.isShapeOrAncestorLocked(shape) && shape.type !== 'embed') return false
|
||||||
if (!this.canInteractWithShapeInReadOnly(shape)) return false
|
if (!this.canInteractWithShapeInReadOnly(shape)) return false
|
||||||
|
|
|
@ -25,7 +25,7 @@ export class PointingCropHandle extends StateNode {
|
||||||
override onEnter = (info: TLPointingCropHandleInfo) => {
|
override onEnter = (info: TLPointingCropHandleInfo) => {
|
||||||
this.info = info
|
this.info = info
|
||||||
this.parent.currentToolIdMask = info.onInteractionEnd
|
this.parent.currentToolIdMask = info.onInteractionEnd
|
||||||
const selectedShape = this.editor.selectedShapes[0]
|
const selectedShape = this.editor.getSelectedShapes()[0]
|
||||||
if (!selectedShape) return
|
if (!selectedShape) return
|
||||||
|
|
||||||
this.updateCursor(selectedShape)
|
this.updateCursor(selectedShape)
|
||||||
|
|
|
@ -32,7 +32,7 @@ export class PointingResizeHandle extends StateNode {
|
||||||
private info = {} as PointingResizeHandleInfo
|
private info = {} as PointingResizeHandleInfo
|
||||||
|
|
||||||
private updateCursor() {
|
private updateCursor() {
|
||||||
const selected = this.editor.selectedShapes
|
const selected = this.editor.getSelectedShapes()
|
||||||
const cursorType = CursorTypeMap[this.info.handle!]
|
const cursorType = CursorTypeMap[this.info.handle!]
|
||||||
this.editor.updateInstanceState({
|
this.editor.updateInstanceState({
|
||||||
cursor: { type: cursorType, rotation: selected.length === 1 ? selected[0].rotation : 0 },
|
cursor: { type: cursorType, rotation: selected.length === 1 ? selected[0].rotation : 0 },
|
||||||
|
|
|
@ -63,7 +63,9 @@ export class Resizing extends StateNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.snapshot = this._createSnapshot()
|
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)
|
if (!isCreating) this.editor.mark(this.markId)
|
||||||
|
|
||||||
|
@ -107,8 +109,9 @@ export class Resizing extends StateNode {
|
||||||
private complete() {
|
private complete() {
|
||||||
this.handleResizeEnd()
|
this.handleResizeEnd()
|
||||||
|
|
||||||
if (this.editAfterComplete && this.editor.onlySelectedShape) {
|
const onlySelectedShape = this.editor.getOnlySelectedShape()
|
||||||
this.editor.setEditingShape(this.editor.onlySelectedShape.id)
|
if (this.editAfterComplete && onlySelectedShape) {
|
||||||
|
this.editor.setEditingShape(onlySelectedShape.id)
|
||||||
this.editor.setCurrentTool('select.editing_shape')
|
this.editor.setCurrentTool('select.editing_shape')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,7 +53,7 @@ export class Translating extends StateNode {
|
||||||
this.isCreating = isCreating
|
this.isCreating = isCreating
|
||||||
this.editAfterComplete = editAfterComplete
|
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.editor.mark(this.markId)
|
||||||
this.isCloning = false
|
this.isCloning = false
|
||||||
this.info = info
|
this.info = info
|
||||||
|
@ -166,7 +166,7 @@ export class Translating extends StateNode {
|
||||||
this.editor.setCurrentTool(this.info.onInteractionEnd)
|
this.editor.setCurrentTool(this.info.onInteractionEnd)
|
||||||
} else {
|
} else {
|
||||||
if (this.editAfterComplete) {
|
if (this.editAfterComplete) {
|
||||||
const onlySelected = this.editor.onlySelectedShape
|
const onlySelected = this.editor.getOnlySelectedShape()
|
||||||
if (onlySelected) {
|
if (onlySelected) {
|
||||||
this.editor.setEditingShape(onlySelected.id)
|
this.editor.setEditingShape(onlySelected.id)
|
||||||
this.editor.setCurrentTool('select.editing_shape')
|
this.editor.setCurrentTool('select.editing_shape')
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { Editor } from '@tldraw/editor'
|
import { Editor } from '@tldraw/editor'
|
||||||
|
|
||||||
export function getShouldEnterCropMode(editor: Editor): boolean {
|
export function getShouldEnterCropMode(editor: Editor): boolean {
|
||||||
const { onlySelectedShape } = editor
|
const onlySelectedShape = editor.getOnlySelectedShape()
|
||||||
return !!(
|
return !!(
|
||||||
onlySelectedShape &&
|
onlySelectedShape &&
|
||||||
!editor.isShapeOrAncestorLocked(onlySelectedShape) &&
|
!editor.isShapeOrAncestorLocked(onlySelectedShape) &&
|
||||||
|
|
|
@ -27,7 +27,7 @@ export const ContextMenu = function ContextMenu({ children }: { children: any })
|
||||||
const cb = useCallback(
|
const cb = useCallback(
|
||||||
(isOpen: boolean) => {
|
(isOpen: boolean) => {
|
||||||
if (!isOpen) {
|
if (!isOpen) {
|
||||||
const { onlySelectedShape } = editor
|
const onlySelectedShape = editor.getOnlySelectedShape()
|
||||||
|
|
||||||
if (onlySelectedShape && editor.isShapeOrAncestorLocked(onlySelectedShape)) {
|
if (onlySelectedShape && editor.isShapeOrAncestorLocked(onlySelectedShape)) {
|
||||||
editor.setSelectedShapes([])
|
editor.setSelectedShapes([])
|
||||||
|
@ -35,8 +35,8 @@ export const ContextMenu = function ContextMenu({ children }: { children: any })
|
||||||
} else {
|
} else {
|
||||||
// Weird route: selecting locked shapes on long press
|
// Weird route: selecting locked shapes on long press
|
||||||
if (editor.getInstanceState().isCoarsePointer) {
|
if (editor.getInstanceState().isCoarsePointer) {
|
||||||
|
const selectedShapes = editor.getSelectedShapes()
|
||||||
const {
|
const {
|
||||||
selectedShapes,
|
|
||||||
inputs: { currentPagePoint },
|
inputs: { currentPagePoint },
|
||||||
} = editor
|
} = editor
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ export const ContextMenu = function ContextMenu({ children }: { children: any })
|
||||||
|
|
||||||
if (
|
if (
|
||||||
// if there are no selected shapes
|
// 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
|
// OR if none of the shapes at the point include the selected shape
|
||||||
!shapesAtPoint.some((s) => selectedShapes.includes(s))
|
!shapesAtPoint.some((s) => selectedShapes.includes(s))
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -175,7 +175,7 @@ const DebugMenuContent = track(function DebugMenuContent({
|
||||||
return count
|
return count
|
||||||
}
|
}
|
||||||
|
|
||||||
const { selectedShapes } = editor
|
const selectedShapes = editor.getSelectedShapes()
|
||||||
|
|
||||||
const shapes = selectedShapes.length === 0 ? editor.renderingShapes : selectedShapes
|
const shapes = selectedShapes.length === 0 ? editor.renderingShapes : selectedShapes
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ type ShapeWithUrl = TLBaseShape<string, { url: string }>
|
||||||
export const EditLinkDialog = track(function EditLinkDialog({ onClose }: TLUiDialogProps) {
|
export const EditLinkDialog = track(function EditLinkDialog({ onClose }: TLUiDialogProps) {
|
||||||
const editor = useEditor()
|
const editor = useEditor()
|
||||||
|
|
||||||
const selectedShape = editor.onlySelectedShape
|
const selectedShape = editor.getOnlySelectedShape()
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!(selectedShape && 'url' in selectedShape.props && typeof selectedShape.props.url === 'string')
|
!(selectedShape && 'url' in selectedShape.props && typeof selectedShape.props.url === 'string')
|
||||||
|
@ -89,7 +89,7 @@ export const EditLinkDialogInner = track(function EditLinkDialogInner({
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const handleClear = useCallback(() => {
|
const handleClear = useCallback(() => {
|
||||||
const { onlySelectedShape } = editor
|
const onlySelectedShape = editor.getOnlySelectedShape()
|
||||||
if (!onlySelectedShape) return
|
if (!onlySelectedShape) return
|
||||||
editor.updateShapes([
|
editor.updateShapes([
|
||||||
{ id: onlySelectedShape.id, type: onlySelectedShape.type, props: { url: '' } },
|
{ id: onlySelectedShape.id, type: onlySelectedShape.type, props: { url: '' } },
|
||||||
|
@ -98,7 +98,7 @@ export const EditLinkDialogInner = track(function EditLinkDialogInner({
|
||||||
}, [editor, onClose])
|
}, [editor, onClose])
|
||||||
|
|
||||||
const handleComplete = useCallback(() => {
|
const handleComplete = useCallback(() => {
|
||||||
const { onlySelectedShape } = editor
|
const onlySelectedShape = editor.getOnlySelectedShape()
|
||||||
|
|
||||||
if (!onlySelectedShape) return
|
if (!onlySelectedShape) return
|
||||||
|
|
||||||
|
|
|
@ -233,7 +233,8 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
|
||||||
trackEvent('toggle-auto-size', { source })
|
trackEvent('toggle-auto-size', { source })
|
||||||
editor.mark('toggling auto size')
|
editor.mark('toggling auto size')
|
||||||
editor.updateShapes(
|
editor.updateShapes(
|
||||||
editor.selectedShapes
|
editor
|
||||||
|
.getSelectedShapes()
|
||||||
.filter(
|
.filter(
|
||||||
(shape): shape is TLTextShape =>
|
(shape): shape is TLTextShape =>
|
||||||
editor.isShapeOfType<TLTextShape>(shape, 'text') && shape.props.autoSize === false
|
editor.isShapeOfType<TLTextShape>(shape, 'text') && shape.props.autoSize === false
|
||||||
|
@ -299,7 +300,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
|
||||||
|
|
||||||
editor.batch(() => {
|
editor.batch(() => {
|
||||||
trackEvent('convert-to-bookmark', { source })
|
trackEvent('convert-to-bookmark', { source })
|
||||||
const shapes = editor.selectedShapes
|
const shapes = editor.getSelectedShapes()
|
||||||
|
|
||||||
const createList: TLShapePartial[] = []
|
const createList: TLShapePartial[] = []
|
||||||
const deleteList: TLShapeId[] = []
|
const deleteList: TLShapeId[] = []
|
||||||
|
@ -441,7 +442,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
|
||||||
if (mustGoBackToSelectToolFirst()) return
|
if (mustGoBackToSelectToolFirst()) return
|
||||||
|
|
||||||
trackEvent('group-shapes', { source })
|
trackEvent('group-shapes', { source })
|
||||||
const { onlySelectedShape } = editor
|
const onlySelectedShape = editor.getOnlySelectedShape()
|
||||||
if (onlySelectedShape && editor.isShapeOfType<TLGroupShape>(onlySelectedShape, 'group')) {
|
if (onlySelectedShape && editor.isShapeOfType<TLGroupShape>(onlySelectedShape, 'group')) {
|
||||||
editor.mark('ungroup')
|
editor.mark('ungroup')
|
||||||
editor.ungroupShapes(editor.getSelectedShapeIds())
|
editor.ungroupShapes(editor.getSelectedShapeIds())
|
||||||
|
|
|
@ -76,7 +76,7 @@ export const TLUiContextMenuSchemaProvider = track(function TLUiContextMenuSchem
|
||||||
const allowUngroup = useAllowUngroup()
|
const allowUngroup = useAllowUngroup()
|
||||||
const hasClipboardWrite = Boolean(window.navigator.clipboard?.write)
|
const hasClipboardWrite = Boolean(window.navigator.clipboard?.write)
|
||||||
const showEditLink = useHasLinkShapeSelected()
|
const showEditLink = useHasLinkShapeSelected()
|
||||||
const { onlySelectedShape } = editor
|
const onlySelectedShape = editor.getOnlySelectedShape()
|
||||||
const isShapeLocked = onlySelectedShape && editor.isShapeOrAncestorLocked(onlySelectedShape)
|
const isShapeLocked = onlySelectedShape && editor.isShapeOrAncestorLocked(onlySelectedShape)
|
||||||
|
|
||||||
const contextTLUiMenuSchema = useMemo<TLUiMenuSchema>(() => {
|
const contextTLUiMenuSchema = useMemo<TLUiMenuSchema>(() => {
|
||||||
|
|
|
@ -5,7 +5,7 @@ export function useHasLinkShapeSelected() {
|
||||||
return useValue(
|
return useValue(
|
||||||
'hasLinkShapeSelected',
|
'hasLinkShapeSelected',
|
||||||
() => {
|
() => {
|
||||||
const { selectedShapes } = editor
|
const selectedShapes = editor.getSelectedShapes()
|
||||||
return (
|
return (
|
||||||
selectedShapes.length === 1 &&
|
selectedShapes.length === 1 &&
|
||||||
'url' in selectedShapes[0].props &&
|
'url' in selectedShapes[0].props &&
|
||||||
|
|
|
@ -84,7 +84,7 @@ export function TLUiMenuSchemaProvider({ overrides, children }: TLUiMenuSchemaPr
|
||||||
const oneEmbedSelected = useValue(
|
const oneEmbedSelected = useValue(
|
||||||
'oneEmbedSelected',
|
'oneEmbedSelected',
|
||||||
() => {
|
() => {
|
||||||
const { onlySelectedShape } = editor
|
const onlySelectedShape = editor.getOnlySelectedShape()
|
||||||
if (!onlySelectedShape) return false
|
if (!onlySelectedShape) return false
|
||||||
return !!(
|
return !!(
|
||||||
editor.isShapeOfType<TLEmbedShape>(onlySelectedShape, 'embed') &&
|
editor.isShapeOfType<TLEmbedShape>(onlySelectedShape, 'embed') &&
|
||||||
|
@ -98,7 +98,7 @@ export function TLUiMenuSchemaProvider({ overrides, children }: TLUiMenuSchemaPr
|
||||||
const oneEmbeddableBookmarkSelected = useValue(
|
const oneEmbeddableBookmarkSelected = useValue(
|
||||||
'oneEmbeddableBookmarkSelected',
|
'oneEmbeddableBookmarkSelected',
|
||||||
() => {
|
() => {
|
||||||
const { onlySelectedShape } = editor
|
const onlySelectedShape = editor.getOnlySelectedShape()
|
||||||
if (!onlySelectedShape) return false
|
if (!onlySelectedShape) return false
|
||||||
return !!(
|
return !!(
|
||||||
editor.isShapeOfType<TLBookmarkShape>(onlySelectedShape, 'bookmark') &&
|
editor.isShapeOfType<TLBookmarkShape>(onlySelectedShape, 'bookmark') &&
|
||||||
|
|
|
@ -12,7 +12,7 @@ export function useOnlyFlippableShape() {
|
||||||
return useValue(
|
return useValue(
|
||||||
'onlyFlippableShape',
|
'onlyFlippableShape',
|
||||||
() => {
|
() => {
|
||||||
const { selectedShapes } = editor
|
const selectedShapes = editor.getSelectedShapes()
|
||||||
return (
|
return (
|
||||||
selectedShapes.length === 1 &&
|
selectedShapes.length === 1 &&
|
||||||
selectedShapes.every(
|
selectedShapes.every(
|
||||||
|
|
|
@ -5,7 +5,7 @@ export function useShowAutoSizeToggle() {
|
||||||
return useValue(
|
return useValue(
|
||||||
'showAutoSizeToggle',
|
'showAutoSizeToggle',
|
||||||
() => {
|
() => {
|
||||||
const { selectedShapes } = editor
|
const selectedShapes = editor.getSelectedShapes()
|
||||||
return (
|
return (
|
||||||
selectedShapes.length === 1 &&
|
selectedShapes.length === 1 &&
|
||||||
editor.isShapeOfType<TLTextShape>(selectedShapes[0], 'text') &&
|
editor.isShapeOfType<TLTextShape>(selectedShapes[0], 'text') &&
|
||||||
|
|
|
@ -415,7 +415,7 @@ describe('When clicking and dragging', () => {
|
||||||
|
|
||||||
describe('Does not erase hollow shapes on click', () => {
|
describe('Does not erase hollow shapes on click', () => {
|
||||||
it('Returns to select on cancel', () => {
|
it('Returns to select on cancel', () => {
|
||||||
editor.selectAll().deleteShapes(editor.selectedShapes)
|
editor.selectAll().deleteShapes(editor.getSelectedShapes())
|
||||||
expect(editor.currentPageShapes.length).toBe(0)
|
expect(editor.currentPageShapes.length).toBe(0)
|
||||||
editor.createShape({
|
editor.createShape({
|
||||||
id: createShapeId(),
|
id: createShapeId(),
|
||||||
|
|
|
@ -19,6 +19,6 @@ describe('Highlight shape', () => {
|
||||||
editor.setCurrentTool('highlight').pointerDown(60, 60).pointerUp()
|
editor.setCurrentTool('highlight').pointerDown(60, 60).pointerUp()
|
||||||
editor.setCurrentTool('select').pointerDown(70, 70).pointerUp()
|
editor.setCurrentTool('select').pointerDown(70, 70).pointerUp()
|
||||||
|
|
||||||
expect(editor.selectedShapes).toHaveLength(1)
|
expect(editor.getSelectedShapes()).toHaveLength(1)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -94,7 +94,7 @@ describe('TLSelectTool.Translating', () => {
|
||||||
editor.pointerDown(150, 150, { target: 'shape', shape })
|
editor.pointerDown(150, 150, { target: 'shape', shape })
|
||||||
editor.pointerMove(150, 250)
|
editor.pointerMove(150, 250)
|
||||||
editor.pointerUp()
|
editor.pointerUp()
|
||||||
const box2Id = editor.onlySelectedShape!.id
|
const box2Id = editor.getOnlySelectedShape()!.id
|
||||||
expect(editor.currentPageShapes.length).toStrictEqual(2)
|
expect(editor.currentPageShapes.length).toStrictEqual(2)
|
||||||
expect(ids.box1).not.toEqual(box2Id)
|
expect(ids.box1).not.toEqual(box2Id)
|
||||||
|
|
||||||
|
@ -318,11 +318,11 @@ describe('When editing shapes', () => {
|
||||||
// start editing the geo shape
|
// start editing the geo shape
|
||||||
editor.doubleClick(50, 50, { target: 'shape', shape: editor.getShape(ids.geo1) })
|
editor.doubleClick(50, 50, { target: 'shape', shape: editor.getShape(ids.geo1) })
|
||||||
expect(editor.editingShapeId).toBe(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
|
// point the text shape
|
||||||
editor.pointerDown(50, 50, { target: 'shape', shape: editor.getShape(ids.text1) })
|
editor.pointerDown(50, 50, { target: 'shape', shape: editor.getShape(ids.text1) })
|
||||||
expect(editor.editingShapeId).toBe(null)
|
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,
|
// 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
|
// start editing the geo shape
|
||||||
editor.doubleClick(50, 50, { target: 'shape', shape: editor.getShape(ids.geo1) })
|
editor.doubleClick(50, 50, { target: 'shape', shape: editor.getShape(ids.geo1) })
|
||||||
expect(editor.editingShapeId).toBe(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
|
// point the other geo shape
|
||||||
editor.pointerDown(50, 50, { target: 'shape', shape: editor.getShape(ids.geo2) })
|
editor.pointerDown(50, 50, { target: 'shape', shape: editor.getShape(ids.geo2) })
|
||||||
// that other shape should now be editing and selected!
|
// that other shape should now be editing and selected!
|
||||||
expect(editor.editingShapeId).toBe(ids.geo2)
|
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
|
// 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) })
|
editor.pointerDown(50, 50, { target: 'shape', shape: editor.getShape(ids.text2) })
|
||||||
// that other shape should now be editing and selected!
|
// that other shape should now be editing and selected!
|
||||||
expect(editor.editingShapeId).toBe(ids.text2)
|
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', () => {
|
it('Double clicking the canvas creates a new text shape', () => {
|
||||||
|
|
|
@ -18,12 +18,12 @@ describe('arrowBindingsIndex', () => {
|
||||||
editor.selectNone()
|
editor.selectNone()
|
||||||
editor.setCurrentTool('arrow')
|
editor.setCurrentTool('arrow')
|
||||||
editor.pointerDown(50, 50)
|
editor.pointerDown(50, 50)
|
||||||
expect(editor.onlySelectedShape).toBe(null)
|
expect(editor.getOnlySelectedShape()).toBe(null)
|
||||||
expect(editor.getArrowsBoundTo(ids.box1)).toEqual([])
|
expect(editor.getArrowsBoundTo(ids.box1)).toEqual([])
|
||||||
|
|
||||||
editor.pointerMove(50, 55)
|
editor.pointerMove(50, 55)
|
||||||
expect(editor.onlySelectedShape).not.toBe(null)
|
expect(editor.getOnlySelectedShape()).not.toBe(null)
|
||||||
const arrow = editor.onlySelectedShape!
|
const arrow = editor.getOnlySelectedShape()!
|
||||||
expect(arrow.type).toBe('arrow')
|
expect(arrow.type).toBe('arrow')
|
||||||
expect(editor.getArrowsBoundTo(ids.box1)).toEqual([
|
expect(editor.getArrowsBoundTo(ids.box1)).toEqual([
|
||||||
{ arrowId: arrow.id, handleId: 'start' },
|
{ arrowId: arrow.id, handleId: 'start' },
|
||||||
|
@ -48,7 +48,7 @@ describe('arrowBindingsIndex', () => {
|
||||||
expect(editor.getArrowsBoundTo(ids.box1)).toEqual([])
|
expect(editor.getArrowsBoundTo(ids.box1)).toEqual([])
|
||||||
|
|
||||||
editor.pointerMove(250, 50)
|
editor.pointerMove(250, 50)
|
||||||
const arrow1 = editor.onlySelectedShape!
|
const arrow1 = editor.getOnlySelectedShape()!
|
||||||
expect(arrow1.type).toBe('arrow')
|
expect(arrow1.type).toBe('arrow')
|
||||||
|
|
||||||
expect(editor.getArrowsBoundTo(ids.box1)).toEqual([{ arrowId: arrow1.id, handleId: 'start' }])
|
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
|
// start at box 1 and end on the page
|
||||||
editor.setCurrentTool('arrow')
|
editor.setCurrentTool('arrow')
|
||||||
editor.pointerDown(50, 50).pointerMove(50, -50).pointerUp(50, -50)
|
editor.pointerDown(50, 50).pointerMove(50, -50).pointerUp(50, -50)
|
||||||
const arrow2 = editor.onlySelectedShape!
|
const arrow2 = editor.getOnlySelectedShape()!
|
||||||
expect(arrow2.type).toBe('arrow')
|
expect(arrow2.type).toBe('arrow')
|
||||||
|
|
||||||
expect(editor.getArrowsBoundTo(ids.box1)).toEqual([
|
expect(editor.getArrowsBoundTo(ids.box1)).toEqual([
|
||||||
|
@ -73,7 +73,7 @@ describe('arrowBindingsIndex', () => {
|
||||||
// start outside box 1 and end in box 1
|
// start outside box 1 and end in box 1
|
||||||
editor.setCurrentTool('arrow')
|
editor.setCurrentTool('arrow')
|
||||||
editor.pointerDown(0, -50).pointerMove(50, 50).pointerUp(50, 50)
|
editor.pointerDown(0, -50).pointerMove(50, 50).pointerUp(50, 50)
|
||||||
const arrow3 = editor.onlySelectedShape!
|
const arrow3 = editor.getOnlySelectedShape()!
|
||||||
expect(arrow3.type).toBe('arrow')
|
expect(arrow3.type).toBe('arrow')
|
||||||
|
|
||||||
expect(editor.getArrowsBoundTo(ids.box1)).toEqual([
|
expect(editor.getArrowsBoundTo(ids.box1)).toEqual([
|
||||||
|
@ -91,7 +91,7 @@ describe('arrowBindingsIndex', () => {
|
||||||
editor.expectToBeIn('arrow.pointing')
|
editor.expectToBeIn('arrow.pointing')
|
||||||
editor.pointerMove(250, -50)
|
editor.pointerMove(250, -50)
|
||||||
editor.expectToBeIn('select.dragging_handle')
|
editor.expectToBeIn('select.dragging_handle')
|
||||||
const arrow4 = editor.onlySelectedShape!
|
const arrow4 = editor.getOnlySelectedShape()!
|
||||||
|
|
||||||
expect(editor.getArrowsBoundTo(ids.box2)).toEqual([
|
expect(editor.getArrowsBoundTo(ids.box2)).toEqual([
|
||||||
{ arrowId: arrow1.id, handleId: 'end' },
|
{ arrowId: arrow1.id, handleId: 'end' },
|
||||||
|
@ -110,7 +110,7 @@ describe('arrowBindingsIndex', () => {
|
||||||
// start outside box 2 and enter in box 2
|
// start outside box 2 and enter in box 2
|
||||||
editor.setCurrentTool('arrow')
|
editor.setCurrentTool('arrow')
|
||||||
editor.pointerDown(250, -50).pointerMove(250, 50).pointerUp(250, 50)
|
editor.pointerDown(250, -50).pointerMove(250, 50).pointerUp(250, 50)
|
||||||
const arrow5 = editor.onlySelectedShape!
|
const arrow5 = editor.getOnlySelectedShape()!
|
||||||
expect(arrow5.type).toBe('arrow')
|
expect(arrow5.type).toBe('arrow')
|
||||||
|
|
||||||
expect(editor.getArrowsBoundTo(ids.box1)).toEqual([
|
expect(editor.getArrowsBoundTo(ids.box1)).toEqual([
|
||||||
|
@ -150,23 +150,23 @@ describe('arrowBindingsIndex', () => {
|
||||||
// span both boxes
|
// span both boxes
|
||||||
editor.setCurrentTool('arrow')
|
editor.setCurrentTool('arrow')
|
||||||
editor.pointerDown(50, 50).pointerMove(250, 50).pointerUp(250, 50)
|
editor.pointerDown(50, 50).pointerMove(250, 50).pointerUp(250, 50)
|
||||||
arrowAId = editor.onlySelectedShape!.id
|
arrowAId = editor.getOnlySelectedShape()!.id
|
||||||
// start at box 1 and leave
|
// start at box 1 and leave
|
||||||
editor.setCurrentTool('arrow')
|
editor.setCurrentTool('arrow')
|
||||||
editor.pointerDown(50, 50).pointerMove(50, -50).pointerUp(50, -50)
|
editor.pointerDown(50, 50).pointerMove(50, -50).pointerUp(50, -50)
|
||||||
arrowBId = editor.onlySelectedShape!.id
|
arrowBId = editor.getOnlySelectedShape()!.id
|
||||||
// start outside box 1 and enter
|
// start outside box 1 and enter
|
||||||
editor.setCurrentTool('arrow')
|
editor.setCurrentTool('arrow')
|
||||||
editor.pointerDown(50, -50).pointerMove(50, 50).pointerUp(50, 50)
|
editor.pointerDown(50, -50).pointerMove(50, 50).pointerUp(50, 50)
|
||||||
arrowCId = editor.onlySelectedShape!.id
|
arrowCId = editor.getOnlySelectedShape()!.id
|
||||||
// start at box 2 and leave
|
// start at box 2 and leave
|
||||||
editor.setCurrentTool('arrow')
|
editor.setCurrentTool('arrow')
|
||||||
editor.pointerDown(250, 50).pointerMove(250, -50).pointerUp(250, -50)
|
editor.pointerDown(250, 50).pointerMove(250, -50).pointerUp(250, -50)
|
||||||
arrowDId = editor.onlySelectedShape!.id
|
arrowDId = editor.getOnlySelectedShape()!.id
|
||||||
// start outside box 2 and enter
|
// start outside box 2 and enter
|
||||||
editor.setCurrentTool('arrow')
|
editor.setCurrentTool('arrow')
|
||||||
editor.pointerDown(250, -50).pointerMove(250, 50).pointerUp(250, 50)
|
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', () => {
|
it('deletes the entry if you delete the bound shapes', () => {
|
||||||
expect(editor.getArrowsBoundTo(ids.box2)).toHaveLength(3)
|
expect(editor.getArrowsBoundTo(ids.box2)).toHaveLength(3)
|
||||||
|
@ -236,7 +236,8 @@ describe('arrowBindingsIndex', () => {
|
||||||
editor.selectAll()
|
editor.selectAll()
|
||||||
editor.duplicateShapes(editor.getSelectedShapeIds())
|
editor.duplicateShapes(editor.getSelectedShapeIds())
|
||||||
|
|
||||||
const [box1Clone, box2Clone] = editor.selectedShapes
|
const [box1Clone, box2Clone] = editor
|
||||||
|
.getSelectedShapes()
|
||||||
.filter((shape) => editor.isShapeOfType<TLGeoShape>(shape, 'geo'))
|
.filter((shape) => editor.isShapeOfType<TLGeoShape>(shape, 'geo'))
|
||||||
.sort((a, b) => a.x - b.x)
|
.sort((a, b) => a.x - b.x)
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ const ids = {
|
||||||
arrow3: createShapeId('arrow3'),
|
arrow3: createShapeId('arrow3'),
|
||||||
}
|
}
|
||||||
|
|
||||||
const arrow = () => editor.onlySelectedShape as TLArrowShape
|
const arrow = () => editor.getOnlySelectedShape() as TLArrowShape
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
editor = new TestEditor()
|
editor = new TestEditor()
|
||||||
|
|
|
@ -10,12 +10,12 @@ beforeEach(() => {
|
||||||
it('Sets shape meta by default to an empty object', () => {
|
it('Sets shape meta by default to an empty object', () => {
|
||||||
const id = createShapeId()
|
const id = createShapeId()
|
||||||
editor.createShapes([{ id, type: 'geo' }]).select(id)
|
editor.createShapes([{ id, type: 'geo' }]).select(id)
|
||||||
expect(editor.onlySelectedShape!.meta).toStrictEqual({})
|
expect(editor.getOnlySelectedShape()!.meta).toStrictEqual({})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Sets shape meta', () => {
|
it('Sets shape meta', () => {
|
||||||
editor.getInitialMetaForShape = (shape) => ({ firstThreeCharactersOfId: shape.id.slice(0, 3) })
|
editor.getInitialMetaForShape = (shape) => ({ firstThreeCharactersOfId: shape.id.slice(0, 3) })
|
||||||
const id = createShapeId()
|
const id = createShapeId()
|
||||||
editor.createShapes([{ id, type: 'geo' }]).select(id)
|
editor.createShapes([{ id, type: 'geo' }]).select(id)
|
||||||
expect(editor.onlySelectedShape!.meta).toStrictEqual({ firstThreeCharactersOfId: 'sha' })
|
expect(editor.getOnlySelectedShape()!.meta).toStrictEqual({ firstThreeCharactersOfId: 'sha' })
|
||||||
})
|
})
|
||||||
|
|
|
@ -189,9 +189,9 @@ describe('arrows', () => {
|
||||||
editor.pointerMove(255, 255)
|
editor.pointerMove(255, 255)
|
||||||
editor.pointerMove(450, 450)
|
editor.pointerMove(450, 450)
|
||||||
editor.pointerUp(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
|
// exiting at the bottom right corner of the first box
|
||||||
x: 300,
|
x: 300,
|
||||||
y: 300,
|
y: 300,
|
||||||
|
@ -224,7 +224,7 @@ describe('arrows', () => {
|
||||||
.pointerMove(255, 255)
|
.pointerMove(255, 255)
|
||||||
.pointerMove(450, 450)
|
.pointerMove(450, 450)
|
||||||
.pointerUp(450, 450)
|
.pointerUp(450, 450)
|
||||||
const arrow = editor.onlySelectedShape!
|
const arrow = editor.getOnlySelectedShape()!
|
||||||
|
|
||||||
expect(editor.getArrowsBoundTo(firstBox.id).length).toBe(1)
|
expect(editor.getArrowsBoundTo(firstBox.id).length).toBe(1)
|
||||||
expect(editor.getArrowsBoundTo(secondBox.id).length).toBe(1)
|
expect(editor.getArrowsBoundTo(secondBox.id).length).toBe(1)
|
||||||
|
|
|
@ -50,7 +50,7 @@ describe('when multiple shapes are selected', () => {
|
||||||
it('stretches horizontally and preserves aspect ratio', () => {
|
it('stretches horizontally and preserves aspect ratio', () => {
|
||||||
const videoA = createVideoShape()
|
const videoA = createVideoShape()
|
||||||
editor.selectAll()
|
editor.selectAll()
|
||||||
expect(editor.selectedShapes.length).toBe(4)
|
expect(editor.getSelectedShapes().length).toBe(4)
|
||||||
editor.stretchShapes(editor.getSelectedShapeIds(), 'horizontal')
|
editor.stretchShapes(editor.getSelectedShapeIds(), 'horizontal')
|
||||||
jest.advanceTimersByTime(1000)
|
jest.advanceTimersByTime(1000)
|
||||||
const newHeight = (500 * 9) / 16
|
const newHeight = (500 * 9) / 16
|
||||||
|
@ -76,7 +76,7 @@ describe('when multiple shapes are selected', () => {
|
||||||
it('stretches vertically and preserves aspect ratio', () => {
|
it('stretches vertically and preserves aspect ratio', () => {
|
||||||
const videoA = createVideoShape()
|
const videoA = createVideoShape()
|
||||||
editor.selectAll()
|
editor.selectAll()
|
||||||
expect(editor.selectedShapes.length).toBe(4)
|
expect(editor.getSelectedShapes().length).toBe(4)
|
||||||
editor.stretchShapes(editor.getSelectedShapeIds(), 'vertical')
|
editor.stretchShapes(editor.getSelectedShapeIds(), 'vertical')
|
||||||
jest.advanceTimersByTime(1000)
|
jest.advanceTimersByTime(1000)
|
||||||
const newWidth = (500 * 16) / 9
|
const newWidth = (500 * 16) / 9
|
||||||
|
|
|
@ -297,7 +297,7 @@ describe('When one shape is selected', () => {
|
||||||
|
|
||||||
editor.selectAll()
|
editor.selectAll()
|
||||||
editor.groupShapes(editor.getSelectedShapeIds()) // this will also select the new group
|
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.on('change', fn)
|
||||||
editor.flipShapes(editor.getSelectedShapeIds(), 'horizontal')
|
editor.flipShapes(editor.getSelectedShapeIds(), 'horizontal')
|
||||||
|
|
||||||
|
|
|
@ -20,8 +20,8 @@ describe('creating frames', () => {
|
||||||
it('can be done', () => {
|
it('can be done', () => {
|
||||||
editor.setCurrentTool('frame')
|
editor.setCurrentTool('frame')
|
||||||
editor.pointerDown(100, 100).pointerMove(200, 200).pointerUp(200, 200)
|
editor.pointerDown(100, 100).pointerMove(200, 200).pointerUp(200, 200)
|
||||||
expect(editor.onlySelectedShape?.type).toBe('frame')
|
expect(editor.getOnlySelectedShape()?.type).toBe('frame')
|
||||||
expect(editor.getShapePageBounds(editor.onlySelectedShape!)).toMatchObject({
|
expect(editor.getShapePageBounds(editor.getOnlySelectedShape()!)).toMatchObject({
|
||||||
x: 100,
|
x: 100,
|
||||||
y: 100,
|
y: 100,
|
||||||
w: 100,
|
w: 100,
|
||||||
|
@ -31,9 +31,9 @@ describe('creating frames', () => {
|
||||||
it('will create with a default size if no dragging happens', () => {
|
it('will create with a default size if no dragging happens', () => {
|
||||||
editor.setCurrentTool('frame')
|
editor.setCurrentTool('frame')
|
||||||
editor.pointerDown(100, 100).pointerUp(100, 100)
|
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()
|
const { w, h } = editor.getShapeUtil<TLFrameShape>('frame').getDefaultProps()
|
||||||
expect(editor.getShapePageBounds(editor.onlySelectedShape!)).toMatchObject({
|
expect(editor.getShapePageBounds(editor.getOnlySelectedShape()!)).toMatchObject({
|
||||||
x: 100 - w / 2,
|
x: 100 - w / 2,
|
||||||
y: 100 - h / 2,
|
y: 100 - h / 2,
|
||||||
w,
|
w,
|
||||||
|
@ -43,7 +43,7 @@ describe('creating frames', () => {
|
||||||
it('can be canceled while pointing', () => {
|
it('can be canceled while pointing', () => {
|
||||||
editor.setCurrentTool('frame')
|
editor.setCurrentTool('frame')
|
||||||
editor.pointerDown(100, 100).cancel().pointerUp(100, 100)
|
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)
|
expect(editor.currentPageShapes).toHaveLength(0)
|
||||||
})
|
})
|
||||||
it('can be canceled while dragging', () => {
|
it('can be canceled while dragging', () => {
|
||||||
|
@ -52,35 +52,35 @@ describe('creating frames', () => {
|
||||||
editor.expectPathToBe('root.select.resizing')
|
editor.expectPathToBe('root.select.resizing')
|
||||||
editor.cancel()
|
editor.cancel()
|
||||||
editor.pointerUp()
|
editor.pointerUp()
|
||||||
expect(editor.onlySelectedShape?.type).toBe(undefined)
|
expect(editor.getOnlySelectedShape()?.type).toBe(undefined)
|
||||||
expect(editor.currentPageShapes).toHaveLength(0)
|
expect(editor.currentPageShapes).toHaveLength(0)
|
||||||
})
|
})
|
||||||
it('can be undone', () => {
|
it('can be undone', () => {
|
||||||
editor.setCurrentTool('frame')
|
editor.setCurrentTool('frame')
|
||||||
editor.pointerDown(100, 100).pointerMove(200, 200).pointerUp(200, 200)
|
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)
|
expect(editor.currentPageShapes).toHaveLength(1)
|
||||||
|
|
||||||
editor.undo()
|
editor.undo()
|
||||||
|
|
||||||
expect(editor.onlySelectedShape?.type).toBe(undefined)
|
expect(editor.getOnlySelectedShape()?.type).toBe(undefined)
|
||||||
expect(editor.currentPageShapes).toHaveLength(0)
|
expect(editor.currentPageShapes).toHaveLength(0)
|
||||||
})
|
})
|
||||||
it('can be done inside other frames', () => {
|
it('can be done inside other frames', () => {
|
||||||
editor.setCurrentTool('frame')
|
editor.setCurrentTool('frame')
|
||||||
editor.pointerDown(100, 100).pointerMove(200, 200).pointerUp(200, 200)
|
editor.pointerDown(100, 100).pointerMove(200, 200).pointerUp(200, 200)
|
||||||
|
|
||||||
const frameAId = editor.onlySelectedShape!.id
|
const frameAId = editor.getOnlySelectedShape()!.id
|
||||||
|
|
||||||
editor.setCurrentTool('frame')
|
editor.setCurrentTool('frame')
|
||||||
editor.pointerDown(125, 125).pointerMove(175, 175).pointerUp(175, 175)
|
editor.pointerDown(125, 125).pointerMove(175, 175).pointerUp(175, 175)
|
||||||
|
|
||||||
expect(editor.currentPageShapes).toHaveLength(2)
|
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,
|
x: 125,
|
||||||
y: 125,
|
y: 125,
|
||||||
w: 50,
|
w: 50,
|
||||||
|
@ -91,7 +91,7 @@ describe('creating frames', () => {
|
||||||
editor.setCurrentTool('frame')
|
editor.setCurrentTool('frame')
|
||||||
editor.pointerDown(100, 100).pointerMove(200, 200).pointerUp(200, 200)
|
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)
|
editor.rotateSelection(Math.PI / 2)
|
||||||
|
|
||||||
|
@ -100,9 +100,9 @@ describe('creating frames', () => {
|
||||||
|
|
||||||
expect(editor.currentPageShapes).toHaveLength(2)
|
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,
|
x: 125,
|
||||||
y: 125,
|
y: 125,
|
||||||
w: 50,
|
w: 50,
|
||||||
|
@ -118,7 +118,7 @@ describe('creating frames', () => {
|
||||||
editor.setCurrentTool('frame')
|
editor.setCurrentTool('frame')
|
||||||
editor.pointerDown(100, 100).pointerMove(49, 149)
|
editor.pointerDown(100, 100).pointerMove(49, 149)
|
||||||
|
|
||||||
expect(editor.getShapePageBounds(editor.onlySelectedShape!)).toMatchObject({
|
expect(editor.getShapePageBounds(editor.getOnlySelectedShape()!)).toMatchObject({
|
||||||
x: 49,
|
x: 49,
|
||||||
y: 100,
|
y: 100,
|
||||||
w: 51,
|
w: 51,
|
||||||
|
@ -128,7 +128,7 @@ describe('creating frames', () => {
|
||||||
// x should snap
|
// x should snap
|
||||||
editor.keyDown('Control')
|
editor.keyDown('Control')
|
||||||
expect(editor.snaps.lines).toHaveLength(1)
|
expect(editor.snaps.lines).toHaveLength(1)
|
||||||
expect(editor.getShapePageBounds(editor.onlySelectedShape!)).toMatchObject({
|
expect(editor.getShapePageBounds(editor.getOnlySelectedShape()!)).toMatchObject({
|
||||||
x: 50,
|
x: 50,
|
||||||
y: 100,
|
y: 100,
|
||||||
w: 50,
|
w: 50,
|
||||||
|
@ -184,7 +184,7 @@ describe('frame shapes', () => {
|
||||||
editor.pointerDown(100, 100).pointerMove(200, 200).pointerUp(200, 200)
|
editor.pointerDown(100, 100).pointerMove(200, 200).pointerUp(200, 200)
|
||||||
|
|
||||||
editor.resizeSelection({ scaleX: 0.5, scaleY: 0.5 }, 'bottom_right')
|
editor.resizeSelection({ scaleX: 0.5, scaleY: 0.5 }, 'bottom_right')
|
||||||
expect(editor.getShapePageBounds(editor.onlySelectedShape!)).toCloselyMatchObject({
|
expect(editor.getShapePageBounds(editor.getOnlySelectedShape()!)).toCloselyMatchObject({
|
||||||
x: 100,
|
x: 100,
|
||||||
y: 100,
|
y: 100,
|
||||||
w: 50,
|
w: 50,
|
||||||
|
@ -198,7 +198,7 @@ describe('frame shapes', () => {
|
||||||
editor.setCurrentTool('frame')
|
editor.setCurrentTool('frame')
|
||||||
editor.pointerDown(100, 100).pointerMove(200, 200).pointerUp(200, 200)
|
editor.pointerDown(100, 100).pointerMove(200, 200).pointerUp(200, 200)
|
||||||
editor.resizeSelection({ scaleX: 0.5, scaleY: 0.5 }, 'bottom_right', { altKey: true })
|
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,
|
x: 125,
|
||||||
y: 125,
|
y: 125,
|
||||||
w: 50,
|
w: 50,
|
||||||
|
@ -210,12 +210,12 @@ describe('frame shapes', () => {
|
||||||
editor.setCurrentTool('frame')
|
editor.setCurrentTool('frame')
|
||||||
editor.pointerDown(100, 100).pointerMove(200, 200).pointerUp(200, 200)
|
editor.pointerDown(100, 100).pointerMove(200, 200).pointerUp(200, 200)
|
||||||
|
|
||||||
const frameId = editor.onlySelectedShape!.id
|
const frameId = editor.getOnlySelectedShape()!.id
|
||||||
|
|
||||||
editor.setCurrentTool('geo')
|
editor.setCurrentTool('geo')
|
||||||
editor.pointerDown(125, 125).pointerMove(175, 175).pointerUp(175, 175)
|
editor.pointerDown(125, 125).pointerMove(175, 175).pointerUp(175, 175)
|
||||||
|
|
||||||
const boxId = editor.onlySelectedShape!.id
|
const boxId = editor.getOnlySelectedShape()!.id
|
||||||
|
|
||||||
editor.select(frameId)
|
editor.select(frameId)
|
||||||
|
|
||||||
|
@ -239,42 +239,42 @@ describe('frame shapes', () => {
|
||||||
editor.setCurrentTool('frame')
|
editor.setCurrentTool('frame')
|
||||||
editor.pointerDown(100, 100).pointerMove(200, 200).pointerUp(200, 200)
|
editor.pointerDown(100, 100).pointerMove(200, 200).pointerUp(200, 200)
|
||||||
|
|
||||||
const frameId = editor.onlySelectedShape!.id
|
const frameId = editor.getOnlySelectedShape()!.id
|
||||||
|
|
||||||
editor.createShapes([
|
editor.createShapes([
|
||||||
{ type: 'geo', id: ids.boxA, x: 250, y: 250, props: { w: 50, h: 50, fill: 'solid' } },
|
{ 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.setCurrentTool('select')
|
||||||
editor.pointerDown(275, 275).pointerMove(150, 150)
|
editor.pointerDown(275, 275).pointerMove(150, 150)
|
||||||
|
|
||||||
jest.advanceTimersByTime(300)
|
jest.advanceTimersByTime(300)
|
||||||
|
|
||||||
expect(editor.onlySelectedShape!.id).toBe(ids.boxA)
|
expect(editor.getOnlySelectedShape()!.id).toBe(ids.boxA)
|
||||||
expect(editor.onlySelectedShape!.parentId).toBe(frameId)
|
expect(editor.getOnlySelectedShape()!.parentId).toBe(frameId)
|
||||||
|
|
||||||
editor.pointerMove(275, 275)
|
editor.pointerMove(275, 275)
|
||||||
jest.advanceTimersByTime(250)
|
jest.advanceTimersByTime(250)
|
||||||
|
|
||||||
expect(editor.onlySelectedShape!.parentId).toBe(editor.currentPageId)
|
expect(editor.getOnlySelectedShape()!.parentId).toBe(editor.currentPageId)
|
||||||
|
|
||||||
editor.pointerMove(150, 150)
|
editor.pointerMove(150, 150)
|
||||||
jest.advanceTimersByTime(250)
|
jest.advanceTimersByTime(250)
|
||||||
|
|
||||||
expect(editor.onlySelectedShape!.parentId).toBe(frameId)
|
expect(editor.getOnlySelectedShape()!.parentId).toBe(frameId)
|
||||||
|
|
||||||
editor.pointerUp(150, 150)
|
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', () => {
|
it('can have shapes dragged on top and dropped before the timeout fires', () => {
|
||||||
editor.setCurrentTool('frame')
|
editor.setCurrentTool('frame')
|
||||||
editor.pointerDown(100, 100).pointerMove(200, 200).pointerUp(200, 200)
|
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
|
// Create a new shape off of the frame
|
||||||
editor.createShapes([
|
editor.createShapes([
|
||||||
|
@ -282,37 +282,37 @@ describe('frame shapes', () => {
|
||||||
])
|
])
|
||||||
|
|
||||||
// It should be a child of the page
|
// 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
|
// Drag the shape on top of the frame
|
||||||
editor.setCurrentTool('select')
|
editor.setCurrentTool('select')
|
||||||
editor.pointerDown(275, 275, ids.boxA).pointerMove(150, 150)
|
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
|
// 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
|
// On pointer up, the shape should be dropped into the frame
|
||||||
editor.pointerUp()
|
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', () => {
|
it('does not reparent shapes that are being dragged from within the frame', () => {
|
||||||
editor.setCurrentTool('frame')
|
editor.setCurrentTool('frame')
|
||||||
editor.pointerDown(100, 100).pointerMove(200, 200).pointerUp(200, 200)
|
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
|
// create a box within the frame
|
||||||
editor.setCurrentTool('geo')
|
editor.setCurrentTool('geo')
|
||||||
editor.pointerDown(125, 125).pointerMove(175, 175).pointerUp(175, 175)
|
editor.pointerDown(125, 125).pointerMove(175, 175).pointerUp(175, 175)
|
||||||
expect(editor.onlySelectedShape!.parentId).toBe(frameId)
|
expect(editor.getOnlySelectedShape()!.parentId).toBe(frameId)
|
||||||
const boxAid = editor.onlySelectedShape!.id
|
const boxAid = editor.getOnlySelectedShape()!.id
|
||||||
|
|
||||||
// create another box within the frame
|
// create another box within the frame
|
||||||
editor.setCurrentTool('geo')
|
editor.setCurrentTool('geo')
|
||||||
editor.pointerDown(130, 130).pointerMove(180, 180).pointerUp(180, 180)
|
editor.pointerDown(130, 130).pointerMove(180, 180).pointerUp(180, 180)
|
||||||
expect(editor.onlySelectedShape!.parentId).toBe(frameId)
|
expect(editor.getOnlySelectedShape()!.parentId).toBe(frameId)
|
||||||
const boxBid = editor.onlySelectedShape!.id
|
const boxBid = editor.getOnlySelectedShape()!.id
|
||||||
|
|
||||||
// dragging box A around should not cause the index to change or the frame to be highlighted
|
// 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)
|
jest.advanceTimersByTime(2500)
|
||||||
|
|
||||||
expect(editor.onlySelectedShape!.id).toBe(boxAid)
|
expect(editor.getOnlySelectedShape()!.id).toBe(boxAid)
|
||||||
expect(editor.onlySelectedShape!.parentId).toBe(frameId)
|
expect(editor.getOnlySelectedShape()!.parentId).toBe(frameId)
|
||||||
expect(editor.hintingShapeIds).toHaveLength(0)
|
expect(editor.hintingShapeIds).toHaveLength(0)
|
||||||
// box A should still be beneath box B
|
// box A should still be beneath box B
|
||||||
expect(editor.getShape(boxAid)!.index.localeCompare(editor.getShape(boxBid)!.index)).toBe(-1)
|
expect(editor.getShape(boxAid)!.index.localeCompare(editor.getShape(boxBid)!.index)).toBe(-1)
|
||||||
|
@ -355,12 +355,12 @@ describe('frame shapes', () => {
|
||||||
|
|
||||||
editor.setCurrentTool('geo')
|
editor.setCurrentTool('geo')
|
||||||
editor.pointerDown(125, 125).pointerMove(175, 175).pointerUp(175, 175)
|
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
|
// make a shape outside the frame
|
||||||
editor.setCurrentTool('geo')
|
editor.setCurrentTool('geo')
|
||||||
editor.pointerDown(275, 125).pointerMove(280, 130).pointerUp(280, 130)
|
editor.pointerDown(275, 125).pointerMove(280, 130).pointerUp(280, 130)
|
||||||
expect(editor.getShapePageBounds(editor.onlySelectedShape!)).toMatchObject({
|
expect(editor.getShapePageBounds(editor.getOnlySelectedShape()!)).toMatchObject({
|
||||||
x: 275,
|
x: 275,
|
||||||
y: 125,
|
y: 125,
|
||||||
w: 5,
|
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
|
// 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.setCurrentTool('select')
|
||||||
editor
|
editor
|
||||||
.pointerDown(277.5, 127.5, editor.onlySelectedShape!.id)
|
.pointerDown(277.5, 127.5, editor.getOnlySelectedShape()!.id)
|
||||||
.pointerMove(287.5, 126.5)
|
.pointerMove(287.5, 126.5)
|
||||||
.pointerMove(277.5, 126.5)
|
.pointerMove(277.5, 126.5)
|
||||||
|
|
||||||
// now try to snap
|
// now try to snap
|
||||||
editor.keyDown('Control')
|
editor.keyDown('Control')
|
||||||
expect(editor.getShapePageBounds(editor.onlySelectedShape!)).toMatchObject({
|
expect(editor.getShapePageBounds(editor.getOnlySelectedShape()!)).toMatchObject({
|
||||||
x: 275,
|
x: 275,
|
||||||
y: 124,
|
y: 124,
|
||||||
w: 5,
|
w: 5,
|
||||||
|
@ -388,7 +388,7 @@ describe('frame shapes', () => {
|
||||||
|
|
||||||
editor.pointerMove(287.5, 126.5).pointerMove(277.5, 126.5)
|
editor.pointerMove(287.5, 126.5).pointerMove(277.5, 126.5)
|
||||||
expect(editor.snaps.lines).toHaveLength(1)
|
expect(editor.snaps.lines).toHaveLength(1)
|
||||||
expect(editor.getShapePageBounds(editor.onlySelectedShape!)).toMatchObject({
|
expect(editor.getShapePageBounds(editor.getOnlySelectedShape()!)).toMatchObject({
|
||||||
x: 275,
|
x: 275,
|
||||||
y: 125,
|
y: 125,
|
||||||
w: 5,
|
w: 5,
|
||||||
|
@ -399,17 +399,17 @@ describe('frame shapes', () => {
|
||||||
it('children of a frame will not snap to shapes outside the frame', () => {
|
it('children of a frame will not snap to shapes outside the frame', () => {
|
||||||
editor.setCurrentTool('frame')
|
editor.setCurrentTool('frame')
|
||||||
editor.pointerDown(100, 100).pointerMove(200, 200).pointerUp(200, 200)
|
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
|
// make a shape inside the frame
|
||||||
editor.setCurrentTool('geo')
|
editor.setCurrentTool('geo')
|
||||||
editor.pointerDown(125, 125).pointerMove(175, 175).pointerUp(175, 175)
|
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
|
// make a shape outside the frame
|
||||||
editor.setCurrentTool('geo')
|
editor.setCurrentTool('geo')
|
||||||
editor.pointerDown(275, 125).pointerMove(280, 130).pointerUp(280, 130)
|
editor.pointerDown(275, 125).pointerMove(280, 130).pointerUp(280, 130)
|
||||||
const outerBoxId = editor.onlySelectedShape!.id
|
const outerBoxId = editor.getOnlySelectedShape()!.id
|
||||||
|
|
||||||
editor.setCurrentTool('select')
|
editor.setCurrentTool('select')
|
||||||
editor.pointerDown(150, 150, innerBoxId).pointerMove(150, 50).pointerMove(150, 148)
|
editor.pointerDown(150, 150, innerBoxId).pointerMove(150, 50).pointerMove(150, 148)
|
||||||
|
@ -429,7 +429,7 @@ describe('frame shapes', () => {
|
||||||
editor.setCurrentTool('geo')
|
editor.setCurrentTool('geo')
|
||||||
editor.pointerDown(150, 150).pointerMove(250, 250).pointerUp(250, 250)
|
editor.pointerDown(150, 150).pointerMove(250, 250).pointerUp(250, 250)
|
||||||
|
|
||||||
expect(editor.getShapePageBounds(editor.onlySelectedShape!)).toMatchObject({
|
expect(editor.getShapePageBounds(editor.getOnlySelectedShape()!)).toMatchObject({
|
||||||
x: 150,
|
x: 150,
|
||||||
y: 150,
|
y: 150,
|
||||||
w: 100,
|
w: 100,
|
||||||
|
@ -437,13 +437,13 @@ describe('frame shapes', () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
// mask should be a 50px box around the top left corner
|
// 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)"`
|
`"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', () => {
|
it('masks its nested children', () => {
|
||||||
|
@ -453,12 +453,12 @@ describe('frame shapes', () => {
|
||||||
editor.setCurrentTool('frame')
|
editor.setCurrentTool('frame')
|
||||||
editor.pointerDown(150, 150).pointerMove(250, 250).pointerUp(250, 250)
|
editor.pointerDown(150, 150).pointerMove(250, 250).pointerUp(250, 250)
|
||||||
|
|
||||||
const innerFrameId = editor.onlySelectedShape!.id
|
const innerFrameId = editor.getOnlySelectedShape()!.id
|
||||||
|
|
||||||
editor.setCurrentTool('geo')
|
editor.setCurrentTool('geo')
|
||||||
editor.pointerDown(100, 100).pointerMove(250, 250).pointerUp(250, 250)
|
editor.pointerDown(100, 100).pointerMove(250, 250).pointerUp(250, 250)
|
||||||
|
|
||||||
const boxId = editor.onlySelectedShape!.id
|
const boxId = editor.getOnlySelectedShape()!.id
|
||||||
|
|
||||||
editor.reparentShapes([boxId], innerFrameId)
|
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', () => {
|
it('arrows started within the frame will bind to it and have the page as their parent', () => {
|
||||||
editor.setCurrentTool('frame')
|
editor.setCurrentTool('frame')
|
||||||
editor.pointerDown(100, 100).pointerMove(200, 200).pointerUp(200, 200)
|
editor.pointerDown(100, 100).pointerMove(200, 200).pointerUp(200, 200)
|
||||||
const frameId = editor.onlySelectedShape!.id
|
const frameId = editor.getOnlySelectedShape()!.id
|
||||||
|
|
||||||
editor.setCurrentTool('arrow')
|
editor.setCurrentTool('arrow')
|
||||||
editor.pointerDown(150, 150).pointerMove(250, 250).pointerUp(250, 250)
|
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.start).toMatchObject({ boundShapeId: frameId })
|
||||||
expect(arrow.props.end).toMatchObject({ type: 'point' })
|
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 ', () => {
|
it('arrows started within the frame can bind to a shape within the frame ', () => {
|
||||||
editor.setCurrentTool('frame')
|
editor.setCurrentTool('frame')
|
||||||
editor.pointerDown(100, 100).pointerMove(200, 200).pointerUp(200, 200)
|
editor.pointerDown(100, 100).pointerMove(200, 200).pointerUp(200, 200)
|
||||||
const frameId = editor.onlySelectedShape!.id
|
const frameId = editor.getOnlySelectedShape()!.id
|
||||||
|
|
||||||
editor.setCurrentTool('geo')
|
editor.setCurrentTool('geo')
|
||||||
editor
|
editor
|
||||||
|
@ -496,12 +496,12 @@ describe('frame shapes', () => {
|
||||||
.pointerUp(175, 175)
|
.pointerUp(175, 175)
|
||||||
.setStyleForSelectedShapes(DefaultFillStyle, 'solid')
|
.setStyleForSelectedShapes(DefaultFillStyle, 'solid')
|
||||||
.setStyleForNextShapes(DefaultFillStyle, 'solid')
|
.setStyleForNextShapes(DefaultFillStyle, 'solid')
|
||||||
const boxId = editor.onlySelectedShape!.id
|
const boxId = editor.getOnlySelectedShape()!.id
|
||||||
|
|
||||||
editor.setCurrentTool('arrow')
|
editor.setCurrentTool('arrow')
|
||||||
editor.pointerDown(150, 150).pointerMove(190, 190).pointerUp(190, 190)
|
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.start).toMatchObject({ boundShapeId: boxId })
|
||||||
expect(arrow.props.end).toMatchObject({ boundShapeId: frameId })
|
expect(arrow.props.end).toMatchObject({ boundShapeId: frameId })
|
||||||
|
@ -513,7 +513,7 @@ describe('frame shapes', () => {
|
||||||
editor.setCurrentTool('frame')
|
editor.setCurrentTool('frame')
|
||||||
editor.pointerDown(100, 100).pointerMove(200, 200).pointerUp(200, 200)
|
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.getSelectedShapeIds()[0]).toBe(frameId)
|
||||||
expect(editor.getCurrentPageState().editingShapeId).toBe(null)
|
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', () => {
|
it('can be selected with box brushing only if the whole frame is selected', () => {
|
||||||
editor.setCurrentTool('frame')
|
editor.setCurrentTool('frame')
|
||||||
editor.pointerDown(100, 100).pointerMove(200, 200).pointerUp(200, 200)
|
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
|
// select from outside the frame
|
||||||
editor.setCurrentTool('select')
|
editor.setCurrentTool('select')
|
||||||
|
@ -541,7 +541,7 @@ describe('frame shapes', () => {
|
||||||
editor.pointerMove(250, 250)
|
editor.pointerMove(250, 250)
|
||||||
|
|
||||||
expect(editor.getSelectedShapeIds()).toHaveLength(1)
|
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', () => {
|
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', () => {
|
it('children of a frame will not be selected from outside of the frame', () => {
|
||||||
editor.setCurrentTool('frame')
|
editor.setCurrentTool('frame')
|
||||||
editor.pointerDown(100, 100).pointerMove(200, 200).pointerUp(200, 200)
|
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
|
// make a shape inside the frame that extends out of the frame
|
||||||
editor.setCurrentTool('geo')
|
editor.setCurrentTool('geo')
|
||||||
editor.pointerDown(150, 150).pointerMove(400, 400).pointerUp(400, 400)
|
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
|
// select from outside the frame via box brushing
|
||||||
editor.setCurrentTool('select')
|
editor.setCurrentTool('select')
|
||||||
|
@ -581,7 +581,7 @@ describe('frame shapes', () => {
|
||||||
|
|
||||||
// Check if the inner box was selected
|
// Check if the inner box was selected
|
||||||
expect(editor.getSelectedShapeIds()).toHaveLength(1)
|
expect(editor.getSelectedShapeIds()).toHaveLength(1)
|
||||||
expect(editor.onlySelectedShape!.id).toBe(innerBoxId)
|
expect(editor.getOnlySelectedShape()!.id).toBe(innerBoxId)
|
||||||
|
|
||||||
// Deselect everything
|
// Deselect everything
|
||||||
editor.deselect()
|
editor.deselect()
|
||||||
|
@ -600,7 +600,7 @@ describe('frame shapes', () => {
|
||||||
it('arrows will not bind to parts of shapes outside the frame', () => {
|
it('arrows will not bind to parts of shapes outside the frame', () => {
|
||||||
editor.setCurrentTool('frame')
|
editor.setCurrentTool('frame')
|
||||||
editor.pointerDown(100, 100).pointerMove(200, 200).pointerUp(200, 200)
|
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
|
// make a shape inside the frame that extends out of the frame
|
||||||
editor.setCurrentTool('geo')
|
editor.setCurrentTool('geo')
|
||||||
|
@ -610,14 +610,14 @@ describe('frame shapes', () => {
|
||||||
.pointerUp(400, 400)
|
.pointerUp(400, 400)
|
||||||
.setStyleForSelectedShapes(DefaultFillStyle, 'solid')
|
.setStyleForSelectedShapes(DefaultFillStyle, 'solid')
|
||||||
.setStyleForNextShapes(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
|
// Make an arrow that binds to the inner box's bottom right corner
|
||||||
editor.setCurrentTool('arrow')
|
editor.setCurrentTool('arrow')
|
||||||
editor.pointerDown(500, 500).pointerMove(375, 375)
|
editor.pointerDown(500, 500).pointerMove(375, 375)
|
||||||
|
|
||||||
// Check if the arrow's handles remain points
|
// 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({
|
expect(arrow.props.start).toMatchObject({
|
||||||
type: 'point',
|
type: 'point',
|
||||||
x: 0,
|
x: 0,
|
||||||
|
@ -633,7 +633,7 @@ describe('frame shapes', () => {
|
||||||
editor.pointerMove(175, 175).pointerUp(175, 175)
|
editor.pointerMove(175, 175).pointerUp(175, 175)
|
||||||
|
|
||||||
// Check if arrow's end handle is bound to the inner box
|
// 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 })
|
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.setCurrentTool('frame')
|
||||||
editor.pointerDown(100, 100).pointerMove(200, 200).pointerUp(200, 200)
|
editor.pointerDown(100, 100).pointerMove(200, 200).pointerUp(200, 200)
|
||||||
const frameId = editor.onlySelectedShape!.id
|
const frameId = editor.getOnlySelectedShape()!.id
|
||||||
|
|
||||||
editor.setCurrentTool('geo')
|
editor.setCurrentTool('geo')
|
||||||
editor
|
editor
|
||||||
|
@ -664,7 +664,7 @@ test('arrows bound to a shape within a group within a frame are reparented if th
|
||||||
.pointerUp(120, 120)
|
.pointerUp(120, 120)
|
||||||
.setStyleForSelectedShapes(DefaultFillStyle, 'solid')
|
.setStyleForSelectedShapes(DefaultFillStyle, 'solid')
|
||||||
.setStyleForNextShapes(DefaultFillStyle, 'solid')
|
.setStyleForNextShapes(DefaultFillStyle, 'solid')
|
||||||
const boxAId = editor.onlySelectedShape!.id
|
const boxAId = editor.getOnlySelectedShape()!.id
|
||||||
|
|
||||||
editor.setCurrentTool('geo')
|
editor.setCurrentTool('geo')
|
||||||
editor
|
editor
|
||||||
|
@ -673,7 +673,7 @@ test('arrows bound to a shape within a group within a frame are reparented if th
|
||||||
.pointerUp(190, 120)
|
.pointerUp(190, 120)
|
||||||
.setStyleForSelectedShapes(DefaultFillStyle, 'solid')
|
.setStyleForSelectedShapes(DefaultFillStyle, 'solid')
|
||||||
.setStyleForNextShapes(DefaultFillStyle, 'solid')
|
.setStyleForNextShapes(DefaultFillStyle, 'solid')
|
||||||
const boxBId = editor.onlySelectedShape!.id
|
const boxBId = editor.getOnlySelectedShape()!.id
|
||||||
|
|
||||||
editor.setCurrentTool('geo')
|
editor.setCurrentTool('geo')
|
||||||
editor
|
editor
|
||||||
|
@ -682,16 +682,16 @@ test('arrows bound to a shape within a group within a frame are reparented if th
|
||||||
.pointerUp(170, 170)
|
.pointerUp(170, 170)
|
||||||
.setStyleForSelectedShapes(DefaultFillStyle, 'solid')
|
.setStyleForSelectedShapes(DefaultFillStyle, 'solid')
|
||||||
.setStyleForNextShapes(DefaultFillStyle, 'solid')
|
.setStyleForNextShapes(DefaultFillStyle, 'solid')
|
||||||
const boxCId = editor.onlySelectedShape!.id
|
const boxCId = editor.getOnlySelectedShape()!.id
|
||||||
|
|
||||||
editor.setCurrentTool('select')
|
editor.setCurrentTool('select')
|
||||||
editor.select(boxBId, boxCId)
|
editor.select(boxBId, boxCId)
|
||||||
editor.groupShapes(editor.getSelectedShapeIds())
|
editor.groupShapes(editor.getSelectedShapeIds())
|
||||||
const groupId = editor.onlySelectedShape!.id
|
const groupId = editor.getOnlySelectedShape()!.id
|
||||||
|
|
||||||
editor.setCurrentTool('arrow')
|
editor.setCurrentTool('arrow')
|
||||||
editor.pointerDown(115, 115).pointerMove(185, 115).pointerUp(185, 115)
|
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(boxAId)).toHaveLength(1)
|
||||||
expect(editor.getArrowsBoundTo(boxBId)).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()
|
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)
|
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()
|
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)
|
expect(editor.focusedGroupId).toBe(ids.group1)
|
||||||
|
|
||||||
editor
|
editor
|
||||||
|
|
|
@ -475,9 +475,9 @@ describe('When pasting into frames...', () => {
|
||||||
editor.setCamera({ x: -editor.viewportScreenBounds.w, y: -editor.viewportScreenBounds.h, z: 1 })
|
editor.setCamera({ x: -editor.viewportScreenBounds.w, y: -editor.viewportScreenBounds.h, z: 1 })
|
||||||
// paste the box
|
// paste the box
|
||||||
editor.paste()
|
editor.paste()
|
||||||
const boxId = editor.onlySelectedShape!.id
|
const boxId = editor.getOnlySelectedShape()!.id
|
||||||
// it should be a child of the frame
|
// 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
|
// it should have pageBounds of 10x10 because it is not rotated relative to the viewport
|
||||||
expect(editor.getShapePageBounds(boxId)).toMatchObject({ w: 10, h: 10 })
|
expect(editor.getShapePageBounds(boxId)).toMatchObject({ w: 10, h: 10 })
|
||||||
// it should be in the middle of the frame
|
// it should be in the middle of the frame
|
||||||
|
|
|
@ -15,7 +15,7 @@ describe(SelectTool, () => {
|
||||||
describe('pointer down while shape is being edited', () => {
|
describe('pointer down while shape is being edited', () => {
|
||||||
it('captures the pointer down event if it is on the shape', () => {
|
it('captures the pointer down event if it is on the shape', () => {
|
||||||
editor.setCurrentTool('geo').pointerDown(0, 0).pointerMove(100, 100).pointerUp(100, 100)
|
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._transformPointerDownSpy.mockRestore()
|
||||||
editor._transformPointerUpSpy.mockRestore()
|
editor._transformPointerUpSpy.mockRestore()
|
||||||
editor.setCurrentTool('select')
|
editor.setCurrentTool('select')
|
||||||
|
@ -42,7 +42,7 @@ describe(SelectTool, () => {
|
||||||
})
|
})
|
||||||
it('does not allow pressing undo to end up in the editing state', () => {
|
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)
|
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._transformPointerDownSpy.mockRestore()
|
||||||
editor._transformPointerUpSpy.mockRestore()
|
editor._transformPointerUpSpy.mockRestore()
|
||||||
editor.setCurrentTool('select')
|
editor.setCurrentTool('select')
|
||||||
|
|
|
@ -75,9 +75,9 @@ describe('Hovering shapes', () => {
|
||||||
editor.pointerMove(50, 50)
|
editor.pointerMove(50, 50)
|
||||||
editor.pointerDown()
|
editor.pointerDown()
|
||||||
expect(editor.isIn('select.pointing_shape')).toBe(true)
|
expect(editor.isIn('select.pointing_shape')).toBe(true)
|
||||||
expect(editor.selectedShapes.length).toBe(1)
|
expect(editor.getSelectedShapes().length).toBe(1)
|
||||||
editor.pointerUp()
|
editor.pointerUp()
|
||||||
expect(editor.selectedShapes.length).toBe(1)
|
expect(editor.getSelectedShapes().length).toBe(1)
|
||||||
expect(editor.isIn('select.idle')).toBe(true)
|
expect(editor.isIn('select.idle')).toBe(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -85,10 +85,10 @@ describe('Hovering shapes', () => {
|
||||||
editor.pointerMove(50, 50)
|
editor.pointerMove(50, 50)
|
||||||
editor.pointerDown()
|
editor.pointerDown()
|
||||||
expect(editor.isIn('select.pointing_canvas')).toBe(true)
|
expect(editor.isIn('select.pointing_canvas')).toBe(true)
|
||||||
expect(editor.selectedShapes.length).toBe(0)
|
expect(editor.getSelectedShapes().length).toBe(0)
|
||||||
editor.pointerUp()
|
editor.pointerUp()
|
||||||
expect(editor.isIn('select.idle')).toBe(true)
|
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', () => {
|
it('hovers the margins or inside of filled shapes', () => {
|
||||||
|
|
|
@ -182,7 +182,7 @@ describe('When cloning...', () => {
|
||||||
// Start cloning!
|
// Start cloning!
|
||||||
editor.keyDown('Alt')
|
editor.keyDown('Alt')
|
||||||
expect(editor.currentPageShapeIds.size).toBe(4)
|
expect(editor.currentPageShapeIds.size).toBe(4)
|
||||||
const newShape = editor.selectedShapes[0]
|
const newShape = editor.getSelectedShapes()[0]
|
||||||
expect(newShape.id).not.toBe(ids.box1)
|
expect(newShape.id).not.toBe(ids.box1)
|
||||||
|
|
||||||
editor
|
editor
|
||||||
|
@ -1792,19 +1792,19 @@ it('clones a single shape simply', () => {
|
||||||
.pointerMove(50, 50)
|
.pointerMove(50, 50)
|
||||||
.click()
|
.click()
|
||||||
|
|
||||||
expect(editor.onlySelectedShape).toBe(editor.currentPageShapes[0])
|
expect(editor.getOnlySelectedShape()).toBe(editor.currentPageShapes[0])
|
||||||
expect(editor.hoveredShape).toBe(editor.currentPageShapes[0])
|
expect(editor.hoveredShape).toBe(editor.currentPageShapes[0])
|
||||||
|
|
||||||
// click on the canvas to deselect
|
// click on the canvas to deselect
|
||||||
editor.pointerMove(200, 50).click()
|
editor.pointerMove(200, 50).click()
|
||||||
|
|
||||||
expect(editor.onlySelectedShape).toBe(null)
|
expect(editor.getOnlySelectedShape()).toBe(null)
|
||||||
expect(editor.hoveredShape).toBe(undefined)
|
expect(editor.hoveredShape).toBe(undefined)
|
||||||
|
|
||||||
// move back over the the shape
|
// move back over the the shape
|
||||||
editor.pointerMove(50, 50)
|
editor.pointerMove(50, 50)
|
||||||
|
|
||||||
expect(editor.onlySelectedShape).toBe(null)
|
expect(editor.getOnlySelectedShape()).toBe(null)
|
||||||
expect(editor.hoveredShape).toBe(editor.currentPageShapes[0])
|
expect(editor.hoveredShape).toBe(editor.currentPageShapes[0])
|
||||||
|
|
||||||
// start dragging the shape
|
// start dragging the shape
|
||||||
|
@ -1818,7 +1818,7 @@ it('clones a single shape simply', () => {
|
||||||
|
|
||||||
expect(editor.currentPageShapes).toHaveLength(2)
|
expect(editor.currentPageShapes).toHaveLength(2)
|
||||||
const [, sticky2] = editor.currentPageShapes
|
const [, sticky2] = editor.currentPageShapes
|
||||||
expect(editor.onlySelectedShape).toBe(sticky2)
|
expect(editor.getOnlySelectedShape()).toBe(sticky2)
|
||||||
expect(editor.editingShape).toBe(undefined)
|
expect(editor.editingShape).toBe(undefined)
|
||||||
expect(editor.hoveredShape).toBe(sticky2)
|
expect(editor.hoveredShape).toBe(sticky2)
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in a new issue