mirror of
https://github.com/BoostIo/Boostnote
synced 2026-01-06 13:39:19 +00:00
merge from upstream/master
This commit is contained in:
69
browser/main/Detail/FromUrlButton.js
Normal file
69
browser/main/Detail/FromUrlButton.js
Normal file
@@ -0,0 +1,69 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
import CSSModules from 'browser/lib/CSSModules'
|
||||
import styles from './FromUrlButton.styl'
|
||||
import _ from 'lodash'
|
||||
import i18n from 'browser/lib/i18n'
|
||||
|
||||
class FromUrlButton extends React.Component {
|
||||
constructor (props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
isActive: false
|
||||
}
|
||||
}
|
||||
|
||||
handleMouseDown (e) {
|
||||
this.setState({
|
||||
isActive: true
|
||||
})
|
||||
}
|
||||
|
||||
handleMouseUp (e) {
|
||||
this.setState({
|
||||
isActive: false
|
||||
})
|
||||
}
|
||||
|
||||
handleMouseLeave (e) {
|
||||
this.setState({
|
||||
isActive: false
|
||||
})
|
||||
}
|
||||
|
||||
render () {
|
||||
const { className } = this.props
|
||||
|
||||
return (
|
||||
<button className={_.isString(className)
|
||||
? 'FromUrlButton ' + className
|
||||
: 'FromUrlButton'
|
||||
}
|
||||
styleName={this.state.isActive || this.props.isActive
|
||||
? 'root--active'
|
||||
: 'root'
|
||||
}
|
||||
onMouseDown={(e) => this.handleMouseDown(e)}
|
||||
onMouseUp={(e) => this.handleMouseUp(e)}
|
||||
onMouseLeave={(e) => this.handleMouseLeave(e)}
|
||||
onClick={this.props.onClick}>
|
||||
<img styleName='icon'
|
||||
src={this.state.isActive || this.props.isActive
|
||||
? '../resources/icon/icon-external.svg'
|
||||
: '../resources/icon/icon-external.svg'
|
||||
}
|
||||
/>
|
||||
<span styleName='tooltip'>{i18n.__('Convert URL to Markdown')}</span>
|
||||
</button>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
FromUrlButton.propTypes = {
|
||||
isActive: PropTypes.bool,
|
||||
onClick: PropTypes.func,
|
||||
className: PropTypes.string
|
||||
}
|
||||
|
||||
export default CSSModules(FromUrlButton, styles)
|
||||
41
browser/main/Detail/FromUrlButton.styl
Normal file
41
browser/main/Detail/FromUrlButton.styl
Normal file
@@ -0,0 +1,41 @@
|
||||
.root
|
||||
top 45px
|
||||
topBarButtonRight()
|
||||
&:hover
|
||||
transition 0.2s
|
||||
color alpha($ui-favorite-star-button-color, 0.6)
|
||||
&:hover .tooltip
|
||||
opacity 1
|
||||
|
||||
.tooltip
|
||||
tooltip()
|
||||
position absolute
|
||||
pointer-events none
|
||||
top 50px
|
||||
right 125px
|
||||
width 90px
|
||||
z-index 200
|
||||
padding 5px
|
||||
line-height normal
|
||||
border-radius 2px
|
||||
opacity 0
|
||||
transition 0.1s
|
||||
|
||||
.root--active
|
||||
@extend .root
|
||||
transition 0.15s
|
||||
color $ui-favorite-star-button-color
|
||||
&:hover
|
||||
transition 0.2s
|
||||
color alpha($ui-favorite-star-button-color, 0.6)
|
||||
|
||||
.icon
|
||||
transition transform 0.15s
|
||||
height 13px
|
||||
|
||||
body[data-theme="dark"]
|
||||
.root
|
||||
topBarButtonDark()
|
||||
&:hover
|
||||
transition 0.2s
|
||||
color alpha($ui-favorite-star-button-color, 0.6)
|
||||
@@ -11,7 +11,7 @@ const FullscreenButton = ({
|
||||
const hotkey = (OSX ? i18n.__('Command(⌘)') : i18n.__('Ctrl(^)')) + '+B'
|
||||
return (
|
||||
<button styleName='control-fullScreenButton' title={i18n.__('Fullscreen')} onMouseDown={(e) => onClick(e)}>
|
||||
<img styleName='iconInfo' src='../resources/icon/icon-full.svg' />
|
||||
<img src='../resources/icon/icon-full.svg' />
|
||||
<span lang={i18n.locale} styleName='tooltip'>{i18n.__('Fullscreen')}({hotkey})</span>
|
||||
</button>
|
||||
)
|
||||
|
||||
@@ -60,7 +60,7 @@ class InfoPanel extends React.Component {
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<input styleName='infoPanel-noteLink' ref='noteLink' value={noteLink} onClick={(e) => { e.target.select() }} />
|
||||
<input styleName='infoPanel-noteLink' ref='noteLink' defaultValue={noteLink} onClick={(e) => { e.target.select() }} />
|
||||
<button onClick={() => this.copyNoteLink()} styleName='infoPanel-copyButton'>
|
||||
<i className='fa fa-clipboard' />
|
||||
</button>
|
||||
|
||||
@@ -69,9 +69,6 @@ class MarkdownNoteDetail extends React.Component {
|
||||
})
|
||||
ee.on('hotkey:deletenote', this.handleDeleteNote.bind(this))
|
||||
ee.on('code:generate-toc', this.generateToc)
|
||||
|
||||
// Focus content if using blur or double click
|
||||
if (this.state.switchPreview === 'BLUR' || this.state.switchPreview === 'DBL_CLICK') this.focus()
|
||||
}
|
||||
|
||||
componentWillReceiveProps (nextProps) {
|
||||
@@ -86,6 +83,20 @@ class MarkdownNoteDetail extends React.Component {
|
||||
if (this.refs.tags) this.refs.tags.reset()
|
||||
})
|
||||
}
|
||||
|
||||
// Focus content if using blur or double click
|
||||
// --> Moved here from componentDidMount so a re-render during search won't set focus to the editor
|
||||
const {switchPreview} = nextProps.config.editor
|
||||
|
||||
if (this.state.switchPreview !== switchPreview) {
|
||||
this.setState({
|
||||
switchPreview
|
||||
})
|
||||
if (switchPreview === 'BLUR' || switchPreview === 'DBL_CLICK') {
|
||||
console.log('setting focus', switchPreview)
|
||||
this.focus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
@@ -143,7 +154,6 @@ class MarkdownNoteDetail extends React.Component {
|
||||
}
|
||||
|
||||
handleFolderChange (e) {
|
||||
const { dispatch } = this.props
|
||||
const { note } = this.state
|
||||
const value = this.refs.folder.value
|
||||
const splitted = value.split('-')
|
||||
@@ -302,7 +312,7 @@ class MarkdownNoteDetail extends React.Component {
|
||||
}
|
||||
|
||||
getToggleLockButton () {
|
||||
return this.state.isLocked ? '../resources/icon/icon-previewoff-on.svg' : '../resources/icon/icon-previewoff-off.svg'
|
||||
return this.state.isLocked ? '../resources/icon/icon-lock.svg' : '../resources/icon/icon-unlock.svg'
|
||||
}
|
||||
|
||||
handleDeleteKeyDown (e) {
|
||||
@@ -411,7 +421,7 @@ class MarkdownNoteDetail extends React.Component {
|
||||
}
|
||||
|
||||
render () {
|
||||
const { data, location, config } = this.props
|
||||
const { data, dispatch, location, config } = this.props
|
||||
const { note, editorType, isStacking } = this.state
|
||||
const storageKey = note.storage
|
||||
const folderKey = note.folder
|
||||
@@ -451,7 +461,7 @@ class MarkdownNoteDetail extends React.Component {
|
||||
|
||||
const detailTopBar = <div styleName='info'>
|
||||
<div styleName='info-left'>
|
||||
<div styleName='info-left-top'>
|
||||
<div>
|
||||
<FolderSelect styleName='info-left-top-folderSelect'
|
||||
value={this.state.note.storage + '-' + this.state.note.folder}
|
||||
ref='folder'
|
||||
@@ -466,6 +476,7 @@ class MarkdownNoteDetail extends React.Component {
|
||||
saveTagsAlphabetically={config.ui.saveTagsAlphabetically}
|
||||
showTagsAlphabetically={config.ui.showTagsAlphabetically}
|
||||
data={data}
|
||||
dispatch={dispatch}
|
||||
onChange={this.handleUpdateTag.bind(this)}
|
||||
coloredTags={config.coloredTags}
|
||||
/>
|
||||
@@ -478,6 +489,7 @@ class MarkdownNoteDetail extends React.Component {
|
||||
}
|
||||
|
||||
<ToggleModeButton onClick={(e) => this.handleSwitchMode(e)} editorType={editorType} />
|
||||
|
||||
<StarButton
|
||||
onClick={(e) => this.handleStarButtonClick(e)}
|
||||
isActive={note.isStarred}
|
||||
@@ -490,7 +502,7 @@ class MarkdownNoteDetail extends React.Component {
|
||||
onFocus={(e) => this.handleFocus(e)}
|
||||
onMouseDown={(e) => this.handleLockButtonMouseDown(e)}
|
||||
>
|
||||
<img styleName='iconInfo' src={imgSrc} />
|
||||
<img src={imgSrc} />
|
||||
{this.state.isLocked ? <span styleName='tooltip'>Unlock</span> : <span styleName='tooltip'>Lock</span>}
|
||||
</button>
|
||||
|
||||
@@ -517,7 +529,7 @@ class MarkdownNoteDetail extends React.Component {
|
||||
exportAsTxt={this.exportAsTxt}
|
||||
exportAsHtml={this.exportAsHtml}
|
||||
exportAsPdf={this.exportAsPdf}
|
||||
wordCount={note.content.split(' ').length}
|
||||
wordCount={note.content.trim().split(/\s+/g).length}
|
||||
letterCount={note.content.replace(/\r?\n/g, '').length}
|
||||
type={note.type}
|
||||
print={this.print}
|
||||
|
||||
@@ -81,11 +81,4 @@ body[data-theme="dracula"]
|
||||
.root
|
||||
border-left 1px solid $ui-dracula-borderColor
|
||||
background-color $ui-dracula-noteDetail-backgroundColor
|
||||
|
||||
div
|
||||
> button, div
|
||||
-webkit-user-drag none
|
||||
user-select none
|
||||
> img, span
|
||||
-webkit-user-drag none
|
||||
user-select none
|
||||
|
||||
|
||||
@@ -107,4 +107,12 @@ body[data-theme="monokai"]
|
||||
body[data-theme="dracula"]
|
||||
.info
|
||||
border-color $ui-dracula-borderColor
|
||||
background-color $ui-dracula-noteDetail-backgroundColor
|
||||
background-color $ui-dracula-noteDetail-backgroundColor
|
||||
|
||||
.info > div
|
||||
> button
|
||||
-webkit-user-drag none
|
||||
user-select none
|
||||
> img, span
|
||||
-webkit-user-drag none
|
||||
user-select none
|
||||
@@ -10,7 +10,7 @@ const PermanentDeleteButton = ({
|
||||
<button styleName='control-trashButton--in-trash'
|
||||
onClick={(e) => onClick(e)}
|
||||
>
|
||||
<img styleName='iconInfo' src='../resources/icon/icon-trash.svg' />
|
||||
<img src='../resources/icon/icon-trash.svg' />
|
||||
<span styleName='tooltip'>{i18n.__('Permanent Delete')}</span>
|
||||
</button>
|
||||
)
|
||||
|
||||
@@ -518,6 +518,19 @@ class SnippetNoteDetail extends React.Component {
|
||||
])
|
||||
}
|
||||
|
||||
handleWrapLineButtonClick (e) {
|
||||
context.popup([
|
||||
{
|
||||
label: 'on',
|
||||
click: (e) => this.handleWrapLineItemClick(e, true)
|
||||
},
|
||||
{
|
||||
label: 'off',
|
||||
click: (e) => this.handleWrapLineItemClick(e, false)
|
||||
}
|
||||
])
|
||||
}
|
||||
|
||||
handleIndentSizeItemClick (e, indentSize) {
|
||||
const { config, dispatch } = this.props
|
||||
const editor = Object.assign({}, config.editor, {
|
||||
@@ -550,6 +563,22 @@ class SnippetNoteDetail extends React.Component {
|
||||
})
|
||||
}
|
||||
|
||||
handleWrapLineItemClick (e, lineWrapping) {
|
||||
const { config, dispatch } = this.props
|
||||
const editor = Object.assign({}, config.editor, {
|
||||
lineWrapping
|
||||
})
|
||||
ConfigManager.set({
|
||||
editor
|
||||
})
|
||||
dispatch({
|
||||
type: 'SET_CONFIG',
|
||||
config: {
|
||||
editor
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
focus () {
|
||||
this.refs.description.focus()
|
||||
}
|
||||
@@ -670,7 +699,7 @@ class SnippetNoteDetail extends React.Component {
|
||||
}
|
||||
|
||||
render () {
|
||||
const { data, config, location } = this.props
|
||||
const { data, dispatch, config, location } = this.props
|
||||
const { note } = this.state
|
||||
|
||||
const storageKey = note.storage
|
||||
@@ -720,6 +749,7 @@ class SnippetNoteDetail extends React.Component {
|
||||
mode={snippet.mode || (autoDetect ? null : config.editor.snippetDefaultLanguage)}
|
||||
value={snippet.content}
|
||||
linesHighlighted={snippet.linesHighlighted}
|
||||
lineWrapping={config.editor.lineWrapping}
|
||||
theme={config.editor.theme}
|
||||
fontFamily={config.editor.fontFamily}
|
||||
fontSize={editorFontSize}
|
||||
@@ -778,7 +808,7 @@ class SnippetNoteDetail extends React.Component {
|
||||
|
||||
const detailTopBar = <div styleName='info'>
|
||||
<div styleName='info-left'>
|
||||
<div styleName='info-left-top'>
|
||||
<div>
|
||||
<FolderSelect styleName='info-left-top-folderSelect'
|
||||
value={this.state.note.storage + '-' + this.state.note.folder}
|
||||
ref='folder'
|
||||
@@ -793,6 +823,7 @@ class SnippetNoteDetail extends React.Component {
|
||||
saveTagsAlphabetically={config.ui.saveTagsAlphabetically}
|
||||
showTagsAlphabetically={config.ui.showTagsAlphabetically}
|
||||
data={data}
|
||||
dispatch={dispatch}
|
||||
onChange={(e) => this.handleChange(e)}
|
||||
coloredTags={config.coloredTags}
|
||||
/>
|
||||
@@ -899,6 +930,12 @@ class SnippetNoteDetail extends React.Component {
|
||||
size: {config.editor.indentSize}
|
||||
<i className='fa fa-caret-down' />
|
||||
</button>
|
||||
<button
|
||||
onClick={(e) => this.handleWrapLineButtonClick(e)}
|
||||
>
|
||||
Wrap Line: {config.editor.lineWrapping ? 'on' : 'off'}
|
||||
<i className='fa fa-caret-down' />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<StatusBar
|
||||
|
||||
@@ -42,4 +42,4 @@ body[data-theme="dark"]
|
||||
topBarButtonDark()
|
||||
&:hover
|
||||
transition 0.2s
|
||||
color alpha($ui-favorite-star-button-color, 0.6)
|
||||
color alpha($ui-favorite-star-button-color, 0.6)
|
||||
|
||||
@@ -8,6 +8,7 @@ import AwsMobileAnalyticsConfig from 'browser/main/lib/AwsMobileAnalyticsConfig'
|
||||
import i18n from 'browser/lib/i18n'
|
||||
import ee from 'browser/main/lib/eventEmitter'
|
||||
import Autosuggest from 'react-autosuggest'
|
||||
import { push } from 'connected-react-router'
|
||||
|
||||
class TagSelect extends React.Component {
|
||||
constructor (props) {
|
||||
@@ -96,8 +97,11 @@ class TagSelect extends React.Component {
|
||||
}
|
||||
|
||||
handleTagLabelClick (tag) {
|
||||
const { router } = this.context
|
||||
router.push(`/tags/${tag}`)
|
||||
const { dispatch } = this.props
|
||||
|
||||
// Note: `tag` requires encoding later.
|
||||
// E.g. % in tag is a problem (see issue #3170) - encodeURIComponent(tag) is not working.
|
||||
dispatch(push(`/tags/${tag}`))
|
||||
}
|
||||
|
||||
handleTagRemoveButtonClick (tag) {
|
||||
@@ -255,11 +259,8 @@ class TagSelect extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
TagSelect.contextTypes = {
|
||||
router: PropTypes.shape({})
|
||||
}
|
||||
|
||||
TagSelect.propTypes = {
|
||||
dispatch: PropTypes.func,
|
||||
className: PropTypes.string,
|
||||
value: PropTypes.arrayOf(PropTypes.string),
|
||||
onChange: PropTypes.func,
|
||||
|
||||
@@ -8,11 +8,11 @@ const ToggleModeButton = ({
|
||||
onClick, editorType
|
||||
}) => (
|
||||
<div styleName='control-toggleModeButton'>
|
||||
<div styleName={editorType === 'SPLIT' ? 'active' : 'non-active'} onClick={() => onClick('SPLIT')}>
|
||||
<img styleName='item-star' src={editorType === 'EDITOR_PREVIEW' ? '../resources/icon/icon-mode-markdown-off-active.svg' : ''} />
|
||||
<div styleName={editorType === 'SPLIT' ? 'active' : undefined} onClick={() => onClick('SPLIT')}>
|
||||
<img src={editorType === 'EDITOR_PREVIEW' ? '../resources/icon/icon-mode-markdown-off-active.svg' : ''} />
|
||||
</div>
|
||||
<div styleName={editorType === 'EDITOR_PREVIEW' ? 'active' : 'non-active'} onClick={() => onClick('EDITOR_PREVIEW')}>
|
||||
<img styleName='item-star' src={editorType === 'EDITOR_PREVIEW' ? '' : '../resources/icon/icon-mode-split-on-active.svg'} />
|
||||
<div styleName={editorType === 'EDITOR_PREVIEW' ? 'active' : undefined} onClick={() => onClick('EDITOR_PREVIEW')}>
|
||||
<img src={editorType === 'EDITOR_PREVIEW' ? '' : '../resources/icon/icon-mode-split-on-active.svg'} />
|
||||
</div>
|
||||
<span lang={i18n.locale} styleName='tooltip'>{i18n.__('Toggle Mode')}</span>
|
||||
</div>
|
||||
@@ -20,7 +20,7 @@ const ToggleModeButton = ({
|
||||
|
||||
ToggleModeButton.propTypes = {
|
||||
onClick: PropTypes.func.isRequired,
|
||||
editorType: PropTypes.string.Required
|
||||
editorType: PropTypes.string.isRequired
|
||||
}
|
||||
|
||||
export default CSSModules(ToggleModeButton, styles)
|
||||
|
||||
@@ -75,3 +75,10 @@ body[data-theme="dracula"]
|
||||
.active
|
||||
background-color #bd93f9
|
||||
box-shadow 2px 0px 7px #222222
|
||||
|
||||
.control-toggleModeButton
|
||||
-webkit-user-drag none
|
||||
user-select none
|
||||
> div img
|
||||
-webkit-user-drag none
|
||||
user-select none
|
||||
|
||||
@@ -10,7 +10,7 @@ const TrashButton = ({
|
||||
<button styleName='control-trashButton'
|
||||
onClick={(e) => onClick(e)}
|
||||
>
|
||||
<img styleName='iconInfo' src='../resources/icon/icon-trash.svg' />
|
||||
<img src='../resources/icon/icon-trash.svg' />
|
||||
<span lang={i18n.locale} styleName='tooltip'>{i18n.__('Trash')}</span>
|
||||
</button>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user