@xyo-network/react-chain-blockchain 1.18.0 → 1.18.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. package/dist/browser/components/account/BalanceHistoryFlexbox.d.ts +1 -1
  2. package/dist/browser/components/account/BalanceHistoryFlexbox.d.ts.map +1 -1
  3. package/dist/browser/components/account/helpers/formatBalanceMagnitude.d.ts +1 -1
  4. package/dist/browser/components/account/helpers/formatBalanceMagnitude.d.ts.map +1 -1
  5. package/dist/browser/components/account/hooks/usePagedAccountBalanceHistory.d.ts +1 -1
  6. package/dist/browser/components/account/hooks/usePagedAccountBalanceHistory.d.ts.map +1 -1
  7. package/dist/browser/components/block/helpers/blockProducer.d.ts +6 -2
  8. package/dist/browser/components/block/helpers/blockProducer.d.ts.map +1 -1
  9. package/dist/browser/components/block/helpers/payloadCountsFromBlock.d.ts +1 -1
  10. package/dist/browser/components/block/helpers/payloadCountsFromBlock.d.ts.map +1 -1
  11. package/dist/browser/components/block/hooks/useBlockProducer.d.ts +6 -2
  12. package/dist/browser/components/block/hooks/useBlockProducer.d.ts.map +1 -1
  13. package/dist/browser/components/block/hooks/usePayloadCountsFromBlock.d.ts +1 -1
  14. package/dist/browser/components/block/hooks/usePayloadCountsFromBlock.d.ts.map +1 -1
  15. package/dist/browser/components/block/table/cell/lib/BlockTableCellProps.d.ts +1 -1
  16. package/dist/browser/components/block/table/cell/lib/BlockTableCellProps.d.ts.map +1 -1
  17. package/dist/browser/components/chain/hooks/useOnBlock.d.ts +21 -13
  18. package/dist/browser/components/chain/hooks/useOnBlock.d.ts.map +1 -1
  19. package/dist/browser/components/index.d.ts +1 -0
  20. package/dist/browser/components/index.d.ts.map +1 -1
  21. package/dist/browser/components/payload/builder/transfer/Form.d.ts.map +1 -1
  22. package/dist/browser/components/payload/builder/transfer/builder/SingleFlexbox.d.ts.map +1 -1
  23. package/dist/browser/components/payload/fields/XyoAddressTextField.d.ts.map +1 -1
  24. package/dist/browser/components/rate/SpanTypography.d.ts +8 -0
  25. package/dist/browser/components/rate/SpanTypography.d.ts.map +1 -0
  26. package/dist/browser/components/rate/SpeedTypography.d.ts +7 -0
  27. package/dist/browser/components/rate/SpeedTypography.d.ts.map +1 -0
  28. package/dist/browser/components/rate/TimeTypography.d.ts +7 -0
  29. package/dist/browser/components/rate/TimeTypography.d.ts.map +1 -0
  30. package/dist/browser/components/rate/flexbox/FlexBox.d.ts +7 -0
  31. package/dist/browser/components/rate/flexbox/FlexBox.d.ts.map +1 -0
  32. package/dist/browser/components/rate/flexbox/FlexBox.stories.d.ts +10 -0
  33. package/dist/browser/components/rate/flexbox/FlexBox.stories.d.ts.map +1 -0
  34. package/dist/browser/components/rate/flexbox/index.d.ts +2 -0
  35. package/dist/browser/components/rate/flexbox/index.d.ts.map +1 -0
  36. package/dist/browser/components/rate/gauge/Container.d.ts +15 -0
  37. package/dist/browser/components/rate/gauge/Container.d.ts.map +1 -0
  38. package/dist/browser/components/rate/gauge/Container.stories.d.ts +12 -0
  39. package/dist/browser/components/rate/gauge/Container.stories.d.ts.map +1 -0
  40. package/dist/browser/components/rate/gauge/Pointer.d.ts +6 -0
  41. package/dist/browser/components/rate/gauge/Pointer.d.ts.map +1 -0
  42. package/dist/browser/components/rate/gauge/Ticks.d.ts +38 -0
  43. package/dist/browser/components/rate/gauge/Ticks.d.ts.map +1 -0
  44. package/dist/browser/components/rate/gauge/WithLabel.d.ts +5 -0
  45. package/dist/browser/components/rate/gauge/WithLabel.d.ts.map +1 -0
  46. package/dist/browser/components/rate/gauge/WithLabel.stories.d.ts +11 -0
  47. package/dist/browser/components/rate/gauge/WithLabel.stories.d.ts.map +1 -0
  48. package/dist/browser/components/rate/gauge/helpers/blockRateConversions.d.ts +4 -0
  49. package/dist/browser/components/rate/gauge/helpers/blockRateConversions.d.ts.map +1 -0
  50. package/dist/browser/components/rate/gauge/helpers/index.d.ts +2 -0
  51. package/dist/browser/components/rate/gauge/helpers/index.d.ts.map +1 -0
  52. package/dist/browser/components/rate/gauge/index.d.ts +5 -0
  53. package/dist/browser/components/rate/gauge/index.d.ts.map +1 -0
  54. package/dist/browser/components/rate/index.d.ts +7 -0
  55. package/dist/browser/components/rate/index.d.ts.map +1 -0
  56. package/dist/browser/components/rate/support/MetricTypography.d.ts +11 -0
  57. package/dist/browser/components/rate/support/MetricTypography.d.ts.map +1 -0
  58. package/dist/browser/components/rate/support/index.d.ts +2 -0
  59. package/dist/browser/components/rate/support/index.d.ts.map +1 -0
  60. package/dist/browser/components/transactions/TransactionsQuickTipButton.d.ts.map +1 -1
  61. package/dist/browser/context/analyzer/state.d.ts +1 -1
  62. package/dist/browser/context/analyzer/state.d.ts.map +1 -1
  63. package/dist/browser/context/chain/Provider.d.ts.map +1 -1
  64. package/dist/browser/context/chain/State.d.ts +1 -2
  65. package/dist/browser/context/chain/State.d.ts.map +1 -1
  66. package/dist/browser/helpers/rate/index.d.ts +2 -0
  67. package/dist/browser/helpers/rate/index.d.ts.map +1 -0
  68. package/dist/browser/helpers/rate/rateUnitToLabel.d.ts +3 -0
  69. package/dist/browser/helpers/rate/rateUnitToLabel.d.ts.map +1 -0
  70. package/dist/browser/helpers/txsFromBlock.d.ts +4 -22
  71. package/dist/browser/helpers/txsFromBlock.d.ts.map +1 -1
  72. package/dist/browser/hooks/chain-iterator/useChainIteratorParams.d.ts +47 -143
  73. package/dist/browser/hooks/chain-iterator/useChainIteratorParams.d.ts.map +1 -1
  74. package/dist/browser/hooks/useTxsFromBlock.d.ts +2 -22
  75. package/dist/browser/hooks/useTxsFromBlock.d.ts.map +1 -1
  76. package/dist/browser/index.mjs +719 -326
  77. package/dist/browser/index.mjs.map +1 -1
  78. package/dist/browser/types/BlockComponentProps.d.ts +1 -1
  79. package/dist/browser/types/BlockComponentProps.d.ts.map +1 -1
  80. package/dist/browser/types/BlockComponents.d.ts +1 -1
  81. package/dist/browser/types/BlockComponents.d.ts.map +1 -1
  82. package/dist/browser/types/render/BlockChainRenderProps.d.ts +1 -2
  83. package/dist/browser/types/render/BlockChainRenderProps.d.ts.map +1 -1
  84. package/package.json +31 -31
  85. package/src/components/account/BalanceHistoryFlexbox.tsx +2 -2
  86. package/src/components/account/helpers/formatBalanceMagnitude.ts +1 -1
  87. package/src/components/account/hooks/usePagedAccountBalanceHistory.ts +9 -14
  88. package/src/components/block/helpers/blockProducer.ts +1 -1
  89. package/src/components/block/helpers/payloadCountsFromBlock.ts +1 -1
  90. package/src/components/block/hooks/useBlockProducer.ts +1 -1
  91. package/src/components/block/hooks/usePayloadCountsFromBlock.ts +1 -1
  92. package/src/components/block/table/cell/lib/BlockTableCellProps.ts +1 -1
  93. package/src/components/chain/hooks/useOnBlock.ts +3 -3
  94. package/src/components/index.ts +1 -0
  95. package/src/components/payload/builder/transfer/Form.tsx +5 -2
  96. package/src/components/payload/builder/transfer/builder/SingleFlexbox.tsx +12 -15
  97. package/src/components/payload/fields/XyoAddressTextField.tsx +10 -3
  98. package/src/components/rate/SpanTypography.tsx +20 -0
  99. package/src/components/rate/SpeedTypography.tsx +17 -0
  100. package/src/components/rate/TimeTypography.tsx +17 -0
  101. package/src/components/rate/flexbox/FlexBox.stories.tsx +44 -0
  102. package/src/components/rate/flexbox/FlexBox.tsx +31 -0
  103. package/src/components/rate/flexbox/index.ts +1 -0
  104. package/src/components/rate/gauge/Container.stories.tsx +77 -0
  105. package/src/components/rate/gauge/Container.tsx +75 -0
  106. package/src/components/rate/gauge/Pointer.tsx +67 -0
  107. package/src/components/rate/gauge/Ticks.tsx +122 -0
  108. package/src/components/rate/gauge/WithLabel.stories.tsx +70 -0
  109. package/src/components/rate/gauge/WithLabel.tsx +20 -0
  110. package/src/components/rate/gauge/helpers/blockRateConversions.ts +104 -0
  111. package/src/components/rate/gauge/helpers/index.ts +1 -0
  112. package/src/components/rate/gauge/index.ts +4 -0
  113. package/src/components/rate/index.ts +6 -0
  114. package/src/components/rate/support/MetricTypography.tsx +78 -0
  115. package/src/components/rate/support/index.ts +1 -0
  116. package/src/components/transactions/TransactionsQuickTipButton.tsx +14 -9
  117. package/src/context/analyzer/Provider.tsx +2 -2
  118. package/src/context/analyzer/state.ts +1 -1
  119. package/src/context/chain/Provider.tsx +28 -21
  120. package/src/context/chain/State.ts +1 -2
  121. package/src/helpers/rate/index.ts +1 -0
  122. package/src/helpers/rate/rateUnitToLabel.ts +27 -0
  123. package/src/helpers/txsFromBlock.ts +4 -2
  124. package/src/hooks/chain-iterator/useChainIteratorParams.ts +13 -4
  125. package/src/hooks/useTxsFromBlock.ts +1 -1
  126. package/src/types/BlockComponentProps.ts +1 -1
  127. package/src/types/BlockComponents.ts +1 -1
  128. package/src/types/render/BlockChainRenderProps.ts +1 -2
