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 / icon-accordion / index.js

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

309 lines 18.7 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 useState = wp.element.useState;
5 var registerBlockType = wp.blocks.registerBlockType;
6 var InspectorControls = wp.blockEditor.InspectorControls;
7 var useBlockProps = wp.blockEditor.useBlockProps;
8 var RichText = wp.blockEditor.RichText;
9 var PanelColorSettings = wp.blockEditor.PanelColorSettings;
10 var PanelBody = wp.components.PanelBody;
11 var ToggleControl = wp.components.ToggleControl;
12 var RangeControl = wp.components.RangeControl;
13 var SelectControl = wp.components.SelectControl;
14 var TextControl = wp.components.TextControl;
15 var Button = wp.components.Button;
16 var ColorPicker = wp.components.ColorPicker;
17 var Popover = wp.components.Popover;
18 var Fragment = wp.element.Fragment;
19
20 var _TypographyControl, _typoCssVars;
21 function getTypographyControl() { return _TypographyControl || (_TypographyControl = window.bkbgTypographyControl); }
22 function getTypoCssVars() { return _typoCssVars || (_typoCssVars = window.bkbgTypoCssVars); }
23
24 var IP = function () { return window.bkbgIconPicker; };
25
26 /* Expand icons */
27 var expandIcons = {
28 chevron: { open: '', closed: '' },
29 plus: { open: '', closed: '+' },
30 arrow: { open: '', closed: '' },
31 caret: { open: '', closed: '' }
32 };
33
34 var BkbgColorSwatch = function (props) {
35 var _st = useState(false); var isOpen = _st[0]; var setOpen = _st[1];
36 return el(Fragment, {},
37 el('div', { style: { display: 'flex', alignItems: 'center', gap: '8px', marginBottom: '8px' } },
38 el('span', { style: { fontSize: '11px', fontWeight: 500, textTransform: 'uppercase', color: '#1e1e1e' } }, props.label),
39 el('button', {
40 type: 'button',
41 onClick: function () { setOpen(!isOpen); },
42 style: { width: '28px', height: '28px', borderRadius: '4px', border: '1px solid #ccc', background: props.value || '#ffffff', cursor: 'pointer', padding: 0 }
43 })
44 ),
45 isOpen && el(Popover, { onClose: function () { setOpen(false); } },
46 el('div', { style: { padding: '8px' } },
47 el(ColorPicker, { color: props.value, onChangeComplete: function (c) { props.onChange(c.hex); }, enableAlpha: true })
48 )
49 )
50 );
51 };
52
53 registerBlockType('blockenberg/icon-accordion', {
54 edit: function (props) {
55 var attr = props.attributes;
56 var set = props.setAttributes;
57 var items = attr.items || [];
58
59 var openState = useState(items.map(function (it) { return it.defaultOpen || false; }));
60 var openArr = openState[0];
61 var setOpenArr = openState[1];
62
63 function toggleOpen(idx) {
64 var next;
65 if (attr.allowMultiple) {
66 next = openArr.map(function (v, i) { return i === idx ? !v : v; });
67 } else {
68 next = openArr.map(function (v, i) { return i === idx ? !v : false; });
69 }
70 setOpenArr(next);
71 }
72
73 function updateItem(idx, field, val) {
74 var updated = items.map(function (it, i) {
75 if (i !== idx) return it;
76 var p = {}; p[field] = val;
77 return Object.assign({}, it, p);
78 });
79 set({ items: updated });
80 }
81
82 function addItem() {
83 var icons = ['💡', '🔥', '', '🎯', '🚀', '💎', '🌟', '⚙️'];
84 var bgs = ['#ede9fe', '#fce7f3', '#dcfce7', '#fef9c3', '#dbeafe', '#ffedd5'];
85 var cols = ['#6366f1', '#ec4899', '#16a34a', '#ca8a04', '#2563eb', '#ea580c'];
86 var n = items.length % icons.length;
87 set({ items: items.concat([{ icon: icons[n], iconType: 'custom-char', iconDashicon: '', iconImageUrl: '', iconBg: bgs[n], iconColor: cols[n], title: 'New Item', content: 'Describe this feature or answer here.', badge: '', defaultOpen: false }]) });
88 setOpenArr(openArr.concat([false]));
89 }
90
91 function removeItem(idx) {
92 set({ items: items.filter(function (_, i) { return i !== idx; }) });
93 setOpenArr(openArr.filter(function (_, i) { return i !== idx; }));
94 }
95
96 function moveItem(idx, dir) {
97 var arr = items.slice();
98 var oArr = openArr.slice();
99 var t = idx + dir;
100 if (t < 0 || t >= arr.length) return;
101 var tmp = arr[idx]; arr[idx] = arr[t]; arr[t] = tmp;
102 var oTmp = oArr[idx]; oArr[idx] = oArr[t]; oArr[t] = oTmp;
103 set({ items: arr }); setOpenArr(oArr);
104 }
105
106 var expIcons = expandIcons[attr.expandIcon] || expandIcons.chevron;
107 var iStyle = attr.iconStyle;
108 var iRadius = iStyle === 'circle' ? '50%' : iStyle === 'rounded' ? '10px' : '4px';
109
110 /* Accordion style helpers */
111 function panelStyle(isOpen) {
112 var base = {
113 background: attr.panelBg,
114 borderRadius: attr.borderRadius + 'px',
115 marginBottom: attr.gap + 'px',
116 overflow: 'hidden'
117 };
118 if (attr.accordionStyle === 'card') {
119 base.border = '1px solid ' + attr.panelBorder;
120 base.boxShadow = isOpen ? '0 4px 20px rgba(0,0,0,0.06)' : 'none';
121 }
122 if (attr.accordionStyle === 'filled') {
123 base.background = isOpen ? attr.accentColor : attr.panelBg;
124 }
125 if (attr.accordionStyle === 'minimal') {
126 base.borderBottom = '1px solid ' + attr.panelBorder;
127 base.borderRadius = '0';
128 base.background = 'transparent';
129 }
130 return base;
131 }
132
133 /* Inspector */
134 var inspector = el(InspectorControls, {},
135 el(PanelBody, { title: __('Accordion Settings', 'blockenberg'), initialOpen: true },
136 el(SelectControl, {
137 label: __('Style', 'blockenberg'),
138 value: attr.accordionStyle,
139 options: [
140 { label: 'Card (bordered)', value: 'card' },
141 { label: 'Filled (accent on open)', value: 'filled' },
142 { label: 'Minimal (underline)', value: 'minimal' },
143 { label: 'Default (flat)', value: 'default' }
144 ],
145 onChange: function (v) { set({ accordionStyle: v }); },
146 __nextHasNoMarginBottom: true
147 }),
148 el(SelectControl, {
149 label: __('Icon Shape', 'blockenberg'),
150 value: attr.iconStyle,
151 options: [
152 { label: 'Rounded Square', value: 'rounded' },
153 { label: 'Circle', value: 'circle' },
154 { label: 'Square', value: 'square' }
155 ],
156 onChange: function (v) { set({ iconStyle: v }); },
157 __nextHasNoMarginBottom: true
158 }),
159 el(RangeControl, {
160 label: __('Icon Size (px)', 'blockenberg'),
161 value: attr.iconSize, min: 28, max: 72,
162 onChange: function (v) { set({ iconSize: v }); },
163 __nextHasNoMarginBottom: true
164 }),
165 el(SelectControl, {
166 label: __('Expand Icon', 'blockenberg'),
167 value: attr.expandIcon,
168 options: [
169 { label: 'Chevron ▾', value: 'chevron' },
170 { label: 'Plus / Minus + −', value: 'plus' },
171 { label: 'Arrow ↓', value: 'arrow' },
172 { label: 'Caret ▶', value: 'caret' }
173 ],
174 onChange: function (v) { set({ expandIcon: v }); },
175 __nextHasNoMarginBottom: true
176 }),
177 el(ToggleControl, {
178 label: __('Allow Multiple Open', 'blockenberg'),
179 checked: attr.allowMultiple,
180 onChange: function (v) { set({ allowMultiple: v }); },
181 __nextHasNoMarginBottom: true
182 }),
183 el(RangeControl, {
184 label: __('Gap Between Items (px)', 'blockenberg'),
185 value: attr.gap, min: 0, max: 40,
186 onChange: function (v) { set({ gap: v }); },
187 __nextHasNoMarginBottom: true
188 }),
189 el(RangeControl, {
190 label: __('Border Radius (px)', 'blockenberg'),
191 value: attr.borderRadius, min: 0, max: 24,
192 onChange: function (v) { set({ borderRadius: v }); },
193 __nextHasNoMarginBottom: true
194 }),
195 el(RangeControl, {
196 label: __('Padding Top (px)', 'blockenberg'),
197 value: attr.paddingTop, min: 0, max: 200,
198 onChange: function (v) { set({ paddingTop: v }); },
199 __nextHasNoMarginBottom: true
200 }),
201 el(RangeControl, {
202 label: __('Padding Bottom (px)', 'blockenberg'),
203 value: attr.paddingBottom, min: 0, max: 200,
204 onChange: function (v) { set({ paddingBottom: v }); },
205 __nextHasNoMarginBottom: true
206 })
207 ),
208 el(PanelBody, { title: __('Typography', 'blockenberg'), initialOpen: false },
209 getTypographyControl() && el(getTypographyControl(), { label: __('Title', 'blockenberg'), typo: attr.titleTypo || {}, onChange: function (v) { set({ titleTypo: v }); } }),
210 getTypographyControl() && el(getTypographyControl(), { label: __('Content', 'blockenberg'), typo: attr.contentTypo || {}, onChange: function (v) { set({ contentTypo: v }); } })
211 ),
212 el(PanelColorSettings, {
213 title: __('Colors', 'blockenberg'), initialOpen: false,
214 colorSettings: [
215 { value: attr.bgColor, onChange: function (v) { set({ bgColor: v || '' }); }, label: __('Section Background', 'blockenberg') },
216 { value: attr.panelBg, onChange: function (v) { set({ panelBg: v || '#ffffff' }); }, label: __('Panel Background', 'blockenberg') },
217 { value: attr.panelBorder, onChange: function (v) { set({ panelBorder: v || '#e5e7eb' }); }, label: __('Panel Border', 'blockenberg') },
218 { value: attr.titleColor, onChange: function (v) { set({ titleColor: v || '#111827' }); }, label: __('Title', 'blockenberg') },
219 { value: attr.contentColor, onChange: function (v) { set({ contentColor: v || '#6b7280' }); }, label: __('Content', 'blockenberg') },
220 { value: attr.badgeBg, onChange: function (v) { set({ badgeBg: v || '#f3f4f6' }); }, label: __('Badge Background', 'blockenberg') },
221 { value: attr.badgeColor, onChange: function (v) { set({ badgeColor: v || '#374151' }); }, label: __('Badge Text', 'blockenberg') },
222 { value: attr.accentColor, onChange: function (v) { set({ accentColor: v || '#6366f1' }); }, label: __('Accent / Filled BG', 'blockenberg') },
223 { value: attr.expandColor, onChange: function (v) { set({ expandColor: v || '#9ca3af' }); }, label: __('Expand Icon', 'blockenberg') }
224 ]
225 })
226 );
227
228 /* Editor render */
229 var wrapEditorStyle = { background: attr.bgColor || '', paddingTop: attr.paddingTop + 'px', paddingBottom: attr.paddingBottom + 'px' };
230 var _tv = getTypoCssVars();
231 if (_tv) {
232 Object.assign(wrapEditorStyle, _tv(attr.titleTypo, '--bkbg-iac-tt-'));
233 Object.assign(wrapEditorStyle, _tv(attr.contentTypo, '--bkbg-iac-ct-'));
234 }
235 wrapEditorStyle['--bkbg-iac-tt-sz'] = (attr.titleFontSize || 15) + 'px';
236 wrapEditorStyle['--bkbg-iac-tt-w'] = attr.titleFontWeight || '700';
237 wrapEditorStyle['--bkbg-iac-tt-lh'] = attr.titleLineHeight || 1.3;
238 wrapEditorStyle['--bkbg-iac-ct-sz'] = (attr.contentFontSize || 14) + 'px';
239 wrapEditorStyle['--bkbg-iac-ct-w'] = attr.contentFontWeight || '400';
240 wrapEditorStyle['--bkbg-iac-ct-lh'] = attr.contentLineHeight || 1.7;
241
242 var wrap = el('div', { style: wrapEditorStyle },
243 items.map(function (it, idx) {
244 var isOpen = openArr[idx];
245 var isFilled = attr.accordionStyle === 'filled' && isOpen;
246 var tColor = isFilled ? '#ffffff' : attr.titleColor;
247 var cColor = isFilled ? 'rgba(255,255,255,0.8)' : attr.contentColor;
248
249 return el('div', { key: idx, style: panelStyle(isOpen) },
250 /* Header */
251 el('div', {
252 onClick: function () { toggleOpen(idx); },
253 style: { display: 'flex', alignItems: 'center', gap: '14px', padding: '14px 18px', cursor: 'pointer', userSelect: 'none', background: attr.headerBg || 'transparent' }
254 },
255 /* Icon */
256 el('div', {
257 style: {
258 width: attr.iconSize + 'px', height: attr.iconSize + 'px',
259 background: isFilled ? 'rgba(255,255,255,0.2)' : it.iconBg,
260 borderRadius: iRadius, display: 'flex', alignItems: 'center',
261 justifyContent: 'center', fontSize: (attr.iconSize * 0.5) + 'px',
262 flexShrink: 0
263 }
264 }, (it.iconType || 'custom-char') !== 'custom-char' ? IP().buildEditorIcon(it.iconType, it.icon, it.iconDashicon, it.iconImageUrl, it.iconDashiconColor) : (it.icon || '')),
265 /* Title + badge */
266 el('div', { style: { flex: 1, display: 'flex', alignItems: 'center', gap: '10px', minWidth: 0 } },
267 el('span', { className: 'bkbg-iac-title', style: { color: tColor, fontFamily: 'var(--bkbg-iac-tt-font-family, inherit)', fontSize: 'var(--bkbg-iac-tt-font-size-d, var(--bkbg-iac-tt-sz, 15px))', fontWeight: 'var(--bkbg-iac-tt-font-weight, var(--bkbg-iac-tt-w, 700))', lineHeight: 'var(--bkbg-iac-tt-line-height-d, var(--bkbg-iac-tt-lh, 1.3))' } }, it.title || ''),
268 it.badge && el('span', { style: { background: isFilled ? 'rgba(255,255,255,0.25)' : attr.badgeBg, color: isFilled ? '#fff' : attr.badgeColor, fontSize: '11px', fontWeight: '700', padding: '2px 8px', borderRadius: '999px' } }, it.badge)
269 ),
270 /* Expand icon */
271 el('span', { style: { color: isFilled ? 'rgba(255,255,255,0.7)' : attr.expandColor, fontSize: '18px', transition: 'transform 0.3s', transform: isOpen ? '' : (attr.expandIcon === 'chevron' ? 'rotate(-90deg)' : '') } }, isOpen ? expIcons.open : expIcons.closed)
272 ),
273 /* Content */
274 isOpen && el('div', { style: { padding: '0 18px 16px', paddingLeft: (18 + attr.iconSize + 14) + 'px' } },
275 el(RichText, {
276 tagName: 'p',
277 value: it.content,
278 onChange: function (v) { updateItem(idx, 'content', v); },
279 placeholder: __('Item content...', 'blockenberg'),
280 style: { color: cColor, margin: 0, fontFamily: 'var(--bkbg-iac-ct-font-family, inherit)', fontSize: 'var(--bkbg-iac-ct-font-size-d, var(--bkbg-iac-ct-sz, 14px))', fontWeight: 'var(--bkbg-iac-ct-font-weight, var(--bkbg-iac-ct-w, 400))', lineHeight: 'var(--bkbg-iac-ct-line-height-d, var(--bkbg-iac-ct-lh, 1.7))' }
281 })
282 ),
283 /* Item controls */
284 el('div', { style: { display: 'flex', gap: '6px', padding: '4px 18px 10px', flexWrap: 'wrap' } },
285 el(IP().IconPickerControl, { iconType: it.iconType || 'custom-char', customChar: it.icon, dashicon: it.iconDashicon || '', imageUrl: it.iconImageUrl || '',
286 onChangeType: function (v) { updateItem(idx, 'iconType', v); }, onChangeChar: function (v) { updateItem(idx, 'icon', v); },
287 onChangeDashicon: function (v) { updateItem(idx, 'iconDashicon', v); }, onChangeImageUrl: function (v) { updateItem(idx, 'iconImageUrl', v); } }),
288 el(TextControl, { label: 'Badge', value: it.badge, onChange: function (v) { updateItem(idx, 'badge', v); }, style: { width: '80px' }, placeholder: 'Optional', __nextHasNoMarginBottom: true }),
289 el(BkbgColorSwatch, { label: 'Icon BG', value: it.iconBg, onChange: function (v) { updateItem(idx, 'iconBg', v); } }),
290 idx > 0 && el(Button, { onClick: function () { moveItem(idx, -1); }, variant: 'tertiary', isSmall: true }, ''),
291 idx < items.length - 1 && el(Button, { onClick: function () { moveItem(idx, 1); }, variant: 'tertiary', isSmall: true }, ''),
292 el(Button, { onClick: function () { removeItem(idx); }, variant: 'link', isDestructive: true, isSmall: true }, __('Remove', 'blockenberg'))
293 )
294 );
295 }),
296 el(Button, { onClick: addItem, variant: 'primary', isSmall: true, style: { marginTop: '8px' } }, __('+ Add Item', 'blockenberg'))
297 );
298
299 return el(Fragment, {}, inspector, el('div', useBlockProps(), wrap));
300 },
301
302 save: function (props) {
303 return el('div', useBlockProps.save(),
304 el('div', { className: 'bkbg-iac-app', 'data-opts': JSON.stringify(props.attributes) })
305 );
306 }
307 });
308 }() );
309