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 / coupon / index.js

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

502 lines 28.1 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 registerBlockType = wp.blocks.registerBlockType;
6 var __ = wp.i18n.__;
7 var InspectorControls = wp.blockEditor.InspectorControls;
8 var PanelColorSettings = wp.blockEditor.PanelColorSettings;
9 var useBlockProps = wp.blockEditor.useBlockProps;
10 var PanelBody = wp.components.PanelBody;
11 var TextControl = wp.components.TextControl;
12 var TextareaControl = wp.components.TextareaControl;
13 var ToggleControl = wp.components.ToggleControl;
14 var RangeControl = wp.components.RangeControl;
15 var SelectControl = wp.components.SelectControl;
16 var Button = wp.components.Button;
17
18 var _cpTC, _cpTV;
19 function _tc() { return _cpTC || (_cpTC = window.bkbgTypographyControl); }
20 function _tv(obj, prefix) { var fn = _cpTV || (_cpTV = window.bkbgTypoCssVars); return fn ? fn(obj, prefix) : {}; }
21
22 var STYLE_OPTIONS = [
23 { label: __('Classic (strip)', 'blockenberg'), value: 'classic' },
24 { label: __('Card (flat)', 'blockenberg'), value: 'card' },
25 { label: __('Minimal', 'blockenberg'), value: 'minimal' },
26 { label: __('Ribbon (accent)', 'blockenberg'), value: 'ribbon' },
27 ];
28
29 var DISCOUNT_TYPES = [
30 { label: __('Percentage (%)', 'blockenberg'), value: 'percent' },
31 { label: __('Fixed amount', 'blockenberg'), value: 'fixed' },
32 { label: __('Free shipping', 'blockenberg'), value: 'shipping' },
33 { label: __('Buy one get one','blockenberg'), value: 'bogo' },
34 { label: __('Custom label', 'blockenberg'), value: 'custom' },
35 ];
36
37 var BADGE_SHAPES = [
38 { label: __('Circle', 'blockenberg'), value: 'circle' },
39 { label: __('Rounded', 'blockenberg'), value: 'rounded' },
40 { label: __('Pill', 'blockenberg'), value: 'pill' },
41 ];
42
43 var BADGE_POSITIONS = [
44 { label: __('Top right', 'blockenberg'), value: 'top-right' },
45 { label: __('Top left', 'blockenberg'), value: 'top-left' },
46 { label: __('Inline', 'blockenberg'), value: 'inline' },
47 ];
48
49 var STRIP_SIDES = [
50 { label: __('Left', 'blockenberg'), value: 'left' },
51 { label: __('Top', 'blockenberg'), value: 'top' },
52 { label: __('Right', 'blockenberg'), value: 'right' },
53 { label: __('Bottom', 'blockenberg'), value: 'bottom' },
54 ];
55
56 function badgeLabel(a) {
57 if (a.discountType === 'percent') return a.discountValue + '%\nOFF';
58 if (a.discountType === 'fixed') return '$' + a.discountValue + '\nOFF';
59 if (a.discountType === 'shipping') return 'FREE\nSHIPPING';
60 if (a.discountType === 'bogo') return 'BOGO';
61 return a.discountValue;
62 }
63
64 function CouponPreview(props) {
65 var a = props.attributes;
66 var lines = badgeLabel(a).split('\n');
67
68 var wrapStyle = {
69 position: 'relative',
70 background: a.bgColor,
71 color: a.textColor,
72 borderRadius: a.borderRadius + 'px',
73 maxWidth: a.maxWidth + 'px',
74 overflow: 'hidden',
75 boxShadow: '0 4px 24px rgba(0,0,0,0.10)',
76 };
77
78 // Strip
79 var stripStyle = { position: 'absolute', background: a.accentColor };
80 if (a.style === 'classic') {
81 if (a.stripSide === 'left') Object.assign(stripStyle, { top: 0, left: 0, width: '8px', height: '100%' });
82 if (a.stripSide === 'right') Object.assign(stripStyle, { top: 0, right: 0, width: '8px', height: '100%' });
83 if (a.stripSide === 'top') Object.assign(stripStyle, { top: 0, left: 0, height: '8px', width: '100%' });
84 if (a.stripSide === 'bottom') Object.assign(stripStyle, { bottom: 0, left: 0, height: '8px', width: '100%' });
85 }
86
87 var bodyPad = a.style === 'classic'
88 ? (a.stripSide === 'left' ? '28px 28px 28px 40px' : a.stripSide === 'right' ? '28px 40px 28px 28px' : '28px')
89 : '28px';
90
91 if (a.style === 'minimal') {
92 wrapStyle.boxShadow = 'none';
93 wrapStyle.border = '2px dashed ' + a.accentColor;
94 }
95
96 if (a.style === 'ribbon') {
97 wrapStyle.borderTop = '6px solid ' + a.accentColor;
98 }
99
100 return el('div', { className: 'bkcp-coupon bkcp-style-' + a.style, style: wrapStyle },
101 a.style === 'classic' && el('div', { className: 'bkcp-strip', style: stripStyle }),
102
103 // Badge
104 a.showBadge && a.badgePosition !== 'inline' && el('div', {
105 className: 'bkcp-badge bkcp-badge-' + a.badgeShape + ' bkcp-badge-' + a.badgePosition,
106 style: {
107 position: 'absolute',
108 background: a.badgeBg,
109 color: a.badgeColor,
110 display: 'flex',
111 flexDirection: 'column',
112 alignItems: 'center',
113 justifyContent: 'center',
114 textAlign: 'center',
115 fontWeight: 800,
116 borderRadius: a.badgeShape === 'circle' ? '50%' : a.badgeShape === 'pill' ? '999px' : '10px',
117 width: a.badgeShape === 'circle' ? '72px' : 'auto',
118 height: a.badgeShape === 'circle' ? '72px' : 'auto',
119 padding: a.badgeShape === 'circle' ? '0' : '8px 14px',
120 top: a.badgePosition === 'top-right' || a.badgePosition === 'top-left' ? '16px' : undefined,
121 right: a.badgePosition === 'top-right' ? '16px' : undefined,
122 left: a.badgePosition === 'top-left' ? '16px' : undefined,
123 lineHeight: 1.1,
124 fontSize: a.badgeShape === 'circle' ? '13px' : '15px',
125 }
126 }, lines.map(function (l, i) { return el('span', { key: i, style: { display: 'block', fontSize: i === 0 ? '22px' : '11px' } }, l); })),
127
128 // Body
129 el('div', { className: 'bkcp-body', style: { padding: bodyPad } },
130 // Inline badge
131 a.showBadge && a.badgePosition === 'inline' && el('div', {
132 style: {
133 display: 'inline-block',
134 background: a.badgeBg,
135 color: a.badgeColor,
136 borderRadius: a.badgeShape === 'circle' ? '50%' : a.badgeShape === 'pill' ? '999px' : '8px',
137 padding: '6px 14px',
138 fontWeight: 800,
139 fontSize: '14px',
140 marginBottom: '12px',
141 }
142 }, lines.join(' ')),
143
144 el('h3', {
145 className: 'bkcp-title',
146 style: { margin: '0 0 8px', color: a.textColor }
147 }, a.title),
148
149 el('p', {
150 className: 'bkcp-desc',
151 style: { margin: '0 0 20px', color: a.descColor }
152 }, a.description),
153
154 // Code display
155 a.showCode && el('div', { className: 'bkcp-code-row', style: { display: 'flex', alignItems: 'center', gap: '10px', marginBottom: '20px' } },
156 el('div', { className: 'bkcp-code-wrap', style: {
157 flex: 1,
158 background: a.codeBg,
159 border: '2px dashed ' + a.codeColor,
160 borderRadius: '8px',
161 padding: '10px 16px',
162 }},
163 el('span', { className: 'bkcp-code-label', style: { display: 'block', fontSize: '11px', textTransform: 'uppercase', letterSpacing: '1px', color: a.descColor, marginBottom: '2px' } }, a.codeLabel),
164 el('span', { className: 'bkcp-code', style: { fontFamily: 'monospace', fontSize: (a.codeFontSize || 22) + 'px', fontWeight: 800, letterSpacing: '3px', color: a.codeColor } }, a.code)
165 ),
166 el('button', {
167 className: 'bkcp-copy-btn',
168 type: 'button',
169 style: {
170 background: a.accentColor,
171 color: '#fff',
172 border: 'none',
173 borderRadius: '8px',
174 padding: '10px 18px',
175 fontWeight: 700,
176 fontSize: '14px',
177 cursor: 'pointer',
178 whiteSpace: 'nowrap',
179 }
180 }, a.copyLabel)
181 ),
182
183 // Expiry
184 a.showExpiry && el('div', { className: 'bkcp-expiry', style: { fontSize: '13px', color: a.descColor, marginBottom: a.showButton ? '16px' : '0' } },
185 el('span', null, a.expiryLabel + ' '),
186 a.expiryDate && el('strong', { style: { color: a.textColor } }, a.expiryDate),
187 a.showCountdown && el('span', { className: 'bkcp-countdown', 'data-expiry': a.expiryDate, style: { marginLeft: '8px', fontWeight: 700, color: a.accentColor } })
188 ),
189
190 // Button
191 a.showButton && el('a', {
192 href: '#',
193 className: 'bkcp-btn',
194 style: {
195 display: 'inline-block',
196 background: a.btnBg,
197 color: a.btnColor,
198 padding: '13px 28px',
199 borderRadius: '8px',
200 fontWeight: 700,
201 fontSize: (a.btnFontSize || 16) + 'px',
202 textDecoration: 'none',
203 marginTop: '4px',
204 }
205 }, a.buttonText),
206
207 // Note
208 a.showNote && a.note && el('p', {
209 className: 'bkcp-note',
210 style: { margin: '12px 0 0', fontSize: '12px', color: a.descColor }
211 }, a.note)
212 )
213 );
214 }
215
216 registerBlockType('blockenberg/coupon', {
217 title: __('Coupon', 'blockenberg'),
218 description: __('Promo/discount coupon card with copyable code & expiry.', 'blockenberg'),
219 category: 'bkbg-marketing',
220 icon: 'tickets-alt',
221
222 edit: function (props) {
223 var attributes = props.attributes;
224 var setAttributes = props.setAttributes;
225 var blockProps = useBlockProps({ className: 'bkcp-editor-wrap', style: Object.assign({ display: 'flex', justifyContent: 'flex-start' }, _tv(attributes.typoTitle, '--bkcp-ttl-'), _tv(attributes.typoDesc, '--bkcp-dsc-')) });
226
227 return el(Fragment, null,
228 el(InspectorControls, null,
229 /* ── Content ──────────────────────────────────────────── */
230 el(PanelBody, { title: __('Content', 'blockenberg'), initialOpen: true },
231 el(TextControl, {
232 label: __('Title', 'blockenberg'),
233 value: attributes.title,
234 onChange: function (v) { setAttributes({ title: v }); },
235 }),
236 el(TextareaControl, {
237 label: __('Description', 'blockenberg'),
238 value: attributes.description,
239 rows: 3,
240 onChange: function (v) { setAttributes({ description: v }); },
241 }),
242 el(SelectControl, {
243 label: __('Discount type', 'blockenberg'),
244 value: attributes.discountType,
245 options: DISCOUNT_TYPES,
246 onChange: function (v) { setAttributes({ discountType: v }); },
247 }),
248 el(TextControl, {
249 label: __('Discount value / label', 'blockenberg'),
250 value: attributes.discountValue,
251 onChange: function (v) { setAttributes({ discountValue: v }); },
252 })
253 ),
254 /* ── Coupon Code ─────────────────────────────────────── */
255 el(PanelBody, { title: __('Coupon Code', 'blockenberg'), initialOpen: true },
256 el(ToggleControl, {
257 label: __('Show code', 'blockenberg'),
258 checked: attributes.showCode,
259 onChange: function (v) { setAttributes({ showCode: v }); },
260 __nextHasNoMarginBottom: true,
261 }),
262 attributes.showCode && el(TextControl, {
263 label: __('Code', 'blockenberg'),
264 value: attributes.code,
265 onChange: function (v) { setAttributes({ code: v }); },
266 }),
267 attributes.showCode && el(TextControl, {
268 label: __('Code label', 'blockenberg'),
269 value: attributes.codeLabel,
270 onChange: function (v) { setAttributes({ codeLabel: v }); },
271 }),
272 attributes.showCode && el(TextControl, {
273 label: __('Copy button label', 'blockenberg'),
274 value: attributes.copyLabel,
275 onChange: function (v) { setAttributes({ copyLabel: v }); },
276 }),
277 attributes.showCode && el(TextControl, {
278 label: __('Copied! label', 'blockenberg'),
279 value: attributes.copiedLabel,
280 onChange: function (v) { setAttributes({ copiedLabel: v }); },
281 })
282 ),
283 /* ── Expiry ───────────────────────────────────────────── */
284 el(PanelBody, { title: __('Expiry', 'blockenberg'), initialOpen: false },
285 el(ToggleControl, {
286 label: __('Show expiry', 'blockenberg'),
287 checked: attributes.showExpiry,
288 onChange: function (v) { setAttributes({ showExpiry: v }); },
289 __nextHasNoMarginBottom: true,
290 }),
291 attributes.showExpiry && el(TextControl, {
292 label: __('Expiry date', 'blockenberg'),
293 help: __('Format: YYYY-MM-DD or any readable date', 'blockenberg'),
294 value: attributes.expiryDate,
295 onChange: function (v) { setAttributes({ expiryDate: v }); },
296 }),
297 attributes.showExpiry && el(TextControl, {
298 label: __('Expiry label', 'blockenberg'),
299 value: attributes.expiryLabel,
300 onChange: function (v) { setAttributes({ expiryLabel: v }); },
301 }),
302 attributes.showExpiry && el(ToggleControl, {
303 label: __('Show live countdown', 'blockenberg'),
304 checked: attributes.showCountdown,
305 onChange: function (v) { setAttributes({ showCountdown: v }); },
306 __nextHasNoMarginBottom: true,
307 })
308 ),
309 /* ── CTA Button ───────────────────────────────────────── */
310 el(PanelBody, { title: __('CTA Button', 'blockenberg'), initialOpen: false },
311 el(ToggleControl, {
312 label: __('Show button', 'blockenberg'),
313 checked: attributes.showButton,
314 onChange: function (v) { setAttributes({ showButton: v }); },
315 __nextHasNoMarginBottom: true,
316 }),
317 attributes.showButton && el(TextControl, {
318 label: __('Button text', 'blockenberg'),
319 value: attributes.buttonText,
320 onChange: function (v) { setAttributes({ buttonText: v }); },
321 }),
322 attributes.showButton && el(TextControl, {
323 label: __('Button URL', 'blockenberg'),
324 value: attributes.buttonUrl,
325 type: 'url',
326 onChange: function (v) { setAttributes({ buttonUrl: v }); },
327 }),
328 attributes.showButton && el(ToggleControl, {
329 label: __('Open in new tab', 'blockenberg'),
330 checked: attributes.buttonNewTab,
331 onChange: function (v) { setAttributes({ buttonNewTab: v }); },
332 __nextHasNoMarginBottom: true,
333 }),
334 el(ToggleControl, {
335 label: __('Show fine print / note', 'blockenberg'),
336 checked: attributes.showNote,
337 onChange: function (v) { setAttributes({ showNote: v }); },
338 __nextHasNoMarginBottom: true,
339 }),
340 attributes.showNote && el(TextControl, {
341 label: __('Note text', 'blockenberg'),
342 value: attributes.note,
343 onChange: function (v) { setAttributes({ note: v }); },
344 })
345 ),
346 /* ── Badge ────────────────────────────────────────────── */
347 el(PanelBody, { title: __('Badge', 'blockenberg'), initialOpen: false },
348 el(ToggleControl, {
349 label: __('Show badge', 'blockenberg'),
350 checked: attributes.showBadge,
351 onChange: function (v) { setAttributes({ showBadge: v }); },
352 __nextHasNoMarginBottom: true,
353 }),
354 attributes.showBadge && el(SelectControl, {
355 label: __('Badge shape', 'blockenberg'),
356 value: attributes.badgeShape,
357 options: BADGE_SHAPES,
358 onChange: function (v) { setAttributes({ badgeShape: v }); },
359 }),
360 attributes.showBadge && el(SelectControl, {
361 label: __('Badge position', 'blockenberg'),
362 value: attributes.badgePosition,
363 options: BADGE_POSITIONS,
364 onChange: function (v) { setAttributes({ badgePosition: v }); },
365 })
366 ),
367 /* ── Appearance ───────────────────────────────────────── */
368 el(PanelBody, { title: __('Appearance', 'blockenberg'), initialOpen: false },
369 el(SelectControl, {
370 label: __('Card style', 'blockenberg'),
371 value: attributes.style,
372 options: STYLE_OPTIONS,
373 onChange: function (v) { setAttributes({ style: v }); },
374 }),
375 attributes.style === 'classic' && el(SelectControl, {
376 label: __('Accent strip side', 'blockenberg'),
377 value: attributes.stripSide,
378 options: STRIP_SIDES,
379 onChange: function (v) { setAttributes({ stripSide: v }); },
380 }),
381 el(RangeControl, {
382 label: __('Border radius (px)', 'blockenberg'),
383 value: attributes.borderRadius,
384 min: 0, max: 40,
385 onChange: function (v) { setAttributes({ borderRadius: v }); },
386 }),
387 el(RangeControl, {
388 label: __('Max width (px)', 'blockenberg'),
389 value: attributes.maxWidth,
390 min: 280, max: 900,
391 onChange: function (v) { setAttributes({ maxWidth: v }); },
392 })
393 ),
394 /* ── Colors ───────────────────────────────────────────── */
395 el(PanelColorSettings, {
396 title: __('Colors', 'blockenberg'),
397 initialOpen: false,
398 colorSettings: [
399 { value: attributes.accentColor, onChange: function (v) { setAttributes({ accentColor: v || '#6c3fb5' }); }, label: __('Accent / strip', 'blockenberg') },
400 { value: attributes.bgColor, onChange: function (v) { setAttributes({ bgColor: v || '#ffffff' }); }, label: __('Card background', 'blockenberg') },
401 { value: attributes.textColor, onChange: function (v) { setAttributes({ textColor: v || '#1f2937' }); }, label: __('Title text', 'blockenberg') },
402 { value: attributes.descColor, onChange: function (v) { setAttributes({ descColor: v || '#6b7280' }); }, label: __('Description text', 'blockenberg') },
403 { value: attributes.badgeBg, onChange: function (v) { setAttributes({ badgeBg: v || '#6c3fb5' }); }, label: __('Badge background', 'blockenberg') },
404 { value: attributes.badgeColor, onChange: function (v) { setAttributes({ badgeColor: v || '#ffffff' }); }, label: __('Badge text', 'blockenberg') },
405 { value: attributes.codeBg, onChange: function (v) { setAttributes({ codeBg: v || '#f5f0ff' }); }, label: __('Code background', 'blockenberg') },
406 { value: attributes.codeColor, onChange: function (v) { setAttributes({ codeColor: v || '#6c3fb5' }); }, label: __('Code text', 'blockenberg') },
407 { value: attributes.btnBg, onChange: function (v) { setAttributes({ btnBg: v || '#6c3fb5' }); }, label: __('Button background', 'blockenberg') },
408 { value: attributes.btnColor, onChange: function (v) { setAttributes({ btnColor: v || '#ffffff' }); }, label: __('Button text', 'blockenberg') },
409 ],
410 }),
411 /* ── Typography ────────────────────────────────────────── */
412 el(PanelBody, { title: __('Typography', 'blockenberg'), initialOpen: false },
413 _tc() && el(_tc(), { label: __('Title', 'blockenberg'), value: attributes.typoTitle, onChange: function (v) { setAttributes({ typoTitle: v }); } }),
414 _tc() && el(_tc(), { label: __('Description', 'blockenberg'), value: attributes.typoDesc, onChange: function (v) { setAttributes({ typoDesc: v }); } }),
415 el(RangeControl, { label: __('Code font size (px)', 'blockenberg'), value: attributes.codeFontSize, min: 14, max: 36, onChange: function (v) { setAttributes({ codeFontSize: v }); } }),
416 el(RangeControl, { label: __('Button font size (px)', 'blockenberg'), value: attributes.btnFontSize, min: 11, max: 24, onChange: function (v) { setAttributes({ btnFontSize: v }); } })
417 )
418 ),
419 el('div', blockProps,
420 el(CouponPreview, { attributes: attributes })
421 )
422 );
423 },
424
425 save: function (props) {
426 var a = props.attributes;
427 var lines = badgeLabel(a).split('\n');
428
429 var wrapStyle = Object.assign({
430 '--bkcp-accent': a.accentColor,
431 '--bkcp-bg': a.bgColor,
432 '--bkcp-text': a.textColor,
433 '--bkcp-desc': a.descColor,
434 '--bkcp-badge-bg': a.badgeBg,
435 '--bkcp-badge-c': a.badgeColor,
436 '--bkcp-code-bg': a.codeBg,
437 '--bkcp-code-c': a.codeColor,
438 '--bkcp-btn-bg': a.btnBg,
439 '--bkcp-btn-c': a.btnColor,
440 '--bkcp-radius': a.borderRadius + 'px',
441 maxWidth: a.maxWidth + 'px',
442 }, _tv(a.typoTitle, '--bkcp-ttl-'), _tv(a.typoDesc, '--bkcp-dsc-'));
443
444 return el('div', {
445 className: 'bkcp-coupon bkcp-style-' + a.style + ' bkcp-strip-' + a.stripSide,
446 style: wrapStyle,
447 'data-copied-label': a.copiedLabel,
448 },
449 a.style === 'classic' && el('div', { className: 'bkcp-strip', 'aria-hidden': 'true' }),
450
451 a.showBadge && a.badgePosition !== 'inline' && el('div', {
452 className: 'bkcp-badge bkcp-badge-' + a.badgeShape + ' bkcp-badge-pos-' + a.badgePosition,
453 'aria-hidden': 'true',
454 },
455 lines.map(function (l, i) { return el('span', { key: i, 'data-line': i }, l); })
456 ),
457
458 el('div', { className: 'bkcp-body' },
459 a.showBadge && a.badgePosition === 'inline' && el('div', {
460 className: 'bkcp-badge-inline bkcp-badge-' + a.badgeShape,
461 'aria-hidden': 'true',
462 }, lines.join(' ')),
463
464 el('h3', { className: 'bkcp-title' }, a.title),
465 el('p', { className: 'bkcp-desc' }, a.description),
466
467 a.showCode && el('div', { className: 'bkcp-code-row' },
468 el('div', { className: 'bkcp-code-wrap' },
469 el('span', { className: 'bkcp-code-label' }, a.codeLabel),
470 el('span', { className: 'bkcp-code', 'data-code': a.code }, a.code)
471 ),
472 el('button', {
473 className: 'bkcp-copy-btn',
474 type: 'button',
475 'data-copy': a.code,
476 'aria-label': a.copyLabel + ' ' + a.code,
477 }, a.copyLabel)
478 ),
479
480 a.showExpiry && el('div', { className: 'bkcp-expiry' },
481 el('span', { className: 'bkcp-expiry-label' }, a.expiryLabel + ' '),
482 a.expiryDate && el('strong', { className: 'bkcp-expiry-date' }, a.expiryDate),
483 a.showCountdown && el('span', {
484 className: 'bkcp-countdown',
485 'data-expiry': a.expiryDate,
486 })
487 ),
488
489 a.showButton && el('a', {
490 href: a.buttonUrl || '#',
491 target: a.buttonNewTab ? '_blank' : undefined,
492 rel: a.buttonNewTab ? 'noopener noreferrer' : undefined,
493 className: 'bkcp-btn',
494 }, a.buttonText),
495
496 a.showNote && a.note && el('p', { className: 'bkcp-note' }, a.note)
497 )
498 );
499 },
500 });
501 }() );
502