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

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

242 lines 14.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 useState = wp.element.useState;
4 var Fragment = wp.element.Fragment;
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 RichText = wp.blockEditor.RichText;
11 var PanelBody = wp.components.PanelBody;
12 var RangeControl = wp.components.RangeControl;
13 var TextControl = wp.components.TextControl;
14 var ToggleControl = wp.components.ToggleControl;
15
16 var _tc; function getTypoControl() { return _tc || (_tc = window.bkbgTypographyControl); }
17 var _tv; function getTypoCssVars() { return _tv || (_tv = window.bkbgTypoCssVars); }
18
19 var v1Attributes = {
20 title: { type: 'string', default: 'Recipe Scaler' },
21 subtitle: { type: 'string', default: 'Adjust serving size and all ingredient quantities update automatically' },
22 recipeName: { type: 'string', default: 'Classic Pancakes' },
23 defaultServings: { type: 'number', default: 4 },
24 maxServings: { type: 'number', default: 24 },
25 showQuickBtns: { type: 'boolean', default: true },
26 showNotes: { type: 'boolean', default: true },
27 recipeNotes: { type: 'string', default: 'Do not over-mix the batter \u2014 a few lumps are fine!' },
28 accentColor: { type: 'string', default: '#f97316' },
29 sectionBg: { type: 'string', default: '#fff7ed' },
30 cardBg: { type: 'string', default: '#ffffff' },
31 inputBg: { type: 'string', default: '#fafafa' },
32 ingredientBg: { type: 'string', default: '#fef3c7' },
33 titleColor: { type: 'string', default: '#111827' },
34 subtitleColor: { type: 'string', default: '#6b7280' },
35 labelColor: { type: 'string', default: '#374151' },
36 titleFontSize: { type: 'integer', default: 26 },
37 contentMaxWidth: { type: 'integer', default: 660 },
38 titleFontWeight: { type: 'integer', default: 700 },
39 titleLineHeight: { type: 'number', default: 1.2 }
40 };
41
42 var SAMPLE_INGREDIENTS = [
43 { qty: 2, unit: 'cups', name: 'All-purpose flour' },
44 { qty: 2, unit: 'tsp', name: 'Baking powder' },
45 { qty: 0.5, unit: 'tsp', name: 'Salt' },
46 { qty: 2, unit: 'tbsp', name: 'Sugar' },
47 { qty: 2, unit: '', name: 'Large eggs' },
48 { qty: 1.5, unit: 'cups', name: 'Milk' },
49 { qty: 3, unit: 'tbsp', name: 'Butter, melted' }
50 ];
51
52 var QUICK_BTNS = [1, 2, 4, 6, 8, 12];
53
54 function fmtQty(n) {
55 if (n === Math.round(n)) return String(Math.round(n));
56 var f = Math.round(n * 100) / 100;
57 // nice fractions for common values
58 var fracs = [[0.25,'¼'],[0.5,'½'],[0.75,'¾'],[0.33,'�
59 �'],[0.67,'�
60 �'],[0.125,'�
61 �']];
62 var whole = Math.floor(f);
63 var rem = Math.round((f - whole) * 100) / 100;
64 for (var i=0;i<fracs.length;i++) {
65 if (Math.abs(rem - fracs[i][0]) < 0.01) {
66 return (whole > 0 ? whole + ' ' : '') + fracs[i][1];
67 }
68 }
69 return f.toFixed(2).replace(/\.?0+$/, '');
70 }
71
72 function EditorPreview(props) {
73 var a = props.attributes;
74 var servings= a.defaultServings;
75 var base = a.defaultServings;
76
77 var wrapStyle = {
78 background: a.sectionBg, borderRadius:'14px', padding:'32px 24px',
79 maxWidth: a.contentMaxWidth+'px', margin:'0 auto', fontFamily:'inherit', boxSizing:'border-box'
80 };
81
82 return el('div', { style: wrapStyle },
83 el(RichText, { tagName:'h3', className:'bkbg-rsc-title', value:a.title, onChange:function(v){ props.setAttributes({title:v}); },
84 style:{ color:a.titleColor, margin:'0 0 4px 0' }, placeholder:'Block title...' }),
85 el(RichText, { tagName:'p', value:a.subtitle, onChange:function(v){ props.setAttributes({subtitle:v}); },
86 style:{ fontSize:'14px', color:a.subtitleColor, margin:'0 0 20px 0' }, placeholder:'Subtitle...' }),
87
88 // Recipe name
89 el('div', { style:{ background:a.cardBg, borderRadius:'12px', padding:'14px 18px', border:'1.5px solid #e5e7eb', marginBottom:'18px', display:'flex', alignItems:'center', gap:'12px' } },
90 el('span', { style:{ fontSize:'24px' } }, '🍽️'),
91 el('span', { style:{ fontSize:'18px', fontWeight:'700', color:a.titleColor } }, a.recipeName || 'Recipe')
92 ),
93
94 // Servings control
95 el('div', { style:{ background:a.cardBg, borderRadius:'12px', padding:'14px 18px', border:'1.5px solid #e5e7eb', marginBottom:'18px' } },
96 el('div', { style:{ fontSize:'12px', fontWeight:'700', color:a.labelColor, textTransform:'uppercase', letterSpacing:'0.06em', marginBottom:'10px' } }, 'Servings'),
97 el('div', { style:{ display:'flex', alignItems:'center', gap:'12px', flexWrap:'wrap' } },
98 el('button', { style:{ width:'36px', height:'36px', borderRadius:'8px', border:'2px solid '+a.accentColor, background:'none', color:a.accentColor, fontSize:'20px', fontWeight:'700', cursor:'pointer' } }, ''),
99 el('span', { style:{ fontSize:'28px', fontWeight:'700', color:a.accentColor, minWidth:'40px', textAlign:'center' } }, servings),
100 el('button', { style:{ width:'36px', height:'36px', borderRadius:'8px', border:'2px solid '+a.accentColor, background:'none', color:a.accentColor, fontSize:'20px', fontWeight:'700', cursor:'pointer' } }, '+'),
101 el('span', { style:{ fontSize:'14px', color:a.labelColor } }, 'servings')
102 ),
103 a.showQuickBtns && el('div', { style:{ display:'flex', gap:'6px', flexWrap:'wrap', marginTop:'12px' } },
104 QUICK_BTNS.map(function(n) {
105 var active = n === servings;
106 return el('button', { key:n, style:{ padding:'5px 13px', borderRadius:'6px', border:'1.5px solid '+(active?a.accentColor:'#e5e7eb'), background:active?a.accentColor:'transparent', color:active?'#fff':a.labelColor, fontSize:'13px', fontWeight:active?'700':'400', cursor:'pointer' } }, n + 'x');
107 })
108 )
109 ),
110
111 // Ingredients list preview
112 el('div', { style:{ marginBottom:'16px' } },
113 el('div', { style:{ fontSize:'12px', fontWeight:'700', color:a.labelColor, textTransform:'uppercase', letterSpacing:'0.06em', marginBottom:'10px' } }, 'Ingredients'),
114 SAMPLE_INGREDIENTS.map(function(ing) {
115 var scaled = fmtQty(ing.qty * servings / base);
116 return el('div', { key:ing.name, style:{ display:'flex', alignItems:'center', gap:'10px', padding:'8px 12px', background:a.ingredientBg, borderRadius:'8px', marginBottom:'6px' } },
117 el('span', { style:{ fontWeight:'700', color:a.accentColor, minWidth:'45px', fontSize:'15px' } }, scaled),
118 el('span', { style:{ color:'#6b7280', fontSize:'14px', minWidth:'38px' } }, ing.unit),
119 el('span', { style:{ flex:1, fontSize:'14px', color:a.labelColor } }, ing.name)
120 );
121 }),
122 el('div', { style:{ color:a.accentColor, fontSize:'13px', fontWeight:'600', marginTop:'8px', cursor:'pointer', opacity:0.7 } }, '+ Add ingredient (frontend)')
123 ),
124
125 // Notes
126 a.showNotes && a.recipeNotes && el('div', { style:{ background:'#fef9c3', border:'1.5px solid #fde047', borderRadius:'10px', padding:'12px 16px' } },
127 el('div', { style:{ fontSize:'12px', fontWeight:'700', color:'#854d0e', marginBottom:'4px' } }, '📝 Recipe Notes'),
128 el('div', { style:{ fontSize:'13px', color:'#78350f' } }, a.recipeNotes)
129 )
130 );
131 }
132
133 registerBlockType('blockenberg/recipe-scaler', {
134 edit: function(props) {
135 var a = props.attributes;
136 var set = props.setAttributes;
137 var TC = getTypoControl();
138
139 var colorSettings = [
140 { value:a.accentColor, onChange:function(v){ set({accentColor: v||'#f97316'}); }, label:'Accent color' },
141 { value:a.sectionBg, onChange:function(v){ set({sectionBg: v||'#fff7ed'}); }, label:'Section background' },
142 { value:a.cardBg, onChange:function(v){ set({cardBg: v||'#ffffff'}); }, label:'Card background' },
143 { value:a.inputBg, onChange:function(v){ set({inputBg: v||'#fafafa'}); }, label:'Input background' },
144 { value:a.ingredientBg, onChange:function(v){ set({ingredientBg: v||'#fef3c7'}); }, label:'Ingredient row bg' },
145 { value:a.titleColor, onChange:function(v){ set({titleColor: v||'#111827'}); }, label:'Title color' },
146 { value:a.subtitleColor, onChange:function(v){ set({subtitleColor: v||'#6b7280'}); }, label:'Subtitle color' },
147 { value:a.labelColor, onChange:function(v){ set({labelColor: v||'#374151'}); }, label:'Label color' }
148 ];
149
150 return el(Fragment, null,
151 el(InspectorControls, null,
152 el(PanelBody, { title:'Recipe Settings', initialOpen:true },
153 el(TextControl, { label:'Recipe Name', value:a.recipeName, onChange:function(v){ set({recipeName:v}); } }),
154 el(RangeControl, { label:'Default Servings', value:a.defaultServings, min:1, max:24, onChange:function(v){ set({defaultServings:v}); } }),
155 el(RangeControl, { label:'Maximum Servings', value:a.maxServings, min:4, max:100, onChange:function(v){ set({maxServings:v}); } }),
156 el(ToggleControl,{ __nextHasNoMarginBottom:true, label:'Show quick serving buttons', checked:a.showQuickBtns, onChange:function(v){ set({showQuickBtns:v}); } }),
157 el(ToggleControl,{ __nextHasNoMarginBottom:true, label:'Show recipe notes', checked:a.showNotes, onChange:function(v){ set({showNotes:v}); } }),
158 a.showNotes && el(TextControl, { label:'Recipe Notes', value:a.recipeNotes, onChange:function(v){ set({recipeNotes:v}); } })
159 ),
160 el(PanelBody, { title:'Sizing', initialOpen:false },
161 el(RangeControl, { label:'Max Width (px)', value:a.contentMaxWidth, min:400, max:1100, step:20, onChange:function(v){ set({contentMaxWidth:v}); } })
162 ),
163
164 el( PanelBody, { title: __( 'Typography', 'blockenberg' ), initialOpen: false },
165 TC && el(TC, { label: __('Title', 'blockenberg'), value: a.titleTypo || {}, onChange: function(v) { set({ titleTypo: v }); } })
166 ),
167 el(PanelColorSettings, { title:'Colors', initialOpen:false, disableCustomGradients:true, colorSettings:colorSettings })
168 ),
169 el('div', useBlockProps((function() {
170 var _tvFn = getTypoCssVars();
171 var s = {};
172 if (_tvFn) Object.assign(s, _tvFn(a.titleTypo || {}, '--bkrsc-tt-'));
173 return { style: s };
174 })()),
175 el(EditorPreview, { attributes:a, setAttributes:set })
176 )
177 );
178 },
179
180 save: function(props) {
181 var a = props.attributes;
182 return el('div', useBlockProps.save(),
183 el('div', {
184 className: 'bkbg-rsc-app',
185 'data-opts': JSON.stringify({
186 title: a.title,
187 subtitle: a.subtitle,
188 recipeName: a.recipeName,
189 defaultServings:a.defaultServings,
190 maxServings: a.maxServings,
191 showQuickBtns: a.showQuickBtns,
192 showNotes: a.showNotes,
193 recipeNotes: a.recipeNotes,
194 accentColor: a.accentColor,
195 sectionBg: a.sectionBg,
196 cardBg: a.cardBg,
197 inputBg: a.inputBg,
198 ingredientBg: a.ingredientBg,
199 titleColor: a.titleColor,
200 subtitleColor: a.subtitleColor,
201 labelColor: a.labelColor,
202 titleTypo: a.titleTypo || {},
203 contentMaxWidth:a.contentMaxWidth
204 })
205 })
206 );
207 },
208
209 deprecated: [{
210 attributes: v1Attributes,
211 save: function(props) {
212 var a = props.attributes;
213 return el('div', useBlockProps.save(),
214 el('div', {
215 className: 'bkbg-rsc-app',
216 'data-opts': JSON.stringify({
217 title: a.title,
218 subtitle: a.subtitle,
219 recipeName: a.recipeName,
220 defaultServings:a.defaultServings,
221 maxServings: a.maxServings,
222 showQuickBtns: a.showQuickBtns,
223 showNotes: a.showNotes,
224 recipeNotes: a.recipeNotes,
225 accentColor: a.accentColor,
226 sectionBg: a.sectionBg,
227 cardBg: a.cardBg,
228 inputBg: a.inputBg,
229 ingredientBg: a.ingredientBg,
230 titleColor: a.titleColor,
231 subtitleColor: a.subtitleColor,
232 labelColor: a.labelColor,
233 titleFontSize: a.titleFontSize,
234 contentMaxWidth:a.contentMaxWidth
235 })
236 })
237 );
238 }
239 }]
240 });
241 }() );
242