PluginProbe
Code Block Pro – Beautiful Syntax Highlighting / 1.13.0
Code Block Pro – Beautiful Syntax Highlighting v1.13.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
← All changes | src/editor/Edit.tsx +169 -21 1.5.01.13.0 View file →
@@ -1,13 +1,22 @@
1 -import { useEffect, useRef } from '@wordpress/element';
1 +import {
2 + useCallback,
3 + useEffect,
4 + useLayoutEffect,
5 + useRef,
6 +} from '@wordpress/element';
7 +import { escapeHTML } from '@wordpress/escape-html';
2 8 import { applyFilters } from '@wordpress/hooks';
9 +import { decodeEntities } from '@wordpress/html-entities';
10 +import { sprintf, __ } from '@wordpress/i18n';
11 +import { colord } from 'colord';
3 12 import Editor from 'react-simple-code-editor';
4 13 import { useDefaults } from '../hooks/useDefaults';
5 14 import { useTheme } from '../hooks/useTheme';
6 -import { useGlobalStore } from '../state/global';
7 15 import { useLanguageStore } from '../state/language';
8 -import { useThemeStore } from '../state/theme';
9 -import { AttributesPropsAndSetter } from '../types';
16 +import { AttributesPropsAndSetter, Lang } from '../types';
17 +import { parseJSONArrayWithRanges } from '../util/arrayHelpers';
18 +import { getEditorLanguage } from '../util/languages';
10 19
11 20 export const Edit = ({
12 21 attributes,
13 22 setAttributes,
@@ -17,18 +26,56 @@
17 26 theme,
18 27 code = '',
19 28 bgColor: backgroundColor,
20 29 textColor: color,
30 + disablePadding,
31 + lineNumbersWidth,
32 + lineNumbers,
33 + startingLineNumber,
34 + footerType,
35 + fontSize,
36 + fontFamily,
37 + lineHeight,
38 + lineBlurs,
39 + lineHighlights,
40 + enableBlurring,
41 + enableHighlighting,
42 + seeMoreAfterLine,
43 + seeMoreTransition,
44 + enableMaxHeight,
45 + editorHeight,
21 46 } = attributes;
47 +
22 48 const textAreaRef = useRef<HTMLDivElement>(null);
23 - const handleChange = (code: string) => setAttributes({ code });
49 + const handleChange = (code: string) =>
50 + setAttributes({ code: escapeHTML(code) });
24 51 const { previousLanguage } = useLanguageStore();
25 52 const { highlighter, error, loading } = useTheme({
26 53 theme,
27 54 lang: language ?? previousLanguage,
28 55 });
56 + const hasFooter = footerType && footerType !== 'none';
29 57 useDefaults({ attributes, setAttributes });
30 58
59 + const getHighlights = useCallback(() => {
60 + if (!enableHighlighting) return [];
61 + return parseJSONArrayWithRanges(lineHighlights, startingLineNumber).map(
62 + (line: number) => ({
63 + line,
64 + classes: ['cbp-line-highlight'],
65 + }),
66 + );
67 + }, [enableHighlighting, lineHighlights, startingLineNumber]);
68 + const getBlurs = useCallback(() => {
69 + if (!enableBlurring) return [];
70 + return parseJSONArrayWithRanges(lineBlurs, startingLineNumber).map(
71 + (line: number) => ({
72 + line,
73 + classes: ['cbp-no-blur'],
74 + }),
75 + );
76 + }, [enableBlurring, lineBlurs, startingLineNumber]);
77 +
31 78 useEffect(() => {
32 79 if (!highlighter) return;
33 80 setAttributes({
34 81 bgColor: highlighter.getBackgroundColor(),
@@ -37,31 +84,112 @@
37 84 }, [theme, highlighter, setAttributes]);
38 85
39 86 useEffect(() => {
40 87 if (!highlighter) return;
41 - // applyFilters()
42 - setAttributes({
43 - codeHTML: applyFilters(
44 - 'blocks.codeBlockPro.codeHTML',
45 - highlighter.codeToHtml(code, {
46 - lang: language ?? previousLanguage,
47 - }),
48 - attributes,
49 - ) as string,
50 - });
88 + const l = (language ?? previousLanguage) as Lang | 'ansi';
89 + const lang = getEditorLanguage(l);
90 + const c = decodeEntities(code);
91 + const lineOptions = [
92 + ...getHighlights(),
93 + ...getBlurs(),
94 + enableMaxHeight && !Number.isNaN(seeMoreAfterLine)
95 + ? {
96 + line: Number(seeMoreAfterLine),
97 + classes: [
98 + 'cbp-see-more-line',
99 + seeMoreTransition ? 'cbp-see-more-transition' : '',
100 + ],
101 + }
102 + : {},
103 + ];
104 + const rendered =
105 + l === 'ansi'
106 + ? highlighter.ansiToHtml(c, { lineOptions })
107 + : highlighter.codeToHtml(c, { lang, lineOptions });
108 + const codeHTML = applyFilters(
109 + 'blocks.codeBlockPro.codeHTML',
110 + rendered,
111 + attributes,
112 + ) as string;
113 + const lineHighlightColor = colord(color)
114 + .saturate(0.5)
115 + .alpha(0.2)
116 + .toRgbString();
117 + setAttributes({ codeHTML, lineHighlightColor });
51 118 }, [
52 119 highlighter,
120 + seeMoreAfterLine,
121 + seeMoreTransition,
122 + color,
53 123 code,
124 + enableMaxHeight,
54 125 language,
55 126 setAttributes,
56 127 previousLanguage,
57 128 attributes,
129 + lineHighlights,
130 + lineBlurs,
131 + getBlurs,
132 + getHighlights,
58 133 ]);
59 134
135 + useLayoutEffect(() => {
136 + if (!textAreaRef.current) return;
137 + if (!lineNumbers) {
138 + setAttributes({ lineNumbersWidth: undefined });
139 + return;
140 + }
141 + // Get the last line (assumingly the widest)
142 + const lastLine = Array.from(
143 + textAreaRef?.current?.querySelectorAll('.line') ?? [],
144 + )?.at(-1) as HTMLElement;
145 + // Make sure there are no width constraints
146 + lastLine?.classList?.add('cbp-line-number-width-forced');
147 + const lastLineWidth = lastLine?.getBoundingClientRect()?.width ?? 0;
148 + // Add .cbp-line-number-disabled to disable the line number
149 + lastLine?.classList.add('cbp-line-number-disabled');
150 + // Re calculate the width of the last line
151 + const newWidth = lastLine?.getBoundingClientRect()?.width ?? 0;
152 + // Remove the classes
153 + lastLine?.classList.remove('cbp-line-number-disabled');
154 + lastLine?.classList?.remove('cbp-line-number-width-forced');
155 + // Calculate the difference
156 + if (lastLineWidth - newWidth > 0) {
157 + setAttributes({ lineNumbersWidth: lastLineWidth - newWidth - 12 });
158 + }
159 + }, [
160 + lineNumbers,
161 + startingLineNumber,
162 + code,
163 + loading,
164 + error,
165 + textAreaRef,
166 + setAttributes,
167 + fontSize,
168 + fontFamily,
169 + lineHeight,
170 + ]);
171 +
172 + if (!loading && !highlighter) {
173 + return (
174 + <div
175 + className="p-8 px-4 text-left"
176 + style={{ backgroundColor, color }}>
177 + {sprintf(
178 + __(
179 + 'Theme %s not found. Please select a different theme.',
180 + 'code-block-pro',
181 + ),
182 + theme,
183 + )}
184 + </div>
185 + );
186 + }
187 +
60 188 if ((loading && code) || error) {
61 189 return (
62 190 <div
63 - className="p-8 px-4 text-left"
191 + className="p-6 px-4 text-left"
64 192 style={{ backgroundColor, color }}>
65 193 {error?.message ?? ''}
66 194 </div>
67 195 );
@@ -67,13 +195,30 @@
67 195 );
68 196 }
69 197
70 198 return (
71 - <div ref={textAreaRef}>
199 + <div
200 + ref={textAreaRef}
201 + style={{
202 + maxHeight: Number(editorHeight)
203 + ? Number(editorHeight)
204 + : undefined,
205 + overflow: Number(editorHeight) ? 'auto' : undefined,
206 + }}>
72 207 <Editor
73 - value={code}
208 + value={decodeEntities(code)}
74 209 onValueChange={handleChange}
75 - padding={16}
210 + padding={{
211 + top: disablePadding ? 0 : 16,
212 + bottom: disablePadding || hasFooter ? 0 : 16,
213 + left: (() => {
214 + if (!lineNumbers && disablePadding) return 0;
215 + if (!lineNumbers) return 16;
216 + if (disablePadding) return (lineNumbersWidth ?? 0) + 16;
217 + return (lineNumbersWidth ?? 0) + 32;
218 + })(),
219 + right: 0,
220 + }}
76 221 style={{ backgroundColor, color }}
77 222 // eslint-disable-next-line
78 223 onKeyDown={(e: any) =>
79 224 e.key === 'Tab' &&
@@ -81,10 +226,13 @@
81 226 textAreaRef.current?.querySelector('textarea')?.focus()
82 227 }
83 228 highlight={(code: string) =>
84 229 highlighter
85 - ?.codeToHtml(code, {
86 - lang: language ?? previousLanguage,
230 + ?.codeToHtml(decodeEntities(code), {
231 + lang: getEditorLanguage(
232 + language ?? previousLanguage,
233 + ),
234 + lineOptions: [...getHighlights(), ...getBlurs()],
87 235 })
88 236 ?.replace(/<\/?[pre|code][^>]*>/g, '')
89 237 }
90 238 />