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 / tool-card / index.js

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

252 lines 17.1 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 __ = wp.i18n.__;
4 var registerBlockType = wp.blocks.registerBlockType;
5 var InspectorControls = wp.blockEditor.InspectorControls;
6 var MediaUpload = wp.blockEditor.MediaUpload;
7 var MediaUploadCheck = wp.blockEditor.MediaUploadCheck;
8 var useBlockProps = wp.blockEditor.useBlockProps;
9 var RichText = wp.blockEditor.RichText;
10 var PanelColorSettings = wp.blockEditor.PanelColorSettings;
11 var PanelBody = wp.components.PanelBody;
12 var ToggleControl = wp.components.ToggleControl;
13 var RangeControl = wp.components.RangeControl;
14 var SelectControl = wp.components.SelectControl;
15 var TextControl = wp.components.TextControl;
16 var TextareaControl = wp.components.TextareaControl;
17 var Button = wp.components.Button;
18
19 // Resolve the shared components lazily without defining globals for each block.
20 function _tc(props, key, label) {
21 var Control = window.bkbgTypographyControl;
22 return Control ? el(Control, {
23 label: label,
24 value: props.attributes[key] || {},
25 onChange: function (value) { var update = {}; update[key] = value; props.setAttributes(update); }
26 }) : null;
27 }
28 function _tvf(typo, prefix) {
29 var cssVars = window.bkbgTypoCssVars;
30 return cssVars ? cssVars(typo || {}, prefix) : {};
31 }
32 function getTypoControl(props, attrName, label) { return _tc(props, attrName, label); }
33
34 function HalfStar(color, key) {
35 return el('span', { key: key, style: { position: 'relative', display: 'inline-block', color: '#d1d5db', lineHeight: 1 } },
36 '�
37 ',
38 el('span', { style: { position: 'absolute', left: 0, top: 0, width: '50%', overflow: 'hidden', color: color } }, '�
39 ')
40 );
41 }
42
43 function StarRow(rating, starColor) {
44 var stars = [];
45 for (var i = 1; i <= 5; i++) {
46 if (rating >= i) {
47 stars.push(el('span', { key: i, style: { color: starColor, fontSize: '16px' } }, '�
48 '));
49 } else if (rating >= i - 0.5) {
50 stars.push(HalfStar(starColor, i));
51 } else {
52 stars.push(el('span', { key: i, style: { color: '#d1d5db', fontSize: '16px' } }, '☆'));
53 }
54 }
55 return el('span', {}, stars);
56 }
57
58 registerBlockType('blockenberg/tool-card', {
59 edit: function (props) {
60 var attr = props.attributes;
61 var set = props.setAttributes;
62
63 var blockProps = (function () {
64 var s = { background: '#f8fafc', padding: '24px' };
65 Object.assign(s, _tvf(attr.nameTypo, '--bktlc-nt-'), _tvf(attr.bodyTypo, '--bktlc-bt-'));
66 return useBlockProps({ style: s });
67 })();
68
69 /* Features list management */
70 function FeaturesList() {
71 return el('div', { style: { marginTop: '12px' } },
72 el('label', { style: { display: 'block', fontSize: '11px', fontWeight: 500, textTransform: 'uppercase', marginBottom: '8px', color: '#1e1e1e' } }, __('Key Features', 'blockenberg')),
73 (attr.features || []).map(function (f, idx) {
74 return el('div', { key: idx, style: { display: 'flex', gap: '6px', marginBottom: '6px' } },
75 el(TextControl, {
76 value: f, __nextHasNoMarginBottom: true,
77 onChange: function (v) {
78 var feats = attr.features.slice();
79 feats[idx] = v;
80 set({ features: feats });
81 }
82 }),
83 el(Button, {
84 variant: 'tertiary', isDestructive: true, style: { flexShrink: 0 },
85 onClick: function () { var feats = attr.features.filter(function (_, i) { return i !== idx; }); set({ features: feats }); }
86 }, '✕')
87 );
88 }),
89 el(Button, {
90 variant: 'secondary', __nextHasNoMarginBottom: true, style: { marginTop: '6px' },
91 onClick: function () { set({ features: (attr.features || []).concat(['New feature']) }); }
92 }, __('+ Add Feature', 'blockenberg'))
93 );
94 }
95
96 /* Card preview */
97 var isHorizontal = attr.layout === 'horizontal';
98 var cardStyle = {
99 display: 'flex', gap: '24px', flexDirection: isHorizontal ? 'row' : 'column',
100 background: attr.bgColor, border: '1px solid ' + attr.borderColor,
101 borderRadius: attr.borderRadius + 'px', padding: '28px', position: 'relative'
102 };
103
104 var logoSection = el('div', { style: { flexShrink: 0, display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '12px', minWidth: isHorizontal ? '140px' : 'auto' } },
105 attr.logoUrl
106 ? el('img', { src: attr.logoUrl, alt: attr.logoAlt, style: { width: '72px', height: '72px', objectFit: 'contain', borderRadius: '12px', border: '1px solid ' + attr.borderColor } })
107 : el('div', { style: { width: '72px', height: '72px', borderRadius: '12px', background: attr.accentColor + '22', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: '32px' } }, '🔧'),
108 attr.showRating && el('div', { style: { textAlign: 'center' } },
109 StarRow(attr.rating, attr.starColor),
110 el('div', { style: { fontSize: '11px', color: attr.taglineColor, marginTop: '2px' } }, attr.ratingCount)
111 ),
112 attr.showPricing && el('div', { style: { fontSize: '11px', color: attr.pricingColor, textAlign: 'center', background: attr.bgColor === '#ffffff' ? '#f8fafc' : '#f0f0f0', borderRadius: '6px', padding: '4px 8px' } },
113 el('strong', { style: { textTransform: 'uppercase', fontSize: '10px', color: attr.accentColor } }, attr.pricing), el('br'),
114 attr.pricingNote
115 ),
116 el(Button, { variant: 'primary', style: { background: attr.ctaBg, color: attr.ctaColor, border: 'none', borderRadius: '6px', padding: '10px 18px', fontWeight: 600, width: '100%', justifyContent: 'center' } }, attr.ctaLabel)
117 );
118
119 var contentSection = el('div', { style: { flex: 1, minWidth: 0 } },
120 el('div', { style: { display: 'flex', alignItems: 'flex-start', gap: '10px', marginBottom: '6px' } },
121 el('h3', { className: 'bkbg-tlc-name', style: { margin: 0, color: attr.nameColor } }, attr.toolName),
122 attr.showBadge && attr.badgeText && el('span', { style: { background: attr.badgeBg, color: attr.badgeColor, fontSize: '11px', fontWeight: 700, padding: '3px 8px', borderRadius: '4px', whiteSpace: 'nowrap', flexShrink: 0 } }, attr.badgeText)
123 ),
124 el('p', { className: 'bkbg-tlc-tagline', style: { margin: '0 0 12px', color: attr.taglineColor } }, attr.tagline),
125 el('p', { className: 'bkbg-tlc-desc', style: { margin: '0 0 16px', color: attr.descColor } }, attr.description),
126 el('ul', { style: { margin: '0', paddingLeft: '0', listStyle: 'none' } },
127 (attr.features || []).slice(0, 5).map(function (f, i) {
128 return el('li', { key: i, style: { display: 'flex', alignItems: 'flex-start', gap: '8px', marginBottom: '6px', color: attr.featureColor } },
129 el('span', { style: { color: attr.accentColor, fontWeight: 700, flexShrink: 0 } }, '✓'),
130 f
131 );
132 })
133 )
134 );
135
136 var controls = el(InspectorControls, {},
137 el(PanelBody, { title: __('Tool Details', 'blockenberg'), initialOpen: true },
138 el(TextControl, { label: __('Tool Name', 'blockenberg'), value: attr.toolName, __nextHasNoMarginBottom: true, onChange: function (v) { set({ toolName: v }); } }),
139 el('div', { style: { marginTop: '12px' } },
140 el(TextControl, { label: __('Tagline', 'blockenberg'), value: attr.tagline, __nextHasNoMarginBottom: true, onChange: function (v) { set({ tagline: v }); } })
141 ),
142 el('div', { style: { marginTop: '12px' } },
143 el(TextareaControl, { label: __('Description', 'blockenberg'), value: attr.description, __nextHasNoMarginBottom: true, onChange: function (v) { set({ description: v }); } })
144 ),
145 FeaturesList()
146 ),
147 el(PanelBody, { title: __('Logo', 'blockenberg'), initialOpen: false },
148 el(MediaUploadCheck, {},
149 el(MediaUpload, {
150 onSelect: function (m) { set({ logoUrl: m.url, logoId: m.id, logoAlt: m.alt || '' }); },
151 allowedTypes: ['image'], value: attr.logoId,
152 render: function (rp) {
153 return el('div', {},
154 attr.logoUrl && el('img', { src: attr.logoUrl, style: { width: '72px', marginBottom: '8px', borderRadius: '8px' } }),
155 el(Button, { onClick: rp.open, variant: 'secondary', __nextHasNoMarginBottom: true }, attr.logoUrl ? __('Replace Logo', 'blockenberg') : __('Upload Logo', 'blockenberg')),
156 attr.logoUrl && el(Button, { onClick: function () { set({ logoUrl: '', logoId: 0, logoAlt: '' }); }, variant: 'link', isDestructive: true, style: { marginLeft: '8px' } }, __('Remove', 'blockenberg'))
157 );
158 }
159 })
160 )
161 ),
162 el(PanelBody, { title: __('Rating & Pricing', 'blockenberg'), initialOpen: false },
163 el(ToggleControl, { label: __('Show Rating', 'blockenberg'), checked: attr.showRating, __nextHasNoMarginBottom: true, onChange: function (v) { set({ showRating: v }); } }),
164 attr.showRating && el('div', { style: { marginTop: '12px' } },
165 el(RangeControl, { label: __('Rating (0-5)', 'blockenberg'), value: attr.rating, min: 0, max: 5, step: 0.5, __nextHasNoMarginBottom: true, onChange: function (v) { set({ rating: v }); } }),
166 el('div', { style: { marginTop: '8px' } },
167 el(TextControl, { label: __('Review Count Label', 'blockenberg'), value: attr.ratingCount, __nextHasNoMarginBottom: true, onChange: function (v) { set({ ratingCount: v }); } })
168 )
169 ),
170 el('div', { style: { marginTop: '12px' } },
171 el(ToggleControl, { label: __('Show Pricing', 'blockenberg'), checked: attr.showPricing, __nextHasNoMarginBottom: true, onChange: function (v) { set({ showPricing: v }); } })
172 ),
173 attr.showPricing && el('div', { style: { marginTop: '8px' } },
174 el(SelectControl, {
175 label: __('Pricing Model', 'blockenberg'), value: attr.pricing, __nextHasNoMarginBottom: true,
176 options: [{ label: 'Free', value: 'free' }, { label: 'Freemium', value: 'freemium' }, { label: 'Paid', value: 'paid' }],
177 onChange: function (v) { set({ pricing: v }); }
178 }),
179 el('div', { style: { marginTop: '8px' } },
180 el(TextControl, { label: __('Pricing Note', 'blockenberg'), value: attr.pricingNote, __nextHasNoMarginBottom: true, onChange: function (v) { set({ pricingNote: v }); } })
181 )
182 )
183 ),
184 el(PanelBody, { title: __('Badge & CTA', 'blockenberg'), initialOpen: false },
185 el(ToggleControl, { label: __('Show Badge', 'blockenberg'), checked: attr.showBadge, __nextHasNoMarginBottom: true, onChange: function (v) { set({ showBadge: v }); } }),
186 attr.showBadge && el('div', { style: { marginTop: '8px' } },
187 el(TextControl, { label: __('Badge Text', 'blockenberg'), value: attr.badgeText, __nextHasNoMarginBottom: true, onChange: function (v) { set({ badgeText: v }); } })
188 ),
189 el('div', { style: { marginTop: '12px' } },
190 el(TextControl, { label: __('CTA Label', 'blockenberg'), value: attr.ctaLabel, __nextHasNoMarginBottom: true, onChange: function (v) { set({ ctaLabel: v }); } })
191 ),
192 el('div', { style: { marginTop: '8px' } },
193 el(TextControl, { label: __('CTA URL', 'blockenberg'), value: attr.ctaUrl, __nextHasNoMarginBottom: true, onChange: function (v) { set({ ctaUrl: v }); } })
194 )
195 ),
196 el(PanelBody, { title: __('Layout', 'blockenberg'), initialOpen: false },
197 el(SelectControl, {
198 label: __('Layout', 'blockenberg'), value: attr.layout, __nextHasNoMarginBottom: true,
199 options: [{ label: 'Horizontal', value: 'horizontal' }, { label: 'Vertical', value: 'vertical' }, { label: 'Compact', value: 'compact' }],
200 onChange: function (v) { set({ layout: v }); }
201 }),
202 el('div', { style: { marginTop: '12px' } },
203 el(RangeControl, { label: __('Border Radius', 'blockenberg'), value: attr.borderRadius, min: 0, max: 32, __nextHasNoMarginBottom: true, onChange: function (v) { set({ borderRadius: v }); } })
204 )
205 ),
206 el(PanelBody, { title: __('Typography', 'blockenberg'), initialOpen: false },
207 getTypoControl(props, 'nameTypo', __('Tool Name', 'blockenberg')),
208 getTypoControl(props, 'bodyTypo', __('Body', 'blockenberg'))
209 ),
210 el(PanelColorSettings, {
211 title: __('Colors', 'blockenberg'), initialOpen: false,
212 colorSettings: [
213 { label: __('Card Background', 'blockenberg'), value: attr.bgColor, onChange: function (v) { set({ bgColor: v || '#ffffff' }); } },
214 { label: __('Card Border', 'blockenberg'), value: attr.borderColor, onChange: function (v) { set({ borderColor: v || '#e2e8f0' }); } },
215 { label: __('Accent', 'blockenberg'), value: attr.accentColor, onChange: function (v) { set({ accentColor: v || '#6366f1' }); } },
216 { label: __('Tool Name', 'blockenberg'), value: attr.nameColor, onChange: function (v) { set({ nameColor: v || '#111827' }); } },
217 { label: __('Tagline', 'blockenberg'), value: attr.taglineColor, onChange: function (v) { set({ taglineColor: v || '#6b7280' }); } },
218 { label: __('Description', 'blockenberg'), value: attr.descColor, onChange: function (v) { set({ descColor: v || '#374151' }); } },
219 { label: __('Feature Text', 'blockenberg'), value: attr.featureColor, onChange: function (v) { set({ featureColor: v || '#374151' }); } },
220 { label: __('Stars', 'blockenberg'), value: attr.starColor, onChange: function (v) { set({ starColor: v || '#f59e0b' }); } },
221 { label: __('Pricing Text', 'blockenberg'), value: attr.pricingColor, onChange: function (v) { set({ pricingColor: v || '#374151' }); } },
222 { label: __('Badge Background', 'blockenberg'), value: attr.badgeBg, onChange: function (v) { set({ badgeBg: v || '#fef3c7' }); } },
223 { label: __('Badge Text', 'blockenberg'), value: attr.badgeColor, onChange: function (v) { set({ badgeColor: v || '#92400e' }); } },
224 { label: __('CTA Button', 'blockenberg'), value: attr.ctaBg, onChange: function (v) { set({ ctaBg: v || '#6366f1' }); } },
225 { label: __('CTA Text', 'blockenberg'), value: attr.ctaColor, onChange: function (v) { set({ ctaColor: v || '#ffffff' }); } }
226 ]
227 })
228 );
229
230 return el('div', blockProps,
231 controls,
232 el('div', { className: 'bkbg-tlc-preview', style: cardStyle },
233 logoSection,
234 contentSection
235 )
236 );
237 },
238 save: function (props) {
239 var attr = props.attributes;
240 var useBlockProps = wp.blockEditor.useBlockProps;
241 var bp = (function () {
242 var tv = Object.assign({}, _tvf(attr.nameTypo, '--bktlc-nt-'), _tvf(attr.bodyTypo, '--bktlc-bt-'));
243 var parts = []; Object.keys(tv).forEach(function (k) { parts.push(k + ':' + tv[k]); });
244 return useBlockProps.save(parts.length ? { style: parts.join(';') } : {});
245 })();
246 return el('div', bp,
247 el('div', { className: 'bkbg-tlc-app', 'data-opts': JSON.stringify(attr) })
248 );
249 }
250 });
251 }() );
252