@stellar-expert/ui-framework 1.16.8 → 1.17.0

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 (78) hide show
  1. package/CLAUDE.md +35 -0
  2. package/README.md +1125 -3
  3. package/account/account-address.js +127 -127
  4. package/account/available-balance.js +22 -22
  5. package/account/identicon.js +90 -90
  6. package/account/signer-key.js +65 -64
  7. package/api/explorer-api-hooks.js +209 -202
  8. package/api/explorer-api-paginated-list-hooks.js +441 -440
  9. package/api/explorer-batch-info-loader.js +111 -85
  10. package/api/explorer-tx-api.js +28 -28
  11. package/api/ledger-stream.js +15 -0
  12. package/asset/amount.js +56 -56
  13. package/asset/asset-icon.js +41 -41
  14. package/asset/asset-issuer.js +21 -21
  15. package/asset/asset-link.js +107 -107
  16. package/asset/asset-list-hooks.js +59 -59
  17. package/asset/asset-meta-hooks.js +88 -88
  18. package/asset/asset-selector.js +72 -71
  19. package/claimable-balance/claimable-balance-claimants.js +23 -18
  20. package/contract/contract-api.js +15 -0
  21. package/contract/invocation-info-view.js +10 -2
  22. package/contract/sc-val.js +131 -107
  23. package/controls/button-group.js +25 -19
  24. package/controls/button.js +93 -78
  25. package/controls/code-block.js +42 -34
  26. package/controls/dropdown.js +318 -287
  27. package/controls/external-link.js +10 -4
  28. package/controls/info-tooltip.js +23 -16
  29. package/controls/slider.js +29 -19
  30. package/controls/tabs.js +94 -94
  31. package/controls/tooltip.js +244 -240
  32. package/controls/update-highlighter.js +32 -27
  33. package/date/date-selector.js +56 -54
  34. package/date/elapsed-time.js +28 -21
  35. package/dex/price-dynamic.js +53 -44
  36. package/directory/directory-hooks.js +109 -97
  37. package/effect/effect-description.js +346 -344
  38. package/errors/error-boundary.js +110 -97
  39. package/horizon/horizon-account-helpers.js +104 -104
  40. package/horizon/horizon-ledger-helpers.js +35 -35
  41. package/horizon/horizon-trades-helper.js +88 -88
  42. package/horizon/horizon-transaction-helpers.js +36 -36
  43. package/index.d.ts +1241 -0
  44. package/interaction/accordion.js +43 -35
  45. package/interaction/autofocus.js +13 -9
  46. package/interaction/block-select.js +64 -53
  47. package/interaction/copy-to-clipboard.js +25 -18
  48. package/interaction/inline-progress.js +20 -15
  49. package/interaction/qr-code.js +34 -34
  50. package/interaction/responsive.js +20 -20
  51. package/interaction/spoiler.js +52 -39
  52. package/interaction/theme-selector.js +13 -10
  53. package/ledger/ledger-entry-href-formatter.js +27 -26
  54. package/ledger/ledger-entry-link.js +93 -58
  55. package/ledger/ledger-info-parser.js +46 -18
  56. package/meta/page-meta-tags.js +243 -238
  57. package/module/dynamic-module.js +47 -47
  58. package/package.json +41 -40
  59. package/state/on-screen-hooks.js +22 -22
  60. package/state/page-visibility-helpers.js +29 -16
  61. package/state/page-visibility-hooks.js +13 -11
  62. package/state/screen-orientation-hooks.js +19 -15
  63. package/state/state-hooks.js +76 -76
  64. package/state/stellar-network-hooks.js +56 -44
  65. package/state/theme.js +29 -28
  66. package/stellar/key-type.js +92 -91
  67. package/stellar/ledger-generic-id.js +39 -39
  68. package/stellar/signature-hint-utils.js +65 -65
  69. package/toast/toast-notifications-block.js +59 -59
  70. package/tx/op-description-view.js +945 -942
  71. package/tx/op-icon.js +98 -98
  72. package/tx/parser/op-balance-changes.js +66 -66
  73. package/tx/parser/op-descriptor.js +51 -48
  74. package/tx/parser/tx-details-parser.js +81 -81
  75. package/tx/parser/tx-matcher.js +371 -371
  76. package/tx/parser/type-filter-matcher.js +126 -126
  77. package/tx/tx-list-hooks.js +32 -18
  78. package/tx/tx-operations-list.js +117 -116
