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 / knowledge-check / index.js

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

340 lines 18.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 Fragment = wp.element.Fragment;
4 var useState = wp.element.useState;
5 var registerBlockType = wp.blocks.registerBlockType;
6 var useBlockProps = wp.blockEditor.useBlockProps;
7 var InspectorControls = wp.blockEditor.InspectorControls;
8 var PanelColorSettings = wp.blockEditor.PanelColorSettings;
9 var PanelBody = wp.components.PanelBody;
10 var TextControl = wp.components.TextControl;
11 var TextareaControl = wp.components.TextareaControl;
12 var ToggleControl = wp.components.ToggleControl;
13 var RangeControl = wp.components.RangeControl;
14 var SelectControl = wp.components.SelectControl;
15 var Button = wp.components.Button;
16 var __ = wp.i18n.__;
17
18 var _tc, _tv;
19 function getTypoControl() { return _tc || (_tc = window.bkbgTypographyControl); }
20 function getTypoCssVars() { return _tv || (_tv = window.bkbgTypoCssVars); }
21
22 // ── helpers ────────────────────────────────────────────────────────────────
23 function updateOption(opts, idx, field, val) {
24 return opts.map(function (o, i) {
25 if (i !== idx) return o;
26 var u = {}; u[field] = val;
27 return Object.assign({}, o, u);
28 });
29 }
30
31 // ── QuizPreview ─────────────────────────────────────────────────────────────
32 function QuizPreview(props) {
33 var a = props.attributes;
34 var setAttributes = props.setAttributes;
35
36 return el('div', {
37 className: 'bkbg-kc-block bkbg-kc-style-' + a.style,
38 style: { background: a.bgColor, borderColor: a.borderColor, borderRadius: a.borderRadius + 'px' }
39 },
40 // Header row
41 el('div', { className: 'bkbg-kc-header' },
42 a.showIcon && el('span', { className: 'bkbg-kc-icon' }, a.icon),
43 el('div', { className: 'bkbg-kc-meta' },
44 a.topic && el('span', { className: 'bkbg-kc-topic', style: { color: a.accentColor, borderColor: a.accentColor } }, a.topic),
45 a.difficulty && el('span', { className: 'bkbg-kc-difficulty' }, a.difficulty)
46 )
47 ),
48 // Question
49 el('p', {
50 className: 'bkbg-kc-question',
51 style: { color: a.questionColor },
52 contentEditable: true,
53 suppressContentEditableWarning: true,
54 onBlur: function (e) { setAttributes({ question: e.target.textContent }); }
55 }, a.question),
56 // Options
57 el('div', { className: 'bkbg-kc-options' },
58 a.options.map(function (opt, i) {
59 return el('label', {
60 key: i,
61 className: 'bkbg-kc-option' + (opt.correct ? ' is-correct-preview' : ''),
62 style: {
63 background: opt.correct ? a.correctBg : a.optionBg,
64 color: opt.correct ? a.correctColor : a.optionColor,
65 borderColor: opt.correct ? a.correctColor : a.optionBorderColor,
66 borderRadius: (a.borderRadius - 2) + 'px'
67 }
68 },
69 el('span', { className: 'bkbg-kc-opt-letter', style: { background: a.accentColor, color: '#fff' } },
70 String.fromCharCode(65 + i)
71 ),
72 el('span', { className: 'bkbg-kc-opt-text' }, opt.text),
73 opt.correct && el('span', { className: 'bkbg-kc-correct-badge' }, '')
74 );
75 })
76 ),
77 // Explanation preview
78 a.showExplanation && a.explanation && el('div', {
79 className: 'bkbg-kc-explanation',
80 style: { background: a.explanationBg, color: a.explanationColor, borderRadius: (a.borderRadius - 4) + 'px' }
81 },
82 el('strong', null, 'Explanation: '),
83 a.explanation
84 ),
85 // Reveal button (static in editor)
86 el('div', { className: 'bkbg-kc-actions' },
87 el('div', {
88 className: 'bkbg-kc-reveal-btn',
89 style: { background: a.revealBg, color: a.revealColor, borderRadius: (a.borderRadius - 2) + 'px' }
90 }, a.revealLabel)
91 )
92 );
93 }
94
95 // ── OptionEditor ────────────────────────────────────────────────────────────
96 function OptionEditor(props) {
97 var options = props.options;
98 var setAttributes = props.setAttributes;
99 var accentColor = props.accentColor;
100
101 function addOption() {
102 setAttributes({ options: options.concat([{ text: 'New option', correct: false }]) });
103 }
104
105 function remove(i) {
106 setAttributes({ options: options.filter(function (_, j) { return j !== i; }) });
107 }
108
109 function setCorrect(i) {
110 setAttributes({
111 options: options.map(function (o, j) {
112 return Object.assign({}, o, { correct: j === i });
113 })
114 });
115 }
116
117 return el('div', null,
118 options.map(function (opt, i) {
119 return el('div', { key: i, className: 'bkbg-kc-opt-editor', style: { borderLeftColor: opt.correct ? '#15803d' : accentColor } },
120 el('div', { className: 'bkbg-kc-opt-editor-row' },
121 el('span', { className: 'bkbg-kc-opt-editor-letter' }, String.fromCharCode(65 + i)),
122 el(TextControl, {
123 value: opt.text,
124 placeholder: __('Option text…'),
125 __nextHasNoMarginBottom: true,
126 onChange: function (v) { setAttributes({ options: updateOption(options, i, 'text', v) }); }
127 }),
128 el(Button, {
129 variant: opt.correct ? 'primary' : 'secondary',
130 isSmall: true,
131 onClick: function () { setCorrect(i); },
132 title: __('Mark as correct')
133 }, ''),
134 options.length > 2 && el(Button, {
135 variant: 'link',
136 isDestructive: true,
137 isSmall: true,
138 onClick: function () { remove(i); }
139 }, '')
140 )
141 );
142 }),
143 options.length < 6 && el(Button, {
144 variant: 'secondary',
145 onClick: addOption,
146 style: { marginTop: '6px' }
147 }, __('+ Add Option'))
148 );
149 }
150
151 // ── register ────────────────────────────────────────────────────────────────
152 registerBlockType('blockenberg/knowledge-check', {
153 edit: function (props) {
154 var a = props.attributes;
155 var setAttributes = props.setAttributes;
156
157 return el(Fragment, null,
158 el(InspectorControls, null,
159 el(PanelBody, { title: __('Question'), initialOpen: true },
160 el(SelectControl, {
161 label: __('Question type'),
162 value: a.questionType,
163 __nextHasNoMarginBottom: true,
164 options: [
165 { value: 'multiple', label: 'Multiple choice' },
166 { value: 'truefalse', label: 'True / False' }
167 ],
168 onChange: function (v) {
169 if (v === 'truefalse') {
170 setAttributes({
171 questionType: v,
172 options: [
173 { text: 'True', correct: true },
174 { text: 'False', correct: false }
175 ]
176 });
177 } else {
178 setAttributes({ questionType: v });
179 }
180 }
181 }),
182 el(TextareaControl, {
183 label: __('Question'),
184 value: a.question,
185 rows: 3,
186 __nextHasNoMarginBottom: true,
187 onChange: function (v) { setAttributes({ question: v }); }
188 }),
189 el(TextControl, {
190 label: __('Topic tag (optional)'),
191 value: a.topic,
192 __nextHasNoMarginBottom: true,
193 onChange: function (v) { setAttributes({ topic: v }); }
194 }),
195 el(TextControl, {
196 label: __('Difficulty label (optional)'),
197 value: a.difficulty,
198 __nextHasNoMarginBottom: true,
199 onChange: function (v) { setAttributes({ difficulty: v }); }
200 }),
201 el(TextControl, {
202 label: __('Icon (emoji)'),
203 value: a.icon,
204 __nextHasNoMarginBottom: true,
205 onChange: function (v) { setAttributes({ icon: v }); }
206 }),
207 el(ToggleControl, {
208 label: __('Show icon'),
209 checked: a.showIcon,
210 __nextHasNoMarginBottom: true,
211 onChange: function (v) { setAttributes({ showIcon: v }); }
212 })
213 ),
214 el(PanelBody, { title: __('Answer Options'), initialOpen: true },
215 el(OptionEditor, {
216 options: a.options,
217 setAttributes: setAttributes,
218 accentColor: a.accentColor
219 })
220 ),
221 el(PanelBody, { title: __('Explanation'), initialOpen: false },
222 el(ToggleControl, {
223 label: __('Show explanation after reveal'),
224 checked: a.showExplanation,
225 __nextHasNoMarginBottom: true,
226 onChange: function (v) { setAttributes({ showExplanation: v }); }
227 }),
228 el(TextareaControl, {
229 label: __('Explanation text'),
230 value: a.explanation,
231 rows: 4,
232 __nextHasNoMarginBottom: true,
233 onChange: function (v) { setAttributes({ explanation: v }); }
234 })
235 ),
236 el(PanelBody, { title: __('Interaction'), initialOpen: false },
237 el(TextControl, {
238 label: __('Reveal button label'),
239 value: a.revealLabel,
240 __nextHasNoMarginBottom: true,
241 onChange: function (v) { setAttributes({ revealLabel: v }); }
242 }),
243 el(TextControl, {
244 label: __('Correct label'),
245 value: a.correctLabel,
246 __nextHasNoMarginBottom: true,
247 onChange: function (v) { setAttributes({ correctLabel: v }); }
248 }),
249 el(TextControl, {
250 label: __('Incorrect label'),
251 value: a.incorrectLabel,
252 __nextHasNoMarginBottom: true,
253 onChange: function (v) { setAttributes({ incorrectLabel: v }); }
254 }),
255 el(ToggleControl, {
256 label: __('Allow retry'),
257 checked: a.allowRetry,
258 __nextHasNoMarginBottom: true,
259 onChange: function (v) { setAttributes({ allowRetry: v }); }
260 }),
261 el(ToggleControl, {
262 label: __('Shuffle options on load'),
263 checked: a.shuffleOptions,
264 __nextHasNoMarginBottom: true,
265 onChange: function (v) { setAttributes({ shuffleOptions: v }); }
266 })
267 ),
268 el(PanelBody, { title: __('Style'), initialOpen: false },
269 el(SelectControl, {
270 label: __('Block style'),
271 value: a.style,
272 __nextHasNoMarginBottom: true,
273 options: [
274 { value: 'card', label: 'Card (bordered)' },
275 { value: 'flat', label: 'Flat' },
276 { value: 'minimal', label: 'Minimal' }
277 ],
278 onChange: function (v) { setAttributes({ style: v }); }
279 }),
280 el(RangeControl, {
281 label: __('Border radius (px)'),
282 value: a.borderRadius,
283 min: 0, max: 24,
284 __nextHasNoMarginBottom: true,
285 onChange: function (v) { setAttributes({ borderRadius: v }); }
286 })
287 ),
288 el(PanelColorSettings, {
289 title: __('Colors'),
290 initialOpen: false,
291 colorSettings: [
292 { label: __('Background'), value: a.bgColor, onChange: function (v) { setAttributes({ bgColor: v || '#f8fafc' }); } },
293 { label: __('Border'), value: a.borderColor, onChange: function (v) { setAttributes({ borderColor: v || '#e2e8f0' }); } },
294 { label: __('Question text'), value: a.questionColor, onChange: function (v) { setAttributes({ questionColor: v || '#0f172a' }); } },
295 { label: __('Accent'), value: a.accentColor, onChange: function (v) { setAttributes({ accentColor: v || '#6366f1' }); } },
296 { label: __('Option background'), value: a.optionBg, onChange: function (v) { setAttributes({ optionBg: v || '#ffffff' }); } },
297 { label: __('Option text'), value: a.optionColor, onChange: function (v) { setAttributes({ optionColor: v || '#334155' }); } },
298 { label: __('Correct highlight bg'), value: a.correctBg, onChange: function (v) { setAttributes({ correctBg: v || '#dcfce7' }); } },
299 { label: __('Correct text'), value: a.correctColor, onChange: function (v) { setAttributes({ correctColor: v || '#15803d' }); } },
300 { label: __('Incorrect highlight bg'), value: a.incorrectBg, onChange: function (v) { setAttributes({ incorrectBg: v || '#fee2e2' }); } },
301 { label: __('Incorrect text'), value: a.incorrectColor, onChange: function (v) { setAttributes({ incorrectColor: v || '#b91c1c' }); } },
302 { label: __('Explanation bg'), value: a.explanationBg, onChange: function (v) { setAttributes({ explanationBg: v || '#eff6ff' }); } },
303 { label: __('Explanation text'), value: a.explanationColor, onChange: function (v) { setAttributes({ explanationColor: v || '#1e40af' }); } },
304 { label: __('Reveal button bg'), value: a.revealBg, onChange: function (v) { setAttributes({ revealBg: v || '#6366f1' }); } },
305 { label: __('Reveal button text'), value: a.revealColor, onChange: function (v) { setAttributes({ revealColor: v || '#ffffff' }); } }
306 ]
307 }),
308 el(PanelBody, { title: 'Typography', initialOpen: false },
309 getTypoControl() && el(getTypoControl(), { label: __('Question'), value: a.questionTypo || {}, onChange: function (v) { setAttributes({ questionTypo: v }); } }),
310 getTypoControl() && el(getTypoControl(), { label: __('Option'), value: a.optionTypo || {}, onChange: function (v) { setAttributes({ optionTypo: v }); } }),
311 getTypoControl() && el(getTypoControl(), { label: __('Result'), value: a.resultTypo || {}, onChange: function (v) { setAttributes({ resultTypo: v }); } })
312 ),
313
314 ),
315 el('div', useBlockProps((function () {
316 var _tvFn = getTypoCssVars();
317 var s = {};
318 if (_tvFn) {
319 Object.assign(s, _tvFn(a.questionTypo, '--bkbg-kc-q-'));
320 Object.assign(s, _tvFn(a.optionTypo, '--bkbg-kc-o-'));
321 Object.assign(s, _tvFn(a.resultTypo, '--bkbg-kc-r-'));
322 }
323 return { style: s };
324 })()),
325 el(QuizPreview, { attributes: a, setAttributes: setAttributes })
326 )
327 );
328 },
329
330 save: function (props) {
331 return el('div', useBlockProps.save(),
332 el('div', {
333 className: 'bkbg-kc-app',
334 'data-opts': JSON.stringify(props.attributes)
335 })
336 );
337 }
338 });
339 }() );
340