mirror of
https://github.com/BoostIo/Boostnote
synced 2025-12-14 02:06:29 +00:00
Merge pull request #2758 from elfman/colorTag
add feature: colored tags
This commit is contained in:
68
browser/components/ColorPicker.js
Normal file
68
browser/components/ColorPicker.js
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import { SketchPicker } from 'react-color'
|
||||||
|
import CSSModules from 'browser/lib/CSSModules'
|
||||||
|
import styles from './ColorPicker.styl'
|
||||||
|
|
||||||
|
const componentHeight = 330
|
||||||
|
|
||||||
|
class ColorPicker extends React.Component {
|
||||||
|
constructor (props) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
color: this.props.color || '#939395'
|
||||||
|
}
|
||||||
|
|
||||||
|
this.onColorChange = this.onColorChange.bind(this)
|
||||||
|
this.handleConfirm = this.handleConfirm.bind(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillReceiveProps (nextProps) {
|
||||||
|
this.onColorChange(nextProps.color)
|
||||||
|
}
|
||||||
|
|
||||||
|
onColorChange (color) {
|
||||||
|
this.setState({
|
||||||
|
color
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
handleConfirm () {
|
||||||
|
this.props.onConfirm(this.state.color)
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { onReset, onCancel, targetRect } = this.props
|
||||||
|
const { color } = this.state
|
||||||
|
|
||||||
|
const clientHeight = document.body.clientHeight
|
||||||
|
const alignX = targetRect.right + 4
|
||||||
|
let alignY = targetRect.top
|
||||||
|
if (targetRect.top + componentHeight > clientHeight) {
|
||||||
|
alignY = targetRect.bottom - componentHeight
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div styleName='colorPicker' style={{top: `${alignY}px`, left: `${alignX}px`}}>
|
||||||
|
<div styleName='cover' onClick={onCancel} />
|
||||||
|
<SketchPicker color={color} onChange={this.onColorChange} />
|
||||||
|
<div styleName='footer'>
|
||||||
|
<button styleName='btn-reset' onClick={onReset}>Reset</button>
|
||||||
|
<button styleName='btn-cancel' onClick={onCancel}>Cancel</button>
|
||||||
|
<button styleName='btn-confirm' onClick={this.handleConfirm}>Confirm</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColorPicker.propTypes = {
|
||||||
|
color: PropTypes.string,
|
||||||
|
targetRect: PropTypes.object,
|
||||||
|
onConfirm: PropTypes.func.isRequired,
|
||||||
|
onCancel: PropTypes.func.isRequired,
|
||||||
|
onReset: PropTypes.func.isRequired
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CSSModules(ColorPicker, styles)
|
||||||
39
browser/components/ColorPicker.styl
Normal file
39
browser/components/ColorPicker.styl
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
.colorPicker
|
||||||
|
position fixed
|
||||||
|
z-index 2
|
||||||
|
display flex
|
||||||
|
flex-direction column
|
||||||
|
|
||||||
|
.cover
|
||||||
|
position fixed
|
||||||
|
top 0
|
||||||
|
right 0
|
||||||
|
bottom 0
|
||||||
|
left 0
|
||||||
|
|
||||||
|
.footer
|
||||||
|
display flex
|
||||||
|
justify-content center
|
||||||
|
z-index 2
|
||||||
|
align-items center
|
||||||
|
& > button + button
|
||||||
|
margin-left 10px
|
||||||
|
|
||||||
|
.btn-cancel,
|
||||||
|
.btn-confirm,
|
||||||
|
.btn-reset
|
||||||
|
vertical-align middle
|
||||||
|
height 25px
|
||||||
|
margin-top 2.5px
|
||||||
|
border-radius 2px
|
||||||
|
border none
|
||||||
|
padding 0 5px
|
||||||
|
background-color $default-button-background
|
||||||
|
&:hover
|
||||||
|
background-color $default-button-background--hover
|
||||||
|
.btn-confirm
|
||||||
|
background-color #1EC38B
|
||||||
|
&:hover
|
||||||
|
background-color darken(#1EC38B, 25%)
|
||||||
|
|
||||||
|
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { isArray } from 'lodash'
|
import { isArray } from 'lodash'
|
||||||
|
import invertColor from 'invert-color'
|
||||||
import CSSModules from 'browser/lib/CSSModules'
|
import CSSModules from 'browser/lib/CSSModules'
|
||||||
import { getTodoStatus } from 'browser/lib/getTodoStatus'
|
import { getTodoStatus } from 'browser/lib/getTodoStatus'
|
||||||
import styles from './NoteItem.styl'
|
import styles from './NoteItem.styl'
|
||||||
@@ -13,29 +14,38 @@ import i18n from 'browser/lib/i18n'
|
|||||||
/**
|
/**
|
||||||
* @description Tag element component.
|
* @description Tag element component.
|
||||||
* @param {string} tagName
|
* @param {string} tagName
|
||||||
|
* @param {string} color
|
||||||
* @return {React.Component}
|
* @return {React.Component}
|
||||||
*/
|
*/
|
||||||
const TagElement = ({ tagName }) => (
|
const TagElement = ({ tagName, color }) => {
|
||||||
<span styleName='item-bottom-tagList-item' key={tagName}>
|
const style = {}
|
||||||
|
if (color) {
|
||||||
|
style.backgroundColor = color
|
||||||
|
style.color = invertColor(color, { black: '#222', white: '#f1f1f1', threshold: 0.3 })
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<span styleName='item-bottom-tagList-item' key={tagName} style={style}>
|
||||||
#{tagName}
|
#{tagName}
|
||||||
</span>
|
</span>
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description Tag element list component.
|
* @description Tag element list component.
|
||||||
* @param {Array|null} tags
|
* @param {Array|null} tags
|
||||||
* @param {boolean} showTagsAlphabetically
|
* @param {boolean} showTagsAlphabetically
|
||||||
|
* @param {Object} coloredTags
|
||||||
* @return {React.Component}
|
* @return {React.Component}
|
||||||
*/
|
*/
|
||||||
const TagElementList = (tags, showTagsAlphabetically) => {
|
const TagElementList = (tags, showTagsAlphabetically, coloredTags) => {
|
||||||
if (!isArray(tags)) {
|
if (!isArray(tags)) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showTagsAlphabetically) {
|
if (showTagsAlphabetically) {
|
||||||
return _.sortBy(tags).map(tag => TagElement({ tagName: tag }))
|
return _.sortBy(tags).map(tag => TagElement({ tagName: tag, color: coloredTags[tag] }))
|
||||||
} else {
|
} else {
|
||||||
return tags.map(tag => TagElement({ tagName: tag }))
|
return tags.map(tag => TagElement({ tagName: tag, color: coloredTags[tag] }))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,6 +56,7 @@ const TagElementList = (tags, showTagsAlphabetically) => {
|
|||||||
* @param {Function} handleNoteClick
|
* @param {Function} handleNoteClick
|
||||||
* @param {Function} handleNoteContextMenu
|
* @param {Function} handleNoteContextMenu
|
||||||
* @param {Function} handleDragStart
|
* @param {Function} handleDragStart
|
||||||
|
* @param {Object} coloredTags
|
||||||
* @param {string} dateDisplay
|
* @param {string} dateDisplay
|
||||||
*/
|
*/
|
||||||
const NoteItem = ({
|
const NoteItem = ({
|
||||||
@@ -59,7 +70,8 @@ const NoteItem = ({
|
|||||||
storageName,
|
storageName,
|
||||||
folderName,
|
folderName,
|
||||||
viewType,
|
viewType,
|
||||||
showTagsAlphabetically
|
showTagsAlphabetically,
|
||||||
|
coloredTags
|
||||||
}) => (
|
}) => (
|
||||||
<div
|
<div
|
||||||
styleName={isActive ? 'item--active' : 'item'}
|
styleName={isActive ? 'item--active' : 'item'}
|
||||||
@@ -97,7 +109,7 @@ const NoteItem = ({
|
|||||||
<div styleName='item-bottom'>
|
<div styleName='item-bottom'>
|
||||||
<div styleName='item-bottom-tagList'>
|
<div styleName='item-bottom-tagList'>
|
||||||
{note.tags.length > 0
|
{note.tags.length > 0
|
||||||
? TagElementList(note.tags, showTagsAlphabetically)
|
? TagElementList(note.tags, showTagsAlphabetically, coloredTags)
|
||||||
: <span
|
: <span
|
||||||
style={{ fontStyle: 'italic', opacity: 0.5 }}
|
style={{ fontStyle: 'italic', opacity: 0.5 }}
|
||||||
styleName='item-bottom-tagList-empty'
|
styleName='item-bottom-tagList-empty'
|
||||||
@@ -127,6 +139,7 @@ const NoteItem = ({
|
|||||||
NoteItem.propTypes = {
|
NoteItem.propTypes = {
|
||||||
isActive: PropTypes.bool.isRequired,
|
isActive: PropTypes.bool.isRequired,
|
||||||
dateDisplay: PropTypes.string.isRequired,
|
dateDisplay: PropTypes.string.isRequired,
|
||||||
|
coloredTags: PropTypes.object,
|
||||||
note: PropTypes.shape({
|
note: PropTypes.shape({
|
||||||
storage: PropTypes.string.isRequired,
|
storage: PropTypes.string.isRequired,
|
||||||
key: PropTypes.string.isRequired,
|
key: PropTypes.string.isRequired,
|
||||||
|
|||||||
@@ -10,11 +10,12 @@ import CSSModules from 'browser/lib/CSSModules'
|
|||||||
* @param {string} name
|
* @param {string} name
|
||||||
* @param {Function} handleClickTagListItem
|
* @param {Function} handleClickTagListItem
|
||||||
* @param {Function} handleClickNarrowToTag
|
* @param {Function} handleClickNarrowToTag
|
||||||
* @param {bool} isActive
|
* @param {boolean} isActive
|
||||||
* @param {bool} isRelated
|
* @param {boolean} isRelated
|
||||||
|
* @param {string} bgColor tab backgroundColor
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const TagListItem = ({name, handleClickTagListItem, handleClickNarrowToTag, handleContextMenu, isActive, isRelated, count}) => (
|
const TagListItem = ({name, handleClickTagListItem, handleClickNarrowToTag, handleContextMenu, isActive, isRelated, count, color}) => (
|
||||||
<div styleName='tagList-itemContainer' onContextMenu={e => handleContextMenu(e, name)}>
|
<div styleName='tagList-itemContainer' onContextMenu={e => handleContextMenu(e, name)}>
|
||||||
{isRelated
|
{isRelated
|
||||||
? <button styleName={isActive ? 'tagList-itemNarrow-active' : 'tagList-itemNarrow'} onClick={() => handleClickNarrowToTag(name)}>
|
? <button styleName={isActive ? 'tagList-itemNarrow-active' : 'tagList-itemNarrow'} onClick={() => handleClickNarrowToTag(name)}>
|
||||||
@@ -23,6 +24,7 @@ const TagListItem = ({name, handleClickTagListItem, handleClickNarrowToTag, hand
|
|||||||
: <div styleName={isActive ? 'tagList-itemNarrow-active' : 'tagList-itemNarrow'} />
|
: <div styleName={isActive ? 'tagList-itemNarrow-active' : 'tagList-itemNarrow'} />
|
||||||
}
|
}
|
||||||
<button styleName={isActive ? 'tagList-item-active' : 'tagList-item'} onClick={() => handleClickTagListItem(name)}>
|
<button styleName={isActive ? 'tagList-item-active' : 'tagList-item'} onClick={() => handleClickTagListItem(name)}>
|
||||||
|
<span styleName='tagList-item-color' style={{backgroundColor: color || 'transparent'}} />
|
||||||
<span styleName='tagList-item-name'>
|
<span styleName='tagList-item-name'>
|
||||||
{`# ${name}`}
|
{`# ${name}`}
|
||||||
<span styleName='tagList-item-count'>{count !== 0 ? count : ''}</span>
|
<span styleName='tagList-item-count'>{count !== 0 ? count : ''}</span>
|
||||||
@@ -33,7 +35,8 @@ const TagListItem = ({name, handleClickTagListItem, handleClickNarrowToTag, hand
|
|||||||
|
|
||||||
TagListItem.propTypes = {
|
TagListItem.propTypes = {
|
||||||
name: PropTypes.string.isRequired,
|
name: PropTypes.string.isRequired,
|
||||||
handleClickTagListItem: PropTypes.func.isRequired
|
handleClickTagListItem: PropTypes.func.isRequired,
|
||||||
|
color: PropTypes.string
|
||||||
}
|
}
|
||||||
|
|
||||||
export default CSSModules(TagListItem, styles)
|
export default CSSModules(TagListItem, styles)
|
||||||
|
|||||||
@@ -71,6 +71,11 @@
|
|||||||
padding-right 15px
|
padding-right 15px
|
||||||
font-size 13px
|
font-size 13px
|
||||||
|
|
||||||
|
.tagList-item-color
|
||||||
|
height 26px
|
||||||
|
width 3px
|
||||||
|
display inline-block
|
||||||
|
|
||||||
body[data-theme="white"]
|
body[data-theme="white"]
|
||||||
.tagList-item
|
.tagList-item
|
||||||
color $ui-inactive-text-color
|
color $ui-inactive-text-color
|
||||||
|
|||||||
@@ -437,6 +437,7 @@ class MarkdownNoteDetail extends React.Component {
|
|||||||
showTagsAlphabetically={config.ui.showTagsAlphabetically}
|
showTagsAlphabetically={config.ui.showTagsAlphabetically}
|
||||||
data={data}
|
data={data}
|
||||||
onChange={this.handleUpdateTag.bind(this)}
|
onChange={this.handleUpdateTag.bind(this)}
|
||||||
|
coloredTags={config.coloredTags}
|
||||||
/>
|
/>
|
||||||
<TodoListPercentage onClearCheckboxClick={(e) => this.handleClearTodo(e)} percentageOfTodo={getTodoPercentageOfCompleted(note.content)} />
|
<TodoListPercentage onClearCheckboxClick={(e) => this.handleClearTodo(e)} percentageOfTodo={getTodoPercentageOfCompleted(note.content)} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -792,6 +792,7 @@ class SnippetNoteDetail extends React.Component {
|
|||||||
showTagsAlphabetically={config.ui.showTagsAlphabetically}
|
showTagsAlphabetically={config.ui.showTagsAlphabetically}
|
||||||
data={data}
|
data={data}
|
||||||
onChange={(e) => this.handleChange(e)}
|
onChange={(e) => this.handleChange(e)}
|
||||||
|
coloredTags={config.coloredTags}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div styleName='info-right'>
|
<div styleName='info-right'>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
import invertColor from 'invert-color'
|
||||||
import CSSModules from 'browser/lib/CSSModules'
|
import CSSModules from 'browser/lib/CSSModules'
|
||||||
import styles from './TagSelect.styl'
|
import styles from './TagSelect.styl'
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
@@ -185,19 +186,34 @@ class TagSelect extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { value, className, showTagsAlphabetically } = this.props
|
const { value, className, showTagsAlphabetically, coloredTags } = this.props
|
||||||
|
|
||||||
const tagList = _.isArray(value)
|
const tagList = _.isArray(value)
|
||||||
? (showTagsAlphabetically ? _.sortBy(value) : value).map((tag) => {
|
? (showTagsAlphabetically ? _.sortBy(value) : value).map((tag) => {
|
||||||
|
const wrapperStyle = {}
|
||||||
|
const textStyle = {}
|
||||||
|
const BLACK = '#333333'
|
||||||
|
const WHITE = '#f1f1f1'
|
||||||
|
const color = coloredTags[tag]
|
||||||
|
const invertedColor = color && invertColor(color, { black: BLACK, white: WHITE })
|
||||||
|
let iconRemove = '../resources/icon/icon-x.svg'
|
||||||
|
if (color) {
|
||||||
|
wrapperStyle.backgroundColor = color
|
||||||
|
textStyle.color = invertedColor
|
||||||
|
}
|
||||||
|
if (invertedColor === WHITE) {
|
||||||
|
iconRemove = '../resources/icon/icon-x-light.svg'
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<span styleName='tag'
|
<span styleName='tag'
|
||||||
key={tag}
|
key={tag}
|
||||||
|
style={wrapperStyle}
|
||||||
>
|
>
|
||||||
<span styleName='tag-label' onClick={(e) => this.handleTagLabelClick(tag)}>#{tag}</span>
|
<span styleName='tag-label' style={textStyle} onClick={(e) => this.handleTagLabelClick(tag)}>#{tag}</span>
|
||||||
<button styleName='tag-removeButton'
|
<button styleName='tag-removeButton'
|
||||||
onClick={(e) => this.handleTagRemoveButtonClick(tag)}
|
onClick={(e) => this.handleTagRemoveButtonClick(tag)}
|
||||||
>
|
>
|
||||||
<img className='tag-removeButton-icon' src='../resources/icon/icon-x.svg' width='8px' />
|
<img className='tag-removeButton-icon' src={iconRemove} width='8px' />
|
||||||
</button>
|
</button>
|
||||||
</span>
|
</span>
|
||||||
)
|
)
|
||||||
@@ -246,7 +262,8 @@ TagSelect.contextTypes = {
|
|||||||
TagSelect.propTypes = {
|
TagSelect.propTypes = {
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
value: PropTypes.arrayOf(PropTypes.string),
|
value: PropTypes.arrayOf(PropTypes.string),
|
||||||
onChange: PropTypes.func
|
onChange: PropTypes.func,
|
||||||
|
coloredTags: PropTypes.object
|
||||||
}
|
}
|
||||||
|
|
||||||
export default CSSModules(TagSelect, styles)
|
export default CSSModules(TagSelect, styles)
|
||||||
|
|||||||
@@ -1047,6 +1047,7 @@ class NoteList extends React.Component {
|
|||||||
storageName={this.getNoteStorage(note).name}
|
storageName={this.getNoteStorage(note).name}
|
||||||
viewType={viewType}
|
viewType={viewType}
|
||||||
showTagsAlphabetically={config.ui.showTagsAlphabetically}
|
showTagsAlphabetically={config.ui.showTagsAlphabetically}
|
||||||
|
coloredTags={config.coloredTags}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import i18n from 'browser/lib/i18n'
|
|||||||
import context from 'browser/lib/context'
|
import context from 'browser/lib/context'
|
||||||
import { remote } from 'electron'
|
import { remote } from 'electron'
|
||||||
import { confirmDeleteNote } from 'browser/lib/confirmDeleteNote'
|
import { confirmDeleteNote } from 'browser/lib/confirmDeleteNote'
|
||||||
|
import ColorPicker from 'browser/components/ColorPicker'
|
||||||
|
|
||||||
function matchActiveTags (tags, activeTags) {
|
function matchActiveTags (tags, activeTags) {
|
||||||
return _.every(activeTags, v => tags.indexOf(v) >= 0)
|
return _.every(activeTags, v => tags.indexOf(v) >= 0)
|
||||||
@@ -27,6 +28,22 @@ function matchActiveTags (tags, activeTags) {
|
|||||||
|
|
||||||
class SideNav extends React.Component {
|
class SideNav extends React.Component {
|
||||||
// TODO: should not use electron stuff v0.7
|
// TODO: should not use electron stuff v0.7
|
||||||
|
constructor (props) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
colorPicker: {
|
||||||
|
show: false,
|
||||||
|
color: null,
|
||||||
|
tagName: null,
|
||||||
|
targetRect: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.dismissColorPicker = this.dismissColorPicker.bind(this)
|
||||||
|
this.handleColorPickerConfirm = this.handleColorPickerConfirm.bind(this)
|
||||||
|
this.handleColorPickerReset = this.handleColorPickerReset.bind(this)
|
||||||
|
}
|
||||||
|
|
||||||
componentDidMount () {
|
componentDidMount () {
|
||||||
EventEmitter.on('side:preferences', this.handleMenuButtonClick)
|
EventEmitter.on('side:preferences', this.handleMenuButtonClick)
|
||||||
@@ -104,9 +121,64 @@ class SideNav extends React.Component {
|
|||||||
click: this.deleteTag.bind(this, tag)
|
click: this.deleteTag.bind(this, tag)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
menu.push({
|
||||||
|
label: i18n.__('Customize Color'),
|
||||||
|
click: this.displayColorPicker.bind(this, tag, e.target.getBoundingClientRect())
|
||||||
|
})
|
||||||
|
|
||||||
context.popup(menu)
|
context.popup(menu)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dismissColorPicker () {
|
||||||
|
this.setState({
|
||||||
|
colorPicker: {
|
||||||
|
show: false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
displayColorPicker (tagName, rect) {
|
||||||
|
const { config } = this.props
|
||||||
|
this.setState({
|
||||||
|
colorPicker: {
|
||||||
|
show: true,
|
||||||
|
color: config.coloredTags[tagName],
|
||||||
|
tagName,
|
||||||
|
targetRect: rect
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
handleColorPickerConfirm (color) {
|
||||||
|
const { dispatch, config: {coloredTags} } = this.props
|
||||||
|
const { colorPicker: { tagName } } = this.state
|
||||||
|
const newColoredTags = Object.assign({}, coloredTags, {[tagName]: color.hex})
|
||||||
|
|
||||||
|
const config = { coloredTags: newColoredTags }
|
||||||
|
ConfigManager.set(config)
|
||||||
|
dispatch({
|
||||||
|
type: 'SET_CONFIG',
|
||||||
|
config
|
||||||
|
})
|
||||||
|
this.dismissColorPicker()
|
||||||
|
}
|
||||||
|
|
||||||
|
handleColorPickerReset () {
|
||||||
|
const { dispatch, config: {coloredTags} } = this.props
|
||||||
|
const { colorPicker: { tagName } } = this.state
|
||||||
|
const newColoredTags = Object.assign({}, coloredTags)
|
||||||
|
|
||||||
|
delete newColoredTags[tagName]
|
||||||
|
|
||||||
|
const config = { coloredTags: newColoredTags }
|
||||||
|
ConfigManager.set(config)
|
||||||
|
dispatch({
|
||||||
|
type: 'SET_CONFIG',
|
||||||
|
config
|
||||||
|
})
|
||||||
|
this.dismissColorPicker()
|
||||||
|
}
|
||||||
|
|
||||||
handleToggleButtonClick (e) {
|
handleToggleButtonClick (e) {
|
||||||
const { dispatch, config } = this.props
|
const { dispatch, config } = this.props
|
||||||
|
|
||||||
@@ -207,6 +279,7 @@ class SideNav extends React.Component {
|
|||||||
|
|
||||||
tagListComponent () {
|
tagListComponent () {
|
||||||
const { data, location, config } = this.props
|
const { data, location, config } = this.props
|
||||||
|
const { colorPicker } = this.state
|
||||||
const activeTags = this.getActiveTags(location.pathname)
|
const activeTags = this.getActiveTags(location.pathname)
|
||||||
const relatedTags = this.getRelatedTags(activeTags, data.noteMap)
|
const relatedTags = this.getRelatedTags(activeTags, data.noteMap)
|
||||||
let tagList = _.sortBy(data.tagNoteMap.map(
|
let tagList = _.sortBy(data.tagNoteMap.map(
|
||||||
@@ -237,10 +310,11 @@ class SideNav extends React.Component {
|
|||||||
handleClickTagListItem={this.handleClickTagListItem.bind(this)}
|
handleClickTagListItem={this.handleClickTagListItem.bind(this)}
|
||||||
handleClickNarrowToTag={this.handleClickNarrowToTag.bind(this)}
|
handleClickNarrowToTag={this.handleClickNarrowToTag.bind(this)}
|
||||||
handleContextMenu={this.handleTagContextMenu.bind(this)}
|
handleContextMenu={this.handleTagContextMenu.bind(this)}
|
||||||
isActive={this.getTagActive(location.pathname, tag.name)}
|
isActive={this.getTagActive(location.pathname, tag.name) || (colorPicker.tagName === tag.name)}
|
||||||
isRelated={tag.related}
|
isRelated={tag.related}
|
||||||
key={tag.name}
|
key={tag.name}
|
||||||
count={tag.size}
|
count={tag.size}
|
||||||
|
color={config.coloredTags[tag.name]}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@@ -333,6 +407,7 @@ class SideNav extends React.Component {
|
|||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { data, location, config, dispatch } = this.props
|
const { data, location, config, dispatch } = this.props
|
||||||
|
const { colorPicker: colorPickerState } = this.state
|
||||||
|
|
||||||
const isFolded = config.isSideNavFolded
|
const isFolded = config.isSideNavFolded
|
||||||
|
|
||||||
@@ -349,6 +424,20 @@ class SideNav extends React.Component {
|
|||||||
useDragHandle
|
useDragHandle
|
||||||
/>
|
/>
|
||||||
})
|
})
|
||||||
|
|
||||||
|
let colorPicker
|
||||||
|
if (colorPickerState.show) {
|
||||||
|
colorPicker = (
|
||||||
|
<ColorPicker
|
||||||
|
color={colorPickerState.color}
|
||||||
|
targetRect={colorPickerState.targetRect}
|
||||||
|
onConfirm={this.handleColorPickerConfirm}
|
||||||
|
onCancel={this.dismissColorPicker}
|
||||||
|
onReset={this.handleColorPickerReset}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const style = {}
|
const style = {}
|
||||||
if (!isFolded) style.width = this.props.width
|
if (!isFolded) style.width = this.props.width
|
||||||
const isTagActive = location.pathname.match(/tag/)
|
const isTagActive = location.pathname.match(/tag/)
|
||||||
@@ -368,6 +457,7 @@ class SideNav extends React.Component {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{this.SideNavComponent(isFolded, storageList)}
|
{this.SideNavComponent(isFolded, storageList)}
|
||||||
|
{colorPicker}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,7 +86,8 @@ export const DEFAULT_CONFIG = {
|
|||||||
token: '',
|
token: '',
|
||||||
username: '',
|
username: '',
|
||||||
password: ''
|
password: ''
|
||||||
}
|
},
|
||||||
|
coloredTags: {}
|
||||||
}
|
}
|
||||||
|
|
||||||
function validate (config) {
|
function validate (config) {
|
||||||
|
|||||||
@@ -62,7 +62,7 @@
|
|||||||
.folderItem-right-button
|
.folderItem-right-button
|
||||||
vertical-align middle
|
vertical-align middle
|
||||||
height 25px
|
height 25px
|
||||||
margin-top 2.5px
|
margin-top 2px
|
||||||
colorDefaultButton()
|
colorDefaultButton()
|
||||||
border-radius 2px
|
border-radius 2px
|
||||||
border $ui-border
|
border $ui-border
|
||||||
|
|||||||
@@ -70,6 +70,7 @@
|
|||||||
"i18n-2": "^0.7.2",
|
"i18n-2": "^0.7.2",
|
||||||
"iconv-lite": "^0.4.19",
|
"iconv-lite": "^0.4.19",
|
||||||
"immutable": "^3.8.1",
|
"immutable": "^3.8.1",
|
||||||
|
"invert-color": "^2.0.0",
|
||||||
"js-sequence-diagrams": "^1000000.0.6",
|
"js-sequence-diagrams": "^1000000.0.6",
|
||||||
"js-yaml": "^3.12.0",
|
"js-yaml": "^3.12.0",
|
||||||
"katex": "^0.9.0",
|
"katex": "^0.9.0",
|
||||||
@@ -98,6 +99,7 @@
|
|||||||
"react": "^15.5.4",
|
"react": "^15.5.4",
|
||||||
"react-autosuggest": "^9.4.0",
|
"react-autosuggest": "^9.4.0",
|
||||||
"react-codemirror": "^0.3.0",
|
"react-codemirror": "^0.3.0",
|
||||||
|
"react-color": "^2.2.2",
|
||||||
"react-debounce-render": "^4.0.1",
|
"react-debounce-render": "^4.0.1",
|
||||||
"react-dom": "^15.0.2",
|
"react-dom": "^15.0.2",
|
||||||
"react-image-carousel": "^2.0.18",
|
"react-image-carousel": "^2.0.18",
|
||||||
@@ -154,7 +156,6 @@
|
|||||||
"merge-stream": "^1.0.0",
|
"merge-stream": "^1.0.0",
|
||||||
"mock-require": "^3.0.1",
|
"mock-require": "^3.0.1",
|
||||||
"nib": "^1.1.0",
|
"nib": "^1.1.0",
|
||||||
"react-color": "^2.2.2",
|
|
||||||
"react-css-modules": "^3.7.6",
|
"react-css-modules": "^3.7.6",
|
||||||
"react-input-autosize": "^1.1.0",
|
"react-input-autosize": "^1.1.0",
|
||||||
"react-router": "^2.4.0",
|
"react-router": "^2.4.0",
|
||||||
|
|||||||
17
resources/icon/icon-x-light.svg
Normal file
17
resources/icon/icon-x-light.svg
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="14px" height="14px" viewBox="0 0 14 14" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<!-- Generator: Sketch 47.1 (45422) - http://www.bohemiancoding.com/sketch -->
|
||||||
|
<title>x</title>
|
||||||
|
<desc>Created with Sketch.</desc>
|
||||||
|
<defs></defs>
|
||||||
|
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<g id="Feather" transform="translate(-1910.000000, -644.000000)" stroke="#c1c1c1" stroke-width="2">
|
||||||
|
<g id="Group" transform="translate(175.000000, 332.000000)">
|
||||||
|
<g id="x" transform="translate(1736.000000, 313.000000)">
|
||||||
|
<path d="M12,0 L0,12" id="Shape"></path>
|
||||||
|
<path d="M0,0 L12,12" id="Shape"></path>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 917 B |
@@ -3,7 +3,7 @@ import renderer from 'react-test-renderer'
|
|||||||
import TagListItem from 'browser/components/TagListItem'
|
import TagListItem from 'browser/components/TagListItem'
|
||||||
|
|
||||||
it('TagListItem renders correctly', () => {
|
it('TagListItem renders correctly', () => {
|
||||||
const tagListItem = renderer.create(<TagListItem name='Test' handleClickTagListItem={jest.fn()} />)
|
const tagListItem = renderer.create(<TagListItem name='Test' handleClickTagListItem={jest.fn()} color='#000' />)
|
||||||
|
|
||||||
expect(tagListItem.toJSON()).toMatchSnapshot()
|
expect(tagListItem.toJSON()).toMatchSnapshot()
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -12,6 +12,14 @@ exports[`TagListItem renders correctly 1`] = `
|
|||||||
className="tagList-item"
|
className="tagList-item"
|
||||||
onClick={[Function]}
|
onClick={[Function]}
|
||||||
>
|
>
|
||||||
|
<span
|
||||||
|
className="tagList-item-color"
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"backgroundColor": "#000",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
<span
|
<span
|
||||||
className="tagList-item-name"
|
className="tagList-item-name"
|
||||||
>
|
>
|
||||||
|
|||||||
Reference in New Issue
Block a user