@stellar-expert/ui-framework 1.9.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/.npmrc +1 -0
- package/LICENSE +21 -0
- package/README.md +3 -0
- package/account/account-address.js +127 -0
- package/account/account-address.scss +20 -0
- package/account/available-balance.js +21 -0
- package/account/identicon.js +91 -0
- package/account/identicon.scss +6 -0
- package/account/signer-key.js +65 -0
- package/api/api-cache.js +5 -0
- package/api/explorer-api-call.js +37 -0
- package/api/explorer-api-hooks.js +202 -0
- package/api/explorer-api-paginated-list-hooks.js +439 -0
- package/api/explorer-batch-info-loader.js +85 -0
- package/api/explorer-tx-api.js +29 -0
- package/asset/amount.js +53 -0
- package/asset/asset-icon.js +34 -0
- package/asset/asset-issuer.js +22 -0
- package/asset/asset-link.js +93 -0
- package/asset/asset-link.scss +26 -0
- package/asset/asset-list-hooks.js +60 -0
- package/asset/asset-meta-hooks.js +78 -0
- package/asset/asset-selector.js +72 -0
- package/asset/asset-selector.scss +41 -0
- package/basic-styles/base.scss +53 -0
- package/basic-styles/form.scss +90 -0
- package/basic-styles/grid.scss +388 -0
- package/basic-styles/link.scss +17 -0
- package/basic-styles/lists.scss +68 -0
- package/basic-styles/loader.scss +22 -0
- package/basic-styles/responsive.scss +29 -0
- package/basic-styles/scrollbars.scss +19 -0
- package/basic-styles/spacing.scss +50 -0
- package/basic-styles/table.scss +104 -0
- package/basic-styles/themes.scss +59 -0
- package/basic-styles/typography.scss +191 -0
- package/basic-styles/variables.scss +26 -0
- package/claimable-balance/claimable-balance-claimants.js +12 -0
- package/contract/sc-val.js +70 -0
- package/contract/wasm-section-parser.js +159 -0
- package/contract/wasm-section-reader.js +79 -0
- package/contract/xdr-reader.js +148 -0
- package/controls/active-icon.scss +23 -0
- package/controls/button-group.js +20 -0
- package/controls/button-group.scss +48 -0
- package/controls/button.js +74 -0
- package/controls/button.scss +148 -0
- package/controls/code-block.js +35 -0
- package/controls/code-block.scss +74 -0
- package/controls/dropdown.js +281 -0
- package/controls/dropdown.scss +161 -0
- package/controls/external-link.js +5 -0
- package/controls/info-tooltip.js +17 -0
- package/controls/info-tooltip.scss +4 -0
- package/controls/slider.js +20 -0
- package/controls/slider.scss +22 -0
- package/controls/tabs.js +94 -0
- package/controls/tabs.scss +73 -0
- package/controls/tooltip.js +214 -0
- package/controls/tooltip.scss +97 -0
- package/controls/update-highlighter.js +28 -0
- package/controls/update-highlighter.scss +10 -0
- package/date/date-selector.js +44 -0
- package/date/elapsed-time.js +22 -0
- package/date/utc-timestamp.js +23 -0
- package/dex/price-dynamic.js +45 -0
- package/dex/price-dynamic.scss +36 -0
- package/directory/directory-hooks.js +89 -0
- package/effect/effect-description.js +289 -0
- package/effect/op-effects-view.js +11 -0
- package/effect/op-effects.scss +8 -0
- package/errors/error-boundary.js +98 -0
- package/fonts/font.scss +23 -0
- package/fonts/icons/config.json +1858 -0
- package/fonts/icons/icons-embedded.scss +644 -0
- package/horizon/horizon-account-helpers.js +104 -0
- package/horizon/horizon-client-helpers.js +52 -0
- package/horizon/horizon-ledger-helpers.js +36 -0
- package/horizon/horizon-orderbook-helpers.js +14 -0
- package/horizon/horizon-trades-helper.js +89 -0
- package/horizon/horizon-transaction-helpers.js +36 -0
- package/index.js +85 -0
- package/index.scss +16 -0
- package/interaction/autofocus.js +9 -0
- package/interaction/block-select.js +54 -0
- package/interaction/block-select.scss +24 -0
- package/interaction/copy-to-clipboard.js +19 -0
- package/interaction/inline-progress.js +16 -0
- package/interaction/qr-code.js +45 -0
- package/interaction/responsive.js +21 -0
- package/interaction/spoiler.js +40 -0
- package/interaction/spoiler.scss +9 -0
- package/interaction/theme-selector.js +11 -0
- package/ledger/ledger-entry-href-formatter.js +22 -0
- package/ledger/ledger-entry-link.js +59 -0
- package/meta/page-meta-tags.js +229 -0
- package/module/dynamic-module.js +48 -0
- package/package.json +41 -0
- package/state/on-screen-hooks.js +23 -0
- package/state/page-visibility-helpers.js +17 -0
- package/state/page-visibility-hooks.js +12 -0
- package/state/screen-orientation-hooks.js +16 -0
- package/state/state-hooks.js +77 -0
- package/state/stellar-network-hooks.js +45 -0
- package/state/theme.js +29 -0
- package/stellar/key-type.js +92 -0
- package/stellar/ledger-generic-id.js +40 -0
- package/stellar/signature-hint-utils.js +65 -0
- package/toast/toast-notification-instance.js +43 -0
- package/toast/toast-notification.js +46 -0
- package/toast/toast-notifications-block.js +55 -0
- package/toast/toast-notifications.scss +166 -0
- package/tx/op-accounting-changes.js +27 -0
- package/tx/op-description-view.js +925 -0
- package/tx/op-description.scss +98 -0
- package/tx/op-icon.js +93 -0
- package/tx/parser/op-balance-changes.js +62 -0
- package/tx/parser/op-descriptor.js +49 -0
- package/tx/parser/tx-details-parser.js +75 -0
- package/tx/parser/tx-matcher.js +372 -0
- package/tx/parser/type-filter-matcher.js +127 -0
- package/tx/tx-fee-effect.js +14 -0
- package/tx/tx-list-hooks.js +19 -0
- package/tx/tx-operations-list.js +105 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import {applyListQueryParameters, loadAllHorizonRecords, initHorizon} from './horizon-client-helpers'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Retrieve account status from Horizon
|
|
5
|
+
* @param {String} accountAddress
|
|
6
|
+
* @returns {Promise<AccountResponse>}
|
|
7
|
+
*/
|
|
8
|
+
export function loadAccount(accountAddress) {
|
|
9
|
+
return initHorizon()
|
|
10
|
+
.loadAccount(accountAddress)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Load assets issued by the account from Horizon
|
|
15
|
+
* @param {String} account - Account address
|
|
16
|
+
* @param {ListQueryParams} [queryParams] - Query parameters (optional)
|
|
17
|
+
* @return {Promise<Array<Object>>}
|
|
18
|
+
*/
|
|
19
|
+
export function loadIssuedAssets(account, queryParams = null) {
|
|
20
|
+
const query = applyListQueryParameters(initHorizon().assets().forIssuer(account).limit(200), queryParams)
|
|
21
|
+
return query.call()
|
|
22
|
+
.then(({records}) => {
|
|
23
|
+
if (records && records.length) {
|
|
24
|
+
records.sort((a, b) => a.asset_code - b.asset_code)
|
|
25
|
+
}
|
|
26
|
+
return records || []
|
|
27
|
+
})
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Load offer from Horizon for the particular account
|
|
32
|
+
* @param {String} account - Account address
|
|
33
|
+
* @param {ListQueryParams} [queryParams] - Query parameters (optional)
|
|
34
|
+
* @return {Promise<Array<Object>>}
|
|
35
|
+
*/
|
|
36
|
+
export function loadAccountOffers(account, queryParams = null) {
|
|
37
|
+
const query = applyListQueryParameters(initHorizon().offers().forAccount(account), queryParams)
|
|
38
|
+
return loadAllHorizonRecords(query)
|
|
39
|
+
.then(r => r.records)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Load pending claimable balances for a given account pubkey
|
|
45
|
+
* @param {String} account - Account address
|
|
46
|
+
* @return {Promise<Array<Object>>}
|
|
47
|
+
*/
|
|
48
|
+
export function loadAccountClaimableBalances(account) {
|
|
49
|
+
return initHorizon().claimableBalances()
|
|
50
|
+
.claimant(account)
|
|
51
|
+
.limit(100)
|
|
52
|
+
.call()
|
|
53
|
+
.then(r => r.records)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Detect account lock status based on the signers configuration
|
|
58
|
+
* @param {AccountResponse} account - Account info from Horizon
|
|
59
|
+
* @returns {'unlocked'|'locked'|'partially locked'}
|
|
60
|
+
*/
|
|
61
|
+
export function getAccountLockStatus(account) {
|
|
62
|
+
const {high_threshold, med_threshold, low_threshold} = account.thresholds
|
|
63
|
+
|
|
64
|
+
//calculate the sum of all signers weights
|
|
65
|
+
const totalSignersWeight = account.signers.reduce((res, signer) => res + signer.weight, 0)
|
|
66
|
+
|
|
67
|
+
if (totalSignersWeight > 0) {
|
|
68
|
+
//compare it with med_threshold and high_threshold
|
|
69
|
+
if (totalSignersWeight >= med_threshold || totalSignersWeight >= high_threshold) {
|
|
70
|
+
return 'unlocked'
|
|
71
|
+
} else if (totalSignersWeight >= low_threshold) {
|
|
72
|
+
return 'partially locked'
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return 'locked'
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Retrieve balance for a given Horizon account info
|
|
80
|
+
* @param {AccountResponse} account - Account Horizon info
|
|
81
|
+
* @param {{code: String, issuer: String}} asset - Asset or null (for XLM)
|
|
82
|
+
* @returns {{total: Number, available: Number, [asset]: {code: String, issuer: String}}}
|
|
83
|
+
*/
|
|
84
|
+
export function getAccountBalance(account, asset = null) {
|
|
85
|
+
if (!account.balances) return {asset, total: 0, available: 0}
|
|
86
|
+
const assetBalance = account.balances.find(b => (!asset && b.asset_type === 'native') ||
|
|
87
|
+
(b.asset_issuer === asset.issuer && b.asset_code === asset.code)),
|
|
88
|
+
res = {asset, total: 0, available: 0}
|
|
89
|
+
|
|
90
|
+
if (assetBalance) {
|
|
91
|
+
res.total = parseFloat(assetBalance.balance)
|
|
92
|
+
res.available = res.total - parseFloat(assetBalance.selling_liabilities)
|
|
93
|
+
if (!asset) { //for XLM we also need to check reserved amount
|
|
94
|
+
res.available -= (parseInt(account.subentry_count) + 2) * baseReserve
|
|
95
|
+
}
|
|
96
|
+
} else if (asset && asset.issuer === account.address) {
|
|
97
|
+
res.total = res.available = 922337203685
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return res
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import {Horizon} from '@stellar/stellar-sdk'
|
|
2
|
+
|
|
3
|
+
export function initHorizon() {
|
|
4
|
+
return new Horizon.Server(window.horizonOrigin)
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Load all data from the horizon query using next() iteration
|
|
9
|
+
* @param {CallBuilder} query
|
|
10
|
+
* @return {Promise<Object[]>}
|
|
11
|
+
*/
|
|
12
|
+
export async function loadAllHorizonRecords(query) {
|
|
13
|
+
let res = []
|
|
14
|
+
let response = await query.call()
|
|
15
|
+
while (true) {
|
|
16
|
+
const {records = []} = response
|
|
17
|
+
const limitParam = (query.url.query() || '')
|
|
18
|
+
.split('&')
|
|
19
|
+
.find(q => q.indexOf('limit=') === 0)
|
|
20
|
+
const limit = (limitParam && parseInt(limitParam.split('=')[1])) || 10
|
|
21
|
+
if (!records.length) break
|
|
22
|
+
res = res.concat(records)
|
|
23
|
+
if (records.length < limit) break
|
|
24
|
+
response = await response.next()
|
|
25
|
+
}
|
|
26
|
+
return {records: res}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @typedef {Object} ListQueryParams
|
|
31
|
+
* @property {string} [cursor] - Paging cursor.
|
|
32
|
+
* @property [('asc'|'desc'))] [order] - Sort order.
|
|
33
|
+
* @property {number} [limit] - Page size (200 by default).
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Apply list query parameters to horizon query
|
|
38
|
+
* @param {object} query - Horizon query.
|
|
39
|
+
* @param {object} queryParameters - Parameters to apply.
|
|
40
|
+
* @return {object}
|
|
41
|
+
*/
|
|
42
|
+
export function applyListQueryParameters(query, queryParameters) {
|
|
43
|
+
const {cursor, order, limit = 200} = queryParameters || {}
|
|
44
|
+
if (cursor) {
|
|
45
|
+
query.cursor(cursor)
|
|
46
|
+
}
|
|
47
|
+
if (order) {
|
|
48
|
+
query.order(order || 'asc')
|
|
49
|
+
}
|
|
50
|
+
query.limit(limit)
|
|
51
|
+
return query
|
|
52
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import {initHorizon, applyListQueryParameters} from './horizon-client-helpers'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Load ledgers from Horizon
|
|
5
|
+
* @param {ListQueryParams} [queryParams] - Query parameters (optional)
|
|
6
|
+
* @return {Promise<Array<Object>>}
|
|
7
|
+
*/
|
|
8
|
+
export function loadLedgers(queryParams = null) {
|
|
9
|
+
const query = applyListQueryParameters(initHorizon().ledgers(), queryParams)
|
|
10
|
+
return query.call()
|
|
11
|
+
.then(r => r.records)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Load ledger by its sequence
|
|
16
|
+
* @param {Number} sequence - Sequence of the ledger to fetch
|
|
17
|
+
* @return {Object}
|
|
18
|
+
*/
|
|
19
|
+
export function loadLedger(sequence) {
|
|
20
|
+
return initHorizon().ledgers()
|
|
21
|
+
.ledger(sequence)
|
|
22
|
+
.call()
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Stream ledgers from Horizon
|
|
27
|
+
* @param {String} cursor - Cursor to start from
|
|
28
|
+
* @param {Function} onNewLedger - Callback to invoke when new ledger arrives
|
|
29
|
+
* @return {Function}
|
|
30
|
+
*/
|
|
31
|
+
export function streamLedgers(cursor, onNewLedger) {
|
|
32
|
+
return initHorizon().ledgers()
|
|
33
|
+
.order('asc')
|
|
34
|
+
.cursor(cursor || 'now')
|
|
35
|
+
.stream({onmessage: ledger => onNewLedger(ledger)})
|
|
36
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import {AssetDescriptor} from '@stellar-expert/asset-descriptor'
|
|
2
|
+
import {applyListQueryParameters, initHorizon} from './horizon-client-helpers'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Load orderbook for assets.
|
|
6
|
+
* @param {object} selling - Selling asset.
|
|
7
|
+
* @param {object} buying - Buying asset.
|
|
8
|
+
* @param {ListQueryParams} [queryParams] - Query parameters (optional).
|
|
9
|
+
* @return {Promise<Array<object>>}
|
|
10
|
+
*/
|
|
11
|
+
export function loadOrderbook(selling, buying, queryParams = null) {
|
|
12
|
+
const query = applyListQueryParameters(initHorizon().orderbook(AssetDescriptor.parse(selling).toAsset(), AssetDescriptor.parse(buying).toAsset()), queryParams)
|
|
13
|
+
return query.call()
|
|
14
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import {AssetDescriptor} from '@stellar-expert/asset-descriptor'
|
|
2
|
+
import {initHorizon, applyListQueryParameters} from './horizon-client-helpers'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Load offer from Horizon for the particular account
|
|
6
|
+
* @param {AssetDescriptor} baseAsset - Base asset descriptor
|
|
7
|
+
* @param {AssetDescriptor} counterAsset - Counter asset descriptor
|
|
8
|
+
* @param {ListQueryParams} [queryParams] - Query parameters (optional)
|
|
9
|
+
* @return {Promise<Array<Object>>}
|
|
10
|
+
*/
|
|
11
|
+
export function loadMarketTrades(baseAsset, counterAsset, queryParams = null) {
|
|
12
|
+
const q = initHorizon().trades()
|
|
13
|
+
.forAssetPair(baseAsset.toAsset(), counterAsset.toAsset())
|
|
14
|
+
const query = applyListQueryParameters(q, queryParams)
|
|
15
|
+
return query.call()
|
|
16
|
+
.then(r=>r.records)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Stream trades from Horizon
|
|
22
|
+
* @param {String} cursor - Cursor to start from
|
|
23
|
+
* @param {AssetDescriptor} baseAsset - Base asset descriptor
|
|
24
|
+
* @param {AssetDescriptor} counterAsset - Counter asset descriptor
|
|
25
|
+
* @param {Function} onNewTrade - Callback to invoke when new trade arrives
|
|
26
|
+
* @return {Function}
|
|
27
|
+
*/
|
|
28
|
+
export function streamMarketTrades(cursor, baseAsset, counterAsset, onNewTrade) {
|
|
29
|
+
return initHorizon().trades()
|
|
30
|
+
.forAssetPair(baseAsset.toAsset(), counterAsset.toAsset())
|
|
31
|
+
.order('asc')
|
|
32
|
+
.cursor(cursor || 'now')
|
|
33
|
+
.stream({onmessage: op => onNewTrade(op)})
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Stream all trades from Horizon
|
|
39
|
+
* @param {String} cursor - Cursor to start from
|
|
40
|
+
* @param {Function} onNewTrade - Callback to invoke when new trade arrives
|
|
41
|
+
* @return {Function}
|
|
42
|
+
*/
|
|
43
|
+
export function streamTrades(cursor, onNewTrade) {
|
|
44
|
+
return initHorizon().trades()
|
|
45
|
+
.order('asc')
|
|
46
|
+
.cursor(cursor || 'now')
|
|
47
|
+
.limit(200)
|
|
48
|
+
.stream({onmessage: op => onNewTrade(op)})
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
*
|
|
54
|
+
* @param {String|AssetDescriptor|Asset} base - Base asset
|
|
55
|
+
* @param {String|AssetDescriptor|Asset} counter - Counter asset
|
|
56
|
+
* @param {('5m'|'15m'|'1h'|'1d'|'1w')} resolution - Segment duration
|
|
57
|
+
* @param {Number} period - Period in days
|
|
58
|
+
* @param {Number} [limit] - Max number of records to fetch
|
|
59
|
+
* @return {Promise<TradeAggregationRecord>}
|
|
60
|
+
*/
|
|
61
|
+
export function loadTradesAggregation({base, counter, resolution, period, limit = 200}) {
|
|
62
|
+
let res
|
|
63
|
+
switch (resolution) {
|
|
64
|
+
case '5m':
|
|
65
|
+
res = 300000
|
|
66
|
+
break
|
|
67
|
+
case '15m':
|
|
68
|
+
res = 900000
|
|
69
|
+
break
|
|
70
|
+
case '1h':
|
|
71
|
+
res = 3600000
|
|
72
|
+
break
|
|
73
|
+
case '1d':
|
|
74
|
+
res = 86400000
|
|
75
|
+
break
|
|
76
|
+
case '1w':
|
|
77
|
+
res = 604800000
|
|
78
|
+
break
|
|
79
|
+
default:
|
|
80
|
+
throw new Error(`Not supported trades aggregation resolution: ${resolution}`)
|
|
81
|
+
}
|
|
82
|
+
const endTime = new Date().getTime(),
|
|
83
|
+
startTime = endTime - period * 24 * 60 * 60 * 1000
|
|
84
|
+
|
|
85
|
+
return initHorizon()
|
|
86
|
+
.tradeAggregation(AssetDescriptor.parse(base).toAsset(), AssetDescriptor.parse(counter).toAsset(), startTime, endTime, res, 0)
|
|
87
|
+
.limit(limit)
|
|
88
|
+
.call()
|
|
89
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import {initHorizon, applyListQueryParameters} from './horizon-client-helpers'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Load transactions from Horizon
|
|
5
|
+
* @param {ListQueryParams} [queryParams] - Query parameters (optional)
|
|
6
|
+
* @return {Promise<Array<Object>>}
|
|
7
|
+
*/
|
|
8
|
+
export function loadTransactions(queryParams = null) {
|
|
9
|
+
const query = applyListQueryParameters(initHorizon().transactions().includeFailed(queryParams && !!queryParams.includeFailed), queryParams)
|
|
10
|
+
return query.call()
|
|
11
|
+
.then(r => r.records)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Stream operations from Horizon
|
|
16
|
+
* @param {String} cursor - Cursor to start from
|
|
17
|
+
* @param {Function} onNewTx - Callback to invoke when new operation arrives
|
|
18
|
+
* @param {Boolean} includeFailed - Include failed transactions into the stream
|
|
19
|
+
* @return {Function}
|
|
20
|
+
*/
|
|
21
|
+
export function streamTransactions(cursor, onNewTx, includeFailed) {
|
|
22
|
+
return initHorizon().transactions().includeFailed(!!includeFailed)
|
|
23
|
+
.order('asc')
|
|
24
|
+
.cursor(cursor || 'now')
|
|
25
|
+
.stream({onmessage: op => onNewTx(op)})
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Submit transaction to Horizon
|
|
30
|
+
* @param {Transaction} tx
|
|
31
|
+
* @returns {Promise<Horizon.SubmitTransactionResponse>}
|
|
32
|
+
*/
|
|
33
|
+
export function submitTransaction(tx) {
|
|
34
|
+
return initHorizon()
|
|
35
|
+
.submitTransaction(tx)
|
|
36
|
+
}
|
package/index.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import './index.scss'
|
|
2
|
+
|
|
3
|
+
window.explorerFrontendOrigin = window.explorerFrontendOrigin || 'https://stellar.expert'
|
|
4
|
+
window.explorerApiOrigin = window.explorerApiOrigin || 'https://api.stellar.expert'
|
|
5
|
+
window.horizonOrigin = window.horizonOrigin || 'https://horizon.stellar.org'
|
|
6
|
+
|
|
7
|
+
//modules
|
|
8
|
+
export * from './module/dynamic-module'
|
|
9
|
+
//state management and utils
|
|
10
|
+
export * from './state/state-hooks'
|
|
11
|
+
export * from './state/on-screen-hooks'
|
|
12
|
+
export * from './state/stellar-network-hooks'
|
|
13
|
+
export * from './state/screen-orientation-hooks'
|
|
14
|
+
export * from './state/page-visibility-helpers'
|
|
15
|
+
export * from './state/theme'
|
|
16
|
+
export * from './meta/page-meta-tags'
|
|
17
|
+
//explorer API bindings
|
|
18
|
+
export * from './api/explorer-api-hooks'
|
|
19
|
+
export * from './api/explorer-api-paginated-list-hooks'
|
|
20
|
+
export * from './api/explorer-tx-api'
|
|
21
|
+
//Horizon API binding and utils
|
|
22
|
+
export * from './stellar/ledger-generic-id'
|
|
23
|
+
export * from './horizon/horizon-client-helpers'
|
|
24
|
+
export * from './horizon/horizon-ledger-helpers'
|
|
25
|
+
export * from './horizon/horizon-transaction-helpers'
|
|
26
|
+
export * from './horizon/horizon-account-helpers'
|
|
27
|
+
export * from './horizon/horizon-orderbook-helpers'
|
|
28
|
+
export * from './horizon/horizon-trades-helper'
|
|
29
|
+
//basic UI controls
|
|
30
|
+
export * from './controls/button'
|
|
31
|
+
export * from './controls/button-group'
|
|
32
|
+
export * from './controls/info-tooltip'
|
|
33
|
+
export * from './controls/tooltip'
|
|
34
|
+
export * from './controls/update-highlighter'
|
|
35
|
+
export * from './controls/tabs'
|
|
36
|
+
export * from './controls/dropdown'
|
|
37
|
+
export * from './controls/code-block'
|
|
38
|
+
export * from './controls/slider'
|
|
39
|
+
export * from './controls/external-link'
|
|
40
|
+
export * from './toast/toast-notifications-block'
|
|
41
|
+
export * from './errors/error-boundary'
|
|
42
|
+
//interaction
|
|
43
|
+
export * from './interaction/autofocus'
|
|
44
|
+
export * from './interaction/block-select'
|
|
45
|
+
export * from './interaction/copy-to-clipboard'
|
|
46
|
+
export * from './interaction/spoiler'
|
|
47
|
+
export * from './interaction/theme-selector'
|
|
48
|
+
export * from './interaction/inline-progress'
|
|
49
|
+
export * from './interaction/responsive'
|
|
50
|
+
export * from './interaction/qr-code'
|
|
51
|
+
//date components
|
|
52
|
+
export * from './date/utc-timestamp'
|
|
53
|
+
export * from './date/elapsed-time'
|
|
54
|
+
export * from './date/date-selector'
|
|
55
|
+
//ledger-entries-related components
|
|
56
|
+
export * from './ledger/ledger-entry-link'
|
|
57
|
+
export * from './ledger/ledger-entry-href-formatter'
|
|
58
|
+
//account-related components
|
|
59
|
+
export * from './account/identicon'
|
|
60
|
+
export * from './account/account-address'
|
|
61
|
+
export * from './account/signer-key'
|
|
62
|
+
export * from './account/available-balance'
|
|
63
|
+
//asset-related components
|
|
64
|
+
export * from './asset/asset-link'
|
|
65
|
+
export * from './asset/asset-issuer'
|
|
66
|
+
export * from './asset/asset-icon'
|
|
67
|
+
export * from './asset/asset-selector'
|
|
68
|
+
export * from './asset/amount'
|
|
69
|
+
export * from './asset/asset-meta-hooks'
|
|
70
|
+
export * from './asset/asset-list-hooks'
|
|
71
|
+
//claimable-balance-related components
|
|
72
|
+
export * from './claimable-balance/claimable-balance-claimants'
|
|
73
|
+
//DEX-related components
|
|
74
|
+
export * from './dex/price-dynamic'
|
|
75
|
+
//directory-related components
|
|
76
|
+
export * from './directory/directory-hooks'
|
|
77
|
+
//transaction/operation/effects components
|
|
78
|
+
export * from './tx/tx-operations-list'
|
|
79
|
+
export * from './tx/parser/tx-details-parser'
|
|
80
|
+
export * from './tx/tx-list-hooks'
|
|
81
|
+
export * from './effect/effect-description'
|
|
82
|
+
//Stellar-specific utils
|
|
83
|
+
export * from './stellar/key-type'
|
|
84
|
+
export * from './stellar/signature-hint-utils'
|
|
85
|
+
export * from './contract/wasm-section-parser'
|
package/index.scss
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
@import "basic-styles/variables";
|
|
2
|
+
@import "basic-styles/themes";
|
|
3
|
+
@import "basic-styles/scrollbars";
|
|
4
|
+
@import "basic-styles/base";
|
|
5
|
+
@import "fonts/font";
|
|
6
|
+
@import "fonts/icons/icons-embedded";
|
|
7
|
+
@import "controls/active-icon";
|
|
8
|
+
@import "basic-styles/link";
|
|
9
|
+
@import "basic-styles/grid";
|
|
10
|
+
@import "basic-styles/loader";
|
|
11
|
+
@import "basic-styles/typography";
|
|
12
|
+
@import "basic-styles/spacing";
|
|
13
|
+
@import "basic-styles/form";
|
|
14
|
+
@import "basic-styles/lists";
|
|
15
|
+
@import "basic-styles/table";
|
|
16
|
+
@import "basic-styles/responsive";
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import PropTypes from 'prop-types'
|
|
3
|
+
import cn from 'classnames'
|
|
4
|
+
import './block-select.scss'
|
|
5
|
+
|
|
6
|
+
function select(target) {
|
|
7
|
+
//handle textarea and inputs
|
|
8
|
+
if (target.nodeName.match(/^(INPUT|TEXTAREA)$/i)) {
|
|
9
|
+
target.focus()
|
|
10
|
+
target.select()
|
|
11
|
+
return
|
|
12
|
+
}
|
|
13
|
+
//handle selection inside text elements
|
|
14
|
+
const range = document.createRange()
|
|
15
|
+
range.selectNodeContents(target)
|
|
16
|
+
const sel = window.getSelection()
|
|
17
|
+
if (typeof sel.setBaseAndExtent === 'function') {
|
|
18
|
+
// Safari
|
|
19
|
+
sel.setBaseAndExtent(target, 0, target, 1)
|
|
20
|
+
}
|
|
21
|
+
sel.removeAllRanges()
|
|
22
|
+
sel.addRange(range)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const BlockSelect = React.memo(function BlockSelect({as = 'span', children, title, className, wrap, inline, style, ...op}) {
|
|
26
|
+
const props = {
|
|
27
|
+
className: cn('block-select', className),
|
|
28
|
+
onFocus: e => select(e.target),
|
|
29
|
+
tabIndex: '-1',
|
|
30
|
+
style,
|
|
31
|
+
title,
|
|
32
|
+
...op
|
|
33
|
+
}
|
|
34
|
+
if (wrap) {
|
|
35
|
+
props.style = {...props.style, whiteSpace: 'normal', overflow: 'visible'}
|
|
36
|
+
}
|
|
37
|
+
if (wrap === false) {
|
|
38
|
+
props.style = {...props.style, whiteSpace: 'nowrap', overflow: 'hidden'}
|
|
39
|
+
}
|
|
40
|
+
if (inline) {
|
|
41
|
+
props.style = {...props.style, display: 'inline'}
|
|
42
|
+
}
|
|
43
|
+
return React.createElement(as, props, children)
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
BlockSelect.propTypes = {
|
|
47
|
+
children: PropTypes.any.isRequired,
|
|
48
|
+
title: PropTypes.string,
|
|
49
|
+
className: PropTypes.string,
|
|
50
|
+
style: PropTypes.object,
|
|
51
|
+
wrap: PropTypes.bool,
|
|
52
|
+
inline: PropTypes.bool,
|
|
53
|
+
as: PropTypes.string
|
|
54
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
@use "../basic-styles/variables";
|
|
2
|
+
|
|
3
|
+
.block-select {
|
|
4
|
+
display: inline-block;
|
|
5
|
+
width: auto;
|
|
6
|
+
overflow: auto;
|
|
7
|
+
text-overflow: ellipsis;
|
|
8
|
+
vertical-align: bottom;
|
|
9
|
+
|
|
10
|
+
&:focus {
|
|
11
|
+
outline: none;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
&textarea {
|
|
15
|
+
width: 100%;
|
|
16
|
+
height: 7em;
|
|
17
|
+
margin: 0;
|
|
18
|
+
resize: vertical;
|
|
19
|
+
|
|
20
|
+
&.code {
|
|
21
|
+
font-family: $font-face-mono;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import PropTypes from 'prop-types'
|
|
3
|
+
import {CopyToClipboard as Copy} from 'react-copy-to-clipboard'
|
|
4
|
+
|
|
5
|
+
export const CopyToClipboard = React.memo(function CopyToClipboard({text, children, title}) {
|
|
6
|
+
return <Copy text={text}>
|
|
7
|
+
{children ? children : <a href="#" className="icon-copy active-icon" title={title}/>}
|
|
8
|
+
</Copy>
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
CopyToClipboard.defaultProps = {
|
|
12
|
+
title: 'Copy to clipboard'
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
CopyToClipboard.propTypes = {
|
|
16
|
+
text: PropTypes.string.isRequired,
|
|
17
|
+
title: PropTypes.string,
|
|
18
|
+
children: PropTypes.any
|
|
19
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import React, {useEffect, useRef, useState} from 'react'
|
|
2
|
+
import PropTypes from 'prop-types'
|
|
3
|
+
|
|
4
|
+
export const InlineProgress = React.memo(function InlineProgress({dots = 5}) {
|
|
5
|
+
const [progress, setProgress] = useState(0),
|
|
6
|
+
intervalRef = useRef(0)
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
intervalRef.current = setInterval(() => setProgress(p => ++p), 500)
|
|
9
|
+
return () => clearInterval(intervalRef.current)
|
|
10
|
+
}, [])
|
|
11
|
+
return <span>{'.'.repeat(progress % (dots + 1))}</span>
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
InlineProgress.propTypes = {
|
|
15
|
+
dots: PropTypes.number
|
|
16
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import React, {useMemo, useRef} from 'react'
|
|
2
|
+
import QR from 'qrcode.react'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* QrCode renderer
|
|
6
|
+
* @param {String} value - Value to encode
|
|
7
|
+
* @param {String} [caption] - Additional caption under QR code
|
|
8
|
+
* @param {Number} [size] - Width|height fo the rendered QR code image
|
|
9
|
+
* @param {String} [embeddedImage] - Optional logo to render in the center of QR code
|
|
10
|
+
* @param {Number} [embeddedSize] - Embeded logo size (by default 10% of QR code size)
|
|
11
|
+
* @return {JSX.Element}
|
|
12
|
+
*/
|
|
13
|
+
export const QrCode = React.memo(function QrCode({value, caption, size = 320, embeddedImage, embeddedSize}) {
|
|
14
|
+
const foreground = useMemo(() => getComputedStyle(document.documentElement).getPropertyValue('--color-primary'))
|
|
15
|
+
const containerRef = useRef()
|
|
16
|
+
return <div className="text-center" ref={containerRef}>
|
|
17
|
+
<QR value={value} size={256} level="Q" includeMargin imageSettings={embedImage(embeddedImage, embeddedSize, size)}
|
|
18
|
+
fgColor={foreground} style={{width: size + 'px', height: size + 'px', display: 'block', margin: 'auto'}}/>
|
|
19
|
+
{!!caption && <div className="text-small dimmed condensed word-break">{caption}</div>}
|
|
20
|
+
</div>
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
function embedImage(src, size, qrSize) {
|
|
24
|
+
if (!src)
|
|
25
|
+
return undefined
|
|
26
|
+
if (!size) {
|
|
27
|
+
size = qrSize * 0.1
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
src,
|
|
31
|
+
height: size,
|
|
32
|
+
width: size,
|
|
33
|
+
excavate: true
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function download(container, caption) {
|
|
38
|
+
const canvas = container.querySelector('canvas')
|
|
39
|
+
const link = document.createElement('a')
|
|
40
|
+
link.href = canvas.toDataURL()
|
|
41
|
+
link.download = caption ? caption.replace(/\W+/g, '-') + '-qr.png' : `qr${new Date().getTime()}.png`
|
|
42
|
+
document.body.appendChild(link)
|
|
43
|
+
link.click()
|
|
44
|
+
document.body.removeChild(link)
|
|
45
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import {useState, useEffect} from 'react'
|
|
2
|
+
import {throttle} from 'throttle-debounce'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Measures window client width
|
|
6
|
+
* @return {Number}
|
|
7
|
+
*/
|
|
8
|
+
export function useWindowWidth() {
|
|
9
|
+
const [width, setWidth] = useState(window.innerWidth)
|
|
10
|
+
|
|
11
|
+
const onResize = throttle(100, function () {
|
|
12
|
+
setWidth(window.innerWidth)
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
window.addEventListener('resize', onResize)
|
|
17
|
+
return () => window.removeEventListener('resize', onResize)
|
|
18
|
+
}, [])
|
|
19
|
+
|
|
20
|
+
return width
|
|
21
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import React, {useCallback, useEffect, useState} from 'react'
|
|
2
|
+
import PropTypes from 'prop-types'
|
|
3
|
+
import cn from 'classnames'
|
|
4
|
+
import './spoiler.scss'
|
|
5
|
+
|
|
6
|
+
export const Spoiler = React.memo(function Spoiler({expanded, showMore = 'Show more', showLess = 'Show less', onChange, className, micro, style, active, children}) {
|
|
7
|
+
const [expandedState, setExpandedState] = useState(expanded || false)
|
|
8
|
+
useEffect(() => {
|
|
9
|
+
setExpandedState(expanded)
|
|
10
|
+
}, [expanded])
|
|
11
|
+
|
|
12
|
+
const toggle = useCallback(() => {
|
|
13
|
+
setExpandedState(prevState => {
|
|
14
|
+
const newState = !prevState
|
|
15
|
+
setExpandedState(newState)
|
|
16
|
+
onChange && onChange({expanded: newState})
|
|
17
|
+
})
|
|
18
|
+
}, [onChange])
|
|
19
|
+
|
|
20
|
+
const text = expandedState ? showLess : showMore
|
|
21
|
+
return <>
|
|
22
|
+
<span className={cn('spoiler', className)}>
|
|
23
|
+
<a href="#" className={!active ? 'dimmed' : undefined} title={micro ? text : undefined} style={style} onClick={toggle}>
|
|
24
|
+
{!micro && <span className="spoiler-text">{text}</span>}
|
|
25
|
+
<i className={`icon ${expandedState ? 'icon-less' : 'icon-more'}`}/>
|
|
26
|
+
</a>
|
|
27
|
+
</span>
|
|
28
|
+
{expandedState ? children : null}
|
|
29
|
+
</>
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
Spoiler.propTypes = {
|
|
33
|
+
expanded: PropTypes.bool,
|
|
34
|
+
micro: PropTypes.bool,
|
|
35
|
+
onChange: PropTypes.func,
|
|
36
|
+
showMore: PropTypes.string,
|
|
37
|
+
showLess: PropTypes.string,
|
|
38
|
+
active: PropTypes.bool,
|
|
39
|
+
style: PropTypes.object
|
|
40
|
+
}
|