WIP: Search form in header bar

This commit is contained in:
Tom Raithel
2017-03-20 22:47:46 +01:00
parent 7a6f2d97ac
commit fd029dad9e
7 changed files with 138 additions and 29 deletions

1
assets/icons/close.svg Normal file
View File

@@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="100%" height="100%" viewBox="0 0 22 22" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;"><path d="M18.703,1.405l-7.675,7.732l-7.674,-7.732c-0.54,-0.54 -1.405,-0.54 -1.892,0c-0.54,0.541 -0.54,1.406 0,1.892l7.675,7.731l-7.732,7.675c-0.54,0.54 -0.54,1.405 0,1.892c0.541,0.54 1.406,0.54 1.892,0l7.731,-7.675l7.675,7.675c0.54,0.54 1.405,0.54 1.892,0c0.487,-0.541 0.54,-1.405 0,-1.892l-7.675,-7.675l7.675,-7.674c0.54,-0.54 0.54,-1.405 0,-1.892c-0.484,-0.54 -1.352,-0.54 -1.892,-0.057Z" style="fill:#7e8991;"/></svg>

After

Width:  |  Height:  |  Size: 830 B

View File

@@ -1,11 +1,39 @@
import React from 'react';
import classNames from 'classnames';
import Branding from './Branding';
import Link from './Link';
import LogoLink from './LogoLink';
import Search from './Search';
import { getItemPageNames } from '../../common/config';
export default function Header({ pageName }) {
class Header extends React.Component {
constructor(...args) {
super(...args);
this.state = {
searchOpen: false,
};
}
openSearch = () => {
this.setState({
searchOpen: true,
});
}
closeSearch = () => {
this.setState({
searchOpen: false,
});
}
handleOpenClick = (e) => {
e.preventDefault();
this.openSearch();
}
render() {
const { pageName } = this.props;
const { searchOpen } = this.state;
const smallLogo = pageName !== 'index';
return (
@@ -24,11 +52,17 @@ export default function Header({ pageName }) {
</Link>
</div>
<div className="nav__item">
<a className="icon-link" href="#todo">
<a className="icon-link" href="#" onClick={this.handleOpenClick}>
<span className="icon icon--search icon-link__icon"></span>Search
</a>
<div className={classNames('nav__search', { 'is-open': searchOpen })}>
<Search onClose={this.closeSearch} open={searchOpen} />
</div>
</div>
</div>
</Branding>
);
}
}
export default Header;

View File

@@ -30,7 +30,6 @@ class PageOverview extends React.Component {
e.preventDefault();
this.setState({
...this.state,
ring,
});
}
@@ -66,7 +65,6 @@ class PageOverview extends React.Component {
handleSearchTermChange = (value) => {
this.setState({
...this.state,
search: value,
});
};

View File

@@ -1,14 +1,21 @@
import React from 'react';
import classNames from 'classnames';
export default function Search({ value, onChange, onSubmit = () => {} }) {
export default function Search({ value, onChange, onClose, open = false, onSubmit = () => {} }) {
const closable = typeof onClose === 'function';
const handleSubmit = (e) => {
e.preventDefault();
onSubmit();
};
const handleClose = (e) => {
e.preventDefault();
onClose();
}
return (
<form className="search" onSubmit={handleSubmit}>
<form className={classNames('search', { 'search--closable': closable })} onSubmit={handleSubmit}>
<input
value={value}
type="text"
@@ -16,12 +23,19 @@ export default function Search({ value, onChange, onSubmit = () => {} }) {
className="search__field"
placeholder="What are you looking for?"
/>
<span className="search__button">
<span className={classNames('search__button', { 'is-open': open })}>
<button type="submit" className="button">
<span className="icon icon--search button__icon" />
Search
</button>
</span>
{
closable && (
<a href="#" className={classNames('search__close', { 'is-open': open })} onClick={handleClose}>
<span className="icon icon--close" />
</a>
)
}
</form>
);
}

View File

@@ -26,4 +26,8 @@
&--back {
background-image: url('/assets/icons/back.svg');
}
&--close {
background-image: url('/assets/icons/close.svg');
}
}

View File

@@ -1,9 +1,33 @@
.nav {
white-space: nowrap;
&__item {
display: inline-flex;
position: relative;
& + .nav__item {
margin-left: 20px;
}
}
&__search {
position: absolute;
right: 0;
top: 50%;
visibility: hidden;
overflow: hidden;
width: 0;
margin-top: -25px;
opacity: 0.8;
transition:
width 400ms cubic-bezier(0.24, 1.12, 0.71, 0.98) 100ms,
visibility 0s linear 500ms,
opacity 200ms linear;
&.is-open {
opacity: 1;
width: 600px;
visibility: visible;
transition-delay: 0s;
}
}
}

View File

@@ -35,4 +35,38 @@
margin-top: -18px;
right: 7px;
}
&--closable {
.search {
&__field {
padding-right: 160px;
}
&__button {
right: 50px;
transform: translateX(20px);
transition: transform 450ms cubic-bezier(0.24, 1.12, 0.71, 0.98) 100ms;
&.is-open {
transform: translateX(0);
transition-delay: 250ms;
}
}
&__close {
position: absolute;
padding: 10px;
top: 50%;
margin-top: -21px;
right: 4px;
transform: scale(0.2);
transition: transform 400ms cubic-bezier(0.24, 1.12, 0.71, 0.98);
&.is-open {
transform: rotate(180deg) scale(1);
transition-delay: 300ms;
}
}
}
}
}