arrows: separate out handle behavior from labels (#2621)
This is a followup on the arrows work. - allow labels to go to the ends if no arrowhead is present - avoid using / overloading TLHandle and use a new PointingLabel state to specifically address label movement - removes the feature flag to launch this feature! ### Change Type - [x] `patch` — Bug fix ### Release Notes - Arrow labels: provide more polish on label placement --------- Co-authored-by: Steve Ruiz <steveruizok@gmail.com>
This commit is contained in:
parent
f87702bda4
commit
34a95b2ec8
22 changed files with 572 additions and 445 deletions
|
@ -969,9 +969,7 @@ export const EVENT_NAME_MAP: Record<Exclude<TLEventName, TLPinchEventName>, keyo
|
||||||
export function extractSessionStateFromLegacySnapshot(store: Record<string, UnknownRecord>): null | TLSessionStateSnapshot;
|
export function extractSessionStateFromLegacySnapshot(store: Record<string, UnknownRecord>): null | TLSessionStateSnapshot;
|
||||||
|
|
||||||
// @internal (undocumented)
|
// @internal (undocumented)
|
||||||
export const featureFlags: {
|
export const featureFlags: Record<string, DebugFlag<boolean>>;
|
||||||
canMoveArrowLabel: DebugFlag<boolean>;
|
|
||||||
};
|
|
||||||
|
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
export type GapsSnapLine = {
|
export type GapsSnapLine = {
|
||||||
|
@ -1041,6 +1039,9 @@ export abstract class Geometry2d {
|
||||||
_vertices: undefined | Vec[];
|
_vertices: undefined | Vec[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @public
|
||||||
|
export function getArcMeasure(A: number, B: number, sweepFlag: number, largeArcFlag: number): number;
|
||||||
|
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
export function getArrowTerminalsInArrowSpace(editor: Editor, shape: TLArrowShape): {
|
export function getArrowTerminalsInArrowSpace(editor: Editor, shape: TLArrowShape): {
|
||||||
start: Vec;
|
start: Vec;
|
||||||
|
@ -1092,6 +1093,9 @@ export function getPointerInfo(e: PointerEvent | React.PointerEvent): {
|
||||||
isPen: boolean;
|
isPen: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// @public
|
||||||
|
export function getPointInArcT(mAB: number, A: number, B: number, P: number): number;
|
||||||
|
|
||||||
// @public
|
// @public
|
||||||
export function getPointOnCircle(center: VecLike, r: number, a: number): Vec;
|
export function getPointOnCircle(center: VecLike, r: number, a: number): Vec;
|
||||||
|
|
||||||
|
@ -1661,8 +1665,6 @@ export abstract class ShapeUtil<Shape extends TLUnknownShape = TLUnknownShape> {
|
||||||
onDropShapesOver?: TLOnDragHandler<Shape>;
|
onDropShapesOver?: TLOnDragHandler<Shape>;
|
||||||
onEditEnd?: TLOnEditEndHandler<Shape>;
|
onEditEnd?: TLOnEditEndHandler<Shape>;
|
||||||
onHandleDrag?: TLOnHandleDragHandler<Shape>;
|
onHandleDrag?: TLOnHandleDragHandler<Shape>;
|
||||||
onHandleDragEnd?: TLOnHandleDragStartHandler<Shape>;
|
|
||||||
onHandleDragStart?: TLOnHandleDragStartHandler<Shape>;
|
|
||||||
onResize?: TLOnResizeHandler<Shape>;
|
onResize?: TLOnResizeHandler<Shape>;
|
||||||
onResizeEnd?: TLOnResizeEndHandler<Shape>;
|
onResizeEnd?: TLOnResizeEndHandler<Shape>;
|
||||||
onResizeStart?: TLOnResizeStartHandler<Shape>;
|
onResizeStart?: TLOnResizeStartHandler<Shape>;
|
||||||
|
@ -2382,9 +2384,6 @@ export type TLOnHandleDragHandler<T extends TLShape> = (shape: T, info: {
|
||||||
initial?: T | undefined;
|
initial?: T | undefined;
|
||||||
}) => TLShapePartial<T> | void;
|
}) => TLShapePartial<T> | void;
|
||||||
|
|
||||||
// @public (undocumented)
|
|
||||||
export type TLOnHandleDragStartHandler<T extends TLShape> = (shape: T) => TLShapePartial<T> | void;
|
|
||||||
|
|
||||||
// @public
|
// @public
|
||||||
export type TLOnMountHandler = (editor: Editor) => (() => undefined | void) | undefined | void;
|
export type TLOnMountHandler = (editor: Editor) => (() => undefined | void) | undefined | void;
|
||||||
|
|
||||||
|
|
|
@ -21183,6 +21183,99 @@
|
||||||
],
|
],
|
||||||
"implementsTokenRanges": []
|
"implementsTokenRanges": []
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"kind": "Function",
|
||||||
|
"canonicalReference": "@tldraw/editor!getArcMeasure:function(1)",
|
||||||
|
"docComment": "/**\n * Get the measure of an arc.\n *\n * @param A - The angle from center to arc's start point (A) on the circle\n *\n * @param B - The angle from center to arc's end point (B) on the circle\n *\n * @param sweepFlag - 1 if the arc is clockwise, 0 if counter-clockwise\n *\n * @param largeArcFlag - 1 if the arc is greater than 180 degrees, 0 if less than 180 degrees\n *\n * @returns The measure of the arc, negative if counter-clockwise\n *\n * @public\n */\n",
|
||||||
|
"excerptTokens": [
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": "export declare function getArcMeasure(A: "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": "number"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": ", B: "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": "number"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": ", sweepFlag: "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": "number"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": ", largeArcFlag: "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": "number"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": "): "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": "number"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": ";"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"fileUrlPath": "packages/editor/src/lib/primitives/utils.ts",
|
||||||
|
"returnTypeTokenRange": {
|
||||||
|
"startIndex": 9,
|
||||||
|
"endIndex": 10
|
||||||
|
},
|
||||||
|
"releaseTag": "Public",
|
||||||
|
"overloadIndex": 1,
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"parameterName": "A",
|
||||||
|
"parameterTypeTokenRange": {
|
||||||
|
"startIndex": 1,
|
||||||
|
"endIndex": 2
|
||||||
|
},
|
||||||
|
"isOptional": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameterName": "B",
|
||||||
|
"parameterTypeTokenRange": {
|
||||||
|
"startIndex": 3,
|
||||||
|
"endIndex": 4
|
||||||
|
},
|
||||||
|
"isOptional": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameterName": "sweepFlag",
|
||||||
|
"parameterTypeTokenRange": {
|
||||||
|
"startIndex": 5,
|
||||||
|
"endIndex": 6
|
||||||
|
},
|
||||||
|
"isOptional": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameterName": "largeArcFlag",
|
||||||
|
"parameterTypeTokenRange": {
|
||||||
|
"startIndex": 7,
|
||||||
|
"endIndex": 8
|
||||||
|
},
|
||||||
|
"isOptional": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "getArcMeasure"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"kind": "Function",
|
"kind": "Function",
|
||||||
"canonicalReference": "@tldraw/editor!getArrowTerminalsInArrowSpace:function(1)",
|
"canonicalReference": "@tldraw/editor!getArrowTerminalsInArrowSpace:function(1)",
|
||||||
|
@ -21898,6 +21991,99 @@
|
||||||
],
|
],
|
||||||
"name": "getPointerInfo"
|
"name": "getPointerInfo"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"kind": "Function",
|
||||||
|
"canonicalReference": "@tldraw/editor!getPointInArcT:function(1)",
|
||||||
|
"docComment": "/**\n * Returns the t value of the point on the arc.\n *\n * @param mAB - The measure of the arc from A to B, negative if counter-clockwise\n *\n * @param A - The angle from center to arc's start point (A) on the circle\n *\n * @param B - The angle from center to arc's end point (B) on the circle\n *\n * @param P - The angle on the circle (P) to find the t value for\n *\n * @returns The t value of the point on the arc, with 0 being the start and 1 being the end\n *\n * @public\n */\n",
|
||||||
|
"excerptTokens": [
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": "export declare function getPointInArcT(mAB: "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": "number"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": ", A: "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": "number"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": ", B: "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": "number"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": ", P: "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": "number"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": "): "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": "number"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": ";"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"fileUrlPath": "packages/editor/src/lib/primitives/utils.ts",
|
||||||
|
"returnTypeTokenRange": {
|
||||||
|
"startIndex": 9,
|
||||||
|
"endIndex": 10
|
||||||
|
},
|
||||||
|
"releaseTag": "Public",
|
||||||
|
"overloadIndex": 1,
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"parameterName": "mAB",
|
||||||
|
"parameterTypeTokenRange": {
|
||||||
|
"startIndex": 1,
|
||||||
|
"endIndex": 2
|
||||||
|
},
|
||||||
|
"isOptional": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameterName": "A",
|
||||||
|
"parameterTypeTokenRange": {
|
||||||
|
"startIndex": 3,
|
||||||
|
"endIndex": 4
|
||||||
|
},
|
||||||
|
"isOptional": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameterName": "B",
|
||||||
|
"parameterTypeTokenRange": {
|
||||||
|
"startIndex": 5,
|
||||||
|
"endIndex": 6
|
||||||
|
},
|
||||||
|
"isOptional": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameterName": "P",
|
||||||
|
"parameterTypeTokenRange": {
|
||||||
|
"startIndex": 7,
|
||||||
|
"endIndex": 8
|
||||||
|
},
|
||||||
|
"isOptional": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "getPointInArcT"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"kind": "Function",
|
"kind": "Function",
|
||||||
"canonicalReference": "@tldraw/editor!getPointOnCircle:function(1)",
|
"canonicalReference": "@tldraw/editor!getPointOnCircle:function(1)",
|
||||||
|
@ -31338,76 +31524,6 @@
|
||||||
"isProtected": false,
|
"isProtected": false,
|
||||||
"isAbstract": false
|
"isAbstract": false
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"kind": "Property",
|
|
||||||
"canonicalReference": "@tldraw/editor!ShapeUtil#onHandleDragEnd:member",
|
|
||||||
"docComment": "/**\n * A callback called when a shape starts being dragged.\n *\n * @param shape - The shape.\n *\n * @returns A change to apply to the shape, or void.\n *\n * @public\n */\n",
|
|
||||||
"excerptTokens": [
|
|
||||||
{
|
|
||||||
"kind": "Content",
|
|
||||||
"text": "onHandleDragEnd?: "
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"kind": "Reference",
|
|
||||||
"text": "TLOnHandleDragStartHandler",
|
|
||||||
"canonicalReference": "@tldraw/editor!TLOnHandleDragStartHandler:type"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"kind": "Content",
|
|
||||||
"text": "<Shape>"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"kind": "Content",
|
|
||||||
"text": ";"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"isReadonly": false,
|
|
||||||
"isOptional": true,
|
|
||||||
"releaseTag": "Public",
|
|
||||||
"name": "onHandleDragEnd",
|
|
||||||
"propertyTypeTokenRange": {
|
|
||||||
"startIndex": 1,
|
|
||||||
"endIndex": 3
|
|
||||||
},
|
|
||||||
"isStatic": false,
|
|
||||||
"isProtected": false,
|
|
||||||
"isAbstract": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"kind": "Property",
|
|
||||||
"canonicalReference": "@tldraw/editor!ShapeUtil#onHandleDragStart:member",
|
|
||||||
"docComment": "/**\n * A callback called when a shape starts being dragged.\n *\n * @param shape - The shape.\n *\n * @returns A change to apply to the shape, or void.\n *\n * @public\n */\n",
|
|
||||||
"excerptTokens": [
|
|
||||||
{
|
|
||||||
"kind": "Content",
|
|
||||||
"text": "onHandleDragStart?: "
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"kind": "Reference",
|
|
||||||
"text": "TLOnHandleDragStartHandler",
|
|
||||||
"canonicalReference": "@tldraw/editor!TLOnHandleDragStartHandler:type"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"kind": "Content",
|
|
||||||
"text": "<Shape>"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"kind": "Content",
|
|
||||||
"text": ";"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"isReadonly": false,
|
|
||||||
"isOptional": true,
|
|
||||||
"releaseTag": "Public",
|
|
||||||
"name": "onHandleDragStart",
|
|
||||||
"propertyTypeTokenRange": {
|
|
||||||
"startIndex": 1,
|
|
||||||
"endIndex": 3
|
|
||||||
},
|
|
||||||
"isStatic": false,
|
|
||||||
"isProtected": false,
|
|
||||||
"isAbstract": false
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"kind": "Property",
|
"kind": "Property",
|
||||||
"canonicalReference": "@tldraw/editor!ShapeUtil#onResize:member",
|
"canonicalReference": "@tldraw/editor!ShapeUtil#onResize:member",
|
||||||
|
@ -39551,63 +39667,6 @@
|
||||||
"endIndex": 8
|
"endIndex": 8
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"kind": "TypeAlias",
|
|
||||||
"canonicalReference": "@tldraw/editor!TLOnHandleDragStartHandler:type",
|
|
||||||
"docComment": "/**\n * @public\n */\n",
|
|
||||||
"excerptTokens": [
|
|
||||||
{
|
|
||||||
"kind": "Content",
|
|
||||||
"text": "export type TLOnHandleDragStartHandler<T extends "
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"kind": "Reference",
|
|
||||||
"text": "TLShape",
|
|
||||||
"canonicalReference": "@tldraw/tlschema!TLShape:type"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"kind": "Content",
|
|
||||||
"text": "> = "
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"kind": "Content",
|
|
||||||
"text": "(shape: T) => "
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"kind": "Reference",
|
|
||||||
"text": "TLShapePartial",
|
|
||||||
"canonicalReference": "@tldraw/tlschema!TLShapePartial:type"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"kind": "Content",
|
|
||||||
"text": "<T> | void"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"kind": "Content",
|
|
||||||
"text": ";"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"fileUrlPath": "packages/editor/src/lib/editor/shapes/ShapeUtil.ts",
|
|
||||||
"releaseTag": "Public",
|
|
||||||
"name": "TLOnHandleDragStartHandler",
|
|
||||||
"typeParameters": [
|
|
||||||
{
|
|
||||||
"typeParameterName": "T",
|
|
||||||
"constraintTokenRange": {
|
|
||||||
"startIndex": 1,
|
|
||||||
"endIndex": 2
|
|
||||||
},
|
|
||||||
"defaultTypeTokenRange": {
|
|
||||||
"startIndex": 0,
|
|
||||||
"endIndex": 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"typeTokenRange": {
|
|
||||||
"startIndex": 3,
|
|
||||||
"endIndex": 6
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"kind": "TypeAlias",
|
"kind": "TypeAlias",
|
||||||
"canonicalReference": "@tldraw/editor!TLOnMountHandler:type",
|
"canonicalReference": "@tldraw/editor!TLOnMountHandler:type",
|
||||||
|
|
|
@ -525,16 +525,6 @@ input,
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tl-handle__text-adjust {
|
|
||||||
cursor: var(--tl-cursor-grab);
|
|
||||||
fill: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tl-handle__text-adjust:hover {
|
|
||||||
stroke-width: calc(2px * var(--tl-scale));
|
|
||||||
stroke: var(--color-selection-stroke);
|
|
||||||
}
|
|
||||||
|
|
||||||
.tl-handle__create {
|
.tl-handle__create {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
@ -1153,6 +1143,10 @@ input,
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tl-arrow-label__inner p {
|
||||||
|
cursor: var(--tl-cursor-grab);
|
||||||
|
}
|
||||||
|
|
||||||
.tl-arrow-label p,
|
.tl-arrow-label p,
|
||||||
.tl-arrow-label textarea {
|
.tl-arrow-label textarea {
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
|
|
|
@ -175,7 +175,6 @@ export {
|
||||||
type TLOnDragHandler,
|
type TLOnDragHandler,
|
||||||
type TLOnEditEndHandler,
|
type TLOnEditEndHandler,
|
||||||
type TLOnHandleDragHandler,
|
type TLOnHandleDragHandler,
|
||||||
type TLOnHandleDragStartHandler,
|
|
||||||
type TLOnResizeEndHandler,
|
type TLOnResizeEndHandler,
|
||||||
type TLOnResizeHandler,
|
type TLOnResizeHandler,
|
||||||
type TLOnResizeStartHandler,
|
type TLOnResizeStartHandler,
|
||||||
|
@ -319,6 +318,8 @@ export {
|
||||||
clockwiseAngleDist,
|
clockwiseAngleDist,
|
||||||
counterClockwiseAngleDist,
|
counterClockwiseAngleDist,
|
||||||
degreesToRadians,
|
degreesToRadians,
|
||||||
|
getArcMeasure,
|
||||||
|
getPointInArcT,
|
||||||
getPointOnCircle,
|
getPointOnCircle,
|
||||||
getPolygonVertices,
|
getPolygonVertices,
|
||||||
isSafeFloat,
|
isSafeFloat,
|
||||||
|
|
|
@ -14,14 +14,6 @@ export type TLHandleComponent = ComponentType<{
|
||||||
|
|
||||||
/** @public */
|
/** @public */
|
||||||
export const DefaultHandle: TLHandleComponent = ({ handle, isCoarse, className, zoom }) => {
|
export const DefaultHandle: TLHandleComponent = ({ handle, isCoarse, className, zoom }) => {
|
||||||
if (handle.type === 'text-adjust') {
|
|
||||||
return (
|
|
||||||
<g className={classNames('tl-handle', 'tl-handle__text-adjust', className)}>
|
|
||||||
<rect rx={4} ry={4} width={handle.w} height={handle.h} />
|
|
||||||
</g>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const bgRadius = (isCoarse ? COARSE_HANDLE_RADIUS : HANDLE_RADIUS) / zoom
|
const bgRadius = (isCoarse ? COARSE_HANDLE_RADIUS : HANDLE_RADIUS) / zoom
|
||||||
const fgRadius = (handle.type === 'create' && isCoarse ? 3 : 4) / zoom
|
const fgRadius = (handle.type === 'create' && isCoarse ? 3 : 4) / zoom
|
||||||
|
|
||||||
|
|
|
@ -411,15 +411,6 @@ export abstract class ShapeUtil<Shape extends TLUnknownShape = TLUnknownShape> {
|
||||||
*/
|
*/
|
||||||
onTranslateEnd?: TLOnTranslateEndHandler<Shape>
|
onTranslateEnd?: TLOnTranslateEndHandler<Shape>
|
||||||
|
|
||||||
/**
|
|
||||||
* A callback called when a shape starts being dragged.
|
|
||||||
*
|
|
||||||
* @param shape - The shape.
|
|
||||||
* @returns A change to apply to the shape, or void.
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
onHandleDragStart?: TLOnHandleDragStartHandler<Shape>
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A callback called when a shape's handle changes.
|
* A callback called when a shape's handle changes.
|
||||||
*
|
*
|
||||||
|
@ -430,15 +421,6 @@ export abstract class ShapeUtil<Shape extends TLUnknownShape = TLUnknownShape> {
|
||||||
*/
|
*/
|
||||||
onHandleDrag?: TLOnHandleDragHandler<Shape>
|
onHandleDrag?: TLOnHandleDragHandler<Shape>
|
||||||
|
|
||||||
/**
|
|
||||||
* A callback called when a shape starts being dragged.
|
|
||||||
*
|
|
||||||
* @param shape - The shape.
|
|
||||||
* @returns A change to apply to the shape, or void.
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
onHandleDragEnd?: TLOnHandleDragStartHandler<Shape>
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A callback called when a shape starts being rotated.
|
* A callback called when a shape starts being rotated.
|
||||||
*
|
*
|
||||||
|
@ -604,9 +586,6 @@ export type TLOnBindingChangeHandler<T extends TLShape> = (shape: T) => TLShapeP
|
||||||
/** @public */
|
/** @public */
|
||||||
export type TLOnChildrenChangeHandler<T extends TLShape> = (shape: T) => TLShapePartial[] | void
|
export type TLOnChildrenChangeHandler<T extends TLShape> = (shape: T) => TLShapePartial[] | void
|
||||||
|
|
||||||
/** @public */
|
|
||||||
export type TLOnHandleDragStartHandler<T extends TLShape> = (shape: T) => TLShapePartial<T> | void
|
|
||||||
|
|
||||||
/** @public */
|
/** @public */
|
||||||
export type TLOnHandleDragHandler<T extends TLShape> = (
|
export type TLOnHandleDragHandler<T extends TLShape> = (
|
||||||
shape: T,
|
shape: T,
|
||||||
|
@ -617,9 +596,6 @@ export type TLOnHandleDragHandler<T extends TLShape> = (
|
||||||
}
|
}
|
||||||
) => TLShapePartial<T> | void
|
) => TLShapePartial<T> | void
|
||||||
|
|
||||||
/** @public */
|
|
||||||
export type TLOnHandleDragEndHandler<T extends TLShape> = (shape: T) => TLShapePartial<T> | void
|
|
||||||
|
|
||||||
/** @public */
|
/** @public */
|
||||||
export type TLOnClickHandler<T extends TLShape> = (shape: T) => TLShapePartial<T> | void
|
export type TLOnClickHandler<T extends TLShape> = (shape: T) => TLShapePartial<T> | void
|
||||||
/** @public */
|
/** @public */
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { Vec } from '../Vec'
|
import { Vec } from '../Vec'
|
||||||
import { intersectLineSegmentCircle } from '../intersect'
|
import { intersectLineSegmentCircle } from '../intersect'
|
||||||
import { PI, PI2, getPointOnCircle, shortAngleDist } from '../utils'
|
import { getArcMeasure, getPointInArcT, getPointOnCircle } from '../utils'
|
||||||
import { Geometry2d, Geometry2dOptions } from './Geometry2d'
|
import { Geometry2d, Geometry2dOptions } from './Geometry2d'
|
||||||
import { getVerticesCountForLength } from './geometry-constants'
|
import { getVerticesCountForLength } from './geometry-constants'
|
||||||
|
|
||||||
|
@ -89,49 +89,3 @@ export class Arc2d extends Geometry2d {
|
||||||
return vertices
|
return vertices
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the t value of the point on the arc.
|
|
||||||
*
|
|
||||||
* @param mAB - The measure of the arc from A to B, negative if counter-clockwise
|
|
||||||
* @param A - The angle from center to arc's start point (A) on the circle
|
|
||||||
* @param B - The angle from center to arc's end point (B) on the circle
|
|
||||||
* @param P - The angle on the circle (P) to find the t value for
|
|
||||||
*
|
|
||||||
* @returns The t value of the point on the arc, with 0 being the start and 1 being the end
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
function getPointInArcT(mAB: number, A: number, B: number, P: number): number {
|
|
||||||
let mAP: number
|
|
||||||
if (Math.abs(mAB) > PI) {
|
|
||||||
mAP = shortAngleDist(A, P)
|
|
||||||
const mPB = shortAngleDist(P, B)
|
|
||||||
if (Math.abs(mAP) < Math.abs(mPB)) {
|
|
||||||
return mAP / mAB
|
|
||||||
} else {
|
|
||||||
return (mAB - mPB) / mAB
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
mAP = shortAngleDist(A, P)
|
|
||||||
return mAP / mAB
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the measure of an arc.
|
|
||||||
*
|
|
||||||
* @param A The angle from center to arc's start point (A) on the circle
|
|
||||||
* @param B The angle from center to arc's end point (B) on the circle
|
|
||||||
* @param sweepFlag 1 if the arc is clockwise, 0 if counter-clockwise
|
|
||||||
* @param largeArcFlag 1 if the arc is greater than 180 degrees, 0 if less than 180 degrees
|
|
||||||
*
|
|
||||||
* @returns The measure of the arc, negative if counter-clockwise
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
function getArcMeasure(A: number, B: number, sweepFlag: number, largeArcFlag: number) {
|
|
||||||
const m = ((2 * ((B - A) % PI2)) % PI2) - ((B - A) % PI2)
|
|
||||||
if (!largeArcFlag) return m
|
|
||||||
return (PI2 - Math.abs(m)) * (sweepFlag ? 1 : -1)
|
|
||||||
}
|
|
||||||
|
|
59
packages/editor/src/lib/primitives/utils.test.ts
Normal file
59
packages/editor/src/lib/primitives/utils.test.ts
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
import { getPointInArcT } from './utils'
|
||||||
|
|
||||||
|
describe('getPointInArcT', () => {
|
||||||
|
it('should return 0 for the start of the arc', () => {
|
||||||
|
const mAB = Math.PI / 2 // 90 degrees
|
||||||
|
const A = 0 // Start angle
|
||||||
|
const B = Math.PI / 2 // End angle
|
||||||
|
const P = 0 // Point angle, same as start
|
||||||
|
expect(getPointInArcT(mAB, A, B, P)).toBe(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return 1 for the end of the arc', () => {
|
||||||
|
const mAB = Math.PI / 2 // 90 degrees
|
||||||
|
const A = 0 // Start angle
|
||||||
|
const B = Math.PI / 2 // End angle
|
||||||
|
const P = Math.PI / 2 // Point angle, same as end
|
||||||
|
expect(getPointInArcT(mAB, A, B, P)).toBe(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return 0.5 for the midpoint of the arc', () => {
|
||||||
|
const mAB = Math.PI // 180 degrees
|
||||||
|
const A = 0 // Start angle
|
||||||
|
const B = Math.PI // End angle
|
||||||
|
const P = Math.PI / 2 // Point angle, midpoint
|
||||||
|
expect(getPointInArcT(mAB, A, B, P)).toBe(0.5)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle negative arcs correctly', () => {
|
||||||
|
const mAB = -Math.PI / 2 // -90 degrees, counter-clockwise
|
||||||
|
const A = Math.PI / 2 // Start angle
|
||||||
|
const B = 0 // End angle
|
||||||
|
const P = Math.PI / 4 // Point angle, quarter way
|
||||||
|
expect(getPointInArcT(mAB, A, B, P)).toBe(0.5)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return correct t value for arcs larger than PI', () => {
|
||||||
|
const mAB = Math.PI * 1.5 // 270 degrees
|
||||||
|
const A = 0 // Start angle
|
||||||
|
const B = -Math.PI / 2 // End angle, going counter-clockwise
|
||||||
|
const P = -Math.PI / 4 // Point angle, halfway
|
||||||
|
expect(getPointInArcT(mAB, A, B, P)).toBe(7 / 6)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle edge case where measurement to center is negative but measure to points near the end are positive', () => {
|
||||||
|
const mAB = -2.8 // Arc measure
|
||||||
|
const A = 0 // Start angle
|
||||||
|
const B = 2.2 // End angle
|
||||||
|
const P = 1.1 // Point angle, should be near the end
|
||||||
|
expect(getPointInArcT(mAB, A, B, P)).toBe(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle edge case where measurement to center is negative but measure to points near the end are positive with other endpoint', () => {
|
||||||
|
const mAB = 0 // Arc measure
|
||||||
|
const A = 0 // Start angle
|
||||||
|
const B = 2.2 // End angle
|
||||||
|
const P = 1.1 // Point angle, should be near the end
|
||||||
|
expect(getPointInArcT(mAB, A, B, P)).toBe(1)
|
||||||
|
})
|
||||||
|
})
|
|
@ -372,3 +372,58 @@ export function angleDistance(fromAngle: number, toAngle: number, direction: num
|
||||||
: counterClockwiseAngleDist(fromAngle, toAngle)
|
: counterClockwiseAngleDist(fromAngle, toAngle)
|
||||||
return dist
|
return dist
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the t value of the point on the arc.
|
||||||
|
*
|
||||||
|
* @param mAB - The measure of the arc from A to B, negative if counter-clockwise
|
||||||
|
* @param A - The angle from center to arc's start point (A) on the circle
|
||||||
|
* @param B - The angle from center to arc's end point (B) on the circle
|
||||||
|
* @param P - The angle on the circle (P) to find the t value for
|
||||||
|
*
|
||||||
|
* @returns The t value of the point on the arc, with 0 being the start and 1 being the end
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export function getPointInArcT(mAB: number, A: number, B: number, P: number): number {
|
||||||
|
let mAP: number
|
||||||
|
if (Math.abs(mAB) > PI) {
|
||||||
|
mAP = shortAngleDist(A, P)
|
||||||
|
const mPB = shortAngleDist(P, B)
|
||||||
|
if (Math.abs(mAP) < Math.abs(mPB)) {
|
||||||
|
return mAP / mAB
|
||||||
|
} else {
|
||||||
|
return (mAB - mPB) / mAB
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mAP = shortAngleDist(A, P)
|
||||||
|
const t = mAP / mAB
|
||||||
|
|
||||||
|
// If the arc is something like -2.8 to 2.2, then we'll get a weird bug
|
||||||
|
// where the measurement to the center is negative but measure to points
|
||||||
|
// near the end are positive
|
||||||
|
if (Math.sign(mAP) !== Math.sign(mAB)) {
|
||||||
|
return Math.abs(t) > 0.5 ? 1 : 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the measure of an arc.
|
||||||
|
*
|
||||||
|
* @param A - The angle from center to arc's start point (A) on the circle
|
||||||
|
* @param B - The angle from center to arc's end point (B) on the circle
|
||||||
|
* @param sweepFlag - 1 if the arc is clockwise, 0 if counter-clockwise
|
||||||
|
* @param largeArcFlag - 1 if the arc is greater than 180 degrees, 0 if less than 180 degrees
|
||||||
|
*
|
||||||
|
* @returns The measure of the arc, negative if counter-clockwise
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export function getArcMeasure(A: number, B: number, sweepFlag: number, largeArcFlag: number) {
|
||||||
|
const m = ((2 * ((B - A) % PI2)) % PI2) - ((B - A) % PI2)
|
||||||
|
if (!largeArcFlag) return m
|
||||||
|
return (PI2 - Math.abs(m)) * (sweepFlag ? 1 : -1)
|
||||||
|
}
|
||||||
|
|
|
@ -7,9 +7,9 @@ import { Atom, atom, react } from '@tldraw/state'
|
||||||
// development. Use `createFeatureFlag` to create a boolean flag which will be
|
// development. Use `createFeatureFlag` to create a boolean flag which will be
|
||||||
// `true` by default in development and staging, and `false` in production.
|
// `true` by default in development and staging, and `false` in production.
|
||||||
/** @internal */
|
/** @internal */
|
||||||
export const featureFlags = {
|
export const featureFlags: Record<string, DebugFlag<boolean>> = {
|
||||||
canMoveArrowLabel: createFeatureFlag('canMoveArrowLabel'),
|
// canMoveArrowLabel: createFeatureFlag('canMoveArrowLabel'),
|
||||||
} satisfies Record<string, DebugFlag<boolean>>
|
}
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
export const debugFlags = {
|
export const debugFlags = {
|
||||||
|
@ -111,16 +111,16 @@ function createDebugValue<T>(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function createFeatureFlag(
|
// function createFeatureFlag(
|
||||||
name: string,
|
// name: string,
|
||||||
defaults: Defaults<boolean> = { all: true, production: false }
|
// defaults: Defaults<boolean> = { all: true, production: false }
|
||||||
) {
|
// ) {
|
||||||
return createDebugValueBase({
|
// return createDebugValueBase({
|
||||||
name,
|
// name,
|
||||||
defaults,
|
// defaults,
|
||||||
shouldStoreForSession: true,
|
// shouldStoreForSession: true,
|
||||||
})
|
// })
|
||||||
}
|
// }
|
||||||
|
|
||||||
function createDebugValueBase<T>(def: DebugFlagDef<T>): DebugFlag<T> {
|
function createDebugValueBase<T>(def: DebugFlagDef<T>): DebugFlag<T> {
|
||||||
const defaultValue = getDefaultValue(def)
|
const defaultValue = getDefaultValue(def)
|
||||||
|
|
|
@ -80,7 +80,6 @@ import { TLOnBeforeUpdateHandler } from '@tldraw/editor';
|
||||||
import { TLOnDoubleClickHandler } from '@tldraw/editor';
|
import { TLOnDoubleClickHandler } from '@tldraw/editor';
|
||||||
import { TLOnEditEndHandler } from '@tldraw/editor';
|
import { TLOnEditEndHandler } from '@tldraw/editor';
|
||||||
import { TLOnHandleDragHandler } from '@tldraw/editor';
|
import { TLOnHandleDragHandler } from '@tldraw/editor';
|
||||||
import { TLOnHandleDragStartHandler } from '@tldraw/editor';
|
|
||||||
import { TLOnResizeEndHandler } from '@tldraw/editor';
|
import { TLOnResizeEndHandler } from '@tldraw/editor';
|
||||||
import { TLOnResizeHandler } from '@tldraw/editor';
|
import { TLOnResizeHandler } from '@tldraw/editor';
|
||||||
import { TLOnTranslateHandler } from '@tldraw/editor';
|
import { TLOnTranslateHandler } from '@tldraw/editor';
|
||||||
|
@ -164,8 +163,6 @@ export class ArrowShapeUtil extends ShapeUtil<TLArrowShape> {
|
||||||
// (undocumented)
|
// (undocumented)
|
||||||
onHandleDrag: TLOnHandleDragHandler<TLArrowShape>;
|
onHandleDrag: TLOnHandleDragHandler<TLArrowShape>;
|
||||||
// (undocumented)
|
// (undocumented)
|
||||||
onHandleDragStart: TLOnHandleDragStartHandler<TLArrowShape>;
|
|
||||||
// (undocumented)
|
|
||||||
onResize: TLOnResizeHandler<TLArrowShape>;
|
onResize: TLOnResizeHandler<TLArrowShape>;
|
||||||
// (undocumented)
|
// (undocumented)
|
||||||
onTranslate?: TLOnTranslateHandler<TLArrowShape>;
|
onTranslate?: TLOnTranslateHandler<TLArrowShape>;
|
||||||
|
@ -1082,7 +1079,7 @@ function Root({ id, children, modal, debugOpen, }: {
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
export class SelectTool extends StateNode {
|
export class SelectTool extends StateNode {
|
||||||
// (undocumented)
|
// (undocumented)
|
||||||
static children: () => (typeof Brushing | typeof Crop | typeof Cropping | typeof DraggingHandle | typeof EditingShape | typeof Idle_11 | typeof PointingCanvas | typeof PointingCropHandle | typeof PointingHandle | typeof PointingResizeHandle | typeof PointingRotateHandle | typeof PointingSelection | typeof PointingShape | typeof Resizing | typeof Rotating | typeof ScribbleBrushing | typeof Translating)[];
|
static children: () => (typeof Brushing | typeof Crop | typeof Cropping | typeof DraggingHandle | typeof EditingShape | typeof Idle_11 | typeof PointingArrowLabel | typeof PointingCanvas | typeof PointingCropHandle | typeof PointingHandle | typeof PointingResizeHandle | typeof PointingRotateHandle | typeof PointingSelection | typeof PointingShape | typeof Resizing | typeof Rotating | typeof ScribbleBrushing | typeof Translating)[];
|
||||||
// (undocumented)
|
// (undocumented)
|
||||||
static id: string;
|
static id: string;
|
||||||
// (undocumented)
|
// (undocumented)
|
||||||
|
|
|
@ -1127,50 +1127,6 @@
|
||||||
"isProtected": false,
|
"isProtected": false,
|
||||||
"isAbstract": false
|
"isAbstract": false
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"kind": "Property",
|
|
||||||
"canonicalReference": "@tldraw/tldraw!ArrowShapeUtil#onHandleDragStart:member",
|
|
||||||
"docComment": "",
|
|
||||||
"excerptTokens": [
|
|
||||||
{
|
|
||||||
"kind": "Content",
|
|
||||||
"text": "onHandleDragStart: "
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"kind": "Reference",
|
|
||||||
"text": "TLOnHandleDragStartHandler",
|
|
||||||
"canonicalReference": "@tldraw/editor!TLOnHandleDragStartHandler:type"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"kind": "Content",
|
|
||||||
"text": "<"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"kind": "Reference",
|
|
||||||
"text": "TLArrowShape",
|
|
||||||
"canonicalReference": "@tldraw/tlschema!TLArrowShape:type"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"kind": "Content",
|
|
||||||
"text": ">"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"kind": "Content",
|
|
||||||
"text": ";"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"isReadonly": false,
|
|
||||||
"isOptional": false,
|
|
||||||
"releaseTag": "Public",
|
|
||||||
"name": "onHandleDragStart",
|
|
||||||
"propertyTypeTokenRange": {
|
|
||||||
"startIndex": 1,
|
|
||||||
"endIndex": 5
|
|
||||||
},
|
|
||||||
"isStatic": false,
|
|
||||||
"isProtected": false,
|
|
||||||
"isAbstract": false
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"kind": "Property",
|
"kind": "Property",
|
||||||
"canonicalReference": "@tldraw/tldraw!ArrowShapeUtil#onResize:member",
|
"canonicalReference": "@tldraw/tldraw!ArrowShapeUtil#onResize:member",
|
||||||
|
@ -12681,6 +12637,15 @@
|
||||||
"kind": "Content",
|
"kind": "Content",
|
||||||
"text": " | typeof "
|
"text": " | typeof "
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"kind": "Reference",
|
||||||
|
"text": "PointingArrowLabel",
|
||||||
|
"canonicalReference": "@tldraw/tldraw!~PointingArrowLabel:class"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Content",
|
||||||
|
"text": " | typeof "
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"kind": "Reference",
|
"kind": "Reference",
|
||||||
"text": "PointingCanvas",
|
"text": "PointingCanvas",
|
||||||
|
@ -12791,7 +12756,7 @@
|
||||||
"name": "children",
|
"name": "children",
|
||||||
"propertyTypeTokenRange": {
|
"propertyTypeTokenRange": {
|
||||||
"startIndex": 1,
|
"startIndex": 1,
|
||||||
"endIndex": 36
|
"endIndex": 38
|
||||||
},
|
},
|
||||||
"isStatic": true,
|
"isStatic": true,
|
||||||
"isProtected": false,
|
"isProtected": false,
|
||||||
|
|
|
@ -18,7 +18,6 @@ import {
|
||||||
TLHandle,
|
TLHandle,
|
||||||
TLOnEditEndHandler,
|
TLOnEditEndHandler,
|
||||||
TLOnHandleDragHandler,
|
TLOnHandleDragHandler,
|
||||||
TLOnHandleDragStartHandler,
|
|
||||||
TLOnResizeHandler,
|
TLOnResizeHandler,
|
||||||
TLOnTranslateHandler,
|
TLOnTranslateHandler,
|
||||||
TLOnTranslateStartHandler,
|
TLOnTranslateStartHandler,
|
||||||
|
@ -28,10 +27,7 @@ import {
|
||||||
Vec,
|
Vec,
|
||||||
arrowShapeMigrations,
|
arrowShapeMigrations,
|
||||||
arrowShapeProps,
|
arrowShapeProps,
|
||||||
clockwiseAngleDist,
|
|
||||||
counterClockwiseAngleDist,
|
|
||||||
deepCopy,
|
deepCopy,
|
||||||
featureFlags,
|
|
||||||
getArrowTerminalsInArrowSpace,
|
getArrowTerminalsInArrowSpace,
|
||||||
getDefaultColorTheme,
|
getDefaultColorTheme,
|
||||||
mapObjectMapValues,
|
mapObjectMapValues,
|
||||||
|
@ -64,7 +60,6 @@ let globalRenderIndex = 0
|
||||||
enum ARROW_HANDLES {
|
enum ARROW_HANDLES {
|
||||||
START = 'start',
|
START = 'start',
|
||||||
MIDDLE = 'middle',
|
MIDDLE = 'middle',
|
||||||
LABEL = 'middle-text',
|
|
||||||
END = 'end',
|
END = 'end',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,11 +145,6 @@ export class ArrowShapeUtil extends ShapeUtil<TLArrowShape> {
|
||||||
override getHandles(shape: TLArrowShape): TLHandle[] {
|
override getHandles(shape: TLArrowShape): TLHandle[] {
|
||||||
const info = this.editor.getArrowInfo(shape)!
|
const info = this.editor.getArrowInfo(shape)!
|
||||||
|
|
||||||
const hasText = shape.props.text.trim()
|
|
||||||
const labelGeometry = hasText
|
|
||||||
? (this.editor.getShapeGeometry<Group2d>(shape).children[1] as Rectangle2d)
|
|
||||||
: null
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
id: ARROW_HANDLES.START,
|
id: ARROW_HANDLES.START,
|
||||||
|
@ -172,17 +162,6 @@ export class ArrowShapeUtil extends ShapeUtil<TLArrowShape> {
|
||||||
y: info.middle.y,
|
y: info.middle.y,
|
||||||
canBind: false,
|
canBind: false,
|
||||||
},
|
},
|
||||||
featureFlags.canMoveArrowLabel.get() &&
|
|
||||||
labelGeometry && {
|
|
||||||
id: ARROW_HANDLES.LABEL,
|
|
||||||
type: 'text-adjust',
|
|
||||||
index: 'a4',
|
|
||||||
x: labelGeometry.x,
|
|
||||||
y: labelGeometry.y,
|
|
||||||
w: labelGeometry.w,
|
|
||||||
h: labelGeometry.h,
|
|
||||||
canBind: false,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
id: ARROW_HANDLES.END,
|
id: ARROW_HANDLES.END,
|
||||||
type: 'vertex',
|
type: 'vertex',
|
||||||
|
@ -194,19 +173,6 @@ export class ArrowShapeUtil extends ShapeUtil<TLArrowShape> {
|
||||||
].filter(Boolean) as TLHandle[]
|
].filter(Boolean) as TLHandle[]
|
||||||
}
|
}
|
||||||
|
|
||||||
private _labelDragOffset = new Vec(0, 0)
|
|
||||||
override onHandleDragStart: TLOnHandleDragStartHandler<TLArrowShape> = (shape) => {
|
|
||||||
const geometry = this.editor.getShapeGeometry<Group2d>(shape)
|
|
||||||
const labelGeometry = geometry.children[1] as Rectangle2d
|
|
||||||
if (labelGeometry) {
|
|
||||||
const pointInShapeSpace = this.editor.getPointInShapeSpace(
|
|
||||||
shape,
|
|
||||||
this.editor.inputs.currentPagePoint
|
|
||||||
)
|
|
||||||
this._labelDragOffset = Vec.Sub(labelGeometry.center, pointInShapeSpace)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override onHandleDrag: TLOnHandleDragHandler<TLArrowShape> = (shape, { handle, isPrecise }) => {
|
override onHandleDrag: TLOnHandleDragHandler<TLArrowShape> = (shape, { handle, isPrecise }) => {
|
||||||
const handleId = handle.id as ARROW_HANDLES
|
const handleId = handle.id as ARROW_HANDLES
|
||||||
|
|
||||||
|
@ -227,42 +193,6 @@ export class ArrowShapeUtil extends ShapeUtil<TLArrowShape> {
|
||||||
return { id: shape.id, type: shape.type, props: { bend } }
|
return { id: shape.id, type: shape.type, props: { bend } }
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is for moving the text label to a different position on the arrow.
|
|
||||||
if (handleId === ARROW_HANDLES.LABEL) {
|
|
||||||
const next = deepCopy(shape) as TLArrowShape
|
|
||||||
const info = this.editor.getArrowInfo(shape)!
|
|
||||||
|
|
||||||
const geometry = this.editor.getShapeGeometry<Group2d>(shape)
|
|
||||||
const lineGeometry = geometry.children[0] as Geometry2d
|
|
||||||
const pointInShapeSpace = this.editor.getPointInShapeSpace(
|
|
||||||
shape,
|
|
||||||
this.editor.inputs.currentPagePoint
|
|
||||||
)
|
|
||||||
const nearestPoint = lineGeometry.nearestPoint(
|
|
||||||
Vec.Add(pointInShapeSpace, this._labelDragOffset)
|
|
||||||
)
|
|
||||||
|
|
||||||
let nextLabelPosition
|
|
||||||
if (info.isStraight) {
|
|
||||||
const lineLength = Vec.Dist(info.start.point, info.end.point)
|
|
||||||
const segmentLength = Vec.Dist(info.end.point, nearestPoint)
|
|
||||||
nextLabelPosition = 1 - segmentLength / lineLength
|
|
||||||
} else {
|
|
||||||
const isClockwise = shape.props.bend < 0
|
|
||||||
const distFn = isClockwise ? clockwiseAngleDist : counterClockwiseAngleDist
|
|
||||||
|
|
||||||
const angleCenterNearestPoint = Vec.Angle(info.handleArc.center, nearestPoint)
|
|
||||||
const angleCenterStart = Vec.Angle(info.handleArc.center, info.start.point)
|
|
||||||
const angleCenterEnd = Vec.Angle(info.handleArc.center, info.end.point)
|
|
||||||
const arcLength = distFn(angleCenterStart, angleCenterEnd)
|
|
||||||
const segmentLength = distFn(angleCenterNearestPoint, angleCenterEnd)
|
|
||||||
nextLabelPosition = 1 - segmentLength / arcLength
|
|
||||||
}
|
|
||||||
next.props.labelPosition = nextLabelPosition
|
|
||||||
|
|
||||||
return next
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start or end, pointing the arrow...
|
// Start or end, pointing the arrow...
|
||||||
|
|
||||||
const next = deepCopy(shape) as TLArrowShape
|
const next = deepCopy(shape) as TLArrowShape
|
||||||
|
|
|
@ -268,14 +268,28 @@ export function getArrowLabelPosition(editor: Editor, shape: TLArrowShape) {
|
||||||
const debugGeom: Geometry2d[] = []
|
const debugGeom: Geometry2d[] = []
|
||||||
const info = editor.getArrowInfo(shape)!
|
const info = editor.getArrowInfo(shape)!
|
||||||
|
|
||||||
|
const hasStartArrowhead = info.start.arrowhead !== 'none'
|
||||||
|
const hasEndArrowhead = info.end.arrowhead !== 'none'
|
||||||
if (info.isStraight) {
|
if (info.isStraight) {
|
||||||
const range = getStraightArrowLabelRange(editor, shape, info)
|
const range = getStraightArrowLabelRange(editor, shape, info)
|
||||||
const clampedPosition = clamp(shape.props.labelPosition, range.start, range.end)
|
let clampedPosition = clamp(
|
||||||
|
shape.props.labelPosition,
|
||||||
|
hasStartArrowhead ? range.start : 0,
|
||||||
|
hasEndArrowhead ? range.end : 1
|
||||||
|
)
|
||||||
|
// This makes the position snap in the middle.
|
||||||
|
clampedPosition = clampedPosition >= 0.48 && clampedPosition <= 0.52 ? 0.5 : clampedPosition
|
||||||
labelCenter = Vec.Lrp(info.start.point, info.end.point, clampedPosition)
|
labelCenter = Vec.Lrp(info.start.point, info.end.point, clampedPosition)
|
||||||
} else {
|
} else {
|
||||||
const range = getCurvedArrowLabelRange(editor, shape, info)
|
const range = getCurvedArrowLabelRange(editor, shape, info)
|
||||||
if (range.dbg) debugGeom.push(...range.dbg)
|
if (range.dbg) debugGeom.push(...range.dbg)
|
||||||
const clampedPosition = clamp(shape.props.labelPosition, range.start, range.end)
|
let clampedPosition = clamp(
|
||||||
|
shape.props.labelPosition,
|
||||||
|
hasStartArrowhead ? range.start : 0,
|
||||||
|
hasEndArrowhead ? range.end : 1
|
||||||
|
)
|
||||||
|
// This makes the position snap in the middle.
|
||||||
|
clampedPosition = clampedPosition >= 0.48 && clampedPosition <= 0.52 ? 0.5 : clampedPosition
|
||||||
const labelAngle = interpolateArcAngles(
|
const labelAngle = interpolateArcAngles(
|
||||||
Vec.Angle(info.bodyArc.center, info.start.point),
|
Vec.Angle(info.bodyArc.center, info.start.point),
|
||||||
Vec.Angle(info.bodyArc.center, info.end.point),
|
Vec.Angle(info.bodyArc.center, info.end.point),
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { Cropping } from './childStates/Cropping'
|
||||||
import { DraggingHandle } from './childStates/DraggingHandle'
|
import { DraggingHandle } from './childStates/DraggingHandle'
|
||||||
import { EditingShape } from './childStates/EditingShape'
|
import { EditingShape } from './childStates/EditingShape'
|
||||||
import { Idle } from './childStates/Idle'
|
import { Idle } from './childStates/Idle'
|
||||||
|
import { PointingArrowLabel } from './childStates/PointingArrowLabel'
|
||||||
import { PointingCanvas } from './childStates/PointingCanvas'
|
import { PointingCanvas } from './childStates/PointingCanvas'
|
||||||
import { PointingCropHandle } from './childStates/PointingCropHandle'
|
import { PointingCropHandle } from './childStates/PointingCropHandle'
|
||||||
import { PointingHandle } from './childStates/PointingHandle'
|
import { PointingHandle } from './childStates/PointingHandle'
|
||||||
|
@ -39,6 +40,7 @@ export class SelectTool extends StateNode {
|
||||||
Resizing,
|
Resizing,
|
||||||
Rotating,
|
Rotating,
|
||||||
PointingRotateHandle,
|
PointingRotateHandle,
|
||||||
|
PointingArrowLabel,
|
||||||
PointingHandle,
|
PointingHandle,
|
||||||
DraggingHandle,
|
DraggingHandle,
|
||||||
]
|
]
|
||||||
|
|
|
@ -11,7 +11,6 @@ import {
|
||||||
TLPointerEventInfo,
|
TLPointerEventInfo,
|
||||||
TLShapeId,
|
TLShapeId,
|
||||||
TLShapePartial,
|
TLShapePartial,
|
||||||
TLUnknownShape,
|
|
||||||
Vec,
|
Vec,
|
||||||
deepCopy,
|
deepCopy,
|
||||||
snapAngle,
|
snapAngle,
|
||||||
|
@ -110,15 +109,6 @@ export class DraggingHandle extends StateNode {
|
||||||
}
|
}
|
||||||
// -->
|
// -->
|
||||||
|
|
||||||
const util = this.editor.getShapeUtil(shape)
|
|
||||||
const changes = util.onHandleDragStart?.(shape)
|
|
||||||
|
|
||||||
const next: TLShapePartial<any> = { ...shape, ...changes }
|
|
||||||
|
|
||||||
if (changes) {
|
|
||||||
this.editor.updateShapes([next], { squashing: true })
|
|
||||||
}
|
|
||||||
|
|
||||||
this.update()
|
this.update()
|
||||||
|
|
||||||
this.editor.select(this.shapeId)
|
this.editor.select(this.shapeId)
|
||||||
|
@ -180,18 +170,6 @@ export class DraggingHandle extends StateNode {
|
||||||
this.editor.setHintingShapes([])
|
this.editor.setHintingShapes([])
|
||||||
this.editor.snaps.clear()
|
this.editor.snaps.clear()
|
||||||
|
|
||||||
const { editor, shapeId } = this
|
|
||||||
const shape = editor.getShape(shapeId) as TLArrowShape | (TLUnknownShape & TLArrowShape)
|
|
||||||
|
|
||||||
if (shape) {
|
|
||||||
const util = this.editor.getShapeUtil(shape)
|
|
||||||
const changes = util.onHandleDragEnd?.(shape)
|
|
||||||
const next: TLShapePartial<any> = { ...shape, ...changes }
|
|
||||||
if (changes) {
|
|
||||||
this.editor.updateShapes([next], { squashing: true })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.editor.updateInstanceState(
|
this.editor.updateInstanceState(
|
||||||
{ cursor: { type: 'default', rotation: 0 } },
|
{ cursor: { type: 'default', rotation: 0 } },
|
||||||
{ ephemeral: true }
|
{ ephemeral: true }
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
import {
|
import {
|
||||||
Editor,
|
Editor,
|
||||||
|
Group2d,
|
||||||
HIT_TEST_MARGIN,
|
HIT_TEST_MARGIN,
|
||||||
StateNode,
|
StateNode,
|
||||||
|
TLArrowShape,
|
||||||
TLClickEventInfo,
|
TLClickEventInfo,
|
||||||
TLEventHandlers,
|
TLEventHandlers,
|
||||||
TLGroupShape,
|
TLGroupShape,
|
||||||
|
@ -90,7 +92,25 @@ export class Idle extends StateNode {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case 'shape': {
|
case 'shape': {
|
||||||
if (this.editor.isShapeOrAncestorLocked(info.shape)) {
|
const { shape } = info
|
||||||
|
const pointInShapeSpace = this.editor.getPointInShapeSpace(
|
||||||
|
shape,
|
||||||
|
this.editor.inputs.currentPagePoint
|
||||||
|
)
|
||||||
|
// todo: Extract into general hit test for arrows
|
||||||
|
if (this.editor.isShapeOfType<TLArrowShape>(shape, 'arrow')) {
|
||||||
|
// How should we handle multiple labels? Do shapes ever have multiple labels?
|
||||||
|
const labelGeometry = this.editor.getShapeGeometry<Group2d>(shape).children[1]
|
||||||
|
// Knowing what we know about arrows... if the shape has no text in its label,
|
||||||
|
// then the label geometry should not be there.
|
||||||
|
if (labelGeometry && pointInPolygon(pointInShapeSpace, labelGeometry.vertices)) {
|
||||||
|
// We're moving the label on a shape.
|
||||||
|
this.parent.transition('pointing_arrow_label', info)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.editor.isShapeOrAncestorLocked(shape)) {
|
||||||
this.parent.transition('pointing_canvas', info)
|
this.parent.transition('pointing_canvas', info)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,143 @@
|
||||||
|
import {
|
||||||
|
Arc2d,
|
||||||
|
Geometry2d,
|
||||||
|
Group2d,
|
||||||
|
StateNode,
|
||||||
|
TLArrowShape,
|
||||||
|
TLEventHandlers,
|
||||||
|
TLPointerEventInfo,
|
||||||
|
TLShapeId,
|
||||||
|
Vec,
|
||||||
|
getPointInArcT,
|
||||||
|
} from '@tldraw/editor'
|
||||||
|
|
||||||
|
export class PointingArrowLabel extends StateNode {
|
||||||
|
static override id = 'pointing_arrow_label'
|
||||||
|
|
||||||
|
shapeId = '' as TLShapeId
|
||||||
|
markId = ''
|
||||||
|
|
||||||
|
private info = {} as TLPointerEventInfo & {
|
||||||
|
shape: TLArrowShape
|
||||||
|
onInteractionEnd?: string
|
||||||
|
isCreating: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
private updateCursor() {
|
||||||
|
this.editor.updateInstanceState({
|
||||||
|
cursor: {
|
||||||
|
type: 'grabbing',
|
||||||
|
rotation: 0,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
override onEnter = (
|
||||||
|
info: TLPointerEventInfo & {
|
||||||
|
shape: TLArrowShape
|
||||||
|
onInteractionEnd?: string
|
||||||
|
isCreating: boolean
|
||||||
|
}
|
||||||
|
) => {
|
||||||
|
const { shape } = info
|
||||||
|
this.parent.setCurrentToolIdMask(info.onInteractionEnd)
|
||||||
|
this.info = info
|
||||||
|
this.shapeId = shape.id
|
||||||
|
this.updateCursor()
|
||||||
|
|
||||||
|
const geometry = this.editor.getShapeGeometry<Group2d>(shape)
|
||||||
|
const labelGeometry = geometry.children[1]
|
||||||
|
if (!labelGeometry) {
|
||||||
|
throw Error(`Expected to find an arrow label geometry for shape: ${shape.id}`)
|
||||||
|
}
|
||||||
|
const { currentPagePoint } = this.editor.inputs
|
||||||
|
const pointInShapeSpace = this.editor.getPointInShapeSpace(shape, currentPagePoint)
|
||||||
|
|
||||||
|
this._labelDragOffset = Vec.Sub(labelGeometry.center, pointInShapeSpace)
|
||||||
|
|
||||||
|
this.markId = 'label-drag start'
|
||||||
|
this.editor.mark(this.markId)
|
||||||
|
this.editor.setSelectedShapes([this.shapeId])
|
||||||
|
}
|
||||||
|
|
||||||
|
override onExit = () => {
|
||||||
|
this.parent.setCurrentToolIdMask(undefined)
|
||||||
|
|
||||||
|
this.editor.updateInstanceState(
|
||||||
|
{ cursor: { type: 'default', rotation: 0 } },
|
||||||
|
{ ephemeral: true }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private _labelDragOffset = new Vec(0, 0)
|
||||||
|
|
||||||
|
override onPointerMove = () => {
|
||||||
|
const { isDragging } = this.editor.inputs
|
||||||
|
if (!isDragging) return
|
||||||
|
|
||||||
|
const shape = this.editor.getShape<TLArrowShape>(this.shapeId)
|
||||||
|
if (!shape) return
|
||||||
|
|
||||||
|
const info = this.editor.getArrowInfo(shape)!
|
||||||
|
|
||||||
|
const groupGeometry = this.editor.getShapeGeometry<Group2d>(shape)
|
||||||
|
const bodyGeometry = groupGeometry.children[0] as Geometry2d
|
||||||
|
const pointInShapeSpace = this.editor.getPointInShapeSpace(
|
||||||
|
shape,
|
||||||
|
this.editor.inputs.currentPagePoint
|
||||||
|
)
|
||||||
|
const nearestPoint = bodyGeometry.nearestPoint(
|
||||||
|
Vec.Add(pointInShapeSpace, this._labelDragOffset)
|
||||||
|
)
|
||||||
|
|
||||||
|
let nextLabelPosition
|
||||||
|
if (info.isStraight) {
|
||||||
|
// straight arrows
|
||||||
|
const lineLength = Vec.Dist(info.start.point, info.end.point)
|
||||||
|
const segmentLength = Vec.Dist(info.end.point, nearestPoint)
|
||||||
|
nextLabelPosition = 1 - segmentLength / lineLength
|
||||||
|
} else {
|
||||||
|
const { _center, measure, angleEnd, angleStart } = groupGeometry.children[0] as Arc2d
|
||||||
|
nextLabelPosition = getPointInArcT(measure, angleStart, angleEnd, _center.angle(nearestPoint))
|
||||||
|
}
|
||||||
|
|
||||||
|
this.editor.updateShape<TLArrowShape>(
|
||||||
|
{ id: shape.id, type: shape.type, props: { labelPosition: nextLabelPosition } },
|
||||||
|
{ squashing: true }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override onPointerUp = () => {
|
||||||
|
this.complete()
|
||||||
|
}
|
||||||
|
|
||||||
|
override onCancel: TLEventHandlers['onCancel'] = () => {
|
||||||
|
this.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
override onComplete: TLEventHandlers['onComplete'] = () => {
|
||||||
|
this.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
override onInterrupt = () => {
|
||||||
|
this.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
private complete() {
|
||||||
|
if (this.info.onInteractionEnd) {
|
||||||
|
this.editor.setCurrentTool(this.info.onInteractionEnd, {})
|
||||||
|
} else {
|
||||||
|
this.parent.transition('idle')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private cancel() {
|
||||||
|
this.editor.bailToMark(this.markId)
|
||||||
|
|
||||||
|
if (this.info.onInteractionEnd) {
|
||||||
|
this.editor.setCurrentTool(this.info.onInteractionEnd, {})
|
||||||
|
} else {
|
||||||
|
this.parent.transition('idle')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,6 +7,7 @@ const ids = {
|
||||||
box1: createShapeId('box1'),
|
box1: createShapeId('box1'),
|
||||||
line1: createShapeId('line1'),
|
line1: createShapeId('line1'),
|
||||||
embed1: createShapeId('embed1'),
|
embed1: createShapeId('embed1'),
|
||||||
|
arrow1: createShapeId('arrow1'),
|
||||||
}
|
}
|
||||||
|
|
||||||
jest.useFakeTimers()
|
jest.useFakeTimers()
|
||||||
|
@ -166,6 +167,56 @@ describe('DraggingHandle', () => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('PointingLabel', () => {
|
||||||
|
it('Enters from pointing_arrow_label and exits to idle', () => {
|
||||||
|
editor.createShapes([
|
||||||
|
{ id: ids.arrow1, type: 'arrow', x: 100, y: 100, props: { text: 'Test Label' } },
|
||||||
|
])
|
||||||
|
const shape = editor.getShape(ids.arrow1)
|
||||||
|
editor.pointerDown(150, 150, {
|
||||||
|
target: 'shape',
|
||||||
|
shape,
|
||||||
|
})
|
||||||
|
editor.pointerMove(100, 100)
|
||||||
|
editor.expectToBeIn('select.pointing_arrow_label')
|
||||||
|
|
||||||
|
editor.pointerUp()
|
||||||
|
editor.expectToBeIn('select.idle')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Bails on escape', () => {
|
||||||
|
editor.createShapes([
|
||||||
|
{ id: ids.arrow1, type: 'arrow', x: 100, y: 100, props: { text: 'Test Label' } },
|
||||||
|
])
|
||||||
|
const shape = editor.getShape(ids.arrow1)
|
||||||
|
|
||||||
|
editor.pointerDown(150, 150, {
|
||||||
|
target: 'shape',
|
||||||
|
shape,
|
||||||
|
})
|
||||||
|
editor.pointerMove(100, 100)
|
||||||
|
editor.expectToBeIn('select.pointing_arrow_label')
|
||||||
|
editor.cancel()
|
||||||
|
editor.expectToBeIn('select.idle')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Doesnt go into pointing_arrow_label mode if not selecting the arrow shape', () => {
|
||||||
|
editor.createShapes([
|
||||||
|
{ id: ids.arrow1, type: 'arrow', x: 100, y: 100, props: { text: 'Test Label' } },
|
||||||
|
])
|
||||||
|
const shape = editor.getShape(ids.arrow1)
|
||||||
|
editor.pointerDown(0, 150, {
|
||||||
|
target: 'shape',
|
||||||
|
shape,
|
||||||
|
})
|
||||||
|
editor.pointerMove(100, 100)
|
||||||
|
editor.expectToBeIn('select.translating')
|
||||||
|
|
||||||
|
editor.pointerUp()
|
||||||
|
editor.expectToBeIn('select.idle')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe('When double clicking a shape', () => {
|
describe('When double clicking a shape', () => {
|
||||||
it('begins editing a geo shapes label', () => {
|
it('begins editing a geo shapes label', () => {
|
||||||
editor
|
editor
|
||||||
|
|
|
@ -960,16 +960,12 @@ export interface TLHandle {
|
||||||
canBind?: boolean;
|
canBind?: boolean;
|
||||||
// (undocumented)
|
// (undocumented)
|
||||||
canSnap?: boolean;
|
canSnap?: boolean;
|
||||||
// (undocumented)
|
|
||||||
h?: number;
|
|
||||||
id: string;
|
id: string;
|
||||||
// (undocumented)
|
// (undocumented)
|
||||||
index: string;
|
index: string;
|
||||||
// (undocumented)
|
// (undocumented)
|
||||||
type: TLHandleType;
|
type: TLHandleType;
|
||||||
// (undocumented)
|
// (undocumented)
|
||||||
w?: number;
|
|
||||||
// (undocumented)
|
|
||||||
x: number;
|
x: number;
|
||||||
// (undocumented)
|
// (undocumented)
|
||||||
y: number;
|
y: number;
|
||||||
|
|
|
@ -6027,33 +6027,6 @@
|
||||||
"endIndex": 2
|
"endIndex": 2
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"kind": "PropertySignature",
|
|
||||||
"canonicalReference": "@tldraw/tlschema!TLHandle#h:member",
|
|
||||||
"docComment": "",
|
|
||||||
"excerptTokens": [
|
|
||||||
{
|
|
||||||
"kind": "Content",
|
|
||||||
"text": "h?: "
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"kind": "Content",
|
|
||||||
"text": "number"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"kind": "Content",
|
|
||||||
"text": ";"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"isReadonly": false,
|
|
||||||
"isOptional": true,
|
|
||||||
"releaseTag": "Public",
|
|
||||||
"name": "h",
|
|
||||||
"propertyTypeTokenRange": {
|
|
||||||
"startIndex": 1,
|
|
||||||
"endIndex": 2
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"kind": "PropertySignature",
|
"kind": "PropertySignature",
|
||||||
"canonicalReference": "@tldraw/tlschema!TLHandle#id:member",
|
"canonicalReference": "@tldraw/tlschema!TLHandle#id:member",
|
||||||
|
@ -6136,33 +6109,6 @@
|
||||||
"endIndex": 2
|
"endIndex": 2
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"kind": "PropertySignature",
|
|
||||||
"canonicalReference": "@tldraw/tlschema!TLHandle#w:member",
|
|
||||||
"docComment": "",
|
|
||||||
"excerptTokens": [
|
|
||||||
{
|
|
||||||
"kind": "Content",
|
|
||||||
"text": "w?: "
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"kind": "Content",
|
|
||||||
"text": "number"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"kind": "Content",
|
|
||||||
"text": ";"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"isReadonly": false,
|
|
||||||
"isOptional": true,
|
|
||||||
"releaseTag": "Public",
|
|
||||||
"name": "w",
|
|
||||||
"propertyTypeTokenRange": {
|
|
||||||
"startIndex": 1,
|
|
||||||
"endIndex": 2
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"kind": "PropertySignature",
|
"kind": "PropertySignature",
|
||||||
"canonicalReference": "@tldraw/tlschema!TLHandle#x:member",
|
"canonicalReference": "@tldraw/tlschema!TLHandle#x:member",
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { SetValue } from '../util-types'
|
||||||
* The handle types used by tldraw's default shapes.
|
* The handle types used by tldraw's default shapes.
|
||||||
*
|
*
|
||||||
* @public */
|
* @public */
|
||||||
export const TL_HANDLE_TYPES = new Set(['vertex', 'virtual', 'create', 'text-adjust'] as const)
|
export const TL_HANDLE_TYPES = new Set(['vertex', 'virtual', 'create'] as const)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A type for the handle types used by tldraw's default shapes.
|
* A type for the handle types used by tldraw's default shapes.
|
||||||
|
@ -27,8 +27,6 @@ export interface TLHandle {
|
||||||
index: string
|
index: string
|
||||||
x: number
|
x: number
|
||||||
y: number
|
y: number
|
||||||
w?: number
|
|
||||||
h?: number
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
|
@ -40,6 +38,4 @@ export const handleValidator: T.Validator<TLHandle> = T.object({
|
||||||
index: T.string,
|
index: T.string,
|
||||||
x: T.number,
|
x: T.number,
|
||||||
y: T.number,
|
y: T.number,
|
||||||
w: T.optional(T.number),
|
|
||||||
h: T.optional(T.number),
|
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in a new issue