Merge pull request #209 from XRPLF/feat/json-in-ui
Display tx json in UI inside textarea.
This commit is contained in:
115
components/Textarea.tsx
Normal file
115
components/Textarea.tsx
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
import { styled } from "../stitches.config";
|
||||||
|
|
||||||
|
export const Textarea = styled("textarea", {
|
||||||
|
// Reset
|
||||||
|
appearance: "none",
|
||||||
|
borderWidth: "0",
|
||||||
|
boxSizing: "border-box",
|
||||||
|
fontFamily: "inherit",
|
||||||
|
outline: "none",
|
||||||
|
width: "100%",
|
||||||
|
flex: "1",
|
||||||
|
backgroundColor: "$mauve4",
|
||||||
|
display: "inline-flex",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
borderRadius: "$sm",
|
||||||
|
p: "$2",
|
||||||
|
fontSize: "$md",
|
||||||
|
lineHeight: 1,
|
||||||
|
color: "$mauve12",
|
||||||
|
boxShadow: `0 0 0 1px $colors$mauve8`,
|
||||||
|
WebkitTapHighlightColor: "rgba(0,0,0,0)",
|
||||||
|
"&::before": {
|
||||||
|
boxSizing: "border-box",
|
||||||
|
},
|
||||||
|
"&::after": {
|
||||||
|
boxSizing: "border-box",
|
||||||
|
},
|
||||||
|
fontVariantNumeric: "tabular-nums",
|
||||||
|
|
||||||
|
"&:-webkit-autofill": {
|
||||||
|
boxShadow: "inset 0 0 0 1px $colors$blue6, inset 0 0 0 100px $colors$blue3",
|
||||||
|
},
|
||||||
|
|
||||||
|
"&:-webkit-autofill::first-line": {
|
||||||
|
fontFamily: "$untitled",
|
||||||
|
color: "$mauve12",
|
||||||
|
},
|
||||||
|
|
||||||
|
"&:focus": {
|
||||||
|
boxShadow: `0 0 0 1px $colors$mauve10`,
|
||||||
|
"&:-webkit-autofill": {
|
||||||
|
boxShadow: `0 0 0 1px $colors$mauve10`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"&::placeholder": {
|
||||||
|
color: "$mauve9",
|
||||||
|
},
|
||||||
|
"&:disabled": {
|
||||||
|
pointerEvents: "none",
|
||||||
|
backgroundColor: "$mauve2",
|
||||||
|
color: "$mauve8",
|
||||||
|
cursor: "not-allowed",
|
||||||
|
"&::placeholder": {
|
||||||
|
color: "$mauve7",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
variants: {
|
||||||
|
variant: {
|
||||||
|
ghost: {
|
||||||
|
boxShadow: "none",
|
||||||
|
backgroundColor: "transparent",
|
||||||
|
"@hover": {
|
||||||
|
"&:hover": {
|
||||||
|
boxShadow: "inset 0 0 0 1px $colors$mauve7",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"&:focus": {
|
||||||
|
backgroundColor: "$loContrast",
|
||||||
|
boxShadow: `0 0 0 1px $colors$mauve10`,
|
||||||
|
},
|
||||||
|
"&:disabled": {
|
||||||
|
backgroundColor: "transparent",
|
||||||
|
},
|
||||||
|
"&:read-only": {
|
||||||
|
backgroundColor: "transparent",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
deep: {
|
||||||
|
backgroundColor: "$deep",
|
||||||
|
boxShadow: "none",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
state: {
|
||||||
|
invalid: {
|
||||||
|
boxShadow: "inset 0 0 0 1px $colors$crimson7",
|
||||||
|
"&:focus": {
|
||||||
|
boxShadow:
|
||||||
|
"inset 0px 0px 0px 1px $colors$crimson8, 0px 0px 0px 1px $colors$crimson8",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
valid: {
|
||||||
|
boxShadow: "inset 0 0 0 1px $colors$grass7",
|
||||||
|
"&:focus": {
|
||||||
|
boxShadow:
|
||||||
|
"inset 0px 0px 0px 1px $colors$grass8, 0px 0px 0px 1px $colors$grass8",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
cursor: {
|
||||||
|
default: {
|
||||||
|
cursor: "default",
|
||||||
|
"&:focus": {
|
||||||
|
cursor: "text",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
text: {
|
||||||
|
cursor: "text",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default Textarea;
|
||||||
@@ -15,6 +15,7 @@ import { useSnapshot } from "valtio";
|
|||||||
import state from "../../state";
|
import state from "../../state";
|
||||||
import { streamState } from "../DebugStream";
|
import { streamState } from "../DebugStream";
|
||||||
import { Button } from "..";
|
import { Button } from "..";
|
||||||
|
import Textarea from "../Textarea";
|
||||||
|
|
||||||
interface UIProps {
|
interface UIProps {
|
||||||
setState: (
|
setState: (
|
||||||
@@ -37,22 +38,22 @@ export const TxUI: FC<UIProps> = ({
|
|||||||
txFields,
|
txFields,
|
||||||
} = txState;
|
} = txState;
|
||||||
|
|
||||||
const transactionsOptions = transactionsData.map((tx) => ({
|
const transactionsOptions = transactionsData.map(tx => ({
|
||||||
value: tx.TransactionType,
|
value: tx.TransactionType,
|
||||||
label: tx.TransactionType,
|
label: tx.TransactionType,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const accountOptions: SelectOption[] = accounts.map((acc) => ({
|
const accountOptions: SelectOption[] = accounts.map(acc => ({
|
||||||
label: acc.name,
|
label: acc.name,
|
||||||
value: acc.address,
|
value: acc.address,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const destAccountOptions: SelectOption[] = accounts
|
const destAccountOptions: SelectOption[] = accounts
|
||||||
.map((acc) => ({
|
.map(acc => ({
|
||||||
label: acc.name,
|
label: acc.name,
|
||||||
value: acc.address,
|
value: acc.address,
|
||||||
}))
|
}))
|
||||||
.filter((acc) => acc.value !== selectedAccount?.value);
|
.filter(acc => acc.value !== selectedAccount?.value);
|
||||||
|
|
||||||
const [feeLoading, setFeeLoading] = useState(false);
|
const [feeLoading, setFeeLoading] = useState(false);
|
||||||
|
|
||||||
@@ -107,9 +108,12 @@ export const TxUI: FC<UIProps> = ({
|
|||||||
const specialFields = ["TransactionType", "Account", "Destination"];
|
const specialFields = ["TransactionType", "Account", "Destination"];
|
||||||
|
|
||||||
const otherFields = Object.keys(txFields).filter(
|
const otherFields = Object.keys(txFields).filter(
|
||||||
(k) => !specialFields.includes(k)
|
k => !specialFields.includes(k)
|
||||||
) as [keyof TxFields];
|
) as [keyof TxFields];
|
||||||
|
|
||||||
|
const switchToJson = () =>
|
||||||
|
setState({ editorSavedValue: null, viewType: "json" });
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const defaultOption = transactionsOptions.find(
|
const defaultOption = transactionsOptions.find(
|
||||||
tt => tt.value === "Payment"
|
tt => tt.value === "Payment"
|
||||||
@@ -117,7 +121,7 @@ export const TxUI: FC<UIProps> = ({
|
|||||||
if (defaultOption) {
|
if (defaultOption) {
|
||||||
handleChangeTxType(defaultOption);
|
handleChangeTxType(defaultOption);
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -200,13 +204,13 @@ export const TxUI: FC<UIProps> = ({
|
|||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
)}
|
)}
|
||||||
{otherFields.map((field) => {
|
{otherFields.map(field => {
|
||||||
let _value = txFields[field];
|
let _value = txFields[field];
|
||||||
|
|
||||||
let value: string | undefined;
|
let value: string | undefined;
|
||||||
if (typeof _value === "object") {
|
if (typeof _value === "object") {
|
||||||
if (_value.$type === "json" && typeof _value.$value === "object") {
|
if (_value.$type === "json" && typeof _value.$value === "object") {
|
||||||
value = JSON.stringify(_value.$value);
|
value = JSON.stringify(_value.$value, null, 2);
|
||||||
} else {
|
} else {
|
||||||
value = _value.$value.toString();
|
value = _value.$value.toString();
|
||||||
}
|
}
|
||||||
@@ -214,9 +218,13 @@ export const TxUI: FC<UIProps> = ({
|
|||||||
value = _value?.toString();
|
value = _value?.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
let isXrp = typeof _value === "object" && _value.$type === "xrp";
|
const isXrp = typeof _value === "object" && _value.$type === "xrp";
|
||||||
|
const isJson = typeof _value === "object" && _value.$type === "json";
|
||||||
const isFee = field === "Fee";
|
const isFee = field === "Fee";
|
||||||
|
let rows = isJson
|
||||||
|
? (value?.match(/\n/gm)?.length || 0) + 1
|
||||||
|
: undefined;
|
||||||
|
if (rows && rows > 5) rows = 5;
|
||||||
return (
|
return (
|
||||||
<Flex column key={field} css={{ mb: "$2", pr: "1px" }}>
|
<Flex column key={field} css={{ mb: "$2", pr: "1px" }}>
|
||||||
<Flex
|
<Flex
|
||||||
@@ -231,18 +239,30 @@ export const TxUI: FC<UIProps> = ({
|
|||||||
<Text muted css={{ mr: "$3" }}>
|
<Text muted css={{ mr: "$3" }}>
|
||||||
{field + (isXrp ? " (XRP)" : "")}:{" "}
|
{field + (isXrp ? " (XRP)" : "")}:{" "}
|
||||||
</Text>
|
</Text>
|
||||||
<Input
|
{isJson ? (
|
||||||
value={value}
|
<Textarea
|
||||||
onChange={(e) => {
|
rows={rows}
|
||||||
let value = e.target.value;
|
value={value}
|
||||||
if (value && (value.includes(".") || value.includes(","))) {
|
spellCheck={false}
|
||||||
value = value.replaceAll(".", "").replaceAll(",", "");
|
onChange={switchToJson}
|
||||||
}
|
css={{
|
||||||
|
width: "70%",
|
||||||
handleSetField(field, value);
|
flex: "inherit",
|
||||||
}}
|
resize: "vertical",
|
||||||
css={{ width: "70%", flex: "inherit" }}
|
}}
|
||||||
/>
|
/>
|
||||||
|
) : (
|
||||||
|
<Input
|
||||||
|
value={value}
|
||||||
|
onChange={e => {
|
||||||
|
handleSetField(field, e.target.value);
|
||||||
|
}}
|
||||||
|
css={{
|
||||||
|
width: "70%",
|
||||||
|
flex: "inherit",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{isFee && (
|
{isFee && (
|
||||||
<Button
|
<Button
|
||||||
size="xs"
|
size="xs"
|
||||||
|
|||||||
Reference in New Issue
Block a user