mirror of
https://github.com/remvze/moodist.git
synced 2025-12-17 08:54:13 +00:00
refactor: seperate buttons
This commit is contained in:
parent
eccba87557
commit
b117a4b495
8 changed files with 216 additions and 200 deletions
|
|
@ -3,82 +3,9 @@
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
top: 30px;
|
top: 30px;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
width: max-content;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
margin: 0 auto;
|
||||||
column-gap: 10px;
|
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;
|
|
||||||
|
|
||||||
&:disabled,
|
|
||||||
&.disabled {
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
|
|
||||||
& span {
|
|
||||||
font-size: var(--font-lg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
& .smallButton {
|
|
||||||
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;
|
|
||||||
transition: 0.2s;
|
|
||||||
|
|
||||||
&:disabled,
|
|
||||||
&.disabled {
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
& .tooltip {
|
|
||||||
padding: 6px 12px;
|
|
||||||
border: 1px solid var(--color-neutral-200);
|
|
||||||
border-radius: 100px;
|
|
||||||
background-color: var(--color-neutral-100);
|
|
||||||
font-size: var(--font-xsm);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,134 +1,13 @@
|
||||||
import { useEffect, useState } from 'react';
|
import { PlayButton } from './play';
|
||||||
import { BiPause, BiPlay, BiUndo, BiTrash } from 'react-icons/bi/index';
|
import { UnselectButton } from './unselect';
|
||||||
import {
|
|
||||||
useFloating,
|
|
||||||
autoUpdate,
|
|
||||||
offset,
|
|
||||||
flip,
|
|
||||||
shift,
|
|
||||||
useHover,
|
|
||||||
useFocus,
|
|
||||||
useDismiss,
|
|
||||||
useRole,
|
|
||||||
useInteractions,
|
|
||||||
} from '@floating-ui/react';
|
|
||||||
import { AnimatePresence, motion } from 'framer-motion';
|
|
||||||
|
|
||||||
import { useSoundStore } from '@/store';
|
|
||||||
import { usePlay } from '@/contexts/play';
|
|
||||||
import { cn } from '@/helpers/styles';
|
|
||||||
|
|
||||||
import styles from './buttons.module.css';
|
import styles from './buttons.module.css';
|
||||||
|
|
||||||
export function Buttons() {
|
export function Buttons() {
|
||||||
/**
|
|
||||||
* Tooltip Start
|
|
||||||
*/
|
|
||||||
const [isTooltipOpen, setIsTooltipOpen] = useState(false);
|
|
||||||
|
|
||||||
const { context, floatingStyles, refs } = useFloating({
|
|
||||||
middleware: [offset(15), flip(), shift()],
|
|
||||||
onOpenChange: setIsTooltipOpen,
|
|
||||||
open: isTooltipOpen,
|
|
||||||
placement: 'top',
|
|
||||||
whileElementsMounted: autoUpdate,
|
|
||||||
});
|
|
||||||
|
|
||||||
const hover = useHover(context, { move: false });
|
|
||||||
const focus = useFocus(context);
|
|
||||||
const dismiss = useDismiss(context);
|
|
||||||
const role = useRole(context, { role: 'tooltip' });
|
|
||||||
|
|
||||||
const { getFloatingProps, getReferenceProps } = useInteractions([
|
|
||||||
hover,
|
|
||||||
focus,
|
|
||||||
dismiss,
|
|
||||||
role,
|
|
||||||
]);
|
|
||||||
/**
|
|
||||||
* Tooltip End
|
|
||||||
*/
|
|
||||||
|
|
||||||
const { isPlaying, pause, toggle } = usePlay();
|
|
||||||
const noSelected = useSoundStore(state => state.noSelected());
|
|
||||||
const restoreHistory = useSoundStore(state => state.restoreHistory);
|
|
||||||
const hasHistory = useSoundStore(state => !!state.history);
|
|
||||||
const unselectAll = useSoundStore(state => state.unselectAll);
|
|
||||||
|
|
||||||
const handleClick = () => {
|
|
||||||
if (noSelected) return pause();
|
|
||||||
|
|
||||||
toggle();
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (isPlaying && noSelected) pause();
|
|
||||||
}, [isPlaying, pause, noSelected]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.buttons}>
|
<div className={styles.buttons}>
|
||||||
<motion.button
|
<PlayButton />
|
||||||
className={cn(styles.playButton, noSelected && styles.disabled)}
|
<UnselectButton />
|
||||||
disabled={noSelected}
|
|
||||||
layout
|
|
||||||
onClick={handleClick}
|
|
||||||
>
|
|
||||||
{isPlaying ? (
|
|
||||||
<>
|
|
||||||
<span>
|
|
||||||
<BiPause />
|
|
||||||
</span>{' '}
|
|
||||||
Pause
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<span>
|
|
||||||
<BiPlay />
|
|
||||||
</span>{' '}
|
|
||||||
Play
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</motion.button>
|
|
||||||
|
|
||||||
<AnimatePresence mode="popLayout">
|
|
||||||
{(!noSelected || hasHistory) && (
|
|
||||||
<motion.div
|
|
||||||
animate={{ opacity: 1, x: 0 }}
|
|
||||||
exit={{ opacity: 0, x: 20 }}
|
|
||||||
initial={{ opacity: 0, x: 20 }}
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
disabled={noSelected && !hasHistory}
|
|
||||||
ref={refs.setReference}
|
|
||||||
{...getReferenceProps}
|
|
||||||
aria-label={
|
|
||||||
hasHistory ? 'Restore Unselected Sounds' : 'Unselect All Sounds'
|
|
||||||
}
|
|
||||||
className={cn(
|
|
||||||
styles.smallButton,
|
|
||||||
hasHistory ? styles.restore : styles.delete,
|
|
||||||
noSelected && !hasHistory && styles.disabled,
|
|
||||||
)}
|
|
||||||
onClick={() => {
|
|
||||||
if (hasHistory) restoreHistory();
|
|
||||||
else if (!noSelected) unselectAll(true);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{hasHistory ? <BiUndo /> : <BiTrash />}
|
|
||||||
</button>
|
|
||||||
</motion.div>
|
|
||||||
)}
|
|
||||||
</AnimatePresence>
|
|
||||||
|
|
||||||
{isTooltipOpen && (
|
|
||||||
<div
|
|
||||||
ref={refs.setFloating}
|
|
||||||
style={floatingStyles}
|
|
||||||
{...getFloatingProps({ className: styles.tooltip })}
|
|
||||||
>
|
|
||||||
{hasHistory ? 'Restore unselected sounds.' : 'Unselect all sounds.'}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
1
src/components/buttons/play/index.ts
Normal file
1
src/components/buttons/play/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export { PlayButton } from './play';
|
||||||
27
src/components/buttons/play/play.module.css
Normal file
27
src/components/buttons/play/play.module.css
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
.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;
|
||||||
|
|
||||||
|
&:disabled,
|
||||||
|
&.disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
& span {
|
||||||
|
font-size: var(--font-lg);
|
||||||
|
}
|
||||||
|
}
|
||||||
49
src/components/buttons/play/play.tsx
Normal file
49
src/components/buttons/play/play.tsx
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
import { BiPause, BiPlay } from 'react-icons/bi/index';
|
||||||
|
import { motion } from 'framer-motion';
|
||||||
|
|
||||||
|
import { useSoundStore } from '@/store';
|
||||||
|
import { usePlay } from '@/contexts/play';
|
||||||
|
import { cn } from '@/helpers/styles';
|
||||||
|
|
||||||
|
import styles from './play.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 (
|
||||||
|
<motion.button
|
||||||
|
className={cn(styles.playButton, noSelected && styles.disabled)}
|
||||||
|
disabled={noSelected}
|
||||||
|
layout
|
||||||
|
onClick={handleClick}
|
||||||
|
>
|
||||||
|
{isPlaying ? (
|
||||||
|
<>
|
||||||
|
<span>
|
||||||
|
<BiPause />
|
||||||
|
</span>{' '}
|
||||||
|
Pause
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<span>
|
||||||
|
<BiPlay />
|
||||||
|
</span>{' '}
|
||||||
|
Play
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</motion.button>
|
||||||
|
);
|
||||||
|
}
|
||||||
1
src/components/buttons/unselect/index.ts
Normal file
1
src/components/buttons/unselect/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export { UnselectButton } from './unselect';
|
||||||
39
src/components/buttons/unselect/unselect.module.css
Normal file
39
src/components/buttons/unselect/unselect.module.css
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
.unselectButton {
|
||||||
|
display: flex;
|
||||||
|
width: 45px;
|
||||||
|
height: 45px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border: none;
|
||||||
|
border-radius: 100px;
|
||||||
|
border-top: 2px solid #fb7185;
|
||||||
|
border-bottom: 3px solid #be123c;
|
||||||
|
background-color: #f43f5e;
|
||||||
|
color: var(--color-foreground);
|
||||||
|
cursor: pointer;
|
||||||
|
font-family: var(--font-heading);
|
||||||
|
font-size: var(--font-md);
|
||||||
|
line-height: 0;
|
||||||
|
outline: none;
|
||||||
|
transition: 0.2s;
|
||||||
|
|
||||||
|
&:disabled,
|
||||||
|
&.disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.restore {
|
||||||
|
border-top-color: #34d399;
|
||||||
|
border-bottom-color: #047857;
|
||||||
|
background-color: #10b981;
|
||||||
|
color: var(--color-foreground);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip {
|
||||||
|
padding: 6px 12px;
|
||||||
|
border: 1px solid var(--color-neutral-200);
|
||||||
|
border-radius: 100px;
|
||||||
|
background-color: var(--color-neutral-100);
|
||||||
|
font-size: var(--font-xsm);
|
||||||
|
}
|
||||||
93
src/components/buttons/unselect/unselect.tsx
Normal file
93
src/components/buttons/unselect/unselect.tsx
Normal file
|
|
@ -0,0 +1,93 @@
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { BiUndo, BiTrash } from 'react-icons/bi/index';
|
||||||
|
import {
|
||||||
|
useFloating,
|
||||||
|
autoUpdate,
|
||||||
|
offset,
|
||||||
|
flip,
|
||||||
|
shift,
|
||||||
|
useHover,
|
||||||
|
useFocus,
|
||||||
|
useDismiss,
|
||||||
|
useRole,
|
||||||
|
useInteractions,
|
||||||
|
} from '@floating-ui/react';
|
||||||
|
import { AnimatePresence, motion } from 'framer-motion';
|
||||||
|
|
||||||
|
import { useSoundStore } from '@/store';
|
||||||
|
import { cn } from '@/helpers/styles';
|
||||||
|
|
||||||
|
import styles from './unselect.module.css';
|
||||||
|
|
||||||
|
export function UnselectButton() {
|
||||||
|
const [isTooltipOpen, setIsTooltipOpen] = useState(false);
|
||||||
|
|
||||||
|
const { context, floatingStyles, refs } = useFloating({
|
||||||
|
middleware: [offset(15), flip(), shift()],
|
||||||
|
onOpenChange: setIsTooltipOpen,
|
||||||
|
open: isTooltipOpen,
|
||||||
|
placement: 'top',
|
||||||
|
whileElementsMounted: autoUpdate,
|
||||||
|
});
|
||||||
|
|
||||||
|
const hover = useHover(context, { move: false });
|
||||||
|
const focus = useFocus(context);
|
||||||
|
const dismiss = useDismiss(context);
|
||||||
|
const role = useRole(context, { role: 'tooltip' });
|
||||||
|
|
||||||
|
const { getFloatingProps, getReferenceProps } = useInteractions([
|
||||||
|
hover,
|
||||||
|
focus,
|
||||||
|
dismiss,
|
||||||
|
role,
|
||||||
|
]);
|
||||||
|
|
||||||
|
const noSelected = useSoundStore(state => state.noSelected());
|
||||||
|
const restoreHistory = useSoundStore(state => state.restoreHistory);
|
||||||
|
const hasHistory = useSoundStore(state => !!state.history);
|
||||||
|
const unselectAll = useSoundStore(state => state.unselectAll);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<AnimatePresence mode="popLayout">
|
||||||
|
{(!noSelected || hasHistory) && (
|
||||||
|
<motion.div
|
||||||
|
animate={{ opacity: 1, x: 0 }}
|
||||||
|
exit={{ opacity: 0, x: 20 }}
|
||||||
|
initial={{ opacity: 0, x: 20 }}
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
disabled={noSelected && !hasHistory}
|
||||||
|
ref={refs.setReference}
|
||||||
|
{...getReferenceProps}
|
||||||
|
aria-label={
|
||||||
|
hasHistory ? 'Restore Unselected Sounds' : 'Unselect All Sounds'
|
||||||
|
}
|
||||||
|
className={cn(
|
||||||
|
styles.unselectButton,
|
||||||
|
hasHistory && styles.restore,
|
||||||
|
noSelected && !hasHistory && styles.disabled,
|
||||||
|
)}
|
||||||
|
onClick={() => {
|
||||||
|
if (hasHistory) restoreHistory();
|
||||||
|
else if (!noSelected) unselectAll(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{hasHistory ? <BiUndo /> : <BiTrash />}
|
||||||
|
</button>
|
||||||
|
</motion.div>
|
||||||
|
)}
|
||||||
|
</AnimatePresence>
|
||||||
|
|
||||||
|
{isTooltipOpen && (
|
||||||
|
<div
|
||||||
|
ref={refs.setFloating}
|
||||||
|
style={floatingStyles}
|
||||||
|
{...getFloatingProps({ className: styles.tooltip })}
|
||||||
|
>
|
||||||
|
{hasHistory ? 'Restore unselected sounds.' : 'Unselect all sounds.'}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue