11<template >
2- <div
3- class =" v-code-block--container v-code-block-mb-5"
4- :class =" codeBlockClasses"
5- >
6- <div class =" v-code-block--container-header" :style =" headerStyles" >
7- <div
8- class =" v-code-block--container-label v-code-block-pb-1"
9- :class =" labelClasses"
10- >
2+ <div class =" v-code-block v-code-block--mb-5" :class =" codeBlockClasses" >
3+ <div
4+ v-if =" label || tabs || slots.label || slots.tabs"
5+ class =" v-code-block--header"
6+ :style =" headerStyles"
7+ >
8+ <div class =" v-code-block--label v-code-block--pb-1" :class =" labelClasses" >
119 <template v-if =" slots .label " >
1210 <slot name =" label" />
1311 </template >
1614 </template >
1715 </div >
1816
19- <div class =" v-code-block--container- tabs" :style =" tabGroupStyle" >
17+ <div class =" v-code-block--tabs" :style =" tabGroupStyle" >
2018 <template v-if =" slots .tabs " >
2119 <slot name =" tabs" />
2220 </template >
2321 <template v-else >
2422 <!-- ======================================== Copy Code Tab/Button -->
2523 <div
26- v-if =" showCopyTab && showTabs "
27- class =" v-code-block--container- tab"
24+ v-if =" copyTab && tabs "
25+ class =" v-code-block--tab"
2826 :class =" tabClasses"
2927 @click =" copyCode"
3028 >
31- <div class =" v-code-block--container- button-copy" >
32- <fa-icon
33- v-if =" showCopyIcons "
34- class =" fa-fw v-code-block-me-1 v-code-block--container -button-copy-icon"
29+ <div class =" v-code-block--button-copy" >
30+ <StatusIcons
31+ v-if =" copyIcons "
32+ class =" v-code-block--button-copy-icon"
3533 :class =" iconClasses"
36- :icon =" buttonIconValue "
34+ :icon =" copyStatus "
3735 />
38- {{ buttonTextValue }}
36+ {{ copyTextValue }}
3937 </div >
4038 </div >
4139
4240 <!-- ======================================== Run Tab/Button -->
4341 <div
44- v-if =" showRunTab && showTabs && !isMobile"
45- class =" v-code-block--container- tab"
42+ v-if =" runTab && tabs && !isMobile"
43+ class =" v-code-block--tab"
4644 :class =" tabClasses"
4745 @click =" runCode"
4846 >
49- <div class =" v-code-block--container- button-run" >Run</div >
47+ <div class =" v-code-block--button-run" >Run</div >
5048 </div >
5149 </template >
5250 </div >
5351 </div >
54- <div class =" v-code-block--container-code" >
52+ <div class =" v-code-block--code" >
53+ <div
54+ class =" v-code-block--code-copy-button"
55+ :class =" copyButtonClasses"
56+ @click =" copyCode"
57+ >
58+ <template v-if =" slots .copyButton " >
59+ <slot name =" copyButton" />
60+ </template >
61+ <template v-else >
62+ <StatusIcons
63+ v-if =" copyButton"
64+ class =" v-code-block--button-copy-icon"
65+ :class =" iconClasses"
66+ :icon =" copyStatus"
67+ />
68+ </template >
69+ </div >
70+
5571 <pre :class =" `language-${props.lang}`" :style =" preTagStyles" >
56- <code :class =" `language-${props.lang} ${browserWindow ? 'v-code-block--container- code-browser' : ''}`" :style =" codeTagStyles" v-html =" renderCode" ></code >
72+ <code :class =" `language-${props.lang} ${browserWindow ? 'v-code-block--code-browser' : ''}`" :style =" codeTagStyles" v-html =" renderCode" ></code >
5773 </pre >
5874 </div >
5975 </div >
@@ -70,9 +86,11 @@ import {
7086 watch ,
7187} from ' vue' ;
7288import Prism from ' prismjs' ;
73- // import PrismComponents from 'prismjs/components';
7489import UAParser from ' ua-parser-js' ;
7590
91+ import StatusIcons from ' @/plugin/StatusIcons.vue' ;
92+ import neonBunnyCarrotTheme from ' @/plugin/theme/neon-bunny-carrot.css?inline' ;
93+ import neonBunnyTheme from ' @/plugin/theme/neon-bunny.css?inline' ;
7694import prismTheme from ' prismjs/themes/prism.css?inline' ;
7795import prismThemeCoy from ' prismjs/themes/prism-coy.css?inline' ;
7896import prismThemeDark from ' prismjs/themes/prism-dark.css?inline' ;
@@ -81,15 +99,13 @@ import prismThemeOkaidia from 'prismjs/themes/prism-okaidia.css?inline';
8199import prismThemeSolarizedlight from ' prismjs/themes/prism-solarizedlight.css?inline' ;
82100import prismThemeTomorrow from ' prismjs/themes/prism-tomorrow.css?inline' ;
83101import prismThemeTwilight from ' prismjs/themes/prism-twilight.css?inline' ;
84- import neonBunnyTheme from ' @/plugin/theme/neon-bunny.css?inline' ;
85- import neonBunnyCarrotTheme from ' @/plugin/theme/neon-bunny-carrot.css?inline' ;
86102
87103// ! Remove this later as it should be loaded by the user ! //
88104import prismThemeNightOwl from ' prism-themes/themes/prism-night-owl.css?inline' ;
89105
90106
91107// -------------------------------------------------- Emits & Slots & Injects //
92- const emit = defineEmits ([' copied ' , ' run ' ]);
108+ const emit = defineEmits ([' run ' , ' update:copy-status ' ]);
93109const slots = useSlots ();
94110const codeBlockGlobalOptions = inject (' codeBlockGlobalOptions' );
95111
@@ -110,20 +126,35 @@ const props = defineProps({
110126 required: false ,
111127 default: ' 0.5rem' ,
112128 },
113- copyIcon: {
129+ copyButton: {
130+ type: Boolean ,
131+ required: false ,
132+ default: true ,
133+ },
134+ copyIcons: {
135+ type: Boolean ,
136+ required: false ,
137+ default: true ,
138+ },
139+ copyTab: {
140+ type: Boolean ,
141+ required: false ,
142+ default: true ,
143+ },
144+ copyFailedText: {
114145 type: String ,
115146 required: false ,
116- default: ' fa-solid fa-copy ' ,
147+ default: ' Copy failed! ' ,
117148 },
118149 copyText: {
119150 type: String ,
120151 required: false ,
121152 default: ' Copy Code' ,
122153 },
123- failedIcon : {
154+ copySuccessText : {
124155 type: String ,
125156 required: false ,
126- default: ' fa-solid fa-xmark ' ,
157+ default: ' Copied! ' ,
127158 },
128159 floatingTabs: {
129160 type: Boolean ,
@@ -138,7 +169,7 @@ const props = defineProps({
138169 indent: {
139170 type: Number ,
140171 required: false ,
141- default: 4 ,
172+ default: 2 ,
142173 },
143174 label: {
144175 type: String ,
@@ -155,93 +186,91 @@ const props = defineProps({
155186 required: false ,
156187 default: ' auto' ,
157188 },
158- showCopyIcons: {
159- type: Boolean ,
160- required: false ,
161- default: true ,
162- },
163- showRunTab: {
189+ persistentCopyButton: {
164190 type: Boolean ,
165191 required: false ,
166192 default: false ,
167193 },
168- showCopyTab: {
169- type: Boolean ,
170- required: false ,
171- default: true ,
172- },
173- showTabs: {
194+ runTab: {
174195 type: Boolean ,
175196 required: false ,
176- default: true ,
177- },
178- successIcon: {
179- type: String ,
180- required: false ,
181- default: ' fa-solid fa-check' ,
197+ default: false ,
182198 },
183199 tabGap: {
184200 type: String ,
185201 required: false ,
186202 default: ' 0.25rem' ,
187203 },
204+ tabs: {
205+ type: Boolean ,
206+ required: false ,
207+ default: false ,
208+ },
188209 theme: {
189- type: String , Boolean ,
210+ type: [ String , Boolean ] ,
190211 required: false ,
191- default: ' ' ,
212+ default: ' neon-bunny ' ,
192213 }
193214});
194215
195216
196217// -------------------------------------------------- Data //
197- const buttonIconValue: string = ref (' ' );
198- const buttonTextValue: string = ref (' ' );
218+ const copyTextValue: string = ref (' ' );
199219const convertedCode: string = ref (' ' );
200220const copying: boolean = ref (false );
201221const copyStatus: string = ref (' copy' );
202- const iconClass: string = ref (' fa-solid fa-copy' );
203222const isMobile: boolean = ref (false );
204223const stylesheetId = ' v-code-block--theme' ;
205224const useTheme = ref (' ' );
206225
207226
208227// -------------------------------------------------- Computed //
209228const codeBlockClasses = computed <string >(() => {
210- return isMobile .value ? ' v-code-block--container- mobile' : ' ' ;
229+ return isMobile .value ? ' v-code-block--mobile' : ' ' ;
211230});
212231
213232const codeTagStyles = computed <object >(() => {
214233 const width = useTheme .value === ' coy' ? ' 100%' : ' ' ;
215234 return { width };
216235});
217236
237+ const copyButtonClasses = computed <string >(() => {
238+ return {
239+ ' v-code-block--code-copy-button' : true ,
240+ ' v-code-block--code-copy-button-mobile' : isMobile .value ,
241+ [` v-code-block--code-copy-button-persist ` ]: props .persistentCopyButton ,
242+ [` v-code-block--code-copy-button-status-${copyStatus .value } ` ]: true ,
243+ };
244+ });
245+
218246const headerStyles = computed <object >(() => {
219247 return {
220248 bottom: props .floatingTabs ? ' 1px' : ' 0' ,
221- gap: props .tabGap ,
249+ gap: convertToUnit ( props .tabGap ) ,
222250 };
223251});
224252
225253const iconClasses = computed <object >(() => {
226254 const theme = useTheme .value === ' ' || useTheme .value === ' prism' ? ' default' : useTheme .value ;
227255
228256 const classes = {
229- [` v-code-block--container-tab-${theme }-icon ` ]: true ,
230- [` v-code-block--container-button-copy-icon-status-${copyStatus .value } ` ]: true ,
231- [` v-code-block--container-tab-${theme }-icon-status-${copyStatus .value } ` ]: true ,
257+ ' v-code-block--me-1' : true ,
258+ [` v-code-block--tab-${theme }-icon ` ]: true ,
259+ [` v-code-block--button-copy-icon-status-${copyStatus .value } ` ]: true ,
260+ [` v-code-block--tab-${theme }-icon-status-${copyStatus .value } ` ]: true ,
232261 };
233262 return classes ;
234263});
235264
236265const labelClasses = computed <string >(() => {
237- return isMobile .value ? ' v-code-block--container- label-mobile' : ' ' ;
266+ return isMobile .value ? ' v-code-block--label-mobile' : ' ' ;
238267});
239268
240269const preTagStyles = computed <object >(() => {
241270 const radius = props .codeBlockRadius ;
242271 let borderRadius = ` ${radius } 0 ${radius } ${radius } ` ;
243272
244- if (! props .showTabs || (! props .showCopyTab && ! props .showRunTab )) {
273+ if (! props .tabs || (! props .copyTab && ! props .runTab )) {
245274 borderRadius = radius ;
246275 }
247276
@@ -268,14 +297,14 @@ const renderCode = computed<unknown>(() => {
268297const tabClasses = computed <object >(() => {
269298 const theme = useTheme .value === ' ' || useTheme .value === ' prism' ? ' default' : useTheme .value ;
270299 const classes = {
271- [` v-code-block--container- tab-${theme } ` ]: true ,
300+ [` v-code-block--tab-${theme } ` ]: true ,
272301 };
273302 return classes ;
274303});
275304
276305const tabGroupStyle = computed <object >(() => {
277306 return {
278- gap: props .tabGap ,
307+ gap: convertToUnit ( props .tabGap ) ,
279308 };
280309});
281310
@@ -288,17 +317,14 @@ watch(props, () => {
288317 }
289318
290319 if (props .copyText ) {
291- buttonTextValue .value = props .copyText ;
320+ copyTextValue .value = props .copyText ;
292321 }
293322});
294323
295- console .log ({ Prism });
296-
297324
298325// -------------------------------------------------- Mounts //
299326onBeforeMount (() => {
300- buttonTextValue .value = props .copyText ;
301- buttonIconValue .value = props .copyIcon ;
327+ copyTextValue .value = props .copyText ;
302328});
303329
304330onMounted (() => {
@@ -310,9 +336,9 @@ onMounted(() => {
310336
311337// -------------------------------------------------- Methods //
312338function convertCode(): void {
313- if (props .lang === ' json' ) {
314339
315- convertedCode .value = JSON .stringify (props .code , null , props .indent );
340+ if (props .lang === ' json' ) {
341+ convertedCode .value = JSON .stringify (JSON .parse (props .code ), null , props .indent );
316342 return ;
317343 }
318344
@@ -339,23 +365,20 @@ function copyCode(): void {
339365 copying .value = true ;
340366
341367 navigator .clipboard .writeText (convertedCode .value ).then (() => {
342- buttonIconValue .value = props .successIcon ;
343- buttonTextValue .value = ' Copied!' ;
344- iconClass .value = ' fa-solid fa-check' ;
368+ copyTextValue .value = props .copySuccessText ;
345369 copyStatus .value = ' success' ;
370+ emit (' update:copy-status' , copyStatus .value );
346371 }, (err ) => {
347- buttonIconValue .value = props .failedIcon ;
348- buttonTextValue .value = ' Copy failed!' ;
349- iconClass .value = ' fa-solid fa-xmark' ;
372+ copyTextValue .value = props .copyFailedText ;
350373 copyStatus .value = ' failed' ;
374+ emit (' update:copy-status' , copyStatus .value );
351375 console .error (' Copy to clipboard failed: ' , err );
352376 });
353377
354378 setTimeout (() => {
355- buttonIconValue .value = props .copyIcon ;
356- buttonTextValue .value = props .copyText ;
379+ copyTextValue .value = props .copyText ;
357380 copyStatus .value = ' copy' ;
358- iconClass . value = ' fa-solid fa- copy' ;
381+ emit ( ' update: copy-status ' , copyStatus . value ) ;
359382 copying .value = false ;
360383 }, 3000 );
361384}
@@ -435,8 +458,10 @@ function runCode(): void {
435458
436459
437460<style lang="scss">
461+ @import ' ./styles/utilities' ;
462+
438463.v-code-block {
439- & --container {
464+ & - {
440465 & --label {
441466 & -mobile {
442467 input ,
@@ -451,5 +476,6 @@ function runCode(): void {
451476 </style >
452477
453478<style lang="scss" scoped>
454- @import ' ../style ' ;
479+ @import ' ./styles/main ' ;
455480 </style >
481+
0 commit comments