diff --git a/src/components/CopyButton.stories.tsx b/src/components/CopyButton.stories.tsx new file mode 100644 index 0000000..71f4070 --- /dev/null +++ b/src/components/CopyButton.stories.tsx @@ -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 = (args) => ( + + + +); + +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, + } +}; diff --git a/src/components/CopyButton.tsx b/src/components/CopyButton.tsx new file mode 100644 index 0000000..5da154e --- /dev/null +++ b/src/components/CopyButton.tsx @@ -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 ? ( + + {children} + + + ) : ( + + ); +}; + +const Styled: Record = {}; + +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; + } + `} +`; diff --git a/src/views/dialogs/MnemonicExportDialog.tsx b/src/views/dialogs/MnemonicExportDialog.tsx index b654170..a53c7e0 100644 --- a/src/views/dialogs/MnemonicExportDialog.tsx +++ b/src/views/dialogs/MnemonicExportDialog.tsx @@ -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) => { } > - + ), diff --git a/src/views/dialogs/ReceiveDialog.tsx b/src/views/dialogs/ReceiveDialog.tsx index 0f8db5e..b9546fa 100644 --- a/src/views/dialogs/ReceiveDialog.tsx +++ b/src/views/dialogs/ReceiveDialog.tsx @@ -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 } > - + )} diff --git a/src/views/tables/TransferHistoryTable.tsx b/src/views/tables/TransferHistoryTable.tsx index 59fa60b..cf7c9c3 100644 --- a/src/views/tables/TransferHistoryTable.tsx +++ b/src/views/tables/TransferHistoryTable.tsx @@ -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 }) => ( - - + + {fromAddress ? truncateAddress(fromAddress) : '-'} + {' '} + + {toAddress ? truncateAddress(toAddress) : '-'} + ), }, @@ -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 ? ( - - {truncateAddress(address)} - - - ) : ( - '-' - ); -}; - const Styled: Record = {}; 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; - } - `} -`;