112 lines
3.2 KiB
TypeScript
112 lines
3.2 KiB
TypeScript
import { FormControl } from 'components/FormControl'
|
|
import { AddressInput, NumberInput } from 'components/forms/FormInput'
|
|
import { useEffect, useId, useMemo } from 'react'
|
|
import { FaMinus, FaPlus } from 'react-icons/fa'
|
|
|
|
import { useInputState, useNumberInputState } from './FormInput.hooks'
|
|
|
|
export interface Attribute {
|
|
address: string
|
|
weight: number
|
|
}
|
|
|
|
export interface MemberAttributesProps {
|
|
title: string
|
|
subtitle?: string
|
|
isRequired?: boolean
|
|
attributes: [string, Attribute][]
|
|
onAdd: () => void
|
|
onChange: (key: string, attribute: Attribute) => void
|
|
onRemove: (key: string) => void
|
|
}
|
|
|
|
export function MemberAttributes(props: MemberAttributesProps) {
|
|
const { title, subtitle, isRequired, attributes, onAdd, onChange, onRemove } = props
|
|
|
|
const calculateMemberPercent = (id: string) => {
|
|
const total = attributes.reduce((acc, [, { weight }]) => acc + (weight ? weight : 0), 0)
|
|
// return attributes.map(([id, { weight }]) => [id, weight / total])
|
|
const memberWeight = attributes.find(([memberId]) => memberId === id)?.[1].weight
|
|
return memberWeight ? memberWeight / total : 0
|
|
}
|
|
|
|
return (
|
|
<FormControl isRequired={isRequired} subtitle={subtitle} title={title}>
|
|
{attributes.map(([id], i) => (
|
|
<MemberAttribute
|
|
key={`ma-${id}`}
|
|
defaultAttribute={attributes[i][1]}
|
|
id={id}
|
|
isLast={i === attributes.length - 1}
|
|
onAdd={onAdd}
|
|
onChange={onChange}
|
|
onRemove={onRemove}
|
|
percentage={(Number(calculateMemberPercent(id)) * 100).toFixed(2)}
|
|
/>
|
|
))}
|
|
</FormControl>
|
|
)
|
|
}
|
|
|
|
export interface MemberAttributeProps {
|
|
id: string
|
|
isLast: boolean
|
|
onAdd: MemberAttributesProps['onAdd']
|
|
onChange: MemberAttributesProps['onChange']
|
|
onRemove: MemberAttributesProps['onRemove']
|
|
defaultAttribute: Attribute
|
|
percentage?: string
|
|
}
|
|
|
|
export function MemberAttribute({
|
|
id,
|
|
isLast,
|
|
onAdd,
|
|
onChange,
|
|
onRemove,
|
|
defaultAttribute,
|
|
percentage,
|
|
}: MemberAttributeProps) {
|
|
const Icon = useMemo(() => (isLast ? FaPlus : FaMinus), [isLast])
|
|
|
|
const htmlId = useId()
|
|
|
|
const addressState = useInputState({
|
|
id: `ma-address-${htmlId}`,
|
|
name: `ma-address-${htmlId}`,
|
|
title: `Address`,
|
|
defaultValue: defaultAttribute.address,
|
|
})
|
|
|
|
const weightState = useNumberInputState({
|
|
id: `ma-weight-${htmlId}`,
|
|
name: `ma-weight-${htmlId}`,
|
|
title: `Weight ${percentage ? `: ${percentage}%` : ''}`,
|
|
defaultValue: defaultAttribute.weight,
|
|
})
|
|
|
|
useEffect(() => {
|
|
onChange(id, { address: addressState.value, weight: weightState.value })
|
|
}, [addressState.value, weightState.value, id])
|
|
|
|
return (
|
|
<div className="grid relative md:grid-cols-[50%_43%_7%] lg:grid-cols-[65%_28%_7%] 2xl:grid-cols-[70%_23%_7%] 2xl:space-x-2">
|
|
<AddressInput {...addressState} />
|
|
<NumberInput {...weightState} />
|
|
|
|
<div className="flex justify-end items-end pb-2 w-8">
|
|
<button
|
|
className="flex justify-center items-center p-2 bg-stargaze-80 hover:bg-plumbus-60 rounded-full"
|
|
onClick={(e) => {
|
|
e.preventDefault()
|
|
isLast ? onAdd() : onRemove(id)
|
|
}}
|
|
type="button"
|
|
>
|
|
<Icon className="w-3 h-3" />
|
|
</button>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|