-
Notifications
You must be signed in to change notification settings - Fork 41
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(ui): create "combo-list" component
A component for list, select and delete sound combos.
- Loading branch information
Showing
3 changed files
with
277 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
import { Fragment, useState } from 'react' | ||
import { Menu, Transition, Switch } from '@headlessui/react' | ||
import { PiPlaylistBold } from 'react-icons/pi' | ||
import { FiTrash } from 'react-icons/fi' | ||
|
||
import { useThemeStore } from '@/stores/theme-store' | ||
import { useComboStore } from '@/stores/combo-store' | ||
import { useSoundsStateStore } from '@/stores/sounds-state-store' | ||
|
||
import { | ||
comboButton, | ||
editButton, | ||
toggleEditButton, | ||
toggleEditContainer, | ||
triggerButton | ||
} from './styles' | ||
|
||
export function ComboList() { | ||
const theme = useThemeStore(set => set.theme) | ||
const sounds = useSoundsStateStore(state => state.sounds) | ||
const bulkUpdate = useSoundsStateStore(state => state.bulkUpdate) | ||
const combos = useComboStore(state => state.combos) | ||
const deleteCombo = useComboStore(state => state.deleteCombo) | ||
|
||
const [editMode, setEditMode] = useState(false) | ||
|
||
function updateCombo(id: string) { | ||
const combo = combos.filter(combo => combo.id === id) | ||
|
||
if (combo.length === 0) return | ||
|
||
const comboSounds = combo[0].sounds | ||
|
||
const activeSoundsIds = comboSounds.map(sound => sound.id) | ||
|
||
const disabledSounds = sounds | ||
.filter(sound => !activeSoundsIds.includes(sound.id)) | ||
.map(sound => ({ ...sound, active: false })) | ||
|
||
console.log( | ||
`enabledSounds: ${comboSounds.map( | ||
sound => `${sound.id}:${sound.volume}` | ||
)}` | ||
) | ||
|
||
bulkUpdate([...disabledSounds, ...comboSounds]) | ||
} | ||
|
||
const isEmpty = combos.length === 0 | ||
|
||
return ( | ||
<div className="z-40"> | ||
<Menu> | ||
<Menu.Button | ||
title="Toggle combo list" | ||
disabled={isEmpty} | ||
className={triggerButton({ theme })} | ||
> | ||
<PiPlaylistBold size={24} /> | ||
</Menu.Button> | ||
<Transition | ||
as={Fragment} | ||
enter="transition ease-out duration-100" | ||
enterFrom="transform opacity-0 scale-95" | ||
enterTo="transform opacity-100 scale-100" | ||
leave="transition ease-in duration-75" | ||
leaveFrom="transform opacity-100 scale-100" | ||
leaveTo="transform opacity-0 scale-95" | ||
> | ||
<Menu.Items | ||
as="div" | ||
className="snap absolute left-4 right-4 mt-2 flex max-h-[85vh] origin-top-right snap-y snap-mandatory scroll-pt-4 flex-col gap-1 overflow-y-auto rounded-2xl p-4 shadow-2xl backdrop-blur-md scrollbar-hide focus:outline-none md:left-auto md:right-0" | ||
> | ||
<div className={toggleEditContainer({ theme })}> | ||
<button | ||
onClick={() => setEditMode(false)} | ||
className={toggleEditButton({ theme, active: !editMode })} | ||
> | ||
Select | ||
</button> | ||
<button | ||
data-is-active={editMode} | ||
onClick={() => setEditMode(true)} | ||
className={toggleEditButton({ theme, active: editMode })} | ||
> | ||
Edit | ||
</button> | ||
</div> | ||
|
||
{!editMode && | ||
combos.map(combo => ( | ||
<Menu.Item key={combo.id}> | ||
{({ active }) => ( | ||
<button | ||
className={comboButton({ theme, active })} | ||
onClick={() => updateCombo(combo.id)} | ||
> | ||
<span className="font-bold">{combo.name}</span> | ||
</button> | ||
)} | ||
</Menu.Item> | ||
))} | ||
|
||
{editMode && | ||
combos.map(combo => ( | ||
<button | ||
key={combo.id} | ||
className={editButton({ theme })} | ||
onClick={() => deleteCombo(combo.id)} | ||
> | ||
<span className="font-bold">{combo.name}</span> | ||
<FiTrash /> | ||
</button> | ||
))} | ||
</Menu.Items> | ||
</Transition> | ||
</Menu> | ||
</div> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
import { tv } from 'tailwind-variants' | ||
|
||
export const triggerButton = tv({ | ||
base: /*tw:*/ 'flex opacity-90 hover:opacity-100 disabled:opacity-50 disabled:hover:opacity-50', | ||
variants: { | ||
theme: { | ||
transition: /*tw:*/ 'text-white', | ||
dark: /*tw:*/ 'text-dark-foreground', | ||
'blue-room': /*tw:*/ 'text-blue-room', | ||
train: /*tw:*/ 'text-train', | ||
waterfall: /*tw:*/ 'text-waterfall', | ||
light: /*tw:*/ 'text-light-foreground', | ||
'camping-fire': /*tw:*/ 'text-camping-fire' | ||
} | ||
} | ||
}) | ||
|
||
export const comboButton = tv({ | ||
base: /*tw:*/ 'rounded-xl p-3 leading-none tracking-wider duration-300', | ||
variants: { | ||
theme: { | ||
transition: /*tw:*/ 'text-white bg-white/5', | ||
dark: /*tw:*/ 'text-dark-foreground bg-dark-foreground/5', | ||
light: /*tw:*/ 'text-light-foreground bg-light-foreground/5', | ||
'blue-room': /*tw:*/ 'text-blue-room bg-blue-room/5', | ||
train: /*tw:*/ 'text-train bg-train/5', | ||
waterfall: /*tw:*/ 'text-waterfall bg-waterfall/5', | ||
'camping-fire': /*tw:*/ 'text-camping-fire bg-camping-fire/5' | ||
}, | ||
active: { | ||
true: /*tw:*/ 'bg-white/20' | ||
} | ||
}, | ||
compoundVariants: [ | ||
{ | ||
theme: 'dark', | ||
active: true, | ||
class: /*tw:*/ 'bg-dark-foreground/20' | ||
}, | ||
{ | ||
theme: 'light', | ||
active: true, | ||
class: /*tw:*/ 'bg-light-foreground/20' | ||
}, | ||
{ | ||
theme: 'blue-room', | ||
active: true, | ||
class: /*tw:*/ 'bg-blue-room/20' | ||
}, | ||
{ | ||
theme: 'train', | ||
active: true, | ||
class: /*tw:*/ 'bg-train/20' | ||
}, | ||
{ | ||
theme: 'waterfall', | ||
active: true, | ||
class: /*tw:*/ 'bg-waterfall/20' | ||
}, | ||
{ | ||
theme: 'camping-fire', | ||
active: true, | ||
class: /*tw:*/ 'bg-camping-fire/20' | ||
} | ||
] | ||
}) | ||
|
||
export const editButton = tv({ | ||
extend: comboButton, | ||
base: /*tw:*/ 'flex items-center gap-1 justify-between', | ||
variants: { | ||
theme: { | ||
transition: /*tw:*/ 'hover:bg-white/20', | ||
dark: /*tw:*/ 'hover:bg-dark-foreground/20', | ||
light: /*tw:*/ 'hover:bg-light-foreground/20', | ||
'blue-room': /*tw:*/ 'hover:bg-blue-room/20', | ||
train: /*tw:*/ 'hover:bg-train/20', | ||
waterfall: /*tw:*/ 'hover:bg-waterfall/20', | ||
'camping-fire': /*tw:*/ 'hover:bg-camping-fire/20' | ||
}, | ||
active: { | ||
true: /*tw:*/ 'bg-white/20' | ||
} | ||
} | ||
}) | ||
|
||
export const toggleEditContainer = tv({ | ||
base: /*tw:*/ 'min-w-40 mb-2 flex items-center justify-around gap-1 rounded-lg', | ||
variants: { | ||
theme: { | ||
transition: /*tw:*/ 'bg-white/5 text-white', | ||
dark: /*tw:*/ 'bg-dark-foreground/5 text-dark-foreground', | ||
light: /*tw:*/ 'bg-light-foreground/5 text-light-foreground', | ||
'blue-room': /*tw:*/ 'bg-blue-room/5 text-blue-room', | ||
train: /*tw:*/ 'bg-train/5 text-train', | ||
waterfall: /*tw:*/ 'bg-waterfall/5 text-waterfall', | ||
'camping-fire': /*tw:*/ 'bg-camping-fire/5 text-camping-fire' | ||
} | ||
} | ||
}) | ||
|
||
export const toggleEditButton = tv({ | ||
base: /*tw:*/ 'flex-1 rounded-lg py-1', | ||
variants: { | ||
theme: { | ||
transition: /*tw:*/ 'hover:bg-white/10', | ||
dark: /*tw:*/ 'hover:bg-dark-foreground/10', | ||
light: /*tw:*/ 'hover:bg-light-foreground/10', | ||
'blue-room': /*tw:*/ 'hover:bg-blue-room/10', | ||
train: /*tw:*/ 'hover:bg-train/10', | ||
waterfall: /*tw:*/ 'hover:bg-waterfall/10', | ||
'camping-fire': /*tw:*/ 'hover:bg-camping-fire/10' | ||
}, | ||
active: { | ||
true: '' | ||
} | ||
}, | ||
compoundVariants: [ | ||
{ | ||
theme: 'transition', | ||
active: true, | ||
class: /*tw:*/ 'bg-white/20' | ||
}, | ||
{ | ||
theme: 'dark', | ||
active: true, | ||
class: /*tw:*/ 'bg-dark-foreground/20' | ||
}, | ||
{ | ||
theme: 'light', | ||
active: true, | ||
class: /*tw:*/ 'bg-light-foreground/20' | ||
}, | ||
{ | ||
theme: 'blue-room', | ||
active: true, | ||
class: /*tw:*/ 'bg-blue-room/20' | ||
}, | ||
{ | ||
theme: 'train', | ||
active: true, | ||
class: /*tw:*/ 'bg-train/20' | ||
}, | ||
{ | ||
theme: 'waterfall', | ||
active: true, | ||
class: /*tw:*/ 'bg-waterfall/20' | ||
}, | ||
{ | ||
theme: 'camping-fire', | ||
active: true, | ||
class: /*tw:*/ 'bg-camping-fire/20' | ||
} | ||
] | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters