Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions src/common/Button/Button.tsx
Original file line number Diff line number Diff line change
@@ -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<string, string>;

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 (
<button
data-testid={dataTestId}
onClick={onClick}
disabled={disabled}
class={{
[styles.button]: true,
[styles.buttonDisabled]: disabled,
[size]: Boolean(size),
[styles.buttonLarge]: size === ButtonSizeEnum.large,
[styles.buttonSmall]: size === ButtonSizeEnum.small,
[variant]: Boolean(variant),
[styles.buttonPrimary]: variant === ButtonVariantEnum.primary,
[styles.buttonSecondary]: variant === ButtonVariantEnum.secondary,
[styles.buttonSecondarySmall]: variant === ButtonVariantEnum.secondary && size === ButtonSizeEnum.small,
[styles.buttonNeutral]: variant === ButtonVariantEnum.neutral,
[className]: Boolean(className),
}}
>
{children}
</button>
);

}
61 changes: 61 additions & 0 deletions src/common/CopyButton/CopyButton.tsx
Original file line number Diff line number Diff line change
@@ -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<string, string>;

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 (
<div
onClick={handleClick}
class={{
[styles.copyButton]: true,
[className]: Boolean(className),
}}
>
<Icon
name={isSuccess ? 'check' : 'copy'}
class={{
[styles.copyButtonIcon]: true,
[styles.copyButtonIconCheck]: isSuccess,
[iconClass]: Boolean(iconClass),
}}
/>
</div>
);
}
8 changes: 8 additions & 0 deletions src/common/Icon/Icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ 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';
import { ArrowRightIcon } from './components/ArrowRightIcon';

