11<script setup lang="ts">
22import { useCopy } from ' @/composables'
3+ import { transformHTML } from ' @/utils/dom'
34
45import IconButton from ' ./IconButton.vue'
56import Copy from ' ./icons/Copy.vue'
@@ -12,7 +13,7 @@ const props = defineProps<{
1213}>()
1314
1415const prismAlias: Record <string , string > = {
15- vue: ' html' ,
16+ vue: ' html'
1617}
1718
1819const lang = computed (() => {
@@ -29,11 +30,36 @@ const highlighted = computed(() => {
2930 return props .code
3031 }
3132
32- return Prism .highlight (props .code , Prism .languages [lang .value ], lang .value )
33+ const html = Prism .highlight (props .code , Prism .languages [lang .value ], lang .value )
34+
35+ return transformHTML (html , (tpl ) => {
36+ tpl .querySelectorAll <HTMLElement >(' .token.variable, .token.constant' ).forEach ((el ) => {
37+ el .setAttribute (' tabindex' , ' 0' )
38+ el .classList .add (' copyable' )
39+ el .dataset .tooltipType = ' text'
40+ el .dataset .tooltip = ' Copy'
41+ el .innerHTML = ` <span>${el .innerHTML }</span> `
42+ })
43+
44+ tpl .querySelectorAll (' .token.number + .token.unit' ).forEach ((el ) => {
45+ const span = document .createElement (' span' )
46+ span .className = ' token dimension'
47+ el .parentNode ! .insertBefore (span , el .nextElementSibling )
48+ span .appendChild (el .previousElementSibling ! )
49+ span .appendChild (el )
50+ })
51+ })
3352})
3453
3554const code = computed (() => props .code )
3655const copy = useCopy (code )
56+
57+ function handleClick(event : MouseEvent ) {
58+ const target = event .target as HTMLElement
59+ if (target .closest (' .token.copyable' )) {
60+ copy (target )
61+ }
62+ }
3763 </script >
3864
3965<template >
@@ -47,7 +73,7 @@ const copy = useCopy(code)
4773 </IconButton >
4874 </div >
4975 </header >
50- <pre class =" tp-code-content" ><code v-html =" highlighted" /></pre >
76+ <pre class =" tp-code-content" ><code v-html =" highlighted" @click = " handleClick " /></pre >
5177 </section >
5278</template >
5379
@@ -100,6 +126,14 @@ const copy = useCopy(code)
100126 color : var (--color-text );
101127}
102128
129+ .tp-code-content .token.property {
130+ color : var (--color-text );
131+ }
132+
133+ .tp-code-content .token.plain {
134+ color : var (--color-codevalue );
135+ }
136+
103137.tp-code-content .token.selector {
104138 color : var (--color-codeclassname );
105139}
@@ -144,4 +178,52 @@ const copy = useCopy(code)
144178.highlight .token.number {
145179 color : var (--color-codevalue );
146180}
181+
182+ .tp-code-content .token.number ,
183+ .tp-code-content .token.unit {
184+ color : var (--color-codevalue );
185+ }
186+
187+ .tp-code-content .token.copyable {
188+ text-decoration-line : underline ;
189+ text-decoration-color : color-mix(in srgb , var (--color-codevariable ) 50% , transparent );
190+ text-decoration-style : dashed ;
191+ text-decoration-thickness : 1px ;
192+ text-underline-offset : 2px ;
193+ cursor : pointer ;
194+ -webkit-user-select : text ;
195+ user-select : text ;
196+ background : transparent ;
197+ display : unset ;
198+ }
199+
200+ .tp-code-content .token.copyable > span {
201+ line-height : 130% ;
202+ background : transparent ;
203+ border-radius : 4px ;
204+ padding : 0 2px ;
205+ margin : -2px ;
206+ }
207+
208+ .tp-code-content .token.copyable :hover > span {
209+ background : color-mix(in srgb , var (--color-codevariable ) 15% , transparent );
210+ }
211+
212+ .tp-code-content .token.copyable {
213+ outline : 2px solid transparent ;
214+ }
215+
216+ .tp-code-content .token.copyable :focus-visible {
217+ outline : 2px solid var (--color-border-selected );
218+ outline-offset : -2px ;
219+ }
220+
221+ .tp-code-content .token.variable :focus {
222+ outline : 2px solid var (--color-border-selected );
223+ outline-offset : -2px ;
224+ }
225+
226+ .tp-code-content .token.variable :focus:not (:focus-visible ) {
227+ outline : 2px solid transparent ;
228+ }
147229 </style >
0 commit comments