| 1 |
( function () { |
| 2 |
const el = window.wp.element.createElement; |
| 3 |
const { registerBlockType } = window.wp.blocks; |
| 4 |
const { InspectorControls, useBlockProps, PanelColorSettings } = window.wp.blockEditor; |
| 5 |
const { PanelBody, RangeControl, ToggleControl, TextControl, Button, SelectControl } = window.wp.components; |
| 6 |
const { __ } = window.wp.i18n; |
| 7 |
|
| 8 |
/* ── Typography lazy getters ──────────────────────────────── */ |
| 9 |
var _ttTC, _ttTV; |
| 10 |
function _tc() { return _ttTC || (_ttTC = window.bkbgTypographyControl); } |
| 11 |
function _tv() { return _ttTV || (_ttTV = window.bkbgTypoCssVars); } var IP = function () { return window.bkbgIconPicker; }; |
| 12 |
// ── Card renderer ───────────────────────────────────────────────────────── |
| 13 |
function HighlightCard( props ) { |
| 14 |
const { item, a } = props; |
| 15 |
const accentClr = item.accentColor || '#6366f1'; |
| 16 |
|
| 17 |
const cardStyle = { |
| 18 |
background: a.cardBg, |
| 19 |
borderRadius: a.borderRadius + 'px', |
| 20 |
padding: a.cardPadding + 'px', |
| 21 |
boxSizing: 'border-box', |
| 22 |
display: 'flex', |
| 23 |
flexDirection: 'column', |
| 24 |
gap: 10, |
| 25 |
overflow: 'hidden', |
| 26 |
position: 'relative', |
| 27 |
}; |
| 28 |
|
| 29 |
return el( 'div', { className: 'bkbg-hr-card', style: cardStyle }, |
| 30 |
// Accent bar at top |
| 31 |
a.accentLine && el( 'div', { className: 'bkbg-hr-accent', style: { position: 'absolute', top: 0, left: 0, right: 0, height: a.accentHeight + 'px', background: accentClr } } ), |
| 32 |
|
| 33 |
// Icon |
| 34 |
a.showIcon && item.icon && el( 'div', { className: 'bkbg-hr-icon', style: { fontSize: a.iconSize + 'px', lineHeight: 1, paddingTop: a.accentLine ? ( a.accentHeight + 4 ) + 'px' : 0 } }, (item.iconType || 'custom-char') !== 'custom-char' && IP() ? IP().buildSaveIcon(el, item.iconType, item.icon, item.iconDashicon, item.iconImageUrl, item.iconDashiconColor) : item.icon ), |
| 35 |
|
| 36 |
// Tag |
| 37 |
a.showTag && item.tag && el( 'span', { className: 'bkbg-hr-tag', style: { color: a.tagColor, background: a.tagBg, borderRadius: 99 + 'px', padding: '3px 10px', alignSelf: 'flex-start' } }, item.tag ), |
| 38 |
|
| 39 |
// Title |
| 40 |
el( 'div', { className: 'bkbg-hr-item-title', style: { color: a.headingColor } }, item.title ), |
| 41 |
|
| 42 |
// Description |
| 43 |
a.showDesc && item.description && el( 'p', { className: 'bkbg-hr-desc', style: { color: a.textColor, margin: 0 } }, item.description ), |
| 44 |
); |
| 45 |
} |
| 46 |
|
| 47 |
function renderReel( a ) { |
| 48 |
const items = a.items || []; |
| 49 |
return el( 'div', { className: 'bkbg-hr-grid', style: { display: 'grid', gridTemplateColumns: `repeat(${ a.columns }, 1fr)`, gap: 20 } }, |
| 50 |
items.map( ( item, i ) => el( HighlightCard, { key: i, item, a } ) ) |
| 51 |
); |
| 52 |
} |
| 53 |
|
| 54 |
function updItem( setAttributes, items, ii, field, val ) { |
| 55 |
setAttributes( { items: items.map( ( m, i ) => i === ii ? { ...m, [field]: val } : m ) } ); |
| 56 |
} |
| 57 |
|
| 58 |
// ── Block ───────────────────────────────────────────────────────────────── |
| 59 |
registerBlockType( 'blockenberg/highlight-reel', { |
| 60 |
edit: function ( props ) { |
| 61 |
const { attributes: a, setAttributes } = props; |
| 62 |
const blockProps = useBlockProps( { className: 'bkbg-hr-wrap', style: Object.assign( { background: a.bgColor, boxSizing: 'border-box' }, _tv() && _tv()( a.typoHeading, '--bkbg-hr-hl-' ), _tv() && _tv()( a.typoCardTitle, '--bkbg-hr-ct-' ), _tv() && _tv()( a.typoBody, '--bkbg-hr-bd-' ) ) } ); |
| 63 |
|
| 64 |
return el( 'div', blockProps, |
| 65 |
el( InspectorControls, {}, |
| 66 |
|
| 67 |
el( PanelBody, { title: __( 'Settings', 'blockenberg' ), initialOpen: true }, |
| 68 |
el( TextControl, { label: __( 'Title', 'blockenberg' ), value: a.title, onChange: v => setAttributes( { title: v } ) } ), |
| 69 |
el( ToggleControl, { label: __( 'Show Title', 'blockenberg' ), checked: a.showTitle, onChange: v => setAttributes( { showTitle: v } ) } ), |
| 70 |
el( TextControl, { label: __( 'Subtitle', 'blockenberg' ), value: a.subtitle, onChange: v => setAttributes( { subtitle: v } ) } ), |
| 71 |
el( ToggleControl, { label: __( 'Show Subtitle', 'blockenberg' ), checked: a.showSubtitle, onChange: v => setAttributes( { showSubtitle: v } ) } ), |
| 72 |
el( RangeControl, { label: __( 'Columns', 'blockenberg' ), value: a.columns, onChange: v => setAttributes( { columns: v } ), min: 1, max: 6 } ), |
| 73 |
el( ToggleControl, { label: __( 'Show Icons', 'blockenberg' ), checked: a.showIcon, onChange: v => setAttributes( { showIcon: v } ) } ), |
| 74 |
el( ToggleControl, { label: __( 'Show Tags', 'blockenberg' ), checked: a.showTag, onChange: v => setAttributes( { showTag: v } ) } ), |
| 75 |
el( ToggleControl, { label: __( 'Show Descriptions', 'blockenberg' ),checked: a.showDesc, onChange: v => setAttributes( { showDesc: v } ) } ), |
| 76 |
el( ToggleControl, { label: __( 'Accent Line', 'blockenberg' ), checked: a.accentLine, onChange: v => setAttributes( { accentLine: v } ) } ), |
| 77 |
), |
| 78 |
|
| 79 |
el( PanelBody, { title: __( 'Style', 'blockenberg' ), initialOpen: false }, |
| 80 |
el( RangeControl, { label: __( 'Icon Size', 'blockenberg' ), value: a.iconSize, onChange: v => setAttributes( { iconSize: v } ), min: 18, max: 72 } ), |
| 81 |
el( RangeControl, { label: __( 'Border Radius', 'blockenberg' ), value: a.borderRadius, onChange: v => setAttributes( { borderRadius: v } ), min: 0, max: 32 } ), |
| 82 |
el( RangeControl, { label: __( 'Card Padding', 'blockenberg' ), value: a.cardPadding, onChange: v => setAttributes( { cardPadding: v } ), min: 8, max: 48 } ), |
| 83 |
a.accentLine && el( RangeControl, { label: __( 'Accent Height', 'blockenberg' ), value: a.accentHeight, onChange: v => setAttributes( { accentHeight: v } ), min: 2, max: 12 } ), |
| 84 |
), |
| 85 |
|
| 86 |
el( PanelBody, { title: __( 'Typography', 'blockenberg' ), initialOpen: false }, |
| 87 |
_tc() && _tc()({ label: __('Section Heading','blockenberg'), value: a.typoHeading, onChange: v => setAttributes({ typoHeading: v }) }), |
| 88 |
_tc() && _tc()({ label: __('Card Title','blockenberg'), value: a.typoCardTitle, onChange: v => setAttributes({ typoCardTitle: v }) }), |
| 89 |
_tc() && _tc()({ label: __('Body / Subtitle','blockenberg'), value: a.typoBody, onChange: v => setAttributes({ typoBody: v }) }) |
| 90 |
), |
| 91 |
el( PanelColorSettings, { |
| 92 |
title: __( 'Colors', 'blockenberg' ), initialOpen: false, |
| 93 |
colorSettings: [ |
| 94 |
{ label: __( 'Wrapper BG', 'blockenberg' ), value: a.bgColor, onChange: v => setAttributes( { bgColor: v || '#ffffff' } ) }, |
| 95 |
{ label: __( 'Card BG', 'blockenberg' ), value: a.cardBg, onChange: v => setAttributes( { cardBg: v || '#f9fafb' } ) }, |
| 96 |
{ label: __( 'Heading', 'blockenberg' ), value: a.headingColor, onChange: v => setAttributes( { headingColor: v || '#111827' } ) }, |
| 97 |
{ label: __( 'Subtitle', 'blockenberg' ), value: a.subtitleColor,onChange: v => setAttributes( { subtitleColor:v || '#6b7280' } ) }, |
| 98 |
{ label: __( 'Body Text', 'blockenberg' ), value: a.textColor, onChange: v => setAttributes( { textColor: v || '#374151' } ) }, |
| 99 |
{ label: __( 'Tag BG', 'blockenberg' ), value: a.tagBg, onChange: v => setAttributes( { tagBg: v || '#ede9fe' } ) }, |
| 100 |
{ label: __( 'Tag Text', 'blockenberg' ), value: a.tagColor, onChange: v => setAttributes( { tagColor: v || '#6d28d9' } ) }, |
| 101 |
] |
| 102 |
} ), |
| 103 |
|
| 104 |
el( PanelBody, { title: __( 'Items', 'blockenberg' ), initialOpen: false }, |
| 105 |
el( Button, { variant: 'secondary', style: { marginBottom: 10 }, onClick: () => setAttributes( { items: [ ...( a.items || [] ), { icon: '⭐', iconType: 'custom-char', iconDashicon: '', iconImageUrl: '', accentColor: '#6366f1', tag: 'Highlight', title: 'New Highlight', description: 'Describe this moment or achievement.' } ] } ) }, __( '+ Add Item', 'blockenberg' ) ), |
| 106 |
a.items.map( ( item, ii ) => |
| 107 |
el( PanelBody, { key: ii, title: item.title.length > 28 ? item.title.slice( 0, 26 ) + '…' : item.title, initialOpen: false }, |
| 108 |
IP() ? el( IP().IconPickerControl, { |
| 109 |
iconType: item.iconType || 'custom-char', |
| 110 |
customChar: item.icon || '', |
| 111 |
dashicon: item.iconDashicon || '', |
| 112 |
imageUrl: item.iconImageUrl || '', |
| 113 |
onChangeType: v => updItem( setAttributes, a.items, ii, 'iconType', v ), |
| 114 |
onChangeChar: v => updItem( setAttributes, a.items, ii, 'icon', v ), |
| 115 |
onChangeDashicon: v => updItem( setAttributes, a.items, ii, 'iconDashicon', v ), |
| 116 |
onChangeImageUrl: v => updItem( setAttributes, a.items, ii, 'iconImageUrl', v ), |
| 117 |
label: __( 'Icon', 'blockenberg' ) |
| 118 |
} ) : el( TextControl, { label: __( 'Icon (emoji or text)', 'blockenberg' ), value: item.icon, onChange: v => updItem( setAttributes, a.items, ii, 'icon', v ) } ), |
| 119 |
el( TextControl, { label: __( 'Tag', 'blockenberg' ), value: item.tag, onChange: v => updItem( setAttributes, a.items, ii, 'tag', v ) } ), |
| 120 |
el( TextControl, { label: __( 'Title', 'blockenberg' ), value: item.title, onChange: v => updItem( setAttributes, a.items, ii, 'title', v ) } ), |
| 121 |
el( 'div', { style: { marginBottom: 8 } }, |
| 122 |
el( 'label', { style: { fontSize: 12, fontWeight: 600, display: 'block', marginBottom: 4 } }, __( 'Description', 'blockenberg' ) ), |
| 123 |
el( 'textarea', { value: item.description, rows: 3, onChange: e => updItem( setAttributes, a.items, ii, 'description', e.target.value ), style: { width: '100%', fontSize: 12, padding: '6px 8px', borderRadius: 4, border: '1px solid #d1d5db', resize: 'vertical', boxSizing: 'border-box' } } ), |
| 124 |
), |
| 125 |
el( 'div', { style: { display: 'flex', alignItems: 'center', gap: 8, marginBottom: 8 } }, |
| 126 |
el( 'label', { style: { fontSize: 12 } }, __( 'Accent Color', 'blockenberg' ) ), |
| 127 |
el( 'input', { type: 'color', value: item.accentColor || '#6366f1', style: { width: 40, height: 28, border: 'none', borderRadius: 4, cursor: 'pointer' }, onChange: e => updItem( setAttributes, a.items, ii, 'accentColor', e.target.value ) } ), |
| 128 |
), |
| 129 |
el( Button, { isDestructive: true, isSmall: true, onClick: () => setAttributes( { items: a.items.filter( ( _, x ) => x !== ii ) } ) }, __( 'Remove', 'blockenberg' ) ), |
| 130 |
) |
| 131 |
), |
| 132 |
), |
| 133 |
), |
| 134 |
|
| 135 |
// Header |
| 136 |
el( 'div', { className: 'bkbg-hr-header', style: { marginBottom: 28 } }, |
| 137 |
a.showTitle && a.title && el( 'h2', { className: 'bkbg-hr-title', style: { color: a.headingColor, margin: '0 0 6px' } }, a.title ), |
| 138 |
a.showSubtitle && a.subtitle && el( 'p', { className: 'bkbg-hr-subtitle', style: { color: a.subtitleColor, margin: 0 } }, a.subtitle ), |
| 139 |
), |
| 140 |
|
| 141 |
renderReel( a ), |
| 142 |
); |
| 143 |
}, |
| 144 |
|
| 145 |
save: function ( { attributes: a } ) { |
| 146 |
const blockProps = useBlockProps.save( { className: 'bkbg-hr-wrap', style: Object.assign( { background: a.bgColor, boxSizing: 'border-box' }, _tv() && _tv()( a.typoHeading, '--bkbg-hr-hl-' ), _tv() && _tv()( a.typoCardTitle, '--bkbg-hr-ct-' ), _tv() && _tv()( a.typoBody, '--bkbg-hr-bd-' ) ) } ); |
| 147 |
return el( 'div', blockProps, |
| 148 |
el( 'div', { className: 'bkbg-hr-header', style: { marginBottom: 28 } }, |
| 149 |
a.showTitle && a.title ? el( 'h2', { className: 'bkbg-hr-title', style: { color: a.headingColor, margin: '0 0 6px' } }, a.title ) : null, |
| 150 |
a.showSubtitle && a.subtitle ? el( 'p', { className: 'bkbg-hr-subtitle', style: { color: a.subtitleColor, margin: 0 } }, a.subtitle ) : null, |
| 151 |
), |
| 152 |
renderReel( a ), |
| 153 |
); |
| 154 |
}, |
| 155 |
} ); |
| 156 |
}() ); |
| 157 |
|