refactor: seperate buttons

This commit is contained in:
MAZE 2023-10-14 15:14:58 +03:30
parent eccba87557
commit b117a4b495
8 changed files with 216 additions and 200 deletions

View file

@ -3,82 +3,9 @@
z-index: 10;
top: 30px;
display: flex;
width: max-content;
align-items: center;
justify-content: center;
margin: 0 auto;
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);
}
}

View file

@ -1,134 +1,13 @@
import { useEffect, useState } from 'react';
import { BiPause, BiPlay, 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 { usePlay } from '@/contexts/play';
import { cn } from '@/helpers/styles';
import { PlayButton } from './play';
import { UnselectButton } from './unselect';
import styles from './buttons.module.css';
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 (
<div className={styles.buttons}>
<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>
<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>
)}
<PlayButton />
<UnselectButton />
</div>
);
}

View file

@ -0,0 +1 @@
export { PlayButton } from './play';

View 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);
}
}

View 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>
);
}

View file

@ -0,0 +1 @@
export { UnselectButton } from './unselect';

View 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);
}

View 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>
)}
</>
);
}