2
2
- Open Bank Project - API Explorer II
3
3
- Copyright (C) 2023-2024, TESOBE GmbH
4
4
-
5
- - This program is free software: you can redistribute it and/or modify
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
6
17
- it under the terms of the GNU Affero General Public License as published by
7
18
- the Free Software Foundation, either version 3 of the License, or
8
19
- (at your option) any later version.
27
38
28
39
<script setup lang="ts">
29
40
import { ref , onMounted , onUpdated , nextTick } from ' vue'
41
+ import { DocumentCopy , Check } from ' @element-plus/icons-vue'
30
42
31
43
// Declare global hljs
32
44
declare global {
@@ -40,13 +52,16 @@ declare global {
40
52
interface Props {
41
53
code: any
42
54
language? : string
55
+ copyable? : boolean
43
56
}
44
57
45
58
const props = withDefaults (defineProps <Props >(), {
46
- language: ' json'
59
+ language: ' json' ,
60
+ copyable: false
47
61
})
48
62
49
63
const codeBlockRef = ref <HTMLElement >()
64
+ const copied = ref (false )
50
65
51
66
const highlight = async () => {
52
67
await nextTick ()
@@ -58,6 +73,18 @@ const highlight = async () => {
58
73
}
59
74
}
60
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
+
61
88
onMounted (() => {
62
89
highlight ()
63
90
})
@@ -73,7 +100,29 @@ const formattedCode = typeof props.code === 'string'
73
100
74
101
<template >
75
102
<div ref =" codeBlockRef" class =" code-block" >
76
- <pre ><code :class =" language" >{{ formattedCode }}</code ></pre >
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 >
77
126
</div >
78
127
</template >
79
128
@@ -84,9 +133,52 @@ const formattedCode = typeof props.code === 'string'
84
133
overflow : hidden ;
85
134
background : #1e1e1e ;
86
135
border : 1px solid #333 ;
136
+ position : relative ;
87
137
}
88
138
89
- .code-block pre {
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 {
90
182
margin : 0 ;
91
183
padding : 1.5rem ;
92
184
background : #1e1e1e ;
@@ -99,7 +191,7 @@ const formattedCode = typeof props.code === 'string'
99
191
word-wrap : break-word ;
100
192
}
101
193
102
- .code-block code {
194
+ .code-container code {
103
195
background : transparent ;
104
196
padding : 0 ;
105
197
border-radius : 0 ;
@@ -108,21 +200,38 @@ const formattedCode = typeof props.code === 'string'
108
200
}
109
201
110
202
/* Custom scrollbar for code blocks */
111
- .code-block pre ::-webkit-scrollbar {
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 {
112
221
height : 8px ;
113
222
width : 8px ;
114
223
}
115
224
116
- .code-block pre ::-webkit-scrollbar-track {
225
+ .code-container pre ::-webkit-scrollbar-track {
117
226
background : #2d2d2d ;
118
227
}
119
228
120
- .code-block pre ::-webkit-scrollbar-thumb {
229
+ .code-container pre ::-webkit-scrollbar-thumb {
121
230
background : #555 ;
122
231
border-radius : 4px ;
123
232
}
124
233
125
- .code-block pre ::-webkit-scrollbar-thumb :hover {
234
+ .code-container pre ::-webkit-scrollbar-thumb :hover {
126
235
background : #777 ;
127
236
}
128
237
0 commit comments