File size: 1,724 Bytes
5e0e982
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
709db16
5e0e982
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
/** Pressable with pointer-down scale feedback. */

import { animate } from "motion";
import type { ComponentChildren, JSX } from "preact";
import { useRef } from "preact/hooks";
import { prefersReducedMotion, springs } from "../motion/springs";

type PressableProps = {
  children: ComponentChildren;
  className?: string;
  disabled?: boolean;
  onClick?: JSX.MouseEventHandler<HTMLButtonElement>;
  type?: "button" | "submit";
  ariaLabel?: string;
  ariaPressed?: boolean;
};

export function Pressable({
  children,
  className = "",
  disabled = false,
  onClick,
  type = "button",
  ariaLabel,
  ariaPressed,
}: PressableProps) {
  const ref = useRef<HTMLButtonElement>(null);
  const active = useRef(false);

  const setScale = (value: number) => {
    const el = ref.current;
    if (!el || disabled) return;
    if (prefersReducedMotion()) {
      el.style.transform = "none";
      el.style.opacity = value < 1 ? "0.85" : "1";
      return;
    }
    animate(el, { scale: value }, springs.press);
  };

  const onPointerDown = (event: PointerEvent) => {
    if (disabled || event.button !== 0) return;
    active.current = true;
    ref.current?.setPointerCapture(event.pointerId);
    setScale(0.98);
  };

  const endPress = () => {
    if (!active.current) return;
    active.current = false;
    setScale(1);
  };

  return (
    <button
      ref={ref}
      type={type}
      disabled={disabled}
      className={`pressable ${className}`.trim()}
      aria-label={ariaLabel}
      aria-pressed={ariaPressed}
      onPointerDown={onPointerDown}
      onPointerUp={endPress}
      onPointerCancel={endPress}
      onPointerLeave={endPress}
      onClick={onClick}
    >
      {children}
    </button>
  );
}