# blockenberg/2.0.7/blocks/notification-feed/index.js

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

- Page: https://pluginprobe.com/plugins/blockenberg/2.0.7/code/blocks/notification-feed/index.js
- Raw: https://pluginprobe.com/plugins/blockenberg/2.0.7/raw/blocks/notification-feed/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/notification-feed/index.js#L10-L20`.

```javascript
( function () {
    const el = window.wp.element.createElement;
    const { useState } = window.wp.element;
    const { registerBlockType } = window.wp.blocks;
    const { InspectorControls, useBlockProps, PanelColorSettings } = window.wp.blockEditor;
    const { PanelBody, RangeControl, ToggleControl, TextControl, TextareaControl, SelectControl, Button, ColorPicker, Popover } = window.wp.components;
    const { __ } = window.wp.i18n;

    var _tc, _tv;
    function getTypoControl() { return _tc || (_tc = window.bkbgTypographyControl); }
    function getTypoCssVars() { return _tv || (_tv = window.bkbgTypoCssVars); }
    var IP = function () { return window.bkbgIconPicker; };

    // ── Render one notification row ───────────────────────────────────────────
    function renderItem( notif, idx, a, isLast ) {
        const isCard    = a.layout === 'card';
        const itemStyle = {
            display:        'flex',
            alignItems:     'flex-start',
            gap:            '14px',
            padding:        a.itemPad + 'px',
            borderBottom:   ( !isLast && a.showDividers && !isCard ) ? `1px solid ${ a.dividerColor }` : 'none',
            background:     notif.unread ? ( isCard ? 'rgba(79,70,229,0.03)' : 'transparent' ) : 'transparent',
            borderRadius:   isCard ? '10px' : 0,
            border:         isCard ? `1px solid ${ a.cardBorder }` : null,
            position:       'relative',
        };

        return el( 'div', { key: idx, className: 'bkbg-nf-item' + ( notif.unread ? ' bkbg-nf-unread' : '' ), style: itemStyle },

            // Unread indicator dot
            notif.unread && el( 'span', {
                className: 'bkbg-nf-dot',
                style: { background: a.unreadDotColor, position: 'absolute', top: a.itemPad + 4 + 'px', right: a.itemPad + 'px', width: '8px', height: '8px', borderRadius: '50%', display: 'block' }
            } ),

            // Icon badge
            el( 'div', {
                className: 'bkbg-nf-icon',
                style: {
                    width:          a.iconSize + 'px',
                    height:         a.iconSize + 'px',
                    borderRadius:   '10px',
                    background:     notif.iconBg || '#ede9fe',
                    display:        'flex',
                    alignItems:     'center',
                    justifyContent: 'center',
                    fontSize:       Math.round( a.iconSize * 0.52 ) + 'px',
                    flexShrink:     0,
                }
            }, (function () {
                var _nt = notif.iconType || 'custom-char';
                if (_nt !== 'custom-char' && IP()) {
                    var _n = IP().buildSaveIcon(_nt, notif.icon, notif.iconDashicon, notif.iconImageUrl, notif.iconDashiconColor);
                    if (_n) return _n;
                }
                return notif.icon || '🔔';
            })() ),

            // Content
            el( 'div', { className: 'bkbg-nf-content', style: { flex: 1, minWidth: 0 } },
                el( 'div', { className: 'bkbg-nf-row1', style: { display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', gap: '8px', flexWrap: 'wrap' } },
                    el( 'span', {
                        className: 'bkbg-nf-title',
                        style: { color: a.titleColor }
                    }, notif.title || '' ),
                    a.showTimestamp && notif.time && el( 'span', {
                        className: 'bkbg-nf-time',
                        style: { fontSize: a.timeFontSize + 'px', color: a.timeColor, flexShrink: 0, whiteSpace: 'nowrap' }
                    }, notif.time ),
                ),
                notif.body && el( 'p', {
                    className: 'bkbg-nf-body',
                    style: { color: a.bodyColor, margin: '4px 0 0' }
                }, notif.body ),
            ),
        );
    }

    // ── Render full feed ──────────────────────────────────────────────────────
    function renderFeed( a ) {
        const items = a.notifications || [];
        const wrap  = {
            background:   a.bgColor,
            maxWidth:     a.maxWidth + 'px',
            borderRadius: a.layout === 'card' ? 0 : '12px',
            overflow:     'hidden',
            border:       a.layout === 'card' ? 'none' : `1px solid ${ a.cardBorder }`,
        };

        return el( 'div', { className: 'bkbg-nf-list', style: wrap },
            a.layout === 'card'
                ? el( 'div', { style: { display: 'flex', flexDirection: 'column', gap: a.itemPad + 'px' } },
                    items.map( ( notif, i ) => renderItem( notif, i, a, i === items.length - 1 ) )
                )
                : items.map( ( notif, i ) => renderItem( notif, i, a, i === items.length - 1 ) )
        );
    }

    // ── Helpers ───────────────────────────────────────────────────────────────
    function updNotif( setAttributes, notifications, idx, field, val ) {
        setAttributes( { notifications: notifications.map( ( n, i ) => i === idx ? { ...n, [field]: val } : n ) } );
    }

    // ── Block ─────────────────────────────────────────────────────────────────
    var BkbgColorSwatch = function (props) {
        var _st = useState(false); var isOpen = _st[0]; var setOpen = _st[1];
        return el('div', {},
            el('div', { style: { display: 'flex', alignItems: 'center', gap: '8px', marginBottom: '8px' } },
                el('span', { style: { fontSize: '11px', fontWeight: 500, textTransform: 'uppercase', color: '#1e1e1e' } }, props.label),
                el('button', {
                    type: 'button',
                    onClick: function () { setOpen(!isOpen); },
                    style: { width: '28px', height: '28px', borderRadius: '4px', border: '1px solid #ccc', background: props.value || '#ffffff', cursor: 'pointer', padding: 0 }
                })
            ),
            isOpen && el(Popover, { onClose: function () { setOpen(false); } },
                el('div', { style: { padding: '8px' } },
                    el(ColorPicker, { color: props.value, onChangeComplete: function (c) { props.onChange(c.hex); }, enableAlpha: true })
                )
            )
        );
    };

    registerBlockType( 'blockenberg/notification-feed', {
        edit: function ( props ) {
            const { attributes: a, setAttributes } = props;
            const blockProps = useBlockProps( (function () {
                var _tvf = getTypoCssVars();
                var s = {};
                if (_tvf) { Object.assign(s, _tvf(a.titleTypo, '--bkbg-nf-tt-')); Object.assign(s, _tvf(a.bodyTypo, '--bkbg-nf-bd-')); }
                return { className: 'bkbg-notification-feed-wrap', style: s };
            })() );

            const notifs = a.notifications || [];

            return el( 'div', blockProps,
                el( InspectorControls, {},

                    el( PanelBody, { title: __( 'Feed Settings', 'blockenberg' ), initialOpen: true },
                        el( TextControl, { label: __( 'Title', 'blockenberg' ), value: a.title, onChange: v => setAttributes( { title: v } ) } ),
                        el( ToggleControl, { label: __( 'Show Title', 'blockenberg' ),         checked: a.showTitle,      onChange: v => setAttributes( { showTitle: v } ) } ),
                        el( ToggleControl, { label: __( 'Show Timestamps', 'blockenberg' ),    checked: a.showTimestamp,  onChange: v => setAttributes( { showTimestamp: v } ) } ),
                        el( ToggleControl, { label: __( 'Show Dividers', 'blockenberg' ),      checked: a.showDividers,   onChange: v => setAttributes( { showDividers: v } ) } ),
                        el( ToggleControl, { label: __( 'Bold Unread Titles', 'blockenberg' ), checked: a.markUnreadBold, onChange: v => setAttributes( { markUnreadBold: v } ) } ),
                        el( SelectControl, {
                            label: __( 'Layout', 'blockenberg' ),
                            value: a.layout,
                            options: [ { label: __( 'Cards (spaced)', 'blockenberg' ), value: 'card' }, { label: __( 'List (bordered box)', 'blockenberg' ), value: 'list' } ],
                            onChange: v => setAttributes( { layout: v } ),
                        } ),
                        el( RangeControl, { label: __( 'Max Width (px)', 'blockenberg' ),  value: a.maxWidth,       onChange: v => setAttributes( { maxWidth: v } ),       min: 300, max: 1200, step: 10 } ),
                        el( RangeControl, { label: __( 'Item Padding', 'blockenberg' ),    value: a.itemPad,        onChange: v => setAttributes( { itemPad: v } ),        min: 8, max: 40 } ),
                        el( RangeControl, { label: __( 'Icon Size', 'blockenberg' ),       value: a.iconSize,       onChange: v => setAttributes( { iconSize: v } ),       min: 28, max: 72 } ),
                    ),

                    el( PanelBody, { title: __( 'Typography', 'blockenberg' ), initialOpen: false },
                        getTypoControl() && el(getTypoControl(), { label: __('Title Typography', 'blockenberg'), value: a.titleTypo || {}, onChange: function (v) { return setAttributes( { titleTypo: v } ); } }),
                        getTypoControl() && el(getTypoControl(), { label: __('Body Typography', 'blockenberg'), value: a.bodyTypo || {}, onChange: function (v) { return setAttributes( { bodyTypo: v } ); } }),
                        el( RangeControl, { label: __( 'Time Font Size', 'blockenberg' ),  value: a.timeFontSize,   onChange: v => setAttributes( { timeFontSize: v } ),   min: 9, max: 16, __nextHasNoMarginBottom: true } )
                    ),
el( PanelColorSettings, {
                        title: __( 'Colors', 'blockenberg' ), initialOpen: false,
                        colorSettings: [
                            { label: __( 'Background', 'blockenberg' ),      value: a.bgColor,      onChange: v => setAttributes( { bgColor:      v || '#ffffff' } ) },
                            { label: __( 'Title Color', 'blockenberg' ),     value: a.titleColor,   onChange: v => setAttributes( { titleColor:   v || '#111827' } ) },
                            { label: __( 'Body Color', 'blockenberg' ),      value: a.bodyColor,    onChange: v => setAttributes( { bodyColor:    v || '#4b5563' } ) },
                            { label: __( 'Time Color', 'blockenberg' ),      value: a.timeColor,    onChange: v => setAttributes( { timeColor:    v || '#9ca3af' } ) },
                            { label: __( 'Divider Color', 'blockenberg' ),   value: a.dividerColor, onChange: v => setAttributes( { dividerColor: v || '#f3f4f6' } ) },
                            { label: __( 'Card Border', 'blockenberg' ),     value: a.cardBorder,   onChange: v => setAttributes( { cardBorder:   v || '#e5e7eb' } ) },
                            { label: __( 'Unread Dot', 'blockenberg' ),      value: a.unreadDotColor, onChange: v => setAttributes( { unreadDotColor: v || '#4f46e5' } ) },
                        ]
                    } ),

                    el( PanelBody, { title: __( 'Notifications', 'blockenberg' ), initialOpen: false },
                        el( Button, {
                            variant: 'secondary', style: { marginBottom: 10 },
                            onClick: () => setAttributes( { notifications: [ ...notifs, { icon: '🔔', iconType: 'custom-char', iconDashicon: '', iconImageUrl: '', iconBg: '#ede9fe', title: 'New notification', body: '', time: 'Just now', unread: true } ] } )
                        }, __( '+ Add Notification', 'blockenberg' ) ),

                        notifs.map( ( notif, i ) =>
                            el( PanelBody, { key: i, title: ( notif.unread ? '🔵 ' : '' ) + ( notif.title || `Notification ${ i + 1 }` ), initialOpen: false },
                                el(IP().IconPickerControl, {
                                    iconType: notif.iconType || 'custom-char',
                                    customChar: notif.icon || '',
                                    dashicon: notif.iconDashicon || '',
                                    imageUrl: notif.iconImageUrl || '',
                                    onChangeType: v => updNotif( setAttributes, notifs, i, 'iconType', v ),
                                    onChangeChar: v => updNotif( setAttributes, notifs, i, 'icon', v ),
                                    onChangeDashicon: v => updNotif( setAttributes, notifs, i, 'iconDashicon', v ),
                                    onChangeImageUrl: v => updNotif( setAttributes, notifs, i, 'iconImageUrl', v ),
                                    label: __( 'Icon', 'blockenberg' )
                                }),
                                el( BkbgColorSwatch, { label: __( 'Icon BG (hex)', 'blockenberg' ), value: notif.iconBg, onChange: v => updNotif( setAttributes, notifs, i, 'iconBg', v ) } ),
                                el( TextControl, { label: __( 'Title', 'blockenberg' ),         value: notif.title,  onChange: v => updNotif( setAttributes, notifs, i, 'title',  v ) } ),
                                el( TextareaControl, { label: __( 'Body Text', 'blockenberg' ), value: notif.body,   onChange: v => updNotif( setAttributes, notifs, i, 'body',   v ), rows: 2 } ),
                                el( TextControl, { label: __( 'Timestamp', 'blockenberg' ),     value: notif.time,   onChange: v => updNotif( setAttributes, notifs, i, 'time',   v ) } ),
                                el( ToggleControl, { label: __( 'Mark as Unread', 'blockenberg' ), checked: !! notif.unread, onChange: v => updNotif( setAttributes, notifs, i, 'unread', v ) } ),
                                el( Button, { isDestructive: true, isSmall: true, onClick: () => setAttributes( { notifications: notifs.filter( ( _, x ) => x !== i ) } ) }, __( 'Remove', 'blockenberg' ) ),
                            )
                        ),
                    ),
                ),

                a.showTitle && a.title && el( 'h3', { className: 'bkbg-nf-heading', style: { color: a.titleColor } }, a.title ),
                renderFeed( a ),
            );
        },

        save: function ( { attributes: a } ) {
            var _tvf = getTypoCssVars();
            var blockProps = useBlockProps.save( (function () {
                var s = {};
                if (_tvf) { Object.assign(s, _tvf(a.titleTypo, '--bkbg-nf-tt-')); Object.assign(s, _tvf(a.bodyTypo, '--bkbg-nf-bd-')); }
                return { className: 'bkbg-notification-feed-wrap', style: s };
            })() );
            return el( 'div', blockProps,
                a.showTitle && a.title ? el( 'h3', { className: 'bkbg-nf-heading', style: { color: a.titleColor } }, a.title ) : null,
                renderFeed( a ),
            );
        },

        deprecated: [{
            attributes: {
                title:          { type: 'string',  default: 'Recent Activity' },
                showTitle:      { type: 'boolean', default: true },
                layout:         { type: 'string',  default: 'card' },
                showTimestamp:  { type: 'boolean', default: true },
                showDividers:   { type: 'boolean', default: true },
                markUnreadBold: { type: 'boolean', default: true },
                maxWidth:       { type: 'number',  default: 600 },
                itemPad:        { type: 'number',  default: 16 },
                iconSize:       { type: 'number',  default: 42 },
                titleFontSize:  { type: 'number',  default: 15 },
                bodyFontSize:   { type: 'number',  default: 13 },
                timeFontSize:   { type: 'number',  default: 11 },
                fontWeight:     { type: 'number',  default: 600 },
                bodyFontWeight: { type: 'number',  default: 400 },
                bgColor:        { type: 'string',  default: '#ffffff' },
                titleColor:     { type: 'string',  default: '#111827' },
                bodyColor:      { type: 'string',  default: '#4b5563' },
                timeColor:      { type: 'string',  default: '#9ca3af' },
                dividerColor:   { type: 'string',  default: '#f3f4f6' },
                unreadDotColor: { type: 'string',  default: '#4f46e5' },
                cardBorder:     { type: 'string',  default: '#e5e7eb' },
                notifications:  { type: 'array',   default: [] }
            },
            save: function ( { attributes: a } ) {
                var blockProps = useBlockProps.save( { className: 'bkbg-notification-feed-wrap' } );
                return el( 'div', blockProps,
                    a.showTitle && a.title ? el( 'h3', { className: 'bkbg-nf-heading', style: { color: a.titleColor } }, a.title ) : null,
                    renderFeed( a ),
                );
            }
        }],
    } );
}() );

```
