Skip to content

Instantly share code, notes, and snippets.

@ysfzrn
Created February 1, 2018 10:23
Show Gist options
  • Save ysfzrn/d176d6e591c2e2f520d00f10b1ca32d6 to your computer and use it in GitHub Desktop.
Save ysfzrn/d176d6e591c2e2f520d00f10b1ca32d6 to your computer and use it in GitHub Desktop.
StyledSelectField
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