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 / category-grid / frontend.js

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

291 lines 14.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /* Category Grid — frontend */
2 (function () {
3 'use strict';
4
5 function typoCssVarsForEl(typo, prefix, el) {
6 if (!typo) return;
7 if (typo.family) el.style.setProperty(prefix + 'font-family', "'" + typo.family + "', sans-serif");
8 if (typo.weight) el.style.setProperty(prefix + 'font-weight', typo.weight);
9 if (typo.transform) el.style.setProperty(prefix + 'text-transform', typo.transform);
10 if (typo.style) el.style.setProperty(prefix + 'font-style', typo.style);
11 if (typo.decoration) el.style.setProperty(prefix + 'text-decoration', typo.decoration);
12 var su = typo.sizeUnit || 'px';
13 if (typo.sizeDesktop !== '' && typo.sizeDesktop != null) el.style.setProperty(prefix + 'font-size-d', typo.sizeDesktop + su);
14 if (typo.sizeTablet !== '' && typo.sizeTablet != null) el.style.setProperty(prefix + 'font-size-t', typo.sizeTablet + su);
15 if (typo.sizeMobile !== '' && typo.sizeMobile != null) el.style.setProperty(prefix + 'font-size-m', typo.sizeMobile + su);
16 var lhu = typo.lineHeightUnit || '';
17 if (typo.lineHeightDesktop !== '' && typo.lineHeightDesktop != null) el.style.setProperty(prefix + 'line-height-d', typo.lineHeightDesktop + lhu);
18 if (typo.lineHeightTablet !== '' && typo.lineHeightTablet != null) el.style.setProperty(prefix + 'line-height-t', typo.lineHeightTablet + lhu);
19 if (typo.lineHeightMobile !== '' && typo.lineHeightMobile != null) el.style.setProperty(prefix + 'line-height-m', typo.lineHeightMobile + lhu);
20 var lsu = typo.letterSpacingUnit || 'px';
21 if (typo.letterSpacingDesktop !== '' && typo.letterSpacingDesktop != null) el.style.setProperty(prefix + 'letter-spacing-d', typo.letterSpacingDesktop + lsu);
22 if (typo.letterSpacingTablet !== '' && typo.letterSpacingTablet != null) el.style.setProperty(prefix + 'letter-spacing-t', typo.letterSpacingTablet + lsu);
23 if (typo.letterSpacingMobile !== '' && typo.letterSpacingMobile != null) el.style.setProperty(prefix + 'letter-spacing-m', typo.letterSpacingMobile + lsu);
24 var wsu = typo.wordSpacingUnit || 'px';
25 if (typo.wordSpacingDesktop !== '' && typo.wordSpacingDesktop != null) el.style.setProperty(prefix + 'word-spacing-d', typo.wordSpacingDesktop + wsu);
26 if (typo.wordSpacingTablet !== '' && typo.wordSpacingTablet != null) el.style.setProperty(prefix + 'word-spacing-t', typo.wordSpacingTablet + wsu);
27 if (typo.wordSpacingMobile !== '' && typo.wordSpacingMobile != null) el.style.setProperty(prefix + 'word-spacing-m', typo.wordSpacingMobile + wsu);
28 }
29
30 /* ── emoji for categories (hash-based stable assignment) ─────────────────── */
31 var DEFAULT_EMOJIS = ['📁', '📂', '🗂️', '📌', '🔖', '🏷️', '💡', '✏️', '📝', '📊', '🎯', '🔍'];
32
33 function emojiForName(name) {
34 var hash = 0;
35 for (var i = 0; i < name.length; i++) {
36 hash = (hash * 31 + name.charCodeAt(i)) | 0;
37 }
38 return DEFAULT_EMOJIS[Math.abs(hash) % DEFAULT_EMOJIS.length];
39 }
40
41 function getCatColor(index, colorMode, multiColors, accent) {
42 if (colorMode === 'multi') {
43 var cols = (multiColors || '').split(',').map(function (c) { return c.trim(); }).filter(Boolean);
44 if (cols.length) { return cols[index % cols.length]; }
45 }
46 return accent || '#6c3fb5';
47 }
48
49 /* ── build a single card ──────────────────────────────────────────────────── */
50 function buildCard(cat, index, cfg) {
51 var accent = getCatColor(index, cfg.colorMode, cfg.multiColors, cfg.accent);
52 var emoji = emojiForName(cat.name);
53 var showBar = cfg.cardStyle === 'card-accent';
54 var isCircle = cfg.cardStyle === 'icon-circle';
55 var isLeft = cfg.iconPos === 'left';
56
57 var a = document.createElement('a');
58 a.className = 'bkcg-card';
59 a.href = cat.link || '#';
60 a.target = cfg.linkTarget || '_self';
61 if (cfg.linkTarget === '_blank') { a.rel = 'noopener noreferrer'; }
62
63 Object.assign(a.style, {
64 background: cfg.cardBg,
65 borderRadius: cfg.cardRadius + 'px',
66 padding: cfg.cardPadding + 'px',
67 boxShadow: cfg.cardShadow ? '0 2px 16px rgba(0,0,0,0.08)' : 'none',
68 border: cfg.cardBorder ? '1px solid ' + cfg.borderColor : 'none',
69 textAlign: cfg.textAlign,
70 display: 'flex',
71 flexDirection: isLeft ? 'row' : 'column',
72 alignItems: isLeft ? 'flex-start' : (cfg.textAlign === 'center' ? 'center' : 'flex-start'),
73 gap: isLeft ? '12px' : '8px',
74 textDecoration: 'none',
75 color: 'inherit',
76 position: 'relative',
77 overflow: 'hidden',
78 });
79
80 a.setAttribute('data-bg-hover', cfg.cardBgHover);
81 a.setAttribute('data-bg', cfg.cardBg);
82
83 /* accent top bar */
84 if (showBar) {
85 var bar = document.createElement('div');
86 bar.style.cssText = 'position:absolute;top:0;left:0;right:0;height:3px;background:' + accent;
87 a.appendChild(bar);
88 }
89
90 /* icon */
91 if (cfg.showIcon) {
92 var iconWrap = document.createElement('div');
93 iconWrap.className = 'bkcg-icon';
94 Object.assign(iconWrap.style, {
95 fontSize: cfg.iconSize + 'px',
96 lineHeight: '1',
97 flexShrink: '0',
98 background: isCircle ? accent + '18' : 'transparent',
99 width: isCircle ? (cfg.iconSize * 1.8) + 'px' : undefined,
100 height: isCircle ? (cfg.iconSize * 1.8) + 'px' : undefined,
101 borderRadius: isCircle ? '50%' : undefined,
102 display: isCircle ? 'flex' : 'block',
103 alignItems: 'center',
104 justifyContent: 'center',
105 });
106 var useChar = !cfg.iconType || cfg.iconType === 'custom-char';
107 if (useChar) {
108 iconWrap.textContent = emoji;
109 } else {
110 var IP = window.bkbgIconPicker;
111 var iconEl = IP && IP.buildFrontendIcon(cfg.iconType, null, cfg.iconDashicon, cfg.iconImageUrl, cfg.iconDashiconColor);
112 if (iconEl) iconWrap.appendChild(iconEl);
113 else iconWrap.textContent = emoji;
114 }
115 a.appendChild(iconWrap);
116 }
117
118 /* body */
119 var body = document.createElement('div');
120 body.className = 'bkcg-body';
121 body.style.flex = '1';
122 body.style.minWidth = '0';
123
124 var nameEl = document.createElement('p');
125 nameEl.className = 'bkcg-name';
126 nameEl.textContent = cat.name;
127 Object.assign(nameEl.style, {
128 margin: '0 0 4px', color: cfg.nameColor
129 });
130 body.appendChild(nameEl);
131
132 if (cfg.showCount) {
133 var countEl = document.createElement('span');
134 countEl.className = 'bkcg-count';
135 countEl.textContent = cat.count + ' ' + (cfg.countLabel || 'posts');
136 Object.assign(countEl.style, {
137 display: 'inline-block', background: cfg.countBg, color: cfg.countColor,
138 borderRadius: '999px', padding: '1px 8px', fontSize: '11px', fontWeight: '600', marginBottom: '6px'
139 });
140 body.appendChild(countEl);
141 }
142
143 if (cfg.showDesc && cat.description) {
144 var descEl = document.createElement('p');
145 descEl.className = 'bkcg-desc';
146 var desc = cat.description.replace(/<[^>]+>/g, '').slice(0, cfg.descLen || 80);
147 descEl.textContent = desc;
148 Object.assign(descEl.style, {
149 margin: '4px 0 0', color: cfg.descColor
150 });
151 body.appendChild(descEl);
152 }
153
154 if (cfg.showArrow) {
155 var arrow = document.createElement('span');
156 arrow.className = 'bkcg-arrow';
157 arrow.textContent = '';
158 Object.assign(arrow.style, { display: 'block', marginTop: '8px', color: accent, fontSize: '18px' });
159 body.appendChild(arrow);
160 }
161
162 a.appendChild(body);
163 return a;
164 }
165
166 /* ── hover effect ────────────────────────────────────────────────────────── */
167 function attachHover(grid, cfg) {
168 if (grid.getAttribute('data-animate') !== '1') { return; }
169
170 grid.addEventListener('mouseover', function (e) {
171 var card = e.target.closest('.bkcg-card');
172 if (!card) { return; }
173 card.style.background = cfg.cardBgHover;
174 });
175
176 grid.addEventListener('mouseout', function (e) {
177 var card = e.target.closest('.bkcg-card');
178 if (!card) { return; }
179 card.style.background = cfg.cardBg;
180 });
181 }
182
183 /* ── read all config from data attributes ─────────────────────────────────── */
184 function parseConfig(grid) {
185 return {
186 max: parseInt(grid.getAttribute('data-max'), 10) || 9,
187 orderBy: grid.getAttribute('data-order-by') || 'count',
188 order: grid.getAttribute('data-order') || 'desc',
189 hideEmpty: grid.getAttribute('data-hide-empty') !== '0',
190 exclude: grid.getAttribute('data-exclude') || '',
191 include: grid.getAttribute('data-include') || '',
192 columns: parseInt(grid.getAttribute('data-columns'), 10) || 3,
193 gap: parseInt(grid.getAttribute('data-gap'), 10) || 16,
194 cardStyle: grid.getAttribute('data-card-style') || 'card',
195 iconPos: grid.getAttribute('data-icon-pos') || 'top',
196 showIcon: grid.getAttribute('data-show-icon') !== '0',
197 iconSize: parseInt(grid.getAttribute('data-icon-size'), 10) || 36,
198 iconType: grid.getAttribute('data-icon-type') || 'custom-char',
199 iconDashicon: grid.getAttribute('data-icon-dashicon') || '',
200 iconImageUrl: grid.getAttribute('data-icon-image-url') || '',
201 showCount: grid.getAttribute('data-show-count') !== '0',
202 countLabel: grid.getAttribute('data-count-label') || 'posts',
203 showDesc: grid.getAttribute('data-show-desc') === '1',
204 descLen: parseInt(grid.getAttribute('data-desc-len'), 10) || 80,
205 showArrow: grid.getAttribute('data-show-arrow') !== '0',
206 linkTarget: grid.getAttribute('data-link-target') || '_self',
207 textAlign: grid.getAttribute('data-text-align') || 'center',
208 colorMode: grid.getAttribute('data-color-mode') || 'single',
209 multiColors: grid.getAttribute('data-multi-colors') || '',
210 accent: grid.getAttribute('data-accent') || '#6c3fb5',
211 nameColor: grid.getAttribute('data-name-color') || '#1e1e1e',
212 nameSize: parseInt(grid.getAttribute('data-name-size'), 10) || 16,
213 countColor: grid.getAttribute('data-count-color') || '#6c3fb5',
214 countBg: grid.getAttribute('data-count-bg') || '#ede9fe',
215 descColor: grid.getAttribute('data-desc-color') || '#6b7280',
216 descSize: parseInt(grid.getAttribute('data-desc-size'), 10) || 13,
217 cardBg: grid.getAttribute('data-card-bg') || '#ffffff',
218 cardBgHover: grid.getAttribute('data-card-bg-hover') || '#f5f3ff',
219 cardRadius: parseInt(grid.getAttribute('data-card-radius'), 10) || 12,
220 cardPadding: parseInt(grid.getAttribute('data-card-padding'), 10) || 24,
221 cardShadow: grid.getAttribute('data-card-shadow') !== '0',
222 cardBorder: grid.getAttribute('data-card-border') === '1',
223 borderColor: grid.getAttribute('data-border-color') || '#e5e7eb',
224 };
225 }
226
227 /* ── main init ───────────────────────────────────────────────────────────── */
228 function init() {
229 document.querySelectorAll('.bkcg-grid[data-max]').forEach(function (grid) {
230 if (grid._bkcgInit) { return; }
231 grid._bkcgInit = true;
232
233 var cfg = parseConfig(grid);
234
235 /* apply grid CSS */
236 grid.style.gridTemplateColumns = 'repeat(' + cfg.columns + ', 1fr)';
237 grid.style.gap = cfg.gap + 'px';
238
239 /* typography CSS vars */
240 var typoName = {};
241 var typoDesc = {};
242 try { typoName = JSON.parse(grid.getAttribute('data-typo-name') || '{}'); } catch (e) {}
243 try { typoDesc = JSON.parse(grid.getAttribute('data-typo-desc') || '{}'); } catch (e) {}
244 typoCssVarsForEl(typoName, '--bkcg-n-', grid);
245 typoCssVarsForEl(typoDesc, '--bkcg-d-', grid);
246
247 /* build API URL */
248 var apiBase = (window.wpApiSettings && window.wpApiSettings.root)
249 ? window.wpApiSettings.root.replace(/\/$/, '')
250 : '/wp-json';
251
252 var params = [
253 'per_page=' + cfg.max,
254 'orderby=' + cfg.orderBy,
255 'order=' + cfg.order,
256 cfg.hideEmpty ? 'hide_empty=true' : '',
257 cfg.exclude ? 'exclude=' + cfg.exclude : '',
258 cfg.include ? 'include=' + cfg.include : '',
259 '_fields=id,name,count,description,link',
260 ].filter(Boolean).join('&');
261
262 var url = apiBase + '/wp/v2/categories?' + params;
263
264 fetch(url)
265 .then(function (r) { return r.json(); })
266 .then(function (cats) {
267 grid.innerHTML = '';
268 cats.forEach(function (cat, idx) {
269 var card = buildCard({
270 name: cat.name,
271 count: cat.count,
272 description: cat.description ? cat.description.replace(/<[^>]+>/g, '') : '',
273 link: cat.link || '#',
274 }, idx, cfg);
275 grid.appendChild(card);
276 });
277 attachHover(grid, cfg);
278 })
279 .catch(function () {
280 grid.innerHTML = '<p style="color:#888;text-align:center;padding:24px;">Could not load categories.</p>';
281 });
282 });
283 }
284
285 if (document.readyState === 'loading') {
286 document.addEventListener('DOMContentLoaded', init);
287 } else {
288 init();
289 }
290 }());
291