@@ -0,0 +1,75 @@
1
+ import type { GaugeContainerProps } from '@mui/x-charts'
2
+ import {
3
+ GaugeContainer, GaugeReferenceArc, GaugeValueArc,
4
+ } from '@mui/x-charts'
5
+ import type { BlockRate, TimeDurations } from '@xyo-network/xl1-sdk'
6
+ import type { PropsWithChildren } from 'react'
7
+
8
+ import { BlockRateConversions } from './helpers/index.ts'
9
+ import { GaugePointer } from './Pointer.tsx'
10
+ import { GaugeTicks } from './Ticks.tsx'
11
+
12
+ export type GaugeConfig = {
13
+ // whether to show ticks on the gauge
14
+ showTicks?: boolean
15
+ // the block rate to target on the gauge
16
+ targetBlockRate: number
17
+ // the unit of time for the target block rate (e.g., seconds, minutes)
18
+ targetBlockRateUnit: keyof TimeDurations
19
+ // the position of the pointer when the block rate matches the target rate
20
+ // should be approximately between 2/3 and 3/4 position of the gauge
21
+ targetPosition: number
22
+ }
23
+
24
+ export interface BlockRateSpeedGaugeProps extends GaugeContainerProps, PropsWithChildren {
25
+ blockRate?: BlockRate
26
+ gaugeConfig?: GaugeConfig
27
+ }
28
+
29
+ export const BlockRateSpeedGaugeContainer: React.FC<BlockRateSpeedGaugeProps> = ({
30
+ blockRate,
31
+ children,
32
+ gaugeConfig,
33
+ startAngle = -110,
34
+ endAngle = 110,
35
+ width = 200,
36
+ height = 150,
37
+ ...props
38
+ }) => {
39
+ const { rate, rateUnit } = blockRate || {}
40
+ const {
41
+ targetBlockRate = 5, targetBlockRateUnit = 'minutes', targetPosition = 75, showTicks = true,
42
+ } = gaugeConfig || {}
43
+
44
+ // Get the appropriate converter function based on the target block rate unit
45
+ const converter = BlockRateConversions[targetBlockRateUnit]
46
+
47
+ // Normalize rate to the gauge
48
+ const maxGaugeValue = endAngle // matches endAngle
49
+ const blocksPerMinute = rate === undefined ? 0 : converter(rate, rateUnit)
50
+
51
+ // Scale: 5 blocks/min should map to 75 out of 110 max
52
+ // This means: normalizedValue = (blocksPerMinute / targetRate) * targetPosition
53
+ // And we want it capped at maxGaugeValue
54
+ const normalizedValue = Math.min((blocksPerMinute / targetBlockRate) * targetPosition, maxGaugeValue)
55
+
56
+ return (
57
+ <GaugeContainer
58
+ width={width}
59
+ height={height}
60
+ startAngle={startAngle}
61
+ endAngle={endAngle}
62
+ value={normalizedValue}
63
+ valueMax={endAngle}
64
+ innerRadius="95%"
65
+ outerRadius="100%"
66
+ {...props}
67
+ >
68
+ {showTicks && <GaugeTicks />}
69
+ <GaugeReferenceArc />
70
+ <GaugeValueArc />
71
+ <GaugePointer startAngle={-2} />
72
+ {children}
73
+ </GaugeContainer>
74
+ )
75
+ }
@@ -0,0 +1,67 @@
1
+ import { useTheme } from '@mui/material'
2
+ import { useGaugeState } from '@mui/x-charts/Gauge'
3
+ import { isDefined } from '@xylabs/sdk-js'
4
+ import { useEffect, useState } from 'react'
5
+
6
+ export interface GaugePointerProps {
7
+ pointerColor?: string
8
+ startAngle?: number
9
+ }
10
+
11
+ export const GaugePointer: React.FC<GaugePointerProps> = ({ pointerColor, startAngle = 0 }) => {
12
+ const theme = useTheme()
13
+ const strokeColor = isDefined(pointerColor) ? pointerColor : theme.palette.error.dark
14
+ const {
15
+ valueAngle, outerRadius, cx, cy,
16
+ } = useGaugeState()
17
+
18
+ const [currentAngle, setCurrentAngle] = useState(startAngle)
19
+
20
+ useEffect(() => {
21
+ if (valueAngle === null) return
22
+
23
+ const duration = 500 // 0.5 second animation
24
+ const startTime = Date.now()
25
+ const animationStartAngle = currentAngle // Start from current position
26
+ const endAngle = valueAngle
27
+
28
+ const animate = () => {
29
+ const now = Date.now()
30
+ const elapsed = now - startTime
31
+ const progress = Math.min(elapsed / duration, 1)
32
+
33
+ // Ease-out cubic for smooth deceleration
34
+ const easeProgress = 1 - Math.pow(1 - progress, 3)
35
+
36
+ // take the current angle and add the difference to the end angle based on progress
37
+ const newAngle = animationStartAngle + (endAngle - animationStartAngle) * easeProgress
38
+ setCurrentAngle(newAngle)
39
+
40
+ if (progress < 1) {
41
+ globalThis.requestAnimationFrame(animate)
42
+ }
43
+ }
44
+
45
+ globalThis.requestAnimationFrame(animate)
46
+ }, [currentAngle, valueAngle])
47
+
48
+ if (valueAngle === null) {
49
+ // No value to display
50
+ return null
51
+ }
52
+
53
+ const target = {
54
+ x: cx + outerRadius * Math.sin(currentAngle),
55
+ y: cy - outerRadius * Math.cos(currentAngle),
56
+ }
57
+ return (
58
+ <g className="GaugePointer">
59
+ <circle cx={cx} cy={cy} r={5} fill={strokeColor} />
60
+ <path
61
+ d={`M ${cx} ${cy} L ${target.x} ${target.y}`}
62
+ stroke={strokeColor}
63
+ strokeWidth={3}
64
+ />
65
+ </g>
66
+ )
67
+ }
@@ -0,0 +1,122 @@
1
+ import { useTheme } from '@mui/material'
2
+ import { useGaugeState } from '@mui/x-charts/Gauge'
3
+ import { isDefined } from '@xylabs/sdk-js'
4
+ import type { JSX } from 'react'
5
+
6
+ export interface GaugeTicksProps {
7
+ /**
8
+ * Length of major ticks as a percentage of the radius
9
+ * @default 0.15
10
+ */
11
+ majorTickLength?: number
12
+ /**
13
+ * Width of major ticks in pixels
14
+ * @default 2
15
+ */
16
+ majorTickWidth?: number
17
+ /**
18
+ * Length of minor ticks as a percentage of the radius
19
+ * @default 0.08
20
+ */
21
+ minorTickLength?: number
22
+ /**
23
+ * Width of minor ticks in pixels
24
+ * @default 1
25
+ */
26
+ minorTickWidth?: number
27
+ /**
28
+ * Number of minor ticks between major ticks
29
+ * @default 4
30
+ */
31
+ minorTicksPerMajor?: number
32
+ /**
33
+ * Number of major ticks to display
34
+ * @default 11
35
+ */
36
+ numTicks?: number
37
+ /**
38
+ * Color of the ticks
39
+ */
40
+ tickColor?: string
41
+ }
42
+
43
+ export const GaugeTicks: React.FC<GaugeTicksProps> = ({
44
+ numTicks = 11,
45
+ minorTicksPerMajor = 4,
46
+ majorTickLength = 0.2,
47
+ minorTickLength = 0.15,
48
+ majorTickWidth = 2,
49
+ minorTickWidth = 1,
50
+ tickColor,
51
+ }) => {
52
+ const theme = useTheme()
53
+ const {
54
+ startAngle, endAngle, outerRadius, cx, cy,
55
+ } = useGaugeState()
56
+
57
+ const color = isDefined(tickColor) ? tickColor : theme.vars.palette.text.secondary
58
+
59
+ if (startAngle === null || endAngle === null) {
60
+ return null
61
+ }
62
+
63
+ const ticks: JSX.Element[] = []
64
+ const totalAngle = endAngle - startAngle
65
+
66
+ // Generate major ticks
67
+ for (let i = 0; i < numTicks; i++) {
68
+ const angle = startAngle + (i / (numTicks - 1)) * totalAngle
69
+ const tickLength = outerRadius * majorTickLength
70
+ const tickStart = outerRadius
71
+ const innerRadius = tickStart - tickLength
72
+
73
+ const x1 = cx + tickStart * Math.sin(angle)
74
+ const y1 = cy - tickStart * Math.cos(angle)
75
+ const x2 = cx + innerRadius * Math.sin(angle)
76
+ const y2 = cy - innerRadius * Math.cos(angle)
77
+
78
+ ticks.push(
79
+ <line
80
+ className={`GaugeTick-major-${i}`}
81
+ key={`major-${i}`}
82
+ x1={x1}
83
+ y1={y1}
84
+ x2={x2}
85
+ y2={y2}
86
+ stroke={color}
87
+ strokeWidth={majorTickWidth}
88
+ // strokeLinecap="round"
89
+ />,
90
+ )
91
+ // Generate minor ticks between major ticks (except after the last major tick)
92
+ if (i < numTicks - 1) {
93
+ const angleStep = totalAngle / (numTicks - 1) / (minorTicksPerMajor + 1)
94
+ for (let j = 1; j <= minorTicksPerMajor; j++) {
95
+ const minorAngle = angle + angleStep * j
96
+ const minorTickLen = outerRadius * minorTickLength
97
+ const minorTickStart = outerRadius
98
+ const minorInnerRadius = minorTickStart - minorTickLen
99
+
100
+ const mx1 = cx + minorTickStart * Math.sin(minorAngle)
101
+ const my1 = cy - minorTickStart * Math.cos(minorAngle)
102
+ const mx2 = cx + minorInnerRadius * Math.sin(minorAngle)
103
+ const my2 = cy - minorInnerRadius * Math.cos(minorAngle)
104
+
105
+ ticks.push(
106
+ <line
107
+ key={`minor-${i}-${j}`}
108
+ x1={mx1}
109
+ y1={my1}
110
+ x2={mx2}
111
+ y2={my2}
112
+ stroke={color}
113
+ strokeWidth={minorTickWidth}
114
+ // strokeLinecap="round"
115
+ />,
116
+ )
117
+ }
118
+ }
119
+ }
120
+
121
+ return <g className="GaugeTicks">{ticks}</g>
122
+ }
@@ -0,0 +1,70 @@
1
+ import type { Meta, StoryFn } from '@storybook/react-vite'
2
+ import type { BlockRate } from '@xyo-network/xl1-sdk'
3
+ import { asXL1BlockRange } from '@xyo-network/xl1-sdk'
4
+
5
+ import type { GaugeConfig } from './Container.tsx'
6
+ import { BlockRateSpeedGaugeWithLabel } from './WithLabel.tsx'
7
+
8
+ const blockRateSeconds: BlockRate = {
9
+ range: asXL1BlockRange([100, 200], true),
10
+ span: 100,
11
+ rate: 0.083_33,
12
+ rateUnit: 'seconds',
13
+ timeDifference: (100 * 12),
14
+ }
15
+
16
+ const blockRateMinutes: BlockRate = {
17
+ range: asXL1BlockRange([100, 200], true),
18
+ span: 100,
19
+ rate: 5,
20
+ rateUnit: 'minutes',
21
+ timeDifference: (100 * 12) / 60,
22
+ }
23
+
24
+ const blockRateHour: BlockRate = {
25
+ range: asXL1BlockRange([100, 200], true),
26
+ span: 100,
27
+ rate: 7 * 60,
28
+ rateUnit: 'hours',
29
+ timeDifference: (100 * 12) / 60,
30
+ }
31
+
32
+ const gaugeConfigHours: GaugeConfig = {
33
+ targetBlockRate: 5 * 60,
34
+ targetBlockRateUnit: 'hours',
35
+ targetPosition: 75,
36
+ }
37
+
38
+ const gaugeConfigMinutes: GaugeConfig = {
39
+ targetBlockRate: 5,
40
+ targetBlockRateUnit: 'minutes',
41
+ targetPosition: 75,
42
+ }
43
+
44
+ export default {
45
+ title: 'BlockRate/Gauge/Label',
46
+ component: BlockRateSpeedGaugeWithLabel,
47
+ } satisfies Meta<typeof BlockRateSpeedGaugeWithLabel>
48
+
49
+ const Template: StoryFn<typeof BlockRateSpeedGaugeWithLabel> = (args) => {
50
+ return <BlockRateSpeedGaugeWithLabel {...args} />
51
+ }
52
+
53
+ const Default = Template.bind({})
54
+ Default.args = {}
55
+
56
+ const WithBlockRateSeconds = Template.bind({})
57
+ WithBlockRateSeconds.args = { blockRate: blockRateSeconds }
58
+
59
+ const WithBlockRateMinutes = Template.bind({})
60
+ WithBlockRateMinutes.args = { blockRate: blockRateMinutes, gaugeConfig: gaugeConfigMinutes }
61
+
62
+ const WithBlockRateHours = Template.bind({})
63
+ WithBlockRateHours.args = { blockRate: blockRateHour, gaugeConfig: gaugeConfigHours }
64
+
65
+ export {
66
+ Default,
67
+ WithBlockRateHours,
68
+ WithBlockRateMinutes,
69
+ WithBlockRateSeconds,
70
+ }
@@ -0,0 +1,20 @@
1
+ import { FlexCol } from '@xylabs/react-flexbox'
2
+ import { isDefined } from '@xylabs/sdk-js'
3
+
4
+ import { rateUnitToLabel } from '../../../helpers/rate/index.ts'
5
+ import { BlockRateSpeedTypography } from '../SpeedTypography.tsx'
6
+ import type { BlockRateSpeedGaugeProps } from './Container.tsx'
7
+ import { BlockRateSpeedGaugeContainer } from './Container.tsx'
8
+
9
+ export interface BlockRateSpeedGaugeWithLabelProps extends BlockRateSpeedGaugeProps {}
10
+
11
+ export const BlockRateSpeedGaugeWithLabel: React.FC<BlockRateSpeedGaugeProps> = ({ blockRate, ...props }) => {
12
+ const rateUnitLabel = isDefined(blockRate?.rateUnit) ? rateUnitToLabel(blockRate.rateUnit) : ''
13
+
14
+ return (
15
+ <FlexCol>
16
+ <BlockRateSpeedGaugeContainer blockRate={blockRate} {...props} />
17
+ <BlockRateSpeedTypography rate={blockRate?.rate} rateUnitLabel={rateUnitLabel} />
18
+ </FlexCol>
19
+ )
20
+ }
@@ -0,0 +1,104 @@
1
+ import { isDefined, isUndefined } from '@xylabs/sdk-js'
2
+ import type { TimeDurations } from '@xyo-network/xl1-sdk'
3
+
4
+ export type BlockRateConverter = (rateValue: number, unit?: keyof TimeDurations) => number
5
+
6
+ const toBlocksPerMillisecond: BlockRateConverter = (rateValue: number, unit?: keyof TimeDurations): number => {
7
+ if (isUndefined(unit)) return rateValue
8
+
9
+ const conversions: Record<keyof TimeDurations, number> = {
10
+ millis: 1, // already in milliseconds
11
+ seconds: 1000, // 1 second = 1000 milliseconds
12
+ minutes: 60_000, // 1 minute = 60000 milliseconds
13
+ hours: 3_600_000, // 1 hour = 3600000 milliseconds
14
+ days: 86_400_000, // 1 day = 86400000 milliseconds
15
+ weeks: 604_800_000, // 1 week = 604800000 milliseconds
16
+ }
17
+
18
+ return rateValue * (isDefined(conversions[unit]) ? conversions[unit] : 1)
19
+ }
20
+
21
+ const toBlocksPerSecond: BlockRateConverter = (rateValue: number, unit?: keyof TimeDurations): number => {
22
+ if (isUndefined(unit)) return rateValue
23
+
24
+ const conversions: Record<keyof TimeDurations, number> = {
25
+ millis: 0.001, // 1 millisecond = 0.001 seconds
26
+ seconds: 1, // already in seconds
27
+ minutes: 60, // 1 minute = 60 seconds
28
+ hours: 3600, // 1 hour = 3600 seconds
29
+ days: 86_400, // 1 day = 86400 seconds
30
+ weeks: 604_800, // 1 week = 604800 seconds
31
+ }
32
+
33
+ return rateValue * (isDefined(conversions[unit]) ? conversions[unit] : 1)
34
+ }
35
+
36
+ // Convert rate to blocks per minute based on the unit
37
+ const toBlocksPerMinute: BlockRateConverter = (rateValue: number, unit?: keyof TimeDurations): number => {
38
+ if (isUndefined(unit)) return rateValue
39
+
40
+ const conversions: Record<keyof TimeDurations, number> = {
41
+ millis: 60_000, // 1 minute = 60000 milliseconds
42
+ seconds: 60, // 1 minute = 60 seconds
43
+ minutes: 1, // already in minutes
44
+ hours: 1 / 60, // 1 hour = 60 minutes
45
+ days: 1 / 1440, // 1 day = 1440 minutes
46
+ weeks: 1 / 10_080, // 1 week = 10080 minutes
47
+ }
48
+
49
+ return rateValue * (isDefined(conversions[unit]) ? conversions[unit] : 1)
50
+ }
51
+
52
+ const toBlocksPerHour: BlockRateConverter = (rateValue: number, unit?: keyof TimeDurations): number => {
53
+ if (isUndefined(unit)) return rateValue
54
+
55
+ const conversions: Record<keyof TimeDurations, number> = {
56
+ millis: 3_600_000, // 1 hour = 3600000 milliseconds
57
+ seconds: 3600, // 1 hour = 3600 seconds
58
+ minutes: 60, // 1 hour = 60 minutes
59
+ hours: 1, // already in hours
60
+ days: 1 / 24, // 1 day = 24 hours
61
+ weeks: 1 / 168, // 1 week = 168 hours
62
+ }
63
+
64
+ return rateValue * (isDefined(conversions[unit]) ? conversions[unit] : 1)
65
+ }
66
+
67
+ const toBlocksPerDay: BlockRateConverter = (rateValue: number, unit?: keyof TimeDurations): number => {
68
+ if (isUndefined(unit)) return rateValue
69
+
70
+ const conversions: Record<keyof TimeDurations, number> = {
71
+ millis: 86_400_000, // 1 day = 86400000 milliseconds
72
+ seconds: 86_400, // 1 day = 86400 seconds
73
+ minutes: 1440, // 1 day = 1440 minutes
74
+ hours: 24, // 1 day = 24 hours
75
+ days: 1, // already in days
76
+ weeks: 1 / 7, // 1 week = 7 days
77
+ }
78
+
79
+ return rateValue * (isDefined(conversions[unit]) ? conversions[unit] : 1)
80
+ }
81
+
82
+ const toBlocksPerWeek: BlockRateConverter = (rateValue: number, unit?: keyof TimeDurations): number => {
83
+ if (isUndefined(unit)) return rateValue
84
+
85
+ const conversions: Record<keyof TimeDurations, number> = {
86
+ millis: 604_800_000, // 1 week = 604800000 milliseconds
87
+ seconds: 604_800, // 1 week = 604800 seconds
88
+ minutes: 10_080, // 1 week = 10080 minutes
89
+ hours: 168, // 1 week = 168 hours
90
+ days: 7, // 1 week = 7 days
91
+ weeks: 1, // already in weeks
92
+ }
93
+
94
+ return rateValue * (isDefined(conversions[unit]) ? conversions[unit] : 1)
95
+ }
96
+
97
+ export const BlockRateConversions: Record<keyof TimeDurations, BlockRateConverter> = {
98
+ millis: toBlocksPerMillisecond,
99
+ seconds: toBlocksPerSecond,
100
+ minutes: toBlocksPerMinute,
101
+ hours: toBlocksPerHour,
102
+ days: toBlocksPerDay,
103
+ weeks: toBlocksPerWeek,
104
+ }
@@ -0,0 +1 @@
1
+ export * from './blockRateConversions.ts'
@@ -0,0 +1,4 @@
1
+ export * from './Container.tsx'
2
+ export * from './Pointer.tsx'
3
+ export * from './Ticks.tsx'
4
+ export * from './WithLabel.tsx'
@@ -0,0 +1,6 @@
1
+ export * from './flexbox/index.ts'
2
+ export * from './gauge/index.ts'
3
+ export * from './SpanTypography.tsx'
4
+ export * from './SpeedTypography.tsx'
5
+ export * from './support/index.ts'
6
+ export * from './TimeTypography.tsx'
@@ -0,0 +1,78 @@
1
+ import type { TypographyProps } from '@mui/material'
2
+ import { Icon, Typography } from '@mui/material'
3
+ import { isUndefined } from '@xylabs/sdk-js'
4
+ import type { ReactNode } from 'react'
5
+ import { useEffect, useState } from 'react'
6
+
7
+ export interface MetricTypographyProps extends TypographyProps {
8
+ animationDurationMs?: number
9
+ disableAnimation?: boolean
10
+ icon?: ReactNode
11
+ label?: ReactNode
12
+ metric?: number
13
+ }
14
+
15
+ export const MetricTypography: React.FC<MetricTypographyProps> = ({
16
+ animationDurationMs = 1000, disableAnimation, icon, label, metric, sx, ...props
17
+ }) => {
18
+ const [displayValue, setDisplayValue] = useState(0)
19
+
20
+ if (isUndefined(metric) && displayValue !== 0) {
21
+ setDisplayValue(0)
22
+ }
23
+
24
+ useEffect(() => {
25
+ if (isUndefined(metric)) return
26
+ const duration = animationDurationMs
27
+ const startTime = Date.now()
28
+ const startValue = 0
29
+ const endValue = metric
30
+
31
+ const animate = () => {
32
+ const now = Date.now()
33
+ const elapsed = now - startTime
34
+ const progress = Math.min(elapsed / duration, 1)
35
+
36
+ // Easing function for smooth animation (ease-out
37
+ const easeProgress = 1 - Math.pow(1 - progress, 3)
38
+
39
+ const currentValue = startValue + (endValue - startValue) * easeProgress
40
+ setDisplayValue(currentValue)
41
+
42
+ if (progress < 1) {
43
+ requestAnimationFrame(animate)
44
+ }
45
+ }
46
+
47
+ globalThis.requestAnimationFrame(animate)
48
+ }, [animationDurationMs, metric])
49
+
50
+ // Check if metric is a whole number
51
+ const isWholeNumber = metric !== undefined && Number.isInteger(metric)
52
+
53
+ // Format based on whether the target is a whole number
54
+ const formattedValue = disableAnimation
55
+ ? metric
56
+ : displayValue.toLocaleString(navigator.language, {
57
+ minimumFractionDigits: 0,
58
+ maximumFractionDigits: isWholeNumber ? 0 : 2,
59
+ })
60
+
61
+ return (
62
+ <Typography
63
+ variant="h4"
64
+ mb={1}
65
+ sx={{
66
+ display: 'flex', alignItems: 'end', gap: 0.5, ...sx,
67
+ }}
68
+ {...props}
69
+ >
70
+ <Icon fontSize="large">{icon}</Icon>
71
+ {formattedValue}
72
+ {' '}
73
+ <Typography component="span" variant="caption" lineHeight={1.86}>
74
+ {label}
75
+ </Typography>
76
+ </Typography>
77
+ )
78
+ }
@@ -0,0 +1 @@
1
+ export * from './MetricTypography.tsx'
@@ -8,11 +8,9 @@ import {
8
8
  } from '@xylabs/react-flexbox'
