1import { css } from '@emotion/react'; 2import { breakpoints, iconSize, SearchIcon, shadows, spacing, theme } from '@expo/styleguide'; 3import { Dispatch, SetStateAction, useEffect, useState } from 'react'; 4 5import { isAppleDevice } from './utils'; 6 7import { Button } from '~/ui/components/Button'; 8import { CALLOUT, KBD } from '~/ui/components/Text'; 9 10type Props = { 11 setOpen: Dispatch<SetStateAction<boolean>>; 12}; 13 14export const CommandMenuTrigger = ({ setOpen }: Props) => { 15 const [isMac, setIsMac] = useState<boolean | null>(null); 16 17 useEffect(() => { 18 setIsMac(typeof navigator !== 'undefined' && isAppleDevice()); 19 }, []); 20 21 useEffect(() => { 22 if (isMac !== null) { 23 const keyDownListener = (e: KeyboardEvent) => { 24 if (e.key === 'k' && (isMac ? e.metaKey : e.ctrlKey)) { 25 e.preventDefault(); 26 setOpen(open => !open); 27 } 28 }; 29 document.addEventListener('keydown', keyDownListener, false); 30 return () => document.removeEventListener('keydown', keyDownListener); 31 } 32 }, [isMac]); 33 34 return ( 35 <Button theme="ghost" css={buttonStyle} onClick={() => setOpen(true)}> 36 <SearchIcon size={iconSize.sm} /> 37 <CALLOUT css={[labelStyle, hideOnMobileStyle]}>Search</CALLOUT> 38 {isMac !== null && ( 39 <div css={[keysWrapperStyle, hideOnMobileStyle]}> 40 <KBD>{isMac ? '⌘' : 'Ctrl'}</KBD> <KBD>K</KBD> 41 </div> 42 )} 43 </Button> 44 ); 45}; 46 47const buttonStyle = css({ 48 backgroundColor: theme.background.default, 49 width: '20vw', 50 minWidth: 240, 51 maxWidth: 320, 52 padding: `0 ${spacing[3]}px`, 53 borderColor: theme.border.default, 54 boxShadow: shadows.xs, 55 marginRight: spacing[3], 56 57 '&:focus': { 58 boxShadow: shadows.xs, 59 }, 60 61 '> div': { 62 width: '100%', 63 gap: spacing[2.5], 64 }, 65 66 kbd: { 67 height: 20, 68 lineHeight: '19px', 69 }, 70 71 [`@media screen and (max-width: ${breakpoints.medium}px)`]: { 72 minWidth: 42, 73 width: 42, 74 marginRight: 0, 75 }, 76}); 77 78const labelStyle = css({ 79 color: theme.icon.secondary, 80}); 81 82const keysWrapperStyle = css({ 83 marginLeft: 'auto', 84}); 85 86const hideOnMobileStyle = css({ 87 [`@media screen and (max-width: ${breakpoints.medium}px)`]: { 88 display: 'none', 89 }, 90}); 91