Skip to content

Instantly share code, notes, and snippets.

@yarastqt
Created July 26, 2021 10:34
Show Gist options
  • Select an option

  • Save yarastqt/73e3ba9fc01ced316e4e536943bccd3c to your computer and use it in GitHub Desktop.

Select an option

Save yarastqt/73e3ba9fc01ced316e4e536943bccd3c to your computer and use it in GitHub Desktop.
import { useState } from 'react'
import { useUniqId } from '../../libs/useUniqId'
export function useSelectState(props: any) {
const { children } = props
const [isOpened, setOpened] = useState(false)
const [selected, setSelected] = useState<{ option: any; value: any }>({} as any)
const [focusedIndex, setFocusedIndex] = useState(-1)
const [selectedIndex, setSelectedIndex] = useState(-1)
const total = children.length - 1
const $collection = children.map((child: any, index: any) => {
return {
key: index,
index,
value: child.props.value,
children: child.props.children,
}
})
return {
collection: $collection,
isOpened,
selected,
toggleOpened: () => {
setOpened(!isOpened)
},
setOpened: () => {
setOpened(true)
setFocusedIndex(selectedIndex > 0 ? selectedIndex : 0)
},
setClosed: () => {
setOpened(false)
setFocusedIndex(-1)
},
focusNext: () => {
const next = focusedIndex + 1
setFocusedIndex(next > total ? 0 : next)
},
focusPrev: () => {
const next = focusedIndex - 1
setFocusedIndex(next < 0 ? total : next)
},
select: (index?: number) => {
if (index !== undefined) {
setSelectedIndex(index)
setFocusedIndex(index)
} else {
setSelectedIndex(focusedIndex)
}
const target = $collection[index ?? focusedIndex]
setSelected({
value: target.value,
option: target.children,
})
setOpened(false)
},
isFocused: (index: number) => {
return focusedIndex === index
},
isSelected: (index: number) => {
return selectedIndex === index
},
}
}
export function useSelect(props: any, state: any) {
const uniqId = useUniqId()
return {
triggerProps: {
'aria-haspopup': 'listbox',
'aria-expanded': state.isOpened,
'aria-controls': state.isOpened ? uniqId : undefined,
onPressStart: () => {
if (state.isOpened) {
state.setClosed()
} else {
state.setOpened()
}
},
onKeyDown: (event: any) => {
if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
state.setOpened()
}
},
},
menuProps: {
id: uniqId,
state,
role: 'listbox',
// autoFocus: true,
onBlur: (event: any) => {
if (!event.currentTarget.contains(event.relatedTarget)) {
event.preventDefault()
state.setClosed()
}
},
},
}
}
@yarastqt

Copy link
Copy Markdown
Author

Select.tsx

import { useSelectState, useSelect } from 'react-web-sdk'
import { Button, ArrowShortBottom, ArrowShortTop, Popup, MenuBase } from 'ui-kit'

export const Select = (props) => {
  const state = useSelectState(props)
  const { triggerProps, menuProps } = useSelect(props, state)
  const arrowIcon = state.isOpened ? <ArrowShortTop /> : <ArrowShortBottom />

  return (
    <>
      <Button {...triggerProps} addonAfter={arrowIcon}>
        {state.selected.option ?? 'Select option'}
      </Button>
      <Popup visible={state.isOpened}>
        <MenuBase {...menuProps} />
      </Popup>
    </>
  )
}

@yarastqt

Copy link
Copy Markdown
Author

Menu.tsx

import { forwardRef, useRef } from 'react'
import { useMenu, useMenuItem, useHover, useForkRef } from 'react-web-sdk'
import { Check } from 'ui-kit'

import './Menu.css'

export const MenuBase = forwardRef((props, ref) => {
  const { state } = props
  const menuRef = useRef(null)
  const { menuProps } = useMenu(props, state, ref)
  const forkedRef = useForkRef(menuRef, ref)

  return (
    <ul {...menuProps} ref={forkedRef} className="Menu">
      {state.collection.map((item) => (
        <MenuItem {...item} state={state} />
      ))}
    </ul>
  )
})

const MenuItem = (props) => {
  const { children, state } = props
  const ref = useRef(null)
  const { itemProps, isFocused, isSelected } = useMenuItem(props, state, ref)
  const { isHovered, hoverProps } = useHover(props)

  return (
    <li
      {...itemProps}
      {...hoverProps}
      ref={ref}
      className="Menu-Item"
      data-selected={isSelected}
      data-focused={isFocused}
      data-hovered={isHovered}
    >
      {children}
      {isSelected && <Check />}
    </li>
  )
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment