@stellar-expert/ui-framework 1.16.8 → 1.19.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/README.md +1168 -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/filter/editors/account-filter-view.js +20 -0
- package/filter/editors/asset-filter-view.js +9 -0
- package/filter/editors/timestamp-filter-view.js +34 -0
- package/filter/editors/type-filter-view.js +148 -0
- package/filter/filter-editors.js +102 -0
- package/filter/filter-view.js +218 -0
- package/filter/filter.scss +57 -0
- 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 +1271 -0
- package/index.js +95 -93
- 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 +71 -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/meta/page-meta-tags.js
CHANGED
|
@@ -1,239 +1,244 @@
|
|
|
1
|
-
import {useEffect} from 'react'
|
|
2
|
-
import isEqual from 'react-fast-compare'
|
|
3
|
-
|
|
4
|
-
const metaProps = {
|
|
5
|
-
serviceTitle: '',
|
|
6
|
-
description: '',
|
|
7
|
-
image: '',
|
|
8
|
-
imageEndpoint: '',
|
|
9
|
-
origin: '',
|
|
10
|
-
domain: ''
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
setOrigin(window.location.origin)
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Initialize default meta properties for the application
|
|
17
|
-
* @param {{serviceTitle:
|
|
18
|
-
*/
|
|
19
|
-
export function initMeta(appMetaProps) {
|
|
20
|
-
const {origin, ...props} = appMetaProps
|
|
21
|
-
Object.assign(metaProps, props)
|
|
22
|
-
if (origin) {
|
|
23
|
-
setOrigin(origin)
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function setOrigin(origin) {
|
|
28
|
-
metaProps.origin = origin
|
|
29
|
-
metaProps.domain = origin.replace(/https?:\/\//, '')
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function formatPageTitle(title) {
|
|
33
|
-
if (!title)
|
|
34
|
-
return metaProps.serviceTitle
|
|
35
|
-
if (title.includes(metaProps.serviceTitle))
|
|
36
|
-
return title
|
|
37
|
-
return `${title} ${metaProps.serviceTitle}`
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function generateCanonicalLink(params, canonicalUrl) {
|
|
41
|
-
return {
|
|
42
|
-
tag: 'link',
|
|
43
|
-
locator: 'rel',
|
|
44
|
-
tags: [
|
|
45
|
-
{name: 'canonical', content: canonicalUrl, attribute: 'href'}
|
|
46
|
-
]
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function generateDescriptionMeta({description}) {
|
|
51
|
-
return {
|
|
52
|
-
locator: 'name',
|
|
53
|
-
tags: [
|
|
54
|
-
{name: 'description', content: description || metaProps.description}
|
|
55
|
-
]
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function generateOpenGraphMeta({description, title, image}, canonicalUrl) {
|
|
60
|
-
//imageEndpoint
|
|
61
|
-
return {
|
|
62
|
-
locator: 'property',
|
|
63
|
-
tags: [
|
|
64
|
-
{name: 'og:title', content: formatPageTitle(title)},
|
|
65
|
-
{name: 'og:url', content: canonicalUrl},
|
|
66
|
-
{name: 'og:site_name', content: formatPageTitle(metaProps.serviceTitle)},
|
|
67
|
-
{name: 'og:description', content: description || metaProps.description},
|
|
68
|
-
{name: 'og:type', content: 'website'},
|
|
69
|
-
{name: 'og:image:width', content: 1200},
|
|
70
|
-
{name: 'og:image:height', content: 630},
|
|
71
|
-
{name: 'og:image', content: formatPageImage(image, canonicalUrl)}
|
|
72
|
-
]
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function generateItemPropSchema({description, title, image}) {
|
|
77
|
-
return {
|
|
78
|
-
locator: 'itemprop',
|
|
79
|
-
tags: [
|
|
80
|
-
{name: 'name', content: title},
|
|
81
|
-
{name: 'description', content: description || metaProps.description},
|
|
82
|
-
{name: 'image', content: image || metaProps.image}
|
|
83
|
-
]
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
function generateLdJsonSchema({title, description, image}, canonicalUrl) {
|
|
88
|
-
return {
|
|
89
|
-
tag: 'script',
|
|
90
|
-
locator: 'type',
|
|
91
|
-
tags: [
|
|
92
|
-
{
|
|
93
|
-
name: 'application/ld+json',
|
|
94
|
-
content: JSON.stringify({
|
|
95
|
-
'@context': 'http://schema.org',
|
|
96
|
-
'@type': 'WebPage',
|
|
97
|
-
name: formatPageTitle(title),
|
|
98
|
-
description: description || metaProps.description,
|
|
99
|
-
url: canonicalUrl,
|
|
100
|
-
thumbnailUrl: formatPageImage(image, canonicalUrl),
|
|
101
|
-
publisher: {
|
|
102
|
-
'@type': 'ProfilePage',
|
|
103
|
-
name: metaProps.serviceTitle
|
|
104
|
-
}
|
|
105
|
-
})
|
|
106
|
-
}
|
|
107
|
-
]
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
*
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
tag
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
*
|
|
173
|
-
|
|
174
|
-
|
|
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
|
-
* @property {
|
|
237
|
-
|
|
238
|
-
|
|
1
|
+
import {useEffect} from 'react'
|
|
2
|
+
import isEqual from 'react-fast-compare'
|
|
3
|
+
|
|
4
|
+
const metaProps = {
|
|
5
|
+
serviceTitle: '',
|
|
6
|
+
description: '',
|
|
7
|
+
image: '',
|
|
8
|
+
imageEndpoint: '',
|
|
9
|
+
origin: '',
|
|
10
|
+
domain: ''
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
setOrigin(window.location.origin)
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Initialize default meta properties for the application
|
|
17
|
+
* @param {{serviceTitle: string, description: string, origin: string, [image]: string, [imageEndpoint]: string}} appMetaProps
|
|
18
|
+
*/
|
|
19
|
+
export function initMeta(appMetaProps) {
|
|
20
|
+
const {origin, ...props} = appMetaProps
|
|
21
|
+
Object.assign(metaProps, props)
|
|
22
|
+
if (origin) {
|
|
23
|
+
setOrigin(origin)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function setOrigin(origin) {
|
|
28
|
+
metaProps.origin = origin
|
|
29
|
+
metaProps.domain = origin.replace(/https?:\/\//, '')
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function formatPageTitle(title) {
|
|
33
|
+
if (!title)
|
|
34
|
+
return metaProps.serviceTitle
|
|
35
|
+
if (title.includes(metaProps.serviceTitle))
|
|
36
|
+
return title
|
|
37
|
+
return `${title} ${metaProps.serviceTitle}`
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function generateCanonicalLink(params, canonicalUrl) {
|
|
41
|
+
return {
|
|
42
|
+
tag: 'link',
|
|
43
|
+
locator: 'rel',
|
|
44
|
+
tags: [
|
|
45
|
+
{name: 'canonical', content: canonicalUrl, attribute: 'href'}
|
|
46
|
+
]
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function generateDescriptionMeta({description}) {
|
|
51
|
+
return {
|
|
52
|
+
locator: 'name',
|
|
53
|
+
tags: [
|
|
54
|
+
{name: 'description', content: description || metaProps.description}
|
|
55
|
+
]
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function generateOpenGraphMeta({description, title, image}, canonicalUrl) {
|
|
60
|
+
//imageEndpoint
|
|
61
|
+
return {
|
|
62
|
+
locator: 'property',
|
|
63
|
+
tags: [
|
|
64
|
+
{name: 'og:title', content: formatPageTitle(title)},
|
|
65
|
+
{name: 'og:url', content: canonicalUrl},
|
|
66
|
+
{name: 'og:site_name', content: formatPageTitle(metaProps.serviceTitle)},
|
|
67
|
+
{name: 'og:description', content: description || metaProps.description},
|
|
68
|
+
{name: 'og:type', content: 'website'},
|
|
69
|
+
{name: 'og:image:width', content: 1200},
|
|
70
|
+
{name: 'og:image:height', content: 630},
|
|
71
|
+
{name: 'og:image', content: formatPageImage(image, canonicalUrl)}
|
|
72
|
+
]
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function generateItemPropSchema({description, title, image}) {
|
|
77
|
+
return {
|
|
78
|
+
locator: 'itemprop',
|
|
79
|
+
tags: [
|
|
80
|
+
{name: 'name', content: title},
|
|
81
|
+
{name: 'description', content: description || metaProps.description},
|
|
82
|
+
{name: 'image', content: image || metaProps.image}
|
|
83
|
+
]
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function generateLdJsonSchema({title, description, image}, canonicalUrl) {
|
|
88
|
+
return {
|
|
89
|
+
tag: 'script',
|
|
90
|
+
locator: 'type',
|
|
91
|
+
tags: [
|
|
92
|
+
{
|
|
93
|
+
name: 'application/ld+json',
|
|
94
|
+
content: JSON.stringify({
|
|
95
|
+
'@context': 'http://schema.org',
|
|
96
|
+
'@type': 'WebPage',
|
|
97
|
+
name: formatPageTitle(title),
|
|
98
|
+
description: description || metaProps.description,
|
|
99
|
+
url: canonicalUrl,
|
|
100
|
+
thumbnailUrl: formatPageImage(image, canonicalUrl),
|
|
101
|
+
publisher: {
|
|
102
|
+
'@type': 'ProfilePage',
|
|
103
|
+
name: metaProps.serviceTitle
|
|
104
|
+
}
|
|
105
|
+
})
|
|
106
|
+
}
|
|
107
|
+
]
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Replace or create HTML meta tags in the document head
|
|
113
|
+
* @param {MetaTagReplacement} replacement
|
|
114
|
+
*/
|
|
115
|
+
function replaceMetaTags(replacement) {
|
|
116
|
+
const {tag, tags, locator} = replacement
|
|
117
|
+
const selector = tag || 'meta'
|
|
118
|
+
tags.forEach(tagSettings => {
|
|
119
|
+
let tag = document.querySelector(`${selector}[${locator}="${tagSettings.name}"]`)
|
|
120
|
+
if (!tag) {
|
|
121
|
+
tag = createTag(selector)
|
|
122
|
+
tag.setAttribute(locator, tagSettings.name)
|
|
123
|
+
}
|
|
124
|
+
if (selector === 'meta') {
|
|
125
|
+
tag.content = tagSettings.content
|
|
126
|
+
} else if (tagSettings.attribute) {
|
|
127
|
+
tag[tagSettings.attribute] = tagSettings.content
|
|
128
|
+
} else {
|
|
129
|
+
tag.innerText = tagSettings.content
|
|
130
|
+
}
|
|
131
|
+
})
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function createTag(tagName, props) {
|
|
135
|
+
let tag = document.createElement(tagName)
|
|
136
|
+
for (let key in props) {
|
|
137
|
+
tag[key] = props[key]
|
|
138
|
+
}
|
|
139
|
+
document.head.appendChild(tag)
|
|
140
|
+
return tag
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function removeTag(selector) {
|
|
144
|
+
let tag = document.querySelector(selector)
|
|
145
|
+
if (tag) {
|
|
146
|
+
tag.parentElement.removeChild(tag)
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function formatCanonicalUrl() {
|
|
151
|
+
return metaProps.origin + window.location.pathname// + location.search
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function formatPageImage(image, canonicalUrl) {
|
|
155
|
+
if (image)
|
|
156
|
+
return image
|
|
157
|
+
if (metaProps.imageEndpoint)
|
|
158
|
+
return metaProps.imageEndpoint + canonicalUrl.replace(metaProps.origin, '')
|
|
159
|
+
return metaProps.image
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
let pageMeta = {}
|
|
163
|
+
|
|
164
|
+
const tagReplacerPipeline = [
|
|
165
|
+
generateCanonicalLink,
|
|
166
|
+
generateDescriptionMeta,
|
|
167
|
+
generateOpenGraphMeta,
|
|
168
|
+
generateLdJsonSchema
|
|
169
|
+
] // generateItemPropSchema
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Update page metadata tags
|
|
173
|
+
* @param {PageMeta} meta - Page metadata
|
|
174
|
+
*/
|
|
175
|
+
function setPageMetadata(meta) {
|
|
176
|
+
if (isEqual(pageMeta, meta))
|
|
177
|
+
return
|
|
178
|
+
//generate canonical URL
|
|
179
|
+
const canonicalUrl = formatCanonicalUrl()
|
|
180
|
+
document.title = formatPageTitle(meta.title)
|
|
181
|
+
for (const replacer of tagReplacerPipeline) {
|
|
182
|
+
replaceMetaTags(replacer(meta, canonicalUrl))
|
|
183
|
+
}
|
|
184
|
+
if (meta.customMeta) {
|
|
185
|
+
replaceMetaTags(meta.customMeta)
|
|
186
|
+
}
|
|
187
|
+
pageMeta = meta
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Reset page metadata tags to their default values
|
|
192
|
+
* @param {PageMeta} meta - Page metadata
|
|
193
|
+
*/
|
|
194
|
+
function resetPageMetadata(meta) {
|
|
195
|
+
setPageMetadata({
|
|
196
|
+
title: metaProps.serviceTitle,
|
|
197
|
+
description: metaProps.description,
|
|
198
|
+
image: metaProps.image
|
|
199
|
+
})
|
|
200
|
+
//TODO: add logic to cleanup custom page meta tags on page unload
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* React hook for setting page metadata
|
|
205
|
+
* @param {PageMeta} meta - Page metadata
|
|
206
|
+
*/
|
|
207
|
+
export function usePageMetadata(meta) {
|
|
208
|
+
useEffect(() => {
|
|
209
|
+
setPageMetadata(meta)
|
|
210
|
+
return () => resetPageMetadata(meta)
|
|
211
|
+
}, [JSON.stringify(meta), [formatCanonicalUrl()]])
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Set or remove the robots "noindex,nofollow" meta tag
|
|
216
|
+
* @param {boolean} noIndex - Whether to add (true) or remove (false) the noindex directive
|
|
217
|
+
*/
|
|
218
|
+
export function setPageNoIndex(noIndex) {
|
|
219
|
+
if (!noIndex) {
|
|
220
|
+
removeTag('meta[name=robots]')
|
|
221
|
+
} else {
|
|
222
|
+
replaceMetaTags({
|
|
223
|
+
locator: 'name',
|
|
224
|
+
tags: [
|
|
225
|
+
{name: 'robots', content: 'noindex,nofollow'}
|
|
226
|
+
]
|
|
227
|
+
})
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* @typedef {Object} PageMeta
|
|
233
|
+
* @property {string} title - Page title
|
|
234
|
+
* @property {string} description - Contents description
|
|
235
|
+
* @property {string} [image] - Page image url
|
|
236
|
+
* @property {MetaTagReplacement} [customMeta] - Custom metadata tags
|
|
237
|
+
*/
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* @typedef {Object} MetaTagReplacement
|
|
241
|
+
* @property {string} [tag] - Tag name to search for ("meta" by default)
|
|
242
|
+
* @property {string} locator - Tag attribute to match
|
|
243
|
+
* @property {{name: string, content: string, [attribute]: string}[]} tags - Tag properties to set
|
|
239
244
|
*/
|
package/module/dynamic-module.js
CHANGED
|
@@ -1,48 +1,48 @@
|
|
|
1
|
-
import React, {useEffect, useState} from 'react'
|
|
2
|
-
import PropTypes from 'prop-types'
|
|
3
|
-
import {withErrorBoundary} from '../errors/error-boundary'
|
|
4
|
-
|
|
5
|
-
const loadedModules = new Map()
|
|
6
|
-
|
|
7
|
-
export const DynamicModule = withErrorBoundary(
|
|
8
|
-
/**
|
|
9
|
-
* Dynamically loadable module
|
|
10
|
-
* @param {
|
|
11
|
-
* @param {
|
|
12
|
-
* @param {*} [otherProps]
|
|
13
|
-
* @constructor
|
|
14
|
-
*/
|
|
15
|
-
function DynamicModule({load, module, ...otherProps}) {
|
|
16
|
-
const [error, setError] = useState(undefined)
|
|
17
|
-
const key = module || load
|
|
18
|
-
|
|
19
|
-
useEffect(() => {
|
|
20
|
-
if (loadedModules.get(key))
|
|
21
|
-
return //skip if module is already loaded
|
|
22
|
-
load()
|
|
23
|
-
.then(dynamicModule => {
|
|
24
|
-
//use default export for ES modules or top-level export for CJS
|
|
25
|
-
if (dynamicModule.__esModule) {
|
|
26
|
-
dynamicModule = dynamicModule.default
|
|
27
|
-
}
|
|
28
|
-
//add to cache
|
|
29
|
-
loadedModules.set(key, dynamicModule)
|
|
30
|
-
setError(null)
|
|
31
|
-
})
|
|
32
|
-
.catch(error => {
|
|
33
|
-
error.message = 'Failed to load module. ' + error.message
|
|
34
|
-
setError(error)
|
|
35
|
-
})
|
|
36
|
-
}, [module || load])
|
|
37
|
-
if (error)
|
|
38
|
-
throw error
|
|
39
|
-
const resolvedModule = loadedModules.get(key)
|
|
40
|
-
if (!resolvedModule)
|
|
41
|
-
return <div className="loader"/>
|
|
42
|
-
return React.createElement(resolvedModule, otherProps)
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
DynamicModule.propTypes = {
|
|
46
|
-
load: PropTypes.func.isRequired,
|
|
47
|
-
module: PropTypes.string
|
|
1
|
+
import React, {useEffect, useState} from 'react'
|
|
2
|
+
import PropTypes from 'prop-types'
|
|
3
|
+
import {withErrorBoundary} from '../errors/error-boundary'
|
|
4
|
+
|
|
5
|
+
const loadedModules = new Map()
|
|
6
|
+
|
|
7
|
+
export const DynamicModule = withErrorBoundary(
|
|
8
|
+
/**
|
|
9
|
+
* Dynamically loadable module
|
|
10
|
+
* @param {function} load - Dynamic load function, e.g. ()=>import('./dynamic_module_import')
|
|
11
|
+
* @param {string} module - Module unique name for aching purpose
|
|
12
|
+
* @param {*} [otherProps]
|
|
13
|
+
* @constructor
|
|
14
|
+
*/
|
|
15
|
+
function DynamicModule({load, module, ...otherProps}) {
|
|
16
|
+
const [error, setError] = useState(undefined)
|
|
17
|
+
const key = module || load
|
|
18
|
+
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
if (loadedModules.get(key))
|
|
21
|
+
return //skip if module is already loaded
|
|
22
|
+
load()
|
|
23
|
+
.then(dynamicModule => {
|
|
24
|
+
//use default export for ES modules or top-level export for CJS
|
|
25
|
+
if (dynamicModule.__esModule) {
|
|
26
|
+
dynamicModule = dynamicModule.default
|
|
27
|
+
}
|
|
28
|
+
//add to cache
|
|
29
|
+
loadedModules.set(key, dynamicModule)
|
|
30
|
+
setError(null)
|
|
31
|
+
})
|
|
32
|
+
.catch(error => {
|
|
33
|
+
error.message = 'Failed to load module. ' + error.message
|
|
34
|
+
setError(error)
|
|
35
|
+
})
|
|
36
|
+
}, [module || load])
|
|
37
|
+
if (error)
|
|
38
|
+
throw error
|
|
39
|
+
const resolvedModule = loadedModules.get(key)
|
|
40
|
+
if (!resolvedModule)
|
|
41
|
+
return <div className="loader"/>
|
|
42
|
+
return React.createElement(resolvedModule, otherProps)
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
DynamicModule.propTypes = {
|
|
46
|
+
load: PropTypes.func.isRequired,
|
|
47
|
+
module: PropTypes.string
|
|
48
48
|
}
|