@@ -1,241 +1,245 @@
1
- import React, {useState, useRef} from 'react'
2
- import cn from 'classnames'
3
- import './tooltip.scss'
4
-
5
- /**
6
- * Compute tooltip position
7
- * @param {EventTarget} target - Mouse hover target
8
- * @param {Element} node - Tooltip object
9
- * @param {'top'|'bottom'|'left'|'right'} desiredPlace - Desired tooltip place
10
- * @param {Object} offset
11
- * @return {{place: String, position: {top: Number, left: Number}}}
12
- */
13
- function calculateTooltipPosition(target, node, desiredPlace, offset) {
14
- //dimensions of node and target
15
- const {width: tipWidth, height: tipHeight} = getBoundingRect(node)
16
- const {width: targetWidth, height: targetHeight} = getBoundingRect(target)
17
- //mouse offset
18
- const {mouseX, mouseY} = getMouseOffset(target)
19
- const {offsetX, offsetY} = parseOffset(offset)
20
- const defaultOffset = getDefaultPosition(targetWidth, targetHeight, tipWidth, tipHeight)
21
-
22
- // Get the edge offset of the tooltip
23
- function getTipOffsetLeft(place) {
24
- return mouseX + defaultOffset[place].l + offsetX
25
- }
26
-
27
- function getTipOffsetRight(place) {
28
- return mouseX + defaultOffset[place].r + offsetX
29
- }
30
-
31
- function getTipOffsetTop(place) {
32
- return mouseY + defaultOffset[place].t + offsetY
33
- }
34
-
35
- function getTipOffsetBottom(place) {
36
- return mouseY + defaultOffset[place].b + offsetY
37
- }
38
-
39
- function isOutside(p) {
40
- return getTipOffsetLeft(p) < 0 || //outside left
41
- getTipOffsetRight(p) > window.innerWidth || //outside right
42
- getTipOffsetTop(p) < 0 || //outside top
43
- getTipOffsetBottom(p) > window.innerHeight //outside bottom
44
- }
45
-
46
- let place = 'top'
47
- if (!isOutside(desiredPlace)) {
48
- place = desiredPlace || 'top'
49
- } else {
50
- const possiblePlacements = (['top', 'bottom', 'left', 'right']).filter(p => !isOutside(p))
51
- if (possiblePlacements.length > 0 && isOutside(desiredPlace)) {
52
- place = possiblePlacements[0] || 'top'
53
- }
54
- }
55
-
56
- return {
57
- place,
58
- position: {
59
- top: (getTipOffsetTop(place) + window.scrollY) | 0,
60
- left: tipWidth >= window.innerWidth ? 0 : ((getTipOffsetLeft(place) + window.scrollX) | 0)
61
- }
62
- }
63
- }
64
-
65
- /**
66
- * @param {Element} node
67
- * @return {PositionRect}
68
- */
69
- function getBoundingRect(node) {
70
- const {width, height, top, left} = node.getBoundingClientRect()
71
- return {
72
- width: width | 0,
73
- height: height | 0,
74
- top: top | 0,
75
- left: left | 0
76
- }
77
- }
78
-
79
- /**
80
- * @param {Element} element
81
- * @return {{mouseX: Number, mouseY: Number}}
82
- */
83
- function getMouseOffset(element) {
84
- const {width, height, top, left} = getBoundingRect(element)
85
- return {
86
- mouseX: (left + (width / 2)) | 0,
87
- mouseY: (top + (height / 2)) | 0
88
- }
89
- }
90
-
91
- /**
92
- * @param {Number} targetWidth
93
- * @param {Number} targetHeight
94
- * @param {Number} tipWidth
95
- * @param {Number} tipHeight
96
- * */
97
- function getDefaultPosition(targetWidth, targetHeight, tipWidth, tipHeight) {
98
- const notchSize = 4
99
- return {
100
- top: {
101
- l: -tipWidth / 2,
102
- r: tipWidth / 2,
103
- t: -targetHeight / 2 - tipHeight + notchSize,
104
- b: -targetHeight / 2
105
- },
106
- bottom: {
107
- l: -tipWidth / 2,
108
- r: tipWidth / 2,
109
- t: targetHeight / 2 - notchSize,
110
- b: targetHeight / 2 + tipHeight
111
- },
112
- left: {
113
- l: -tipWidth - targetWidth / 2 + notchSize,
114
- r: -targetWidth / 2 + notchSize,
115
- t: -tipHeight / 2,
116
- b: tipHeight / 2
117
- },
118
- right: {
119
- l: targetWidth / 2 - notchSize / 2,
120
- r: tipWidth + targetWidth / 2,
121
- t: -tipHeight / 2,
122
- b: tipHeight / 2
123
- }
124
- }
125
- }
126
-
127
- /**
128
- * @param {PositionOffset} offset
129
- * @return {{offsetX: Number, offsetY: Number}}
130
- */
131
- function parseOffset(offset) {
132
- let offsetX = 0,
133
- offsetY = 0
134
-
135
- for (let key in Object.keys(offset)) {
136
- const value = parseInt(offset[key])
137
- if (key === 'top') {
138
- offsetY -= value
139
- } else if (key === 'bottom') {
140
- offsetY += value
141
- } else if (key === 'left') {
142
- offsetX -= value
143
- } else if (key === 'right') {
144
- offsetX += value
145
- }
146
- }
147
-
148
- return {offsetX, offsetY}
149
- }
150
-
151
- /**
152
- * Tooltip component
153
- * @param {HTMLElement} trigger
154
- * @param {PositionDescriptor} desiredPlace
155
- * @param {PositionOffset} [offset]
156
- * @param {'hover'|'click'} [activation]
157
- * @param {String} [maxWidth]
158
- * @param {*} [children]
159
- * @constructor
160
- */
161
- export const Tooltip = React.memo(function Tooltip({
162
- trigger,
163
- desiredPlace = 'top',
164
- offset = {},
165
- activation = 'hover',
166
- children,
167
- maxWidth,
168
- ...op
169
- }) {
170
- const [visible, setVisible] = useState(false)
171
- const [rendered, setRendered] = useState(false)
172
- const [place, setPlace] = useState('top')
173
- const [position, setPosition] = useState({top: 0, left: 0})
174
- const content = useRef(null)
175
-
176
- function activate(e) {
177
- if (visible)
178
- return
179
- const {currentTarget} = e
180
- setRendered(true)
181
- content.current.showPopover()
182
- setTimeout(() => {
183
- if (visible)
184
- return
185
- const {place, position} = calculateTooltipPosition(currentTarget, content.current, desiredPlace, offset)
186
- setVisible(true)
187
- setPosition(position)
188
- setPlace(place)
189
- }, 400)
190
- }
191
-
192
- function onMouseLeave(e) {
193
- if (!visible)
194
- return
195
- content.current.hidePopover()
196
- setVisible(false)
197
- setRendered(false)
198
- }
199
-
200
- const triggerProps = {
201
- onMouseLeave,
202
- ...op
203
- }
204
- if (activation === 'hover') {
205
- triggerProps.onMouseEnter = activate
206
- } else {
207
- triggerProps.onClick = activate
208
- }
209
- const containerStyle = {
210
- left: position.left + 'px',
211
- top: position.top + 'px'
212
- }
213
- if (maxWidth) {
214
- containerStyle.width = '100vw'
215
- containerStyle.maxWidth = maxWidth
216
- }
217
-
218
- return React.cloneElement(trigger, triggerProps, <div ref={content} className="tooltip-wrapper" style={containerStyle} popover="auto">
219
- <div className={cn('tooltip', place, {visible})}>
220
- <div className="tooltip-content">{rendered ? children : null}</div>
221
- </div>
222
- </div>)
223
- })
224
-
225
- /**
226
- * @typedef {'top'|'bottom'|'left'|'right'} PositionDescriptor
227
- */
228
- /**
229
- * @typedef {Object} PositionRect
230
- * @property {Number} top - Top offset
231
- * @property {Number} left - Left offset
232
- * @property {Number} width - Element width
233
- * @property {Number} height - Element height
234
- */
235
- /**
236
- * @typedef {Object} PositionOffset
237
- * @property {Number} [top] - Top offset
238
- * @property {Number} [bottom] - Bottom offset
239
- * @property {Number} [left] - Left offset
240
- * @property {Number} [right] - Right offset
1
+ import React, {useState, useRef} from 'react'
2
+ import cn from 'classnames'
3
+ import './tooltip.scss'
4
+
5
+ /**
6
+ * Compute tooltip position
7
+ * @param {EventTarget} target - Mouse hover target
8
+ * @param {Element} node - Tooltip object
9
+ * @param {'top'|'bottom'|'left'|'right'} desiredPlace - Desired tooltip place
10
+ * @param {Object} offset
11
+ * @return {{place: string, position: {top: number, left: number}}}
12
+ */
13
+ function calculateTooltipPosition(target, node, desiredPlace, offset) {
14
+ //dimensions of node and target
15
+ const {width: tipWidth, height: tipHeight} = getBoundingRect(node)
16
+ const {width: targetWidth, height: targetHeight} = getBoundingRect(target)
17
+ //mouse offset
18
+ const {mouseX, mouseY} = getMouseOffset(target)
19
+ const {offsetX, offsetY} = parseOffset(offset)
20
+ const defaultOffset = getDefaultPosition(targetWidth, targetHeight, tipWidth, tipHeight)
21
+
22
+ // Get the edge offset of the tooltip
23
+ function getTipOffsetLeft(place) {
24
+ return mouseX + defaultOffset[place].l + offsetX
25
+ }
26
+
27
+ function getTipOffsetRight(place) {
28
+ return mouseX + defaultOffset[place].r + offsetX
29
+ }
30
+
31
+ function getTipOffsetTop(place) {
32
+ return mouseY + defaultOffset[place].t + offsetY
33
+ }
34
+
35
+ function getTipOffsetBottom(place) {
36
+ return mouseY + defaultOffset[place].b + offsetY
37
+ }
38
+
39
+ function isOutside(p) {
40
+ return getTipOffsetLeft(p) < 0 || //outside left
41
+ getTipOffsetRight(p) > window.innerWidth || //outside right
42
+ getTipOffsetTop(p) < 0 || //outside top
43
+ getTipOffsetBottom(p) > window.innerHeight //outside bottom
44
+ }
45
+
46
+ let place = 'top'
47
+ if (!isOutside(desiredPlace)) {
48
+ place = desiredPlace || 'top'
49
+ } else {
50
+ const possiblePlacements = (['top', 'bottom', 'left', 'right']).filter(p => !isOutside(p))
51
+ if (possiblePlacements.length > 0 && isOutside(desiredPlace)) {
52
+ place = possiblePlacements[0] || 'top'
53
+ }
54
+ }
55
+
56
+ return {
57
+ place,
58
+ position: {
59
+ top: (getTipOffsetTop(place) + window.scrollY) | 0,
60
+ left: tipWidth >= window.innerWidth ? 0 : ((getTipOffsetLeft(place) + window.scrollX) | 0)
61
+ }
62
+ }
63
+ }
64
+
65
+ /**
66
+ * Get the bounding rectangle of a DOM node
67
+ * @param {Element} node
68
+ * @return {PositionRect}
69
+ */
70
+ function getBoundingRect(node) {
71
+ const {width, height, top, left} = node.getBoundingClientRect()
72
+ return {
73
+ width: width | 0,
74
+ height: height | 0,
75
+ top: top | 0,
76
+ left: left | 0
77
+ }
78
+ }
79
+
80
+ /**
81
+ * Calculate element offset for mouse positioning
82
+ * @param {Element} element
83
+ * @return {{mouseX: number, mouseY: number}}
84
+ */
85
+ function getMouseOffset(element) {
86
+ const {width, height, top, left} = getBoundingRect(element)
87
+ return {
88
+ mouseX: (left + (width / 2)) | 0,
89
+ mouseY: (top + (height / 2)) | 0
90
+ }
91
+ }
92
+
93
+ /**
94
+ * Calculate default tooltip position boundaries for each placement direction
95
+ * @param {number} targetWidth
96
+ * @param {number} targetHeight
97
+ * @param {number} tipWidth
98
+ * @param {number} tipHeight
99
+ */
100
+ function getDefaultPosition(targetWidth, targetHeight, tipWidth, tipHeight) {
101
+ const notchSize = 4
102
+ return {
103
+ top: {
104
+ l: -tipWidth / 2,
105
+ r: tipWidth / 2,
106
+ t: -targetHeight / 2 - tipHeight + notchSize,
107
+ b: -targetHeight / 2
108
+ },
109
+ bottom: {
110
+ l: -tipWidth / 2,
111
+ r: tipWidth / 2,
112
+ t: targetHeight / 2 - notchSize,
113
+ b: targetHeight / 2 + tipHeight
114
+ },
115
+ left: {
116
+ l: -tipWidth - targetWidth / 2 + notchSize,
117
+ r: -targetWidth / 2 + notchSize,
118
+ t: -tipHeight / 2,
119
+ b: tipHeight / 2
120
+ },
121
+ right: {
122
+ l: targetWidth / 2 - notchSize / 2,
123
+ r: tipWidth + targetWidth / 2,
124
+ t: -tipHeight / 2,
125
+ b: tipHeight / 2
126
+ }
127
+ }
128
+ }
129
+
130
+ /**
131
+ * Parse position offset value into X/Y coordinates
132
+ * @param {PositionOffset} offset
133
+ * @return {{offsetX: number, offsetY: number}}
134
+ */
135
+ function parseOffset(offset) {
136
+ let offsetX = 0,
137
+ offsetY = 0
138
+
139
+ for (let key in Object.keys(offset)) {
140
+ const value = parseInt(offset[key])
141
+ if (key === 'top') {
142
+ offsetY -= value
143
+ } else if (key === 'bottom') {
144
+ offsetY += value
145
+ } else if (key === 'left') {
146
+ offsetX -= value
147
+ } else if (key === 'right') {
148
+ offsetX += value
149
+ }
150
+ }
151
+
152
+ return {offsetX, offsetY}
153
+ }
154
+
155
+ /**
156
+ * Tooltip component
157
+ * @param {HTMLElement} trigger
158
+ * @param {PositionDescriptor} desiredPlace
159
+ * @param {PositionOffset} [offset]
160
+ * @param {'hover'|'click'} [activation]
161
+ * @param {string} [maxWidth]
162
+ * @param {*} [children]
163
+ * @constructor
164
+ */
165
+ export const Tooltip = React.memo(function Tooltip({
166
+ trigger,
167
+ desiredPlace = 'top',
168
+ offset = {},
169
+ activation = 'hover',
170
+ children,
171
+ maxWidth,
172
+ ...op
173
+ }) {
174
+ const [visible, setVisible] = useState(false)
175
+ const [rendered, setRendered] = useState(false)
176
+ const [place, setPlace] = useState('top')
177
+ const [position, setPosition] = useState({top: 0, left: 0})
178
+ const content = useRef(null)
179
+
180
+ function activate(e) {
181
+ if (visible)
182
+ return
183
+ const {currentTarget} = e
184
+ setRendered(true)
185
+ content.current.showPopover()
186
+ setTimeout(() => {
187
+ if (visible)
188
+ return
189
+ const {place, position} = calculateTooltipPosition(currentTarget, content.current, desiredPlace, offset)
190
+ setVisible(true)
191
+ setPosition(position)
192
+ setPlace(place)
193
+ }, 400)
194
+ }
195
+
196
+ function onMouseLeave(e) {
197
+ if (!visible)
198
+ return
199
+ content.current.hidePopover()
200
+ setVisible(false)
201
+ setRendered(false)
202
+ }
203
+
204
+ const triggerProps = {
205
+ onMouseLeave,
206
+ ...op
207
+ }
208
+ if (activation === 'hover') {
209
+ triggerProps.onMouseEnter = activate
210
+ } else {
211
+ triggerProps.onClick = activate
212
+ }
213
+ const containerStyle = {
214
+ left: position.left + 'px',
215
+ top: position.top + 'px'
216
+ }
217
+ if (maxWidth) {
218
+ containerStyle.width = '100vw'
219
+ containerStyle.maxWidth = maxWidth
220
+ }
221
+
222
+ return React.cloneElement(trigger, triggerProps, <div ref={content} className="tooltip-wrapper" style={containerStyle} popover="auto">
223
+ <div className={cn('tooltip', place, {visible})}>
224
+ <div className="tooltip-content">{rendered ? children : null}</div>
225
+ </div>
226
+ </div>)
227
+ })
228
+
229
+ /**
230
+ * @typedef {'top'|'bottom'|'left'|'right'} PositionDescriptor
231
+ */
232
+ /**
233
+ * @typedef {Object} PositionRect
234
+ * @property {number} top - Top offset
235
+ * @property {number} left - Left offset
236
+ * @property {number} width - Element width
237
+ * @property {number} height - Element height
238
+ */
239
+ /**
240
+ * @typedef {Object} PositionOffset
241
+ * @property {number} [top] - Top offset
242
+ * @property {number} [bottom] - Bottom offset
243
+ * @property {number} [left] - Left offset
244
+ * @property {number} [right] - Right offset
241
245
  */
