import { create } from "zustand/react" import { subscribeWithSelector } from "zustand/middleware" import type { Collection } from "@markone/core" import { createContext, memo, useCallback, useContext, useEffect, useRef } from "react" import { clsx } from "clsx" import { Button } from "~/components/button" import { useMnemonics } from "~/hooks/use-mnemonics" import { DialogKind, useCollectionPageStore } from "./-store" import { useStore } from "zustand" export enum CollectionListItemAction { Delete = "Delete", Edit = "Edit", } type ItemActionCallback = (collection: Collection, action: CollectionListItemAction) => void interface CollectionListProps { collections: Collection[] className?: string } interface CreateStoreOptions { collections: Collection[] } interface CollectionListState { collections: Collection[] selectedIndex: number selectedCollectionId: string isItemExpanded: boolean setCollections: (collections: Collection[]) => void setSelectedIndex: (index: number) => void setSelectedCollectionId: (id: string) => void setIsItemExpanded: (expanded: boolean) => void } type CollectionListStore = ReturnType const CollectionListStoreContext = createContext(null) function createCollectionListStore({ collections }: CreateStoreOptions) { return create()( subscribeWithSelector((set) => ({ collections, selectedIndex: 0, selectedCollectionId: collections.length > 0 ? collections[0].id : "", isItemExpanded: false, setCollections(collections: Collection[]) { set({ collections, selectedCollectionId: collections.length > 0 ? collections[0].id : "", selectedIndex: 0 }) }, setSelectedIndex(index: number) { set({ selectedIndex: index }) }, setSelectedCollectionId(id: string) { set({ selectedCollectionId: id }) }, setIsItemExpanded(expanded: boolean) { set({ isItemExpanded: expanded }) }, })), ) } function useCollectionListStoreContext() { const store = useContext(CollectionListStoreContext) if (!store) throw new Error("CollectionListStoreContext not found") return store } function useCollectionListStore(selector: (state: CollectionListState) => T): T { const store = useCollectionListStoreContext() return useStore(store, selector) } function CollectionList({ collections, className }: CollectionListProps) { const storeRef = useRef(null) if (!storeRef.current) { storeRef.current = createCollectionListStore({ collections }) } useEffect(() => { // biome-ignore lint/style/noNonNullAssertion: storeRef.current is already set above, so cant be null const store = storeRef.current! store.getState().setCollections(collections) }, [collections]) return ( <_CollectionList className={className} /> ) } const _CollectionList = memo(({ className }: { className?: string }) => { const store = useCollectionListStoreContext() const handleCollectionListAction = useCollectionPageStore((state) => state.handleCollectionListAction) useMnemonics( { j: selectNextItem, ArrowDown: selectNextItem, k: selectPrevItem, ArrowUp: selectPrevItem, h: collapseItem, ArrowLeft: collapseItem, l: expandItem, ArrowRight: expandItem, d: deleteItem, e: editItem, }, { ignore: useCallback(() => useCollectionPageStore.getState().dialog.kind !== DialogKind.None, []), }, ) function deleteItem() { const { collections, selectedIndex } = store.getState() handleCollectionListAction(collections[selectedIndex], CollectionListItemAction.Delete) } function editItem() { const { collections, selectedIndex } = store.getState() handleCollectionListAction(collections[selectedIndex], CollectionListItemAction.Edit) } function selectPrevItem() { const { collections, selectedIndex, setSelectedCollectionId } = store.getState() const prevIndex = selectedIndex - 1 if (prevIndex >= 0) { setSelectedCollectionId(collections[prevIndex].id) } } function selectNextItem() { const { collections, selectedIndex, setSelectedCollectionId } = store.getState() const nextIndex = selectedIndex + 1 if (nextIndex < collections.length) { setSelectedCollectionId(collections[nextIndex].id) } } function expandItem() { store.getState().setIsItemExpanded(true) } function collapseItem() { store.getState().setIsItemExpanded(false) } return (
) }) function ListContainer() { const collections = useCollectionListStore((state) => state.collections) const selectedItemId = useCollectionListStore((state) => state.selectedCollectionId) return collections.length === 0 ? (

You have not created any collections!

) : ( collections.map((collection, index) => ( )) ) } const CollectionListItem = memo( ({ collection, selected, index }: { collection: Collection; selected: boolean; index: number }) => { const store = useCollectionListStoreContext() const isItemExpanded = useCollectionListStore((state) => state.isItemExpanded) const setSelectedCollectionId = useCollectionListStore((state) => state.setSelectedCollectionId) const setIsItemExpanded = useCollectionListStore((state) => state.setIsItemExpanded) const handleCollectionListAction = useCollectionPageStore((state) => state.handleCollectionListAction) useEffect(() => { if (selected) { store.getState().setSelectedIndex(index) } }, [selected, index, store]) function onItemHover() { if (!store.getState().isItemExpanded) { setSelectedCollectionId(collection.id) } } function deleteItem() { handleCollectionListAction(collection, CollectionListItemAction.Delete) } function editItem() { handleCollectionListAction(collection, CollectionListItemAction.Edit) } return (
  • {collection.name}

    {collection.description}

    {isItemExpanded && selected ? (
     
    ) : null}
  • ) }, ) export { CollectionList } export type { CollectionListState }