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 / daily-schedule / index.js

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

363 lines 20.0 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 registerBlockType = wp.blocks.registerBlockType;
5 var __ = wp.i18n.__;
6 var InspectorControls = wp.blockEditor.InspectorControls;
7 var PanelColorSettings = wp.blockEditor.PanelColorSettings;
8 var useBlockProps = wp.blockEditor.useBlockProps;
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
16 var _dsTC, _dsTV;
17 function _tc() { return _dsTC || (_dsTC = window.bkbgTypographyControl); }
18 function _tv(typo, prefix) { return _dsTV ? _dsTV(typo, prefix) : ((_dsTV = window.bkbgTypoCssVars) ? _dsTV(typo, prefix) : {}); }
19
20 // ── helpers ────────────────────────────────────────────────────
21 function upd(arr, idx, field, val) {
22 return arr.map(function (e, i) {
23 if (i !== idx) return e;
24 var u = {}; u[field] = val;
25 return Object.assign({}, e, u);
26 });
27 }
28
29 var CATEGORY_OPTIONS = [
30 { value: 'work', label: 'Work' },
31 { value: 'break', label: 'Break' },
32 { value: 'personal', label: 'Personal' },
33 { value: 'health', label: 'Health / Exercise' },
34 { value: 'meal', label: 'Meal / Food' },
35 ];
36
37 function catColors(cat, a) {
38 if (cat === 'work') return { bg: a.workBg, color: a.workColor };
39 if (cat === 'break') return { bg: a.breakBg, color: a.breakColor };
40 if (cat === 'personal') return { bg: a.personalBg, color: a.personalColor };
41 if (cat === 'health') return { bg: a.healthBg, color: a.healthColor };
42 if (cat === 'meal') return { bg: a.mealBg, color: a.mealColor };
43 return { bg: a.workBg, color: a.workColor };
44 }
45
46 function catEmoji(cat) {
47 if (cat === 'work') return '💻';
48 if (cat === 'break') return '';
49 if (cat === 'personal') return '🎨';
50 if (cat === 'health') return '🏃';
51 if (cat === 'meal') return '🍽️';
52 return '📌';
53 }
54
55 function fmtTime(h, m, use12) {
56 if (!use12) {
57 return (h < 10 ? '0' : '') + h + ':' + (m === 0 ? '00' : (m < 10 ? '0' : '') + m);
58 }
59 var ampm = h >= 12 ? 'PM' : 'AM';
60 var hh = h % 12 || 12;
61 return hh + ':' + (m === 0 ? '00' : (m < 10 ? '0' : '') + m) + ' ' + ampm;
62 }
63
64 // ── render schedule preview ────────────────────────────────────
65 function renderSchedule(a) {
66 var hourStart = a.hourStart || 6;
67 var hourEnd = a.hourEnd || 22;
68 var hourHeight = a.hourHeight || 60;
69 var totalHours = Math.max(hourEnd - hourStart, 1);
70 var totalHeight = totalHours * hourHeight;
71 var timeColW = 68;
72 var fontSize = a.fontSize || 13;
73
74 // Hour grid lines + time labels
75 var hourRows = [];
76 for (var h = hourStart; h <= hourEnd; h++) {
77 var y = (h - hourStart) * hourHeight;
78 hourRows.push(
79 el('div', { key: h, style: { position: 'absolute', top: y + 'px', left: '0', right: '0', display: 'flex', alignItems: 'flex-start' } },
80 // Time label
81 el('div', {
82 style: {
83 width: timeColW + 'px', flexShrink: '0',
84 color: a.timeColor || '#475569',
85 fontSize: (fontSize - 1) + 'px',
86 paddingRight: '10px',
87 textAlign: 'right', lineHeight: '1',
88 transform: 'translateY(-7px)', userSelect: 'none'
89 }
90 }, fmtTime(h, 0, a.use12Hour)),
91 // Grid line
92 a.showGridLines ? el('div', {
93 style: {
94 flex: '1', height: '1px', background: a.gridLineColor || '#1e293b',
95 marginTop: '0'
96 }
97 }) : null
98 )
99 );
100 }
101
102 // Event blocks
103 var eventEls = (a.events || []).map(function (ev, idx) {
104 var top = ((ev.startHour - hourStart) + (ev.startMin / 60)) * hourHeight;
105 var h = (ev.duration / 60) * hourHeight;
106 var cc = catColors(ev.category, a);
107 var endH = ev.startHour + Math.floor((ev.startMin + ev.duration) / 60);
108 var endM = (ev.startMin + ev.duration) % 60;
109
110 var timeStr = a.showEventTime
111 ? fmtTime(ev.startHour, ev.startMin, a.use12Hour) + '' + fmtTime(endH, endM, a.use12Hour)
112 : null;
113
114 return el('div', {
115 key: idx,
116 style: {
117 position: 'absolute',
118 top: top + 'px',
119 left: (timeColW + 4) + 'px',
120 right: '0',
121 height: Math.max(h - 4, 20) + 'px',
122 background: cc.bg,
123 borderRadius: '6px',
124 padding: '4px 8px',
125 overflow: 'hidden',
126 boxSizing: 'border-box',
127 borderLeft: '3px solid ' + cc.color
128 }
129 },
130 el('div', { style: { display: 'flex', alignItems: 'center', gap: '5px' } },
131 el('span', { style: { fontSize: '13px' } }, catEmoji(ev.category)),
132 el('span', { style: { color: cc.color, fontSize: fontSize + 'px', fontWeight: '700', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' } }, ev.title)
133 ),
134 timeStr ? el('div', { style: { color: cc.color, fontSize: (a.timeFontSize || (fontSize - 2)) + 'px', opacity: '.8', marginTop: '1px' } }, timeStr) : null,
135 (a.showEventNote && ev.note && h > 48) ? el('div', { style: { color: cc.color, fontSize: (a.timeFontSize || (fontSize - 2)) + 'px', opacity: '.65', marginTop: '2px' } }, ev.note) : null
136 );
137 });
138
139 return el('div', {
140 style: {
141 position: 'relative',
142 height: totalHeight + 20 + 'px',
143 padding: '10px 12px 16px'
144 }
145 }, hourRows, eventEls);
146 }
147
148 // ── edit ──────────────────────────────────────────────────────
149 function Edit(props) {
150 var a = props.attributes;
151 var set = props.setAttributes;
152
153 var HOUR_OPTIONS = [];
154 for (var i = 0; i <= 23; i++) {
155 HOUR_OPTIONS.push({ value: i, label: fmtTime(i, 0, a.use12Hour) });
156 }
157
158 var colorSettings = [
159 { label: __('Background'), value: a.bgColor, onChange: function (v) { set({ bgColor: v || '#0f172a' }); } },
160 { label: __('Header BG'), value: a.headerBg, onChange: function (v) { set({ headerBg: v || '#1e293b' }); } },
161 { label: __('Border'), value: a.borderColor, onChange: function (v) { set({ borderColor: v || '#1e293b' }); } },
162 { label: __('Title'), value: a.titleColor, onChange: function (v) { set({ titleColor: v || '#f8fafc' }); } },
163 { label: __('Time labels'), value: a.timeColor, onChange: function (v) { set({ timeColor: v || '#475569' }); } },
164 { label: __('Grid lines'), value: a.gridLineColor, onChange: function (v) { set({ gridLineColor: v || '#1e293b' }); } },
165 { label: __('Work BG'), value: a.workBg, onChange: function (v) { set({ workBg: v || '#1e3a5f' }); } },
166 { label: __('Work text'), value: a.workColor, onChange: function (v) { set({ workColor: v || '#93c5fd' }); } },
167 { label: __('Break BG'), value: a.breakBg, onChange: function (v) { set({ breakBg: v || '#14532d' }); } },
168 { label: __('Break text'), value: a.breakColor, onChange: function (v) { set({ breakColor: v || '#86efac' }); } },
169 { label: __('Personal BG'), value: a.personalBg, onChange: function (v) { set({ personalBg: v || '#3b0764' }); } },
170 { label: __('Personal text'),value: a.personalColor, onChange: function (v) { set({ personalColor: v || '#c4b5fd' }); } },
171 { label: __('Health BG'), value: a.healthBg, onChange: function (v) { set({ healthBg: v || '#431407' }); } },
172 { label: __('Health text'), value: a.healthColor, onChange: function (v) { set({ healthColor: v || '#fdba74' }); } },
173 { label: __('Meal BG'), value: a.mealBg, onChange: function (v) { set({ mealBg: v || '#451a03' }); } },
174 { label: __('Meal text'), value: a.mealColor, onChange: function (v) { set({ mealColor: v || '#fde68a' }); } },
175 ];
176
177 function fmtTimeLocal(h, m) { return fmtTime(h, m, a.use12Hour); }
178
179 var inspector = el(InspectorControls, {},
180 el(PanelBody, { title: __('Schedule Settings'), initialOpen: true },
181 el(TextControl, {
182 label: __('Day title'), value: a.dayTitle, __nextHasNoMarginBottom: true,
183 onChange: function (v) { set({ dayTitle: v }); }
184 }),
185 el(ToggleControl, {
186 label: __('Show title'), checked: a.showTitle, __nextHasNoMarginBottom: true,
187 onChange: function (v) { set({ showTitle: v }); }
188 }),
189 el(SelectControl, {
190 label: __('Day starts at'), value: a.hourStart, options: HOUR_OPTIONS, __nextHasNoMarginBottom: true,
191 onChange: function (v) { set({ hourStart: parseInt(v, 10) }); }
192 }),
193 el(SelectControl, {
194 label: __('Day ends at'), value: a.hourEnd, options: HOUR_OPTIONS, __nextHasNoMarginBottom: true,
195 onChange: function (v) { set({ hourEnd: parseInt(v, 10) }); }
196 }),
197 el(ToggleControl, {
198 label: __('12-hour clock'), checked: a.use12Hour, __nextHasNoMarginBottom: true,
199 onChange: function (v) { set({ use12Hour: v }); }
200 }),
201 el(RangeControl, {
202 label: __('Hour height (px)'), value: a.hourHeight, min: 30, max: 120, __nextHasNoMarginBottom: true,
203 onChange: function (v) { set({ hourHeight: v }); }
204 }),
205 el(ToggleControl, {
206 label: __('Show grid lines'), checked: a.showGridLines, __nextHasNoMarginBottom: true,
207 onChange: function (v) { set({ showGridLines: v }); }
208 }),
209 el(ToggleControl, {
210 label: __('Show event times'), checked: a.showEventTime, __nextHasNoMarginBottom: true,
211 onChange: function (v) { set({ showEventTime: v }); }
212 }),
213 el(ToggleControl, {
214 label: __('Show event notes'), checked: a.showEventNote, __nextHasNoMarginBottom: true,
215 onChange: function (v) { set({ showEventNote: v }); }
216 })
217 ),
218 el(PanelBody, { title: __('Typography'), initialOpen: false },
219 _tc() && el(_tc(), { label: __('Title'), typo: a.typoTitle || {}, onChange: function(v) { set({ typoTitle: v }); } }),
220 el(RangeControl, {
221 label: __('Body size'), value: a.fontSize, min: 10, max: 20, __nextHasNoMarginBottom: true,
222 onChange: function (v) { set({ fontSize: v }); }
223 }),
224 el(RangeControl, {
225 label: __('Event time font size'), value: a.timeFontSize, min: 9, max: 16, __nextHasNoMarginBottom: true,
226 onChange: function (v) { set({ timeFontSize: v }); }
227 })
228 ),
229 el(PanelBody, { title: __('Style'), initialOpen: false },
230 el(RangeControl, {
231 label: __('Border radius'), value: a.borderRadius, min: 0, max: 30, __nextHasNoMarginBottom: true,
232 onChange: function (v) { set({ borderRadius: v }); }
233 })
234 ),
235 el(PanelBody, { title: __('Events (' + a.events.length + ')'), initialOpen: false },
236 a.events.map(function (ev, idx) {
237 return el(PanelBody, {
238 key: idx,
239 title: catEmoji(ev.category) + ' ' + ev.title.substring(0, 28),
240 initialOpen: false
241 },
242 el(TextControl, {
243 label: __('Title'), value: ev.title, __nextHasNoMarginBottom: true,
244 onChange: function (v) { set({ events: upd(a.events, idx, 'title', v) }); }
245 }),
246 el(SelectControl, {
247 label: __('Category'), value: ev.category, options: CATEGORY_OPTIONS, __nextHasNoMarginBottom: true,
248 onChange: function (v) { set({ events: upd(a.events, idx, 'category', v) }); }
249 }),
250 el(SelectControl, {
251 label: __('Start hour'), value: ev.startHour, options: HOUR_OPTIONS, __nextHasNoMarginBottom: true,
252 onChange: function (v) { set({ events: upd(a.events, idx, 'startHour', parseInt(v, 10)) }); }
253 }),
254 el(SelectControl, {
255 label: __('Start minute'),
256 value: ev.startMin,
257 options: [{ value: 0, label: ':00' }, { value: 15, label: ':15' }, { value: 30, label: ':30' }, { value: 45, label: ':45' }],
258 __nextHasNoMarginBottom: true,
259 onChange: function (v) { set({ events: upd(a.events, idx, 'startMin', parseInt(v, 10)) }); }
260 }),
261 el(RangeControl, {
262 label: __('Duration (min)'), value: ev.duration, min: 15, max: 480, step: 15, __nextHasNoMarginBottom: true,
263 onChange: function (v) { set({ events: upd(a.events, idx, 'duration', v) }); }
264 }),
265 el(TextControl, {
266 label: __('Note (optional)'), value: ev.note || '', __nextHasNoMarginBottom: true,
267 onChange: function (v) { set({ events: upd(a.events, idx, 'note', v) }); }
268 }),
269 el(Button, {
270 variant: 'secondary', isDestructive: true, __nextHasNoMarginBottom: true,
271 onClick: function () { var n = a.events.slice(); n.splice(idx, 1); set({ events: n }); }
272 }, __('Remove event'))
273 );
274 }),
275 el(Button, {
276 variant: 'primary', __nextHasNoMarginBottom: true,
277 onClick: function () {
278 set({ events: a.events.concat([{ title: 'New Event', startHour: 9, startMin: 0, duration: 60, category: 'work', note: '' }]) });
279 }
280 }, __('+ Add event'))
281 ),
282 el(PanelColorSettings, {
283 title: __('Colors'),
284 initialOpen: false,
285 colorSettings: colorSettings
286 })
287 );
288
289 // Header
290 var header = a.showTitle ? el('div', {
291 style: {
292 padding: '18px 20px 14px', background: a.headerBg,
293 borderBottom: '1px solid ' + a.borderColor
294 }
295 },
296 el('h2', { className: 'bkbg-ds-title', style: { color: a.titleColor, margin: '0' } }, a.dayTitle),
297 // Category legend
298 el('div', { style: { display: 'flex', gap: '12px', marginTop: '10px', flexWrap: 'wrap' } },
299 CATEGORY_OPTIONS.map(function (opt) {
300 var cc = catColors(opt.value, a);
301 return el('span', {
302 key: opt.value,
303 style: {
304 display: 'inline-flex', alignItems: 'center', gap: '5px',
305 background: cc.bg, color: cc.color,
306 borderRadius: '20px', padding: '3px 10px', fontSize: '11px', fontWeight: '600'
307 }
308 }, catEmoji(opt.value), opt.label);
309 })
310 )
311 ) : null;
312
313 return el(Fragment, {},
314 inspector,
315 el('div', Object.assign({}, useBlockProps(), {
316 style: Object.assign({
317 background: a.bgColor, border: '1px solid ' + a.borderColor,
318 borderRadius: a.borderRadius + 'px', overflow: 'hidden',
319 fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif'
320 },
321 { '--bkbg-ds-ttl-fs': (a.titleFontSize || 22) + 'px' },
322 _tv(a.typoTitle, '--bkbg-ds-ttl-'))
323 }), header, renderSchedule(a))
324 );
325 }
326
327 function fmtTime(h, m, use12) {
328 if (!use12) {
329 return (h < 10 ? '0' : '') + h + ':' + (m === 0 ? '00' : (m < 10 ? '0' : '') + m);
330 }
331 var ampm = h >= 12 ? 'PM' : 'AM';
332 var hh = h % 12 || 12;
333 return hh + ':' + (m === 0 ? '00' : (m < 10 ? '0' : '') + m) + ' ' + ampm;
334 }
335
336 function catColors(cat, a) {
337 if (cat === 'work') return { bg: a.workBg, color: a.workColor };
338 if (cat === 'break') return { bg: a.breakBg, color: a.breakColor };
339 if (cat === 'personal') return { bg: a.personalBg, color: a.personalColor };
340 if (cat === 'health') return { bg: a.healthBg, color: a.healthColor };
341 if (cat === 'meal') return { bg: a.mealBg, color: a.mealColor };
342 return { bg: a.workBg, color: a.workColor };
343 }
344
345 function catEmoji(cat) {
346 if (cat === 'work') return '💻';
347 if (cat === 'break') return '';
348 if (cat === 'personal') return '🎨';
349 if (cat === 'health') return '🏃';
350 if (cat === 'meal') return '🍽️';
351 return '📌';
352 }
353
354 registerBlockType('blockenberg/daily-schedule', {
355 edit: Edit,
356 save: function (props) {
357 return el('div', useBlockProps.save(),
358 el('div', { className: 'bkbg-daily-schedule-app', 'data-opts': JSON.stringify(props.attributes) })
359 );
360 }
361 });
362 })();
363