更新markdown编辑器;
parent
09356d0d92
commit
779aeb65a2
|
@ -605,7 +605,7 @@ class Editor extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#chnageFontSize(ev) {
|
#changeFontSize(ev) {
|
||||||
if (ev.target === ev.currentTarget) {
|
if (ev.target === ev.currentTarget) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -677,6 +677,8 @@ class Editor extends Component {
|
||||||
grids[i].classList.toggle('active', _x <= x && _y <= y)
|
grids[i].classList.toggle('active', _x <= x && _y <= y)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
this.#gridx = -1
|
||||||
|
this.#gridy = -1
|
||||||
grids.forEach(it => it.classList.remove('active'))
|
grids.forEach(it => it.classList.remove('active'))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -826,7 +828,7 @@ class Editor extends Component {
|
||||||
<div
|
<div
|
||||||
class="font-layer noselect"
|
class="font-layer noselect"
|
||||||
ref="font"
|
ref="font"
|
||||||
@click=${this.#chnageFontSize}
|
@click=${this.#changeFontSize}
|
||||||
>
|
>
|
||||||
<span data-size="6">6号字体</span>
|
<span data-size="6">6号字体</span>
|
||||||
<span data-size="5">5号字体</span>
|
<span data-size="5">5号字体</span>
|
||||||
|
|
|
@ -33,7 +33,7 @@ const Helper = {
|
||||||
isHr(str) {
|
isHr(str) {
|
||||||
var s = str[0]
|
var s = str[0]
|
||||||
if (HR_LIST.includes(s)) {
|
if (HR_LIST.includes(s)) {
|
||||||
return str.slice(0, 3) === s.repeat(3) ? str.slice(3) : false
|
return str.trim() === s.repeat(3)
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
},
|
},
|
||||||
|
@ -118,8 +118,8 @@ const Decoder = {
|
||||||
.replace(ESCAPE_RE, '$1') // 处理转义字符
|
.replace(ESCAPE_RE, '$1') // 处理转义字符
|
||||||
},
|
},
|
||||||
// 分割线
|
// 分割线
|
||||||
hr(name = '') {
|
hr() {
|
||||||
return `\n\n<fieldset class="md-hr"><legend name="${name}"></legend></fieldset>\n\n`
|
return `\n\n<hr>\n\n`
|
||||||
},
|
},
|
||||||
// 标题
|
// 标题
|
||||||
head(str) {
|
head(str) {
|
||||||
|
@ -362,10 +362,8 @@ class Tool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 无属性标签
|
// 无属性标签
|
||||||
|
if (Helper.isHr(it)) {
|
||||||
let hrName = Helper.isHr(it)
|
html += Decoder.hr()
|
||||||
if (typeof hrName === 'string') {
|
|
||||||
html += Decoder.hr(hrName)
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -76,19 +76,11 @@ class Markd extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fieldset.md-hr {
|
hr {
|
||||||
|
height: 1px;
|
||||||
margin: 30px 0;
|
margin: 30px 0;
|
||||||
border: 0;
|
border: 0;
|
||||||
border-top: 1px dashed var(--color-plain-3);
|
border-top: 1px dashed var(--color-plain-3);
|
||||||
|
|
||||||
legend {
|
|
||||||
color: var(--color-grey-1);
|
|
||||||
text-align: center;
|
|
||||||
font-size: 12px;
|
|
||||||
&::before {
|
|
||||||
content: attr(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ol {
|
ol {
|
||||||
margin-left: 1em;
|
margin-left: 1em;
|
||||||
|
|
|
@ -30,7 +30,7 @@ function showDialog(dialog, elem) {
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
header(elem) {
|
header(elem) {
|
||||||
showDialog(this.__HEADER_ADDON__, elem)
|
this.$refs.header.classList.add('fadein')
|
||||||
},
|
},
|
||||||
|
|
||||||
h(level) {
|
h(level) {
|
||||||
|
@ -97,7 +97,8 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
table(elem) {
|
table(elem) {
|
||||||
showDialog(this.__TABLE_ADDON__, elem)
|
// showDialog(this.__TABLE_ADDON__, elem)
|
||||||
|
this.$refs.table.classList.add('fadein')
|
||||||
},
|
},
|
||||||
|
|
||||||
link(elem) {
|
link(elem) {
|
||||||
|
|
|
@ -234,20 +234,71 @@ class MEditor extends Component {
|
||||||
`,
|
`,
|
||||||
|
|
||||||
css`
|
css`
|
||||||
.addon-table {
|
.font-layer,
|
||||||
|
.table-layer {
|
||||||
|
visibility: hidden;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
z-index: 99;
|
||||||
|
width: 80px;
|
||||||
|
padding: 5px 0;
|
||||||
|
line-height: 25px;
|
||||||
|
background: #fff;
|
||||||
|
box-shadow: 0 0 8px rgba(0, 0, 0, 0.2);
|
||||||
|
font-size: 13px;
|
||||||
|
opacity: 0;
|
||||||
|
transition: all ease-in-out 0.2s;
|
||||||
|
|
||||||
|
&.fadein {
|
||||||
|
visibility: visible;
|
||||||
|
top: 34px;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.font-layer {
|
||||||
|
font-family: Menlo, Monaco, Consolas, 'Courier New', monospace;
|
||||||
|
|
||||||
|
span {
|
||||||
|
display: block;
|
||||||
|
padding: 0 8px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #f7f8fb;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
&:nth-child(2) {
|
||||||
|
font-size: 22px;
|
||||||
|
}
|
||||||
|
&:nth-child(3) {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
&:nth-child(4) {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
&:nth-child(5) {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
&:nth-child(6) {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-layer {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-wrap: wrap;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
width: 222px;
|
left: 240px;
|
||||||
height: 222px;
|
width: 200px;
|
||||||
|
height: 200px;
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
|
|
||||||
li {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
height: 20px;
|
|
||||||
|
|
||||||
span {
|
span {
|
||||||
width: 20px;
|
width: 20px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
|
@ -258,36 +309,6 @@ class MEditor extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.addon-header {
|
|
||||||
width: 108px;
|
|
||||||
height: 190px;
|
|
||||||
padding: 5px 0;
|
|
||||||
line-height: 30px;
|
|
||||||
user-select: none;
|
|
||||||
background: #fff;
|
|
||||||
|
|
||||||
li {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
width: 100%;
|
|
||||||
height: 30px;
|
|
||||||
padding: 0 12px;
|
|
||||||
transition: background 0.1s ease-in-out;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
.icon {
|
|
||||||
width: 14px;
|
|
||||||
height: 14px;
|
|
||||||
margin-right: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: var(--color-plain-1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.addon-link {
|
.addon-link {
|
||||||
width: 320px;
|
width: 320px;
|
||||||
|
@ -404,6 +425,25 @@ class MEditor extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
`,
|
||||||
|
|
||||||
|
css`
|
||||||
|
.editor,
|
||||||
|
wc-markd {
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
width: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-scrollbar-thumb {
|
||||||
|
visibility: hidden;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover::-webkit-scrollbar-thumb {
|
||||||
|
visibility: visible;
|
||||||
|
background: rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
}
|
||||||
`
|
`
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -411,7 +451,6 @@ class MEditor extends Component {
|
||||||
try {
|
try {
|
||||||
return this.$refs.editor.value
|
return this.$refs.editor.value
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err)
|
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -431,7 +470,7 @@ class MEditor extends Component {
|
||||||
|
|
||||||
#toolbar = [...DEFAULT_TOOLS]
|
#toolbar = [...DEFAULT_TOOLS]
|
||||||
#value = ''
|
#value = ''
|
||||||
#cache = { bar: 0, y: 0 }
|
|
||||||
#gridx = 0
|
#gridx = 0
|
||||||
#gridy = 0
|
#gridy = 0
|
||||||
|
|
||||||
|
@ -439,49 +478,10 @@ class MEditor extends Component {
|
||||||
|
|
||||||
previewEnabled = false
|
previewEnabled = false
|
||||||
|
|
||||||
__init__() {
|
|
||||||
//
|
|
||||||
let { outer, inner, thumb } = this.$refs
|
|
||||||
let height = outer.offsetHeight
|
|
||||||
let scrollHeight = inner.scrollHeight + 10
|
|
||||||
|
|
||||||
let bar = 50 // 滚动条的高度
|
|
||||||
bar = (height * (height / scrollHeight)) >> 0
|
|
||||||
|
|
||||||
if (bar < 50) {
|
|
||||||
bar = 50
|
|
||||||
}
|
|
||||||
|
|
||||||
// 100%或主体高度比滚动条还短时不显示
|
|
||||||
if (bar >= height) {
|
|
||||||
bar = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
this.#cache.bar = bar
|
|
||||||
thumb.style.height = bar + 'px'
|
|
||||||
}
|
|
||||||
|
|
||||||
#fetchScroll(moveY) {
|
|
||||||
let { bar } = this.#cache
|
|
||||||
let { outer, thumb, inner } = this.$refs
|
|
||||||
let height = outer.offsetHeight
|
|
||||||
let scrollHeight = inner.scrollHeight + 10
|
|
||||||
|
|
||||||
if (moveY < 0) {
|
|
||||||
moveY = 0
|
|
||||||
} else if (moveY > height - bar) {
|
|
||||||
moveY = height - bar
|
|
||||||
}
|
|
||||||
|
|
||||||
inner.scrollTop = (scrollHeight - height) * (moveY / (height - bar))
|
|
||||||
thumb.style.transform = `translateY(${moveY}px)`
|
|
||||||
return moveY
|
|
||||||
}
|
|
||||||
|
|
||||||
#hideLayers() {
|
#hideLayers() {
|
||||||
this.$refs.font.classList.remove('fadein')
|
this.$refs.header.classList.remove('fadein')
|
||||||
this.$refs.color.classList.remove('fadein')
|
// this.$refs.color.classList.remove('fadein')
|
||||||
this.$refs.link.classList.remove('fadein')
|
// this.$refs.link.classList.remove('fadein')
|
||||||
this.$refs.table.classList.remove('fadein')
|
this.$refs.table.classList.remove('fadein')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -548,8 +548,6 @@ class MEditor extends Component {
|
||||||
var elem = ev.target
|
var elem = ev.target
|
||||||
var act
|
var act
|
||||||
|
|
||||||
this.restoreSelection()
|
|
||||||
|
|
||||||
if (elem === ev.currentTarget) {
|
if (elem === ev.currentTarget) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -564,20 +562,19 @@ class MEditor extends Component {
|
||||||
|
|
||||||
act = elem.dataset.act
|
act = elem.dataset.act
|
||||||
|
|
||||||
// this.#hideLayers()
|
this.#hideLayers()
|
||||||
|
|
||||||
Addon[act].call(this, elem)
|
Addon[act].call(this, elem)
|
||||||
}
|
}
|
||||||
|
|
||||||
#chnageFontSize(ev) {
|
#setHeaderLevel(ev) {
|
||||||
if (ev.target === ev.currentTarget) {
|
if (ev.target === ev.currentTarget) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.$refs.font.classList.remove('fadein')
|
this.$refs.header.classList.remove('fadein')
|
||||||
this.$refs.editor.focus()
|
this.$refs.editor.focus()
|
||||||
this.restoreSelection()
|
let level = +ev.target.dataset.value
|
||||||
this.exec(ACTTION.font, ev.target.dataset.size)
|
Addon.h.call(this, level)
|
||||||
this.saveSelection()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#chnageColor(ev) {
|
#chnageColor(ev) {
|
||||||
|
@ -604,17 +601,16 @@ class MEditor extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
#insertTable(ev) {
|
#insertTable(ev) {
|
||||||
let th = `<th> </th>`.repeat(this.#gridx + 1)
|
|
||||||
let td = `<td> </td>`.repeat(this.#gridx + 1)
|
|
||||||
this.exec(
|
|
||||||
'insertHtml',
|
|
||||||
`<br>
|
|
||||||
<table>
|
|
||||||
<thead><tr>${th}</tr></thead>
|
|
||||||
<tbody>${`<tr>${td}</tr>`.repeat(this.#gridy + 1)}</tbody>
|
|
||||||
</table><br>`
|
|
||||||
)
|
|
||||||
this.$refs.table.classList.remove('fadein')
|
this.$refs.table.classList.remove('fadein')
|
||||||
|
if (this.#gridx < 0 || this.#gridy < 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let thead = `\n\n${'| 表头 '.repeat(this.#gridx)}|\n`
|
||||||
|
let pipe = `${'| -- '.repeat(this.#gridx)}|\n`
|
||||||
|
let tbody = ('| '.repeat(this.#gridx) + '|\n').repeat(this.#gridy)
|
||||||
|
this.#gridx = -1
|
||||||
|
this.#gridy = -1
|
||||||
|
this.insert(thead + pipe + tbody, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
#tableSelect(ev) {
|
#tableSelect(ev) {
|
||||||
|
@ -641,29 +637,12 @@ class MEditor extends Component {
|
||||||
grids[i].classList.toggle('active', _x <= x && _y <= y)
|
grids[i].classList.toggle('active', _x <= x && _y <= y)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
this.#gridx = -1
|
||||||
|
this.#gridy = -1
|
||||||
grids.forEach(it => it.classList.remove('active'))
|
grids.forEach(it => it.classList.remove('active'))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 保存选中
|
|
||||||
saveSelection() {
|
|
||||||
var gs = this.root.getSelection()
|
|
||||||
if (gs.getRangeAt && gs.rangeCount) {
|
|
||||||
this.#select = gs.getRangeAt(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 清除选中并重置选中
|
|
||||||
restoreSelection() {
|
|
||||||
var gs = this.root.getSelection()
|
|
||||||
if (this.#select) {
|
|
||||||
try {
|
|
||||||
gs.removeAllRanges()
|
|
||||||
} catch (err) {}
|
|
||||||
gs.addRange(this.#select)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 往文本框中插入内容
|
* 往文本框中插入内容
|
||||||
* @param {String} val [要插入的文本]
|
* @param {String} val [要插入的文本]
|
||||||
|
@ -907,6 +886,18 @@ class MEditor extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#syncScrollToPreview(ev) {
|
||||||
|
if (this.previewEnabled) {
|
||||||
|
let { editor, view } = this.$refs
|
||||||
|
let st = editor.scrollTop
|
||||||
|
let eh = editor.clientHeight
|
||||||
|
let sh = editor.scrollHeight
|
||||||
|
let veh = view.clientHeight
|
||||||
|
let vsh = view.scrollHeight
|
||||||
|
this.$refs.view.scrollTop = (st / (sh - eh)) * (vsh - veh)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
Addon.preview.call(this, this)
|
Addon.preview.call(this, this)
|
||||||
}
|
}
|
||||||
|
@ -934,9 +925,35 @@ class MEditor extends Component {
|
||||||
@keydown=${this.#handleKeydown}
|
@keydown=${this.#handleKeydown}
|
||||||
@paste.prevent=${this.#handlePaste}
|
@paste.prevent=${this.#handlePaste}
|
||||||
@input=${this.#updatePreview}
|
@input=${this.#updatePreview}
|
||||||
|
@scroll=${this.#syncScrollToPreview}
|
||||||
></textarea>
|
></textarea>
|
||||||
<wc-markd ref="view" class="preview"></wc-markd>
|
<wc-markd ref="view" class="preview"></wc-markd>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="font-layer noselect"
|
||||||
|
ref="header"
|
||||||
|
@click=${this.#setHeaderLevel}
|
||||||
|
>
|
||||||
|
<span data-value="1">H1</span>
|
||||||
|
<span data-value="2">H2</span>
|
||||||
|
<span data-value="3">H3</span>
|
||||||
|
<span data-value="4">H4</span>
|
||||||
|
<span data-value="5">H5</span>
|
||||||
|
<span data-value="6">H6</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="table-layer noselect"
|
||||||
|
ref="table"
|
||||||
|
@click=${this.#insertTable}
|
||||||
|
@mousemove=${this.#tableSelect}
|
||||||
|
@mouseleave=${this.#tableSelect}
|
||||||
|
>
|
||||||
|
${Array(81)
|
||||||
|
.fill(0)
|
||||||
|
.map((_, n) => html`<span data-idx=${n}></span>`)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue