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: Native Send Component #1874

Draft
wants to merge 77 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 50 commits
Commits
Show all changes
77 commits
Select commit Hold shift + click to select a range
a86df54
send scaffolding
brendan-defi Jan 22, 2025
9847c09
remove ethbalance from provider props
brendan-defi Jan 22, 2025
318e32a
update utils
brendan-defi Jan 22, 2025
ff7d2fa
refactor to remove sendprovider dep
brendan-defi Jan 22, 2025
9afd32d
starting address selection
brendan-defi Jan 22, 2025
96f02d7
update getAddress to support basename reverse res
brendan-defi Jan 23, 2025
53433ae
utils + tests
brendan-defi Jan 23, 2025
7254218
early send flows
brendan-defi Jan 23, 2025
e9254e2
move send into wallet
brendan-defi Jan 23, 2025
8f30262
send in walletisland
brendan-defi Jan 23, 2025
b921ac5
state drives active screen
brendan-defi Jan 23, 2025
91a38a7
use amount input in send
brendan-defi Jan 23, 2025
3d86e6e
send amount input
brendan-defi Jan 24, 2025
51fd103
remove comments, unused imports
brendan-defi Jan 24, 2025
f43e395
fix imports
brendan-defi Jan 24, 2025
08757f8
fix imports, tests
brendan-defi Jan 24, 2025
33ba23d
removed duplicate hook
brendan-defi Jan 24, 2025
210adc5
add close button
brendan-defi Jan 24, 2025
020bbf7
address input reset
brendan-defi Jan 25, 2025
8fd2959
send updates
brendan-defi Jan 25, 2025
e0e628b
token balance component
brendan-defi Jan 25, 2025
779c941
enable actions on token balance
brendan-defi Jan 25, 2025
9eb6db5
token selection
brendan-defi Jan 26, 2025
a44d528
improved token reselection
brendan-defi Jan 27, 2025
ff8e814
improved asset type switch
brendan-defi Jan 27, 2025
25b8a55
update send button
brendan-defi Jan 27, 2025
c534c15
header back button
brendan-defi Jan 27, 2025
55f53ff
fix imports
brendan-defi Jan 27, 2025
8f0a51f
refactor provider for clarity
brendan-defi Jan 27, 2025
35bf65d
update filename
brendan-defi Jan 27, 2025
9df538d
fix subtitle
brendan-defi Jan 28, 2025
1ff95e5
add fundcard when no eth balance
brendan-defi Jan 28, 2025
6982ae7
minor refactor
brendan-defi Jan 28, 2025
2279e9b
design review
brendan-defi Jan 29, 2025
e5cafbd
fix ethBalance race condition
brendan-defi Jan 29, 2025
35b25ca
fix lints
brendan-defi Jan 29, 2025
eb50673
use common files
brendan-defi Jan 29, 2025
250b5ee
subcomponents use props
brendan-defi Jan 29, 2025
4498479
override classnames
brendan-defi Jan 29, 2025
67195c7
input field uses names
brendan-defi Jan 29, 2025
8b49f1d
update send fund wallet
brendan-defi Jan 29, 2025
aafd0b4
use common files
brendan-defi Jan 29, 2025
8cfd20f
remove send exportsc
brendan-defi Jan 29, 2025
faaf63b
update token balance display
brendan-defi Jan 29, 2025
830a6c9
remove max width from token name
brendan-defi Jan 29, 2025
2c24140
fix use max
brendan-defi Jan 29, 2025
13e6444
hardcode input autocomplete props
brendan-defi Jan 29, 2025
4e2921e
send on base and mainnet
brendan-defi Jan 29, 2025
7ef5f64
fix button-within-button bug
brendan-defi Jan 29, 2025
cdc00d7
implement succcessOverride
brendan-defi Jan 29, 2025
1236114
fix lints
brendan-defi Jan 29, 2025
6d9c479
implement lifecycle tracking
brendan-defi Jan 30, 2025
f2ab55b
update types to conform to standard useLifecycleStatus non-null expec…
brendan-defi Jan 30, 2025
d782eb0
fix lints
brendan-defi Jan 30, 2025
767ce16
fix playground package.json
brendan-defi Jan 30, 2025
3a33745
remove error export, improve number transforms
brendan-defi Jan 30, 2025
ba7f51a
improve number transforms
brendan-defi Jan 30, 2025
6bee52c
improved number transforms
brendan-defi Jan 30, 2025
4245695
improved number transforms
brendan-defi Jan 30, 2025
df2930b
utils for readability and reuse
brendan-defi Jan 30, 2025
80f180a
remove identity wrapper
brendan-defi Jan 30, 2025
255cfb4
utility for resolving the displayed address/name
brendan-defi Jan 30, 2025
f0beaef
use utility
brendan-defi Jan 30, 2025
90eacf3
revert type change
brendan-defi Jan 30, 2025
5d031a4
update lifecycle types
brendan-defi Jan 30, 2025
ddf6abf
update and create utils
brendan-defi Jan 30, 2025
9816bcb
button label
brendan-defi Jan 30, 2025
4281d2e
update provider and types
brendan-defi Jan 30, 2025
c76fe05
clean provider
brendan-defi Jan 30, 2025
1f58995
handle null args
brendan-defi Jan 30, 2025
58275a6
refactor to better leverage TextInput
brendan-defi Jan 30, 2025
b13ecd6
consolidate address selection
brendan-defi Jan 30, 2025
5693495
refactor to avoid rerenders
brendan-defi Jan 30, 2025
1ced463
handle status updates
brendan-defi Jan 30, 2025
7418419
header handles address reset
brendan-defi Jan 30, 2025
9262f14
fix lints
brendan-defi Jan 30, 2025
c444463
fix spacing
brendan-defi Jan 31, 2025
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
5 changes: 3 additions & 2 deletions playground/nextjs-app-router/onchainkit/package.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
{
"name": "@coinbase/onchainkit",
"version": "0.36.8",
"version": "0.36.9",
"type": "module",
"repository": "https://github.com/coinbase/onchainkit.git",
"license": "MIT",
"scripts": {
"build": "packemon build --addEngines --addFiles --loadConfigs --declaration && npx packemon validate --no-license --no-people --no-repo && tailwindcss -i ./src/styles/index.css -o ./src/tailwind.css --minify && tailwindcss -i ./src/styles/index-with-tailwind.css -o ./src/styles.css --minify",
"build": "packemon build --addEngines --addFiles --loadConfigs --declaration && tscpaths -p tsconfig.esm.json -s ./src -o ./esm && npx packemon validate --no-license --no-people --no-repo && tailwindcss -i ./src/styles/index.css -o ./src/tailwind.css --minify && tailwindcss -i ./src/styles/index-with-tailwind.css -o ./src/styles.css --minify",
"check": "biome check --write .",
"check:unsafe": "biome check . --fix --unsafe",
"ci:check": "biome ci --formatter-enabled=false --linter-enabled=false",
Expand Down Expand Up @@ -77,6 +77,7 @@
"rimraf": "^5.0.5",
"storybook": "^8.2.9",
"tailwindcss": "^3.4.3",
"tscpaths": "^0.0.9",
"tsup": "^8.3.5",
"typescript": "~5.3.3",
"vite": "^5.3.3",
Expand Down
10 changes: 2 additions & 8 deletions src/internal/components/TextInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,6 @@ type TextInputReact = {
setValue?: (s: string) => void;
value: string;
inputValidator?: (s: string) => boolean;
/** autocomplete attribute handles browser autocomplete, defaults to 'off' */
autoComplete?: string;
/** data-1p-ignore attribute handles password manager autocomplete, defaults to true */
'data-1p-ignore'?: boolean;
};

export const TextInput = forwardRef<HTMLInputElement, TextInputReact>(
Expand All @@ -41,8 +37,6 @@ export const TextInput = forwardRef<HTMLInputElement, TextInputReact>(
inputMode,
value,
inputValidator = () => true,
autoComplete = 'off',
'data-1p-ignore': data1pIgnore = true,
},
ref,
) => {
Expand Down Expand Up @@ -80,8 +74,8 @@ export const TextInput = forwardRef<HTMLInputElement, TextInputReact>(
onChange={handleChange}
onFocus={onFocus}
disabled={disabled}
autoComplete={autoComplete}
data-1p-ignore={data1pIgnore}
autoComplete="off" // autocomplete attribute handles browser autocomplete
data-1p-ignore={true} // data-1p-ignore attribute handles password manager autocomplete
/>
);
},
Expand Down
129 changes: 129 additions & 0 deletions src/internal/components/TokenBalance.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import type { PortfolioTokenWithFiatValue } from '@/api/types';
import { truncateDecimalPlaces } from '@/internal/utils/truncateDecimalPlaces';
import { border, cn, color, text } from '@/styles/theme';
import { TokenImage } from '@/token';
import { useMemo } from 'react';

type TokenBalanceProps = {
token: PortfolioTokenWithFiatValue;
subtitle: string;
showImage?: boolean;
onClick?: (token: PortfolioTokenWithFiatValue) => void;
className?: string;
} & (
| { showAction?: true; actionText?: string; onActionPress?: () => void }
| { showAction?: false; actionText?: never; onActionPress?: never }
);

export function TokenBalance({
token,
subtitle,
showImage = true,
onClick,
showAction = false,
actionText = 'Use max',
onActionPress,
className,
}: TokenBalanceProps) {
const formattedValueInFiat = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
}).format(token.fiatBalance);
brendan-defi marked this conversation as resolved.
Show resolved Hide resolved

const tokenContent = useMemo(() => {
return (
<div className="grid w-full grid-cols-[2.5rem_1fr_auto] items-center gap-3">
<div className="h-10 w-10">
{showImage && <TokenImage token={token} size={40} />}
</div>
<div className="flex min-w-0 flex-col text-left">
<span
className={cn(
text.headline,
color.foreground,
'overflow-hidden text-ellipsis whitespace-nowrap',
)}
>
{token.name?.trim()}
</span>
<span className={cn(text.label2, color.foregroundMuted)}>
{`${truncateDecimalPlaces(
token.cryptoBalance / 10 ** token.decimals,
2,
brendan-defi marked this conversation as resolved.
Show resolved Hide resolved
)} ${token.symbol} ${subtitle}`}
</span>
</div>
<div className="text-right">
{showAction ? (
<span
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why <span > here? Probably should be a button if it's clickable

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can't have a button within a button, and sometimes the whole container is a button

aria-label={actionText}
onClick={(e) => {
e.stopPropagation();
onActionPress?.();
}}
onKeyDown={(e) => {
e.stopPropagation();
onActionPress?.();
}}
className={cn(
text.label2,
color.primary,
border.radius,
'ml-auto p-0.5 font-bold',
'border border-transparent hover:border-[--ock-line-primary]',
)}
>
{actionText}
</span>
) : (
<span
className={cn(
text.label2,
color.foregroundMuted,
'whitespace-nowrap',
)}
>
{formattedValueInFiat}
</span>
)}
</div>
</div>
);
}, [
showImage,
token,
subtitle,
showAction,
actionText,
onActionPress,
formattedValueInFiat,
]);

if (onClick) {
return (
<button
type="button"
onClick={() => onClick(token)}
className={cn(
'flex w-full items-center justify-start gap-4 px-2 py-1',
className,
)}
data-testid="ockTokenBalanceButton"
>
{tokenContent}
</button>
);
}

return (
<div
className={cn(
'flex w-full items-center justify-start gap-4 px-2 py-1',
className,
)}
data-testid="ockTokenBalanceDiv"
>
{tokenContent}
</div>
);
}
13 changes: 11 additions & 2 deletions src/internal/components/amount-input/AmountInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ type AmountInputProps = {
setCryptoAmount: (value: string) => void;
exchangeRate: string;
className?: string;
textClassName?: string;
};

