Skip to content

Commit f693676

Browse files
authored
Add convert single line button to code block with sql language (#1033)
1 parent 4dffd96 commit f693676

File tree

2 files changed

+138
-0
lines changed

2 files changed

+138
-0
lines changed

src/.vuepress/client.ts

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import { defineDocSearchConfig } from '@vuepress/plugin-docsearch/client';
2020
import { computed } from 'vue';
21+
import { onMounted, nextTick } from 'vue';
2122
import {
2223
defineClientConfig,
2324
usePageData,
@@ -52,6 +53,93 @@ export default defineClientConfig({
5253
lastTo = to.fullPath;
5354
});
5455

56+
function addConvertSingleLineButton() {
57+
if (typeof document === 'undefined') return;
58+
59+
const blocks = document.querySelectorAll<HTMLElement>('div[class*="language-sql"] pre');
60+
const copyIcon = `<svg t="1773227012571" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7157" width="18px" height="18px"><path d="M860.253405 512.000461a431.238022 431.238022 0 0 0-139.308875-316.862415A40.958141 40.958141 0 1 0 665.702238 255.448908a348.144194 348.144194 0 0 1-202.691598 603.108619l30.718605-30.718605a41.019578 41.019578 0 0 0-57.904571-58.109362l-81.301909 81.301909a56.317443 56.317443 0 0 0 0 79.663583l81.301909 81.301909a40.97862 40.97862 0 0 0 57.955769-57.955769l-14.847326-14.796128A430.674847 430.674847 0 0 0 860.253405 512.000461zM350.01737 254.271362a40.958141 40.958141 0 0 0 57.955768 0l81.301909-81.301909a56.317443 56.317443 0 0 0 0-79.663583L407.973138 12.003961a40.97862 40.97862 0 0 0-57.955768 57.955768l16.74164 16.690443a430.060475 430.060475 0 0 0-227.31768 742.366296A40.958141 40.958141 0 0 0 194.683622 768.552013 348.144194 348.144194 0 0 1 378.688068 167.696092l-28.670698 28.619501a40.958141 40.958141 0 0 0 0 57.955769z" fill="#ffffff" p-id="7158"></path></svg>`;
61+
const tipContent = pageData.value.lang === 'zh-CN' ? '已转换为单行' : 'Converted as single line';
62+
const hoverTipContent = pageData.value.lang === 'zh-CN' ? '转换为单行' : 'Convert as single line';
63+
const copyTipContent = pageData.value.lang === 'zh-CN' ? '复制内容' : 'Copy content';
64+
65+
blocks.forEach((pre) => {
66+
if (pre.querySelector('.copy-one-line-btn')) return;
67+
68+
const parentEl = pre.parentElement;
69+
if (!parentEl) return;
70+
const lineNumbers = parentEl.querySelector('div[class="line-numbers"]');
71+
if (!lineNumbers) return;
72+
const copyBtn = parentEl.querySelector<HTMLElement>('button[class*="vp-copy-code-button"]');
73+
if (!copyBtn) return;
74+
copyBtn.title = copyTipContent;
75+
76+
const code = pre.querySelector('code');
77+
if (!code) return;
78+
79+
const copiedTooltip = document.createElement('div');
80+
copiedTooltip.className = 'copy-one-line-tooltip';
81+
pre.appendChild(copiedTooltip);
82+
83+
const btn = document.createElement('button');
84+
btn.innerHTML = copyIcon;
85+
btn.className = 'copy-one-line-btn';
86+
btn.title = hoverTipContent;
87+
88+
btn.onclick = () => {
89+
const text = code.innerText;
90+
const lines = text.split('\n');
91+
92+
const single = lines
93+
.map((line, i) => {
94+
const trimmed = line.trim();
95+
if (!trimmed || trimmed === '') return '';
96+
console.log('line', trimmed);
97+
if (i === lines.length - 1) return trimmed;
98+
if (trimmed.endsWith(';')) {
99+
return trimmed + '\n';
100+
}
101+
if (trimmed.endsWith('\\')) {
102+
return trimmed.slice(0, -1) + ' ';
103+
}
104+
return trimmed + ' ';
105+
})
106+
.join('');
107+
108+
const convertedSpan = single.split('\n').map((line) => `<span class="line"><span>${line}\n</span></span>`).join('');
109+
const counter = single.split('\n').length;
110+
code.innerHTML = convertedSpan;
111+
if (lineNumbers) {
112+
const childCount = lineNumbers.children.length;
113+
for (let i = counter; i < childCount; i++) {
114+
const child = lineNumbers.children[0];
115+
lineNumbers.removeChild(child);
116+
}
117+
}
118+
119+
btn.style.opacity = '80';
120+
btn.style.backgroundColor = 'rgb(47, 53, 66)';
121+
copiedTooltip.style.opacity = '100';
122+
copiedTooltip.innerText = tipContent;
123+
124+
setTimeout(() => {
125+
btn.style.backgroundColor = '';
126+
copiedTooltip.innerText = '';
127+
btn.style.visibility = 'hidden';
128+
copiedTooltip.style.visibility = 'hidden';
129+
}, 1500);
130+
};
131+
pre.appendChild(btn);
132+
});
133+
}
134+
135+
onMounted(() => {
136+
nextTick(() => addConvertSingleLineButton());
137+
});
138+
139+
router.afterEach(() => {
140+
nextTick(() => addConvertSingleLineButton());
141+
});
142+
55143
const docSearchConfig = computed(() => ({
56144
appId: 'JLT9R2YGAE',
57145
apiKey: 'f1f30c0df04d74534e066d07786bce05',

src/.vuepress/styles/index.scss

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,56 @@
1616
* limitations under the License.
1717
*/
1818

19+
.copy-one-line-tooltip {
20+
position: absolute;
21+
right: 95px;
22+
top: 8px;
23+
padding: 10px 10px 7px;
24+
background-color: rgb(47, 53, 66);
25+
border-radius: 8px;
26+
opacity: 0;
27+
font-family: var(--vp-font);
28+
font-size: 13px;
29+
}
30+
31+
.copy-one-line-btn {
32+
position: absolute;
33+
right: 50px;
34+
top: 8px;
35+
padding: 10px 10px 7px;
36+
background-color: transparent;
37+
border: none;
38+
border-radius: 8px;
39+
cursor: pointer;
40+
opacity: 0;
41+
transition: opacity 0.3s ease;
42+
}
43+
44+
.copy-one-line-btn:hover::before {
45+
content: attr(data-tooltip);
46+
position: absolute;
47+
bottom: 100%;
48+
left: 50%;
49+
transform: translateX(-50%);
50+
padding: 4px 8px;
51+
background: rgba(0, 0, 0, 0.8);
52+
color: white;
53+
font-size: 12px;
54+
border-radius: 4px;
55+
white-space: nowrap;
56+
margin-bottom: 6px;
57+
pointer-events: none;
58+
z-index: 1000;
59+
}
60+
61+
.language-sql pre:hover .copy-one-line-btn {
62+
opacity: 0.8;
63+
}
64+
65+
.copy-one-line-btn:hover {
66+
background-color: rgb(47, 53, 66);
67+
}
68+
1969
#main-description {
2070
max-width: 55rem;
2171
}

0 commit comments

Comments
 (0)