From 823aa5ad43126747cb071a0c347b200cc84df714 Mon Sep 17 00:00:00 2001 From: Noah Loomans Date: Thu, 28 Jun 2018 16:08:57 +0200 Subject: Split search --- src/client/react/components/container/Search.js | 141 +----------------- src/client/react/components/container/Search.scss | 42 ------ .../react/components/presentational/Search.js | 158 +++++++++++++++++++++ .../react/components/presentational/Search.scss | 42 ++++++ 4 files changed, 203 insertions(+), 180 deletions(-) delete mode 100644 src/client/react/components/container/Search.scss create mode 100644 src/client/react/components/presentational/Search.js create mode 100644 src/client/react/components/presentational/Search.scss diff --git a/src/client/react/components/container/Search.js b/src/client/react/components/container/Search.js index 3e599e7..8308d67 100644 --- a/src/client/react/components/container/Search.js +++ b/src/client/react/components/container/Search.js @@ -18,145 +18,10 @@ * */ -import React from 'react'; -import PropTypes from 'prop-types'; import { connect } from 'react-redux'; -import classnames from 'classnames'; import { withRouter } from 'react-router-dom'; - -import SearchIcon from 'react-icons/lib/md/search'; - -import { userFromMatch } from '../../lib/url'; -import { setUser as setUserAction } from '../../store/actions'; - -import users from '../../users'; -import Menu from './Menu'; -import Results from './Results'; -import IconFromUserType from '../presentational/IconFromUserType'; - -import './Search.scss'; - -class Search extends React.Component { - static propTypes = { - selectedResult: PropTypes.string, - searchText: PropTypes.string.isRequired, - match: PropTypes.object.isRequired, - setUser: PropTypes.func.isRequired, - onInputChange: PropTypes.func.isRequired, - changeSelectedResult: PropTypes.func.isRequired, - }; - - static defaultProps = { - selectedResult: null, - }; - - constructor(props) { - super(props); - - this.state = { - hasFocus: false, - }; - - this.onFocus = this.onFocus.bind(this); - this.onBlur = this.onBlur.bind(this); - this.onKeyDown = this.onKeyDown.bind(this); - } - - onFocus() { - this.setState({ - hasFocus: true, - }); - } - - onBlur() { - this.setState({ - hasFocus: false, - }); - } - - onKeyDown(event) { - const { - selectedResult, - match, - setUser, - changeSelectedResult, - } = this.props; - - const urlUser = userFromMatch(match); - - switch (event.key) { - case 'ArrowUp': - event.preventDefault(); - changeSelectedResult(-1); - break; - - case 'ArrowDown': - event.preventDefault(); - changeSelectedResult(+1); - break; - - case 'Escape': - event.preventDefault(); - setUser(urlUser); - break; - - case 'Enter': - event.preventDefault(); - if (selectedResult) { - setUser(selectedResult); - } - break; - - default: - // Do nothing - } - } - - render() { - const { - searchText, - match, - onInputChange, - } = this.props; - - const { - hasFocus, - } = this.state; - - const urlUser = userFromMatch(match); - - const isExactMatch = ( - urlUser != null && searchText === users.byId[urlUser].value - ); - - return ( -
-
-
-
- } - /> -
- onInputChange(event.target.value)} - onKeyDown={this.onKeyDown} - value={searchText} - placeholder="Zoeken" - onFocus={this.onFocus} - onBlur={this.onBlur} - autoComplete="off" - /> - -
- -
-
- ); - } -} +import Search from '../presentational/Search'; +import { setUser } from '../../store/actions'; const mapStateToProps = state => ({ results: state.search.results, @@ -165,7 +30,7 @@ const mapStateToProps = state => ({ }); const mapDispatchToProps = dispatch => ({ - setUser: user => dispatch(setUserAction(user)), + setUser: user => dispatch(setUser(user)), onInputChange: searchText => dispatch({ type: 'SEARCH/INPUT_CHANGE', searchText, diff --git a/src/client/react/components/container/Search.scss b/src/client/react/components/container/Search.scss deleted file mode 100644 index ef629c2..0000000 --- a/src/client/react/components/container/Search.scss +++ /dev/null @@ -1,42 +0,0 @@ -.Search { - height: 54px; - position: relative; - - .overflow { - border-radius: 2px; - background-color: white; - position: absolute; - width: 100%; - box-shadow: 0 2px 2px 0 rgba(0,0,0,0.16), 0 0 0 1px rgba(0,0,0,0.08); - - &.hasFocus { - box-shadow: 0 3px 8px 0 rgba(0,0,0,0.2), 0 0 0 1px rgba(0,0,0,0.08); - } - - .inputWrapper { - display: flex; - height: 54px; - - .iconWrapper { - height: 54px; - padding: 15px; - - svg { - height: 24px; - width: 24px; - } - } - - input { - border: 0; - background-color: transparent; - flex-grow: 1; - height: inherit; - padding: 16px; - padding-left: 0px; - font-size: 16px; - outline: none; - } - } - } -} diff --git a/src/client/react/components/presentational/Search.js b/src/client/react/components/presentational/Search.js new file mode 100644 index 0000000..8c90efb --- /dev/null +++ b/src/client/react/components/presentational/Search.js @@ -0,0 +1,158 @@ +/** + * Copyright (C) 2018 Noah Loomans + * + * This file is part of rooster.hetmml.nl. + * + * rooster.hetmml.nl is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * rooster.hetmml.nl is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with rooster.hetmml.nl. If not, see . + * + */ + +import React from 'react'; +import PropTypes from 'prop-types'; +import classnames from 'classnames'; + +import SearchIcon from 'react-icons/lib/md/search'; + +import { userFromMatch } from '../../lib/url'; + +import users from '../../users'; +import Menu from '../container/Menu'; +import Results from '../container/Results'; +import IconFromUserType from './IconFromUserType'; + +import './Search.scss'; + +class Search extends React.Component { + static propTypes = { + selectedResult: PropTypes.string, + searchText: PropTypes.string.isRequired, + match: PropTypes.object.isRequired, + setUser: PropTypes.func.isRequired, + onInputChange: PropTypes.func.isRequired, + changeSelectedResult: PropTypes.func.isRequired, + }; + + static defaultProps = { + selectedResult: null, + }; + + constructor(props) { + super(props); + + this.state = { + hasFocus: false, + }; + + this.onFocus = this.onFocus.bind(this); + this.onBlur = this.onBlur.bind(this); + this.onKeyDown = this.onKeyDown.bind(this); + } + + onFocus() { + this.setState({ + hasFocus: true, + }); + } + + onBlur() { + this.setState({ + hasFocus: false, + }); + } + + onKeyDown(event) { + const { + selectedResult, + match, + setUser, + changeSelectedResult, + } = this.props; + + const urlUser = userFromMatch(match); + + switch (event.key) { + case 'ArrowUp': + event.preventDefault(); + changeSelectedResult(-1); + break; + + case 'ArrowDown': + event.preventDefault(); + changeSelectedResult(+1); + break; + + case 'Escape': + event.preventDefault(); + setUser(urlUser); + break; + + case 'Enter': + event.preventDefault(); + if (selectedResult) { + setUser(selectedResult); + } + break; + + default: + // Do nothing + } + } + + render() { + const { + searchText, + match, + onInputChange, + } = this.props; + + const { + hasFocus, + } = this.state; + + const urlUser = userFromMatch(match); + + const isExactMatch = ( + urlUser != null && searchText === users.byId[urlUser].value + ); + + return ( +
+
+
+
+ } + /> +
+ onInputChange(event.target.value)} + onKeyDown={this.onKeyDown} + value={searchText} + placeholder="Zoeken" + onFocus={this.onFocus} + onBlur={this.onBlur} + autoComplete="off" + /> + +
+ +
+
+ ); + } +} + +export default Search; diff --git a/src/client/react/components/presentational/Search.scss b/src/client/react/components/presentational/Search.scss new file mode 100644 index 0000000..ef629c2 --- /dev/null +++ b/src/client/react/components/presentational/Search.scss @@ -0,0 +1,42 @@ +.Search { + height: 54px; + position: relative; + + .overflow { + border-radius: 2px; + background-color: white; + position: absolute; + width: 100%; + box-shadow: 0 2px 2px 0 rgba(0,0,0,0.16), 0 0 0 1px rgba(0,0,0,0.08); + + &.hasFocus { + box-shadow: 0 3px 8px 0 rgba(0,0,0,0.2), 0 0 0 1px rgba(0,0,0,0.08); + } + + .inputWrapper { + display: flex; + height: 54px; + + .iconWrapper { + height: 54px; + padding: 15px; + + svg { + height: 24px; + width: 24px; + } + } + + input { + border: 0; + background-color: transparent; + flex-grow: 1; + height: inherit; + padding: 16px; + padding-left: 0px; + font-size: 16px; + outline: none; + } + } + } +} -- cgit v1.1