PluginProbe
Code Block Pro – Beautiful Syntax Highlighting / 1.11.0
Code Block Pro – Beautiful Syntax Highlighting v1.11.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.11.0, at src/editor/Edit.tsx

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