velora-player/assets/js/block-editor.js
2026-03-23 16:55:03 +01:00

222 lines
6.8 KiB
JavaScript

( function( wp ) {
const { __ } = wp.i18n;
const { getBlockType, registerBlockType } = wp.blocks;
const { createElement: el, Fragment, useState } = wp.element;
const { InspectorControls, MediaUpload, MediaUploadCheck } = wp.blockEditor;
const { PanelBody, TextControl, TextareaControl, Button, ToggleControl, SelectControl, BaseControl, Notice } = wp.components;
const ServerSideRender = wp.serverSideRender;
const themeOptions = [
{ label: __( 'Modern Dark', 'modern-audio-player' ), value: 'modern-dark' },
{ label: __( 'Glassmorphism', 'modern-audio-player' ), value: 'glassmorphism' },
{ label: __( 'Podcast Style', 'modern-audio-player' ), value: 'podcast-style' }
];
function getAudioUrlWarning( value ) {
if ( ! value ) {
return '';
}
try {
const url = new window.URL( value, window.location.origin );
const hostname = url.hostname.toLowerCase();
const pathname = url.pathname.toLowerCase();
if ( [ 'youtube.com', 'www.youtube.com', 'youtu.be', 'soundcloud.com', 'open.spotify.com', 'music.apple.com' ].includes( hostname ) ) {
return __( 'This URL looks like a page, not a direct audio file. Please use a direct MP3 or other audio file URL.', 'modern-audio-player' );
}
if ( /\.(html?|php)$/i.test( pathname ) ) {
return __( 'This URL does not look like a direct audio file. Please confirm it points directly to the audio asset.', 'modern-audio-player' );
}
} catch ( error ) {
return __( 'Please enter a valid audio URL.', 'modern-audio-player' );
}
return '';
}
function formatPlaylistText( playlist ) {
if ( ! Array.isArray( playlist ) || ! playlist.length ) {
return '';
}
return playlist.map( function( track ) {
return [
track.title || '',
track.src || '',
track.image || ''
].join( ' | ' );
} ).join( '\n' );
}
function parsePlaylistText( value ) {
return String( value || '' )
.split( /\r\n|\r|\n/ )
.map( function( line ) {
return line.trim();
} )
.filter( function( line ) {
return line.length > 0;
} )
.map( function( line ) {
const parts = line.split( '|' ).map( function( part ) {
return part.trim();
} );
return {
title: parts[ 0 ] || '',
src: parts[ 1 ] || '',
image: parts[ 2 ] || ''
};
} )
.filter( function( track ) {
return !! track.src;
} );
}
const blockSettings = {
edit: function( props ) {
const attributes = props.attributes;
const setAttributes = props.setAttributes;
const initialPlaylistText = formatPlaylistText( attributes.playlist || [] );
const playlistState = useState( initialPlaylistText );
const playlistText = playlistState[ 0 ];
const setPlaylistText = playlistState[ 1 ];
const firstPlaylistTrack = Array.isArray( attributes.playlist ) && attributes.playlist.length ? attributes.playlist[ 0 ] : null;
const audioUrlWarning = getAudioUrlWarning( attributes.src || ( firstPlaylistTrack && firstPlaylistTrack.src ) || '' );
function onSelectAudio( media ) {
setAttributes( {
src: media && media.url ? media.url : '',
title: attributes.title || ( media && media.title ? media.title : '' )
} );
}
function onSelectImage( media ) {
setAttributes( {
image: media && media.url ? media.url : ''
} );
}
return el(
Fragment,
{},
el(
InspectorControls,
{},
el(
PanelBody,
{ title: __( 'Player Content', 'modern-audio-player' ), initialOpen: true },
el( TextControl, {
label: __( 'Track Title', 'modern-audio-player' ),
value: attributes.title || '',
onChange: function( value ) {
setAttributes( { title: value } );
}
} ),
el( TextControl, {
label: __( 'Audio Source URL', 'modern-audio-player' ),
value: attributes.src || '',
help: __( 'Paste a direct audio URL or choose one from the media library below. This remains the fallback single-track source.', 'modern-audio-player' ),
onChange: function( value ) {
setAttributes( { src: value } );
}
} ),
audioUrlWarning ? el( Notice, {
status: 'warning',
isDismissible: false
}, audioUrlWarning ) : null,
el(
BaseControl,
{ label: __( 'Audio File', 'modern-audio-player' ) },
el(
MediaUploadCheck,
{},
el( MediaUpload, {
onSelect: onSelectAudio,
allowedTypes: [ 'audio' ],
render: function( renderProps ) {
return el(
Button,
{ variant: 'secondary', onClick: renderProps.open },
attributes.src ? __( 'Replace Audio File', 'modern-audio-player' ) : __( 'Choose Audio File', 'modern-audio-player' )
);
}
} )
)
),
el(
BaseControl,
{ label: __( 'Cover Image', 'modern-audio-player' ) },
el(
MediaUploadCheck,
{},
el( MediaUpload, {
onSelect: onSelectImage,
allowedTypes: [ 'image' ],
render: function( renderProps ) {
return el(
Button,
{ variant: 'secondary', onClick: renderProps.open },
attributes.image ? __( 'Replace Cover Image', 'modern-audio-player' ) : __( 'Choose Cover Image', 'modern-audio-player' )
);
}
} )
)
),
el( TextareaControl, {
label: __( 'Playlist Tracks', 'modern-audio-player' ),
help: __( 'One track per line in this format: Title | Audio URL | Image URL. If empty, the single-track fields above are used.', 'modern-audio-player' ),
value: playlistText,
onChange: function( value ) {
setPlaylistText( value );
setAttributes( { playlist: parsePlaylistText( value ) } );
}
} )
),
el(
PanelBody,
{ title: __( 'Theme', 'modern-audio-player' ), initialOpen: false },
el( ToggleControl, {
label: __( 'Inherit Global Theme', 'modern-audio-player' ),
checked: !! attributes.useGlobalTheme,
onChange: function( value ) {
setAttributes( { useGlobalTheme: value } );
}
} ),
el( SelectControl, {
label: __( 'Theme Override', 'modern-audio-player' ),
value: attributes.theme || 'modern-dark',
options: themeOptions,
disabled: !! attributes.useGlobalTheme,
onChange: function( value ) {
setAttributes( { theme: value } );
}
} )
)
),
el(
'div',
{ className: 'map-editor-preview' },
el( ServerSideRender, {
block: props.name,
attributes: attributes
} )
)
);
},
save: function() {
return null;
}
};
if ( ! getBlockType( 'velora/audio-player' ) ) {
registerBlockType( 'velora/audio-player', blockSettings );
}
if ( ! getBlockType( 'map/audio-player' ) ) {
registerBlockType( 'map/audio-player', blockSettings );
}
} )( window.wp );