@@ -2,21 +2,15 @@ import SvgCircle from '@/features/common/components/svg/circle'
22import SvgPointerLeft from '@/features/common/components/svg/pointer-left'
33import SvgPointerRight from '@/features/common/components/svg/pointer-right'
44import { cn } from '@/features/common/utils'
5- import { useMemo , useState } from 'react'
5+ import { useMemo } from 'react'
66
7- type AccountFoo = {
8- account : string
9- // the status for the arrow only
10- status : 'from' | 'to' | 'middle' | 'outside'
11- }
12-
13- type TransactionFooDrawing = {
7+ type TransactionArrow = {
148 from : number
159 to : number
1610 direction : 'leftToRight' | 'rightToLeft' | 'toSelf'
1711}
1812
19- type TransactionTableRowProps = {
13+ type TransactionRowProps = {
2014 transaction : Transaction
2115 cellHeight ?: number
2216 lineWidth ?: number
@@ -27,66 +21,110 @@ type TransactionTableRowProps = {
2721 indentLevel ?: number
2822 verticalBars ?: number [ ]
2923}
30- function TransactionTableRow ( {
24+ function TransactionRow ( {
3125 transaction,
3226 accounts,
3327 hasParent = false ,
3428 hasNextSibbling = false ,
3529 hasChildren = false ,
3630 indentLevel = 0 ,
3731 verticalBars,
38- } : TransactionTableRowProps ) {
39- const foo = useMemo ( ( ) => calcTransactionFoo ( transaction , accounts ) , [ accounts , transaction ] )
32+ } : TransactionRowProps ) {
33+ const transactionArrow = useMemo ( ( ) => calcTransactionArrow ( transaction , accounts ) , [ accounts , transaction ] )
4034
4135 return (
4236 < >
43- < tr >
44- < td className = { cn ( 'p-0 relative pr-8' ) } >
45- { verticalBars &&
37+ < div className = { cn ( 'p-0 relative pr-8' ) } >
38+ {
39+ // The side vertical bars when there are nested items
40+ verticalBars &&
4641 verticalBars . length &&
4742 verticalBars
4843 . filter ( ( b ) => b > 0 )
49- . map ( ( b , i ) => < div key = { i } className = { cn ( 'h-10 border-primary border-l-2 absolute' ) } style = { { marginLeft : b * 16 } } > </ div > ) }
50- < div className = { cn ( `relative h-10 p-0 flex items-center` , 'px-0' ) } style = { { marginLeft : indentLevel * 16 } } >
51- { hasParent && (
52- < div className = { cn ( 'w-8' , `border-primary border-l-2 border-b-2 rounded-bl-lg` , `h-[50%]` , `absolute top-0 left-0` ) } > </ div >
53- ) }
54- < div className = { cn ( 'inline ml-8' ) } > { transaction . name } </ div >
55- { hasParent && hasNextSibbling && (
56- < div className = { cn ( 'w-8' , 'border-primary border-l-2' , 'h-[22px]' , 'absolute top-[18px] left-0' ) } > </ div >
57- ) }
58- { hasChildren && (
44+ . map ( ( b , i ) => (
45+ < div
46+ key = { i }
47+ className = { cn ( 'h-full border-primary absolute' ) }
48+ style = { { marginLeft : b * graphConfig . indentationWidth , borderLeftWidth : `${ graphConfig . lineWidth } px` } }
49+ > </ div >
50+ ) )
51+ }
52+ < div
53+ className = { cn ( `relative h-full p-0 flex items-center` , 'px-0' ) }
54+ style = { { marginLeft : indentLevel * graphConfig . indentationWidth } }
55+ >
56+ {
57+ // The connection between this transaction and the parent
58+ hasParent && (
5959 < div
60- className = { cn ( 'w-2 ml-4' , 'border-primary border-l-2 border-t-2 rounded-tl-lg' , 'h-[22px]' , 'absolute top-[18px] left-0' ) }
60+ className = { cn ( 'w-8' , `border-primary rounded-bl-lg` , `h-1/2` , `absolute top-0 left-0` ) }
61+ style = { { borderLeftWidth : `${ graphConfig . lineWidth } px` , borderBottomWidth : `${ graphConfig . lineWidth } px` } }
62+ > </ div >
63+ )
64+ }
65+ < div className = { cn ( 'inline ml-8' ) } > { transaction . name } </ div >
66+ {
67+ // The connection between this transaction and the next sibbling
68+ hasParent && hasNextSibbling && (
69+ < div
70+ className = { cn ( 'w-8' , 'border-primary' , 'absolute top-[18px] left-0' ) }
71+ style = { {
72+ borderLeftWidth : `${ graphConfig . lineWidth } px` ,
73+ height : `calc(50% + ${ graphConfig . lineWidth } px)` ,
74+ top : `calc(50% - ${ graphConfig . lineWidth } px)` ,
75+ } }
76+ > </ div >
77+ )
78+ }
79+ {
80+ // The connection between this transaction and the children
81+ hasChildren && (
82+ < div
83+ className = { cn ( 'w-2 ml-4' , 'border-primary rounded-tl-lg' , 'absolute left-0' ) }
84+ style = { {
85+ borderLeftWidth : `${ graphConfig . lineWidth } px` ,
86+ borderTopWidth : `${ graphConfig . lineWidth } px` ,
87+ height : `calc(50% + ${ graphConfig . lineWidth } px)` ,
88+ top : `calc(50% - ${ graphConfig . lineWidth } px)` ,
89+ } }
6190 > </ div >
62- ) }
63- </ div >
64- </ td >
65- { accounts . map ( ( account , index ) => {
66- if ( index < foo . from || index > foo . to ) return < td key = { index } > </ td >
67- if ( index === foo . from )
68- return (
69- < td key = { index } colSpan = { foo . to - foo . from + 1 } >
70- < div className = { cn ( 'flex items-center justify-center' ) } >
71- < SvgCircle width = { 20 } height = { 20 } > </ SvgCircle >
72- < div
73- style = { { width : `calc(${ ( 100 - 100 / ( foo . to - foo . from + 1 ) ) . toFixed ( 2 ) } % - 20px)` , height : '20px' } }
74- className = "relative text-primary"
75- >
76- { foo . direction === 'rightToLeft' && < SvgPointerLeft className = { cn ( 'absolute top-0 left-0' ) } /> }
77- < div className = { cn ( 'border-primary border-b-2 h-1/2' ) } > </ div >
78- { foo . direction === 'leftToRight' && < SvgPointerRight className = { cn ( 'absolute top-0 right-0' ) } /> }
79- </ div >
80- < SvgCircle width = { 20 } height = { 20 } > </ SvgCircle >
81- </ div >
82- </ td >
8391 )
84- else return null
85- } ) }
86- </ tr >
92+ }
93+ </ div >
94+ </ div >
95+ { accounts . map ( ( _ , index ) => {
96+ if ( index < transactionArrow . from || index > transactionArrow . to ) return < div key = { index } > </ div >
97+ if ( index === transactionArrow . from )
98+ return (
99+ < div
100+ className = { cn ( 'flex items-center justify-center' ) }
101+ style = { {
102+ // 2 and 3 are the number to offset the name column
103+ gridColumnStart : transactionArrow . from + 2 ,
104+ gridColumnEnd : transactionArrow . to + 3 ,
105+ } }
106+ >
107+ < SvgCircle width = { graphConfig . circleDimension } height = { graphConfig . circleDimension } > </ SvgCircle >
108+ < div
109+ style = { {
110+ width : `calc(${ ( 100 - 100 / ( transactionArrow . to - transactionArrow . from + 1 ) ) . toFixed ( 2 ) } % - ${ graphConfig . circleDimension } px)` ,
111+ height : `${ graphConfig . circleDimension } px` ,
112+ } }
113+ className = "relative text-primary"
114+ >
115+ { transactionArrow . direction === 'rightToLeft' && < SvgPointerLeft className = { cn ( 'absolute top-0 left-0' ) } /> }
116+ < div className = { cn ( 'border-primary border-b-2 h-1/2' ) } > </ div >
117+ { transactionArrow . direction === 'leftToRight' && < SvgPointerRight className = { cn ( 'absolute top-0 right-0' ) } /> }
118+ </ div >
119+ < SvgCircle width = { graphConfig . circleDimension } height = { graphConfig . circleDimension } > </ SvgCircle >
120+ </ div >
121+ )
122+ else return null
123+ } ) }
124+
87125 { hasChildren &&
88126 transaction . transactions ?. map ( ( childTransaction , index , arr ) => (
89- < TransactionTableRow
127+ < TransactionRow
90128 transaction = { childTransaction }
91129 hasChildren = { childTransaction . transactions && childTransaction . transactions . length > 0 }
92130 hasParent = { true }
@@ -159,56 +197,55 @@ export function GroupPage() {
159197 } ,
160198 ] ,
161199 }
162- const accounts = extractSendersAndReceivers ( group )
163- const allTransactionCounts = 16
200+ const { transactionCount, accounts } = extractSendersAndReceivers ( group )
164201
165202 return (
166- < table className = { cn ( 'relative' ) } >
167- < tr >
168- < th > </ th >
169- { accounts . map ( ( account , index ) => (
170- < th className = { cn ( 'w-32 p-2 h-10' ) } key = { index } >
171- { account }
172- </ th >
173- ) ) }
174- </ tr >
175- < tbody className = { cn ( 'absolute top-10 right-0 -z-10' ) } >
176- < tr >
177- < td className = { cn ( 'p-0' ) } > </ td >
178- < td
203+ < div
204+ className = { cn ( 'relative grid' ) }
205+ style = { {
206+ gridTemplateColumns : `minmax(${ graphConfig . colWidth } px, 1fr) repeat(${ accounts . length } , ${ graphConfig . colWidth } px)` ,
207+ gridTemplateRows : `repeat(${ transactionCount + 1 } , ${ graphConfig . rowHeight } px)` ,
208+ } }
209+ >
210+ < div > { /* The first header cell is empty */ } </ div >
211+ { accounts . map ( ( account , index ) => (
212+ < div className = { cn ( 'p-2 flex justify-center' ) } key = { index } >
213+ { account }
214+ </ div >
215+ ) ) }
216+ < div className = { cn ( 'absolute right-0 -z-10' ) } style = { { top : `${ graphConfig . rowHeight } px` } } >
217+ < div >
218+ < div className = { cn ( 'p-0' ) } > </ div >
219+ < div
179220 className = { cn ( 'p-0' ) }
180- rowSpan = { allTransactionCounts }
181- colSpan = { accounts . length }
182- style = { { height : `${ allTransactionCounts * 40 } px` , width : `${ 128 * accounts . length } px` } }
221+ style = { { height : `${ transactionCount * graphConfig . rowHeight } px` , width : `${ graphConfig . colWidth * accounts . length } px` } }
183222 >
184223 < div
185224 className = { cn ( 'grid h-full' ) }
186225 style = { {
187226 gridTemplateColumns : `repeat(${ accounts . length } , minmax(0, 1fr))` ,
188- height : `${ allTransactionCounts * 40 } px` ,
227+ height : `${ transactionCount * graphConfig . rowHeight } px` ,
189228 } }
190229 >
191- { accounts . map ( ( account , index ) => (
230+ { accounts . map ( ( _ , index ) => (
192231 < div key = { index } className = { cn ( 'flex justify-center' ) } >
193232 < div className = { cn ( 'border-muted border-l-2 h-full border-dashed' ) } > </ div >
194233 </ div >
195234 ) ) }
196235 </ div >
197- </ td >
198- </ tr >
199- </ tbody >
200- < tbody >
201- { group . transactions . map ( ( transaction , index , arr ) => (
202- < TransactionTableRow
203- transaction = { transaction }
204- hasChildren = { transaction . transactions && transaction . transactions . length > 0 }
205- hasParent = { false }
206- hasNextSibbling = { index < arr . length - 1 }
207- accounts = { accounts }
208- />
209- ) ) }
210- </ tbody >
211- </ table >
236+ </ div >
237+ </ div >
238+ </ div >
239+ { group . transactions . map ( ( transaction , index , arr ) => (
240+ < TransactionRow
241+ transaction = { transaction }
242+ hasChildren = { transaction . transactions && transaction . transactions . length > 0 }
243+ hasParent = { false }
244+ hasNextSibbling = { index < arr . length - 1 }
245+ accounts = { accounts }
246+ />
247+ ) ) }
248+ </ div >
212249 )
213250}
214251
@@ -223,14 +260,16 @@ export type Transaction = {
223260 receiver : string
224261}
225262
226- function extractSendersAndReceivers ( group : Group ) : string [ ] {
227- let sendersAndReceivers : string [ ] = [ ]
263+ function extractSendersAndReceivers ( group : Group ) {
264+ let transactionCount = 0
265+ let accounts : string [ ] = [ ]
228266
229267 function extract ( transactionArr : Transaction [ ] ) {
230268 if ( transactionArr ) {
231269 transactionArr . forEach ( ( transaction ) => {
232- sendersAndReceivers . push ( transaction . sender )
233- sendersAndReceivers . push ( transaction . receiver )
270+ transactionCount ++
271+ accounts . push ( transaction . sender )
272+ accounts . push ( transaction . receiver )
234273 if ( transaction . transactions ) {
235274 extract ( transaction . transactions )
236275 }
@@ -241,12 +280,17 @@ function extractSendersAndReceivers(group: Group): string[] {
241280 extract ( group . transactions )
242281
243282 // Remove duplicates
244- sendersAndReceivers = Array . from ( new Set ( sendersAndReceivers ) )
283+ accounts = Array . from ( new Set ( accounts ) )
284+ // Sort
285+ accounts = accounts . sort ( ( a , b ) => ( a > b ? 1 : a < b ? - 1 : 0 ) )
245286
246- return sendersAndReceivers . sort ( ( a , b ) => ( a > b ? 1 : a < b ? - 1 : 0 ) )
287+ return {
288+ transactionCount : transactionCount ,
289+ accounts : accounts ,
290+ }
247291}
248292
249- function calcTransactionFoo ( transaction : Transaction , accounts : string [ ] ) : TransactionFooDrawing {
293+ function calcTransactionArrow ( transaction : Transaction , accounts : string [ ] ) : TransactionArrow {
250294 const fromAccount = accounts . findIndex ( ( a ) => transaction . sender === a )
251295 const toAccount = accounts . findIndex ( ( a ) => transaction . receiver === a )
252296 const direction = fromAccount < toAccount ? 'leftToRight' : fromAccount > toAccount ? 'rightToLeft' : 'toSelf'
@@ -257,3 +301,11 @@ function calcTransactionFoo(transaction: Transaction, accounts: string[]): Trans
257301 direction : direction ,
258302 }
259303}
304+
305+ const graphConfig = {
306+ rowHeight : 40 ,
307+ colWidth : 128 ,
308+ indentationWidth : 16 ,
309+ lineWidth : 2 ,
310+ circleDimension : 20 ,
311+ }
0 commit comments