@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.
- package/CLAUDE.md +35 -0
- package/README.md +1125 -3
- package/account/account-address.js +127 -127
- package/account/available-balance.js +22 -22
- package/account/identicon.js +90 -90
- package/account/signer-key.js +65 -64
- package/api/explorer-api-hooks.js +209 -202
- package/api/explorer-api-paginated-list-hooks.js +441 -440
- package/api/explorer-batch-info-loader.js +111 -85
- package/api/explorer-tx-api.js +28 -28
- package/api/ledger-stream.js +15 -0
- package/asset/amount.js +56 -56
- package/asset/asset-icon.js +41 -41
- package/asset/asset-issuer.js +21 -21
- package/asset/asset-link.js +107 -107
- package/asset/asset-list-hooks.js +59 -59
- package/asset/asset-meta-hooks.js +88 -88
- package/asset/asset-selector.js +72 -71
- package/claimable-balance/claimable-balance-claimants.js +23 -18
- package/contract/contract-api.js +15 -0
- package/contract/invocation-info-view.js +10 -2
- package/contract/sc-val.js +131 -107
- package/controls/button-group.js +25 -19
- package/controls/button.js +93 -78
- package/controls/code-block.js +42 -34
- package/controls/dropdown.js +318 -287
- package/controls/external-link.js +10 -4
- package/controls/info-tooltip.js +23 -16
- package/controls/slider.js +29 -19
- package/controls/tabs.js +94 -94
- package/controls/tooltip.js +244 -240
- package/controls/update-highlighter.js +32 -27
- package/date/date-selector.js +56 -54
- package/date/elapsed-time.js +28 -21
- package/dex/price-dynamic.js +53 -44
- package/directory/directory-hooks.js +109 -97
- package/effect/effect-description.js +346 -344
- package/errors/error-boundary.js +110 -97
- package/horizon/horizon-account-helpers.js +104 -104
- package/horizon/horizon-ledger-helpers.js +35 -35
- package/horizon/horizon-trades-helper.js +88 -88
- package/horizon/horizon-transaction-helpers.js +36 -36
- package/index.d.ts +1241 -0
- package/interaction/accordion.js +43 -35
- package/interaction/autofocus.js +13 -9
- package/interaction/block-select.js +64 -53
- package/interaction/copy-to-clipboard.js +25 -18
- package/interaction/inline-progress.js +20 -15
- package/interaction/qr-code.js +34 -34
- package/interaction/responsive.js +20 -20
- package/interaction/spoiler.js +52 -39
- package/interaction/theme-selector.js +13 -10
- package/ledger/ledger-entry-href-formatter.js +27 -26
- package/ledger/ledger-entry-link.js +93 -58
- package/ledger/ledger-info-parser.js +46 -18
- package/meta/page-meta-tags.js +243 -238
- package/module/dynamic-module.js +47 -47
- package/package.json +41 -40
- package/state/on-screen-hooks.js +22 -22
- package/state/page-visibility-helpers.js +29 -16
- package/state/page-visibility-hooks.js +13 -11
- package/state/screen-orientation-hooks.js +19 -15
- package/state/state-hooks.js +76 -76
- package/state/stellar-network-hooks.js +56 -44
- package/state/theme.js +29 -28
- package/stellar/key-type.js +92 -91
- package/stellar/ledger-generic-id.js +39 -39
- package/stellar/signature-hint-utils.js +65 -65
- package/toast/toast-notifications-block.js +59 -59
- package/tx/op-description-view.js +945 -942
- package/tx/op-icon.js +98 -98
- package/tx/parser/op-balance-changes.js +66 -66
- package/tx/parser/op-descriptor.js +51 -48
- package/tx/parser/tx-details-parser.js +81 -81
- package/tx/parser/tx-matcher.js +371 -371
- package/tx/parser/type-filter-matcher.js +126 -126
- package/tx/tx-list-hooks.js +32 -18
- package/tx/tx-operations-list.js +117 -116
package/controls/tooltip.js
CHANGED
|
@@ -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:
|
|
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
|
-
*
|
|
67
|
-
* @
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
*
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
*
|
|
95
|
-
* @param {
|
|
96
|
-
*
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
} else if (key === '
|
|
144
|
-
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
*
|
|
157
|
-
* @param {
|
|
158
|
-
* @param {
|
|
159
|
-
* @
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
const
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
})
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
* @
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
* @
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
* @
|
|
237
|
-
* @property {
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
* @
|
|
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
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
//
|
|
16
|
-
timerRef.current
|
|
17
|
-
timerRef.current
|
|
18
|
-
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
})
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
}
|