# blockenberg/2.0.7/blocks/feature-list/index.js

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

- Page: https://pluginprobe.com/plugins/blockenberg/2.0.7/code/blocks/feature-list/index.js
- Raw: https://pluginprobe.com/plugins/blockenberg/2.0.7/raw/blocks/feature-list/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/feature-list/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 RichText = wp.blockEditor.RichText;
    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;
    var Fragment = wp.element.Fragment;

    var _flTC, _flTV;
    function _tc() { return _flTC || (_flTC = window.bkbgTypographyControl); }
    function _tv() { return _flTV || (_flTV = window.bkbgTypoCssVars); }

    /* Status icon sets */
    var STATUS_ICONS = {
        'color-check': { included: '✓', 'not-included': '✗', partial: '◐', 'coming-soon': '⏱' },
        'emoji':       { included: '✅', 'not-included': '❌', partial: '🔶', 'coming-soon': '🔜' },
        'minimal':     { included: '•', 'not-included': '–', partial: '~', 'coming-soon': '…' }
    };

    function getStatusColor(status, a) {
        if (status === 'included')     return a.includedColor;
        if (status === 'not-included') return a.notIncludedColor;
        if (status === 'partial')      return a.partialColor;
        if (status === 'coming-soon')  return a.comingSoonColor;
        return a.labelColor;
    }

    function getStatusIcon(status, iconSet) {
        var icons = STATUS_ICONS[iconSet] || STATUS_ICONS['color-check'];
        return icons[status] || '•';
    }

    registerBlockType('blockenberg/feature-list', {
        edit: function (props) {
            var attr = props.attributes;
            var set = props.setAttributes;
            var items = attr.items || [];

            function updateItem(idx, field, val) {
                var updated = items.map(function (it, i) {
                    if (i !== idx) return it;
                    var p = {}; p[field] = val;
                    return Object.assign({}, it, p);
                });
                set({ items: updated });
            }

            function addItem() {
                set({ items: items.concat([{ label: 'New Feature', status: 'included', description: '', badge: '', group: '' }]) });
            }

            function removeItem(idx) { set({ items: items.filter(function (_, i) { return i !== idx; }) }); }

            function moveItem(idx, dir) {
                var arr = items.slice();
                var to = idx + dir;
                if (to < 0 || to >= arr.length) return;
                var tmp = arr[idx]; arr[idx] = arr[to]; arr[to] = tmp;
                set({ items: arr });
            }

            var inspector = el(InspectorControls, {},
                el(PanelBody, { title: __('Style', 'blockenberg'), initialOpen: true },
                    el(SelectControl, {
                        label: __('List Style', 'blockenberg'), value: attr.listStyle,
                        options: [
                            { label: 'Bordered rows', value: 'bordered' },
                            { label: 'Striped rows', value: 'striped' },
                            { label: 'Cards', value: 'card' },
                            { label: 'Compact (no lines)', value: 'compact' }
                        ],
                        onChange: function (v) { set({ listStyle: v }); }, __nextHasNoMarginBottom: true
                    }),
                    el(SelectControl, {
                        label: __('Icon Set', 'blockenberg'), value: attr.iconSet,
                        options: [
                            { label: 'Color check / cross', value: 'color-check' },
                            { label: 'Emoji', value: 'emoji' },
                            { label: 'Minimal (dot / dash)', value: 'minimal' }
                        ],
                        onChange: function (v) { set({ iconSet: v }); }, __nextHasNoMarginBottom: true
                    }),
                    el(RangeControl, { label: __('Icon Size (px)', 'blockenberg'), value: attr.iconSize, min: 14, max: 32, onChange: function (v) { set({ iconSize: v }); }, __nextHasNoMarginBottom: true }),
                    el(RangeControl, { label: __('Border Radius (px)', 'blockenberg'), value: attr.borderRadius, min: 0, max: 24, onChange: function (v) { set({ borderRadius: v }); }, __nextHasNoMarginBottom: true })
                ),
                el(PanelBody, { title: __('Visibility', 'blockenberg'), initialOpen: false },
                    el(ToggleControl, { label: __('Show Heading', 'blockenberg'), checked: attr.showHeading, onChange: function (v) { set({ showHeading: v }); }, __nextHasNoMarginBottom: true }),
                    el(ToggleControl, { label: __('Show Descriptions', 'blockenberg'), checked: attr.showDesc, onChange: function (v) { set({ showDesc: v }); }, __nextHasNoMarginBottom: true }),
                    el(ToggleControl, { label: __('Show Badges', 'blockenberg'), checked: attr.showBadge, onChange: function (v) { set({ showBadge: v }); }, __nextHasNoMarginBottom: true }),
                    el(ToggleControl, { label: __('Show "Not Included" Items', 'blockenberg'), checked: attr.showNotIncluded, onChange: function (v) { set({ showNotIncluded: v }); }, __nextHasNoMarginBottom: true }),
                    el(ToggleControl, { label: __('Strike Through "Not Included"', 'blockenberg'), checked: attr.strikeNotIncluded, onChange: function (v) { set({ strikeNotIncluded: v }); }, __nextHasNoMarginBottom: true })
                ),
                el(PanelBody, { title: __('Items', 'blockenberg'), initialOpen: true },
                    items.map(function (it, idx) {
                        return el('div', { key: idx, style: { border: '1px solid #e5e7eb', borderRadius: '8px', padding: '10px', marginBottom: '10px' } },
                            el('div', { style: { display: 'flex', gap: '6px', alignItems: 'center', marginBottom: '8px' } },
                                el('span', { style: { fontWeight: '700', fontSize: '12px', color: getStatusColor(it.status, attr), minWidth: '16px' } }, getStatusIcon(it.status, attr.iconSet)),
                                el(SelectControl, {
                                    value: it.status,
                                    options: [
                                        { label: '✓ Included', value: 'included' },
                                        { label: '✗ Not included', value: 'not-included' },
                                        { label: '◐ Partial', value: 'partial' },
                                        { label: '⏱ Coming soon', value: 'coming-soon' }
                                    ],
                                    onChange: function (v) { updateItem(idx, 'status', v); },
                                    style: { flex: 1, margin: 0 },
                                    __nextHasNoMarginBottom: true
                                }),
                                el(Button, { onClick: function () { moveItem(idx, -1); }, isSmall: true, variant: 'tertiary', disabled: idx === 0 }, '↑'),
                                el(Button, { onClick: function () { moveItem(idx, 1); }, isSmall: true, variant: 'tertiary', disabled: idx === items.length - 1 }, '↓'),
                                el(Button, { onClick: function () { removeItem(idx); }, isSmall: true, variant: 'link', isDestructive: true }, '✕')
                            ),
                            el(TextControl, { label: __('Label', 'blockenberg'), value: it.label, onChange: function (v) { updateItem(idx, 'label', v); }, __nextHasNoMarginBottom: true }),
                            el(TextControl, { label: __('Description (optional)', 'blockenberg'), value: it.description, onChange: function (v) { updateItem(idx, 'description', v); }, __nextHasNoMarginBottom: true }),
                            el(TextControl, { label: __('Badge (optional)', 'blockenberg'), value: it.badge, onChange: function (v) { updateItem(idx, 'badge', v); }, __nextHasNoMarginBottom: true }),
                            el(TextControl, { label: __('Group Header (leave blank to skip)', 'blockenberg'), value: it.group, onChange: function (v) { updateItem(idx, 'group', v); }, __nextHasNoMarginBottom: true })
                        );
                    }),
                    el(Button, { onClick: addItem, variant: 'primary', isSmall: true }, __('+ Add Feature', 'blockenberg'))
                ),
                el(PanelBody, { title: __('Spacing', 'blockenberg'), initialOpen: false },
                    el(RangeControl, { label: __('Padding Top (px)', 'blockenberg'), value: attr.paddingTop, min: 0, max: 240, onChange: function (v) { set({ paddingTop: v }); }, __nextHasNoMarginBottom: true }),
                    el(RangeControl, { label: __('Padding Bottom (px)', 'blockenberg'), value: attr.paddingBottom, min: 0, max: 240, onChange: function (v) { set({ paddingBottom: v }); }, __nextHasNoMarginBottom: true })
                ),
                
                el( PanelBody, { title: __( 'Typography', 'blockenberg' ), initialOpen: false },
                    _tc() && el(_tc(), {
                        label: __('Heading Typography', 'blockenberg'),
                        value: attr.typoHeading,
                        onChange: function (v) { set({ typoHeading: v }); }
                    }),
                    _tc() && el(_tc(), {
                        label: __('Label Typography', 'blockenberg'),
                        value: attr.typoLabel,
                        onChange: function (v) { set({ typoLabel: v }); }
                    }),
                    _tc() && el(_tc(), {
                        label: __('Description Typography', 'blockenberg'),
                        value: attr.typoDesc,
                        onChange: function (v) { set({ typoDesc: v }); }
                    })
                ),
el(PanelColorSettings, {
                    title: __('Colors', 'blockenberg'), initialOpen: false,
                    colorSettings: [
                        { value: attr.bgColor,          onChange: function (v) { set({ bgColor: v || '' }); },          label: __('Section BG', 'blockenberg') },
                        { value: attr.itemBg,           onChange: function (v) { set({ itemBg: v || '#ffffff' }); },     label: __('Row BG', 'blockenberg') },
                        { value: attr.stripeBg,         onChange: function (v) { set({ stripeBg: v || '#f9fafb' }); },  label: __('Stripe BG', 'blockenberg') },
                        { value: attr.borderColor,      onChange: function (v) { set({ borderColor: v || '#e5e7eb' }); }, label: __('Border', 'blockenberg') },
                        { value: attr.labelColor,       onChange: function (v) { set({ labelColor: v || '#111827' }); }, label: __('Label', 'blockenberg') },
                        { value: attr.descColor,        onChange: function (v) { set({ descColor: v || '#6b7280' }); },  label: __('Description', 'blockenberg') },
                        { value: attr.headingColor,     onChange: function (v) { set({ headingColor: v || '#111827' }); }, label: __('Heading', 'blockenberg') },
                        { value: attr.includedColor,    onChange: function (v) { set({ includedColor: v || '#10b981' }); }, label: __('Included Icon', 'blockenberg') },
                        { value: attr.notIncludedColor, onChange: function (v) { set({ notIncludedColor: v || '#d1d5db' }); }, label: __('Not Included Icon', 'blockenberg') },
                        { value: attr.partialColor,     onChange: function (v) { set({ partialColor: v || '#f59e0b' }); }, label: __('Partial Icon', 'blockenberg') },
                        { value: attr.comingSoonColor,  onChange: function (v) { set({ comingSoonColor: v || '#8b5cf6' }); }, label: __('Coming Soon Icon', 'blockenberg') },
                        { value: attr.badgeBg,          onChange: function (v) { set({ badgeBg: v || '#ede9fe' }); },    label: __('Badge BG', 'blockenberg') },
                        { value: attr.badgeColor,       onChange: function (v) { set({ badgeColor: v || '#7c3aed' }); }, label: __('Badge Text', 'blockenberg') }
                    ]
                })
            );

            /* Editor preview */
            var wrapStyle = Object.assign({
                background: attr.bgColor || 'transparent',
                paddingTop: attr.paddingTop + 'px',
                paddingBottom: attr.paddingBottom + 'px'
            }, _tv()(attr.typoHeading, '--bkbg-fl-hd-'), _tv()(attr.typoLabel, '--bkbg-fl-lb-'), _tv()(attr.typoDesc, '--bkbg-fl-ds-'));

            var listWrapStyle = {
                borderRadius: attr.listStyle === 'card' ? attr.borderRadius + 'px' : 0,
                border: attr.listStyle === 'bordered' || attr.listStyle === 'card' ? ('1px solid ' + attr.borderColor) : 'none',
                overflow: 'hidden'
            };

            var prevGroup = '';
            var rowEls = [];
            items.forEach(function (it, idx) {
                if (!attr.showNotIncluded && it.status === 'not-included') return;

                /* Group header */
                if (it.group && it.group !== prevGroup) {
                    rowEls.push(el('div', { key: 'g-' + idx, style: { padding: '8px 16px 4px', fontSize: '11px', fontWeight: '700', textTransform: 'uppercase', letterSpacing: '0.08em', color: attr.descColor, background: attr.stripeBg, borderBottom: '1px solid ' + attr.borderColor } }, it.group));
                    prevGroup = it.group;
                }

                var isBordered = attr.listStyle === 'bordered' || attr.listStyle === 'card';
                var isStriped = attr.listStyle === 'striped';
                var rowBg = isStriped && idx % 2 === 1 ? attr.stripeBg : attr.itemBg;
                var iconColor = getStatusColor(it.status, attr);
                var icon = getStatusIcon(it.status, attr.iconSet);
                var isNotIncl = it.status === 'not-included';

                rowEls.push(el('div', {
                    key: idx,
                    style: {
                        display: 'flex', alignItems: 'center', gap: '12px', padding: '12px 16px',
                        background: rowBg, borderBottom: isBordered && idx < items.length - 1 ? ('1px solid ' + attr.borderColor) : 'none',
                        marginBottom: attr.listStyle === 'card' ? '0' : (attr.gap > 0 ? attr.gap + 'px' : '0')
                    }
                },
                    el('span', { style: { color: iconColor, fontSize: attr.iconSize + 'px', flexShrink: 0, width: attr.iconSize + 'px', textAlign: 'center', fontWeight: '700' } }, icon),
                    el('div', { style: { flex: 1 } },
                        el('div', { className: 'bkbg-fl-label', style: { color: isNotIncl ? attr.notIncludedColor : attr.labelColor, textDecoration: isNotIncl && attr.strikeNotIncluded ? 'line-through' : 'none' } }, it.label),
                        attr.showDesc && it.description && el('div', { className: 'bkbg-fl-desc', style: { color: attr.descColor } }, it.description)
                    ),
                    attr.showBadge && it.badge && el('span', { style: { background: attr.badgeBg, color: attr.badgeColor, fontSize: '11px', fontWeight: '700', padding: '3px 8px', borderRadius: '20px', flexShrink: 0 } }, it.badge)
                ));
            });

            return el(Fragment, {},
                inspector,
                el('div', useBlockProps({ className: 'bkbg-fl-wrap', style: wrapStyle }),
                    attr.showHeading && el(RichText, {
                        tagName: 'h3', className: 'bkbg-fl-heading', value: attr.heading,
                        onChange: function (v) { set({ heading: v }); },
                        placeholder: __('Heading…', 'blockenberg'),
                        style: { color: attr.headingColor, marginBottom: '20px' }
                    }),
                    el('div', { style: listWrapStyle }, rowEls)
                )
            );
        },

        save: function (props) {
            var el2 = wp.element.createElement;
            var ubp = wp.blockEditor.useBlockProps;
            return el2('div', ubp.save(),
                el2('div', { className: 'bkbg-fl-app', 'data-opts': JSON.stringify(props.attributes) })
            );
        }
    });
}() );

```
