@ewjdev/anyclick-react 1.1.1 → 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/dist/index.js CHANGED
@@ -28,6 +28,7 @@ __export(index_exports, {
28
28
  DEFAULT_SENSITIVE_SELECTORS: () => import_anyclick_core4.DEFAULT_SENSITIVE_SELECTORS,
29
29
  FeedbackContext: () => FeedbackContext,
30
30
  FeedbackProvider: () => FeedbackProvider,
31
+ FunModeBridge: () => FunModeBridge,
31
32
  ScreenshotPreview: () => ScreenshotPreview,
32
33
  applyHighlights: () => applyHighlights,
33
34
  captureAllScreenshots: () => import_anyclick_core4.captureAllScreenshots,
@@ -120,16 +121,19 @@ var menuStyles = {
120
121
  ...menuCSSVariables
121
122
  },
122
123
  header: {
123
- padding: "12px 16px",
124
+ padding: "8px 12px",
124
125
  borderBottom: "1px solid var(--anyclick-menu-border, #e5e5e5)",
125
126
  color: "var(--anyclick-menu-text-muted, #666)",
126
127
  fontSize: "12px",
127
128
  fontWeight: 500,
128
129
  textTransform: "uppercase",
129
- letterSpacing: "0.5px"
130
+ letterSpacing: "0.5px",
131
+ display: "flex",
132
+ justifyContent: "space-between",
133
+ alignItems: "center"
130
134
  },
131
135
  itemList: {
132
- padding: "4px 0"
136
+ padding: "0px 0px"
133
137
  },
134
138
  item: {
135
139
  display: "flex",
@@ -531,6 +535,7 @@ function generateHighlightCSS(colors) {
531
535
  border-radius: 4px !important;
532
536
  position: relative;
533
537
  z-index: 9997;
538
+ top: 0;
534
539
  }
535
540
 
536
541
  .${HIGHLIGHT_CONTAINER_CLASS} {
@@ -1072,19 +1077,27 @@ var import_jsx_runtime2 = require("react/jsx-runtime");
1072
1077
  var VIEWPORT_PADDING = 10;
1073
1078
  var screenshotIndicatorStyle = {
1074
1079
  display: "flex",
1075
- alignItems: "center",
1076
- gap: "6px",
1077
- padding: "8px 12px",
1078
- fontSize: "11px",
1079
- color: "var(--anyclick-menu-text-muted, #9ca3af)",
1080
- borderTop: "1px solid var(--anyclick-menu-border, #f3f4f6)",
1081
- marginTop: "4px"
1080
+ marginLeft: "4px",
1081
+ opacity: 0.7,
1082
+ justifyContent: "flex-end",
1083
+ flex: 1
1082
1084
  };
1083
1085
  var defaultIcons = {
1084
1086
  issue: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Flag, { className: "w-4 h-4" }),
1085
1087
  feature: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Plus, { className: "w-4 h-4" }),
1086
1088
  like: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ThumbsUp, { className: "w-4 h-4" })
1087
1089
  };
1090
+ var DefaultHeader = ({
1091
+ styles,
1092
+ className,
1093
+ title = "Send Feedback",
1094
+ children
1095
+ }) => {
1096
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: styles, className, children: [
1097
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: title }),
1098
+ children
1099
+ ] });
1100
+ };
1088
1101
  function MenuItem({
1089
1102
  item,
1090
1103
  onClick,
@@ -1262,7 +1275,9 @@ function ContextMenu({
1262
1275
  className,
1263
1276
  highlightConfig,
1264
1277
  screenshotConfig,
1265
- positionMode = "inView"
1278
+ positionMode = "inView",
1279
+ header,
1280
+ footer
1266
1281
  }) {
1267
1282
  const [selectedType, setSelectedType] = (0, import_react5.useState)(null);
1268
1283
  const [currentView, setCurrentView] = (0, import_react5.useState)("menu");
@@ -1451,11 +1466,24 @@ function ContextMenu({
1451
1466
  if (!visible || !targetElement) {
1452
1467
  return null;
1453
1468
  }
1454
- const handleItemClick = (item) => {
1469
+ const handleItemClick = async (item) => {
1455
1470
  if (item.children && item.children.length > 0) {
1456
1471
  setSubmenuStack((prev) => [...prev, item.children]);
1457
1472
  return;
1458
1473
  }
1474
+ if (item.onClick) {
1475
+ try {
1476
+ const result = await item.onClick({
1477
+ targetElement,
1478
+ containerElement,
1479
+ closeMenu: onClose
1480
+ });
1481
+ return result;
1482
+ } catch (error) {
1483
+ console.error("Anyclick menu onClick error:", error);
1484
+ return;
1485
+ }
1486
+ }
1459
1487
  if (item.showComment) {
1460
1488
  setSelectedType(item.type);
1461
1489
  setCurrentView("comment");
@@ -1536,45 +1564,35 @@ function ContextMenu({
1536
1564
  role: "menu",
1537
1565
  "aria-label": "Feedback options",
1538
1566
  children: [
1539
- currentView !== "screenshot-preview" && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1540
- "div",
1541
- {
1542
- style: {
1543
- ...menuStyles.header,
1544
- display: "flex",
1545
- alignItems: "center",
1546
- justifyContent: "space-between"
1547
- },
1548
- children: [
1549
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: "Send Feedback" }),
1550
- positionMode === "dynamic" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1551
- "div",
1552
- {
1553
- "data-drag-handle": true,
1554
- onPointerDown: handleDragStart,
1555
- style: {
1556
- cursor: isDragging ? "grabbing" : "grab",
1557
- padding: "4px",
1558
- marginRight: "-4px",
1559
- borderRadius: "4px",
1560
- display: "flex",
1561
- alignItems: "center",
1562
- opacity: 0.5,
1563
- transition: "opacity 0.15s"
1564
- },
1565
- onMouseEnter: (e) => {
1566
- e.currentTarget.style.opacity = "1";
1567
- },
1568
- onMouseLeave: (e) => {
1569
- e.currentTarget.style.opacity = "0.5";
1570
- },
1571
- title: "Drag to move",
1572
- children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(GripVertical, { className: "w-4 h-4" })
1573
- }
1574
- )
1575
- ]
1576
- }
1577
- ),
1567
+ !header && currentView !== "screenshot-preview" && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(DefaultHeader, { styles: menuStyles.header, title: "Send Feedback", children: [
1568
+ showPreview && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: screenshotIndicatorStyle, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Camera, { className: "w-3 h-3" }) }),
1569
+ positionMode === "dynamic" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1570
+ "div",
1571
+ {
1572
+ "data-drag-handle": true,
1573
+ onPointerDown: handleDragStart,
1574
+ style: {
1575
+ cursor: isDragging ? "grabbing" : "grab",
1576
+ padding: "4px",
1577
+ marginRight: "-4px",
1578
+ borderRadius: "4px",
1579
+ display: "flex",
1580
+ alignItems: "center",
1581
+ opacity: 0.5,
1582
+ transition: "opacity 0.15s"
1583
+ },
1584
+ onMouseEnter: (e) => {
1585
+ e.currentTarget.style.opacity = "1";
1586
+ },
1587
+ onMouseLeave: (e) => {
1588
+ e.currentTarget.style.opacity = "0.5";
1589
+ },
1590
+ title: "Drag to move",
1591
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(GripVertical, { className: "w-4 h-4" })
1592
+ }
1593
+ )
1594
+ ] }),
1595
+ !!header && header,
1578
1596
  currentView === "menu" && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: menuStyles.itemList, children: [
1579
1597
  submenuStack.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(BackButton, { onClick: handleBack }),
1580
1598
  currentItems.map((item) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
@@ -1586,11 +1604,7 @@ function ContextMenu({
1586
1604
  hasChildren: item.children && item.children.length > 0
1587
1605
  },
1588
1606
  item.type
1589
- )),
1590
- showPreview && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: screenshotIndicatorStyle, children: [
1591
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Camera, { className: "w-3 h-3" }),
1592
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: "Screenshots will be captured" })
1593
- ] })
1607
+ ))
1594
1608
  ] }),
