WIP: Search form in header bar
This commit is contained in:
1
assets/icons/close.svg
Normal file
1
assets/icons/close.svg
Normal 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 |
@@ -1,34 +1,68 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import classNames from 'classnames';
|
||||||
import Branding from './Branding';
|
import Branding from './Branding';
|
||||||
import Link from './Link';
|
import Link from './Link';
|
||||||
import LogoLink from './LogoLink';
|
import LogoLink from './LogoLink';
|
||||||
|
import Search from './Search';
|
||||||
import { getItemPageNames } from '../../common/config';
|
import { getItemPageNames } from '../../common/config';
|
||||||
|
|
||||||
export default function Header({ pageName }) {
|
class Header extends React.Component {
|
||||||
|
constructor(...args) {
|
||||||
|
super(...args);
|
||||||
|
this.state = {
|
||||||
|
searchOpen: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const smallLogo = pageName !== 'index';
|
openSearch = () => {
|
||||||
|
this.setState({
|
||||||
|
searchOpen: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
closeSearch = () => {
|
||||||
<Branding
|
this.setState({
|
||||||
logoContent={<LogoLink small={smallLogo} />}
|
searchOpen: false,
|
||||||
>
|
});
|
||||||
<div className="nav">
|
}
|
||||||
<div className="nav__item">
|
|
||||||
<Link pageName="help" className="icon-link">
|
handleOpenClick = (e) => {
|
||||||
<span className="icon icon--question icon-link__icon"></span>How to Use Technology Radar?
|
e.preventDefault();
|
||||||
</Link>
|
this.openSearch();
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { pageName } = this.props;
|
||||||
|
const { searchOpen } = this.state;
|
||||||
|
const smallLogo = pageName !== 'index';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Branding
|
||||||
|
logoContent={<LogoLink small={smallLogo} />}
|
||||||
|
>
|
||||||
|
<div className="nav">
|
||||||
|
<div className="nav__item">
|
||||||
|
<Link pageName="help" className="icon-link">
|
||||||
|
<span className="icon icon--question icon-link__icon"></span>How to Use Technology Radar?
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
<div className="nav__item">
|
||||||
|
<Link pageName="overview" className="icon-link">
|
||||||
|
<span className="icon icon--overview icon-link__icon"></span>Technologies Overview
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
<div className="nav__item">
|
||||||
|
<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>
|
</div>
|
||||||
<div className="nav__item">
|
</Branding>
|
||||||
<Link pageName="overview" className="icon-link">
|
);
|
||||||
<span className="icon icon--overview icon-link__icon"></span>Technologies Overview
|
}
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
<div className="nav__item">
|
|
||||||
<a className="icon-link" href="#todo">
|
|
||||||
<span className="icon icon--search icon-link__icon"></span>Search
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Branding>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default Header;
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ class PageOverview extends React.Component {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
...this.state,
|
|
||||||
ring,
|
ring,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -66,7 +65,6 @@ class PageOverview extends React.Component {
|
|||||||
|
|
||||||
handleSearchTermChange = (value) => {
|
handleSearchTermChange = (value) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
...this.state,
|
|
||||||
search: value,
|
search: value,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,14 +1,21 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import classNames from 'classnames';
|
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) => {
|
const handleSubmit = (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
onSubmit();
|
onSubmit();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleClose = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
onClose();
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form className="search" onSubmit={handleSubmit}>
|
<form className={classNames('search', { 'search--closable': closable })} onSubmit={handleSubmit}>
|
||||||
<input
|
<input
|
||||||
value={value}
|
value={value}
|
||||||
type="text"
|
type="text"
|
||||||
@@ -16,12 +23,19 @@ export default function Search({ value, onChange, onSubmit = () => {} }) {
|
|||||||
className="search__field"
|
className="search__field"
|
||||||
placeholder="What are you looking for?"
|
placeholder="What are you looking for?"
|
||||||
/>
|
/>
|
||||||
<span className="search__button">
|
<span className={classNames('search__button', { 'is-open': open })}>
|
||||||
<button type="submit" className="button">
|
<button type="submit" className="button">
|
||||||
<span className="icon icon--search button__icon" />
|
<span className="icon icon--search button__icon" />
|
||||||
Search
|
Search
|
||||||
</button>
|
</button>
|
||||||
</span>
|
</span>
|
||||||
|
{
|
||||||
|
closable && (
|
||||||
|
<a href="#" className={classNames('search__close', { 'is-open': open })} onClick={handleClose}>
|
||||||
|
<span className="icon icon--close" />
|
||||||
|
</a>
|
||||||
|
)
|
||||||
|
}
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,4 +26,8 @@
|
|||||||
&--back {
|
&--back {
|
||||||
background-image: url('/assets/icons/back.svg');
|
background-image: url('/assets/icons/back.svg');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&--close {
|
||||||
|
background-image: url('/assets/icons/close.svg');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,33 @@
|
|||||||
.nav {
|
.nav {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|
||||||
&__item {
|
&__item {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
|
position: relative;
|
||||||
& + .nav__item {
|
& + .nav__item {
|
||||||
margin-left: 20px;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,4 +35,38 @@
|
|||||||
margin-top: -18px;
|
margin-top: -18px;
|
||||||
right: 7px;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user