diff --git a/browser/components/MarkdownPreview.js b/browser/components/MarkdownPreview.js index 45304c11..6d5183ae 100644 --- a/browser/components/MarkdownPreview.js +++ b/browser/components/MarkdownPreview.js @@ -71,7 +71,6 @@ export default class MarkdownPreview extends React.Component { el.removeEventListener('click', goExternal) }) - let { value, fontFamily, fontSize, codeBlockFontFamily, lineNumber, codeBlockTheme } = this.props fontFamily = _.isString(fontFamily) && fontFamily.trim().length > 0 ? [fontFamily].concat(defaultFontFamily) diff --git a/browser/lib/modes.js b/browser/lib/modes.js index 256769cf..2ea68258 100644 --- a/browser/lib/modes.js +++ b/browser/lib/modes.js @@ -1,90 +1,9 @@ const modes = [ - // Major { name: 'text', label: 'Plain text', mode: 'text' }, - { - name: 'markdown', - label: 'Markdown', - alias: ['md'], - mode: 'markdown' - }, - { - name: 'javascript', - label: 'JavaScript', - alias: ['js', 'jscript', 'babel', 'es'], - mode: 'javascript' - }, - { - name: 'html', - label: 'HTML', - alias: [], - mode: 'html' - }, - { - name: 'css', - label: 'CSS', - alias: ['cascade', 'stylesheet'], - mode: 'css' - }, - { - name: 'php', - label: 'PHP', - alias: [], - mode: 'php' - }, - { - name: 'python', - label: 'Python', - alias: ['py'], - mode: 'python' - }, - { - name: 'ruby', - label: 'Ruby', - alias: ['rb'], - mode: 'ruby' - }, - { - name: 'java', - label: 'Java', - alias: [], - mode: 'java' - }, - { - name: 'c', - label: 'C', - alias: ['c', 'h', 'clang', 'clang'], - mode: 'c_cpp' - }, - { - name: 'cpp', - label: 'C++', - alias: ['cc', 'cpp', 'cxx', 'hh', 'c++', 'cplusplus'], - mode: 'c_cpp' - }, - { - name: 'csharp', - label: 'C#', - alias: ['cs', 'c#'], - mode: 'csharp' - }, - { - name: 'swift', - label: 'Swift', - alias: [], - mode: 'swift' - }, - { - name: 'golang', - label: 'Go', - alias: ['go'], - mode: 'golang' - }, - - // Minor { name: 'abap', label: 'ABAP', @@ -145,6 +64,12 @@ const modes = [ alias: ['dos', 'windows', 'bat', 'cmd', 'btm'], mode: 'batchfile' }, + { + name: 'c', + label: 'C', + alias: ['c', 'h', 'clang', 'clang'], + mode: 'c_cpp' + }, { name: 'cirru', label: 'Cirru', @@ -175,6 +100,24 @@ const modes = [ alias: ['cfm', 'cfc'], mode: 'coldfusion' }, + { + name: 'cpp', + label: 'C++', + alias: ['cc', 'cpp', 'cxx', 'hh', 'c++', 'cplusplus'], + mode: 'c_cpp' + }, + { + name: 'csharp', + label: 'C#', + alias: ['cs', 'c#'], + mode: 'csharp' + }, + { + name: 'css', + label: 'CSS', + alias: ['cascade', 'stylesheet'], + mode: 'css' + }, { name: 'curly', label: 'Curly', @@ -283,6 +226,12 @@ const modes = [ alias: ['opengl', 'shading'], mode: 'glsl' }, + { + name: 'golang', + label: 'Go', + alias: ['go'], + mode: 'golang' + }, { name: 'groovy', label: 'Groovy', @@ -313,6 +262,12 @@ const modes = [ alias: ['hx', 'hxml'], mode: 'haxe' }, + { + name: 'html', + label: 'HTML', + alias: [], + mode: 'html' + }, { name: 'html_ruby', label: 'HTML (Ruby)', @@ -355,6 +310,18 @@ const modes = [ alias: [], mode: 'jade' }, + { + name: 'java', + label: 'Java', + alias: [], + mode: 'java' + }, + { + name: 'javascript', + label: 'JavaScript', + alias: ['js', 'jscript', 'babel', 'es'], + mode: 'javascript' + }, { name: 'json', label: 'JSON', @@ -451,6 +418,12 @@ const modes = [ alias: [], mode: 'makefile' }, + { + name: 'markdown', + label: 'Markdown', + alias: ['md'], + mode: 'markdown' + }, { name: 'mask', label: 'Mask', @@ -529,6 +502,12 @@ const modes = [ alias: ['postgres'], mode: 'pgsql' }, + { + name: 'php', + label: 'PHP', + alias: [], + mode: 'php' + }, { name: 'powershell', label: 'PowerShell', @@ -559,6 +538,12 @@ const modes = [ alias: ['protocol', 'buffers'], mode: 'protobuf' }, + { + name: 'python', + label: 'Python', + alias: ['py'], + mode: 'python' + }, { name: 'r', label: 'R', @@ -571,6 +556,12 @@ const modes = [ alias: [], mode: 'rdoc' }, + { + name: 'ruby', + label: 'Ruby', + alias: ['rb'], + mode: 'ruby' + }, { name: 'rust', label: 'Rust', @@ -667,6 +658,12 @@ const modes = [ alias: [], mode: 'svg' }, + { + name: 'swift', + label: 'Swift', + alias: [], + mode: 'swift' + }, { name: 'swig', label: 'SWIG', diff --git a/browser/main/Detail/NoteDetail.js b/browser/main/Detail/MarkdownNoteDetail.js similarity index 96% rename from browser/main/Detail/NoteDetail.js rename to browser/main/Detail/MarkdownNoteDetail.js index 25f989ab..f888a795 100644 --- a/browser/main/Detail/NoteDetail.js +++ b/browser/main/Detail/MarkdownNoteDetail.js @@ -1,6 +1,6 @@ import React, { PropTypes } from 'react' import CSSModules from 'browser/lib/CSSModules' -import styles from './NoteDetail.styl' +import styles from './MarkdownNoteDetail.styl' import MarkdownEditor from 'browser/components/MarkdownEditor' import StarButton from './StarButton' import TagSelect from './TagSelect' @@ -13,7 +13,7 @@ const { remote } = electron const Menu = remote.Menu const MenuItem = remote.MenuItem -class NoteDetail extends React.Component { +class MarkdownNoteDetail extends React.Component { constructor (props) { super(props) @@ -217,7 +217,7 @@ class NoteDetail extends React.Component { } } -NoteDetail.propTypes = { +MarkdownNoteDetail.propTypes = { dispatch: PropTypes.func, repositories: PropTypes.array, note: PropTypes.shape({ @@ -229,4 +229,4 @@ NoteDetail.propTypes = { ignorePreviewPointerEvents: PropTypes.bool } -export default CSSModules(NoteDetail, styles) +export default CSSModules(MarkdownNoteDetail, styles) diff --git a/browser/main/Detail/NoteDetail.styl b/browser/main/Detail/MarkdownNoteDetail.styl similarity index 97% rename from browser/main/Detail/NoteDetail.styl rename to browser/main/Detail/MarkdownNoteDetail.styl index 1b8ebe83..8d6236ef 100644 --- a/browser/main/Detail/NoteDetail.styl +++ b/browser/main/Detail/MarkdownNoteDetail.styl @@ -2,7 +2,7 @@ $info-height = 75px .root absolute top bottom right - border-width 1px 0 + border-width 0 0 1px border-style solid border-color $ui-borderColor diff --git a/browser/main/Detail/SnippetNoteDetail.js b/browser/main/Detail/SnippetNoteDetail.js new file mode 100644 index 00000000..d6181b67 --- /dev/null +++ b/browser/main/Detail/SnippetNoteDetail.js @@ -0,0 +1,410 @@ +import React, { PropTypes } from 'react' +import CSSModules from 'browser/lib/CSSModules' +import styles from './SnippetNoteDetail.styl' +import CodeEditor from 'browser/components/CodeEditor' +import StarButton from './StarButton' +import TagSelect from './TagSelect' +import FolderSelect from './FolderSelect' +import Commander from 'browser/main/lib/Commander' +import dataApi from 'browser/main/lib/dataApi' +import modes from 'browser/lib/modes' + +const electron = require('electron') +const { remote } = electron +const Menu = remote.Menu +const MenuItem = remote.MenuItem + +class SnippetNoteDetail extends React.Component { + constructor (props) { + super(props) + + this.state = { + snippetIndex: 0, + note: Object.assign({ + description: '' + }, props.note, { + snippets: props.note.snippets.map((snippet) => Object.assign({}, snippet)) + }) + } + } + + componentDidMount () { + Commander.bind('note-detail', this) + } + + componentWillUnmount () { + Commander.release(this) + } + + fire (command) { + switch (command) { + case 'focus': + this.refs.description.focus() + } + } + + componentWillReceiveProps (nextProps) { + if (nextProps.note.key !== this.props.note.key) { + this.setState({ + snippetIndex: 0, + note: Object.assign({ + description: '' + }, nextProps.note, { + snippets: nextProps.note.snippets.map((snippet) => Object.assign({}, snippet)) + }) + }, () => { + let { snippets } = this.state.note + snippets.forEach((snippet, index) => { + this.refs['code-' + index].reload() + }) + this.refs.tags.reset() + }) + } + } + + findTitle (value) { + let splitted = value.split('\n') + let title = null + + for (let i = 0; i < splitted.length; i++) { + let trimmedLine = splitted[i].trim() + if (trimmedLine.match(/^# .+/)) { + title = trimmedLine.substring(1, trimmedLine.length).trim() + break + } + } + + if (title == null) { + for (let i = 0; i < splitted.length; i++) { + let trimmedLine = splitted[i].trim() + if (trimmedLine.length > 0) { + title = trimmedLine + break + } + } + if (title == null) { + title = '' + } + } + + return title + } + + handleChange (e) { + let { note } = this.state + + note.tags = this.refs.tags.value + note.description = this.refs.description.value + note.updatedAt = new Date() + note.title = this.findTitle(note.description) + + this.setState({ + note + }, () => { + this.save() + }) + } + + save () { + let { note, dispatch } = this.props + + dispatch({ + type: 'UPDATE_NOTE', + note: this.state.note + }) + + dataApi + .updateNote(note.storage, note.folder, note.key, this.state.note) + } + + handleFolderChange (e) { + + } + + handleStarButtonClick (e) { + let { note } = this.state + + note.isStarred = !note.isStarred + + this.setState({ + note + }, () => { + this.save() + }) + } + + exportAsFile () { + + } + + handleShareButtonClick (e) { + let menu = new Menu() + menu.append(new MenuItem({ + label: 'Export as a File', + click: (e) => this.handlePreferencesButtonClick(e) + })) + menu.append(new MenuItem({ + label: 'Export to Web', + disabled: true, + click: (e) => this.handlePreferencesButtonClick(e) + })) + menu.popup(remote.getCurrentWindow()) + } + + handleContextButtonClick (e) { + let menu = new Menu() + menu.append(new MenuItem({ + label: 'Delete', + click: (e) => this.handlePreferencesButtonClick(e) + })) + menu.popup(remote.getCurrentWindow()) + } + + handleTabPlusButtonClick (e) { + let { note } = this.state + + note.snippets = note.snippets.concat([{ + name: '', + mode: 'text', + content: '' + }]) + + this.setState({ + note + }) + } + + handleTabButtonClick (index) { + return (e) => { + this.setState({ + snippetIndex: index + }) + } + } + + handleTabDeleteButtonClick (index) { + return (e) => { + if (this.state.note.snippets.length > 1) { + let snippets = this.state.note.snippets.slice() + snippets.splice(index, 1) + this.state.note.snippets = snippets + this.setState({ + note: this.state.note + }) + } + } + } + + handleNameInputChange (index) { + return (e) => { + let snippets = this.state.note.snippets.slice() + snippets[index].name = e.target.value + this.state.note.snippets = snippets + + this.setState({ + note: this.state.note + }, () => { + this.save() + }) + } + } + + handleModeButtonClick (index) { + return (e) => { + let menu = new Menu() + modes.forEach((mode) => { + menu.append(new MenuItem({ + label: mode.label, + click: (e) => this.handleModeOptionClick(index, mode.name)(e) + })) + }) + menu.popup(remote.getCurrentWindow()) + } + } + + handleModeOptionClick (index, name) { + return (e) => { + let snippets = this.state.note.snippets.slice() + snippets[index].mode = name + this.state.note.snippets = snippets + + this.setState({ + note: this.state.note + }, () => { + this.save() + }) + } + } + + handleCodeChange (index) { + return (e) => { + let snippets = this.state.note.snippets.slice() + snippets[index].content = this.refs['code-' + index].value + this.state.note.snippets = snippets + this.setState({ + note: this.state.note + }, () => { + this.save() + }) + } + } + + render () { + let { storages, config } = this.props + let { note } = this.state + + let editorFontSize = parseInt(config.editor.fontSize, 10) + if (!(editorFontSize > 0 && editorFontSize < 101)) editorFontSize = 14 + let editorIndentSize = parseInt(config.editor.indentSize, 10) + if (!(editorFontSize > 0 && editorFontSize < 132)) editorIndentSize = 4 + + let tabList = note.snippets.map((snippet, index) => { + let isActive = this.state.snippetIndex === index + return
+ + {note.snippets.length > 1 && + + } +
+ }) + let viewList = note.snippets.map((snippet, index) => { + let isActive = this.state.snippetIndex === index + let mode = snippet.mode === 'text' + ? null + : modes.filter((mode) => mode.name === snippet.mode)[0] + + return
+
+ this.handleNameInputChange(index)(e)} + /> + +
+ this.handleCodeChange(index)(e)} + ref={'code-' + index} + /> +
+ }) + + return ( +
+
+
+ +
+ this.handleFolderChange(e)} + /> +
+
+ this.handleChange(e)} + /> +
+
+
+ this.handleStarButtonClick(e)} + isActive={note.isStarred} + /> + + +
+
+
+
+