Add popovers with descriptions for each single parameter

This commit is contained in:
dapplion 2019-08-10 18:04:58 +02:00
parent 1c993642af
commit cce04ea80e
8 changed files with 225 additions and 108 deletions

View File

@ -15,6 +15,12 @@ import SupplyVsDemandChart from "./SupplyVsDemandChart";
import ResultParams from "./ResultParams"; import ResultParams from "./ResultParams";
import PriceSimulationChart from "./PriceSimulationChart"; import PriceSimulationChart from "./PriceSimulationChart";
import HelpText from "./HelpText"; import HelpText from "./HelpText";
// Text content
import {
parameterDescriptions,
simulationParameterDescriptions,
resultParameterDescriptions
} from "./parametersDescriptions";
// Utils // Utils
import { getLast, getAvg, pause } from "./utils"; import { getLast, getAvg, pause } from "./utils";
import { import {
@ -122,11 +128,10 @@ const useStyles = makeStyles((theme: Theme) =>
} }
}, },
descriptionTitle: { descriptionTitle: {
fontWeight: theme.typography.fontWeightBold,
padding: theme.spacing(0.5) padding: theme.spacing(0.5)
}, },
descriptionName: { descriptionBody: {
fontWeight: theme.typography.fontWeightBold color: "#dbdfe4"
}, },
descriptionPadding: { descriptionPadding: {
padding: theme.spacing(0.5) padding: theme.spacing(0.5)
@ -134,73 +139,6 @@ const useStyles = makeStyles((theme: Theme) =>
}) })
); );
const parameterDescriptions = [
{
name: "Initial raise",
text: "Total funds raised in the hatch period of the ABC launch"
},
{
name: "Allocation to funding pool",
text:
"The percentage of the funds raised in the Hatch sale that go directly into the project funding pool to compensate future work done in the project"
},
{
name: "Hatch price",
text:
"The price paid per 'ABC token' by community members involved in hatching the project"
},
{
name: "Post-hatch price",
text:
"The price of the 'ABC token' when the curve enters the open phase and is live for public participation"
},
{
name: "Exit tribute",
text:
"The percentage of funds that are diverted to the project funding pool from community members who exit funds from the project by burning 'ABC tokens' in exchange for collateral"
}
];
const simulationParameterDescriptions = [
{
name: "Price",
text: "Price of the token over time."
},
{
name: "Floor price",
text:
"Lower bound of the price guaranteed by the vesting of hatch tokens. It decreases over time as more hatch tokens are allowed to be traded"
},
{
name: "Total exit tributes",
text:
"Cumulative sum of exit tributes collected from only exit /sell transactions"
}
];
const resultParameterDescriptions = [
{
name: "Total reserve",
text:
"Total DAI in the smart contract reserve at the end of the simulated period"
},
{
name: "Funds generated from initial hatch",
text:
"Fraction of the funds (theta) raised during the hatch that go directly to the cause (analytic result)"
},
{
name: "Funds generated from exit tributes",
text:
"Cumulative sum of exit tributes collected from only exit /sell transactions"
},
{
name: "Average slippage",
text:
"Average of the slippage of each transaction occured during the simulation period"
}
];
export default function App() { export default function App() {
const [curveParams, setCurveParams] = useState({ const [curveParams, setCurveParams] = useState({
theta: 0.35, // fraction allocated to reserve (.) theta: 0.35, // fraction allocated to reserve (.)
@ -386,14 +324,17 @@ export default function App() {
const resultFields = [ const resultFields = [
{ {
label: `Total reserve`, label: `Total reserve`,
description: resultParameterDescriptions.totalReserve.text,
value: (+totalReserve.toPrecision(3)).toLocaleString() + " DAI" value: (+totalReserve.toPrecision(3)).toLocaleString() + " DAI"
}, },
{ {
label: `Funds generated from initial hatch`, label: `Funds generated from initial hatch`,
description: resultParameterDescriptions.initialHatchFunds.text,
value: Math.round(d0 * theta).toLocaleString() + " DAI" value: Math.round(d0 * theta).toLocaleString() + " DAI"
}, },
{ {
label: `Funds generated from exit tributes (${withdrawCount} txs)`, label: `Funds generated from exit tributes (${withdrawCount} txs)`,
description: resultParameterDescriptions.exitTributes.text,
value: value:
(+getLast(withdrawFeeTimeseries).toPrecision(3)).toLocaleString() + (+getLast(withdrawFeeTimeseries).toPrecision(3)).toLocaleString() +
" DAI" " DAI"
@ -402,6 +343,7 @@ export default function App() {
label: `Average slippage (avg tx size ${Math.round( label: `Average slippage (avg tx size ${Math.round(
avgTxSize avgTxSize
).toLocaleString()} DAI)`, ).toLocaleString()} DAI)`,
description: resultParameterDescriptions.slippage.text,
value: +(100 * avgSlippage).toFixed(3) + "%" value: +(100 * avgSlippage).toFixed(3) + "%"
} }
]; ];
@ -432,15 +374,21 @@ export default function App() {
</div> </div>
<table> <table>
<tbody> <tbody>
{parameterDescriptions.map(({ name, text }) => ( {[
parameterDescriptions.theta,
parameterDescriptions.p0,
parameterDescriptions.p1,
parameterDescriptions.wFee,
parameterDescriptions.d0
].map(({ name, text }) => (
<tr key={name}> <tr key={name}>
<td> <td>
<Typography className={classes.descriptionName}> <Typography>{name}</Typography>
{name}
</Typography>
</td> </td>
<td> <td>
<Typography>{text}</Typography> <Typography className={classes.descriptionBody}>
{text}
</Typography>
</td> </td>
</tr> </tr>
))} ))}
@ -478,7 +426,7 @@ export default function App() {
<HelpText <HelpText
text={ text={
<div className={classes.descriptionPadding}> <div className={classes.descriptionPadding}>
<Typography> <Typography className={classes.descriptionBody}>
Visualization of the token bonding curve analytic Visualization of the token bonding curve analytic
function on a specific range of reserve [0, 4 * R0]. function on a specific range of reserve [0, 4 * R0].
This result is deterministic given the current set of This result is deterministic given the current set of
@ -533,7 +481,7 @@ export default function App() {
text={ text={
<div className={classes.descriptionContainer}> <div className={classes.descriptionContainer}>
<div className={classes.descriptionPadding}> <div className={classes.descriptionPadding}>
<Typography> <Typography className={classes.descriptionBody}>
This chart shows a 52 week simulation of discrete This chart shows a 52 week simulation of discrete
transactions interacting with the token bonding transactions interacting with the token bonding
curve. Each transaction adds or substract reserve curve. Each transaction adds or substract reserve
@ -546,22 +494,22 @@ export default function App() {
<table> <table>
<tbody> <tbody>
{simulationParameterDescriptions.map( {Object.values(
({ name, text }) => ( simulationParameterDescriptions
<tr key={name}> ).map(({ name, text }) => (
<td> <tr key={name}>
<Typography <td>
className={classes.descriptionName} <Typography>{name}</Typography>
> </td>
{name} <td>
</Typography> <Typography
</td> className={classes.descriptionBody}
<td> >
<Typography>{text}</Typography> {text}
</td> </Typography>
</tr> </td>
) </tr>
)} ))}
</tbody> </tbody>
</table> </table>
</div> </div>
@ -595,18 +543,18 @@ export default function App() {
</div> </div>
<table> <table>
<tbody> <tbody>
{resultParameterDescriptions.map( {Object.values(resultParameterDescriptions).map(
({ name, text }) => ( ({ name, text }) => (
<tr key={name}> <tr key={name}>
<td> <td>
<Typography <Typography>{name}</Typography>
className={classes.descriptionName}
>
{name}
</Typography>
</td> </td>
<td> <td>
<Typography>{text}</Typography> <Typography
className={classes.descriptionBody}
>
{text}
</Typography>
</td> </td>
</tr> </tr>
) )

View File

@ -1,6 +1,7 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import { InputFieldInterface, CurveParamsInterface } from "./types"; import { InputFieldInterface, CurveParamsInterface } from "./types";
import InputParams from "./InputParams"; import InputParams from "./InputParams";
import { parameterDescriptions } from "./parametersDescriptions";
export default function CurveDesignInputParams({ export default function CurveDesignInputParams({
curveParams, curveParams,
@ -42,6 +43,7 @@ export default function CurveDesignInputParams({
const inputFields: InputFieldInterface[] = [ const inputFields: InputFieldInterface[] = [
{ {
label: "Allocation to funding pool", label: "Allocation to funding pool",
description: parameterDescriptions.theta.text,
value: theta, value: theta,
setter: setTheta, setter: setTheta,
min: 0, min: 0,
@ -54,6 +56,7 @@ export default function CurveDesignInputParams({
}, },
{ {
label: "Hatch price (DAI/token)", label: "Hatch price (DAI/token)",
description: parameterDescriptions.p0.text,
value: p0, value: p0,
setter: _setP0, setter: _setP0,
min: 0.01, min: 0.01,
@ -65,6 +68,7 @@ export default function CurveDesignInputParams({
}, },
{ {
label: "Post-hatch price (DAI/token)", label: "Post-hatch price (DAI/token)",
description: parameterDescriptions.p1.text,
value: p1, value: p1,
setter: setP1, setter: setP1,
min: p0 || 0.1, min: p0 || 0.1,
@ -76,6 +80,7 @@ export default function CurveDesignInputParams({
}, },
{ {
label: "Exit tribute", label: "Exit tribute",
description: parameterDescriptions.wFee.text,
value: wFee, value: wFee,
setter: setWFee, setter: setWFee,
min: 0, min: 0,

View File

@ -1,11 +1,11 @@
import React from "react"; import React from "react";
import { createStyles, makeStyles, Theme } from "@material-ui/core/styles"; import { createStyles, makeStyles, Theme } from "@material-ui/core/styles";
import Typography from "@material-ui/core/Typography";
import Grid from "@material-ui/core/Grid"; import Grid from "@material-ui/core/Grid";
import TextField from "@material-ui/core/TextField"; import TextField from "@material-ui/core/TextField";
import NumberFormat from "react-number-format"; import NumberFormat from "react-number-format";
import { InputFieldInterface } from "./types"; import { InputFieldInterface } from "./types";
import PrettoSlider from "./PrettoSlider"; import PrettoSlider from "./PrettoSlider";
import TextWithPopover from "./TextWithPopover";
const useStyles = makeStyles((theme: Theme) => const useStyles = makeStyles((theme: Theme) =>
createStyles({ createStyles({
@ -81,6 +81,7 @@ export default function InputParams({
{inputFields.map( {inputFields.map(
({ ({
label, label,
description,
value, value,
setter, setter,
min, min,
@ -103,9 +104,7 @@ export default function InputParams({
return ( return (
<Grid key={label} container spacing={0} className={classes.listBox}> <Grid key={label} container spacing={0} className={classes.listBox}>
<Grid item xs={6} className={classes.leftContainer}> <Grid item xs={6} className={classes.leftContainer}>
<Typography id={label} gutterBottom> <TextWithPopover content={label} popoverText={description} />
{label}
</Typography>
</Grid> </Grid>
<Grid item xs={2} className={classes.centerContainer}> <Grid item xs={2} className={classes.centerContainer}>

View File

@ -2,6 +2,7 @@ import React from "react";
import { createStyles, makeStyles, Theme } from "@material-ui/core/styles"; import { createStyles, makeStyles, Theme } from "@material-ui/core/styles";
import Typography from "@material-ui/core/Typography"; import Typography from "@material-ui/core/Typography";
import Grid from "@material-ui/core/Grid"; import Grid from "@material-ui/core/Grid";
import TextWithPopover from "./TextWithPopover";
const useStyles = makeStyles((theme: Theme) => const useStyles = makeStyles((theme: Theme) =>
createStyles({ createStyles({
@ -45,6 +46,7 @@ export default function ResultParams({
}: { }: {
resultFields: { resultFields: {
label: string; label: string;
description: string;
value: number | string; value: number | string;
}[]; }[];
}) { }) {
@ -52,12 +54,10 @@ export default function ResultParams({
return ( return (
<div className={classes.listBoxContainer}> <div className={classes.listBoxContainer}>
{resultFields.map(({ label, value }) => ( {resultFields.map(({ label, description, value }) => (
<Grid key={label} container spacing={0} className={classes.listBox}> <Grid key={label} container spacing={0} className={classes.listBox}>
<Grid item xs={8} className={classes.leftContainer}> <Grid item xs={8} className={classes.leftContainer}>
<Typography id={label} gutterBottom> <TextWithPopover content={label} popoverText={description} />
{label}
</Typography>
</Grid> </Grid>
<Grid item xs={4} className={classes.centerContainer}> <Grid item xs={4} className={classes.centerContainer}>

View File

@ -1,6 +1,7 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import { InputFieldInterface, CurveParamsInterface } from "./types"; import { InputFieldInterface, CurveParamsInterface } from "./types";
import InputParams from "./InputParams"; import InputParams from "./InputParams";
import { parameterDescriptions } from "./parametersDescriptions";
export default function CurveDesignInputParams({ export default function CurveDesignInputParams({
curveParams, curveParams,
@ -25,6 +26,7 @@ export default function CurveDesignInputParams({
const inputFields: InputFieldInterface[] = [ const inputFields: InputFieldInterface[] = [
{ {
label: "Initial raise (DAI)", label: "Initial raise (DAI)",
description: parameterDescriptions.d0.text,
value: d0, value: d0,
setter: setD0, setter: setD0,
min: 0.1e6, min: 0.1e6,

93
src/TextWithPopover.tsx Normal file
View File

@ -0,0 +1,93 @@
import React from "react";
import { makeStyles } from "@material-ui/core/styles";
import Popover from "@material-ui/core/Popover";
import Typography from "@material-ui/core/Typography";
import Box from "@material-ui/core/Box";
const useStyles = makeStyles(theme => ({
container: {
color: theme.palette.text.secondary,
display: "flex",
marginLeft: "6px",
fontSize: "0.9rem",
cursor: "pointer",
transition: "color ease 150ms",
"&:hover": {
color: "#c3c9d0"
}
},
popoverContainer: {
padding: theme.spacing(2),
"& > p:not(:last-child)": {
paddingBottom: theme.spacing(1),
marginBottom: theme.spacing(1),
borderBottom: "1px solid #3f5463"
}
},
paper: {
backgroundColor: "#384b59",
maxWidth: theme.breakpoints.values.md * 0.9,
[`@media screen and (max-width: ${theme.breakpoints.values.md}px)`]: {
maxWidth: "90vw"
},
padding: theme.spacing(0.5)
},
descriptionBody: {
color: "#dbdfe4"
}
}));
export default function TextWithPopover({
content,
popoverText
}: {
content: string;
popoverText: string;
}) {
const classes = useStyles();
const [anchorEl, setAnchorEl] = React.useState(null);
function handleClick(event: any) {
setAnchorEl(event.currentTarget);
}
function handleClose() {
setAnchorEl(null);
}
const open = Boolean(anchorEl);
const id = open ? "simple-popover" : undefined;
return (
<div className={classes.container}>
<div aria-describedby={id} onClick={handleClick}>
<Typography gutterBottom>{content}</Typography>
</div>
<Popover
PaperProps={{
className: classes.paper
}}
id={id}
open={open}
anchorEl={anchorEl}
onClose={handleClose}
onClick={handleClose}
anchorOrigin={{
vertical: "bottom",
horizontal: "center"
}}
transformOrigin={{
vertical: "top",
horizontal: "center"
}}
>
<Box className={classes.popoverContainer}>
<Typography>{content}</Typography>
<Typography className={classes.descriptionBody}>
{popoverText}
</Typography>
</Box>
</Popover>
</div>
);
}

View File

@ -0,0 +1,69 @@
export interface DescriptionObject {
[key: string]: { name: string; text: string };
}
export const parameterDescriptions: DescriptionObject = {
theta: {
name: "Allocation to funding pool",
text:
"The percentage of the funds raised in the Hatch sale that go directly into the project funding pool to compensate future work done in the project"
},
p0: {
name: "Hatch price",
text:
"The price paid per 'ABC token' by community members involved in hatching the project"
},
p1: {
name: "Post-hatch price",
text:
"The price of the 'ABC token' when the curve enters the open phase and is live for public participation"
},
wFee: {
name: "Exit tribute",
text:
"The percentage of funds that are diverted to the project funding pool from community members who exit funds from the project by burning 'ABC tokens' in exchange for collateral"
},
d0: {
name: "Initial raise",
text: "Total funds raised in the hatch period of the ABC launch"
}
};
export const simulationParameterDescriptions: DescriptionObject = {
price: {
name: "Price",
text: "Price of the token over time."
},
floorPrice: {
name: "Floor price",
text:
"Lower bound of the price guaranteed by the vesting of hatch tokens. It decreases over time as more hatch tokens are allowed to be traded"
},
exitTributes: {
name: "Total exit tributes",
text:
"Cumulative sum of exit tributes collected from only exit /sell transactions"
}
};
export const resultParameterDescriptions: DescriptionObject = {
totalReserve: {
name: "Total reserve",
text:
"Total DAI in the smart contract reserve at the end of the simulated period"
},
initialHatchFunds: {
name: "Funds generated from initial hatch",
text:
"Fraction of the funds (theta) raised during the hatch that go directly to the cause (analytic result)"
},
exitTributes: {
name: "Funds generated from exit tributes",
text: simulationParameterDescriptions.exitTributes.text
},
slippage: {
name: "Average slippage",
text:
"Average of the slippage of each transaction occured during the simulation period"
}
};

View File

@ -1,5 +1,6 @@
export interface InputFieldInterface { export interface InputFieldInterface {
label: string; label: string;
description: string;
value: number; value: number;
setter(newValue: any): void; setter(newValue: any): void;
min: number; min: number;