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 / sticky-two-col / index.js

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

272 lines 17.6 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 MediaUpload = wp.blockEditor.MediaUpload;
8 var MediaUploadCheck = wp.blockEditor.MediaUploadCheck;
9 var useBlockProps = wp.blockEditor.useBlockProps;
10 var RichText = wp.blockEditor.RichText;
11 var PanelColorSettings = wp.blockEditor.PanelColorSettings;
12 var PanelBody = wp.components.PanelBody;
13 var ToggleControl = wp.components.ToggleControl;
14 var RangeControl = wp.components.RangeControl;
15 var SelectControl = wp.components.SelectControl;
16 var TextControl = wp.components.TextControl;
17 var Button = wp.components.Button;
18 var ColorPicker = wp.components.ColorPicker;
19 var Popover = wp.components.Popover;
20 var Fragment = wp.element.Fragment;
21
22 var _tc, _tv;
23 function getTypoControl() { return _tc || (_tc = window.bkbgTypographyControl); }
24 function getTypoCssVars() { return _tv || (_tv = window.bkbgTypoCssVars); }
25 var IP = function () { return window.bkbgIconPicker; };
26
27 var ratioMap = { '50-50': [50, 50], '45-55': [45, 55], '55-45': [55, 45], '40-60': [40, 60], '60-40': [60, 40] };
28
29 function BkbgColorSwatch(props) {
30 var st = useState(false); var isOpen = st[0]; var setIsOpen = st[1];
31 return el(Fragment, null,
32 el('div', { style: { display: 'flex', alignItems: 'center', gap: '8px', marginBottom: '6px' } },
33 el('span', { style: { fontSize: '11px', fontWeight: 500, textTransform: 'uppercase' } }, props.label),
34 el('button', { type: 'button', style: { width: 28, height: 28, borderRadius: 4, border: '1px solid #ccc', background: props.value || '#fff', cursor: 'pointer', padding: 0 }, onClick: function () { setIsOpen(!isOpen); } })
35 ),
36 isOpen && el(Popover, { onClose: function () { setIsOpen(false); } }, el('div', { style: { padding: '8px' } }, el(ColorPicker, { color: props.value, enableAlpha: true, onChange: props.onChange })))
37 );
38 }
39
40 registerBlockType('blockenberg/sticky-two-col', {
41 edit: function (props) {
42 var attr = props.attributes;
43 var set = props.setAttributes;
44 var items = attr.items || [];
45
46 var previewState = useState(0);
47 var previewIdx = previewState[0];
48 var setPreviewIdx = previewState[1];
49 var previewItem = items[previewIdx] || {};
50
51 function updateItem(idx, field, val) {
52 var updated = items.map(function (it, i) {
53 if (i !== idx) return it;
54 var p = {}; p[field] = val;
55 return Object.assign({}, it, p);
56 });
57 set({ items: updated });
58 }
59
60 function addItem() {
61 var accents = ['#6366f1', '#ec4899', '#16a34a', '#f59e0b', '#0ea5e9', '#ef4444'];
62 var icons = ['💡', '', '🚀', '🎯', '🔥', ''];
63 var n = items.length % accents.length;
64 set({ items: items.concat([{ eyebrow: 'Step ' + (items.length + 1), title: 'New Feature', text: 'Describe this feature here.', icon: icons[n], iconType: 'custom-char', iconDashicon: '', iconImageUrl: '', imageUrl: '', imageId: 0, imageAlt: '', accentColor: accents[n] }]) });
65 }
66
67 function removeItem(idx) {
68 set({ items: items.filter(function (_, i) { return i !== idx; }) });
69 if (previewIdx >= items.length - 1) setPreviewIdx(Math.max(0, previewIdx - 1));
70 }
71
72 var cols = ratioMap[attr.splitRatio] || [50, 50];
73 var isStickRight = attr.stickyPosition === 'right';
74
75 /* Inspector */
76 var inspector = el(InspectorControls, {},
77 el(PanelBody, { title: __('Layout', 'blockenberg'), initialOpen: true },
78 el(SelectControl, {
79 label: __('Sticky Column', 'blockenberg'),
80 value: attr.stickyPosition,
81 options: [
82 { label: 'Right column sticky', value: 'right' },
83 { label: 'Left column sticky', value: 'left' }
84 ],
85 onChange: function (v) { set({ stickyPosition: v }); },
86 __nextHasNoMarginBottom: true
87 }),
88 el(SelectControl, {
89 label: __('Column Ratio', 'blockenberg'),
90 value: attr.splitRatio,
91 options: [
92 { label: '50 / 50', value: '50-50' },
93 { label: '45 / 55', value: '45-55' },
94 { label: '55 / 45', value: '55-45' },
95 { label: '40 / 60', value: '40-60' },
96 { label: '60 / 40', value: '60-40' }
97 ],
98 onChange: function (v) { set({ splitRatio: v }); },
99 __nextHasNoMarginBottom: true
100 }),
101 el(RangeControl, {
102 label: __('Sticky Offset from Top (px)', 'blockenberg'),
103 value: attr.stickyOffset, min: 0, max: 200,
104 onChange: function (v) { set({ stickyOffset: v }); },
105 __nextHasNoMarginBottom: true
106 }),
107 el(RangeControl, {
108 label: __('Image Border Radius (px)', 'blockenberg'),
109 value: attr.imageRadius, min: 0, max: 40,
110 onChange: function (v) { set({ imageRadius: v }); },
111 __nextHasNoMarginBottom: true
112 }),
113 el(RangeControl, {
114 label: __('Icon Size (px)', 'blockenberg'),
115 value: attr.iconSize, min: 20, max: 64,
116 onChange: function (v) { set({ iconSize: v }); },
117 __nextHasNoMarginBottom: true
118 }),
119 el(RangeControl, {
120 label: __('Gap Between Items (px)', 'blockenberg'),
121 value: attr.gap, min: 24, max: 200,
122 onChange: function (v) { set({ gap: v }); },
123 __nextHasNoMarginBottom: true
124 }),
125 el(ToggleControl, {
126 label: __('Show Divider Line', 'blockenberg'),
127 checked: attr.showDivider,
128 onChange: function (v) { set({ showDivider: v }); },
129 __nextHasNoMarginBottom: true
130 }),
131 el(ToggleControl, {
132 label: __('Show Progress Bar', 'blockenberg'),
133 checked: attr.showProgressBar,
134 onChange: function (v) { set({ showProgressBar: v }); },
135 __nextHasNoMarginBottom: true
136 }),
137 el(RangeControl, {
138 label: __('Padding Top (px)', 'blockenberg'),
139 value: attr.paddingTop, min: 0, max: 200,
140 onChange: function (v) { set({ paddingTop: v }); },
141 __nextHasNoMarginBottom: true
142 }),
143 el(RangeControl, {
144 label: __('Padding Bottom (px)', 'blockenberg'),
145 value: attr.paddingBottom, min: 0, max: 200,
146 onChange: function (v) { set({ paddingBottom: v }); },
147 __nextHasNoMarginBottom: true
148 })
149 ),
150 el(PanelColorSettings, {
151 title: __('Colors', 'blockenberg'), initialOpen: false,
152 colorSettings: [
153 { value: attr.bgColor, onChange: function (v) { set({ bgColor: v || '' }); }, label: __('Section Background', 'blockenberg') },
154 { value: attr.panelBg, onChange: function (v) { set({ panelBg: v || '#f8f8fc' }); }, label: __('Sticky Panel Background', 'blockenberg') },
155 { value: attr.eyebrowColor, onChange: function (v) { set({ eyebrowColor: v || '#6366f1' }); }, label: __('Eyebrow Color', 'blockenberg') },
156 { value: attr.titleColor, onChange: function (v) { set({ titleColor: v || '#111827' }); }, label: __('Title Color', 'blockenberg') },
157 { value: attr.textColor, onChange: function (v) { set({ textColor: v || '#6b7280' }); }, label: __('Body Text', 'blockenberg') },
158 { value: attr.iconBg, onChange: function (v) { set({ iconBg: v || '#ede9fe' }); }, label: __('Icon Background', 'blockenberg') },
159 { value: attr.dividerColor, onChange: function (v) { set({ dividerColor: v || '#e5e7eb' }); }, label: __('Divider', 'blockenberg') },
160 { value: attr.accentColor, onChange: function (v) { set({ accentColor: v || '#6366f1' }); }, label: __('Accent', 'blockenberg') }
161 ]
162 }),
163 el(PanelBody, { title: __('Typography', 'blockenberg'), initialOpen: false },
164 getTypoControl() && getTypoControl()({ label: __('Eyebrow', 'blockenberg'), value: attr.eyebrowTypo, onChange: function (v) { set({ eyebrowTypo: v }); } }),
165 getTypoControl() && getTypoControl()({ label: __('Title', 'blockenberg'), value: attr.titleTypo, onChange: function (v) { set({ titleTypo: v }); } }),
166 getTypoControl() && getTypoControl()({ label: __('Body', 'blockenberg'), value: attr.bodyTypo, onChange: function (v) { set({ bodyTypo: v }); } })
167 )
168 );
169
170 /* Sticky panel preview */
171 var stickyPanel = el('div', { style: { flex: cols[isStickRight ? 1 : 0] + '%', background: attr.panelBg, borderRadius: attr.imageRadius + 'px', padding: '24px', minHeight: '320px', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: '16px', position: 'relative' } },
172 previewItem.imageUrl
173 ? el('img', { src: previewItem.imageUrl, style: { width: '100%', height: '280px', objectFit: 'cover', borderRadius: attr.imageRadius + 'px' } })
174 : el('div', { style: { width: '100%', height: '200px', background: (previewItem.accentColor || attr.accentColor) + '20', borderRadius: attr.imageRadius + 'px', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: '48px' } }, (previewItem.iconType || 'custom-char') !== 'custom-char' ? IP().buildEditorIcon(previewItem.iconType, previewItem.icon, previewItem.iconDashicon, previewItem.iconImageUrl, previewItem.iconDashiconColor) : (previewItem.icon || '🖼️')),
175 el('div', { style: { color: attr.textColor, fontSize: '13px', textAlign: 'center' } }, __('Sticky panel preview — click items below to switch', 'blockenberg')),
176 el(MediaUploadCheck, {},
177 el(MediaUpload, {
178 onSelect: function (m) { updateItem(previewIdx, 'imageUrl', m.url); updateItem(previewIdx, 'imageId', m.id); updateItem(previewIdx, 'imageAlt', m.alt || ''); },
179 allowedTypes: ['image'], value: previewItem.imageId || 0,
180 render: function (ref) { return el(Button, { onClick: ref.open, variant: 'primary', isSmall: true }, previewItem.imageUrl ? __('Replace Image', 'blockenberg') : __('Choose Image', 'blockenberg')); }
181 })
182 )
183 );
184
185 /* Scrolling items */
186 var scrollCol = el('div', { style: { flex: cols[isStickRight ? 0 : 1] + '%', paddingRight: isStickRight ? '0' : '0', borderRight: isStickRight && attr.showDivider ? ('1px solid ' + attr.dividerColor) : 'none', borderLeft: !isStickRight && attr.showDivider ? ('1px solid ' + attr.dividerColor) : 'none' } },
187 items.map(function (it, idx) {
188 var isActive = idx === previewIdx;
189 var accent = it.accentColor || attr.accentColor;
190 return el('div', {
191 key: idx,
192 onClick: function () { setPreviewIdx(idx); },
193 style: {
194 padding: '24px 0',
195 paddingBottom: idx < items.length - 1 ? attr.gap + 'px' : '24px',
196 borderLeft: isStickRight ? ('3px solid ' + (isActive ? accent : 'transparent')) : 'none',
197 borderRight: !isStickRight ? ('3px solid ' + (isActive ? accent : 'transparent')) : 'none',
198 paddingLeft: isStickRight ? '24px' : '0',
199 paddingRight: !isStickRight ? '24px' : '0',
200 cursor: 'pointer',
201 opacity: isActive ? 1 : 0.65,
202 transition: 'opacity 0.2s, border-color 0.2s'
203 }
204 },
205 el('div', { style: { display: 'flex', alignItems: 'flex-start', gap: '14px' } },
206 el('div', { style: { width: attr.iconSize + 'px', height: attr.iconSize + 'px', background: isActive ? (accent + '20') : attr.iconBg, borderRadius: '10px', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: Math.round(attr.iconSize * 0.55) + 'px', flexShrink: 0, transition: 'background 0.2s' } }, (it.iconType || 'custom-char') !== 'custom-char' ? IP().buildEditorIcon(it.iconType, it.icon, it.iconDashicon, it.iconImageUrl, it.iconDashiconColor) : (it.icon || '')),
207 el('div', { style: { flex: 1 } },
208 it.eyebrow && el('div', { className: 'bkbg-s2c-eyebrow', style: { color: accent, marginBottom: '6px' } }, it.eyebrow),
209 el(RichText, {
210 tagName: 'h3',
211 className: 'bkbg-s2c-title',
212 value: it.title,
213 onChange: function (v) { updateItem(idx, 'title', v); },
214 placeholder: __('Feature title', 'blockenberg'),
215 style: { color: attr.titleColor, margin: '0 0 10px' }
216 }),
217 el(RichText, {
218 tagName: 'p',
219 className: 'bkbg-s2c-text',
220 value: it.text,
221 onChange: function (v) { updateItem(idx, 'text', v); },
222 placeholder: __('Describe this feature...', 'blockenberg'),
223 style: { color: attr.textColor, margin: 0 }
224 })
225 )
226 ),
227 el('div', { style: { display: 'flex', gap: '6px', marginTop: '8px', paddingLeft: (attr.iconSize + 14) + 'px' } },
228 el(IP().IconPickerControl, IP().iconPickerProps(it, function (patch) { var updated = items.map(function (x, j) { if (j !== idx) return x; return Object.assign({}, x, patch); }); set({ items: updated }); }, { label: 'Icon', charAttr: 'icon', typeAttr: 'iconType', dashiconAttr: 'iconDashicon', imageUrlAttr: 'iconImageUrl' })),
229 el(TextControl, { label: 'Eyebrow', value: it.eyebrow, onChange: function (v) { updateItem(idx, 'eyebrow', v); }, style: { width: '90px' }, __nextHasNoMarginBottom: true }),
230 el(BkbgColorSwatch, { label: 'Accent', value: it.accentColor, onChange: function (v) { updateItem(idx, 'accentColor', v); } }),
231 el(Button, { onClick: function () { removeItem(idx); }, variant: 'link', isDestructive: true, isSmall: true }, __('Remove', 'blockenberg'))
232 )
233 );
234 }),
235 el('div', { style: { marginTop: '16px' } },
236 el(Button, { onClick: addItem, variant: 'primary', isSmall: true }, __('+ Add Feature', 'blockenberg'))
237 )
238 );
239
240 var leftCol = isStickRight ? scrollCol : stickyPanel;
241 var rightCol = isStickRight ? stickyPanel : scrollCol;
242
243 return el(Fragment, {},
244 inspector,
245 el('div', useBlockProps((function() {
246 var _tvf = getTypoCssVars();
247 var s = { background: attr.bgColor || '', paddingTop: attr.paddingTop + 'px', paddingBottom: attr.paddingBottom + 'px' };
248 if (_tvf) {
249 Object.assign(s, _tvf(attr.eyebrowTypo, '--bks2c-eb-'));
250 Object.assign(s, _tvf(attr.titleTypo, '--bks2c-tt-'));
251 Object.assign(s, _tvf(attr.bodyTypo, '--bks2c-bd-'));
252 }
253 s['--bks2c-title-fw'] = attr.titleFontWeight || '800';
254 s['--bks2c-title-lh'] = attr.titleLineHeight || 1.2;
255 s['--bks2c-body-lh'] = attr.bodyLineHeight || 1.7;
256 return { style: s };
257 })()),
258 el('div', { style: { display: 'flex', gap: '48px', alignItems: 'flex-start' } },
259 leftCol, rightCol
260 )
261 )
262 );
263 },
264
265 save: function (props) {
266 return el('div', useBlockProps.save(),
267 el('div', { className: 'bkbg-s2c-app', 'data-opts': JSON.stringify(props.attributes) })
268 );
269 }
270 });
271 }() );
272