@mlola-ui/behavior 1.1.6 → 1.1.7

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mlola-ui/behavior",
3
- "version": "1.1.6",
3
+ "version": "1.1.7",
4
4
  "description": "Framework-free behavior for Mlola UI markup: standard DOM, no runtime peers",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
package/src/index.js CHANGED
@@ -22,6 +22,7 @@ import {
22
22
  focusTrapIndex,
23
23
  percentOf,
24
24
  fitTooltip,
25
+ isSidewaysDrag,
25
26
  placeFloating,
26
27
  rovingIndex,
27
28
  sliderValueForKey,
@@ -456,22 +457,40 @@ const behaviors = {
456
457
  return valueFromRatio((event.clientX - box.left) / box.width, bounds());
457
458
  };
458
459
 
459
- let dragging = false;
460
+ // The whole control row takes the pointer. A mouse or pen sets the value
461
+ // where it presses; a finger only once it moves sideways, or on a tap, so
462
+ // scrolling past a slider leaves it alone (the React build does the same).
463
+ const surface = root.querySelector(".ml-slider") ?? track;
464
+ let gesture = null;
460
465
 
461
466
  return [
462
- on(track, "pointerdown", (event) => {
463
- if (disabled) return;
464
- dragging = true;
465
- track.setPointerCapture?.(event.pointerId);
466
- set(fromPointer(event));
467
+ on(surface, "pointerdown", (event) => {
468
+ if (disabled || event.button !== 0) return;
469
+ const touch = event.pointerType === "touch";
470
+ gesture = { id: event.pointerId, touch, x: event.clientX, y: event.clientY, active: !touch };
471
+ if (!touch) {
472
+ surface.setPointerCapture?.(event.pointerId);
473
+ set(fromPointer(event));
474
+ }
467
475
  valued.focus();
468
476
  }),
469
- on(track, "pointermove", (event) => {
470
- if (dragging && !disabled) set(fromPointer(event));
477
+ on(surface, "pointermove", (event) => {
478
+ if (disabled || !gesture || gesture.id !== event.pointerId) return;
479
+ if (!gesture.active) {
480
+ if (!isSidewaysDrag(event.clientX - gesture.x, event.clientY - gesture.y)) return;
481
+ gesture.active = true;
482
+ surface.setPointerCapture?.(event.pointerId);
483
+ }
484
+ set(fromPointer(event));
485
+ }),
486
+ on(surface, "pointerup", (event) => {
487
+ const ended = gesture;
488
+ gesture = null;
489
+ surface.releasePointerCapture?.(event.pointerId);
490
+ if (!disabled && ended?.touch && !ended.active) set(fromPointer(event));
471
491
  }),
472
- on(track, "pointerup", (event) => {
473
- dragging = false;
474
- track.releasePointerCapture?.(event.pointerId);
492
+ on(surface, "pointercancel", () => {
493
+ gesture = null;
475
494
  }),
476
495
  on(valued, "keydown", (event) => {
477
496
  if (disabled) return;
@@ -63,6 +63,16 @@ export function sliderValueForKey(key, current, { min = 0, max = 100, step = 1 }
63
63
  return clampToStep(current + delta, { min: low, max: high, step: increment });
64
64
  }
65
65
 
66
+ /**
67
+ * Whether a finger that pressed on a horizontal control and moved `dx`, `dy`
68
+ * pixels is dragging it rather than scrolling the page: it went sideways at
69
+ * least `threshold` pixels, and more sideways than up or down. Until then the
70
+ * control changes nothing, so a person scrolling past it does not move it.
71
+ */
72
+ export function isSidewaysDrag(dx, dy, threshold = 6) {
73
+ return Math.abs(dx) >= threshold && Math.abs(dx) >= Math.abs(dy);
74
+ }
75
+
66
76
  /**
67
77
  * Where a roving-focus key lands. `enabled` reports whether an index may take
68
78
  * focus, so disabled entries are stepped over rather than landed on.
package/src/logic.d.ts CHANGED
@@ -27,6 +27,7 @@ export interface DisclosureOptions {
27
27
  export function clampToStep(raw: number, bounds?: Bounds): number;
28
28
  export function valueFromRatio(ratio: number, bounds?: Bounds): number;
29
29
  export function percentOf(value: number, bounds?: Pick<Bounds, "min" | "max">): number;
30
+ export function isSidewaysDrag(dx: number, dy: number, threshold?: number): boolean;
30
31
  export function sliderValueForKey(
31
32
  key: string,
32
33
  current: number,