mirror of
https://github.com/BoostIo/Boostnote
synced 2025-12-13 09:46:22 +00:00
Merge branch 'master' into fixIssue2534
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable camelcase */
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
import CSSModules from 'browser/lib/CSSModules'
|
||||
@@ -29,20 +30,23 @@ class MarkdownEditor extends React.Component {
|
||||
isLocked: props.isLocked
|
||||
}
|
||||
|
||||
this.lockEditorCode = () => this.handleLockEditor()
|
||||
this.lockEditorCode = this.handleLockEditor.bind(this)
|
||||
this.focusEditor = this.focusEditor.bind(this)
|
||||
|
||||
this.previewRef = React.createRef()
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.value = this.refs.code.value
|
||||
eventEmitter.on('editor:lock', this.lockEditorCode)
|
||||
eventEmitter.on('editor:focus', this.focusEditor.bind(this))
|
||||
eventEmitter.on('editor:focus', this.focusEditor)
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
this.value = this.refs.code.value
|
||||
}
|
||||
|
||||
componentWillReceiveProps(props) {
|
||||
UNSAFE_componentWillReceiveProps(props) {
|
||||
if (props.value !== this.props.value) {
|
||||
this.queueRendering(props.value)
|
||||
}
|
||||
@@ -51,7 +55,7 @@ class MarkdownEditor extends React.Component {
|
||||
componentWillUnmount() {
|
||||
this.cancelQueue()
|
||||
eventEmitter.off('editor:lock', this.lockEditorCode)
|
||||
eventEmitter.off('editor:focus', this.focusEditor.bind(this))
|
||||
eventEmitter.off('editor:focus', this.focusEditor)
|
||||
}
|
||||
|
||||
focusEditor() {
|
||||
@@ -60,6 +64,9 @@ class MarkdownEditor extends React.Component {
|
||||
status: 'CODE'
|
||||
},
|
||||
() => {
|
||||
if (this.refs.code == null) {
|
||||
return
|
||||
}
|
||||
this.refs.code.focus()
|
||||
}
|
||||
)
|
||||
@@ -104,7 +111,7 @@ class MarkdownEditor extends React.Component {
|
||||
if (newStatus === 'CODE') {
|
||||
this.refs.code.focus()
|
||||
} else {
|
||||
this.refs.preview.focus()
|
||||
this.previewRef.current.focus()
|
||||
}
|
||||
eventEmitter.emit('topbar:togglelockbutton', this.state.status)
|
||||
|
||||
@@ -131,8 +138,8 @@ class MarkdownEditor extends React.Component {
|
||||
status: 'PREVIEW'
|
||||
},
|
||||
() => {
|
||||
this.refs.preview.focus()
|
||||
this.refs.preview.scrollToRow(cursorPosition.line)
|
||||
this.previewRef.current.focus()
|
||||
this.previewRef.current.scrollToRow(cursorPosition.line)
|
||||
}
|
||||
)
|
||||
eventEmitter.emit('topbar:togglelockbutton', this.state.status)
|
||||
@@ -379,6 +386,7 @@ class MarkdownEditor extends React.Component {
|
||||
RTL={RTL}
|
||||
/>
|
||||
<MarkdownPreview
|
||||
ref={this.previewRef}
|
||||
styleName={
|
||||
this.state.status === 'PREVIEW' ? 'preview' : 'preview--hide'
|
||||
}
|
||||
@@ -397,7 +405,6 @@ class MarkdownEditor extends React.Component {
|
||||
breaks={config.preview.breaks}
|
||||
sanitize={config.preview.sanitize}
|
||||
mermaidHTMLLabel={config.preview.mermaidHTMLLabel}
|
||||
ref='preview'
|
||||
onContextMenu={e => this.handleContextMenu(e)}
|
||||
onDoubleClick={e => this.handleDoubleClick(e)}
|
||||
tabIndex='0'
|
||||
|
||||
@@ -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'
|
||||
@@ -21,6 +22,7 @@ import { escapeHtmlCharacters } from 'browser/lib/utils'
|
||||
import yaml from 'js-yaml'
|
||||
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 i18n from 'browser/lib/i18n'
|
||||
@@ -252,7 +254,7 @@ function getSourceLineNumberByElement(element) {
|
||||
return parent.dataset.line !== undefined ? parseInt(parent.dataset.line) : -1
|
||||
}
|
||||
|
||||
export default class MarkdownPreview extends React.Component {
|
||||
class MarkdownPreview extends React.Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
@@ -1116,6 +1118,7 @@ export default class MarkdownPreview extends React.Component {
|
||||
e.stopPropagation()
|
||||
|
||||
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 parser = document.createElement('a')
|
||||
@@ -1169,6 +1172,13 @@ 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
|
||||
this.openExternal(href)
|
||||
}
|
||||
@@ -1213,3 +1223,10 @@ MarkdownPreview.propTypes = {
|
||||
smartArrows: PropTypes.bool,
|
||||
breaks: PropTypes.bool
|
||||
}
|
||||
|
||||
export default connect(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
{ forwardRef: true }
|
||||
)(MarkdownPreview)
|
||||
|
||||
@@ -215,6 +215,7 @@ class MarkdownSplitEditor extends React.Component {
|
||||
<div styleName='slider-hitbox' />
|
||||
</div>
|
||||
<MarkdownPreview
|
||||
ref='preview'
|
||||
style={previewStyle}
|
||||
theme={config.ui.theme}
|
||||
keyMap={config.editor.keyMap}
|
||||
@@ -229,7 +230,6 @@ class MarkdownSplitEditor extends React.Component {
|
||||
breaks={config.preview.breaks}
|
||||
sanitize={config.preview.sanitize}
|
||||
mermaidHTMLLabel={config.preview.mermaidHTMLLabel}
|
||||
ref='preview'
|
||||
tabInde='0'
|
||||
value={value}
|
||||
onCheckboxClick={e => this.handleCheckboxClick(e)}
|
||||
|
||||
@@ -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,6 +53,7 @@ const StorageItem = ({
|
||||
<button
|
||||
styleName={isActive ? 'folderList-item--active' : 'folderList-item'}
|
||||
onClick={handleButtonClick}
|
||||
onMouseEnter={handleMouseEnter}
|
||||
onContextMenu={handleContextMenu}
|
||||
onDrop={handleDrop}
|
||||
onDragEnter={handleDragEnter}
|
||||
@@ -75,7 +80,9 @@ const StorageItem = ({
|
||||
<span styleName='folderList-item-noteCount'>{noteCount}</span>
|
||||
)}
|
||||
{isFolded && (
|
||||
<span styleName='folderList-item-tooltip'>{folderName}</span>
|
||||
<span styleName='folderList-item-tooltip' ref={tooltipRef}>
|
||||
{folderName}
|
||||
</span>
|
||||
)}
|
||||
</button>
|
||||
)
|
||||
@@ -83,7 +90,9 @@ const StorageItem = ({
|
||||
|
||||
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
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
.storageList
|
||||
margin-bottom 37px
|
||||
absolute left right
|
||||
bottom 37px
|
||||
top 180px
|
||||
overflow-y auto
|
||||
|
||||
.storageList-folded
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable camelcase */
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
import CSSModules from 'browser/lib/CSSModules'
|
||||
@@ -57,7 +58,8 @@ class MarkdownNoteDetail extends React.Component {
|
||||
this.dispatchTimer = null
|
||||
|
||||
this.toggleLockButton = this.handleToggleLockButton.bind(this)
|
||||
this.generateToc = () => this.handleGenerateToc()
|
||||
this.generateToc = this.handleGenerateToc.bind(this)
|
||||
this.handleUpdateContent = this.handleUpdateContent.bind(this)
|
||||
}
|
||||
|
||||
focus() {
|
||||
@@ -76,7 +78,7 @@ class MarkdownNoteDetail extends React.Component {
|
||||
ee.on('code:generate-toc', this.generateToc)
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
UNSAFE_componentWillReceiveProps(nextProps) {
|
||||
const isNewNote = nextProps.note.key !== this.props.note.key
|
||||
const hasDeletedTags =
|
||||
nextProps.note.tags.length < this.props.note.tags.length
|
||||
@@ -392,6 +394,9 @@ class MarkdownNoteDetail extends React.Component {
|
||||
}
|
||||
|
||||
handleSwitchDirection() {
|
||||
if (!this.props.config.editor.rtlEnabled) {
|
||||
return
|
||||
}
|
||||
// If in split mode, hide the lock button
|
||||
const direction = this.state.RTL
|
||||
this.setState({ RTL: !direction })
|
||||
@@ -436,10 +441,10 @@ class MarkdownNoteDetail extends React.Component {
|
||||
storageKey={note.storage}
|
||||
noteKey={note.key}
|
||||
linesHighlighted={note.linesHighlighted}
|
||||
onChange={this.handleUpdateContent.bind(this)}
|
||||
onChange={this.handleUpdateContent}
|
||||
isLocked={this.state.isLocked}
|
||||
ignorePreviewPointerEvents={ignorePreviewPointerEvents}
|
||||
RTL={this.state.RTL}
|
||||
RTL={config.editor.rtlEnabled && this.state.RTL}
|
||||
/>
|
||||
)
|
||||
} else {
|
||||
@@ -451,9 +456,9 @@ class MarkdownNoteDetail extends React.Component {
|
||||
storageKey={note.storage}
|
||||
noteKey={note.key}
|
||||
linesHighlighted={note.linesHighlighted}
|
||||
onChange={this.handleUpdateContent.bind(this)}
|
||||
onChange={this.handleUpdateContent}
|
||||
ignorePreviewPointerEvents={ignorePreviewPointerEvents}
|
||||
RTL={this.state.RTL}
|
||||
RTL={config.editor.rtlEnabled && this.state.RTL}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -536,10 +541,12 @@ class MarkdownNoteDetail extends React.Component {
|
||||
onClick={e => this.handleSwitchMode(e)}
|
||||
editorType={editorType}
|
||||
/>
|
||||
<ToggleDirectionButton
|
||||
onClick={e => this.handleSwitchDirection(e)}
|
||||
isRTL={this.state.RTL}
|
||||
/>
|
||||
{this.props.config.editor.rtlEnabled && (
|
||||
<ToggleDirectionButton
|
||||
onClick={e => this.handleSwitchDirection(e)}
|
||||
isRTL={this.state.RTL}
|
||||
/>
|
||||
)}
|
||||
<StarButton
|
||||
onClick={e => this.handleStarButtonClick(e)}
|
||||
isActive={note.isStarred}
|
||||
|
||||
@@ -20,7 +20,7 @@ const ToggleDirectionButton = ({ onClick, isRTL }) => (
|
||||
|
||||
ToggleDirectionButton.propTypes = {
|
||||
onClick: PropTypes.func.isRequired,
|
||||
isRTL: PropTypes.string.isRequired
|
||||
isRTL: PropTypes.bool.isRequired
|
||||
}
|
||||
|
||||
export default CSSModules(ToggleDirectionButton, styles)
|
||||
|
||||
@@ -183,6 +183,7 @@ class Main extends React.Component {
|
||||
'menubar:togglemenubar',
|
||||
this.toggleMenuBarVisible.bind(this)
|
||||
)
|
||||
eventEmitter.on('dispatch:push', this.changeRoutePush.bind(this))
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
@@ -191,9 +192,15 @@ class Main extends React.Component {
|
||||
'menubar:togglemenubar',
|
||||
this.toggleMenuBarVisible.bind(this)
|
||||
)
|
||||
eventEmitter.off('dispatch:push', this.changeRoutePush.bind(this))
|
||||
clearInterval(this.refreshTheme)
|
||||
}
|
||||
|
||||
changeRoutePush(event, destination) {
|
||||
const { dispatch } = this.props
|
||||
dispatch(push(destination))
|
||||
}
|
||||
|
||||
toggleMenuBarVisible() {
|
||||
const { config } = this.props
|
||||
const { ui } = config
|
||||
|
||||
@@ -144,6 +144,15 @@ class StorageItem extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
handleFolderMouseEnter(e, tooltipRef, isFolded) {
|
||||
if (isFolded) {
|
||||
const buttonEl = e.currentTarget
|
||||
const tooltipEl = tooltipRef.current
|
||||
|
||||
tooltipEl.style.top = buttonEl.getBoundingClientRect().y + 'px'
|
||||
}
|
||||
}
|
||||
|
||||
handleFolderButtonContextMenu(e, folder) {
|
||||
context.popup([
|
||||
{
|
||||
@@ -316,6 +325,7 @@ class StorageItem extends React.Component {
|
||||
folder.key
|
||||
)
|
||||
const isActive = !!location.pathname.match(folderRegex)
|
||||
const tooltipRef = React.createRef(null)
|
||||
const noteSet = folderNoteMap.get(storage.key + '-' + folder.key)
|
||||
|
||||
let noteCount = 0
|
||||
@@ -339,7 +349,11 @@ class StorageItem extends React.Component {
|
||||
key={folder.key}
|
||||
index={index}
|
||||
isActive={isActive || folder.key === this.state.draggedOver}
|
||||
tooltipRef={tooltipRef}
|
||||
handleButtonClick={e => this.handleFolderButtonClick(folder.key)(e)}
|
||||
handleMouseEnter={e =>
|
||||
this.handleFolderMouseEnter(e, tooltipRef, isFolded)
|
||||
}
|
||||
handleContextMenu={e => this.handleFolderButtonContextMenu(e, folder)}
|
||||
folderName={folder.name}
|
||||
folderColor={folder.color}
|
||||
|
||||
@@ -32,7 +32,7 @@ export const DEFAULT_CONFIG = {
|
||||
hotkey: {
|
||||
toggleMain: OSX ? 'Command + Alt + L' : 'Super + Alt + E',
|
||||
toggleMode: OSX ? 'Command + Alt + M' : 'Ctrl + M',
|
||||
toggleDirection: OSX ? 'Command + Alt + Right' : 'Ctrl + Right',
|
||||
toggleDirection: OSX ? 'Command + Alt + Right' : 'Ctrl + Alt + Right',
|
||||
deleteNote: OSX
|
||||
? 'Command + Shift + Backspace'
|
||||
: 'Ctrl + Shift + Backspace',
|
||||
@@ -89,7 +89,8 @@ export const DEFAULT_CONFIG = {
|
||||
"semi": false,
|
||||
"singleQuote": true
|
||||
}`,
|
||||
deleteUnusedAttachments: true
|
||||
deleteUnusedAttachments: true,
|
||||
rtlEnabled: false
|
||||
},
|
||||
preview: {
|
||||
fontSize: '14',
|
||||
|
||||
@@ -133,7 +133,8 @@ class UiTab extends React.Component {
|
||||
.getCodeMirror()
|
||||
.getValue(),
|
||||
prettierConfig: this.prettierConfigCM.getCodeMirror().getValue(),
|
||||
deleteUnusedAttachments: this.refs.deleteUnusedAttachments.checked
|
||||
deleteUnusedAttachments: this.refs.deleteUnusedAttachments.checked,
|
||||
rtlEnabled: this.refs.rtlEnabled.checked
|
||||
},
|
||||
preview: {
|
||||
fontSize: this.refs.previewFontSize.value,
|
||||
@@ -861,6 +862,18 @@ class UiTab extends React.Component {
|
||||
)}
|
||||
</label>
|
||||
</div>
|
||||
<div styleName='group-checkBoxSection'>
|
||||
<label>
|
||||
<input
|
||||
onChange={e => this.handleUIChange(e)}
|
||||
checked={this.state.config.editor.rtlEnabled}
|
||||
ref='rtlEnabled'
|
||||
type='checkbox'
|
||||
/>
|
||||
|
||||
{i18n.__('Enable right to left direction(RTL)')}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div styleName='group-section'>
|
||||
<div styleName='group-section-label'>
|
||||
|
||||
Reference in New Issue
Block a user