diff --git a/browser/components/CodeEditor.js b/browser/components/CodeEditor.js index 7080b1fc..bc2719dc 100644 --- a/browser/components/CodeEditor.js +++ b/browser/components/CodeEditor.js @@ -273,7 +273,7 @@ export default class CodeEditor extends React.Component { } componentDidMount () { - const { rulers, enableRulers, enableMarkdownLint } = this.props + const { rulers, enableRulers, enableMarkdownLint, RTL } = this.props eventEmitter.on('line:jump', this.scrollToLineHandeler) snippetManager.init() @@ -294,6 +294,8 @@ export default class CodeEditor extends React.Component { scrollPastEnd: this.props.scrollPastEnd, inputStyle: 'textarea', dragDrop: false, + direction: RTL ? 'rtl' : 'ltr', + rtlMoveVisually: RTL, foldGutter: true, lint: enableMarkdownLint ? this.getCodeEditorLintConfig() : false, gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter', 'CodeMirror-lint-markers'], @@ -555,6 +557,10 @@ export default class CodeEditor extends React.Component { if (prevProps.keyMap !== this.props.keyMap) { needRefresh = true } + if (prevProps.RTL !== this.props.RTL) { + this.editor.setOption('direction', this.props.RTL ? 'rtl' : 'ltr') + this.editor.setOption('rtlMoveVisually', this.props.RTL) + } if (prevProps.enableMarkdownLint !== enableMarkdownLint || prevProps.customMarkdownLintConfig !== customMarkdownLintConfig) { if (!enableMarkdownLint) { this.editor.setOption('lint', {default: false}) @@ -1219,7 +1225,8 @@ CodeEditor.propTypes = { spellCheck: PropTypes.bool, enableMarkdownLint: PropTypes.bool, customMarkdownLintConfig: PropTypes.string, - deleteUnusedAttachments: PropTypes.bool + deleteUnusedAttachments: PropTypes.bool, + RTL: PropTypes.bool } CodeEditor.defaultProps = { @@ -1235,5 +1242,6 @@ CodeEditor.defaultProps = { enableMarkdownLint: DEFAULT_CONFIG.editor.enableMarkdownLint, customMarkdownLintConfig: DEFAULT_CONFIG.editor.customMarkdownLintConfig, prettierConfig: DEFAULT_CONFIG.editor.prettierConfig, - deleteUnusedAttachments: DEFAULT_CONFIG.editor.deleteUnusedAttachments + deleteUnusedAttachments: DEFAULT_CONFIG.editor.deleteUnusedAttachments, + RTL: false } diff --git a/browser/components/MarkdownEditor.js b/browser/components/MarkdownEditor.js index 5c2ddbdb..a2265321 100644 --- a/browser/components/MarkdownEditor.js +++ b/browser/components/MarkdownEditor.js @@ -267,7 +267,7 @@ class MarkdownEditor extends React.Component { } render () { - const {className, value, config, storageKey, noteKey, linesHighlighted} = this.props + const {className, value, config, storageKey, noteKey, linesHighlighted, RTL} = this.props let editorFontSize = parseInt(config.editor.fontSize, 10) if (!(editorFontSize > 0 && editorFontSize < 101)) editorFontSize = 14 @@ -325,6 +325,7 @@ class MarkdownEditor extends React.Component { customMarkdownLintConfig={config.editor.customMarkdownLintConfig} prettierConfig={config.editor.prettierConfig} deleteUnusedAttachments={config.editor.deleteUnusedAttachments} + RTL={RTL} /> this.handleDropImage(e)} + RTL={RTL} /> ) diff --git a/browser/components/MarkdownPreview.js b/browser/components/MarkdownPreview.js index 4a9f6392..5dc2bcc8 100755 --- a/browser/components/MarkdownPreview.js +++ b/browser/components/MarkdownPreview.js @@ -64,7 +64,8 @@ function buildStyle (opts) { scrollPastEnd, theme, allowCustomCSS, - customCSS + customCSS, + RTL } = opts return ` @font-face { @@ -101,11 +102,14 @@ ${markdownStyle} body { font-family: '${fontFamily.join("','")}'; font-size: ${fontSize}px; + ${scrollPastEnd ? ` padding-bottom: 90vh; box-sizing: border-box; ` : ''} + ${RTL ? 'direction: rtl;' : ''} + ${RTL ? 'text-align: right;' : ''} } @media print { body { @@ -115,6 +119,8 @@ body { code { font-family: '${codeBlockFontFamily.join("','")}'; background-color: rgba(0,0,0,0.04); + text-align: left; + direction: ltr; } .lineNumber { ${lineNumber && 'display: block !important;'} @@ -337,7 +343,8 @@ export default class MarkdownPreview extends React.Component { scrollPastEnd, theme, allowCustomCSS, - customCSS + customCSS, + RTL } = this.getStyleParams() const inlineStyles = buildStyle({ @@ -348,7 +355,8 @@ export default class MarkdownPreview extends React.Component { scrollPastEnd, theme, allowCustomCSS, - customCSS + customCSS, + RTL }) let body = this.refs.root.contentWindow.document.body.innerHTML body = attachmentManagement.fixLocalURLS( @@ -619,7 +627,8 @@ 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 @@ -643,7 +652,8 @@ export default class MarkdownPreview extends React.Component { scrollPastEnd, theme, allowCustomCSS, - customCSS + customCSS, + RTL } = this.props let { fontFamily, codeBlockFontFamily } = this.props fontFamily = _.isString(fontFamily) && fontFamily.trim().length > 0 @@ -669,7 +679,8 @@ export default class MarkdownPreview extends React.Component { scrollPastEnd, theme, allowCustomCSS, - customCSS + customCSS, + RTL } } @@ -683,7 +694,8 @@ export default class MarkdownPreview extends React.Component { scrollPastEnd, theme, allowCustomCSS, - customCSS + customCSS, + RTL } = this.getStyleParams() this.getWindow().document.getElementById( @@ -697,7 +709,8 @@ export default class MarkdownPreview extends React.Component { scrollPastEnd, theme, allowCustomCSS, - customCSS + customCSS, + RTL }) } diff --git a/browser/components/MarkdownSplitEditor.js b/browser/components/MarkdownSplitEditor.js index f5996c59..8ae9b751 100644 --- a/browser/components/MarkdownSplitEditor.js +++ b/browser/components/MarkdownSplitEditor.js @@ -137,7 +137,7 @@ class MarkdownSplitEditor extends React.Component { } render () { - const {config, value, storageKey, noteKey, linesHighlighted} = this.props + const {config, value, storageKey, noteKey, linesHighlighted, RTL} = this.props const storage = findStorage(storageKey) let editorFontSize = parseInt(config.editor.fontSize, 10) if (!(editorFontSize > 0 && editorFontSize < 101)) editorFontSize = 14 @@ -183,6 +183,7 @@ class MarkdownSplitEditor extends React.Component { enableMarkdownLint={config.editor.enableMarkdownLint} customMarkdownLintConfig={config.editor.customMarkdownLintConfig} deleteUnusedAttachments={config.editor.deleteUnusedAttachments} + RTL={RTL} />
this.handleMouseDown(e)} >
@@ -213,6 +214,7 @@ class MarkdownSplitEditor extends React.Component { customCSS={config.preview.customCSS} allowCustomCSS={config.preview.allowCustomCSS} lineThroughCheckbox={config.preview.lineThroughCheckbox} + RTL={RTL} />
) diff --git a/browser/main/Detail/MarkdownNoteDetail.js b/browser/main/Detail/MarkdownNoteDetail.js index 207e1e2b..083259d8 100755 --- a/browser/main/Detail/MarkdownNoteDetail.js +++ b/browser/main/Detail/MarkdownNoteDetail.js @@ -31,6 +31,7 @@ import { confirmDeleteNote } from 'browser/lib/confirmDeleteNote' import markdownToc from 'browser/lib/markdown-toc-generator' import queryString from 'query-string' import { replace } from 'connected-react-router' +import ToggleDirectionButton from 'browser/main/Detail/ToggleDirectionButton' class MarkdownNoteDetail extends React.Component { constructor (props) { @@ -46,7 +47,8 @@ class MarkdownNoteDetail extends React.Component { isLockButtonShown: props.config.editor.type !== 'SPLIT', isLocked: false, editorType: props.config.editor.type, - switchPreview: props.config.editor.switchPreview + switchPreview: props.config.editor.switchPreview, + RTL: false } this.dispatchTimer = null @@ -61,6 +63,7 @@ class MarkdownNoteDetail extends React.Component { componentDidMount () { ee.on('topbar:togglelockbutton', this.toggleLockButton) + ee.on('topbar:toggledirectionbutton', () => this.handleSwitchDirection()) ee.on('topbar:togglemodebutton', () => { const reversedType = this.state.editorType === 'SPLIT' ? 'EDITOR_PREVIEW' : 'SPLIT' this.handleSwitchMode(reversedType) @@ -99,6 +102,7 @@ class MarkdownNoteDetail extends React.Component { componentWillUnmount () { ee.off('topbar:togglelockbutton', this.toggleLockButton) + ee.on('topbar:toggledirectionbutton', this.handleSwitchDirection) ee.off('code:generate-toc', this.generateToc) if (this.saveQueue != null) this.saveNow() } @@ -354,6 +358,12 @@ class MarkdownNoteDetail extends React.Component { }) } + handleSwitchDirection () { + // If in split mode, hide the lock button + const direction = this.state.RTL + this.setState({ RTL: !direction }) + } + handleDeleteNote () { this.handleTrashButtonClick() } @@ -393,6 +403,7 @@ class MarkdownNoteDetail extends React.Component { onChange={this.handleUpdateContent.bind(this)} isLocked={this.state.isLocked} ignorePreviewPointerEvents={ignorePreviewPointerEvents} + RTL={this.state.RTL} /> } else { return } } @@ -472,7 +484,7 @@ class MarkdownNoteDetail extends React.Component {
this.handleSwitchMode(e)} editorType={editorType} /> - + this.handleSwitchDirection(e)} isRTL={this.state.RTL} /> this.handleStarButtonClick(e)} isActive={note.isStarred} @@ -518,6 +530,7 @@ class MarkdownNoteDetail extends React.Component { print={this.print} />
+ return ( diff --git a/browser/main/Detail/MarkdownNoteDetail.styl b/browser/main/Detail/MarkdownNoteDetail.styl index a24e9881..a7adbea1 100644 --- a/browser/main/Detail/MarkdownNoteDetail.styl +++ b/browser/main/Detail/MarkdownNoteDetail.styl @@ -15,7 +15,7 @@ .control-lockButton topBarButtonRight() position absolute - right 225px + right 265px &:hover .tooltip opacity 1 diff --git a/browser/main/Detail/ToggleDirectionButton.js b/browser/main/Detail/ToggleDirectionButton.js new file mode 100644 index 00000000..6584d4fa --- /dev/null +++ b/browser/main/Detail/ToggleDirectionButton.js @@ -0,0 +1,26 @@ +import PropTypes from 'prop-types' +import React from 'react' +import CSSModules from 'browser/lib/CSSModules' +import styles from './ToggleDirectionButton.styl' +import i18n from 'browser/lib/i18n' + +const ToggleDirectionButton = ({ + onClick, isRTL +}) => ( +
+
onClick()}> + +
+
onClick()}> + +
+ {i18n.__('Toggle Direction')} +
+) + +ToggleDirectionButton.propTypes = { + onClick: PropTypes.func.isRequired, + isRTL: PropTypes.string.isRequired +} + +export default CSSModules(ToggleDirectionButton, styles) diff --git a/browser/main/Detail/ToggleDirectionButton.styl b/browser/main/Detail/ToggleDirectionButton.styl new file mode 100644 index 00000000..5146ad23 --- /dev/null +++ b/browser/main/Detail/ToggleDirectionButton.styl @@ -0,0 +1,85 @@ +.control-toggleModeButton + height 25px + border-radius 50px + background-color #F4F4F4 + width 52px + display flex + align-items center + position: relative + top 2px + margin-left 5px + .active + background-color #1EC38B + width 33px + height 24px + box-shadow 2px 0px 7px #eee + z-index 1 + + div + width 40px + height 100% + border-radius 50% + display flex + align-items center + justify-content center + cursor pointer + + &:hover .tooltip + opacity 1 + +.tooltip + tooltip() + position absolute + pointer-events none + top 33px + left -10px + z-index 200 + width 80px + padding 5px + line-height normal + border-radius 2px + opacity 0 + transition 0.1s + +.tooltip:lang(ja) + @extend .tooltip + left -8px + width 70px + +body[data-theme="dark"] + .control-fullScreenButton + topBarButtonDark() + + .control-toggleModeButton + background-color #3A404C + .active + background-color #1EC38B + box-shadow 2px 0px 7px #444444 + +body[data-theme="solarized-dark"] + .control-toggleModeButton + background-color #002B36 + .active + background-color #1EC38B + box-shadow 2px 0px 7px #222222 + +body[data-theme="monokai"] + .control-toggleModeButton + background-color #373831 + .active + background-color #f92672 + box-shadow 2px 0px 7px #222222 + +body[data-theme="dracula"] + .control-toggleModeButton + background-color #44475a + .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 diff --git a/browser/main/global.styl b/browser/main/global.styl index d864993d..d1205b37 100644 --- a/browser/main/global.styl +++ b/browser/main/global.styl @@ -182,4 +182,4 @@ body[data-theme="default"] .SideNav ::-webkit-scrollbar-thumb background-color rgba(255, 255, 255, 0.3) -@import '../styles/Detail/TagSelect.styl' \ No newline at end of file +@import '../styles/Detail/TagSelect.styl' diff --git a/browser/main/lib/ConfigManager.js b/browser/main/lib/ConfigManager.js index ce641b9a..7d3b4fd5 100644 --- a/browser/main/lib/ConfigManager.js +++ b/browser/main/lib/ConfigManager.js @@ -31,6 +31,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', deleteNote: OSX ? 'Command + Shift + Backspace' : 'Ctrl + Shift + Backspace', pasteSmartly: OSX ? 'Command + Shift + V' : 'Ctrl + Shift + V', prettifyMarkdown: OSX ? 'Command + Shift + F' : 'Ctrl + Shift + F', diff --git a/browser/main/lib/shortcut.js b/browser/main/lib/shortcut.js index 3165606a..98e26e06 100644 --- a/browser/main/lib/shortcut.js +++ b/browser/main/lib/shortcut.js @@ -4,6 +4,9 @@ module.exports = { 'toggleMode': () => { ee.emit('topbar:togglemodebutton') }, + 'toggleDirection': () => { + ee.emit('topbar:toggledirectionbutton') + }, 'deleteNote': () => { ee.emit('hotkey:deletenote') }, diff --git a/browser/main/modals/PreferencesModal/HotkeyTab.js b/browser/main/modals/PreferencesModal/HotkeyTab.js index 9c4f5655..00fea4f0 100644 --- a/browser/main/modals/PreferencesModal/HotkeyTab.js +++ b/browser/main/modals/PreferencesModal/HotkeyTab.js @@ -30,7 +30,8 @@ class HotkeyTab extends React.Component { this.handleSettingError = (err) => { if ( this.state.config.hotkey.toggleMain === '' || - this.state.config.hotkey.toggleMode === '' + this.state.config.hotkey.toggleMode === '' || + this.state.config.hotkey.toggleDirection === '' ) { this.setState({keymapAlert: { type: 'success', @@ -79,6 +80,7 @@ class HotkeyTab extends React.Component { config.hotkey = Object.assign({}, config.hotkey, { toggleMain: this.refs.toggleMain.value, toggleMode: this.refs.toggleMode.value, + toggleDirection: this.refs.toggleDirection.value, deleteNote: this.refs.deleteNote.value, pasteSmartly: this.refs.pasteSmartly.value, prettifyMarkdown: this.refs.prettifyMarkdown.value, @@ -154,6 +156,17 @@ class HotkeyTab extends React.Component { /> +
+
{i18n.__('Toggle Direction')}
+
+ this.handleHotkeyChange(e)} + ref='toggleDirection' + value={config.hotkey.toggleDirection} + type='text' + /> +
+
{i18n.__('Delete Note')}
diff --git a/resources/icon/icon-left-to-right.svg b/resources/icon/icon-left-to-right.svg new file mode 100644 index 00000000..2489e763 --- /dev/null +++ b/resources/icon/icon-left-to-right.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/resources/icon/icon-right-to-left.svg b/resources/icon/icon-right-to-left.svg new file mode 100644 index 00000000..da833c2d --- /dev/null +++ b/resources/icon/icon-right-to-left.svg @@ -0,0 +1 @@ + \ No newline at end of file