-
-
Save ysfzrn/d176d6e591c2e2f520d00f10b1ca32d6 to your computer and use it in GitHub Desktop.
StyledSelectField
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React from "react"; | |
import styled, { keyframes } from "styled-components"; | |
import { observer } from "mobx-react"; | |
import { detectmob } from "../utils/sharedStyle"; | |
import { List, AutoSizer } from "react-virtualized"; | |
let ClickOutComponent = require("react-onclickout"); | |
const ArrowDown = require("../public/assets/arrow-down.png"); | |
const isMob = detectmob(); | |
//var isIE = /*@cc_on!@*/false || !!document.documentMode; | |
@observer | |
class SelectField extends React.Component { | |
constructor(props) { | |
super(props); | |
this.state = { | |
focussing: false, | |
used: false, | |
open: false, | |
doFilter: false, | |
currentIndex: 0 | |
}; | |
} | |
componentDidMount() { | |
const { value } = this.props; | |
if (value !== undefined && value !== null && value !== "") { | |
this.setState({ used: true }); | |
} | |
} | |
componentWillReceiveProps(nextProps) { | |
const { value } = nextProps; | |
if (value !== undefined && value !== null && value !== "") { | |
this.setState({ used: true }); | |
} else { | |
this.setState({ used: false }); | |
} | |
} | |
onFocusHandle = event => { | |
if(this.props.selectOnFocus){ | |
event.target.select(); | |
} | |
this.setState({ focussing: true }); | |
}; | |
onBlurHandle = () => { | |
this.setState({ focussing: false }); | |
}; | |
onChange = event => { | |
const { currentIndex } = this.state; | |
const list = this.getFilterList(); | |
this.props.onChange(event.target.name, event.target.value); | |
if (this.input.value) { | |
this.setState({ open: true, used: true, doFilter: true, currentIndex: currentIndex >= list.length ? 0 : currentIndex }); | |
} else { | |
this.setState({ open: true, used: false, doFilter: true }); | |
} | |
}; | |
handlePopover = () => { | |
this.setState({ open: !this.state.open }); | |
//document.addEventListener("keydown", this.listenKeyPress); | |
}; | |
onClickOut = e => { | |
this.setState({ open: false }); | |
//document.removeEventListener("keydown", this.listenKeyPress); | |
}; | |
pressEnter=()=>{ | |
const list = this.getFilterList(); | |
const item = list[this.state.currentIndex]; | |
this.onSelected(item); | |
} | |
onSelected = item => { | |
this.props.onSelected(item); | |
this.setState({ open: false, doFilter: false }); | |
}; | |
listenKeyPress = event => { | |
const keyName = event.key; | |
const { currentIndex } = this.state; | |
const list = this.getFilterList(); | |
let _tempIndex = currentIndex; | |
if (keyName === "ArrowUp") { | |
_tempIndex = _tempIndex - 1; | |
if (_tempIndex < 0) { | |
_tempIndex = list.length - 1; | |
} | |
} else if (keyName === "ArrowDown") { | |
_tempIndex = _tempIndex + 1; | |
if (_tempIndex >= list.length) { | |
_tempIndex = 0; | |
} | |
} else if(keyName === "Enter"){ | |
//document.removeEventListener("keydown", this.listenKeyPress); | |
this.pressEnter(); | |
} | |
this.setState({ currentIndex: _tempIndex }); | |
}; | |
getFilterList = () => { | |
return this.props.options.filter(item => { | |
return this.input && this.state.doFilter | |
? item.name.toLowerCase().indexOf(this.input.value.toLowerCase()) > -1 | |
: true; | |
}); | |
}; | |
rowRenderer = ({ | |
key, // Unique key within array of rows | |
index, // Index of row within collection | |
isScrolling, // The List is currently being scrolled | |
isVisible, // This row is visible within the List (eg it is not an overscanned row) | |
style // Style object to be applied to row (to position it) | |
}) => { | |
const list = this.getFilterList(); | |
const { customField } = this.props; | |
return ( | |
<ListItem | |
key={key} | |
style={style} | |
innerRef={comp => { | |
this[`listItem${key}`] = comp; | |
}} | |
onClick={() => this.onSelected(list[index])} | |
> | |
{customField ? list[index][customField] : list[index].name} | |
</ListItem> | |
); | |
}; | |
render() { | |
const { | |
id, | |
label, | |
name, | |
type, | |
value, | |
placeholder, | |
options, | |
fontSize, | |
highLight, | |
iconTop | |
} = this.props; | |
const { focussing, used, open } = this.state; | |
const list = this.getFilterList(); | |
return ( | |
<ClickOutComponent onClickOut={this.onClickOut}> | |
<Group onClick={!isMob && this.handlePopover}> | |
<TextInput | |
innerRef={comp => { | |
this.input = comp; | |
}} | |
id={id} | |
name={name} | |
type={type} | |
{...this.props} | |
placeholder={placeholder} | |
value={value === null ? "" : value} | |
onFocus={this.onFocusHandle} | |
onBlur={this.onBlurHandle} | |
focussing={focussing} | |
onChange={this.onChange} | |
autoComplete="off" | |
onTouchStart={isMob ? this.handlePopover : ()=>{} } | |
fontSize={fontSize} | |
/> | |
{highLight && <Line focussing={focussing} used={used} />} | |
<Label | |
focussing={focussing} | |
used={used} | |
placeholder={placeholder} | |
fontSize={fontSize} | |
> | |
{label}{" "} | |
</Label> | |
<Icon | |
src={ArrowDown} | |
open={open} | |
onClick={this.handlePopover} | |
onTouchTap={this.handlePopover} | |
iconTop={iconTop} | |
/> | |
<Highlight focussing={focussing} /> | |
<BarBefore focussing={focussing} /> | |
<Bar focussing={focussing} /> | |
<BarAfter focussing={focussing} /> | |
<Popover open={open}> | |
{options.length > 0 ? ( | |
<AutoSizer disableHeight> | |
{({ width }) => ( | |
<List | |
width={width} | |
height={220} | |
rowCount={list.length} | |
rowHeight={45} | |
rowRenderer={this.rowRenderer} | |
overscanRowCount={6} | |
/> | |
)} | |
</AutoSizer> | |
) : null} | |
</Popover> | |
</Group> | |
</ClickOutComponent> | |
); | |
} | |
} | |
SelectField.defaultProps = { | |
highLight: true, | |
fontSize: 24, | |
iconTop: 20 | |
}; | |
const Group = styled.div` | |
position: relative; | |
margin-bottom: 25px; | |
margin-top: 20px; | |
padding-right: 15px; | |
`; | |
const TextInput = styled.input` | |
::-webkit-input-placeholder { | |
color: #A5A5A5 !important; | |
font-weight: 300; | |
} | |
:-moz-placeholder { | |
color: #A5A5A5 !important; | |
font-weight: 300; | |
} | |
::-moz-placeholder { | |
color: #A5A5A5 !important; | |
font-weight: 300; | |
} | |
:-ms-input-placeholder { | |
color: #A5A5A5 !important; | |
font-weight: 300; | |
} | |
font-size: ${p => (p.fontSize ? p.fontSize : 24)}px; | |
padding: 10px 10px 10px 5px; | |
background: transparent; | |
display: block; | |
width: 100%; | |
min-width: 200px; | |
height:50px; | |
border: none; | |
border-bottom: 0px solid ${props => props.theme.palette.primary1Color}; | |
outline: ${props => (props.focussing ? "none" : "hidden")}; | |
`; | |
const Line = styled.div` | |
border: 1px solid #cdcdcd; | |
background-color: #cdcdcd; | |
width: 100%; | |
min-width: 200px; | |
height: 1px; | |
display: ${props => (props.focussing ? "none" : "hidden")}; | |
`; | |
const Label = styled.label` | |
font-weight: normal; | |
position: absolute; | |
pointer-events: none; | |
left: 5px; | |
transition: 0.3s linear font-size, 0.3s linear top; | |
color: ${props => | |
props.focussing || props.used | |
? props.theme.palette.primary1Color | |
: "#cdcdcd"}; | |
font-size: ${props => | |
props.focussing || props.used ? props.fontSize - 8 : props.fontSize}px; | |
top: ${props => | |
props.placeholder || props.focussing || props.used ? -20 : 10}px; | |
`; | |
const Icon = styled.img` | |
font-weight: normal; | |
position: absolute; | |
pointer-events: none; | |
right: 5px; | |
top: ${p => (p.iconTop ? p.iconTop : 20)}px; | |
width: 26px; | |
height: 15px; | |
cursor: pointer; | |
transform-origin: center; | |
transform: ${p => | |
p.open ? "rotate3d(0, 0, 1, 180deg)" : "rotate3d(0, 0, 1, 0deg)"}; | |
`; | |
const inputHighlighter = keyframes` | |
from { background:#016490; } | |
to { width:0; background:transparent; } | |
`; | |
const Highlight = styled.span` | |
position: absolute; | |
height: 60%; | |
width: 200px; | |
top: 25%; | |
left: 0; | |
pointer-events: none; | |
opacity: 0.5; | |
animation: 0.3s ${props => (props.focussing ? inputHighlighter : undefined)} linear; | |
`; | |
const Bar = styled.span` | |
position: relative; | |
display: block; | |
`; | |
const BarBefore = styled(Bar)` | |
content: ""; | |
height: 3px; | |
bottom: 10px; | |
position: absolute; | |
background: ${props => props.theme.palette.primary1Color}; | |
left: 50%; | |
transition: 0.4s linear width; | |
width: ${props => (props.focussing ? "50%" : 0)}; | |
`; | |
const BarAfter = styled(Bar)` | |
content: ""; | |
height: 3px; | |
bottom: 10px; | |
position: absolute; | |
background: ${props => props.theme.palette.primary1Color}; | |
right: 50%; | |
transition: 0.4s linear width; | |
width: ${props => (props.focussing ? "50%" : 0)}; | |
`; | |
//animation: ${ p=> p.isIE ? undefined : `${fadeIn} 0.3s linear` }; | |
const Popover = styled.div` | |
display: ${p => (p.open ? "block" : "none")}; | |
position: absolute; | |
left: 0; | |
right: 0; | |
top: 60px; | |
z-index: 300; | |
background-color: #ffffff; | |
max-height: 225px; | |
overflow-y: auto; | |
overflow-x: hidden; | |
box-shadow: 5px 5px 5px #ccc; | |
`; | |
const ListItem = styled.li` | |
display: flex; | |
flex-direction: row; | |
align-items: center; | |
min-height: 34px; | |
padding: 10px 20px; | |
font-size: 14px; | |
cursor:pointer; | |
&:hover { | |
background-color: #e0e0e0; | |
} | |
&:focus { | |
background-color: #e0e0e0; | |
} | |
`; | |
export default SelectField; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment