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

index.js in Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor 2.0.14, at blocks/unit-converter/index.js

366 lines 20.8 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 PanelBody = wp.components.PanelBody;
11 var RangeControl = wp.components.RangeControl;
12 var SelectControl = wp.components.SelectControl;
13 var TextControl = wp.components.TextControl;
14 var ToggleControl = wp.components.ToggleControl;
15 var CheckboxControl = wp.components.CheckboxControl;
16
17 // Resolve the shared components lazily without defining globals for each block.
18 function _tc(label, key, attrs, setA) {
19 var Control = window.bkbgTypographyControl;
20 return Control ? el(Control, {
21 label: label,
22 value: attrs[key] || {},
23 onChange: function (value) { var update = {}; update[key] = value; setA(update); }
24 }) : null;
25 }
26 function _tvf(vars, key, attrs, prefix) {
27 var cssVars = window.bkbgTypoCssVars;
28 if (cssVars) Object.assign(vars, cssVars(attrs[key] || {}, prefix));
29 return vars;
30 }
31 function getTypoControl(label, key, attrs, setA) { return _tc(label, key, attrs, setA); }
32 function getTypoCssVars(attrs) {
33 var v = {};
34 _tvf(v, 'titleTypo', attrs, '--bkuc-tt-');
35 _tvf(v, 'subtitleTypo', attrs, '--bkuc-st-');
36 return v;
37 }
38
39 /* ── Conversion data ────────────────────────────────────────────────────── */
40 var CATEGORIES = {
41 length: {
42 label: 'Length',
43 units: {
44 meter: { label: 'Meter (m)', toBase: 1 },
45 kilometer: { label: 'Kilometer (km)', toBase: 1000 },
46 centimeter: { label: 'Centimeter (cm)', toBase: 0.01 },
47 millimeter: { label: 'Millimeter (mm)', toBase: 0.001 },
48 mile: { label: 'Mile (mi)', toBase: 1609.344 },
49 yard: { label: 'Yard (yd)', toBase: 0.9144 },
50 foot: { label: 'Foot (ft)', toBase: 0.3048 },
51 inch: { label: 'Inch (in)', toBase: 0.0254 },
52 nautical_mile: { label: 'Nautical Mile', toBase: 1852 },
53 },
54 defaultFrom: 'meter',
55 defaultTo: 'foot',
56 },
57 weight: {
58 label: 'Weight',
59 units: {
60 kilogram: { label: 'Kilogram (kg)', toBase: 1 },
61 gram: { label: 'Gram (g)', toBase: 0.001 },
62 milligram: { label: 'Milligram (mg)', toBase: 0.000001 },
63 pound: { label: 'Pound (lb)', toBase: 0.453592 },
64 ounce: { label: 'Ounce (oz)', toBase: 0.0283495 },
65 ton: { label: 'Metric Ton (t)', toBase: 1000 },
66 stone: { label: 'Stone (st)', toBase: 6.35029 },
67 },
68 defaultFrom: 'kilogram',
69 defaultTo: 'pound',
70 },
71 temperature: {
72 label: 'Temperature',
73 units: {
74 celsius: { label: 'Celsius (°C)', toBase: null },
75 fahrenheit: { label: 'Fahrenheit (°F)', toBase: null },
76 kelvin: { label: 'Kelvin (K)', toBase: null },
77 },
78 defaultFrom: 'celsius',
79 defaultTo: 'fahrenheit',
80 },
81 volume: {
82 label: 'Volume',
83 units: {
84 liter: { label: 'Liter (L)', toBase: 1 },
85 milliliter: { label: 'Milliliter (mL)', toBase: 0.001 },
86 cubic_meter:{ label: 'Cubic Meter (m³)',toBase: 1000 },
87 gallon_us: { label: 'Gallon (US)', toBase: 3.78541 },
88 gallon_uk: { label: 'Gallon (UK)', toBase: 4.54609 },
89 quart: { label: 'Quart (US)', toBase: 0.946353 },
90 pint: { label: 'Pint (US)', toBase: 0.473176 },
91 cup: { label: 'Cup (US)', toBase: 0.236588 },
92 fluid_oz: { label: 'Fluid Oz (US)', toBase: 0.0295735 },
93 tablespoon: { label: 'Tablespoon', toBase: 0.0147868 },
94 teaspoon: { label: 'Teaspoon', toBase: 0.00492892 },
95 },
96 defaultFrom: 'liter',
97 defaultTo: 'gallon_us',
98 },
99 area: {
100 label: 'Area',
101 units: {
102 sqmeter: { label: 'Square Meter (m²)', toBase: 1 },
103 sqkilometer:{ label: 'Square Km (km²)', toBase: 1000000 },
104 sqcentimeter:{ label:'Square Cm (cm²)', toBase: 0.0001 },
105 sqfoot: { label: 'Square Foot (ft²)', toBase: 0.092903 },
106 sqinch: { label: 'Square Inch (in²)', toBase: 0.00064516 },
107 sqyard: { label: 'Square Yard (yd²)', toBase: 0.836127 },
108 squaremile: { label: 'Square Mile (mi²)', toBase: 2589988 },
109 acre: { label: 'Acre', toBase: 4046.86 },
110 hectare: { label: 'Hectare (ha)', toBase: 10000 },
111 },
112 defaultFrom: 'sqmeter',
113 defaultTo: 'sqfoot',
114 },
115 speed: {
116 label: 'Speed',
117 units: {
118 ms: { label: 'Meter/second (m/s)', toBase: 1 },
119 kmh: { label: 'Km/hour (km/h)', toBase: 0.277778 },
120 mph: { label: 'Mile/hour (mph)', toBase: 0.44704 },
121 knot: { label: 'Knot (kn)', toBase: 0.514444 },
122 fts: { label: 'Foot/second (ft/s)', toBase: 0.3048 },
123 mach: { label: 'Mach', toBase: 343 },
124 },
125 defaultFrom: 'kmh',
126 defaultTo: 'mph',
127 },
128 };
129
130 function convertValue(cat, fromKey, toKey, value) {
131 if (isNaN(value) || value === '') return '';
132 var num = parseFloat(value);
133 if (cat === 'temperature') {
134 var c;
135 if (fromKey === 'celsius') c = num;
136 else if (fromKey === 'fahrenheit') c = (num - 32) * 5 / 9;
137 else c = num - 273.15;
138
139 if (toKey === 'celsius') return c;
140 if (toKey === 'fahrenheit') return c * 9 / 5 + 32;
141 return c + 273.15;
142 }
143 var units = CATEGORIES[cat].units;
144 var base = num * units[fromKey].toBase;
145 return base / units[toKey].toBase;
146 }
147
148 function fmtResult(val, dp) {
149 if (val === '' || isNaN(val)) return '—';
150 var n = parseFloat(val);
151 if (Math.abs(n) < 1e-10 && n !== 0) return n.toExponential(4);
152 return parseFloat(n.toFixed(dp)).toLocaleString('en-US', { maximumFractionDigits: dp });
153 }
154
155 /* ── Preview component ──────────────────────────────────────────────────── */
156 function ConverterPreview(props) {
157 var a = props.attrs;
158 var cats = (a.enabledCategories && a.enabledCategories.length) ? a.enabledCategories : ['length', 'weight', 'temperature'];
159
160 var catState = useState(a.defaultCategory || 'length');
161 var activeCat = catState[0]; var setActiveCat = catState[1];
162
163 var cat = CATEGORIES[activeCat] || CATEGORIES['length'];
164 var unitKeys = Object.keys(cat.units);
165
166 var fromState = useState(cat.defaultFrom);
167 var fromKey = fromState[0]; var setFromKey = fromState[1];
168 var toState = useState(cat.defaultTo);
169 var toKey = toState[0]; var setToKey = toState[1];
170 var valState = useState('1');
171 var value = valState[0]; var setValue = valState[1];
172
173 var result = convertValue(activeCat, fromKey, toKey, value);
174 var resultFmt = fmtResult(result, a.decimalPlaces || 4);
175
176 var accent = a.accentColor || '#6c3fb5';
177 var cRadius = (a.cardRadius || 16) + 'px';
178
179 var cardStyle = {
180 background: a.cardBg || '#ffffff',
181 borderRadius: cRadius,
182 padding: '32px',
183 boxShadow: '0 4px 24px rgba(0,0,0,0.08)',
184 maxWidth: (a.maxWidth || 640) + 'px',
185 margin: '0 auto',
186 paddingTop: (a.paddingTop || 60) + 'px',
187 paddingBottom: (a.paddingBottom || 60) + 'px',
188 boxSizing: 'border-box',
189 };
190
191 var unitOptions = unitKeys.map(function(k) { return { label: cat.units[k].label, value: k }; });
192
193 return el('div', { className: 'bkbg-uc-card', style: cardStyle },
194 a.showTitle && el('h2', { className: 'bkbg-uc-title', style: { color: a.titleColor || '#1e1b4b' } }, a.title || 'Unit Converter'),
195 a.showSubtitle && el('p', { className: 'bkbg-uc-subtitle', style: { color: a.subtitleColor || '#6b7280' } }, a.subtitle),
196
197 /* Category tabs */
198 a.showCategoryTabs && el('div', { style: { display: 'flex', flexWrap: 'wrap', gap: '6px', marginBottom: '24px', justifyContent: 'center' } },
199 cats.map(function(catKey) {
200 var isActive = activeCat === catKey;
201 var catDef = CATEGORIES[catKey];
202 if (!catDef) return null;
203 return el('button', {
204 key: catKey,
205 onClick: function() {
206 setActiveCat(catKey);
207 setFromKey(CATEGORIES[catKey].defaultFrom);
208 setToKey(CATEGORIES[catKey].defaultTo);
209 },
210 style: {
211 padding: '6px 14px',
212 borderRadius: '99px',
213 border: 'none',
214 cursor: 'pointer',
215 fontWeight: 600,
216 fontSize: '13px',
217 background: isActive ? (a.tabActiveBg || accent) : (a.tabInactiveBg || '#f3f4f6'),
218 color: isActive ? (a.tabActiveColor || '#fff') : (a.tabInactiveColor || '#374151'),
219 transition: 'all 0.2s',
220 }
221 }, catDef.label);
222 })
223 ),
224
225 /* From / To row */
226 el('div', { style: { display: 'grid', gridTemplateColumns: '1fr auto 1fr', gap: '12px', alignItems: 'end', marginBottom: '20px' } },
227 el('div', null,
228 el('label', { style: { display: 'block', fontSize: '12px', fontWeight: 600, color: a.labelColor || '#374151', marginBottom: '4px' } }, __('From', 'blockenberg')),
229 el('select', {
230 value: fromKey,
231 onChange: function(e) { setFromKey(e.target.value); },
232 style: { width: '100%', padding: '8px 10px', borderRadius: '8px', border: '1.5px solid #e5e7eb', fontSize: '14px', background: '#fff' }
233 }, unitOptions.map(function(o) { return el('option', { key: o.value, value: o.value }, o.label); }))
234 ),
235 el('button', {
236 onClick: function() { var tmp = fromKey; setFromKey(toKey); setToKey(tmp); },
237 style: { background: 'none', border: '1.5px solid ' + accent, borderRadius: '8px', padding: '8px 12px', cursor: 'pointer', color: accent, fontWeight: 700, fontSize: '18px', lineHeight: 1 }
238 }, '⇄'),
239 el('div', null,
240 el('label', { style: { display: 'block', fontSize: '12px', fontWeight: 600, color: a.labelColor || '#374151', marginBottom: '4px' } }, __('To', 'blockenberg')),
241 el('select', {
242 value: toKey,
243 onChange: function(e) { setToKey(e.target.value); },
244 style: { width: '100%', padding: '8px 10px', borderRadius: '8px', border: '1.5px solid #e5e7eb', fontSize: '14px', background: '#fff' }
245 }, unitOptions.map(function(o) { return el('option', { key: o.value, value: o.value }, o.label); }))
246 )
247 ),
248
249 /* Value input */
250 el('div', { style: { marginBottom: '20px' } },
251 el('label', { style: { display: 'block', fontSize: '13px', fontWeight: 600, color: a.labelColor || '#374151', marginBottom: '6px' } }, __('Value', 'blockenberg')),
252 el('input', {
253 type: 'number',
254 value: value,
255 onChange: function(e) { setValue(e.target.value); },
256 style: { width: '100%', padding: '12px 14px', borderRadius: '8px', border: '1.5px solid #e5e7eb', fontSize: '18px', boxSizing: 'border-box', outline: 'none' }
257 })
258 ),
259
260 /* Result */
261 el('div', {
262 style: {
263 background: a.resultBg || '#f5f3ff',
264 border: '1.5px solid ' + (a.resultBorder || '#ede9fe'),
265 borderRadius: cRadius,
266 padding: '20px 24px',
267 textAlign: 'center',
268 }
269 },
270 el('div', { style: { fontSize: '13px', color: '#9ca3af', marginBottom: '4px' } }, (cat.units[fromKey] ? cat.units[fromKey].label : fromKey) + ' → ' + (cat.units[toKey] ? cat.units[toKey].label : toKey)),
271 el('div', {
272 style: { fontSize: (a.resultSize || 40) + 'px', fontWeight: 800, color: a.resultColor || accent, lineHeight: 1.1 }
273 }, resultFmt),
274 el('div', { style: { fontSize: '14px', color: '#6b7280', marginTop: '4px' } }, cat.units[toKey] ? cat.units[toKey].label : '')
275 )
276 );
277 }
278
279 registerBlockType('blockenberg/unit-converter', {
280 edit: function(props) {
281 var a = props.attributes;
282 var setAttr = props.setAttributes;
283 var blockProps = useBlockProps((function () { var _tv = getTypoCssVars(a); var s = {}; Object.assign(s, _tv); if (a.bgColor) s.background = a.bgColor; return { style: s }; })());
284
285 var ALL_CATS = ['length', 'weight', 'temperature', 'volume', 'area', 'speed'];
286
287 return el(Fragment, null,
288 el(InspectorControls, null,
289
290 el(PanelBody, { title: __('Content', 'blockenberg'), initialOpen: true },
291 el(ToggleControl, { label: __('Show Title', 'blockenberg'), checked: a.showTitle, onChange: function(v) { setAttr({ showTitle: v }); }, __nextHasNoMarginBottom: true }),
292 a.showTitle && el(TextControl, { label: __('Title', 'blockenberg'), value: a.title, onChange: function(v) { setAttr({ title: v }); } }),
293 el(ToggleControl, { label: __('Show Subtitle', 'blockenberg'), checked: a.showSubtitle, onChange: function(v) { setAttr({ showSubtitle: v }); }, __nextHasNoMarginBottom: true }),
294 a.showSubtitle && el(TextControl, { label: __('Subtitle', 'blockenberg'), value: a.subtitle, onChange: function(v) { setAttr({ subtitle: v }); } }),
295 el(SelectControl, { label: __('Default Category', 'blockenberg'), value: a.defaultCategory, options: ALL_CATS.map(function(k) { return { label: CATEGORIES[k].label, value: k }; }), onChange: function(v) { setAttr({ defaultCategory: v }); } }),
296 el(RangeControl, { label: __('Decimal Places', 'blockenberg'), value: a.decimalPlaces, min: 0, max: 10, onChange: function(v) { setAttr({ decimalPlaces: v }); } })
297 ),
298
299 el(PanelBody, { title: __('Enabled Categories', 'blockenberg'), initialOpen: false },
300 ALL_CATS.map(function(k) {
301 var enabled = (a.enabledCategories || ALL_CATS).includes(k);
302 return el(CheckboxControl, {
303 key: k,
304 label: CATEGORIES[k].label,
305 checked: enabled,
306 __nextHasNoMarginBottom: true,
307 onChange: function(v) {
308 var cur = (a.enabledCategories || ALL_CATS).filter(function(x) { return x !== k; });
309 if (v) cur.push(k);
310 setAttr({ enabledCategories: ALL_CATS.filter(function(x) { return cur.includes(x); }) });
311 }
312 });
313 })
314 ),
315
316 el(PanelBody, { title: __('Display Options', 'blockenberg'), initialOpen: false },
317 el(ToggleControl, { label: __('Show Category Tabs', 'blockenberg'), checked: a.showCategoryTabs, onChange: function(v) { setAttr({ showCategoryTabs: v }); }, __nextHasNoMarginBottom: true }),
318 el(ToggleControl, { label: __('Show Swap Button', 'blockenberg'), checked: a.showSwapButton, onChange: function(v) { setAttr({ showSwapButton: v }); }, __nextHasNoMarginBottom: true })
319 ),
320
321
322 el( PanelBody, { title: __( 'Typography', 'blockenberg' ), initialOpen: false },
323 getTypoControl(__('Title', 'blockenberg'), 'titleTypo', a, setAttr),
324 getTypoControl(__('Subtitle', 'blockenberg'), 'subtitleTypo', a, setAttr),
325 el(RangeControl, { label: __('Result Number Size (px)', 'blockenberg'), value: a.resultSize, min: 24, max: 80, onChange: function(v) { setAttr({ resultSize: v }); } })
326 ),
327 el(PanelColorSettings, {
328 title: __('Colors', 'blockenberg'), initialOpen: false,
329 colorSettings: [
330 { label: __('Accent Color', 'blockenberg'), value: a.accentColor, onChange: function(v) { setAttr({ accentColor: v || '#6c3fb5' }); } },
331 { label: __('Result Number Color', 'blockenberg'), value: a.resultColor, onChange: function(v) { setAttr({ resultColor: v || '#6c3fb5' }); } },
332 { label: __('Active Tab Background', 'blockenberg'), value: a.tabActiveBg, onChange: function(v) { setAttr({ tabActiveBg: v || '#6c3fb5' }); } },
333 { label: __('Active Tab Text', 'blockenberg'), value: a.tabActiveColor, onChange: function(v) { setAttr({ tabActiveColor: v || '#ffffff' }); } },
334 { label: __('Card Background', 'blockenberg'), value: a.cardBg, onChange: function(v) { setAttr({ cardBg: v || '#ffffff' }); } },
335 { label: __('Result Background', 'blockenberg'), value: a.resultBg, onChange: function(v) { setAttr({ resultBg: v || '#f5f3ff' }); } },
336 { label: __('Section Background', 'blockenberg'), value: a.bgColor, onChange: function(v) { setAttr({ bgColor: v || '' }); } },
337 ]
338 }),
339
340 el(PanelBody, { title: __('Sizing', 'blockenberg'), initialOpen: false },
341 el(RangeControl, { label: __('Card Radius (px)', 'blockenberg'), value: a.cardRadius, min: 0, max: 40, onChange: function(v) { setAttr({ cardRadius: v }); } }),
342 el(RangeControl, { label: __('Max Width (px)', 'blockenberg'), value: a.maxWidth, min: 320, max: 1200, onChange: function(v) { setAttr({ maxWidth: v }); } }),
343 el(RangeControl, { label: __('Padding Top (px)', 'blockenberg'), value: a.paddingTop, min: 0, max: 160, onChange: function(v) { setAttr({ paddingTop: v }); } }),
344 el(RangeControl, { label: __('Padding Bottom (px)', 'blockenberg'), value: a.paddingBottom, min: 0, max: 160, onChange: function(v) { setAttr({ paddingBottom: v }); } })
345 )
346 ),
347
348 el('div', blockProps,
349 el(ConverterPreview, { attrs: a })
350 )
351 );
352 },
353
354 save: function(props) {
355 var a = props.attributes;
356 var blockProps = wp.blockEditor.useBlockProps.save((function () { var _tv = getTypoCssVars(a); var s = {}; Object.assign(s, _tv); if (a.bgColor) s.background = a.bgColor; return { style: s }; })());
357 var opts = JSON.stringify(a);
358 return el('div', blockProps,
359 el('div', { className: 'bkbg-uc-app', 'data-opts': opts },
360 el('p', { className: 'bkbg-uc-loading' }, 'Loading converter…')
361 )
362 );
363 }
364 });
365 }() );
366