Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(bridge-ui-v2): AddressInput component #14572

Merged
merged 2 commits into from
Aug 24, 2023
Merged
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
25 changes: 13 additions & 12 deletions packages/bridge-ui-v2/src/components/Bridge/AddressInput.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
import { t } from 'svelte-i18n';
import type { Address } from 'viem';

import { Alert } from '$components/Alert';
import FlatAlert from '$components/Alert/FlatAlert.svelte';
import { Icon } from '$components/Icon';
import { uid } from '$libs/util/uid';

let input: HTMLInputElement;
Expand All @@ -21,9 +22,11 @@
if (address && address instanceof EventTarget) {
address = (address as HTMLInputElement).value;
}

const addr = address as string;
if (addr.length < 42) {
tooShort = true;
isValidEthereumAddress = false;
} else {
tooShort = false;
isValidEthereumAddress = isAddress(addr);
Expand All @@ -46,26 +49,24 @@
<div class="f-between-center text-secondary-content">
<label class="body-regular" for={inputId}>{$t('inputs.address_input.label')}</label>
</div>
<div class="relative f-items-center">
<div class="relative f-items-center }">
<input
bind:this={input}
id={inputId}
type="string"
placeholder="0x1B77..."
bind:value={ethereumAddress}
on:input={(e) => validateEthereumAddress(e.target)}
class="w-full input-box outline-none py-6 pr-16 px-[26px] title-subsection-bold placeholder:text-tertiary-content" />
class="w-full input-box py-6 pr-16 px-[26px] title-subsection-bold placeholder:text-tertiary-content" />
<button class="absolute right-6 uppercase body-bold text-secondary-content" on:click={clear}>
<Icon type="x-close-circle" fillClass="fill-primary-icon" size={24} />
</button>
</div>
</div>
<div>
<div class="mt-3">
{#if !isValidEthereumAddress && !tooShort}
<Alert type="error" forceColumnFlow>
<!-- TODO: i18n! -->
<p class="font-bold">Invalid address</p>
<p>This doesn't seem to be a valid Ethereum address</p>
</Alert>
<FlatAlert type="error" forceColumnFlow message={$t('inputs.address_input.errors.invalid')} />
{:else if isValidEthereumAddress && !tooShort && showAlert}
<Alert type="success">
<p class="font-bold">Valid address format</p>
</Alert>
<FlatAlert type="success" forceColumnFlow message={$t('inputs.address_input.success')} />
{/if}
</div>
2 changes: 1 addition & 1 deletion packages/bridge-ui-v2/src/components/Bridge/Amount.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@
error={$insufficientBalance}
on:input={inputAmount}
bind:this={inputBox}
class="py-6 pr-16 px-[26px] title-subsection-bold" />
class="py-6 pr-16 px-[26px] title-subsection-bold border-0" />
<!-- TODO: talk to Jane about the MAX button and its styling -->
<button
class="absolute right-6 uppercase hover:font-bold"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { t } from 'svelte-i18n';
import { formatEther } from 'viem';

import { Alert } from '$components/Alert';
import FlatAlert from '$components/Alert/FlatAlert.svelte';
import { Button } from '$components/Button';
import { Icon } from '$components/Icon';
import { InputBox } from '$components/InputBox';
Expand Down Expand Up @@ -195,9 +195,7 @@
</div>

{#if !hasEnoughEth}
<Alert type="warning">
{$t('processing_fee.none.warning')}
</Alert>
<FlatAlert type="error" message={$t('processing_fee.none.warning')} />
{/if}
</li>

Expand Down Expand Up @@ -227,7 +225,7 @@
min="0"
placeholder="0.01"
disabled={selectedFeeMethod !== ProcessingFeeMethod.CUSTOM}
class="w-full input-box outline-none p-6 pr-16 title-subsection-bold placeholder:text-tertiary-content"
class="w-full input-box p-6 pr-16 title-subsection-bold placeholder:text-tertiary-content"
on:input={inputProcessFee}
bind:this={inputBox} />
<span class="absolute right-6 uppercase body-bold text-secondary-content">ETH</span>
Expand Down
63 changes: 27 additions & 36 deletions packages/bridge-ui-v2/src/components/Bridge/Recipient.svelte
Original file line number Diff line number Diff line change
@@ -1,71 +1,61 @@
<script lang="ts">
import { t } from 'svelte-i18n';
import { type Address, isAddress } from 'viem';
import type { Address } from 'viem';

import { Button } from '$components/Button';
import { Icon } from '$components/Icon';
import { InputBox } from '$components/InputBox';
import { Tooltip } from '$components/Tooltip';
import { shortenAddress } from '$libs/util/shortenAddress';
import { uid } from '$libs/util/uid';
import { account } from '$stores/account';

import AddressInput from './AddressInput.svelte';
import { recipientAddress } from './state';

let dialogId = `dialog-${uid()}`;
let addressInput: AddressInput;

let modalOpen = false;
let invalidAddress = false; // TODO: will be used soon
let invalidAddress = false;
let prevRecipientAddress: Maybe<Address> = null;

let inputBox: InputBox;

// Public API
export function clearRecipient() {
inputBox.clear(); // update UI
$recipientAddress = null; // update state
}

function closeModal() {
modalOpen = false;
}

function openModal() {
modalOpen = true;
addressInput.focus();
}

function cancelModal() {
inputBox.clear();

// Revert change of recipient address
$recipientAddress = prevRecipientAddress;

closeModal();
}

function inputRecipientAddress(event: Event) {
const { value } = event.target as HTMLInputElement;

if (isAddress(value)) {
invalidAddress = false;
$recipientAddress = value;
} else {
invalidAddress = true;
}
}

function modalOpenChange(open: boolean) {
if (open) {
// Save it in case we want to cancel
prevRecipientAddress = $recipientAddress;
}
}

inputBox.setValue($recipientAddress as string);
inputBox.focus();
function onAddressValidation(event: CustomEvent<{ isValidEthereumAddress: boolean; addr: Address }>) {
const { isValidEthereumAddress, addr } = event.detail;
if (isValidEthereumAddress) {
$recipientAddress = addr;
invalidAddress = false;
} else {
invalidAddress = true;
}
}

$: modalOpenChange(modalOpen);

$: ethereumAddressBinding = $recipientAddress || undefined;

$: displayedRecipient = $recipientAddress || $account?.address;
</script>

Expand All @@ -84,6 +74,7 @@
<span class="body-small-regular text-secondary-content mt-[4px]">
{#if displayedRecipient}
{shortenAddress(displayedRecipient, 15, 13)}
<span class="text-secondary">{$recipientAddress === $account?.address ? '' : '| Customized'}</span>
{:else}
{$t('recipient.placeholder')}
{/if}
Expand All @@ -99,15 +90,11 @@

<p class="body-regular text-secondary-content mb-3">{$t('recipient.description')}</p>

<div class="relative f-items-center my-[20px]">
<InputBox
placeholder={$t('recipient.placeholder')}
class="w-full input-box outline-none p-6 pr-16 title-subsection-bold placeholder:text-tertiary-content"
on:input={inputRecipientAddress}
bind:this={inputBox} />
<button class="absolute right-6 uppercase body-bold text-secondary-content" on:click={clearRecipient}>
<Icon type="x-close-circle" fillClass="fill-primary-icon" size={24} />
</button>
<div class="relative my-[20px]">
<AddressInput
bind:this={addressInput}
bind:ethereumAddress={ethereumAddressBinding}
on:addressvalidation={onAddressValidation} />
</div>

<div class="grid grid-cols-2 gap-[20px]">
Expand All @@ -117,7 +104,11 @@
class="px-[28px] py-[10px] rounded-full w-auto bg-transparent !border border-primary-brand hover:border-primary-interactive-hover">
<span class="body-bold">{$t('common.cancel')}</span>
</Button>
<Button type="primary" class="px-[28px] py-[10px] rounded-full w-auto" on:click={closeModal}>
<Button
type="primary"
disabled={invalidAddress || !ethereumAddressBinding}
class="px-[28px] py-[10px] rounded-full w-auto"
on:click={closeModal}>
<span class="body-bold">{$t('common.confirm')}</span>
</Button>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
let input: HTMLInputElement;

let classes = classNames(
'w-full input-box placeholder:text-tertiary-content bg-neutral-background border-0 shadow-none outline-none font-bold text-2xl',
'w-full input-box placeholder:text-tertiary-content bg-neutral-background shadow-none font-bold text-2xl',
$$props.class,
);

Expand Down
8 changes: 6 additions & 2 deletions packages/bridge-ui-v2/src/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@
"none": {
"label": "None",
"text": "Use ETH to manually claim your bridged token later",
"warning": "This option cannot be selected. You have not enough ETH to cover the gas fees for claiming."
"warning": "Insufficient ETH to cover the gas fees for claiming."
},
"custom": {
"label": "Custom",
Expand Down Expand Up @@ -189,7 +189,11 @@
},
"address_input": {
"label": "Address",
"placeholder": "Enter an address"
"placeholder": "Enter an address",
"errors": {
"invalid": "Invalid address format"
},
"success": "Valid address format"
},
"amount": {
"label": "Amount",
Expand Down
2 changes: 1 addition & 1 deletion packages/bridge-ui-v2/src/libs/token/getAddress.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { type Address, getContract, type GetContractResult } from '@wagmi/core';
import { getContract, type GetContractResult } from '@wagmi/core';

import { PUBLIC_L1_CHAIN_ID, PUBLIC_L2_CHAIN_ID } from '$env/static/public';

Expand Down
3 changes: 3 additions & 0 deletions packages/bridge-ui-v2/src/styles/components.css
Original file line number Diff line number Diff line change
Expand Up @@ -165,5 +165,8 @@
backdrop-filter: blur(10px);
}

input {
border: 1px solid var(--border-dark-primary, #5d636f);
}
/* TODO: add more components here */
}