/** * Settings → Integrations → Blocks, shortcodes & template tags. * * One tile per page element: shortcode, block markup or PHP, with a copy button and the * element's options. Names, descriptions and fields come from the element registry * (`config.elements`); add-ons list extra shortcodes through `streamsera_admin_data` (`shortcodes`). */ import { __, sprintf } from '@wordpress/i18n'; import { useMemo, useState } from '@wordpress/element'; import { registerSettingsSection } from '../../player/extensions'; import { Section, I } from '../ui'; import { config } from '../api'; type Field = { key: string; label: string; type: string; default: string | number | boolean; options: Array< [ string, string ] >; }; type Element = { id: string; label: string; description: string; block: string; pro?: boolean; fields: Field[]; }; type Format = 'shortcode' | 'block' | 'php'; type Item = { id: string; label: string; description: string; cat: string; icon: string; shortcode: string; tag: string; block: string; fields: Field[]; }; const CATS: Record< string, { label: string; icon: string } > = { player: { label: __( 'Player', 'streamsera' ), icon: 'player' }, stations: { label: __( 'Stations', 'streamsera' ), icon: 'broadcast' }, schedule: { label: __( 'Schedule', 'streamsera' ), icon: 'calendar' }, hosts: { label: __( 'Hosts', 'streamsera' ), icon: 'listeners' }, more: { label: __( 'More', 'streamsera' ), icon: 'grid' }, }; /** Element id → [ category, icon ]. */ const META: Record< string, [ string, string ] > = { player: [ 'player', 'player' ], 'now-playing': [ 'player', 'music' ], 'listen-button': [ 'player', 'button' ], 'recently-played': [ 'player', 'history' ], history: [ 'player', 'search' ], charts: [ 'player', 'like' ], station: [ 'stations', 'broadcast' ], 'station-list': [ 'stations', 'grid' ], schedule: [ 'schedule', 'calendar' ], 'now-next': [ 'schedule', 'clock' ], show: [ 'schedule', 'megaphone' ], 'show-list': [ 'schedule', 'list' ], host: [ 'hosts', 'user' ], 'host-list': [ 'hosts', 'listeners' ], }; const ALIAS: Record< string, string > = { '': 'player', stations: 'station-list', shows: 'show-list', hosts: 'host-list', }; function elementId( tag: string ) { const id = tag.replace( /^streamsera_?/, '' ).replace( /_/g, '-' ); return ALIAS[ id ] ?? id; } /** * Attributes written in an example shortcode, e.g. [ [ 'style', 'card|horizontal' ] ]. * @param sc */ function attrs( sc: string ): Array< [ string, string ] > { const out: Array< [ string, string ] > = []; sc.replace( /(\w+)="([^"]*)"/g, ( _m, k, v ) => { out.push( [ k, v ] ); return ''; } ); return out; } function fieldFor( item: Item, attr: string ) { const keys = 'id' === attr ? [ 'player' ] : [ attr, `show_${ attr }`, `${ attr }s` ]; return item.fields.find( ( f ) => keys.includes( f.key ) ); } /** * The shortcode with only the chosen attributes. * @param item * @param on */ function build( item: Item, on: string[] ) { const parts = attrs( item.shortcode ) .filter( ( [ k ] ) => on.includes( k ) ) .map( ( [ k, v ] ) => `${ k }="${ v.split( '|' )[ 0 ] }"` ); return `[${ item.tag }${ parts.length ? ' ' + parts.join( ' ' ) : '' }]`; } function codeFor( item: Item, format: Format, sc: string ) { if ( 'block' === format ) { return item.block ? `` : ''; } if ( 'php' === format ) { return 'player' === item.id ? ` 'card' ] ); ?>` : ``; } return sc; } /** * Coloured shortcode: tag, attribute names and values. * @param root0 * @param root0.code * @param root0.format */ function Pretty( { code, format }: { code: string; format: Format } ) { if ( 'shortcode' !== format ) { return { code }; } const [ , head = '', rest = '' ] = code.match( /^\[(\S+)\s?(.*)\]$/ ) || []; const parts: any[] = []; let last = 0; rest.replace( /(\w+)="([^"]*)"/g, ( m, k, v, at ) => { parts.push( rest.slice( last, at ) ); parts.push( { k }= "{ v }" ); last = at + m.length; return m; } ); parts.push( rest.slice( last ) ); return ( <> [ { head } { rest ? ' ' : '' } { parts } ] > ); } function CopyBtn( { code, compact = false, }: { code: string; compact?: boolean; } ) { const [ done, setDone ] = useState( false ); const copy = async () => { try { await navigator.clipboard.writeText( code ); } catch { const ta = document.createElement( 'textarea' ); ta.value = code; document.body.appendChild( ta ); ta.select(); document.execCommand( 'copy' ); ta.remove(); } setDone( true ); setTimeout( () => setDone( false ), 1600 ); }; return ( ); } function Tile( { item, format, open, onToggle, }: { item: Item; format: Format; open: boolean; onToggle: () => void; } ) { const all = attrs( item.shortcode ); const defaults = all.map( ( [ k ] ) => k ); const [ on, setOn ] = useState< string[] >( defaults ); const sc = build( item, on ); const code = codeFor( item, format, sc ); const changed = on.join() !== defaults.join(); const flip = ( k: string ) => setOn( on.includes( k ) ? on.filter( ( x ) => x !== k ) : defaults.filter( ( x ) => x === k || on.includes( x ) ) ); const rows = all.map( ( [ k, v ] ) => { const f = fieldFor( item, k ); const values = v.includes( '|' ) ? v.split( '|' ).join( ' · ' ) : f?.options?.length ? f.options.map( ( o ) => o[ 0 ] ).join( ' · ' ) : ''; return { k, label: f?.label || k, values, val: v.split( '|' )[ 0 ], }; } ); const canOpen = rows.length > 0 && 'block' !== format; return (
{ __( 'Available as a shortcode only.', 'streamsera' ) }
) } { canOpen ? ( ) : null }
"{ r.val }"
{ __( 'Nothing matches that filter.', 'streamsera' ) }
) : null }