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

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

302 lines 21.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 useState = wp.element.useState;
4 var __ = wp.i18n.__;
5 var registerBlockType = wp.blocks.registerBlockType;
6 var InspectorControls = wp.blockEditor.InspectorControls;
7 var useBlockProps = wp.blockEditor.useBlockProps;
8 var MediaUpload = wp.blockEditor.MediaUpload;
9 var MediaUploadCheck = wp.blockEditor.MediaUploadCheck;
10 var PanelBody = wp.components.PanelBody;
11 var Button = wp.components.Button;
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 ColorPicker = wp.components.ColorPicker;
17 var Popover = wp.components.Popover;
18
19 var _tc, _tv;
20 function getTypoControl() { return _tc || (_tc = window.bkbgTypographyControl); }
21 function getTypoCssVars() { return _tv || (_tv = window.bkbgTypoCssVars); }
22
23 var CARD_STYLE_OPTIONS = [
24 { label: __('Card (border + shadow)', 'blockenberg'), value: 'card' },
25 { label: __('Flat (border only)', 'blockenberg'), value: 'flat' },
26 { label: __('Ghost (no border)', 'blockenberg'), value: 'ghost' },
27 ];
28 var HOVER_OPTIONS = [
29 { label: __('Lift (shadow)', 'blockenberg'), value: 'lift' },
30 { label: __('Border Glow', 'blockenberg'), value: 'glow' },
31 { label: __('None', 'blockenberg'), value: 'none' },
32 ];
33 var RATIO_OPTIONS = [
34 { label: __('16:9', 'blockenberg'), value: '16/9' },
35 { label: __('4:3', 'blockenberg'), value: '4/3' },
36 { label: __('1:1', 'blockenberg'), value: '1/1' },
37 { label: __('3:2', 'blockenberg'), value: '3/2' },
38 ];
39
40 function makeId() { return 'lc' + Math.random().toString(36).substr(2, 6); }
41
42 function buildWrapStyle(a) {
43 var s = {
44 '--bkbg-lc-columns': a.columns,
45 '--bkbg-lc-gap': a.gap + 'px',
46 '--bkbg-lc-name-color': a.nameColor,
47 '--bkbg-lc-address-color': a.addressColor,
48 '--bkbg-lc-meta-color': a.metaColor,
49 '--bkbg-lc-hours-color': a.hoursColor,
50 '--bkbg-lc-icon-color': a.iconColor,
51 '--bkbg-lc-dir-color': a.directionsColor,
52 '--bkbg-lc-dir-bg': a.directionsBg,
53 '--bkbg-lc-card-bg': a.cardBg,
54 '--bkbg-lc-border-color': a.borderColor,
55 '--bkbg-lc-card-r': a.cardRadius + 'px',
56 '--bkbg-lc-card-pad': a.cardPadding + 'px',
57 '--bkbg-lc-img-r': a.imageRadius + 'px',
58 '--bkbg-lc-btn-r': a.btnRadius + 'px',
59 paddingTop: a.paddingTop + 'px',
60 paddingBottom: a.paddingBottom + 'px',
61 backgroundColor: a.bgColor || undefined,
62 };
63 var _tvFn = getTypoCssVars();
64 if (_tvFn) {
65 Object.assign(s, _tvFn(a.nameTypo, '--bkbg-locc-n-'));
66 Object.assign(s, _tvFn(a.detailTypo, '--bkbg-locc-d-'));
67 Object.assign(s, _tvFn(a.btnTypo, '--bkbg-locc-bt-'));
68 }
69 return s;
70 }
71
72 function renderColorControl(key, label, value, onChange, openKey, setOpenKey) {
73 var isOpen = openKey === key;
74 return el('div', { key: key, style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '4px 0', gap: '8px' } },
75 el('span', { style: { fontSize: '12px', color: '#1e1e1e', flex: 1, lineHeight: 1.4 } }, label),
76 el('div', { style: { position: 'relative', flexShrink: 0 } },
77 el('button', { type: 'button', onClick: function () { setOpenKey(isOpen ? null : key); }, style: { width: '28px', height: '28px', borderRadius: '4px', border: isOpen ? '2px solid #007cba' : '2px solid #ddd', cursor: 'pointer', padding: 0, background: value || '#ccc' } }),
78 isOpen && el(Popover, { position: 'bottom left', onClose: function () { setOpenKey(null); } },
79 el('div', { style: { padding: '8px' } },
80 el(ColorPicker, { color: value, enableAlpha: true, onChange: onChange })
81 )
82 )
83 )
84 );
85 }
86
87 function LocationCardPreview(props) {
88 var loc = props.loc;
89 var a = props.a;
90 var cardShadow = a.cardStyle === 'card' ? '0 2px 12px rgba(0,0,0,0.08)' : 'none';
91 var cardBorder = a.cardStyle !== 'ghost' ? '1px solid ' + a.borderColor : 'none';
92 return el('div', { style: { background: a.cardBg, borderRadius: a.cardRadius + 'px', border: cardBorder, boxShadow: cardShadow, overflow: 'hidden', fontFamily: 'sans-serif' } },
93 /* Image */
94 a.showImage && el('div', { style: { aspectRatio: a.imageRatio, background: '#f3f4f6', overflow: 'hidden', display: 'flex', alignItems: 'center', justifyContent: 'center', borderRadius: a.imageRadius + 'px' } },
95 loc.imageUrl
96 ? el('img', { src: loc.imageUrl, style: { width: '100%', height: '100%', objectFit: 'cover' } })
97 : el('span', { style: { fontSize: '40px', opacity: 0.3 } }, '📍')
98 ),
99 /* Body */
100 el('div', { className: 'bkbg-lc-card-body', style: { padding: a.cardPadding + 'px' } },
101 el('div', { className: 'bkbg-lc-name', style: { color: a.nameColor } }, loc.name || __('Location Name', 'blockenberg')),
102 (loc.address || loc.city) && el('div', { className: 'bkbg-lc-address', style: { color: a.addressColor } },
103 loc.address && el('div', null, loc.address),
104 (loc.city || loc.state || loc.zip) && el('div', null, [loc.city, loc.state, loc.zip].filter(Boolean).join(', ') + (loc.country ? ', ' + loc.country : ''))
105 ),
106 /* Meta */
107 el('div', { style: { display: 'flex', flexDirection: 'column', gap: '5px', marginBottom: '12px' } },
108 a.showPhone && loc.phone && el('div', { className: 'bkbg-lc-phone', style: { color: a.metaColor, display: 'flex', gap: '6px', alignItems: 'center' } },
109 el('span', { style: { color: a.iconColor } }, '📞'), loc.phone
110 ),
111 a.showEmail && loc.email && el('div', { className: 'bkbg-lc-email', style: { color: a.metaColor, display: 'flex', gap: '6px', alignItems: 'center' } },
112 el('span', { style: { color: a.iconColor } }, '✉️'), loc.email
113 ),
114 a.showWebsite && loc.website && el('div', { className: 'bkbg-lc-phone', style: { color: a.directionsColor, display: 'flex', gap: '6px', alignItems: 'center' } },
115 el('span', { style: { color: a.iconColor } }, '🌐'), loc.website
116 ),
117 a.showHours && loc.hoursSnippet && el('div', { className: 'bkbg-lc-phone', style: { color: a.hoursColor, display: 'flex', gap: '6px', alignItems: 'center' } },
118 el('span', { style: { color: a.iconColor } }, '🕐'), loc.hoursSnippet
119 )
120 ),
121 a.showDirectionsLink && el('a', { className: 'bkbg-lc-btn-directions', href: loc.mapUrl || '#', style: { display: 'inline-flex', alignItems: 'center', gap: '6px', padding: '8px 16px', background: a.directionsBg, color: a.directionsColor, borderRadius: a.btnRadius + 'px', textDecoration: 'none' } },
122 '📌 ', a.directionsLabel
123 )
124 )
125 );
126 }
127
128 registerBlockType('blockenberg/location-cards', {
129 edit: function (props) {
130 var a = props.attributes;
131 var setA = props.setAttributes;
132 var openColor = useState(null);
133 var openKey = openColor[0];
134 var setOpenKey = openColor[1];
135
136 function setColor(key) {
137 return function (v) { var o = {}; o[key] = v; setA(o); };
138 }
139 function cc(key, label, val) {
140 return renderColorControl(key, label, val, setColor(key), openKey, setOpenKey);
141 }
142
143 function updateLocField(id, field, val) {
144 setA({ locations: a.locations.map(function (l) { return l.id === id ? Object.assign({}, l, { [field]: val }) : l; }) });
145 }
146 function removeLocation(id) {
147 setA({ locations: a.locations.filter(function (l) { return l.id !== id; }) });
148 }
149 function addLocation() {
150 setA({ locations: a.locations.concat([{ id: makeId(), name: 'New Location', address: '123 Main St', city: 'City', state: 'ST', zip: '00000', country: 'USA', phone: '', email: '', website: '', mapUrl: '#', hoursSnippet: 'Mon–Fri 9am–5pm', imageUrl: '', imageId: 0 }]) });
151 }
152
153 var inspector = el(InspectorControls, null,
154
155 /* Display Settings */
156 el(PanelBody, { title: __('Display Settings', 'blockenberg'), initialOpen: true },
157 el(ToggleControl, { label: __('Show Location Image', 'blockenberg'), checked: a.showImage, onChange: function (v) { setA({ showImage: v }); }, __nextHasNoMarginBottom: true }),
158 el(ToggleControl, { label: __('Show Phone Number', 'blockenberg'), checked: a.showPhone, onChange: function (v) { setA({ showPhone: v }); }, __nextHasNoMarginBottom: true }),
159 el(ToggleControl, { label: __('Show Email', 'blockenberg'), checked: a.showEmail, onChange: function (v) { setA({ showEmail: v }); }, __nextHasNoMarginBottom: true }),
160 el(ToggleControl, { label: __('Show Website', 'blockenberg'), checked: a.showWebsite, onChange: function (v) { setA({ showWebsite: v }); }, __nextHasNoMarginBottom: true }),
161 el(ToggleControl, { label: __('Show Hours', 'blockenberg'), checked: a.showHours, onChange: function (v) { setA({ showHours: v }); }, __nextHasNoMarginBottom: true }),
162 el(ToggleControl, { label: __('Show Directions Button', 'blockenberg'), checked: a.showDirectionsLink, onChange: function (v) { setA({ showDirectionsLink: v }); }, __nextHasNoMarginBottom: true }),
163 el(TextControl, { label: __('Directions Button Label', 'blockenberg'), value: a.directionsLabel, onChange: function (v) { setA({ directionsLabel: v }); } })
164 ),
165
166 /* Layout */
167 el(PanelBody, { title: __('Layout', 'blockenberg'), initialOpen: false },
168 el(RangeControl, { label: __('Columns (Desktop)', 'blockenberg'), value: a.columns, min: 1, max: 4, onChange: function (v) { setA({ columns: v }); } }),
169 el(RangeControl, { label: __('Columns (Tablet)', 'blockenberg'), value: a.columnsTablet, min: 1, max: 3, onChange: function (v) { setA({ columnsTablet: v }); } }),
170 el(RangeControl, { label: __('Columns (Mobile)', 'blockenberg'), value: a.columnsMobile, min: 1, max: 2, onChange: function (v) { setA({ columnsMobile: v }); } }),
171 el(RangeControl, { label: __('Gap Between Cards (px)', 'blockenberg'), value: a.gap, min: 0, max: 80, onChange: function (v) { setA({ gap: v }); } }),
172 el(SelectControl, { label: __('Card Style', 'blockenberg'), value: a.cardStyle, options: CARD_STYLE_OPTIONS, onChange: function (v) { setA({ cardStyle: v }); } }),
173 el(SelectControl, { label: __('Hover Effect', 'blockenberg'), value: a.hoverEffect, options: HOVER_OPTIONS, onChange: function (v) { setA({ hoverEffect: v }); } }),
174 el(SelectControl, { label: __('Image Aspect Ratio', 'blockenberg'), value: a.imageRatio, options: RATIO_OPTIONS, onChange: function (v) { setA({ imageRatio: v }); } }),
175 el(RangeControl, { label: __('Card Border Radius', 'blockenberg'), value: a.cardRadius, min: 0, max: 40, onChange: function (v) { setA({ cardRadius: v }); } }),
176 el(RangeControl, { label: __('Card Padding', 'blockenberg'), value: a.cardPadding, min: 8, max: 60, onChange: function (v) { setA({ cardPadding: v }); } }),
177 el(RangeControl, { label: __('Image Border Radius', 'blockenberg'), value: a.imageRadius, min: 0, max: 40, onChange: function (v) { setA({ imageRadius: v }); } }),
178 el(RangeControl, { label: __('Button Border Radius', 'blockenberg'), value: a.btnRadius, min: 0, max: 40, onChange: function (v) { setA({ btnRadius: v }); } })
179 ),
180
181 /* Typography */
182 el(PanelBody, { title: __('Typography', 'blockenberg'), initialOpen: false },
183 getTypoControl() && el(getTypoControl(), { label: __('Location Name', 'blockenberg'), value: a.nameTypo || {}, onChange: function (v) { setA({ nameTypo: v }); } }),
184 getTypoControl() && el(getTypoControl(), { label: __('Details', 'blockenberg'), value: a.detailTypo || {}, onChange: function (v) { setA({ detailTypo: v }); } }),
185 getTypoControl() && el(getTypoControl(), { label: __('Button', 'blockenberg'), value: a.btnTypo || {}, onChange: function (v) { setA({ btnTypo: v }); } })
186 ),
187
188 /* Colors */
189 el(PanelBody, { title: __('Colors', 'blockenberg'), initialOpen: false },
190 cc('bgColor', __('Section Background', 'blockenberg'), a.bgColor),
191 cc('cardBg', __('Card Background', 'blockenberg'), a.cardBg),
192 cc('borderColor', __('Border Color', 'blockenberg'), a.borderColor),
193 cc('nameColor', __('Location Name', 'blockenberg'), a.nameColor),
194 cc('addressColor', __('Address Text', 'blockenberg'), a.addressColor),
195 cc('metaColor', __('Contact Info', 'blockenberg'), a.metaColor),
196 cc('hoursColor', __('Hours Text', 'blockenberg'), a.hoursColor),
197 cc('iconColor', __('Icon Color', 'blockenberg'), a.iconColor),
198 cc('directionsColor', __('Directions Button Text', 'blockenberg'), a.directionsColor),
199 cc('directionsBg', __('Directions Button BG', 'blockenberg'), a.directionsBg)
200 ),
201
202 /* Spacing */
203 el(PanelBody, { title: __('Spacing', 'blockenberg'), initialOpen: false },
204 el(RangeControl, { label: __('Padding Top (px)', 'blockenberg'), value: a.paddingTop, min: 0, max: 200, onChange: function (v) { setA({ paddingTop: v }); } }),
205 el(RangeControl, { label: __('Padding Bottom (px)', 'blockenberg'), value: a.paddingBottom, min: 0, max: 200, onChange: function (v) { setA({ paddingBottom: v }); } })
206 ),
207
208 /* Locations */
209 el(PanelBody, { title: __('Locations', 'blockenberg'), initialOpen: false },
210 a.locations.map(function (loc) {
211 return el(PanelBody, { key: loc.id, title: loc.name || __('Location', 'blockenberg'), initialOpen: false },
212 el(TextControl, { label: __('Name', 'blockenberg'), value: loc.name, onChange: function (v) { updateLocField(loc.id, 'name', v); } }),
213 el(TextControl, { label: __('Street Address', 'blockenberg'), value: loc.address, onChange: function (v) { updateLocField(loc.id, 'address', v); } }),
214 el(TextControl, { label: __('City', 'blockenberg'), value: loc.city, onChange: function (v) { updateLocField(loc.id, 'city', v); } }),
215 el(TextControl, { label: __('State / Province', 'blockenberg'), value: loc.state, onChange: function (v) { updateLocField(loc.id, 'state', v); } }),
216 el(TextControl, { label: __('ZIP / Postcode', 'blockenberg'), value: loc.zip, onChange: function (v) { updateLocField(loc.id, 'zip', v); } }),
217 el(TextControl, { label: __('Country', 'blockenberg'), value: loc.country, onChange: function (v) { updateLocField(loc.id, 'country', v); } }),
218 el(TextControl, { label: __('Phone', 'blockenberg'), value: loc.phone, onChange: function (v) { updateLocField(loc.id, 'phone', v); } }),
219 el(TextControl, { label: __('Email', 'blockenberg'), value: loc.email, onChange: function (v) { updateLocField(loc.id, 'email', v); } }),
220 el(TextControl, { label: __('Website', 'blockenberg'), value: loc.website, onChange: function (v) { updateLocField(loc.id, 'website', v); } }),
221 el(TextControl, { label: __('Google Maps URL', 'blockenberg'), value: loc.mapUrl, onChange: function (v) { updateLocField(loc.id, 'mapUrl', v); } }),
222 el(TextControl, { label: __('Hours Snippet', 'blockenberg'), value: loc.hoursSnippet, onChange: function (v) { updateLocField(loc.id, 'hoursSnippet', v); } }),
223 el(MediaUploadCheck, null,
224 el(MediaUpload, {
225 onSelect: function (m) { updateLocField(loc.id, 'imageUrl', m.url); updateLocField(loc.id, 'imageId', m.id); },
226 allowedTypes: ['image'],
227 value: loc.imageId,
228 render: function (ref) {
229 return el('div', { style: { marginTop: '6px' } },
230 loc.imageUrl && el('img', { src: loc.imageUrl, style: { width: '100%', borderRadius: '4px', marginBottom: '6px' } }),
231 el(Button, { onClick: ref.open, variant: loc.imageId ? 'secondary' : 'primary' },
232 loc.imageId ? __('Replace Image', 'blockenberg') : __('Set Location Image', 'blockenberg')
233 ),
234 loc.imageId && el(Button, { onClick: function () { updateLocField(loc.id, 'imageUrl', ''); updateLocField(loc.id, 'imageId', 0); }, variant: 'link', isDestructive: true }, __('Remove', 'blockenberg'))
235 );
236 }
237 })
238 ),
239 el(Button, { variant: 'link', isDestructive: true, onClick: function () { removeLocation(loc.id); } }, __('Delete Location', 'blockenberg'))
240 );
241 }),
242 el(Button, { variant: 'secondary', onClick: addLocation }, __('+ Add Location', 'blockenberg'))
243 )
244 );
245
246 /* — Preview grid — */
247 var gridStyle = {
248 display: 'grid',
249 gridTemplateColumns: 'repeat(' + a.columns + ', 1fr)',
250 gap: a.gap + 'px',
251 };
252
253 return el('div', useBlockProps({ style: buildWrapStyle(a) }),
254 inspector,
255 el('div', { style: gridStyle },
256 a.locations.map(function (loc) {
257 return el('div', { key: loc.id },
258 el(LocationCardPreview, { loc: loc, a: a })
259 );
260 })
261 )
262 );
263 },
264
265 save: function (props) {
266 var a = props.attributes;
267 return el('div', Object.assign({}, useBlockProps.save(), {
268 className: 'bkbg-lc-wrap',
269 style: buildWrapStyle(a),
270 'data-columns-tablet': a.columnsTablet,
271 'data-columns-mobile': a.columnsMobile,
272 'data-card-style': a.cardStyle,
273 'data-hover': a.hoverEffect,
274 }),
275 el('div', { className: 'bkbg-lc-grid' },
276 a.locations.map(function (loc) {
277 return el('div', { key: loc.id, className: 'bkbg-lc-card' },
278 a.showImage && el('div', { className: 'bkbg-lc-image-wrap' },
279 loc.imageUrl && el('img', { className: 'bkbg-lc-image', src: loc.imageUrl, alt: loc.name, loading: 'lazy' })
280 ),
281 el('div', { className: 'bkbg-lc-body' },
282 el('h3', { className: 'bkbg-lc-name' }, loc.name),
283 el('address', { className: 'bkbg-lc-address' },
284 loc.address && el('span', { className: 'bkbg-lc-street' }, loc.address),
285 (loc.city || loc.state) && el('span', { className: 'bkbg-lc-city-state' }, [loc.city, loc.state, loc.zip].filter(Boolean).join(', '))
286 ),
287 el('div', { className: 'bkbg-lc-meta' },
288 a.showPhone && loc.phone && el('div', { className: 'bkbg-lc-meta-row' }, el('span', { className: 'bkbg-lc-icon' }, '📞'), el('a', { href: 'tel:' + loc.phone }, loc.phone)),
289 a.showEmail && loc.email && el('div', { className: 'bkbg-lc-meta-row' }, el('span', { className: 'bkbg-lc-icon' }, '✉️'), el('a', { href: 'mailto:' + loc.email }, loc.email)),
290 a.showWebsite && loc.website && el('div', { className: 'bkbg-lc-meta-row' }, el('span', { className: 'bkbg-lc-icon' }, '🌐'), el('a', { href: loc.website, target: '_blank', rel: 'noopener' }, loc.website)),
291 a.showHours && loc.hoursSnippet && el('div', { className: 'bkbg-lc-meta-row' }, el('span', { className: 'bkbg-lc-icon' }, '🕐'), el('span', null, loc.hoursSnippet))
292 ),
293 a.showDirectionsLink && loc.mapUrl && el('a', { className: 'bkbg-lc-directions-btn', href: loc.mapUrl, target: '_blank', rel: 'noopener' }, '📌 ' + a.directionsLabel)
294 )
295 );
296 })
297 )
298 );
299 },
300 });
301 }() );
302