@edv4h/usketch-plugin-shape-frame 1.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 EdV4H
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,2 @@
1
+ export { framePlugin } from "./plugin.js";
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export { framePlugin } from "./plugin.js";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { UsketchPlugin } from "@edv4h/usketch-shared";
2
+ export declare const framePlugin: UsketchPlugin;
3
+ //# sourceMappingURL=plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAKX,aAAa,EACb,MAAM,uBAAuB,CAAC;AA+D/B,eAAO,MAAM,WAAW,EAAE,aAoJzB,CAAC"}
package/dist/plugin.js ADDED
@@ -0,0 +1,178 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { generateId } from "@edv4h/usketch-shared";
3
+ import { containsAABB, createAddShapeCommand, createReparentCommand } from "@edv4h/usketch-store";
4
+ import { createDefaultFrame, getBoundsFrame, hitTestFrame, renderFrame, resizeFrame, } from "./shapes/frame.js";
5
+ /**
6
+ * LOD component: labeled empty frame — dashed border + top-left title.
7
+ * Frames are containers, so they mostly need to show where they are.
8
+ */
9
+ function SimplifiedFrame({ shape }) {
10
+ const label = shape.name || "Frame";
11
+ const rotation = typeof shape.rotation === "number" ? shape.rotation : 0;
12
+ return (_jsx("div", { style: {
13
+ position: "absolute",
14
+ left: shape.x,
15
+ top: shape.y,
16
+ width: shape.width,
17
+ height: shape.height,
18
+ border: "1px dashed #999",
19
+ background: "rgba(255,255,255,0.3)",
20
+ pointerEvents: "none",
21
+ overflow: "hidden",
22
+ transform: rotation ? `rotate(${rotation}deg)` : undefined,
23
+ transformOrigin: "center center",
24
+ }, children: _jsx("div", { style: {
25
+ position: "absolute",
26
+ top: 4,
27
+ left: 6,
28
+ fontSize: 11,
29
+ color: "#666",
30
+ fontWeight: 500,
31
+ whiteSpace: "nowrap",
32
+ }, children: label }) }));
33
+ }
34
+ function FrameIcon() {
35
+ return (_jsxs("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", stroke: "currentColor", children: [_jsx("line", { x1: "7", y1: "2", x2: "7", y2: "18", strokeWidth: "1.5", strokeLinecap: "round" }), _jsx("line", { x1: "13", y1: "2", x2: "13", y2: "18", strokeWidth: "1.5", strokeLinecap: "round" }), _jsx("line", { x1: "2", y1: "7", x2: "18", y2: "7", strokeWidth: "1.5", strokeLinecap: "round" }), _jsx("line", { x1: "2", y1: "13", x2: "18", y2: "13", strokeWidth: "1.5", strokeLinecap: "round" })] }));
36
+ }
37
+ export const framePlugin = {
38
+ id: "usketch-plugin-shape-frame",
39
+ name: "フレーム",
40
+ setup(ctx) {
41
+ // Register the "frame" shape type
42
+ ctx.shapes.register("frame", {
43
+ render: renderFrame,
44
+ getBounds: getBoundsFrame,
45
+ hitTest: hitTestFrame,
46
+ resize: resizeFrame,
47
+ createDefault: createDefaultFrame,
48
+ renderTarget: "html",
49
+ minSize: { width: 50, height: 50 },
50
+ simplifiedComponent: SimplifiedFrame,
51
+ });
52
+ // Drawing tool state
53
+ let drawState = null;
54
+ function onPointerDown(toolCtx, event) {
55
+ const id = generateId();
56
+ drawState = { startX: event.worldPoint.x, startY: event.worldPoint.y, shapeId: id };
57
+ const shape = createDefaultFrame({ id, x: event.worldPoint.x, y: event.worldPoint.y });
58
+ shape.width = 0;
59
+ shape.height = 0;
60
+ shape.style = {
61
+ ...shape.style,
62
+ stroke: toolCtx.store.getStyleSettings().stroke,
63
+ };
64
+ toolCtx.store.addShape(shape);
65
+ }
66
+ function onPointerMove(toolCtx, event) {
67
+ if (!drawState)
68
+ return;
69
+ const x = Math.min(drawState.startX, event.worldPoint.x);
70
+ const y = Math.min(drawState.startY, event.worldPoint.y);
71
+ const width = Math.abs(event.worldPoint.x - drawState.startX);
72
+ const height = Math.abs(event.worldPoint.y - drawState.startY);
73
+ toolCtx.store.updateShape(drawState.shapeId, { x, y, width, height });
74
+ }
75
+ function onPointerUp(toolCtx) {
76
+ if (!drawState)
77
+ return;
78
+ const shape = toolCtx.store.getShape(drawState.shapeId);
79
+ if (shape && shape.width > 10 && shape.height > 10) {
80
+ toolCtx.store.deleteShape(drawState.shapeId);
81
+ toolCtx.commands.execute(createAddShapeCommand(toolCtx.store, shape));
82
+ }
83
+ else {
84
+ toolCtx.store.deleteShape(drawState.shapeId);
85
+ }
86
+ drawState = null;
87
+ toolCtx.store.setActiveToolId("select");
88
+ }
89
+ ctx.tools.register("frame-draw", {
90
+ icon: FrameIcon,
91
+ cursor: "crosshair",
92
+ shortcut: "f",
93
+ order: 11,
94
+ onPointerDown,
95
+ onPointerMove,
96
+ onPointerUp,
97
+ onDeactivate() {
98
+ if (drawState) {
99
+ ctx.store.deleteShape(drawState.shapeId);
100
+ drawState = null;
101
+ }
102
+ },
103
+ });
104
+ // ── Auto-parenting ──
105
+ // On move-end: reparent moved shapes (emitted by select tool after move command commits).
106
+ // Use setTimeout to defer reparent until after React finishes the current render cycle,
107
+ // since the emit happens inside a queueMicrotask from the select tool.
108
+ const unsubMoveEnd = ctx.events.on("shapes:move-end", (data) => {
109
+ if (!data?.shapeIds)
110
+ return;
111
+ setTimeout(() => {
112
+ for (const shapeId of data.shapeIds) {
113
+ autoReparent(shapeId);
114
+ }
115
+ }, 0);
116
+ });
117
+ // On shape:added (e.g. drawing inside a frame)
118
+ const unsubAdded = ctx.store.onMutation((event) => {
119
+ if (event.type !== "shape:added")
120
+ return;
121
+ const payload = event.payload;
122
+ if (!payload?.id)
123
+ return;
124
+ const shape = ctx.store.getShape(payload.id);
125
+ if (!shape || shape.type === "frame" || shape.type === "connector")
126
+ return;
127
+ autoReparent(payload.id);
128
+ });
129
+ function autoReparent(shapeId) {
130
+ const shape = ctx.store.getShape(shapeId);
131
+ if (!shape || shape.type === "frame" || shape.type === "connector")
132
+ return;
133
+ const shapeBounds = { x: shape.x, y: shape.y, width: shape.width, height: shape.height };
134
+ // Find the smallest frame that fully contains this shape
135
+ let bestFrame = null;
136
+ for (const [id, candidate] of ctx.store.getShapes()) {
137
+ if (id === shapeId || candidate.type !== "frame")
138
+ continue;
139
+ const frameBounds = {
140
+ x: candidate.x,
141
+ y: candidate.y,
142
+ width: candidate.width,
143
+ height: candidate.height,
144
+ };
145
+ if (containsAABB(frameBounds, shapeBounds)) {
146
+ if (!bestFrame ||
147
+ candidate.width * candidate.height < bestFrame.width * bestFrame.height) {
148
+ bestFrame = candidate;
149
+ }
150
+ }
151
+ }
152
+ const currentParentId = shape.parentId;
153
+ const newParentId = bestFrame?.id;
154
+ if (currentParentId !== newParentId) {
155
+ ctx.commands.execute(createReparentCommand(ctx.store, [shapeId], newParentId ?? undefined));
156
+ }
157
+ }
158
+ // ── Frame move: move all children along with the frame ──
159
+ const unsubMove = ctx.store.onMutation((event) => {
160
+ if (event.type !== "shape:updated")
161
+ return;
162
+ const payload = event.payload;
163
+ if (!payload?.id)
164
+ return;
165
+ const shape = ctx.store.getShape(payload.id);
166
+ if (!shape || shape.type !== "frame")
167
+ return;
168
+ // This is handled by the select tool which includes children in snapshots
169
+ // when moving a frame. No additional logic needed here.
170
+ });
171
+ this.teardown = () => {
172
+ unsubMoveEnd();
173
+ unsubAdded();
174
+ unsubMove();
175
+ };
176
+ },
177
+ };
178
+ //# sourceMappingURL=plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.tsx"],"names":[],"mappings":";AAOA,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAClG,OAAO,EACN,kBAAkB,EAClB,cAAc,EACd,YAAY,EACZ,WAAW,EACX,WAAW,GACX,MAAM,mBAAmB,CAAC;AAE3B;;;GAGG;AACH,SAAS,eAAe,CAAC,EAAE,KAAK,EAAwB;IACvD,MAAM,KAAK,GAAI,KAAK,CAAC,IAAe,IAAI,OAAO,CAAC;IAChD,MAAM,QAAQ,GAAG,OAAO,KAAK,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IACzE,OAAO,CACN,cACC,KAAK,EAAE;YACN,QAAQ,EAAE,UAAU;YACpB,IAAI,EAAE,KAAK,CAAC,CAAC;YACb,GAAG,EAAE,KAAK,CAAC,CAAC;YACZ,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,MAAM,EAAE,iBAAiB;YACzB,UAAU,EAAE,uBAAuB;YACnC,aAAa,EAAE,MAAM;YACrB,QAAQ,EAAE,QAAQ;YAClB,SAAS,EAAE,QAAQ,CAAC,CAAC,CAAC,UAAU,QAAQ,MAAM,CAAC,CAAC,CAAC,SAAS;YAC1D,eAAe,EAAE,eAAe;SAChC,YAED,cACC,KAAK,EAAE;gBACN,QAAQ,EAAE,UAAU;gBACpB,GAAG,EAAE,CAAC;gBACN,IAAI,EAAE,CAAC;gBACP,QAAQ,EAAE,EAAE;gBACZ,KAAK,EAAE,MAAM;gBACb,UAAU,EAAE,GAAG;gBACf,UAAU,EAAE,QAAQ;aACpB,YAEA,KAAK,GACD,GACD,CACN,CAAC;AACH,CAAC;AAED,SAAS,SAAS;IACjB,OAAO,CACN,eAAK,KAAK,EAAC,IAAI,EAAC,MAAM,EAAC,IAAI,EAAC,OAAO,EAAC,WAAW,EAAC,IAAI,EAAC,MAAM,EAAC,MAAM,EAAC,cAAc,aAEhF,eAAM,EAAE,EAAC,GAAG,EAAC,EAAE,EAAC,GAAG,EAAC,EAAE,EAAC,GAAG,EAAC,EAAE,EAAC,IAAI,EAAC,WAAW,EAAC,KAAK,EAAC,aAAa,EAAC,OAAO,GAAG,EAC7E,eAAM,EAAE,EAAC,IAAI,EAAC,EAAE,EAAC,GAAG,EAAC,EAAE,EAAC,IAAI,EAAC,EAAE,EAAC,IAAI,EAAC,WAAW,EAAC,KAAK,EAAC,aAAa,EAAC,OAAO,GAAG,EAC/E,eAAM,EAAE,EAAC,GAAG,EAAC,EAAE,EAAC,GAAG,EAAC,EAAE,EAAC,IAAI,EAAC,EAAE,EAAC,GAAG,EAAC,WAAW,EAAC,KAAK,EAAC,aAAa,EAAC,OAAO,GAAG,EAC7E,eAAM,EAAE,EAAC,GAAG,EAAC,EAAE,EAAC,IAAI,EAAC,EAAE,EAAC,IAAI,EAAC,EAAE,EAAC,IAAI,EAAC,WAAW,EAAC,KAAK,EAAC,aAAa,EAAC,OAAO,GAAG,IAC1E,CACN,CAAC;AACH,CAAC;AAED,MAAM,CAAC,MAAM,WAAW,GAAkB;IACzC,EAAE,EAAE,4BAA4B;IAChC,IAAI,EAAE,MAAM;IAEZ,KAAK,CAAC,GAAkB;QACvB,kCAAkC;QAClC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE;YAC5B,MAAM,EAAE,WAAW;YACnB,SAAS,EAAE,cAAc;YACzB,OAAO,EAAE,YAAY;YACrB,MAAM,EAAE,WAAW;YACnB,aAAa,EAAE,kBAAkB;YACjC,YAAY,EAAE,MAAM;YACpB,OAAO,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE;YAClC,mBAAmB,EAAE,eAAe;SACpC,CAAC,CAAC;QAEH,qBAAqB;QACrB,IAAI,SAAS,GAA+D,IAAI,CAAC;QAEjF,SAAS,aAAa,CAAC,OAAoB,EAAE,KAAyB;YACrE,MAAM,EAAE,GAAG,UAAU,EAAE,CAAC;YACxB,SAAS,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;YACpF,MAAM,KAAK,GAAG,kBAAkB,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC;YACvF,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC;YAChB,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;YACjB,KAAK,CAAC,KAAK,GAAG;gBACb,GAAG,KAAK,CAAC,KAAK;gBACd,MAAM,EAAE,OAAO,CAAC,KAAK,CAAC,gBAAgB,EAAE,CAAC,MAAM;aAC/C,CAAC;YACF,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QAC/B,CAAC;QAED,SAAS,aAAa,CAAC,OAAoB,EAAE,KAAyB;YACrE,IAAI,CAAC,SAAS;gBAAE,OAAO;YACvB,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,MAAM,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;YACzD,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,MAAM,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;YACzD,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;YAC9D,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;YAC/D,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,SAAS,CAAC,OAAO,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;QACvE,CAAC;QAED,SAAS,WAAW,CAAC,OAAoB;YACxC,IAAI,CAAC,SAAS;gBAAE,OAAO;YACvB,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;YACxD,IAAI,KAAK,IAAI,KAAK,CAAC,KAAK,GAAG,EAAE,IAAI,KAAK,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;gBACpD,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;gBAC7C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,qBAAqB,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;YACvE,CAAC;iBAAM,CAAC;gBACP,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;YAC9C,CAAC;YACD,SAAS,GAAG,IAAI,CAAC;YACjB,OAAO,CAAC,KAAK,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC;QACzC,CAAC;QAED,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,YAAY,EAAE;YAChC,IAAI,EAAE,SAAS;YACf,MAAM,EAAE,WAAW;YACnB,QAAQ,EAAE,GAAG;YACb,KAAK,EAAE,EAAE;YACT,aAAa;YACb,aAAa;YACb,WAAW;YACX,YAAY;gBACX,IAAI,SAAS,EAAE,CAAC;oBACf,GAAG,CAAC,KAAK,CAAC,WAAW,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;oBACzC,SAAS,GAAG,IAAI,CAAC;gBAClB,CAAC;YACF,CAAC;SACD,CAAC,CAAC;QAEH,uBAAuB;QAEvB,0FAA0F;QAC1F,wFAAwF;QACxF,uEAAuE;QACvE,MAAM,YAAY,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAyB,iBAAiB,EAAE,CAAC,IAAI,EAAE,EAAE;YACtF,IAAI,CAAC,IAAI,EAAE,QAAQ;gBAAE,OAAO;YAC5B,UAAU,CAAC,GAAG,EAAE;gBACf,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;oBACrC,YAAY,CAAC,OAAO,CAAC,CAAC;gBACvB,CAAC;YACF,CAAC,EAAE,CAAC,CAAC,CAAC;QACP,CAAC,CAAC,CAAC;QAEH,+CAA+C;QAC/C,MAAM,UAAU,GAAG,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,KAAK,EAAE,EAAE;YACjD,IAAI,KAAK,CAAC,IAAI,KAAK,aAAa;gBAAE,OAAO;YACzC,MAAM,OAAO,GAAG,KAAK,CAAC,OAAqC,CAAC;YAC5D,IAAI,CAAC,OAAO,EAAE,EAAE;gBAAE,OAAO;YACzB,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YAC7C,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,IAAI,KAAK,CAAC,IAAI,KAAK,WAAW;gBAAE,OAAO;YAC3E,YAAY,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAC1B,CAAC,CAAC,CAAC;QAEH,SAAS,YAAY,CAAC,OAAe;YACpC,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;YAC1C,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,IAAI,KAAK,CAAC,IAAI,KAAK,WAAW;gBAAE,OAAO;YAE3E,MAAM,WAAW,GAAG,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC;YAEzF,yDAAyD;YACzD,IAAI,SAAS,GAAqB,IAAI,CAAC;YACvC,KAAK,MAAM,CAAC,EAAE,EAAE,SAAS,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,SAAS,EAAE,EAAE,CAAC;gBACrD,IAAI,EAAE,KAAK,OAAO,IAAI,SAAS,CAAC,IAAI,KAAK,OAAO;oBAAE,SAAS;gBAC3D,MAAM,WAAW,GAAG;oBACnB,CAAC,EAAE,SAAS,CAAC,CAAC;oBACd,CAAC,EAAE,SAAS,CAAC,CAAC;oBACd,KAAK,EAAE,SAAS,CAAC,KAAK;oBACtB,MAAM,EAAE,SAAS,CAAC,MAAM;iBACxB,CAAC;gBACF,IAAI,YAAY,CAAC,WAAW,EAAE,WAAW,CAAC,EAAE,CAAC;oBAC5C,IACC,CAAC,SAAS;wBACV,SAAS,CAAC,KAAK,GAAG,SAAS,CAAC,MAAM,GAAG,SAAS,CAAC,KAAK,GAAG,SAAS,CAAC,MAAM,EACtE,CAAC;wBACF,SAAS,GAAG,SAAS,CAAC;oBACvB,CAAC;gBACF,CAAC;YACF,CAAC;YAED,MAAM,eAAe,GAAG,KAAK,CAAC,QAA8B,CAAC;YAC7D,MAAM,WAAW,GAAG,SAAS,EAAE,EAAE,CAAC;YAElC,IAAI,eAAe,KAAK,WAAW,EAAE,CAAC;gBACrC,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,qBAAqB,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,OAAO,CAAC,EAAE,WAAW,IAAI,SAAS,CAAC,CAAC,CAAC;YAC7F,CAAC;QACF,CAAC;QAED,2DAA2D;QAC3D,MAAM,SAAS,GAAG,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,KAAK,EAAE,EAAE;YAChD,IAAI,KAAK,CAAC,IAAI,KAAK,eAAe;gBAAE,OAAO;YAC3C,MAAM,OAAO,GAAG,KAAK,CAAC,OAAqC,CAAC;YAC5D,IAAI,CAAC,OAAO,EAAE,EAAE;gBAAE,OAAO;YAEzB,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YAC7C,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO;gBAAE,OAAO;YAE7C,0EAA0E;YAC1E,wDAAwD;QACzD,CAAC,CAAC,CAAC;QAEF,IAAsB,CAAC,QAAQ,GAAG,GAAG,EAAE;YACvC,YAAY,EAAE,CAAC;YACf,UAAU,EAAE,CAAC;YACb,SAAS,EAAE,CAAC;QACb,CAAC,CAAC;IACH,CAAC;CACD,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { BoundingBox, Point, ShapeData } from "@edv4h/usketch-shared";
2
+ export declare function renderFrame(data: ShapeData): import("react/jsx-runtime").JSX.Element;
3
+ export declare function getBoundsFrame(data: ShapeData): BoundingBox;
4
+ export declare function hitTestFrame(data: ShapeData, point: Point): boolean;
5
+ export declare function resizeFrame(data: ShapeData, handle: string, delta: Point): ShapeData;
6
+ export declare function createDefaultFrame(params: {
7
+ id: string;
8
+ x: number;
9
+ y: number;
10
+ }): ShapeData;
11
+ //# sourceMappingURL=frame.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frame.d.ts","sourceRoot":"","sources":["../../src/shapes/frame.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAI3E,wBAAgB,WAAW,CAAC,IAAI,EAAE,SAAS,2CA0C1C;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,SAAS,GAAG,WAAW,CAE3D;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,GAAG,OAAO,CAiBnE;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,SAAS,CAuBpF;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAW1F"}
@@ -0,0 +1,83 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ const TITLE_HEIGHT = 24;
3
+ export function renderFrame(data) {
4
+ const title = data.frameTitle ?? "Frame";
5
+ const bgColor = data.style.fill === "transparent" ? "#f8f8f8" : data.style.fill;
6
+ // Position/size is handled by ShapeWrapper (position: absolute; left/top/width/height).
7
+ // Render content at (0,0) within the wrapper — no position styling needed.
8
+ return (_jsxs(_Fragment, { children: [_jsx("div", { style: {
9
+ marginTop: -TITLE_HEIGHT,
10
+ height: TITLE_HEIGHT,
11
+ display: "flex",
12
+ alignItems: "center",
13
+ paddingLeft: 6,
14
+ fontSize: 12,
15
+ fontFamily: "system-ui, sans-serif",
16
+ color: "#666",
17
+ userSelect: "none",
18
+ whiteSpace: "nowrap",
19
+ overflow: "hidden",
20
+ pointerEvents: "none",
21
+ }, children: title }), _jsx("div", { style: {
22
+ width: "100%",
23
+ height: "100%",
24
+ backgroundColor: bgColor,
25
+ opacity: data.style.opacity,
26
+ border: `${data.style.strokeWidth}px solid ${data.style.stroke}`,
27
+ borderRadius: 4,
28
+ boxSizing: "border-box",
29
+ pointerEvents: "none",
30
+ } })] }));
31
+ }
32
+ export function getBoundsFrame(data) {
33
+ return { x: data.x, y: data.y, width: data.width, height: data.height };
34
+ }
35
+ export function hitTestFrame(data, point) {
36
+ // Frame body
37
+ if (point.x >= data.x &&
38
+ point.x <= data.x + data.width &&
39
+ point.y >= data.y &&
40
+ point.y <= data.y + data.height) {
41
+ return true;
42
+ }
43
+ // Title label above the frame
44
+ return (point.x >= data.x &&
45
+ point.x <= data.x + data.width &&
46
+ point.y >= data.y - TITLE_HEIGHT &&
47
+ point.y < data.y);
48
+ }
49
+ export function resizeFrame(data, handle, delta) {
50
+ const result = { ...data };
51
+ const minW = 50;
52
+ const minH = 50;
53
+ if (handle.includes("e")) {
54
+ result.width = Math.max(minW, data.width + delta.x);
55
+ }
56
+ if (handle.includes("w")) {
57
+ const newWidth = Math.max(minW, data.width - delta.x);
58
+ result.x = data.x + data.width - newWidth;
59
+ result.width = newWidth;
60
+ }
61
+ if (handle.includes("s")) {
62
+ result.height = Math.max(minH, data.height + delta.y);
63
+ }
64
+ if (handle.includes("n")) {
65
+ const newHeight = Math.max(minH, data.height - delta.y);
66
+ result.y = data.y + data.height - newHeight;
67
+ result.height = newHeight;
68
+ }
69
+ return result;
70
+ }
71
+ export function createDefaultFrame(params) {
72
+ return {
73
+ id: params.id,
74
+ type: "frame",
75
+ x: params.x,
76
+ y: params.y,
77
+ width: 300,
78
+ height: 200,
79
+ style: { fill: "#f8f8f8", stroke: "#cccccc", strokeWidth: 1, opacity: 1 },
80
+ frameTitle: "Frame",
81
+ };
82
+ }
83
+ //# sourceMappingURL=frame.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frame.js","sourceRoot":"","sources":["../../src/shapes/frame.tsx"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@edv4h/usketch-plugin-shape-frame",
3
+ "version": "1.0.0",
4
+ "type": "module",
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "source": "./src/index.ts",
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ }
13
+ },
14
+ "dependencies": {
15
+ "@edv4h/usketch-core": "1.0.0",
16
+ "@edv4h/usketch-shared": "1.0.0",
17
+ "@edv4h/usketch-store": "1.0.0"
18
+ },
19
+ "peerDependencies": {
20
+ "react": ">=19"
21
+ },
22
+ "devDependencies": {
23
+ "@types/react": "19.2.14",
24
+ "typescript": "5.9.3",
25
+ "vitest": "3.2.4"
26
+ },
27
+ "publishConfig": {
28
+ "access": "public"
29
+ },
30
+ "files": [
31
+ "dist",
32
+ "README.md"
33
+ ],
34
+ "license": "MIT",
35
+ "repository": {
36
+ "type": "git",
37
+ "url": "git+https://github.com/EdV4H/usketch.git",
38
+ "directory": "plugins/usketch-plugin-shape-frame"
39
+ },
40
+ "homepage": "https://github.com/EdV4H/usketch#readme",
41
+ "bugs": {
42
+ "url": "https://github.com/EdV4H/usketch/issues"
43
+ },
44
+ "scripts": {
45
+ "build": "tsc -p tsconfig.json",
46
+ "typecheck": "tsc --noEmit",
47
+ "test": "vitest run --passWithNoTests",
48
+ "clean": "rm -rf dist"
49
+ }
50
+ }