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 / feature-list / index.js

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

244 lines 16.2 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 useBlockProps = wp.blockEditor.useBlockProps;
7 var RichText = wp.blockEditor.RichText;
8 var PanelColorSettings = wp.blockEditor.PanelColorSettings;
9 var PanelBody = wp.components.PanelBody;
10 var ToggleControl = wp.components.ToggleControl;
11 var RangeControl = wp.components.RangeControl;
12 var SelectControl = wp.components.SelectControl;
13 var TextControl = wp.components.TextControl;
14 var Button = wp.components.Button;
15 var Fragment = wp.element.Fragment;
16
17 var _flTC, _flTV;
18 function _tc() { return _flTC || (_flTC = window.bkbgTypographyControl); }
19 function _tv() { return _flTV || (_flTV = window.bkbgTypoCssVars); }
20
21 /* Status icon sets */
22 var STATUS_ICONS = {
23 'color-check': { included: '', 'not-included': '', partial: '', 'coming-soon': '' },
24 'emoji': { included: '�
25 ', 'not-included': '', partial: '🔶', 'coming-soon': '🔜' },
26 'minimal': { included: '', 'not-included': '', partial: '~', 'coming-soon': '' }
27 };
28
29 function getStatusColor(status, a) {
30 if (status === 'included') return a.includedColor;
31 if (status === 'not-included') return a.notIncludedColor;
32 if (status === 'partial') return a.partialColor;
33 if (status === 'coming-soon') return a.comingSoonColor;
34 return a.labelColor;
35 }
36
37 function getStatusIcon(status, iconSet) {
38 var icons = STATUS_ICONS[iconSet] || STATUS_ICONS['color-check'];
39 return icons[status] || '';
40 }
41
42 registerBlockType('blockenberg/feature-list', {
43 edit: function (props) {
44 var attr = props.attributes;
45 var set = props.setAttributes;
46 var items = attr.items || [];
47
48 function updateItem(idx, field, val) {
49 var updated = items.map(function (it, i) {
50 if (i !== idx) return it;
51 var p = {}; p[field] = val;
52 return Object.assign({}, it, p);
53 });
54 set({ items: updated });
55 }
56
57 function addItem() {
58 set({ items: items.concat([{ label: 'New Feature', status: 'included', description: '', badge: '', group: '' }]) });
59 }
60
61 function removeItem(idx) { set({ items: items.filter(function (_, i) { return i !== idx; }) }); }
62
63 function moveItem(idx, dir) {
64 var arr = items.slice();
65 var to = idx + dir;
66 if (to < 0 || to >= arr.length) return;
67 var tmp = arr[idx]; arr[idx] = arr[to]; arr[to] = tmp;
68 set({ items: arr });
69 }
70
71 var inspector = el(InspectorControls, {},
72 el(PanelBody, { title: __('Style', 'blockenberg'), initialOpen: true },
73 el(SelectControl, {
74 label: __('List Style', 'blockenberg'), value: attr.listStyle,
75 options: [
76 { label: 'Bordered rows', value: 'bordered' },
77 { label: 'Striped rows', value: 'striped' },
78 { label: 'Cards', value: 'card' },
79 { label: 'Compact (no lines)', value: 'compact' }
80 ],
81 onChange: function (v) { set({ listStyle: v }); }, __nextHasNoMarginBottom: true
82 }),
83 el(SelectControl, {
84 label: __('Icon Set', 'blockenberg'), value: attr.iconSet,
85 options: [
86 { label: 'Color check / cross', value: 'color-check' },
87 { label: 'Emoji', value: 'emoji' },
88 { label: 'Minimal (dot / dash)', value: 'minimal' }
89 ],
90 onChange: function (v) { set({ iconSet: v }); }, __nextHasNoMarginBottom: true
91 }),
92 el(RangeControl, { label: __('Icon Size (px)', 'blockenberg'), value: attr.iconSize, min: 14, max: 32, onChange: function (v) { set({ iconSize: v }); }, __nextHasNoMarginBottom: true }),
93 el(RangeControl, { label: __('Border Radius (px)', 'blockenberg'), value: attr.borderRadius, min: 0, max: 24, onChange: function (v) { set({ borderRadius: v }); }, __nextHasNoMarginBottom: true })
94 ),
95 el(PanelBody, { title: __('Visibility', 'blockenberg'), initialOpen: false },
96 el(ToggleControl, { label: __('Show Heading', 'blockenberg'), checked: attr.showHeading, onChange: function (v) { set({ showHeading: v }); }, __nextHasNoMarginBottom: true }),
97 el(ToggleControl, { label: __('Show Descriptions', 'blockenberg'), checked: attr.showDesc, onChange: function (v) { set({ showDesc: v }); }, __nextHasNoMarginBottom: true }),
98 el(ToggleControl, { label: __('Show Badges', 'blockenberg'), checked: attr.showBadge, onChange: function (v) { set({ showBadge: v }); }, __nextHasNoMarginBottom: true }),
99 el(ToggleControl, { label: __('Show "Not Included" Items', 'blockenberg'), checked: attr.showNotIncluded, onChange: function (v) { set({ showNotIncluded: v }); }, __nextHasNoMarginBottom: true }),
100 el(ToggleControl, { label: __('Strike Through "Not Included"', 'blockenberg'), checked: attr.strikeNotIncluded, onChange: function (v) { set({ strikeNotIncluded: v }); }, __nextHasNoMarginBottom: true })
101 ),
102 el(PanelBody, { title: __('Items', 'blockenberg'), initialOpen: true },
103 items.map(function (it, idx) {
104 return el('div', { key: idx, style: { border: '1px solid #e5e7eb', borderRadius: '8px', padding: '10px', marginBottom: '10px' } },
105 el('div', { style: { display: 'flex', gap: '6px', alignItems: 'center', marginBottom: '8px' } },
106 el('span', { style: { fontWeight: '700', fontSize: '12px', color: getStatusColor(it.status, attr), minWidth: '16px' } }, getStatusIcon(it.status, attr.iconSet)),
107 el(SelectControl, {
108 value: it.status,
109 options: [
110 { label: '✓ Included', value: 'included' },
111 { label: '✗ Not included', value: 'not-included' },
112 { label: '◐ Partial', value: 'partial' },
113 { label: '⏱ Coming soon', value: 'coming-soon' }
114 ],
115 onChange: function (v) { updateItem(idx, 'status', v); },
116 style: { flex: 1, margin: 0 },
117 __nextHasNoMarginBottom: true
118 }),
119 el(Button, { onClick: function () { moveItem(idx, -1); }, isSmall: true, variant: 'tertiary', disabled: idx === 0 }, ''),
120 el(Button, { onClick: function () { moveItem(idx, 1); }, isSmall: true, variant: 'tertiary', disabled: idx === items.length - 1 }, ''),
121 el(Button, { onClick: function () { removeItem(idx); }, isSmall: true, variant: 'link', isDestructive: true }, '')
122 ),
123 el(TextControl, { label: __('Label', 'blockenberg'), value: it.label, onChange: function (v) { updateItem(idx, 'label', v); }, __nextHasNoMarginBottom: true }),
124 el(TextControl, { label: __('Description (optional)', 'blockenberg'), value: it.description, onChange: function (v) { updateItem(idx, 'description', v); }, __nextHasNoMarginBottom: true }),
125 el(TextControl, { label: __('Badge (optional)', 'blockenberg'), value: it.badge, onChange: function (v) { updateItem(idx, 'badge', v); }, __nextHasNoMarginBottom: true }),
126 el(TextControl, { label: __('Group Header (leave blank to skip)', 'blockenberg'), value: it.group, onChange: function (v) { updateItem(idx, 'group', v); }, __nextHasNoMarginBottom: true })
127 );
128 }),
129 el(Button, { onClick: addItem, variant: 'primary', isSmall: true }, __('+ Add Feature', 'blockenberg'))
130 ),
131 el(PanelBody, { title: __('Spacing', 'blockenberg'), initialOpen: false },
132 el(RangeControl, { label: __('Padding Top (px)', 'blockenberg'), value: attr.paddingTop, min: 0, max: 240, onChange: function (v) { set({ paddingTop: v }); }, __nextHasNoMarginBottom: true }),
133 el(RangeControl, { label: __('Padding Bottom (px)', 'blockenberg'), value: attr.paddingBottom, min: 0, max: 240, onChange: function (v) { set({ paddingBottom: v }); }, __nextHasNoMarginBottom: true })
134 ),
135
136 el( PanelBody, { title: __( 'Typography', 'blockenberg' ), initialOpen: false },
137 _tc() && el(_tc(), {
138 label: __('Heading Typography', 'blockenberg'),
139 value: attr.typoHeading,
140 onChange: function (v) { set({ typoHeading: v }); }
141 }),
142 _tc() && el(_tc(), {
143 label: __('Label Typography', 'blockenberg'),
144 value: attr.typoLabel,
145 onChange: function (v) { set({ typoLabel: v }); }
146 }),
147 _tc() && el(_tc(), {
148 label: __('Description Typography', 'blockenberg'),
149 value: attr.typoDesc,
150 onChange: function (v) { set({ typoDesc: v }); }
151 })
152 ),
153 el(PanelColorSettings, {
154 title: __('Colors', 'blockenberg'), initialOpen: false,
155 colorSettings: [
156 { value: attr.bgColor, onChange: function (v) { set({ bgColor: v || '' }); }, label: __('Section BG', 'blockenberg') },
157 { value: attr.itemBg, onChange: function (v) { set({ itemBg: v || '#ffffff' }); }, label: __('Row BG', 'blockenberg') },
158 { value: attr.stripeBg, onChange: function (v) { set({ stripeBg: v || '#f9fafb' }); }, label: __('Stripe BG', 'blockenberg') },
159 { value: attr.borderColor, onChange: function (v) { set({ borderColor: v || '#e5e7eb' }); }, label: __('Border', 'blockenberg') },
160 { value: attr.labelColor, onChange: function (v) { set({ labelColor: v || '#111827' }); }, label: __('Label', 'blockenberg') },
161 { value: attr.descColor, onChange: function (v) { set({ descColor: v || '#6b7280' }); }, label: __('Description', 'blockenberg') },
162 { value: attr.headingColor, onChange: function (v) { set({ headingColor: v || '#111827' }); }, label: __('Heading', 'blockenberg') },
163 { value: attr.includedColor, onChange: function (v) { set({ includedColor: v || '#10b981' }); }, label: __('Included Icon', 'blockenberg') },
164 { value: attr.notIncludedColor, onChange: function (v) { set({ notIncludedColor: v || '#d1d5db' }); }, label: __('Not Included Icon', 'blockenberg') },
165 { value: attr.partialColor, onChange: function (v) { set({ partialColor: v || '#f59e0b' }); }, label: __('Partial Icon', 'blockenberg') },
166 { value: attr.comingSoonColor, onChange: function (v) { set({ comingSoonColor: v || '#8b5cf6' }); }, label: __('Coming Soon Icon', 'blockenberg') },
167 { value: attr.badgeBg, onChange: function (v) { set({ badgeBg: v || '#ede9fe' }); }, label: __('Badge BG', 'blockenberg') },
168 { value: attr.badgeColor, onChange: function (v) { set({ badgeColor: v || '#7c3aed' }); }, label: __('Badge Text', 'blockenberg') }
169 ]
170 })
171 );
172
173 /* Editor preview */
174 var wrapStyle = Object.assign({
175 background: attr.bgColor || 'transparent',
176 paddingTop: attr.paddingTop + 'px',
177 paddingBottom: attr.paddingBottom + 'px'
178 }, _tv()(attr.typoHeading, '--bkbg-fl-hd-'), _tv()(attr.typoLabel, '--bkbg-fl-lb-'), _tv()(attr.typoDesc, '--bkbg-fl-ds-'));
179
180 var listWrapStyle = {
181 borderRadius: attr.listStyle === 'card' ? attr.borderRadius + 'px' : 0,
182 border: attr.listStyle === 'bordered' || attr.listStyle === 'card' ? ('1px solid ' + attr.borderColor) : 'none',
183 overflow: 'hidden'
184 };
185
186 var prevGroup = '';
187 var rowEls = [];
188 items.forEach(function (it, idx) {
189 if (!attr.showNotIncluded && it.status === 'not-included') return;
190
191 /* Group header */
192 if (it.group && it.group !== prevGroup) {
193 rowEls.push(el('div', { key: 'g-' + idx, style: { padding: '8px 16px 4px', fontSize: '11px', fontWeight: '700', textTransform: 'uppercase', letterSpacing: '0.08em', color: attr.descColor, background: attr.stripeBg, borderBottom: '1px solid ' + attr.borderColor } }, it.group));
194 prevGroup = it.group;
195 }
196
197 var isBordered = attr.listStyle === 'bordered' || attr.listStyle === 'card';
198 var isStriped = attr.listStyle === 'striped';
199 var rowBg = isStriped && idx % 2 === 1 ? attr.stripeBg : attr.itemBg;
200 var iconColor = getStatusColor(it.status, attr);
201 var icon = getStatusIcon(it.status, attr.iconSet);
202 var isNotIncl = it.status === 'not-included';
203
204 rowEls.push(el('div', {
205 key: idx,
206 style: {
207 display: 'flex', alignItems: 'center', gap: '12px', padding: '12px 16px',
208 background: rowBg, borderBottom: isBordered && idx < items.length - 1 ? ('1px solid ' + attr.borderColor) : 'none',
209 marginBottom: attr.listStyle === 'card' ? '0' : (attr.gap > 0 ? attr.gap + 'px' : '0')
210 }
211 },
212 el('span', { style: { color: iconColor, fontSize: attr.iconSize + 'px', flexShrink: 0, width: attr.iconSize + 'px', textAlign: 'center', fontWeight: '700' } }, icon),
213 el('div', { style: { flex: 1 } },
214 el('div', { className: 'bkbg-fl-label', style: { color: isNotIncl ? attr.notIncludedColor : attr.labelColor, textDecoration: isNotIncl && attr.strikeNotIncluded ? 'line-through' : 'none' } }, it.label),
215 attr.showDesc && it.description && el('div', { className: 'bkbg-fl-desc', style: { color: attr.descColor } }, it.description)
216 ),
217 attr.showBadge && it.badge && el('span', { style: { background: attr.badgeBg, color: attr.badgeColor, fontSize: '11px', fontWeight: '700', padding: '3px 8px', borderRadius: '20px', flexShrink: 0 } }, it.badge)
218 ));
219 });
220
221 return el(Fragment, {},
222 inspector,
223 el('div', useBlockProps({ className: 'bkbg-fl-wrap', style: wrapStyle }),
224 attr.showHeading && el(RichText, {
225 tagName: 'h3', className: 'bkbg-fl-heading', value: attr.heading,
226 onChange: function (v) { set({ heading: v }); },
227 placeholder: __('Heading…', 'blockenberg'),
228 style: { color: attr.headingColor, marginBottom: '20px' }
229 }),
230 el('div', { style: listWrapStyle }, rowEls)
231 )
232 );
233 },
234
235 save: function (props) {
236 var el2 = wp.element.createElement;
237 var ubp = wp.blockEditor.useBlockProps;
238 return el2('div', ubp.save(),
239 el2('div', { className: 'bkbg-fl-app', 'data-opts': JSON.stringify(props.attributes) })
240 );
241 }
242 });
243 }() );
244