diff --git a/client/constants.js b/client/constants.js index aaf7637c..f769c33e 100644 --- a/client/constants.js +++ b/client/constants.js @@ -121,6 +121,9 @@ export const SET_ASSETS = 'SET_ASSETS'; export const TOGGLE_DIRECTION = 'TOGGLE_DIRECTION'; export const SET_SORTING = 'SET_SORTING'; +export const SET_SORT_PARAMS = 'SET_SORT_PARAMS'; +export const SET_SEARCH_TERM = 'SET_SEARCH_TERM'; +export const CLOSE_SKETCHLIST_MODAL = 'CLOSE_SKETCHLIST_MODAL'; export const START_LOADING = 'START_LOADING'; export const STOP_LOADING = 'STOP_LOADING'; diff --git a/client/images/magnifyingglass.svg b/client/images/magnifyingglass.svg new file mode 100644 index 00000000..38f54010 --- /dev/null +++ b/client/images/magnifyingglass.svg @@ -0,0 +1,13 @@ + + + + + + + + diff --git a/client/modules/IDE/actions/sorting.js b/client/modules/IDE/actions/sorting.js index 0e306b46..8feb9b12 100644 --- a/client/modules/IDE/actions/sorting.js +++ b/client/modules/IDE/actions/sorting.js @@ -25,3 +25,14 @@ export function toggleDirectionForField(field) { field }; } + +export function setSearchTerm(searchTerm) { + return { + type: ActionTypes.SET_SEARCH_TERM, + query: searchTerm + }; +} + +export function resetSearchTerm() { + return setSearchTerm(''); +} diff --git a/client/modules/IDE/components/Searchbar.jsx b/client/modules/IDE/components/Searchbar.jsx new file mode 100644 index 00000000..f3047b91 --- /dev/null +++ b/client/modules/IDE/components/Searchbar.jsx @@ -0,0 +1,91 @@ +import PropTypes from 'prop-types'; +import React from 'react'; +import InlineSVG from 'react-inlinesvg'; +import { bindActionCreators } from 'redux'; +import { connect } from 'react-redux'; +import { throttle } from 'lodash'; +import * as SortingActions from '../actions/sorting'; + +const searchIcon = require('../../../images/magnifyingglass.svg'); + +class Searchbar extends React.Component { + constructor(props) { + super(props); + this.state = { + searchValue: this.props.searchTerm + }; + this.throttledSearchChange = throttle(this.searchChange, 500); + } + + componentWillUnmount() { + this.props.resetSearchTerm(); + } + + handleResetSearch = () => { + this.setState({ searchValue: '' }, () => { + this.props.resetSearchTerm(); + }); + } + + handleSearchEnter = (e) => { + if (e.key === 'Enter') { + this.props.setSearchTerm(this.state.searchValue); + } + } + + searchChange = (value) => { + this.props.setSearchTerm(this.state.searchValue); + }; + + handleSearchChange = (e) => { + this.setState({ searchValue: e.target.value }, () => { + this.throttledSearchChange(this.state.searchValue); + }); + } + + render() { + const { searchValue } = this.state; + return ( +
+ + + +
+ ); + } +} + +Searchbar.propTypes = { + searchTerm: PropTypes.string.isRequired, + setSearchTerm: PropTypes.func.isRequired, + resetSearchTerm: PropTypes.func.isRequired +}; + +function mapStateToProps(state) { + return { + searchTerm: state.search.searchTerm + }; +} + +function mapDispatchToProps(dispatch) { + return bindActionCreators(Object.assign({}, SortingActions), dispatch); +} + +export default connect(mapStateToProps, mapDispatchToProps)(Searchbar); diff --git a/client/modules/IDE/components/SketchList.jsx b/client/modules/IDE/components/SketchList.jsx index b430df14..bf68aed3 100644 --- a/client/modules/IDE/components/SketchList.jsx +++ b/client/modules/IDE/components/SketchList.jsx @@ -30,7 +30,6 @@ class SketchListRowBase extends React.Component { isFocused: false }; } - onFocusComponent = () => { this.setState({ isFocused: true }); } diff --git a/client/modules/IDE/pages/IDEView.jsx b/client/modules/IDE/pages/IDEView.jsx index b2ae1ebb..179c9739 100644 --- a/client/modules/IDE/pages/IDEView.jsx +++ b/client/modules/IDE/pages/IDEView.jsx @@ -30,6 +30,7 @@ import * as ConsoleActions from '../actions/console'; import { getHTMLFile } from '../reducers/files'; import Overlay from '../../App/components/Overlay'; import SketchList from '../components/SketchList'; +import Searchbar from '../components/Searchbar'; import AssetList from '../components/AssetList'; import About from '../components/About'; import Feedback from '../components/Feedback'; @@ -371,6 +372,7 @@ class IDEView extends React.Component { title="Open a Sketch" previousPath={this.props.ide.previousPath} > + { + switch (action.type) { + case ActionTypes.SET_SEARCH_TERM: + return { ...state, searchTerm: action.query }; + default: + return state; + } +}; diff --git a/client/modules/IDE/selectors/projects.js b/client/modules/IDE/selectors/projects.js index 9ff655d3..b2230826 100644 --- a/client/modules/IDE/selectors/projects.js +++ b/client/modules/IDE/selectors/projects.js @@ -6,9 +6,27 @@ import { DIRECTION } from '../actions/sorting'; const getSketches = state => state.sketches; const getField = state => state.sorting.field; const getDirection = state => state.sorting.direction; +const getSearchTerm = state => state.search.searchTerm; + +const getFilteredSketches = createSelector( + getSketches, + getSearchTerm, + (sketches, search) => { + if (search) { + const searchStrings = sketches.map((sketch) => { + const smallSketch = { + name: sketch.name + }; + return { ...sketch, searchString: Object.values(smallSketch).join(' ').toLowerCase() }; + }); + return searchStrings.filter(sketch => sketch.searchString.includes(search.toLowerCase())); + } + return sketches; + } +); const getSortedSketches = createSelector( - getSketches, + getFilteredSketches, getField, getDirection, (sketches, field, direction) => { diff --git a/client/reducers.js b/client/reducers.js index 057dbd62..4a173a8f 100644 --- a/client/reducers.js +++ b/client/reducers.js @@ -10,6 +10,7 @@ import sketches from './modules/IDE/reducers/projects'; import toast from './modules/IDE/reducers/toast'; import console from './modules/IDE/reducers/console'; import assets from './modules/IDE/reducers/assets'; +import search from './modules/IDE/reducers/search'; import sorting from './modules/IDE/reducers/sorting'; import loading from './modules/IDE/reducers/loading'; @@ -21,6 +22,7 @@ const rootReducer = combineReducers({ user, project, sketches, + search, sorting, editorAccessibility, toast, diff --git a/client/styles/abstracts/_variables.scss b/client/styles/abstracts/_variables.scss index c4a29fc8..820fb05a 100644 --- a/client/styles/abstracts/_variables.scss +++ b/client/styles/abstracts/_variables.scss @@ -57,7 +57,10 @@ $themes: ( input-text-color: #333, input-border-color: #b5b5b5, about-list-text-color: #4a4a4a, - search-background-color: #ebebeb, + search-background-color: #ffffff, + search-clear-background-color: #e9e9e9, + search-hover-text-color: #ffffff, + search-hover-background-color: #4d4d4d, dropdown-color: #414141, keyboard-shortcut-color: #757575, nav-hover-color: $p5js-pink, @@ -106,7 +109,10 @@ $themes: ( input-text-color: #333, input-border-color: #b5b5b5, about-list-text-color: #f4f4f4, - search-background-color: #ebebeb, + search-background-color: #ffffff, + search-clear-background-color: #4f4f4f, + search-hover-text-color: #ffffff, + search-hover-background-color: $p5js-pink, dropdown-color: #dadada, keyboard-shortcut-color: #B5B5B5, nav-hover-color: $p5js-pink, @@ -155,6 +161,9 @@ $themes: ( input-border-color: #b5b5b5, about-list-text-color: #f4f4f4, search-background-color: $white, + search-clear-background-color: #444, + search-hover-text-color: $black, + search-hover-background-color: $yellow, dropdown-color: #e1e1e1, keyboard-shortcut-color: #e1e1e1, nav-hover-color: $yellow, diff --git a/client/styles/components/_overlay.scss b/client/styles/components/_overlay.scss index 2065a230..58602f5b 100644 --- a/client/styles/components/_overlay.scss +++ b/client/styles/components/_overlay.scss @@ -24,6 +24,7 @@ flex-flow: column; max-height: 80%; max-width: 65%; + position: relative; } .overlay__header { diff --git a/client/styles/components/_searchbar.scss b/client/styles/components/_searchbar.scss new file mode 100644 index 00000000..47a5fc84 --- /dev/null +++ b/client/styles/components/_searchbar.scss @@ -0,0 +1,61 @@ +.searchbar { + position: absolute; + display: flex; + padding-left: #{17 / $base-font-size}rem; + right: #{50 / $base-font-size}rem; + top: #{14 / $base-font-size}rem; +} + +.searchbar__input { + width: #{240 / $base-font-size}rem; + height: #{36 / $base-font-size}rem; + border: solid 0.5px; + padding-left: #{36 / $base-font-size}rem; + padding-right: #{38 / $base-font-size}rem; + @include themify() { + border-color: getThemifyVariable('input-border-color'); + background-color: getThemifyVariable('search-background-color'); + } +} + +.searchbar__button { + width: #{31 / $base-font-size}rem; + height: #{36 / $base-font-size}rem; + position: absolute; + padding: 0; + border-right: solid 1px; + @include themify() { + border-right-color: getThemifyVariable('input-border-color'); + } +} + +.searchbar__icon { + display: inline-block; + & svg { + width: #{22 / $base-font-size}rem; + height: #{27 / $base-font-size}rem; + transform: scaleX(-1); + @include themify() { + fill: getThemifyVariable('input-text-color'); + } + } +} + +.searchbar__clear-button { + font-weight: bold; + font-size: #{10 / $base-font-size}rem; + text-align: center; + border-radius: 2px; + align-self: center; + position: absolute; + padding: #{3 / $base-font-size}rem #{4 / $base-font-size}rem; + left: #{216 / $base-font-size}rem;; + @include themify() { + color: getThemifyVariable('primary-text-color'); + background-color: getThemifyVariable('search-clear-background-color'); + &:hover, &:focus { + color: getThemifyVariable('search-hover-text-color'); + background-color: getThemifyVariable('search-hover-background-color'); + } + } +} diff --git a/client/styles/components/_sketch-list.scss b/client/styles/components/_sketch-list.scss index dd7252ae..41fee261 100644 --- a/client/styles/components/_sketch-list.scss +++ b/client/styles/components/_sketch-list.scss @@ -2,7 +2,8 @@ overflow-y: auto; max-width: 100%; width: #{1000 / $base-font-size}rem; - min-height: #{400 / $base-font-size}rem; + height: #{628 / $base-font-size}rem; + // min-height: #{400 / $base-font-size}rem; } .sketches-table { @@ -71,7 +72,6 @@ } } - .sketches-table thead { font-size: #{12 / $base-font-size}rem; @include themify() { diff --git a/client/styles/main.scss b/client/styles/main.scss index 3d9bf977..7bcca6dd 100644 --- a/client/styles/main.scss +++ b/client/styles/main.scss @@ -21,6 +21,7 @@ @import 'components/preferences'; @import 'components/reset-password'; @import 'components/new-password'; +@import 'components/searchbar'; @import 'components/sketch-list'; @import 'components/sidebar'; @import 'components/modal';