1
0
mirror of https://github.com/dap-ps/discover.git synced 2025-03-03 18:30:32 +00:00

Add search box

This commit is contained in:
Onuwa Nnachi Isaac 2019-09-11 11:39:22 +01:00 committed by Andy Tudhope
parent 0a1db006c5
commit e6e63f3b2a
14 changed files with 338 additions and 21 deletions

View File

@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.92798 11.856C2.65405 11.856 0 9.2019 0 5.92798C0 2.65405 2.65405 0 5.92798 0C9.2019 0 11.856 2.65405 11.856 5.92798C11.856 7.20825 11.45 8.39368 10.76 9.36267L15.7087 14.3114C16.0945 14.6973 16.0945 15.3228 15.7087 15.7086C15.3228 16.0945 14.6973 16.0945 14.3115 15.7086L9.36279 10.76C8.3938 11.4501 7.20825 11.856 5.92798 11.856ZM5.92798 9.87988C8.1106 9.87988 9.88013 8.1106 9.88013 5.92798C9.88013 3.74536 8.1106 1.97595 5.92798 1.97595C3.74536 1.97595 1.97607 3.74536 1.97607 5.92798C1.97607 8.1106 3.74536 9.87988 5.92798 9.87988Z" fill="#939BA1"/>
</svg>

After

Width:  |  Height:  |  Size: 709 B

View File

@ -0,0 +1,8 @@
import { connect } from 'react-redux'
import Search from './Search'
const mapStateToProps = state => ({
dappState: state.dapps,
})
export default connect(mapStateToProps)(Search)

View File

@ -0,0 +1,67 @@
import React from 'react'
import PropTypes from 'prop-types'
import searchIcon from '../../assets/images/search.svg'
import SearchResultItem from './SearchResultList'
import { DappState } from '../../data/dapp'
import styles from './Search.module.scss'
class Search extends React.Component {
constructor(props) {
super(props)
this.state = {
dapps: [],
isSearching: false,
}
this.handleChange = this.handleChange.bind(this)
}
handleChange(e) {
const { value } = e.target
const { dappState } = this.props
if (value.length > 1) {
const dapps = dappState.dapps.filter(dapp => dapp.name.startsWith(value))
this.setState({ dapps, isSearching: true })
} else if (value === '') {
this.setState({
isSearching: false,
dapps: [],
})
}
}
render() {
const { searchStyle, onSearch, searchResultStyle } = this.props
const { dapps, isSearching } = this.state
return (
<>
<div className={styles.container}>
<img src={searchIcon} alt="Search Icon" width="16" height="16" />
<input
type="text"
onChange={e => this.handleChange(e)}
className={[styles.search, searchStyle].join(' ')}
placeholder="Search Dapps"
onSearch={onSearch(isSearching)}
/>
</div>
<div className={[styles.searchResults, searchResultStyle].join(' ')}>
<SearchResultItem showActionButtons={false} dapps={dapps} />
</div>
</>
)
}
}
Search.propTypes = {
searchStyle: PropTypes.string,
searchResultStyle: PropTypes.string,
dappState: PropTypes.instanceOf(DappState).isRequired,
onSearch: PropTypes.func.isRequired,
}
Search.defaultProps = {
searchStyle: null,
searchResultStyle: null,
}
export default Search

View File

@ -0,0 +1,29 @@
@import '../../styles/_variables.scss';
.container {
display: flex;
flex: 1;
border: 1px solid #eef2f5;
border-radius: calculateRem(4);
overflow: hidden;
margin: calculateRem(16);
img {
padding: calculateRem(5);
align-self: center;
}
}
.search {
font-family: 'Inter';
font-size: calculateRem(15);
flex: 1;
border: none;
outline: none;
&::placeholder {
color: #939ba1;
}
}
.searchResults {
z-index: 99;
}

View File

