@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,288 +1,319 @@
1
- import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'
2
- import {createPortal} from 'react-dom'
3
- import PropTypes from 'prop-types'
4
- import cn from 'classnames'
5
- import {throttle} from 'throttle-debounce'
6
- import {useDependantState} from '../state/state-hooks'
7
- import './dropdown.scss'
8
-
9
- export const Dropdown = React.memo(function Dropdown({
10
- options,
11
- title,
12
- value,
13
- disabled,
14
- className,
15
- onChange,
16
- hint,
17
- showToggle,
18
- solo,
19
- hideSelected,
20
- header,
21
- footer,
22
- expanded,
23
- onScroll,
24
- onOpen,
25
- onClose,
26
- maxHeight
27
- }) {
28
- const headerRef = useRef()
29
- const listRef = useRef()
30
- const [listOpen, updateListOpen] = useState(false)
31
- const [alignRight, setAlignRight] = useState(false)
32
- //collapse dropdown handler
33
- const collapseDropdown = useCallback(function () {
34
- updateListOpen(open => {
35
- if (!open)
36
- return false
37
- onClose?.call(this, this)
38
- return false
39
- })
40
- }, [onClose])
41
-
42
- //collapse dropdown list on click
43
- const [selectedValue, updateSelectedValue] = useDependantState(() => {
44
- if (listOpen) {
45
- setTimeout(() => {
46
- document.addEventListener('click', collapseDropdown)
47
- }, 200)
48
- }
49
- return value
50
- }, [value, listOpen], () => {
51
- document.removeEventListener('click', collapseDropdown)
52
- })
53
-
54
- //close/open dropdown on header click
55
- const toggleList = useCallback(function toggleList(e) {
56
- e && e.nativeEvent.stopImmediatePropagation()
57
- updateListOpen(prevState => {
58
- if (disabled)
59
- return false;
60
- (prevState ? onClose : onOpen)?.call(this, this)
61
- return !prevState
62
- })
63
- }, [disabled, onClose, onOpen])
64
-
65
- //handle item select action
66
- const select = useCallback(function (option) {
67
- collapseDropdown()
68
- if (disabled)
69
- return
70
- onChange && onChange(option.value || option)
71
- updateSelectedValue(option)
72
- }, [collapseDropdown, onChange])
73
-
74
- //handle user scroll action for infinity scroll support
75
- const scrollList = useMemo(() => throttle(200, e => {
76
- if (!onScroll)
77
- return
78
- const {target} = e
79
- const pos = {position: target.scrollTop, rel: 'middle'}
80
- if (target.scrollTop === 0)
81
- return onScroll({...pos, rel: 'top'})
82
- if (Math.ceil(target.scrollHeight - target.scrollTop - 8) < target.clientHeight)
83
- return onScroll({...pos, rel: 'bottom'})
84
- onScroll(pos)
85
- }), [onScroll])
86
-
87
- //locate currently selected option
88
- const {option: selectedItem, isDefault} = getSelectedOption([value, selectedValue], options)
89
-
90
- //check dropdown container alignment
91
- if (listOpen) {
92
- setTimeout(() => {
93
- if (!alignRight) {
94
- const shouldAlignRight = isAlignedRight(listRef.current)
95
- if (alignRight !== shouldAlignRight) {
96
- setAlignRight(shouldAlignRight)
97
- }
98
- }
99
- }, 100)
100
- }
101
- //show a dropdown after the initial render
102
- useEffect(() => {
103
- if (expanded === true) {
104
- setTimeout(() => updateListOpen(true), 200)
105
- }
106
- }, [])
107
-
108
- const ddTitle = title || selectedItem?.title || selectedItem?.value || selectedItem
109
-
110
- return <div className={cn('dd-wrapper', {disabled}, className)} title={hint}>
111
- <a href="#" className="dd-header" onClick={toggleList} ref={headerRef}>
112
- {ddTitle}{!!showToggle && <span className={cn('dd-toggle', {visible: listOpen})}/>}
113
- </a>
114
- {!!listOpen && createPortal(<div className={className}>
115
- <div className={cn('dd-backdrop', {solo})}/>
116
- <div className={cn('dd-list', {solo, visible: listOpen && !disabled, 'align-right': alignRight})}
117
- style={getListPosition(headerRef.current, solo)} ref={listRef}>
118
- {!!header && <>
119
- <div className="dd-list-header" onClick={preventClosing}>{header}</div>
120
- <hr/>
121
- </>}
122
- <ul onScroll={scrollList} style={{maxHeight: `min(70vh, ${maxHeight})`}}>
123
- {options.filter(opt => !opt.hidden).map((option, i) => {
124
- if (option === '-') return <li className="dd-list-item" key={i + '-'}>
125
- <hr className="flare"/>
126
- </li>
127
- const key = option.value || option.href || option
128
- const isSelected = !isDefault && option === selectedItem
129
- const style = isSelected && hideSelected ? {display: 'none'} : {}
130
- return <DropdownOption {...{key, option, select, isSelected, style}} />
131
- })}
132
- </ul>
133
- {!!footer && <>
134
- <hr/>
135
- <div className="dd-list-footer" onClick={preventClosing}>{footer}</div>
136
- </>}
137
- </div>
138
- </div>, document.body)}
139
- </div>
140
- })
141
-
142
- const DropdownOption = React.memo(function DropdownOption({option, isSelected, select, style}) {
143
- let {value, title, href, className} = option
144
- if (typeof option === 'string') {
145
- value = title = option
146
- }
147
- if (!title) {
148
- title = value
149
- }
150
-
151
- const selectOption = useCallback(function (e) {
152
- if (option.disabled) {
153
- e.stopPropagation()
154
- return
155
- }
156
- if (!option.href) {
157
- e.preventDefault()
158
- }
159
- select(option)
160
- }, [select, option])
161
-
162
- return <li className="dd-list-item" key={value || href} onClick={selectOption} style={style}>
163
- <a href={href || '#'} className={cn({className, selected: isSelected})}>{title}</a>
164
- </li>
165
- })
166
-
167
- function getSelectedOption(values, options) {
168
- for (let v of values) {
169
- if (v !== null && v !== undefined) {
170
- const option = options.find(item => item === v || item.value === v)
171
- if (option) return {option, isDefault: false}
172
- }
173
- }
174
- return {option: options.find(opt => typeof opt === 'string' || !opt.disabled), isDefault: true}
175
- }
176
-
177
- function getListPosition(header, solo) {
178
- if (solo || !header)
179
- return undefined
180
- const rect = header.getBoundingClientRect()
181
- return {
182
- top: rect.bottom + window.scrollY,
183
- left: rect.left + window.scrollX
184
- }
185
- }
186
-
187
- function isAlignedRight(list) {
188
- if (!list)
189
- return
190
- const rect = list.getBoundingClientRect()
191
- return window.innerWidth - rect.right < 0 && rect.left - rect.width >= 0
192
- }
193
-
194
- function preventClosing(e) {
195
- e.stopPropagation()
196
- }
197
-
198
- Dropdown.defaultProps = {
199
- showToggle: true,
200
- disabled: false,
201
- hideSelected: false,
202
- solo: false,
203
- maxHeight: '35em'
204
- }
205
-
206
- Dropdown.propTypes = {
207
- /**
208
- * Available options
209
- */
210
- options: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.shape({
211
- /**
212
- * Internal value used for the item identification
213
- */
214
- value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
215
- /**
216
- * Link href - for dropdown menus
217
- */
218
- href: PropTypes.string,
219
- /**
220
- * Optional friendly item title
221
- */
222
- title: PropTypes.any
223
- }), PropTypes.string])).isRequired,
224
- /**
225
- * Selected value
226
- */
227
- value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
228
- /**
229
- * Title to display instead of the selected value if needed
230
- */
231
- title: PropTypes.any,
232
- /**
233
- * OnChange handler
234
- */
235
- onChange: PropTypes.func,
236
- /**
237
- * Whether the dropdown interaction is disabled or not
238
- */
239
- disabled: PropTypes.bool,
240
- /**
241
- * HTML hover title attribute for the control
242
- */
243
- hint: PropTypes.string,
244
- /**
245
- * Additional HTML classes
246
- */
247
- className: PropTypes.string,
248
- /**
249
- * Whether to show toggle icon
250
- */
251
- showToggle: PropTypes.bool,
252
- /**
253
- * Show a dropdown list in centered dialog
254
- */
255
- solo: PropTypes.bool,
256
- /**
257
- * Do not show selected item in the dropdown list
258
- */
259
- hideSelected: PropTypes.bool,
260
- /**
261
- * Optional dropdown list header
262
- */
263
- header: PropTypes.any,
264
- /**
265
- * Optional dropdown list footer
266
- */
267
- footer: PropTypes.any,
268
- /**
269
- * Initially collapsed or open
270
- */
271
- expanded: PropTypes.bool,
272
- /**
273
- * List scroll handler - fires only if the options list has overflow
274
- */
275
- onScroll: PropTypes.func,
276
- /**
277
- * List open handler
278
- */
279
- onOpen: PropTypes.func,
280
- /**
281
- * Lsi close handler
282
- */
283
- onClose: PropTypes.func,
284
- /**
285
- * Maximum dropdown list height
286
- */
287
- maxHeight: PropTypes.string
1
+ import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'
2
+ import {createPortal} from 'react-dom'
3
+ import PropTypes from 'prop-types'
4
+ import cn from 'classnames'
5
+ import {throttle} from 'throttle-debounce'
6
+ import {useDependantState} from '../state/state-hooks'
7
+ import './dropdown.scss'
8
+
9
+ /**
10
+ * @typedef {Object} DropdownOption
11
+ * @property {string|number} [value] - Internal value used for identification
12
+ * @property {string} [href] - Link URL for menu-style dropdown items
13
+ * @property {*} [title] - Display title (defaults to value)
14
+ * @property {string} [className] - Additional CSS class
15
+ * @property {boolean} [disabled] - Whether the option is disabled
16
+ * @property {boolean} [hidden] - Whether to hide the option
17
+ */
18
+
19
+ /**
20
+ * Customizable dropdown select component
21
+ * @param {Object} props
22
+ * @param {Array<DropdownOption|string>} props.options - Available options
23
+ * @param {string|number} [props.value] - Currently selected value
24
+ * @param {*} [props.title] - Title to display instead of the selected value
25
+ * @param {boolean} [props.disabled=false] - Disable the dropdown
26
+ * @param {string} [props.className] - Additional CSS classes
27
+ * @param {function(string|number): void} [props.onChange] - Callback invoked with the selected value
28
+ * @param {string} [props.hint] - HTML title attribute for the control
29
+ * @param {boolean} [props.showToggle=true] - Show toggle arrow icon
30
+ * @param {boolean} [props.solo=false] - Show dropdown list in a centered dialog overlay
31
+ * @param {boolean} [props.hideSelected=false] - Hide currently selected item from the list
32
+ * @param {*} [props.header] - Optional list header content
33
+ * @param {*} [props.footer] - Optional list footer content
34
+ * @param {boolean} [props.expanded] - Initially open the dropdown
35
+ * @param {function({position: number, rel: 'top'|'middle'|'bottom'}): void} [props.onScroll] - Scroll handler for infinite scroll support
36
+ * @param {function} [props.onOpen] - Handler called when the dropdown opens
37
+ * @param {function} [props.onClose] - Handler called when the dropdown closes
38
+ * @param {string} [props.maxHeight='35em'] - Maximum dropdown list height
39
+ */
40
+ export const Dropdown = React.memo(function Dropdown({
41
+ options,
42
+ title,
43
+ value,
44
+ disabled,
45
+ className,
46
+ onChange,
47
+ hint,
48
+ showToggle,
49
+ solo,
50
+ hideSelected,
51
+ header,
52
+ footer,
53
+ expanded,
54
+ onScroll,
55
+ onOpen,
56
+ onClose,
57
+ maxHeight
58
+ }) {
59
+ const headerRef = useRef()
60
+ const listRef = useRef()
61
+ const [listOpen, updateListOpen] = useState(false)
62
+ const [alignRight, setAlignRight] = useState(false)
63
+ //collapse dropdown handler
64
+ const collapseDropdown = useCallback(function () {
65
+ updateListOpen(open => {
66
+ if (!open)
67
+ return false
68
+ onClose?.call(this, this)
69
+ return false
70
+ })
71
+ }, [onClose])
72
+
73
+ //collapse dropdown list on click
74
+ const [selectedValue, updateSelectedValue] = useDependantState(() => {
75
+ if (listOpen) {
76
+ setTimeout(() => {
77
+ document.addEventListener('click', collapseDropdown)
78
+ }, 200)
79
+ }
80
+ return value
81
+ }, [value, listOpen], () => {
82
+ document.removeEventListener('click', collapseDropdown)
83
+ })
84
+
85
+ //close/open dropdown on header click
86
+ const toggleList = useCallback(function toggleList(e) {
87
+ e && e.nativeEvent.stopImmediatePropagation()
88
+ updateListOpen(prevState => {
89
+ if (disabled)
90
+ return false;
91
+ (prevState ? onClose : onOpen)?.call(this, this)
92
+ return !prevState
93
+ })
94
+ }, [disabled, onClose, onOpen])
95
+
96
+ //handle item select action
97
+ const select = useCallback(function (option) {
98
+ collapseDropdown()
99
+ if (disabled)
100
+ return
101
+ onChange && onChange(option.value || option)
102
+ updateSelectedValue(option)
103
+ }, [collapseDropdown, onChange])
104
+
105
+ //handle user scroll action for infinity scroll support
106
+ const scrollList = useMemo(() => throttle(200, e => {
107
+ if (!onScroll)
108
+ return
109
+ const {target} = e
110
+ const pos = {position: target.scrollTop, rel: 'middle'}
111
+ if (target.scrollTop === 0)
112
+ return onScroll({...pos, rel: 'top'})
113
+ if (Math.ceil(target.scrollHeight - target.scrollTop - 8) < target.clientHeight)
114
+ return onScroll({...pos, rel: 'bottom'})
115
+ onScroll(pos)
116
+ }), [onScroll])
117
+
118
+ //locate currently selected option
119
+ const {option: selectedItem, isDefault} = getSelectedOption([value, selectedValue], options)
120
+
121
+ //check dropdown container alignment
122
+ if (listOpen) {
123
+ setTimeout(() => {
124
+ if (!alignRight) {
125
+ const shouldAlignRight = isAlignedRight(listRef.current)
126
+ if (alignRight !== shouldAlignRight) {
127
+ setAlignRight(shouldAlignRight)
128
+ }
129
+ }
130
+ }, 100)
131
+ }
132
+ //show a dropdown after the initial render
133
+ useEffect(() => {
134
+ if (expanded === true) {
135
+ setTimeout(() => updateListOpen(true), 200)
136
+ }
137
+ }, [])
138
+
139
+ const ddTitle = title || selectedItem?.title || selectedItem?.value || selectedItem
140
+
141
+ return <div className={cn('dd-wrapper', {disabled}, className)} title={hint}>
142
+ <a href="#" className="dd-header" onClick={toggleList} ref={headerRef}>
143
+ {ddTitle}{!!showToggle && <span className={cn('dd-toggle', {visible: listOpen})}/>}
144
+ </a>
145
+ {!!listOpen && createPortal(<div className={className}>
146
+ <div className={cn('dd-backdrop', {solo})}/>
147
+ <div className={cn('dd-list', {solo, visible: listOpen && !disabled, 'align-right': alignRight})}
148
+ style={getListPosition(headerRef.current, solo)} ref={listRef}>
149
+ {!!header && <>
150
+ <div className="dd-list-header" onClick={preventClosing}>{header}</div>
151
+ <hr/>
152
+ </>}
153
+ <ul onScroll={scrollList} style={{maxHeight: `min(70vh, ${maxHeight})`}}>
154
+ {options.filter(opt => !opt.hidden).map((option, i) => {
155
+ if (option === '-') return <li className="dd-list-item" key={i + '-'}>
156
+ <hr className="flare"/>
157
+ </li>
158
+ const key = option.value || option.href || option
159
+ const isSelected = !isDefault && option === selectedItem
160
+ const style = isSelected && hideSelected ? {display: 'none'} : {}
161
+ return <DropdownOption {...{key, option, select, isSelected, style}} />
162
+ })}
163
+ </ul>
164
+ {!!footer && <>
165
+ <hr/>
166
+ <div className="dd-list-footer" onClick={preventClosing}>{footer}</div>
167
+ </>}
168
+ </div>
169
+ </div>, document.body)}
170
+ </div>
171
+ })
172
+
173
+ const DropdownOption = React.memo(function DropdownOption({option, isSelected, select, style}) {
174
+ let {value, title, href, className} = option
175
+ if (typeof option === 'string') {
176
+ value = title = option
177
+ }
178
+ if (!title) {
179
+ title = value
180
+ }
181
+
182
+ const selectOption = useCallback(function (e) {
183
+ if (option.disabled) {
184
+ e.stopPropagation()
185
+ return
186
+ }
187
+ if (!option.href) {
188
+ e.preventDefault()
189
+ }
190
+ select(option)
191
+ }, [select, option])
192
+
193
+ return <li className="dd-list-item" key={value || href} onClick={selectOption} style={style}>
194
+ <a href={href || '#'} className={cn({className, selected: isSelected})}>{title}</a>
195
+ </li>
196
+ })
197
+
198
+ function getSelectedOption(values, options) {
199
+ for (let v of values) {
200
+ if (v !== null && v !== undefined) {
201
+ const option = options.find(item => item === v || item.value === v)
202
+ if (option) return {option, isDefault: false}
203
+ }
204
+ }
205
+ return {option: options.find(opt => typeof opt === 'string' || !opt.disabled), isDefault: true}
206
+ }
207
+
208
+ function getListPosition(header, solo) {
209
+ if (solo || !header)
210
+ return undefined
211
+ const rect = header.getBoundingClientRect()
212
+ return {
213
+ top: rect.bottom + window.scrollY,
214
+ left: rect.left + window.scrollX
215
+ }
216
+ }
217
+
218
+ function isAlignedRight(list) {
219
+ if (!list)
220
+ return
221
+ const rect = list.getBoundingClientRect()
222
+ return window.innerWidth - rect.right < 0 && rect.left - rect.width >= 0
223
+ }
224
+
225
+ function preventClosing(e) {
226
+ e.stopPropagation()
227
+ }
228
+
229
+ Dropdown.defaultProps = {
230
+ showToggle: true,
231
+ disabled: false,
232
+ hideSelected: false,
233
+ solo: false,
234
+ maxHeight: '35em'
235
+ }
236
+
237
+ Dropdown.propTypes = {
238
+ /**
239
+ * Available options
240
+ */
241
+ options: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.shape({
242
+ /**
243
+ * Internal value used for the item identification
244
+ */
245
+ value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
246
+ /**
247
+ * Link href - for dropdown menus
248
+ */
249
+ href: PropTypes.string,
250
+ /**
251
+ * Optional friendly item title
252
+ */
253
+ title: PropTypes.any
254
+ }), PropTypes.string])).isRequired,
255
+ /**
256
+ * Selected value
257
+ */
258
+ value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
259
+ /**
260
+ * Title to display instead of the selected value if needed
261
+ */
262
+ title: PropTypes.any,
263
+ /**
264
+ * OnChange handler
265
+ */
266
+ onChange: PropTypes.func,
267
+ /**
268
+ * Whether the dropdown interaction is disabled or not
269
+ */
270
+ disabled: PropTypes.bool,
271
+ /**
272
+ * HTML hover title attribute for the control
273
+ */
274
+ hint: PropTypes.string,
275
+ /**
276
+ * Additional HTML classes
277
+ */
278
+ className: PropTypes.string,
279
+ /**
280
+ * Whether to show toggle icon
281
+ */
282
+ showToggle: PropTypes.bool,
283
+ /**
284
+ * Show a dropdown list in centered dialog
285
+ */
286
+ solo: PropTypes.bool,
287
+ /**
288
+ * Do not show selected item in the dropdown list
289
+ */
290
+ hideSelected: PropTypes.bool,
291
+ /**
292
+ * Optional dropdown list header
293
+ */
294
+ header: PropTypes.any,
295
+ /**
296
+ * Optional dropdown list footer
297
+ */
298
+ footer: PropTypes.any,
299
+ /**
300
+ * Initially collapsed or open
301
+ */
302
+ expanded: PropTypes.bool,
303
+ /**
304
+ * List scroll handler - fires only if the options list has overflow
305
+ */
306
+ onScroll: PropTypes.func,
307
+ /**
308
+ * List open handler
309
+ */
310
+ onOpen: PropTypes.func,
311
+ /**
312
+ * Lsi close handler
313
+ */
314
+ onClose: PropTypes.func,
315
+ /**
316
+ * Maximum dropdown list height
317
+ */
318
+ maxHeight: PropTypes.string
288
319
  }
@@ -1,5 +1,11 @@
1
- import React from 'react'
2
-
3
- export const ExternalLink = React.memo(function ExternalLink({href, children, ...otherProps}) {
4
- return <a href={href} target="_blank" rel="noreferrer noopener" {...otherProps}>{children}</a>
1
+ import React from 'react'
2
+
3
+ /**
4
+ * Link that opens in a new tab with `rel="noreferrer noopener"` for security
5
+ * @param {Object} props
6
+ * @param {string} props.href - Link URL
7
+ * @param {*} [props.children] - Link content
8
+ */
9
+ export const ExternalLink = React.memo(function ExternalLink({href, children, ...otherProps}) {
10
+ return <a href={href} target="_blank" rel="noreferrer noopener" {...otherProps}>{children}</a>
5
11
  })