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
← All changes | blocks/flashcard/index.js +330 -130 2.0.10 → 2.0.14 View file →
@@ -1,7 +1,7 @@
1 1 ( function () {
2 2 var el = wp.element.createElement;
3 - var useState = wp.element.useState;
3 + var useEffect = wp.element.useEffect;
4 4 var Fragment = wp.element.Fragment;
5 5 var registerBlockType = wp.blocks.registerBlockType;
6 6 var __ = wp.i18n.__;
7 7 var InspectorControls = wp.blockEditor.InspectorControls;
@@ -8,12 +8,10 @@
8 8 var PanelColorSettings = wp.blockEditor.PanelColorSettings;
9 9 var useBlockProps = wp.blockEditor.useBlockProps;
10 10 var PanelBody = wp.components.PanelBody;
11 11 var RangeControl = wp.components.RangeControl;
12 - var SelectControl = wp.components.SelectControl;
13 12 var TextControl = wp.components.TextControl;
14 13 var ToggleControl = wp.components.ToggleControl;
15 - var Button = wp.components.Button;
16 14
17 15 var _flTC, _flTV;
18 16 function _tc() { return _flTC || (_flTC = window.bkbgTypographyControl); }
19 17 function _tv() { return _flTV || (_flTV = window.bkbgTypoCssVars); }
@@ -25,23 +23,129 @@
25 23 { id: '4', front: 'What is H₂O?', back: 'Water', known: false },
26 24 { id: '5', front: 'What year did WWII end?', back: '1945', known: false }
27 25 ];
28 26
29 - registerBlockType('blockenberg/flashcard', {
30 - edit: function (props) {
27 + /**
28 + * Cards used to be stored as a JSON string. Accept both shapes so older
29 + * posts keep working and can migrate cleanly.
30 + */
31 + function normalizeCards( cards ) {
32 + if ( Array.isArray( cards ) ) {
33 + return cards;
34 + }
35 + if ( typeof cards === 'string' && cards ) {
36 + try {
37 + var parsed = JSON.parse( cards );
38 + if ( Array.isArray( parsed ) ) {
39 + return parsed;
40 + }
41 + } catch ( e ) { /* fall through */ }
42 + }
43 + return DEFAULT_CARDS.slice();
44 + }
45 +
46 + /**
47 + * Only frontend-relevant block attributes — never dump Advanced (bkbg*)
48 + * attrs or other editor-only noise into data-opts. A full JSON.stringify(a)
49 + * breaks block validation whenever attributes are added or defaults change.
50 + */
51 + function buildFrontendOpts( a ) {
52 + return {
53 + title: a.title,
54 + subtitle: a.subtitle,
55 + showTitle: a.showTitle,
56 + showSubtitle: a.showSubtitle,
57 + cards: normalizeCards( a.cards ),
58 + showProgress: a.showProgress,
59 + showShuffleBtn: a.showShuffleBtn,
60 + showKnownBtn: a.showKnownBtn,
61 + enableKeyboardShortcuts: a.enableKeyboardShortcuts !== false,
62 + showKeyboardHint: a.showKeyboardHint,
63 + cardHeight: a.cardHeight,
64 + accentColor: a.accentColor,
65 + cardFrontBg: a.cardFrontBg,
66 + cardBackBg: a.cardBackBg,
67 + cardBackColor: a.cardBackColor,
68 + knownColor: a.knownColor,
69 + sectionBg: a.sectionBg,
70 + cardBg: a.cardBg,
71 + titleColor: a.titleColor,
72 + subtitleColor: a.subtitleColor,
73 + labelColor: a.labelColor,
74 + titleFontSize: a.titleFontSize,
75 + titleFontWeight: a.titleFontWeight,
76 + titleLineHeight: a.titleLineHeight,
77 + subtitleFontSize: a.subtitleFontSize,
78 + subtitleFontWeight: a.subtitleFontWeight,
79 + subtitleLineHeight: a.subtitleLineHeight,
80 + cardFontSize: a.cardFontSize,
81 + cardFontWeight: a.cardFontWeight,
82 + cardLineHeight: a.cardLineHeight,
83 + contentMaxWidth: a.contentMaxWidth,
84 + typoTitle: a.typoTitle || {},
85 + typoSubtitle: a.typoSubtitle || {},
86 + typoCard: a.typoCard || {}
87 + };
88 + }
89 +
90 + function saveElement( a ) {
91 + return el( 'div', useBlockProps.save(),
92 + el( 'div', {
93 + className: 'bkbg-fc-app',
94 + 'data-opts': JSON.stringify( buildFrontendOpts( a ) )
95 + } )
96 + );
97 + }
98 +
99 + /**
100 + * Legacy save: dumped every attribute (including Advanced bkbg* fields).
101 + * Kept for deprecation matching / migrate path. Still fragile — new saves
102 + * must never use this.
103 + */
104 + function legacySaveAllAttrs( props ) {
105 + var a = props.attributes;
106 + var opts = {};
107 + Object.keys( a ).forEach( function ( key ) {
108 + // Strip injected Advanced attrs so we can match older dumps that
109 + // predate / postdate Advanced inconsistently less often.
110 + if ( key.indexOf( 'bkbg' ) === 0 ) {
111 + return;
112 + }
113 + opts[ key ] = a[ key ];
114 + } );
115 + // Legacy posts stored cards as a JSON string inside data-opts.
116 + if ( Array.isArray( opts.cards ) ) {
117 + opts.cards = JSON.stringify( opts.cards );
118 + }
119 + return el( 'div', useBlockProps.save(),
120 + el( 'div', {
121 + className: 'bkbg-fc-app',
122 + 'data-opts': JSON.stringify( opts )
123 + } )
124 + );
125 + }
126 +
127 + registerBlockType( 'blockenberg/flashcard', {
128 + edit: function ( props ) {
31 129 var a = props.attributes;
32 130 var setAttributes = props.setAttributes;
33 131 var keyboardShortcutsEnabled = a.enableKeyboardShortcuts !== false;
34 - var wrapStyle = Object.assign({},
35 - _tv()(a.typoTitle, '--bkbg-flash-tt-'),
36 - _tv()(a.typoSubtitle, '--bkbg-flash-ts-'),
37 - _tv()(a.typoCard, '--bkbg-flash-tc-')
132 + var wrapStyle = Object.assign( {},
133 + _tv()( a.typoTitle, '--bkbg-flash-tt-' ),
134 + _tv()( a.typoSubtitle, '--bkbg-flash-ts-' ),
135 + _tv()( a.typoCard, '--bkbg-flash-tc-' )
38 136 );
39 - var blockProps = useBlockProps({ className: 'bkbg-fc-editor-wrap', style: wrapStyle });
137 + var blockProps = useBlockProps( { className: 'bkbg-fc-editor-wrap', style: wrapStyle } );
40 138
41 - var cards;
42 - try { cards = JSON.parse(a.cards); } catch (e) { cards = DEFAULT_CARDS; }
139 + var cards = normalizeCards( a.cards );
43 140
141 + // One-time migrate: string cards → array attribute.
142 + useEffect( function () {
143 + if ( typeof a.cards === 'string' ) {
144 + setAttributes( { cards: normalizeCards( a.cards ) } );
145 + }
146 + }, [] );
147 +
44 148 var containerStyle = {
45 149 background: a.sectionBg,
46 150 borderRadius: '12px',
47 151 padding: '28px',
@@ -64,24 +168,30 @@
64 168 position: 'relative'
65 169 };
66 170
67 171 function addCard() {
68 - var newCards = cards.concat([{ id: Date.now().toString(), front: 'New question', back: 'Answer', known: false }]);
69 - setAttributes({ cards: JSON.stringify(newCards) });
172 + setAttributes( {
173 + cards: cards.concat( [ { id: Date.now().toString(), front: 'New question', back: 'Answer', known: false } ] )
174 + } );
70 175 }
71 176
72 - function removeCard(id) {
73 - setAttributes({ cards: JSON.stringify(cards.filter(function (c) { return c.id !== id; })) });
177 + function removeCard( id ) {
178 + setAttributes( { cards: cards.filter( function ( c ) { return c.id !== id; } ) } );
74 179 }
75 180
76 - function updateCard(id, field, value) {
77 - var updated = cards.map(function (c) {
78 - if (c.id !== id) return c;
79 - var n = {}; Object.keys(c).forEach(function (k) { n[k] = c[k]; }); n[field] = value; return n;
80 - });
81 - setAttributes({ cards: JSON.stringify(updated) });
181 + function updateCard( id, field, value ) {
182 + var updated = cards.map( function ( c ) {
183 + if ( c.id !== id ) return c;
184 + var n = {};
185 + Object.keys( c ).forEach( function ( k ) { n[ k ] = c[ k ]; } );
186 + n[ field ] = value;
187 + return n;
188 + } );
189 + setAttributes( { cards: updated } );
82 190 }
83 191
192 + var TC = _tc();
193 +
84 194 return el(
85 195 Fragment,
86 196 null,
87 197 el(
@@ -88,59 +198,59 @@
88 198 InspectorControls,
89 199 null,
90 200 el(
91 201 PanelBody,
92 - { title: __('Content', 'blockenberg'), initialOpen: true },
93 - el(TextControl, {
94 - label: __('Title', 'blockenberg'),
202 + { title: __( 'Content', 'blockenberg' ), initialOpen: true },
203 + el( TextControl, {
204 + label: __( 'Title', 'blockenberg' ),
95 205 value: a.title,
96 - onChange: function (v) { setAttributes({ title: v }); }
97 - }),
98 - el(TextControl, {
99 - label: __('Subtitle', 'blockenberg'),
206 + onChange: function ( v ) { setAttributes( { title: v } ); }
207 + } ),
208 + el( TextControl, {
209 + label: __( 'Subtitle', 'blockenberg' ),
100 210 value: a.subtitle,
101 - onChange: function (v) { setAttributes({ subtitle: v }); }
102 - }),
103 - el(ToggleControl, {
104 - label: __('Show Title', 'blockenberg'),
211 + onChange: function ( v ) { setAttributes( { subtitle: v } ); }
212 + } ),
213 + el( ToggleControl, {
214 + label: __( 'Show Title', 'blockenberg' ),
105 215 checked: a.showTitle,
106 - onChange: function (v) { setAttributes({ showTitle: v }); },
216 + onChange: function ( v ) { setAttributes( { showTitle: v } ); },
107 217 __nextHasNoMarginBottom: true
108 - }),
109 - el(ToggleControl, {
110 - label: __('Show Subtitle', 'blockenberg'),
218 + } ),
219 + el( ToggleControl, {
220 + label: __( 'Show Subtitle', 'blockenberg' ),
111 221 checked: a.showSubtitle,
112 - onChange: function (v) { setAttributes({ showSubtitle: v }); },
222 + onChange: function ( v ) { setAttributes( { showSubtitle: v } ); },
113 223 __nextHasNoMarginBottom: true
114 - })
224 + } )
115 225 ),
116 226 el(
117 227 PanelBody,
118 - { title: __('Cards', 'blockenberg'), initialOpen: true },
119 - cards.map(function (card) {
228 + { title: __( 'Cards', 'blockenberg' ), initialOpen: true },
229 + cards.map( function ( card ) {
120 230 return el(
121 231 'div',
122 232 { key: card.id, style: { background: '#f9f9f9', borderRadius: '8px', padding: '12px', marginBottom: '10px', border: '1px solid #e5e7eb' } },
123 - el(TextControl, {
124 - label: __('Front', 'blockenberg'),
233 + el( TextControl, {
234 + label: __( 'Front', 'blockenberg' ),
125 235 value: card.front,
126 - onChange: function (v) { updateCard(card.id, 'front', v); }
127 - }),
128 - el(TextControl, {
129 - label: __('Back', 'blockenberg'),
236 + onChange: function ( v ) { updateCard( card.id, 'front', v ); }
237 + } ),
238 + el( TextControl, {
239 + label: __( 'Back', 'blockenberg' ),
130 240 value: card.back,
131 - onChange: function (v) { updateCard(card.id, 'back', v); }
132 - }),
241 + onChange: function ( v ) { updateCard( card.id, 'back', v ); }
242 + } ),
133 243 el(
134 244 'button',
135 245 {
136 246 style: { background: '#fee2e2', color: '#991b1b', border: 'none', borderRadius: '4px', padding: '4px 10px', cursor: 'pointer', fontSize: '12px' },
137 - onClick: function () { removeCard(card.id); }
247 + onClick: function () { removeCard( card.id ); }
138 248 },
139 - __('Remove', 'blockenberg')
249 + __( 'Remove', 'blockenberg' )
140 250 )
141 251 );
142 - }),
252 + } ),
143 253 el(
144 254 'button',
145 255 {
146 256 style: { background: a.accentColor, color: '#fff', border: 'none', borderRadius: '6px', padding: '8px 16px', cursor: 'pointer', width: '100%', fontWeight: '600' },
@@ -145,86 +255,86 @@
145 255 {
146 256 style: { background: a.accentColor, color: '#fff', border: 'none', borderRadius: '6px', padding: '8px 16px', cursor: 'pointer', width: '100%', fontWeight: '600' },
147 257 onClick: addCard
148 258 },
149 - __('+ Add Card', 'blockenberg')
259 + __( '+ Add Card', 'blockenberg' )
150 260 )
151 261 ),
152 262 el(
153 263 PanelBody,
154 - { title: __('Options', 'blockenberg'), initialOpen: false },
155 - el(ToggleControl, {
156 - label: __('Show Progress Bar', 'blockenberg'),
264 + { title: __( 'Options', 'blockenberg' ), initialOpen: false },
265 + el( ToggleControl, {
266 + label: __( 'Show Progress Bar', 'blockenberg' ),
157 267 checked: a.showProgress,
158 - onChange: function (v) { setAttributes({ showProgress: v }); },
268 + onChange: function ( v ) { setAttributes( { showProgress: v } ); },
159 269 __nextHasNoMarginBottom: true
160 - }),
161 - el(ToggleControl, {
162 - label: __('Show Shuffle Button', 'blockenberg'),
270 + } ),
271 + el( ToggleControl, {
272 + label: __( 'Show Shuffle Button', 'blockenberg' ),
163 273 checked: a.showShuffleBtn,
164 - onChange: function (v) { setAttributes({ showShuffleBtn: v }); },
274 + onChange: function ( v ) { setAttributes( { showShuffleBtn: v } ); },
165 275 __nextHasNoMarginBottom: true
166 - }),
167 - el(ToggleControl, {
168 - label: __('Show "Mark Known" Button', 'blockenberg'),
276 + } ),
277 + el( ToggleControl, {
278 + label: __( 'Show "Mark Known" Button', 'blockenberg' ),
169 279 checked: a.showKnownBtn,
170 - onChange: function (v) { setAttributes({ showKnownBtn: v }); },
280 + onChange: function ( v ) { setAttributes( { showKnownBtn: v } ); },
171 281 __nextHasNoMarginBottom: true
172 - }),
173 - el(ToggleControl, {
174 - label: __('Enable keyboard shortcuts', 'blockenberg'),
282 + } ),
283 + el( ToggleControl, {
284 + label: __( 'Enable keyboard shortcuts', 'blockenberg' ),
175 285 checked: keyboardShortcutsEnabled,
176 - onChange: function (v) { setAttributes({ enableKeyboardShortcuts: v }); },
286 + onChange: function ( v ) { setAttributes( { enableKeyboardShortcuts: v } ); },
177 287 __nextHasNoMarginBottom: true
178 - }),
179 - el(ToggleControl, {
180 - label: __('Show Keyboard Hint', 'blockenberg'),
288 + } ),
289 + el( ToggleControl, {
290 + label: __( 'Show Keyboard Hint', 'blockenberg' ),
181 291 checked: a.showKeyboardHint,
182 - onChange: function (v) { setAttributes({ showKeyboardHint: v }); },
292 + onChange: function ( v ) { setAttributes( { showKeyboardHint: v } ); },
183 293 __nextHasNoMarginBottom: true
184 - }),
185 - el(RangeControl, {
186 - label: __('Card Height (px)', 'blockenberg'),
294 + } ),
295 + el( RangeControl, {
296 + label: __( 'Card Height (px)', 'blockenberg' ),
187 297 value: a.cardHeight,
188 298 min: 120,
189 299 max: 500,
190 - onChange: function (v) { setAttributes({ cardHeight: v }); }
191 - })
300 + onChange: function ( v ) { setAttributes( { cardHeight: v } ); }
301 + } )
192 302 ),
193 303 el(
194 304 PanelBody,
195 - { title: __('Typography', 'blockenberg'), initialOpen: false },
196 - _tc() && _tc()({ label: __('Title', 'blockenberg'), typo: a.typoTitle, onChange: function (v) { setAttributes({ typoTitle: v }); } }),
197 - _tc() && _tc()({ label: __('Subtitle', 'blockenberg'), typo: a.typoSubtitle, onChange: function (v) { setAttributes({ typoSubtitle: v }); } }),
198 - _tc() && _tc()({ label: __('Card Text', 'blockenberg'), typo: a.typoCard, onChange: function (v) { setAttributes({ typoCard: v }); } })
305 + { title: __( 'Typography', 'blockenberg' ), initialOpen: false },
306 + TC && el( TC, { label: __( 'Title', 'blockenberg' ), value: a.typoTitle || {}, onChange: function ( v ) { setAttributes( { typoTitle: v } ); } } ),
307 + TC && el( TC, { label: __( 'Subtitle', 'blockenberg' ), value: a.typoSubtitle || {}, onChange: function ( v ) { setAttributes( { typoSubtitle: v } ); } } ),
308 + TC && el( TC, { label: __( 'Card Text', 'blockenberg' ), value: a.typoCard || {}, onChange: function ( v ) { setAttributes( { typoCard: v } ); } } )
199 309 ),
200 310 el(
201 311 PanelBody,
202 - { title: __('Appearance', 'blockenberg'), initialOpen: false },
203 - el(RangeControl, {
204 - label: __('Max Width (px)', 'blockenberg'),
312 + { title: __( 'Appearance', 'blockenberg' ), initialOpen: false },
313 + el( RangeControl, {
314 + label: __( 'Max Width (px)', 'blockenberg' ),
205 315 value: a.contentMaxWidth,
206 316 min: 320,
207 317 max: 1200,
208 - onChange: function (v) { setAttributes({ contentMaxWidth: v }); }
209 - })
318 + onChange: function ( v ) { setAttributes( { contentMaxWidth: v } ); }
319 + } )
210 320 ),
211 321 el(
212 322 PanelColorSettings,
213 323 {
214 - title: __('Colors', 'blockenberg'),
324 + title: __( 'Colors', 'blockenberg' ),
215 325 initialOpen: false,
216 326 colorSettings: [
217 - { value: a.accentColor, onChange: function (v) { setAttributes({ accentColor: v || '#6366f1' }); }, label: __('Accent Color', 'blockenberg') },
218 - { value: a.cardFrontBg, onChange: function (v) { setAttributes({ cardFrontBg: v || '#ffffff' }); }, label: __('Card Front Background', 'blockenberg') },
219 - { value: a.cardBackBg, onChange: function (v) { setAttributes({ cardBackBg: v || '#4f46e5' }); }, label: __('Card Back Background', 'blockenberg') },
220 - { value: a.cardBackColor, onChange: function (v) { setAttributes({ cardBackColor: v || '#ffffff' }); }, label: __('Card Back Text', 'blockenberg') },
221 - { value: a.knownColor, onChange: function (v) { setAttributes({ knownColor: v || '#22c55e' }); }, label: __('Known Color', 'blockenberg') },
222 - { value: a.sectionBg, onChange: function (v) { setAttributes({ sectionBg: v || '#f5f3ff' }); }, label: __('Section Background', 'blockenberg') },
223 - { value: a.cardBg, onChange: function (v) { setAttributes({ cardBg: v || '#ffffff' }); }, label: __('Controls Background', 'blockenberg') },
224 - { value: a.titleColor, onChange: function (v) { setAttributes({ titleColor: v || '#3730a3' }); }, label: __('Title Color', 'blockenberg') },
225 - { value: a.subtitleColor, onChange: function (v) { setAttributes({ subtitleColor: v || '#6b7280' }); }, label: __('Subtitle Color', 'blockenberg') },
226 - { value: a.labelColor, onChange: function (v) { setAttributes({ labelColor: v || '#374151' }); }, label: __('Label Color', 'blockenberg') }
327 + { value: a.accentColor, onChange: function ( v ) { setAttributes( { accentColor: v || '#6366f1' } ); }, label: __( 'Accent Color', 'blockenberg' ) },
328 + { value: a.cardFrontBg, onChange: function ( v ) { setAttributes( { cardFrontBg: v || '#ffffff' } ); }, label: __( 'Card Front Background', 'blockenberg' ) },
329 + { value: a.cardBackBg, onChange: function ( v ) { setAttributes( { cardBackBg: v || '#4f46e5' } ); }, label: __( 'Card Back Background', 'blockenberg' ) },
330 + { value: a.cardBackColor, onChange: function ( v ) { setAttributes( { cardBackColor: v || '#ffffff' } ); }, label: __( 'Card Back Text', 'blockenberg' ) },
331 + { value: a.knownColor, onChange: function ( v ) { setAttributes( { knownColor: v || '#22c55e' } ); }, label: __( 'Known Color', 'blockenberg' ) },
332 + { value: a.sectionBg, onChange: function ( v ) { setAttributes( { sectionBg: v || '#f5f3ff' } ); }, label: __( 'Section Background', 'blockenberg' ) },
333 + { value: a.cardBg, onChange: function ( v ) { setAttributes( { cardBg: v || '#ffffff' } ); }, label: __( 'Controls Background', 'blockenberg' ) },
334 + { value: a.titleColor, onChange: function ( v ) { setAttributes( { titleColor: v || '#3730a3' } ); }, label: __( 'Title Color', 'blockenberg' ) },
335 + { value: a.subtitleColor, onChange: function ( v ) { setAttributes( { subtitleColor: v || '#6b7280' } ); }, label: __( 'Subtitle Color', 'blockenberg' ) },
336 + { value: a.labelColor, onChange: function ( v ) { setAttributes( { labelColor: v || '#374151' } ); }, label: __( 'Label Color', 'blockenberg' ) }
227 337 ]
228 338 }
229 339 )
230 340 ),
@@ -233,41 +343,41 @@
233 343 blockProps,
234 344 el(
235 345 'div',
236 346 { style: containerStyle },
237 - a.showTitle && el('div', {
347 + a.showTitle && el( 'div', {
238 348 className: 'bkbg-fc-title',
239 349 style: { color: a.titleColor, marginBottom: '6px' }
240 - }, a.title),
241 - a.showSubtitle && el('div', {
350 + }, a.title ),
351 + a.showSubtitle && el( 'div', {
242 352 className: 'bkbg-fc-subtitle',
243 353 style: { color: a.subtitleColor, marginBottom: '20px' }
244 - }, a.subtitle),
245 - a.showProgress && el('div', {
354 + }, a.subtitle ),
355 + a.showProgress && el( 'div', {
246 356 style: { marginBottom: '16px' }
247 357 },
248 - el('div', { style: { display: 'flex', justifyContent: 'space-between', fontSize: '13px', color: a.labelColor, marginBottom: '6px' } },
249 - el('span', null, __('Progress', 'blockenberg')),
250 - el('span', null, '0 / ' + cards.length + ' known')
358 + el( 'div', { style: { display: 'flex', justifyContent: 'space-between', fontSize: '13px', color: a.labelColor, marginBottom: '6px' } },
359 + el( 'span', null, __( 'Progress', 'blockenberg' ) ),
360 + el( 'span', null, '0 / ' + cards.length + ' known' )
251 361 ),
252 - el('div', { style: { height: '8px', background: '#e5e7eb', borderRadius: '4px' } },
253 - el('div', { style: { height: '100%', width: '0%', background: a.accentColor, borderRadius: '4px' } })
362 + el( 'div', { style: { height: '8px', background: '#e5e7eb', borderRadius: '4px' } },
363 + el( 'div', { style: { height: '100%', width: '0%', background: a.accentColor, borderRadius: '4px' } } )
254 364 )
255 365 ),
256 - el('div', { className: 'bkbg-fc-front', style: cardPreviewStyle },
257 - el('span', null, cards[0] ? cards[0].front : 'No cards yet')
366 + el( 'div', { className: 'bkbg-fc-front', style: cardPreviewStyle },
367 + el( 'span', null, cards[ 0 ] ? cards[ 0 ].front : 'No cards yet' )
258 368 ),
259 - el('div', { style: { display: 'flex', justifyContent: 'center', gap: '12px', marginTop: '16px', flexWrap: 'wrap' } },
260 - el('button', { style: { background: '#e5e7eb', color: '#374151', border: 'none', borderRadius: '8px', padding: '10px 20px', cursor: 'pointer', fontSize: '20px' } }, '←'),
261 - a.showKnownBtn && el('button', { style: { background: a.knownColor, color: '#fff', border: 'none', borderRadius: '8px', padding: '10px 18px', cursor: 'pointer', fontWeight: '600' } }, '✓ Know it'),
262 - el('button', { style: { background: '#e5e7eb', color: '#374151', border: 'none', borderRadius: '8px', padding: '10px 20px', cursor: 'pointer', fontSize: '20px' } }, '→')
369 + el( 'div', { style: { display: 'flex', justifyContent: 'center', gap: '12px', marginTop: '16px', flexWrap: 'wrap' } },
370 + el( 'button', { style: { background: '#e5e7eb', color: '#374151', border: 'none', borderRadius: '8px', padding: '10px 20px', cursor: 'pointer', fontSize: '20px' } }, '←' ),
371 + a.showKnownBtn && el( 'button', { style: { background: a.knownColor, color: '#fff', border: 'none', borderRadius: '8px', padding: '10px 18px', cursor: 'pointer', fontWeight: '600' } }, '✓ Know it' ),
372 + el( 'button', { style: { background: '#e5e7eb', color: '#374151', border: 'none', borderRadius: '8px', padding: '10px 20px', cursor: 'pointer', fontSize: '20px' } }, '→' )
263 373 ),
264 - el('div', { style: { textAlign: 'center', marginTop: '10px', display: 'flex', gap: '10px', justifyContent: 'center', flexWrap: 'wrap' } },
265 - el('span', { style: { color: a.subtitleColor, fontSize: '13px' } }, '1 / ' + cards.length),
266 - a.showShuffleBtn && el('button', { style: { background: 'transparent', border: '1px solid ' + a.accentColor, color: a.accentColor, borderRadius: '6px', padding: '4px 12px', cursor: 'pointer', fontSize: '12px' } }, '⇌ Shuffle'),
267 - el('button', { style: { background: 'transparent', border: '1px solid #e5e7eb', color: a.subtitleColor, borderRadius: '6px', padding: '4px 12px', cursor: 'pointer', fontSize: '12px' } }, '↺ Reset')
374 + el( 'div', { style: { textAlign: 'center', marginTop: '10px', display: 'flex', gap: '10px', justifyContent: 'center', flexWrap: 'wrap' } },
375 + el( 'span', { style: { color: a.subtitleColor, fontSize: '13px' } }, '1 / ' + cards.length ),
376 + a.showShuffleBtn && el( 'button', { style: { background: 'transparent', border: '1px solid ' + a.accentColor, color: a.accentColor, borderRadius: '6px', padding: '4px 12px', cursor: 'pointer', fontSize: '12px' } }, '⇌ Shuffle' ),
377 + el( 'button', { style: { background: 'transparent', border: '1px solid #e5e7eb', color: a.subtitleColor, borderRadius: '6px', padding: '4px 12px', cursor: 'pointer', fontSize: '12px' } }, '↺ Reset' )
268 378 ),
269 - keyboardShortcutsEnabled && a.showKeyboardHint && el('div', { style: { textAlign: 'center', marginTop: '12px', color: a.subtitleColor, fontSize: '12px' } },
379 + keyboardShortcutsEnabled && a.showKeyboardHint && el( 'div', { style: { textAlign: 'center', marginTop: '12px', color: a.subtitleColor, fontSize: '12px' } },
270 380 '← → Arrow keys to navigate • Space to flip'
271 381 )
272 382 )
273 383 )
@@ -272,16 +382,106 @@
272 382 )
273 383 )
274 384 );
275 385 },
276 - save: function (props) {
277 - var a = props.attributes;
278 - var blockProps = wp.blockEditor.useBlockProps.save();
279 - return el('div', blockProps,
280 - el('div', {
281 - className: 'bkbg-fc-app',
282 - 'data-opts': JSON.stringify(a)
283 - })
284 - );
285 - }
286 - });
386 +
387 + save: function ( props ) {
388 + return saveElement( props.attributes );
389 + },
390 +
391 + deprecated: [
392 + {
393 + attributes: {
394 + title: { type: 'string', default: 'Flashcard Deck' },
395 + subtitle: { type: 'string', default: 'Click a card to reveal the answer' },
396 + showTitle: { type: 'boolean', default: true },
397 + showSubtitle: { type: 'boolean', default: true },
398 + cards: { type: 'string' },
399 + showProgress: { type: 'boolean', default: true },
400 + showShuffleBtn: { type: 'boolean', default: true },
401 + showKnownBtn: { type: 'boolean', default: true },
402 + enableKeyboardShortcuts: { type: 'boolean', default: true },
403 + showKeyboardHint: { type: 'boolean', default: true },
404 + cardHeight: { type: 'number', default: 220 },
405 + accentColor: { type: 'string', default: '#6366f1' },
406 + cardFrontBg: { type: 'string', default: '#ffffff' },
407 + cardBackBg: { type: 'string', default: '#4f46e5' },
408 + cardBackColor: { type: 'string', default: '#ffffff' },
409 + knownColor: { type: 'string', default: '#22c55e' },
410 + sectionBg: { type: 'string', default: '#f5f3ff' },
411 + cardBg: { type: 'string', default: '#ffffff' },
412 + titleColor: { type: 'string', default: '#3730a3' },
413 + subtitleColor: { type: 'string', default: '#6b7280' },
414 + labelColor: { type: 'string', default: '#374151' },
415 + titleFontSize: { type: 'number', default: 26 },
416 + titleFontWeight: { type: 'string', default: '700' },
417 + titleLineHeight: { type: 'number', default: 1.2 },
418 + subtitleFontSize: { type: 'number', default: 15 },
419 + subtitleFontWeight: { type: 'string', default: '400' },
420 + subtitleLineHeight: { type: 'number', default: 1.5 },
421 + cardFontSize: { type: 'number', default: 18 },
422 + cardFontWeight: { type: 'string', default: '500' },
423 + cardLineHeight: { type: 'number', default: 1.5 },
424 + contentMaxWidth: { type: 'number', default: 660 },
425 + typoTitle: { type: 'object', default: {} },
426 + typoSubtitle: { type: 'object', default: {} },
427 + typoCard: { type: 'object', default: {} }
428 + },
429 + isEligible: function ( attributes ) {
430 + return typeof attributes.cards === 'string';
431 + },
432 + migrate: function ( attributes ) {
433 + var next = {};
434 + Object.keys( attributes ).forEach( function ( k ) { next[ k ] = attributes[ k ]; } );
435 + next.cards = normalizeCards( attributes.cards );
436 + return next;
437 + },
438 + save: legacySaveAllAttrs
439 + },
440 + {
441 + attributes: {
442 + title: { type: 'string', default: 'Flashcard Deck' },
443 + subtitle: { type: 'string', default: 'Click a card to reveal the answer' },
444 + showTitle: { type: 'boolean', default: true },
445 + showSubtitle: { type: 'boolean', default: true },
446 + cards: { type: 'array' },
447 + showProgress: { type: 'boolean', default: true },
448 + showShuffleBtn: { type: 'boolean', default: true },
449 + showKnownBtn: { type: 'boolean', default: true },
450 + enableKeyboardShortcuts: { type: 'boolean', default: true },
451 + showKeyboardHint: { type: 'boolean', default: true },
452 + cardHeight: { type: 'number', default: 220 },
453 + accentColor: { type: 'string', default: '#6366f1' },
454 + cardFrontBg: { type: 'string', default: '#ffffff' },
455 + cardBackBg: { type: 'string', default: '#4f46e5' },
456 + cardBackColor: { type: 'string', default: '#ffffff' },
457 + knownColor: { type: 'string', default: '#22c55e' },
458 + sectionBg: { type: 'string', default: '#f5f3ff' },
459 + cardBg: { type: 'string', default: '#ffffff' },
460 + titleColor: { type: 'string', default: '#3730a3' },
461 + subtitleColor: { type: 'string', default: '#6b7280' },
462 + labelColor: { type: 'string', default: '#374151' },
463 + titleFontSize: { type: 'number', default: 26 },
464 + titleFontWeight: { type: 'string', default: '700' },
465 + titleLineHeight: { type: 'number', default: 1.2 },
466 + subtitleFontSize: { type: 'number', default: 15 },
467 + subtitleFontWeight: { type: 'string', default: '400' },
468 + subtitleLineHeight: { type: 'number', default: 1.5 },
469 + cardFontSize: { type: 'number', default: 18 },
470 + cardFontWeight: { type: 'string', default: '500' },
471 + cardLineHeight: { type: 'number', default: 1.5 },
472 + contentMaxWidth: { type: 'number', default: 660 },
473 + typoTitle: { type: 'object', default: {} },
474 + typoSubtitle: { type: 'object', default: {} },
475 + typoCard: { type: 'object', default: {} }
476 + },
477 + migrate: function ( attributes ) {
478 + var next = {};
479 + Object.keys( attributes ).forEach( function ( k ) { next[ k ] = attributes[ k ]; } );
480 + next.cards = normalizeCards( attributes.cards );
481 + return next;
482 + },
483 + save: legacySaveAllAttrs
484 + }
485 + ]
486 + } );
287 487 }() );