1
0
mirror of https://github.com/BoostIo/Boostnote synced 2025-12-13 17:56:25 +00:00

Narrow list of tags to related ones

When a tag is selected, the tag list narrows to show only the related
ones: all tags associated to the currently visible notes. Clicking on
the plus sign near another tag narrows the list again to the tags of
notes associated with the firstly AND secondly selected tag. To show
every tags again, press the tag icon on the top-left corner of
Boostnote.

Before:
![screencast](https://i.imgur.com/PwAdhLe.gif)

After:
![screencast](https://i.imgur.com/s3JCaFq.gif)

NOTE: Tags are joined with `&` character (`#` not works) in
`location.pathname` thus it will make the tags with this character
unavailable. Any suggestion to pass multiple values via pathname?
This commit is contained in:
bimlas
2018-03-26 16:48:07 +02:00
parent 25440a26ee
commit 6c542750f4
5 changed files with 90 additions and 26 deletions

View File

@@ -12,13 +12,18 @@ import CSSModules from 'browser/lib/CSSModules'
* @param {bool} isActive
*/
const TagListItem = ({name, handleClickTagListItem, isActive, count}) => (
<button styleName={isActive ? 'tagList-item-active' : 'tagList-item'} onClick={() => handleClickTagListItem(name)}>
<span styleName='tagList-item-name'>
{`# ${name}`}
<span styleName='tagList-item-count'>{count}</span>
</span>
</button>
const TagListItem = ({name, handleClickTagListItem, handleClickNarrowToTag, isActive, count}) => (
<div styleName='tagList-itemContainer'>
<button styleName={isActive ? 'tagList-itemNarrow-active' : 'tagList-itemNarrow'} onClick={() => handleClickNarrowToTag(name)}>
<i className={isActive ? 'fa fa-minus-circle' : 'fa fa-plus-circle'} />
</button>
<button styleName={isActive ? 'tagList-item-active' : 'tagList-item'} onClick={() => handleClickTagListItem(name)}>
<span styleName='tagList-item-name'>
{`# ${name}`}
<span styleName='tagList-item-count'>{count}</span>
</span>
</button>
</div>
)
TagListItem.propTypes = {

View File

@@ -1,10 +1,10 @@
.tagList-item
.tagList-itemContainer
display flex
width 100%
.tagList-item, .tagList-itemNarrow
height 26px
background-color transparent
color $ui-inactive-text-color
padding 0
margin-bottom 5px
text-align left
border none
@@ -20,12 +20,18 @@
color $ui-button-default-color
background-color $ui-button-default--active-backgroundColor
.tagList-item-active
background-color $ui-button-default--active-backgroundColor
.tagList-item
display flex
flex 1
width 100%
height 26px
padding 0
.tagList-itemNarrow
padding 0 4px
.tagList-item-active, .tagList-itemNarrow-active
background-color $ui-button-default--active-backgroundColor
height 26px
margin-bottom 5px
text-align left
border none
@@ -36,10 +42,19 @@
background-color alpha($ui-button-default--active-backgroundColor, 60%)
transition 0.2s
.tagList-item-active
display flex
flex 1
width 100%
padding 0
.tagList-itemNarrow-active
padding 0 4px
.tagList-item-name
display block
flex 1
padding 0 15px
padding 0 8px 0 4px
height 26px
line-height 26px
border-width 0 0 0 2px
@@ -55,7 +70,7 @@
font-size 13px
body[data-theme="white"]
.tagList-item
.tagList-item, .tagList-itemNarrow
color $ui-inactive-text-color
&:hover
color $ui-text-color
@@ -64,7 +79,7 @@ body[data-theme="white"]
color $ui-text-color
background-color $ui-button--active-backgroundColor
.tagList-item-active
.tagList-item-active, .tagList-itemNarrow-active
background-color $ui-button--active-backgroundColor
color $ui-text-color
&:hover
@@ -73,7 +88,7 @@ body[data-theme="white"]
color $ui-text-color
body[data-theme="dark"]
.tagList-item
.tagList-item, .tagList-itemNarrow
color $ui-dark-inactive-text-color
&:hover
color $ui-dark-text-color
@@ -82,7 +97,7 @@ body[data-theme="dark"]
color $ui-dark-text-color
background-color $ui-dark-button--active-backgroundColor
.tagList-item-active
.tagList-item-active, .tagList-itemNarrow-active
background-color $ui-dark-button--active-backgroundColor
color $ui-dark-text-color
&:active

View File

@@ -343,10 +343,11 @@ class NoteList extends React.Component {
}
if (location.pathname.match(/\/tags/)) {
const listOfTags = params.tagname.split('&')
return data.noteMap.map(note => {
return note
}).filter(note => {
return note.tags.includes(params.tagname)
return listOfTags.every((tag) => note.tags.includes(tag))
})
}

View File

@@ -91,7 +91,7 @@ class SideNav extends React.Component {
let component
// TagsMode is not selected
if (!location.pathname.match('/tags') && !location.pathname.match('/alltags')) {
if (!location.pathname.match('/tags') && !location.pathname.match('/alltags') && !location.pathname.match('/narrowToTag')) {
component = (
<div>
<SideNavFilter
@@ -145,10 +145,12 @@ class SideNav extends React.Component {
tagListComponent () {
const { data, location, config } = this.props
let tagList = _.sortBy(data.tagNoteMap.map(
(tag, name) => ({name, size: tag.size})),
['name']
)
const relatedTags = this.getRelatedTags(this.getActiveTags(location.pathname), data.noteMap)
let tagList = _.sortBy(data.tagNoteMap.map((tag, name) => {
return { name, size: tag.size }
}), ['name']).filter(tag => {
return (relatedTags.size === 0) ? true : relatedTags.has(tag.name)
})
if (config.sortTagsBy === 'COUNTER') {
tagList = _.sortBy(tagList, item => (0 - item.size))
}
@@ -158,6 +160,7 @@ class SideNav extends React.Component {
<TagListItem
name={tag.name}
handleClickTagListItem={this.handleClickTagListItem.bind(this)}
handleClickNarrowToTag={this.handleClickNarrowToTag.bind(this)}
isActive={this.getTagActive(location.pathname, tag.name)}
key={tag.name}
count={tag.size}
@@ -167,10 +170,33 @@ class SideNav extends React.Component {
)
}
getRelatedTags (activeTags, noteMap) {
const relatedNotes = noteMap.map(note => {
return {key: note.key, tags: note.tags}
}).filter((note) => {
return activeTags.every((tag) => note.tags.includes(tag))
})
let relatedTags = new Set()
relatedNotes.forEach(note => {
note.tags.map(tag => {
relatedTags.add(tag)
})
})
return relatedTags
}
getTagActive (path, tag) {
const pathTag = this.getActiveTags(path)
return pathTag.includes(tag)
}
getActiveTags (path) {
const pathSegments = path.split('/')
const pathTag = pathSegments[pathSegments.length - 1]
return pathTag === tag
const tags = pathSegments[pathSegments.length - 1]
if (tags === 'alltags') {
return []
}
return tags.split('&')
}
handleClickTagListItem (name) {
@@ -192,6 +218,20 @@ class SideNav extends React.Component {
})
}
handleClickNarrowToTag (name) {
const { router } = this.context
const { location } = this.props
let listOfTags = this.getActiveTags(location.pathname)
if (listOfTags.includes(name)) {
listOfTags = listOfTags.filter(function (currentTag) {
return name !== currentTag
})
} else {
listOfTags.push(name)
}
router.push(`/tags/${listOfTags.join('&')}`)
}
emptyTrash (entries) {
const { dispatch } = this.props
const deletionPromises = entries.map((note) => {

View File

@@ -112,6 +112,9 @@ ReactDOM.render((
<IndexRedirect to='/alltags' />
<Route path=':tagname' />
</Route>
<Route path='narrowToTag'>
<Route path=':tag' />
</Route>
<Route path='storages'>
<IndexRedirect to='/home' />
<Route path=':storageKey'>