@ -0,0 +1,51 @@
import React from 'react'
import PropTypes from 'prop-types'
import ReactImageFallback from 'react-image-fallback'
import { DappModel } from '../../../utils/models'
import styles from './SearchResultItem.module.scss'
import icon from '../../../assets/images/icon.svg'
const SearchResultItem = props => {
const { dapp } = props
const { name, description, image, url } = dapp
return (
<div className={`${styles.container}`}>
<div className={styles.imgWrapper}>
<ReactImageFallback
className={styles.image}
src={image}
fallbackImage={icon}
alt="App icon"
/>
</div>
<div className={styles.column}>
<>
<h2 className={styles.header}>{name}</h2>
<p
className={styles.description}
style={{ WebkitBoxOrient: 'vertical' }}
>
{description}
</p>
<a className={styles.link} href={{ url }}>
{url}
&nbsp;&rarr;
</a>
</>
</div>
</div>
)
}
SearchResultItem.defaultProps = {
visible: true,
}
SearchResultItem.propTypes = {
dapp: PropTypes.shape(DappModel).isRequired,
visible: PropTypes.bool,
}
export default SearchResultItem

View File

@ -0,0 +1,59 @@
@import '../../../styles/variables';
.container {
display: flex;
margin: calculateRem(10);
font-family: 'Inter';
}
.header {
color: $headline-color !important;
font-size: calculateRem(15);
line-height: calculateRem(22);
margin-bottom: calculateRem(2);
margin-top: calculateRem(12);
font-weight: 600;
}
.description {
color: #939ba1 !important;
font-size: calculateRem(13);
}
.column {
align-self: flex-end;
overflow-wrap: break-word;
}
.image {
max-width: calculateRem(40);
max-height: calculateRem(40);
margin-top: calculateRem(15);
margin-right: calculateRem(16);
border-radius: 50%;
}
p {
margin: 0;
}
.link {
margin-top: calculateRem(2);
font-size: calculateRem(12);
}
.imgWrapper {
flex: 0 0 auto;
}
@media (max-width: $desktop) {
.column {
width: 78vw;
}
}
@media (min-width: $desktop) {
.column {
width: 24vw;
}
}

View File

@ -0,0 +1,3 @@
import SearchResultItem from './SearchResultItem'
export default SearchResultItem

View File

@ -0,0 +1,16 @@
import React from 'react'
import PropTypes from 'prop-types'
import { DappListModel } from '../../../utils/models'
import SearchResultItem from '../SearchResultItem'
const SearchResultList = ({ dapps }) =>
dapps &&
dapps.map((dapp, i) => (
<SearchResultItem dapp={dapp} key={dapp.id} position={i + 1} />
))
SearchResultList.propTypes = {
dapps: DappListModel.isRequired,
}
export default SearchResultList

View File

@ -0,0 +1,3 @@
import SearchResultList from './SearchResultList'
export default SearchResultList

View File

@ -0,0 +1,3 @@
import Search from './Search.container'
export default Search

View File

