更新markdown编辑器;

master
yutent 2023-10-11 18:47:24 +08:00
parent 09356d0d92
commit 779aeb65a2
5 changed files with 152 additions and 142 deletions

View File

@ -605,7 +605,7 @@ class Editor extends Component {
}
}
#chnageFontSize(ev) {
#changeFontSize(ev) {
if (ev.target === ev.currentTarget) {
return
}
@ -677,6 +677,8 @@ class Editor extends Component {
grids[i].classList.toggle('active', _x <= x && _y <= y)
}
} else {
this.#gridx = -1
this.#gridy = -1
grids.forEach(it => it.classList.remove('active'))
}
}
@ -826,7 +828,7 @@ class Editor extends Component {
<div
class="font-layer noselect"
ref="font"
@click=${this.#chnageFontSize}
@click=${this.#changeFontSize}
>
<span data-size="6">6号字体</span>
<span data-size="5">5号字体</span>

View File

@ -33,7 +33,7 @@ const Helper = {
isHr(str) {
var s = str[0]
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
},
@ -118,8 +118,8 @@ const Decoder = {
.replace(ESCAPE_RE, '$1') // 处理转义字符
},
// 分割线
hr(name = '') {
return `\n\n<fieldset class="md-hr"><legend name="${name}"></legend></fieldset>\n\n`
hr() {
return `\n\n<hr>\n\n`
},
// 标题
head(str) {
@ -362,10 +362,8 @@ class Tool {
}
// 无属性标签
let hrName = Helper.isHr(it)
if (typeof hrName === 'string') {
html += Decoder.hr(hrName)
if (Helper.isHr(it)) {
html += Decoder.hr()
continue
}

View File

@ -76,19 +76,11 @@ class Markd extends Component {
}
}
fieldset.md-hr {
hr {
height: 1px;
margin: 30px 0;
border: 0;
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 {
margin-left: 1em;

View File

@ -30,7 +30,7 @@ function showDialog(dialog, elem) {
export default {
header(elem) {
showDialog(this.__HEADER_ADDON__, elem)
this.$refs.header.classList.add('fadein')
},
h(level) {
@ -97,7 +97,8 @@ export default {
},
table(elem) {
showDialog(this.__TABLE_ADDON__, elem)
// showDialog(this.__TABLE_ADDON__, elem)
this.$refs.table.classList.add('fadein')
},
link(elem) {

View File

@ -234,57 +234,78 @@ class MEditor extends Component {
`,
css`
.addon-table {
display: flex;
flex-direction: column;
justify-content: space-between;
width: 222px;
height: 222px;
padding: 2px;
.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;
li {
display: flex;
justify-content: space-between;
height: 20px;
&.fadein {
visibility: visible;
top: 34px;
opacity: 1;
}
}
span {
width: 20px;
height: 20px;
background: var(--color-plain-1);
.font-layer {
font-family: Menlo, Monaco, Consolas, 'Courier New', monospace;
&.active {
background: rgba(77, 182, 172, 0.3);
}
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;
}
}
}
.addon-header {
width: 108px;
height: 190px;
padding: 5px 0;
line-height: 30px;
user-select: none;
.table-layer {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
left: 240px;
width: 200px;
height: 200px;
padding: 2px;
background: #fff;
li {
display: flex;
align-items: center;
width: 100%;
height: 30px;
padding: 0 12px;
transition: background 0.1s ease-in-out;
cursor: pointer;
span {
width: 20px;
height: 20px;
background: var(--color-plain-1);
.icon {
width: 14px;
height: 14px;
margin-right: 8px;
}
&:hover {
background: var(--color-plain-1);
&.active {
background: rgba(77, 182, 172, 0.3);
}
}
}
@ -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 {
return this.$refs.editor.value
} catch (err) {
console.log(err)
return ''
}
}
@ -431,7 +470,7 @@ class MEditor extends Component {
#toolbar = [...DEFAULT_TOOLS]
#value = ''
#cache = { bar: 0, y: 0 }
#gridx = 0
#gridy = 0
@ -439,49 +478,10 @@ class MEditor extends Component {
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() {
this.$refs.font.classList.remove('fadein')
this.$refs.color.classList.remove('fadein')
this.$refs.link.classList.remove('fadein')
this.$refs.header.classList.remove('fadein')
// this.$refs.color.classList.remove('fadein')
// this.$refs.link.classList.remove('fadein')
this.$refs.table.classList.remove('fadein')
}
@ -548,8 +548,6 @@ class MEditor extends Component {
var elem = ev.target
var act
this.restoreSelection()
if (elem === ev.currentTarget) {
return
}
@ -564,20 +562,19 @@ class MEditor extends Component {
act = elem.dataset.act
// this.#hideLayers()
this.#hideLayers()
Addon[act].call(this, elem)
}
#chnageFontSize(ev) {
#setHeaderLevel(ev) {
if (ev.target === ev.currentTarget) {
return
}
this.$refs.font.classList.remove('fadein')
this.$refs.header.classList.remove('fadein')
this.$refs.editor.focus()
this.restoreSelection()
this.exec(ACTTION.font, ev.target.dataset.size)
this.saveSelection()
let level = +ev.target.dataset.value
Addon.h.call(this, level)
}
#chnageColor(ev) {
@ -604,17 +601,16 @@ class MEditor extends Component {
}
#insertTable(ev) {
let th = `<th>&nbsp;</th>`.repeat(this.#gridx + 1)
let td = `<td>&nbsp;</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')
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) {
@ -641,29 +637,12 @@ class MEditor extends Component {
grids[i].classList.toggle('active', _x <= x && _y <= y)
}
} else {
this.#gridx = -1
this.#gridy = -1
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 [要插入的文本]
@ -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() {
Addon.preview.call(this, this)
}
@ -934,9 +925,35 @@ class MEditor extends Component {
@keydown=${this.#handleKeydown}
@paste.prevent=${this.#handlePaste}
@input=${this.#updatePreview}
@scroll=${this.#syncScrollToPreview}
></textarea>
<wc-markd ref="view" class="preview"></wc-markd>
</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>
`
}