|
|
@ -4,13 +4,16 @@ import React, { PureComponent } from 'react' |
|
|
|
import Downshift from 'downshift' |
|
|
|
import styled from 'styled-components' |
|
|
|
import { space } from 'styled-system' |
|
|
|
import get from 'lodash/get' |
|
|
|
|
|
|
|
import type { Element } from 'react' |
|
|
|
|
|
|
|
import Box from 'components/base/Box' |
|
|
|
import Text from 'components/base/Text' |
|
|
|
import GrowScroll from 'components/base/GrowScroll' |
|
|
|
import Icon from 'components/base/Icon' |
|
|
|
import Input from 'components/base/Input' |
|
|
|
import Search from 'components/base/Search' |
|
|
|
import Text from 'components/base/Text' |
|
|
|
|
|
|
|
import Triangles from './Triangles' |
|
|
|
|
|
|
@ -49,6 +52,7 @@ const TriggerBtn = styled(Box).attrs({ |
|
|
|
` |
|
|
|
|
|
|
|
const Item = styled(Box).attrs({ |
|
|
|
align: 'center', |
|
|
|
p: 2, |
|
|
|
})` |
|
|
|
background: ${p => (p.highlighted ? p.theme.colors.cream : p.theme.colors.white)}; |
|
|
@ -68,8 +72,6 @@ const Dropdown = styled(Box).attrs({ |
|
|
|
left: 0; |
|
|
|
right: 0; |
|
|
|
border: 1px solid ${p => p.theme.colors.mouse}; |
|
|
|
max-height: 300px; |
|
|
|
overflow-y: auto; |
|
|
|
border-radius: 3px; |
|
|
|
box-shadow: rgba(0, 0, 0, 0.05) 0 2px 2px; |
|
|
|
` |
|
|
@ -89,26 +91,83 @@ const FloatingTriangles = styled(Box).attrs({ |
|
|
|
padding-right: 1px; |
|
|
|
` |
|
|
|
|
|
|
|
const IconSelected = styled(Box).attrs({ |
|
|
|
bg: 'blue', |
|
|
|
color: 'white', |
|
|
|
align: 'center', |
|
|
|
justify: 'center', |
|
|
|
})` |
|
|
|
border-radius: 50%; |
|
|
|
height: 15px; |
|
|
|
font-size: 5px; |
|
|
|
width: 15px; |
|
|
|
opacity: ${p => (p.selected ? 1 : 0)}; |
|
|
|
|
|
|
|
// add top for center icon
|
|
|
|
> * { |
|
|
|
top: 1px; |
|
|
|
} |
|
|
|
` |
|
|
|
|
|
|
|
class Select extends PureComponent<Props> { |
|
|
|
static defaultProps = { |
|
|
|
itemToString: (item: Object) => item && item.name, |
|
|
|
keyProp: undefined, |
|
|
|
} |
|
|
|
|
|
|
|
renderItems = (items: Array<Object>, downshiftProps: Object) => { |
|
|
|
_scrollToSelectedItem = true |
|
|
|
_useKeyboard = false |
|
|
|
|
|
|
|
renderItems = (items: Array<Object>, selectedItem: any, downshiftProps: Object) => { |
|
|
|
const { renderItem, keyProp } = this.props |
|
|
|
const { getItemProps, highlightedIndex } = downshiftProps |
|
|
|
|
|
|
|
const selectedItemIndex = items.indexOf(selectedItem) |
|
|
|
|
|
|
|
return ( |
|
|
|
<Dropdown> |
|
|
|
{items.length ? ( |
|
|
|
items.map((item, i) => ( |
|
|
|
<ItemWrapper key={keyProp ? item[keyProp] : item.key} {...getItemProps({ item })}> |
|
|
|
<Item highlighted={i === highlightedIndex}> |
|
|
|
{renderItem ? renderItem(item) : <span>{item.name_highlight || item.name}</span>} |
|
|
|
</Item> |
|
|
|
</ItemWrapper> |
|
|
|
)) |
|
|
|
<GrowScroll |
|
|
|
maxHeight={300} |
|
|
|
onUpdate={scrollbar => { |
|
|
|
const { contentEl } = scrollbar |
|
|
|
const children = get(contentEl, 'children[0].children[0].children', {}) |
|
|
|
|
|
|
|
const currentHighlighted = children[highlightedIndex] |
|
|
|
const currentSelectedItem = children[selectedItemIndex] |
|
|
|
|
|
|
|
if (this._useKeyboard && currentHighlighted) { |
|
|
|
scrollbar.scrollIntoView(currentHighlighted, { |
|
|
|
alignToTop: false, |
|
|
|
}) |
|
|
|
} else if (this._scrollToSelectedItem && currentSelectedItem) { |
|
|
|
scrollbar.scrollIntoView(currentSelectedItem, { |
|
|
|
alignToTop: false, |
|
|
|
}) |
|
|
|
|
|
|
|
this._scrollToSelectedItem = false |
|
|
|
} |
|
|
|
}} |
|
|
|
> |
|
|
|
{items.map((item, i) => ( |
|
|
|
<ItemWrapper key={keyProp ? item[keyProp] : item.key} {...getItemProps({ item })}> |
|
|
|
<Item highlighted={i === highlightedIndex} horizontal flow={10}> |
|
|
|
<Box grow> |
|
|
|
{renderItem ? ( |
|
|
|
renderItem(item) |
|
|
|
) : ( |
|
|
|
<span>{item.name_highlight || item.name}</span> |
|
|
|
)} |
|
|
|
</Box> |
|
|
|
<Box> |
|
|
|
<IconSelected selected={selectedItem === item}> |
|
|
|
<Icon name="check" /> |
|
|
|
</IconSelected> |
|
|
|
</Box> |
|
|
|
</Item> |
|
|
|
</ItemWrapper> |
|
|
|
))} |
|
|
|
</GrowScroll> |
|
|
|
) : ( |
|
|
|
<ItemWrapper> |
|
|
|
<Item>{'No results'}</Item> |
|
|
@ -147,42 +206,53 @@ class Select extends PureComponent<Props> { |
|
|
|
openMenu, |
|
|
|
selectedItem, |
|
|
|
...downshiftProps |
|
|
|
}) => ( |
|
|
|
<Container {...getRootProps({ refKey: 'innerRef' })} {...props}> |
|
|
|
{searchable ? ( |
|
|
|
<Box relative> |
|
|
|
<Input keepEvent {...getInputProps({ placeholder })} onClick={openMenu} /> |
|
|
|
<FloatingTriangles> |
|
|
|
<Triangles /> |
|
|
|
</FloatingTriangles> |
|
|
|
</Box> |
|
|
|
) : ( |
|
|
|
<TriggerBtn {...getButtonProps()} tabIndex={0} horizontal align="center" flow={2}> |
|
|
|
<Box grow> |
|
|
|
{selectedItem && renderSelected ? ( |
|
|
|
renderSelected(selectedItem) |
|
|
|
) : ( |
|
|
|
<Text color="mouse">{placeholder}</Text> |
|
|
|
)} |
|
|
|
}) => { |
|
|
|
if (!isOpen) { |
|
|
|
this._scrollToSelectedItem = true |
|
|
|
} |
|
|
|
|
|
|
|
return ( |
|
|
|
<Container |
|
|
|
{...getRootProps({ refKey: 'innerRef' })} |
|
|
|
{...props} |
|
|
|
onKeyDown={() => (this._useKeyboard = true)} |
|
|
|
onKeyUp={() => (this._useKeyboard = false)} |
|
|
|
> |
|
|
|
{searchable ? ( |
|
|
|
<Box relative> |
|
|
|
<Input keepEvent {...getInputProps({ placeholder })} onClick={openMenu} /> |
|
|
|
<FloatingTriangles> |
|
|
|
<Triangles /> |
|
|
|
</FloatingTriangles> |
|
|
|
</Box> |
|
|
|
<Triangles /> |
|
|
|
</TriggerBtn> |
|
|
|
)} |
|
|
|
{isOpen && |
|
|
|
(searchable ? ( |
|
|
|
<Search |
|
|
|
value={inputValue} |
|
|
|
items={items} |
|
|
|
fuseOptions={fuseOptions} |
|
|
|
highlight={highlight} |
|
|
|
renderHighlight={renderHighlight} |
|
|
|
render={items => this.renderItems(items, downshiftProps)} |
|
|
|
/> |
|
|
|
) : ( |
|
|
|
this.renderItems(items, downshiftProps) |
|
|
|
))} |
|
|
|
</Container> |
|
|
|
)} |
|
|
|
<TriggerBtn {...getButtonProps()} tabIndex={0} horizontal align="center" flow={2}> |
|
|
|
<Box grow> |
|
|
|
{selectedItem && renderSelected ? ( |
|
|
|
renderSelected(selectedItem) |
|
|
|
) : ( |
|
|
|
<Text color="mouse">{placeholder}</Text> |
|
|
|
)} |
|
|
|
</Box> |
|
|
|
<Triangles /> |
|
|
|
</TriggerBtn> |
|
|
|
)} |
|
|
|
{isOpen && |
|
|
|
(searchable ? ( |
|
|
|
<Search |
|
|
|
value={inputValue} |
|
|
|
items={items} |
|
|
|
fuseOptions={fuseOptions} |
|
|
|
highlight={highlight} |
|
|
|
renderHighlight={renderHighlight} |
|
|
|
render={items => this.renderItems(items, selectedItem, downshiftProps)} |
|
|
|
/> |
|
|
|
) : ( |
|
|
|
this.renderItems(items, selectedItem, downshiftProps) |
|
|
|
))} |
|
|
|
</Container> |
|
|
|
) |
|
|
|
}} |
|
|
|
/> |
|
|
|
) |
|
|
|
} |
|
|
|