1595
1609
  currentView === "comment" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1596
1610
  CommentForm,
@@ -1828,6 +1842,7 @@ function AnyclickProvider({
1828
1842
  menuClassName,
1829
1843
  disabled = false,
1830
1844
  highlightConfig,
1845
+ header,
1831
1846
  screenshotConfig,
1832
1847
  scoped = false,
1833
1848
  theme,
@@ -2038,7 +2053,7 @@ function AnyclickProvider({
2038
2053
  localThemeColors: localTheme.highlightConfig?.colors
2039
2054
  });
2040
2055
  }
2041
- const client = (0, import_anyclick_core3.createFeedbackClient)({
2056
+ const client = (0, import_anyclick_core3.createAnyclickClient)({
2042
2057
  adapter,
2043
2058
  targetFilter,
2044
2059
  maxInnerTextLength,
@@ -2080,13 +2095,13 @@ function AnyclickProvider({
2080
2095
  touchHoldDurationMs,
2081
2096
  touchMoveThreshold
2082
2097
  ]);
2083
- const submitFeedback = (0, import_react6.useCallback)(
2098
+ const submitAnyclick = (0, import_react6.useCallback)(
2084
2099
  async (element, type, comment, screenshots) => {
2085
2100
  const client = clientRef.current;
2086
2101
  if (!client) return;
2087
2102
  setIsSubmitting(true);
2088
2103
  try {
2089
- await client.submitFeedback(element, type, {
2104
+ await client.submitAnyclick(element, type, {
2090
2105
  comment,
2091
2106
  metadata,
2092
2107
  screenshots
@@ -2122,10 +2137,10 @@ function AnyclickProvider({
2122
2137
  const handleMenuSelect = (0, import_react6.useCallback)(
2123
2138
  (type, comment, screenshots) => {
2124
2139
  if (targetElement) {
2125
- submitFeedback(targetElement, type, comment, screenshots);
2140
+ submitAnyclick(targetElement, type, comment, screenshots);
2126
2141
  }
2127
2142
  },
2128
- [targetElement, submitFeedback]
2143
+ [targetElement, submitAnyclick]
2129
2144
  );
2130
2145
  const inheritedTheme = getMergedTheme(providerId);
2131
2146
  const mergedTheme = (0, import_react6.useMemo)(
@@ -2168,7 +2183,7 @@ function AnyclickProvider({
2168
2183
  () => ({
2169
2184
  isEnabled: !effectiveDisabled && !isDisabledByAncestor(providerId),
2170
2185
  isSubmitting,
2171
- submitFeedback,
2186
+ submitAnyclick,
2172
2187
  openMenu,
2173
2188
  closeMenu,
2174
2189
  theme: mergedTheme,
@@ -2180,7 +2195,7 @@ function AnyclickProvider({
2180
2195
  providerId,
2181
2196
  isDisabledByAncestor,
2182
2197
  isSubmitting,
2183
- submitFeedback,
2198
+ submitAnyclick,
2184
2199
  openMenu,
2185
2200
  closeMenu,
2186
2201
  mergedTheme,
@@ -2204,13 +2219,107 @@ function AnyclickProvider({
2204
2219
  style: effectiveMenuStyle,
2205
2220
  className: effectiveMenuClassName,
2206
2221
  highlightConfig: effectiveHighlightConfig,
2207
- screenshotConfig: effectiveScreenshotConfig
2222
+ screenshotConfig: effectiveScreenshotConfig,
2223
+ header
2208
2224
  }
2209
2225
  )
2210
2226
  ] });
2211
2227
  }
2212
2228
  var FeedbackProvider = AnyclickProvider;
2213
2229
 
2230
+ // src/FunModeBridge.tsx
2231
+ var import_react7 = require("react");
2232
+ var import_anyclick_pointer = require("@ewjdev/anyclick-pointer");
2233
+ function isFunModeEnabled(theme) {
2234
+ if (!theme) return false;
2235
+ if (theme.funMode === void 0) return false;
2236
+ if (typeof theme.funMode === "boolean") return theme.funMode;
2237
+ return theme.funMode.enabled ?? true;
2238
+ }
2239
+ function buildFunConfig(theme, container) {
2240
+ const funTheme = typeof theme?.funMode === "object" ? theme.funMode : {};
2241
+ const resolveTrackElement = () => {
2242
+ let el = container;
2243
+ while (el) {
2244
+ const rect = el.getBoundingClientRect();
2245
+ if (rect.width > 0 && rect.height > 0) {
2246
+ return el;
2247
+ }
2248
+ el = el.parentElement;
2249
+ }
2250
+ return document.body;
2251
+ };
2252
+ const getTrackElement = () => resolveTrackElement();
2253
+ return {
2254
+ maxSpeed: funTheme.maxSpeed,
2255
+ acceleration: funTheme.acceleration,
2256
+ getTrackElement,
2257
+ getObstacles: () => {
2258
+ const trackElement = getTrackElement();
2259
+ const obstacles = [];
2260
+ const parent = trackElement.parentElement;
2261
+ if (parent) {
2262
+ Array.from(parent.children).forEach((sibling) => {
2263
+ if (sibling !== trackElement) {
2264
+ obstacles.push(sibling.getBoundingClientRect());
2265
+ }
2266
+ });
2267
+ }
2268
+ Array.from(trackElement.children).forEach((child) => {
2269
+ obstacles.push(child.getBoundingClientRect());
2270
+ });
2271
+ return obstacles;
2272
+ }
2273
+ };
2274
+ }
2275
+ function FunModeBridge() {
2276
+ const { setConfig } = (0, import_anyclick_pointer.usePointer)();
2277
+ const providerStore = useProviderStore();
2278
+ const activeProviderRef = (0, import_react7.useRef)(null);
2279
+ const cachedConfigs = (0, import_react7.useRef)({});
2280
+ const findActiveFunProvider = (0, import_react7.useMemo)(() => {
2281
+ return (el) => {
2282
+ if (!el) return null;
2283
+ const providers = providerStore.findProvidersForElement(el);
2284
+ for (const provider of providers) {
2285
+ if (provider.scoped && !provider.disabled && isFunModeEnabled(provider.theme)) {
2286
+ return provider;
2287
+ }
2288
+ }
2289
+ return null;
2290
+ };
2291
+ }, [providerStore]);
2292
+ (0, import_react7.useEffect)(() => {
2293
+ const handleMove = (event) => {
2294
+ const el = document.elementFromPoint(event.clientX, event.clientY);
2295
+ const provider = findActiveFunProvider(el);
2296
+ if (!provider || !provider.containerRef.current) {
2297
+ if (activeProviderRef.current !== null) {
2298
+ activeProviderRef.current = null;
2299
+ setConfig({ mode: "normal" });
2300
+ }
2301
+ return;
2302
+ }
2303
+ if (activeProviderRef.current === provider.id) {
2304
+ return;
2305
+ }
2306
+ activeProviderRef.current = provider.id;
2307
+ if (!cachedConfigs.current[provider.id]) {
2308
+ cachedConfigs.current[provider.id] = {
2309
+ mode: "fun",
2310
+ funConfig: buildFunConfig(provider.theme, provider.containerRef.current)
2311
+ };
2312
+ }
2313
+ setConfig(cachedConfigs.current[provider.id]);
2314
+ };
2315
+ window.addEventListener("pointermove", handleMove, { passive: true });
2316
+ return () => {
2317
+ window.removeEventListener("pointermove", handleMove);
2318
+ };
2319
+ }, [findActiveFunProvider, setConfig]);
2320
+ return null;
2321
+ }
2322
+
2214
2323
  // src/types.ts
2215
2324
  function filterMenuItemsByRole(items, userContext) {
2216
2325
  if (!userContext) {
@@ -2238,6 +2347,7 @@ var import_anyclick_core4 = require("@ewjdev/anyclick-core");
2238
2347
  DEFAULT_SENSITIVE_SELECTORS,
2239
2348
  FeedbackContext,
2240
2349
  FeedbackProvider,
2350
+ FunModeBridge,
2241
2351
  ScreenshotPreview,
2242
2352
  applyHighlights,
2243
2353
  captureAllScreenshots,