mirror of
https://github.com/remvze/moodist.git
synced 2025-12-17 00:44:14 +00:00
feat: implement unselect all functionality
This commit is contained in:
parent
6d02cfb134
commit
8966d59d75
6 changed files with 75 additions and 57 deletions
|
|
@ -25,12 +25,16 @@
|
|||
line-height: 0;
|
||||
outline: none;
|
||||
|
||||
&:disabled {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
& span {
|
||||
font-size: var(--font-lg);
|
||||
}
|
||||
}
|
||||
|
||||
& .shuffleButton {
|
||||
& .smallButton {
|
||||
display: flex;
|
||||
width: 45px;
|
||||
height: 45px;
|
||||
|
|
@ -47,5 +51,24 @@
|
|||
font-size: var(--font-md);
|
||||
line-height: 0;
|
||||
outline: none;
|
||||
transition: 0.2s;
|
||||
|
||||
&:disabled {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
&.restore {
|
||||
border-top-color: #34d399;
|
||||
border-bottom-color: #047857;
|
||||
background-color: #10b981;
|
||||
color: var(--color-foreground);
|
||||
}
|
||||
|
||||
&.delete {
|
||||
border-top-color: #fb7185;
|
||||
border-bottom-color: #be123c;
|
||||
background-color: #f43f5e;
|
||||
color: var(--color-foreground);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,18 @@
|
|||
import { useEffect } from 'react';
|
||||
import { BiPause, BiPlay, BiShuffle } from 'react-icons/bi/index';
|
||||
import { BiPause, BiPlay, BiUndo, BiTrash } from 'react-icons/bi/index';
|
||||
|
||||
import { useSoundStore } from '@/store';
|
||||
import { usePlay } from '@/contexts/play';
|
||||
import { cn } from '@/helpers/styles';
|
||||
|
||||
import styles from './buttons.module.css';
|
||||
|
||||
export function Buttons() {
|
||||
const { isPlaying, pause, play, toggle } = usePlay();
|
||||
const { isPlaying, pause, toggle } = usePlay();
|
||||
const noSelected = useSoundStore(state => state.noSelected());
|
||||
const shuffle = useSoundStore(state => state.shuffle);
|
||||
const restoreHistory = useSoundStore(state => state.restoreHistory);
|
||||
const hasHistory = useSoundStore(state => !!state.history);
|
||||
const unselectAll = useSoundStore(state => state.unselectAll);
|
||||
|
||||
const handleClick = () => {
|
||||
if (noSelected) return pause();
|
||||
|
|
@ -23,7 +26,11 @@ export function Buttons() {
|
|||
|
||||
return (
|
||||
<div className={styles.buttons}>
|
||||
<button className={styles.playButton} onClick={handleClick}>
|
||||
<button
|
||||
className={styles.playButton}
|
||||
disabled={noSelected}
|
||||
onClick={handleClick}
|
||||
>
|
||||
{isPlaying ? (
|
||||
<>
|
||||
<span>
|
||||
|
|
@ -42,14 +49,20 @@ export function Buttons() {
|
|||
</button>
|
||||
|
||||
<button
|
||||
aria-label="Shuffle Sounds"
|
||||
className={styles.shuffleButton}
|
||||
disabled={noSelected && !hasHistory}
|
||||
aria-label={
|
||||
hasHistory ? 'Restore Unselected Sounds' : 'Unselect All Sounds'
|
||||
}
|
||||
className={cn(
|
||||
styles.smallButton,
|
||||
hasHistory ? styles.restore : styles.delete,
|
||||
)}
|
||||
onClick={() => {
|
||||
shuffle();
|
||||
play();
|
||||
if (hasHistory) restoreHistory();
|
||||
else if (!noSelected) unselectAll(true);
|
||||
}}
|
||||
>
|
||||
<BiShuffle />
|
||||
{hasHistory ? <BiUndo /> : <BiTrash />}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,26 +0,0 @@
|
|||
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<T>(array: Array<T>): T {
|
||||
const randomIndex = random(0, array.length);
|
||||
|
||||
return array[randomIndex];
|
||||
}
|
||||
|
||||
export function shuffle<T>(array: Array<T>): Array<T> {
|
||||
return array
|
||||
.map(value => ({ sort: Math.random(), value }))
|
||||
.sort((a, b) => a.sort - b.sort)
|
||||
.map(({ value }) => value);
|
||||
}
|
||||
|
||||
export function pickMany<T>(array: Array<T>, count: number): Array<T> {
|
||||
const shuffled = shuffle(array);
|
||||
|
||||
return shuffled.slice(0, count);
|
||||
}
|
||||
|
|
@ -12,6 +12,7 @@ export const useSoundStore = create<SoundState & SoundActions>()(
|
|||
}),
|
||||
{
|
||||
name: 'moodist-sounds',
|
||||
partialize: state => ({ sounds: state.sounds }),
|
||||
skipHydration: true,
|
||||
storage: createJSONStorage(() => localStorage),
|
||||
version: 0,
|
||||
|
|
|
|||
|
|
@ -2,14 +2,12 @@ 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;
|
||||
unselectAll: (pushToHistory?: boolean) => void;
|
||||
restoreHistory: () => void;
|
||||
}
|
||||
|
||||
export const createActions: StateCreator<
|
||||
|
|
@ -19,6 +17,14 @@ export const createActions: StateCreator<
|
|||
SoundActions
|
||||
> = (set, get) => {
|
||||
return {
|
||||
restoreHistory() {
|
||||
const history = get().history;
|
||||
|
||||
if (!history) return;
|
||||
|
||||
set({ history: null, sounds: history });
|
||||
},
|
||||
|
||||
select(id) {
|
||||
set({
|
||||
sounds: {
|
||||
|
|
@ -37,21 +43,6 @@ 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: {
|
||||
|
|
@ -61,8 +52,18 @@ export const createActions: StateCreator<
|
|||
});
|
||||
},
|
||||
|
||||
unselectAll() {
|
||||
unselectAll(pushToHistory = false) {
|
||||
const noSelected = get().noSelected();
|
||||
|
||||
if (noSelected) return;
|
||||
|
||||
const sounds = get().sounds;
|
||||
|
||||
if (pushToHistory) {
|
||||
const history = JSON.parse(JSON.stringify(sounds));
|
||||
set({ history });
|
||||
}
|
||||
|
||||
const ids = Object.keys(sounds);
|
||||
|
||||
ids.forEach(id => {
|
||||
|
|
|
|||
|
|
@ -11,6 +11,12 @@ export interface SoundState {
|
|||
volume: number;
|
||||
};
|
||||
};
|
||||
history: {
|
||||
[id: string]: {
|
||||
isSelected: boolean;
|
||||
volume: number;
|
||||
};
|
||||
} | null;
|
||||
noSelected: () => boolean;
|
||||
}
|
||||
|
||||
|
|
@ -21,13 +27,13 @@ export const createState: StateCreator<
|
|||
SoundState
|
||||
> = (set, get) => {
|
||||
const state: SoundState = {
|
||||
history: null,
|
||||
noSelected() {
|
||||
const { sounds } = get();
|
||||
const keys = Object.keys(sounds);
|
||||
|
||||
return keys.every(key => !sounds[key].isSelected);
|
||||
},
|
||||
|
||||
sounds: {},
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue