2023-03-21 16:50:02 +08:00
|
|
|
/**
|
|
|
|
* {}
|
|
|
|
* @author yutent<yutent.io@gmail.com>
|
|
|
|
* @date 2023/03/20 18:02:01
|
|
|
|
*/
|
2023-04-23 15:36:35 +08:00
|
|
|
import { html, raw, css, Component, nextTick } from '@bd/core'
|
|
|
|
import { colorHtml, colorJs, colorCss, colorMd } from './colorful.js'
|
|
|
|
import '../icon/index.js'
|
|
|
|
import '../layer/index.js'
|
|
|
|
|
|
|
|
function trim(str) {
|
|
|
|
return str
|
|
|
|
.trim()
|
|
|
|
.replace(/</g, '<')
|
|
|
|
.replace(/>/g, '>')
|
|
|
|
.replace(/&/g, '&')
|
|
|
|
}
|
2023-03-21 16:50:02 +08:00
|
|
|
|
|
|
|
class Code extends Component {
|
|
|
|
static props = {
|
2023-04-23 15:36:35 +08:00
|
|
|
code: {
|
|
|
|
type: String,
|
|
|
|
default: '',
|
|
|
|
attribute: false,
|
|
|
|
observer(v) {
|
|
|
|
this.setCode(v.trim())
|
|
|
|
}
|
|
|
|
},
|
2023-03-21 16:50:02 +08:00
|
|
|
lang: ''
|
|
|
|
}
|
|
|
|
|
|
|
|
static styles = [
|
|
|
|
css`
|
|
|
|
:host {
|
|
|
|
display: flex;
|
|
|
|
border-radius: 3px;
|
|
|
|
}
|
|
|
|
.code-box {
|
|
|
|
display: flex;
|
|
|
|
flex-direction: column;
|
|
|
|
position: relative;
|
|
|
|
width: 100%;
|
|
|
|
margin: 10px 0;
|
2023-04-21 17:31:40 +08:00
|
|
|
padding-bottom: 6px;
|
2023-03-21 16:50:02 +08:00
|
|
|
border-radius: 3px;
|
|
|
|
background: #f7f8fb;
|
|
|
|
color: var(--color-dark-1);
|
|
|
|
box-shadow: 0 1px 5px rgba(0, 0, 0, 0.1);
|
|
|
|
|
|
|
|
.title {
|
|
|
|
flex-shrink: 0;
|
|
|
|
display: flex;
|
|
|
|
justify-content: space-between;
|
|
|
|
align-items: center;
|
|
|
|
width: 100%;
|
|
|
|
height: 32px;
|
|
|
|
padding: 0 12px;
|
|
|
|
line-height: 1;
|
|
|
|
font-size: 14px;
|
|
|
|
user-select: none;
|
|
|
|
|
|
|
|
section {
|
|
|
|
display: flex;
|
|
|
|
align-items: center;
|
|
|
|
}
|
|
|
|
i {
|
|
|
|
display: block;
|
|
|
|
width: 12px;
|
|
|
|
height: 12px;
|
|
|
|
margin-right: 6px;
|
|
|
|
border-radius: 50%;
|
|
|
|
background: var(--color-red-1);
|
|
|
|
|
|
|
|
&:nth-child(2) {
|
|
|
|
background: var(--color-orange-1);
|
|
|
|
}
|
|
|
|
&:nth-child(3) {
|
|
|
|
background: var(--color-green-1);
|
|
|
|
}
|
|
|
|
}
|
2023-04-23 15:36:35 +08:00
|
|
|
|
|
|
|
.act {
|
|
|
|
--size: 18px;
|
|
|
|
margin: 0 6px;
|
|
|
|
color: var(--color-grey-2);
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
|
|
&:hover {
|
|
|
|
color: var(--color-grey-3);
|
|
|
|
}
|
|
|
|
}
|
2023-03-21 16:50:02 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
css`
|
|
|
|
.code-block {
|
|
|
|
display: flex;
|
|
|
|
flex-direction: column;
|
|
|
|
overflow: hidden;
|
|
|
|
overflow-y: auto;
|
|
|
|
line-height: 20px;
|
|
|
|
font-size: 14px;
|
|
|
|
color: var(--color-dark-1);
|
|
|
|
cursor: text;
|
|
|
|
counter-reset: code;
|
|
|
|
|
2023-04-24 17:34:01 +08:00
|
|
|
> code {
|
2023-03-21 16:50:02 +08:00
|
|
|
display: block;
|
|
|
|
position: relative;
|
|
|
|
min-height: 20px;
|
|
|
|
padding: 0 8px 0 45px;
|
|
|
|
font-family: Menlo, Monaco, Consolas, 'Courier New', monospace;
|
|
|
|
white-space: pre-wrap;
|
|
|
|
word-break: break-word;
|
|
|
|
|
|
|
|
&::before {
|
|
|
|
position: absolute;
|
|
|
|
left: 0;
|
|
|
|
width: 40px;
|
|
|
|
height: 100%;
|
|
|
|
padding-right: 5px;
|
|
|
|
text-align: right;
|
|
|
|
color: var(--color-grey-1);
|
|
|
|
content: counter(code);
|
|
|
|
counter-increment: code;
|
|
|
|
}
|
2023-04-23 15:36:35 +08:00
|
|
|
|
2023-04-24 17:34:01 +08:00
|
|
|
.t-tag,
|
|
|
|
.t-keyword,
|
|
|
|
.t-const {
|
2023-04-23 15:36:35 +08:00
|
|
|
color: var(--color-red-2);
|
|
|
|
}
|
2023-04-24 17:34:01 +08:00
|
|
|
.t-attr,
|
|
|
|
.t-fn,
|
|
|
|
.t-var {
|
2023-04-23 15:36:35 +08:00
|
|
|
color: var(--color-blue-2);
|
|
|
|
}
|
2023-04-24 17:34:01 +08:00
|
|
|
.t-str {
|
2023-04-23 15:36:35 +08:00
|
|
|
color: var(--color-green-2);
|
|
|
|
}
|
2023-04-24 17:34:01 +08:00
|
|
|
.t-comment {
|
2023-04-23 15:36:35 +08:00
|
|
|
color: var(--color-grey-2);
|
|
|
|
}
|
2023-04-24 17:34:01 +08:00
|
|
|
|
|
|
|
.t-type {
|
|
|
|
font-weight: bold;
|
2023-04-23 15:36:35 +08:00
|
|
|
color: var(--color-orange-1);
|
|
|
|
}
|
2023-04-24 17:34:01 +08:00
|
|
|
.t-num,
|
|
|
|
.t-buildin {
|
2023-04-23 15:36:35 +08:00
|
|
|
color: #a46ad3;
|
|
|
|
}
|
2023-04-24 17:34:01 +08:00
|
|
|
.t-link {
|
2023-04-23 15:36:35 +08:00
|
|
|
text-decoration: underline;
|
|
|
|
color: var(--color-grey-2);
|
|
|
|
}
|
2023-04-24 17:34:01 +08:00
|
|
|
.t-buildin,
|
|
|
|
.t-keyword,
|
|
|
|
.t-type,
|
|
|
|
.t-link,
|
|
|
|
.t-comment {
|
|
|
|
font-style: italic;
|
|
|
|
}
|
2023-03-21 16:50:02 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
`
|
|
|
|
]
|
|
|
|
|
2023-04-23 15:36:35 +08:00
|
|
|
#code = []
|
|
|
|
|
|
|
|
setCode(txt, a) {
|
|
|
|
let lang = this.lang
|
|
|
|
|
|
|
|
switch (lang) {
|
|
|
|
case 'js':
|
|
|
|
case 'javascript':
|
|
|
|
case 'ts':
|
|
|
|
case 'typescript':
|
|
|
|
txt = colorJs(txt)
|
|
|
|
break
|
|
|
|
|
|
|
|
case 'html':
|
|
|
|
txt = colorHtml(txt)
|
|
|
|
break
|
|
|
|
|
|
|
|
case 'css':
|
|
|
|
case 'scss':
|
|
|
|
case 'less':
|
|
|
|
txt = colorCss(txt)
|
|
|
|
break
|
|
|
|
|
|
|
|
case 'md':
|
|
|
|
case 'markdown':
|
|
|
|
txt = colorMd(txt)
|
|
|
|
break
|
|
|
|
}
|
2023-05-04 15:24:48 +08:00
|
|
|
// 除代码高亮用的特殊标签 <c></c> 外, 其他的全部转义
|
|
|
|
// 避免渲染时混乱
|
|
|
|
txt = txt.replace(/<(\/?)(?!c)(\w+)([^>]*?)>/g, '<$1$2$3>')
|
2023-04-23 15:36:35 +08:00
|
|
|
this.#code = txt.split('\n')
|
|
|
|
}
|
|
|
|
|
|
|
|
copyCode() {
|
|
|
|
navigator.clipboard.writeText(this.code)
|
|
|
|
layer.toast('复制到粘贴板成功', 'success')
|
|
|
|
}
|
|
|
|
|
2023-03-21 16:50:02 +08:00
|
|
|
mounted() {
|
|
|
|
var txt = this.innerHTML || this.textContent
|
|
|
|
txt = txt.trim().replace(/^[\r\n]|\s{2,}$/g, '')
|
2023-04-23 15:36:35 +08:00
|
|
|
|
2023-03-21 16:50:02 +08:00
|
|
|
if (txt.startsWith('<xmp>') && txt.endsWith('</xmp>')) {
|
|
|
|
txt = txt.slice(5, -6).trim()
|
2023-04-23 15:36:35 +08:00
|
|
|
} else if (this.firstElementChild?.tagName === 'TEXTAREA') {
|
|
|
|
txt = this.firstElementChild.value.trim()
|
|
|
|
} else if (txt.startsWith('<pre>') && txt.endsWith('</pre>')) {
|
|
|
|
txt = trim(txt.slice(5, -6))
|
|
|
|
} else {
|
|
|
|
txt = trim(txt)
|
2023-03-21 16:50:02 +08:00
|
|
|
}
|
2023-04-23 15:36:35 +08:00
|
|
|
|
|
|
|
this.textContent = ''
|
2023-03-21 16:50:02 +08:00
|
|
|
if (txt) {
|
|
|
|
nextTick(_ => {
|
2023-04-23 15:36:35 +08:00
|
|
|
this.code = txt
|
2023-03-21 16:50:02 +08:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
render() {
|
|
|
|
return html`
|
|
|
|
<div class="code-box">
|
|
|
|
<header class="title">
|
|
|
|
<section><i></i><i></i><i></i></section>
|
|
|
|
<section>${this.lang}</section>
|
2023-04-23 15:36:35 +08:00
|
|
|
<wc-icon
|
|
|
|
title="复制"
|
|
|
|
class="act"
|
|
|
|
name="doc"
|
|
|
|
@click=${this.copyCode}
|
|
|
|
></wc-icon>
|
2023-03-21 16:50:02 +08:00
|
|
|
</header>
|
|
|
|
<div class="code-block">
|
2023-04-23 15:36:35 +08:00
|
|
|
${this.#code.map(s => html`<code>${raw(s)}</code>`)}
|
2023-03-21 16:50:02 +08:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
`
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Code.reg('code')
|