9
9
  import type { QuickTipButtonProps } from '@xylabs/react-quick-tip-button'
10
10
  import { QuickTipButton } from '@xylabs/react-quick-tip-button'
11
- import type { Hash } from '@xylabs/sdk-js'
11
+ import { type Hash, isDefined } from '@xylabs/sdk-js'
12
12
  import type { HydratedBlockWithHashMeta } from '@xyo-network/xl1-protocol'
13
- import React, {
14
- Fragment, useMemo, useState,
15
- } from 'react'
13
+ import React, { Fragment, useState } from 'react'
16
14
 
17
15
  import { useTxsFromBlock } from '../../hooks/index.ts'
18
16
 
@@ -24,8 +22,16 @@ export const TransactionsQuickTipButton: React.FC<TransactionsQuickTipButtonQuic
24
22
  const [transactions, transactionsError] = useTxsFromBlock(block)
25
23
 
26
24
  const [copied, setCopied] = useState(false)
27
- const [showError, setShowError] = useState(false)
28
- useMemo(() => transactionsError ? setShowError(true) : setShowError(false), [transactionsError])
25
+
26
+ const [closeDialog, setCloseDialog] = useState(false)
27
+ const open = isDefined(transactionsError)
28
+
29
+ // if the error changes, reopen the dialog
30
+ const [previousTransactionError, setPreviousTransactionError] = useState<Error | undefined>(transactionsError)
31
+ if (isDefined(transactionsError) && transactionsError !== previousTransactionError) {
32
+ setPreviousTransactionError(transactionsError)
33
+ setCloseDialog(false)
34
+ }
29
35
 
