@lynx-example/design-guide 0.3.1 → 0.4.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.
@@ -1,5 +1,5 @@
1
1
  import type { ReactNode } from "@lynx-js/react";
2
- import type { LayoutChangeEvent, TouchEvent } from "@lynx-js/types";
2
+ import type { LayoutChangeEvent, MouseEvent, TouchEvent } from "@lynx-js/types";
3
3
 
4
4
  type DotProps = {
5
5
  /** Normalized x position in the field (0..1). */
@@ -37,12 +37,15 @@ type DotFieldProps = {
37
37
  /** Field children (dots or other field-aware elements). */
38
38
  children?: ReactNode;
39
39
 
40
- /** Pointer / touch handlers bound at the field level. */
40
+ /** Pointer (touch/mouse) handlers bound at the field level. */
41
41
  bindtouchstart?: (e: TouchEvent) => void;
42
42
  bindtouchmove?: (e: TouchEvent) => void;
43
43
  bindtouchend?: (e: TouchEvent) => void;
44
44
  bindtouchcancel?: (e: TouchEvent) => void;
45
45
 
46
+ bindmousedown?: (e: MouseEvent) => void;
47
+ bindmousemove?: (e: MouseEvent) => void;
48
+ bindmouseup?: (e: MouseEvent) => void;
46
49
  /** Layout measurement handler for field coordinate mapping. */
47
50
  bindlayoutchange?: (e: LayoutChangeEvent) => void;
48
51
  };
@@ -2,7 +2,7 @@ import { root } from "@lynx-js/react";
2
2
  import { useMemo } from "@lynx-js/react";
3
3
 
4
4
  import { Caption } from "../shared/components/caption/index.jsx";
5
- import { usePointerFieldPoint } from "../shared/hooks/use-pointer-field-point/index.js";
5
+ import { usePointerPoint } from "../shared/hooks/use-pointer-point/index.js";
6
6
  import { CELLS } from "./cells.js";
7
7
  import { lerpColor } from "./color.js";
8
8
  import { makeForceField } from "./field-force.js";
@@ -24,11 +24,8 @@ const forceAt = makeForceField({
24
24
  function App() {
25
25
  const {
26
26
  p, // Force point in normalized space
27
- handlePointerDown,
28
- handlePointerMove,
29
- handlePointerUp,
30
- handleElementLayoutChange,
31
- } = usePointerFieldPoint({ x0: 0.6, y0: 1 });
27
+ bind, // Spreadable interaction bindings (touch + mouse + layout)
28
+ } = usePointerPoint({ x0: 0.6, y0: 1 });
32
29
 
33
30
  const models = useMemo(() => {
34
31
  let minD = Infinity;
@@ -68,10 +65,7 @@ function App() {
68
65
  fieldSize={300}
69
66
  dotSize={5}
70
67
  dotAccentColor="#ff1a6e"
71
- bindtouchstart={handlePointerDown}
72
- bindtouchmove={handlePointerMove}
73
- bindtouchend={handlePointerUp}
74
- bindlayoutchange={handleElementLayoutChange}
68
+ {...bind}
75
69
  >
76
70
  {models.map((m) => (
77
71
  <Dot
@@ -1,16 +1,26 @@
1
+ /*
2
+ * Gooey Effect (blur + contrast)
3
+ *
4
+ * Visual output depends on each platform’s graphics pipeline:
5
+ * - Web renders a precise, crisp Gaussian blur.
6
+ * - iOS appears softer and more diffused.
7
+ * - Android may exhibit minor geometric artifacts on small surfaces
8
+ * (e.g. a diamond-shaped silhouette) due to blur and downsampling.
9
+ *
10
+ * Blur is applied at the container level to reduce per-element artifacts.
11
+ */
12
+
1
13
  .stage {
2
- filter: contrast(100);
3
- /* background color must be explicit, for contrast */
14
+ filter: contrast(5);
15
+ /* Background color must be explicit for contrast-based thresholding */
4
16
  background-color: black;
5
-
6
17
  width: 100%;
7
18
  height: 200px;
8
19
  }
9
20
 
10
- .inner-stage {
11
- /* Lynx: only one filter function per declaration */
12
- filter: blur(20px);
13
-
21
+ .dots-container {
22
+ /* Apply blur at the group level to reduce per-element blur artifacts on some platforms */
23
+ filter: blur(12px);
14
24
  width: 100%;
15
25
  height: 100%;
16
26
  display: flex;
@@ -20,11 +30,11 @@
20
30
  }
21
31
 
22
32
  .dot {
23
- width: 64px;
24
- height: 64px;
25
- border-radius: 32px;
33
+ /* Keep dots crisp; container-level blur produces the gooey merge */
34
+ width: 54px;
35
+ height: 54px;
36
+ border-radius: 999px;
26
37
  background: white;
27
-
28
38
  animation: blob 2200ms ease-in-out infinite;
29
39
  }
30
40
 
@@ -44,19 +54,19 @@
44
54
 
45
55
  @keyframes blob {
46
56
  0% {
47
- transform: translateX(-24px) translateY(0px) scale(1.00);
57
+ transform: translateX(-18px) translateY(0px) scale(1.00);
48
58
  }
49
59
  25% {
50
- transform: translateX(10px) translateY(-6px) scale(1.06);
60
+ transform: translateX(7.5px) translateY(-4.5px) scale(1.06);
51
61
  }
52
62
  50% {
53
- transform: translateX(28px) translateY(0px) scale(1.00);
63
+ transform: translateX(21px) translateY(0px) scale(1.00);
54
64
  }
55
65
  75% {
56
- transform: translateX(6px) translateY(6px) scale(0.98);
66
+ transform: translateX(4.5px) translateY(4.5px) scale(0.98);
57
67
  }
58
68
  100% {
59
- transform: translateX(-24px) translateY(0px) scale(1.00);
69
+ transform: translateX(-18px) translateY(0px) scale(1.00);
60
70
  }
61
71
  }
62
72
 
@@ -73,7 +83,7 @@
73
83
  justify-content: center;
74
84
  align-items: center;
75
85
  background-color: black;
76
- gap: 48px;
86
+ gap: 40px;
77
87
 
78
88
  padding-left: 0;
79
89
  padding-right: 0;
@@ -7,7 +7,7 @@ function App() {
7
7
  return (
8
8
  <view className="design-container">
9
9
  <view className="stage">
10
- <view className="inner-stage">
10
+ <view className="dots-container">
11
11
  <view className="dot g1" />
12
12
  <view className="dot g2" />
13
13
  <view className="dot g3" />
@@ -0,0 +1,70 @@
1
+ import { useRef } from "@lynx-js/react";
2
+ import type { MutableRefObject } from "@lynx-js/react";
3
+ import type { LayoutChangeEvent } from "@lynx-js/types";
4
+
5
+ /**
6
+ * Element measurement frame.
7
+ *
8
+ * Maintains the element's screen-based bounding frame:
9
+ * - left / top (from boundingClientRect)
10
+ * - width / height (from layout change)
11
+ *
12
+ * Designed to be shared by multiple pointer axes
13
+ * to avoid duplicate layout queries.
14
+ */
15
+ export type ElementFrame = {
16
+ /** Screen-based left offset */
17
+ leftRef: MutableRefObject<number | null>;
18
+
19
+ /** Screen-based top offset */
20
+ topRef: MutableRefObject<number | null>;
21
+
22
+ /** Element width (px) */
23
+ widthRef: MutableRefObject<number>;
24
+
25
+ /** Element height (px) */
26
+ heightRef: MutableRefObject<number>;
27
+ };
28
+
29
+ export type UseElementFrameReturnValue = ElementFrame & {
30
+ /** Bind to <view bindlayoutchange={...} /> */
31
+ handleLayoutChange: (e: LayoutChangeEvent) => void;
32
+ };
33
+
34
+ export function useElementFrame(): UseElementFrameReturnValue {
35
+ const leftRef = useRef<number | null>(null);
36
+ const topRef = useRef<number | null>(null);
37
+ const widthRef = useRef(0);
38
+ const heightRef = useRef(0);
39
+
40
+ const handleLayoutChange = (e: LayoutChangeEvent) => {
41
+ // Sync element size from layout event
42
+ widthRef.current = e.detail.width;
43
+ heightRef.current = e.detail.height;
44
+
45
+ // Query screen-based bounding rect
46
+ const currentTarget = lynx
47
+ .createSelectorQuery()
48
+ // @ts-expect-error Lynx internal UID typing
49
+ .selectUniqueID(e.currentTarget.uid ?? e.currentTarget.uniqueId);
50
+
51
+ currentTarget
52
+ ?.invoke({
53
+ method: "boundingClientRect",
54
+ params: { relativeTo: "screen" },
55
+ success: (res: { left: number; top: number }) => {
56
+ leftRef.current = res.left;
57
+ topRef.current = res.top;
58
+ },
59
+ })
60
+ .exec();
61
+ };
62
+
63
+ return {
64
+ leftRef,
65
+ topRef,
66
+ widthRef,
67
+ heightRef,
68
+ handleLayoutChange,
69
+ };
70
+ }
@@ -0,0 +1,90 @@
1
+ import { useRef } from "@lynx-js/react";
2
+ import type { LayoutChangeEvent, TouchEvent } from "@lynx-js/types";
3
+ import { useElementFrame } from "../use-element-frame/index.js";
4
+ import type { PointerAxisPosition, UsePointerAxisProps, UsePointerAxisReturnValueBase } from "./types.js";
5
+
6
+ /**
7
+ * Pointer → element-local coordinates adapter (single axis, X).
8
+ *
9
+ * - The container usually hosts touch/pointer events (recommended for larger hit area).
10
+ * - The element provides the measurement frame (layout + bounding rect).
11
+ * - If you don't need a separate container, you may bind all handlers on the element
12
+ * itself (container === element).
13
+ *
14
+ * No clamping/step logic is applied here.
15
+ */
16
+ function usePointerAxis({
17
+ axis = "x",
18
+ onUpdate,
19
+ onCommit,
20
+ frame: externalFrame,
21
+ }: UsePointerAxisProps = {}): UsePointerAxisReturnValue {
22
+ /** Element (coordinate frame) & metrics */
23
+
24
+ const internalFrameRef = useElementFrame();
25
+ const frame = externalFrame ?? internalFrameRef;
26
+
27
+ const posRef = useRef<PointerAxisPosition | null>(null);
28
+ const draggingRef = useRef(false);
29
+
30
+ const pickCoord = (e: TouchEvent) => (axis === "x" ? e.detail.x : e.detail.y);
31
+
32
+ const startRef = axis === "x" ? frame.leftRef : frame.topRef;
33
+ const lengthRef = axis === "x" ? frame.widthRef : frame.heightRef;
34
+
35
+ const buildPosition = (coord: number): PointerAxisPosition | null => {
36
+ const length = lengthRef.current;
37
+ const start = startRef.current;
38
+
39
+ if (length > 0 && start != null) {
40
+ const offset = coord - start;
41
+ const offsetRatio = offset / length;
42
+ const pos = { offset, offsetRatio, elementLength: length };
43
+ posRef.current = pos;
44
+ return pos;
45
+ }
46
+ return null;
47
+ };
48
+
49
+ const handlePointerDown = (e: TouchEvent) => {
50
+ draggingRef.current = true;
51
+ buildPosition(pickCoord(e));
52
+ if (posRef.current) onUpdate?.(posRef.current);
53
+ };
54
+
55
+ const handlePointerMove = (e: TouchEvent) => {
56
+ if (!draggingRef.current) return;
57
+ buildPosition(pickCoord(e));
58
+ if (posRef.current) onUpdate?.(posRef.current);
59
+ };
60
+
61
+ const handlePointerUp = (e: TouchEvent) => {
62
+ draggingRef.current = false;
63
+ buildPosition(pickCoord(e));
64
+ if (posRef.current) {
65
+ onUpdate?.(posRef.current);
66
+ onCommit?.(posRef.current);
67
+ }
68
+ };
69
+
70
+ const handleElementLayoutChange = (e: LayoutChangeEvent) => {
71
+ // If using injected frame, measurement is handled elsewhere.
72
+ if (externalFrame != null) return;
73
+ internalFrameRef.handleLayoutChange(e);
74
+ };
75
+
76
+ return {
77
+ handlePointerDown,
78
+ handlePointerMove,
79
+ handlePointerUp,
80
+ handleElementLayoutChange,
81
+ };
82
+ }
83
+
84
+ type UsePointerAxisReturnValue = UsePointerAxisReturnValueBase<
85
+ TouchEvent,
86
+ LayoutChangeEvent
87
+ >;
88
+
89
+ export { usePointerAxis };
90
+ export type { PointerAxisPosition, UsePointerAxisProps, UsePointerAxisReturnValue };
@@ -0,0 +1,37 @@
1
+ import type { ElementFrame } from "../use-element-frame/index.js";
2
+
3
+ /** Pointer position in the element’s local frame. */
4
+ type PointerAxisPosition = {
5
+ /** Horizontal offset from element's left edge (px). Can be <0 or >length. */
6
+ offset: number;
7
+ /** offset / elementLength. Can be <0 or >1. */
8
+ offsetRatio: number;
9
+ /** Measured length of the element along this axis (px). */
10
+ elementLength: number;
11
+ };
12
+
13
+ /** Interaction callbacks. */
14
+ type UsePointerAxisProps = {
15
+ axis?: PointerAxis;
16
+ /** Injected measurement frame */
17
+ frame?: ElementFrame;
18
+ /** Fires during drag/move. */
19
+ onUpdate?: (pos: PointerAxisPosition) => void;
20
+ /** Fires on pointer up (final value). */
21
+ onCommit?: (pos: PointerAxisPosition) => void;
22
+ };
23
+
24
+ type UsePointerAxisReturnValueBase<TPointer, TLayout> = {
25
+ /** Bind on CONTAINER (or ELEMENT if container === element): <view bindtouchstart|bindmousedown={handlePointerDown} /> */
26
+ handlePointerDown: (e: TPointer) => void;
27
+ /** Bind on CONTAINER (or ELEMENT if container === element): <view bindtouchmove|bindmousemove={handlePointerMove} /> */
28
+ handlePointerMove: (e: TPointer) => void;
29
+ /** Bind on CONTAINER (or ELEMENT if container===element): <view bindtouchend|bindtouchcancel|bindmousedown|bindmousecancel={handlePointerUp} /> */
30
+ handlePointerUp: (e: TPointer) => void;
31
+ /** Bind on ELEMENT: <view bindlayoutchange={handleElementLayoutChange} /> */
32
+ handleElementLayoutChange: (e: TLayout) => void;
33
+ };
34
+
35
+ type PointerAxis = "x" | "y";
36
+
37
+ export type { PointerAxis, PointerAxisPosition, UsePointerAxisProps, UsePointerAxisReturnValueBase };
@@ -0,0 +1,112 @@
1
+ import { useRef, useState } from "@lynx-js/react";
2
+ import { useTouchEmulation } from "@lynx-js/react-use";
3
+ import type { LayoutChangeEvent, MouseEvent, TouchEvent } from "@lynx-js/types";
4
+ import { useElementFrame } from "../use-element-frame/index.js";
5
+ import { usePointerAxis } from "../use-pointer-axis/index.js";
6
+
7
+ export type PointerPoint = { x: number; y: number };
8
+
9
+ export type UsePointerPointProps = {
10
+ x0?: number;
11
+ y0?: number;
12
+ };
13
+
14
+ export type PointerBindProps = {
15
+ /** Pointer (touch/mouse) handlers bound at the field level. */
16
+ bindtouchstart?: (e: TouchEvent) => void;
17
+ bindtouchmove?: (e: TouchEvent) => void;
18
+ bindtouchend?: (e: TouchEvent) => void;
19
+ bindtouchcancel?: (e: TouchEvent) => void;
20
+
21
+ bindmousedown?: (e: MouseEvent) => void;
22
+ bindmousemove?: (e: MouseEvent) => void;
23
+ bindmouseup?: (e: MouseEvent) => void;
24
+
25
+ /** Layout measurement handler for field coordinate mapping. */
26
+ bindlayoutchange?: (e: LayoutChangeEvent) => void;
27
+ };
28
+
29
+ export type UsePointerPointReturnValue = {
30
+ p: PointerPoint;
31
+ bind: PointerBindProps;
32
+ handlePointerDown: (e: TouchEvent) => void;
33
+ handlePointerMove: (e: TouchEvent) => void;
34
+ handlePointerUp: (e: TouchEvent) => void;
35
+ handleElementLayoutChange: (e: LayoutChangeEvent) => void;
36
+ };
37
+
38
+ function usePointerPoint({
39
+ x0 = 0.5,
40
+ y0 = 0.5,
41
+ }: UsePointerPointProps = {}): UsePointerPointReturnValue {
42
+ const [p, setP] = useState<PointerPoint>({ x: x0, y: y0 });
43
+
44
+ const lastXRef = useRef(x0);
45
+ const lastYRef = useRef(y0);
46
+
47
+ const frameRef = useElementFrame();
48
+
49
+ const axisX = usePointerAxis({
50
+ axis: "x",
51
+ frame: frameRef,
52
+ onUpdate(pos) {
53
+ lastXRef.current = pos.offsetRatio;
54
+ setP({ x: lastXRef.current, y: lastYRef.current });
55
+ },
56
+ });
57
+
58
+ const axisY = usePointerAxis({
59
+ axis: "y",
60
+ frame: frameRef,
61
+ onUpdate(pos) {
62
+ lastYRef.current = pos.offsetRatio;
63
+ setP({ x: lastXRef.current, y: lastYRef.current });
64
+ },
65
+ });
66
+
67
+ const handlePointerDown = (e: TouchEvent) => {
68
+ axisY.handlePointerDown(e);
69
+ axisX.handlePointerDown(e);
70
+ };
71
+
72
+ const handlePointerMove = (e: TouchEvent) => {
73
+ axisY.handlePointerMove(e);
74
+ axisX.handlePointerMove(e);
75
+ };
76
+
77
+ const handlePointerUp = (e: TouchEvent) => {
78
+ axisY.handlePointerUp(e);
79
+ axisX.handlePointerUp(e);
80
+ };
81
+
82
+ const handleElementLayoutChange = (e: LayoutChangeEvent) => {
83
+ frameRef.handleLayoutChange(e);
84
+ };
85
+
86
+ // One emulation at the boundary (touch + mouse)
87
+ const bindPointer = useTouchEmulation({
88
+ onTouchStart: handlePointerDown,
89
+ onTouchMove: handlePointerMove,
90
+ onTouchEnd: handlePointerUp,
91
+ });
92
+
93
+ return {
94
+ p,
95
+ handlePointerDown,
96
+ handlePointerMove,
97
+ handlePointerUp,
98
+ handleElementLayoutChange,
99
+ bind: {
100
+ bindtouchstart: bindPointer.bindtouchstart,
101
+ bindtouchmove: bindPointer.bindtouchmove,
102
+ bindtouchend: bindPointer.bindtouchend,
103
+ bindtouchcancel: bindPointer.bindtouchcancel,
104
+ bindmousedown: bindPointer.bindmousedown,
105
+ bindmousemove: bindPointer.bindmousemove,
106
+ bindmouseup: bindPointer.bindmouseup,
107
+ bindlayoutchange: handleElementLayoutChange,
108
+ },
109
+ };
110
+ }
111
+
112
+ export { usePointerPoint };
@@ -1,85 +0,0 @@
1
- import { useRef, useState } from "@lynx-js/react";
2
- import type { LayoutChangeEvent, TouchEvent } from "@lynx-js/types";
3
- import { usePointerInteraction } from "../use-pointer-interaction/index.js";
4
-
5
- type FieldPoint = {
6
- /** Normalized x in [0, 1] (not clamped). */
7
- x: number;
8
- /** Normalized y in [0, 1] (not clamped). */
9
- y: number;
10
- };
11
-
12
- type UsePointerFieldPointProps = {
13
- /** Initial normalized x, defaults to 0.5*/
14
- x0?: number;
15
- y0?: number;
16
- };
17
-
18
- function usePointerFieldPoint({ x0 = 0.5, y0 = 0.5 }: UsePointerFieldPointProps = {}) {
19
- const [p, setP] = useState<FieldPoint>({ x: x0, y: y0 });
20
-
21
- const eleTopRef = useRef<number | null>(null);
22
- const eleHeightRef = useRef(0);
23
-
24
- const lastYRatioRef = useRef(y0);
25
-
26
- const pointer = usePointerInteraction({
27
- onUpdate(pos) {
28
- setP({
29
- x: pos.offsetRatio,
30
- y: lastYRatioRef.current,
31
- });
32
- },
33
- });
34
-
35
- const updateYFromEvent = (e: TouchEvent) => {
36
- const top = eleTopRef.current;
37
- const height = eleHeightRef.current;
38
- if (top != null && height > 0) {
39
- lastYRatioRef.current = (e.detail.y - top) / height;
40
- }
41
- };
42
-
43
- const handlePointerDown2D = (e: TouchEvent) => {
44
- updateYFromEvent(e);
45
- pointer.handlePointerDown(e);
46
- };
47
-
48
- const handlePointerMove2D = (e: TouchEvent) => {
49
- updateYFromEvent(e);
50
- // X handled by existing hook
51
- pointer.handlePointerMove(e);
52
- };
53
-
54
- const handleLayoutChange2D = (e: LayoutChangeEvent) => {
55
- eleHeightRef.current = e.detail.height;
56
-
57
- const currentTarget = lynx
58
- .createSelectorQuery()
59
- // @ts-expect-error
60
- .selectUniqueID(e.currentTarget.uid);
61
-
62
- currentTarget
63
- ?.invoke({
64
- method: "boundingClientRect",
65
- params: { relativeTo: "screen" },
66
- success: (res: { top: number }) => {
67
- eleTopRef.current = res.top;
68
- },
69
- })
70
- .exec();
71
-
72
- pointer.handleElementLayoutChange(e);
73
- };
74
-
75
- return {
76
- p,
77
- handlePointerDown: handlePointerDown2D,
78
- handlePointerMove: handlePointerMove2D,
79
- handlePointerUp: pointer.handlePointerUp,
80
- handleElementLayoutChange: handleLayoutChange2D,
81
- };
82
- }
83
-
84
- export { usePointerFieldPoint };
85
- export type { FieldPoint };
@@ -1,100 +0,0 @@
1
- import { useRef } from "@lynx-js/react";
2
- import type { LayoutChangeEvent, TouchEvent } from "@lynx-js/types";
3
- import type { PointerPosition, UsePointerInteractionProps, UsePointerInteractionReturnValueBase } from "./types.js";
4
-
5
- /**
6
- * Pointer → element-local coordinates adapter.
7
- *
8
- * - The container usually hosts touch/pointer events (recommended for larger hit area).
9
- * - The element provides the measurement frame (layout + bounding rect).
10
- * - If you don't need a separate container, you may bind all handlers on the element
11
- * itself (container === element).
12
- *
13
- * No clamping/step logic is applied here.
14
- */
15
- function usePointerInteraction({
16
- onUpdate,
17
- onCommit,
18
- }: UsePointerInteractionProps = {}): UsePointerInteractionReturnValue {
19
- /** Element (coordinate frame) & metrics */
20
- const eleLeftRef = useRef<number | null>(null);
21
- const eleWidthRef = useRef(0);
22
-
23
- /** Last computed pointer position snapshot */
24
- const posRef = useRef<PointerPosition | null>(null);
25
-
26
- const draggingRef = useRef(false);
27
-
28
- const buildPosition = (x: number): PointerPosition | null => {
29
- const width = eleWidthRef.current;
30
- const left = eleLeftRef.current;
31
-
32
- if (width > 0 && left != null) {
33
- const offset = x - left;
34
- const offsetRatio = offset / width;
35
- const pos = { offset, offsetRatio, elementWidth: width };
36
- posRef.current = pos;
37
- return pos;
38
- }
39
- return null;
40
- };
41
-
42
- const handlePointerDown = (e: TouchEvent) => {
43
- draggingRef.current = true;
44
- buildPosition(e.detail.x);
45
- if (posRef.current) {
46
- onUpdate?.(posRef.current);
47
- }
48
- };
49
-
50
- const handlePointerMove = (e: TouchEvent) => {
51
- if (!draggingRef.current) return;
52
- buildPosition(e.detail.x);
53
- if (posRef.current) {
54
- onUpdate?.(posRef.current);
55
- }
56
- };
57
-
58
- const handlePointerUp = (e: TouchEvent) => {
59
- draggingRef.current = false;
60
- buildPosition(e.detail.x);
61
- if (posRef.current) {
62
- onUpdate?.(posRef.current);
63
- onCommit?.(posRef.current);
64
- }
65
- };
66
-
67
- const handleElementLayoutChange = (e: LayoutChangeEvent) => {
68
- eleWidthRef.current = e.detail.width;
69
-
70
- const currentTarget = lynx
71
- .createSelectorQuery()
72
- // @ts-expect-error
73
- .selectUniqueID(e.currentTarget.uid);
74
-
75
- currentTarget
76
- ?.invoke({
77
- method: "boundingClientRect",
78
- params: { relativeTo: "screen" }, // screen-based so it matches e.detail.x
79
- success: (res: { left: number }) => {
80
- eleLeftRef.current = res.left;
81
- },
82
- })
83
- .exec();
84
- };
85
-
86
- return {
87
- handlePointerDown,
88
- handlePointerMove,
89
- handlePointerUp,
90
- handleElementLayoutChange,
91
- };
92
- }
93
-
94
- type UsePointerInteractionReturnValue = UsePointerInteractionReturnValueBase<
95
- TouchEvent,
96
- LayoutChangeEvent
97
- >;
98
-
99
- export { usePointerInteraction };
100
- export type { PointerPosition, UsePointerInteractionProps, UsePointerInteractionReturnValue };
@@ -1,30 +0,0 @@
1
- /** Pointer position in the element’s local frame. */
2
- interface PointerPosition {
3
- /** Horizontal offset from element's left edge (px). Can be <0 or >width. */
4
- offset: number;
5
- /** offset / elementWidth. Can be <0 or >1. */
6
- offsetRatio: number;
7
- /** Measured width of the element (px). */
8
- elementWidth: number;
9
- }
10
-
11
- /** Interaction callbacks. */
12
- interface UsePointerInteractionProps {
13
- /** Fires during drag/move. */
14
- onUpdate?: (pos: PointerPosition) => void;
15
- /** Fires on pointer up (final value). */
16
- onCommit?: (pos: PointerPosition) => void;
17
- }
18
-
19
- type UsePointerInteractionReturnValueBase<TTouch, TLayout> = {
20
- /** Bind on CONTAINER (or ELEMENT if container === element): <view bindtouchstart={handlePointerDown} /> */
21
- handlePointerDown: (e: TTouch) => void;
22
- /** Bind on CONTAINER (or ELEMENT if container === element): <view bindtouchmove={handlePointerMove} /> */
23
- handlePointerMove: (e: TTouch) => void;
24
- /** Bind on CONTAINER (or ELEMENT if container===element): <view bindtouchend|bindtouchcancel={handlePointerUp} /> */
25
- handlePointerUp: (e: TTouch) => void;
26
- /** Bind on ELEMENT: <view bindlayoutchange={handleElementLayoutChange} /> */
27
- handleElementLayoutChange: (e: TLayout) => void;
28
- };
29
-
30
- export type { PointerPosition, UsePointerInteractionProps, UsePointerInteractionReturnValueBase };