/**
* WordPress dependencies
*/
import { Spinner } from '@wordpress/components';
import { useEffect, useState } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
/**
* TypewriterText component that animates text with a typewriter effect.
*
* @since 3.19.0
*
* @param {Object} props The component's props.
* @param {string} props.message The message to display with typewriter effect.
*/
const TypewriterText = ( { message }: { message: string } ): JSX.Element => {
const [ displayText, setDisplayText ] = useState( '' );
const [ currentIndex, setCurrentIndex ] = useState( 0 );
const [ isTyping, setIsTyping ] = useState( true );
useEffect( () => {
// Reset when message changes
setDisplayText( '' );
setCurrentIndex( 0 );
setIsTyping( true );
}, [ message ] );
useEffect( () => {
if ( ! isTyping ) {
return;
}
if ( currentIndex < message.length ) {
// Still typing the message.
const typingTimeout = setTimeout( () => {
setDisplayText( ( prev ) => prev + message.charAt( currentIndex ) );
setCurrentIndex( ( prev ) => prev + 1 );
}, 50 ); // Typing speed.
return () => clearTimeout( typingTimeout );
}
}, [ currentIndex, isTyping, message ] );
return (
{ displayText }
);
};
/**
* Defines the props structure for Loading.
*
* @since 3.19.0
*/
type LoadingProps = {
className?: string;
loading?: boolean;
showSpinner?: boolean;
messages?: string[];
randomOrder?: boolean;
children?: React.ReactNode;
typewriter: boolean;
};
/**
* Loading component that displays a spinner and a message.
*
* @since 3.19.0
*
* @param {LoadingProps} props The component's props.
*/
export const Loading = ( {
className = '',
loading = true,
showSpinner = true,
randomOrder = false,
messages = [ __( 'Loading…', 'wp-parsely' ) ],
children,
typewriter = false,
}: LoadingProps ): JSX.Element => {
const [ currentMessage, setCurrentMessage ] = useState( '' );
const [ currentIndex, setCurrentIndex ] = useState( 0 );
useEffect( () => {
if ( ! loading ) {
return;
}
if ( '' === currentMessage ) {
setCurrentMessage( messages[ randomOrder ? Math.floor( Math.random() * messages.length ) : 0 ] );
}
const updateMessage = () => {
if ( randomOrder ) {
// Pick a random message different from the current one.
let nextIndex;
do {
nextIndex = Math.floor( Math.random() * messages.length );
} while ( messages.length > 1 && nextIndex === currentIndex );
setCurrentIndex( nextIndex );
setCurrentMessage( messages[ nextIndex ] );
} else {
// Move to next message sequentially.
const nextIndex = ( currentIndex + 1 ) % messages.length;
setCurrentIndex( nextIndex );
setCurrentMessage( messages[ nextIndex ] );
}
};
// Calculate pause duration based on message length (100ms per character).
const pauseDuration = currentMessage.length * 100;
const intervalId = setInterval( updateMessage, pauseDuration );
return () => clearInterval( intervalId );
}, [ loading, messages, currentIndex, currentMessage, randomOrder ] );
return (
{ showSpinner &&
}
{ children ? children : (
<>
{ typewriter ? : currentMessage }
>
) }
);
};