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
+
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