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

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

399 lines 20.4 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 useState = wp.element.useState;
5 var registerBlockType = wp.blocks.registerBlockType;
6 var InspectorControls = wp.blockEditor.InspectorControls;
7 var useBlockProps = wp.blockEditor.useBlockProps;
8 var PanelBody = wp.components.PanelBody;
9 var PanelColorSettings = wp.blockEditor.PanelColorSettings;
10 var ToggleControl = wp.components.ToggleControl;
11 var RangeControl = wp.components.RangeControl;
12 var SelectControl = wp.components.SelectControl;
13 var TextControl = wp.components.TextControl;
14 var Button = wp.components.Button;
15
16 var _tc, _tvf;
17 Object.defineProperty(window, '_tc', { get: function () { return _tc || (_tc = window.bkbgTypographyControl); } });
18 Object.defineProperty(window, '_tvf', { get: function () { return _tvf || (_tvf = window.bkbgTypoCssVars); } });
19 function getTypoControl(label, key, attrs, setA) { return _tc(label, key, attrs, setA); }
20 function getTypoCssVars(attrs) {
21 var v = {};
22 _tvf(v, 'titleTypo', attrs, '--bkwscd-tt-');
23 _tvf(v, 'eventTypo', attrs, '--bkwscd-ev-');
24 return v;
25 }
26
27 var DAY_NAMES = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'];
28 var EVENT_COLORS = ['#6366f1','#ef4444','#0ea5e9','#22c55e','#f59e0b','#8b5cf6','#ec4899','#14b8a6','#f97316','#64748b'];
29
30 function fmt(h, m, format) {
31 m = m || 0;
32 if (format === '24h') {
33 return ('0' + h).slice(-2) + ':' + ('0' + m).slice(-2);
34 }
35 var ampm = h >= 12 ? 'PM' : 'AM';
36 var h12 = h % 12 || 12;
37 return h12 + (m ? ':' + ('0' + m).slice(-2) : '') + ' ' + ampm;
38 }
39
40 function updateEvent(events, idx, field, val) {
41 return events.map(function (e, i) {
42 if (i !== idx) return e;
43 var upd = {}; upd[field] = val;
44 return Object.assign({}, e, upd);
45 });
46 }
47
48 function ScheduleGrid(props) {
49 var a = props.a;
50 var onEventClick = props.onEventClick;
51
52 var numDays = a.showWeekend ? 7 : 5;
53 var totalHours = a.endHour - a.startHour;
54 var gridHeight = totalHours * a.cellHeight;
55
56 var colWidth = 'calc((100% - 48px) / ' + numDays + ')';
57
58 // Time labels column
59 var timeLabels = [];
60 for (var h = a.startHour; h <= a.endHour; h++) {
61 var topPos = (h - a.startHour) * a.cellHeight;
62 timeLabels.push(el('div', {
63 key: h,
64 className: 'bkbg-wscd-time-label',
65 style: { top: topPos + 'px', color: a.timeColor }
66 }, a.showTime ? fmt(h, 0, a.timeFormat) : ''));
67 }
68
69 // Day columns
70 var dayCols = [];
71 for (var d = 0; d < numDays; d++) {
72 var dayD = d;
73 // Hour grid lines
74 var hourLines = [];
75 for (var dh = 0; dh < totalHours; dh++) {
76 hourLines.push(el('div', {
77 key: dh,
78 className: 'bkbg-wscd-hour-line',
79 style: {
80 top: (dh * a.cellHeight) + 'px',
81 borderTopColor: a.gridColor
82 }
83 }));
84 }
85
86 // Events for this day
87 var dayEvents = a.events.filter(function (ev) { return ev.day === dayD; });
88 var eventEls = dayEvents.map(function (ev, ei) {
89 var top = ((ev.startHour + (ev.startMinute || 0) / 60) - a.startHour) * a.cellHeight;
90 var height = (ev.duration || 1) * a.cellHeight - 4;
91 if (top < 0 || top >= gridHeight) return null;
92
93 return el('div', {
94 key: ei,
95 className: 'bkbg-wscd-event',
96 style: {
97 top: top + 'px',
98 height: height + 'px',
99 background: ev.color || '#6366f1',
100 borderRadius: a.eventRadius + 'px'
101 },
102 onClick: function () { if (onEventClick) onEventClick(ev); }
103 },
104 el('div', { className: 'bkbg-wscd-event-title' }, ev.title),
105 a.showInstructor && ev.instructor && el('div', { className: 'bkbg-wscd-event-sub' }, ev.instructor),
106 a.showLocation && ev.location && el('div', { className: 'bkbg-wscd-event-sub' }, '📍 ' + ev.location)
107 );
108 }).filter(Boolean);
109
110 dayCols.push(el('div', {
111 key: d,
112 className: 'bkbg-wscd-day-col',
113 style: {
114 height: gridHeight + 'px',
115 background: a.bgColor,
116 borderColor: a.borderColor
117 }
118 }, hourLines, eventEls));
119 }
120
121 // Day headers
122 var dayHeaders = [];
123 for (var dh2 = 0; dh2 < numDays; dh2++) {
124 dayHeaders.push(el('div', {
125 key: dh2,
126 className: 'bkbg-wscd-day-header',
127 style: { background: a.headerBg, color: a.headerColor, borderColor: a.borderColor }
128 }, a.dayLabels[dh2] || DAY_NAMES[dh2]));
129 }
130
131 return el('div', {
132 className: 'bkbg-wscd-wrap',
133 style: {
134 '--bkbg-wscd-cols': numDays,
135 '--bkbg-wscd-cell-h': a.cellHeight + 'px',
136 '--bkbg-wscd-radius': a.borderRadius + 'px',
137 '--bkbg-wscd-border': a.borderColor,
138 background: a.bgColor
139 }
140 },
141 a.showTitle && a.title && el('div', { className: 'bkbg-wscd-title', style: { color: a.headerColor } }, a.title),
142 el('div', { className: 'bkbg-wscd-grid' },
143 el('div', { className: 'bkbg-wscd-header-row' },
144 el('div', { className: 'bkbg-wscd-time-corner', style: { background: a.headerBg, borderColor: a.borderColor } }),
145 dayHeaders
146 ),
147 el('div', { className: 'bkbg-wscd-body-row' },
148 el('div', { className: 'bkbg-wscd-time-col', style: { background: a.timeBg, borderColor: a.borderColor, height: gridHeight + 'px' } }, timeLabels),
149 el('div', { className: 'bkbg-wscd-days-area', style: { height: gridHeight + 'px' } }, dayCols)
150 )
151 )
152 );
153 }
154
155 registerBlockType('blockenberg/week-schedule', {
156 title: __('Weekly Schedule', 'blockenberg'),
157 icon: 'calendar',
158 category: 'bkbg-blog',
159 description: __('Visual weekly schedule grid with colored time-slot events.', 'blockenberg'),
160
161 edit: function (props) {
162 var a = props.attributes;
163 var setAttributes = props.setAttributes;
164
165 var openState = useState(null);
166 var openIdx = openState[0]; var setOpenIdx = openState[1];
167
168 var hourOpts = [];
169 for (var h = 0; h <= 23; h++) {
170 hourOpts.push({ label: fmt(h, 0, '12h'), value: h });
171 }
172 var minOpts = [{ label: ':00', value: 0 }, { label: ':15', value: 15 }, { label: ':30', value: 30 }, { label: ':45', value: 45 }];
173 var durationOpts = [
174 { label: '30 min', value: 0.5 },
175 { label: '45 min', value: 0.75 },
176 { label: '1 hr', value: 1 },
177 { label: '1.5 hr', value: 1.5 },
178 { label: '2 hr', value: 2 },
179 { label: '2.5 hr', value: 2.5 },
180 { label: '3 hr', value: 3 }
181 ];
182 var dayOpts = DAY_NAMES.map(function (d, i) { return { label: d, value: i }; });
183 var timeFormatOpts = [{ label: '12-hour', value: '12h' }, { label: '24-hour', value: '24h' }];
184
185 function addEvent() {
186 var colorIdx = a.events.length % EVENT_COLORS.length;
187 setAttributes({ events: a.events.concat([{
188 day: 0, startHour: 9, startMinute: 0, duration: 1,
189 title: 'New Class', instructor: '', location: '', color: EVENT_COLORS[colorIdx]
190 }])});
191 }
192 function removeEvent(idx) {
193 setAttributes({ events: a.events.filter(function (_, i) { return i !== idx; }) });
194 }
195
196 var inspector = el(InspectorControls, {},
197 el(PanelBody, { title: __('Schedule Settings', 'blockenberg'), initialOpen: true },
198 el(TextControl, {
199 label: __('Title', 'blockenberg'),
200 value: a.title,
201 __nextHasNoMarginBottom: true,
202 onChange: function (v) { setAttributes({ title: v }); }
203 }),
204 el(ToggleControl, {
205 label: __('Show Title', 'blockenberg'),
206 checked: a.showTitle,
207 __nextHasNoMarginBottom: true,
208 onChange: function (v) { setAttributes({ showTitle: v }); }
209 }),
210 el(SelectControl, {
211 label: __('Start Hour', 'blockenberg'),
212 value: a.startHour,
213 options: hourOpts,
214 __nextHasNoMarginBottom: true,
215 onChange: function (v) { setAttributes({ startHour: parseInt(v, 10) }); }
216 }),
217 el(SelectControl, {
218 label: __('End Hour', 'blockenberg'),
219 value: a.endHour,
220 options: hourOpts,
221 __nextHasNoMarginBottom: true,
222 onChange: function (v) { setAttributes({ endHour: parseInt(v, 10) }); }
223 }),
224 el(RangeControl, {
225 label: __('Cell Height (px/hr)', 'blockenberg'),
226 value: a.cellHeight,
227 min: 40, max: 120,
228 __nextHasNoMarginBottom: true,
229 onChange: function (v) { setAttributes({ cellHeight: v }); }
230 }),
231 el(SelectControl, {
232 label: __('Time Format', 'blockenberg'),
233 value: a.timeFormat,
234 options: timeFormatOpts,
235 __nextHasNoMarginBottom: true,
236 onChange: function (v) { setAttributes({ timeFormat: v }); }
237 }),
238 el(ToggleControl, {
239 label: __('Show Weekend (Sat/Sun)', 'blockenberg'),
240 checked: a.showWeekend,
241 __nextHasNoMarginBottom: true,
242 onChange: function (v) { setAttributes({ showWeekend: v }); }
243 }),
244 el(ToggleControl, {
245 label: __('Show Instructor', 'blockenberg'),
246 checked: a.showInstructor,
247 __nextHasNoMarginBottom: true,
248 onChange: function (v) { setAttributes({ showInstructor: v }); }
249 }),
250 el(ToggleControl, {
251 label: __('Show Location', 'blockenberg'),
252 checked: a.showLocation,
253 __nextHasNoMarginBottom: true,
254 onChange: function (v) { setAttributes({ showLocation: v }); }
255 }),
256 el(RangeControl, {
257 label: __('Border Radius', 'blockenberg'),
258 value: a.borderRadius,
259 min: 0, max: 20,
260 __nextHasNoMarginBottom: true,
261 onChange: function (v) { setAttributes({ borderRadius: v }); }
262 }),
263 el(RangeControl, {
264 label: __('Event Radius', 'blockenberg'),
265 value: a.eventRadius,
266 min: 0, max: 12,
267 __nextHasNoMarginBottom: true,
268 onChange: function (v) { setAttributes({ eventRadius: v }); }
269 })
270 ),
271 el(PanelBody, { title: __('Events (' + a.events.length + ')', 'blockenberg'), initialOpen: false },
272 a.events.map(function (ev, i) {
273 var isOpen = openIdx === i;
274 return el('div', { key: i, className: 'bkbg-wscd-event-editor' },
275 el('div', {
276 className: 'bkbg-wscd-event-head',
277 style: { borderLeft: '4px solid ' + (ev.color || '#6366f1') },
278 onClick: function () { setOpenIdx(isOpen ? null : i); }
279 },
280 el('span', {}, ev.title || 'Event ' + (i + 1)),
281 el('span', { className: 'bkbg-wscd-ev-day' }, DAY_NAMES[ev.day] || ''),
282 el('div', { style: { marginLeft: 'auto', display: 'flex', gap: '4px' } },
283 el('span', {}, isOpen ? '' : ''),
284 el(Button, { isSmall: true, isDestructive: true, onClick: function (e) { e.stopPropagation(); removeEvent(i); } }, '')
285 )
286 ),
287 isOpen && el('div', { className: 'bkbg-wscd-event-fields' },
288 el(TextControl, {
289 label: __('Title', 'blockenberg'),
290 value: ev.title,
291 __nextHasNoMarginBottom: true,
292 onChange: function (v) { setAttributes({ events: updateEvent(a.events, i, 'title', v) }); }
293 }),
294 el(SelectControl, {
295 label: __('Day', 'blockenberg'),
296 value: ev.day,
297 options: dayOpts,
298 __nextHasNoMarginBottom: true,
299 onChange: function (v) { setAttributes({ events: updateEvent(a.events, i, 'day', parseInt(v, 10)) }); }
300 }),
301 el(SelectControl, {
302 label: __('Start Hour', 'blockenberg'),
303 value: ev.startHour,
304 options: hourOpts,
305 __nextHasNoMarginBottom: true,
306 onChange: function (v) { setAttributes({ events: updateEvent(a.events, i, 'startHour', parseInt(v, 10)) }); }
307 }),
308 el(SelectControl, {
309 label: __('Start Minute', 'blockenberg'),
310 value: ev.startMinute || 0,
311 options: minOpts,
312 __nextHasNoMarginBottom: true,
313 onChange: function (v) { setAttributes({ events: updateEvent(a.events, i, 'startMinute', parseInt(v, 10)) }); }
314 }),
315 el(SelectControl, {
316 label: __('Duration', 'blockenberg'),
317 value: ev.duration,
318 options: durationOpts,
319 __nextHasNoMarginBottom: true,
320 onChange: function (v) { setAttributes({ events: updateEvent(a.events, i, 'duration', parseFloat(v)) }); }
321 }),
322 el(TextControl, {
323 label: __('Instructor', 'blockenberg'),
324 value: ev.instructor,
325 __nextHasNoMarginBottom: true,
326 onChange: function (v) { setAttributes({ events: updateEvent(a.events, i, 'instructor', v) }); }
327 }),
328 el(TextControl, {
329 label: __('Location', 'blockenberg'),
330 value: ev.location,
331 __nextHasNoMarginBottom: true,
332 onChange: function (v) { setAttributes({ events: updateEvent(a.events, i, 'location', v) }); }
333 }),
334 el('div', { style: { marginBottom: '8px' } },
335 el('label', { style: { display: 'block', fontSize: '11px', fontWeight: '600', marginBottom: '4px', textTransform: 'uppercase', color: '#1e1e1e' } }, __('Color', 'blockenberg')),
336 el('div', { style: { display: 'flex', flexWrap: 'wrap', gap: '4px' } },
337 EVENT_COLORS.map(function (c) {
338 return el('button', {
339 key: c,
340 style: {
341 width: '20px', height: '20px',
342 background: c,
343 border: ev.color === c ? '2px solid #000' : '2px solid transparent',
344 borderRadius: '4px',
345 cursor: 'pointer', padding: 0
346 },
347 onClick: function () { setAttributes({ events: updateEvent(a.events, i, 'color', c) }); }
348 });
349 })
350 )
351 )
352 )
353 );
354 }),
355 el(Button, {
356 variant: 'secondary',
357 style: { marginTop: '8px', width: '100%' },
358 onClick: addEvent
359 }, __('+ Add Event', 'blockenberg'))
360 ),
361
362 el( PanelBody, { title: __( 'Typography', 'blockenberg' ), initialOpen: false },
363 getTypoControl(__('Title', 'blockenberg'), 'titleTypo', a, setAttributes),
364 getTypoControl(__('Event', 'blockenberg'), 'eventTypo', a, setAttributes)
365 ),
366 el(PanelColorSettings, {
367 title: __('Colors', 'blockenberg'),
368 initialOpen: false,
369 colorSettings: [
370 { value: a.bgColor, onChange: function (c) { setAttributes({ bgColor: c || '#ffffff' }); }, label: __('Background', 'blockenberg') },
371 { value: a.headerBg, onChange: function (c) { setAttributes({ headerBg: c || '#f8fafc' }); }, label: __('Header Background', 'blockenberg') },
372 { value: a.headerColor, onChange: function (c) { setAttributes({ headerColor: c || '#1e293b' }); }, label: __('Header Text', 'blockenberg') },
373 { value: a.gridColor, onChange: function (c) { setAttributes({ gridColor: c || '#f1f5f9' }); }, label: __('Grid Lines', 'blockenberg') },
374 { value: a.borderColor, onChange: function (c) { setAttributes({ borderColor: c || '#e2e8f0' }); }, label: __('Border', 'blockenberg') },
375 { value: a.timeBg, onChange: function (c) { setAttributes({ timeBg: c || '#f8fafc' }); }, label: __('Time Column Background', 'blockenberg') },
376 { value: a.timeColor, onChange: function (c) { setAttributes({ timeColor: c || '#64748b' }); }, label: __('Time Text', 'blockenberg') }
377 ]
378 })
379 );
380
381 var blockProps = useBlockProps((function () {
382 var s = getTypoCssVars(a);
383 return { className: 'bkbg-editor-wrap', 'data-block-label': 'Weekly Schedule', style: s };
384 })());
385
386 return el('div', blockProps,
387 inspector,
388 el(ScheduleGrid, { a: a })
389 );
390 },
391
392 save: function (props) {
393 return el('div', useBlockProps.save({ style: getTypoCssVars(props.attributes) }),
394 el('div', { className: 'bkbg-wscd-app', 'data-opts': JSON.stringify(props.attributes) })
395 );
396 }
397 });
398 }() );
399