@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,385 @@
1
+ import { Task } from '@lit/task'
2
+ import { useAddressResolver } from '@lukso/transaction-view-headless'
3
+ import makeBlockie from 'ethereum-blockies-base64'
4
+ import { html } from 'lit'
5
+ import { property, state } from 'lit/decorators.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
+ /**
13
+ * Address text view component (text only, no image)
14
+ *
15
+ * A text-only address display component that shows name and/or address.
16
+ * Use alongside <icon-view> for complete address presentation.
17
+ *
18
+ * @example
19
+ * ```html
20
+ * <address-view
21
+ * address="0x1234..."
22
+ * size="medium"
23
+ * ></address-view>
24
+ * ```
25
+ */
26
+ @safeCustomElement('address-view')
27
+ export class AddressView extends CoreLitElement {
28
+ /**
29
+ * The address to display (required)
30
+ */
31
+ @property() address!: string
32
+
33
+ /**
34
+ * Display size
35
+ */
36
+ @property() size: 'x-small' | 'small' | 'medium' | 'large' = 'small'
37
+
38
+ /**
39
+ * Whether to show only the name (no address)
40
+ */
41
+ @property({ type: Boolean, attribute: 'name-only' }) nameOnly = false
42
+
43
+ /**
44
+ * Whether to show only the address (no name)
45
+ */
46
+ @property({ type: Boolean, attribute: 'address-only' }) addressOnly = false
47
+
48
+ /**
49
+ * Card view: render with bordered container, header label and copy button
50
+ */
51
+ @property({ type: Boolean, attribute: 'is-card-view' }) isCardView = false
52
+
53
+ /**
54
+ * Optional label to show in card header (only when isCardView)
55
+ */
56
+ @property() label?: string
57
+
58
+ /**
59
+ * Whether to show a copy button (only relevant for card view)
60
+ */
61
+ @property({ type: Boolean, attribute: 'show-copy-button' }) showCopyButton =
62
+ false
63
+
64
+ /**
65
+ * Maximum width for the display (in pixels)
66
+ */
67
+ @property({ type: Number, attribute: 'max-width' }) maxWidth = 200
68
+
69
+ /**
70
+ * Whether to show the small identicon next to the name/address
71
+ */
72
+ @property({ type: Boolean, attribute: 'show-identicon' }) showIdenticon = true
73
+
74
+ /**
75
+ * Address text color token (e.g., "red-55")
76
+ */
77
+ @property({ type: String, attribute: 'address-color' }) addressColor?: string
78
+
79
+ /**
80
+ * Name text color token (e.g., "blue-50")
81
+ */
82
+ @property({ type: String, attribute: 'name-color' }) nameColor?: string
83
+
84
+ // Internal state
85
+ @state() private isLoadingAddress = false
86
+ @state() private identicon?: string
87
+
88
+ // Composables
89
+ private addressResolver = useAddressResolver()
90
+
91
+ // Task for resolving address data
92
+ private addressTask = new Task(this, {
93
+ task: async ([address], { signal }) => {
94
+ if (!address) return null
95
+
96
+ this.isLoadingAddress = true
97
+ try {
98
+ // Pass the full compound address to resolver (e.g., "0x123...-0x456...")
99
+ const resolved = await this.addressResolver.resolveAddress(address)
100
+ this.requestUpdate() // Force re-render after resolution
101
+ return resolved
102
+ } catch (error) {
103
+ return null
104
+ } finally {
105
+ this.isLoadingAddress = false
106
+ }
107
+ },
108
+ args: () => [this.address],
109
+ })
110
+
111
+ /**
112
+ * Get the display address (first segment only for compound addresses)
113
+ * e.g., "0x123...-0x456..." becomes "0x123..."
114
+ */
115
+ private get displayAddress(): string {
116
+ return this.address.split('-')[0]
117
+ }
118
+
119
+ /**
120
+ * Get display name for the address
121
+ */
122
+ private getDisplayName(): string {
123
+ const resolved = this.addressTask.value
124
+
125
+ if (!resolved) {
126
+ return this.isLoadingAddress ? 'Loading...' : 'Anonymous'
127
+ }
128
+ const computedName = this.addressResolver.formatDisplayName(resolved)
129
+ return this.label || computedName || 'Anonymous'
130
+ }
131
+
132
+ /**
133
+ * Check if this is a contract address (non-profile)
134
+ */
135
+ private get isContract(): boolean {
136
+ const resolved = this.addressTask.value
137
+ return resolved ? resolved.__gqltype !== 'Profile' : false
138
+ }
139
+
140
+ /**
141
+ * Check if this should show the address hash (profiles show hash, contracts don't)
142
+ */
143
+ private get shouldShowAddressHash(): boolean {
144
+ const resolved = this.addressTask.value
145
+ return resolved ? resolved.__gqltype === 'Profile' : true // Default to true for fallback
146
+ }
147
+
148
+ /**
149
+ * Get prefix for the address display
150
+ */
151
+ private get addressPrefix(): string {
152
+ const resolved = this.addressTask.value
153
+ return resolved?.hasProfile ? '@' : ''
154
+ }
155
+
156
+ /**
157
+ * Check if should show name colors
158
+ */
159
+ private get shouldShowNameColor(): boolean {
160
+ const resolved = this.addressTask.value
161
+ return resolved ? this.addressResolver.shouldShowNameColor(resolved) : true
162
+ }
163
+
164
+ /**
165
+ * Handle address click
166
+ */
167
+ private handleClick() {
168
+ this.dispatchEvent(
169
+ new CustomEvent('address-click', {
170
+ detail: {
171
+ address: this.address,
172
+ resolved: this.addressTask.value,
173
+ },
174
+ bubbles: true,
175
+ })
176
+ )
177
+ }
178
+
179
+ /** Generate identicon when address changes */
180
+ protected willUpdate(changed: Map<string, unknown>) {
181
+ if (changed.has('address') && this.address) {
182
+ try {
183
+ this.identicon = makeBlockie(this.displayAddress.toLowerCase())
184
+ } catch {
185
+ this.identicon = undefined
186
+ }
187
+ }
188
+ }
189
+
190
+ /** Copy the display address to clipboard (card view) */
191
+ private handleCopy(event?: Event) {
192
+ // Prevent triggering parent click handlers
193
+ if (event) event.stopPropagation()
194
+ if (navigator.clipboard) {
195
+ navigator.clipboard.writeText(this.displayAddress)
196
+ this.dispatchEvent(
197
+ new CustomEvent('address-copied', {
198
+ detail: { address: this.displayAddress },
199
+ bubbles: true,
200
+ })
201
+ )
202
+ }
203
+ }
204
+
205
+ /** ========== RENDER HELPERS (Use Cases) ========== */
206
+ /** Pixel size mapping for identicon based on component size */
207
+ private getIdenticonSizePx(): number {
208
+ switch (this.size) {
209
+ case 'x-small':
210
+ return 10
211
+ case 'small':
212
+ return 12
213
+ case 'medium':
214
+ return 14
215
+ case 'large':
216
+ return 16
217
+ default:
218
+ return 14
219
+ }
220
+ }
221
+ /**
222
+ * Render only the name (and optionally the address hash when allowed for profiles)
223
+ */
224
+ private renderNameOnly(displayName: string) {
225
+ return html`
226
+ <!-- Name Only Use Case -->
227
+ <lukso-username
228
+ class="inline-block align-middle"
229
+ name=${displayName}
230
+ address=${this.address}
231
+ size=${this.size}
232
+ max-width=${this.maxWidth}
233
+ prefix=${this.addressPrefix}
234
+ address-color=${this.addressColor}
235
+ name-color=${this.nameColor}
236
+ ></lukso-username>
237
+ `
238
+ }
239
+
240
+ /**
241
+ * Render only the raw address (no name)
242
+ */
243
+ private renderAddressOnly() {
244
+ return html`
245
+ <!-- Address Only Use Case -->
246
+ <lukso-username
247
+ class="inline-block align-middle"
248
+ address=${this.displayAddress}
249
+ size=${this.size}
250
+ max-width=${this.maxWidth}
251
+ has-name-color="false"
252
+ ?has-address-color=${this.shouldShowNameColor}
253
+ ></lukso-username>
254
+ `
255
+ }
256
+
257
+ /**
258
+ * Render both name and (conditionally) the address hash (profiles show hash, contracts may hide it)
259
+ */
260
+ private renderNameAndAddress(displayName: string) {
261
+ return html`
262
+ <!-- Name + Address Use Case -->
263
+ <lukso-username
264
+ class="inline-block align-middle"
265
+ name=${displayName}
266
+ address=${this.address}
267
+ size=${this.size}
268
+ max-width=${this.maxWidth}
269
+ prefix=${this.addressPrefix}
270
+ address-color=${this.addressColor}
271
+ name-color=${this.nameColor}
272
+ ></lukso-username>
273
+ `
274
+ }
275
+
276
+ private renderIdenticon() {
277
+ if (!this.showIdenticon || !this.identicon) return ''
278
+ const size = this.getIdenticonSizePx()
279
+ return html`
280
+ <div class="inline-flex items-center justify-center align-middle">
281
+ <img
282
+ class="inline-block rounded-full ring-1 ring-neutral-90 shrink-0"
283
+ src=${this.identicon}
284
+ alt="identicon"
285
+ style="width:${size}px;height:${size}px;image-rendering:pixelated;"
286
+ />
287
+ </div>
288
+ `
289
+ }
290
+
291
+ /** Map size to a typography class for the label */
292
+ private getLabelClass(): string {
293
+ switch (this.size) {
294
+ case 'x-small':
295
+ return 'paragraph-inter-12-semi-bold'
296
+ case 'small':
297
+ return 'heading-inter-14-bold'
298
+ case 'medium':
299
+ return 'heading-inter-17-semi-bold'
300
+ case 'large':
301
+ return 'heading-inter-21-semi-bold'
302
+ default:
303
+ return 'heading-inter-17-semi-bold'
304
+ }
305
+ }
306
+
307
+ private renderDisplayName() {
308
+ const displayName = this.getDisplayName()
309
+ return this.label
310
+ ? html`<div class="inline-flex items-center gap-2 font-semibold text-neutral-20 ${this.getLabelClass()}">${
311
+ this.label
312
+ }</div>`
313
+ : html`<lukso-username
314
+ class="inline-block align-middle"
315
+ name=${displayName}
316
+ size=${this.size}
317
+ ?has-name-color=${this.shouldShowNameColor}
318
+ ></lukso-username>`
319
+ }
320
+
321
+ /**
322
+ * Decide which of the three core inline variants to render
323
+ */
324
+ private renderInlineVariant(displayName: string) {
325
+ if (this.nameOnly) return this.renderNameOnly(displayName)
326
+ if (this.addressOnly) return this.renderAddressOnly()
327
+ return this.renderNameAndAddress(displayName)
328
+ }
329
+
330
+ override render() {
331
+ const displayName = this.getDisplayName()
332
+ // Card view layout
333
+ if (this.isCardView) {
334
+ return html`
335
+ <div class="block border border-neutral-90 rounded-xl py-3 px-4 bg-neutral-100 min-w-[280px]" @click=${
336
+ this.handleClick
337
+ }>
338
+ <div class="flex items-center justify-between mb-2">
339
+ <div class="inline-flex items-center font-normal gap-1">
340
+ ${this.renderIdenticon()}
341
+ ${this.renderDisplayName()}
342
+ </div>
343
+ ${
344
+ this.showCopyButton
345
+ ? html`<button
346
+ class="bg-neutral-100 border-0 rounded-lg w-6 h-6 flex items-center justify-center cursor-pointer text-neutral-20 hover:bg-neutral-95"
347
+ @click=${(e: Event) => this.handleCopy(e)}
348
+ title="Copy address"
349
+ >
350
+ <lukso-icon name="copy" size="small"></lukso-icon>
351
+ </button>`
352
+ : html``
353
+ }
354
+ </div>
355
+ <div class="font-semibold text-neutral-20 break-all flex items-center gap-2">
356
+
357
+ <lukso-username
358
+ address=${this.displayAddress}
359
+ size=${this.size}
360
+ max-width=${this.maxWidth}
361
+ has-name-color="false"
362
+ slice-by="24"
363
+ ?has-address-color=${this.shouldShowNameColor}
364
+ ></lukso-username>
365
+ </div>
366
+ </div>
367
+ `
368
+ }
369
+
370
+ // Inline/simple view (default)
371
+ return html`
372
+ <div class="inline-flex items-center cursor-pointer gap-1" @click=${this.handleClick}>
373
+ ${this.renderIdenticon()}
374
+ <!-- Inline Variants -->
375
+ ${this.renderInlineVariant(displayName)}
376
+ </div>
377
+ `
378
+ }
379
+ }
380
+
381
+ declare global {
382
+ interface HTMLElementTagNameMap {
383
+ 'address-view': AddressView
384
+ }
385
+ }
@@ -0,0 +1,164 @@
1
+ import { html } from 'lit'
2
+ import { customElement, property, query, state } from 'lit/decorators.js'
3
+ import { safeCustomElement } from './safe-custom-element'
4
+ import './load-web-components'
5
+ import makeBlockie from 'ethereum-blockies-base64'
6
+ import { CoreLitElement } from '../styles'
7
+
8
+ export interface SelectOption {
9
+ value: string
10
+ label: string
11
+ disabled: boolean
12
+ }
13
+
14
+ @safeCustomElement('custom-option')
15
+ export class CustomOption extends CoreLitElement {
16
+ @property({ type: Boolean, reflect: true }) selected = false
17
+ @property({ type: Boolean, reflect: true }) disabled = false
18
+ @property({ type: Boolean, reflect: true }) hoverable = true
19
+
20
+ override render() {
21
+ const base =
22
+ 'flex flex-row flex-1 items-center px-2 py-1 gap-2 h-7 rounded-md border-0 cursor-pointer text-[14px] text-neutral-20 w-full justify-between'
23
+ const hover = this.hoverable ? ' hover:bg-neutral-95' : ''
24
+ const selected = this.selected ? ' bg-neutral-90 rounded' : ''
25
+ const disabled = this.disabled ? ' text-neutral-60 cursor-not-allowed' : ''
26
+ return html`<button class="${base}${hover}${selected}${disabled}" ?disabled=${this.disabled}><slot></slot></button>`
27
+ }
28
+ }
29
+
30
+ @safeCustomElement('custom-select')
31
+ export class CustomSelect extends CoreLitElement {
32
+ @property() value?: string
33
+ @property({ type: Boolean }) open = false
34
+ @property({ type: Array }) options: SelectOption[] = []
35
+ @property({ type: String }) size: 'x-small' | 'small' | 'medium' | 'large' =
36
+ 'small'
37
+
38
+ private toggleOpen() {
39
+ this.open = !this.open
40
+ }
41
+
42
+ private close() {
43
+ this.open = false
44
+ }
45
+
46
+ private onOptionClick(opt: SelectOption) {
47
+ if (opt.disabled) return
48
+ this.value = opt.value
49
+ this.dispatchEvent(new Event('change', { bubbles: true }))
50
+ this.close()
51
+ }
52
+
53
+ private onKeyDown(e: KeyboardEvent) {
54
+ if (e.key === 'Escape') this.close()
55
+ }
56
+
57
+ private onOutsideClick = (e: MouseEvent) => {
58
+ if (!this.open) return
59
+ const path = e.composedPath() as Array<EventTarget & Element>
60
+ if (!path.includes(this)) this.close()
61
+ }
62
+
63
+ override connectedCallback(): void {
64
+ super.connectedCallback()
65
+ window.addEventListener('click', this.onOutsideClick, { capture: true })
66
+ }
67
+
68
+ override disconnectedCallback(): void {
69
+ window.removeEventListener('click', this.onOutsideClick, { capture: true })
70
+ super.disconnectedCallback()
71
+ }
72
+
73
+ override updated() {
74
+ // reflect initial selected by value
75
+ const selectedOption = this.getSelectedOption()
76
+ if (selectedOption) {
77
+ this.value = selectedOption.value
78
+ }
79
+ }
80
+
81
+ private getSelectedOption() {
82
+ return this.options.find((o) => o.value === this.value)
83
+ }
84
+
85
+ private getIdenticonSizePx(): number {
86
+ switch (this.size) {
87
+ case 'x-small':
88
+ return 10
89
+ case 'small':
90
+ return 12
91
+ case 'medium':
92
+ return 14
93
+ case 'large':
94
+ return 16
95
+ default:
96
+ return 14
97
+ }
98
+ }
99
+
100
+ private renderIdenticon(address: string) {
101
+ const size = this.getIdenticonSizePx()
102
+ const identicon = makeBlockie(address.toLowerCase())
103
+ return html`
104
+ <div class="inline-flex items-center p-1 justify-center rounded-lg bg-neutral-100 border border-neutral-90 overflow-hidden flex-none">
105
+ <img
106
+ class="block"
107
+ src=${identicon}
108
+ alt="identicon"
109
+ style="width:${size}px;height:${size}px;"
110
+ />
111
+ </div>
112
+ `
113
+ }
114
+
115
+ private renderOptionLabel(option: SelectOption) {
116
+ return option.value.startsWith('0x')
117
+ ? html`<div class="inline-flex items-center gap-2 px-2.5 py-1.5 text-neutral-20 leading-tight font-semibold">${this.renderIdenticon(
118
+ option.value
119
+ )} <span class="whitespace-nowrap overflow-hidden text-ellipsis text-[14px]">${
120
+ option.label
121
+ }</span></div>`
122
+ : html`<url-view url=${option.value} label=${option.label} size="x-small"></url-view>`
123
+ }
124
+
125
+ override render() {
126
+ const selectedOption = this.getSelectedOption()
127
+ return html`
128
+ <div class="relative w-full min-w-[360px]" @keydown=${this.onKeyDown}>
129
+ <button class="box-border flex flex-row flex-1 w-full items-center h-[29px] bg-white border border-neutral-90 rounded-lg text-neutral-20 cursor-pointer justify-between" @click=${
130
+ this.toggleOpen
131
+ } aria-haspopup="listbox" aria-expanded=${this.open}>
132
+ <custom-option is-full-width .hoverable=${false}>
133
+ <div class="flex flex-row flex-1 items-center justify-between">
134
+ <span>${selectedOption ? this.renderOptionLabel(selectedOption) : 'Select option'}</span>
135
+ <lukso-icon class="transition-transform ${this.open ? 'rotate-180' : ''}" name="arrow-down-sm" size="small" color="neutral-20" aria-hidden="true">
136
+ </lukso-icon>
137
+ </div>
138
+ </custom-option>
139
+ </button>
140
+
141
+ ${
142
+ this.open && this.options.length > 0
143
+ ? html`<div class="absolute left-0 top-[calc(100%+8px)] w-full bg-white border border-neutral-90 shadow-[0_0_16px_rgba(189,206,219,0.4)] rounded-xl p-2.5 flex flex-col gap-1 z-[1000] overflow-hidden box-border" role="listbox">
144
+ ${this.options.map(
145
+ (
146
+ opt
147
+ ) => html`<custom-option .selected=${opt.value === this.value} .disabled=${opt.disabled} @click=${() => this.onOptionClick(opt)}>
148
+ ${this.renderOptionLabel(opt)}
149
+ </custom-option>`
150
+ )}
151
+ </div>`
152
+ : ''
153
+ }
154
+ </div>
155
+ `
156
+ }
157
+ }
158
+
159
+ declare global {
160
+ interface HTMLElementTagNameMap {
161
+ 'custom-select': CustomSelect
162
+ 'custom-option': CustomOption
163
+ }
164
+ }
@@ -0,0 +1,89 @@
1
+ import { html } from 'lit'
2
+ import { customElement, property } from 'lit/decorators.js'
3
+ import { safeCustomElement } from './safe-custom-element'
4
+ import './load-web-components'
5
+ import { CoreLitElement } from '../styles'
6
+
7
+ /**
8
+ * Compact function name badge with icon
9
+ * Props:
10
+ * - label (deprecated): text to display; use default slot instead
11
+ * - icon: lukso-icon name
12
+ * - size: x-small | small | medium | large (affects paddings and font)
13
+ * - color: optional lukso-icon color token (e.g., "blue-50")
14
+ */
15
+ @safeCustomElement('function-name-view')
16
+ export class FunctionNameView extends CoreLitElement {
17
+ /**
18
+ * Deprecated: Prefer slot content for the label. Will be used as fallback
19
+ * when no slot is provided to maintain backward compatibility.
20
+ */
21
+ @property() label = ''
22
+ @property() icon: string = 'user'
23
+ @property() size: 'x-small' | 'small' | 'medium' | 'large' = 'small'
24
+ @property({ attribute: 'color' }) color?: string
25
+
26
+ private getPadding(): string {
27
+ switch (this.size) {
28
+ case 'x-small':
29
+ return '2px 8px'
30
+ case 'small':
31
+ return '4px 10px'
32
+ case 'medium':
33
+ return '6px 12px'
34
+ case 'large':
35
+ return '8px 14px'
36
+ default:
37
+ return '4px 10px'
38
+ }
39
+ }
40
+
41
+ private getFontSize(): string {
42
+ switch (this.size) {
43
+ case 'x-small':
44
+ return '12px'
45
+ case 'small':
46
+ return '14px'
47
+ case 'medium':
48
+ return '16px'
49
+ case 'large':
50
+ return '18px'
51
+ default:
52
+ return '14px'
53
+ }
54
+ }
55
+
56
+ private getIconSize(): 'x-small' | 'small' | 'medium' | 'large' {
57
+ return this.size
58
+ }
59
+
60
+ private getHeight(): string {
61
+ switch (this.size) {
62
+ case 'x-small':
63
+ return 'h-6'
64
+ case 'small':
65
+ return 'h-8'
66
+ case 'medium':
67
+ return 'h-10'
68
+ case 'large':
69
+ return 'h-12'
70
+ default:
71
+ return 'h-8'
72
+ }
73
+ }
74
+
75
+ override render() {
76
+ return html`
77
+ <div class="inline-flex ${this.getHeight()} items-center gap-2 bg-neutral-90 text-neutral-20 rounded-xl leading-none font-semibold" style="padding:${this.getPadding()}; font-size:${this.getFontSize()};">
78
+ <lukso-icon name=${this.icon} size=${this.getIconSize()} color=${this.color || ''}></lukso-icon>
79
+ <span class="whitespace-nowrap"><slot>${this.label}</slot></span>
80
+ </div>
81
+ `
82
+ }
83
+ }
84
+
85
+ declare global {
86
+ interface HTMLElementTagNameMap {
87
+ 'function-name-view': FunctionNameView
88
+ }
89
+ }