# blockenberg/2.0.7/blocks/chapter-navigation/index.js

Blockenberg — 600+ Advanced Gutenberg Blocks &amp; AI Agent for WordPress Block Editor, version 2.0.7. 207 lines.

- Page: https://pluginprobe.com/plugins/blockenberg/2.0.7/code/blocks/chapter-navigation/index.js
- Raw: https://pluginprobe.com/plugins/blockenberg/2.0.7/raw/blocks/chapter-navigation/index.js
- Modified: 2026-03-06T12:32:40+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/blockenberg/2.0.7/code/blocks/chapter-navigation/index.js#L10-L20`.

```javascript
( function () {
    var el = wp.element.createElement;
    var __ = wp.i18n.__;
    var registerBlockType = wp.blocks.registerBlockType;
    var InspectorControls = wp.blockEditor.InspectorControls;
    var useBlockProps = wp.blockEditor.useBlockProps;
    var PanelColorSettings = wp.blockEditor.PanelColorSettings;
    var PanelBody = wp.components.PanelBody;
    var ToggleControl = wp.components.ToggleControl;
    var RangeControl = wp.components.RangeControl;
    var SelectControl = wp.components.SelectControl;
    var TextControl = wp.components.TextControl;
    var Button = wp.components.Button;

    function getTypographyControl() { return (window.bkbgTypographyControl || function () { return null; }); }
    function getTypoCssVars() { return (window.bkbgTypoCssVars || function () { return {}; }); }
    function _tv(typo, prefix) { var fn = getTypoCssVars(); return fn(typo || {}, prefix); }

    registerBlockType('blockenberg/chapter-navigation', {
        edit: function (props) {
            var attr = props.attributes;
            var set = props.setAttributes;

            var blockProps = useBlockProps({ style: Object.assign({ padding: attr.paddingTop + 'px 0 ' + attr.paddingBottom + 'px' }, _tv(attr.typoTitle, '--bkbg-cnv-tt-')) });

            var progress = Math.round((attr.currentPart / attr.totalParts) * 100);

            function updateChapter(idx, key, value) {
                var arr = attr.chapters.map(function (c, i) {
                    if (i !== idx) return c;
                    var copy = Object.assign({}, c);
                    copy[key] = value;
                    return copy;
                });
                set({ chapters: arr });
            }

            var wrapStyle = { background: attr.bgColor, border: '1px solid ' + attr.borderColor, borderRadius: attr.borderRadius + 'px', overflow: 'hidden', boxShadow: '0 2px 8px rgba(0,0,0,.06)' };
            if (attr.style === 'banner') {
                wrapStyle = { background: attr.bgColor, borderLeft: '4px solid ' + attr.accentColor, borderTop: '1px solid ' + attr.borderColor, borderBottom: '1px solid ' + attr.borderColor, borderRight: '1px solid ' + attr.borderColor, borderRadius: attr.borderRadius + 'px', overflow: 'hidden' };
            }
            if (attr.style === 'minimal') {
                wrapStyle = { borderTop: '2px solid ' + attr.accentColor, paddingTop: '16px' };
            }

            var controls = el(InspectorControls, {},
                el(PanelBody, { title: __('Series Info', 'blockenberg'), initialOpen: true },
                    el(TextControl, { label: __('Series Title', 'blockenberg'), value: attr.seriesTitle, __nextHasNoMarginBottom: true, onChange: function (v) { set({ seriesTitle: v }); } }),
                    el('div', { style: { marginTop: '8px' } },
                        el(TextControl, { label: __('Current Chapter Title', 'blockenberg'), value: attr.currentTitle, __nextHasNoMarginBottom: true, onChange: function (v) { set({ currentTitle: v }); } })
                    ),
                    el('div', { style: { marginTop: '8px' } },
                        el(TextControl, { label: __('Part Label', 'blockenberg'), value: attr.partLabel, __nextHasNoMarginBottom: true, onChange: function (v) { set({ partLabel: v }); } })
                    ),
                    el('div', { style: { marginTop: '8px', display: 'flex', gap: '8px' } },
                        el('div', { style: { flex: 1 } },
                            el(RangeControl, { label: __('Current Part', 'blockenberg'), value: attr.currentPart, min: 1, max: attr.totalParts, __nextHasNoMarginBottom: true, onChange: function (v) { set({ currentPart: v }); } })
                        ),
                        el('div', { style: { flex: 1 } },
                            el(RangeControl, { label: __('Total Parts', 'blockenberg'), value: attr.totalParts, min: 1, max: 50, __nextHasNoMarginBottom: true, onChange: function (v) { set({ totalParts: v }); } })
                        )
                    )
                ),
                el(PanelBody, { title: __('Prev / Next', 'blockenberg'), initialOpen: false },
                    el(TextControl, { label: __('Previous Chapter Title', 'blockenberg'), value: attr.prevTitle, __nextHasNoMarginBottom: true, onChange: function (v) { set({ prevTitle: v }); } }),
                    el('div', { style: { marginTop: '8px' } },
                        el(TextControl, { label: __('Previous URL', 'blockenberg'), value: attr.prevUrl, placeholder: 'https://...', __nextHasNoMarginBottom: true, onChange: function (v) { set({ prevUrl: v }); } })
                    ),
                    el('div', { style: { marginTop: '12px' } },
                        el(TextControl, { label: __('Next Chapter Title', 'blockenberg'), value: attr.nextTitle, __nextHasNoMarginBottom: true, onChange: function (v) { set({ nextTitle: v }); } })
                    ),
                    el('div', { style: { marginTop: '8px' } },
                        el(TextControl, { label: __('Next URL', 'blockenberg'), value: attr.nextUrl, placeholder: 'https://...', __nextHasNoMarginBottom: true, onChange: function (v) { set({ nextUrl: v }); } })
                    )
                ),
                el(PanelBody, { title: __('Chapter List', 'blockenberg'), initialOpen: false },
                    el(ToggleControl, { label: __('Show All Chapters List', 'blockenberg'), checked: attr.showChapterList, __nextHasNoMarginBottom: true, onChange: function (v) { set({ showChapterList: v }); } }),
                    el('div', { style: { marginTop: '8px' } },
                        el(ToggleControl, { label: __('Open List by Default', 'blockenberg'), checked: attr.chapterListOpen, __nextHasNoMarginBottom: true, onChange: function (v) { set({ chapterListOpen: v }); } })
                    ),
                    el('div', { style: { marginTop: '12px' } },
                        (attr.chapters || []).map(function (ch, idx) {
                            return el('div', { key: idx, style: { marginBottom: '8px', padding: '8px', background: '#f8fafc', borderRadius: '4px', border: '1px solid #e2e8f0' } },
                                el('strong', { style: { fontSize: '11px', color: '#6366f1' } }, (idx + 1) + '.'),
                                el('div', { style: { marginTop: '4px' } },
                                    el(TextControl, { label: '', value: ch.title, placeholder: 'Chapter title', __nextHasNoMarginBottom: true, onChange: function (v) { updateChapter(idx, 'title', v); } })
                                ),
                                el('div', { style: { marginTop: '4px' } },
                                    el(TextControl, { label: '', value: ch.url, placeholder: 'URL (https://...)', __nextHasNoMarginBottom: true, onChange: function (v) { updateChapter(idx, 'url', v); } })
                                ),
                                el(Button, { variant: 'link', isDestructive: true, style: { fontSize: '11px' }, onClick: function () { set({ chapters: attr.chapters.filter(function (_, i) { return i !== idx; }) }); } }, __('Remove', 'blockenberg'))
                            );
                        })
                    ),
                    el(Button, { variant: 'secondary', style: { marginTop: '8px' }, onClick: function () { set({ chapters: (attr.chapters || []).concat([{ title: 'New Chapter', url: '' }]) }); } }, __('+ Add Chapter', 'blockenberg'))
                ),
                el(PanelBody, { title: __('Style & Layout', 'blockenberg'), initialOpen: false },
                    el(SelectControl, {
                        label: __('Style', 'blockenberg'), value: attr.style, __nextHasNoMarginBottom: true,
                        options: [{ label: 'Card (bordered box)', value: 'card' }, { label: 'Banner (left accent)', value: 'banner' }, { label: 'Minimal (top line)', value: 'minimal' }],
                        onChange: function (v) { set({ style: v }); }
                    }),
                    el('div', { style: { marginTop: '12px' } },
                        el(ToggleControl, { label: __('Show Progress Bar', 'blockenberg'), checked: attr.showProgress, __nextHasNoMarginBottom: true, onChange: function (v) { set({ showProgress: v }); } })
                    ),
                    el('div', { style: { marginTop: '4px' } },
                        el(ToggleControl, { label: __('Open Links in New Tab', 'blockenberg'), checked: attr.openInNewTab, __nextHasNoMarginBottom: true, onChange: function (v) { set({ openInNewTab: v }); } })
                    ),
                    el('div', { style: { marginTop: '12px' } },
                        el(RangeControl, { label: __('Border Radius', 'blockenberg'), value: attr.borderRadius, min: 0, max: 24, __nextHasNoMarginBottom: true, onChange: function (v) { set({ borderRadius: v }); } })
                    )
                ),
                el(PanelBody, { title: __('Spacing', 'blockenberg'), initialOpen: false },
                    el(RangeControl, { label: __('Padding Top', 'blockenberg'), value: attr.paddingTop, min: 0, max: 120, __nextHasNoMarginBottom: true, onChange: function (v) { set({ paddingTop: v }); } }),
                    el('div', { style: { marginTop: '12px' } },
                        el(RangeControl, { label: __('Padding Bottom', 'blockenberg'), value: attr.paddingBottom, min: 0, max: 120, __nextHasNoMarginBottom: true, onChange: function (v) { set({ paddingBottom: v }); } })
                    )
                ),
                el(PanelBody, { title: __('Typography', 'blockenberg'), initialOpen: false },
                    el(getTypographyControl(), { label: __('Current Title', 'blockenberg'), value: attr.typoTitle, onChange: function (v) { set({ typoTitle: v }); } }),
                    el(RangeControl, { label: __('Series Title Size (px)', 'blockenberg'), value: attr.seriesTitleFontSize, min: 10, max: 20, __nextHasNoMarginBottom: true, onChange: function (v) { set({ seriesTitleFontSize: v }); } }),
                    el(RangeControl, { label: __('Nav Button Size (px)', 'blockenberg'), value: attr.navFontSize, min: 10, max: 20, __nextHasNoMarginBottom: true, onChange: function (v) { set({ navFontSize: v }); } }),
                    el(RangeControl, { label: __('Chapter List Size (px)', 'blockenberg'), value: attr.chapterListFontSize, min: 10, max: 20, __nextHasNoMarginBottom: true, onChange: function (v) { set({ chapterListFontSize: v }); } })
                ),
                el(PanelColorSettings, {
                    title: __('Colors', 'blockenberg'), initialOpen: false,
                    colorSettings: [
                        { label: __('Background', 'blockenberg'), value: attr.bgColor, onChange: function (v) { set({ bgColor: v || '#ffffff' }); } },
                        { label: __('Border', 'blockenberg'), value: attr.borderColor, onChange: function (v) { set({ borderColor: v || '#e2e8f0' }); } },
                        { label: __('Accent', 'blockenberg'), value: attr.accentColor, onChange: function (v) { set({ accentColor: v || '#6366f1' }); } },
                        { label: __('Series Title', 'blockenberg'), value: attr.seriesTitleColor, onChange: function (v) { set({ seriesTitleColor: v || '#374151' }); } },
                        { label: __('Current Title', 'blockenberg'), value: attr.currentTitleColor, onChange: function (v) { set({ currentTitleColor: v || '#111827' }); } },
                        { label: __('Progress Bar BG', 'blockenberg'), value: attr.progressBg, onChange: function (v) { set({ progressBg: v || '#e2e8f0' }); } },
                        { label: __('Progress Fill', 'blockenberg'), value: attr.progressFill, onChange: function (v) { set({ progressFill: v || '#6366f1' }); } },
                        { label: __('Nav Button BG', 'blockenberg'), value: attr.btnBg, onChange: function (v) { set({ btnBg: v || '#f1f5f9' }); } },
                        { label: __('Nav Button Text', 'blockenberg'), value: attr.btnColor, onChange: function (v) { set({ btnColor: v || '#374151' }); } },
                        { label: __('Pill BG', 'blockenberg'), value: attr.pillBg, onChange: function (v) { set({ pillBg: v || '#eef2ff' }); } },
                        { label: __('Pill Text', 'blockenberg'), value: attr.pillColor, onChange: function (v) { set({ pillColor: v || '#4f46e5' }); } },
                        { label: __('Chapter List BG', 'blockenberg'), value: attr.chapterListBg, onChange: function (v) { set({ chapterListBg: v || '#f8fafc' }); } },
                        { label: __('Active Chapter BG', 'blockenberg'), value: attr.activeChapterBg, onChange: function (v) { set({ activeChapterBg: v || '#eef2ff' }); } },
                        { label: __('Active Chapter Text', 'blockenberg'), value: attr.activeChapterColor, onChange: function (v) { set({ activeChapterColor: v || '#4f46e5' }); } }
                    ]
                })
            );

            /* ── Editor preview ── */
            var header = el('div', { style: { padding: '16px 20px', borderBottom: attr.style !== 'minimal' ? '1px solid ' + attr.borderColor : 'none' } },
                el('div', { style: { display: 'flex', alignItems: 'center', gap: '10px', marginBottom: '6px', flexWrap: 'wrap' } },
                    el('span', { style: { background: attr.pillBg, color: attr.pillColor, padding: '3px 10px', borderRadius: '20px', fontSize: '12px', fontWeight: 600 } }, attr.partLabel + ' ' + attr.currentPart + ' of ' + attr.totalParts),
                    el('span', { style: { color: attr.seriesTitleColor, fontSize: attr.seriesTitleFontSize + 'px' } }, attr.seriesTitle)
                ),
                el('div', { className: 'bkbg-cnv-current-title', style: { color: attr.currentTitleColor } }, attr.currentTitle),
                attr.showProgress && el('div', { style: { marginTop: '10px' } },
                    el('div', { style: { background: attr.progressBg, borderRadius: '4px', height: '6px', overflow: 'hidden' } },
                        el('div', { style: { background: attr.progressFill, width: progress + '%', height: '100%', borderRadius: '4px', transition: 'width .3s' } })
                    ),
                    el('div', { style: { fontSize: '11px', color: attr.seriesTitleColor, marginTop: '4px' } }, progress + '% through the series')
                )
            );

            var navRow = el('div', { style: { display: 'flex', gap: '10px', padding: '14px 20px', flexWrap: 'wrap' } },
                attr.prevTitle && el('a', { href: '#', style: { flex: 1, minWidth: '160px', background: attr.btnBg, color: attr.btnColor, padding: '10px 14px', borderRadius: '6px', fontSize: attr.navFontSize + 'px', textDecoration: 'none', display: 'flex', flexDirection: 'column', gap: '2px' } },
                    el('span', { style: { fontSize: '11px', opacity: 0.7 } }, '← Previous'),
                    el('span', { style: { fontWeight: 600 } }, attr.prevTitle)
                ),
                attr.nextTitle && el('a', { href: '#', style: { flex: 1, minWidth: '160px', background: attr.accentColor, color: '#fff', padding: '10px 14px', borderRadius: '6px', fontSize: attr.navFontSize + 'px', textDecoration: 'none', display: 'flex', flexDirection: 'column', gap: '2px', textAlign: 'right' } },
                    el('span', { style: { fontSize: '11px', opacity: 0.8 } }, 'Next →'),
                    el('span', { style: { fontWeight: 600 } }, attr.nextTitle)
                )
            );

            var chapterList = attr.showChapterList && el('details', { open: attr.chapterListOpen || true, style: { borderTop: '1px solid ' + attr.borderColor } },
                el('summary', { style: { padding: '12px 20px', cursor: 'pointer', fontSize: '13px', fontWeight: 600, color: attr.seriesTitleColor, listStyle: 'none', display: 'flex', alignItems: 'center', justifyContent: 'space-between', background: attr.chapterListBg } },
                    el('span', {}, 'All ' + attr.totalParts + ' chapters'),
                    el('span', { style: { fontSize: '16px' } }, '▾')
                ),
                el('ol', { style: { margin: 0, padding: '8px 0', listStyle: 'none', background: attr.chapterListBg } },
                    (attr.chapters || []).map(function (ch, i) {
                        var isActive = (i + 1) === attr.currentPart;
                        return el('li', { key: i, style: { padding: '8px 20px 8px 44px', position: 'relative', background: isActive ? attr.activeChapterBg : 'transparent', fontSize: attr.chapterListFontSize + 'px' } },
                            el('span', { style: { position: 'absolute', left: '20px', fontWeight: 700, color: isActive ? attr.activeChapterColor : attr.seriesTitleColor } }, i + 1 + '.'),
                            el('span', { style: { color: isActive ? attr.activeChapterColor : attr.seriesTitleColor, fontWeight: isActive ? 600 : 400 } }, ch.title),
                            isActive && el('span', { style: { marginLeft: '8px', fontSize: '11px', padding: '1px 7px', background: attr.accentColor, color: '#fff', borderRadius: '10px' } }, 'Current')
                        );
                    })
                )
            );

            return el('div', blockProps,
                controls,
                el('div', { style: wrapStyle },
                    header,
                    navRow,
                    chapterList
                )
            );
        },
        save: function (props) {
            var attr = props.attributes;
            var useBlockProps = wp.blockEditor.useBlockProps;
            return el('div', useBlockProps.save(),
                el('div', { className: 'bkbg-cnv-app', 'data-opts': JSON.stringify(attr) })
            );
        }
    });
}() );

```
