diff --git a/src/components/buttons/buttons.module.css b/src/components/buttons/buttons.module.css new file mode 100644 index 0000000..e53a005 --- /dev/null +++ b/src/components/buttons/buttons.module.css @@ -0,0 +1,51 @@ +.buttons { + position: sticky; + z-index: 10; + top: 30px; + display: flex; + align-items: center; + justify-content: center; + column-gap: 10px; + + & .playButton { + display: flex; + width: 150px; + height: 45px; + align-items: center; + justify-content: center; + border: none; + border-radius: 100px; + border-top: 2px solid #818cf8; + border-bottom: 3px solid #4f46e5; + background-color: #6366f1; + color: var(--color-foreground); + cursor: pointer; + font-family: var(--font-heading); + font-size: var(--font-base); + line-height: 0; + outline: none; + + & span { + font-size: var(--font-lg); + } + } + + & .shuffleButton { + display: flex; + width: 45px; + height: 45px; + align-items: center; + justify-content: center; + border: none; + border-radius: 100px; + border-top: 2px solid var(--color-neutral-950); + border-bottom: 3px solid var(--color-neutral-600); + background-color: var(--color-neutral-800); + color: var(--color-neutral-200); + cursor: pointer; + font-family: var(--font-heading); + font-size: var(--font-md); + line-height: 0; + outline: none; + } +} diff --git a/src/components/buttons/buttons.tsx b/src/components/buttons/buttons.tsx new file mode 100644 index 0000000..ffbe1c8 --- /dev/null +++ b/src/components/buttons/buttons.tsx @@ -0,0 +1,55 @@ +import { useEffect } from 'react'; +import { BiPause, BiPlay, BiShuffle } from 'react-icons/bi/index'; + +import { useSoundStore } from '@/store'; +import { usePlay } from '@/contexts/play'; + +import styles from './buttons.module.css'; + +export function Buttons() { + const { isPlaying, pause, play, toggle } = usePlay(); + const noSelected = useSoundStore(state => state.noSelected()); + const shuffle = useSoundStore(state => state.shuffle); + + const handleClick = () => { + if (noSelected) return pause(); + + toggle(); + }; + + useEffect(() => { + if (isPlaying && noSelected) pause(); + }, [isPlaying, pause, noSelected]); + + return ( +
+ + + +
+ ); +} diff --git a/src/components/buttons/index.ts b/src/components/buttons/index.ts new file mode 100644 index 0000000..363f19c --- /dev/null +++ b/src/components/buttons/index.ts @@ -0,0 +1 @@ +export { Buttons } from './buttons'; diff --git a/src/components/categories/categories.tsx b/src/components/categories/categories.tsx index 82df73f..879a03d 100644 --- a/src/components/categories/categories.tsx +++ b/src/components/categories/categories.tsx @@ -7,7 +7,7 @@ import { useFavoriteStore } from '@/store/favorite'; import { Container } from '@/components/container'; import { StoreConsumer } from '../store-consumer'; import { Category } from '@/components/category'; -import { PlayButton } from '@/components/play-button'; +import { Buttons } from '@/components/buttons'; import { PlayProvider } from '@/contexts/play'; import { sounds } from '@/data/sounds'; @@ -37,7 +37,7 @@ export function Categories() { - +
{!!favoriteSounds.length && ( diff --git a/src/components/play-button/index.ts b/src/components/play-button/index.ts deleted file mode 100644 index b646911..0000000 --- a/src/components/play-button/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { PlayButton } from './play-button'; diff --git a/src/components/play-button/play-button.module.css b/src/components/play-button/play-button.module.css deleted file mode 100644 index deccaf3..0000000 --- a/src/components/play-button/play-button.module.css +++ /dev/null @@ -1,26 +0,0 @@ -.playButton { - position: sticky; - z-index: 10; - top: 30px; - display: flex; - width: 150px; - height: 45px; - align-items: center; - justify-content: center; - border: none; - border-radius: 100px; - border-top: 2px solid #818cf8; - border-bottom: 3px solid #4f46e5; - margin: 0 auto; - background-color: #6366f1; - color: var(--color-foreground); - cursor: pointer; - font-family: var(--font-heading); - font-size: var(--font-base); - line-height: 0; - outline: none; - - & span { - font-size: var(--font-lg); - } -} diff --git a/src/components/play-button/play-button.tsx b/src/components/play-button/play-button.tsx deleted file mode 100644 index fe5243c..0000000 --- a/src/components/play-button/play-button.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import { useEffect } from 'react'; -import { BiPause, BiPlay } from 'react-icons/bi/index'; - -import { useSoundStore } from '@/store'; -import { usePlay } from '@/contexts/play'; - -import styles from './play-button.module.css'; - -export function PlayButton() { - const { isPlaying, pause, toggle } = usePlay(); - const noSelected = useSoundStore(state => state.noSelected()); - - const handleClick = () => { - if (noSelected) return pause(); - - toggle(); - }; - - useEffect(() => { - if (isPlaying && noSelected) pause(); - }, [isPlaying, pause, noSelected]); - - return ( - - ); -} diff --git a/src/helpers/random.ts b/src/helpers/random.ts new file mode 100644 index 0000000..8e577c2 --- /dev/null +++ b/src/helpers/random.ts @@ -0,0 +1,26 @@ +export function random(min: number, max: number): number { + return Math.random() * (max - min) + min; +} + +export function randomInt(min: number, max: number): number { + return Math.floor(random(min, max)); +} + +export function pickOne(array: Array): T { + const randomIndex = random(0, array.length); + + return array[randomIndex]; +} + +export function shuffle(array: Array): Array { + return array + .map(value => ({ sort: Math.random(), value })) + .sort((a, b) => a.sort - b.sort) + .map(({ value }) => value); +} + +export function pickMany(array: Array, count: number): Array { + const shuffled = shuffle(array); + + return shuffled.slice(0, count); +} diff --git a/src/store/sound/sound.actions.ts b/src/store/sound/sound.actions.ts index 60de6fe..2c18b96 100644 --- a/src/store/sound/sound.actions.ts +++ b/src/store/sound/sound.actions.ts @@ -2,10 +2,14 @@ import type { StateCreator } from 'zustand'; import type { SoundState } from './sound.state'; +import { pickMany, random } from '@/helpers/random'; + export interface SoundActions { select: (id: string) => void; unselect: (id: string) => void; setVolume: (id: string, volume: number) => void; + unselectAll: () => void; + shuffle: () => void; } export const createActions: StateCreator< @@ -33,6 +37,21 @@ export const createActions: StateCreator< }); }, + shuffle() { + get().unselectAll(); + + const sounds = get().sounds; + const ids = Object.keys(sounds); + const randomIDs = pickMany(ids, 4); + + randomIDs.forEach(id => { + sounds[id].isSelected = true; + sounds[id].volume = random(0.2, 0.8); + }); + + set({ sounds }); + }, + unselect(id) { set({ sounds: { @@ -41,5 +60,17 @@ export const createActions: StateCreator< }, }); }, + + unselectAll() { + const sounds = get().sounds; + const ids = Object.keys(sounds); + + ids.forEach(id => { + sounds[id].isSelected = false; + sounds[id].volume = 0.5; + }); + + set({ sounds }); + }, }; };