|
2 | 2 | defineProps<{ |
3 | 3 | html: string |
4 | 4 | }>() |
| 5 | +
|
| 6 | +const { copy } = useClipboard() |
| 7 | +
|
| 8 | +const handleCopy = async (e: MouseEvent) => { |
| 9 | + const target = (e.target as HTMLElement).closest('[data-copy]') |
| 10 | + if (!target) return |
| 11 | +
|
| 12 | + const wrapper = target.closest('.readme-code-block') |
| 13 | + if (!wrapper) return |
| 14 | +
|
| 15 | + const pre = wrapper.querySelector('pre') |
| 16 | + if (!pre?.textContent) return |
| 17 | +
|
| 18 | + await copy(pre.textContent) |
| 19 | +
|
| 20 | + const icon = target.querySelector('span') |
| 21 | + if (!icon) return |
| 22 | +
|
| 23 | + const originalIcon = 'i-carbon:copy' |
| 24 | + const successIcon = 'i-carbon:checkmark' |
| 25 | +
|
| 26 | + icon.classList.remove(originalIcon) |
| 27 | + icon.classList.add(successIcon) |
| 28 | +
|
| 29 | + setTimeout(() => { |
| 30 | + icon.classList.remove(successIcon) |
| 31 | + icon.classList.add(originalIcon) |
| 32 | + }, 2000) |
| 33 | +} |
5 | 34 | </script> |
6 | 35 |
|
7 | 36 | <template> |
8 | | - <article class="readme prose prose-invert max-w-[70ch] lg:max-w-none" v-html="html" /> |
| 37 | + <article |
| 38 | + class="readme prose prose-invert max-w-[70ch] lg:max-w-none" |
| 39 | + v-html="html" |
| 40 | + @click="handleCopy" |
| 41 | + /> |
9 | 42 | </template> |
10 | 43 |
|
11 | 44 | <style scoped> |
@@ -99,6 +132,90 @@ defineProps<{ |
99 | 132 | box-sizing: border-box; |
100 | 133 | } |
101 | 134 |
|
| 135 | +.readme :deep(.readme-code-block) { |
| 136 | + display: block; |
| 137 | + width: 100%; |
| 138 | + position: relative; |
| 139 | +} |
| 140 | +
|
| 141 | +.readme :deep(.readme-copy-button) { |
| 142 | + position: absolute; |
| 143 | + top: 0.4rem; |
| 144 | + inset-inline-end: 0.4rem; |
| 145 | + display: inline-flex; |
| 146 | + align-items: center; |
| 147 | + justify-content: center; |
| 148 | + padding: 0.25rem; |
| 149 | + border-radius: 6px; |
| 150 | + background: color-mix(in srgb, var(--bg-subtle) 80%, transparent); |
| 151 | + border: 1px solid var(--border); |
| 152 | + color: var(--fg-subtle); |
| 153 | + opacity: 0; |
| 154 | + transition: |
| 155 | + opacity 0.2s ease, |
| 156 | + color 0.2s ease, |
| 157 | + border-color 0.2s ease; |
| 158 | +} |
| 159 | +
|
| 160 | +.readme :deep(.readme-code-block:hover .readme-copy-button), |
| 161 | +.readme :deep(.readme-copy-button:focus-visible) { |
| 162 | + opacity: 1; |
| 163 | +} |
| 164 | +
|
| 165 | +.readme :deep(.readme-copy-button:hover) { |
| 166 | + color: var(--fg); |
| 167 | + border-color: var(--border-hover); |
| 168 | +} |
| 169 | +
|
| 170 | +.readme :deep(.readme-copy-button > span) { |
| 171 | + width: 1rem; |
| 172 | + height: 1rem; |
| 173 | + display: inline-block; |
| 174 | + pointer-events: none; |
| 175 | +} |
| 176 | +
|
| 177 | +.readme :deep(.readme-code-block) { |
| 178 | + display: block; |
| 179 | + width: 100%; |
| 180 | + position: relative; |
| 181 | +} |
| 182 | +
|
| 183 | +.readme :deep(.readme-copy-button) { |
| 184 | + position: absolute; |
| 185 | + top: 0.4rem; |
| 186 | + inset-inline-end: 0.4rem; |
| 187 | + display: inline-flex; |
| 188 | + align-items: center; |
| 189 | + justify-content: center; |
| 190 | + padding: 0.25rem; |
| 191 | + border-radius: 6px; |
| 192 | + background: color-mix(in srgb, var(--bg-subtle) 80%, transparent); |
| 193 | + border: 1px solid var(--border); |
| 194 | + color: var(--fg-subtle); |
| 195 | + opacity: 0; |
| 196 | + transition: |
| 197 | + opacity 0.2s ease, |
| 198 | + color 0.2s ease, |
| 199 | + border-color 0.2s ease; |
| 200 | +} |
| 201 | +
|
| 202 | +.readme :deep(.readme-code-block:hover .readme-copy-button), |
| 203 | +.readme :deep(.readme-copy-button:focus-visible) { |
| 204 | + opacity: 1; |
| 205 | +} |
| 206 | +
|
| 207 | +.readme :deep(.readme-copy-button:hover) { |
| 208 | + color: var(--fg); |
| 209 | + border-color: var(--border-hover); |
| 210 | +} |
| 211 | +
|
| 212 | +.readme :deep(.readme-copy-button > span) { |
| 213 | + width: 1.05rem; |
| 214 | + height: 1.05rem; |
| 215 | + display: inline-block; |
| 216 | + pointer-events: none; |
| 217 | +} |
| 218 | +
|
102 | 219 | .readme :deep(pre code), |
103 | 220 | .readme :deep(.shiki code) { |
104 | 221 | background: transparent !important; |
@@ -308,4 +425,17 @@ defineProps<{ |
308 | 425 | margin: 0 0.25rem 0.25rem 0; |
309 | 426 | border-radius: 4px; |
310 | 427 | } |
| 428 | +
|
| 429 | +/* Screen reader only text */ |
| 430 | +.readme :deep(.sr-only) { |
| 431 | + position: absolute; |
| 432 | + width: 1px; |
| 433 | + height: 1px; |
| 434 | + padding: 0; |
| 435 | + margin: -1px; |
| 436 | + overflow: hidden; |
| 437 | + clip: rect(0, 0, 0, 0); |
| 438 | + white-space: nowrap; |
| 439 | + border-width: 0; |
| 440 | +} |
311 | 441 | </style> |
0 commit comments