131 lines
3.6 KiB
TypeScript
131 lines
3.6 KiB
TypeScript
import type { Collection } from "@markone/core"
|
|
import { Link } from "@tanstack/react-router"
|
|
import { clsx } from "clsx"
|
|
import { memo, useCallback, useRef } from "react"
|
|
import { Button } from "~/components/button"
|
|
import { List, type ListRef } from "~/components/list"
|
|
import { DialogKind, useCollectionPageStore } from "./-store"
|
|
import { useMnemonics } from "~/hooks/use-mnemonics"
|
|
|
|
export enum CollectionListItemAction {
|
|
Delete = "Delete",
|
|
Edit = "Edit",
|
|
}
|
|
|
|
interface CollectionListProps {
|
|
collections: Collection[]
|
|
className?: string
|
|
onItemAction: (collection: Collection, action: CollectionListItemAction) => void
|
|
}
|
|
|
|
const CollectionListItem = memo(
|
|
({
|
|
collection,
|
|
isSelected,
|
|
isExpanded,
|
|
onSelect,
|
|
onExpand,
|
|
onItemAction,
|
|
}: {
|
|
collection: Collection
|
|
isSelected: boolean
|
|
isExpanded: boolean
|
|
onSelect: () => void
|
|
onExpand: () => void
|
|
onItemAction: (collection: Collection, action: CollectionListItemAction) => void
|
|
}) => (
|
|
<div
|
|
className={clsx("group flex flex-row justify-start py-2", {
|
|
"bg-teal-600 text-stone-100": isExpanded,
|
|
"text-teal-600": isSelected && !isExpanded,
|
|
})}
|
|
>
|
|
<button
|
|
type="button"
|
|
disabled={!isSelected}
|
|
className={clsx("select-none flex items-start font-bold hover:bg-teal-600 hover:text-stone-100", {
|
|
invisible: !isSelected,
|
|
})}
|
|
onClick={onExpand}
|
|
>
|
|
<span className="sr-only">Options for this collection</span>
|
|
<span> </span>
|
|
<span className={isExpanded ? "rotate-90" : ""}>></span>
|
|
<span> </span>
|
|
</button>
|
|
<div className="flex flex-col w-full">
|
|
<div className="block w-full text-start font-bold">
|
|
<Link to={`/collections/${collection.id}`} className={isSelected ? "underline" : ""}>
|
|
{collection.name}
|
|
</Link>
|
|
</div>
|
|
<p className="opacity-80 text-sm">{collection.description}</p>
|
|
{isExpanded ? (
|
|
<div className="flex flex-col space-y-1 md:flex-row md:space-y-0 md:space-x-2 items-end justify-between pt-2">
|
|
<div className="flex space-x-2">
|
|
<Button
|
|
variant="light"
|
|
className="text-sm"
|
|
onClick={() => onItemAction(collection, CollectionListItemAction.Edit)}
|
|
>
|
|
<span className="underline">E</span>DIT
|
|
</Button>
|
|
<Button
|
|
variant="light"
|
|
className="text-sm"
|
|
onClick={() => onItemAction(collection, CollectionListItemAction.Delete)}
|
|
>
|
|
<span className="underline">D</span>ELETE
|
|
</Button>
|
|
<span className="-ml-2"> </span>
|
|
</div>
|
|
</div>
|
|
) : null}
|
|
</div>
|
|
</div>
|
|
),
|
|
)
|
|
|
|
function CollectionList({ collections, className, onItemAction }: CollectionListProps) {
|
|
const listRef = useRef<ListRef<Collection>>(null)
|
|
|
|
useMnemonics(
|
|
{
|
|
e: () => {
|
|
const selectedCollection = listRef.current?.selectedItem
|
|
if (selectedCollection) {
|
|
onItemAction(selectedCollection, CollectionListItemAction.Edit)
|
|
}
|
|
},
|
|
d: () => {
|
|
const selectedCollection = listRef.current?.selectedItem
|
|
if (selectedCollection) {
|
|
onItemAction(selectedCollection, CollectionListItemAction.Delete)
|
|
}
|
|
},
|
|
},
|
|
{ ignore: useCallback(() => useCollectionPageStore.getState().dialog.kind !== DialogKind.None, []) },
|
|
)
|
|
|
|
return (
|
|
<List
|
|
ref={listRef}
|
|
className="-mt-2"
|
|
items={collections}
|
|
emptyMessage="No collections found!"
|
|
renderItem={({ item: collection, isSelected, isExpanded, onSelect, onExpand }) => (
|
|
<CollectionListItem
|
|
collection={collection}
|
|
isSelected={isSelected}
|
|
isExpanded={isExpanded}
|
|
onSelect={onSelect}
|
|
onExpand={onExpand}
|
|
onItemAction={onItemAction}
|
|
/>
|
|
)}
|
|
/>
|
|
)
|
|
}
|
|
|
|
export { CollectionList }
|