From 9ca0a8fddca5b05fc049b9efae17d73ac2a5844c Mon Sep 17 00:00:00 2001 From: Sol Date: Tue, 4 Aug 2020 19:01:15 +0100 Subject: [PATCH] initial search bar feature added on new branch --- src/actions/index.js | 8 +++ src/components/Layout.js | 17 +++++++ src/components/Map.jsx | 1 + src/components/Search.jsx | 67 +++++++++++++++++++++++++ src/components/SearchRow.jsx | 39 +++++++++++++++ src/reducers/app.js | 12 ++++- src/scss/search.scss | 97 ++++++++++++++++++++++++++++++++++++ 7 files changed, 240 insertions(+), 1 deletion(-) create mode 100644 src/components/Search.jsx create mode 100644 src/components/SearchRow.jsx create mode 100644 src/scss/search.scss diff --git a/src/actions/index.js b/src/actions/index.js index 1ddd64a..36c2463 100644 --- a/src/actions/index.js +++ b/src/actions/index.js @@ -334,6 +334,14 @@ export function toggleCover () { } } +export const UPDATE_SEARCH_QUERY = 'UPDATE_SEARCH_QUERY' +export function updateSearchQuery (searchQuery) { + return { + type: UPDATE_SEARCH_QUERY, + searchQuery + } +} + // ERRORS export const FETCH_SOURCE_ERROR = 'FETCH_SOURCE_ERROR' diff --git a/src/components/Layout.js b/src/components/Layout.js index 37ba4fd..4a1eaaa 100644 --- a/src/components/Layout.js +++ b/src/components/Layout.js @@ -21,6 +21,7 @@ import TemplateCover from './TemplateCover' import colors from '../common/global' import { binarySearch, insetSourceFrom, findDescriptionInFilterTree } from '../common/utilities' import { isMobile } from 'react-device-detect' +import Search from './Search.jsx' class Dashboard extends React.Component { constructor (props) { @@ -35,6 +36,11 @@ class Dashboard extends React.Component { this.getCategoryColor = this.getCategoryColor.bind(this) this.findEventIdx = this.findEventIdx.bind(this) this.onKeyDown = this.onKeyDown.bind(this) +<<<<<<< HEAD +======= + this.selectNarrativeStep = this.selectNarrativeStep.bind(this) + this.updateSearchQuery = this.updateSearchQuery.bind(this) +>>>>>>> 575b8f5... initial search bar feature added } componentDidMount () { @@ -226,6 +232,11 @@ class Dashboard extends React.Component { } } } + + updateSearchQuery (e) { + let queryString = e.target.value; + this.props.actions.updateSearchQuery(queryString) + } render () { const { actions, app, domain, ui, features } = this.props @@ -312,6 +323,12 @@ class Dashboard extends React.Component { notifications={domain.notifications} onToggle={actions.markNotificationsRead} /> + {app.source ? ( this.alignLayers()) map.on('zoomstart', () => { if (this.svgRef.current !== null) this.svgRef.current.classList.add('hide') }) diff --git a/src/components/Search.jsx b/src/components/Search.jsx new file mode 100644 index 0000000..dcc6498 --- /dev/null +++ b/src/components/Search.jsx @@ -0,0 +1,67 @@ +import React from 'react' + +import '../scss/search.scss' + +import SearchRow from './SearchRow.jsx' + +class Search extends React.Component { + constructor(props) { + super(props) + this.state = { + isFolded : true, + searchResults: [], + queryString: '' + } + this.onButtonClick = this.onButtonClick.bind(this) + this.updateSearchQueryResults = this.updateSearchQueryResults.bind(this) + } + + componentDidUpdate (prevProps, prevState) { + if (prevProps.queryString !== this.props.queryString) { + this.updateSearchQueryResults (this.props.queryString) + } + } + + onButtonClick () { + this.setState(prevState => { + return { isFolded : !prevState.isFolded } + }) + } + + updateSearchQueryResults (queryString) { + let searchResults + if (queryString === '') { + searchResults = [] + } else { + searchResults = this.props.events.filter(event => + event.description.toLowerCase().includes(queryString.toLowerCase()) || event.location.includes(queryString) || event.category.includes(queryString) + ) + } + this.setState({ + searchResults: searchResults + }) + } + + render () { + return ( +
+
+ search +
+
+
+ + close +
+
+ {this.state.searchResults.map(result => { + return + })} +
+
+
+ ) + } +} + +export default Search; diff --git a/src/components/SearchRow.jsx b/src/components/SearchRow.jsx new file mode 100644 index 0000000..b5018f8 --- /dev/null +++ b/src/components/SearchRow.jsx @@ -0,0 +1,39 @@ +import React from 'react' + +const SearchRow = ({ description, category, location, date, query }) => { + function getHighlightedText(text, highlight) { + // Split text on highlight term, include term itself into parts, ignore case + const parts = text.split(new RegExp(`(${highlight})`, 'gi')); + return {parts.map(part => part.toLowerCase() === highlight.toLowerCase() ? {part} : part)}; + } + + function getShortDescription(text, searchQuery) { + var regexp = new RegExp(`(([^ ]* ){0,6}[a-zA-Z]*${searchQuery.toLowerCase()}[a-zA-Z]*( [^ ]*){0,5})`, 'gm') + let parts = text.toLowerCase().match(regexp) + for (var x=0; x < (parts ? parts.length : 0); x++) { + parts[x] = '...'+parts[x] + } + const firstLine = [text.match('(([^ ]* ){0,10})', 'm')[0]] + return parts || firstLine; + } + + return ( +
+
+
+ location_on +

{getHighlightedText(location, query)}

+
+
+ event +

{getHighlightedText(date, query)}

+
+
+

{getShortDescription(description, query).map(match => { + return {getHighlightedText(match, query)}...

+ })}

+
+ ) +} + +export default SearchRow diff --git a/src/reducers/app.js b/src/reducers/app.js index 4c960dd..d4f88a2 100644 --- a/src/reducers/app.js +++ b/src/reducers/app.js @@ -22,7 +22,8 @@ import { FETCH_ERROR, FETCH_SOURCE_ERROR, SET_LOADING, - SET_NOT_LOADING + SET_NOT_LOADING, + UPDATE_SEARCH_QUERY } from '../actions' function updateHighlighted (appState, action) { @@ -229,6 +230,13 @@ function setNotLoading (appState) { } } +function updateSearchQuery (appState, action) { + return { + ...appState, + searchQuery: action.searchQuery + } +} + function app (appState = initial.app, action) { switch (action.type) { case UPDATE_HIGHLIGHTED: @@ -275,6 +283,8 @@ function app (appState = initial.app, action) { return setLoading(appState) case SET_NOT_LOADING: return setNotLoading(appState) + case UPDATE_SEARCH_QUERY: + return updateSearchQuery(appState, action) default: return appState } diff --git a/src/scss/search.scss b/src/scss/search.scss new file mode 100644 index 0000000..deac6ee --- /dev/null +++ b/src/scss/search.scss @@ -0,0 +1,97 @@ +#search-bar-icon-container { + position: absolute; + background-color: black; + color: #a0a0a0; + border: #a0a0a0 solid 0.1px; + top: 10px; + margin-left: 10px; + height: 24px; + padding: 10px; + &:hover { + cursor: pointer; + color: white; + } +} + +.search-bar-overlay { + background-color: black; + height: 100vh; + width: 400px; + position: absolute; + transition: 0.2s ease; +} + +.search-bar-input { + width: 300px; + margin: 20px; + line-height: 40px; + font-size: 15px; + color: gray; + padding-left: 15px; + background: black; + border: 1px solid #a0a0a0; + &:focus { + outline: none; + } +} + +#close-search-overlay { + color: #a0a0a0; + vertical-align: middle; + font-size: 30px; + transition: 0.2s ease; + &:hover { + color: white; + cursor: pointer; + } +} + +.folded { + left: -400px; + transition: 0.2s ease; +} + +.search-outer-container { + position: absolute; + left: 110px; + &.narrative-mode { + left: 0; + } +} + +.search-row { + color: white; + padding-left: 10px; + padding-right: 10px; + padding-top: 10px; + padding-bottom: 10px; + background-color: grey; + border-bottom: 1px white solid; + border-top: 1px white solid; +} + +.search-row > p { + margin: 0; +} + +.search-results { + height: calc(100% - 332px); + overflow: auto; +} + +div.location-date-container { + margin-top: 10px; + margin-bottom: 10px; +} + +div.location-date-container > div { + width: 50%; + display: inline-block; + vertical-align: top; +} + +div.location-date-container > div > p { + display: inline; + line-height: 24px; + vertical-align: top; +} \ No newline at end of file