| 1 |
import { useState, useEffect, useCallback } from '@wordpress/element'; |
| 2 |
import apiFetch from '@wordpress/api-fetch'; |
| 3 |
import { __ } from '@wordpress/i18n'; |
| 4 |
|
| 5 |
const CUSTOM_ICONS = { |
| 6 |
progress_bar: ( |
| 7 |
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> |
| 8 |
<rect x="2" y="10" width="20" height="4" rx="2" fill="currentColor" fillOpacity="0.22"/> |
| 9 |
<rect x="2" y="10" width="12" height="4" rx="2" fill="currentColor"/> |
| 10 |
<circle cx="14" cy="12" r="3" fill="currentColor"/> |
| 11 |
</svg> |
| 12 |
), |
| 13 |
donation_form: ( |
| 14 |
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> |
| 15 |
<path d="M12 9c-.5-1.3-1.8-2.2-3.2-2.2C6.9 6.8 5.4 8.3 5.4 10.1c0 2.3 2.3 4.4 6.6 7.1 4.3-2.7 6.6-4.8 6.6-7.1 0-1.8-1.5-3.3-3.4-3.3C13.8 6.8 12.5 7.7 12 9z" fill="currentColor"/> |
| 16 |
<rect x="6" y="18.5" width="12" height="3" rx="1.5" stroke="currentColor" strokeWidth="1.4" fill="none"/> |
| 17 |
<line x1="9.5" y1="18.5" x2="9.5" y2="17" stroke="currentColor" strokeWidth="1.3" strokeLinecap="round"/> |
| 18 |
<line x1="12" y1="18.5" x2="12" y2="16.5" stroke="currentColor" strokeWidth="1.3" strokeLinecap="round"/> |
| 19 |
<line x1="14.5" y1="18.5" x2="14.5" y2="17" stroke="currentColor" strokeWidth="1.3" strokeLinecap="round"/> |
| 20 |
</svg> |
| 21 |
), |
| 22 |
}; |
| 23 |
import { useSortable } from '@dnd-kit/sortable'; |
| 24 |
import { CSS } from '@dnd-kit/utilities'; |
| 25 |
import { getElement } from '../fields/FieldRegistry'; |
| 26 |
import { openWpMedia } from '../utils/media'; |
| 27 |
import { getGlobalCurrencySymbol } from '../utils/currency'; |
| 28 |
|
| 29 |
// Mirror of RendererService::scale_inline_font_sizes() — bumps every explicit |
| 30 |
// px font-size by +2px so editor preview matches the 16px-base frontend. |
| 31 |
function scaleInlineFontSizes( html ) { |
| 32 |
if ( ! html ) return html; |
| 33 |
return html.replace( /\bfont-size\s*:\s*(\d+(?:\.\d+)?)px/gi, ( _, size ) => { |
| 34 |
return `font-size: ${ parseFloat( size ) + 2 }px`; |
| 35 |
} ); |
| 36 |
} |
| 37 |
|
| 38 |
/** |
| 39 |
* A single element card on the canvas — sortable via @dnd-kit/sortable. |
| 40 |
* Elements that have a live preview render their content directly; |
| 41 |
* all others fall back to the generic icon + label row. |
| 42 |
*/ |
| 43 |
export default function ElementCard( { element, columnId, index, totalElements, meta, campaignId, isSelected, dispatch, isDropTarget } ) { |
| 44 |
const def = getElement( element.type ); |
| 45 |
|
| 46 |
const { |
| 47 |
attributes, |
| 48 |
listeners, |
| 49 |
setNodeRef, |
| 50 |
transform, |
| 51 |
transition, |
| 52 |
isDragging, |
| 53 |
} = useSortable( { |
| 54 |
id: element.id, |
| 55 |
data: { source: 'canvas', columnId, element }, |
| 56 |
} ); |
| 57 |
|
| 58 |
const style = { |
| 59 |
transform: CSS.Transform.toString( transform ), |
| 60 |
transition, |
| 61 |
opacity: isDragging ? 0.4 : 1, |
| 62 |
}; |
| 63 |
|
| 64 |
const handleClick = ( e ) => { |
| 65 |
e.stopPropagation(); |
| 66 |
if ( isSelected ) { |
| 67 |
dispatch( { type: 'DESELECT_ELEMENT' } ); |
| 68 |
} else { |
| 69 |
dispatch( { type: 'SELECT_ELEMENT', elementId: element.id, columnId } ); |
| 70 |
} |
| 71 |
}; |
| 72 |
|
| 73 |
const handleEdit = ( e ) => { |
| 74 |
e.stopPropagation(); |
| 75 |
dispatch( { type: 'SELECT_ELEMENT', elementId: element.id, columnId } ); |
| 76 |
}; |
| 77 |
|
| 78 |
const handleDelete = ( e ) => { |
| 79 |
e.stopPropagation(); |
| 80 |
dispatch( { type: 'REMOVE_ELEMENT', columnId, elementId: element.id } ); |
| 81 |
}; |
| 82 |
|
| 83 |
const handleMoveUp = ( e ) => { |
| 84 |
e.stopPropagation(); |
| 85 |
dispatch( { type: 'REORDER_ELEMENTS', columnId, oldIndex: index, newIndex: index - 1 } ); |
| 86 |
}; |
| 87 |
|
| 88 |
const handleMoveDown = ( e ) => { |
| 89 |
e.stopPropagation(); |
| 90 |
dispatch( { type: 'REORDER_ELEMENTS', columnId, oldIndex: index, newIndex: index + 1 } ); |
| 91 |
}; |
| 92 |
|
| 93 |
const isFirst = index === 0; |
| 94 |
const isLast = index === totalElements - 1; |
| 95 |
|
| 96 |
const preview = getElementPreview( element, meta, dispatch, campaignId, columnId ); |
| 97 |
|
| 98 |
return ( |
| 99 |
<div |
| 100 |
ref={ setNodeRef } |
| 101 |
style={ style } |
| 102 |
className={ `bp-element-card${ isSelected ? ' is-selected' : '' }${ preview ? ' bp-element-card--preview' : '' }${ isDropTarget ? ' is-drop-target' : '' }` } |
| 103 |
onClick={ handleClick } |
| 104 |
{ ...attributes } |
| 105 |
> |
| 106 |
{ /* Floating hover toolbar */ } |
| 107 |
<div className="bp-element-card__toolbar"> |
| 108 |
<button |
| 109 |
type="button" |
| 110 |
className="bp-element-card__toolbar-btn bp-element-card__toolbar-btn--drag" |
| 111 |
title={ __( 'Drag to reorder', 'better-payment' ) } |
| 112 |
{ ...listeners } |
| 113 |
> |
| 114 |
<svg width="14" height="14" viewBox="0 0 16 16" fill="none"> |
| 115 |
<path d="M6 3a1 1 0 1 1-2 0 1 1 0 0 1 2 0zm4 0a1 1 0 1 1-2 0 1 1 0 0 1 2 0zm-4 5a1 1 0 1 1-2 0 1 1 0 0 1 2 0zm4 0a1 1 0 1 1-2 0 1 1 0 0 1 2 0zm-4 5a1 1 0 1 1-2 0 1 1 0 0 1 2 0zm4 0a1 1 0 1 1-2 0 1 1 0 0 1 2 0z" fill="currentColor"/> |
| 116 |
</svg> |
| 117 |
</button> |
| 118 |
|
| 119 |
{ ! isFirst && ( |
| 120 |
<button |
| 121 |
type="button" |
| 122 |
className="bp-element-card__toolbar-btn" |
| 123 |
title={ __( 'Move up', 'better-payment' ) } |
| 124 |
onClick={ handleMoveUp } |
| 125 |
> |
| 126 |
<svg width="14" height="14" viewBox="0 0 16 16" fill="none"> |
| 127 |
<path d="M8 12V4M4 8l4-4 4 4" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/> |
| 128 |
</svg> |
| 129 |
</button> |
| 130 |
) } |
| 131 |
|
| 132 |
{ ! isLast && ( |
| 133 |
<button |
| 134 |
type="button" |
| 135 |
className="bp-element-card__toolbar-btn" |
| 136 |
title={ __( 'Move down', 'better-payment' ) } |
| 137 |
onClick={ handleMoveDown } |
| 138 |
> |
| 139 |
<svg width="14" height="14" viewBox="0 0 16 16" fill="none"> |
| 140 |
<path d="M8 4v8m4-4-4 4-4-4" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/> |
| 141 |
</svg> |
| 142 |
</button> |
| 143 |
) } |
| 144 |
|
| 145 |
<button |
| 146 |
type="button" |
| 147 |
className="bp-element-card__toolbar-btn" |
| 148 |
title={ __( 'Edit element', 'better-payment' ) } |
| 149 |
onClick={ handleEdit } |
| 150 |
> |
| 151 |
<svg width="14" height="14" viewBox="0 0 16 16" fill="none"> |
| 152 |
<path d="M11.5 2.5l2 2-7 7H4.5v-2l7-7z" stroke="currentColor" strokeWidth="1.3" strokeLinecap="round" strokeLinejoin="round"/> |
| 153 |
</svg> |
| 154 |
</button> |
| 155 |
|
| 156 |
<button |
| 157 |
type="button" |
| 158 |
className="bp-element-card__toolbar-btn bp-element-card__toolbar-btn--delete" |
| 159 |
title={ __( 'Delete', 'better-payment' ) } |
| 160 |
onClick={ handleDelete } |
| 161 |
> |
| 162 |
<svg width="14" height="14" viewBox="0 0 16 16" fill="none"> |
| 163 |
<path d="M2 4h12M5.333 4V2.667C5.333 2.313 5.473 1.974 5.724 1.724 5.974 1.473 6.313 1.333 6.667 1.333H9.333C9.687 1.333 10.026 1.473 10.276 1.724 10.527 1.974 10.667 2.313 10.667 2.667V4M12.667 4 12 13.333C12 13.687 11.86 14.026 11.609 14.276 11.359 14.527 11.02 14.667 10.667 14.667H5.333C4.98 14.667 4.641 14.527 4.391 14.276 4.14 14.026 4 13.687 4 13.333L3.333 4" stroke="currentColor" strokeWidth="1.3" strokeLinecap="round" strokeLinejoin="round"/> |
| 164 |
</svg> |
| 165 |
</button> |
| 166 |
</div> |
| 167 |
|
| 168 |
{ /* Non-preview fallback body */ } |
| 169 |
{ ! preview && ( |
| 170 |
<div className="bp-element-card__body"> |
| 171 |
{ CUSTOM_ICONS[ element.type ] |
| 172 |
? <span className="bp-cb-field-card__svg-icon">{ CUSTOM_ICONS[ element.type ] }</span> |
| 173 |
: <span className={ `dashicons dashicons-${ def?.icon || 'block-default' }` } /> |
| 174 |
} |
| 175 |
<span className="bp-element-card__label">{ def?.label || element.type }</span> |
| 176 |
</div> |
| 177 |
) } |
| 178 |
|
| 179 |
{ /* Live preview body */ } |
| 180 |
{ preview && ( |
| 181 |
<div className="bp-element-card__preview"> |
| 182 |
{ preview } |
| 183 |
</div> |
| 184 |
) } |
| 185 |
</div> |
| 186 |
); |
| 187 |
} |
| 188 |
|
| 189 |
// ── Per-element live previews ───────────────────────────────────────────────── |
| 190 |
|
| 191 |
function getElementPreview( element, meta, dispatch, campaignId, columnId ) { |
| 192 |
switch ( element.type ) { |
| 193 |
case 'campaign_title': |
| 194 |
return <CampaignTitlePreview element={ element } meta={ meta } />; |
| 195 |
case 'campaign_description': |
| 196 |
return <CampaignDescriptionPreview element={ element } />; |
| 197 |
case 'photo': |
| 198 |
return <PhotoPreview element={ element } columnId={ columnId } dispatch={ dispatch } />; |
| 199 |
case 'social_links': |
| 200 |
return <SocialLinksPreview element={ element } />; |
| 201 |
case 'social_sharing': |
| 202 |
return <SocialSharingPreview element={ element } />; |
| 203 |
case 'progress_bar': |
| 204 |
return <ProgressBarPreview element={ element } meta={ meta } campaignId={ campaignId } />; |
| 205 |
case 'donate_amount': |
| 206 |
return <DonateAmountPreview element={ element } meta={ meta } dispatch={ dispatch } />; |
| 207 |
case 'donation_form': |
| 208 |
return <DonationFormPreview element={ element } meta={ meta } />; |
| 209 |
case 'campaign_summary': |
| 210 |
return <CampaignSummaryPreview element={ element } meta={ meta } campaignId={ campaignId } />; |
| 211 |
case 'organizer': |
| 212 |
return <OrganizerPreview element={ element } />; |
| 213 |
default: |
| 214 |
return null; |
| 215 |
} |
| 216 |
} |
| 217 |
|
| 218 |
// ── Campaign Summary preview ────────────────────────────────────────────────── |
| 219 |
|
| 220 |
function CampaignSummaryPreview( { element, meta, campaignId } ) { |
| 221 |
const s = element.settings || {}; |
| 222 |
const headline = s.headline || ''; |
| 223 |
const showRaised = s.show_raised !== false; |
| 224 |
const showDonors = s.show_donors !== false; |
| 225 |
const showPercent = s.show_percent !== false; |
| 226 |
const showDays = s.show_days !== false; |
| 227 |
|
| 228 |
const [ stats, setStats ] = useState( null ); |
| 229 |
|
| 230 |
useEffect( () => { |
| 231 |
if ( ! campaignId ) return; |
| 232 |
apiFetch( { path: `better-payment/v1/campaigns/${ campaignId }/stats` } ) |
| 233 |
.then( ( data ) => setStats( data ) ) |
| 234 |
.catch( () => {} ); |
| 235 |
}, [ campaignId ] ); |
| 236 |
|
| 237 |
const raised = stats ? parseFloat( stats.total_raised ) : 0; |
| 238 |
const donors = stats ? stats.donor_count : 0; |
| 239 |
const goal = parseFloat( meta?.bpc_goal_amount ) || 0; |
| 240 |
const days = meta?.bpc_end_date |
| 241 |
? Math.max( 0, Math.ceil( ( new Date( meta.bpc_end_date ) - new Date() ) / 86400000 ) ) |
| 242 |
: null; |
| 243 |
const percent = goal > 0 ? Math.min( 100, Math.round( ( raised / goal ) * 1000 ) / 10 ) : 0; |
| 244 |
|
| 245 |
const currencySymbol = getGlobalCurrencySymbol(); |
| 246 |
|
| 247 |
const raisedFormatted = `${ currencySymbol }${ Number( raised ).toLocaleString( 'en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 } ) }`; |
| 248 |
|
| 249 |
return ( |
| 250 |
<div className="bp-element-preview__summary"> |
| 251 |
{ headline && ( |
| 252 |
<p className="bp-element-preview__summary-headline">{ headline }</p> |
| 253 |
) } |
| 254 |
<div className="bp-element-preview__summary-grid"> |
| 255 |
{ showRaised && ( |
| 256 |
<div className="bp-element-preview__summary-item"> |
| 257 |
<strong>{ raisedFormatted }</strong> |
| 258 |
<span>{ __( 'Raised', 'better-payment' ) }</span> |
| 259 |
</div> |
| 260 |
) } |
| 261 |
{ showDonors && ( |
| 262 |
<div className="bp-element-preview__summary-item"> |
| 263 |
<strong>{ donors }</strong> |
| 264 |
<span>{ __( 'Donors', 'better-payment' ) }</span> |
| 265 |
</div> |
| 266 |
) } |
| 267 |
{ showPercent && goal > 0 && ( |
| 268 |
<div className="bp-element-preview__summary-item"> |
| 269 |
<strong>{ percent }%</strong> |
| 270 |
<span>{ __( 'Funded', 'better-payment' ) }</span> |
| 271 |
</div> |
| 272 |
) } |
| 273 |
{ showDays && days !== null && ( |
| 274 |
<div className="bp-element-preview__summary-item"> |
| 275 |
<strong>{ days }</strong> |
| 276 |
<span>{ __( 'Days Left', 'better-payment' ) }</span> |
| 277 |
</div> |
| 278 |
) } |
| 279 |
</div> |
| 280 |
</div> |
| 281 |
); |
| 282 |
} |
| 283 |
|
| 284 |
// ── Campaign Description preview ───────────────────────────────────────────── |
| 285 |
|
| 286 |
function CampaignDescriptionPreview( { element } ) { |
| 287 |
const s = element.settings || {}; |
| 288 |
const headline = s.headline || ''; |
| 289 |
const content = s.content || ''; |
| 290 |
const width = s.width ?? 100; |
| 291 |
const align = s.align || 'left'; |
| 292 |
|
| 293 |
const wrapStyle = { |
| 294 |
width: `${ width }%`, |
| 295 |
textAlign: align, |
| 296 |
margin: align === 'center' ? '0 auto' : align === 'right' ? '0 0 0 auto' : '0', |
| 297 |
}; |
| 298 |
|
| 299 |
if ( ! headline && ! content ) { |
| 300 |
return ( |
| 301 |
<div className="bp-element-preview__description-empty"> |
| 302 |
<span className="dashicons dashicons-editor-paragraph" /> |
| 303 |
<span>{ __( 'Campaign Description', 'better-payment' ) }</span> |
| 304 |
</div> |
| 305 |
); |
| 306 |
} |
| 307 |
|
| 308 |
return ( |
| 309 |
<div className="bp-element-preview__description" style={ wrapStyle }> |
| 310 |
{ headline && ( |
| 311 |
<p className="bp-element-preview__description-headline">{ headline }</p> |
| 312 |
) } |
| 313 |
{ content && ( |
| 314 |
<div |
| 315 |
className="bp-element-preview__description-content" |
| 316 |
dangerouslySetInnerHTML={ { __html: scaleInlineFontSizes( content ) } } |
| 317 |
/> |
| 318 |
) } |
| 319 |
</div> |
| 320 |
); |
| 321 |
} |
| 322 |
|
| 323 |
// ── Progress Bar preview ────────────────────────────────────────────────────── |
| 324 |
|
| 325 |
function ProgressBarPreview( { element, meta, campaignId } ) { |
| 326 |
const s = element.settings || {}; |
| 327 |
const headline = s.headline || ''; |
| 328 |
const showDonated = s.show_donated !== false; |
| 329 |
const showGoal = s.show_goal !== false; |
| 330 |
const donateLabel = s.donate_label || 'Donated:'; |
| 331 |
const goalLabel = s.goal_label || 'Goal:'; |
| 332 |
const width = s.width ?? 100; |
| 333 |
const align = s.align || 'left'; |
| 334 |
const primary = meta?.bpc_color_primary || '#6b63f6'; |
| 335 |
|
| 336 |
const [ stats, setStats ] = useState( null ); |
| 337 |
|
| 338 |
useEffect( () => { |
| 339 |
if ( ! campaignId ) return; |
| 340 |
apiFetch( { path: `better-payment/v1/campaigns/${ campaignId }/stats` } ) |
| 341 |
.then( ( data ) => setStats( data ) ) |
| 342 |
.catch( () => {} ); |
| 343 |
}, [ campaignId ] ); |
| 344 |
|
| 345 |
const raised = stats ? stats.total_raised : 0; |
| 346 |
const goal = parseFloat( meta?.bpc_goal_amount ) || 0; |
| 347 |
const progress = goal > 0 ? Math.min( 100, ( raised / goal ) * 100 ) : 0; |
| 348 |
|
| 349 |
const wrapStyle = { |
| 350 |
width: `${ width }%`, |
| 351 |
margin: align === 'center' ? '0 auto' : align === 'right' ? '0 0 0 auto' : '0', |
| 352 |
}; |
| 353 |
|
| 354 |
const roundAmounts = s.round_amounts || false; |
| 355 |
const currencySymbol = getGlobalCurrencySymbol(); |
| 356 |
|
| 357 |
// Display progress: ceiling integer when round_amounts, else 1 decimal from API. |
| 358 |
const rawRatio = goal > 0 ? Math.min( 100, ( raised / goal ) * 100 ) : 0; |
| 359 |
const displayProgress = roundAmounts |
| 360 |
? Math.ceil( rawRatio ) |
| 361 |
: progress; // 1-decimal float from CampaignStats |
| 362 |
|
| 363 |
const goalFormatted = goal > 0 |
| 364 |
? ( currencySymbol + ( roundAmounts ? Math.ceil( goal ).toLocaleString() : Number( goal ).toFixed( 2 ) ) ) |
| 365 |
: __( 'No goal set', 'better-payment' ); |
| 366 |
|
| 367 |
return ( |
| 368 |
<div className="bp-element-preview__progress" style={ wrapStyle }> |
| 369 |
{ headline && ( |
| 370 |
<p className="bp-element-preview__progress-headline">{ headline }</p> |
| 371 |
) } |
| 372 |
<div className="bp-element-preview__progress-track"> |
| 373 |
<div |
| 374 |
className="bp-element-preview__progress-fill" |
| 375 |
style={ { width: `${ progress }%`, backgroundColor: primary } } |
| 376 |
/> |
| 377 |
</div> |
| 378 |
{ ( showDonated || showGoal ) && ( |
| 379 |
<div className="bp-element-preview__progress-labels"> |
| 380 |
{ showDonated && ( |
| 381 |
<span className="bp-element-preview__progress-donated"> |
| 382 |
{ donateLabel } { displayProgress }% |
| 383 |
</span> |
| 384 |
) } |
| 385 |
{ showGoal && ( |
| 386 |
<span className="bp-element-preview__progress-goal"> |
| 387 |
{ goalLabel } { goalFormatted } |
| 388 |
</span> |
| 389 |
) } |
| 390 |
</div> |
| 391 |
) } |
| 392 |
</div> |
| 393 |
); |
| 394 |
} |
| 395 |
|
| 396 |
// ── Photo preview ───────────────────────────────────────────────────────────── |
| 397 |
|
| 398 |
function PhotoPreview( { element, columnId, dispatch } ) { |
| 399 |
const s = element.settings || {}; |
| 400 |
|
| 401 |
const handleImageClick = useCallback( ( e ) => { |
| 402 |
e.stopPropagation(); |
| 403 |
openWpMedia( ( { url, id, sizes } ) => { |
| 404 |
dispatch( { |
| 405 |
type: 'UPDATE_ELEMENT_SETTINGS', |
| 406 |
elementId: element.id, |
| 407 |
settings: { src: url, src_id: id, src_sizes: sizes }, |
| 408 |
} ); |
| 409 |
} ); |
| 410 |
}, [ element.id, dispatch ] ); |
| 411 |
const src = s.src || ''; |
| 412 |
const alt = s.alt || ''; |
| 413 |
const width = s.width ?? 100; |
| 414 |
const align = s.align || 'center'; |
| 415 |
const size = s.size || 'full'; |
| 416 |
const sizes = s.src_sizes || {}; |
| 417 |
const srcId = s.src_id || 0; |
| 418 |
|
| 419 |
// Keyed by attachment ID — fetched once per ID, cached for all size changes. |
| 420 |
const [ fetchedSizes, setFetchedSizes ] = useState( {} ); |
| 421 |
|
| 422 |
useEffect( () => { |
| 423 |
if ( ! srcId ) { |
| 424 |
setFetchedSizes( {} ); |
| 425 |
return; |
| 426 |
} |
| 427 |
let cancelled = false; |
| 428 |
apiFetch( { path: `/wp/v2/media/${ srcId }` } ) |
| 429 |
.then( ( data ) => { |
| 430 |
if ( cancelled ) return; |
| 431 |
const raw = data?.media_details?.sizes || {}; |
| 432 |
const mapped = {}; |
| 433 |
Object.keys( raw ).forEach( ( k ) => { |
| 434 |
mapped[ k ] = raw[ k ].source_url; |
| 435 |
} ); |
| 436 |
setFetchedSizes( mapped ); |
| 437 |
} ) |
| 438 |
.catch( () => {} ); |
| 439 |
return () => { cancelled = true; }; |
| 440 |
}, [ srcId ] ); |
| 441 |
|
| 442 |
// Priority: src_sizes (from media modal) → fetched from REST API → original src. |
| 443 |
const resolvedUrl = size !== 'full' |
| 444 |
? ( sizes[ size ]?.url || fetchedSizes[ size ] || src ) |
| 445 |
: src; |
| 446 |
|
| 447 |
const wrapStyle = { textAlign: align }; |
| 448 |
const imgStyle = { |
| 449 |
width: 'auto', |
| 450 |
maxWidth: `${ width }%`, |
| 451 |
height: 'auto', |
| 452 |
display: 'block', |
| 453 |
borderRadius: '4px', |
| 454 |
margin: align === 'center' ? '0 auto' : align === 'right' ? '0 0 0 auto' : '0', |
| 455 |
}; |
| 456 |
|
| 457 |
if ( ! src ) { |
| 458 |
return ( |
| 459 |
<div className="bp-element-preview__photo-empty" onClick={ handleImageClick } style={ { cursor: 'pointer' } }> |
| 460 |
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 24 24" fill="#bbb"> |
| 461 |
<path d="M21 19V5c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5l3.5-4.5z"/> |
| 462 |
</svg> |
| 463 |
</div> |
| 464 |
); |
| 465 |
} |
| 466 |
|
| 467 |
return ( |
| 468 |
<div className="bp-element-preview__photo" style={ wrapStyle }> |
| 469 |
<img |
| 470 |
src={ resolvedUrl } |
| 471 |
alt={ alt } |
| 472 |
style={ { ...imgStyle, cursor: 'pointer' } } |
| 473 |
onClick={ handleImageClick } |
| 474 |
/> |
| 475 |
</div> |
| 476 |
); |
| 477 |
} |
| 478 |
|
| 479 |
// ── Social Links preview ────────────────────────────────────────────────────── |
| 480 |
|
| 481 |
const SOCIAL_ICONS = { |
| 482 |
twitter: ( |
| 483 |
<svg viewBox="0 0 24 24" fill="currentColor" width="18" height="18"> |
| 484 |
<path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-4.714-6.231-5.401 6.231H2.744l7.73-8.835L1.254 2.25H8.08l4.253 5.622zm-1.161 17.52h1.833L7.084 4.126H5.117z"/> |
| 485 |
</svg> |
| 486 |
), |
| 487 |
facebook: ( |
| 488 |
<svg viewBox="0 0 24 24" fill="currentColor" width="18" height="18"> |
| 489 |
<path d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"/> |
| 490 |
</svg> |
| 491 |
), |
| 492 |
linkedin: ( |
| 493 |
<svg viewBox="0 0 24 24" fill="currentColor" width="18" height="18"> |
| 494 |
<path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.062 2.062 0 01-2.063-2.065 2.064 2.064 0 112.063 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/> |
| 495 |
</svg> |
| 496 |
), |
| 497 |
instagram: ( |
| 498 |
<svg viewBox="0 0 24 24" fill="currentColor" width="18" height="18"> |
| 499 |
<path d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zm0-2.163c-3.259 0-3.667.014-4.947.072-4.358.2-6.78 2.618-6.98 6.98-.059 1.281-.073 1.689-.073 4.948 0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98 1.281.058 1.689.072 4.948.072 3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98-1.281-.059-1.69-.073-4.949-.073zm0 5.838c-3.403 0-6.162 2.759-6.162 6.162S8.597 18.163 12 18.163s6.162-2.759 6.162-6.162S15.403 5.838 12 5.838zm0 10.162c-2.209 0-4-1.79-4-4 0-2.209 1.791-4 4-4s4 1.791 4 4c0 2.21-1.791 4-4 4zm6.406-11.845a1.44 1.44 0 100 2.881 1.44 1.44 0 000-2.881z"/> |
| 500 |
</svg> |
| 501 |
), |
| 502 |
tiktok: ( |
| 503 |
<svg viewBox="0 0 24 24" fill="currentColor" width="18" height="18"> |
| 504 |
<path d="M12.525.02c1.31-.02 2.61-.01 3.91-.02.08 1.53.63 3.09 1.75 4.17 1.12 1.11 2.7 1.62 4.24 1.79v4.03c-1.44-.05-2.89-.35-4.2-.97-.57-.26-1.1-.59-1.62-.93-.01 2.92.01 5.84-.02 8.75-.08 1.4-.54 2.79-1.35 3.94-1.31 1.92-3.58 3.17-5.91 3.21-1.43.08-2.86-.31-4.08-1.03-2.02-1.19-3.44-3.37-3.65-5.71-.02-.5-.03-1-.01-1.49.18-1.9 1.12-3.72 2.58-4.96 1.66-1.44 3.98-2.13 6.15-1.72.02 1.48-.04 2.96-.04 4.44-.99-.32-2.15-.23-3.02.37-.63.41-1.11 1.04-1.36 1.75-.21.51-.15 1.07-.14 1.61.24 1.64 1.82 3.02 3.5 2.87 1.12-.01 2.19-.66 2.77-1.61.19-.33.4-.67.41-1.06.1-1.79.06-3.57.07-5.36.01-4.03-.01-8.05.02-12.07z"/> |
| 505 |
</svg> |
| 506 |
), |
| 507 |
pinterest: ( |
| 508 |
<svg viewBox="0 0 24 24" fill="currentColor" width="18" height="18"> |
| 509 |
<path d="M12 0C5.373 0 0 5.372 0 12c0 5.084 3.163 9.426 7.627 11.174-.105-.949-.2-2.405.042-3.441.218-.937 1.407-5.965 1.407-5.965s-.359-.719-.359-1.782c0-1.668.967-2.914 2.171-2.914 1.023 0 1.518.769 1.518 1.69 0 1.029-.655 2.568-.994 3.995-.283 1.194.599 2.169 1.777 2.169 2.133 0 3.772-2.249 3.772-5.495 0-2.873-2.064-4.882-5.012-4.882-3.414 0-5.418 2.561-5.418 5.207 0 1.031.397 2.138.893 2.738a.36.36 0 01.083.345l-.333 1.36c-.053.22-.174.267-.402.161-1.499-.698-2.436-2.889-2.436-4.649 0-3.785 2.75-7.262 7.929-7.262 4.163 0 7.398 2.967 7.398 6.931 0 4.136-2.607 7.464-6.227 7.464-1.216 0-2.359-.632-2.75-1.378l-.748 2.853c-.271 1.043-1.002 2.35-1.492 3.146C9.57 23.812 10.763 24 12 24c6.627 0 12-5.373 12-12S18.627 0 12 0z"/> |
| 510 |
</svg> |
| 511 |
), |
| 512 |
youtube: ( |
| 513 |
<svg viewBox="0 0 24 24" fill="currentColor" width="18" height="18"> |
| 514 |
<path d="M23.498 6.186a3.016 3.016 0 00-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 00.502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 002.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 002.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z"/> |
| 515 |
</svg> |
| 516 |
), |
| 517 |
threads: ( |
| 518 |
<svg viewBox="0 0 24 24" fill="currentColor" width="18" height="18"> |
| 519 |
<path d="M12.186 24h-.007c-3.581-.024-6.334-1.205-8.184-3.509C2.35 18.44 1.5 15.586 1.472 12.01v-.017c.028-3.579.879-6.43 2.525-8.482C5.845 1.205 8.6.024 12.18 0h.014c2.746.02 5.043.725 6.826 2.098 1.677 1.29 2.858 3.13 3.509 5.467l-2.04.569c-1.104-3.96-3.898-5.984-8.304-6.015-2.91.022-5.11.936-6.54 2.717C4.307 6.504 3.616 8.914 3.589 12c.027 3.086.718 5.496 2.057 7.164 1.43 1.783 3.631 2.698 6.54 2.717 2.623-.02 4.358-.631 5.8-2.045 1.647-1.613 1.618-3.593 1.09-4.798-.31-.71-.873-1.3-1.634-1.75-.192 1.352-.622 2.446-1.284 3.272-.886 1.102-2.14 1.704-3.73 1.79-1.202.065-2.361-.218-3.259-.801-1.063-.689-1.685-1.74-1.752-2.964-.065-1.19.408-2.285 1.33-3.082.88-.76 2.119-1.207 3.583-1.291a13.853 13.853 0 013.271.165c-.07-.75-.273-1.318-.614-1.716-.434-.507-1.119-.768-2.036-.777h-.045c-.67 0-1.938.181-2.669 1.4l-1.812-.755c.97-1.868 2.836-2.677 4.484-2.677h.06c3.233.03 5.164 2.01 5.34 5.49.208 3.394-1.112 5.49-3.317 6.548-.384.186-.785.34-1.197.461C16.418 23.61 14.397 24 12.186 24z"/> |
| 520 |
</svg> |
| 521 |
), |
| 522 |
bluesky: ( |
| 523 |
<svg viewBox="0 0 24 24" fill="currentColor" width="18" height="18"> |
| 524 |
<path d="M12 10.8c-1.087-2.114-4.046-6.053-6.798-7.995C2.566.944 1.561 1.266.902 1.565.139 1.908 0 3.08 0 3.768c0 .69.378 5.65.624 6.479.815 2.736 3.713 3.66 6.383 3.364.136-.02.275-.039.415-.056-.138.022-.276.04-.415.056-3.912.58-7.387 2.005-2.83 7.078 5.013 5.19 6.87-1.113 7.823-4.308.953 3.195 2.05 9.271 7.733 4.308 4.267-4.308 1.172-6.498-2.74-7.078a8.741 8.741 0 01-.415-.056c.14.017.279.036.415.056 2.67.297 5.568-.628 6.383-3.364.246-.828.624-5.79.624-6.478 0-.69-.139-1.861-.902-2.204-.659-.299-1.664-.62-4.3 1.24C16.046 4.748 13.087 8.687 12 10.8z"/> |
| 525 |
</svg> |
| 526 |
), |
| 527 |
mastodon: ( |
| 528 |
<svg viewBox="0 0 24 24" fill="currentColor" width="18" height="18"> |
| 529 |
<path d="M23.268 5.313c-.35-2.578-2.617-4.61-5.304-5.004C17.51.242 15.792 0 11.813 0h-.03c-3.98 0-4.835.242-5.288.309C3.882.692 1.496 2.518.917 5.127.64 6.412.61 7.837.661 9.143c.074 1.874.088 3.745.26 5.611.118 1.24.325 2.47.62 3.68.55 2.237 2.777 4.098 4.96 4.857 2.336.792 4.849.923 7.256.38.265-.061.527-.132.786-.213.585-.184 1.27-.39 1.774-.753a.057.057 0 00.023-.043v-1.809a.052.052 0 00-.066-.051c-1.517.363-3.072.546-4.632.546-2.685 0-3.463-1.284-3.674-1.818a5.593 5.593 0 01-.319-1.433.053.053 0 01.066-.054c1.517.363 3.072.546 4.632.546.376 0 .75 0 1.124-.01 1.554-.043 3.19-.167 4.72-.498.038-.009.075-.015.11-.024 2.435-.464 4.753-1.92 4.989-5.604.005-.109.033-1.25.033-1.36 0-.43.138-3.032-.019-4.643zm-3.22 9.214h-2.058V9.47c0-1.063-.447-1.601-1.35-1.601-1 0-1.5.647-1.5 1.923v2.786h-2.048V9.792c0-1.276-.5-1.923-1.5-1.923-.903 0-1.35.538-1.35 1.601v5.04H7.884V9.32c0-1.062.27-1.907.81-2.534.558-.627 1.287-.948 2.192-.948 1.047 0 1.84.402 2.363 1.206l.509.855.51-.855c.523-.804 1.316-1.206 2.363-1.206.904 0 1.633.32 2.192.948.54.627.925 1.472.925 2.534v5.19z"/> |
| 530 |
</svg> |
| 531 |
), |
| 532 |
}; |
| 533 |
|
| 534 |
const SOCIAL_ORDER = [ 'twitter', 'facebook', 'linkedin', 'instagram', 'tiktok', 'pinterest', 'youtube', 'threads', 'bluesky', 'mastodon' ]; |
| 535 |
const SOCIAL_SHARING_ORDER = [ 'twitter', 'facebook', 'linkedin', 'pinterest', 'mastodon', 'threads', 'bluesky' ]; |
| 536 |
|
| 537 |
function SocialLinksPreview( { element } ) { |
| 538 |
const s = element.settings || {}; |
| 539 |
const headline = s.headline || 'Follow Now'; |
| 540 |
const align = s.align || 'left'; |
| 541 |
const activeIcons = SOCIAL_ORDER.filter( ( key ) => !! s[ key ] ); |
| 542 |
|
| 543 |
return ( |
| 544 |
<div className="bp-element-preview__social-links" style={ { textAlign: align } }> |
| 545 |
{ headline && ( |
| 546 |
<p className="bp-element-preview__social-headline">{ headline }</p> |
| 547 |
) } |
| 548 |
{ activeIcons.length > 0 && ( |
| 549 |
<div className="bp-element-preview__social-icons" style={ { justifyContent: align === 'center' ? 'center' : align === 'right' ? 'flex-end' : 'flex-start' } }> |
| 550 |
{ activeIcons.map( ( key ) => ( |
| 551 |
<span key={ key } className="bp-element-preview__social-icon" title={ key }> |
| 552 |
{ SOCIAL_ICONS[ key ] } |
| 553 |
</span> |
| 554 |
) ) } |
| 555 |
</div> |
| 556 |
) } |
| 557 |
</div> |
| 558 |
); |
| 559 |
} |
| 560 |
|
| 561 |
function SocialSharingPreview( { element } ) { |
| 562 |
const s = element.settings || {}; |
| 563 |
const headline = s.headline !== undefined && s.headline !== null ? s.headline : 'Share Now'; |
| 564 |
const align = s.align || 'left'; |
| 565 |
const activeIcons = SOCIAL_SHARING_ORDER.filter( ( key ) => s[ key ] !== false ); |
| 566 |
|
| 567 |
return ( |
| 568 |
<div className="bp-element-preview__social-links" style={ { textAlign: align } }> |
| 569 |
{ headline && ( |
| 570 |
<p className="bp-element-preview__social-headline">{ headline }</p> |
| 571 |
) } |
| 572 |
{ activeIcons.length > 0 && ( |
| 573 |
<div className="bp-element-preview__social-icons" style={ { justifyContent: align === 'center' ? 'center' : align === 'right' ? 'flex-end' : 'flex-start' } }> |
| 574 |
{ activeIcons.map( ( key ) => ( |
| 575 |
<span key={ key } className="bp-element-preview__social-icon" title={ key }> |
| 576 |
{ SOCIAL_ICONS[ key ] } |
| 577 |
</span> |
| 578 |
) ) } |
| 579 |
</div> |
| 580 |
) } |
| 581 |
</div> |
| 582 |
); |
| 583 |
} |
| 584 |
|
| 585 |
// ── Campaign Title preview ──────────────────────────────────────────────────── |
| 586 |
|
| 587 |
function CampaignTitlePreview( { element, meta } ) { |
| 588 |
const s = element.settings || {}; |
| 589 |
const title = meta?.title ?? ''; |
| 590 |
const align = s.align || 'left'; |
| 591 |
|
| 592 |
return ( |
| 593 |
<h2 |
| 594 |
className="bp-element-preview__campaign-title" |
| 595 |
style={ { textAlign: align } } |
| 596 |
> |
| 597 |
{ title } |
| 598 |
</h2> |
| 599 |
); |
| 600 |
} |
| 601 |
|
| 602 |
// ── Donate Amount preview ───────────────────────────────────────────────────── |
| 603 |
|
| 604 |
function hexToRgba( hex, alpha ) { |
| 605 |
if ( ! hex || hex.length < 7 ) return `rgba(107,99,246,${ alpha })`; |
| 606 |
const r = parseInt( hex.slice( 1, 3 ), 16 ); |
| 607 |
const g = parseInt( hex.slice( 3, 5 ), 16 ); |
| 608 |
const b = parseInt( hex.slice( 5, 7 ), 16 ); |
| 609 |
return `rgba(${ r },${ g },${ b },${ alpha })`; |
| 610 |
} |
| 611 |
|
| 612 |
function DonateAmountPreview( { element, meta, dispatch } ) { |
| 613 |
const s = element.settings || {}; |
| 614 |
const headline = s.headline || ''; |
| 615 |
const amounts = Array.isArray( meta?.bpc_suggested_amounts ) ? meta.bpc_suggested_amounts : []; |
| 616 |
const allowCustom = !! meta?.bpc_allow_custom_amount; |
| 617 |
const primary = meta?.bpc_color_primary || '#6b63f6'; |
| 618 |
|
| 619 |
const globalCurrencySymbol = getGlobalCurrencySymbol(); |
| 620 |
|
| 621 |
const [ customValue, setCustomValue ] = useState( '' ); |
| 622 |
|
| 623 |
const deselectAll = () => |
| 624 |
dispatch( { |
| 625 |
type: 'UPDATE_META', |
| 626 |
payload: { |
| 627 |
bpc_suggested_amounts: amounts.map( ( a ) => ( { ...a, is_default: false } ) ), |
| 628 |
}, |
| 629 |
} ); |
| 630 |
|
| 631 |
const handleSelectAmount = ( id, e ) => { |
| 632 |
e.stopPropagation(); |
| 633 |
setCustomValue( '' ); |
| 634 |
dispatch( { |
| 635 |
type: 'UPDATE_META', |
| 636 |
payload: { |
| 637 |
bpc_suggested_amounts: amounts.map( ( a ) => ( { ...a, is_default: a.id === id } ) ), |
| 638 |
}, |
| 639 |
} ); |
| 640 |
}; |
| 641 |
|
| 642 |
const handleCustomFocus = ( e ) => { |
| 643 |
e.stopPropagation(); |
| 644 |
deselectAll(); |
| 645 |
}; |
| 646 |
|
| 647 |
const handleCustomChange = ( e ) => { |
| 648 |
setCustomValue( e.target.value ); |
| 649 |
}; |
| 650 |
|
| 651 |
if ( amounts.length === 0 && ! allowCustom ) { |
| 652 |
return ( |
| 653 |
<div className="bp-element-preview__donate-empty"> |
| 654 |
<span className="dashicons dashicons-money-alt" /> |
| 655 |
<span>{ __( 'No suggested amounts yet — add some in Donation Options', 'better-payment' ) }</span> |
| 656 |
</div> |
| 657 |
); |
| 658 |
} |
| 659 |
|
| 660 |
const anySelected = amounts.some( ( a ) => a.is_default ); |
| 661 |
const customActive = allowCustom && ! anySelected && customValue !== ''; |
| 662 |
|
| 663 |
return ( |
| 664 |
<div className="bp-element-preview__donate" style={ { pointerEvents: 'none' } }> |
| 665 |
{ headline && ( |
| 666 |
<p className="bp-element-preview__donate-headline">{ headline }</p> |
| 667 |
) } |
| 668 |
|
| 669 |
{ amounts.length > 0 && ( |
| 670 |
<div className="bp-element-preview__donate-amounts"> |
| 671 |
{ amounts.map( ( item ) => { |
| 672 |
const selected = !! item.is_default; |
| 673 |
const btnStyle = selected |
| 674 |
? { |
| 675 |
borderColor: primary, |
| 676 |
color: primary, |
| 677 |
backgroundColor: hexToRgba( primary, 0.08 ), |
| 678 |
} |
| 679 |
: {}; |
| 680 |
return ( |
| 681 |
<button |
| 682 |
key={ item.id } |
| 683 |
type="button" |
| 684 |
className={ 'bp-element-preview__donate-btn' + ( selected ? ' is-selected' : '' ) } |
| 685 |
style={ btnStyle } |
| 686 |
onClick={ ( e ) => handleSelectAmount( item.id, e ) } |
| 687 |
> |
| 688 |
{ globalCurrencySymbol }{ item.amount } |
| 689 |
</button> |
| 690 |
); |
| 691 |
} ) } |
| 692 |
</div> |
| 693 |
) } |
| 694 |
|
| 695 |
{ allowCustom && ( |
| 696 |
<div |
| 697 |
className={ 'bp-element-preview__donate-custom' + ( customActive ? ' is-active' : '' ) } |
| 698 |
style={ customActive ? { borderColor: primary, boxShadow: `0 0 0 2px ${ hexToRgba( primary, 0.18 ) }` } : {} } |
| 699 |
onClick={ ( e ) => e.stopPropagation() } |
| 700 |
> |
| 701 |
<span className="bp-element-preview__donate-custom-prefix">{ globalCurrencySymbol }</span> |
| 702 |
<input |
| 703 |
type="number" |
| 704 |
min="0" |
| 705 |
step="1" |
| 706 |
value={ customValue } |
| 707 |
placeholder={ __( 'Enter custom amount', 'better-payment' ) } |
| 708 |
onChange={ handleCustomChange } |
| 709 |
onFocus={ handleCustomFocus } |
| 710 |
/> |
| 711 |
</div> |
| 712 |
) } |
| 713 |
</div> |
| 714 |
); |
| 715 |
} |
| 716 |
|
| 717 |
// ── Donation Form (Donate Button) preview ───────────────────────────────────── |
| 718 |
|
| 719 |
function DonationFormPreview( { element, meta } ) { |
| 720 |
const s = element.settings || {}; |
| 721 |
const label = s.button_label || s.button_text || 'Donate Now'; |
| 722 |
const primary = meta?.bpc_color_primary || '#6b63f6'; |
| 723 |
const buttonColor = s.button_color || primary; |
| 724 |
const width = s.width ?? 100; |
| 725 |
const align = s.align || 'center'; |
| 726 |
|
| 727 |
const wrapStyle = { |
| 728 |
width: `${ width }%`, |
| 729 |
margin: align === 'center' ? '0 auto' : align === 'right' ? '0 0 0 auto' : '0', |
| 730 |
}; |
| 731 |
|
| 732 |
return ( |
| 733 |
<div className="bp-element-preview__donate-button-wrap" style={ wrapStyle }> |
| 734 |
<span |
| 735 |
className="bp-element-preview__donate-button-btn" |
| 736 |
style={ { backgroundColor: buttonColor } } |
| 737 |
> |
| 738 |
{ label } |
| 739 |
</span> |
| 740 |
</div> |
| 741 |
); |
| 742 |
} |
| 743 |
|
| 744 |
// ── Organizer preview ───────────────────────────────────────────────────────── |
| 745 |
|
| 746 |
function OrganizerPreview( { element } ) { |
| 747 |
const s = element.settings || {}; |
| 748 |
const roleTitle = s.role_title || 'Organizer'; |
| 749 |
const description = s.description || ''; |
| 750 |
const width = s.width ?? 100; |
| 751 |
const align = s.align || 'left'; |
| 752 |
|
| 753 |
// Resolve display name + avatar from the localized element registry options. |
| 754 |
const def = getElement( 'organizer' ); |
| 755 |
const creatorOptions = def?.settingsSchema?.find( ( c ) => c.key === 'creator_user_id' )?.options || []; |
| 756 |
const creator = creatorOptions.find( ( o ) => String( o.value ) === String( s.creator_user_id ) ); |
| 757 |
const displayName = creator?.label || ''; |
| 758 |
const avatarUrl = creator?.avatar_url || ''; |
| 759 |
|
| 760 |
const wrapStyle = { |
| 761 |
width: `${ width }%`, |
| 762 |
margin: align === 'center' ? '0 auto' : align === 'right' ? '0 0 0 auto' : '0', |
| 763 |
}; |
| 764 |
|
| 765 |
return ( |
| 766 |
<div className="bp-element-preview__organizer" style={ wrapStyle }> |
| 767 |
<div className="bp-element-preview__organizer-avatar"> |
| 768 |
{ avatarUrl |
| 769 |
? <img src={ avatarUrl } alt={ displayName } /> |
| 770 |
: <span className="dashicons dashicons-admin-users" /> |
| 771 |
} |
| 772 |
</div> |
| 773 |
<div className="bp-element-preview__organizer-info"> |
| 774 |
{ displayName && ( |
| 775 |
<span className="bp-element-preview__organizer-name">{ displayName }</span> |
| 776 |
) } |
| 777 |
{ roleTitle && ( |
| 778 |
<span className="bp-element-preview__organizer-role">{ roleTitle }</span> |
| 779 |
) } |
| 780 |
{ description && ( |
| 781 |
<div |
| 782 |
className="bp-element-preview__organizer-desc" |
| 783 |
dangerouslySetInnerHTML={ { __html: scaleInlineFontSizes( description ) } } |
| 784 |
/> |
| 785 |
) } |
| 786 |
</div> |
| 787 |
</div> |
| 788 |
); |
| 789 |
} |
| 790 |
|