house Toolbar components in folder

This commit is contained in:
Lachlan Kermode
2019-02-14 15:35:35 +00:00
parent 5174814244
commit 0758a0d56e
7 changed files with 19 additions and 19 deletions

View File

@@ -0,0 +1,37 @@
import React from 'react'
import SitesIcon from '../presentational/Icons/Sites'
import CoverIcon from '../presentational/Icons/Cover'
import InfoIcon from '../presentational/Icons/Info'
function BottomActions (props) {
function renderToggles () {
return [
<div className='bottom-action-block'>
{process.env.features.USE_SITES ? <SitesIcon
isActive={props.sites.enabled}
onClickHandler={props.sites.toggle}
/> : null}
</div>,
<div className='botttom-action-block'>
<InfoIcon
isActive={props.info.enabled}
onClickHandler={props.info.toggle}
/>
</div>,
<div className='botttom-action-block'>
{process.env.features.USE_COVER ? <CoverIcon
onClickHandler={props.cover.toggle}
/> : null}
</div>
]
}
return (
<div className='bottom-actions'>
{renderToggles()}
</div>
)
}
export default BottomActions

View File

@@ -0,0 +1,38 @@
import React from 'react'
import Checkbox from '../presentational/Checkbox'
import copy from '../../js/data/copy.json'
export default ({
categories,
activeCategories,
onCategoryFilter,
language
}) => {
function renderCategoryTree () {
return (
<div>
{categories.map(cat => {
return (<li
key={cat.category.replace(/ /g, '_')}
className={'tag-filter active'}
style={{ marginLeft: '20px' }}
>
<Checkbox
label={cat.category}
isActive={activeCategories.includes(cat.category)}
onClickCheckbox={() => onCategoryFilter(cat.category)}
/>
</li>)
})}
</div>
)
}
return (
<div className='react-innertabpanel'>
<h2>{copy[language].toolbar.categories}</h2>
<p>{copy[language].toolbar.explore_by_category__description}</p>
{renderCategoryTree()}
</div>
)
}

View File

