@maayan-albert/moab-sdk 1.0.5 → 1.0.6
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/components/DrawingTool.d.ts.map +1 -1
- package/dist/components.js +341 -291
- package/dist/components.js.map +1 -1
- package/dist/components.mjs +326 -276
- package/dist/components.mjs.map +1 -1
- package/dist/index.js +341 -290
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +326 -275
- package/dist/index.mjs.map +1 -1
- package/package.json +7 -2
package/dist/components.js
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var jsxRuntime = require('react/jsx-runtime');
|
|
4
|
-
var
|
|
4
|
+
var React = require('react');
|
|
5
5
|
var lucideReact = require('lucide-react');
|
|
6
|
-
var
|
|
6
|
+
var htmlToImage = require('html-to-image');
|
|
7
7
|
|
|
8
8
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
9
9
|
|
|
10
|
-
var
|
|
10
|
+
var React__default = /*#__PURE__*/_interopDefault(React);
|
|
11
11
|
|
|
12
|
-
// src/components/Button.tsx
|
|
13
12
|
var Button = ({
|
|
14
13
|
children,
|
|
15
14
|
variant = "primary",
|
|
@@ -92,25 +91,77 @@ var Input = ({
|
|
|
92
91
|
] });
|
|
93
92
|
};
|
|
94
93
|
var Input_default = Input;
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
height,
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
94
|
+
|
|
95
|
+
// src/components/DrawingTool.module.scss
|
|
96
|
+
var css = '@keyframes moab__toolbarEnter___kVde9 {\n from {\n opacity: 0;\n transform: scale(0.5) rotate(90deg);\n }\n to {\n opacity: 1;\n transform: scale(1) rotate(0deg);\n }\n}\n@keyframes moab__scaleIn___bOiIm {\n from {\n opacity: 0;\n transform: scale(0.85);\n }\n to {\n opacity: 1;\n transform: scale(1);\n }\n}\n@keyframes moab__scaleOut___wrtHU {\n from {\n opacity: 1;\n transform: scale(1);\n }\n to {\n opacity: 0;\n transform: scale(0.85);\n }\n}\n.moab__toolbar___GXd-P {\n position: fixed;\n bottom: 1.25rem;\n right: 1.25rem;\n z-index: 100000;\n font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;\n pointer-events: none;\n transition: left 0s, top 0s, right 0s, bottom 0s;\n}\n\n.moab__toolbarContainer___sQuuF {\n user-select: none;\n margin-left: auto;\n align-self: flex-end;\n display: flex;\n align-items: center;\n justify-content: center;\n background: #1a1a1a;\n color: #fff;\n border: none;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2), 0 4px 16px rgba(0, 0, 0, 0.1);\n pointer-events: auto;\n cursor: grab;\n transition: width 0.4s cubic-bezier(0.19, 1, 0.22, 1), transform 0.4s cubic-bezier(0.19, 1, 0.22, 1);\n}\n.moab__toolbarContainer___sQuuF.moab__dragging___c6CuY {\n transition: width 0.4s cubic-bezier(0.19, 1, 0.22, 1);\n cursor: grabbing;\n}\n.moab__toolbarContainer___sQuuF.moab__entrance___kTkKT {\n animation: moab__toolbarEnter___kVde9 0.5s cubic-bezier(0.34, 1.2, 0.64, 1) forwards;\n}\n.moab__toolbarContainer___sQuuF.moab__collapsed___xYm1N {\n width: 44px;\n height: 44px;\n border-radius: 22px;\n padding: 0;\n cursor: pointer;\n}\n.moab__toolbarContainer___sQuuF.moab__collapsed___xYm1N svg {\n margin-top: -1px;\n}\n.moab__toolbarContainer___sQuuF.moab__collapsed___xYm1N:hover {\n background: #2a2a2a;\n}\n.moab__toolbarContainer___sQuuF.moab__collapsed___xYm1N:active {\n transform: scale(0.95);\n}\n.moab__toolbarContainer___sQuuF.moab__expanded___Gshmq {\n width: calc-size(auto, size);\n height: 44px;\n border-radius: 1.5rem;\n padding: 0.375rem;\n}\n@supports not (width: calc-size(auto, size)) {\n .moab__toolbarContainer___sQuuF.moab__expanded___Gshmq {\n width: auto;\n }\n}\n\n.moab__toggleContent___KfJ0y {\n position: absolute;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: opacity 0.1s cubic-bezier(0.19, 1, 0.22, 1);\n}\n.moab__toggleContent___KfJ0y.moab__visible___1M9br {\n opacity: 1;\n visibility: visible;\n pointer-events: auto;\n}\n.moab__toggleContent___KfJ0y.moab__hidden___Zdoqy {\n opacity: 0;\n pointer-events: none;\n}\n\n.moab__controlsContent___KGEHS {\n display: flex;\n align-items: center;\n gap: 0.375rem;\n transition: filter 0.8s cubic-bezier(0.19, 1, 0.22, 1), opacity 0.8s cubic-bezier(0.19, 1, 0.22, 1), transform 0.6s cubic-bezier(0.19, 1, 0.22, 1);\n}\n.moab__controlsContent___KGEHS.moab__visible___1M9br {\n opacity: 1;\n filter: blur(0px);\n transform: scale(1);\n visibility: visible;\n pointer-events: auto;\n}\n.moab__controlsContent___KGEHS.moab__hidden___Zdoqy {\n opacity: 0;\n filter: blur(10px);\n transform: scale(0.4);\n pointer-events: none;\n}\n\n.moab__buttonWrapper___ZDZWf {\n position: relative;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.moab__controlButton___AYQcE {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 36px;\n height: 36px;\n padding: 0;\n margin: 0;\n border: none;\n background: transparent;\n color: rgba(255, 255, 255, 0.5);\n border-radius: 999px;\n cursor: pointer;\n transition: background 0.2s cubic-bezier(0.25, 1, 0.5, 1), color 0.2s cubic-bezier(0.25, 1, 0.5, 1), transform 0.15s cubic-bezier(0.25, 1, 0.5, 1);\n}\n.moab__controlButton___AYQcE:hover:not(:disabled) {\n background: rgba(255, 255, 255, 0.1);\n color: rgba(255, 255, 255, 0.9);\n}\n.moab__controlButton___AYQcE[data-active=true]:hover:not(:disabled) {\n background: rgba(60, 130, 247, 0.35);\n color: #3c82f7;\n}\n.moab__controlButton___AYQcE:active:not(:disabled) {\n transform: scale(0.95);\n}\n.moab__controlButton___AYQcE:disabled {\n opacity: 0.3;\n cursor: not-allowed;\n}\n.moab__controlButton___AYQcE[data-active=true] {\n color: #3c82f7;\n background: rgba(60, 130, 247, 0.25);\n}\n.moab__controlButton___AYQcE[data-danger]:hover:not(:disabled) {\n background: rgba(255, 59, 48, 0.15);\n color: #ff3b30;\n}\n\n.moab__buttonTooltip___ZUL58 {\n position: absolute;\n bottom: calc(100% + 0.5rem);\n left: 50%;\n transform: translateX(-50%);\n padding: 0.375rem 0.625rem;\n background: #383838;\n color: rgba(255, 255, 255, 0.7);\n font-size: 0.6875rem;\n font-weight: 400;\n line-height: 1.2;\n border-radius: 0.5rem;\n white-space: nowrap;\n opacity: 0;\n visibility: hidden;\n pointer-events: none;\n transition: opacity 0.15s ease, visibility 0.15s ease, transform 0.15s ease;\n z-index: 100001;\n box-shadow: 0px 1px 8px rgba(0, 0, 0, 0.28);\n}\n.moab__buttonTooltip___ZUL58::after {\n content: "";\n position: absolute;\n top: 100%;\n left: 50%;\n transform: translateX(-50%);\n width: 0;\n height: 0;\n border-left: 4px solid transparent;\n border-right: 4px solid transparent;\n border-top: 4px solid #383838;\n}\n\n.moab__buttonWrapper___ZDZWf:hover .moab__buttonTooltip___ZUL58 {\n opacity: 1;\n visibility: visible;\n}\n\n.moab__divider___o4mDB {\n width: 1px;\n height: 24px;\n background: rgba(255, 255, 255, 0.15);\n margin: 0 0.125rem;\n}\n\n.moab__light___s-xQ4.moab__toolbarContainer___sQuuF {\n background: #fff;\n color: rgba(0, 0, 0, 0.85);\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08), 0 4px 16px rgba(0, 0, 0, 0.06), 0 0 0 1px rgba(0, 0, 0, 0.04);\n}\n.moab__light___s-xQ4.moab__toolbarContainer___sQuuF.moab__collapsed___xYm1N:hover {\n background: #f5f5f5;\n}\n.moab__light___s-xQ4.moab__controlButton___AYQcE {\n color: rgba(0, 0, 0, 0.5);\n}\n.moab__light___s-xQ4.moab__controlButton___AYQcE:hover:not(:disabled) {\n background: rgba(0, 0, 0, 0.06);\n color: rgba(0, 0, 0, 0.85);\n}\n.moab__light___s-xQ4.moab__controlButton___AYQcE[data-active=true]:hover:not(:disabled) {\n background: rgba(60, 130, 247, 0.25);\n color: #3c82f7;\n}\n.moab__light___s-xQ4.moab__controlButton___AYQcE[data-active=true] {\n color: #3c82f7;\n background: rgba(60, 130, 247, 0.15);\n}\n.moab__light___s-xQ4.moab__controlButton___AYQcE[data-danger]:hover:not(:disabled) {\n background: rgba(255, 59, 48, 0.15);\n color: #ff3b30;\n}\n.moab__light___s-xQ4.moab__buttonTooltip___ZUL58 {\n background: #fff;\n color: rgba(0, 0, 0, 0.85);\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08), 0 4px 16px rgba(0, 0, 0, 0.06), 0 0 0 1px rgba(0, 0, 0, 0.04);\n}\n.moab__light___s-xQ4.moab__buttonTooltip___ZUL58::after {\n background: #fff;\n}\n.moab__light___s-xQ4.moab__divider___o4mDB {\n background: rgba(0, 0, 0, 0.1);\n}\n\n.moab-drawing-canvas {\n position: fixed !important;\n top: 0 !important;\n left: 0 !important;\n z-index: 50 !important;\n touch-action: none !important;\n}';
|
|
97
|
+
var classNames = { "toolbar": "moab__toolbar___GXd-P", "toolbarContainer": "moab__toolbarContainer___sQuuF", "dragging": "moab__dragging___c6CuY", "entrance": "moab__entrance___kTkKT", "collapsed": "moab__collapsed___xYm1N", "expanded": "moab__expanded___Gshmq", "toggleContent": "moab__toggleContent___KfJ0y", "visible": "moab__visible___1M9br", "hidden": "moab__hidden___Zdoqy", "controlsContent": "moab__controlsContent___KGEHS", "buttonWrapper": "moab__buttonWrapper___ZDZWf", "controlButton": "moab__controlButton___AYQcE", "buttonTooltip": "moab__buttonTooltip___ZUL58", "divider": "moab__divider___o4mDB", "light": "moab__light___s-xQ4"};
|
|
98
|
+
if (typeof document !== "undefined") {
|
|
99
|
+
let style = document.getElementById("moab-components-DrawingTool");
|
|
100
|
+
if (!style) {
|
|
101
|
+
style = document.createElement("style");
|
|
102
|
+
style.id = "moab-components-DrawingTool";
|
|
103
|
+
document.head.appendChild(style);
|
|
104
|
+
}
|
|
105
|
+
style.textContent = css;
|
|
106
|
+
}
|
|
107
|
+
var DrawingTool_module_default = classNames;
|
|
108
|
+
var TOOLBAR_POSITION_STORAGE_KEY = "moab-drawing-toolbar-position";
|
|
109
|
+
var DrawingTool = () => {
|
|
110
|
+
const canvasRef = React.useRef(null);
|
|
111
|
+
const toolbarRef = React.useRef(null);
|
|
112
|
+
const toolbarWrapperRef = React.useRef(null);
|
|
113
|
+
const [isDrawing, setIsDrawing] = React.useState(false);
|
|
114
|
+
const brushColor = "#3B82F6";
|
|
105
115
|
const brushSize = 5;
|
|
106
|
-
const [canvasSize, setCanvasSize] =
|
|
107
|
-
const [
|
|
108
|
-
const [
|
|
109
|
-
const [
|
|
110
|
-
const [
|
|
111
|
-
const [
|
|
112
|
-
const [
|
|
113
|
-
|
|
116
|
+
const [canvasSize, setCanvasSize] = React.useState({ width: 0, height: 0 });
|
|
117
|
+
const [isOpen, setIsOpen] = React.useState(false);
|
|
118
|
+
const [isPainting, setIsPainting] = React.useState(false);
|
|
119
|
+
const [hasContent, setHasContent] = React.useState(false);
|
|
120
|
+
const [isDraggingToolbar, setIsDraggingToolbar] = React.useState(false);
|
|
121
|
+
const [toolbarPosition, setToolbarPosition] = React.useState(null);
|
|
122
|
+
const [isToolbarReady, setIsToolbarReady] = React.useState(false);
|
|
123
|
+
const [dragStartPos, setDragStartPos] = React.useState(null);
|
|
124
|
+
const [dragRotation, setDragRotation] = React.useState(0);
|
|
125
|
+
const justFinishedToolbarDragRef = React.useRef(false);
|
|
126
|
+
React.useLayoutEffect(() => {
|
|
127
|
+
const toolbarWrapper = toolbarWrapperRef.current;
|
|
128
|
+
if (!toolbarWrapper) return;
|
|
129
|
+
const restorePosition = () => {
|
|
130
|
+
try {
|
|
131
|
+
const stored = localStorage.getItem(TOOLBAR_POSITION_STORAGE_KEY);
|
|
132
|
+
if (stored) {
|
|
133
|
+
const parsed = JSON.parse(stored);
|
|
134
|
+
if (typeof parsed?.x === "number" && typeof parsed?.y === "number") {
|
|
135
|
+
setToolbarPosition({ x: parsed.x, y: parsed.y });
|
|
136
|
+
setIsToolbarReady(true);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
} catch {
|
|
141
|
+
}
|
|
142
|
+
const rect = toolbarWrapper.getBoundingClientRect();
|
|
143
|
+
const padding = 20;
|
|
144
|
+
const startX = padding;
|
|
145
|
+
const startY = Math.max(
|
|
146
|
+
padding,
|
|
147
|
+
window.innerHeight - rect.height - padding
|
|
148
|
+
);
|
|
149
|
+
setToolbarPosition({ x: startX, y: startY });
|
|
150
|
+
setIsToolbarReady(true);
|
|
151
|
+
};
|
|
152
|
+
restorePosition();
|
|
153
|
+
}, []);
|
|
154
|
+
React.useEffect(() => {
|
|
155
|
+
if (!toolbarPosition) return;
|
|
156
|
+
try {
|
|
157
|
+
localStorage.setItem(
|
|
158
|
+
TOOLBAR_POSITION_STORAGE_KEY,
|
|
159
|
+
JSON.stringify(toolbarPosition)
|
|
160
|
+
);
|
|
161
|
+
} catch {
|
|
162
|
+
}
|
|
163
|
+
}, [toolbarPosition]);
|
|
164
|
+
React.useEffect(() => {
|
|
114
165
|
const updateSize = () => {
|
|
115
166
|
setCanvasSize({
|
|
116
167
|
width: window.innerWidth,
|
|
@@ -121,7 +172,7 @@ var DrawingTool = ({
|
|
|
121
172
|
window.addEventListener("resize", updateSize);
|
|
122
173
|
return () => window.removeEventListener("resize", updateSize);
|
|
123
174
|
}, []);
|
|
124
|
-
|
|
175
|
+
React.useEffect(() => {
|
|
125
176
|
const canvas = canvasRef.current;
|
|
126
177
|
if (!canvas || canvasSize.width === 0 || canvasSize.height === 0) return;
|
|
127
178
|
const ctx = canvas.getContext("2d");
|
|
@@ -134,7 +185,7 @@ var DrawingTool = ({
|
|
|
134
185
|
ctx.lineJoin = "round";
|
|
135
186
|
setHasContent(false);
|
|
136
187
|
}, [canvasSize, brushColor, brushSize]);
|
|
137
|
-
const getCoordinates =
|
|
188
|
+
const getCoordinates = React.useCallback(
|
|
138
189
|
(e) => {
|
|
139
190
|
const canvas = canvasRef.current;
|
|
140
191
|
if (!canvas) return { x: 0, y: 0 };
|
|
@@ -155,9 +206,9 @@ var DrawingTool = ({
|
|
|
155
206
|
},
|
|
156
207
|
[]
|
|
157
208
|
);
|
|
158
|
-
const startDrawing =
|
|
209
|
+
const startDrawing = React.useCallback(
|
|
159
210
|
(e) => {
|
|
160
|
-
if (!
|
|
211
|
+
if (!isPainting) return;
|
|
161
212
|
e.preventDefault();
|
|
162
213
|
const canvas = canvasRef.current;
|
|
163
214
|
const ctx = canvas?.getContext("2d");
|
|
@@ -167,9 +218,9 @@ var DrawingTool = ({
|
|
|
167
218
|
ctx.moveTo(x, y);
|
|
168
219
|
setIsDrawing(true);
|
|
169
220
|
},
|
|
170
|
-
[
|
|
221
|
+
[isPainting, getCoordinates]
|
|
171
222
|
);
|
|
172
|
-
const draw =
|
|
223
|
+
const draw = React.useCallback(
|
|
173
224
|
(e) => {
|
|
174
225
|
if (!isDrawing) return;
|
|
175
226
|
e.preventDefault();
|
|
@@ -183,140 +234,175 @@ var DrawingTool = ({
|
|
|
183
234
|
},
|
|
184
235
|
[isDrawing, getCoordinates]
|
|
185
236
|
);
|
|
186
|
-
const stopDrawing =
|
|
237
|
+
const stopDrawing = React.useCallback(() => {
|
|
187
238
|
setIsDrawing(false);
|
|
188
239
|
}, []);
|
|
189
|
-
const clearCanvas =
|
|
240
|
+
const clearCanvas = React.useCallback(() => {
|
|
190
241
|
const canvas = canvasRef.current;
|
|
191
242
|
const ctx = canvas?.getContext("2d");
|
|
192
243
|
if (!canvas || !ctx) return;
|
|
193
244
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
194
245
|
setHasContent(false);
|
|
195
246
|
}, []);
|
|
196
|
-
const takeScreenshot =
|
|
247
|
+
const takeScreenshot = React.useCallback(async () => {
|
|
248
|
+
const toolbar = toolbarRef.current;
|
|
197
249
|
try {
|
|
198
|
-
const toolbar = toolbarRef.current;
|
|
199
250
|
if (toolbar) {
|
|
200
|
-
toolbar.style.
|
|
251
|
+
toolbar.style.visibility = "hidden";
|
|
201
252
|
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
253
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
254
|
+
const dataUrl = await htmlToImage.toPng(document.body, {
|
|
255
|
+
width: window.innerWidth,
|
|
256
|
+
height: window.innerHeight,
|
|
257
|
+
style: {
|
|
258
|
+
transform: `translate(-${window.scrollX}px, -${window.scrollY}px)`
|
|
259
|
+
},
|
|
260
|
+
filter: (node) => {
|
|
261
|
+
if (node instanceof HTMLElement) {
|
|
262
|
+
if (node.closest?.("[data-drawing-toolbar]")) {
|
|
263
|
+
return false;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
return true;
|
|
267
|
+
}
|
|
207
268
|
});
|
|
208
269
|
if (toolbar) {
|
|
209
|
-
toolbar.style.
|
|
270
|
+
toolbar.style.visibility = "";
|
|
271
|
+
}
|
|
272
|
+
const response = await fetch(dataUrl);
|
|
273
|
+
const blob = await response.blob();
|
|
274
|
+
try {
|
|
275
|
+
await navigator.clipboard.write([
|
|
276
|
+
new ClipboardItem({
|
|
277
|
+
"image/png": blob
|
|
278
|
+
})
|
|
279
|
+
]);
|
|
280
|
+
} catch (err) {
|
|
281
|
+
console.error("Failed to copy to clipboard:", err);
|
|
282
|
+
const url = URL.createObjectURL(blob);
|
|
283
|
+
const a = document.createElement("a");
|
|
284
|
+
a.href = url;
|
|
285
|
+
a.download = "screenshot.png";
|
|
286
|
+
a.click();
|
|
287
|
+
URL.revokeObjectURL(url);
|
|
210
288
|
}
|
|
211
|
-
canvas.toBlob(async (blob) => {
|
|
212
|
-
if (!blob) return;
|
|
213
|
-
try {
|
|
214
|
-
await navigator.clipboard.write([
|
|
215
|
-
new ClipboardItem({
|
|
216
|
-
"image/png": blob
|
|
217
|
-
})
|
|
218
|
-
]);
|
|
219
|
-
} catch (err) {
|
|
220
|
-
console.error("Failed to copy to clipboard:", err);
|
|
221
|
-
const url = URL.createObjectURL(blob);
|
|
222
|
-
const a = document.createElement("a");
|
|
223
|
-
a.href = url;
|
|
224
|
-
a.download = "screenshot.png";
|
|
225
|
-
a.click();
|
|
226
|
-
URL.revokeObjectURL(url);
|
|
227
|
-
}
|
|
228
|
-
}, "image/png");
|
|
229
289
|
} catch (error) {
|
|
230
290
|
console.error("Failed to take screenshot:", error);
|
|
291
|
+
if (toolbar) {
|
|
292
|
+
toolbar.style.visibility = "";
|
|
293
|
+
}
|
|
231
294
|
}
|
|
232
295
|
}, []);
|
|
233
|
-
|
|
296
|
+
React.useEffect(() => {
|
|
234
297
|
const canvas = canvasRef.current;
|
|
235
298
|
const ctx = canvas?.getContext("2d");
|
|
236
299
|
if (!ctx) return;
|
|
237
300
|
ctx.strokeStyle = brushColor;
|
|
238
301
|
ctx.lineWidth = brushSize;
|
|
239
302
|
}, [brushColor, brushSize]);
|
|
240
|
-
const
|
|
241
|
-
(
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
const threshold = 50;
|
|
245
|
-
if (Math.abs(deltaX) < threshold && Math.abs(deltaY) < threshold) {
|
|
246
|
-
return null;
|
|
303
|
+
const handleToolbarMouseDown = React.useCallback(
|
|
304
|
+
(e) => {
|
|
305
|
+
if (e.target.closest("button")) {
|
|
306
|
+
return;
|
|
247
307
|
}
|
|
248
|
-
const
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
308
|
+
const toolbarWrapper = toolbarWrapperRef.current;
|
|
309
|
+
if (!toolbarWrapper) return;
|
|
310
|
+
e.preventDefault();
|
|
311
|
+
e.stopPropagation();
|
|
312
|
+
const rect = toolbarWrapper.getBoundingClientRect();
|
|
313
|
+
const randomRotation = (Math.random() - 0.5) * 10;
|
|
314
|
+
setDragRotation(randomRotation);
|
|
315
|
+
setDragStartPos({
|
|
316
|
+
x: e.clientX,
|
|
317
|
+
y: e.clientY,
|
|
318
|
+
toolbarX: toolbarPosition?.x ?? rect.left,
|
|
319
|
+
toolbarY: toolbarPosition?.y ?? rect.top,
|
|
320
|
+
toolbarWidth: rect.width,
|
|
321
|
+
toolbarHeight: rect.height
|
|
322
|
+
});
|
|
258
323
|
},
|
|
259
|
-
[]
|
|
324
|
+
[toolbarPosition]
|
|
260
325
|
);
|
|
261
|
-
|
|
262
|
-
if (
|
|
263
|
-
|
|
264
|
-
}
|
|
265
|
-
e.preventDefault();
|
|
266
|
-
e.stopPropagation();
|
|
267
|
-
setIsDragging(true);
|
|
268
|
-
setDragStart({
|
|
269
|
-
x: e.clientX,
|
|
270
|
-
y: e.clientY
|
|
271
|
-
});
|
|
272
|
-
}, []);
|
|
273
|
-
react.useEffect(() => {
|
|
326
|
+
React.useEffect(() => {
|
|
327
|
+
if (!dragStartPos) return;
|
|
328
|
+
const DRAG_THRESHOLD = 5;
|
|
274
329
|
const handleMouseMove = (e) => {
|
|
275
|
-
|
|
276
|
-
e.
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
const
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
else if (vertical === "bottom" && horizontal === "left")
|
|
295
|
-
setCorner("bottom-left");
|
|
296
|
-
else setCorner("bottom-right");
|
|
330
|
+
const deltaX = e.clientX - dragStartPos.x;
|
|
331
|
+
const deltaY = e.clientY - dragStartPos.y;
|
|
332
|
+
const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
|
|
333
|
+
if (!isDraggingToolbar && distance > DRAG_THRESHOLD) {
|
|
334
|
+
setIsDraggingToolbar(true);
|
|
335
|
+
}
|
|
336
|
+
if (isDraggingToolbar || distance > DRAG_THRESHOLD) {
|
|
337
|
+
const padding = 20;
|
|
338
|
+
const maxX = window.innerWidth - dragStartPos.toolbarWidth - padding;
|
|
339
|
+
const maxY = window.innerHeight - dragStartPos.toolbarHeight - padding;
|
|
340
|
+
const newX = Math.max(
|
|
341
|
+
padding,
|
|
342
|
+
Math.min(maxX, dragStartPos.toolbarX + deltaX)
|
|
343
|
+
);
|
|
344
|
+
const newY = Math.max(
|
|
345
|
+
padding,
|
|
346
|
+
Math.min(maxY, dragStartPos.toolbarY + deltaY)
|
|
347
|
+
);
|
|
348
|
+
setToolbarPosition({ x: newX, y: newY });
|
|
297
349
|
}
|
|
298
350
|
};
|
|
299
|
-
const handleMouseUp = (
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
351
|
+
const handleMouseUp = () => {
|
|
352
|
+
if (isDraggingToolbar) {
|
|
353
|
+
justFinishedToolbarDragRef.current = true;
|
|
354
|
+
setTimeout(() => {
|
|
355
|
+
justFinishedToolbarDragRef.current = false;
|
|
356
|
+
}, 50);
|
|
357
|
+
}
|
|
358
|
+
setIsDraggingToolbar(false);
|
|
359
|
+
setDragStartPos(null);
|
|
303
360
|
};
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
passive: false
|
|
307
|
-
});
|
|
308
|
-
document.addEventListener("mouseup", handleMouseUp, { passive: false });
|
|
309
|
-
}
|
|
361
|
+
document.addEventListener("mousemove", handleMouseMove);
|
|
362
|
+
document.addEventListener("mouseup", handleMouseUp);
|
|
310
363
|
return () => {
|
|
311
364
|
document.removeEventListener("mousemove", handleMouseMove);
|
|
312
365
|
document.removeEventListener("mouseup", handleMouseUp);
|
|
313
366
|
};
|
|
314
|
-
}, [
|
|
315
|
-
|
|
367
|
+
}, [dragStartPos, isDraggingToolbar]);
|
|
368
|
+
React.useEffect(() => {
|
|
369
|
+
if (!toolbarPosition) return;
|
|
370
|
+
const constrainPosition = () => {
|
|
371
|
+
const toolbarWrapper = toolbarWrapperRef.current;
|
|
372
|
+
const rect = toolbarWrapper?.getBoundingClientRect();
|
|
373
|
+
const width = rect?.width ?? 44;
|
|
374
|
+
const height = rect?.height ?? 44;
|
|
375
|
+
const padding = 20;
|
|
376
|
+
const maxX = window.innerWidth - width - padding;
|
|
377
|
+
const maxY = window.innerHeight - height - padding;
|
|
378
|
+
const newX = Math.max(padding, Math.min(maxX, toolbarPosition.x));
|
|
379
|
+
const newY = Math.max(padding, Math.min(maxY, toolbarPosition.y));
|
|
380
|
+
if (newX !== toolbarPosition.x || newY !== toolbarPosition.y) {
|
|
381
|
+
setToolbarPosition({ x: newX, y: newY });
|
|
382
|
+
}
|
|
383
|
+
};
|
|
384
|
+
constrainPosition();
|
|
385
|
+
window.addEventListener("resize", constrainPosition);
|
|
386
|
+
return () => window.removeEventListener("resize", constrainPosition);
|
|
387
|
+
}, [toolbarPosition, isOpen]);
|
|
388
|
+
const [showEntranceAnimation, setShowEntranceAnimation] = React.useState(false);
|
|
389
|
+
const [isDarkMode] = React.useState(true);
|
|
390
|
+
React.useEffect(() => {
|
|
391
|
+
setShowEntranceAnimation(true);
|
|
392
|
+
const timer = setTimeout(() => setShowEntranceAnimation(false), 500);
|
|
393
|
+
return () => clearTimeout(timer);
|
|
394
|
+
}, []);
|
|
395
|
+
React.useEffect(() => {
|
|
396
|
+
if (!isPainting) {
|
|
397
|
+
setIsDrawing(false);
|
|
398
|
+
}
|
|
399
|
+
}, [isPainting]);
|
|
400
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(React__default.default.Fragment, { children: [
|
|
316
401
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
317
402
|
"canvas",
|
|
318
403
|
{
|
|
319
404
|
ref: canvasRef,
|
|
405
|
+
className: "moab-drawing-canvas",
|
|
320
406
|
onMouseDown: startDrawing,
|
|
321
407
|
onMouseMove: draw,
|
|
322
408
|
onMouseUp: stopDrawing,
|
|
@@ -324,186 +410,150 @@ var DrawingTool = ({
|
|
|
324
410
|
onTouchStart: startDrawing,
|
|
325
411
|
onTouchMove: draw,
|
|
326
412
|
onTouchEnd: stopDrawing,
|
|
327
|
-
className: `fixed top-0 left-0 touch-none z-50 transition-opacity ${enabled && !isDragging ? "cursor-crosshair pointer-events-auto opacity-100" : "pointer-events-none opacity-0"}`,
|
|
328
413
|
style: {
|
|
329
414
|
width: `${canvasSize.width}px`,
|
|
330
|
-
height: `${canvasSize.height}px
|
|
415
|
+
height: `${canvasSize.height}px`,
|
|
416
|
+
cursor: isPainting && !isDraggingToolbar ? "crosshair" : "default",
|
|
417
|
+
pointerEvents: isPainting && !isDraggingToolbar ? "auto" : "none",
|
|
418
|
+
opacity: isPainting && !isDraggingToolbar ? 1 : 0,
|
|
419
|
+
transition: "opacity 300ms ease-in-out"
|
|
331
420
|
}
|
|
332
421
|
}
|
|
333
422
|
),
|
|
334
|
-
/* @__PURE__ */ jsxRuntime.
|
|
423
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
335
424
|
"div",
|
|
336
425
|
{
|
|
337
|
-
ref:
|
|
338
|
-
className:
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
children:
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
|
|
426
|
+
ref: toolbarWrapperRef,
|
|
427
|
+
className: DrawingTool_module_default.toolbar,
|
|
428
|
+
"data-drawing-toolbar": true,
|
|
429
|
+
style: toolbarPosition ? {
|
|
430
|
+
left: toolbarPosition.x,
|
|
431
|
+
top: toolbarPosition.y,
|
|
432
|
+
right: "auto",
|
|
433
|
+
bottom: "auto",
|
|
434
|
+
visibility: isToolbarReady ? "visible" : "hidden"
|
|
435
|
+
} : { visibility: "hidden" },
|
|
436
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
437
|
+
"div",
|
|
438
|
+
{
|
|
439
|
+
ref: toolbarRef,
|
|
440
|
+
className: `${DrawingTool_module_default.toolbarContainer} ${isOpen ? DrawingTool_module_default.expanded : DrawingTool_module_default.collapsed} ${showEntranceAnimation ? DrawingTool_module_default.entrance : ""} ${isDraggingToolbar ? DrawingTool_module_default.dragging : ""} ${!isDarkMode ? DrawingTool_module_default.light : ""}`,
|
|
441
|
+
onClick: !isOpen ? (e) => {
|
|
442
|
+
if (justFinishedToolbarDragRef.current) {
|
|
443
|
+
e.preventDefault();
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
446
|
+
setIsOpen(true);
|
|
447
|
+
} : void 0,
|
|
448
|
+
onMouseDown: handleToolbarMouseDown,
|
|
449
|
+
role: !isOpen ? "button" : void 0,
|
|
450
|
+
tabIndex: !isOpen ? 0 : -1,
|
|
451
|
+
style: isDraggingToolbar ? {
|
|
452
|
+
cursor: "grabbing",
|
|
453
|
+
transform: `scale(1.05) rotate(${dragRotation}deg)`
|
|
454
|
+
} : void 0,
|
|
455
|
+
children: [
|
|
456
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
457
|
+
"div",
|
|
359
458
|
{
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
takeScreenshot();
|
|
363
|
-
},
|
|
364
|
-
className: "p-2 text-white hover:bg-neutral-600/30 rounded-full transition-colors focus:outline-none whitespace-nowrap",
|
|
365
|
-
"aria-label": "Take screenshot",
|
|
366
|
-
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
367
|
-
"svg",
|
|
368
|
-
{
|
|
369
|
-
xmlns: "http://www.w3.org/2000/svg",
|
|
370
|
-
width: "18",
|
|
371
|
-
height: "18",
|
|
372
|
-
viewBox: "0 0 24 24",
|
|
373
|
-
fill: "none",
|
|
374
|
-
stroke: "currentColor",
|
|
375
|
-
strokeWidth: "2",
|
|
376
|
-
strokeLinecap: "round",
|
|
377
|
-
strokeLinejoin: "round",
|
|
378
|
-
className: "lucide lucide-camera",
|
|
379
|
-
children: [
|
|
380
|
-
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M13.997 4a2 2 0 0 1 1.76 1.05l.486.9A2 2 0 0 0 18.003 7H20a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V9a2 2 0 0 1 2-2h1.997a2 2 0 0 0 1.759-1.048l.489-.904A2 2 0 0 1 10.004 4z" }),
|
|
381
|
-
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "13", r: "3" })
|
|
382
|
-
]
|
|
383
|
-
}
|
|
384
|
-
)
|
|
459
|
+
className: `${DrawingTool_module_default.toggleContent} ${!isOpen ? DrawingTool_module_default.visible : DrawingTool_module_default.hidden}`,
|
|
460
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Palette, { size: 20, strokeWidth: 2 })
|
|
385
461
|
}
|
|
386
|
-
)
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
style: {
|
|
394
|
-
maxWidth: enabled ? "100px" : "0px",
|
|
395
|
-
opacity: enabled ? 1 : 0,
|
|
396
|
-
transition: "max-width 300ms ease-in-out, opacity 300ms ease-in-out"
|
|
397
|
-
},
|
|
398
|
-
children: [
|
|
399
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
400
|
-
"button",
|
|
401
|
-
{
|
|
402
|
-
onClick: (e) => {
|
|
403
|
-
e.stopPropagation();
|
|
404
|
-
clearCanvas();
|
|
405
|
-
setShowClearTooltip(false);
|
|
406
|
-
},
|
|
407
|
-
onMouseEnter: () => {
|
|
408
|
-
if (hasContent) {
|
|
409
|
-
setShowClearTooltip(true);
|
|
410
|
-
}
|
|
411
|
-
},
|
|
412
|
-
onMouseLeave: () => setShowClearTooltip(false),
|
|
413
|
-
disabled: !hasContent,
|
|
414
|
-
className: "p-2 text-white rounded-full focus:outline-none disabled:opacity-50 disabled:cursor-not-allowed relative group",
|
|
415
|
-
"aria-label": "Clear canvas",
|
|
416
|
-
children: [
|
|
417
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 rounded-full bg-red-600/30 opacity-0 group-hover:opacity-100 transition-opacity disabled:group-hover:opacity-0" }),
|
|
462
|
+
),
|
|
463
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
464
|
+
"div",
|
|
465
|
+
{
|
|
466
|
+
className: `${DrawingTool_module_default.controlsContent} ${isOpen ? DrawingTool_module_default.visible : DrawingTool_module_default.hidden}`,
|
|
467
|
+
children: [
|
|
468
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: DrawingTool_module_default.buttonWrapper, children: [
|
|
418
469
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
419
|
-
|
|
470
|
+
"button",
|
|
420
471
|
{
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
472
|
+
className: `${DrawingTool_module_default.controlButton} ${!isDarkMode ? DrawingTool_module_default.light : ""}`,
|
|
473
|
+
onClick: (e) => {
|
|
474
|
+
e.stopPropagation();
|
|
475
|
+
setIsPainting((prev) => !prev);
|
|
476
|
+
},
|
|
477
|
+
"data-active": isPainting,
|
|
478
|
+
"aria-label": "Toggle draw mode",
|
|
479
|
+
"aria-pressed": isPainting,
|
|
480
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Brush, { size: 18, strokeWidth: 2 })
|
|
424
481
|
}
|
|
425
|
-
)
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
className:
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
},
|
|
501
|
-
"aria-label": "Close",
|
|
502
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { size: 18, strokeWidth: 2.5 })
|
|
503
|
-
}
|
|
504
|
-
)
|
|
505
|
-
] })
|
|
506
|
-
]
|
|
482
|
+
),
|
|
483
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: DrawingTool_module_default.buttonTooltip, children: "Draw" })
|
|
484
|
+
] }),
|
|
485
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: DrawingTool_module_default.buttonWrapper, children: [
|
|
486
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
487
|
+
"button",
|
|
488
|
+
{
|
|
489
|
+
className: `${DrawingTool_module_default.controlButton} ${!isDarkMode ? DrawingTool_module_default.light : ""}`,
|
|
490
|
+
onClick: (e) => {
|
|
491
|
+
e.stopPropagation();
|
|
492
|
+
takeScreenshot();
|
|
493
|
+
},
|
|
494
|
+
"aria-label": "Take screenshot",
|
|
495
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
496
|
+
"svg",
|
|
497
|
+
{
|
|
498
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
499
|
+
width: "18",
|
|
500
|
+
height: "18",
|
|
501
|
+
viewBox: "0 0 24 24",
|
|
502
|
+
fill: "none",
|
|
503
|
+
stroke: "currentColor",
|
|
504
|
+
strokeWidth: "2",
|
|
505
|
+
strokeLinecap: "round",
|
|
506
|
+
strokeLinejoin: "round",
|
|
507
|
+
children: [
|
|
508
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M13.997 4a2 2 0 0 1 1.76 1.05l.486.9A2 2 0 0 0 18.003 7H20a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V9a2 2 0 0 1 2-2h1.997a2 2 0 0 0 1.759-1.048l.489-.904A2 2 0 0 1 10.004 4z" }),
|
|
509
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "13", r: "3" })
|
|
510
|
+
]
|
|
511
|
+
}
|
|
512
|
+
)
|
|
513
|
+
}
|
|
514
|
+
),
|
|
515
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: DrawingTool_module_default.buttonTooltip, children: "Take screenshot" })
|
|
516
|
+
] }),
|
|
517
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: DrawingTool_module_default.buttonWrapper, children: [
|
|
518
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
519
|
+
"button",
|
|
520
|
+
{
|
|
521
|
+
className: `${DrawingTool_module_default.controlButton} ${!isDarkMode ? DrawingTool_module_default.light : ""}`,
|
|
522
|
+
onClick: (e) => {
|
|
523
|
+
e.stopPropagation();
|
|
524
|
+
clearCanvas();
|
|
525
|
+
},
|
|
526
|
+
disabled: !hasContent,
|
|
527
|
+
"data-danger": true,
|
|
528
|
+
"aria-label": "Clear",
|
|
529
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { size: 18, strokeWidth: 2 })
|
|
530
|
+
}
|
|
531
|
+
),
|
|
532
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: DrawingTool_module_default.buttonTooltip, children: "Clear" })
|
|
533
|
+
] }),
|
|
534
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: `${DrawingTool_module_default.divider} ${!isDarkMode ? DrawingTool_module_default.light : ""}` }),
|
|
535
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: DrawingTool_module_default.buttonWrapper, children: [
|
|
536
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
537
|
+
"button",
|
|
538
|
+
{
|
|
539
|
+
className: `${DrawingTool_module_default.controlButton} ${!isDarkMode ? DrawingTool_module_default.light : ""}`,
|
|
540
|
+
onClick: (e) => {
|
|
541
|
+
e.stopPropagation();
|
|
542
|
+
setIsOpen(false);
|
|
543
|
+
setIsPainting(false);
|
|
544
|
+
},
|
|
545
|
+
"aria-label": "Close",
|
|
546
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { size: 18, strokeWidth: 2.5 })
|
|
547
|
+
}
|
|
548
|
+
),
|
|
549
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: DrawingTool_module_default.buttonTooltip, children: "Close" })
|
|
550
|
+
] })
|
|
551
|
+
]
|
|
552
|
+
}
|
|
553
|
+
)
|
|
554
|
+
]
|
|
555
|
+
}
|
|
556
|
+
)
|
|
507
557
|
}
|
|
508
558
|
)
|
|
509
559
|
] });
|