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

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

266 lines 18.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /* ====================================================
2 Stats Section Block — editor (index.js)
3 Block: blockenberg/stats-section
4 ==================================================== */
5 ( function () {
6 var el = wp.element.createElement;
7 var Fragment = wp.element.Fragment;
8 var useState = wp.element.useState;
9 var RichText = wp.blockEditor.RichText;
10 var registerBlockType = wp.blocks.registerBlockType;
11 var InspectorControls = wp.blockEditor.InspectorControls;
12 var useBlockProps = wp.blockEditor.useBlockProps;
13 var PanelBody = wp.components.PanelBody;
14 var PanelColorSettings = wp.blockEditor.PanelColorSettings;
15 var RangeControl = wp.components.RangeControl;
16 var SelectControl = wp.components.SelectControl;
17 var ToggleControl = wp.components.ToggleControl;
18 var TextControl = wp.components.TextControl;
19 var Button = wp.components.Button;
20 var __ = wp.i18n.__;
21
22 var _tc; function getTypoControl() { return _tc || (_tc = window.bkbgTypographyControl); }
23 var _tv; function getTypoCssVars() { return _tv || (_tv = window.bkbgTypoCssVars); }
24 var IP = function () { return window.bkbgIconPicker; };
25
26 /* ── helpers ── */
27 function uid() { return 's' + Math.random().toString(36).slice(2, 7); }
28 function move(arr, from, to) { var a = arr.slice(); a.splice(to, 0, a.splice(from, 1)[0]); return a; }
29
30 function buildWrapStyle(a) {
31 var tv = getTypoCssVars();
32 var s = {
33 '--bkbg-sts-num-color': a.numberColor,
34 '--bkbg-sts-label-color':a.labelColor,
35 '--bkbg-sts-desc-color': a.descColor,
36 '--bkbg-sts-icon-color': a.iconColor,
37 '--bkbg-sts-div-color': a.dividerColor,
38 '--bkbg-sts-card-bg': a.cardBg,
39 '--bkbg-sts-card-border':a.cardBorder,
40 '--bkbg-sts-num-size': a.numberSize + 'px',
41 '--bkbg-sts-label-size': a.labelSize + 'px',
42 '--bkbg-sts-desc-size': a.descSize + 'px',
43 '--bkbg-sts-icon-size': a.iconSize + 'px',
44 '--bkbg-sts-weight': a.fontWeight,
45 '--bkbg-sts-lh': a.lineHeight,
46 '--bkbg-sts-radius': a.borderRadius + 'px',
47 '--bkbg-sts-pad': a.cardPadding + 'px',
48 '--bkbg-sts-cols': a.columns,
49 '--bkbg-sts-gap': a.gap + 'px',
50 '--bkbg-sts-pt': a.paddingTop + 'px',
51 '--bkbg-sts-pb': a.paddingBottom + 'px',
52 '--bkbg-sts-grad-from': a.gradientFrom,
53 '--bkbg-sts-grad-to': a.gradientTo,
54 '--bkbg-sts-title-color':a.titleColor,
55 '--bkbg-sts-sub-color': a.subtitleColor,
56 textAlign: a.textAlign,
57 background: a.sectionBg || undefined,
58 };
59 Object.assign(s, tv(a.titleTypo, '--bksts-tt-'));
60 Object.assign(s, tv(a.subtitleTypo, '--bksts-st-'));
61 Object.assign(s, tv(a.numberTypo, '--bksts-nm-'));
62 Object.assign(s, tv(a.labelTypo, '--bksts-lb-'));
63 Object.assign(s, tv(a.descTypo, '--bksts-ds-'));
64 return s;
65 }
66
67 function StatCard(props) {
68 var stat = props.stat;
69 var a = props.a;
70 var isEdit = props.isEdit;
71
72 var iconEl = a.showIcons && stat.icon
73 ? el('span', { className: 'bkbg-sts-icon' }, (stat.iconType || 'custom-char') !== 'custom-char' ? IP().buildEditorIcon(stat.iconType, stat.icon, stat.iconDashicon, stat.iconImageUrl, stat.iconDashiconColor) : stat.icon)
74 : null;
75
76 var numEl = el('span', { className: 'bkbg-sts-number' },
77 stat.prefix ? el('span', { className: 'bkbg-sts-prefix' }, stat.prefix) : null,
78 el('span', { className: 'bkbg-sts-num-value' }, stat.number),
79 stat.suffix ? el('span', { className: 'bkbg-sts-suffix' }, stat.suffix) : null
80 );
81
82 var labelEl = el('span', { className: 'bkbg-sts-label' }, stat.label);
83 var descEl = a.showDescription && stat.description
84 ? el('span', { className: 'bkbg-sts-desc' }, stat.description)
85 : null;
86
87 return el('div', { className: 'bkbg-sts-item' },
88 iconEl, numEl, labelEl, descEl
89 );
90 }
91
92 /* ── REGISTER ── */
93 registerBlockType('blockenberg/stats-section', {
94 edit: function (props) {
95 var a = props.attributes;
96 var setAttr = props.setAttributes;
97 var stats = a.stats;
98
99 var _act = useState(null);
100 var activeId = _act[0], setActiveId = _act[1];
101
102 function updateStat(id, patch) {
103 setAttr({ stats: stats.map(function(s){ return s.id === id ? Object.assign({}, s, patch) : s; }) });
104 }
105
106 var wrapStyle = buildWrapStyle(a);
107 var blockProps = useBlockProps({ className: 'bkbg-sts-wrap bkbg-sts-layout--' + a.layout + ' bkbg-sts-style--' + a.cardStyle, style: wrapStyle });
108
109 var activeItem = stats.find(function(s){ return s.id === activeId; });
110
111 var inspector = el(InspectorControls, null,
112 /* Stats list */
113 el(PanelBody, { title: __('Stats', 'blockenberg'), initialOpen: true },
114 stats.map(function(stat, idx) {
115 var isActive = activeId === stat.id;
116 return el('div', { key: stat.id },
117 el('div', {
118 style: { display:'flex', alignItems:'center', gap:'6px', background: isActive ? '#f3f0ff' : '#f8f9fa', border: isActive ? '1px solid #6c3fb5' : '1px solid #e2e8f0', borderRadius:'6px', padding:'6px 8px', marginBottom:'4px', cursor:'pointer' },
119 onClick: function(){ setActiveId(isActive ? null : stat.id); }
120 },
121 el('span', { style:{ flex:1, fontWeight:600, fontSize:'14px' } }, (stat.icon ? stat.icon + ' ' : '') + stat.number + ' ' + stat.label),
122 el(Button, { icon:'arrow-up', isSmall:true, disabled:idx===0, onClick:function(e){ e.stopPropagation(); setAttr({ stats: move(stats,idx,idx-1) }); } }),
123 el(Button, { icon:'arrow-down', isSmall:true, disabled:idx===stats.length-1, onClick:function(e){ e.stopPropagation(); setAttr({ stats: move(stats,idx,idx+1) }); } }),
124 el(Button, { icon:'no-alt', isSmall:true, isDestructive:true, onClick:function(e){ e.stopPropagation(); setAttr({ stats: stats.filter(function(_,i){ return i!==idx; }) }); if(activeId===stat.id) setActiveId(null); } })
125 ),
126 isActive ? el('div', { style:{ padding:'8px', background:'#faf8ff', borderRadius:'0 0 6px 6px', border:'1px solid #6c3fb5', borderTop:'none', marginBottom:'4px' } },
127 el(IP().IconPickerControl, IP().iconPickerProps(stat, function (patch) { updateStat(stat.id, patch); }, { label: __('Icon', 'blockenberg'), charAttr: 'icon', typeAttr: 'iconType', dashiconAttr: 'iconDashicon', imageUrlAttr: 'iconImageUrl' })),
128 el(TextControl, { label:__('Prefix (e.g. $)', 'blockenberg'), value:stat.prefix, onChange:function(v){ updateStat(stat.id,{prefix:v}); } }),
129 el(TextControl, { label:__('Number / Value', 'blockenberg'), value:stat.number, onChange:function(v){ updateStat(stat.id,{number:v}); } }),
130 el(TextControl, { label:__('Suffix (e.g. /5)', 'blockenberg'), value:stat.suffix, onChange:function(v){ updateStat(stat.id,{suffix:v}); } }),
131 el(TextControl, { label:__('Label', 'blockenberg'), value:stat.label, onChange:function(v){ updateStat(stat.id,{label:v}); } }),
132 el(TextControl, { label:__('Description', 'blockenberg'), value:stat.description, onChange:function(v){ updateStat(stat.id,{description:v}); } })
133 ) : null
134 );
135 }),
136 el(Button, { variant:'primary', style:{ marginTop:'8px', width:'100%' },
137 onClick:function(){ var n = { id:uid(), icon:'', iconType:'custom-char', iconDashicon:'', iconImageUrl:'', prefix:'', number:'0', suffix:'', label:'Stat Label', description:'Supporting detail' }; setAttr({ stats: stats.concat([n]) }); setActiveId(n.id); }
138 }, __('+ Add Stat', 'blockenberg'))
139 ),
140
141 /* Section header */
142 el(PanelBody, { title:__('Section Header', 'blockenberg'), initialOpen:false },
143 el(ToggleControl, { label:__('Show section header', 'blockenberg'), checked:a.showSectionHeader, onChange:function(v){ setAttr({showSectionHeader:v}); } }),
144 a.showSectionHeader ? el(Fragment, null,
145 el(TextControl, { label:__('Title', 'blockenberg'), value:a.sectionTitle, onChange:function(v){ setAttr({sectionTitle:v}); } }),
146 el(TextControl, { label:__('Subtitle', 'blockenberg'), value:a.sectionSubtitle, onChange:function(v){ setAttr({sectionSubtitle:v}); } }),
147 el(SelectControl, { label:__('Text alignment', 'blockenberg'), value:a.headerAlign, options:[
148 {label:'Left', value:'left'}, {label:'Center', value:'center'}, {label:'Right', value:'right'}
149 ], onChange:function(v){ setAttr({headerAlign:v}); } })
150 ) : null
151 ),
152
153 /* Layout */
154 el(PanelBody, { title:__('Layout & Style', 'blockenberg'), initialOpen:false },
155 el(SelectControl, { label:__('Layout', 'blockenberg'), value:a.layout, options:[
156 {label:__('Row (horizontal)','blockenberg'), value:'row'},
157 {label:__('Grid','blockenberg'), value:'grid'},
158 {label:__('Centered column','blockenberg'), value:'centered'},
159 ], onChange:function(v){ setAttr({layout:v}); } }),
160 el(SelectControl, { label:__('Card style', 'blockenberg'), value:a.cardStyle, options:[
161 {label:__('Plain (no background)','blockenberg'), value:'plain'},
162 {label:__('Card (white bg + shadow)','blockenberg'), value:'card'},
163 {label:__('Bordered','blockenberg'), value:'bordered'},
164 {label:__('Gradient number','blockenberg'), value:'gradient'},
165 ], onChange:function(v){ setAttr({cardStyle:v}); } }),
166 el(RangeControl, { label:__('Columns','blockenberg'), value:a.columns, min:1, max:6, onChange:function(v){ setAttr({columns:v}); } }),
167 el(RangeControl, { label:__('Gap (px)','blockenberg'), value:a.gap, min:0, max:80, onChange:function(v){ setAttr({gap:v}); } }),
168 el(SelectControl, { label:__('Text alignment','blockenberg'), value:a.textAlign, options:[
169 {label:'Left',value:'left'},{label:'Center',value:'center'},{label:'Right',value:'right'}
170 ], onChange:function(v){ setAttr({textAlign:v}); } }),
171 el(ToggleControl, { label:__('Show icons','blockenberg'), checked:a.showIcons, onChange:function(v){ setAttr({showIcons:v}); } }),
172 el(ToggleControl, { label:__('Show dividers between stats','blockenberg'), checked:a.showDividers, onChange:function(v){ setAttr({showDividers:v}); } }),
173 el(ToggleControl, { label:__('Show descriptions','blockenberg'), checked:a.showDescription, onChange:function(v){ setAttr({showDescription:v}); } })
174 ),
175
176 /* Spacing */
177 el(PanelBody, { title:__('Spacing', 'blockenberg'), initialOpen:false },
178 el(RangeControl, { label:__('Section padding top (px)', 'blockenberg'), value:a.paddingTop, min:0, max:160, onChange:function(v){ setAttr({paddingTop:v}); } }),
179 el(RangeControl, { label:__('Section padding bottom (px)', 'blockenberg'), value:a.paddingBottom, min:0, max:160, onChange:function(v){ setAttr({paddingBottom:v}); } }),
180 el(RangeControl, { label:__('Card padding (px)', 'blockenberg'), value:a.cardPadding, min:0, max:80, onChange:function(v){ setAttr({cardPadding:v}); } }),
181 el(RangeControl, { label:__('Border radius (px)', 'blockenberg'), value:a.borderRadius, min:0, max:40, onChange:function(v){ setAttr({borderRadius:v}); } })
182 ),
183
184 /* Typography */
185 el(PanelBody, { title:__('Typography', 'blockenberg'), initialOpen:false },
186 a.showSectionHeader && getTypoControl()({ label: __('Title', 'blockenberg'), value: a.titleTypo, onChange: function (v) { setAttr({ titleTypo: v }); } }),
187 a.showSectionHeader && getTypoControl()({ label: __('Subtitle', 'blockenberg'), value: a.subtitleTypo, onChange: function (v) { setAttr({ subtitleTypo: v }); } }),
188 getTypoControl()({ label: __('Number', 'blockenberg'), value: a.numberTypo, onChange: function (v) { setAttr({ numberTypo: v }); } }),
189 getTypoControl()({ label: __('Label', 'blockenberg'), value: a.labelTypo, onChange: function (v) { setAttr({ labelTypo: v }); } }),
190 a.showDescription && getTypoControl()({ label: __('Description', 'blockenberg'), value: a.descTypo, onChange: function (v) { setAttr({ descTypo: v }); } }),
191 el(RangeControl, { label:__('Icon size (px)','blockenberg'), value:a.iconSize, min:16, max:64, onChange:function(v){ setAttr({iconSize:v}); } })
192 ),
193
194 /* Colors */
195 el(PanelColorSettings, { title:__('Colors', 'blockenberg'), initialOpen:false, colorSettings:[
196 {label:__('Number color','blockenberg'), value:a.numberColor, onChange:function(v){ setAttr({numberColor:v||''}); }},
197 {label:__('Label color','blockenberg'), value:a.labelColor, onChange:function(v){ setAttr({labelColor:v||''}); }},
198 {label:__('Description color','blockenberg'), value:a.descColor, onChange:function(v){ setAttr({descColor:v||''}); }},
199 {label:__('Icon color','blockenberg'), value:a.iconColor, onChange:function(v){ setAttr({iconColor:v||''}); }},
200 {label:__('Divider color','blockenberg'), value:a.dividerColor, onChange:function(v){ setAttr({dividerColor:v||''}); }},
201 {label:__('Card background','blockenberg'),value:a.cardBg, onChange:function(v){ setAttr({cardBg:v||''}); }},
202 {label:__('Card border','blockenberg'), value:a.cardBorder, onChange:function(v){ setAttr({cardBorder:v||''}); }},
203 {label:__('Section background','blockenberg'),value:a.sectionBg, onChange:function(v){ setAttr({sectionBg:v||''}); }},
204 {label:__('Title color','blockenberg'), value:a.titleColor, onChange:function(v){ setAttr({titleColor:v||''}); }},
205 {label:__('Subtitle color','blockenberg'), value:a.subtitleColor,onChange:function(v){ setAttr({subtitleColor:v||''}); }},
206 {label:__('Gradient from','blockenberg'), value:a.gradientFrom, onChange:function(v){ setAttr({gradientFrom:v||''}); }},
207 {label:__('Gradient to','blockenberg'), value:a.gradientTo, onChange:function(v){ setAttr({gradientTo:v||''}); }},
208 ] })
209 );
210
211 /* Preview */
212 var headerEl = a.showSectionHeader ? el('div', { className: 'bkbg-sts-header', style:{ textAlign: a.headerAlign } },
213 el(RichText, { tagName:'h2', className:'bkbg-sts-title', value:a.sectionTitle, onChange:function(v){ setAttr({sectionTitle:v}); }, placeholder:__('Section title…','blockenberg') }),
214 el(RichText, { tagName:'p', className:'bkbg-sts-subtitle', value:a.sectionSubtitle, onChange:function(v){ setAttr({sectionSubtitle:v}); }, placeholder:__('Section subtitle…','blockenberg') })
215 ) : null;
216
217 var gridEl = el('div', {
218 className: 'bkbg-sts-grid' + (a.showDividers ? ' bkbg-sts-dividers' : '')
219 },
220 stats.map(function(stat) {
221 return el(StatCard, { key: stat.id, stat:stat, a:a, isEdit:true });
222 })
223 );
224
225 return el(Fragment, null,
226 inspector,
227 el('div', blockProps,
228 headerEl,
229 gridEl
230 )
231 );
232 },
233
234 save: function (props) {
235 var a = props.attributes;
236 var blockProps = wp.blockEditor.useBlockProps.save({
237 className: 'bkbg-sts-wrap bkbg-sts-layout--' + a.layout + ' bkbg-sts-style--' + a.cardStyle,
238 style: buildWrapStyle(a)
239 });
240
241 var headerEl = a.showSectionHeader ? el('div', { className: 'bkbg-sts-header', style:{ textAlign: a.headerAlign } },
242 el(RichText.Content, { tagName:'h2', className:'bkbg-sts-title', value:a.sectionTitle }),
243 el(RichText.Content, { tagName:'p', className:'bkbg-sts-subtitle', value:a.sectionSubtitle })
244 ) : null;
245
246 return el('div', blockProps,
247 headerEl,
248 el('div', { className: 'bkbg-sts-grid' + (a.showDividers ? ' bkbg-sts-dividers' : '') },
249 a.stats.map(function(stat) {
250 return el('div', { key: stat.id, className: 'bkbg-sts-item' },
251 a.showIcons && stat.icon ? el('span', { className: 'bkbg-sts-icon' }, (stat.iconType || 'custom-char') !== 'custom-char' ? IP().buildSaveIcon(stat.iconType, stat.icon, stat.iconDashicon, stat.iconImageUrl, stat.iconDashiconColor) : stat.icon) : null,
252 el('span', { className: 'bkbg-sts-number' },
253 stat.prefix ? el('span', { className: 'bkbg-sts-prefix' }, stat.prefix) : null,
254 el('span', { className: 'bkbg-sts-num-value' }, stat.number),
255 stat.suffix ? el('span', { className: 'bkbg-sts-suffix' }, stat.suffix) : null
256 ),
257 el('span', { className: 'bkbg-sts-label' }, stat.label),
258 a.showDescription && stat.description ? el('span', { className: 'bkbg-sts-desc' }, stat.description) : null
259 );
260 })
261 )
262 );
263 }
264 });
265 }() );
266