diff --git a/demo/ComboBox.tsx b/demo/ComboBox.tsx new file mode 100644 index 0000000000..819e7d320f --- /dev/null +++ b/demo/ComboBox.tsx @@ -0,0 +1,215 @@ +/** + * Could not find ready-to-use component with required behaviour so + * I quickly hacked my own. Will refactor into separate npm package later + */ + +import * as React from 'react'; +import styled, { StyledFunction } from 'styled-components'; + +function withProps( + styledFunction: StyledFunction>, +): StyledFunction> { + return styledFunction; +} + +const DropDownItem = withProps<{ active: boolean }>(styled.li)` + ${props => ((props as any).active ? 'background-color: #eee' : '')}; + padding: 13px 16px; + &:hover { + background-color: #eee; + } + cursor: pointer; + text-overflow: ellipsis; + overflow: hidden; +`; + +const DropDownList = styled.ul` + box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12), + 0 3px 1px -2px rgba(0, 0, 0, 0.2); + background: #fff; + border-radius: 0 0 2px 2px; + top: 100%; + left: 0; + right: 0; + z-index: 200; + overflow: hidden; + position: absolute; + list-style: none; + margin: 4px 0 0 0; + padding: 5px 0; + font-family: 'Lato'; + overflow: hidden; +`; + +const ComboBoxWrap = styled.div` + position: relative; + width: 100%; + max-width: 500px; + display: flex; +`; + +const Input = styled.input` + box-sizing: border-box; + width: 100%; + padding: 0 10px; + color: #555; + background-color: #fff; + border: 1px solid #ccc; + + font-size: 16px; + height: 28px; + box-sizing: border-box; + vertical-align: middle; + line-height: 1; + outline: none; + + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s; + + &:focus { + border-color: #66afe9; + outline: 0; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6); + } +`; + +const Button = styled.button` + background-color: #fff; + color: #333; + padding: 2px 10px; + touch-action: manipulation; + cursor: pointer; + user-select: none; + border: 1px solid #ccc; + border-left: 0; + font-size: 16px; + height: 28px; + box-sizing: border-box; + vertical-align: middle; + line-height: 1; + outline: none; + width: 80px; +`; + +export interface ComboBoxProps { + onChange?: (val: string) => void; + options: { value: string; label: string }[]; + placeholder?: string; + value?: string; +} +export interface ComboBoxState { + open: boolean; + value: string; + activeItemIdx: number; +} + +export default class ComboBox extends React.Component { + state = { + open: false, + value: this.props.value || '', + activeItemIdx: -1, + }; + + open = () => { + this.setState({ + open: true, + }); + }; + + close = () => { + this.setState({ + open: false, + activeItemIdx: -1, + }); + }; + + handleChange = e => { + this.updateValue(e.currentTarget.value); + }; + + updateValue(value) { + this.setState({ + value, + activeItemIdx: -1, + }); + } + + handleSelect(value: string) { + this.updateValue(value); + if (this.props.onChange) { + this.props.onChange(value); + } + this.close(); + } + + handleKeyPress = (e: React.KeyboardEvent) => { + if (e.keyCode === 13) { + this.handleSelect(e.currentTarget.value); + } else if (e.keyCode === 40) { + const activeItemIdx = Math.min(this.props.options.length - 1, ++this.state.activeItemIdx); + this.setState({ + open: true, + activeItemIdx, + value: this.props.options[activeItemIdx].value, + }); + e.preventDefault(); + } else if (e.keyCode === 38) { + const activeItemIdx = Math.max(0, --this.state.activeItemIdx); + this.setState({ + activeItemIdx, + value: this.props.options[activeItemIdx].value, + }); + e.preventDefault(); + } else if (e.keyCode === 27) { + this.close(); + } + }; + + handleBlur = () => { + setTimeout(() => this.close(), 100); + }; + + handleItemClick = (val, idx) => { + this.handleSelect(val); + this.setState({ + activeItemIdx: idx, + }); + }; + + render() { + const { open, value, activeItemIdx } = this.state; + const { options, placeholder } = this.props; + return ( + + + + {open && ( + + {options.map((option, idx) => ( + { + this.handleItemClick(option.value, idx); + }} + > + + {option.label} + +
+ {option.value} +
+ ))} +
+ )} +
+ ); + } +} diff --git a/demo/index.html b/demo/index.html index 9e0c595eb6..9b0be9488a 100644 --- a/demo/index.html +++ b/demo/index.html @@ -3,7 +3,9 @@ - ReDoc + ReDoc Interactive Demo + +