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 +122 -27 1.8.01.13.0 View file →
@@ -1,11 +1,22 @@
1 -import { useEffect, useLayoutEffect, 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 15 import { useLanguageStore } from '../state/language';
7 -import { AttributesPropsAndSetter } from '../types';
16 +import { AttributesPropsAndSetter, Lang } from '../types';
17 +import { parseJSONArrayWithRanges } from '../util/arrayHelpers';
18 +import { getEditorLanguage } from '../util/languages';
8 19
9 20 export const Edit = ({
10 21 attributes,
11 22 setAttributes,
@@ -23,11 +34,21 @@
23 34 footerType,
24 35 fontSize,
25 36 fontFamily,
26 37 lineHeight,
38 + lineBlurs,
39 + lineHighlights,
40 + enableBlurring,
41 + enableHighlighting,
42 + seeMoreAfterLine,
43 + seeMoreTransition,
44 + enableMaxHeight,
45 + editorHeight,
27 46 } = attributes;
47 +
28 48 const textAreaRef = useRef<HTMLDivElement>(null);
29 - const handleChange = (code: string) => setAttributes({ code });
49 + const handleChange = (code: string) =>
50 + setAttributes({ code: escapeHTML(code) });
30 51 const { previousLanguage } = useLanguageStore();
31 52 const { highlighter, error, loading } = useTheme({
32 53 theme,
33 54 lang: language ?? previousLanguage,
@@ -34,8 +55,27 @@
34 55 });
35 56 const hasFooter = footerType && footerType !== 'none';
36 57 useDefaults({ attributes, setAttributes });
37 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 +
38 78 useEffect(() => {
39 79 if (!highlighter) return;
40 80 setAttributes({
41 81 bgColor: highlighter.getBackgroundColor(),
@@ -44,24 +84,53 @@
44 84 }, [theme, highlighter, setAttributes]);
45 85
46 86 useEffect(() => {
47 87 if (!highlighter) return;
48 - setAttributes({
49 - codeHTML: applyFilters(
50 - 'blocks.codeBlockPro.codeHTML',
51 - highlighter.codeToHtml(code, {
52 - lang: language ?? previousLanguage,
53 - }),
54 - attributes,
55 - ) as string,
56 - });
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 });
57 118 }, [
58 119 highlighter,
120 + seeMoreAfterLine,
121 + seeMoreTransition,
122 + color,
59 123 code,
124 + enableMaxHeight,
60 125 language,
61 126 setAttributes,
62 127 previousLanguage,
63 128 attributes,
129 + lineHighlights,
130 + lineBlurs,
131 + getBlurs,
132 + getHighlights,
64 133 ]);
65 134
66 135 useLayoutEffect(() => {
67 136 if (!textAreaRef.current) return;
@@ -75,16 +144,15 @@
75 144 )?.at(-1) as HTMLElement;
76 145 // Make sure there are no width constraints
77 146 lastLine?.classList?.add('cbp-line-number-width-forced');
78 147 const lastLineWidth = lastLine?.getBoundingClientRect()?.width ?? 0;
79 - // Remove the width constraints
80 - lastLine?.classList?.remove('cbp-line-number-width-forced');
81 - // Add .cbp-line-number-disabled to disable th eline number
148 + // Add .cbp-line-number-disabled to disable the line number
82 149 lastLine?.classList.add('cbp-line-number-disabled');
83 150 // Re calculate the width of the last line
84 151 const newWidth = lastLine?.getBoundingClientRect()?.width ?? 0;
85 - // Remove the class
152 + // Remove the classes
86 153 lastLine?.classList.remove('cbp-line-number-disabled');
154 + lastLine?.classList?.remove('cbp-line-number-width-forced');
87 155 // Calculate the difference
88 156 if (lastLineWidth - newWidth > 0) {
89 157 setAttributes({ lineNumbersWidth: lastLineWidth - newWidth - 12 });
90 158 }
@@ -100,12 +168,28 @@
100 168 fontFamily,
101 169 lineHeight,
102 170 ]);
103 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 +
104 188 if ((loading && code) || error) {
105 189 return (
106 190 <div
107 - className="p-8 px-4 text-left"
191 + className="p-6 px-4 text-left"
108 192 style={{ backgroundColor, color }}>
109 193 {error?.message ?? ''}
110 194 </div>
111 195 );
@@ -111,21 +195,29 @@
111 195 );
112 196 }
113 197
114 198 return (
115 - <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 + }}>
116 207 <Editor
117 - value={code}
208 + value={decodeEntities(code)}
118 209 onValueChange={handleChange}
119 210 padding={{
120 211 top: disablePadding ? 0 : 16,
121 212 bottom: disablePadding || hasFooter ? 0 : 16,
122 - left:
123 - (disablePadding ? 0 : 16) +
124 - // If line numbers are disabled, just offset the 12px padding
125 - (lineNumbersWidth ?? -12) +
126 - 12,
127 - right: disablePadding ? 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,
128 220 }}
129 221 style={{ backgroundColor, color }}
130 222 // eslint-disable-next-line
131 223 onKeyDown={(e: any) =>
@@ -134,10 +226,13 @@
134 226 textAreaRef.current?.querySelector('textarea')?.focus()
135 227 }
136 228 highlight={(code: string) =>
137 229 highlighter
138 - ?.codeToHtml(code, {
139 - lang: language ?? previousLanguage,
230 + ?.codeToHtml(decodeEntities(code), {
231 + lang: getEditorLanguage(
232 + language ?? previousLanguage,
233 + ),
234 + lineOptions: [...getHighlights(), ...getBlurs()],
140 235 })
141 236 ?.replace(/<\/?[pre|code][^>]*>/g, '')
142 237 }
143 238 />