PluginProbe
Code Block Pro – Beautiful Syntax Highlighting / 1.20.0
Code Block Pro – Beautiful Syntax Highlighting v1.20.0
1.27.1 1.27.2 1.27.3 1.27.4 1.27.5 1.27.6 1.27.7 1.28.0 1.3.0 1.4.0 1.5.0 1.5.1 1.5.2 1.6.0 1.7.0 1.8.0 1.9.0 1.9.1 1.9.2 1.9.3 trunk 1.1.0 1.10.0 1.11.0 1.11.1 All 63 releases
code-block-pro / src / front / front.js

front.js in Code Block Pro – Beautiful Syntax Highlighting 1.20.0, at src/front/front.js

183 lines 6.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import copy from 'copy-to-clipboard';
2
3 const containerClass = '.wp-block-kevinbatdorf-code-block-pro';
4
5 const handleCopyButton = () => {
6 const buttons = Array.from(
7 document.querySelectorAll(
8 '.code-block-pro-copy-button:not(.cbp-cb-loaded)',
9 ),
10 );
11 buttons.forEach((button) => {
12 button.classList.add('cbp-cb-loaded');
13 // Setting it to block here lets users deactivate the plugin safely
14 button.style.display = 'block';
15 const handler = (event) => {
16 const { type, key, target } = event;
17 // if keydown event, make sure it's enter or space
18 if (type === 'keydown' && !['Enter', ' '].includes(key)) return;
19 event.preventDefault();
20 const b = target?.closest('span');
21 const code = b?.dataset?.encoded
22 ? decodeURIComponent(decodeURIComponent(b?.dataset?.code))
23 : b?.dataset?.code;
24 copy(code ?? '', {
25 format: 'text/plain',
26 });
27 b.classList.add('cbp-copying');
28 setTimeout(() => {
29 b.classList.remove('cbp-copying');
30 }, 2_000);
31 };
32 ['click', 'keydown'].forEach((evt) =>
33 button.addEventListener(evt, handler),
34 );
35 });
36 };
37
38 const handleHighlighter = () => {
39 const codeBlocks = Array.from(
40 document.querySelectorAll(`${containerClass}:not(.cbp-hl-loaded)`),
41 );
42
43 codeBlocks.forEach((codeBlock) => {
44 codeBlock.classList.add('cbp-hl-loaded');
45 // Search for highlights
46 const highlighters = new Set(
47 codeBlock.querySelectorAll('.cbp-line-highlight'),
48 );
49 // If the codeblock has .cbp-highlight-hover, then get all lines
50 if (codeBlock.classList.contains('cbp-highlight-hover')) {
51 codeBlock
52 .querySelectorAll('span.line')
53 .forEach((line) => highlighters.add(line));
54 }
55
56 if (!highlighters.size) return;
57
58 // If the code block expands, we need to recalculate the width
59 new ResizeObserver(() => {
60 // find the longest line
61 codeBlock.style.setProperty('--cbp-block-width', 'unset');
62 const longestLine = Array.from(highlighters).reduce((a, b) =>
63 a.offsetWidth > b.offsetWidth ? a : b,
64 );
65 codeBlock.style.setProperty(
66 '--cbp-block-width',
67 longestLine.offsetWidth + 'px',
68 );
69 }).observe(codeBlock);
70
71 // Add the highlighter if not already there
72 highlighters.forEach((highlighter) => {
73 if (highlighter.querySelector('.cbp-line-highlighter')) return;
74 highlighter.insertAdjacentHTML(
75 'beforeend',
76 '<span aria-hidden="true" class="cbp-line-highlighter"></span>',
77 );
78 });
79 });
80 };
81
82 const handleFontLoading = () => {
83 if (!window.codeBlockPro?.pluginUrl) return;
84 const elements = Array.from(
85 document.querySelectorAll(
86 '[data-code-block-pro-font-family]:not(.cbp-ff-loaded)',
87 ) || [],
88 );
89 elements.forEach((e) => e.classList.add('cbp-ff-loaded'));
90 const fontsToLoad = new Set(
91 elements.map((f) => f.dataset.codeBlockProFontFamily).filter(Boolean),
92 );
93 [...fontsToLoad].forEach(async (fontName) => {
94 const [name, ext] = fontName.split('.');
95 const url = `url(${window.codeBlockPro.pluginUrl}/build/fonts/${name}.${
96 ext || 'woff2'
97 })`;
98 const font = new FontFace(name, url);
99 await font.load().catch((e) => console.error(e));
100 document.fonts.add(font);
101 });
102 };
103
104 const handleSeeMore = () => {
105 const seeMoreLines = Array.from(
106 document.querySelectorAll(
107 `${containerClass}:not(.cbp-see-more-loaded) .cbp-see-more-line`,
108 ),
109 );
110 seeMoreLines.forEach((line) => {
111 const currentContainer = line.closest(containerClass);
112 currentContainer.classList.add('cbp-see-more-loaded');
113 const pre = line.closest('pre');
114 const initialHeight = pre.offsetHeight;
115 let animationSpeed = 0;
116
117 if (line.classList.contains('cbp-see-more-transition')) {
118 const lineCount = pre.querySelectorAll('code > *').length;
119 const linesBeforeCurrent = Array.from(
120 line.closest('code').children,
121 ).filter((l) => l.offsetTop < line.offsetTop)?.length;
122 animationSpeed = 0.5 + (lineCount - linesBeforeCurrent) * 0.01;
123 pre.style.transition = `max-height ${animationSpeed}s ease-out`;
124 }
125
126 // if the first child it a span then get the height of that span
127 const headerHeight =
128 currentContainer.children[0].tagName === 'SPAN'
129 ? currentContainer.children[0].offsetHeight
130 : 0;
131 const lineHeight = parseFloat(window.getComputedStyle(line).lineHeight);
132 pre.style.maxHeight = `${line.offsetTop + lineHeight - headerHeight}px`;
133
134 const buttonContainer = line
135 .closest(containerClass)
136 .querySelector('.cbp-see-more-container');
137 if (!buttonContainer) return;
138 buttonContainer.style.display = 'flex';
139 const button = buttonContainer.querySelector(
140 '.cbp-see-more-simple-btn',
141 );
142 if (!button) return;
143 if (currentContainer.classList.contains('padding-disabled')) {
144 button.classList.remove('cbp-see-more-simple-btn-hover');
145 }
146 button.style.transition = `all ${
147 Math.max(animationSpeed, 1) / 1.5
148 }s linear`;
149
150 const handle = (event) => {
151 event.preventDefault();
152 button.classList.remove('cbp-see-more-simple-btn-hover');
153 pre.style.maxHeight = initialHeight + 'px';
154 setTimeout(() => {
155 button.style.opacity = 0;
156 button.style.transform = 'translateY(-100%)';
157 setTimeout(
158 () => button.remove(),
159 Math.max(animationSpeed, 1) * 1000,
160 );
161 }, animationSpeed * 1000);
162 };
163 button.addEventListener('click', handle);
164 button.addEventListener('keydown', (event) => {
165 if (event.key === 'Enter') handle(event);
166 });
167 });
168 };
169
170 const init = () => {
171 handleFontLoading();
172 handleSeeMore();
173 handleCopyButton();
174 handleHighlighter();
175 };
176
177 // Functions are idempotent, so we can run them on load, DOMContentLoaded, et al.
178 init();
179 // Useful for when the DOM is modified or loaded in late
180 window.codeBlockProInit = init;
181 window.addEventListener('DOMContentLoaded', init);
182 window.addEventListener('load', init);
183