@ewjdev/anyclick-react 1.1.0 → 1.2.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/README.md +53 -63
- package/dist/index.d.mts +88 -36
- package/dist/index.d.ts +88 -36
- package/dist/index.js +416 -74
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +418 -75
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -3
package/dist/index.mjs
CHANGED
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
useRef as useRef2,
|
|
10
10
|
useState as useState3
|
|
11
11
|
} from "react";
|
|
12
|
-
import {
|
|
12
|
+
import { createAnyclickClient } from "@ewjdev/anyclick-core";
|
|
13
13
|
|
|
14
14
|
// src/context.ts
|
|
15
15
|
import { createContext, useContext } from "react";
|
|
@@ -78,16 +78,19 @@ var menuStyles = {
|
|
|
78
78
|
...menuCSSVariables
|
|
79
79
|
},
|
|
80
80
|
header: {
|
|
81
|
-
padding: "12px
|
|
81
|
+
padding: "8px 12px",
|
|
82
82
|
borderBottom: "1px solid var(--anyclick-menu-border, #e5e5e5)",
|
|
83
83
|
color: "var(--anyclick-menu-text-muted, #666)",
|
|
84
84
|
fontSize: "12px",
|
|
85
85
|
fontWeight: 500,
|
|
86
86
|
textTransform: "uppercase",
|
|
87
|
-
letterSpacing: "0.5px"
|
|
87
|
+
letterSpacing: "0.5px",
|
|
88
|
+
display: "flex",
|
|
89
|
+
justifyContent: "space-between",
|
|
90
|
+
alignItems: "center"
|
|
88
91
|
},
|
|
89
92
|
itemList: {
|
|
90
|
-
padding: "
|
|
93
|
+
padding: "0px 0px"
|
|
91
94
|
},
|
|
92
95
|
item: {
|
|
93
96
|
display: "flex",
|
|
@@ -489,6 +492,7 @@ function generateHighlightCSS(colors) {
|
|
|
489
492
|
border-radius: 4px !important;
|
|
490
493
|
position: relative;
|
|
491
494
|
z-index: 9997;
|
|
495
|
+
top: 0;
|
|
492
496
|
}
|
|
493
497
|
|
|
494
498
|
.${HIGHLIGHT_CONTAINER_CLASS} {
|
|
@@ -731,45 +735,56 @@ var __iconNode7 = [
|
|
|
731
735
|
];
|
|
732
736
|
var Flag = createLucideIcon("flag", __iconNode7);
|
|
733
737
|
|
|
734
|
-
// ../../node_modules/lucide-react/dist/esm/icons/
|
|
738
|
+
// ../../node_modules/lucide-react/dist/esm/icons/grip-vertical.js
|
|
735
739
|
var __iconNode8 = [
|
|
740
|
+
["circle", { cx: "9", cy: "12", r: "1", key: "1vctgf" }],
|
|
741
|
+
["circle", { cx: "9", cy: "5", r: "1", key: "hp0tcf" }],
|
|
742
|
+
["circle", { cx: "9", cy: "19", r: "1", key: "fkjjf6" }],
|
|
743
|
+
["circle", { cx: "15", cy: "12", r: "1", key: "1tmaij" }],
|
|
744
|
+
["circle", { cx: "15", cy: "5", r: "1", key: "19l28e" }],
|
|
745
|
+
["circle", { cx: "15", cy: "19", r: "1", key: "f4zoj3" }]
|
|
746
|
+
];
|
|
747
|
+
var GripVertical = createLucideIcon("grip-vertical", __iconNode8);
|
|
748
|
+
|
|
749
|
+
// ../../node_modules/lucide-react/dist/esm/icons/image.js
|
|
750
|
+
var __iconNode9 = [
|
|
736
751
|
["rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", ry: "2", key: "1m3agn" }],
|
|
737
752
|
["circle", { cx: "9", cy: "9", r: "2", key: "af1f0g" }],
|
|
738
753
|
["path", { d: "m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21", key: "1xmnt7" }]
|
|
739
754
|
];
|
|
740
|
-
var Image = createLucideIcon("image",
|
|
755
|
+
var Image = createLucideIcon("image", __iconNode9);
|
|
741
756
|
|
|
742
757
|
// ../../node_modules/lucide-react/dist/esm/icons/loader-circle.js
|
|
743
|
-
var
|
|
744
|
-
var LoaderCircle = createLucideIcon("loader-circle",
|
|
758
|
+
var __iconNode10 = [["path", { d: "M21 12a9 9 0 1 1-6.219-8.56", key: "13zald" }]];
|
|
759
|
+
var LoaderCircle = createLucideIcon("loader-circle", __iconNode10);
|
|
745
760
|
|
|
746
761
|
// ../../node_modules/lucide-react/dist/esm/icons/plus.js
|
|
747
|
-
var
|
|
762
|
+
var __iconNode11 = [
|
|
748
763
|
["path", { d: "M5 12h14", key: "1ays0h" }],
|
|
749
764
|
["path", { d: "M12 5v14", key: "s699le" }]
|
|
750
765
|
];
|
|
751
|
-
var Plus = createLucideIcon("plus",
|
|
766
|
+
var Plus = createLucideIcon("plus", __iconNode11);
|
|
752
767
|
|
|
753
768
|
// ../../node_modules/lucide-react/dist/esm/icons/refresh-cw.js
|
|
754
|
-
var
|
|
769
|
+
var __iconNode12 = [
|
|
755
770
|
["path", { d: "M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8", key: "v9h5vc" }],
|
|
756
771
|
["path", { d: "M21 3v5h-5", key: "1q7to0" }],
|
|
757
772
|
["path", { d: "M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16", key: "3uifl3" }],
|
|
758
773
|
["path", { d: "M8 16H3v5", key: "1cv678" }]
|
|
759
774
|
];
|
|
760
|
-
var RefreshCw = createLucideIcon("refresh-cw",
|
|
775
|
+
var RefreshCw = createLucideIcon("refresh-cw", __iconNode12);
|
|
761
776
|
|
|
762
777
|
// ../../node_modules/lucide-react/dist/esm/icons/shrink.js
|
|
763
|
-
var
|
|
778
|
+
var __iconNode13 = [
|
|
764
779
|
["path", { d: "m15 15 6 6m-6-6v4.8m0-4.8h4.8", key: "17vawe" }],
|
|
765
780
|
["path", { d: "M9 19.8V15m0 0H4.2M9 15l-6 6", key: "chjx8e" }],
|
|
766
781
|
["path", { d: "M15 4.2V9m0 0h4.8M15 9l6-6", key: "lav6yq" }],
|
|
767
782
|
["path", { d: "M9 4.2V9m0 0H4.2M9 9 3 3", key: "1pxi2q" }]
|
|
768
783
|
];
|
|
769
|
-
var Shrink = createLucideIcon("shrink",
|
|
784
|
+
var Shrink = createLucideIcon("shrink", __iconNode13);
|
|
770
785
|
|
|
771
786
|
// ../../node_modules/lucide-react/dist/esm/icons/thumbs-up.js
|
|
772
|
-
var
|
|
787
|
+
var __iconNode14 = [
|
|
773
788
|
["path", { d: "M7 10v12", key: "1qc93n" }],
|
|
774
789
|
[
|
|
775
790
|
"path",
|
|
@@ -779,14 +794,14 @@ var __iconNode13 = [
|
|
|
779
794
|
}
|
|
780
795
|
]
|
|
781
796
|
];
|
|
782
|
-
var ThumbsUp = createLucideIcon("thumbs-up",
|
|
797
|
+
var ThumbsUp = createLucideIcon("thumbs-up", __iconNode14);
|
|
783
798
|
|
|
784
799
|
// ../../node_modules/lucide-react/dist/esm/icons/x.js
|
|
785
|
-
var
|
|
800
|
+
var __iconNode15 = [
|
|
786
801
|
["path", { d: "M18 6 6 18", key: "1bl5f8" }],
|
|
787
802
|
["path", { d: "m6 6 12 12", key: "d8bk6v" }]
|
|
788
803
|
];
|
|
789
|
-
var X = createLucideIcon("x",
|
|
804
|
+
var X = createLucideIcon("x", __iconNode15);
|
|
790
805
|
|
|
791
806
|
// src/ScreenshotPreview.tsx
|
|
792
807
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -1016,11 +1031,30 @@ function ScreenshotPreview({
|
|
|
1016
1031
|
|
|
1017
1032
|
// src/ContextMenu.tsx
|
|
1018
1033
|
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
1034
|
+
var VIEWPORT_PADDING = 10;
|
|
1035
|
+
var screenshotIndicatorStyle = {
|
|
1036
|
+
display: "flex",
|
|
1037
|
+
marginLeft: "4px",
|
|
1038
|
+
opacity: 0.7,
|
|
1039
|
+
justifyContent: "flex-end",
|
|
1040
|
+
flex: 1
|
|
1041
|
+
};
|
|
1019
1042
|
var defaultIcons = {
|
|
1020
1043
|
issue: /* @__PURE__ */ jsx2(Flag, { className: "w-4 h-4" }),
|
|
1021
1044
|
feature: /* @__PURE__ */ jsx2(Plus, { className: "w-4 h-4" }),
|
|
1022
1045
|
like: /* @__PURE__ */ jsx2(ThumbsUp, { className: "w-4 h-4" })
|
|
1023
1046
|
};
|
|
1047
|
+
var DefaultHeader = ({
|
|
1048
|
+
styles,
|
|
1049
|
+
className,
|
|
1050
|
+
title = "Send Feedback",
|
|
1051
|
+
children
|
|
1052
|
+
}) => {
|
|
1053
|
+
return /* @__PURE__ */ jsxs2("div", { style: styles, className, children: [
|
|
1054
|
+
/* @__PURE__ */ jsx2("span", { children: title }),
|
|
1055
|
+
children
|
|
1056
|
+
] });
|
|
1057
|
+
};
|
|
1024
1058
|
function MenuItem({
|
|
1025
1059
|
item,
|
|
1026
1060
|
onClick,
|
|
@@ -1028,6 +1062,7 @@ function MenuItem({
|
|
|
1028
1062
|
hasChildren
|
|
1029
1063
|
}) {
|
|
1030
1064
|
const [isHovered, setIsHovered] = useState2(false);
|
|
1065
|
+
const [isPressed, setIsPressed] = useState2(false);
|
|
1031
1066
|
return /* @__PURE__ */ jsxs2(
|
|
1032
1067
|
"button",
|
|
1033
1068
|
{
|
|
@@ -1035,11 +1070,25 @@ function MenuItem({
|
|
|
1035
1070
|
onClick,
|
|
1036
1071
|
disabled,
|
|
1037
1072
|
onMouseEnter: () => setIsHovered(true),
|
|
1038
|
-
onMouseLeave: () =>
|
|
1073
|
+
onMouseLeave: () => {
|
|
1074
|
+
setIsHovered(false);
|
|
1075
|
+
setIsPressed(false);
|
|
1076
|
+
},
|
|
1077
|
+
onTouchStart: () => setIsPressed(true),
|
|
1078
|
+
onTouchEnd: () => setIsPressed(false),
|
|
1079
|
+
onTouchCancel: () => setIsPressed(false),
|
|
1039
1080
|
style: {
|
|
1040
1081
|
...menuStyles.item,
|
|
1041
|
-
|
|
1042
|
-
...
|
|
1082
|
+
// Apply hover/pressed state for both mouse and touch
|
|
1083
|
+
...isHovered || isPressed ? menuStyles.itemHover : {},
|
|
1084
|
+
...disabled ? { opacity: 0.5, cursor: "not-allowed" } : {},
|
|
1085
|
+
// Ensure minimum touch target size (44px is recommended)
|
|
1086
|
+
minHeight: "44px",
|
|
1087
|
+
// Prevent text selection on touch
|
|
1088
|
+
WebkitUserSelect: "none",
|
|
1089
|
+
userSelect: "none",
|
|
1090
|
+
// Prevent touch callout on iOS
|
|
1091
|
+
WebkitTouchCallout: "none"
|
|
1043
1092
|
},
|
|
1044
1093
|
children: [
|
|
1045
1094
|
/* @__PURE__ */ jsx2("span", { style: menuStyles.itemIcon, children: item.icon ?? defaultIcons[item.type] }),
|
|
@@ -1057,18 +1106,31 @@ function MenuItem({
|
|
|
1057
1106
|
}
|
|
1058
1107
|
function BackButton({ onClick }) {
|
|
1059
1108
|
const [isHovered, setIsHovered] = useState2(false);
|
|
1109
|
+
const [isPressed, setIsPressed] = useState2(false);
|
|
1060
1110
|
return /* @__PURE__ */ jsxs2(
|
|
1061
1111
|
"button",
|
|
1062
1112
|
{
|
|
1063
1113
|
type: "button",
|
|
1064
1114
|
onClick,
|
|
1065
1115
|
onMouseEnter: () => setIsHovered(true),
|
|
1066
|
-
onMouseLeave: () =>
|
|
1116
|
+
onMouseLeave: () => {
|
|
1117
|
+
setIsHovered(false);
|
|
1118
|
+
setIsPressed(false);
|
|
1119
|
+
},
|
|
1120
|
+
onTouchStart: () => setIsPressed(true),
|
|
1121
|
+
onTouchEnd: () => setIsPressed(false),
|
|
1122
|
+
onTouchCancel: () => setIsPressed(false),
|
|
1067
1123
|
style: {
|
|
1068
1124
|
...menuStyles.item,
|
|
1069
|
-
...isHovered ? menuStyles.itemHover : {},
|
|
1125
|
+
...isHovered || isPressed ? menuStyles.itemHover : {},
|
|
1070
1126
|
borderBottom: "1px solid #e5e5e5",
|
|
1071
|
-
marginBottom: "4px"
|
|
1127
|
+
marginBottom: "4px",
|
|
1128
|
+
// Ensure minimum touch target size
|
|
1129
|
+
minHeight: "44px",
|
|
1130
|
+
// Prevent text selection on touch
|
|
1131
|
+
WebkitUserSelect: "none",
|
|
1132
|
+
userSelect: "none",
|
|
1133
|
+
WebkitTouchCallout: "none"
|
|
1072
1134
|
},
|
|
1073
1135
|
children: [
|
|
1074
1136
|
/* @__PURE__ */ jsx2(ChevronLeft, { className: "w-4 h-4", style: { opacity: 0.5 } }),
|
|
@@ -1138,6 +1200,25 @@ function CommentForm({
|
|
|
1138
1200
|
] })
|
|
1139
1201
|
] });
|
|
1140
1202
|
}
|
|
1203
|
+
function calculateInViewPosition(requestedX, requestedY, menuWidth, menuHeight) {
|
|
1204
|
+
const viewportWidth = window.innerWidth;
|
|
1205
|
+
const viewportHeight = window.innerHeight;
|
|
1206
|
+
let x = requestedX;
|
|
1207
|
+
let y = requestedY;
|
|
1208
|
+
if (x + menuWidth > viewportWidth - VIEWPORT_PADDING) {
|
|
1209
|
+
x = viewportWidth - menuWidth - VIEWPORT_PADDING;
|
|
1210
|
+
}
|
|
1211
|
+
if (x < VIEWPORT_PADDING) {
|
|
1212
|
+
x = VIEWPORT_PADDING;
|
|
1213
|
+
}
|
|
1214
|
+
if (y + menuHeight > viewportHeight - VIEWPORT_PADDING) {
|
|
1215
|
+
y = viewportHeight - menuHeight - VIEWPORT_PADDING;
|
|
1216
|
+
}
|
|
1217
|
+
if (y < VIEWPORT_PADDING) {
|
|
1218
|
+
y = VIEWPORT_PADDING;
|
|
1219
|
+
}
|
|
1220
|
+
return { x, y };
|
|
1221
|
+
}
|
|
1141
1222
|
function ContextMenu({
|
|
1142
1223
|
visible,
|
|
1143
1224
|
position,
|
|
@@ -1150,7 +1231,10 @@ function ContextMenu({
|
|
|
1150
1231
|
style,
|
|
1151
1232
|
className,
|
|
1152
1233
|
highlightConfig,
|
|
1153
|
-
screenshotConfig
|
|
1234
|
+
screenshotConfig,
|
|
1235
|
+
positionMode = "inView",
|
|
1236
|
+
header,
|
|
1237
|
+
footer
|
|
1154
1238
|
}) {
|
|
1155
1239
|
const [selectedType, setSelectedType] = useState2(null);
|
|
1156
1240
|
const [currentView, setCurrentView] = useState2("menu");
|
|
@@ -1159,6 +1243,13 @@ function ContextMenu({
|
|
|
1159
1243
|
const [screenshots, setScreenshots] = useState2(null);
|
|
1160
1244
|
const [isCapturing, setIsCapturing] = useState2(false);
|
|
1161
1245
|
const menuRef = useRef(null);
|
|
1246
|
+
const [adjustedPosition, setAdjustedPosition] = useState2(position);
|
|
1247
|
+
const [isDragging, setIsDragging] = useState2(false);
|
|
1248
|
+
const [dragOffset, setDragOffset] = useState2({
|
|
1249
|
+
x: 0,
|
|
1250
|
+
y: 0
|
|
1251
|
+
});
|
|
1252
|
+
const dragStartRef = useRef(null);
|
|
1162
1253
|
const mergedScreenshotConfig = {
|
|
1163
1254
|
...DEFAULT_SCREENSHOT_CONFIG,
|
|
1164
1255
|
...screenshotConfig
|
|
@@ -1190,6 +1281,9 @@ function ContextMenu({
|
|
|
1190
1281
|
setSubmenuStack([]);
|
|
1191
1282
|
setScreenshots(null);
|
|
1192
1283
|
setIsCapturing(false);
|
|
1284
|
+
setIsDragging(false);
|
|
1285
|
+
setDragOffset({ x: 0, y: 0 });
|
|
1286
|
+
dragStartRef.current = null;
|
|
1193
1287
|
}
|
|
1194
1288
|
}, [visible]);
|
|
1195
1289
|
useEffect(() => {
|
|
@@ -1210,6 +1304,9 @@ function ContextMenu({
|
|
|
1210
1304
|
const handlePointerDown = (event) => {
|
|
1211
1305
|
if (!menuRef.current) return;
|
|
1212
1306
|
const target = event.target;
|
|
1307
|
+
if (target.closest?.("[data-drag-handle]")) {
|
|
1308
|
+
return;
|
|
1309
|
+
}
|
|
1213
1310
|
if (!menuRef.current.contains(target)) {
|
|
1214
1311
|
onClose();
|
|
1215
1312
|
}
|
|
@@ -1220,24 +1317,71 @@ function ContextMenu({
|
|
|
1220
1317
|
};
|
|
1221
1318
|
}, [visible, onClose]);
|
|
1222
1319
|
useEffect(() => {
|
|
1223
|
-
if (visible
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
if (
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1320
|
+
if (visible) {
|
|
1321
|
+
setAdjustedPosition(position);
|
|
1322
|
+
setDragOffset({ x: 0, y: 0 });
|
|
1323
|
+
}
|
|
1324
|
+
}, [visible, position.x, position.y]);
|
|
1325
|
+
useEffect(() => {
|
|
1326
|
+
if (!visible || !menuRef.current) return;
|
|
1327
|
+
const updatePosition = () => {
|
|
1328
|
+
const menuElement = menuRef.current;
|
|
1329
|
+
if (!menuElement) return;
|
|
1330
|
+
const rect = menuElement.getBoundingClientRect();
|
|
1331
|
+
const baseX = position.x + dragOffset.x;
|
|
1332
|
+
const baseY = position.y + dragOffset.y;
|
|
1333
|
+
if (positionMode === "static") {
|
|
1334
|
+
setAdjustedPosition({ x: baseX, y: baseY });
|
|
1335
|
+
} else if (positionMode === "inView" || positionMode === "dynamic") {
|
|
1336
|
+
const adjusted = calculateInViewPosition(
|
|
1337
|
+
baseX,
|
|
1338
|
+
baseY,
|
|
1339
|
+
rect.width,
|
|
1340
|
+
rect.height
|
|
1341
|
+
);
|
|
1342
|
+
setAdjustedPosition(adjusted);
|
|
1238
1343
|
}
|
|
1344
|
+
};
|
|
1345
|
+
requestAnimationFrame(updatePosition);
|
|
1346
|
+
window.addEventListener("resize", updatePosition);
|
|
1347
|
+
return () => window.removeEventListener("resize", updatePosition);
|
|
1348
|
+
}, [visible, position, positionMode, dragOffset, currentView]);
|
|
1349
|
+
useEffect(() => {
|
|
1350
|
+
if (!visible || positionMode !== "dynamic") return;
|
|
1351
|
+
const handlePointerMove = (event) => {
|
|
1352
|
+
if (!isDragging || !dragStartRef.current) return;
|
|
1353
|
+
const deltaX = event.clientX - dragStartRef.current.x;
|
|
1354
|
+
const deltaY = event.clientY - dragStartRef.current.y;
|
|
1355
|
+
setDragOffset((prev) => ({
|
|
1356
|
+
x: prev.x + deltaX,
|
|
1357
|
+
y: prev.y + deltaY
|
|
1358
|
+
}));
|
|
1359
|
+
dragStartRef.current = { x: event.clientX, y: event.clientY };
|
|
1360
|
+
};
|
|
1361
|
+
const handlePointerUp = () => {
|
|
1362
|
+
setIsDragging(false);
|
|
1363
|
+
dragStartRef.current = null;
|
|
1364
|
+
};
|
|
1365
|
+
if (isDragging) {
|
|
1366
|
+
document.addEventListener("pointermove", handlePointerMove);
|
|
1367
|
+
document.addEventListener("pointerup", handlePointerUp);
|
|
1368
|
+
document.addEventListener("pointercancel", handlePointerUp);
|
|
1369
|
+
return () => {
|
|
1370
|
+
document.removeEventListener("pointermove", handlePointerMove);
|
|
1371
|
+
document.removeEventListener("pointerup", handlePointerUp);
|
|
1372
|
+
document.removeEventListener("pointercancel", handlePointerUp);
|
|
1373
|
+
};
|
|
1239
1374
|
}
|
|
1240
|
-
}, [visible,
|
|
1375
|
+
}, [visible, positionMode, isDragging]);
|
|
1376
|
+
const handleDragStart = useCallback(
|
|
1377
|
+
(event) => {
|
|
1378
|
+
if (positionMode !== "dynamic") return;
|
|
1379
|
+
event.preventDefault();
|
|
1380
|
+
setIsDragging(true);
|
|
1381
|
+
dragStartRef.current = { x: event.clientX, y: event.clientY };
|
|
1382
|
+
},
|
|
1383
|
+
[positionMode]
|
|
1384
|
+
);
|
|
1241
1385
|
useEffect(() => {
|
|
1242
1386
|
const handleKeyDown = (e) => {
|
|
1243
1387
|
if (e.key === "Escape") {
|
|
@@ -1259,14 +1403,44 @@ function ContextMenu({
|
|
|
1259
1403
|
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
1260
1404
|
}
|
|
1261
1405
|
}, [visible, currentView, submenuStack.length, onClose]);
|
|
1406
|
+
useEffect(() => {
|
|
1407
|
+
const menuElement = menuRef.current;
|
|
1408
|
+
if (!visible || !menuElement) return;
|
|
1409
|
+
const preventTouchDefault = (e) => {
|
|
1410
|
+
const target = e.target;
|
|
1411
|
+
if (target.tagName === "TEXTAREA" || target.tagName === "INPUT" || target.isContentEditable) {
|
|
1412
|
+
return;
|
|
1413
|
+
}
|
|
1414
|
+
e.preventDefault();
|
|
1415
|
+
};
|
|
1416
|
+
menuElement.addEventListener("touchmove", preventTouchDefault, {
|
|
1417
|
+
passive: false
|
|
1418
|
+
});
|
|
1419
|
+
return () => {
|
|
1420
|
+
menuElement.removeEventListener("touchmove", preventTouchDefault);
|
|
1421
|
+
};
|
|
1422
|
+
}, [visible]);
|
|
1262
1423
|
if (!visible || !targetElement) {
|
|
1263
1424
|
return null;
|
|
1264
1425
|
}
|
|
1265
|
-
const handleItemClick = (item) => {
|
|
1426
|
+
const handleItemClick = async (item) => {
|
|
1266
1427
|
if (item.children && item.children.length > 0) {
|
|
1267
1428
|
setSubmenuStack((prev) => [...prev, item.children]);
|
|
1268
1429
|
return;
|
|
1269
1430
|
}
|
|
1431
|
+
if (item.onClick) {
|
|
1432
|
+
try {
|
|
1433
|
+
const result = await item.onClick({
|
|
1434
|
+
targetElement,
|
|
1435
|
+
containerElement,
|
|
1436
|
+
closeMenu: onClose
|
|
1437
|
+
});
|
|
1438
|
+
return result;
|
|
1439
|
+
} catch (error) {
|
|
1440
|
+
console.error("Anyclick menu onClick error:", error);
|
|
1441
|
+
return;
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1270
1444
|
if (item.showComment) {
|
|
1271
1445
|
setSelectedType(item.type);
|
|
1272
1446
|
setCurrentView("comment");
|
|
@@ -1331,15 +1505,51 @@ function ContextMenu({
|
|
|
1331
1505
|
className,
|
|
1332
1506
|
style: {
|
|
1333
1507
|
...menuStyles.container,
|
|
1334
|
-
left:
|
|
1335
|
-
top:
|
|
1508
|
+
left: adjustedPosition.x,
|
|
1509
|
+
top: adjustedPosition.y,
|
|
1336
1510
|
...containerWidth ? { width: containerWidth, minWidth: containerWidth } : {},
|
|
1511
|
+
// Touch-specific styles
|
|
1512
|
+
WebkitUserSelect: "none",
|
|
1513
|
+
userSelect: "none",
|
|
1514
|
+
WebkitTouchCallout: "none",
|
|
1515
|
+
touchAction: "none",
|
|
1516
|
+
// Prevent default touch behaviors
|
|
1517
|
+
// Cursor style for dragging
|
|
1518
|
+
...isDragging ? { cursor: "grabbing" } : {},
|
|
1337
1519
|
...style
|
|
1338
1520
|
},
|
|
1339
1521
|
role: "menu",
|
|
1340
1522
|
"aria-label": "Feedback options",
|
|
1341
1523
|
children: [
|
|
1342
|
-
currentView !== "screenshot-preview" && /* @__PURE__ */
|
|
1524
|
+
!header && currentView !== "screenshot-preview" && /* @__PURE__ */ jsxs2(DefaultHeader, { styles: menuStyles.header, title: "Send Feedback", children: [
|
|
1525
|
+
showPreview && /* @__PURE__ */ jsx2("div", { style: screenshotIndicatorStyle, children: /* @__PURE__ */ jsx2(Camera, { className: "w-3 h-3" }) }),
|
|
1526
|
+
positionMode === "dynamic" && /* @__PURE__ */ jsx2(
|
|
1527
|
+
"div",
|
|
1528
|
+
{
|
|
1529
|
+
"data-drag-handle": true,
|
|
1530
|
+
onPointerDown: handleDragStart,
|
|
1531
|
+
style: {
|
|
1532
|
+
cursor: isDragging ? "grabbing" : "grab",
|
|
1533
|
+
padding: "4px",
|
|
1534
|
+
marginRight: "-4px",
|
|
1535
|
+
borderRadius: "4px",
|
|
1536
|
+
display: "flex",
|
|
1537
|
+
alignItems: "center",
|
|
1538
|
+
opacity: 0.5,
|
|
1539
|
+
transition: "opacity 0.15s"
|
|
1540
|
+
},
|
|
1541
|
+
onMouseEnter: (e) => {
|
|
1542
|
+
e.currentTarget.style.opacity = "1";
|
|
1543
|
+
},
|
|
1544
|
+
onMouseLeave: (e) => {
|
|
1545
|
+
e.currentTarget.style.opacity = "0.5";
|
|
1546
|
+
},
|
|
1547
|
+
title: "Drag to move",
|
|
1548
|
+
children: /* @__PURE__ */ jsx2(GripVertical, { className: "w-4 h-4" })
|
|
1549
|
+
}
|
|
1550
|
+
)
|
|
1551
|
+
] }),
|
|
1552
|
+
!!header && header,
|
|
1343
1553
|
currentView === "menu" && /* @__PURE__ */ jsxs2("div", { style: menuStyles.itemList, children: [
|
|
1344
1554
|
submenuStack.length > 0 && /* @__PURE__ */ jsx2(BackButton, { onClick: handleBack }),
|
|
1345
1555
|
currentItems.map((item) => /* @__PURE__ */ jsx2(
|
|
@@ -1351,11 +1561,7 @@ function ContextMenu({
|
|
|
1351
1561
|
hasChildren: item.children && item.children.length > 0
|
|
1352
1562
|
},
|
|
1353
1563
|
item.type
|
|
1354
|
-
))
|
|
1355
|
-
showPreview && /* @__PURE__ */ jsxs2("div", { style: screenshotIndicatorStyle, children: [
|
|
1356
|
-
/* @__PURE__ */ jsx2(Camera, { className: "w-3 h-3" }),
|
|
1357
|
-
/* @__PURE__ */ jsx2("span", { children: "Screenshots will be captured" })
|
|
1358
|
-
] })
|
|
1564
|
+
))
|
|
1359
1565
|
] }),
|
|
1360
1566
|
currentView === "comment" && /* @__PURE__ */ jsx2(
|
|
1361
1567
|
CommentForm,
|
|
@@ -1380,16 +1586,6 @@ function ContextMenu({
|
|
|
1380
1586
|
}
|
|
1381
1587
|
);
|
|
1382
1588
|
}
|
|
1383
|
-
var screenshotIndicatorStyle = {
|
|
1384
|
-
display: "flex",
|
|
1385
|
-
alignItems: "center",
|
|
1386
|
-
gap: "6px",
|
|
1387
|
-
padding: "8px 12px",
|
|
1388
|
-
fontSize: "11px",
|
|
1389
|
-
color: "var(--anyclick-menu-text-muted, #9ca3af)",
|
|
1390
|
-
borderTop: "1px solid var(--anyclick-menu-border, #f3f4f6)",
|
|
1391
|
-
marginTop: "4px"
|
|
1392
|
-
};
|
|
1393
1589
|
|
|
1394
1590
|
// src/store.ts
|
|
1395
1591
|
import { create } from "zustand";
|
|
@@ -1535,17 +1731,45 @@ var useProviderStore = create((set, get) => ({
|
|
|
1535
1731
|
}
|
|
1536
1732
|
}
|
|
1537
1733
|
return false;
|
|
1734
|
+
},
|
|
1735
|
+
isElementInAnyScopedProvider: (element) => {
|
|
1736
|
+
const { providers } = get();
|
|
1737
|
+
for (const provider of providers.values()) {
|
|
1738
|
+
if (!provider.scoped) continue;
|
|
1739
|
+
const container = provider.containerRef.current;
|
|
1740
|
+
if (!container) continue;
|
|
1741
|
+
if (container.contains(element)) {
|
|
1742
|
+
if (process.env.NODE_ENV === "development") {
|
|
1743
|
+
console.log(
|
|
1744
|
+
`[Store:isElementInAnyScopedProvider] Element is in scoped provider: ${provider.id}`,
|
|
1745
|
+
{
|
|
1746
|
+
elementTag: element.tagName,
|
|
1747
|
+
providerId: provider.id,
|
|
1748
|
+
providerDisabled: provider.disabled
|
|
1749
|
+
}
|
|
1750
|
+
);
|
|
1751
|
+
}
|
|
1752
|
+
return true;
|
|
1753
|
+
}
|
|
1754
|
+
}
|
|
1755
|
+
return false;
|
|
1538
1756
|
}
|
|
1539
1757
|
}));
|
|
1540
1758
|
function dispatchContextMenuEvent(event, element) {
|
|
1541
1759
|
const store = useProviderStore.getState();
|
|
1542
1760
|
const providers = store.findProvidersForElement(element);
|
|
1761
|
+
const menuEvent = {
|
|
1762
|
+
clientX: event.clientX,
|
|
1763
|
+
clientY: event.clientY,
|
|
1764
|
+
originalEvent: event,
|
|
1765
|
+
isTouch: false
|
|
1766
|
+
};
|
|
1543
1767
|
for (const provider of providers) {
|
|
1544
1768
|
if (store.isDisabledByAncestor(provider.id)) {
|
|
1545
1769
|
continue;
|
|
1546
1770
|
}
|
|
1547
1771
|
if (provider.onContextMenu) {
|
|
1548
|
-
provider.onContextMenu(
|
|
1772
|
+
provider.onContextMenu(menuEvent, element);
|
|
1549
1773
|
break;
|
|
1550
1774
|
}
|
|
1551
1775
|
}
|
|
@@ -1575,9 +1799,12 @@ function AnyclickProvider({
|
|
|
1575
1799
|
menuClassName,
|
|
1576
1800
|
disabled = false,
|
|
1577
1801
|
highlightConfig,
|
|
1802
|
+
header,
|
|
1578
1803
|
screenshotConfig,
|
|
1579
1804
|
scoped = false,
|
|
1580
|
-
theme
|
|
1805
|
+
theme,
|
|
1806
|
+
touchHoldDurationMs,
|
|
1807
|
+
touchMoveThreshold
|
|
1581
1808
|
}) {
|
|
1582
1809
|
const [isSubmitting, setIsSubmitting] = useState3(false);
|
|
1583
1810
|
const [menuVisible, setMenuVisible] = useState3(false);
|
|
@@ -1624,7 +1851,8 @@ function AnyclickProvider({
|
|
|
1624
1851
|
getMergedTheme,
|
|
1625
1852
|
isDisabledByAncestor,
|
|
1626
1853
|
findParentProvider,
|
|
1627
|
-
isElementInDisabledScope
|
|
1854
|
+
isElementInDisabledScope,
|
|
1855
|
+
isElementInAnyScopedProvider
|
|
1628
1856
|
} = useProviderStore();
|
|
1629
1857
|
const parentId = parentContext?.providerId ?? null;
|
|
1630
1858
|
const depth = parentContext ? parentContext.scoped ? 1 : 0 : 0;
|
|
@@ -1670,6 +1898,17 @@ function AnyclickProvider({
|
|
|
1670
1898
|
}
|
|
1671
1899
|
return false;
|
|
1672
1900
|
}
|
|
1901
|
+
if (!scoped && event.isTouch && isElementInAnyScopedProvider(element)) {
|
|
1902
|
+
if (process.env.NODE_ENV === "development") {
|
|
1903
|
+
console.log(
|
|
1904
|
+
`[AnyclickProvider:${providerId}] Deferring to scoped provider for touch event`,
|
|
1905
|
+
{
|
|
1906
|
+
targetTag: element.tagName
|
|
1907
|
+
}
|
|
1908
|
+
);
|
|
1909
|
+
}
|
|
1910
|
+
return false;
|
|
1911
|
+
}
|
|
1673
1912
|
const mergedTheme2 = getMergedTheme(providerId);
|
|
1674
1913
|
if (process.env.NODE_ENV === "development") {
|
|
1675
1914
|
console.log(
|
|
@@ -1678,7 +1917,8 @@ function AnyclickProvider({
|
|
|
1678
1917
|
scoped,
|
|
1679
1918
|
targetTag: element.tagName,
|
|
1680
1919
|
mergedThemeColors: mergedTheme2.highlightConfig?.colors,
|
|
1681
|
-
position: { x: event.clientX, y: event.clientY }
|
|
1920
|
+
position: { x: event.clientX, y: event.clientY },
|
|
1921
|
+
isTouch: event.isTouch
|
|
1682
1922
|
}
|
|
1683
1923
|
);
|
|
1684
1924
|
}
|
|
@@ -1697,7 +1937,8 @@ function AnyclickProvider({
|
|
|
1697
1937
|
getMergedTheme,
|
|
1698
1938
|
highlightConfig,
|
|
1699
1939
|
scoped,
|
|
1700
|
-
isElementInDisabledScope
|
|
1940
|
+
isElementInDisabledScope,
|
|
1941
|
+
isElementInAnyScopedProvider
|
|
1701
1942
|
]
|
|
1702
1943
|
);
|
|
1703
1944
|
useLayoutEffect(() => {
|
|
@@ -1769,7 +2010,7 @@ function AnyclickProvider({
|
|
|
1769
2010
|
localThemeColors: localTheme.highlightConfig?.colors
|
|
1770
2011
|
});
|
|
1771
2012
|
}
|
|
1772
|
-
const client =
|
|
2013
|
+
const client = createAnyclickClient({
|
|
1773
2014
|
adapter,
|
|
1774
2015
|
targetFilter,
|
|
1775
2016
|
maxInnerTextLength,
|
|
@@ -1778,7 +2019,9 @@ function AnyclickProvider({
|
|
|
1778
2019
|
cooldownMs,
|
|
1779
2020
|
stripAttributes,
|
|
1780
2021
|
// For scoped providers, pass the container
|
|
1781
|
-
container: scoped ? containerRef.current : null
|
|
2022
|
+
container: scoped ? containerRef.current : null,
|
|
2023
|
+
touchHoldDurationMs,
|
|
2024
|
+
touchMoveThreshold
|
|
1782
2025
|
});
|
|
1783
2026
|
client.onSubmitSuccess = onSubmitSuccess;
|
|
1784
2027
|
client.onSubmitError = onSubmitError;
|
|
@@ -1805,15 +2048,17 @@ function AnyclickProvider({
|
|
|
1805
2048
|
containerReady,
|
|
1806
2049
|
providerId,
|
|
1807
2050
|
isDisabledByAncestor,
|
|
1808
|
-
handleContextMenu
|
|
2051
|
+
handleContextMenu,
|
|
2052
|
+
touchHoldDurationMs,
|
|
2053
|
+
touchMoveThreshold
|
|
1809
2054
|
]);
|
|
1810
|
-
const
|
|
2055
|
+
const submitAnyclick = useCallback2(
|
|
1811
2056
|
async (element, type, comment, screenshots) => {
|
|
1812
2057
|
const client = clientRef.current;
|
|
1813
2058
|
if (!client) return;
|
|
1814
2059
|
setIsSubmitting(true);
|
|
1815
2060
|
try {
|
|
1816
|
-
await client.
|
|
2061
|
+
await client.submitAnyclick(element, type, {
|
|
1817
2062
|
comment,
|
|
1818
2063
|
metadata,
|
|
1819
2064
|
screenshots
|
|
@@ -1849,10 +2094,10 @@ function AnyclickProvider({
|
|
|
1849
2094
|
const handleMenuSelect = useCallback2(
|
|
1850
2095
|
(type, comment, screenshots) => {
|
|
1851
2096
|
if (targetElement) {
|
|
1852
|
-
|
|
2097
|
+
submitAnyclick(targetElement, type, comment, screenshots);
|
|
1853
2098
|
}
|
|
1854
2099
|
},
|
|
1855
|
-
[targetElement,
|
|
2100
|
+
[targetElement, submitAnyclick]
|
|
1856
2101
|
);
|
|
1857
2102
|
const inheritedTheme = getMergedTheme(providerId);
|
|
1858
2103
|
const mergedTheme = useMemo(
|
|
@@ -1895,7 +2140,7 @@ function AnyclickProvider({
|
|
|
1895
2140
|
() => ({
|
|
1896
2141
|
isEnabled: !effectiveDisabled && !isDisabledByAncestor(providerId),
|
|
1897
2142
|
isSubmitting,
|
|
1898
|
-
|
|
2143
|
+
submitAnyclick,
|
|
1899
2144
|
openMenu,
|
|
1900
2145
|
closeMenu,
|
|
1901
2146
|
theme: mergedTheme,
|
|
@@ -1907,7 +2152,7 @@ function AnyclickProvider({
|
|
|
1907
2152
|
providerId,
|
|
1908
2153
|
isDisabledByAncestor,
|
|
1909
2154
|
isSubmitting,
|
|
1910
|
-
|
|
2155
|
+
submitAnyclick,
|
|
1911
2156
|
openMenu,
|
|
1912
2157
|
closeMenu,
|
|
1913
2158
|
mergedTheme,
|
|
@@ -1931,13 +2176,109 @@ function AnyclickProvider({
|
|
|
1931
2176
|
style: effectiveMenuStyle,
|
|
1932
2177
|
className: effectiveMenuClassName,
|
|
1933
2178
|
highlightConfig: effectiveHighlightConfig,
|
|
1934
|
-
screenshotConfig: effectiveScreenshotConfig
|
|
2179
|
+
screenshotConfig: effectiveScreenshotConfig,
|
|
2180
|
+
header
|
|
1935
2181
|
}
|
|
1936
2182
|
)
|
|
1937
2183
|
] });
|
|
1938
2184
|
}
|
|
1939
2185
|
var FeedbackProvider = AnyclickProvider;
|
|
1940
2186
|
|
|
2187
|
+
// src/FunModeBridge.tsx
|
|
2188
|
+
import { useEffect as useEffect3, useMemo as useMemo2, useRef as useRef3 } from "react";
|
|
2189
|
+
import {
|
|
2190
|
+
usePointer
|
|
2191
|
+
} from "@ewjdev/anyclick-pointer";
|
|
2192
|
+
function isFunModeEnabled(theme) {
|
|
2193
|
+
if (!theme) return false;
|
|
2194
|
+
if (theme.funMode === void 0) return false;
|
|
2195
|
+
if (typeof theme.funMode === "boolean") return theme.funMode;
|
|
2196
|
+
return theme.funMode.enabled ?? true;
|
|
2197
|
+
}
|
|
2198
|
+
function buildFunConfig(theme, container) {
|
|
2199
|
+
const funTheme = typeof theme?.funMode === "object" ? theme.funMode : {};
|
|
2200
|
+
const resolveTrackElement = () => {
|
|
2201
|
+
let el = container;
|
|
2202
|
+
while (el) {
|
|
2203
|
+
const rect = el.getBoundingClientRect();
|
|
2204
|
+
if (rect.width > 0 && rect.height > 0) {
|
|
2205
|
+
return el;
|
|
2206
|
+
}
|
|
2207
|
+
el = el.parentElement;
|
|
2208
|
+
}
|
|
2209
|
+
return document.body;
|
|
2210
|
+
};
|
|
2211
|
+
const getTrackElement = () => resolveTrackElement();
|
|
2212
|
+
return {
|
|
2213
|
+
maxSpeed: funTheme.maxSpeed,
|
|
2214
|
+
acceleration: funTheme.acceleration,
|
|
2215
|
+
getTrackElement,
|
|
2216
|
+
getObstacles: () => {
|
|
2217
|
+
const trackElement = getTrackElement();
|
|
2218
|
+
const obstacles = [];
|
|
2219
|
+
const parent = trackElement.parentElement;
|
|
2220
|
+
if (parent) {
|
|
2221
|
+
Array.from(parent.children).forEach((sibling) => {
|
|
2222
|
+
if (sibling !== trackElement) {
|
|
2223
|
+
obstacles.push(sibling.getBoundingClientRect());
|
|
2224
|
+
}
|
|
2225
|
+
});
|
|
2226
|
+
}
|
|
2227
|
+
Array.from(trackElement.children).forEach((child) => {
|
|
2228
|
+
obstacles.push(child.getBoundingClientRect());
|
|
2229
|
+
});
|
|
2230
|
+
return obstacles;
|
|
2231
|
+
}
|
|
2232
|
+
};
|
|
2233
|
+
}
|
|
2234
|
+
function FunModeBridge() {
|
|
2235
|
+
const { setConfig } = usePointer();
|
|
2236
|
+
const providerStore = useProviderStore();
|
|
2237
|
+
const activeProviderRef = useRef3(null);
|
|
2238
|
+
const cachedConfigs = useRef3({});
|
|
2239
|
+
const findActiveFunProvider = useMemo2(() => {
|
|
2240
|
+
return (el) => {
|
|
2241
|
+
if (!el) return null;
|
|
2242
|
+
const providers = providerStore.findProvidersForElement(el);
|
|
2243
|
+
for (const provider of providers) {
|
|
2244
|
+
if (provider.scoped && !provider.disabled && isFunModeEnabled(provider.theme)) {
|
|
2245
|
+
return provider;
|
|
2246
|
+
}
|
|
2247
|
+
}
|
|
2248
|
+
return null;
|
|
2249
|
+
};
|
|
2250
|
+
}, [providerStore]);
|
|
2251
|
+
useEffect3(() => {
|
|
2252
|
+
const handleMove = (event) => {
|
|
2253
|
+
const el = document.elementFromPoint(event.clientX, event.clientY);
|
|
2254
|
+
const provider = findActiveFunProvider(el);
|
|
2255
|
+
if (!provider || !provider.containerRef.current) {
|
|
2256
|
+
if (activeProviderRef.current !== null) {
|
|
2257
|
+
activeProviderRef.current = null;
|
|
2258
|
+
setConfig({ mode: "normal" });
|
|
2259
|
+
}
|
|
2260
|
+
return;
|
|
2261
|
+
}
|
|
2262
|
+
if (activeProviderRef.current === provider.id) {
|
|
2263
|
+
return;
|
|
2264
|
+
}
|
|
2265
|
+
activeProviderRef.current = provider.id;
|
|
2266
|
+
if (!cachedConfigs.current[provider.id]) {
|
|
2267
|
+
cachedConfigs.current[provider.id] = {
|
|
2268
|
+
mode: "fun",
|
|
2269
|
+
funConfig: buildFunConfig(provider.theme, provider.containerRef.current)
|
|
2270
|
+
};
|
|
2271
|
+
}
|
|
2272
|
+
setConfig(cachedConfigs.current[provider.id]);
|
|
2273
|
+
};
|
|
2274
|
+
window.addEventListener("pointermove", handleMove, { passive: true });
|
|
2275
|
+
return () => {
|
|
2276
|
+
window.removeEventListener("pointermove", handleMove);
|
|
2277
|
+
};
|
|
2278
|
+
}, [findActiveFunProvider, setConfig]);
|
|
2279
|
+
return null;
|
|
2280
|
+
}
|
|
2281
|
+
|
|
1941
2282
|
// src/types.ts
|
|
1942
2283
|
function filterMenuItemsByRole(items, userContext) {
|
|
1943
2284
|
if (!userContext) {
|
|
@@ -1972,6 +2313,7 @@ export {
|
|
|
1972
2313
|
DEFAULT_SENSITIVE_SELECTORS,
|
|
1973
2314
|
FeedbackContext,
|
|
1974
2315
|
FeedbackProvider,
|
|
2316
|
+
FunModeBridge,
|
|
1975
2317
|
ScreenshotPreview,
|
|
1976
2318
|
applyHighlights,
|
|
1977
2319
|
captureAllScreenshots2 as captureAllScreenshots,
|
|
@@ -2008,6 +2350,7 @@ lucide-react/dist/esm/icons/chevron-right.js:
|
|
|
2008
2350
|
lucide-react/dist/esm/icons/circle-alert.js:
|
|
2009
2351
|
lucide-react/dist/esm/icons/expand.js:
|
|
2010
2352
|
lucide-react/dist/esm/icons/flag.js:
|
|
2353
|
+
lucide-react/dist/esm/icons/grip-vertical.js:
|
|
2011
2354
|
lucide-react/dist/esm/icons/image.js:
|
|
2012
2355
|
lucide-react/dist/esm/icons/loader-circle.js:
|
|
2013
2356
|
lucide-react/dist/esm/icons/plus.js:
|