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:  After:  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:
@@ -12,13 +12,18 @@ import CSSModules from 'browser/lib/CSSModules'
|
|||||||
* @param {bool} isActive
|
* @param {bool} isActive
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const TagListItem = ({name, handleClickTagListItem, isActive, count}) => (
|
const TagListItem = ({name, handleClickTagListItem, handleClickNarrowToTag, isActive, count}) => (
|
||||||
<button styleName={isActive ? 'tagList-item-active' : 'tagList-item'} onClick={() => handleClickTagListItem(name)}>
|
<div styleName='tagList-itemContainer'>
|
||||||
<span styleName='tagList-item-name'>
|
<button styleName={isActive ? 'tagList-itemNarrow-active' : 'tagList-itemNarrow'} onClick={() => handleClickNarrowToTag(name)}>
|
||||||
{`# ${name}`}
|
<i className={isActive ? 'fa fa-minus-circle' : 'fa fa-plus-circle'} />
|
||||||
<span styleName='tagList-item-count'>{count}</span>
|
</button>
|
||||||
</span>
|
<button styleName={isActive ? 'tagList-item-active' : 'tagList-item'} onClick={() => handleClickTagListItem(name)}>
|
||||||
</button>
|
<span styleName='tagList-item-name'>
|
||||||
|
{`# ${name}`}
|
||||||
|
<span styleName='tagList-item-count'>{count}</span>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
|
|
||||||
TagListItem.propTypes = {
|
TagListItem.propTypes = {
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
.tagList-item
|
.tagList-itemContainer
|
||||||
display flex
|
display flex
|
||||||
width 100%
|
|
||||||
|
.tagList-item, .tagList-itemNarrow
|
||||||
height 26px
|
height 26px
|
||||||
background-color transparent
|
background-color transparent
|
||||||
color $ui-inactive-text-color
|
color $ui-inactive-text-color
|
||||||
padding 0
|
|
||||||
margin-bottom 5px
|
margin-bottom 5px
|
||||||
text-align left
|
text-align left
|
||||||
border none
|
border none
|
||||||
@@ -20,12 +20,18 @@
|
|||||||
color $ui-button-default-color
|
color $ui-button-default-color
|
||||||
background-color $ui-button-default--active-backgroundColor
|
background-color $ui-button-default--active-backgroundColor
|
||||||
|
|
||||||
.tagList-item-active
|
.tagList-item
|
||||||
background-color $ui-button-default--active-backgroundColor
|
|
||||||
display flex
|
display flex
|
||||||
|
flex 1
|
||||||
width 100%
|
width 100%
|
||||||
height 26px
|
|
||||||
padding 0
|
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
|
margin-bottom 5px
|
||||||
text-align left
|
text-align left
|
||||||
border none
|
border none
|
||||||
@@ -36,10 +42,19 @@
|
|||||||
background-color alpha($ui-button-default--active-backgroundColor, 60%)
|
background-color alpha($ui-button-default--active-backgroundColor, 60%)
|
||||||
transition 0.2s
|
transition 0.2s
|
||||||
|
|
||||||
|
.tagList-item-active
|
||||||
|
display flex
|
||||||
|
flex 1
|
||||||
|
width 100%
|
||||||
|
padding 0
|
||||||
|
|
||||||
|
.tagList-itemNarrow-active
|
||||||
|
padding 0 4px
|
||||||
|
|
||||||
.tagList-item-name
|
.tagList-item-name
|
||||||
display block
|
display block
|
||||||
flex 1
|
flex 1
|
||||||
padding 0 15px
|
padding 0 8px 0 4px
|
||||||
height 26px
|
height 26px
|
||||||
line-height 26px
|
line-height 26px
|
||||||
border-width 0 0 0 2px
|
border-width 0 0 0 2px
|
||||||
@@ -55,7 +70,7 @@
|
|||||||
font-size 13px
|
font-size 13px
|
||||||
|
|
||||||
body[data-theme="white"]
|
body[data-theme="white"]
|
||||||
.tagList-item
|
.tagList-item, .tagList-itemNarrow
|
||||||
color $ui-inactive-text-color
|
color $ui-inactive-text-color
|
||||||
&:hover
|
&:hover
|
||||||
color $ui-text-color
|
color $ui-text-color
|
||||||
@@ -64,7 +79,7 @@ body[data-theme="white"]
|
|||||||
color $ui-text-color
|
color $ui-text-color
|
||||||
background-color $ui-button--active-backgroundColor
|
background-color $ui-button--active-backgroundColor
|
||||||
|
|
||||||
.tagList-item-active
|
.tagList-item-active, .tagList-itemNarrow-active
|
||||||
background-color $ui-button--active-backgroundColor
|
background-color $ui-button--active-backgroundColor
|
||||||
color $ui-text-color
|
color $ui-text-color
|
||||||
&:hover
|
&:hover
|
||||||
@@ -73,7 +88,7 @@ body[data-theme="white"]
|
|||||||
color $ui-text-color
|
color $ui-text-color
|
||||||
|
|
||||||
body[data-theme="dark"]
|
body[data-theme="dark"]
|
||||||
.tagList-item
|
.tagList-item, .tagList-itemNarrow
|
||||||
color $ui-dark-inactive-text-color
|
color $ui-dark-inactive-text-color
|
||||||
&:hover
|
&:hover
|
||||||
color $ui-dark-text-color
|
color $ui-dark-text-color
|
||||||
@@ -82,7 +97,7 @@ body[data-theme="dark"]
|
|||||||
color $ui-dark-text-color
|
color $ui-dark-text-color
|
||||||
background-color $ui-dark-button--active-backgroundColor
|
background-color $ui-dark-button--active-backgroundColor
|
||||||
|
|
||||||
.tagList-item-active
|
.tagList-item-active, .tagList-itemNarrow-active
|
||||||
background-color $ui-dark-button--active-backgroundColor
|
background-color $ui-dark-button--active-backgroundColor
|
||||||
color $ui-dark-text-color
|
color $ui-dark-text-color
|
||||||
&:active
|
&:active
|
||||||
|
|||||||
@@ -343,10 +343,11 @@ class NoteList extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (location.pathname.match(/\/tags/)) {
|
if (location.pathname.match(/\/tags/)) {
|
||||||
|
const listOfTags = params.tagname.split('&')
|
||||||
return data.noteMap.map(note => {
|
return data.noteMap.map(note => {
|
||||||
return note
|
return note
|
||||||
}).filter(note => {
|
}).filter(note => {
|
||||||
return note.tags.includes(params.tagname)
|
return listOfTags.every((tag) => note.tags.includes(tag))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ class SideNav extends React.Component {
|
|||||||
let component
|
let component
|
||||||
|
|
||||||
// TagsMode is not selected
|
// 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 = (
|
component = (
|
||||||
<div>
|
<div>
|
||||||
<SideNavFilter
|
<SideNavFilter
|
||||||
@@ -145,10 +145,12 @@ class SideNav extends React.Component {
|
|||||||
|
|
||||||
tagListComponent () {
|
tagListComponent () {
|
||||||
const { data, location, config } = this.props
|
const { data, location, config } = this.props
|
||||||
let tagList = _.sortBy(data.tagNoteMap.map(
|
const relatedTags = this.getRelatedTags(this.getActiveTags(location.pathname), data.noteMap)
|
||||||
(tag, name) => ({name, size: tag.size})),
|
let tagList = _.sortBy(data.tagNoteMap.map((tag, name) => {
|
||||||
['name']
|
return { name, size: tag.size }
|
||||||
)
|
}), ['name']).filter(tag => {
|
||||||
|
return (relatedTags.size === 0) ? true : relatedTags.has(tag.name)
|
||||||
|
})
|
||||||
if (config.sortTagsBy === 'COUNTER') {
|
if (config.sortTagsBy === 'COUNTER') {
|
||||||
tagList = _.sortBy(tagList, item => (0 - item.size))
|
tagList = _.sortBy(tagList, item => (0 - item.size))
|
||||||
}
|
}
|
||||||
@@ -158,6 +160,7 @@ class SideNav extends React.Component {
|
|||||||
<TagListItem
|
<TagListItem
|
||||||
name={tag.name}
|
name={tag.name}
|
||||||
handleClickTagListItem={this.handleClickTagListItem.bind(this)}
|
handleClickTagListItem={this.handleClickTagListItem.bind(this)}
|
||||||
|
handleClickNarrowToTag={this.handleClickNarrowToTag.bind(this)}
|
||||||
isActive={this.getTagActive(location.pathname, tag.name)}
|
isActive={this.getTagActive(location.pathname, tag.name)}
|
||||||
key={tag.name}
|
key={tag.name}
|
||||||
count={tag.size}
|
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) {
|
getTagActive (path, tag) {
|
||||||
|
const pathTag = this.getActiveTags(path)
|
||||||
|
return pathTag.includes(tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
getActiveTags (path) {
|
||||||
const pathSegments = path.split('/')
|
const pathSegments = path.split('/')
|
||||||
const pathTag = pathSegments[pathSegments.length - 1]
|
const tags = pathSegments[pathSegments.length - 1]
|
||||||
return pathTag === tag
|
if (tags === 'alltags') {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
return tags.split('&')
|
||||||
}
|
}
|
||||||
|
|
||||||
handleClickTagListItem (name) {
|
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) {
|
emptyTrash (entries) {
|
||||||
const { dispatch } = this.props
|
const { dispatch } = this.props
|
||||||
const deletionPromises = entries.map((note) => {
|
const deletionPromises = entries.map((note) => {
|
||||||
|
|||||||
@@ -112,6 +112,9 @@ ReactDOM.render((
|
|||||||
<IndexRedirect to='/alltags' />
|
<IndexRedirect to='/alltags' />
|
||||||
<Route path=':tagname' />
|
<Route path=':tagname' />
|
||||||
</Route>
|
</Route>
|
||||||
|
<Route path='narrowToTag'>
|
||||||
|
<Route path=':tag' />
|
||||||
|
</Route>
|
||||||
<Route path='storages'>
|
<Route path='storages'>
|
||||||
<IndexRedirect to='/home' />
|
<IndexRedirect to='/home' />
|
||||||
<Route path=':storageKey'>
|
<Route path=':storageKey'>
|
||||||
|
|||||||
Reference in New Issue
Block a user