From 3c6b7afcc1181de1e135fab342a8f6118e82cc4c Mon Sep 17 00:00:00 2001 From: Javi Velasco Date: Wed, 6 Jan 2016 12:56:30 +0100 Subject: [PATCH] Fixe autocomplete --- components/autocomplete/Autocomplete.jsx | 412 +++++++++++------------ components/utils/index.js | 4 + server.js | 6 +- webpack.config.development.js | 4 +- 4 files changed, 215 insertions(+), 211 deletions(-) diff --git a/components/autocomplete/Autocomplete.jsx b/components/autocomplete/Autocomplete.jsx index e340298c..8845e834 100644 --- a/components/autocomplete/Autocomplete.jsx +++ b/components/autocomplete/Autocomplete.jsx @@ -1,206 +1,206 @@ -//import React from 'react'; -//import ReactDOM from 'react-dom'; -//import ClassNames from 'classnames'; -//import Input from '../input'; -//import events from '../utils/events'; -//import style from './style'; -// -//const POSITION = { -// AUTO: 'auto', -// DOWN: 'down', -// UP: 'up' -//}; -// -//class Autocomplete extends React.Component { -// static propTypes = { -// className: React.PropTypes.string, -// direction: React.PropTypes.oneOf(['auto', 'up', 'down']), -// disabled: React.PropTypes.bool, -// error: React.PropTypes.string, -// label: React.PropTypes.string, -// multiple: React.PropTypes.bool, -// onChange: React.PropTypes.func, -// source: React.PropTypes.any, -// value: React.PropTypes.any -// }; -// -// static defaultProps = { -// className: '', -// direction: 'auto', -// multiple: true, -// source: {} -// }; -// -// state = { -// direction: this.props.direction, -// focus: false, -// query: this.query(this.props.value) -// }; -// -// componentWillReceiveProps (nextProps) { -// if (!this.props.multiple) { -// this.setState({query: nextProps.value}); -// } -// } -// -// shouldComponentUpdate (nextProps, nextState) { -// if (!this.state.focus && nextState.focus && this.props.direction === POSITION.AUTO) { -// const direction = this.calculateDirection(); -// if (this.state.direction !== direction) { -// this.setState({ direction }); -// return false; -// } -// } -// return true; -// } -// -// handleChange = (keys, event) => { -// const key = this.props.multiple ? keys : keys[0]; -// const query = this.query(key); -// if (this.props.onChange) this.props.onChange(key, event); -// this.setState({ focus: false, query }, () => { this.refs.input.blur(); }); -// }; -// -// handleQueryBlur = () => { -// if (this.state.focus) this.setState({focus: false}); -// }; -// -// handleQueryChange = (value) => { -// this.setState({query: value}); -// }; -// -// handleQueryFocus = () => { -// this.refs.suggestions.scrollTop = 0; -// this.setState({active: '', focus: true}); -// }; -// -// handleQueryKeyUp = (event) => { -// if (event.which === 13 && this.state.active) this.select(this.state.active, event); -// if (event.which === 27) this.refs.input.blur(); -// if ([40, 38].indexOf(event.which) !== -1) { -// const suggestionsKeys = [...this.suggestions().keys()]; -// let index = suggestionsKeys.indexOf(this.state.active) + (event.which === 40 ? +1 : -1); -// if (index < 0) index = suggestionsKeys.length - 1; -// if (index >= suggestionsKeys.length) index = 0; -// this.setState({active: suggestionsKeys[index]}); -// } -// }; -// -// handleSuggestionHover = (key) => { -// this.setState({active: key}); -// }; -// -// calculateDirection () { -// if (this.props.direction === 'auto') { -// const client = ReactDOM.findDOMNode(this.refs.input).getBoundingClientRect(); -// const screen_height = window.innerHeight || document.documentElement.offsetHeight; -// const up = client.top > ((screen_height / 2) + client.height); -// return up ? 'up' : 'down'; -// } else { -// return this.props.direction; -// } -// } -// -// query (key) { -// return !this.props.multiple && this.props.value ? this.source().get(key) : ''; -// } -// -// suggestions2 () { -// const suggestions2 = new Map(); -// const query = this.state.query.toLowerCase().trim() || ''; -// const values = this.values(); -// for (const [key, value] of this.source()) { -// if (!values.has(key) && value.toLowerCase().trim().startsWith(query)) { -// suggestions2.set(key, value); -// } -// } -// return suggestions2; -// } -// -// source () { -// const { source } = this.props; -// if (source.hasOwnProperty('length')) { -// return new Map(source.map((item) => [item, item])); -// } else { -// return new Map(Object.keys(source).map((key) => [key, source[key]])); -// } -// } -// -// values () { -// const valueMap = new Map(); -// const values = this.props.multiple ? this.props.value : [this.props.value]; -// for (const [k, v] of this.source()) { -// if (values.indexOf(k) !== -1) valueMap.set(k, v); -// } -// return valueMap; -// } -// -// select (key, event) { -// events.pauseEvent(event); -// const values = this.values(this.props.value); -// this.handleChange([key, ...values.keys()], event); -// } -// -// unselect (key, event) { -// const values = this.values(this.props.value); -// values.delete(key); -// this.handleChange([...values.keys()], event); -// } -// -// renderSelected () { -// if (this.props.multiple) { -// const selectedItems = [...this.values()].map(([key, value]) => { -// return
  • {value}
  • ; -// }); -// -// return ; -// } -// } -// -// renderSuggestions () { -// const suggestions2 = [...this.suggestions2()].map(([key, value]) => { -// const className = ClassNames(style.suggestion, {[style.active]: this.state.active === key}); -// return ( -//
  • -// {value} -//
  • -// ); -// }); -// -// const className = ClassNames(style.suggestions2, {[style.up]: this.state.direction === 'up'}); -// return ; -// } -// -// render () { -// const {error, label, ...other} = this.props; -// const className = ClassNames(style.root, { -// [style.focus]: this.state.focus -// }, this.props.className); -// -// return ( -//
    -// {this.renderSelected()} -// -// {this.renderSuggestions()} -//
    -// ); -// } -//} -// -//export default Autocomplete; +import React from 'react'; +import ReactDOM from 'react-dom'; +import ClassNames from 'classnames'; +import Input from '../input'; +import events from '../utils/events'; +import style from './style'; + +const POSITION = { + AUTO: 'auto', + DOWN: 'down', + UP: 'up' +}; + +class Autocomplete extends React.Component { + static propTypes = { + className: React.PropTypes.string, + direction: React.PropTypes.oneOf(['auto', 'up', 'down']), + disabled: React.PropTypes.bool, + error: React.PropTypes.string, + label: React.PropTypes.string, + multiple: React.PropTypes.bool, + onChange: React.PropTypes.func, + source: React.PropTypes.any, + value: React.PropTypes.any + }; + + static defaultProps = { + className: '', + direction: 'auto', + multiple: true, + source: {} + }; + + state = { + direction: this.props.direction, + focus: false, + query: this.query(this.props.value) + }; + + componentWillReceiveProps (nextProps) { + if (!this.props.multiple) { + this.setState({query: nextProps.value}); + } + } + + shouldComponentUpdate (nextProps, nextState) { + if (!this.state.focus && nextState.focus && this.props.direction === POSITION.AUTO) { + const direction = this.calculateDirection(); + if (this.state.direction !== direction) { + this.setState({ direction }); + return false; + } + } + return true; + } + + handleChange = (keys, event) => { + const key = this.props.multiple ? keys : keys[0]; + const query = this.query(key); + if (this.props.onChange) this.props.onChange(key, event); + this.setState({ focus: false, query }, () => { this.refs.input.blur(); }); + }; + + handleQueryBlur = () => { + if (this.state.focus) this.setState({focus: false}); + }; + + handleQueryChange = (value) => { + this.setState({query: value}); + }; + + handleQueryFocus = () => { + this.refs.suggestions.scrollTop = 0; + this.setState({active: '', focus: true}); + }; + + handleQueryKeyUp = (event) => { + if (event.which === 13 && this.state.active) this.select(this.state.active, event); + if (event.which === 27) this.refs.input.blur(); + if ([40, 38].indexOf(event.which) !== -1) { + const suggestionsKeys = [...this.suggestions().keys()]; + let index = suggestionsKeys.indexOf(this.state.active) + (event.which === 40 ? +1 : -1); + if (index < 0) index = suggestionsKeys.length - 1; + if (index >= suggestionsKeys.length) index = 0; + this.setState({active: suggestionsKeys[index]}); + } + }; + + handleSuggestionHover = (key) => { + this.setState({active: key}); + }; + + calculateDirection () { + if (this.props.direction === 'auto') { + const client = ReactDOM.findDOMNode(this.refs.input).getBoundingClientRect(); + const screen_height = window.innerHeight || document.documentElement.offsetHeight; + const up = client.top > ((screen_height / 2) + client.height); + return up ? 'up' : 'down'; + } else { + return this.props.direction; + } + } + + query (key) { + return !this.props.multiple && this.props.value ? this.source().get(key) : ''; + } + + suggestions () { + const suggest = new Map(); + const query = this.state.query.toLowerCase().trim() || ''; + const values = this.values(); + for (const [key, value] of this.source()) { + if (!values.has(key) && value.toLowerCase().trim().startsWith(query)) { + suggest.set(key, value); + } + } + return suggest; + } + + source () { + const { source: src } = this.props; + if (src.hasOwnProperty('length')) { + return new Map(src.map((item) => [item, item])); + } else { + return new Map(Object.keys(src).map((key) => [key, src[key]])); + } + } + + values () { + const valueMap = new Map(); + const vals = this.props.multiple ? this.props.value : [this.props.value]; + for (const [k, v] of this.source()) { + if (vals.indexOf(k) !== -1) valueMap.set(k, v); + } + return valueMap; + } + + select (key, event) { + events.pauseEvent(event); + const values = this.values(this.props.value); + this.handleChange([key, ...values.keys()], event); + } + + unselect (key, event) { + const values = this.values(this.props.value); + values.delete(key); + this.handleChange([...values.keys()], event); + } + + renderSelected () { + if (this.props.multiple) { + const selectedItems = [...this.values()].map(([key, value]) => { + return
  • {value}
  • ; + }); + + return ; + } + } + + renderSuggestions () { + const suggestions = [...this.suggestions()].map(([key, value]) => { + const className = ClassNames(style.suggestion, {[style.active]: this.state.active === key}); + return ( +
  • + {value} +
  • + ); + }); + + const className = ClassNames(style.suggestions, {[style.up]: this.state.direction === 'up'}); + return ; + } + + render () { + const {error, label, ...other} = this.props; + const className = ClassNames(style.root, { + [style.focus]: this.state.focus + }, this.props.className); + + return ( +
    + {this.renderSelected()} + + {this.renderSuggestions()} +
    + ); + } +} + +export default Autocomplete; diff --git a/components/utils/index.js b/components/utils/index.js index c48fd647..c4b25603 100644 --- a/components/utils/index.js +++ b/components/utils/index.js @@ -4,3 +4,7 @@ import time from './time'; import utils from './utils'; export default {events, prefixer, time, utils}; +export {events}; +export {prefixer}; +export {time}; +export {utils}; diff --git a/server.js b/server.js index 5c2ad483..a0942f30 100644 --- a/server.js +++ b/server.js @@ -17,14 +17,14 @@ app.use(require('webpack-dev-middleware')(compiler, { app.use(require('webpack-hot-middleware')(compiler)); app.get('*', function (req, res) { - res.sendFile(path.join(__dirname, './www/index.html')); + res.sendFile(path.join(__dirname, './spec/index.html')); }); -app.listen(8081, '0.0.0.0', function (err) { +app.listen(8080, '0.0.0.0', function (err) { if (err) { console.log(err); return; } - console.log('Listening at http://0.0.0.0:8081'); + console.log('Listening at http://0.0.0.0:8080'); }); diff --git a/webpack.config.development.js b/webpack.config.development.js index 8c84b122..24d0da98 100644 --- a/webpack.config.development.js +++ b/webpack.config.development.js @@ -23,8 +23,8 @@ module.exports = { loaders: [ { test: /\.(js|jsx)$/, - loader: ['babel'], - include: path.join(__dirname, 'spec') + loader: 'babel', + exclude: /(node_modules)/ }, { test: /\.(scss|css)$/, loader: ExtractTextPlugin.extract('style', 'css?sourceMap&modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]!postcss!sass?sourceMap')