@lukso/transaction-view-core 0.5.0-dev.a98e9bc → 0.5.0-dev.f500300
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/.turbo/turbo-build.log +15 -15
- package/dist/index.cjs +85 -54
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +85 -54
- package/dist/index.js.map +1 -1
- package/dist/shared/index.cjs +5 -1
- package/dist/shared/index.cjs.map +1 -1
- package/dist/shared/index.d.cts +4 -4
- package/dist/shared/index.d.ts +4 -4
- package/dist/shared/index.js +5 -1
- package/dist/shared/index.js.map +1 -1
- package/package.json +3 -3
- package/src/components/asset-card.ts +61 -58
- package/src/shared/image-list.ts +0 -1
- package/src/shared/value-view.ts +5 -19
- package/src/utils.test.ts +47 -0
- package/src/utils.ts +82 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lukso/transaction-view-core",
|
|
3
|
-
"version": "0.5.0-dev.
|
|
3
|
+
"version": "0.5.0-dev.f500300",
|
|
4
4
|
"description": "Core components and hooks for LUKSO transaction display",
|
|
5
5
|
"main": "./dist/index.cjs",
|
|
6
6
|
"module": "./dist/index.js",
|
|
@@ -35,8 +35,8 @@
|
|
|
35
35
|
"@preact/signals-core": "^1.12.1",
|
|
36
36
|
"ethereum-blockies-base64": "^1.0.2",
|
|
37
37
|
"lit": "^3.3.1",
|
|
38
|
-
"@lukso/transaction-decoder": "1.3.0-dev.
|
|
39
|
-
"@lukso/transaction-view-headless": "0.4.0-dev.
|
|
38
|
+
"@lukso/transaction-decoder": "1.3.0-dev.f500300",
|
|
39
|
+
"@lukso/transaction-view-headless": "0.4.0-dev.f500300"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@tailwindcss/typography": "^0.5.19",
|
|
@@ -16,21 +16,7 @@ import {
|
|
|
16
16
|
} from '@lukso/transaction-view-headless'
|
|
17
17
|
import { safeCustomElement } from '../shared/safe-custom-element.js'
|
|
18
18
|
import { CoreLitElement } from '../styles'
|
|
19
|
-
|
|
20
|
-
// Value formatting utilities
|
|
21
|
-
function formatUnits(value: bigint, decimals = 18): string {
|
|
22
|
-
const divisor = 10n ** BigInt(decimals)
|
|
23
|
-
const quotient = value / divisor
|
|
24
|
-
const remainder = value % divisor
|
|
25
|
-
|
|
26
|
-
if (remainder === 0n) {
|
|
27
|
-
return quotient.toString()
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const remainderStr = remainder.toString().padStart(decimals, '0')
|
|
31
|
-
const trimmed = remainderStr.replace(/0+$/, '')
|
|
32
|
-
return `${quotient}.${trimmed}`
|
|
33
|
-
}
|
|
19
|
+
import { formatUnits, parseBigInt } from '../utils.js'
|
|
34
20
|
|
|
35
21
|
/**
|
|
36
22
|
* Asset card component for displaying token/NFT information
|
|
@@ -58,9 +44,6 @@ export class AssetCard extends CoreLitElement {
|
|
|
58
44
|
@property({ type: String, attribute: 'function-icon' })
|
|
59
45
|
functionIcon: string = 'code-outline'
|
|
60
46
|
|
|
61
|
-
@property({ type: String })
|
|
62
|
-
size: 'small' | 'medium' | 'large' = 'medium'
|
|
63
|
-
|
|
64
47
|
@property({ type: Boolean, attribute: 'show-info-button' })
|
|
65
48
|
showInfoButton = true
|
|
66
49
|
|
|
@@ -211,9 +194,7 @@ export class AssetCard extends CoreLitElement {
|
|
|
211
194
|
const displayTokenId = formattedTokenId ? `${formattedTokenId}` : ''
|
|
212
195
|
if (displayTokenId) return displayTokenId
|
|
213
196
|
// If its a collection, do not show tokenId
|
|
214
|
-
return this.tokenId
|
|
215
|
-
? `${parseInt(this.tokenId, 16)}`
|
|
216
|
-
: undefined
|
|
197
|
+
return this.tokenId ? `${parseInt(this.tokenId, 16)}` : undefined
|
|
217
198
|
}
|
|
218
199
|
|
|
219
200
|
private _renderTokenId() {
|
|
@@ -278,6 +259,7 @@ export class AssetCard extends CoreLitElement {
|
|
|
278
259
|
private _renderImages() {
|
|
279
260
|
const images = (this._resolvedData as any)?.images || []
|
|
280
261
|
if (images.length === 0) return ''
|
|
262
|
+
if (!this.address) return ''
|
|
281
263
|
|
|
282
264
|
return html`
|
|
283
265
|
<image-list
|
|
@@ -292,6 +274,7 @@ export class AssetCard extends CoreLitElement {
|
|
|
292
274
|
private _renderAssets() {
|
|
293
275
|
const assets = (this._resolvedData as any)?.assets || []
|
|
294
276
|
if (assets.length === 0) return ''
|
|
277
|
+
if (!this.address) return ''
|
|
295
278
|
|
|
296
279
|
return html`
|
|
297
280
|
<div class="section">
|
|
@@ -309,11 +292,11 @@ export class AssetCard extends CoreLitElement {
|
|
|
309
292
|
return html`
|
|
310
293
|
<div class="flex justify-between items-center my-4">
|
|
311
294
|
<function-name-view
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
295
|
+
label=${this.functionName || 'Execute'}
|
|
296
|
+
size="small"
|
|
297
|
+
icon=${this.functionIcon ? this.functionIcon : 'code-outline'}
|
|
315
298
|
></function-name-view>
|
|
316
|
-
<function-name-view label=${this._resolvedData?.standard} size="small"></function-name-view>
|
|
299
|
+
<function-name-view label=${this._resolvedData?.standard ?? 'Unknown'} size="small"></function-name-view>
|
|
317
300
|
</div>
|
|
318
301
|
`
|
|
319
302
|
}
|
|
@@ -334,6 +317,15 @@ export class AssetCard extends CoreLitElement {
|
|
|
334
317
|
`
|
|
335
318
|
}
|
|
336
319
|
|
|
320
|
+
private _formattedAmount() {
|
|
321
|
+
const data = this._resolutionTask.value
|
|
322
|
+
const amt = this.amount
|
|
323
|
+
if (amt === undefined || amt === null || amt === '') return undefined
|
|
324
|
+
const bigintAmount = parseBigInt(amt as any)
|
|
325
|
+
if (bigintAmount === undefined) return undefined
|
|
326
|
+
return formatUnits(bigintAmount, data?.decimals)
|
|
327
|
+
}
|
|
328
|
+
|
|
337
329
|
private renderFormattedTokenIdBadge() {
|
|
338
330
|
if (!this.displayedTokenId) return ''
|
|
339
331
|
return html`
|
|
@@ -345,30 +337,47 @@ export class AssetCard extends CoreLitElement {
|
|
|
345
337
|
return html`
|
|
346
338
|
<div >
|
|
347
339
|
${this._renderStandards()}
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
340
|
+
<div class="mt-6 flex-col">
|
|
341
|
+
<div class="flex items-center justify-center gap-4">
|
|
342
|
+
<div class="flex justify-end w-[48%]">
|
|
343
|
+
<div class="border relative border-neutral-90 rounded-xl">
|
|
344
|
+
${this.renderFormattedTokenIdBadge()}
|
|
345
|
+
<div class="p-2 relative">
|
|
346
|
+
${when(
|
|
347
|
+
this.address,
|
|
348
|
+
() => html`
|
|
349
|
+
<icon-view
|
|
350
|
+
address=${this.address ?? ''}
|
|
351
|
+
size="2x-large"
|
|
352
|
+
.showName=${false}
|
|
353
|
+
></icon-view>
|
|
354
|
+
`
|
|
355
|
+
)}
|
|
356
|
+
</div>
|
|
357
|
+
<div style="height: 1px;" class="bg-neutral-90 w-full"></div>
|
|
358
|
+
<div class="px-2">
|
|
359
|
+
${this._renderAddressSection()}
|
|
360
|
+
</div>
|
|
363
361
|
</div>
|
|
364
362
|
</div>
|
|
365
|
-
</div>
|
|
366
363
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
364
|
+
<div class="flex flex-col gap-1 w-[51%]">
|
|
365
|
+
<div class="text-[14px] font-bold text-neutral-20">
|
|
366
|
+
${this._resolvedData?.lsp4TokenName || 'UNKNOWN'}
|
|
367
|
+
</div>
|
|
368
|
+
<div class="text-[17px] font-semibold text-neutral-20">
|
|
369
|
+
${when(
|
|
370
|
+
this.amount,
|
|
371
|
+
() => html`
|
|
372
|
+
<span class="">${this._formattedAmount() ?? ''}</span>
|
|
373
|
+
<span class="ml-1 text-neutral-60 max-w-[60px] whitespace-nowrap overflow-hidden text-ellipsis text-[14px] font-semibold">${
|
|
374
|
+
this._resolvedData?.lsp4TokenSymbol || ''
|
|
375
|
+
}</span>
|
|
376
|
+
`,
|
|
377
|
+
() => html`${this._resolvedData?.lsp4TokenSymbol || ''}`
|
|
378
|
+
)}
|
|
379
|
+
</div>
|
|
380
|
+
|
|
372
381
|
${when(
|
|
373
382
|
this.amount,
|
|
374
383
|
() => html`
|
|
@@ -379,20 +388,14 @@ export class AssetCard extends CoreLitElement {
|
|
|
379
388
|
`,
|
|
380
389
|
() => html`${this._resolvedData?.lsp4TokenSymbol || ''}`
|
|
381
390
|
)}
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
this.displayedTokenId,
|
|
386
|
-
() =>
|
|
387
|
-
html`<div class="text-[14px] font-medium text-neutral-60"><span class="font-bold">#${this.displayedTokenId}</span></div>`
|
|
388
|
-
)}
|
|
389
|
-
<div class="mt-4">
|
|
390
|
-
${when(this.showCreator, () => this._renderCreatorSection())}
|
|
391
|
+
<div class="mt-4">
|
|
392
|
+
${when(this.showCreator, () => this._renderCreatorSection())}
|
|
393
|
+
</div>
|
|
391
394
|
</div>
|
|
392
395
|
</div>
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
+
<div class="border-neutral-90 pt-2">
|
|
397
|
+
${this._renderTokenDetails()}
|
|
398
|
+
</div>
|
|
396
399
|
</div>
|
|
397
400
|
</div>
|
|
398
401
|
`
|
package/src/shared/image-list.ts
CHANGED
package/src/shared/value-view.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Task } from '@lit/task'
|
|
2
2
|
import { useAddressResolver } from '@lukso/transaction-view-headless'
|
|
3
|
-
import { html } from 'lit'
|
|
3
|
+
import { html, type TemplateResult } from 'lit'
|
|
4
4
|
import { property, state } from 'lit/decorators.js'
|
|
5
5
|
import { when } from 'lit/directives/when.js'
|
|
6
6
|
import { safeCustomElement } from './safe-custom-element'
|
|
@@ -8,21 +8,7 @@ import { safeCustomElement } from './safe-custom-element'
|
|
|
8
8
|
// Import LUKSO web components
|
|
9
9
|
import './load-web-components'
|
|
10
10
|
import { CoreLitElement } from '../styles'
|
|
11
|
-
|
|
12
|
-
// Value formatting utilities
|
|
13
|
-
function formatUnits(value: bigint, decimals = 18): string {
|
|
14
|
-
const divisor = 10n ** BigInt(decimals)
|
|
15
|
-
const quotient = value / divisor
|
|
16
|
-
const remainder = value % divisor
|
|
17
|
-
|
|
18
|
-
if (remainder === 0n) {
|
|
19
|
-
return quotient.toString()
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const remainderStr = remainder.toString().padStart(decimals, '0')
|
|
23
|
-
const trimmed = remainderStr.replace(/0+$/, '')
|
|
24
|
-
return `${quotient}.${trimmed}`
|
|
25
|
-
}
|
|
11
|
+
import { formatUnits } from '../utils'
|
|
26
12
|
|
|
27
13
|
/**
|
|
28
14
|
* Value display component
|
|
@@ -47,7 +33,7 @@ export class ValueView extends CoreLitElement {
|
|
|
47
33
|
* The value to display (required)
|
|
48
34
|
* Can be string, number, boolean, or bigint
|
|
49
35
|
*/
|
|
50
|
-
@property() value?: string | number | boolean | bigint | null
|
|
36
|
+
@property() value?: TemplateResult | string | number | boolean | bigint | null
|
|
51
37
|
|
|
52
38
|
/**
|
|
53
39
|
* The key name (used for special formatting logic)
|
|
@@ -98,7 +84,7 @@ export class ValueView extends CoreLitElement {
|
|
|
98
84
|
/**
|
|
99
85
|
* Optional label to show in card header
|
|
100
86
|
*/
|
|
101
|
-
@property() label?: string
|
|
87
|
+
@property() label?: string | TemplateResult
|
|
102
88
|
|
|
103
89
|
/**
|
|
104
90
|
* Maximum width for truncation
|
|
@@ -380,7 +366,7 @@ export class ValueView extends CoreLitElement {
|
|
|
380
366
|
|
|
381
367
|
if (effectiveType === 'currency') {
|
|
382
368
|
const { numeric, symbol } = this.getCurrencyParts()
|
|
383
|
-
if (this.label
|
|
369
|
+
if (this.label) {
|
|
384
370
|
headerText = this.label
|
|
385
371
|
contentTemplate = html`
|
|
386
372
|
<span class="inline-block overflow-hidden text-ellipsis whitespace-nowrap min-w-0 ${this.getNumericClass()}">${numeric}</span>
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { formatUnits, parseBigInt } from './utils.js'
|
|
3
|
+
|
|
4
|
+
describe('formatUnits', () => {
|
|
5
|
+
it('returns integer string when remainder is zero', () => {
|
|
6
|
+
expect(formatUnits(1000000000000000000n)).toBe('1')
|
|
7
|
+
expect(formatUnits(5000n, 3)).toBe('5')
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
it('formats values with fractional part respecting decimals', () => {
|
|
11
|
+
expect(formatUnits(123000000000000000n)).toBe('0.123')
|
|
12
|
+
expect(formatUnits(987654321n, 6)).toBe('987.654321')
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
it('trims trailing zeros in fractional part', () => {
|
|
16
|
+
expect(formatUnits(120n, 2)).toBe('1.2')
|
|
17
|
+
expect(formatUnits(1001000n, 6)).toBe('1.001')
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
it('handles zero value', () => {
|
|
21
|
+
expect(formatUnits(0n)).toBe('0')
|
|
22
|
+
})
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
describe('parseBigInt', () => {
|
|
26
|
+
it('parses decimal and hex strings with optional suffix', () => {
|
|
27
|
+
expect(parseBigInt('42')).toBe(42n)
|
|
28
|
+
expect(parseBigInt('0x10')).toBe(16n)
|
|
29
|
+
expect(parseBigInt('21n')).toBe(21n)
|
|
30
|
+
expect(parseBigInt(' 0xFFn ')).toBe(255n)
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it('returns bigint directly or from integer numbers', () => {
|
|
34
|
+
const value = 123n
|
|
35
|
+
expect(parseBigInt(value)).toBe(value)
|
|
36
|
+
expect(parseBigInt(9007199254740991)).toBe(9007199254740991n)
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it('returns undefined for invalid inputs', () => {
|
|
40
|
+
expect(parseBigInt('')).toBeUndefined()
|
|
41
|
+
expect(parseBigInt('1.5')).toBeUndefined()
|
|
42
|
+
expect(parseBigInt('0xG')).toBeUndefined()
|
|
43
|
+
expect(parseBigInt(1.1)).toBeUndefined()
|
|
44
|
+
expect(parseBigInt(null)).toBeUndefined()
|
|
45
|
+
expect(parseBigInt(undefined)).toBeUndefined()
|
|
46
|
+
})
|
|
47
|
+
})
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// Value formatting utilities
|
|
2
|
+
/**
|
|
3
|
+
* Convert a token amount from base units (bigint) to a decimal string.
|
|
4
|
+
*
|
|
5
|
+
* - Handles arbitrary `decimals` without floating point precision loss.
|
|
6
|
+
* - Trims trailing zeros in the fractional part.
|
|
7
|
+
* - Returns only the integer part when remainder is zero.
|
|
8
|
+
*
|
|
9
|
+
* @param value - The amount in base units as a bigint (e.g., wei for ETH).
|
|
10
|
+
* @param decimals - Number of decimal places the token uses (default 18).
|
|
11
|
+
* @returns A human-readable decimal string.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* // 1 ether in wei
|
|
15
|
+
* formatUnits(1000000000000000000n) // '1'
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* // 1.23 tokens with 2 decimals
|
|
19
|
+
* formatUnits(123n, 2) // '1.23'
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* // No remainder: return integer part only
|
|
23
|
+
* formatUnits(2000000000000000000n) // '2'
|
|
24
|
+
*/
|
|
25
|
+
export function formatUnits(value: bigint, decimals = 18): string {
|
|
26
|
+
const divisor = 10n ** BigInt(decimals)
|
|
27
|
+
const quotient = value / divisor
|
|
28
|
+
const remainder = value % divisor
|
|
29
|
+
|
|
30
|
+
if (remainder === 0n) {
|
|
31
|
+
return quotient.toString()
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const remainderStr = remainder.toString().padStart(decimals, '0')
|
|
35
|
+
const trimmed = remainderStr.replace(/0+$/, '')
|
|
36
|
+
return `${quotient}.${trimmed}`
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function isBigIntString(str: string): boolean {
|
|
40
|
+
const trimmed = str.trim()
|
|
41
|
+
if (trimmed.endsWith('n'))
|
|
42
|
+
return /^[0-9]+n$/.test(trimmed) || /^0x[0-9a-fA-F]+n$/.test(trimmed)
|
|
43
|
+
return /^[0-9]+$/.test(trimmed) || /^0x[0-9a-fA-F]+$/.test(trimmed)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Parse loose bigint inputs coming from attributes, JSON, or user input.
|
|
48
|
+
*
|
|
49
|
+
* - Accepts bigint literals (`123n`), decimal/hex strings, and integers.
|
|
50
|
+
* - Trims whitespace and optional trailing `n` suffix.
|
|
51
|
+
* - Returns `undefined` for invalid formats (floats, empty strings, non-numeric text).
|
|
52
|
+
*
|
|
53
|
+
* @param value - Source value to parse (string | number | bigint | null | undefined).
|
|
54
|
+
* @returns BigInt when parsing succeeds, otherwise `undefined`.
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* parseBigInt('42') // 42n
|
|
58
|
+
* @example
|
|
59
|
+
* parseBigInt('0x10n') // 16n
|
|
60
|
+
* @example
|
|
61
|
+
* parseBigInt(1000n) // 1000n (passthrough)
|
|
62
|
+
* @example
|
|
63
|
+
* parseBigInt('1.5') // undefined (decimals not allowed)
|
|
64
|
+
*/
|
|
65
|
+
export function parseBigInt(
|
|
66
|
+
value: string | number | bigint | null | undefined
|
|
67
|
+
): bigint | undefined {
|
|
68
|
+
if (!value) return undefined
|
|
69
|
+
if (typeof value === 'bigint') return value
|
|
70
|
+
if (typeof value === 'number') {
|
|
71
|
+
if (!Number.isInteger(value)) return undefined
|
|
72
|
+
return BigInt(value)
|
|
73
|
+
}
|
|
74
|
+
const trimmed = value.trim()
|
|
75
|
+
const cleaned = trimmed.endsWith('n') ? trimmed.slice(0, -1) : trimmed
|
|
76
|
+
if (!isBigIntString(cleaned)) return undefined
|
|
77
|
+
try {
|
|
78
|
+
return BigInt(cleaned)
|
|
79
|
+
} catch {
|
|
80
|
+
return undefined
|
|
81
|
+
}
|
|
82
|
+
}
|