npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

react-list-player

v1.0.6

Published

A flexible react playlist component

Downloads

132

Readme

[!WARNING] This project's name was recently changed from react-playlist-player to react-list-player to distinguish it from another project that goes by the same name.

react-list-player Static Badge

A flexible playlist react component

Description

This package centers around the ListPlayer component which is a react component that provides a user interface to control playback of a list of media items.

ListPlayer is fully responsive, and will, by default, adapt to the size of its container element. It also comes with several built-in sizes that the component can "snap" to or smoothly switched between.

The main reason behind not including some form of media player is to avoid limiting the use cases to those attached to specific media types or sources. A playlist can be a list of videos, a list of songs, a list of sound clips, or a mix of everything.

Another reason is that developers may prefer to use their own media player component, or may want to use a different media player component for different media types.

ListPlayer also has a headless mode where only the interactive playlist is presented; the included playback controls (play/pause, prev/next, mute/unmute) are removed. This allows developers to use their own separate playback controls. This allows for more flexibility in the design of the user interface. Many sites for example, have a fixed playback control bar at the bottom of the page, and a separate playlist component elsewhere on the page.

Installation

This package has been published to npm under the name react-list-player.

You can install it using the following command:

npm install react-list-player

You can also install it directly from GitHub using the following command:

npm install bouzidanas/react-list-player

If you want to work straight from the source code, you will need to fork the repository and clone it to your local machine. Then you will need to make sure it is in "demo-mode" and not "lib-mode". To do this, just run the command:

npm run demo

from the root directory of the repository. As a test, you can run the command npm run dev to launch the demo app.

[!NOTE] Versions up to and including Version 1.0.0 use TailwindCSS. If you are using a later version, you can skip the tailwindCSS installation and configuration instructions.

TailwindCSS installation and configuration

Version of the package uses tailwindcss. To get the components to appear properly, you need to have tailwind installed in the project where you will be using react-list-player components. See tailwindCSS documentation for installation instructions.

After installing tailwindCSS, you need to add the following string to the content array in your projects tailwind.config.js file:

"./node_modules/react-list-player/**/*.{js,ts,jsx,tsx}"

This will allow tailwind to scan the react-list-player components for classes that you can use in your project. Your tailwind.config.js file should look something like this:

