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 / filterable-cards / index.js

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

331 lines 19.9 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 __ = wp.i18n.__;
4 var registerBlockType = wp.blocks.registerBlockType;
5 var InspectorControls = wp.blockEditor.InspectorControls;
6 var MediaUpload = wp.blockEditor.MediaUpload;
7 var MediaUploadCheck = wp.blockEditor.MediaUploadCheck;
8 var useBlockProps = wp.blockEditor.useBlockProps;
9 var RichText = wp.blockEditor.RichText;
10 var PanelColorSettings = wp.blockEditor.PanelColorSettings;
11 var PanelBody = wp.components.PanelBody;
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 Button = wp.components.Button;
17 var Fragment = wp.element.Fragment;
18
19 var _fcaTC, _fcaTV;
20 function _tc() { return _fcaTC || (_fcaTC = window.bkbgTypographyControl); }
21 function _tv() { return _fcaTV || (_fcaTV = window.bkbgTypoCssVars); }
22
23 registerBlockType('blockenberg/filterable-cards', {
24 edit: function (props) {
25 var attr = props.attributes;
26 var set = props.setAttributes;
27 var cards = attr.cards || [];
28
29 function updateCard(idx, field, val) {
30 var updated = cards.map(function (c, i) {
31 if (i !== idx) return c;
32 var patch = {}; patch[field] = val;
33 return Object.assign({}, c, patch);
34 });
35 set({ cards: updated });
36 }
37
38 function addCard() {
39 set({
40 cards: cards.concat([{
41 title: 'New Card', description: 'Card description.',
42 category: 'General', imageUrl: '', imageId: 0, imageAlt: '',
43 link: '', linkLabel: 'Learn More', label: '', icon: '', featured: false
44 }])
45 });
46 }
47
48 function duplicateCard(idx) {
49 var arr = cards.slice();
50 arr.splice(idx + 1, 0, Object.assign({}, cards[idx]));
51 set({ cards: arr });
52 }
53
54 function removeCard(idx) {
55 set({ cards: cards.filter(function (_, i) { return i !== idx; }) });
56 }
57
58 /* unique categories */
59 var cats = [attr.allLabel || 'All'];
60 cards.forEach(function (c) { if (c.category && cats.indexOf(c.category) < 0) cats.push(c.category); });
61
62 var ratioMap = { '16:9': '56.25%', '4:3': '75%', '3:2': '66.66%', '1:1': '100%' };
63 var paddingTop = ratioMap[attr.imageRatio] || '66.66%';
64
65 /* Inspector */
66 var inspector = el(InspectorControls, {},
67 el(PanelBody, { title: __('Layout', 'blockenberg'), initialOpen: true },
68 el(RangeControl, {
69 label: __('Columns', 'blockenberg'),
70 value: attr.columns, min: 1, max: 5,
71 onChange: function (v) { set({ columns: v }); },
72 __nextHasNoMarginBottom: true
73 }),
74 el(SelectControl, {
75 label: __('Layout', 'blockenberg'),
76 value: attr.layout,
77 options: [
78 { label: 'Grid', value: 'grid' },
79 { label: 'List', value: 'list' },
80 { label: 'Masonry', value: 'masonry' }
81 ],
82 onChange: function (v) { set({ layout: v }); },
83 __nextHasNoMarginBottom: true
84 }),
85 el(SelectControl, {
86 label: __('Filter Style', 'blockenberg'),
87 value: attr.filterStyle,
88 options: [
89 { label: 'Buttons', value: 'buttons' },
90 { label: 'Tabs', value: 'tabs' },
91 { label: 'Dropdown', value: 'dropdown' }
92 ],
93 onChange: function (v) { set({ filterStyle: v }); },
94 __nextHasNoMarginBottom: true
95 }),
96 el(TextControl, {
97 label: __('\"All\" Label', 'blockenberg'),
98 value: attr.allLabel,
99 onChange: function (v) { set({ allLabel: v }); },
100 __nextHasNoMarginBottom: true
101 }),
102 el(SelectControl, {
103 label: __('Image Ratio', 'blockenberg'),
104 value: attr.imageRatio,
105 options: [
106 { label: '16:9', value: '16:9' },
107 { label: '4:3', value: '4:3' },
108 { label: '3:2', value: '3:2' },
109 { label: '1:1 Square', value: '1:1' }
110 ],
111 onChange: function (v) { set({ imageRatio: v }); },
112 __nextHasNoMarginBottom: true
113 }),
114 el(RangeControl, {
115 label: __('Card Radius (px)', 'blockenberg'),
116 value: attr.cardRadius, min: 0, max: 40,
117 onChange: function (v) { set({ cardRadius: v }); },
118 __nextHasNoMarginBottom: true
119 }),
120 el(RangeControl, {
121 label: __('Gap (px)', 'blockenberg'),
122 value: attr.gap, min: 8, max: 80,
123 onChange: function (v) { set({ gap: v }); },
124 __nextHasNoMarginBottom: true
125 }),
126 el(RangeControl, {
127 label: __('Padding Top (px)', 'blockenberg'),
128 value: attr.paddingTop, min: 0, max: 200,
129 onChange: function (v) { set({ paddingTop: v }); },
130 __nextHasNoMarginBottom: true
131 }),
132 el(RangeControl, {
133 label: __('Padding Bottom (px)', 'blockenberg'),
134 value: attr.paddingBottom, min: 0, max: 200,
135 onChange: function (v) { set({ paddingBottom: v }); },
136 __nextHasNoMarginBottom: true
137 })
138 ),
139 el(PanelBody, { title: __('Visibility', 'blockenberg'), initialOpen: false },
140 el(ToggleControl, { label: __('Show Images', 'blockenberg'), checked: attr.showImages, onChange: function (v) { set({ showImages: v }); }, __nextHasNoMarginBottom: true }),
141 el(ToggleControl, { label: __('Show Descriptions', 'blockenberg'), checked: attr.showDescriptions, onChange: function (v) { set({ showDescriptions: v }); }, __nextHasNoMarginBottom: true }),
142 el(ToggleControl, { label: __('Show Labels', 'blockenberg'), checked: attr.showLabels, onChange: function (v) { set({ showLabels: v }); }, __nextHasNoMarginBottom: true }),
143 el(ToggleControl, { label: __('Show Links', 'blockenberg'), checked: attr.showLinks, onChange: function (v) { set({ showLinks: v }); }, __nextHasNoMarginBottom: true }),
144 el(ToggleControl, { label: __('Animate on Filter', 'blockenberg'), checked: attr.animateFilter, onChange: function (v) { set({ animateFilter: v }); }, __nextHasNoMarginBottom: true })
145 ),
146 el(PanelBody, { title: __('Typography', 'blockenberg'), initialOpen: false },
147 _tc() && _tc()({ label: __('Title', 'blockenberg'), typo: attr.typoTitle, onChange: function (v) { set({ typoTitle: v }); } }),
148 _tc() && _tc()({ label: __('Description', 'blockenberg'), typo: attr.typoDesc, onChange: function (v) { set({ typoDesc: v }); } }),
149 _tc() && _tc()({ label: __('Filter', 'blockenberg'), typo: attr.typoFilter, onChange: function (v) { set({ typoFilter: v }); } })
150 ),
151 el(PanelColorSettings, {
152 title: __('Colors', 'blockenberg'), initialOpen: false,
153 colorSettings: [
154 { value: attr.bgColor, onChange: function (v) { set({ bgColor: v || '' }); }, label: __('Section Background', 'blockenberg') },
155 { value: attr.cardBg, onChange: function (v) { set({ cardBg: v || '#ffffff' }); }, label: __('Card Background', 'blockenberg') },
156 { value: attr.cardBorder, onChange: function (v) { set({ cardBorder: v || '#e5e7eb' }); }, label: __('Card Border', 'blockenberg') },
157 { value: attr.titleColor, onChange: function (v) { set({ titleColor: v || '#111827' }); }, label: __('Title', 'blockenberg') },
158 { value: attr.descColor, onChange: function (v) { set({ descColor: v || '#6b7280' }); }, label: __('Description', 'blockenberg') },
159 { value: attr.labelBg, onChange: function (v) { set({ labelBg: v || '#ede9fe' }); }, label: __('Label Background', 'blockenberg') },
160 { value: attr.labelColor, onChange: function (v) { set({ labelColor: v || '#6366f1' }); }, label: __('Label Text', 'blockenberg') },
161 { value: attr.linkColor, onChange: function (v) { set({ linkColor: v || '#6366f1' }); }, label: __('Link', 'blockenberg') },
162 { value: attr.filterBtnBg, onChange: function (v) { set({ filterBtnBg: v || '#f3f4f6' }); }, label: __('Filter Button BG', 'blockenberg') },
163 { value: attr.filterBtnColor, onChange: function (v) { set({ filterBtnColor: v || '#374151' }); }, label: __('Filter Button Text', 'blockenberg') },
164 { value: attr.filterBtnActiveBg, onChange: function (v) { set({ filterBtnActiveBg: v || '#6366f1' }); }, label: __('Active Filter BG', 'blockenberg') },
165 { value: attr.filterBtnActiveColor, onChange: function (v) { set({ filterBtnActiveColor: v || '#ffffff' }); }, label: __('Active Filter Text', 'blockenberg') },
166 { value: attr.accentColor, onChange: function (v) { set({ accentColor: v || '#6366f1' }); }, label: __('Accent', 'blockenberg') }
167 ]
168 })
169 );
170
171 /* Filter bar preview */
172 var filterBar = el('div', { className: 'bkbg-fc-filter bkbg-fc-filter--' + attr.filterStyle, style: { marginBottom: '24px' } },
173 attr.filterStyle === 'dropdown'
174 ? el('select', { className: 'bkbg-fc-dropdown', style: { background: attr.filterBtnBg, color: attr.filterBtnColor, border: '1px solid ' + attr.cardBorder, borderRadius: '6px', padding: '8px 14px' } },
175 cats.map(function (cat, i) { return el('option', { key: i }, cat); })
176 )
177 : el('div', { className: 'bkbg-fc-btns', style: { display: 'flex', gap: '8px', flexWrap: 'wrap' } },
178 cats.map(function (cat, i) {
179 var isAll = i === 0;
180 return el('span', {
181 key: i, className: 'bkbg-fc-btn' + (isAll ? ' active' : ''),
182 style: {
183 background: isAll ? attr.filterBtnActiveBg : attr.filterBtnBg,
184 color: isAll ? attr.filterBtnActiveColor : attr.filterBtnColor,
185 padding: '7px 16px', borderRadius: attr.filterStyle === 'tabs' ? '6px 6px 0 0' : '999px',
186 cursor: 'pointer',
187 border: attr.filterStyle === 'tabs' ? ('1px solid ' + attr.cardBorder) : 'none'
188 }
189 }, cat);
190 })
191 )
192 );
193
194 /* Cards grid */
195 var gridStyle = {
196 display: 'grid',
197 gridTemplateColumns: 'repeat(' + attr.columns + ', 1fr)',
198 gap: attr.gap + 'px'
199 };
200 if (attr.layout === 'list') {
201 gridStyle.gridTemplateColumns = '1fr';
202 }
203
204 var cardsGrid = el('div', { className: 'bkbg-fc-grid', style: gridStyle },
205 cards.map(function (card, idx) {
206 return el('div', {
207 key: idx,
208 className: 'bkbg-fc-card-wrap',
209 style: {
210 background: attr.cardBg,
211 border: '1px solid ' + (card.featured ? attr.accentColor : attr.cardBorder),
212 borderRadius: attr.cardRadius + 'px',
213 overflow: 'hidden',
214 position: 'relative',
215 display: attr.layout === 'list' ? 'flex' : 'block'
216 }
217 },
218 card.featured && el('div', { style: { position: 'absolute', top: 0, left: 0, right: 0, height: '3px', background: attr.accentColor } }),
219 /* Image */
220 attr.showImages && el('div', {
221 style: { position: 'relative', paddingTop: paddingTop, background: '#f3f4f6', overflow: 'hidden', flexShrink: 0, width: attr.layout === 'list' ? '200px' : 'auto' }
222 },
223 card.imageUrl
224 ? el('img', { src: card.imageUrl, alt: card.imageAlt || '', style: { position: 'absolute', top: 0, left: 0, width: '100%', height: '100%', objectFit: 'cover' } })
225 : el('div', { style: { position: 'absolute', inset: 0, display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#9ca3af', fontSize: '12px' } }, __('No image', 'blockenberg')),
226 el(MediaUploadCheck, {},
227 el(MediaUpload, {
228 onSelect: function (m) { updateCard(idx, 'imageUrl', m.url); updateCard(idx, 'imageId', m.id); updateCard(idx, 'imageAlt', m.alt || ''); },
229 allowedTypes: ['image'], value: card.imageId,
230 render: function (ref) {
231 return el(Button, { onClick: ref.open, variant: 'primary', isSmall: true, style: { position: 'absolute', bottom: '6px', right: '6px', zIndex: 1, fontSize: '11px' } }, card.imageUrl ? '' : '+');
232 }
233 })
234 )
235 ),
236 /* Body */
237 el('div', { style: { padding: '16px', flex: 1 } },
238 el('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: '8px' } },
239 el(TextControl, {
240 label: __('Category', 'blockenberg'),
241 value: card.category,
242 onChange: function (v) { updateCard(idx, 'category', v); },
243 placeholder: 'Category',
244 style: { flex: 1, marginRight: '8px' },
245 __nextHasNoMarginBottom: true
246 }),
247 attr.showLabels && el(TextControl, {
248 label: __('Label', 'blockenberg'),
249 value: card.label,
250 onChange: function (v) { updateCard(idx, 'label', v); },
251 placeholder: 'Label',
252 style: { width: '80px' },
253 __nextHasNoMarginBottom: true
254 })
255 ),
256 el(RichText, {
257 tagName: 'h3',
258 className: 'bkbg-fc-card-title',
259 value: card.title,
260 onChange: function (v) { updateCard(idx, 'title', v); },
261 placeholder: __('Card title', 'blockenberg'),
262 style: { color: attr.titleColor, margin: '0 0 6px' }
263 }),
264 attr.showDescriptions && el(RichText, {
265 tagName: 'p',
266 className: 'bkbg-fc-card-desc',
267 value: card.description,
268 onChange: function (v) { updateCard(idx, 'description', v); },
269 placeholder: __('Description...', 'blockenberg'),
270 style: { color: attr.descColor, margin: '0 0 10px' }
271 }),
272 attr.showLinks && el('div', { style: { display: 'flex', gap: '8px', alignItems: 'center' } },
273 el(TextControl, {
274 label: __('Link URL', 'blockenberg'),
275 value: card.link,
276 onChange: function (v) { updateCard(idx, 'link', v); },
277 placeholder: 'https://',
278 style: { flex: 1 },
279 __nextHasNoMarginBottom: true
280 }),
281 el(TextControl, {
282 label: __('Link Label', 'blockenberg'),
283 value: card.linkLabel,
284 onChange: function (v) { updateCard(idx, 'linkLabel', v); },
285 style: { width: '100px' },
286 __nextHasNoMarginBottom: true
287 })
288 ),
289 el(ToggleControl, {
290 label: __('Featured', 'blockenberg'),
291 checked: card.featured,
292 onChange: function (v) { updateCard(idx, 'featured', v); },
293 __nextHasNoMarginBottom: true
294 }),
295 el('div', { style: { display: 'flex', gap: '6px', marginTop: '8px' } },
296 el(Button, { onClick: function () { duplicateCard(idx); }, variant: 'secondary', isSmall: true }, __('Duplicate', 'blockenberg')),
297 el(Button, { onClick: function () { removeCard(idx); }, variant: 'link', isDestructive: true, isSmall: true }, __('Remove', 'blockenberg'))
298 )
299 )
300 );
301 }),
302 el('div', { style: { gridColumn: '1 / -1', textAlign: 'center', paddingTop: '8px' } },
303 el(Button, { onClick: addCard, variant: 'primary', isSmall: true }, __('+ Add Card', 'blockenberg'))
304 )
305 );
306
307 var wrapStyle = Object.assign(
308 { background: attr.bgColor || '', paddingTop: attr.paddingTop + 'px', paddingBottom: attr.paddingBottom + 'px' },
309 _tv()(attr.typoTitle, '--bkbg-fcards-tt-'),
310 _tv()(attr.typoDesc, '--bkbg-fcards-td-'),
311 _tv()(attr.typoFilter, '--bkbg-fcards-tf-')
312 );
313
314 return el(Fragment, {},
315 inspector,
316 el('div', useBlockProps({ className: 'bkbg-fcards-wrap', style: wrapStyle }),
317 filterBar,
318 cardsGrid
319 )
320 );
321 },
322
323 save: function (props) {
324 var attr = props.attributes;
325 return el('div', useBlockProps.save(),
326 el('div', { className: 'bkbg-fc-app', 'data-opts': JSON.stringify(attr) })
327 );
328 }
329 });
330 }() );
331