@@ -5,12 +5,118 @@ import { cn } from '@/features/common/utils'
55import { isDefined } from '@/utils/is-defined'
66import { useMemo } from 'react'
77
8- type TransactionArrow = {
8+ type Arrow = {
99 from : number
1010 to : number
1111 direction : 'leftToRight' | 'rightToLeft' | 'toSelf'
1212}
1313
14+ function VerticalBars ( { verticalBars } : { verticalBars : ( number | undefined ) [ ] } ) {
15+ // The side vertical bars when there are nested items
16+ return ( verticalBars ?? [ ] )
17+ . filter ( isDefined )
18+ . map ( ( b , i ) => (
19+ < div
20+ key = { i }
21+ className = { cn ( 'h-full border-primary absolute' ) }
22+ style = { { marginLeft : b * graphConfig . indentationWidth , borderLeftWidth : `${ graphConfig . lineWidth } px` } }
23+ > </ div >
24+ ) )
25+ }
26+
27+ function ConnectionToParent ( ) {
28+ // The connection between this transaction and the parent
29+ return (
30+ < div
31+ className = { cn ( `border-primary rounded-bl-lg` , `h-1/2` , `absolute top-0 left-0` ) }
32+ style = { {
33+ borderLeftWidth : `${ graphConfig . lineWidth } px` ,
34+ borderBottomWidth : `${ graphConfig . lineWidth } px` ,
35+ width : `${ graphConfig . indentationWidth + 8 } px` ,
36+ } }
37+ > </ div >
38+ )
39+ }
40+
41+ function TransactionName ( { hasParent, name } : { hasParent : boolean ; name : string } ) {
42+ return (
43+ < div
44+ className = { cn ( 'inline' ) }
45+ style = { {
46+ marginLeft : hasParent ? `${ graphConfig . indentationWidth + 8 } px` : `16px` ,
47+ } }
48+ >
49+ { name }
50+ </ div >
51+ )
52+ }
53+
54+ function ConnectionToSibbling ( ) {
55+ // The connection between this transaction and the next sibbling
56+ return (
57+ < div
58+ className = { cn ( 'border-primary' , 'absolute left-0' ) }
59+ style = { {
60+ width : `${ graphConfig . indentationWidth + 8 } px` ,
61+ borderLeftWidth : `${ graphConfig . lineWidth } px` ,
62+ height : `calc(50% + ${ graphConfig . lineWidth } px)` ,
63+ top : `calc(50% - ${ graphConfig . lineWidth } px)` ,
64+ } }
65+ > </ div >
66+ )
67+ }
68+
69+ function ConnectionToChildren ( { indentLevel } : { indentLevel : number | undefined } ) {
70+ // The connection between this transaction and the children
71+ return (
72+ < div
73+ className = { cn ( 'w-2' , 'border-primary rounded-tl-lg' , 'absolute left-0' ) }
74+ style = { {
75+ marginLeft : indentLevel != null ? `${ graphConfig . indentationWidth } px` : undefined ,
76+ borderLeftWidth : `${ graphConfig . lineWidth } px` ,
77+ borderTopWidth : `${ graphConfig . lineWidth } px` ,
78+ height : `calc(50% + ${ graphConfig . lineWidth } px)` ,
79+ top : `calc(50% - ${ graphConfig . lineWidth } px)` ,
80+ } }
81+ > </ div >
82+ )
83+ }
84+
85+ function DisplayArrow ( { arrow : transactionArrow } : { arrow : Arrow } ) {
86+ return (
87+ < div
88+ className = { cn ( 'flex items-center justify-center' ) }
89+ style = { {
90+ // 2 and 3 are the number to offset the name column
91+ gridColumnStart : transactionArrow . from + 2 ,
92+ gridColumnEnd : transactionArrow . to + 3 ,
93+ } }
94+ >
95+ < SvgCircle width = { graphConfig . circleDimension } height = { graphConfig . circleDimension } > </ SvgCircle >
96+ < div
97+ style = { {
98+ width : `calc(${ ( 100 - 100 / ( transactionArrow . to - transactionArrow . from + 1 ) ) . toFixed ( 2 ) } % - ${ graphConfig . circleDimension } px)` ,
99+ height : `${ graphConfig . circleDimension } px` ,
100+ } }
101+ className = "relative text-primary"
102+ >
103+ { transactionArrow . direction === 'rightToLeft' && < SvgPointerLeft className = { cn ( 'absolute top-0 left-0' ) } /> }
104+ < div className = { cn ( 'border-primary h-1/2' ) } style = { { borderBottomWidth : graphConfig . lineWidth } } > </ div >
105+ { transactionArrow . direction === 'leftToRight' && < SvgPointerRight className = { cn ( 'absolute top-0 right-0' ) } /> }
106+ </ div >
107+ < SvgCircle width = { graphConfig . circleDimension } height = { graphConfig . circleDimension } > </ SvgCircle >
108+ </ div >
109+ )
110+ }
111+
112+ function DisplaySelfTransaction ( ) {
113+ return (
114+ < div className = { cn ( 'flex items-center justify-center' ) } >
115+ < SvgCircle width = { graphConfig . circleDimension } height = { graphConfig . circleDimension } > </ SvgCircle >
116+ </ div >
117+ )
118+ }
119+
14120type TransactionRowProps = {
15121 transaction : Transaction
16122 hasParent ?: boolean
@@ -29,104 +135,26 @@ function TransactionRow({
29135 indentLevel,
30136 verticalBars,
31137} : TransactionRowProps ) {
32- const transactionArrow = useMemo ( ( ) => calcTransactionArrow ( transaction , accounts ) , [ accounts , transaction ] )
138+ const arrow = useMemo ( ( ) => calcArrow ( transaction , accounts ) , [ accounts , transaction ] )
33139
34140 return (
35141 < >
36142 < div className = { cn ( 'p-0 relative pr-8' ) } >
37- {
38- // The side vertical bars when there are nested items
39- ( verticalBars ?? [ ] ) . filter ( isDefined ) . map ( ( b , i ) => (
40- < div
41- key = { i }
42- className = { cn ( 'h-full border-primary absolute' ) }
43- style = { { marginLeft : b * graphConfig . indentationWidth , borderLeftWidth : `${ graphConfig . lineWidth } px` } }
44- > </ div >
45- ) )
46- }
143+ < VerticalBars verticalBars = { verticalBars } />
47144 < div
48145 className = { cn ( `relative h-full p-0 flex items-center` , 'px-0' ) }
49146 style = { { marginLeft : ( indentLevel ?? 0 ) * graphConfig . indentationWidth } }
50147 >
51- {
52- // The connection between this transaction and the parent
53- hasParent && (
54- < div
55- className = { cn ( `border-primary rounded-bl-lg` , `h-1/2` , `absolute top-0 left-0` ) }
56- style = { {
57- borderLeftWidth : `${ graphConfig . lineWidth } px` ,
58- borderBottomWidth : `${ graphConfig . lineWidth } px` ,
59- width : `${ graphConfig . indentationWidth + 8 } px` ,
60- } }
61- > </ div >
62- )
63- }
64- < div
65- className = { cn ( 'inline' ) }
66- style = { {
67- marginLeft : hasParent ? `${ graphConfig . indentationWidth + 8 } px` : `16px` ,
68- } }
69- >
70- { transaction . name }
71- </ div >
72- {
73- // The connection between this transaction and the next sibbling
74- hasParent && hasNextSibbling && (
75- < div
76- className = { cn ( 'border-primary' , 'absolute left-0' ) }
77- style = { {
78- width : `${ graphConfig . indentationWidth + 8 } px` ,
79- borderLeftWidth : `${ graphConfig . lineWidth } px` ,
80- height : `calc(50% + ${ graphConfig . lineWidth } px)` ,
81- top : `calc(50% - ${ graphConfig . lineWidth } px)` ,
82- } }
83- > </ div >
84- )
85- }
86- {
87- // The connection between this transaction and the children
88- hasChildren && (
89- < div
90- className = { cn ( 'w-2' , 'border-primary rounded-tl-lg' , 'absolute left-0' ) }
91- style = { {
92- marginLeft : indentLevel != null ? `${ graphConfig . indentationWidth } px` : undefined ,
93- borderLeftWidth : `${ graphConfig . lineWidth } px` ,
94- borderTopWidth : `${ graphConfig . lineWidth } px` ,
95- height : `calc(50% + ${ graphConfig . lineWidth } px)` ,
96- top : `calc(50% - ${ graphConfig . lineWidth } px)` ,
97- } }
98- > </ div >
99- )
100- }
148+ { hasParent && < ConnectionToParent /> }
149+ < TransactionName hasParent = { hasParent } name = { transaction . name } />
150+ { hasParent && hasNextSibbling && < ConnectionToSibbling /> }
151+ { hasChildren && < ConnectionToChildren indentLevel = { indentLevel } /> }
101152 </ div >
102153 </ div >
103154 { accounts . map ( ( _ , index ) => {
104- if ( index < transactionArrow . from || index > transactionArrow . to ) return < div key = { index } > </ div >
105- if ( index === transactionArrow . from )
106- return (
107- < div
108- className = { cn ( 'flex items-center justify-center' ) }
109- style = { {
110- // 2 and 3 are the number to offset the name column
111- gridColumnStart : transactionArrow . from + 2 ,
112- gridColumnEnd : transactionArrow . to + 3 ,
113- } }
114- >
115- < SvgCircle width = { graphConfig . circleDimension } height = { graphConfig . circleDimension } > </ SvgCircle >
116- < div
117- style = { {
118- width : `calc(${ ( 100 - 100 / ( transactionArrow . to - transactionArrow . from + 1 ) ) . toFixed ( 2 ) } % - ${ graphConfig . circleDimension } px)` ,
119- height : `${ graphConfig . circleDimension } px` ,
120- } }
121- className = "relative text-primary"
122- >
123- { transactionArrow . direction === 'rightToLeft' && < SvgPointerLeft className = { cn ( 'absolute top-0 left-0' ) } /> }
124- < div className = { cn ( 'border-primary h-1/2' ) } style = { { borderBottomWidth : graphConfig . lineWidth } } > </ div >
125- { transactionArrow . direction === 'leftToRight' && < SvgPointerRight className = { cn ( 'absolute top-0 right-0' ) } /> }
126- </ div >
127- < SvgCircle width = { graphConfig . circleDimension } height = { graphConfig . circleDimension } > </ SvgCircle >
128- </ div >
129- )
155+ if ( index < arrow . from || index > arrow . to ) return < div key = { index } > </ div >
156+ if ( index === arrow . from && index === arrow . to ) return < DisplaySelfTransaction />
157+ if ( index === arrow . from ) return < DisplayArrow arrow = { arrow } />
130158 else return null
131159 } ) }
132160
@@ -214,7 +242,7 @@ export function GroupPage() {
214242
215243 return (
216244 < div
217- className = { cn ( 'relative grid overflow-x-scroll max-w-full ' ) }
245+ className = { cn ( 'relative grid' ) }
218246 style = { {
219247 gridTemplateColumns : `minmax(${ graphConfig . colWidth } px, 1fr) repeat(${ accounts . length } , ${ graphConfig . colWidth } px)` ,
220248 gridTemplateRows : `repeat(${ transactionCount + 1 } , ${ graphConfig . rowHeight } px)` ,
@@ -305,7 +333,7 @@ function extractSendersAndReceivers(group: Group) {
305333 }
306334}
307335
308- function calcTransactionArrow ( transaction : Transaction , accounts : string [ ] ) : TransactionArrow {
336+ function calcArrow ( transaction : Transaction , accounts : string [ ] ) : Arrow {
309337 const fromAccount = accounts . findIndex ( ( a ) => transaction . sender === a )
310338 const toAccount = accounts . findIndex ( ( a ) => transaction . receiver === a )
311339 const direction = fromAccount < toAccount ? 'leftToRight' : fromAccount > toAccount ? 'rightToLeft' : 'toSelf'
0 commit comments