mirror of
https://github.com/oceanprotocol/market.git
synced 2024-12-02 05:57:29 +01:00
lots of pricve refactor, build out advanced UI
This commit is contained in:
parent
576b9f4c2e
commit
cbee8a714d
@ -37,7 +37,6 @@
|
|||||||
{
|
{
|
||||||
"name": "price",
|
"name": "price",
|
||||||
"label": "Price",
|
"label": "Price",
|
||||||
"help": "Set your price for accessing this data set in Ocean Tokens.",
|
|
||||||
"type": "price",
|
"type": "price",
|
||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
|
@ -1,9 +1,23 @@
|
|||||||
import React, { ReactElement } from 'react'
|
import React, { ReactElement } from 'react'
|
||||||
import styles from './Help.module.css'
|
import styles from './Help.module.css'
|
||||||
import Markdown from '../Markdown'
|
import Markdown from '../Markdown'
|
||||||
|
import classNames from 'classnames/bind'
|
||||||
|
|
||||||
const FormHelp = ({ children }: { children: string }): ReactElement => (
|
const cx = classNames.bind(styles)
|
||||||
<Markdown className={styles.help} text={children} />
|
|
||||||
)
|
const FormHelp = ({
|
||||||
|
children,
|
||||||
|
className
|
||||||
|
}: {
|
||||||
|
children: string
|
||||||
|
className?: string
|
||||||
|
}): ReactElement => {
|
||||||
|
const styleClasses = cx({
|
||||||
|
help: true,
|
||||||
|
[className]: className
|
||||||
|
})
|
||||||
|
|
||||||
|
return <Markdown className={styleClasses} text={children} />
|
||||||
|
}
|
||||||
|
|
||||||
export default FormHelp
|
export default FormHelp
|
||||||
|
@ -33,7 +33,8 @@
|
|||||||
|
|
||||||
.input[readonly],
|
.input[readonly],
|
||||||
.input[disabled] {
|
.input[disabled] {
|
||||||
background-color: var(--brand-grey-lighter);
|
background-color: var(--brand-grey-dimmed);
|
||||||
|
color: var(--brand-grey-light);
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
@ -84,6 +85,56 @@
|
|||||||
padding-left: 0.5rem;
|
padding-left: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.prefixGroup,
|
||||||
|
.postfixGroup {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prefixGroup input,
|
||||||
|
.postfixGroup input {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prefixGroup input {
|
||||||
|
border-left: 0;
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
border-bottom-left-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.postfixGroup input {
|
||||||
|
border-right: 0;
|
||||||
|
border-top-right-radius: 0;
|
||||||
|
border-bottom-right-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prefix,
|
||||||
|
.postfix {
|
||||||
|
border: 1px solid var(--brand-grey-lighter);
|
||||||
|
min-height: 43px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding-left: calc(var(--spacer) / 2);
|
||||||
|
padding-right: calc(var(--spacer) / 2);
|
||||||
|
color: var(--color-secondary);
|
||||||
|
font-size: var(--font-size-small);
|
||||||
|
transition: border 0.2s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prefix {
|
||||||
|
border-top-left-radius: var(--border-radius);
|
||||||
|
border-bottom-left-radius: var(--border-radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
.postfix {
|
||||||
|
border-top-right-radius: var(--border-radius);
|
||||||
|
border-bottom-right-radius: var(--border-radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
.input:focus + .postfix {
|
||||||
|
border-color: var(--brand-grey);
|
||||||
|
}
|
||||||
|
|
||||||
/* Size modifiers */
|
/* Size modifiers */
|
||||||
|
|
||||||
.small {
|
.small {
|
||||||
|
@ -7,7 +7,7 @@ import Terms from '../../molecules/FormFields/Terms'
|
|||||||
import Price from '../../molecules/FormFields/Price'
|
import Price from '../../molecules/FormFields/Price'
|
||||||
|
|
||||||
export default function InputElement(props: InputProps): ReactElement {
|
export default function InputElement(props: InputProps): ReactElement {
|
||||||
const { type, options, rows, name, value } = props
|
const { type, options, rows, name, prefix, postfix } = props
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'select':
|
case 'select':
|
||||||
@ -62,7 +62,19 @@ export default function InputElement(props: InputProps): ReactElement {
|
|||||||
case 'terms':
|
case 'terms':
|
||||||
return <Terms name={name} {...props} />
|
return <Terms name={name} {...props} />
|
||||||
default:
|
default:
|
||||||
return (
|
return prefix || postfix ? (
|
||||||
|
<div className={`${prefix ? styles.prefixGroup : styles.postfixGroup}`}>
|
||||||
|
{prefix && <div className={styles.prefix}>{prefix}</div>}
|
||||||
|
<input
|
||||||
|
id={name}
|
||||||
|
className={styles.input}
|
||||||
|
name={name}
|
||||||
|
{...props}
|
||||||
|
type={type || 'text'}
|
||||||
|
/>
|
||||||
|
{postfix && <div className={styles.postfix}>{postfix}</div>}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
<input
|
<input
|
||||||
id={name}
|
id={name}
|
||||||
className={styles.input}
|
className={styles.input}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { FormEvent, ChangeEvent, ReactElement } from 'react'
|
import React, { FormEvent, ChangeEvent, ReactElement, ReactNode } from 'react'
|
||||||
import InputElement from './InputElement'
|
import InputElement from './InputElement'
|
||||||
import Help from './Help'
|
import Help from './Help'
|
||||||
import Label from './Label'
|
import Label from './Label'
|
||||||
@ -31,8 +31,12 @@ export interface InputProps {
|
|||||||
pattern?: string
|
pattern?: string
|
||||||
min?: string
|
min?: string
|
||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
|
readOnly?: boolean
|
||||||
field?: any
|
field?: any
|
||||||
form?: any
|
form?: any
|
||||||
|
prefix?: string
|
||||||
|
postfix?: string
|
||||||
|
step?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Input(props: Partial<InputProps>): ReactElement {
|
export default function Input(props: Partial<InputProps>): ReactElement {
|
||||||
|
@ -2,19 +2,29 @@ import React, { useEffect, useState, ReactElement } from 'react'
|
|||||||
import useSWR from 'swr'
|
import useSWR from 'swr'
|
||||||
import { fetchData, isBrowser } from '../../../utils'
|
import { fetchData, isBrowser } from '../../../utils'
|
||||||
import styles from './Conversion.module.css'
|
import styles from './Conversion.module.css'
|
||||||
|
import classNames from 'classnames/bind'
|
||||||
|
|
||||||
|
const cx = classNames.bind(styles)
|
||||||
|
|
||||||
const currencies = 'EUR' // comma-separated list
|
const currencies = 'EUR' // comma-separated list
|
||||||
const url = `https://api.coingecko.com/api/v3/simple/price?ids=ocean-protocol&vs_currencies=${currencies}&include_24hr_change=true`
|
const url = `https://api.coingecko.com/api/v3/simple/price?ids=ocean-protocol&vs_currencies=${currencies}&include_24hr_change=true`
|
||||||
|
|
||||||
export default function Conversion({
|
export default function Conversion({
|
||||||
price,
|
price,
|
||||||
update = true
|
update = true,
|
||||||
|
className
|
||||||
}: {
|
}: {
|
||||||
price: string // expects price in OCEAN, not wei
|
price: string // expects price in OCEAN, not wei
|
||||||
update?: boolean
|
update?: boolean
|
||||||
|
className?: string
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
const [priceEur, setPriceEur] = useState('0.00')
|
const [priceEur, setPriceEur] = useState('0.00')
|
||||||
|
|
||||||
|
const styleClasses = cx({
|
||||||
|
conversion: true,
|
||||||
|
[className]: className
|
||||||
|
})
|
||||||
|
|
||||||
const onSuccess = async (data: { 'ocean-protocol': { eur: number } }) => {
|
const onSuccess = async (data: { 'ocean-protocol': { eur: number } }) => {
|
||||||
if (!data) return
|
if (!data) return
|
||||||
if (!price || price === '' || price === '0') {
|
if (!price || price === '' || price === '0') {
|
||||||
@ -45,5 +55,5 @@ export default function Conversion({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return <span className={styles.conversion}>≈ EUR {priceEur}</span>
|
return <span className={styleClasses}>≈ EUR {priceEur}</span>
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
.advancedInput {
|
.advanced {
|
||||||
display: flex;
|
|
||||||
gap: calc(var(--spacer) / 2);
|
|
||||||
justify-content: center;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.advancedInput label {
|
.advanced > div:last-child {
|
||||||
|
display: grid;
|
||||||
|
gap: calc(var(--spacer) / 2);
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { ReactElement } from 'react'
|
import React, { ReactElement, useState, ChangeEvent } from 'react'
|
||||||
import { InputProps } from '../../../atoms/Input'
|
import { InputProps } from '../../../atoms/Input'
|
||||||
import InputElement from '../../../atoms/Input/InputElement'
|
import InputElement from '../../../atoms/Input/InputElement'
|
||||||
import stylesIndex from './index.module.css'
|
import stylesIndex from './index.module.css'
|
||||||
@ -6,13 +6,51 @@ import styles from './Advanced.module.css'
|
|||||||
import Label from '../../../atoms/Input/Label'
|
import Label from '../../../atoms/Input/Label'
|
||||||
import { MetadataPublishForm } from '../../../../@types/MetaData'
|
import { MetadataPublishForm } from '../../../../@types/MetaData'
|
||||||
import Cost from './Cost'
|
import Cost from './Cost'
|
||||||
|
import Conversion from '../../../atoms/Price/Conversion'
|
||||||
|
import FormHelp from '../../../atoms/Input/Help'
|
||||||
|
|
||||||
export default function Advanced(props: InputProps): ReactElement {
|
export default function Advanced(props: InputProps): ReactElement {
|
||||||
const { price } = props.form.values as MetadataPublishForm
|
const { price } = props.form.values as MetadataPublishForm
|
||||||
|
const [weight, setWeight] = useState('10')
|
||||||
|
|
||||||
|
const liquidity = (price.cost * Number(weight)).toString()
|
||||||
|
|
||||||
|
function handleWeightChange(event: ChangeEvent<HTMLInputElement>) {
|
||||||
|
setWeight(event.target.value)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`${stylesIndex.content} ${styles.advancedInput}`}>
|
<div className={stylesIndex.content}>
|
||||||
|
<div className={styles.advanced}>
|
||||||
|
<FormHelp className={stylesIndex.help}>
|
||||||
|
Set your price for accessing this data set. A Data Token for this data
|
||||||
|
set worth the entered amount of OCEAN, and a Data Token/OCEAN
|
||||||
|
liquidity pool will be created with Balancer.
|
||||||
|
</FormHelp>
|
||||||
|
|
||||||
|
<div>
|
||||||
<Cost {...props} />
|
<Cost {...props} />
|
||||||
|
<div>
|
||||||
|
<Label htmlFor="weight">Weight on Data Token</Label>
|
||||||
|
<InputElement
|
||||||
|
value={weight}
|
||||||
|
name="weight"
|
||||||
|
type="number"
|
||||||
|
onChange={handleWeightChange}
|
||||||
|
step="10"
|
||||||
|
postfix="%"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className={styles.liquidity}>
|
||||||
|
<Label htmlFor="liquidity">Liquidity</Label>
|
||||||
|
<InputElement
|
||||||
|
value={liquidity}
|
||||||
|
name="liquidity"
|
||||||
|
readOnly
|
||||||
|
prefix="OCEAN"
|
||||||
|
/>
|
||||||
|
<Conversion price={liquidity} className={stylesIndex.conversion} />
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<Label htmlFor="price.tokensToMint">Tokens to Mint</Label>
|
<Label htmlFor="price.tokensToMint">Tokens to Mint</Label>
|
||||||
<InputElement
|
<InputElement
|
||||||
@ -20,8 +58,11 @@ export default function Advanced(props: InputProps): ReactElement {
|
|||||||
value={(price && price.tokensToMint) || 1}
|
value={(price && price.tokensToMint) || 1}
|
||||||
name="price.tokensToMint"
|
name="price.tokensToMint"
|
||||||
type="number"
|
type="number"
|
||||||
|
disabled
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,28 +0,0 @@
|
|||||||
.cost {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cost label {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cost input {
|
|
||||||
border-top-left-radius: 0;
|
|
||||||
border-bottom-left-radius: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.prefix {
|
|
||||||
border: 1px solid var(--brand-grey-lighter);
|
|
||||||
min-height: 43px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
padding-left: calc(var(--spacer) / 2);
|
|
||||||
padding-right: calc(var(--spacer) / 2);
|
|
||||||
color: var(--color-secondary);
|
|
||||||
font-size: var(--font-size-small);
|
|
||||||
margin-right: -2px;
|
|
||||||
border-top-left-radius: var(--border-radius);
|
|
||||||
border-bottom-left-radius: var(--border-radius);
|
|
||||||
}
|
|
@ -1,26 +1,30 @@
|
|||||||
import React, { ReactElement } from 'react'
|
import React, { ReactElement } from 'react'
|
||||||
import InputElement from '../../../atoms/Input/InputElement'
|
import InputElement from '../../../atoms/Input/InputElement'
|
||||||
import styles from './Cost.module.css'
|
|
||||||
import { MetadataPublishForm } from '../../../../@types/MetaData'
|
import { MetadataPublishForm } from '../../../../@types/MetaData'
|
||||||
import Conversion from '../../../atoms/Price/Conversion'
|
import Conversion from '../../../atoms/Price/Conversion'
|
||||||
import { InputProps } from '../../../atoms/Input'
|
import { InputProps } from '../../../atoms/Input'
|
||||||
import Label from '../../../atoms/Input/Label'
|
import Label from '../../../atoms/Input/Label'
|
||||||
|
import stylesIndex from './index.module.css'
|
||||||
|
|
||||||
export default function Cost(props: InputProps): ReactElement {
|
export default function Cost(props: InputProps): ReactElement {
|
||||||
const { price } = props.form.values as MetadataPublishForm
|
const { price } = props.form.values as MetadataPublishForm
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.cost}>
|
<div>
|
||||||
<Label htmlFor="price.cost">Cost</Label>
|
<Label htmlFor="price.cost">Cost</Label>
|
||||||
|
|
||||||
<div className={styles.prefix}>OCEAN</div>
|
|
||||||
<InputElement
|
<InputElement
|
||||||
{...props.field}
|
{...props.field}
|
||||||
value={(price && price.cost) || 0}
|
value={(price && price.cost) || 0}
|
||||||
name="price.cost"
|
name="price.cost"
|
||||||
type="number"
|
type="number"
|
||||||
|
prefix="OCEAN"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Conversion
|
||||||
|
price={price.cost.toString()}
|
||||||
|
className={stylesIndex.conversion}
|
||||||
/>
|
/>
|
||||||
<Conversion price={price.cost.toString()} />
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
.simple {
|
.simple {
|
||||||
margin-bottom: calc(var(--spacer) / 2);
|
|
||||||
text-align: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.simple > div {
|
.simple > div:last-child {
|
||||||
justify-content: center;
|
max-width: 13.25rem;
|
||||||
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.simple label {
|
.simple label {
|
||||||
|
@ -9,9 +9,12 @@ export default function Simple(props: InputProps): ReactElement {
|
|||||||
return (
|
return (
|
||||||
<div className={stylesIndex.content}>
|
<div className={stylesIndex.content}>
|
||||||
<div className={styles.simple}>
|
<div className={styles.simple}>
|
||||||
|
<FormHelp className={stylesIndex.help}>
|
||||||
|
Set your price for accessing this data set. A Data Token for this data
|
||||||
|
set worth the entered amount of OCEAN will be created.
|
||||||
|
</FormHelp>
|
||||||
<Cost {...props} />
|
<Cost {...props} />
|
||||||
</div>
|
</div>
|
||||||
{props.help && <FormHelp>{props.help}</FormHelp>}
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -20,3 +20,14 @@
|
|||||||
.content p {
|
.content p {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.conversion {
|
||||||
|
display: block;
|
||||||
|
padding-left: 5rem;
|
||||||
|
margin-top: calc(var(--spacer) / 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.help {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: calc(var(--spacer) / 2);
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user