No impure getters pt2 (#2202)

follow up to #2189
This commit is contained in:
David Sheldrick 2023-11-13 12:42:07 +00:00 committed by GitHub
parent ae5c60ab36
commit 2ca2f81f2a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
86 changed files with 930 additions and 743 deletions

View file

@ -88,7 +88,7 @@ const InsideOfEditorContext = () => {
let i = 0 let i = 0
const interval = setInterval(() => { const interval = setInterval(() => {
const selection = [...editor.selectedShapeIds] const selection = [...editor.getSelectedShapeIds()]
editor.selectAll() editor.selectAll()
editor.setStyleForSelectedShapes(DefaultColorStyle, i % 2 ? 'blue' : 'light-blue') editor.setStyleForSelectedShapes(DefaultColorStyle, i % 2 ? 'blue' : 'light-blue')
editor.setStyleForNextShapes(DefaultColorStyle, i % 2 ? 'blue' : 'light-blue') editor.setStyleForNextShapes(DefaultColorStyle, i % 2 ? 'blue' : 'light-blue')

View file

@ -22,7 +22,7 @@ const CustomUi = track(() => {
switch (e.key) { switch (e.key) {
case 'Delete': case 'Delete':
case 'Backspace': { case 'Backspace': {
editor.deleteShapes(editor.selectedShapeIds) editor.deleteShapes(editor.getSelectedShapeIds())
break break
} }
case 'v': { case 'v': {

View file

@ -52,9 +52,9 @@ class IdleState extends StateNode {
} }
case 'shape': { case 'shape': {
if (editor.inputs.shiftKey) { if (editor.inputs.shiftKey) {
editor.select(...editor.selectedShapeIds, info.shape.id) editor.select(...editor.getSelectedShapeIds(), info.shape.id)
} else { } else {
if (!editor.selectedShapeIds.includes(info.shape.id)) { if (!editor.getSelectedShapeIds().includes(info.shape.id)) {
editor.select(info.shape.id) editor.select(info.shape.id)
} }
this.parent.transition('pointing', info) this.parent.transition('pointing', info)

View file

@ -18,7 +18,7 @@ export default function OnlyEditorExample() {
onMount={(editor: Editor) => { onMount={(editor: Editor) => {
editor editor
.selectAll() .selectAll()
.deleteShapes(editor.selectedShapeIds) .deleteShapes(editor.getSelectedShapeIds())
.createShapes([ .createShapes([
{ {
id: createShapeId(), id: createShapeId(),

View file

@ -618,6 +618,7 @@ export class Editor extends EventEmitter<TLEventMap> {
get currentPageShapeIds(): Set<TLShapeId>; get currentPageShapeIds(): Set<TLShapeId>;
get currentPageShapes(): TLShape[]; get currentPageShapes(): TLShape[];
get currentPageShapesSorted(): TLShape[]; get currentPageShapesSorted(): TLShape[];
// @deprecated (undocumented)
get currentPageState(): TLInstancePageState; get currentPageState(): TLInstancePageState;
// @deprecated (undocumented) // @deprecated (undocumented)
get currentTool(): StateNode | undefined; get currentTool(): StateNode | undefined;
@ -679,6 +680,7 @@ export class Editor extends EventEmitter<TLEventMap> {
getCanUndo(): boolean; getCanUndo(): boolean;
getContainer: () => HTMLElement; getContainer: () => HTMLElement;
getContentFromCurrentPage(shapes: TLShape[] | TLShapeId[]): TLContent | undefined; getContentFromCurrentPage(shapes: TLShape[] | TLShapeId[]): TLContent | undefined;
getCurrentPageState(): TLInstancePageState;
getCurrentTool(): StateNode | undefined; getCurrentTool(): StateNode | undefined;
getCurrentToolId(): string; getCurrentToolId(): string;
getDocumentSettings(): TLDocument; getDocumentSettings(): TLDocument;
@ -686,13 +688,16 @@ export class Editor extends EventEmitter<TLEventMap> {
getHighestIndexForParent(parent: TLPage | TLParentId | TLShape): string; getHighestIndexForParent(parent: TLPage | TLParentId | TLShape): string;
getInitialMetaForShape(_shape: TLShape): JsonObject; getInitialMetaForShape(_shape: TLShape): JsonObject;
getInstanceState(): TLInstance; getInstanceState(): TLInstance;
getIsMenuOpen(): boolean;
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;
getPageShapeIds(page: TLPage | TLPageId): Set<TLShapeId>; getPageShapeIds(page: TLPage | TLPageId): Set<TLShapeId>;
getPageStates(): TLInstancePageState[];
getPointInParentSpace(shape: TLShape | TLShapeId, point: VecLike): Vec2d; getPointInParentSpace(shape: TLShape | TLShapeId, point: VecLike): Vec2d;
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[];
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>;
@ -770,6 +775,7 @@ export class Editor extends EventEmitter<TLEventMap> {
isAncestorSelected(shape: TLShape | TLShapeId): boolean; isAncestorSelected(shape: TLShape | TLShapeId): boolean;
isIn(path: string): boolean; isIn(path: string): boolean;
isInAny(...paths: string[]): boolean; isInAny(...paths: string[]): boolean;
// @deprecated (undocumented)
get isMenuOpen(): boolean; get isMenuOpen(): boolean;
isPointInShape(shape: TLShape | TLShapeId, point: VecLike, opts?: { isPointInShape(shape: TLShape | TLShapeId, point: VecLike, opts?: {
margin?: number | undefined; margin?: number | undefined;
@ -790,6 +796,7 @@ export class Editor extends EventEmitter<TLEventMap> {
get openMenus(): string[]; get openMenus(): string[];
packShapes(shapes: TLShape[] | TLShapeId[], gap: number): this; packShapes(shapes: TLShape[] | TLShapeId[], gap: number): this;
get pages(): TLPage[]; get pages(): TLPage[];
// @deprecated (undocumented)
get pageStates(): TLInstancePageState[]; get pageStates(): TLInstancePageState[];
pageToScreen(point: VecLike): { pageToScreen(point: VecLike): {
x: number; x: number;
@ -840,6 +847,7 @@ export class Editor extends EventEmitter<TLEventMap> {
readonly scribbles: ScribbleManager; readonly scribbles: ScribbleManager;
select(...shapes: TLShape[] | TLShapeId[]): this; select(...shapes: TLShape[] | TLShapeId[]): this;
selectAll(): this; selectAll(): this;
// @deprecated (undocumented)
get selectedShapeIds(): TLShapeId[]; get selectedShapeIds(): TLShapeId[];
get selectedShapes(): TLShape[]; get selectedShapes(): TLShape[];
get selectionPageBounds(): Box2d | null; get selectionPageBounds(): Box2d | null;

View file

@ -6749,7 +6749,7 @@
{ {
"kind": "Method", "kind": "Method",
"canonicalReference": "@tldraw/editor!Editor#alignShapes:member(1)", "canonicalReference": "@tldraw/editor!Editor#alignShapes:member(1)",
"docComment": "/**\n * Align shape positions.\n *\n * @param shapes - The shapes (or shape ids) to align.\n *\n * @param operation - The align operation to apply.\n *\n * @example\n * ```ts\n * editor.alignShapes([box1, box2], 'left')\n * editor.alignShapes(editor.selectedShapeIds, 'left')\n * ```\n *\n * @public\n */\n", "docComment": "/**\n * Align shape positions.\n *\n * @param shapes - The shapes (or shape ids) to align.\n *\n * @param operation - The align operation to apply.\n *\n * @example\n * ```ts\n * editor.alignShapes([box1, box2], 'left')\n * editor.alignShapes(editor.getSelectedShapeIds(), 'left')\n * ```\n *\n * @public\n */\n",
"excerptTokens": [ "excerptTokens": [
{ {
"kind": "Content", "kind": "Content",
@ -7229,7 +7229,7 @@
{ {
"kind": "Method", "kind": "Method",
"canonicalReference": "@tldraw/editor!Editor#batch:member(1)", "canonicalReference": "@tldraw/editor!Editor#batch:member(1)",
"docComment": "/**\n * Run a function in a batch, which will be undone/redone as a single action.\n *\n * @example\n * ```ts\n * editor.batch(() => {\n * \teditor.selectAll()\n * \teditor.deleteShapes(editor.selectedShapeIds)\n * \teditor.createShapes(myShapes)\n * \teditor.selectNone()\n * })\n *\n * editor.undo() // will undo all of the above\n * ```\n *\n * @public\n */\n", "docComment": "/**\n * Run a function in a batch, which will be undone/redone as a single action.\n *\n * @example\n * ```ts\n * editor.batch(() => {\n * \teditor.selectAll()\n * \teditor.deleteShapes(editor.getSelectedShapeIds())\n * \teditor.createShapes(myShapes)\n * \teditor.selectNone()\n * })\n *\n * editor.undo() // will undo all of the above\n * ```\n *\n * @public\n */\n",
"excerptTokens": [ "excerptTokens": [
{ {
"kind": "Content", "kind": "Content",
@ -8249,7 +8249,7 @@
{ {
"kind": "Property", "kind": "Property",
"canonicalReference": "@tldraw/editor!Editor#currentPageState:member", "canonicalReference": "@tldraw/editor!Editor#currentPageState:member",
"docComment": "/**\n * The current page state.\n *\n * @public\n */\n", "docComment": "/**\n * @deprecated\n *\n * Use `getCurrentPageState` instead.\n */\n",
"excerptTokens": [ "excerptTokens": [
{ {
"kind": "Content", "kind": "Content",
@ -8884,7 +8884,7 @@
{ {
"kind": "Method", "kind": "Method",
"canonicalReference": "@tldraw/editor!Editor#distributeShapes:member(1)", "canonicalReference": "@tldraw/editor!Editor#distributeShapes:member(1)",
"docComment": "/**\n * Distribute shape positions.\n *\n * @param shapes - The shapes (or shape ids) to distribute.\n *\n * @param operation - Whether to distribute shapes horizontally or vertically.\n *\n * @example\n * ```ts\n * editor.distributeShapes([box1, box2], 'horizontal')\n * editor.distributeShapes(editor.selectedShapeIds, 'horizontal')\n * ```\n *\n * @public\n */\n", "docComment": "/**\n * Distribute shape positions.\n *\n * @param shapes - The shapes (or shape ids) to distribute.\n *\n * @param operation - Whether to distribute shapes horizontally or vertically.\n *\n * @example\n * ```ts\n * editor.distributeShapes([box1, box2], 'horizontal')\n * editor.distributeShapes(editor.getSelectedShapeIds(), 'horizontal')\n * ```\n *\n * @public\n */\n",
"excerptTokens": [ "excerptTokens": [
{ {
"kind": "Content", "kind": "Content",
@ -9507,7 +9507,7 @@
{ {
"kind": "Method", "kind": "Method",
"canonicalReference": "@tldraw/editor!Editor#flipShapes:member(1)", "canonicalReference": "@tldraw/editor!Editor#flipShapes:member(1)",
"docComment": "/**\n * Flip shape positions.\n *\n * @param shapes - The ids of the shapes to flip.\n *\n * @param operation - Whether to flip horizontally or vertically.\n *\n * @example\n * ```ts\n * editor.flipShapes([box1, box2], 'horizontal', 32)\n * editor.flipShapes(editor.selectedShapeIds, 'horizontal', 32)\n * ```\n *\n * @public\n */\n", "docComment": "/**\n * Flip shape positions.\n *\n * @param shapes - The ids of the shapes to flip.\n *\n * @param operation - Whether to flip horizontally or vertically.\n *\n * @example\n * ```ts\n * editor.flipShapes([box1, box2], 'horizontal', 32)\n * editor.flipShapes(editor.getSelectedShapeIds(), 'horizontal', 32)\n * ```\n *\n * @public\n */\n",
"excerptTokens": [ "excerptTokens": [
{ {
"kind": "Content", "kind": "Content",
@ -10131,6 +10131,38 @@
"isAbstract": false, "isAbstract": false,
"name": "getContentFromCurrentPage" "name": "getContentFromCurrentPage"
}, },
{
"kind": "Method",
"canonicalReference": "@tldraw/editor!Editor#getCurrentPageState:member(1)",
"docComment": "/**\n * The current page state.\n *\n * @public\n */\n",
"excerptTokens": [
{
"kind": "Content",
"text": "getCurrentPageState(): "
},
{
"kind": "Reference",
"text": "TLInstancePageState",
"canonicalReference": "@tldraw/tlschema!TLInstancePageState:interface"
},
{
"kind": "Content",
"text": ";"
}
],
"isStatic": false,
"returnTypeTokenRange": {
"startIndex": 1,
"endIndex": 2
},
"releaseTag": "Public",
"isProtected": false,
"overloadIndex": 1,
"parameters": [],
"isOptional": false,
"isAbstract": false,
"name": "getCurrentPageState"
},
{ {
"kind": "Method", "kind": "Method",
"canonicalReference": "@tldraw/editor!Editor#getCurrentTool:member(1)", "canonicalReference": "@tldraw/editor!Editor#getCurrentTool:member(1)",
@ -10454,6 +10486,37 @@
"isAbstract": false, "isAbstract": false,
"name": "getInstanceState" "name": "getInstanceState"
}, },
{
"kind": "Method",
"canonicalReference": "@tldraw/editor!Editor#getIsMenuOpen:member(1)",
"docComment": "/**\n * Get whether any menus are open.\n *\n * @example\n * ```ts\n * editor.isMenuOpen()\n * ```\n *\n * @public\n */\n",
"excerptTokens": [
{
"kind": "Content",
"text": "getIsMenuOpen(): "
},
{
"kind": "Content",
"text": "boolean"
},
{
"kind": "Content",
"text": ";"
}
],
"isStatic": false,
"returnTypeTokenRange": {
"startIndex": 1,
"endIndex": 2
},
"releaseTag": "Public",
"isProtected": false,
"overloadIndex": 1,
"parameters": [],
"isOptional": false,
"isAbstract": false,
"name": "getIsMenuOpen"
},
{ {
"kind": "Method", "kind": "Method",
"canonicalReference": "@tldraw/editor!Editor#getOpenMenus:member(1)", "canonicalReference": "@tldraw/editor!Editor#getOpenMenus:member(1)",
@ -10704,6 +10767,42 @@
"isAbstract": false, "isAbstract": false,
"name": "getPageShapeIds" "name": "getPageShapeIds"
}, },
{
"kind": "Method",
"canonicalReference": "@tldraw/editor!Editor#getPageStates:member(1)",
"docComment": "/**\n * Page states.\n *\n * @public\n */\n",
"excerptTokens": [
{
"kind": "Content",
"text": "getPageStates(): "
},
{
"kind": "Reference",
"text": "TLInstancePageState",
"canonicalReference": "@tldraw/tlschema!TLInstancePageState:interface"
},
{
"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": "getPageStates"
},
{ {
"kind": "Method", "kind": "Method",
"canonicalReference": "@tldraw/editor!Editor#getPointInParentSpace:member(1)", "canonicalReference": "@tldraw/editor!Editor#getPointInParentSpace:member(1)",
@ -10910,6 +11009,42 @@
"isAbstract": false, "isAbstract": false,
"name": "getSelectedShapeAtPoint" "name": "getSelectedShapeAtPoint"
}, },
{
"kind": "Method",
"canonicalReference": "@tldraw/editor!Editor#getSelectedShapeIds:member(1)",
"docComment": "/**\n * The current selected ids.\n *\n * @public\n */\n",
"excerptTokens": [
{
"kind": "Content",
"text": "getSelectedShapeIds(): "
},
{
"kind": "Reference",
"text": "TLShapeId",
"canonicalReference": "@tldraw/tlschema!TLShapeId: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": "getSelectedShapeIds"
},
{ {
"kind": "Method", "kind": "Method",
"canonicalReference": "@tldraw/editor!Editor#getShape:member(1)", "canonicalReference": "@tldraw/editor!Editor#getShape:member(1)",
@ -13253,7 +13388,7 @@
{ {
"kind": "Property", "kind": "Property",
"canonicalReference": "@tldraw/editor!Editor#isMenuOpen:member", "canonicalReference": "@tldraw/editor!Editor#isMenuOpen:member",
"docComment": "/**\n * Get whether any menus are open.\n *\n * @example\n * ```ts\n * editor.isMenuOpen()\n * ```\n *\n * @public\n */\n", "docComment": "/**\n * @deprecated\n *\n * Use `getIsMenuOpen` instead.\n */\n",
"excerptTokens": [ "excerptTokens": [
{ {
"kind": "Content", "kind": "Content",
@ -14055,7 +14190,7 @@
{ {
"kind": "Method", "kind": "Method",
"canonicalReference": "@tldraw/editor!Editor#packShapes:member(1)", "canonicalReference": "@tldraw/editor!Editor#packShapes:member(1)",
"docComment": "/**\n * Pack shapes into a grid centered on their current position. Based on potpack (https://github.com/mapbox/potpack).\n *\n * @param shapes - The shapes (or shape ids) to pack.\n *\n * @param gap - The padding to apply to the packed shapes. Defaults to 16.\n *\n * @example\n * ```ts\n * editor.packShapes([box1, box2], 32)\n * editor.packShapes(editor.selectedShapeIds, 32)\n * ```\n *\n */\n", "docComment": "/**\n * Pack shapes into a grid centered on their current position. Based on potpack (https://github.com/mapbox/potpack).\n *\n * @param shapes - The shapes (or shape ids) to pack.\n *\n * @param gap - The padding to apply to the packed shapes. Defaults to 16.\n *\n * @example\n * ```ts\n * editor.packShapes([box1, box2], 32)\n * editor.packShapes(editor.getSelectedShapeIds(), 32)\n * ```\n *\n */\n",
"excerptTokens": [ "excerptTokens": [
{ {
"kind": "Content", "kind": "Content",
@ -14168,7 +14303,7 @@
{ {
"kind": "Property", "kind": "Property",
"canonicalReference": "@tldraw/editor!Editor#pageStates:member", "canonicalReference": "@tldraw/editor!Editor#pageStates:member",
"docComment": "/**\n * Page states.\n *\n * @public\n */\n", "docComment": "/**\n * @deprecated\n *\n * Use `getPageStates` instead.\n */\n",
"excerptTokens": [ "excerptTokens": [
{ {
"kind": "Content", "kind": "Content",
@ -15354,7 +15489,7 @@
{ {
"kind": "Method", "kind": "Method",
"canonicalReference": "@tldraw/editor!Editor#rotateShapesBy:member(1)", "canonicalReference": "@tldraw/editor!Editor#rotateShapesBy:member(1)",
"docComment": "/**\n * Rotate shapes by a delta in radians. Note: Currently, this assumes that the shapes are your currently selected shapes.\n *\n * @param shapes - The shapes (or shape ids) of the shapes to move.\n *\n * @param delta - The delta in radians to apply to the selection rotation.\n *\n * @example\n * ```ts\n * editor.rotateShapesBy(editor.selectedShapeIds, Math.PI)\n * editor.rotateShapesBy(editor.selectedShapeIds, Math.PI / 2)\n * ```\n *\n */\n", "docComment": "/**\n * Rotate shapes by a delta in radians. Note: Currently, this assumes that the shapes are your currently selected shapes.\n *\n * @param shapes - The shapes (or shape ids) of the shapes to move.\n *\n * @param delta - The delta in radians to apply to the selection rotation.\n *\n * @example\n * ```ts\n * editor.rotateShapesBy(editor.getSelectedShapeIds(), Math.PI)\n * editor.rotateShapesBy(editor.getSelectedShapeIds(), Math.PI / 2)\n * ```\n *\n */\n",
"excerptTokens": [ "excerptTokens": [
{ {
"kind": "Content", "kind": "Content",
@ -15605,7 +15740,7 @@
{ {
"kind": "Property", "kind": "Property",
"canonicalReference": "@tldraw/editor!Editor#selectedShapeIds:member", "canonicalReference": "@tldraw/editor!Editor#selectedShapeIds:member",
"docComment": "/**\n * The current selected ids.\n *\n * @public\n */\n", "docComment": "/**\n * @deprecated\n *\n * Use `getSelectedShapeIds` instead.\n */\n",
"excerptTokens": [ "excerptTokens": [
{ {
"kind": "Content", "kind": "Content",
@ -17199,7 +17334,7 @@
{ {
"kind": "Method", "kind": "Method",
"canonicalReference": "@tldraw/editor!Editor#stackShapes:member(1)", "canonicalReference": "@tldraw/editor!Editor#stackShapes:member(1)",
"docComment": "/**\n * Stack shape.\n *\n * @param shapes - The shapes (or shape ids) to stack.\n *\n * @param operation - Whether to stack horizontally or vertically.\n *\n * @param gap - The gap to leave between shapes.\n *\n * @example\n * ```ts\n * editor.stackShapes([box1, box2], 'horizontal', 32)\n * editor.stackShapes(editor.selectedShapeIds, 'horizontal', 32)\n * ```\n *\n * @public\n */\n", "docComment": "/**\n * Stack shape.\n *\n * @param shapes - The shapes (or shape ids) to stack.\n *\n * @param operation - Whether to stack horizontally or vertically.\n *\n * @param gap - The gap to leave between shapes.\n *\n * @example\n * ```ts\n * editor.stackShapes([box1, box2], 'horizontal', 32)\n * editor.stackShapes(editor.getSelectedShapeIds(), 'horizontal', 32)\n * ```\n *\n * @public\n */\n",
"excerptTokens": [ "excerptTokens": [
{ {
"kind": "Content", "kind": "Content",
@ -17434,7 +17569,7 @@
{ {
"kind": "Method", "kind": "Method",
"canonicalReference": "@tldraw/editor!Editor#stretchShapes:member(1)", "canonicalReference": "@tldraw/editor!Editor#stretchShapes:member(1)",
"docComment": "/**\n * Stretch shape sizes and positions to fill their common bounding box.\n *\n * @param shapes - The shapes (or shape ids) to stretch.\n *\n * @param operation - Whether to stretch shapes horizontally or vertically.\n *\n * @example\n * ```ts\n * editor.stretchShapes([box1, box2], 'horizontal')\n * editor.stretchShapes(editor.selectedShapeIds, 'horizontal')\n * ```\n *\n * @public\n */\n", "docComment": "/**\n * Stretch shape sizes and positions to fill their common bounding box.\n *\n * @param shapes - The shapes (or shape ids) to stretch.\n *\n * @param operation - Whether to stretch shapes horizontally or vertically.\n *\n * @example\n * ```ts\n * editor.stretchShapes([box1, box2], 'horizontal')\n * editor.stretchShapes(editor.getSelectedShapeIds(), 'horizontal')\n * ```\n *\n * @public\n */\n",
"excerptTokens": [ "excerptTokens": [
{ {
"kind": "Content", "kind": "Content",

View file

@ -335,7 +335,7 @@ function SelectedIdIndicators() {
const editor = useEditor() const editor = useEditor()
const selectedShapeIds = useValue( const selectedShapeIds = useValue(
'selectedShapeIds', 'selectedShapeIds',
() => editor.currentPageState.selectedShapeIds, () => editor.getCurrentPageState().selectedShapeIds,
[editor] [editor]
) )
const shouldDisplay = useValue( const shouldDisplay = useValue(
@ -381,7 +381,7 @@ const HoveredShapeIndicator = function HoveredShapeIndicator() {
() => editor.getInstanceState().isHoveringCanvas, () => editor.getInstanceState().isHoveringCanvas,
[editor] [editor]
) )
const hoveredShapeId = useValue('hovered id', () => editor.currentPageState.hoveredShapeId, [ const hoveredShapeId = useValue('hovered id', () => editor.getCurrentPageState().hoveredShapeId, [
editor, editor,
]) ])

View file

@ -464,7 +464,7 @@ export class Editor extends EventEmitter<TLEventMap> {
} }
const deletedIds = new Set([record.id]) const deletedIds = new Set([record.id])
const updates = compact( const updates = compact(
this.pageStates.map((pageState) => { this.getPageStates().map((pageState) => {
return cleanupInstancePageState(pageState, deletedIds) return cleanupInstancePageState(pageState, deletedIds)
}) })
) )
@ -514,7 +514,7 @@ export class Editor extends EventEmitter<TLEventMap> {
allMovingIds.add(id) allMovingIds.add(id)
}) })
for (const instancePageState of this.pageStates) { for (const instancePageState of this.getPageStates()) {
if (instancePageState.pageId === next.parentId) continue if (instancePageState.pageId === next.parentId) continue
const nextPageState = cleanupInstancePageState(instancePageState, allMovingIds) const nextPageState = cleanupInstancePageState(instancePageState, allMovingIds)
@ -892,7 +892,7 @@ export class Editor extends EventEmitter<TLEventMap> {
* ```ts * ```ts
* editor.batch(() => { * editor.batch(() => {
* editor.selectAll() * editor.selectAll()
* editor.deleteShapes(editor.selectedShapeIds) * editor.deleteShapes(editor.getSelectedShapeIds())
* editor.createShapes(myShapes) * editor.createShapes(myShapes)
* editor.selectNone() * editor.selectNone()
* }) * })
@ -1345,10 +1345,17 @@ export class Editor extends EventEmitter<TLEventMap> {
* *
* @public * @public
*/ */
@computed get isMenuOpen(): boolean { @computed getIsMenuOpen(): boolean {
return this.getOpenMenus().length > 0 return this.getOpenMenus().length > 0
} }
/**
* @deprecated Use `getIsMenuOpen` instead.
*/
get isMenuOpen() {
return this.getIsMenuOpen()
}
/* --------------------- Cursor --------------------- */ /* --------------------- Cursor --------------------- */
/** /**
@ -1374,11 +1381,19 @@ export class Editor extends EventEmitter<TLEventMap> {
* *
* @public * @public
*/ */
@computed get pageStates(): TLInstancePageState[] { @computed getPageStates(): TLInstancePageState[] {
return this._pageStates.get() return this._getPageStatesQuery().get()
} }
/**
* @deprecated Use `getPageStates` instead.
*/
get pageStates() {
return this.getPageStates()
}
/** @internal */ /** @internal */
@computed private get _pageStates() { @computed private _getPageStatesQuery() {
return this.store.query.records('instance_page_state') return this.store.query.records('instance_page_state')
} }
@ -1387,11 +1402,19 @@ export class Editor extends EventEmitter<TLEventMap> {
* *
* @public * @public
*/ */
@computed get currentPageState(): TLInstancePageState { @computed getCurrentPageState(): TLInstancePageState {
return this.store.get(this._currentPageStateId)! return this.store.get(this._getCurrentPageStateId())!
} }
/**
* @deprecated Use `getCurrentPageState` instead.
*/
get currentPageState() {
return this.getCurrentPageState()
}
/** @internal */ /** @internal */
@computed private get _currentPageStateId() { @computed private _getCurrentPageStateId() {
return InstancePageStateRecordType.createId(this.currentPageId) return InstancePageStateRecordType.createId(this.currentPageId)
} }
@ -1426,7 +1449,7 @@ export class Editor extends EventEmitter<TLEventMap> {
partial: Partial<Omit<TLInstancePageState, 'selectedShapeIds'>>, partial: Partial<Omit<TLInstancePageState, 'selectedShapeIds'>>,
historyOptions?: TLCommandHistoryOptions historyOptions?: TLCommandHistoryOptions
) => { ) => {
const prev = this.store.get(partial.id ?? this.currentPageState.id)! const prev = this.store.get(partial.id ?? this.getCurrentPageState().id)!
return { data: { prev, partial }, ...historyOptions } return { data: { prev, partial }, ...historyOptions }
}, },
{ {
@ -1444,8 +1467,15 @@ export class Editor extends EventEmitter<TLEventMap> {
* *
* @public * @public
*/ */
@computed get selectedShapeIds() { @computed getSelectedShapeIds() {
return this.currentPageState.selectedShapeIds return this.getCurrentPageState().selectedShapeIds
}
/**
* @deprecated Use `getSelectedShapeIds` instead.
*/
get selectedShapeIds() {
return this.getSelectedShapeIds()
} }
/** /**
@ -1460,7 +1490,7 @@ export class Editor extends EventEmitter<TLEventMap> {
* @readonly * @readonly
*/ */
@computed get selectedShapes(): TLShape[] { @computed get selectedShapes(): TLShape[] {
const { selectedShapeIds } = this.currentPageState const { selectedShapeIds } = this.getCurrentPageState()
return compact(selectedShapeIds.map((id) => this.store.get(id))) return compact(selectedShapeIds.map((id) => this.store.get(id)))
} }
@ -1491,7 +1521,7 @@ export class Editor extends EventEmitter<TLEventMap> {
private _setSelectedShapes = this.history.createCommand( private _setSelectedShapes = this.history.createCommand(
'setSelectedShapes', 'setSelectedShapes',
(ids: TLShapeId[], historyOptions?: TLCommandHistoryOptions) => { (ids: TLShapeId[], historyOptions?: TLCommandHistoryOptions) => {
const { selectedShapeIds: prevSelectedShapeIds } = this.currentPageState const { selectedShapeIds: prevSelectedShapeIds } = this.getCurrentPageState()
const prevSet = new Set(prevSelectedShapeIds) const prevSet = new Set(prevSelectedShapeIds)
if (ids.length === prevSet.size && ids.every((id) => prevSet.has(id))) return null if (ids.length === prevSet.size && ids.every((id) => prevSet.has(id))) return null
@ -1504,12 +1534,12 @@ export class Editor extends EventEmitter<TLEventMap> {
}, },
{ {
do: ({ selectedShapeIds }) => { do: ({ selectedShapeIds }) => {
this.store.put([{ ...this.currentPageState, selectedShapeIds }]) this.store.put([{ ...this.getCurrentPageState(), selectedShapeIds }])
}, },
undo: ({ prevSelectedShapeIds }) => { undo: ({ prevSelectedShapeIds }) => {
this.store.put([ this.store.put([
{ {
...this.currentPageState, ...this.getCurrentPageState(),
selectedShapeIds: prevSelectedShapeIds, selectedShapeIds: prevSelectedShapeIds,
}, },
]) ])
@ -1534,7 +1564,7 @@ export class Editor extends EventEmitter<TLEventMap> {
const id = typeof shape === 'string' ? shape : shape?.id ?? null const id = typeof shape === 'string' ? shape : shape?.id ?? null
const _shape = this.getShape(id) const _shape = this.getShape(id)
if (!_shape) return false if (!_shape) return false
const { selectedShapeIds } = this const selectedShapeIds = this.getSelectedShapeIds()
return !!this.findShapeAncestor(_shape, (parent) => selectedShapeIds.includes(parent.id)) return !!this.findShapeAncestor(_shape, (parent) => selectedShapeIds.includes(parent.id))
} }
@ -1575,7 +1605,7 @@ export class Editor extends EventEmitter<TLEventMap> {
typeof shapes[0] === 'string' typeof shapes[0] === 'string'
? (shapes as TLShapeId[]) ? (shapes as TLShapeId[])
: (shapes as TLShape[]).map((shape) => shape.id) : (shapes as TLShape[]).map((shape) => shape.id)
const { selectedShapeIds } = this const selectedShapeIds = this.getSelectedShapeIds()
if (selectedShapeIds.length > 0 && ids.length > 0) { if (selectedShapeIds.length > 0 && ids.length > 0) {
this.setSelectedShapes(selectedShapeIds.filter((id) => !ids.includes(id))) this.setSelectedShapes(selectedShapeIds.filter((id) => !ids.includes(id)))
} }
@ -1612,7 +1642,7 @@ export class Editor extends EventEmitter<TLEventMap> {
* @public * @public
*/ */
selectNone(): this { selectNone(): this {
if (this.selectedShapeIds.length > 0) { if (this.getSelectedShapeIds().length > 0) {
this.setSelectedShapes([]) this.setSelectedShapes([])
} }
@ -1648,10 +1678,7 @@ export class Editor extends EventEmitter<TLEventMap> {
* @public * @public
*/ */
@computed get selectionPageBounds(): Box2d | null { @computed get selectionPageBounds(): Box2d | null {
const { const selectedShapeIds = this.getCurrentPageState().selectedShapeIds
currentPageState: { selectedShapeIds },
} = this
if (selectedShapeIds.length === 0) return null if (selectedShapeIds.length === 0) return null
return Box2d.Common(compact(selectedShapeIds.map((id) => this.getShapePageBounds(id)))) return Box2d.Common(compact(selectedShapeIds.map((id) => this.getShapePageBounds(id))))
@ -1664,12 +1691,12 @@ export class Editor extends EventEmitter<TLEventMap> {
* @public * @public
*/ */
@computed get selectionRotation(): number { @computed get selectionRotation(): number {
const { selectedShapeIds } = this const selectedShapeIds = this.getSelectedShapeIds()
if (selectedShapeIds.length === 0) { if (selectedShapeIds.length === 0) {
return 0 return 0
} }
if (selectedShapeIds.length === 1) { if (selectedShapeIds.length === 1) {
return this.getShapePageTransform(this.selectedShapeIds[0])!.rotation() return this.getShapePageTransform(this.getSelectedShapeIds()[0])!.rotation()
} }
const allRotations = selectedShapeIds.map((id) => this.getShapePageTransform(id)!.rotation()) const allRotations = selectedShapeIds.map((id) => this.getShapePageTransform(id)!.rotation())
@ -1687,7 +1714,7 @@ export class Editor extends EventEmitter<TLEventMap> {
* @public * @public
*/ */
@computed get selectionRotatedPageBounds(): Box2d | undefined { @computed get selectionRotatedPageBounds(): Box2d | undefined {
const { selectedShapeIds } = this const selectedShapeIds = this.getSelectedShapeIds()
if (selectedShapeIds.length === 0) { if (selectedShapeIds.length === 0) {
return undefined return undefined
@ -1707,7 +1734,7 @@ export class Editor extends EventEmitter<TLEventMap> {
// need to 'un-rotate' all the outlines of the existing nodes so we can fit them inside a box // need to 'un-rotate' all the outlines of the existing nodes so we can fit them inside a box
const boxFromRotatedVertices = Box2d.FromPoints( const boxFromRotatedVertices = Box2d.FromPoints(
this.selectedShapeIds this.getSelectedShapeIds()
.flatMap((id) => { .flatMap((id) => {
const pageTransform = this.getShapePageTransform(id) const pageTransform = this.getShapePageTransform(id)
if (!pageTransform) return [] if (!pageTransform) return []
@ -1728,7 +1755,7 @@ export class Editor extends EventEmitter<TLEventMap> {
* @public * @public
*/ */
@computed get focusedGroupId(): TLShapeId | TLPageId { @computed get focusedGroupId(): TLShapeId | TLPageId {
return this.currentPageState.focusedGroupId ?? this.currentPageId return this.getCurrentPageState().focusedGroupId ?? this.currentPageId
} }
/** /**
@ -1773,7 +1800,7 @@ export class Editor extends EventEmitter<TLEventMap> {
private _setFocusedGroupId = this.history.createCommand( private _setFocusedGroupId = this.history.createCommand(
'setFocusedGroupId', 'setFocusedGroupId',
(next: TLShapeId | null) => { (next: TLShapeId | null) => {
const prev = this.currentPageState.focusedGroupId const prev = this.getCurrentPageState().focusedGroupId
if (prev === next) return if (prev === next) return
return { return {
data: { data: {
@ -1786,10 +1813,10 @@ export class Editor extends EventEmitter<TLEventMap> {
}, },
{ {
do: ({ next }) => { do: ({ next }) => {
this.store.update(this.currentPageState.id, (s) => ({ ...s, focusedGroupId: next })) this.store.update(this.getCurrentPageState().id, (s) => ({ ...s, focusedGroupId: next }))
}, },
undo: ({ prev }) => { undo: ({ prev }) => {
this.store.update(this.currentPageState.id, (s) => ({ ...s, focusedGroupId: prev })) this.store.update(this.getCurrentPageState().id, (s) => ({ ...s, focusedGroupId: prev }))
}, },
squash({ prev }, { next }) { squash({ prev }, { next }) {
return { prev, next } return { prev, next }
@ -1828,7 +1855,7 @@ export class Editor extends EventEmitter<TLEventMap> {
* @public * @public
*/ */
@computed get editingShapeId() { @computed get editingShapeId() {
return this.currentPageState.editingShapeId return this.getCurrentPageState().editingShapeId
} }
/** /**
@ -1880,7 +1907,7 @@ export class Editor extends EventEmitter<TLEventMap> {
* @public * @public
*/ */
@computed get hoveredShapeId() { @computed get hoveredShapeId() {
return this.currentPageState.hoveredShapeId return this.getCurrentPageState().hoveredShapeId
} }
/** /**
@ -1921,7 +1948,7 @@ export class Editor extends EventEmitter<TLEventMap> {
* @public * @public
*/ */
@computed get hintingShapeIds() { @computed get hintingShapeIds() {
return this.currentPageState.hintingShapeIds return this.getCurrentPageState().hintingShapeIds
} }
/** /**
@ -1965,7 +1992,7 @@ export class Editor extends EventEmitter<TLEventMap> {
* @public * @public
*/ */
@computed get erasingShapeIds() { @computed get erasingShapeIds() {
return this.currentPageState.erasingShapeIds return this.getCurrentPageState().erasingShapeIds
} }
/** /**
@ -2024,7 +2051,7 @@ export class Editor extends EventEmitter<TLEventMap> {
* @public * @public
*/ */
get croppingShapeId() { get croppingShapeId() {
return this.currentPageState.croppingShapeId return this.getCurrentPageState().croppingShapeId
} }
/** /**
@ -3082,7 +3109,7 @@ export class Editor extends EventEmitter<TLEventMap> {
// We only really need these if we're using editor state, but that's ok // We only really need these if we're using editor state, but that's ok
const editingShapeId = this.editingShapeId const editingShapeId = this.editingShapeId
const selectedShapeIds = this.selectedShapeIds const selectedShapeIds = this.getSelectedShapeIds()
const erasingShapeIds = this.erasingShapeIds const erasingShapeIds = this.erasingShapeIds
const renderingBoundsExpanded = this.renderingBoundsExpanded const renderingBoundsExpanded = this.renderingBoundsExpanded
@ -3366,7 +3393,7 @@ export class Editor extends EventEmitter<TLEventMap> {
// in multiplayer contexts this page might have been deleted // in multiplayer contexts this page might have been deleted
return return
} }
if (!this.pageStates.find((p) => p.pageId === toId)) { if (!this.getPageStates().find((p) => p.pageId === toId)) {
const camera = CameraRecordType.create({ const camera = CameraRecordType.create({
id: CameraRecordType.createId(toId), id: CameraRecordType.createId(toId),
}) })
@ -3541,7 +3568,7 @@ export class Editor extends EventEmitter<TLEventMap> {
if (pages.length === 1) return null if (pages.length === 1) return null
const deletedPage = this.getPage(id) const deletedPage = this.getPage(id)
const deletedPageStates = this.pageStates.filter((s) => s.pageId === id) const deletedPageStates = this.getPageStates().filter((s) => s.pageId === id)
if (!deletedPage) return null if (!deletedPage) return null
@ -4268,7 +4295,7 @@ export class Editor extends EventEmitter<TLEventMap> {
* @returns The top-most selected shape at the given point, or undefined if there is no shape at the point. * @returns The top-most selected shape at the given point, or undefined if there is no shape at the point.
*/ */
getSelectedShapeAtPoint(point: VecLike): TLShape | undefined { getSelectedShapeAtPoint(point: VecLike): TLShape | undefined {
const { selectedShapeIds } = this const selectedShapeIds = this.getSelectedShapeIds()
return this.currentPageShapesSorted return this.currentPageShapesSorted
.filter((shape) => shape.type !== 'group' && selectedShapeIds.includes(shape.id)) .filter((shape) => shape.type !== 'group' && selectedShapeIds.includes(shape.id))
.reverse() // findlast .reverse() // findlast
@ -5083,8 +5110,8 @@ export class Editor extends EventEmitter<TLEventMap> {
* *
* @example * @example
* ```ts * ```ts
* editor.rotateShapesBy(editor.selectedShapeIds, Math.PI) * editor.rotateShapesBy(editor.getSelectedShapeIds(), Math.PI)
* editor.rotateShapesBy(editor.selectedShapeIds, Math.PI / 2) * editor.rotateShapesBy(editor.getSelectedShapeIds(), Math.PI / 2)
* ``` * ```
* *
* @param shapes - The shapes (or shape ids) of the shapes to move. * @param shapes - The shapes (or shape ids) of the shapes to move.
@ -5576,7 +5603,7 @@ export class Editor extends EventEmitter<TLEventMap> {
* @example * @example
* ```ts * ```ts
* editor.flipShapes([box1, box2], 'horizontal', 32) * editor.flipShapes([box1, box2], 'horizontal', 32)
* editor.flipShapes(editor.selectedShapeIds, 'horizontal', 32) * editor.flipShapes(editor.getSelectedShapeIds(), 'horizontal', 32)
* ``` * ```
* *
* @param shapes - The ids of the shapes to flip. * @param shapes - The ids of the shapes to flip.
@ -5641,7 +5668,7 @@ export class Editor extends EventEmitter<TLEventMap> {
* @example * @example
* ```ts * ```ts
* editor.stackShapes([box1, box2], 'horizontal', 32) * editor.stackShapes([box1, box2], 'horizontal', 32)
* editor.stackShapes(editor.selectedShapeIds, 'horizontal', 32) * editor.stackShapes(editor.getSelectedShapeIds(), 'horizontal', 32)
* ``` * ```
* *
* @param shapes - The shapes (or shape ids) to stack. * @param shapes - The shapes (or shape ids) to stack.
@ -5791,7 +5818,7 @@ export class Editor extends EventEmitter<TLEventMap> {
* @example * @example
* ```ts * ```ts
* editor.packShapes([box1, box2], 32) * editor.packShapes([box1, box2], 32)
* editor.packShapes(editor.selectedShapeIds, 32) * editor.packShapes(editor.getSelectedShapeIds(), 32)
* ``` * ```
* *
* *
@ -5950,7 +5977,7 @@ export class Editor extends EventEmitter<TLEventMap> {
* @example * @example
* ```ts * ```ts
* editor.alignShapes([box1, box2], 'left') * editor.alignShapes([box1, box2], 'left')
* editor.alignShapes(editor.selectedShapeIds, 'left') * editor.alignShapes(editor.getSelectedShapeIds(), 'left')
* ``` * ```
* *
* @param shapes - The shapes (or shape ids) to align. * @param shapes - The shapes (or shape ids) to align.
@ -6045,7 +6072,7 @@ export class Editor extends EventEmitter<TLEventMap> {
* @example * @example
* ```ts * ```ts
* editor.distributeShapes([box1, box2], 'horizontal') * editor.distributeShapes([box1, box2], 'horizontal')
* editor.distributeShapes(editor.selectedShapeIds, 'horizontal') * editor.distributeShapes(editor.getSelectedShapeIds(), 'horizontal')
* ``` * ```
* *
* @param shapes - The shapes (or shape ids) to distribute. * @param shapes - The shapes (or shape ids) to distribute.
@ -6136,7 +6163,7 @@ export class Editor extends EventEmitter<TLEventMap> {
* @example * @example
* ```ts * ```ts
* editor.stretchShapes([box1, box2], 'horizontal') * editor.stretchShapes([box1, box2], 'horizontal')
* editor.stretchShapes(editor.selectedShapeIds, 'horizontal') * editor.stretchShapes(editor.getSelectedShapeIds(), 'horizontal')
* ``` * ```
* *
* @param shapes - The shapes (or shape ids) to stretch. * @param shapes - The shapes (or shape ids) to stretch.
@ -7181,7 +7208,7 @@ export class Editor extends EventEmitter<TLEventMap> {
(ids: TLShapeId[]) => { (ids: TLShapeId[]) => {
if (this.getInstanceState().isReadonly) return null if (this.getInstanceState().isReadonly) return null
if (ids.length === 0) return null if (ids.length === 0) return null
const prevSelectedShapeIds = [...this.currentPageState.selectedShapeIds] const prevSelectedShapeIds = [...this.getCurrentPageState().selectedShapeIds]
const allIds = new Set(ids) const allIds = new Set(ids)
@ -7213,14 +7240,14 @@ export class Editor extends EventEmitter<TLEventMap> {
{ {
do: ({ deletedIds, postSelectedShapeIds }) => { do: ({ deletedIds, postSelectedShapeIds }) => {
this.store.remove(deletedIds) this.store.remove(deletedIds)
this.store.update(this.currentPageState.id, (state) => ({ this.store.update(this.getCurrentPageState().id, (state) => ({
...state, ...state,
selectedShapeIds: postSelectedShapeIds, selectedShapeIds: postSelectedShapeIds,
})) }))
}, },
undo: ({ snapshots, prevSelectedShapeIds }) => { undo: ({ snapshots, prevSelectedShapeIds }) => {
this.store.put(snapshots) this.store.put(snapshots)
this.store.update(this.currentPageState.id, (state) => ({ this.store.update(this.getCurrentPageState().id, (state) => ({
...state, ...state,
selectedShapeIds: prevSelectedShapeIds, selectedShapeIds: prevSelectedShapeIds,
})) }))
@ -7302,7 +7329,7 @@ export class Editor extends EventEmitter<TLEventMap> {
get sharedStyles(): ReadonlySharedStyleMap { get sharedStyles(): ReadonlySharedStyleMap {
// If we're in selecting and if we have a selection, return the shared styles from the // If we're in selecting and if we have a selection, return the shared styles from the
// current selection // current selection
if (this.isIn('select') && this.selectedShapeIds.length > 0) { if (this.isIn('select') && this.getSelectedShapeIds().length > 0) {
return this._selectionSharedStyles.get() return this._selectionSharedStyles.get()
} }
@ -7327,7 +7354,7 @@ export class Editor extends EventEmitter<TLEventMap> {
* @public * @public
*/ */
@computed get sharedOpacity(): SharedStyle<number> { @computed get sharedOpacity(): SharedStyle<number> {
if (this.isIn('select') && this.selectedShapeIds.length > 0) { if (this.isIn('select') && this.getSelectedShapeIds().length > 0) {
const shapesToCheck: TLShape[] = [] const shapesToCheck: TLShape[] = []
const addShape = (shapeId: TLShapeId) => { const addShape = (shapeId: TLShapeId) => {
const shape = this.getShape(shapeId) const shape = this.getShape(shapeId)
@ -7343,7 +7370,7 @@ export class Editor extends EventEmitter<TLEventMap> {
shapesToCheck.push(shape) shapesToCheck.push(shape)
} }
} }
for (const shapeId of this.selectedShapeIds) { for (const shapeId of this.getSelectedShapeIds()) {
addShape(shapeId) addShape(shapeId)
} }
@ -8649,7 +8676,7 @@ export class Editor extends EventEmitter<TLEventMap> {
if (!inputs.isEditing) { if (!inputs.isEditing) {
this._pinchStart = this.camera.z this._pinchStart = this.camera.z
if (!this._selectedShapeIdsAtPointerDown.length) { if (!this._selectedShapeIdsAtPointerDown.length) {
this._selectedShapeIdsAtPointerDown = this.selectedShapeIds this._selectedShapeIdsAtPointerDown = this.getSelectedShapeIds()
} }
this._didPinch = true this._didPinch = true
@ -8709,7 +8736,7 @@ export class Editor extends EventEmitter<TLEventMap> {
this._updateInputsFromEvent(info) this._updateInputsFromEvent(info)
if (this.isMenuOpen) { if (this.getIsMenuOpen()) {
// noop // noop
} else { } else {
if (inputs.ctrlKey) { if (inputs.ctrlKey) {
@ -8759,7 +8786,7 @@ export class Editor extends EventEmitter<TLEventMap> {
switch (info.name) { switch (info.name) {
case 'pointer_down': { case 'pointer_down': {
this._selectedShapeIdsAtPointerDown = this.selectedShapeIds this._selectedShapeIdsAtPointerDown = this.getSelectedShapeIds()
// Firefox bug fix... // Firefox bug fix...
// If it's a left-mouse-click, we store the pointer id for later user // If it's a left-mouse-click, we store the pointer id for later user
@ -8843,7 +8870,7 @@ export class Editor extends EventEmitter<TLEventMap> {
inputs.isPointing = false inputs.isPointing = false
inputs.isDragging = false inputs.isDragging = false
if (this.isMenuOpen) { if (this.getIsMenuOpen()) {
// Suppressing pointerup here as <ContextMenu/> doesn't seem to do what we what here. // Suppressing pointerup here as <ContextMenu/> doesn't seem to do what we what here.
return return
} }

View file

@ -248,7 +248,8 @@ export class SnapManager {
// TODO: make this an incremental derivation // TODO: make this an incremental derivation
@computed get snappableShapes(): GapNode[] { @computed get snappableShapes(): GapNode[] {
const { editor } = this const { editor } = this
const { selectedShapeIds, renderingBounds: renderingBounds } = editor const { renderingBounds: renderingBounds } = editor
const selectedShapeIds = editor.getSelectedShapeIds()
const snappableShapes: GapNode[] = [] const snappableShapes: GapNode[] = []

View file

@ -51,7 +51,7 @@ export class GroupShapeUtil extends ShapeUtil<TLGroupShape> {
component(shape: TLGroupShape) { component(shape: TLGroupShape) {
const isErasing = this.editor.erasingShapeIds.includes(shape.id) const isErasing = this.editor.erasingShapeIds.includes(shape.id)
const { hintingShapeIds } = this.editor.currentPageState const { hintingShapeIds } = this.editor.getCurrentPageState()
const isHintingOtherGroup = const isHintingOtherGroup =
hintingShapeIds.length > 0 && hintingShapeIds.length > 0 &&
hintingShapeIds.some( hintingShapeIds.some(
@ -60,7 +60,7 @@ export class GroupShapeUtil extends ShapeUtil<TLGroupShape> {
this.editor.isShapeOfType<TLGroupShape>(this.editor.getShape(id)!, 'group') this.editor.isShapeOfType<TLGroupShape>(this.editor.getShape(id)!, 'group')
) )
const isFocused = this.editor.currentPageState.focusedGroupId !== shape.id const isFocused = this.editor.getCurrentPageState().focusedGroupId !== shape.id
if ( if (
!isErasing && // always show the outline while we're erasing the group !isErasing && // always show the outline while we're erasing the group
@ -89,13 +89,13 @@ export class GroupShapeUtil extends ShapeUtil<TLGroupShape> {
override onChildrenChange: TLOnChildrenChangeHandler<TLGroupShape> = (group) => { override onChildrenChange: TLOnChildrenChangeHandler<TLGroupShape> = (group) => {
const children = this.editor.getSortedChildIdsForParent(group.id) const children = this.editor.getSortedChildIdsForParent(group.id)
if (children.length === 0) { if (children.length === 0) {
if (this.editor.currentPageState.focusedGroupId === group.id) { if (this.editor.getCurrentPageState().focusedGroupId === group.id) {
this.editor.popFocusedGroupId() this.editor.popFocusedGroupId()
} }
this.editor.deleteShapes([group.id]) this.editor.deleteShapes([group.id])
return return
} else if (children.length === 1) { } else if (children.length === 1) {
if (this.editor.currentPageState.focusedGroupId === group.id) { if (this.editor.getCurrentPageState().focusedGroupId === group.id) {
this.editor.popFocusedGroupId() this.editor.popFocusedGroupId()
} }
this.editor.reparentShapes(children, group.parentId) this.editor.reparentShapes(children, group.parentId)

View file

@ -13,8 +13,7 @@ export class Pointing extends StateNode {
wasFocusedOnEnter = false wasFocusedOnEnter = false
override onEnter = () => { override onEnter = () => {
const { isMenuOpen } = this.editor this.wasFocusedOnEnter = !this.editor.getIsMenuOpen()
this.wasFocusedOnEnter = !isMenuOpen
} }
override onPointerMove: TLEventHandlers['onPointerMove'] = (info) => { override onPointerMove: TLEventHandlers['onPointerMove'] = (info) => {

View file

@ -83,7 +83,7 @@ export function useDocumentEvents() {
break break
} }
case 'Tab': { case 'Tab': {
if (isFocusingInput() || editor.isMenuOpen) { if (isFocusingInput() || editor.getIsMenuOpen()) {
return return
} }
break break
@ -126,7 +126,7 @@ export function useDocumentEvents() {
// escape de-selects them. Only when the user's selection is empty // escape de-selects them. Only when the user's selection is empty
// should we allow escape to do its normal thing. // should we allow escape to do its normal thing.
if (editor.editingShape || editor.selectedShapeIds.length > 0) { if (editor.editingShape || editor.getSelectedShapeIds().length > 0) {
e.preventDefault() e.preventDefault()
} }
@ -147,7 +147,7 @@ export function useDocumentEvents() {
return return
} }
default: { default: {
if (isFocusingInput() || editor.isMenuOpen) { if (isFocusingInput() || editor.getIsMenuOpen()) {
return return
} }
} }
@ -170,7 +170,7 @@ export function useDocumentEvents() {
if ((e as any).isKilled) return if ((e as any).isKilled) return
;(e as any).isKilled = true ;(e as any).isKilled = true
if (isFocusingInput() || editor.isMenuOpen) { if (isFocusingInput() || editor.getIsMenuOpen()) {
return return
} }

View file

@ -34,7 +34,7 @@ export const TldrawSelectionForeground: TLSelectionForegroundComponent = track(
const bottomLeftEvents = useSelectionEvents('bottom_left') const bottomLeftEvents = useSelectionEvents('bottom_left')
const isDefaultCursor = const isDefaultCursor =
!editor.isMenuOpen && 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.selectedShapes

View file

@ -23,7 +23,7 @@ beforeEach(() => {
editor = new TestEditor() editor = new TestEditor()
editor editor
.selectAll() .selectAll()
.deleteShapes(editor.selectedShapeIds) .deleteShapes(editor.getSelectedShapeIds())
.createShapes([ .createShapes([
{ id: ids.box1, type: 'geo', x: 100, y: 100, props: { w: 100, h: 100 } }, { id: ids.box1, type: 'geo', x: 100, y: 100, props: { w: 100, h: 100 } },
{ id: ids.box2, type: 'geo', x: 300, y: 300, props: { w: 100, h: 100 } }, { id: ids.box2, type: 'geo', x: 300, y: 300, props: { w: 100, h: 100 } },
@ -382,7 +382,7 @@ describe('When pointing an end shape', () => {
describe('reparenting issue', () => { describe('reparenting issue', () => {
it('Correctly sets index when reparenting', () => { it('Correctly sets index when reparenting', () => {
editor.selectAll().deleteShapes(editor.selectedShapeIds) editor.selectAll().deleteShapes(editor.getSelectedShapeIds())
// Create an arrow! // Create an arrow!
editor.setCurrentTool('arrow') editor.setCurrentTool('arrow')
@ -440,7 +440,7 @@ describe('reparenting issue', () => {
}) })
it('Correctly sets index when reparenting with multiple arrows', () => { it('Correctly sets index when reparenting with multiple arrows', () => {
editor.selectAll().deleteShapes(editor.selectedShapeIds) editor.selectAll().deleteShapes(editor.getSelectedShapeIds())
// create two rectangles: // create two rectangles:
editor.createShapes([ editor.createShapes([
@ -494,7 +494,7 @@ describe('reparenting issue', () => {
expect(arrow2BoundIndex).toBe('a1G') expect(arrow2BoundIndex).toBe('a1G')
// nudge everything around and make sure we all stay in the right order // nudge everything around and make sure we all stay in the right order
editor.selectAll().nudgeShapes(editor.selectedShapeIds, { x: -1, y: 0 }) editor.selectAll().nudgeShapes(editor.getSelectedShapeIds(), { x: -1, y: 0 })
expect(editor.getShape(arrow1Id)!.index).toBe('a1V') expect(editor.getShape(arrow1Id)!.index).toBe('a1V')
expect(editor.getShape(arrow2Id)!.index).toBe('a1G') expect(editor.getShape(arrow2Id)!.index).toBe('a1G')
}) })
@ -502,7 +502,7 @@ describe('reparenting issue', () => {
describe('line bug', () => { describe('line bug', () => {
it('works as expected when binding to a straight line', () => { it('works as expected when binding to a straight line', () => {
editor.selectAll().deleteShapes(editor.selectedShapeIds) editor.selectAll().deleteShapes(editor.getSelectedShapeIds())
expect(editor.currentPageShapes.length).toBe(0) expect(editor.currentPageShapes.length).toBe(0)
@ -528,7 +528,7 @@ describe('line bug', () => {
}) })
it('works as expected when binding to a straight horizontal line', () => { it('works as expected when binding to a straight horizontal line', () => {
editor.selectAll().deleteShapes(editor.selectedShapeIds) editor.selectAll().deleteShapes(editor.getSelectedShapeIds())
expect(editor.currentPageShapes.length).toBe(0) expect(editor.currentPageShapes.length).toBe(0)

View file

@ -32,7 +32,7 @@ beforeEach(() => {
editor = new TestEditor() editor = new TestEditor()
editor editor
.selectAll() .selectAll()
.deleteShapes(editor.selectedShapeIds) .deleteShapes(editor.getSelectedShapeIds())
.createShapes([ .createShapes([
{ id: ids.box1, type: 'geo', x: 100, y: 100, props: { w: 100, h: 100 } }, { id: ids.box1, type: 'geo', x: 100, y: 100, props: { w: 100, h: 100 } },
{ id: ids.box2, type: 'geo', x: 300, y: 300, props: { w: 100, h: 100 } }, { id: ids.box2, type: 'geo', x: 300, y: 300, props: { w: 100, h: 100 } },
@ -179,7 +179,7 @@ describe('Other cases when arrow are moved', () => {
editor.select(ids.arrow1, ids.box2) editor.select(ids.arrow1, ids.box2)
// When box one is not selected, unbinds box1 and keeps binding to box2 // When box one is not selected, unbinds box1 and keeps binding to box2
editor.nudgeShapes(editor.selectedShapeIds, { x: 0, y: -1 }) editor.nudgeShapes(editor.getSelectedShapeIds(), { x: 0, y: -1 })
expect(editor.getShape(ids.arrow1)).toMatchObject({ expect(editor.getShape(ids.arrow1)).toMatchObject({
props: { props: {
@ -190,7 +190,7 @@ describe('Other cases when arrow are moved', () => {
// unbinds when only the arrow is selected (not its bound shapes) // unbinds when only the arrow is selected (not its bound shapes)
editor.select(ids.arrow1) editor.select(ids.arrow1)
editor.nudgeShapes(editor.selectedShapeIds, { x: 0, y: -1 }) editor.nudgeShapes(editor.getSelectedShapeIds(), { x: 0, y: -1 })
expect(editor.getShape(ids.arrow1)).toMatchObject({ expect(editor.getShape(ids.arrow1)).toMatchObject({
props: { start: { type: 'point' }, end: { type: 'point' } }, props: { start: { type: 'point' }, end: { type: 'point' } },
@ -202,7 +202,7 @@ describe('Other cases when arrow are moved', () => {
// When box one is not selected, unbinds box1 and keeps binding to box2 // When box one is not selected, unbinds box1 and keeps binding to box2
editor.select(ids.arrow1, ids.box2, ids.box3) editor.select(ids.arrow1, ids.box2, ids.box3)
editor.alignShapes(editor.selectedShapeIds, 'right') editor.alignShapes(editor.getSelectedShapeIds(), 'right')
jest.advanceTimersByTime(1000) jest.advanceTimersByTime(1000)
expect(editor.getShape(ids.arrow1)).toMatchObject({ expect(editor.getShape(ids.arrow1)).toMatchObject({
@ -214,7 +214,7 @@ describe('Other cases when arrow are moved', () => {
// unbinds when only the arrow is selected (not its bound shapes) // unbinds when only the arrow is selected (not its bound shapes)
editor.select(ids.arrow1, ids.box3) editor.select(ids.arrow1, ids.box3)
editor.alignShapes(editor.selectedShapeIds, 'top') editor.alignShapes(editor.getSelectedShapeIds(), 'top')
jest.advanceTimersByTime(1000) jest.advanceTimersByTime(1000)
expect(editor.getShape(ids.arrow1)).toMatchObject({ expect(editor.getShape(ids.arrow1)).toMatchObject({
@ -237,7 +237,7 @@ describe('Other cases when arrow are moved', () => {
// When box one is not selected, unbinds box1 and keeps binding to box2 // When box one is not selected, unbinds box1 and keeps binding to box2
editor.select(ids.arrow1, ids.box2, ids.box3) editor.select(ids.arrow1, ids.box2, ids.box3)
editor.distributeShapes(editor.selectedShapeIds, 'horizontal') editor.distributeShapes(editor.getSelectedShapeIds(), 'horizontal')
jest.advanceTimersByTime(1000) jest.advanceTimersByTime(1000)
expect(editor.getShape(ids.arrow1)).toMatchObject({ expect(editor.getShape(ids.arrow1)).toMatchObject({
@ -255,7 +255,7 @@ describe('Other cases when arrow are moved', () => {
// unbinds when only the arrow is selected (not its bound shapes) if the arrow itself has moved // unbinds when only the arrow is selected (not its bound shapes) if the arrow itself has moved
editor.select(ids.arrow1, ids.box3, ids.box4) editor.select(ids.arrow1, ids.box3, ids.box4)
editor.distributeShapes(editor.selectedShapeIds, 'vertical') editor.distributeShapes(editor.getSelectedShapeIds(), 'vertical')
jest.advanceTimersByTime(1000) jest.advanceTimersByTime(1000)
// The arrow didn't actually move // The arrow didn't actually move
@ -274,7 +274,7 @@ describe('Other cases when arrow are moved', () => {
// The arrow will move this time, so it should unbind // The arrow will move this time, so it should unbind
editor.updateShapes([{ id: ids.box4, type: 'geo', y: -600 }]) editor.updateShapes([{ id: ids.box4, type: 'geo', y: -600 }])
editor.distributeShapes(editor.selectedShapeIds, 'vertical') editor.distributeShapes(editor.getSelectedShapeIds(), 'vertical')
jest.advanceTimersByTime(1000) jest.advanceTimersByTime(1000)
expect(editor.getShape(ids.arrow1)).toMatchObject({ expect(editor.getShape(ids.arrow1)).toMatchObject({
@ -293,13 +293,13 @@ describe('Other cases when arrow are moved', () => {
// create shapes in a group: // create shapes in a group:
editor editor
.selectAll() .selectAll()
.deleteShapes(editor.selectedShapeIds) .deleteShapes(editor.getSelectedShapeIds())
.createShapes([ .createShapes([
{ id: ids.box3, type: 'geo', x: 0, y: 300, props: { w: 100, h: 100 } }, { id: ids.box3, type: 'geo', x: 0, y: 300, props: { w: 100, h: 100 } },
{ id: ids.box4, type: 'geo', x: 0, y: 600, props: { w: 100, h: 100 } }, { id: ids.box4, type: 'geo', x: 0, y: 600, props: { w: 100, h: 100 } },
]) ])
.selectAll() .selectAll()
.groupShapes(editor.selectedShapeIds) .groupShapes(editor.getSelectedShapeIds())
editor.setCurrentTool('arrow').pointerDown(1000, 1000).pointerMove(50, 350).pointerUp(50, 350) editor.setCurrentTool('arrow').pointerDown(1000, 1000).pointerMove(50, 350).pointerUp(50, 350)
let arrow = editor.currentPageShapes[editor.currentPageShapes.length - 1] let arrow = editor.currentPageShapes[editor.currentPageShapes.length - 1]
@ -308,7 +308,7 @@ describe('Other cases when arrow are moved', () => {
expect(arrow.props.end.boundShapeId).toBe(ids.box3) expect(arrow.props.end.boundShapeId).toBe(ids.box3)
// translate: // translate:
editor.selectAll().nudgeShapes(editor.selectedShapeIds, { x: 0, y: 1 }) editor.selectAll().nudgeShapes(editor.getSelectedShapeIds(), { x: 0, y: 1 })
// arrow should still be bound to box3 // arrow should still be bound to box3
arrow = editor.getShape(arrow.id)! arrow = editor.getShape(arrow.id)!
@ -360,7 +360,7 @@ describe('resizing', () => {
it('resizes', () => { it('resizes', () => {
editor editor
.selectAll() .selectAll()
.deleteShapes(editor.selectedShapeIds) .deleteShapes(editor.getSelectedShapeIds())
.setCurrentTool('arrow') .setCurrentTool('arrow')
.pointerDown(0, 0) .pointerDown(0, 0)
.pointerMove(200, 200) .pointerMove(200, 200)
@ -415,7 +415,7 @@ describe('resizing', () => {
it('flips bend when flipping x or y', () => { it('flips bend when flipping x or y', () => {
editor editor
.selectAll() .selectAll()
.deleteShapes(editor.selectedShapeIds) .deleteShapes(editor.getSelectedShapeIds())
.setCurrentTool('arrow') .setCurrentTool('arrow')
.pointerDown(0, 0) .pointerDown(0, 0)
.pointerMove(200, 200) .pointerMove(200, 200)
@ -484,7 +484,7 @@ describe("an arrow's parents", () => {
let boxCid: TLShapeId let boxCid: TLShapeId
beforeEach(() => { beforeEach(() => {
editor.selectAll().deleteShapes(editor.selectedShapeIds) editor.selectAll().deleteShapes(editor.getSelectedShapeIds())
editor.setCurrentTool('frame') editor.setCurrentTool('frame')
editor.pointerDown(0, 0).pointerMove(100, 100).pointerUp() editor.pointerDown(0, 0).pointerMove(100, 100).pointerUp()

View file

@ -339,7 +339,7 @@ export class ArrowShapeUtil extends ShapeUtil<TLArrowShape> {
// If at least one bound shape is in the selection, do nothing; // If at least one bound shape is in the selection, do nothing;
// If no bound shapes are in the selection, unbind any bound shapes // If no bound shapes are in the selection, unbind any bound shapes
const { selectedShapeIds } = this.editor const selectedShapeIds = this.editor.getSelectedShapeIds()
if ( if (
(startBindingId && (startBindingId &&

View file

@ -53,7 +53,7 @@ export class Drawing extends StateNode {
override onEnter = (info: TLPointerEventInfo) => { override onEnter = (info: TLPointerEventInfo) => {
this.markId = null this.markId = null
this.info = info this.info = info
this.canDraw = !this.editor.isMenuOpen this.canDraw = !this.editor.getIsMenuOpen()
this.lastRecordedPoint = this.editor.inputs.currentPagePoint.clone() this.lastRecordedPoint = this.editor.inputs.currentPagePoint.clone()
if (this.canDraw) { if (this.canDraw) {
this.startShape() this.startShape()

View file

@ -84,7 +84,7 @@ export class EmbedShapeUtil extends BaseBoxShapeUtil<TLEmbedShape> {
const isHoveringWhileEditingSameShape = useValue( const isHoveringWhileEditingSameShape = useValue(
'is hovering', 'is hovering',
() => { () => {
const { editingShapeId, hoveredShapeId } = this.editor.currentPageState const { editingShapeId, hoveredShapeId } = this.editor.getCurrentPageState()
if (editingShapeId && hoveredShapeId !== editingShapeId) { if (editingShapeId && hoveredShapeId !== editingShapeId) {
const editingShape = this.editor.getShape(editingShapeId) const editingShape = this.editor.getShape(editingShapeId)

View file

@ -21,7 +21,7 @@ describe(FrameShapeTool, () => {
expect(editor.currentPageShapes.length).toBe(1) expect(editor.currentPageShapes.length).toBe(1)
expect(editor.currentPageShapes[0]?.type).toBe('frame') expect(editor.currentPageShapes[0]?.type).toBe('frame')
expect(editor.selectedShapeIds[0]).toBe(editor.currentPageShapes[0]?.id) expect(editor.getSelectedShapeIds()[0]).toBe(editor.currentPageShapes[0]?.id)
editor.undo() editor.undo()
@ -41,7 +41,7 @@ describe(FrameShapeTool, () => {
expect(editor.currentPageShapes.length).toBe(1) expect(editor.currentPageShapes.length).toBe(1)
expect(editor.currentPageShapes[0]?.type).toBe('frame') expect(editor.currentPageShapes[0]?.type).toBe('frame')
expect(editor.selectedShapeIds[0]).toBe(editor.currentPageShapes[0]?.id) expect(editor.getSelectedShapeIds()[0]).toBe(editor.currentPageShapes[0]?.id)
editor.undo() editor.undo()

View file

@ -21,7 +21,7 @@ describe(GeoShapeTool, () => {
expect(editor.currentPageShapes.length).toBe(1) expect(editor.currentPageShapes.length).toBe(1)
expect(editor.currentPageShapes[0]?.type).toBe('geo') expect(editor.currentPageShapes[0]?.type).toBe('geo')
expect(editor.selectedShapeIds[0]).toBe(editor.currentPageShapes[0]?.id) expect(editor.getSelectedShapeIds()[0]).toBe(editor.currentPageShapes[0]?.id)
editor.undo() editor.undo()
@ -41,7 +41,7 @@ describe(GeoShapeTool, () => {
expect(editor.currentPageShapes.length).toBe(1) expect(editor.currentPageShapes.length).toBe(1)
expect(editor.currentPageShapes[0]?.type).toBe('geo') expect(editor.currentPageShapes[0]?.type).toBe('geo')
expect(editor.selectedShapeIds[0]).toBe(editor.currentPageShapes[0]?.id) expect(editor.getSelectedShapeIds()[0]).toBe(editor.currentPageShapes[0]?.id)
editor.undo() editor.undo()

View file

@ -15,7 +15,7 @@ beforeEach(() => {
editor = new TestEditor() editor = new TestEditor()
editor editor
.selectAll() .selectAll()
.deleteShapes(editor.selectedShapeIds) .deleteShapes(editor.getSelectedShapeIds())
.createShapes([ .createShapes([
{ {
id: id, id: id,
@ -149,7 +149,7 @@ describe('Misc', () => {
it('nudges', () => { it('nudges', () => {
editor.select(id) editor.select(id)
editor.nudgeShapes(editor.selectedShapeIds, { x: 1, y: 0 }) editor.nudgeShapes(editor.getSelectedShapeIds(), { x: 1, y: 0 })
editor.expectShapeToMatch({ editor.expectShapeToMatch({
id: id, id: id,
@ -157,7 +157,7 @@ describe('Misc', () => {
y: 150, y: 150,
}) })
editor.nudgeShapes(editor.selectedShapeIds, { x: 0, y: 10 }) editor.nudgeShapes(editor.getSelectedShapeIds(), { x: 0, y: 10 })
editor.expectShapeToMatch({ editor.expectShapeToMatch({
id: id, id: id,
@ -176,12 +176,12 @@ describe('Misc', () => {
editor.select(boxID, id) editor.select(boxID, id)
expect(editor.getShapePageBounds(box)!.maxX).not.toEqual(editor.getShapePageBounds(line)!.maxX) expect(editor.getShapePageBounds(box)!.maxX).not.toEqual(editor.getShapePageBounds(line)!.maxX)
editor.alignShapes(editor.selectedShapeIds, 'right') editor.alignShapes(editor.getSelectedShapeIds(), 'right')
jest.advanceTimersByTime(1000) jest.advanceTimersByTime(1000)
expect(editor.getShapePageBounds(box)!.maxX).toEqual(editor.getShapePageBounds(line)!.maxX) expect(editor.getShapePageBounds(box)!.maxX).toEqual(editor.getShapePageBounds(line)!.maxX)
expect(editor.getShapePageBounds(box)!.maxY).not.toEqual(editor.getShapePageBounds(line)!.maxY) expect(editor.getShapePageBounds(box)!.maxY).not.toEqual(editor.getShapePageBounds(line)!.maxY)
editor.alignShapes(editor.selectedShapeIds, 'bottom') editor.alignShapes(editor.getSelectedShapeIds(), 'bottom')
jest.advanceTimersByTime(1000) jest.advanceTimersByTime(1000)
expect(editor.getShapePageBounds(box)!.maxY).toEqual(editor.getShapePageBounds(line)!.maxY) expect(editor.getShapePageBounds(box)!.maxY).toEqual(editor.getShapePageBounds(line)!.maxY)
}) })
@ -213,7 +213,7 @@ describe('Misc', () => {
const duplicate = ids.filter((i) => i !== id)[0] const duplicate = ids.filter((i) => i !== id)[0]
editor.select(duplicate) editor.select(duplicate)
editor.deleteShapes(editor.selectedShapeIds) editor.deleteShapes(editor.getSelectedShapeIds())
ids = Array.from(editor.currentPageShapeIds.values()) ids = Array.from(editor.currentPageShapeIds.values())
expect(ids.length).toEqual(1) expect(ids.length).toEqual(1)

View file

@ -21,7 +21,7 @@ describe(NoteShapeTool, () => {
expect(editor.currentPageShapes.length).toBe(1) expect(editor.currentPageShapes.length).toBe(1)
expect(editor.currentPageShapes[0]?.type).toBe('note') expect(editor.currentPageShapes[0]?.type).toBe('note')
expect(editor.selectedShapeIds[0]).toBe(editor.currentPageShapes[0]?.id) expect(editor.getSelectedShapeIds()[0]).toBe(editor.currentPageShapes[0]?.id)
editor.cancel() // leave edit mode editor.cancel() // leave edit mode
@ -44,7 +44,7 @@ describe(NoteShapeTool, () => {
expect(editor.currentPageShapes.length).toBe(1) expect(editor.currentPageShapes.length).toBe(1)
expect(editor.currentPageShapes[0]?.type).toBe('note') expect(editor.currentPageShapes[0]?.type).toBe('note')
expect(editor.selectedShapeIds[0]).toBe(editor.currentPageShapes[0]?.id) expect(editor.getSelectedShapeIds()[0]).toBe(editor.currentPageShapes[0]?.id)
editor.undo() editor.undo()

View file

@ -21,7 +21,7 @@ export class Pointing extends StateNode {
shape = {} as TLNoteShape shape = {} as TLNoteShape
override onEnter = () => { override onEnter = () => {
this.wasFocusedOnEnter = !this.editor.isMenuOpen this.wasFocusedOnEnter = !this.editor.getIsMenuOpen()
if (this.wasFocusedOnEnter) { if (this.wasFocusedOnEnter) {
this.shape = this.createShape() this.shape = this.createShape()
} }

View file

@ -117,7 +117,7 @@ export class Erasing extends StateNode {
} }
complete() { complete() {
this.editor.deleteShapes(this.editor.currentPageState.erasingShapeIds) this.editor.deleteShapes(this.editor.getCurrentPageState().erasingShapeIds)
this.editor.setErasingShapes([]) this.editor.setErasingShapes([])
this.parent.transition('idle', {}) this.parent.transition('idle', {})
} }

View file

@ -42,7 +42,7 @@ export class SelectTool extends StateNode {
] ]
override onExit = () => { override onExit = () => {
if (this.editor.currentPageState.editingShapeId) { if (this.editor.getCurrentPageState().editingShapeId) {
this.editor.setEditingShape(null) this.editor.setEditingShape(null)
} }
} }

View file

@ -49,7 +49,7 @@ export class Brushing extends StateNode {
) )
this.info = info this.info = info
this.initialSelectedShapeIds = this.editor.selectedShapeIds.slice() this.initialSelectedShapeIds = this.editor.getSelectedShapeIds().slice()
this.initialStartShape = this.editor.getShapesAtPoint(currentPagePoint)[0] this.initialStartShape = this.editor.getShapesAtPoint(currentPagePoint)[0]
this.onPointerMove() this.onPointerMove()
} }

View file

@ -39,7 +39,7 @@ export class Idle extends StateNode {
} }
override onPointerDown: TLEventHandlers['onPointerDown'] = (info) => { override onPointerDown: TLEventHandlers['onPointerDown'] = (info) => {
if (this.editor.isMenuOpen) return if (this.editor.getIsMenuOpen()) return
if (info.ctrlKey) { if (info.ctrlKey) {
this.cancel() this.cancel()

View file

@ -20,7 +20,7 @@ export class EditingShape extends StateNode {
} }
override onExit = () => { override onExit = () => {
const { editingShapeId } = this.editor.currentPageState const { editingShapeId } = this.editor.getCurrentPageState()
if (!editingShapeId) return if (!editingShapeId) return
// Clear the editing shape // Clear the editing shape

View file

@ -35,7 +35,7 @@ export class Idle extends StateNode {
} }
override onPointerDown: TLEventHandlers['onPointerDown'] = (info) => { override onPointerDown: TLEventHandlers['onPointerDown'] = (info) => {
if (this.editor.isMenuOpen) return if (this.editor.getIsMenuOpen()) return
const shouldEnterCropMode = info.ctrlKey && getShouldEnterCropMode(this.editor) const shouldEnterCropMode = info.ctrlKey && getShouldEnterCropMode(this.editor)
@ -66,9 +66,9 @@ export class Idle extends StateNode {
return return
} }
const selectedShapeIds = this.editor.getSelectedShapeIds()
const { const {
onlySelectedShape, onlySelectedShape,
selectedShapeIds,
inputs: { currentPagePoint }, inputs: { currentPagePoint },
} = this.editor } = this.editor
@ -140,7 +140,7 @@ export class Idle extends StateNode {
} }
default: { default: {
const { hoveredShape } = this.editor const { hoveredShape } = this.editor
if (hoveredShape && !this.editor.selectedShapeIds.includes(hoveredShape.id)) { if (hoveredShape && !this.editor.getSelectedShapeIds().includes(hoveredShape.id)) {
this.onPointerDown({ this.onPointerDown({
...info, ...info,
shape: hoveredShape, shape: hoveredShape,
@ -345,9 +345,9 @@ export class Idle extends StateNode {
return return
} }
const selectedShapeIds = this.editor.getSelectedShapeIds()
const { const {
onlySelectedShape, onlySelectedShape,
selectedShapeIds,
inputs: { currentPagePoint }, inputs: { currentPagePoint },
} = this.editor } = this.editor
@ -369,7 +369,7 @@ export class Idle extends StateNode {
break break
} }
case 'shape': { case 'shape': {
const { selectedShapeIds } = this.editor.currentPageState const { selectedShapeIds } = this.editor.getCurrentPageState()
const { shape } = info const { shape } = info
const targetShape = this.editor.getOutermostSelectableShape( const targetShape = this.editor.getOutermostSelectableShape(
@ -389,7 +389,7 @@ export class Idle extends StateNode {
override onCancel: TLEventHandlers['onCancel'] = () => { override onCancel: TLEventHandlers['onCancel'] = () => {
if ( if (
this.editor.focusedGroupId !== this.editor.currentPageId && this.editor.focusedGroupId !== this.editor.currentPageId &&
this.editor.selectedShapeIds.length > 0 this.editor.getSelectedShapeIds().length > 0
) { ) {
this.editor.popFocusedGroupId() this.editor.popFocusedGroupId()
} else { } else {
@ -544,7 +544,7 @@ export class Idle extends StateNode {
? MAJOR_NUDGE_FACTOR ? MAJOR_NUDGE_FACTOR
: MINOR_NUDGE_FACTOR : MINOR_NUDGE_FACTOR
this.editor.nudgeShapes(this.editor.selectedShapeIds, delta.mul(step)) this.editor.nudgeShapes(this.editor.getSelectedShapeIds(), delta.mul(step))
} }
private canInteractWithShapeInReadOnly(shape: TLShape) { private canInteractWithShapeInReadOnly(shape: TLShape) {

View file

@ -8,7 +8,7 @@ export class PointingCanvas extends StateNode {
const { inputs } = this.editor const { inputs } = this.editor
if (!inputs.shiftKey) { if (!inputs.shiftKey) {
if (this.editor.selectedShapeIds.length > 0) { if (this.editor.getSelectedShapeIds().length > 0) {
this.editor.mark('selecting none') this.editor.mark('selecting none')
this.editor.selectNone() this.editor.selectNone()
} }

View file

@ -18,8 +18,8 @@ export class PointingShape extends StateNode {
didSelectOnEnter = false didSelectOnEnter = false
override onEnter = (info: TLPointerEventInfo & { target: 'shape' }) => { override onEnter = (info: TLPointerEventInfo & { target: 'shape' }) => {
const selectedShapeIds = this.editor.getSelectedShapeIds()
const { const {
selectedShapeIds,
focusedGroupId, focusedGroupId,
selectionRotatedPageBounds: selectionBounds, selectionRotatedPageBounds: selectionBounds,
inputs: { currentPagePoint, shiftKey, altKey }, inputs: { currentPagePoint, shiftKey, altKey },
@ -60,10 +60,10 @@ export class PointingShape extends StateNode {
} }
override onPointerUp: TLEventHandlers['onPointerUp'] = (info) => { override onPointerUp: TLEventHandlers['onPointerUp'] = (info) => {
const selectedShapeIds = this.editor.getSelectedShapeIds()
const { const {
zoomLevel, zoomLevel,
focusedGroupId, focusedGroupId,
selectedShapeIds,
inputs: { currentPagePoint, shiftKey }, inputs: { currentPagePoint, shiftKey },
} = this.editor } = this.editor
@ -180,7 +180,7 @@ export class PointingShape extends StateNode {
this.editor.mark('shift deselecting on pointer up') this.editor.mark('shift deselecting on pointer up')
this.editor.setSelectedShapes([ this.editor.setSelectedShapes([
...this.editor.selectedShapeIds.filter((id) => !ancestors.find((a) => a.id === id)), ...this.editor.getSelectedShapeIds().filter((id) => !ancestors.find((a) => a.id === id)),
outermostSelectableShape.id, outermostSelectableShape.id,
]) ])
} else { } else {

View file

@ -364,8 +364,8 @@ export class Resizing extends StateNode {
} }
_createSnapshot = () => { _createSnapshot = () => {
const selectedShapeIds = this.editor.getSelectedShapeIds()
const { const {
selectedShapeIds,
selectionRotation, selectionRotation,
inputs: { originPagePoint }, inputs: { originPagePoint },
} = this.editor } = this.editor

View file

@ -26,7 +26,7 @@ export class ScribbleBrushing extends StateNode {
override onEnter = () => { override onEnter = () => {
this.initialSelectedShapeIds = new Set<TLShapeId>( this.initialSelectedShapeIds = new Set<TLShapeId>(
this.editor.inputs.shiftKey ? this.editor.selectedShapeIds : [] this.editor.inputs.shiftKey ? this.editor.getSelectedShapeIds() : []
) )
this.newlySelectedShapeIds = new Set<TLShapeId>() this.newlySelectedShapeIds = new Set<TLShapeId>()
this.size = 0 this.size = 0

View file

@ -132,7 +132,7 @@ export class Translating extends StateNode {
this.markId = 'translating' this.markId = 'translating'
this.editor.mark(this.markId) this.editor.mark(this.markId)
this.editor.duplicateShapes(Array.from(this.editor.selectedShapeIds)) this.editor.duplicateShapes(Array.from(this.editor.getSelectedShapeIds()))
this.snapshot = getTranslatingSnapshot(this.editor) this.snapshot = getTranslatingSnapshot(this.editor)
this.handleStart() this.handleStart()
@ -285,7 +285,7 @@ function getTranslatingSnapshot(editor: Editor) {
const pagePoints: Vec2d[] = [] const pagePoints: Vec2d[] = []
const shapeSnapshots = compact( const shapeSnapshots = compact(
editor.selectedShapeIds.map((id): null | MovingShapeSnapshot => { editor.getSelectedShapeIds().map((id): null | MovingShapeSnapshot => {
const shape = editor.getShape(id) const shape = editor.getShape(id)
if (!shape) return null if (!shape) return null
movingShapes.push(shape) movingShapes.push(shape)
@ -312,8 +312,8 @@ function getTranslatingSnapshot(editor: Editor) {
shapeSnapshots, shapeSnapshots,
initialPageBounds: editor.selectionPageBounds!, initialPageBounds: editor.selectionPageBounds!,
initialSnapPoints: initialSnapPoints:
editor.selectedShapeIds.length === 1 editor.getSelectedShapeIds().length === 1
? editor.snaps.snapPointsCache.get(editor.selectedShapeIds[0])! ? editor.snaps.snapPointsCache.get(editor.getSelectedShapeIds()[0])!
: editor.selectionPageBounds : editor.selectionPageBounds
? editor.selectionPageBounds.snapPoints.map((p, i) => ({ ? editor.selectionPageBounds.snapPoints.map((p, i) => ({
id: 'selection:' + i, id: 'selection:' + i,

View file

@ -1,7 +1,7 @@
import { Editor, HIT_TEST_MARGIN, TLShape, isShapeId } from '@tldraw/editor' import { Editor, HIT_TEST_MARGIN, TLShape, isShapeId } from '@tldraw/editor'
export function selectOnCanvasPointerUp(editor: Editor) { export function selectOnCanvasPointerUp(editor: Editor) {
const { selectedShapeIds } = editor const selectedShapeIds = editor.getSelectedShapeIds()
const { shiftKey, altKey, currentPagePoint } = editor.inputs const { shiftKey, altKey, currentPagePoint } = editor.inputs
const hitShape = editor.getShapeAtPoint(currentPagePoint, { const hitShape = editor.getShapeAtPoint(currentPagePoint, {

View file

@ -20,7 +20,7 @@ export function updateHoveredId(editor: Editor) {
} else { } else {
if ( if (
outermostShape.id === editor.focusedGroupId || outermostShape.id === editor.focusedGroupId ||
editor.selectedShapeIds.includes(outermostShape.id) editor.getSelectedShapeIds().includes(outermostShape.id)
) { ) {
shapeToHover = hitShape shapeToHover = hitShape
} else { } else {

View file

@ -15,7 +15,7 @@ export const DuplicateButton = track(function DuplicateButton() {
icon={action.icon} icon={action.icon}
type="icon" type="icon"
onClick={() => action.onSelect('quick-actions')} onClick={() => action.onSelect('quick-actions')}
disabled={!(editor.isIn('select') && editor.selectedShapeIds.length > 0)} disabled={!(editor.isIn('select') && editor.getSelectedShapeIds().length > 0)}
title={`${msg(action.label!)} ${kbdStr(action.kbd!)}`} title={`${msg(action.label!)} ${kbdStr(action.kbd!)}`}
smallIcon smallIcon
/> />

View file

@ -36,7 +36,7 @@ export const MoveToPageMenu = track(function MoveToPageMenu() {
disabled={currentPageId === page.id} disabled={currentPageId === page.id}
onSelect={() => { onSelect={() => {
editor.mark('move_shapes_to_page') editor.mark('move_shapes_to_page')
editor.moveShapesToPage(editor.selectedShapeIds, page.id as TLPageId) editor.moveShapesToPage(editor.getSelectedShapeIds(), page.id as TLPageId)
const toPage = editor.getPage(page.id) const toPage = editor.getPage(page.id)
@ -79,7 +79,7 @@ export const MoveToPageMenu = track(function MoveToPageMenu() {
key="new-page" key="new-page"
onSelect={() => { onSelect={() => {
const newPageId = PageRecordType.createId() const newPageId = PageRecordType.createId()
const ids = editor.selectedShapeIds const ids = editor.getSelectedShapeIds()
editor.batch(() => { editor.batch(() => {
editor.mark('move_shapes_to_page') editor.mark('move_shapes_to_page')
editor.createPage({ name: 'Page', id: newPageId }) editor.createPage({ name: 'Page', id: newPageId })

View file

@ -178,7 +178,10 @@ export class MinimapManager {
const { editor, canvasScreenBounds, canvasPageBounds, contentPageBounds, contentScreenBounds } = const { editor, canvasScreenBounds, canvasPageBounds, contentPageBounds, contentScreenBounds } =
this this
const { width: cw, height: ch } = canvasScreenBounds const { width: cw, height: ch } = canvasScreenBounds
const { viewportPageBounds, selectedShapeIds } = editor
const selectedShapeIds = this.editor.getSelectedShapeIds()
const { viewportPageBounds } = editor
if (!cvs || !pageBounds) { if (!cvs || !pageBounds) {
return return

View file

@ -13,7 +13,7 @@ export const ZoomMenu = track(function ZoomMenu() {
const zoom = editor.zoomLevel const zoom = editor.zoomLevel
const hasShapes = editor.currentPageShapeIds.size > 0 const hasShapes = editor.currentPageShapeIds.size > 0
const hasSelected = editor.selectedShapeIds.length > 0 const hasSelected = editor.getSelectedShapeIds().length > 0
const isZoomedTo100 = editor.zoomLevel === 1 const isZoomedTo100 = editor.zoomLevel === 1
const handleDoubleClick = React.useCallback(() => { const handleDoubleClick = React.useCallback(() => {

View file

@ -20,7 +20,7 @@ export const TrashButton = track(function TrashButton() {
icon={action.icon} icon={action.icon}
type="icon" type="icon"
onClick={() => action.onSelect('quick-actions')} onClick={() => action.onSelect('quick-actions')}
disabled={!(editor.isIn('select') && editor.selectedShapeIds.length > 0)} disabled={!(editor.isIn('select') && editor.getSelectedShapeIds().length > 0)}
title={`${msg(action.label!)} ${kbdStr(action.kbd!)}`} title={`${msg(action.label!)} ${kbdStr(action.kbd!)}`}
smallIcon smallIcon
/> />

View file

@ -137,7 +137,7 @@ export function menuItem(
} }
function shapesWithUnboundArrows(editor: Editor) { function shapesWithUnboundArrows(editor: Editor) {
const { selectedShapeIds } = editor const selectedShapeIds = editor.getSelectedShapeIds()
const selectedShapes = selectedShapeIds.map((id) => { const selectedShapes = selectedShapeIds.map((id) => {
return editor.getShape(id) return editor.getShape(id)
}) })
@ -174,7 +174,7 @@ export const useAllowUngroup = () => {
const editor = useEditor() const editor = useEditor()
return useValue( return useValue(
'allowUngroup', 'allowUngroup',
() => editor.selectedShapeIds.some((id) => editor.getShape(id)?.type === 'group'), () => editor.getSelectedShapeIds().some((id) => editor.getShape(id)?.type === 'group'),
[editor] [editor]
) )
} }

View file

@ -95,7 +95,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
} }
function hasSelectedShapes() { function hasSelectedShapes() {
return editor.selectedShapeIds.length > 0 return editor.getSelectedShapeIds().length > 0
} }
const actions = makeActions([ const actions = makeActions([
@ -163,7 +163,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
readonlyOk: true, readonlyOk: true,
onSelect(source) { onSelect(source) {
trackEvent('export-as', { format: 'svg', source }) trackEvent('export-as', { format: 'svg', source })
exportAs(editor.selectedShapeIds, 'svg') exportAs(editor.getSelectedShapeIds(), 'svg')
}, },
}, },
{ {
@ -174,7 +174,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
readonlyOk: true, readonlyOk: true,
onSelect(source) { onSelect(source) {
trackEvent('export-as', { format: 'png', source }) trackEvent('export-as', { format: 'png', source })
exportAs(editor.selectedShapeIds, 'png') exportAs(editor.getSelectedShapeIds(), 'png')
}, },
}, },
{ {
@ -185,7 +185,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
readonlyOk: true, readonlyOk: true,
onSelect(source) { onSelect(source) {
trackEvent('export-as', { format: 'json', source }) trackEvent('export-as', { format: 'json', source })
exportAs(editor.selectedShapeIds, 'json') exportAs(editor.getSelectedShapeIds(), 'json')
}, },
}, },
{ {
@ -197,7 +197,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
readonlyOk: true, readonlyOk: true,
onSelect(source) { onSelect(source) {
trackEvent('copy-as', { format: 'svg', source }) trackEvent('copy-as', { format: 'svg', source })
copyAs(editor.selectedShapeIds, 'svg') copyAs(editor.getSelectedShapeIds(), 'svg')
}, },
}, },
{ {
@ -208,7 +208,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
readonlyOk: true, readonlyOk: true,
onSelect(source) { onSelect(source) {
trackEvent('copy-as', { format: 'png', source }) trackEvent('copy-as', { format: 'png', source })
copyAs(editor.selectedShapeIds, 'png') copyAs(editor.getSelectedShapeIds(), 'png')
}, },
}, },
{ {
@ -219,7 +219,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
readonlyOk: true, readonlyOk: true,
onSelect(source) { onSelect(source) {
trackEvent('copy-as', { format: 'json', source }) trackEvent('copy-as', { format: 'json', source })
copyAs(editor.selectedShapeIds, 'json') copyAs(editor.getSelectedShapeIds(), 'json')
}, },
}, },
{ {
@ -258,7 +258,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
readonlyOk: true, readonlyOk: true,
onSelect(source) { onSelect(source) {
trackEvent('open-embed-link', { source }) trackEvent('open-embed-link', { source })
const ids = editor.selectedShapeIds const ids = editor.getSelectedShapeIds()
const warnMsg = 'No embed shapes selected' const warnMsg = 'No embed shapes selected'
if (ids.length !== 1) { if (ids.length !== 1) {
console.error(warnMsg) console.error(warnMsg)
@ -344,7 +344,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
trackEvent('convert-to-embed', { source }) trackEvent('convert-to-embed', { source })
editor.batch(() => { editor.batch(() => {
const ids = editor.selectedShapeIds const ids = editor.getSelectedShapeIds()
const shapes = compact(ids.map((id) => editor.getShape(id))) const shapes = compact(ids.map((id) => editor.getShape(id)))
const createList: TLShapePartial[] = [] const createList: TLShapePartial[] = []
@ -400,7 +400,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
if (mustGoBackToSelectToolFirst()) return if (mustGoBackToSelectToolFirst()) return
trackEvent('duplicate-shapes', { source }) trackEvent('duplicate-shapes', { source })
const ids = editor.selectedShapeIds const ids = editor.getSelectedShapeIds()
const commonBounds = Box2d.Common(compact(ids.map((id) => editor.getShapePageBounds(id)))) const commonBounds = Box2d.Common(compact(ids.map((id) => editor.getShapePageBounds(id))))
const offset = editor.getInstanceState().canMoveCamera const offset = editor.getInstanceState().canMoveCamera
? { ? {
@ -427,7 +427,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
trackEvent('ungroup-shapes', { source }) trackEvent('ungroup-shapes', { source })
editor.mark('ungroup') editor.mark('ungroup')
editor.ungroupShapes(editor.selectedShapeIds) editor.ungroupShapes(editor.getSelectedShapeIds())
}, },
}, },
{ {
@ -444,10 +444,10 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
const { onlySelectedShape } = editor const { onlySelectedShape } = editor
if (onlySelectedShape && editor.isShapeOfType<TLGroupShape>(onlySelectedShape, 'group')) { if (onlySelectedShape && editor.isShapeOfType<TLGroupShape>(onlySelectedShape, 'group')) {
editor.mark('ungroup') editor.mark('ungroup')
editor.ungroupShapes(editor.selectedShapeIds) editor.ungroupShapes(editor.getSelectedShapeIds())
} else { } else {
editor.mark('group') editor.mark('group')
editor.groupShapes(editor.selectedShapeIds) editor.groupShapes(editor.getSelectedShapeIds())
} }
}, },
}, },
@ -463,7 +463,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
trackEvent('align-shapes', { operation: 'left', source }) trackEvent('align-shapes', { operation: 'left', source })
editor.mark('align left') editor.mark('align left')
editor.alignShapes(editor.selectedShapeIds, 'left') editor.alignShapes(editor.getSelectedShapeIds(), 'left')
}, },
}, },
{ {
@ -479,7 +479,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
trackEvent('align-shapes', { operation: 'center-horizontal', source }) trackEvent('align-shapes', { operation: 'center-horizontal', source })
editor.mark('align center horizontal') editor.mark('align center horizontal')
editor.alignShapes(editor.selectedShapeIds, 'center-horizontal') editor.alignShapes(editor.getSelectedShapeIds(), 'center-horizontal')
}, },
}, },
{ {
@ -494,7 +494,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
trackEvent('align-shapes', { operation: 'right', source }) trackEvent('align-shapes', { operation: 'right', source })
editor.mark('align right') editor.mark('align right')
editor.alignShapes(editor.selectedShapeIds, 'right') editor.alignShapes(editor.getSelectedShapeIds(), 'right')
}, },
}, },
{ {
@ -510,7 +510,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
trackEvent('align-shapes', { operation: 'center-vertical', source }) trackEvent('align-shapes', { operation: 'center-vertical', source })
editor.mark('align center vertical') editor.mark('align center vertical')
editor.alignShapes(editor.selectedShapeIds, 'center-vertical') editor.alignShapes(editor.getSelectedShapeIds(), 'center-vertical')
}, },
}, },
{ {
@ -525,7 +525,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
trackEvent('align-shapes', { operation: 'top', source }) trackEvent('align-shapes', { operation: 'top', source })
editor.mark('align top') editor.mark('align top')
editor.alignShapes(editor.selectedShapeIds, 'top') editor.alignShapes(editor.getSelectedShapeIds(), 'top')
}, },
}, },
{ {
@ -540,7 +540,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
trackEvent('align-shapes', { operation: 'bottom', source }) trackEvent('align-shapes', { operation: 'bottom', source })
editor.mark('align bottom') editor.mark('align bottom')
editor.alignShapes(editor.selectedShapeIds, 'bottom') editor.alignShapes(editor.getSelectedShapeIds(), 'bottom')
}, },
}, },
{ {
@ -556,7 +556,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
trackEvent('distribute-shapes', { operation: 'horizontal', source }) trackEvent('distribute-shapes', { operation: 'horizontal', source })
editor.mark('distribute horizontal') editor.mark('distribute horizontal')
editor.distributeShapes(editor.selectedShapeIds, 'horizontal') editor.distributeShapes(editor.getSelectedShapeIds(), 'horizontal')
}, },
}, },
{ {
@ -572,7 +572,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
trackEvent('distribute-shapes', { operation: 'vertical', source }) trackEvent('distribute-shapes', { operation: 'vertical', source })
editor.mark('distribute vertical') editor.mark('distribute vertical')
editor.distributeShapes(editor.selectedShapeIds, 'vertical') editor.distributeShapes(editor.getSelectedShapeIds(), 'vertical')
}, },
}, },
{ {
@ -587,7 +587,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
trackEvent('stretch-shapes', { operation: 'horizontal', source }) trackEvent('stretch-shapes', { operation: 'horizontal', source })
editor.mark('stretch horizontal') editor.mark('stretch horizontal')
editor.stretchShapes(editor.selectedShapeIds, 'horizontal') editor.stretchShapes(editor.getSelectedShapeIds(), 'horizontal')
}, },
}, },
{ {
@ -602,7 +602,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
trackEvent('stretch-shapes', { operation: 'vertical', source }) trackEvent('stretch-shapes', { operation: 'vertical', source })
editor.mark('stretch vertical') editor.mark('stretch vertical')
editor.stretchShapes(editor.selectedShapeIds, 'vertical') editor.stretchShapes(editor.getSelectedShapeIds(), 'vertical')
}, },
}, },
{ {
@ -617,7 +617,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
trackEvent('flip-shapes', { operation: 'horizontal', source }) trackEvent('flip-shapes', { operation: 'horizontal', source })
editor.mark('flip horizontal') editor.mark('flip horizontal')
editor.flipShapes(editor.selectedShapeIds, 'horizontal') editor.flipShapes(editor.getSelectedShapeIds(), 'horizontal')
}, },
}, },
{ {
@ -632,7 +632,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
trackEvent('flip-shapes', { operation: 'vertical', source }) trackEvent('flip-shapes', { operation: 'vertical', source })
editor.mark('flip vertical') editor.mark('flip vertical')
editor.flipShapes(editor.selectedShapeIds, 'vertical') editor.flipShapes(editor.getSelectedShapeIds(), 'vertical')
}, },
}, },
{ {
@ -646,7 +646,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
trackEvent('pack-shapes', { source }) trackEvent('pack-shapes', { source })
editor.mark('pack') editor.mark('pack')
editor.packShapes(editor.selectedShapeIds, 16) editor.packShapes(editor.getSelectedShapeIds(), 16)
}, },
}, },
{ {
@ -661,7 +661,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
trackEvent('stack-shapes', { operation: 'vertical', source }) trackEvent('stack-shapes', { operation: 'vertical', source })
editor.mark('stack-vertical') editor.mark('stack-vertical')
editor.stackShapes(editor.selectedShapeIds, 'vertical', 16) editor.stackShapes(editor.getSelectedShapeIds(), 'vertical', 16)
}, },
}, },
{ {
@ -676,7 +676,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
trackEvent('stack-shapes', { operation: 'horizontal', source }) trackEvent('stack-shapes', { operation: 'horizontal', source })
editor.mark('stack-horizontal') editor.mark('stack-horizontal')
editor.stackShapes(editor.selectedShapeIds, 'horizontal', 16) editor.stackShapes(editor.getSelectedShapeIds(), 'horizontal', 16)
}, },
}, },
{ {
@ -691,7 +691,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
trackEvent('reorder-shapes', { operation: 'toFront', source }) trackEvent('reorder-shapes', { operation: 'toFront', source })
editor.mark('bring to front') editor.mark('bring to front')
editor.bringToFront(editor.selectedShapeIds) editor.bringToFront(editor.getSelectedShapeIds())
}, },
}, },
{ {
@ -706,7 +706,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
trackEvent('reorder-shapes', { operation: 'forward', source }) trackEvent('reorder-shapes', { operation: 'forward', source })
editor.mark('bring forward') editor.mark('bring forward')
editor.bringForward(editor.selectedShapeIds) editor.bringForward(editor.getSelectedShapeIds())
}, },
}, },
{ {
@ -721,7 +721,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
trackEvent('reorder-shapes', { operation: 'backward', source }) trackEvent('reorder-shapes', { operation: 'backward', source })
editor.mark('send backward') editor.mark('send backward')
editor.sendBackward(editor.selectedShapeIds) editor.sendBackward(editor.getSelectedShapeIds())
}, },
}, },
{ {
@ -736,7 +736,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
trackEvent('reorder-shapes', { operation: 'toBack', source }) trackEvent('reorder-shapes', { operation: 'toBack', source })
editor.mark('send to back') editor.mark('send to back')
editor.sendToBack(editor.selectedShapeIds) editor.sendToBack(editor.getSelectedShapeIds())
}, },
}, },
{ {
@ -820,7 +820,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
trackEvent('delete-shapes', { source }) trackEvent('delete-shapes', { source })
editor.mark('delete') editor.mark('delete')
editor.deleteShapes(editor.selectedShapeIds) editor.deleteShapes(editor.getSelectedShapeIds())
}, },
}, },
{ {
@ -836,7 +836,10 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
editor.mark('rotate-cw') editor.mark('rotate-cw')
const offset = editor.selectionRotation % (TAU / 2) const offset = editor.selectionRotation % (TAU / 2)
const dontUseOffset = approximately(offset, 0) || approximately(offset, TAU / 2) const dontUseOffset = approximately(offset, 0) || approximately(offset, TAU / 2)
editor.rotateShapesBy(editor.selectedShapeIds, TAU / 2 - (dontUseOffset ? 0 : offset)) editor.rotateShapesBy(
editor.getSelectedShapeIds(),
TAU / 2 - (dontUseOffset ? 0 : offset)
)
}, },
}, },
{ {
@ -852,7 +855,10 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
editor.mark('rotate-ccw') editor.mark('rotate-ccw')
const offset = editor.selectionRotation % (TAU / 2) const offset = editor.selectionRotation % (TAU / 2)
const offsetCloseToZero = approximately(offset, 0) const offsetCloseToZero = approximately(offset, 0)
editor.rotateShapesBy(editor.selectedShapeIds, offsetCloseToZero ? -(TAU / 2) : -offset) editor.rotateShapesBy(
editor.getSelectedShapeIds(),
offsetCloseToZero ? -(TAU / 2) : -offset
)
}, },
}, },
{ {
@ -1084,7 +1090,7 @@ export function ActionsProvider({ overrides, children }: ActionsProviderProps) {
onSelect(source) { onSelect(source) {
editor.mark('locking') editor.mark('locking')
trackEvent('toggle-lock', { source }) trackEvent('toggle-lock', { source })
editor.toggleLock(editor.selectedShapeIds) editor.toggleLock(editor.getSelectedShapeIds())
}, },
}, },
]) ])

View file

@ -40,7 +40,9 @@ export const ActionsMenuSchemaProvider = ({
const editor = useEditor() const editor = useEditor()
const actions = useActions() const actions = useActions()
const selectedCount = useValue('selected count', () => editor.selectedShapeIds.length, [editor]) const selectedCount = useValue('selected count', () => editor.getSelectedShapeIds().length, [
editor,
])
const oneSelected = selectedCount > 0 const oneSelected = selectedCount > 0
const twoSelected = selectedCount > 1 const twoSelected = selectedCount > 1

View file

@ -74,7 +74,7 @@ const INPUTS = ['input', 'select', 'textarea']
function disallowClipboardEvents(editor: Editor) { function disallowClipboardEvents(editor: Editor) {
const { activeElement } = document const { activeElement } = document
return ( return (
editor.isMenuOpen || editor.getIsMenuOpen() ||
(activeElement && (activeElement &&
(activeElement.getAttribute('contenteditable') || (activeElement.getAttribute('contenteditable') ||
INPUTS.indexOf(activeElement.tagName.toLowerCase()) > -1)) INPUTS.indexOf(activeElement.tagName.toLowerCase()) > -1))
@ -497,7 +497,7 @@ async function handleClipboardThings(editor: Editor, things: ClipboardThing[], p
* @public * @public
*/ */
const handleNativeOrMenuCopy = (editor: Editor) => { const handleNativeOrMenuCopy = (editor: Editor) => {
const content = editor.getContentFromCurrentPage(editor.selectedShapeIds) const content = editor.getContentFromCurrentPage(editor.getSelectedShapeIds())
if (!content) { if (!content) {
if (navigator && navigator.clipboard) { if (navigator && navigator.clipboard) {
navigator.clipboard.writeText('') navigator.clipboard.writeText('')
@ -570,7 +570,7 @@ export function useMenuClipboardEvents() {
const copy = useCallback( const copy = useCallback(
function onCopy(source: TLUiEventSource) { function onCopy(source: TLUiEventSource) {
if (editor.selectedShapeIds.length === 0) return if (editor.getSelectedShapeIds().length === 0) return
handleNativeOrMenuCopy(editor) handleNativeOrMenuCopy(editor)
trackEvent('copy', { source }) trackEvent('copy', { source })
@ -580,10 +580,10 @@ export function useMenuClipboardEvents() {
const cut = useCallback( const cut = useCallback(
function onCut(source: TLUiEventSource) { function onCut(source: TLUiEventSource) {
if (editor.selectedShapeIds.length === 0) return if (editor.getSelectedShapeIds().length === 0) return
handleNativeOrMenuCopy(editor) handleNativeOrMenuCopy(editor)
editor.deleteShapes(editor.selectedShapeIds) editor.deleteShapes(editor.getSelectedShapeIds())
trackEvent('cut', { source }) trackEvent('cut', { source })
}, },
[editor, trackEvent] [editor, trackEvent]
@ -633,7 +633,7 @@ export function useNativeClipboardEvents() {
if (!appIsFocused) return if (!appIsFocused) return
const copy = () => { const copy = () => {
if ( if (
editor.selectedShapeIds.length === 0 || editor.getSelectedShapeIds().length === 0 ||
editor.editingShapeId !== null || editor.editingShapeId !== null ||
disallowClipboardEvents(editor) disallowClipboardEvents(editor)
) )
@ -644,13 +644,13 @@ export function useNativeClipboardEvents() {
function cut() { function cut() {
if ( if (
editor.selectedShapeIds.length === 0 || editor.getSelectedShapeIds().length === 0 ||
editor.editingShapeId !== null || editor.editingShapeId !== null ||
disallowClipboardEvents(editor) disallowClipboardEvents(editor)
) )
return return
handleNativeOrMenuCopy(editor) handleNativeOrMenuCopy(editor)
editor.deleteShapes(editor.selectedShapeIds) editor.deleteShapes(editor.getSelectedShapeIds())
trackEvent('cut', { source: 'kbd' }) trackEvent('cut', { source: 'kbd' })
} }

View file

@ -55,7 +55,7 @@ export const TLUiContextMenuSchemaProvider = track(function TLUiContextMenuSchem
const onlyFlippableShapeSelected = useOnlyFlippableShape() const onlyFlippableShapeSelected = useOnlyFlippableShape()
const selectedCount = editor.selectedShapeIds.length const selectedCount = editor.getSelectedShapeIds().length
const oneSelected = selectedCount > 0 const oneSelected = selectedCount > 0

View file

@ -18,7 +18,7 @@ export function useCopyAs() {
// //
// this is fine for navigator.clipboard.write, but for fallbacks it's a // this is fine for navigator.clipboard.write, but for fallbacks it's a
// little awkward. // little awkward.
function copyAs(ids: TLShapeId[] = editor.selectedShapeIds, format: TLCopyType = 'svg') { function copyAs(ids: TLShapeId[] = editor.getSelectedShapeIds(), format: TLCopyType = 'svg') {
if (ids.length === 0) { if (ids.length === 0) {
ids = [...editor.currentPageShapeIds] ids = [...editor.currentPageShapeIds]
} }

View file

@ -17,7 +17,7 @@ export function useExportAs() {
return useCallback( return useCallback(
async function exportAs( async function exportAs(
ids: TLShapeId[] = editor.selectedShapeIds, ids: TLShapeId[] = editor.getSelectedShapeIds(),
format: TLExportType = 'png' format: TLExportType = 'png'
) { ) {
if (ids.length === 0) { if (ids.length === 0) {

View file

@ -37,7 +37,7 @@ export const HelpMenuSchemaProvider = track(function HelpMenuSchemaProvider({
const editor = useEditor() const editor = useEditor()
const actions = useActions() const actions = useActions()
const selectedCount = editor.selectedShapeIds.length const selectedCount = editor.getSelectedShapeIds().length
const oneSelected = selectedCount > 0 const oneSelected = selectedCount > 0
const twoSelected = selectedCount > 1 const twoSelected = selectedCount > 1

View file

@ -37,7 +37,7 @@ export function useKeyboardShortcuts() {
// Add hotkeys for actions and tools. // Add hotkeys for actions and tools.
// Except those that in SKIP_KBDS! // Except those that in SKIP_KBDS!
const areShortcutsDisabled = () => const areShortcutsDisabled = () =>
editor.isMenuOpen || editor.editingShapeId !== null || editor.crashingError editor.getIsMenuOpen() || editor.editingShapeId !== null || editor.crashingError
for (const action of Object.values(actions)) { for (const action of Object.values(actions)) {
if (!action.kbd) continue if (!action.kbd) continue

View file

@ -64,7 +64,9 @@ export function TLUiMenuSchemaProvider({ overrides, children }: TLUiMenuSchemaPr
const emptyPage = useValue('emptyPage', () => editor.currentPageShapeIds.size === 0, [editor]) const emptyPage = useValue('emptyPage', () => editor.currentPageShapeIds.size === 0, [editor])
const selectedCount = useValue('selectedCount', () => editor.selectedShapeIds.length, [editor]) const selectedCount = useValue('selectedCount', () => editor.getSelectedShapeIds().length, [
editor,
])
const noneSelected = selectedCount === 0 const noneSelected = selectedCount === 0
const oneSelected = selectedCount > 0 const oneSelected = selectedCount > 0
const twoSelected = selectedCount > 1 const twoSelected = selectedCount > 1

View file

@ -171,7 +171,8 @@ export function usePrint() {
} }
} }
const { pages, currentPageId, selectedShapeIds } = editor const selectedShapeIds = editor.getSelectedShapeIds()
const { pages, currentPageId } = editor
const preserveAspectRatio = 'xMidYMid meet' const preserveAspectRatio = 'xMidYMid meet'
@ -182,7 +183,7 @@ export function usePrint() {
preserveAspectRatio, preserveAspectRatio,
} }
if (editor.selectedShapeIds.length > 0) { if (editor.getSelectedShapeIds().length > 0) {
// Print the selected ids from the current page // Print the selected ids from the current page
const svg = await editor.getSvg(selectedShapeIds, svgOpts) const svg = await editor.getSvg(selectedShapeIds, svgOpts)

View file

@ -21,9 +21,10 @@ export function useRelevantStyles(): {
'getRelevantStyles', 'getRelevantStyles',
() => { () => {
const styles = new SharedStyleMap(editor.sharedStyles) const styles = new SharedStyleMap(editor.sharedStyles)
const hasShape = editor.selectedShapeIds.length > 0 || !!editor.root.current.get()?.shapeType const hasShape =
editor.getSelectedShapeIds().length > 0 || !!editor.root.current.get()?.shapeType
if (styles.size === 0 && editor.isIn('select') && editor.selectedShapeIds.length === 0) { if (styles.size === 0 && editor.isIn('select') && editor.getSelectedShapeIds().length === 0) {
for (const style of selectToolStyles) { for (const style of selectToolStyles) {
styles.applyValue(style, editor.getStyleForNextShape(style)) styles.applyValue(style, editor.getStyleForNextShape(style))
} }

View file

@ -48,7 +48,7 @@ export function buildFromV1Document(editor: Editor, document: LegacyTldrawDocume
// Delete all of the shapes on the current page // Delete all of the shapes on the current page
editor.selectAll() editor.selectAll()
editor.deleteShapes(editor.selectedShapeIds) editor.deleteShapes(editor.getSelectedShapeIds())
// Create assets // Create assets
const v1AssetIdsToV2AssetIds = new Map<string, TLAssetId>() const v1AssetIdsToV2AssetIds = new Map<string, TLAssetId>()

View file

@ -118,15 +118,15 @@ describe('shapes that are moved to another page', () => {
describe("should be excluded from the previous page's selectedShapeIds", () => { describe("should be excluded from the previous page's selectedShapeIds", () => {
test('[boxes]', () => { test('[boxes]', () => {
editor.setSelectedShapes([ids.box1, ids.box2, ids.box3]) editor.setSelectedShapes([ids.box1, ids.box2, ids.box3])
expect(editor.selectedShapeIds).toEqual([ids.box1, ids.box2, ids.box3]) expect(editor.getSelectedShapeIds()).toEqual([ids.box1, ids.box2, ids.box3])
moveShapesToPage2() moveShapesToPage2()
expect(editor.selectedShapeIds).toEqual([]) expect(editor.getSelectedShapeIds()).toEqual([])
}) })
test('[frame that does not move]', () => { test('[frame that does not move]', () => {
editor.setSelectedShapes([ids.frame1]) editor.setSelectedShapes([ids.frame1])
expect(editor.selectedShapeIds).toEqual([ids.frame1]) expect(editor.getSelectedShapeIds()).toEqual([ids.frame1])
moveShapesToPage2() moveShapesToPage2()
expect(editor.selectedShapeIds).toEqual([ids.frame1]) expect(editor.getSelectedShapeIds()).toEqual([ids.frame1])
}) })
}) })
}) })

View file

@ -15,7 +15,7 @@ beforeEach(() => {
editor = new TestEditor() editor = new TestEditor()
editor editor
.selectAll() .selectAll()
.deleteShapes(editor.selectedShapeIds) .deleteShapes(editor.getSelectedShapeIds())
.createShapes([{ id: ids.box1, type: 'geo', x: 100, y: 100, props: { w: 100, h: 100 } }]) .createShapes([{ id: ids.box1, type: 'geo', x: 100, y: 100, props: { w: 100, h: 100 } }])
}) })
@ -100,7 +100,7 @@ describe('TLSelectTool.Translating', () => {
// shift-alt-drag the original, we shouldn't duplicate the copy too: // shift-alt-drag the original, we shouldn't duplicate the copy too:
editor.pointerDown(150, 150, { target: 'shape', shape }) editor.pointerDown(150, 150, { target: 'shape', shape })
expect(editor.selectedShapeIds).toStrictEqual([ids.box1]) expect(editor.getSelectedShapeIds()).toStrictEqual([ids.box1])
editor.pointerMove(250, 150) editor.pointerMove(250, 150)
editor.pointerUp() editor.pointerUp()
expect(editor.currentPageShapes.length).toStrictEqual(3) expect(editor.currentPageShapes.length).toStrictEqual(3)
@ -170,7 +170,7 @@ describe('When double clicking a shape', () => {
it('begins editing a geo shapes label', () => { it('begins editing a geo shapes label', () => {
editor editor
.selectAll() .selectAll()
.deleteShapes(editor.selectedShapeIds) .deleteShapes(editor.getSelectedShapeIds())
.selectNone() .selectNone()
.createShapes([{ id: createShapeId(), type: 'geo' }]) .createShapes([{ id: createShapeId(), type: 'geo' }])
.doubleClick(50, 50, { target: 'shape', shape: editor.currentPageShapes[0] }) .doubleClick(50, 50, { target: 'shape', shape: editor.currentPageShapes[0] })
@ -183,7 +183,7 @@ describe('When pressing enter on a selected shape', () => {
const id = createShapeId() const id = createShapeId()
editor editor
.selectAll() .selectAll()
.deleteShapes(editor.selectedShapeIds) .deleteShapes(editor.getSelectedShapeIds())
.selectNone() .selectNone()
.createShapes([{ id, type: 'geo' }]) .createShapes([{ id, type: 'geo' }])
.select(id) .select(id)
@ -214,7 +214,7 @@ describe('When double clicking the selection edge', () => {
const id = createShapeId() const id = createShapeId()
editor editor
.selectAll() .selectAll()
.deleteShapes(editor.selectedShapeIds) .deleteShapes(editor.getSelectedShapeIds())
.selectNone() .selectNone()
.createShapes([{ id, type: 'text', x: 100, y: 100, props: { scale: 2, text: 'hello' } }]) .createShapes([{ id, type: 'text', x: 100, y: 100, props: { scale: 2, text: 'hello' } }])
.select(id) .select(id)
@ -227,7 +227,7 @@ describe('When double clicking the selection edge', () => {
const id = createShapeId() const id = createShapeId()
editor editor
.selectAll() .selectAll()
.deleteShapes(editor.selectedShapeIds) .deleteShapes(editor.getSelectedShapeIds())
.selectNone() .selectNone()
.createShapes([ .createShapes([
{ {
@ -250,7 +250,7 @@ describe('When double clicking the selection edge', () => {
const id = createShapeId() const id = createShapeId()
editor editor
.selectAll() .selectAll()
.deleteShapes(editor.selectedShapeIds) .deleteShapes(editor.getSelectedShapeIds())
.selectNone() .selectNone()
.createShapes([ .createShapes([
{ {
@ -275,7 +275,7 @@ describe('When double clicking the selection edge', () => {
const id = createShapeId() const id = createShapeId()
editor editor
.selectAll() .selectAll()
.deleteShapes(editor.selectedShapeIds) .deleteShapes(editor.getSelectedShapeIds())
.selectNone() .selectNone()
.createShapes([ .createShapes([
{ {
@ -313,7 +313,7 @@ describe('When editing shapes', () => {
it('Pointing a shape of a different type selects it and leaves editing', () => { it('Pointing a shape of a different type selects it and leaves editing', () => {
expect(editor.editingShapeId).toBe(null) expect(editor.editingShapeId).toBe(null)
expect(editor.selectedShapeIds.length).toBe(0) expect(editor.getSelectedShapeIds().length).toBe(0)
// 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) })
@ -329,7 +329,7 @@ describe('When editing shapes', () => {
// because useEditableText implements the behavior in React // because useEditableText implements the behavior in React
it.skip('Pointing a shape of a different type selects it and leaves editing', () => { it.skip('Pointing a shape of a different type selects it and leaves editing', () => {
expect(editor.editingShapeId).toBe(null) expect(editor.editingShapeId).toBe(null)
expect(editor.selectedShapeIds.length).toBe(0) expect(editor.getSelectedShapeIds().length).toBe(0)
// 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) })
@ -345,7 +345,7 @@ describe('When editing shapes', () => {
// 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
it.skip('Works with text, too', () => { it.skip('Works with text, too', () => {
expect(editor.editingShapeId).toBe(null) expect(editor.editingShapeId).toBe(null)
expect(editor.selectedShapeIds.length).toBe(0) expect(editor.getSelectedShapeIds().length).toBe(0)
// start editing the geo shape // start editing the geo shape
editor.doubleClick(50, 50, { target: 'shape', shape: editor.getShape(ids.text1) }) editor.doubleClick(50, 50, { target: 'shape', shape: editor.getShape(ids.text1) })
@ -357,7 +357,7 @@ describe('When editing shapes', () => {
it('Double clicking the canvas creates a new text shape', () => { it('Double clicking the canvas creates a new text shape', () => {
expect(editor.editingShapeId).toBe(null) expect(editor.editingShapeId).toBe(null)
expect(editor.selectedShapeIds.length).toBe(0) expect(editor.getSelectedShapeIds().length).toBe(0)
expect(editor.currentPageShapes.length).toBe(5) expect(editor.currentPageShapes.length).toBe(5)
editor.doubleClick(750, 750) editor.doubleClick(750, 750)
expect(editor.currentPageShapes.length).toBe(6) expect(editor.currentPageShapes.length).toBe(6)
@ -366,37 +366,37 @@ describe('When editing shapes', () => {
it('It deletes an empty text shape when your click away', () => { it('It deletes an empty text shape when your click away', () => {
expect(editor.editingShapeId).toBe(null) expect(editor.editingShapeId).toBe(null)
expect(editor.selectedShapeIds.length).toBe(0) expect(editor.getSelectedShapeIds().length).toBe(0)
expect(editor.currentPageShapes.length).toBe(5) expect(editor.currentPageShapes.length).toBe(5)
// Create a new shape by double clicking // Create a new shape by double clicking
editor.doubleClick(750, 750) editor.doubleClick(750, 750)
expect(editor.selectedShapeIds.length).toBe(1) expect(editor.getSelectedShapeIds().length).toBe(1)
expect(editor.currentPageShapes.length).toBe(6) expect(editor.currentPageShapes.length).toBe(6)
const shapeId = editor.selectedShapeIds[0] const shapeId = editor.getSelectedShapeIds()[0]
// Click away // Click away
editor.click(1000, 1000) editor.click(1000, 1000)
expect(editor.selectedShapeIds.length).toBe(0) expect(editor.getSelectedShapeIds().length).toBe(0)
expect(editor.currentPageShapes.length).toBe(5) expect(editor.currentPageShapes.length).toBe(5)
expect(editor.getShape(shapeId)).toBe(undefined) expect(editor.getShape(shapeId)).toBe(undefined)
}) })
it('It deletes an empty text shape when your click another text shape', () => { it('It deletes an empty text shape when your click another text shape', () => {
expect(editor.editingShapeId).toBe(null) expect(editor.editingShapeId).toBe(null)
expect(editor.selectedShapeIds.length).toBe(0) expect(editor.getSelectedShapeIds().length).toBe(0)
expect(editor.currentPageShapes.length).toBe(5) expect(editor.currentPageShapes.length).toBe(5)
// Create a new shape by double clicking // Create a new shape by double clicking
editor.doubleClick(750, 750) editor.doubleClick(750, 750)
expect(editor.selectedShapeIds.length).toBe(1) expect(editor.getSelectedShapeIds().length).toBe(1)
expect(editor.currentPageShapes.length).toBe(6) expect(editor.currentPageShapes.length).toBe(6)
const shapeId = editor.selectedShapeIds[0] const shapeId = editor.getSelectedShapeIds()[0]
// Click another text shape // Click another text shape
editor.pointerMove(50, 50) editor.pointerMove(50, 50)
editor.click() editor.click()
expect(editor.selectedShapeIds.length).toBe(1) expect(editor.getSelectedShapeIds().length).toBe(1)
expect(editor.currentPageShapes.length).toBe(5) expect(editor.currentPageShapes.length).toBe(5)
expect(editor.getShape(shapeId)).toBe(undefined) expect(editor.getShape(shapeId)).toBe(undefined)
}) })
@ -423,7 +423,7 @@ describe('When in readonly mode', () => {
it('Begins editing embed when double clicked', () => { it('Begins editing embed when double clicked', () => {
expect(editor.editingShapeId).toBe(null) expect(editor.editingShapeId).toBe(null)
expect(editor.selectedShapeIds.length).toBe(0) expect(editor.getSelectedShapeIds().length).toBe(0)
expect(editor.getInstanceState().isReadonly).toBe(true) expect(editor.getInstanceState().isReadonly).toBe(true)
const shape = editor.getShape(ids.embed1) const shape = editor.getShape(ids.embed1)
@ -433,11 +433,11 @@ describe('When in readonly mode', () => {
it('Begins editing embed when pressing Enter on a selected embed', () => { it('Begins editing embed when pressing Enter on a selected embed', () => {
expect(editor.editingShapeId).toBe(null) expect(editor.editingShapeId).toBe(null)
expect(editor.selectedShapeIds.length).toBe(0) expect(editor.getSelectedShapeIds().length).toBe(0)
expect(editor.getInstanceState().isReadonly).toBe(true) expect(editor.getInstanceState().isReadonly).toBe(true)
editor.setSelectedShapes([ids.embed1]) editor.setSelectedShapes([ids.embed1])
expect(editor.selectedShapeIds.length).toBe(1) expect(editor.getSelectedShapeIds().length).toBe(1)
editor.keyUp('Enter') editor.keyUp('Enter')
expect(editor.editingShapeId).toBe(ids.embed1) expect(editor.editingShapeId).toBe(ids.embed1)

View file

@ -131,7 +131,7 @@ export class TestEditor extends Editor {
clipboard = null as TLContent | null clipboard = null as TLContent | null
copy = (ids = this.selectedShapeIds) => { copy = (ids = this.getSelectedShapeIds()) => {
if (ids.length > 0) { if (ids.length > 0) {
const content = this.getContentFromCurrentPage(ids) const content = this.getContentFromCurrentPage(ids)
if (content) { if (content) {
@ -141,7 +141,7 @@ export class TestEditor extends Editor {
return this return this
} }
cut = (ids = this.selectedShapeIds) => { cut = (ids = this.getSelectedShapeIds()) => {
if (ids.length > 0) { if (ids.length > 0) {
const content = this.getContentFromCurrentPage(ids) const content = this.getContentFromCurrentPage(ids)
if (content) { if (content) {
@ -471,7 +471,7 @@ export class TestEditor extends Editor {
shiftKey = false, shiftKey = false,
}: { handle?: RotateCorner; shiftKey?: boolean } = {} }: { handle?: RotateCorner; shiftKey?: boolean } = {}
) { ) {
if (this.selectedShapeIds.length === 0) { if (this.getSelectedShapeIds().length === 0) {
throw new Error('No selection') throw new Error('No selection')
} }
@ -504,14 +504,14 @@ export class TestEditor extends Editor {
} }
translateSelection(dx: number, dy: number, options?: Partial<TLPointerEventInfo>) { translateSelection(dx: number, dy: number, options?: Partial<TLPointerEventInfo>) {
if (this.selectedShapeIds.length === 0) { if (this.getSelectedShapeIds().length === 0) {
throw new Error('No selection') throw new Error('No selection')
} }
this.setCurrentTool('select') this.setCurrentTool('select')
const center = this.selectionPageCenter! const center = this.selectionPageCenter!
this.pointerDown(center.x, center.y, this.selectedShapeIds[0]) this.pointerDown(center.x, center.y, this.getSelectedShapeIds()[0])
const numSteps = 10 const numSteps = 10
for (let i = 1; i < numSteps; i++) { for (let i = 1; i < numSteps; i++) {
this.pointerMove(center.x + (i * dx) / numSteps, center.y + (i * dy) / numSteps, options) this.pointerMove(center.x + (i * dx) / numSteps, center.y + (i * dy) / numSteps, options)
@ -525,7 +525,7 @@ export class TestEditor extends Editor {
handle: SelectionHandle, handle: SelectionHandle,
options?: Partial<TLPointerEventInfo> options?: Partial<TLPointerEventInfo>
) { ) {
if (this.selectedShapeIds.length === 0) { if (this.getSelectedShapeIds().length === 0) {
throw new Error('No selection') throw new Error('No selection')
} }
this.setCurrentTool('select') this.setCurrentTool('select')

View file

@ -255,7 +255,7 @@ describe('<TldrawEditor />', () => {
// Select the shape // Select the shape
await act(async () => editor.select(id)) await act(async () => editor.select(id))
expect(editor.selectedShapeIds.length).toBe(1) expect(editor.getSelectedShapeIds().length).toBe(1)
// Is the shape's component rendering? // Is the shape's component rendering?
expect(document.querySelectorAll('.tl-shape-indicator')).toHaveLength(1) expect(document.querySelectorAll('.tl-shape-indicator')).toHaveLength(1)

View file

@ -13,7 +13,7 @@ beforeEach(() => {
editor = new TestEditor() editor = new TestEditor()
editor editor
.selectAll() .selectAll()
.deleteShapes(editor.selectedShapeIds) .deleteShapes(editor.getSelectedShapeIds())
.createShapes([{ id: ids.box1, type: 'geo', x: 100, y: 100, props: { w: 100, h: 100 } }]) .createShapes([{ id: ids.box1, type: 'geo', x: 100, y: 100, props: { w: 100, h: 100 } }])
}) })

View file

@ -234,7 +234,7 @@ describe('arrowBindingsIndex', () => {
expect(editor.getArrowsBoundTo(ids.box1)).toHaveLength(3) expect(editor.getArrowsBoundTo(ids.box1)).toHaveLength(3)
editor.selectAll() editor.selectAll()
editor.duplicateShapes(editor.selectedShapeIds) editor.duplicateShapes(editor.getSelectedShapeIds())
const [box1Clone, box2Clone] = editor.selectedShapes const [box1Clone, box2Clone] = editor.selectedShapes
.filter((shape) => editor.isShapeOfType<TLGeoShape>(shape, 'geo')) .filter((shape) => editor.isShapeOfType<TLGeoShape>(shape, 'geo'))

View file

@ -24,7 +24,7 @@ describe('when less than two shapes are selected', () => {
const fn = jest.fn() const fn = jest.fn()
editor.on('update', fn) editor.on('update', fn)
editor.alignShapes(editor.selectedShapeIds, 'top') editor.alignShapes(editor.getSelectedShapeIds(), 'top')
jest.advanceTimersByTime(1000) jest.advanceTimersByTime(1000)
expect(fn).not.toHaveBeenCalled() expect(fn).not.toHaveBeenCalled()
}) })
@ -33,7 +33,7 @@ describe('when less than two shapes are selected', () => {
describe('when multiple shapes are selected', () => { describe('when multiple shapes are selected', () => {
it('does, undoes and redoes command', () => { it('does, undoes and redoes command', () => {
editor.mark('align') editor.mark('align')
editor.alignShapes(editor.selectedShapeIds, 'top') editor.alignShapes(editor.getSelectedShapeIds(), 'top')
jest.advanceTimersByTime(1000) jest.advanceTimersByTime(1000)
editor.expectShapeToMatch({ id: ids.boxB, y: 0 }) editor.expectShapeToMatch({ id: ids.boxB, y: 0 })
@ -44,7 +44,7 @@ describe('when multiple shapes are selected', () => {
}) })
it('aligns top', () => { it('aligns top', () => {
editor.alignShapes(editor.selectedShapeIds, 'top') editor.alignShapes(editor.getSelectedShapeIds(), 'top')
jest.advanceTimersByTime(1000) jest.advanceTimersByTime(1000)
editor.expectShapeToMatch( editor.expectShapeToMatch(
@ -55,7 +55,7 @@ describe('when multiple shapes are selected', () => {
}) })
it('aligns right', () => { it('aligns right', () => {
editor.alignShapes(editor.selectedShapeIds, 'right') editor.alignShapes(editor.getSelectedShapeIds(), 'right')
jest.advanceTimersByTime(1000) jest.advanceTimersByTime(1000)
editor.expectShapeToMatch( editor.expectShapeToMatch(
@ -66,7 +66,7 @@ describe('when multiple shapes are selected', () => {
}) })
it('aligns bottom', () => { it('aligns bottom', () => {
editor.alignShapes(editor.selectedShapeIds, 'bottom') editor.alignShapes(editor.getSelectedShapeIds(), 'bottom')
jest.advanceTimersByTime(1000) jest.advanceTimersByTime(1000)
editor.expectShapeToMatch( editor.expectShapeToMatch(
@ -77,7 +77,7 @@ describe('when multiple shapes are selected', () => {
}) })
it('aligns left', () => { it('aligns left', () => {
editor.alignShapes(editor.selectedShapeIds, 'left') editor.alignShapes(editor.getSelectedShapeIds(), 'left')
jest.advanceTimersByTime(1000) jest.advanceTimersByTime(1000)
editor.expectShapeToMatch( editor.expectShapeToMatch(
@ -88,7 +88,7 @@ describe('when multiple shapes are selected', () => {
}) })
it('aligns center horizontal', () => { it('aligns center horizontal', () => {
editor.alignShapes(editor.selectedShapeIds, 'center-horizontal') editor.alignShapes(editor.getSelectedShapeIds(), 'center-horizontal')
jest.advanceTimersByTime(1000) jest.advanceTimersByTime(1000)
editor.expectShapeToMatch( editor.expectShapeToMatch(
@ -99,7 +99,7 @@ describe('when multiple shapes are selected', () => {
}) })
it('aligns center vertical', () => { it('aligns center vertical', () => {
editor.alignShapes(editor.selectedShapeIds, 'center-vertical') editor.alignShapes(editor.getSelectedShapeIds(), 'center-vertical')
jest.advanceTimersByTime(1000) jest.advanceTimersByTime(1000)
editor.expectShapeToMatch( editor.expectShapeToMatch(
@ -128,9 +128,9 @@ describe('when multiple shapes are selected', () => {
}, },
]) ])
editor.alignShapes(editor.selectedShapeIds, 'center-vertical') editor.alignShapes(editor.getSelectedShapeIds(), 'center-vertical')
jest.advanceTimersByTime(1000) jest.advanceTimersByTime(1000)
editor.alignShapes(editor.selectedShapeIds, 'center-horizontal') editor.alignShapes(editor.getSelectedShapeIds(), 'center-horizontal')
jest.advanceTimersByTime(1000) jest.advanceTimersByTime(1000)
const commonBounds = Box2d.Common([ const commonBounds = Box2d.Common([
@ -167,9 +167,9 @@ describe('when multiple shapes are selected', () => {
}, },
]) ])
editor.alignShapes(editor.selectedShapeIds, 'top') editor.alignShapes(editor.getSelectedShapeIds(), 'top')
jest.advanceTimersByTime(1000) jest.advanceTimersByTime(1000)
editor.alignShapes(editor.selectedShapeIds, 'left') editor.alignShapes(editor.getSelectedShapeIds(), 'left')
jest.advanceTimersByTime(1000) jest.advanceTimersByTime(1000)
const commonBounds = Box2d.Common([ const commonBounds = Box2d.Common([
@ -207,9 +207,9 @@ describe('when multiple shapes are selected', () => {
]) ])
editor.setSelectedShapes([ids.boxA, ids.boxB, ids.boxC]) editor.setSelectedShapes([ids.boxA, ids.boxB, ids.boxC])
editor.alignShapes(editor.selectedShapeIds, 'bottom') editor.alignShapes(editor.getSelectedShapeIds(), 'bottom')
jest.advanceTimersByTime(1000) jest.advanceTimersByTime(1000)
editor.alignShapes(editor.selectedShapeIds, 'right') editor.alignShapes(editor.getSelectedShapeIds(), 'right')
jest.advanceTimersByTime(1000) jest.advanceTimersByTime(1000)
const commonBounds = Box2d.Common([ const commonBounds = Box2d.Common([
@ -231,7 +231,7 @@ describe('When shapes are parented to other shapes...', () => {
beforeEach(() => { beforeEach(() => {
editor = new TestEditor() editor = new TestEditor()
editor.selectAll() editor.selectAll()
editor.deleteShapes(editor.selectedShapeIds) editor.deleteShapes(editor.getSelectedShapeIds())
ids = editor.createShapesFromJsx([ ids = editor.createShapesFromJsx([
<TL.geo ref="boxA" x={0} y={0} w={100} h={100}> <TL.geo ref="boxA" x={0} y={0} w={100} h={100}>
<TL.geo ref="boxB" x={100} y={100} w={50} h={50} /> <TL.geo ref="boxB" x={100} y={100} w={50} h={50} />
@ -250,9 +250,9 @@ describe('When shapes are parented to other shapes...', () => {
editor.getShapePageBounds(ids.boxB)!, editor.getShapePageBounds(ids.boxB)!,
]) ])
editor.alignShapes(editor.selectedShapeIds, 'top') editor.alignShapes(editor.getSelectedShapeIds(), 'top')
jest.advanceTimersByTime(1000) jest.advanceTimersByTime(1000)
editor.alignShapes(editor.selectedShapeIds, 'left') editor.alignShapes(editor.getSelectedShapeIds(), 'left')
jest.advanceTimersByTime(1000) jest.advanceTimersByTime(1000)
const commonBoundsAfter = Box2d.Common([ const commonBoundsAfter = Box2d.Common([
@ -272,9 +272,9 @@ describe('When shapes are parented to other shapes...', () => {
editor.getShapePageBounds(ids.boxB)!, editor.getShapePageBounds(ids.boxB)!,
]) ])
editor.alignShapes(editor.selectedShapeIds, 'bottom') editor.alignShapes(editor.getSelectedShapeIds(), 'bottom')
jest.advanceTimersByTime(1000) jest.advanceTimersByTime(1000)
editor.alignShapes(editor.selectedShapeIds, 'right') editor.alignShapes(editor.getSelectedShapeIds(), 'right')
jest.advanceTimersByTime(1000) jest.advanceTimersByTime(1000)
const commonBoundsAfter = Box2d.Common([ const commonBoundsAfter = Box2d.Common([
@ -291,7 +291,7 @@ describe('When shapes are parented to a rotated shape...', () => {
beforeEach(() => { beforeEach(() => {
editor = new TestEditor() editor = new TestEditor()
editor.selectAll() editor.selectAll()
editor.deleteShapes(editor.selectedShapeIds) editor.deleteShapes(editor.getSelectedShapeIds())
editor.createShapes([ editor.createShapes([
{ {
id: ids.boxA, id: ids.boxA,
@ -338,9 +338,9 @@ describe('When shapes are parented to a rotated shape...', () => {
editor.getShapePageBounds(ids.boxB)!, editor.getShapePageBounds(ids.boxB)!,
]) ])
editor.alignShapes(editor.selectedShapeIds, 'top') editor.alignShapes(editor.getSelectedShapeIds(), 'top')
jest.advanceTimersByTime(1000) jest.advanceTimersByTime(1000)
editor.alignShapes(editor.selectedShapeIds, 'left') editor.alignShapes(editor.getSelectedShapeIds(), 'left')
jest.advanceTimersByTime(1000) jest.advanceTimersByTime(1000)
const commonBoundsAfter = Box2d.Common([ const commonBoundsAfter = Box2d.Common([
@ -366,9 +366,9 @@ describe('When shapes are parented to a rotated shape...', () => {
editor.getShapePageBounds(ids.boxB)!, editor.getShapePageBounds(ids.boxB)!,
]) ])
editor.alignShapes(editor.selectedShapeIds, 'bottom') editor.alignShapes(editor.getSelectedShapeIds(), 'bottom')
jest.advanceTimersByTime(1000) jest.advanceTimersByTime(1000)
editor.alignShapes(editor.selectedShapeIds, 'right') editor.alignShapes(editor.getSelectedShapeIds(), 'right')
jest.advanceTimersByTime(1000) jest.advanceTimersByTime(1000)
const commonBoundsAfter = Box2d.Common([ const commonBoundsAfter = Box2d.Common([

View file

@ -14,11 +14,11 @@ const ids = {
beforeEach(() => { beforeEach(() => {
editor = new TestEditor() editor = new TestEditor()
editor.selectAll().deleteShapes(editor.selectedShapeIds) editor.selectAll().deleteShapes(editor.getSelectedShapeIds())
}) })
afterEach(() => { afterEach(() => {
editor.selectAll().deleteShapes(editor.selectedShapeIds) editor.selectAll().deleteShapes(editor.getSelectedShapeIds())
}) })
const doMockClipboard = () => { const doMockClipboard = () => {
@ -446,7 +446,7 @@ describe('When copying and pasting', () => {
editor editor
// Create group // Create group
.selectAll() .selectAll()
.groupShapes(editor.selectedShapeIds) .groupShapes(editor.getSelectedShapeIds())
// Move the group // Move the group
.updateShapes([ .updateShapes([
{ {

View file

@ -17,7 +17,7 @@ beforeEach(() => {
editor = new TestEditor() editor = new TestEditor()
editor editor
.selectAll() .selectAll()
.deleteShapes(editor.selectedShapeIds) .deleteShapes(editor.getSelectedShapeIds())
.createShapes([ .createShapes([
{ id: ids.box1, type: 'geo', x: 100, y: 100, props: { w: 100, h: 100 } }, { id: ids.box1, type: 'geo', x: 100, y: 100, props: { w: 100, h: 100 } },
{ id: ids.box2, type: 'geo', x: 300, y: 300, props: { w: 100, h: 100 } }, { id: ids.box2, type: 'geo', x: 300, y: 300, props: { w: 100, h: 100 } },
@ -50,24 +50,24 @@ describe('Editor.deleteShapes', () => {
it('Deletes a shape', () => { it('Deletes a shape', () => {
editor.select(ids.box3, ids.box4) editor.select(ids.box3, ids.box4)
editor.mark('before deleting') editor.mark('before deleting')
editor.deleteShapes(editor.selectedShapeIds) // delete the selected shapes editor.deleteShapes(editor.getSelectedShapeIds()) // delete the selected shapes
expect(editor.getShape(ids.box3)).toBeUndefined() expect(editor.getShape(ids.box3)).toBeUndefined()
expect(editor.getShape(ids.box4)).toBeUndefined() expect(editor.getShape(ids.box4)).toBeUndefined()
expect(editor.selectedShapeIds).toMatchObject([]) expect(editor.getSelectedShapeIds()).toMatchObject([])
editor.undo() editor.undo()
expect(editor.getShape(ids.box3)).not.toBeUndefined() expect(editor.getShape(ids.box3)).not.toBeUndefined()
expect(editor.getShape(ids.box4)).not.toBeUndefined() expect(editor.getShape(ids.box4)).not.toBeUndefined()
expect(editor.selectedShapeIds).toMatchObject([ids.box3, ids.box4]) expect(editor.getSelectedShapeIds()).toMatchObject([ids.box3, ids.box4])
editor.redo() editor.redo()
expect(editor.getShape(ids.box3)).toBeUndefined() expect(editor.getShape(ids.box3)).toBeUndefined()
expect(editor.getShape(ids.box4)).toBeUndefined() expect(editor.getShape(ids.box4)).toBeUndefined()
expect(editor.selectedShapeIds).toMatchObject([]) expect(editor.getSelectedShapeIds()).toMatchObject([])
}) })
it('Does nothing on an empty ids array', () => { it('Does nothing on an empty ids array', () => {
editor.selectNone() editor.selectNone()
const before = editor.store.serialize() const before = editor.store.serialize()
editor.deleteShapes(editor.selectedShapeIds) // should be a noop, nothing to delete editor.deleteShapes(editor.getSelectedShapeIds()) // should be a noop, nothing to delete
expect(editor.store.serialize()).toStrictEqual(before) expect(editor.store.serialize()).toStrictEqual(before)
}) })
@ -75,7 +75,7 @@ describe('Editor.deleteShapes', () => {
editor.reparentShapes([ids.box4], ids.box3) editor.reparentShapes([ids.box4], ids.box3)
editor.select(ids.box3) editor.select(ids.box3)
editor.mark('before deleting') editor.mark('before deleting')
editor.deleteShapes(editor.selectedShapeIds) // should be a noop, nothing to delete editor.deleteShapes(editor.getSelectedShapeIds()) // should be a noop, nothing to delete
expect(editor.getShape(ids.box3)).toBeUndefined() expect(editor.getShape(ids.box3)).toBeUndefined()
expect(editor.getShape(ids.box4)).toBeUndefined() expect(editor.getShape(ids.box4)).toBeUndefined()
editor.undo() editor.undo()
@ -96,7 +96,7 @@ describe('When deleting arrows', () => {
// @ts-expect-error // @ts-expect-error
expect(editor._arrowBindingsIndex.get()[ids.box2]).not.toBeUndefined() expect(editor._arrowBindingsIndex.get()[ids.box2]).not.toBeUndefined()
editor.deleteShapes(editor.selectedShapeIds) // delete the selected shapes editor.deleteShapes(editor.getSelectedShapeIds()) // delete the selected shapes
// @ts-expect-error // @ts-expect-error
expect(editor._arrowBindingsIndex.get()[ids.box1]).toBeUndefined() expect(editor._arrowBindingsIndex.get()[ids.box1]).toBeUndefined()
// @ts-expect-error // @ts-expect-error

View file

@ -19,7 +19,7 @@ beforeEach(() => {
describe('distributeShapes command', () => { describe('distributeShapes command', () => {
beforeEach(() => { beforeEach(() => {
editor.selectAll() editor.selectAll()
editor.deleteShapes(editor.selectedShapeIds) editor.deleteShapes(editor.getSelectedShapeIds())
editor.createShapes([ editor.createShapes([
{ {
id: ids.boxA, id: ids.boxA,
@ -47,7 +47,7 @@ describe('distributeShapes command', () => {
editor.setSelectedShapes([ids.boxA, ids.boxB]) editor.setSelectedShapes([ids.boxA, ids.boxB])
const fn = jest.fn() const fn = jest.fn()
editor.on('change-history', fn) editor.on('change-history', fn)
editor.distributeShapes(editor.selectedShapeIds, 'horizontal') editor.distributeShapes(editor.getSelectedShapeIds(), 'horizontal')
jest.advanceTimersByTime(1000) jest.advanceTimersByTime(1000)
expect(fn).not.toHaveBeenCalled() expect(fn).not.toHaveBeenCalled()
}) })
@ -56,7 +56,7 @@ describe('distributeShapes command', () => {
describe('When distributing...', () => { describe('When distributing...', () => {
it('distributeShapes horizontally', () => { it('distributeShapes horizontally', () => {
editor.selectAll() editor.selectAll()
editor.distributeShapes(editor.selectedShapeIds, 'horizontal') editor.distributeShapes(editor.getSelectedShapeIds(), 'horizontal')
jest.advanceTimersByTime(1000) jest.advanceTimersByTime(1000)
editor.expectShapeToMatch( editor.expectShapeToMatch(
{ id: ids.boxA, x: 0 }, { id: ids.boxA, x: 0 },
@ -68,7 +68,7 @@ describe('distributeShapes command', () => {
it('distributeShapes horizontally when shapes are clustered', () => { it('distributeShapes horizontally when shapes are clustered', () => {
editor.updateShapes([{ id: ids.boxC, type: 'geo', x: 25 }]) editor.updateShapes([{ id: ids.boxC, type: 'geo', x: 25 }])
editor.selectAll() editor.selectAll()
editor.distributeShapes(editor.selectedShapeIds, 'horizontal') editor.distributeShapes(editor.getSelectedShapeIds(), 'horizontal')
jest.advanceTimersByTime(1000) jest.advanceTimersByTime(1000)
editor.expectShapeToMatch( editor.expectShapeToMatch(
{ id: ids.boxA, x: 0 }, { id: ids.boxA, x: 0 },
@ -79,7 +79,7 @@ describe('distributeShapes command', () => {
it('distributeShapes vertically', () => { it('distributeShapes vertically', () => {
editor.selectAll() editor.selectAll()
editor.distributeShapes(editor.selectedShapeIds, 'vertical') editor.distributeShapes(editor.getSelectedShapeIds(), 'vertical')
jest.advanceTimersByTime(1000) jest.advanceTimersByTime(1000)
editor.expectShapeToMatch( editor.expectShapeToMatch(
{ id: ids.boxA, y: 0 }, { id: ids.boxA, y: 0 },
@ -91,7 +91,7 @@ describe('distributeShapes command', () => {
it('distributeShapes vertically when shapes are clustered', () => { it('distributeShapes vertically when shapes are clustered', () => {
editor.updateShapes([{ id: ids.boxC, type: 'geo', y: 25 }]) editor.updateShapes([{ id: ids.boxC, type: 'geo', y: 25 }])
editor.selectAll() editor.selectAll()
editor.distributeShapes(editor.selectedShapeIds, 'vertical') editor.distributeShapes(editor.getSelectedShapeIds(), 'vertical')
jest.advanceTimersByTime(1000) jest.advanceTimersByTime(1000)
editor.expectShapeToMatch( editor.expectShapeToMatch(
{ id: ids.boxA, y: 0 }, { id: ids.boxA, y: 0 },
@ -103,7 +103,7 @@ describe('distributeShapes command', () => {
it('distributeShapes shapes that are the child of another shape.', () => { it('distributeShapes shapes that are the child of another shape.', () => {
editor.selectAll() editor.selectAll()
editor.deleteShapes(editor.selectedShapeIds) editor.deleteShapes(editor.getSelectedShapeIds())
editor.createShapes([ editor.createShapes([
{ {
id: ids.boxA, id: ids.boxA,
@ -133,7 +133,7 @@ describe('distributeShapes command', () => {
]) ])
editor.setSelectedShapes([ids.boxB, ids.boxC, ids.boxD]) editor.setSelectedShapes([ids.boxB, ids.boxC, ids.boxD])
editor.distributeShapes(editor.selectedShapeIds, 'horizontal') editor.distributeShapes(editor.getSelectedShapeIds(), 'horizontal')
jest.advanceTimersByTime(1000) jest.advanceTimersByTime(1000)
editor.expectShapeToMatch( editor.expectShapeToMatch(
@ -145,7 +145,7 @@ describe('distributeShapes command', () => {
it('distributeShapes shapes that are the child of another shape when clustered.', () => { it('distributeShapes shapes that are the child of another shape when clustered.', () => {
editor.selectAll() editor.selectAll()
editor.deleteShapes(editor.selectedShapeIds) editor.deleteShapes(editor.getSelectedShapeIds())
editor.createShapes([ editor.createShapes([
{ {
id: ids.boxA, id: ids.boxA,
@ -175,7 +175,7 @@ describe('distributeShapes command', () => {
]) ])
editor.setSelectedShapes([ids.boxB, ids.boxC, ids.boxD]) editor.setSelectedShapes([ids.boxB, ids.boxC, ids.boxD])
editor.distributeShapes(editor.selectedShapeIds, 'horizontal') editor.distributeShapes(editor.getSelectedShapeIds(), 'horizontal')
jest.advanceTimersByTime(1000) jest.advanceTimersByTime(1000)
editor.expectShapeToMatch( editor.expectShapeToMatch(
@ -188,7 +188,7 @@ describe('distributeShapes command', () => {
it('distributeShapes shapes that are the child of another shape when a parent is rotated.', () => { it('distributeShapes shapes that are the child of another shape when a parent is rotated.', () => {
editor = new TestEditor() editor = new TestEditor()
editor.selectAll() editor.selectAll()
editor.deleteShapes(editor.selectedShapeIds) editor.deleteShapes(editor.getSelectedShapeIds())
editor.createShapes([ editor.createShapes([
{ {
id: ids.boxA, id: ids.boxA,
@ -220,7 +220,7 @@ describe('distributeShapes command', () => {
editor.setSelectedShapes([ids.boxB, ids.boxC, ids.boxD]) editor.setSelectedShapes([ids.boxB, ids.boxC, ids.boxD])
editor.distributeShapes(editor.selectedShapeIds, 'horizontal') editor.distributeShapes(editor.getSelectedShapeIds(), 'horizontal')
jest.advanceTimersByTime(1000) jest.advanceTimersByTime(1000)
editor.expectShapeToMatch( editor.expectShapeToMatch(

View file

@ -51,7 +51,7 @@ beforeEach(() => {
}) })
it('gets an SVG', async () => { it('gets an SVG', async () => {
const svg = await editor.getSvg(editor.selectedShapeIds) const svg = await editor.getSvg(editor.getSelectedShapeIds())
expect(svg).toBeTruthy() expect(svg).toBeTruthy()
}) })
@ -63,7 +63,7 @@ it('Does not get an SVG when no ids are provided', async () => {
}) })
it('Gets the bounding box at the correct size', async () => { it('Gets the bounding box at the correct size', async () => {
const svg = await editor.getSvg(editor.selectedShapeIds) const svg = await editor.getSvg(editor.getSelectedShapeIds())
const bbox = editor.selectionRotatedPageBounds! const bbox = editor.selectionRotatedPageBounds!
const expanded = bbox.expandBy(SVG_PADDING) // adds 32px padding const expanded = bbox.expandBy(SVG_PADDING) // adds 32px padding
@ -72,7 +72,7 @@ it('Gets the bounding box at the correct size', async () => {
}) })
it('Gets the bounding box at the correct size', async () => { it('Gets the bounding box at the correct size', async () => {
const svg = (await editor.getSvg(editor.selectedShapeIds))! const svg = (await editor.getSvg(editor.getSelectedShapeIds()))!
const bbox = editor.selectionRotatedPageBounds! const bbox = editor.selectionRotatedPageBounds!
const expanded = bbox.expandBy(SVG_PADDING) // adds 32px padding const expanded = bbox.expandBy(SVG_PADDING) // adds 32px padding
@ -81,7 +81,7 @@ it('Gets the bounding box at the correct size', async () => {
}) })
it('Matches a snapshot', async () => { it('Matches a snapshot', async () => {
const svg = (await editor.getSvg(editor.selectedShapeIds))! const svg = (await editor.getSvg(editor.getSelectedShapeIds()))!
const elm = document.createElement('wrapper') const elm = document.createElement('wrapper')
elm.appendChild(svg) elm.appendChild(svg)
@ -90,21 +90,21 @@ it('Matches a snapshot', async () => {
}) })
it('Accepts a scale option', async () => { it('Accepts a scale option', async () => {
const svg1 = (await editor.getSvg(editor.selectedShapeIds, { scale: 1 }))! const svg1 = (await editor.getSvg(editor.getSelectedShapeIds(), { scale: 1 }))!
expect(svg1.getAttribute('width')).toBe('564') expect(svg1.getAttribute('width')).toBe('564')
const svg2 = (await editor.getSvg(editor.selectedShapeIds, { scale: 2 }))! const svg2 = (await editor.getSvg(editor.getSelectedShapeIds(), { scale: 2 }))!
expect(svg2.getAttribute('width')).toBe('1128') expect(svg2.getAttribute('width')).toBe('1128')
}) })
it('Accepts a background option', async () => { it('Accepts a background option', async () => {
const svg1 = (await editor.getSvg(editor.selectedShapeIds, { background: true }))! const svg1 = (await editor.getSvg(editor.getSelectedShapeIds(), { background: true }))!
expect(svg1.style.backgroundColor).not.toBe('transparent') expect(svg1.style.backgroundColor).not.toBe('transparent')
const svg2 = (await editor.getSvg(editor.selectedShapeIds, { background: false }))! const svg2 = (await editor.getSvg(editor.getSelectedShapeIds(), { background: false }))!
expect(svg2.style.backgroundColor).toBe('transparent') expect(svg2.style.backgroundColor).toBe('transparent')
}) })

View file

@ -17,7 +17,7 @@ const ids = {
beforeEach(() => { beforeEach(() => {
editor = new TestEditor() editor = new TestEditor()
editor.selectAll() editor.selectAll()
editor.deleteShapes(editor.selectedShapeIds) editor.deleteShapes(editor.getSelectedShapeIds())
editor.createShapes([ editor.createShapes([
{ {
id: ids.lockedShapeA, id: ids.lockedShapeA,
@ -83,10 +83,10 @@ beforeEach(() => {
describe('Locking', () => { describe('Locking', () => {
it('Can lock shapes', () => { it('Can lock shapes', () => {
editor.setSelectedShapes([ids.unlockedShapeA]) editor.setSelectedShapes([ids.unlockedShapeA])
editor.toggleLock(editor.selectedShapeIds) editor.toggleLock(editor.getSelectedShapeIds())
expect(editor.getShape(ids.unlockedShapeA)!.isLocked).toBe(true) expect(editor.getShape(ids.unlockedShapeA)!.isLocked).toBe(true)
// Locking deselects the shape // Locking deselects the shape
expect(editor.selectedShapeIds).toEqual([]) expect(editor.getSelectedShapeIds()).toEqual([])
}) })
}) })
@ -117,7 +117,7 @@ describe('Locked shapes', () => {
it('Cannot be selected with select all', () => { it('Cannot be selected with select all', () => {
editor.selectAll() editor.selectAll()
expect(editor.selectedShapeIds).toEqual([ids.unlockedShapeA, ids.unlockedShapeB]) expect(editor.getSelectedShapeIds()).toEqual([ids.unlockedShapeA, ids.unlockedShapeB])
}) })
it('Cannot be selected by clicking', () => { it('Cannot be selected by clicking', () => {
@ -128,7 +128,7 @@ describe('Locked shapes', () => {
.expectToBeIn('select.pointing_canvas') .expectToBeIn('select.pointing_canvas')
.pointerUp() .pointerUp()
.expectToBeIn('select.idle') .expectToBeIn('select.idle')
expect(editor.selectedShapeIds).not.toContain(shape.id) expect(editor.getSelectedShapeIds()).not.toContain(shape.id)
}) })
it('Cannot be edited', () => { it('Cannot be edited', () => {
@ -138,7 +138,7 @@ describe('Locked shapes', () => {
// We create a new shape and we edit that one // We create a new shape and we edit that one
editor.doubleClick(10, 10, { target: 'shape', shape }).expectToBeIn('select.editing_shape') editor.doubleClick(10, 10, { target: 'shape', shape }).expectToBeIn('select.editing_shape')
expect(editor.currentPageShapes.length).toBe(shapeCount + 1) expect(editor.currentPageShapes.length).toBe(shapeCount + 1)
expect(editor.selectedShapeIds).not.toContain(shape.id) expect(editor.getSelectedShapeIds()).not.toContain(shape.id)
}) })
it('Cannot be grouped', () => { it('Cannot be grouped', () => {
@ -169,7 +169,7 @@ describe('Unlocking', () => {
(id) => editor.getShape(id)!.isLocked (id) => editor.getShape(id)!.isLocked
) )
expect(lockedStatus).toStrictEqual([true, true]) expect(lockedStatus).toStrictEqual([true, true])
editor.toggleLock(editor.selectedShapeIds) editor.toggleLock(editor.getSelectedShapeIds())
lockedStatus = [ids.lockedShapeA, ids.lockedShapeB].map((id) => editor.getShape(id)!.isLocked) lockedStatus = [ids.lockedShapeA, ids.lockedShapeB].map((id) => editor.getShape(id)!.isLocked)
expect(lockedStatus).toStrictEqual([false, false]) expect(lockedStatus).toStrictEqual([false, false])
}) })

View file

@ -155,7 +155,7 @@ describe('arrows', () => {
let secondBox: TLShape let secondBox: TLShape
beforeEach(() => { beforeEach(() => {
editor.selectAll().deleteShapes(editor.selectedShapeIds) editor.selectAll().deleteShapes(editor.getSelectedShapeIds())
// draw a first box // draw a first box
editor.createShapes([ editor.createShapes([
{ {

View file

@ -39,22 +39,22 @@ function nudgeAndGet(ids: TLShapeId[], key: string, shiftKey: boolean) {
switch (key) { switch (key) {
case 'ArrowLeft': { case 'ArrowLeft': {
editor.mark('nudge') editor.mark('nudge')
editor.nudgeShapes(editor.selectedShapeIds, { x: -step, y: 0 }) editor.nudgeShapes(editor.getSelectedShapeIds(), { x: -step, y: 0 })
break break
} }
case 'ArrowRight': { case 'ArrowRight': {
editor.mark('nudge') editor.mark('nudge')
editor.nudgeShapes(editor.selectedShapeIds, { x: step, y: 0 }) editor.nudgeShapes(editor.getSelectedShapeIds(), { x: step, y: 0 })
break break
} }
case 'ArrowUp': { case 'ArrowUp': {
editor.mark('nudge') editor.mark('nudge')
editor.nudgeShapes(editor.selectedShapeIds, { x: 0, y: -step }) editor.nudgeShapes(editor.getSelectedShapeIds(), { x: 0, y: -step })
break break
} }
case 'ArrowDown': { case 'ArrowDown': {
editor.mark('nudge') editor.mark('nudge')
editor.nudgeShapes(editor.selectedShapeIds, { x: 0, y: step }) editor.nudgeShapes(editor.getSelectedShapeIds(), { x: 0, y: step })
break break
} }
} }

View file

@ -15,7 +15,7 @@ jest.useFakeTimers()
beforeEach(() => { beforeEach(() => {
editor = new TestEditor() editor = new TestEditor()
editor.selectAll() editor.selectAll()
editor.deleteShapes(editor.selectedShapeIds) editor.deleteShapes(editor.getSelectedShapeIds())
editor.createShapes([ editor.createShapes([
{ {
id: ids.boxA, id: ids.boxA,
@ -42,7 +42,7 @@ describe('editor.packShapes', () => {
it('packs shapes', () => { it('packs shapes', () => {
editor.selectAll() editor.selectAll()
const centerBefore = editor.selectionRotatedPageBounds!.center.clone() const centerBefore = editor.selectionRotatedPageBounds!.center.clone()
editor.packShapes(editor.selectedShapeIds, 16) editor.packShapes(editor.getSelectedShapeIds(), 16)
jest.advanceTimersByTime(1000) jest.advanceTimersByTime(1000)
expect(editor.currentPageShapes.map((s) => ({ ...s, parentId: 'wahtever' }))).toMatchSnapshot( expect(editor.currentPageShapes.map((s) => ({ ...s, parentId: 'wahtever' }))).toMatchSnapshot(
'packed shapes' 'packed shapes'
@ -53,7 +53,7 @@ describe('editor.packShapes', () => {
it('packs rotated shapes', () => { it('packs rotated shapes', () => {
editor.updateShapes([{ id: ids.boxA, type: 'geo', rotation: Math.PI }]) editor.updateShapes([{ id: ids.boxA, type: 'geo', rotation: Math.PI }])
editor.selectAll().packShapes(editor.selectedShapeIds, 16) editor.selectAll().packShapes(editor.getSelectedShapeIds(), 16)
jest.advanceTimersByTime(1000) jest.advanceTimersByTime(1000)
expect(editor.currentPageShapes.map((s) => ({ ...s, parentId: 'wahtever' }))).toMatchSnapshot( expect(editor.currentPageShapes.map((s) => ({ ...s, parentId: 'wahtever' }))).toMatchSnapshot(
'packed shapes' 'packed shapes'

View file

@ -60,7 +60,7 @@ describe('editor.rotateShapes', () => {
const { selectionPageCenter } = editor const { selectionPageCenter } = editor
// Rotate the shape... // Rotate the shape...
editor.rotateShapesBy(editor.selectedShapeIds, Math.PI) editor.rotateShapesBy(editor.getSelectedShapeIds(), Math.PI)
// Once for each shape // Once for each shape
expect(fnStart).toHaveBeenCalledTimes(2) expect(fnStart).toHaveBeenCalledTimes(2)

View file

@ -40,9 +40,9 @@ describe('setCurrentPage', () => {
it("adding a page to the store by any means adds tab state for the page if it doesn't already exist", () => { it("adding a page to the store by any means adds tab state for the page if it doesn't already exist", () => {
const page = PageRecordType.create({ name: 'test', index: 'a4' }) const page = PageRecordType.create({ name: 'test', index: 'a4' })
expect(editor.pageStates.find((p) => p.pageId === page.id)).toBeUndefined() expect(editor.getPageStates().find((p) => p.pageId === page.id)).toBeUndefined()
editor.store.put([page]) editor.store.put([page])
expect(editor.pageStates.find((p) => p.pageId === page.id)).not.toBeUndefined() expect(editor.getPageStates().find((p) => p.pageId === page.id)).not.toBeUndefined()
}) })
it('squashes', () => { it('squashes', () => {

View file

@ -15,43 +15,43 @@ beforeEach(() => {
}) })
it('Sets selected shapes', () => { it('Sets selected shapes', () => {
expect(editor.selectedShapeIds).toMatchObject([]) expect(editor.getSelectedShapeIds()).toMatchObject([])
editor.setSelectedShapes([ids.box1, ids.box2]) editor.setSelectedShapes([ids.box1, ids.box2])
expect(editor.selectedShapeIds).toMatchObject([ids.box1, ids.box2]) expect(editor.getSelectedShapeIds()).toMatchObject([ids.box1, ids.box2])
editor.undo() editor.undo()
expect(editor.selectedShapeIds).toMatchObject([]) expect(editor.getSelectedShapeIds()).toMatchObject([])
editor.redo() editor.redo()
expect(editor.selectedShapeIds).toMatchObject([ids.box1, ids.box2]) expect(editor.getSelectedShapeIds()).toMatchObject([ids.box1, ids.box2])
}) })
it('Prevents parent and child from both being selected', () => { it('Prevents parent and child from both being selected', () => {
editor.setSelectedShapes([ids.box2, ids.ellipse1]) // ellipse1 is child of box2 editor.setSelectedShapes([ids.box2, ids.ellipse1]) // ellipse1 is child of box2
expect(editor.selectedShapeIds).toMatchObject([ids.box2]) expect(editor.getSelectedShapeIds()).toMatchObject([ids.box2])
}) })
it('Deleting the parent also deletes descendants', () => { it('Deleting the parent also deletes descendants', () => {
editor.setSelectedShapes([ids.box2]) editor.setSelectedShapes([ids.box2])
expect(editor.selectedShapeIds).toMatchObject([ids.box2]) expect(editor.getSelectedShapeIds()).toMatchObject([ids.box2])
expect(editor.getShape(ids.box2)).not.toBeUndefined() expect(editor.getShape(ids.box2)).not.toBeUndefined()
expect(editor.getShape(ids.ellipse1)).not.toBeUndefined() expect(editor.getShape(ids.ellipse1)).not.toBeUndefined()
editor.mark('') editor.mark('')
editor.deleteShapes([ids.box2]) editor.deleteShapes([ids.box2])
expect(editor.selectedShapeIds).toMatchObject([]) expect(editor.getSelectedShapeIds()).toMatchObject([])
expect(editor.getShape(ids.box2)).toBeUndefined() expect(editor.getShape(ids.box2)).toBeUndefined()
expect(editor.getShape(ids.ellipse1)).toBeUndefined() expect(editor.getShape(ids.ellipse1)).toBeUndefined()
editor.undo() editor.undo()
expect(editor.selectedShapeIds).toMatchObject([ids.box2]) expect(editor.getSelectedShapeIds()).toMatchObject([ids.box2])
expect(editor.getShape(ids.box2)).not.toBe(undefined) expect(editor.getShape(ids.box2)).not.toBe(undefined)
expect(editor.getShape(ids.ellipse1)).not.toBe(undefined) expect(editor.getShape(ids.ellipse1)).not.toBe(undefined)
editor.redo() editor.redo()
expect(editor.selectedShapeIds).toMatchObject([]) expect(editor.getSelectedShapeIds()).toMatchObject([])
expect(editor.getShape(ids.box2)).toBeUndefined() expect(editor.getShape(ids.box2)).toBeUndefined()
expect(editor.getShape(ids.ellipse1)).toBeUndefined() expect(editor.getShape(ids.ellipse1)).toBeUndefined()
}) })

View file

@ -19,7 +19,7 @@ beforeEach(() => {
describe('distributeShapes command', () => { describe('distributeShapes command', () => {
beforeEach(() => { beforeEach(() => {
editor.selectAll() editor.selectAll()
editor.deleteShapes(editor.selectedShapeIds) editor.deleteShapes(editor.getSelectedShapeIds())
editor.createShapes([ editor.createShapes([
{ {
id: ids.boxA, id: ids.boxA,
@ -53,7 +53,7 @@ describe('distributeShapes command', () => {
editor.setSelectedShapes([ids.boxA, ids.boxB]) editor.setSelectedShapes([ids.boxA, ids.boxB])
const fn = jest.fn() const fn = jest.fn()
editor.on('change-history', fn) editor.on('change-history', fn)
editor.stackShapes(editor.selectedShapeIds, 'horizontal', 0) editor.stackShapes(editor.getSelectedShapeIds(), 'horizontal', 0)
jest.advanceTimersByTime(1000) jest.advanceTimersByTime(1000)
expect(fn).not.toHaveBeenCalled() expect(fn).not.toHaveBeenCalled()
}) })
@ -62,7 +62,7 @@ describe('distributeShapes command', () => {
describe('when stacking horizontally', () => { describe('when stacking horizontally', () => {
it('stacks the shapes based on a given value', () => { it('stacks the shapes based on a given value', () => {
editor.setSelectedShapes([ids.boxA, ids.boxB, ids.boxC, ids.boxD]) editor.setSelectedShapes([ids.boxA, ids.boxB, ids.boxC, ids.boxD])
editor.stackShapes(editor.selectedShapeIds, 'horizontal', 10) editor.stackShapes(editor.getSelectedShapeIds(), 'horizontal', 10)
jest.advanceTimersByTime(1000) jest.advanceTimersByTime(1000)
// 200 distance gap between c and d // 200 distance gap between c and d
editor.expectShapeToMatch({ editor.expectShapeToMatch({
@ -89,7 +89,7 @@ describe('distributeShapes command', () => {
it('stacks the shapes based on the most common gap', () => { it('stacks the shapes based on the most common gap', () => {
editor.setSelectedShapes([ids.boxA, ids.boxB, ids.boxC, ids.boxD]) editor.setSelectedShapes([ids.boxA, ids.boxB, ids.boxC, ids.boxD])
editor.stackShapes(editor.selectedShapeIds, 'horizontal', 0) editor.stackShapes(editor.getSelectedShapeIds(), 'horizontal', 0)
jest.advanceTimersByTime(1000) jest.advanceTimersByTime(1000)
// 200 distance gap between c and d // 200 distance gap between c and d
editor.expectShapeToMatch({ editor.expectShapeToMatch({
@ -117,7 +117,7 @@ describe('distributeShapes command', () => {
it('stacks the shapes based on the average', () => { it('stacks the shapes based on the average', () => {
editor.updateShapes([{ id: ids.boxD, type: 'geo', x: 540, y: 700 }]) editor.updateShapes([{ id: ids.boxD, type: 'geo', x: 540, y: 700 }])
editor.setSelectedShapes([ids.boxA, ids.boxB, ids.boxC, ids.boxD]) editor.setSelectedShapes([ids.boxA, ids.boxB, ids.boxC, ids.boxD])
editor.stackShapes(editor.selectedShapeIds, 'horizontal', 0) editor.stackShapes(editor.getSelectedShapeIds(), 'horizontal', 0)
jest.advanceTimersByTime(1000) jest.advanceTimersByTime(1000)
editor.expectShapeToMatch({ editor.expectShapeToMatch({
id: ids.boxA, id: ids.boxA,
@ -145,7 +145,7 @@ describe('distributeShapes command', () => {
describe('when stacking vertically', () => { describe('when stacking vertically', () => {
it('stacks the shapes based on a given value', () => { it('stacks the shapes based on a given value', () => {
editor.setSelectedShapes([ids.boxA, ids.boxB, ids.boxC, ids.boxD]) editor.setSelectedShapes([ids.boxA, ids.boxB, ids.boxC, ids.boxD])
editor.stackShapes(editor.selectedShapeIds, 'vertical', 10) editor.stackShapes(editor.getSelectedShapeIds(), 'vertical', 10)
jest.advanceTimersByTime(1000) jest.advanceTimersByTime(1000)
// 200 distance gap between c and d // 200 distance gap between c and d
editor.expectShapeToMatch({ editor.expectShapeToMatch({
@ -172,7 +172,7 @@ describe('distributeShapes command', () => {
it('stacks the shapes based on the most common gap', () => { it('stacks the shapes based on the most common gap', () => {
editor.setSelectedShapes([ids.boxA, ids.boxB, ids.boxC, ids.boxD]) editor.setSelectedShapes([ids.boxA, ids.boxB, ids.boxC, ids.boxD])
editor.stackShapes(editor.selectedShapeIds, 'vertical', 0) editor.stackShapes(editor.getSelectedShapeIds(), 'vertical', 0)
jest.advanceTimersByTime(1000) jest.advanceTimersByTime(1000)
// 200 distance gap between c and d // 200 distance gap between c and d
editor.expectShapeToMatch({ editor.expectShapeToMatch({
@ -200,7 +200,7 @@ describe('distributeShapes command', () => {
it('stacks the shapes based on the average', () => { it('stacks the shapes based on the average', () => {
editor.updateShapes([{ id: ids.boxD, type: 'geo', x: 700, y: 540 }]) editor.updateShapes([{ id: ids.boxD, type: 'geo', x: 700, y: 540 }])
editor.setSelectedShapes([ids.boxA, ids.boxB, ids.boxC, ids.boxD]) editor.setSelectedShapes([ids.boxA, ids.boxB, ids.boxC, ids.boxD])
editor.stackShapes(editor.selectedShapeIds, 'vertical', 0) editor.stackShapes(editor.getSelectedShapeIds(), 'vertical', 0)
jest.advanceTimersByTime(1000) jest.advanceTimersByTime(1000)
editor.expectShapeToMatch({ editor.expectShapeToMatch({
id: ids.boxA, id: ids.boxA,

View file

@ -14,7 +14,7 @@ function createVideoShape() {
beforeEach(() => { beforeEach(() => {
editor = new TestEditor() editor = new TestEditor()
editor.selectAll() editor.selectAll()
editor.deleteShapes(editor.selectedShapeIds) editor.deleteShapes(editor.getSelectedShapeIds())
ids = editor.createShapesFromJsx([ ids = editor.createShapesFromJsx([
<TL.geo ref="boxA" x={0} y={0} w={100} h={100} />, <TL.geo ref="boxA" x={0} y={0} w={100} h={100} />,
<TL.geo ref="boxB" x={100} y={100} w={50} h={50} />, <TL.geo ref="boxB" x={100} y={100} w={50} h={50} />,
@ -28,7 +28,7 @@ describe('when less than two shapes are selected', () => {
editor.setSelectedShapes([ids.boxB]) editor.setSelectedShapes([ids.boxB])
const fn = jest.fn() const fn = jest.fn()
editor.on('change-history', fn) editor.on('change-history', fn)
editor.stretchShapes(editor.selectedShapeIds, 'horizontal') editor.stretchShapes(editor.getSelectedShapeIds(), 'horizontal')
jest.advanceTimersByTime(1000) jest.advanceTimersByTime(1000)
expect(fn).not.toHaveBeenCalled() expect(fn).not.toHaveBeenCalled()
@ -38,7 +38,7 @@ describe('when less than two shapes are selected', () => {
describe('when multiple shapes are selected', () => { describe('when multiple shapes are selected', () => {
it('stretches horizontally', () => { it('stretches horizontally', () => {
editor.selectAll() editor.selectAll()
editor.stretchShapes(editor.selectedShapeIds, 'horizontal') editor.stretchShapes(editor.getSelectedShapeIds(), 'horizontal')
jest.advanceTimersByTime(1000) jest.advanceTimersByTime(1000)
editor.expectShapeToMatch( editor.expectShapeToMatch(
{ id: ids.boxA, x: 0, y: 0, props: { w: 500 } }, { id: ids.boxA, x: 0, y: 0, props: { w: 500 } },
@ -51,7 +51,7 @@ describe('when multiple shapes are selected', () => {
const videoA = createVideoShape() const videoA = createVideoShape()
editor.selectAll() editor.selectAll()
expect(editor.selectedShapes.length).toBe(4) expect(editor.selectedShapes.length).toBe(4)
editor.stretchShapes(editor.selectedShapeIds, 'horizontal') editor.stretchShapes(editor.getSelectedShapeIds(), 'horizontal')
jest.advanceTimersByTime(1000) jest.advanceTimersByTime(1000)
const newHeight = (500 * 9) / 16 const newHeight = (500 * 9) / 16
editor.expectShapeToMatch( editor.expectShapeToMatch(
@ -64,7 +64,7 @@ describe('when multiple shapes are selected', () => {
it('stretches vertically', () => { it('stretches vertically', () => {
editor.selectAll() editor.selectAll()
editor.stretchShapes(editor.selectedShapeIds, 'vertical') editor.stretchShapes(editor.getSelectedShapeIds(), 'vertical')
jest.advanceTimersByTime(1000) jest.advanceTimersByTime(1000)
editor.expectShapeToMatch( editor.expectShapeToMatch(
{ id: ids.boxA, x: 0, y: 0, props: { h: 500 } }, { id: ids.boxA, x: 0, y: 0, props: { h: 500 } },
@ -77,7 +77,7 @@ describe('when multiple shapes are selected', () => {
const videoA = createVideoShape() const videoA = createVideoShape()
editor.selectAll() editor.selectAll()
expect(editor.selectedShapes.length).toBe(4) expect(editor.selectedShapes.length).toBe(4)
editor.stretchShapes(editor.selectedShapeIds, 'vertical') editor.stretchShapes(editor.getSelectedShapeIds(), 'vertical')
jest.advanceTimersByTime(1000) jest.advanceTimersByTime(1000)
const newWidth = (500 * 16) / 9 const newWidth = (500 * 16) / 9
editor.expectShapeToMatch( editor.expectShapeToMatch(
@ -90,7 +90,7 @@ describe('when multiple shapes are selected', () => {
it('does, undoes and redoes command', () => { it('does, undoes and redoes command', () => {
editor.mark('stretch') editor.mark('stretch')
editor.stretchShapes(editor.selectedShapeIds, 'horizontal') editor.stretchShapes(editor.getSelectedShapeIds(), 'horizontal')
jest.advanceTimersByTime(1000) jest.advanceTimersByTime(1000)
editor.expectShapeToMatch({ id: ids.boxB, x: 0, props: { w: 500 } }) editor.expectShapeToMatch({ id: ids.boxB, x: 0, props: { w: 500 } })
@ -105,7 +105,7 @@ describe('When shapes are the child of another shape.', () => {
it('stretches horizontally', () => { it('stretches horizontally', () => {
editor.reparentShapes([ids.boxB], ids.boxA) editor.reparentShapes([ids.boxB], ids.boxA)
editor.select(ids.boxB, ids.boxC) editor.select(ids.boxB, ids.boxC)
editor.stretchShapes(editor.selectedShapeIds, 'horizontal') editor.stretchShapes(editor.getSelectedShapeIds(), 'horizontal')
jest.advanceTimersByTime(1000) jest.advanceTimersByTime(1000)
editor.expectShapeToMatch( editor.expectShapeToMatch(
{ id: ids.boxB, x: 100, y: 100, props: { w: 400 } }, { id: ids.boxB, x: 100, y: 100, props: { w: 400 } },
@ -116,7 +116,7 @@ describe('When shapes are the child of another shape.', () => {
it('stretches vertically', () => { it('stretches vertically', () => {
editor.reparentShapes([ids.boxB], ids.boxA) editor.reparentShapes([ids.boxB], ids.boxA)
editor.select(ids.boxB, ids.boxC) editor.select(ids.boxB, ids.boxC)
editor.stretchShapes(editor.selectedShapeIds, 'vertical') editor.stretchShapes(editor.getSelectedShapeIds(), 'vertical')
jest.advanceTimersByTime(1000) jest.advanceTimersByTime(1000)
editor.expectShapeToMatch( editor.expectShapeToMatch(
{ id: ids.boxB, x: 100, y: 100, props: { h: 400 } }, { id: ids.boxB, x: 100, y: 100, props: { h: 400 } },
@ -129,7 +129,7 @@ describe('When shapes are the child of a rotated shape.', () => {
beforeEach(() => { beforeEach(() => {
editor = new TestEditor() editor = new TestEditor()
editor.selectAll() editor.selectAll()
editor.deleteShapes(editor.selectedShapeIds) editor.deleteShapes(editor.getSelectedShapeIds())
ids = editor.createShapesFromJsx([ ids = editor.createShapesFromJsx([
<TL.geo ref="boxA" x={0} y={0} w={100} h={100} rotation={PI}> <TL.geo ref="boxA" x={0} y={0} w={100} h={100} rotation={PI}>
<TL.geo ref="boxB" x={100} y={100} w={50} h={50} /> <TL.geo ref="boxB" x={100} y={100} w={50} h={50} />
@ -141,7 +141,7 @@ describe('When shapes are the child of a rotated shape.', () => {
it('does not stretches rotated shapes', () => { it('does not stretches rotated shapes', () => {
editor.select(ids.boxB, ids.boxC) editor.select(ids.boxB, ids.boxC)
editor.stretchShapes(editor.selectedShapeIds, 'horizontal') editor.stretchShapes(editor.getSelectedShapeIds(), 'horizontal')
jest.advanceTimersByTime(1000) jest.advanceTimersByTime(1000)
editor.expectShapeToMatch( editor.expectShapeToMatch(
{ {
@ -167,7 +167,7 @@ describe('When shapes are the child of a rotated shape.', () => {
it('does not stretches rotated shapes', () => { it('does not stretches rotated shapes', () => {
editor.select(ids.boxB, ids.boxC) editor.select(ids.boxB, ids.boxC)
editor.stretchShapes(editor.selectedShapeIds, 'vertical') editor.stretchShapes(editor.getSelectedShapeIds(), 'vertical')
jest.advanceTimersByTime(1000) jest.advanceTimersByTime(1000)
editor.expectShapeToMatch( editor.expectShapeToMatch(
{ {
@ -195,7 +195,7 @@ describe('When shapes are the child of a rotated shape.', () => {
describe('When shapes have 0-width or 0-height', () => { describe('When shapes have 0-width or 0-height', () => {
it('Does not error with 0-width', () => { it('Does not error with 0-width', () => {
editor.selectAll() editor.selectAll()
editor.deleteShapes(editor.selectedShapeIds) editor.deleteShapes(editor.getSelectedShapeIds())
editor editor
.setCurrentTool('arrow') .setCurrentTool('arrow')
@ -213,13 +213,13 @@ describe('When shapes have 0-width or 0-height', () => {
editor.selectAll() editor.selectAll()
// make sure we don't get any errors: // make sure we don't get any errors:
editor.stretchShapes(editor.selectedShapeIds, 'horizontal') editor.stretchShapes(editor.getSelectedShapeIds(), 'horizontal')
editor.stretchShapes(editor.selectedShapeIds, 'vertical') editor.stretchShapes(editor.getSelectedShapeIds(), 'vertical')
}) })
it('Does not error with 0-height', () => { it('Does not error with 0-height', () => {
editor.selectAll() editor.selectAll()
editor.deleteShapes(editor.selectedShapeIds) editor.deleteShapes(editor.getSelectedShapeIds())
editor editor
// draw a perfectly horiztonal arrow: // draw a perfectly horiztonal arrow:
@ -239,7 +239,7 @@ describe('When shapes have 0-width or 0-height', () => {
editor.selectAll() editor.selectAll()
// make sure we don't get any errors: // make sure we don't get any errors:
editor.stretchShapes(editor.selectedShapeIds, 'horizontal') editor.stretchShapes(editor.getSelectedShapeIds(), 'horizontal')
editor.stretchShapes(editor.selectedShapeIds, 'vertical') editor.stretchShapes(editor.getSelectedShapeIds(), 'vertical')
}) })
}) })

View file

@ -156,7 +156,7 @@ describe('When a shape has a different shape as a parent...', () => {
}) })
it('Throws out all shapes if any update is invalid', () => { it('Throws out all shapes if any update is invalid', () => {
editor.selectAll().deleteShapes(editor.selectedShapeIds) editor.selectAll().deleteShapes(editor.getSelectedShapeIds())
editor.createShapes([ editor.createShapes([
{ id: ids.box1, type: 'geo', x: 0, y: 0 }, { id: ids.box1, type: 'geo', x: 0, y: 0 },

View file

@ -91,27 +91,27 @@ describe('When in the select.idle state', () => {
editor.select(ids.boxA) editor.select(ids.boxA)
editor.expectPathToBe('root.select.idle') editor.expectPathToBe('root.select.idle')
expect(editor.selectedShapeIds).toMatchObject([ids.boxA]) expect(editor.getSelectedShapeIds()).toMatchObject([ids.boxA])
expect(editor.croppingShapeId).toBe(null) expect(editor.croppingShapeId).toBe(null)
editor.doubleClick(550, 550, ids.imageB) editor.doubleClick(550, 550, ids.imageB)
editor.expectPathToBe('root.select.crop.idle') editor.expectPathToBe('root.select.crop.idle')
expect(editor.selectedShapeIds).toMatchObject([ids.imageB]) expect(editor.getSelectedShapeIds()).toMatchObject([ids.imageB])
expect(editor.croppingShapeId).toBe(ids.imageB) expect(editor.croppingShapeId).toBe(ids.imageB)
editor.undo() editor.undo()
// first selection should have been a mark // first selection should have been a mark
editor.expectPathToBe('root.select.idle') editor.expectPathToBe('root.select.idle')
expect(editor.selectedShapeIds).toMatchObject([ids.imageB]) expect(editor.getSelectedShapeIds()).toMatchObject([ids.imageB])
expect(editor.croppingShapeId).toBe(null) expect(editor.croppingShapeId).toBe(null)
editor.undo() editor.undo()
// back to start // back to start
editor.expectPathToBe('root.select.idle') editor.expectPathToBe('root.select.idle')
expect(editor.selectedShapeIds).toMatchObject([ids.boxA]) expect(editor.getSelectedShapeIds()).toMatchObject([ids.boxA])
expect(editor.croppingShapeId).toBe(null) expect(editor.croppingShapeId).toBe(null)
editor editor
@ -119,7 +119,7 @@ describe('When in the select.idle state', () => {
.redo() // crop again .redo() // crop again
editor.expectPathToBe('root.select.crop.idle') editor.expectPathToBe('root.select.crop.idle')
expect(editor.selectedShapeIds).toMatchObject([ids.imageB]) expect(editor.getSelectedShapeIds()).toMatchObject([ids.imageB])
expect(editor.croppingShapeId).toBe(ids.imageB) expect(editor.croppingShapeId).toBe(ids.imageB)
}) })
@ -296,7 +296,7 @@ describe('When in the crop.idle state', () => {
.expectPathToBe('root.select.crop.pointing_crop') .expectPathToBe('root.select.crop.pointing_crop')
expect(editor.croppingShapeId).toBe(ids.imageB) expect(editor.croppingShapeId).toBe(ids.imageB)
expect(editor.selectedShapeIds).toMatchObject([ids.imageB]) expect(editor.getSelectedShapeIds()).toMatchObject([ids.imageB])
}) })
it('clicking another image shape should set that shape as the new cropping shape and transition to pointing_crop', () => { it('clicking another image shape should set that shape as the new cropping shape and transition to pointing_crop', () => {
@ -308,7 +308,7 @@ describe('When in the crop.idle state', () => {
.expectPathToBe('root.select.crop.pointing_crop') .expectPathToBe('root.select.crop.pointing_crop')
expect(editor.croppingShapeId).toBe(ids.imageA) expect(editor.croppingShapeId).toBe(ids.imageA)
expect(editor.selectedShapeIds).toMatchObject([ids.imageA]) expect(editor.getSelectedShapeIds()).toMatchObject([ids.imageA])
}) })
it('rotating will return to select.crop.idle', () => { it('rotating will return to select.crop.idle', () => {

View file

@ -14,12 +14,12 @@ const ids = {
beforeEach(() => { beforeEach(() => {
editor = new TestEditor() editor = new TestEditor()
editor.selectAll().deleteShapes(editor.selectedShapeIds) editor.selectAll().deleteShapes(editor.getSelectedShapeIds())
}) })
it('creates new bindings for arrows when pasting', async () => { it('creates new bindings for arrows when pasting', async () => {
editor editor
.selectAll() .selectAll()
.deleteShapes(editor.selectedShapeIds) .deleteShapes(editor.getSelectedShapeIds())
.createShapes([ .createShapes([
{ id: ids.box1, type: 'geo', x: 100, y: 100, props: { w: 100, h: 100 } }, { id: ids.box1, type: 'geo', x: 100, y: 100, props: { w: 100, h: 100 } },
{ id: ids.box2, type: 'geo', x: 300, y: 300, props: { w: 100, h: 100 } }, { id: ids.box2, type: 'geo', x: 300, y: 300, props: { w: 100, h: 100 } },
@ -47,7 +47,7 @@ it('creates new bindings for arrows when pasting', async () => {
const shapesBefore = editor.currentPageShapes const shapesBefore = editor.currentPageShapes
editor.selectAll().duplicateShapes(editor.selectedShapeIds) editor.selectAll().duplicateShapes(editor.getSelectedShapeIds())
const shapesAfter = editor.currentPageShapes const shapesAfter = editor.currentPageShapes
@ -174,17 +174,17 @@ describe('When duplicating shapes that include arrows', () => {
}) })
it('Preserves the same selection bounds', () => { it('Preserves the same selection bounds', () => {
editor.selectAll().deleteShapes(editor.selectedShapeIds).createShapes(shapes).selectAll() editor.selectAll().deleteShapes(editor.getSelectedShapeIds()).createShapes(shapes).selectAll()
const boundsBefore = editor.selectionRotatedPageBounds! const boundsBefore = editor.selectionRotatedPageBounds!
editor.duplicateShapes(editor.selectedShapeIds) editor.duplicateShapes(editor.getSelectedShapeIds())
expect(editor.selectionRotatedPageBounds).toCloselyMatchObject(boundsBefore) expect(editor.selectionRotatedPageBounds).toCloselyMatchObject(boundsBefore)
}) })
it('Preserves the same selection bounds when only duplicating the arrows', () => { it('Preserves the same selection bounds when only duplicating the arrows', () => {
editor editor
.selectAll() .selectAll()
.deleteShapes(editor.selectedShapeIds) .deleteShapes(editor.getSelectedShapeIds())
.createShapes(shapes) .createShapes(shapes)
.select( .select(
...editor.currentPageShapes ...editor.currentPageShapes
@ -193,7 +193,7 @@ describe('When duplicating shapes that include arrows', () => {
) )
const boundsBefore = editor.selectionRotatedPageBounds! const boundsBefore = editor.selectionRotatedPageBounds!
editor.duplicateShapes(editor.selectedShapeIds) editor.duplicateShapes(editor.getSelectedShapeIds())
const boundsAfter = editor.selectionRotatedPageBounds! const boundsAfter = editor.selectionRotatedPageBounds!
// It's not exactly exact, but close enough is plenty close // It's not exactly exact, but close enough is plenty close

View file

@ -23,7 +23,7 @@ const ids = {
beforeEach(() => { beforeEach(() => {
editor = new TestEditor() editor = new TestEditor()
editor.selectAll() editor.selectAll()
editor.deleteShapes(editor.selectedShapeIds) editor.deleteShapes(editor.getSelectedShapeIds())
editor.createShapes([ editor.createShapes([
{ {
id: ids.boxA, id: ids.boxA,
@ -62,7 +62,7 @@ describe('When flipping horizontally', () => {
it('Flips the selected shapes', () => { it('Flips the selected shapes', () => {
editor.select(ids.boxA, ids.boxB, ids.boxC) editor.select(ids.boxA, ids.boxB, ids.boxC)
editor.mark('flipped') editor.mark('flipped')
editor.flipShapes(editor.selectedShapeIds, 'horizontal') editor.flipShapes(editor.getSelectedShapeIds(), 'horizontal')
editor.expectShapeToMatch( editor.expectShapeToMatch(
{ {
@ -106,7 +106,7 @@ describe('When flipping horizontally', () => {
editor.select(ids.boxA, ids.boxB) editor.select(ids.boxA, ids.boxB)
const a = editor.selectionPageBounds const a = editor.selectionPageBounds
editor.mark('flipped') editor.mark('flipped')
editor.flipShapes(editor.selectedShapeIds, 'horizontal') editor.flipShapes(editor.getSelectedShapeIds(), 'horizontal')
const b = editor.selectionPageBounds const b = editor.selectionPageBounds
expect(a!).toCloselyMatchObject(b!) expect(a!).toCloselyMatchObject(b!)
@ -131,7 +131,7 @@ describe('When flipping horizontally', () => {
editor.select(ids.boxB, ids.boxC) editor.select(ids.boxB, ids.boxC)
const a = editor.selectionPageBounds const a = editor.selectionPageBounds
editor.mark('flipped') editor.mark('flipped')
editor.flipShapes(editor.selectedShapeIds, 'horizontal') editor.flipShapes(editor.getSelectedShapeIds(), 'horizontal')
const b = editor.selectionPageBounds const b = editor.selectionPageBounds
expect(a).toCloselyMatchObject(b!) expect(a).toCloselyMatchObject(b!)
@ -142,7 +142,7 @@ describe('When flipping vertically', () => {
it('Flips the selected shapes', () => { it('Flips the selected shapes', () => {
editor.select(ids.boxA, ids.boxB, ids.boxC) editor.select(ids.boxA, ids.boxB, ids.boxC)
editor.mark('flipped') editor.mark('flipped')
editor.flipShapes(editor.selectedShapeIds, 'vertical') editor.flipShapes(editor.getSelectedShapeIds(), 'vertical')
editor.expectShapeToMatch( editor.expectShapeToMatch(
{ {
@ -186,7 +186,7 @@ describe('When flipping vertically', () => {
editor.select(ids.boxA, ids.boxB) editor.select(ids.boxA, ids.boxB)
const a = editor.selectionPageBounds const a = editor.selectionPageBounds
editor.mark('flipped') editor.mark('flipped')
editor.flipShapes(editor.selectedShapeIds, 'vertical') editor.flipShapes(editor.getSelectedShapeIds(), 'vertical')
const b = editor.selectionPageBounds const b = editor.selectionPageBounds
expect(a).toCloselyMatchObject(b!) expect(a).toCloselyMatchObject(b!)
@ -210,7 +210,7 @@ describe('When flipping vertically', () => {
editor.select(ids.boxB, ids.boxC) editor.select(ids.boxB, ids.boxC)
const a = editor.selectionPageBounds const a = editor.selectionPageBounds
editor.mark('flipped') editor.mark('flipped')
editor.flipShapes(editor.selectedShapeIds, 'vertical') editor.flipShapes(editor.getSelectedShapeIds(), 'vertical')
const b = editor.selectionPageBounds const b = editor.selectionPageBounds
expect(a).toCloselyMatchObject(b!) expect(a).toCloselyMatchObject(b!)
@ -221,12 +221,12 @@ it('Preserves the selection bounds.', () => {
editor.selectAll() editor.selectAll()
const a = editor.selectionPageBounds const a = editor.selectionPageBounds
editor.mark('flipped') editor.mark('flipped')
editor.flipShapes(editor.selectedShapeIds, 'horizontal') editor.flipShapes(editor.getSelectedShapeIds(), 'horizontal')
const b = editor.selectionPageBounds const b = editor.selectionPageBounds
expect(a).toMatchObject(b!) expect(a).toMatchObject(b!)
editor.mark('flipped') editor.mark('flipped')
editor.flipShapes(editor.selectedShapeIds, 'vertical') editor.flipShapes(editor.getSelectedShapeIds(), 'vertical')
const c = editor.selectionPageBounds const c = editor.selectionPageBounds
expect(a).toMatchObject(c!) expect(a).toMatchObject(c!)
@ -287,7 +287,7 @@ describe('When one shape is selected', () => {
it('Does nothing if the shape is not a group', () => { it('Does nothing if the shape is not a group', () => {
const before = editor.getShape(ids.boxA)! const before = editor.getShape(ids.boxA)!
editor.select(ids.boxA) editor.select(ids.boxA)
editor.flipShapes(editor.selectedShapeIds, 'horizontal') editor.flipShapes(editor.getSelectedShapeIds(), 'horizontal')
expect(editor.getShape(ids.boxA)).toMatchObject(before) expect(editor.getShape(ids.boxA)).toMatchObject(before)
}) })
@ -296,10 +296,10 @@ describe('When one shape is selected', () => {
const fn = jest.fn() const fn = jest.fn()
editor.selectAll() editor.selectAll()
editor.groupShapes(editor.selectedShapeIds) // 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.selectedShapes[0]
editor.on('change', fn) editor.on('change', fn)
editor.flipShapes(editor.selectedShapeIds, 'horizontal') editor.flipShapes(editor.getSelectedShapeIds(), 'horizontal')
// The change event should have been called // The change event should have been called
jest.runOnlyPendingTimers() jest.runOnlyPendingTimers()
@ -352,7 +352,7 @@ describe('flipping rotated shapes', () => {
arrowD: createShapeId('arrowD'), arrowD: createShapeId('arrowD'),
} }
beforeEach(() => { beforeEach(() => {
editor.selectAll().deleteShapes(editor.selectedShapeIds) editor.selectAll().deleteShapes(editor.getSelectedShapeIds())
const props: Partial<TLArrowShapeProps> = { const props: Partial<TLArrowShapeProps> = {
start: { start: {
type: 'point', type: 'point',
@ -416,7 +416,7 @@ describe('flipping rotated shapes', () => {
} }
test('flipping horizontally', () => { test('flipping horizontally', () => {
editor.flipShapes(editor.selectedShapeIds, 'horizontal') editor.flipShapes(editor.getSelectedShapeIds(), 'horizontal')
// now arrow A should be pointing from top to left // now arrow A should be pointing from top to left
let { start, end } = getStartAndEndPoints(ids.arrowA) let { start, end } = getStartAndEndPoints(ids.arrowA)
expect(start).toCloselyMatchObject(topPoint) expect(start).toCloselyMatchObject(topPoint)
@ -439,7 +439,7 @@ describe('flipping rotated shapes', () => {
}) })
test('flipping vertically', () => { test('flipping vertically', () => {
editor.flipShapes(editor.selectedShapeIds, 'vertical') editor.flipShapes(editor.getSelectedShapeIds(), 'vertical')
// arrows that have height 0 get nudged by a pixel when flipped vertically // arrows that have height 0 get nudged by a pixel when flipped vertically
// so we need to use a fairly loose tolerance // so we need to use a fairly loose tolerance
// now arrow A should be pointing from bottom to right // now arrow A should be pointing from bottom to right
@ -558,18 +558,18 @@ describe('When flipping shapes that include arrows', () => {
}) })
it('Flips horizontally', () => { it('Flips horizontally', () => {
editor.selectAll().deleteShapes(editor.selectedShapeIds).createShapes(shapes) editor.selectAll().deleteShapes(editor.getSelectedShapeIds()).createShapes(shapes)
const boundsBefore = editor.selectionRotatedPageBounds! const boundsBefore = editor.selectionRotatedPageBounds!
editor.flipShapes(editor.selectedShapeIds, 'horizontal') editor.flipShapes(editor.getSelectedShapeIds(), 'horizontal')
expect(editor.selectionRotatedPageBounds).toCloselyMatchObject(boundsBefore) expect(editor.selectionRotatedPageBounds).toCloselyMatchObject(boundsBefore)
}) })
it('Flips vertically', () => { it('Flips vertically', () => {
editor.selectAll().deleteShapes(editor.selectedShapeIds).createShapes(shapes) editor.selectAll().deleteShapes(editor.getSelectedShapeIds()).createShapes(shapes)
const boundsBefore = editor.selectionRotatedPageBounds! const boundsBefore = editor.selectionRotatedPageBounds!
editor.flipShapes(editor.selectedShapeIds, 'vertical') editor.flipShapes(editor.getSelectedShapeIds(), 'vertical')
expect(editor.selectionRotatedPageBounds).toCloselyMatchObject(boundsBefore) expect(editor.selectionRotatedPageBounds).toCloselyMatchObject(boundsBefore)
}) })
}) })

View file

@ -515,15 +515,15 @@ describe('frame shapes', () => {
const frameId = editor.onlySelectedShape!.id const frameId = editor.onlySelectedShape!.id
expect(editor.selectedShapeIds[0]).toBe(frameId) expect(editor.getSelectedShapeIds()[0]).toBe(frameId)
expect(editor.currentPageState.editingShapeId).toBe(null) expect(editor.getCurrentPageState().editingShapeId).toBe(null)
editor.setCurrentTool('select') editor.setCurrentTool('select')
editor.keyDown('Enter') editor.keyDown('Enter')
editor.keyUp('Enter') editor.keyUp('Enter')
expect(editor.currentPageState.editingShapeId).toBe(frameId) expect(editor.getCurrentPageState().editingShapeId).toBe(frameId)
}) })
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', () => {
@ -536,11 +536,11 @@ describe('frame shapes', () => {
editor.pointerDown(50, 50).pointerMove(150, 150) editor.pointerDown(50, 50).pointerMove(150, 150)
editor.expectPathToBe('root.select.brushing') editor.expectPathToBe('root.select.brushing')
expect(editor.selectedShapeIds).toHaveLength(0) expect(editor.getSelectedShapeIds()).toHaveLength(0)
editor.pointerMove(250, 250) editor.pointerMove(250, 250)
expect(editor.selectedShapeIds).toHaveLength(1) expect(editor.getSelectedShapeIds()).toHaveLength(1)
expect(editor.onlySelectedShape!.id).toBe(frameId) expect(editor.onlySelectedShape!.id).toBe(frameId)
}) })
@ -555,7 +555,7 @@ describe('frame shapes', () => {
editor.pointerDown(150, 150).pointerMove(250, 250) editor.pointerDown(150, 150).pointerMove(250, 250)
editor.expectPathToBe('root.select.brushing') editor.expectPathToBe('root.select.brushing')
expect(editor.selectedShapeIds).toHaveLength(0) expect(editor.getSelectedShapeIds()).toHaveLength(0)
}) })
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', () => {
@ -573,14 +573,14 @@ describe('frame shapes', () => {
editor.pointerDown(500, 500).pointerMove(300, 300).pointerUp(300, 300) editor.pointerDown(500, 500).pointerMove(300, 300).pointerUp(300, 300)
// Check if the inner box was selected // Check if the inner box was selected
expect(editor.selectedShapeIds).toHaveLength(0) expect(editor.getSelectedShapeIds()).toHaveLength(0)
// Select from outside the frame via box brushing // Select from outside the frame via box brushing
// but also include the frame in the selection // but also include the frame in the selection
editor.pointerDown(400, 0).pointerMove(195, 175).pointerUp(195, 175) editor.pointerDown(400, 0).pointerMove(195, 175).pointerUp(195, 175)
// Check if the inner box was selected // Check if the inner box was selected
expect(editor.selectedShapeIds).toHaveLength(1) expect(editor.getSelectedShapeIds()).toHaveLength(1)
expect(editor.onlySelectedShape!.id).toBe(innerBoxId) expect(editor.onlySelectedShape!.id).toBe(innerBoxId)
// Deselect everything // Deselect everything
@ -594,7 +594,7 @@ describe('frame shapes', () => {
// Check if the inner box was selected // Check if the inner box was selected
editor.pointerUp(300, 300) editor.pointerUp(300, 300)
expect(editor.selectedShapeIds).toHaveLength(0) expect(editor.getSelectedShapeIds()).toHaveLength(0)
}) })
it('arrows will not bind to parts of shapes outside the frame', () => { it('arrows will not bind to parts of shapes outside the frame', () => {
@ -686,7 +686,7 @@ test('arrows bound to a shape within a group within a frame are reparented if th
editor.setCurrentTool('select') editor.setCurrentTool('select')
editor.select(boxBId, boxCId) editor.select(boxBId, boxCId)
editor.groupShapes(editor.selectedShapeIds) editor.groupShapes(editor.getSelectedShapeIds())
const groupId = editor.onlySelectedShape!.id const groupId = editor.onlySelectedShape!.id
editor.setCurrentTool('arrow') editor.setCurrentTool('arrow')
@ -732,9 +732,9 @@ describe('When dragging a shape inside a group inside a frame', () => {
it('When dragging a shape out of a frame', () => { it('When dragging a shape out of a frame', () => {
editor.select(ids.box1, ids.box2) editor.select(ids.box1, ids.box2)
expect(editor.selectedShapeIds).toHaveLength(2) expect(editor.getSelectedShapeIds()).toHaveLength(2)
editor.groupShapes(editor.selectedShapeIds, ids.group1) editor.groupShapes(editor.getSelectedShapeIds(), ids.group1)
expect(editor.getShape(ids.box1)!.parentId).toBe(ids.group1) expect(editor.getShape(ids.box1)!.parentId).toBe(ids.group1)
@ -752,9 +752,9 @@ describe('When dragging a shape inside a group inside a frame', () => {
it('reparents the shape to the page if it leaves the frame', () => { it('reparents the shape to the page if it leaves the frame', () => {
editor.select(ids.box1, ids.box2) editor.select(ids.box1, ids.box2)
expect(editor.selectedShapeIds).toHaveLength(2) expect(editor.getSelectedShapeIds()).toHaveLength(2)
editor.groupShapes(editor.selectedShapeIds, ids.group1) editor.groupShapes(editor.getSelectedShapeIds(), ids.group1)
expect(editor.getShape(ids.box1)!.parentId).toBe(ids.group1) expect(editor.getShape(ids.box1)!.parentId).toBe(ids.group1)

View file

@ -85,8 +85,8 @@ afterEach(() => {
const getAllShapes = () => editor.currentPageShapes const getAllShapes = () => editor.currentPageShapes
const onlySelectedId = () => { const onlySelectedId = () => {
expect(editor.selectedShapeIds).toHaveLength(1) expect(editor.getSelectedShapeIds()).toHaveLength(1)
return editor.selectedShapeIds[0] return editor.getSelectedShapeIds()[0]
} }
const onlySelectedShape = () => { const onlySelectedShape = () => {
@ -114,12 +114,12 @@ describe('creating groups', () => {
editor.select(ids.boxA, ids.boxB) editor.select(ids.boxA, ids.boxB)
expect(getAllShapes()).toHaveLength(3) expect(getAllShapes()).toHaveLength(3)
expect(editor.selectedShapeIds.length).toBe(2) expect(editor.getSelectedShapeIds().length).toBe(2)
editor.groupShapes(editor.selectedShapeIds) editor.groupShapes(editor.getSelectedShapeIds())
expect(getAllShapes()).toHaveLength(4) expect(getAllShapes()).toHaveLength(4)
expect(editor.selectedShapeIds.length).toBe(1) expect(editor.getSelectedShapeIds().length).toBe(1)
expect(editor.getShape(ids.boxA)).toBeTruthy() expect(editor.getShape(ids.boxA)).toBeTruthy()
expect(editor.getShape(ids.boxB)).toBeTruthy() expect(editor.getShape(ids.boxB)).toBeTruthy()
@ -142,10 +142,10 @@ describe('creating groups', () => {
// └───┘ └───┘ └───┘ // └───┘ └───┘ └───┘
editor.createShapes([box(ids.boxA, 0, 0), box(ids.boxB, 20, 0), box(ids.boxC, 40, 0)]) editor.createShapes([box(ids.boxA, 0, 0), box(ids.boxB, 20, 0), box(ids.boxC, 40, 0)])
expect(getAllShapes()).toHaveLength(3) expect(getAllShapes()).toHaveLength(3)
editor.groupShapes(editor.selectedShapeIds) editor.groupShapes(editor.getSelectedShapeIds())
expect(getAllShapes()).toHaveLength(3) expect(getAllShapes()).toHaveLength(3)
editor.select(ids.boxA) editor.select(ids.boxA)
editor.groupShapes(editor.selectedShapeIds) editor.groupShapes(editor.getSelectedShapeIds())
expect(getAllShapes()).toHaveLength(3) expect(getAllShapes()).toHaveLength(3)
expect(onlySelectedId()).toBe(ids.boxA) expect(onlySelectedId()).toBe(ids.boxA)
}) })
@ -181,7 +181,7 @@ describe('creating groups', () => {
} }
editor.select(ids.boxA, ids.boxB, ids.boxC) editor.select(ids.boxA, ids.boxB, ids.boxC)
editor.groupShapes(editor.selectedShapeIds) editor.groupShapes(editor.getSelectedShapeIds())
try { try {
expect({ expect({
@ -213,17 +213,17 @@ describe('creating groups', () => {
]) ])
editor.select(ids.boxA, ids.boxB) editor.select(ids.boxA, ids.boxB)
editor.groupShapes(editor.selectedShapeIds) editor.groupShapes(editor.getSelectedShapeIds())
const groupAId = onlySelectedId() const groupAId = onlySelectedId()
editor.select(ids.boxC, ids.boxD) editor.select(ids.boxC, ids.boxD)
editor.groupShapes(editor.selectedShapeIds) editor.groupShapes(editor.getSelectedShapeIds())
const groupBId = onlySelectedId() const groupBId = onlySelectedId()
editor.select(groupAId, groupBId) editor.select(groupAId, groupBId)
editor.groupShapes(editor.selectedShapeIds) editor.groupShapes(editor.getSelectedShapeIds())
const uberGroup = onlySelectedShape() const uberGroup = onlySelectedShape()
expect(uberGroup.type).toBe(GroupShapeUtil.type) expect(uberGroup.type).toBe(GroupShapeUtil.type)
@ -258,14 +258,14 @@ describe('creating groups', () => {
]) ])
editor.select(ids.boxA, ids.boxB, ids.boxC) editor.select(ids.boxA, ids.boxB, ids.boxC)
editor.groupShapes(editor.selectedShapeIds) editor.groupShapes(editor.getSelectedShapeIds())
const groupA = onlySelectedShape() const groupA = onlySelectedShape()
editor.select(ids.boxD, ids.boxE, ids.boxF) editor.select(ids.boxD, ids.boxE, ids.boxF)
editor.groupShapes(editor.selectedShapeIds) editor.groupShapes(editor.getSelectedShapeIds())
const groupB = onlySelectedShape() const groupB = onlySelectedShape()
editor.select(ids.boxB, ids.boxE) editor.select(ids.boxB, ids.boxE)
editor.groupShapes(editor.selectedShapeIds) editor.groupShapes(editor.getSelectedShapeIds())
const groupC = onlySelectedShape() const groupC = onlySelectedShape()
expect(children(groupA).size).toBe(2) expect(children(groupA).size).toBe(2)
@ -294,9 +294,9 @@ describe('creating groups', () => {
editor.updateInstanceState({ isReadonly: true }) editor.updateInstanceState({ isReadonly: true })
editor.setCurrentTool('hand') editor.setCurrentTool('hand')
editor.selectAll() editor.selectAll()
expect(editor.selectedShapeIds.length).toBe(3) expect(editor.getSelectedShapeIds().length).toBe(3)
editor.groupShapes(editor.selectedShapeIds) editor.groupShapes(editor.getSelectedShapeIds())
expect(editor.selectedShapeIds.length).toBe(3) expect(editor.getSelectedShapeIds().length).toBe(3)
}) })
it('keeps order correct simple', () => { it('keeps order correct simple', () => {
// 0 10 20 30 40 50 60 70 // 0 10 20 30 40 50 60 70
@ -311,7 +311,7 @@ describe('creating groups', () => {
]) ])
editor.select(ids.boxC, ids.boxB) editor.select(ids.boxC, ids.boxB)
editor.groupShapes(editor.selectedShapeIds) editor.groupShapes(editor.getSelectedShapeIds())
const groupAId = onlySelectedId() const groupAId = onlySelectedId()
const sortedGroupChildrenIds = editor const sortedGroupChildrenIds = editor
@ -344,7 +344,7 @@ describe('creating groups', () => {
]) ])
editor.select(ids.boxC, ids.boxA) editor.select(ids.boxC, ids.boxA)
editor.groupShapes(editor.selectedShapeIds) editor.groupShapes(editor.getSelectedShapeIds())
const groupAId = onlySelectedId() const groupAId = onlySelectedId()
@ -375,14 +375,14 @@ describe('ungrouping shapes', () => {
editor.createShapes([box(ids.boxA, 0, 0), box(ids.boxB, 20, 0), box(ids.boxC, 40, 0)]) editor.createShapes([box(ids.boxA, 0, 0), box(ids.boxB, 20, 0), box(ids.boxC, 40, 0)])
editor.select(ids.boxA, ids.boxB) editor.select(ids.boxA, ids.boxB)
editor.groupShapes(editor.selectedShapeIds) editor.groupShapes(editor.getSelectedShapeIds())
const groupA = onlySelectedShape() const groupA = onlySelectedShape()
editor.ungroupShapes(editor.selectedShapeIds) editor.ungroupShapes(editor.getSelectedShapeIds())
expect(isRemoved(groupA)).toBe(true) expect(isRemoved(groupA)).toBe(true)
expect(new Set(editor.selectedShapeIds)).toEqual(new Set([ids.boxA, ids.boxB])) expect(new Set(editor.getSelectedShapeIds())).toEqual(new Set([ids.boxA, ids.boxB]))
expect(editor.getShapePageBounds(ids.boxA)!).toCloselyMatchObject({ expect(editor.getShapePageBounds(ids.boxA)!).toCloselyMatchObject({
x: 0, x: 0,
@ -401,14 +401,16 @@ describe('ungrouping shapes', () => {
it('selects the groups children and other non-group shapes on ungroup', () => { it('selects the groups children and other non-group shapes on ungroup', () => {
editor.createShapes([box(ids.boxA, 0, 0), box(ids.boxB, 20, 0), box(ids.boxC, 40, 0)]) editor.createShapes([box(ids.boxA, 0, 0), box(ids.boxB, 20, 0), box(ids.boxC, 40, 0)])
editor.select(ids.boxA, ids.boxB) editor.select(ids.boxA, ids.boxB)
editor.groupShapes(editor.selectedShapeIds) editor.groupShapes(editor.getSelectedShapeIds())
const groupA = onlySelectedShape() const groupA = onlySelectedShape()
editor.select(groupA.id, ids.boxC) editor.select(groupA.id, ids.boxC)
editor.ungroupShapes(editor.selectedShapeIds) editor.ungroupShapes(editor.getSelectedShapeIds())
expect(new Set(editor.selectedShapeIds)).toMatchObject(new Set([ids.boxA, ids.boxB, ids.boxC])) expect(new Set(editor.getSelectedShapeIds())).toMatchObject(
new Set([ids.boxA, ids.boxB, ids.boxC])
)
}) })
it('preserves the page positions and rotations of the ungrouped shapes', () => { it('preserves the page positions and rotations of the ungrouped shapes', () => {
for (let i = 0; i < 100; i++) { for (let i = 0; i < 100; i++) {
@ -442,9 +444,9 @@ describe('ungrouping shapes', () => {
} }
editor.select(ids.boxA, ids.boxB, ids.boxC) editor.select(ids.boxA, ids.boxB, ids.boxC)
editor.groupShapes(editor.selectedShapeIds) editor.groupShapes(editor.getSelectedShapeIds())
editor.ungroupShapes(editor.selectedShapeIds) editor.ungroupShapes(editor.getSelectedShapeIds())
expect(editor.selectedShapeIds.length).toBe(3) expect(editor.getSelectedShapeIds().length).toBe(3)
try { try {
expect({ expect({
@ -476,20 +478,20 @@ describe('ungrouping shapes', () => {
]) ])
editor.select(ids.boxA, ids.boxB) editor.select(ids.boxA, ids.boxB)
editor.groupShapes(editor.selectedShapeIds) editor.groupShapes(editor.getSelectedShapeIds())
const groupAId = onlySelectedId() const groupAId = onlySelectedId()
editor.select(ids.boxC, ids.boxD) editor.select(ids.boxC, ids.boxD)
editor.groupShapes(editor.selectedShapeIds) editor.groupShapes(editor.getSelectedShapeIds())
const groupBId = onlySelectedId() const groupBId = onlySelectedId()
editor.select(groupAId, groupBId) editor.select(groupAId, groupBId)
editor.groupShapes(editor.selectedShapeIds) editor.groupShapes(editor.getSelectedShapeIds())
expect(editor.selectedShapeIds.length).toBe(1) expect(editor.getSelectedShapeIds().length).toBe(1)
editor.ungroupShapes(editor.selectedShapeIds) editor.ungroupShapes(editor.getSelectedShapeIds())
expect(editor.selectedShapeIds.length).toBe(2) expect(editor.getSelectedShapeIds().length).toBe(2)
expect(editor.getShape(groupAId)).not.toBe(undefined) expect(editor.getShape(groupAId)).not.toBe(undefined)
expect(editor.getShape(groupBId)).not.toBe(undefined) expect(editor.getShape(groupBId)).not.toBe(undefined)
}) })
@ -500,14 +502,14 @@ describe('ungrouping shapes', () => {
// └───┘ └───┘ └───┘ // └───┘ └───┘ └───┘
editor.createShapes([box(ids.boxA, 0, 0), box(ids.boxB, 20, 0), box(ids.boxC, 40, 0)]) editor.createShapes([box(ids.boxA, 0, 0), box(ids.boxB, 20, 0), box(ids.boxC, 40, 0)])
editor.selectAll() editor.selectAll()
expect(editor.selectedShapeIds.length).toBe(3) expect(editor.getSelectedShapeIds().length).toBe(3)
editor.groupShapes(editor.selectedShapeIds) editor.groupShapes(editor.getSelectedShapeIds())
expect(editor.selectedShapeIds.length).toBe(1) expect(editor.getSelectedShapeIds().length).toBe(1)
editor.updateInstanceState({ isReadonly: true }) editor.updateInstanceState({ isReadonly: true })
editor.setCurrentTool('hand') editor.setCurrentTool('hand')
editor.ungroupShapes(editor.selectedShapeIds) editor.ungroupShapes(editor.getSelectedShapeIds())
expect(editor.selectedShapeIds.length).toBe(1) expect(editor.getSelectedShapeIds().length).toBe(1)
expect(onlySelectedShape().type).toBe(GroupShapeUtil.type) expect(onlySelectedShape().type).toBe(GroupShapeUtil.type)
}) })
it('keeps order correct simple', () => { it('keeps order correct simple', () => {
@ -523,8 +525,8 @@ describe('ungrouping shapes', () => {
]) ])
editor.select(ids.boxC, ids.boxB) editor.select(ids.boxC, ids.boxB)
editor.groupShapes(editor.selectedShapeIds) editor.groupShapes(editor.getSelectedShapeIds())
editor.ungroupShapes(editor.selectedShapeIds) editor.ungroupShapes(editor.getSelectedShapeIds())
const sortedShapesOnCurrentPage = editor.currentPageShapes const sortedShapesOnCurrentPage = editor.currentPageShapes
.sort(sortByIndex) .sort(sortByIndex)
@ -548,8 +550,8 @@ describe('ungrouping shapes', () => {
]) ])
editor.select(ids.boxC, ids.boxA) editor.select(ids.boxC, ids.boxA)
editor.groupShapes(editor.selectedShapeIds) editor.groupShapes(editor.getSelectedShapeIds())
editor.ungroupShapes(editor.selectedShapeIds) editor.ungroupShapes(editor.getSelectedShapeIds())
const sortedShapesOnCurrentPage = editor.currentPageShapes const sortedShapesOnCurrentPage = editor.currentPageShapes
.sort(sortByIndex) .sort(sortByIndex)
@ -580,7 +582,7 @@ describe('the bounds of a group', () => {
]) ])
editor.select(ids.boxA, ids.boxB) editor.select(ids.boxA, ids.boxB)
editor.groupShapes(editor.selectedShapeIds) editor.groupShapes(editor.getSelectedShapeIds())
const group = onlySelectedShape() const group = onlySelectedShape()
expect(editor.getShapePageBounds(group.id)!.minX).toBe(0) expect(editor.getShapePageBounds(group.id)!.minX).toBe(0)
@ -605,7 +607,7 @@ describe('the bounds of a group', () => {
editor.createShapes([box(ids.boxA, 0, 0), box(ids.boxB, 20, 0), box(ids.boxC, 40, 0)]) editor.createShapes([box(ids.boxA, 0, 0), box(ids.boxB, 20, 0), box(ids.boxC, 40, 0)])
editor.select(ids.boxA, ids.boxB, ids.boxC) editor.select(ids.boxA, ids.boxB, ids.boxC)
editor.groupShapes(editor.selectedShapeIds) editor.groupShapes(editor.getSelectedShapeIds())
const group = onlySelectedShape() const group = onlySelectedShape()
expect(editor.getShapePageBounds(group.id)!).toCloselyMatchObject({ expect(editor.getShapePageBounds(group.id)!).toCloselyMatchObject({
@ -641,7 +643,7 @@ describe('the bounds of a group', () => {
editor.createShapes([box(ids.boxA, 0, 0), box(ids.boxB, 20, 0), box(ids.boxC, 40, 0)]) editor.createShapes([box(ids.boxA, 0, 0), box(ids.boxB, 20, 0), box(ids.boxC, 40, 0)])
editor.select(ids.boxA, ids.boxB, ids.boxC) editor.select(ids.boxA, ids.boxB, ids.boxC)
editor.groupShapes(editor.selectedShapeIds) editor.groupShapes(editor.getSelectedShapeIds())
const group = onlySelectedShape() const group = onlySelectedShape()
expect(editor.getShapePageBounds(group.id)!).toCloselyMatchObject({ expect(editor.getShapePageBounds(group.id)!).toCloselyMatchObject({
@ -688,7 +690,7 @@ describe('the bounds of a rotated group', () => {
]) ])
editor.select(ids.boxA, ids.boxB) editor.select(ids.boxA, ids.boxB)
editor.groupShapes(editor.selectedShapeIds) editor.groupShapes(editor.getSelectedShapeIds())
const group = onlySelectedShape() const group = onlySelectedShape()
editor.rotateSelection(Math.PI / 2) editor.rotateSelection(Math.PI / 2)
@ -721,7 +723,7 @@ describe('the bounds of a rotated group', () => {
editor.createShapes([box(ids.boxA, 0, 0), box(ids.boxB, 20, 0), box(ids.boxC, 40, 0)]) editor.createShapes([box(ids.boxA, 0, 0), box(ids.boxB, 20, 0), box(ids.boxC, 40, 0)])
editor.select(ids.boxA, ids.boxB, ids.boxC) editor.select(ids.boxA, ids.boxB, ids.boxC)
editor.groupShapes(editor.selectedShapeIds) editor.groupShapes(editor.getSelectedShapeIds())
const group = onlySelectedShape() const group = onlySelectedShape()
editor.updateShapes([{ id: group.id, type: 'group', rotation: Math.PI / 2, x: 10, y: 0 }]) editor.updateShapes([{ id: group.id, type: 'group', rotation: Math.PI / 2, x: 10, y: 0 }])
@ -761,7 +763,7 @@ describe('the bounds of a rotated group', () => {
expect(editor.getShapeGeometry(ids.boxA)!.bounds).toMatchObject({ x: 0, y: 0, w: 10, h: 10 }) expect(editor.getShapeGeometry(ids.boxA)!.bounds).toMatchObject({ x: 0, y: 0, w: 10, h: 10 })
editor.select(ids.boxA, ids.boxB, ids.boxC) editor.select(ids.boxA, ids.boxB, ids.boxC)
editor.groupShapes(editor.selectedShapeIds) editor.groupShapes(editor.getSelectedShapeIds())
const group = onlySelectedShape() const group = onlySelectedShape()
editor.updateShapes([{ id: group.id, type: 'group', rotation: Math.PI / 2, x: 10, y: 0 }]) editor.updateShapes([{ id: group.id, type: 'group', rotation: Math.PI / 2, x: 10, y: 0 }])
@ -812,13 +814,13 @@ describe('focus layers', () => {
box(ids.boxD, 60, 0), box(ids.boxD, 60, 0),
]) ])
editor.select(ids.boxA, ids.boxB) editor.select(ids.boxA, ids.boxB)
editor.groupShapes(editor.selectedShapeIds) editor.groupShapes(editor.getSelectedShapeIds())
groupAId = onlySelectedId() groupAId = onlySelectedId()
editor.select(ids.boxC, ids.boxD) editor.select(ids.boxC, ids.boxD)
editor.groupShapes(editor.selectedShapeIds) editor.groupShapes(editor.getSelectedShapeIds())
groupBId = onlySelectedId() groupBId = onlySelectedId()
editor.select(groupAId, groupBId) editor.select(groupAId, groupBId)
editor.groupShapes(editor.selectedShapeIds) editor.groupShapes(editor.getSelectedShapeIds())
groupCId = onlySelectedId() groupCId = onlySelectedId()
editor.selectNone() editor.selectNone()
}) })
@ -839,11 +841,11 @@ describe('focus layers', () => {
expect(editor.focusedGroupId).toBe(editor.currentPageId) expect(editor.focusedGroupId).toBe(editor.currentPageId)
editor.select(ids.boxA) editor.select(ids.boxA)
expect(editor.focusedGroupId).toBe(groupAId) expect(editor.focusedGroupId).toBe(groupAId)
editor.setSelectedShapes([...editor.selectedShapeIds, ids.boxC]) editor.setSelectedShapes([...editor.getSelectedShapeIds(), ids.boxC])
expect(editor.focusedGroupId).toBe(groupCId) expect(editor.focusedGroupId).toBe(groupCId)
editor.deselect(ids.boxA) editor.deselect(ids.boxA)
expect(editor.focusedGroupId).toBe(groupBId) expect(editor.focusedGroupId).toBe(groupBId)
editor.setSelectedShapes([...editor.selectedShapeIds, ids.boxB]) editor.setSelectedShapes([...editor.getSelectedShapeIds(), ids.boxB])
expect(editor.focusedGroupId).toBe(groupCId) expect(editor.focusedGroupId).toBe(groupCId)
}) })
it('should not adjust the focus layer when clearing the selection', () => { it('should not adjust the focus layer when clearing the selection', () => {
@ -960,14 +962,14 @@ describe('the select tool', () => {
editor editor
.pointerDown(60, 0, ids.boxC, { shiftKey: true }) .pointerDown(60, 0, ids.boxC, { shiftKey: true })
.pointerUp(0, 0, ids.boxC, { shiftKey: true }) .pointerUp(0, 0, ids.boxC, { shiftKey: true })
expect(editor.selectedShapeIds.includes(ids.boxA)).toBe(true) expect(editor.getSelectedShapeIds().includes(ids.boxA)).toBe(true)
expect(editor.selectedShapeIds.includes(groupBId)).toBe(true) expect(editor.getSelectedShapeIds().includes(groupBId)).toBe(true)
expect(editor.focusedGroupId).toBe(groupCId) expect(editor.focusedGroupId).toBe(groupCId)
editor.pointerDown(60, 0, ids.boxC, { shiftKey: true }).pointerUp() editor.pointerDown(60, 0, ids.boxC, { shiftKey: true }).pointerUp()
expect(editor.selectedShapeIds.includes(ids.boxA)).toBe(true) expect(editor.getSelectedShapeIds().includes(ids.boxA)).toBe(true)
expect(editor.selectedShapeIds.includes(groupBId)).toBe(false) expect(editor.getSelectedShapeIds().includes(groupBId)).toBe(false)
expect(editor.selectedShapeIds.includes(ids.boxC)).toBe(true) expect(editor.getSelectedShapeIds().includes(ids.boxC)).toBe(true)
expect(editor.focusedGroupId).toBe(groupCId) expect(editor.focusedGroupId).toBe(groupCId)
}) })
@ -978,7 +980,7 @@ describe('the select tool', () => {
// click outside the focused group, but inside another group // click outside the focused group, but inside another group
editor.pointerDown(-235, 5, { target: 'canvas' }).pointerUp(-235, 5) editor.pointerDown(-235, 5, { target: 'canvas' }).pointerUp(-235, 5)
expect(editor.focusedGroupId).toBe(editor.currentPageId) expect(editor.focusedGroupId).toBe(editor.currentPageId)
expect(editor.selectedShapeIds).toHaveLength(0) expect(editor.getSelectedShapeIds()).toHaveLength(0)
editor.select(ids.boxA) editor.select(ids.boxA)
expect(editor.focusedGroupId).toBe(groupAId) expect(editor.focusedGroupId).toBe(groupAId)
@ -986,7 +988,7 @@ describe('the select tool', () => {
// click the empty canvas // click the empty canvas
editor.pointerDown(-235, 50, { target: 'canvas' }).pointerUp(-235, 50) editor.pointerDown(-235, 50, { target: 'canvas' }).pointerUp(-235, 50)
expect(editor.focusedGroupId).toBe(editor.currentPageId) expect(editor.focusedGroupId).toBe(editor.currentPageId)
expect(editor.selectedShapeIds).toHaveLength(0) expect(editor.getSelectedShapeIds()).toHaveLength(0)
}) })
// ! Removed with hollow shape clicking feature // ! Removed with hollow shape clicking feature
@ -1013,18 +1015,18 @@ describe('the select tool', () => {
it('should pop the focus layer when escape is pressed in idle state', () => { it('should pop the focus layer when escape is pressed in idle state', () => {
editor.select(ids.boxA) editor.select(ids.boxA)
expect(editor.selectedShapeIds).toMatchObject([ids.boxA]) // box1 expect(editor.getSelectedShapeIds()).toMatchObject([ids.boxA]) // box1
expect(editor.focusedGroupId).toBe(groupAId) expect(editor.focusedGroupId).toBe(groupAId)
// deselct // deselct
editor.cancel() editor.cancel()
expect(editor.selectedShapeIds).toMatchObject([groupAId]) // groupA expect(editor.getSelectedShapeIds()).toMatchObject([groupAId]) // groupA
expect(editor.focusedGroupId).toBe(groupCId) expect(editor.focusedGroupId).toBe(groupCId)
// pop focus layer // pop focus layer
editor.cancel() editor.cancel()
expect(editor.selectedShapeIds.length).toBe(1) // Group C expect(editor.getSelectedShapeIds().length).toBe(1) // Group C
expect(editor.focusedGroupId).toBe(editor.currentPageId) expect(editor.focusedGroupId).toBe(editor.currentPageId)
editor.cancel() editor.cancel()
expect(editor.selectedShapeIds.length).toBe(0) expect(editor.getSelectedShapeIds().length).toBe(0)
expect(editor.focusedGroupId).toBe(editor.currentPageId) expect(editor.focusedGroupId).toBe(editor.currentPageId)
}) })
@ -1047,8 +1049,8 @@ describe('the select tool', () => {
editor.pointerDown(-305, -5, { target: 'canvas' }).pointerMove(35, 9, ids.boxB) editor.pointerDown(-305, -5, { target: 'canvas' }).pointerMove(35, 9, ids.boxB)
expect(editor.root.path.get()).toBe(`root.select.brushing`) expect(editor.root.path.get()).toBe(`root.select.brushing`)
expect(editor.selectedShapeIds.includes(ids.boxA)).toBe(true) expect(editor.getSelectedShapeIds().includes(ids.boxA)).toBe(true)
expect(editor.selectedShapeIds.includes(ids.boxB)).toBe(true) expect(editor.getSelectedShapeIds().includes(ids.boxB)).toBe(true)
// this one didn't make sense as written—the forced event on canvas won't work now // this one didn't make sense as written—the forced event on canvas won't work now
// that we're doing hit testing manually—we'll catch that it was inside a shape // that we're doing hit testing manually—we'll catch that it was inside a shape
@ -1076,7 +1078,7 @@ describe('the select tool', () => {
// ▲ // ▲
// │ mouse selection // │ mouse selection
editor.pointerDown(12.5, -15, undefined).pointerMove(17.5, 15, ids.boxB) editor.pointerDown(12.5, -15, undefined).pointerMove(17.5, 15, ids.boxB)
expect(editor.selectedShapeIds.length).toBe(0) expect(editor.getSelectedShapeIds().length).toBe(0)
editor.pointerMove(35, 15) editor.pointerMove(35, 15)
expect(onlySelectedId()).toBe(groupCId) expect(onlySelectedId()).toBe(groupCId)
}) })
@ -1104,13 +1106,13 @@ describe("when a group's children are deleted", () => {
box(ids.boxD, 60, 0), box(ids.boxD, 60, 0),
]) ])
editor.select(ids.boxA, ids.boxB) editor.select(ids.boxA, ids.boxB)
editor.groupShapes(editor.selectedShapeIds) editor.groupShapes(editor.getSelectedShapeIds())
groupAId = onlySelectedId() groupAId = onlySelectedId()
editor.select(ids.boxC, ids.boxD) editor.select(ids.boxC, ids.boxD)
editor.groupShapes(editor.selectedShapeIds) editor.groupShapes(editor.getSelectedShapeIds())
groupBId = onlySelectedId() groupBId = onlySelectedId()
editor.select(groupAId, groupBId) editor.select(groupAId, groupBId)
editor.groupShapes(editor.selectedShapeIds) editor.groupShapes(editor.getSelectedShapeIds())
groupCId = onlySelectedId() groupCId = onlySelectedId()
editor.selectNone() editor.selectNone()
}) })
@ -1148,7 +1150,7 @@ describe('creating new shapes', () => {
// └──────────────────────────────┘ // └──────────────────────────────┘
editor.createShapes([box(ids.boxA, 0, 0), box(ids.boxB, 90, 90)]) editor.createShapes([box(ids.boxA, 0, 0), box(ids.boxB, 90, 90)])
editor.select(ids.boxA, ids.boxB) editor.select(ids.boxA, ids.boxB)
editor.groupShapes(editor.selectedShapeIds) editor.groupShapes(editor.getSelectedShapeIds())
groupA = onlySelectedShape() as TLGroupShape groupA = onlySelectedShape() as TLGroupShape
editor.selectNone() editor.selectNone()
}) })
@ -1478,13 +1480,13 @@ describe('erasing', () => {
box(ids.boxE, 0, 20), box(ids.boxE, 0, 20),
]) ])
editor.select(ids.boxA, ids.boxB) editor.select(ids.boxA, ids.boxB)
editor.groupShapes(editor.selectedShapeIds) editor.groupShapes(editor.getSelectedShapeIds())
groupAId = onlySelectedId() groupAId = onlySelectedId()
editor.select(ids.boxC, ids.boxD) editor.select(ids.boxC, ids.boxD)
editor.groupShapes(editor.selectedShapeIds) editor.groupShapes(editor.getSelectedShapeIds())
groupBId = onlySelectedId() groupBId = onlySelectedId()
editor.select(groupAId, groupBId) editor.select(groupAId, groupBId)
editor.groupShapes(editor.selectedShapeIds) editor.groupShapes(editor.getSelectedShapeIds())
groupCId = onlySelectedId() groupCId = onlySelectedId()
editor.selectNone() editor.selectNone()
}) })
@ -1494,8 +1496,8 @@ describe('erasing', () => {
// erase D // erase D
editor.pointerDown(65, 5, ids.boxD) editor.pointerDown(65, 5, ids.boxD)
expect(editor.currentPageState.erasingShapeIds.length).toBe(1) expect(editor.getCurrentPageState().erasingShapeIds.length).toBe(1)
expect(editor.currentPageState.erasingShapeIds[0]).toBe(groupCId) expect(editor.getCurrentPageState().erasingShapeIds[0]).toBe(groupCId)
editor.pointerUp() editor.pointerUp()
expect(editor.getShape(groupCId)).toBeFalsy() expect(editor.getShape(groupCId)).toBeFalsy()
}) })
@ -1515,8 +1517,8 @@ describe('erasing', () => {
// erase B // erase B
editor.pointerDown(25, 5, ids.boxB) editor.pointerDown(25, 5, ids.boxB)
expect(editor.currentPageState.erasingShapeIds.length).toBe(1) expect(editor.getCurrentPageState().erasingShapeIds.length).toBe(1)
expect(editor.currentPageState.erasingShapeIds[0]).toBe(ids.boxB) expect(editor.getCurrentPageState().erasingShapeIds[0]).toBe(ids.boxB)
editor.pointerUp() editor.pointerUp()
// group A disappears // group A disappears
@ -1531,8 +1533,8 @@ describe('erasing', () => {
// erase E // erase E
editor.pointerDown(5, 25, ids.boxE) editor.pointerDown(5, 25, ids.boxE)
expect(editor.currentPageState.erasingShapeIds.length).toBe(1) expect(editor.getCurrentPageState().erasingShapeIds.length).toBe(1)
expect(editor.currentPageState.erasingShapeIds[0]).toBe(ids.boxE) expect(editor.getCurrentPageState().erasingShapeIds[0]).toBe(ids.boxE)
// move to group B // move to group B
editor.pointerMove(65, 5) editor.pointerMove(65, 5)
@ -1545,7 +1547,7 @@ describe('binding bug', () => {
beforeEach(() => { beforeEach(() => {
editor.createShapes([box(ids.boxA, 0, 0, 10, 10), box(ids.boxB, 50, 0, 10, 10)]) editor.createShapes([box(ids.boxA, 0, 0, 10, 10), box(ids.boxB, 50, 0, 10, 10)])
editor.select(ids.boxA, ids.boxB) editor.select(ids.boxA, ids.boxB)
editor.groupShapes(editor.selectedShapeIds) editor.groupShapes(editor.getSelectedShapeIds())
editor.selectNone() editor.selectNone()
}) })
@ -1585,13 +1587,13 @@ describe('bindings', () => {
box(ids.boxE, 0, 20), box(ids.boxE, 0, 20),
]) ])
editor.select(ids.boxA, ids.boxB) editor.select(ids.boxA, ids.boxB)
editor.groupShapes(editor.selectedShapeIds) editor.groupShapes(editor.getSelectedShapeIds())
groupAId = onlySelectedId() groupAId = onlySelectedId()
editor.select(ids.boxC, ids.boxD) editor.select(ids.boxC, ids.boxD)
editor.groupShapes(editor.selectedShapeIds) editor.groupShapes(editor.getSelectedShapeIds())
groupBId = onlySelectedId() groupBId = onlySelectedId()
editor.select(groupAId, groupBId) editor.select(groupAId, groupBId)
editor.groupShapes(editor.selectedShapeIds) editor.groupShapes(editor.getSelectedShapeIds())
editor.selectNone() editor.selectNone()
}) })
@ -1670,7 +1672,7 @@ describe('grouping arrows', () => {
expect(arrowBBefore.index).toBe('a2') expect(arrowBBefore.index).toBe('a2')
editor.select(arrowAId, arrowBId) editor.select(arrowAId, arrowBId)
editor.groupShapes(editor.selectedShapeIds) editor.groupShapes(editor.getSelectedShapeIds())
const arrowAAfter = editor.getShape(arrowAId)! const arrowAAfter = editor.getShape(arrowAId)!
const arrowBAfter = editor.getShape(arrowBId)! const arrowBAfter = editor.getShape(arrowBId)!
@ -1702,7 +1704,7 @@ describe('moving handles within a group', () => {
// └──────────────────────────────┘ // └──────────────────────────────┘
editor.createShapes([box(ids.boxA, 0, 0), box(ids.boxB, 90, 90)]) editor.createShapes([box(ids.boxA, 0, 0), box(ids.boxB, 90, 90)])
editor.select(ids.boxA, ids.boxB) editor.select(ids.boxA, ids.boxB)
editor.groupShapes(editor.selectedShapeIds) editor.groupShapes(editor.getSelectedShapeIds())
groupA = onlySelectedShape() as TLGroupShape groupA = onlySelectedShape() as TLGroupShape
editor.selectNone() editor.selectNone()
}) })
@ -1903,13 +1905,13 @@ describe('snapping', () => {
box(ids.boxE, 0, 20), box(ids.boxE, 0, 20),
]) ])
editor.select(ids.boxA, ids.boxB) editor.select(ids.boxA, ids.boxB)
editor.groupShapes(editor.selectedShapeIds) editor.groupShapes(editor.getSelectedShapeIds())
groupAId = onlySelectedId() groupAId = onlySelectedId()
editor.select(ids.boxC, ids.boxD) editor.select(ids.boxC, ids.boxD)
editor.groupShapes(editor.selectedShapeIds) editor.groupShapes(editor.getSelectedShapeIds())
groupBId = onlySelectedId() groupBId = onlySelectedId()
editor.select(groupAId, groupBId) editor.select(groupAId, groupBId)
editor.groupShapes(editor.selectedShapeIds) editor.groupShapes(editor.getSelectedShapeIds())
groupCId = onlySelectedId() groupCId = onlySelectedId()
editor.selectNone() editor.selectNone()
}) })
@ -1933,22 +1935,22 @@ describe('When pressing enter with selected group', () => {
it('Should select the children of the group when enter is pressed', () => { it('Should select the children of the group when enter is pressed', () => {
editor.createShapes([box(ids.boxA, 0, 0), box(ids.boxB, 20, 0), box(ids.boxC, 40, 0)]) editor.createShapes([box(ids.boxA, 0, 0), box(ids.boxB, 20, 0), box(ids.boxC, 40, 0)])
editor.select(ids.boxA, ids.boxB, ids.boxC) editor.select(ids.boxA, ids.boxB, ids.boxC)
editor.groupShapes(editor.selectedShapeIds) editor.groupShapes(editor.getSelectedShapeIds())
editor.keyDown('Enter') editor.keyDown('Enter')
editor.keyUp('Enter') editor.keyUp('Enter')
expect(editor.selectedShapeIds).toMatchObject([ids.boxA, ids.boxB, ids.boxC]) expect(editor.getSelectedShapeIds()).toMatchObject([ids.boxA, ids.boxB, ids.boxC])
}) })
it('Should select the children of multiple groups when enter is pressed', () => { it('Should select the children of multiple groups when enter is pressed', () => {
editor.createShapes([box(ids.boxA, 0, 0), box(ids.boxB, 20, 0)]) editor.createShapes([box(ids.boxA, 0, 0), box(ids.boxB, 20, 0)])
editor.createShapes([box(ids.boxC, 40, 0), box(ids.boxD, 70, 0)]) editor.createShapes([box(ids.boxC, 40, 0), box(ids.boxD, 70, 0)])
editor.select(ids.boxA, ids.boxB) editor.select(ids.boxA, ids.boxB)
editor.groupShapes(editor.selectedShapeIds) editor.groupShapes(editor.getSelectedShapeIds())
editor.select(ids.boxC, ids.boxD) editor.select(ids.boxC, ids.boxD)
editor.groupShapes(editor.selectedShapeIds) editor.groupShapes(editor.getSelectedShapeIds())
editor.selectAll() // both groups editor.selectAll() // both groups
editor.keyDown('Enter') editor.keyDown('Enter')
editor.keyUp('Enter') editor.keyUp('Enter')
expect(editor.selectedShapeIds).toMatchObject([ids.boxA, ids.boxB, ids.boxC, ids.boxD]) expect(editor.getSelectedShapeIds()).toMatchObject([ids.boxA, ids.boxB, ids.boxC, ids.boxD])
}) })
}) })
@ -1958,7 +1960,7 @@ describe('Group opacity', () => {
editor.select(ids.boxA, ids.boxB) editor.select(ids.boxA, ids.boxB)
editor.setOpacityForSelectedShapes(0.5) editor.setOpacityForSelectedShapes(0.5)
editor.setOpacityForNextShapes(0.5) editor.setOpacityForNextShapes(0.5)
editor.groupShapes(editor.selectedShapeIds) editor.groupShapes(editor.getSelectedShapeIds())
const group = editor.getShape(onlySelectedId())! const group = editor.getShape(onlySelectedId())!
assert(editor.isShapeOfType<TLGroupShape>(group, 'group')) assert(editor.isShapeOfType<TLGroupShape>(group, 'group'))
expect(group.opacity).toBe(1) expect(group.opacity).toBe(1)
@ -1988,7 +1990,7 @@ describe('Grouping / ungrouping locked shapes', () => {
expect(editor.getShape(ids.boxB)).toBeTruthy() expect(editor.getShape(ids.boxB)).toBeTruthy()
// Both be selected // Both be selected
expect(editor.selectedShapeIds).toMatchObject([ids.boxA, ids.boxB]) expect(editor.getSelectedShapeIds()).toMatchObject([ids.boxA, ids.boxB])
// And be in the correct position // And be in the correct position
expect(editor.getShape(ids.boxA)!.x).toBe(100) expect(editor.getShape(ids.boxA)!.x).toBe(100)

View file

@ -363,7 +363,7 @@ it('pastes shapes with children', () => {
describe('When pasting into frames...', () => { describe('When pasting into frames...', () => {
it('Does not paste into a clipped frame', () => { it('Does not paste into a clipped frame', () => {
// clear the page // clear the page
editor.selectAll().deleteShapes(editor.selectedShapeIds) editor.selectAll().deleteShapes(editor.getSelectedShapeIds())
editor editor
// move the two frames far from all other shapes // move the two frames far from all other shapes
@ -416,7 +416,7 @@ describe('When pasting into frames...', () => {
]) ])
// Make sure that frame 1 is brought to front // Make sure that frame 1 is brought to front
.select(ids.frame1) .select(ids.frame1)
.bringToFront(editor.selectedShapeIds) .bringToFront(editor.getSelectedShapeIds())
editor.setCamera({ x: -2000, y: -2000, z: 1 }) editor.setCamera({ x: -2000, y: -2000, z: 1 })
editor.updateRenderingBounds() editor.updateRenderingBounds()
@ -436,7 +436,7 @@ describe('When pasting into frames...', () => {
it('keeps things in the right place', () => { it('keeps things in the right place', () => {
// clear the page // clear the page
editor.selectAll().deleteShapes(editor.selectedShapeIds) editor.selectAll().deleteShapes(editor.getSelectedShapeIds())
// create a small box and copy it // create a small box and copy it
editor.createShapes([ editor.createShapes([
{ {
@ -452,7 +452,7 @@ describe('When pasting into frames...', () => {
]) ])
editor.selectAll().copy() editor.selectAll().copy()
// now delete it // now delete it
editor.deleteShapes(editor.selectedShapeIds) editor.deleteShapes(editor.getSelectedShapeIds())
// create a big frame away from the origin, the size of the viewport // create a big frame away from the origin, the size of the viewport
editor editor

View file

@ -3570,13 +3570,13 @@ describe('nodes that have do not resize', () => {
editor.select(ids.boxA, noteBId, noteCId) editor.select(ids.boxA, noteBId, noteCId)
editor.flipShapes(editor.selectedShapeIds, 'horizontal') editor.flipShapes(editor.getSelectedShapeIds(), 'horizontal')
expect(editor.getShapePageBounds(ids.boxA)).toMatchObject({ x: 300, y: 0, w: 200, h: 200 }) expect(editor.getShapePageBounds(ids.boxA)).toMatchObject({ x: 300, y: 0, w: 200, h: 200 })
expect(editor.getShapePageBounds(noteBId)).toMatchObject({ x: 0, y: 0, w: 200, h: 200 }) expect(editor.getShapePageBounds(noteBId)).toMatchObject({ x: 0, y: 0, w: 200, h: 200 })
expect(editor.getShapePageBounds(noteCId)).toMatchObject({ x: 300, y: 300, w: 200, h: 200 }) expect(editor.getShapePageBounds(noteCId)).toMatchObject({ x: 300, y: 300, w: 200, h: 200 })
editor.flipShapes(editor.selectedShapeIds, 'vertical') editor.flipShapes(editor.getSelectedShapeIds(), 'vertical')
expect(editor.getShapePageBounds(ids.boxA)).toMatchObject({ expect(editor.getShapePageBounds(ids.boxA)).toMatchObject({
x: 300, x: 300,

View file

@ -22,7 +22,7 @@ describe(SelectTool, () => {
editor.expectPathToBe('root.select.idle') editor.expectPathToBe('root.select.idle')
editor.doubleClick(50, 50, shapeId) editor.doubleClick(50, 50, shapeId)
expect(editor.currentPageState.editingShapeId).toBe(shapeId) expect(editor.getCurrentPageState().editingShapeId).toBe(shapeId)
// note: this behavior has moved to the React hook useEditableText. // note: this behavior has moved to the React hook useEditableText.
// clicking on the input will preserve selection, however you can // clicking on the input will preserve selection, however you can
@ -36,7 +36,7 @@ describe(SelectTool, () => {
jest.advanceTimersByTime(1000) jest.advanceTimersByTime(1000)
editor.pointerDown(150, 150).pointerUp() editor.pointerDown(150, 150).pointerUp()
expect(editor.currentPageState.editingShapeId).toBe(null) expect(editor.getCurrentPageState().editingShapeId).toBe(null)
expect(editor.root.path.get()).toEqual('root.select.idle') expect(editor.root.path.get()).toEqual('root.select.idle')
}) })
}) })
@ -48,18 +48,18 @@ describe(SelectTool, () => {
editor.setCurrentTool('select') editor.setCurrentTool('select')
editor.doubleClick(50, 50, shapeId) editor.doubleClick(50, 50, shapeId)
expect(editor.currentPageState.editingShapeId).toBe(shapeId) expect(editor.getCurrentPageState().editingShapeId).toBe(shapeId)
// clicking outside the shape should end editing // clicking outside the shape should end editing
jest.advanceTimersByTime(1000) jest.advanceTimersByTime(1000)
editor.pointerDown(150, 150).pointerUp() editor.pointerDown(150, 150).pointerUp()
expect(editor.currentPageState.editingShapeId).toBe(null) expect(editor.getCurrentPageState().editingShapeId).toBe(null)
expect(editor.root.path.get()).toEqual('root.select.idle') expect(editor.root.path.get()).toEqual('root.select.idle')
editor.undo() editor.undo()
expect(editor.currentPageState.editingShapeId).toBe(null) expect(editor.getCurrentPageState().editingShapeId).toBe(null)
}) })
}) })
@ -74,9 +74,9 @@ describe('When pointing a shape behind the current selection', () => {
editor.select(ids.A, ids.C) editor.select(ids.A, ids.C)
// don't select it yet! It's behind the current selection // don't select it yet! It's behind the current selection
editor.pointerDown(75, 75) editor.pointerDown(75, 75)
expect(editor.selectedShapeIds).toMatchObject([ids.A, ids.C]) expect(editor.getSelectedShapeIds()).toMatchObject([ids.A, ids.C])
editor.pointerUp(75, 75) editor.pointerUp(75, 75)
expect(editor.selectedShapeIds).toMatchObject([ids.B]) expect(editor.getSelectedShapeIds()).toMatchObject([ids.B])
}) })
it('Selects on shift+pointer up', () => { it('Selects on shift+pointer up', () => {
@ -91,20 +91,20 @@ describe('When pointing a shape behind the current selection', () => {
// don't select B yet! It's behind the current selection // don't select B yet! It's behind the current selection
editor.pointerDown(75, 75, { target: 'canvas' }, { shiftKey: true }) editor.pointerDown(75, 75, { target: 'canvas' }, { shiftKey: true })
editor.expectToBeIn('select.pointing_selection') editor.expectToBeIn('select.pointing_selection')
expect(editor.selectedShapeIds).toMatchObject([ids.A, ids.C]) expect(editor.getSelectedShapeIds()).toMatchObject([ids.A, ids.C])
editor.pointerUp(75, 75, { target: 'canvas' }, { shiftKey: true }) editor.pointerUp(75, 75, { target: 'canvas' }, { shiftKey: true })
editor.expectToBeIn('select.idle') editor.expectToBeIn('select.idle')
expect(editor.selectedShapeIds).toMatchObject([ids.A, ids.C, ids.B]) expect(editor.getSelectedShapeIds()).toMatchObject([ids.A, ids.C, ids.B])
// and deselect // and deselect
editor.pointerDown(75, 75, { target: 'canvas' }, { shiftKey: true }) editor.pointerDown(75, 75, { target: 'canvas' }, { shiftKey: true })
editor.expectToBeIn('select.pointing_shape') editor.expectToBeIn('select.pointing_shape')
expect(editor.selectedShapeIds).toMatchObject([ids.A, ids.C, ids.B]) expect(editor.getSelectedShapeIds()).toMatchObject([ids.A, ids.C, ids.B])
editor.pointerUp(75, 75, { target: 'canvas' }, { shiftKey: true }) editor.pointerUp(75, 75, { target: 'canvas' }, { shiftKey: true })
editor.expectToBeIn('select.idle') editor.expectToBeIn('select.idle')
expect(editor.selectedShapeIds).toMatchObject([ids.A, ids.C]) expect(editor.getSelectedShapeIds()).toMatchObject([ids.A, ids.C])
}) })
it('Moves on pointer move, does not select on pointer up', () => { it('Moves on pointer move, does not select on pointer up', () => {
@ -119,9 +119,9 @@ describe('When pointing a shape behind the current selection', () => {
editor.pointerMove(150, 150) editor.pointerMove(150, 150)
editor.pointerMove(151, 151) editor.pointerMove(151, 151)
editor.pointerMove(100, 100) editor.pointerMove(100, 100)
expect(editor.selectedShapeIds).toMatchObject([ids.A, ids.C]) expect(editor.getSelectedShapeIds()).toMatchObject([ids.A, ids.C])
editor.pointerUp(100, 100, ids.B) editor.pointerUp(100, 100, ids.B)
expect(editor.selectedShapeIds).toMatchObject([ids.A, ids.C]) // no change! we've moved expect(editor.getSelectedShapeIds()).toMatchObject([ids.A, ids.C]) // no change! we've moved
}) })
}) })
@ -129,7 +129,7 @@ describe('When brushing arrows', () => {
it('Brushes a straight arrow', () => { it('Brushes a straight arrow', () => {
const ids = editor const ids = editor
.selectAll() .selectAll()
.deleteShapes(editor.selectedShapeIds) .deleteShapes(editor.getSelectedShapeIds())
.setCamera({ x: 0, y: 0, z: 1 }) .setCamera({ x: 0, y: 0, z: 1 })
.createShapesFromJsx([ .createShapesFromJsx([
<TL.arrow <TL.arrow
@ -145,13 +145,13 @@ describe('When brushing arrows', () => {
editor.pointerDown(0, 45) editor.pointerDown(0, 45)
editor.pointerMove(100, 55) editor.pointerMove(100, 55)
editor.expectPathToBe('root.select.brushing') editor.expectPathToBe('root.select.brushing')
expect(editor.selectedShapeIds).toStrictEqual([ids.arrow1]) expect(editor.getSelectedShapeIds()).toStrictEqual([ids.arrow1])
}) })
it('Brushes within the curve of a curved arrow without selecting the arrow', () => { it('Brushes within the curve of a curved arrow without selecting the arrow', () => {
editor editor
.selectAll() .selectAll()
.deleteShapes(editor.selectedShapeIds) .deleteShapes(editor.getSelectedShapeIds())
.setCamera({ x: 0, y: 0, z: 1 }) .setCamera({ x: 0, y: 0, z: 1 })
.createShapesFromJsx([ .createShapesFromJsx([
<TL.arrow <TL.arrow
@ -167,6 +167,6 @@ describe('When brushing arrows', () => {
editor.pointerDown(55, 45) editor.pointerDown(55, 45)
editor.pointerMove(45, 55) editor.pointerMove(45, 55)
editor.expectPathToBe('root.select.brushing') editor.expectPathToBe('root.select.brushing')
expect(editor.selectedShapeIds).toStrictEqual([]) expect(editor.getSelectedShapeIds()).toStrictEqual([])
}) })
}) })

File diff suppressed because it is too large Load diff

View file

@ -66,7 +66,7 @@ describe('When interacting with a shape...', () => {
util.onRotateEnd = fnEnd util.onRotateEnd = fnEnd
editor.selectAll() editor.selectAll()
expect(editor.selectedShapeIds).toMatchObject([ids.frame1, ids.box1]) expect(editor.getSelectedShapeIds()).toMatchObject([ids.frame1, ids.box1])
editor editor
.pointerDown(300, 300, { .pointerDown(300, 300, {
@ -115,7 +115,7 @@ describe('When interacting with a shape...', () => {
util.onResizeEnd = fnEnd util.onResizeEnd = fnEnd
editor.selectAll() editor.selectAll()
expect(editor.selectedShapeIds).toMatchObject([ids.frame1, ids.box1]) expect(editor.getSelectedShapeIds()).toMatchObject([ids.frame1, ids.box1])
editor.pointerDown(300, 300, { editor.pointerDown(300, 300, {
target: 'selection', target: 'selection',
@ -152,7 +152,7 @@ describe('When interacting with a shape...', () => {
util.onTranslateEnd = fnEnd util.onTranslateEnd = fnEnd
editor.selectAll() editor.selectAll()
expect(editor.selectedShapeIds).toMatchObject([ids.frame1, ids.box1]) expect(editor.getSelectedShapeIds()).toMatchObject([ids.frame1, ids.box1])
// Translate the shapes... // Translate the shapes...
editor.pointerDown(50, 50, ids.box1).pointerMove(50, 40).pointerUp(50, 40) editor.pointerDown(50, 50, ids.box1).pointerMove(50, 40).pointerUp(50, 40)
@ -178,7 +178,7 @@ describe('When interacting with a shape...', () => {
// If a shape has an onClick handler, and if the handler returns nothing, // If a shape has an onClick handler, and if the handler returns nothing,
// then normal selection rules should apply. // then normal selection rules should apply.
expect(editor.selectedShapeIds.length).toBe(1) expect(editor.getSelectedShapeIds().length).toBe(1)
}) })
it('Uses the shape utils onClick handler', () => { it('Uses the shape utils onClick handler', () => {
@ -199,6 +199,6 @@ describe('When interacting with a shape...', () => {
// If a shape has an onClick handler, and it returns something, then // If a shape has an onClick handler, and it returns something, then
// it should not be selected. // it should not be selected.
expect(editor.selectedShapeIds.length).toBe(0) expect(editor.getSelectedShapeIds().length).toBe(0)
}) })
}) })

View file

@ -209,8 +209,8 @@ describe('When cloning...', () => {
// Start cloning! // Start cloning!
editor.keyDown('Alt') editor.keyDown('Alt')
expect(editor.currentPageShapeIds.size).toBe(5) // Two new shapes! expect(editor.currentPageShapeIds.size).toBe(5) // Two new shapes!
const newShapeA = editor.getShape(editor.selectedShapeIds[0])! const newShapeA = editor.getShape(editor.getSelectedShapeIds()[0])!
const newShapeB = editor.getShape(editor.selectedShapeIds[1])! const newShapeB = editor.getShape(editor.getSelectedShapeIds()[1])!
expect(newShapeA).toBeDefined() expect(newShapeA).toBeDefined()
expect(newShapeB).toBeDefined() expect(newShapeB).toBeDefined()
@ -246,7 +246,7 @@ describe('When cloning...', () => {
editor.keyDown('Alt', { altKey: true }) editor.keyDown('Alt', { altKey: true })
expect(editor.currentPageShapeIds.size).toBe(5) // Creates a clone of B and C (its descendant) expect(editor.currentPageShapeIds.size).toBe(5) // Creates a clone of B and C (its descendant)
const newShapeA = editor.getShape(editor.selectedShapeIds[0])! const newShapeA = editor.getShape(editor.getSelectedShapeIds()[0])!
const newShapeB = editor.getShape(editor.getSortedChildIdsForParent(newShapeA.id)[0])! const newShapeB = editor.getShape(editor.getSortedChildIdsForParent(newShapeA.id)[0])!
expect(newShapeA).toBeDefined() expect(newShapeA).toBeDefined()
@ -1571,7 +1571,7 @@ describe('translating a shape with a bound shape', () => {
// | └───┘ | // | └───┘ |
// └───────────────────┘ // └───────────────────┘
expect(editor.getShape(editor.selectedShapeIds[0])?.type).toBe('arrow') expect(editor.getShape(editor.getSelectedShapeIds()[0])?.type).toBe('arrow')
editor.pointerDown(50, 50, ids.box1).pointerMove(84, 110, { ctrlKey: true }) editor.pointerDown(50, 50, ids.box1).pointerMove(84, 110, { ctrlKey: true })