Skip to content

Commit d78568f

Browse files
Merge pull request #258 from multiversx/development
0.1.0
2 parents 28feb74 + fb60634 commit d78568f

File tree

67 files changed

+1007
-749
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

67 files changed

+1007
-749
lines changed

CHANGELOG.md

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
11+
## [[0.1.0](https://github.com/multiversx/mx-sdk-dapp-ui/pull/256)] - 2025-10-29
12+
13+
- [Fixed trim component state](https://github.com/multiversx/mx-sdk-dapp-ui/pull/259)
14+
- [Fixed main conflicts with main branch](https://github.com/multiversx/mx-sdk-dapp-ui/pull/257)
15+
- [Changed ppu to gasPriceOption in sign transactions panel](https://github.com/multiversx/mx-sdk-dapp-ui/pull/255)
16+
- [Fixed icons on batch transactions toast](https://github.com/multiversx/mx-sdk-dapp-ui/pull/253)
17+
- [Added enum for transaction status](https://github.com/multiversx/mx-sdk-dapp-ui/pull/254)
18+
- [Fixed finished toasts loader](https://github.com/multiversx/mx-sdk-dapp-ui/pull/252)
19+
- [Refactored utility components](https://github.com/multiversx/mx-sdk-dapp-ui/pull/249)
20+
1021
## [[0.0.36](https://github.com/multiversx/mx-sdk-dapp-ui/pull/251)] - 2025-10-15
1122

1223
- [Updated icons type](https://github.com/multiversx/mx-sdk-dapp-ui/pull/250)
13-
1424
- [Added the Storybook as a live server deployment](https://github.com/multiversx/mx-sdk-dapp-ui/pull/248)
15-
25+
- [Updated icons type](https://github.com/multiversx/mx-sdk-dapp-ui/pull/250)
26+
- [Added the Storybook as a live server deployment](https://github.com/multiversx/mx-sdk-dapp-ui/pull/248)
1627
- [Refactor components in transactions table](https://github.com/multiversx/mx-sdk-dapp-ui/pull/243)
1728

1829
## [[0.0.35](https://github.com/multiversx/mx-sdk-dapp-ui/pull/246)] - 2025-10-10

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@multiversx/sdk-dapp-ui",
3-
"version": "0.0.36",
3+
"version": "0.1.0",
44
"description": "A library to hold UI components for a dApp on the MultiversX blockchain",
55
"author": "MultiversX",
66
"license": "MIT",
@@ -132,4 +132,4 @@
132132
"typescript": "^5.7.3",
133133
"vite": "^7.0.6"
134134
}
135-
}
135+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { h } from '@stencil/core';
2+
import { Icon } from 'common/Icon';
3+
4+
// prettier-ignore
5+
const styles = {
6+
explorerLink: 'explorer-link mvx:decoration-0 mvx:flex',
7+
explorerLinkIcon: 'explorer-link-icon mvx:flex mvx:justify-center mvx:transition-opacity mvx:duration-200 mvx:ease-in-out mvx:hover:opacity-80'
8+
} satisfies Record<string, string>;
9+
10+
interface ExplorerLinkPropsType {
11+
class?: string;
12+
iconClass?: string;
13+
dataTestId?: string;
14+
link: string;
15+
hasIcon?: boolean;
16+
}
17+
18+
export function ExplorerLink({ class: className, iconClass, dataTestId, link, hasIcon }: ExplorerLinkPropsType, children?: JSX.Element) {
19+
if (!link) {
20+
return null;
21+
}
22+
23+
return (
24+
<a
25+
target="_blank"
26+
rel="noreferrer"
27+
href={link}
28+
data-testid={dataTestId}
29+
class={{ [styles.explorerLink]: true, [className]: Boolean(className) }}
30+
>
31+
{hasIcon ?
32+
(<Icon
33+
name="arrow-up-right-from-square-icon"
34+
class={{ [styles.explorerLinkIcon]: true, [iconClass]: Boolean(iconClass) }}
35+
/>)
36+
: children}
37+
</a>
38+
);
39+
40+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { InvalidFormatAmount, ValidFormatAmount } from "./components";
2+
3+
interface FormatAmountPropsType {
4+
class?: string;
5+
dataTestId?: string;
6+
isValid: boolean;
7+
label?: string;
8+
labelClass?: string;
9+
showLabel?: boolean;
10+
valueDecimal: string;
11+
valueInteger: string;
12+
decimalClass?: string;
13+
}
14+
15+
export function FormatAmount({ class: className, dataTestId, isValid, label, labelClass, showLabel = true, valueDecimal, valueInteger, decimalClass }: FormatAmountPropsType) {
16+
return isValid ?
17+
ValidFormatAmount({ dataTestId, class: className, valueInteger, valueDecimal, decimalClass, showLabel, label, labelClass })
18+
: InvalidFormatAmount({ dataTestId, class: className });
19+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { h } from '@stencil/core';
2+
import { DataTestIdsEnum } from 'constants/dataTestIds.enum';
3+
4+
// prettier-ignore
5+
const styles = {
6+
intAmount: 'int-amount mvx:text-inherit',
7+
} satisfies Record<string, string>;
8+
9+
interface InvalidFormatAmountPropsType {
10+
class?: string;
11+
dataTestId?: string;
12+
}
13+
14+
export function InvalidFormatAmount({ dataTestId, class: className }: InvalidFormatAmountPropsType) {
15+
return (
16+
<span data-testid={dataTestId ?? DataTestIdsEnum.formatAmountComponent} class={className}>
17+
<span class={styles.intAmount} data-testid={DataTestIdsEnum.formatAmountInt}>
18+
...
19+
</span>
20+
</span>
21+
);
22+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { h } from '@stencil/core';
2+
import classNames from 'classnames';
3+
import { DataTestIdsEnum } from 'constants/dataTestIds.enum';
4+
5+
// prettier-ignore
6+
const styles = {
7+
formatAmount: 'format-amount mvx:items-center',
8+
intAmount: 'int-amount mvx:text-inherit',
9+
decimals: 'decimals mvx:text-inherit',
10+
symbol: 'symbol mvx:text-inherit mvx:ml-1'
11+
} satisfies Record<string, string>;
12+
13+
interface ValidFormatAmountPropsType {
14+
class?: string;
15+
dataTestId?: string;
16+
label?: string;
17+
labelClass?: string;
18+
showLabel?: boolean;
19+
valueDecimal: string;
20+
valueInteger: string;
21+
decimalClass?: string;
22+
}
23+
24+
export function ValidFormatAmount({
25+
dataTestId,
26+
class: className,
27+
valueInteger,
28+
valueDecimal,
29+
decimalClass,
30+
showLabel,
31+
label,
32+
labelClass
33+
}: ValidFormatAmountPropsType) {
34+
return (
35+
<span data-testid={dataTestId} class={classNames(className, styles.formatAmount)}>
36+
<span class={styles.intAmount} data-testid={DataTestIdsEnum.formatAmountInt}>
37+
{valueInteger}
38+
</span>
39+
{valueDecimal && (
40+
<span
41+
class={{ [styles.decimals]: true, [decimalClass]: Boolean(decimalClass) }}
42+
data-testid={DataTestIdsEnum.formatAmountDecimals}
43+
>
44+
{valueDecimal}
45+
</span>
46+
)}
47+
{showLabel && label && (
48+
<span
49+
class={{
50+
[styles.symbol]: true,
51+
[labelClass]: Boolean(labelClass),
52+
}}
53+
data-testid={DataTestIdsEnum.formatAmountSymbol}
54+
>
55+
{label}
56+
</span>
57+
)}
58+
</span>
59+
);
60+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from './InvalidFormatAmount';
2+
export * from './ValidFormatAmount'

src/common/Icon/Icon.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,25 @@ import { AngleRightIcon } from './components/AngleRightIcon';
66
import { AnglesLeftIcon } from './components/AnglesLeftIcon';
77
import { AnglesRightIcon } from './components/AnglesRightIcon';
88
import { AngleUpIcon } from './components/AngleUpIcon';
9+
import { ArrowsRotateIcon } from './components/ArrowsRotateIcon';
910
import { ArrowUpRightFromSquareIcon } from './components/ArrowUpRightFromSquare';
1011
import { ArrowUpRightIcon } from './components/ArrowUpRightIcon';
1112
import { BackArrowIcon } from './components/BackArrowIcon';
13+
import { BanIcon } from './components/BanIcon';
1214
import { CheckIcon } from './components/CheckIcon';
15+
import { CircleCheckIcon } from './components/CircleCheckIcon';
1316
import { CircleExclamationIcon } from './components/CircleExclamationIcon';
17+
import { CircleInfoIcon } from './components/CircleInfoIcon';
1418
import { CloseIcon } from './components/CloseIcon';
19+
import { CoinsIcon } from './components/CoinsIcon';
1520
import { ContractIcon } from './components/ContractIcon';
1621
import { CopyIcon } from './components/CopyIcon';
22+
import { HourglassIcon } from './components/HourglassIcon';
1723
import { LayersIcon } from './components/LayersIcon';
1824
import { LockIcon } from './components/LockIcon';
1925
import { PencilIcon } from './components/PencilIcon';
2026
import { TriangularWarningIcon } from './components/TriangularWarningIcon';
2127
import type { IconPropsType } from './icon.types';
22-
import { HourglassIcon } from './components/HourglassIcon';
23-
import { BanIcon } from './components/BanIcon';
24-
import { CircleCheckIcon } from './components/CircleCheckIcon';
25-
import { CircleInfoIcon } from './components/CircleInfoIcon';
26-
import { CoinsIcon } from './components/CoinsIcon';
27-
import { ArrowsRotateIcon } from './components/ArrowsRotateIcon';
2828

2929
export const Icon = ({ name, ...properties }: IconPropsType) => {
3030
if (!name) {

src/common/Trim/Trim.tsx

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
import { h } from '@stencil/core';
2+
import { DataTestIdsEnum } from 'constants/dataTestIds.enum';
3+
import { ELLIPSIS } from 'constants/htmlStrings';
4+
import { safeWindow } from 'constants/window.constants';
5+
6+
import styles from './trim.styles'
7+
8+
interface TrimPropsType {
9+
dataTestId?: string;
10+
class?: string;
11+
text: string;
12+
}
13+
14+
export function Trim({
15+
dataTestId = DataTestIdsEnum.trim,
16+
class: className,
17+
text
18+
}: TrimPropsType) {
19+
let fullWidthUntrimmedElementReference: HTMLDivElement;
20+
let trimElementReference: HTMLDivElement;
21+
let resizeObserver: ResizeObserver;
22+
let currentTrimFontSize = '1rem';
23+
let trimFullElement: HTMLDivElement;
24+
let trimWrapperElement: HTMLDivElement;
25+
26+
const handleTrimElementReference = (element: HTMLDivElement) => {
27+
if (element) {
28+
trimElementReference = element;
29+
setupResizeObserver();
30+
requestAnimationFrame(checkOverflow);
31+
}
32+
}
33+
34+
const handleFullWidthTrimElementReference = (element: HTMLDivElement) => {
35+
if (element) {
36+
fullWidthUntrimmedElementReference = element;
37+
}
38+
}
39+
40+
const handleTrimFullRef = (element: HTMLDivElement) => {
41+
if (element) {
42+
trimFullElement = element;
43+
}
44+
};
45+
46+
const handleTrimWrapperRef = (element: HTMLDivElement) => {
47+
if (element) {
48+
trimWrapperElement = element;
49+
}
50+
};
51+
52+
const setupResizeObserver = () => {
53+
if (resizeObserver) {
54+
resizeObserver.disconnect();
55+
}
56+
57+
resizeObserver = new ResizeObserver(() => {
58+
checkOverflow();
59+
});
60+
61+
if (trimElementReference) {
62+
resizeObserver.observe(trimElementReference);
63+
}
64+
}
65+
66+
const checkOverflow = () => {
67+
if (!fullWidthUntrimmedElementReference || !trimElementReference || !trimFullElement || !trimWrapperElement) {
68+
return;
69+
}
70+
71+
const hiddenFullWidthElementWidth = fullWidthUntrimmedElementReference.scrollWidth;
72+
const trimmedElementWidth = trimElementReference.clientWidth;
73+
const isTrimElementOverflowing = hiddenFullWidthElementWidth > trimmedElementWidth;
74+
75+
if (safeWindow) {
76+
currentTrimFontSize = safeWindow.getComputedStyle(trimElementReference).fontSize;
77+
}
78+
79+
const getIdentifierClass = (classes: string) => classes.split(' ')[0];
80+
81+
const trimLeftSelector = `.${getIdentifierClass(styles.trimLeft)}`;
82+
const trimRightSelector = `.${getIdentifierClass(styles.trimRight)}`;
83+
84+
const trimLeftElement = trimElementReference.querySelector(trimLeftSelector) as HTMLElement;
85+
const trimRightElement = trimElementReference.querySelector(trimRightSelector) as HTMLElement;
86+
if (trimLeftElement) {
87+
trimLeftElement.style.fontSize = currentTrimFontSize;
88+
}
89+
90+
if (trimRightElement) {
91+
trimRightElement.style.fontSize = currentTrimFontSize;
92+
}
93+
94+
const trimFullVisibleClasses = styles.trimFullVisible.split(/\s+/);
95+
const trimWrapperVisibleClasses = styles.trimWrapperVisible.split(/\s+/);
96+
97+
if (isTrimElementOverflowing) {
98+
trimFullElement.classList.remove(...trimFullVisibleClasses);
99+
trimWrapperElement.classList.add(...trimWrapperVisibleClasses);
100+
} else {
101+
trimFullElement.classList.add(...trimFullVisibleClasses);
102+
trimWrapperElement.classList.remove(...trimWrapperVisibleClasses);
103+
}
104+
};
105+
106+
const middleTextIndex = Math.floor(text.length / 2);
107+
const leftHandText = text.slice(0, middleTextIndex);
108+
const rightHandText = text.slice(middleTextIndex);
109+
110+
return (
111+
<div
112+
data-testid={dataTestId}
113+
ref={handleTrimElementReference}
114+
class={{ [styles.trim]: true, [className]: Boolean(className) }}
115+
>
116+
<div
117+
data-testid={DataTestIdsEnum.trimFullAddress}
118+
ref={el => {
119+
handleFullWidthTrimElementReference(el);
120+
handleTrimFullRef(el);
121+
}}
122+
class={styles.trimFull}
123+
>
124+
{text}
125+
</div>
126+
127+
<div ref={handleTrimWrapperRef} class={styles.trimWrapper}>
128+
<div class={styles.trimLeftWrapper}>
129+
<div class={styles.trimLeft} style={{ fontSize: currentTrimFontSize }}>
130+
{leftHandText}
131+
</div>
132+
</div>
133+
134+
<div class={styles.trimEllipsisWrapper}>
135+
<div class={styles.trimEllipsis}>{ELLIPSIS}</div>
136+
</div>
137+
138+
<div class={styles.trimRightWrapper} style={{ direction: 'rtl' }}>
139+
<div class={styles.trimRight} style={{ fontSize: currentTrimFontSize }}>
140+
{rightHandText}
141+
</div>
142+
</div>
143+
</div>
144+
</div>
145+
);
146+
}

src/common/Trim/trim.styles.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// prettier-ignore
2+
export default {
3+
trim: 'trim mvx:flex mvx:relative mvx:max-w-full mvx:overflow-hidden mvx:whitespace-nowrap',
4+
trimFull: 'trim-full mvx:text-transparent mvx:absolute mvx:leading-5',
5+
trimFullVisible: 'trim-full-visible mvx:!text-inherit mvx:relative mvx:leading-5',
6+
trimWrapper: 'trim-wrapper mvx:hidden',
7+
trimWrapperVisible: 'trim-wrapper-visible mvx:overflow-hidden mvx:max-w-full mvx:flex',
8+
trimEllipsisWrapper: 'trim-ellipsis-wrapper mvx:block mvx:flex-shrink-0 mvx:pointer-events-none mvx:select-none',
9+
trimEllipsis: 'trim-ellipsis mvx:block mvx:leading-5',
10+
trimLeftWrapper: 'trim-left-wrapper mvx:flex-shrink mvx:text-ellipsis mvx:overflow-hidden mvx:text-left mvx:text-[1px]',
11+
trimLeft: 'trim-left mvx:select-none mvx:pointer-events-none mvx:inline mvx:text-base mvx:leading-5 mvx:-webkit-letter-spacing',
12+
trimRightWrapper: 'trim-right-wrapper mvx:flex-shrink mvx:text-ellipsis mvx:overflow-hidden mvx:whitespace-nowrap mvx:text-right mvx:text-[1px]',
13+
trimRight: 'trim-right mvx:select-none mvx:pointer-events-none mvx:inline mvx:text-base mvx:leading-5 mvx:text-clip mvx:-webkit-letter-spacing',
14+
trimStoriesWrapper: 'trim-stories-wrapper mvx:text-primary'
15+
} satisfies Record<string, string>;

0 commit comments

Comments
 (0)