add CopyButton component

This commit is contained in:
Aleka Cheung
2023-08-25 14:07:03 -04:00
parent e30940e7df
commit 64f663ed5b
5 changed files with 112 additions and 76 deletions
+28
View File
@@ -0,0 +1,28 @@
import type { Story } from '@ladle/react';
import { CopyButton, type CopyButtonProps } from '@/components/CopyButton';
import { StoryWrapper } from '.ladle/components';
export const CopyButtonStory: Story<CopyButtonProps> = (args) => (
<StoryWrapper>
<CopyButton {...args} />
</StoryWrapper>
);
CopyButtonStory.args = {
value: 'some text to copy',
};
CopyButtonStory.argTypes = {
shownAsText: {
options: [true, false],
control: { type: 'select' },
defaultValue: false,
},
children: {
options: ['some text to copy'],
control: { type: 'select' },
defaultValue: undefined,
}
};
+65
View File
@@ -0,0 +1,65 @@
import { useState } from 'react';
import styled, { css, type AnyStyledComponent } from 'styled-components';
import { ButtonAction } from '@/constants/buttons';
import { STRING_KEYS } from '@/constants/localization';
import { useStringGetter } from '@/hooks';
import { layoutMixins } from '@/styles/layoutMixins';
import { Button, ButtonProps } from './Button';
import { Icon, IconName } from './Icon';
export type CopyButtonProps = {
value?: string;
shownAsText?: boolean;
children?: React.ReactNode;
} & ButtonProps;
export const CopyButton = ({ value, shownAsText, children, ...buttonProps }: CopyButtonProps) => {
const stringGetter = useStringGetter();
const [copied, setCopied] = useState(false);
const onCopy = () => {
if (!value) return;
setCopied(true);
navigator.clipboard.writeText(value);
setTimeout(() => setCopied(false), 500);
};
return shownAsText ? (
<Styled.InlineRow onClick={onCopy} copied={copied}>
{children}
<Icon iconName={IconName.Copy} />
</Styled.InlineRow>
) : (
<Button
{...buttonProps}
action={copied ? ButtonAction.Create : ButtonAction.Primary}
onClick={onCopy}
>
<Icon iconName={IconName.Copy} />
{children ?? stringGetter({ key: copied ? STRING_KEYS.COPIED : STRING_KEYS.COPY })}
</Button>
);
};
const Styled: Record<string, AnyStyledComponent> = {};
Styled.InlineRow = styled.div<{ copied: boolean }>`
${layoutMixins.inlineRow}
cursor: pointer;
${({ copied }) =>
copied
? css`
filter: brightness(0.8);
`
: css`
&:hover {
filter: brightness(1.1);
text-decoration: underline;
}
`}
`;
+2 -14
View File
@@ -9,7 +9,7 @@ import { breakpoints } from '@/styles';
import { layoutMixins } from '@/styles/layoutMixins';
import { AlertMessage } from '@/components/AlertMessage';
import { Button } from '@/components/Button';
import { CopyButton } from '@/components/CopyButton';
import { Dialog } from '@/components/Dialog';
import { Checkbox } from '@/components/Checkbox';
import { Icon, IconName } from '@/components/Icon';
@@ -30,21 +30,12 @@ export const MnemonicExportDialog = ({ setIsOpen }: ElementProps) => {
const [hasAcknowledged, setHasAcknowledged] = useState(false);
const [currentStep, setCurrentStep] = useState(MnemonicExportStep.AcknowledgeRisk);
const [isShowing, setIsShowing] = useState(false);
const [copied, setCopied] = useState(false);
const stringGetter = useStringGetter();
const { hdKey } = useAccounts();
const { mnemonic } = hdKey ?? {};
const onCopy = () => {
setCopied(true);
if (mnemonic) {
navigator.clipboard.writeText(mnemonic);
}
setTimeout(() => setCopied(false), 500);
};
const title = {
[MnemonicExportStep.AcknowledgeRisk]: stringGetter({ key: STRING_KEYS.REVEAL_SECRET_PHRASE }),
[MnemonicExportStep.DisplayMnemonic]: stringGetter({ key: STRING_KEYS.EXPORT_SECRET_PHRASE }),
@@ -116,10 +107,7 @@ export const MnemonicExportDialog = ({ setIsOpen }: ElementProps) => {
</Styled.WordList>
}
>
<Button action={copied ? ButtonAction.Create : ButtonAction.Primary} onClick={onCopy}>
<Icon iconName={IconName.Copy} />
{stringGetter({ key: copied ? STRING_KEYS.COPIED : STRING_KEYS.COPY })}
</Button>
<CopyButton value={mnemonic} />
</WithReceipt>
</>
),
+2 -15
View File
@@ -1,7 +1,6 @@
import { useState } from 'react';
import styled, { type AnyStyledComponent } from 'styled-components';
import { ButtonAction } from '@/constants/buttons';
import { STRING_KEYS } from '@/constants/localization';
import { DydxChainAsset } from '@/constants/wallets';
@@ -10,9 +9,8 @@ import { layoutMixins } from '@/styles/layoutMixins';
import { useAccounts, useStringGetter } from '@/hooks';
import { AssetIcon } from '@/components/AssetIcon';
import { Button } from '@/components/Button';
import { CopyButton } from '@/components/CopyButton';
import { Dialog } from '@/components/Dialog';
import { Icon, IconName } from '@/components/Icon';
import { QrCode } from '@/components/QrCode';
import { SelectItem, SelectMenu } from '@/components/SelectMenu';
import { WithDetailsReceipt } from '@/components/WithDetailsReceipt';
@@ -31,14 +29,6 @@ export const ReceiveDialog = ({ selectedAsset = DydxChainAsset.DYDX, setIsOpen }
const { dydxAddress } = useAccounts();
const [asset, setAsset] = useState(selectedAsset);
const [copied, setCopied] = useState(false);
const onCopy = () => {
if (!dydxAddress) return;
setCopied(true);
navigator.clipboard.writeText(dydxAddress);
setTimeout(() => setCopied(false), 500);
};
const assetOptions = [
{
@@ -89,10 +79,7 @@ export const ReceiveDialog = ({ selectedAsset = DydxChainAsset.DYDX, setIsOpen }
>
<QrCode hasLogo value={dydxAddress!} />
</Styled.WithDetailsReceipt>
<Button action={copied ? ButtonAction.Create : ButtonAction.Primary} onClick={onCopy}>
<Icon iconName={IconName.Copy} />
{stringGetter({ key: copied ? STRING_KEYS.COPIED : STRING_KEYS.COPY_ADDRESS })}
</Button>
<CopyButton value={dydxAddress} />
</>
)}
</Styled.Content>
+15 -47
View File
@@ -1,9 +1,10 @@
import { useState } from 'react';
import styled, { type AnyStyledComponent, css } from 'styled-components';
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
import type { ColumnSize } from '@react-types/table';
import { type SubaccountTransfer } from '@/constants/abacus';
import { ButtonAction } from '@/constants/buttons';
import { DialogTypes } from '@/constants/dialogs';
import { STRING_KEYS, StringGetterFunction } from '@/constants/localization';
import { useBreakpoints, useStringGetter } from '@/hooks';
@@ -11,20 +12,19 @@ import { useBreakpoints, useStringGetter } from '@/hooks';
import { layoutMixins } from '@/styles/layoutMixins';
import { tradeViewMixins } from '@/styles/tradeViewMixins';
import { Icon, IconName } from '@/components/Icon';
import { Button } from '@/components/Button';
import { CopyButton } from '@/components/CopyButton';
import { Icon } from '@/components/Icon';
import { Link } from '@/components/Link';
import { Output, OutputType } from '@/components/Output';
import { Table, TableCell, TableColumnHeader, type ColumnDef } from '@/components/Table';
import { OnboardingTriggerButton } from '@/views/dialogs/OnboardingTriggerButton';
import { getSubaccountTransfers } from '@/state/accountSelectors';
import { calculateCanAccountTrade } from '@/state/accountCalculators';
import { openDialog } from '@/state/dialogs';
import { truncateAddress } from '@/lib/wallet';
import { Button } from '@/components/Button';
import { ButtonAction } from '@/constants/buttons';
import { openDialog } from '@/state/dialogs';
import { DialogTypes } from '@/constants/dialogs';
import { Link } from '@/components/Link';
import { calculateCanAccountTrade } from '@/state/accountCalculators';
import { OnboardingTriggerButton } from '../dialogs/OnboardingTriggerButton';
const MOBILE_TRANSFERS_PER_PAGE = 50;
@@ -79,8 +79,12 @@ const getTransferHistoryTableColumnDef = ({
),
renderCell: ({ fromAddress, toAddress }) => (
<TableCell stacked>
<CopyableAddress address={fromAddress ?? undefined} />
<CopyableAddress address={toAddress ?? undefined} />
<CopyButton shownAsText value={fromAddress ?? undefined}>
{fromAddress ? truncateAddress(fromAddress) : '-'}
</CopyButton>{' '}
<CopyButton shownAsText value={toAddress ?? undefined}>
{toAddress ? truncateAddress(toAddress) : '-'}
</CopyButton>
</TableCell>
),
},
@@ -168,26 +172,6 @@ export const TransferHistoryTable = ({
);
};
const CopyableAddress = ({ address }: { address?: string }) => {
const [copied, setCopied] = useState(false);
const onCopy = () => {
if (!address) return;
setCopied(true);
navigator.clipboard.writeText(address);
setTimeout(() => setCopied(false), 500);
};
return address ? (
<Styled.CopyableAddress onClick={onCopy} copied={copied}>
{truncateAddress(address)}
<Icon iconName={IconName.Copy} />
</Styled.CopyableAddress>
) : (
'-'
);
};
const Styled: Record<string, AnyStyledComponent> = {};
Styled.Table = styled(Table)`
@@ -209,19 +193,3 @@ Styled.TimeOutput = styled(Output)`
Styled.TxHash = styled(Link)`
justify-content: flex-end;
`;
Styled.CopyableAddress = styled(Styled.InlineRow)<{ copied: boolean }>`
cursor: pointer;
${({ copied }) =>
copied
? css`
filter: brightness(0.8);
`
: css`
&:hover {
filter: brightness(1.1);
text-decoration: underline;
}
`}
`;