@ -2,6 +2,7 @@ import React from 'react'
import PropTypes from 'prop-types'
import CategoryIcon from '../../common/components/CategoryIcon'
import ViewAll from '../../common/components/ViewAll'
import Search from '../../common/components/Search'
import categories from '../../common/utils/categories'
import humanise from '../../common/utils/humanise'
import dropdownArrows from '../../common/assets/images/dropdown-arrows.svg'
@ -10,13 +11,14 @@ import styles from './CategorySelector.module.scss'
class CategorySelector extends React.Component {
constructor(props) {
super(props)
this.state = { open: false }
this.state = { open: false, isSearching: false }
this.toggle = this.toggle.bind(this)
this.updateCategory = this.updateCategory.bind(this)
this.container = React.createRef()
this.onClickSubmit = this.onClickSubmit.bind(this)
this.onClickHighestRanked = this.onClickHighestRanked.bind(this)
this.onClickRecentlyAdded = this.onClickRecentlyAdded.bind(this)
this.onSearch = this.onSearch.bind(this)
}
componentDidMount() {
@ -43,6 +45,10 @@ class CategorySelector extends React.Component {
this.setState({ open: false })
}
onSearch(isSearching) {
this.setState({ isSearching })
}
onClickHighestRanked(e) {
const { onClickCloseDesktopMenu } = this.props
onClickCloseDesktopMenu()
@ -77,6 +83,7 @@ class CategorySelector extends React.Component {
showSubmitDApp,
} = this.props
let { open } = this.state
const { isSearching } = this.state
if (alwaysOpen === true) open = true
return (
@ -85,29 +92,45 @@ class CategorySelector extends React.Component {
style={open ? { visible: 'block' } : { display: 'none' }}
className={styles.open}
>
<div className={styles.openHeader}>
<Search
searchStyle={styles.search}
onSearch={this.onSearch}
searchResultStyle={styles.searchResult}
/>
<div
className={styles.openHeader}
style={isSearching ? { display: 'none' } : { display: 'flex' }}
>
<h2>Categories</h2>
<ViewAll size="small" />
</div>
{categories.map(c => (
<button
className={
c.key === category
? [styles.openButton, styles.selected].join(' ')
: styles.openButton
}
key={c.key}
type="button"
value={c.key}
onClick={this.updateCategory}
>
<CategoryIcon category={c.key} />
{c.value}
</button>
))}
<div
className={styles.categories}
style={isSearching ? { display: 'none' } : { display: 'block' }}
>
{categories.map(c => (
<button
className={
c.key === category
? [styles.openButton, styles.selected].join(' ')
: styles.openButton
}
key={c.key}
type="button"
value={c.key}
onClick={this.updateCategory}
>
<CategoryIcon category={c.key} />
{c.value}
</button>
))}
</div>
{showLists && (
<>
<div
className={styles.categories}
style={isSearching ? { display: 'none' } : { display: 'block' }}
>
<div className={`${styles.openHeader} ${styles.spacing}`}>
<h2>Lists</h2>
</div>
@ -151,7 +174,7 @@ class CategorySelector extends React.Component {
</svg>
{'Recently added'}
</button>
</>
</div>
)}
{showSubmitDApp && (
@ -159,6 +182,7 @@ class CategorySelector extends React.Component {
className={`${styles.openButton} ${styles.submitDapp}`}
type="button"
onClick={this.onClickSubmit}
style={isSearching ? { display: 'none' } : { display: 'flex' }}
>
<svg
width="20"

View File

@ -1,5 +1,18 @@
@import '../../common/styles/variables';
.search {
width: 85%;
margin: calculateRem(9);
}
.searchResult {
position: relative;
}
.categories {
opacity: 1;
}
.open {
border-radius: 8px;
box-shadow: 0px 4px 12px rgba(0, 34, 51, 0.08),

View File

@ -4,6 +4,7 @@ import RecentlyAdded from '../RecentlyAdded'
import HighestRanked from '../HighestRanked'
import Categories from '../Categories'
import FeaturedDapps from '../../common/components/FeatureDapps'
import Search from '../../common/components/Search'
import Footer from '../Footer'
import LoadingHome from '../LoadingHome'
import featured from '../../common/data/featured'
@ -13,11 +14,19 @@ import DesktopMenu from '../DesktopMenu/DesktopMenu.container'
class Home extends React.Component {
constructor(props) {
super(props)
this.state = {}
this.state = {
isSearching: false,
}
this.onSearch = this.onSearch.bind(this)
}
onSearch(isSearching) {
this.setState({ isSearching })
}
render() {
const { dapps } = this.props
const { isSearching } = this.state
const loaded = dapps.loaded
return (
@ -28,6 +37,13 @@ class Home extends React.Component {
<h2 className={styles.headline}>Discover</h2>
</div>
<DesktopMenu />
<div className={styles.mobileSearch}>
<Search
searchStyle={styles.search}
searchResultStyle={styles.searchResult}
onSearch={this.onSearch}
/>
</div>
<FeaturedDapps featured={featured} />
<Categories />
<HighestRanked />

View File

@ -9,3 +9,25 @@
font-size: calculateRem(17);
margin: 0;
}
.mobileSearch {
display: none;
}
.search {
display: flex;
width: 100%;
align-self: center;
padding: calculateRem(9);
}
@media (max-width: $desktop) {
.mobileSearch {
display: block;
}
.searchResult {
width: 98vw;
position: absolute;
background-color: #ffffff;
}
}