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
code-block-pro / src / editor / Edit.tsx

Edit.tsx in Code Block Pro – Beautiful Syntax Highlighting 1.13.0, at src/editor/Edit.tsx

242 lines 7.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import {
2 useCallback,
3 useEffect,
4 useLayoutEffect,
5 useRef,
6 } from '@wordpress/element';
7 import { escapeHTML } from '@wordpress/escape-html';
8 import { applyFilters } from '@wordpress/hooks';
9 import { decodeEntities } from '@wordpress/html-entities';
10 import { sprintf, __ } from '@wordpress/i18n';
11 import { colord } from 'colord';
12 import Editor from 'react-simple-code-editor';
13 import { useDefaults } from '../hooks/useDefaults';
14 import { useTheme } from '../hooks/useTheme';
15 import { useLanguageStore } from '../state/language';
16 import { AttributesPropsAndSetter, Lang } from '../types';
17 import { parseJSONArrayWithRanges } from '../util/arrayHelpers';
18 import { getEditorLanguage } from '../util/languages';
19
20 export const Edit = ({
21 attributes,
22 setAttributes,
23 }: AttributesPropsAndSetter) => {
24 const {
25 language,
26 theme,
27 code = '',
28 bgColor: backgroundColor,
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,
46 } = attributes;
47
48 const textAreaRef = useRef<HTMLDivElement>(null);
49 const handleChange = (code: string) =>
50 setAttributes({ code: escapeHTML(code) });
51 const { previousLanguage } = useLanguageStore();
52 const { highlighter, error, loading } = useTheme({
53 theme,
54 lang: language ?? previousLanguage,
55 });
56 const hasFooter = footerType && footerType !== 'none';
57 useDefaults({ attributes, setAttributes });
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
78 useEffect(() => {
79 if (!highlighter) return;
80 setAttributes({
81 bgColor: highlighter.getBackgroundColor(),
82 textColor: highlighter.getForegroundColor(),
83 });
84 }, [theme, highlighter, setAttributes]);
85
86 useEffect(() => {
87 if (!highlighter) return;
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 });
118 }, [
119 highlighter,
120 seeMoreAfterLine,
121 seeMoreTransition,
122 color,
123 code,
124 enableMaxHeight,
125 language,
126 setAttributes,
127 previousLanguage,
128 attributes,
129 lineHighlights,
130 lineBlurs,
131 getBlurs,
132 getHighlights,
133 ]);
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
188 if ((loading && code) || error) {
189 return (
190 <div
191 className="p-6 px-4 text-left"
192 style={{ backgroundColor, color }}>
193 {error?.message ?? ''}
194 </div>
195 );
196 }
197
198 return (
199 <div
200 ref={textAreaRef}
201 style={{
202 maxHeight: Number(editorHeight)
203 ? Number(editorHeight)
204 : undefined,
205 overflow: Number(editorHeight) ? 'auto' : undefined,
206 }}>
207 <Editor
208 value={decodeEntities(code)}
209 onValueChange={handleChange}
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 }}
221 style={{ backgroundColor, color }}
222 // eslint-disable-next-line
223 onKeyDown={(e: any) =>
224 e.key === 'Tab' &&
225 // Tab lock here. Pressing Escape will unlock.
226 textAreaRef.current?.querySelector('textarea')?.focus()
227 }
228 highlight={(code: string) =>
229 highlighter
230 ?.codeToHtml(decodeEntities(code), {
231 lang: getEditorLanguage(
232 language ?? previousLanguage,
233 ),
234 lineOptions: [...getHighlights(), ...getBlurs()],
235 })
236 ?.replace(/<\/?[pre|code][^>]*>/g, '')
237 }
238 />
239 </div>
240 );
241 };
242