diff --git a/apps/examples/e2e/shared-e2e.ts b/apps/examples/e2e/shared-e2e.ts index 426e3475b..a14f06753 100644 --- a/apps/examples/e2e/shared-e2e.ts +++ b/apps/examples/e2e/shared-e2e.ts @@ -10,7 +10,7 @@ export function sleep(ms: number) { // } // export async function expectToHaveNShapes(page: Page, numberOfShapes: number) { -// expect(await page.evaluate(() => editor.shapesOnCurrentPage.length)).toBe(numberOfShapes) +// expect(await page.evaluate(() => editor.currentPageShapes.length)).toBe(numberOfShapes) // } // export async function expectToHaveNSelectedShapes(page: Page, numberOfSelectedShapes: number) { diff --git a/apps/examples/e2e/tests/test-clipboard.spec.ts b/apps/examples/e2e/tests/test-clipboard.spec.ts index 8fb1b006b..55b9f2cc3 100644 --- a/apps/examples/e2e/tests/test-clipboard.spec.ts +++ b/apps/examples/e2e/tests/test-clipboard.spec.ts @@ -23,7 +23,7 @@ test.describe.skip('clipboard tests', () => { await page.mouse.down() await page.mouse.up() - expect(await page.evaluate(() => editor.shapesOnCurrentPage.length)).toBe(1) + expect(await page.evaluate(() => editor.currentPageShapes.length)).toBe(1) expect(await page.evaluate(() => editor.selectedShapes.length)).toBe(1) await page.keyboard.down('Control') @@ -32,7 +32,7 @@ test.describe.skip('clipboard tests', () => { await page.keyboard.press('KeyV') await page.keyboard.up('Control') - expect(await page.evaluate(() => editor.shapesOnCurrentPage.length)).toBe(2) + expect(await page.evaluate(() => editor.currentPageShapes.length)).toBe(2) expect(await page.evaluate(() => editor.selectedShapes.length)).toBe(1) }) @@ -42,7 +42,7 @@ test.describe.skip('clipboard tests', () => { await page.mouse.down() await page.mouse.up() - expect(await page.evaluate(() => editor.shapesOnCurrentPage.length)).toBe(1) + expect(await page.evaluate(() => editor.currentPageShapes.length)).toBe(1) expect(await page.evaluate(() => editor.selectedShapes.length)).toBe(1) await page.getByTestId('main.menu').click() @@ -53,7 +53,7 @@ test.describe.skip('clipboard tests', () => { await page.getByTestId('menu-item.edit').click() await page.getByTestId('menu-item.paste').click() - expect(await page.evaluate(() => editor.shapesOnCurrentPage.length)).toBe(2) + expect(await page.evaluate(() => editor.currentPageShapes.length)).toBe(2) expect(await page.evaluate(() => editor.selectedShapes.length)).toBe(1) }) @@ -63,7 +63,7 @@ test.describe.skip('clipboard tests', () => { await page.mouse.down() await page.mouse.up() - expect(await page.evaluate(() => editor.shapesOnCurrentPage.length)).toBe(1) + expect(await page.evaluate(() => editor.currentPageShapes.length)).toBe(1) expect(await page.evaluate(() => editor.selectedShapes.length)).toBe(1) await page.mouse.click(100, 100, { button: 'right' }) @@ -73,7 +73,7 @@ test.describe.skip('clipboard tests', () => { await page.mouse.click(100, 100, { button: 'right' }) await page.getByTestId('menu-item.paste').click() - expect(await page.evaluate(() => editor.shapesOnCurrentPage.length)).toBe(2) + expect(await page.evaluate(() => editor.currentPageShapes.length)).toBe(2) expect(await page.evaluate(() => editor.selectedShapes.length)).toBe(1) }) }) diff --git a/apps/examples/src/examples/CustomConfigExample/CardShape/CardShapeUtil.tsx b/apps/examples/src/examples/CustomConfigExample/CardShape/CardShapeUtil.tsx index 359bf9f91..60131a7a3 100644 --- a/apps/examples/src/examples/CustomConfigExample/CardShape/CardShapeUtil.tsx +++ b/apps/examples/src/examples/CustomConfigExample/CardShape/CardShapeUtil.tsx @@ -46,7 +46,7 @@ export class CardShapeUtil extends ShapeUtil { // Render method — the React component that will be rendered for the shape component(shape: ICardShape) { - const bounds = this.editor.getGeometry(shape).bounds + const bounds = this.editor.getShapeGeometry(shape).bounds const theme = getDefaultColorTheme({ isDarkMode: this.editor.user.isDarkMode }) // Unfortunately eslint will think this is a class components diff --git a/apps/examples/src/examples/CustomStylesExample/CardShape.tsx b/apps/examples/src/examples/CustomStylesExample/CardShape.tsx index e36b91a10..750fbb597 100644 --- a/apps/examples/src/examples/CustomStylesExample/CardShape.tsx +++ b/apps/examples/src/examples/CustomStylesExample/CardShape.tsx @@ -55,7 +55,7 @@ export class CardShapeUtil extends BaseBoxShapeUtil { } component(shape: CardShape) { - const bounds = this.editor.getGeometry(shape).bounds + const bounds = this.editor.getShapeGeometry(shape).bounds const theme = getDefaultColorTheme({ isDarkMode: this.editor.user.isDarkMode }) return ( diff --git a/packages/editor/api-report.md b/packages/editor/api-report.md index ec6e9e81f..ac89ad10f 100644 --- a/packages/editor/api-report.md +++ b/packages/editor/api-report.md @@ -539,14 +539,14 @@ export class Editor extends EventEmitter { ease?: (t: number) => number; }): this; animateToShape(shapeId: TLShapeId, opts?: TLAnimationOptions): this; - animateToUser(userId: string): void; + animateToUser(userId: string): this; // @internal (undocumented) annotateError(error: unknown, { origin, willCrashApp, tags, extras, }: { origin: string; willCrashApp: boolean; tags?: Record; extras?: Record; - }): void; + }): this; get assets(): (TLBookmarkAsset | TLImageAsset | TLVideoAsset)[]; bail(): this; bailToMark(id: string): this; @@ -570,10 +570,9 @@ export class Editor extends EventEmitter { centerOnPoint(point: VecLike, animation?: TLAnimationOptions): this; // @internal protected _clickManager: ClickManager; - get commonBoundsOfAllShapesOnCurrentPage(): Box2d | undefined; complete(): this; // @internal (undocumented) - crash(error: unknown): void; + crash(error: unknown): this; // @internal get crashingError(): unknown; createAssets(assets: TLAsset[]): this; @@ -591,11 +590,15 @@ export class Editor extends EventEmitter { }; }; createPage(title: string, id?: TLPageId, belowPageIndex?: string): this; - createShape(partial: TLShapePartial, select?: boolean): this; - createShapes(partials: TLShapePartial[], select?: boolean): this; + createShape(partial: OptionalKeys, 'id'>): this; + createShapes(partials: OptionalKeys, 'id'>[]): this; get croppingShapeId(): null | TLShapeId; get currentPage(): TLPage; + get currentPageBounds(): Box2d | undefined; get currentPageId(): TLPageId; + get currentPageShapeIds(): Set; + get currentPageShapes(): TLShape[]; + get currentPageShapesSorted(): TLShape[]; get currentPageState(): TLInstancePageState; get currentTool(): StateNode | undefined; get currentToolId(): string; @@ -648,12 +651,12 @@ export class Editor extends EventEmitter { }) => void) | null; }[K]; }; - findAncestor(shape: TLShape, predicate: (parent: TLShape) => boolean): TLShape | undefined; - // (undocumented) - findAncestor(id: TLShapeId, predicate: (parent: TLShape) => boolean): TLShape | undefined; findCommonAncestor(shapes: TLShape[], predicate?: (shape: TLShape) => boolean): TLShapeId | undefined; // (undocumented) findCommonAncestor(ids: TLShapeId[], predicate?: (shape: TLShape) => boolean): TLShapeId | undefined; + findShapeAncestor(shape: TLShape, predicate: (parent: TLShape) => boolean): TLShape | undefined; + // (undocumented) + findShapeAncestor(id: TLShapeId, predicate: (parent: TLShape) => boolean): TLShape | undefined; flipShapes(shapes: TLShape[], operation: 'horizontal' | 'vertical'): this; // (undocumented) flipShapes(ids: TLShapeId[], operation: 'horizontal' | 'vertical'): this; @@ -663,9 +666,6 @@ export class Editor extends EventEmitter { getAncestorPageId(shape?: TLShape): TLPageId | undefined; // (undocumented) getAncestorPageId(shapeId?: TLShapeId): TLPageId | undefined; - getAncestors(shape: TLShape, acc?: TLShape[]): TLShape[]; - // (undocumented) - getAncestors(id: TLShapeId, acc?: TLShape[]): TLShape[]; // (undocumented) getArrowInfo(shape: TLArrowShape): ArrowInfo | undefined; getArrowsBoundTo(shapeId: TLShapeId): { @@ -676,51 +676,24 @@ export class Editor extends EventEmitter { // (undocumented) getAsset(id: TLAssetId): TLAsset | undefined; getAssetForExternalContent(info: TLExternalAssetContent_2): Promise; - getClipPath(shape: TLShape): string | undefined; - // (undocumented) - getClipPath(id: TLShapeId): string | undefined; getContainer: () => HTMLElement; getContent(ids: TLShapeId[]): TLContent | undefined; // (undocumented) getContent(shapes: TLShape[]): TLContent | undefined; + getCurrentPageShapeIds(pageId: TLPageId): Set; + // (undocumented) + getCurrentPageShapeIds(page: TLPage): Set; getDroppingOverShape(point: VecLike, droppingShapes?: TLShape[]): TLShape | undefined; - getGeometry(id: TLShapeId): T; - // (undocumented) - getGeometry(shape: TLShape): T; - getHandles(id: T['id']): TLHandle[] | undefined; - // (undocumented) - getHandles(shape: T): TLHandle[] | undefined; getHighestIndexForParent(parent: TLPage | TLShape): string; // (undocumented) getHighestIndexForParent(parentId: TLParentId): string; getInitialMetaForShape(_shape: TLShape): JsonObject; - getMaskedPageBounds(id: TLShapeId): Box2d | undefined; - // (undocumented) - getMaskedPageBounds(shape: TLShape): Box2d | undefined; getOutermostSelectableShape(shape: TLShape, filter?: (shape: TLShape) => boolean): TLShape; // (undocumented) getOutermostSelectableShape(id: TLShapeId, filter?: (shape: TLShape) => boolean): TLShape; - getOutlineSegments(shape: T): Vec2d[][]; - // (undocumented) - getOutlineSegments(id: T['id']): Vec2d[][]; getPage(page: TLPage): TLPage | undefined; // (undocumented) getPage(id: TLPageId): TLPage | undefined; - getPageBounds(shape: TLShape): Box2d | undefined; - // (undocumented) - getPageBounds(id: TLShapeId): Box2d | undefined; - getPageMask(id: TLShapeId): undefined | VecLike[]; - // (undocumented) - getPageMask(shape: TLShape): undefined | VecLike[]; - getPageTransform(id: TLShapeId): Matrix2d; - // (undocumented) - getPageTransform(shape: TLShape): Matrix2d; - getParentShape(shape?: TLShape): TLShape | undefined; - // (undocumented) - getParentShape(shapeId?: TLShapeId): TLShape | undefined; - getParentTransform(shape: TLShape): Matrix2d; - // (undocumented) - getParentTransform(id: TLShapeId): Matrix2d; getPointInParentSpace(shape: TLShape, point: VecLike): Vec2d; // (undocumented) getPointInParentSpace(id: TLShapeId, point: VecLike): Vec2d; @@ -731,6 +704,9 @@ export class Editor extends EventEmitter { getShape(id: TLParentId): T | undefined; // (undocumented) getShape(shape: TLShape): T | undefined; + getShapeAncestors(shape: TLShape, acc?: TLShape[]): TLShape[]; + // (undocumented) + getShapeAncestors(id: TLShapeId, acc?: TLShape[]): TLShape[]; getShapeAndDescendantIds(ids: TLShapeId[]): Set; getShapeAtPoint(point: VecLike, opts?: { hitInside?: boolean | undefined; @@ -739,9 +715,39 @@ export class Editor extends EventEmitter { hitFrameInside?: boolean | undefined; filter?: ((shape: TLShape) => boolean) | undefined; }): TLShape | undefined; - getShapeIdsInPage(page: TLPage): Set; + getShapeClipPath(shape: TLShape): string | undefined; // (undocumented) - getShapeIdsInPage(pageId: TLPageId): Set; + getShapeClipPath(id: TLShapeId): string | undefined; + getShapeGeometry(id: TLShapeId): T; + // (undocumented) + getShapeGeometry(shape: TLShape): T; + getShapeHandles(id: T['id']): TLHandle[] | undefined; + // (undocumented) + getShapeHandles(shape: T): TLHandle[] | undefined; + getShapeLocalTransform(shape: TLShape): Matrix2d; + // (undocumented) + getShapeLocalTransform(id: TLShapeId): Matrix2d; + getShapeMask(id: TLShapeId): undefined | VecLike[]; + // (undocumented) + getShapeMask(shape: TLShape): undefined | VecLike[]; + getShapeMaskedPageBounds(id: TLShapeId): Box2d | undefined; + // (undocumented) + getShapeMaskedPageBounds(shape: TLShape): Box2d | undefined; + getShapeOutlineSegments(shape: T): Vec2d[][]; + // (undocumented) + getShapeOutlineSegments(id: T['id']): Vec2d[][]; + getShapePageBounds(shape: TLShape): Box2d | undefined; + // (undocumented) + getShapePageBounds(id: TLShapeId): Box2d | undefined; + getShapePageTransform(id: TLShapeId): Matrix2d; + // (undocumented) + getShapePageTransform(shape: TLShape): Matrix2d; + getShapeParent(shape?: TLShape): TLShape | undefined; + // (undocumented) + getShapeParent(shapeId?: TLShapeId): TLShape | undefined; + getShapeParentTransform(shape: TLShape): Matrix2d; + // (undocumented) + getShapeParentTransform(id: TLShapeId): Matrix2d; getShapesAtPoint(point: VecLike, opts?: { margin?: number | undefined; hitInside?: boolean | undefined; @@ -766,9 +772,6 @@ export class Editor extends EventEmitter { darkMode?: boolean | undefined; preserveAspectRatio: React.SVGAttributes['preserveAspectRatio']; }>): Promise; - getTransform(shape: TLShape): Matrix2d; - // (undocumented) - getTransform(id: TLShapeId): Matrix2d; groupShapes(ids: TLShapeId[], groupId?: TLShapeId): this; // (undocumented) groupShapes(shapes: TLShape[], groupId?: TLShapeId): this; @@ -777,7 +780,6 @@ export class Editor extends EventEmitter { hasAncestor(shapeId: TLShapeId | undefined, ancestorId: TLShapeId): boolean; get hintingShapeIds(): TLShapeId[]; readonly history: HistoryManager; - // (undocumented) get hoveredShape(): TLUnknownShape | undefined; get hoveredShapeId(): null | TLShapeId; inputs: { @@ -906,8 +908,8 @@ export class Editor extends EventEmitter { selectAll(): this; get selectedShapeIds(): TLShapeId[]; get selectedShapes(): TLShape[]; - get selectionBounds(): Box2d | undefined; get selectionPageBounds(): Box2d | null; + get selectionRotatedPageBounds(): Box2d | undefined; get selectionRotation(): number; selectNone(): this; sendBackward(shapes: TLShape[]): this; @@ -917,27 +919,19 @@ export class Editor extends EventEmitter { // (undocumented) sendToBack(ids: TLShapeId[]): this; setCamera(point: VecLike, animation?: TLAnimationOptions): this; - // (undocumented) setCroppingId(id: null | TLShapeId): this; setCurrentPage(page: TLPage, opts?: TLViewportOptions): this; // (undocumented) setCurrentPage(pageId: TLPageId, opts?: TLViewportOptions): this; setCurrentTool(id: string, info?: {}): this; - // (undocumented) setEditingId(id: null | TLShapeId): this; - // (undocumented) setErasingIds(ids: TLShapeId[]): this; - // (undocumented) - setFocusedGroupId(next: TLPageId | TLShapeId): this; - // (undocumented) + setFocusedGroupId(next: null | TLShapeId): this; setHintingIds(ids: TLShapeId[]): this; - // (undocumented) setHoveredId(id: null | TLShapeId): this; setOpacity(opacity: number, ephemeral?: boolean, squashing?: boolean): this; setSelectedShapeIds(ids: TLShapeId[], squashing?: boolean): this; setStyle(style: StyleProp, value: T, ephemeral?: boolean, squashing?: boolean): this; - get shapeIdsOnCurrentPage(): Set; - get shapesOnCurrentPage(): TLShape[]; shapeUtils: { readonly [K in string]?: ShapeUtil; }; @@ -949,13 +943,12 @@ export class Editor extends EventEmitter { direction: VecLike; friction: number; speedThreshold?: number | undefined; - }): this | undefined; + }): this; readonly snaps: SnapManager; - get sortedShapesOnCurrentPage(): TLShape[]; stackShapes(shapes: TLShape[], operation: 'horizontal' | 'vertical', gap: number): this; // (undocumented) stackShapes(ids: TLShapeId[], operation: 'horizontal' | 'vertical', gap: number): this; - startFollowingUser(userId: string): this | undefined; + startFollowingUser(userId: string): this; stopCameraAnimation(): this; stopFollowingUser(): this; readonly store: TLStore; diff --git a/packages/editor/src/lib/components/Canvas.tsx b/packages/editor/src/lib/components/Canvas.tsx index 700a8675c..4445ffb11 100644 --- a/packages/editor/src/lib/components/Canvas.tsx +++ b/packages/editor/src/lib/components/Canvas.tsx @@ -192,13 +192,13 @@ function HandlesWrapper() { const isReadonly = useValue('isChangingStyle', () => editor.instanceState.isReadonly, [editor]) const handles = useValue( 'handles', - () => (editor.onlySelectedShape ? editor.getHandles(editor.onlySelectedShape) : undefined), + () => (editor.onlySelectedShape ? editor.getShapeHandles(editor.onlySelectedShape) : undefined), [editor] ) const transform = useValue( 'transform', () => - editor.onlySelectedShape ? editor.getPageTransform(editor.onlySelectedShape) : undefined, + editor.onlySelectedShape ? editor.getShapePageTransform(editor.onlySelectedShape) : undefined, [editor] ) @@ -396,7 +396,7 @@ const DebugSvgCopy = track(function DupSvg({ id }: { id: TLShapeId }) { const unsubscribe = react('shape to svg', async () => { const renderId = Math.random() latest = renderId - const bb = editor.getPageBounds(id) + const bb = editor.getShapePageBounds(id) const el = await editor.getSvg([id], { padding: 0 }) if (el && bb && latest === renderId) { el.style.setProperty('overflow', 'visible') @@ -443,7 +443,9 @@ const UiLogger = track(() => { export function SelectionForegroundWrapper() { const editor = useEditor() const selectionRotation = useValue('selection rotation', () => editor.selectionRotation, [editor]) - const selectionBounds = useValue('selection bounds', () => editor.selectionBounds, [editor]) + const selectionBounds = useValue('selection bounds', () => editor.selectionRotatedPageBounds, [ + editor, + ]) const { SelectionForeground } = useEditorComponents() if (!selectionBounds || !SelectionForeground) return null return @@ -452,7 +454,9 @@ export function SelectionForegroundWrapper() { export function SelectionBackgroundWrapper() { const editor = useEditor() const selectionRotation = useValue('selection rotation', () => editor.selectionRotation, [editor]) - const selectionBounds = useValue('selection bounds', () => editor.selectionBounds, [editor]) + const selectionBounds = useValue('selection bounds', () => editor.selectionRotatedPageBounds, [ + editor, + ]) const { SelectionBackground } = useEditorComponents() if (!selectionBounds || !SelectionBackground) return null return diff --git a/packages/editor/src/lib/components/GeometryDebuggingView.tsx b/packages/editor/src/lib/components/GeometryDebuggingView.tsx index 0e597629d..ce40c0b5d 100644 --- a/packages/editor/src/lib/components/GeometryDebuggingView.tsx +++ b/packages/editor/src/lib/components/GeometryDebuggingView.tsx @@ -33,8 +33,8 @@ export const GeometryDebuggingView = track(function GeometryDebuggingView({ > {renderingShapes.map((result) => { const shape = editor.getShape(result.id)! - const geometry = editor.getGeometry(shape) - const pageTransform = editor.getPageTransform(shape)! + const geometry = editor.getShapeGeometry(shape) + const pageTransform = editor.getShapePageTransform(shape)! const pointInShapeSpace = editor.getPointInShapeSpace(shape, currentPagePoint) const nearestPointOnShape = geometry.nearestPoint(pointInShapeSpace) diff --git a/packages/editor/src/lib/components/Shape.tsx b/packages/editor/src/lib/components/Shape.tsx index c29121b79..820632ed7 100644 --- a/packages/editor/src/lib/components/Shape.tsx +++ b/packages/editor/src/lib/components/Shape.tsx @@ -53,7 +53,7 @@ export const Shape = track(function Shape({ const shape = editor.getShape(id) if (!shape) return // probably the shape was just deleted - const pageTransform = editor.getPageTransform(id) + const pageTransform = editor.getShapePageTransform(id) const transform = Matrix2d.toCssString(pageTransform) setProperty('transform', transform) }, @@ -66,7 +66,7 @@ export const Shape = track(function Shape({ const shape = editor.getShape(id) if (!shape) return null - const clipPath = editor.getClipPath(id) + const clipPath = editor.getShapeClipPath(id) setProperty('clip-path', clipPath ?? 'none') }, [editor, setProperty] @@ -78,7 +78,7 @@ export const Shape = track(function Shape({ const shape = editor.getShape(id) if (!shape) return null - const bounds = editor.getGeometry(shape).bounds + const bounds = editor.getShapeGeometry(shape).bounds setProperty('width', Math.max(1, Math.ceil(bounds.width)) + 'px') setProperty('height', Math.max(1, Math.ceil(bounds.height)) + 'px') }, @@ -155,7 +155,7 @@ const InnerShapeBackground = React.memo( const CulledShape = React.memo( function CulledShape({ shape }: { shape: T }) { const editor = useEditor() - const bounds = editor.getGeometry(shape).bounds + const bounds = editor.getShapeGeometry(shape).bounds return (
{ - const pageTransform = editor.getPageTransform(id) + const pageTransform = editor.getShapePageTransform(id) if (!pageTransform) return '' return pageTransform.toCssString() }, diff --git a/packages/editor/src/lib/config/createTLStore.ts b/packages/editor/src/lib/config/createTLStore.ts index b15bb9a52..2a4022f0c 100644 --- a/packages/editor/src/lib/config/createTLStore.ts +++ b/packages/editor/src/lib/config/createTLStore.ts @@ -33,7 +33,7 @@ export function createTLStore({ initialData, defaultName = '', ...rest }: TLStor 'schema' in rest ? rest.schema : createTLSchema({ - shapes: shapesOnCurrentPageToShapeMap(checkShapesAndAddCore(rest.shapeUtils)), + shapes: currentPageShapesToShapeMap(checkShapesAndAddCore(rest.shapeUtils)), }) return new Store({ schema, @@ -44,7 +44,7 @@ export function createTLStore({ initialData, defaultName = '', ...rest }: TLStor }) } -function shapesOnCurrentPageToShapeMap(shapeUtils: TLShapeUtilConstructor[]) { +function currentPageShapesToShapeMap(shapeUtils: TLShapeUtilConstructor[]) { return Object.fromEntries( shapeUtils.map((s): [string, SchemaShapeInfo] => [ s.type, diff --git a/packages/editor/src/lib/editor/Editor.ts b/packages/editor/src/lib/editor/Editor.ts index 2ba6c7ebc..4d83276d8 100644 --- a/packages/editor/src/lib/editor/Editor.ts +++ b/packages/editor/src/lib/editor/Editor.ts @@ -121,7 +121,7 @@ import { SvgExportContext, SvgExportDef } from './types/SvgExportContext' import { TLContent } from './types/clipboard-types' import { TLEventMap } from './types/emit-types' import { TLEventInfo, TLPinchEventInfo, TLPointerEventInfo } from './types/event-types' -import { RequiredKeys } from './types/misc-types' +import { OptionalKeys, RequiredKeys } from './types/misc-types' import { TLResizeHandle } from './types/selection-types' /** @public */ @@ -566,7 +566,7 @@ export class Editor extends EventEmitter { } }) - this._shapeIdsOnCurrentPage = deriveShapeIdsInCurrentPage(this.store, () => this.currentPageId) + this._currentPageShapeIds = deriveShapeIdsInCurrentPage(this.store, () => this.currentPageId) this._parentIdsToChildIds = parentsToChildren(this.store) this.disposables.add( @@ -860,7 +860,7 @@ export class Editor extends EventEmitter { * * @public */ - bailToMark(id: string) { + bailToMark(id: string): this { this.history.bailToMark(id) return this } @@ -882,7 +882,7 @@ export class Editor extends EventEmitter { * * @public */ - batch(fn: () => void) { + batch(fn: () => void): this { this.history.batch(fn) return this } @@ -941,7 +941,7 @@ export class Editor extends EventEmitter { tags?: Record extras?: Record } - ) { + ): this { const defaultAnnotations = this.createErrorAnnotations(origin, willCrashApp) annotateError(error, { tags: { ...defaultAnnotations.tags, ...tags }, @@ -950,6 +950,7 @@ export class Editor extends EventEmitter { if (willCrashApp) { this.store.markAsPossiblyCorrupted() } + return this } /** @internal */ @@ -1006,10 +1007,11 @@ export class Editor extends EventEmitter { } /** @internal */ - crash(error: unknown) { + crash(error: unknown): this { this._crashingError = error this.store.markAsPossiblyCorrupted() this.emit('crash', { error }) + return this } /* ------------------- Statechart ------------------- */ @@ -1167,7 +1169,7 @@ export class Editor extends EventEmitter { partial: Partial>, ephemeral = true, squashing = true - ) { + ): this { this._updateInstanceState(partial, ephemeral, squashing) if (partial.isChangingStyle !== undefined) { @@ -1382,7 +1384,7 @@ export class Editor extends EventEmitter { * * @public */ - setSelectedShapeIds(ids: TLShapeId[], squashing = false) { + setSelectedShapeIds(ids: TLShapeId[], squashing = false): this { this._setSelectedShapeIds(ids, squashing) return this } @@ -1432,11 +1434,11 @@ export class Editor extends EventEmitter { */ isAncestorSelected(id: TLShapeId): boolean isAncestorSelected(shape: TLShape): boolean - isAncestorSelected(arg: TLShape | TLShapeId) { + isAncestorSelected(arg: TLShape | TLShapeId): boolean { const shape = this.getShape(typeof arg === 'string' ? arg : arg.id) if (!shape) return false const { selectedShapeIds } = this - return !!this.findAncestor(shape, (parent) => selectedShapeIds.includes(parent.id)) + return !!this.findShapeAncestor(shape, (parent) => selectedShapeIds.includes(parent.id)) } /** @@ -1497,7 +1499,7 @@ export class Editor extends EventEmitter { * * @public */ - selectAll() { + selectAll(): this { const ids = this.getSortedChildIdsForParent(this.currentPageId) // page might have no shapes if (ids.length <= 0) return this @@ -1560,8 +1562,9 @@ export class Editor extends EventEmitter { } /** - * The current page bounds of all the selected shapes (Not the same thing as the page bounds of - * the selection bounding box when the selection has been rotated) + * The current page bounds of all the selected shapes. If the + * selection is rotated, then these bounds are the axis-aligned + * box that the rotated bounds would fit inside of. * * @readonly * @@ -1574,11 +1577,11 @@ export class Editor extends EventEmitter { if (selectedShapeIds.length === 0) return null - return Box2d.Common(compact(selectedShapeIds.map((id) => this.getPageBounds(id)))) + return Box2d.Common(compact(selectedShapeIds.map((id) => this.getShapePageBounds(id)))) } /** - * The rotation of the selection bounding box. + * The rotation of the selection bounding box in page space. * * @readonly * @public @@ -1589,24 +1592,24 @@ export class Editor extends EventEmitter { return 0 } if (selectedShapeIds.length === 1) { - return this.getPageTransform(this.selectedShapeIds[0])!.rotation() + return this.getShapePageTransform(this.selectedShapeIds[0])!.rotation() } - const allRotations = selectedShapeIds.map((id) => this.getPageTransform(id)!.rotation()) + const allRotations = selectedShapeIds.map((id) => this.getShapePageTransform(id)!.rotation()) // if the rotations are all compatible with each other, return the rotation of any one of them if (allRotations.every((rotation) => Math.abs(rotation - allRotations[0]) < Math.PI / 180)) { - return this.getPageTransform(selectedShapeIds[0])!.rotation() + return this.getShapePageTransform(selectedShapeIds[0])!.rotation() } return 0 } /** - * The bounds of the selection bounding box. + * The bounds of the selection bounding box in page space. * * @readonly * @public */ - @computed get selectionBounds(): Box2d | undefined { + @computed get selectionRotatedPageBounds(): Box2d | undefined { const { selectedShapeIds } = this if (selectedShapeIds.length === 0) { @@ -1619,26 +1622,25 @@ export class Editor extends EventEmitter { } if (selectedShapeIds.length === 1) { - const bounds = this.getGeometry(selectedShapeIds[0]).bounds.clone() - bounds.point = Matrix2d.applyToPoint( - this.getPageTransform(selectedShapeIds[0])!, - bounds.point - ) + const bounds = this.getShapeGeometry(selectedShapeIds[0]).bounds.clone() + const pageTransform = this.getShapePageTransform(selectedShapeIds[0])! + bounds.point = pageTransform.applyToPoint(bounds.point) return bounds } // need to 'un-rotate' all the outlines of the existing nodes so we can fit them inside a box - const allPoints = this.selectedShapeIds - .flatMap((id) => { - const pageTransform = this.getPageTransform(id) - if (!pageTransform) return [] - return pageTransform.applyToPoints(this.getGeometry(id).vertices) - }) - .map((p) => Vec2d.Rot(p, -selectionRotation)) - const box = Box2d.FromPoints(allPoints) + const boxFromRotatedVertices = Box2d.FromPoints( + this.selectedShapeIds + .flatMap((id) => { + const pageTransform = this.getShapePageTransform(id) + if (!pageTransform) return [] + return pageTransform.applyToPoints(this.getShapeGeometry(id).vertices) + }) + .map((p) => Vec2d.Rot(p, -selectionRotation)) + ) // now position box so that it's top-left corner is in the right place - box.point = box.point.rot(selectionRotation) - return box + boxFromRotatedVertices.point = boxFromRotatedVertices.point.rot(selectionRotation) + return boxFromRotatedVertices } // Focus Layer Id @@ -1652,15 +1654,22 @@ export class Editor extends EventEmitter { return this.currentPageState.focusedGroupId ?? this.currentPageId } - setFocusedGroupId(next: TLShapeId | TLPageId): this { + /** + * Set the current focus layer id. + * + * @param next - The shape id (or page id) to set as the focus layer id. + * + * @public + */ + setFocusedGroupId(next: TLShapeId | null): this { this._setFocusedGroupId(next) return this } + /** @internal */ private _setFocusedGroupId = this.history.createCommand( 'setFocusedGroupId', - (next: undefined | TLShapeId | TLPageId) => { - next = isPageId(next as string) ? undefined : (next as TLShapeId | undefined) + (next: TLShapeId | null) => { // When we first click an empty canvas we don't want this to show up in the undo stack if (!next && !this.canUndo) { return @@ -1677,7 +1686,7 @@ export class Editor extends EventEmitter { }, { do: ({ next }) => { - this.store.update(this.currentPageState.id, (s) => ({ ...s, focusedGroupId: next ?? null })) + this.store.update(this.currentPageState.id, (s) => ({ ...s, focusedGroupId: next })) }, undo: ({ prev }) => { this.store.update(this.currentPageState.id, (s) => ({ ...s, focusedGroupId: prev })) @@ -1699,15 +1708,15 @@ export class Editor extends EventEmitter { if (focusedShape) { // If we have a focused layer, look for an ancestor of the focused shape that is a group - const match = this.findAncestor(focusedShape, (shape) => + const match = this.findShapeAncestor(focusedShape, (shape) => this.isShapeOfType(shape, 'group') ) // If we have an ancestor that can become a focused layer, set it as the focused layer - this.setFocusedGroupId(match?.id ?? this.currentPageId) + this.setFocusedGroupId(match?.id ?? null) this.select(focusedShape.id) } else { // If there's no focused shape, then clear the focus layer and clear selection - this.setFocusedGroupId(this.currentPageId) + this.setFocusedGroupId(null) this.selectNone() } @@ -1722,6 +1731,14 @@ export class Editor extends EventEmitter { get editingShapeId() { return this.currentPageState.editingShapeId } + + /** + * Set the current editing shape id. + * + * @param id - The shape id to set as editing. + * + * @public + */ setEditingId(id: TLShapeId | null): this { if (!id) { this._setInstancePageState({ editingShapeId: null }) @@ -1748,11 +1765,25 @@ export class Editor extends EventEmitter { @computed get hoveredShapeId() { return this.currentPageState.hoveredShapeId } + + /** + * Set the editor's current hovered shape id. + * + * @param id - The shape id to set as hovered. + * + * @public + */ setHoveredId(id: TLShapeId | null): this { if (id === this.currentPageState.hoveredShapeId) return this this.updateCurrentPageState({ hoveredShapeId: id }, true) return this } + + /** + * The editor's current hovered shape. + * + * @public + */ @computed get hoveredShape() { return this.hoveredShapeId ? this.getShape(this.hoveredShapeId) : undefined } @@ -1760,13 +1791,21 @@ export class Editor extends EventEmitter { // Hinting ids /** - * The editor's current hinting ids. + * The editor's current hinting shape ids. * * @public */ @computed get hintingShapeIds() { return this.currentPageState.hintingShapeIds } + + /** + * Set the editor's current hinting shape ids. + * + * @param ids - The shape ids to set as hinting. + * + * @public + */ setHintingIds(ids: TLShapeId[]): this { // always ephemeral this.store.update(this.currentPageState.id, (s) => ({ ...s, hintingShapeIds: dedupe(ids) })) @@ -1782,6 +1821,13 @@ export class Editor extends EventEmitter { return this.currentPageState.erasingShapeIds } + /** + * Set the editor's current erasing shape ids. + * + * @param ids - The shape ids to set as erasing. + * + * @public + */ setErasingIds(ids: TLShapeId[]): this { const erasingShapeIds = this.erasingShapeIdsSet if (ids.length === erasingShapeIds.size && ids.every((id) => erasingShapeIds.has(id))) @@ -1807,6 +1853,14 @@ export class Editor extends EventEmitter { get croppingShapeId() { return this.currentPageState.croppingShapeId } + + /** + * Set the current cropping shape id. + * + * @param id - The shape id to set as cropping. + * + * @public + */ setCroppingId(id: TLShapeId | null): this { if (id !== this.croppingShapeId) { if (!id) { @@ -1962,8 +2016,8 @@ export class Editor extends EventEmitter { * * @public */ - zoomToContent() { - const bounds = this.selectionPageBounds ?? this.commonBoundsOfAllShapesOnCurrentPage + zoomToContent(): this { + const bounds = this.selectionPageBounds ?? this.currentPageBounds if (bounds) { this.zoomToBounds(bounds, Math.min(1, this.zoomLevel), { duration: 220 }) @@ -1988,10 +2042,10 @@ export class Editor extends EventEmitter { zoomToFit(animation?: TLAnimationOptions): this { if (!this.instanceState.canMoveCamera) return this - const ids = [...this.shapeIdsOnCurrentPage] + const ids = [...this.currentPageShapeIds] if (ids.length <= 0) return this - const pageBounds = Box2d.Common(compact(ids.map((id) => this.getPageBounds(id)))) + const pageBounds = Box2d.Common(compact(ids.map((id) => this.getShapePageBounds(id)))) this.zoomToBounds(pageBounds, undefined, animation) return this } @@ -2024,21 +2078,6 @@ export class Editor extends EventEmitter { return this } - /** - * Zoom the camera in. - * - * @example - * ```ts - * editor.zoomIn() - * editor.zoomIn(editor.viewportScreenCenter, { duration: 120 }) - * editor.zoomIn(editor.inputs.currentScreenPoint, { duration: 120 }) - * ``` - * - * @param animation - (optional) The options for an animation. - * - * @public - */ - /** * Zoom the camera in. * @@ -2135,12 +2174,10 @@ export class Editor extends EventEmitter { zoomToSelection(animation?: TLAnimationOptions): this { if (!this.instanceState.canMoveCamera) return this - const ids = this.selectedShapeIds - if (ids.length <= 0) return this + const { selectionPageBounds } = this + if (!selectionPageBounds) return this - const selectionBounds = Box2d.Common(compact(ids.map((id) => this.getPageBounds(id)))) - - this.zoomToBounds(selectionBounds, Math.max(1, this.camera.z), animation) + this.zoomToBounds(selectionPageBounds, Math.max(1, this.zoomLevel), animation) return this } @@ -2157,7 +2194,7 @@ export class Editor extends EventEmitter { if (!this.instanceState.canMoveCamera) return this if (ids.length <= 0) return this - const selectionBounds = Box2d.Common(compact(ids.map((id) => this.getPageBounds(id)))) + const selectionBounds = Box2d.Common(compact(ids.map((id) => this.getShapePageBounds(id)))) const { viewportPageBounds } = this @@ -2269,9 +2306,8 @@ export class Editor extends EventEmitter { * * @public */ - stopCameraAnimation() { + stopCameraAnimation(): this { this.emit('stop-camera-animation') - return this } @@ -2367,14 +2403,14 @@ export class Editor extends EventEmitter { friction: number speedThreshold?: number } - ) { + ): this { if (!this.instanceState.canMoveCamera) return this this.stopCameraAnimation() const { animationSpeed } = this.user - if (animationSpeed === 0) return + if (animationSpeed === 0) return this const { speed, friction, direction, speedThreshold = 0.01 } = opts let currentSpeed = Math.min(speed, 1) @@ -2411,7 +2447,7 @@ export class Editor extends EventEmitter { * @param userId - The id of the user to aniamte to. * @public */ - animateToUser(userId: string) { + animateToUser(userId: string): this { const presences = this.store.query.records('instance_presence', () => ({ userId: { eq: userId }, })) @@ -2422,7 +2458,7 @@ export class Editor extends EventEmitter { }) .pop() - if (!presence) return + if (!presence) return this this.batch(() => { // If we're following someone, stop following them @@ -2454,6 +2490,8 @@ export class Editor extends EventEmitter { this.updateInstanceState({ highlightedUserIds }) }, COLLABORATOR_IDLE_TIMEOUT) }) + + return this } /** @@ -2467,7 +2505,7 @@ export class Editor extends EventEmitter { const activeArea = this.viewportScreenBounds.clone().expandBy(-32) const viewportAspectRatio = activeArea.width / activeArea.height - const shapePageBounds = this.getPageBounds(shapeId) + const shapePageBounds = this.getShapePageBounds(shapeId) if (!shapePageBounds) return this @@ -2511,7 +2549,7 @@ export class Editor extends EventEmitter { * * @public */ - updateViewportScreenBounds(center = false) { + updateViewportScreenBounds(center = false): this { const container = this.getContainer() if (!container) return this @@ -2651,7 +2689,7 @@ export class Editor extends EventEmitter { * * @public */ - startFollowingUser(userId: string) { + startFollowingUser(userId: string): this { const leaderPresences = this.store.query.records('instance_presence', () => ({ userId: { eq: userId }, })) @@ -2664,7 +2702,7 @@ export class Editor extends EventEmitter { // If the leader is following us, then we can't follow them if (leaderPresences.value.some((p) => p.followingUserId === thisUserId)) { - return + return this } transact(() => { @@ -2774,7 +2812,7 @@ export class Editor extends EventEmitter { * * @public */ - stopFollowingUser() { + stopFollowingUser(): this { this.updateInstanceState({ followingUserId: null }, true) this.emit('stop-following') return this @@ -2890,7 +2928,7 @@ export class Editor extends EventEmitter { } // If a child is outside of its parent's clipping bounds, then bounds will be undefined. - const maskedPageBounds = this.getMaskedPageBounds(id) + const maskedPageBounds = this.getShapeMaskedPageBounds(id) // Whether the shape is on screen. Use the "strict" viewport here. const isInViewport = maskedPageBounds @@ -3059,6 +3097,7 @@ export class Editor extends EventEmitter { * @example * ```ts * editor.getPage(myPage.id) + * editor.getPage(myPage) * ``` * * @public @@ -3069,20 +3108,16 @@ export class Editor extends EventEmitter { return this.store.get(typeof id === 'string' ? id : id.id) } - /** - * A cache of shape ids in the current page. - * - * @internal - */ - private readonly _shapeIdsOnCurrentPage: ReturnType + /* @internal */ + private readonly _currentPageShapeIds: ReturnType /** * An array of all of the shapes on the current page. * * @public */ - get shapeIdsOnCurrentPage() { - return this._shapeIdsOnCurrentPage.value + get currentPageShapeIds() { + return this._currentPageShapeIds.value } /** @@ -3090,17 +3125,17 @@ export class Editor extends EventEmitter { * * @example * ```ts - * const idsOnPage1 = editor.getShapeIdsInPage('page1') - * const idsOnPage2 = editor.getShapeIdsInPage('page2') + * const idsOnPage1 = editor.getCurrentPageShapeIds('page1') + * const idsOnPage2 = editor.getCurrentPageShapeIds(myPage2) * ``` * - * @param pageId - The id of the page. + * @param page - The page (or page id) to get. * * @public **/ - getShapeIdsInPage(page: TLPage): Set - getShapeIdsInPage(pageId: TLPageId): Set - getShapeIdsInPage(arg: TLPageId | TLPage): Set { + getCurrentPageShapeIds(pageId: TLPageId): Set + getCurrentPageShapeIds(page: TLPage): Set + getCurrentPageShapeIds(arg: TLPageId | TLPage): Set { const pageId = typeof arg === 'string' ? arg : arg.id const result = this.store.query.exec('shape', { parentId: { eq: pageId } }) return this.getShapeAndDescendantIds(result.map((s) => s.id)) @@ -3112,9 +3147,10 @@ export class Editor extends EventEmitter { * @example * ```ts * editor.setCurrentPage('page1') + * editor.setCurrentPage(myPage1) * ``` * - * @param pageId - The id of the page to set as the current page. + * @param page - The page (or page id) to set as the current page. * @param options - Options for setting the current page. * * @public @@ -3189,6 +3225,7 @@ export class Editor extends EventEmitter { * @example * ```ts * editor.updatePage({ id: 'page2', name: 'Page 2' }) + * editor.updatePage({ id: 'page2', name: 'Page 2' }, true) * ``` * * @param partial - The partial of the shape to update. @@ -3234,10 +3271,12 @@ export class Editor extends EventEmitter { * ```ts * editor.createPage('New Page') * editor.createPage('New Page', 'page1') + * editor.createPage('New Page', 'page1', 'a2') * ``` * * @param id - The new page's id. * @param title - The new page's title. + * @param belowPageIndex - (optional) The index to create below. * * @public */ @@ -3461,7 +3500,7 @@ export class Editor extends EventEmitter { * * @public */ - createAssets(assets: TLAsset[]) { + createAssets(assets: TLAsset[]): this { this._createAssets(assets) return this } @@ -3497,7 +3536,7 @@ export class Editor extends EventEmitter { * * @public */ - updateAssets(assets: TLAssetPartial[]) { + updateAssets(assets: TLAssetPartial[]): this { this._updateAssets(assets) return this } @@ -3546,7 +3585,7 @@ export class Editor extends EventEmitter { */ deleteAssets(assets: TLAsset[]): this deleteAssets(ids: TLAssetId[]): this - deleteAssets(arg: TLAssetId[] | TLAsset[]) { + deleteAssets(arg: TLAssetId[] | TLAsset[]): this { const ids = typeof arg[0] === 'string' ? (arg as TLAssetId[]) : (arg as TLAsset[]).map((a) => a.id) this._deleteAssets(ids) @@ -3594,7 +3633,7 @@ export class Editor extends EventEmitter { /* --------------------- Shapes --------------------- */ @computed - private get _geometryCache(): ComputedCache { + private get _shapeGeometryCache(): ComputedCache { return this.store.createComputedCache('bounds', (shape) => { return this.getShapeUtil(shape).getGeometry(shape) }) @@ -3605,22 +3644,22 @@ export class Editor extends EventEmitter { * * @example * ```ts - * editor.getGeometry(myShape) - * editor.getGeometry(myShapeId) + * editor.getShapeGeometry(myShape) + * editor.getShapeGeometry(myShapeId) * ``` * * @param shape - The shape (or shape id) to get the geometry for. * * @public */ - getGeometry(id: TLShapeId): T - getGeometry(shape: TLShape): T - getGeometry(id: TLShape | TLShapeId): T { - return this._geometryCache.get(typeof id === 'string' ? id : id.id)! as T + getShapeGeometry(id: TLShapeId): T + getShapeGeometry(shape: TLShape): T + getShapeGeometry(id: TLShape | TLShapeId): T { + return this._shapeGeometryCache.get(typeof id === 'string' ? id : id.id)! as T } - @computed - private get _outlineSegmentsCache(): ComputedCache { + /** @internal */ + @computed private get _shapeOutlineSegmentsCache(): ComputedCache { return this.store.createComputedCache('outline-segments', (shape) => { return this.getShapeUtil(shape).getOutlineSegments(shape) }) @@ -3631,24 +3670,25 @@ export class Editor extends EventEmitter { * * @example * ```ts - * editor.getOutlineSegments(myShape) - * editor.getOutlineSegments(myShapeId) + * editor.getShapeOutlineSegments(myShape) + * editor.getShapeOutlineSegments(myShapeId) * ``` * * @param shape - The shape (or shape id) to get the outline segments for. * * @public */ - getOutlineSegments(shape: T): Vec2d[][] - getOutlineSegments(id: T['id']): Vec2d[][] - getOutlineSegments(shape: T | T['id']): Vec2d[][] { + getShapeOutlineSegments(shape: T): Vec2d[][] + getShapeOutlineSegments(id: T['id']): Vec2d[][] + getShapeOutlineSegments(shape: T | T['id']): Vec2d[][] { return ( - this._outlineSegmentsCache.get(typeof shape === 'string' ? shape : shape.id) ?? EMPTY_ARRAY + this._shapeOutlineSegmentsCache.get(typeof shape === 'string' ? shape : shape.id) ?? + EMPTY_ARRAY ) } - @computed - private get handlesCache(): ComputedCache { + /** @internal */ + @computed private get _shapeHandlesCache(): ComputedCache { return this.store.createComputedCache('handles', (shape) => { return this.getShapeUtil(shape).getHandles?.(shape) }) @@ -3659,17 +3699,17 @@ export class Editor extends EventEmitter { * * @example * ```ts - * editor.getHandles(myShape) - * editor.getHandles(myShapeId) + * editor.getShapeHandles(myShape) + * editor.getShapeHandles(myShapeId) * ``` * * @param shape - The shape (or shape id) to get the handles for. * @public */ - getHandles(id: T['id']): TLHandle[] | undefined - getHandles(shape: T): TLHandle[] | undefined - getHandles(shape: T | T['id']): TLHandle[] | undefined { - return this.handlesCache.get(typeof shape === 'string' ? shape : shape.id) + getShapeHandles(id: T['id']): TLHandle[] | undefined + getShapeHandles(shape: T): TLHandle[] | undefined + getShapeHandles(shape: T | T['id']): TLHandle[] | undefined { + return this._shapeHandlesCache.get(typeof shape === 'string' ? shape : shape.id) } /** @@ -3680,16 +3720,16 @@ export class Editor extends EventEmitter { * * @example * ```ts - * editor.getTransform(myShape) + * editor.getShapeLocalTransform(myShape) * ``` * * @param shape - The shape to get the local transform for. * * @public */ - getTransform(shape: TLShape): Matrix2d - getTransform(id: TLShapeId): Matrix2d - getTransform(arg: TLShape | TLShapeId): Matrix2d { + getShapeLocalTransform(shape: TLShape): Matrix2d + getShapeLocalTransform(id: TLShapeId): Matrix2d + getShapeLocalTransform(arg: TLShape | TLShapeId): Matrix2d { const shape = typeof arg === 'string' ? this.getShape(arg) : arg if (!shape) throw Error('Editor.getTransform: shape not found') return Matrix2d.Identity().translate(shape.x, shape.y).rotate(shape.rotation) @@ -3700,18 +3740,19 @@ export class Editor extends EventEmitter { * * @internal */ - @computed private get _pageTransformCache(): ComputedCache { + @computed private get _shapePageTransformCache(): ComputedCache { return this.store.createComputedCache('pageTransformCache', (shape) => { if (isPageId(shape.parentId)) { - return this.getTransform(shape) + return this.getShapeLocalTransform(shape) } // If the shape's parent doesn't exist yet (e.g. when merging in changes from remote in the wrong order) // then we can't compute the transform yet, so just return the identity matrix. // In the future we should look at creating a store update mechanism that understands and preserves // ordering. - const parentTransform = this._pageTransformCache.get(shape.parentId) ?? Matrix2d.Identity() - return Matrix2d.Compose(parentTransform, this.getTransform(shape)!) + const parentTransform = + this._shapePageTransformCache.get(shape.parentId) ?? Matrix2d.Identity() + return Matrix2d.Compose(parentTransform, this.getShapeLocalTransform(shape)!) }) } @@ -3720,54 +3761,50 @@ export class Editor extends EventEmitter { * * @example * ```ts - * editor.getParentTransform(myShape) + * editor.getShapeParentTransform(myShape) * ``` * * @param shape - The shape (or shape id) to get the parent transform for. * * @public */ - getParentTransform(shape: TLShape): Matrix2d - getParentTransform(id: TLShapeId): Matrix2d - getParentTransform(arg: TLShape | TLShapeId): Matrix2d { + getShapeParentTransform(shape: TLShape): Matrix2d + getShapeParentTransform(id: TLShapeId): Matrix2d + getShapeParentTransform(arg: TLShape | TLShapeId): Matrix2d { const shape = typeof arg === 'string' ? this.getShape(arg) : arg if (!shape || isPageId(shape.parentId)) return Matrix2d.Identity() - return this._pageTransformCache.get(shape.parentId) ?? Matrix2d.Identity() + return this._shapePageTransformCache.get(shape.parentId) ?? Matrix2d.Identity() } /** - * Get the page transform (or absolute transform) of a shape. + * Get the transform of a shape in page space. * * @example * ```ts - * editor.getPageTransform(myShape) - * editor.getPageTransform(myShapeId) + * editor.getShapePageTransform(myShape) + * editor.getShapePageTransform(myShapeId) * ``` * * @param shape - The shape (or shape id) to get the page transform for. * * @public */ - getPageTransform(id: TLShapeId): Matrix2d - getPageTransform(shape: TLShape): Matrix2d - getPageTransform(arg: TLShape | TLShapeId): Matrix2d { + getShapePageTransform(id: TLShapeId): Matrix2d + getShapePageTransform(shape: TLShape): Matrix2d + getShapePageTransform(arg: TLShape | TLShapeId): Matrix2d { const id = typeof arg === 'string' ? arg : this.getShape(arg)!.id - return this._pageTransformCache.get(id) ?? Matrix2d.Identity() + return this._shapePageTransformCache.get(id) ?? Matrix2d.Identity() } - /** - * A cache of axis aligned page bounding boxes. - * - * @internal - */ - @computed private get _pageBoundsCache(): ComputedCache { + /** @internal */ + @computed private get _shapePageBoundsCache(): ComputedCache { return this.store.createComputedCache('pageBoundsCache', (shape) => { - const pageTransform = this._pageTransformCache.get(shape.id) + const pageTransform = this._shapePageTransformCache.get(shape.id) if (!pageTransform) return new Box2d() const result = Box2d.FromPoints( - Matrix2d.applyToPoints(pageTransform, this.getGeometry(shape).vertices) + Matrix2d.applyToPoints(pageTransform, this.getShapeGeometry(shape).vertices) ) return result @@ -3775,22 +3812,22 @@ export class Editor extends EventEmitter { } /** - * Get the page (or absolute) bounds of a shape. + * Get the bounds of a shape in page space. * * @example * ```ts - * editor.getPageBounds(myShape) - * editor.getPageBounds(myShapeId) + * editor.getShapePageBounds(myShape) + * editor.getShapePageBounds(myShapeId) * ``` * * @param shape - The shape (or shape id) to get the bounds for. * * @public */ - getPageBounds(shape: TLShape): Box2d | undefined - getPageBounds(id: TLShapeId): Box2d | undefined - getPageBounds(shape: TLShape | TLShapeId): Box2d | undefined { - return this._pageBoundsCache.get(typeof shape === 'string' ? shape : shape.id) + getShapePageBounds(shape: TLShape): Box2d | undefined + getShapePageBounds(id: TLShapeId): Box2d | undefined + getShapePageBounds(shape: TLShape | TLShapeId): Box2d | undefined { + return this._shapePageBoundsCache.get(typeof shape === 'string' ? shape : shape.id) } /** @@ -3798,11 +3835,11 @@ export class Editor extends EventEmitter { * * @internal */ - @computed private get _clipPathCache(): ComputedCache { + @computed private get _shapeClipPathCache(): ComputedCache { return this.store.createComputedCache('clipPathCache', (shape) => { - const pageMask = this._pageMaskCache.get(shape.id) + const pageMask = this._shapeMaskCache.get(shape.id) if (!pageMask) return undefined - const pageTransform = this._pageTransformCache.get(shape.id) + const pageTransform = this._shapePageTransformCache.get(shape.id) if (!pageTransform) return undefined if (pageMask.length === 0) { @@ -3820,8 +3857,8 @@ export class Editor extends EventEmitter { * * @example * ```ts - * const clipPath = editor.getClipPathBy(shape) - * const clipPath = editor.getClipPathBy(shape.id) + * const clipPath = editor.getShapeClipPath(shape) + * const clipPath = editor.getShapeClipPath(shape.id) * ``` * * @param shape - The shape (or shape id) to get the clip path for. @@ -3830,24 +3867,20 @@ export class Editor extends EventEmitter { * * @public */ - getClipPath(shape: TLShape): string | undefined - getClipPath(id: TLShapeId): string | undefined - getClipPath(shape: TLShape | TLShapeId): string | undefined { - return this._clipPathCache.get(typeof shape === 'string' ? shape : shape.id) + getShapeClipPath(shape: TLShape): string | undefined + getShapeClipPath(id: TLShapeId): string | undefined + getShapeClipPath(shape: TLShape | TLShapeId): string | undefined { + return this._shapeClipPathCache.get(typeof shape === 'string' ? shape : shape.id) } - /** - * A cache of page masks used for clipping. - * - * @internal - */ - @computed private get _pageMaskCache(): ComputedCache { + /** @internal */ + @computed private get _shapeMaskCache(): ComputedCache { return this.store.createComputedCache('pageMaskCache', (shape) => { if (isPageId(shape.parentId)) { return undefined } - const frameAncestors = this.getAncestors(shape.id).filter((shape) => + const frameAncestors = this.getShapeAncestors(shape.id).filter((shape) => this.isShapeOfType(shape, 'frame') ) @@ -3856,7 +3889,7 @@ export class Editor extends EventEmitter { const pageMask = frameAncestors .map((s) => // Apply the frame transform to the frame outline to get the frame outline in page space - this._pageTransformCache.get(s.id)!.applyToPoints(this.getGeometry(s).vertices) + this._shapePageTransformCache.get(s.id)!.applyToPoints(this.getShapeGeometry(s).vertices) ) .reduce((acc, b) => { if (!(b && acc)) return undefined @@ -3872,47 +3905,47 @@ export class Editor extends EventEmitter { } /** - * Get the page mask for a shape. + * Get the mask (in page space) for a shape. * * @example * ```ts - * const pageMask = editor.getPageMask(shape.id) + * const pageMask = editor.getShapeMask(shape.id) * ``` * - * @param id - The id of the shape to get the page mask for. + * @param id - The id of the shape to get the mask for. * - * @returns The page mask for the shape. + * @returns The mask for the shape. * * @public */ - getPageMask(id: TLShapeId): VecLike[] | undefined - getPageMask(shape: TLShape): VecLike[] | undefined - getPageMask(shape: TLShapeId | TLShape): VecLike[] | undefined { - return this._pageMaskCache.get(typeof shape === 'string' ? shape : shape.id) + getShapeMask(id: TLShapeId): VecLike[] | undefined + getShapeMask(shape: TLShape): VecLike[] | undefined + getShapeMask(shape: TLShapeId | TLShape): VecLike[] | undefined { + return this._shapeMaskCache.get(typeof shape === 'string' ? shape : shape.id) } /** - * Get the page (or absolute) bounds of a shape, incorporating any masks. For example, if the + * Get the bounds of a shape in page space, incorporating any masks. For example, if the * shape were the child of a frame and was half way out of the frame, the bounds would be the half * of the shape that was in the frame. * * @example * ```ts - * editor.getMaskedPageBounds(myShape) - * editor.getMaskedPageBounds(myShapeId) + * editor.getShapeMaskedPageBounds(myShape) + * editor.getShapeMaskedPageBounds(myShapeId) * ``` * * @param shape - The shape to get the masked bounds for. * * @public */ - getMaskedPageBounds(id: TLShapeId): Box2d | undefined - getMaskedPageBounds(shape: TLShape): Box2d | undefined - getMaskedPageBounds(id: TLShapeId | TLShape): Box2d | undefined { + getShapeMaskedPageBounds(id: TLShapeId): Box2d | undefined + getShapeMaskedPageBounds(shape: TLShape): Box2d | undefined + getShapeMaskedPageBounds(id: TLShapeId | TLShape): Box2d | undefined { if (typeof id !== 'string') id = id.id - const pageBounds = this._pageBoundsCache.get(id) + const pageBounds = this._shapePageBoundsCache.get(id) if (!pageBounds) return - const pageMask = this._pageMaskCache.get(id) + const pageMask = this._shapeMaskCache.get(id) if (pageMask) { const intersection = intersectPolygonPolygon(pageMask, pageBounds.corners) if (!intersection) return @@ -3927,17 +3960,17 @@ export class Editor extends EventEmitter { * * @example * ```ts - * const ancestors = editor.getAncestors(myShape) - * const ancestors = editor.getAncestors(myShapeId) + * const ancestors = editor.getShapeAncestors(myShape) + * const ancestors = editor.getShapeAncestors(myShapeId) * ``` * * @param shape - The shape (or shape id) to get the ancestors for. * * @public */ - getAncestors(shape: TLShape, acc?: TLShape[]): TLShape[] - getAncestors(id: TLShapeId, acc?: TLShape[]): TLShape[] - getAncestors(arg: TLShapeId | TLShape, acc: TLShape[] = []): TLShape[] { + getShapeAncestors(shape: TLShape, acc?: TLShape[]): TLShape[] + getShapeAncestors(id: TLShapeId, acc?: TLShape[]): TLShape[] + getShapeAncestors(arg: TLShapeId | TLShape, acc: TLShape[] = []): TLShape[] { const shape = typeof arg === 'string' ? this.getShape(arg) : arg if (!shape) return acc const parentId = shape.parentId @@ -3949,7 +3982,7 @@ export class Editor extends EventEmitter { const parent = this.store.get(parentId) if (!parent) return acc acc.push(parent) - return this.getAncestors(parent, acc) + return this.getShapeAncestors(parent, acc) } /** @@ -3957,16 +3990,18 @@ export class Editor extends EventEmitter { * * @example * ```ts - * const ancestor = editor.findAncestor(myShape) + * const ancestor = editor.findShapeAncestor(myShape) + * const ancestor = editor.findShapeAncestor(myShape.id) + * const ancestor = editor.findShapeAncestor(myShape.id, (shape) => shape.type === 'frame') * ``` * * @param shape - The shape to check the ancestors for. * * @public */ - findAncestor(shape: TLShape, predicate: (parent: TLShape) => boolean): TLShape | undefined - findAncestor(id: TLShapeId, predicate: (parent: TLShape) => boolean): TLShape | undefined - findAncestor( + findShapeAncestor(shape: TLShape, predicate: (parent: TLShape) => boolean): TLShape | undefined + findShapeAncestor(id: TLShapeId, predicate: (parent: TLShape) => boolean): TLShape | undefined + findShapeAncestor( arg: TLShape | TLShapeId, predicate: (parent: TLShape) => boolean ): TLShape | undefined { @@ -3978,7 +4013,7 @@ export class Editor extends EventEmitter { const parent = this.getShape(parentId) if (!parent) return - return predicate(parent) ? parent : this.findAncestor(parent, predicate) + return predicate(parent) ? parent : this.findShapeAncestor(parent, predicate) } /** @@ -3995,7 +4030,7 @@ export class Editor extends EventEmitter { const shape = typeof arg === 'string' ? this.getShape(arg) : arg if (!shape) return false if (shape.parentId === ancestorId) return true - return this.hasAncestor(this.getParentShape(shape), ancestorId) + return this.hasAncestor(this.getShapeParent(shape), ancestorId) } /** @@ -4030,21 +4065,21 @@ export class Editor extends EventEmitter { if (isPageId(parentId)) { return } - return predicate ? this.findAncestor(shapes[0], predicate)?.id : parentId + return predicate ? this.findShapeAncestor(shapes[0], predicate)?.id : parentId } const [nodeA, ...others] = shapes - let ancestor = this.getParentShape(nodeA) + let ancestor = this.getShapeParent(nodeA) while (ancestor) { // TODO: this is not ideal, optimize if (predicate && !predicate(ancestor)) { - ancestor = this.getParentShape(ancestor) + ancestor = this.getShapeParent(ancestor) continue } if (others.every((shape) => this.hasAncestor(shape, ancestor!.id))) { return ancestor!.id } - ancestor = this.getParentShape(ancestor) + ancestor = this.getShapeParent(ancestor) } return undefined } @@ -4062,19 +4097,19 @@ export class Editor extends EventEmitter { const shape = typeof arg === 'string' ? this.getShape(arg) : arg if (shape === undefined) return false if (shape.isLocked) return true - return this.isShapeOrAncestorLocked(this.getParentShape(shape)) + return this.isShapeOrAncestorLocked(this.getShapeParent(shape)) } /** - * The common bounds of all of the shapes on the page. + * The bounds of the current page (the common bounds of all of the shapes on the page). * * @public */ - @computed get commonBoundsOfAllShapesOnCurrentPage(): Box2d | undefined { + @computed get currentPageBounds(): Box2d | undefined { let commonBounds: Box2d | undefined - this.shapeIdsOnCurrentPage.forEach((shapeId) => { - const bounds = this.getMaskedPageBounds(shapeId) + this.currentPageShapeIds.forEach((shapeId) => { + const bounds = this.getShapeMaskedPageBounds(shapeId) if (!bounds) return if (!commonBounds) { commonBounds = bounds.clone() @@ -4095,7 +4130,7 @@ export class Editor extends EventEmitter { */ getSelectedShapeAtPoint(point: VecLike): TLShape | undefined { const { selectedShapeIds } = this - return this.sortedShapesOnCurrentPage + return this.currentPageShapesSorted .filter((shape) => shape.type !== 'group' && selectedShapeIds.includes(shape.id)) .findLast((shape) => this.isPointInShape(shape, point, { hitInside: true, margin: 0 })) } @@ -4119,7 +4154,11 @@ export class Editor extends EventEmitter { } ): TLShape | undefined { // are we inside of a shape but not hovering it? - const { viewportPageBounds, zoomLevel, sortedShapesOnCurrentPage } = this + const { + viewportPageBounds, + zoomLevel, + currentPageShapesSorted: sortedShapesOnCurrentPage, + } = this const { filter, margin = 0, hitInside = false, hitFrameInside = false } = opts let inHollowSmallestArea = Infinity @@ -4130,7 +4169,7 @@ export class Editor extends EventEmitter { const shapesToCheck = sortedShapesOnCurrentPage.filter((shape) => { if (this.isShapeOfType(shape, 'group')) return false - const pageMask = this.getPageMask(shape) + const pageMask = this.getShapeMask(shape) if (pageMask && !pointInPolygon(point, pageMask)) return false if (filter) return filter(shape) return true @@ -4138,7 +4177,7 @@ export class Editor extends EventEmitter { for (let i = shapesToCheck.length - 1; i >= 0; i--) { const shape = shapesToCheck[i] - let geometry = this.getGeometry(shape) + let geometry = this.getShapeGeometry(shape) const pointInShapeSpace = this.getPointInShapeSpace(shape, point) const distance = geometry.distanceToPoint(pointInShapeSpace, hitInside) @@ -4183,7 +4222,7 @@ export class Editor extends EventEmitter { return inMarginClosestToEdgeHit || shape } else { // If the shape is bigger than the viewport, then skip it. - if (this.getPageBounds(shape)!.contains(viewportPageBounds)) continue + if (this.getShapePageBounds(shape)!.contains(viewportPageBounds)) continue // For hollow shapes... if (Math.abs(distance) < margin) { @@ -4242,7 +4281,7 @@ export class Editor extends EventEmitter { point: VecLike, opts = {} as { margin?: number; hitInside?: boolean } ): TLShape[] { - return this.shapesOnCurrentPage.filter((shape) => this.isPointInShape(shape, point, opts)) + return this.currentPageShapes.filter((shape) => this.isPointInShape(shape, point, opts)) } /** @@ -4282,10 +4321,10 @@ export class Editor extends EventEmitter { if (typeof id !== 'string') id = id.id // If the shape is masked, and if the point falls outside of that // mask, then it's defintely a miss—we don't need to test further. - const pageMask = this.getPageMask(id) + const pageMask = this.getShapeMask(id) if (pageMask && !pointInPolygon(point, pageMask)) return false - return this.getGeometry(id).hitTestPoint( + return this.getShapeGeometry(id).hitTestPoint( this.getPointInShapeSpace(id, point), margin, hitInside @@ -4311,17 +4350,15 @@ export class Editor extends EventEmitter { getPointInShapeSpace(id: TLShapeId, point: VecLike): Vec2d getPointInShapeSpace(arg: TLShape | TLShapeId, point: VecLike): Vec2d { const id = typeof arg === 'string' ? arg : arg.id - return this._pageTransformCache.get(id)!.clone().invert().applyToPoint(point) + return this._shapePageTransformCache.get(id)!.clone().invert().applyToPoint(point) } /** - * Convert a delta in page space to a point in the local space of a shape. For example, if a - * shape's page point were `{ x: 100, y: 100 }`, a page point at `{ x: 110, y: 110 }` would be at - * `{ x: 10, y: 10 }` in the shape's local space. + * Convert a delta in page space to a point in the local space of a shape's parent. * * @example * ```ts - * editor.getPointInShapeSpace(myShape.id, { x: 100, y: 100 }) + * editor.getPointInParentSpace(myShape.id, { x: 100, y: 100 }) * ``` * * @param shape - The shape to get the point in the local space of. @@ -4336,7 +4373,7 @@ export class Editor extends EventEmitter { if (!shape) return new Vec2d(0, 0) if (isPageId(shape.parentId)) return Vec2d.From(point) - const parentTransform = this.getPageTransform(shape.parentId) + const parentTransform = this.getShapePageTransform(shape.parentId) if (!parentTransform) return Vec2d.From(point) return parentTransform.clone().invert().applyToPoint(point) } @@ -4346,15 +4383,15 @@ export class Editor extends EventEmitter { * * @example * ```ts - * editor.shapesOnCurrentPage + * editor.currentPageShapes * ``` * * @readonly * * @public */ - @computed get shapesOnCurrentPage() { - return Array.from(this.shapeIdsOnCurrentPage, (id) => this.store.get(id)! as TLShape) + @computed get currentPageShapes() { + return Array.from(this.currentPageShapeIds, (id) => this.store.get(id)! as TLShape) } /** @@ -4363,17 +4400,17 @@ export class Editor extends EventEmitter { * * @example * ```ts - * editor.sortedShapesOnCurrentPage + * editor.currentPageShapesSorted * ``` * * @readonly * * @public */ - @computed get sortedShapesOnCurrentPage(): TLShape[] { + @computed get currentPageShapesSorted(): TLShape[] { // todo: consider making into a function call that includes options for selected-only, rendering, etc. // todo: consider making a derivation or something, or merging with rendering shapes - const shapes = new Set(this.shapesOnCurrentPage.sort(sortByIndex)) + const shapes = new Set(this.currentPageShapes.sort(sortByIndex)) const results: TLShape[] = [] @@ -4451,14 +4488,14 @@ export class Editor extends EventEmitter { * * @example * ```ts - * editor.getParentShape(myShape) + * editor.getShapeParent(myShape) * ``` * * @public */ - getParentShape(shape?: TLShape): TLShape | undefined - getParentShape(shapeId?: TLShapeId): TLShape | undefined - getParentShape(arg?: TLShape | TLShapeId): TLShape | undefined { + getShapeParent(shape?: TLShape): TLShape | undefined + getShapeParent(shapeId?: TLShapeId): TLShape | undefined + getShapeParent(arg?: TLShape | TLShapeId): TLShape | undefined { const shape = typeof arg === 'string' ? this.getShape(arg)! : arg if (shape === undefined || !isShapeId(shape.parentId)) return undefined return this.store.get(shape.parentId) @@ -4482,7 +4519,7 @@ export class Editor extends EventEmitter { return targetShape } - const ancestor = this.findAncestor( + const ancestor = this.findShapeAncestor( targetShape, (ancestor) => ancestor.parentId === siblingShape.parentId ) @@ -4582,7 +4619,7 @@ export class Editor extends EventEmitter { const parentTransform = isPageId(parentId) ? Matrix2d.Identity() - : this.getPageTransform(parentId)! + : this.getShapePageTransform(parentId)! const parentPageRotation = parentTransform.rotation() @@ -4631,7 +4668,7 @@ export class Editor extends EventEmitter { id = ids[i] const shape = this.getShape(id) if (!shape) continue - const pageTransform = this.getPageTransform(shape)! + const pageTransform = this.getShapePageTransform(shape)! if (!pageTransform) continue const pagePoint = pageTransform.point() @@ -4772,7 +4809,7 @@ export class Editor extends EventEmitter { */ getDroppingOverShape(point: VecLike, droppingShapes: TLShape[] = []) { // starting from the top... - return this.sortedShapesOnCurrentPage.findLast((shape) => { + return this.currentPageShapesSorted.findLast((shape) => { if ( // only allow shapes that can receive children !this.getShapeUtil(shape).canDropShapes(shape, droppingShapes) || @@ -4784,11 +4821,11 @@ export class Editor extends EventEmitter { // Only allow dropping into the masked page bounds of the shape, e.g. when a frame is // partially clipped by its own parent frame - const maskedPageBounds = this.getMaskedPageBounds(shape.id) + const maskedPageBounds = this.getShapeMaskedPageBounds(shape.id) if ( maskedPageBounds && maskedPageBounds.containsPoint(point) && - this.getGeometry(shape).hitTestPoint(this.getPointInShapeSpace(shape, point), 0, true) + this.getShapeGeometry(shape).hitTestPoint(this.getPointInShapeSpace(shape, point), 0, true) ) { return true } @@ -4830,7 +4867,7 @@ export class Editor extends EventEmitter { } else if (this.focusedGroupId === node.id) { break } - node = this.getParentShape(node) + node = this.getShapeParent(node) } return match @@ -4896,7 +4933,7 @@ export class Editor extends EventEmitter { } const localDelta = Vec2d.Cast(offset) - const parentTransform = this.getParentTransform(shape) + const parentTransform = this.getShapeParentTransform(shape) if (parentTransform) localDelta.rot(-parentTransform.rotation()) const translateStartChanges = this.getShapeUtil(shape).onTranslateStart?.(shape) @@ -4973,7 +5010,7 @@ export class Editor extends EventEmitter { let oy = 0 if (offset && initialIds.has(id)) { - const parentTransform = this.getParentTransform(shape) + const parentTransform = this.getShapeParentTransform(shape) const vec = new Vec2d(offset.x, offset.y).rot(-parentTransform!.rotation()) ox = vec.x oy = vec.y @@ -5082,14 +5119,14 @@ export class Editor extends EventEmitter { this.history.batch(() => { const maxShapesReached = - shapesToCreate.length + this.shapeIdsOnCurrentPage.size > MAX_SHAPES_PER_PAGE + shapesToCreate.length + this.currentPageShapeIds.size > MAX_SHAPES_PER_PAGE if (maxShapesReached) { alertMaxShapes(this) } const newShapes = maxShapesReached - ? shapesToCreate.slice(0, MAX_SHAPES_PER_PAGE - this.shapeIdsOnCurrentPage.size) + ? shapesToCreate.slice(0, MAX_SHAPES_PER_PAGE - this.currentPageShapeIds.size) : shapesToCreate const ids = newShapes.map((s) => s.id) @@ -5148,7 +5185,7 @@ export class Editor extends EventEmitter { // If there is no space on pageId, or if the selected shapes // would take the new page above the limit, don't move the shapes - if (this.getShapeIdsInPage(pageId).size + content.shapes.length > MAX_SHAPES_PER_PAGE) { + if (this.getCurrentPageShapeIds(pageId).size + content.shapes.length > MAX_SHAPES_PER_PAGE) { alertMaxShapes(this, pageId) return this } @@ -5165,7 +5202,7 @@ export class Editor extends EventEmitter { // Put the shape content onto the new page; parents and indices will // be taken care of by the putContent method; make sure to pop any focus // layers so that the content will be put onto the page. - this.setFocusedGroupId(this.currentPageId) + this.setFocusedGroupId(null) this.selectNone() this.putContent(content, { select: true, preserveIds: true, preservePosition: true }) @@ -5173,7 +5210,7 @@ export class Editor extends EventEmitter { // "from" page's camera, then center the "to" page's camera on the // pasted shapes this.setCamera({ ...this.camera, z: fromPageZ }) - this.centerOnPoint(this.selectionBounds!.center) + this.centerOnPoint(this.selectionRotatedPageBounds!.center) }) return this @@ -5358,12 +5395,14 @@ export class Editor extends EventEmitter { .flat() ) - const scaleOriginPage = Box2d.Common(compact(shapes.map((id) => this.getPageBounds(id)))).center + const scaleOriginPage = Box2d.Common( + compact(shapes.map((id) => this.getShapePageBounds(id))) + ).center this.batch(() => { for (const shape of shapes) { - const bounds = this.getGeometry(shape).bounds - const initialPageTransform = this.getPageTransform(shape.id) + const bounds = this.getShapeGeometry(shape).bounds + const initialPageTransform = this.getShapePageTransform(shape.id) if (!initialPageTransform) continue this.resizeShape( shape.id, @@ -5400,7 +5439,11 @@ export class Editor extends EventEmitter { */ stackShapes(shapes: TLShape[], operation: 'horizontal' | 'vertical', gap: number): this stackShapes(ids: TLShapeId[], operation: 'horizontal' | 'vertical', gap: number): this - stackShapes(arg: TLShapeId[] | TLShape[], operation: 'horizontal' | 'vertical', gap: number) { + stackShapes( + arg: TLShapeId[] | TLShape[], + operation: 'horizontal' | 'vertical', + gap: number + ): this { const ids = typeof arg[0] === 'string' ? (arg as TLShapeId[]) : (arg as TLShape[]).map((s) => s.id) if (this.instanceState.isReadonly) return this @@ -5422,7 +5465,7 @@ export class Editor extends EventEmitter { if ((gap === 0 && len < 3) || len < 2) return this const pageBounds = Object.fromEntries( - shapes.map((shape) => [shape.id, this.getPageBounds(shape)!]) + shapes.map((shape) => [shape.id, this.getShapePageBounds(shape)!]) ) let val: 'x' | 'y' @@ -5498,9 +5541,9 @@ export class Editor extends EventEmitter { const delta = { x: 0, y: 0 } delta[val] = v + shapeGap - pageBounds[shape.id][val] - const parent = this.getParentShape(shape) + const parent = this.getShapeParent(shape) const localDelta = parent - ? Vec2d.Rot(delta, -this.getPageTransform(parent)!.decompose().rotation) + ? Vec2d.Rot(delta, -this.getShapePageTransform(parent)!.decompose().rotation) : delta const translateStartChanges = this.getShapeUtil(shape).onTranslateStart?.(shape) @@ -5571,7 +5614,7 @@ export class Editor extends EventEmitter { for (let i = 0; i < shapes.length; i++) { shape = shapes[i] - bounds = this.getPageBounds(shape)! + bounds = this.getShapePageBounds(shape)! shapePageBounds[shape.id] = bounds nextShapePageBounds[shape.id] = bounds.clone() area += bounds.width * bounds.height @@ -5655,7 +5698,7 @@ export class Editor extends EventEmitter { nextBounds = nextShapePageBounds[shape.id] const delta = Vec2d.Sub(nextBounds.point, bounds.point).add(centerDelta) - const parentTransform = this.getParentTransform(shape) + const parentTransform = this.getShapeParentTransform(shape) if (parentTransform) delta.rot(-parentTransform.rotation()) const change: TLShapePartial = { @@ -5718,7 +5761,7 @@ export class Editor extends EventEmitter { const shapes = compact(ids.map((id) => this.getShape(id))) const shapePageBounds = Object.fromEntries( - shapes.map((shape) => [shape.id, this.getPageBounds(shape)]) + shapes.map((shape) => [shape.id, this.getShapePageBounds(shape)]) ) const commonBounds = Box2d.Common(compact(Object.values(shapePageBounds))) @@ -5757,9 +5800,9 @@ export class Editor extends EventEmitter { } } - const parent = this.getParentShape(shape) + const parent = this.getShapeParent(shape) const localDelta = parent - ? Vec2d.Rot(delta, -this.getPageTransform(parent)!.decompose().rotation) + ? Vec2d.Rot(delta, -this.getShapePageTransform(parent)!.decompose().rotation) : delta const translateChanges = this.getShapeUtil(shape).onTranslateStart?.(shape) @@ -5810,7 +5853,7 @@ export class Editor extends EventEmitter { const len = ids.length const shapes = compact(ids.map((id) => this.getShape(id))) const pageBounds = Object.fromEntries( - shapes.map((shape) => [shape.id, this.getPageBounds(shape)!]) + shapes.map((shape) => [shape.id, this.getShapePageBounds(shape)!]) ) let val: 'x' | 'y' @@ -5849,9 +5892,9 @@ export class Editor extends EventEmitter { const delta = { x: 0, y: 0 } delta[val] = v + step * i - pageBounds[shape.id][dim] / 2 - pageBounds[shape.id][val] - const parent = this.getParentShape(shape) + const parent = this.getShapeParent(shape) const localDelta = parent - ? Vec2d.Rot(delta, -this.getPageTransform(parent)!.rotation()) + ? Vec2d.Rot(delta, -this.getShapePageTransform(parent)!.rotation()) : delta const translateStartChanges = this.getShapeUtil(shape).onTranslateStart?.(shape) @@ -5897,8 +5940,8 @@ export class Editor extends EventEmitter { if (ids.length < 2) return this const shapes = compact(ids.map((id) => this.getShape(id))) - const shapeBounds = Object.fromEntries(ids.map((id) => [id, this.getGeometry(id).bounds])) - const shapePageBounds = Object.fromEntries(ids.map((id) => [id, this.getPageBounds(id)!])) + const shapeBounds = Object.fromEntries(ids.map((id) => [id, this.getShapeGeometry(id).bounds])) + const shapePageBounds = Object.fromEntries(ids.map((id) => [id, this.getShapePageBounds(id)!])) const commonBounds = Box2d.Common(compact(Object.values(shapePageBounds))) const changes: TLShapePartial[] = [] @@ -5907,12 +5950,12 @@ export class Editor extends EventEmitter { case 'vertical': { this.batch(() => { for (const shape of shapes) { - const pageRotation = this.getPageTransform(shape)!.rotation() + const pageRotation = this.getShapePageTransform(shape)!.rotation() if (pageRotation % PI2) continue const bounds = shapeBounds[shape.id] const pageBounds = shapePageBounds[shape.id] const localOffset = new Vec2d(0, commonBounds.minY - pageBounds.minY) - const parentTransform = this.getParentTransform(shape) + const parentTransform = this.getShapeParentTransform(shape) if (parentTransform) localOffset.rot(-parentTransform.rotation()) const { x, y } = Vec2d.Add(localOffset, shape) @@ -5932,10 +5975,10 @@ export class Editor extends EventEmitter { for (const shape of shapes) { const bounds = shapeBounds[shape.id] const pageBounds = shapePageBounds[shape.id] - const pageRotation = this.getPageTransform(shape)!.rotation() + const pageRotation = this.getShapePageTransform(shape)!.rotation() if (pageRotation % PI2) continue const localOffset = new Vec2d(commonBounds.minX - pageBounds.minX, 0) - const parentTransform = this.getParentTransform(shape) + const parentTransform = this.getShapeParentTransform(shape) if (parentTransform) localOffset.rot(-parentTransform.rotation()) const { x, y } = Vec2d.Add(localOffset, shape) @@ -5987,12 +6030,12 @@ export class Editor extends EventEmitter { const initialShape = options.initialShape ?? this.getShape(id) if (!initialShape) return this - const scaleOrigin = options.scaleOrigin ?? this.getPageBounds(id)?.center + const scaleOrigin = options.scaleOrigin ?? this.getShapePageBounds(id)?.center if (!scaleOrigin) return this const pageTransform = options.initialPageTransform ? Matrix2d.Cast(options.initialPageTransform) - : this.getPageTransform(id) + : this.getShapePageTransform(id) if (!pageTransform) return this const pageRotation = pageTransform.rotation() @@ -6001,7 +6044,7 @@ export class Editor extends EventEmitter { const scaleAxisRotation = options.scaleAxisRotation ?? pageRotation - const initialBounds = options.initialBounds ?? this.getGeometry(id).bounds + const initialBounds = options.initialBounds ?? this.getShapeGeometry(id).bounds if (!initialBounds) return this @@ -6176,8 +6219,8 @@ export class Editor extends EventEmitter { ) // now calculate how far away the shape is from where it needs to be - const pageBounds = this.getPageBounds(id)! - const pageTransform = this.getPageTransform(id)! + const pageBounds = this.getShapePageBounds(id)! + const pageTransform = this.getShapePageTransform(id)! const currentPageCenter = pageBounds.center const shapePageTransformOrigin = pageTransform.point() if (!currentPageCenter || !shapePageTransformOrigin) return this @@ -6246,8 +6289,8 @@ export class Editor extends EventEmitter { * * @public */ - createShape(partial: TLShapePartial, select = false) { - this._createShapes([partial], select) + createShape(partial: OptionalKeys, 'id'>): this { + this._createShapes([partial]) return this } @@ -6264,24 +6307,24 @@ export class Editor extends EventEmitter { * * @public */ - createShapes(partials: TLShapePartial[], select = false) { + createShapes(partials: OptionalKeys, 'id'>[]) { if (!Array.isArray(partials)) { throw Error('Editor.createShapes: must provide an array of shapes or shape partials') } - this._createShapes(partials, select) + this._createShapes(partials) return this } /** @internal */ private _createShapes = this.history.createCommand( 'createShapes', - (partials: TLShapePartial[], select = false) => { + (partials: OptionalKeys[]) => { if (this.instanceState.isReadonly) return null if (partials.length <= 0) return null - const { shapeIdsOnCurrentPage: shapeIds } = this + const { currentPageShapeIds } = this - const maxShapesReached = partials.length + shapeIds.size > MAX_SHAPES_PER_PAGE + const maxShapesReached = partials.length + currentPageShapeIds.size > MAX_SHAPES_PER_PAGE if (maxShapesReached) { // can't create more shapes than fit on the page @@ -6291,20 +6334,17 @@ export class Editor extends EventEmitter { if (partials.length === 0) return null - const prevSelectedShapeIds = select ? this.selectedShapeIds : undefined - return { data: { currentPageId: this.currentPageId, - createdIds: partials.map((p) => p.id), - prevSelectedShapeIds, - partials, - select, + partials: partials.map((p) => + p.id ? p : { ...p, id: createShapeId() } + ) as TLShapePartial[], }, } }, { - do: ({ createdIds, partials, select }) => { + do: ({ partials }) => { const { focusedGroupId } = this // 1. Parents @@ -6312,7 +6352,7 @@ export class Editor extends EventEmitter { // Make sure that each partial will become the child of either the // page or another shape that exists (or that will exist) in this page. - const { sortedShapesOnCurrentPage } = this + const { currentPageShapesSorted: sortedShapesOnCurrentPage } = this partials = partials.map((partial) => { // If the partial does not provide the parentId OR if the provided // parentId is NOT in the store AND NOT among the other shapes being @@ -6355,7 +6395,7 @@ export class Editor extends EventEmitter { partial.x = point.x partial.y = point.y partial.rotation = - -this.getPageTransform(parentId)!.rotation() + (partial.rotation ?? 0) + -this.getShapePageTransform(parentId)!.rotation() + (partial.rotation ?? 0) } // a shape cannot be it's own parent. This was a rare issue with frames/groups in the syncFuzz tests. @@ -6446,26 +6486,9 @@ export class Editor extends EventEmitter { }) this.store.put(shapeRecordsToCreate) - - // If we're also selecting the newly created shapes, attempt to select all of them; - - // the engine will filter out any shapes that are descendants of other new shapes. - if (select) { - this.store.update(this.currentPageState.id, (state) => ({ - ...state, - selectedShapeIds: createdIds, - })) - } }, - undo: ({ createdIds, prevSelectedShapeIds }) => { - this.store.remove(createdIds) - - if (prevSelectedShapeIds) { - this.store.update(this.currentPageState.id, (state) => ({ - ...state, - selectedShapeIds: prevSelectedShapeIds, - })) - } + undo: ({ partials }) => { + this.store.remove(partials.map((p) => p.id)) }, } ) @@ -6632,7 +6655,7 @@ export class Editor extends EventEmitter { const shapes = compact(this._getUnlockedShapeIds(ids).map((id) => this.getShape(id))) const sortedShapeIds = shapes.sort(sortByIndex).map((s) => s.id) - const pageBounds = Box2d.Common(compact(shapes.map((id) => this.getPageBounds(id)))) + const pageBounds = Box2d.Common(compact(shapes.map((id) => this.getShapePageBounds(id)))) const { x, y } = pageBounds.point @@ -7413,7 +7436,7 @@ export class Editor extends EventEmitter { ) shapes = shapes.map((shape) => { - pageTransforms[shape.id] = this.getPageTransform(shape.id)! + pageTransforms[shape.id] = this.getShapePageTransform(shape.id)! shape = structuredClone(shape) as typeof shape @@ -7497,7 +7520,7 @@ export class Editor extends EventEmitter { // Need to get page point and rotation of the shape because shapes in // groups use local position/rotation - const pageTransform = this.getPageTransform(shape.id)! + const pageTransform = this.getShapePageTransform(shape.id)! const pagePoint = pageTransform.point() const pageRotation = pageTransform.rotation() shape.x = pagePoint.x @@ -7570,7 +7593,7 @@ export class Editor extends EventEmitter { if (lowestDepth === 0) break const isFrame = this.isShapeOfType(shape, 'frame') - const ancestors = this.getAncestors(shape) + const ancestors = this.getShapeAncestors(shape) if (isFrame) ancestors.push(shape) const depth = isFrame ? ancestors.length + 1 : ancestors.length @@ -7602,7 +7625,7 @@ export class Editor extends EventEmitter { if (!isPageId(pasteParentId)) { const parent = this.getShape(pasteParentId) if (parent) { - if (!this.viewportPageBounds.includes(this.getPageBounds(parent)!)) { + if (!this.viewportPageBounds.includes(this.getShapePageBounds(parent)!)) { pasteParentId = currentPageId } else { if (rootShapeIds.length === 1) { @@ -7687,7 +7710,7 @@ export class Editor extends EventEmitter { return newShape }) - if (newShapes.length + this.shapeIdsOnCurrentPage.size > MAX_SHAPES_PER_PAGE) { + if (newShapes.length + this.currentPageShapeIds.size > MAX_SHAPES_PER_PAGE) { // There's some complexity here involving children // that might be created without their parents, so // if we're going over the limit then just don't paste. @@ -7777,7 +7800,11 @@ export class Editor extends EventEmitter { } // Create the shapes with root shapes as children of the page - this.createShapes(newShapes, select) + this.createShapes(newShapes) + + if (select) { + this.select(...rootShapes.map((s) => s.id)) + } // And then, if needed, reparent the root shapes to the paste parent if (pasteParentId !== currentPageId) { @@ -7788,13 +7815,13 @@ export class Editor extends EventEmitter { } const newCreatedShapes = newShapes.map((s) => this.getShape(s.id)!) - const bounds = Box2d.Common(newCreatedShapes.map((s) => this.getPageBounds(s)!)) + const bounds = Box2d.Common(newCreatedShapes.map((s) => this.getShapePageBounds(s)!)) if (point === undefined) { if (!isPageId(pasteParentId)) { // Put the shapes in the middle of the (on screen) parent const shape = this.getShape(pasteParentId)! - point = this.getGeometry(shape).bounds.center + point = this.getShapeGeometry(shape).bounds.center } else { const { viewportPageBounds } = this if (preservePosition || viewportPageBounds.includes(Box2d.From(bounds))) { @@ -7996,7 +8023,7 @@ export class Editor extends EventEmitter { } if (!shapeSvgElement && !backgroundSvgElement) { - const bounds = this.getPageBounds(shape)! + const bounds = this.getShapePageBounds(shape)! const elm = window.document.createElementNS('http://www.w3.org/2000/svg', 'rect') elm.setAttribute('width', bounds.width + '') elm.setAttribute('height', bounds.height + '') @@ -8006,7 +8033,7 @@ export class Editor extends EventEmitter { shapeSvgElement = elm } - let pageTransform = this.getPageTransform(shape)!.toCssString() + let pageTransform = this.getShapePageTransform(shape)!.toCssString() if ('scale' in shape.props) { if (shape.props.scale !== 1) { pageTransform = `${pageTransform} scale(${shape.props.scale}, ${shape.props.scale})` @@ -8019,7 +8046,7 @@ export class Editor extends EventEmitter { backgroundSvgElement?.setAttribute('opacity', opacity + '') // Create svg mask if shape has a frame as parent - const pageMask = this.getPageMask(shape.id) + const pageMask = this.getShapeMask(shape.id) if (pageMask) { // Create a clip path and add it to defs const clipPathEl = document.createElementNS('http://www.w3.org/2000/svg', 'clipPath') diff --git a/packages/editor/src/lib/editor/managers/SnapManager.ts b/packages/editor/src/lib/editor/managers/SnapManager.ts index 43b93daef..aac698b51 100644 --- a/packages/editor/src/lib/editor/managers/SnapManager.ts +++ b/packages/editor/src/lib/editor/managers/SnapManager.ts @@ -235,9 +235,9 @@ export class SnapManager { @computed get snapPointsCache() { const { editor } = this return editor.store.createComputedCache('snapPoints', (shape) => { - const pageTransfrorm = editor.getPageTransform(shape.id) + const pageTransfrorm = editor.getShapePageTransform(shape.id) if (!pageTransfrorm) return undefined - const snapPoints = this.editor.getGeometry(shape).snapPoints + const snapPoints = this.editor.getShapeGeometry(shape).snapPoints return snapPoints.map((point, i) => { const { x, y } = Matrix2d.applyToPoint(pageTransfrorm, point) return { x, y, id: `${shape.id}:${i}` } @@ -267,7 +267,7 @@ export class SnapManager { // Skip any shapes that don't allow snapping if (!util.canSnap(childShape)) continue // Only consider shapes if they're inside of the viewport page bounds - const pageBounds = editor.getPageBounds(childId) + const pageBounds = editor.getShapePageBounds(childId) if (!(pageBounds && renderingBounds.includes(pageBounds))) continue // Snap to children of groups but not group itself if (editor.isShapeOfType(childShape, 'group')) { @@ -277,7 +277,7 @@ export class SnapManager { snappableShapes.push({ id: childId, pageBounds, - isClosed: editor.getGeometry(childShape).isClosed, + isClosed: editor.getShapeGeometry(childShape).isClosed, }) } } @@ -498,9 +498,9 @@ export class SnapManager { @computed get outlinesInPageSpace() { return this.snappableShapes.map(({ id, isClosed }) => { - const outline = deepCopy(this.editor.getGeometry(id).vertices) + const outline = deepCopy(this.editor.getShapeGeometry(id).vertices) if (isClosed) outline.push(outline[0]) - const pageTransform = this.editor.getPageTransform(id) + const pageTransform = this.editor.getShapePageTransform(id) if (!pageTransform) throw Error('No page transform') return Matrix2d.applyToPoints(pageTransform, outline) }) diff --git a/packages/editor/src/lib/editor/shapes/ShapeUtil.ts b/packages/editor/src/lib/editor/shapes/ShapeUtil.ts index 454da4ca7..9edd1baaf 100644 --- a/packages/editor/src/lib/editor/shapes/ShapeUtil.ts +++ b/packages/editor/src/lib/editor/shapes/ShapeUtil.ts @@ -208,7 +208,7 @@ export abstract class ShapeUtil { * @public */ getOutlineSegments(shape: Shape): Vec2d[][] { - return [this.editor.getGeometry(shape).vertices] + return [this.editor.getShapeGeometry(shape).vertices] } /** diff --git a/packages/editor/src/lib/editor/shapes/group/GroupShapeUtil.tsx b/packages/editor/src/lib/editor/shapes/group/GroupShapeUtil.tsx index 589f0714c..ecc6f358e 100644 --- a/packages/editor/src/lib/editor/shapes/group/GroupShapeUtil.tsx +++ b/packages/editor/src/lib/editor/shapes/group/GroupShapeUtil.tsx @@ -31,8 +31,8 @@ export class GroupShapeUtil extends ShapeUtil { return new Group2d({ children: children.map((childId) => { const shape = this.editor.getShape(childId)! - const geometry = this.editor.getGeometry(childId) - const points = this.editor.getTransform(shape)!.applyToPoints(geometry.vertices) + const geometry = this.editor.getShapeGeometry(childId) + const points = this.editor.getShapeLocalTransform(shape)!.applyToPoints(geometry.vertices) if (geometry.isClosed) { return new Polygon2d({ @@ -77,7 +77,7 @@ export class GroupShapeUtil extends ShapeUtil { return null } - const bounds = this.editor.getGeometry(shape).bounds + const bounds = this.editor.getShapeGeometry(shape).bounds return ( @@ -92,7 +92,7 @@ export class GroupShapeUtil extends ShapeUtil { camera: { z: zoomLevel }, } = this.editor - const bounds = this.editor.getGeometry(shape).bounds + const bounds = this.editor.getShapeGeometry(shape).bounds return } diff --git a/packages/editor/src/lib/editor/shapes/shared/arrow/curved-arrow.ts b/packages/editor/src/lib/editor/shapes/shared/arrow/curved-arrow.ts index 837e385af..bf26d1904 100644 --- a/packages/editor/src/lib/editor/shapes/shared/arrow/curved-arrow.ts +++ b/packages/editor/src/lib/editor/shapes/shared/arrow/curved-arrow.ts @@ -58,7 +58,7 @@ export function getCurvedArrowInfo(editor: Editor, shape: TLArrowShape, extraBen return getStraightArrowInfo(editor, shape) } - const arrowPageTransform = editor.getPageTransform(shape)! + const arrowPageTransform = editor.getShapePageTransform(shape)! if (startShapeInfo && !startShapeInfo.isExact) { // Points in page space @@ -80,7 +80,7 @@ export function getCurvedArrowInfo(editor: Editor, shape: TLArrowShape, extraBen let intersections = fn( centerInStartShapeLocalSpace, handleArc.radius, - editor.getGeometry(startShapeInfo.shape).vertices + editor.getShapeGeometry(startShapeInfo.shape).vertices ) if (intersections) { @@ -160,7 +160,7 @@ export function getCurvedArrowInfo(editor: Editor, shape: TLArrowShape, extraBen let intersections = fn( centerInEndShapeLocalSpace, handleArc.radius, - editor.getGeometry(endShapeInfo.shape).outerVertices + editor.getShapeGeometry(endShapeInfo.shape).outerVertices ) if (intersections) { diff --git a/packages/editor/src/lib/editor/shapes/shared/arrow/shared.ts b/packages/editor/src/lib/editor/shapes/shared/arrow/shared.ts index c0d76e58c..74e03a77e 100644 --- a/packages/editor/src/lib/editor/shapes/shared/arrow/shared.ts +++ b/packages/editor/src/lib/editor/shapes/shared/arrow/shared.ts @@ -25,8 +25,8 @@ export function getBoundShapeInfoForTerminal( } const shape = editor.getShape(terminal.boundShapeId)! - const transform = editor.getPageTransform(shape)! - const geometry = editor.getGeometry(shape) + const transform = editor.getShapePageTransform(shape)! + const geometry = editor.getShapeGeometry(shape) return { shape, @@ -56,9 +56,9 @@ export function getArrowTerminalInArrowSpace( // Find the actual local point of the normalized terminal on // the bound shape and transform it to page space, then transform // it to arrow space - const { point, size } = editor.getGeometry(boundShape).bounds + const { point, size } = editor.getShapeGeometry(boundShape).bounds const shapePoint = Vec2d.Add(point, Vec2d.MulV(terminal.normalizedAnchor, size)) - const pagePoint = Matrix2d.applyToPoint(editor.getPageTransform(boundShape)!, shapePoint) + const pagePoint = Matrix2d.applyToPoint(editor.getShapePageTransform(boundShape)!, shapePoint) const arrowPoint = Matrix2d.applyToPoint(Matrix2d.Inverse(arrowPageTransform), pagePoint) return arrowPoint } @@ -66,7 +66,7 @@ export function getArrowTerminalInArrowSpace( /** @public */ export function getArrowTerminalsInArrowSpace(editor: Editor, shape: TLArrowShape) { - const arrowPageTransform = editor.getPageTransform(shape)! + const arrowPageTransform = editor.getShapePageTransform(shape)! const start = getArrowTerminalInArrowSpace(editor, arrowPageTransform, shape.props.start) const end = getArrowTerminalInArrowSpace(editor, arrowPageTransform, shape.props.end) diff --git a/packages/editor/src/lib/editor/shapes/shared/arrow/straight-arrow.ts b/packages/editor/src/lib/editor/shapes/shared/arrow/straight-arrow.ts index 9b2a5f621..e21b2cd18 100644 --- a/packages/editor/src/lib/editor/shapes/shared/arrow/straight-arrow.ts +++ b/packages/editor/src/lib/editor/shapes/shared/arrow/straight-arrow.ts @@ -32,7 +32,7 @@ export function getStraightArrowInfo(editor: Editor, shape: TLArrowShape): Arrow const startShapeInfo = getBoundShapeInfoForTerminal(editor, start) const endShapeInfo = getBoundShapeInfoForTerminal(editor, end) - const arrowPageTransform = editor.getPageTransform(shape)! + const arrowPageTransform = editor.getShapePageTransform(shape)! // Update the position of the arrowhead's end point updateArrowheadPointWithBoundShape( diff --git a/packages/editor/src/lib/editor/tools/BaseBoxShapeTool/children/Pointing.ts b/packages/editor/src/lib/editor/tools/BaseBoxShapeTool/children/Pointing.ts index 8c1fb70f3..90a457c88 100644 --- a/packages/editor/src/lib/editor/tools/BaseBoxShapeTool/children/Pointing.ts +++ b/packages/editor/src/lib/editor/tools/BaseBoxShapeTool/children/Pointing.ts @@ -29,8 +29,8 @@ export class Pointing extends StateNode { this.editor.mark(this.markId) - this.editor.createShapes( - [ + this.editor + .createShapes([ { id, type: shapeType, @@ -41,9 +41,8 @@ export class Pointing extends StateNode { h: 1, }, }, - ], - true - ) + ]) + .select(id) this.editor.setCurrentTool('select.resizing', { ...info, target: 'selection', @@ -99,7 +98,7 @@ export class Pointing extends StateNode { const { w, h } = this.editor.getShapeUtil(shape).getDefaultProps() as TLBaseBoxShape['props'] const delta = new Vec2d(w / 2, h / 2) - const parentTransform = this.editor.getParentTransform(shape) + const parentTransform = this.editor.getShapeParentTransform(shape) if (parentTransform) delta.rot(-parentTransform.rotation()) this.editor.updateShapes([ diff --git a/packages/editor/src/lib/hooks/useGestureEvents.ts b/packages/editor/src/lib/hooks/useGestureEvents.ts index 750e16cfa..85cdd4398 100644 --- a/packages/editor/src/lib/hooks/useGestureEvents.ts +++ b/packages/editor/src/lib/hooks/useGestureEvents.ts @@ -69,7 +69,7 @@ export function useGestureEvents(ref: React.RefObject) { if (shape) { const util = editor.getShapeUtil(shape) if (util.canScroll(shape)) { - const bounds = editor.getPageBounds(editor.editingShapeId) + const bounds = editor.getShapePageBounds(editor.editingShapeId) if (bounds?.containsPoint(editor.inputs.currentPagePoint)) { return } diff --git a/packages/editor/src/lib/hooks/useHandleEvents.ts b/packages/editor/src/lib/hooks/useHandleEvents.ts index 525b4a0fa..3fb42597e 100644 --- a/packages/editor/src/lib/hooks/useHandleEvents.ts +++ b/packages/editor/src/lib/hooks/useHandleEvents.ts @@ -7,7 +7,7 @@ import { useEditor } from './useEditor' function getHandle(editor: Editor, id: TLShapeId, handleId: string) { const shape = editor.getShape(id)! - const handles = editor.getHandles(shape)! + const handles = editor.getShapeHandles(shape)! return { shape, handle: handles.find((h) => h.id === handleId) } } diff --git a/packages/editor/src/lib/hooks/useZoomCss.ts b/packages/editor/src/lib/hooks/useZoomCss.ts index 70a1e5539..88664d300 100644 --- a/packages/editor/src/lib/hooks/useZoomCss.ts +++ b/packages/editor/src/lib/hooks/useZoomCss.ts @@ -13,7 +13,7 @@ export function useZoomCss() { const setScaleDebounced = debounce(setScale, 100) const scheduler = new EffectScheduler('useZoomCss', () => { - const numShapes = editor.shapeIdsOnCurrentPage.size + const numShapes = editor.currentPageShapeIds.size if (numShapes < 300) { setScale(editor.zoomLevel) } else { diff --git a/packages/editor/src/lib/utils/rotation.ts b/packages/editor/src/lib/utils/rotation.ts index 5e427a51d..2087c66d6 100644 --- a/packages/editor/src/lib/utils/rotation.ts +++ b/packages/editor/src/lib/utils/rotation.ts @@ -9,7 +9,7 @@ import { Vec2d } from '../primitives/Vec2d' export function getRotationSnapshot({ editor }: { editor: Editor }): TLRotationSnapshot | null { const { selectionRotation, - selectionBounds, + selectionRotatedPageBounds: selectionBounds, inputs: { originPagePoint }, selectedShapes, } = editor @@ -33,7 +33,7 @@ export function getRotationSnapshot({ editor }: { editor: Editor }): TLRotationS initialSelectionRotation: selectionRotation, shapeSnapshots: selectedShapes.map((shape) => ({ shape: structuredClone(shape), - initialPagePoint: editor.getPageTransform(shape.id)!.point(), + initialPagePoint: editor.getShapePageTransform(shape.id)!.point(), })), } } @@ -78,7 +78,7 @@ export function applyRotationToSnapshotShapes({ // around the pivot point (the average center of all rotating shapes.) const parentTransform = isShapeId(shape.parentId) - ? editor.getPageTransform(shape.parentId)! + ? editor.getShapePageTransform(shape.parentId)! : Matrix2d.Identity() const newPagePoint = Vec2d.RotWith(initialPagePoint, selectionPageCenter, delta) diff --git a/packages/tldraw/src/lib/shapes/arrow/ArrowShapeTool.test.ts b/packages/tldraw/src/lib/shapes/arrow/ArrowShapeTool.test.ts index 919879018..59a73e8ec 100644 --- a/packages/tldraw/src/lib/shapes/arrow/ArrowShapeTool.test.ts +++ b/packages/tldraw/src/lib/shapes/arrow/ArrowShapeTool.test.ts @@ -39,9 +39,9 @@ it('enters the arrow state', () => { describe('When in the idle state', () => { it('enters the pointing state and creates a shape on pointer down', () => { - const shapesBefore = editor.shapesOnCurrentPage.length + const shapesBefore = editor.currentPageShapes.length editor.setCurrentTool('arrow').pointerDown(0, 0) - const shapesAfter = editor.shapesOnCurrentPage.length + const shapesAfter = editor.currentPageShapes.length expect(shapesAfter).toBe(shapesBefore + 1) editor.expectPathToBe('root.arrow.pointing') }) @@ -55,18 +55,18 @@ describe('When in the idle state', () => { describe('When in the pointing state', () => { it('cancels on pointer up', () => { - const shapesBefore = editor.shapesOnCurrentPage.length + const shapesBefore = editor.currentPageShapes.length editor.setCurrentTool('arrow').pointerDown(0, 0).pointerUp(0, 0) - const shapesAfter = editor.shapesOnCurrentPage.length + const shapesAfter = editor.currentPageShapes.length expect(shapesAfter).toBe(shapesBefore) expect(editor.hintingShapeIds.length).toBe(0) editor.expectPathToBe('root.arrow.idle') }) it('bails on cancel', () => { - const shapesBefore = editor.shapesOnCurrentPage.length + const shapesBefore = editor.currentPageShapes.length editor.setCurrentTool('arrow').pointerDown(0, 0).cancel() - const shapesAfter = editor.shapesOnCurrentPage.length + const shapesAfter = editor.currentPageShapes.length expect(shapesAfter).toBe(shapesBefore) expect(editor.hintingShapeIds.length).toBe(0) editor.expectPathToBe('root.arrow.idle') @@ -82,7 +82,7 @@ describe('When in the pointing state', () => { describe('When dragging the arrow', () => { it('updates the arrow on pointer move', () => { editor.setCurrentTool('arrow').pointerDown(0, 0).pointerMove(10, 10) - const arrow = editor.shapesOnCurrentPage[editor.shapesOnCurrentPage.length - 1] + const arrow = editor.currentPageShapes[editor.currentPageShapes.length - 1] editor.expectShapeToMatch(arrow, { id: arrow.id, type: 'arrow', @@ -97,9 +97,9 @@ describe('When dragging the arrow', () => { }) it('returns to select.idle, keeping shape, on pointer up', () => { - const shapesBefore = editor.shapesOnCurrentPage.length + const shapesBefore = editor.currentPageShapes.length editor.setCurrentTool('arrow').pointerDown(0, 0).pointerMove(10, 10).pointerUp(10, 10) - const shapesAfter = editor.shapesOnCurrentPage.length + const shapesAfter = editor.currentPageShapes.length expect(shapesAfter).toBe(shapesBefore + 1) expect(editor.hintingShapeIds.length).toBe(0) editor.expectPathToBe('root.select.idle') @@ -107,18 +107,18 @@ describe('When dragging the arrow', () => { it('returns to arrow.idle, keeping shape, on pointer up when tool lock is active', () => { editor.updateInstanceState({ isToolLocked: true }) - const shapesBefore = editor.shapesOnCurrentPage.length + const shapesBefore = editor.currentPageShapes.length editor.setCurrentTool('arrow').pointerDown(0, 0).pointerMove(10, 10).pointerUp(10, 10) - const shapesAfter = editor.shapesOnCurrentPage.length + const shapesAfter = editor.currentPageShapes.length expect(shapesAfter).toBe(shapesBefore + 1) expect(editor.hintingShapeIds.length).toBe(0) editor.expectPathToBe('root.arrow.idle') }) it('bails on cancel', () => { - const shapesBefore = editor.shapesOnCurrentPage.length + const shapesBefore = editor.currentPageShapes.length editor.setCurrentTool('arrow').pointerDown(0, 0).pointerMove(10, 10).cancel() - const shapesAfter = editor.shapesOnCurrentPage.length + const shapesAfter = editor.currentPageShapes.length expect(shapesAfter).toBe(shapesBefore) editor.expectPathToBe('root.arrow.idle') }) @@ -139,7 +139,7 @@ describe('When pointing a start shape', () => { // Clear hinting ids when moving away expect(editor.hintingShapeIds.length).toBe(0) - const arrow = editor.shapesOnCurrentPage[editor.shapesOnCurrentPage.length - 1] + const arrow = editor.currentPageShapes[editor.currentPageShapes.length - 1] editor.expectShapeToMatch(arrow, { id: arrow.id, type: 'arrow', @@ -179,7 +179,7 @@ describe('When pointing an end shape', () => { // Set hinting id when pointing the shape expect(editor.hintingShapeIds.length).toBe(1) - const arrow = editor.shapesOnCurrentPage[editor.shapesOnCurrentPage.length - 1] + const arrow = editor.currentPageShapes[editor.currentPageShapes.length - 1] editor.expectShapeToMatch(arrow, { id: arrow.id, type: 'arrow', @@ -208,7 +208,7 @@ describe('When pointing an end shape', () => { editor.pointerMove(375, 375) - let arrow = editor.shapesOnCurrentPage[editor.shapesOnCurrentPage.length - 1] + let arrow = editor.currentPageShapes[editor.currentPageShapes.length - 1] expect(editor.hintingShapeIds.length).toBe(1) @@ -230,7 +230,7 @@ describe('When pointing an end shape', () => { jest.advanceTimersByTime(1000) - arrow = editor.shapesOnCurrentPage[editor.shapesOnCurrentPage.length - 1] + arrow = editor.currentPageShapes[editor.currentPageShapes.length - 1] editor.expectShapeToMatch(arrow, { id: arrow.id, @@ -250,7 +250,7 @@ describe('When pointing an end shape', () => { editor.pointerMove(375, 0) expect(editor.hintingShapeIds.length).toBe(0) - arrow = editor.shapesOnCurrentPage[editor.shapesOnCurrentPage.length - 1] + arrow = editor.currentPageShapes[editor.currentPageShapes.length - 1] editor.expectShapeToMatch(arrow, { id: arrow.id, @@ -268,7 +268,7 @@ describe('When pointing an end shape', () => { editor.pointerMove(325, 325) expect(editor.hintingShapeIds.length).toBe(1) - arrow = editor.shapesOnCurrentPage[editor.shapesOnCurrentPage.length - 1] + arrow = editor.currentPageShapes[editor.currentPageShapes.length - 1] editor.expectShapeToMatch(arrow, { id: arrow.id, @@ -289,7 +289,7 @@ describe('When pointing an end shape', () => { // Give time for the velocity to die down jest.advanceTimersByTime(1000) - arrow = editor.shapesOnCurrentPage[editor.shapesOnCurrentPage.length - 1] + arrow = editor.currentPageShapes[editor.currentPageShapes.length - 1] editor.expectShapeToMatch(arrow, { id: arrow.id, @@ -316,7 +316,7 @@ describe('When pointing an end shape', () => { editor.inputs.pointerVelocity = new Vec2d(1, 1) editor.pointerMove(370, 370) - const arrow = editor.shapesOnCurrentPage[editor.shapesOnCurrentPage.length - 1] + const arrow = editor.currentPageShapes[editor.currentPageShapes.length - 1] expect(editor.hintingShapeIds.length).toBe(1) @@ -340,7 +340,7 @@ describe('When pointing an end shape', () => { it('begins precise when moving slowly', () => { editor.setCurrentTool('arrow').pointerDown(0, 0) - let arrow = editor.shapesOnCurrentPage[editor.shapesOnCurrentPage.length - 1] + let arrow = editor.currentPageShapes[editor.currentPageShapes.length - 1] editor.expectShapeToMatch(arrow, { id: arrow.id, @@ -358,7 +358,7 @@ describe('When pointing an end shape', () => { editor.inputs.pointerVelocity = new Vec2d(0.001, 0.001) editor.pointerMove(375, 375) - arrow = editor.shapesOnCurrentPage[editor.shapesOnCurrentPage.length - 1] + arrow = editor.currentPageShapes[editor.currentPageShapes.length - 1] expect(editor.hintingShapeIds.length).toBe(1) @@ -390,7 +390,7 @@ describe('reparenting issue', () => { editor.pointerMove(100, 100) editor.pointerUp() - const arrowId = editor.sortedShapesOnCurrentPage[0].id + const arrowId = editor.currentPageShapesSorted[0].id // Now create three shapes editor.createShapes([ diff --git a/packages/tldraw/src/lib/shapes/arrow/ArrowShapeUtil.test.ts b/packages/tldraw/src/lib/shapes/arrow/ArrowShapeUtil.test.ts index 5f731152d..3da07adfa 100644 --- a/packages/tldraw/src/lib/shapes/arrow/ArrowShapeUtil.test.ts +++ b/packages/tldraw/src/lib/shapes/arrow/ArrowShapeUtil.test.ts @@ -302,7 +302,7 @@ describe('Other cases when arrow are moved', () => { .groupShapes(editor.selectedShapeIds) editor.setCurrentTool('arrow').pointerDown(1000, 1000).pointerMove(50, 350).pointerUp(50, 350) - let arrow = editor.shapesOnCurrentPage[editor.shapesOnCurrentPage.length - 1] + let arrow = editor.currentPageShapes[editor.currentPageShapes.length - 1] assert(editor.isShapeOfType(arrow, 'arrow')) assert(arrow.props.end.type === 'binding') expect(arrow.props.end.boundShapeId).toBe(ids.box3) @@ -322,7 +322,7 @@ describe('When a shape it rotated', () => { it('binds correctly', () => { editor.setCurrentTool('arrow').pointerDown(0, 0).pointerMove(375, 375) - const arrow = editor.shapesOnCurrentPage[editor.shapesOnCurrentPage.length - 1] + const arrow = editor.currentPageShapes[editor.currentPageShapes.length - 1] expect(editor.getShape(arrow.id)).toMatchObject({ props: { @@ -371,8 +371,8 @@ describe('resizing', () => { .pointerUp() .setCurrentTool('select') - const arrow1 = editor.shapesOnCurrentPage.at(-2)! - const arrow2 = editor.shapesOnCurrentPage.at(-1)! + const arrow1 = editor.currentPageShapes.at(-2)! + const arrow2 = editor.currentPageShapes.at(-1)! editor .select(arrow1.id, arrow2.id) @@ -426,8 +426,8 @@ describe('resizing', () => { .pointerUp() .setCurrentTool('select') - const arrow1 = editor.shapesOnCurrentPage.at(-2)! - const arrow2 = editor.shapesOnCurrentPage.at(-1)! + const arrow1 = editor.currentPageShapes.at(-2)! + const arrow2 = editor.currentPageShapes.at(-1)! editor.updateShapes([{ id: arrow1.id, type: 'arrow', props: { bend: 50 } }]) diff --git a/packages/tldraw/src/lib/shapes/arrow/ArrowShapeUtil.tsx b/packages/tldraw/src/lib/shapes/arrow/ArrowShapeUtil.tsx index d1d68cf47..39f08825c 100644 --- a/packages/tldraw/src/lib/shapes/arrow/ArrowShapeUtil.tsx +++ b/packages/tldraw/src/lib/shapes/arrow/ArrowShapeUtil.tsx @@ -223,7 +223,7 @@ export class ArrowShapeUtil extends ShapeUtil { const next = deepCopy(shape) as TLArrowShape - const pageTransform = this.editor.getPageTransform(next.id)! + const pageTransform = this.editor.getShapePageTransform(next.id)! const pointInPageSpace = pageTransform.applyToPoint(handle) if (this.editor.inputs.ctrlKey) { @@ -237,7 +237,7 @@ export class ArrowShapeUtil extends ShapeUtil { return next } - const point = this.editor.getPageTransform(shape.id)!.applyToPoint(handle) + const point = this.editor.getShapePageTransform(shape.id)!.applyToPoint(handle) const target = this.editor.getShapeAtPoint(point, { filter: (shape) => this.editor.getShapeUtil(shape).canBind(shape), @@ -258,7 +258,7 @@ export class ArrowShapeUtil extends ShapeUtil { // we've got a target! the handle is being dragged over a shape, bind to it - const targetGeometry = this.editor.getGeometry(target) + const targetGeometry = this.editor.getShapeGeometry(target) const targetBounds = targetGeometry.bounds const pointInTargetSpace = this.editor.getPointInShapeSpace(target, pointInPageSpace) @@ -491,7 +491,7 @@ export class ArrowShapeUtil extends ShapeUtil { ) && !this.editor.instanceState.isReadonly const info = this.editor.getArrowInfo(shape) - const bounds = this.editor.getGeometry(shape).bounds + const bounds = this.editor.getShapeGeometry(shape).bounds // eslint-disable-next-line react-hooks/rules-of-hooks const changeIndex = React.useMemo(() => { @@ -564,7 +564,7 @@ export class ArrowShapeUtil extends ShapeUtil { ) const labelGeometry = shape.props.text.trim() - ? (this.editor.getGeometry(shape).children[1] as Rectangle2d) + ? (this.editor.getShapeGeometry(shape).children[1] as Rectangle2d) : null const maskStartArrowhead = !( @@ -667,7 +667,7 @@ export class ArrowShapeUtil extends ShapeUtil { const { start, end } = getArrowTerminalsInArrowSpace(this.editor, shape) const info = this.editor.getArrowInfo(shape) - const geometry = this.editor.getGeometry(shape) + const geometry = this.editor.getShapeGeometry(shape) const bounds = geometry.bounds const labelGeometry = shape.props.text.trim() ? (geometry.children[1] as Rectangle2d) : null @@ -799,7 +799,7 @@ export class ArrowShapeUtil extends ShapeUtil { // Arrowhead end path const ae = info.end.arrowhead && getArrowheadPathForType(info, 'end', strokeWidth) - const geometry = this.editor.getGeometry(shape) + const geometry = this.editor.getShapeGeometry(shape) const bounds = geometry.bounds const labelGeometry = shape.props.text.trim() ? (geometry.children[1] as Rectangle2d) : null diff --git a/packages/tldraw/src/lib/shapes/arrow/toolStates/Pointing.ts b/packages/tldraw/src/lib/shapes/arrow/toolStates/Pointing.ts index 73de24bea..60cf7e9de 100644 --- a/packages/tldraw/src/lib/shapes/arrow/toolStates/Pointing.ts +++ b/packages/tldraw/src/lib/shapes/arrow/toolStates/Pointing.ts @@ -43,7 +43,7 @@ export class Pointing extends StateNode { this.editor.setCurrentTool('select.dragging_handle', { shape: this.shape, - handle: this.editor.getHandles(this.shape)!.find((h) => h.id === 'end')!, + handle: this.editor.getShapeHandles(this.shape)!.find((h) => h.id === 'end')!, isCreating: true, onInteractionEnd: 'arrow', }) @@ -94,7 +94,7 @@ export class Pointing extends StateNode { const shape = this.editor.getShape(id) if (!shape) throw Error(`expected shape`) - const handles = this.editor.getHandles(shape) + const handles = this.editor.getShapeHandles(shape) if (!handles) throw Error(`expected handles for arrow`) const util = this.editor.getShapeUtil('arrow') @@ -121,7 +121,7 @@ export class Pointing extends StateNode { const shape = this.shape if (!shape) throw Error(`expected shape`) - const handles = this.editor.getHandles(shape) + const handles = this.editor.getShapeHandles(shape) if (!handles) throw Error(`expected handles for arrow`) // end update diff --git a/packages/tldraw/src/lib/shapes/bookmark/BookmarkShapeUtil.tsx b/packages/tldraw/src/lib/shapes/bookmark/BookmarkShapeUtil.tsx index 2cbbadb17..b744ef7bb 100644 --- a/packages/tldraw/src/lib/shapes/bookmark/BookmarkShapeUtil.tsx +++ b/packages/tldraw/src/lib/shapes/bookmark/BookmarkShapeUtil.tsx @@ -44,7 +44,7 @@ export class BookmarkShapeUtil extends BaseBoxShapeUtil { shape.props.assetId ? this.editor.getAsset(shape.props.assetId) : null ) as TLBookmarkAsset - const pageRotation = this.editor.getPageTransform(shape)!.rotation() + const pageRotation = this.editor.getShapePageTransform(shape)!.rotation() const address = getHumanReadableAddress(shape) diff --git a/packages/tldraw/src/lib/shapes/draw/toolStates/Drawing.ts b/packages/tldraw/src/lib/shapes/draw/toolStates/Drawing.ts index 6eb50a98d..00e46cf42 100644 --- a/packages/tldraw/src/lib/shapes/draw/toolStates/Drawing.ts +++ b/packages/tldraw/src/lib/shapes/draw/toolStates/Drawing.ts @@ -210,7 +210,7 @@ export class Drawing extends StateNode { // Convert prevPoint to page space const prevPointPageSpace = Matrix2d.applyToPoint( - this.editor.getPageTransform(shape.id)!, + this.editor.getShapePageTransform(shape.id)!, prevPoint ) this.pagePointWhereCurrentSegmentChanged = prevPointPageSpace @@ -336,7 +336,7 @@ export class Drawing extends StateNode { points: [{ ...prevLastPoint }, newLastPoint], } - const transform = this.editor.getPageTransform(shape)! + const transform = this.editor.getShapePageTransform(shape)! this.pagePointWhereCurrentSegmentChanged = Matrix2d.applyToPoint( transform, @@ -500,7 +500,7 @@ export class Drawing extends StateNode { } if (didSnap && snapSegment) { - const transform = this.editor.getPageTransform(shape)! + const transform = this.editor.getShapePageTransform(shape)! const first = snapSegment.points[0] const lastPoint = last(snapSegment.points) if (!lastPoint) throw Error('Expected a last point!') diff --git a/packages/tldraw/src/lib/shapes/embed/EmbedShapeUtil.tsx b/packages/tldraw/src/lib/shapes/embed/EmbedShapeUtil.tsx index 961d508f5..53b07cacc 100644 --- a/packages/tldraw/src/lib/shapes/embed/EmbedShapeUtil.tsx +++ b/packages/tldraw/src/lib/shapes/embed/EmbedShapeUtil.tsx @@ -97,7 +97,7 @@ export class EmbedShapeUtil extends BaseBoxShapeUtil { [] ) - const pageRotation = this.editor.getPageTransform(shape)!.rotation() + const pageRotation = this.editor.getShapePageTransform(shape)!.rotation() const isInteractive = isEditing || isHoveringWhileEditingSameShape diff --git a/packages/tldraw/src/lib/shapes/frame/FrameShapeTool.test.ts b/packages/tldraw/src/lib/shapes/frame/FrameShapeTool.test.ts index 2f6769d59..7bd118f33 100644 --- a/packages/tldraw/src/lib/shapes/frame/FrameShapeTool.test.ts +++ b/packages/tldraw/src/lib/shapes/frame/FrameShapeTool.test.ts @@ -12,44 +12,44 @@ afterEach(() => { describe(FrameShapeTool, () => { it('Creates frame shapes on click-and-drag, supports undo and redo', () => { - expect(editor.shapesOnCurrentPage.length).toBe(0) + expect(editor.currentPageShapes.length).toBe(0) editor.setCurrentTool('frame') editor.pointerDown(50, 50) editor.pointerMove(100, 100) editor.pointerUp(100, 100) - expect(editor.shapesOnCurrentPage.length).toBe(1) - expect(editor.shapesOnCurrentPage[0]?.type).toBe('frame') - expect(editor.selectedShapeIds[0]).toBe(editor.shapesOnCurrentPage[0]?.id) + expect(editor.currentPageShapes.length).toBe(1) + expect(editor.currentPageShapes[0]?.type).toBe('frame') + expect(editor.selectedShapeIds[0]).toBe(editor.currentPageShapes[0]?.id) editor.undo() - expect(editor.shapesOnCurrentPage.length).toBe(0) + expect(editor.currentPageShapes.length).toBe(0) editor.redo() - expect(editor.shapesOnCurrentPage.length).toBe(1) + expect(editor.currentPageShapes.length).toBe(1) }) it('Creates frame shapes on click, supports undo and redo', () => { - expect(editor.shapesOnCurrentPage.length).toBe(0) + expect(editor.currentPageShapes.length).toBe(0) editor.setCurrentTool('frame') editor.pointerDown(50, 50) editor.pointerUp(50, 50) - expect(editor.shapesOnCurrentPage.length).toBe(1) - expect(editor.shapesOnCurrentPage[0]?.type).toBe('frame') - expect(editor.selectedShapeIds[0]).toBe(editor.shapesOnCurrentPage[0]?.id) + expect(editor.currentPageShapes.length).toBe(1) + expect(editor.currentPageShapes[0]?.type).toBe('frame') + expect(editor.selectedShapeIds[0]).toBe(editor.currentPageShapes[0]?.id) editor.undo() - expect(editor.shapesOnCurrentPage.length).toBe(0) + expect(editor.currentPageShapes.length).toBe(0) editor.redo() - expect(editor.shapesOnCurrentPage.length).toBe(1) + expect(editor.currentPageShapes.length).toBe(1) }) }) @@ -114,22 +114,22 @@ describe('When in the pointing state', () => { }) it('Creates a frame and returns to select tool on pointer up', () => { - expect(editor.shapesOnCurrentPage.length).toBe(0) + expect(editor.currentPageShapes.length).toBe(0) editor.setCurrentTool('frame') editor.pointerDown(50, 50) editor.pointerUp(50, 50) editor.expectPathToBe('root.select.idle') - expect(editor.shapesOnCurrentPage.length).toBe(1) + expect(editor.currentPageShapes.length).toBe(1) }) it('Creates a frame and returns to frame.idle on pointer up if tool lock is enabled', () => { editor.updateInstanceState({ isToolLocked: true }) - expect(editor.shapesOnCurrentPage.length).toBe(0) + expect(editor.currentPageShapes.length).toBe(0) editor.setCurrentTool('frame') editor.pointerDown(50, 50) editor.pointerUp(50, 50) editor.expectPathToBe('root.frame.idle') - expect(editor.shapesOnCurrentPage.length).toBe(1) + expect(editor.currentPageShapes.length).toBe(1) }) }) diff --git a/packages/tldraw/src/lib/shapes/frame/FrameShapeUtil.tsx b/packages/tldraw/src/lib/shapes/frame/FrameShapeUtil.tsx index 0d2bf41b4..51c93ac73 100644 --- a/packages/tldraw/src/lib/shapes/frame/FrameShapeUtil.tsx +++ b/packages/tldraw/src/lib/shapes/frame/FrameShapeUtil.tsx @@ -50,7 +50,7 @@ export class FrameShapeUtil extends BaseBoxShapeUtil { } override component(shape: TLFrameShape) { - const bounds = this.editor.getGeometry(shape).bounds + const bounds = this.editor.getShapeGeometry(shape).bounds // eslint-disable-next-line react-hooks/rules-of-hooks const theme = useDefaultColorTheme() @@ -90,7 +90,9 @@ export class FrameShapeUtil extends BaseBoxShapeUtil { g.appendChild(rect) // Text label - const pageRotation = canonicalizeRotation(this.editor.getPageTransform(shape.id)!.rotation()) + const pageRotation = canonicalizeRotation( + this.editor.getShapePageTransform(shape.id)!.rotation() + ) // rotate right 45 deg const offsetRotation = pageRotation + Math.PI / 4 const scaledRotation = (offsetRotation * (2 / Math.PI) + 4) % 4 @@ -163,7 +165,7 @@ export class FrameShapeUtil extends BaseBoxShapeUtil { } indicator(shape: TLFrameShape) { - const bounds = this.editor.getGeometry(shape).bounds + const bounds = this.editor.getShapeGeometry(shape).bounds return ( { } override onResizeEnd: TLOnResizeEndHandler = (shape) => { - const bounds = this.editor.getPageBounds(shape)! + const bounds = this.editor.getShapePageBounds(shape)! const children = this.editor.getSortedChildIdsForParent(shape.id) const shapesToReparent: TLShapeId[] = [] for (const childId of children) { - const childBounds = this.editor.getPageBounds(childId)! + const childBounds = this.editor.getShapePageBounds(childId)! if (!bounds.includes(childBounds)) { shapesToReparent.push(childId) } diff --git a/packages/tldraw/src/lib/shapes/frame/components/FrameHeading.tsx b/packages/tldraw/src/lib/shapes/frame/components/FrameHeading.tsx index 1bf5d6726..47932227d 100644 --- a/packages/tldraw/src/lib/shapes/frame/components/FrameHeading.tsx +++ b/packages/tldraw/src/lib/shapes/frame/components/FrameHeading.tsx @@ -23,7 +23,7 @@ export const FrameHeading = function FrameHeading({ }) { const editor = useEditor() - const pageRotation = canonicalizeRotation(editor.getPageTransform(id)!.rotation()) + const pageRotation = canonicalizeRotation(editor.getShapePageTransform(id)!.rotation()) const isEditing = useIsEditing(id) const rInput = useRef(null) diff --git a/packages/tldraw/src/lib/shapes/geo/GeoShapeTool.test.ts b/packages/tldraw/src/lib/shapes/geo/GeoShapeTool.test.ts index f5ebb2adc..2c5ddd88a 100644 --- a/packages/tldraw/src/lib/shapes/geo/GeoShapeTool.test.ts +++ b/packages/tldraw/src/lib/shapes/geo/GeoShapeTool.test.ts @@ -12,44 +12,44 @@ afterEach(() => { describe(GeoShapeTool, () => { it('Creates geo shapes on click-and-drag, supports undo and redo', () => { - expect(editor.shapesOnCurrentPage.length).toBe(0) + expect(editor.currentPageShapes.length).toBe(0) editor.setCurrentTool('geo') editor.pointerDown(50, 50) editor.pointerMove(100, 100) editor.pointerUp() - expect(editor.shapesOnCurrentPage.length).toBe(1) - expect(editor.shapesOnCurrentPage[0]?.type).toBe('geo') - expect(editor.selectedShapeIds[0]).toBe(editor.shapesOnCurrentPage[0]?.id) + expect(editor.currentPageShapes.length).toBe(1) + expect(editor.currentPageShapes[0]?.type).toBe('geo') + expect(editor.selectedShapeIds[0]).toBe(editor.currentPageShapes[0]?.id) editor.undo() - expect(editor.shapesOnCurrentPage.length).toBe(0) + expect(editor.currentPageShapes.length).toBe(0) editor.redo() - expect(editor.shapesOnCurrentPage.length).toBe(1) + expect(editor.currentPageShapes.length).toBe(1) }) it('Creates geo shapes on click, supports undo and redo', () => { - expect(editor.shapesOnCurrentPage.length).toBe(0) + expect(editor.currentPageShapes.length).toBe(0) editor.setCurrentTool('geo') editor.pointerDown(50, 50) editor.pointerUp(50, 50) - expect(editor.shapesOnCurrentPage.length).toBe(1) - expect(editor.shapesOnCurrentPage[0]?.type).toBe('geo') - expect(editor.selectedShapeIds[0]).toBe(editor.shapesOnCurrentPage[0]?.id) + expect(editor.currentPageShapes.length).toBe(1) + expect(editor.currentPageShapes[0]?.type).toBe('geo') + expect(editor.selectedShapeIds[0]).toBe(editor.currentPageShapes[0]?.id) editor.undo() - expect(editor.shapesOnCurrentPage.length).toBe(0) + expect(editor.currentPageShapes.length).toBe(0) editor.redo() - expect(editor.shapesOnCurrentPage.length).toBe(1) + expect(editor.currentPageShapes.length).toBe(1) }) }) @@ -100,7 +100,7 @@ describe('When in the idle state', () => { editor.pointerMove(200, 200) editor.pointerUp(200, 200) - expect(editor.shapesOnCurrentPage.length).toBe(2) + expect(editor.currentPageShapes.length).toBe(2) editor.selectAll() expect(editor.selectedShapes.length).toBe(2) @@ -143,22 +143,22 @@ describe('When in the pointing state', () => { }) it('Creates a geo and returns to select tool on pointer up', () => { - expect(editor.shapesOnCurrentPage.length).toBe(0) + expect(editor.currentPageShapes.length).toBe(0) editor.setCurrentTool('geo') editor.pointerDown(50, 50) editor.pointerUp(50, 50) editor.expectPathToBe('root.select.idle') - expect(editor.shapesOnCurrentPage.length).toBe(1) + expect(editor.currentPageShapes.length).toBe(1) }) it('Creates a geo and returns to geo.idle on pointer up if tool lock is enabled', () => { editor.updateInstanceState({ isToolLocked: true }) - expect(editor.shapesOnCurrentPage.length).toBe(0) + expect(editor.currentPageShapes.length).toBe(0) editor.setCurrentTool('geo') editor.pointerDown(50, 50) editor.pointerUp(50, 50) editor.expectPathToBe('root.geo.idle') - expect(editor.shapesOnCurrentPage.length).toBe(1) + expect(editor.currentPageShapes.length).toBe(1) }) }) diff --git a/packages/tldraw/src/lib/shapes/geo/GeoShapeUtil.tsx b/packages/tldraw/src/lib/shapes/geo/GeoShapeUtil.tsx index 7ea6b9198..702322b8c 100644 --- a/packages/tldraw/src/lib/shapes/geo/GeoShapeUtil.tsx +++ b/packages/tldraw/src/lib/shapes/geo/GeoShapeUtil.tsx @@ -467,7 +467,7 @@ export class GeoShapeUtil extends BaseBoxShapeUtil { break } default: { - const geometry = this.editor.getGeometry(shape) + const geometry = this.editor.getShapeGeometry(shape) const outline = geometry instanceof Group2d ? geometry.children[0].vertices : geometry.vertices const lines = getLines(shape.props, strokeWidth) @@ -554,7 +554,7 @@ export class GeoShapeUtil extends BaseBoxShapeUtil { } default: { - const geometry = this.editor.getGeometry(shape) + const geometry = this.editor.getShapeGeometry(shape) const outline = geometry instanceof Group2d ? geometry.children[0].vertices : geometry.vertices let path: string @@ -714,7 +714,7 @@ export class GeoShapeUtil extends BaseBoxShapeUtil { break } default: { - const geometry = this.editor.getGeometry(shape) + const geometry = this.editor.getShapeGeometry(shape) const outline = geometry instanceof Group2d ? geometry.children[0].vertices : geometry.vertices const lines = getLines(shape.props, strokeWidth) @@ -760,7 +760,7 @@ export class GeoShapeUtil extends BaseBoxShapeUtil { } if (props.text) { - const bounds = this.editor.getGeometry(shape).bounds + const bounds = this.editor.getShapeGeometry(shape).bounds ctx.addExportDef(getFontDefForExport(shape.props.font)) diff --git a/packages/tldraw/src/lib/shapes/geo/toolStates/Pointing.ts b/packages/tldraw/src/lib/shapes/geo/toolStates/Pointing.ts index 5e2a7d1d6..a328550e7 100644 --- a/packages/tldraw/src/lib/shapes/geo/toolStates/Pointing.ts +++ b/packages/tldraw/src/lib/shapes/geo/toolStates/Pointing.ts @@ -99,7 +99,7 @@ export class Pointing extends StateNode { : new Box2d(0, 0, 200, 200) const delta = bounds.center - const parentTransform = this.editor.getParentTransform(shape) + const parentTransform = this.editor.getShapeParentTransform(shape) if (parentTransform) delta.rot(-parentTransform.rotation()) this.editor.select(id) diff --git a/packages/tldraw/src/lib/shapes/line/LineShapeTool.test.ts b/packages/tldraw/src/lib/shapes/line/LineShapeTool.test.ts index 668a1ad6f..b3924b24d 100644 --- a/packages/tldraw/src/lib/shapes/line/LineShapeTool.test.ts +++ b/packages/tldraw/src/lib/shapes/line/LineShapeTool.test.ts @@ -15,9 +15,9 @@ it('enters the line state', () => { describe('When in the idle state', () => { it('enters the pointing state and creates a shape on pointer down', () => { - const shapesBefore = editor.shapesOnCurrentPage.length + const shapesBefore = editor.currentPageShapes.length editor.setCurrentTool('line').pointerDown(0, 0, { target: 'canvas' }) - const shapesAfter = editor.shapesOnCurrentPage.length + const shapesAfter = editor.currentPageShapes.length expect(shapesAfter).toBe(shapesBefore + 1) editor.expectPathToBe('root.line.pointing') }) @@ -31,18 +31,18 @@ describe('When in the idle state', () => { describe('When in the pointing state', () => { it('createes on pointer up', () => { - const shapesBefore = editor.shapesOnCurrentPage.length + const shapesBefore = editor.currentPageShapes.length editor.setCurrentTool('line').pointerDown(0, 0, { target: 'canvas' }).pointerUp(0, 0) - const shapesAfter = editor.shapesOnCurrentPage.length + const shapesAfter = editor.currentPageShapes.length expect(shapesAfter).toBe(shapesBefore + 1) expect(editor.hintingShapeIds.length).toBe(0) editor.expectPathToBe('root.line.idle') }) it('bails on cancel', () => { - const shapesBefore = editor.shapesOnCurrentPage.length + const shapesBefore = editor.currentPageShapes.length editor.setCurrentTool('line').pointerDown(0, 0, { target: 'canvas' }).cancel() - const shapesAfter = editor.shapesOnCurrentPage.length + const shapesAfter = editor.currentPageShapes.length expect(shapesAfter).toBe(shapesBefore) expect(editor.hintingShapeIds.length).toBe(0) editor.expectPathToBe('root.line.idle') @@ -58,7 +58,7 @@ describe('When in the pointing state', () => { describe('When dragging the line', () => { it('updates the line on pointer move', () => { editor.setCurrentTool('line').pointerDown(0, 0, { target: 'canvas' }).pointerMove(10, 10) - const line = editor.shapesOnCurrentPage[editor.shapesOnCurrentPage.length - 1] + const line = editor.currentPageShapes[editor.currentPageShapes.length - 1] editor.expectShapeToMatch(line, { id: line.id, type: 'line', @@ -75,13 +75,13 @@ describe('When dragging the line', () => { }) it('returns to select.idle, keeping shape, on pointer up', () => { - const shapesBefore = editor.shapesOnCurrentPage.length + const shapesBefore = editor.currentPageShapes.length editor .setCurrentTool('line') .pointerDown(0, 0, { target: 'canvas' }) .pointerMove(10, 10) .pointerUp(10, 10) - const shapesAfter = editor.shapesOnCurrentPage.length + const shapesAfter = editor.currentPageShapes.length expect(shapesAfter).toBe(shapesBefore + 1) expect(editor.hintingShapeIds.length).toBe(0) editor.expectPathToBe('root.select.idle') @@ -89,26 +89,26 @@ describe('When dragging the line', () => { it('returns to line.idle, keeping shape, on pointer up if tool lock is enabled', () => { editor.updateInstanceState({ isToolLocked: true }) - const shapesBefore = editor.shapesOnCurrentPage.length + const shapesBefore = editor.currentPageShapes.length editor .setCurrentTool('line') .pointerDown(0, 0, { target: 'canvas' }) .pointerMove(10, 10) .pointerUp(10, 10) - const shapesAfter = editor.shapesOnCurrentPage.length + const shapesAfter = editor.currentPageShapes.length expect(shapesAfter).toBe(shapesBefore + 1) expect(editor.hintingShapeIds.length).toBe(0) editor.expectPathToBe('root.line.idle') }) it('bails on cancel', () => { - const shapesBefore = editor.shapesOnCurrentPage.length + const shapesBefore = editor.currentPageShapes.length editor .setCurrentTool('line') .pointerDown(0, 0, { target: 'canvas' }) .pointerMove(10, 10) .cancel() - const shapesAfter = editor.shapesOnCurrentPage.length + const shapesAfter = editor.currentPageShapes.length expect(shapesAfter).toBe(shapesBefore) editor.expectPathToBe('root.line.idle') }) @@ -126,7 +126,7 @@ describe('When extending the line with the shift-key in tool-lock mode', () => { .pointerDown(20, 10, { target: 'canvas' }) .pointerUp(20, 10) - const line = editor.shapesOnCurrentPage[editor.shapesOnCurrentPage.length - 1] + const line = editor.currentPageShapes[editor.currentPageShapes.length - 1] assert(editor.isShapeOfType(line, 'line')) const handles = Object.values(line.props.handles) expect(handles.length).toBe(3) @@ -143,7 +143,7 @@ describe('When extending the line with the shift-key in tool-lock mode', () => { .pointerMove(30, 10) .pointerUp(30, 10) - const line = editor.shapesOnCurrentPage[editor.shapesOnCurrentPage.length - 1] + const line = editor.currentPageShapes[editor.currentPageShapes.length - 1] assert(editor.isShapeOfType(line, 'line')) const handles = Object.values(line.props.handles) expect(handles.length).toBe(3) @@ -161,7 +161,7 @@ describe('When extending the line with the shift-key in tool-lock mode', () => { .pointerMove(30, 10) .pointerUp(30, 10) - const line = editor.shapesOnCurrentPage[editor.shapesOnCurrentPage.length - 1] + const line = editor.currentPageShapes[editor.currentPageShapes.length - 1] assert(editor.isShapeOfType(line, 'line')) const handles = Object.values(line.props.handles) expect(handles.length).toBe(3) @@ -181,7 +181,7 @@ describe('When extending the line with the shift-key in tool-lock mode', () => { .pointerMove(30, 10) .pointerUp(30, 10) - const line = editor.shapesOnCurrentPage[editor.shapesOnCurrentPage.length - 1] + const line = editor.currentPageShapes[editor.currentPageShapes.length - 1] assert(editor.isShapeOfType(line, 'line')) const handles = Object.values(line.props.handles) expect(handles.length).toBe(3) @@ -203,7 +203,7 @@ describe('When extending the line with the shift-key in tool-lock mode', () => { .pointerMove(40, 10) .pointerUp(40, 10) - const line = editor.shapesOnCurrentPage[editor.shapesOnCurrentPage.length - 1] + const line = editor.currentPageShapes[editor.currentPageShapes.length - 1] assert(editor.isShapeOfType(line, 'line')) const handles = Object.values(line.props.handles) expect(handles.length).toBe(3) diff --git a/packages/tldraw/src/lib/shapes/line/LineShapeUtil.test.ts b/packages/tldraw/src/lib/shapes/line/LineShapeUtil.test.ts index ecbe88ebf..f3166600d 100644 --- a/packages/tldraw/src/lib/shapes/line/LineShapeUtil.test.ts +++ b/packages/tldraw/src/lib/shapes/line/LineShapeUtil.test.ts @@ -175,15 +175,15 @@ describe('Misc', () => { editor.select(boxID, id) - expect(editor.getPageBounds(box)!.maxX).not.toEqual(editor.getPageBounds(line)!.maxX) + expect(editor.getShapePageBounds(box)!.maxX).not.toEqual(editor.getShapePageBounds(line)!.maxX) editor.alignShapes(editor.selectedShapeIds, 'right') jest.advanceTimersByTime(1000) - expect(editor.getPageBounds(box)!.maxX).toEqual(editor.getPageBounds(line)!.maxX) + expect(editor.getShapePageBounds(box)!.maxX).toEqual(editor.getShapePageBounds(line)!.maxX) - expect(editor.getPageBounds(box)!.maxY).not.toEqual(editor.getPageBounds(line)!.maxY) + expect(editor.getShapePageBounds(box)!.maxY).not.toEqual(editor.getShapePageBounds(line)!.maxY) editor.alignShapes(editor.selectedShapeIds, 'bottom') jest.advanceTimersByTime(1000) - expect(editor.getPageBounds(box)!.maxY).toEqual(editor.getPageBounds(line)!.maxY) + expect(editor.getShapePageBounds(box)!.maxY).toEqual(editor.getShapePageBounds(line)!.maxY) }) it('duplicates', () => { @@ -195,7 +195,7 @@ describe('Misc', () => { editor.pointerMove(50, 50) // Move shape by 25, 25 editor.pointerUp().keyUp('Alt') - expect(Array.from(editor.shapeIdsOnCurrentPage.values()).length).toEqual(2) + expect(Array.from(editor.currentPageShapeIds.values()).length).toEqual(2) }) it('deletes', () => { @@ -207,7 +207,7 @@ describe('Misc', () => { editor.pointerMove(50, 50) // Move shape by 25, 25 editor.pointerUp().keyUp('Alt') - let ids = Array.from(editor.shapeIdsOnCurrentPage.values()) + let ids = Array.from(editor.currentPageShapeIds.values()) expect(ids.length).toEqual(2) const duplicate = ids.filter((i) => i !== id)[0] @@ -215,7 +215,7 @@ describe('Misc', () => { editor.deleteShapes(editor.selectedShapeIds) - ids = Array.from(editor.shapeIdsOnCurrentPage.values()) + ids = Array.from(editor.currentPageShapeIds.values()) expect(ids.length).toEqual(1) expect(ids[0]).toEqual(id) }) diff --git a/packages/tldraw/src/lib/shapes/line/LineShapeUtil.tsx b/packages/tldraw/src/lib/shapes/line/LineShapeUtil.tsx index 596a60b2b..1ed664de7 100644 --- a/packages/tldraw/src/lib/shapes/line/LineShapeUtil.tsx +++ b/packages/tldraw/src/lib/shapes/line/LineShapeUtil.tsx @@ -103,7 +103,7 @@ export class LineShapeUtil extends ShapeUtil { } override getOutlineSegments(shape: TLLineShape) { - const spline = this.editor.getGeometry(shape) as Polyline2d | CubicSpline2d + const spline = this.editor.getShapeGeometry(shape) as Polyline2d | CubicSpline2d return spline.segments.map((s) => s.vertices) } diff --git a/packages/tldraw/src/lib/shapes/line/toolStates/Pointing.ts b/packages/tldraw/src/lib/shapes/line/toolStates/Pointing.ts index 9e6a7fff1..8fb75d972 100644 --- a/packages/tldraw/src/lib/shapes/line/toolStates/Pointing.ts +++ b/packages/tldraw/src/lib/shapes/line/toolStates/Pointing.ts @@ -34,14 +34,14 @@ export class Pointing extends StateNode { this.shape = shape if (inputs.shiftKey) { - const handles = this.editor.getHandles(this.shape) + const handles = this.editor.getShapeHandles(this.shape) if (!handles) return const vertexHandles = handles.filter((h) => h.type === 'vertex').sort(sortByIndex) const endHandle = vertexHandles[vertexHandles.length - 1] const shapePagePoint = Matrix2d.applyToPoint( - this.editor.getParentTransform(this.shape)!, + this.editor.getShapeParentTransform(this.shape)!, new Vec2d(this.shape.x, this.shape.y) ) @@ -105,7 +105,7 @@ export class Pointing extends StateNode { if (!this.shape) return if (this.editor.inputs.isDragging) { - const handles = this.editor.getHandles(this.shape) + const handles = this.editor.getShapeHandles(this.shape) if (!handles) { if (this.markId) this.editor.bailToMark(this.markId) throw Error('No handles found') diff --git a/packages/tldraw/src/lib/shapes/note/NoteShapeTool.test.ts b/packages/tldraw/src/lib/shapes/note/NoteShapeTool.test.ts index ec75c42d8..b9d77d8b3 100644 --- a/packages/tldraw/src/lib/shapes/note/NoteShapeTool.test.ts +++ b/packages/tldraw/src/lib/shapes/note/NoteShapeTool.test.ts @@ -12,47 +12,47 @@ afterEach(() => { describe(NoteShapeTool, () => { it('Creates note shapes on click-and-drag, supports undo and redo', () => { - expect(editor.shapesOnCurrentPage.length).toBe(0) + expect(editor.currentPageShapes.length).toBe(0) editor.setCurrentTool('note') editor.pointerDown(50, 50) editor.pointerMove(100, 100) editor.pointerUp(100, 100) - expect(editor.shapesOnCurrentPage.length).toBe(1) - expect(editor.shapesOnCurrentPage[0]?.type).toBe('note') - expect(editor.selectedShapeIds[0]).toBe(editor.shapesOnCurrentPage[0]?.id) + expect(editor.currentPageShapes.length).toBe(1) + expect(editor.currentPageShapes[0]?.type).toBe('note') + expect(editor.selectedShapeIds[0]).toBe(editor.currentPageShapes[0]?.id) editor.cancel() // leave edit mode editor.undo() // undoes the selection change editor.undo() - expect(editor.shapesOnCurrentPage.length).toBe(0) + expect(editor.currentPageShapes.length).toBe(0) editor.redo() - expect(editor.shapesOnCurrentPage.length).toBe(1) + expect(editor.currentPageShapes.length).toBe(1) }) it('Creates note shapes on click, supports undo and redo', () => { - expect(editor.shapesOnCurrentPage.length).toBe(0) + expect(editor.currentPageShapes.length).toBe(0) editor.setCurrentTool('note') editor.pointerDown(50, 50) editor.pointerUp(50, 50) - expect(editor.shapesOnCurrentPage.length).toBe(1) - expect(editor.shapesOnCurrentPage[0]?.type).toBe('note') - expect(editor.selectedShapeIds[0]).toBe(editor.shapesOnCurrentPage[0]?.id) + expect(editor.currentPageShapes.length).toBe(1) + expect(editor.currentPageShapes[0]?.type).toBe('note') + expect(editor.selectedShapeIds[0]).toBe(editor.currentPageShapes[0]?.id) editor.undo() - expect(editor.shapesOnCurrentPage.length).toBe(0) + expect(editor.currentPageShapes.length).toBe(0) editor.redo() - expect(editor.shapesOnCurrentPage.length).toBe(1) + expect(editor.currentPageShapes.length).toBe(1) }) }) @@ -126,21 +126,21 @@ describe('When in the pointing state', () => { }) it('Creates a note and begins editing on pointer up', () => { - expect(editor.shapesOnCurrentPage.length).toBe(0) + expect(editor.currentPageShapes.length).toBe(0) editor.setCurrentTool('note') editor.pointerDown(50, 50) editor.pointerUp(50, 50) editor.expectPathToBe('root.select.editing_shape') - expect(editor.shapesOnCurrentPage.length).toBe(1) + expect(editor.currentPageShapes.length).toBe(1) }) it('Creates a frame and returns to frame.idle on pointer up if tool lock is enabled', () => { editor.updateInstanceState({ isToolLocked: true }) - expect(editor.shapesOnCurrentPage.length).toBe(0) + expect(editor.currentPageShapes.length).toBe(0) editor.setCurrentTool('note') editor.pointerDown(50, 50) editor.pointerUp(50, 50) editor.expectPathToBe('root.note.idle') - expect(editor.shapesOnCurrentPage.length).toBe(1) + expect(editor.currentPageShapes.length).toBe(1) }) }) diff --git a/packages/tldraw/src/lib/shapes/note/NoteShapeUtil.tsx b/packages/tldraw/src/lib/shapes/note/NoteShapeUtil.tsx index 411c3110b..fdac856ad 100644 --- a/packages/tldraw/src/lib/shapes/note/NoteShapeUtil.tsx +++ b/packages/tldraw/src/lib/shapes/note/NoteShapeUtil.tsx @@ -113,7 +113,7 @@ export class NoteShapeUtil extends ShapeUtil { override toSvg(shape: TLNoteShape, ctx: SvgExportContext) { ctx.addExportDef(getFontDefForExport(shape.props.font)) const theme = getDefaultColorTheme({ isDarkMode: this.editor.user.isDarkMode }) - const bounds = this.editor.getGeometry(shape).bounds + const bounds = this.editor.getShapeGeometry(shape).bounds const g = document.createElementNS('http://www.w3.org/2000/svg', 'g') diff --git a/packages/tldraw/src/lib/shapes/note/toolStates/Pointing.ts b/packages/tldraw/src/lib/shapes/note/toolStates/Pointing.ts index 37272c3f9..4cf0e6486 100644 --- a/packages/tldraw/src/lib/shapes/note/toolStates/Pointing.ts +++ b/packages/tldraw/src/lib/shapes/note/toolStates/Pointing.ts @@ -89,20 +89,19 @@ export class Pointing extends StateNode { this.markId = `creating:${id}` this.editor.mark(this.markId) - this.editor.createShapes( - [ + this.editor + .createShapes([ { id, type: 'note', x: originPagePoint.x, y: originPagePoint.y, }, - ], - true - ) + ]) + .select(id) const shape = this.editor.getShape(id)! - const bounds = this.editor.getGeometry(shape).bounds + const bounds = this.editor.getShapeGeometry(shape).bounds // Center the text around the created point this.editor.updateShapes([ diff --git a/packages/tldraw/src/lib/shapes/text/TextShapeTool.test.ts b/packages/tldraw/src/lib/shapes/text/TextShapeTool.test.ts index a609ab014..6ecc88097 100644 --- a/packages/tldraw/src/lib/shapes/text/TextShapeTool.test.ts +++ b/packages/tldraw/src/lib/shapes/text/TextShapeTool.test.ts @@ -13,7 +13,7 @@ afterEach(() => { describe(TextShapeTool, () => { it('Creates text, edits it, undoes and redoes', () => { - expect(editor.shapesOnCurrentPage.length).toBe(0) + expect(editor.currentPageShapes.length).toBe(0) editor.setCurrentTool('text') editor.expectToBeIn('text.idle') editor.pointerDown(0, 0) @@ -22,28 +22,28 @@ describe(TextShapeTool, () => { editor.expectToBeIn('select.editing_shape') // This comes from the component, not the state chart editor.updateShapes([ - { ...editor.shapesOnCurrentPage[0]!, type: 'text', props: { text: 'Hello' } }, + { ...editor.currentPageShapes[0]!, type: 'text', props: { text: 'Hello' } }, ]) // Deselect the editing shape editor.cancel() editor.expectToBeIn('select.idle') - expect(editor.shapesOnCurrentPage.length).toBe(1) + expect(editor.currentPageShapes.length).toBe(1) editor.expectShapeToMatch({ - id: editor.shapesOnCurrentPage[0].id, + id: editor.currentPageShapes[0].id, type: 'text', props: { text: 'Hello' }, }) editor.undo() - expect(editor.shapesOnCurrentPage.length).toBe(0) + expect(editor.currentPageShapes.length).toBe(0) editor.redo() - expect(editor.shapesOnCurrentPage.length).toBe(1) + expect(editor.currentPageShapes.length).toBe(1) editor.expectShapeToMatch({ - id: editor.shapesOnCurrentPage[0].id, + id: editor.currentPageShapes[0].id, type: 'text', props: { text: 'Hello' }, }) @@ -71,7 +71,7 @@ describe('When in idle state', () => { editor.pointerDown(0, 0) editor.pointerUp() editor.expectToBeIn('select.editing_shape') - expect(editor.shapesOnCurrentPage.length).toBe(1) + expect(editor.currentPageShapes.length).toBe(1) }) it('returns to select on cancel', () => { @@ -87,7 +87,7 @@ describe('When in the pointing state', () => { editor.pointerDown(0, 0) editor.cancel() editor.expectToBeIn('text.idle') - expect(editor.shapesOnCurrentPage.length).toBe(0) + expect(editor.currentPageShapes.length).toBe(0) }) it('returns to idle on interrupt', () => { @@ -96,7 +96,7 @@ describe('When in the pointing state', () => { editor.expectToBeIn('text.pointing') editor.interrupt() editor.expectToBeIn('text.idle') - expect(editor.shapesOnCurrentPage.length).toBe(0) + expect(editor.currentPageShapes.length).toBe(0) }) it('transitions to select.resizing when dragging and edits on pointer up', () => { @@ -105,7 +105,7 @@ describe('When in the pointing state', () => { editor.pointerMove(10, 10) editor.expectToBeIn('select.resizing') editor.pointerUp() - expect(editor.shapesOnCurrentPage.length).toBe(1) + expect(editor.currentPageShapes.length).toBe(1) editor.expectToBeIn('select.editing_shape') }) @@ -115,8 +115,8 @@ describe('When in the pointing state', () => { const y = 0 editor.pointerDown(x, y) editor.pointerUp() - const bounds = editor.getPageBounds(editor.shapesOnCurrentPage[0])! - expect(editor.shapesOnCurrentPage[0]).toMatchObject({ + const bounds = editor.getShapePageBounds(editor.currentPageShapes[0])! + expect(editor.currentPageShapes[0]).toMatchObject({ x: x - bounds.width / 2, y: y - bounds.height / 2, }) @@ -131,7 +131,7 @@ describe('When resizing', () => { editor.expectToBeIn('select.resizing') editor.cancel() editor.expectToBeIn('text.idle') - expect(editor.shapesOnCurrentPage.length).toBe(0) + expect(editor.currentPageShapes.length).toBe(0) }) it('does not bails on interrupt while resizing', () => { @@ -140,7 +140,7 @@ describe('When resizing', () => { editor.pointerMove(100, 100) editor.expectToBeIn('select.resizing') editor.interrupt() - expect(editor.shapesOnCurrentPage.length).toBe(1) + expect(editor.currentPageShapes.length).toBe(1) }) it('preserves the top left when the text has a fixed width', () => { @@ -149,7 +149,7 @@ describe('When resizing', () => { const y = 0 editor.pointerDown(x, y) editor.pointerMove(x + 100, y + 100) - expect(editor.shapesOnCurrentPage[0]).toMatchObject({ + expect(editor.currentPageShapes[0]).toMatchObject({ x, y, }) diff --git a/packages/tldraw/src/lib/shapes/text/TextShapeUtil.tsx b/packages/tldraw/src/lib/shapes/text/TextShapeUtil.tsx index 5fdfa5be5..267276b5f 100644 --- a/packages/tldraw/src/lib/shapes/text/TextShapeUtil.tsx +++ b/packages/tldraw/src/lib/shapes/text/TextShapeUtil.tsx @@ -144,7 +144,7 @@ export class TextShapeUtil extends ShapeUtil { } indicator(shape: TLTextShape) { - const bounds = this.editor.getGeometry(shape).bounds + const bounds = this.editor.getShapeGeometry(shape).bounds return } @@ -152,7 +152,7 @@ export class TextShapeUtil extends ShapeUtil { ctx.addExportDef(getFontDefForExport(shape.props.font)) const theme = getDefaultColorTheme({ isDarkMode: this.editor.user.isDarkMode }) - const bounds = this.editor.getGeometry(shape).bounds + const bounds = this.editor.getShapeGeometry(shape).bounds const text = shape.props.text const width = bounds.width / (shape.props.scale ?? 1) diff --git a/packages/tldraw/src/lib/shapes/text/toolStates/Pointing.ts b/packages/tldraw/src/lib/shapes/text/toolStates/Pointing.ts index 7b7bdeca7..4f37c5523 100644 --- a/packages/tldraw/src/lib/shapes/text/toolStates/Pointing.ts +++ b/packages/tldraw/src/lib/shapes/text/toolStates/Pointing.ts @@ -72,8 +72,8 @@ export class Pointing extends StateNode { this.editor.mark('creating text shape') const id = createShapeId() const { x, y } = this.editor.inputs.currentPagePoint - this.editor.createShapes( - [ + this.editor + .createShapes([ { id, type: 'text', @@ -84,9 +84,8 @@ export class Pointing extends StateNode { autoSize: true, }, }, - ], - true - ) + ]) + .select(id) this.editor.setEditingId(id) this.editor.setCurrentTool('select') diff --git a/packages/tldraw/src/lib/shapes/video/VideoShapeUtil.tsx b/packages/tldraw/src/lib/shapes/video/VideoShapeUtil.tsx index 98d4a1383..ab3da4399 100644 --- a/packages/tldraw/src/lib/shapes/video/VideoShapeUtil.tsx +++ b/packages/tldraw/src/lib/shapes/video/VideoShapeUtil.tsx @@ -71,7 +71,7 @@ const TLVideoUtilComponent = track(function TLVideoUtilComponent(props: { }) { const { shape, videoUtil } = props const showControls = - videoUtil.editor.getGeometry(shape).bounds.w * videoUtil.editor.zoomLevel >= 110 + videoUtil.editor.getShapeGeometry(shape).bounds.w * videoUtil.editor.zoomLevel >= 110 const asset = shape.props.assetId ? videoUtil.editor.getAsset(shape.props.assetId) : null const { time, playing } = shape.props const isEditing = useIsEditing(shape.id) diff --git a/packages/tldraw/src/lib/tools/EraserTool/children/Erasing.ts b/packages/tldraw/src/lib/tools/EraserTool/children/Erasing.ts index b61425508..d40201d48 100644 --- a/packages/tldraw/src/lib/tools/EraserTool/children/Erasing.ts +++ b/packages/tldraw/src/lib/tools/EraserTool/children/Erasing.ts @@ -25,7 +25,7 @@ export class Erasing extends StateNode { const { originPagePoint } = this.editor.inputs this.excludedShapeIds = new Set( - this.editor.shapesOnCurrentPage + this.editor.currentPageShapes .filter( (shape) => this.editor.isShapeOrAncestorLocked(shape) || @@ -95,7 +95,7 @@ export class Erasing extends StateNode { update() { const { zoomLevel, - shapesOnCurrentPage, + currentPageShapes: currentPageShapes, erasingShapeIdsSet, inputs: { currentPagePoint, previousPagePoint }, } = this.editor @@ -106,17 +106,17 @@ export class Erasing extends StateNode { const erasing = new Set(erasingShapeIdsSet) - for (const shape of shapesOnCurrentPage) { + for (const shape of currentPageShapes) { if (this.editor.isShapeOfType(shape, 'group')) continue // Avoid testing masked shapes, unless the pointer is inside the mask - const pageMask = this.editor.getPageMask(shape.id) + const pageMask = this.editor.getShapeMask(shape.id) if (pageMask && !pointInPolygon(currentPagePoint, pageMask)) { continue } // Hit test the shape using a line segment - const geometry = this.editor.getGeometry(shape) + const geometry = this.editor.getShapeGeometry(shape) const A = this.editor.getPointInShapeSpace(shape, previousPagePoint) const B = this.editor.getPointInShapeSpace(shape, currentPagePoint) diff --git a/packages/tldraw/src/lib/tools/EraserTool/children/Pointing.ts b/packages/tldraw/src/lib/tools/EraserTool/children/Pointing.ts index db6b072f0..f31bf54d7 100644 --- a/packages/tldraw/src/lib/tools/EraserTool/children/Pointing.ts +++ b/packages/tldraw/src/lib/tools/EraserTool/children/Pointing.ts @@ -13,7 +13,7 @@ export class Pointing extends StateNode { override onEnter = () => { const { inputs: { currentPagePoint }, - sortedShapesOnCurrentPage, + currentPageShapesSorted: sortedShapesOnCurrentPage, zoomLevel, } = this.editor diff --git a/packages/tldraw/src/lib/tools/SelectTool/children/Brushing.ts b/packages/tldraw/src/lib/tools/SelectTool/children/Brushing.ts index f400c5d10..9fa671139 100644 --- a/packages/tldraw/src/lib/tools/SelectTool/children/Brushing.ts +++ b/packages/tldraw/src/lib/tools/SelectTool/children/Brushing.ts @@ -39,7 +39,7 @@ export class Brushing extends StateNode { } this.excludedShapeIds = new Set( - this.editor.shapesOnCurrentPage + this.editor.currentPageShapes .filter( (shape) => this.editor.isShapeOfType(shape, 'group') || @@ -96,7 +96,7 @@ export class Brushing extends StateNode { const { zoomLevel, currentPageId, - shapesOnCurrentPage, + currentPageShapes: currentPageShapes, inputs: { originPagePoint, currentPagePoint, shiftKey, ctrlKey }, } = this.editor @@ -118,12 +118,12 @@ export class Brushing extends StateNode { const { excludedShapeIds } = this - testAllShapes: for (let i = 0, n = shapesOnCurrentPage.length; i < n; i++) { - shape = shapesOnCurrentPage[i] + testAllShapes: for (let i = 0, n = currentPageShapes.length; i < n; i++) { + shape = currentPageShapes[i] if (excludedShapeIds.has(shape.id)) continue testAllShapes if (results.has(shape.id)) continue testAllShapes - pageBounds = this.editor.getPageBounds(shape) + pageBounds = this.editor.getShapePageBounds(shape) if (!pageBounds) continue testAllShapes // If the brush fully wraps a shape, it's almost certainly a hit @@ -144,9 +144,9 @@ export class Brushing extends StateNode { if (this.brush.collides(pageBounds)) { // Shapes expect to hit test line segments in their own coordinate system, // so we first need to get the brush corners in the shape's local space. - const geometry = this.editor.getGeometry(shape) + const geometry = this.editor.getShapeGeometry(shape) - pageTransform = this.editor.getPageTransform(shape) + pageTransform = this.editor.getShapePageTransform(shape) if (!pageTransform) { continue testAllShapes @@ -190,7 +190,7 @@ export class Brushing extends StateNode { // Find the outermost selectable shape, check to see if it has a // page mask; and if so, check to see if the brush intersects it const selectedShape = this.editor.getOutermostSelectableShape(shape) - const pageMask = this.editor.getPageMask(selectedShape.id) + const pageMask = this.editor.getShapeMask(selectedShape.id) if ( pageMask && diff --git a/packages/tldraw/src/lib/tools/SelectTool/children/Cropping.ts b/packages/tldraw/src/lib/tools/SelectTool/children/Cropping.ts index 13325f2b0..42b8c3f55 100644 --- a/packages/tldraw/src/lib/tools/SelectTool/children/Cropping.ts +++ b/packages/tldraw/src/lib/tools/SelectTool/children/Cropping.ts @@ -230,7 +230,7 @@ export class Cropping extends StateNode { const shape = this.editor.onlySelectedShape as TLImageShape - const selectionBounds = this.editor.selectionBounds! + const selectionBounds = this.editor.selectionRotatedPageBounds! const dragHandlePoint = Vec2d.RotWith( selectionBounds.getHandlePoint(this.info.handle!), diff --git a/packages/tldraw/src/lib/tools/SelectTool/children/DraggingHandle.ts b/packages/tldraw/src/lib/tools/SelectTool/children/DraggingHandle.ts index 78713dec0..280d13d4c 100644 --- a/packages/tldraw/src/lib/tools/SelectTool/children/DraggingHandle.ts +++ b/packages/tldraw/src/lib/tools/SelectTool/children/DraggingHandle.ts @@ -54,7 +54,7 @@ export class DraggingHandle extends StateNode { this.shapeId = shape.id this.markId = isCreating ? `creating:${shape.id}` : this.editor.mark('dragging handle') this.initialHandle = deepCopy(handle) - this.initialPageTransform = this.editor.getPageTransform(shape)! + this.initialPageTransform = this.editor.getShapePageTransform(shape)! this.initialPageRotation = this.initialPageTransform.rotation() this.initialPagePoint = this.editor.inputs.originPagePoint.clone() @@ -64,7 +64,7 @@ export class DraggingHandle extends StateNode { ) //