Skip to content

Commit

Permalink
[DPCP-31] Audio Player (#10)
Browse files Browse the repository at this point in the history
* ar(feat) DPCP-31: Audio Player

* ar(feat) DPCP-31: Audio Player

* ar(feat) DPCP-31: Audio Player

* ar(feat) DPCP-31: Audio Player

* ar(feat) DPCP-31: Audio Player

* ar(feat) DPCP-31: Audio Player

* ar(feat) DPCP-31: Audio Player

* ar(feat) DPCP-31: Audio Player

* ar(feat) DPCP-31: Audio Player

* ar(feat) DPCP-31: Audio Player

* ar(feat) DPCP-31: Audio Player

* ar(feat) DPCP-31: Audio Player

* ar(feat) DPCP-31: Audio Player

* ar(feat) DPCP-31: Audio Player

* ar(feat) DPCP-31: Audio Player

* ar(feat) DPCP-31: Audio Player

* ar(feat) DPCP-31: Audio Player

* ar(feat) DPCP-31: Audio Player
  • Loading branch information
angeloreale authored Aug 4, 2024
1 parent 623ce0e commit 976e504
Show file tree
Hide file tree
Showing 6 changed files with 285 additions and 0 deletions.
177 changes: 177 additions & 0 deletions src/molecules/02_AudioPlayer/AudioPlayer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
/* eslint jsx-a11y/media-has-caption:0, no-nested-ternary:0, no-unused-vars:0, max-len:0, no-shadow:0, @typescript-eslint/no-explicit-any:0, object-curly-newline:0 */
// @atoms/AudioPlayer.tsx
import clsx from 'clsx';
import { Fragment, useRef, useState, useEffect } from 'react';
import Grid, {
EBleedVariant,
EGridVariant,
} from '../../atoms/10_Grid/Grid.tsx';

import { Button, ButtonVariant, EButtonTheme } from '../../atoms/01_Button';
import { Typography } from '../../atoms/02_Typography';
import { ESystemIcon } from '../../atoms/05_SystemIcon';

export const DEFAULT_TRACKS = [
{
id: 'dreampip__chan_0000',
className: '',
onPlay: () => {},
title: 'This is the track playing',
url: 'https://www.dreampip.com/api/nexus/audio',
isPlaying: false,
},
{
id: 'dreampip__chan_0001',
className: '',
onPlay: () => {},
title: 'This is the track playing',
url: 'https://www.dreampip.com/api/nexus/audio/1',
isPlaying: false,
},
];

export enum EAudioPlayerVariant {
DEFAULT = 'default',
}

export interface IBackgroundImage {
mobile?: string;
desktop?: string;
}

export interface IAudioTrack {
id?: string;
className?: string;
onPlay?: () => void;
title?: string;
url?: string;
isPlaying?: boolean;
}

export interface IAudioPlayer {
id?: string;
className?: string;
tracks?: IAudioTrack[];
onPlayTrack?: () => void;
nativeControls?: boolean;
prompt?: string;
theme?: 'light' | 'dark';
}

export const HAudioPlayer = function ({
id = 'atom__AudioPlayer',
className = '',
tracks = DEFAULT_TRACKS,
onPlayTrack = () => {},
nativeControls = false,
prompt = 'Rotation portals',
theme = 'light',
}: IAudioPlayer) {
const audioElement = useRef<HTMLAudioElement>(null);
const [status, setStatus] = useState('stopped');
const [title, setTitle] = useState(prompt);
const gridSx = [
{
[`class02
grid
sm:grid-cols-12
sm:!gap-a0
md:!gap-a1
content-center
items-center
align-center
justify-center
`]: true,
},
];

const gridStyles = `${clsx(gridSx)} ${className}`;

const handlePlay = () => {
if (!audioElement.current) return;
onPlayTrack();
const isPlaying = status === 'playing';

if (isPlaying) {
audioElement.current?.pause();
audioElement.current.currentTime = 0;
setStatus('stopped');
} else {
audioElement.current?.play();
setStatus('playing');
}
};

const handleStatus = (status: string, options: { title?: string }) => () => {
setStatus(status);
setTitle(options?.title || prompt);
};

useEffect(() => {
const handlePlay = handleStatus('playing', {
title: audioElement.current?.getAttribute('data-title') || prompt,
});
const handleStop = handleStatus('stopped', {});
audioElement.current?.addEventListener('play', handlePlay);
audioElement.current?.addEventListener('ended', handleStop);

return () => {
audioElement.current?.removeEventListener('play', handlePlay);
audioElement.current?.removeEventListener('ended', handleStop);
};
}, [status]);

return (
<Grid
theme={theme}
id={id}
className={gridStyles}
variant={EGridVariant.TWELVE_COLUMNS}
bleed={EBleedVariant.RESPONSIVE}
>
<div className="flex items-center justify-center col-span-full sm:col-span-1 lg:col-span-1 col-start-0">
<Button
className="w-full"
theme={theme}
icon={status === 'playing' ? ESystemIcon.stop : ESystemIcon.play}
variant={ButtonVariant.FILLED}
buttonTheme={EButtonTheme.PRIMARY}
onClick={handlePlay}
/>
<audio
src={tracks[0].url}
controls={nativeControls}
ref={audioElement}
autoPlay
preload="none"
>
{tracks.map((file) => (
<>
<source
key={`molecules__AudioPlayer__element-source-${file.title}`}
src={file.url}
type="audio/mpeg"
/>
<track
kind="subtitles"
key={`molecules__AudioPlayer__element-track-${file.title}`}
data-title={file.title}
srcLang="en"
label="English"
src={file.url}
/>
</>
))}
</audio>
</div>
<Typography
className="col-span-full sm:col-span-6 lg:col-span-4 col-start-0"
truncate
>
{title}
</Typography>
</Grid>
);
};

export default HAudioPlayer;
28 changes: 28 additions & 0 deletions src/molecules/02_AudioPlayer/__docs__/02-AudioPlayer.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// @atoms/02_AudioPlayer/__test__/02-AudioPlayer.stories.tsx
import type { Meta, StoryObj } from '@storybook/react';
import Example from './Example.tsx';
import { EAudioPlayerVariant } from '../AudioPlayer';

const meta: Meta<typeof Example> = {
title: 'Molecules/02-AudioPlayer',
component: Example,
argTypes: {
theme: {
options: ['light', 'dark'],
control: { type: 'radio' },
},
variant: {
options: Object.values(EAudioPlayerVariant),
control: { type: 'radio' },
},
},
};

export default meta;
type Story = StoryObj<typeof Example>;

export const Default: Story = {
args: {
variant: EAudioPlayerVariant.DEFAULT,
},
};
55 changes: 55 additions & 0 deletions src/molecules/02_AudioPlayer/__docs__/AudioPlayer.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { Canvas, Meta } from "@storybook/blocks";
import Example from "./Example.tsx";
import * as AudioPlayer from "./02-AudioPlayer.stories.tsx";

<Meta of={AudioPlayer} title="AudioPlayer" />

# AudioPlayer

AudioPlayer component with different props.

#### Example

<Canvas of={AudioPlayer.Default} />

## Usage

```ts
import { AudioPlayer } from "@dreampipcom/oneiros";

export const DEFAULT_TRACKS = [
{
id: 'dreampip__chan_0000',
className: '',
onPlay: () => {},
title: 'This is the track playing',
url: 'https://www.dreampip.com/api/nexus/audio',
isPlaying: false,
},
{
id: 'dreampip__chan_0001',
className: '',
onPlay: () => {},
title: 'This is the track playing',
url: 'https://www.dreampip.com/api/nexus/audio/1',
isPlaying: false,
},
];

const App = () => {
return (
<AudioPlayer
tracks={DEFAULT_TRACKS}
/>
);
};

export default Example;
```

#### Arguments

- **tracks** — An array of Tracks props, where each index represents a Card in the grid.
- **onPlayTrack** — A function to run when the play button is clicked.
- **nativeControls** — A boolean to determine whether the native browser player should display.
- **prompt** — A string to show by the side of the player.
9 changes: 9 additions & 0 deletions src/molecules/02_AudioPlayer/__docs__/Example.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// @atoms/02_AudioPlayer/__test__/Example.tsx
import React, { FC } from 'react';
import AudioPlayer, { ICard } from '../AudioPlayer.tsx';

const Example: FC<ICard> = function ({ theme = 'light', cards }) {
return <AudioPlayer cards={cards} theme={theme} />;
};

export default Example;
13 changes: 13 additions & 0 deletions src/molecules/02_AudioPlayer/__test__/02-AudioPlayer.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// @atoms/02_Card/__test__/02-AudioPlayer.test.tsx
import React from 'react';
import { describe, expect, it } from 'vitest';
import { render } from '@testing-library/react';
import AudioPlayer from '../AudioPlayer';

describe('AudioPlayer component', () => {
it('AudioPlayer should render correctly', () => {
const result = render(<AudioPlayer id="grid-atom--test" />);
const component = result.container.querySelector('#grid-atom--test');
expect(component).toBeInTheDocument();
});
});
3 changes: 3 additions & 0 deletions src/molecules/02_AudioPlayer/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// @atoms/AudioPlayer/index.ts
export { default as AudioPlayer, EAudioPlayerVariant } from './AudioPlayer';
export const AudioPlayerName = 'AudioPlayer';

0 comments on commit 976e504

Please sign in to comment.