@lukso/transaction-view-core 0.3.1-dev.a8c9315

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 (62) hide show
  1. package/.turbo/turbo-build.log +33 -0
  2. package/CHANGELOG.md +104 -0
  3. package/LICENSE +201 -0
  4. package/README.md +264 -0
  5. package/dist/index.cjs +4573 -0
  6. package/dist/index.cjs.map +1 -0
  7. package/dist/index.d.cts +493 -0
  8. package/dist/index.d.ts +493 -0
  9. package/dist/index.js +4534 -0
  10. package/dist/index.js.map +1 -0
  11. package/dist/shared/index.cjs +2480 -0
  12. package/dist/shared/index.cjs.map +1 -0
  13. package/dist/shared/index.d.cts +887 -0
  14. package/dist/shared/index.d.ts +887 -0
  15. package/dist/shared/index.js +2437 -0
  16. package/dist/shared/index.js.map +1 -0
  17. package/dist/styles/index.cjs +58 -0
  18. package/dist/styles/index.cjs.map +1 -0
  19. package/dist/styles/index.d.cts +9 -0
  20. package/dist/styles/index.d.ts +9 -0
  21. package/dist/styles/index.js +31 -0
  22. package/dist/styles/index.js.map +1 -0
  23. package/package.json +57 -0
  24. package/publish.log +0 -0
  25. package/src/components/asset-card.ts +426 -0
  26. package/src/components/contract-card.ts +238 -0
  27. package/src/components/description-view.ts +605 -0
  28. package/src/components/index.ts +20 -0
  29. package/src/components/info-panel.ts +437 -0
  30. package/src/components/permissions-block.ts +138 -0
  31. package/src/components/transaction-playback.ts +429 -0
  32. package/src/components/universal-token-view.ts +281 -0
  33. package/src/declaration.d.ts +4 -0
  34. package/src/examples/INTEGRATION_GUIDE.md +240 -0
  35. package/src/examples/TransactionContentWithReplacement.vue +269 -0
  36. package/src/index.ts +38 -0
  37. package/src/registry/index.ts +31 -0
  38. package/src/registry/lit-view-loader.ts +191 -0
  39. package/src/shared/address-display.ts +370 -0
  40. package/src/shared/address-view.ts +385 -0
  41. package/src/shared/custom-select.ts +164 -0
  42. package/src/shared/function-name-view.ts +89 -0
  43. package/src/shared/gas-provider-view.ts +218 -0
  44. package/src/shared/icon-view.ts +288 -0
  45. package/src/shared/image-list.ts +261 -0
  46. package/src/shared/image-loader-controller.ts +160 -0
  47. package/src/shared/index.ts +34 -0
  48. package/src/shared/load-web-components.ts +56 -0
  49. package/src/shared/notification-view.ts +132 -0
  50. package/src/shared/progress-view.ts +86 -0
  51. package/src/shared/safe-custom-element.ts +35 -0
  52. package/src/shared/scroll-to-bottom-button.ts +55 -0
  53. package/src/shared/signal-controller.ts +124 -0
  54. package/src/shared/transaction-view-controller.ts +345 -0
  55. package/src/shared/url-view.ts +129 -0
  56. package/src/shared/value-view.ts +510 -0
  57. package/src/styles/index.ts +28 -0
  58. package/src/styles/styles.css +1 -0
  59. package/src/vue-entry.ts +15 -0
  60. package/tailwind.config.cjs +9 -0
  61. package/tsconfig.json +22 -0
  62. package/tsup.config.ts +90 -0
