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 / horizontal-timeline / index.js

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

261 lines 17.5 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 __ = wp.i18n.__;
5 var registerBlockType = wp.blocks.registerBlockType;
6 var InspectorControls = wp.blockEditor.InspectorControls;
7 var useBlockProps = wp.blockEditor.useBlockProps;
8 var PanelColorSettings = wp.blockEditor.PanelColorSettings;
9 var PanelBody = wp.components.PanelBody;
10 var TextControl = wp.components.TextControl;
11 var TextareaControl = wp.components.TextareaControl;
12 var ToggleControl = wp.components.ToggleControl;
13 var RangeControl = wp.components.RangeControl;
14 var SelectControl = wp.components.SelectControl;
15 var Button = wp.components.Button;
16
17 var _TypographyControl, _typoCssVars;
18 function getTypographyControl() { return _TypographyControl || (_TypographyControl = window.bkbgTypographyControl); }
19 function getTypoCssVars() { return _typoCssVars || (_typoCssVars = window.bkbgTypoCssVars); }
20 var IP = function () { return window.bkbgIconPicker; };
21
22 var LAYOUTS = [
23 { label: 'Alternating (above & below)', value: 'alternating' },
24 { label: 'All above the line', value: 'top' },
25 { label: 'All below the line', value: 'bottom' },
26 ];
27
28 function cloneItems(items) { return items.map(function (i) { return Object.assign({}, i); }); }
29
30 function ItemEditor(props) {
31 var items = props.items;
32 var set = props.set;
33
34 function updateItem(idx, field, val) {
35 var next = cloneItems(items);
36 next[idx][field] = val;
37 set(next);
38 }
39 function removeItem(idx) {
40 var next = cloneItems(items);
41 next.splice(idx, 1);
42 set(next);
43 }
44 function addItem() {
45 set(cloneItems(items).concat([{ date: '2025', title: 'New Milestone', description: 'Describe this milestone…', icon: '', iconType: 'custom-char', iconDashicon: '', iconImageUrl: '', imageUrl: '' }]));
46 }
47 function moveUp(idx) {
48 if (idx === 0) return;
49 var next = cloneItems(items);
50 var tmp = next[idx - 1]; next[idx - 1] = next[idx]; next[idx] = tmp;
51 set(next);
52 }
53 function moveDown(idx) {
54 if (idx === items.length - 1) return;
55 var next = cloneItems(items);
56 var tmp = next[idx + 1]; next[idx + 1] = next[idx]; next[idx] = tmp;
57 set(next);
58 }
59
60 return el('div', null,
61 items.map(function (item, idx) {
62 return el(PanelBody, { key: idx, title: (item.icon || '') + ' ' + (item.title || 'Item ' + (idx + 1)), initialOpen: false },
63 el(TextControl, { __nextHasNoMarginBottom: true, label: 'Date / Label', value: item.date, onChange: function (v) { updateItem(idx, 'date', v); } }),
64 IP() ? el(IP().IconPickerControl, {
65 iconType: item.iconType || 'custom-char',
66 customChar: item.icon || '',
67 dashicon: item.iconDashicon || '',
68 imageUrl: item.iconImageUrl || '',
69 onChangeType: function (v) { updateItem(idx, 'iconType', v); },
70 onChangeChar: function (v) { updateItem(idx, 'icon', v); },
71 onChangeDashicon: function (v) { updateItem(idx, 'iconDashicon', v); },
72 onChangeImageUrl: function (v) { updateItem(idx, 'iconImageUrl', v); },
73 label: 'Icon'
74 }) : el(TextControl, { __nextHasNoMarginBottom: true, label: 'Icon (emoji)', value: item.icon, onChange: function (v) { updateItem(idx, 'icon', v); } }),
75 el(TextControl, { __nextHasNoMarginBottom: true, label: 'Title', value: item.title, onChange: function (v) { updateItem(idx, 'title', v); } }),
76 el(TextareaControl, { __nextHasNoMarginBottom: true, label: 'Description', value: item.description, onChange: function (v) { updateItem(idx, 'description', v); }, rows: 3 }),
77 el(TextControl, { __nextHasNoMarginBottom: true, label: 'Image URL (optional)', value: item.imageUrl || '', onChange: function (v) { updateItem(idx, 'imageUrl', v); } }),
78 el('div', { style: { display: 'flex', gap: '8px', marginTop: '8px' } },
79 el(Button, { isSmall: true, variant: 'secondary', onClick: function () { moveUp(idx); }, disabled: idx === 0 }, '↑ Up'),
80 el(Button, { isSmall: true, variant: 'secondary', onClick: function () { moveDown(idx); }, disabled: idx === items.length - 1 }, '↓ Down'),
81 el(Button, { isSmall: true, isDestructive: true, onClick: function () { removeItem(idx); } }, '✕ Remove'),
82 )
83 );
84 }),
85 el(Button, { variant: 'secondary', onClick: addItem, style: { marginTop: '12px', width: '100%' } }, '+ Add Milestone')
86 );
87 }
88
89 /* Editor preview */
90 function TimelinePreview(props) {
91 var a = props.attributes;
92 var trackPadding = 48;
93 var halfH = Math.round(a.containerHeight / 2);
94 var _tv = getTypoCssVars();
95
96 var wrapStyle = {
97 background: a.sectionBg,
98 padding: '20px 0',
99 position: 'relative',
100 overflow: 'hidden',
101 };
102 Object.assign(wrapStyle, _tv(a.titleTypo || {}, '--bkbg-ht-tt-'));
103 Object.assign(wrapStyle, _tv(a.descTypo || {}, '--bkbg-ht-desc-'));
104 Object.assign(wrapStyle, _tv(a.dateTypo || {}, '--bkbg-ht-date-'));
105 if (a.titleFontSize && a.titleFontSize !== 13) wrapStyle['--bkbg-ht-tt-sz'] = a.titleFontSize + 'px';
106 if (a.titleFontWeight && a.titleFontWeight !== '600') wrapStyle['--bkbg-ht-tt-fw'] = a.titleFontWeight;
107 if (a.titleLineHeight && a.titleLineHeight !== 1.3) wrapStyle['--bkbg-ht-tt-lh'] = String(a.titleLineHeight);
108 if (a.descFontSize && a.descFontSize !== 11) wrapStyle['--bkbg-ht-desc-sz'] = a.descFontSize + 'px';
109 if (a.descLineHeight && a.descLineHeight !== 1.5) wrapStyle['--bkbg-ht-desc-lh'] = String(a.descLineHeight);
110 if (a.dateFontSize && a.dateFontSize !== 11) wrapStyle['--bkbg-ht-date-sz'] = a.dateFontSize + 'px';
111 var trackStyle = {
112 display: 'flex',
113 alignItems: 'center',
114 gap: a.itemGap + 'px',
115 padding: '0 ' + trackPadding + 'px',
116 height: a.containerHeight + 'px',
117 position: 'relative',
118 overflowX: 'auto',
119 overflowY: 'visible',
120 };
121 var lineStyle = {
122 position: 'absolute',
123 left: 0,
124 right: 0,
125 top: halfH + 'px',
126 height: a.lineThickness + 'px',
127 background: a.lineColor,
128 pointerEvents: 'none',
129 };
130
131 return el('div', { style: wrapStyle },
132 el('div', { style: trackStyle },
133 el('div', { style: lineStyle }),
134 a.items.map(function (item, idx) {
135 var isTop = a.layout === 'top' || (a.layout === 'alternating' && idx % 2 === 0);
136 var cardStyle = {
137 background: a.cardBg,
138 border: '1px solid ' + a.cardBorderColor,
139 borderRadius: a.cardRadius + 'px',
140 padding: a.cardPadding + 'px',
141 width: a.itemWidth + 'px',
142 flexShrink: 0,
143 boxShadow: a.cardShadow ? '0 4px 16px rgba(0,0,0,0.08)' : 'none',
144 position: 'relative',
145 alignSelf: isTop ? 'flex-start' : 'flex-end',
146 marginTop: isTop ? '0' : 'auto',
147 };
148 return el('div', { key: idx, style: { display: 'flex', flexDirection: 'column', alignItems: 'center', flexShrink: 0, position: 'relative', height: '100%' } },
149 /* card top */
150 isTop && el('div', { style: cardStyle },
151 a.showIcons && el('div', { style: { fontSize: a.iconSize + 'px', marginBottom: '8px' } }, (item.iconType || 'custom-char') !== 'custom-char' && IP() ? IP().buildEditorIcon(el, item.iconType, item.icon, item.iconDashicon, item.iconImageUrl, item.iconDashiconColor) : item.icon),
152 a.showDates && el('div', { className: 'bkbg-ht-card-date', style: { color: a.dateColor, marginBottom: '4px' } }, item.date),
153 el('div', { className: 'bkbg-ht-card-title', style: { color: a.titleColor, marginBottom: '4px' } }, item.title),
154 el('div', { className: 'bkbg-ht-card-desc', style: { color: a.descColor } }, item.description),
155 ),
156 /* dot */
157 a.showConnectorDot && el('div', { style: {
158 width: a.dotSize + 'px',
159 height: a.dotSize + 'px',
160 borderRadius: '50%',
161 background: a.dotColor,
162 border: '3px solid ' + a.dotBorderColor,
163 boxShadow: '0 0 0 3px ' + a.dotColor + '33',
164 flexShrink: 0,
165 position: 'absolute',
166 top: halfH - Math.round(a.dotSize / 2) + 'px',
167 left: '50%',
168 transform: 'translateX(-50%)',
169 zIndex: 2,
170 }}),
171 /* card bottom */
172 !isTop && el('div', { style: Object.assign({}, cardStyle, { alignSelf: 'flex-end' }) },
173 a.showIcons && el('div', { style: { fontSize: a.iconSize + 'px', marginBottom: '8px' } }, (item.iconType || 'custom-char') !== 'custom-char' && IP() ? IP().buildEditorIcon(el, item.iconType, item.icon, item.iconDashicon, item.iconImageUrl, item.iconDashiconColor) : item.icon),
174 a.showDates && el('div', { className: 'bkbg-ht-card-date', style: { color: a.dateColor, marginBottom: '4px' } }, item.date),
175 el('div', { className: 'bkbg-ht-card-title', style: { color: a.titleColor, marginBottom: '4px' } }, item.title),
176 el('div', { className: 'bkbg-ht-card-desc', style: { color: a.descColor } }, item.description),
177 )
178 );
179 })
180 )
181 );
182 }
183
184 registerBlockType('blockenberg/horizontal-timeline', {
185 edit: function (props) {
186 var a = props.attributes;
187 var set = props.setAttributes;
188
189 return el(Fragment, null,
190 el(InspectorControls, null,
191 /* Items */
192 el(PanelBody, { title: 'Timeline Items', initialOpen: true },
193 el(ItemEditor, { items: a.items, set: function (v) { set({ items: v }); } })
194 ),
195 /* Layout */
196 el(PanelBody, { title: 'Layout', initialOpen: false },
197 el(SelectControl, { __nextHasNoMarginBottom: true, label: 'Item placement', value: a.layout, options: LAYOUTS, onChange: function (v) { set({ layout: v }); } }),
198 el(RangeControl, { __nextHasNoMarginBottom: true, label: 'Container height (px)', value: a.containerHeight, min: 260, max: 700, step: 10, onChange: function (v) { set({ containerHeight: v }); } }),
199 el(RangeControl, { __nextHasNoMarginBottom: true, label: 'Card width (px)', value: a.itemWidth, min: 140, max: 400, step: 10, onChange: function (v) { set({ itemWidth: v }); } }),
200 el(RangeControl, { __nextHasNoMarginBottom: true, label: 'Gap between items (px)', value: a.itemGap, min: 8, max: 120, step: 4, onChange: function (v) { set({ itemGap: v }); } }),
201 ),
202 /* Card */
203 el(PanelBody, { title: 'Card Style', initialOpen: false },
204 el(RangeControl, { __nextHasNoMarginBottom: true, label: 'Card radius (px)', value: a.cardRadius, min: 0, max: 32, onChange: function (v) { set({ cardRadius: v }); } }),
205 el(RangeControl, { __nextHasNoMarginBottom: true, label: 'Card padding (px)', value: a.cardPadding, min: 8, max: 48, onChange: function (v) { set({ cardPadding: v }); } }),
206 el(ToggleControl, { __nextHasNoMarginBottom: true, label: 'Card shadow', checked: a.cardShadow, onChange: function (v) { set({ cardShadow: v }); } }),
207 ),
208 /* Line & Dots */
209 el(PanelBody, { title: 'Line & Dots', initialOpen: false },
210 el(RangeControl, { __nextHasNoMarginBottom: true, label: 'Line thickness (px)', value: a.lineThickness, min: 1, max: 12, onChange: function (v) { set({ lineThickness: v }); } }),
211 el(ToggleControl, { __nextHasNoMarginBottom: true, label: 'Show connector dots', checked: a.showConnectorDot, onChange: function (v) { set({ showConnectorDot: v }); } }),
212 a.showConnectorDot && el(RangeControl, { __nextHasNoMarginBottom: true, label: 'Dot size (px)', value: a.dotSize, min: 8, max: 32, onChange: function (v) { set({ dotSize: v }); } }),
213 ),
214 /* Display */
215 el(PanelBody, { title: 'Display Options', initialOpen: false },
216 el(ToggleControl, { __nextHasNoMarginBottom: true, label: 'Show icons', checked: a.showIcons, onChange: function (v) { set({ showIcons: v }); } }),
217 a.showIcons && el(RangeControl, { __nextHasNoMarginBottom: true, label: 'Icon size (px)', value: a.iconSize, min: 16, max: 56, onChange: function (v) { set({ iconSize: v }); } }),
218 el(ToggleControl, { __nextHasNoMarginBottom: true, label: 'Show date labels', checked: a.showDates, onChange: function (v) { set({ showDates: v }); } }),
219 el(ToggleControl, { __nextHasNoMarginBottom: true, label: 'Enable drag to scroll', checked: a.draggable, onChange: function (v) { set({ draggable: v }); } }),
220 el(ToggleControl, { __nextHasNoMarginBottom: true, label: 'Show navigation arrows', checked: a.showArrows, onChange: function (v) { set({ showArrows: v }); } }),
221 el(ToggleControl, { __nextHasNoMarginBottom: true, label: 'Animate items on scroll-into-view', checked: a.animateOnScroll, onChange: function (v) { set({ animateOnScroll: v }); } }),
222 ),
223 /* Typography */
224 el(PanelBody, { title: 'Typography', initialOpen: false },
225 el(getTypographyControl(), { label: __('Title Typography', 'blockenberg'), value: a.titleTypo || {}, onChange: function (v) { set({ titleTypo: v }); } }),
226 el(getTypographyControl(), { label: __('Description Typography', 'blockenberg'), value: a.descTypo || {}, onChange: function (v) { set({ descTypo: v }); } }),
227 el(getTypographyControl(), { label: __('Date Typography', 'blockenberg'), value: a.dateTypo || {}, onChange: function (v) { set({ dateTypo: v }); } })
228 ),
229 /* Colors */
230 el(PanelColorSettings, {
231 title: 'Colors',
232 initialOpen: false,
233 colorSettings: [
234 { value: a.sectionBg, onChange: function (v) { set({ sectionBg: v || '#f8fafc' }); }, label: 'Section background' },
235 { value: a.lineColor, onChange: function (v) { set({ lineColor: v || '#6366f1' }); }, label: 'Timeline line' },
236 { value: a.dotColor, onChange: function (v) { set({ dotColor: v || '#6366f1' }); }, label: 'Dot fill' },
237 { value: a.dotBorderColor, onChange: function (v) { set({ dotBorderColor: v || '#ffffff' }); }, label: 'Dot border' },
238 { value: a.cardBg, onChange: function (v) { set({ cardBg: v || '#ffffff' }); }, label: 'Card background' },
239 { value: a.cardBorderColor, onChange: function (v) { set({ cardBorderColor: v || '#e2e8f0' }); }, label: 'Card border' },
240 { value: a.dateColor, onChange: function (v) { set({ dateColor: v || '#6366f1' }); }, label: 'Date text' },
241 { value: a.titleColor, onChange: function (v) { set({ titleColor: v || '#1e293b' }); }, label: 'Title text' },
242 { value: a.descColor, onChange: function (v) { set({ descColor: v || '#64748b' }); }, label: 'Description text' },
243 { value: a.arrowBg, onChange: function (v) { set({ arrowBg: v || '#6366f1' }); }, label: 'Arrow button background' },
244 { value: a.arrowColor, onChange: function (v) { set({ arrowColor: v || '#ffffff' }); }, label: 'Arrow button icon' },
245 ],
246 }),
247 ),
248 el('div', useBlockProps(),
249 el(TimelinePreview, { attributes: a })
250 )
251 );
252 },
253 save: function (props) {
254 var a = props.attributes;
255 return el('div', useBlockProps.save(),
256 el('div', { className: 'bkbg-ht-app', 'data-opts': JSON.stringify(a) })
257 );
258 },
259 });
260 }() );
261