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