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

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

258 lines 16.9 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 { 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