PluginProbe
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor / 2.0.7
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor v2.0.7
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 / certificate / index.js

index.js in Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor 2.0.7, at blocks/certificate/index.js

506 lines 25.4 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 __ = wp.i18n.__;
4 var useState = wp.element.useState;
5 var registerBlockType = wp.blocks.registerBlockType;
6 var InspectorControls = wp.blockEditor.InspectorControls;
7 var MediaUpload = wp.blockEditor.MediaUpload;
8 var useBlockProps = wp.blockEditor.useBlockProps;
9 var PanelBody = wp.components.PanelBody;
10 var PanelColorSettings = wp.blockEditor.PanelColorSettings;
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 TextareaControl = wp.components.TextareaControl;
16 var Button = wp.components.Button;
17
18 function getTypographyControl() { return (window.bkbgTypographyControl || function () { return null; }); }
19 function getTypoCssVars() { return (window.bkbgTypoCssVars || function () { return {}; }); }
20 function _tv(typo, prefix) { var fn = getTypoCssVars(); return fn(typo || {}, prefix); }
21
22 function EditableText(props) {
23 var field = props.field;
24 var value = props.value;
25 var onChange = props.onChange;
26 var tag = props.tag || 'div';
27 var className = props.className || '';
28 var style = props.style || {};
29 var isEditing = props.isEditing;
30 var onStartEdit = props.onStartEdit;
31 var onEndEdit = props.onEndEdit;
32
33 if (isEditing) {
34 return el('input', {
35 type: 'text',
36 className: 'bkbg-cert-inline-input ' + className,
37 value: value,
38 autoFocus: true,
39 style: style,
40 onChange: function (e) { onChange(e.target.value); },
41 onBlur: onEndEdit,
42 onKeyDown: function (e) { if (e.key === 'Enter') onEndEdit(); }
43 });
44 }
45 return el(tag, {
46 className: 'bkbg-cert-clickable ' + className,
47 style: style,
48 onClick: onStartEdit
49 }, value || '');
50 }
51
52 function CertPreview(props) {
53 var a = props.a;
54 var setAttributes = props.setAttributes;
55 var editField = props.editField;
56 var startEdit = props.startEdit;
57 var endEdit = props.endEdit;
58
59 var fontFamily = a.fontFamily === 'serif'
60 ? 'Georgia, "Times New Roman", serif'
61 : 'system-ui, -apple-system, sans-serif';
62
63 var wrapStyle = Object.assign({
64 background: a.bgColor,
65 border: a.borderWidth + 'px solid ' + a.borderColor,
66 borderRadius: a.borderRadius + 'px',
67 maxWidth: a.maxWidth + 'px',
68 margin: '0 auto',
69 padding: '48px 56px',
70 position: 'relative',
71 fontFamily: fontFamily,
72 boxShadow: a.shadowSize > 0 ? '0 ' + (a.shadowSize * 4) + 'px ' + (a.shadowSize * 16) + 'px rgba(0,0,0,.' + (a.shadowSize * 4) + ')' : 'none',
73 boxSizing: 'border-box'
74 }, _tv(a.typoRecipient, '--bkbg-cert-r-'), _tv(a.typoCourseTitle, '--bkbg-cert-ct-'), _tv(a.typoBody, '--bkbg-cert-bd-'));
75
76 var innerBorderStyle = a.innerBorder ? {
77 position: 'absolute',
78 inset: '10px',
79 border: '1px solid ' + a.borderColor,
80 pointerEvents: 'none',
81 borderRadius: a.borderRadius + 'px'
82 } : null;
83
84 // Corner decorations for classic style
85 var corners = a.style === 'classic' ? el('div', { className: 'bkbg-cert-corners' },
86 el('span', { className: 'bkbg-cert-corner tl', style: { color: a.accentColor } }, ''),
87 el('span', { className: 'bkbg-cert-corner tr', style: { color: a.accentColor } }, ''),
88 el('span', { className: 'bkbg-cert-corner bl', style: { color: a.accentColor } }, ''),
89 el('span', { className: 'bkbg-cert-corner br', style: { color: a.accentColor } }, '')
90 ) : null;
91
92 // Issuer logo
93 var logoEl = null;
94 if (a.issuerLogoUrl) {
95 logoEl = el('img', { src: a.issuerLogoUrl, alt: a.issuerName, className: 'bkbg-cert-logo' });
96 }
97
98 // Badge
99 var badgeEl = a.showBadge ? el('div', { className: 'bkbg-cert-badge', style: { borderColor: a.accentColor, color: a.accentColor } }, a.badgeEmoji) : null;
100
101 // Pre-title
102 var preTitleEl = el(EditableText, {
103 field: 'preTitle', value: a.preTitle,
104 tag: 'div', className: 'bkbg-cert-pretitle',
105 style: { color: a.preTitleColor, letterSpacing: '0.12em' },
106 isEditing: editField === 'preTitle',
107 onChange: function (v) { setAttributes({ preTitle: v }); },
108 onStartEdit: function () { startEdit('preTitle'); },
109 onEndEdit: endEdit
110 });
111
112 // Awarded to text
113 var awardedToEl = el(EditableText, {
114 field: 'awardedTo', value: a.awardedTo,
115 tag: 'div', className: 'bkbg-cert-awarded-to',
116 style: { color: a.descColor },
117 isEditing: editField === 'awardedTo',
118 onChange: function (v) { setAttributes({ awardedTo: v }); },
119 onStartEdit: function () { startEdit('awardedTo'); },
120 onEndEdit: endEdit
121 });
122
123 // Recipient name
124 var recipientEl = el(EditableText, {
125 field: 'recipientName', value: a.recipientName,
126 tag: 'div', className: 'bkbg-cert-recipient',
127 style: { color: a.recipientColor },
128 isEditing: editField === 'recipientName',
129 onChange: function (v) { setAttributes({ recipientName: v }); },
130 onStartEdit: function () { startEdit('recipientName'); },
131 onEndEdit: endEdit
132 });
133
134 // Course title
135 var courseTitleEl = el(EditableText, {
136 field: 'courseTitle', value: a.courseTitle,
137 tag: 'div', className: 'bkbg-cert-course',
138 style: { color: a.courseTitleColor },
139 isEditing: editField === 'courseTitle',
140 onChange: function (v) { setAttributes({ courseTitle: v }); },
141 onStartEdit: function () { startEdit('courseTitle'); },
142 onEndEdit: endEdit
143 });
144
145 // Course description
146 var courseDescEl = editField === 'courseDesc'
147 ? el('textarea', {
148 className: 'bkbg-cert-inline-input bkbg-cert-desc',
149 value: a.courseDesc,
150 autoFocus: true,
151 rows: 3,
152 style: { color: a.descColor, width: '100%', resize: 'vertical' },
153 onChange: function (e) { setAttributes({ courseDesc: e.target.value }); },
154 onBlur: endEdit
155 })
156 : el('div', {
157 className: 'bkbg-cert-desc bkbg-cert-clickable',
158 style: { color: a.descColor },
159 onClick: function () { startEdit('courseDesc'); }
160 }, a.courseDesc);
161
162 // Divider
163 var divider = el('div', { className: 'bkbg-cert-divider', style: { '--bkbg-cert-accent': a.accentColor } });
164
165 // Signatures row
166 var sig1El = a.showSignature1 ? el('div', { className: 'bkbg-cert-sig' },
167 el('div', { className: 'bkbg-cert-sig-line', style: { borderColor: a.metaColor } }),
168 el(EditableText, {
169 field: 'signature1Name', value: a.signature1Name,
170 tag: 'div', className: 'bkbg-cert-sig-name',
171 style: { color: a.issuerColor },
172 isEditing: editField === 'signature1Name',
173 onChange: function (v) { setAttributes({ signature1Name: v }); },
174 onStartEdit: function () { startEdit('signature1Name'); },
175 onEndEdit: endEdit
176 }),
177 el(EditableText, {
178 field: 'signature1Title', value: a.signature1Title,
179 tag: 'div', className: 'bkbg-cert-sig-title',
180 style: { color: a.metaColor },
181 isEditing: editField === 'signature1Title',
182 onChange: function (v) { setAttributes({ signature1Title: v }); },
183 onStartEdit: function () { startEdit('signature1Title'); },
184 onEndEdit: endEdit
185 })
186 ) : null;
187
188 var sig2El = a.showSignature2 ? el('div', { className: 'bkbg-cert-sig' },
189 el('div', { className: 'bkbg-cert-sig-line', style: { borderColor: a.metaColor } }),
190 el(EditableText, {
191 field: 'signature2Name', value: a.signature2Name,
192 tag: 'div', className: 'bkbg-cert-sig-name',
193 style: { color: a.issuerColor },
194 isEditing: editField === 'signature2Name',
195 onChange: function (v) { setAttributes({ signature2Name: v }); },
196 onStartEdit: function () { startEdit('signature2Name'); },
197 onEndEdit: endEdit
198 }),
199 el(EditableText, {
200 field: 'signature2Title', value: a.signature2Title,
201 tag: 'div', className: 'bkbg-cert-sig-title',
202 style: { color: a.metaColor },
203 isEditing: editField === 'signature2Title',
204 onChange: function (v) { setAttributes({ signature2Title: v }); },
205 onStartEdit: function () { startEdit('signature2Title'); },
206 onEndEdit: endEdit
207 })
208 ) : null;
209
210 // Issuer row
211 var issuerRow = el('div', { className: 'bkbg-cert-issuer-row' },
212 logoEl,
213 el(EditableText, {
214 field: 'issuerName', value: a.issuerName,
215 tag: 'div', className: 'bkbg-cert-issuer-name',
216 style: { color: a.issuerColor },
217 isEditing: editField === 'issuerName',
218 onChange: function (v) { setAttributes({ issuerName: v }); },
219 onStartEdit: function () { startEdit('issuerName'); },
220 onEndEdit: endEdit
221 })
222 );
223
224 // Meta row (date + credential)
225 var metaRow = el('div', { className: 'bkbg-cert-meta', style: { color: a.metaColor } },
226 a.showDate && el('span', { className: 'bkbg-cert-date' },
227 el('span', { className: 'bkbg-cert-meta-label' }, 'Date: '),
228 editField === 'completionDate'
229 ? el('input', { type: 'text', className: 'bkbg-cert-inline-input', value: a.completionDate, autoFocus: true, onChange: function (e) { setAttributes({ completionDate: e.target.value }); }, onBlur: endEdit })
230 : el('span', { className: 'bkbg-cert-clickable', onClick: function () { startEdit('completionDate'); } }, a.completionDate)
231 ),
232 a.showCredentialId && el('span', { className: 'bkbg-cert-cred' },
233 el('span', { className: 'bkbg-cert-meta-label' }, 'Credential ID: '),
234 editField === 'credentialId'
235 ? el('input', { type: 'text', className: 'bkbg-cert-inline-input', value: a.credentialId, autoFocus: true, onChange: function (e) { setAttributes({ credentialId: e.target.value }); }, onBlur: endEdit })
236 : el('span', { className: 'bkbg-cert-clickable', onClick: function () { startEdit('credentialId'); } }, a.credentialId)
237 )
238 );
239
240 return el('div', { className: 'bkbg-cert-wrap bkbg-cert-style-' + (a.style || 'classic'), style: wrapStyle },
241 a.innerBorder && el('div', { className: 'bkbg-cert-inner-border', style: innerBorderStyle }),
242 corners,
243 el('div', { className: 'bkbg-cert-body' },
244 badgeEl,
245 preTitleEl,
246 awardedToEl,
247 recipientEl,
248 courseTitleEl,
249 courseDescEl,
250 divider,
251 el('div', { className: 'bkbg-cert-sigs-row' }, sig1El, sig2El),
252 divider,
253 issuerRow,
254 metaRow
255 )
256 );
257 }
258
259 registerBlockType('blockenberg/certificate', {
260 title: __('Certificate', 'blockenberg'),
261 icon: 'awards',
262 category: 'bkbg-business',
263 description: __('Digital certificate or diploma with recipient, issuer, and credential details.', 'blockenberg'),
264
265 edit: function (props) {
266 var a = props.attributes;
267 var setAttributes = props.setAttributes;
268
269 var editState = useState(null);
270 var editField = editState[0]; var setEditField = editState[1];
271
272 function startEdit(f) { setEditField(f); }
273 function endEdit() { setEditField(null); }
274
275 var styleOpts = [
276 { label: 'Classic', value: 'classic' },
277 { label: 'Modern', value: 'modern' },
278 { label: 'Minimal', value: 'minimal' }
279 ];
280 var fontOpts = [
281 { label: 'Serif (Traditional)', value: 'serif' },
282 { label: 'Sans-serif (Modern)', value: 'sans' }
283 ];
284
285 var inspector = el(InspectorControls, {},
286 el(PanelBody, { title: __('Style', 'blockenberg'), initialOpen: true },
287 el(SelectControl, {
288 label: __('Certificate Style', 'blockenberg'),
289 value: a.style,
290 options: styleOpts,
291 __nextHasNoMarginBottom: true,
292 onChange: function (v) { setAttributes({ style: v }); }
293 }),
294 el(RangeControl, {
295 label: __('Max Width (px)', 'blockenberg'),
296 value: a.maxWidth,
297 min: 480, max: 1200,
298 __nextHasNoMarginBottom: true,
299 onChange: function (v) { setAttributes({ maxWidth: v }); }
300 }),
301 el(RangeControl, {
302 label: __('Border Width', 'blockenberg'),
303 value: a.borderWidth,
304 min: 0, max: 20,
305 __nextHasNoMarginBottom: true,
306 onChange: function (v) { setAttributes({ borderWidth: v }); }
307 }),
308 el(RangeControl, {
309 label: __('Border Radius', 'blockenberg'),
310 value: a.borderRadius,
311 min: 0, max: 20,
312 __nextHasNoMarginBottom: true,
313 onChange: function (v) { setAttributes({ borderRadius: v }); }
314 }),
315 el(RangeControl, {
316 label: __('Shadow', 'blockenberg'),
317 value: a.shadowSize,
318 min: 0, max: 5,
319 __nextHasNoMarginBottom: true,
320 onChange: function (v) { setAttributes({ shadowSize: v }); }
321 }),
322 el(ToggleControl, {
323 label: __('Inner Border Frame', 'blockenberg'),
324 checked: a.innerBorder,
325 __nextHasNoMarginBottom: true,
326 onChange: function (v) { setAttributes({ innerBorder: v }); }
327 }),
328 el(ToggleControl, {
329 label: __('Show Badge / Emoji', 'blockenberg'),
330 checked: a.showBadge,
331 __nextHasNoMarginBottom: true,
332 onChange: function (v) { setAttributes({ showBadge: v }); }
333 }),
334 a.showBadge && el(TextControl, {
335 label: __('Badge Emoji', 'blockenberg'),
336 value: a.badgeEmoji,
337 __nextHasNoMarginBottom: true,
338 onChange: function (v) { setAttributes({ badgeEmoji: v }); }
339 })
340 ),
341 el(PanelBody, { title: __('Content', 'blockenberg'), initialOpen: false },
342 el(TextControl, {
343 label: __('Pre-title (e.g. "Certificate of Completion")', 'blockenberg'),
344 value: a.preTitle,
345 __nextHasNoMarginBottom: true,
346 onChange: function (v) { setAttributes({ preTitle: v }); }
347 }),
348 el(TextControl, {
349 label: __('Awarded-to text', 'blockenberg'),
350 value: a.awardedTo,
351 __nextHasNoMarginBottom: true,
352 onChange: function (v) { setAttributes({ awardedTo: v }); }
353 }),
354 el(TextControl, {
355 label: __('Recipient Name', 'blockenberg'),
356 value: a.recipientName,
357 __nextHasNoMarginBottom: true,
358 onChange: function (v) { setAttributes({ recipientName: v }); }
359 }),
360 el(TextControl, {
361 label: __('Course / Achievement Title', 'blockenberg'),
362 value: a.courseTitle,
363 __nextHasNoMarginBottom: true,
364 onChange: function (v) { setAttributes({ courseTitle: v }); }
365 }),
366 el(TextareaControl, {
367 label: __('Description', 'blockenberg'),
368 value: a.courseDesc,
369 __nextHasNoMarginBottom: true,
370 onChange: function (v) { setAttributes({ courseDesc: v }); }
371 }),
372 el(TextControl, {
373 label: __('Issuer Name', 'blockenberg'),
374 value: a.issuerName,
375 __nextHasNoMarginBottom: true,
376 onChange: function (v) { setAttributes({ issuerName: v }); }
377 }),
378 el(ToggleControl, {
379 label: __('Show Date', 'blockenberg'),
380 checked: a.showDate,
381 __nextHasNoMarginBottom: true,
382 onChange: function (v) { setAttributes({ showDate: v }); }
383 }),
384 a.showDate && el(TextControl, {
385 label: __('Completion Date', 'blockenberg'),
386 value: a.completionDate,
387 __nextHasNoMarginBottom: true,
388 onChange: function (v) { setAttributes({ completionDate: v }); }
389 }),
390 el(ToggleControl, {
391 label: __('Show Credential ID', 'blockenberg'),
392 checked: a.showCredentialId,
393 __nextHasNoMarginBottom: true,
394 onChange: function (v) { setAttributes({ showCredentialId: v }); }
395 }),
396 a.showCredentialId && el(TextControl, {
397 label: __('Credential ID', 'blockenberg'),
398 value: a.credentialId,
399 __nextHasNoMarginBottom: true,
400 onChange: function (v) { setAttributes({ credentialId: v }); }
401 })
402 ),
403 el(PanelBody, { title: __('Issuer Logo', 'blockenberg'), initialOpen: false },
404 a.issuerLogoUrl && el('div', { style: { marginBottom: '8px' } },
405 el('img', { src: a.issuerLogoUrl, style: { maxHeight: '60px', display: 'block', marginBottom: '8px' }, alt: '' }),
406 el(Button, { isDestructive: true, isSmall: true, onClick: function () { setAttributes({ issuerLogoUrl: '', issuerLogoId: 0 }); } }, __('Remove', 'blockenberg'))
407 ),
408 el(MediaUpload, {
409 onSelect: function (media) { setAttributes({ issuerLogoUrl: media.url, issuerLogoId: media.id }); },
410 allowedTypes: ['image'],
411 value: a.issuerLogoId,
412 render: function (rp) {
413 return el(Button, { variant: 'secondary', onClick: rp.open }, a.issuerLogoUrl ? __('Change Logo', 'blockenberg') : __('Upload Logo', 'blockenberg'));
414 }
415 })
416 ),
417 el(PanelBody, { title: __('Signatures', 'blockenberg'), initialOpen: false },
418 el(ToggleControl, {
419 label: __('Signature 1', 'blockenberg'),
420 checked: a.showSignature1,
421 __nextHasNoMarginBottom: true,
422 onChange: function (v) { setAttributes({ showSignature1: v }); }
423 }),
424 a.showSignature1 && el(TextControl, {
425 label: __('Name', 'blockenberg'),
426 value: a.signature1Name,
427 __nextHasNoMarginBottom: true,
428 onChange: function (v) { setAttributes({ signature1Name: v }); }
429 }),
430 a.showSignature1 && el(TextControl, {
431 label: __('Title', 'blockenberg'),
432 value: a.signature1Title,
433 __nextHasNoMarginBottom: true,
434 onChange: function (v) { setAttributes({ signature1Title: v }); }
435 }),
436 el(ToggleControl, {
437 label: __('Signature 2', 'blockenberg'),
438 checked: a.showSignature2,
439 __nextHasNoMarginBottom: true,
440 onChange: function (v) { setAttributes({ showSignature2: v }); }
441 }),
442 a.showSignature2 && el(TextControl, {
443 label: __('Name', 'blockenberg'),
444 value: a.signature2Name,
445 __nextHasNoMarginBottom: true,
446 onChange: function (v) { setAttributes({ signature2Name: v }); }
447 }),
448 a.showSignature2 && el(TextControl, {
449 label: __('Title', 'blockenberg'),
450 value: a.signature2Title,
451 __nextHasNoMarginBottom: true,
452 onChange: function (v) { setAttributes({ signature2Title: v }); }
453 })
454 ),
455
456 el( PanelBody, { title: __( 'Typography', 'blockenberg' ), initialOpen: false },
457 el(SelectControl, {
458 label: __('Font Family', 'blockenberg'),
459 value: a.fontFamily,
460 options: fontOpts,
461 __nextHasNoMarginBottom: true,
462 onChange: function (v) { setAttributes({ fontFamily: v }); }
463 }),
464 el(getTypographyControl(), { label: __('Recipient Name', 'blockenberg'), value: a.typoRecipient, onChange: function (v) { setAttributes({ typoRecipient: v }); } }),
465 el(getTypographyControl(), { label: __('Course Title', 'blockenberg'), value: a.typoCourseTitle, onChange: function (v) { setAttributes({ typoCourseTitle: v }); } }),
466 el(getTypographyControl(), { label: __('Body / Description', 'blockenberg'), value: a.typoBody, onChange: function (v) { setAttributes({ typoBody: v }); } })
467 ),
468 el(PanelColorSettings, {
469 title: __('Colors', 'blockenberg'),
470 initialOpen: false,
471 colorSettings: [
472 { value: a.bgColor, onChange: function (c) { setAttributes({ bgColor: c || '#fffef7' }); }, label: __('Background', 'blockenberg') },
473 { value: a.borderColor, onChange: function (c) { setAttributes({ borderColor: c || '#c9a84c' }); }, label: __('Border / Frame', 'blockenberg') },
474 { value: a.accentColor, onChange: function (c) { setAttributes({ accentColor: c || '#8b6914' }); }, label: __('Accent', 'blockenberg') },
475 { value: a.preTitleColor, onChange: function (c) { setAttributes({ preTitleColor: c || '#8b6914' }); }, label: __('Pre-title', 'blockenberg') },
476 { value: a.recipientColor, onChange: function (c) { setAttributes({ recipientColor: c || '#1e293b' }); }, label: __('Recipient Name', 'blockenberg') },
477 { value: a.courseTitleColor, onChange: function (c) { setAttributes({ courseTitleColor: c || '#1a3a6b' }); }, label: __('Course Title', 'blockenberg') },
478 { value: a.descColor, onChange: function (c) { setAttributes({ descColor: c || '#475569' }); }, label: __('Description', 'blockenberg') },
479 { value: a.issuerColor, onChange: function (c) { setAttributes({ issuerColor: c || '#1e293b' }); }, label: __('Issuer Name', 'blockenberg') },
480 { value: a.metaColor, onChange: function (c) { setAttributes({ metaColor: c || '#64748b' }); }, label: __('Meta / Signature Title', 'blockenberg') }
481 ]
482 })
483 );
484
485 var blockProps = useBlockProps({ className: 'bkbg-editor-wrap', 'data-block-label': 'Certificate' });
486
487 return el('div', blockProps,
488 inspector,
489 el(CertPreview, {
490 a: a,
491 setAttributes: setAttributes,
492 editField: editField,
493 startEdit: startEdit,
494 endEdit: endEdit
495 })
496 );
497 },
498
499 save: function (props) {
500 return el('div', useBlockProps.save(),
501 el('div', { className: 'bkbg-cert-app', 'data-opts': JSON.stringify(props.attributes) })
502 );
503 }
504 });
505 }() );
506