Skip to content

Commit 3e645db

Browse files
committed
add feature: colored tags
1 parent 13c2f47 commit 3e645db

File tree

13 files changed

+205
-13
lines changed

13 files changed

+205
-13
lines changed

browser/components/ColorPicker.js

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import React from 'react'
2+
import PropTypes from 'prop-types'
3+
import { SketchPicker } from 'react-color'
4+
import CSSModules from 'browser/lib/CSSModules'
5+
import styles from './ColorPicker.styl'
6+
7+
const componentHeight = 333
8+
9+
class ColorPicker extends React.Component {
10+
constructor (props) {
11+
super(props)
12+
13+
this.state = {
14+
color: this.props.color || '#888888'
15+
}
16+
17+
this.onColorChange = this.onColorChange.bind(this)
18+
this.handleConfirm = this.handleConfirm.bind(this)
19+
}
20+
21+
onColorChange (color) {
22+
this.setState({
23+
color
24+
})
25+
}
26+
27+
handleConfirm () {
28+
this.props.onConfirm(this.state.color)
29+
}
30+
31+
render () {
32+
const { onReset, onCancel, targetRect } = this.props
33+
const { color } = this.state
34+
35+
const clientHeight = document.body.clientHeight
36+
const alignX = targetRect.right + 4
37+
let alignY = targetRect.top
38+
if (targetRect.top + componentHeight > clientHeight) {
39+
alignY = targetRect.bottom - componentHeight
40+
}
41+
42+
return (
43+
<div styleName='colorPicker' style={{top: `${alignY}px`, left: `${alignX}px`}}>
44+
<SketchPicker color={color} onChange={this.onColorChange} />
45+
<div styleName='footer'>
46+
<button styleName='btn-reset' onClick={onReset}>Reset</button>
47+
<button styleName='btn-cancel' onClick={onCancel}>Cancel</button>
48+
<button styleName='btn-confirm' onClick={this.handleConfirm}>Confirm</button>
49+
</div>
50+
</div>
51+
)
52+
}
53+
}
54+
55+
ColorPicker.propTypes = {
56+
color: PropTypes.string,
57+
targetRect: PropTypes.object,
58+
onConfirm: PropTypes.func.isRequired,
59+
onCancel: PropTypes.func.isRequired,
60+
onReset: PropTypes.func.isRequired
61+
}
62+
63+
export default CSSModules(ColorPicker, styles)

browser/components/ColorPicker.styl

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
.colorPicker
2+
position fixed
3+
z-index 10000
4+
display flex
5+
flex-direction column
6+
.footer
7+
display flex
8+
justify-content center
9+
align-items center
10+
padding 5px
11+
& > button + button
12+
margin-left 10px
13+
14+
.btn-cancel,
15+
.btn-confirm,
16+
.btn-reset
17+
height 1.6em
18+
border 1px solid #888888
19+
background-color #fff
20+
font-size 16px
21+
border-radius 4px
22+
.btn-confirm
23+
background-color $ui-button-default--active-backgroundColor
24+
25+

