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 }