30
36
  const onCopy = async (transactionHash: Hash) => {
31
37
  await navigator.clipboard.writeText(transactionHash)
@@ -49,8 +55,7 @@ export const TransactionsQuickTipButton: React.FC<TransactionsQuickTipButtonQuic
49
55
  ? null
50
56
  : (
51
57
  <FlexGrowCol gap={1} alignItems="stretch">
52
- {/* eslint-disable-next-line @typescript-eslint/no-unused-vars */}
53
- {transactions?.map(([transaction, hash]) => (
58
+ {transactions?.map(([_transaction, hash]) => (
54
59
  <Fragment key={hash}>
55
60
  <FlexGrowRow gap={2} justifyContent="start">
56
61
  <ContentCopyIcon sx={{ cursor: 'pointer' }} onClick={() => void onCopy(hash)} />
@@ -73,7 +78,7 @@ export const TransactionsQuickTipButton: React.FC<TransactionsQuickTipButtonQuic
73
78
  </FlexGrowCol>
74
79
  )}
75
80
  </QuickTipButton>
76
- <Snackbar open={showError} onClose={() => setShowError(false)}>
81
+ <Snackbar open={open && closeDialog === false} onClose={() => setCloseDialog(true)}>
77
82
  <ErrorRender error={transactionsError} scope="TransactionsQuickTipButton" />
78
83
  </Snackbar>
79
84
  </>
@@ -14,9 +14,9 @@ export interface ChainAnalyzersProviderProps extends PropsWithChildren {
14
14
 
15
15
  export const ChainAnalyzersProvider: React.FC<ChainAnalyzersProviderProps> = ({ analyzers: analyzersProp, children }) => {
16
16
  const [analyzersState, setAnalyzersState] = useState<ChainAnalyzers>(analyzersProp ?? {} as ChainAnalyzers)
17
- const [statsUpdated, setStatsUpdated] = useState(Date.now())
17
+ const [statsUpdated, setStatsUpdated] = useState(1)
18
18
 
19
- const analyzers = useMemo(() => ({ ...analyzersProp, ...analyzersState }), [analyzersProp])
19
+ const analyzers = useMemo(() => ({ ...analyzersProp, ...analyzersState }), [analyzersProp, analyzersState])
20
20
 
21
21
  const addAnalyzers: ChainAnalyzersState['addAnalyzers'] = useCallback((analyzers: ChainAnalyzersState['analyzers']) => {
22
22
  if (isUndefined(analyzers)) return
@@ -1,5 +1,5 @@
1
1
  import type { ContextExState } from '@xylabs/react-shared'
2
- import type { ChainAnalyzer, HydratedBlock } from '@xyo-network/xl1-protocol'
2
+ import type { ChainAnalyzer, HydratedBlock } from '@xyo-network/xl1-sdk'
3
3
 
4
4
  export type ChainAnalyzerIds = 'producers'
5
5