PluginProbe
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor / 2.0.11
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor v2.0.11
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 / bracket / index.js

index.js in Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor 2.0.11, at blocks/bracket/index.js

349 lines 19.1 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 useState = wp.element.useState;
5 var registerBlockType = wp.blocks.registerBlockType;
6 var useBlockProps = wp.blockEditor.useBlockProps;
7 var InspectorControls = wp.blockEditor.InspectorControls;
8 var PanelColorSettings = wp.blockEditor.PanelColorSettings;
9 var PanelBody = wp.components.PanelBody;
10 var TextControl = wp.components.TextControl;
11 var ToggleControl = wp.components.ToggleControl;
12 var RangeControl = wp.components.RangeControl;
13 var SelectControl = wp.components.SelectControl;
14 var Button = wp.components.Button;
15 var __ = wp.i18n.__;
16
17 function getTypographyControl() {
18 return (window.bkbgTypographyControl || function () { return null; });
19 }
20
21 function _tv(typo, prefix) {
22 if (!typo) return {};
23 var s = {};
24 if (typo.family) s[prefix + 'font-family'] = "'" + typo.family + "', sans-serif";
25 if (typo.weight) s[prefix + 'font-weight'] = typo.weight;
26 if (typo.transform) s[prefix + 'text-transform'] = typo.transform;
27 if (typo.style) s[prefix + 'font-style'] = typo.style;
28 if (typo.decoration) s[prefix + 'text-decoration'] = typo.decoration;
29 var su = typo.sizeUnit || 'px';
30 if (typo.sizeDesktop !== '' && typo.sizeDesktop != null) s[prefix + 'font-size-d'] = typo.sizeDesktop + su;
31 if (typo.sizeTablet !== '' && typo.sizeTablet != null) s[prefix + 'font-size-t'] = typo.sizeTablet + su;
32 if (typo.sizeMobile !== '' && typo.sizeMobile != null) s[prefix + 'font-size-m'] = typo.sizeMobile + su;
33 var lhu = typo.lineHeightUnit || '';
34 if (typo.lineHeightDesktop !== '' && typo.lineHeightDesktop != null) s[prefix + 'line-height-d'] = typo.lineHeightDesktop + lhu;
35 if (typo.lineHeightTablet !== '' && typo.lineHeightTablet != null) s[prefix + 'line-height-t'] = typo.lineHeightTablet + lhu;
36 if (typo.lineHeightMobile !== '' && typo.lineHeightMobile != null) s[prefix + 'line-height-m'] = typo.lineHeightMobile + lhu;
37 var lsu = typo.letterSpacingUnit || 'px';
38 if (typo.letterSpacingDesktop !== '' && typo.letterSpacingDesktop != null) s[prefix + 'letter-spacing-d'] = typo.letterSpacingDesktop + lsu;
39 if (typo.letterSpacingTablet !== '' && typo.letterSpacingTablet != null) s[prefix + 'letter-spacing-t'] = typo.letterSpacingTablet + lsu;
40 if (typo.letterSpacingMobile !== '' && typo.letterSpacingMobile != null) s[prefix + 'letter-spacing-m'] = typo.letterSpacingMobile + lsu;
41 var wsu = typo.wordSpacingUnit || 'px';
42 if (typo.wordSpacingDesktop !== '' && typo.wordSpacingDesktop != null) s[prefix + 'word-spacing-d'] = typo.wordSpacingDesktop + wsu;
43 if (typo.wordSpacingTablet !== '' && typo.wordSpacingTablet != null) s[prefix + 'word-spacing-t'] = typo.wordSpacingTablet + wsu;
44 if (typo.wordSpacingMobile !== '' && typo.wordSpacingMobile != null) s[prefix + 'word-spacing-m'] = typo.wordSpacingMobile + wsu;
45 return s;
46 }
47
48 // ── bracket logic ────────────────────────────────────────────────────────────
49 function getRounds(bracketSize) {
50 var rounds = [];
51 var n = bracketSize;
52 while (n >= 2) { rounds.push(n / 2); n = n / 2; }
53 return rounds; // e.g. [4, 2, 1] for bracketSize=8
54 }
55
56 function getMatchKey(r, m) { return 'r' + r + '_m' + m; }
57
58 function getMatchTeams(r, m, teams, winners) {
59 if (r === 0) {
60 return [teams[m * 2] || '?', teams[m * 2 + 1] || '?'];
61 }
62 var t1key = getMatchKey(r - 1, m * 2);
63 var t2key = getMatchKey(r - 1, m * 2 + 1);
64 return [winners[t1key] || '?', winners[t2key] || '?'];
65 }
66
67 function setWinner(winners, r, m, team) {
68 var newWinners = Object.assign({}, winners);
69 var key = getMatchKey(r, m);
70 // If winner changes, clear downstream
71 if (newWinners[key] !== team) {
72 newWinners[key] = team;
73 // Clear all keys that depended on this match
74 clearDownstream(newWinners, r, m);
75 }
76 return newWinners;
77 }
78
79 function clearDownstream(winners, r, m) {
80 // The result of (r, m) feeds into (r+1, Math.floor(m/2))
81 var nextR = r + 1;
82 var nextM = Math.floor(m / 2);
83 var nextKey = getMatchKey(nextR, nextM);
84 // Only clear if there's a winner set downstream
85 if (winners[nextKey]) {
86 delete winners[nextKey];
87 clearDownstream(winners, nextR, nextM);
88 }
89 }
90
91 // ── Match component ──────────────────────────────────────────────────────────
92 function Match(props) {
93 var a = props.a;
94 var roundIdx = props.roundIdx;
95 var matchIdx = props.matchIdx;
96 var teams = props.teams;
97 var winners = props.winners;
98 var setAttributes = props.setAttributes;
99 var isLast = props.isLast; // last match in last round
100
101 var matchTeams = getMatchTeams(roundIdx, matchIdx, teams, winners);
102 var winnerKey = getMatchKey(roundIdx, matchIdx);
103 var currentWinner = winners[winnerKey] || null;
104
105 function pickWinner(team) {
106 if (team === '?') return;
107 var newWinners = setWinner(Object.assign({}, winners), roundIdx, matchIdx, team);
108 setAttributes({ winners: newWinners });
109 }
110
111 return el('div', { className: 'bkbg-bk-match', style: { borderRadius: a.borderRadius + 'px', borderColor: a.matchBorderColor } },
112 matchTeams.map(function (team, ti) {
113 var isWinner = currentWinner === team && team !== '?';
114 var isLoser = currentWinner && currentWinner !== team && team !== '?';
115 var available = team !== '?';
116
117 return el('div', {
118 key: ti,
119 className: 'bkbg-bk-team' + (isWinner ? ' is-winner' : '') + (isLoser ? ' is-loser' : '') + (available ? ' is-available' : ''),
120 style: {
121 background: isWinner ? a.winnerBg : a.matchBg,
122 color: isWinner ? a.winnerColor : (isLoser ? a.loserColor : a.teamColor),
123 borderColor: isWinner ? a.winnerBorderColor : 'transparent',
124 cursor: available ? 'pointer' : 'default'
125 },
126 onClick: function () { if (available) pickWinner(team); },
127 title: available ? __('Click to set as winner') : ''
128 },
129 isWinner && el('span', { className: 'bkbg-bk-win-icon' }, '�
130 '),
131 el('span', { className: 'bkbg-bk-team-name' }, team)
132 );
133 })
134 );
135 }
136
137 // ── BracketPreview ───────────────────────────────────────────────────────────
138 function BracketPreview(props) {
139 var a = props.a;
140 var setAttributes = props.setAttributes;
141
142 var rounds = getRounds(a.bracketSize);
143 var numRounds = rounds.length;
144
145 // Default round labels: expand if needed
146 var defaultLabels = ['Round of ' + a.bracketSize, 'Quarterfinals', 'Semifinals', 'Final'];
147 function getRoundLabel(i) {
148 if (a.roundLabels && a.roundLabels[i] !== undefined) return a.roundLabels[i];
149 if (i === numRounds - 1) return 'Final';
150 if (i === numRounds - 2) return 'Semifinals';
151 return 'Round ' + (i + 1);
152 }
153
154 // Champion
155 var finalMatchKey = getMatchKey(numRounds - 1, 0);
156 var champion = a.winners[finalMatchKey] || null;
157
158 return el('div', { className: 'bkbg-bk-block', style: { background: a.bgColor, borderRadius: a.borderRadius + 'px' } },
159 a.showTitle && el('h3', {
160 className: 'bkbg-bk-title',
161 style: { color: a.titleColor },
162 contentEditable: true,
163 suppressContentEditableWarning: true,
164 onBlur: function (e) { setAttributes({ bracketTitle: e.target.textContent }); }
165 }, a.bracketTitle),
166 el('div', { className: 'bkbg-bk-grid' },
167 rounds.map(function (matchCount, ri) {
168 return el('div', { key: ri, className: 'bkbg-bk-round' },
169 a.showRoundLabels && el('div', { className: 'bkbg-bk-round-label', style: { color: a.roundLabelColor } },
170 getRoundLabel(ri)
171 ),
172 el('div', { className: 'bkbg-bk-matches' },
173 Array.from({ length: matchCount }, function (_, mi) {
174 return el(Match, {
175 key: mi,
176 a: a,
177 roundIdx: ri,
178 matchIdx: mi,
179 teams: a.teams,
180 winners: a.winners,
181 setAttributes: setAttributes
182 });
183 })
184 )
185 );
186 }),
187 // Champion column
188 el('div', { className: 'bkbg-bk-round bkbg-bk-champion-col' },
189 a.showRoundLabels && el('div', { className: 'bkbg-bk-round-label', style: { color: a.roundLabelColor } }, ''),
190 el('div', { className: 'bkbg-bk-matches' },
191 el('div', {
192 className: 'bkbg-bk-champion',
193 style: {
194 background: a.championBg,
195 borderColor: a.championBorderColor,
196 color: a.championColor,
197 borderRadius: a.borderRadius + 'px'
198 }
199 },
200 el('div', { className: 'bkbg-bk-champion-label' }, a.winnerLabel),
201 el('div', { className: 'bkbg-bk-champion-name' }, champion || '')
202 )
203 )
204 )
205 )
206 );
207 }
208
209 // ── register ─────────────────────────────────────────────────────────────────
210 registerBlockType('blockenberg/bracket', {
211 edit: function (props) {
212 var a = props.attributes;
213 var setAttributes = props.setAttributes;
214
215 function updateTeam(idx, val) {
216 var newTeams = a.teams.slice();
217 newTeams[idx] = val;
218 setAttributes({ teams: newTeams });
219 }
220
221 function updateRoundLabel(idx, val) {
222 var newLabels = (a.roundLabels || []).slice();
223 while (newLabels.length <= idx) newLabels.push('');
224 newLabels[idx] = val;
225 setAttributes({ roundLabels: newLabels });
226 }
227
228 var rounds = getRounds(a.bracketSize);
229
230 return el(Fragment, null,
231 el(InspectorControls, null,
232 el(PanelBody, { title: __('Bracket Setup'), initialOpen: true },
233 el(SelectControl, {
234 label: __('Bracket size'),
235 value: String(a.bracketSize),
236 __nextHasNoMarginBottom: true,
237 options: [
238 { value: '4', label: '4 Teams (2 rounds)' },
239 { value: '8', label: '8 Teams (3 rounds)' },
240 { value: '16', label: '16 Teams (4 rounds)' }
241 ],
242 onChange: function (v) {
243 var size = parseInt(v, 10);
244 var newTeams = Array.from({ length: size }, function (_, i) {
245 return a.teams[i] || 'Team ' + (i + 1);
246 });
247 setAttributes({ bracketSize: size, teams: newTeams, winners: {} });
248 }
249 }),
250 el(Button, {
251 variant: 'secondary',
252 isSmall: true,
253 onClick: function () { setAttributes({ winners: {} }); },
254 style: { marginTop: '6px' }
255 }, __('Reset all results'))
256 ),
257 el(PanelBody, { title: __('Teams'), initialOpen: true },
258 a.teams.map(function (team, i) {
259 return el(TextControl, {
260 key: i,
261 label: __('Team ') + (i + 1),
262 value: team,
263 __nextHasNoMarginBottom: true,
264 onChange: function (v) { updateTeam(i, v); }
265 });
266 })
267 ),
268 el(PanelBody, { title: __('Round Labels'), initialOpen: false },
269 rounds.map(function (_, ri) {
270 var def = ri === rounds.length - 1 ? 'Final' : (ri === rounds.length - 2 ? 'Semifinals' : 'Round ' + (ri + 1));
271 return el(TextControl, {
272 key: ri,
273 label: __('Round ') + (ri + 1),
274 value: (a.roundLabels && a.roundLabels[ri]) || def,
275 __nextHasNoMarginBottom: true,
276 onChange: function (v) { updateRoundLabel(ri, v); }
277 });
278 }),
279 el(TextControl, {
280 label: __('Winner label'),
281 value: a.winnerLabel,
282 __nextHasNoMarginBottom: true,
283 onChange: function (v) { setAttributes({ winnerLabel: v }); }
284 })
285 ),
286 el(PanelBody, { title: __('Display'), initialOpen: false },
287 el(ToggleControl, {
288 label: __('Show bracket title'),
289 checked: a.showTitle,
290 __nextHasNoMarginBottom: true,
291 onChange: function (v) { setAttributes({ showTitle: v }); }
292 }),
293 el(ToggleControl, {
294 label: __('Show round labels'),
295 checked: a.showRoundLabels,
296 __nextHasNoMarginBottom: true,
297 onChange: function (v) { setAttributes({ showRoundLabels: v }); }
298 }),
299 el(RangeControl, {
300 label: __('Border radius (px)'),
301 value: a.borderRadius,
302 min: 0, max: 20,
303 __nextHasNoMarginBottom: true,
304 onChange: function (v) { setAttributes({ borderRadius: v }); }
305 })
306 ),
307 el(PanelBody, { title: __('Typography', 'blockenberg'), initialOpen: false },
308 el(getTypographyControl(), { label: __('Title', 'blockenberg'), value: a.typoTitle, onChange: function (v) { setAttributes({ typoTitle: v }); } }),
309 el(getTypographyControl(), { label: __('Team Name', 'blockenberg'), value: a.typoTeam, onChange: function (v) { setAttributes({ typoTeam: v }); } }),
310 el(getTypographyControl(), { label: __('Round Label', 'blockenberg'), value: a.typoLabel, onChange: function (v) { setAttributes({ typoLabel: v }); } })
311 ),
312 el(PanelColorSettings, {
313 title: __('Colors'),
314 initialOpen: false,
315 colorSettings: [
316 { label: __('Background'), value: a.bgColor, onChange: function (v) { setAttributes({ bgColor: v || '#f8fafc' }); } },
317 { label: __('Accent'), value: a.accentColor, onChange: function (v) { setAttributes({ accentColor: v || '#f59e0b' }); } },
318 { label: __('Title'), value: a.titleColor, onChange: function (v) { setAttributes({ titleColor: v || '#0f172a' }); } },
319 { label: __('Match border'), value: a.matchBorderColor, onChange: function (v) { setAttributes({ matchBorderColor: v || '#cbd5e1' }); } },
320 { label: __('Match background'), value: a.matchBg, onChange: function (v) { setAttributes({ matchBg: v || '#ffffff' }); } },
321 { label: __('Team text'), value: a.teamColor, onChange: function (v) { setAttributes({ teamColor: v || '#334155' }); } },
322 { label: __('Winner background'), value: a.winnerBg, onChange: function (v) { setAttributes({ winnerBg: v || '#fef9c3' }); } },
323 { label: __('Winner text'), value: a.winnerColor, onChange: function (v) { setAttributes({ winnerColor: v || '#713f12' }); } },
324 { label: __('Winner border'), value: a.winnerBorderColor, onChange: function (v) { setAttributes({ winnerBorderColor: v || '#fbbf24' }); } },
325 { label: __('Loser text'), value: a.loserColor, onChange: function (v) { setAttributes({ loserColor: v || '#cbd5e1' }); } },
326 { label: __('Connector lines'), value: a.connectorColor, onChange: function (v) { setAttributes({ connectorColor: v || '#cbd5e1' }); } },
327 { label: __('Champion bg'), value: a.championBg, onChange: function (v) { setAttributes({ championBg: v || '#fffbeb' }); } },
328 { label: __('Champion border'), value: a.championBorderColor, onChange: function (v) { setAttributes({ championBorderColor: v || '#f59e0b' }); } },
329 { label: __('Champion text'), value: a.championColor, onChange: function (v) { setAttributes({ championColor: v || '#92400e' }); } }
330 ]
331 })
332 ),
333 el('div', useBlockProps({ className: 'bkbg-bk-editor-wrap', style: Object.assign({}, _tv(a.typoTitle, '--bkbg-bkt-title-'), _tv(a.typoTeam, '--bkbg-bkt-team-'), _tv(a.typoLabel, '--bkbg-bkt-label-')) }),
334 el(BracketPreview, { a: a, setAttributes: setAttributes })
335 )
336 );
337 },
338
339 save: function (props) {
340 return el('div', useBlockProps.save(),
341 el('div', {
342 className: 'bkbg-bk-app',
343 'data-opts': JSON.stringify(props.attributes)
344 })
345 );
346 }
347 });
348 }() );
349