Skip to content

Commit 49db5bc

Browse files
committed
Merge branch 'pr-846'
2 parents 7542968 + f65d96c commit 49db5bc

5 files changed

Lines changed: 250 additions & 21 deletions

File tree

public/css/index.css

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,24 @@ body.night{
2020
background: #333 !important;
2121
}
2222

23+
.toolbar {
24+
background-color: #1c1c1e;
25+
border: 1px solid #343434;
26+
}
27+
28+
.toolbar > .btn-toolbar > .btn-group > .btn {
29+
background-color: #1c1c1e;
30+
padding: 5px;
31+
font-size: 1em;
32+
}
33+
34+
.toolbar > .btn-toolbar > .btn-group > .btn:hover {
35+
background-color: #383a3e;
36+
37+
padding: 5px;
38+
}
39+
40+
2341
.CodeMirror {
2442
font-family: "Source Code Pro", Consolas, monaco, monospace;
2543
letter-spacing: 0.025em;

public/js/index.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -566,7 +566,10 @@ var previousFocusOnEditor = null
566566

567567
function checkEditorStyle () {
568568
var desireHeight = editorInstance.statusBar ? (ui.area.edit.height() - editorInstance.statusBar.outerHeight()) : ui.area.edit.height()
569-
// set editor height and min height based on scrollbar style and mode
569+
if (editorInstance.toolBar) {
570+
desireHeight = desireHeight - editorInstance.toolBar.outerHeight()
571+
}
572+
// set editor height and min height based on scrollbar style and mode
570573
var scrollbarStyle = editor.getOption('scrollbarStyle')
571574
if (scrollbarStyle === 'overlay' || appState.currentMode === modeType.both) {
572575
ui.area.codemirrorScroll.css('height', desireHeight + 'px')
@@ -804,6 +807,10 @@ function changeMode (type) {
804807
editorInstance.addStatusBar()
805808
editorInstance.updateStatusBar()
806809
}
810+
// add and update tool bar
811+
if (!editorInstance.toolBar) {
812+
editorInstance.addToolBar()
813+
}
807814
// work around foldGutter might not init properly
808815
editor.setOption('foldGutter', false)
809816
editor.setOption('foldGutter', true)

public/js/lib/editor/index.js

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as utils from './utils'
22
import config from './config'
33
import statusBarTemplate from './statusbar.html'
4+
import toolBarTemplate from './toolbar.html'
45

56
/* config section */
67
const isMac = CodeMirror.keyMap.default === CodeMirror.keyMap.macDefault
@@ -136,6 +137,88 @@ export default class Editor {
136137
})
137138
}
138139

140+
addToolBar () {
141+
this.toolBar = $(toolBarTemplate)
142+
this.toolbarPanel = this.editor.addPanel(this.toolBar[0], {
143+
position: 'top'
144+
})
145+
146+
var makeBold = $('#makeBold')
147+
var makeItalic = $('#makeItalic')
148+
var makeStrike = $('#makeStrike')
149+
var makeHeader = $('#makeHeader')
150+
var makeCode = $('#makeCode')
151+
var makeQuote = $('#makeQuote')
152+
var makeGenericList = $('#makeGenericList')
153+
var makeOrderedList = $('#makeOrderedList')
154+
var makeCheckList = $('#makeCheckList')
155+
var makeLink = $('#makeLink')
156+
var makeImage = $('#makeImage')
157+
var makeTable = $('#makeTable')
158+
var makeLine = $('#makeLine')
159+
var makeComment = $('#makeComment')
160+
161+
makeBold.click(() => {
162+
utils.wrapTextWith(this.editor, this.editor, '**')
163+
this.editor.focus()
164+
})
165+
166+
makeItalic.click(() => {
167+
utils.wrapTextWith(this.editor, this.editor, '*')
168+
this.editor.focus()
169+
})
170+
171+
makeStrike.click(() => {
172+
utils.wrapTextWith(this.editor, this.editor, '~~')
173+
this.editor.focus()
174+
})
175+
176+
makeHeader.click(() => {
177+
utils.insertHeader(this.editor)
178+
})
179+
180+
makeCode.click(() => {
181+
utils.wrapTextWith(this.editor, this.editor, '```')
182+
this.editor.focus()
183+
})
184+
185+
makeQuote.click(() => {
186+
utils.insertOnStartOfLines(this.editor, '> ')
187+
})
188+
189+
makeGenericList.click(() => {
190+
utils.insertOnStartOfLines(this.editor, '* ')
191+
})
192+
193+
makeOrderedList.click(() => {
194+
utils.insertOnStartOfLines(this.editor, '1. ')
195+
})
196+
197+
makeCheckList.click(() => {
198+
utils.insertOnStartOfLines(this.editor, '- [ ] ')
199+
})
200+
201+
makeLink.click(() => {
202+
utils.insertLink(this.editor, false)
203+
})
204+
205+
makeImage.click(() => {
206+
utils.insertLink(this.editor, true)
207+
})
208+
209+
makeTable.click(() => {
210+
utils.insertText(this.editor, '\n\n| Column 1 | Column 2 | Column 3 |\n| -------- | -------- | -------- |\n| Text | Text | Text |\n')
211+
})
212+
213+
makeLine.click(() => {
214+
utils.insertText(this.editor, '\n----\n')
215+
})
216+
217+
makeComment.click(() => {
218+
utils.insertText(this.editor, '> []')
219+
})
220+
}
221+
139222
addStatusBar () {
140223
this.statusBar = $(statusBarTemplate)
141224
this.statusCursor = this.statusBar.find('.status-cursor > .status-line-column')

public/js/lib/editor/toolbar.html

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<div class="toolbar">
2+
<div class="btn-toolbar" role="toolbar" aria-label="Editor toolbar">
3+
<div class="btn-group" role="group">
4+
<a id="makeBold" class="btn btn-sm btn-dark text-uppercase" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" title="Bold">
5+
<i class="fa fa-bold fa-fw"></i>
6+
</a>
7+
<a id="makeItalic" class="btn btn-sm btn-dark text-uppercase" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" title="Italic">
8+
<i class="fa fa-italic fa-fw"></i>
9+
</a>
10+
<a id="makeStrike" class="btn btn-sm btn-dark text-uppercase" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" title="Strikethrough">
11+
<i class="fa fa-strikethrough fa-fw"></i>
12+
</a>
13+
<a id="makeHeader" class="btn btn-sm btn-dark text-uppercase" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" title="Heading">
14+
<i class="fa fa-h1 fa-fw">H</i>
15+
</a>
16+
<a id="makeCode" class="btn btn-sm btn-dark text-uppercase" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" title="Quote">
17+
<i class="fa fa-code fa-fw"></i>
18+
</a>
19+
<a id="makeQuote" class="btn btn-sm btn-dark text-uppercase" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" title="Quote">
20+
<i class="fa fa-quote-right fa-fw"></i>
21+
</a>
22+
<a id="makeGenericList" class="btn btn-sm btn-dark text-uppercase" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" title="List">
23+
<i class="fa fa-list fa-fw"></i>
24+
</a>
25+
<a id="makeOrderedList" class="btn btn-sm btn-dark text-uppercase" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" title="Numbered List">
26+
<i class="fa fa-list-ol fa-fw"></i>
27+
</a>
28+
<a id="makeCheckList" class="btn btn-sm btn-dark text-uppercase" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" title="Check List">
29+
<i class="fa fa-check-square fa-fw"></i>
30+
</a>
31+
<a id="makeLink" class="btn btn-sm btn-dark text-uppercase" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" title="Link">
32+
<i class="fa fa-link fa-fw"></i>
33+
</a>
34+
<a id="makeImage" class="btn btn-sm btn-dark text-uppercase" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" title="Image">
35+
<i class="fa fa-image fa-fw"></i>
36+
</a>
37+
<a id="makeTable" class="btn btn-sm btn-dark text-uppercase" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" title="Table">
38+
<i class="fa fa-table fa-fw"></i>
39+
</a>
40+
<a id="makeLine" class="btn btn-sm btn-dark text-uppercase" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" title="Line">
41+
<i class="fa fa-minus fa-fw"></i>
42+
</a>
43+
<a id="makeComment" class="btn btn-sm btn-dark text-uppercase" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" title="Line">
44+
<i class="fa fa-comment fa-fw"></i>
45+
</a>
46+
</div>
47+
</div>
48+
</div>

public/js/lib/editor/utils.js

Lines changed: 93 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,39 +3,39 @@ export function wrapTextWith (editor, cm, symbol) {
33
if (!cm.getSelection()) {
44
return CodeMirror.Pass
55
} else {
6-
var ranges = cm.listSelections()
7-
for (var i = 0; i < ranges.length; i++) {
8-
var range = ranges[i]
6+
let ranges = cm.listSelections()
7+
for (let i = 0; i < ranges.length; i++) {
8+
let range = ranges[i]
99
if (!range.empty()) {
1010
const from = range.from()
1111
const to = range.to()
1212

1313
if (symbol !== 'Backspace') {
14-
cm.replaceRange(symbol, to, to, '+input')
15-
cm.replaceRange(symbol, from, from, '+input')
16-
// workaround selection range not correct after add symbol
17-
var _ranges = cm.listSelections()
18-
var anchorIndex = editor.indexFromPos(_ranges[i].anchor)
19-
var headIndex = editor.indexFromPos(_ranges[i].head)
14+
let selection = cm.getRange(from, to)
15+
let anchorIndex = editor.indexFromPos(ranges[i].anchor)
16+
let headIndex = editor.indexFromPos(ranges[i].head)
17+
cm.replaceRange(symbol + selection + symbol, from, to, '+input')
2018
if (anchorIndex > headIndex) {
21-
_ranges[i].anchor.ch--
19+
ranges[i].anchor.ch+= symbol.length
20+
ranges[i].head.ch+= symbol.length
2221
} else {
23-
_ranges[i].head.ch--
22+
ranges[i].head.ch+= symbol.length
23+
ranges[i].anchor.ch+= symbol.length
2424
}
25-
cm.setSelections(_ranges)
25+
cm.setSelections(ranges)
2626
} else {
27-
var preEndPos = {
27+
let preEndPos = {
2828
line: to.line,
29-
ch: to.ch + 1
29+
ch: to.ch + symbol.length
3030
}
31-
var preText = cm.getRange(to, preEndPos)
32-
var preIndex = wrapSymbols.indexOf(preText)
33-
var postEndPos = {
31+
let preText = cm.getRange(to, preEndPos)
32+
let preIndex = wrapSymbols.indexOf(preText)
33+
let postEndPos = {
3434
line: from.line,
35-
ch: from.ch - 1
35+
ch: from.ch - symbol.length
3636
}
37-
var postText = cm.getRange(postEndPos, from)
38-
var postIndex = wrapSymbols.indexOf(postText)
37+
let postText = cm.getRange(postEndPos, from)
38+
let postIndex = wrapSymbols.indexOf(postText)
3939
// check if surround symbol are list in array and matched
4040
if (preIndex > -1 && postIndex > -1 && preIndex === postIndex) {
4141
cm.replaceRange('', to, preEndPos, '+delete')
@@ -46,3 +46,76 @@ export function wrapTextWith (editor, cm, symbol) {
4646
}
4747
}
4848
}
49+
50+
export function insertText (cm, text, cursorEnd = 0) {
51+
let cursor = cm.getCursor()
52+
cm.replaceSelection(text, cursor, cursor)
53+
cm.focus()
54+
cm.setCursor({line: cursor.line, ch: cursor.ch + cursorEnd})
55+
}
56+
57+
export function insertLink(cm, isImage) {
58+
let cursor = cm.getCursor()
59+
let ranges = cm.listSelections()
60+
const linkEnd = '](https://)'
61+
const symbol = (isImage) ? '![' : '['
62+
63+
for (let i = 0; i < ranges.length; i++) {
64+
let range = ranges[i]
65+
if (!range.empty()) {
66+
const from = range.from()
67+
const to = range.to()
68+
let anchorIndex = editor.indexFromPos(ranges[i].anchor)
69+
let headIndex = editor.indexFromPos(ranges[i].head)
70+
let selection = cm.getRange(from, to)
71+
selection = symbol + selection + linkEnd
72+
cm.replaceRange(selection, from, to)
73+
if (anchorIndex > headIndex) {
74+
ranges[i].anchor.ch+= symbol.length
75+
ranges[i].head.ch+= symbol.length
76+
} else {
77+
ranges[i].head.ch+= symbol.length
78+
ranges[i].anchor.ch+= symbol.length
79+
}
80+
cm.setSelections(ranges)
81+
} else {
82+
cm.replaceRange(symbol + linkEnd, cursor, cursor)
83+
cm.setCursor({line: cursor.line, ch: cursor.ch + symbol.length + linkend.length})
84+
}
85+
}
86+
cm.focus()
87+
}
88+
89+
export function insertHeader (cm) {
90+
let cursor = cm.getCursor()
91+
let startOfLine = {line: cursor.line, ch: 0}
92+
let startOfLineText = cm.getRange(startOfLine, {line: cursor.line, ch: 1})
93+
// See if it is already a header
94+
if (startOfLineText === '#') {
95+
cm.replaceRange('#', startOfLine, startOfLine)
96+
} else {
97+
cm.replaceRange('# ', startOfLine, startOfLine)
98+
}
99+
cm.focus()
100+
}
101+
102+
export function insertOnStartOfLines (cm, symbol) {
103+
let cursor = cm.getCursor()
104+
let ranges = cm.listSelections()
105+
106+
for (let i = 0; i < ranges.length; i++) {
107+
let range = ranges[i]
108+
if (!range.empty()) {
109+
const from = range.from()
110+
const to = range.to()
111+
let selection = cm.getRange({line: from.line, ch: 0}, to)
112+
selection = selection.replace(/\n/g, '\n' + symbol)
113+
selection = symbol + selection
114+
cm.replaceRange(selection, from, to)
115+
} else {
116+
cm.replaceRange(symbol, {line: cursor.line, ch: 0}, {line: cursor.line, ch: 0})
117+
}
118+
}
119+
cm.setCursor({line: cursor.line, ch: cursor.ch + symbol.length})
120+
cm.focus()
121+
}

0 commit comments

Comments
 (0)