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

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

570 lines 32.0 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 RichText = wp.blockEditor.RichText;
11 var MediaUpload = wp.blockEditor.MediaUpload;
12 var MediaUploadCheck = wp.blockEditor.MediaUploadCheck;
13 var PanelBody = wp.components.PanelBody;
14 var RangeControl = wp.components.RangeControl;
15 var TextControl = wp.components.TextControl;
16 var TextareaControl = wp.components.TextareaControl;
17 var ToggleControl = wp.components.ToggleControl;
18 var Button = wp.components.Button;
19
20 var _TypographyControl, _typoCssVars;
21 function getTypographyControl() { return _TypographyControl || (_TypographyControl = window.bkbgTypographyControl); }
22 function getTypoCssVars() { return _typoCssVars || (_typoCssVars = window.bkbgTypoCssVars); }
23
24 function uid() {
25 return 'hs-' + Math.random().toString(36).slice(2, 9);
26 }
27
28 function ItemEditor(props) {
29 var item = props.item;
30 var label = props.label;
31 var onChange = props.onChange;
32 var onRemove = props.onRemove;
33
34 return el(PanelBody, {
35 title: (item.name || __('(empty)', 'blockenberg')).slice(0, 28),
36 initialOpen: false
37 },
38 el(TextControl, {
39 label: label + ' ' + __('name', 'blockenberg'),
40 value: item.name,
41 onChange: function (v) { onChange({ name: v }); }
42 }),
43 el(Button, {
44 isDestructive: true, isSmall: true,
45 onClick: onRemove
46 }, __('Remove', 'blockenberg'))
47 );
48 }
49
50 function StepEditor(props) {
51 var step = props.step;
52 var idx = props.idx;
53 var onChange = props.onChange;
54 var onRemove = props.onRemove;
55
56 return el(PanelBody, {
57 title: __('Step', 'blockenberg') + ' ' + (idx + 1) + ': ' + (step.name || '').slice(0, 20),
58 initialOpen: false
59 },
60 el(TextControl, {
61 label: __('Step title', 'blockenberg'),
62 value: step.name,
63 onChange: function (v) { onChange({ name: v }); }
64 }),
65 el(TextareaControl, {
66 label: __('Step description', 'blockenberg'),
67 value: step.text,
68 rows: 3,
69 onChange: function (v) { onChange({ text: v }); }
70 }),
71 el('div', { style: { marginBottom: '12px' } },
72 el('p', { style: { fontSize: '11px', color: '#757575', margin: '0 0 6px' } }, __('Step image (optional)', 'blockenberg')),
73 el(MediaUploadCheck, null,
74 el(MediaUpload, {
75 onSelect: function (media) { onChange({ imageUrl: media.url, imageId: media.id }); },
76 allowedTypes: ['image'],
77 value: step.imageId,
78 render: function (mediaProps) {
79 return el(Fragment, null,
80 step.imageUrl && el('img', {
81 src: step.imageUrl,
82 style: { width: '100%', height: '120px', objectFit: 'cover', borderRadius: '6px', display: 'block', marginBottom: '6px' }
83 }),
84 el(Button, { isSmall: true, variant: 'secondary', onClick: mediaProps.open },
85 step.imageUrl ? __('Replace image', 'blockenberg') : __('Set image', 'blockenberg')),
86 step.imageUrl && el(Button, {
87 isSmall: true, isDestructive: true,
88 style: { marginLeft: '8px' },
89 onClick: function () { onChange({ imageUrl: '', imageId: 0 }); }
90 }, __('Remove', 'blockenberg'))
91 );
92 }
93 })
94 )
95 ),
96 el(Button, { isDestructive: true, isSmall: true, onClick: onRemove }, __('Remove step', 'blockenberg'))
97 );
98 }
99
100 function StepPreview(props) {
101 var step = props.step;
102 var idx = props.idx;
103 var a = props.attrs;
104
105 return el('div', {
106 className: 'bkbg-hs-step',
107 style: {
108 display: 'flex',
109 gap: '20px',
110 background: a.cardBg || '#ffffff',
111 border: '1px solid ' + (a.cardBorder || '#e5e7eb'),
112 borderRadius: a.cardRadius + 'px',
113 padding: '20px',
114 marginBottom: '16px',
115 alignItems: 'flex-start'
116 }
117 },
118 a.showStepNumbers && el('div', {
119 className: 'bkbg-hs-step-num',
120 style: {
121 minWidth: a.stepNumSize * 2 + 'px',
122 height: a.stepNumSize * 2 + 'px',
123 borderRadius: '50%',
124 background: a.stepNumBg || '#6c3fb5',
125 color: a.stepNumColor || '#ffffff',
126 display: 'flex',
127 alignItems: 'center',
128 justifyContent: 'center',
129 fontSize: a.stepNumSize + 'px',
130 fontWeight: 800,
131 flexShrink: 0
132 }
133 }, idx + 1),
134 el('div', { className: 'bkbg-hs-step-content', style: { flex: 1 } },
135 el('div', {
136 className: 'bkbg-hs-step-name',
137 style: { color: a.stepNameColor || '#1e1b4b', marginBottom: '6px' }
138 }, step.name || __('Step title', 'blockenberg')),
139 el('div', {
140 className: 'bkbg-hs-step-text',
141 style: { color: a.stepTextColor || '#374151' }
142 }, step.text || __('Step description…', 'blockenberg')),
143 a.showStepImages && step.imageUrl && el('img', {
144 src: step.imageUrl,
145 alt: step.name,
146 style: { width: '100%', maxWidth: '480px', borderRadius: '8px', marginTop: '12px', display: 'block' }
147 })
148 )
149 );
150 }
151
152 registerBlockType('blockenberg/howto-schema', {
153 edit: function (props) {
154 var attrs = props.attributes;
155 var setAttr = function (obj) { props.setAttributes(obj); };
156
157 var steps = attrs.steps || [];
158 var supplies = attrs.supplies || [];
159 var tools = attrs.tools || [];
160
161 function addStep() {
162 setAttr({ steps: steps.concat([{ id: uid(), name: '', text: '', imageUrl: '', imageId: 0 }]) });
163 }
164 function updateStep(id, patch) {
165 setAttr({ steps: steps.map(function (s) { return s.id === id ? Object.assign({}, s, patch) : s; }) });
166 }
167 function removeStep(id) {
168 setAttr({ steps: steps.filter(function (s) { return s.id !== id; }) });
169 }
170
171 function addSupply() {
172 setAttr({ supplies: supplies.concat([{ id: uid(), name: '' }]) });
173 }
174 function updateSupply(id, patch) {
175 setAttr({ supplies: supplies.map(function (s) { return s.id === id ? Object.assign({}, s, patch) : s; }) });
176 }
177 function removeSupply(id) {
178 setAttr({ supplies: supplies.filter(function (s) { return s.id !== id; }) });
179 }
180
181 function addTool() {
182 setAttr({ tools: tools.concat([{ id: uid(), name: '' }]) });
183 }
184 function updateTool(id, patch) {
185 setAttr({ tools: tools.map(function (t) { return t.id === id ? Object.assign({}, t, patch) : t; }) });
186 }
187 function removeTool(id) {
188 setAttr({ tools: tools.filter(function (t) { return t.id !== id; }) });
189 }
190
191 var wrapStyle = { paddingTop: attrs.paddingTop + 'px', paddingBottom: attrs.paddingBottom + 'px' };
192 if (attrs.bgColor) wrapStyle.background = attrs.bgColor;
193 var _tv = getTypoCssVars();
194 if (_tv) {
195 Object.assign(wrapStyle, _tv(attrs.titleTypo, '--bkbg-hts-tt-'));
196 Object.assign(wrapStyle, _tv(attrs.stepNameTypo, '--bkbg-hts-sn-'));
197 Object.assign(wrapStyle, _tv(attrs.stepTextTypo, '--bkbg-hts-st-'));
198 }
199 wrapStyle['--bkbg-hts-tt-sz'] = (attrs.titleSize || 30) + 'px';
200 wrapStyle['--bkbg-hts-sn-sz'] = (attrs.stepNameSize || 17) + 'px';
201 wrapStyle['--bkbg-hts-st-sz'] = (attrs.stepTextSize || 14) + 'px';
202
203 var blockProps = useBlockProps({ className: 'bkbg-hs-wrap', style: wrapStyle });
204
205 return el(Fragment, null,
206 el(InspectorControls, null,
207
208 el(PanelBody, { title: __('Steps', 'blockenberg'), initialOpen: true },
209 steps.map(function (s, i) {
210 return el(StepEditor, {
211 key: s.id,
212 step: s,
213 idx: i,
214 onChange: function (patch) { updateStep(s.id, patch); },
215 onRemove: function () { removeStep(s.id); }
216 });
217 }),
218 el(Button, {
219 variant: 'primary', isSmall: true,
220 style: { marginTop: '12px', width: '100%', justifyContent: 'center' },
221 onClick: addStep
222 }, __('+ Add Step', 'blockenberg'))
223 ),
224
225 el(PanelBody, { title: __('Supplies', 'blockenberg'), initialOpen: false },
226 el(ToggleControl, {
227 label: __('Show supplies section', 'blockenberg'),
228 checked: attrs.showSupplies,
229 onChange: function (v) { setAttr({ showSupplies: v }); },
230 __nextHasNoMarginBottom: true
231 }),
232 supplies.map(function (s) {
233 return el(ItemEditor, {
234 key: s.id,
235 item: s,
236 label: __('Supply', 'blockenberg'),
237 onChange: function (patch) { updateSupply(s.id, patch); },
238 onRemove: function () { removeSupply(s.id); }
239 });
240 }),
241 el(Button, {
242 isSmall: true, variant: 'secondary',
243 style: { marginTop: '8px' },
244 onClick: addSupply
245 }, __('+ Add Supply', 'blockenberg'))
246 ),
247
248 el(PanelBody, { title: __('Tools', 'blockenberg'), initialOpen: false },
249 el(ToggleControl, {
250 label: __('Show tools section', 'blockenberg'),
251 checked: attrs.showTools,
252 onChange: function (v) { setAttr({ showTools: v }); },
253 __nextHasNoMarginBottom: true
254 }),
255 tools.map(function (t) {
256 return el(ItemEditor, {
257 key: t.id,
258 item: t,
259 label: __('Tool', 'blockenberg'),
260 onChange: function (patch) { updateTool(t.id, patch); },
261 onRemove: function () { removeTool(t.id); }
262 });
263 }),
264 el(Button, {
265 isSmall: true, variant: 'secondary',
266 style: { marginTop: '8px' },
267 onClick: addTool
268 }, __('+ Add Tool', 'blockenberg'))
269 ),
270
271 el(PanelBody, { title: __('Guide Info', 'blockenberg'), initialOpen: false },
272 el(TextControl, {
273 label: __('Total time (ISO 8601 – e.g. PT30M, PT2H)', 'blockenberg'),
274 value: attrs.totalTime,
275 onChange: function (v) { setAttr({ totalTime: v }); }
276 }),
277 el(TextControl, {
278 label: __('Time label (displayed)', 'blockenberg'),
279 value: attrs.totalTimeLabel,
280 onChange: function (v) { setAttr({ totalTimeLabel: v }); }
281 }),
282 el(TextControl, {
283 label: __('Estimated cost (number only)', 'blockenberg'),
284 value: attrs.estimatedCost,
285 onChange: function (v) { setAttr({ estimatedCost: v }); }
286 }),
287 el(TextControl, {
288 label: __('Currency (e.g. USD, EUR)', 'blockenberg'),
289 value: attrs.costCurrency,
290 onChange: function (v) { setAttr({ costCurrency: v }); }
291 })
292 ),
293
294 el(PanelBody, { title: __('Display', 'blockenberg'), initialOpen: false },
295 el(ToggleControl, { label: __('Show title', 'blockenberg'), checked: attrs.showTitle, onChange: function (v) { setAttr({ showTitle: v }); }, __nextHasNoMarginBottom: true }),
296 el(ToggleControl, { label: __('Show description', 'blockenberg'), checked: attrs.showDescription, onChange: function (v) { setAttr({ showDescription: v }); }, __nextHasNoMarginBottom: true }),
297 el(ToggleControl, { label: __('Show meta bar (time/cost)', 'blockenberg'), checked: attrs.showMeta, onChange: function (v) { setAttr({ showMeta: v }); }, __nextHasNoMarginBottom: true }),
298 el(ToggleControl, { label: __('Numbered steps', 'blockenberg'), checked: attrs.showStepNumbers, onChange: function (v) { setAttr({ showStepNumbers: v }); }, __nextHasNoMarginBottom: true }),
299 el(ToggleControl, { label: __('Show step images', 'blockenberg'), checked: attrs.showStepImages, onChange: function (v) { setAttr({ showStepImages: v }); }, __nextHasNoMarginBottom: true })
300 ),
301
302 el(PanelBody, { title: __('Typography', 'blockenberg'), initialOpen: false },
303 el(RangeControl, { label: __('Step number size (px)', 'blockenberg'), value: attrs.stepNumSize, min: 12, max: 32, onChange: function (v) { setAttr({ stepNumSize: v }); } }),
304 getTypographyControl() && el(getTypographyControl(), { label: __('Title', 'blockenberg'), typo: attrs.titleTypo || {}, onChange: function(v){ setAttr({ titleTypo: v }); } }),
305 getTypographyControl() && el(getTypographyControl(), { label: __('Step Title', 'blockenberg'), typo: attrs.stepNameTypo || {}, onChange: function(v){ setAttr({ stepNameTypo: v }); } }),
306 getTypographyControl() && el(getTypographyControl(), { label: __('Step Text', 'blockenberg'), typo: attrs.stepTextTypo || {}, onChange: function(v){ setAttr({ stepTextTypo: v }); } })
307 ),
308
309 el(PanelBody, { title: __('Spacing', 'blockenberg'), initialOpen: false },
310 el(RangeControl, { label: __('Padding top (px)', 'blockenberg'), value: attrs.paddingTop, min: 0, max: 200, onChange: function (v) { setAttr({ paddingTop: v }); } }),
311 el(RangeControl, { label: __('Padding bottom (px)', 'blockenberg'), value: attrs.paddingBottom, min: 0, max: 200, onChange: function (v) { setAttr({ paddingBottom: v }); } })
312 ),
313
314 el(PanelColorSettings, {
315 title: __('Colors', 'blockenberg'), initialOpen: false,
316 colorSettings: [
317 { label: __('Accent', 'blockenberg'), value: attrs.accentColor, onChange: function (v) { setAttr({ accentColor: v || '#6c3fb5' }); } },
318 { label: __('Title', 'blockenberg'), value: attrs.titleColor, onChange: function (v) { setAttr({ titleColor: v || '#1e1b4b' }); } },
319 { label: __('Description', 'blockenberg'), value: attrs.descColor, onChange: function (v) { setAttr({ descColor: v || '#374151' }); } },
320 { label: __('Step number background', 'blockenberg'), value: attrs.stepNumBg, onChange: function (v) { setAttr({ stepNumBg: v || '#6c3fb5' }); } },
321 { label: __('Step number text', 'blockenberg'), value: attrs.stepNumColor, onChange: function (v) { setAttr({ stepNumColor: v || '#ffffff' }); } },
322 { label: __('Step title', 'blockenberg'), value: attrs.stepNameColor, onChange: function (v) { setAttr({ stepNameColor: v || '#1e1b4b' }); } },
323 { label: __('Step text', 'blockenberg'), value: attrs.stepTextColor, onChange: function (v) { setAttr({ stepTextColor: v || '#374151' }); } },
324 { label: __('Meta bar background', 'blockenberg'), value: attrs.metaBg, onChange: function (v) { setAttr({ metaBg: v || '#f5f3ff' }); } },
325 { label: __('Meta bar border', 'blockenberg'), value: attrs.metaBorder, onChange: function (v) { setAttr({ metaBorder: v || '#ede9fe' }); } },
326 { label: __('Meta bar text', 'blockenberg'), value: attrs.metaColor, onChange: function (v) { setAttr({ metaColor: v || '#374151' }); } },
327 { label: __('Card background', 'blockenberg'), value: attrs.cardBg, onChange: function (v) { setAttr({ cardBg: v || '#ffffff' }); } },
328 { label: __('Card border', 'blockenberg'), value: attrs.cardBorder, onChange: function (v) { setAttr({ cardBorder: v || '#e5e7eb' }); } },
329 { label: __('Supply/tool background', 'blockenberg'), value: attrs.supplyBg, onChange: function (v) { setAttr({ supplyBg: v || '#f9fafb' }); } },
330 { label: __('Supply/tool border', 'blockenberg'), value: attrs.supplyBorder, onChange: function (v) { setAttr({ supplyBorder: v || '#e5e7eb' }); } },
331 { label: __('Block background', 'blockenberg'), value: attrs.bgColor, onChange: function (v) { setAttr({ bgColor: v || '' }); } }
332 ]
333 })
334 ),
335
336 el('div', blockProps,
337 attrs.showTitle && el(RichText, {
338 tagName: 'h2',
339 className: 'bkbg-hs-title',
340 style: { color: attrs.titleColor, margin: '0 0 12px' },
341 value: attrs.title,
342 onChange: function (v) { setAttr({ title: v }); },
343 placeholder: __('How To…', 'blockenberg')
344 }),
345 attrs.showDescription && el(RichText, {
346 tagName: 'p',
347 className: 'bkbg-hs-desc',
348 style: { color: attrs.descColor, margin: '0 0 16px', lineHeight: 1.7 },
349 value: attrs.description,
350 onChange: function (v) { setAttr({ description: v }); },
351 placeholder: __('Describe what readers will learn to do…', 'blockenberg')
352 }),
353
354 attrs.showMeta && el('div', {
355 className: 'bkbg-hs-meta',
356 style: {
357 display: 'flex', flexWrap: 'wrap', gap: '16px',
358 background: attrs.metaBg || '#f5f3ff',
359 border: '1px solid ' + (attrs.metaBorder || '#ede9fe'),
360 borderRadius: attrs.cardRadius + 'px',
361 padding: '12px 16px', marginBottom: '24px'
362 }
363 },
364 attrs.totalTimeLabel && el('div', { style: { display: 'flex', alignItems: 'center', gap: '6px', color: attrs.metaColor || '#374151', fontSize: '14px' } },
365 el('span', { style: { fontWeight: 700 } }, ''),
366 el('span', null, attrs.totalTimeLabel)
367 ),
368 attrs.estimatedCost && el('div', { style: { display: 'flex', alignItems: 'center', gap: '6px', color: attrs.metaColor || '#374151', fontSize: '14px' } },
369 el('span', { style: { fontWeight: 700 } }, '💰'),
370 el('span', null, attrs.estimatedCost + ' ' + (attrs.costCurrency || 'USD'))
371 )
372 ),
373
374 (attrs.showSupplies && supplies.length > 0) && el('div', { className: 'bkbg-hs-supplies', style: { marginBottom: '24px' } },
375 el('h3', { style: { fontSize: '16px', fontWeight: 700, color: attrs.titleColor, margin: '0 0 10px' } }, __('What You Need', 'blockenberg')),
376 el('div', { style: { display: 'flex', flexWrap: 'wrap', gap: '8px' } },
377 supplies.map(function (s) {
378 return el('span', {
379 key: s.id,
380 style: { background: attrs.supplyBg || '#f9fafb', border: '1px solid ' + (attrs.supplyBorder || '#e5e7eb'), borderRadius: '6px', padding: '4px 12px', fontSize: '13px', color: attrs.stepTextColor || '#374151' }
381 }, s.name || __('Supply', 'blockenberg'));
382 })
383 )
384 ),
385
386 (attrs.showTools && tools.length > 0) && el('div', { className: 'bkbg-hs-tools', style: { marginBottom: '24px' } },
387 el('h3', { style: { fontSize: '16px', fontWeight: 700, color: attrs.titleColor, margin: '0 0 10px' } }, __('Tools', 'blockenberg')),
388 el('div', { style: { display: 'flex', flexWrap: 'wrap', gap: '8px' } },
389 tools.map(function (t) {
390 return el('span', {
391 key: t.id,
392 style: { background: attrs.supplyBg || '#f9fafb', border: '1px solid ' + (attrs.supplyBorder || '#e5e7eb'), borderRadius: '6px', padding: '4px 12px', fontSize: '13px', color: attrs.stepTextColor || '#374151' }
393 }, t.name || __('Tool', 'blockenberg'));
394 })
395 )
396 ),
397
398 el('div', { className: 'bkbg-hs-steps' },
399 steps.length === 0
400 ? el('p', { style: { color: '#9ca3af', textAlign: 'center', padding: '40px 0' } }, __('Add your first step in the sidebar →', 'blockenberg'))
401 : steps.map(function (s, i) {
402 return el(StepPreview, { key: s.id, step: s, idx: i, attrs: attrs });
403 })
404 )
405 )
406 );
407 },
408
409 save: function (props) {
410 var a = props.attributes;
411 var steps = a.steps || [];
412 var supplies = a.supplies || [];
413 var tools = a.tools || [];
414
415 var wrapStyle = { paddingTop: a.paddingTop + 'px', paddingBottom: a.paddingBottom + 'px' };
416 if (a.bgColor) wrapStyle.background = a.bgColor;
417 var _tv = getTypoCssVars();
418 if (_tv) {
419 Object.assign(wrapStyle, _tv(a.titleTypo, '--bkbg-hts-tt-'));
420 Object.assign(wrapStyle, _tv(a.stepNameTypo, '--bkbg-hts-sn-'));
421 Object.assign(wrapStyle, _tv(a.stepTextTypo, '--bkbg-hts-st-'));
422 }
423 wrapStyle['--bkbg-hts-tt-sz'] = (a.titleSize || 30) + 'px';
424 wrapStyle['--bkbg-hts-sn-sz'] = (a.stepNameSize || 17) + 'px';
425 wrapStyle['--bkbg-hts-st-sz'] = (a.stepTextSize || 14) + 'px';
426
427 var blockProps = wp.blockEditor.useBlockProps.save({ className: 'bkbg-hs-wrap', style: wrapStyle });
428
429 // Build HowTo JSON-LD
430 var schemaData = {
431 '@context': 'https://schema.org',
432 '@type': 'HowTo',
433 'name': a.title || '',
434 'description': a.description || '',
435 'totalTime': a.totalTime || undefined
436 };
437 if (a.estimatedCost) {
438 schemaData.estimatedCost = { '@type': 'MonetaryAmount', 'currency': a.costCurrency || 'USD', 'value': a.estimatedCost };
439 }
440 if (supplies.length > 0) {
441 schemaData.supply = supplies.map(function (s) { return { '@type': 'HowToSupply', 'name': s.name }; });
442 }
443 if (tools.length > 0) {
444 schemaData.tool = tools.map(function (t) { return { '@type': 'HowToTool', 'name': t.name }; });
445 }
446 schemaData.step = steps.map(function (s, i) {
447 var step = { '@type': 'HowToStep', 'position': i + 1, 'name': s.name, 'text': s.text };
448 if (s.imageUrl) step.image = s.imageUrl;
449 return step;
450 });
451
452 return el('div', blockProps,
453 a.showTitle && el('h2', {
454 className: 'bkbg-hs-title',
455 style: { color: a.titleColor, margin: '0 0 12px' },
456 dangerouslySetInnerHTML: { __html: a.title }
457 }),
458 a.showDescription && el('p', {
459 className: 'bkbg-hs-desc',
460 style: { color: a.descColor, margin: '0 0 16px', lineHeight: 1.7 },
461 dangerouslySetInnerHTML: { __html: a.description }
462 }),
463
464 a.showMeta && el('div', {
465 className: 'bkbg-hs-meta',
466 style: {
467 display: 'flex', flexWrap: 'wrap', gap: '16px',
468 background: a.metaBg || '#f5f3ff',
469 border: '1px solid ' + (a.metaBorder || '#ede9fe'),
470 borderRadius: a.cardRadius + 'px',
471 padding: '12px 16px', marginBottom: '24px'
472 }
473 },
474 a.totalTimeLabel && el('div', { style: { display: 'flex', alignItems: 'center', gap: '6px', color: a.metaColor || '#374151', fontSize: '14px' } },
475 el('span', { style: { fontWeight: 700 }, 'aria-hidden': 'true' }, ''),
476 el('span', null, a.totalTimeLabel)
477 ),
478 a.estimatedCost && el('div', { style: { display: 'flex', alignItems: 'center', gap: '6px', color: a.metaColor || '#374151', fontSize: '14px' } },
479 el('span', { style: { fontWeight: 700 }, 'aria-hidden': 'true' }, '💰'),
480 el('span', null, a.estimatedCost + ' ' + (a.costCurrency || 'USD'))
481 )
482 ),
483
484 (a.showSupplies && supplies.length > 0) && el('div', { className: 'bkbg-hs-supplies', style: { marginBottom: '24px' } },
485 el('h3', { style: { fontSize: '16px', fontWeight: 700, color: a.titleColor, margin: '0 0 10px' } }, __('What You Need', 'blockenberg')),
486 el('div', { style: { display: 'flex', flexWrap: 'wrap', gap: '8px' } },
487 supplies.map(function (s) {
488 return el('span', {
489 key: s.id,
490 style: { background: a.supplyBg || '#f9fafb', border: '1px solid ' + (a.supplyBorder || '#e5e7eb'), borderRadius: '6px', padding: '4px 12px', fontSize: '13px', color: a.stepTextColor || '#374151' }
491 }, s.name);
492 })
493 )
494 ),
495
496 (a.showTools && tools.length > 0) && el('div', { className: 'bkbg-hs-tools', style: { marginBottom: '24px' } },
497 el('h3', { style: { fontSize: '16px', fontWeight: 700, color: a.titleColor, margin: '0 0 10px' } }, __('Tools', 'blockenberg')),
498 el('div', { style: { display: 'flex', flexWrap: 'wrap', gap: '8px' } },
499 tools.map(function (t) {
500 return el('span', {
501 key: t.id,
502 style: { background: a.supplyBg || '#f9fafb', border: '1px solid ' + (a.supplyBorder || '#e5e7eb'), borderRadius: '6px', padding: '4px 12px', fontSize: '13px', color: a.stepTextColor || '#374151' }
503 }, t.name);
504 })
505 )
506 ),
507
508 el('ol', { className: 'bkbg-hs-steps', style: { listStyle: 'none', margin: 0, padding: 0 } },
509 steps.map(function (s, i) {
510 return el('li', {
511 key: s.id,
512 className: 'bkbg-hs-step',
513 style: {
514 display: 'flex',
515 gap: '20px',
516 background: a.cardBg || '#ffffff',
517 border: '1px solid ' + (a.cardBorder || '#e5e7eb'),
518 borderRadius: a.cardRadius + 'px',
519 padding: '20px',
520 marginBottom: '16px',
521 alignItems: 'flex-start'
522 }
523 },
524 a.showStepNumbers && el('div', {
525 className: 'bkbg-hs-step-num',
526 'aria-hidden': 'true',
527 style: {
528 minWidth: a.stepNumSize * 2 + 'px',
529 height: a.stepNumSize * 2 + 'px',
530 borderRadius: '50%',
531 background: a.stepNumBg || '#6c3fb5',
532 color: a.stepNumColor || '#ffffff',
533 display: 'flex',
534 alignItems: 'center',
535 justifyContent: 'center',
536 fontSize: a.stepNumSize + 'px',
537 fontWeight: 800,
538 flexShrink: 0
539 }
540 }, i + 1),
541 el('div', { className: 'bkbg-hs-step-content', style: { flex: 1 } },
542 el('h3', {
543 className: 'bkbg-hs-step-name',
544 style: { color: a.stepNameColor || '#1e1b4b', margin: '0 0 6px' }
545 }, s.name),
546 el('p', {
547 className: 'bkbg-hs-step-text',
548 style: { color: a.stepTextColor || '#374151', margin: 0 }
549 }, s.text),
550 a.showStepImages && s.imageUrl && el('img', {
551 src: s.imageUrl,
552 alt: s.name,
553 loading: 'lazy',
554 style: { width: '100%', maxWidth: '480px', borderRadius: '8px', marginTop: '12px', display: 'block' }
555 })
556 )
557 );
558 })
559 ),
560
561 // HowTo JSON-LD structured data
562 el('script', {
563 type: 'application/ld+json',
564 dangerouslySetInnerHTML: { __html: JSON.stringify(schemaData, null, 2) }
565 })
566 );
567 }
568 });
569 }() );
570