mirror of
https://github.com/BoostIo/Boostnote
synced 2025-12-13 09:46:22 +00:00
Merge with master resolve conflict
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import React, { PropTypes } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
import _ from 'lodash'
|
||||
import CodeMirror from 'codemirror'
|
||||
import path from 'path'
|
||||
@@ -67,7 +68,7 @@ export default class CodeEditor extends React.Component {
|
||||
if (cm.somethingSelected()) cm.indentSelection('add')
|
||||
else {
|
||||
const tabs = cm.getOption('indentWithTabs')
|
||||
if (line.trimLeft() === '- ' || line.trimLeft() === '* ' || line.trimLeft() === '+ ') {
|
||||
if (line.trimLeft().match(/^(-|\*|\+) (\[( |x)\] )?$/)) {
|
||||
cm.execCommand('goLineStart')
|
||||
if (tabs) {
|
||||
cm.execCommand('insertTab')
|
||||
@@ -103,13 +104,14 @@ export default class CodeEditor extends React.Component {
|
||||
this.editor.on('change', this.changeHandler)
|
||||
this.editor.on('paste', this.pasteHandler)
|
||||
|
||||
let editorTheme = document.getElementById('editorTheme')
|
||||
const editorTheme = document.getElementById('editorTheme')
|
||||
editorTheme.addEventListener('load', this.loadStyleHandler)
|
||||
|
||||
CodeMirror.Vim.defineEx('quit', 'q', this.quitEditor)
|
||||
CodeMirror.Vim.defineEx('q!', 'q!', this.quitEditor)
|
||||
CodeMirror.Vim.defineEx('wq', 'wq', this.quitEditor)
|
||||
CodeMirror.Vim.defineEx('qw', 'qw', this.quitEditor)
|
||||
CodeMirror.Vim.map('ZZ', ':q', 'normal')
|
||||
}
|
||||
|
||||
quitEditor () {
|
||||
@@ -120,7 +122,7 @@ export default class CodeEditor extends React.Component {
|
||||
this.editor.off('blur', this.blurHandler)
|
||||
this.editor.off('change', this.changeHandler)
|
||||
this.editor.off('paste', this.pasteHandler)
|
||||
let editorTheme = document.getElementById('editorTheme')
|
||||
const editorTheme = document.getElementById('editorTheme')
|
||||
editorTheme.removeEventListener('load', this.loadStyleHandler)
|
||||
}
|
||||
|
||||
@@ -196,7 +198,7 @@ export default class CodeEditor extends React.Component {
|
||||
}
|
||||
|
||||
setValue (value) {
|
||||
let cursor = this.editor.getCursor()
|
||||
const cursor = this.editor.getCursor()
|
||||
this.editor.setValue(value)
|
||||
this.editor.setCursor(cursor)
|
||||
}
|
||||
@@ -213,9 +215,7 @@ export default class CodeEditor extends React.Component {
|
||||
}
|
||||
|
||||
insertImageMd (imageMd) {
|
||||
const textarea = this.editor.getInputField()
|
||||
const cm = this.editor
|
||||
cm.replaceSelection(`${textarea.value.substr(0, textarea.selectionStart)}${imageMd}${textarea.value.substr(textarea.selectionEnd)}`)
|
||||
this.editor.replaceSelection(imageMd)
|
||||
}
|
||||
|
||||
handlePaste (editor, e) {
|
||||
@@ -223,7 +223,7 @@ export default class CodeEditor extends React.Component {
|
||||
if (!dataTransferItem.type.match('image')) return
|
||||
|
||||
const blob = dataTransferItem.getAsFile()
|
||||
let reader = new FileReader()
|
||||
const reader = new FileReader()
|
||||
let base64data
|
||||
|
||||
reader.readAsDataURL(blob)
|
||||
@@ -243,7 +243,8 @@ export default class CodeEditor extends React.Component {
|
||||
}
|
||||
|
||||
render () {
|
||||
let { className, fontFamily, fontSize } = this.props
|
||||
const { className, fontSize } = this.props
|
||||
let fontFamily = this.props.className
|
||||
fontFamily = _.isString(fontFamily) && fontFamily.length > 0
|
||||
? [fontFamily].concat(defaultEditorFontFamily)
|
||||
: defaultEditorFontFamily
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import React, { PropTypes } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
import CSSModules from 'browser/lib/CSSModules'
|
||||
import styles from './MarkdownEditor.styl'
|
||||
import CodeEditor from 'browser/components/CodeEditor'
|
||||
import MarkdownPreview from 'browser/components/MarkdownPreview'
|
||||
import eventEmitter from 'browser/main/lib/eventEmitter'
|
||||
import { findStorage } from 'browser/lib/findStorage'
|
||||
const _ = require('lodash')
|
||||
|
||||
class MarkdownEditor extends React.Component {
|
||||
constructor (props) {
|
||||
@@ -70,9 +70,9 @@ class MarkdownEditor extends React.Component {
|
||||
}
|
||||
|
||||
handleContextMenu (e) {
|
||||
let { config } = this.props
|
||||
const { config } = this.props
|
||||
if (config.editor.switchPreview === 'RIGHTCLICK') {
|
||||
let newStatus = this.state.status === 'PREVIEW'
|
||||
const newStatus = this.state.status === 'PREVIEW'
|
||||
? 'CODE'
|
||||
: 'PREVIEW'
|
||||
this.setState({
|
||||
@@ -91,9 +91,9 @@ class MarkdownEditor extends React.Component {
|
||||
handleBlur (e) {
|
||||
if (this.state.isLocked) return
|
||||
this.setState({ keyPressed: new Set() })
|
||||
let { config } = this.props
|
||||
const { config } = this.props
|
||||
if (config.editor.switchPreview === 'BLUR') {
|
||||
let cursorPosition = this.refs.code.editor.getCursor()
|
||||
const cursorPosition = this.refs.code.editor.getCursor()
|
||||
this.setState({
|
||||
status: 'PREVIEW'
|
||||
}, () => {
|
||||
@@ -109,7 +109,7 @@ class MarkdownEditor extends React.Component {
|
||||
}
|
||||
|
||||
handlePreviewMouseUp (e) {
|
||||
let { config } = this.props
|
||||
const { config } = this.props
|
||||
if (config.editor.switchPreview === 'BLUR' && new Date() - this.previewMouseDownedAt < 200) {
|
||||
this.setState({
|
||||
status: 'CODE'
|
||||
@@ -123,15 +123,15 @@ class MarkdownEditor extends React.Component {
|
||||
handleCheckboxClick (e) {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
let idMatch = /checkbox-([0-9]+)/
|
||||
let checkedMatch = /\[x\]/i
|
||||
let uncheckedMatch = /\[ \]/
|
||||
const idMatch = /checkbox-([0-9]+)/
|
||||
const checkedMatch = /\[x\]/i
|
||||
const uncheckedMatch = /\[ \]/
|
||||
if (idMatch.test(e.target.getAttribute('id'))) {
|
||||
let lineIndex = parseInt(e.target.getAttribute('id').match(idMatch)[1], 10) - 1
|
||||
let lines = this.refs.code.value
|
||||
const lineIndex = parseInt(e.target.getAttribute('id').match(idMatch)[1], 10) - 1
|
||||
const lines = this.refs.code.value
|
||||
.split('\n')
|
||||
|
||||
let targetLine = lines[lineIndex]
|
||||
const targetLine = lines[lineIndex]
|
||||
|
||||
if (targetLine.match(checkedMatch)) {
|
||||
lines[lineIndex] = targetLine.replace(checkedMatch, '[ ]')
|
||||
@@ -163,12 +163,12 @@ class MarkdownEditor extends React.Component {
|
||||
}
|
||||
|
||||
handleKeyDown (e) {
|
||||
let { config } = this.props
|
||||
const { config } = this.props
|
||||
if (this.state.status !== 'CODE') return false
|
||||
const keyPressed = this.state.keyPressed
|
||||
keyPressed.add(e.keyCode)
|
||||
this.setState({ keyPressed })
|
||||
let isNoteHandlerKey = (el) => { return keyPressed.has(el) }
|
||||
const isNoteHandlerKey = (el) => { return keyPressed.has(el) }
|
||||
// These conditions are for ctrl-e and ctrl-w
|
||||
if (keyPressed.size === this.escapeFromEditor.length &&
|
||||
!this.state.isLocked && this.state.status === 'CODE' &&
|
||||
@@ -207,14 +207,14 @@ class MarkdownEditor extends React.Component {
|
||||
}
|
||||
|
||||
render () {
|
||||
let { className, value, config, storageKey } = this.props
|
||||
const { className, value, config, storageKey } = this.props
|
||||
|
||||
let editorFontSize = parseInt(config.editor.fontSize, 10)
|
||||
if (!(editorFontSize > 0 && editorFontSize < 101)) editorFontSize = 14
|
||||
let editorIndentSize = parseInt(config.editor.indentSize, 10)
|
||||
if (!(editorFontSize > 0 && editorFontSize < 132)) editorIndentSize = 4
|
||||
|
||||
let previewStyle = {}
|
||||
const previewStyle = {}
|
||||
if (this.props.ignorePreviewPointerEvents) previewStyle.pointerEvents = 'none'
|
||||
|
||||
const storage = findStorage(storageKey)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React, { PropTypes } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
import markdown from 'browser/lib/markdown'
|
||||
import _ from 'lodash'
|
||||
import CodeMirror from 'codemirror'
|
||||
@@ -33,6 +34,15 @@ function buildStyle (fontFamily, fontSize, codeBlockFontFamily, lineNumber) {
|
||||
font-weight: normal;
|
||||
text-rendering: optimizeLegibility;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Lato';
|
||||
src: url('${appPath}/resources/fonts/Lato-Black.woff2') format('woff2'), /* Modern Browsers */
|
||||
url('${appPath}/resources/fonts/Lato-Black.woff') format('woff'), /* Modern Browsers */
|
||||
url('${appPath}/resources/fonts/Lato-Black.ttf') format('truetype');
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
text-rendering: optimizeLegibility;
|
||||
}
|
||||
${markdownStyle}
|
||||
body {
|
||||
font-family: '${fontFamily.join("','")}';
|
||||
@@ -117,10 +127,10 @@ export default class MarkdownPreview extends React.Component {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
|
||||
let anchor = e.target.closest('a')
|
||||
let href = anchor.getAttribute('href')
|
||||
const anchor = e.target.closest('a')
|
||||
const href = anchor.getAttribute('href')
|
||||
if (_.isString(href) && href.match(/^#/)) {
|
||||
let targetElement = this.refs.root.contentWindow.document.getElementById(href.substring(1, href.length))
|
||||
const targetElement = this.refs.root.contentWindow.document.getElementById(href.substring(1, href.length))
|
||||
if (targetElement != null) {
|
||||
this.getWindow().scrollTo(0, targetElement.offsetTop)
|
||||
}
|
||||
@@ -242,7 +252,8 @@ export default class MarkdownPreview extends React.Component {
|
||||
}
|
||||
|
||||
applyStyle () {
|
||||
let { fontFamily, fontSize, codeBlockFontFamily, lineNumber, codeBlockTheme } = this.props
|
||||
const { fontSize, lineNumber, codeBlockTheme } = this.props
|
||||
let { fontFamily, codeBlockFontFamily } = this.props
|
||||
fontFamily = _.isString(fontFamily) && fontFamily.trim().length > 0
|
||||
? [fontFamily].concat(defaultFontFamily)
|
||||
: defaultFontFamily
|
||||
@@ -258,7 +269,9 @@ export default class MarkdownPreview extends React.Component {
|
||||
theme = consts.THEMES.some((_theme) => _theme === theme) && theme !== 'default'
|
||||
? theme
|
||||
: 'elegant'
|
||||
this.getWindow().document.getElementById('codeTheme').href = `${appPath}/node_modules/codemirror/theme/${theme.split(' ')[0]}.css`
|
||||
this.getWindow().document.getElementById('codeTheme').href = theme.startsWith('solarized')
|
||||
? `${appPath}/node_modules/codemirror/theme/solarized.css`
|
||||
: `${appPath}/node_modules/codemirror/theme/${theme}.css`
|
||||
}
|
||||
|
||||
rewriteIframe () {
|
||||
@@ -273,7 +286,8 @@ export default class MarkdownPreview extends React.Component {
|
||||
el.removeEventListener('click', this.linkClickHandler)
|
||||
})
|
||||
|
||||
let { value, theme, indentSize, codeBlockTheme, showCopyNotification, storagePath } = this.props
|
||||
const { theme, indentSize, showCopyNotification, storagePath } = this.props
|
||||
let { value, codeBlockTheme } = this.props
|
||||
|
||||
this.refs.root.contentWindow.document.body.setAttribute('data-theme', theme)
|
||||
|
||||
@@ -316,7 +330,7 @@ export default class MarkdownPreview extends React.Component {
|
||||
let syntax = CodeMirror.findModeByName(el.className)
|
||||
if (syntax == null) syntax = CodeMirror.findModeByName('Plain Text')
|
||||
CodeMirror.requireMode(syntax.mode, () => {
|
||||
let content = htmlTextHelper.decodeEntities(el.innerHTML)
|
||||
const content = htmlTextHelper.decodeEntities(el.innerHTML)
|
||||
const copyIcon = document.createElement('i')
|
||||
copyIcon.innerHTML = '<button class="clipboardButton"><svg width="13" height="13" viewBox="0 0 1792 1792" ><path d="M768 1664h896v-640h-416q-40 0-68-28t-28-68v-416h-384v1152zm256-1440v-64q0-13-9.5-22.5t-22.5-9.5h-704q-13 0-22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h704q13 0 22.5-9.5t9.5-22.5zm256 672h299l-299-299v299zm512 128v672q0 40-28 68t-68 28h-960q-40 0-68-28t-28-68v-160h-544q-40 0-68-28t-28-68v-1344q0-40 28-68t68-28h1088q40 0 68 28t28 68v328q21 13 36 28l408 408q28 28 48 76t20 88z"/></svg></button>'
|
||||
copyIcon.onclick = (e) => {
|
||||
@@ -341,7 +355,7 @@ export default class MarkdownPreview extends React.Component {
|
||||
})
|
||||
})
|
||||
})
|
||||
let opts = {}
|
||||
const opts = {}
|
||||
// if (this.props.theme === 'dark') {
|
||||
// opts['font-color'] = '#DDD'
|
||||
// opts['line-color'] = '#DDD'
|
||||
@@ -351,7 +365,7 @@ export default class MarkdownPreview extends React.Component {
|
||||
_.forEach(this.refs.root.contentWindow.document.querySelectorAll('.flowchart'), (el) => {
|
||||
Raphael.setWindow(this.getWindow())
|
||||
try {
|
||||
let diagram = flowchart.parse(htmlTextHelper.decodeEntities(el.innerHTML))
|
||||
const diagram = flowchart.parse(htmlTextHelper.decodeEntities(el.innerHTML))
|
||||
el.innerHTML = ''
|
||||
diagram.drawSVG(el, opts)
|
||||
_.forEach(el.querySelectorAll('a'), (el) => {
|
||||
@@ -367,7 +381,7 @@ export default class MarkdownPreview extends React.Component {
|
||||
_.forEach(this.refs.root.contentWindow.document.querySelectorAll('.sequence'), (el) => {
|
||||
Raphael.setWindow(this.getWindow())
|
||||
try {
|
||||
let diagram = SequenceDiagram.parse(htmlTextHelper.decodeEntities(el.innerHTML))
|
||||
const diagram = SequenceDiagram.parse(htmlTextHelper.decodeEntities(el.innerHTML))
|
||||
el.innerHTML = ''
|
||||
diagram.drawSVG(el, {theme: 'simple'})
|
||||
_.forEach(el.querySelectorAll('a'), (el) => {
|
||||
@@ -390,11 +404,11 @@ export default class MarkdownPreview extends React.Component {
|
||||
}
|
||||
|
||||
scrollTo (targetRow) {
|
||||
let blocks = this.getWindow().document.querySelectorAll('body>[data-line]')
|
||||
const blocks = this.getWindow().document.querySelectorAll('body>[data-line]')
|
||||
|
||||
for (let index = 0; index < blocks.length; index++) {
|
||||
let block = blocks[index]
|
||||
let row = parseInt(block.getAttribute('data-line'))
|
||||
const row = parseInt(block.getAttribute('data-line'))
|
||||
if (row > targetRow || index === blocks.length - 1) {
|
||||
block = blocks[index - 1]
|
||||
block != null && this.getWindow().scrollTo(0, block.offsetTop)
|
||||
@@ -424,7 +438,7 @@ export default class MarkdownPreview extends React.Component {
|
||||
}
|
||||
|
||||
render () {
|
||||
let { className, style, tabIndex } = this.props
|
||||
const { className, style, tabIndex } = this.props
|
||||
return (
|
||||
<iframe className={className != null
|
||||
? 'MarkdownPreview ' + className
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React, {PropTypes} from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
import CSSModules from 'browser/lib/CSSModules'
|
||||
import styles from './ModalEscButton.styl'
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/**
|
||||
* @fileoverview Micro component for toggle SideNav
|
||||
*/
|
||||
import React, { PropTypes } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
import styles from './NavToggleButton.styl'
|
||||
import CSSModules from 'browser/lib/CSSModules'
|
||||
|
||||
|
||||
@@ -10,6 +10,9 @@
|
||||
line-height 32px
|
||||
padding 0
|
||||
|
||||
body[data-theme="white"]
|
||||
navWhiteButtonColor()
|
||||
|
||||
body[data-theme="dark"]
|
||||
.navToggle
|
||||
&:hover
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/**
|
||||
* @fileoverview Note item component.
|
||||
*/
|
||||
import React, { PropTypes } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
import { isArray } from 'lodash'
|
||||
import CSSModules from 'browser/lib/CSSModules'
|
||||
import { getTodoStatus } from 'browser/lib/getTodoStatus'
|
||||
@@ -70,7 +71,7 @@ const NoteItem = ({ isActive, note, dateDisplay, handleNoteClick, handleNoteCont
|
||||
|
||||
<div styleName='item-bottom-time'>{dateDisplay}</div>
|
||||
{note.isStarred
|
||||
? <i styleName='item-star' className='fa fa-star' /> : ''
|
||||
? <img styleName='item-star' src='../resources/icon/icon-starred.svg' /> : ''
|
||||
}
|
||||
{note.isPinned && !pathname.match(/\/home|\/starred|\/trash/)
|
||||
? <i styleName='item-pin' className='fa fa-thumb-tack' /> : ''
|
||||
|
||||
@@ -11,9 +11,9 @@ $control-height = 30px
|
||||
user-select none
|
||||
cursor pointer
|
||||
background-color $ui-noteList-backgroundColor
|
||||
transition background-color 0.2s
|
||||
transition 0.2s
|
||||
&:hover
|
||||
background-color alpha($ui-button--active-backgroundColor, 40%)
|
||||
background-color alpha($ui-button--active-backgroundColor, 20%)
|
||||
.item-title
|
||||
.item-title-icon
|
||||
.item-bottom-time
|
||||
@@ -25,7 +25,7 @@ $control-height = 30px
|
||||
.item-star
|
||||
color $ui-favorite-star-button-color
|
||||
&:active
|
||||
background-color $ui-button--active-backgroundColor
|
||||
background-color alpha($ui-button--active-backgroundColor, 40%)
|
||||
color $ui-text-color
|
||||
.item-title
|
||||
.item-title-icon
|
||||
@@ -43,7 +43,7 @@ $control-height = 30px
|
||||
|
||||
.item--active
|
||||
@extend .item
|
||||
background-color $ui-button--active-backgroundColor
|
||||
background-color alpha($ui-button--active-backgroundColor, 60%)
|
||||
color $ui-text-color
|
||||
.item-title
|
||||
.item-title-empty
|
||||
@@ -59,12 +59,12 @@ $control-height = 30px
|
||||
.item-star
|
||||
color $ui-favorite-star-button-color
|
||||
&:hover
|
||||
background-color alpha($ui-button--active-backgroundColor, 50%)
|
||||
background-color alpha($ui-button--active-backgroundColor, 40%)
|
||||
color #e74c3c
|
||||
.menu-button-label
|
||||
color $ui-text-color
|
||||
&:active, &:active:hover
|
||||
background-color alpha($ui-button--active-backgroundColor, 50%)
|
||||
background-color alpha($ui-button--active-backgroundColor, 40%)
|
||||
color #e74c3c
|
||||
.menu-button-label
|
||||
color $ui-text-color
|
||||
@@ -73,14 +73,16 @@ $control-height = 30px
|
||||
position relative
|
||||
font-size 12px
|
||||
color $ui-inactive-text-color
|
||||
top 2px
|
||||
|
||||
.item-title
|
||||
font-size 14px
|
||||
font-size 15px
|
||||
font-weight 700
|
||||
position relative
|
||||
top -12px
|
||||
left 20px
|
||||
padding-right 15px
|
||||
padding-bottom 4px
|
||||
padding 0px 15px 0px 0px
|
||||
margin-bottom 4px
|
||||
overflow ellipsis
|
||||
color $ui-inactive-text-color
|
||||
|
||||
@@ -91,7 +93,7 @@ $control-height = 30px
|
||||
.item-bottom
|
||||
position relative
|
||||
bottom 0px
|
||||
margin-top 2px
|
||||
margin-top 10px
|
||||
font-size 12px
|
||||
line-height 20px
|
||||
overflow ellipsis
|
||||
@@ -100,19 +102,17 @@ $control-height = 30px
|
||||
.item-bottom-tagList
|
||||
flex 1
|
||||
overflow ellipsis
|
||||
line-height 20px
|
||||
padding-top 7px
|
||||
line-height 25px
|
||||
padding-left 2px
|
||||
margin-right 27px
|
||||
margin-right 40px
|
||||
|
||||
.item-bottom-tagList-item
|
||||
font-size 11px
|
||||
margin-right 8px
|
||||
padding 0
|
||||
height 20px
|
||||
box-sizing border-box
|
||||
border-radius 2px
|
||||
padding 1px 2px
|
||||
padding 4px
|
||||
vertical-align middle
|
||||
background-color white
|
||||
color $ui-inactive-text-color
|
||||
@@ -125,10 +125,10 @@ $control-height = 30px
|
||||
|
||||
.item-star
|
||||
position absolute
|
||||
right -20px
|
||||
bottom 2px
|
||||
width 34px
|
||||
height 34px
|
||||
right -6px
|
||||
bottom 23px
|
||||
width 16px
|
||||
height 16px
|
||||
color alpha($ui-favorite-star-button-color, 60%)
|
||||
font-size 12px
|
||||
padding 0
|
||||
@@ -136,8 +136,8 @@ $control-height = 30px
|
||||
|
||||
.item-pin
|
||||
position absolute
|
||||
right -21px
|
||||
bottom 28px
|
||||
right 0px
|
||||
bottom 2px
|
||||
width 34px
|
||||
height 34px
|
||||
color #E54D42
|
||||
@@ -145,6 +145,20 @@ $control-height = 30px
|
||||
padding 0
|
||||
border-radius 17px
|
||||
|
||||
body[data-theme="white"]
|
||||
.item
|
||||
background-color $ui-white-noteList-backgroundColor
|
||||
&:hover
|
||||
background-color alpha($ui-button--active-backgroundColor, 60%)
|
||||
&:active
|
||||
background-color $ui-button--active-backgroundColor
|
||||
|
||||
.item--active
|
||||
@extend .item
|
||||
background-color $ui-button--active-backgroundColor
|
||||
&:hover
|
||||
background-color alpha($ui-button--active-backgroundColor, 60%)
|
||||
|
||||
body[data-theme="dark"]
|
||||
.root
|
||||
border-color $ui-dark-borderColor
|
||||
@@ -196,7 +210,7 @@ body[data-theme="dark"]
|
||||
background-color alpha(white, 10%)
|
||||
color $ui-dark-text-color
|
||||
&:hover
|
||||
background-color alpha($ui-dark-button--active-backgroundColor, 50%)
|
||||
background-color alpha($ui-dark-button--active-backgroundColor, 60%)
|
||||
color #c0392b
|
||||
.item-bottom-tagList-item
|
||||
background-color alpha(#fff, 20%)
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/**
|
||||
* @fileoverview Note item component with simple display mode.
|
||||
*/
|
||||
import React, { PropTypes } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
import CSSModules from 'browser/lib/CSSModules'
|
||||
import styles from './NoteItemSimple.styl'
|
||||
|
||||
|
||||
@@ -11,15 +11,15 @@ $control-height = 30px
|
||||
user-select none
|
||||
cursor pointer
|
||||
background-color $ui-noteList-backgroundColor
|
||||
transition background-color 0.15s
|
||||
transition 0.2s
|
||||
&:hover
|
||||
background-color alpha($ui-button--active-backgroundColor, 40%)
|
||||
background-color alpha($ui-button--active-backgroundColor, 20%)
|
||||
.item-simple-title
|
||||
.item-simple-title-empty
|
||||
.item-simple-title-icon
|
||||
color $ui-text-color
|
||||
&:active
|
||||
background-color $ui-button--active-backgroundColor
|
||||
background-color alpha($ui-button--active-backgroundColor, 40%)
|
||||
color $ui-text-color
|
||||
.item-simple-title
|
||||
.item-simple-title-empty
|
||||
@@ -28,7 +28,7 @@ $control-height = 30px
|
||||
|
||||
.item-simple--active
|
||||
@extend .item-simple
|
||||
background-color $ui-button--active-backgroundColor
|
||||
background-color alpha($ui-button--active-backgroundColor, 60%)
|
||||
color $ui-text-color
|
||||
.item-simple-title
|
||||
.item-simple-title-empty
|
||||
@@ -37,12 +37,12 @@ $control-height = 30px
|
||||
.item-simple-title-icon
|
||||
color $ui-text-color
|
||||
&:hover
|
||||
background-color alpha($ui-button--active-backgroundColor, 50%)
|
||||
background-color alpha($ui-button--active-backgroundColor, 40%)
|
||||
color #e74c3c
|
||||
.menu-button-label
|
||||
color $ui-text-color
|
||||
&:active, &:active:hover
|
||||
background-color alpha($ui-button--active-backgroundColor, 50%)
|
||||
background-color alpha($ui-button--active-backgroundColor, 40%)
|
||||
color #e74c3c
|
||||
.menu-button-label
|
||||
color $ui-text-color
|
||||
@@ -67,6 +67,20 @@ $control-height = 30px
|
||||
font-weight normal
|
||||
color $ui-inactive-text-color
|
||||
|
||||
body[data-theme="white"]
|
||||
.item-simple
|
||||
background-color $ui-white-noteList-backgroundColor
|
||||
&:hover
|
||||
background-color alpha($ui-button--active-backgroundColor, 60%)
|
||||
&:active
|
||||
background-color $ui-button--active-backgroundColor
|
||||
|
||||
.item-simple--active
|
||||
@extend .item-simple
|
||||
background-color $ui-button--active-backgroundColor
|
||||
&:hover
|
||||
background-color alpha($ui-button--active-backgroundColor, 60%)
|
||||
|
||||
body[data-theme="dark"]
|
||||
.root
|
||||
border-color $ui-dark-borderColor
|
||||
@@ -115,7 +129,7 @@ body[data-theme="dark"]
|
||||
background-color alpha(white, 10%)
|
||||
color $ui-dark-text-color
|
||||
&:hover
|
||||
background-color alpha($ui-dark-button--active-backgroundColor, 50%)
|
||||
background-color alpha($ui-dark-button--active-backgroundColor, 60%)
|
||||
color #c0392b
|
||||
.item-simple-bottom-tagList-item
|
||||
background-color alpha(#fff, 20%)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { PropTypes } from 'react'
|
||||
import React from 'react'
|
||||
import CSSModules from 'browser/lib/CSSModules'
|
||||
import styles from './RealtimeNotification.styl'
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/**
|
||||
* @fileoverview Filter for all notes.
|
||||
*/
|
||||
import React, { PropTypes } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
import CSSModules from 'browser/lib/CSSModules'
|
||||
import styles from './SideNavFilter.styl'
|
||||
|
||||
@@ -15,27 +16,53 @@ import styles from './SideNavFilter.styl'
|
||||
*/
|
||||
const SideNavFilter = ({
|
||||
isFolded, isHomeActive, handleAllNotesButtonClick,
|
||||
isStarredActive, handleStarredButtonClick, isTrashedActive, handleTrashedButtonClick
|
||||
isStarredActive, handleStarredButtonClick, isTrashedActive, handleTrashedButtonClick, counterDelNote,
|
||||
counterTotalNote, counterStarredNote
|
||||
}) => (
|
||||
<div styleName={isFolded ? 'menu--folded' : 'menu'}>
|
||||
|
||||
<button styleName={isHomeActive ? 'menu-button--active' : 'menu-button'}
|
||||
onClick={handleAllNotesButtonClick}
|
||||
>
|
||||
<i className='fa fa-archive fa-fw' />
|
||||
<div styleName='iconWrap'>
|
||||
<img src={isHomeActive
|
||||
? '../resources/icon/icon-all-active.svg'
|
||||
: '../resources/icon/icon-all.svg'
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<span styleName='menu-button-label'>All Notes</span>
|
||||
<span styleName='counters'>{counterTotalNote}</span>
|
||||
</button>
|
||||
|
||||
<button styleName={isStarredActive ? 'menu-button-star--active' : 'menu-button'}
|
||||
onClick={handleStarredButtonClick}
|
||||
>
|
||||
<i className='fa fa-star fa-fw' />
|
||||
<div styleName='iconWrap'>
|
||||
<img src={isStarredActive
|
||||
? '../resources/icon/icon-star-active.svg'
|
||||
: '../resources/icon/icon-star.svg'
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<span styleName='menu-button-label'>Starred</span>
|
||||
<span styleName='counters'>{counterStarredNote}</span>
|
||||
</button>
|
||||
<button styleName={isTrashedActive ? 'menu-button--active' : 'menu-button'}
|
||||
|
||||
<button styleName={isTrashedActive ? 'menu-button-trash--active' : 'menu-button'}
|
||||
onClick={handleTrashedButtonClick}
|
||||
>
|
||||
<i className='fa fa-trash fa-fw' />
|
||||
<div styleName='iconWrap'>
|
||||
<img src={isTrashedActive
|
||||
? '../resources/icon/icon-trash-active.svg'
|
||||
: '../resources/icon/icon-trash.svg'
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<span styleName='menu-button-label'>Trash</span>
|
||||
<span styleName='counters'>{counterDelNote}</span>
|
||||
</button>
|
||||
|
||||
</div>
|
||||
)
|
||||
|
||||
|
||||
@@ -3,49 +3,55 @@
|
||||
|
||||
.menu-button
|
||||
navButtonColor()
|
||||
height 32px
|
||||
padding 0 15px
|
||||
font-size 13px
|
||||
height 36px
|
||||
padding 0 15px 0 20px
|
||||
font-size 14px
|
||||
width 100%
|
||||
text-align left
|
||||
overflow ellipsis
|
||||
display flex
|
||||
align-items center
|
||||
&:hover, &:active, &:active:hover
|
||||
color #1EC38B
|
||||
background-color alpha($ui-button-default--active-backgroundColor, 20%)
|
||||
|
||||
.iconWrap
|
||||
width 20px
|
||||
text-align center
|
||||
|
||||
.counters
|
||||
float right
|
||||
color $ui-inactive-text-color
|
||||
|
||||
.menu-button--active
|
||||
@extend .menu-button
|
||||
color #e74c3c
|
||||
background-color $ui-button--active-backgroundColor
|
||||
.menu-button-label
|
||||
color $ui-text-color
|
||||
SideNavFilter()
|
||||
color #1EC38B
|
||||
background-color alpha($ui-button-default--active-backgroundColor, 20%)
|
||||
.menu-button-label, .counters
|
||||
color #1EC38B
|
||||
&:hover
|
||||
background-color alpha($ui-button--active-backgroundColor, 50%)
|
||||
color #e74c3c
|
||||
.menu-button-label
|
||||
color $ui-text-color
|
||||
&:active, &:active:hover
|
||||
background-color alpha($ui-button--active-backgroundColor, 50%)
|
||||
color #e74c3c
|
||||
.menu-button-label
|
||||
color $ui-text-color
|
||||
color #1EC38B
|
||||
|
||||
.menu-button-star--active
|
||||
@extend .menu-button
|
||||
color #F9BF3B
|
||||
background-color $ui-button--active-backgroundColor
|
||||
.menu-button-label
|
||||
color $ui-text-color
|
||||
&:hover
|
||||
background-color alpha($ui-button--active-backgroundColor, 50%)
|
||||
color #F9BF3B
|
||||
.menu-button-label
|
||||
color $ui-text-color
|
||||
&:active, &:active:hover
|
||||
background-color alpha($ui-button--active-backgroundColor, 50%)
|
||||
color #F9BF3B
|
||||
.menu-button-label
|
||||
color $ui-text-color
|
||||
SideNavFilter()
|
||||
color #1EC38B
|
||||
background-color alpha($ui-button-default--active-backgroundColor, 20%)
|
||||
.menu-button-label, .counters
|
||||
color #1EC38B
|
||||
|
||||
.menu-button-trash--active
|
||||
@extend .menu-button
|
||||
SideNavFilter()
|
||||
color #1EC38B
|
||||
background-color alpha($ui-button-default--active-backgroundColor, 20%)
|
||||
.menu-button-label, .counters
|
||||
color #1EC38B
|
||||
|
||||
.menu-button-label
|
||||
margin-left 5px
|
||||
margin-left 10px
|
||||
flex 1
|
||||
|
||||
.menu--folded
|
||||
@extend .menu
|
||||
@@ -54,6 +60,10 @@
|
||||
&:hover .menu-button-label
|
||||
transition opacity 0.15s
|
||||
opacity 1
|
||||
color $ui-tooltip-text-color
|
||||
background-color $ui-tooltip-backgroundColor
|
||||
|
||||
|
||||
.menu-button-label
|
||||
position fixed
|
||||
display inline-block
|
||||
@@ -63,15 +73,73 @@
|
||||
margin-top -8px
|
||||
margin-left 0
|
||||
overflow ellipsis
|
||||
background-color $ui-tooltip-backgroundColor
|
||||
z-index 10
|
||||
color white
|
||||
line-height 32px
|
||||
border-top-right-radius 2px
|
||||
border-bottom-right-radius 2px
|
||||
pointer-events none
|
||||
opacity 0
|
||||
font-size 13px
|
||||
.counters
|
||||
display none
|
||||
|
||||
body[data-theme="white"]
|
||||
.menu-button
|
||||
navWhiteButtonColor()
|
||||
|
||||
.counters
|
||||
color $ui-inactive-text-color
|
||||
|
||||
.menu-button--active
|
||||
@extend .menu-button
|
||||
color #e74c3c
|
||||
background-color $ui-button--active-backgroundColor
|
||||
.menu-button-label
|
||||
color $ui-text-color
|
||||
&:hover
|
||||
background-color alpha($ui-button--active-backgroundColor, 50%)
|
||||
color #e74c3c
|
||||
.menu-button-label
|
||||
color $ui-text-color
|
||||
&:active, &:active:hover
|
||||
background-color alpha($ui-button--active-backgroundColor, 50%)
|
||||
color #e74c3c
|
||||
.menu-button-label
|
||||
color $ui-text-color
|
||||
|
||||
.menu-button-star--active
|
||||
@extend .menu-button
|
||||
color #F9BF3B
|
||||
background-color $ui-button--active-backgroundColor
|
||||
.menu-button-label
|
||||
color $ui-text-color
|
||||
&:hover
|
||||
background-color alpha($ui-button--active-backgroundColor, 50%)
|
||||
color #F9BF3B
|
||||
.menu-button-label
|
||||
color $ui-text-color
|
||||
&:active, &:active:hover
|
||||
background-color alpha($ui-button--active-backgroundColor, 50%)
|
||||
color #F9BF3B
|
||||
.menu-button-label
|
||||
color $ui-text-color
|
||||
|
||||
.menu-button-trash--active
|
||||
@extend .menu-button
|
||||
color #5D9E36
|
||||
background-color $ui-button--active-backgroundColor
|
||||
.menu-button-label
|
||||
color $ui-text-color
|
||||
&:hover
|
||||
background-color alpha($ui-button--active-backgroundColor, 50%)
|
||||
color #5D9E36
|
||||
.menu-button-label
|
||||
color $ui-text-color
|
||||
&:active, &:active:hover
|
||||
background-color alpha($ui-button--active-backgroundColor, 50%)
|
||||
color #5D9E36
|
||||
.menu-button-label
|
||||
color $ui-text-color
|
||||
|
||||
body[data-theme="dark"]
|
||||
.menu-button
|
||||
@@ -103,3 +171,14 @@ body[data-theme="dark"]
|
||||
color $ui-favorite-star-button-color
|
||||
.menu-button-label
|
||||
color $ui-dark-text-color
|
||||
|
||||
.menu-button-trash--active
|
||||
color #5D9E36
|
||||
background-color $ui-dark-button--active-backgroundColor
|
||||
.menu-button-label
|
||||
color $ui-dark-text-color
|
||||
&:hover
|
||||
background-color alpha($ui-dark-button--active-backgroundColor, 50%)
|
||||
color #5D9E36
|
||||
.menu-button-label
|
||||
color $ui-dark-text-color
|
||||
@@ -86,7 +86,7 @@ class SnippetTab extends React.Component {
|
||||
}
|
||||
|
||||
render () {
|
||||
let { isActive, snippet, isDeletable } = this.props
|
||||
const { isActive, snippet, isDeletable } = this.props
|
||||
return (
|
||||
<div styleName={isActive
|
||||
? 'root--active'
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/**
|
||||
* @fileoverview Micro component for showing storage.
|
||||
*/
|
||||
import React, { PropTypes } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
import styles from './StorageItem.styl'
|
||||
import CSSModules from 'browser/lib/CSSModules'
|
||||
import { isNumber } from 'lodash'
|
||||
|
||||
@@ -5,32 +5,31 @@
|
||||
.folderList-item
|
||||
display flex
|
||||
width 100%
|
||||
height 26px
|
||||
height 34px
|
||||
background-color transparent
|
||||
color $ui-inactive-text-color
|
||||
padding 0
|
||||
margin-bottom 5px
|
||||
text-align left
|
||||
border none
|
||||
overflow ellipsis
|
||||
font-size 13px
|
||||
font-size 14px
|
||||
&:first-child
|
||||
margin-top 0
|
||||
&:hover
|
||||
color $ui-text-color
|
||||
background-color alpha($ui-button--active-backgroundColor, 20%)
|
||||
color #1EC38B;
|
||||
background-color alpha($ui-button-default--active-backgroundColor, 20%)
|
||||
transition background-color 0.15s
|
||||
&:active
|
||||
color $ui-text-color
|
||||
background-color $ui-button--active-backgroundColor
|
||||
|
||||
color $$ui-button-default-color
|
||||
background-color alpha($ui-button-default--active-backgroundColor, 20%)
|
||||
|
||||
.folderList-item--active
|
||||
@extend .folderList-item
|
||||
color $ui-text-color
|
||||
background-color $ui-button--active-backgroundColor
|
||||
color #1EC38B
|
||||
background-color alpha($ui-button-default--active-backgroundColor, 20%)
|
||||
&:hover
|
||||
color $ui-text-color
|
||||
background-color alpha($ui-button--active-backgroundColor, 50%)
|
||||
color #1EC38B;
|
||||
background-color alpha($ui-button-default--active-backgroundColor, 50%)
|
||||
|
||||
.folderList-item-name
|
||||
display block
|
||||
@@ -69,8 +68,28 @@
|
||||
|
||||
.folderList-item-name--folded
|
||||
@extend .folderList-item-name
|
||||
padding-left 12px
|
||||
padding-left 17px
|
||||
text
|
||||
display none
|
||||
|
||||
body[data-theme="white"]
|
||||
.folderList-item
|
||||
color $ui-inactive-text-color
|
||||
&:hover
|
||||
color $ui-text-color
|
||||
background-color alpha($ui-button--active-backgroundColor, 20%)
|
||||
transition background-color 0.15s
|
||||
&:active
|
||||
color $ui-text-color
|
||||
background-color $ui-button--active-backgroundColor
|
||||
|
||||
.folderList-item--active
|
||||
@extend .folderList-item
|
||||
color $ui-text-color
|
||||
background-color $ui-button--active-backgroundColor
|
||||
&:hover
|
||||
color $ui-text-color
|
||||
background-color alpha($ui-button--active-backgroundColor, 50%)
|
||||
|
||||
body[data-theme="dark"]
|
||||
.folderList-item
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
/**
|
||||
* @fileoverview Micro component for showing StorageList
|
||||
*/
|
||||
import React, { PropTypes } from 'react'
|
||||
import styles from './StorgaeList.styl'
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
import styles from './StorageList.styl'
|
||||
import CSSModules from 'browser/lib/CSSModules'
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
.storageList
|
||||
absolute left right
|
||||
bottom 37px
|
||||
top 160px
|
||||
top 180px
|
||||
overflow-y auto
|
||||
|
||||
.storageList-empty
|
||||
@@ -1,7 +1,8 @@
|
||||
/**
|
||||
* @fileoverview Micro component for showing TagList.
|
||||
*/
|
||||
import React, { PropTypes } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
import styles from './TagListItem.styl'
|
||||
import CSSModules from 'browser/lib/CSSModules'
|
||||
|
||||
|
||||
@@ -13,15 +13,15 @@
|
||||
&:first-child
|
||||
margin-top 0
|
||||
&:hover
|
||||
color $ui-text-color
|
||||
background-color alpha($ui-button--active-backgroundColor, 20%)
|
||||
color $ui-button-default-color
|
||||
background-color alpha($ui-button-default--active-backgroundColor, 20%)
|
||||
transition background-color 0.15s
|
||||
&:active
|
||||
color $ui-text-color
|
||||
background-color $ui-button--active-backgroundColor
|
||||
&:active, &:active:hover
|
||||
color $ui-button-default-color
|
||||
background-color $ui-button-default--active-backgroundColor
|
||||
|
||||
.tagList-item-active
|
||||
background-color $ui-button--active-backgroundColor
|
||||
background-color $ui-button-default--active-backgroundColor
|
||||
display flex
|
||||
width 100%
|
||||
height 26px
|
||||
@@ -31,8 +31,9 @@
|
||||
border none
|
||||
overflow ellipsis
|
||||
font-size 13px
|
||||
color $ui-button-default-color
|
||||
&:hover
|
||||
background-color alpha($ui-button--active-backgroundColor, 60%)
|
||||
background-color alpha($ui-button-default--active-backgroundColor, 60%)
|
||||
transition 0.2s
|
||||
|
||||
.tagList-item-name
|
||||
@@ -47,6 +48,22 @@
|
||||
overflow hidden
|
||||
text-overflow ellipsis
|
||||
|
||||
body[data-theme="white"]
|
||||
.tagList-item
|
||||
color $ui-inactive-text-color
|
||||
&:hover
|
||||
color $ui-text-color
|
||||
background-color alpha($ui-button--active-backgroundColor, 20%)
|
||||
&:active
|
||||
color $ui-text-color
|
||||
background-color $ui-button--active-backgroundColor
|
||||
|
||||
.tagList-item-active
|
||||
background-color $ui-button--active-backgroundColor
|
||||
color $ui-text-color
|
||||
&:hover
|
||||
background-color alpha($ui-button--active-backgroundColor, 60%)
|
||||
|
||||
body[data-theme="dark"]
|
||||
.tagList-item
|
||||
color $ui-dark-inactive-text-color
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
* @fileoverview Percentage of todo achievement.
|
||||
*/
|
||||
|
||||
import React, { PropTypes } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
import CSSModules from 'browser/lib/CSSModules'
|
||||
import styles from './TodoListPercentage.styl'
|
||||
|
||||
|
||||
@@ -1,24 +1,25 @@
|
||||
.percentageBar
|
||||
position absolute
|
||||
top 58px
|
||||
right: 0px
|
||||
top 50px
|
||||
right 0px
|
||||
left 0px
|
||||
background-color #DADFE1
|
||||
width 100%
|
||||
height: 15px
|
||||
height: 17px
|
||||
font-size: 12px
|
||||
z-index 100
|
||||
border-radius 2px
|
||||
|
||||
.progressBar
|
||||
background-color: #6C7A89
|
||||
height 15px
|
||||
background-color: #1EC38B
|
||||
height 17px
|
||||
border-radius 2px
|
||||
transition 0.3s
|
||||
transition 0.4s cubic-bezier(0.4, 0.4, 0, 1)
|
||||
|
||||
.percentageText
|
||||
color #f4f4f4
|
||||
padding: 2px 43%
|
||||
font-weight 600
|
||||
|
||||
body[data-theme="dark"]
|
||||
.percentageBar
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
* @fileoverview Percentage of todo achievement.
|
||||
*/
|
||||
|
||||
import React, { PropTypes } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
import CSSModules from 'browser/lib/CSSModules'
|
||||
import styles from './TodoProcess.styl'
|
||||
|
||||
|
||||
@@ -118,7 +118,7 @@ hr
|
||||
h1, h2, h3, h4, h5, h6
|
||||
font-weight bold
|
||||
h1
|
||||
font-size 2.25em
|
||||
font-size 2.55em
|
||||
padding-bottom 0.3em
|
||||
line-height 1.2em
|
||||
border-bottom solid 1px borderColor
|
||||
|
||||
@@ -64,7 +64,7 @@ $list-width = 250px
|
||||
|
||||
.result-nav-storageList
|
||||
absolute bottom left right
|
||||
top 110px + 32px + 10px + 10px
|
||||
top 110px + 32px + 10px + 10px + 20px
|
||||
overflow-y auto
|
||||
|
||||
.result-list
|
||||
|
||||
@@ -56,7 +56,7 @@ class NoteDetail extends React.Component {
|
||||
}
|
||||
|
||||
selectPriorSnippet () {
|
||||
let { note } = this.props
|
||||
const { note } = this.props
|
||||
if (note.type === 'SNIPPET_NOTE' && note.snippets.length > 1) {
|
||||
this.setState({
|
||||
snippetIndex: (this.state.snippetIndex + note.snippets.length - 1) % note.snippets.length
|
||||
@@ -65,7 +65,7 @@ class NoteDetail extends React.Component {
|
||||
}
|
||||
|
||||
selectNextSnippet () {
|
||||
let { note } = this.props
|
||||
const { note } = this.props
|
||||
if (note.type === 'SNIPPET_NOTE' && note.snippets.length > 1) {
|
||||
this.setState({
|
||||
snippetIndex: (this.state.snippetIndex + 1) % note.snippets.length
|
||||
@@ -74,7 +74,7 @@ class NoteDetail extends React.Component {
|
||||
}
|
||||
|
||||
saveToClipboard () {
|
||||
let { note } = this.props
|
||||
const { note } = this.props
|
||||
|
||||
if (note.type === 'MARKDOWN_NOTE') {
|
||||
clipboard.writeText(note.content)
|
||||
@@ -95,7 +95,7 @@ class NoteDetail extends React.Component {
|
||||
}
|
||||
|
||||
render () {
|
||||
let { note, config } = this.props
|
||||
const { note, config } = this.props
|
||||
if (note == null) {
|
||||
return (
|
||||
<div styleName='root' />
|
||||
@@ -110,8 +110,8 @@ class NoteDetail extends React.Component {
|
||||
const storage = findStorage(note.storage)
|
||||
|
||||
if (note.type === 'SNIPPET_NOTE') {
|
||||
let tabList = note.snippets.map((snippet, index) => {
|
||||
let isActive = this.state.snippetIndex === index
|
||||
const tabList = note.snippets.map((snippet, index) => {
|
||||
const isActive = this.state.snippetIndex === index
|
||||
return <div styleName={isActive
|
||||
? 'tabList-item--active'
|
||||
: 'tabList-item'
|
||||
@@ -131,8 +131,8 @@ class NoteDetail extends React.Component {
|
||||
</div>
|
||||
})
|
||||
|
||||
let viewList = note.snippets.map((snippet, index) => {
|
||||
let isActive = this.state.snippetIndex === index
|
||||
const viewList = note.snippets.map((snippet, index) => {
|
||||
const isActive = this.state.snippetIndex === index
|
||||
|
||||
let syntax = CodeMirror.findModeByName(pass(snippet.mode))
|
||||
if (syntax == null) syntax = CodeMirror.findModeByName('Plain Text')
|
||||
|
||||
@@ -18,18 +18,18 @@ class NoteList extends React.Component {
|
||||
}
|
||||
|
||||
componentDidUpdate () {
|
||||
let { index } = this.props
|
||||
const { index } = this.props
|
||||
|
||||
if (index > -1) {
|
||||
let list = this.refs.root
|
||||
let item = list.childNodes[index]
|
||||
const list = this.refs.root
|
||||
const item = list.childNodes[index]
|
||||
if (item == null) return null
|
||||
|
||||
let overflowBelow = item.offsetTop + item.clientHeight - list.clientHeight - list.scrollTop > 0
|
||||
const overflowBelow = item.offsetTop + item.clientHeight - list.clientHeight - list.scrollTop > 0
|
||||
if (overflowBelow) {
|
||||
list.scrollTop = item.offsetTop + item.clientHeight - list.clientHeight
|
||||
}
|
||||
let overflowAbove = list.scrollTop > item.offsetTop
|
||||
const overflowAbove = list.scrollTop > item.offsetTop
|
||||
if (overflowAbove) {
|
||||
list.scrollTop = item.offsetTop
|
||||
}
|
||||
@@ -44,7 +44,7 @@ class NoteList extends React.Component {
|
||||
}
|
||||
|
||||
handleScroll (e) {
|
||||
let { notes } = this.props
|
||||
const { notes } = this.props
|
||||
|
||||
if (e.target.offsetHeight + e.target.scrollTop > e.target.scrollHeight - 100 && notes.length > this.state.range * 10 + 10) {
|
||||
this.setState({
|
||||
@@ -54,9 +54,9 @@ class NoteList extends React.Component {
|
||||
}
|
||||
|
||||
render () {
|
||||
let { notes, index } = this.props
|
||||
const { notes, index } = this.props
|
||||
|
||||
let notesList = notes
|
||||
const notesList = notes
|
||||
.slice(0, 10 + 10 * this.state.range)
|
||||
.map((note, _index) => {
|
||||
const isActive = (index === _index)
|
||||
|
||||
@@ -19,18 +19,18 @@ class StorageSection extends React.Component {
|
||||
}
|
||||
|
||||
handleHeaderClick (e) {
|
||||
let { storage } = this.props
|
||||
const { storage } = this.props
|
||||
this.props.handleStorageButtonClick(e, storage.key)
|
||||
}
|
||||
|
||||
handleFolderClick (e, folder) {
|
||||
let { storage } = this.props
|
||||
const { storage } = this.props
|
||||
this.props.handleFolderButtonClick(e, storage.key, folder.key)
|
||||
}
|
||||
|
||||
render () {
|
||||
let { storage, filter } = this.props
|
||||
let folderList = storage.folders
|
||||
const { storage, filter } = this.props
|
||||
const folderList = storage.folders
|
||||
.map(folder => (
|
||||
<StorageItem
|
||||
key={folder.key}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import React, { PropTypes } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import { connect, Provider } from 'react-redux'
|
||||
import _ from 'lodash'
|
||||
import ipc from './ipcClient'
|
||||
import store from './store'
|
||||
import CSSModules from 'browser/lib/CSSModules'
|
||||
import styles from './FinderMain.styl'
|
||||
@@ -19,7 +19,7 @@ const { remote } = electron
|
||||
const { Menu } = remote
|
||||
|
||||
function hideFinder () {
|
||||
let finderWindow = remote.getCurrentWindow()
|
||||
const finderWindow = remote.getCurrentWindow()
|
||||
if (global.process.platform === 'win32') {
|
||||
finderWindow.blur()
|
||||
finderWindow.hide()
|
||||
@@ -136,7 +136,7 @@ class FinderMain extends React.Component {
|
||||
}
|
||||
|
||||
handleOnlySnippetCheckboxChange (e) {
|
||||
let { filter } = this.state
|
||||
const { filter } = this.state
|
||||
filter.includeSnippet = e.target.checked
|
||||
this.setState({
|
||||
filter: filter,
|
||||
@@ -147,7 +147,7 @@ class FinderMain extends React.Component {
|
||||
}
|
||||
|
||||
handleOnlyMarkdownCheckboxChange (e) {
|
||||
let { filter } = this.state
|
||||
const { filter } = this.state
|
||||
filter.includeMarkdown = e.target.checked
|
||||
this.refs.list.resetScroll()
|
||||
this.setState({
|
||||
@@ -159,7 +159,7 @@ class FinderMain extends React.Component {
|
||||
}
|
||||
|
||||
handleAllNotesButtonClick (e) {
|
||||
let { filter } = this.state
|
||||
const { filter } = this.state
|
||||
filter.type = 'ALL'
|
||||
this.refs.list.resetScroll()
|
||||
this.setState({
|
||||
@@ -171,7 +171,7 @@ class FinderMain extends React.Component {
|
||||
}
|
||||
|
||||
handleStarredButtonClick (e) {
|
||||
let { filter } = this.state
|
||||
const { filter } = this.state
|
||||
filter.type = 'STARRED'
|
||||
this.refs.list.resetScroll()
|
||||
this.setState({
|
||||
@@ -183,7 +183,7 @@ class FinderMain extends React.Component {
|
||||
}
|
||||
|
||||
handleStorageButtonClick (e, storage) {
|
||||
let { filter } = this.state
|
||||
const { filter } = this.state
|
||||
filter.type = 'STORAGE'
|
||||
filter.storage = storage
|
||||
this.refs.list.resetScroll()
|
||||
@@ -196,7 +196,7 @@ class FinderMain extends React.Component {
|
||||
}
|
||||
|
||||
handleFolderButtonClick (e, storage, folder) {
|
||||
let { filter } = this.state
|
||||
const { filter } = this.state
|
||||
filter.type = 'FOLDER'
|
||||
filter.storage = storage
|
||||
filter.folder = folder
|
||||
@@ -218,12 +218,12 @@ class FinderMain extends React.Component {
|
||||
}
|
||||
|
||||
render () {
|
||||
let { data, config } = this.props
|
||||
let { filter, search } = this.state
|
||||
let storageList = []
|
||||
for (let key in data.storageMap) {
|
||||
let storage = data.storageMap[key]
|
||||
let item = (
|
||||
const { data, config } = this.props
|
||||
const { filter, search } = this.state
|
||||
const storageList = []
|
||||
for (const key in data.storageMap) {
|
||||
const storage = data.storageMap[key]
|
||||
const item = (
|
||||
<StorageSection
|
||||
filter={filter}
|
||||
storage={storage}
|
||||
@@ -252,7 +252,7 @@ class FinderMain extends React.Component {
|
||||
notes.push(data.noteMap[id])
|
||||
})
|
||||
} else {
|
||||
for (let key in data.noteMap) {
|
||||
for (const key in data.noteMap) {
|
||||
notes.push(data.noteMap[key])
|
||||
}
|
||||
}
|
||||
@@ -264,13 +264,13 @@ class FinderMain extends React.Component {
|
||||
}
|
||||
|
||||
if (search.trim().length > 0) {
|
||||
let needle = new RegExp(_.escapeRegExp(search.trim()), 'i')
|
||||
const needle = new RegExp(_.escapeRegExp(search.trim()), 'i')
|
||||
notes = notes.filter((note) => note.title.match(needle))
|
||||
}
|
||||
notes = notes
|
||||
.sort((a, b) => new Date(b.updatedAt) - new Date(a.updatedAt))
|
||||
|
||||
let activeNote = notes[this.state.index]
|
||||
const activeNote = notes[this.state.index]
|
||||
this.noteCount = notes.length
|
||||
|
||||
return (
|
||||
|
||||
@@ -10,7 +10,7 @@ nodeIpc.config.retry = 1500
|
||||
nodeIpc.config.silent = true
|
||||
|
||||
function killFinder () {
|
||||
let finderWindow = remote.getCurrentWindow()
|
||||
const finderWindow = remote.getCurrentWindow()
|
||||
finderWindow.removeAllListeners()
|
||||
if (global.process.platform === 'darwin') {
|
||||
// Only OSX has another app process.
|
||||
@@ -21,7 +21,7 @@ function killFinder () {
|
||||
}
|
||||
|
||||
function toggleFinder () {
|
||||
let finderWindow = remote.getCurrentWindow()
|
||||
const finderWindow = remote.getCurrentWindow()
|
||||
if (global.process.platform === 'darwin') {
|
||||
if (finderWindow.isVisible()) {
|
||||
finderWindow.hide()
|
||||
@@ -84,6 +84,8 @@ nodeIpc.connectTo(
|
||||
const { config } = payload
|
||||
if (config.ui.theme === 'dark') {
|
||||
document.body.setAttribute('data-theme', 'dark')
|
||||
} else if (config.ui.theme === 'white') {
|
||||
document.body.setAttribute('data-theme', 'white')
|
||||
} else {
|
||||
document.body.setAttribute('data-theme', 'default')
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { combineReducers, createStore } from 'redux'
|
||||
import { routerReducer } from 'react-router-redux'
|
||||
import { DEFAULT_CONFIG } from 'browser/main/lib/ConfigManager'
|
||||
|
||||
let defaultData = {
|
||||
const defaultData = {
|
||||
storageMap: {},
|
||||
noteMap: {},
|
||||
starredSet: [],
|
||||
@@ -40,12 +40,12 @@ function config (state = DEFAULT_CONFIG, action) {
|
||||
return state
|
||||
}
|
||||
|
||||
let reducer = combineReducers({
|
||||
const reducer = combineReducers({
|
||||
data,
|
||||
config,
|
||||
routing: routerReducer
|
||||
})
|
||||
|
||||
let store = createStore(reducer)
|
||||
const store = createStore(reducer)
|
||||
|
||||
export default store
|
||||
|
||||
@@ -38,15 +38,15 @@ class MutableMap {
|
||||
}
|
||||
|
||||
map (cb) {
|
||||
let result = []
|
||||
for (let [key, value] of this._map) {
|
||||
const result = []
|
||||
for (const [key, value] of this._map) {
|
||||
result.push(cb(value, key))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
toJS () {
|
||||
let result = {}
|
||||
const result = {}
|
||||
for (let [key, value] of this._map) {
|
||||
if (value instanceof MutableSet || value instanceof MutableMap) {
|
||||
value = value.toJS()
|
||||
@@ -85,7 +85,7 @@ class MutableSet {
|
||||
}
|
||||
|
||||
map (cb) {
|
||||
let result = []
|
||||
const result = []
|
||||
this._set.forEach(function (value, key) {
|
||||
result.push(cb(value, key))
|
||||
})
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
export function findNoteTitle (value) {
|
||||
let splitted = value.split('\n')
|
||||
const splitted = value.split('\n')
|
||||
let title = null
|
||||
let isInsideCodeBlock = false
|
||||
|
||||
splitted.some((line, index) => {
|
||||
let trimmedLine = line.trim()
|
||||
let trimmedNextLine = splitted[index + 1] === undefined ? '' : splitted[index + 1].trim()
|
||||
const trimmedLine = line.trim()
|
||||
const trimmedNextLine = splitted[index + 1] === undefined ? '' : splitted[index + 1].trim()
|
||||
if (trimmedLine.match('```')) {
|
||||
isInsideCodeBlock = !isInsideCodeBlock
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
export function getTodoStatus (content) {
|
||||
let splitted = content.split('\n')
|
||||
const splitted = content.split('\n')
|
||||
let numberOfTodo = 0
|
||||
let numberOfCompletedTodo = 0
|
||||
|
||||
splitted.forEach((line) => {
|
||||
let trimmedLine = line.trim()
|
||||
const trimmedLine = line.trim()
|
||||
if (trimmedLine.match(/^[\+\-\*] \[\s|x\] ./)) {
|
||||
numberOfTodo++
|
||||
}
|
||||
|
||||
@@ -3,11 +3,12 @@ import emoji from 'markdown-it-emoji'
|
||||
import math from '@rokt33r/markdown-it-math'
|
||||
import _ from 'lodash'
|
||||
|
||||
// FIXME We should not depend on global variable.
|
||||
const katex = window.katex
|
||||
|
||||
function createGutter (str) {
|
||||
let lc = (str.match(/\n/g) || []).length
|
||||
let lines = []
|
||||
const lc = (str.match(/\n/g) || []).length
|
||||
const lines = []
|
||||
for (let i = 1; i <= lc; i++) {
|
||||
lines.push('<span class="CodeMirror-linenumber">' + i + '</span>')
|
||||
}
|
||||
@@ -75,8 +76,8 @@ md.use(require('markdown-it-plantuml'))
|
||||
md.block.ruler.at('paragraph', function (state, startLine/*, endLine */) {
|
||||
let content, terminate, i, l, token
|
||||
let nextLine = startLine + 1
|
||||
let terminatorRules = state.md.block.ruler.getRules('paragraph')
|
||||
let endLine = state.lineMax
|
||||
const terminatorRules = state.md.block.ruler.getRules('paragraph')
|
||||
const endLine = state.lineMax
|
||||
|
||||
// jump line-by-line until empty one or EOF
|
||||
for (; nextLine < endLine && !state.isEmpty(nextLine); nextLine++) {
|
||||
@@ -106,7 +107,7 @@ md.block.ruler.at('paragraph', function (state, startLine/*, endLine */) {
|
||||
token.map = [ startLine, state.line ]
|
||||
|
||||
if (state.parentType === 'list') {
|
||||
let match = content.match(/^\[( |x)\] ?(.+)/i)
|
||||
const match = content.match(/^\[( |x)\] ?(.+)/i)
|
||||
if (match) {
|
||||
content = `<label class='taskListItem' for='checkbox-${startLine + 1}'><input type='checkbox'${match[1] !== ' ' ? ' checked' : ''} id='checkbox-${startLine + 1}'/> ${content.substring(4, content.length)}</label>`
|
||||
}
|
||||
@@ -123,7 +124,7 @@ md.block.ruler.at('paragraph', function (state, startLine/*, endLine */) {
|
||||
})
|
||||
|
||||
// Add line number attribute for scrolling
|
||||
let originalRender = md.renderer.render
|
||||
const originalRender = md.renderer.render
|
||||
md.renderer.render = function render (tokens, options, env) {
|
||||
tokens.forEach((token) => {
|
||||
switch (token.type) {
|
||||
@@ -134,40 +135,12 @@ md.renderer.render = function render (tokens, options, env) {
|
||||
token.attrPush(['data-line', token.map[0]])
|
||||
}
|
||||
})
|
||||
let result = originalRender.call(md.renderer, tokens, options, env)
|
||||
const result = originalRender.call(md.renderer, tokens, options, env)
|
||||
return result
|
||||
}
|
||||
// FIXME We should not depend on global variable.
|
||||
window.md = md
|
||||
|
||||
function strip (input) {
|
||||
var output = input
|
||||
try {
|
||||
output = output
|
||||
.replace(/^([\s\t]*)([\*\-\+]|\d\.)\s+/gm, '$1')
|
||||
.replace(/\n={2,}/g, '\n')
|
||||
.replace(/~~/g, '')
|
||||
.replace(/`{3}.*\n/g, '')
|
||||
.replace(/<(.*?)>/g, '$1')
|
||||
.replace(/^[=\-]{2,}\s*$/g, '')
|
||||
.replace(/\[\^.+?\](: .*?$)?/g, '')
|
||||
.replace(/\s{0,2}\[.*?\]: .*?$/g, '')
|
||||
.replace(/!\[.*?\][\[\(].*?[\]\)]/g, '')
|
||||
.replace(/\[(.*?)\][\[\(].*?[\]\)]/g, '$1')
|
||||
.replace(/>/g, '')
|
||||
.replace(/^\s{1,2}\[(.*?)\]: (\S+)( ".*?")?\s*$/g, '')
|
||||
.replace(/^#{1,6}\s*([^#]*)\s*(#{1,6})?/gm, '$1')
|
||||
.replace(/([\*_]{1,3})(\S.*?\S)\1/g, '$2')
|
||||
.replace(/(`{3,})(.*?)\1/gm, '$2')
|
||||
.replace(/^-{3,}\s*$/g, '')
|
||||
.replace(/`(.+?)`/g, '$1')
|
||||
.replace(/\n{2,}/g, '\n\n')
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
return input
|
||||
}
|
||||
return output
|
||||
}
|
||||
|
||||
function normalizeLinkText (linkText) {
|
||||
return md.normalizeLinkText(linkText)
|
||||
}
|
||||
@@ -178,7 +151,7 @@ const markdown = {
|
||||
const renderedContent = md.render(content)
|
||||
return renderedContent
|
||||
},
|
||||
strip,
|
||||
normalizeLinkText
|
||||
}
|
||||
|
||||
export default markdown
|
||||
|
||||
39
browser/lib/markdownTextHelper.js
Normal file
39
browser/lib/markdownTextHelper.js
Normal file
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* @fileoverview Text trimmer for markdown note.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @return {string}
|
||||
*/
|
||||
export function strip (input) {
|
||||
let output = input
|
||||
try {
|
||||
output = output
|
||||
.replace(/^([\s\t]*)([\*\-\+]|\d+\.)\s+/gm, '$1')
|
||||
.replace(/\n={2,}/g, '\n')
|
||||
.replace(/~~/g, '')
|
||||
.replace(/`{3}.*\n/g, '')
|
||||
.replace(/<(.*?)>/g, '$1')
|
||||
.replace(/^[=\-]{2,}\s*$/g, '')
|
||||
.replace(/\[\^.+?\](: .*?$)?/g, '')
|
||||
.replace(/\s{0,2}\[.*?\]: .*?$/g, '')
|
||||
.replace(/!\[.*?\][\[\(].*?[\]\)]/g, '')
|
||||
.replace(/\[(.*?)\][\[\(].*?[\]\)]/g, '$1')
|
||||
.replace(/>/g, '')
|
||||
.replace(/^\s{1,2}\[(.*?)\]: (\S+)( ".*?")?\s*$/g, '')
|
||||
.replace(/^#{1,6}\s*([^#]*)\s*(#{1,6})?/gm, '$1')
|
||||
.replace(/(`{3,})(.*?)\1/gm, '$2')
|
||||
.replace(/^-{3,}\s*$/g, '')
|
||||
.replace(/`(.+?)`/g, '$1')
|
||||
.replace(/\n{2,}/g, '\n\n')
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
return input
|
||||
}
|
||||
return output
|
||||
}
|
||||
|
||||
export default {
|
||||
strip
|
||||
}
|
||||
@@ -16,7 +16,7 @@ export default function searchFromNotes (notes, search) {
|
||||
|
||||
function findByTag (notes, block) {
|
||||
const tag = block.match(/#(.+)/)[1]
|
||||
let regExp = new RegExp(_.escapeRegExp(tag), 'i')
|
||||
const regExp = new RegExp(_.escapeRegExp(tag), 'i')
|
||||
return notes.filter((note) => {
|
||||
if (!_.isArray(note.tags)) return false
|
||||
return note.tags.some((_tag) => {
|
||||
@@ -26,7 +26,7 @@ function findByTag (notes, block) {
|
||||
}
|
||||
|
||||
function findByWord (notes, block) {
|
||||
let regExp = new RegExp(_.escapeRegExp(block), 'i')
|
||||
const regExp = new RegExp(_.escapeRegExp(block), 'i')
|
||||
return notes.filter((note) => {
|
||||
if (_.isArray(note.tags) && note.tags.some((_tag) => {
|
||||
return _tag.match(regExp)
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
.root
|
||||
absolute top bottom right
|
||||
display flex
|
||||
align-items center
|
||||
justify-content center
|
||||
|
||||
.empty
|
||||
height 320px
|
||||
@@ -8,8 +11,9 @@
|
||||
|
||||
.empty-message
|
||||
width 100%
|
||||
font-size 42px
|
||||
line-height 72px
|
||||
font-size 36px
|
||||
font-weight 600
|
||||
line-height 56px
|
||||
text-align center
|
||||
color $ui-inactive-text-color
|
||||
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
*/
|
||||
|
||||
// Margin on the left side and the right side for NoteDetail component.
|
||||
$note-detail-left-margin = 25px
|
||||
$note-detail-right-margin = 25px
|
||||
$note-detail-left-margin = 100px
|
||||
$note-detail-right-margin = 120px
|
||||
$snippet-note-detail-left-margin = 60px
|
||||
$snippet-note-detail-right-margin = 80px
|
||||
|
||||
$note-detail-box-shadow = 2px 0 15px -8px #b1b1b1 inset
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React, { PropTypes } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
import CSSModules from 'browser/lib/CSSModules'
|
||||
import styles from './FolderSelect.styl'
|
||||
import _ from 'lodash'
|
||||
@@ -73,8 +74,8 @@ class FolderSelect extends React.Component {
|
||||
case 9:
|
||||
if (e.shiftKey) {
|
||||
e.preventDefault()
|
||||
let tabbable = document.querySelectorAll('a:not([disabled]), button:not([disabled]), input[type=text]:not([disabled]), [tabindex]:not([disabled]):not([tabindex="-1"])')
|
||||
let previousEl = tabbable[Array.prototype.indexOf.call(tabbable, this.refs.root) - 1]
|
||||
const tabbable = document.querySelectorAll('a:not([disabled]), button:not([disabled]), input[type=text]:not([disabled]), [tabindex]:not([disabled]):not([tabindex="-1"])')
|
||||
const previousEl = tabbable[Array.prototype.indexOf.call(tabbable, this.refs.root) - 1]
|
||||
if (previousEl != null) previousEl.focus()
|
||||
}
|
||||
}
|
||||
@@ -89,9 +90,9 @@ class FolderSelect extends React.Component {
|
||||
}
|
||||
|
||||
handleSearchInputChange (e) {
|
||||
let { folders } = this.props
|
||||
let search = this.refs.search.value
|
||||
let optionIndex = search.length > 0
|
||||
const { folders } = this.props
|
||||
const search = this.refs.search.value
|
||||
const optionIndex = search.length > 0
|
||||
? _.findIndex(folders, (folder) => {
|
||||
return folder.name.match(new RegExp('^' + _.escapeRegExp(search), 'i'))
|
||||
})
|
||||
@@ -129,7 +130,7 @@ class FolderSelect extends React.Component {
|
||||
|
||||
nextOption () {
|
||||
let { optionIndex } = this.state
|
||||
let { folders } = this.props
|
||||
const { folders } = this.props
|
||||
|
||||
optionIndex++
|
||||
if (optionIndex >= folders.length) optionIndex = 0
|
||||
@@ -140,7 +141,7 @@ class FolderSelect extends React.Component {
|
||||
}
|
||||
|
||||
previousOption () {
|
||||
let { folders } = this.props
|
||||
const { folders } = this.props
|
||||
let { optionIndex } = this.state
|
||||
|
||||
optionIndex--
|
||||
@@ -152,10 +153,10 @@ class FolderSelect extends React.Component {
|
||||
}
|
||||
|
||||
selectOption () {
|
||||
let { folders } = this.props
|
||||
let optionIndex = this.state.optionIndex
|
||||
const { folders } = this.props
|
||||
const optionIndex = this.state.optionIndex
|
||||
|
||||
let folder = folders[optionIndex]
|
||||
const folder = folders[optionIndex]
|
||||
if (folder != null) {
|
||||
this.setState({
|
||||
status: 'FOCUS'
|
||||
@@ -184,10 +185,10 @@ class FolderSelect extends React.Component {
|
||||
}
|
||||
|
||||
render () {
|
||||
let { className, data, value } = this.props
|
||||
let splitted = value.split('-')
|
||||
let storageKey = splitted.shift()
|
||||
let folderKey = splitted.shift()
|
||||
const { className, data, value } = this.props
|
||||
const splitted = value.split('-')
|
||||
const storageKey = splitted.shift()
|
||||
const folderKey = splitted.shift()
|
||||
let options = []
|
||||
data.storageMap.forEach((storage, index) => {
|
||||
storage.folders.forEach((folder) => {
|
||||
@@ -198,14 +199,14 @@ class FolderSelect extends React.Component {
|
||||
})
|
||||
})
|
||||
|
||||
let currentOption = options.filter((option) => option.storage.key === storageKey && option.folder.key === folderKey)[0]
|
||||
const currentOption = options.filter((option) => option.storage.key === storageKey && option.folder.key === folderKey)[0]
|
||||
|
||||
if (this.state.search.trim().length > 0) {
|
||||
let filter = new RegExp('^' + _.escapeRegExp(this.state.search), 'i')
|
||||
const filter = new RegExp('^' + _.escapeRegExp(this.state.search), 'i')
|
||||
options = options.filter((option) => filter.test(option.folder.name))
|
||||
}
|
||||
|
||||
let optionList = options
|
||||
const optionList = options
|
||||
.map((option, index) => {
|
||||
return (
|
||||
<div styleName={index === this.state.optionIndex
|
||||
@@ -259,12 +260,11 @@ class FolderSelect extends React.Component {
|
||||
{optionList}
|
||||
</div>
|
||||
</div>
|
||||
: <div styleName='idle'>
|
||||
: <div styleName='idle' style={{color: currentOption.folder.color}}>
|
||||
<div styleName='idle-label'>
|
||||
<span styleName='idle-label-name'
|
||||
style={{color: currentOption.folder.color}}
|
||||
>
|
||||
{currentOption.folder.name} /
|
||||
<i className='fa fa-folder' />
|
||||
<span styleName='idle-label-name'>
|
||||
{currentOption.folder.name}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
.root
|
||||
position relative
|
||||
border solid 1px transparent
|
||||
line-height 26px
|
||||
vertical-align middle
|
||||
border-radius 2px
|
||||
transition 0.15s
|
||||
user-select none
|
||||
margin-right 10px
|
||||
&:hover
|
||||
background-color $ui-button--hover-backgroundColor
|
||||
|
||||
.root--search, .root--focus
|
||||
@extend .root
|
||||
background-color $ui-noteDetail-backgroundColor = #F4F4F4
|
||||
background-color $ui-noteDetail-backgroundColor = #fff
|
||||
border-color $ui-input--focus-borderColor
|
||||
width 100px
|
||||
width 154px
|
||||
height 30px
|
||||
&:hover
|
||||
border-color $ui-input--focus-borderColor
|
||||
border-color $ui-input--focus-borderColor = #fff
|
||||
|
||||
.idle
|
||||
position relative
|
||||
@@ -24,13 +25,16 @@
|
||||
.idle-label
|
||||
right 20px
|
||||
overflow ellipsis
|
||||
display flex
|
||||
align-items center
|
||||
|
||||
.idle-label-name
|
||||
font-size 14px
|
||||
padding 2px
|
||||
font-size 13px
|
||||
font-weight 600
|
||||
margin-left 4px
|
||||
|
||||
.idle-label-name-surfix
|
||||
font-size 10px
|
||||
font-size 15px
|
||||
color $ui-inactive-text-color
|
||||
margin-left 5px
|
||||
.idle-caret
|
||||
@@ -38,40 +42,42 @@
|
||||
height 34px
|
||||
width 20px
|
||||
line-height 34px
|
||||
|
||||
.search
|
||||
absolute top left right bottom
|
||||
line-height 34px
|
||||
|
||||
|
||||
.search-input
|
||||
vertical-align middle
|
||||
position relative
|
||||
top -2px
|
||||
top 0
|
||||
font-size 14px
|
||||
outline none
|
||||
border none
|
||||
height 20px
|
||||
line-height 20px
|
||||
width 100%
|
||||
background-color transparent
|
||||
padding 0 10px
|
||||
|
||||
.search-optionList
|
||||
position fixed
|
||||
position absolute
|
||||
top 30px
|
||||
max-height 450px
|
||||
min-width 150px
|
||||
overflow auto
|
||||
z-index 200
|
||||
border $ui-border
|
||||
background-color white
|
||||
border-radius 2px
|
||||
padding 10px 6px
|
||||
|
||||
.search-optionList-item
|
||||
width 140px
|
||||
height 34px
|
||||
width 250px
|
||||
display flex
|
||||
align-items center
|
||||
box-sizing border-box
|
||||
padding 0 5px
|
||||
padding 0
|
||||
margin-bottom 10px
|
||||
overflow ellipsis
|
||||
cursor pointer
|
||||
&:hover
|
||||
background-color $ui-button--hover-backgroundColor
|
||||
background-color $ui-button--hover-backgroundColor
|
||||
|
||||
.search-optionList-item--active
|
||||
@extend .search-optionList-item
|
||||
@@ -81,13 +87,17 @@
|
||||
background-color $ui-button--active-backgroundColor
|
||||
color $ui-button--active-color
|
||||
.search-optionList-item-name
|
||||
border-left solid 2px transparent
|
||||
padding 2px 5px
|
||||
border-left solid 3px transparent
|
||||
padding 6px
|
||||
.search-optionList-item-name-surfix
|
||||
font-size 10px
|
||||
color $ui-inactive-text-color
|
||||
margin-left 5px
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
body[data-theme="dark"]
|
||||
.root
|
||||
color $ui-dark-text-color
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React, { PropTypes } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
import CSSModules from 'browser/lib/CSSModules'
|
||||
import styles from './InfoButton.styl'
|
||||
|
||||
@@ -6,9 +7,9 @@ const InfoButton = ({
|
||||
onClick
|
||||
}) => (
|
||||
<button styleName='control-infoButton'
|
||||
onClick={onClick}
|
||||
onClick={(e) => onClick(e)}
|
||||
>
|
||||
<i className='fa fa-info infoButton' styleName='info-button' />
|
||||
<img className='infoButton' src='../resources/icon/icon-info.svg' />
|
||||
</button>
|
||||
)
|
||||
|
||||
|
||||
@@ -1,20 +1,10 @@
|
||||
.control-infoButton
|
||||
float right
|
||||
top 10px
|
||||
margin-bottom 10px
|
||||
topBarButtonLight()
|
||||
|
||||
.control-infoPanel
|
||||
position fixed
|
||||
pointer-events none
|
||||
top 50px
|
||||
z-index 200
|
||||
line-height normal
|
||||
border-radius 2px
|
||||
opacity 0
|
||||
transition 0.1s
|
||||
|
||||
.infoButton
|
||||
padding 0px
|
||||
margin 15px 0
|
||||
|
||||
body[data-theme="dark"]
|
||||
.control-infoButton
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React, { PropTypes } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
import CSSModules from 'browser/lib/CSSModules'
|
||||
import styles from './InfoPanel.styl'
|
||||
|
||||
|
||||
@@ -10,12 +10,12 @@
|
||||
|
||||
.control-infoButton-panel
|
||||
z-index 200
|
||||
margin-top 38px
|
||||
margin-left -140px
|
||||
margin-top 0px
|
||||
right 0
|
||||
position absolute
|
||||
padding 20px 25px 0 25px
|
||||
width 300px
|
||||
height 500px
|
||||
height 350px
|
||||
overflow auto
|
||||
background-color $ui-noteList-backgroundColor
|
||||
box-shadow 2px 12px 15px 2px rgba(0, 0, 0, 0.1), 2px 1px 50px 2px rgba(0, 0, 0, 0.1)
|
||||
@@ -32,8 +32,8 @@
|
||||
|
||||
.control-infoButton-panel-trash
|
||||
z-index 200
|
||||
margin-top 45px
|
||||
margin-left -190px
|
||||
margin-top 0px
|
||||
right 0px
|
||||
position absolute
|
||||
padding 20px 25px 0 25px
|
||||
width 300px
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React, { PropTypes } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
import CSSModules from 'browser/lib/CSSModules'
|
||||
import styles from './InfoPanel.styl'
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React, { PropTypes } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
import CSSModules from 'browser/lib/CSSModules'
|
||||
import styles from './MarkdownNoteDetail.styl'
|
||||
import MarkdownEditor from 'browser/components/MarkdownEditor'
|
||||
@@ -9,12 +10,13 @@ import FolderSelect from './FolderSelect'
|
||||
import dataApi from 'browser/main/lib/dataApi'
|
||||
import { hashHistory } from 'react-router'
|
||||
import ee from 'browser/main/lib/eventEmitter'
|
||||
import markdown from 'browser/lib/markdown'
|
||||
import markdown from 'browser/lib/markdownTextHelper'
|
||||
import StatusBar from '../StatusBar'
|
||||
import _ from 'lodash'
|
||||
import { findNoteTitle } from 'browser/lib/findNoteTitle'
|
||||
import AwsMobileAnalyticsConfig from 'browser/main/lib/AwsMobileAnalyticsConfig'
|
||||
import TrashButton from './TrashButton'
|
||||
import PermanentDeleteButton from './PermanentDeleteButton'
|
||||
import InfoButton from './InfoButton'
|
||||
import InfoPanel from './InfoPanel'
|
||||
import InfoPanelTrashed from './InfoPanelTrashed'
|
||||
@@ -24,7 +26,7 @@ import striptags from 'striptags'
|
||||
|
||||
const electron = require('electron')
|
||||
const { remote } = electron
|
||||
const { Menu, MenuItem, dialog } = remote
|
||||
const { dialog } = remote
|
||||
|
||||
class MarkdownNoteDetail extends React.Component {
|
||||
constructor (props) {
|
||||
@@ -73,7 +75,7 @@ class MarkdownNoteDetail extends React.Component {
|
||||
}
|
||||
|
||||
handleChange (e) {
|
||||
let { note } = this.state
|
||||
const { note } = this.state
|
||||
|
||||
note.content = this.refs.content.value
|
||||
if (this.refs.tags) note.tags = this.refs.tags.value
|
||||
@@ -95,7 +97,7 @@ class MarkdownNoteDetail extends React.Component {
|
||||
}
|
||||
|
||||
saveNow () {
|
||||
let { note, dispatch } = this.props
|
||||
const { note, dispatch } = this.props
|
||||
clearTimeout(this.saveQueue)
|
||||
this.saveQueue = null
|
||||
|
||||
@@ -111,11 +113,11 @@ class MarkdownNoteDetail extends React.Component {
|
||||
}
|
||||
|
||||
handleFolderChange (e) {
|
||||
let { note } = this.state
|
||||
let value = this.refs.folder.value
|
||||
let splitted = value.split('-')
|
||||
let newStorageKey = splitted.shift()
|
||||
let newFolderKey = splitted.shift()
|
||||
const { note } = this.state
|
||||
const value = this.refs.folder.value
|
||||
const splitted = value.split('-')
|
||||
const newStorageKey = splitted.shift()
|
||||
const newFolderKey = splitted.shift()
|
||||
|
||||
dataApi
|
||||
.moveNote(note.storage, note.key, newStorageKey, newFolderKey)
|
||||
@@ -124,7 +126,7 @@ class MarkdownNoteDetail extends React.Component {
|
||||
isMovingNote: true,
|
||||
note: Object.assign({}, newNote)
|
||||
}, () => {
|
||||
let { dispatch, location } = this.props
|
||||
const { dispatch, location } = this.props
|
||||
dispatch({
|
||||
type: 'MOVE_NOTE',
|
||||
originNote: note,
|
||||
@@ -144,7 +146,7 @@ class MarkdownNoteDetail extends React.Component {
|
||||
}
|
||||
|
||||
handleStarButtonClick (e) {
|
||||
let { note } = this.state
|
||||
const { note } = this.state
|
||||
if (!note.isStarred) AwsMobileAnalyticsConfig.recordDynamicCustomEvent('ADD_STAR')
|
||||
|
||||
note.isStarred = !note.isStarred
|
||||
@@ -169,22 +171,22 @@ class MarkdownNoteDetail extends React.Component {
|
||||
}
|
||||
|
||||
handleTrashButtonClick (e) {
|
||||
let { note } = this.state
|
||||
const { note } = this.state
|
||||
const { isTrashed } = note
|
||||
|
||||
if (isTrashed) {
|
||||
let dialogueButtonIndex = dialog.showMessageBox(remote.getCurrentWindow(), {
|
||||
const dialogueButtonIndex = dialog.showMessageBox(remote.getCurrentWindow(), {
|
||||
type: 'warning',
|
||||
message: 'Confirm note deletion',
|
||||
detail: 'This will permanently remove this note.',
|
||||
buttons: ['Confirm', 'Cancel']
|
||||
})
|
||||
if (dialogueButtonIndex === 1) return
|
||||
let { note, dispatch } = this.props
|
||||
const { note, dispatch } = this.props
|
||||
dataApi
|
||||
.deleteNote(note.storage, note.key)
|
||||
.then((data) => {
|
||||
let dispatchHandler = () => {
|
||||
const dispatchHandler = () => {
|
||||
dispatch({
|
||||
type: 'DELETE_NOTE',
|
||||
storageKey: data.storageKey,
|
||||
@@ -206,7 +208,7 @@ class MarkdownNoteDetail extends React.Component {
|
||||
}
|
||||
|
||||
handleUndoButtonClick (e) {
|
||||
let { note } = this.state
|
||||
const { note } = this.state
|
||||
|
||||
note.isTrashed = false
|
||||
|
||||
@@ -231,7 +233,7 @@ class MarkdownNoteDetail extends React.Component {
|
||||
}
|
||||
|
||||
getToggleLockButton () {
|
||||
return this.state.isLocked ? 'fa-lock' : 'fa-unlock'
|
||||
return this.state.isLocked ? '../resources/icon/icon-lock.svg' : '../resources/icon/icon-unlock.svg'
|
||||
}
|
||||
|
||||
handleDeleteKeyDown (e) {
|
||||
@@ -261,12 +263,12 @@ class MarkdownNoteDetail extends React.Component {
|
||||
}
|
||||
|
||||
render () {
|
||||
let { data, config, location } = this.props
|
||||
let { note } = this.state
|
||||
let storageKey = note.storage
|
||||
let folderKey = note.folder
|
||||
const { data, config, location } = this.props
|
||||
const { note } = this.state
|
||||
const storageKey = note.storage
|
||||
const folderKey = note.folder
|
||||
|
||||
let options = []
|
||||
const options = []
|
||||
data.storageMap.forEach((storage, index) => {
|
||||
storage.folders.forEach((folder) => {
|
||||
options.push({
|
||||
@@ -275,7 +277,7 @@ class MarkdownNoteDetail extends React.Component {
|
||||
})
|
||||
})
|
||||
})
|
||||
let currentOption = options.filter((option) => option.storage.key === storageKey && option.folder.key === folderKey)[0]
|
||||
const currentOption = options.filter((option) => option.storage.key === storageKey && option.folder.key === folderKey)[0]
|
||||
|
||||
const trashTopBar = <div styleName='info'>
|
||||
<div styleName='info-left'>
|
||||
@@ -285,7 +287,7 @@ class MarkdownNoteDetail extends React.Component {
|
||||
/>
|
||||
</div>
|
||||
<div styleName='info-right'>
|
||||
<TrashButton onClick={(e) => this.handleTrashButtonClick(e)} />
|
||||
<PermanentDeleteButton onClick={(e) => this.handleTrashButtonClick(e)} />
|
||||
<InfoButton
|
||||
onClick={(e) => this.handleInfoButtonClick(e)}
|
||||
/>
|
||||
@@ -302,10 +304,6 @@ class MarkdownNoteDetail extends React.Component {
|
||||
|
||||
const detailTopBar = <div styleName='info'>
|
||||
<div styleName='info-left'>
|
||||
<StarButton styleName='info-left-button'
|
||||
onClick={(e) => this.handleStarButtonClick(e)}
|
||||
isActive={note.isStarred}
|
||||
/>
|
||||
<div styleName='info-left-top'>
|
||||
<FolderSelect styleName='info-left-top-folderSelect'
|
||||
value={this.state.note.storage + '-' + this.state.note.folder}
|
||||
@@ -325,31 +323,38 @@ class MarkdownNoteDetail extends React.Component {
|
||||
/>
|
||||
</div>
|
||||
<div styleName='info-right'>
|
||||
<InfoButton
|
||||
onClick={(e) => this.handleInfoButtonClick(e)}
|
||||
/>
|
||||
|
||||
<StarButton
|
||||
onClick={(e) => this.handleStarButtonClick(e)}
|
||||
isActive={note.isStarred}
|
||||
/>
|
||||
|
||||
{(() => {
|
||||
const faClassName = `fa ${this.getToggleLockButton()}`
|
||||
const imgSrc = `${this.getToggleLockButton()}`
|
||||
const lockButtonComponent =
|
||||
<button styleName='control-lockButton'
|
||||
onFocus={(e) => this.handleFocus(e)}
|
||||
onMouseDown={(e) => this.handleLockButtonMouseDown(e)}
|
||||
>
|
||||
<i className={faClassName} styleName='lock-button' />
|
||||
<span styleName='control-lockButton-tooltip'>
|
||||
{this.state.isLocked ? 'Unlock Editor' : 'Keep Editor Locked'}
|
||||
</span>
|
||||
<img styleName='iconInfo' src={imgSrc} />
|
||||
</button>
|
||||
|
||||
return (
|
||||
this.state.isLockButtonShown ? lockButtonComponent : ''
|
||||
)
|
||||
})()}
|
||||
<TrashButton onClick={(e) => this.handleTrashButtonClick(e)} />
|
||||
|
||||
<button styleName='control-fullScreenButton'
|
||||
onMouseDown={(e) => this.handleFullScreenButton(e)}
|
||||
>
|
||||
<i className='fa fa-window-maximize' styleName='fullScreen-button' />
|
||||
<img styleName='iconInfo' src='../resources/icon/icon-sidebar.svg' />
|
||||
</button>
|
||||
<InfoButton
|
||||
onClick={(e) => this.handleInfoButtonClick(e)}
|
||||
/>
|
||||
|
||||
<TrashButton onClick={(e) => this.handleTrashButtonClick(e)} />
|
||||
|
||||
<InfoPanel
|
||||
storageName={currentOption.storage.name}
|
||||
folderName={currentOption.folder.name}
|
||||
|
||||
@@ -3,47 +3,47 @@
|
||||
|
||||
.root
|
||||
absolute top right bottom
|
||||
border-width 0
|
||||
border-left 1px solid alpha(#DEDEDE, 60%)
|
||||
background-color $ui-noteDetail-backgroundColor
|
||||
box-shadow $note-detail-box-shadow
|
||||
box-shadow none
|
||||
padding 20px 40px
|
||||
|
||||
.lock-button
|
||||
padding-bottom 3px
|
||||
|
||||
.control-lockButton
|
||||
top 160px
|
||||
margin-bottom 10px
|
||||
topBarButtonLight()
|
||||
|
||||
.control-lockButton-tooltip
|
||||
tooltip()
|
||||
position fixed
|
||||
pointer-events none
|
||||
top 50px
|
||||
z-index 200
|
||||
padding 5px
|
||||
line-height normal
|
||||
border-radius 2px
|
||||
opacity 0
|
||||
transition 0.1s
|
||||
.trashed-infopanel
|
||||
top 40px
|
||||
position relative
|
||||
|
||||
.control-fullScreenButton
|
||||
float right
|
||||
padding 0 0 2px 0
|
||||
top 80px
|
||||
topBarButtonLight()
|
||||
|
||||
.body
|
||||
absolute left right
|
||||
left $note-detail-left-margin
|
||||
right $note-detail-right-margin
|
||||
left 0
|
||||
right 0
|
||||
top $info-height + $info-margin-under-border
|
||||
bottom $statusBar-height
|
||||
|
||||
margin 0 45px
|
||||
.body-noteEditor
|
||||
absolute top bottom left right
|
||||
|
||||
body[data-theme="white"]
|
||||
.root
|
||||
box-shadow $note-detail-box-shadow
|
||||
border none
|
||||
|
||||
body[data-theme="dark"]
|
||||
.root
|
||||
background-color $ui-dark-noteDetail-backgroundColor
|
||||
box-shadow none
|
||||
border none
|
||||
|
||||
.control-lockButton
|
||||
topBarButtonDark()
|
||||
|
||||
@@ -1,33 +1,31 @@
|
||||
@import('DetailVars')
|
||||
|
||||
$info-height = 60px
|
||||
$info-margin-under-border = 27px
|
||||
$info-height = 50px
|
||||
$info-margin-under-border = 30px
|
||||
|
||||
.info
|
||||
absolute top left right
|
||||
left $note-detail-left-margin
|
||||
right $note-detail-right-margin
|
||||
left 0
|
||||
right 0
|
||||
height $info-height
|
||||
border-bottom $ui-border
|
||||
border-bottom 1px solid #eee
|
||||
background-color $ui-noteDetail-backgroundColor
|
||||
width 100%
|
||||
display flex
|
||||
align-items center
|
||||
|
||||
.info-left
|
||||
float left
|
||||
padding 0 5px
|
||||
margin 0px 2px
|
||||
.info-left-top
|
||||
display inline-block
|
||||
height $info-height
|
||||
line-height $info-height
|
||||
padding 0 10px
|
||||
width 100%
|
||||
display flex
|
||||
align-items center
|
||||
|
||||
|
||||
.info-left-top-folderSelect
|
||||
padding 0 3px
|
||||
height 34px
|
||||
line-height 26px
|
||||
display flex
|
||||
align-items center
|
||||
justify-content center
|
||||
border-radius 3px
|
||||
|
||||
.info-left-button
|
||||
width 34px
|
||||
height 34px
|
||||
@@ -48,18 +46,18 @@ $info-margin-under-border = 27px
|
||||
|
||||
.info-right
|
||||
position absolute
|
||||
right 0
|
||||
top 0
|
||||
background $ui-noteDetail-backgroundColor
|
||||
right 40px
|
||||
top 60px
|
||||
bottom 1px
|
||||
padding-left 30px
|
||||
z-index 101
|
||||
|
||||
.undo-button
|
||||
width 34px
|
||||
height 34px
|
||||
border-radius 17px
|
||||
font-size 14px
|
||||
margin 15px 7px
|
||||
margin 5px 0px
|
||||
border none
|
||||
color $ui-button-color
|
||||
display flex
|
||||
@@ -72,6 +70,7 @@ $info-margin-under-border = 27px
|
||||
border-color $ui-button--active-backgroundColor
|
||||
&:hover
|
||||
background-color alpha($ui-button--hover-backgroundColor, 60%)
|
||||
transition 0.2s
|
||||
.control-lockButton-tooltip
|
||||
opacity 1
|
||||
|
||||
|
||||
20
browser/main/Detail/PermanentDeleteButton.js
Normal file
20
browser/main/Detail/PermanentDeleteButton.js
Normal file
@@ -0,0 +1,20 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
import CSSModules from 'browser/lib/CSSModules'
|
||||
import styles from './TrashButton.styl'
|
||||
|
||||
const PermanentDeleteButton = ({
|
||||
onClick
|
||||
}) => (
|
||||
<button styleName='control-trashButton--in-trash'
|
||||
onClick={(e) => onClick(e)}
|
||||
>
|
||||
<img styleName='iconInfo' src='../resources/icon/icon-trash.svg' />
|
||||
</button>
|
||||
)
|
||||
|
||||
PermanentDeleteButton.propTypes = {
|
||||
onClick: PropTypes.func.isRequired
|
||||
}
|
||||
|
||||
export default CSSModules(PermanentDeleteButton, styles)
|
||||
@@ -1,4 +1,5 @@
|
||||
import React, { PropTypes } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
import CSSModules from 'browser/lib/CSSModules'
|
||||
import styles from './SnippetNoteDetail.styl'
|
||||
import CodeEditor from 'browser/components/CodeEditor'
|
||||
@@ -18,6 +19,7 @@ import _ from 'lodash'
|
||||
import { findNoteTitle } from 'browser/lib/findNoteTitle'
|
||||
import AwsMobileAnalyticsConfig from 'browser/main/lib/AwsMobileAnalyticsConfig'
|
||||
import TrashButton from './TrashButton'
|
||||
import PermanentDeleteButton from './PermanentDeleteButton'
|
||||
import InfoButton from './InfoButton'
|
||||
import InfoPanel from './InfoPanel'
|
||||
import InfoPanelTrashed from './InfoPanelTrashed'
|
||||
@@ -60,7 +62,7 @@ class SnippetNoteDetail extends React.Component {
|
||||
componentWillReceiveProps (nextProps) {
|
||||
if (nextProps.note.key !== this.props.note.key && !this.isMovingNote) {
|
||||
if (this.saveQueue != null) this.saveNow()
|
||||
let nextNote = Object.assign({
|
||||
const nextNote = Object.assign({
|
||||
description: ''
|
||||
}, nextProps.note, {
|
||||
snippets: nextProps.note.snippets.map((snippet) => Object.assign({}, snippet))
|
||||
@@ -69,7 +71,7 @@ class SnippetNoteDetail extends React.Component {
|
||||
snippetIndex: 0,
|
||||
note: nextNote
|
||||
}, () => {
|
||||
let { snippets } = this.state.note
|
||||
const { snippets } = this.state.note
|
||||
snippets.forEach((snippet, index) => {
|
||||
this.refs['code-' + index].reload()
|
||||
})
|
||||
@@ -83,7 +85,7 @@ class SnippetNoteDetail extends React.Component {
|
||||
}
|
||||
|
||||
handleChange (e) {
|
||||
let { note } = this.state
|
||||
const { note } = this.state
|
||||
|
||||
if (this.refs.tags) note.tags = this.refs.tags.value
|
||||
note.description = this.refs.description.value
|
||||
@@ -105,7 +107,7 @@ class SnippetNoteDetail extends React.Component {
|
||||
}
|
||||
|
||||
saveNow () {
|
||||
let { note, dispatch } = this.props
|
||||
const { note, dispatch } = this.props
|
||||
clearTimeout(this.saveQueue)
|
||||
this.saveQueue = null
|
||||
|
||||
@@ -121,11 +123,11 @@ class SnippetNoteDetail extends React.Component {
|
||||
}
|
||||
|
||||
handleFolderChange (e) {
|
||||
let { note } = this.state
|
||||
let value = this.refs.folder.value
|
||||
let splitted = value.split('-')
|
||||
let newStorageKey = splitted.shift()
|
||||
let newFolderKey = splitted.shift()
|
||||
const { note } = this.state
|
||||
const value = this.refs.folder.value
|
||||
const splitted = value.split('-')
|
||||
const newStorageKey = splitted.shift()
|
||||
const newFolderKey = splitted.shift()
|
||||
|
||||
dataApi
|
||||
.moveNote(note.storage, note.key, newStorageKey, newFolderKey)
|
||||
@@ -134,7 +136,7 @@ class SnippetNoteDetail extends React.Component {
|
||||
isMovingNote: true,
|
||||
note: Object.assign({}, newNote)
|
||||
}, () => {
|
||||
let { dispatch, location } = this.props
|
||||
const { dispatch, location } = this.props
|
||||
dispatch({
|
||||
type: 'MOVE_NOTE',
|
||||
originNote: note,
|
||||
@@ -154,7 +156,7 @@ class SnippetNoteDetail extends React.Component {
|
||||
}
|
||||
|
||||
handleStarButtonClick (e) {
|
||||
let { note } = this.state
|
||||
const { note } = this.state
|
||||
if (!note.isStarred) AwsMobileAnalyticsConfig.recordDynamicCustomEvent('ADD_STAR')
|
||||
|
||||
note.isStarred = !note.isStarred
|
||||
@@ -171,22 +173,22 @@ class SnippetNoteDetail extends React.Component {
|
||||
}
|
||||
|
||||
handleTrashButtonClick (e) {
|
||||
let { note } = this.state
|
||||
const { note } = this.state
|
||||
const { isTrashed } = note
|
||||
|
||||
if (isTrashed) {
|
||||
let dialogueButtonIndex = dialog.showMessageBox(remote.getCurrentWindow(), {
|
||||
const dialogueButtonIndex = dialog.showMessageBox(remote.getCurrentWindow(), {
|
||||
type: 'warning',
|
||||
message: 'Confirm note deletion',
|
||||
detail: 'This will permanently remove this note.',
|
||||
buttons: ['Confirm', 'Cancel']
|
||||
})
|
||||
if (dialogueButtonIndex === 1) return
|
||||
let { note, dispatch } = this.props
|
||||
const { note, dispatch } = this.props
|
||||
dataApi
|
||||
.deleteNote(note.storage, note.key)
|
||||
.then((data) => {
|
||||
let dispatchHandler = () => {
|
||||
const dispatchHandler = () => {
|
||||
dispatch({
|
||||
type: 'DELETE_NOTE',
|
||||
storageKey: data.storageKey,
|
||||
@@ -208,7 +210,7 @@ class SnippetNoteDetail extends React.Component {
|
||||
}
|
||||
|
||||
handleUndoButtonClick (e) {
|
||||
let { note } = this.state
|
||||
const { note } = this.state
|
||||
|
||||
note.isTrashed = false
|
||||
|
||||
@@ -237,7 +239,7 @@ class SnippetNoteDetail extends React.Component {
|
||||
handleTabDeleteButtonClick (e, index) {
|
||||
if (this.state.note.snippets.length > 1) {
|
||||
if (this.state.note.snippets[index].content.trim().length > 0) {
|
||||
let dialogIndex = dialog.showMessageBox(remote.getCurrentWindow(), {
|
||||
const dialogIndex = dialog.showMessageBox(remote.getCurrentWindow(), {
|
||||
type: 'warning',
|
||||
message: 'Delete a snippet',
|
||||
detail: 'This work cannot be undone.',
|
||||
@@ -287,7 +289,7 @@ class SnippetNoteDetail extends React.Component {
|
||||
|
||||
handleModeOptionClick (index, name) {
|
||||
return (e) => {
|
||||
let snippets = this.state.note.snippets.slice()
|
||||
const snippets = this.state.note.snippets.slice()
|
||||
snippets[index].mode = name
|
||||
this.setState({note: Object.assign(this.state.note, {snippets: snippets})})
|
||||
|
||||
@@ -305,7 +307,7 @@ class SnippetNoteDetail extends React.Component {
|
||||
|
||||
handleCodeChange (index) {
|
||||
return (e) => {
|
||||
let snippets = this.state.note.snippets.slice()
|
||||
const snippets = this.state.note.snippets.slice()
|
||||
snippets[index].content = this.refs['code-' + index].value
|
||||
this.setState({note: Object.assign(this.state.note, {snippets: snippets})})
|
||||
this.setState({
|
||||
@@ -332,7 +334,7 @@ class SnippetNoteDetail extends React.Component {
|
||||
break
|
||||
case 76:
|
||||
{
|
||||
let isSuper = global.process.platform === 'darwin'
|
||||
const isSuper = global.process.platform === 'darwin'
|
||||
? e.metaKey
|
||||
: e.ctrlKey
|
||||
if (isSuper) {
|
||||
@@ -343,7 +345,7 @@ class SnippetNoteDetail extends React.Component {
|
||||
break
|
||||
case 84:
|
||||
{
|
||||
let isSuper = global.process.platform === 'darwin'
|
||||
const isSuper = global.process.platform === 'darwin'
|
||||
? e.metaKey
|
||||
: e.ctrlKey
|
||||
if (isSuper) {
|
||||
@@ -356,7 +358,7 @@ class SnippetNoteDetail extends React.Component {
|
||||
}
|
||||
|
||||
handleModeButtonClick (e, index) {
|
||||
let menu = new Menu()
|
||||
const menu = new Menu()
|
||||
CodeMirror.modeInfo.forEach((mode) => {
|
||||
menu.append(new MenuItem({
|
||||
label: mode.name,
|
||||
@@ -397,8 +399,8 @@ class SnippetNoteDetail extends React.Component {
|
||||
}
|
||||
|
||||
handleIndentSizeItemClick (e, indentSize) {
|
||||
let { config, dispatch } = this.props
|
||||
let editor = Object.assign({}, config.editor, {
|
||||
const { config, dispatch } = this.props
|
||||
const editor = Object.assign({}, config.editor, {
|
||||
indentSize
|
||||
})
|
||||
ConfigManager.set({
|
||||
@@ -413,8 +415,8 @@ class SnippetNoteDetail extends React.Component {
|
||||
}
|
||||
|
||||
handleIndentTypeItemClick (e, indentType) {
|
||||
let { config, dispatch } = this.props
|
||||
let editor = Object.assign({}, config.editor, {
|
||||
const { config, dispatch } = this.props
|
||||
const editor = Object.assign({}, config.editor, {
|
||||
indentType
|
||||
})
|
||||
ConfigManager.set({
|
||||
@@ -433,14 +435,14 @@ class SnippetNoteDetail extends React.Component {
|
||||
}
|
||||
|
||||
addSnippet () {
|
||||
let { note } = this.state
|
||||
const { note } = this.state
|
||||
|
||||
note.snippets = note.snippets.concat([{
|
||||
name: '',
|
||||
mode: 'Plain Text',
|
||||
content: ''
|
||||
}])
|
||||
let snippetIndex = note.snippets.length - 1
|
||||
const snippetIndex = note.snippets.length - 1
|
||||
|
||||
this.setState({
|
||||
note,
|
||||
@@ -486,19 +488,19 @@ class SnippetNoteDetail extends React.Component {
|
||||
}
|
||||
|
||||
render () {
|
||||
let { data, config, location } = this.props
|
||||
let { note } = this.state
|
||||
const { data, config, location } = this.props
|
||||
const { note } = this.state
|
||||
|
||||
let storageKey = note.storage
|
||||
let folderKey = note.folder
|
||||
const storageKey = note.storage
|
||||
const folderKey = note.folder
|
||||
|
||||
let editorFontSize = parseInt(config.editor.fontSize, 10)
|
||||
if (!(editorFontSize > 0 && editorFontSize < 101)) editorFontSize = 14
|
||||
let editorIndentSize = parseInt(config.editor.indentSize, 10)
|
||||
if (!(editorFontSize > 0 && editorFontSize < 132)) editorIndentSize = 4
|
||||
|
||||
let tabList = note.snippets.map((snippet, index) => {
|
||||
let isActive = this.state.snippetIndex === index
|
||||
const tabList = note.snippets.map((snippet, index) => {
|
||||
const isActive = this.state.snippetIndex === index
|
||||
|
||||
return <SnippetTab
|
||||
key={index}
|
||||
@@ -512,8 +514,8 @@ class SnippetNoteDetail extends React.Component {
|
||||
/>
|
||||
})
|
||||
|
||||
let viewList = note.snippets.map((snippet, index) => {
|
||||
let isActive = this.state.snippetIndex === index
|
||||
const viewList = note.snippets.map((snippet, index) => {
|
||||
const isActive = this.state.snippetIndex === index
|
||||
|
||||
let syntax = CodeMirror.findModeByName(pass(snippet.mode))
|
||||
if (syntax == null) syntax = CodeMirror.findModeByName('Plain Text')
|
||||
@@ -547,7 +549,7 @@ class SnippetNoteDetail extends React.Component {
|
||||
</div>
|
||||
})
|
||||
|
||||
let options = []
|
||||
const options = []
|
||||
data.storageMap.forEach((storage, index) => {
|
||||
storage.folders.forEach((folder) => {
|
||||
options.push({
|
||||
@@ -556,7 +558,7 @@ class SnippetNoteDetail extends React.Component {
|
||||
})
|
||||
})
|
||||
})
|
||||
let currentOption = options.filter((option) => option.storage.key === storageKey && option.folder.key === folderKey)[0]
|
||||
const currentOption = options.filter((option) => option.storage.key === storageKey && option.folder.key === folderKey)[0]
|
||||
|
||||
const trashTopBar = <div styleName='info'>
|
||||
<div styleName='info-left'>
|
||||
@@ -566,7 +568,7 @@ class SnippetNoteDetail extends React.Component {
|
||||
/>
|
||||
</div>
|
||||
<div styleName='info-right'>
|
||||
<TrashButton onClick={(e) => this.handleTrashButtonClick(e)} />
|
||||
<PermanentDeleteButton onClick={(e) => this.handleTrashButtonClick(e)} />
|
||||
<InfoButton
|
||||
onClick={(e) => this.handleInfoButtonClick(e)}
|
||||
/>
|
||||
@@ -583,10 +585,6 @@ class SnippetNoteDetail extends React.Component {
|
||||
|
||||
const detailTopBar = <div styleName='info'>
|
||||
<div styleName='info-left'>
|
||||
<StarButton styleName='info-left-button'
|
||||
onClick={(e) => this.handleStarButtonClick(e)}
|
||||
isActive={note.isStarred}
|
||||
/>
|
||||
<div styleName='info-left-top'>
|
||||
<FolderSelect styleName='info-left-top-folderSelect'
|
||||
value={this.state.note.storage + '-' + this.state.note.folder}
|
||||
@@ -603,15 +601,21 @@ class SnippetNoteDetail extends React.Component {
|
||||
/>
|
||||
</div>
|
||||
<div styleName='info-right'>
|
||||
<TrashButton onClick={(e) => this.handleTrashButtonClick(e)} />
|
||||
<button styleName='control-fullScreenButton'
|
||||
onMouseDown={(e) => this.handleFullScreenButton(e)}
|
||||
>
|
||||
<i className='fa fa-window-maximize' styleName='fullScreen-button' />
|
||||
</button>
|
||||
<InfoButton
|
||||
onClick={(e) => this.handleInfoButtonClick(e)}
|
||||
/>
|
||||
|
||||
<StarButton
|
||||
onClick={(e) => this.handleStarButtonClick(e)}
|
||||
isActive={note.isStarred}
|
||||
/>
|
||||
|
||||
<button styleName='control-fullScreenButton'
|
||||
onMouseDown={(e) => this.handleFullScreenButton(e)}>
|
||||
<img styleName='iconInfo' src='../resources/icon/icon-sidebar.svg' />
|
||||
</button>
|
||||
|
||||
<TrashButton onClick={(e) => this.handleTrashButtonClick(e)} />
|
||||
<InfoPanel
|
||||
storageName={currentOption.storage.name}
|
||||
folderName={currentOption.folder.name}
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
|
||||
.root
|
||||
absolute top bottom right
|
||||
border-width 0
|
||||
border-left 1px solid alpha(#DEDEDE, 60%)
|
||||
background-color $ui-noteDetail-backgroundColor
|
||||
box-shadow $note-detail-box-shadow
|
||||
box-shadow none
|
||||
|
||||
.body
|
||||
absolute left right
|
||||
left $note-detail-left-margin
|
||||
right $note-detail-right-margin
|
||||
left $snippet-note-detail-left-margin
|
||||
right $snippet-note-detail-right-margin
|
||||
top $info-height + $info-margin-under-border
|
||||
bottom $statusBar-height
|
||||
background-color $ui-noteDetail-backgroundColor
|
||||
@@ -43,7 +43,7 @@
|
||||
overflow hidden
|
||||
|
||||
.tabList .plusButton
|
||||
navButtonColor()
|
||||
navWhiteButtonColor()
|
||||
width 30px
|
||||
|
||||
.tabView
|
||||
@@ -55,24 +55,30 @@
|
||||
|
||||
.override
|
||||
absolute bottom left
|
||||
bottom 2px
|
||||
bottom 1px
|
||||
left 60px
|
||||
height 23px
|
||||
z-index 101
|
||||
button
|
||||
navButtonColor()
|
||||
height 24px
|
||||
padding 0 6px
|
||||
&:hover
|
||||
color $ui-active-color
|
||||
&:active .update-icon
|
||||
color white
|
||||
color $ui-active-color
|
||||
|
||||
.control-fullScreenButton
|
||||
float right
|
||||
padding 0 0 2px 0
|
||||
top 80px
|
||||
margin-bottom 10px
|
||||
topBarButtonLight()
|
||||
|
||||
body[data-theme="white"]
|
||||
.root
|
||||
box-shadow $note-detail-box-shadow
|
||||
border none
|
||||
|
||||
body[data-theme="dark"]
|
||||
.root
|
||||
border-color $ui-dark-borderColor
|
||||
border none
|
||||
background-color $ui-dark-noteDetail-backgroundColor
|
||||
box-shadow none
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React, { PropTypes } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
import CSSModules from 'browser/lib/CSSModules'
|
||||
import styles from './StarButton.styl'
|
||||
import _ from 'lodash'
|
||||
@@ -31,7 +32,7 @@ class StarButton extends React.Component {
|
||||
}
|
||||
|
||||
render () {
|
||||
let { className } = this.props
|
||||
const { className } = this.props
|
||||
|
||||
return (
|
||||
<button className={_.isString(className)
|
||||
@@ -47,10 +48,10 @@ class StarButton extends React.Component {
|
||||
onMouseLeave={(e) => this.handleMouseLeave(e)}
|
||||
onClick={this.props.onClick}
|
||||
>
|
||||
<i styleName='icon'
|
||||
className={this.state.isActive || this.props.isActive
|
||||
? 'fa fa-star'
|
||||
: 'fa fa-star-o'
|
||||
<img styleName='icon'
|
||||
src={this.state.isActive || this.props.isActive
|
||||
? '../resources/icon/icon-starred.svg'
|
||||
: '../resources/icon/icon-star.svg'
|
||||
}
|
||||
/>
|
||||
</button>
|
||||
|
||||
@@ -1,47 +1,24 @@
|
||||
.root
|
||||
left 7px
|
||||
top 0
|
||||
padding 0
|
||||
color alpha($ui-favorite-star-button-color, 60%)
|
||||
top 45px
|
||||
topBarButtonLight()
|
||||
&:hover
|
||||
transition 0.15s
|
||||
background-color alpha($ui-button--active-backgroundColor, 40%)
|
||||
color $ui-favorite-star-button-color
|
||||
&:active
|
||||
transition 0.15s
|
||||
background-color alpha($ui-button--active-backgroundColor, 40%)
|
||||
color $ui-favorite-star-button-color
|
||||
transition 0.2s
|
||||
color alpha($ui-favorite-star-button-color, 0.6)
|
||||
|
||||
.root--active
|
||||
@extend .root
|
||||
transition 0.15s
|
||||
color $ui-favorite-star-button-color
|
||||
&:hover
|
||||
transition 0.15s
|
||||
color $ui-favorite-star-button-color
|
||||
background-color alpha($ui-button--active-backgroundColor, 40%)
|
||||
&:active
|
||||
transition 0.15s
|
||||
color $ui-favorite-star-button-color
|
||||
background-color alpha($ui-button--active-backgroundColor, 40%)
|
||||
transition 0.2s
|
||||
color alpha($ui-favorite-star-button-color, 0.6)
|
||||
|
||||
.icon
|
||||
transition transform 0.15s
|
||||
|
||||
body[data-theme="dark"]
|
||||
.root
|
||||
topBarButtonDark()
|
||||
&:hover
|
||||
transition 0.15s
|
||||
background-color alpha($ui-dark-button--active-backgroundColor, 20%)
|
||||
color $ui-favorite-star-button-color
|
||||
&:active
|
||||
transition 0.15s
|
||||
background-color alpha($ui-dark-button--active-backgroundColor, 20%)
|
||||
color $ui-favorite-star-button-color
|
||||
|
||||
.root--active
|
||||
@extend .root
|
||||
color $ui-favorite-star-button-color
|
||||
&:hover
|
||||
transition 0.15s
|
||||
color $ui-favorite-star-button-color
|
||||
background-color alpha($ui-dark-button--active-backgroundColor, 20%)
|
||||
transition 0.2s
|
||||
color alpha($ui-favorite-star-button-color, 0.6)
|
||||
@@ -1,4 +1,5 @@
|
||||
import React, { PropTypes } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
import CSSModules from 'browser/lib/CSSModules'
|
||||
import styles from './TagSelect.styl'
|
||||
import _ from 'lodash'
|
||||
@@ -59,7 +60,7 @@ class TagSelect extends React.Component {
|
||||
submitTag () {
|
||||
AwsMobileAnalyticsConfig.recordDynamicCustomEvent('ADD_TAG')
|
||||
let { value } = this.props
|
||||
let newTag = this.refs.newTag.value.trim().replace(/ +/g, '_')
|
||||
const newTag = this.refs.newTag.value.trim().replace(/ +/g, '_')
|
||||
|
||||
if (newTag.length <= 0) {
|
||||
this.setState({
|
||||
@@ -101,9 +102,9 @@ class TagSelect extends React.Component {
|
||||
}
|
||||
|
||||
render () {
|
||||
let { value, className } = this.props
|
||||
const { value, className } = this.props
|
||||
|
||||
let tagList = _.isArray(value)
|
||||
const tagList = _.isArray(value)
|
||||
? value.map((tag) => {
|
||||
return (
|
||||
<span styleName='tag'
|
||||
@@ -113,7 +114,7 @@ class TagSelect extends React.Component {
|
||||
<button styleName='tag-removeButton'
|
||||
onClick={(e) => this.handleTagRemoveButtonClick(tag)(e)}
|
||||
>
|
||||
<i className='fa fa-times fa-fw tag-removeButton-icon' />
|
||||
<img className='tag-removeButton-icon' src='../resources/icon/icon-x.svg' width='8px' />
|
||||
</button>
|
||||
</span>
|
||||
)
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
.root
|
||||
display inline-block
|
||||
display flex
|
||||
align-items center
|
||||
user-select none
|
||||
height 23px
|
||||
vertical-align middle
|
||||
width 300px
|
||||
width 100%
|
||||
overflow-x scroll
|
||||
white-space nowrap
|
||||
|
||||
@@ -11,51 +11,42 @@
|
||||
display none
|
||||
|
||||
.tag
|
||||
display inline-block
|
||||
margin 1px 3px
|
||||
padding 0
|
||||
height 20px
|
||||
background-color alpha($ui-tag-backgroundColor, 10%)
|
||||
border-radius 3px
|
||||
overflow hidden
|
||||
display flex
|
||||
align-items center
|
||||
margin 0px 2px
|
||||
padding 2px 4px
|
||||
background-color alpha($ui-tag-backgroundColor, 3%)
|
||||
border-radius 4px
|
||||
position relative
|
||||
clearfix()
|
||||
|
||||
.tag-removeButton
|
||||
float right
|
||||
height 20px
|
||||
width 18px
|
||||
margin 0
|
||||
padding 0
|
||||
border-style solid
|
||||
border-width 0
|
||||
border-radius 20px
|
||||
line-height 18px
|
||||
background-color transparent
|
||||
color $ui-button-color
|
||||
position absolute
|
||||
right 6px
|
||||
|
||||
.tag-removeButton-icon
|
||||
width 5px
|
||||
padding-right 4px
|
||||
|
||||
.tag-label
|
||||
font-size 11px
|
||||
font-weight 600
|
||||
color: alpha($ui-text-color, 80%)
|
||||
float left
|
||||
height 20px
|
||||
line-height 20px
|
||||
padding 0 6px
|
||||
font-size 13px
|
||||
color: $ui-text-color
|
||||
padding 4px 16px 4px 8px
|
||||
|
||||
.newTag
|
||||
display inline-block
|
||||
margin 2px 0 15px 2px
|
||||
vertical-align middle
|
||||
height 18px
|
||||
box-sizing border-box
|
||||
border none
|
||||
background-color transparent
|
||||
outline none
|
||||
padding 0 4px
|
||||
font-size 13px
|
||||
|
||||
body[data-theme="dark"]
|
||||
.tag
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React, { PropTypes } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
import CSSModules from 'browser/lib/CSSModules'
|
||||
import styles from './TrashButton.styl'
|
||||
|
||||
@@ -8,7 +9,7 @@ const TrashButton = ({
|
||||
<button styleName='control-trashButton'
|
||||
onClick={(e) => onClick(e)}
|
||||
>
|
||||
<i className='fa fa-trash trashButton' styleName='info-button' />
|
||||
<img styleName='iconInfo' src='../resources/icon/icon-trash.svg' />
|
||||
</button>
|
||||
)
|
||||
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
.control-trashButton
|
||||
float right
|
||||
top 120px
|
||||
margin-bottom 10px
|
||||
topBarButtonLight()
|
||||
|
||||
.control-trashButton--in-trash
|
||||
top 60px
|
||||
topBarButtonLight()
|
||||
|
||||
.trashButton
|
||||
padding 0px
|
||||
margin 15px 0
|
||||
|
||||
body[data-theme="dark"]
|
||||
.control-trashButton
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React, { PropTypes } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
import CSSModules from 'browser/lib/CSSModules'
|
||||
import styles from './Detail.styl'
|
||||
import _ from 'lodash'
|
||||
@@ -32,12 +33,12 @@ class Detail extends React.Component {
|
||||
}
|
||||
|
||||
render () {
|
||||
let { location, data, config } = this.props
|
||||
const { location, data, config } = this.props
|
||||
let note = null
|
||||
if (location.query.key != null) {
|
||||
let splitted = location.query.key.split('-')
|
||||
let storageKey = splitted.shift()
|
||||
let noteKey = splitted.shift()
|
||||
const splitted = location.query.key.split('-')
|
||||
const storageKey = splitted.shift()
|
||||
const noteKey = splitted.shift()
|
||||
|
||||
note = data.noteMap.get(storageKey + '-' + noteKey)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React, { PropTypes } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
import CSSModules from 'browser/lib/CSSModules'
|
||||
import styles from './Main.styl'
|
||||
import { connect } from 'react-redux'
|
||||
@@ -11,14 +12,9 @@ import _ from 'lodash'
|
||||
import ConfigManager from 'browser/main/lib/ConfigManager'
|
||||
import modal from 'browser/main/lib/modal'
|
||||
import InitModal from 'browser/main/modals/InitModal'
|
||||
import mixpanel from 'browser/main/lib/mixpanel'
|
||||
import mobileAnalytics from 'browser/main/lib/AwsMobileAnalyticsConfig'
|
||||
import eventEmitter from 'browser/main/lib/eventEmitter'
|
||||
|
||||
function focused () {
|
||||
mixpanel.track('MAIN_FOCUSED')
|
||||
}
|
||||
|
||||
class Main extends React.Component {
|
||||
|
||||
constructor (props) {
|
||||
@@ -28,7 +24,7 @@ class Main extends React.Component {
|
||||
mobileAnalytics.initAwsMobileAnalytics()
|
||||
}
|
||||
|
||||
let { config } = props
|
||||
const { config } = props
|
||||
|
||||
this.state = {
|
||||
isRightSliderFocused: false,
|
||||
@@ -44,7 +40,7 @@ class Main extends React.Component {
|
||||
}
|
||||
|
||||
getChildContext () {
|
||||
let { status, config } = this.props
|
||||
const { status, config } = this.props
|
||||
|
||||
return {
|
||||
status,
|
||||
@@ -53,10 +49,12 @@ class Main extends React.Component {
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
let { dispatch, config } = this.props
|
||||
const { dispatch, config } = this.props
|
||||
|
||||
if (config.ui.theme === 'dark') {
|
||||
document.body.setAttribute('data-theme', 'dark')
|
||||
} else if (config.ui.theme === 'white') {
|
||||
document.body.setAttribute('data-theme', 'white')
|
||||
} else {
|
||||
document.body.setAttribute('data-theme', 'default')
|
||||
}
|
||||
@@ -76,11 +74,9 @@ class Main extends React.Component {
|
||||
})
|
||||
|
||||
eventEmitter.on('editor:fullscreen', this.toggleFullScreen)
|
||||
window.addEventListener('focus', focused)
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
window.removeEventListener('focus', focused)
|
||||
eventEmitter.off('editor:fullscreen', this.toggleFullScreen)
|
||||
}
|
||||
|
||||
@@ -104,8 +100,8 @@ class Main extends React.Component {
|
||||
this.setState({
|
||||
isRightSliderFocused: false
|
||||
}, () => {
|
||||
let { dispatch } = this.props
|
||||
let newListWidth = this.state.listWidth
|
||||
const { dispatch } = this.props
|
||||
const newListWidth = this.state.listWidth
|
||||
// TODO: ConfigManager should dispatch itself.
|
||||
ConfigManager.set({listWidth: newListWidth})
|
||||
dispatch({
|
||||
@@ -120,8 +116,8 @@ class Main extends React.Component {
|
||||
this.setState({
|
||||
isLeftSliderFocused: false
|
||||
}, () => {
|
||||
let { dispatch } = this.props
|
||||
let navWidth = this.state.navWidth
|
||||
const { dispatch } = this.props
|
||||
const navWidth = this.state.navWidth
|
||||
// TODO: ConfigManager should dispatch itself.
|
||||
ConfigManager.set({ navWidth })
|
||||
dispatch({
|
||||
@@ -134,7 +130,7 @@ class Main extends React.Component {
|
||||
|
||||
handleMouseMove (e) {
|
||||
if (this.state.isRightSliderFocused) {
|
||||
let offset = this.refs.body.getBoundingClientRect().left
|
||||
const offset = this.refs.body.getBoundingClientRect().left
|
||||
let newListWidth = e.pageX - offset
|
||||
if (newListWidth < 10) {
|
||||
newListWidth = 10
|
||||
@@ -187,7 +183,7 @@ class Main extends React.Component {
|
||||
}
|
||||
|
||||
render () {
|
||||
let { config } = this.props
|
||||
const { config } = this.props
|
||||
|
||||
// the width of the navigation bar when it is folded/collapsed
|
||||
const foldedNavigationWidth = 44
|
||||
|
||||
@@ -44,6 +44,13 @@ $control-height = 34px
|
||||
opacity 0
|
||||
transition 0.1s
|
||||
|
||||
body[data-theme="white"]
|
||||
.root, .root--expanded
|
||||
background-color $ui-white-noteList-backgroundColor
|
||||
|
||||
.control-newNoteButton
|
||||
background-color $ui-white-noteList-backgroundColor
|
||||
|
||||
body[data-theme="dark"]
|
||||
.root, .root--expanded
|
||||
background-color $ui-dark-noteList-backgroundColor
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import React, { PropTypes } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
import CSSModules from 'browser/lib/CSSModules'
|
||||
import styles from './NewNoteButton.styl'
|
||||
import _ from 'lodash'
|
||||
import modal from 'browser/main/lib/modal'
|
||||
import NewNoteModal from 'browser/main/modals/NewNoteModal'
|
||||
import { hashHistory } from 'react-router'
|
||||
import eventEmitter from 'browser/main/lib/eventEmitter'
|
||||
import dataApi from 'browser/main/lib/dataApi'
|
||||
|
||||
const { remote } = require('electron')
|
||||
const { dialog } = remote
|
||||
@@ -34,7 +33,7 @@ class NewNoteButton extends React.Component {
|
||||
}
|
||||
|
||||
handleNewNoteButtonClick (e) {
|
||||
const { config, location, dispatch } = this.props
|
||||
const { location, dispatch } = this.props
|
||||
const { storage, folder } = this.resolveTargetFolder()
|
||||
|
||||
modal.open(NewNoteModal, {
|
||||
@@ -51,7 +50,7 @@ class NewNoteButton extends React.Component {
|
||||
|
||||
// Find first storage
|
||||
if (storage == null) {
|
||||
for (let kv of data.storageMap) {
|
||||
for (const kv of data.storageMap) {
|
||||
storage = kv[1]
|
||||
break
|
||||
}
|
||||
@@ -85,7 +84,7 @@ class NewNoteButton extends React.Component {
|
||||
<div styleName='control'>
|
||||
<button styleName='control-newNoteButton'
|
||||
onClick={(e) => this.handleNewNoteButtonClick(e)}>
|
||||
<i className='fa fa-pencil-square-o' />
|
||||
<img styleName='iconTag' src='../resources/icon/icon-newnote.svg' />
|
||||
<span styleName='control-newNoteButton-tooltip'>
|
||||
Make a Note {OSX ? '⌘' : '^'} + n
|
||||
</span>
|
||||
|
||||
@@ -21,14 +21,14 @@ $control-height = 30px
|
||||
|
||||
.control-sortBy-select
|
||||
appearance: none;
|
||||
margin-left 3px
|
||||
margin-left 5px
|
||||
color $ui-inactive-text-color
|
||||
padding 0
|
||||
border none
|
||||
background-color transparent
|
||||
outline none
|
||||
cursor pointer
|
||||
font-size 11px
|
||||
font-size 12px
|
||||
&:hover
|
||||
transition 0.2s
|
||||
color $ui-text-color
|
||||
@@ -59,6 +59,13 @@ $control-height = 30px
|
||||
top $control-height
|
||||
overflow auto
|
||||
|
||||
body[data-theme="white"]
|
||||
.root
|
||||
background-color $ui-white-noteList-backgroundColor
|
||||
|
||||
.control
|
||||
background-color $ui-white-noteList-backgroundColor
|
||||
|
||||
body[data-theme="dark"]
|
||||
.root
|
||||
border-color $ui-dark-borderColor
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
.root
|
||||
absolute top left bottom
|
||||
width $sideNav-width
|
||||
background-color #f9f9f9
|
||||
background-color #2E3235
|
||||
user-select none
|
||||
color $ui-text-color
|
||||
height: 100vh
|
||||
@@ -11,24 +11,25 @@
|
||||
.top
|
||||
padding-bottom 15px
|
||||
|
||||
.top-menu
|
||||
.top-menu-preference
|
||||
navButtonColor()
|
||||
position absolute
|
||||
top 22px
|
||||
right 5px
|
||||
height 23px
|
||||
right 10px
|
||||
width 2em
|
||||
background-color transparent
|
||||
&:hover
|
||||
color $ui-text-color
|
||||
color $ui-button-default--active-backgroundColor
|
||||
background-color transparent
|
||||
&:active, &:active:hover
|
||||
color $ui-text-color
|
||||
background-color alpha($ui-button--active-backgroundColor, 20%)
|
||||
color $ui-button-default--active-backgroundColor
|
||||
|
||||
.switch-buttons
|
||||
background-color transparent
|
||||
border 0
|
||||
height 25px
|
||||
margin 20px auto 0px 8px
|
||||
margin 24px auto 4px 14px
|
||||
display flex
|
||||
text-align center
|
||||
|
||||
.non-active-button
|
||||
color $ui-inactive-text-color
|
||||
@@ -36,13 +37,16 @@
|
||||
border 0
|
||||
background-color transparent
|
||||
transition 0.2s
|
||||
display flex
|
||||
text-align center
|
||||
margin-right 4px;
|
||||
&:hover
|
||||
color alpha(#0B99F1, 60%)
|
||||
color alpha(#239F86, 60%)
|
||||
|
||||
.active-button
|
||||
@extend .non-active-button
|
||||
color #0B99F1
|
||||
|
||||
color $ui-button-default--active-backgroundColor
|
||||
|
||||
.top-menu-label
|
||||
margin-left 5px
|
||||
overflow ellipsis
|
||||
@@ -57,7 +61,7 @@
|
||||
padding-left 15px
|
||||
padding-bottom 13px
|
||||
p
|
||||
color $ui-text-color
|
||||
color $ui-button-default-color
|
||||
|
||||
.tagList
|
||||
overflow-y auto
|
||||
@@ -66,6 +70,7 @@
|
||||
.root--folded
|
||||
height 100vh
|
||||
width $sideNav--folded-width
|
||||
background-color #2E3235
|
||||
.switch-buttons
|
||||
display none
|
||||
.top
|
||||
@@ -88,7 +93,6 @@
|
||||
opacity 0
|
||||
margin-left 0
|
||||
overflow hidden
|
||||
background-color $ui-tooltip-backgroundColor
|
||||
z-index 10
|
||||
color white
|
||||
line-height 30px
|
||||
@@ -96,6 +100,41 @@
|
||||
border-bottom-right-radius 2px
|
||||
pointer-events none
|
||||
font-size 13px
|
||||
.top-menu-preference
|
||||
position absolute
|
||||
left 11px
|
||||
|
||||
body[data-theme="white"]
|
||||
.root, .root--folded
|
||||
background-color #f9f9f9
|
||||
color $ui-text-color
|
||||
|
||||
.top-menu-preference
|
||||
navWhiteButtonColor()
|
||||
background-color transparent
|
||||
&:hover
|
||||
color #0B99F1
|
||||
background-color transparent
|
||||
&:active, &:active:hover
|
||||
color #0B99F1
|
||||
background-color transparent
|
||||
|
||||
.non-active-button
|
||||
color $ui-inactive-text-color
|
||||
&:hover
|
||||
color alpha(#0B99F1, 60%)
|
||||
|
||||
.tag-title
|
||||
p
|
||||
color $ui-text-color
|
||||
|
||||
.non-active-button
|
||||
&:hover
|
||||
color alpha(#0B99F1, 60%)
|
||||
|
||||
.active-button
|
||||
@extend .non-active-button
|
||||
color #0B99F1
|
||||
|
||||
body[data-theme="dark"]
|
||||
.root, .root--folded
|
||||
@@ -106,12 +145,15 @@ body[data-theme="dark"]
|
||||
.top
|
||||
border-color $ui-dark-borderColor
|
||||
|
||||
.top-menu
|
||||
.top-menu-preference
|
||||
navDarkButtonColor()
|
||||
background-color transparent
|
||||
&:active
|
||||
background-color alpha($ui-dark-button--active-backgroundColor, 20%)
|
||||
background-color transparent
|
||||
&:hover
|
||||
background-color alpha($ui-dark-button--active-backgroundColor, 20%)
|
||||
background-color transparent
|
||||
|
||||
.non-active-button
|
||||
color alpha($ui-dark-text-color, 60%)
|
||||
|
||||
@@ -5,27 +5,23 @@
|
||||
|
||||
.header
|
||||
position relative
|
||||
height 25px
|
||||
height 36px
|
||||
width 100%
|
||||
margin-bottom 5px
|
||||
transition 0.15s
|
||||
display flex
|
||||
align-items center
|
||||
|
||||
.header--active
|
||||
margin-bottom 5px
|
||||
background-color $ui-button--active-backgroundColor
|
||||
background-color alpha($ui-button-default--active-backgroundColor, 20%)
|
||||
transition color background-color 0.15s
|
||||
|
||||
.header--active
|
||||
display flex
|
||||
align-items center
|
||||
.header-toggleButton
|
||||
color $ui-text-color
|
||||
|
||||
.header--active
|
||||
.header-info
|
||||
color $ui-text-color
|
||||
|
||||
.header--active
|
||||
.header-addFolderButton
|
||||
color $ui-text-color
|
||||
color #1EC38B
|
||||
|
||||
.header-toggleButton
|
||||
navButtonColor()
|
||||
@@ -38,23 +34,31 @@
|
||||
border-radius 50%
|
||||
&:hover
|
||||
transition 0.2s
|
||||
background-color alpha($ui-button--active-backgroundColor, 40%)
|
||||
background-color alpha($ui-button-default--hover-backgroundColor, 40%)
|
||||
color $ui-text-color
|
||||
|
||||
.header-info
|
||||
navButtonColor()
|
||||
display block
|
||||
width 100%
|
||||
height 25px
|
||||
padding-left 23px
|
||||
padding-right 10px
|
||||
height 36px
|
||||
padding-left 25px
|
||||
padding-right 15px
|
||||
line-height 22px
|
||||
cursor pointer
|
||||
font-size 13px
|
||||
font-size 14px
|
||||
border none
|
||||
overflow ellipsis
|
||||
text-align left
|
||||
background-color alpha($ui-button--active-backgroundColor, 20%)
|
||||
font-weight 600;
|
||||
background-color transparent
|
||||
&:hover
|
||||
color #1EC38B
|
||||
background-color alpha($ui-button-default--active-backgroundColor, 20%)
|
||||
transition background-color 0.15s
|
||||
&:active, &:active:hover
|
||||
color #1EC38B
|
||||
background-color alpha($ui-button-default--active-backgroundColor, 20%)
|
||||
|
||||
.header-info-path
|
||||
font-size 10px
|
||||
@@ -63,17 +67,14 @@
|
||||
.header-addFolderButton
|
||||
navButtonColor()
|
||||
position absolute
|
||||
right 0
|
||||
right 7px
|
||||
width 25px
|
||||
height 25px
|
||||
padding 0
|
||||
border none
|
||||
margin-right 5px
|
||||
border-radius 50%
|
||||
&:hover
|
||||
transition 0.2s
|
||||
background-color alpha($ui-button--active-backgroundColor, 40%)
|
||||
color $ui-text-color
|
||||
|
||||
.root--folded
|
||||
@extend .root
|
||||
@@ -102,6 +103,33 @@
|
||||
font-size 10px
|
||||
margin 0 5px
|
||||
|
||||
body[data-theme="white"]
|
||||
.header--active
|
||||
background-color $ui-button--active-backgroundColor
|
||||
transition color background-color 0.15s
|
||||
.header-toggleButton
|
||||
color $ui-text-color
|
||||
.header-info
|
||||
color $ui-text-color
|
||||
.header-addFolderButton
|
||||
color $ui-text-color
|
||||
|
||||
.header-toggleButton
|
||||
navWhiteButtonColor()
|
||||
&:hover
|
||||
background-color alpha($ui-button--active-backgroundColor, 40%)
|
||||
color $ui-text-color
|
||||
|
||||
.header-info
|
||||
navWhiteButtonColor()
|
||||
background-color alpha($ui-button--active-backgroundColor, 20%)
|
||||
|
||||
.header-addFolderButton
|
||||
navWhiteButtonColor()
|
||||
&:hover
|
||||
background-color alpha($ui-button--active-backgroundColor, 40%)
|
||||
color $ui-text-color
|
||||
|
||||
body[data-theme="dark"]
|
||||
.header--active
|
||||
background-color $ui-dark-button--active-backgroundColor
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React, { PropTypes } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
import CSSModules from 'browser/lib/CSSModules'
|
||||
import styles from './SideNav.styl'
|
||||
import { openModal } from 'browser/main/lib/modal'
|
||||
@@ -9,25 +10,35 @@ import TagListItem from 'browser/components/TagListItem'
|
||||
import SideNavFilter from 'browser/components/SideNavFilter'
|
||||
import StorageList from 'browser/components/StorageList'
|
||||
import NavToggleButton from 'browser/components/NavToggleButton'
|
||||
import EventEmitter from 'browser/main/lib/eventEmitter'
|
||||
|
||||
class SideNav extends React.Component {
|
||||
// TODO: should not use electron stuff v0.7
|
||||
|
||||
componentDidMount () {
|
||||
EventEmitter.on('side:preferences', this.handleMenuButtonClick)
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
EventEmitter.off('side:preferences', this.handleMenuButtonClick)
|
||||
}
|
||||
|
||||
handleMenuButtonClick (e) {
|
||||
openModal(PreferencesModal)
|
||||
}
|
||||
|
||||
handleHomeButtonClick (e) {
|
||||
let { router } = this.context
|
||||
const { router } = this.context
|
||||
router.push('/home')
|
||||
}
|
||||
|
||||
handleStarredButtonClick (e) {
|
||||
let { router } = this.context
|
||||
const { router } = this.context
|
||||
router.push('/starred')
|
||||
}
|
||||
|
||||
handleToggleButtonClick (e) {
|
||||
let { dispatch, config } = this.props
|
||||
const { dispatch, config } = this.props
|
||||
|
||||
ConfigManager.set({isSideNavFolded: !config.isSideNavFolded})
|
||||
dispatch({
|
||||
@@ -37,7 +48,7 @@ class SideNav extends React.Component {
|
||||
}
|
||||
|
||||
handleTrashedButtonClick (e) {
|
||||
let { router } = this.context
|
||||
const { router } = this.context
|
||||
router.push('/trashed')
|
||||
}
|
||||
|
||||
@@ -52,7 +63,7 @@ class SideNav extends React.Component {
|
||||
}
|
||||
|
||||
SideNavComponent (isFolded, storageList) {
|
||||
let { location, data } = this.props
|
||||
const { location, data } = this.props
|
||||
|
||||
const isHomeActive = !!location.pathname.match(/^\/home$/)
|
||||
const isStarredActive = !!location.pathname.match(/^\/starred$/)
|
||||
@@ -72,6 +83,9 @@ class SideNav extends React.Component {
|
||||
isTrashedActive={isTrashedActive}
|
||||
handleStarredButtonClick={(e) => this.handleStarredButtonClick(e)}
|
||||
handleTrashedButtonClick={(e) => this.handleTrashedButtonClick(e)}
|
||||
counterTotalNote={data.noteMap._map.size}
|
||||
counterStarredNote={data.starredSet._set.size}
|
||||
counterDelNote={data.trashedSet._set.size}
|
||||
/>
|
||||
|
||||
<StorageList storageList={storageList} />
|
||||
@@ -96,7 +110,7 @@ class SideNav extends React.Component {
|
||||
|
||||
tagListComponent () {
|
||||
const { data, location } = this.props
|
||||
let tagList = data.tagNoteMap.map((tag, key) => {
|
||||
const tagList = data.tagNoteMap.map((tag, key) => {
|
||||
return key
|
||||
})
|
||||
return (
|
||||
@@ -123,11 +137,11 @@ class SideNav extends React.Component {
|
||||
}
|
||||
|
||||
render () {
|
||||
let { data, location, config, dispatch } = this.props
|
||||
const { data, location, config, dispatch } = this.props
|
||||
|
||||
let isFolded = config.isSideNavFolded
|
||||
const isFolded = config.isSideNavFolded
|
||||
|
||||
let storageList = data.storageMap.map((storage, key) => {
|
||||
const storageList = data.storageMap.map((storage, key) => {
|
||||
return <StorageItem
|
||||
key={storage.key}
|
||||
storage={storage}
|
||||
@@ -137,7 +151,7 @@ class SideNav extends React.Component {
|
||||
dispatch={dispatch}
|
||||
/>
|
||||
})
|
||||
let style = {}
|
||||
const style = {}
|
||||
if (!isFolded) style.width = this.props.width
|
||||
const isTagActive = location.pathname.match(/tag/)
|
||||
return (
|
||||
@@ -148,15 +162,26 @@ class SideNav extends React.Component {
|
||||
>
|
||||
<div styleName='top'>
|
||||
<div styleName='switch-buttons'>
|
||||
<button styleName={isTagActive ? 'non-active-button' : 'active-button'} onClick={this.handleSwitchFoldersButtonClick.bind(this)}><i className='fa fa-folder fa-fw' /></button>
|
||||
<button styleName={isTagActive ? 'active-button' : 'non-active-button'} onClick={this.handleSwitchTagsButtonClick.bind(this)}><i className='fa fa-tags fa-fw' /></button>
|
||||
<button styleName={isTagActive ? 'non-active-button' : 'active-button'} onClick={this.handleSwitchFoldersButtonClick.bind(this)}>
|
||||
<img src={isTagActive
|
||||
? '../resources/icon/icon-list.svg'
|
||||
: '../resources/icon/icon-list-active.svg'
|
||||
}
|
||||
/>
|
||||
</button>
|
||||
<button styleName={isTagActive ? 'active-button' : 'non-active-button'} onClick={this.handleSwitchTagsButtonClick.bind(this)}>
|
||||
<img src={isTagActive
|
||||
? '../resources/icon/icon-tag-active.svg'
|
||||
: '../resources/icon/icon-tag.svg'
|
||||
}
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<button styleName='top-menu'
|
||||
<button styleName='top-menu-preference'
|
||||
onClick={(e) => this.handleMenuButtonClick(e)}
|
||||
>
|
||||
<i className='fa fa-wrench fa-fw' />
|
||||
<span styleName='top-menu-label'>Preferences</span>
|
||||
<img styleName='iconTag' src='../resources/icon/icon-setting.svg' />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
@import('../Detail/DetailVars')
|
||||
|
||||
.root
|
||||
absolute bottom left right
|
||||
position absolute
|
||||
bottom 10px
|
||||
right 10px
|
||||
z-index 100
|
||||
background-color $ui-noteDetail-backgroundColor
|
||||
display flex
|
||||
|
||||
.blank
|
||||
@@ -21,8 +22,19 @@
|
||||
|
||||
.zoom
|
||||
navButtonColor()
|
||||
height 24px
|
||||
|
||||
color rgba(0,0,0,.54)
|
||||
height 20px
|
||||
display flex
|
||||
padding 0
|
||||
align-items center
|
||||
background-color transparent
|
||||
&:hover
|
||||
color $ui-active-color
|
||||
&:active
|
||||
color $ui-active-color
|
||||
span
|
||||
margin-left 5px
|
||||
|
||||
.update
|
||||
navButtonColor()
|
||||
height 24px
|
||||
@@ -37,14 +49,14 @@
|
||||
|
||||
body[data-theme="dark"]
|
||||
.root
|
||||
background-color $ui-dark-noteDetail-backgroundColor
|
||||
border-color $ui-dark-borderColor
|
||||
box-shadow none
|
||||
|
||||
.zoom
|
||||
border-color $ui-dark-borderColor
|
||||
background-color transparent
|
||||
color #f9f9f9
|
||||
&:hover
|
||||
background-color alpha($ui-dark-button--active-backgroundColor, 20%)
|
||||
transition 0.15s
|
||||
color $ui-dark-text-color
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React, { PropTypes } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
import CSSModules from 'browser/lib/CSSModules'
|
||||
import styles from './StatusBar.styl'
|
||||
import ZoomManager from 'browser/main/lib/ZoomManager'
|
||||
@@ -11,7 +12,7 @@ const zoomOptions = [0.8, 0.9, 1, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2
|
||||
|
||||
class StatusBar extends React.Component {
|
||||
updateApp () {
|
||||
let index = dialog.showMessageBox(remote.getCurrentWindow(), {
|
||||
const index = dialog.showMessageBox(remote.getCurrentWindow(), {
|
||||
type: 'warning',
|
||||
message: 'Update Boostnote',
|
||||
detail: 'New Boostnote is ready to be installed.',
|
||||
@@ -24,7 +25,7 @@ class StatusBar extends React.Component {
|
||||
}
|
||||
|
||||
handleZoomButtonClick (e) {
|
||||
let menu = new Menu()
|
||||
const menu = new Menu()
|
||||
|
||||
zoomOptions.forEach((zoom) => {
|
||||
menu.append(new MenuItem({
|
||||
@@ -37,7 +38,7 @@ class StatusBar extends React.Component {
|
||||
}
|
||||
|
||||
handleZoomMenuItemClick (zoomFactor) {
|
||||
let { dispatch } = this.props
|
||||
const { dispatch } = this.props
|
||||
ZoomManager.setZoom(zoomFactor)
|
||||
dispatch({
|
||||
type: 'SET_ZOOM',
|
||||
@@ -46,7 +47,7 @@ class StatusBar extends React.Component {
|
||||
}
|
||||
|
||||
render () {
|
||||
let { config, status } = this.context
|
||||
const { config, status } = this.context
|
||||
|
||||
return (
|
||||
<div className='StatusBar'
|
||||
@@ -55,8 +56,8 @@ class StatusBar extends React.Component {
|
||||
<button styleName='zoom'
|
||||
onClick={(e) => this.handleZoomButtonClick(e)}
|
||||
>
|
||||
<i className='fa fa-search-plus' />
|
||||
{Math.floor(config.zoom * 100)}%
|
||||
<img src='../resources/icon/icon-zoom.svg' />
|
||||
<span>{Math.floor(config.zoom * 100)}%</span>
|
||||
</button>
|
||||
|
||||
{status.updateReady
|
||||
|
||||
@@ -36,7 +36,7 @@ $control-height = 34px
|
||||
outline none
|
||||
border none
|
||||
color $ui-text-color
|
||||
font-size 16px
|
||||
font-size 18px
|
||||
padding-bottom 2px
|
||||
background-color $ui-noteList-backgroundColor
|
||||
|
||||
@@ -112,6 +112,21 @@ $control-height = 34px
|
||||
opacity 0
|
||||
transition 0.1s
|
||||
|
||||
body[data-theme="white"]
|
||||
.root, .root--expanded
|
||||
background-color $ui-white-noteList-backgroundColor
|
||||
|
||||
.control
|
||||
border-color $ui-dark-borderColor
|
||||
|
||||
.control-search
|
||||
background-color $ui-white-noteList-backgroundColor
|
||||
|
||||
.control-search-input
|
||||
background-color $ui-white-noteList-backgroundColor
|
||||
input
|
||||
background-color $ui-white-noteList-backgroundColor
|
||||
|
||||
body[data-theme="dark"]
|
||||
.root, .root--expanded
|
||||
background-color $ui-dark-noteList-backgroundColor
|
||||
|
||||
@@ -1,16 +1,11 @@
|
||||
import React, { PropTypes } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
import CSSModules from 'browser/lib/CSSModules'
|
||||
import styles from './TopBar.styl'
|
||||
import _ from 'lodash'
|
||||
import NewNoteModal from 'browser/main/modals/NewNoteModal'
|
||||
import ee from 'browser/main/lib/eventEmitter'
|
||||
import NewNoteButton from 'browser/main/NewNoteButton'
|
||||
|
||||
const { remote } = require('electron')
|
||||
const { dialog } = remote
|
||||
|
||||
const OSX = window.process.platform === 'darwin'
|
||||
|
||||
class TopBar extends React.Component {
|
||||
constructor (props) {
|
||||
super(props)
|
||||
@@ -121,7 +116,7 @@ class TopBar extends React.Component {
|
||||
}
|
||||
|
||||
render () {
|
||||
let { config, style, data, location } = this.props
|
||||
const { config, style, location } = this.props
|
||||
return (
|
||||
<div className='TopBar'
|
||||
styleName={config.isSideNavFolded ? 'root--expanded' : 'root'}
|
||||
|
||||
@@ -12,6 +12,7 @@ body
|
||||
color textColor
|
||||
font-size fontSize
|
||||
font-weight 200
|
||||
-webkit-font-smoothing antialiased
|
||||
|
||||
button, input, select, textarea
|
||||
font-family DEFAULT_FONTS
|
||||
|
||||
@@ -36,7 +36,7 @@ document.addEventListener('click', function (e) {
|
||||
if (infoPanel) infoPanel.style.display = 'none'
|
||||
})
|
||||
|
||||
let el = document.getElementById('content')
|
||||
const el = document.getElementById('content')
|
||||
const history = syncHistoryWithStore(hashHistory, store)
|
||||
|
||||
function notify (...args) {
|
||||
@@ -44,7 +44,7 @@ function notify (...args) {
|
||||
}
|
||||
|
||||
function updateApp () {
|
||||
let index = dialog.showMessageBox(remote.getCurrentWindow(), {
|
||||
const index = dialog.showMessageBox(remote.getCurrentWindow(), {
|
||||
type: 'warning',
|
||||
message: 'Update Boostnote',
|
||||
detail: 'New Boostnote is ready to be installed.',
|
||||
@@ -81,7 +81,7 @@ ReactDOM.render((
|
||||
</Router>
|
||||
</Provider>
|
||||
), el, function () {
|
||||
let loadingCover = document.getElementById('loadingCover')
|
||||
const loadingCover = document.getElementById('loadingCover')
|
||||
loadingCover.parentNode.removeChild(loadingCover)
|
||||
|
||||
ipcRenderer.on('update-ready', function () {
|
||||
|
||||
@@ -4,6 +4,7 @@ const ConfigManager = require('browser/main/lib/ConfigManager')
|
||||
|
||||
const remote = require('electron').remote
|
||||
const os = require('os')
|
||||
let mobileAnalyticsClient
|
||||
|
||||
AWS.config.region = 'us-east-1'
|
||||
if (process.env.NODE_ENV === 'production' && ConfigManager.default.get().amaEnabled) {
|
||||
@@ -13,7 +14,7 @@ if (process.env.NODE_ENV === 'production' && ConfigManager.default.get().amaEnab
|
||||
|
||||
const validPlatformName = convertPlatformName(os.platform())
|
||||
|
||||
const mobileAnalyticsClient = new AMA.Manager({
|
||||
mobileAnalyticsClient = new AMA.Manager({
|
||||
appId: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
|
||||
appTitle: 'xxxxxxxxxx',
|
||||
appVersionName: remote.app.getVersion().toString(),
|
||||
|
||||
@@ -13,10 +13,10 @@ function release (el) {
|
||||
|
||||
function fire (command) {
|
||||
console.info('COMMAND >>', command)
|
||||
let splitted = command.split(':')
|
||||
let target = splitted[0]
|
||||
let targetCommand = splitted[1]
|
||||
let targetCallees = callees
|
||||
const splitted = command.split(':')
|
||||
const target = splitted[0]
|
||||
const targetCommand = splitted[1]
|
||||
const targetCallees = callees
|
||||
.filter((callee) => callee.name === target)
|
||||
|
||||
targetCallees.forEach((callee) => {
|
||||
|
||||
@@ -6,8 +6,6 @@ const win = global.process.platform === 'win32'
|
||||
const electron = require('electron')
|
||||
const { ipcRenderer } = electron
|
||||
const consts = require('browser/lib/consts')
|
||||
const path = require('path')
|
||||
const fs = require('fs')
|
||||
|
||||
let isInitialized = false
|
||||
|
||||
@@ -91,7 +89,11 @@ function get () {
|
||||
: 'default'
|
||||
|
||||
if (config.editor.theme !== 'default') {
|
||||
editorTheme.setAttribute('href', '../node_modules/codemirror/theme/' + config.editor.theme + '.css')
|
||||
if (config.editor.theme.startsWith('solarized')) {
|
||||
editorTheme.setAttribute('href', '../node_modules/codemirror/theme/solarized.css')
|
||||
} else {
|
||||
editorTheme.setAttribute('href', '../node_modules/codemirror/theme/' + config.editor.theme + '.css')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,13 +101,15 @@ function get () {
|
||||
}
|
||||
|
||||
function set (updates) {
|
||||
let currentConfig = get()
|
||||
let newConfig = Object.assign({}, DEFAULT_CONFIG, currentConfig, updates)
|
||||
const currentConfig = get()
|
||||
const newConfig = Object.assign({}, DEFAULT_CONFIG, currentConfig, updates)
|
||||
if (!validate(newConfig)) throw new Error('INVALID CONFIG')
|
||||
_save(newConfig)
|
||||
|
||||
if (newConfig.ui.theme === 'dark') {
|
||||
document.body.setAttribute('data-theme', 'dark')
|
||||
} else if (newConfig.ui.theme === 'white') {
|
||||
document.body.setAttribute('data-theme', 'white')
|
||||
} else {
|
||||
document.body.setAttribute('data-theme', 'default')
|
||||
}
|
||||
@@ -117,12 +121,16 @@ function set (updates) {
|
||||
editorTheme.setAttribute('rel', 'stylesheet')
|
||||
document.head.appendChild(editorTheme)
|
||||
}
|
||||
let newTheme = consts.THEMES.some((theme) => theme === newConfig.editor.theme)
|
||||
const newTheme = consts.THEMES.some((theme) => theme === newConfig.editor.theme)
|
||||
? newConfig.editor.theme
|
||||
: 'default'
|
||||
|
||||
if (newTheme !== 'default') {
|
||||
editorTheme.setAttribute('href', '../node_modules/codemirror/theme/' + newTheme + '.css')
|
||||
if (newTheme.startsWith('solarized')) {
|
||||
editorTheme.setAttribute('href', '../node_modules/codemirror/theme/solarized.css')
|
||||
} else {
|
||||
editorTheme.setAttribute('href', '../node_modules/codemirror/theme/' + newTheme + '.css')
|
||||
}
|
||||
}
|
||||
|
||||
ipcRenderer.send('config-renew', {
|
||||
@@ -131,7 +139,7 @@ function set (updates) {
|
||||
}
|
||||
|
||||
function assignConfigValues (originalConfig, rcConfig) {
|
||||
let config = Object.assign({}, DEFAULT_CONFIG, originalConfig, rcConfig)
|
||||
const config = Object.assign({}, DEFAULT_CONFIG, originalConfig, rcConfig)
|
||||
config.hotkey = Object.assign({}, DEFAULT_CONFIG.hotkey, originalConfig.hotkey, rcConfig.hotkey)
|
||||
config.ui = Object.assign({}, DEFAULT_CONFIG.ui, originalConfig.ui, rcConfig.ui)
|
||||
config.editor = Object.assign({}, DEFAULT_CONFIG.editor, originalConfig.editor, rcConfig.editor)
|
||||
|
||||
@@ -19,7 +19,7 @@ function setZoom (zoomFactor, noSave = false) {
|
||||
}
|
||||
|
||||
function getZoom () {
|
||||
let config = ConfigManager.get()
|
||||
const config = ConfigManager.get()
|
||||
|
||||
return config.zoom
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const _ = require('lodash')
|
||||
const sander = require('sander')
|
||||
const { findStorage } = require('browser/lib/findStorage')
|
||||
|
||||
/**
|
||||
|
||||
@@ -23,7 +23,6 @@ const { findStorage } = require('browser/lib/findStorage')
|
||||
* ```
|
||||
*/
|
||||
function createFolder (storageKey, input) {
|
||||
let rawStorages
|
||||
let targetStorage
|
||||
try {
|
||||
if (input == null) throw new Error('No input found.')
|
||||
@@ -41,7 +40,7 @@ function createFolder (storageKey, input) {
|
||||
while (storage.folders.some((folder) => folder.key === key)) {
|
||||
key = keygen()
|
||||
}
|
||||
let newFolder = {
|
||||
const newFolder = {
|
||||
key,
|
||||
color: input.color,
|
||||
name: input.name
|
||||
|
||||
@@ -66,7 +66,7 @@ function createNote (storageKey, input) {
|
||||
}
|
||||
}
|
||||
}
|
||||
let noteData = Object.assign({}, input, {
|
||||
const noteData = Object.assign({}, input, {
|
||||
key,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
|
||||
@@ -19,7 +19,6 @@ const { findStorage } = require('browser/lib/findStorage')
|
||||
* ```
|
||||
*/
|
||||
function deleteFolder (storageKey, folderKey) {
|
||||
let rawStorages
|
||||
let targetStorage
|
||||
try {
|
||||
targetStorage = findStorage(storageKey)
|
||||
@@ -38,17 +37,17 @@ function deleteFolder (storageKey, folderKey) {
|
||||
})
|
||||
})
|
||||
.then(function deleteFolderAndNotes (data) {
|
||||
let { storage, notes } = data
|
||||
const { storage, notes } = data
|
||||
storage.folders = storage.folders
|
||||
.filter(function excludeTargetFolder (folder) {
|
||||
return folder.key !== folderKey
|
||||
})
|
||||
|
||||
let targetNotes = notes.filter(function filterTargetNotes (note) {
|
||||
const targetNotes = notes.filter(function filterTargetNotes (note) {
|
||||
return note.folder === folderKey
|
||||
})
|
||||
|
||||
let deleteAllNotes = targetNotes
|
||||
const deleteAllNotes = targetNotes
|
||||
.map(function deleteNote (note) {
|
||||
const notePath = path.join(storage.path, 'notes', note.key + '.cson')
|
||||
return sander.unlink(notePath)
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
const resolveStorageData = require('./resolveStorageData')
|
||||
const _ = require('lodash')
|
||||
const path = require('path')
|
||||
const sander = require('sander')
|
||||
const { findStorage } = require('browser/lib/findStorage')
|
||||
@@ -14,7 +13,7 @@ function deleteNote (storageKey, noteKey) {
|
||||
|
||||
return resolveStorageData(targetStorage)
|
||||
.then(function deleteNoteFile (storage) {
|
||||
let notePath = path.join(storage.path, 'notes', noteKey + '.cson')
|
||||
const notePath = path.join(storage.path, 'notes', noteKey + '.cson')
|
||||
|
||||
try {
|
||||
sander.unlinkSync(notePath)
|
||||
|
||||
@@ -20,7 +20,7 @@ const CSON = require('@rokt33r/season')
|
||||
* 3. empty directory
|
||||
*/
|
||||
function init () {
|
||||
let fetchStorages = function () {
|
||||
const fetchStorages = function () {
|
||||
let rawStorages
|
||||
try {
|
||||
rawStorages = JSON.parse(window.localStorage.getItem('storages'))
|
||||
@@ -34,8 +34,8 @@ function init () {
|
||||
.map(resolveStorageData))
|
||||
}
|
||||
|
||||
let fetchNotes = function (storages) {
|
||||
let findNotesFromEachStorage = storages
|
||||
const fetchNotes = function (storages) {
|
||||
const findNotesFromEachStorage = storages
|
||||
.map((storage) => {
|
||||
return resolveStorageNotes(storage)
|
||||
.then((notes) => {
|
||||
|
||||
@@ -9,7 +9,7 @@ const sander = require('sander')
|
||||
function migrateFromV5Storage (storageKey, data) {
|
||||
let targetStorage
|
||||
try {
|
||||
let cachedStorageList = JSON.parse(localStorage.getItem('storages'))
|
||||
const cachedStorageList = JSON.parse(localStorage.getItem('storages'))
|
||||
if (!_.isArray(cachedStorageList)) throw new Error('Target storage doesn\'t exist.')
|
||||
|
||||
targetStorage = _.find(cachedStorageList, {key: storageKey})
|
||||
@@ -24,15 +24,15 @@ function migrateFromV5Storage (storageKey, data) {
|
||||
}
|
||||
|
||||
function importAll (storage, data) {
|
||||
let oldArticles = data.articles
|
||||
let notes = []
|
||||
const oldArticles = data.articles
|
||||
const notes = []
|
||||
data.folders
|
||||
.forEach(function (oldFolder) {
|
||||
let folderKey = keygen()
|
||||
while (storage.folders.some((folder) => folder.key === folderKey)) {
|
||||
folderKey = keygen()
|
||||
}
|
||||
let newFolder = {
|
||||
const newFolder = {
|
||||
key: folderKey,
|
||||
name: oldFolder.name,
|
||||
color: consts.FOLDER_COLORS[Math.floor(Math.random() * 7) % 7]
|
||||
@@ -40,7 +40,7 @@ function importAll (storage, data) {
|
||||
|
||||
storage.folders.push(newFolder)
|
||||
|
||||
let articles = oldArticles.filter((article) => article.FolderKey === oldFolder.key)
|
||||
const articles = oldArticles.filter((article) => article.FolderKey === oldFolder.key)
|
||||
articles.forEach((article) => {
|
||||
let noteKey = keygen()
|
||||
let isUnique = false
|
||||
@@ -59,7 +59,7 @@ function importAll (storage, data) {
|
||||
}
|
||||
|
||||
if (article.mode === 'markdown') {
|
||||
let newNote = {
|
||||
const newNote = {
|
||||
tags: article.tags,
|
||||
createdAt: article.createdAt,
|
||||
updatedAt: article.updatedAt,
|
||||
@@ -73,7 +73,7 @@ function importAll (storage, data) {
|
||||
}
|
||||
notes.push(newNote)
|
||||
} else {
|
||||
let newNote = {
|
||||
const newNote = {
|
||||
tags: article.tags,
|
||||
createdAt: article.createdAt,
|
||||
updatedAt: article.updatedAt,
|
||||
|
||||
@@ -20,7 +20,7 @@ function moveNote (storageKey, noteKey, newStorageKey, newFolderKey) {
|
||||
.then(function saveNote (_oldStorage) {
|
||||
oldStorage = _oldStorage
|
||||
let noteData
|
||||
let notePath = path.join(oldStorage.path, 'notes', noteKey + '.cson')
|
||||
const notePath = path.join(oldStorage.path, 'notes', noteKey + '.cson')
|
||||
try {
|
||||
noteData = CSON.readFileSync(notePath)
|
||||
} catch (err) {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
const _ = require('lodash')
|
||||
const resolveStorageData = require('./resolveStorageData')
|
||||
const { findStorage } = require('browser/lib/findStorage')
|
||||
|
||||
/**
|
||||
* @param {String} key
|
||||
@@ -19,7 +18,7 @@ function renameStorage (key, name) {
|
||||
console.error(err)
|
||||
return Promise.reject(err)
|
||||
}
|
||||
let targetStorage = _.find(cachedStorageList, {key: key})
|
||||
const targetStorage = _.find(cachedStorageList, {key: key})
|
||||
if (targetStorage == null) return Promise.reject('Storage')
|
||||
|
||||
targetStorage.name = name
|
||||
|
||||
@@ -18,7 +18,6 @@ const { findStorage } = require('browser/lib/findStorage')
|
||||
* ```
|
||||
*/
|
||||
function reorderFolder (storageKey, oldIndex, newIndex) {
|
||||
let rawStorages
|
||||
let targetStorage
|
||||
try {
|
||||
if (!_.isNumber(oldIndex)) throw new Error('oldIndex must be a number.')
|
||||
|
||||
@@ -4,7 +4,7 @@ const CSON = require('@rokt33r/season')
|
||||
const migrateFromV6Storage = require('./migrateFromV6Storage')
|
||||
|
||||
function resolveStorageData (storageCache) {
|
||||
let storage = {
|
||||
const storage = {
|
||||
key: storageCache.key,
|
||||
name: storageCache.name,
|
||||
type: storageCache.type,
|
||||
@@ -13,7 +13,7 @@ function resolveStorageData (storageCache) {
|
||||
|
||||
const boostnoteJSONPath = path.join(storageCache.path, 'boostnote.json')
|
||||
try {
|
||||
let jsonData = CSON.readFileSync(boostnoteJSONPath)
|
||||
const jsonData = CSON.readFileSync(boostnoteJSONPath)
|
||||
if (!_.isArray(jsonData.folders)) throw new Error('folders should be an array.')
|
||||
storage.folders = jsonData.folders
|
||||
storage.version = jsonData.version
|
||||
@@ -28,7 +28,7 @@ function resolveStorageData (storageCache) {
|
||||
storage.version = '1.0'
|
||||
}
|
||||
|
||||
let version = parseInt(storage.version, 10)
|
||||
const version = parseInt(storage.version, 10)
|
||||
if (version >= 1) {
|
||||
if (version > 1) {
|
||||
console.log('The repository version is newer than one of current app.')
|
||||
|
||||
@@ -16,13 +16,13 @@ function resolveStorageNotes (storage) {
|
||||
}
|
||||
notePathList = []
|
||||
}
|
||||
let notes = notePathList
|
||||
const notes = notePathList
|
||||
.filter(function filterOnlyCSONFile (notePath) {
|
||||
return /\.cson$/.test(notePath)
|
||||
})
|
||||
.map(function parseCSONFile (notePath) {
|
||||
try {
|
||||
let data = CSON.readFileSync(path.join(notesDirPath, notePath))
|
||||
const data = CSON.readFileSync(path.join(notesDirPath, notePath))
|
||||
data.key = path.basename(notePath, '.cson')
|
||||
data.storage = storage.key
|
||||
return data
|
||||
|
||||
@@ -23,7 +23,6 @@ const { findStorage } = require('browser/lib/findStorage')
|
||||
* ```
|
||||
*/
|
||||
function updateFolder (storageKey, folderKey, input) {
|
||||
let rawStorages
|
||||
let targetStorage
|
||||
try {
|
||||
if (input == null) throw new Error('No input found.')
|
||||
@@ -37,7 +36,7 @@ function updateFolder (storageKey, folderKey, input) {
|
||||
|
||||
return resolveStorageData(targetStorage)
|
||||
.then(function updateFolder (storage) {
|
||||
let targetFolder = _.find(storage.folders, {key: folderKey})
|
||||
const targetFolder = _.find(storage.folders, {key: folderKey})
|
||||
if (targetFolder == null) throw new Error('Target folder doesn\'t exist.')
|
||||
targetFolder.name = input.name
|
||||
targetFolder.color = input.color
|
||||
|
||||
@@ -5,7 +5,7 @@ const CSON = require('@rokt33r/season')
|
||||
const { findStorage } = require('browser/lib/findStorage')
|
||||
|
||||
function validateInput (input) {
|
||||
let validatedInput = {}
|
||||
const validatedInput = {}
|
||||
|
||||
if (input.tags != null) {
|
||||
if (!_.isArray(input.tags)) validatedInput.tags = []
|
||||
@@ -81,7 +81,7 @@ function updateNote (storageKey, noteKey, input) {
|
||||
return resolveStorageData(targetStorage)
|
||||
.then(function saveNote (storage) {
|
||||
let noteData
|
||||
let notePath = path.join(storage.path, 'notes', noteKey + '.cson')
|
||||
const notePath = path.join(storage.path, 'notes', noteKey + '.cson')
|
||||
try {
|
||||
noteData = CSON.readFileSync(notePath)
|
||||
} catch (err) {
|
||||
|
||||
@@ -1,121 +0,0 @@
|
||||
import store from 'browser/main/store'
|
||||
|
||||
const _ = require('lodash')
|
||||
const keygen = require('browser/lib/keygen')
|
||||
const Mixpanel = require('mixpanel')
|
||||
const mixpanel = Mixpanel.init('7a0aca437d72dfd07cbcbf58d3b61f27', {key: 'fde4fd23f4d550f1b646bcd7d4374b1f'})
|
||||
const moment = require('moment')
|
||||
const electron = require('electron')
|
||||
|
||||
function _getClientKey () {
|
||||
let clientKey = localStorage.getItem('clientKey')
|
||||
if (!_.isString(clientKey) || clientKey.length !== 40) {
|
||||
clientKey = keygen(20)
|
||||
_setClientKey(clientKey)
|
||||
}
|
||||
|
||||
return clientKey
|
||||
}
|
||||
|
||||
function _setClientKey (newKey) {
|
||||
localStorage.setItem('clientKey', newKey)
|
||||
}
|
||||
|
||||
function _fetch () {
|
||||
let events
|
||||
try {
|
||||
events = JSON.parse(localStorage.getItem('events'))
|
||||
if (!_.isArray(events)) throw new Error('events is not an array.')
|
||||
} catch (err) {
|
||||
console.warn(err)
|
||||
events = []
|
||||
localStorage.setItem('events', JSON.stringify(events))
|
||||
console.info('Events cache initialzed')
|
||||
}
|
||||
return events
|
||||
}
|
||||
|
||||
function _keep (name, properties) {
|
||||
let events = _fetch()
|
||||
properties.time = new Date()
|
||||
events.push({
|
||||
name,
|
||||
properties
|
||||
})
|
||||
localStorage.setItem('events', JSON.stringify(events))
|
||||
}
|
||||
|
||||
function _keepUnique (name, properties) {
|
||||
let events = _fetch()
|
||||
properties.time = new Date()
|
||||
events = events.filter((event) => event.name !== name)
|
||||
events.push({
|
||||
name,
|
||||
properties
|
||||
})
|
||||
localStorage.setItem('events', JSON.stringify(events))
|
||||
}
|
||||
|
||||
function _flush () {
|
||||
let events = _fetch()
|
||||
let spliced = events.splice(0, 50)
|
||||
localStorage.setItem('events', JSON.stringify(events))
|
||||
|
||||
if (spliced.length > 0) {
|
||||
let parsedEvents = spliced
|
||||
.filter((event) => {
|
||||
if (!_.isObject(event)) return false
|
||||
if (!_.isString(event.name)) return false
|
||||
if (!_.isObject(event.properties)) return false
|
||||
if (!moment(event.properties.time).isValid()) return false
|
||||
if (new Date() - moment(event.properties.time).toDate() > 1000 * 3600 * 24 * 3) return false
|
||||
return true
|
||||
})
|
||||
.map((event) => {
|
||||
return {
|
||||
event: event.name,
|
||||
properties: event.properties
|
||||
}
|
||||
})
|
||||
|
||||
mixpanel.import_batch(parsedEvents, {}, (errs) => {
|
||||
if (errs.length > 0) {
|
||||
let events = _fetch()
|
||||
events = events.concat(spliced)
|
||||
localStorage.setItem('events', JSON.stringify(events))
|
||||
} else {
|
||||
_flush()
|
||||
}
|
||||
})
|
||||
|
||||
let state = store.getState()
|
||||
mixpanel.people.set(_getClientKey(), {
|
||||
storage_count: state.data.storageMap.size,
|
||||
note_count: state.data.noteMap.size,
|
||||
version: electron.remote.app.getVersion()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
setInterval(_flush, 1000 * 60 * 60)
|
||||
|
||||
function track (name, properties) {
|
||||
switch (name) {
|
||||
case 'MAIN_FOCUSED':
|
||||
properties = Object.assign({}, properties, {
|
||||
distinct_id: _getClientKey()
|
||||
})
|
||||
_keepUnique(name, properties)
|
||||
break
|
||||
default:
|
||||
properties = Object.assign({}, properties, {
|
||||
distinct_id: _getClientKey()
|
||||
})
|
||||
_keep(name, properties)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
_mp: mixpanel,
|
||||
track
|
||||
}
|
||||
@@ -16,7 +16,7 @@ class ModalBase extends React.Component {
|
||||
close () {
|
||||
if (modalBase != null) modalBase.setState({component: null, componentProps: null, isHidden: true})
|
||||
// Toggle overflow style on NoteList
|
||||
let list = document.querySelector('.NoteList__list___browser-main-NoteList-')
|
||||
const list = document.querySelector('.NoteList__list___browser-main-NoteList-')
|
||||
list.style.overflow = 'auto'
|
||||
}
|
||||
|
||||
@@ -34,14 +34,14 @@ class ModalBase extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
let el = document.createElement('div')
|
||||
const el = document.createElement('div')
|
||||
document.body.appendChild(el)
|
||||
let modalBase = ReactDOM.render(<ModalBase />, el)
|
||||
const modalBase = ReactDOM.render(<ModalBase />, el)
|
||||
|
||||
export function openModal (component, props) {
|
||||
if (modalBase == null) { return }
|
||||
// Hide scrollbar by removing overflow when modal opens
|
||||
let list = document.querySelector('.NoteList__list___browser-main-NoteList-')
|
||||
const list = document.querySelector('.NoteList__list___browser-main-NoteList-')
|
||||
list.style.overflow = 'hidden'
|
||||
document.body.setAttribute('data-modal', 'open')
|
||||
modalBase.setState({component: component, componentProps: props, isHidden: false})
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React, { PropTypes } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
import CSSModules from 'browser/lib/CSSModules'
|
||||
import styles from './CreateFolderModal.styl'
|
||||
import dataApi from 'browser/main/lib/dataApi'
|
||||
@@ -51,8 +52,8 @@ class CreateFolderModal extends React.Component {
|
||||
confirm () {
|
||||
AwsMobileAnalyticsConfig.recordDynamicCustomEvent('ADD_FOLDER')
|
||||
if (this.state.name.trim().length > 0) {
|
||||
let { storage } = this.props
|
||||
let input = {
|
||||
const { storage } = this.props
|
||||
const input = {
|
||||
name: this.state.name.trim(),
|
||||
color: consts.FOLDER_COLORS[Math.floor(Math.random() * 7) % 7]
|
||||
}
|
||||
|
||||
@@ -13,9 +13,9 @@ const electron = require('electron')
|
||||
const { remote } = electron
|
||||
|
||||
function browseFolder () {
|
||||
let dialog = remote.dialog
|
||||
const dialog = remote.dialog
|
||||
|
||||
let defaultPath = remote.app.getPath('home')
|
||||
const defaultPath = remote.app.getPath('home')
|
||||
return new Promise((resolve, reject) => {
|
||||
dialog.showOpenDialog({
|
||||
title: 'Select Directory',
|
||||
@@ -55,7 +55,7 @@ class InitModal extends React.Component {
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
}
|
||||
let newState = {
|
||||
const newState = {
|
||||
isLoading: false
|
||||
}
|
||||
if (data != null) {
|
||||
@@ -122,7 +122,7 @@ class InitModal extends React.Component {
|
||||
notes: data.notes
|
||||
})
|
||||
|
||||
let defaultSnippetNote = dataApi
|
||||
const defaultSnippetNote = dataApi
|
||||
.createNote(data.storage.key, {
|
||||
type: 'SNIPPET_NOTE',
|
||||
folder: data.storage.folders[0].key,
|
||||
@@ -147,12 +147,12 @@ class InitModal extends React.Component {
|
||||
note: note
|
||||
})
|
||||
})
|
||||
let defaultMarkdownNote = dataApi
|
||||
const defaultMarkdownNote = dataApi
|
||||
.createNote(data.storage.key, {
|
||||
type: 'MARKDOWN_NOTE',
|
||||
folder: data.storage.folders[0].key,
|
||||
title: 'Welcome to Boostnote!',
|
||||
content: '# Welcome to Boostnote! \n### _Click to edit this note._\n\n---\n\nBoostnote is an *open source* note-taking app. \nRepository is published on [GitHub](https://github.com/BoostIO/Boostnote), and tweeting everyday on [@Boostnoteapp](https://twitter.com/boostnoteapp)!\n\n## Features \n- [x] No internet or registration required. \n- [ ] Quick search and copy the content of note. macOS: <kbd>Cmd</kbd> + <kbd>Alt</kbd> + <kbd>S</kbd> / windows: <kbd>Ctrl</kbd> + <kbd>Alt</kbd> + <kbd>S</kbd> \n- [ ] Markdown & Snippet note. \n- [ ] Available for `vim` and `emacs` mode. \n- [ ] Choose your favorite theme on UI, Editor and Code Block! \n--- \n\n- Copy Codeblock on Markdown Preview.\n```javascript\nvar boostnote = document.getElementById(\'enjoy\').innerHTML\n\nconsole.log(boostnote)\n```'
|
||||
content: '# Welcome to Boostnote!\n## Click here to edit markdown :wave:\n\n<iframe width="560" height="315" src="https://www.youtube.com/embed/L0qNPLsvmyM" frameborder="0" allowfullscreen></iframe>\n\n## Docs :memo:\n- [Boostnote | Boost your happiness, productivity and creativity.](https://hackernoon.com/boostnote-boost-your-happiness-productivity-and-creativity-315034efeebe)\n- [Cloud Syncing & Backups](https://github.com/BoostIO/Boostnote/wiki/Cloud-Syncing-and-Backup)\n- [How to sync your data across Desktop and Mobile apps](https://github.com/BoostIO/Boostnote/wiki/Sync-Data-Across-Desktop-and-Mobile-apps)\n- [Convert data from **Evernote** to Boostnote.](https://github.com/BoostIO/Boostnote/wiki/Evernote)\n- [Keyboard Shortcuts](https://github.com/BoostIO/Boostnote/wiki/Keyboard-Shortcuts)\n- [Keymaps in Editor mode](https://github.com/BoostIO/Boostnote/wiki/Keymaps-in-Editor-mode)\n- [How to set syntax highlight in Snippet note](https://github.com/BoostIO/Boostnote/wiki/Syntax-Highlighting)\n\n---\n\n## Article Archive :books:\n- [Reddit English](http://bit.ly/2mOJPu7)\n- [Reddit Spanish](https://www.reddit.com/r/boostnote_es/)\n- [Reddit Chinese](https://www.reddit.com/r/boostnote_cn/)\n- [Reddit Japanese](https://www.reddit.com/r/boostnote_jp/)\n\n---\n\n## Community :beers:\n- [GitHub](http://bit.ly/2AWWzkD)\n- [Twitter](http://bit.ly/2z8BUJZ)\n- [Facebook Group](http://bit.ly/2jcca8t)'
|
||||
})
|
||||
.then((note) => {
|
||||
store.dispatch({
|
||||
@@ -184,6 +184,12 @@ class InitModal extends React.Component {
|
||||
})
|
||||
}
|
||||
|
||||
handleKeyDown (e) {
|
||||
if (e.keyCode === 27) {
|
||||
this.props.close()
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
if (this.state.isLoading) {
|
||||
return <div styleName='root--loading'>
|
||||
@@ -194,7 +200,7 @@ class InitModal extends React.Component {
|
||||
return (
|
||||
<div styleName='root'
|
||||
tabIndex='-1'
|
||||
onKeyDown={this.props.close}
|
||||
onKeyDown={(e) => this.handleKeyDown(e)}
|
||||
>
|
||||
|
||||
<div styleName='header'>
|
||||
|
||||
@@ -26,7 +26,7 @@ class NewNoteModal extends React.Component {
|
||||
handleMarkdownNoteButtonClick (e) {
|
||||
AwsMobileAnalyticsConfig.recordDynamicCustomEvent('ADD_MARKDOWN')
|
||||
AwsMobileAnalyticsConfig.recordDynamicCustomEvent('ADD_ALLNOTE')
|
||||
let { storage, folder, dispatch, location } = this.props
|
||||
const { storage, folder, dispatch, location } = this.props
|
||||
dataApi
|
||||
.createNote(storage, {
|
||||
type: 'MARKDOWN_NOTE',
|
||||
@@ -58,7 +58,7 @@ class NewNoteModal extends React.Component {
|
||||
handleSnippetNoteButtonClick (e) {
|
||||
AwsMobileAnalyticsConfig.recordDynamicCustomEvent('ADD_SNIPPET')
|
||||
AwsMobileAnalyticsConfig.recordDynamicCustomEvent('ADD_ALLNOTE')
|
||||
let { storage, folder, dispatch, location } = this.props
|
||||
const { storage, folder, dispatch, location } = this.props
|
||||
|
||||
dataApi
|
||||
.createNote(storage, {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React, { PropTypes } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
import CSSModules from 'browser/lib/CSSModules'
|
||||
import ReactDOM from 'react-dom'
|
||||
import styles from './FolderItem.styl'
|
||||
@@ -23,7 +24,7 @@ class FolderItem extends React.Component {
|
||||
}
|
||||
|
||||
handleEditChange (e) {
|
||||
let { folder } = this.state
|
||||
const { folder } = this.state
|
||||
|
||||
folder.name = this.refs.nameInput.value
|
||||
this.setState({
|
||||
@@ -36,7 +37,7 @@ class FolderItem extends React.Component {
|
||||
}
|
||||
|
||||
confirm () {
|
||||
let { storage, folder } = this.props
|
||||
const { storage, folder } = this.props
|
||||
dataApi
|
||||
.updateFolder(storage.key, folder.key, {
|
||||
color: this.state.folder.color,
|
||||
@@ -162,7 +163,7 @@ class FolderItem extends React.Component {
|
||||
}
|
||||
|
||||
handleDeleteConfirmButtonClick (e) {
|
||||
let { storage, folder } = this.props
|
||||
const { storage, folder } = this.props
|
||||
dataApi
|
||||
.deleteFolder(storage.key, folder.key)
|
||||
.then((data) => {
|
||||
@@ -197,8 +198,8 @@ class FolderItem extends React.Component {
|
||||
}
|
||||
|
||||
handleEditButtonClick (e) {
|
||||
let { folder: propsFolder } = this.props
|
||||
let { folder: stateFolder } = this.state
|
||||
const { folder: propsFolder } = this.props
|
||||
const { folder: stateFolder } = this.state
|
||||
const folder = Object.assign({}, stateFolder, propsFolder)
|
||||
this.setState({
|
||||
status: 'EDIT',
|
||||
@@ -215,7 +216,7 @@ class FolderItem extends React.Component {
|
||||
}
|
||||
|
||||
renderIdle () {
|
||||
let { folder } = this.props
|
||||
const { folder } = this.props
|
||||
return (
|
||||
<div styleName='folderItem'
|
||||
onDoubleClick={(e) => this.handleEditButtonClick(e)}
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
import React, { PropTypes } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
import CSSModules from 'browser/lib/CSSModules'
|
||||
import dataApi from 'browser/main/lib/dataApi'
|
||||
import styles from './FolderList.styl'
|
||||
import store from 'browser/main/store'
|
||||
import FolderItem from './FolderItem'
|
||||
import { SortableContainer, arrayMove } from 'react-sortable-hoc'
|
||||
import { SortableContainer } from 'react-sortable-hoc'
|
||||
|
||||
class FolderList extends React.Component {
|
||||
render () {
|
||||
let { storage, hostBoundingBox } = this.props
|
||||
const { storage, hostBoundingBox } = this.props
|
||||
|
||||
let folderList = storage.folders.map((folder, index) => {
|
||||
const folderList = storage.folders.map((folder, index) => {
|
||||
return <FolderItem key={folder.key}
|
||||
folder={folder}
|
||||
storage={storage}
|
||||
@@ -53,7 +54,7 @@ class SortableFolderListComponent extends React.Component {
|
||||
constructor (props) {
|
||||
super(props)
|
||||
this.onSortEnd = ({oldIndex, newIndex}) => {
|
||||
let { storage } = this.props
|
||||
const { storage } = this.props
|
||||
dataApi
|
||||
.reorderFolder(storage.key, oldIndex, newIndex)
|
||||
.then((data) => {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React, { PropTypes } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
import CSSModules from 'browser/lib/CSSModules'
|
||||
import styles from './ConfigTab.styl'
|
||||
import ConfigManager from 'browser/main/lib/ConfigManager'
|
||||
@@ -41,7 +42,7 @@ class HotkeyTab extends React.Component {
|
||||
}
|
||||
|
||||
handleSaveButtonClick (e) {
|
||||
let newConfig = {
|
||||
const newConfig = {
|
||||
hotkey: this.state.config.hotkey
|
||||
}
|
||||
|
||||
@@ -61,7 +62,7 @@ class HotkeyTab extends React.Component {
|
||||
}
|
||||
|
||||
handleHotkeyChange (e) {
|
||||
let { config } = this.state
|
||||
const { config } = this.state
|
||||
config.hotkey = {
|
||||
toggleFinder: this.refs.toggleFinder.value,
|
||||
toggleMain: this.refs.toggleMain.value
|
||||
@@ -80,13 +81,13 @@ class HotkeyTab extends React.Component {
|
||||
}
|
||||
|
||||
render () {
|
||||
let keymapAlert = this.state.keymapAlert
|
||||
let keymapAlertElement = keymapAlert != null
|
||||
const keymapAlert = this.state.keymapAlert
|
||||
const keymapAlertElement = keymapAlert != null
|
||||
? <p className={`alert ${keymapAlert.type}`}>
|
||||
{keymapAlert.message}
|
||||
</p>
|
||||
: null
|
||||
let { config } = this.state
|
||||
const { config } = this.state
|
||||
|
||||
return (
|
||||
<div styleName='root'>
|
||||
|
||||
@@ -68,7 +68,41 @@ class InfoTab extends React.Component {
|
||||
render () {
|
||||
return (
|
||||
<div styleName='root'>
|
||||
<div styleName='header'>Info</div>
|
||||
|
||||
<div styleName='header--sub'>Community</div>
|
||||
<div styleName='top'>
|
||||
<ul styleName='list'>
|
||||
<li>
|
||||
<a href='https://github.com/BoostIO/Boostnote/issues'
|
||||
onClick={(e) => this.handleLinkClick(e)}
|
||||
>GitHub</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href='https://medium.com/boostnote'
|
||||
onClick={(e) => this.handleLinkClick(e)}
|
||||
>Blog</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href='https://www.reddit.com/r/Boostnote/'
|
||||
onClick={(e) => this.handleLinkClick(e)}
|
||||
>Reddit</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href='https://www.facebook.com/groups/boostnote'
|
||||
onClick={(e) => this.handleLinkClick(e)}
|
||||
>Facebook Group</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href='https://twitter.com/boostnoteapp'
|
||||
onClick={(e) => this.handleLinkClick(e)}
|
||||
>Twitter</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
<div styleName='header--sub'>Info</div>
|
||||
|
||||
<div styleName='top'>
|
||||
<div styleName='icon-space'>
|
||||
@@ -81,31 +115,17 @@ class InfoTab extends React.Component {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul styleName='list'>
|
||||
<li>
|
||||
<a href='https://boostnote.io'
|
||||
onClick={(e) => this.handleLinkClick(e)}
|
||||
>Website</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href='https://boostnote.paintory.com/'
|
||||
onClick={(e) => this.handleLinkClick(e)}
|
||||
>Boostnote Shop</a> : Products are shipped to all over the world 🌏
|
||||
</li>
|
||||
<li>
|
||||
<a href='https://opencollective.com/boostnoteio'
|
||||
onClick={(e) => this.handleLinkClick(e)}
|
||||
>Crowdfunding</a> : Thank you for your support 🎉
|
||||
</li>
|
||||
<li>
|
||||
<a href='https://github.com/BoostIO/Boostnote/issues'
|
||||
onClick={(e) => this.handleLinkClick(e)}
|
||||
>GitHub Issues</a> : We'd love to hear your feedback 🙌
|
||||
</li>
|
||||
<li>
|
||||
<a href='https://github.com/BoostIO/Boostnote/blob/master/docs/build.md'
|
||||
onClick={(e) => this.handleLinkClick(e)}
|
||||
>Development</a> : Development configurations for Boostnote 🚀
|
||||
>Development</a> : Development configurations for Boostnote.
|
||||
</li>
|
||||
<li styleName='cc'>
|
||||
Copyright (C) 2017 Maisin&Co.
|
||||
@@ -114,7 +134,9 @@ class InfoTab extends React.Component {
|
||||
License: GPL v3
|
||||
</li>
|
||||
</ul>
|
||||
<hr />
|
||||
|
||||
<hr styleName='separate-line' />
|
||||
|
||||
<div styleName='policy'>Data collection policy</div>
|
||||
<div>We collect only the number of DAU for Boostnote and **DO NOT collect** any detail information such as your note content.</div>
|
||||
<div>You can see how it works on <a href='https://github.com/BoostIO/Boostnote' onClick={(e) => this.handleLinkClick(e)}>GitHub</a>.</div>
|
||||
|
||||
@@ -42,6 +42,9 @@
|
||||
color #4E8EC6
|
||||
text-decoration none
|
||||
|
||||
.separate-line
|
||||
margin 40px 0
|
||||
|
||||
.policy
|
||||
width 100%
|
||||
font-size 20px
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user