@@ -0,0 +1,129 @@
1
+ import { html } from 'lit'
2
+ import { customElement, property, state } from 'lit/decorators.js'
3
+ import { safeCustomElement } from './safe-custom-element'
4
+ import './load-web-components'
5
+ import { CoreLitElement } from '../styles'
6
+
7
+ /**
8
+ * UrlView - display a URL with favicon/logo and human-friendly name
9
+ *
10
+ * Props:
11
+ * - url (required): full URL like https://example.com/path
12
+ * - size: x-small | small | medium | large
13
+ * - label?: optional label displayed above
14
+ * - showLogo: whether to show favicon/logo (default true)
15
+ * - logoUrl?: explicit logo URL to use instead of favicon
16
+ * - maxWidth: name truncation width in px
17
+ */
18
+ @safeCustomElement('url-view')
19
+ export class UrlView extends CoreLitElement {
20
+ @property() url!: string
21
+ @property() size: 'x-small' | 'small' | 'medium' | 'large' = 'small'
22
+ @property() label?: string
23
+ @property({ type: Boolean, attribute: 'show-logo' }) showLogo = true
24
+ @property({ attribute: 'logo-url' }) logoUrl?: string
25
+ @property({ type: Number, attribute: 'max-width' }) maxWidth = 240
26
+
27
+ @state() private favicon?: string
28
+ @state() private displayName = ''
29
+ private faviconCandidates: string[] = []
30
+ private faviconIndex = 0
31
+
32
+ protected willUpdate(changed: Map<string, unknown>) {
33
+ if (changed.has('url') || changed.has('label') || changed.has('logoUrl')) {
34
+ this.computeMeta()
35
+ }
36
+ }
37
+
38
+ private computeMeta() {
39
+ try {
40
+ const u = new URL(this.url)
41
+ const origin = u.origin
42
+ this.faviconCandidates = []
43
+ if (this.logoUrl) this.faviconCandidates.push(this.logoUrl)
44
+ this.faviconCandidates.push(
45
+ `${origin}/favicon.ico`,
46
+ `${origin}/favicon.png`
47
+ )
48
+ this.faviconIndex = 0
49
+ this.favicon = this.faviconCandidates[this.faviconIndex]
50
+ const hostname = u.hostname.replace(/^www\./, '')
51
+ const hostnamePretty = hostname
52
+ .split('.')
53
+ .filter(Boolean)
54
+ .slice(-2)
55
+ .join(' ')
56
+ .replace(/-/g, ' ')
57
+ .replace(/\b\w/g, (c) => c.toUpperCase())
58
+
59
+ if (this.label && this.label.trim()) {
60
+ this.displayName = this.label.trim()
61
+ } else {
62
+ this.displayName = hostnamePretty || this.url
63
+ }
64
+ } catch {
65
+ this.favicon = undefined
66
+ // Fallback to raw string if URL parse fails
67
+ this.displayName = this.label && this.label.trim() ? this.label : this.url
68
+ }
69
+ }
70
+
71
+ private handleImageError() {
72
+ this.faviconIndex += 1
73
+ if (this.faviconIndex < this.faviconCandidates.length) {
74
+ this.favicon = this.faviconCandidates[this.faviconIndex]
75
+ } else {
76
+ this.favicon = undefined
77
+ }
78
+ }
79
+
80
+ private getSizePixels(): number {
81
+ switch (this.size) {
82
+ case 'x-small':
83
+ return 14
84
+ case 'small':
85
+ return 16
86
+ case 'medium':
87
+ return 20
88
+ case 'large':
89
+ return 24
90
+ default:
91
+ return 16
92
+ }
93
+ }
94
+
95
+ override render() {
96
+ const sizePx = this.getSizePixels()
97
+ return html`
98
+ <div class="inline-flex flex-col gap-1">
99
+ <div class="inline-flex items-center gap-2 px-2.5 py-1.5 text-neutral-20 leading-tight font-semibold">
100
+ ${
101
+ this.showLogo
102
+ ? html`
103
+ <div class="inline-flex items-center p-1 justify-center rounded-lg bg-neutral-100 border border-neutral-90 overflow-hidden flex-none">
104
+ ${
105
+ this.favicon
106
+ ? html`<img
107
+ src=${this.favicon}
108
+ style="width:${sizePx}px;height:${sizePx}px;"
109
+ alt="favicon"
110
+ @error=${this.handleImageError}
111
+ />`
112
+ : html`<lukso-icon name="globe" size=${this.size} color="blue-85"></lukso-icon>`
113
+ }
114
+ </div>
115
+ `
116
+ : ''
117
+ }
118
+ <span class="whitespace-nowrap overflow-hidden text-ellipsis leading-tight" style=${`max-width:${this.maxWidth}px;font-size: ${sizePx}px;`}>${this.displayName}</span>
119
+ </div>
120
+ </div>
121
+ `
122
+ }
123
+ }
124
+
125
+ declare global {
126
+ interface HTMLElementTagNameMap {
127
+ 'url-view': UrlView
128
+ }
129
+ }
@@ -0,0 +1,510 @@
1
+ import { Task } from '@lit/task'
2
+ import { useAddressResolver } from '@lukso/transaction-view-headless'
3
+ import { css, html } from 'lit'
4
+ import { property, state } from 'lit/decorators.js'
5
+ import { when } from 'lit/directives/when.js'
6
+ import { safeCustomElement } from './safe-custom-element'
7
+
8
+ // Import LUKSO web components
9
+ import './load-web-components'
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
+ }
26
+
27
+ /**
28
+ * Value display component
29
+ *
30
+ * Displays various types of values with appropriate formatting and styling.
31
+ * Supports numbers, strings, booleans, BigInt values, and special handling
32
+ * for Ethereum values with currency symbols.
33
+ *
34
+ * @example
35
+ * ```html
36
+ * <value-view
37
+ * value="1000000000000000000"
38
+ * type="currency"
39
+ * currency-symbol="LYX"
40
+ * decimals="18"
41
+ * ></value-view>
42
+ * ```
43
+ */
44
+ @safeCustomElement('value-view')
45
+ export class ValueView extends CoreLitElement {
46
+ /**
47
+ * The value to display (required)
48
+ * Can be string, number, boolean, or bigint
49
+ */
50
+ @property() value?: string | number | boolean | bigint | null
51
+
52
+ /**
53
+ * The key name (used for special formatting logic)
54
+ */
55
+ @property({ attribute: 'key-name' }) keyName?: string
56
+
57
+ @property({ type: String, attribute: 'container-class-names' })
58
+ containerClassNames?: string
59
+
60
+ /**
61
+ * Value type for formatting
62
+ */
63
+ @property() type:
64
+ | 'string'
65
+ | 'number'
66
+ | 'boolean'
67
+ | 'currency'
68
+ | 'bigint'
69
+ | 'address'
70
+ | 'auto' = 'auto'
71
+
72
+ /**
73
+ * Currency symbol for currency type
74
+ */
75
+ @property({ attribute: 'currency-symbol' }) currencySymbol = 'LYX'
76
+
77
+ /**
78
+ * Number of decimals for currency formatting
79
+ */
80
+ @property({ type: Number }) decimals = 18
81
+
82
+ /**
83
+ * Display size
84
+ */
85
+ @property() size: 'x-small' | 'small' | 'medium' | 'large' = 'medium'
86
+
87
+ /**
88
+ * Whether to show copy button
89
+ */
90
+ @property({ type: Boolean, attribute: 'show-copy-button' }) showCopyButton =
91
+ false
92
+
93
+ /**
94
+ * Render as a card with bordered container and label
95
+ */
96
+ @property({ type: Boolean, attribute: 'is-card-view' }) isCardView = false
97
+
98
+ /**
99
+ * Optional label to show in card header
100
+ */
101
+ @property() label?: string
102
+
103
+ /**
104
+ * Maximum width for truncation
105
+ */
106
+ @property({ type: Number, attribute: 'max-width' }) maxWidth?: number
107
+
108
+ /**
109
+ * Token/contract address for automatic metadata resolution
110
+ * When provided, ValueView will automatically resolve token symbol and decimals
111
+ */
112
+ @property() address?: string
113
+
114
+ // Internal state for resolved metadata
115
+ @state() private resolvedMetadata?: {
116
+ symbol?: string
117
+ decimals?: number
118
+ name?: string
119
+ }
120
+
121
+ // Tracks whether the component has any slotted content
122
+ @state() private _hasSlotContent = false
123
+
124
+ private _onSlotChange(e: Event) {
125
+ const slot = e.target as HTMLSlotElement
126
+ const assigned = slot?.assignedNodes({ flatten: true }) ?? []
127
+ this._hasSlotContent = assigned.length > 0
128
+ }
129
+
130
+ protected override firstUpdated(): void {
131
+ const slot = this.renderRoot.querySelector('slot') as HTMLSlotElement | null
132
+ if (slot) {
133
+ const assigned = slot.assignedNodes({ flatten: true })
134
+ this._hasSlotContent = assigned.length > 0
135
+ }
136
+ }
137
+
138
+ // Address resolution utilities (lazy initialization)
139
+ private get addressResolver() {
140
+ try {
141
+ return useAddressResolver()
142
+ } catch (error) {
143
+ console.warn('ValueView: Address resolver not available:', error)
144
+ return {
145
+ resolveAddress: async () => null,
146
+ }
147
+ }
148
+ }
149
+
150
+ // Task for resolving address metadata
151
+ private addressTask = new Task(this, {
152
+ task: async ([address]) => {
153
+ if (!address) return null
154
+ try {
155
+ return this.addressResolver.resolveAddress(address)
156
+ } catch (error) {
157
+ console.warn('ValueView: Failed to resolve address metadata:', error)
158
+ return null
159
+ }
160
+ },
161
+ args: () => [this.address] as const,
162
+ })
163
+
164
+ /**
165
+ * Get the effective type for the value
166
+ */
167
+ private getEffectiveType(): string {
168
+ if (this.type !== 'auto') return this.type
169
+
170
+ const value = this.value
171
+
172
+ if (value === null || value === undefined) return 'null'
173
+ if (typeof value === 'string') return 'string'
174
+ if (typeof value === 'number') return 'number'
175
+ if (typeof value === 'boolean') return 'boolean'
176
+ if (typeof value === 'bigint') {
177
+ // Special handling for 'value' key - treat as currency
178
+ if (this.keyName === 'value') return 'currency'
179
+ return 'bigint'
180
+ }
181
+
182
+ return 'string'
183
+ }
184
+
185
+ /**
186
+ * Get CSS class for the value type
187
+ */
188
+ private getValueClass(): string {
189
+ const effectiveType = this.getEffectiveType()
190
+
191
+ switch (effectiveType) {
192
+ case 'string':
193
+ return 'text-success-60'
194
+ case 'number':
195
+ return 'text-info-60'
196
+ case 'boolean':
197
+ return 'text-error-60'
198
+ case 'currency':
199
+ return 'text-warning-60 font-medium'
200
+ case 'bigint':
201
+ return 'text-secondary-60'
202
+ case 'address':
203
+ return 'text-info-60 font-mono'
204
+ case 'null':
205
+ return 'text-neutral-70 italic'
206
+ default:
207
+ return 'text-neutral-10'
208
+ }
209
+ }
210
+
211
+ /**
212
+ * Format the value for display
213
+ */
214
+ private getFormattedValue(): string {
215
+ const value = this.value
216
+ const effectiveType = this.getEffectiveType()
217
+
218
+ if (value === null) return 'null'
219
+ if (value === undefined) return 'undefined'
220
+
221
+ switch (effectiveType) {
222
+ case 'currency':
223
+ const { numeric, symbol } = this.getCurrencyParts()
224
+ return `${numeric} ${symbol}`
225
+
226
+ case 'bigint':
227
+ if (typeof value === 'bigint') {
228
+ return value.toString() + 'n'
229
+ }
230
+ return String(value) + 'n'
231
+
232
+ case 'boolean':
233
+ return value ? 'true' : 'false'
234
+
235
+ case 'number':
236
+ if (typeof value === 'number') {
237
+ // Format large numbers with commas
238
+ return value.toLocaleString()
239
+ }
240
+ return String(value)
241
+
242
+ case 'string':
243
+ return String(value)
244
+
245
+ case 'null':
246
+ return 'null'
247
+
248
+ default:
249
+ return String(value)
250
+ }
251
+ }
252
+
253
+ /**
254
+ * Compute currency display parts: numeric portion and symbol
255
+ */
256
+ private getCurrencyParts(): { numeric: string; symbol: string } {
257
+ const value = this.value
258
+ const resolvedData: any = this.addressTask.value
259
+ const effectiveDecimals = resolvedData?.lsp4TokenSymbol
260
+ ? (resolvedData.baseAsset?.decimals ??
261
+ resolvedData.decimals ??
262
+ this.decimals)
263
+ : this.decimals
264
+ const effectiveSymbol =
265
+ resolvedData?.lsp4TokenSymbol ||
266
+ resolvedData?.baseAsset?.lsp4TokenSymbol ||
267
+ this.currencySymbol
268
+
269
+ let numeric = String(value ?? '')
270
+ try {
271
+ if (typeof value === 'bigint') {
272
+ numeric = formatUnits(value, effectiveDecimals)
273
+ } else if (typeof value === 'string' && !isNaN(Number(value))) {
274
+ const bigintValue = BigInt(value)
275
+ numeric = formatUnits(bigintValue, effectiveDecimals)
276
+ }
277
+ } catch {
278
+ // fallback to raw string
279
+ numeric = String(value ?? '')
280
+ }
281
+
282
+ return { numeric, symbol: effectiveSymbol }
283
+ }
284
+
285
+ /** Typography class for numeric value based on size */
286
+ private getNumericClass(): string {
287
+ switch (this.size) {
288
+ case 'x-small':
289
+ return 'paragraph-ptmono-12-bold'
290
+ case 'small':
291
+ return 'paragraph-ptmono-14-bold'
292
+ case 'medium':
293
+ return 'paragraph-ptmono-16-bold'
294
+ case 'large':
295
+ return 'paragraph-ptmono-24-bold'
296
+ default:
297
+ return 'paragraph-ptmono-16-bold'
298
+ }
299
+ }
300
+
301
+ /** Typography class for currency symbol based on size */
302
+ private getCurrencyClass(): string {
303
+ switch (this.size) {
304
+ case 'x-small':
305
+ return 'paragraph-inter-12-regular'
306
+ case 'small':
307
+ return 'paragraph-inter-14-regular'
308
+ case 'medium':
309
+ return 'paragraph-inter-16-regular'
310
+ case 'large':
311
+ return 'paragraph-inter-20-regular'
312
+ default:
313
+ return 'paragraph-inter-16-regular'
314
+ }
315
+ }
316
+
317
+ /**
318
+ * Get size-appropriate font size
319
+ */
320
+ private getContainerTextClass(): string {
321
+ switch (this.size) {
322
+ case 'x-small':
323
+ return 'text-[12px]'
324
+ case 'small':
325
+ return 'text-[14px]'
326
+ case 'medium':
327
+ return 'text-[16px]'
328
+ case 'large':
329
+ return 'text-[18px]'
330
+ default:
331
+ return 'text-[16px]'
332
+ }
333
+ }
334
+
335
+ /**
336
+ * Handle copy to clipboard
337
+ */
338
+ private async handleCopy(e?: Event) {
339
+ if (e) e.stopPropagation()
340
+ const textToCopy = String(this.value)
341
+
342
+ try {
343
+ await navigator.clipboard.writeText(textToCopy)
344
+
345
+ // Dispatch copy event
346
+ this.dispatchEvent(
347
+ new CustomEvent('value-copied', {
348
+ detail: { value: textToCopy },
349
+ bubbles: true,
350
+ })
351
+ )
352
+
353
+ // Value copied successfully - UI feedback handled by event
354
+ } catch (error) {
355
+ console.error('Failed to copy value:', error)
356
+ }
357
+ }
358
+
359
+ /**
360
+ * Handle value click
361
+ */
362
+ private handleClick() {
363
+ this.dispatchEvent(
364
+ new CustomEvent('value-click', {
365
+ detail: {
366
+ value: this.value,
367
+ formattedValue: this.getFormattedValue(),
368
+ type: this.getEffectiveType(),
369
+ },
370
+ bubbles: true,
371
+ })
372
+ )
373
+ }
374
+
375
+ override render() {
376
+ const formattedValue = this.getFormattedValue()
377
+ const valueClass = this.getValueClass()
378
+
379
+ // Card view
380
+ if (this.isCardView) {
381
+ const effectiveType = this.getEffectiveType()
382
+ let headerText = this.label || ''
383
+ let contentTemplate = html`
384
+ <span class="inline-block text-wrap min-w-0 text-center">${formattedValue}</span>
385
+ `
386
+
387
+ if (effectiveType === 'currency') {
388
+ const { numeric, symbol } = this.getCurrencyParts()
389
+ if (this.label && this.label.trim() !== '') {
390
+ headerText = this.label
391
+ contentTemplate = html`
392
+ <span class="inline-block overflow-hidden text-ellipsis whitespace-nowrap min-w-0 ${this.getNumericClass()}">${numeric}</span>
393
+ <span class="inline-block ${this.getCurrencyClass()}"> ${symbol}</span>
394
+ `
395
+ } else {
396
+ headerText = symbol
397
+ contentTemplate = html`
398
+ <span class="inline-block overflow-hidden text-ellipsis whitespace-nowrap min-w-0 ${this.getNumericClass()}">${numeric}</span>
399
+ `
400
+ }
401
+ }
402
+
403
+ return html`
404
+ <div
405
+ class="block border border-neutral-90 rounded-xl bg-white py-3 px-4 min-w-[320px] ${this.containerClassNames || ''}"
406
+ @click=${this.handleClick}
407
+ style="${this.maxWidth ? `max-width: ${this.maxWidth}px` : ''}"
408
+ >
409
+ <div class="flex items-center justify-between mb-2">
410
+ ${
411
+ headerText
412
+ ? html`<div class="${this.getLabelClass()} text-neutral-20 overflow-hidden text-ellipsis whitespace-nowrap">${headerText}</div>`
413
+ : html``
414
+ }
415
+ ${
416
+ this.showCopyButton
417
+ ? html`
418
+ <button
419
+ class="bg-white rounded-lg w-6 h-6 flex items-center justify-center cursor-pointer text-neutral-20 hover:bg-neutral-95"
420
+ @click=${(e: Event) => this.handleCopy(e)}
421
+ title="Copy value"
422
+ aria-label="Copy"
423
+ >
424
+ <lukso-icon name="copy" size="small"></lukso-icon>
425
+ </button>
426
+ `
427
+ : html``
428
+ }
429
+ </div>
430
+ ${when(
431
+ !this._hasSlotContent,
432
+ () => html`
433
+ <div class="font-semibold text-neutral-10 break-words flex items-center gap-2 text-wrap ${
434
+ this.getUseMonospace() ? 'font-mono' : ''
435
+ }">
436
+ ${contentTemplate}
437
+ </div>
438
+ `
439
+ )}
440
+ <slot @slotchange=${this._onSlotChange}></slot>
441
+ </div>
442
+ `
443
+ }
444
+
445
+ // Inline view (default)
446
+ return html`
447
+ <div
448
+ class="inline-flex items-center gap-1.5 cursor-pointer transition-all duration-200 rounded px-1 py-0.5 hover:bg-neutral-95 min-w-[320px] ${this.getContainerTextClass()} ${this.containerClassNames || ''}"
449
+ @click=${this.handleClick}
450
+ style="${this.maxWidth ? `max-width: ${this.maxWidth}px` : ''}"
451
+ >
452
+ <span class="inline-block overflow-hidden text-ellipsis whitespace-nowrap min-w-0 ${valueClass}">
453
+ ${formattedValue}
454
+ </span>
455
+ ${
456
+ this.showCopyButton
457
+ ? html`
458
+ <button
459
+ class="inline-flex items-center justify-center bg-transparent border-0 cursor-pointer text-neutral-70 p-0.5 rounded transition-all duration-200 hover:text-primary-50 hover:bg-neutral-90 flex-shrink-0"
460
+ @click=${(e: Event) => this.handleCopy(e)}
461
+ title="Copy value"
462
+ aria-label="Copy value to clipboard"
463
+ >
464
+ <lukso-icon name="copy" size="small"></lukso-icon>
465
+ </button>
466
+ `
467
+ : ''
468
+ }
469
+ </div>
470
+ `
471
+ }
472
+
473
+ /** Whether to use monospace font for the value content */
474
+ private getUseMonospace(): boolean {
475
+ const t = this.getEffectiveType()
476
+ if (
477
+ t === 'address' ||
478
+ t === 'bigint' ||
479
+ this.value?.toString().startsWith('0x')
480
+ )
481
+ return true
482
+ if (t === 'string') {
483
+ const s = String(this.value ?? '')
484
+ return /^0x[0-9a-fA-F]+$/.test(s) || s.length > 32
485
+ }
486
+ return false
487
+ }
488
+
489
+ /** Map size to a typography class for the label */
490
+ private getLabelClass(): string {
491
+ switch (this.size) {
492
+ case 'x-small':
493
+ return 'paragraph-inter-12-semi-bold'
494
+ case 'small':
495
+ return 'heading-inter-14-bold'
496
+ case 'medium':
497
+ return 'heading-inter-17-semi-bold'
498
+ case 'large':
499
+ return 'heading-inter-21-semi-bold'
500
+ default:
501
+ return 'heading-inter-17-semi-bold'
502
+ }
503
+ }
504
+ }
505
+
506
+ declare global {
507
+ interface HTMLElementTagNameMap {
508
+ 'value-view': ValueView
509
+ }
510
+ }
@@ -0,0 +1,28 @@
1
+ // Export the built CSS as a string for components that need to inject styles
2
+ // Tailwind + web-components CSS is compiled into this file by our PostCSS pipeline
3
+ // esbuild-raw-plugin ensures importing CSS returns its raw contents as a string
4
+
5
+ import output from 'styles.css?raw'
6
+ import { css, LitElement, unsafeCSS } from 'lit'
7
+
8
+ const style = css`
9
+ ${unsafeCSS(output)}
10
+
11
+ :host([is-inline]) {
12
+ display: inline-flex;
13
+ align-items: center;
14
+ }
15
+
16
+ :host([is-full-width]) {
17
+ width: 100%;
18
+ flex: 1;
19
+ display: flex;
20
+ }
21
+
22
+ `
23
+
24
+ export class CoreLitElement extends LitElement {
25
+ static styles = [style]
26
+ }
27
+
28
+ export default style
@@ -0,0 +1 @@
1
+ @import "@lukso/web-components/dist/styles/main.css";
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Vue-specific entry point for transaction view core
3
+ * Import this when you need Vue integration, to avoid Vue dependency issues in other frameworks
4
+ */
5
+
6
+ // Re-export types
7
+ export type { DecoderResult } from '@lukso/transaction-decoder'
8
+ export type {
9
+ DecoderRecordForSelection,
10
+ ViewContext,
11
+ } from '@lukso/transaction-view-headless'
12
+ // Also re-export the core functionality
13
+ export * from './registry/index.js'
14
+ export * from './shared/index.js'
15
+ export * from './vue/index.js'
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Tailwind v3 config for @lukso/transaction-view-core
3
+ * Minimal theme extensions to support @lukso/web-components CSS apply tokens
4
+ * like text-48, leading-58, font-400, font-inter, etc.
5
+ */
6
+ module.exports = {
7
+ content: ['./src/**/*.{ts,tsx,js,jsx,vue,html}'],
8
+ presets: [require('@lukso/web-components/tailwind.config')],
9
+ }