mirror of
https://github.com/BoostIo/Boostnote
synced 2025-12-16 03:06:27 +00:00
Merge branch 'master' into export-yfm
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -3,4 +3,3 @@
|
||||
|
||||
.spellcheck-select
|
||||
border: none
|
||||
text-decoration underline wavy red
|
||||
|
||||
77
browser/components/ColorPicker.js
Normal file
77
browser/components/ColorPicker.js
Normal file
@@ -0,0 +1,77 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { SketchPicker } from 'react-color'
|
||||
import CSSModules from 'browser/lib/CSSModules'
|
||||
import styles from './ColorPicker.styl'
|
||||
|
||||
const componentHeight = 330
|
||||
|
||||
class ColorPicker extends React.Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
color: this.props.color || '#939395'
|
||||
}
|
||||
|
||||
this.onColorChange = this.onColorChange.bind(this)
|
||||
this.handleConfirm = this.handleConfirm.bind(this)
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
this.onColorChange(nextProps.color)
|
||||
}
|
||||
|
||||
onColorChange(color) {
|
||||
this.setState({
|
||||
color
|
||||
})
|
||||
}
|
||||
|
||||
handleConfirm() {
|
||||
this.props.onConfirm(this.state.color)
|
||||
}
|
||||
|
||||
render() {
|
||||
const { onReset, onCancel, targetRect } = this.props
|
||||
const { color } = this.state
|
||||
|
||||
const clientHeight = document.body.clientHeight
|
||||
const alignX = targetRect.right + 4
|
||||
let alignY = targetRect.top
|
||||
if (targetRect.top + componentHeight > clientHeight) {
|
||||
alignY = targetRect.bottom - componentHeight
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
styleName='colorPicker'
|
||||
style={{ top: `${alignY}px`, left: `${alignX}px` }}
|
||||
>
|
||||
<div styleName='cover' onClick={onCancel} />
|
||||
<SketchPicker color={color} onChange={this.onColorChange} />
|
||||
<div styleName='footer'>
|
||||
<button styleName='btn-reset' onClick={onReset}>
|
||||
Reset
|
||||
</button>
|
||||
<button styleName='btn-cancel' onClick={onCancel}>
|
||||
Cancel
|
||||
</button>
|
||||
<button styleName='btn-confirm' onClick={this.handleConfirm}>
|
||||
Confirm
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
ColorPicker.propTypes = {
|
||||
color: PropTypes.string,
|
||||
targetRect: PropTypes.object,
|
||||
onConfirm: PropTypes.func.isRequired,
|
||||
onCancel: PropTypes.func.isRequired,
|
||||
onReset: PropTypes.func.isRequired
|
||||
}
|
||||
|
||||
export default CSSModules(ColorPicker, styles)
|
||||
39
browser/components/ColorPicker.styl
Normal file
39
browser/components/ColorPicker.styl
Normal file
@@ -0,0 +1,39 @@
|
||||
.colorPicker
|
||||
position fixed
|
||||
z-index 2
|
||||
display flex
|
||||
flex-direction column
|
||||
|
||||
.cover
|
||||
position fixed
|
||||
top 0
|
||||
right 0
|
||||
bottom 0
|
||||
left 0
|
||||
|
||||
.footer
|
||||
display flex
|
||||
justify-content center
|
||||
z-index 2
|
||||
align-items center
|
||||
& > button + button
|
||||
margin-left 10px
|
||||
|
||||
.btn-cancel,
|
||||
.btn-confirm,
|
||||
.btn-reset
|
||||
vertical-align middle
|
||||
height 25px
|
||||
margin-top 2.5px
|
||||
border-radius 2px
|
||||
border none
|
||||
padding 0 5px
|
||||
background-color $default-button-background
|
||||
&:hover
|
||||
background-color $default-button-background--hover
|
||||
.btn-confirm
|
||||
background-color #1EC38B
|
||||
&:hover
|
||||
background-color darken(#1EC38B, 25%)
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable camelcase */
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
import CSSModules from 'browser/lib/CSSModules'
|
||||
@@ -10,7 +11,7 @@ import ConfigManager from 'browser/main/lib/ConfigManager'
|
||||
import attachmentManagement from 'browser/main/lib/dataApi/attachmentManagement'
|
||||
|
||||
class MarkdownEditor extends React.Component {
|
||||
constructor (props) {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
// char codes for ctrl + w
|
||||
@@ -20,194 +21,246 @@ class MarkdownEditor extends React.Component {
|
||||
this.supportMdSelectionBold = [16, 17, 186]
|
||||
|
||||
this.state = {
|
||||
status: props.config.editor.switchPreview === 'RIGHTCLICK' ? props.config.editor.delfaultStatus : 'PREVIEW',
|
||||
status:
|
||||
props.config.editor.switchPreview === 'RIGHTCLICK'
|
||||
? props.config.editor.delfaultStatus
|
||||
: 'CODE',
|
||||
renderValue: props.value,
|
||||
keyPressed: new Set(),
|
||||
isLocked: false
|
||||
isLocked: props.isLocked
|
||||
}
|
||||
|
||||
this.lockEditorCode = () => this.handleLockEditor()
|
||||
this.lockEditorCode = this.handleLockEditor.bind(this)
|
||||
this.focusEditor = this.focusEditor.bind(this)
|
||||
|
||||
this.previewRef = React.createRef()
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
componentDidMount() {
|
||||
this.value = this.refs.code.value
|
||||
eventEmitter.on('editor:lock', this.lockEditorCode)
|
||||
eventEmitter.on('editor:focus', this.focusEditor)
|
||||
}
|
||||
|
||||
componentDidUpdate () {
|
||||
componentDidUpdate() {
|
||||
this.value = this.refs.code.value
|
||||
}
|
||||
|
||||
componentWillReceiveProps (props) {
|
||||
UNSAFE_componentWillReceiveProps(props) {
|
||||
if (props.value !== this.props.value) {
|
||||
this.queueRendering(props.value)
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
componentWillUnmount() {
|
||||
this.cancelQueue()
|
||||
eventEmitter.off('editor:lock', this.lockEditorCode)
|
||||
eventEmitter.off('editor:focus', this.focusEditor)
|
||||
}
|
||||
|
||||
queueRendering (value) {
|
||||
focusEditor() {
|
||||
this.setState(
|
||||
{
|
||||
status: 'CODE'
|
||||
},
|
||||
() => {
|
||||
if (this.refs.code == null) {
|
||||
return
|
||||
}
|
||||
this.refs.code.focus()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
queueRendering(value) {
|
||||
clearTimeout(this.renderTimer)
|
||||
this.renderTimer = setTimeout(() => {
|
||||
this.renderPreview(value)
|
||||
}, 500)
|
||||
}
|
||||
|
||||
cancelQueue () {
|
||||
cancelQueue() {
|
||||
clearTimeout(this.renderTimer)
|
||||
}
|
||||
|
||||
renderPreview (value) {
|
||||
renderPreview(value) {
|
||||
this.setState({
|
||||
renderValue: value
|
||||
})
|
||||
}
|
||||
|
||||
setValue (value) {
|
||||
setValue(value) {
|
||||
this.refs.code.setValue(value)
|
||||
}
|
||||
|
||||
handleChange (e) {
|
||||
handleChange(e) {
|
||||
this.value = this.refs.code.value
|
||||
this.props.onChange(e)
|
||||
}
|
||||
|
||||
handleContextMenu (e) {
|
||||
handleContextMenu(e) {
|
||||
if (this.state.isLocked) return
|
||||
const { config } = this.props
|
||||
if (config.editor.switchPreview === 'RIGHTCLICK') {
|
||||
const newStatus = this.state.status === 'PREVIEW' ? 'CODE' : 'PREVIEW'
|
||||
this.setState({
|
||||
status: newStatus
|
||||
}, () => {
|
||||
if (newStatus === 'CODE') {
|
||||
this.refs.code.focus()
|
||||
} else {
|
||||
this.refs.preview.focus()
|
||||
}
|
||||
eventEmitter.emit('topbar:togglelockbutton', this.state.status)
|
||||
this.setState(
|
||||
{
|
||||
status: newStatus
|
||||
},
|
||||
() => {
|
||||
if (newStatus === 'CODE') {
|
||||
this.refs.code.focus()
|
||||
} else {
|
||||
this.previewRef.current.focus()
|
||||
}
|
||||
eventEmitter.emit('topbar:togglelockbutton', this.state.status)
|
||||
|
||||
const newConfig = Object.assign({}, config)
|
||||
newConfig.editor.delfaultStatus = newStatus
|
||||
ConfigManager.set(newConfig)
|
||||
})
|
||||
const newConfig = Object.assign({}, config)
|
||||
newConfig.editor.delfaultStatus = newStatus
|
||||
ConfigManager.set(newConfig)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
handleBlur (e) {
|
||||
handleBlur(e) {
|
||||
if (this.state.isLocked) return
|
||||
this.setState({ keyPressed: new Set() })
|
||||
const { config } = this.props
|
||||
if (config.editor.switchPreview === 'BLUR' ||
|
||||
(config.editor.switchPreview === 'DBL_CLICK' && this.state.status === 'CODE')
|
||||
if (
|
||||
config.editor.switchPreview === 'BLUR' ||
|
||||
(config.editor.switchPreview === 'DBL_CLICK' &&
|
||||
this.state.status === 'CODE')
|
||||
) {
|
||||
const cursorPosition = this.refs.code.editor.getCursor()
|
||||
this.setState({
|
||||
status: 'PREVIEW'
|
||||
}, () => {
|
||||
this.refs.preview.focus()
|
||||
this.refs.preview.scrollTo(cursorPosition.line)
|
||||
})
|
||||
this.setState(
|
||||
{
|
||||
status: 'PREVIEW'
|
||||
},
|
||||
() => {
|
||||
this.previewRef.current.focus()
|
||||
this.previewRef.current.scrollToRow(cursorPosition.line)
|
||||
}
|
||||
)
|
||||
eventEmitter.emit('topbar:togglelockbutton', this.state.status)
|
||||
}
|
||||
}
|
||||
|
||||
handleDoubleClick (e) {
|
||||
handleDoubleClick(e) {
|
||||
if (this.state.isLocked) return
|
||||
this.setState({keyPressed: new Set()})
|
||||
this.setState({ keyPressed: new Set() })
|
||||
const { config } = this.props
|
||||
if (config.editor.switchPreview === 'DBL_CLICK') {
|
||||
this.setState({
|
||||
status: 'CODE'
|
||||
}, () => {
|
||||
this.refs.code.focus()
|
||||
eventEmitter.emit('topbar:togglelockbutton', this.state.status)
|
||||
})
|
||||
this.setState(
|
||||
{
|
||||
status: 'CODE'
|
||||
},
|
||||
() => {
|
||||
this.refs.code.focus()
|
||||
eventEmitter.emit('topbar:togglelockbutton', this.state.status)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
handlePreviewMouseDown (e) {
|
||||
handlePreviewMouseDown(e) {
|
||||
this.previewMouseDownedAt = new Date()
|
||||
}
|
||||
|
||||
handlePreviewMouseUp (e) {
|
||||
handlePreviewMouseUp(e) {
|
||||
const { config } = this.props
|
||||
if (config.editor.switchPreview === 'BLUR' && new Date() - this.previewMouseDownedAt < 200) {
|
||||
this.setState({
|
||||
status: 'CODE'
|
||||
}, () => {
|
||||
this.refs.code.focus()
|
||||
})
|
||||
if (
|
||||
config.editor.switchPreview === 'BLUR' &&
|
||||
new Date() - this.previewMouseDownedAt < 200
|
||||
) {
|
||||
this.setState(
|
||||
{
|
||||
status: 'CODE'
|
||||
},
|
||||
() => {
|
||||
this.refs.code.focus()
|
||||
}
|
||||
)
|
||||
eventEmitter.emit('topbar:togglelockbutton', this.state.status)
|
||||
}
|
||||
}
|
||||
|
||||
handleCheckboxClick (e) {
|
||||
handleCheckboxClick(e) {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
const idMatch = /checkbox-([0-9]+)/
|
||||
const checkedMatch = /^\s*[\+\-\*] \[x\]/i
|
||||
const uncheckedMatch = /^\s*[\+\-\*] \[ \]/
|
||||
const checkReplace = /\[x\]/i
|
||||
const uncheckReplace = /\[ \]/
|
||||
const checkedMatch = /^(\s*>?)*\s*[+\-*] \[x]/i
|
||||
const uncheckedMatch = /^(\s*>?)*\s*[+\-*] \[ ]/
|
||||
const checkReplace = /\[x]/i
|
||||
const uncheckReplace = /\[ ]/
|
||||
if (idMatch.test(e.target.getAttribute('id'))) {
|
||||
const lineIndex = parseInt(e.target.getAttribute('id').match(idMatch)[1], 10) - 1
|
||||
const lines = this.refs.code.value
|
||||
.split('\n')
|
||||
const lineIndex =
|
||||
parseInt(e.target.getAttribute('id').match(idMatch)[1], 10) - 1
|
||||
const lines = this.refs.code.value.split('\n')
|
||||
|
||||
const targetLine = lines[lineIndex]
|
||||
let newLine = targetLine
|
||||
|
||||
if (targetLine.match(checkedMatch)) {
|
||||
lines[lineIndex] = targetLine.replace(checkReplace, '[ ]')
|
||||
newLine = targetLine.replace(checkReplace, '[ ]')
|
||||
}
|
||||
if (targetLine.match(uncheckedMatch)) {
|
||||
lines[lineIndex] = targetLine.replace(uncheckReplace, '[x]')
|
||||
newLine = targetLine.replace(uncheckReplace, '[x]')
|
||||
}
|
||||
this.refs.code.setValue(lines.join('\n'))
|
||||
this.refs.code.setLineContent(lineIndex, newLine)
|
||||
}
|
||||
}
|
||||
|
||||
focus () {
|
||||
focus() {
|
||||
if (this.state.status === 'PREVIEW') {
|
||||
this.setState({
|
||||
status: 'CODE'
|
||||
}, () => {
|
||||
this.refs.code.focus()
|
||||
})
|
||||
this.setState(
|
||||
{
|
||||
status: 'CODE'
|
||||
},
|
||||
() => {
|
||||
this.refs.code.focus()
|
||||
}
|
||||
)
|
||||
} else {
|
||||
this.refs.code.focus()
|
||||
}
|
||||
eventEmitter.emit('topbar:togglelockbutton', this.state.status)
|
||||
}
|
||||
|
||||
reload () {
|
||||
reload() {
|
||||
this.refs.code.reload()
|
||||
this.cancelQueue()
|
||||
this.renderPreview(this.props.value)
|
||||
}
|
||||
|
||||
handleKeyDown (e) {
|
||||
handleKeyDown(e) {
|
||||
const { config } = this.props
|
||||
if (this.state.status !== 'CODE') return false
|
||||
const keyPressed = this.state.keyPressed
|
||||
keyPressed.add(e.keyCode)
|
||||
this.setState({ keyPressed })
|
||||
const 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' &&
|
||||
this.escapeFromEditor.every(isNoteHandlerKey)) {
|
||||
if (
|
||||
keyPressed.size === this.escapeFromEditor.length &&
|
||||
!this.state.isLocked &&
|
||||
this.state.status === 'CODE' &&
|
||||
this.escapeFromEditor.every(isNoteHandlerKey)
|
||||
) {
|
||||
this.handleContextMenu()
|
||||
if (config.editor.switchPreview === 'BLUR') document.activeElement.blur()
|
||||
}
|
||||
if (keyPressed.size === this.supportMdSelectionBold.length && this.supportMdSelectionBold.every(isNoteHandlerKey)) {
|
||||
if (
|
||||
keyPressed.size === this.supportMdSelectionBold.length &&
|
||||
this.supportMdSelectionBold.every(isNoteHandlerKey)
|
||||
) {
|
||||
this.addMdAroundWord('**')
|
||||
}
|
||||
}
|
||||
|
||||
addMdAroundWord (mdElement) {
|
||||
addMdAroundWord(mdElement) {
|
||||
if (this.refs.code.editor.getSelection()) {
|
||||
return this.addMdAroundSelection(mdElement)
|
||||
}
|
||||
@@ -215,47 +268,64 @@ class MarkdownEditor extends React.Component {
|
||||
const word = this.refs.code.editor.findWordAt(currentCaret)
|
||||
const cmDoc = this.refs.code.editor.getDoc()
|
||||
cmDoc.replaceRange(mdElement, word.anchor)
|
||||
cmDoc.replaceRange(mdElement, { line: word.head.line, ch: word.head.ch + mdElement.length })
|
||||
}
|
||||
|
||||
addMdAroundSelection (mdElement) {
|
||||
this.refs.code.editor.replaceSelection(`${mdElement}${this.refs.code.editor.getSelection()}${mdElement}`)
|
||||
}
|
||||
|
||||
handleDropImage (dropEvent) {
|
||||
dropEvent.preventDefault()
|
||||
const { storageKey, noteKey } = this.props
|
||||
|
||||
this.setState({
|
||||
status: 'CODE'
|
||||
}, () => {
|
||||
this.refs.code.focus()
|
||||
|
||||
this.refs.code.editor.execCommand('goDocEnd')
|
||||
this.refs.code.editor.execCommand('goLineEnd')
|
||||
this.refs.code.editor.execCommand('newlineAndIndent')
|
||||
|
||||
attachmentManagement.handleAttachmentDrop(
|
||||
this.refs.code,
|
||||
storageKey,
|
||||
noteKey,
|
||||
dropEvent
|
||||
)
|
||||
cmDoc.replaceRange(mdElement, {
|
||||
line: word.head.line,
|
||||
ch: word.head.ch + mdElement.length
|
||||
})
|
||||
}
|
||||
|
||||
handleKeyUp (e) {
|
||||
addMdAroundSelection(mdElement) {
|
||||
this.refs.code.editor.replaceSelection(
|
||||
`${mdElement}${this.refs.code.editor.getSelection()}${mdElement}`
|
||||
)
|
||||
}
|
||||
|
||||
handleDropImage(dropEvent) {
|
||||
dropEvent.preventDefault()
|
||||
const { storageKey, noteKey } = this.props
|
||||
|
||||
this.setState(
|
||||
{
|
||||
status: 'CODE'
|
||||
},
|
||||
() => {
|
||||
this.refs.code.focus()
|
||||
|
||||
this.refs.code.editor.execCommand('goDocEnd')
|
||||
this.refs.code.editor.execCommand('goLineEnd')
|
||||
this.refs.code.editor.execCommand('newlineAndIndent')
|
||||
|
||||
attachmentManagement.handleAttachmentDrop(
|
||||
this.refs.code,
|
||||
storageKey,
|
||||
noteKey,
|
||||
dropEvent
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
handleKeyUp(e) {
|
||||
const keyPressed = this.state.keyPressed
|
||||
keyPressed.delete(e.keyCode)
|
||||
this.setState({ keyPressed })
|
||||
}
|
||||
|
||||
handleLockEditor () {
|
||||
handleLockEditor() {
|
||||
this.setState({ isLocked: !this.state.isLocked })
|
||||
}
|
||||
|
||||
render () {
|
||||
const {className, value, config, storageKey, noteKey, linesHighlighted, getNote} = this.props
|
||||
render() {
|
||||
const {
|
||||
className,
|
||||
value,
|
||||
config,
|
||||
storageKey,
|
||||
noteKey,
|
||||
linesHighlighted,
|
||||
getNote,
|
||||
RTL
|
||||
} = this.props
|
||||
|
||||
let editorFontSize = parseInt(config.editor.fontSize, 10)
|
||||
if (!(editorFontSize > 0 && editorFontSize < 101)) editorFontSize = 14
|
||||
@@ -263,23 +333,24 @@ class MarkdownEditor extends React.Component {
|
||||
if (!(editorFontSize > 0 && editorFontSize < 132)) editorIndentSize = 4
|
||||
|
||||
const previewStyle = {}
|
||||
if (this.props.ignorePreviewPointerEvents) previewStyle.pointerEvents = 'none'
|
||||
if (this.props.ignorePreviewPointerEvents)
|
||||
previewStyle.pointerEvents = 'none'
|
||||
|
||||
const storage = findStorage(storageKey)
|
||||
|
||||
return (
|
||||
<div className={className == null
|
||||
? 'MarkdownEditor'
|
||||
: `MarkdownEditor ${className}`
|
||||
<div
|
||||
className={
|
||||
className == null ? 'MarkdownEditor' : `MarkdownEditor ${className}`
|
||||
}
|
||||
onContextMenu={(e) => this.handleContextMenu(e)}
|
||||
onContextMenu={e => this.handleContextMenu(e)}
|
||||
tabIndex='-1'
|
||||
onKeyDown={(e) => this.handleKeyDown(e)}
|
||||
onKeyUp={(e) => this.handleKeyUp(e)}
|
||||
onKeyDown={e => this.handleKeyDown(e)}
|
||||
onKeyUp={e => this.handleKeyUp(e)}
|
||||
>
|
||||
<CodeEditor styleName={this.state.status === 'CODE'
|
||||
? 'codeEditor'
|
||||
: 'codeEditor--hide'
|
||||
<CodeEditor
|
||||
styleName={
|
||||
this.state.status === 'CODE' ? 'codeEditor' : 'codeEditor--hide'
|
||||
}
|
||||
ref='code'
|
||||
mode='Boost Flavored Markdown'
|
||||
@@ -293,6 +364,7 @@ class MarkdownEditor extends React.Component {
|
||||
enableRulers={config.editor.enableRulers}
|
||||
rulers={config.editor.rulers}
|
||||
displayLineNumbers={config.editor.displayLineNumbers}
|
||||
lineWrapping
|
||||
matchingPairs={config.editor.matchingPairs}
|
||||
matchingTriples={config.editor.matchingTriples}
|
||||
explodingPairs={config.editor.explodingPairs}
|
||||
@@ -302,16 +374,22 @@ class MarkdownEditor extends React.Component {
|
||||
fetchUrlTitle={config.editor.fetchUrlTitle}
|
||||
enableTableEditor={config.editor.enableTableEditor}
|
||||
linesHighlighted={linesHighlighted}
|
||||
onChange={(e) => this.handleChange(e)}
|
||||
onBlur={(e) => this.handleBlur(e)}
|
||||
onChange={e => this.handleChange(e)}
|
||||
onBlur={e => this.handleBlur(e)}
|
||||
spellCheck={config.editor.spellcheck}
|
||||
enableSmartPaste={config.editor.enableSmartPaste}
|
||||
hotkey={config.hotkey}
|
||||
switchPreview={config.editor.switchPreview}
|
||||
enableMarkdownLint={config.editor.enableMarkdownLint}
|
||||
customMarkdownLintConfig={config.editor.customMarkdownLintConfig}
|
||||
prettierConfig={config.editor.prettierConfig}
|
||||
deleteUnusedAttachments={config.editor.deleteUnusedAttachments}
|
||||
RTL={RTL}
|
||||
/>
|
||||
<MarkdownPreview styleName={this.state.status === 'PREVIEW'
|
||||
? 'preview'
|
||||
: 'preview--hide'
|
||||
<MarkdownPreview
|
||||
ref={this.previewRef}
|
||||
styleName={
|
||||
this.state.status === 'PREVIEW' ? 'preview' : 'preview--hide'
|
||||
}
|
||||
style={previewStyle}
|
||||
theme={config.ui.theme}
|
||||
@@ -327,14 +405,14 @@ class MarkdownEditor extends React.Component {
|
||||
smartArrows={config.preview.smartArrows}
|
||||
breaks={config.preview.breaks}
|
||||
sanitize={config.preview.sanitize}
|
||||
ref='preview'
|
||||
onContextMenu={(e) => this.handleContextMenu(e)}
|
||||
onDoubleClick={(e) => this.handleDoubleClick(e)}
|
||||
mermaidHTMLLabel={config.preview.mermaidHTMLLabel}
|
||||
onContextMenu={e => this.handleContextMenu(e)}
|
||||
onDoubleClick={e => this.handleDoubleClick(e)}
|
||||
tabIndex='0'
|
||||
value={this.state.renderValue}
|
||||
onMouseUp={(e) => this.handlePreviewMouseUp(e)}
|
||||
onMouseDown={(e) => this.handlePreviewMouseDown(e)}
|
||||
onCheckboxClick={(e) => this.handleCheckboxClick(e)}
|
||||
onMouseUp={e => this.handlePreviewMouseUp(e)}
|
||||
onMouseDown={e => this.handlePreviewMouseDown(e)}
|
||||
onCheckboxClick={e => this.handleCheckboxClick(e)}
|
||||
showCopyNotification={config.ui.showCopyNotification}
|
||||
storagePath={storage.path}
|
||||
noteKey={noteKey}
|
||||
@@ -343,7 +421,8 @@ class MarkdownEditor extends React.Component {
|
||||
lineThroughCheckbox={config.preview.lineThroughCheckbox}
|
||||
getNote={getNote}
|
||||
export={config.export}
|
||||
onDrop={(e) => this.handleDropImage(e)}
|
||||
onDrop={e => this.handleDropImage(e)}
|
||||
RTL={RTL}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
import { connect } from 'react-redux'
|
||||
import Markdown from 'browser/lib/markdown'
|
||||
import _ from 'lodash'
|
||||
import CodeMirror from 'codemirror'
|
||||
@@ -8,53 +9,84 @@ import consts from 'browser/lib/consts'
|
||||
import Raphael from 'raphael'
|
||||
import flowchart from 'flowchart'
|
||||
import mermaidRender from './render/MermaidRender'
|
||||
import SequenceDiagram from 'js-sequence-diagrams'
|
||||
import SequenceDiagram from '@rokt33r/js-sequence-diagrams'
|
||||
import Chart from 'chart.js'
|
||||
import eventEmitter from 'browser/main/lib/eventEmitter'
|
||||
import config from 'browser/main/lib/ConfigManager'
|
||||
import htmlTextHelper from 'browser/lib/htmlTextHelper'
|
||||
import convertModeName from 'browser/lib/convertModeName'
|
||||
import copy from 'copy-to-clipboard'
|
||||
import mdurl from 'mdurl'
|
||||
import exportNote from 'browser/main/lib/dataApi/exportNote'
|
||||
import formatMarkdown from 'browser/main/lib/dataApi/formatMarkdown'
|
||||
import formatHTML, { CSS_FILES, buildStyle, getCodeThemeLink, getStyleParams } from 'browser/main/lib/dataApi/formatHTML'
|
||||
import formatHTML, {
|
||||
CSS_FILES,
|
||||
buildStyle,
|
||||
getCodeThemeLink,
|
||||
getStyleParams
|
||||
} from 'browser/main/lib/dataApi/formatHTML'
|
||||
import formatPDF from 'browser/main/lib/dataApi/formatPDF'
|
||||
import { escapeHtmlCharacters } from 'browser/lib/utils'
|
||||
import yaml from 'js-yaml'
|
||||
import context from 'browser/lib/context'
|
||||
import i18n from 'browser/lib/i18n'
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import uri2path from 'file-uri-to-path'
|
||||
import { remote, shell } from 'electron'
|
||||
import attachmentManagement from '../main/lib/dataApi/attachmentManagement'
|
||||
import filenamify from 'filenamify'
|
||||
import { render } from 'react-dom'
|
||||
import Carousel from 'react-image-carousel'
|
||||
import { push } from 'connected-react-router'
|
||||
import ConfigManager from '../main/lib/ConfigManager'
|
||||
import uiThemes from 'browser/lib/ui-themes'
|
||||
import { buildMarkdownPreviewContextMenu } from 'browser/lib/contextMenuBuilder'
|
||||
|
||||
const dialog = remote.dialog
|
||||
|
||||
const scrollBarStyle = `
|
||||
::-webkit-scrollbar {
|
||||
${config.get().ui.showScrollBar ? '' : 'display: none;'}
|
||||
width: 12px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
${config.get().ui.showScrollBar ? '' : 'display: none;'}
|
||||
background-color: rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track-piece {
|
||||
background-color: inherit;
|
||||
}
|
||||
`
|
||||
const scrollBarDarkStyle = `
|
||||
::-webkit-scrollbar {
|
||||
${config.get().ui.showScrollBar ? '' : 'display: none;'}
|
||||
width: 12px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
${config.get().ui.showScrollBar ? '' : 'display: none;'}
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track-piece {
|
||||
background-color: inherit;
|
||||
}
|
||||
`
|
||||
|
||||
export default class MarkdownPreview extends React.Component {
|
||||
constructor (props) {
|
||||
// return the line number of the line that used to generate the specified element
|
||||
// return -1 if the line is not found
|
||||
function getSourceLineNumberByElement(element) {
|
||||
let isHasLineNumber = element.dataset.line !== undefined
|
||||
let parent = element
|
||||
while (!isHasLineNumber && parent.parentElement !== null) {
|
||||
parent = parent.parentElement
|
||||
isHasLineNumber = parent.dataset.line !== undefined
|
||||
}
|
||||
return parent.dataset.line !== undefined ? parseInt(parent.dataset.line) : -1
|
||||
}
|
||||
|
||||
class MarkdownPreview extends React.Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.contextMenuHandler = e => this.handleContextMenu(e)
|
||||
@@ -69,14 +101,16 @@ export default class MarkdownPreview extends React.Component {
|
||||
this.saveAsTextHandler = () => this.handleSaveAsText()
|
||||
this.saveAsMdHandler = () => this.handleSaveAsMd()
|
||||
this.saveAsHtmlHandler = () => this.handleSaveAsHtml()
|
||||
this.saveAsPdfHandler = () => this.handleSaveAsPdf()
|
||||
this.printHandler = () => this.handlePrint()
|
||||
this.resizeHandler = _.throttle(this.handleResize.bind(this), 100)
|
||||
|
||||
this.linkClickHandler = this.handleLinkClick.bind(this)
|
||||
this.initMarkdown = this.initMarkdown.bind(this)
|
||||
this.initMarkdown()
|
||||
}
|
||||
|
||||
initMarkdown () {
|
||||
initMarkdown() {
|
||||
const { smartQuotes, sanitize, breaks } = this.props
|
||||
this.markdown = new Markdown({
|
||||
typographer: smartQuotes,
|
||||
@@ -85,64 +119,61 @@ export default class MarkdownPreview extends React.Component {
|
||||
})
|
||||
}
|
||||
|
||||
handleCheckboxClick (e) {
|
||||
handleCheckboxClick(e) {
|
||||
this.props.onCheckboxClick(e)
|
||||
}
|
||||
|
||||
handleScroll (e) {
|
||||
handleScroll(e) {
|
||||
if (this.props.onScroll) {
|
||||
this.props.onScroll(e)
|
||||
}
|
||||
}
|
||||
|
||||
handleContextMenu (event) {
|
||||
// If a contextMenu handler was passed to us, use it instead of the self-defined one -> return
|
||||
if (_.isFunction(this.props.onContextMenu)) {
|
||||
handleContextMenu(event) {
|
||||
const menu = buildMarkdownPreviewContextMenu(this, event)
|
||||
const switchPreview = ConfigManager.get().editor.switchPreview
|
||||
if (menu != null && switchPreview !== 'RIGHTCLICK') {
|
||||
menu.popup(remote.getCurrentWindow())
|
||||
} else if (_.isFunction(this.props.onContextMenu)) {
|
||||
this.props.onContextMenu(event)
|
||||
return
|
||||
}
|
||||
// No contextMenu was passed to us -> execute our own link-opener
|
||||
if (event.target.tagName.toLowerCase() === 'a') {
|
||||
const href = event.target.href
|
||||
const isLocalFile = href.startsWith('file:')
|
||||
if (isLocalFile) {
|
||||
const absPath = uri2path(href)
|
||||
try {
|
||||
if (fs.lstatSync(absPath).isFile()) {
|
||||
context.popup([
|
||||
{
|
||||
label: i18n.__('Show in explorer'),
|
||||
click: (e) => shell.showItemInFolder(absPath)
|
||||
}
|
||||
])
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('Error while evaluating if the file is locally available', e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleDoubleClick (e) {
|
||||
handleDoubleClick(e) {
|
||||
if (this.props.onDoubleClick != null) this.props.onDoubleClick(e)
|
||||
}
|
||||
|
||||
handleMouseDown (e) {
|
||||
handleMouseDown(e) {
|
||||
const config = ConfigManager.get()
|
||||
if (config.editor.switchPreview === 'RIGHTCLICK' && e.buttons === 2 && config.editor.type === 'SPLIT') {
|
||||
const clickElement = e.target
|
||||
const targetTag = clickElement.tagName // The direct parent HTML of where was clicked ie "BODY" or "DIV"
|
||||
const lineNumber = getSourceLineNumberByElement(clickElement) // Line location of element clicked.
|
||||
|
||||
if (
|
||||
config.editor.switchPreview === 'RIGHTCLICK' &&
|
||||
e.buttons === 2 &&
|
||||
config.editor.type === 'SPLIT'
|
||||
) {
|
||||
eventEmitter.emit('topbar:togglemodebutton', 'CODE')
|
||||
}
|
||||
if (e.target != null) {
|
||||
switch (e.target.tagName) {
|
||||
case 'A':
|
||||
case 'INPUT':
|
||||
return null
|
||||
if (e.ctrlKey) {
|
||||
if (config.editor.type === 'SPLIT') {
|
||||
if (lineNumber !== -1) {
|
||||
eventEmitter.emit('line:jump', lineNumber)
|
||||
}
|
||||
} else {
|
||||
if (lineNumber !== -1) {
|
||||
eventEmitter.emit('editor:focus')
|
||||
eventEmitter.emit('line:jump', lineNumber)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.props.onMouseDown != null) this.props.onMouseDown(e)
|
||||
|
||||
if (this.props.onMouseDown != null && targetTag === 'BODY')
|
||||
this.props.onMouseDown(e)
|
||||
}
|
||||
|
||||
handleMouseUp (e) {
|
||||
handleMouseUp(e) {
|
||||
if (!this.props.onMouseUp) return
|
||||
if (e.target != null && e.target.tagName === 'A') {
|
||||
return null
|
||||
@@ -150,23 +181,27 @@ export default class MarkdownPreview extends React.Component {
|
||||
if (this.props.onMouseUp != null) this.props.onMouseUp(e)
|
||||
}
|
||||
|
||||
handleSaveAsText () {
|
||||
handleSaveAsText() {
|
||||
this.exportAsDocument('txt')
|
||||
}
|
||||
|
||||
handleSaveAsMd () {
|
||||
handleSaveAsMd() {
|
||||
this.exportAsDocument('md', formatMarkdown(this.props))
|
||||
}
|
||||
|
||||
handleSaveAsHtml () {
|
||||
handleSaveAsHtml() {
|
||||
this.exportAsDocument('html', formatHTML(this.props))
|
||||
}
|
||||
|
||||
handlePrint () {
|
||||
handleSaveAsPdf() {
|
||||
this.exportAsDocument('pdf', formatPDF(this.props))
|
||||
}
|
||||
|
||||
handlePrint() {
|
||||
this.refs.root.contentWindow.print()
|
||||
}
|
||||
|
||||
exportAsDocument (fileType, contentFormatter) {
|
||||
exportAsDocument(fileType, contentFormatter) {
|
||||
const note = this.props.getNote()
|
||||
|
||||
const options = {
|
||||
@@ -185,7 +220,8 @@ export default class MarkdownPreview extends React.Component {
|
||||
.then(res => {
|
||||
dialog.showMessageBox(remote.getCurrentWindow(), {
|
||||
type: 'info',
|
||||
message: `Exported to ${filename}`
|
||||
message: `Exported to ${filename}`,
|
||||
buttons: [i18n.__('Ok')]
|
||||
})
|
||||
})
|
||||
.catch(err => {
|
||||
@@ -199,7 +235,7 @@ export default class MarkdownPreview extends React.Component {
|
||||
})
|
||||
}
|
||||
|
||||
fixDecodedURI (node) {
|
||||
fixDecodedURI(node) {
|
||||
if (
|
||||
node &&
|
||||
node.children.length === 1 &&
|
||||
@@ -211,21 +247,41 @@ export default class MarkdownPreview extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
getScrollBarStyle () {
|
||||
const { theme } = this.props
|
||||
|
||||
switch (theme) {
|
||||
case 'dark':
|
||||
case 'solarized-dark':
|
||||
case 'monokai':
|
||||
case 'dracula':
|
||||
return scrollBarDarkStyle
|
||||
default:
|
||||
return scrollBarStyle
|
||||
/**
|
||||
* @description Convert special characters between three ```
|
||||
* @param {string[]} splitWithCodeTag Array of HTML strings separated by three ```
|
||||
* @returns {string} HTML in which special characters between three ``` have been converted
|
||||
*/
|
||||
escapeHtmlCharactersInCodeTag(splitWithCodeTag) {
|
||||
for (let index = 0; index < splitWithCodeTag.length; index++) {
|
||||
const codeTagRequired =
|
||||
splitWithCodeTag[index] !== '```' && index < splitWithCodeTag.length - 1
|
||||
if (codeTagRequired) {
|
||||
splitWithCodeTag.splice(index + 1, 0, '```')
|
||||
}
|
||||
}
|
||||
let inCodeTag = false
|
||||
let result = ''
|
||||
for (let content of splitWithCodeTag) {
|
||||
if (content === '```') {
|
||||
inCodeTag = !inCodeTag
|
||||
} else if (inCodeTag) {
|
||||
content = escapeHtmlCharacters(content)
|
||||
}
|
||||
result += content
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
getScrollBarStyle() {
|
||||
const { theme } = this.props
|
||||
|
||||
return uiThemes.some(item => item.name === theme && item.isDark)
|
||||
? scrollBarDarkStyle
|
||||
: scrollBarStyle
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { onDrop } = this.props
|
||||
|
||||
this.refs.root.setAttribute('sandbox', 'allow-scripts')
|
||||
@@ -275,13 +331,15 @@ export default class MarkdownPreview extends React.Component {
|
||||
'scroll',
|
||||
this.scrollHandler
|
||||
)
|
||||
this.refs.root.contentWindow.addEventListener('resize', this.resizeHandler)
|
||||
eventEmitter.on('export:save-text', this.saveAsTextHandler)
|
||||
eventEmitter.on('export:save-md', this.saveAsMdHandler)
|
||||
eventEmitter.on('export:save-html', this.saveAsHtmlHandler)
|
||||
eventEmitter.on('export:save-pdf', this.saveAsPdfHandler)
|
||||
eventEmitter.on('print', this.printHandler)
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
componentWillUnmount() {
|
||||
const { onDrop } = this.props
|
||||
|
||||
this.refs.root.contentWindow.document.body.removeEventListener(
|
||||
@@ -312,23 +370,31 @@ export default class MarkdownPreview extends React.Component {
|
||||
'scroll',
|
||||
this.scrollHandler
|
||||
)
|
||||
this.refs.root.contentWindow.removeEventListener(
|
||||
'resize',
|
||||
this.resizeHandler
|
||||
)
|
||||
eventEmitter.off('export:save-text', this.saveAsTextHandler)
|
||||
eventEmitter.off('export:save-md', this.saveAsMdHandler)
|
||||
eventEmitter.off('export:save-html', this.saveAsHtmlHandler)
|
||||
eventEmitter.off('export:save-pdf', this.saveAsPdfHandler)
|
||||
eventEmitter.off('print', this.printHandler)
|
||||
}
|
||||
|
||||
componentDidUpdate (prevProps) {
|
||||
if (prevProps.value !== this.props.value) this.rewriteIframe()
|
||||
componentDidUpdate(prevProps) {
|
||||
// actual rewriteIframe function should be called only once
|
||||
let needsRewriteIframe = false
|
||||
if (prevProps.value !== this.props.value) needsRewriteIframe = true
|
||||
if (
|
||||
prevProps.smartQuotes !== this.props.smartQuotes ||
|
||||
prevProps.sanitize !== this.props.sanitize ||
|
||||
prevProps.mermaidHTMLLabel !== this.props.mermaidHTMLLabel ||
|
||||
prevProps.smartArrows !== this.props.smartArrows ||
|
||||
prevProps.breaks !== this.props.breaks ||
|
||||
prevProps.lineThroughCheckbox !== this.props.lineThroughCheckbox
|
||||
) {
|
||||
this.initMarkdown()
|
||||
this.rewriteIframe()
|
||||
needsRewriteIframe = true
|
||||
}
|
||||
if (
|
||||
prevProps.fontFamily !== this.props.fontFamily ||
|
||||
@@ -340,14 +406,24 @@ export default class MarkdownPreview extends React.Component {
|
||||
prevProps.theme !== this.props.theme ||
|
||||
prevProps.scrollPastEnd !== this.props.scrollPastEnd ||
|
||||
prevProps.allowCustomCSS !== this.props.allowCustomCSS ||
|
||||
prevProps.customCSS !== this.props.customCSS
|
||||
prevProps.customCSS !== this.props.customCSS ||
|
||||
prevProps.RTL !== this.props.RTL
|
||||
) {
|
||||
this.applyStyle()
|
||||
needsRewriteIframe = true
|
||||
}
|
||||
|
||||
if (needsRewriteIframe) {
|
||||
this.rewriteIframe()
|
||||
}
|
||||
|
||||
// Should scroll to top after selecting another note
|
||||
if (prevProps.noteKey !== this.props.noteKey) {
|
||||
this.scrollTo(0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
applyStyle () {
|
||||
applyStyle() {
|
||||
const {
|
||||
fontFamily,
|
||||
fontSize,
|
||||
@@ -357,10 +433,13 @@ export default class MarkdownPreview extends React.Component {
|
||||
scrollPastEnd,
|
||||
theme,
|
||||
allowCustomCSS,
|
||||
customCSS
|
||||
customCSS,
|
||||
RTL
|
||||
} = getStyleParams(this.props)
|
||||
|
||||
this.getWindow().document.getElementById('codeTheme').href = getCodeThemeLink(codeBlockTheme)
|
||||
this.getWindow().document.getElementById(
|
||||
'codeTheme'
|
||||
).href = getCodeThemeLink(codeBlockTheme)
|
||||
|
||||
this.getWindow().document.getElementById('style').innerHTML = buildStyle(
|
||||
fontFamily,
|
||||
@@ -370,11 +449,12 @@ export default class MarkdownPreview extends React.Component {
|
||||
scrollPastEnd,
|
||||
theme,
|
||||
allowCustomCSS,
|
||||
customCSS
|
||||
customCSS,
|
||||
RTL
|
||||
)
|
||||
}
|
||||
|
||||
rewriteIframe () {
|
||||
rewriteIframe() {
|
||||
_.forEach(
|
||||
this.refs.root.contentWindow.document.querySelectorAll(
|
||||
'input[type="checkbox"]'
|
||||
@@ -396,11 +476,17 @@ export default class MarkdownPreview extends React.Component {
|
||||
indentSize,
|
||||
showCopyNotification,
|
||||
storagePath,
|
||||
noteKey
|
||||
noteKey,
|
||||
sanitize,
|
||||
mermaidHTMLLabel
|
||||
} = this.props
|
||||
let { value, codeBlockTheme } = this.props
|
||||
|
||||
this.refs.root.contentWindow.document.body.setAttribute('data-theme', theme)
|
||||
if (sanitize === 'NONE') {
|
||||
const splitWithCodeTag = value.split('```')
|
||||
value = this.escapeHtmlCharactersInCodeTag(splitWithCodeTag)
|
||||
}
|
||||
const renderedHTML = this.markdown.render(value)
|
||||
attachmentManagement.migrateAttachments(value, storagePath, noteKey)
|
||||
this.refs.root.contentWindow.document.body.innerHTML = attachmentManagement.fixLocalURLS(
|
||||
@@ -424,9 +510,11 @@ export default class MarkdownPreview extends React.Component {
|
||||
}
|
||||
)
|
||||
|
||||
codeBlockTheme = consts.THEMES.some(_theme => _theme === codeBlockTheme)
|
||||
? codeBlockTheme
|
||||
: 'default'
|
||||
codeBlockTheme = consts.THEMES.find(theme => theme.name === codeBlockTheme)
|
||||
|
||||
const codeBlockThemeClassName = codeBlockTheme
|
||||
? codeBlockTheme.className
|
||||
: 'cm-s-default'
|
||||
|
||||
_.forEach(
|
||||
this.refs.root.contentWindow.document.querySelectorAll('.code code'),
|
||||
@@ -439,6 +527,8 @@ export default class MarkdownPreview extends React.Component {
|
||||
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 => {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
copy(content)
|
||||
if (showCopyNotification) {
|
||||
this.notify('Saved to Clipboard!', {
|
||||
@@ -447,27 +537,20 @@ export default class MarkdownPreview extends React.Component {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
el.parentNode.appendChild(copyIcon)
|
||||
el.innerHTML = ''
|
||||
if (codeBlockTheme.indexOf('solarized') === 0) {
|
||||
const [refThema, color] = codeBlockTheme.split(' ')
|
||||
el.parentNode.className += ` cm-s-${refThema} cm-s-${color}`
|
||||
} else {
|
||||
el.parentNode.className += ` cm-s-${codeBlockTheme}`
|
||||
}
|
||||
el.parentNode.className += ` ${codeBlockThemeClassName}`
|
||||
|
||||
CodeMirror.runMode(content, syntax.mime, el, {
|
||||
tabSize: indentSize
|
||||
})
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
const opts = {}
|
||||
// if (this.props.theme === 'dark') {
|
||||
// opts['font-color'] = '#DDD'
|
||||
// opts['line-color'] = '#DDD'
|
||||
// opts['element-color'] = '#DDD'
|
||||
// opts['fill'] = '#3A404C'
|
||||
// }
|
||||
|
||||
_.forEach(
|
||||
this.refs.root.contentWindow.document.querySelectorAll('.flowchart'),
|
||||
el => {
|
||||
@@ -513,7 +596,10 @@ export default class MarkdownPreview extends React.Component {
|
||||
el => {
|
||||
try {
|
||||
const format = el.attributes.getNamedItem('data-format').value
|
||||
const chartConfig = format === 'yaml' ? yaml.load(el.innerHTML) : JSON.parse(el.innerHTML)
|
||||
const chartConfig =
|
||||
format === 'yaml'
|
||||
? yaml.load(el.innerHTML)
|
||||
: JSON.parse(el.innerHTML)
|
||||
el.innerHTML = ''
|
||||
|
||||
const canvas = document.createElement('canvas')
|
||||
@@ -525,6 +611,7 @@ export default class MarkdownPreview extends React.Component {
|
||||
canvas.height = height.value + 'vh'
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const chart = new Chart(canvas, chartConfig)
|
||||
} catch (e) {
|
||||
el.className = 'chart-error'
|
||||
@@ -535,7 +622,12 @@ export default class MarkdownPreview extends React.Component {
|
||||
_.forEach(
|
||||
this.refs.root.contentWindow.document.querySelectorAll('.mermaid'),
|
||||
el => {
|
||||
mermaidRender(el, htmlTextHelper.decodeEntities(el.innerHTML), theme)
|
||||
mermaidRender(
|
||||
el,
|
||||
htmlTextHelper.decodeEntities(el.innerHTML),
|
||||
theme,
|
||||
mermaidHTMLLabel
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
@@ -557,26 +649,133 @@ export default class MarkdownPreview extends React.Component {
|
||||
autoplay = 0
|
||||
}
|
||||
|
||||
render(
|
||||
<Carousel
|
||||
images={images}
|
||||
autoplay={autoplay}
|
||||
/>,
|
||||
el
|
||||
render(<Carousel images={images} autoplay={autoplay} />, el)
|
||||
}
|
||||
)
|
||||
|
||||
const markdownPreviewIframe = document.querySelector('.MarkdownPreview')
|
||||
const rect = markdownPreviewIframe.getBoundingClientRect()
|
||||
const config = { attributes: true, subtree: true }
|
||||
const imgObserver = new MutationObserver(mutationList => {
|
||||
for (const mu of mutationList) {
|
||||
if (mu.target.className === 'carouselContent-enter-done') {
|
||||
this.setImgOnClickEventHelper(mu.target, rect)
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const imgList = markdownPreviewIframe.contentWindow.document.body.querySelectorAll(
|
||||
'img'
|
||||
)
|
||||
for (const img of imgList) {
|
||||
const parentEl = img.parentElement
|
||||
this.setImgOnClickEventHelper(img, rect)
|
||||
imgObserver.observe(parentEl, config)
|
||||
}
|
||||
|
||||
const aList = markdownPreviewIframe.contentWindow.document.body.querySelectorAll(
|
||||
'a'
|
||||
)
|
||||
for (const a of aList) {
|
||||
a.removeEventListener('click', this.linkClickHandler)
|
||||
a.addEventListener('click', this.linkClickHandler)
|
||||
}
|
||||
}
|
||||
|
||||
setImgOnClickEventHelper(img, rect) {
|
||||
img.onclick = () => {
|
||||
const widthMagnification = document.body.clientWidth / img.width
|
||||
const heightMagnification = document.body.clientHeight / img.height
|
||||
const baseOnWidth = widthMagnification < heightMagnification
|
||||
const magnification = baseOnWidth
|
||||
? widthMagnification
|
||||
: heightMagnification
|
||||
|
||||
const zoomImgWidth = img.width * magnification
|
||||
const zoomImgHeight = img.height * magnification
|
||||
const zoomImgTop = (document.body.clientHeight - zoomImgHeight) / 2
|
||||
const zoomImgLeft = (document.body.clientWidth - zoomImgWidth) / 2
|
||||
const originalImgTop = img.y + rect.top
|
||||
const originalImgLeft = img.x + rect.left
|
||||
const originalImgRect = {
|
||||
top: `${originalImgTop}px`,
|
||||
left: `${originalImgLeft}px`,
|
||||
width: `${img.width}px`,
|
||||
height: `${img.height}px`
|
||||
}
|
||||
const zoomInImgRect = {
|
||||
top: `${baseOnWidth ? zoomImgTop : 0}px`,
|
||||
left: `${baseOnWidth ? 0 : zoomImgLeft}px`,
|
||||
width: `${zoomImgWidth}px`,
|
||||
height: `${zoomImgHeight}px`
|
||||
}
|
||||
const animationSpeed = 300
|
||||
|
||||
const zoomImg = document.createElement('img')
|
||||
zoomImg.src = img.src
|
||||
zoomImg.style = `
|
||||
position: absolute;
|
||||
top: ${baseOnWidth ? zoomImgTop : 0}px;
|
||||
left: ${baseOnWidth ? 0 : zoomImgLeft}px;
|
||||
width: ${zoomImgWidth};
|
||||
height: ${zoomImgHeight}px;
|
||||
`
|
||||
zoomImg.animate([originalImgRect, zoomInImgRect], animationSpeed)
|
||||
|
||||
const overlay = document.createElement('div')
|
||||
overlay.style = `
|
||||
background-color: rgba(0,0,0,0.5);
|
||||
cursor: zoom-out;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: ${document.body.clientHeight}px;
|
||||
z-index: 100;
|
||||
`
|
||||
overlay.onclick = () => {
|
||||
zoomImg.style = `
|
||||
position: absolute;
|
||||
top: ${originalImgTop}px;
|
||||
left: ${originalImgLeft}px;
|
||||
width: ${img.width}px;
|
||||
height: ${img.height}px;
|
||||
`
|
||||
const zoomOutImgAnimation = zoomImg.animate(
|
||||
[zoomInImgRect, originalImgRect],
|
||||
animationSpeed
|
||||
)
|
||||
zoomOutImgAnimation.onfinish = () => overlay.remove()
|
||||
}
|
||||
|
||||
overlay.appendChild(zoomImg)
|
||||
document.body.appendChild(overlay)
|
||||
}
|
||||
}
|
||||
|
||||
handleResize() {
|
||||
_.forEach(
|
||||
this.refs.root.contentWindow.document.querySelectorAll('svg[ratio]'),
|
||||
el => {
|
||||
el.setAttribute('height', el.clientWidth / el.getAttribute('ratio'))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
focus () {
|
||||
focus() {
|
||||
this.refs.root.focus()
|
||||
}
|
||||
|
||||
getWindow () {
|
||||
getWindow() {
|
||||
return this.refs.root.contentWindow
|
||||
}
|
||||
|
||||
scrollTo (targetRow) {
|
||||
/**
|
||||
* @public
|
||||
* @param {Number} targetRow
|
||||
*/
|
||||
scrollToRow(targetRow) {
|
||||
const blocks = this.getWindow().document.querySelectorAll(
|
||||
'body>[data-line]'
|
||||
)
|
||||
@@ -586,18 +785,27 @@ export default class MarkdownPreview extends React.Component {
|
||||
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)
|
||||
block != null && this.scrollTo(0, block.offsetTop)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
preventImageDroppedHandler (e) {
|
||||
/**
|
||||
* `document.body.scrollTo`
|
||||
* @param {Number} x
|
||||
* @param {Number} y
|
||||
*/
|
||||
scrollTo(x, y) {
|
||||
this.getWindow().document.body.scrollTo(x, y)
|
||||
}
|
||||
|
||||
preventImageDroppedHandler(e) {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
}
|
||||
|
||||
notify (title, options) {
|
||||
notify(title, options) {
|
||||
if (global.process.platform === 'win32') {
|
||||
options.icon = path.join(
|
||||
'file://',
|
||||
@@ -608,24 +816,35 @@ export default class MarkdownPreview extends React.Component {
|
||||
return new window.Notification(title, options)
|
||||
}
|
||||
|
||||
handleLinkClick (e) {
|
||||
handleLinkClick(e) {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
|
||||
const href = e.target.href
|
||||
const linkHash = href.split('/').pop()
|
||||
const rawHref = e.target.getAttribute('href')
|
||||
const { dispatch } = this.props
|
||||
if (!rawHref) return // not checked href because parser will create file://... string for [empty link]()
|
||||
|
||||
const regexNoteInternalLink = /main.html#(.+)/
|
||||
if (regexNoteInternalLink.test(linkHash)) {
|
||||
const targetId = mdurl.encode(linkHash.match(regexNoteInternalLink)[1])
|
||||
const targetElement = this.refs.root.contentWindow.document.getElementById(
|
||||
targetId
|
||||
)
|
||||
const parser = document.createElement('a')
|
||||
parser.href = rawHref
|
||||
const isStartWithHash = rawHref[0] === '#'
|
||||
const { href, hash } = parser
|
||||
|
||||
if (targetElement != null) {
|
||||
this.getWindow().scrollTo(0, targetElement.offsetTop)
|
||||
const linkHash = hash === '' ? rawHref : hash // needed because we're having special link formats that are removed by parser e.g. :line:10
|
||||
|
||||
const extractIdRegex = /file:\/\/.*main.?\w*.html#/ // file://path/to/main(.development.)html
|
||||
const regexNoteInternalLink = new RegExp(`${extractIdRegex.source}(.+)`)
|
||||
if (isStartWithHash || regexNoteInternalLink.test(rawHref)) {
|
||||
const posOfHash = linkHash.indexOf('#')
|
||||
if (posOfHash > -1) {
|
||||
const extractedId = linkHash.slice(posOfHash + 1)
|
||||
const targetId = mdurl.encode(extractedId)
|
||||
const targetElement = this.getWindow().document.getElementById(targetId)
|
||||
|
||||
if (targetElement != null) {
|
||||
this.scrollTo(0, targetElement.offsetTop)
|
||||
}
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// this will match the new uuid v4 hash and the old hash
|
||||
@@ -656,11 +875,29 @@ export default class MarkdownPreview extends React.Component {
|
||||
return
|
||||
}
|
||||
|
||||
const regexIsTagLink = /^:tag:([\w]+)$/
|
||||
if (regexIsTagLink.test(rawHref)) {
|
||||
const tag = rawHref.match(regexIsTagLink)[1]
|
||||
dispatch(push(`/tags/${encodeURIComponent(tag)}`))
|
||||
return
|
||||
}
|
||||
|
||||
// other case
|
||||
shell.openExternal(href)
|
||||
this.openExternal(href)
|
||||
}
|
||||
|
||||
render () {
|
||||
openExternal(href) {
|
||||
try {
|
||||
const success =
|
||||
shell.openExternal(href) || shell.openExternal(decodeURI(href))
|
||||
if (!success) console.error('failed to open url ' + href)
|
||||
} catch (e) {
|
||||
// URI Error threw from decodeURI
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { className, style, tabIndex } = this.props
|
||||
return (
|
||||
<iframe
|
||||
@@ -689,3 +926,10 @@ MarkdownPreview.propTypes = {
|
||||
smartArrows: PropTypes.bool,
|
||||
breaks: PropTypes.bool
|
||||
}
|
||||
|
||||
export default connect(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
{ forwardRef: true }
|
||||
)(MarkdownPreview)
|
||||
|
||||
@@ -8,7 +8,7 @@ import styles from './MarkdownSplitEditor.styl'
|
||||
import CSSModules from 'browser/lib/CSSModules'
|
||||
|
||||
class MarkdownSplitEditor extends React.Component {
|
||||
constructor (props) {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.value = props.value
|
||||
this.focus = () => this.refs.code.focus()
|
||||
@@ -16,23 +16,27 @@ class MarkdownSplitEditor extends React.Component {
|
||||
this.userScroll = true
|
||||
this.state = {
|
||||
isSliderFocused: false,
|
||||
codeEditorWidthInPercent: 50
|
||||
codeEditorWidthInPercent: 50,
|
||||
codeEditorHeightInPercent: 50
|
||||
}
|
||||
}
|
||||
|
||||
setValue (value) {
|
||||
setValue(value) {
|
||||
this.refs.code.setValue(value)
|
||||
}
|
||||
|
||||
handleOnChange (e) {
|
||||
handleOnChange(e) {
|
||||
this.value = this.refs.code.value
|
||||
this.props.onChange(e)
|
||||
}
|
||||
|
||||
handleScroll (e) {
|
||||
handleScroll(e) {
|
||||
if (!this.props.config.preview.scrollSync) return
|
||||
|
||||
const previewDoc = _.get(this, 'refs.preview.refs.root.contentWindow.document')
|
||||
const previewDoc = _.get(
|
||||
this,
|
||||
'refs.preview.refs.root.contentWindow.document'
|
||||
)
|
||||
const codeDoc = _.get(this, 'refs.code.editor.doc')
|
||||
let srcTop, srcHeight, targetTop, targetHeight
|
||||
|
||||
@@ -49,7 +53,7 @@ class MarkdownSplitEditor extends React.Component {
|
||||
targetHeight = _.get(codeDoc, 'height')
|
||||
}
|
||||
|
||||
const distance = (targetHeight * srcTop / srcHeight) - targetTop
|
||||
const distance = (targetHeight * srcTop) / srcHeight - targetTop
|
||||
const framerate = 1000 / 60
|
||||
const frames = 20
|
||||
const refractory = frames * framerate
|
||||
@@ -60,111 +64,207 @@ class MarkdownSplitEditor extends React.Component {
|
||||
let scrollPos, time
|
||||
const timer = setInterval(() => {
|
||||
time = frame / frames
|
||||
scrollPos = time < 0.5
|
||||
? 2 * time * time // ease in
|
||||
: -1 + (4 - 2 * time) * time // ease out
|
||||
if (e.doc) _.set(previewDoc, 'body.scrollTop', targetTop + scrollPos * distance)
|
||||
else _.get(this, 'refs.code.editor').scrollTo(0, targetTop + scrollPos * distance)
|
||||
scrollPos =
|
||||
time < 0.5
|
||||
? 2 * time * time // ease in
|
||||
: -1 + (4 - 2 * time) * time // ease out
|
||||
if (e.doc)
|
||||
_.set(previewDoc, 'body.scrollTop', targetTop + scrollPos * distance)
|
||||
else
|
||||
_.get(this, 'refs.code.editor').scrollTo(
|
||||
0,
|
||||
targetTop + scrollPos * distance
|
||||
)
|
||||
if (frame >= frames) {
|
||||
clearInterval(timer)
|
||||
setTimeout(() => { this.userScroll = true }, refractory)
|
||||
setTimeout(() => {
|
||||
this.userScroll = true
|
||||
}, refractory)
|
||||
}
|
||||
frame++
|
||||
}, framerate)
|
||||
}
|
||||
}
|
||||
|
||||
handleCheckboxClick (e) {
|
||||
handleCheckboxClick(e) {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
const idMatch = /checkbox-([0-9]+)/
|
||||
const checkedMatch = /^\s*[\+\-\*] \[x\]/i
|
||||
const uncheckedMatch = /^\s*[\+\-\*] \[ \]/
|
||||
const checkReplace = /\[x\]/i
|
||||
const uncheckReplace = /\[ \]/
|
||||
const checkedMatch = /^(\s*>?)*\s*[+\-*] \[x]/i
|
||||
const uncheckedMatch = /^(\s*>?)*\s*[+\-*] \[ ]/
|
||||
const checkReplace = /\[x]/i
|
||||
const uncheckReplace = /\[ ]/
|
||||
if (idMatch.test(e.target.getAttribute('id'))) {
|
||||
const lineIndex = parseInt(e.target.getAttribute('id').match(idMatch)[1], 10) - 1
|
||||
const lines = this.refs.code.value
|
||||
.split('\n')
|
||||
const lineIndex =
|
||||
parseInt(e.target.getAttribute('id').match(idMatch)[1], 10) - 1
|
||||
const lines = this.refs.code.value.split('\n')
|
||||
|
||||
const targetLine = lines[lineIndex]
|
||||
let newLine = targetLine
|
||||
|
||||
if (targetLine.match(checkedMatch)) {
|
||||
lines[lineIndex] = targetLine.replace(checkReplace, '[ ]')
|
||||
newLine = targetLine.replace(checkReplace, '[ ]')
|
||||
}
|
||||
if (targetLine.match(uncheckedMatch)) {
|
||||
lines[lineIndex] = targetLine.replace(uncheckReplace, '[x]')
|
||||
newLine = targetLine.replace(uncheckReplace, '[x]')
|
||||
}
|
||||
this.refs.code.setValue(lines.join('\n'))
|
||||
this.refs.code.setLineContent(lineIndex, newLine)
|
||||
}
|
||||
}
|
||||
|
||||
handleMouseMove (e) {
|
||||
handleMouseMove(e) {
|
||||
if (this.state.isSliderFocused) {
|
||||
const rootRect = this.refs.root.getBoundingClientRect()
|
||||
const rootWidth = rootRect.width
|
||||
const offset = rootRect.left
|
||||
let newCodeEditorWidthInPercent = (e.pageX - offset) / rootWidth * 100
|
||||
if (this.props.isStacking) {
|
||||
const rootHeight = rootRect.height
|
||||
const offset = rootRect.top
|
||||
let newCodeEditorHeightInPercent =
|
||||
((e.pageY - offset) / rootHeight) * 100
|
||||
|
||||
// limit minSize to 10%, maxSize to 90%
|
||||
if (newCodeEditorWidthInPercent <= 10) {
|
||||
newCodeEditorWidthInPercent = 10
|
||||
// limit minSize to 10%, maxSize to 90%
|
||||
if (newCodeEditorHeightInPercent <= 10) {
|
||||
newCodeEditorHeightInPercent = 10
|
||||
}
|
||||
|
||||
if (newCodeEditorHeightInPercent >= 90) {
|
||||
newCodeEditorHeightInPercent = 90
|
||||
}
|
||||
|
||||
this.setState({
|
||||
codeEditorHeightInPercent: newCodeEditorHeightInPercent
|
||||
})
|
||||
} else {
|
||||
const rootWidth = rootRect.width
|
||||
const offset = rootRect.left
|
||||
let newCodeEditorWidthInPercent = ((e.pageX - offset) / rootWidth) * 100
|
||||
|
||||
// limit minSize to 10%, maxSize to 90%
|
||||
if (newCodeEditorWidthInPercent <= 10) {
|
||||
newCodeEditorWidthInPercent = 10
|
||||
}
|
||||
|
||||
if (newCodeEditorWidthInPercent >= 90) {
|
||||
newCodeEditorWidthInPercent = 90
|
||||
}
|
||||
|
||||
this.setState({
|
||||
codeEditorWidthInPercent: newCodeEditorWidthInPercent
|
||||
})
|
||||
}
|
||||
|
||||
if (newCodeEditorWidthInPercent >= 90) {
|
||||
newCodeEditorWidthInPercent = 90
|
||||
}
|
||||
|
||||
this.setState({
|
||||
codeEditorWidthInPercent: newCodeEditorWidthInPercent
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
handleMouseUp (e) {
|
||||
handleMouseUp(e) {
|
||||
e.preventDefault()
|
||||
this.setState({
|
||||
isSliderFocused: false
|
||||
})
|
||||
}
|
||||
|
||||
handleMouseDown (e) {
|
||||
handleMouseDown(e) {
|
||||
e.preventDefault()
|
||||
this.setState({
|
||||
isSliderFocused: true
|
||||
})
|
||||
}
|
||||
|
||||
render () {
|
||||
const {config, value, storageKey, noteKey, linesHighlighted, getNote} = this.props
|
||||
const storage = findStorage(storageKey)
|
||||
render() {
|
||||
const {
|
||||
config,
|
||||
value,
|
||||
storageKey,
|
||||
noteKey,
|
||||
linesHighlighted,
|
||||
getNote,
|
||||
isStacking,
|
||||
RTL
|
||||
} = this.props
|
||||
let storage
|
||||
try {
|
||||
storage = findStorage(storageKey)
|
||||
} catch (e) {
|
||||
return <div />
|
||||
}
|
||||
|
||||
let editorStyle = {}
|
||||
let previewStyle = {}
|
||||
let sliderStyle = {}
|
||||
|
||||
let editorFontSize = parseInt(config.editor.fontSize, 10)
|
||||
if (!(editorFontSize > 0 && editorFontSize < 101)) editorFontSize = 14
|
||||
editorStyle.fontSize = editorFontSize
|
||||
|
||||
let editorIndentSize = parseInt(config.editor.indentSize, 10)
|
||||
if (!(editorFontSize > 0 && editorFontSize < 132)) editorIndentSize = 4
|
||||
const previewStyle = {}
|
||||
previewStyle.width = (100 - this.state.codeEditorWidthInPercent) + '%'
|
||||
if (this.props.ignorePreviewPointerEvents || this.state.isSliderFocused) previewStyle.pointerEvents = 'none'
|
||||
if (!(editorStyle.fontSize > 0 && editorStyle.fontSize < 132))
|
||||
editorIndentSize = 4
|
||||
editorStyle.indentSize = editorIndentSize
|
||||
|
||||
editorStyle = Object.assign(
|
||||
editorStyle,
|
||||
isStacking
|
||||
? {
|
||||
width: '100%',
|
||||
height: `${this.state.codeEditorHeightInPercent}%`
|
||||
}
|
||||
: {
|
||||
width: `${this.state.codeEditorWidthInPercent}%`,
|
||||
height: '100%'
|
||||
}
|
||||
)
|
||||
|
||||
previewStyle = Object.assign(
|
||||
previewStyle,
|
||||
isStacking
|
||||
? {
|
||||
width: '100%',
|
||||
height: `${100 - this.state.codeEditorHeightInPercent}%`
|
||||
}
|
||||
: {
|
||||
width: `${100 - this.state.codeEditorWidthInPercent}%`,
|
||||
height: '100%'
|
||||
}
|
||||
)
|
||||
|
||||
sliderStyle = Object.assign(
|
||||
sliderStyle,
|
||||
isStacking
|
||||
? {
|
||||
left: 0,
|
||||
top: `${this.state.codeEditorHeightInPercent}%`
|
||||
}
|
||||
: {
|
||||
left: `${this.state.codeEditorWidthInPercent}%`,
|
||||
top: 0
|
||||
}
|
||||
)
|
||||
|
||||
if (this.props.ignorePreviewPointerEvents || this.state.isSliderFocused)
|
||||
previewStyle.pointerEvents = 'none'
|
||||
|
||||
return (
|
||||
<div styleName='root' ref='root'
|
||||
<div
|
||||
styleName='root'
|
||||
ref='root'
|
||||
onMouseMove={e => this.handleMouseMove(e)}
|
||||
onMouseUp={e => this.handleMouseUp(e)}>
|
||||
onMouseUp={e => this.handleMouseUp(e)}
|
||||
>
|
||||
<CodeEditor
|
||||
styleName='codeEditor'
|
||||
ref='code'
|
||||
width={this.state.codeEditorWidthInPercent + '%'}
|
||||
width={editorStyle.width}
|
||||
height={editorStyle.height}
|
||||
mode='Boost Flavored Markdown'
|
||||
value={value}
|
||||
theme={config.editor.theme}
|
||||
keyMap={config.editor.keyMap}
|
||||
fontFamily={config.editor.fontFamily}
|
||||
fontSize={editorFontSize}
|
||||
fontSize={editorStyle.fontSize}
|
||||
displayLineNumbers={config.editor.displayLineNumbers}
|
||||
lineWrapping
|
||||
matchingPairs={config.editor.matchingPairs}
|
||||
matchingTriples={config.editor.matchingTriples}
|
||||
explodingPairs={config.editor.explodingPairs}
|
||||
indentType={config.editor.indentType}
|
||||
indentSize={editorIndentSize}
|
||||
indentSize={editorStyle.indentSize}
|
||||
enableRulers={config.editor.enableRulers}
|
||||
rulers={config.editor.rulers}
|
||||
scrollPastEnd={config.editor.scrollPastEnd}
|
||||
@@ -173,19 +273,27 @@ class MarkdownSplitEditor extends React.Component {
|
||||
storageKey={storageKey}
|
||||
noteKey={noteKey}
|
||||
linesHighlighted={linesHighlighted}
|
||||
onChange={(e) => this.handleOnChange(e)}
|
||||
onChange={e => this.handleOnChange(e)}
|
||||
onScroll={this.handleScroll.bind(this)}
|
||||
spellCheck={config.editor.spellcheck}
|
||||
enableSmartPaste={config.editor.enableSmartPaste}
|
||||
hotkey={config.hotkey}
|
||||
switchPreview={config.editor.switchPreview}
|
||||
/>
|
||||
<div styleName='slider' style={{left: this.state.codeEditorWidthInPercent + '%'}} onMouseDown={e => this.handleMouseDown(e)} >
|
||||
enableMarkdownLint={config.editor.enableMarkdownLint}
|
||||
customMarkdownLintConfig={config.editor.customMarkdownLintConfig}
|
||||
deleteUnusedAttachments={config.editor.deleteUnusedAttachments}
|
||||
RTL={RTL}
|
||||
/>
|
||||
<div
|
||||
styleName={isStacking ? 'slider-hoz' : 'slider'}
|
||||
style={{ left: sliderStyle.left, top: sliderStyle.top }}
|
||||
onMouseDown={e => this.handleMouseDown(e)}
|
||||
>
|
||||
<div styleName='slider-hitbox' />
|
||||
</div>
|
||||
<MarkdownPreview
|
||||
ref='preview'
|
||||
style={previewStyle}
|
||||
styleName='preview'
|
||||
theme={config.ui.theme}
|
||||
keyMap={config.editor.keyMap}
|
||||
fontSize={config.preview.fontSize}
|
||||
@@ -199,10 +307,10 @@ class MarkdownSplitEditor extends React.Component {
|
||||
smartArrows={config.preview.smartArrows}
|
||||
breaks={config.preview.breaks}
|
||||
sanitize={config.preview.sanitize}
|
||||
ref='preview'
|
||||
mermaidHTMLLabel={config.preview.mermaidHTMLLabel}
|
||||
tabInde='0'
|
||||
value={value}
|
||||
onCheckboxClick={(e) => this.handleCheckboxClick(e)}
|
||||
onCheckboxClick={e => this.handleCheckboxClick(e)}
|
||||
onScroll={this.handleScroll.bind(this)}
|
||||
showCopyNotification={config.ui.showCopyNotification}
|
||||
storagePath={storage.path}
|
||||
@@ -212,7 +320,8 @@ class MarkdownSplitEditor extends React.Component {
|
||||
lineThroughCheckbox={config.preview.lineThroughCheckbox}
|
||||
getNote={getNote}
|
||||
export={config.export}
|
||||
/>
|
||||
RTL={RTL}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3,14 +3,36 @@
|
||||
height 100%
|
||||
font-size 30px
|
||||
display flex
|
||||
flex-wrap wrap
|
||||
.slider
|
||||
absolute top bottom
|
||||
top -2px
|
||||
width 0
|
||||
z-index 0
|
||||
border-left 1px solid $ui-borderColor
|
||||
.slider-hitbox
|
||||
absolute top bottom left right
|
||||
width 7px
|
||||
left -3px
|
||||
z-index 10
|
||||
cursor col-resize
|
||||
.slider-hoz
|
||||
absolute left right
|
||||
.slider-hitbox
|
||||
absolute left right
|
||||
width: 100%
|
||||
height 7px
|
||||
cursor row-resize
|
||||
|
||||
|
||||
apply-theme(theme)
|
||||
body[data-theme={theme}]
|
||||
.root
|
||||
.slider
|
||||
border-left 1px solid get-theme-var(theme, 'borderColor')
|
||||
|
||||
for theme in 'dark' 'dracula' 'solarized-dark'
|
||||
apply-theme(theme)
|
||||
|
||||
for theme in $themes
|
||||
apply-theme(theme)
|
||||
@@ -3,12 +3,10 @@ import React from 'react'
|
||||
import CSSModules from 'browser/lib/CSSModules'
|
||||
import styles from './ModalEscButton.styl'
|
||||
|
||||
const ModalEscButton = ({
|
||||
handleEscButtonClick
|
||||
}) => (
|
||||
const ModalEscButton = ({ handleEscButtonClick }) => (
|
||||
<button styleName='escButton' onClick={handleEscButtonClick}>
|
||||
<div styleName='esc-mark'>×</div>
|
||||
<div styleName='esc-text'>esc</div>
|
||||
<div>esc</div>
|
||||
</button>
|
||||
)
|
||||
|
||||
|
||||
@@ -1,24 +1,23 @@
|
||||
/**
|
||||
* @fileoverview Micro component for toggle SideNav
|
||||
*/
|
||||
* @fileoverview Micro component for toggle SideNav
|
||||
*/
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
import styles from './NavToggleButton.styl'
|
||||
import CSSModules from 'browser/lib/CSSModules'
|
||||
|
||||
/**
|
||||
* @param {boolean} isFolded
|
||||
* @param {Function} handleToggleButtonClick
|
||||
*/
|
||||
* @param {boolean} isFolded
|
||||
* @param {Function} handleToggleButtonClick
|
||||
*/
|
||||
|
||||
const NavToggleButton = ({isFolded, handleToggleButtonClick}) => (
|
||||
<button styleName='navToggle'
|
||||
onClick={(e) => handleToggleButtonClick(e)}
|
||||
>
|
||||
{isFolded
|
||||
? <i className='fa fa-angle-double-right' />
|
||||
: <i className='fa fa-angle-double-left' />
|
||||
}
|
||||
const NavToggleButton = ({ isFolded, handleToggleButtonClick }) => (
|
||||
<button styleName='navToggle' onClick={e => handleToggleButtonClick(e)}>
|
||||
{isFolded ? (
|
||||
<i className='fa fa-angle-double-right fa-2x' />
|
||||
) : (
|
||||
<i className='fa fa-angle-double-left fa-2x' />
|
||||
)}
|
||||
</button>
|
||||
)
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
border-radius 16.5px
|
||||
height 34px
|
||||
width 34px
|
||||
line-height 32px
|
||||
line-height 100%
|
||||
padding 0
|
||||
&:hover
|
||||
border: 1px solid #1EC38B;
|
||||
@@ -17,10 +17,16 @@
|
||||
body[data-theme="white"]
|
||||
navWhiteButtonColor()
|
||||
|
||||
body[data-theme="dark"]
|
||||
.navToggle
|
||||
&:hover
|
||||
background-color alpha($ui-dark-button--active-backgroundColor, 20%)
|
||||
apply-theme(theme)
|
||||
body[data-theme={theme}]
|
||||
.navToggle:hover
|
||||
background-color alpha(get-theme-var(theme, 'button--active-backgroundColor'), 20%)
|
||||
border 1px solid get-theme-var(theme, 'button--active-backgroundColor')
|
||||
transition 0.15s
|
||||
color $ui-dark-text-color
|
||||
color get-theme-var(theme, 'text-color')
|
||||
|
||||
for theme in 'dark' 'dracula' 'solarized-dark'
|
||||
apply-theme(theme)
|
||||
|
||||
for theme in $themes
|
||||
apply-theme(theme)
|
||||
@@ -3,7 +3,9 @@
|
||||
*/
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
import { isArray } from 'lodash'
|
||||
import { isArray, sortBy } from 'lodash'
|
||||
import invertColor from 'invert-color'
|
||||
import Emoji from 'react-emoji-render'
|
||||
import CSSModules from 'browser/lib/CSSModules'
|
||||
import { getTodoStatus } from 'browser/lib/getTodoStatus'
|
||||
import styles from './NoteItem.styl'
|
||||
@@ -13,29 +15,46 @@ import i18n from 'browser/lib/i18n'
|
||||
/**
|
||||
* @description Tag element component.
|
||||
* @param {string} tagName
|
||||
* @param {string} color
|
||||
* @return {React.Component}
|
||||
*/
|
||||
const TagElement = ({ tagName }) => (
|
||||
<span styleName='item-bottom-tagList-item' key={tagName}>
|
||||
#{tagName}
|
||||
</span>
|
||||
)
|
||||
const TagElement = ({ tagName, color }) => {
|
||||
const style = {}
|
||||
if (color) {
|
||||
style.backgroundColor = color
|
||||
style.color = invertColor(color, {
|
||||
black: '#222',
|
||||
white: '#f1f1f1',
|
||||
threshold: 0.3
|
||||
})
|
||||
}
|
||||
return (
|
||||
<span styleName='item-bottom-tagList-item' key={tagName} style={style}>
|
||||
#{tagName}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Tag element list component.
|
||||
* @param {Array|null} tags
|
||||
* @param {boolean} showTagsAlphabetically
|
||||
* @param {Object} coloredTags
|
||||
* @return {React.Component}
|
||||
*/
|
||||
const TagElementList = (tags, showTagsAlphabetically) => {
|
||||
const TagElementList = (tags, showTagsAlphabetically, coloredTags) => {
|
||||
if (!isArray(tags)) {
|
||||
return []
|
||||
}
|
||||
|
||||
if (showTagsAlphabetically) {
|
||||
return _.sortBy(tags).map(tag => TagElement({ tagName: tag }))
|
||||
return sortBy(tags).map(tag =>
|
||||
TagElement({ tagName: tag, color: coloredTags[tag] })
|
||||
)
|
||||
} else {
|
||||
return tags.map(tag => TagElement({ tagName: tag }))
|
||||
return tags.map(tag =>
|
||||
TagElement({ tagName: tag, color: coloredTags[tag] })
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,6 +65,7 @@ const TagElementList = (tags, showTagsAlphabetically) => {
|
||||
* @param {Function} handleNoteClick
|
||||
* @param {Function} handleNoteContextMenu
|
||||
* @param {Function} handleDragStart
|
||||
* @param {Object} coloredTags
|
||||
* @param {string} dateDisplay
|
||||
*/
|
||||
const NoteItem = ({
|
||||
@@ -59,7 +79,8 @@ const NoteItem = ({
|
||||
storageName,
|
||||
folderName,
|
||||
viewType,
|
||||
showTagsAlphabetically
|
||||
showTagsAlphabetically,
|
||||
coloredTags
|
||||
}) => (
|
||||
<div
|
||||
styleName={isActive ? 'item--active' : 'item'}
|
||||
@@ -70,13 +91,17 @@ const NoteItem = ({
|
||||
draggable='true'
|
||||
>
|
||||
<div styleName='item-wrapper'>
|
||||
{note.type === 'SNIPPET_NOTE'
|
||||
? <i styleName='item-title-icon' className='fa fa-fw fa-code' />
|
||||
: <i styleName='item-title-icon' className='fa fa-fw fa-file-text-o' />}
|
||||
{note.type === 'SNIPPET_NOTE' ? (
|
||||
<i styleName='item-title-icon' className='fa fa-fw fa-code' />
|
||||
) : (
|
||||
<i styleName='item-title-icon' className='fa fa-fw fa-file-text-o' />
|
||||
)}
|
||||
<div styleName='item-title'>
|
||||
{note.title.trim().length > 0
|
||||
? note.title
|
||||
: <span styleName='item-title-empty'>{i18n.__('Empty note')}</span>}
|
||||
{note.title.trim().length > 0 ? (
|
||||
<Emoji text={note.title} />
|
||||
) : (
|
||||
<span styleName='item-title-empty'>{i18n.__('Empty note')}</span>
|
||||
)}
|
||||
</div>
|
||||
<div styleName='item-middle'>
|
||||
<div styleName='item-middle-time'>{dateDisplay}</div>
|
||||
@@ -85,7 +110,9 @@ const NoteItem = ({
|
||||
title={
|
||||
viewType === 'ALL'
|
||||
? storageName
|
||||
: viewType === 'STORAGE' ? folderName : null
|
||||
: viewType === 'STORAGE'
|
||||
? folderName
|
||||
: null
|
||||
}
|
||||
styleName='item-middle-app-meta-label'
|
||||
>
|
||||
@@ -96,28 +123,36 @@ const NoteItem = ({
|
||||
</div>
|
||||
<div styleName='item-bottom'>
|
||||
<div styleName='item-bottom-tagList'>
|
||||
{note.tags.length > 0
|
||||
? TagElementList(note.tags, showTagsAlphabetically)
|
||||
: <span
|
||||
{note.tags.length > 0 ? (
|
||||
TagElementList(note.tags, showTagsAlphabetically, coloredTags)
|
||||
) : (
|
||||
<span
|
||||
style={{ fontStyle: 'italic', opacity: 0.5 }}
|
||||
styleName='item-bottom-tagList-empty'
|
||||
>
|
||||
>
|
||||
{i18n.__('No tags')}
|
||||
</span>}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
{note.isStarred
|
||||
? <img
|
||||
{note.isStarred ? (
|
||||
<img
|
||||
styleName='item-star'
|
||||
src='../resources/icon/icon-starred.svg'
|
||||
/>
|
||||
: ''}
|
||||
{note.isPinned && !pathname.match(/\/starred|\/trash/)
|
||||
? <i styleName='item-pin' className='fa fa-thumb-tack' />
|
||||
: ''}
|
||||
{note.type === 'MARKDOWN_NOTE'
|
||||
? <TodoProcess todoStatus={getTodoStatus(note.content)} />
|
||||
: ''}
|
||||
/>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
{note.isPinned && !pathname.match(/\/starred|\/trash/) ? (
|
||||
<i styleName='item-pin' className='fa fa-thumb-tack' />
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
{note.type === 'MARKDOWN_NOTE' ? (
|
||||
<TodoProcess todoStatus={getTodoStatus(note.content)} />
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -127,6 +162,7 @@ const NoteItem = ({
|
||||
NoteItem.propTypes = {
|
||||
isActive: PropTypes.bool.isRequired,
|
||||
dateDisplay: PropTypes.string.isRequired,
|
||||
coloredTags: PropTypes.object,
|
||||
note: PropTypes.shape({
|
||||
storage: PropTypes.string.isRequired,
|
||||
key: PropTypes.string.isRequired,
|
||||
@@ -135,15 +171,14 @@ NoteItem.propTypes = {
|
||||
tags: PropTypes.array,
|
||||
isStarred: PropTypes.bool.isRequired,
|
||||
isTrashed: PropTypes.bool.isRequired,
|
||||
blog: {
|
||||
blog: PropTypes.shape({
|
||||
blogLink: PropTypes.string,
|
||||
blogId: PropTypes.number
|
||||
}
|
||||
})
|
||||
}),
|
||||
handleNoteClick: PropTypes.func.isRequired,
|
||||
handleNoteContextMenu: PropTypes.func.isRequired,
|
||||
handleDragStart: PropTypes.func.isRequired,
|
||||
handleDragEnd: PropTypes.func.isRequired
|
||||
handleDragStart: PropTypes.func.isRequired
|
||||
}
|
||||
|
||||
export default CSSModules(NoteItem, styles)
|
||||
|
||||
@@ -194,7 +194,7 @@ body[data-theme="dark"]
|
||||
color $ui-dark-text-color
|
||||
.item-bottom-tagList-item
|
||||
transition 0.15s
|
||||
background-color alpha(#fff, 20%)
|
||||
background-color alpha($ui-dark-tagList-backgroundColor, 20%)
|
||||
color $ui-dark-text-color
|
||||
&:active
|
||||
transition 0.15s
|
||||
@@ -207,7 +207,7 @@ body[data-theme="dark"]
|
||||
color $ui-dark-text-color
|
||||
.item-bottom-tagList-item
|
||||
transition 0.15s
|
||||
background-color alpha(white, 10%)
|
||||
background-color alpha($ui-dark-tagList-backgroundColor, 10%)
|
||||
color $ui-dark-text-color
|
||||
|
||||
.item-wrapper
|
||||
@@ -223,13 +223,13 @@ body[data-theme="dark"]
|
||||
.item-bottom-time
|
||||
color $ui-dark-text-color
|
||||
.item-bottom-tagList-item
|
||||
background-color alpha(white, 10%)
|
||||
background-color alpha($ui-dark-tagList-backgroundColor, 10%)
|
||||
color $ui-dark-text-color
|
||||
&:hover
|
||||
background-color alpha($ui-dark-button--active-backgroundColor, 60%)
|
||||
color #c0392b
|
||||
color $ui-dark-button--hover-color
|
||||
.item-bottom-tagList-item
|
||||
background-color alpha(#fff, 20%)
|
||||
background-color alpha($ui-dark-tagList-backgroundColor, 20%)
|
||||
|
||||
.item-title
|
||||
color $ui-inactive-text-color
|
||||
@@ -322,148 +322,82 @@ body[data-theme="solarized-dark"]
|
||||
color $ui-inactive-text-color
|
||||
vertical-align middle
|
||||
|
||||
body[data-theme="monokai"]
|
||||
.root
|
||||
border-color $ui-monokai-borderColor
|
||||
background-color $ui-monokai-noteList-backgroundColor
|
||||
apply-theme(theme)
|
||||
body[data-theme={theme}]
|
||||
.root
|
||||
border-color get-theme-var(theme, 'borderColor')
|
||||
background-color get-theme-var(theme, 'noteList-backgroundColor')
|
||||
|
||||
.item
|
||||
border-color $ui-monokai-borderColor
|
||||
background-color $ui-monokai-noteList-backgroundColor
|
||||
&:hover
|
||||
transition 0.15s
|
||||
// background-color alpha($ui-monokai-noteList-backgroundColor, 20%)
|
||||
color $ui-monokai-text-color
|
||||
.item-title
|
||||
.item-title-icon
|
||||
.item-bottom-time
|
||||
.item
|
||||
border-color get-theme-var(theme, 'borderColor')
|
||||
background-color get-theme-var(theme, 'noteList-backgroundColor')
|
||||
&:hover
|
||||
transition 0.15s
|
||||
color $ui-monokai-text-color
|
||||
.item-bottom-tagList-item
|
||||
// background-color alpha(get-theme-var(theme, 'noteList-backgroundColor'), 20%)
|
||||
color get-theme-var(theme, 'text-color')
|
||||
.item-title
|
||||
.item-title-icon
|
||||
.item-bottom-time
|
||||
transition 0.15s
|
||||
color get-theme-var(theme, 'text-color')
|
||||
.item-bottom-tagList-item
|
||||
transition 0.15s
|
||||
background-color alpha(get-theme-var(theme, 'noteList-backgroundColor'), 20%)
|
||||
color get-theme-var(theme, 'text-color')
|
||||
&:active
|
||||
transition 0.15s
|
||||
background-color alpha($ui-monokai-noteList-backgroundColor, 20%)
|
||||
color $ui-monokai-text-color
|
||||
&:active
|
||||
transition 0.15s
|
||||
background-color $ui-monokai-noteList-backgroundColor
|
||||
color $ui-monokai-text-color
|
||||
.item-title
|
||||
.item-title-icon
|
||||
.item-bottom-time
|
||||
transition 0.15s
|
||||
color $ui-monokai-text-color
|
||||
.item-bottom-tagList-item
|
||||
transition 0.15s
|
||||
background-color alpha($ui-monokai-noteList-backgroundColor, 10%)
|
||||
color $ui-monokai-text-color
|
||||
background-color get-theme-var(theme, 'noteList-backgroundColor')
|
||||
color get-theme-var(theme, 'text-color')
|
||||
.item-title
|
||||
.item-title-icon
|
||||
.item-bottom-time
|
||||
transition 0.15s
|
||||
color get-theme-var(theme, 'text-color')
|
||||
.item-bottom-tagList-item
|
||||
transition 0.15s
|
||||
background-color alpha(get-theme-var(theme, 'noteList-backgroundColor'), 10%)
|
||||
color get-theme-var(theme, 'text-color')
|
||||
|
||||
.item-wrapper
|
||||
border-color alpha($ui-monokai-button-backgroundColor, 60%)
|
||||
|
||||
.item--active
|
||||
border-color $ui-monokai-borderColor
|
||||
background-color $ui-monokai-button-backgroundColor
|
||||
.item-wrapper
|
||||
border-color transparent
|
||||
.item-title
|
||||
.item-title-icon
|
||||
.item-bottom-time
|
||||
color $ui-monokai-active-color
|
||||
.item-bottom-tagList-item
|
||||
background-color alpha(white, 10%)
|
||||
color $ui-monokai-text-color
|
||||
&:hover
|
||||
// background-color alpha($ui-monokai-button--active-backgroundColor, 60%)
|
||||
color #f92672
|
||||
.item-bottom-tagList-item
|
||||
background-color alpha(#fff, 20%)
|
||||
border-color alpha(get-theme-var(theme, 'button-backgroundColor'), 60%)
|
||||
|
||||
.item-title
|
||||
color $ui-inactive-text-color
|
||||
|
||||
.item-title-icon
|
||||
color $ui-inactive-text-color
|
||||
|
||||
.item-title-empty
|
||||
color $ui-inactive-text-color
|
||||
|
||||
.item-bottom-tagList-item
|
||||
background-color alpha($ui-dark-button--active-backgroundColor, 40%)
|
||||
color $ui-inactive-text-color
|
||||
|
||||
.item-bottom-tagList-empty
|
||||
color $ui-inactive-text-color
|
||||
vertical-align middle
|
||||
|
||||
body[data-theme="dracula"]
|
||||
.root
|
||||
border-color $ui-dracula-borderColor
|
||||
background-color $ui-dracula-noteList-backgroundColor
|
||||
|
||||
.item
|
||||
border-color $ui-dracula-borderColor
|
||||
background-color $ui-dracula-noteList-backgroundColor
|
||||
&:hover
|
||||
transition 0.15s
|
||||
// background-color alpha($ui-dracula-noteList-backgroundColor, 20%)
|
||||
color $ui-dracula-text-color
|
||||
.item--active
|
||||
border-color get-theme-var(theme, 'borderColor')
|
||||
background-color get-theme-var(theme, 'button-backgroundColor')
|
||||
.item-wrapper
|
||||
border-color transparent
|
||||
.item-title
|
||||
.item-title-icon
|
||||
.item-bottom-time
|
||||
transition 0.15s
|
||||
color $ui-dracula-text-color
|
||||
color get-theme-var(theme, 'active-color')
|
||||
.item-bottom-tagList-item
|
||||
transition 0.15s
|
||||
background-color alpha($ui-dracula-noteList-backgroundColor, 20%)
|
||||
color $ui-dracula-text-color
|
||||
&:active
|
||||
transition 0.15s
|
||||
background-color $ui-dracula-noteList-backgroundColor
|
||||
color $ui-dracula-text-color
|
||||
.item-title
|
||||
.item-title-icon
|
||||
.item-bottom-time
|
||||
transition 0.15s
|
||||
color $ui-dracula-text-color
|
||||
.item-bottom-tagList-item
|
||||
transition 0.15s
|
||||
background-color alpha($ui-dracula-noteList-backgroundColor, 10%)
|
||||
color $ui-dracula-text-color
|
||||
background-color alpha(get-theme-var(theme, 'tagList-backgroundColor'), 10%)
|
||||
color get-theme-var(theme, 'text-color')
|
||||
&:hover
|
||||
// background-color alpha(get-theme-var(theme, 'button--active-backgroundColor'), 60%)
|
||||
color get-theme-var(theme, 'button--hover-color')
|
||||
.item-bottom-tagList-item
|
||||
background-color alpha(get-theme-var(theme, 'tagList-backgroundColor'), 20%)
|
||||
|
||||
.item-wrapper
|
||||
border-color alpha($ui-dracula-button-backgroundColor, 60%)
|
||||
|
||||
.item--active
|
||||
border-color $ui-dracula-borderColor
|
||||
background-color $ui-dracula-button-backgroundColor
|
||||
.item-wrapper
|
||||
border-color transparent
|
||||
.item-title
|
||||
color $ui-inactive-text-color
|
||||
|
||||
.item-title-icon
|
||||
.item-bottom-time
|
||||
color $ui-dracula-active-color
|
||||
color $ui-inactive-text-color
|
||||
|
||||
.item-title-empty
|
||||
color $ui-inactive-text-color
|
||||
|
||||
.item-bottom-tagList-item
|
||||
background-color alpha(#f8f8f2, 10%)
|
||||
color $ui-dracula-text-color
|
||||
&:hover
|
||||
// background-color alpha($ui-dracula-button--active-backgroundColor, 60%)
|
||||
color #ff79c6
|
||||
.item-bottom-tagList-item
|
||||
background-color alpha(#f8f8f2, 20%)
|
||||
background-color alpha($ui-dark-button--active-backgroundColor, 40%)
|
||||
color $ui-inactive-text-color
|
||||
|
||||
.item-title
|
||||
color $ui-inactive-text-color
|
||||
.item-bottom-tagList-empty
|
||||
color $ui-inactive-text-color
|
||||
vertical-align middle
|
||||
|
||||
.item-title-icon
|
||||
color $ui-inactive-text-color
|
||||
for theme in 'dracula'
|
||||
apply-theme(theme)
|
||||
|
||||
.item-title-empty
|
||||
color $ui-inactive-text-color
|
||||
|
||||
.item-bottom-tagList-item
|
||||
background-color alpha($ui-dark-button--active-backgroundColor, 40%)
|
||||
color $ui-inactive-text-color
|
||||
|
||||
.item-bottom-tagList-empty
|
||||
color $ui-inactive-text-color
|
||||
vertical-align middle
|
||||
for theme in $themes
|
||||
apply-theme(theme)
|
||||
@@ -25,10 +25,8 @@ const NoteItemSimple = ({
|
||||
pathname,
|
||||
storage
|
||||
}) => (
|
||||
<div styleName={isActive
|
||||
? 'item-simple--active'
|
||||
: 'item-simple'
|
||||
}
|
||||
<div
|
||||
styleName={isActive ? 'item-simple--active' : 'item-simple'}
|
||||
key={note.key}
|
||||
onClick={e => handleNoteClick(e, note.key)}
|
||||
onContextMenu={e => handleNoteContextMenu(e, note.key)}
|
||||
@@ -36,23 +34,29 @@ const NoteItemSimple = ({
|
||||
draggable='true'
|
||||
>
|
||||
<div styleName='item-simple-title'>
|
||||
{note.type === 'SNIPPET_NOTE'
|
||||
? <i styleName='item-simple-title-icon' className='fa fa-fw fa-code' />
|
||||
: <i styleName='item-simple-title-icon' className='fa fa-fw fa-file-text-o' />
|
||||
}
|
||||
{note.isPinned && !pathname.match(/\/starred|\/trash/)
|
||||
? <i styleName='item-pin' className='fa fa-thumb-tack' />
|
||||
: ''
|
||||
}
|
||||
{note.title.trim().length > 0
|
||||
? note.title
|
||||
: <span styleName='item-simple-title-empty'>{i18n.__('Empty note')}</span>
|
||||
}
|
||||
{isAllNotesView && <div styleName='item-simple-right'>
|
||||
<span styleName='item-simple-right-storageName'>
|
||||
{storage.name}
|
||||
</span>
|
||||
</div>}
|
||||
{note.type === 'SNIPPET_NOTE' ? (
|
||||
<i styleName='item-simple-title-icon' className='fa fa-fw fa-code' />
|
||||
) : (
|
||||
<i
|
||||
styleName='item-simple-title-icon'
|
||||
className='fa fa-fw fa-file-text-o'
|
||||
/>
|
||||
)}
|
||||
{note.isPinned && !pathname.match(/\/starred|\/trash/) ? (
|
||||
<i styleName='item-pin' className='fa fa-thumb-tack' />
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
{note.title.trim().length > 0 ? (
|
||||
note.title
|
||||
) : (
|
||||
<span styleName='item-simple-title-empty'>{i18n.__('Empty note')}</span>
|
||||
)}
|
||||
{isAllNotesView && (
|
||||
<div styleName='item-simple-right'>
|
||||
<span styleName='item-simple-right-storageName'>{storage.name}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -223,130 +223,73 @@ body[data-theme="solarized-dark"]
|
||||
padding-left 4px
|
||||
opacity 0.4
|
||||
|
||||
body[data-theme="monokai"]
|
||||
.root
|
||||
border-color $ui-monokai-borderColor
|
||||
background-color $ui-monokai-noteList-backgroundColor
|
||||
apply-theme(theme)
|
||||
body[data-theme={theme}]
|
||||
.root
|
||||
border-color get-theme-var(theme, 'borderColor')
|
||||
background-color get-theme-var(theme, 'noteList-backgroundColor')
|
||||
|
||||
.item-simple
|
||||
border-color $ui-monokai-borderColor
|
||||
background-color $ui-monokai-noteList-backgroundColor
|
||||
&:hover
|
||||
transition 0.15s
|
||||
background-color alpha($ui-monokai-button-backgroundColor, 60%)
|
||||
color $ui-monokai-text-color
|
||||
.item-simple
|
||||
border-color get-theme-var(theme, 'borderColor')
|
||||
background-color get-theme-var(theme, 'noteList-backgroundColor')
|
||||
&:hover
|
||||
transition 0.15s
|
||||
background-color alpha(get-theme-var(theme, 'button-backgroundColor'), 60%)
|
||||
color get-theme-var(theme, 'text-color')
|
||||
.item-simple-title
|
||||
.item-simple-title-empty
|
||||
.item-simple-title-icon
|
||||
.item-simple-bottom-time
|
||||
transition 0.15s
|
||||
color get-theme-var(theme, 'text-color')
|
||||
.item-simple-bottom-tagList-item
|
||||
transition 0.15s
|
||||
background-color alpha(get-theme-var(theme, 'tagList-backgroundColor'), 20%)
|
||||
color get-theme-var(theme, 'text-color')
|
||||
&:active
|
||||
transition 0.15s
|
||||
background-color get-theme-var(theme, 'button--active-backgroundColor')
|
||||
color get-theme-var(theme, 'text-color')
|
||||
.item-simple-title
|
||||
.item-simple-title-empty
|
||||
.item-simple-title-icon
|
||||
.item-simple-bottom-time
|
||||
transition 0.15s
|
||||
color get-theme-var(theme, 'text-color')
|
||||
.item-simple-bottom-tagList-item
|
||||
transition 0.15s
|
||||
background-color alpha(get-theme-var(theme, 'tagList-backgroundColor'), 10%)
|
||||
color get-theme-var(theme, 'text-color')
|
||||
|
||||
.item-simple--active
|
||||
border-color get-theme-var(theme, 'borderColor')
|
||||
background-color get-theme-var(theme, 'button--active-backgroundColor')
|
||||
.item-simple-wrapper
|
||||
border-color transparent
|
||||
.item-simple-title
|
||||
.item-simple-title-empty
|
||||
.item-simple-title-icon
|
||||
.item-simple-bottom-time
|
||||
transition 0.15s
|
||||
color $ui-monokai-text-color
|
||||
color get-theme-var(theme, 'text-color')
|
||||
.item-simple-bottom-tagList-item
|
||||
transition 0.15s
|
||||
background-color alpha(#fff, 20%)
|
||||
color $ui-monokai-text-color
|
||||
&:active
|
||||
transition 0.15s
|
||||
background-color $ui-monokai-button--active-backgroundColor
|
||||
color $ui-monokai-text-color
|
||||
.item-simple-title
|
||||
.item-simple-title-empty
|
||||
.item-simple-title-icon
|
||||
.item-simple-bottom-time
|
||||
transition 0.15s
|
||||
color $ui-monokai-text-color
|
||||
.item-simple-bottom-tagList-item
|
||||
transition 0.15s
|
||||
background-color alpha(white, 10%)
|
||||
color $ui-monokai-text-color
|
||||
|
||||
.item-simple--active
|
||||
border-color $ui-monokai-borderColor
|
||||
background-color $ui-monokai-button--active-backgroundColor
|
||||
.item-simple-wrapper
|
||||
border-color transparent
|
||||
background-color alpha(get-theme-var(theme, 'tagList-backgroundColor'), 10%)
|
||||
color get-theme-var(theme, 'text-color')
|
||||
&:hover
|
||||
// background-color alpha(get-theme-var(theme, 'button--active-backgroundColor'), 60%)
|
||||
color #c0392b
|
||||
.item-simple-bottom-tagList-item
|
||||
background-color alpha(get-theme-var(theme, 'tagList-backgroundColor'), 20%)
|
||||
.item-simple-title
|
||||
.item-simple-title-empty
|
||||
.item-simple-title-icon
|
||||
.item-simple-bottom-time
|
||||
color $ui-monokai-text-color
|
||||
.item-simple-bottom-tagList-item
|
||||
background-color alpha(white, 10%)
|
||||
color $ui-monokai-text-color
|
||||
&:hover
|
||||
// background-color alpha($ui-dark-button--active-backgroundColor, 60%)
|
||||
color #c0392b
|
||||
.item-simple-bottom-tagList-item
|
||||
background-color alpha(#fff, 20%)
|
||||
.item-simple-title
|
||||
color $ui-dark-text-color
|
||||
border-bottom $ui-dark-borderColor
|
||||
.item-simple-right
|
||||
float right
|
||||
.item-simple-right-storageName
|
||||
padding-left 4px
|
||||
opacity 0.4
|
||||
color $ui-dark-text-color
|
||||
border-bottom $ui-dark-borderColor
|
||||
.item-simple-right
|
||||
float right
|
||||
.item-simple-right-storageName
|
||||
padding-left 4px
|
||||
opacity 0.4
|
||||
|
||||
body[data-theme="dracula"]
|
||||
.root
|
||||
border-color $ui-dracula-borderColor
|
||||
background-color $ui-dracula-noteList-backgroundColor
|
||||
for theme in 'dracula'
|
||||
apply-theme(theme)
|
||||
|
||||
.item-simple
|
||||
border-color $ui-dracula-borderColor
|
||||
background-color $ui-dracula-noteList-backgroundColor
|
||||
&:hover
|
||||
transition 0.15s
|
||||
background-color alpha($ui-dracula-button-backgroundColor, 60%)
|
||||
color $ui-dracula-text-color
|
||||
.item-simple-title
|
||||
.item-simple-title-empty
|
||||
.item-simple-title-icon
|
||||
.item-simple-bottom-time
|
||||
transition 0.15s
|
||||
color $ui-dracula-text-color
|
||||
.item-simple-bottom-tagList-item
|
||||
transition 0.15s
|
||||
background-color alpha(#f8f8f2, 20%)
|
||||
color $ui-dracula-text-color
|
||||
&:active
|
||||
transition 0.15s
|
||||
background-color $ui-dracula-button--active-backgroundColor
|
||||
color $ui-dracula-text-color
|
||||
.item-simple-title
|
||||
.item-simple-title-empty
|
||||
.item-simple-title-icon
|
||||
.item-simple-bottom-time
|
||||
transition 0.15s
|
||||
color $ui-dracula-text-color
|
||||
.item-simple-bottom-tagList-item
|
||||
transition 0.15s
|
||||
background-color alpha(#f8f8f2, 10%)
|
||||
color $ui-dracula-text-color
|
||||
|
||||
.item-simple--active
|
||||
border-color $ui-dracula-borderColor
|
||||
background-color $ui-dracula-button--active-backgroundColor
|
||||
.item-simple-wrapper
|
||||
border-color transparent
|
||||
.item-simple-title
|
||||
.item-simple-title-empty
|
||||
.item-simple-title-icon
|
||||
.item-simple-bottom-time
|
||||
color $ui-dracula-text-color
|
||||
.item-simple-bottom-tagList-item
|
||||
background-color alpha(#f8f8f2, 10%)
|
||||
color $ui-dracula-text-color
|
||||
&:hover
|
||||
// background-color alpha($ui-dark-button--active-backgroundColor, 60%)
|
||||
color #c0392b
|
||||
.item-simple-bottom-tagList-item
|
||||
background-color alpha(#f8f8f2, 20%)
|
||||
.item-simple-title
|
||||
color $ui-dark-text-color
|
||||
border-bottom $ui-dark-borderColor
|
||||
.item-simple-right
|
||||
float right
|
||||
.item-simple-right-storageName
|
||||
padding-left 4px
|
||||
opacity 0.4
|
||||
for theme in $themes
|
||||
apply-theme(theme)
|
||||
@@ -6,7 +6,7 @@ const electron = require('electron')
|
||||
const { shell } = electron
|
||||
|
||||
class RealtimeNotification extends React.Component {
|
||||
constructor (props) {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
@@ -14,38 +14,46 @@ class RealtimeNotification extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
componentDidMount() {
|
||||
this.fetchNotifications()
|
||||
}
|
||||
|
||||
fetchNotifications () {
|
||||
const notificationsUrl = 'https://raw.githubusercontent.com/BoostIO/notification/master/notification.json'
|
||||
fetchNotifications() {
|
||||
const notificationsUrl =
|
||||
'https://raw.githubusercontent.com/BoostIO/notification/master/notification.json'
|
||||
fetch(notificationsUrl)
|
||||
.then(response => {
|
||||
return response.json()
|
||||
})
|
||||
.then(json => {
|
||||
this.setState({notifications: json.notifications})
|
||||
this.setState({ notifications: json.notifications })
|
||||
})
|
||||
}
|
||||
|
||||
handleLinkClick (e) {
|
||||
handleLinkClick(e) {
|
||||
shell.openExternal(e.currentTarget.href)
|
||||
e.preventDefault()
|
||||
}
|
||||
|
||||
render () {
|
||||
render() {
|
||||
const { notifications } = this.state
|
||||
const link = notifications.length > 0
|
||||
? <a styleName='notification-link' href={notifications[0].linkUrl}
|
||||
onClick={(e) => this.handleLinkClick(e)}
|
||||
>
|
||||
Info: {notifications[0].text}
|
||||
</a>
|
||||
: ''
|
||||
const link =
|
||||
notifications.length > 0 ? (
|
||||
<a
|
||||
styleName='notification-link'
|
||||
href={notifications[0].linkUrl}
|
||||
onClick={e => this.handleLinkClick(e)}
|
||||
>
|
||||
Info: {notifications[0].text}
|
||||
</a>
|
||||
) : (
|
||||
''
|
||||
)
|
||||
|
||||
return (
|
||||
<div styleName='notification-area' style={this.props.style}>{link}</div>
|
||||
<div styleName='notification-area' style={this.props.style}>
|
||||
{link}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,36 +30,20 @@ body[data-theme="dark"]
|
||||
&:hover
|
||||
color #5CB85C
|
||||
|
||||
apply-theme(theme)
|
||||
body[data-theme={theme}]
|
||||
.notification-area
|
||||
background-color none
|
||||
|
||||
body[data-theme="solarized-dark"]
|
||||
.notification-area
|
||||
background-color none
|
||||
.notification-link
|
||||
color get-theme-var(theme, 'text-color')
|
||||
border none
|
||||
background-color get-theme-var(theme, 'button-backgroundColor')
|
||||
&:hover
|
||||
color get-theme-var(theme, 'button--hover-color')
|
||||
|
||||
.notification-link
|
||||
color $ui-solarized-dark-text-color
|
||||
border none
|
||||
background-color $ui-solarized-dark-button-backgroundColor
|
||||
&:hover
|
||||
color #5CB85C
|
||||
for theme in 'solarized-dark' 'dracula'
|
||||
apply-theme(theme)
|
||||
|
||||
body[data-theme="monokai"]
|
||||
.notification-area
|
||||
background-color none
|
||||
|
||||
.notification-link
|
||||
color $ui-monokai-text-color
|
||||
border none
|
||||
background-color $ui-monokai-button-backgroundColor
|
||||
&:hover
|
||||
color #5CB85C
|
||||
|
||||
body[data-theme="dracula"]
|
||||
.notification-area
|
||||
background-color none
|
||||
|
||||
.notification-link
|
||||
color $ui-dracula-text-color
|
||||
border none
|
||||
background-color $ui-dracula-button-backgroundColor
|
||||
&:hover
|
||||
color #ff79c6
|
||||
for theme in $themes
|
||||
apply-theme(theme)
|
||||
@@ -16,54 +16,70 @@ import i18n from 'browser/lib/i18n'
|
||||
* @return {React.Component}
|
||||
*/
|
||||
const SideNavFilter = ({
|
||||
isFolded, isHomeActive, handleAllNotesButtonClick,
|
||||
isStarredActive, handleStarredButtonClick, isTrashedActive, handleTrashedButtonClick, counterDelNote,
|
||||
counterTotalNote, counterStarredNote, handleFilterButtonContextMenu
|
||||
isFolded,
|
||||
isHomeActive,
|
||||
handleAllNotesButtonClick,
|
||||
isStarredActive,
|
||||
handleStarredButtonClick,
|
||||
isTrashedActive,
|
||||
handleTrashedButtonClick,
|
||||
counterDelNote,
|
||||
counterTotalNote,
|
||||
counterStarredNote,
|
||||
handleFilterButtonContextMenu
|
||||
}) => (
|
||||
<div styleName={isFolded ? 'menu--folded' : 'menu'}>
|
||||
|
||||
<button styleName={isHomeActive ? 'menu-button--active' : 'menu-button'}
|
||||
<button
|
||||
styleName={isHomeActive ? 'menu-button--active' : 'menu-button'}
|
||||
onClick={handleAllNotesButtonClick}
|
||||
>
|
||||
<div styleName='iconWrap'>
|
||||
<img src={isHomeActive
|
||||
? '../resources/icon/icon-all-active.svg'
|
||||
: '../resources/icon/icon-all.svg'
|
||||
}
|
||||
<img
|
||||
src={
|
||||
isHomeActive
|
||||
? '../resources/icon/icon-all-active.svg'
|
||||
: '../resources/icon/icon-all.svg'
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<span styleName='menu-button-label'>{i18n.__('All Notes')}</span>
|
||||
<span styleName='counters'>{counterTotalNote}</span>
|
||||
</button>
|
||||
|
||||
<button styleName={isStarredActive ? 'menu-button-star--active' : 'menu-button'}
|
||||
<button
|
||||
styleName={isStarredActive ? 'menu-button-star--active' : 'menu-button'}
|
||||
onClick={handleStarredButtonClick}
|
||||
>
|
||||
<div styleName='iconWrap'>
|
||||
<img src={isStarredActive
|
||||
? '../resources/icon/icon-star-active.svg'
|
||||
: '../resources/icon/icon-star-sidenav.svg'
|
||||
}
|
||||
<img
|
||||
src={
|
||||
isStarredActive
|
||||
? '../resources/icon/icon-star-active.svg'
|
||||
: '../resources/icon/icon-star-sidenav.svg'
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<span styleName='menu-button-label'>{i18n.__('Starred')}</span>
|
||||
<span styleName='counters'>{counterStarredNote}</span>
|
||||
</button>
|
||||
|
||||
<button styleName={isTrashedActive ? 'menu-button-trash--active' : 'menu-button'}
|
||||
onClick={handleTrashedButtonClick} onContextMenu={handleFilterButtonContextMenu}
|
||||
<button
|
||||
styleName={isTrashedActive ? 'menu-button-trash--active' : 'menu-button'}
|
||||
onClick={handleTrashedButtonClick}
|
||||
onContextMenu={handleFilterButtonContextMenu}
|
||||
>
|
||||
<div styleName='iconWrap'>
|
||||
<img src={isTrashedActive
|
||||
? '../resources/icon/icon-trash-active.svg'
|
||||
: '../resources/icon/icon-trash-sidenav.svg'
|
||||
}
|
||||
<img
|
||||
src={
|
||||
isTrashedActive
|
||||
? '../resources/icon/icon-trash-active.svg'
|
||||
: '../resources/icon/icon-trash-sidenav.svg'
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<span styleName='menu-button-label'>{i18n.__('Trash')}</span>
|
||||
<span styleName='counters'>{counterDelNote}</span>
|
||||
</button>
|
||||
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -74,7 +90,7 @@ SideNavFilter.propTypes = {
|
||||
isStarredActive: PropTypes.bool.isRequired,
|
||||
isTrashedActive: PropTypes.bool.isRequired,
|
||||
handleStarredButtonClick: PropTypes.func.isRequired,
|
||||
handleTrashdButtonClick: PropTypes.func.isRequired
|
||||
handleTrashedButtonClick: PropTypes.func.isRequired
|
||||
}
|
||||
|
||||
export default CSSModules(SideNavFilter, styles)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
.menu
|
||||
margin-bottom 30px
|
||||
margin-bottom 20px
|
||||
|
||||
.menu-button
|
||||
navButtonColor()
|
||||
@@ -180,129 +180,51 @@ body[data-theme="dark"]
|
||||
.menu-button-label
|
||||
color $ui-dark-text-color
|
||||
|
||||
apply-theme(theme)
|
||||
body[data-theme={theme}]
|
||||
.menu-button
|
||||
&:active
|
||||
background-color get-theme-var(theme, 'noteList-backgroundColor')
|
||||
color get-theme-var(theme, 'text-color')
|
||||
&:hover
|
||||
background-color get-theme-var(theme, 'button-backgroundColor')
|
||||
color get-theme-var(theme, 'text-color')
|
||||
|
||||
body[data-theme="solarized-dark"]
|
||||
.menu-button
|
||||
&:active
|
||||
background-color $ui-solarized-dark-noteList-backgroundColor
|
||||
color $ui-solarized-dark-text-color
|
||||
&:hover
|
||||
background-color $ui-solarized-dark-button-backgroundColor
|
||||
color $ui-solarized-dark-text-color
|
||||
|
||||
.menu-button--active
|
||||
color $ui-solarized-dark-text-color
|
||||
background-color $ui-solarized-dark-button-backgroundColor
|
||||
.menu-button-label
|
||||
color $ui-solarized-dark-text-color
|
||||
&:hover
|
||||
background-color $ui-solarized-dark-button-backgroundColor
|
||||
color $ui-solarized-dark-text-color
|
||||
.menu-button--active
|
||||
color get-theme-var(theme, 'text-color')
|
||||
background-color get-theme-var(theme, 'button-backgroundColor')
|
||||
.menu-button-label
|
||||
color $ui-solarized-dark-text-color
|
||||
color get-theme-var(theme, 'text-color')
|
||||
&:hover
|
||||
background-color get-theme-var(theme, 'button-backgroundColor')
|
||||
color get-theme-var(theme, 'text-color')
|
||||
.menu-button-label
|
||||
color get-theme-var(theme, 'text-color')
|
||||
|
||||
.menu-button-star--active
|
||||
color $ui-solarized-dark-text-color
|
||||
background-color $ui-solarized-dark-button-backgroundColor
|
||||
.menu-button-label
|
||||
color $ui-solarized-dark-text-color
|
||||
&:hover
|
||||
background-color $ui-solarized-dark-button-backgroundColor
|
||||
color $ui-solarized-dark-text-color
|
||||
.menu-button-star--active
|
||||
color get-theme-var(theme, 'text-color')
|
||||
background-color get-theme-var(theme, 'button-backgroundColor')
|
||||
.menu-button-label
|
||||
color $ui-solarized-dark-text-color
|
||||
color get-theme-var(theme, 'text-color')
|
||||
&:hover
|
||||
background-color get-theme-var(theme, 'button-backgroundColor')
|
||||
color get-theme-var(theme, 'text-color')
|
||||
.menu-button-label
|
||||
color get-theme-var(theme, 'text-color')
|
||||
|
||||
.menu-button-trash--active
|
||||
color $ui-solarized-dark-text-color
|
||||
background-color $ui-solarized-dark-button-backgroundColor
|
||||
.menu-button-label
|
||||
color $ui-solarized-dark-text-color
|
||||
&:hover
|
||||
background-color $ui-solarized-dark-button-backgroundColor
|
||||
color $ui-solarized-dark-text-color
|
||||
.menu-button-trash--active
|
||||
color get-theme-var(theme, 'text-color')
|
||||
background-color get-theme-var(theme, 'button-backgroundColor')
|
||||
.menu-button-label
|
||||
color $ui-solarized-dark-text-color
|
||||
color get-theme-var(theme, 'text-color')
|
||||
&:hover
|
||||
background-color get-theme-var(theme, 'button-backgroundColor')
|
||||
color get-theme-var(theme, 'text-color')
|
||||
.menu-button-label
|
||||
color get-theme-var(theme, 'text-color')
|
||||
|
||||
body[data-theme="monokai"]
|
||||
.menu-button
|
||||
&:active
|
||||
background-color $ui-monokai-noteList-backgroundColor
|
||||
color $ui-monokai-text-color
|
||||
&:hover
|
||||
background-color $ui-monokai-button-backgroundColor
|
||||
color $ui-monokai-text-color
|
||||
for theme in 'solarized-dark' 'dracula'
|
||||
apply-theme(theme)
|
||||
|
||||
.menu-button--active
|
||||
color $ui-monokai-text-color
|
||||
background-color $ui-monokai-button-backgroundColor
|
||||
.menu-button-label
|
||||
color $ui-monokai-text-color
|
||||
&:hover
|
||||
background-color $ui-monokai-button-backgroundColor
|
||||
color $ui-monokai-text-color
|
||||
.menu-button-label
|
||||
color $ui-monokai-text-color
|
||||
|
||||
.menu-button-star--active
|
||||
color $ui-monokai-text-color
|
||||
background-color $ui-monokai-button-backgroundColor
|
||||
.menu-button-label
|
||||
color $ui-monokai-text-color
|
||||
&:hover
|
||||
background-color $ui-monokai-button-backgroundColor
|
||||
color $ui-monokai-text-color
|
||||
.menu-button-label
|
||||
color $ui-monokai-text-color
|
||||
|
||||
.menu-button-trash--active
|
||||
color $ui-monokai-text-color
|
||||
background-color $ui-monokai-button-backgroundColor
|
||||
.menu-button-label
|
||||
color $ui-monokai-text-color
|
||||
&:hover
|
||||
background-color $ui-monokai-button-backgroundColor
|
||||
color $ui-monokai-text-color
|
||||
.menu-button-label
|
||||
color $ui-monokai-text-color
|
||||
|
||||
body[data-theme="dracula"]
|
||||
.menu-button
|
||||
&:active
|
||||
background-color $ui-dracula-noteList-backgroundColor
|
||||
color $ui-dracula-text-color
|
||||
&:hover
|
||||
background-color $ui-dracula-button-backgroundColor
|
||||
color $ui-dracula-text-color
|
||||
|
||||
.menu-button--active
|
||||
color $ui-dracula-text-color
|
||||
background-color $ui-dracula-button-backgroundColor
|
||||
.menu-button-label
|
||||
color $ui-dracula-text-color
|
||||
&:hover
|
||||
background-color $ui-dracula-button-backgroundColor
|
||||
color $ui-dracula-text-color
|
||||
.menu-button-label
|
||||
color $ui-dracula-text-color
|
||||
|
||||
.menu-button-star--active
|
||||
color $ui-dracula-text-color
|
||||
background-color $ui-dracula-button-backgroundColor
|
||||
.menu-button-label
|
||||
color $ui-dracula-text-color
|
||||
&:hover
|
||||
background-color $ui-dracula-button-backgroundColor
|
||||
color $ui-dracula-text-color
|
||||
.menu-button-label
|
||||
color $ui-dracula-text-color
|
||||
|
||||
.menu-button-trash--active
|
||||
color $ui-dracula-text-color
|
||||
background-color $ui-dracula-button-backgroundColor
|
||||
.menu-button-label
|
||||
color $ui-dracula-text-color
|
||||
&:hover
|
||||
background-color $ui-dracula-button-backgroundColor
|
||||
color $ui-dracula-text-color
|
||||
.menu-button-label
|
||||
color $ui-dracula-text-color
|
||||
for theme in $themes
|
||||
apply-theme(theme)
|
||||
@@ -5,7 +5,7 @@ import context from 'browser/lib/context'
|
||||
import i18n from 'browser/lib/i18n'
|
||||
|
||||
class SnippetTab extends React.Component {
|
||||
constructor (props) {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
@@ -14,7 +14,7 @@ class SnippetTab extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUpdate (nextProps) {
|
||||
componentWillUpdate(nextProps) {
|
||||
if (nextProps.snippet.name !== this.props.snippet.name) {
|
||||
this.setState({
|
||||
name: nextProps.snippet.name
|
||||
@@ -22,34 +22,34 @@ class SnippetTab extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
handleClick (e) {
|
||||
handleClick(e) {
|
||||
this.props.onClick(e)
|
||||
}
|
||||
|
||||
handleContextMenu (e) {
|
||||
handleContextMenu(e) {
|
||||
context.popup([
|
||||
{
|
||||
label: i18n.__('Rename'),
|
||||
click: (e) => this.handleRenameClick(e)
|
||||
click: e => this.handleRenameClick(e)
|
||||
}
|
||||
])
|
||||
}
|
||||
|
||||
handleRenameClick (e) {
|
||||
handleRenameClick(e) {
|
||||
this.startRenaming()
|
||||
}
|
||||
|
||||
handleNameInputBlur (e) {
|
||||
handleNameInputBlur(e) {
|
||||
this.handleRename()
|
||||
}
|
||||
|
||||
handleNameInputChange (e) {
|
||||
handleNameInputChange(e) {
|
||||
this.setState({
|
||||
name: e.target.value
|
||||
})
|
||||
}
|
||||
|
||||
handleNameInputKeyDown (e) {
|
||||
handleNameInputKeyDown(e) {
|
||||
switch (e.keyCode) {
|
||||
case 13:
|
||||
this.handleRename()
|
||||
@@ -63,84 +63,87 @@ class SnippetTab extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
handleRename () {
|
||||
this.setState({
|
||||
isRenaming: false
|
||||
}, () => {
|
||||
if (this.props.snippet.name !== this.state.name) {
|
||||
this.props.onRename(this.state.name)
|
||||
handleRename() {
|
||||
this.setState(
|
||||
{
|
||||
isRenaming: false
|
||||
},
|
||||
() => {
|
||||
if (this.props.snippet.name !== this.state.name) {
|
||||
this.props.onRename(this.state.name)
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
handleDeleteButtonClick (e) {
|
||||
handleDeleteButtonClick(e) {
|
||||
this.props.onDelete(e)
|
||||
}
|
||||
|
||||
startRenaming () {
|
||||
this.setState({
|
||||
isRenaming: true
|
||||
}, () => {
|
||||
this.refs.name.focus()
|
||||
this.refs.name.select()
|
||||
})
|
||||
startRenaming() {
|
||||
this.setState(
|
||||
{
|
||||
isRenaming: true
|
||||
},
|
||||
() => {
|
||||
this.refs.name.focus()
|
||||
this.refs.name.select()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
handleDragStart (e) {
|
||||
handleDragStart(e) {
|
||||
e.dataTransfer.dropEffect = 'move'
|
||||
this.props.onDragStart(e)
|
||||
}
|
||||
|
||||
handleDrop (e) {
|
||||
handleDrop(e) {
|
||||
this.props.onDrop(e)
|
||||
}
|
||||
|
||||
render () {
|
||||
render() {
|
||||
const { isActive, snippet, isDeletable } = this.props
|
||||
return (
|
||||
<div styleName={isActive
|
||||
? 'root--active'
|
||||
: 'root'
|
||||
}
|
||||
>
|
||||
{!this.state.isRenaming
|
||||
? <button styleName='button'
|
||||
onClick={(e) => this.handleClick(e)}
|
||||
onDoubleClick={(e) => this.handleRenameClick(e)}
|
||||
onContextMenu={(e) => this.handleContextMenu(e)}
|
||||
onDragStart={(e) => this.handleDragStart(e)}
|
||||
onDrop={(e) => this.handleDrop(e)}
|
||||
<div styleName={isActive ? 'root--active' : 'root'}>
|
||||
{!this.state.isRenaming ? (
|
||||
<button
|
||||
styleName='button'
|
||||
onClick={e => this.handleClick(e)}
|
||||
onDoubleClick={e => this.handleRenameClick(e)}
|
||||
onContextMenu={e => this.handleContextMenu(e)}
|
||||
onDragStart={e => this.handleDragStart(e)}
|
||||
onDrop={e => this.handleDrop(e)}
|
||||
draggable='true'
|
||||
>
|
||||
{snippet.name.trim().length > 0
|
||||
? snippet.name
|
||||
: <span styleName='button-unnamed'>
|
||||
{i18n.__('Unnamed')}
|
||||
</span>
|
||||
}
|
||||
{snippet.name.trim().length > 0 ? (
|
||||
snippet.name
|
||||
) : (
|
||||
<span>{i18n.__('Unnamed')}</span>
|
||||
)}
|
||||
</button>
|
||||
: <input styleName='input'
|
||||
) : (
|
||||
<input
|
||||
styleName='input'
|
||||
ref='name'
|
||||
value={this.state.name}
|
||||
onChange={(e) => this.handleNameInputChange(e)}
|
||||
onBlur={(e) => this.handleNameInputBlur(e)}
|
||||
onKeyDown={(e) => this.handleNameInputKeyDown(e)}
|
||||
onChange={e => this.handleNameInputChange(e)}
|
||||
onBlur={e => this.handleNameInputBlur(e)}
|
||||
onKeyDown={e => this.handleNameInputKeyDown(e)}
|
||||
/>
|
||||
}
|
||||
{isDeletable &&
|
||||
<button styleName='deleteButton'
|
||||
onClick={(e) => this.handleDeleteButtonClick(e)}
|
||||
)}
|
||||
{isDeletable && (
|
||||
<button
|
||||
styleName='deleteButton'
|
||||
onClick={e => this.handleDeleteButtonClick(e)}
|
||||
>
|
||||
<i className='fa fa-times' />
|
||||
</button>
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
SnippetTab.propTypes = {
|
||||
|
||||
}
|
||||
SnippetTab.propTypes = {}
|
||||
|
||||
export default CSSModules(SnippetTab, styles)
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
width 100%
|
||||
outline none
|
||||
|
||||
body[data-theme="default"], body[data-theme="white"]
|
||||
body[data-theme="default"], body[data-theme="white"]
|
||||
.root--active
|
||||
&:hover
|
||||
background-color alpha($ui-button--active-backgroundColor, 60%)
|
||||
@@ -100,103 +100,43 @@ body[data-theme="dark"]
|
||||
color $ui-dark-text-color
|
||||
transition 0.15s
|
||||
|
||||
body[data-theme="solarized-dark"]
|
||||
.root
|
||||
border-color $ui-solarized-dark-borderColor
|
||||
&:hover
|
||||
background-color $ui-solarized-dark-noteDetail-backgroundColor
|
||||
transition 0.15s
|
||||
apply-theme(theme)
|
||||
body[data-theme={theme}]
|
||||
.root
|
||||
border-color get-theme-var(theme, 'borderColor')
|
||||
&:hover
|
||||
background-color get-theme-var(theme, 'noteDetail-backgroundColor')
|
||||
transition 0.15s
|
||||
.deleteButton
|
||||
color get-theme-var(theme, 'text-color')
|
||||
transition 0.15s
|
||||
.button
|
||||
color get-theme-var(theme, 'text-color')
|
||||
transition 0.15s
|
||||
|
||||
.root--active
|
||||
color get-theme-var(theme, 'active-color')
|
||||
background-color get-theme-var(theme, 'button-backgroundColor')
|
||||
border-color get-theme-var(theme, 'borderColor')
|
||||
.deleteButton
|
||||
color $ui-solarized-dark-button--active-color
|
||||
transition 0.15s
|
||||
color get-theme-var(theme, 'text-color')
|
||||
.button
|
||||
color $ui-solarized-dark-button--active-color
|
||||
transition 0.15s
|
||||
color get-theme-var(theme, 'active-color')
|
||||
|
||||
.root--active
|
||||
color $ui-solarized-dark-button--active-color
|
||||
background-color $ui-solarized-dark-button-backgroundColor
|
||||
border-color $ui-solarized-dark-borderColor
|
||||
.deleteButton
|
||||
color $ui-solarized-dark-button--active-color
|
||||
.button
|
||||
color $ui-solarized-dark-button--active-color
|
||||
border none
|
||||
color $ui-inactive-text-color
|
||||
background-color transparent
|
||||
transition color background-color 0.15s
|
||||
border-left 4px solid transparent
|
||||
|
||||
.button
|
||||
border none
|
||||
color $ui-solarized-dark-text-color
|
||||
background-color transparent
|
||||
transition color background-color 0.15s
|
||||
border-left 4px solid transparent
|
||||
|
||||
.input
|
||||
background-color $ui-solarized-dark-noteDetail-backgroundColor
|
||||
color $ui-solarized-dark-button--active-color
|
||||
transition 0.15s
|
||||
|
||||
body[data-theme="monokai"]
|
||||
.root
|
||||
border-color $ui-monokai-borderColor
|
||||
&:hover
|
||||
background-color $ui-monokai-noteDetail-backgroundColor
|
||||
.input
|
||||
background-color get-theme-var(theme, 'noteDetail-backgroundColor')
|
||||
color get-theme-var(theme, 'text-color')
|
||||
transition 0.15s
|
||||
.deleteButton
|
||||
color $ui-monokai-text-color
|
||||
transition 0.15s
|
||||
.button
|
||||
color $ui-monokai-text-color
|
||||
transition 0.15s
|
||||
|
||||
.root--active
|
||||
color $ui-monokai-active-color
|
||||
background-color $ui-monokai-button-backgroundColor
|
||||
border-color $ui-monokai-borderColor
|
||||
.deleteButton
|
||||
color $ui-monokai-text-color
|
||||
.button
|
||||
color $ui-monokai-active-color
|
||||
|
||||
.button
|
||||
border none
|
||||
color $ui-inactive-text-color
|
||||
background-color transparent
|
||||
transition color background-color 0.15s
|
||||
border-left 4px solid transparent
|
||||
for theme in 'solarized-dark' 'dracula'
|
||||
apply-theme(theme)
|
||||
|
||||
.input
|
||||
background-color $ui-monokai-noteDetail-backgroundColor
|
||||
color $ui-monokai-text-color
|
||||
transition 0.15s
|
||||
|
||||
body[data-theme="dracula"]
|
||||
.root
|
||||
border-color $ui-dracula-borderColor
|
||||
&:hover
|
||||
background-color $ui-dracula-noteDetail-backgroundColor
|
||||
transition 0.15s
|
||||
.deleteButton
|
||||
color $ui-dracula-text-color
|
||||
transition 0.15s
|
||||
.button
|
||||
color $ui-dracula-text-color
|
||||
transition 0.15s
|
||||
|
||||
.root--active
|
||||
color $ui-dracula-text-color
|
||||
background-color $ui-dracula-button-backgroundColor
|
||||
border-color $ui-dracula-borderColor
|
||||
.deleteButton
|
||||
color $ui-dracula-text-color
|
||||
.button
|
||||
color $ui-dracula-active-color
|
||||
|
||||
.button
|
||||
border none
|
||||
color $ui-inactive-text-color
|
||||
background-color transparent
|
||||
transition color background-color 0.15s
|
||||
border-left 4px solid transparent
|
||||
|
||||
.input
|
||||
background-color $ui-dracula-noteDetail-backgroundColor
|
||||
color $ui-dracula-text-color
|
||||
for theme in $themes
|
||||
apply-theme(theme)
|
||||
@@ -21,7 +21,9 @@ const FolderIcon = ({ className, color, isActive }) => {
|
||||
|
||||
/**
|
||||
* @param {boolean} isActive
|
||||
* @param {object} tooltipRef,
|
||||
* @param {Function} handleButtonClick
|
||||
* @param {Function} handleMouseEnter
|
||||
* @param {Function} handleContextMenu
|
||||
* @param {string} folderName
|
||||
* @param {string} folderColor
|
||||
@@ -35,7 +37,9 @@ const FolderIcon = ({ className, color, isActive }) => {
|
||||
const StorageItem = ({
|
||||
styles,
|
||||
isActive,
|
||||
tooltipRef,
|
||||
handleButtonClick,
|
||||
handleMouseEnter,
|
||||
handleContextMenu,
|
||||
folderName,
|
||||
folderColor,
|
||||
@@ -49,13 +53,15 @@ const StorageItem = ({
|
||||
<button
|
||||
styleName={isActive ? 'folderList-item--active' : 'folderList-item'}
|
||||
onClick={handleButtonClick}
|
||||
onMouseEnter={handleMouseEnter}
|
||||
onContextMenu={handleContextMenu}
|
||||
onDrop={handleDrop}
|
||||
onDragEnter={handleDragEnter}
|
||||
onDragLeave={handleDragLeave}
|
||||
>
|
||||
{!isFolded &&
|
||||
<DraggableIcon className={styles['folderList-item-reorder']} />}
|
||||
{!isFolded && (
|
||||
<DraggableIcon className={styles['folderList-item-reorder']} />
|
||||
)}
|
||||
<span
|
||||
styleName={
|
||||
isFolded ? 'folderList-item-name--folded' : 'folderList-item-name'
|
||||
@@ -70,18 +76,23 @@ const StorageItem = ({
|
||||
? _.truncate(folderName, { length: 1, omission: '' })
|
||||
: folderName}
|
||||
</span>
|
||||
{!isFolded &&
|
||||
_.isNumber(noteCount) &&
|
||||
<span styleName='folderList-item-noteCount'>{noteCount}</span>}
|
||||
{isFolded &&
|
||||
<span styleName='folderList-item-tooltip'>{folderName}</span>}
|
||||
{!isFolded && _.isNumber(noteCount) && (
|
||||
<span styleName='folderList-item-noteCount'>{noteCount}</span>
|
||||
)}
|
||||
{isFolded && (
|
||||
<span styleName='folderList-item-tooltip' ref={tooltipRef}>
|
||||
{folderName}
|
||||
</span>
|
||||
)}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
StorageItem.propTypes = {
|
||||
isActive: PropTypes.bool.isRequired,
|
||||
tooltipRef: PropTypes.object,
|
||||
handleButtonClick: PropTypes.func,
|
||||
handleMouseEnter: PropTypes.func,
|
||||
handleContextMenu: PropTypes.func,
|
||||
folderName: PropTypes.string.isRequired,
|
||||
folderColor: PropTypes.string,
|
||||
|
||||
@@ -60,6 +60,7 @@
|
||||
border-bottom-right-radius 2px
|
||||
height 34px
|
||||
line-height 32px
|
||||
transition-property opacity
|
||||
|
||||
.folderList-item:hover, .folderList-item--active:hover
|
||||
.folderList-item-tooltip
|
||||
@@ -120,59 +121,28 @@ body[data-theme="dark"]
|
||||
color $ui-dark-text-color
|
||||
background-color alpha($ui-dark-button--active-backgroundColor, 50%)
|
||||
|
||||
body[data-theme="solarized-dark"]
|
||||
.folderList-item
|
||||
&:hover
|
||||
background-color $ui-solarized-dark-button-backgroundColor
|
||||
color $ui-solarized-dark-text-color
|
||||
&:active
|
||||
color $ui-solarized-dark-text-color
|
||||
background-color $ui-solarized-dark-button-backgroundColor
|
||||
apply-theme(theme)
|
||||
body[data-theme={theme}]
|
||||
.folderList-item
|
||||
&:hover
|
||||
background-color get-theme-var(theme, 'button-backgroundColor')
|
||||
color get-theme-var(theme, 'text-color')
|
||||
&:active
|
||||
color get-theme-var(theme, 'text-color')
|
||||
background-color get-theme-var(theme, 'button-backgroundColor')
|
||||
|
||||
.folderList-item--active
|
||||
@extend .folderList-item
|
||||
color $ui-solarized-dark-text-color
|
||||
background-color $ui-solarized-dark-button-backgroundColor
|
||||
&:active
|
||||
background-color $ui-solarized-dark-button-backgroundColor
|
||||
&:hover
|
||||
color $ui-solarized-dark-text-color
|
||||
background-color $ui-solarized-dark-button-backgroundColor
|
||||
.folderList-item--active
|
||||
@extend .folderList-item
|
||||
color get-theme-var(theme, 'text-color')
|
||||
background-color get-theme-var(theme, 'button-backgroundColor')
|
||||
&:active
|
||||
background-color get-theme-var(theme, 'button-backgroundColor')
|
||||
&:hover
|
||||
color get-theme-var(theme, 'text-color')
|
||||
background-color get-theme-var(theme, 'button-backgroundColor')
|
||||
|
||||
body[data-theme="monokai"]
|
||||
.folderList-item
|
||||
&:hover
|
||||
background-color $ui-monokai-button-backgroundColor
|
||||
color $ui-monokai-text-color
|
||||
&:active
|
||||
color $ui-monokai-text-color
|
||||
background-color $ui-monokai-button-backgroundColor
|
||||
for theme in 'solarized-dark' 'dracula'
|
||||
apply-theme(theme)
|
||||
|
||||
.folderList-item--active
|
||||
@extend .folderList-item
|
||||
color $ui-monokai-text-color
|
||||
background-color $ui-monokai-button-backgroundColor
|
||||
&:active
|
||||
background-color $ui-monokai-button-backgroundColor
|
||||
&:hover
|
||||
color $ui-monokai-text-color
|
||||
background-color $ui-monokai-button-backgroundColor
|
||||
|
||||
body[data-theme="dracula"]
|
||||
.folderList-item
|
||||
&:hover
|
||||
background-color $ui-dracula-button-backgroundColor
|
||||
color $ui-dracula-text-color
|
||||
&:active
|
||||
color $ui-dracula-text-color
|
||||
background-color $ui-dracula-button-backgroundColor
|
||||
|
||||
.folderList-item--active
|
||||
@extend .folderList-item
|
||||
color $ui-dracula-text-color
|
||||
background-color $ui-dracula-button-backgroundColor
|
||||
&:active
|
||||
background-color $ui-dracula-button-backgroundColor
|
||||
&:hover
|
||||
color $ui-dracula-text-color
|
||||
background-color $ui-dracula-button-backgroundColor
|
||||
for theme in $themes
|
||||
apply-theme(theme)
|
||||
@@ -1,18 +1,20 @@
|
||||
/**
|
||||
* @fileoverview Micro component for showing StorageList
|
||||
*/
|
||||
* @fileoverview Micro component for showing StorageList
|
||||
*/
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
import styles from './StorageList.styl'
|
||||
import CSSModules from 'browser/lib/CSSModules'
|
||||
|
||||
/**
|
||||
* @param {Array} storageList
|
||||
*/
|
||||
* @param {Array} storageList
|
||||
*/
|
||||
|
||||
const StorageList = ({storageList, isFolded}) => (
|
||||
const StorageList = ({ storageList, isFolded }) => (
|
||||
<div styleName={isFolded ? 'storageList-folded' : 'storageList'}>
|
||||
{storageList.length > 0 ? storageList : (
|
||||
{storageList.length > 0 ? (
|
||||
storageList
|
||||
) : (
|
||||
<div styleName='storageList-empty'>No storage mount.</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -1,28 +1,58 @@
|
||||
/**
|
||||
* @fileoverview Micro component for showing TagList.
|
||||
*/
|
||||
* @fileoverview Micro component for showing TagList.
|
||||
*/
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
import styles from './TagListItem.styl'
|
||||
import CSSModules from 'browser/lib/CSSModules'
|
||||
|
||||
/**
|
||||
* @param {string} name
|
||||
* @param {Function} handleClickTagListItem
|
||||
* @param {Function} handleClickNarrowToTag
|
||||
* @param {bool} isActive
|
||||
* @param {bool} isRelated
|
||||
*/
|
||||
* @param {string} name
|
||||
* @param {Function} handleClickTagListItem
|
||||
* @param {Function} handleClickNarrowToTag
|
||||
* @param {boolean} isActive
|
||||
* @param {boolean} isRelated
|
||||
* @param {string} bgColor tab backgroundColor
|
||||
*/
|
||||
|
||||
const TagListItem = ({name, handleClickTagListItem, handleClickNarrowToTag, handleContextMenu, isActive, isRelated, count}) => (
|
||||
<div styleName='tagList-itemContainer' onContextMenu={e => handleContextMenu(e, name)}>
|
||||
{isRelated
|
||||
? <button styleName={isActive ? 'tagList-itemNarrow-active' : 'tagList-itemNarrow'} onClick={() => handleClickNarrowToTag(name)}>
|
||||
const TagListItem = ({
|
||||
name,
|
||||
handleClickTagListItem,
|
||||
handleClickNarrowToTag,
|
||||
handleContextMenu,
|
||||
isActive,
|
||||
isRelated,
|
||||
count,
|
||||
color
|
||||
}) => (
|
||||
<div
|
||||
styleName='tagList-itemContainer'
|
||||
onContextMenu={e => handleContextMenu(e, name)}
|
||||
>
|
||||
{isRelated ? (
|
||||
<button
|
||||
styleName={
|
||||
isActive ? 'tagList-itemNarrow-active' : 'tagList-itemNarrow'
|
||||
}
|
||||
onClick={() => handleClickNarrowToTag(name)}
|
||||
>
|
||||
<i className={isActive ? 'fa fa-minus-circle' : 'fa fa-plus-circle'} />
|
||||
</button>
|
||||
: <div styleName={isActive ? 'tagList-itemNarrow-active' : 'tagList-itemNarrow'} />
|
||||
}
|
||||
<button styleName={isActive ? 'tagList-item-active' : 'tagList-item'} onClick={() => handleClickTagListItem(name)}>
|
||||
) : (
|
||||
<div
|
||||
styleName={
|
||||
isActive ? 'tagList-itemNarrow-active' : 'tagList-itemNarrow'
|
||||
}
|
||||
/>
|
||||
)}
|
||||
<button
|
||||
styleName={isActive ? 'tagList-item-active' : 'tagList-item'}
|
||||
onClick={() => handleClickTagListItem(name)}
|
||||
>
|
||||
<span
|
||||
styleName='tagList-item-color'
|
||||
style={{ backgroundColor: color || 'transparent' }}
|
||||
/>
|
||||
<span styleName='tagList-item-name'>
|
||||
{`# ${name}`}
|
||||
<span styleName='tagList-item-count'>{count !== 0 ? count : ''}</span>
|
||||
@@ -33,7 +63,8 @@ const TagListItem = ({name, handleClickTagListItem, handleClickNarrowToTag, hand
|
||||
|
||||
TagListItem.propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
handleClickTagListItem: PropTypes.func.isRequired
|
||||
handleClickTagListItem: PropTypes.func.isRequired,
|
||||
color: PropTypes.string
|
||||
}
|
||||
|
||||
export default CSSModules(TagListItem, styles)
|
||||
|
||||
@@ -71,6 +71,11 @@
|
||||
padding-right 15px
|
||||
font-size 13px
|
||||
|
||||
.tagList-item-color
|
||||
height 26px
|
||||
width 3px
|
||||
display inline-block
|
||||
|
||||
body[data-theme="white"]
|
||||
.tagList-item
|
||||
color $ui-inactive-text-color
|
||||
@@ -89,23 +94,30 @@ body[data-theme="white"]
|
||||
.tagList-item-count
|
||||
color $ui-text-color
|
||||
|
||||
body[data-theme="dark"]
|
||||
.tagList-item
|
||||
color $ui-dark-inactive-text-color
|
||||
&:hover
|
||||
color $ui-dark-text-color
|
||||
background-color alpha($ui-dark-button--active-backgroundColor, 20%)
|
||||
&:active
|
||||
color $ui-dark-text-color
|
||||
background-color $ui-dark-button--active-backgroundColor
|
||||
apply-theme(theme)
|
||||
body[data-theme={theme}]
|
||||
.tagList-item
|
||||
color get-theme-var(theme, 'inactive-text-color')
|
||||
&:hover
|
||||
color get-theme-var(theme, 'text-color')
|
||||
background-color alpha(get-theme-var(theme, 'button--active-backgroundColor'), 20%)
|
||||
&:active
|
||||
color get-theme-var(theme, 'text-color')
|
||||
background-color get-theme-var(theme, 'button--active-backgroundColor')
|
||||
|
||||
.tagList-item-active
|
||||
background-color $ui-dark-button--active-backgroundColor
|
||||
color $ui-dark-text-color
|
||||
&:active
|
||||
background-color alpha($ui-dark-button--active-backgroundColor, 50%)
|
||||
&:hover
|
||||
color $ui-dark-text-color
|
||||
background-color alpha($ui-dark-button--active-backgroundColor, 50%)
|
||||
.tagList-item-count
|
||||
color $ui-dark-button--active-color
|
||||
.tagList-item-active
|
||||
background-color get-theme-var(theme, 'button--active-backgroundColor')
|
||||
color get-theme-var(theme, 'text-color')
|
||||
&:active
|
||||
background-color alpha(get-theme-var(theme, 'button--active-backgroundColor'), 50%)
|
||||
&:hover
|
||||
color get-theme-var(theme, 'text-color')
|
||||
background-color alpha(get-theme-var(theme, 'button--active-backgroundColor'), 50%)
|
||||
.tagList-item-count
|
||||
color get-theme-var(theme, 'button--active-color')
|
||||
|
||||
for theme in 'dark'
|
||||
apply-theme(theme)
|
||||
|
||||
for theme in $themes
|
||||
apply-theme(theme)
|
||||
@@ -11,17 +11,20 @@ import styles from './TodoListPercentage.styl'
|
||||
* @param {number} percentageOfTodo
|
||||
*/
|
||||
|
||||
const TodoListPercentage = ({
|
||||
percentageOfTodo, onClearCheckboxClick
|
||||
}) => (
|
||||
<div styleName='percentageBar' style={{display: isNaN(percentageOfTodo) ? 'none' : ''}}>
|
||||
<div styleName='progressBar' style={{width: `${percentageOfTodo}%`}}>
|
||||
const TodoListPercentage = ({ percentageOfTodo, onClearCheckboxClick }) => (
|
||||
<div
|
||||
styleName='percentageBar'
|
||||
style={{ display: isNaN(percentageOfTodo) ? 'none' : '' }}
|
||||
>
|
||||
<div styleName='progressBar' style={{ width: `${percentageOfTodo}%` }}>
|
||||
<div styleName='progressBarInner'>
|
||||
<p styleName='percentageText'>{percentageOfTodo}%</p>
|
||||
</div>
|
||||
</div>
|
||||
<div styleName='todoClear'>
|
||||
<p styleName='todoClearText' onClick={(e) => onClearCheckboxClick(e)}>clear</p>
|
||||
<p styleName='todoClearText' onClick={e => onClearCheckboxClick(e)}>
|
||||
clear
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -54,7 +54,7 @@ body[data-theme="dark"]
|
||||
|
||||
.percentageText
|
||||
color $ui-dark-text-color
|
||||
|
||||
|
||||
.todoClearText
|
||||
color $ui-dark-text-color
|
||||
|
||||
@@ -71,25 +71,19 @@ body[data-theme="solarized-dark"]
|
||||
.todoClearText
|
||||
color #fdf6e3
|
||||
|
||||
body[data-theme="monokai"]
|
||||
.percentageBar
|
||||
background-color: $ui-monokai-borderColor
|
||||
apply-theme(theme)
|
||||
body[data-theme={theme}]
|
||||
.percentageBar
|
||||
background-color: get-theme-var(theme, 'borderColor')
|
||||
|
||||
.progressBar
|
||||
background-color $ui-monokai-active-color
|
||||
.progressBar
|
||||
background-color get-theme-var(theme, 'active-color')
|
||||
|
||||
.percentageText
|
||||
color $ui-monokai-text-color
|
||||
.percentageText
|
||||
color get-theme-var(theme, 'text-color')
|
||||
|
||||
body[data-theme="dracula"]
|
||||
.percentageBar
|
||||
background-color $ui-dracula-borderColor
|
||||
for theme in 'dracula'
|
||||
apply-theme(theme)
|
||||
|
||||
.progressBar
|
||||
background-color: $ui-dracula-active-color
|
||||
|
||||
.percentageText
|
||||
color $ui-dracula-text-color
|
||||
|
||||
.percentageText
|
||||
color $ui-dracula-text-color
|
||||
for theme in $themes
|
||||
apply-theme(theme)
|
||||
@@ -8,27 +8,30 @@ import CSSModules from 'browser/lib/CSSModules'
|
||||
import styles from './TodoProcess.styl'
|
||||
|
||||
const TodoProcess = ({
|
||||
todoStatus: {
|
||||
total: totalTodo,
|
||||
completed: completedTodo
|
||||
}
|
||||
todoStatus: { total: totalTodo, completed: completedTodo }
|
||||
}) => (
|
||||
<div styleName='todo-process' style={{display: totalTodo > 0 ? '' : 'none'}}>
|
||||
<div
|
||||
styleName='todo-process'
|
||||
style={{ display: totalTodo > 0 ? '' : 'none' }}
|
||||
>
|
||||
<div styleName='todo-process-text'>
|
||||
<i className='fa fa-fw fa-check-square-o' />
|
||||
{completedTodo} of {totalTodo}
|
||||
</div>
|
||||
<div styleName='todo-process-bar'>
|
||||
<div styleName='todo-process-bar--inner' style={{width: parseInt(completedTodo / totalTodo * 100) + '%'}} />
|
||||
<div
|
||||
styleName='todo-process-bar--inner'
|
||||
style={{ width: parseInt((completedTodo / totalTodo) * 100) + '%' }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
TodoProcess.propTypes = {
|
||||
todoStatus: {
|
||||
todoStatus: PropTypes.exact({
|
||||
total: PropTypes.number.isRequired,
|
||||
completed: PropTypes.number.isRequired
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export default CSSModules(TodoProcess, styles)
|
||||
|
||||
@@ -61,6 +61,8 @@ body
|
||||
// allow inline line breaks
|
||||
.katex
|
||||
white-space initial
|
||||
.katex .katex-html
|
||||
display inline-flex
|
||||
.katex .mfrac>.vlist>span:nth-child(2)
|
||||
top 0 !important
|
||||
.katex-error
|
||||
@@ -122,40 +124,34 @@ hr
|
||||
border-bottom solid 1px borderColor
|
||||
margin 15px 0
|
||||
h1, h2, h3, h4, h5, h6
|
||||
margin 1em 0 1.5em
|
||||
line-height 1.4
|
||||
font-weight bold
|
||||
word-wrap break-word
|
||||
padding .2em 0 .2em
|
||||
h1
|
||||
font-size 2.55em
|
||||
padding-bottom 0.3em
|
||||
line-height 1.2em
|
||||
line-height 1.2
|
||||
border-bottom solid 1px borderColor
|
||||
margin 1em 0 0.44em
|
||||
&:first-child
|
||||
margin-top 0
|
||||
h2
|
||||
font-size 1.75em
|
||||
padding-bottom 0.3em
|
||||
line-height 1.225em
|
||||
line-height 1.225
|
||||
border-bottom solid 1px borderColor
|
||||
margin 1em 0 0.57em
|
||||
&:first-child
|
||||
margin-top 0
|
||||
h3
|
||||
font-size 1.5em
|
||||
line-height 1.43em
|
||||
margin 1em 0 0.66em
|
||||
line-height 1.43
|
||||
h4
|
||||
font-size 1.25em
|
||||
line-height 1.4em
|
||||
margin 1em 0 0.8em
|
||||
line-height 1.4
|
||||
h5
|
||||
font-size 1em
|
||||
line-height 1.4em
|
||||
margin 1em 0 1em
|
||||
line-height 1.1
|
||||
h6
|
||||
font-size 1em
|
||||
line-height 1.4em
|
||||
margin 1em 0 1em
|
||||
color #777
|
||||
p
|
||||
line-height 1.6em
|
||||
@@ -163,6 +159,7 @@ p
|
||||
white-space pre-line
|
||||
word-wrap break-word
|
||||
img
|
||||
cursor zoom-in
|
||||
max-width 100%
|
||||
strong, b
|
||||
font-weight bold
|
||||
@@ -360,7 +357,10 @@ admonition_types = {
|
||||
danger: {color: #c2185b, icon: "block"},
|
||||
caution: {color: #ffa726, icon: "warning"},
|
||||
error: {color: #d32f2f, icon: "error_outline"},
|
||||
attention: {color: #455a64, icon: "priority_high"}
|
||||
question: {color: #64dd17, icon: "help_outline"},
|
||||
quote: {color: #9e9e9e, icon: "format_quote"},
|
||||
abstract: {color: #00b0ff, icon: "subject"},
|
||||
attention: {color: #455a64, icon: "priority_high"},
|
||||
}
|
||||
|
||||
for name, val in admonition_types
|
||||
@@ -421,6 +421,9 @@ pre.fence
|
||||
canvas, svg
|
||||
max-width 100% !important
|
||||
|
||||
svg[ratio]
|
||||
width 100%
|
||||
|
||||
.gallery
|
||||
width 100%
|
||||
height 50vh
|
||||
@@ -441,6 +444,44 @@ pre.fence
|
||||
color $ui-text-color
|
||||
background-color $ui-tag-backgroundColor
|
||||
|
||||
.markdownIt-TOC-wrapper
|
||||
list-style none
|
||||
position fixed
|
||||
right 0
|
||||
top 0
|
||||
margin-left 15px
|
||||
z-index 1000
|
||||
transition transform .2s ease-in-out
|
||||
transform translateX(100%)
|
||||
|
||||
.markdownIt-TOC
|
||||
display block
|
||||
max-height 90vh
|
||||
overflow-y auto
|
||||
padding 25px
|
||||
padding-left 38px
|
||||
|
||||
&,
|
||||
&:before
|
||||
background-color $ui-dark-backgroundColor
|
||||
color: $ui-dark-text-color
|
||||
|
||||
&:hover
|
||||
transform translateX(-15px)
|
||||
|
||||
&:before
|
||||
content 'TOC'
|
||||
position absolute
|
||||
width 60px
|
||||
height 30px
|
||||
top 60px
|
||||
left -29px
|
||||
display flex
|
||||
align-items center
|
||||
justify-content center
|
||||
transform-origin top left
|
||||
transform rotate(-90deg)
|
||||
|
||||
themeDarkBackground = darken(#21252B, 10%)
|
||||
themeDarkText = #f9f9f9
|
||||
themeDarkBorder = lighten(themeDarkBackground, 20%)
|
||||
@@ -508,137 +549,63 @@ body[data-theme="dark"]
|
||||
color $ui-dark-text-color
|
||||
background-color $ui-dark-tag-backgroundColor
|
||||
|
||||
themeSolarizedDarkTableOdd = $ui-solarized-dark-noteDetail-backgroundColor
|
||||
themeSolarizedDarkTableEven = darken($ui-solarized-dark-noteDetail-backgroundColor, 10%)
|
||||
themeSolarizedDarkTableHead = themeSolarizedDarkTableEven
|
||||
themeSolarizedDarkTableBorder = themeDarkBorder
|
||||
.markdownIt-TOC-wrapper
|
||||
&,
|
||||
&:before
|
||||
background-color darken(themeDarkBackground, 5%)
|
||||
color themeDarkText
|
||||
|
||||
body[data-theme="solarized-dark"]
|
||||
color $ui-solarized-dark-text-color
|
||||
border-color themeDarkBorder
|
||||
background-color $ui-solarized-dark-noteDetail-backgroundColor
|
||||
table
|
||||
thead
|
||||
tr
|
||||
background-color themeSolarizedDarkTableHead
|
||||
th
|
||||
border-color themeSolarizedDarkTableBorder
|
||||
&:last-child
|
||||
border-right solid 1px themeSolarizedDarkTableBorder
|
||||
tbody
|
||||
tr:nth-child(2n + 1)
|
||||
background-color themeSolarizedDarkTableOdd
|
||||
tr:nth-child(2n)
|
||||
background-color themeSolarizedDarkTableEven
|
||||
td
|
||||
border-color themeSolarizedDarkTableBorder
|
||||
&:last-child
|
||||
border-right solid 1px themeSolarizedDarkTableBorder
|
||||
dl
|
||||
apply-theme(theme)
|
||||
body[data-theme={theme}]
|
||||
color get-theme-var(theme, 'text-color')
|
||||
border-color themeDarkBorder
|
||||
background-color themeSolarizedDarkTableHead
|
||||
dt
|
||||
border-color themeDarkBorder
|
||||
dd
|
||||
border-color themeDarkBorder
|
||||
background-color $ui-solarized-dark-noteDetail-backgroundColor
|
||||
background-color get-theme-var(theme, 'noteDetail-backgroundColor')
|
||||
table
|
||||
thead
|
||||
tr
|
||||
background-color get-theme-var(theme, 'table-head-backgroundColor')
|
||||
th
|
||||
border-color get-theme-var(theme, 'table-borderColor')
|
||||
&:last-child
|
||||
border-right solid 1px get-theme-var(theme, 'table-borderColor')
|
||||
tbody
|
||||
tr:nth-child(2n + 1)
|
||||
background-color get-theme-var(theme, 'table-odd-backgroundColor')
|
||||
tr:nth-child(2n)
|
||||
background-color get-theme-var(theme, 'table-even-backgroundColor')
|
||||
td
|
||||
border-color get-theme-var(theme, 'table-borderColor')
|
||||
&:last-child
|
||||
border-right solid 1px get-theme-var(theme, 'table-borderColor')
|
||||
kbd
|
||||
background-color get-theme-var(theme, 'kbd-backgroundColor')
|
||||
color get-theme-var(theme, 'kbd-color')
|
||||
|
||||
pre.fence
|
||||
.gallery
|
||||
.carousel-main, .carousel-footer
|
||||
background-color $ui-solarized-dark-noteDetail-backgroundColor
|
||||
.prev, .next
|
||||
color $ui-solarized-dark-button--active-color
|
||||
background-color $ui-solarized-dark-button-backgroundColor
|
||||
dl
|
||||
border-color themeDarkBorder
|
||||
background-color get-theme-var(theme, 'table-head-backgroundColor')
|
||||
dt
|
||||
border-color themeDarkBorder
|
||||
dd
|
||||
border-color themeDarkBorder
|
||||
background-color get-theme-var(theme, 'noteDetail-backgroundColor')
|
||||
|
||||
themeMonokaiTableOdd = $ui-monokai-noteDetail-backgroundColor
|
||||
themeMonokaiTableEven = darken($ui-monokai-noteDetail-backgroundColor, 10%)
|
||||
themeMonokaiTableHead = themeMonokaiTableEven
|
||||
themeMonokaiTableBorder = themeDarkBorder
|
||||
pre.fence
|
||||
.gallery
|
||||
.carousel-main, .carousel-footer
|
||||
background-color get-theme-var(theme, 'noteDetail-backgroundColor')
|
||||
.prev, .next
|
||||
color get-theme-var(theme, 'button--active-color')
|
||||
background-color get-theme-var(theme, 'button-backgroundColor')
|
||||
|
||||
body[data-theme="monokai"]
|
||||
color $ui-monokai-text-color
|
||||
border-color themeDarkBorder
|
||||
background-color $ui-monokai-noteDetail-backgroundColor
|
||||
table
|
||||
thead
|
||||
tr
|
||||
background-color themeMonokaiTableHead
|
||||
th
|
||||
border-color themeMonokaiTableBorder
|
||||
&:last-child
|
||||
border-right solid 1px themeMonokaiTableBorder
|
||||
tbody
|
||||
tr:nth-child(2n + 1)
|
||||
background-color themeMonokaiTableOdd
|
||||
tr:nth-child(2n)
|
||||
background-color themeMonokaiTableEven
|
||||
td
|
||||
border-color themeMonokaiTableBorder
|
||||
&:last-child
|
||||
border-right solid 1px themeMonokaiTableBorder
|
||||
kbd
|
||||
background-color themeDarkBackground
|
||||
.markdownIt-TOC-wrapper
|
||||
&,
|
||||
&:before
|
||||
background-color darken(get-theme-var(theme, 'noteDetail-backgroundColor'), 15%)
|
||||
color themeDarkText
|
||||
|
||||
dl
|
||||
border-color themeDarkBorder
|
||||
background-color themeMonokaiTableHead
|
||||
dt
|
||||
border-color themeDarkBorder
|
||||
dd
|
||||
border-color themeDarkBorder
|
||||
background-color $ui-monokai-noteDetail-backgroundColor
|
||||
for theme in 'solarized-dark' 'dracula'
|
||||
apply-theme(theme)
|
||||
|
||||
pre.fence
|
||||
.gallery
|
||||
.carousel-main, .carousel-footer
|
||||
background-color $ui-monokai-noteDetail-backgroundColor
|
||||
.prev, .next
|
||||
color $ui-monokai-button--active-color
|
||||
background-color $ui-monokai-button-backgroundColor
|
||||
|
||||
themeDraculaTableOdd = $ui-dracula-noteDetail-backgroundColor
|
||||
themeDraculaTableEven = darken($ui-dracula-noteDetail-backgroundColor, 10%)
|
||||
themeDraculaTableHead = themeDraculaTableEven
|
||||
themeDraculaTableBorder = themeDarkBorder
|
||||
|
||||
body[data-theme="dracula"]
|
||||
color $ui-dracula-text-color
|
||||
border-color themeDarkBorder
|
||||
background-color $ui-dracula-noteDetail-backgroundColor
|
||||
table
|
||||
thead
|
||||
tr
|
||||
background-color themeDraculaTableHead
|
||||
th
|
||||
border-color themeDraculaTableBorder
|
||||
&:last-child
|
||||
border-right solid 1px themeDraculaTableBorder
|
||||
tbody
|
||||
tr:nth-child(2n + 1)
|
||||
background-color themeDraculaTableOdd
|
||||
tr:nth-child(2n)
|
||||
background-color themeDraculaTableEven
|
||||
td
|
||||
border-color themeDraculaTableBorder
|
||||
&:last-child
|
||||
border-right solid 1px themeDraculaTableBorder
|
||||
kbd
|
||||
background-color themeDarkBackground
|
||||
|
||||
dl
|
||||
border-color themeDarkBorder
|
||||
background-color themeDraculaTableHead
|
||||
dt
|
||||
border-color themeDarkBorder
|
||||
dd
|
||||
border-color themeDarkBorder
|
||||
background-color $ui-dracula-noteDetail-backgroundColor
|
||||
|
||||
pre.fence
|
||||
.gallery
|
||||
.carousel-main, .carousel-footer
|
||||
background-color $ui-dracula-noteDetail-backgroundColor
|
||||
.prev, .next
|
||||
color $ui-dracula-button--active-color
|
||||
background-color $ui-dracula-button-backgroundColor
|
||||
for theme in $themes
|
||||
apply-theme(theme)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import mermaidAPI from 'mermaid'
|
||||
import uiThemes from 'browser/lib/ui-themes'
|
||||
|
||||
// fixes bad styling in the mermaid dark theme
|
||||
const darkThemeStyling = `
|
||||
@@ -6,11 +7,11 @@ const darkThemeStyling = `
|
||||
fill: white;
|
||||
}`
|
||||
|
||||
function getRandomInt (min, max) {
|
||||
function getRandomInt(min, max) {
|
||||
return Math.floor(Math.random() * (max - min)) + min
|
||||
}
|
||||
|
||||
function getId () {
|
||||
function getId() {
|
||||
const pool = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
|
||||
let id = 'm-'
|
||||
for (let i = 0; i < 7; i++) {
|
||||
@@ -19,20 +20,48 @@ function getId () {
|
||||
return id
|
||||
}
|
||||
|
||||
function render (element, content, theme) {
|
||||
function render(element, content, theme, enableHTMLLabel) {
|
||||
try {
|
||||
const height = element.attributes.getNamedItem('data-height')
|
||||
if (height && height.value !== 'undefined') {
|
||||
const isPredefined = height && height.value !== 'undefined'
|
||||
|
||||
if (isPredefined) {
|
||||
element.style.height = height.value + 'vh'
|
||||
}
|
||||
const isDarkTheme = theme === 'dark' || theme === 'solarized-dark' || theme === 'monokai' || theme === 'dracula'
|
||||
|
||||
const isDarkTheme = uiThemes.some(
|
||||
item => item.name === theme && item.isDark
|
||||
)
|
||||
|
||||
mermaidAPI.initialize({
|
||||
theme: isDarkTheme ? 'dark' : 'default',
|
||||
themeCSS: isDarkTheme ? darkThemeStyling : '',
|
||||
useMaxWidth: false
|
||||
flowchart: {
|
||||
htmlLabels: enableHTMLLabel
|
||||
},
|
||||
gantt: {
|
||||
useWidth: element.clientWidth
|
||||
}
|
||||
})
|
||||
mermaidAPI.render(getId(), content, (svgGraph) => {
|
||||
|
||||
mermaidAPI.render(getId(), content, svgGraph => {
|
||||
element.innerHTML = svgGraph
|
||||
|
||||
if (!isPredefined) {
|
||||
const el = element.firstChild
|
||||
const viewBox = el.getAttribute('viewBox').split(' ')
|
||||
|
||||
let ratio = viewBox[2] / viewBox[3]
|
||||
|
||||
if (el.style.maxWidth) {
|
||||
const maxWidth = parseFloat(el.style.maxWidth)
|
||||
|
||||
ratio *= el.parentNode.clientWidth / maxWidth
|
||||
}
|
||||
|
||||
el.setAttribute('ratio', ratio)
|
||||
el.setAttribute('height', el.parentNode.clientWidth / ratio)
|
||||
}
|
||||
})
|
||||
} catch (e) {
|
||||
element.className = 'mermaid-error'
|
||||
|
||||
Reference in New Issue
Block a user