1+ <!--
2+ - Open Bank Project - API Explorer II
3+ - Copyright (C) 2023-2024, TESOBE GmbH
4+ -
5+ <div v-if="copyable" class="code-block-header">
6+ <button
7+ @click="copyToClipboard"
8+ class="copy-button"
9+ :class="{ 'copied': copied }"
10+ >
11+ <el-icon v-if="!copied" class="icon"><DocumentCopy /></el-icon>
12+ <el-icon v-else class="icon"><Check /></el-icon>
13+ <span v-if="!copied">Copy</span>
14+ <span v-else>Copied!</span>
15+ </button>
16+ </div>rogram is free software: you can redistribute it and/or modify
17+ - it under the terms of the GNU Affero General Public License as published by
18+ - the Free Software Foundation, either version 3 of the License, or
19+ - (at your option) any later version.
20+ -
21+ - This program is distributed in the hope that it will be useful,
22+ - but WITHOUT ANY WARRANTY; without even the implied warranty of
23+ - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24+ - GNU Affero General Public License for more details.
25+ -
26+ - You should have received a copy of the GNU Affero General Public License
27+ - along with this program. If not, see <http://www.gnu.org/licenses/>.
28+ -
29+ - Email: contact@tesobe.com
30+ - TESOBE GmbH
31+ - Osloerstrasse 16/17
32+ - Berlin 13359, Germany
33+ -
34+ - This product includes software developed at
35+ - TESOBE (http://www.tesobe.com/)
36+ -
37+ -->
38+
39+ <script setup lang="ts">
40+ import { ref , onMounted , onUpdated , nextTick } from ' vue'
41+ import { DocumentCopy , Check } from ' @element-plus/icons-vue'
42+
43+ // Declare global hljs
44+ declare global {
45+ interface Window {
46+ hljs: {
47+ highlightElement: (element : HTMLElement ) => void
48+ }
49+ }
50+ }
51+
52+ interface Props {
53+ code: any
54+ language? : string
55+ copyable? : boolean
56+ }
57+
58+ const props = withDefaults (defineProps <Props >(), {
59+ language: ' json' ,
60+ copyable: false
61+ })
62+
63+ const codeBlockRef = ref <HTMLElement >()
64+ const copied = ref (false )
65+
66+ const highlight = async () => {
67+ await nextTick ()
68+ if (codeBlockRef .value && window .hljs ) {
69+ const codeElements = codeBlockRef .value .querySelectorAll (' pre code' )
70+ codeElements .forEach ((block ) => {
71+ window .hljs .highlightElement (block as HTMLElement )
72+ })
73+ }
74+ }
75+
76+ const copyToClipboard = async () => {
77+ try {
78+ await navigator .clipboard .writeText (formattedCode )
79+ copied .value = true
80+ setTimeout (() => {
81+ copied .value = false
82+ }, 2000 )
83+ } catch (err ) {
84+ console .error (' Failed to copy: ' , err )
85+ }
86+ }
87+
88+ onMounted (() => {
89+ highlight ()
90+ })
91+
92+ onUpdated (() => {
93+ highlight ()
94+ })
95+
96+ const formattedCode = typeof props .code === ' string'
97+ ? props .code
98+ : JSON .stringify (props .code , null , 2 )
99+ </script >
100+
101+ <template >
102+ <div ref =" codeBlockRef" class =" code-block" >
103+ <div v-if =" copyable" class =" code-block-header" >
104+ <button
105+ @click =" copyToClipboard"
106+ class =" copy-button"
107+ :class =" { 'copied': copied }"
108+ >
109+ <span v-if =" !copied" >
110+ <el-icon :size =" 10" >
111+ <DocumentCopy />
112+ </el-icon >
113+ Copy
114+ </span >
115+ <span v-else >
116+ <el-icon :size =" 10" >
117+ <Check />
118+ </el-icon >
119+ Copied!
120+ </span >
121+ </button >
122+ </div >
123+ <div class =" code-container" >
124+ <pre ><code :class =" language" >{{ formattedCode }}</code ></pre >
125+ </div >
126+ </div >
127+ </template >
128+
129+ <style scoped>
130+ .code-block {
131+ margin : 1rem 0 ;
132+ border-radius : 8px ;
133+ overflow : hidden ;
134+ background : #1e1e1e ;
135+ border : 1px solid #333 ;
136+ position : relative ;
137+ }
138+
139+ .code-block-header {
140+ background : #2d2d2d ;
141+ padding : 0.5rem 1rem ;
142+ border-bottom : 1px solid #333 ;
143+ display : flex ;
144+ justify-content : flex-end ;
145+ }
146+
147+ .copy-button {
148+ background : #444 ;
149+ border : 1px solid #666 ;
150+ color : #ddd ;
151+ padding : 0.25rem 0.75rem ;
152+ border-radius : 4px ;
153+ cursor : pointer ;
154+ font-size : 12px ;
155+ transition : all 0.2s ease ;
156+ display : flex ;
157+ align-items : center ;
158+ gap : 0.25rem ;
159+ }
160+
161+ .copy-button .icon {
162+ font-size : 14px ;
163+ }
164+
165+ .copy-button :hover {
166+ background : #555 ;
167+ border-color : #777 ;
168+ }
169+
170+ .copy-button.copied {
171+ background : #4caf50 ;
172+ border-color : #4caf50 ;
173+ color : white ;
174+ }
175+
176+ .code-container {
177+ max-height : 500px ;
178+ overflow-y : auto ;
179+ }
180+
181+ .code-container pre {
182+ margin : 0 ;
183+ padding : 1.5rem ;
184+ background : #1e1e1e ;
185+ color : #ddd ;
186+ font-family : ' Fira Code' , ' Courier New' , monospace ;
187+ font-size : 14px ;
188+ line-height : 1.5 ;
189+ overflow-x : auto ;
190+ white-space : pre-wrap ;
191+ word-wrap : break-word ;
192+ }
193+
194+ .code-container code {
195+ background : transparent ;
196+ padding : 0 ;
197+ border-radius : 0 ;
198+ font-family : inherit ;
199+ font-size : inherit ;
200+ }
201+
202+ /* Custom scrollbar for code blocks */
203+ .code-container ::-webkit-scrollbar {
204+ width : 8px ;
205+ }
206+
207+ .code-container ::-webkit-scrollbar-track {
208+ background : #2d2d2d ;
209+ }
210+
211+ .code-container ::-webkit-scrollbar-thumb {
212+ background : #555 ;
213+ border-radius : 4px ;
214+ }
215+
216+ .code-container ::-webkit-scrollbar-thumb :hover {
217+ background : #777 ;
218+ }
219+
220+ .code-container pre ::-webkit-scrollbar {
221+ height : 8px ;
222+ width : 8px ;
223+ }
224+
225+ .code-container pre ::-webkit-scrollbar-track {
226+ background : #2d2d2d ;
227+ }
228+
229+ .code-container pre ::-webkit-scrollbar-thumb {
230+ background : #555 ;
231+ border-radius : 4px ;
232+ }
233+
234+ .code-container pre ::-webkit-scrollbar-thumb :hover {
235+ background : #777 ;
236+ }
237+
238+ /* Syntax highlighting enhancements */
239+ .code-block :deep(.hljs-string ) {
240+ color : #98c379 ;
241+ }
242+
243+ .code-block :deep(.hljs-number ) {
244+ color : #d19a66 ;
245+ }
246+
247+ .code-block :deep(.hljs-literal ) {
248+ color : #56b6c2 ;
249+ }
250+
251+ .code-block :deep(.hljs-attr ) {
252+ color : #e06c75 ;
253+ }
254+
255+ .code-block :deep(.hljs-punctuation ) {
256+ color : #abb2bf ;
257+ }
258+ </style >
0 commit comments