import * as React from "react" ; import { ReactComponentElement , useEffect , useRef , useState } from "react" ; import { motion } from "framer-motion" ; import { Direction , Focusable , useFocusable , useSunbeam } from "react-sunbeam" ; import { Colors , Typography } from "../../styles" ; export function Picker ( { label , children , onPick , } ) { const [ open , setOpen ] = useState ( false ) ; const clonedChildren = React . Children . map ( children , ( option ) => { return React . cloneElement ( option , { focusable : open , onPick : ( value ) => { onPick ( value ) ; setOpen ( false ) ; } , focusKey : option . props . focusKey ?? `PickerOption:${ option . props . value }` , } ) ; } ) ; const clonedChildrenArray = React . Children . toArray ( clonedChildren ) as ReactComponentElement < typeof PickerOption > [ ] ; const selectedOption = clonedChildrenArray . find ( ( option ) => option . props . selected ) ! ; const focusKeyByValue = clonedChildrenArray . reduce ( ( result , option ) => { result [ option . props . value ] = option . props . focusKey ! ; return result ; } , { } as Record < string , string > ) ; return ( < Focusable lock = { open ? [ Direction . UP , Direction . DOWN ] : undefined } onKeyPress = { ( event ) => { if ( event . key === "Enter" || event . key === " " ) { event . stopPropagation ( ) ; setOpen ( true ) ; } if ( ( event . key === "Backspace" || event . key === "Escape" ) && open ) { event . stopPropagation ( ) ; setOpen ( false ) ; } } } > { ( { focused , path } ) => { const { setFocus } = useSunbeam ( ) ; const prevOpen = usePrevious ( open , open ) ; useEffect ( ( ) => { if ( ! open || prevOpen === open ) return ; setFocus ( path . concat ( focusKeyByValue [ selectedOption . props . value ] ) ) ; } ) ; return ( < div style = { { position : "relative" , display : "flex" , justifyContent : "space-between" , padding : "10px" , marginBottom : "8px" , maxWidth : "450px" , ... Typography . bodyText , } } > { } < motion . div animate = { { opacity : focused ? 1 : 0 } } transition = { { ease : "easeOut" , duration : 0.15 } } style = { { position : "absolute" , top : - 2 , left : - 2 , width : "100%" , height : "100%" , border : `2px solid ${ Colors . textBlack }` , zIndex : - 1 , } } /> { } < div style = { { position : "absolute" , top : - 2 , left : - 2 , width : "calc(100% + 4px)" , height : "calc(100% + 4px)" , background : Colors . background , zIndex : - 2 , } } /> { } < motion . div animate = { { x : focused ? 0 : - 4 , y : focused ? 0 : - 4 , opacity : focused ? 1 : 0 , } } transition = { { ease : "easeOut" , duration : 0.15 } } style = { { position : "absolute" , top : 0 , left : 0 , width : "100%" , height : "100%" , boxShadow : `6px 6px 0px 0px ${ Colors . textBlack }` , zIndex : - 3 , } } /> < div > { label } < /div> < div style = { { display : "flex" , flexDirection : "column" , alignItems : "flex-end" } } > { open ? clonedChildren : selectedOption } < /div> < /div>); } } < /Focusable>); } export function PickerOption ( { label , value , focusKey , focusable , onPick , } ) { const ref = useRef ( null ) ; const { focused } = useFocusable ( { focusable , elementRef : ref , focusKey , onKeyPress ( event ) { if ( event . key === "Enter" || event . key === " " ) { event . stopPropagation ( ) ; if ( onPick ) onPick ( value ) ; } } , } ) ; return ( < div ref = { ref } style = { { fontWeight : focused ? 700 : 500 } } > { label } < /div>); } function usePrevious < T > ( value , initialValue ) { const ref = useRef < T > ( initialValue ) ; useEffect ( ( ) => { ref . current = value ; } , [ value ] ) ; return ref . current ; }