@@ -0,0 +1,218 @@
import React from 'react'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import * as actions from '../../actions'
import * as selectors from '../../selectors'
import { Tabs, TabPanel } from 'react-tabs'
import Search from './Search'
import TagListPanel from './TagListPanel'
import CategoriesListPanel from './CategoriesListPanel'
import BottomActions from './BottomActions'
import copy from '../../js/data/copy.json'
import { trimAndEllipse } from '../../js/utilities.js'
class Toolbar extends React.Component {
constructor (props) {
super(props)
this.state = { _selected: -1 }
}
selectTab (selected) {
const _selected = (this.state._selected === selected) ? -1 : selected
this.setState({ _selected })
}
renderClosePanel () {
return (
<div className='panel-header' onClick={() => this.selectTab(-1)}>
<div className='caret' />
</div>
)
}
renderSearch () {
if (process.env.features.USE_SEARCH) {
return (
<TabPanel>
<Search
language={this.props.language}
tags={this.props.tags}
categories={this.props.categories}
tagFilters={this.props.tagFilters}
categoryFilters={this.props.categoryFilters}
filter={this.props.filter}
/>
</TabPanel>
)
}
}
goToNarrative (narrative) {
this.selectTab(-1) // set all unselected within this component
this.props.methods.onSelectNarrative(narrative)
}
renderToolbarNarrativePanel () {
return (
<TabPanel>
<h2>{copy[this.props.language].toolbar.narrative_panel_title}</h2>
<p>{copy[this.props.language].toolbar.narrative_summary}</p>
{this.props.narratives.map((narr) => {
return (
<div className='panel-action action'>
<button style={{ backgroundColor: '#000' }} onClick={() => { this.goToNarrative(narr) }}>
<p>{narr.label}</p>
<p><small>{trimAndEllipse(narr.description, 120)}</small></p>
</button>
</div>
)
})}
</TabPanel>
)
}
renderToolbarCategoriesPanel () {
if (process.env.features.CATEGORIES_AS_TAGS) {
return (
<TabPanel>
<CategoriesListPanel
categories={this.props.categories}
activeCategories={this.props.activeCategories}
onCategoryFilter={this.props.methods.onCategoryFilter}
language={this.props.language}
/>
</TabPanel>
)
}
}
renderToolbarTagPanel () {
if (process.env.features.USE_TAGS &&
this.props.tags.children) {
return (
<TabPanel>
<TagListPanel
tags={this.props.tags}
activeTags={this.props.activeTags}
onTagFilter={this.props.methods.onTagFilter}
language={this.props.language}
/>
</TabPanel>
)
}
return null
}
renderToolbarTab (_selected, label, iconKey) {
const isActive = (this.state._selected === _selected)
let classes = (isActive) ? 'toolbar-tab active' : 'toolbar-tab'
return (
<div className={classes} onClick={() => { this.selectTab(_selected) }}>
<i className='material-icons'>{iconKey}</i>
<div className='tab-caption'>{label}</div>
</div>
)
}
renderToolbarPanels () {
let classes = (this.state._selected >= 0) ? 'toolbar-panels' : 'toolbar-panels folded'
return (
<div className={classes}>
{this.renderClosePanel()}
<Tabs selectedIndex={this.state._selected}>
{this.renderToolbarNarrativePanel()}
{this.renderToolbarCategoriesPanel()}
{this.renderToolbarTagPanel()}
</Tabs>
</div>
)
}
renderToolbarNavs () {
if (this.props.narratives) {
return this.props.narratives.map((nar, idx) => {
const isActive = (idx === this.state._selected)
let classes = (isActive) ? 'toolbar-tab active' : 'toolbar-tab'
return (
<div className={classes} onClick={() => { this.selectTab(idx) }}>
<div className='tab-caption'>{nar.label}</div>
</div>
)
})
}
return null
}
renderToolbarTabs () {
let title = copy[this.props.language].toolbar.title
if (process.env.title) title = process.env.title
const narrativesLabel = copy[this.props.language].toolbar.narratives_label
const tagsLabel = copy[this.props.language].toolbar.tags_label
const categoriesLabel = 'Categories' // TODO:
const isTags = this.props.tags && this.props.tags.children
const isCategories = true
return (
<div className='toolbar'>
<div className='toolbar-header'><p>{title}</p></div>
<div className='toolbar-tabs'>
{this.renderToolbarTab(0, narrativesLabel, 'timeline')}
{(isCategories) ? this.renderToolbarTab(1, categoriesLabel, 'widgets') : null}
{(isTags) ? this.renderToolbarTab(2, tagsLabel, 'filter_list') : null}
</div>
<BottomActions
info={{
enabled: this.props.infoShowing,
toggle: this.props.actions.toggleInfoPopup
}}
sites={{
enabled: this.props.sitesShowing,
toggle: this.props.actions.toggleSites
}}
cover={{
toggle: this.props.actions.toggleCover
}}
/>
</div>
)
}
render () {
const { isNarrative } = this.props
return (
<div id='toolbar-wrapper' className={`toolbar-wrapper ${(isNarrative) ? 'narrative-mode' : ''}`}>
{this.renderToolbarTabs()}
{this.renderToolbarPanels()}
</div>
)
}
}
function mapStateToProps (state) {
return {
tags: selectors.getTagTree(state),
categories: selectors.getCategories(state),
narratives: selectors.selectNarratives(state),
language: state.app.language,
activeTags: selectors.getActiveTags(state),
activeCategories: selectors.getActiveCategories(state),
viewFilters: state.app.filters.views,
features: state.app.features,
narrative: state.app.narrative,
sitesShowing: state.app.flags.isShowingSites,
infoShowing: state.app.flags.isInfopopup
}
}
function mapDispatchToProps (dispatch) {
return {
actions: bindActionCreators(actions, dispatch)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Toolbar)

View File

@@ -0,0 +1,72 @@
/* global fetch */
import React from 'react'
import copy from '../../js/data/copy.json'
import TagFilter from './TagFilter'
export default class Search extends React.Component {
constructor (props) {
super(props)
this.state = {
searchValue: undefined,
searchResults: []
}
this.handleSearchChange = this.handleSearchChange.bind(this)
this.handleSearchSubmit = this.handleSearchSubmit.bind(this)
}
handleSearchSubmit (e) {
e.preventDefault()
fetch(`api/search/${this.state.searchValue}`)
.then(response => response.json())
.then(json => {
this.setState({
searchResults: json.tags
})
})
}
handleSearchChange (event) {
this.setState({ searchValue: event.target.value })
}
renderSearchResults () {
return (
this.state.searchResults.map(tag => {
return (
<TagFilter
isShowTree
tags={this.props.tags}
categories={this.props.categories}
tagFilters={this.props.tagFilters}
categoryFilters={this.props.categoryFilters}
filter={this.props.filter}
tag={tag}
isCategory={this.props.isCategory}
/>
)
})
)
}
render () {
return (
<div className='search-content'>
<h2>{copy[this.props.language].toolbar.panels.search.title}</h2>
<form onSubmit={this.handleSearchSubmit}>
<input
value={this.state.searchValue}
onChange={this.handleSearchChange}
autoFocus
type='text'
name='search-input'
placeholder={copy[this.props.language].toolbar.panels.search.placeholder}
/>
</form>
<ul>
{this.renderSearchResults()}
</ul>
</div>
)
}
}

View File

@@ -0,0 +1,82 @@
import React from 'react'
import Checkbox from '../presentational/Checkbox'
function TagFilter (props) {
function isActive () {
if (props.isCategory) {
return props.categoryFilters.includes(props.tag.id)
}
return props.tagFilters.includes(props.tag.id)
}
function onClickTag () {
if (isActive()) {
props.filter({
tags: props.tagFilters.filter(element => element !== props.tag.id)
})
} else {
props.filter({
tags: props.tagFilters.concat(props.tag.id)
})
}
}
function onClickCategory () {
if (isActive()) {
props.filter({
categories: props.categoryFilters.filter(element => element !== props.tag.id)
})
} else {
props.filter({
categories: props.categoryFilters.concat(props.tag.id)
})
}
}
function renderTag () {
const tag = props.tag
let classes = (isActive()) ? 'tag-filter active' : 'tag-filter'
let label = `${tag.name} ( ${tag.mentions} )`
if (props.isShowTree) {
label = `${tag.group} > ${tag.subgroup} > ${tag.name} ( ${tag.mentions} )`
}
return (
<li
key={props.tag.id}
className={classes}
>
<Checkbox
isActive={isActive()}
label={label}
onClickCheckbox={() => onClickTag()}
/>
</li>
)
}
function renderCategory () {
const category = props.categories[props.tag.id]
let classes = (isActive()) ? 'tag-filter active' : 'tag-filter'
if (category) {
return (
<li
key={props.tag.id}
className={classes}
>
<Checkbox
isActive={isActive()}
label={`${category.name} ( ${category.counts} )`}
onClickCheckbox={onClickCategory}
/>
</li>
)
}
return (<div />)
}
if (props.isCategory) return (renderCategory())
return (renderTag())
}
export default TagFilter

View File

@@ -0,0 +1,45 @@
import React from 'react'
import Checkbox from '../presentational/Checkbox'
import copy from '../../js/data/copy.json'
function TagListPanel ({
tags,
activeTags,
onTagFilter,
language
}) {
function createNodeComponent (node, depth) {
return (
<li
key={node.key.replace(/ /g, '_')}
className={'tag-filter active'}
style={{ marginLeft: `${depth * 20}px` }}
>
<Checkbox
label={node.key}
isActive={activeTags.includes(node.key)}
onClickCheckbox={() => onTagFilter(node.key)}
/>
</li>
)
}
function renderTree () {
/* NOTE: only render first layer of tags */
return (
<div>
{Object.values(tags.children).map(tag => createNodeComponent(tag, 1))}
</div>
)
}
return (
<div className='react-innertabpanel'>
<h2>{copy[language].toolbar.tags}</h2>
<p>{copy[language].toolbar.explore_by_tag__description}</p>
{renderTree()}
</div>
)
}
export default TagListPanel