From acd09df409cd4851435cc6e6031e4e6d16d9c66a Mon Sep 17 00:00:00 2001 From: Iulia Cimpeanu Date: Wed, 12 Nov 2025 15:41:53 +0200 Subject: [PATCH 1/9] Update styles --- .../sign-transactions-footer.scss | 68 +----- .../sign-transactions-footer.styles.ts | 18 ++ .../sign-transactions-footer.tsx | 211 +++++++++--------- .../sign-transactions-panel.tsx | 2 +- 4 files changed, 124 insertions(+), 175 deletions(-) create mode 100644 src/components/functional/sign-transactions-panel/components/sign-transactions-footer/sign-transactions-footer.styles.ts diff --git a/src/components/functional/sign-transactions-panel/components/sign-transactions-footer/sign-transactions-footer.scss b/src/components/functional/sign-transactions-panel/components/sign-transactions-footer/sign-transactions-footer.scss index 6b3ce95f..ff1190ee 100644 --- a/src/components/functional/sign-transactions-panel/components/sign-transactions-footer/sign-transactions-footer.scss +++ b/src/components/functional/sign-transactions-panel/components/sign-transactions-footer/sign-transactions-footer.scss @@ -1,67 +1 @@ -:host { - @apply mvx:flex mvx:flex-col mvx:flex-1; - - .sign-transactions-footer { - @apply mvx:mt-auto mvx:flex mvx:flex-col mvx:items-center mvx:pt-5 mvx:gap-5 mvx:text-center; - - .sign-transactions-footer-buttons { - @apply mvx:flex mvx:items-center mvx:w-full mvx:gap-3; - - .sign-transactions-footer-button-wrapper { - @apply mvx:relative mvx:flex mvx:flex-col mvx:flex-1; - - &.cancel { - @apply mvx:w-32 mvx:max-w-32; - } - - .sign-transactions-footer-button-tooltip-wrapper { - @apply mvx:absolute mvx:inset-0; - } - - .sign-transactions-footer-button { - @apply mvx:flex; - - .sign-transactions-footer-button-icon { - @apply mvx:flex mvx:transition-all mvx:duration-200 mvx:ease-in-out; - - &.lighter { - fill: var(--mvx-text-color-secondary); - } - } - } - } - } - - .sign-transactions-footer-identity { - @apply mvx:max-w-64 mvx:flex mvx:items-center mvx:text-base mvx:gap-2; - - .sign-transactions-footer-identity-label { - @apply mvx:whitespace-nowrap; - color: var(--mvx-text-color-secondary); - } - - .sign-transactions-footer-identity-address { - @apply mvx:relative mvx:min-w-0 mvx:max-w-none; - top: 0.25px; - color: var(--mvx-text-color-primary); - - .trim-wrapper { - @apply mvx:items-end mvx:leading-none; - } - } - - .sign-transactions-footer-identity-username { - @apply mvx:flex mvx:items-center mvx:text-base; - color: var(--mvx-text-color-primary); - - .sign-transactions-footer-identity-username-prefix { - color: var(--mvx-text-accent-color); - } - } - - .sign-transactions-footer-identity-copy { - color: var(--mvx-text-color-primary); - } - } - } -} +// This is needed to trigger the Stecil Tailwind compilation for inline Tailwind classes. \ No newline at end of file diff --git a/src/components/functional/sign-transactions-panel/components/sign-transactions-footer/sign-transactions-footer.styles.ts b/src/components/functional/sign-transactions-panel/components/sign-transactions-footer/sign-transactions-footer.styles.ts new file mode 100644 index 00000000..506d768c --- /dev/null +++ b/src/components/functional/sign-transactions-panel/components/sign-transactions-footer/sign-transactions-footer.styles.ts @@ -0,0 +1,18 @@ +export default { + signTransactionsFooterContainer: 'sign-transactions-footer-container mvx:flex mvx:flex-col mvx:flex-1 mvx:h-full', + signTransactionsFooter: 'sign-transactions-footer mvx:mt-auto mvx:flex mvx:flex-col mvx:items-center mvx:pt-5 mvx:gap-5 mvx:text-center', + signTransactionsFooterButtons: 'sign-transactions-footer-buttons mvx:flex mvx:items-center mvx:w-full mvx:gap-3', + signTransactionsFooterButtonWrapper: 'sign-transactions-footer-button-wrapper mvx:relative mvx:flex mvx:flex-col mvx:flex-1', + signTransactionsFooterButtonWrapperCancel: 'sign-transactions-footer-button-wrapper-cancel mvx:w-32 mvx:max-w-32', + signTransactionsFooterButtonTooltipWrapper: 'sign-transactions-footer-button-tooltip-wrapper mvx:absolute mvx:inset-0', + signTransactionsFooterButton: 'sign-transactions-footer-button mvx:flex', + signTransactionsFooterButtonIcon: 'sign-transactions-footer-button-icon mvx:flex mvx:transition-all mvx:duration-200 mvx:ease-in-out', + signTransactionsFooterButtonIconLighter: 'sign-transactions-footer-button-icon-lighter mvx:fill-secondary-text', + signTransactionsFooterIdentity: 'sign-transactions-footer-identity mvx:max-w-64 mvx:flex mvx:items-center mvx:text-base mvx:gap-2', + signTransactionsFooterIdentityLabel: 'sign-transactions-footer-identity-label mvx:whitespace-nowrap mvx:text-secondary-text', + signTransactionsFooterIdentityAddress: 'sign-transactions-footer-identity-address mvx:relative mvx:min-w-0 mvx:max-w-none mvx:top-[0.25px] mvx:text-primary', + signTransactionsTrimWrapper: 'sign-transactions-trim-wrapper mvx:items-end mvx:leading-none', + signTransactionsFooterIdentityUsername: 'sign-transactions-footer-identity-username mvx:flex mvx:items-center mvx:text-base mvx:text-primary', + signTransactionsFooterIdentityUsernamePrefix: 'sign-transactions-footer-identity-username-prefix mvx:text-accent', + signTransactionsFooterIdentityCopy: 'sign-transactions-footer-identity-copy mvx:text-primary' +} satisfies Record; \ No newline at end of file diff --git a/src/components/functional/sign-transactions-panel/components/sign-transactions-footer/sign-transactions-footer.tsx b/src/components/functional/sign-transactions-panel/components/sign-transactions-footer/sign-transactions-footer.tsx index 2bc8a967..6abcdcf3 100644 --- a/src/components/functional/sign-transactions-panel/components/sign-transactions-footer/sign-transactions-footer.tsx +++ b/src/components/functional/sign-transactions-panel/components/sign-transactions-footer/sign-transactions-footer.tsx @@ -4,6 +4,7 @@ import { Icon } from 'common/Icon'; import { DataTestIdsEnum } from 'constants/dataTestIds.enum'; import state from '../../signTransactionsPanelStore'; +import styles from './sign-transactions-footer.styles'; const signTransactionsFooterClasses: Record = { buttonTooltip: 'mvx:absolute mvx:top-0 mvx:h-12 mvx:left-0 mvx:right-0', @@ -56,122 +57,118 @@ export class SignTransactionsFooter { const checkButtonText = providerName ? `Check ${providerName}` : 'Check your device'; return ( - - + ); From 41ae84620c44868e83ca6d5d146b4d0a34a2815e Mon Sep 17 00:00:00 2001 From: Iulia Cimpeanu Date: Thu, 13 Nov 2025 15:03:43 +0200 Subject: [PATCH 3/9] Refactored copy button --- src/common/CopyButton/CopyButton.tsx | 61 +++++++++++++++++ .../copy-button/copy-button.stories.tsx | 0 .../common/copy-button/copy-button.tsx | 23 +++++++ .../copy-button/tests/copy-button.spec.ts | 13 ++-- .../SignTransactionsFooter.tsx | 3 +- .../visual/copy-button/copy-button.tsx | 68 ------------------- .../tests/data-with-explorer-link.spec.ts | 2 +- 7 files changed, 94 insertions(+), 76 deletions(-) create mode 100644 src/common/CopyButton/CopyButton.tsx rename src/components/{visual => common}/copy-button/copy-button.stories.tsx (100%) create mode 100644 src/components/common/copy-button/copy-button.tsx rename src/components/{visual => common}/copy-button/tests/copy-button.spec.ts (93%) delete mode 100644 src/components/visual/copy-button/copy-button.tsx diff --git a/src/common/CopyButton/CopyButton.tsx b/src/common/CopyButton/CopyButton.tsx new file mode 100644 index 00000000..2a55269c --- /dev/null +++ b/src/common/CopyButton/CopyButton.tsx @@ -0,0 +1,61 @@ +import { h } from '@stencil/core'; +import { Icon } from 'common/Icon'; +import { copyToClipboard } from 'utils/copyToClipboard'; + +// prettier-ignore +const styles = { + copyButton: 'copy-button mvx:flex', + copyButtonIcon: 'copy-button-icon mvx:flex mvx:cursor-pointer mvx:justify-center mvx:transition-opacity mvx:duration-200 mvx:ease-in-out mvx:hover:opacity-80', + copyButtonIconCheck: 'copy-button-icon-check mvx:hover:opacity-100! mvx:cursor-default!', +} satisfies Record; + +interface CopyButtonPropsType { + iconClass?: string; + class?: string; + text: string; +} + +let timeoutId: number | null = null; +let isSuccess: boolean = false; + +export function CopyButton({ iconClass, class: className, text }: CopyButtonPropsType) { + + const handleClick = async (event: MouseEvent) => { + const trimmedText = text ? text.trim() : text; + const success = await copyToClipboard(trimmedText); + + event.preventDefault(); + event.stopPropagation(); + + isSuccess = success; + + if (success) { + if (timeoutId) { + clearTimeout(timeoutId); + timeoutId = null; + } + timeoutId = window.setTimeout(() => { + isSuccess = false; + }, 2000); + } + } + + return ( +
+ +
+ ); +} diff --git a/src/components/visual/copy-button/copy-button.stories.tsx b/src/components/common/copy-button/copy-button.stories.tsx similarity index 100% rename from src/components/visual/copy-button/copy-button.stories.tsx rename to src/components/common/copy-button/copy-button.stories.tsx diff --git a/src/components/common/copy-button/copy-button.tsx b/src/components/common/copy-button/copy-button.tsx new file mode 100644 index 00000000..da4db14f --- /dev/null +++ b/src/components/common/copy-button/copy-button.tsx @@ -0,0 +1,23 @@ +import { Component, Prop, h } from '@stencil/core'; +import { CopyButton as CopyButtonComponent } from 'common/CopyButton/CopyButton'; + +@Component({ + tag: 'mvx-copy-button', + shadow: false, +}) +export class CopyButton { + @Prop() iconClass?: string; + @Prop() class?: string; + @Prop() text: string; + + + render() { + return ( + + ); + } +} diff --git a/src/components/visual/copy-button/tests/copy-button.spec.ts b/src/components/common/copy-button/tests/copy-button.spec.ts similarity index 93% rename from src/components/visual/copy-button/tests/copy-button.spec.ts rename to src/components/common/copy-button/tests/copy-button.spec.ts index d10b193b..7422d672 100644 --- a/src/components/visual/copy-button/tests/copy-button.spec.ts +++ b/src/components/common/copy-button/tests/copy-button.spec.ts @@ -47,8 +47,8 @@ describe('CopyButton', () => { }); const copyButton = page.root; - const component = page.rootInstance as CopyButton; - await component.handleClick(new MouseEvent('click') as any); + const element = copyButton.querySelector('div'); + element.click(); await page.waitForChanges(); expect(copyButton).toEqualHtml(` @@ -71,8 +71,8 @@ describe('CopyButton', () => { }); const copyButton = page.root; - const component = page.rootInstance as CopyButton; - await component.handleClick(new MouseEvent('click') as any); + const element = copyButton.querySelector('div'); + element.click(); await page.waitForChanges(); expect(copyButton).toEqualHtml(` @@ -97,8 +97,9 @@ describe('CopyButton', () => { stopPropagation: jest.fn(), }; - const component = page.rootInstance as CopyButton; - await component.handleClick(mockEvent as any); + const element = page.root.querySelector('div'); + element.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true })); + await page.waitForChanges(); expect(mockEvent.preventDefault).toHaveBeenCalledTimes(1); expect(mockEvent.stopPropagation).toHaveBeenCalledTimes(1); diff --git a/src/components/functional/sign-transactions-panel/components/SignTransactionsFooter/SignTransactionsFooter.tsx b/src/components/functional/sign-transactions-panel/components/SignTransactionsFooter/SignTransactionsFooter.tsx index 64ef5ccd..f9161eac 100644 --- a/src/components/functional/sign-transactions-panel/components/SignTransactionsFooter/SignTransactionsFooter.tsx +++ b/src/components/functional/sign-transactions-panel/components/SignTransactionsFooter/SignTransactionsFooter.tsx @@ -6,6 +6,7 @@ import { DataTestIdsEnum } from 'constants/dataTestIds.enum'; import state from '../../signTransactionsPanelStore'; import styles from './signTransactionsFooter.styles'; import { Trim } from 'common/Trim/Trim'; +import { CopyButton } from 'common/CopyButton/CopyButton'; let isWaitingForSignature: boolean = false; let lastCommonData = { ...state.commonData }; @@ -148,7 +149,7 @@ export function SignTransactionsFooter() { /> )} - + diff --git a/src/components/visual/copy-button/copy-button.tsx b/src/components/visual/copy-button/copy-button.tsx deleted file mode 100644 index 693b696b..00000000 --- a/src/components/visual/copy-button/copy-button.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import { Component, h, Prop, State } from '@stencil/core'; -import { Icon } from 'common/Icon'; -import { copyToClipboard } from 'utils/copyToClipboard'; - -// prettier-ignore -const styles = { - copyButton: 'copy-button mvx:flex', - copyButtonIcon: 'copy-button-icon mvx:flex mvx:cursor-pointer mvx:justify-center mvx:transition-opacity mvx:duration-200 mvx:ease-in-out mvx:hover:opacity-80', - copyButtonIconCheck: 'copy-button-icon-check mvx:hover:opacity-100! mvx:cursor-default!', -} satisfies Record; - -@Component({ - tag: 'mvx-copy-button', - shadow: false, -}) -export class CopyButton { - private timeoutId: number | null = null; - - @State() isSuccess: boolean = false; - @Prop() iconClass?: string; - @Prop() class?: string; - @Prop() text: string; - - async handleClick(event: MouseEvent) { - const trimmedText = this.text ? this.text.trim() : this.text; - const success = await copyToClipboard(trimmedText); - - const setSuccessStateTo = (newSuccessState: boolean) => { - this.isSuccess = newSuccessState; - }; - - event.preventDefault(); - event.stopPropagation(); - setSuccessStateTo(success); - - if (success) { - this.timeoutId = window.setTimeout(() => setSuccessStateTo(false), 2000); - } - } - - disconnectedCallback() { - if (this.timeoutId) { - clearTimeout(this.timeoutId); - this.timeoutId = null; - } - } - - render() { - return ( -
- -
- ); - } -} diff --git a/src/components/visual/data-with-explorer-link/tests/data-with-explorer-link.spec.ts b/src/components/visual/data-with-explorer-link/tests/data-with-explorer-link.spec.ts index 9c6ecec1..04b0d42f 100644 --- a/src/components/visual/data-with-explorer-link/tests/data-with-explorer-link.spec.ts +++ b/src/components/visual/data-with-explorer-link/tests/data-with-explorer-link.spec.ts @@ -2,7 +2,7 @@ import { newSpecPage } from '@stencil/core/testing'; import { Trim } from 'common/Trim/Trim'; import { ExplorerLink } from '../../../common/explorer-link/explorer-link'; -import { CopyButton } from '../../copy-button/copy-button'; +import { CopyButton } from '../../../common/copy-button/copy-button'; import { Tooltip } from '../../tooltip/tooltip'; import { DataWithExplorerLink } from '../data-with-explorer-link'; From 2bc1e6867e72d6ea00816ab0f030cefa89ffe016 Mon Sep 17 00:00:00 2001 From: Iulia Cimpeanu Date: Mon, 17 Nov 2025 11:52:24 +0200 Subject: [PATCH 4/9] Refactored button component --- src/common/Button/Button.tsx | 49 +++++++++++++ .../button => common/Button}/button.types.ts | 0 src/components/common/button/button.scss | 1 + .../button/button.stories.tsx | 2 +- src/components/common/button/button.tsx | 31 +++++++++ src/components/visual/button/button.scss | 69 ------------------- src/components/visual/button/button.tsx | 38 ---------- src/global/tailwind.css | 5 ++ 8 files changed, 87 insertions(+), 108 deletions(-) create mode 100644 src/common/Button/Button.tsx rename src/{components/visual/button => common/Button}/button.types.ts (100%) create mode 100644 src/components/common/button/button.scss rename src/components/{visual => common}/button/button.stories.tsx (95%) create mode 100644 src/components/common/button/button.tsx delete mode 100644 src/components/visual/button/button.scss delete mode 100644 src/components/visual/button/button.tsx diff --git a/src/common/Button/Button.tsx b/src/common/Button/Button.tsx new file mode 100644 index 00000000..d6a8e556 --- /dev/null +++ b/src/common/Button/Button.tsx @@ -0,0 +1,49 @@ +import { h } from '@stencil/core'; +import { ButtonSizeEnum, ButtonVariantEnum } from './button.types'; + +// prettier-ignore +const styles = { + button: 'button mvx:flex mvx:items-center mvx:justify-center mvx:font-bold mvx:leading-none mvx:px-4 mvx:max-h-full mvx:rounded-xl mvx:cursor-pointer mvx:transition-all mvx:duration-200 mvx:ease-in-out mvx:gap-2', + buttonLarge: 'button-large mvx:h-12 mvx:text-base mvx:px-6', + buttonSmall: 'button-small mvx:h-10 mvx:text-xs mvx:rounded-xl', + buttonPrimary: 'button-primary mvx:text-button-primary mvx:bg-button-bg-primary mvx:border mvx:border-button-bg-primary', + buttonSecondary: 'button-secondary mvx:relative mvx:text-button-secondary mvx:border mvx:border-transparent mvx:after:absolute mvx:after:inset-0 mvx:after:rounded-lg mvx:after:opacity-40 mvx:after:transition-all mvx:after:duration-200 mvx:after:ease-in-out mvx:after:bg-button-bg-secondary mvx:after:content-[""] mvx:after:-z-1 mvx:hover:opacity-100 mvx:hover:text-button-primary mvx:hover:after:opacity-100 mvx:hover:after:bg-button-bg-primary', + buttonSecondarySmall: 'button-secondary-small mvx:after:rounded-xl', + buttonNeutral: 'button-neutral mvx:text-neutral-925 mvx:bg-white mvx:hover:opacity-75', + buttonDisabled: 'button-disabled mvx:pointer-events-none mvx:bg-transparent mvx:cursor-default mvx:border mvx:border-secondary-text mvx:text-secondary-text mvx:hover:opacity-100' +} satisfies Record; + +interface ButtonPropsType { + class?: string; + dataTestId?: string; + disabled?: boolean; + size?: `${ButtonSizeEnum}`; + variant?: `${ButtonVariantEnum}`; + onClick?: (event: MouseEvent) => void; +} + +export function Button({ class: className = '', dataTestId = '', disabled = false, size = 'large', variant = 'primary', onClick }: ButtonPropsType, children?: any) { + return ( + + ); + +} diff --git a/src/components/visual/button/button.types.ts b/src/common/Button/button.types.ts similarity index 100% rename from src/components/visual/button/button.types.ts rename to src/common/Button/button.types.ts diff --git a/src/components/common/button/button.scss b/src/components/common/button/button.scss new file mode 100644 index 00000000..ff1190ee --- /dev/null +++ b/src/components/common/button/button.scss @@ -0,0 +1 @@ +// This is needed to trigger the Stecil Tailwind compilation for inline Tailwind classes. \ No newline at end of file diff --git a/src/components/visual/button/button.stories.tsx b/src/components/common/button/button.stories.tsx similarity index 95% rename from src/components/visual/button/button.stories.tsx rename to src/components/common/button/button.stories.tsx index 23a1b35a..6232d65c 100644 --- a/src/components/visual/button/button.stories.tsx +++ b/src/components/common/button/button.stories.tsx @@ -5,7 +5,7 @@ import type { Meta, StoryObj } from '@stencil/storybook-plugin'; import capitalize from 'lodash.capitalize'; import type { Button } from './button'; -import { ButtonSizeEnum, ButtonVariantEnum } from './button.types'; +import { ButtonSizeEnum, ButtonVariantEnum } from '../../../common/Button/button.types'; // prettier-ignore const styles = { diff --git a/src/components/common/button/button.tsx b/src/components/common/button/button.tsx new file mode 100644 index 00000000..f9971221 --- /dev/null +++ b/src/components/common/button/button.tsx @@ -0,0 +1,31 @@ +import { Component, h, Prop } from '@stencil/core'; +import type { ButtonSizeEnum, ButtonVariantEnum } from '../../../common/Button/button.types'; +import { Button as ButtonComponent } from 'common/Button/Button'; + +@Component({ + tag: 'mvx-button', + styleUrl: 'button.scss', + shadow: true, +}) + +export class Button { + @Prop() class?: string = ''; + @Prop() dataTestId?: string = ''; + @Prop() disabled?: boolean = false; + @Prop() size?: `${ButtonSizeEnum}` = 'large'; + @Prop() variant?: `${ButtonVariantEnum}` = 'primary'; + + render() { + return ( + + + + ); + } +} diff --git a/src/components/visual/button/button.scss b/src/components/visual/button/button.scss deleted file mode 100644 index 0085031b..00000000 --- a/src/components/visual/button/button.scss +++ /dev/null @@ -1,69 +0,0 @@ -:host { - @apply mvx:flex; - - .button { - @apply mvx:flex mvx:items-center mvx:justify-center mvx:font-bold mvx:leading-none mvx:px-4 mvx:max-h-full; - @apply mvx:rounded-xl mvx:cursor-pointer mvx:transition-all mvx:duration-200 mvx:ease-in-out mvx:gap-2; - - &.large { - @apply mvx:h-12 mvx:text-base mvx:px-6; - } - - &.small { - @apply mvx:h-10 mvx:text-xs mvx:rounded-xl; - } - - &.primary { - color: var(--mvx-button-text-primary); - background-color: var(--mvx-button-bg-primary); - border: 1px solid var(--mvx-button-bg-primary); - } - - &.secondary { - @apply mvx:relative; - color: var(--mvx-button-text-secondary); - border: 1px solid transparent; - - &.small:after { - @apply mvx:rounded-xl; - } - - &:after { - @apply mvx:absolute mvx:inset-0 mvx:rounded-lg mvx:opacity-40 mvx:transition-all mvx:duration-200 mvx:ease-in-out; - background-color: var(--mvx-button-bg-secondary); - content: ''; - z-index: -1; - } - - &:hover { - @apply mvx:opacity-100; - color: var(--mvx-button-text-primary); - - &:after { - @apply mvx:opacity-100; - background-color: var(--mvx-button-bg-primary); - } - } - } - - &.neutral { - color: var(--mvx-neutral-925); - background: var(--mvx-white); - } - - &:hover { - @apply mvx:opacity-75; - } - - &.disabled, - &:disabled { - @apply mvx:pointer-events-none mvx:bg-transparent mvx:cursor-default; - border: 1px solid var(--mvx-text-color-secondary); - color: var(--mvx-text-color-secondary); - - &:hover { - @apply mvx:opacity-100; - } - } - } -} diff --git a/src/components/visual/button/button.tsx b/src/components/visual/button/button.tsx deleted file mode 100644 index 40967113..00000000 --- a/src/components/visual/button/button.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import type { EventEmitter } from '@stencil/core'; -import { Component, Event, h, Prop } from '@stencil/core'; - -import type { ButtonSizeEnum, ButtonVariantEnum } from './button.types'; - -@Component({ - tag: 'mvx-button', - styleUrl: 'button.scss', - shadow: true, -}) -export class Button { - @Event() buttonClick: EventEmitter; - - @Prop() class?: string = ''; - @Prop() dataTestId?: string = ''; - @Prop() disabled?: boolean = false; - @Prop() size?: `${ButtonSizeEnum}` = 'large'; - @Prop() variant?: `${ButtonVariantEnum}` = 'primary'; - - render() { - return ( - - ); - } -} diff --git a/src/global/tailwind.css b/src/global/tailwind.css index 35760edb..5340bf45 100644 --- a/src/global/tailwind.css +++ b/src/global/tailwind.css @@ -29,9 +29,14 @@ --color-transaction-method: var(--mvx-transaction-method); --color-slate-550: var(--mvx-slate-550); --color-gray-200: var(--mvx-gray-200); + --color-neutral-925: var(--mvx-neutral-925); --color-thumb: var(--mvx-scrollbar-thumb); --color-outline-variant: var(--mvx-border-color-secondary); --color-emphasize: var(--mvx-bg-accent-color); + --color-button-primary: var(--mvx-button-text-primary); + --color-button-secondary: var(--mvx-button-text-secondary); + --color-button-bg-primary: var(--mvx-button-bg-primary); + --color-button-bg-secondary: var(--mvx-button-bg-secondary); } @layer utilities { From d59bc269311c591f2bc6712880bb58d2422ec562 Mon Sep 17 00:00:00 2001 From: Iulia Cimpeanu Date: Mon, 17 Nov 2025 15:39:44 +0200 Subject: [PATCH 5/9] Refactored tooltip component --- src/common/Button/Button.tsx | 2 +- src/common/Tooltip/Tooltip.tsx | 87 +++++++++++++++++++ src/components.d.ts | 4 +- src/components/common/button/button.tsx | 8 +- src/components/common/tooltip/tooltip.scss | 1 + src/components/common/tooltip/tooltip.tsx | 38 ++++++++ .../SignTransactionsFooter.tsx | 28 +++--- .../sign-transactions-panel.tsx | 23 ++++- .../tests/data-with-explorer-link.spec.ts | 2 +- src/components/visual/tooltip/tooltip.scss | 40 --------- src/components/visual/tooltip/tooltip.tsx | 87 ------------------- 11 files changed, 176 insertions(+), 144 deletions(-) create mode 100644 src/common/Tooltip/Tooltip.tsx create mode 100644 src/components/common/tooltip/tooltip.scss create mode 100644 src/components/common/tooltip/tooltip.tsx delete mode 100644 src/components/visual/tooltip/tooltip.scss delete mode 100644 src/components/visual/tooltip/tooltip.tsx diff --git a/src/common/Button/Button.tsx b/src/common/Button/Button.tsx index d6a8e556..506d2bb1 100644 --- a/src/common/Button/Button.tsx +++ b/src/common/Button/Button.tsx @@ -10,7 +10,7 @@ const styles = { buttonSecondary: 'button-secondary mvx:relative mvx:text-button-secondary mvx:border mvx:border-transparent mvx:after:absolute mvx:after:inset-0 mvx:after:rounded-lg mvx:after:opacity-40 mvx:after:transition-all mvx:after:duration-200 mvx:after:ease-in-out mvx:after:bg-button-bg-secondary mvx:after:content-[""] mvx:after:-z-1 mvx:hover:opacity-100 mvx:hover:text-button-primary mvx:hover:after:opacity-100 mvx:hover:after:bg-button-bg-primary', buttonSecondarySmall: 'button-secondary-small mvx:after:rounded-xl', buttonNeutral: 'button-neutral mvx:text-neutral-925 mvx:bg-white mvx:hover:opacity-75', - buttonDisabled: 'button-disabled mvx:pointer-events-none mvx:bg-transparent mvx:cursor-default mvx:border mvx:border-secondary-text mvx:text-secondary-text mvx:hover:opacity-100' + buttonDisabled: 'button-disabled mvx:pointer-events-none mvx:bg-transparent mvx:cursor-default mvx:border mvx:border-secondary-text mvx:!text-secondary-text mvx:hover:opacity-100' } satisfies Record; interface ButtonPropsType { diff --git a/src/common/Tooltip/Tooltip.tsx b/src/common/Tooltip/Tooltip.tsx new file mode 100644 index 00000000..6a7b85b8 --- /dev/null +++ b/src/common/Tooltip/Tooltip.tsx @@ -0,0 +1,87 @@ +import { h } from '@stencil/core'; + +// prettier-ignore +const styles = { + tooltip: 'tooltip mvx:flex mvx:relative', + tooltipContentWrapper: 'tooltip-content-wrapper mvx:left-1/2 mvx:absolute mvx:z-1 mvx:transform mvx:-translate-x-1/2', + tooltipContent: 'tooltip-content mvx:flex-row mvx:cursor-default mvx:p-2 mvx:whitespace-nowrap mvx:text-xs mvx:rounded-xl mvx:leading-none mvx:bg-surface mvx:border-outline-variant mvx:border mvx:text-primary mvx:after:left-1/2 mvx:after:origin-center mvx:after:w-2 mvx:after:h-2 mvx:after:absolute mvx:after:border mvx:after:border-outline-variant mvx:after:bg-surface mvx:after:translate-x-[calc(50%-8px)] mvx:after:-rotate-[45deg] mvx:after:content-[""]', + tooltipContentTop: 'tooltip-content-top mvx:after:border-t-0 mvx:after:border-r-0 mvx:after:-bottom-1', + tooltipContentBottom: 'tooltip-content-bottom mvx:after:border-b-0 mvx:after:border-l-0 mvx:after:-top-1' +} satisfies Record; + +interface TooltipPropsType { + position?: 'top' | 'bottom'; + triggerOnClick?: boolean; + trigger: HTMLElement; + class?: string; + isTooltipVisible?: boolean; + onVisibilityChange?: (isTooltipVisible: boolean) => void; +} + +export function Tooltip({ position = 'top', triggerOnClick = false, trigger, class: className, onVisibilityChange, isTooltipVisible = false }: TooltipPropsType, children?: any) { + const setTooltipVisible = (isTooltipVisible: boolean) => { + onVisibilityChange?.(isTooltipVisible); + } + + const handleEllipsisClick = (event: MouseEvent) => { + if (!triggerOnClick) { + return; + } + + event.preventDefault(); + setTooltipVisible(!isTooltipVisible); + } + + const handleFocusOut = (event: FocusEvent) => { + const relatedTarget = event.relatedTarget as Node; + const currentTarget = event.currentTarget as HTMLElement; + + if (!currentTarget.contains(relatedTarget)) { + setTooltipVisible(false); + } + } + + const handleMouseEvent = (isTooltipVisible: boolean) => { + if (triggerOnClick) { + return; + } + + return (event: MouseEvent) => { + event.preventDefault(); + setTooltipVisible(isTooltipVisible); + }; + } + + return ( +
+ {isTooltipVisible && ( +
+
event.stopPropagation()} + > + {children} +
+
+ )} + + {trigger} +
+ ); +} \ No newline at end of file diff --git a/src/components.d.ts b/src/components.d.ts index f763ea63..ae6b2896 100644 --- a/src/components.d.ts +++ b/src/components.d.ts @@ -6,7 +6,7 @@ */ import { HTMLStencilElement, JSXBase } from "@stencil/core/internal"; import { IAddressTableData } from "./types/address-table.types"; -import { ButtonSizeEnum, ButtonVariantEnum } from "./components/visual/button/button.types"; +import { ButtonSizeEnum, ButtonVariantEnum } from "./common/Button/button.types"; import { CustomToastType, IComponentToast, ISimpleToast } from "./components/functional/toasts-list/components/transaction-toast/transaction-toast.type"; import { IConfirmScreenData, IConnectScreenData, ILedgerConnectPanelData } from "./components/functional/ledger-connect/ledger-connect.types"; import { IEventBus } from "./utils/EventBus"; @@ -20,7 +20,7 @@ import { TransactionRowType } from "./components/controlled/transactions-table/t import { IProviderBase } from "./types/provider.types"; import { IEventBus as IEventBus1, unknown as IWalletConnectPanelData } from "./components.d"; export { IAddressTableData } from "./types/address-table.types"; -export { ButtonSizeEnum, ButtonVariantEnum } from "./components/visual/button/button.types"; +export { ButtonSizeEnum, ButtonVariantEnum } from "./common/Button/button.types"; export { CustomToastType, IComponentToast, ISimpleToast } from "./components/functional/toasts-list/components/transaction-toast/transaction-toast.type"; export { IConfirmScreenData, IConnectScreenData, ILedgerConnectPanelData } from "./components/functional/ledger-connect/ledger-connect.types"; export { IEventBus } from "./utils/EventBus"; diff --git a/src/components/common/button/button.tsx b/src/components/common/button/button.tsx index f9971221..ebcc07aa 100644 --- a/src/components/common/button/button.tsx +++ b/src/components/common/button/button.tsx @@ -1,5 +1,8 @@ -import { Component, h, Prop } from '@stencil/core'; +import type { EventEmitter } from '@stencil/core'; +import { Component, Event, h, Prop } from '@stencil/core'; + import type { ButtonSizeEnum, ButtonVariantEnum } from '../../../common/Button/button.types'; + import { Button as ButtonComponent } from 'common/Button/Button'; @Component({ @@ -7,8 +10,9 @@ import { Button as ButtonComponent } from 'common/Button/Button'; styleUrl: 'button.scss', shadow: true, }) - export class Button { + @Event() buttonClick: EventEmitter; + @Prop() class?: string = ''; @Prop() dataTestId?: string = ''; @Prop() disabled?: boolean = false; diff --git a/src/components/common/tooltip/tooltip.scss b/src/components/common/tooltip/tooltip.scss new file mode 100644 index 00000000..ff1190ee --- /dev/null +++ b/src/components/common/tooltip/tooltip.scss @@ -0,0 +1 @@ +// This is needed to trigger the Stecil Tailwind compilation for inline Tailwind classes. \ No newline at end of file diff --git a/src/components/common/tooltip/tooltip.tsx b/src/components/common/tooltip/tooltip.tsx new file mode 100644 index 00000000..09b19046 --- /dev/null +++ b/src/components/common/tooltip/tooltip.tsx @@ -0,0 +1,38 @@ +import { Component, Event, EventEmitter, h, Prop, State } from '@stencil/core'; +import { Tooltip as TooltipComponent } from 'common/Tooltip/Tooltip'; + +@Component({ + tag: 'mvx-tooltip', + styleUrl: 'tooltip.scss', + shadow: true, +}) +export class Tooltip { + @Prop() position: 'top' | 'bottom' = 'top'; + @Prop() triggerOnClick?: boolean = false; + @Prop() trigger: HTMLElement; + @Prop() class?: string; + + @Event() triggerRender: EventEmitter; + @State() private isVisible: boolean = false; + + private handleVisibilityChange = (isVisible: boolean) => { + this.triggerRender.emit(isVisible); + this.isVisible = isVisible; + }; + + + render() { + return ( + + + + ); + } +} diff --git a/src/components/functional/sign-transactions-panel/components/SignTransactionsFooter/SignTransactionsFooter.tsx b/src/components/functional/sign-transactions-panel/components/SignTransactionsFooter/SignTransactionsFooter.tsx index f9161eac..90a72659 100644 --- a/src/components/functional/sign-transactions-panel/components/SignTransactionsFooter/SignTransactionsFooter.tsx +++ b/src/components/functional/sign-transactions-panel/components/SignTransactionsFooter/SignTransactionsFooter.tsx @@ -5,13 +5,19 @@ import { DataTestIdsEnum } from 'constants/dataTestIds.enum'; import state from '../../signTransactionsPanelStore'; import styles from './signTransactionsFooter.styles'; -import { Trim } from 'common/Trim/Trim'; import { CopyButton } from 'common/CopyButton/CopyButton'; +import { Tooltip } from 'common/Tooltip/Tooltip'; +import { Button } from 'common/Button/Button'; let isWaitingForSignature: boolean = false; let lastCommonData = { ...state.commonData }; -export function SignTransactionsFooter() { +interface SignTransactionsFooterPropsType { + tooltipVisible?: boolean; + onTooltipVisibilityChange?: (visible: boolean) => void; +} + +export function SignTransactionsFooter({ tooltipVisible, onTooltipVisibilityChange }: SignTransactionsFooterPropsType) { const currentCommonData = { ...state.commonData }; const hasChanged = JSON.stringify(currentCommonData) !== JSON.stringify(lastCommonData); @@ -44,15 +50,15 @@ export function SignTransactionsFooter() {
- {isFirstTransaction ? 'Cancel' : 'Back'} - +
@@ -61,7 +67,9 @@ export function SignTransactionsFooter() { class={styles.signTransactionsFooterButtonTooltipWrapper} onClick={(event: MouseEvent) => event.stopPropagation()} > - )} - +
)} - )} - +
@@ -142,7 +150,7 @@ export function SignTransactionsFooter() { )} {!username && address && ( - ; + @Component({ tag: 'mvx-sign-transactions-panel', styleUrl: 'sign-transactions-panel.scss', @@ -28,6 +40,7 @@ export class SignTransactionsPanel { @State() isOpen: boolean = false; @State() activeTab: TransactionTabsEnum = TransactionTabsEnum.overview; + @State() private isFooterTooltipVisible: boolean = false; @Method() async getEventBus() { await this.connectionMonitor.waitForConnection(); @@ -115,7 +128,12 @@ export class SignTransactionsPanel { }; } + private handleIsFooterTooltipVisible = (isTooltipVisible: boolean) => { + this.isFooterTooltipVisible = isTooltipVisible; + }; + render() { + console.log(styles) //TODO: remove this const transactionTabs = Object.values(TransactionTabsEnum); const { commonData } = state; @@ -153,7 +171,10 @@ export class SignTransactionsPanel { )} - + ); diff --git a/src/components/visual/data-with-explorer-link/tests/data-with-explorer-link.spec.ts b/src/components/visual/data-with-explorer-link/tests/data-with-explorer-link.spec.ts index 04b0d42f..f8ba557c 100644 --- a/src/components/visual/data-with-explorer-link/tests/data-with-explorer-link.spec.ts +++ b/src/components/visual/data-with-explorer-link/tests/data-with-explorer-link.spec.ts @@ -3,7 +3,7 @@ import { Trim } from 'common/Trim/Trim'; import { ExplorerLink } from '../../../common/explorer-link/explorer-link'; import { CopyButton } from '../../../common/copy-button/copy-button'; -import { Tooltip } from '../../tooltip/tooltip'; +import { Tooltip } from '../../../common/tooltip/tooltip'; import { DataWithExplorerLink } from '../data-with-explorer-link'; describe('DataWithExplorerLink', () => { diff --git a/src/components/visual/tooltip/tooltip.scss b/src/components/visual/tooltip/tooltip.scss deleted file mode 100644 index 4ecce9e3..00000000 --- a/src/components/visual/tooltip/tooltip.scss +++ /dev/null @@ -1,40 +0,0 @@ -.tooltip { - @apply mvx:flex mvx:relative; - - .tooltip-content-wrapper { - @apply mvx:left-1/2 mvx:absolute mvx:z-1 mvx:transform mvx:-translate-x-1/2; - - &.top { - bottom: calc(100% + 16px); - } - - &.bottom { - top: calc(100% + 16px); - } - - .tooltip-content { - @apply mvx:flex-row mvx:cursor-default mvx:p-2 mvx:whitespace-nowrap mvx:text-xs mvx:rounded-xl mvx:leading-none; - background: var(--mvx-bg-color-primary); - border: 1px solid var(--mvx-border-color-secondary); - color: var(--mvx-text-color-primary); - - &:after { - @apply mvx:left-1/2 mvx:origin-center mvx:w-2 mvx:h-2 mvx:absolute; - border: 1px solid var(--mvx-border-color-secondary); - background-color: var(--mvx-bg-color-primary); - content: ''; - transform: translateX(calc(50% - 8px)) rotateZ(calc(45deg * -1)); - } - - &.top:after { - @apply mvx:border-t-0 mvx:border-r-0; - bottom: calc(4px * -1); - } - - &.bottom:after { - @apply mvx:border-b-0 mvx:border-l-0; - top: calc(4px * -1); - } - } - } -} diff --git a/src/components/visual/tooltip/tooltip.tsx b/src/components/visual/tooltip/tooltip.tsx deleted file mode 100644 index dd2d3376..00000000 --- a/src/components/visual/tooltip/tooltip.tsx +++ /dev/null @@ -1,87 +0,0 @@ -import type { EventEmitter } from '@stencil/core'; -import { Component, Event, h, Prop, State } from '@stencil/core'; - -// prettier-ignore -const styles = { - tooltip: 'tooltip mvx:flex mvx:relative', -} satisfies Record; - -@Component({ - tag: 'mvx-tooltip', - styleUrl: 'tooltip.scss', - shadow: true, -}) -export class Tooltip { - @Event() triggerRender: EventEmitter; - @State() isTooltipVisible: boolean = false; - - @Prop() position: 'top' | 'bottom' = 'top'; - @Prop() triggerOnClick?: boolean = false; - @Prop() trigger: HTMLElement; - @Prop() class?: string; - - constructor() { - this.handleEllipsisClick = this.handleEllipsisClick.bind(this); - this.handleFocusOut = this.handleFocusOut.bind(this); - } - - private setTooltipVisible(isTooltipVisible: boolean) { - this.isTooltipVisible = isTooltipVisible; - this.triggerRender.emit(this.isTooltipVisible); - } - - private handleEllipsisClick(event: MouseEvent) { - if (!this.triggerOnClick) { - return; - } - - event.preventDefault(); - this.setTooltipVisible(!this.isTooltipVisible); - } - - private handleFocusOut(event: FocusEvent) { - const relatedTarget = event.relatedTarget as Node; - const currentTarget = event.currentTarget as HTMLElement; - - if (!currentTarget.contains(relatedTarget)) { - this.setTooltipVisible(false); - } - } - - private handleMouseEvent(isTooltipVisible: boolean) { - if (this.triggerOnClick) { - return; - } - - return (event: MouseEvent) => { - event.preventDefault(); - this.setTooltipVisible(isTooltipVisible); - }; - } - - render() { - return ( -
- {this.isTooltipVisible && ( -
-
event.stopPropagation()} - > - -
-
- )} - - {this.trigger} -
- ); - } -} From 19d62af479096a2c4227d2c139cbdc1881b22466 Mon Sep 17 00:00:00 2001 From: Iulia Cimpeanu Date: Mon, 17 Nov 2025 17:03:06 +0200 Subject: [PATCH 6/9] Refactored spinner icon component --- src/common/Icon/Icon.tsx | 4 ++++ .../Icon/components/SpinnerIcon/SpinnerIcon.tsx | 16 ++++++++++++++++ src/common/Icon/components/SpinnerIcon/index.ts | 1 + src/common/Icon/icon.types.ts | 3 ++- .../SignTransactionsFooter.tsx | 2 +- .../sign-transactions-panel.tsx | 2 +- src/global/tailwind.css | 10 ++++++++++ 7 files changed, 35 insertions(+), 3 deletions(-) create mode 100644 src/common/Icon/components/SpinnerIcon/SpinnerIcon.tsx create mode 100644 src/common/Icon/components/SpinnerIcon/index.ts diff --git a/src/common/Icon/Icon.tsx b/src/common/Icon/Icon.tsx index 2a431186..26d64a74 100644 --- a/src/common/Icon/Icon.tsx +++ b/src/common/Icon/Icon.tsx @@ -24,6 +24,7 @@ import { LayersIcon } from './components/LayersIcon'; import { LockIcon } from './components/LockIcon'; import { PencilIcon } from './components/PencilIcon'; import { TriangularWarningIcon } from './components/TriangularWarningIcon'; +import { SpinnerIcon } from './components/SpinnerIcon'; import type { IconPropsType } from './icon.types'; export const Icon = ({ name, ...properties }: IconPropsType) => { @@ -104,6 +105,9 @@ export const Icon = ({ name, ...properties }: IconPropsType) => { case 'arrows-rotate': return ; + case 'spinner': + return + default: console.error(`No data for the ${name} icon.`); return null; diff --git a/src/common/Icon/components/SpinnerIcon/SpinnerIcon.tsx b/src/common/Icon/components/SpinnerIcon/SpinnerIcon.tsx new file mode 100644 index 00000000..0c6ee5ac --- /dev/null +++ b/src/common/Icon/components/SpinnerIcon/SpinnerIcon.tsx @@ -0,0 +1,16 @@ +import { h } from '@stencil/core'; + +const styles = { + spinnerIcon: 'spinner-icon mvx:w-4 mvx:h-4 mvx:fill-secondary-text mvx:animate-spinner' +} satisfies Record; + +export const SpinnerIcon = ({ class: className }: { class?: string }) => ( + + + +); + + diff --git a/src/common/Icon/components/SpinnerIcon/index.ts b/src/common/Icon/components/SpinnerIcon/index.ts new file mode 100644 index 00000000..2a092c9d --- /dev/null +++ b/src/common/Icon/components/SpinnerIcon/index.ts @@ -0,0 +1 @@ +export * from './SpinnerIcon'; \ No newline at end of file diff --git a/src/common/Icon/icon.types.ts b/src/common/Icon/icon.types.ts index 34c5f645..4f8c4fb3 100644 --- a/src/common/Icon/icon.types.ts +++ b/src/common/Icon/icon.types.ts @@ -24,7 +24,8 @@ export enum IconNamesEnum { circleCheck = 'circle-check', circleInfo = 'circle-info', coins = 'coins', - arrowsRotate = 'arrows-rotate' + arrowsRotate = 'arrows-rotate', + spinner = 'spinner' } export type IconPropsType = JSXBase.IntrinsicElements['svg'] & { diff --git a/src/components/functional/sign-transactions-panel/components/SignTransactionsFooter/SignTransactionsFooter.tsx b/src/components/functional/sign-transactions-panel/components/SignTransactionsFooter/SignTransactionsFooter.tsx index 90a72659..8ffdb67b 100644 --- a/src/components/functional/sign-transactions-panel/components/SignTransactionsFooter/SignTransactionsFooter.tsx +++ b/src/components/functional/sign-transactions-panel/components/SignTransactionsFooter/SignTransactionsFooter.tsx @@ -119,7 +119,7 @@ export function SignTransactionsFooter({ tooltipVisible, onTooltipVisibilityChan > {isWaitingForSignature ? ( - + ) : ( diff --git a/src/components/functional/sign-transactions-panel/sign-transactions-panel.tsx b/src/components/functional/sign-transactions-panel/sign-transactions-panel.tsx index 4474cca6..4536b776 100644 --- a/src/components/functional/sign-transactions-panel/sign-transactions-panel.tsx +++ b/src/components/functional/sign-transactions-panel/sign-transactions-panel.tsx @@ -19,7 +19,7 @@ const styles = { buttonSecondary: 'button-secondary mvx:relative mvx:text-button-secondary mvx:border mvx:border-transparent mvx:after:absolute mvx:after:inset-0 mvx:after:rounded-lg mvx:after:opacity-40 mvx:after:transition-all mvx:after:duration-200 mvx:after:ease-in-out mvx:after:bg-button-bg-secondary mvx:after:content-[""] mvx:after:-z-1 mvx:hover:opacity-100 mvx:hover:text-button-primary mvx:hover:after:opacity-100 mvx:hover:after:bg-button-bg-primary', buttonSecondarySmall: 'button-secondary-small mvx:after:rounded-xl', buttonNeutral: 'button-neutral mvx:text-neutral-925 mvx:bg-white mvx:hover:opacity-75', - buttonDisabled: 'button-disabled mvx:pointer-events-none mvx:bg-transparent mvx:cursor-default mvx:border mvx:border-secondary-text mvx:text-secondary-text mvx:hover:opacity-100' + buttonDisabled: 'button-disabled mvx:pointer-events-none mvx:bg-transparent mvx:cursor-default mvx:border mvx:border-secondary-text mvx:!text-secondary-text mvx:hover:opacity-100' } satisfies Record; @Component({ diff --git a/src/global/tailwind.css b/src/global/tailwind.css index 5340bf45..66eb3fb9 100644 --- a/src/global/tailwind.css +++ b/src/global/tailwind.css @@ -88,4 +88,14 @@ letter-spacing: -0.001em; } } + + @keyframes SpinnerAnimation { + to { + transform: rotate(360deg); + } + } + + .mvx\:animate-spinner { + animation: SpinnerAnimation 3000ms linear infinite; + } } \ No newline at end of file From 3ba682dcc5fd5c5e476d01da7371d9f338d89dce Mon Sep 17 00:00:00 2001 From: Iulia Cimpeanu Date: Mon, 17 Nov 2025 17:20:55 +0200 Subject: [PATCH 7/9] Refactored arrow right component --- src/common/Icon/Icon.tsx | 4 ++++ .../components/ArrowRightIcon/ArrowRightIcon.tsx | 13 +++++++++++++ src/common/Icon/components/ArrowRightIcon/index.ts | 1 + src/common/Icon/icon.types.ts | 3 ++- .../SignTransactionsFooter.tsx | 2 +- 5 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 src/common/Icon/components/ArrowRightIcon/ArrowRightIcon.tsx create mode 100644 src/common/Icon/components/ArrowRightIcon/index.ts diff --git a/src/common/Icon/Icon.tsx b/src/common/Icon/Icon.tsx index 26d64a74..84ffa060 100644 --- a/src/common/Icon/Icon.tsx +++ b/src/common/Icon/Icon.tsx @@ -26,6 +26,7 @@ import { PencilIcon } from './components/PencilIcon'; import { TriangularWarningIcon } from './components/TriangularWarningIcon'; import { SpinnerIcon } from './components/SpinnerIcon'; import type { IconPropsType } from './icon.types'; +import { ArrowRightIcon } from './components/ArrowRightIcon'; export const Icon = ({ name, ...properties }: IconPropsType) => { if (!name) { @@ -108,6 +109,9 @@ export const Icon = ({ name, ...properties }: IconPropsType) => { case 'spinner': return + case 'arrow-right': + return + default: console.error(`No data for the ${name} icon.`); return null; diff --git a/src/common/Icon/components/ArrowRightIcon/ArrowRightIcon.tsx b/src/common/Icon/components/ArrowRightIcon/ArrowRightIcon.tsx new file mode 100644 index 00000000..c6a358be --- /dev/null +++ b/src/common/Icon/components/ArrowRightIcon/ArrowRightIcon.tsx @@ -0,0 +1,13 @@ +import { h } from '@stencil/core'; + +const styles = { + arrowRightIcon: 'arrow-right-icon mvx:w-4 mvx:h-4 mvx:text-inherit' +} satisfies Record; + +export const ArrowRightIcon = ({ class: className }: { class?: string }) => ( + + + +); + + diff --git a/src/common/Icon/components/ArrowRightIcon/index.ts b/src/common/Icon/components/ArrowRightIcon/index.ts new file mode 100644 index 00000000..190655e7 --- /dev/null +++ b/src/common/Icon/components/ArrowRightIcon/index.ts @@ -0,0 +1 @@ +export * from './ArrowRightIcon'; \ No newline at end of file diff --git a/src/common/Icon/icon.types.ts b/src/common/Icon/icon.types.ts index 4f8c4fb3..c699f9a0 100644 --- a/src/common/Icon/icon.types.ts +++ b/src/common/Icon/icon.types.ts @@ -25,7 +25,8 @@ export enum IconNamesEnum { circleInfo = 'circle-info', coins = 'coins', arrowsRotate = 'arrows-rotate', - spinner = 'spinner' + spinner = 'spinner', + arrowRight = 'arrow-right' } export type IconPropsType = JSXBase.IntrinsicElements['svg'] & { diff --git a/src/components/functional/sign-transactions-panel/components/SignTransactionsFooter/SignTransactionsFooter.tsx b/src/components/functional/sign-transactions-panel/components/SignTransactionsFooter/SignTransactionsFooter.tsx index 8ffdb67b..84c73d60 100644 --- a/src/components/functional/sign-transactions-panel/components/SignTransactionsFooter/SignTransactionsFooter.tsx +++ b/src/components/functional/sign-transactions-panel/components/SignTransactionsFooter/SignTransactionsFooter.tsx @@ -129,7 +129,7 @@ export function SignTransactionsFooter({ tooltipVisible, onTooltipVisibilityChan ) : ( - + )} From 02d264c36eb505ec1196ed8ac494a4241cba4d57 Mon Sep 17 00:00:00 2001 From: Iulia Cimpeanu Date: Tue, 18 Nov 2025 14:24:05 +0200 Subject: [PATCH 8/9] Updated copy button --- src/common/CopyButton/CopyButton.tsx | 35 ++++--------------- src/common/CopyButton/CopyButtonHandler.ts | 26 ++++++++++++++ .../common/copy-button/copy-button.tsx | 9 ++++- .../SignTransactionsFooter.tsx | 9 +++-- .../sign-transactions-panel.tsx | 10 +++++- 5 files changed, 55 insertions(+), 34 deletions(-) create mode 100644 src/common/CopyButton/CopyButtonHandler.ts diff --git a/src/common/CopyButton/CopyButton.tsx b/src/common/CopyButton/CopyButton.tsx index 2a55269c..9880c6ee 100644 --- a/src/common/CopyButton/CopyButton.tsx +++ b/src/common/CopyButton/CopyButton.tsx @@ -1,6 +1,5 @@ import { h } from '@stencil/core'; import { Icon } from 'common/Icon'; -import { copyToClipboard } from 'utils/copyToClipboard'; // prettier-ignore const styles = { @@ -13,46 +12,24 @@ interface CopyButtonPropsType { iconClass?: string; class?: string; text: string; + isSuccessOnCopy?: boolean; + handleCopyButtonClick?: (event: MouseEvent) => void; } -let timeoutId: number | null = null; -let isSuccess: boolean = false; - -export function CopyButton({ iconClass, class: className, text }: CopyButtonPropsType) { - - const handleClick = async (event: MouseEvent) => { - const trimmedText = text ? text.trim() : text; - const success = await copyToClipboard(trimmedText); - - event.preventDefault(); - event.stopPropagation(); - - isSuccess = success; - - if (success) { - if (timeoutId) { - clearTimeout(timeoutId); - timeoutId = null; - } - timeoutId = window.setTimeout(() => { - isSuccess = false; - }, 2000); - } - } - +export function CopyButton({ iconClass, class: className, isSuccessOnCopy, handleCopyButtonClick }: CopyButtonPropsType) { return (
handleCopyButtonClick?.(event)} class={{ [styles.copyButton]: true, [className]: Boolean(className), }} > diff --git a/src/common/CopyButton/CopyButtonHandler.ts b/src/common/CopyButton/CopyButtonHandler.ts new file mode 100644 index 00000000..97f8a3bc --- /dev/null +++ b/src/common/CopyButton/CopyButtonHandler.ts @@ -0,0 +1,26 @@ +import { copyToClipboard } from 'utils/copyToClipboard'; + +interface CopyHandlerOptions { + onSuccessChange?: (isSuccess: boolean) => void; +} + +export function CopyButtonHandler({ onSuccessChange }: CopyHandlerOptions) { + let timeoutId: number | null = null; + + return async (event: MouseEvent, text?: string) => { + const trimmedText = text ? text.trim() : text; + const success = await copyToClipboard(trimmedText); + + event.preventDefault(); + event.stopPropagation(); + + if (onSuccessChange) { + onSuccessChange(success); + + window.clearTimeout(timeoutId); + if (success) { + timeoutId = window.setTimeout(() => onSuccessChange(false), 2000); + } + } + }; +} \ No newline at end of file diff --git a/src/components/common/copy-button/copy-button.tsx b/src/components/common/copy-button/copy-button.tsx index da4db14f..a48c177d 100644 --- a/src/components/common/copy-button/copy-button.tsx +++ b/src/components/common/copy-button/copy-button.tsx @@ -1,15 +1,20 @@ -import { Component, Prop, h } from '@stencil/core'; +import { Component, Prop, State, h } from '@stencil/core'; import { CopyButton as CopyButtonComponent } from 'common/CopyButton/CopyButton'; +import { CopyButtonHandler } from 'common/CopyButton/CopyButtonHandler'; @Component({ tag: 'mvx-copy-button', shadow: false, }) export class CopyButton { + @State() isSuccess: boolean = false; @Prop() iconClass?: string; @Prop() class?: string; @Prop() text: string; + private handleClick = CopyButtonHandler({ + onSuccessChange: (isSuccess) => (this.isSuccess = isSuccess), + }); render() { return ( @@ -17,6 +22,8 @@ export class CopyButton { iconClass={this.iconClass} class={this.class} text={this.text} + isSuccessOnCopy={this.isSuccess} + handleCopyButtonClick={this.handleClick} /> ); } diff --git a/src/components/functional/sign-transactions-panel/components/SignTransactionsFooter/SignTransactionsFooter.tsx b/src/components/functional/sign-transactions-panel/components/SignTransactionsFooter/SignTransactionsFooter.tsx index 84c73d60..cef756f8 100644 --- a/src/components/functional/sign-transactions-panel/components/SignTransactionsFooter/SignTransactionsFooter.tsx +++ b/src/components/functional/sign-transactions-panel/components/SignTransactionsFooter/SignTransactionsFooter.tsx @@ -8,6 +8,7 @@ import styles from './signTransactionsFooter.styles'; import { CopyButton } from 'common/CopyButton/CopyButton'; import { Tooltip } from 'common/Tooltip/Tooltip'; import { Button } from 'common/Button/Button'; +import { ExplorerLink } from 'common/ExplorerLink/ExplorerLink'; let isWaitingForSignature: boolean = false; let lastCommonData = { ...state.commonData }; @@ -15,9 +16,11 @@ let lastCommonData = { ...state.commonData }; interface SignTransactionsFooterPropsType { tooltipVisible?: boolean; onTooltipVisibilityChange?: (visible: boolean) => void; + isSuccessOnCopy?: boolean; + handleCopyButtonClick?: (event: MouseEvent, text?: string) => void; } -export function SignTransactionsFooter({ tooltipVisible, onTooltipVisibilityChange }: SignTransactionsFooterPropsType) { +export function SignTransactionsFooter({ tooltipVisible, onTooltipVisibilityChange, isSuccessOnCopy, handleCopyButtonClick }: SignTransactionsFooterPropsType) { const currentCommonData = { ...state.commonData }; const hasChanged = JSON.stringify(currentCommonData) !== JSON.stringify(lastCommonData); @@ -157,8 +160,8 @@ export function SignTransactionsFooter({ tooltipVisible, onTooltipVisibilityChan /> )} - - + handleCopyButtonClick?.(event, username ?? address)} /> +
diff --git a/src/components/functional/sign-transactions-panel/sign-transactions-panel.tsx b/src/components/functional/sign-transactions-panel/sign-transactions-panel.tsx index 4536b776..0fdada58 100644 --- a/src/components/functional/sign-transactions-panel/sign-transactions-panel.tsx +++ b/src/components/functional/sign-transactions-panel/sign-transactions-panel.tsx @@ -9,6 +9,7 @@ import type { IOverviewProps, ISignTransactionsPanelData } from './sign-transact import { SignEventsEnum, TransactionTabsEnum } from './sign-transactions-panel.types'; import state, { resetState } from './signTransactionsPanelStore'; import { SignTransactionsFooter } from './components/SignTransactionsFooter/SignTransactionsFooter'; +import { CopyButtonHandler } from 'common/CopyButton/CopyButtonHandler'; // prettier-ignore const styles = { @@ -40,7 +41,8 @@ export class SignTransactionsPanel { @State() isOpen: boolean = false; @State() activeTab: TransactionTabsEnum = TransactionTabsEnum.overview; - @State() private isFooterTooltipVisible: boolean = false; + @State() isFooterTooltipVisible: boolean = false; + @State() isSuccessOnCopy: boolean = false; @Method() async getEventBus() { await this.connectionMonitor.waitForConnection(); @@ -132,6 +134,10 @@ export class SignTransactionsPanel { this.isFooterTooltipVisible = isTooltipVisible; }; + private handleCopyButtonClick = CopyButtonHandler({ + onSuccessChange: (isSuccess) => (this.isSuccessOnCopy = isSuccess), + }); + render() { console.log(styles) //TODO: remove this const transactionTabs = Object.values(TransactionTabsEnum); @@ -174,6 +180,8 @@ export class SignTransactionsPanel { From 261b855b32fc6341989221445e762feb6c874e7d Mon Sep 17 00:00:00 2001 From: Iulia Cimpeanu Date: Wed, 19 Nov 2025 11:12:48 +0200 Subject: [PATCH 9/9] Updated styles --- .../SignTransactionsFooter.tsx | 3 +- .../signTransactionsFooter.styles.ts | 4 +- .../sign-transactions-panel.scss | 38 +++++++++++++++++++ src/global/tailwind.css | 3 ++ 4 files changed, 45 insertions(+), 3 deletions(-) diff --git a/src/components/functional/sign-transactions-panel/components/SignTransactionsFooter/SignTransactionsFooter.tsx b/src/components/functional/sign-transactions-panel/components/SignTransactionsFooter/SignTransactionsFooter.tsx index cef756f8..53450d8e 100644 --- a/src/components/functional/sign-transactions-panel/components/SignTransactionsFooter/SignTransactionsFooter.tsx +++ b/src/components/functional/sign-transactions-panel/components/SignTransactionsFooter/SignTransactionsFooter.tsx @@ -9,6 +9,7 @@ import { CopyButton } from 'common/CopyButton/CopyButton'; import { Tooltip } from 'common/Tooltip/Tooltip'; import { Button } from 'common/Button/Button'; import { ExplorerLink } from 'common/ExplorerLink/ExplorerLink'; +import { Trim } from 'common/Trim/Trim'; let isWaitingForSignature: boolean = false; let lastCommonData = { ...state.commonData }; @@ -153,7 +154,7 @@ export function SignTransactionsFooter({ tooltipVisible, onTooltipVisibilityChan )} {!username && address && ( -