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} title="" /> 37 <CALLOUT css={labelStyle}>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 padding: `0 ${spacing[3]}px`, 50 borderColor: theme.border.default, 51 boxShadow: shadows.xs, 52 marginBottom: spacing[2.5], 53 54 '&:focus': { 55 boxShadow: shadows.xs, 56 }, 57 58 '> div': { 59 width: '100%', 60 gap: spacing[2.5], 61 }, 62 63 kbd: { 64 height: 20, 65 lineHeight: '19px', 66 }, 67}); 68 69const labelStyle = css({ 70 color: theme.icon.secondary, 71}); 72 73const keysWrapperStyle = css({ 74 marginLeft: 'auto', 75}); 76 77const hideOnMobileStyle = css({ 78 [`@media screen and (max-width: ${breakpoints.medium}px)`]: { 79 display: 'none', 80 }, 81}); 82