export const Icon = ({ name, ...properties }: IconPropsType) => {
if (!name) {
Expand Down Expand Up @@ -104,6 +106,12 @@ export const Icon = ({ name, ...properties }: IconPropsType) => {
case 'arrows-rotate':
return <ArrowsRotateIcon {...properties} />;

case 'spinner':
return <SpinnerIcon />

case 'arrow-right':
return <ArrowRightIcon />

default:
console.error(`No data for the ${name} icon.`);
return null;
Expand Down
13 changes: 13 additions & 0 deletions src/common/Icon/components/ArrowRightIcon/ArrowRightIcon.tsx
Original file line number Diff line number Diff line change
@@ -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<string, string>;

export const ArrowRightIcon = ({ class: className }: { class?: string }) => (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" width="16" height="16" class={{ [styles.arrowRightIcon]: true, [className]: Boolean(className) }}>
<path fill="currentColor" d="M438.6 278.6c12.5-12.5 12.5-32.8 0-45.3l-160-160c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L338.8 224 32 224c-17.7 0-32 14.3-32 32s14.3 32 32 32l306.7 0L233.4 393.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0l160-160z" />
</svg>
);


1 change: 1 addition & 0 deletions src/common/Icon/components/ArrowRightIcon/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './ArrowRightIcon';
16 changes: 16 additions & 0 deletions src/common/Icon/components/SpinnerIcon/SpinnerIcon.tsx
Original file line number Diff line number Diff line change
@@ -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<string, string>;

export const SpinnerIcon = ({ class: className }: { class?: string }) => (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="16" height="16" class={{ [styles.spinnerIcon]: true, [className]: Boolean(className) }}>
<path
fill="currentColor"
d="M304 48a48 48 0 1 0 -96 0 48 48 0 1 0 96 0zm0 416a48 48 0 1 0 -96 0 48 48 0 1 0 96 0zM48 304a48 48 0 1 0 0-96 48 48 0 1 0 0 96zm464-48a48 48 0 1 0 -96 0 48 48 0 1 0 96 0zM142.9 437A48 48 0 1 0 75 369.1 48 48 0 1 0 142.9 437zm0-294.2A48 48 0 1 0 75 75a48 48 0 1 0 67.9 67.9zM369.1 437A48 48 0 1 0 437 369.1 48 48 0 1 0 369.1 437z"
/>
</svg>
);


1 change: 1 addition & 0 deletions src/common/Icon/components/SpinnerIcon/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './SpinnerIcon';
4 changes: 3 additions & 1 deletion src/common/Icon/icon.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ export enum IconNamesEnum {
circleCheck = 'circle-check',
circleInfo = 'circle-info',
coins = 'coins',
arrowsRotate = 'arrows-rotate'
arrowsRotate = 'arrows-rotate',
spinner = 'spinner',
arrowRight = 'arrow-right'
}

export type IconPropsType = JSXBase.IntrinsicElements['svg'] & {
Expand Down
87 changes: 87 additions & 0 deletions src/common/Tooltip/Tooltip.tsx
Original file line number Diff line number Diff line change
@@ -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<string, string>;

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 (
<div
onClick={handleEllipsisClick}
onMouseEnter={handleMouseEvent(true)}
onMouseLeave={handleMouseEvent(false)}
class={{ [styles.tooltip]: true, [className]: Boolean(className) }}
>
{isTooltipVisible && (
<div class={styles.tooltipContentWrapper}
style={{
top: position === 'bottom' ? 'calc(100% + 16px)' : undefined,
bottom: position === 'top' ? 'calc(100% + 16px)' : undefined,
}}
>
<div
class={{
[styles.tooltipContent]: true,
[styles.tooltipContentTop]: position === 'top',
[styles.tooltipContentBottom]: position === 'bottom'
}}
tabIndex={-1}
onFocusout={handleFocusOut}
onClick={(event: MouseEvent) => event.stopPropagation()}
>
{children}
</div>
</div>
)}

<span>{trigger}</span>
</div>
);
}
17 changes: 2 additions & 15 deletions src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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";
Expand Down Expand Up @@ -275,8 +275,6 @@ export namespace Components {
*/
"isToggled": boolean;
}
interface MvxSignTransactionsFooter {
}
interface MvxSignTransactionsHeader {
}
interface MvxSignTransactionsOverview {
Expand Down Expand Up @@ -854,12 +852,6 @@ declare global {
prototype: HTMLMvxSignTransactionsAdvancedDataDecodeElement;
new (): HTMLMvxSignTransactionsAdvancedDataDecodeElement;
};
interface HTMLMvxSignTransactionsFooterElement extends Components.MvxSignTransactionsFooter, HTMLStencilElement {
}
var HTMLMvxSignTransactionsFooterElement: {
prototype: HTMLMvxSignTransactionsFooterElement;
new (): HTMLMvxSignTransactionsFooterElement;
};
interface HTMLMvxSignTransactionsHeaderElement extends Components.MvxSignTransactionsHeader, HTMLStencilElement {
}
var HTMLMvxSignTransactionsHeaderElement: {
Expand Down Expand Up @@ -1111,7 +1103,6 @@ declare global {
"mvx-sign-transactions-advanced": HTMLMvxSignTransactionsAdvancedElement;
"mvx-sign-transactions-advanced-data": HTMLMvxSignTransactionsAdvancedDataElement;
"mvx-sign-transactions-advanced-data-decode": HTMLMvxSignTransactionsAdvancedDataDecodeElement;
"mvx-sign-transactions-footer": HTMLMvxSignTransactionsFooterElement;
"mvx-sign-transactions-header": HTMLMvxSignTransactionsHeaderElement;
"mvx-sign-transactions-overview": HTMLMvxSignTransactionsOverviewElement;
"mvx-sign-transactions-panel": HTMLMvxSignTransactionsPanelElement;
Expand Down Expand Up @@ -1390,8 +1381,6 @@ declare namespace LocalJSX {
*/
"isToggled"?: boolean;
}
interface MvxSignTransactionsFooter {
}
interface MvxSignTransactionsHeader {
}
interface MvxSignTransactionsOverview {
Expand Down Expand Up @@ -1597,7 +1586,6 @@ declare namespace LocalJSX {
"mvx-sign-transactions-advanced": MvxSignTransactionsAdvanced;
"mvx-sign-transactions-advanced-data": MvxSignTransactionsAdvancedData;
"mvx-sign-transactions-advanced-data-decode": MvxSignTransactionsAdvancedDataDecode;
"mvx-sign-transactions-footer": MvxSignTransactionsFooter;
"mvx-sign-transactions-header": MvxSignTransactionsHeader;
"mvx-sign-transactions-overview": MvxSignTransactionsOverview;
"mvx-sign-transactions-panel": MvxSignTransactionsPanel;
Expand Down Expand Up @@ -1669,7 +1657,6 @@ declare module "@stencil/core" {
"mvx-sign-transactions-advanced": LocalJSX.MvxSignTransactionsAdvanced & JSXBase.HTMLAttributes<HTMLMvxSignTransactionsAdvancedElement>;
"mvx-sign-transactions-advanced-data": LocalJSX.MvxSignTransactionsAdvancedData & JSXBase.HTMLAttributes<HTMLMvxSignTransactionsAdvancedDataElement>;
"mvx-sign-transactions-advanced-data-decode": LocalJSX.MvxSignTransactionsAdvancedDataDecode & JSXBase.HTMLAttributes<HTMLMvxSignTransactionsAdvancedDataDecodeElement>;
"mvx-sign-transactions-footer": LocalJSX.MvxSignTransactionsFooter & JSXBase.HTMLAttributes<HTMLMvxSignTransactionsFooterElement>;
"mvx-sign-transactions-header": LocalJSX.MvxSignTransactionsHeader & JSXBase.HTMLAttributes<HTMLMvxSignTransactionsHeaderElement>;
"mvx-sign-transactions-overview": LocalJSX.MvxSignTransactionsOverview & JSXBase.HTMLAttributes<HTMLMvxSignTransactionsOverviewElement>;
"mvx-sign-transactions-panel": LocalJSX.MvxSignTransactionsPanel & JSXBase.HTMLAttributes<HTMLMvxSignTransactionsPanelElement>;
Expand Down
1 change: 1 addition & 0 deletions src/components/common/button/button.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// This is needed to trigger the Stecil Tailwind compilation for inline Tailwind classes.
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import type { EventEmitter } from '@stencil/core';
import { Component, Event, h, Prop } from '@stencil/core';

import type { ButtonSizeEnum, ButtonVariantEnum } from './button.types';
import type { ButtonSizeEnum, ButtonVariantEnum } from '../../../common/Button/button.types';

import { Button as ButtonComponent } from 'common/Button/Button';

@Component({
tag: 'mvx-button',
Expand All @@ -19,20 +21,15 @@ export class Button {

render() {
return (
<button
data-testid={this.dataTestId}
onClick={this.buttonClick.emit.bind(this)}
<ButtonComponent
class={this.class}
dataTestId={this.dataTestId}
disabled={this.disabled}
class={{
button: true,
disabled: this.disabled,
[this.size]: Boolean(this.size),
[this.variant]: Boolean(this.variant),
[this.class]: Boolean(this.class),
}}
size={this.size}
variant={this.variant}
>
<slot />
</button>
</ButtonComponent>
);
}
}
Loading