@@ -2,33 +2,31 @@ import { ChevronDownIcon } from "@heroicons/react/16/solid";
2
2
import { AnimatePresence , motion } from "framer-motion" ;
3
3
import { useCallback , useEffect , useMemo , useRef , useState } from "react" ;
4
4
import Keyboard from "react-simple-keyboard" ;
5
+ import { LuKeyboard } from "react-icons/lu" ;
5
6
6
7
import Card from "@components/Card" ;
7
8
// eslint-disable-next-line import/order
8
- import { Button } from "@components/Button" ;
9
+ import { Button , LinkButton } from "@components/Button" ;
9
10
10
11
import "react-simple-keyboard/build/css/index.css" ;
11
12
12
- import AttachIconRaw from "@/assets/attach-icon.svg" ;
13
13
import DetachIconRaw from "@/assets/detach-icon.svg" ;
14
14
import { cx } from "@/cva.config" ;
15
15
import { useHidStore , useUiStore } from "@/hooks/stores" ;
16
16
import useKeyboard from "@/hooks/useKeyboard" ;
17
17
import useKeyboardLayout from "@/hooks/useKeyboardLayout" ;
18
- import { keys , modifiers , latchingKeys , decodeModifiers } from "@/keyboardMappings" ;
18
+ import { decodeModifiers , keys , latchingKeys , modifiers } from "@/keyboardMappings" ;
19
19
20
20
export const DetachIcon = ( { className } : { className ?: string } ) => {
21
21
return < img src = { DetachIconRaw } alt = "Detach Icon" className = { className } /> ;
22
22
} ;
23
23
24
- const AttachIcon = ( { className } : { className ?: string } ) => {
25
- return < img src = { AttachIconRaw } alt = "Attach Icon" className = { className } /> ;
26
- } ;
27
-
28
24
function KeyboardWrapper ( ) {
29
25
const keyboardRef = useRef < HTMLDivElement > ( null ) ;
30
- const { isAttachedVirtualKeyboardVisible, setAttachedVirtualKeyboardVisibility } = useUiStore ( ) ;
31
- const { keysDownState, /* keyboardLedState,*/ isVirtualKeyboardEnabled, setVirtualKeyboardEnabled } = useHidStore ( ) ;
26
+ const { isAttachedVirtualKeyboardVisible, setAttachedVirtualKeyboardVisibility } =
27
+ useUiStore ( ) ;
28
+ const { keysDownState, isVirtualKeyboardEnabled, setVirtualKeyboardEnabled } =
29
+ useHidStore ( ) ;
32
30
const { handleKeyPress, executeMacro } = useKeyboard ( ) ;
33
31
const { selectedKeyboard } = useKeyboardLayout ( ) ;
34
32
@@ -44,29 +42,28 @@ function KeyboardWrapper() {
44
42
return selectedKeyboard . virtualKeyboard ;
45
43
} , [ selectedKeyboard ] ) ;
46
44
47
- //const isCapsLockActive = useMemo(() => {
48
- // return (keyboardLedState.caps_lock);
49
- //}, [keyboardLedState]);
50
-
51
- const { isShiftActive, /*isControlActive, isAltActive, isMetaActive, isAltGrActive*/ } = useMemo ( ( ) => {
45
+ const { isShiftActive } = useMemo ( ( ) => {
52
46
return decodeModifiers ( keysDownState . modifier ) ;
53
47
} , [ keysDownState ] ) ;
54
48
55
49
const mainLayoutName = useMemo ( ( ) => {
56
- const layoutName = isShiftActive ? "shift" : "default" ;
57
- return layoutName ;
50
+ return isShiftActive ? "shift" : "default" ;
58
51
} , [ isShiftActive ] ) ;
59
52
60
53
const keyNamesForDownKeys = useMemo ( ( ) => {
61
54
const activeModifierMask = keysDownState . modifier || 0 ;
62
- const modifierNames = Object . entries ( modifiers ) . filter ( ( [ _ , mask ] ) => ( activeModifierMask & mask ) !== 0 ) . map ( ( [ name , _ ] ) => name ) ;
55
+ const modifierNames = Object . entries ( modifiers )
56
+ . filter ( ( [ _ , mask ] ) => ( activeModifierMask & mask ) !== 0 )
57
+ . map ( ( [ name , _ ] ) => name ) ;
63
58
64
59
const keysDown = keysDownState . keys || [ ] ;
65
- const keyNames = Object . entries ( keys ) . filter ( ( [ _ , value ] ) => keysDown . includes ( value ) ) . map ( ( [ name , _ ] ) => name ) ;
60
+ const keyNames = Object . entries ( keys )
61
+ . filter ( ( [ _ , value ] ) => keysDown . includes ( value ) )
62
+ . map ( ( [ name , _ ] ) => name ) ;
66
63
67
- return [ ...modifierNames , ...keyNames , ' ' ] ; // we have to have at least one space to avoid keyboard whining
64
+ return [ ...modifierNames , ...keyNames , " " ] ; // we have to have at least one space to avoid keyboard whining
68
65
} , [ keysDownState ] ) ;
69
-
66
+
70
67
const startDrag = useCallback ( ( e : MouseEvent | TouchEvent ) => {
71
68
if ( ! keyboardRef . current ) return ;
72
69
if ( e instanceof TouchEvent && e . touches . length > 1 ) return ;
@@ -110,6 +107,9 @@ function KeyboardWrapper() {
110
107
} , [ ] ) ;
111
108
112
109
useEffect ( ( ) => {
110
+ // Is the keyboard detached or attached?
111
+ if ( isAttachedVirtualKeyboardVisible ) return ;
112
+
113
113
const handle = keyboardRef . current ;
114
114
if ( handle ) {
115
115
handle . addEventListener ( "touchstart" , startDrag ) ;
@@ -134,15 +134,12 @@ function KeyboardWrapper() {
134
134
document . removeEventListener ( "mousemove" , onDrag ) ;
135
135
document . removeEventListener ( "touchmove" , onDrag ) ;
136
136
} ;
137
- } , [ endDrag , onDrag , startDrag ] ) ;
137
+ } , [ isAttachedVirtualKeyboardVisible , endDrag , onDrag , startDrag ] ) ;
138
138
139
- const onKeyUp = useCallback (
140
- async ( _ : string , e : MouseEvent | undefined ) => {
141
- e ?. preventDefault ( ) ;
142
- e ?. stopPropagation ( ) ;
143
- } ,
144
- [ ]
145
- ) ;
139
+ const onKeyUp = useCallback ( async ( _ : string , e : MouseEvent | undefined ) => {
140
+ e ?. preventDefault ( ) ;
141
+ e ?. stopPropagation ( ) ;
142
+ } , [ ] ) ;
146
143
147
144
const onKeyDown = useCallback (
148
145
async ( key : string , e : MouseEvent | undefined ) => {
@@ -151,33 +148,41 @@ function KeyboardWrapper() {
151
148
152
149
// handle the fake key-macros we have defined for common combinations
153
150
if ( key === "CtrlAltDelete" ) {
154
- await executeMacro ( [ { keys : [ "Delete" ] , modifiers : [ "ControlLeft" , "AltLeft" ] , delay : 100 } ] ) ;
151
+ await executeMacro ( [
152
+ { keys : [ "Delete" ] , modifiers : [ "ControlLeft" , "AltLeft" ] , delay : 100 } ,
153
+ ] ) ;
155
154
return ;
156
155
}
157
156
158
157
if ( key === "AltMetaEscape" ) {
159
- await executeMacro ( [ { keys : [ "Escape" ] , modifiers : [ "AltLeft" , "MetaLeft" ] , delay : 100 } ] ) ;
158
+ await executeMacro ( [
159
+ { keys : [ "Escape" ] , modifiers : [ "AltLeft" , "MetaLeft" ] , delay : 100 } ,
160
+ ] ) ;
160
161
return ;
161
162
}
162
163
163
164
if ( key === "CtrlAltBackspace" ) {
164
- await executeMacro ( [ { keys : [ "Backspace" ] , modifiers : [ "ControlLeft" , "AltLeft" ] , delay : 100 } ] ) ;
165
+ await executeMacro ( [
166
+ { keys : [ "Backspace" ] , modifiers : [ "ControlLeft" , "AltLeft" ] , delay : 100 } ,
167
+ ] ) ;
165
168
return ;
166
169
}
167
170
168
171
// if they press any of the latching keys, we send a keypress down event and the release it automatically (on timer)
169
172
if ( latchingKeys . includes ( key ) ) {
170
173
console . debug ( `Latching key pressed: ${ key } sending down and delayed up pair` ) ;
171
- handleKeyPress ( keys [ key ] , true )
174
+ handleKeyPress ( keys [ key ] , true ) ;
172
175
setTimeout ( ( ) => handleKeyPress ( keys [ key ] , false ) , 100 ) ;
173
176
return ;
174
177
}
175
178
176
179
// if they press any of the dynamic keys, we send a keypress down event but we don't release it until they click it again
177
180
if ( Object . keys ( modifiers ) . includes ( key ) ) {
178
181
const currentlyDown = keyNamesForDownKeys . includes ( key ) ;
179
- console . debug ( `Dynamic key pressed: ${ key } was currently down: ${ currentlyDown } , toggling state` ) ;
180
- handleKeyPress ( keys [ key ] , ! currentlyDown )
182
+ console . debug (
183
+ `Dynamic key pressed: ${ key } was currently down: ${ currentlyDown } , toggling state` ,
184
+ ) ;
185
+ handleKeyPress ( keys [ key ] , ! currentlyDown ) ;
181
186
return ;
182
187
}
183
188
@@ -211,7 +216,7 @@ function KeyboardWrapper() {
211
216
< div
212
217
className = { cx (
213
218
! isAttachedVirtualKeyboardVisible
214
- ? "fixed left -0 top -0 z-50 select-none"
219
+ ? "fixed top -0 left -0 z-10 select-none"
215
220
: "relative" ,
216
221
) }
217
222
ref = { keyboardRef }
@@ -224,9 +229,10 @@ function KeyboardWrapper() {
224
229
< Card
225
230
className = { cx ( "overflow-hidden" , {
226
231
"rounded-none" : isAttachedVirtualKeyboardVisible ,
232
+ "keyboard-detached" : ! isAttachedVirtualKeyboardVisible ,
227
233
} ) }
228
234
>
229
- < div className = "flex items-center justify-center border-b border-b-slate-800/30 bg-white px-2 py-1 dark:border-b-slate-300/20 dark:bg-slate-800" >
235
+ < div className = "flex items-center justify-center border-b border-b-slate-800/30 bg-white px-2 py-4 dark:border-b-slate-300/20 dark:bg-slate-800" >
230
236
< div className = "absolute left-2 flex items-center gap-x-2" >
231
237
{ isAttachedVirtualKeyboardVisible ? (
232
238
< Button
@@ -240,15 +246,25 @@ function KeyboardWrapper() {
240
246
size = "XS"
241
247
theme = "light"
242
248
text = "Attach"
243
- LeadingIcon = { AttachIcon }
244
249
onClick = { ( ) => setAttachedVirtualKeyboardVisibility ( true ) }
245
250
/>
246
251
) }
247
252
</ div >
248
- < h2 className = "select-none self-center font-sans text-[12px] text-slate-700 dark:text-slate-300" >
253
+ < h2 className = "self-center font-sans text-sm leading-none font-medium text-slate-700 select-none dark:text-slate-300" >
249
254
Virtual Keyboard
250
255
</ h2 >
251
- < div className = "absolute right-2" >
256
+ < div className = "absolute right-2 flex items-center gap-x-2" >
257
+ < div className = "hidden md:flex gap-x-2 items-center" >
258
+ < LinkButton
259
+ size = "XS"
260
+ to = "settings/keyboard"
261
+ theme = "light"
262
+ text = { selectedKeyboard . name }
263
+ LeadingIcon = { LuKeyboard }
264
+ />
265
+ < div className = "h-[20px] w-px bg-slate-800/20 dark:bg-slate-200/20" />
266
+ </ div >
267
+
252
268
< Button
253
269
size = "XS"
254
270
theme = "light"
@@ -317,7 +333,7 @@ function KeyboardWrapper() {
317
333
stopMouseUpPropagation = { true }
318
334
/>
319
335
</ div >
320
- { /* TODO add optional number pad */ }
336
+ { /* TODO add optional number pad */ }
321
337
</ div >
322
338
</ div >
323
339
</ Card >
0 commit comments