File Extension Image for Cards.jsx Cards.jsx
4281 Chars | 165 Lines | 4.28 kb | Text File Parent Directory
import React, { useEffect, memo, lazy, Suspense, useContext } from 'react'
import { useState } from 'react'
import StaticPair from './Pair/StaticPair'

// Contains a solid 20% of the bundle, so lazy load it
const EditingPair = lazy(() => import('./Pair/EditingPair'))

import CategorySelect from './CategorySelect'
import PictureSelect from './PictureSelect'

import Button from 'react-bootstrap/Button'

import { usePack } from '@/stores/pack'
import { useShallow } from 'zustand/react/shallow'

import { motion } from 'framer-motion'
import { ThemeContext } from '@/lib/context'

function MotionWrapper({ yes, index, style, children }) {
	if (yes) {
		return (
			<motion.div
				key={index}
				className="p-3"
				style={style}
				initial={{ x: -100 }}
				animate={{ x: 0 }}
				exit={{ scale: 0 }}
			>
				{children}
			</motion.div>
		)
	} else {
		return children
	}
}

/**
 * Rendered for each element in `pack.content`
 *
 * Creates either a `StaticPair` or `EditingPair` depending on the `edit` prop
 *
 * If `edit` is false, it includes an edit button that locally overrides the `edit` prop
 *
 * @component
 * @param {number} index - The index of the card in the pack
 * @param {boolean} edit - Whether or not the card is being edited
 */
function Cards({ index, edit }) {
	const [cards, previousCard, categories, packLength, deleteCard, canEdit] = usePack(
		useShallow((state) => [
			// Data
			state.pack.content[index],
			state.pack.content[index - 1],
			state.pack.categories,
			state.pack.content.length,
			state.deleteCard,
			// Editing
			state.canEdit,
		])
	)

	const theme = useContext(ThemeContext)

	const [editing, setEditing] = useState(edit)

	useEffect(() => {
		setEditing(edit)
	}, [edit])

	function deletePair() {
		// let p = [...pack.content]
		// p.splice(index, 1)
		deleteCard(index)
	}

	if (cards === undefined) {
		// If the card is undefined, this is probably the first card being deleted
		return <></>
	}

	var extra = ''
	var cardCSS = {}
	if (index !== 0 && cards?.picture !== undefined) {
		if (previousCard?.picture !== cards?.picture && cards.picture !== undefined) {
			extra = <img className="w-100" src={cards.picture}></img>
			cardCSS = {
				boxShadow: '-2px 0 0 #D7DDFC',
			}
		} else if (previousCard?.picture === cards?.picture) {
			cardCSS = {
				boxShadow: '-2px 0 0 #DEE2E6',
			}
		}
	} else if (cards?.picture !== undefined) {
		// max-width: 100px;
		// width: 100%;
		// height: auto;
		extra = <img className="w-100" src={cards.picture}></img>
		cardCSS = {
			boxShadow: '-2px 0 0 #DEE2E6',
		}
	}

	return (
		<div
			style={{
				backgroundImage: `linear-gradient(to right, ${
					categories[cards.category]['colors'][0] + (theme.dark && '33')
				},${categories[cards.category]['colors'][1] + (theme.dark && '33')})`,
				...cardCSS,
			}}
			className="p-3"
		>
			<>{extra}</>
			<div className="d-flex justify-content-between align-items-center mt-5">
				<CategorySelect index={index} />

				<div className="d-flex align-items-center">
					{canEdit ? <PictureSelect index={index} /> : <></>}
					<span className="text-muted ms-3 me-4">{index + 1}</span>
				</div>
			</div>

			{canEdit && editing ? (
				<Suspense fallback={<div>Loading...</div>}>
					<EditingPair index={index} />
				</Suspense>
			) : (
				<StaticPair index={index} />
			)}

			{/* Edit/Remove buttons underneath the cards */}
			{canEdit && (
				<div className="d-flex justify-content-between">
					{!edit && (
						<Button
							className="text-muted text-decoration-none"
							variant="link"
							size="sm"
							onClick={() => setEditing(!editing)}
						>
							{editing ? 'Save' : 'Edit'}
						</Button>
					)}
					{/* eslint-disable-next-line prettier/prettier */}
					{packLength > 1 && (
						<Button
							className="text-muted text-decoration-none"
							variant="link"
							size="sm"
							onClick={deletePair}
							tabIndex="-1"
						>
							Remove
						</Button>
					)}
				</div>
			)}
		</div>
	)
}

// Memo is necessary to prevent the re-rendering of all cards when one is edited
export default memo(Cards)