/** * {} * @author yutent * @date 2023/03/20 18:02:01 */ import { html, raw, css, Component, nextTick } from 'wkit' 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, '&') } class Code extends Component { static props = { code: { type: String, default: '', attribute: false, observer(v) { this.setCode(v.trim()) } }, 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; 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 { --wc-icon-size: 18px; margin: 0 6px; color: var(--color-grey-2); cursor: pointer; &:hover { color: var(--color-grey-3); } } } } `, 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 { 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; } } } ` ] #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 } // 除代码高亮用的特殊标签 外, 其他的全部转义 // 避免渲染时混乱 if (skip === false) { txt = txt .replace(/<(\/?)(?!c)(\w+)([^>]*?)>/g, '<$1$2$3>') .replace(/]*?)>/gi, '<$1$2>') .replace(/<\?([^\?>]*?)\?>/g, '<?$1?>') .replace(/<\!\-\-([^>]*?)\-\->/g, '<!--$1-->') } this.#code = txt.split('\n') } copyCode() { if (navigator.clipboard) { navigator.clipboard.writeText(this.code) } else { let ta = document.createElement('textarea') ta.style.position = 'fixed' ta.style.left = '-2000px' ta.value = this.code this.root.appendChild(ta) ta.select() document.execCommand('copy') ta.remove() } layer.toast('复制到粘贴板成功', 'success') } mounted() { var txt = this.innerHTML || this.textContent txt = txt.trim().replace(/^[\r\n]|\s{2,}$/g, '') if (txt.startsWith('') && txt.endsWith('')) { txt = txt.slice(5, -6).trim() } else if (this.firstElementChild?.tagName === 'TEXTAREA') { txt = this.firstElementChild.value.trim() } else if (txt.startsWith('
') && txt.endsWith('
')) { txt = trim(txt.slice(5, -6)) } else { txt = trim(txt) } this.textContent = '' if (txt) { nextTick(_ => { this.code = txt }) } } render() { return html`
${this.lang}
${this.#code.map(s => html`${raw(s)}`)}
` } } Code.reg('code')