@fanvue/ui 2.15.1 → 2.16.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.
@@ -217,6 +217,7 @@ const TextField = require("./components/TextField/TextField.cjs");
217
217
  const Toast = require("./components/Toast/Toast.cjs");
218
218
  const Tooltip = require("./components/Tooltip/Tooltip.cjs");
219
219
  const cn = require("./utils/cn.cjs");
220
+ const useSuppressClickAfterDrag = require("./utils/useSuppressClickAfterDrag.cjs");
220
221
  Object.defineProperty(exports, "Slot", {
221
222
  enumerable: true,
222
223
  get: () => reactSlot.Slot
@@ -506,4 +507,5 @@ exports.TooltipContent = Tooltip.TooltipContent;
506
507
  exports.TooltipProvider = Tooltip.TooltipProvider;
507
508
  exports.TooltipTrigger = Tooltip.TooltipTrigger;
508
509
  exports.cn = cn.cn;
510
+ exports.useSuppressClickAfterDrag = useSuppressClickAfterDrag.useSuppressClickAfterDrag;
509
511
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -22,10 +22,11 @@ const React__namespace = /* @__PURE__ */ _interopNamespaceDefault(React);
22
22
  const TAP_MOVEMENT_THRESHOLD_PX = 10;
23
23
  function useSuppressClickAfterDrag(props) {
24
24
  const tapRef = React__namespace.useRef(null);
25
+ const consumer = props ?? {};
25
26
  return {
26
- ...props,
27
+ ...consumer,
27
28
  onPointerDown(event) {
28
- props.onPointerDown?.(event);
29
+ consumer.onPointerDown?.(event);
29
30
  if (event.pointerType === "mouse") {
30
31
  tapRef.current = null;
31
32
  return;
@@ -39,7 +40,7 @@ function useSuppressClickAfterDrag(props) {
39
40
  };
40
41
  },
41
42
  onPointerMove(event) {
42
- props.onPointerMove?.(event);
43
+ consumer.onPointerMove?.(event);
43
44
  const tap = tapRef.current;
44
45
  if (tap === null || event.pointerId !== tap.pointerId || tap.movedPastThreshold) return;
45
46
  const dx = event.clientX - tap.x;
@@ -49,7 +50,7 @@ function useSuppressClickAfterDrag(props) {
49
50
  }
50
51
  },
51
52
  onPointerCancel(event) {
52
- props.onPointerCancel?.(event);
53
+ consumer.onPointerCancel?.(event);
53
54
  const tap = tapRef.current;
54
55
  if (tap !== null && event.pointerId === tap.pointerId) {
55
56
  tapRef.current = null;
@@ -63,7 +64,7 @@ function useSuppressClickAfterDrag(props) {
63
64
  event.stopPropagation();
64
65
  return;
65
66
  }
66
- props.onClick?.(event);
67
+ consumer.onClick?.(event);
67
68
  }
68
69
  };
69
70
  }
@@ -1 +1 @@
1
- {"version":3,"file":"useSuppressClickAfterDrag.cjs","sources":["../../../src/utils/useSuppressClickAfterDrag.ts"],"sourcesContent":["import * as React from \"react\";\n\nconst TAP_MOVEMENT_THRESHOLD_PX = 10;\n\ntype ActiveTap = {\n pointerId: number;\n x: number;\n y: number;\n movedPastThreshold: boolean;\n};\n\ntype GatedHandlers = {\n onPointerDown?: React.PointerEventHandler;\n onPointerMove?: React.PointerEventHandler;\n onPointerCancel?: React.PointerEventHandler;\n onClick?: React.MouseEventHandler;\n};\n\n/**\n * Composes the consumer's pointer/click handlers with a touch-drag tracker\n * that suppresses the synthetic click an Android Chrome scroll-drag-end can\n * dispatch on a click-based Radix trigger (Popover / Dialog / Drawer).\n *\n * On touch / pen input, if the press-and-release crosses a movement threshold,\n * the subsequent `click` is preventDefault'd — short-circuiting Radix's own\n * `onOpenToggle` via `composeEventHandlers` — and stopPropagation'd so it\n * doesn't bubble to ancestors. Mouse input passes through unchanged because\n * mouse never triggers the Android scroll-drag bug class.\n *\n * Related: radix-ui/primitives#1912 (DropdownMenu pointerdown variant) and\n * #2702 (DismissableLayer touch dismiss). For the DropdownMenu pointerdown\n * variant see `components/DropdownMenu/DropdownMenu.tsx`.\n */\nexport function useSuppressClickAfterDrag<P extends GatedHandlers>(props: P): P {\n const tapRef = React.useRef<ActiveTap | null>(null);\n\n return {\n ...props,\n onPointerDown(event) {\n props.onPointerDown?.(event);\n if (event.pointerType === \"mouse\") {\n tapRef.current = null;\n return;\n }\n // Keep pointermove on this element if the finger drifts off.\n // Optional because jsdom (used in tests) doesn't implement it.\n event.currentTarget.setPointerCapture?.(event.pointerId);\n tapRef.current = {\n pointerId: event.pointerId,\n x: event.clientX,\n y: event.clientY,\n movedPastThreshold: false,\n };\n },\n onPointerMove(event) {\n props.onPointerMove?.(event);\n const tap = tapRef.current;\n if (tap === null || event.pointerId !== tap.pointerId || tap.movedPastThreshold) return;\n const dx = event.clientX - tap.x;\n const dy = event.clientY - tap.y;\n if (Math.hypot(dx, dy) > TAP_MOVEMENT_THRESHOLD_PX) {\n tap.movedPastThreshold = true;\n }\n },\n onPointerCancel(event) {\n props.onPointerCancel?.(event);\n const tap = tapRef.current;\n if (tap !== null && event.pointerId === tap.pointerId) {\n tapRef.current = null;\n }\n },\n onClick(event) {\n const tap = tapRef.current;\n tapRef.current = null;\n if (tap?.movedPastThreshold) {\n // preventDefault stops Radix's onClick → onOpenToggle via\n // composeEventHandlers. stopPropagation prevents the synthetic click\n // from bubbling to ancestor click handlers.\n event.preventDefault();\n event.stopPropagation();\n return;\n }\n props.onClick?.(event);\n },\n };\n}\n"],"names":["React"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAEA,MAAM,4BAA4B;AA+B3B,SAAS,0BAAmD,OAAa;AAC9E,QAAM,SAASA,iBAAM,OAAyB,IAAI;AAElD,SAAO;AAAA,IACL,GAAG;AAAA,IACH,cAAc,OAAO;AACnB,YAAM,gBAAgB,KAAK;AAC3B,UAAI,MAAM,gBAAgB,SAAS;AACjC,eAAO,UAAU;AACjB;AAAA,MACF;AAGA,YAAM,cAAc,oBAAoB,MAAM,SAAS;AACvD,aAAO,UAAU;AAAA,QACf,WAAW,MAAM;AAAA,QACjB,GAAG,MAAM;AAAA,QACT,GAAG,MAAM;AAAA,QACT,oBAAoB;AAAA,MAAA;AAAA,IAExB;AAAA,IACA,cAAc,OAAO;AACnB,YAAM,gBAAgB,KAAK;AAC3B,YAAM,MAAM,OAAO;AACnB,UAAI,QAAQ,QAAQ,MAAM,cAAc,IAAI,aAAa,IAAI,mBAAoB;AACjF,YAAM,KAAK,MAAM,UAAU,IAAI;AAC/B,YAAM,KAAK,MAAM,UAAU,IAAI;AAC/B,UAAI,KAAK,MAAM,IAAI,EAAE,IAAI,2BAA2B;AAClD,YAAI,qBAAqB;AAAA,MAC3B;AAAA,IACF;AAAA,IACA,gBAAgB,OAAO;AACrB,YAAM,kBAAkB,KAAK;AAC7B,YAAM,MAAM,OAAO;AACnB,UAAI,QAAQ,QAAQ,MAAM,cAAc,IAAI,WAAW;AACrD,eAAO,UAAU;AAAA,MACnB;AAAA,IACF;AAAA,IACA,QAAQ,OAAO;AACb,YAAM,MAAM,OAAO;AACnB,aAAO,UAAU;AACjB,UAAI,KAAK,oBAAoB;AAI3B,cAAM,eAAA;AACN,cAAM,gBAAA;AACN;AAAA,MACF;AACA,YAAM,UAAU,KAAK;AAAA,IACvB;AAAA,EAAA;AAEJ;;"}
1
+ {"version":3,"file":"useSuppressClickAfterDrag.cjs","sources":["../../../src/utils/useSuppressClickAfterDrag.ts"],"sourcesContent":["import * as React from \"react\";\n\nconst TAP_MOVEMENT_THRESHOLD_PX = 10;\n\ntype ActiveTap = {\n pointerId: number;\n x: number;\n y: number;\n movedPastThreshold: boolean;\n};\n\ntype GatedHandlers = {\n onPointerDown?: React.PointerEventHandler;\n onPointerMove?: React.PointerEventHandler;\n onPointerCancel?: React.PointerEventHandler;\n onClick?: React.MouseEventHandler;\n};\n\n/**\n * Composes the consumer's pointer/click handlers with a touch-drag tracker\n * that suppresses the synthetic click an Android Chrome scroll-drag-end can\n * dispatch on a click-based Radix trigger (Popover / Dialog / Drawer).\n *\n * On touch / pen input, if the press-and-release crosses a movement threshold,\n * the subsequent `click` is preventDefault'd — short-circuiting Radix's own\n * `onOpenToggle` via `composeEventHandlers` — and stopPropagation'd so it\n * doesn't bubble to ancestors. Mouse input passes through unchanged because\n * mouse never triggers the Android scroll-drag bug class.\n *\n * Related: radix-ui/primitives#1912 (DropdownMenu pointerdown variant) and\n * #2702 (DismissableLayer touch dismiss). For the DropdownMenu pointerdown\n * variant see `components/DropdownMenu/DropdownMenu.tsx`.\n */\nexport function useSuppressClickAfterDrag<P extends GatedHandlers = GatedHandlers>(\n props?: P,\n): P & Required<GatedHandlers> {\n const tapRef = React.useRef<ActiveTap | null>(null);\n const consumer = props ?? ({} as P);\n\n return {\n ...consumer,\n onPointerDown(event) {\n consumer.onPointerDown?.(event);\n if (event.pointerType === \"mouse\") {\n tapRef.current = null;\n return;\n }\n // Keep pointermove on this element if the finger drifts off.\n // Optional because jsdom (used in tests) doesn't implement it.\n event.currentTarget.setPointerCapture?.(event.pointerId);\n tapRef.current = {\n pointerId: event.pointerId,\n x: event.clientX,\n y: event.clientY,\n movedPastThreshold: false,\n };\n },\n onPointerMove(event) {\n consumer.onPointerMove?.(event);\n const tap = tapRef.current;\n if (tap === null || event.pointerId !== tap.pointerId || tap.movedPastThreshold) return;\n const dx = event.clientX - tap.x;\n const dy = event.clientY - tap.y;\n if (Math.hypot(dx, dy) > TAP_MOVEMENT_THRESHOLD_PX) {\n tap.movedPastThreshold = true;\n }\n },\n onPointerCancel(event) {\n consumer.onPointerCancel?.(event);\n const tap = tapRef.current;\n if (tap !== null && event.pointerId === tap.pointerId) {\n tapRef.current = null;\n }\n },\n onClick(event) {\n const tap = tapRef.current;\n tapRef.current = null;\n if (tap?.movedPastThreshold) {\n // preventDefault stops Radix's onClick → onOpenToggle via\n // composeEventHandlers. stopPropagation prevents the synthetic click\n // from bubbling to ancestor click handlers.\n event.preventDefault();\n event.stopPropagation();\n return;\n }\n consumer.onClick?.(event);\n },\n };\n}\n"],"names":["React"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAEA,MAAM,4BAA4B;AA+B3B,SAAS,0BACd,OAC6B;AAC7B,QAAM,SAASA,iBAAM,OAAyB,IAAI;AAClD,QAAM,WAAW,SAAU,CAAA;AAE3B,SAAO;AAAA,IACL,GAAG;AAAA,IACH,cAAc,OAAO;AACnB,eAAS,gBAAgB,KAAK;AAC9B,UAAI,MAAM,gBAAgB,SAAS;AACjC,eAAO,UAAU;AACjB;AAAA,MACF;AAGA,YAAM,cAAc,oBAAoB,MAAM,SAAS;AACvD,aAAO,UAAU;AAAA,QACf,WAAW,MAAM;AAAA,QACjB,GAAG,MAAM;AAAA,QACT,GAAG,MAAM;AAAA,QACT,oBAAoB;AAAA,MAAA;AAAA,IAExB;AAAA,IACA,cAAc,OAAO;AACnB,eAAS,gBAAgB,KAAK;AAC9B,YAAM,MAAM,OAAO;AACnB,UAAI,QAAQ,QAAQ,MAAM,cAAc,IAAI,aAAa,IAAI,mBAAoB;AACjF,YAAM,KAAK,MAAM,UAAU,IAAI;AAC/B,YAAM,KAAK,MAAM,UAAU,IAAI;AAC/B,UAAI,KAAK,MAAM,IAAI,EAAE,IAAI,2BAA2B;AAClD,YAAI,qBAAqB;AAAA,MAC3B;AAAA,IACF;AAAA,IACA,gBAAgB,OAAO;AACrB,eAAS,kBAAkB,KAAK;AAChC,YAAM,MAAM,OAAO;AACnB,UAAI,QAAQ,QAAQ,MAAM,cAAc,IAAI,WAAW;AACrD,eAAO,UAAU;AAAA,MACnB;AAAA,IACF;AAAA,IACA,QAAQ,OAAO;AACb,YAAM,MAAM,OAAO;AACnB,aAAO,UAAU;AACjB,UAAI,KAAK,oBAAoB;AAI3B,cAAM,eAAA;AACN,cAAM,gBAAA;AACN;AAAA,MACF;AACA,eAAS,UAAU,KAAK;AAAA,IAC1B;AAAA,EAAA;AAEJ;;"}
package/dist/index.d.ts CHANGED
@@ -2198,6 +2198,13 @@ export declare const GameIcon: React_2.ForwardRefExoticComponent<BaseIconProps &
2198
2198
  /** Props for {@link GameIcon}. See {@link BaseIconProps} for the shared shape. */
2199
2199
  export declare type GameIconProps = BaseIconProps;
2200
2200
 
2201
+ declare type GatedHandlers = {
2202
+ onPointerDown?: React_2.PointerEventHandler;
2203
+ onPointerMove?: React_2.PointerEventHandler;
2204
+ onPointerCancel?: React_2.PointerEventHandler;
2205
+ onClick?: React_2.MouseEventHandler;
2206
+ };
2207
+
2201
2208
  /**
2202
2209
  * Gender icon. Renders at sizes 16, 24, or 32 px with outlined and filled variants.
2203
2210
  *
@@ -4519,6 +4526,23 @@ export declare const UsersIcon: React_2.ForwardRefExoticComponent<BaseIconProps
4519
4526
  /** Props for {@link UsersIcon}. See {@link BaseIconProps} for the shared shape. */
4520
4527
  export declare type UsersIconProps = BaseIconProps;
4521
4528
 
4529
+ /**
4530
+ * Composes the consumer's pointer/click handlers with a touch-drag tracker
4531
+ * that suppresses the synthetic click an Android Chrome scroll-drag-end can
4532
+ * dispatch on a click-based Radix trigger (Popover / Dialog / Drawer).
4533
+ *
4534
+ * On touch / pen input, if the press-and-release crosses a movement threshold,
4535
+ * the subsequent `click` is preventDefault'd — short-circuiting Radix's own
4536
+ * `onOpenToggle` via `composeEventHandlers` — and stopPropagation'd so it
4537
+ * doesn't bubble to ancestors. Mouse input passes through unchanged because
4538
+ * mouse never triggers the Android scroll-drag bug class.
4539
+ *
4540
+ * Related: radix-ui/primitives#1912 (DropdownMenu pointerdown variant) and
4541
+ * #2702 (DismissableLayer touch dismiss). For the DropdownMenu pointerdown
4542
+ * variant see `components/DropdownMenu/DropdownMenu.tsx`.
4543
+ */
4544
+ export declare function useSuppressClickAfterDrag<P extends GatedHandlers = GatedHandlers>(props?: P): P & Required<GatedHandlers>;
4545
+
4522
4546
  /**
4523
4547
  * Vault icon. Renders at sizes 16, 24, or 32 px with outlined and filled variants.
4524
4548
  *
package/dist/index.mjs CHANGED
@@ -215,6 +215,7 @@ import { TextField } from "./components/TextField/TextField.mjs";
215
215
  import { Toast, ToastProvider, ToastViewport } from "./components/Toast/Toast.mjs";
216
216
  import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "./components/Tooltip/Tooltip.mjs";
217
217
  import { cn } from "./utils/cn.mjs";
218
+ import { useSuppressClickAfterDrag } from "./utils/useSuppressClickAfterDrag.mjs";
218
219
  export {
219
220
  AI2Icon,
220
221
  AIDisclosureIcon,
@@ -498,6 +499,7 @@ export {
498
499
  WifiOnIcon,
499
500
  WrenchIcon,
500
501
  cn,
501
- useAudioRecorder
502
+ useAudioRecorder,
503
+ useSuppressClickAfterDrag
502
504
  };
503
505
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -3,10 +3,11 @@ import * as React from "react";
3
3
  const TAP_MOVEMENT_THRESHOLD_PX = 10;
4
4
  function useSuppressClickAfterDrag(props) {
5
5
  const tapRef = React.useRef(null);
6
+ const consumer = props ?? {};
6
7
  return {
7
- ...props,
8
+ ...consumer,
8
9
  onPointerDown(event) {
9
- props.onPointerDown?.(event);
10
+ consumer.onPointerDown?.(event);
10
11
  if (event.pointerType === "mouse") {
11
12
  tapRef.current = null;
12
13
  return;
@@ -20,7 +21,7 @@ function useSuppressClickAfterDrag(props) {
20
21
  };
21
22
  },
22
23
  onPointerMove(event) {
23
- props.onPointerMove?.(event);
24
+ consumer.onPointerMove?.(event);
24
25
  const tap = tapRef.current;
25
26
  if (tap === null || event.pointerId !== tap.pointerId || tap.movedPastThreshold) return;
26
27
  const dx = event.clientX - tap.x;
@@ -30,7 +31,7 @@ function useSuppressClickAfterDrag(props) {
30
31
  }
31
32
  },
32
33
  onPointerCancel(event) {
33
- props.onPointerCancel?.(event);
34
+ consumer.onPointerCancel?.(event);
34
35
  const tap = tapRef.current;
35
36
  if (tap !== null && event.pointerId === tap.pointerId) {
36
37
  tapRef.current = null;
@@ -44,7 +45,7 @@ function useSuppressClickAfterDrag(props) {
44
45
  event.stopPropagation();
45
46
  return;
46
47
  }
47
- props.onClick?.(event);
48
+ consumer.onClick?.(event);
48
49
  }
49
50
  };
50
51
  }
@@ -1 +1 @@
1
- {"version":3,"file":"useSuppressClickAfterDrag.mjs","sources":["../../src/utils/useSuppressClickAfterDrag.ts"],"sourcesContent":["import * as React from \"react\";\n\nconst TAP_MOVEMENT_THRESHOLD_PX = 10;\n\ntype ActiveTap = {\n pointerId: number;\n x: number;\n y: number;\n movedPastThreshold: boolean;\n};\n\ntype GatedHandlers = {\n onPointerDown?: React.PointerEventHandler;\n onPointerMove?: React.PointerEventHandler;\n onPointerCancel?: React.PointerEventHandler;\n onClick?: React.MouseEventHandler;\n};\n\n/**\n * Composes the consumer's pointer/click handlers with a touch-drag tracker\n * that suppresses the synthetic click an Android Chrome scroll-drag-end can\n * dispatch on a click-based Radix trigger (Popover / Dialog / Drawer).\n *\n * On touch / pen input, if the press-and-release crosses a movement threshold,\n * the subsequent `click` is preventDefault'd — short-circuiting Radix's own\n * `onOpenToggle` via `composeEventHandlers` — and stopPropagation'd so it\n * doesn't bubble to ancestors. Mouse input passes through unchanged because\n * mouse never triggers the Android scroll-drag bug class.\n *\n * Related: radix-ui/primitives#1912 (DropdownMenu pointerdown variant) and\n * #2702 (DismissableLayer touch dismiss). For the DropdownMenu pointerdown\n * variant see `components/DropdownMenu/DropdownMenu.tsx`.\n */\nexport function useSuppressClickAfterDrag<P extends GatedHandlers>(props: P): P {\n const tapRef = React.useRef<ActiveTap | null>(null);\n\n return {\n ...props,\n onPointerDown(event) {\n props.onPointerDown?.(event);\n if (event.pointerType === \"mouse\") {\n tapRef.current = null;\n return;\n }\n // Keep pointermove on this element if the finger drifts off.\n // Optional because jsdom (used in tests) doesn't implement it.\n event.currentTarget.setPointerCapture?.(event.pointerId);\n tapRef.current = {\n pointerId: event.pointerId,\n x: event.clientX,\n y: event.clientY,\n movedPastThreshold: false,\n };\n },\n onPointerMove(event) {\n props.onPointerMove?.(event);\n const tap = tapRef.current;\n if (tap === null || event.pointerId !== tap.pointerId || tap.movedPastThreshold) return;\n const dx = event.clientX - tap.x;\n const dy = event.clientY - tap.y;\n if (Math.hypot(dx, dy) > TAP_MOVEMENT_THRESHOLD_PX) {\n tap.movedPastThreshold = true;\n }\n },\n onPointerCancel(event) {\n props.onPointerCancel?.(event);\n const tap = tapRef.current;\n if (tap !== null && event.pointerId === tap.pointerId) {\n tapRef.current = null;\n }\n },\n onClick(event) {\n const tap = tapRef.current;\n tapRef.current = null;\n if (tap?.movedPastThreshold) {\n // preventDefault stops Radix's onClick → onOpenToggle via\n // composeEventHandlers. stopPropagation prevents the synthetic click\n // from bubbling to ancestor click handlers.\n event.preventDefault();\n event.stopPropagation();\n return;\n }\n props.onClick?.(event);\n },\n };\n}\n"],"names":[],"mappings":";;AAEA,MAAM,4BAA4B;AA+B3B,SAAS,0BAAmD,OAAa;AAC9E,QAAM,SAAS,MAAM,OAAyB,IAAI;AAElD,SAAO;AAAA,IACL,GAAG;AAAA,IACH,cAAc,OAAO;AACnB,YAAM,gBAAgB,KAAK;AAC3B,UAAI,MAAM,gBAAgB,SAAS;AACjC,eAAO,UAAU;AACjB;AAAA,MACF;AAGA,YAAM,cAAc,oBAAoB,MAAM,SAAS;AACvD,aAAO,UAAU;AAAA,QACf,WAAW,MAAM;AAAA,QACjB,GAAG,MAAM;AAAA,QACT,GAAG,MAAM;AAAA,QACT,oBAAoB;AAAA,MAAA;AAAA,IAExB;AAAA,IACA,cAAc,OAAO;AACnB,YAAM,gBAAgB,KAAK;AAC3B,YAAM,MAAM,OAAO;AACnB,UAAI,QAAQ,QAAQ,MAAM,cAAc,IAAI,aAAa,IAAI,mBAAoB;AACjF,YAAM,KAAK,MAAM,UAAU,IAAI;AAC/B,YAAM,KAAK,MAAM,UAAU,IAAI;AAC/B,UAAI,KAAK,MAAM,IAAI,EAAE,IAAI,2BAA2B;AAClD,YAAI,qBAAqB;AAAA,MAC3B;AAAA,IACF;AAAA,IACA,gBAAgB,OAAO;AACrB,YAAM,kBAAkB,KAAK;AAC7B,YAAM,MAAM,OAAO;AACnB,UAAI,QAAQ,QAAQ,MAAM,cAAc,IAAI,WAAW;AACrD,eAAO,UAAU;AAAA,MACnB;AAAA,IACF;AAAA,IACA,QAAQ,OAAO;AACb,YAAM,MAAM,OAAO;AACnB,aAAO,UAAU;AACjB,UAAI,KAAK,oBAAoB;AAI3B,cAAM,eAAA;AACN,cAAM,gBAAA;AACN;AAAA,MACF;AACA,YAAM,UAAU,KAAK;AAAA,IACvB;AAAA,EAAA;AAEJ;"}
1
+ {"version":3,"file":"useSuppressClickAfterDrag.mjs","sources":["../../src/utils/useSuppressClickAfterDrag.ts"],"sourcesContent":["import * as React from \"react\";\n\nconst TAP_MOVEMENT_THRESHOLD_PX = 10;\n\ntype ActiveTap = {\n pointerId: number;\n x: number;\n y: number;\n movedPastThreshold: boolean;\n};\n\ntype GatedHandlers = {\n onPointerDown?: React.PointerEventHandler;\n onPointerMove?: React.PointerEventHandler;\n onPointerCancel?: React.PointerEventHandler;\n onClick?: React.MouseEventHandler;\n};\n\n/**\n * Composes the consumer's pointer/click handlers with a touch-drag tracker\n * that suppresses the synthetic click an Android Chrome scroll-drag-end can\n * dispatch on a click-based Radix trigger (Popover / Dialog / Drawer).\n *\n * On touch / pen input, if the press-and-release crosses a movement threshold,\n * the subsequent `click` is preventDefault'd — short-circuiting Radix's own\n * `onOpenToggle` via `composeEventHandlers` — and stopPropagation'd so it\n * doesn't bubble to ancestors. Mouse input passes through unchanged because\n * mouse never triggers the Android scroll-drag bug class.\n *\n * Related: radix-ui/primitives#1912 (DropdownMenu pointerdown variant) and\n * #2702 (DismissableLayer touch dismiss). For the DropdownMenu pointerdown\n * variant see `components/DropdownMenu/DropdownMenu.tsx`.\n */\nexport function useSuppressClickAfterDrag<P extends GatedHandlers = GatedHandlers>(\n props?: P,\n): P & Required<GatedHandlers> {\n const tapRef = React.useRef<ActiveTap | null>(null);\n const consumer = props ?? ({} as P);\n\n return {\n ...consumer,\n onPointerDown(event) {\n consumer.onPointerDown?.(event);\n if (event.pointerType === \"mouse\") {\n tapRef.current = null;\n return;\n }\n // Keep pointermove on this element if the finger drifts off.\n // Optional because jsdom (used in tests) doesn't implement it.\n event.currentTarget.setPointerCapture?.(event.pointerId);\n tapRef.current = {\n pointerId: event.pointerId,\n x: event.clientX,\n y: event.clientY,\n movedPastThreshold: false,\n };\n },\n onPointerMove(event) {\n consumer.onPointerMove?.(event);\n const tap = tapRef.current;\n if (tap === null || event.pointerId !== tap.pointerId || tap.movedPastThreshold) return;\n const dx = event.clientX - tap.x;\n const dy = event.clientY - tap.y;\n if (Math.hypot(dx, dy) > TAP_MOVEMENT_THRESHOLD_PX) {\n tap.movedPastThreshold = true;\n }\n },\n onPointerCancel(event) {\n consumer.onPointerCancel?.(event);\n const tap = tapRef.current;\n if (tap !== null && event.pointerId === tap.pointerId) {\n tapRef.current = null;\n }\n },\n onClick(event) {\n const tap = tapRef.current;\n tapRef.current = null;\n if (tap?.movedPastThreshold) {\n // preventDefault stops Radix's onClick → onOpenToggle via\n // composeEventHandlers. stopPropagation prevents the synthetic click\n // from bubbling to ancestor click handlers.\n event.preventDefault();\n event.stopPropagation();\n return;\n }\n consumer.onClick?.(event);\n },\n };\n}\n"],"names":[],"mappings":";;AAEA,MAAM,4BAA4B;AA+B3B,SAAS,0BACd,OAC6B;AAC7B,QAAM,SAAS,MAAM,OAAyB,IAAI;AAClD,QAAM,WAAW,SAAU,CAAA;AAE3B,SAAO;AAAA,IACL,GAAG;AAAA,IACH,cAAc,OAAO;AACnB,eAAS,gBAAgB,KAAK;AAC9B,UAAI,MAAM,gBAAgB,SAAS;AACjC,eAAO,UAAU;AACjB;AAAA,MACF;AAGA,YAAM,cAAc,oBAAoB,MAAM,SAAS;AACvD,aAAO,UAAU;AAAA,QACf,WAAW,MAAM;AAAA,QACjB,GAAG,MAAM;AAAA,QACT,GAAG,MAAM;AAAA,QACT,oBAAoB;AAAA,MAAA;AAAA,IAExB;AAAA,IACA,cAAc,OAAO;AACnB,eAAS,gBAAgB,KAAK;AAC9B,YAAM,MAAM,OAAO;AACnB,UAAI,QAAQ,QAAQ,MAAM,cAAc,IAAI,aAAa,IAAI,mBAAoB;AACjF,YAAM,KAAK,MAAM,UAAU,IAAI;AAC/B,YAAM,KAAK,MAAM,UAAU,IAAI;AAC/B,UAAI,KAAK,MAAM,IAAI,EAAE,IAAI,2BAA2B;AAClD,YAAI,qBAAqB;AAAA,MAC3B;AAAA,IACF;AAAA,IACA,gBAAgB,OAAO;AACrB,eAAS,kBAAkB,KAAK;AAChC,YAAM,MAAM,OAAO;AACnB,UAAI,QAAQ,QAAQ,MAAM,cAAc,IAAI,WAAW;AACrD,eAAO,UAAU;AAAA,MACnB;AAAA,IACF;AAAA,IACA,QAAQ,OAAO;AACb,YAAM,MAAM,OAAO;AACnB,aAAO,UAAU;AACjB,UAAI,KAAK,oBAAoB;AAI3B,cAAM,eAAA;AACN,cAAM,gBAAA;AACN;AAAA,MACF;AACA,eAAS,UAAU,KAAK;AAAA,IAC1B;AAAA,EAAA;AAEJ;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fanvue/ui",
3
- "version": "2.15.1",
3
+ "version": "2.16.0",
4
4
  "description": "React component library built with Tailwind CSS for Fanvue ecosystem",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org",