Test page UI layout

This commit is contained in:
muzam
2022-01-05 16:32:07 +05:30
parent 938b567256
commit 7f06876e3e
12 changed files with 552 additions and 114 deletions

View File

@@ -1,18 +1,14 @@
import toast from "react-hot-toast";
import { useSnapshot } from "valtio";
import { ArrowSquareOut, Copy, Wallet, X } from "phosphor-react";
import React, { useEffect, useState } from "react";
import React, { useEffect, useState, FC } from "react";
import Dinero from "dinero.js";
import Button from "./Button";
import { addFaucetAccount, deployHook, importAccount } from "../state/actions";
import state from "../state";
import Box from "./Box";
import Container from "./Container";
import Heading from "./Heading";
import Stack from "./Stack";
import Text from "./Text";
import Flex from "./Flex";
import { Container, Heading, Stack, Text, Flex } from ".";
import {
Dialog,
DialogContent,
@@ -40,13 +36,11 @@ const AccountDialog = ({
}) => {
const snap = useSnapshot(state);
const [showSecret, setShowSecret] = useState(false);
const activeAccount = snap.accounts.find(
(account) => account.address === activeAccountAddress
);
const activeAccount = snap.accounts.find(account => account.address === activeAccountAddress);
return (
<Dialog
open={Boolean(activeAccountAddress)}
onOpenChange={(open) => {
onOpenChange={open => {
setShowSecret(false);
!open && setActiveAccountAddress(null);
}}
@@ -141,7 +135,7 @@ const AccountDialog = ({
}}
ghost
size="xs"
onClick={() => setShowSecret((curr) => !curr)}
onClick={() => setShowSecret(curr => !curr)}
>
{showSecret ? "Hide" : "Show"}
</Button>
@@ -187,11 +181,7 @@ const AccountDialog = ({
target="_blank"
rel="noreferrer noopener"
>
<Button
size="sm"
ghost
css={{ color: "$green11 !important", mt: "$3" }}
>
<Button size="sm" ghost css={{ color: "$green11 !important", mt: "$3" }}>
<ArrowSquareOut size="15px" />
</Button>
</a>
@@ -207,10 +197,8 @@ const AccountDialog = ({
>
{activeAccount && activeAccount?.hooks?.length > 0
? activeAccount?.hooks
.map((i) => {
return `${i?.substring(0, 6)}...${i?.substring(
i.length - 4
)}`;
.map(i => {
return `${i?.substring(0, 6)}...${i?.substring(i.length - 4)}`;
})
.join(", ")
: ""}
@@ -229,15 +217,19 @@ const AccountDialog = ({
);
};
const Accounts = () => {
interface AccountProps {
card?: boolean;
hideDeployBtn?: boolean;
showHookStats?: boolean;
}
const Accounts: FC<AccountProps> = props => {
const snap = useSnapshot(state);
const [activeAccountAddress, setActiveAccountAddress] = useState<
string | null
>(null);
const [activeAccountAddress, setActiveAccountAddress] = useState<string | null>(null);
useEffect(() => {
const fetchAccInfo = async () => {
if (snap.clientStatus === "online") {
const requests = snap.accounts.map((acc) =>
const requests = snap.accounts.map(acc =>
snap.client?.send({
id: acc.address,
command: "account_info",
@@ -249,15 +241,13 @@ const Accounts = () => {
const address = res?.account_data?.Account as string;
const balance = res?.account_data?.Balance as string;
const sequence = res?.account_data?.Sequence as number;
const accountToUpdate = state.accounts.find(
(acc) => acc.address === address
);
const accountToUpdate = state.accounts.find(acc => acc.address === address);
if (accountToUpdate) {
accountToUpdate.xrp = balance;
accountToUpdate.sequence = sequence;
}
});
const objectRequests = snap.accounts.map((acc) => {
const objectRequests = snap.accounts.map(acc => {
return snap.client?.send({
id: `${acc.address}-hooks`,
command: "account_objects",
@@ -267,9 +257,7 @@ const Accounts = () => {
const objectResponses = await Promise.all(objectRequests);
objectResponses.forEach((res: any) => {
const address = res?.account as string;
const accountToUpdate = state.accounts.find(
(acc) => acc.address === address
);
const accountToUpdate = state.accounts.find(acc => acc.address === address);
if (accountToUpdate) {
accountToUpdate.hooks = res.account_objects
.filter((ac: any) => ac?.LedgerEntryType === "Hook")
@@ -299,16 +287,20 @@ const Accounts = () => {
as="div"
css={{
display: "flex",
borderTop: "1px solid $mauve6",
background: "$mauve1",
backgroundColor: props.card ? "$deep" : "$mauve1",
position: "relative",
width: "50%",
width: "100%",
height: "100%",
flexShrink: 0,
borderTop: "1px solid $mauve6",
borderRight: "1px solid $mauve6",
borderLeft: "1px solid $mauve6",
borderBottom: "1px solid $mauve6",
borderRadius: props.card ? "$md" : undefined,
}}
>
<Container css={{ px: 0, flexShrink: 1 }}>
<Flex css={{ py: "$3" }}>
<Container css={{ p: 0, flexShrink: 1, height: "100%" }}>
<Flex css={{ py: "$3", borderBottom: props.card ? "1px solid $mauve6" : undefined }}>
<Heading
as="h3"
css={{
@@ -326,79 +318,94 @@ const Accounts = () => {
<Wallet size="15px" /> <Text css={{ lineHeight: 1 }}>Accounts</Text>
</Heading>
<Flex css={{ ml: "auto", gap: "$3", marginRight: "$3" }}>
<Button ghost size="xs" onClick={() => addFaucetAccount(true)}>
<Button ghost size="sm" onClick={() => addFaucetAccount(true)}>
Create
</Button>
<ImportAccountDialog />
</Flex>
</Flex>
<Box
as="div"
<Stack
css={{
display: "flex",
flexDirection: "column",
width: "100%",
height: "160px",
fontSize: "13px",
wordWrap: "break-word",
fontWeight: "$body",
fontFamily: "$monospace",
gap: 0,
height: "calc(100% - 52px)",
flexWrap: "nowrap",
overflowY: "auto",
wordWrap: "break-word",
}}
>
<Stack css={{ flexDirection: "column", gap: 0 }}>
{snap.accounts.map((account) => (
<Flex
key={account.address + account.name}
onClick={() => setActiveAccountAddress(account.address)}
css={{
gap: "$3",
p: "$2 $3",
justifyContent: "center",
cursor: "pointer",
"@hover": {
"&:hover": {
background: "$mauve3",
},
{snap.accounts.map(account => (
<Flex
column
key={account.address + account.name}
onClick={() => setActiveAccountAddress(account.address)}
css={{
px: "$3",
py: props.card ? "$3" : "$2",
cursor: "pointer",
borderBottom: props.card ? "1px solid $mauve6" : undefined,
"@hover": {
"&:hover": {
background: "$mauve3",
},
},
}}
>
<Flex
row
css={{
justifyContent: "space-between",
}}
>
<Text>{account.name} </Text>
<Text css={{ color: "$mauve9" }}>
{account.address} (
{Dinero({
amount: Number(account?.xrp || "0"),
precision: 6,
})
.toUnit()
.toLocaleString(undefined, {
style: "currency",
currency: "XRP",
currencyDisplay: "name",
})}
)
</Text>
<Button
css={{ ml: "auto" }}
size="xs"
uppercase
isLoading={account.isLoading}
disabled={
account.isLoading ||
!snap.files.filter((file) => file.compiledWatContent).length
}
variant="secondary"
onClick={(e) => {
e.stopPropagation();
deployHook(account);
}}
>
Deploy
</Button>
<Box>
<Text>{account.name} </Text>
<Text css={{ color: "$mauve9" }}>
{account.address} (
{Dinero({
amount: Number(account?.xrp || "0"),
precision: 6,
})
.toUnit()
.toLocaleString(undefined, {
style: "currency",
currency: "XRP",
currencyDisplay: "name",
})}
)
</Text>
</Box>
{!props.hideDeployBtn && (
<Button
css={{ ml: "auto" }}
size="xs"
uppercase
isLoading={account.isLoading}
disabled={
account.isLoading ||
!snap.files.filter(file => file.compiledWatContent).length
}
variant="secondary"
onClick={e => {
e.stopPropagation();
deployHook(account);
}}
>
Deploy
</Button>
)}
</Flex>
))}
</Stack>
</Box>
{props.showHookStats && (
<Text muted small css={{ mt: "$2" }}>
X hooks installed
</Text>
)}
</Flex>
))}
</Stack>
</Container>
<AccountDialog
activeAccountAddress={activeAccountAddress}
@@ -413,7 +420,7 @@ const ImportAccountDialog = () => {
return (
<Dialog>
<DialogTrigger asChild>
<Button ghost size="xs">
<Button ghost size="sm">
Import
</Button>
</DialogTrigger>
@@ -425,7 +432,7 @@ const ImportAccountDialog = () => {
name="secret"
type="password"
value={value}
onChange={(e) => setValue(e.target.value)}
onChange={e => setValue(e.target.value)}
/>
</DialogDescription>

View File

@@ -3,6 +3,23 @@ import Box from "./Box";
const Flex = styled(Box, {
display: "flex",
variants: {
row: {
true: {
flexDirection: "row",
},
},
column: {
true: {
flexDirection: "column",
},
},
fluid: {
true: {
width: '100%'
}
}
},
});
export default Flex;

View File

@@ -110,20 +110,22 @@ export const Input = styled("input", {
backgroundColor: "transparent",
},
},
deep: {
backgroundColor: "$deep",
boxShadow: "none",
},
},
state: {
invalid: {
boxShadow: "inset 0 0 0 1px $colors$red7",
"&:focus": {
boxShadow:
"inset 0px 0px 0px 1px $colors$red8, 0px 0px 0px 1px $colors$red8",
boxShadow: "inset 0px 0px 0px 1px $colors$red8, 0px 0px 0px 1px $colors$red8",
},
},
valid: {
boxShadow: "inset 0 0 0 1px $colors$green7",
"&:focus": {
boxShadow:
"inset 0px 0px 0px 1px $colors$green8, 0px 0px 0px 1px $colors$green8",
boxShadow: "inset 0px 0px 0px 1px $colors$green8, 0px 0px 0px 1px $colors$green8",
},
},
},

55
components/Select.tsx Normal file
View File

@@ -0,0 +1,55 @@
import { FC } from "react";
import { gray, grayDark } from "@radix-ui/colors";
import { useTheme } from "next-themes";
import { styled } from '../stitches.config';
import dynamic from 'next/dynamic';
import type { Props } from "react-select";
const SelectInput = dynamic(() => import("react-select"), { ssr: false });
const Select: FC<Props> = props => {
const { theme } = useTheme();
const isDark = theme === "dark";
const colors: any = {
// primary: pink.pink9,
primary: isDark ? grayDark.gray4 : gray.gray4,
secondary: isDark ? grayDark.gray8 : gray.gray8,
background: isDark ? "rgb(10, 10, 10)" : "rgb(245, 245, 245)",
searchText: isDark ? grayDark.gray12 : gray.gray12,
placeholder: isDark ? grayDark.gray11 : gray.gray11,
};
colors.outline = colors.background;
colors.selected = colors.secondary;
return (
<SelectInput
theme={theme => ({
...theme,
spacing: {
...theme.spacing,
controlHeight: 30
},
colors: {
primary: colors.selected,
primary25: colors.primary,
primary50: colors.primary,
primary75: colors.primary,
danger: colors.primary,
dangerLight: colors.primary,
neutral0: colors.background,
neutral5: colors.primary,
neutral10: colors.primary,
neutral20: colors.outline,
neutral30: colors.primary,
neutral40: colors.primary,
neutral50: colors.placeholder,
neutral60: colors.primary,
neutral70: colors.primary,
neutral80: colors.searchText,
neutral90: colors.primary,
},
})}
{...props}
/>
);
};
export default styled(Select, {});

70
components/Tabs.tsx Normal file
View File

@@ -0,0 +1,70 @@
import { useEffect, useState } from "react";
import type { ReactNode, ReactElement } from "react";
import { Button, Stack } from ".";
interface TabProps {
header?: string;
children: ReactNode;
}
interface Props {
activeIndex?: number;
activeHeader?: string;
headless?: boolean;
children: ReactElement<TabProps>[];
}
export const Tab = (props: TabProps) => null;
export const Tabs = ({ children, activeIndex, activeHeader, headless }: Props) => {
const [active, setActive] = useState(activeIndex || 0);
const tabProps: TabProps[] = children.map(elem => elem.props);
useEffect(() => {
if (activeIndex) setActive(activeIndex);
}, [activeIndex]);
useEffect(() => {
if (activeHeader) {
const idx = tabProps.findIndex(tab => tab.header === activeHeader);
setActive(idx);
}
}, [activeHeader, tabProps]);
return (
<>
{!headless && (
<Stack
css={{
gap: "$3",
flex: 1,
flexWrap: "nowrap",
marginBottom: "-1px",
}}
>
{tabProps.map((prop, idx) => (
<Button
key={prop.header}
role="tab"
tabIndex={idx}
onClick={() => setActive(idx)}
onKeyPress={() => setActive(idx)}
outline={active !== idx}
size="sm"
css={{
"&:hover": {
span: {
visibility: "visible",
},
},
}}
>
{prop.header || idx}
</Button>
))}
</Stack>
)}
{tabProps[active].children}
</>
);
};

View File

@@ -4,6 +4,18 @@ const Text = styled("span", {
fontFamily: "$body",
lineHeight: "$body",
color: "$text",
variants: {
small: {
true: {
fontSize: '$xs'
}
},
muted: {
true: {
color: '$mauve9'
}
}
}
});
export default Text;

15
components/index.tsx Normal file
View File

@@ -0,0 +1,15 @@
export { default as Flex } from './Flex'
export { default as Container } from './Container'
export { default as Heading } from './Heading'
export { default as Stack } from './Stack'
export { default as Text } from './Text'
export { default as Input } from './Input'
export { default as Select } from './Select'
export * from './Tabs'
export * from './AlertDialog'
export { default as Box } from './Box'
export { default as Button } from './Button'
export { default as ButtonGroup } from './ButtonGroup'
export { default as DeployFooter } from './DeployFooter'
export * from './Dialog'
export * from './DropdownMenu'