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 / post-grid / index.js

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

524 lines 26.5 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 __ = wp.i18n.__;
5 var useState = wp.element.useState;
6 var useEffect = wp.element.useEffect;
7 var registerBlockType = wp.blocks.registerBlockType;
8 var InspectorControls = wp.blockEditor.InspectorControls;
9 var useBlockProps = wp.blockEditor.useBlockProps;
10 var PanelBody = wp.components.PanelBody;
11 var PanelColorSettings = wp.blockEditor.PanelColorSettings;
12 var ToggleControl = wp.components.ToggleControl;
13 var RangeControl = wp.components.RangeControl;
14 var SelectControl = wp.components.SelectControl;
15 var TextControl = wp.components.TextControl;
16 var Spinner = wp.components.Spinner;
17 var Notice = wp.components.Notice;
18
19 var _tc; function getTypoControl() { return _tc || (_tc = window.bkbgTypographyControl); }
20 var _tv; function getTypoCssVars() { return _tv || (_tv = window.bkbgTypoCssVars); }
21
22 var cardStyleOptions = [
23 { label: 'Shadow', value: 'shadow' },
24 { label: 'Outlined', value: 'outlined' },
25 { label: 'Flat', value: 'flat' },
26 { label: 'Elevated', value: 'elevated' }
27 ];
28
29 var hoverEffectOptions = [
30 { label: 'Zoom', value: 'zoom' },
31 { label: 'Fade', value: 'fade' },
32 { label: 'Lift', value: 'lift' },
33 { label: 'None', value: 'none' }
34 ];
35
36 var orderbyOptions = [
37 { label: 'Date', value: 'date' },
38 { label: 'Title', value: 'title' },
39 { label: 'Menu Order', value: 'menu_order' },
40 { label: 'Modified Date', value: 'modified' },
41 { label: 'Random', value: 'rand' }
42 ];
43
44 var readMoreStyleOptions = [
45 { label: 'Text Link', value: 'link' },
46 { label: 'Button', value: 'button' },
47 { label: 'Arrow →', value: 'arrow' }
48 ];
49
50 var imageRatioOptions = [
51 { label: '16:9 (56.25%)', value: '56.25' },
52 { label: '4:3 (75%)', value: '75' },
53 { label: '1:1 (100%)', value: '100' },
54 { label: '3:2 (66.67%)',value: '66.67' },
55 { label: '21:9 (42.86%)',value: '42.86' }
56 ];
57
58 /* ─── Card template (used in editor preview & could be reused) ─── */
59 function renderCard(post, attrs) {
60 var a = attrs;
61 var bodyParts = [];
62 if (a.showMeta && post.meta) {
63 bodyParts.push(el('div', { className: 'bkbg-pg-meta', key: 'meta' }, post.meta));
64 }
65 bodyParts.push(
66 el('h3', { className: 'bkbg-pg-title', key: 'title' },
67 el('a', { href: post.link }, post.title)
68 )
69 );
70 if (a.showExcerpt && post.excerpt) {
71 bodyParts.push(el('p', { className: 'bkbg-pg-excerpt', key: 'exc' }, post.excerpt));
72 }
73 if (a.showReadMore) {
74 bodyParts.push(
75 el('a', {
76 href: post.link,
77 className: 'bkbg-pg-readmore bkbg-pg-readmore--' + a.readMoreStyle,
78 key: 'rm'
79 }, a.readMoreLabel + (a.readMoreStyle === 'arrow' ? '' : ''))
80 );
81 }
82
83 return el('article', { className: 'bkbg-pg-card', key: post.id },
84 a.showImage && post.image
85 ? el('div', { className: 'bkbg-pg-thumb bkbg-pg-hover--' + a.imageHoverEffect },
86 el('div', { className: 'bkbg-pg-thumb-inner' },
87 el('img', { src: post.image, alt: post.title, loading: 'lazy' })
88 )
89 )
90 : null,
91 el('div', { className: 'bkbg-pg-body' }, ...bodyParts)
92 );
93 }
94
95 registerBlockType('blockenberg/post-grid', {
96 title: __('Post Grid', 'blockenberg'),
97 description: __('Display posts or CPT in a responsive card grid.', 'blockenberg'),
98 category: 'bkbg-blog',
99 icon: el('svg', { viewBox: '0 0 24 24', xmlns: 'http://www.w3.org/2000/svg' },
100 el('path', { d: 'M3 3h8v8H3V3zm0 10h8v8H3v-8zm10-10h8v8h-8V3zm0 10h8v8h-8v-8z' })
101 ),
102 attributes: {
103 postType: { type: 'string', default: 'post' },
104 orderby: { type: 'string', default: 'date' },
105 order: { type: 'string', default: 'desc' },
106 perPage: { type: 'number', default: 6 },
107 offset: { type: 'number', default: 0 },
108 excerptLen: { type: 'number', default: 20 },
109 columns: { type: 'number', default: 3 },
110 columnsTablet: { type: 'number', default: 2 },
111 columnsMobile: { type: 'number', default: 1 },
112 gapX: { type: 'number', default: 24 },
113 gapY: { type: 'number', default: 24 },
114 showImage: { type: 'boolean', default: true },
115 imageRatio: { type: 'string', default: '56.25' },
116 imageHoverEffect: { type: 'string', default: 'zoom' },
117 showMeta: { type: 'boolean', default: true },
118 showExcerpt: { type: 'boolean', default: true },
119 showReadMore: { type: 'boolean', default: true },
120 readMoreLabel: { type: 'string', default: 'Read More' },
121 readMoreStyle: { type: 'string', default: 'link' },
122 cardStyle: { type: 'string', default: 'shadow' },
123 titleTypo: { type: 'object', default: {} },
124 excerptTypo: { type: 'object', default: {} },
125 metaTypo: { type: 'object', default: {} },
126 cardBg: { type: 'string', default: '#ffffff' },
127 cardRadius: { type: 'number', default: 10 },
128 titleColor: { type: 'string', default: '#111827' },
129 titleHoverColor: { type: 'string', default: '#2563eb' },
130 excerptColor: { type: 'string', default: '#6b7280' },
131 metaColor: { type: 'string', default: '#9ca3af' },
132 linkColor: { type: 'string', default: '#2563eb' },
133 borderColor: { type: 'string', default: '#e5e7eb' },
134 noPostsMessage: { type: 'string', default: 'No posts found.' }
135 },
136
137 deprecated: [{
138 attributes: {
139 postType: { type: 'string', default: 'post' },
140 orderby: { type: 'string', default: 'date' },
141 order: { type: 'string', default: 'desc' },
142 perPage: { type: 'number', default: 6 },
143 offset: { type: 'number', default: 0 },
144 excerptLen: { type: 'number', default: 20 },
145 columns: { type: 'number', default: 3 },
146 columnsTablet: { type: 'number', default: 2 },
147 columnsMobile: { type: 'number', default: 1 },
148 gapX: { type: 'number', default: 24 },
149 gapY: { type: 'number', default: 24 },
150 showImage: { type: 'boolean', default: true },
151 imageRatio: { type: 'string', default: '56.25' },
152 imageHoverEffect: { type: 'string', default: 'zoom' },
153 showMeta: { type: 'boolean', default: true },
154 showExcerpt: { type: 'boolean', default: true },
155 showReadMore: { type: 'boolean', default: true },
156 readMoreLabel: { type: 'string', default: 'Read More' },
157 readMoreStyle: { type: 'string', default: 'link' },
158 cardStyle: { type: 'string', default: 'shadow' },
159 titleSize: { type: 'number', default: 18 },
160 titleWeight: { type: 'number', default: 700 },
161 excerptSize: { type: 'number', default: 14 },
162 metaSize: { type: 'number', default: 12 },
163 cardBg: { type: 'string', default: '#ffffff' },
164 cardRadius: { type: 'number', default: 10 },
165 titleColor: { type: 'string', default: '#111827' },
166 titleHoverColor: { type: 'string', default: '#2563eb' },
167 excerptColor: { type: 'string', default: '#6b7280' },
168 metaColor: { type: 'string', default: '#9ca3af' },
169 linkColor: { type: 'string', default: '#2563eb' },
170 borderColor: { type: 'string', default: '#e5e7eb' },
171 noPostsMessage: { type: 'string', default: 'No posts found.' },
172 titleLineHeight: { type: 'number', default: 1.3 },
173 excerptFontWeight:{ type: 'string', default: '400' }
174 },
175 save: function (props) {
176 var a = props.attributes;
177 var blockProps = wp.blockEditor.useBlockProps.save({
178 className: 'bkbg-pg-wrap bkbg-pg-card--' + a.cardStyle,
179 'data-post-type': a.postType,
180 'data-orderby': a.orderby,
181 'data-order': a.order,
182 'data-per-page': a.perPage,
183 'data-offset': a.offset,
184 'data-excerpt-len': a.excerptLen,
185 'data-show-image': a.showImage ? '1' : '0',
186 'data-show-meta': a.showMeta ? '1' : '0',
187 'data-show-excerpt':a.showExcerpt ? '1' : '0',
188 'data-show-rm': a.showReadMore ? '1' : '0',
189 'data-rm-label': a.readMoreLabel,
190 'data-rm-style': a.readMoreStyle,
191 'data-no-posts': a.noPostsMessage,
192 'data-hover': a.imageHoverEffect,
193 style: {
194 '--bkbg-pg-cols': a.columns,
195 '--bkbg-pg-cols-tab': a.columnsTablet,
196 '--bkbg-pg-cols-mob': a.columnsMobile,
197 '--bkbg-pg-gap-x': a.gapX + 'px',
198 '--bkbg-pg-gap-y': a.gapY + 'px',
199 '--bkbg-pg-ratio': a.imageRatio + '%',
200 '--bkbg-pg-card-bg': a.cardBg,
201 '--bkbg-pg-card-radius': a.cardRadius + 'px',
202 '--bkbg-pg-title-size': a.titleSize + 'px',
203 '--bkbg-pg-title-weight':a.titleWeight,
204 '--bkbg-pg-exc-size': a.excerptSize + 'px',
205 '--bkbg-pg-meta-size': a.metaSize + 'px',
206 '--bkbg-pg-title-color': a.titleColor,
207 '--bkbg-pg-title-hover': a.titleHoverColor,
208 '--bkbg-pg-exc-color': a.excerptColor,
209 '--bkbg-pg-meta-color': a.metaColor,
210 '--bkbg-pg-link-color': a.linkColor,
211 '--bkbg-pg-border': a.borderColor
212 }
213 });
214 return el('div', blockProps,
215 el('div', { className: 'bkbg-pg-grid' })
216 );
217 }
218 }],
219
220 edit: function (props) {
221 var a = props.attributes;
222 var setAttr = props.setAttributes;
223
224 var _state = useState({ posts: [], loading: true, error: '' });
225 var state = _state[0];
226 var setState = _state[1];
227
228 useEffect(function () {
229 setState({ posts: [], loading: true, error: '' });
230 var path = '/blockenberg/v1/post-grid?type=' + a.postType
231 + '&orderby=' + a.orderby
232 + '&order=' + a.order
233 + '&per_page=' + a.perPage
234 + '&offset=' + a.offset
235 + '&excerpt_len=' + a.excerptLen;
236
237 wp.apiFetch({ path: path })
238 .then(function (res) {
239 setState({ posts: res.posts || [], loading: false, error: '' });
240 })
241 .catch(function (err) {
242 setState({ posts: [], loading: false, error: err.message || 'Error loading posts.' });
243 });
244 }, [a.postType, a.orderby, a.order, a.perPage, a.offset, a.excerptLen]);
245
246 var blockProps = useBlockProps({
247 className: 'bkbg-pg-wrap bkbg-pg-card--' + a.cardStyle,
248 style: (function () {
249 var _tvFn = getTypoCssVars();
250 var s = {
251 '--bkbg-pg-cols': a.columns,
252 '--bkbg-pg-cols-tab': a.columnsTablet,
253 '--bkbg-pg-cols-mob': a.columnsMobile,
254 '--bkbg-pg-gap-x': a.gapX + 'px',
255 '--bkbg-pg-gap-y': a.gapY + 'px',
256 '--bkbg-pg-ratio': a.imageRatio + '%',
257 '--bkbg-pg-card-bg': a.cardBg,
258 '--bkbg-pg-card-radius': a.cardRadius + 'px',
259 '--bkbg-pg-title-color': a.titleColor,
260 '--bkbg-pg-title-hover': a.titleHoverColor,
261 '--bkbg-pg-exc-color': a.excerptColor,
262 '--bkbg-pg-meta-color': a.metaColor,
263 '--bkbg-pg-link-color': a.linkColor,
264 '--bkbg-pg-border': a.borderColor
265 };
266 Object.assign(s, _tvFn(a.titleTypo, '--bkbg-pgr-tt-'));
267 Object.assign(s, _tvFn(a.excerptTypo, '--bkbg-pgr-ex-'));
268 Object.assign(s, _tvFn(a.metaTypo, '--bkbg-pgr-mt-'));
269 return s;
270 })()
271 });
272
273 var inspector = el(InspectorControls, null,
274
275 /* ── Query ── */
276 el(PanelBody, { title: __('Query', 'blockenberg'), initialOpen: true },
277 el(SelectControl, {
278 label: __('Post Type', 'blockenberg'),
279 value: a.postType,
280 options: [
281 { label: 'Posts', value: 'post' },
282 { label: 'Pages', value: 'page' },
283 { label: 'Products (WooCommerce)', value: 'product' }
284 ],
285 onChange: function (v) { setAttr({ postType: v }); }
286 }),
287 el(SelectControl, {
288 label: __('Order By', 'blockenberg'),
289 value: a.orderby,
290 options: orderbyOptions,
291 onChange: function (v) { setAttr({ orderby: v }); }
292 }),
293 el(SelectControl, {
294 label: __('Order', 'blockenberg'),
295 value: a.order,
296 options: [
297 { label: 'Newest first (DESC)', value: 'desc' },
298 { label: 'Oldest first (ASC)', value: 'asc' }
299 ],
300 onChange: function (v) { setAttr({ order: v }); }
301 }),
302 el(RangeControl, {
303 label: __('Posts per page', 'blockenberg'),
304 value: a.perPage, min: 1, max: 48,
305 onChange: function (v) { setAttr({ perPage: v }); }
306 }),
307 el(RangeControl, {
308 label: __('Offset', 'blockenberg'),
309 value: a.offset, min: 0, max: 48,
310 onChange: function (v) { setAttr({ offset: v }); }
311 }),
312 el(RangeControl, {
313 label: __('Excerpt word count', 'blockenberg'),
314 value: a.excerptLen, min: 5, max: 60,
315 onChange: function (v) { setAttr({ excerptLen: v }); }
316 })
317 ),
318
319 /* ── Layout ── */
320 el(PanelBody, { title: __('Layout', 'blockenberg'), initialOpen: false },
321 el(RangeControl, {
322 label: __('Columns (Desktop)', 'blockenberg'),
323 value: a.columns, min: 1, max: 6,
324 onChange: function (v) { setAttr({ columns: v }); }
325 }),
326 el(RangeControl, {
327 label: __('Columns (Tablet)', 'blockenberg'),
328 value: a.columnsTablet, min: 1, max: 4,
329 onChange: function (v) { setAttr({ columnsTablet: v }); }
330 }),
331 el(RangeControl, {
332 label: __('Columns (Mobile)', 'blockenberg'),
333 value: a.columnsMobile, min: 1, max: 2,
334 onChange: function (v) { setAttr({ columnsMobile: v }); }
335 }),
336 el(RangeControl, {
337 label: __('Column gap (px)', 'blockenberg'),
338 value: a.gapX, min: 0, max: 80,
339 onChange: function (v) { setAttr({ gapX: v }); }
340 }),
341 el(RangeControl, {
342 label: __('Row gap (px)', 'blockenberg'),
343 value: a.gapY, min: 0, max: 80,
344 onChange: function (v) { setAttr({ gapY: v }); }
345 }),
346 el(SelectControl, {
347 label: __('Card Style', 'blockenberg'),
348 value: a.cardStyle,
349 options: cardStyleOptions,
350 onChange: function (v) { setAttr({ cardStyle: v }); }
351 }),
352 el(RangeControl, {
353 label: __('Card border radius (px)', 'blockenberg'),
354 value: a.cardRadius, min: 0, max: 40,
355 onChange: function (v) { setAttr({ cardRadius: v }); }
356 })
357 ),
358
359 /* ── Image ── */
360 el(PanelBody, { title: __('Image', 'blockenberg'), initialOpen: false },
361 el(ToggleControl, {
362 label: __('Show featured image', 'blockenberg'),
363 checked: a.showImage,
364 onChange: function (v) { setAttr({ showImage: v }); },
365 __nextHasNoMarginBottom: true
366 }),
367 a.showImage && el(Fragment, null,
368 el(SelectControl, {
369 label: __('Image ratio', 'blockenberg'),
370 value: a.imageRatio,
371 options: imageRatioOptions,
372 onChange: function (v) { setAttr({ imageRatio: v }); }
373 }),
374 el(SelectControl, {
375 label: __('Hover effect', 'blockenberg'),
376 value: a.imageHoverEffect,
377 options: hoverEffectOptions,
378 onChange: function (v) { setAttr({ imageHoverEffect: v }); }
379 })
380 )
381 ),
382
383 /* ── Content visibility ── */
384 el(PanelBody, { title: __('Content', 'blockenberg'), initialOpen: false },
385 el(ToggleControl, {
386 label: __('Show meta (date / author)', 'blockenberg'),
387 checked: a.showMeta,
388 onChange: function (v) { setAttr({ showMeta: v }); },
389 __nextHasNoMarginBottom: true
390 }),
391 el(ToggleControl, {
392 label: __('Show excerpt', 'blockenberg'),
393 checked: a.showExcerpt,
394 onChange: function (v) { setAttr({ showExcerpt: v }); },
395 __nextHasNoMarginBottom: true
396 }),
397 el(ToggleControl, {
398 label: __('Show Read More', 'blockenberg'),
399 checked: a.showReadMore,
400 onChange: function (v) { setAttr({ showReadMore: v }); },
401 __nextHasNoMarginBottom: true
402 }),
403 a.showReadMore && el(Fragment, null,
404 el(TextControl, {
405 label: __('Read More label', 'blockenberg'),
406 value: a.readMoreLabel,
407 onChange: function (v) { setAttr({ readMoreLabel: v }); }
408 }),
409 el(SelectControl, {
410 label: __('Read More style', 'blockenberg'),
411 value: a.readMoreStyle,
412 options: readMoreStyleOptions,
413 onChange: function (v) { setAttr({ readMoreStyle: v }); }
414 })
415 ),
416 el(TextControl, {
417 label: __('No posts message', 'blockenberg'),
418 value: a.noPostsMessage,
419 onChange: function (v) { setAttr({ noPostsMessage: v }); }
420 })
421 ),
422
423 /* ── Typography ── */
424 el(PanelBody, { title: __('Typography', 'blockenberg'), initialOpen: false },
425 (function () {
426 var TC = getTypoControl();
427 if (!TC) return el('p', null, 'Loading…');
428 return el(Fragment, null,
429 el(TC, { label: 'Title Typography', value: a.titleTypo, onChange: function (v) { setAttr({ titleTypo: v }); } }),
430 el(TC, { label: 'Excerpt Typography', value: a.excerptTypo, onChange: function (v) { setAttr({ excerptTypo: v }); } }),
431 el(TC, { label: 'Meta Typography', value: a.metaTypo, onChange: function (v) { setAttr({ metaTypo: v }); } })
432 );
433 })()
434 ),
435
436 /* ── Colors ── */
437 el(PanelBody, { title: __('Colors', 'blockenberg'), initialOpen: false },
438 el(PanelColorSettings, {
439 title: __('Card Colors', 'blockenberg'),
440 initialOpen: false,
441 colorSettings: [
442 { label: __('Card background', 'blockenberg'), value: a.cardBg, onChange: function(v){ setAttr({ cardBg: v||'#ffffff' }); } },
443 { label: __('Title color', 'blockenberg'), value: a.titleColor, onChange: function(v){ setAttr({ titleColor: v||'#111827' }); } },
444 { label: __('Title hover color', 'blockenberg'),value: a.titleHoverColor, onChange: function(v){ setAttr({ titleHoverColor: v||'#2563eb' }); } },
445 { label: __('Excerpt color', 'blockenberg'), value: a.excerptColor, onChange: function(v){ setAttr({ excerptColor: v||'#6b7280' }); } },
446 { label: __('Meta color', 'blockenberg'), value: a.metaColor, onChange: function(v){ setAttr({ metaColor: v||'#9ca3af' }); } },
447 { label: __('Link/button color', 'blockenberg'),value: a.linkColor, onChange: function(v){ setAttr({ linkColor: v||'#2563eb' }); } },
448 { label: __('Border color', 'blockenberg'), value: a.borderColor, onChange: function(v){ setAttr({ borderColor: v||'#e5e7eb' }); } }
449 ]
450 })
451 )
452 );
453
454 /* ─── Editor preview ─── */
455 var content;
456 if (state.loading) {
457 content = el('div', { className: 'bkbg-pg-loading' },
458 el(Spinner),
459 el('span', null, ' ' + __('Loading posts…', 'blockenberg'))
460 );
461 } else if (state.error) {
462 content = el(Notice, { status: 'error', isDismissible: false }, state.error);
463 } else if (!state.posts.length) {
464 content = el('p', { className: 'bkbg-pg-empty' }, a.noPostsMessage);
465 } else {
466 content = el('div', { className: 'bkbg-pg-grid' },
467 state.posts.map(function (post) { return renderCard(post, a); })
468 );
469 }
470
471 return el(Fragment, null,
472 inspector,
473 el('div', blockProps, content)
474 );
475 },
476
477 save: function (props) {
478 var a = props.attributes;
479 var _tvFn = getTypoCssVars();
480 var saveStyle = {
481 '--bkbg-pg-cols': a.columns,
482 '--bkbg-pg-cols-tab': a.columnsTablet,
483 '--bkbg-pg-cols-mob': a.columnsMobile,
484 '--bkbg-pg-gap-x': a.gapX + 'px',
485 '--bkbg-pg-gap-y': a.gapY + 'px',
486 '--bkbg-pg-ratio': a.imageRatio + '%',
487 '--bkbg-pg-card-bg': a.cardBg,
488 '--bkbg-pg-card-radius': a.cardRadius + 'px',
489 '--bkbg-pg-title-color': a.titleColor,
490 '--bkbg-pg-title-hover': a.titleHoverColor,
491 '--bkbg-pg-exc-color': a.excerptColor,
492 '--bkbg-pg-meta-color': a.metaColor,
493 '--bkbg-pg-link-color': a.linkColor,
494 '--bkbg-pg-border': a.borderColor
495 };
496 Object.assign(saveStyle, _tvFn(a.titleTypo, '--bkbg-pgr-tt-'));
497 Object.assign(saveStyle, _tvFn(a.excerptTypo, '--bkbg-pgr-ex-'));
498 Object.assign(saveStyle, _tvFn(a.metaTypo, '--bkbg-pgr-mt-'));
499
500 var blockProps = wp.blockEditor.useBlockProps.save({
501 className: 'bkbg-pg-wrap bkbg-pg-card--' + a.cardStyle,
502 'data-post-type': a.postType,
503 'data-orderby': a.orderby,
504 'data-order': a.order,
505 'data-per-page': a.perPage,
506 'data-offset': a.offset,
507 'data-excerpt-len': a.excerptLen,
508 'data-show-image': a.showImage ? '1' : '0',
509 'data-show-meta': a.showMeta ? '1' : '0',
510 'data-show-excerpt':a.showExcerpt ? '1' : '0',
511 'data-show-rm': a.showReadMore ? '1' : '0',
512 'data-rm-label': a.readMoreLabel,
513 'data-rm-style': a.readMoreStyle,
514 'data-no-posts': a.noPostsMessage,
515 'data-hover': a.imageHoverEffect,
516 style: saveStyle
517 });
518 return el('div', blockProps,
519 el('div', { className: 'bkbg-pg-grid' })
520 );
521 }
522 });
523 }() );
524