1
Fork 0

Add search bar

This commit is contained in:
viktorstrate 2020-03-05 20:10:43 +01:00
parent c30f5a833a
commit 525edbea07
4 changed files with 233 additions and 19 deletions

View File

@ -17,6 +17,7 @@
"downloadjs": "^1.4.7",
"graphql": "^14.6.0",
"graphql-tag": "^2.10.3",
"lodash": "^4.17.15",
"parcel-bundler": "^1.12.4",
"prettier": "^1.19.1",
"prop-types": "^15.7.2",

View File

@ -1,4 +1,4 @@
import React, { Component } from 'react'
import React from 'react'
import PropTypes from 'prop-types'
import styled from 'styled-components'
import { NavLink } from 'react-router-dom'
@ -8,6 +8,7 @@ import { Query } from 'react-apollo'
import gql from 'graphql-tag'
import { Authorized } from './AuthorizedRoute'
import Helmet from 'react-helmet'
import Header from './components/header/Header'
const adminQuery = gql`
query adminQuery {
@ -76,21 +77,6 @@ const SideButtonLabel = styled.div`
font-size: 16px;
`
const Header = styled.div`
height: 60px;
width: 100%;
position: fixed;
background: white;
top: 0;
/* border-bottom: 1px solid rgba(0, 0, 0, 0.1); */
box-shadow: 0 0 2px rgba(0, 0, 0, 0.3);
`
const Title = styled.h1`
font-size: 36px;
padding: 5px 12px;
`
const Layout = ({ children, title }) => (
<Container>
<Helmet>
@ -132,9 +118,7 @@ const Layout = ({ children, title }) => (
<div style={{ height: 24 }}></div>
</Content>
</Sidebar>
<Header>
<Title>Photoview</Title>
</Header>
<Header />
</Container>
)

View File

@ -0,0 +1,29 @@
import React from 'react'
import styled from 'styled-components'
import SearchBar from './Searchbar'
const Container = styled.div`
height: 60px;
width: 100%;
display: inline-flex;
position: fixed;
background: white;
top: 0;
/* border-bottom: 1px solid rgba(0, 0, 0, 0.1); */
box-shadow: 0 0 2px rgba(0, 0, 0, 0.3);
`
const Title = styled.h1`
font-size: 36px;
padding: 5px 12px;
flex-grow: 1;
`
const Header = () => (
<Container>
<Title>Photoview</Title>
<SearchBar />
</Container>
)
export default Header

View File

@ -0,0 +1,200 @@
import React, { useState } from 'react'
import PropTypes from 'prop-types'
import styled from 'styled-components'
import { useLazyQuery } from '@apollo/react-hooks'
import gql from 'graphql-tag'
import debounce from 'lodash/debounce'
import ProtectedImage from '../photoGallery/ProtectedImage'
import { NavLink } from 'react-router-dom'
const Container = styled.div`
height: 60px;
width: 350px;
margin: 0 12px;
padding: 12px 0;
position: relative;
`
const SearchField = styled.input`
height: 100%;
width: 100%;
border: 1px solid #eee;
border-radius: 4px;
padding: 0 8px;
font-size: 16px;
font-family: Lato, 'Helvetica Neue', Arial, Helvetica, sans-serif;
&:focus {
box-shadow: 0 0 4px #eee;
border-color: #3d82c6;
}
`
const Results = styled.div`
display: ${({ show }) => (show ? 'block' : 'none')};
position: absolute;
width: 100%;
min-height: 40px;
max-height: calc(100vh - 100px);
overflow-y: scroll;
padding: 28px 4px 32px;
background-color: white;
box-shadow: 0 0 4px #eee;
border: 1px solid #ccc;
border-radius: 4px;
top: 50%;
z-index: -1;
${SearchField}:not(:focus) ~ & {
display: none;
}
`
const SEARCH_QUERY = gql`
query searchQuery($query: String!) {
search(query: $query) {
albums {
id
title
thumbnail {
thumbnail {
url
}
}
}
photos {
id
title
thumbnail {
url
}
}
}
}
`
const SearchBar = () => {
const [fetchSearches, fetchResult] = useLazyQuery(SEARCH_QUERY)
const [query, setQuery] = useState('')
const [fetched, setFetched] = useState(false)
let debouncedFetch = null
const fetchEvent = e => {
e.persist()
if (!debouncedFetch) {
debouncedFetch = debounce(() => {
console.log('searching', e.target.value.trim())
fetchSearches({ variables: { query: e.target.value.trim() } })
setFetched(true)
}, 250)
}
setQuery(e.target.value)
if (e.target.value.trim() != '') {
debouncedFetch()
} else {
setFetched(false)
}
}
let results = null
if (query.trim().length > 0 && fetched) {
results = <SearchResults result={fetchResult} />
}
return (
<Container>
<SearchField type="search" placeholder="Search" onChange={fetchEvent} />
{results}
</Container>
)
}
const ResultTitle = styled.h1`
font-size: 20px;
margin: 12px 0 4px;
`
const SearchResults = ({ result }) => {
const { data, loading } = result
const photos = (data && data.search.photos) || []
const albums = (data && data.search.albums) || []
let message = null
if (loading) message = 'Loading results...'
else if (data && photos.length == 0 && albums.length == 0)
message = 'No results found'
const albumElements = albums.map(album => (
<AlbumRow key={album.id} {...album} />
))
const photoElements = photos.map(photo => (
<PhotoRow key={photo.id} {...photo} />
))
return (
<Results
onMouseDown={e => {
// Prevent input blur event
e.preventDefault()
}}
show={data}
>
{message}
<ResultTitle>Albums</ResultTitle>
{albumElements}
<ResultTitle>Photos</ResultTitle>
{photoElements}
</Results>
)
}
const RowLink = styled(NavLink)`
display: flex;
align-items: center;
border-bottom: 1px solid #eee;
color: black;
`
const PhotoSearchThumbnail = styled(ProtectedImage)`
width: 50px;
height: 50px;
object-fit: contain;
`
const AlbumSearchThumbnail = styled(ProtectedImage)`
width: 50px;
height: 50px;
margin: 4px 0;
border-radius: 4px;
border: 1px solid #888;
object-fit: cover;
`
const RowTitle = styled.span`
flex-grow: 1;
padding-left: 8px;
`
const PhotoRow = photo => (
<RowLink to="/">
<PhotoSearchThumbnail src={photo.thumbnail.url} />
<RowTitle>{photo.title}</RowTitle>
</RowLink>
)
const AlbumRow = album => (
<RowLink to={`/album/${album.id}`}>
<AlbumSearchThumbnail src={album.thumbnail.thumbnail.url} />
<RowTitle>{album.title}</RowTitle>
</RowLink>
)
SearchResults.propTypes = {
result: PropTypes.object,
}
export default SearchBar