/** @type {import('tailwindcss').Config} */
export default {
  content: [
    "./index.html",
    "./src/**/*.{js,ts,jsx,tsx}",
+    "./node_modules/react-list-player/**/*.{js,ts,jsx,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}

Usage

Below is a basic skeleton of how to use the ListPlayer component.

import { useState } from 'react';
import { ListPlayer, ListPlayerContext } from 'react-list-player';

// The following test object has type ListInfo which is defined in the ListPlayer file
const testListInfo = {}; // object needed to populate the playlist header with

// The following test array has type Track which is defined in the ListPlayer file
const testTracks = [...]; // array of track objects needed populate the playlist rows with

function App() {
  const [selectedTrack, setSelectedTrack] = useState(-1);   // -1 means no track is selected
  const [isPlaying, setIsPlaying] = useState(false);        // play/pause
  const [isMuted, setIsMuted] = useState(false);            // mute/unmute

  return (
    <ListPlayerContext.Provider value={{selectedTrack, setSelectedTrack, isPlaying, setIsPlaying, isMuted, setIsMuted}}>
      <div className='container-for-sizing-player'>
        <ListPlayer 
          tracks={testTracks} 
          listInfo={testListInfo}
        />
      </div>
    </ListPlayerContext.Provider>
  )
}

In the example above, <ListPlayer> is wrapped in a <ListPlayerContext.Provider> component. This is necessary for the <ListPlayer> component to work properly. The <ListPlayer> component is designed to share context with components external to it. This allows the developer to synchronize the state of the <ListPlayer> component with other components such as a media player component. In general, it allows the application to control and respond to changes in the <ListPlayer> component and vice versa.

The <ListPlayer> component responds to changes in the three shared context variables: selectedTrack, isPlaying, and isMuted. If the value of selectedTrack is -1, this means no track is selected. This is usually the case when the playlist is first rendered. If you change the value of selectedTrack to a valid index, the playlist will automatically scroll to that track and change the isPlaying state. Thus, the isPlaying state variable can be used to trigger pausing and playing of the media item.

Example: Responding to ListPlayer via changes in shared context variables

import { useState } from 'react';
import { ListPlayer, ListPlayerContext } from 'react-list-player';

const testListInfo = {/*...*/};

const testTracks = [/*...*/]; 

function App() {
  const [selectedTrack, setSelectedTrack] = useState(-1);   // -1 means no track is selected
  const [isPlaying, setIsPlaying] = useState(false);        // play/pause
  const [isMuted, setIsMuted] = useState(false);            // mute/unmute

  const audioRef = useRef<HTMLAudioElement>(null);

  const audioSrcs = ["/audio/Sos.mp3", "/audio/Fields of Blue.mp3", "/audio/Forbidden Doors.mp3", "/audio/Show Me How.mp3", "/audio/I Dont Know You.mp3"];

  // The following handler is needed only for the case where the
  // ListPlayer is requesting the current track to be restarted
  const handleOnPlay = (index:number, resume:boolean) => {
    if(index === selectedTrack && !resume) {
      audioRef.current?.load();
      audioRef.current?.play();
    }
  }

  useEffect(() => {
    if(audioRef.current) {
      if(isPlaying) {
        audioRef.current?.play();
      } else {
        audioRef.current?.pause();
      }
    }
  }, [isPlaying, selectedTrack]);

  return (
    <ListPlayerContext.Provider value={{selectedTrack, setSelectedTrack, isPlaying, setIsPlaying, isMuted, setIsMuted}}>
      <div className='container-for-sizing-player'>
        <ListPlayer  
          tracks={testTracks} 
          listInfo={testListInfo} 
          playCallback={handleOnPlay} 
        />
      </div>
      <audio 
        ref={audioRef} 
        src={audioSrcs[selectedTrack]}
        muted={isMuted} 
        onEnded={() => {setSelectedTrack(selectedTrack + 1)}}
      />
    </ListPlayerContext.Provider>
  )
}

Alternatively, if <ListPlayer> is given a playCallback function, it will call that function with the index of the selected track and a flag indicating whether to resume playback or start playback from the beginning. You can also give <ListPlayer> a pauseCallback function that will be called when playback should be paused. Finally, there is also a muteCallback function that is called when the mute/unmute button is clicked.

Example: Responding to ListPlayer via callback functions

import { useState } from 'react';
import { ListPlayer, ListPlayerContext } from 'react-list-player';

const testListInfo = {/*...*/};

const testTracks = [/*...*/]; 

function App() {
  const [selectedTrack, setSelectedTrack] = useState(-1);   // -1 means no track is selected
  const [isPlaying, setIsPlaying] = useState(false);        // play/pause
  const [isMuted, setIsMuted] = useState(false);            // mute/unmute

  const audioRef = useRef<HTMLAudioElement>(null);

  const audioSrcs = ["/audio/Sos.mp3", "/audio/Fields of Blue.mp3", "/audio/Forbidden Doors.mp3", "/audio/Show Me How.mp3", "/audio/I Dont Know You.mp3"];

  const handleOnPlay = (index:number, resume:boolean) => {
    if(index === selectedTrack && !resume) {
      audioRef.current?.load();
      audioRef.current?.play();
    } else {
      audioRef.current?.play();
    }
  }

  const handleOnPause = () => {
    audioRef.current?.pause();
  }

  return (
    <ListPlayerContext.Provider value={{selectedTrack, setSelectedTrack, isPlaying, setIsPlaying, isMuted, setIsMuted}}>
      <div className='container-for-sizing-player'>
        <ListPlayer  
          tracks={testTracks} 
          listInfo={testListInfo} 
          playCallback={handleOnPlay} 
          pauseCallback={handleOnPause}
        />
      </div>
      <audio 
        ref={audioRef} 
        src={audioSrcs[selectedTrack]}
        muted={isMuted} 
        onEnded={() => {setSelectedTrack(selectedTrack + 1)}}
      />
    </ListPlayerContext.Provider>
  )
}

Responding via callbacks (second example) is the recommended approach of the two shown above.

Currently, it is recommended to use callbacks when you need the parent app to respond to the ListPlayer and to use the shared state variables to have the parent app control the ListPlayer. In regards to the latter, just remember that when you change any of the state variables at the top level, the ListPlayer will respond to reflect those changes. However, the ListPlayer will not in turn call the callbacks (since that would be redundant and also because the "command" didnt come directly from the user interacting with ListPlayer elements...like clicking the play button). So you have to directly perform the actions that the callbacks would have performed (if that is desired behavior).

Data props

ListPlayer has two props that are used to populate the playlist: tracks and listInfo. The tracks prop is an array of objects that contain information about each track. The listInfo prop is an object that contains information about the playlist as a whole. Both of these props are optional only to allow easier testing (if not provided, then test data is used). In practice, you will need to provide both props.

tracks prop

The tracks prop is an array of objects that contain information about each track. The track type is defined (in ListPlayer.tsx) as follows:

type track = {
    title: playerText[];       // title of track
    artist: playerText[];      // artist of track
    album: playerText[];       // album of track
    duration: string;          // duration of track
    src?: string;              // audio url or path
    imageSrc?: string;         // album cover image url or path
    meta?: trackInfo;          // additional track info
}

The title, artist, and album properties are arrays of playerText objects (to differentiate regular text from text that will be treated as badges). The playerText type is defined as follows:

type playerText = text | badge;

type text = {
    type: 'text';
    content: string;            //text to display
    link?: string;              //link to open on click
    externalLink?: boolean;     //open link in new tab
    className?: string;         //classes to apply to the span that contains the text
    style?: CSSProperties;      //styles to apply to the span that contains the text
}

type badge = {
    type: 'badge';
    content: string;            //text to display
    className?: string;         //classes to apply to the span that contains the text
    style?: CSSProperties;      //styles to apply to the span that contains the text
}
Example: Track array containing one track
[
  {
    title: [
      {
        type: 'text',
        content: 'Sos',
        className: 'title'
      },
      {
        type: 'badge',
        content: 'New',
        className: 'new'
      }
    ],
    artist: [
      {
        type: 'text',
        content: 'Timothy Fleet',
        className: 'artist',
        link: 'https://music.youtube.com/channel/UCmGqnW6VmhOV4KW67vhzPCA'
      },
      {
        type: 'text',
        content: '&'
      },
      {
        type: 'text',
        content: 'Wayne Murray and company',
        className: 'artist',
        link: 'https://music.youtube.com/channel/UCkZXltuX3Rta9OiD-O505xg'
      }
    ],
    album: [
      {
        type: 'text',
        content: 'Vintage Radio: 1980s',
        className: 'album'
      },
      {
        type: 'badge',
        content: 'Explicit',
        className: 'explicit'
      }
    ],
    duration: "2:37"
  }
]

listInfo prop

The listInfo prop is an object that contains information about the playlist as a whole. This information will be displayed in the header of the playlist. The ListInfo type is defined (in ListPlayer.tsx) as follows:

type listInfo = playlistInfo | albumInfo | artistInfo;

type playlistInfo = {
    type: 'playlist'
    name: string;
    numTracks: number;
    duration: string;
    creationDate?: string;
    imageSrc?: string;
}

type albumInfo = {
    type: 'album';
    name: string;
    numTracks: number;
    duration: string;
    releaseDate?: string;
    genre?: string;
    imageSrc?: string;
}

type artistInfo = {
    type: 'artist';
    name: string;
    numTracks: number;
    numAlbums: number;
    genre?: string;
    imageSrc?: string;
}

Note that there are three different playlist info types: playlistInfo, albumInfo, and artistInfo. This is because the playlist header can display different information depending on the type of playlist. artistInfo should be used for a playlist of content from same source (like a person or a studio). albumInfo should be used for a playlist of content from the same collection. playlistInfo should be used for a playlist of content from multiple sources and/or collections.

Example: Playlist info object
{
  type: 'playlist',
  name: 'My Playlist',
  numTracks: 1,
  duration: '3:37',
  creationDate: '2021-08-01',
  imageSrc: '/images/playlist.jpg'
}

Additional Features

An optional feature of <ListPlayer> is the ability to replace the default header with a custom header. This is done by passing a component as a child of <ListPlayer>. The component will be rendered in place of the default header. Note that you can embed your media player component in your custom header component although it is not necessary.

Another optional feature is the ability to remove the playback controls from the header. This is done by passing the noControls prop to <ListPlayer>. This is useful if you want to use your own playback controls.

Finally, there is a headless mode where only the playlist is rendered. This is done by passing the noHeader prop to <ListPlayer>. This is useful if you want to keep the playback controls in a separate location from the playlist.

Demo

[!NOTE] The tracks used in the demo are protected by copywrite. The audio files are omitted from the repository and the demo site. You can run the demo locally by cloning the repository and adding your own audio files to the public/audio folder. Make sure to also add the file names to the audioSrcs array if you are using the example code from the previous section.

Static Badge :arrow_left: (Click here to see a demo of the ListPlayer component)

License

Static Badge