diff --git a/packages/core/src/components/container/container.tsx b/packages/core/src/components/container/container.tsx
index c88bdb820..9354cbbe6 100644
--- a/packages/core/src/components/container/container.tsx
+++ b/packages/core/src/components/container/container.tsx
@@ -15,7 +15,7 @@ export const Container = React.memo(
const rBounds = usePosition(bounds, rotation)
return (
-
+
{children}
)
diff --git a/packages/core/src/components/handles/handle.tsx b/packages/core/src/components/handles/handle.tsx
index 8fa422bc2..3a698e1ed 100644
--- a/packages/core/src/components/handles/handle.tsx
+++ b/packages/core/src/components/handles/handle.tsx
@@ -12,26 +12,22 @@ interface HandleProps {
export const Handle = React.memo(({ id, point }: HandleProps) => {
const events = useHandleEvents(id)
- const bounds = React.useMemo(
- () =>
- Utils.translateBounds(
+ return (
+
+ )}
+ >
-
+
diff --git a/packages/core/src/components/shape/shape.tsx b/packages/core/src/components/shape/shape.tsx
index 47ba6047c..32b359dee 100644
--- a/packages/core/src/components/shape/shape.tsx
+++ b/packages/core/src/components/shape/shape.tsx
@@ -25,12 +25,7 @@ export const Shape = ({
const events = useShapeEvents(shape.id, isCurrentParent)
return (
-
+
.tl-handle-bg {
+ .tl-handle:hover .tl-handle-bg {
fill: var(--tl-selectFill);
}
- .tl-handles:hover > .tl-handle-bg > * {
+ .tl-handle:hover .tl-handle-bg > * {
stroke: var(--tl-selectFill);
}
- .tl-handles:active > .tl-handle-bg {
+ .tl-handle:active .tl-handle-bg {
fill: var(--tl-selectFill);
}
- .tl-handles:active > .tl-handle-bg > * {
+ .tl-handle:active .tl-handle-bg > * {
stroke: var(--tl-selectFill);
}
@@ -309,7 +309,7 @@ const tlcss = css`
fill: transparent;
stroke: none;
pointer-events: all;
- r: calc(20 / max(1, var(--tl-zoom)));
+ r: calc(20px / max(1, var(--tl-zoom)));
}
.tl-binding-indicator {
diff --git a/packages/core/src/shapes/createShape.tsx b/packages/core/src/shapes/createShape.tsx
index f9d274f2d..a516db99e 100644
--- a/packages/core/src/shapes/createShape.tsx
+++ b/packages/core/src/shapes/createShape.tsx
@@ -206,8 +206,13 @@ export const ShapeUtil = function (() =>
const sw = strokeWidth * 1.618
+ const dots = getArcPoints(shape)
+
return (
@@ -249,7 +251,7 @@ export const Arrow = new ShapeUtil(() =>
},
Indicator({ shape }) {
- const path = Utils.getFromCache(simplePathCache, shape.handles, () => getArrowPath(shape))
+ const path = getArrowPath(shape)
return
},
@@ -260,20 +262,25 @@ export const Arrow = new ShapeUtil(() =>
getBounds(shape) {
const bounds = Utils.getFromCache(this.boundsCache, shape, () => {
- const { start, bend, end } = shape.handles
- return Utils.getBoundsFromPoints([start.point, bend.point, end.point])
+ const points = getArcPoints(shape)
+ return Utils.getBoundsFromPoints(points)
})
return Utils.translateBounds(bounds, shape.point)
},
getRotatedBounds(shape) {
- const { start, bend, end } = shape.handles
+ let points = getArcPoints(shape)
- return Utils.translateBounds(
- Utils.getBoundsFromPoints([start.point, bend.point, end.point], shape.rotation),
- shape.point
- )
+ const { minX, minY, maxX, maxY } = Utils.getBoundsFromPoints(points)
+
+ if (shape.rotation !== 0) {
+ points = points.map((pt) =>
+ Vec.rotWith(pt, [(minX + maxX) / 2, (minY + maxY) / 2], shape.rotation || 0)
+ )
+ }
+
+ return Utils.translateBounds(Utils.getBoundsFromPoints(points), shape.point)
},
getCenter(shape) {
@@ -544,11 +551,10 @@ export const Arrow = new ShapeUtil(() =>
// Zero out the handles to prevent handles with negative points. If a handle's x or y
// is below zero, we need to move the shape left or up to make it zero.
- const bounds = Utils.getBoundsFromPoints(
- Object.values(nextShape.handles).map((handle) => handle.point)
- )
+ const topLeft = shape.point
+ const nextBounds = this.getBounds({ ...nextShape } as ArrowShape)
- const offset = [bounds.minX, bounds.minY]
+ const offset = Vec.sub([nextBounds.minX, nextBounds.minY], topLeft)
if (!Vec.isEqual(offset, [0, 0])) {
Object.values(nextShape.handles).forEach((handle) => {
@@ -766,3 +772,22 @@ function getArrowPath(shape: ArrowShape) {
return path.join(' ')
}
+
+function getArcPoints(shape: ArrowShape) {
+ const { start, bend, end } = shape.handles
+ const points: number[][] = [start.point, end.point]
+
+ if (Vec.dist2(bend.point, Vec.med(start.point, end.point)) > 4) {
+ // We're an arc, calculate points along the arc
+ const { center, radius } = getArrowArc(shape)
+ const startAngle = Vec.angle(center, start.point)
+ const endAngle = Vec.angle(center, end.point)
+
+ for (let i = 1 / 20; i < 1; i += 1 / 20) {
+ const angle = Utils.lerpAngles(startAngle, endAngle, i)
+ points.push(Vec.nudgeAtAngle(center, angle, radius))
+ }
+ }
+
+ return points
+}
diff --git a/packages/tldraw/src/state/session/sessions/arrow/arrow.session.ts b/packages/tldraw/src/state/session/sessions/arrow/arrow.session.ts
index 1f8415f79..d778276d4 100644
--- a/packages/tldraw/src/state/session/sessions/arrow/arrow.session.ts
+++ b/packages/tldraw/src/state/session/sessions/arrow/arrow.session.ts
@@ -10,6 +10,7 @@ import {
import { Vec } from '@tldraw/vec'
import { Utils } from '@tldraw/core'
import { TLDR } from '~state/tldr'
+import { ThickArrowDownIcon } from '@radix-ui/react-icons'
export class ArrowSession implements Session {
id = 'transform_single'
@@ -18,6 +19,7 @@ export class ArrowSession implements Session {
delta = [0, 0]
offset = [0, 0]
origin: number[]
+ topLeft: number[]
initialShape: ArrowShape
handleId: 'start' | 'end'
bindableShapeIds: string[]
@@ -33,6 +35,7 @@ export class ArrowSession implements Session {
this.origin = point
this.handleId = handleId
this.initialShape = TLDR.getShape(data, shapeId, data.appState.currentPageId)
+ this.topLeft = this.initialShape.point
this.bindableShapeIds = TLDR.getBindableShapeIds(data)
const initialBindingId = this.initialShape.handles[this.handleId].bindingId
@@ -66,7 +69,9 @@ export class ArrowSession implements Session {
}
// First update the handle's next point
- const change = TLDR.getShapeUtils(shape.type).onHandleChange(
+ const utils = TLDR.getShapeUtils(shape.type)
+
+ const change = utils.onHandleChange(
shape,
{
[handleId]: handle,
diff --git a/packages/tldraw/src/state/session/sessions/handle/handle.session.ts b/packages/tldraw/src/state/session/sessions/handle/handle.session.ts
index d40f313fc..5b93ed8e0 100644
--- a/packages/tldraw/src/state/session/sessions/handle/handle.session.ts
+++ b/packages/tldraw/src/state/session/sessions/handle/handle.session.ts
@@ -9,6 +9,7 @@ export class HandleSession implements Session {
status = TLDrawStatus.TranslatingHandle
commandId: string
delta = [0, 0]
+ topLeft: number[]
origin: number[]
shiftKey = false
initialShape: ShapesWithProp<'handles'>
@@ -17,6 +18,7 @@ export class HandleSession implements Session {
constructor(data: Data, handleId: string, point: number[], commandId = 'move_handle') {
const { currentPageId } = data.appState
const shapeId = TLDR.getSelectedIds(data, currentPageId)[0]
+ this.topLeft = point
this.origin = point
this.handleId = handleId
this.initialShape = TLDR.getShape(data, shapeId, currentPageId)
@@ -43,6 +45,7 @@ export class HandleSession implements Session {
}
// First update the handle's next point
+
const change = TLDR.getShapeUtils(shape).onHandleChange(
shape,
{
diff --git a/packages/tldraw/tsconfig.json b/packages/tldraw/tsconfig.json
index 734e77e6f..d94705f05 100644
--- a/packages/tldraw/tsconfig.json
+++ b/packages/tldraw/tsconfig.json
@@ -6,7 +6,6 @@
"outDir": "./dist/types",
"rootDir": "src",
"baseUrl": "src",
- "emitDeclarationOnly": false,
"paths": {
"~*": ["./*"],
"@tldraw/core": ["../core"],