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

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

378 lines 20.4 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 __ = wp.i18n.__;
5 var registerBlockType = wp.blocks.registerBlockType;
6 var InspectorControls = wp.blockEditor.InspectorControls;
7 var RichText = wp.blockEditor.RichText;
8 var useBlockProps = wp.blockEditor.useBlockProps;
9 var PanelColorSettings = wp.blockEditor.PanelColorSettings;
10 var PanelBody = wp.components.PanelBody;
11 var RangeControl = wp.components.RangeControl;
12 var SelectControl = wp.components.SelectControl;
13 var ToggleControl = wp.components.ToggleControl;
14 var TextControl = wp.components.TextControl;
15 var Button = wp.components.Button;
16 var TextareaControl = wp.components.TextareaControl;
17
18 var _faTC, _faTV;
19 function _tc() { return _faTC || (_faTC = window.bkbgTypographyControl); }
20 function _tv(t, p) { return (_faTV || (_faTV = window.bkbgTypoCssVars)) ? _faTV(t, p) : {}; }
21
22 function wrapStyle(a) {
23 return Object.assign({
24 '--bkbg-fa-bg': a.bgColor,
25 '--bkbg-fa-heading-c': a.headingColor,
26 '--bkbg-fa-sub-c': a.subColor,
27 '--bkbg-fa-q-c': a.questionColor,
28 '--bkbg-fa-a-c': a.answerColor,
29 '--bkbg-fa-accent': a.accentColor,
30 '--bkbg-fa-border': a.borderColor,
31 '--bkbg-fa-card-bg': a.cardBg,
32 '--bkbg-fa-icon-c': a.iconColor,
33 '--bkbg-fa-pt': a.paddingTop + 'px',
34 '--bkbg-fa-pb': a.paddingBottom + 'px',
35 '--bkbg-fa-q-pad': a.questionPadding + 'px',
36 '--bkbg-fa-radius': a.cardRadius + 'px',
37 '--bkbg-fa-icon-sz': a.iconSize + 'px',
38 '--bkbg-fa-sect-gap': a.sectionGap + 'px',
39 '--bkbg-fa-item-gap': a.itemGap + 'px',
40 '--bkbg-fa-max-w': a.maxWidth + 'px',
41 },
42 _tv(a.typoHeading || {}, '--bkbg-fa-hd-'),
43 _tv(a.typoSubheading || {}, '--bkbg-fa-sh-'),
44 _tv(a.typoQuestion || {}, '--bkbg-fa-qt-'),
45 _tv(a.typoAnswer || {}, '--bkbg-fa-an-')
46 );
47 }
48
49 function getIconEl(iconStyle, isOpen) {
50 if (iconStyle === 'chevron') {
51 return el('span', { className: 'bkbg-fa-icon dashicons ' + (isOpen ? 'dashicons-arrow-up-alt2' : 'dashicons-arrow-down-alt2') });
52 }
53 if (iconStyle === 'arrow') {
54 return el('span', { className: 'bkbg-fa-icon dashicons ' + (isOpen ? 'dashicons-arrow-up' : 'dashicons-arrow-down') });
55 }
56 // plus/minus
57 return el('span', { className: 'bkbg-fa-icon bkbg-fa-plus-icon', style: { fontSize: 'var(--bkbg-fa-icon-sz, 20px)' } }, isOpen ? '' : '+');
58 }
59
60 registerBlockType('blockenberg/faq-accordion', {
61 title: __('FAQ Accordion', 'blockenberg'),
62 icon: 'editor-help',
63 category: 'bkbg-business',
64
65 edit: function (props) {
66 var a = props.attributes;
67 var set = props.setAttributes;
68
69 var expandedState = useState(0);
70 var expanded = expandedState[0];
71 var setExpanded = expandedState[1];
72
73 var editingState = useState(null);
74 var editing = editingState[0];
75 var setEditing = editingState[1];
76
77 var styleOptions = [
78 { label: __('Minimal (lines)', 'blockenberg'), value: 'minimal' },
79 { label: __('Bordered', 'blockenberg'), value: 'bordered' },
80 { label: __('Card', 'blockenberg'), value: 'card' },
81 { label: __('Boxed', 'blockenberg'), value: 'boxed' },
82 { label: __('Dark', 'blockenberg'), value: 'dark' },
83 ];
84 var iconStyleOptions = [
85 { label: __('Plus / Minus', 'blockenberg'), value: 'plus' },
86 { label: __('Chevron (arrow)', 'blockenberg'), value: 'chevron' },
87 { label: __('Arrow', 'blockenberg'), value: 'arrow' },
88 ];
89 var alignOptions = [
90 { label: __('Left', 'blockenberg'), value: 'left' },
91 { label: __('Center', 'blockenberg'), value: 'center' },
92 ];
93
94 function addItem() {
95 var ns = a.items.concat([{ question: __('New Question?', 'blockenberg'), answer: __('Your answer here.', 'blockenberg'), defaultOpen: false }]);
96 set({ items: ns });
97 setEditing(ns.length - 1);
98 }
99 function removeItem(i) {
100 set({ items: a.items.filter(function (_, idx) { return idx !== i; }) });
101 if (editing === i) setEditing(null);
102 }
103 function moveItem(i, dir) {
104 var ni = i + dir;
105 if (ni < 0 || ni >= a.items.length) return;
106 var ns = a.items.slice();
107 var tmp = ns[i]; ns[i] = ns[ni]; ns[ni] = tmp;
108 set({ items: ns });
109 }
110 function updateItem(i, key, val) {
111 var ns = a.items.slice();
112 ns[i] = Object.assign({}, ns[i]);
113 ns[i][key] = val;
114 set({ items: ns });
115 }
116
117 var blockProps = useBlockProps({
118 className: 'bkbg-fa-wrap bkbg-fa-style--' + a.style + ' bkbg-fa-heading-align--' + a.headingAlign,
119 style: wrapStyle(a)
120 });
121
122 var inspector = el(InspectorControls, {},
123 el(PanelBody, { title: __('Section Header', 'blockenberg'), initialOpen: true },
124 el(ToggleControl, {
125 label: __('Show Heading', 'blockenberg'),
126 checked: a.showHeading,
127 __nextHasNoMarginBottom: true,
128 onChange: function (v) { set({ showHeading: v }); }
129 }),
130 a.showHeading && el(TextControl, {
131 label: __('Heading', 'blockenberg'),
132 value: a.heading,
133 onChange: function (v) { set({ heading: v }); }
134 }),
135 el(ToggleControl, {
136 label: __('Show Subheading', 'blockenberg'),
137 checked: a.showSubheading,
138 __nextHasNoMarginBottom: true,
139 onChange: function (v) { set({ showSubheading: v }); }
140 }),
141 a.showSubheading && el(TextControl, {
142 label: __('Subheading', 'blockenberg'),
143 value: a.subheading,
144 onChange: function (v) { set({ subheading: v }); }
145 }),
146 el(SelectControl, {
147 label: __('Heading Alignment', 'blockenberg'),
148 value: a.headingAlign,
149 options: alignOptions,
150 onChange: function (v) { set({ headingAlign: v }); }
151 })
152 ),
153 el(PanelBody, { title: __('Style & Behavior', 'blockenberg'), initialOpen: false },
154 el(SelectControl, {
155 label: __('Accordion Style', 'blockenberg'),
156 value: a.style,
157 options: styleOptions,
158 onChange: function (v) { set({ style: v }); }
159 }),
160 el(SelectControl, {
161 label: __('Icon Style', 'blockenberg'),
162 value: a.iconStyle,
163 options: iconStyleOptions,
164 onChange: function (v) { set({ iconStyle: v }); }
165 }),
166 el(ToggleControl, {
167 label: __('Show Icon', 'blockenberg'),
168 checked: a.showIcon,
169 __nextHasNoMarginBottom: true,
170 onChange: function (v) { set({ showIcon: v }); }
171 }),
172 el(ToggleControl, {
173 label: __('Show Numbers', 'blockenberg'),
174 checked: a.showNumbers,
175 __nextHasNoMarginBottom: true,
176 onChange: function (v) { set({ showNumbers: v }); }
177 }),
178 el(ToggleControl, {
179 label: __('Allow Multiple Open', 'blockenberg'),
180 checked: a.allowMultiple,
181 __nextHasNoMarginBottom: true,
182 onChange: function (v) { set({ allowMultiple: v }); }
183 }),
184 el(ToggleControl, {
185 label: __('Add Schema.org Markup', 'blockenberg'),
186 checked: a.addSchema,
187 __nextHasNoMarginBottom: true,
188 onChange: function (v) { set({ addSchema: v }); }
189 }),
190 el(RangeControl, {
191 label: __('Max Content Width (px)', 'blockenberg'),
192 value: a.maxWidth,
193 onChange: function (v) { set({ maxWidth: v }); },
194 min: 400, max: 1100, step: 20
195 }),
196 el(RangeControl, {
197 label: __('Card Radius (px)', 'blockenberg'),
198 value: a.cardRadius,
199 onChange: function (v) { set({ cardRadius: v }); },
200 min: 0, max: 24
201 }),
202 el(RangeControl, {
203 label: __('Item Gap (px)', 'blockenberg'),
204 value: a.itemGap,
205 onChange: function (v) { set({ itemGap: v }); },
206 min: 0, max: 32
207 }),
208 el(RangeControl, {
209 label: __('Padding Top (px)', 'blockenberg'),
210 value: a.paddingTop,
211 onChange: function (v) { set({ paddingTop: v }); },
212 min: 0, max: 180
213 }),
214 el(RangeControl, {
215 label: __('Padding Bottom (px)', 'blockenberg'),
216 value: a.paddingBottom,
217 onChange: function (v) { set({ paddingBottom: v }); },
218 min: 0, max: 180
219 })
220 ),
221 el(PanelBody, { title: __('Typography', 'blockenberg'), initialOpen: false },
222 _tc() && el(_tc(), { label: 'Heading', typo: a.typoHeading || {}, onChange: function (v) { set({ typoHeading: v }); } }),
223 _tc() && el(_tc(), { label: 'Subheading', typo: a.typoSubheading || {}, onChange: function (v) { set({ typoSubheading: v }); } }),
224 _tc() && el(_tc(), { label: 'Question', typo: a.typoQuestion || {}, onChange: function (v) { set({ typoQuestion: v }); } }),
225 _tc() && el(_tc(), { label: 'Answer', typo: a.typoAnswer || {}, onChange: function (v) { set({ typoAnswer: v }); } }),
226 el(RangeControl, { label: __('Icon Size (px)', 'blockenberg'), value: a.iconSize, onChange: function (v) { set({ iconSize: v }); }, min: 12, max: 32 }),
227 el(RangeControl, { label: __('Section-to-FAQ Gap (px)', 'blockenberg'), value: a.sectionGap, onChange: function (v) { set({ sectionGap: v }); }, min: 16, max: 96 })
228 ),
229 el(PanelBody, { title: __('FAQ Items', 'blockenberg') + ' (' + a.items.length + ')', initialOpen: false },
230 a.items.map(function (item, i) {
231 var isEditing = editing === i;
232 return el('div', {
233 key: i,
234 className: 'bkbg-fa-item-ctrl' + (isEditing ? ' bkbg-fa-item-ctrl--open' : ''),
235 style: { marginBottom: '8px' }
236 },
237 el('div', {
238 className: 'bkbg-fa-item-head',
239 style: { cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'space-between' },
240 onClick: function () { setEditing(isEditing ? null : i); }
241 },
242 el('span', { style: { fontSize: '13px', fontWeight: 600, flex: 1, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' } },
243 (i + 1) + '. ' + (item.question || __('(empty)', 'blockenberg'))
244 ),
245 el('span', { style: { color: isEditing ? '#6c3fb5' : '#94a3b8' } }, isEditing ? '' : '')
246 ),
247 isEditing && el('div', { className: 'bkbg-fa-item-body', style: { marginTop: '10px' } },
248 el(TextControl, {
249 label: __('Question', 'blockenberg'),
250 value: item.question,
251 onChange: function (v) { updateItem(i, 'question', v); }
252 }),
253 el(TextareaControl, {
254 label: __('Answer', 'blockenberg'),
255 value: item.answer,
256 rows: 4,
257 onChange: function (v) { updateItem(i, 'answer', v); }
258 }),
259 el(ToggleControl, {
260 label: __('Open by default', 'blockenberg'),
261 checked: item.defaultOpen,
262 __nextHasNoMarginBottom: true,
263 onChange: function (v) { updateItem(i, 'defaultOpen', v); }
264 }),
265 el('div', { style: { display: 'flex', gap: '6px', marginTop: '8px' } },
266 el(Button, { onClick: function () { moveItem(i, -1); }, variant: 'secondary', isSmall: true, disabled: i === 0 }, ''),
267 el(Button, { onClick: function () { moveItem(i, 1); }, variant: 'secondary', isSmall: true, disabled: i === a.items.length - 1 }, ''),
268 el(Button, { onClick: function () { removeItem(i); }, variant: 'tertiary', isDestructive: true, isSmall: true }, __('Remove', 'blockenberg'))
269 )
270 )
271 );
272 }),
273 el(Button, {
274 onClick: addItem,
275 variant: 'primary',
276 isSmall: true,
277 style: { marginTop: '8px', width: '100%', justifyContent: 'center' }
278 }, __('+ Add Question', 'blockenberg'))
279 ),
280 el(PanelColorSettings, {
281 title: __('Colors', 'blockenberg'),
282 initialOpen: false,
283 colorSettings: [
284 { label: __('Background', 'blockenberg'), value: a.bgColor, onChange: function (v) { set({ bgColor: v || '#ffffff' }); } },
285 { label: __('Accent Color', 'blockenberg'), value: a.accentColor, onChange: function (v) { set({ accentColor: v || '#6c3fb5' }); } },
286 { label: __('Heading', 'blockenberg'), value: a.headingColor, onChange: function (v) { set({ headingColor: v || '#0f172a' }); } },
287 { label: __('Subheading', 'blockenberg'), value: a.subColor, onChange: function (v) { set({ subColor: v || '#64748b' }); } },
288 { label: __('Question Text', 'blockenberg'), value: a.questionColor, onChange: function (v) { set({ questionColor: v || '#0f172a' }); } },
289 { label: __('Answer Text', 'blockenberg'), value: a.answerColor, onChange: function (v) { set({ answerColor: v || '#475569' }); } },
290 { label: __('Icon', 'blockenberg'), value: a.iconColor, onChange: function (v) { set({ iconColor: v || '#6c3fb5' }); } },
291 { label: __('Border', 'blockenberg'), value: a.borderColor, onChange: function (v) { set({ borderColor: v || '#e2e8f0' }); } },
292 { label: __('Card Background', 'blockenberg'), value: a.cardBg, onChange: function (v) { set({ cardBg: v || '#ffffff' }); } },
293 ]
294 })
295 );
296
297 // Canvas accordion
298 var canvas = el('div', blockProps,
299 (a.showHeading || a.showSubheading) && el('div', { className: 'bkbg-fa-header' },
300 a.showHeading && el('h2', { className: 'bkbg-fa-heading' }, a.heading),
301 a.showSubheading && el('p', { className: 'bkbg-fa-sub' }, a.subheading)
302 ),
303 el('div', { className: 'bkbg-fa-list' },
304 a.items.map(function (item, i) {
305 var isOpen = expanded === i;
306 return el('div', {
307 key: i,
308 className: 'bkbg-fa-item' + (isOpen ? ' bkbg-fa-item--open' : ''),
309 onClick: function () { setExpanded(isOpen ? null : i); }
310 },
311 el('div', { className: 'bkbg-fa-question' },
312 a.showNumbers && el('span', { className: 'bkbg-fa-num' }, ('0' + (i + 1)).slice(-2)),
313 el('span', { className: 'bkbg-fa-question-text' }, item.question),
314 a.showIcon && getIconEl(a.iconStyle, isOpen)
315 ),
316 isOpen && el('div', { className: 'bkbg-fa-answer' },
317 el('p', {}, item.answer)
318 )
319 );
320 })
321 )
322 );
323
324 return el('div', {}, inspector, canvas);
325 },
326
327 save: function (props) {
328 var a = props.attributes;
329 var blockProps = useBlockProps.save({
330 className: 'bkbg-fa-wrap bkbg-fa-style--' + a.style + ' bkbg-fa-heading-align--' + a.headingAlign,
331 style: wrapStyle(a),
332 'data-allow-multiple': a.allowMultiple ? '1' : '0'
333 });
334
335 var schemaItems = a.addSchema ? JSON.stringify(a.items.map(function (item) {
336 return { '@type': 'Question', name: item.question, acceptedAnswer: { '@type': 'Answer', text: item.answer } };
337 })) : null;
338
339 return el('div', blockProps,
340 a.addSchema && el('script', {
341 type: 'application/ld+json',
342 dangerouslySetInnerHTML: {
343 __html: JSON.stringify({
344 '@context': 'https://schema.org',
345 '@type': 'FAQPage',
346 mainEntity: a.items.map(function (item) {
347 return { '@type': 'Question', name: item.question, acceptedAnswer: { '@type': 'Answer', text: item.answer } };
348 })
349 })
350 }
351 }),
352 (a.showHeading || a.showSubheading) && el('div', { className: 'bkbg-fa-header' },
353 a.showHeading && el('h2', { className: 'bkbg-fa-heading' }, a.heading),
354 a.showSubheading && el('p', { className: 'bkbg-fa-sub' }, a.subheading)
355 ),
356 el('div', { className: 'bkbg-fa-list' },
357 a.items.map(function (item, i) {
358 return el('div', {
359 key: i,
360 className: 'bkbg-fa-item' + (item.defaultOpen ? ' bkbg-fa-item--open' : ''),
361 'data-faq-item': '1'
362 },
363 el('button', { className: 'bkbg-fa-question', 'aria-expanded': item.defaultOpen ? 'true' : 'false' },
364 a.showNumbers && el('span', { className: 'bkbg-fa-num' }, ('0' + (i + 1)).slice(-2)),
365 el('span', { className: 'bkbg-fa-question-text' }, item.question),
366 a.showIcon && el('span', { className: 'bkbg-fa-icon', 'aria-hidden': 'true' })
367 ),
368 el('div', { className: 'bkbg-fa-answer', hidden: !item.defaultOpen },
369 el('p', {}, item.answer)
370 )
371 );
372 })
373 )
374 );
375 }
376 });
377 }() );
378