1
0
forked from a/lifeto-shop
lifeto-shop/src/lib/table/tanstack.tsx

147 lines
4.4 KiB
TypeScript
Raw Normal View History

2025-06-20 05:41:10 +00:00
import { createColumnHelper } from '@tanstack/react-table'
import { useAtomValue, useSetAtom } from 'jotai'
import { useMemo } from 'react'
import { currentItemSelectionAtom, itemSelectionSetActionAtom } from '@/state/atoms'
import { StatsColumns } from '../columns'
import { ItemWithSelection } from './defs'
2025-05-25 05:17:41 +00:00
2025-06-20 05:41:10 +00:00
const ch = createColumnHelper<ItemWithSelection>()
2025-05-25 05:17:41 +00:00
const columns = {
icon: ch.display({
id: 'icon',
2025-06-20 05:41:10 +00:00
header: function Component(_col) {
2025-05-25 05:17:41 +00:00
return <div className="flex flex-row justify-center"></div>
},
2025-06-20 05:41:10 +00:00
cell: function Component({ row }) {
const setItemSelection = useSetAtom(itemSelectionSetActionAtom)
const c = useAtomValue(currentItemSelectionAtom)
const selected = useMemo(() => {
return c[0].has(row.original.item.id)
2025-05-25 05:17:41 +00:00
}, [c])
2025-06-20 05:41:10 +00:00
return (
2025-06-20 06:18:37 +00:00
<button
type="button"
2025-06-20 05:41:10 +00:00
className={`no-select flex flex-row ${row.original.status?.selected ? 'animate-pulse' : ''}`}
onClick={_e => {
setItemSelection({
[row.original.item.id]: selected ? undefined : row.original.item.item_count,
})
}}
>
<div className="flex flex-row w-6 h-6 justify-center">
<img
src={row.original.item.item_image || ''}
alt="icon"
className="select-none object-contain select-none"
/>
</div>
2025-06-20 06:18:37 +00:00
</button>
2025-06-20 05:41:10 +00:00
)
2025-05-25 05:17:41 +00:00
},
}),
count: ch.display({
id: 'count',
2025-06-20 05:41:10 +00:00
header: function Component(_col) {
2025-05-25 05:17:41 +00:00
return <div className="flex flex-row justify-center">#</div>
},
2025-06-20 05:41:10 +00:00
cell: function Component({ row }) {
const c = useAtomValue(currentItemSelectionAtom)
const setItemSelection = useSetAtom(itemSelectionSetActionAtom)
const currentValue = useMemo(() => {
const got = c[0].get(row.original.item.id)
if (got !== undefined) {
return got.toString()
2025-05-25 05:17:41 +00:00
}
2025-06-20 05:41:10 +00:00
return ''
2025-05-25 05:17:41 +00:00
}, [c])
const itemCount = row.original.item.item_count
2025-06-20 05:41:10 +00:00
return (
<div
className={`flex flex-row select-none ${row.original.status?.selected ? 'bg-gray-200' : ''}`}
>
<input
className="w-10 text-center "
value={currentValue}
onChange={e => {
if (e.target.value === '') {
setItemSelection({ [row.original.item.id]: undefined })
return
}
if (e.target.value === '-') {
setItemSelection({
[row.original.item.id]: itemCount,
})
}
let parsedInt = parseInt(e.target.value)
if (Number.isNaN(parsedInt)) {
return
}
if (parsedInt > itemCount) {
parsedInt = itemCount
}
2025-05-25 05:17:41 +00:00
setItemSelection({
2025-06-20 05:41:10 +00:00
[row.original.item.id]: parsedInt,
2025-05-25 05:17:41 +00:00
})
2025-06-20 05:41:10 +00:00
}}
placeholder={itemCount.toString()}
/>
</div>
)
2025-05-25 05:17:41 +00:00
},
}),
name: ch.display({
id: 'name',
2025-06-20 05:41:10 +00:00
header: _col => {
return <div className="flex flex-row text-sm">name</div>
2025-05-25 05:17:41 +00:00
},
2025-06-20 05:41:10 +00:00
cell: function Component({ row }) {
return (
<div className="flex flex-row whitespace-pre">
<span>{row.original.item.item_name}</span>
</div>
)
2025-05-25 05:17:41 +00:00
},
}),
slots: ch.display({
id: 'slots',
2025-06-20 05:41:10 +00:00
header: _col => {
return <div className="flex flex-row text-sm">slots</div>
2025-05-25 05:17:41 +00:00
},
2025-06-20 05:41:10 +00:00
cell: function Component({ row }) {
return (
<div className="flex flex-row justify-center">
<span>{row.original.item.item_slots}</span>
</div>
)
2025-05-25 05:17:41 +00:00
},
}),
stats: ch.group({
id: 'stats',
2025-06-20 05:41:10 +00:00
header: _col => {
return <div className="flex flex-row text-sm">stats</div>
2025-05-25 05:17:41 +00:00
},
columns: [
2025-06-20 05:41:10 +00:00
...StatsColumns.map(c => {
2025-05-25 05:17:41 +00:00
return ch.display({
2025-06-20 05:41:10 +00:00
id: `stats.${c}`,
header: _col => {
return <div className="flex flex-row text-sm justify-center">{c}</div>
2025-05-25 05:17:41 +00:00
},
2025-06-20 05:41:10 +00:00
cell: function Component({ row }) {
2025-05-25 05:17:41 +00:00
const stats = row.original.item.stats
2025-06-20 05:41:10 +00:00
const stat = stats ? stats[c] : ''
return (
<div className={`flex flex-row justify-start ${stat ? 'border' : ''}`}>
<span>{stat}</span>
</div>
)
2025-05-25 05:17:41 +00:00
},
})
2025-06-20 05:41:10 +00:00
}),
],
2025-05-25 05:17:41 +00:00
}),
2025-06-20 05:41:10 +00:00
} as const
2025-05-25 05:17:41 +00:00
2025-06-20 05:41:10 +00:00
export const InventoryColumns = columns