Modify search button to be expandable.

*  Accordingly issue#814.

*  Input contract if un-focus input element

* Search text data persisted when contracted/expanded
This commit is contained in:
Ricardo de Arruda 2020-07-03 23:20:44 +03:00
parent e41117c878
commit 2f54532be0
3 changed files with 72 additions and 22 deletions

View file

@ -250,10 +250,17 @@ pre {
word-break: keep-all; word-break: keep-all;
} }
.search-bar { .form-control.search-input {
flex-basis: 10%; float: right !important;
flex-shrink: 3; transition: width 0.5s ease-out 0s !important;
flex-grow: 0.3; }
max-width: 33%;
min-width: 11em; .show-input {
width: 13em !important;
}
.hide-input {
background: transparent !important;
width: 0px !important;
padding: 0 !important;
} }

View file

@ -1,5 +1,5 @@
import { Component, linkEvent } from 'inferno'; import { Component, linkEvent, createRef, RefObject } from 'inferno';
import { Link, withRouter } from 'inferno-router'; import { Link } from 'inferno-router';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { retryWhen, delay, take } from 'rxjs/operators'; import { retryWhen, delay, take } from 'rxjs/operators';
import { WebSocketService, UserService } from '../services'; import { WebSocketService, UserService } from '../services';
@ -45,11 +45,13 @@ interface NavbarState {
siteName: string; siteName: string;
admins: Array<UserView>; admins: Array<UserView>;
searchParam: string; searchParam: string;
toggleSearch: boolean;
} }
class Navbar extends Component<any, NavbarState> { export class Navbar extends Component<any, NavbarState> {
private wsSub: Subscription; private wsSub: Subscription;
private userSub: Subscription; private userSub: Subscription;
private searchTextField: RefObject<HTMLInputElement>;
emptyState: NavbarState = { emptyState: NavbarState = {
isLoggedIn: UserService.Instance.user !== undefined, isLoggedIn: UserService.Instance.user !== undefined,
unreadCount: 0, unreadCount: 0,
@ -60,6 +62,7 @@ class Navbar extends Component<any, NavbarState> {
siteName: undefined, siteName: undefined,
admins: [], admins: [],
searchParam: '', searchParam: '',
toggleSearch: false,
}; };
constructor(props: any, context: any) { constructor(props: any, context: any) {
@ -92,7 +95,7 @@ class Navbar extends Component<any, NavbarState> {
WebSocketService.Instance.getSite(); WebSocketService.Instance.getSite();
this.handleSearchParam = this.handleSearchParam.bind(this); this.searchTextField = createRef();
} }
handleSearchParam(i: Navbar, event: any) { handleSearchParam(i: Navbar, event: any) {
@ -101,10 +104,16 @@ class Navbar extends Component<any, NavbarState> {
} }
updateUrl() { updateUrl() {
this.props.history.push( const searchParam = this.state.searchParam;
`/search/q/${this.state.searchParam}/type/all/sort/topall/page/1`
);
this.setState({ searchParam: '' }); this.setState({ searchParam: '' });
this.setState({ toggleSearch: false });
if (searchParam === '') {
this.context.router.history.push(`/search/`);
} else {
this.context.router.history.push(
`/search/q/${searchParam}/type/all/sort/topall/page/1`
);
}
} }
handleSearchSubmit(i: Navbar, event: any) { handleSearchSubmit(i: Navbar, event: any) {
@ -112,6 +121,24 @@ class Navbar extends Component<any, NavbarState> {
i.updateUrl(); i.updateUrl();
} }
handleSearchBtn(i: Navbar, event: any) {
event.preventDefault();
i.setState({ toggleSearch: true });
i.searchTextField.current.focus();
const offsetWidth = i.searchTextField.current.offsetWidth;
if (i.state.searchParam && offsetWidth > 100) {
i.updateUrl();
}
}
handleSearchBlur(i: Navbar, event: any) {
if (!(event.relatedTarget && event.relatedTarget.name !== 'search-btn')) {
i.state.toggleSearch = false;
i.setState(i.state);
}
}
render() { render() {
return this.navbar(); return this.navbar();
} }
@ -199,16 +226,34 @@ class Navbar extends Component<any, NavbarState> {
</Link> </Link>
</li> </li>
</ul> </ul>
{!this.props.history.location.pathname.match(/^\/search/) && ( {!this.context.router.history.location.pathname.match(
<div class="nav-item search-bar"> /^\/search/
<form onSubmit={linkEvent(this, this.handleSearchSubmit)}> ) && (
<div class="nav-item my-2">
<form
class="form-inline"
onSubmit={linkEvent(this, this.handleSearchSubmit)}
>
<input <input
class="form-control mr-sm-2" class={`form-control mr-0 search-input ${
this.state.toggleSearch ? 'show-input' : 'hide-input'
}`}
onInput={linkEvent(this, this.handleSearchParam)} onInput={linkEvent(this, this.handleSearchParam)}
value={this.state.searchParam} value={this.state.searchParam}
type="search" ref={this.searchTextField}
type="text"
placeholder={i18n.t('search')} placeholder={i18n.t('search')}
onBlur={linkEvent(this, this.handleSearchBlur)}
></input> ></input>
<div class="mx-sm-2">
<button
name="search-btn"
onClick={linkEvent(this, this.handleSearchBtn)}
class="btn btn-secondary"
>
{i18n.t('search')}
</button>
</div>
</form> </form>
</div> </div>
)} )}
@ -457,5 +502,3 @@ class Navbar extends Component<any, NavbarState> {
} }
} }
} }
export default withRouter(Navbar);

2
ui/src/index.tsx vendored
View file

@ -2,7 +2,7 @@ import { render, Component } from 'inferno';
import { BrowserRouter, Route, Switch } from 'inferno-router'; import { BrowserRouter, Route, Switch } from 'inferno-router';
import { Provider } from 'inferno-i18next'; import { Provider } from 'inferno-i18next';
import { Main } from './components/main'; import { Main } from './components/main';
import Navbar from './components/navbar'; import { Navbar } from './components/navbar';
import { Footer } from './components/footer'; import { Footer } from './components/footer';
import { Login } from './components/login'; import { Login } from './components/login';
import { CreatePost } from './components/create-post'; import { CreatePost } from './components/create-post';