@edv4h/usketch-plugin-shape-text 3.0.20 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dom-text-measurer.d.ts +11 -0
- package/dist/dom-text-measurer.d.ts.map +1 -0
- package/dist/dom-text-measurer.js +44 -0
- package/dist/dom-text-measurer.js.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/plugin.d.ts +6 -0
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +40 -45
- package/dist/plugin.js.map +1 -1
- package/dist/text-sizing.d.ts +70 -0
- package/dist/text-sizing.d.ts.map +1 -0
- package/dist/text-sizing.js +168 -0
- package/dist/text-sizing.js.map +1 -0
- package/package.json +5 -5
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { TextMeasurer } from "./text-sizing.js";
|
|
2
|
+
/**
|
|
3
|
+
* Measures unwrapped text with a hidden element styled like the text shape's
|
|
4
|
+
* renderer (padding 4, line-height 1.4, border-box). Lives outside the canvas
|
|
5
|
+
* so the camera zoom doesn't scale the result.
|
|
6
|
+
*/
|
|
7
|
+
export declare function createDomTextMeasurer(): {
|
|
8
|
+
measure: TextMeasurer;
|
|
9
|
+
dispose: () => void;
|
|
10
|
+
};
|
|
11
|
+
//# sourceMappingURL=dom-text-measurer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dom-text-measurer.d.ts","sourceRoot":"","sources":["../src/dom-text-measurer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAErD;;;;GAIG;AACH,wBAAgB,qBAAqB,IAAI;IAAE,OAAO,EAAE,YAAY,CAAC;IAAC,OAAO,EAAE,MAAM,IAAI,CAAA;CAAE,CAsCtF"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Measures unwrapped text with a hidden element styled like the text shape's
|
|
3
|
+
* renderer (padding 4, line-height 1.4, border-box). Lives outside the canvas
|
|
4
|
+
* so the camera zoom doesn't scale the result.
|
|
5
|
+
*/
|
|
6
|
+
export function createDomTextMeasurer() {
|
|
7
|
+
let el = null;
|
|
8
|
+
const measure = ({ text, fontSize, fontFamily }) => {
|
|
9
|
+
if (typeof document === "undefined")
|
|
10
|
+
return { width: 0, height: 0 };
|
|
11
|
+
if (!el) {
|
|
12
|
+
el = document.createElement("div");
|
|
13
|
+
Object.assign(el.style, {
|
|
14
|
+
position: "absolute",
|
|
15
|
+
left: "-99999px",
|
|
16
|
+
top: "0",
|
|
17
|
+
visibility: "hidden",
|
|
18
|
+
pointerEvents: "none",
|
|
19
|
+
display: "inline-block",
|
|
20
|
+
whiteSpace: "pre",
|
|
21
|
+
lineHeight: "1.4",
|
|
22
|
+
padding: "4px",
|
|
23
|
+
boxSizing: "border-box",
|
|
24
|
+
});
|
|
25
|
+
el.setAttribute("aria-hidden", "true");
|
|
26
|
+
document.body.appendChild(el);
|
|
27
|
+
}
|
|
28
|
+
el.style.fontSize = `${fontSize}px`;
|
|
29
|
+
el.style.fontFamily = fontFamily;
|
|
30
|
+
// An empty text or a trailing newline still occupies a line in the editor;
|
|
31
|
+
// a zero-width space keeps that line measurable.
|
|
32
|
+
el.textContent = text === "" || text.endsWith("\n") ? `${text}` : text;
|
|
33
|
+
const rect = el.getBoundingClientRect();
|
|
34
|
+
return { width: rect.width, height: rect.height };
|
|
35
|
+
};
|
|
36
|
+
return {
|
|
37
|
+
measure,
|
|
38
|
+
dispose() {
|
|
39
|
+
el?.remove();
|
|
40
|
+
el = null;
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=dom-text-measurer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dom-text-measurer.js","sourceRoot":"","sources":["../src/dom-text-measurer.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,UAAU,qBAAqB;IACpC,IAAI,EAAE,GAA0B,IAAI,CAAC;IAErC,MAAM,OAAO,GAAiB,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,EAAE,EAAE;QAChE,IAAI,OAAO,QAAQ,KAAK,WAAW;YAAE,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;QACpE,IAAI,CAAC,EAAE,EAAE,CAAC;YACT,EAAE,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;YACnC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE;gBACvB,QAAQ,EAAE,UAAU;gBACpB,IAAI,EAAE,UAAU;gBAChB,GAAG,EAAE,GAAG;gBACR,UAAU,EAAE,QAAQ;gBACpB,aAAa,EAAE,MAAM;gBACrB,OAAO,EAAE,cAAc;gBACvB,UAAU,EAAE,KAAK;gBACjB,UAAU,EAAE,KAAK;gBACjB,OAAO,EAAE,KAAK;gBACd,SAAS,EAAE,YAAY;aACvB,CAAC,CAAC;YACH,EAAE,CAAC,YAAY,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;YACvC,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;QAC/B,CAAC;QACD,EAAE,CAAC,KAAK,CAAC,QAAQ,GAAG,GAAG,QAAQ,IAAI,CAAC;QACpC,EAAE,CAAC,KAAK,CAAC,UAAU,GAAG,UAAU,CAAC;QACjC,2EAA2E;QAC3E,iDAAiD;QACjD,EAAE,CAAC,WAAW,GAAG,IAAI,KAAK,EAAE,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;QACxE,MAAM,IAAI,GAAG,EAAE,CAAC,qBAAqB,EAAE,CAAC;QACxC,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;IACnD,CAAC,CAAC;IAEF,OAAO;QACN,OAAO;QACP,OAAO;YACN,EAAE,EAAE,MAAM,EAAE,CAAC;YACb,EAAE,GAAG,IAAI,CAAC;QACX,CAAC;KACD,CAAC;AACH,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
export { createTextPlugin } from "./plugin.js";
|
|
1
|
+
export { createTextPlugin, type TextShapeData } from "./plugin.js";
|
|
2
|
+
export { AUTO_WIDTH_MIN, applyTextBounds, computeAutoFitPatch, fitTextOnInput, fitTextToContent, keepTopLeft, resizeText, type TextMeasurer, WIDTH_JITTER, } from "./text-sizing.js";
|
|
2
3
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AACnE,OAAO,EACN,cAAc,EACd,eAAe,EACf,mBAAmB,EACnB,cAAc,EACd,gBAAgB,EAChB,WAAW,EACX,UAAU,EACV,KAAK,YAAY,EACjB,YAAY,GACZ,MAAM,kBAAkB,CAAC"}
|
package/dist/index.js
CHANGED
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAsB,MAAM,aAAa,CAAC;AACnE,OAAO,EACN,cAAc,EACd,eAAe,EACf,mBAAmB,EACnB,cAAc,EACd,gBAAgB,EAChB,WAAW,EACX,UAAU,EAEV,YAAY,GACZ,MAAM,kBAAkB,CAAC"}
|
package/dist/plugin.d.ts
CHANGED
|
@@ -5,6 +5,12 @@ export interface TextShapeData extends ShapeData {
|
|
|
5
5
|
fontSize: number;
|
|
6
6
|
fontFamily: string;
|
|
7
7
|
isEditing: boolean;
|
|
8
|
+
/**
|
|
9
|
+
* true: auto width (no wrapping, width follows the longest line).
|
|
10
|
+
* false / absent: fixed width (wraps). Texts saved before auto width
|
|
11
|
+
* existed have no flag and keep their fixed width.
|
|
12
|
+
*/
|
|
13
|
+
autoWidth?: boolean;
|
|
8
14
|
}
|
|
9
15
|
export declare function createTextPlugin(): UsketchPlugin;
|
|
10
16
|
//# sourceMappingURL=plugin.d.ts.map
|
package/dist/plugin.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.tsx"],"names":[],"mappings":"AACA,OAAO,EAQN,KAAK,SAAS,EAEd,KAAK,aAAa,EAElB,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.tsx"],"names":[],"mappings":"AACA,OAAO,EAQN,KAAK,SAAS,EAEd,KAAK,aAAa,EAElB,MAAM,uBAAuB,CAAC;AAY/B,iEAAiE;AACjE,MAAM,WAAW,aAAc,SAAQ,SAAS;IAC/C,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,OAAO,CAAC;IACnB;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACpB;AAmJD,wBAAgB,gBAAgB,IAAI,aAAa,CA+EhD"}
|
package/dist/plugin.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { createEditableTextController, editableTextProps } from "@edv4h/usketch-shape-utils";
|
|
3
|
-
import { DEFAULT_STYLE, generateId, withRotation, } from "@edv4h/usketch-shared";
|
|
3
|
+
import { DEFAULT_STYLE, generateId, getContainerLayout, withRotation, } from "@edv4h/usketch-shared";
|
|
4
4
|
import { createAddShapeCommand } from "@edv4h/usketch-store";
|
|
5
|
+
import { createDomTextMeasurer } from "./dom-text-measurer.js";
|
|
6
|
+
import { AUTO_WIDTH_MIN, applyTextBounds, fitTextOnInput, fitTextToContent, isSideHandle, resizeText, } from "./text-sizing.js";
|
|
5
7
|
/**
|
|
6
8
|
* LOD component: first line of text, truncated. Dropped editing + focus ring.
|
|
7
9
|
*/
|
|
@@ -31,8 +33,9 @@ function SimplifiedText({ shape }) {
|
|
|
31
33
|
const textStyle = (data) => ({
|
|
32
34
|
width: "100%",
|
|
33
35
|
height: "100%",
|
|
34
|
-
|
|
35
|
-
|
|
36
|
+
// Auto width never wraps — the shape widens to the longest line instead.
|
|
37
|
+
whiteSpace: data.autoWidth ? "pre" : "pre-wrap",
|
|
38
|
+
wordBreak: data.autoWidth ? undefined : "break-word",
|
|
36
39
|
outline: "none",
|
|
37
40
|
fontFamily: data.fontFamily ?? "system-ui, sans-serif",
|
|
38
41
|
fontSize: data.fontSize ?? 16,
|
|
@@ -58,45 +61,12 @@ function hitTest(data, point) {
|
|
|
58
61
|
point.y >= data.y &&
|
|
59
62
|
point.y <= data.y + data.height);
|
|
60
63
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
break;
|
|
68
|
-
case "nw":
|
|
69
|
-
x += delta.x;
|
|
70
|
-
y += delta.y;
|
|
71
|
-
width -= delta.x;
|
|
72
|
-
height -= delta.y;
|
|
73
|
-
break;
|
|
74
|
-
case "ne":
|
|
75
|
-
y += delta.y;
|
|
76
|
-
width += delta.x;
|
|
77
|
-
height -= delta.y;
|
|
78
|
-
break;
|
|
79
|
-
case "sw":
|
|
80
|
-
x += delta.x;
|
|
81
|
-
width -= delta.x;
|
|
82
|
-
height += delta.y;
|
|
83
|
-
break;
|
|
84
|
-
case "e":
|
|
85
|
-
width += delta.x;
|
|
86
|
-
break;
|
|
87
|
-
case "w":
|
|
88
|
-
x += delta.x;
|
|
89
|
-
width -= delta.x;
|
|
90
|
-
break;
|
|
91
|
-
case "n":
|
|
92
|
-
y += delta.y;
|
|
93
|
-
height -= delta.y;
|
|
94
|
-
break;
|
|
95
|
-
case "s":
|
|
96
|
-
height += delta.y;
|
|
97
|
-
break;
|
|
98
|
-
}
|
|
99
|
-
return { ...data, x, y, width: Math.max(40, width), height: Math.max(24, height) };
|
|
64
|
+
/** Whether the shape's parent is a container that lays out (and sizes) its children. */
|
|
65
|
+
function isInLayoutContainer(ctx, shape) {
|
|
66
|
+
if (typeof shape.parentId !== "string")
|
|
67
|
+
return false;
|
|
68
|
+
const parent = ctx.store.getShape(shape.parentId);
|
|
69
|
+
return !!parent && !!getContainerLayout(ctx.shapes.get(parent.type));
|
|
100
70
|
}
|
|
101
71
|
function createDefault(params) {
|
|
102
72
|
return {
|
|
@@ -126,6 +96,7 @@ function debugFields(shape) {
|
|
|
126
96
|
fontSize: data.fontSize ?? 16,
|
|
127
97
|
fontFamily: data.fontFamily ?? "system-ui, sans-serif",
|
|
128
98
|
isEditing: data.isEditing ?? false,
|
|
99
|
+
autoWidth: data.autoWidth ?? false,
|
|
129
100
|
};
|
|
130
101
|
}
|
|
131
102
|
// ── Icon ──
|
|
@@ -138,6 +109,7 @@ export function createTextPlugin() {
|
|
|
138
109
|
id: "usketch-plugin-shape-text",
|
|
139
110
|
name: "テキスト",
|
|
140
111
|
setup(ctx) {
|
|
112
|
+
const measurer = createDomTextMeasurer();
|
|
141
113
|
// Shared editable-text controller (machine + DOM/canvas wiring).
|
|
142
114
|
const editor = createEditableTextController(ctx, {
|
|
143
115
|
isEditableType: (type) => type === "text",
|
|
@@ -145,19 +117,35 @@ export function createTextPlugin() {
|
|
|
145
117
|
growHeight: true,
|
|
146
118
|
minHeight: 28,
|
|
147
119
|
deleteWhenEmpty: true,
|
|
120
|
+
fitOnInput: (shape, text, next) => fitTextOnInput(shape, text, measurer.measure, {
|
|
121
|
+
height: next.height,
|
|
122
|
+
laidOut: isInLayoutContainer(ctx, shape),
|
|
123
|
+
}),
|
|
148
124
|
});
|
|
149
125
|
// ── Shape registration ──
|
|
150
126
|
ctx.shapes.register("text", {
|
|
151
127
|
render,
|
|
152
128
|
getBounds,
|
|
153
129
|
hitTest: withRotation(hitTest),
|
|
154
|
-
resize,
|
|
130
|
+
resize: resizeText,
|
|
131
|
+
applyBounds: applyTextBounds,
|
|
155
132
|
createDefault,
|
|
156
133
|
renderTarget: "html",
|
|
157
134
|
minSize: { width: 40, height: 24 },
|
|
158
135
|
simplifiedComponent: SimplifiedText,
|
|
159
136
|
serializeForAi,
|
|
160
137
|
debugFields,
|
|
138
|
+
onHandleDoubleClick(toolCtx, data, handle) {
|
|
139
|
+
if (!isSideHandle(handle))
|
|
140
|
+
return false;
|
|
141
|
+
const command = fitTextToContent(toolCtx.store, data.id, measurer.measure);
|
|
142
|
+
if (command)
|
|
143
|
+
toolCtx.commands.execute(command);
|
|
144
|
+
// The canvas emits this pointer-down to the editor right after the
|
|
145
|
+
// tool; without this it would also count as a double-click to edit.
|
|
146
|
+
editor.cancelPendingClick();
|
|
147
|
+
return true;
|
|
148
|
+
},
|
|
161
149
|
});
|
|
162
150
|
// ── Draw tool registration ──
|
|
163
151
|
ctx.tools.register("text-draw", {
|
|
@@ -168,8 +156,14 @@ export function createTextPlugin() {
|
|
|
168
156
|
onPointerDown(toolCtx, event) {
|
|
169
157
|
const id = generateId();
|
|
170
158
|
const defaults = createDefault({ id, x: event.worldPoint.x, y: event.worldPoint.y });
|
|
171
|
-
// Anchor: left-center (shift Y up by half height)
|
|
172
|
-
|
|
159
|
+
// Anchor: left-center (shift Y up by half height). New text starts
|
|
160
|
+
// in auto width and grows to the right as the user types.
|
|
161
|
+
const shape = {
|
|
162
|
+
...defaults,
|
|
163
|
+
y: defaults.y - defaults.height / 2,
|
|
164
|
+
autoWidth: true,
|
|
165
|
+
width: AUTO_WIDTH_MIN,
|
|
166
|
+
};
|
|
173
167
|
toolCtx.commands.execute(createAddShapeCommand(toolCtx.store, shape));
|
|
174
168
|
toolCtx.store.setSelection([id]);
|
|
175
169
|
toolCtx.store.resetToDefaultTool();
|
|
@@ -181,6 +175,7 @@ export function createTextPlugin() {
|
|
|
181
175
|
// ── Teardown ──
|
|
182
176
|
return () => {
|
|
183
177
|
editor.teardown();
|
|
178
|
+
measurer.dispose();
|
|
184
179
|
};
|
|
185
180
|
},
|
|
186
181
|
};
|
package/dist/plugin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,4BAA4B,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAC7F,OAAO,EAGN,aAAa,EACb,UAAU,
|
|
1
|
+
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { type BoardStore, type BoundingBox, type Command, type Point, type ResizeHandle, type ShapeData } from "@edv4h/usketch-shared";
|
|
2
|
+
/**
|
|
3
|
+
* Figma-style text sizing.
|
|
4
|
+
*
|
|
5
|
+
* - `autoWidth: true` — no wrapping; the width follows the longest line.
|
|
6
|
+
* - `autoWidth: false` / absent — fixed width; text wraps inside it. Text shapes
|
|
7
|
+
* saved before auto width existed have no flag and stay fixed width.
|
|
8
|
+
*/
|
|
9
|
+
/** Smallest auto width, so an empty text still shows its caret. */
|
|
10
|
+
export declare const AUTO_WIDTH_MIN = 16;
|
|
11
|
+
/**
|
|
12
|
+
* Width changes smaller than this (world px) are hand jitter — e.g. the first
|
|
13
|
+
* click of a handle double-click — and keep the width (and auto width) as is.
|
|
14
|
+
*/
|
|
15
|
+
export declare const WIDTH_JITTER = 2;
|
|
16
|
+
/** Unwrapped box size (padding included) of `text` rendered in the given font. */
|
|
17
|
+
export type TextMeasurer = (input: {
|
|
18
|
+
text: string;
|
|
19
|
+
fontSize: number;
|
|
20
|
+
fontFamily: string;
|
|
21
|
+
}) => {
|
|
22
|
+
width: number;
|
|
23
|
+
height: number;
|
|
24
|
+
};
|
|
25
|
+
type TextLike = ShapeData & {
|
|
26
|
+
text?: string;
|
|
27
|
+
fontSize?: number;
|
|
28
|
+
fontFamily?: string;
|
|
29
|
+
autoWidth?: boolean;
|
|
30
|
+
};
|
|
31
|
+
/** Handles that change the width — dragging one turns auto width into fixed width. */
|
|
32
|
+
export declare function isHorizontalHandle(handle: ResizeHandle): boolean;
|
|
33
|
+
/** Handles whose double-click returns the text to auto width. */
|
|
34
|
+
export declare function isSideHandle(handle: ResizeHandle): boolean;
|
|
35
|
+
export declare function resizeText(data: ShapeData, handle: ResizeHandle, delta: Point): ShapeData;
|
|
36
|
+
/**
|
|
37
|
+
* `ShapeDefinition.applyBounds` for multi-selection resize, which bypasses
|
|
38
|
+
* `resize`: a real width change turns auto width into fixed width there too.
|
|
39
|
+
*/
|
|
40
|
+
export declare function applyTextBounds(data: ShapeData, bounds: BoundingBox): Partial<ShapeData>;
|
|
41
|
+
/**
|
|
42
|
+
* Position that keeps a rotated shape's top-left corner where it is when its
|
|
43
|
+
* size changes to `width` × `height`. A shape rotates around its center, so a
|
|
44
|
+
* new size alone would shift the visible left / top edges. Null when unrotated.
|
|
45
|
+
*/
|
|
46
|
+
export declare function keepTopLeft(data: ShapeData, width: number, height: number): Point | null;
|
|
47
|
+
/** Patch that returns the text to auto width, fitted to its content. */
|
|
48
|
+
export declare function computeAutoFitPatch(data: ShapeData, measure: TextMeasurer): {
|
|
49
|
+
autoWidth: true;
|
|
50
|
+
width: number;
|
|
51
|
+
height: number;
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* Geometry patch applied while typing. Auto-width text follows its content.
|
|
55
|
+
* A rotated text keeps its top-left corner in place as it grows (`height` is
|
|
56
|
+
* the height the editor is about to apply). Inside a container that lays out
|
|
57
|
+
* its children (and so sets their width), the text switches to fixed width
|
|
58
|
+
* instead of fighting the layout over the width.
|
|
59
|
+
*/
|
|
60
|
+
export declare function fitTextOnInput(data: ShapeData, text: string, measure: TextMeasurer, options?: {
|
|
61
|
+
height?: number;
|
|
62
|
+
laidOut?: boolean;
|
|
63
|
+
}): Partial<TextLike> | null;
|
|
64
|
+
/**
|
|
65
|
+
* Return a text shape to auto width and fit it to its content (the left edge
|
|
66
|
+
* stays put). Returns the undoable command, or null when nothing would change.
|
|
67
|
+
*/
|
|
68
|
+
export declare function fitTextToContent(store: BoardStore, shapeId: string, measure: TextMeasurer): Command | null;
|
|
69
|
+
export {};
|
|
70
|
+
//# sourceMappingURL=text-sizing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text-sizing.d.ts","sourceRoot":"","sources":["../src/text-sizing.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,UAAU,EACf,KAAK,WAAW,EAChB,KAAK,OAAO,EACZ,KAAK,KAAK,EACV,KAAK,YAAY,EACjB,KAAK,SAAS,EAEd,MAAM,uBAAuB,CAAC;AAG/B;;;;;;GAMG;AAEH,mEAAmE;AACnE,eAAO,MAAM,cAAc,KAAK,CAAC;AAEjC;;;GAGG;AACH,eAAO,MAAM,YAAY,IAAI,CAAC;AAE9B,kFAAkF;AAClF,MAAM,MAAM,YAAY,GAAG,CAAC,KAAK,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,KAAK;IAC7F,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,KAAK,QAAQ,GAAG,SAAS,GAAG;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAIF,sFAAsF;AACtF,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAEhE;AAED,iEAAiE;AACjE,wBAAgB,YAAY,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAE1D;AAUD,wBAAgB,UAAU,CAAC,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,KAAK,GAAG,SAAS,CAwDzF;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,SAAS,CAAC,CAMxF;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,KAAK,GAAG,IAAI,CAaxF;AAED,wEAAwE;AACxE,wBAAgB,mBAAmB,CAClC,IAAI,EAAE,SAAS,EACf,OAAO,EAAE,YAAY,GACnB;IAAE,SAAS,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAOpD;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC7B,IAAI,EAAE,SAAS,EACf,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,YAAY,EACrB,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAO,GAClD,OAAO,CAAC,QAAQ,CAAC,GAAG,IAAI,CAY1B;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC/B,KAAK,EAAE,UAAU,EACjB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,YAAY,GACnB,OAAO,GAAG,IAAI,CAchB"}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { safeRotation, } from "@edv4h/usketch-shared";
|
|
2
|
+
import { createUpdateShapeCommand } from "@edv4h/usketch-store";
|
|
3
|
+
/**
|
|
4
|
+
* Figma-style text sizing.
|
|
5
|
+
*
|
|
6
|
+
* - `autoWidth: true` — no wrapping; the width follows the longest line.
|
|
7
|
+
* - `autoWidth: false` / absent — fixed width; text wraps inside it. Text shapes
|
|
8
|
+
* saved before auto width existed have no flag and stay fixed width.
|
|
9
|
+
*/
|
|
10
|
+
/** Smallest auto width, so an empty text still shows its caret. */
|
|
11
|
+
export const AUTO_WIDTH_MIN = 16;
|
|
12
|
+
/**
|
|
13
|
+
* Width changes smaller than this (world px) are hand jitter — e.g. the first
|
|
14
|
+
* click of a handle double-click — and keep the width (and auto width) as is.
|
|
15
|
+
*/
|
|
16
|
+
export const WIDTH_JITTER = 2;
|
|
17
|
+
const HORIZONTAL_HANDLES = new Set(["e", "w", "ne", "nw", "se", "sw"]);
|
|
18
|
+
/** Handles that change the width — dragging one turns auto width into fixed width. */
|
|
19
|
+
export function isHorizontalHandle(handle) {
|
|
20
|
+
return HORIZONTAL_HANDLES.has(handle);
|
|
21
|
+
}
|
|
22
|
+
/** Handles whose double-click returns the text to auto width. */
|
|
23
|
+
export function isSideHandle(handle) {
|
|
24
|
+
return handle === "e" || handle === "w";
|
|
25
|
+
}
|
|
26
|
+
function measureText(data, text, measure) {
|
|
27
|
+
return measure({
|
|
28
|
+
text,
|
|
29
|
+
fontSize: data.fontSize ?? 16,
|
|
30
|
+
fontFamily: data.fontFamily ?? "system-ui, sans-serif",
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
export function resizeText(data, handle, delta) {
|
|
34
|
+
let { x, y, width, height } = data;
|
|
35
|
+
switch (handle) {
|
|
36
|
+
case "se":
|
|
37
|
+
width += delta.x;
|
|
38
|
+
height += delta.y;
|
|
39
|
+
break;
|
|
40
|
+
case "nw":
|
|
41
|
+
x += delta.x;
|
|
42
|
+
y += delta.y;
|
|
43
|
+
width -= delta.x;
|
|
44
|
+
height -= delta.y;
|
|
45
|
+
break;
|
|
46
|
+
case "ne":
|
|
47
|
+
y += delta.y;
|
|
48
|
+
width += delta.x;
|
|
49
|
+
height -= delta.y;
|
|
50
|
+
break;
|
|
51
|
+
case "sw":
|
|
52
|
+
x += delta.x;
|
|
53
|
+
width -= delta.x;
|
|
54
|
+
height += delta.y;
|
|
55
|
+
break;
|
|
56
|
+
case "e":
|
|
57
|
+
width += delta.x;
|
|
58
|
+
break;
|
|
59
|
+
case "w":
|
|
60
|
+
x += delta.x;
|
|
61
|
+
width -= delta.x;
|
|
62
|
+
break;
|
|
63
|
+
case "n":
|
|
64
|
+
y += delta.y;
|
|
65
|
+
height -= delta.y;
|
|
66
|
+
break;
|
|
67
|
+
case "s":
|
|
68
|
+
height += delta.y;
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
// n/s handles leave the width alone — don't let the min-width clamp widen a
|
|
72
|
+
// narrow auto-width text (e.g. an empty one) as a side effect. Neither does
|
|
73
|
+
// a jitter-sized drag of a side handle.
|
|
74
|
+
if (isHorizontalHandle(handle)) {
|
|
75
|
+
if (Math.abs(width - data.width) < WIDTH_JITTER) {
|
|
76
|
+
width = data.width;
|
|
77
|
+
x = data.x;
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
width = Math.max(40, width);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
const resized = { ...data, x, y, width, height: Math.max(24, height) };
|
|
84
|
+
// Only an actual width change turns auto width into fixed width; merely
|
|
85
|
+
// grabbing a handle (or the first half of a double-click) must not.
|
|
86
|
+
if (data.autoWidth && width !== data.width) {
|
|
87
|
+
resized.autoWidth = false;
|
|
88
|
+
}
|
|
89
|
+
return resized;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* `ShapeDefinition.applyBounds` for multi-selection resize, which bypasses
|
|
93
|
+
* `resize`: a real width change turns auto width into fixed width there too.
|
|
94
|
+
*/
|
|
95
|
+
export function applyTextBounds(data, bounds) {
|
|
96
|
+
const patch = { ...bounds };
|
|
97
|
+
if (data.autoWidth && Math.abs(bounds.width - data.width) >= WIDTH_JITTER) {
|
|
98
|
+
patch.autoWidth = false;
|
|
99
|
+
}
|
|
100
|
+
return patch;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Position that keeps a rotated shape's top-left corner where it is when its
|
|
104
|
+
* size changes to `width` × `height`. A shape rotates around its center, so a
|
|
105
|
+
* new size alone would shift the visible left / top edges. Null when unrotated.
|
|
106
|
+
*/
|
|
107
|
+
export function keepTopLeft(data, width, height) {
|
|
108
|
+
const rad = (safeRotation(data.rotation) * Math.PI) / 180;
|
|
109
|
+
if (!rad)
|
|
110
|
+
return null;
|
|
111
|
+
const cos = Math.cos(rad);
|
|
112
|
+
const sin = Math.sin(rad);
|
|
113
|
+
const rotate = (u, v) => ({ x: u * cos - v * sin, y: u * sin + v * cos });
|
|
114
|
+
const offset = rotate(-data.width / 2, -data.height / 2);
|
|
115
|
+
const topLeft = {
|
|
116
|
+
x: data.x + data.width / 2 + offset.x,
|
|
117
|
+
y: data.y + data.height / 2 + offset.y,
|
|
118
|
+
};
|
|
119
|
+
const toCenter = rotate(width / 2, height / 2);
|
|
120
|
+
return { x: topLeft.x + toCenter.x - width / 2, y: topLeft.y + toCenter.y - height / 2 };
|
|
121
|
+
}
|
|
122
|
+
/** Patch that returns the text to auto width, fitted to its content. */
|
|
123
|
+
export function computeAutoFitPatch(data, measure) {
|
|
124
|
+
const size = measureText(data, data.text ?? "", measure);
|
|
125
|
+
return {
|
|
126
|
+
autoWidth: true,
|
|
127
|
+
width: Math.max(AUTO_WIDTH_MIN, Math.ceil(size.width)),
|
|
128
|
+
height: Math.ceil(size.height),
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Geometry patch applied while typing. Auto-width text follows its content.
|
|
133
|
+
* A rotated text keeps its top-left corner in place as it grows (`height` is
|
|
134
|
+
* the height the editor is about to apply). Inside a container that lays out
|
|
135
|
+
* its children (and so sets their width), the text switches to fixed width
|
|
136
|
+
* instead of fighting the layout over the width.
|
|
137
|
+
*/
|
|
138
|
+
export function fitTextOnInput(data, text, measure, options = {}) {
|
|
139
|
+
const patch = {};
|
|
140
|
+
let width = data.width;
|
|
141
|
+
if (data.autoWidth) {
|
|
142
|
+
if (options.laidOut)
|
|
143
|
+
return { autoWidth: false };
|
|
144
|
+
const size = measureText(data, text, measure);
|
|
145
|
+
width = Math.max(AUTO_WIDTH_MIN, Math.ceil(size.width));
|
|
146
|
+
patch.width = width;
|
|
147
|
+
}
|
|
148
|
+
const position = keepTopLeft(data, width, options.height ?? data.height);
|
|
149
|
+
if (position)
|
|
150
|
+
Object.assign(patch, position);
|
|
151
|
+
return Object.keys(patch).length > 0 ? patch : null;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Return a text shape to auto width and fit it to its content (the left edge
|
|
155
|
+
* stays put). Returns the undoable command, or null when nothing would change.
|
|
156
|
+
*/
|
|
157
|
+
export function fitTextToContent(store, shapeId, measure) {
|
|
158
|
+
const shape = store.getShape(shapeId);
|
|
159
|
+
if (shape?.type !== "text")
|
|
160
|
+
return null;
|
|
161
|
+
const to = computeAutoFitPatch(shape, measure);
|
|
162
|
+
const from = { autoWidth: shape.autoWidth, width: shape.width, height: shape.height };
|
|
163
|
+
if (from.autoWidth === to.autoWidth && from.width === to.width && from.height === to.height) {
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
return createUpdateShapeCommand(store, shapeId, from, to);
|
|
167
|
+
}
|
|
168
|
+
//# sourceMappingURL=text-sizing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text-sizing.js","sourceRoot":"","sources":["../src/text-sizing.ts"],"names":[],"mappings":"AAAA,OAAO,EAON,YAAY,GACZ,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAEhE;;;;;;GAMG;AAEH,mEAAmE;AACnE,MAAM,CAAC,MAAM,cAAc,GAAG,EAAE,CAAC;AAEjC;;;GAGG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,CAAC;AAe9B,MAAM,kBAAkB,GAA8B,IAAI,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;AAElG,sFAAsF;AACtF,MAAM,UAAU,kBAAkB,CAAC,MAAoB;IACtD,OAAO,kBAAkB,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;AACvC,CAAC;AAED,iEAAiE;AACjE,MAAM,UAAU,YAAY,CAAC,MAAoB;IAChD,OAAO,MAAM,KAAK,GAAG,IAAI,MAAM,KAAK,GAAG,CAAC;AACzC,CAAC;AAED,SAAS,WAAW,CAAC,IAAc,EAAE,IAAY,EAAE,OAAqB;IACvE,OAAO,OAAO,CAAC;QACd,IAAI;QACJ,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,EAAE;QAC7B,UAAU,EAAE,IAAI,CAAC,UAAU,IAAI,uBAAuB;KACtD,CAAC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,IAAe,EAAE,MAAoB,EAAE,KAAY;IAC7E,IAAI,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IACnC,QAAQ,MAAM,EAAE,CAAC;QAChB,KAAK,IAAI;YACR,KAAK,IAAI,KAAK,CAAC,CAAC,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,CAAC,CAAC;YAClB,MAAM;QACP,KAAK,IAAI;YACR,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC;YACb,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC;YACb,KAAK,IAAI,KAAK,CAAC,CAAC,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,CAAC,CAAC;YAClB,MAAM;QACP,KAAK,IAAI;YACR,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC;YACb,KAAK,IAAI,KAAK,CAAC,CAAC,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,CAAC,CAAC;YAClB,MAAM;QACP,KAAK,IAAI;YACR,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC;YACb,KAAK,IAAI,KAAK,CAAC,CAAC,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,CAAC,CAAC;YAClB,MAAM;QACP,KAAK,GAAG;YACP,KAAK,IAAI,KAAK,CAAC,CAAC,CAAC;YACjB,MAAM;QACP,KAAK,GAAG;YACP,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC;YACb,KAAK,IAAI,KAAK,CAAC,CAAC,CAAC;YACjB,MAAM;QACP,KAAK,GAAG;YACP,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC;YACb,MAAM,IAAI,KAAK,CAAC,CAAC,CAAC;YAClB,MAAM;QACP,KAAK,GAAG;YACP,MAAM,IAAI,KAAK,CAAC,CAAC,CAAC;YAClB,MAAM;IACR,CAAC;IACD,4EAA4E;IAC5E,4EAA4E;IAC5E,wCAAwC;IACxC,IAAI,kBAAkB,CAAC,MAAM,CAAC,EAAE,CAAC;QAChC,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,YAAY,EAAE,CAAC;YACjD,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;YACnB,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;QACZ,CAAC;aAAM,CAAC;YACP,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;QAC7B,CAAC;IACF,CAAC;IACD,MAAM,OAAO,GAAG,EAAE,GAAG,IAAI,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,MAAM,CAAC,EAAE,CAAC;IACvE,wEAAwE;IACxE,oEAAoE;IACpE,IAAK,IAAiB,CAAC,SAAS,IAAI,KAAK,KAAK,IAAI,CAAC,KAAK,EAAE,CAAC;QACzD,OAAoB,CAAC,SAAS,GAAG,KAAK,CAAC;IACzC,CAAC;IACD,OAAO,OAAO,CAAC;AAChB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,IAAe,EAAE,MAAmB;IACnE,MAAM,KAAK,GAAsB,EAAE,GAAG,MAAM,EAAE,CAAC;IAC/C,IAAK,IAAiB,CAAC,SAAS,IAAI,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,YAAY,EAAE,CAAC;QACzF,KAAK,CAAC,SAAS,GAAG,KAAK,CAAC;IACzB,CAAC;IACD,OAAO,KAAK,CAAC;AACd,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,IAAe,EAAE,KAAa,EAAE,MAAc;IACzE,MAAM,GAAG,GAAG,CAAC,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC;IAC1D,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IACtB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC1B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC1B,MAAM,MAAM,GAAG,CAAC,CAAS,EAAE,CAAS,EAAS,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,GAAG,GAAG,CAAC,GAAG,GAAG,EAAE,CAAC,EAAE,CAAC,GAAG,GAAG,GAAG,CAAC,GAAG,GAAG,EAAE,CAAC,CAAC;IACjG,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACzD,MAAM,OAAO,GAAG;QACf,CAAC,EAAE,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC;QACrC,CAAC,EAAE,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC;KACtC,CAAC;IACF,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,GAAG,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,CAAC;IAC/C,OAAO,EAAE,CAAC,EAAE,OAAO,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,GAAG,KAAK,GAAG,CAAC,EAAE,CAAC,EAAE,OAAO,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,GAAG,MAAM,GAAG,CAAC,EAAE,CAAC;AAC1F,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,mBAAmB,CAClC,IAAe,EACf,OAAqB;IAErB,MAAM,IAAI,GAAG,WAAW,CAAC,IAAgB,EAAG,IAAiB,CAAC,IAAI,IAAI,EAAE,EAAE,OAAO,CAAC,CAAC;IACnF,OAAO;QACN,SAAS,EAAE,IAAI;QACf,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,cAAc,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACtD,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC;KAC9B,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAC7B,IAAe,EACf,IAAY,EACZ,OAAqB,EACrB,OAAO,GAA2C,EAAE;IAEpD,MAAM,KAAK,GAAsB,EAAE,CAAC;IACpC,IAAI,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;IACvB,IAAK,IAAiB,CAAC,SAAS,EAAE,CAAC;QAClC,IAAI,OAAO,CAAC,OAAO;YAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;QACjD,MAAM,IAAI,GAAG,WAAW,CAAC,IAAgB,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;QAC1D,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,cAAc,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;QACxD,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC;IACrB,CAAC;IACD,MAAM,QAAQ,GAAG,WAAW,CAAC,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC,CAAC;IACzE,IAAI,QAAQ;QAAE,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IAC7C,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;AACrD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAC/B,KAAiB,EACjB,OAAe,EACf,OAAqB;IAErB,MAAM,KAAK,GAAG,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAyB,CAAC;IAC9D,IAAI,KAAK,EAAE,IAAI,KAAK,MAAM;QAAE,OAAO,IAAI,CAAC;IACxC,MAAM,EAAE,GAAG,mBAAmB,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IAC/C,MAAM,IAAI,GAAG,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC;IACtF,IAAI,IAAI,CAAC,SAAS,KAAK,EAAE,CAAC,SAAS,IAAI,IAAI,CAAC,KAAK,KAAK,EAAE,CAAC,KAAK,IAAI,IAAI,CAAC,MAAM,KAAK,EAAE,CAAC,MAAM,EAAE,CAAC;QAC7F,OAAO,IAAI,CAAC;IACb,CAAC;IACD,OAAO,wBAAwB,CAC9B,KAAK,EACL,OAAO,EACP,IAA0B,EAC1B,EAAwB,CACxB,CAAC;AACH,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@edv4h/usketch-plugin-shape-text",
|
|
3
|
-
"version": "3.0
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -12,9 +12,9 @@
|
|
|
12
12
|
}
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@edv4h/usketch-shape-utils": "2.
|
|
16
|
-
"@edv4h/usketch-shared": "4.
|
|
17
|
-
"@edv4h/usketch-store": "3.
|
|
15
|
+
"@edv4h/usketch-shape-utils": "2.4.0",
|
|
16
|
+
"@edv4h/usketch-shared": "4.19.0",
|
|
17
|
+
"@edv4h/usketch-store": "3.10.0"
|
|
18
18
|
},
|
|
19
19
|
"peerDependencies": {
|
|
20
20
|
"react": ">=19"
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"devDependencies": {
|
|
23
23
|
"@types/react": "19.3.0",
|
|
24
24
|
"typescript": "7.0.2",
|
|
25
|
-
"vitest": "5.0.
|
|
25
|
+
"vitest": "5.0.5"
|
|
26
26
|
},
|
|
27
27
|
"publishConfig": {
|
|
28
28
|
"access": "public"
|