Commit 4b35149a authored by Igor Stuev's avatar Igor Stuev Committed by GitHub

Merge pull request #58 from blockscout/public-tag

public tags page
parents 0854840b 1ab370a9
export const publicTags = [
{
addresses: [
{
address: '0x35317007D203b8a86CA727ad44E473E40450E377',
addressName: 'DarkForest',
},
{
address: '0x35317007D203b8a86CA727ad44E473E40450E378',
addressName: 'DarkForest2',
},
],
tags: [
{
name: 'darkforest',
// colorHex: '#4A5568',
// backgroundHex: '#E2E8F0',
},
],
date: 'Jun 10, 2022',
id: '123',
userName: 'Tatyana',
userEmail: 'sample@gmail.com',
companyName: 'Contract name',
companyUrl: 'contractname.com',
comment: 'Please use #ED8936 color for tag...',
},
{
addresses: [
{
address: '0x35317007D203b8a86CA727ad44E473E40450E377',
},
],
tags: [
{
name: 'OMNI',
colorHex: '#FFFFFF',
backgroundHex: '#1A202C',
},
{
name: '123456789012345678901237123123',
colorHex: '#FFFFFF',
backgroundHex: '#6B46C1',
},
],
date: 'Jun 5, 2022',
id: '456',
},
{
addresses: [
{
address: '0x35317007D203b8a86CA727ad44E473E40450E377',
addressName: 'Contract name',
},
],
tags: [
{
name: 'SANA',
colorHex: '#FFFFFF',
backgroundHex: '#ED8936',
},
],
date: 'Jun 1, 2022',
id: '789',
},
];
export type TPublicTags = Array<TPublicTagItem>
export type TPublicTagItem = {
addresses: Array<TPublicTagAddress>;
tags: Array<TPublicTag>;
// status: typeof STATUS;
date: string;
// id is for react element key, as tag or address may not be unique
id: string;
userName?: string;
userEmail?: string;
companyName?: string;
companyUrl?: string;
comment?: string;
}
export type TPublicTagAddress = {
address: string;
addressName?: string;
}
export type TPublicTag = {
name: string;
colorHex?: string;
backgroundHex?: string;
}
<svg width="16" height="16" viewBox="0 0 16 2" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M.5 1a.937.937 0 0 1 .938-.938h13.124a.938.938 0 0 1 0 1.875H1.438A.937.937 0 0 1 .5 1Z" fill="currentColor"/></svg>
\ No newline at end of file
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M8 .5a.937.937 0 0 1 .938.938v5.625h5.624a.937.937 0 0 1 0 1.875H8.939v5.624a.937.937 0 0 1-1.876 0V8.939H1.438a.937.937 0 1 1 0-1.876h5.625V1.438A.937.937 0 0 1 8 .5Z" fill="currentColor"/></svg>
\ No newline at end of file
import React from 'react';
import type { NextPage } from 'next';
import Head from 'next/head'
import PublicTags from 'ui/pages/PublicTags';
const PublicTagsPage: NextPage = () => {
return (
<>
<Head><title>Public tags</title></Head>
<PublicTags/>
</>
);
}
export default PublicTagsPage
...@@ -18,7 +18,6 @@ const variantPrimary = { ...@@ -18,7 +18,6 @@ const variantPrimary = {
} }
const variantSecondary = { const variantSecondary = {
bg: 'white',
color: 'blue.600', color: 'blue.600',
fontWeight: 600, fontWeight: 600,
borderColor: 'blue.600', borderColor: 'blue.600',
...@@ -32,7 +31,7 @@ const variantSecondary = { ...@@ -32,7 +31,7 @@ const variantSecondary = {
}, },
} }
const variantIconBlue: SystemStyleFunction = (props) => { const variantIcon: SystemStyleFunction = (props) => {
return { return {
color: mode('blue.600', 'blue.300')(props), color: mode('blue.600', 'blue.300')(props),
_hover: { _hover: {
...@@ -41,10 +40,24 @@ const variantIconBlue: SystemStyleFunction = (props) => { ...@@ -41,10 +40,24 @@ const variantIconBlue: SystemStyleFunction = (props) => {
} }
} }
const variantIconBorder = {
color: 'blue.600',
borderColor: 'blue.600',
border: '2px solid',
_hover: {
color: 'blue.400',
borderColor: 'blue.400',
},
_disabled: {
opacity: 0.2,
},
}
const variants = { const variants = {
primary: variantPrimary, primary: variantPrimary,
secondary: variantSecondary, secondary: variantSecondary,
iconBlue: variantIconBlue, icon: variantIcon,
iconBorder: variantIconBorder,
} }
const Button: ComponentStyleConfig = { const Button: ComponentStyleConfig = {
......
import type { ComponentStyleConfig } from '@chakra-ui/theme';
import type { SystemStyleObject } from '@chakra-ui/theme-tools';
const baseStyleLabel: SystemStyleObject = {
_disabled: { opacity: 0.2 },
}
const Checkbox: ComponentStyleConfig = {
baseStyle: {
label: baseStyleLabel,
},
}
export default Checkbox;
...@@ -5,23 +5,21 @@ import type { StyleFunctionProps, PartsStyleFunction } from '@chakra-ui/theme-to ...@@ -5,23 +5,21 @@ import type { StyleFunctionProps, PartsStyleFunction } from '@chakra-ui/theme-to
import type { Dict } from '@chakra-ui/utils'; import type { Dict } from '@chakra-ui/utils';
import getDefaultFormColors from '../utils/getDefaultFormColors'; import getDefaultFormColors from '../utils/getDefaultFormColors';
const activeInputStyles = {
paddingTop: '30px',
paddingBottom: '10px',
}
const getActiveLabelStyles = (theme: Dict, fc: string) => ({ const getActiveLabelStyles = (theme: Dict, fc: string) => ({
color: getColor(theme, fc), color: getColor(theme, fc),
transform: 'scale(0.75) translateY(-10px)', transform: 'scale(0.75) translateY(-10px)',
}) });
const getActiveInputStyles = (theme: Dict, fc: string) => ({
paddingTop: '30px',
paddingBottom: '10px',
borderColor: getColor(theme, fc),
})
const variantFloating: PartsStyleFunction<typeof parts> = (props: StyleFunctionProps) => { const variantFloating: PartsStyleFunction<typeof parts> = (props: StyleFunctionProps) => {
const { theme } = props; const { theme } = props;
const { focusColor: fc, errorColor: ec } = getDefaultFormColors(props); const { focusColor: fc, errorColor: ec } = getDefaultFormColors(props);
const activeLabelStyles = getActiveLabelStyles(theme, fc); const activeLabelStyles = getActiveLabelStyles(theme, fc);
const activeInputStyles = getActiveInputStyles(theme, fc);
return { return {
container: { container: {
...@@ -29,7 +27,7 @@ const variantFloating: PartsStyleFunction<typeof parts> = (props: StyleFunctionP ...@@ -29,7 +27,7 @@ const variantFloating: PartsStyleFunction<typeof parts> = (props: StyleFunctionP
label: { label: {
...activeLabelStyles, ...activeLabelStyles,
}, },
input: { 'input, textarea': {
...activeInputStyles, ...activeInputStyles,
}, },
'label .chakra-form__required-indicator': { 'label .chakra-form__required-indicator': {
...@@ -38,7 +36,6 @@ const variantFloating: PartsStyleFunction<typeof parts> = (props: StyleFunctionP ...@@ -38,7 +36,6 @@ const variantFloating: PartsStyleFunction<typeof parts> = (props: StyleFunctionP
}, },
// label's styles // label's styles
label: { label: {
top: '20px',
left: '22px', left: '22px',
zIndex: 2, zIndex: 2,
position: 'absolute', position: 'absolute',
...@@ -50,27 +47,30 @@ const variantFloating: PartsStyleFunction<typeof parts> = (props: StyleFunctionP ...@@ -50,27 +47,30 @@ const variantFloating: PartsStyleFunction<typeof parts> = (props: StyleFunctionP
fontSize: 'md', fontSize: 'md',
lineHeight: '20px', lineHeight: '20px',
}, },
'input:not(:placeholder-shown) + label': { 'input + label': {
top: 'calc(50% - 10px);',
},
'textarea + label': {
top: '20px',
},
'input:not(:placeholder-shown) + label, textarea:not(:placeholder-shown) + label': {
...activeLabelStyles, ...activeLabelStyles,
}, },
'input[aria-invalid=true] + label': { 'input[aria-invalid=true] + label, textarea[aria-invalid=true] + label': {
color: getColor(theme, ec), color: getColor(theme, ec),
}, },
// input's styles // input's styles
input: { 'input, textarea': {
padding: '20px', padding: '20px',
}, },
'input:not(:placeholder-shown)': { 'input:not(:placeholder-shown), textarea:not(:placeholder-shown)': {
...activeInputStyles, ...activeInputStyles,
}, },
'input[aria-invalid=true]': {
borderColor: getColor(theme, ec),
},
// indicator's styles // indicator's styles
'input:not(:placeholder-shown) + label .chakra-form__required-indicator': { 'input:not(:placeholder-shown) + label .chakra-form__required-indicator, textarea:not(:placeholder-shown) + label .chakra-form__required-indicator': {
color: getColor(theme, fc), color: getColor(theme, fc),
}, },
'input[aria-invalid=true] + label .chakra-form__required-indicator': { 'input[aria-invalid=true] + label .chakra-form__required-indicator, textarea[aria-invalid=true] + label .chakra-form__required-indicator': {
color: getColor(theme, ec), color: getColor(theme, ec),
}, },
}, },
......
import type { inputAnatomy as parts } from '@chakra-ui/anatomy'; import type { inputAnatomy as parts } from '@chakra-ui/anatomy';
import type { ComponentStyleConfig } from '@chakra-ui/theme'; import type { ComponentStyleConfig } from '@chakra-ui/theme';
import type { PartsStyleFunction, SystemStyleObject } from '@chakra-ui/theme-tools'; import type { PartsStyleFunction, SystemStyleObject } from '@chakra-ui/theme-tools';
import { getColor, mode } from '@chakra-ui/theme-tools'; import { mode } from '@chakra-ui/theme-tools';
import getDefaultFormColors from '../utils/getDefaultFormColors';
import getDefaultTransitionProps from '../utils/getDefaultTransitionProps'; import getDefaultTransitionProps from '../utils/getDefaultTransitionProps';
import getOutlinedFieldStyles from '../utils/getOutlinedFieldStyles';
import { Input as InputComponent } from '@chakra-ui/react';
const sizes: Record<string, SystemStyleObject> = { const sizes: Record<string, SystemStyleObject> = {
md: { md: {
...@@ -14,42 +16,21 @@ const sizes: Record<string, SystemStyleObject> = { ...@@ -14,42 +16,21 @@ const sizes: Record<string, SystemStyleObject> = {
h: '60px', h: '60px',
borderRadius: 'base', borderRadius: 'base',
}, },
lg: {
fontSize: 'md',
lineHeight: '20px',
px: '24px',
py: '28px',
h: '80px',
borderRadius: 'base',
},
} }
const variantOutline: PartsStyleFunction<typeof parts> = (props) => { const variantOutline: PartsStyleFunction<typeof parts> = (props) => {
const { theme } = props
const { focusColor: fc, errorColor: ec } = getDefaultFormColors(props);
const transitionProps = getDefaultTransitionProps(); const transitionProps = getDefaultTransitionProps();
return { return {
field: { field: getOutlinedFieldStyles(props),
border: '2px solid',
bg: 'inherit',
borderColor: mode('gray.100', 'whiteAlpha.200')(props),
...transitionProps,
_hover: {
borderColor: mode('gray.300', 'whiteAlpha.400')(props),
},
_readOnly: {
boxShadow: 'none !important',
userSelect: 'all',
},
_disabled: {
opacity: 1,
background: mode('gray.200', 'whiteAlpha.400')(props),
border: 'none',
cursor: 'not-allowed',
},
_invalid: {
borderColor: getColor(theme, ec),
boxShadow: `none`,
},
_focusVisible: {
zIndex: 1,
borderColor: getColor(theme, fc),
boxShadow: '0px 4px 6px -1px rgba(0, 0, 0, 0.1), 0px 2px 4px -1px rgba(0, 0, 0, 0.06)',
},
},
addon: { addon: {
border: '2px solid', border: '2px solid',
borderColor: mode('gray.100', 'whiteAlpha.200')(props), borderColor: mode('gray.100', 'whiteAlpha.200')(props),
...@@ -65,6 +46,10 @@ const Input: ComponentStyleConfig = { ...@@ -65,6 +46,10 @@ const Input: ComponentStyleConfig = {
field: sizes.md, field: sizes.md,
addon: sizes.md, addon: sizes.md,
}, },
lg: {
field: sizes.lg,
addon: sizes.lg,
},
}, },
defaultProps: { defaultProps: {
size: 'md', size: 'md',
...@@ -74,4 +59,9 @@ const Input: ComponentStyleConfig = { ...@@ -74,4 +59,9 @@ const Input: ComponentStyleConfig = {
}, },
} }
InputComponent.defaultProps = {
...InputComponent.defaultProps,
placeholder: ' ',
}
export default Input; export default Input;
import type { ComponentStyleConfig } from '@chakra-ui/theme';
import type { SystemStyleObject } from '@chakra-ui/theme-tools';
const baseStyleLabel: SystemStyleObject = {
_disabled: { opacity: 0.2 },
}
const Radio: ComponentStyleConfig = {
baseStyle: {
label: baseStyleLabel,
},
}
export default Radio;
...@@ -33,7 +33,7 @@ const Table: ComponentMultiStyleConfig = { ...@@ -33,7 +33,7 @@ const Table: ComponentMultiStyleConfig = {
th: { th: {
textTransform: 'none', textTransform: 'none',
fontFamily: 'body', fontFamily: 'body',
fontWeight: 'normal', fontWeight: '500',
overflow: 'hidden', overflow: 'hidden',
color: 'gray.500', color: 'gray.500',
letterSpacing: 'none', letterSpacing: 'none',
......
import type { SystemStyleFunction } from '@chakra-ui/theme-tools';
import type { ComponentStyleConfig } from '@chakra-ui/theme';
import { mode } from '@chakra-ui/theme-tools';
const variantSecondary: SystemStyleFunction = (props) => ({
color: mode('gray.500', 'gray.400')(props),
});
const Text: ComponentStyleConfig = {
variants: {
secondary: variantSecondary,
},
}
export default Text;
import type {
SystemStyleObject,
} from '@chakra-ui/theme-tools'
import type { ComponentStyleConfig } from '@chakra-ui/theme';
import getOutlinedFieldStyles from '../utils/getOutlinedFieldStyles';
import { Textarea as TextareaComponent } from '@chakra-ui/react';
const sizes: Record<string, SystemStyleObject> = {
lg: {
fontSize: 'md',
lineHeight: '20px',
px: '24px',
py: '28px',
h: '160px',
borderRadius: 'base',
},
}
const Textarea: ComponentStyleConfig = {
sizes,
variants: {
outline: (props) => getOutlinedFieldStyles(props),
},
defaultProps: {
size: 'md',
variant: 'outline',
},
}
TextareaComponent.defaultProps = {
...TextareaComponent.defaultProps,
placeholder: ' ',
}
export default Textarea;
import Button from './Button'; import Button from './Button';
import Checkbox from './Checkbox';
import Form from './Form'; import Form from './Form';
import Heading from './Heading'; import Heading from './Heading';
import Input from './Input'; import Input from './Input';
import Link from './Link'; import Link from './Link';
import Modal from './Modal'; import Modal from './Modal';
import Radio from './Radio';
import Table from './Table'; import Table from './Table';
import Tabs from './Tabs'; import Tabs from './Tabs';
import Tag from './Tag'; import Tag from './Tag';
import Text from './Text';
import Textarea from './Textarea';
import Tooltip from './Tooltip'; import Tooltip from './Tooltip';
const components = { const components = {
Button, Button,
Checkbox,
Heading, Heading,
Input, Input,
Form, Form,
Link, Link,
Modal, Modal,
Radio,
Tabs, Tabs,
Table, Table,
Tag, Tag,
Text,
Textarea,
Tooltip, Tooltip,
} }
......
...@@ -2,9 +2,10 @@ import type { StyleFunctionProps } from '@chakra-ui/theme-tools'; ...@@ -2,9 +2,10 @@ import type { StyleFunctionProps } from '@chakra-ui/theme-tools';
import { mode } from '@chakra-ui/theme-tools'; import { mode } from '@chakra-ui/theme-tools';
export default function getDefaultFormColors(props: StyleFunctionProps) { export default function getDefaultFormColors(props: StyleFunctionProps) {
const { focusBorderColor: fc, errorBorderColor: ec } = props const { focusBorderColor: fc, errorBorderColor: ec, filledBorderColor: flc } = props
return { return {
focusColor: fc || mode('brand.700', 'brand.300')(props), focusColor: fc || mode('brand.700', 'brand.300')(props),
errorColor: ec || mode('red.400', 'red.300')(props), errorColor: ec || mode('red.400', 'red.300')(props),
filledColor: flc || mode('gray.300', 'gray.600')(props),
} }
} }
import type { StyleFunctionProps } from '@chakra-ui/theme-tools';
import { mode, getColor } from '@chakra-ui/theme-tools';
import getDefaultFormColors from './getDefaultFormColors';
import getDefaultTransitionProps from './getDefaultTransitionProps';
export default function getOutlinedFieldStyles(props: StyleFunctionProps) {
const { theme } = props
const { focusColor: fc, errorColor: ec, filledColor: flc } = getDefaultFormColors(props);
const transitionProps = getDefaultTransitionProps();
return {
border: '2px solid',
bg: 'inherit',
borderColor: getColor(theme, flc),
...transitionProps,
_hover: {
borderColor: mode('gray.200', 'whiteAlpha.400')(props),
},
_readOnly: {
boxShadow: 'none !important',
userSelect: 'all',
},
_disabled: {
opacity: 1,
background: mode('gray.200', 'whiteAlpha.400')(props),
border: 'none',
cursor: 'not-allowed',
},
_invalid: {
borderColor: getColor(theme, ec),
boxShadow: `none`,
},
_focusVisible: {
zIndex: 1,
borderColor: getColor(theme, fc),
boxShadow: '0px 4px 6px -1px rgba(0, 0, 0, 0.1), 0px 2px 4px -1px rgba(0, 0, 0, 0.06)',
},
':placeholder-shown:not(:focus-visible):not(:hover)': { borderColor: mode('gray.100', 'whiteAlpha.200')(props) },
':-webkit-autofill': { transition: 'background-color 5000s ease-in-out 0s' },
':-webkit-autofill:hover': { transition: 'background-color 5000s ease-in-out 0s' },
':-webkit-autofill:focus': { transition: 'background-color 5000s ease-in-out 0s' },
}
}
...@@ -40,7 +40,6 @@ const ApiKeyForm: React.FC<Props> = ({ data }) => { ...@@ -40,7 +40,6 @@ const ApiKeyForm: React.FC<Props> = ({ data }) => {
<FormControl variant="floating" id="address" isRequired> <FormControl variant="floating" id="address" isRequired>
<Input <Input
{ ...field } { ...field }
placeholder=" "
disabled={ true } disabled={ true }
/> />
<FormLabel>Auto-generated API key token</FormLabel> <FormLabel>Auto-generated API key token</FormLabel>
...@@ -53,7 +52,6 @@ const ApiKeyForm: React.FC<Props> = ({ data }) => { ...@@ -53,7 +52,6 @@ const ApiKeyForm: React.FC<Props> = ({ data }) => {
<FormControl variant="floating" id="name" isRequired> <FormControl variant="floating" id="name" isRequired>
<Input <Input
{ ...field } { ...field }
placeholder=" "
isInvalid={ Boolean(errors.name) } isInvalid={ Boolean(errors.name) }
maxLength={ NAME_MAX_LENGTH } maxLength={ NAME_MAX_LENGTH }
/> />
......
...@@ -5,7 +5,6 @@ import { ...@@ -5,7 +5,6 @@ import {
Td, Td,
HStack, HStack,
Text, Text,
useColorModeValue,
} from '@chakra-ui/react' } from '@chakra-ui/react'
import EditButton from 'ui/shared/EditButton'; import EditButton from 'ui/shared/EditButton';
...@@ -30,7 +29,6 @@ const WatchlistTableItem = ({ item, onEditClick, onDeleteClick }: Props) => { ...@@ -30,7 +29,6 @@ const WatchlistTableItem = ({ item, onEditClick, onDeleteClick }: Props) => {
return onDeleteClick(item); return onDeleteClick(item);
}, [ item, onDeleteClick ]); }, [ item, onDeleteClick ]);
const secondaryColor = useColorModeValue('gray.500', 'gray.400');
return ( return (
<Tr alignItems="top" key={ item.token }> <Tr alignItems="top" key={ item.token }>
<Td> <Td>
...@@ -38,7 +36,7 @@ const WatchlistTableItem = ({ item, onEditClick, onDeleteClick }: Props) => { ...@@ -38,7 +36,7 @@ const WatchlistTableItem = ({ item, onEditClick, onDeleteClick }: Props) => {
<Text fontSize="md" fontWeight={ 600 }>{ item.token }</Text> <Text fontSize="md" fontWeight={ 600 }>{ item.token }</Text>
<CopyToClipboard text={ item.token }/> <CopyToClipboard text={ item.token }/>
</HStack> </HStack>
<Text fontSize="sm" marginTop={ 0.5 } color={ secondaryColor }>{ item.name }</Text> <Text fontSize="sm" marginTop={ 0.5 } variant="secondary">{ item.name }</Text>
</Td> </Td>
<Td> <Td>
<HStack spacing={ 6 }> <HStack spacing={ 6 }>
......
...@@ -25,7 +25,7 @@ const DeleteAddressModal: React.FC<Props> = ({ isOpen, onClose, name }) => { ...@@ -25,7 +25,7 @@ const DeleteAddressModal: React.FC<Props> = ({ isOpen, onClose, name }) => {
onClose={ onClose } onClose={ onClose }
onDelete={ onDelete } onDelete={ onDelete }
title="Remove API key" title="Remove API key"
renderText={ renderText } renderContent={ renderText }
/> />
) )
} }
......
import React, { useCallback, useState } from 'react'; import React, { useCallback, useState } from 'react';
import { Box, Button, Heading, HStack, Link, Text, useColorModeValue, useDisclosure } from '@chakra-ui/react'; import { Box, Button, HStack, Link, Text, useDisclosure } from '@chakra-ui/react';
import Page from 'ui/shared/Page/Page'; import Page from 'ui/shared/Page/Page';
import AccountPageHeader from 'ui/shared/AccountPageHeader';
import ApiKeyTable from 'ui/apiKey/ApiKeyTable/ApiKeyTable'; import ApiKeyTable from 'ui/apiKey/ApiKeyTable/ApiKeyTable';
import ApiKeyModal from 'ui/apiKey/ApiKeyModal/ApiKeyModal'; import ApiKeyModal from 'ui/apiKey/ApiKeyModal/ApiKeyModal';
import DeleteApiKeyModal from 'ui/apiKey/DeleteApiKeyModal'; import DeleteApiKeyModal from 'ui/apiKey/DeleteApiKeyModal';
...@@ -42,14 +42,12 @@ const ApiKeys: React.FC = () => { ...@@ -42,14 +42,12 @@ const ApiKeys: React.FC = () => {
deleteModalProps.onClose(); deleteModalProps.onClose();
}, [ deleteModalProps ]); }, [ deleteModalProps ]);
const captionColor = useColorModeValue('gray.500', 'gray.400');
const canAdd = apiKey.length < DATA_LIMIT const canAdd = apiKey.length < DATA_LIMIT
return ( return (
<Page> <Page>
<Box h="100%"> <Box h="100%">
<Heading as="h1" size="lg" marginBottom={ 8 }>API keys</Heading> <AccountPageHeader text="API keys"/>
<Text marginBottom={ 12 }> <Text marginBottom={ 12 }>
Create API keys to use for your RPC and EthRPC API requests. For more information, see { space } Create API keys to use for your RPC and EthRPC API requests. For more information, see { space }
<Link href="#">“How to use a Blockscout API key”</Link>. <Link href="#">“How to use a Blockscout API key”</Link>.
...@@ -72,7 +70,7 @@ const ApiKeys: React.FC = () => { ...@@ -72,7 +70,7 @@ const ApiKeys: React.FC = () => {
Add API key Add API key
</Button> </Button>
{ !canAdd && ( { !canAdd && (
<Text fontSize="sm" color={ captionColor }> <Text fontSize="sm" variant="secondary">
{ `You have added the maximum number of API keys (${ DATA_LIMIT }). Contact us to request additional keys.` } { `You have added the maximum number of API keys (${ DATA_LIMIT }). Contact us to request additional keys.` }
</Text> </Text>
) } ) }
......
...@@ -2,7 +2,6 @@ import React from 'react'; ...@@ -2,7 +2,6 @@ import React from 'react';
import { import {
Box, Box,
Heading,
Tab, Tab,
Tabs, Tabs,
TabList, TabList,
...@@ -11,6 +10,7 @@ import { ...@@ -11,6 +10,7 @@ import {
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import Page from 'ui/shared/Page/Page'; import Page from 'ui/shared/Page/Page';
import AccountPageHeader from 'ui/shared/AccountPageHeader';
import PrivateAddressTags from 'ui/privateTags/PrivateAddressTags'; import PrivateAddressTags from 'ui/privateTags/PrivateAddressTags';
import PrivateTransactionTags from 'ui/privateTags/PrivateTransactionTags'; import PrivateTransactionTags from 'ui/privateTags/PrivateTransactionTags';
...@@ -22,7 +22,7 @@ const PrivateTags: React.FC = () => { ...@@ -22,7 +22,7 @@ const PrivateTags: React.FC = () => {
return ( return (
<Page> <Page>
<Box h="100%"> <Box h="100%">
<Heading as="h1" size="lg" marginBottom={ 8 }>Private tags</Heading> <AccountPageHeader text="Private tags"/>
<Tabs variant="soft-rounded" colorScheme="blue" isLazy> <Tabs variant="soft-rounded" colorScheme="blue" isLazy>
<TabList marginBottom={ 8 }> <TabList marginBottom={ 8 }>
<Tab>Address</Tab> <Tab>Address</Tab>
......
import React, { useCallback, useState } from 'react';
import { animateScroll } from 'react-scroll';
import {
Box,
useToast,
} from '@chakra-ui/react';
import Page from 'ui/shared/Page/Page';
import AccountPageHeader from 'ui/shared/AccountPageHeader';
import PublicTagsData from 'ui/publicTags/PublicTagsData';
import PublicTagsForm from 'ui/publicTags/PublicTagsForm/PublicTagsForm';
type TScreen = 'data' | 'form';
type TToastAction = 'added' | 'removed';
const toastDescriptions = {
added: 'Your request sent to moderator. Waiting for...',
removed: 'Tags have been removed.',
} as Record<TToastAction, string>;
const PublicTags: React.FC = () => {
const [ screen, setScreen ] = useState<TScreen>('data');
const [ formData, setFormData ] = useState();
const toast = useToast()
const showToast = useCallback((action: TToastAction) => {
toast({
position: 'top-right',
title: 'Success',
description: toastDescriptions[action],
colorScheme: 'green',
status: 'success',
variant: 'subtle',
isClosable: true,
icon: null,
});
}, [ toast ]);
const changeToFormScreen = useCallback((data: any) => {
setFormData(data);
setScreen('form');
animateScroll.scrollToTop({
duration: 500,
delay: 100,
});
}, []);
const changeToDataScreen = useCallback((success?: boolean) => {
if (success) {
showToast('added');
}
setScreen('data');
animateScroll.scrollToTop({
duration: 500,
delay: 100,
});
}, [ showToast ]);
const onTagDelete = useCallback(() => showToast('removed'), [ showToast ]);
let content;
let header;
if (screen === 'data') {
content = <PublicTagsData changeToFormScreen={ changeToFormScreen } onTagDelete={ onTagDelete }/>
header = 'Public tags'
} else {
content = <PublicTagsForm changeToDataScreen={ changeToDataScreen } data={ formData }/>
header = formData ? 'Request to edit a public tag/label' : 'Request a public tag/label';
}
return (
<Page>
<Box h="100%">
<AccountPageHeader text={ header }/>
{ content }
</Box>
</Page>
);
};
export default PublicTags;
import React, { useCallback, useState } from 'react'; import React, { useCallback, useState } from 'react';
import { Box, Button, Text, useDisclosure, Heading } from '@chakra-ui/react'; import { Box, Button, Text, useDisclosure } from '@chakra-ui/react';
import Page from 'ui/shared/Page/Page'; import Page from 'ui/shared/Page/Page';
import AccountPageHeader from 'ui/shared/AccountPageHeader';
import WatchlistTable from 'ui/watchlist/WatchlistTable/WatchlistTable'; import WatchlistTable from 'ui/watchlist/WatchlistTable/WatchlistTable';
import AddressModal from 'ui/watchlist/AddressModal/AddressModal'; import AddressModal from 'ui/watchlist/AddressModal/AddressModal';
...@@ -41,7 +41,7 @@ const WatchList: React.FC = () => { ...@@ -41,7 +41,7 @@ const WatchList: React.FC = () => {
return ( return (
<Page> <Page>
<Box h="100%"> <Box h="100%">
<Heading as="h1" size="lg" marginBottom={ 8 }>Watch list</Heading> <AccountPageHeader text="Watch list"/>
<Text marginBottom={ 12 }>An email notification can be sent to you when an address on your watch list sends or receives any transactions.</Text> <Text marginBottom={ 12 }>An email notification can be sent to you when an address on your watch list sends or receives any transactions.</Text>
{ Boolean(watchlist.length) && ( { Boolean(watchlist.length) && (
<WatchlistTable <WatchlistTable
......
...@@ -36,7 +36,7 @@ const AddressForm: React.FC<Props> = ({ data }) => { ...@@ -36,7 +36,7 @@ const AddressForm: React.FC<Props> = ({ data }) => {
const onSubmit: SubmitHandler<Inputs> = data => console.log(data); const onSubmit: SubmitHandler<Inputs> = data => console.log(data);
const renderAddressInput = useCallback(({ field }: {field: ControllerRenderProps<Inputs, 'address'>}) => { const renderAddressInput = useCallback(({ field }: {field: ControllerRenderProps<Inputs, 'address'>}) => {
return <AddressInput field={ field } isInvalid={ Boolean(errors.address) }/> return <AddressInput<Inputs, 'address'> field={ field } isInvalid={ Boolean(errors.address) }/>
}, [ errors ]); }, [ errors ]);
const renderTagInput = useCallback(({ field }: {field: ControllerRenderProps<Inputs, 'tag'>}) => { const renderTagInput = useCallback(({ field }: {field: ControllerRenderProps<Inputs, 'tag'>}) => {
......
...@@ -26,7 +26,7 @@ const DeletePrivateTagModal: React.FC<Props> = ({ isOpen, onClose, tag }) => { ...@@ -26,7 +26,7 @@ const DeletePrivateTagModal: React.FC<Props> = ({ isOpen, onClose, tag }) => {
onClose={ onClose } onClose={ onClose }
onDelete={ onDelete } onDelete={ onDelete }
title="Removal of private tag" title="Removal of private tag"
renderText={ renderText } renderContent={ renderText }
/> />
) )
} }
......
import React, { useCallback, useState } from 'react';
import type { ChangeEvent } from 'react';
import { Flex, Text, FormControl, FormLabel, Textarea } from '@chakra-ui/react';
import DeleteModal from 'ui/shared/DeleteModal';
import type { TPublicTag } from 'data/publicTags';
type Props = {
isOpen: boolean;
onClose: () => void;
tags: Array<TPublicTag>;
onDeleteSuccess: () => void;
}
const DeletePublicTagModal: React.FC<Props> = ({ isOpen, onClose, tags = [], onDeleteSuccess }) => {
const onDelete = useCallback(() => {
// eslint-disable-next-line no-console
console.log('delete', tags);
onDeleteSuccess();
}, [ tags, onDeleteSuccess ]);
const [ reason, setReason ] = useState<string>('');
const onFieldChange = useCallback((event: ChangeEvent<HTMLTextAreaElement>) => {
setReason(event.currentTarget.value);
}, []);
const renderContent = useCallback(() => {
let text;
if (tags.length === 1) {
text = (
<>
<Text display="flex">Public tag</Text>
<Text fontWeight="600" whiteSpace="pre">{ ` "${ tags[0].name }" ` }</Text>
<Text>will be removed.</Text>
</>
)
}
if (tags.length > 1) {
const tagsText: Array<JSX.Element | string> = [];
tags.forEach((tag, index) => {
if (index < tags.length - 2) {
tagsText.push(<Text fontWeight="600" whiteSpace="pre">{ ` "${ tag.name }"` }</Text>);
tagsText.push(',');
}
if (index === tags.length - 2) {
tagsText.push(<Text fontWeight="600" whiteSpace="pre">{ ` "${ tag.name }" ` }</Text>);
tagsText.push('and');
}
if (index === tags.length - 1) {
tagsText.push(<Text fontWeight="600" whiteSpace="pre">{ ` "${ tag.name }" ` }</Text>);
}
})
text = (
<>
<Text>Public tags</Text>{ tagsText }<Text>will be removed.</Text>
</>
)
}
return (
<>
<Flex marginBottom={ 12 }>
{ text }
</Flex>
<FormControl variant="floating" id="tag-delete">
<Textarea
size="lg"
value={ reason }
onChange={ onFieldChange }
/>
<FormLabel>Why do you want to remove tags?</FormLabel>
</FormControl>
</>
)
}, [ tags, reason, onFieldChange ]);
return (
<DeleteModal
isOpen={ isOpen }
onClose={ onClose }
onDelete={ onDelete }
title="Request to remove a public tag"
renderContent={ renderContent }
/>
)
}
export default DeletePublicTagModal;
import React from 'react';
import {
Table,
Thead,
Tbody,
Tr,
Th,
TableContainer,
} from '@chakra-ui/react'
import type { TPublicTagItem, TPublicTags } from 'data/publicTags';
import PublicTagTableItem from './PublicTagTableItem';
interface Props {
data: TPublicTags;
onEditClick: (data: TPublicTagItem) => void;
onDeleteClick: (data: TPublicTagItem) => void;
}
const PublicTagTable = ({ data, onEditClick, onDeleteClick }: Props) => {
return (
<TableContainer width="100%">
<Table variant="simple" minWidth="600px">
<Thead>
<Tr>
<Th width="60%">Smart contract / Address (0x...)</Th>
<Th width="40%">Public tag</Th>
<Th width="200px">Submission date</Th>
<Th width="108px"></Th>
</Tr>
</Thead>
<Tbody>
{ data.map((item: TPublicTagItem) => (
<PublicTagTableItem
item={ item }
key={ item.id }
onDeleteClick={ onDeleteClick }
onEditClick={ onEditClick }
/>
)) }
</Tbody>
</Table>
</TableContainer>
);
};
export default PublicTagTable;
import React, { useCallback } from 'react';
import {
Box,
Tag,
Text,
Tr,
Td,
HStack,
VStack,
useColorModeValue,
} from '@chakra-ui/react'
import AddressIcon from 'ui/shared/AddressIcon';
import AddressLinkWithTooltip from 'ui/shared/AddressLinkWithTooltip';
import TruncatedTextTooltip from 'ui/shared/TruncatedTextTooltip';
import type { TPublicTagItem, TPublicTagAddress, TPublicTag } from 'data/publicTags';
import EditButton from 'ui/shared/EditButton';
import DeleteButton from 'ui/shared/DeleteButton';
interface Props {
item: TPublicTagItem;
onEditClick: (data: TPublicTagItem) => void;
onDeleteClick: (data: TPublicTagItem) => void;
}
const PublicTagTableItem = ({ item, onEditClick, onDeleteClick }: Props) => {
const onItemEditClick = useCallback(() => {
return onEditClick(item);
}, [ item, onEditClick ]);
const onItemDeleteClick = useCallback(() => {
return onDeleteClick(item);
}, [ item, onDeleteClick ]);
const secondaryColor = useColorModeValue('gray.500', 'gray.400');
return (
<Tr alignItems="top" key={ item.id }>
<Td>
<VStack spacing={ 4 } alignItems="unset">
{ item.addresses.map((adr: TPublicTagAddress) => {
return (
<HStack spacing={ 4 } key={ adr.address } overflow="hidden" alignItems="start">
<AddressIcon address={ adr.address }/>
<Box overflow="hidden">
<AddressLinkWithTooltip address={ adr.address }/>
{ adr.addressName && <Text fontSize="sm" color={ secondaryColor } mt={ 0.5 }>{ adr.addressName }</Text> }
</Box>
</HStack>
)
}) }
</VStack>
</Td>
<Td>
<VStack spacing={ 2 } alignItems="baseline">
{ item.tags.map((tag: TPublicTag) => {
return (
<TruncatedTextTooltip label={ tag.name } key={ tag.name }>
<Tag color={ tag.colorHex || 'gray.600' } background={ tag.backgroundHex || 'gray.200' } lineHeight="24px">
{ tag.name }
</Tag>
</TruncatedTextTooltip>
)
}) }
</VStack>
</Td>
<Td>
<Text fontSize="sm" color={ secondaryColor }>{ item.date }</Text>
</Td>
<Td>
<HStack spacing={ 6 }>
<EditButton onClick={ onItemEditClick }/>
<DeleteButton onClick={ onItemDeleteClick }/>
</HStack>
</Td>
</Tr>
)
};
export default PublicTagTableItem;
import { Box, Text, Button, useDisclosure } from '@chakra-ui/react';
import React, { useCallback, useState } from 'react';
import type { TPublicTagItem, TPublicTag } from 'data/publicTags';
import { publicTags } from 'data/publicTags';
import PublicTagTable from './PublicTagTable/PublicTagTable';
import DeletePublicTagModal from './DeletePublicTagModal'
type Props = {
changeToFormScreen: (data?: any) => void;
onTagDelete: () => void;
}
const PublicTagsData = ({ changeToFormScreen, onTagDelete }: Props) => {
const deleteModalProps = useDisclosure();
const [ deleteModalData, setDeleteModalData ] = useState<Array<TPublicTag>>([]);
const onDeleteModalClose = useCallback(() => {
setDeleteModalData([]);
deleteModalProps.onClose();
}, [ deleteModalProps ]);
const changeToForm = useCallback(() => {
changeToFormScreen();
}, [ changeToFormScreen ]);
const onItemEditClick = useCallback((item: TPublicTagItem) => {
changeToFormScreen(item);
}, [ changeToFormScreen ])
const onItemDeleteClick = useCallback((item: TPublicTagItem) => {
setDeleteModalData(item.tags);
deleteModalProps.onOpen();
}, [ deleteModalProps ]);
return (
<>
<Text marginBottom={ 12 }>
You can request a public category tag which is displayed to all Blockscout users.
Public tags may be added to contract or external addresses, and any associated transactions will inherit that tag.
Clicking a tag opens a page with related information and helps provide context and data organization.
Requests are sent to a moderator for review and approval. This process can take several days.
</Text>
<PublicTagTable data={ publicTags } onEditClick={ onItemEditClick } onDeleteClick={ onItemDeleteClick }/>
<Box marginTop={ 8 }>
<Button
variant="primary"
size="lg"
onClick={ changeToForm }
>
Request to add public tag
</Button>
</Box>
<DeletePublicTagModal
{ ...deleteModalProps }
onClose={ onDeleteModalClose }
tags={ deleteModalData }
onDeleteSuccess={ onTagDelete }
/>
</>
)
}
export default PublicTagsData;
import React, { useCallback } from 'react';
import type { ControllerRenderProps, Control } from 'react-hook-form';
import { RadioGroup, Radio, Stack } from '@chakra-ui/react';
import { Controller } from 'react-hook-form';
import type { Inputs } from './PublicTagsForm';
interface Props {
control: Control<Inputs>;
canReport: boolean;
}
export default function PublicTagFormAction({ control, canReport }: Props) {
const renderRadioGroup = useCallback(({ field }: {field: ControllerRenderProps<Inputs, 'action'>}) => {
return (
<RadioGroup defaultValue="add" value={ field.value } colorScheme="blue">
<Stack spacing={ 5 }>
<Radio value="add">
I want to add tags for my project
</Radio>
<Radio value="report" isDisabled={ canReport }>
I want to report an incorrect public tag
</Radio>
</Stack>
</RadioGroup>
)
}, [ canReport ])
return (
<Controller
name="action"
control={ control }
render={ renderRadioGroup }
/>
)
}
import React, { useCallback } from 'react';
import type { ControllerRenderProps, Control } from 'react-hook-form';
import { IconButton, Icon } from '@chakra-ui/react';
import { Controller } from 'react-hook-form';
import type { Inputs } from './PublicTagsForm';
import AddressInput from 'ui/shared/AddressInput';
import PlusIcon from 'icons/plus.svg';
import MinusIcon from 'icons/minus.svg';
interface Props {
control: Control<Inputs>;
index: number;
fieldsLength: number;
hasError: boolean;
onAddFieldClick: (e: React.SyntheticEvent) => void;
onRemoveFieldClick: (index: number) => (e: React.SyntheticEvent) => void;
}
const MAX_INPUTS_NUM = 10;
export default function PublicTagFormAction({ control, index, fieldsLength, hasError, onAddFieldClick, onRemoveFieldClick }: Props) {
const renderAddressInput = useCallback(({ field }: {field: ControllerRenderProps<Inputs, `addresses.${ number }.address`>}) => {
return (
<AddressInput<Inputs, `addresses.${ number }.address`>
field={ field }
isInvalid={ hasError }
size="lg"
placeholder="Smart contract / Address (0x...)"
/>
)
}, [ hasError ]);
return (
<>
<Controller
name={ `addresses.${ index }.address` }
control={ control }
render={ renderAddressInput }
/>
{ index === fieldsLength - 1 && fieldsLength < MAX_INPUTS_NUM && (
<IconButton
aria-label="add"
variant="iconBorder"
w="30px"
h="30px"
onClick={ onAddFieldClick }
icon={ <Icon as={ PlusIcon } w="20px" h="20px"/> }
position="absolute"
right={ index === 0 ? '-50px' : '-100px' }
top="25px"
/>
) }
{ fieldsLength > 1 && (
<IconButton
aria-label="delete"
variant="iconBorder"
w="30px"
h="30px"
onClick={ onRemoveFieldClick(index) }
icon={ <Icon as={ MinusIcon } w="20px" h="20px"/> }
position="absolute"
right="-50px"
top="25px"
/>
) }</>
)
}
import React, { useCallback } from 'react';
import type { ControllerRenderProps, Control } from 'react-hook-form';
import { FormControl, FormLabel, Textarea } from '@chakra-ui/react';
import { Controller } from 'react-hook-form';
import type { Inputs } from './PublicTagsForm';
interface Props {
control: Control<Inputs>;
}
export default function PublicTagFormComment({ control }: Props) {
const renderComment = useCallback(({ field }: {field: ControllerRenderProps<Inputs, 'comment'>}) => {
return (
<FormControl variant="floating" id={ field.name }>
<Textarea
{ ...field }
size="lg"
/>
<FormLabel>Specify the reason for adding tags and color preference(s).</FormLabel>
</FormControl>
)
}, [])
return (
<Controller
name="comment"
control={ control }
render={ renderComment }
/>
)
}
import {
Button,
Box,
Grid,
GridItem,
Text,
HStack,
} from '@chakra-ui/react';
import React, { useCallback } from 'react';
import type { TPublicTagItem, TPublicTag, TPublicTagAddress } from 'data/publicTags';
import type { Path } from 'react-hook-form';
import { useForm, useFieldArray } from 'react-hook-form';
import PublicTagFormAction from './PublicTagFormAction';
import PublicTagFormComment from './PublicTagFormComment';
import PublicTagsFormInput from './PublicTagsFormInput';
import PublicTagFormAddressInput from './PublicTagFormAddressInput';
type Props = {
changeToDataScreen: (success?: boolean) => void;
data?: TPublicTagItem;
}
export type Inputs = {
userName: string;
userEmail: string;
companyName: string;
companyUrl: string;
action: 'add' | 'report';
tag: string;
addresses: Array<{
name: string;
address: string;
}>;
comment: string;
}
const placeholders = {
userName: 'Your name',
userEmail: 'Email',
companyName: 'Company name',
companyUrl: 'Company website',
tag: 'Public tag (max 35 characters)',
comment: 'Specify the reason for adding tags and color preference(s).',
} as Record<Path<Inputs>, string>;
const ADDRESS_INPUT_BUTTONS_WIDTH = 170;
const PublicTagsForm = ({ changeToDataScreen, data }: Props) => {
const { control, handleSubmit, formState: { errors } } = useForm<Inputs>({
defaultValues: {
userName: data?.userName,
userEmail: data?.userEmail,
companyName: data?.companyName,
companyUrl: data?.companyUrl,
tag: data?.tags.map((tag: TPublicTag) => tag.name).join('; '),
addresses: data?.addresses.map((adr: TPublicTagAddress, index: number) => ({ name: `address.${ index }.address`, address: adr.address })) ||
[ { name: 'address.0.address', address: '' } ],
comment: data?.comment,
},
});
const { fields, append, remove } = useFieldArray({
name: 'addresses',
control,
});
const onAddFieldClick = useCallback(() => append({ address: '' }), [ append ]);
const onRemoveFieldClick = useCallback((index: number) => () => remove(index), [ remove ]);
const changeToData = useCallback(() => {
changeToDataScreen(true);
}, [ changeToDataScreen ]);
return (
<Box width={ `calc(100% - ${ ADDRESS_INPUT_BUTTONS_WIDTH }px)` } maxWidth="844px">
<Text size="sm" variant="secondary" paddingBottom={ 5 }>Company info</Text>
<Grid templateColumns="1fr 1fr" rowGap={ 4 } columnGap={ 5 }>
<GridItem>
<PublicTagsFormInput<Inputs> fieldName="userName" control={ control } label={ placeholders.userName } required/>
</GridItem>
<GridItem>
<PublicTagsFormInput<Inputs> fieldName="companyName" control={ control } label={ placeholders.companyName }/>
</GridItem>
<GridItem>
<PublicTagsFormInput<Inputs> fieldName="userEmail" control={ control } label={ placeholders.userEmail } required/>
</GridItem>
<GridItem>
<PublicTagsFormInput<Inputs> fieldName="companyUrl" control={ control } label={ placeholders.companyUrl }/>
</GridItem>
</Grid>
<Box marginTop={ 4 } marginBottom={ 8 }>
<PublicTagFormAction canReport={ Boolean(data) } control={ control }/>
</Box>
<Text size="sm" variant="secondary" marginBottom={ 5 }>Public tags (2 tags maximum, please use &quot;;&quot; as a divider)</Text>
<Box marginBottom={ 4 }>
<PublicTagsFormInput<Inputs> fieldName="tag" control={ control } label={ placeholders.tag } required/>
</Box>
{ fields.map((field, index) => {
return (
<Box position="relative" key={ field.id } marginBottom={ 4 }>
<PublicTagFormAddressInput
control={ control }
hasError={ Boolean(errors.addresses) }
index={ index }
fieldsLength={ fields.length }
onAddFieldClick={ onAddFieldClick }
onRemoveFieldClick={ onRemoveFieldClick }
/>
</Box>
)
}) }
<Box marginBottom={ 8 }>
<PublicTagFormComment control={ control }/>
</Box>
<HStack spacing={ 6 }>
<Button
size="lg"
variant="primary"
onClick={ handleSubmit(changeToData) }
disabled={ Object.keys(errors).length > 0 }
>
Send request
</Button>
<Button
size="lg"
variant="secondary"
onClick={ changeToData }
>
Cancel
</Button>
</HStack>
</Box>
)
}
export default PublicTagsForm;
import React, { useCallback } from 'react';
import type { ControllerRenderProps, FieldValues, Path, Control } from 'react-hook-form';
import { FormControl, FormLabel, Input } from '@chakra-ui/react';
import { Controller } from 'react-hook-form';
interface Props<TInputs extends FieldValues> {
fieldName: Path<TInputs>;
label: string;
required?: boolean;
control: Control<TInputs, object>;
}
export default function PublicTagsFormInput<Inputs extends FieldValues>({ label, control, required, fieldName }: Props<Inputs>) {
const renderInput = useCallback(({ field }: {field: ControllerRenderProps<Inputs, typeof fieldName>}) => {
return (
<FormControl variant="floating" id={ field.name } isRequired={ required }>
<Input
{ ...field }
size="lg"
required={ required }
/>
<FormLabel>{ label }</FormLabel>
</FormControl>
)
}, [ label, required ]);
return (
<Controller
name={ fieldName }
control={ control }
render={ renderInput }
/>
)
}
import React from 'react';
import { Heading } from '@chakra-ui/react';
const PageHeader = ({ text }: {text: string}) => {
return (
<Heading as="h1" size="lg" marginBottom={ 8 }>{ text }</Heading>
)
}
export default PageHeader;
import React from 'react' import React from 'react'
import type { ControllerRenderProps } from 'react-hook-form'; import type { ControllerRenderProps, FieldValues, Path } from 'react-hook-form';
import { import {
Input, Input,
...@@ -9,29 +9,29 @@ import { ...@@ -9,29 +9,29 @@ import {
const ADDRESS_LENGTH = 42; const ADDRESS_LENGTH = 42;
type Props = { type Props<TInputs extends FieldValues, TInputName extends Path<TInputs>> = {
field: ControllerRenderProps<any, 'address'>; field: ControllerRenderProps<TInputs, TInputName>;
isInvalid: boolean; isInvalid: boolean;
size?: string;
placeholder?: string;
} }
const AddressInput: React.FC<Props> = ({ field, isInvalid }) => { export default function AddressInput<Inputs extends FieldValues, Name extends Path<Inputs>>(
{
field,
isInvalid,
size,
placeholder = 'Address (0x...)',
}: Props<Inputs, Name>) {
return ( return (
<FormControl variant="floating" id="address" isRequired> <FormControl variant="floating" id="address" isRequired>
<Input <Input
{ ...field } { ...field }
placeholder=" "
isInvalid={ isInvalid } isInvalid={ isInvalid }
maxLength={ ADDRESS_LENGTH } maxLength={ ADDRESS_LENGTH }
// TODO: move this to input theme size={ size }
css={{
':-webkit-autofill': { transition: 'background-color 5000s ease-in-out 0s' },
':-webkit-autofill:hover': { transition: 'background-color 5000s ease-in-out 0s' },
':-webkit-autofill:focus': { transition: 'background-color 5000s ease-in-out 0s' },
}}
/> />
<FormLabel>Address (0x...)</FormLabel> <FormLabel>{ placeholder }</FormLabel>
</FormControl> </FormControl>
) )
} }
export default AddressInput
...@@ -22,7 +22,7 @@ const CopyToClipboard = ({ text }: {text: string}) => { ...@@ -22,7 +22,7 @@ const CopyToClipboard = ({ text }: {text: string}) => {
icon={ <CopyIcon/> } icon={ <CopyIcon/> }
w="20px" w="20px"
h="20px" h="20px"
variant="iconBlue" variant="icon"
onClick={ onCopy } onClick={ onCopy }
/> />
</Tooltip> </Tooltip>
......
...@@ -14,7 +14,7 @@ const DeleteButton = ({ onClick }: Props) => { ...@@ -14,7 +14,7 @@ const DeleteButton = ({ onClick }: Props) => {
<Tooltip label="Delete"> <Tooltip label="Delete">
<IconButton <IconButton
aria-label="delete" aria-label="delete"
variant="iconBlue" variant="icon"
w="30px" w="30px"
h="30px" h="30px"
onClick={ onClick } onClick={ onClick }
......
...@@ -16,10 +16,10 @@ type Props = { ...@@ -16,10 +16,10 @@ type Props = {
onClose: () => void; onClose: () => void;
onDelete: () => void; onDelete: () => void;
title: string; title: string;
renderText: () => JSX.Element; renderContent: () => JSX.Element;
} }
const DeleteModal: React.FC<Props> = ({ isOpen, onClose, onDelete, title, renderText }) => { const DeleteModal: React.FC<Props> = ({ isOpen, onClose, onDelete, title, renderContent }) => {
const onDeleteClick = useCallback(() => { const onDeleteClick = useCallback(() => {
onDelete(); onDelete();
...@@ -33,7 +33,7 @@ const DeleteModal: React.FC<Props> = ({ isOpen, onClose, onDelete, title, render ...@@ -33,7 +33,7 @@ const DeleteModal: React.FC<Props> = ({ isOpen, onClose, onDelete, title, render
<ModalHeader fontWeight="500" textStyle="h3">{ title }</ModalHeader> <ModalHeader fontWeight="500" textStyle="h3">{ title }</ModalHeader>
<ModalCloseButton/> <ModalCloseButton/>
<ModalBody> <ModalBody>
{ renderText() } { renderContent() }
</ModalBody> </ModalBody>
<ModalFooter> <ModalFooter>
<Button variant="primary" size="lg" onClick={ onDeleteClick }> <Button variant="primary" size="lg" onClick={ onDeleteClick }>
......
...@@ -14,7 +14,7 @@ const EditButton = ({ onClick }: Props) => { ...@@ -14,7 +14,7 @@ const EditButton = ({ onClick }: Props) => {
<Tooltip label="Edit"> <Tooltip label="Edit">
<IconButton <IconButton
aria-label="edit" aria-label="edit"
variant="iconBlue" variant="icon"
w="30px" w="30px"
h="30px" h="30px"
onClick={ onClick } onClick={ onClick }
......
...@@ -20,7 +20,6 @@ const TagInput: React.FC<Props> = ({ field, isInvalid }) => { ...@@ -20,7 +20,6 @@ const TagInput: React.FC<Props> = ({ field, isInvalid }) => {
<FormControl variant="floating" id="tag" isRequired> <FormControl variant="floating" id="tag" isRequired>
<Input <Input
{ ...field } { ...field }
placeholder=" "
isInvalid={ isInvalid } isInvalid={ isInvalid }
maxLength={ TAG_MAX_LENGTH } maxLength={ TAG_MAX_LENGTH }
/> />
......
...@@ -19,7 +19,6 @@ const AddressInput: React.FC<Props> = ({ field, isInvalid }) => { ...@@ -19,7 +19,6 @@ const AddressInput: React.FC<Props> = ({ field, isInvalid }) => {
<FormControl variant="floating" id="transaction" isRequired> <FormControl variant="floating" id="transaction" isRequired>
<Input <Input
{ ...field } { ...field }
placeholder=" "
isInvalid={ isInvalid } isInvalid={ isInvalid }
maxLength={ HASH_LENGTH } maxLength={ HASH_LENGTH }
/> />
......
...@@ -43,7 +43,7 @@ const AddressForm: React.FC<Props> = ({ data }) => { ...@@ -43,7 +43,7 @@ const AddressForm: React.FC<Props> = ({ data }) => {
const onSubmit: SubmitHandler<Inputs> = data => console.log(data); const onSubmit: SubmitHandler<Inputs> = data => console.log(data);
const renderAddressInput = useCallback(({ field }: {field: ControllerRenderProps<Inputs, 'address'>}) => { const renderAddressInput = useCallback(({ field }: {field: ControllerRenderProps<Inputs, 'address'>}) => {
return <AddressInput field={ field } isInvalid={ Boolean(errors.address) }/> return <AddressInput<Inputs, 'address'> field={ field } isInvalid={ Boolean(errors.address) }/>
}, [ errors ]); }, [ errors ]);
const renderTagInput = useCallback(({ field }: {field: ControllerRenderProps<Inputs, 'tag'>}) => { const renderTagInput = useCallback(({ field }: {field: ControllerRenderProps<Inputs, 'tag'>}) => {
...@@ -85,7 +85,7 @@ const AddressForm: React.FC<Props> = ({ data }) => { ...@@ -85,7 +85,7 @@ const AddressForm: React.FC<Props> = ({ data }) => {
render={ renderTagInput } render={ renderTagInput }
/> />
</Box> </Box>
<Text color="gray.500" fontSize="sm" marginBottom={ 5 }> <Text variant="secondary" fontSize="sm" marginBottom={ 5 }>
Please select what types of notifications you will receive Please select what types of notifications you will receive
</Text> </Text>
<Box marginBottom={ 8 }> <Box marginBottom={ 8 }>
...@@ -102,7 +102,7 @@ const AddressForm: React.FC<Props> = ({ data }) => { ...@@ -102,7 +102,7 @@ const AddressForm: React.FC<Props> = ({ data }) => {
}) } }) }
</Grid> </Grid>
</Box> </Box>
<Text color="gray.500" fontSize="sm" marginBottom={ 5 }>Notification methods</Text> <Text variant="secondary" fontSize="sm" marginBottom={ 5 }>Notification methods</Text>
<Controller <Controller
name="notification" name="notification"
control={ control } control={ control }
......
...@@ -26,7 +26,7 @@ const DeleteAddressModal: React.FC<Props> = ({ isOpen, onClose, address }) => { ...@@ -26,7 +26,7 @@ const DeleteAddressModal: React.FC<Props> = ({ isOpen, onClose, address }) => {
onClose={ onClose } onClose={ onClose }
onDelete={ onDelete } onDelete={ onDelete }
title="Remove address from watch list" title="Remove address from watch list"
renderText={ renderText } renderContent={ renderText }
/> />
) )
} }
......
...@@ -11,7 +11,6 @@ import WalletIcon from 'icons/wallet.svg'; ...@@ -11,7 +11,6 @@ import WalletIcon from 'icons/wallet.svg';
const WatchListAddressItem = ({ item }: {item: TWatchlistItem}) => { const WatchListAddressItem = ({ item }: {item: TWatchlistItem}) => {
const mainTextColor = useColorModeValue('gray.700', 'gray.50'); const mainTextColor = useColorModeValue('gray.700', 'gray.50');
const secondaryTextColor = useColorModeValue('gray.500', 'gray.400');
return ( return (
<HStack spacing={ 3 } align="top"> <HStack spacing={ 3 } align="top">
...@@ -22,14 +21,14 @@ const WatchListAddressItem = ({ item }: {item: TWatchlistItem}) => { ...@@ -22,14 +21,14 @@ const WatchListAddressItem = ({ item }: {item: TWatchlistItem}) => {
<HStack spacing={ 0 } fontSize="sm" h={ 6 }> <HStack spacing={ 0 } fontSize="sm" h={ 6 }>
<Image src="./xdai.png" alt="chain-logo" marginRight="10px" w="16px" h="16px"/> <Image src="./xdai.png" alt="chain-logo" marginRight="10px" w="16px" h="16px"/>
<Text color={ mainTextColor }>{ `xDAI balance:${ nbsp }` + item.tokenBalance }</Text> <Text color={ mainTextColor }>{ `xDAI balance:${ nbsp }` + item.tokenBalance }</Text>
<Text color={ secondaryTextColor }>{ `${ nbsp }($${ item.tokenBalanceUSD } USD)` }</Text> <Text variant="secondary">{ `${ nbsp }($${ item.tokenBalanceUSD } USD)` }</Text>
</HStack> </HStack>
) } ) }
{ item.tokensAmount && ( { item.tokensAmount && (
<HStack spacing={ 0 } fontSize="sm" h={ 6 }> <HStack spacing={ 0 } fontSize="sm" h={ 6 }>
<Icon as={ TokensIcon } marginRight="10px" w="17px" h="16px"/> <Icon as={ TokensIcon } marginRight="10px" w="17px" h="16px"/>
<Text color={ mainTextColor }>{ `Tokens:${ nbsp }` + item.tokensAmount }</Text> <Text color={ mainTextColor }>{ `Tokens:${ nbsp }` + item.tokensAmount }</Text>
<Text color={ secondaryTextColor }>{ `${ nbsp }($${ item.tokensUSD } USD)` }</Text> <Text variant="secondary">{ `${ nbsp }($${ item.tokensUSD } USD)` }</Text>
</HStack> </HStack>
) } ) }
{ item.totalUSD && ( { item.totalUSD && (
......
...@@ -919,6 +919,13 @@ ...@@ -919,6 +919,13 @@
dependencies: dependencies:
"@types/react" "*" "@types/react" "*"
"@types/react-scroll@^1.8.4":
version "1.8.4"
resolved "https://registry.yarnpkg.com/@types/react-scroll/-/react-scroll-1.8.4.tgz#2b6258fb34104d3fcc7a22e8eceaadc669ba3ad1"
integrity sha512-DpHA9PYw42/rBrfKbGE/kAEvHRfyDL/ACfKB/ORWUYuCLi/yGrntxSzYXmg/7TLgQsJ5ma13GCDOzFSOz+8XOA==
dependencies:
"@types/react" "*"
"@types/react@*", "@types/react@18.0.9": "@types/react@*", "@types/react@18.0.9":
version "18.0.9" version "18.0.9"
resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.9.tgz#d6712a38bd6cd83469603e7359511126f122e878" resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.9.tgz#d6712a38bd6cd83469603e7359511126f122e878"
...@@ -2413,6 +2420,11 @@ lodash.mergewith@4.6.2: ...@@ -2413,6 +2420,11 @@ lodash.mergewith@4.6.2:
resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz#617121f89ac55f59047c7aec1ccd6654c6590f55" resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz#617121f89ac55f59047c7aec1ccd6654c6590f55"
integrity sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ== integrity sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==
lodash.throttle@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4"
integrity sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==
lodash@^4.0.0: lodash@^4.0.0:
version "4.17.21" version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
...@@ -2756,7 +2768,7 @@ prelude-ls@^1.2.1: ...@@ -2756,7 +2768,7 @@ prelude-ls@^1.2.1:
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==
prop-types@^15.6.2, prop-types@^15.8.1: prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1:
version "15.8.1" version "15.8.1"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
...@@ -2853,6 +2865,14 @@ react-remove-scroll@^2.5.4: ...@@ -2853,6 +2865,14 @@ react-remove-scroll@^2.5.4:
use-callback-ref "^1.3.0" use-callback-ref "^1.3.0"
use-sidecar "^1.1.2" use-sidecar "^1.1.2"
react-scroll@^1.8.7:
version "1.8.7"
resolved "https://registry.yarnpkg.com/react-scroll/-/react-scroll-1.8.7.tgz#8020035329efad00f03964e18aff6822137de3aa"
integrity sha512-fBOIwweAlhicx8RqP9tQXn/Uhd+DTtVRjw+0VBsIn1Z+MjRYLhTZ0tMoTAU1vOD3dce8mI6copexI4yWII+Luw==
dependencies:
lodash.throttle "^4.1.1"
prop-types "^15.7.2"
react-style-singleton@^2.2.1: react-style-singleton@^2.2.1:
version "2.2.1" version "2.2.1"
resolved "https://registry.yarnpkg.com/react-style-singleton/-/react-style-singleton-2.2.1.tgz#f99e420492b2d8f34d38308ff660b60d0b1205b4" resolved "https://registry.yarnpkg.com/react-style-singleton/-/react-style-singleton-2.2.1.tgz#f99e420492b2d8f34d38308ff660b60d0b1205b4"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment