Browse Source

Merge pull request #417 from meriadec/react-select

Rewrite Select component
master
Gaëtan Renaudeau 7 years ago
committed by GitHub
parent
commit
773291c7f4
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      package.json
  2. 3
      src/components/BalanceSummary/stories.js
  3. 2
      src/components/FeesField/BitcoinKind.js
  4. 51
      src/components/SelectAccount/index.js
  5. 62
      src/components/SelectCurrency/index.js
  6. 2
      src/components/SelectExchange.js
  7. 31
      src/components/SettingsPage/sections/Display.js
  8. 2
      src/components/base/InputCurrency/index.js
  9. 343
      src/components/base/LegacySelect/index.js
  10. 117
      src/components/base/LegacySelect/stories.js
  11. 71
      src/components/base/Select/createRenderers.js
  12. 65
      src/components/base/Select/createStyles.js
  13. 0
      src/components/base/Select/customRenders.js
  14. 380
      src/components/base/Select/index.js
  15. 127
      src/components/base/Select/stories.js
  16. 62
      src/components/base/Select/style.js
  17. 2
      src/components/modals/AccountSettingRenderBody.js
  18. 18
      src/styles/global.js
  19. 108
      yarn.lock

1
package.json

@ -78,6 +78,7 @@
"react-router": "^4.2.0",
"react-router-dom": "^4.2.2",
"react-router-redux": "5.0.0-alpha.9",
"react-select": "2.0.0-beta.6",
"react-smooth-scrollbar": "^8.0.6",
"react-spring": "^4.2.1",
"redux": "^4.0.0",

3
src/components/BalanceSummary/stories.js

@ -4,6 +4,7 @@ import React from 'react'
import { storiesOf } from '@storybook/react'
import { number } from '@storybook/addon-knobs'
import { translate } from 'react-i18next'
import { getFiatCurrencyByTicker } from '@ledgerhq/live-common/lib/helpers/currencies'
import BalanceInfos from './BalanceInfos'
@ -14,7 +15,7 @@ const BalanceInfosComp = translate()(BalanceInfos)
stories.add('BalanceInfos', () => (
<BalanceInfosComp
since="month"
counterValue="USD"
counterValue={getFiatCurrencyByTicker('USD')}
totalBalance={number('totalBalance', 1000, { min: 0 })}
sinceBalance={number('sinceBalance', 500, { min: 0 })}
refBalance={number('refBalance', 200, { min: 0 })}

2
src/components/FeesField/BitcoinKind.js

@ -5,7 +5,7 @@ import type { Account } from '@ledgerhq/live-common/lib/types'
import styled from 'styled-components'
import InputCurrency from 'components/base/InputCurrency'
import Select from 'components/base/Select'
import Select from 'components/base/LegacySelect'
import type { Fees } from 'api/Fees'
import WithFeesAPI from '../WithFeesAPI'
import GenericContainer from './GenericContainer'

51
src/components/SelectAccount/index.js

@ -5,10 +5,9 @@ import { connect } from 'react-redux'
import { translate } from 'react-i18next'
import { getCryptoCurrencyIcon } from '@ledgerhq/live-common/lib/react'
import noop from 'lodash/noop'
import type { Account } from '@ledgerhq/live-common/lib/types'
import type { T } from 'types/common'
import type { Option } from 'components/base/Select'
import { getVisibleAccounts } from 'reducers/accounts'
@ -21,9 +20,10 @@ const mapStateToProps = state => ({
accounts: getVisibleAccounts(state),
})
const renderItem = a => {
const Icon = getCryptoCurrencyIcon(a.currency)
const { color } = a.currency
const renderOption = a => {
const { data: account } = a
const Icon = getCryptoCurrencyIcon(account.currency)
const { color } = account.currency
// FIXME: we need a non-hacky way to handle text ellipsis
const nameOuterStyle = { width: 0 }
@ -38,11 +38,11 @@ const renderItem = a => {
)}
<Box grow style={nameOuterStyle} ff="Open Sans|SemiBold" color="dark" fontSize={4}>
<Text style={nameInnerStyle} ff="Open Sans|SemiBold" color="dark" fontSize={4}>
{a.name}
{account.name}
</Text>
</Box>
<Box>
<FormattedVal color="grey" val={a.balance} unit={a.unit} showCode />
<FormattedVal color="grey" val={account.balance} unit={account.unit} showCode />
</Box>
</Box>
)
@ -50,28 +50,27 @@ const renderItem = a => {
type Props = {
accounts: Account[],
onChange?: () => Account | void,
value?: Account | null,
onChange: Option => void,
value: ?Account,
t: T,
}
const RawSelectAccount = ({ accounts, onChange, value, t, ...props }: Props) => (
<Select
{...props}
value={value && accounts.find(a => value && a.id === value.id)}
renderSelected={renderItem}
renderItem={renderItem}
keyProp="id"
items={accounts.sort((a, b) => (a.name < b.name ? -1 : 1))}
placeholder={t('common:selectAccount')}
fontSize={4}
onChange={onChange}
/>
)
RawSelectAccount.defaultProps = {
onChange: noop,
value: undefined,
const RawSelectAccount = ({ accounts, onChange, value, t, ...props }: Props) => {
const options = accounts
.sort((a, b) => (a.name < b.name ? -1 : 1))
.map(a => ({ ...a, value: a.id, label: a.name }))
const selectedOption = value ? options.find(o => o.value === value.id) : null
return (
<Select
{...props}
value={selectedOption}
options={options}
renderValue={renderOption}
renderOption={renderOption}
placeholder={t('common:selectAccount')}
onChange={onChange}
/>
)
}
export const SelectAccount = translate()(RawSelectAccount)

62
src/components/SelectCurrency/index.js

@ -4,32 +4,17 @@ import React from 'react'
import { translate } from 'react-i18next'
import { connect } from 'react-redux'
import noop from 'lodash/noop'
import type { CryptoCurrency } from '@ledgerhq/live-common/lib/types'
import type { T } from 'types/common'
import { availableCurrencies } from 'reducers/settings'
import type { Option } from 'components/base/Select'
import CryptoCurrencyIcon from 'components/CryptoCurrencyIcon'
import Select from 'components/base/Select'
import Box from 'components/base/Box'
const renderItem = (currency: CryptoCurrency) => {
const { color, name } = currency
return (
<Box grow horizontal alignItems="center" flow={2}>
<Box style={{ width: 16, height: 16, color }}>
<CryptoCurrencyIcon currency={currency} size={16} />
</Box>
<Box grow ff="Open Sans|SemiBold" color="dark" fontSize={4}>
{name}
</Box>
</Box>
)
}
type OwnProps = {
onChange: Function,
onChange: Option => void,
currencies?: CryptoCurrency[],
value?: CryptoCurrency,
placeholder: string,
@ -44,23 +29,34 @@ const mapStateToProps = (state, props: OwnProps) => ({
currencies: props.currencies || availableCurrencies(state),
})
const SelectCurrency = ({ onChange, value, t, placeholder, currencies, ...props }: Props) => (
<Select
{...props}
value={value}
renderSelected={renderItem}
renderItem={renderItem}
keyProp="id"
items={currencies}
placeholder={placeholder || t('common:selectCurrency')}
fontSize={4}
onChange={onChange}
/>
)
const SelectCurrency = ({ onChange, value, t, placeholder, currencies, ...props }: Props) => {
const options = currencies ? currencies.map(c => ({ ...c, value: c.id, label: c.name })) : []
return (
<Select
{...props}
value={value}
renderOption={renderOption}
renderValue={renderOption}
options={options}
placeholder={placeholder || t('common:selectCurrency')}
onChange={onChange}
/>
)
}
SelectCurrency.defaultProps = {
onChange: noop,
value: undefined,
const renderOption = (option: Option) => {
const { data: currency } = option
const { color, name } = currency
return (
<Box grow horizontal alignItems="center" flow={2}>
<Box style={{ width: 16, height: 16, color }}>
<CryptoCurrencyIcon currency={currency} size={16} />
</Box>
<Box grow ff="Open Sans|SemiBold" color="dark" fontSize={4}>
{name}
</Box>
</Box>
)
}
export default translate()(connect(mapStateToProps)(SelectCurrency))

2
src/components/SelectExchange.js

@ -2,7 +2,7 @@
import React, { Component } from 'react'
import type { Currency } from '@ledgerhq/live-common/lib/types'
import type { Exchange } from '@ledgerhq/live-common/lib/countervalues/types'
import Select from 'components/base/Select'
import Select from 'components/base/LegacySelect'
import Box from 'components/base/Box'
import Text from 'components/base/Text'
import CounterValues from 'helpers/countervalues'

31
src/components/SettingsPage/sections/Display.js

@ -23,16 +23,16 @@ import {
const regions = Object.keys(regionsByKey).map(key => {
const [language, region] = key.split('-')
return { key, language, region, name: regionsByKey[key] }
return { value: key, language, region, label: regionsByKey[key] }
})
const fiats = listFiatCurrencies()
.map(f => f.units[0])
// For now we take first unit, in the future we'll need to figure out something else
.map(fiat => ({
key: fiat.code,
value: fiat.code,
label: `${fiat.name} - ${fiat.code}${fiat.symbol ? ` (${fiat.symbol})` : ''}`,
fiat,
name: `${fiat.name} - ${fiat.code}${fiat.symbol ? ` (${fiat.symbol})` : ''}`,
}))
type Props = {
@ -79,7 +79,7 @@ class TabProfile extends PureComponent<Props, State> {
})
}
handleChangeLanguage = ({ key: languageKey }: *) => {
handleChangeLanguage = ({ value: languageKey }: *) => {
const { i18n, saveSettings } = this.props
this.setState({ cachedLanguageKey: languageKey })
window.requestIdleCallback(() => {
@ -117,12 +117,16 @@ class TabProfile extends PureComponent<Props, State> {
cachedRegion,
} = this.state
const languages = languageKeys.map(key => ({ key, name: t(`language:${key}`) }))
const currentLanguage = languages.find(l => l.key === cachedLanguageKey)
const languages = languageKeys.map(key => ({ value: key, label: t(`language:${key}`) }))
const currentLanguage = languages.find(l => l.value === cachedLanguageKey)
const regionsFiltered = regions.filter(({ language }) => cachedLanguageKey === language)
const currentRegion =
regionsFiltered.find(({ region }) => cachedRegion === region) || regionsFiltered[0]
const cvOption = cachedCounterValue
? fiats.find(f => f.value === cachedCounterValue.value)
: null
return (
<Section>
<Header
@ -136,33 +140,34 @@ class TabProfile extends PureComponent<Props, State> {
desc={t('settings:display.counterValueDesc')}
>
<Select
style={{ minWidth: 250 }}
small
minWidth={250}
onChange={this.handleChangeCounterValue}
itemToString={item => (item ? item.name : '')}
renderSelected={item => item && item.name}
items={fiats}
value={cachedCounterValue}
options={fiats}
value={cvOption}
/>
</Row>
<Row title={t('settings:display.language')} desc={t('settings:display.languageDesc')}>
<Select
style={{ minWidth: 250 }}
small
minWidth={250}
isSearchable={false}
onChange={this.handleChangeLanguage}
renderSelected={item => item && item.name}
value={currentLanguage}
items={languages}
options={languages}
/>
</Row>
<Row title={t('settings:display.region')} desc={t('settings:display.regionDesc')}>
<Select
style={{ minWidth: 250 }}
small
minWidth={250}
onChange={this.handleChangeRegion}
renderSelected={item => item && item.name}
value={currentRegion}
items={regionsFiltered}
options={regionsFiltered}
/>
</Row>
<Row title={t('settings:display.stock')} desc={t('settings:display.stockDesc')}>

2
src/components/base/InputCurrency/index.js

@ -10,7 +10,7 @@ import isNaN from 'lodash/isNaN'
import Box from 'components/base/Box'
import Input from 'components/base/Input'
import Select from 'components/base/Select'
import Select from 'components/base/LegacySelect'
import type { Unit } from '@ledgerhq/live-common/lib/types'

343
src/components/base/LegacySelect/index.js

@ -0,0 +1,343 @@
// @flow
import React, { PureComponent } from 'react'
import Downshift from 'downshift'
import styled from 'styled-components'
import { space } from 'styled-system'
import Box from 'components/base/Box'
import GrowScroll from 'components/base/GrowScroll'
import Input from 'components/base/Input'
import Search from 'components/base/Search'
import Text from 'components/base/Text'
import IconCheck from 'icons/Check'
type Props = {
bg?: string,
flatLeft?: boolean,
flatRight?: boolean,
fakeFocusRight?: boolean,
fuseOptions?: Object,
highlight?: boolean,
items: Array<any>,
itemToString?: Function,
keyProp?: string,
maxHeight?: number,
onChange?: Function,
placeholder?: string,
renderHighlight?: string => React$Node,
renderItem?: (*) => React$Node,
renderSelected?: any => React$Node,
searchable?: boolean,
value?: *,
disabled: boolean,
small?: boolean,
}
const Container = styled(Box).attrs({ relative: true, color: 'graphite' })``
const TriggerBtn = styled(Box).attrs({
alignItems: 'center',
ff: p => (p.small ? 'Open Sans' : 'Open Sans|SemiBold'),
flow: 2,
fontSize: p => (p.small ? 3 : 4),
horizontal: true,
px: 3,
})`
${space};
height: ${p => (p.small ? '34' : '40')}px;
background: ${p => (p.disabled ? p.theme.colors.lightGrey : p.bg || p.theme.colors.white)};
border-bottom-left-radius: ${p => (p.flatLeft ? 0 : p.theme.radii[1])}px;
border-bottom-right-radius: ${p => (p.flatRight ? 0 : p.theme.radii[1])}px;
border-top-left-radius: ${p => (p.flatLeft ? 0 : p.theme.radii[1])}px;
border-top-right-radius: ${p => (p.flatRight ? 0 : p.theme.radii[1])}px;
border: 1px solid ${p => p.theme.colors.fog};
color: ${p => p.theme.colors.graphite};
cursor: ${p => (p.disabled ? 'cursor' : 'pointer')};
display: flex;
width: 100%;
&:focus {
outline: none;
${p =>
p.disabled
? ''
: `
border-color: ${p.theme.colors.wallet};
box-shadow: rgba(0, 0, 0, 0.05) 0 2px 2px;`};
}
${p => {
const c = p.theme.colors.wallet
return p.fakeFocusRight
? `
border-top: 1px solid ${c};
border-right: 1px solid ${c};
border-bottom: 1px solid ${c};
`
: ''
}};
`
const Item = styled(Box).attrs({
alignItems: 'center',
fontSize: 4,
ff: p => `Open Sans|${p.selected ? 'SemiBold' : 'Regular'}`,
px: 3,
py: 2,
color: 'dark',
})`
background: ${p => (p.highlighted ? p.theme.colors.lightGrey : p.theme.colors.white)};
${p =>
p.first &&
`
border-top-left-radius: ${p.theme.radii[1]}px;
border-top-right-radius: ${p.theme.radii[1]}px;
`} ${p =>
p.last &&
`
border-bottom-left-radius: ${p.theme.radii[1]}px;
border-bottom-right-radius: ${p.theme.radii[1]}px;
`};
`
const Dropdown = styled(Box).attrs({
mt: 1,
})`
border-radius: ${p => p.theme.radii[1]}px;
border: 1px solid ${p => p.theme.colors.fog};
box-shadow: rgba(0, 0, 0, 0.05) 0 2px 2px;
left: 0;
position: absolute;
right: 0;
top: 100%;
z-index: 1;
`
const IconSelected = styled(Box).attrs({
color: 'wallet',
alignItems: 'center',
justifyContent: 'center',
})`
height: 12px;
width: 12px;
opacity: ${p => (p.selected ? 1 : 0)};
`
const AngleDown = props => (
<Box color="grey" alignItems="center" justifyContent="center" {...props}>
<svg viewBox="0 0 16 16" width="16" height="16">
<path
fill="currentColor"
d="M7.70785815 10.86875l-5.08670521-4.5875c-.16153725-.146875-.16153725-.384375 0-.53125l.68051867-.61875c.16153726-.146875.42274645-.146875.58428371 0L8 8.834375l4.1140447-3.703125c.1615372-.146875.4227464-.146875.5842837 0l.6805187.61875c.1615372.146875.1615372.384375 0 .53125l-5.08670525 4.5875c-.16153726.146875-.42274644.146875-.5842837 0z"
/>
</svg>
</Box>
)
const renderSelectedItem = ({ selectedItem, renderSelected, placeholder }: any) =>
selectedItem && renderSelected ? (
renderSelected(selectedItem)
) : (
<Text color="fog">{placeholder}</Text>
)
class LegacySelect extends PureComponent<Props> {
static defaultProps = {
bg: undefined,
disabled: false,
small: false,
fakeFocusRight: false,
flatLeft: false,
flatRight: false,
itemToString: (item: Object) => item && item.name,
keyProp: undefined,
maxHeight: 300,
}
_scrollToSelectedItem = true
_oldHighlightedIndex = 0
_useKeyboard = false
_children = {}
renderItems = (items: Array<Object>, selectedItem: any, downshiftProps: Object) => {
const { renderItem, maxHeight, keyProp } = this.props
const { getItemProps, highlightedIndex } = downshiftProps
const selectedItemIndex = items.indexOf(selectedItem)
return (
<Dropdown>
{items.length ? (
<GrowScroll
maxHeight={maxHeight}
onUpdate={scrollbar => {
const currentHighlighted = this._children[highlightedIndex]
const currentSelectedItem = this._children[selectedItemIndex]
if (this._useKeyboard && currentHighlighted) {
scrollbar.scrollIntoView(currentHighlighted, {
alignToTop: highlightedIndex < this._oldHighlightedIndex,
offsetTop: -1,
onlyScrollIfNeeded: true,
})
} else if (this._scrollToSelectedItem && currentSelectedItem) {
window.requestAnimationFrame(() =>
scrollbar.scrollIntoView(currentSelectedItem, {
offsetTop: -1,
}),
)
this._scrollToSelectedItem = false
}
this._oldHighlightedIndex = highlightedIndex
}}
>
{items.map((item, i) => (
<Box
key={keyProp ? item[keyProp] : item.key}
innerRef={n => (this._children[i] = n)}
{...getItemProps({ item })}
>
<Item
first={i === 0}
last={i === items.length - 1}
highlighted={i === highlightedIndex}
selected={selectedItem === item}
horizontal
flow={3}
>
<Box grow>
{renderItem ? (
renderItem(item)
) : (
<span>{item.name_highlight || item.name}</span>
)}
</Box>
<Box>
<IconSelected selected={selectedItem === item}>
<IconCheck size={12} />
</IconSelected>
</Box>
</Item>
</Box>
))}
</GrowScroll>
) : (
<Box>
<Item>{'No results'}</Item>
</Box>
)}
</Dropdown>
)
}
render() {
const {
disabled,
fakeFocusRight,
flatLeft,
flatRight,
fuseOptions,
highlight,
items,
itemToString,
onChange,
placeholder,
renderHighlight,
renderSelected,
searchable,
value,
small,
...props
} = this.props
return (
<Downshift
selectedItem={value}
itemToString={itemToString}
onChange={onChange}
render={({
getInputProps,
getToggleButtonProps,
getRootProps,
isOpen,
inputValue,
openMenu,
selectedItem,
...downshiftProps
}) => {
if (!isOpen) {
this._scrollToSelectedItem = true
}
if (disabled) {
return (
<Container {...getRootProps({ refKey: 'innerRef' })}>
<TriggerBtn disabled bg={props.bg} tabIndex={0} small={small}>
{renderSelectedItem({ selectedItem, renderSelected, placeholder })}
</TriggerBtn>
</Container>
)
}
return (
<Container
{...getRootProps({ refKey: 'innerRef' })}
{...props}
horizontal
onKeyDown={() => (this._useKeyboard = true)}
onKeyUp={() => (this._useKeyboard = false)}
>
{searchable ? (
<Box grow>
<Input
small
keepEvent
onClick={openMenu}
renderRight={<AngleDown mr={2} />}
{...getInputProps({ placeholder })}
/>
</Box>
) : (
<TriggerBtn
{...getToggleButtonProps()}
bg={props.bg}
fakeFocusRight={fakeFocusRight}
flatLeft={flatLeft}
flatRight={flatRight}
tabIndex={0}
small={small}
>
<Box grow>
{renderSelectedItem({ selectedItem, renderSelected, placeholder })}
</Box>
<AngleDown mr={-1} />
</TriggerBtn>
)}
<div hidden={!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)
)}
</div>
</Container>
)
}}
/>
)
}
}
export default LegacySelect

117
src/components/base/LegacySelect/stories.js

@ -0,0 +1,117 @@
// @flow
import React, { PureComponent } from 'react'
import { storiesOf } from '@storybook/react'
import { boolean } from '@storybook/addon-knobs'
import Box from 'components/base/Box'
import LegacySelect from 'components/base/LegacySelect'
import Text from 'components/base/Text'
const stories = storiesOf('Components/base/LegacySelect', module)
const itemsChessPlayers = [
{ key: 'aleksandr-grichtchouk', name: 'Aleksandr Grichtchouk' },
{ key: 'fabiano-caruana', name: 'Fabiano Caruana' },
{ key: 'garry-kasparov', name: 'Garry Kasparov' },
{ key: 'hikaru-nakamura', name: 'Hikaru Nakamura' },
{ key: 'levon-aronian', name: 'Levon Aronian' },
{ key: 'magnus-carlsen', name: 'Magnus Carlsen' },
{ key: 'maxime-vachier-lagrave', name: 'Maxime Vachier-Lagrave' },
{ key: 'shakhriyar-mamedyarov', name: 'Shakhriyar Mamedyarov' },
{ key: 'veselin-topalov', name: 'Veselin Topalov' },
{ key: 'viswanathan-anand', name: 'Viswanathan Anand' },
{ key: 'vladimir-kramnik', name: 'Vladimir Kramnik' },
]
type State = {
item: Object | null,
}
class Wrapper extends PureComponent<any, State> {
state = {
item: null,
}
handleChange = item => this.setState({ item })
render() {
const { children } = this.props
const { item } = this.state
return (
<div>
{children(this.handleChange)}
{item && (
<Box mt={2}>
<pre>
{'You selected:'}
{JSON.stringify(item)}
</pre>
</Box>
)}
</div>
)
}
}
stories.add('basic', () => (
<Wrapper>
{onChange => (
<LegacySelect
disabled={boolean('disabled', false)}
placeholder="Choose a chess player..."
items={itemsChessPlayers}
renderSelected={item => item.name}
onChange={onChange}
/>
)}
</Wrapper>
))
stories.add('searchable', () => (
<LegacySelect
placeholder="Choose a chess player..."
items={itemsChessPlayers}
searchable
highlight
fuseOptions={{ keys: ['name'] }}
itemToString={item => (item ? item.name : '')}
renderHighlight={(text, key) => (
<Text key={key} fontWeight="bold">
{text}
</Text>
)}
/>
))
const itemsColors = [
{ key: 'absolute zero', name: 'Absolute Zero', color: '#0048BA' },
{ key: 'acid green', name: 'Acid Green', color: '#B0BF1A' },
{ key: 'aero', name: 'Aero', color: '#7CB9E8' },
{ key: 'aero blue', name: 'Aero Blue', color: '#C9FFE5' },
{ key: 'african violet', name: 'African Violet', color: '#B284BE' },
{ key: 'air force blue (usaf)', name: 'Air Force Blue (USAF)', color: '#00308F' },
{ key: 'air superiority blue', name: 'Air Superiority Blue', color: '#72A0C1' },
]
stories.add('custom render', () => (
<LegacySelect
placeholder="Choose a color..."
items={itemsColors}
highlight
searchable
fuseOptions={{ keys: ['name', 'color'] }}
itemToString={item => (item ? item.name : '')}
renderHighlight={(text, key) => (
<Text key={key} fontWeight="bold">
{text}
</Text>
)}
renderItem={item => (
<Box horizontal flow={2}>
<Box bg={item.color} style={{ width: 20, height: 20 }} />
<span>{item.name_highlight || item.name}</span>
</Box>
)}
/>
))

71
src/components/base/Select/createRenderers.js

@ -0,0 +1,71 @@
// @flow
import React from 'react'
import styled from 'styled-components'
import { components } from 'react-select'
import type { OptionProps } from 'react-select/lib/types'
import Box from 'components/base/Box'
import IconCheck from 'icons/Check'
import IconAngleDown from 'icons/AngleDown'
import IconCross from 'icons/Cross'
import type { Option } from './index'
export default ({
renderOption,
renderValue,
}: {
renderOption: Option => Node,
renderValue: Option => Node,
}) => ({
...STYLES_OVERRIDE,
Option: (props: OptionProps) => {
const { data, isSelected } = props
return (
<components.Option {...props}>
<Box horizontal pr={4} relative>
<Box grow>{renderOption ? renderOption(props) : data.label}</Box>
{isSelected && (
<CheckContainer color="wallet">
<IconCheck size={12} color="red" />
</CheckContainer>
)}
</Box>
</components.Option>
)
},
SingleValue: (props: OptionProps) => {
const { data } = props
return (
<components.SingleValue {...props}>
{renderValue ? renderValue(props) : data.label}
</components.SingleValue>
)
},
})
const STYLES_OVERRIDE = {
DropdownIndicator: (props: OptionProps) => (
<components.DropdownIndicator {...props}>
<IconAngleDown size={20} />
</components.DropdownIndicator>
),
ClearIndicator: (props: OptionProps) => (
<components.ClearIndicator {...props}>
<IconCross size={16} />
</components.ClearIndicator>
),
}
const CheckContainer = styled(Box).attrs({
align: 'center',
justify: 'center',
})`
position: absolute;
top: 0;
right: 0;
bottom: 0;
width: 10px;
`

65
src/components/base/Select/createStyles.js

@ -0,0 +1,65 @@
// @flow
import { colors } from 'styles/theme'
import { ff } from 'styles/helpers'
export default ({ width, minWidth }: { width: number, minWidth: number }) => ({
control: (styles: Object, { isFocused }: Object) => ({
...styles,
width,
minWidth,
...ff('Open Sans|SemiBold'),
height: 40,
backgroundColor: 'white',
cursor: 'pointer',
...(isFocused
? {
borderColor: colors.wallet,
boxShadow: 'rgba(0, 0, 0, 0.05) 0 2px 2px',
}
: {}),
}),
valueContainer: (styles: Object) => ({
...styles,
paddingLeft: 15,
color: colors.graphite,
}),
indicatorSeparator: (styles: Object) => ({
...styles,
background: 'none',
}),
option: (styles: Object, { isFocused, isSelected }: Object) => ({
...styles,
...ff('Open Sans|Regular'),
color: colors.dark,
padding: '10px 15px 10px 15px',
...(isFocused
? {
background: colors.lightGrey,
color: colors.dark,
}
: {}),
...(isSelected
? {
background: 'unset !important',
...ff('Open Sans|SemiBold'),
}
: {
cursor: 'pointer',
}),
}),
menu: (styles: Object) => ({
...styles,
border: `1px solid ${colors.fog}`,
boxShadow: 'rgba(0, 0, 0, 0.05) 0 2px 2px',
}),
menuList: (styles: Object) => ({
...styles,
background: 'white',
borderRadius: 3,
}),
container: (styles: Object) => ({
...styles,
fontSize: 13,
}),
})

0
src/components/base/Select/customRenders.js

380
src/components/base/Select/index.js

@ -1,343 +1,83 @@
// @flow
import React, { PureComponent } from 'react'
import Downshift from 'downshift'
import styled from 'styled-components'
import { space } from 'styled-system'
import React, { Component } from 'react'
import ReactSelect from 'react-select'
import { translate } from 'react-i18next'
import Box from 'components/base/Box'
import GrowScroll from 'components/base/GrowScroll'
import Input from 'components/base/Input'
import Search from 'components/base/Search'
import Text from 'components/base/Text'
import IconCheck from 'icons/Check'
import createStyles from './createStyles'
import createRenderers from './createRenderers'
type Props = {
bg?: string,
flatLeft?: boolean,
flatRight?: boolean,
fakeFocusRight?: boolean,
fuseOptions?: Object,
highlight?: boolean,
items: Array<any>,
itemToString?: Function,
keyProp?: string,
maxHeight?: number,
onChange?: Function,
placeholder?: string,
renderHighlight?: string => React$Node,
renderItem?: (*) => React$Node,
renderSelected?: any => React$Node,
searchable?: boolean,
value?: *,
disabled: boolean,
small?: boolean,
}
const Container = styled(Box).attrs({ relative: true, color: 'graphite' })``
const TriggerBtn = styled(Box).attrs({
alignItems: 'center',
ff: p => (p.small ? 'Open Sans' : 'Open Sans|SemiBold'),
flow: 2,
fontSize: p => (p.small ? 3 : 4),
horizontal: true,
px: 3,
})`
${space};
height: ${p => (p.small ? '34' : '40')}px;
background: ${p => (p.disabled ? p.theme.colors.lightGrey : p.bg || p.theme.colors.white)};
border-bottom-left-radius: ${p => (p.flatLeft ? 0 : p.theme.radii[1])}px;
border-bottom-right-radius: ${p => (p.flatRight ? 0 : p.theme.radii[1])}px;
border-top-left-radius: ${p => (p.flatLeft ? 0 : p.theme.radii[1])}px;
border-top-right-radius: ${p => (p.flatRight ? 0 : p.theme.radii[1])}px;
border: 1px solid ${p => p.theme.colors.fog};
color: ${p => p.theme.colors.graphite};
cursor: ${p => (p.disabled ? 'cursor' : 'pointer')};
display: flex;
width: 100%;
&:focus {
outline: none;
${p =>
p.disabled
? ''
: `
border-color: ${p.theme.colors.wallet};
box-shadow: rgba(0, 0, 0, 0.05) 0 2px 2px;`};
}
${p => {
const c = p.theme.colors.wallet
return p.fakeFocusRight
? `
border-top: 1px solid ${c};
border-right: 1px solid ${c};
border-bottom: 1px solid ${c};
`
: ''
}};
`
// required
value: ?Option,
options: Option[],
onChange: Option => void,
const Item = styled(Box).attrs({
alignItems: 'center',
fontSize: 4,
ff: p => `Open Sans|${p.selected ? 'SemiBold' : 'Regular'}`,
px: 3,
py: 2,
color: 'dark',
})`
background: ${p => (p.highlighted ? p.theme.colors.lightGrey : p.theme.colors.white)};
// custom renders
renderOption: Option => Node,
renderValue: Option => Node,
${p =>
p.first &&
`
border-top-left-radius: ${p.theme.radii[1]}px;
border-top-right-radius: ${p.theme.radii[1]}px;
`} ${p =>
p.last &&
`
border-bottom-left-radius: ${p.theme.radii[1]}px;
border-bottom-right-radius: ${p.theme.radii[1]}px;
`};
`
const Dropdown = styled(Box).attrs({
mt: 1,
})`
border-radius: ${p => p.theme.radii[1]}px;
border: 1px solid ${p => p.theme.colors.fog};
box-shadow: rgba(0, 0, 0, 0.05) 0 2px 2px;
left: 0;
position: absolute;
right: 0;
top: 100%;
z-index: 1;
`
const IconSelected = styled(Box).attrs({
color: 'wallet',
alignItems: 'center',
justifyContent: 'center',
})`
height: 12px;
width: 12px;
opacity: ${p => (p.selected ? 1 : 0)};
`
const AngleDown = props => (
<Box color="grey" alignItems="center" justifyContent="center" {...props}>
<svg viewBox="0 0 16 16" width="16" height="16">
<path
fill="currentColor"
d="M7.70785815 10.86875l-5.08670521-4.5875c-.16153725-.146875-.16153725-.384375 0-.53125l.68051867-.61875c.16153726-.146875.42274645-.146875.58428371 0L8 8.834375l4.1140447-3.703125c.1615372-.146875.4227464-.146875.5842837 0l.6805187.61875c.1615372.146875.1615372.384375 0 .53125l-5.08670525 4.5875c-.16153726.146875-.42274644.146875-.5842837 0z"
/>
</svg>
</Box>
)
const renderSelectedItem = ({ selectedItem, renderSelected, placeholder }: any) =>
selectedItem && renderSelected ? (
renderSelected(selectedItem)
) : (
<Text color="fog">{placeholder}</Text>
)
class Select extends PureComponent<Props> {
static defaultProps = {
bg: undefined,
disabled: false,
small: false,
fakeFocusRight: false,
flatLeft: false,
flatRight: false,
itemToString: (item: Object) => item && item.name,
keyProp: undefined,
maxHeight: 300,
}
_scrollToSelectedItem = true
_oldHighlightedIndex = 0
_useKeyboard = false
_children = {}
renderItems = (items: Array<Object>, selectedItem: any, downshiftProps: Object) => {
const { renderItem, maxHeight, keyProp } = this.props
const { getItemProps, highlightedIndex } = downshiftProps
const selectedItemIndex = items.indexOf(selectedItem)
return (
<Dropdown>
{items.length ? (
<GrowScroll
maxHeight={maxHeight}
onUpdate={scrollbar => {
const currentHighlighted = this._children[highlightedIndex]
const currentSelectedItem = this._children[selectedItemIndex]
if (this._useKeyboard && currentHighlighted) {
scrollbar.scrollIntoView(currentHighlighted, {
alignToTop: highlightedIndex < this._oldHighlightedIndex,
offsetTop: -1,
onlyScrollIfNeeded: true,
})
} else if (this._scrollToSelectedItem && currentSelectedItem) {
window.requestAnimationFrame(() =>
scrollbar.scrollIntoView(currentSelectedItem, {
offsetTop: -1,
}),
)
// optional
placeholder?: string,
isClearable?: boolean,
isDisabled?: boolean,
isLoading?: boolean,
isSearchable?: boolean,
width: number,
minWidth: number,
}
this._scrollToSelectedItem = false
}
export type Option = {
value: 'string',
label: 'string',
data: any,
}
this._oldHighlightedIndex = highlightedIndex
}}
>
{items.map((item, i) => (
<Box
key={keyProp ? item[keyProp] : item.key}
innerRef={n => (this._children[i] = n)}
{...getItemProps({ item })}
>
<Item
first={i === 0}
last={i === items.length - 1}
highlighted={i === highlightedIndex}
selected={selectedItem === item}
horizontal
flow={3}
>
<Box grow>
{renderItem ? (
renderItem(item)
) : (
<span>{item.name_highlight || item.name}</span>
)}
</Box>
<Box>
<IconSelected selected={selectedItem === item}>
<IconCheck size={12} />
</IconSelected>
</Box>
</Item>
</Box>
))}
</GrowScroll>
) : (
<Box>
<Item>{'No results'}</Item>
</Box>
)}
</Dropdown>
)
class Select extends Component<Props> {
handleChange = (value, { action }) => {
const { onChange } = this.props
if (action === 'select-option') {
onChange(value)
}
}
render() {
const {
disabled,
fakeFocusRight,
flatLeft,
flatRight,
fuseOptions,
highlight,
items,
itemToString,
onChange,
placeholder,
renderHighlight,
renderSelected,
searchable,
value,
small,
isClearable,
isSearchable,
isDisabled,
isLoading,
placeholder,
options,
renderOption,
renderValue,
width,
minWidth,
...props
} = this.props
return (
<Downshift
selectedItem={value}
itemToString={itemToString}
onChange={onChange}
render={({
getInputProps,
getToggleButtonProps,
getRootProps,
isOpen,
inputValue,
openMenu,
selectedItem,
...downshiftProps
}) => {
if (!isOpen) {
this._scrollToSelectedItem = true
}
if (disabled) {
return (
<Container {...getRootProps({ refKey: 'innerRef' })}>
<TriggerBtn disabled bg={props.bg} tabIndex={0} small={small}>
{renderSelectedItem({ selectedItem, renderSelected, placeholder })}
</TriggerBtn>
</Container>
)
}
return (
<Container
{...getRootProps({ refKey: 'innerRef' })}
{...props}
horizontal
onKeyDown={() => (this._useKeyboard = true)}
onKeyUp={() => (this._useKeyboard = false)}
>
{searchable ? (
<Box grow>
<Input
small
keepEvent
onClick={openMenu}
renderRight={<AngleDown mr={2} />}
{...getInputProps({ placeholder })}
/>
</Box>
) : (
<TriggerBtn
{...getToggleButtonProps()}
bg={props.bg}
fakeFocusRight={fakeFocusRight}
flatLeft={flatLeft}
flatRight={flatRight}
tabIndex={0}
small={small}
>
<Box grow>
{renderSelectedItem({ selectedItem, renderSelected, placeholder })}
</Box>
<AngleDown mr={-1} />
</TriggerBtn>
)}
<div hidden={!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)
)}
</div>
</Container>
)
}}
<ReactSelect
value={value}
maxMenuHeight={300}
classNamePrefix="select"
options={options}
components={createRenderers({ renderOption, renderValue })}
styles={createStyles({ width, minWidth })}
placeholder={placeholder}
isDisabled={isDisabled}
isLoading={isLoading}
isClearable={isClearable}
isSearchable={isSearchable}
blurInputOnSelect={false}
onChange={this.handleChange}
backspaceRemovesValue
menuShouldBlockScroll
{...props}
/>
)
}
}
export default Select
export default translate()(Select)

127
src/components/base/Select/stories.js

@ -6,28 +6,69 @@ import { boolean } from '@storybook/addon-knobs'
import Box from 'components/base/Box'
import Select from 'components/base/Select'
import Text from 'components/base/Text'
const stories = storiesOf('Components/base/Select', module)
const itemsChessPlayers = [
{ key: 'aleksandr-grichtchouk', name: 'Aleksandr Grichtchouk' },
{ key: 'fabiano-caruana', name: 'Fabiano Caruana' },
{ key: 'garry-kasparov', name: 'Garry Kasparov' },
{ key: 'hikaru-nakamura', name: 'Hikaru Nakamura' },
{ key: 'levon-aronian', name: 'Levon Aronian' },
{ key: 'magnus-carlsen', name: 'Magnus Carlsen' },
{ key: 'maxime-vachier-lagrave', name: 'Maxime Vachier-Lagrave' },
{ key: 'shakhriyar-mamedyarov', name: 'Shakhriyar Mamedyarov' },
{ key: 'veselin-topalov', name: 'Veselin Topalov' },
{ key: 'viswanathan-anand', name: 'Viswanathan Anand' },
{ key: 'vladimir-kramnik', name: 'Vladimir Kramnik' },
{ value: 'aleksandr-grichtchouk', label: 'Aleksandr Grichtchouk' },
{ value: 'fabiano-caruana', label: 'Fabiano Caruana' },
{ value: 'garry-kasparov', label: 'Garry Kasparov' },
{ value: 'hikaru-nakamura', label: 'Hikaru Nakamura' },
{ value: 'levon-aronian', label: 'Levon Aronian' },
{ value: 'magnus-carlsen', label: 'Magnus Carlsen' },
{ value: 'maxime-vachier-lagrave', label: 'Maxime Vachier-Lagrave' },
{ value: 'shakhriyar-mamedyarov', label: 'Shakhriyar Mamedyarov' },
{ value: 'veselin-topalov', label: 'Veselin Topalov' },
{ value: 'viswanathan-anand', label: 'Viswanathan Anand' },
{ value: 'vladimir-kramnik', label: 'Vladimir Kramnik' },
]
type State = {
item: Object | null,
}
stories.add('basic', () => (
<Wrapper>
{onChange => (
<Select
disabled={boolean('disabled', false)}
placeholder="Choose a chess player..."
options={itemsChessPlayers}
renderSelected={item => item.name}
onChange={onChange}
/>
)}
</Wrapper>
))
const itemsColors = [
{ value: 'absolute zero', label: 'Absolute Zero', color: '#0048BA' },
{ value: 'acid green', label: 'Acid Green', color: '#B0BF1A' },
{ value: 'aero', label: 'Aero', color: '#7CB9E8' },
{ value: 'aero blue', label: 'Aero Blue', color: '#C9FFE5' },
{ value: 'african violet', label: 'African Violet', color: '#B284BE' },
{ value: 'air force blue (usaf)', label: 'Air Force Blue (USAF)', color: '#00308F' },
{ value: 'air superiority blue', label: 'Air Superiority Blue', color: '#72A0C1' },
]
stories.add('custom render', () => (
<Wrapper>
{onChange => (
<Select
placeholder="Choose a color..."
options={itemsColors}
onChange={onChange}
renderOption={item => (
<Box horizontal flow={2}>
<Box bg={item.data.color} style={{ width: 20, height: 20 }} />
<span>{item.label}</span>
</Box>
)}
/>
)}
</Wrapper>
))
class Wrapper extends PureComponent<any, State> {
state = {
item: null,
@ -53,65 +94,3 @@ class Wrapper extends PureComponent<any, State> {
)
}
}
stories.add('basic', () => (
<Wrapper>
{onChange => (
<Select
disabled={boolean('disabled', false)}
placeholder="Choose a chess player..."
items={itemsChessPlayers}
renderSelected={item => item.name}
onChange={onChange}
/>
)}
</Wrapper>
))
stories.add('searchable', () => (
<Select
placeholder="Choose a chess player..."
items={itemsChessPlayers}
searchable
highlight
fuseOptions={{ keys: ['name'] }}
itemToString={item => (item ? item.name : '')}
renderHighlight={(text, key) => (
<Text key={key} fontWeight="bold">
{text}
</Text>
)}
/>
))
const itemsColors = [
{ key: 'absolute zero', name: 'Absolute Zero', color: '#0048BA' },
{ key: 'acid green', name: 'Acid Green', color: '#B0BF1A' },
{ key: 'aero', name: 'Aero', color: '#7CB9E8' },
{ key: 'aero blue', name: 'Aero Blue', color: '#C9FFE5' },
{ key: 'african violet', name: 'African Violet', color: '#B284BE' },
{ key: 'air force blue (usaf)', name: 'Air Force Blue (USAF)', color: '#00308F' },
{ key: 'air superiority blue', name: 'Air Superiority Blue', color: '#72A0C1' },
]
stories.add('custom render', () => (
<Select
placeholder="Choose a color..."
items={itemsColors}
highlight
searchable
fuseOptions={{ keys: ['name', 'color'] }}
itemToString={item => (item ? item.name : '')}
renderHighlight={(text, key) => (
<Text key={key} fontWeight="bold">
{text}
</Text>
)}
renderItem={item => (
<Box horizontal flow={2}>
<Box bg={item.color} style={{ width: 20, height: 20 }} />
<span>{item.name_highlight || item.name}</span>
</Box>
)}
/>
))

62
src/components/base/Select/style.js

@ -0,0 +1,62 @@
import { colors } from 'styles/theme'
import { ff } from 'styles/helpers'
export default {
control: (styles, { isFocused }) => ({
...styles,
...ff('Open Sans|SemiBold'),
height: 40,
backgroundColor: 'white',
cursor: 'pointer',
...(isFocused
? {
borderColor: colors.wallet,
boxShadow: 'rgba(0, 0, 0, 0.05) 0 2px 2px',
}
: {}),
}),
valueContainer: styles => ({
...styles,
paddingLeft: 15,
color: colors.graphite,
}),
indicatorSeparator: styles => ({
...styles,
background: 'none',
}),
option: (styles, { isFocused, isSelected }) => ({
...styles,
...ff('Open Sans|Regular'),
color: colors.dark,
padding: '10px 15px 10px 15px',
...(isFocused
? {
background: colors.lightGrey,
color: colors.dark,
}
: {}),
...(isSelected
? {
background: 'unset !important',
...ff('Open Sans|SemiBold'),
}
: {
cursor: 'pointer',
}),
}),
menu: styles => ({
...styles,
border: `1px solid ${colors.fog}`,
boxShadow: 'rgba(0, 0, 0, 0.05) 0 2px 2px',
}),
menuList: styles => ({
...styles,
background: 'white',
borderRadius: 3,
overflow: 'hidden',
}),
container: styles => ({
...styles,
fontSize: 13,
}),
}

2
src/components/modals/AccountSettingRenderBody.js

@ -18,7 +18,7 @@ import CryptoCurrencyIcon from 'components/CryptoCurrencyIcon'
import Box from 'components/base/Box'
import Button from 'components/base/Button'
import Input, { ErrorMessageInput } from 'components/base/Input'
import Select from 'components/base/Select/index'
import Select from 'components/base/LegacySelect'
import { ModalBody, ModalTitle, ModalFooter, ModalContent } from 'components/base/Modal'
type State = {

18
src/styles/global.js

@ -103,6 +103,24 @@ injectGlobal`
fill: ${colors.dark};
}
.select__control:hover, .select__control-is-focused {
border-color: ${colors.fog};
}
.select__single-value {
color: inherit !important;
right: 0;
left: 15px;
}
.select__placeholder {
color ${colors.fog} !important;
}
.select__option:active {
background: ${colors.lightGrey} !important;
}
::selection {
background: ${rgba(colors.wallet, 0.1)};
}

108
yarn.lock

@ -237,6 +237,13 @@
dependencies:
"@babel/types" "7.0.0-beta.48"
"@babel/helper-module-imports@7.0.0-beta.32":
version "7.0.0-beta.32"
resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.0.0-beta.32.tgz#8126fc024107c226879841b973677a4f4e510a03"
dependencies:
"@babel/types" "7.0.0-beta.32"
lodash "^4.2.0"
"@babel/helper-module-imports@7.0.0-beta.35":
version "7.0.0-beta.35"
resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.0.0-beta.35.tgz#308e350e731752cdb4d0f058df1d704925c64e0a"
@ -1386,6 +1393,14 @@
invariant "^2.2.0"
lodash "^4.17.5"
"@babel/types@7.0.0-beta.32":
version "7.0.0-beta.32"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.0.0-beta.32.tgz#c317d0ecc89297b80bbcb2f50608e31f6452a5ff"
dependencies:
esutils "^2.0.2"
lodash "^4.2.0"
to-fast-properties "^2.0.0"
"@babel/types@7.0.0-beta.35":
version "7.0.0-beta.35"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.0.0-beta.35.tgz#cf933a9a9a38484ca724b335b88d83726d5ab960"
@ -1418,6 +1433,22 @@
lodash "^4.17.5"
to-fast-properties "^2.0.0"
"@emotion/hash@^0.6.2":
version "0.6.3"
resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.6.3.tgz#0e7a5604626fc6c6d4ac4061a2f5ac80d50262a4"
"@emotion/memoize@^0.6.1":
version "0.6.2"
resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.6.2.tgz#138e00b332d519b4e307bded6159e5ba48aba3ae"
"@emotion/stylis@^0.6.5":
version "0.6.8"
resolved "https://registry.yarnpkg.com/@emotion/stylis/-/stylis-0.6.8.tgz#6ad4e8d32b19b440efa4481bbbcb98a8c12765bb"
"@emotion/unitless@^0.6.2":
version "0.6.3"
resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.6.3.tgz#65682e68a82701c70eefb38d7f941a2c0bfa90de"
"@ledgerhq/hw-app-btc@^4.12.0", "@ledgerhq/hw-app-btc@^4.7.3":
version "4.12.0"
resolved "https://registry.yarnpkg.com/@ledgerhq/hw-app-btc/-/hw-app-btc-4.12.0.tgz#deae3200dcb6eb196f05a7357c5499f3920e6c6f"
@ -2594,6 +2625,22 @@ babel-plugin-component@^1.1.1:
dependencies:
"@babel/helper-module-imports" "7.0.0-beta.35"
babel-plugin-emotion@^9.1.2:
version "9.1.2"
resolved "https://registry.yarnpkg.com/babel-plugin-emotion/-/babel-plugin-emotion-9.1.2.tgz#e26b313fa0fecd0f2cc07b1e4ef05da167e4f740"
dependencies:
"@babel/helper-module-imports" "7.0.0-beta.32"
"@emotion/hash" "^0.6.2"
"@emotion/memoize" "^0.6.1"
"@emotion/stylis" "^0.6.5"
babel-plugin-macros "^2.0.0"
babel-plugin-syntax-jsx "^6.18.0"
convert-source-map "^1.5.0"
find-root "^1.1.0"
mkdirp "^0.5.1"
source-map "^0.5.7"
touch "^1.0.0"
babel-plugin-istanbul@^4.1.5:
version "4.1.6"
resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-4.1.6.tgz#36c59b2192efce81c5b378321b74175add1c9a45"
@ -2607,7 +2654,7 @@ babel-plugin-jest-hoist@^22.4.4:
version "22.4.4"
resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-22.4.4.tgz#b9851906eab34c7bf6f8c895a2b08bea1a844c0b"
babel-plugin-macros@^2.2.0:
babel-plugin-macros@^2.0.0, babel-plugin-macros@^2.2.0:
version "2.2.1"
resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-2.2.1.tgz#7cc0f84735aa86f776b51860793a98928f43a7fa"
dependencies:
@ -2752,7 +2799,7 @@ babel-plugin-syntax-function-bind@^6.8.0:
version "6.13.0"
resolved "https://registry.yarnpkg.com/babel-plugin-syntax-function-bind/-/babel-plugin-syntax-function-bind-6.13.0.tgz#48c495f177bdf31a981e732f55adc0bdd2601f46"
babel-plugin-syntax-jsx@^6.3.13, babel-plugin-syntax-jsx@^6.8.0:
babel-plugin-syntax-jsx@^6.18.0, babel-plugin-syntax-jsx@^6.3.13, babel-plugin-syntax-jsx@^6.8.0:
version "6.18.0"
resolved "https://registry.yarnpkg.com/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz#0af32a9a6e13ca7a3fd5069e62d7b0f58d0d8946"
@ -4494,7 +4541,7 @@ content-type@~1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
convert-source-map@^1.1.0, convert-source-map@^1.4.0, convert-source-map@^1.5.1:
convert-source-map@^1.1.0, convert-source-map@^1.4.0, convert-source-map@^1.5.0, convert-source-map@^1.5.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.1.tgz#b8278097b9bc229365de5c62cf5fcaed8b5599e5"
@ -4561,6 +4608,17 @@ create-ecdh@^4.0.0:
bn.js "^4.1.0"
elliptic "^6.0.0"
create-emotion@^9.1.3:
version "9.1.3"
resolved "https://registry.yarnpkg.com/create-emotion/-/create-emotion-9.1.3.tgz#a2e570415eb3e1ec7c42489ba55b2361baf0ed3f"
dependencies:
"@emotion/hash" "^0.6.2"
"@emotion/memoize" "^0.6.1"
"@emotion/stylis" "^0.6.5"
"@emotion/unitless" "^0.6.2"
stylis "^3.5.0"
stylis-rule-sheet "^0.0.10"
create-error-class@^3.0.0:
version "3.0.2"
resolved "https://registry.yarnpkg.com/create-error-class/-/create-error-class-3.0.2.tgz#06be7abef947a3f14a30fd610671d401bca8b7b6"
@ -5738,6 +5796,13 @@ emojis-list@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389"
emotion@^9.1.2:
version "9.1.3"
resolved "https://registry.yarnpkg.com/emotion/-/emotion-9.1.3.tgz#e9b3e897ba3d5c0aff5628b0008a8993fa9c0937"
dependencies:
babel-plugin-emotion "^9.1.2"
create-emotion "^9.1.3"
encodeurl@~1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
@ -6516,6 +6581,10 @@ find-npm-prefix@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/find-npm-prefix/-/find-npm-prefix-1.0.2.tgz#8d8ce2c78b3b4b9e66c8acc6a37c231eb841cfdf"
find-root@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4"
find-up@^1.0.0:
version "1.1.2"
resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f"
@ -9659,6 +9728,12 @@ nopt@^4.0.1, nopt@~4.0.1:
abbrev "1"
osenv "^0.1.4"
nopt@~1.0.10:
version "1.0.10"
resolved "https://registry.yarnpkg.com/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee"
dependencies:
abbrev "1"
normalize-package-data@^2.0.0, normalize-package-data@^2.3.2, normalize-package-data@^2.3.4, normalize-package-data@^2.4.0, "normalize-package-data@~1.0.1 || ^2.0.0", normalize-package-data@~2.4.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.4.0.tgz#12f95a307d58352075a04907b84ac8be98ac012f"
@ -11136,7 +11211,7 @@ radium@^0.19.0:
inline-style-prefixer "^2.0.5"
prop-types "^15.5.8"
raf@^3.1.0:
raf@^3.1.0, raf@^3.4.0:
version "3.4.0"
resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.0.tgz#a28876881b4bc2ca9117d4138163ddb80f781575"
dependencies:
@ -11310,6 +11385,12 @@ react-icons@^2.2.7:
dependencies:
react-icon-base "2.1.0"
react-input-autosize@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/react-input-autosize/-/react-input-autosize-2.2.1.tgz#ec428fa15b1592994fb5f9aa15bb1eb6baf420f8"
dependencies:
prop-types "^15.5.8"
react-inspector@^2.2.2:
version "2.3.0"
resolved "https://registry.yarnpkg.com/react-inspector/-/react-inspector-2.3.0.tgz#fc9c1d38ab687fc0d190dcaf133ae40158968fc8"
@ -11403,6 +11484,17 @@ react-router@^4.2.0:
prop-types "^15.5.4"
warning "^3.0.0"
react-select@2.0.0-beta.6:
version "2.0.0-beta.6"
resolved "https://registry.yarnpkg.com/react-select/-/react-select-2.0.0-beta.6.tgz#87ac27831f348cb9535dfd825534934adcfb7e97"
dependencies:
classnames "^2.2.5"
emotion "^9.1.2"
prop-types "^15.6.0"
raf "^3.4.0"
react-input-autosize "^2.2.1"
react-transition-group "^2.2.1"
react-smooth-scrollbar@^8.0.6:
version "8.0.6"
resolved "https://registry.yarnpkg.com/react-smooth-scrollbar/-/react-smooth-scrollbar-8.0.6.tgz#179072e6a547b3af589ea303c50fd86366275edc"
@ -11443,7 +11535,7 @@ react-textarea-autosize@^5.2.1:
dependencies:
prop-types "^15.6.0"
react-transition-group@^2.0.0:
react-transition-group@^2.0.0, react-transition-group@^2.2.1:
version "2.3.1"
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-2.3.1.tgz#31d611b33e143a5e0f2d94c348e026a0f3b474b6"
dependencies:
@ -13318,6 +13410,12 @@ toposort@^1.0.0:
version "1.0.7"
resolved "https://registry.yarnpkg.com/toposort/-/toposort-1.0.7.tgz#2e68442d9f64ec720b8cc89e6443ac6caa950029"
touch@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/touch/-/touch-1.0.0.tgz#449cbe2dbae5a8c8038e30d71fa0ff464947c4de"
dependencies:
nopt "~1.0.10"
tough-cookie@>=2.3.3, tough-cookie@^2.3.3, tough-cookie@~2.3.3:
version "2.3.4"
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.4.tgz#ec60cee38ac675063ffc97a5c18970578ee83655"

Loading…
Cancel
Save