PluginProbe
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor / 2.0.13
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor v2.0.13
2.0.13 2.0.12 2.0.11 2.0.10 2.0.9 trunk 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8
blockenberg / blocks / code-tabs / index.js

index.js in Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor 2.0.13, at blocks/code-tabs/index.js

434 lines 21.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 ( function () {
2 var el = wp.element.createElement;
3 var Fragment = wp.element.Fragment;
4 var useState = wp.element.useState;
5 var __ = wp.i18n.__;
6 var registerBlockType = wp.blocks.registerBlockType;
7 var InspectorControls = wp.blockEditor.InspectorControls;
8 var useBlockProps = wp.blockEditor.useBlockProps;
9 var PanelColorSettings = wp.blockEditor.PanelColorSettings;
10 var PanelBody = wp.components.PanelBody;
11 var ToggleControl = wp.components.ToggleControl;
12 var RangeControl = wp.components.RangeControl;
13 var SelectControl = wp.components.SelectControl;
14 var TextControl = wp.components.TextControl;
15 var Button = wp.components.Button;
16
17 function getTypographyControl() {
18 return (window.bkbgTypographyControl || function () { return null; });
19 }
20
21 var _tv = (function () {
22 var fn = window.bkbgTypoCssVars;
23 return fn ? fn : function () { return {}; };
24 })();
25
26 function updateItem(arr, idx, field, val) {
27 return arr.map(function (item, i) {
28 if (i !== idx) return item;
29 var p = {}; p[field] = val;
30 return Object.assign({}, item, p);
31 });
32 }
33
34 function moveItem(arr, from, to) {
35 var a = arr.slice();
36 var item = a.splice(from, 1)[0];
37 a.splice(to, 0, item);
38 return a;
39 }
40
41 var LANG_OPTIONS = [
42 { value: 'javascript', label: 'JavaScript' },
43 { value: 'typescript', label: 'TypeScript' },
44 { value: 'python', label: 'Python' },
45 { value: 'php', label: 'PHP' },
46 { value: 'ruby', label: 'Ruby' },
47 { value: 'go', label: 'Go' },
48 { value: 'rust', label: 'Rust' },
49 { value: 'java', label: 'Java' },
50 { value: 'csharp', label: 'C#' },
51 { value: 'cpp', label: 'C++' },
52 { value: 'bash', label: 'Bash / Shell' },
53 { value: 'sql', label: 'SQL' },
54 { value: 'html', label: 'HTML' },
55 { value: 'css', label: 'CSS' },
56 { value: 'json', label: 'JSON' },
57 { value: 'yaml', label: 'YAML' },
58 { value: 'markdown', label: 'Markdown' },
59 { value: 'plaintext', label: 'Plain Text' }
60 ];
61
62 var THEME_OPTIONS = [
63 { value: 'dark', label: 'Dark (default)' },
64 { value: 'monokai', label: 'Monokai' },
65 { value: 'dracula', label: 'Dracula' },
66 { value: 'github-dark', label: 'GitHub Dark' },
67 { value: 'nord', label: 'Nord' },
68 { value: 'light', label: 'Light' },
69 { value: 'github-light', label: 'GitHub Light' },
70 { value: 'solarized-light', label: 'Solarized Light' }
71 ];
72
73 /* ── CodePreview ── */
74 function CodePreview(props) {
75 var a = props.attributes;
76 var editorActive = props.editorActive;
77 var setEditorActive = props.setEditorActive;
78 var tabs = a.tabs || [];
79
80 if (tabs.length === 0) {
81 return el('div', { style: { padding: '24px', textAlign: 'center', color: '#9ca3af', background: '#13131f', borderRadius: a.borderRadius + 'px' } }, 'Add a tab to preview code');
82 }
83
84 var activeIdx = Math.min(editorActive, tabs.length - 1);
85 var activeTab = tabs[activeIdx];
86
87 /* header */
88 var tabPills = el('div', {
89 style: {
90 display: 'flex',
91 gap: '6px',
92 alignItems: 'center',
93 flexWrap: 'wrap',
94 justifyContent: a.tabAlign === 'center' ? 'center' : a.tabAlign === 'right' ? 'flex-end' : 'flex-start'
95 }
96 },
97 tabs.map(function (tab, idx) {
98 var isActive = idx === activeIdx;
99 return el('button', {
100 key: idx,
101 onClick: function () { setEditorActive(idx); },
102 style: {
103 padding: a.tabStyle === 'pills' ? '5px 14px' : '8px 16px',
104 borderRadius: a.tabStyle === 'pills' ? '20px' : a.tabStyle === 'underline' ? '0' : '6px',
105 background: isActive ? (a.tabActiveBg || '#6366f1') : (a.tabIdleBg || '#2d2d3f'),
106 color: isActive ? (a.tabActiveColor || '#fff') : (a.tabIdleColor || '#a0a0c0'),
107 border: a.tabStyle === 'underline' ? ('0 0 2px 0 solid ' + (isActive ? (a.tabActiveBg || '#6366f1') : 'transparent')) : 'none',
108 borderBottom: a.tabStyle === 'underline' ? ('2px solid ' + (isActive ? (a.tabActiveBg || '#6366f1') : 'transparent')) : undefined,
109 fontSize: '12px',
110 fontWeight: isActive ? '600' : '400',
111 cursor: 'pointer',
112 transition: 'all 0.15s',
113 fontFamily: 'ui-monospace, SFMono-Regular, monospace'
114 }
115 }, tab.label || 'Tab ' + (idx + 1));
116 })
117 );
118
119 var header = el('div', {
120 style: {
121 background: a.headerBg || '#1e1e2e',
122 padding: '10px 16px',
123 borderRadius: a.borderRadius + 'px ' + a.borderRadius + 'px 0 0',
124 display: 'flex',
125 alignItems: 'center',
126 gap: '12px',
127 flexWrap: 'wrap'
128 }
129 },
130 /* Window dots */
131 el('div', { style: { display: 'flex', gap: '6px', flexShrink: 0 } },
132 el('div', { style: { width: '12px', height: '12px', borderRadius: '50%', background: '#ff5f57' } }),
133 el('div', { style: { width: '12px', height: '12px', borderRadius: '50%', background: '#febc2e' } }),
134 el('div', { style: { width: '12px', height: '12px', borderRadius: '50%', background: '#28c840' } })
135 ),
136 /* Tabs */
137 tabPills,
138 /* Filename */
139 a.showFileName && activeTab.fileName && el('span', {
140 style: {
141 marginLeft: 'auto',
142 fontSize: '11px',
143 color: '#6b7280',
144 fontFamily: 'ui-monospace, SFMono-Regular, monospace'
145 }
146 }, activeTab.fileName),
147 /* Lang badge */
148 a.showLangBadge && el('span', {
149 style: {
150 fontSize: '10px',
151 fontWeight: '700',
152 color: '#a0a0c0',
153 background: '#2d2d3f',
154 padding: '2px 8px',
155 borderRadius: '4px',
156 textTransform: 'uppercase',
157 letterSpacing: '0.05em',
158 fontFamily: 'ui-monospace, SFMono-Regular, monospace'
159 }
160 }, activeTab.lang || 'text')
161 );
162
163 /* code body */
164 var lines = (activeTab.code || '').split('\n');
165 var codeRows = lines.map(function (line, li) {
166 return el('div', { key: li, style: { display: 'flex' } },
167 a.showLineNumbers && el('span', {
168 style: {
169 userSelect: 'none',
170 minWidth: '40px',
171 paddingRight: '16px',
172 color: a.lineNumColor || '#4a4a6a',
173 textAlign: 'right',
174 flexShrink: 0
175 }
176 }, li + 1),
177 el('span', {
178 style: {
179 flex: 1,
180 whiteSpace: 'pre',
181 color: '#e0e0f0'
182 }
183 }, line)
184 );
185 });
186
187 var codeBody = el('div', {
188 style: {
189 background: a.codeBg || '#13131f',
190 padding: '16px',
191 overflowX: 'auto',
192 overflowY: a.maxHeight > 0 ? 'auto' : 'visible',
193 maxHeight: a.maxHeight > 0 ? a.maxHeight + 'px' : 'none',
194 borderRadius: '0 0 ' + a.borderRadius + 'px ' + a.borderRadius + 'px',
195 fontFamily: 'ui-monospace, SFMono-Regular, "Fira Code", monospace',
196 fontSize: 'var(--bkbg-ctabs-cd-font-size-d, 13px)',
197 lineHeight: 'var(--bkbg-ctabs-cd-line-height-d, 1.65)',
198 position: 'relative'
199 }
200 },
201 a.showCopyButton && el('button', {
202 style: {
203 position: 'absolute',
204 top: '10px',
205 right: '10px',
206 background: a.copyBg || '#2d2d3f',
207 color: a.copyColor || '#a0a0c0',
208 border: 'none',
209 borderRadius: '6px',
210 padding: '4px 10px',
211 fontSize: '11px',
212 cursor: 'pointer',
213 fontFamily: 'inherit'
214 }
215 }, 'Copy'),
216 el('div', null, codeRows)
217 );
218
219 return el('div', { style: { borderRadius: a.borderRadius + 'px', overflow: 'hidden' } },
220 header,
221 codeBody
222 );
223 }
224
225 /* ── TabEditor ── */
226 function TabEditor(props) {
227 var tabs = props.tabs;
228 var onChange = props.onChange;
229 var activeIdx = props.activeIdx;
230 var setActiveIdx = props.setActiveIdx;
231
232 function addTab() {
233 onChange(tabs.concat([{ label: 'New Tab', lang: 'javascript', fileName: '', code: '// your code here' }]));
234 setActiveIdx(tabs.length);
235 }
236
237 return el(Fragment, null,
238 tabs.map(function (tab, idx) {
239 var isOpen = idx === activeIdx;
240 return el('div', {
241 key: idx,
242 style: {
243 border: '1px solid ' + (isOpen ? '#6366f1' : '#e5e7eb'),
244 borderRadius: '6px',
245 marginBottom: '6px',
246 overflow: 'hidden'
247 }
248 },
249 /* row header */
250 el('div', {
251 style: {
252 display: 'flex',
253 alignItems: 'center',
254 gap: '6px',
255 padding: '7px 10px',
256 background: isOpen ? '#f0f0ff' : '#f9fafb',
257 cursor: 'pointer'
258 },
259 onClick: function () { setActiveIdx(isOpen ? -1 : idx); }
260 },
261 el('span', { style: { flex: 1, fontSize: '12px', fontWeight: '600', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' } }, tab.label || 'Tab ' + (idx + 1)),
262 el('span', { style: { fontSize: '10px', color: '#9ca3af', flexShrink: 0 } }, tab.lang || ''),
263 el(Button, { icon: 'arrow-up-alt2', isSmall: true, disabled: idx === 0, onClick: function (e) { e.stopPropagation(); onChange(moveItem(tabs, idx, idx - 1)); setActiveIdx(idx - 1); } }),
264 el(Button, { icon: 'arrow-down-alt2', isSmall: true, disabled: idx === tabs.length - 1, onClick: function (e) { e.stopPropagation(); onChange(moveItem(tabs, idx, idx + 1)); setActiveIdx(idx + 1); } }),
265 el(Button, { icon: 'no-alt', isSmall: true, isDestructive: true, onClick: function (e) { e.stopPropagation(); var a = tabs.slice(); a.splice(idx, 1); onChange(a); setActiveIdx(-1); } })
266 ),
267
268 isOpen && el('div', { style: { padding: '10px', display: 'flex', flexDirection: 'column', gap: '8px' } },
269 el(TextControl, { label: __('Tab Label', 'blockenberg'), value: tab.label || '', onChange: function (v) { onChange(updateItem(tabs, idx, 'label', v)); }, __nextHasNoMarginBottom: true }),
270 el(SelectControl, {
271 label: __('Language', 'blockenberg'),
272 value: tab.lang || 'javascript',
273 options: LANG_OPTIONS,
274 onChange: function (v) { onChange(updateItem(tabs, idx, 'lang', v)); },
275 __nextHasNoMarginBottom: true
276 }),
277 el(TextControl, { label: __('File Name (optional)', 'blockenberg'), value: tab.fileName || '', onChange: function (v) { onChange(updateItem(tabs, idx, 'fileName', v)); }, __nextHasNoMarginBottom: true }),
278 el('div', null,
279 el('label', { style: { fontSize: '11px', fontWeight: '600', color: '#1e1e1e', display: 'block', marginBottom: '6px' } }, __('Code', 'blockenberg')),
280 el('textarea', {
281 value: tab.code || '',
282 onChange: function (e) { onChange(updateItem(tabs, idx, 'code', e.target.value)); },
283 spellCheck: false,
284 style: {
285 width: '100%',
286 minHeight: '160px',
287 fontFamily: 'ui-monospace, SFMono-Regular, monospace',
288 fontSize: '12px',
289 padding: '8px',
290 border: '1px solid #e5e7eb',
291 borderRadius: '4px',
292 background: '#13131f',
293 color: '#e0e0f0',
294 resize: 'vertical',
295 boxSizing: 'border-box',
296 lineHeight: '1.6',
297 tabSize: 2
298 }
299 })
300 )
301 )
302 );
303 }),
304 el(Button, {
305 variant: 'secondary',
306 onClick: addTab,
307 style: { marginTop: '6px', width: '100%', justifyContent: 'center' }
308 }, __('+ Add Tab', 'blockenberg'))
309 );
310 }
311
312 /* ── register ── */
313 registerBlockType('blockenberg/code-tabs', {
314 edit: function (props) {
315 var a = props.attributes;
316 var set = props.setAttributes;
317 var tabIdxState = useState(-1);
318 var tabIdx = tabIdxState[0];
319 var setTabIdx = tabIdxState[1];
320 var previewActiveState = useState(0);
321 var previewActive = previewActiveState[0];
322 var setPreviewActive = previewActiveState[1];
323
324 var bpStyle = {
325 paddingTop: (a.paddingTop || 0) + 'px',
326 paddingBottom: (a.paddingBottom || 0) + 'px',
327 background: a.bgColor
328 };
329 Object.assign(bpStyle, _tv(a.typoCode, '--bkbg-ctabs-cd'));
330 var blockProps = useBlockProps({ style: bpStyle });
331
332 var inspector = el(InspectorControls, null,
333 /* Tabs */
334 el(PanelBody, { title: __('Code Tabs', 'blockenberg'), initialOpen: true },
335 el(TabEditor, {
336 tabs: a.tabs,
337 onChange: function (v) { set({ tabs: v }); },
338 activeIdx: tabIdx,
339 setActiveIdx: setTabIdx
340 })
341 ),
342
343 /* Display */
344 el(PanelBody, { title: __('Display', 'blockenberg'), initialOpen: false },
345 el(SelectControl, {
346 label: __('Theme', 'blockenberg'),
347 value: a.theme,
348 options: THEME_OPTIONS,
349 onChange: function (v) { set({ theme: v }); },
350 __nextHasNoMarginBottom: true
351 }),
352 el('div', { style: { marginTop: '8px' } },
353 el(SelectControl, {
354 label: __('Tab Style', 'blockenberg'),
355 value: a.tabStyle,
356 options: [
357 { value: 'pills', label: 'Pills' },
358 { value: 'buttons', label: 'Buttons' },
359 { value: 'underline', label: 'Underline' }
360 ],
361 onChange: function (v) { set({ tabStyle: v }); },
362 __nextHasNoMarginBottom: true
363 })
364 ),
365 el('div', { style: { marginTop: '8px' } },
366 el(SelectControl, {
367 label: __('Tab Alignment', 'blockenberg'),
368 value: a.tabAlign,
369 options: [
370 { value: 'left', label: 'Left' },
371 { value: 'center', label: 'Center' },
372 { value: 'right', label: 'Right' }
373 ],
374 onChange: function (v) { set({ tabAlign: v }); },
375 __nextHasNoMarginBottom: true
376 })
377 ),
378 el(ToggleControl, { label: __('Show Line Numbers', 'blockenberg'), checked: a.showLineNumbers, onChange: function (v) { set({ showLineNumbers: v }); }, __nextHasNoMarginBottom: true }),
379 el(ToggleControl, { label: __('Show Copy Button', 'blockenberg'), checked: a.showCopyButton, onChange: function (v) { set({ showCopyButton: v }); }, __nextHasNoMarginBottom: true }),
380 el(ToggleControl, { label: __('Show File Name', 'blockenberg'), checked: a.showFileName, onChange: function (v) { set({ showFileName: v }); }, __nextHasNoMarginBottom: true }),
381 el(ToggleControl, { label: __('Show Language Badge', 'blockenberg'), checked: a.showLangBadge, onChange: function (v) { set({ showLangBadge: v }); }, __nextHasNoMarginBottom: true }),
382 el('div', { style: { marginTop: '8px' } },
383 el(RangeControl, { label: __('Max Height (0 = auto)', 'blockenberg'), value: a.maxHeight, min: 0, max: 800, step: 20, onChange: function (v) { set({ maxHeight: v }); }, __nextHasNoMarginBottom: true })
384 ),
385 el('div', { style: { marginTop: '8px' } },
386 el(RangeControl, { label: __('Border Radius', 'blockenberg'), value: a.borderRadius, min: 0, max: 24, onChange: function (v) { set({ borderRadius: v }); }, __nextHasNoMarginBottom: true })
387 ),
388 el('div', { style: { marginTop: '8px' } },
389 el(RangeControl, { label: __('Padding Top', 'blockenberg'), value: a.paddingTop, min: 0, max: 120, step: 4, onChange: function (v) { set({ paddingTop: v }); }, __nextHasNoMarginBottom: true })
390 ),
391 el('div', { style: { marginTop: '8px' } },
392 el(RangeControl, { label: __('Padding Bottom', 'blockenberg'), value: a.paddingBottom, min: 0, max: 120, step: 4, onChange: function (v) { set({ paddingBottom: v }); }, __nextHasNoMarginBottom: true })
393 )
394 ),
395
396 /* Colors */
397
398 el( PanelBody, { title: __( 'Typography', 'blockenberg' ), initialOpen: false },
399 el(getTypographyControl(), { label: __('Code', 'blockenberg'), value: a.typoCode, onChange: function (v) { set({ typoCode: v }); } })
400 ),
401 el(PanelColorSettings, {
402 title: __('Colors', 'blockenberg'),
403 initialOpen: false,
404 colorSettings: [
405 { value: a.bgColor, onChange: function (v) { set({ bgColor: v }); }, label: __('Page Background', 'blockenberg') },
406 { value: a.headerBg, onChange: function (v) { set({ headerBg: v }); }, label: __('Header Background', 'blockenberg') },
407 { value: a.codeBg, onChange: function (v) { set({ codeBg: v }); }, label: __('Code Background', 'blockenberg') },
408 { value: a.tabActiveBg, onChange: function (v) { set({ tabActiveBg: v }); }, label: __('Active Tab Background', 'blockenberg') },
409 { value: a.tabActiveColor, onChange: function (v) { set({ tabActiveColor: v }); }, label: __('Active Tab Text', 'blockenberg') },
410 { value: a.tabIdleBg, onChange: function (v) { set({ tabIdleBg: v }); }, label: __('Idle Tab Background', 'blockenberg') },
411 { value: a.tabIdleColor, onChange: function (v) { set({ tabIdleColor: v }); }, label: __('Idle Tab Text', 'blockenberg') },
412 { value: a.lineNumColor, onChange: function (v) { set({ lineNumColor: v }); }, label: __('Line Number Color', 'blockenberg') },
413 { value: a.copyBg, onChange: function (v) { set({ copyBg: v }); }, label: __('Copy Button Background', 'blockenberg') },
414 { value: a.copyColor, onChange: function (v) { set({ copyColor: v }); }, label: __('Copy Button Text', 'blockenberg') }
415 ]
416 })
417 );
418
419 return el(Fragment, null,
420 inspector,
421 el('div', blockProps,
422 el(CodePreview, { attributes: a, editorActive: previewActive, setEditorActive: setPreviewActive })
423 )
424 );
425 },
426
427 save: function (props) {
428 return el('div', useBlockProps.save(),
429 el('div', { className: 'bkbg-ctabs-app', 'data-opts': JSON.stringify(props.attributes) })
430 );
431 }
432 });
433 }() );
434