mirror of
https://github.com/Alamantus/Lexiconga.git
synced 2025-05-09 11:41:47 +02:00
Improve SearchBox functionality
This commit is contained in:
parent
b5d57393c2
commit
08428f51ac
5 changed files with 307 additions and 250 deletions
|
@ -1,230 +0,0 @@
|
|||
import Inferno from 'inferno';
|
||||
import Component from 'inferno-component';
|
||||
|
||||
import Helper from '../../Helper';
|
||||
|
||||
import dictionary from '../../managers/DictionaryData';
|
||||
|
||||
export class SearchBox extends Component {
|
||||
constructor (props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
searchingIn: 'name'
|
||||
, searchTerm: ''
|
||||
, filteredPartsOfSpeech: []
|
||||
, showHeader: false
|
||||
, showAdvanced: false
|
||||
};
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
if (this.state.showHeader && this.searchBox) {
|
||||
this.searchBox.focus();
|
||||
}
|
||||
}
|
||||
|
||||
search () {
|
||||
const {searchingIn, searchTerm, filteredPartsOfSpeech} = this.state;
|
||||
const searchConfig = {
|
||||
searchingIn
|
||||
, searchTerm
|
||||
, filteredPartsOfSpeech
|
||||
};
|
||||
|
||||
this.props.search(searchConfig);
|
||||
}
|
||||
|
||||
displaySearchHeader () {
|
||||
if (this.state.showHeader) {
|
||||
return (
|
||||
<header className='search-bar is-small'>
|
||||
<div className='search-body'>
|
||||
<div className='search-background'
|
||||
onClick={() => this.hideHeader()} />
|
||||
|
||||
<div className='container'>
|
||||
<div className='box'>
|
||||
<div className='columns is-mobile'>
|
||||
|
||||
<div className='column'>
|
||||
<div className='field has-addons'>
|
||||
<p className='control'>
|
||||
<span className='select'>
|
||||
<select value={this.state.searchingIn}
|
||||
onChange={event => {
|
||||
this.setState({ searchingIn: event.target.value });
|
||||
}}>
|
||||
<option value='name'>Word</option>
|
||||
<option value='definition'>Definition</option>
|
||||
<option value='details'>Details</option>
|
||||
</select>
|
||||
</span>
|
||||
</p>
|
||||
<p className='control is-expanded'>
|
||||
<input className='input' type='text' placeholder='Search Term'
|
||||
ref={input => {
|
||||
this.searchBox = input;
|
||||
}}
|
||||
value={this.state.searchTerm}
|
||||
onChange={event => {
|
||||
console.log(event);
|
||||
this.setState({ searchTerm: event.target.value });
|
||||
}} />
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{this.showFilterOptions()}
|
||||
|
||||
</div>
|
||||
|
||||
<div className='column is-narrow'>
|
||||
<div className='field has-addons'>
|
||||
<a className='delete'
|
||||
onClick={() => this.hideHeader()} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
showFilterOptions () {
|
||||
if (dictionary.partsOfSpeech.length > 0) {
|
||||
const searchMethodSectionJSX = this.state.searchingIn !== 'details'
|
||||
? (
|
||||
<div className='field is-horizontal'>
|
||||
<div className='field-label is-normal'>
|
||||
<label className='label'>Search Method</label>
|
||||
</div>
|
||||
<div className='field-body'>
|
||||
<div className='field'>
|
||||
<p className='control'>
|
||||
<label className='radio'>
|
||||
<input type='radio' name='searchmethod' checked={true} />
|
||||
Contains
|
||||
</label>
|
||||
<label className='radio'>
|
||||
<input type='radio' name='searchmethod' />
|
||||
Starts With
|
||||
</label>
|
||||
<label className='radio'>
|
||||
<input type='radio' name='searchmethod' />
|
||||
Exact
|
||||
</label>
|
||||
</p>
|
||||
</div>
|
||||
<div className='field'>
|
||||
<p className='control'>
|
||||
<span className='help'>
|
||||
<strong>Contains:</strong>
|
||||
Search term is anywhere within the {this.state.searchingIn.capitalize()}
|
||||
</span>
|
||||
<span className='help'>
|
||||
<strong>Starts With:</strong>
|
||||
The {this.state.searchingIn.capitalize()} begins with the search term
|
||||
</span>
|
||||
<span className='help'>
|
||||
<strong>Exact:</strong>
|
||||
Search term matches the {this.state.searchingIn.capitalize()} exactly
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
: null;
|
||||
|
||||
const filterSectionJSX = (
|
||||
<div className='field is-horizontal'>
|
||||
<div className='field-label is-normal'>
|
||||
<label className='label'>Filter</label>
|
||||
</div>
|
||||
<div className='field-body'>
|
||||
<div className='field is-grouped'>
|
||||
{dictionary.partsOfSpeech.map((partOfSpeech) => {
|
||||
return (
|
||||
<p className='control'>
|
||||
<label key={'filterPartOfSpeech' + Date.now()}
|
||||
className='checkbox'>
|
||||
<input type='checkbox' checked={true} />
|
||||
{partOfSpeech}
|
||||
</label>
|
||||
</p>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const advancedSectionJSX = (
|
||||
<div className='column'>
|
||||
<div className='box'>
|
||||
{searchMethodSectionJSX}
|
||||
{filterSectionJSX}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div class='columns'>
|
||||
<div class='column is-narrow'>
|
||||
<div className='field'>
|
||||
<p className='control'>
|
||||
<a className={`button is-link is-small${this.state.showAdvanced ? ' is-active' : ''}`}
|
||||
onClick={() => this.setState({showAdvanced: !this.state.showAdvanced})}>
|
||||
Advanced
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{this.state.showAdvanced ? advancedSectionJSX : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
showHeader () {
|
||||
this.setState({
|
||||
showHeader: true
|
||||
});
|
||||
}
|
||||
|
||||
hideHeader () {
|
||||
this.setState({
|
||||
showHeader: false
|
||||
});
|
||||
}
|
||||
|
||||
render () {
|
||||
return (
|
||||
<div>
|
||||
<div className='field has-addons is-hidden-touch'>
|
||||
<p className='control'>
|
||||
<input className='input' type='text' readonly={true}
|
||||
value={this.state.searchTerm}
|
||||
onClick={() => this.showHeader()} />
|
||||
</p>
|
||||
<p className='control'>
|
||||
<a className='button is-link'
|
||||
onClick={() => this.showHeader()}>
|
||||
Search
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<a className='button is-hidden-desktop'
|
||||
onClick={() => this.showHeader()}>
|
||||
S
|
||||
</a>
|
||||
|
||||
{this.displaySearchHeader()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
6
src/components/management/SearchBox/SearchMethod.js
Normal file
6
src/components/management/SearchBox/SearchMethod.js
Normal file
|
@ -0,0 +1,6 @@
|
|||
export default {
|
||||
contains: 'contains',
|
||||
startsWith: 'start',
|
||||
endsWith: 'end',
|
||||
isExactly: 'exact',
|
||||
};
|
273
src/components/management/SearchBox/index.jsx
Normal file
273
src/components/management/SearchBox/index.jsx
Normal file
|
@ -0,0 +1,273 @@
|
|||
import Inferno from 'inferno';
|
||||
import Component from 'inferno-component';
|
||||
|
||||
import Helper from '../../../Helper';
|
||||
|
||||
import dictionary from '../../../managers/DictionaryData';
|
||||
|
||||
import METHOD from './SearchMethod.js';
|
||||
|
||||
export class SearchBox extends Component {
|
||||
constructor (props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
searchingIn: 'name',
|
||||
searchMethod: METHOD.contains,
|
||||
searchTerm: '',
|
||||
filteredPartsOfSpeech: [],
|
||||
showHeader: false,
|
||||
showAdvanced: false,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
if (this.state.showHeader && this.searchBox) {
|
||||
this.searchBox.focus();
|
||||
}
|
||||
}
|
||||
|
||||
search () {
|
||||
const {searchingIn, searchMethod, searchTerm, filteredPartsOfSpeech} = this.state;
|
||||
const searchConfig = {
|
||||
searchingIn,
|
||||
searchMethod,
|
||||
searchTerm,
|
||||
filteredPartsOfSpeech,
|
||||
};
|
||||
|
||||
this.props.search(searchConfig);
|
||||
}
|
||||
|
||||
displaySearchHeader () {
|
||||
if (this.state.showHeader) {
|
||||
return (
|
||||
<header className='search-bar is-small'>
|
||||
<div className='search-body'>
|
||||
<div className='search-background'
|
||||
onClick={ this.hideHeader.bind(this) } />
|
||||
|
||||
<div className='container'>
|
||||
<div className='box'>
|
||||
<div className='columns is-mobile'>
|
||||
|
||||
<div className='column'>
|
||||
<div className='field has-addons'>
|
||||
<p className='control'>
|
||||
<span className='select'>
|
||||
<select value={ this.state.searchingIn }
|
||||
onChange={event => {
|
||||
this.setState({ searchingIn: event.target.value });
|
||||
}}>
|
||||
<option value='name'>Word</option>
|
||||
<option value='definition'>Definition</option>
|
||||
<option value='details'>Details</option>
|
||||
</select>
|
||||
</span>
|
||||
</p>
|
||||
<p className='control is-expanded'>
|
||||
<input className='input' type='text' placeholder='Search Term'
|
||||
ref={input => {
|
||||
this.searchBox = input;
|
||||
}}
|
||||
value={ this.state.searchTerm }
|
||||
onChange={event => {
|
||||
console.log(event);
|
||||
this.setState({ searchTerm: event.target.value });
|
||||
}} />
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{ this.showFilterOptions() }
|
||||
|
||||
</div>
|
||||
|
||||
<div className='column is-narrow'>
|
||||
<div className='field has-addons'>
|
||||
<a className='delete'
|
||||
onClick={ this.hideHeader.bind(this) }
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
showFilterOptions () {
|
||||
if (this.props.partsOfSpeech.length > 0) {
|
||||
const searchMethodSectionJSX = this.state.searchingIn !== 'details'
|
||||
? (
|
||||
<div className='field is-horizontal'>
|
||||
<div className='field-label is-normal'>
|
||||
<label className='label'>Search Method</label>
|
||||
</div>
|
||||
<div className='field-body'>
|
||||
<div className='field'>
|
||||
<p className='control'>
|
||||
<label className='radio'
|
||||
title={ `Search term is anywhere within the ${ this.state.searchingIn.capitalize() }` }
|
||||
>
|
||||
<input type='radio' name='searchmethod'
|
||||
value={ METHOD.contains }
|
||||
checked={ this.state.searchMethod === METHOD.contains }
|
||||
onClick={event => {
|
||||
if (event.currentTarget.checked) {
|
||||
this.setState({
|
||||
searchMethod: event.currentTarget.value,
|
||||
});
|
||||
}
|
||||
}}
|
||||
/>
|
||||
Contains
|
||||
</label>
|
||||
<label className='radio'
|
||||
title={ `The ${ this.state.searchingIn.capitalize() } begins with the search term` }
|
||||
>
|
||||
<input type='radio' name='searchmethod'
|
||||
value={ METHOD.startsWith }
|
||||
checked={ this.state.searchMethod === METHOD.startsWith }
|
||||
onClick={event => {
|
||||
if (event.currentTarget.checked) {
|
||||
this.setState({
|
||||
searchMethod: event.currentTarget.value,
|
||||
});
|
||||
}
|
||||
}}
|
||||
/>
|
||||
Starts With
|
||||
</label>
|
||||
<label className='radio'
|
||||
title={ `The ${ this.state.searchingIn.capitalize() } ends with the search term` }
|
||||
>
|
||||
<input type='radio' name='searchmethod'
|
||||
value={ METHOD.endsWith }
|
||||
checked={ this.state.searchMethod === METHOD.endsWith }
|
||||
onClick={event => {
|
||||
if (event.currentTarget.checked) {
|
||||
this.setState({
|
||||
searchMethod: event.currentTarget.value,
|
||||
});
|
||||
}
|
||||
}}
|
||||
/>
|
||||
Ends With
|
||||
</label>
|
||||
<label className='radio'
|
||||
title={ `Search term matches the ${ this.state.searchingIn.capitalize() } exactly` }
|
||||
>
|
||||
<input type='radio' name='searchmethod'
|
||||
value={ METHOD.isExactly }
|
||||
checked={ this.state.searchMethod === METHOD.isExactly }
|
||||
onClick={event => {
|
||||
if (event.currentTarget.checked) {
|
||||
this.setState({
|
||||
searchMethod: event.currentTarget.value,
|
||||
});
|
||||
}
|
||||
}}
|
||||
/>
|
||||
Exact
|
||||
</label>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
: null;
|
||||
|
||||
const filterSectionJSX = (
|
||||
<div className='field is-horizontal'>
|
||||
<div className='field-label is-normal'>
|
||||
<label className='label'>Filter</label>
|
||||
</div>
|
||||
<div className='field-body'>
|
||||
<div className='field is-grouped'>
|
||||
{
|
||||
this.props.partsOfSpeech.map(partOfSpeech => {
|
||||
return (
|
||||
<p className='control'>
|
||||
<label key={ 'filterPartOfSpeech' + Date.now() }
|
||||
className='checkbox'>
|
||||
<input type='checkbox' checked={ true } />
|
||||
{ partOfSpeech }
|
||||
</label>
|
||||
</p>
|
||||
);
|
||||
})
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const advancedSectionJSX = (
|
||||
<div className='column'>
|
||||
<div className='box'>
|
||||
{ searchMethodSectionJSX }
|
||||
{ filterSectionJSX }
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div class='columns'>
|
||||
<div class='column is-narrow'>
|
||||
<div className='field'>
|
||||
<p className='control'>
|
||||
<a className={ `button is-link is-small${ this.state.showAdvanced ? ' is-active' : '' }` }
|
||||
onClick={ () => this.setState({ showAdvanced: !this.state.showAdvanced }) }>
|
||||
Advanced
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{ this.state.showAdvanced ? advancedSectionJSX : null }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
showHeader () {
|
||||
this.setState({
|
||||
showHeader: true
|
||||
});
|
||||
}
|
||||
|
||||
hideHeader () {
|
||||
this.setState({
|
||||
showHeader: false
|
||||
});
|
||||
}
|
||||
|
||||
render () {
|
||||
return (
|
||||
<div>
|
||||
<div className='field has-addons is-hidden-touch'>
|
||||
<p className='control'>
|
||||
<input className='input' type='text' readonly={ true }
|
||||
value={ this.state.searchTerm }
|
||||
onClick={ this.showHeader.bind(this) } />
|
||||
</p>
|
||||
<p className='control'>
|
||||
<a className='button is-link'
|
||||
onClick={ this.showHeader.bind(this) }>
|
||||
Search
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<a className='button is-hidden-desktop'
|
||||
onClick={ this.showHeader.bind(this) }>
|
||||
S
|
||||
</a>
|
||||
|
||||
{ this.displaySearchHeader() }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -8,7 +8,7 @@ export class Header extends Component {
|
|||
super(props);
|
||||
|
||||
this.state = {
|
||||
displayNavMenu: false
|
||||
displayNavMenu: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -17,14 +17,15 @@ export class Header extends Component {
|
|||
<nav className='nav'>
|
||||
<div className='nav-left'>
|
||||
<a href='/' className='nav-item image'>
|
||||
<img src={`./logo.${(typeof SVGRect !== 'undefined') ? 'svg' : 'png'}`} alt='Lexiconga Logo' />
|
||||
<img src={ `./logo.${ (typeof SVGRect !== 'undefined') ? 'svg' : 'png' }` } alt='Lexiconga Logo' />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div className='nav-center'>
|
||||
<div className='nav-item'>
|
||||
<SearchBox
|
||||
search={searchConfig => this.props.search(searchConfig)} />
|
||||
partsOfSpeech={ this.props.partsOfSpeech }
|
||||
search={ searchConfig => this.props.search(searchConfig) } />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -35,7 +36,7 @@ export class Header extends Component {
|
|||
<span></span>
|
||||
</span>
|
||||
|
||||
<div className={`nav-right nav-menu${this.state.displayNavMenu ? ' is-active' : ''}`}>
|
||||
<div className={ `nav-right nav-menu${ this.state.displayNavMenu ? ' is-active' : '' }` }>
|
||||
<span className='nav-item'>
|
||||
<a className='button'>
|
||||
Login
|
||||
|
|
|
@ -19,41 +19,47 @@ class App extends Component {
|
|||
super(props);
|
||||
|
||||
this.state = {
|
||||
name: dictionary.name
|
||||
, specification: dictionary.specification
|
||||
, description: dictionary.description
|
||||
, partsOfSpeech: dictionary.partsOfSpeech
|
||||
, displayedWords: []
|
||||
, searchConfig: null
|
||||
name: dictionary.name,
|
||||
specification: dictionary.specification,
|
||||
description: dictionary.description,
|
||||
partsOfSpeech: dictionary.partsOfSpeech,
|
||||
displayedWords: [],
|
||||
searchConfig: null,
|
||||
}
|
||||
}
|
||||
|
||||
get dictionaryInfo () {
|
||||
const {name, specification, description, partsOfSpeech} = this.state;
|
||||
const info = {
|
||||
name
|
||||
, specification
|
||||
, description
|
||||
, partsOfSpeech
|
||||
name,
|
||||
specification,
|
||||
description,
|
||||
partsOfSpeech,
|
||||
};
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
updatePartsOfSpeech () {
|
||||
this.setState({
|
||||
partsOfSpeech: dictionary.partsOfSpeech,
|
||||
});
|
||||
}
|
||||
|
||||
updateDisplayedWords () {
|
||||
// const {searchIn, searchTerm, filteredPartsOfSpeech} = this.state.searchConfig;
|
||||
|
||||
// TODO: Sort out searching to remove this temporary solution.
|
||||
dictionary.wordsPromise.then(words => {
|
||||
this.setState({
|
||||
displayedWords: words
|
||||
displayedWords: words,
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
search (searchConfig) {
|
||||
this.setState({
|
||||
searchConfig: searchConfig
|
||||
searchConfig: searchConfig,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -61,12 +67,13 @@ class App extends Component {
|
|||
return (
|
||||
<div>
|
||||
<Header
|
||||
search={searchConfig => this.search(searchConfig)} />
|
||||
partsOfSpeech={ this.state.partsOfSpeech }
|
||||
search={ searchConfig => this.search(searchConfig) } />
|
||||
|
||||
<MainDisplay
|
||||
dictionaryInfo={this.dictionaryInfo}
|
||||
wordsToDisplay={this.state.displayedWords}
|
||||
updateDisplay={() => this.updateDisplayedWords()} />
|
||||
dictionaryInfo={ this.dictionaryInfo }
|
||||
wordsToDisplay={ this.state.displayedWords }
|
||||
updateDisplay={ this.updateDisplayedWords.bind(this) } />
|
||||
|
||||
<Footer />
|
||||
</div>
|
||||
|
|
Loading…
Add table
Reference in a new issue