1
1
import { useState } from 'react'
2
2
3
3
import Alert from '@mui/material/Alert'
4
- import Box from '@mui/material/Box '
4
+ import Stack from '@mui/material/Stack '
5
5
import Skeleton from '@mui/material/Skeleton'
6
6
import Snackbar from '@mui/material/Snackbar'
7
7
import Tooltip from '@mui/material/Tooltip'
8
8
import Typography from '@mui/material/Typography'
9
9
import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined'
10
10
11
11
import { SizesAndSpaces } from '@ui-kit/themes/design/1_sizes_spaces'
12
- import { TypographyVariantKey , TYPOGRAPHY_VARIANTS } from '@ui-kit/themes/typography'
12
+ import { TypographyVariantKey } from '@ui-kit/themes/typography'
13
13
import { abbreviateNumber , scaleSuffix } from '@ui-kit/utils'
14
14
import { Duration } from '../../themes/design/0_primitives'
15
15
import AlertTitle from '@mui/material/AlertTitle'
16
16
17
- const { Spacing } = SizesAndSpaces
17
+ const { Spacing, IconSize } = SizesAndSpaces
18
18
19
19
// Correspond to flexbox align items values.
20
20
export const ALIGNMENTS = [ 'start' , 'center' , 'end' ] as const
@@ -27,6 +27,13 @@ const MetricSize = {
27
27
extraLarge : 'highlightXxl' ,
28
28
} as const satisfies Record < string , TypographyVariantKey >
29
29
30
+ const MetricUnitSize = {
31
+ small : 'highlightXs' ,
32
+ medium : 'highlightS' ,
33
+ large : 'highlightM' ,
34
+ extraLarge : 'highlightL' ,
35
+ } as const satisfies Record < string , TypographyVariantKey >
36
+
30
37
export const SIZES = Object . keys ( MetricSize ) as ( keyof typeof MetricSize ) [ ]
31
38
32
39
type UnitOptions = {
@@ -79,27 +86,79 @@ const formatChange = (value: number) => {
79
86
} )
80
87
}
81
88
82
- type Props = {
83
- /** Label that goes above the value */
84
- label : string
85
- /** Optional tooltip content shown next to the label */
86
- tooltip ?: string
87
- /** The text to display when the value is copied to the clipboard */
88
- copyText ?: string
89
+ type MetricValueProps = Required < Pick < Props , 'value' | 'formatter' | 'abbreviate' > > & {
90
+ change ?: number
91
+ unit : UnitOptions | undefined
92
+ fontVariant : TypographyVariantKey
93
+ fontVariantUnit : TypographyVariantKey
94
+ copyValue : ( ) => void
95
+ }
89
96
97
+ const MetricValue = ( {
98
+ value,
99
+ formatter,
100
+ change,
101
+ abbreviate,
102
+ unit,
103
+ fontVariant,
104
+ fontVariantUnit,
105
+ copyValue,
106
+ } : MetricValueProps ) => (
107
+ < Stack direction = "row" gap = { Spacing . xxs } alignItems = "baseline" >
108
+ < Tooltip arrow placement = "bottom" title = { value . toLocaleString ( ) } onClick = { copyValue } sx = { { cursor : 'pointer' } } >
109
+ < Stack direction = "row" alignItems = "baseline" >
110
+ { unit ?. position === 'prefix' && (
111
+ < Typography variant = { fontVariantUnit } color = "textSecondary" >
112
+ { unit . symbol }
113
+ </ Typography >
114
+ ) }
115
+
116
+ < Typography variant = { fontVariant } color = "textPrimary" >
117
+ { formatter ( abbreviate ? abbreviateNumber ( value ) : value ) }
118
+ </ Typography >
119
+
120
+ { abbreviate && (
121
+ < Typography variant = { fontVariant } color = "textPrimary" textTransform = "capitalize" >
122
+ { scaleSuffix ( value ) }
123
+ </ Typography >
124
+ ) }
125
+
126
+ { unit ?. position === 'suffix' && (
127
+ < Typography variant = { fontVariantUnit } color = "textSecondary" >
128
+ { unit . symbol }
129
+ </ Typography >
130
+ ) }
131
+ </ Stack >
132
+ </ Tooltip >
133
+
134
+ { ( change || change === 0 ) && (
135
+ < Typography variant = "highlightM" color = { change > 0 ? 'success' : change < 0 ? 'error' : 'textHighlight' } >
136
+ { formatChange ( change ) } %
137
+ </ Typography >
138
+ ) }
139
+ </ Stack >
140
+ )
141
+
142
+ type Props = {
90
143
/** The actual metric value to display */
91
144
value : number
92
- /** Optional formatter for metric value */
93
- formatter ?: ( value : number ) => string
94
- /** If the value should be abbreviated to 1.23k or 3.45m */
95
- abbreviate ?: boolean
145
+ /** A unit can be a currency symbol or percentage, prefix or suffix */
146
+ unit ?: Unit | undefined
96
147
/** The number of decimals the value should contain */
97
148
decimals ?: number
98
- /** A unit can be a currency symbol or percentage, prefix or suffix */
99
- unit ?: Unit
100
-
149
+ /** If the value should be abbreviated to 1.23k or 3.45m */
150
+ abbreviate ?: boolean
101
151
/** Optional value that denotes a change in metric value since 'last' time */
102
152
change ?: number
153
+ /** Optional formatter for metric value */
154
+ formatter ?: ( value : number ) => string
155
+
156
+ /** Label that goes above the value */
157
+ label : string
158
+ /** Optional tooltip content shown next to the label */
159
+ tooltip ?: string
160
+ /** The text to display when the value is copied to the clipboard */
161
+ copyText ?: string
103
162
104
163
/** Notional values give extra context to the metric, like underlying value */
105
164
notional ?: number
@@ -114,17 +173,16 @@ type Props = {
114
173
}
115
174
116
175
export const Metric = ( {
117
- label,
118
- tooltip,
119
- copyText,
120
-
121
176
value,
122
- formatter = ( value : number ) => formatValue ( value , decimals ) ,
123
- abbreviate,
124
177
unit,
178
+ abbreviate,
179
+ change,
125
180
decimals = 1 ,
181
+ formatter = ( value : number ) => formatValue ( value , decimals ) ,
126
182
127
- change,
183
+ label,
184
+ tooltip,
185
+ copyText,
128
186
129
187
notional,
130
188
notionalFormatter = ( value : number ) => formatNotionalValue ( value , notionalDecimals ) ,
@@ -149,74 +207,37 @@ export const Metric = ({
149
207
setOpenCopyAlert ( true )
150
208
}
151
209
210
+ const metricValueProps = {
211
+ value,
212
+ unit,
213
+ abbreviate,
214
+ change,
215
+ formatter,
216
+ fontVariant : MetricSize [ size ] ,
217
+ fontVariantUnit : MetricUnitSize [ size ] ,
218
+ copyValue,
219
+ }
220
+
152
221
return (
153
- < Box display = "flex" flexDirection = "column" alignItems = { alignment } gap = { Spacing . xs } >
222
+ < Stack alignItems = { alignment } >
154
223
< Typography variant = "bodyXsRegular" color = "textTertiary" >
155
224
{ label }
156
225
{ tooltip && (
157
226
< Tooltip arrow placement = "top" title = { tooltip } >
158
227
< span >
159
228
{ ' ' }
160
- < InfoOutlinedIcon sx = { { fontSize : '1.25em' } } />
229
+ < InfoOutlinedIcon sx = { { width : IconSize . xs , height : IconSize . xs } } />
161
230
</ span >
162
231
</ Tooltip >
163
232
) }
164
233
</ Typography >
165
234
166
235
{ loading ? (
167
- < Skeleton
168
- variant = "text"
169
- width = "100%"
170
- sx = { {
171
- fontSize : TYPOGRAPHY_VARIANTS [ MetricSize [ size ] ] ,
172
- } }
173
- />
236
+ < Skeleton variant = "text" >
237
+ < MetricValue { ...metricValueProps } />
238
+ </ Skeleton >
174
239
) : (
175
- < Box display = "flex" gap = { Spacing . xs } alignItems = "baseline" >
176
- < Tooltip
177
- arrow
178
- placement = "bottom"
179
- title = { value . toLocaleString ( ) }
180
- onClick = { copyValue }
181
- sx = { {
182
- cursor : 'pointer' ,
183
- } }
184
- >
185
- < Box display = "flex" gap = { Spacing . xxs } alignItems = "baseline" >
186
- { unit ?. position === 'prefix' && (
187
- < Typography variant = { MetricSize [ size ] } color = "textSecondary" >
188
- { unit . symbol }
189
- </ Typography >
190
- ) }
191
-
192
- < Typography variant = { MetricSize [ size ] } color = "textPrimary" >
193
- { formatter ( abbreviate ? abbreviateNumber ( value ) : value ) }
194
- </ Typography >
195
-
196
- { abbreviate && (
197
- < Typography variant = { MetricSize [ size ] } color = "textPrimary" textTransform = "capitalize" >
198
- { scaleSuffix ( value ) }
199
- </ Typography >
200
- ) }
201
-
202
- { unit ?. position === 'suffix' && (
203
- < Typography variant = { MetricSize [ size ] } color = "textSecondary" >
204
- { unit . symbol }
205
- </ Typography >
206
- ) }
207
- </ Box >
208
- </ Tooltip >
209
-
210
- { ( change || change === 0 ) && (
211
- < Typography
212
- variant = "highlightM"
213
- color = { change > 0 ? 'success' : change < 0 ? 'error' : 'textHighlight' }
214
- sx = { { marginInlineStart : Spacing . xs } }
215
- >
216
- { formatChange ( change ) } %
217
- </ Typography >
218
- ) }
219
- </ Box >
240
+ < MetricValue { ...metricValueProps } />
220
241
) }
221
242
222
243
{ notional !== undefined && (
@@ -234,6 +255,6 @@ export const Metric = ({
234
255
{ value }
235
256
</ Alert >
236
257
</ Snackbar >
237
- </ Box >
258
+ </ Stack >
238
259
)
239
260
}
0 commit comments