ui/src/code/index.js

263 lines
5.8 KiB
JavaScript
Raw Normal View History

2023-03-21 16:50:02 +08:00
/**
* {}
* @author yutent<yutent.io@gmail.com>
* @date 2023/03/20 18:02:01
*/
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(/&lt;/g, '<')
.replace(/&gt;/g, '>')
.replace(/&amp;/g, '&')
}
2023-03-21 16:50:02 +08:00
class Code extends Component {
static props = {
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;
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);
}
}
.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;
> 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;
}
.t-tag,
.t-keyword,
.t-const {
color: var(--color-red-2);
}
.t-attr,
.t-fn,
.t-var {
color: var(--color-blue-2);
}
.t-str {
color: var(--color-green-2);
}
.t-comment {
color: var(--color-grey-2);
}
.t-type {
font-weight: bold;
color: var(--color-orange-1);
}
.t-num,
.t-buildin {
color: #a46ad3;
}
.t-link {
text-decoration: underline;
color: var(--color-grey-2);
}
.t-buildin,
.t-keyword,
.t-type,
.t-link,
.t-comment {
font-style: italic;
}
2023-03-21 16:50:02 +08:00
}
}
`
]
#code = []
setCode(txt, a) {
let lang = this.lang
let skip = false
switch (lang) {
case 'js':
case 'javascript':
case 'ts':
case 'typescript':
txt = colorJs(txt)
break
case 'html':
case 'xml':
txt = colorHtml(txt)
break
case 'css':
case 'scss':
case 'less':
skip = true
txt = colorCss(txt)
break
case 'md':
case 'markdown':
txt = colorMd(txt)
break
}
2023-05-04 15:24:48 +08:00
// 除代码高亮用的特殊标签 <c></c> 外, 其他的全部转义
// 避免渲染时混乱
if (skip === false) {
txt = txt
.replace(/<(\/?)(?!c)(\w+)([^>]*?)>/g, '&lt;$1$2$3>')
.replace(/<!(doctype)([^>]*?)>/gi, '&lt;$1$2>')
.replace(/<\?([^\?>]*?)\?>/g, '&lt;?$1?>')
.replace(/<\!\-\-([^>]*?)\-\->/g, '&lt;!--$1--&gt;')
}
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-03-21 16:50:02 +08:00
if (txt.startsWith('<xmp>') && txt.endsWith('</xmp>')) {
txt = txt.slice(5, -6).trim()
} 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
}
this.textContent = ''
2023-03-21 16:50:02 +08:00
if (txt) {
nextTick(_ => {
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>
<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">
${this.#code.map(s => html`<code>${raw(s)}</code>`)}
2023-03-21 16:50:02 +08:00
</div>
</div>
`
}
}
Code.reg('code')