@@ -1,28 +1,33 @@
1
- import React, {useRef} from 'react'
2
- import PropTypes from 'prop-types'
3
- import cn from 'classnames'
4
- import {useDependantState} from '../state/state-hooks'
5
- import './update-highlighter.scss'
6
-
7
- export const UpdateHighlighter = React.memo(function UpdateHighlighter({children}) {
8
- const timerRef = useRef(0)
9
- const [active, setActiveState] = useDependantState(() => {
10
- //reset the timer in case if previous animation wasn't finished yet
11
- if (timerRef.current) {
12
- clearTimeout(timerRef.current)
13
- useRef.current = 0
14
- }
15
- //schedule class reset
16
- timerRef.current = setTimeout(function () {
17
- timerRef.current = 0
18
- setActiveState(false)
19
- }, 600)
20
- return true
21
- }, [children])
22
-
23
- return <span className={cn({highlighter: active})}>{children}</span>
24
- })
25
-
26
- UpdateHighlighter.propTypes = {
27
- children: PropTypes.any.isRequired
1
+ import React, {useRef} from 'react'
2
+ import PropTypes from 'prop-types'
3
+ import cn from 'classnames'
4
+ import {useDependantState} from '../state/state-hooks'
5
+ import './update-highlighter.scss'
6
+
7
+ /**
8
+ * Briefly highlights its content with an animation when children change
9
+ * @param {Object} props
10
+ * @param {*} props.children - Content to monitor and highlight on update
11
+ */
12
+ export const UpdateHighlighter = React.memo(function UpdateHighlighter({children}) {
13
+ const timerRef = useRef(0)
14
+ const [active, setActiveState] = useDependantState(() => {
15
+ //reset the timer in case if previous animation wasn't finished yet
16
+ if (timerRef.current) {
17
+ clearTimeout(timerRef.current)
18
+ useRef.current = 0
19
+ }
20
+ //schedule class reset
21
+ timerRef.current = setTimeout(function () {
22
+ timerRef.current = 0
23
+ setActiveState(false)
24
+ }, 600)
25
+ return true
26
+ }, [children])
27
+
28
+ return <span className={cn({highlighter: active})}>{children}</span>
29
+ })
30
+
31
+ UpdateHighlighter.propTypes = {
32
+ children: PropTypes.any.isRequired
28
33
  }