| 1 |
( function () { |
| 2 |
const el = window.wp.element.createElement; |
| 3 |
const { useState } = window.wp.element; |
| 4 |
const { registerBlockType } = window.wp.blocks; |
| 5 |
const { InspectorControls, useBlockProps, PanelColorSettings } = window.wp.blockEditor; |
| 6 |
const { PanelBody, RangeControl, ToggleControl, TextControl, TextareaControl, SelectControl, Button, ColorPicker, Popover } = window.wp.components; |
| 7 |
const { __ } = window.wp.i18n; |
| 8 |
|
| 9 |
var _tc, _tv; |
| 10 |
function getTypoControl() { return _tc || (_tc = window.bkbgTypographyControl); } |
| 11 |
function getTypoCssVars() { return _tv || (_tv = window.bkbgTypoCssVars); } |
| 12 |
var IP = function () { return window.bkbgIconPicker; }; |
| 13 |
|
| 14 |
// ── Render one notification row ─────────────────────────────────────────── |
| 15 |
function renderItem( notif, idx, a, isLast ) { |
| 16 |
const isCard = a.layout === 'card'; |
| 17 |
const itemStyle = { |
| 18 |
display: 'flex', |
| 19 |
alignItems: 'flex-start', |
| 20 |
gap: '14px', |
| 21 |
padding: a.itemPad + 'px', |
| 22 |
borderBottom: ( !isLast && a.showDividers && !isCard ) ? `1px solid ${ a.dividerColor }` : 'none', |
| 23 |
background: notif.unread ? ( isCard ? 'rgba(79,70,229,0.03)' : 'transparent' ) : 'transparent', |
| 24 |
borderRadius: isCard ? '10px' : 0, |
| 25 |
border: isCard ? `1px solid ${ a.cardBorder }` : null, |
| 26 |
position: 'relative', |
| 27 |
}; |
| 28 |
|
| 29 |
return el( 'div', { key: idx, className: 'bkbg-nf-item' + ( notif.unread ? ' bkbg-nf-unread' : '' ), style: itemStyle }, |
| 30 |
|
| 31 |
// Unread indicator dot |
| 32 |
notif.unread && el( 'span', { |
| 33 |
className: 'bkbg-nf-dot', |
| 34 |
style: { background: a.unreadDotColor, position: 'absolute', top: a.itemPad + 4 + 'px', right: a.itemPad + 'px', width: '8px', height: '8px', borderRadius: '50%', display: 'block' } |
| 35 |
} ), |
| 36 |
|
| 37 |
// Icon badge |
| 38 |
el( 'div', { |
| 39 |
className: 'bkbg-nf-icon', |
| 40 |
style: { |
| 41 |
width: a.iconSize + 'px', |
| 42 |
height: a.iconSize + 'px', |
| 43 |
borderRadius: '10px', |
| 44 |
background: notif.iconBg || '#ede9fe', |
| 45 |
display: 'flex', |
| 46 |
alignItems: 'center', |
| 47 |
justifyContent: 'center', |
| 48 |
fontSize: Math.round( a.iconSize * 0.52 ) + 'px', |
| 49 |
flexShrink: 0, |
| 50 |
} |
| 51 |
}, (function () { |
| 52 |
var _nt = notif.iconType || 'custom-char'; |
| 53 |
if (_nt !== 'custom-char' && IP()) { |
| 54 |
var _n = IP().buildSaveIcon(_nt, notif.icon, notif.iconDashicon, notif.iconImageUrl, notif.iconDashiconColor); |
| 55 |
if (_n) return _n; |
| 56 |
} |
| 57 |
return notif.icon || '🔔'; |
| 58 |
})() ), |
| 59 |
|
| 60 |
// Content |
| 61 |
el( 'div', { className: 'bkbg-nf-content', style: { flex: 1, minWidth: 0 } }, |
| 62 |
el( 'div', { className: 'bkbg-nf-row1', style: { display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', gap: '8px', flexWrap: 'wrap' } }, |
| 63 |
el( 'span', { |
| 64 |
className: 'bkbg-nf-title', |
| 65 |
style: { color: a.titleColor } |
| 66 |
}, notif.title || '' ), |
| 67 |
a.showTimestamp && notif.time && el( 'span', { |
| 68 |
className: 'bkbg-nf-time', |
| 69 |
style: { fontSize: a.timeFontSize + 'px', color: a.timeColor, flexShrink: 0, whiteSpace: 'nowrap' } |
| 70 |
}, notif.time ), |
| 71 |
), |
| 72 |
notif.body && el( 'p', { |
| 73 |
className: 'bkbg-nf-body', |
| 74 |
style: { color: a.bodyColor, margin: '4px 0 0' } |
| 75 |
}, notif.body ), |
| 76 |
), |
| 77 |
); |
| 78 |
} |
| 79 |
|
| 80 |
// ── Render full feed ────────────────────────────────────────────────────── |
| 81 |
function renderFeed( a ) { |
| 82 |
const items = a.notifications || []; |
| 83 |
const wrap = { |
| 84 |
background: a.bgColor, |
| 85 |
maxWidth: a.maxWidth + 'px', |
| 86 |
borderRadius: a.layout === 'card' ? 0 : '12px', |
| 87 |
overflow: 'hidden', |
| 88 |
border: a.layout === 'card' ? 'none' : `1px solid ${ a.cardBorder }`, |
| 89 |
}; |
| 90 |
|
| 91 |
return el( 'div', { className: 'bkbg-nf-list', style: wrap }, |
| 92 |
a.layout === 'card' |
| 93 |
? el( 'div', { style: { display: 'flex', flexDirection: 'column', gap: a.itemPad + 'px' } }, |
| 94 |
items.map( ( notif, i ) => renderItem( notif, i, a, i === items.length - 1 ) ) |
| 95 |
) |
| 96 |
: items.map( ( notif, i ) => renderItem( notif, i, a, i === items.length - 1 ) ) |
| 97 |
); |
| 98 |
} |
| 99 |
|
| 100 |
// ── Helpers ─────────────────────────────────────────────────────────────── |
| 101 |
function updNotif( setAttributes, notifications, idx, field, val ) { |
| 102 |
setAttributes( { notifications: notifications.map( ( n, i ) => i === idx ? { ...n, [field]: val } : n ) } ); |
| 103 |
} |
| 104 |
|
| 105 |
// ── Block ───────────────────────────────────────────────────────────────── |
| 106 |
var BkbgColorSwatch = function (props) { |
| 107 |
var _st = useState(false); var isOpen = _st[0]; var setOpen = _st[1]; |
| 108 |
return el('div', {}, |
| 109 |
el('div', { style: { display: 'flex', alignItems: 'center', gap: '8px', marginBottom: '8px' } }, |
| 110 |
el('span', { style: { fontSize: '11px', fontWeight: 500, textTransform: 'uppercase', color: '#1e1e1e' } }, props.label), |
| 111 |
el('button', { |
| 112 |
type: 'button', |
| 113 |
onClick: function () { setOpen(!isOpen); }, |
| 114 |
style: { width: '28px', height: '28px', borderRadius: '4px', border: '1px solid #ccc', background: props.value || '#ffffff', cursor: 'pointer', padding: 0 } |
| 115 |
}) |
| 116 |
), |
| 117 |
isOpen && el(Popover, { onClose: function () { setOpen(false); } }, |
| 118 |
el('div', { style: { padding: '8px' } }, |
| 119 |
el(ColorPicker, { color: props.value, onChangeComplete: function (c) { props.onChange(c.hex); }, enableAlpha: true }) |
| 120 |
) |
| 121 |
) |
| 122 |
); |
| 123 |
}; |
| 124 |
|
| 125 |
registerBlockType( 'blockenberg/notification-feed', { |
| 126 |
edit: function ( props ) { |
| 127 |
const { attributes: a, setAttributes } = props; |
| 128 |
const blockProps = useBlockProps( (function () { |
| 129 |
var _tvf = getTypoCssVars(); |
| 130 |
var s = {}; |
| 131 |
if (_tvf) { Object.assign(s, _tvf(a.titleTypo, '--bkbg-nf-tt-')); Object.assign(s, _tvf(a.bodyTypo, '--bkbg-nf-bd-')); } |
| 132 |
return { className: 'bkbg-notification-feed-wrap', style: s }; |
| 133 |
})() ); |
| 134 |
|
| 135 |
const notifs = a.notifications || []; |
| 136 |
|
| 137 |
return el( 'div', blockProps, |
| 138 |
el( InspectorControls, {}, |
| 139 |
|
| 140 |
el( PanelBody, { title: __( 'Feed Settings', 'blockenberg' ), initialOpen: true }, |
| 141 |
el( TextControl, { label: __( 'Title', 'blockenberg' ), value: a.title, onChange: v => setAttributes( { title: v } ) } ), |
| 142 |
el( ToggleControl, { label: __( 'Show Title', 'blockenberg' ), checked: a.showTitle, onChange: v => setAttributes( { showTitle: v } ) } ), |
| 143 |
el( ToggleControl, { label: __( 'Show Timestamps', 'blockenberg' ), checked: a.showTimestamp, onChange: v => setAttributes( { showTimestamp: v } ) } ), |
| 144 |
el( ToggleControl, { label: __( 'Show Dividers', 'blockenberg' ), checked: a.showDividers, onChange: v => setAttributes( { showDividers: v } ) } ), |
| 145 |
el( ToggleControl, { label: __( 'Bold Unread Titles', 'blockenberg' ), checked: a.markUnreadBold, onChange: v => setAttributes( { markUnreadBold: v } ) } ), |
| 146 |
el( SelectControl, { |
| 147 |
label: __( 'Layout', 'blockenberg' ), |
| 148 |
value: a.layout, |
| 149 |
options: [ { label: __( 'Cards (spaced)', 'blockenberg' ), value: 'card' }, { label: __( 'List (bordered box)', 'blockenberg' ), value: 'list' } ], |
| 150 |
onChange: v => setAttributes( { layout: v } ), |
| 151 |
} ), |
| 152 |
el( RangeControl, { label: __( 'Max Width (px)', 'blockenberg' ), value: a.maxWidth, onChange: v => setAttributes( { maxWidth: v } ), min: 300, max: 1200, step: 10 } ), |
| 153 |
el( RangeControl, { label: __( 'Item Padding', 'blockenberg' ), value: a.itemPad, onChange: v => setAttributes( { itemPad: v } ), min: 8, max: 40 } ), |
| 154 |
el( RangeControl, { label: __( 'Icon Size', 'blockenberg' ), value: a.iconSize, onChange: v => setAttributes( { iconSize: v } ), min: 28, max: 72 } ), |
| 155 |
), |
| 156 |
|
| 157 |
el( PanelBody, { title: __( 'Typography', 'blockenberg' ), initialOpen: false }, |
| 158 |
getTypoControl() && el(getTypoControl(), { label: __('Title Typography', 'blockenberg'), value: a.titleTypo || {}, onChange: function (v) { return setAttributes( { titleTypo: v } ); } }), |
| 159 |
getTypoControl() && el(getTypoControl(), { label: __('Body Typography', 'blockenberg'), value: a.bodyTypo || {}, onChange: function (v) { return setAttributes( { bodyTypo: v } ); } }), |
| 160 |
el( RangeControl, { label: __( 'Time Font Size', 'blockenberg' ), value: a.timeFontSize, onChange: v => setAttributes( { timeFontSize: v } ), min: 9, max: 16, __nextHasNoMarginBottom: true } ) |
| 161 |
), |
| 162 |
el( PanelColorSettings, { |
| 163 |
title: __( 'Colors', 'blockenberg' ), initialOpen: false, |
| 164 |
colorSettings: [ |
| 165 |
{ label: __( 'Background', 'blockenberg' ), value: a.bgColor, onChange: v => setAttributes( { bgColor: v || '#ffffff' } ) }, |
| 166 |
{ label: __( 'Title Color', 'blockenberg' ), value: a.titleColor, onChange: v => setAttributes( { titleColor: v || '#111827' } ) }, |
| 167 |
{ label: __( 'Body Color', 'blockenberg' ), value: a.bodyColor, onChange: v => setAttributes( { bodyColor: v || '#4b5563' } ) }, |
| 168 |
{ label: __( 'Time Color', 'blockenberg' ), value: a.timeColor, onChange: v => setAttributes( { timeColor: v || '#9ca3af' } ) }, |
| 169 |
{ label: __( 'Divider Color', 'blockenberg' ), value: a.dividerColor, onChange: v => setAttributes( { dividerColor: v || '#f3f4f6' } ) }, |
| 170 |
{ label: __( 'Card Border', 'blockenberg' ), value: a.cardBorder, onChange: v => setAttributes( { cardBorder: v || '#e5e7eb' } ) }, |
| 171 |
{ label: __( 'Unread Dot', 'blockenberg' ), value: a.unreadDotColor, onChange: v => setAttributes( { unreadDotColor: v || '#4f46e5' } ) }, |
| 172 |
] |
| 173 |
} ), |
| 174 |
|
| 175 |
el( PanelBody, { title: __( 'Notifications', 'blockenberg' ), initialOpen: false }, |
| 176 |
el( Button, { |
| 177 |
variant: 'secondary', style: { marginBottom: 10 }, |
| 178 |
onClick: () => setAttributes( { notifications: [ ...notifs, { icon: '🔔', iconType: 'custom-char', iconDashicon: '', iconImageUrl: '', iconBg: '#ede9fe', title: 'New notification', body: '', time: 'Just now', unread: true } ] } ) |
| 179 |
}, __( '+ Add Notification', 'blockenberg' ) ), |
| 180 |
|
| 181 |
notifs.map( ( notif, i ) => |
| 182 |
el( PanelBody, { key: i, title: ( notif.unread ? '🔵 ' : '' ) + ( notif.title || `Notification ${ i + 1 }` ), initialOpen: false }, |
| 183 |
el(IP().IconPickerControl, { |
| 184 |
iconType: notif.iconType || 'custom-char', |
| 185 |
customChar: notif.icon || '', |
| 186 |
dashicon: notif.iconDashicon || '', |
| 187 |
imageUrl: notif.iconImageUrl || '', |
| 188 |
onChangeType: v => updNotif( setAttributes, notifs, i, 'iconType', v ), |
| 189 |
onChangeChar: v => updNotif( setAttributes, notifs, i, 'icon', v ), |
| 190 |
onChangeDashicon: v => updNotif( setAttributes, notifs, i, 'iconDashicon', v ), |
| 191 |
onChangeImageUrl: v => updNotif( setAttributes, notifs, i, 'iconImageUrl', v ), |
| 192 |
label: __( 'Icon', 'blockenberg' ) |
| 193 |
}), |
| 194 |
el( BkbgColorSwatch, { label: __( 'Icon BG (hex)', 'blockenberg' ), value: notif.iconBg, onChange: v => updNotif( setAttributes, notifs, i, 'iconBg', v ) } ), |
| 195 |
el( TextControl, { label: __( 'Title', 'blockenberg' ), value: notif.title, onChange: v => updNotif( setAttributes, notifs, i, 'title', v ) } ), |
| 196 |
el( TextareaControl, { label: __( 'Body Text', 'blockenberg' ), value: notif.body, onChange: v => updNotif( setAttributes, notifs, i, 'body', v ), rows: 2 } ), |
| 197 |
el( TextControl, { label: __( 'Timestamp', 'blockenberg' ), value: notif.time, onChange: v => updNotif( setAttributes, notifs, i, 'time', v ) } ), |
| 198 |
el( ToggleControl, { label: __( 'Mark as Unread', 'blockenberg' ), checked: !! notif.unread, onChange: v => updNotif( setAttributes, notifs, i, 'unread', v ) } ), |
| 199 |
el( Button, { isDestructive: true, isSmall: true, onClick: () => setAttributes( { notifications: notifs.filter( ( _, x ) => x !== i ) } ) }, __( 'Remove', 'blockenberg' ) ), |
| 200 |
) |
| 201 |
), |
| 202 |
), |
| 203 |
), |
| 204 |
|
| 205 |
a.showTitle && a.title && el( 'h3', { className: 'bkbg-nf-heading', style: { color: a.titleColor } }, a.title ), |
| 206 |
renderFeed( a ), |
| 207 |
); |
| 208 |
}, |
| 209 |
|
| 210 |
save: function ( { attributes: a } ) { |
| 211 |
var _tvf = getTypoCssVars(); |
| 212 |
var blockProps = useBlockProps.save( (function () { |
| 213 |
var s = {}; |
| 214 |
if (_tvf) { Object.assign(s, _tvf(a.titleTypo, '--bkbg-nf-tt-')); Object.assign(s, _tvf(a.bodyTypo, '--bkbg-nf-bd-')); } |
| 215 |
return { className: 'bkbg-notification-feed-wrap', style: s }; |
| 216 |
})() ); |
| 217 |
return el( 'div', blockProps, |
| 218 |
a.showTitle && a.title ? el( 'h3', { className: 'bkbg-nf-heading', style: { color: a.titleColor } }, a.title ) : null, |
| 219 |
renderFeed( a ), |
| 220 |
); |
| 221 |
}, |
| 222 |
|
| 223 |
deprecated: [{ |
| 224 |
attributes: { |
| 225 |
title: { type: 'string', default: 'Recent Activity' }, |
| 226 |
showTitle: { type: 'boolean', default: true }, |
| 227 |
layout: { type: 'string', default: 'card' }, |
| 228 |
showTimestamp: { type: 'boolean', default: true }, |
| 229 |
showDividers: { type: 'boolean', default: true }, |
| 230 |
markUnreadBold: { type: 'boolean', default: true }, |
| 231 |
maxWidth: { type: 'number', default: 600 }, |
| 232 |
itemPad: { type: 'number', default: 16 }, |
| 233 |
iconSize: { type: 'number', default: 42 }, |
| 234 |
titleFontSize: { type: 'number', default: 15 }, |
| 235 |
bodyFontSize: { type: 'number', default: 13 }, |
| 236 |
timeFontSize: { type: 'number', default: 11 }, |
| 237 |
fontWeight: { type: 'number', default: 600 }, |
| 238 |
bodyFontWeight: { type: 'number', default: 400 }, |
| 239 |
bgColor: { type: 'string', default: '#ffffff' }, |
| 240 |
titleColor: { type: 'string', default: '#111827' }, |
| 241 |
bodyColor: { type: 'string', default: '#4b5563' }, |
| 242 |
timeColor: { type: 'string', default: '#9ca3af' }, |
| 243 |
dividerColor: { type: 'string', default: '#f3f4f6' }, |
| 244 |
unreadDotColor: { type: 'string', default: '#4f46e5' }, |
| 245 |
cardBorder: { type: 'string', default: '#e5e7eb' }, |
| 246 |
notifications: { type: 'array', default: [] } |
| 247 |
}, |
| 248 |
save: function ( { attributes: a } ) { |
| 249 |
var blockProps = useBlockProps.save( { className: 'bkbg-notification-feed-wrap' } ); |
| 250 |
return el( 'div', blockProps, |
| 251 |
a.showTitle && a.title ? el( 'h3', { className: 'bkbg-nf-heading', style: { color: a.titleColor } }, a.title ) : null, |
| 252 |
renderFeed( a ), |
| 253 |
); |
| 254 |
} |
| 255 |
}], |
| 256 |
} ); |
| 257 |
}() ); |
| 258 |
|