@wordpress/block-library
Version:
Block library for the WordPress editor.
8 lines (7 loc) • 12.6 kB
Source Map (JSON)
{
"version": 3,
"sources": ["../../src/utils/waveform-player.js"],
"sourcesContent": ["import { useEffect, useRef } from '@wordpress/element';\nimport { useEvent, useRefEffect } from '@wordpress/compose';\nimport { __, _x } from '@wordpress/i18n';\nimport {\n\tapplyWaveformPlayerStyles,\n\tinitWaveformPlayer,\n\tsetupPlayButtonArtwork,\n\tupdateSeekControlLabel,\n} from './waveform-utils';\n\n/**\n * Update the metadata of a WaveformPlayer element to reflect current props.\n *\n * `loadTrack()` owns full track swaps, but it also resets the audio element.\n * This keeps same-source metadata edits lightweight in the editor.\n *\n * @param {Object} player - The waveform player.\n * @param {Object} metadata - The track metadata.\n * @param {string} metadata.title - The track title.\n * @param {string} metadata.artist - The artist name.\n * @param {string} metadata.image - The track image URL.\n * @param {string} metadata.imageAlt - The track image alt text.\n * @param {boolean} showPlayButtonArtwork - Whether to show artwork on the play button.\n */\nfunction updatePlayerMetadata(\n\tplayer,\n\t{ title, artist, image, imageAlt },\n\tshowPlayButtonArtwork\n) {\n\tconst { instance, container } = player;\n\tconst playerArtwork = showPlayButtonArtwork ? undefined : image;\n\n\tif ( instance.titleEl ) {\n\t\tinstance.titleEl.textContent = title ?? '';\n\t}\n\tupdateSeekControlLabel( instance, title || __( 'Seek' ) );\n\n\tif ( typeof instance.syncArtist === 'function' ) {\n\t\tinstance.syncArtist( artist || '' );\n\t} else if ( instance.artistEl ) {\n\t\tinstance.artistEl.textContent = artist ?? '';\n\t\tinstance.artistEl.style.display = artist ? '' : 'none';\n\t}\n\n\tif ( typeof instance.syncArtwork === 'function' ) {\n\t\tinstance.syncArtwork(\n\t\t\tplayerArtwork || null,\n\t\t\tplayerArtwork ? imageAlt || '' : ''\n\t\t);\n\t} else if ( instance.artworkEl && playerArtwork ) {\n\t\tinstance.artworkEl.src = playerArtwork;\n\t\tinstance.artworkEl.alt = imageAlt || '';\n\t}\n\tif ( showPlayButtonArtwork ) {\n\t\tsetupPlayButtonArtwork( container, image );\n\t}\n}\n\n/**\n * A reusable WaveformPlayer component for the block editor.\n *\n * Renders an audio waveform visualization with play/pause controls.\n * Automatically inherits colors from the parent block's text color.\n *\n * @param {Object} props - Component props.\n * @param {string} props.src - The audio file URL.\n * @param {string} props.title - The track title.\n * @param {string} props.artist - The artist name.\n * @param {string} props.image - The track image URL.\n * @param {string} props.imageAlt - The track image alt text.\n * @param {string} props.color - The waveform color.\n * @param {string} props.gradient - The waveform gradient.\n * @param {string} props.backgroundColor - The waveform background color.\n * @param {string} props.backgroundGradient - The waveform background gradient.\n * @param {string} props.textColor - The player text color.\n * @param {string} props.waveformStyle - Waveform style (bars, mirror, line, blocks, dots, seekbar).\n * @param {Function} props.onEnded - Callback when the track finishes playing.\n * @param {boolean} props.showPlayButtonArtwork - Whether to show artwork on the play button.\n * @return {Element} The WaveformPlayer element.\n */\nexport function WaveformPlayer( {\n\tsrc,\n\ttitle,\n\tartist,\n\timage,\n\timageAlt,\n\tcolor,\n\tgradient,\n\tbackgroundColor,\n\tbackgroundGradient,\n\ttextColor,\n\twaveformStyle,\n\tonEnded,\n\tshowPlayButtonArtwork = false,\n} ) {\n\t// Store onEnded in a stable callback so it doesn't need to be a useRefEffect dependency.\n\t// The callback changes reference on every render (its dependency chain\n\t// includes an unstable array), which would cause useRefEffect to destroy\n\t// and recreate the entire player on every re-render, making it disappear\n\t// during editor resizes.\n\tconst onEndedEvent = useEvent( onEnded );\n\n\t// Ref for the WaveformPlayer instance\n\tconst playerRef = useRef();\n\n\t// WaveformPlayer needs an audio source on init, but the source may change\n\t// throughout its lifetime.\n\tconst hasSrc = !! src;\n\n\t// Combined props ref for `initWaveformPlayer`, which is called\n\t// asynchronously after this component mounts.\n\tconst metadataRef = useRef( { src, title, artist, image, imageAlt } );\n\tconst stylesRef = useRef( {\n\t\tcolor,\n\t\tgradient,\n\t\tbackgroundColor,\n\t\tbackgroundGradient,\n\t\ttextColor,\n\t} );\n\tuseEffect( () => {\n\t\tmetadataRef.current = { src, title, artist, image, imageAlt };\n\t}, [ src, title, artist, image, imageAlt ] );\n\n\tuseEffect( () => {\n\t\tstylesRef.current = {\n\t\t\tcolor,\n\t\t\tgradient,\n\t\t\tbackgroundColor,\n\t\t\tbackgroundGradient,\n\t\t\ttextColor,\n\t\t};\n\t}, [ color, gradient, backgroundColor, backgroundGradient, textColor ] );\n\n\tuseEffect( () => {\n\t\tif ( playerRef.current?.container ) {\n\t\t\tapplyWaveformPlayerStyles( playerRef.current.container, {\n\t\t\t\tbackgroundColor,\n\t\t\t\tbackgroundGradient,\n\t\t\t\ttextColor,\n\t\t\t\tplayButtonColor: showPlayButtonArtwork ? undefined : color,\n\t\t\t\tplayButtonGradient: showPlayButtonArtwork\n\t\t\t\t\t? undefined\n\t\t\t\t\t: gradient,\n\t\t\t} );\n\t\t}\n\t}, [\n\t\tbackgroundColor,\n\t\tbackgroundGradient,\n\t\tcolor,\n\t\tgradient,\n\t\tshowPlayButtonArtwork,\n\t\ttextColor,\n\t] );\n\n\tconst ref = useRefEffect(\n\t\t( element ) => {\n\t\t\tif ( ! hasSrc ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tlet cancelled = false;\n\t\t\tlet playerDestroy;\n\n\t\t\tfunction init() {\n\t\t\t\tif ( cancelled ) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst player = initWaveformPlayer( element, {\n\t\t\t\t\tsrc: metadataRef.current.src,\n\t\t\t\t\ttitle: metadataRef.current.title,\n\t\t\t\t\tartist: metadataRef.current.artist,\n\t\t\t\t\timage: metadataRef.current.image,\n\t\t\t\t\timageAlt: metadataRef.current.imageAlt,\n\t\t\t\t\twaveformColor: stylesRef.current.color,\n\t\t\t\t\twaveformGradient: stylesRef.current.gradient,\n\t\t\t\t\tbackgroundColor: stylesRef.current.backgroundColor,\n\t\t\t\t\tbackgroundGradient: stylesRef.current.backgroundGradient,\n\t\t\t\t\ttextColor: stylesRef.current.textColor,\n\t\t\t\t\twaveformStyle,\n\t\t\t\t\tlabels: {\n\t\t\t\t\t\tseek: __( 'Seek' ),\n\t\t\t\t\t\t/* translators: %1$s: current audio time, %2$s: total audio duration. */\n\t\t\t\t\t\tseekValueText: _x(\n\t\t\t\t\t\t\t'%1$s of %2$s',\n\t\t\t\t\t\t\t'audio current time of total duration'\n\t\t\t\t\t\t),\n\t\t\t\t\t},\n\t\t\t\t\tonEnded: () => onEndedEvent?.(),\n\t\t\t\t\tshowPlayButtonArtwork,\n\t\t\t\t} );\n\t\t\t\tplayerRef.current = player;\n\t\t\t\tconst { destroy } = player;\n\t\t\t\tplayerDestroy = destroy;\n\t\t\t}\n\n\t\t\t// Defer initialization so the element inherits the correct\n\t\t\t// text color, which is used to derive waveform colors. In the\n\t\t\t// editor iframe, theme styles (CSS custom properties) are\n\t\t\t// injected dynamically, so getComputedStyle may return the\n\t\t\t// default black on first render.\n\t\t\t// Using a requestAnimationFrame loop isn't sufficient to solve the issue.\n\t\t\t// TODO - find a better option than a setTimeout, so we're not relying on an arbitrary number.\n\t\t\tconst timeoutId = setTimeout( init, 100 );\n\n\t\t\treturn () => {\n\t\t\t\tcancelled = true;\n\t\t\t\tclearTimeout( timeoutId );\n\t\t\t\tplayerRef.current = undefined;\n\t\t\t\tplayerDestroy?.();\n\t\t\t};\n\t\t},\n\t\t[\n\t\t\tonEndedEvent,\n\t\t\thasSrc,\n\t\t\twaveformStyle,\n\t\t\tcolor,\n\t\t\tgradient,\n\t\t\ttextColor,\n\t\t\tshowPlayButtonArtwork,\n\t\t]\n\t);\n\n\tuseEffect( () => {\n\t\tif ( playerRef.current?.instance ) {\n\t\t\tconst player = playerRef.current;\n\t\t\tif ( player ) {\n\t\t\t\tupdatePlayerMetadata(\n\t\t\t\t\tplayer,\n\t\t\t\t\t{\n\t\t\t\t\t\ttitle,\n\t\t\t\t\t\tartist,\n\t\t\t\t\t\timage,\n\t\t\t\t\t\timageAlt,\n\t\t\t\t\t},\n\t\t\t\t\tshowPlayButtonArtwork\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}, [ title, artist, image, imageAlt, showPlayButtonArtwork ] );\n\n\tuseEffect( () => {\n\t\tif ( src && playerRef.current?.instance ) {\n\t\t\tconst wasPlaying = playerRef.current.instance.isPlaying;\n\t\t\tconst promise = playerRef.current.instance.loadTrack(\n\t\t\t\tsrc,\n\t\t\t\tmetadataRef.current.title,\n\t\t\t\tmetadataRef.current.artist,\n\t\t\t\t{\n\t\t\t\t\tartwork: showPlayButtonArtwork\n\t\t\t\t\t\t? undefined\n\t\t\t\t\t\t: metadataRef.current.image,\n\t\t\t\t\tartworkAlt: showPlayButtonArtwork\n\t\t\t\t\t\t? ''\n\t\t\t\t\t\t: metadataRef.current.imageAlt,\n\t\t\t\t}\n\t\t\t);\n\t\t\tpromise.then( () => {\n\t\t\t\tif ( showPlayButtonArtwork && playerRef.current?.container ) {\n\t\t\t\t\tsetupPlayButtonArtwork(\n\t\t\t\t\t\tplayerRef.current.container,\n\t\t\t\t\t\tmetadataRef.current.image\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif ( ! wasPlaying ) {\n\t\t\t\t\tplayerRef.current?.instance.pause();\n\t\t\t\t}\n\t\t\t} );\n\t\t}\n\t}, [ src, showPlayButtonArtwork ] );\n\n\treturn <div ref={ ref } className=\"wp-block-playlist__waveform-player\" />;\n}\n"],
"mappings": "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",
"names": []
}