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 / photo-card / index.js

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

258 lines 19.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 ( function () {
2 const el = window.wp.element.createElement;
3 const { registerBlockType } = window.wp.blocks;
4 const { InspectorControls, useBlockProps, PanelColorSettings } = window.wp.blockEditor;
5 const { PanelBody, RangeControl, ToggleControl, TextControl, Button, SelectControl } = window.wp.components;
6 const { __ } = window.wp.i18n;
7
8 var _tc, _tv;
9 function getTypoControl() { return _tc || (_tc = window.bkbgTypographyControl); }
10 function getTypoCssVars() { return _tv || (_tv = window.bkbgTypoCssVars); }
11
12 // ── Image / placeholder area ───────────────────────────────────────────────
13 function CardImage( { imageUrl, imageAlt, aspectRatio, borderRadius, imagePlaceholderBg, category, showCategory, categoryBg, categoryColor, layout } ) {
14 const radii = layout === 'vertical'
15 ? borderRadius + 'px ' + borderRadius + 'px 0 0'
16 : '0'; // handled by parent radius
17
18 const wrapStyle = {
19 position: 'relative',
20 overflow: 'hidden',
21 borderRadius: layout === 'vertical' ? ( borderRadius + 'px ' + borderRadius + 'px 0 0' ) : ( borderRadius + 'px 0 0 ' + borderRadius + 'px' ),
22 background: imagePlaceholderBg,
23 flexShrink: 0,
24 };
25
26 if ( layout === 'horizontal' ) {
27 wrapStyle.width = '45%';
28 wrapStyle.minHeight = 200;
29 } else {
30 wrapStyle.aspectRatio = aspectRatio;
31 wrapStyle.width = '100%';
32 }
33
34 return el( 'div', { className: 'bkbg-pc-image', style: wrapStyle },
35 imageUrl
36 ? el( 'img', { src: imageUrl, alt: imageAlt, style: { width: '100%', height: '100%', objectFit: 'cover', display: 'block' } } )
37 : el( 'div', { style: { width: '100%', height: '100%', minHeight: 140, display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#9ca3af', fontSize: 13 } }, '🖼 ' + __( 'No image set', 'blockenberg' ) ),
38 showCategory && category && el( 'span', { className: 'bkbg-pc-cat', style: {
39 position: 'absolute', top: 12, left: 12,
40 background: categoryBg, color: categoryColor,
41 padding: '3px 10px', borderRadius: 99,
42 letterSpacing: 0.3, textTransform: 'uppercase',
43 } }, category ),
44 );
45 }
46
47 // ── Full render ───────────────────────────────────────────────────────────
48 function renderPhotoCard( a ) {
49 const isHoriz = a.layout === 'horizontal';
50 return el( 'div', { className: 'bkbg-pc-card', style: {
51 background: a.cardBg,
52 border: '1px solid ' + a.borderColor,
53 borderRadius: a.borderRadius + 'px',
54 overflow: 'hidden',
55 display: 'flex',
56 flexDirection: isHoriz ? ( a.imageSide === 'right' ? 'row-reverse' : 'row' ) : 'column',
57 boxSizing: 'border-box',
58 boxShadow: '0 1px 4px rgba(0,0,0,0.07)',
59 } },
60 el( CardImage, {
61 imageUrl: a.imageUrl, imageAlt: a.imageAlt, aspectRatio: a.aspectRatio,
62 borderRadius: a.borderRadius, imagePlaceholderBg: a.imagePlaceholderBg,
63 category: a.category, showCategory: a.showCategory,
64 categoryBg: a.categoryBg, categoryColor: a.categoryColor,
65 layout: a.layout,
66 } ),
67
68 // Content
69 el( 'div', { className: 'bkbg-pc-body', style: { padding: a.padding + 'px', flex: 1, display: 'flex', flexDirection: 'column', gap: 10 } },
70 el( 'h3', { className: 'bkbg-pc-title', style: { margin: 0, color: a.titleColor } }, a.title ),
71 a.showExcerpt && el( 'p', { className: 'bkbg-pc-excerpt', style: { margin: 0, color: a.textColor } }, a.excerpt ),
72
73 // Meta row
74 el( 'div', { className: 'bkbg-pc-meta', style: { display: 'flex', flexWrap: 'wrap', gap: 6, alignItems: 'center', color: a.metaColor, marginTop: 'auto' } },
75 a.showAuthor && el( 'span', {}, '✍️ ' + a.author ),
76 a.showAuthor && a.showDate && el( 'span', { style: { opacity: 0.4 } }, '·' ),
77 a.showDate && el( 'span', {}, a.date ),
78 a.showDate && a.showReadTime && el( 'span', { style: { opacity: 0.4 } }, '·' ),
79 a.showReadTime && el( 'span', {}, '' + a.readTime ),
80 ),
81
82 a.showLink && el( 'a', { href: a.linkUrl || '#', style: {
83 color: a.linkColor, textDecoration: 'none', display: 'inline-flex', alignItems: 'center', gap: 4,
84 } }, a.linkText ),
85 ),
86 );
87 }
88
89 // ── Legacy render (for deprecated save) ──────────────────────────────────
90 function renderPhotoCardLegacy( a ) {
91 var isHoriz = a.layout === 'horizontal';
92 var imgStyle = {
93 position: 'relative', overflow: 'hidden',
94 borderRadius: isHoriz ? ( a.borderRadius + 'px 0 0 ' + a.borderRadius + 'px' ) : ( a.borderRadius + 'px ' + a.borderRadius + 'px 0 0' ),
95 background: a.imagePlaceholderBg, flexShrink: 0,
96 };
97 if ( isHoriz ) { imgStyle.width = '45%'; imgStyle.minHeight = 200; }
98 else { imgStyle.aspectRatio = a.aspectRatio; imgStyle.width = '100%'; }
99
100 var imageArea = el( 'div', { className: 'bkbg-pc-image', style: imgStyle },
101 a.imageUrl
102 ? el( 'img', { src: a.imageUrl, alt: a.imageAlt, style: { width: '100%', height: '100%', objectFit: 'cover', display: 'block' } } )
103 : el( 'div', { style: { width: '100%', height: '100%', minHeight: 140, display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#9ca3af', fontSize: 13 } }, '🖼 ' + __( 'No image set', 'blockenberg' ) ),
104 a.showCategory && a.category && el( 'span', { className: 'bkbg-pc-cat', style: {
105 position: 'absolute', top: 12, left: 12,
106 background: a.categoryBg, color: a.categoryColor,
107 fontSize: a.metaFontSize + 'px', fontWeight: 700,
108 padding: '3px 10px', borderRadius: 99,
109 letterSpacing: 0.3, textTransform: 'uppercase',
110 } }, a.category ),
111 );
112
113 return el( 'div', { className: 'bkbg-pc-card', style: {
114 background: a.cardBg, border: '1px solid ' + a.borderColor,
115 borderRadius: a.borderRadius + 'px', overflow: 'hidden',
116 display: 'flex',
117 flexDirection: isHoriz ? ( a.imageSide === 'right' ? 'row-reverse' : 'row' ) : 'column',
118 boxSizing: 'border-box', boxShadow: '0 1px 4px rgba(0,0,0,0.07)',
119 } },
120 imageArea,
121 el( 'div', { className: 'bkbg-pc-body', style: { padding: a.padding + 'px', flex: 1, display: 'flex', flexDirection: 'column', gap: 10 } },
122 el( 'h3', { className: 'bkbg-pc-title', style: { margin: 0, fontSize: a.titleFontSize + 'px', fontWeight: 700, color: a.titleColor, lineHeight: 1.3 } }, a.title ),
123 a.showExcerpt && el( 'p', { className: 'bkbg-pc-excerpt', style: { margin: 0, fontSize: a.fontSize + 'px', color: a.textColor, lineHeight: 1.65 } }, a.excerpt ),
124 el( 'div', { className: 'bkbg-pc-meta', style: { display: 'flex', flexWrap: 'wrap', gap: 6, alignItems: 'center', fontSize: a.metaFontSize + 'px', color: a.metaColor, marginTop: 'auto' } },
125 a.showAuthor && el( 'span', {}, '✍️ ' + a.author ),
126 a.showAuthor && a.showDate && el( 'span', { style: { opacity: 0.4 } }, '·' ),
127 a.showDate && el( 'span', {}, a.date ),
128 a.showDate && a.showReadTime && el( 'span', { style: { opacity: 0.4 } }, '·' ),
129 a.showReadTime && el( 'span', {}, '' + a.readTime ),
130 ),
131 a.showLink && el( 'a', { href: a.linkUrl || '#', style: {
132 fontSize: a.fontSize + 'px', fontWeight: 600, color: a.linkColor, textDecoration: 'none', display: 'inline-flex', alignItems: 'center', gap: 4,
133 } }, a.linkText ),
134 ),
135 );
136 }
137
138 // ── Block ─────────────────────────────────────────────────────────────────
139 registerBlockType( 'blockenberg/photo-card', {
140 edit: function ( props ) {
141 const { attributes: a, setAttributes } = props;
142 const blockProps = useBlockProps((function () {
143 var tv = getTypoCssVars();
144 var s = { background: a.bgColor };
145 Object.assign(s, tv(a.titleTypo, '--bkbg-pc-tt-'));
146 Object.assign(s, tv(a.bodyTypo, '--bkbg-pc-bd-'));
147 Object.assign(s, tv(a.metaTypo, '--bkbg-pc-mt-'));
148 return { className: 'bkbg-pc-wrap', style: s };
149 })());
150
151 return el( 'div', blockProps,
152 el( InspectorControls, {},
153
154 el( PanelBody, { title: __( 'Content', 'blockenberg' ), initialOpen: true },
155 el( TextControl, { label: __( 'Image URL', 'blockenberg' ), value: a.imageUrl, onChange: v => setAttributes( { imageUrl: v } ) } ),
156 el( TextControl, { label: __( 'Image Alt', 'blockenberg' ), value: a.imageAlt, onChange: v => setAttributes( { imageAlt: v } ) } ),
157 el( SelectControl, { label: __( 'Aspect Ratio', 'blockenberg' ), value: a.aspectRatio, options: [
158 { label: '16 / 9', value: '16/9' },
159 { label: '4 / 3', value: '4/3' },
160 { label: '3 / 2', value: '3/2' },
161 { label: '1 / 1', value: '1/1' },
162 { label: '2 / 1', value: '2/1' },
163 ], onChange: v => setAttributes( { aspectRatio: v } ) } ),
164 el( ToggleControl, { label: __( 'Show Category', 'blockenberg' ), checked: a.showCategory, onChange: v => setAttributes( { showCategory: v } ) } ),
165 el( TextControl, { label: __( 'Category', 'blockenberg' ), value: a.category, onChange: v => setAttributes( { category: v } ) } ),
166 el( TextControl, { label: __( 'Title', 'blockenberg' ), value: a.title, onChange: v => setAttributes( { title: v } ) } ),
167 el( ToggleControl, { label: __( 'Show Excerpt', 'blockenberg' ), checked: a.showExcerpt, onChange: v => setAttributes( { showExcerpt: v } ) } ),
168 a.showExcerpt && el( TextControl, { label: __( 'Excerpt', 'blockenberg' ), value: a.excerpt, onChange: v => setAttributes( { excerpt: v } ) } ),
169 ),
170
171 el( PanelBody, { title: __( 'Meta', 'blockenberg' ), initialOpen: false },
172 el( ToggleControl, { label: __( 'Show Author', 'blockenberg' ), checked: a.showAuthor, onChange: v => setAttributes( { showAuthor: v } ) } ),
173 a.showAuthor && el( TextControl, { label: __( 'Author', 'blockenberg' ), value: a.author, onChange: v => setAttributes( { author: v } ) } ),
174 el( ToggleControl, { label: __( 'Show Date', 'blockenberg' ), checked: a.showDate, onChange: v => setAttributes( { showDate: v } ) } ),
175 a.showDate && el( TextControl, { label: __( 'Date', 'blockenberg' ), value: a.date, onChange: v => setAttributes( { date: v } ) } ),
176 el( ToggleControl, { label: __( 'Show Read Time', 'blockenberg' ), checked: a.showReadTime, onChange: v => setAttributes( { showReadTime: v } ) } ),
177 a.showReadTime && el( TextControl, { label: __( 'Read Time', 'blockenberg' ), value: a.readTime, onChange: v => setAttributes( { readTime: v } ) } ),
178 el( ToggleControl, { label: __( 'Show Link', 'blockenberg' ), checked: a.showLink, onChange: v => setAttributes( { showLink: v } ) } ),
179 a.showLink && el( TextControl, { label: __( 'Link URL', 'blockenberg' ), value: a.linkUrl, onChange: v => setAttributes( { linkUrl: v } ) } ),
180 a.showLink && el( TextControl, { label: __( 'Link Text', 'blockenberg' ), value: a.linkText, onChange: v => setAttributes( { linkText: v } ) } ),
181 ),
182
183 el( PanelBody, { title: __( 'Layout', 'blockenberg' ), initialOpen: false },
184 el( SelectControl, { label: __( 'Layout', 'blockenberg' ), value: a.layout, options: [
185 { label: 'Vertical (image top)', value: 'vertical' },
186 { label: 'Horizontal (image side)', value: 'horizontal' },
187 ], onChange: v => setAttributes( { layout: v } ) } ),
188 a.layout === 'horizontal' && el( SelectControl, { label: __( 'Image Side', 'blockenberg' ), value: a.imageSide, options: [
189 { label: 'Left', value: 'left' }, { label: 'Right', value: 'right' },
190 ], onChange: v => setAttributes( { imageSide: v } ) } ),
191 el( RangeControl, { label: __( 'Border Radius', 'blockenberg' ), value: a.borderRadius, onChange: v => setAttributes( { borderRadius: v } ), min: 0, max: 32 } ),
192 el( RangeControl, { label: __( 'Content Padding', 'blockenberg' ),value: a.padding, onChange: v => setAttributes( { padding: v } ), min: 12, max: 56 } ),
193 ),
194
195
196 el( PanelBody, { title: __( 'Typography', 'blockenberg' ), initialOpen: false },
197 el( getTypoControl(), { label: __( 'Title', 'blockenberg' ), value: a.titleTypo, onChange: v => setAttributes({ titleTypo: v }) }),
198 el( getTypoControl(), { label: __( 'Body', 'blockenberg' ), value: a.bodyTypo, onChange: v => setAttributes({ bodyTypo: v }) }),
199 el( getTypoControl(), { label: __( 'Meta', 'blockenberg' ), value: a.metaTypo, onChange: v => setAttributes({ metaTypo: v }) })
200 ),
201 el( PanelColorSettings, {
202 title: __( 'Colors', 'blockenberg' ), initialOpen: false,
203 colorSettings: [
204 { label: __( 'Page Background', 'blockenberg' ), value: a.bgColor, onChange: v => setAttributes( { bgColor: v || '#ffffff' } ) },
205 { label: __( 'Card Background', 'blockenberg' ), value: a.cardBg, onChange: v => setAttributes( { cardBg: v || '#ffffff' } ) },
206 { label: __( 'Border', 'blockenberg' ), value: a.borderColor, onChange: v => setAttributes( { borderColor: v || '#e5e7eb' } ) },
207 { label: __( 'Title', 'blockenberg' ), value: a.titleColor, onChange: v => setAttributes( { titleColor: v || '#111827' } ) },
208 { label: __( 'Body Text', 'blockenberg' ), value: a.textColor, onChange: v => setAttributes( { textColor: v || '#6b7280' } ) },
209 { label: __( 'Meta Text', 'blockenberg' ), value: a.metaColor, onChange: v => setAttributes( { metaColor: v || '#9ca3af' } ) },
210 { label: __( 'Category Background', 'blockenberg' ),value: a.categoryBg, onChange: v => setAttributes( { categoryBg: v || '#6366f1' } ) },
211 { label: __( 'Category Text', 'blockenberg' ), value: a.categoryColor, onChange: v => setAttributes( { categoryColor: v || '#ffffff' } ) },
212 { label: __( 'Link', 'blockenberg' ), value: a.linkColor, onChange: v => setAttributes( { linkColor: v || '#6366f1' } ) },
213 { label: __( 'Image Placeholder', 'blockenberg' ), value: a.imagePlaceholderBg, onChange: v => setAttributes( { imagePlaceholderBg: v || '#e5e7eb' } ) },
214 ]
215 } ),
216 ),
217
218 renderPhotoCard( a ),
219 );
220 },
221
222 save: function ( { attributes: a } ) {
223 const blockProps = useBlockProps.save((function () {
224 var tv = getTypoCssVars();
225 var s = { background: a.bgColor };
226 Object.assign(s, tv(a.titleTypo, '--bkbg-pc-tt-'));
227 Object.assign(s, tv(a.bodyTypo, '--bkbg-pc-bd-'));
228 Object.assign(s, tv(a.metaTypo, '--bkbg-pc-mt-'));
229 return { className: 'bkbg-pc-wrap', style: s };
230 })());
231 return el( 'div', blockProps, renderPhotoCard( a ) );
232 },
233
234 deprecated: [{
235 attributes: {
236 imageUrl:{type:"string","default":""},imageAlt:{type:"string","default":""},aspectRatio:{type:"string","default":"16/9"},
237 category:{type:"string","default":"Technology"},showCategory:{type:"boolean","default":true},
238 title:{type:"string","default":"The Future of Web Development Starts Today"},
239 excerpt:{type:"string","default":"Explore how modern tooling, AI-assisted coding, and component-driven design are reshaping the way we build for the web."},
240 showExcerpt:{type:"boolean","default":true},author:{type:"string","default":"Alex Morgan"},showAuthor:{type:"boolean","default":true},
241 date:{type:"string","default":"25 Feb 2026"},showDate:{type:"boolean","default":true},readTime:{type:"string","default":"5 min read"},showReadTime:{type:"boolean","default":true},
242 linkUrl:{type:"string","default":""},linkText:{type:"string","default":"Read article \u2192"},showLink:{type:"boolean","default":true},
243 layout:{type:"string","default":"vertical"},imageSide:{type:"string","default":"left"},
244 borderRadius:{type:"number","default":14},padding:{type:"number","default":20},
245 fontSize:{type:"number","default":14},titleFontSize:{type:"number","default":19},metaFontSize:{type:"number","default":12},
246 bgColor:{type:"string","default":"#ffffff"},cardBg:{type:"string","default":"#ffffff"},borderColor:{type:"string","default":"#e5e7eb"},
247 titleColor:{type:"string","default":"#111827"},textColor:{type:"string","default":"#6b7280"},metaColor:{type:"string","default":"#9ca3af"},
248 categoryBg:{type:"string","default":"#6366f1"},categoryColor:{type:"string","default":"#ffffff"},linkColor:{type:"string","default":"#6366f1"},
249 imagePlaceholderBg:{type:"string","default":"#e5e7eb"},titleFontWeight:{type:"string","default":"700"},bodyFontWeight:{type:"string","default":"400"}
250 },
251 save: function({ attributes: a }) {
252 const blockProps = useBlockProps.save({ className: 'bkbg-pc-wrap', style: { background: a.bgColor } });
253 return el('div', blockProps, renderPhotoCardLegacy(a));
254 }
255 }],
256 } );
257 }() );
258