From 2fcbac574bf3b3cf476e05b632eb7fc708eee5a5 Mon Sep 17 00:00:00 2001 From: Angelo Reale <12191809+angeloreale@users.noreply.github.com> Date: Sun, 4 Aug 2024 18:41:14 +0100 Subject: [PATCH 01/18] ar(feat) DPCP-31: Audio Player --- src/atoms/05_SystemIcon/SystemIcon.tsx | 2 +- src/molecules/02_AudioPlayer/AudioPlayer.tsx | 106 ++++++++++++ .../__docs__/02-AudioPlayer.stories.tsx | 28 ++++ .../02_AudioPlayer/__docs__/AudioPlayer.mdx | 153 ++++++++++++++++++ .../02_AudioPlayer/__docs__/Example.tsx | 9 ++ .../__test__/02-AudioPlayer.test.tsx | 13 ++ src/molecules/02_AudioPlayer/index.ts | 3 + 7 files changed, 313 insertions(+), 1 deletion(-) create mode 100644 src/molecules/02_AudioPlayer/AudioPlayer.tsx create mode 100644 src/molecules/02_AudioPlayer/__docs__/02-AudioPlayer.stories.tsx create mode 100644 src/molecules/02_AudioPlayer/__docs__/AudioPlayer.mdx create mode 100644 src/molecules/02_AudioPlayer/__docs__/Example.tsx create mode 100644 src/molecules/02_AudioPlayer/__test__/02-AudioPlayer.test.tsx create mode 100644 src/molecules/02_AudioPlayer/index.ts diff --git a/src/atoms/05_SystemIcon/SystemIcon.tsx b/src/atoms/05_SystemIcon/SystemIcon.tsx index ae74c39..5d68dcb 100644 --- a/src/atoms/05_SystemIcon/SystemIcon.tsx +++ b/src/atoms/05_SystemIcon/SystemIcon.tsx @@ -2,7 +2,7 @@ // @atoms/SystemIcon.tsx import { useMemo } from 'react'; import clsx from 'clsx'; -import { DreamPipColors } from '../../../tailwind.config.ts'; +import { DreamPipColors } from '../../../dist/tailwind.config.ts'; import * as Icons from './assets'; type Theme = 'light' | 'dark'; diff --git a/src/molecules/02_AudioPlayer/AudioPlayer.tsx b/src/molecules/02_AudioPlayer/AudioPlayer.tsx new file mode 100644 index 0000000..5cc9260 --- /dev/null +++ b/src/molecules/02_AudioPlayer/AudioPlayer.tsx @@ -0,0 +1,106 @@ +/* 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 } from 'react'; +import Grid, { + EBleedVariant, + EGridVariant, +} from '../../atoms/10_Grid/Grid.tsx'; + +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; + theme?: 'light' | 'dark'; +} + +export const HAudioPlayer = function ({ + id = 'atom__AudioPlayer', + className = '', + tracks = DEFAULT_TRACKS, + onPlayTrack = () => {}, + theme = 'light', +}: IAudioPlayer) { + const gridSx = [ + { + [`class02 + grid + sm:grid-cols-12 + sm:!gap-a0 + md:!gap-a4 + `]: true, + }, + ]; + + const gridStyles = `${clsx(gridSx)} ${className}`; + + onPlayTrack(); + + return ( + + + + ); +}; + +export default HAudioPlayer; diff --git a/src/molecules/02_AudioPlayer/__docs__/02-AudioPlayer.stories.tsx b/src/molecules/02_AudioPlayer/__docs__/02-AudioPlayer.stories.tsx new file mode 100644 index 0000000..e0fcb97 --- /dev/null +++ b/src/molecules/02_AudioPlayer/__docs__/02-AudioPlayer.stories.tsx @@ -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 = { + 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; + +export const Default: Story = { + args: { + variant: EAudioPlayerVariant.DEFAULT, + }, +}; diff --git a/src/molecules/02_AudioPlayer/__docs__/AudioPlayer.mdx b/src/molecules/02_AudioPlayer/__docs__/AudioPlayer.mdx new file mode 100644 index 0000000..d4e4912 --- /dev/null +++ b/src/molecules/02_AudioPlayer/__docs__/AudioPlayer.mdx @@ -0,0 +1,153 @@ +import { Canvas, Meta } from "@storybook/blocks"; +import Example from "./Example.tsx"; +import * as AudioPlayer from "./02-AudioPlayer.stories.tsx"; + + + +# AudioPlayer + +AudioPlayer component with different props. + +#### Example + + + +## Usage + +```ts +import { AudioPlayer } from "@dreampipcom/oneiros"; + +export const DEFAULT_CARDS = [ + { + id: 'molecule__AudioPlayer__card--02', + className: '', + onLike: () => {}, + title: 'This is a card example #02', + where: 'Barcelona (Catalonia)', + when: 'February', + price: '299€', + link: 'https://dreampip.com', + badgeLink: 'https://dreampip.com', + rating: '3/5', + selected: false, + }, + { + id: 'molecule__AudioPlayer__card--02', + className: '', + onLike: () => {}, + title: 'This is a card example #02', + where: 'Barcelona (Catalonia)', + when: 'February', + price: '299€', + link: 'https://dreampip.com', + badgeLink: 'https://dreampip.com', + rating: '4.5/5', + selected: true, + }, + { + id: 'molecule__AudioPlayer__card--03', + className: '', + onLike: () => {}, + title: 'This is a card example #03', + where: 'Barcelona (Catalonia)', + when: 'February', + price: '299€', + link: 'https://dreampip.com', + badgeLink: 'https://dreampip.com', + rating: '3/5', + selected: false, + }, + { + id: 'molecule__AudioPlayer__card--04', + className: '', + onLike: () => {}, + title: 'This is a card example #04', + where: 'Barcelona (Catalonia)', + when: 'February', + price: '299€', + link: 'https://dreampip.com', + badgeLink: 'https://dreampip.com', + rating: '2/5', + selected: false, + }, + { + id: 'molecule__AudioPlayer__card--05', + className: '', + onLike: () => {}, + title: 'This is a card example #05', + where: 'Barcelona (Catalonia)', + when: 'February', + price: '299€', + link: 'https://dreampip.com', + badgeLink: 'https://dreampip.com', + rating: '3.5/5', + selected: false, + }, + { + id: 'molecule__AudioPlayer__card--06', + className: '', + onLike: () => {}, + title: 'This is a card example #06', + where: 'Barcelona (Catalonia)', + when: 'February', + price: '299€', + link: 'https://dreampip.com', + badgeLink: 'https://dreampip.com', + rating: '3.6/5', + selected: false, + }, + { + id: 'molecule__AudioPlayer__card--07', + className: '', + onLike: () => {}, + title: 'This is a card example #07', + where: 'Barcelona (Catalonia)', + when: 'February', + price: '299€', + link: 'https://dreampip.com', + badgeLink: 'https://dreampip.com', + rating: '5/5', + selected: false, + }, + { + id: 'molecule__AudioPlayer__card--08', + className: '', + onLike: () => {}, + title: 'This is a card example #08', + where: 'Barcelona (Catalonia)', + when: 'February', + price: '299€', + link: 'https://dreampip.com', + badgeLink: 'https://dreampip.com', + rating: '4.8/5', + selected: false, + }, + { + id: 'molecule__AudioPlayer__card--09', + className: '', + onLike: () => {}, + title: 'This is a card example #08', + where: 'Barcelona (Catalonia)', + when: 'February', + price: '299€', + link: 'https://dreampip.com', + badgeLink: 'https://dreampip.com', + rating: '3/5', + selected: false, + }, +]; + +const App = () => { + return ( + + ); +}; + +export default Example; +``` + +#### Arguments + +- **cards** — An array of Cards props, where each index represents a Card in the grid. diff --git a/src/molecules/02_AudioPlayer/__docs__/Example.tsx b/src/molecules/02_AudioPlayer/__docs__/Example.tsx new file mode 100644 index 0000000..cc59d21 --- /dev/null +++ b/src/molecules/02_AudioPlayer/__docs__/Example.tsx @@ -0,0 +1,9 @@ +// @atoms/02_AudioPlayer/__test__/Example.tsx +import React, { FC } from 'react'; +import AudioPlayer, { ICard } from '../AudioPlayer.tsx'; + +const Example: FC = function ({ theme = 'light', cards }) { + return ; +}; + +export default Example; diff --git a/src/molecules/02_AudioPlayer/__test__/02-AudioPlayer.test.tsx b/src/molecules/02_AudioPlayer/__test__/02-AudioPlayer.test.tsx new file mode 100644 index 0000000..feaec0a --- /dev/null +++ b/src/molecules/02_AudioPlayer/__test__/02-AudioPlayer.test.tsx @@ -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(); + const component = result.container.querySelector('#grid-atom--test'); + expect(component).toBeInTheDocument(); + }); +}); diff --git a/src/molecules/02_AudioPlayer/index.ts b/src/molecules/02_AudioPlayer/index.ts new file mode 100644 index 0000000..91d7b32 --- /dev/null +++ b/src/molecules/02_AudioPlayer/index.ts @@ -0,0 +1,3 @@ +// @atoms/AudioPlayer/index.ts +export { default as AudioPlayer, EAudioPlayerVariant } from './AudioPlayer'; +export const AudioPlayerName = 'AudioPlayer'; From a3e793f54b50c589dac5182a7815271d4ef183b4 Mon Sep 17 00:00:00 2001 From: Angelo Reale <12191809+angeloreale@users.noreply.github.com> Date: Sun, 4 Aug 2024 19:34:32 +0100 Subject: [PATCH 02/18] ar(feat) DPCP-31: Audio Player --- src/molecules/02_AudioPlayer/AudioPlayer.tsx | 102 +++++++++++++++---- 1 file changed, 84 insertions(+), 18 deletions(-) diff --git a/src/molecules/02_AudioPlayer/AudioPlayer.tsx b/src/molecules/02_AudioPlayer/AudioPlayer.tsx index 5cc9260..e065232 100644 --- a/src/molecules/02_AudioPlayer/AudioPlayer.tsx +++ b/src/molecules/02_AudioPlayer/AudioPlayer.tsx @@ -1,12 +1,16 @@ /* 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 } from 'react'; +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', @@ -49,6 +53,8 @@ export interface IAudioPlayer { className?: string; tracks?: IAudioTrack[]; onPlayTrack?: () => void; + nativeControls?: boolean; + prompt?: string; theme?: 'light' | 'dark'; } @@ -57,22 +63,62 @@ export const HAudioPlayer = function ({ className = '', tracks = DEFAULT_TRACKS, onPlayTrack = () => {}, + nativeControls = false, + prompt = 'Rotation portals', theme = 'light', }: IAudioPlayer) { + const audioElement = useRef(); + const [status, setStatus] = useState('stopped'); + const [title, setTitle] = useState(prompt); const gridSx = [ { [`class02 grid sm:grid-cols-12 sm:!gap-a0 - md:!gap-a4 + md:!gap-a1 + content-center + items-center + align-center + justify-center `]: true, }, ]; const gridStyles = `${clsx(gridSx)} ${className}`; - onPlayTrack(); + const handlePlay = () => { + onPlayTrack(); + audioElement.current.isPlaying = status === 'playing'; + + if (audioElement?.current?.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'), + }); + 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 ( -