File size: 3,195 Bytes
2485dd8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
import {useRef, useEffect} from 'react';
import * as THREE from 'three';
import {extend} from '@react-three/fiber';
import ThreeMeshUI from 'three-mesh-ui';
import ThreeMeshUIText, {ThreeMeshUITextType} from './ThreeMeshUIText';
import {Interactive} from '@react-three/xr';

/**
 * Using `?url` at the end of this import tells vite this is a static asset, and
 * provides us a URL to the hashed version of the file when the project is built.
 * See: https://vitejs.dev/guide/assets.html#explicit-url-imports
 */
import robotoFontFamilyJson from '../assets/RobotoMono-Regular-msdf.json?url';
import robotoFontTexture from '../assets/RobotoMono-Regular.png';

extend(ThreeMeshUI);

/**
 * Button component that renders as a three-mesh-ui block
 */
export default function Button({
  onClick,
  content,
  width,
  height,
  fontSize,
  borderRadius,
  padding,
}) {
  const button = useRef<JSX.IntrinsicElements['block']>();
  const textRef = useRef<ThreeMeshUITextType>();

  useEffect(() => {
    if (textRef.current != null) {
      textRef.current.set({content});
    }
  }, [textRef, content]);

  useEffect(() => {
    if (!button.current) {
      return;
    }
    button.current.setupState({
      state: 'hovered',
      attributes: {
        offset: 0.002,
        backgroundColor: new THREE.Color(0x607b8f),
        fontColor: new THREE.Color(0xffffff),
      },
    });
    button.current.setupState({
      state: 'idle',
      attributes: {
        offset: 0.001,
        backgroundColor: new THREE.Color(0x465a69),
        fontColor: new THREE.Color(0xffffff),
      },
    });
    button.current.setupState({
      state: 'selected',
      attributes: {
        offset: 0.005,
        backgroundColor: new THREE.Color(0x000000),
        fontColor: new THREE.Color(0xffffff),
      },
    });
    button.current.setState('idle');
  }, []);

  const args = [
    {
      width,
      height,
      fontSize,
      padding,
      justifyContent: 'end',
      textAlign: 'center',
      alignItems: 'center',
      borderRadius,
      fontFamily: robotoFontFamilyJson,
      fontTexture: robotoFontTexture,
      backgroundOpacity: 1,
      backgroundColor: new THREE.Color(0x779092),
      fontColor: new THREE.Color(0x000000),
    },
  ];

  return (
    <Interactive
      // These are for XR mode
      onSelect={() => {
        onClick();
      }}
      onHover={() => button.current.setState('hovered')}
      onBlur={() => button.current.setState('idle')}
      onSelectStart={() => button.current.setState('selected')}
      onSelectEnd={() => button.current.setState('idle')}>
      <block
        // These are for non-XR modes
        onPointerEnter={() => button.current.setState('hovered')}
        onPointerLeave={() => button.current.setState('idle')}
        onPointerDown={() => button.current.setState('selected')}
        onPointerUp={() => {
          button.current.setState('hovered');
          onClick();
        }}>
        <block args={args} ref={button}>
          <ThreeMeshUIText
            ref={textRef}
            fontColor={new THREE.Color(0xffffff)}
            content={content}
          />
        </block>
      </block>
    </Interactive>
  );
}