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