优化meditor
parent
4ddffd2585
commit
2457e14a86
|
@ -60,7 +60,8 @@
|
||||||
min-width: 200px;
|
min-width: 200px;
|
||||||
min-height: 100px;
|
min-height: 100px;
|
||||||
// max-height: 360px;
|
// max-height: 360px;
|
||||||
border-radius: 2px;
|
border-radius: 3px;
|
||||||
|
transition: box-shadow 0.15s linear;
|
||||||
}
|
}
|
||||||
|
|
||||||
.meditor {
|
.meditor {
|
||||||
|
@ -135,6 +136,7 @@
|
||||||
background: none;
|
background: none;
|
||||||
outline: none;
|
outline: none;
|
||||||
resize: none;
|
resize: none;
|
||||||
|
cursor: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
.preview {
|
.preview {
|
||||||
|
@ -336,6 +338,10 @@
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:host(:focus-within) {
|
||||||
|
box-shadow: 0 0 0 2px var(--color-plain-a);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
@ -360,6 +366,10 @@ export default class Meditor {
|
||||||
preview: window.innerWidth > 768
|
preview: window.innerWidth > 768
|
||||||
}
|
}
|
||||||
|
|
||||||
|
state = {
|
||||||
|
toolbar: []
|
||||||
|
}
|
||||||
|
|
||||||
__init__() {
|
__init__() {
|
||||||
/* render */
|
/* render */
|
||||||
|
|
||||||
|
@ -625,27 +635,24 @@ export default class Meditor {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 往文本框中插入内容
|
/**
|
||||||
insert(val, isSelect) {
|
* 往文本框中插入内容
|
||||||
|
* @param {String} val [要插入的文本]
|
||||||
|
* @param {Boolean} isSelect [插入之后是否要选中文本]
|
||||||
|
* @param {Boolean} offset [选中文本时的偏移量]
|
||||||
|
*/
|
||||||
|
insert(val = '', isSelect = false, offset = 0) {
|
||||||
var dom = this.__EDITOR__
|
var dom = this.__EDITOR__
|
||||||
if (document.selection) {
|
var start = dom.selectionStart
|
||||||
dom.focus()
|
var end = dom.selectionEnd
|
||||||
let range = document.selection.createRange()
|
var scrollTop = dom.scrollTop
|
||||||
range.text = val
|
|
||||||
dom.focus()
|
|
||||||
range.moveStart('character', -1)
|
|
||||||
} else if (dom.selectionStart || dom.selectionStart === 0) {
|
|
||||||
let startPos = dom.selectionStart
|
|
||||||
let endPos = dom.selectionEnd
|
|
||||||
let scrollTop = dom.scrollTop
|
|
||||||
|
|
||||||
|
if (start || start === 0) {
|
||||||
dom.value =
|
dom.value =
|
||||||
dom.value.slice(0, startPos) +
|
dom.value.slice(0, start) + val + dom.value.slice(end, dom.value.length)
|
||||||
val +
|
|
||||||
dom.value.slice(endPos, dom.value.length)
|
|
||||||
|
|
||||||
dom.selectionStart = isSelect ? startPos : startPos + val.length
|
dom.selectionStart = (isSelect ? start : start + val.length) + offset
|
||||||
dom.selectionEnd = startPos + val.length
|
dom.selectionEnd = start + val.length - offset
|
||||||
dom.scrollTop = scrollTop
|
dom.scrollTop = scrollTop
|
||||||
dom.focus()
|
dom.focus()
|
||||||
} else {
|
} else {
|
||||||
|
@ -659,39 +666,70 @@ export default class Meditor {
|
||||||
* @param {[type]} dom [要操作的元素]
|
* @param {[type]} dom [要操作的元素]
|
||||||
* @param {[type]} forceHoleLine [是否强制光标所在的整行文本]
|
* @param {[type]} forceHoleLine [是否强制光标所在的整行文本]
|
||||||
*/
|
*/
|
||||||
selection(forceHoleLine) {
|
selection(forceHoleLine = false) {
|
||||||
var dom = this.__EDITOR__
|
var dom = this.__EDITOR__
|
||||||
if (document.selection) {
|
var start = dom.selectionStart
|
||||||
return document.selection.createRange().text
|
var end = dom.selectionEnd
|
||||||
} else {
|
|
||||||
let startPos = dom.selectionStart
|
|
||||||
let endPos = dom.selectionEnd
|
|
||||||
|
|
||||||
if (endPos) {
|
if (end) {
|
||||||
//强制选择整行
|
//强制选择整行
|
||||||
if (forceHoleLine) {
|
if (forceHoleLine) {
|
||||||
startPos = dom.value.slice(0, startPos).lastIndexOf('\n')
|
start = dom.value.slice(0, start).lastIndexOf('\n')
|
||||||
|
|
||||||
let tmpEnd = dom.value.slice(endPos).indexOf('\n')
|
let tmpEnd = dom.value.slice(end).indexOf('\n')
|
||||||
tmpEnd = tmpEnd < 0 ? dom.value.slice(endPos).length : tmpEnd
|
tmpEnd = tmpEnd < 0 ? dom.value.slice(end).length : tmpEnd
|
||||||
|
|
||||||
startPos += 1 // 把\n加上
|
start += 1 // 把\n加上
|
||||||
endPos += tmpEnd
|
end += tmpEnd
|
||||||
|
|
||||||
dom.selectionStart = startPos
|
dom.selectionStart = start
|
||||||
dom.selectionEnd = endPos
|
dom.selectionEnd = end
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
//强制选择整行
|
//强制选择整行
|
||||||
if (forceHoleLine) {
|
if (forceHoleLine) {
|
||||||
endPos = dom.value.indexOf('\n')
|
end = dom.value.indexOf('\n')
|
||||||
endPos = endPos < 0 ? dom.value.length : endPos
|
end = end < 0 ? dom.value.length : end
|
||||||
dom.selectionEnd = endPos
|
dom.selectionEnd = end
|
||||||
}
|
|
||||||
}
|
}
|
||||||
dom.focus()
|
|
||||||
return dom.value.slice(startPos, endPos)
|
|
||||||
}
|
}
|
||||||
|
dom.focus()
|
||||||
|
return dom.value.slice(start, end)
|
||||||
|
}
|
||||||
|
|
||||||
|
select(start = 0, end = 0) {
|
||||||
|
var dom = this.__EDITOR__
|
||||||
|
dom.selectionStart = start
|
||||||
|
dom.selectionEnd = end
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor(step) {
|
||||||
|
var pos = this.__EDITOR__.selectionStart || 0
|
||||||
|
pos += step
|
||||||
|
|
||||||
|
if (step === 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pos < 0) {
|
||||||
|
pos = 0
|
||||||
|
}
|
||||||
|
this.select(pos, pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
_getCursorText(n) {
|
||||||
|
var dom = this.__EDITOR__
|
||||||
|
var start = dom.selectionStart
|
||||||
|
var end = dom.selectionEnd
|
||||||
|
var pos = end
|
||||||
|
|
||||||
|
if (n < 0) {
|
||||||
|
pos = start - 1
|
||||||
|
if (pos < 0) {
|
||||||
|
pos = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this.value[pos] || ''
|
||||||
}
|
}
|
||||||
|
|
||||||
get value() {
|
get value() {
|
||||||
|
@ -701,7 +739,28 @@ export default class Meditor {
|
||||||
set value(val) {
|
set value(val) {
|
||||||
this.__EDITOR__.value = val
|
this.__EDITOR__.value = val
|
||||||
if (this.props.preview) {
|
if (this.props.preview) {
|
||||||
this.__VIEW__.textContent = val
|
this.__VIEW__.value = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get disabled() {
|
||||||
|
return this.props.disabled
|
||||||
|
}
|
||||||
|
|
||||||
|
set disabled(val) {
|
||||||
|
var type = typeof val
|
||||||
|
|
||||||
|
if (val === this.props.disabled) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ((type === 'boolean' && val) || type !== 'boolean') {
|
||||||
|
this.props.disabled = true
|
||||||
|
this.__EDITOR__.disabled = true
|
||||||
|
this.setAttribute('disabled', '')
|
||||||
|
} else {
|
||||||
|
this.props.disabled = false
|
||||||
|
this.__EDITOR__.disabled = false
|
||||||
|
this.removeAttribute('disabled')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -717,7 +776,7 @@ export default class Meditor {
|
||||||
if (this.props.preview) {
|
if (this.props.preview) {
|
||||||
var txt = this.__EDITOR__.value.trim()
|
var txt = this.__EDITOR__.value.trim()
|
||||||
if (txt) {
|
if (txt) {
|
||||||
this.__VIEW__.textContent = txt
|
this.__VIEW__.value = txt
|
||||||
} else {
|
} else {
|
||||||
this.__VIEW__.clear()
|
this.__VIEW__.clear()
|
||||||
}
|
}
|
||||||
|
@ -727,23 +786,88 @@ export default class Meditor {
|
||||||
$.bind(this.__EDITOR__, 'keydown', ev => {
|
$.bind(this.__EDITOR__, 'keydown', ev => {
|
||||||
let wrap = this.selection() || ''
|
let wrap = this.selection() || ''
|
||||||
let select = !!wrap
|
let select = !!wrap
|
||||||
//tab键改为插入2个空格,阻止默认事件,防止焦点失去
|
|
||||||
if (ev.keyCode === 9) {
|
switch (ev.keyCode) {
|
||||||
ev.preventDefault()
|
//tab键改为插入2个空格,阻止默认事件,防止焦点失去
|
||||||
wrap = wrap
|
case 9:
|
||||||
.split('\n')
|
|
||||||
.map(function(it) {
|
|
||||||
return ev.shiftKey ? it.replace(/^\s\s/, '') : ' ' + it
|
|
||||||
})
|
|
||||||
.join('\n')
|
|
||||||
this.insert(wrap, select)
|
|
||||||
}
|
|
||||||
//修复按退格键删除选中文本时,选中的状态不更新的bug
|
|
||||||
if (ev.keyCode === 8) {
|
|
||||||
if (select) {
|
|
||||||
ev.preventDefault()
|
ev.preventDefault()
|
||||||
this.insert('', select)
|
wrap = wrap
|
||||||
}
|
.split('\n')
|
||||||
|
.map(function(it) {
|
||||||
|
return ev.shiftKey ? it.replace(/^\s\s/, '') : ' ' + it
|
||||||
|
})
|
||||||
|
.join('\n')
|
||||||
|
this.insert(wrap, select)
|
||||||
|
break
|
||||||
|
|
||||||
|
//修复按退格键删除选中文本时,选中的状态不更新的bug
|
||||||
|
case 8:
|
||||||
|
if (!select) {
|
||||||
|
let pos = this.__EDITOR__.selectionStart
|
||||||
|
let prev = this._getCursorText(-1)
|
||||||
|
let next = this._getCursorText(1)
|
||||||
|
|
||||||
|
if (
|
||||||
|
(prev === next && (prev === '"' || prev === "'")) ||
|
||||||
|
(prev === '[' && next === ']') ||
|
||||||
|
(prev === '{' && next === '}') ||
|
||||||
|
(prev === '(' && next === ')')
|
||||||
|
) {
|
||||||
|
this.select(pos - 1, pos + 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
|
||||||
|
// (
|
||||||
|
case 57:
|
||||||
|
if (ev.shiftKey) {
|
||||||
|
ev.preventDefault()
|
||||||
|
|
||||||
|
this.insert('(' + wrap + ')', select, (select && 1) || 0)
|
||||||
|
this.cursor(select ? 0 : -1)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
|
||||||
|
case 219: // [ & {
|
||||||
|
ev.preventDefault()
|
||||||
|
this.insert(
|
||||||
|
ev.shiftKey ? `{${wrap}}` : `[${wrap}]`,
|
||||||
|
select,
|
||||||
|
(select && 1) || 0
|
||||||
|
)
|
||||||
|
this.cursor(select ? 0 : -1)
|
||||||
|
|
||||||
|
break
|
||||||
|
|
||||||
|
// ' & "
|
||||||
|
case 222:
|
||||||
|
{
|
||||||
|
ev.preventDefault()
|
||||||
|
let val = "'"
|
||||||
|
let prev = this._getCursorText(-1)
|
||||||
|
let next = this._getCursorText(1)
|
||||||
|
|
||||||
|
if (select) {
|
||||||
|
this.insert(val + wrap + val, true, 1)
|
||||||
|
} else {
|
||||||
|
if (ev.shiftKey) {
|
||||||
|
val = '"'
|
||||||
|
}
|
||||||
|
if (prev === next && prev === val) {
|
||||||
|
this.cursor(1)
|
||||||
|
} else {
|
||||||
|
if (prev === val || next === val) {
|
||||||
|
this.insert(val)
|
||||||
|
} else {
|
||||||
|
this.insert(val.repeat(2))
|
||||||
|
this.cursor(-1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -807,13 +931,21 @@ export default class Meditor {
|
||||||
this.value = val
|
this.value = val
|
||||||
break
|
break
|
||||||
|
|
||||||
|
case 'preview':
|
||||||
|
if (val === 'false') {
|
||||||
|
this.props.preview = false
|
||||||
|
} else {
|
||||||
|
this.props.preview = window.innerWidth > 768
|
||||||
|
}
|
||||||
|
break
|
||||||
|
|
||||||
case 'readonly':
|
case 'readonly':
|
||||||
case 'disabled':
|
case 'disabled':
|
||||||
var k = name
|
var k = name
|
||||||
if (k === 'readonly') {
|
if (k === 'readonly') {
|
||||||
k = 'readOnly'
|
k = 'readOnly'
|
||||||
}
|
}
|
||||||
this[k] = true
|
this[k] = val !== null
|
||||||
break
|
break
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
Reference in New Issue