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

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

247 lines 16.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 ( function () {
2 const el = window.wp.element.createElement;
3 const { registerBlockType } = window.wp.blocks;
4 const { InspectorControls, useBlockProps, MediaUpload, PanelColorSettings } = window.wp.blockEditor;
5 const { PanelBody, RangeControl, SelectControl, ToggleControl, TextControl, Button } = window.wp.components;
6 const { useState } = window.wp.element;
7 const { __ } = window.wp.i18n;
8
9 var _tc; function getTypoControl() { return _tc || (_tc = window.bkbgTypographyControl); }
10 var _tv; function getTypoCssVars() { return _tv || (_tv = window.bkbgTypoCssVars); }
11
12 const STYLES = [
13 { label: 'Light', value: 'light' },
14 { label: 'Dark', value: 'dark' },
15 { label: 'Glass', value: 'glass' },
16 { label: 'Gradient', value: 'gradient' },
17 ];
18
19 function wrapStyle(a) {
20 var _tvf = getTypoCssVars();
21 var s = {
22 '--bkbg-sth-bg': a.bgColor,
23 '--bkbg-sth-text': a.textColor,
24 '--bkbg-sth-link': a.linkColor,
25 '--bkbg-sth-link-hover': a.linkHoverColor,
26 '--bkbg-sth-accent': a.accentColor,
27 '--bkbg-sth-cta-bg': a.ctaBg,
28 '--bkbg-sth-cta-text': a.ctaTextColor,
29 '--bkbg-sth-border': a.borderColor,
30 '--bkbg-sth-max-w': a.maxWidth + 'px',
31 '--bkbg-sth-h': a.height + 'px',
32 '--bkbg-sth-h-shrunk': a.heightShrunk + 'px',
33 '--bkbg-sth-logo-sz': a.logoSize + 'px',
34 '--bkbg-sth-logo-w': a.logoWeight,
35 '--bkbg-sth-logo-lh': a.logoLineHeight,
36 '--bkbg-sth-link-sz': a.linkSize + 'px',
37 '--bkbg-sth-link-w': a.linkWeight,
38 '--bkbg-sth-link-gap': a.linkGap + 'px',
39 '--bkbg-sth-link-lh': a.linkLineHeight,
40 '--bkbg-sth-cta-sz': a.ctaSize + 'px',
41 '--bkbg-sth-cta-w': a.ctaWeight,
42 '--bkbg-sth-cta-r': a.ctaRadius + 'px',
43 };
44 Object.assign(s, _tvf(a.logoTypo, '--bksth-lg-'));
45 Object.assign(s, _tvf(a.linkTypo, '--bksth-lk-'));
46 Object.assign(s, _tvf(a.ctaTypo, '--bksth-ct-'));
47 return s;
48 }
49
50 registerBlockType('blockenberg/sticky-header', {
51 edit: function (props) {
52 const { attributes: a, setAttributes } = props;
53 const [editingLink, setEditingLink] = useState(null);
54
55 function updateLink(i, key, val) {
56 const next = a.navLinks.slice();
57 next[i] = Object.assign({}, next[i], { [key]: val });
58 setAttributes({ navLinks: next });
59 }
60 function addLink() {
61 setAttributes({ navLinks: [...a.navLinks, { label: 'New Link', url: '#', openNew: false }] });
62 }
63 function removeLink(i) {
64 setAttributes({ navLinks: a.navLinks.filter((_, x) => x !== i) });
65 if (editingLink === i) setEditingLink(null);
66 }
67 function moveLink(i, dir) {
68 const next = a.navLinks.slice();
69 const target = i + dir;
70 if (target < 0 || target >= next.length) return;
71 [next[i], next[target]] = [next[target], next[i]];
72 setAttributes({ navLinks: next });
73 }
74
75 const classList = [
76 'bkbg-sth-header',
77 'bkbg-sth-style--' + a.style,
78 a.borderBottom ? 'bkbg-sth-border-bottom' : '',
79 a.shadow ? 'bkbg-sth-shadow' : '',
80 ].filter(Boolean).join(' ');
81
82 const blockProps = useBlockProps({ className: 'bkbg-sth-wrap', style: wrapStyle(a) });
83
84 return el('div', blockProps,
85 el(InspectorControls, null,
86
87 el(PanelBody, { title: __('Logo'), initialOpen: true },
88 el(ToggleControl, { label: __('Show Logo'), checked: a.showLogo, onChange: v => setAttributes({ showLogo: v }), __nextHasNoMarginBottom: true }),
89 el(TextControl, { label: __('Logo Text (fallback)'), value: a.logoText, onChange: v => setAttributes({ logoText: v }) }),
90 a.logoUrl
91 ? el('div', { style: { marginBottom: 8 } },
92 el('img', { src: a.logoUrl, style: { maxWidth: a.logoWidth + 'px', height: 'auto', display: 'block', marginBottom: 8 } }),
93 el(Button, { isDestructive: true, isSmall: true, onClick: () => setAttributes({ logoUrl: '', logoId: 0 }) }, __('Remove Logo')),
94 )
95 : el(MediaUpload, {
96 onSelect: m => setAttributes({ logoUrl: m.url, logoId: m.id }),
97 allowedTypes: ['image'],
98 value: a.logoId,
99 render: ({ open }) => el(Button, { variant: 'secondary', onClick: open, style: { width: '100%', justifyContent: 'center' } }, __('Upload Logo Image')),
100 }),
101 el(RangeControl, { label: __('Logo Image Width (px)'), value: a.logoWidth, min: 40, max: 300, onChange: v => setAttributes({ logoWidth: v }) }),
102 el(TextControl, { label: __('Site URL'), value: a.siteUrl, onChange: v => setAttributes({ siteUrl: v }) }),
103 ),
104
105 el(PanelBody, { title: __('Navigation Links'), initialOpen: true },
106 a.navLinks.map((lnk, i) =>
107 el('div', { key: i, style: { border: '1px solid ' + (editingLink === i ? '#6c3fb5' : '#e2e8f0'), borderRadius: 8, marginBottom: 8, overflow: 'hidden' } },
108 el('div', {
109 style: { display: 'flex', alignItems: 'center', gap: 8, padding: '8px 12px', cursor: 'pointer', background: editingLink === i ? '#f5f0ff' : '#fafafa' },
110 onClick: () => setEditingLink(editingLink === i ? null : i),
111 },
112 el('span', { style: { flex: 1, fontWeight: 600, fontSize: 13 } }, lnk.label || __('Link') + ' ' + (i + 1)),
113 el('span', { className: 'dashicons dashicons-' + (editingLink === i ? 'arrow-up-alt2' : 'arrow-down-alt2'), style: { fontSize: 16, color: '#94a3b8' } }),
114 ),
115 editingLink === i && el('div', { style: { padding: '0 12px 12px' } },
116 el(TextControl, { label: __('Label'), value: lnk.label, onChange: v => updateLink(i, 'label', v) }),
117 el(TextControl, { label: __('URL'), value: lnk.url, onChange: v => updateLink(i, 'url', v) }),
118 el(ToggleControl, { label: __('Open in New Tab'), checked: lnk.openNew, onChange: v => updateLink(i, 'openNew', v), __nextHasNoMarginBottom: true }),
119 el('div', { style: { display: 'flex', gap: 6, marginTop: 8 } },
120 el(Button, { isSmall: true, variant: 'secondary', onClick: () => moveLink(i, -1), disabled: i === 0 }, ''),
121 el(Button, { isSmall: true, variant: 'secondary', onClick: () => moveLink(i, 1), disabled: i === a.navLinks.length - 1 }, ''),
122 el(Button, { isSmall: true, isDestructive: true, onClick: () => removeLink(i) }, __('Remove')),
123 ),
124 ),
125 )
126 ),
127 el(Button, { variant: 'secondary', onClick: addLink, style: { width: '100%', justifyContent: 'center', marginTop: 8 } }, __('+ Add Link')),
128 ),
129
130 el(PanelBody, { title: __('CTA Button'), initialOpen: false },
131 el(ToggleControl, { label: __('Show CTA Button'), checked: a.showCta, onChange: v => setAttributes({ showCta: v }), __nextHasNoMarginBottom: true }),
132 el(TextControl, { label: __('Button Text'), value: a.ctaText, onChange: v => setAttributes({ ctaText: v }) }),
133 el(TextControl, { label: __('Button URL'), value: a.ctaUrl, onChange: v => setAttributes({ ctaUrl: v }) }),
134 el(ToggleControl, { label: __('Open in New Tab'), checked: a.ctaOpenNew, onChange: v => setAttributes({ ctaOpenNew: v }), __nextHasNoMarginBottom: true }),
135 ),
136
137 el(PanelBody, { title: __('Style & Behavior'), initialOpen: false },
138 el(SelectControl, { label: __('Style'), value: a.style, options: STYLES, onChange: v => setAttributes({ style: v }) }),
139 el(ToggleControl, { label: __('Sticky on Scroll'), checked: a.sticky, onChange: v => setAttributes({ sticky: v }), __nextHasNoMarginBottom: true }),
140 el(ToggleControl, { label: __('Shrink on Scroll'), checked: a.scrollShrink, onChange: v => setAttributes({ scrollShrink: v }), __nextHasNoMarginBottom: true }),
141 el(ToggleControl, { label: __('Show Mobile Menu Toggle'), checked: a.showMobileMenu, onChange: v => setAttributes({ showMobileMenu: v }), __nextHasNoMarginBottom: true }),
142 el(ToggleControl, { label: __('Border Bottom'), checked: a.borderBottom, onChange: v => setAttributes({ borderBottom: v }), __nextHasNoMarginBottom: true }),
143 el(ToggleControl, { label: __('Drop Shadow (when scrolled)'), checked: a.shadow, onChange: v => setAttributes({ shadow: v }), __nextHasNoMarginBottom: true }),
144 el(RangeControl, { label: __('Header Height (px)'), value: a.height, min: 40, max: 120, onChange: v => setAttributes({ height: v }) }),
145 el(RangeControl, { label: __('Shrunk Height (px)'), value: a.heightShrunk, min: 36, max: 100, onChange: v => setAttributes({ heightShrunk: v }) }),
146 el(RangeControl, { label: __('Max Width (px)'), value: a.maxWidth, min: 760, max: 1600, onChange: v => setAttributes({ maxWidth: v }) }),
147 el(RangeControl, { label: __('z-index'), value: a.zIndex, min: 1, max: 9999, onChange: v => setAttributes({ zIndex: v }) }),
148 ),
149
150 el(PanelBody, { title: __('Typography'), initialOpen: false },
151 getTypoControl() && getTypoControl()({ label: __('Logo Text'), value: a.logoTypo, onChange: function (v) { setAttributes({ logoTypo: v }); } }),
152 getTypoControl() && getTypoControl()({ label: __('Nav Links'), value: a.linkTypo, onChange: function (v) { setAttributes({ linkTypo: v }); } }),
153 getTypoControl() && getTypoControl()({ label: __('CTA Button'), value: a.ctaTypo, onChange: function (v) { setAttributes({ ctaTypo: v }); } }),
154 el(RangeControl, { label: __('Nav Gap (px)'), value: a.linkGap, min: 8, max: 64, onChange: v => setAttributes({ linkGap: v }) }),
155 el(RangeControl, { label: __('CTA Radius (px)'), value: a.ctaRadius, min: 0, max: 32, onChange: v => setAttributes({ ctaRadius: v }) }),
156 ),
157
158 el(PanelColorSettings, {
159 title: __('Colors'), initialOpen: false,
160 colorSettings: [
161 { label: __('Background'), value: a.bgColor, onChange: v => setAttributes({ bgColor: v || '#ffffff' }) },
162 { label: __('Text / Logo'), value: a.textColor, onChange: v => setAttributes({ textColor: v || '#0f172a' }) },
163 { label: __('Nav Links'), value: a.linkColor, onChange: v => setAttributes({ linkColor: v || '#374151' }) },
164 { label: __('Link Hover'), value: a.linkHoverColor,onChange: v => setAttributes({ linkHoverColor:v || '#6c3fb5' }) },
165 { label: __('Accent'), value: a.accentColor, onChange: v => setAttributes({ accentColor: v || '#6c3fb5' }) },
166 { label: __('CTA BG'), value: a.ctaBg, onChange: v => setAttributes({ ctaBg: v || '#6c3fb5' }) },
167 { label: __('CTA Text'), value: a.ctaTextColor, onChange: v => setAttributes({ ctaTextColor: v || '#ffffff' }) },
168 { label: __('Border'), value: a.borderColor, onChange: v => setAttributes({ borderColor: v || '#e2e8f0' }) },
169 ]
170 }),
171 ),
172
173 /* ---- Editor Canvas ---- */
174 el('header', {
175 className: classList,
176 style: { position: 'relative' },
177 },
178 el('div', { className: 'bkbg-sth-inner' },
179 /* Logo */
180 a.showLogo && el('div', { className: 'bkbg-sth-logo' },
181 a.logoUrl
182 ? el('img', { src: a.logoUrl, alt: a.logoText, className: 'bkbg-sth-logo-img' })
183 : el('span', { className: 'bkbg-sth-logo-text' }, a.logoText),
184 ),
185 /* Nav */
186 el('nav', { className: 'bkbg-sth-nav' },
187 a.navLinks.map((lnk, i) =>
188 el('a', { key: i, className: 'bkbg-sth-nav-link', href: lnk.url, onClick: e => e.preventDefault() }, lnk.label)
189 )
190 ),
191 /* CTA + Hamburger */
192 el('div', { className: 'bkbg-sth-actions' },
193 a.showCta && el('a', { className: 'bkbg-sth-cta', href: a.ctaUrl, onClick: e => e.preventDefault() }, a.ctaText),
194 a.showMobileMenu && el('button', { className: 'bkbg-sth-hamburger', 'aria-label': 'Menu' },
195 el('span'), el('span'), el('span'),
196 ),
197 ),
198 ),
199 ),
200 );
201 },
202
203 save: function ({ attributes: a }) {
204 const classList = [
205 'bkbg-sth-header',
206 'bkbg-sth-style--' + a.style,
207 a.sticky ? 'bkbg-sth-sticky' : '',
208 a.scrollShrink ? 'bkbg-sth-shrink' : '',
209 a.borderBottom ? 'bkbg-sth-border-bottom' : '',
210 a.shadow ? 'bkbg-sth-shadow' : '',
211 ].filter(Boolean).join(' ');
212
213 return el('div', {
214 className: 'bkbg-sth-wrap',
215 style: { ...wrapStyle(a), '--bkbg-sth-z': a.zIndex },
216 },
217 el('header', { className: classList, 'data-sticky': a.sticky ? '1' : '0', 'data-shrink': a.scrollShrink ? '1' : '0' },
218 el('div', { className: 'bkbg-sth-inner' },
219 a.showLogo && el('a', { href: a.siteUrl, className: 'bkbg-sth-logo' },
220 a.logoUrl
221 ? el('img', { src: a.logoUrl, alt: a.logoText, className: 'bkbg-sth-logo-img', style: { width: a.logoWidth + 'px' } })
222 : el('span', { className: 'bkbg-sth-logo-text' }, a.logoText),
223 ),
224 el('nav', { className: 'bkbg-sth-nav', 'aria-label': 'Main navigation' },
225 a.navLinks.map((lnk, i) =>
226 el('a', { key: i, className: 'bkbg-sth-nav-link', href: lnk.url, target: lnk.openNew ? '_blank' : undefined, rel: lnk.openNew ? 'noopener noreferrer' : undefined }, lnk.label)
227 )
228 ),
229 el('div', { className: 'bkbg-sth-actions' },
230 a.showCta && el('a', { className: 'bkbg-sth-cta', href: a.ctaUrl, target: a.ctaOpenNew ? '_blank' : undefined, rel: a.ctaOpenNew ? 'noopener noreferrer' : undefined }, a.ctaText),
231 a.showMobileMenu && el('button', { className: 'bkbg-sth-hamburger', 'aria-label': __('Open menu'), 'aria-expanded': 'false', 'aria-controls': 'bkbg-sth-mobile-nav' },
232 el('span'), el('span'), el('span'),
233 ),
234 ),
235 ),
236 a.showMobileMenu && el('div', { id: 'bkbg-sth-mobile-nav', className: 'bkbg-sth-mobile-nav', 'aria-hidden': 'true' },
237 a.navLinks.map((lnk, i) =>
238 el('a', { key: i, className: 'bkbg-sth-mobile-link', href: lnk.url, target: lnk.openNew ? '_blank' : undefined }, lnk.label)
239 ),
240 a.showCta && el('a', { className: 'bkbg-sth-cta bkbg-sth-mobile-cta', href: a.ctaUrl }, a.ctaText),
241 ),
242 ),
243 );
244 }
245 });
246 }() );
247