browser/components/NoteItem.js

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ import i18n from 'browser/lib/i18n'
1515
* @param {string} tagName
1616
* @return {React.Component}
1717
*/
18-
const TagElement = ({ tagName }) => (
19-
<span styleName='item-bottom-tagList-item' key={tagName}>
18+
const TagElement = ({ tagName, color }) => (
19+
<span styleName='item-bottom-tagList-item' key={tagName} style={{backgroundColor: color}}>
2020
#{tagName}
2121
</span>
2222
)
@@ -27,15 +27,15 @@ const TagElement = ({ tagName }) => (
2727
* @param {boolean} showTagsAlphabetically
2828
* @return {React.Component}
2929
*/
30-
const TagElementList = (tags, showTagsAlphabetically) => {
30+
const TagElementList = (tags, showTagsAlphabetically, tagConfig) => {
3131
if (!isArray(tags)) {
3232
return []
3333
}
3434

3535
if (showTagsAlphabetically) {
3636
return _.sortBy(tags).map(tag => TagElement({ tagName: tag }))
3737
} else {
38-
return tags.map(tag => TagElement({ tagName: tag }))
38+
return tags.map(tag => TagElement({ tagName: tag, color: tagConfig[tag] }))
3939
}
4040
}
4141

@@ -59,7 +59,8 @@ const NoteItem = ({
5959
storageName,
6060
folderName,
6161
viewType,
62-
showTagsAlphabetically
62+
showTagsAlphabetically,
63+
tagConfig
6364
}) => (
6465
<div
6566
styleName={isActive ? 'item--active' : 'item'}
@@ -97,7 +98,7 @@ const NoteItem = ({
9798
<div styleName='item-bottom'>
9899
<div styleName='item-bottom-tagList'>
99100
{note.tags.length > 0
100-
? TagElementList(note.tags, showTagsAlphabetically)
101+
? TagElementList(note.tags, showTagsAlphabetically, tagConfig)
101102
: <span
102103
style={{ fontStyle: 'italic', opacity: 0.5 }}
103104
styleName='item-bottom-tagList-empty'
@@ -127,6 +128,7 @@ const NoteItem = ({
127128
NoteItem.propTypes = {
128129
isActive: PropTypes.bool.isRequired,
129130
dateDisplay: PropTypes.string.isRequired,
131+
tagConfig: PropTypes.object,
130132
note: PropTypes.shape({
131133
storage: PropTypes.string.isRequired,
132134
key: PropTypes.string.isRequired,

browser/components/TagListItem.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,10 @@ import CSSModules from 'browser/lib/CSSModules'
1212
* @param {Function} handleClickNarrowToTag
1313
* @param {bool} isActive
1414
* @param {bool} isRelated
15+
* @param {string} bgColor tab backgroundColor
1516
*/
1617

17-
const TagListItem = ({name, handleClickTagListItem, handleClickNarrowToTag, handleContextMenu, isActive, isRelated, count}) => (
18+
const TagListItem = ({name, handleClickTagListItem, handleClickNarrowToTag, handleContextMenu, isActive, isRelated, count, color}) => (
1819
<div styleName='tagList-itemContainer' onContextMenu={e => handleContextMenu(e, name)}>
1920
{isRelated
2021
? <button styleName={isActive ? 'tagList-itemNarrow-active' : 'tagList-itemNarrow'} onClick={() => handleClickNarrowToTag(name)}>
@@ -23,7 +24,7 @@ const TagListItem = ({name, handleClickTagListItem, handleClickNarrowToTag, hand
2324
: <div styleName={isActive ? 'tagList-itemNarrow-active' : 'tagList-itemNarrow'} />
2425
}
2526
<button styleName={isActive ? 'tagList-item-active' : 'tagList-item'} onClick={() => handleClickTagListItem(name)}>
26-
<span styleName='tagList-item-name'>
27+
<span styleName='tagList-item-name' style={{color}}>
2728
{`# ${name}`}
2829
<span styleName='tagList-item-count'>{count !== 0 ? count : ''}</span>
2930
</span>
@@ -33,7 +34,8 @@ const TagListItem = ({name, handleClickTagListItem, handleClickNarrowToTag, hand
3334

3435
TagListItem.propTypes = {
3536
name: PropTypes.string.isRequired,
36-
handleClickTagListItem: PropTypes.func.isRequired
37+
handleClickTagListItem: PropTypes.func.isRequired,
38+
color: PropTypes.string
3739
}
3840

3941
export default CSSModules(TagListItem, styles)

browser/main/Detail/MarkdownNoteDetail.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -437,6 +437,7 @@ class MarkdownNoteDetail extends React.Component {
437437
showTagsAlphabetically={config.ui.showTagsAlphabetically}
438438
data={data}
439439
onChange={this.handleUpdateTag.bind(this)}
440+
tagConfig={config.tag}
440441
/>
441442
<TodoListPercentage onClearCheckboxClick={(e) => this.handleClearTodo(e)} percentageOfTodo={getTodoPercentageOfCompleted(note.content)} />
442443
</div>

browser/main/Detail/SnippetNoteDetail.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -788,6 +788,7 @@ class SnippetNoteDetail extends React.Component {
788788
showTagsAlphabetically={config.ui.showTagsAlphabetically}
789789
data={data}
790790
onChange={(e) => this.handleChange(e)}
791+
tagConfig={config.tag}
791792
/>
792793
</div>
793794
<div styleName='info-right'>

browser/main/Detail/TagSelect.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -179,13 +179,14 @@ class TagSelect extends React.Component {
179179
}
180180

181181
render () {
182-
const { value, className, showTagsAlphabetically } = this.props
182+
const { value, className, showTagsAlphabetically, tagConfig } = this.props
183183

184184
const tagList = _.isArray(value)
185185
? (showTagsAlphabetically ? _.sortBy(value) : value).map((tag) => {
186186
return (
187187
<span styleName='tag'
188188
key={tag}
189+
style={{backgroundColor: tagConfig[tag]}}
189190
>
190191
<span styleName='tag-label' onClick={(e) => this.handleTagLabelClick(tag)}>#{tag}</span>
191192
<button styleName='tag-removeButton'
@@ -240,7 +241,8 @@ TagSelect.contextTypes = {
240241
TagSelect.propTypes = {
241242
className: PropTypes.string,
242243
value: PropTypes.arrayOf(PropTypes.string),
243-
onChange: PropTypes.func
244+
onChange: PropTypes.func,
245+
tagConfig: PropTypes.object
244246
}
245247

246248
export default CSSModules(TagSelect, styles)

browser/main/NoteList/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1047,6 +1047,7 @@ class NoteList extends React.Component {
10471047
storageName={this.getNoteStorage(note).name}
10481048
viewType={viewType}
10491049
showTagsAlphabetically={config.ui.showTagsAlphabetically}
1050+
tagConfig={config.tag}
10501051
/>
10511052
)
10521053
}

browser/main/SideNav/index.js

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,30 @@ import i18n from 'browser/lib/i18n'
2020
import context from 'browser/lib/context'
2121
import { remote } from 'electron'
2222
import { confirmDeleteNote } from 'browser/lib/confirmDeleteNote'
23+
import ColorPicker from 'browser/components/ColorPicker'
2324

2425
function matchActiveTags (tags, activeTags) {
2526
return _.every(activeTags, v => tags.indexOf(v) >= 0)
2627
}
2728

2829
class SideNav extends React.Component {
2930
// TODO: should not use electron stuff v0.7
31+
constructor (props) {
32+
super(props)
33+
34+
this.state = {
35+
colorPickerState: {
36+
show: false,
37+
color: null,
38+
tagName: null,
39+
targetRect: null
40+
}
41+
}
42+
43+
this.dismissColorPicker = this.dismissColorPicker.bind(this)
44+
this.handleColorPickerConfirm = this.handleColorPickerConfirm.bind(this)
45+
this.handleColorPickerReset = this.handleColorPickerReset.bind(this)
46+
}
3047

3148
componentDidMount () {
3249
EventEmitter.on('side:preferences', this.handleMenuButtonClick)
@@ -104,9 +121,63 @@ class SideNav extends React.Component {
104121
click: this.deleteTag.bind(this, tag)
105122
})
106123

124+
menu.push({
125+
label: i18n.__('Customize Color'),
126+
click: this.displayColorPicker.bind(this, tag, e.target.getBoundingClientRect())
127+
})
128+
107129
context.popup(menu)
108130
}
109131

132+
dismissColorPicker () {
133+
this.setState({
134+
colorPickerState: {
135+
show: false
136+
}
137+
})
138+
}
139+
140+
displayColorPicker (tagName, rect) {
141+
const { config } = this.props
142+
this.setState({
143+
colorPickerState: {
144+
show: true,
145+
color: config.tag && config.tag[tagName],
146+
tagName,
147+
targetRect: rect
148+
}
149+
})
150+
}
151+
152+
handleColorPickerConfirm (color) {
153+
const { dispatch, config: {tag} } = this.props
154+
const { colorPickerState: { tagName } } = this.state
155+
const tagConfig = Object.assign({}, tag, {[tagName]: color.hex})
156+
157+
const config = {tag: tagConfig}
158+
ConfigManager.set(config)
159+
dispatch({
160+
type: 'SET_CONFIG',
161+
config
162+
})
163+
this.dismissColorPicker()
164+
}
165+
166+
handleColorPickerReset () {
167+
const { dispatch, config: {tag} } = this.props
168+
const { colorPickerState: { tagName } } = this.state
169+
const tagConfig = Object.assign({}, tag)
170+
delete tagConfig[tagName]
171+
172+
const config = {tag: tagConfig}
173+
ConfigManager.set(config)
174+
dispatch({
175+
type: 'SET_CONFIG',
176+
config
177+
})
178+
this.dismissColorPicker()
179+
}
180+
110181
handleToggleButtonClick (e) {
111182
const { dispatch, config } = this.props
112183

@@ -241,6 +312,7 @@ class SideNav extends React.Component {
241312
isRelated={tag.related}
242313
key={tag.name}
243314
count={tag.size}
315+
color={config.tag[tag.name]}
244316
/>
245317
)
246318
})
@@ -333,6 +405,7 @@ class SideNav extends React.Component {
333405

334406
render () {
335407
const { data, location, config, dispatch } = this.props
408+
const { colorPickerState } = this.state
336409

337410
const isFolded = config.isSideNavFolded
338411

@@ -349,6 +422,20 @@ class SideNav extends React.Component {
349422
useDragHandle
350423
/>
351424
})
425+
426+
let colorPicker
427+
if (colorPickerState.show) {
428+
colorPicker = (
429+
<ColorPicker
430+
color={colorPickerState.color}
431+
targetRect={colorPickerState.targetRect}
432+
onConfirm={this.handleColorPickerConfirm}
433+
onCancel={this.dismissColorPicker}
434+
onReset={this.handleColorPickerReset}
435+
/>
436+
)
437+
}
438+
352439
const style = {}
353440
if (!isFolded) style.width = this.props.width
354441
const isTagActive = location.pathname.match(/tag/)
@@ -368,6 +455,7 @@ class SideNav extends React.Component {
368455
</div>
369456
</div>
370457
{this.SideNavComponent(isFolded, storageList)}
458+
{colorPicker}
371459
</div>
372460
)
373461
}

browser/main/lib/ConfigManager.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,8 @@ export const DEFAULT_CONFIG = {
8686
token: '',
8787
username: '',
8888
password: ''
89-
}
89+
},
90+
tag: {}
9091
}
9192

9293
function validate (config) {

0 commit comments

Comments
 (0)