export function AmountInput({
Expand All @@ -24,10 +25,11 @@ export function AmountInput({
asset,
selectedInputType,
currency,
className,
setFiatAmount,
setCryptoAmount,
exchangeRate,
className,
textClassName,
}: AmountInputProps) {
const containerRef = useRef<HTMLDivElement>(null);
const inputRef = useRef<HTMLInputElement>(null);
Expand Down Expand Up @@ -101,6 +103,7 @@ export function AmountInput({
'[appearance:textfield]',
'[&::-webkit-inner-spin-button]:m-0 [&::-webkit-inner-spin-button]:appearance-none',
'[&::-webkit-outer-spin-button]:m-0 [&::-webkit-outer-spin-button]:appearance-none',
textClassName,
)}
value={value}
onChange={handleAmountChange}
Expand All @@ -110,12 +113,17 @@ export function AmountInput({
placeholder="0"
/>

<CurrencyLabel ref={currencySpanRef} label={currencyOrAsset} />
<CurrencyLabel
ref={currencySpanRef}
label={currencyOrAsset}
className={textClassName}
/>
</div>

{/* Hidden span for measuring text width
Without this span the input field would not adjust its width based on the text width and would look like this:
[0.12--------Empty Space-------][ETH] - As you can see the currency symbol is far away from the inputed value

With this span we can measure the width of the text in the input field and set the width of the input field to match the text width
[0.12][ETH] - Now the currency symbol is displayed next to the input field
*/}
Expand All @@ -128,6 +136,7 @@ export function AmountInput({
'text-6xl leading-none outline-none',
'pointer-events-none absolute whitespace-nowrap opacity-0',
'left-[-9999px]', // Hide the span from the DOM
textClassName,
)}
>
{value ? `${value}.` : '0.'}
Expand Down
4 changes: 3 additions & 1 deletion src/internal/components/amount-input/CurrencyLabel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ import { forwardRef } from 'react';

type CurrencyLabelProps = {
label: string;
className?: string;
};

export const CurrencyLabel = forwardRef<HTMLSpanElement, CurrencyLabelProps>(
({ label }, ref) => {
({ label, className }, ref) => {
return (
<span
ref={ref}
Expand All @@ -15,6 +16,7 @@ export const CurrencyLabel = forwardRef<HTMLSpanElement, CurrencyLabelProps>(
color.disabled,
'flex items-center justify-center bg-transparent',
'text-6xl leading-none outline-none',
className,
)}
data-testid="ockCurrencySpan"
>
Expand Down
9 changes: 8 additions & 1 deletion src/token/components/TokenRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,14 @@ export const TokenRow = memo(function TokenRow({
<span className="flex items-center gap-3">
{!hideImage && <TokenImage token={token} size={28} />}
<span className="flex flex-col items-start">
<span className={cn(text.headline)}>{token.name}</span>
<span
className={cn(
text.headline,
'overflow-hidden text-ellipsis whitespace-nowrap text-left',
)}
>
{token.name.trim()}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious what the need for trim() is here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

some tokens have whitespace at the front / end for style /s. we don't want that

</span>
{!hideSymbol && (
<span className={cn(text.body, color.foregroundMuted)}>
{token.symbol}
Expand Down
Loading
Loading