import React, { useEffect, useRef } from 'react'; import { __, sprintf } from '@wordpress/i18n'; import { X, LayoutDashboard, ScanSearch, Stamp, Sparkles, type LucideIcon } from 'lucide-react'; import type { WhatsNewItem } from '../../types'; interface Props { /** The announced release, e.g. "6.9.0". */ version: string; /** The unseen items, newest release first (app/Config/whats-new.php). */ items: WhatsNewItem[]; onDismiss: () => void; /** Dismiss for good, then go there. */ onGo: ( path: string ) => void; } /** The icon names whats-new.php may use. An unknown name falls back to the sparkle. */ const ICONS: Record< string, LucideIcon > = { dashboard: LayoutDashboard, checkup: ScanSearch, watermark: Stamp, }; /** * One-time "What's new" popup. It closes on the X, on "Got it" and on Escape, never on a click outside * (a stray click must not use up the only showing), and every close is remembered by the server. */ export default function WhatsNewPopup( { version, items, onDismiss, onGo }: Props ) { const dialog = useRef( null ); // 6.9.2 -> 6.9: the release, not the patch. const label = version.split( '.' ).slice( 0, 2 ).join( '.' ); useEffect( () => { // Focus the dialog, not a button: Escape works and screen readers announce it, without a focus ring. dialog.current?.focus(); const onKey = ( e: KeyboardEvent ) => { if ( 'Escape' === e.key ) { onDismiss(); } }; document.addEventListener( 'keydown', onKey ); return () => document.removeEventListener( 'keydown', onKey ); }, [] ); return (
{ __( 'WHAT’S NEW', 'image-sizes' ) }

{ sprintf( /* translators: %s: version, e.g. 6.9 */ __( 'New in ThumbPress %s', 'image-sizes' ), label, ) }

{ __( 'Here is what changed since you last updated.', 'image-sizes' ) }

    { items.map( ( item ) => { const Icon = ICONS[ item.icon ] || Sparkles; return (
  • { item.title } { item.pro && ( { __( 'PRO', 'image-sizes' ) } ) }

    { item.description }

    { item.to && item.cta && ( ) }
  • ); } ) }
); }