CategorySelect.jsx
6186 Chars | 230 Lines | 6.20 kb | Text File
Parent Directory
import React, { useContext, useState } from 'react'
import Dropdown from 'react-bootstrap/Dropdown'
import Modal from 'react-bootstrap/Modal'
import Button from 'react-bootstrap/Button'
import { hslToHex } from '@/lib/utilities'
import { saveStarred, usePack } from '@/stores/pack'
import { useShallow } from 'zustand/react/shallow'
import { ThemeContext } from '@/lib/context'
import { syncLocalStorage } from '@/lib/firebase'
import { useUser } from '@/stores/user'
/**
* A dropdown menu that allows the user to select a category for a card and contains a modal to add or edit categories
*
* Directly modifies the `Pack` state
*
* @component
* @param {number} index - The index of the card in the pack
*/
function CategorySelect({ index }) {
const [user] = useUser(useShallow((state) => [state.user]))
const [
cards, // TODO: fix misleading name
uuid,
packUUID,
categories,
addCategory,
editCategory,
setCardCategory,
removeCardCategory,
canEdit,
] = usePack(
useShallow((state) => [
// Data
state.pack.content[index].category,
state.pack.content[index].uuid,
state.pack.uuid,
state.pack.categories,
state.addCategory,
state.editCategory,
state.setCardCategory,
state.removeCardCategory,
state.canEdit,
])
)
const theme = useContext(ThemeContext)
// New Category modal
const [show, setShow] = useState(false)
const [newCategory, setNewCategory] = useState('')
const [newColor, setNewColor] = useState(['', ''])
const [replaceCategory, setReplaceCategory] = useState('')
const [editingCategoryDropdown, editCategoryDropdown] = useState(false)
const [starred, setStarred] = useState(
localStorage.getItem(`starred-${packUUID}-${uuid}`) === 'true'
)
function submitNewCategory() {
if (replaceCategory == '') {
addCategory(newCategory, newColor)
} else {
editCategory(replaceCategory, { name: newCategory, colors: newColor })
setNewCategory('New Category')
setNewColor(['#FFFFFF', '#FFFFFF'])
setReplaceCategory('')
}
setShow(false)
}
function changeCardCategory(category) {
setCardCategory(index, category)
}
function generateNewColor(currentHexCode) {
var hue = currentHexCode
if (currentHexCode == null) {
hue = Math.floor(Math.random() * 360)
setNewColor([hslToHex(hue, 100, 97), hslToHex(hue, 100, 99)])
} else {
setNewColor([hue, hue])
}
}
function showEditModal(id, category) {
setNewCategory(category.name)
setNewColor(category.colors)
setReplaceCategory(id)
setShow(true)
}
async function toggleStarred(val) {
setStarred(val)
saveStarred(val, packUUID, uuid, user?.uid)
}
return (
<>
<div className="d-flex justify-content-between align-items-center">
{starred ? (
<Button
size="sm"
variant={theme.dark ? 'secondary' : 'dark'}
onClick={() => toggleStarred(false)}
tabIndex="-1"
>
⭐
</Button>
) : (
<Button
size="sm"
variant={theme.color}
onClick={() => toggleStarred(true)}
tabIndex="-1"
>
⭐
</Button>
)}
{canEdit ? (
<Dropdown>
<Dropdown.Toggle
size="sm"
className="ms-2"
variant={theme.color}
tabIndex="-1"
>
{categories[cards].name}
</Dropdown.Toggle>
<Dropdown.Menu>
{Object.entries(categories).map(([id, category]) => (
<div key={id}>
<Dropdown.Item
onClick={() => changeCardCategory(id)}
key={category.name}
>
{category.name}
</Dropdown.Item>
{editingCategoryDropdown && category.name !== 'Default' && (
<div className="d-flex justify-content-between align-items-center ps-2 pe-2 pt-1 pb-1">
<Button
size="sm"
variant="outline-light"
onClick={() => showEditModal(id, category)}
>
✏️
</Button>
<Button
size="sm"
variant="outline-light"
onClick={() => removeCardCategory(id)}
>
🗑️
</Button>
</div>
)}
</div>
))}
<li>
<hr className="dropdown-divider" />
</li>
<div className="text-center d-flex justify-content-between align-items-center ps-2 pe-2">
<Button
size="sm"
variant={theme.color}
onClick={() => editCategoryDropdown(!editingCategoryDropdown)}
>
⚒️
</Button>
</div>
<li>
<hr className="dropdown-divider" />
</li>
<Dropdown.Item
className="text-center"
onClick={() => {
generateNewColor()
setShow(true)
}}
>
New
</Dropdown.Item>
</Dropdown.Menu>
</Dropdown>
) : (
<Button size="sm" className="ms-2" variant={theme.color} tabIndex="-1" disabled>
{categories[cards].name}
</Button>
)}
</div>
{/* New Category Modal */}
<Modal show={show} onHide={() => setShow(false)}>
<Modal.Header closeButton>
<Modal.Title>Categories</Modal.Title>
</Modal.Header>
<Modal.Body>
<input
type="text"
value={newCategory}
onChange={(e) => setNewCategory(e.target.value)}
className="form-control"
/>
<div className="mt-3 d-flex justify-content-between">
<input
type="color"
className="form-control form-control-color"
value={newColor[0]}
onChange={(e) => generateNewColor(e.target.value)}
/>
<Button variant="dark" onClick={() => generateNewColor(null)}>
Randomize
</Button>
</div>
</Modal.Body>
<Modal.Footer>
<Button variant="primary" onClick={submitNewCategory}>
{/* eslint-disable-next-line prettier/prettier */}
{replaceCategory == '' ? 'Add New Category' : 'Set Category'}
</Button>
</Modal.Footer>
</Modal>
</>
)
}
export default CategorySelect