/ src / components / table.tsx
table.tsx
 1  import { useState } from 'react'
 2  import { LiaEditSolid } from 'react-icons/lia'
 3  
 4  import { type Radix, num2str } from '#/radixes.ts'
 5  
 6  
 7  export function Tables({ children }: { children: React.ReactNode[] }) {
 8  	return (
 9  		<main className="flex flex-wrap justify-center items-start gap-4 overflow-x-clip">
10  			{ children }
11  		</main>
12  	)
13  }
14  
15  export default function Table({ radix, numbers, low, high, mainRow, columns, rows, updateColumns, updateRows }: {
16  	radix: Radix,
17  	numbers: number[][],
18  	low: number,
19  	high: number,
20  	mainRow?: number,
21  	columns?: number,
22  	rows?: number,
23  	updateColumns?: (columns: number) => void,
24  	updateRows?: (rows: number) => void,
25  }) {
26  	const [ edit, setEdit ] = useState(false)
27  
28  	const space = low === 0 ? high : high - low
29  
30  	return (
31  		<div className="card bg-white max-w-full shadow-xl">
32  			<div className="flex justify-end items-center mx-2">
33  				<div className="flex-1 flex justify-center">
34  					<span className="tooltip tooltip-top whitespace-pre before:content-[attr(data-tip)] before:max-w-200" data-tip={getCharsForTooltip(radix)}>
35  						<span className="card-title badge badge-lg badge-outline m-2">{radix.name}</span>
36  					</span>
37  				</div>{ edit &&
38  				<div className="flex justify-end">
39  					<EditRowsOrColumns rows={rows} update={updateRows} setEdit={setEdit}/>
40  					<EditRowsOrColumns columns={columns} update={updateColumns} setEdit={setEdit}/>
41  				</div>}{ (updateColumns ?? updateRows) &&
42  				<LiaEditSolid onClick={() => { setEdit(!edit) }}/>}
43  			</div>
44  			<div className="card-body overflow-scroll p-2">
45  				<table className="table table-xs table-fixed text-sm w-auto">
46  					<tbody>{ numbers.map((row, rowIndex) =>
47  						<tr className={`hover row${rowIndex === mainRow ? ' bg-base-200' : ''}`} key={row[0]}>{ row.map(number =>
48  							<td className="text-right px-0.5 py-0.5 w-8" key={number}>
49  								<Value value={number} radix={radix} low={low} space={space}/>
50  							</td>)}
51  						</tr>)}
52  					</tbody>
53  				</table>
54  			</div>
55  		</div>
56  	)
57  }
58  
59  function Value({ value, radix, low, space }: { value: number, radix: Radix, low: number, space: number }) {
60  	if (Number.isNaN(value)) return <span/>
61  	return (
62  		<div className="relative">
63  			<div
64  				className="font-mono font-semibold text-xl text-right whitespace-nowrap"
65  				style={{ color: `hsl(${(low === 0 ? value : value - low) / space * 300} 80% 40%)` }}
66  			>
67  				{ num2str(BigInt(value), radix) }
68  			</div>
69  			<span className="text-[0.6em] leading-0.5 text-center absolute right-0 top-0.5">{value}</span>
70  		</div>
71  	)
72  }
73  
74  function EditRowsOrColumns({ columns, rows, update, setEdit }: { columns?: number, rows?: number, update?: (value: number) => void, setEdit: (value: boolean) => void }) {
75  	if (!update) return
76  
77  	return (
78  		<div className="tooltip tooltip-bottom" data-tip={columns ? 'number of columns' : 'number of rows'}>
79  			<input
80  				className="input input-xs w-[4em]"
81  				type="number"
82  				value={columns ?? rows}
83  				onChange={e => { update(Number(e.target.value)) }}
84  				onKeyDown={e => { if (e.key === 'Escape') setEdit(false) }}
85  			/>
86  		</div>
87  	)
88  }
89  
90  export function getCharsForTooltip(radix: Radix): string {
91  	return [ ...radix.values.entries() ].slice(radix.system === 'sum' || radix.system === 'bijective' ? 1 : 0).map(([ k, v ], i) => `${k}:${v}${(i + 1) % (radix.system === 'sum' ? 9 : 10) === 0 ? '\n' : ' '}`).join('')
92  }