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

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

208 lines 15.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 useState = wp.element.useState;
4 var useEffect = wp.element.useEffect;
5 var useRef = wp.element.useRef;
6 var Fragment = wp.element.Fragment;
7 var registerBlockType = wp.blocks.registerBlockType;
8 var __ = wp.i18n.__;
9 var InspectorControls = wp.blockEditor.InspectorControls;
10 var PanelColorSettings = wp.blockEditor.PanelColorSettings;
11 var useBlockProps = wp.blockEditor.useBlockProps;
12 var PanelBody = wp.components.PanelBody;
13 var RangeControl = wp.components.RangeControl;
14 var TextControl = wp.components.TextControl;
15 var ToggleControl = wp.components.ToggleControl;
16 var Button = wp.components.Button;
17 var ColorPicker = wp.components.ColorPicker;
18 var Popover = wp.components.Popover;
19
20 var _tc; function getTypoControl() { return _tc || (_tc = window.bkbgTypographyControl); }
21 var _tv; function getTypoCssVars() { return _tv || (_tv = window.bkbgTypoCssVars); }
22
23 function parseItems(text) {
24 return String(text||'').split('\n').map(function(s){ return s.trim(); }).filter(Boolean);
25 }
26
27 function drawWheel(canvas, items, colors, textColor, centerColor, rotation) {
28 var ctx = canvas.getContext('2d');
29 var size = canvas.width;
30 var cx = size / 2, cy = size / 2, rad = size / 2 - 4;
31 ctx.clearRect(0, 0, size, size);
32 if (!items.length) return;
33 var slice = (2 * Math.PI) / items.length;
34 var clrs = colors.split(',').map(function(c){ return c.trim(); });
35 items.forEach(function(item, i) {
36 var start = rotation + i * slice;
37 var end = start + slice;
38 var color = clrs[i % clrs.length];
39 ctx.beginPath();
40 ctx.moveTo(cx, cy);
41 ctx.arc(cx, cy, rad, start, end);
42 ctx.closePath();
43 ctx.fillStyle = color;
44 ctx.fill();
45 ctx.strokeStyle = 'rgba(255,255,255,0.3)';
46 ctx.lineWidth = 2;
47 ctx.stroke();
48 // text
49 ctx.save();
50 ctx.translate(cx, cy);
51 ctx.rotate(start + slice / 2);
52 ctx.textAlign = 'right';
53 ctx.fillStyle = textColor || '#ffffff';
54 ctx.font = 'bold ' + Math.min(15, Math.max(9, Math.floor(rad * 0.13))) + 'px -apple-system,sans-serif';
55 var maxLen = Math.floor(rad * 0.55);
56 var label = item.length > 14 ? item.slice(0,13)+'' : item;
57 ctx.fillText(label, rad - 12, 5);
58 ctx.restore();
59 });
60 // center circle
61 ctx.beginPath();
62 ctx.arc(cx, cy, rad * 0.12, 0, 2*Math.PI);
63 ctx.fillStyle = centerColor || '#ffffff';
64 ctx.fill();
65 ctx.strokeStyle = 'rgba(0,0,0,0.1)';
66 ctx.lineWidth = 2;
67 ctx.stroke();
68 }
69
70 function WheelPreview(props) {
71 var a = props.attributes;
72 var sa = props.setAttributes;
73 var canvasRef = useRef(null);
74 var items = parseItems(a.defaultItems);
75 var size = Math.min(a.wheelSize || 340, 340);
76
77 useEffect(function() {
78 if (canvasRef.current) {
79 drawWheel(canvasRef.current, items, a.colors||'#6c3fb5,#10b981,#f59e0b,#3b82f6,#ef4444,#8b5cf6', a.textColor, a.centerColor, 0);
80 }
81 }, [a.defaultItems, a.colors, a.textColor, a.centerColor, a.wheelSize]);
82
83 return el('div', { style: { paddingTop: a.paddingTop+'px', paddingBottom: a.paddingBottom+'px', background: a.sectionBg||undefined } },
84 el('div', { style: { background: a.cardBg, borderRadius: a.cardRadius+'px', padding: '36px 28px', maxWidth: a.maxWidth+'px', margin: '0 auto', boxShadow: '0 4px 24px rgba(0,0,0,0.09)', textAlign: 'center' } },
85
86 (a.showTitle||a.showSubtitle) && el('div', { style:{ marginBottom:'20px' }},
87 a.showTitle && el('div', { className: 'bkbg-sw-title', style:{ color:a.titleColor,marginBottom:'6px' }}, a.title),
88 a.showSubtitle && el('div', { className: 'bkbg-sw-subtitle', style:{ color:a.subtitleColor }}, a.subtitle)
89 ),
90
91 // Textarea
92 el('div', { style:{ marginBottom:'18px', textAlign:'left' }},
93 el('label', { style:{ fontSize:'12px',fontWeight:600,textTransform:'uppercase',letterSpacing:'.04em',color:a.labelColor,display:'block',marginBottom:'5px' }}, 'Items (' + items.length + ')'),
94 el('textarea', { rows: 5, value: a.defaultItems, style:{ width:'100%',padding:'9px 12px',border:'1.5px solid #e5e7eb',borderRadius:'8px',fontSize:'14px',fontFamily:'monospace',resize:'vertical',outline:'none',boxSizing:'border-box' },
95 onChange: function(e){ sa({ defaultItems: e.target.value }); }
96 })
97 ),
98
99 // Wheel + pointer
100 el('div', { style:{ position:'relative', display:'inline-block', marginBottom:'20px' }},
101 // pointer triangle
102 el('div', { style:{ position:'absolute', top:'-10px', left:'50%', transform:'translateX(-50%)', width:0, height:0, borderLeft:'12px solid transparent', borderRight:'12px solid transparent', borderTop:'22px solid '+(a.pointerColor||'#1f2937'), zIndex:10, filter:'drop-shadow(0 2px 4px rgba(0,0,0,0.2))' }}),
103 el('canvas', { ref: canvasRef, width: size, height: size, style:{ display:'block', maxWidth:'100%' }})
104 ),
105
106 // Spin button
107 el('button', { style:{ display:'block', margin:'0 auto 16px', padding:'13px 48px', borderRadius:(a.btnRadius||100)+'px', border:'none', background:a.btnBg||'#6c3fb5', color:a.btnColor||'#fff', fontWeight:700, fontSize:'16px', cursor:'default', fontFamily:'inherit' }}, a.buttonLabel||'Spin!'),
108
109 // Winner placeholder
110 a.showWinnerBox && el('div', { className: 'bkbg-sw-winner-text', style:{ background:a.winnerBg||'#6c3fb5', borderRadius:'10px', padding:'16px 24px', color:a.winnerColor||'#fff', display:'inline-block', opacity:.25 }}, '🎉 Winner will appear here')
111 )
112 );
113 }
114
115 /* ── BkbgMultiColorControl ──────────────────────────────────────── */
116 function BkbgMultiColorControl(props) {
117 var label = props.label;
118 var value = props.value || '';
119 var onChange = props.onChange;
120 var colors = value.split(',').map(function (c) { return c.trim(); }).filter(Boolean);
121 var st = useState(-1); var openIdx = st[0]; var setOpenIdx = st[1];
122 return el(Fragment, null,
123 el('div', { style: { marginBottom: '8px' } },
124 el('span', { style: { fontSize: '11px', fontWeight: 500, textTransform: 'uppercase', display: 'block', marginBottom: '4px' } }, label),
125 el('div', { style: { display: 'flex', flexWrap: 'wrap', gap: '6px', alignItems: 'center' } },
126 colors.map(function (c, i) {
127 return el('div', { key: i, style: { position: 'relative', display: 'inline-flex' } },
128 el('button', { type: 'button', style: { width: 28, height: 28, borderRadius: 4, border: '1px solid #ccc', background: c, cursor: 'pointer', padding: 0 }, onClick: function () { setOpenIdx(openIdx === i ? -1 : i); } }),
129 el('button', { type: 'button', 'aria-label': 'Remove', style: { position: 'absolute', top: -6, right: -6, width: 14, height: 14, borderRadius: '50%', border: 'none', background: '#d00', color: '#fff', fontSize: '10px', lineHeight: '14px', cursor: 'pointer', padding: 0, textAlign: 'center' }, onClick: function () { var n = colors.slice(); n.splice(i, 1); onChange(n.join(',')); } }, '×'),
130 openIdx === i && el(Popover, { onClose: function () { setOpenIdx(-1); } }, el('div', { style: { padding: '8px' } }, el(ColorPicker, { color: c, enableAlpha: true, onChange: function (v) { var n = colors.slice(); n[i] = v; onChange(n.join(',')); } })))
131 );
132 }),
133 el(Button, { isSmall: true, variant: 'secondary', onClick: function () { onChange(value ? value + ',#6c3fb5' : '#6c3fb5'); }, style: { height: 28, minWidth: 28 } }, '+')
134 )
135 )
136 );
137 }
138
139 registerBlockType('blockenberg/spin-wheel', {
140 edit: function(props) {
141 var a = props.attributes; var set = props.setAttributes;
142 var blockProps = useBlockProps((function () {
143 var s = {};
144 var tv = getTypoCssVars();
145 if (tv) {
146 Object.assign(s, tv(a.titleTypo, '--bksw-tt-'));
147 Object.assign(s, tv(a.subtitleTypo, '--bksw-st-'));
148 Object.assign(s, tv(a.winnerTypo, '--bksw-wn-'));
149 }
150 return { style: s };
151 })());
152 var colorSettings = [
153 { value: a.btnBg, onChange: function(v){ set({btnBg:v}); }, label: 'Spin Button Background' },
154 { value: a.btnColor, onChange: function(v){ set({btnColor:v}); }, label: 'Spin Button Text' },
155 { value: a.pointerColor, onChange: function(v){ set({pointerColor:v}); }, label: 'Pointer Color' },
156 { value: a.textColor, onChange: function(v){ set({textColor:v}); }, label: 'Wheel Item Text' },
157 { value: a.centerColor, onChange: function(v){ set({centerColor:v}); }, label: 'Center Circle Color' },
158 { value: a.cardBg, onChange: function(v){ set({cardBg:v}); }, label: 'Card Background' },
159 { value: a.winnerBg, onChange: function(v){ set({winnerBg:v}); }, label: 'Winner Box Background' },
160 { value: a.winnerColor, onChange: function(v){ set({winnerColor:v}); }, label: 'Winner Box Text' },
161 { value: a.historyBg, onChange: function(v){ set({historyBg:v}); }, label: 'History Background' },
162 { value: a.historyColor, onChange: function(v){ set({historyColor:v}); }, label: 'History Text' },
163 { value: a.labelColor, onChange: function(v){ set({labelColor:v}); }, label: 'Label Color' },
164 { value: a.titleColor, onChange: function(v){ set({titleColor:v}); }, label: 'Title Color' },
165 { value: a.subtitleColor,onChange: function(v){ set({subtitleColor:v}); }, label: 'Subtitle Color' },
166 { value: a.sectionBg, onChange: function(v){ set({sectionBg:v}); }, label: 'Section Background' }
167 ];
168 return el(Fragment, null,
169 el(InspectorControls, null,
170 el(PanelBody, { title: 'Header', initialOpen: false },
171 el(ToggleControl, { __nextHasNoMarginBottom: true, label: 'Show Title', checked: a.showTitle, onChange: function(v){ set({showTitle:v}); } }),
172 el(ToggleControl, { __nextHasNoMarginBottom: true, label: 'Show Subtitle', checked: a.showSubtitle, onChange: function(v){ set({showSubtitle:v}); } }),
173 el(TextControl, { label: 'Title', value: a.title, onChange: function(v){ set({title:v}); } }),
174 el(TextControl, { label: 'Subtitle', value: a.subtitle, onChange: function(v){ set({subtitle:v}); } })
175 ),
176 el(PanelBody, { title: 'Wheel Settings', initialOpen: true },
177 el(TextControl, { label: 'Button Label', value: a.buttonLabel, onChange: function(v){ set({buttonLabel:v}); } }),
178 el(BkbgMultiColorControl, { label: 'Segment Colors (comma-separated hex)', value: a.colors, onChange: function(v){ set({colors:v}); } }),
179 el(RangeControl, { label: 'Spin Duration (sec)', value: a.spinDuration, min: 1, max: 10, step: 0.5, onChange: function(v){ set({spinDuration:v}); } }),
180 el(ToggleControl, { __nextHasNoMarginBottom: true, label: 'Show Winner Box', checked: a.showWinnerBox, onChange: function(v){ set({showWinnerBox:v}); } }),
181 el(ToggleControl, { __nextHasNoMarginBottom: true, label: 'Show Spin History',checked: a.showHistory, onChange: function(v){ set({showHistory:v}); } })
182 ),
183
184 el( PanelBody, { title: __( 'Typography', 'blockenberg' ), initialOpen: false },
185 getTypoControl() ? el(getTypoControl(), { label: __('Title Typography', 'blockenberg'), value: a.titleTypo || {}, onChange: function (v) { set({ titleTypo: v }); } }) : null,
186 getTypoControl() ? el(getTypoControl(), { label: __('Subtitle Typography', 'blockenberg'), value: a.subtitleTypo || {}, onChange: function (v) { set({ subtitleTypo: v }); } }) : null,
187 getTypoControl() ? el(getTypoControl(), { label: __('Winner Typography', 'blockenberg'), value: a.winnerTypo || {}, onChange: function (v) { set({ winnerTypo: v }); } }) : null
188 ),
189 el(PanelColorSettings, { title: 'Colors', initialOpen: false, colorSettings: colorSettings }),
190 el(PanelBody, { title: 'Sizing & Layout', initialOpen: false },
191 el(RangeControl, { label: 'Wheel Size (px)', value: a.wheelSize, min: 180,max: 500, step: 10, onChange: function(v){ set({wheelSize:v}); } }),
192 el(RangeControl, { label: 'Card Border Radius', value: a.cardRadius, min: 0, max: 48, step: 1, onChange: function(v){ set({cardRadius:v}); } }),
193 el(RangeControl, { label: 'Button Radius', value: a.btnRadius, min: 0, max: 100, step: 2, onChange: function(v){ set({btnRadius:v}); } }),
194 el(RangeControl, { label: 'Max Width (px)', value: a.maxWidth, min: 300,max: 900, step: 10, onChange: function(v){ set({maxWidth:v}); } }),
195 el(RangeControl, { label: 'Padding Top (px)', value: a.paddingTop, min: 0, max: 160, step: 4, onChange: function(v){ set({paddingTop:v}); } }),
196 el(RangeControl, { label: 'Padding Bottom (px)',value: a.paddingBottom, min: 0, max: 160, step: 4, onChange: function(v){ set({paddingBottom:v}); } })
197 )
198 ),
199 el('div', blockProps, el(WheelPreview, { attributes: a, setAttributes: set }))
200 );
201 },
202 save: function(props) {
203 var a = props.attributes;
204 return el('div', wp.blockEditor.useBlockProps.save(), el('div', { className: 'bkbg-sw-app', 'data-opts': JSON.stringify({ titleTypo: a.titleTypo, subtitleTypo: a.subtitleTypo, winnerTypo: a.winnerTypo, title: a.title, subtitle: a.subtitle, showTitle: a.showTitle, showSubtitle: a.showSubtitle, defaultItems: a.defaultItems, buttonLabel: a.buttonLabel, showWinnerBox: a.showWinnerBox, showHistory: a.showHistory, spinDuration: a.spinDuration, wheelSize: a.wheelSize, colors: a.colors, textColor: a.textColor, pointerColor: a.pointerColor, centerColor: a.centerColor, cardBg: a.cardBg, winnerBg: a.winnerBg, winnerColor: a.winnerColor, historyBg: a.historyBg, historyColor: a.historyColor, btnBg: a.btnBg, btnColor: a.btnColor, labelColor: a.labelColor, titleColor: a.titleColor, subtitleColor: a.subtitleColor, sectionBg: a.sectionBg, cardRadius: a.cardRadius, btnRadius: a.btnRadius, maxWidth: a.maxWidth, paddingTop: a.paddingTop, paddingBottom: a.paddingBottom }) }));
205 }
206 });
207 }() );
208