Vote for council candidates (#1427)
* Change council to collective/elections * fix proposal type * package * Submit candidacy * update index * Minor eslint cleanups * rename back to council * blank line * council voting * interface * Cleanup * tslint * retract vote * travis ci * rv console * SetIndex type * types.ts SetIndex import * bump api & fix retractVoter
This commit is contained in:
parent
64e2f8e99c
commit
43cb425b56
@ -14,9 +14,9 @@ import { withApi, withCalls, withMulti } from '@polkadot/ui-api';
|
||||
|
||||
import translate from '../translate';
|
||||
|
||||
type Props = TxModalProps & ApiProps & {
|
||||
interface Props extends TxModalProps, ApiProps {
|
||||
memberCount: number;
|
||||
};
|
||||
}
|
||||
|
||||
interface State extends TxModalState {
|
||||
method: Method | null;
|
||||
|
||||
@ -2,11 +2,13 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { AccountId } from '@polkadot/types';
|
||||
|
||||
import React from 'react';
|
||||
import { AddressCard } from '@polkadot/ui-app';
|
||||
|
||||
interface Props {
|
||||
address: string;
|
||||
address: AccountId;
|
||||
}
|
||||
|
||||
export default class Candidate extends React.PureComponent<Props> {
|
||||
|
||||
@ -2,9 +2,9 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { BlockNumber } from '@polkadot/types';
|
||||
import { I18nProps } from '@polkadot/ui-app/types';
|
||||
|
||||
import BN from 'bn.js';
|
||||
import React from 'react';
|
||||
import { AddressCard } from '@polkadot/ui-app';
|
||||
import { formatNumber } from '@polkadot/util';
|
||||
@ -13,7 +13,7 @@ import translate from '../translate';
|
||||
|
||||
interface Props extends I18nProps {
|
||||
address: string;
|
||||
block: BN;
|
||||
block: BlockNumber;
|
||||
}
|
||||
|
||||
class Member extends React.PureComponent<Props> {
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { I18nProps } from '@polkadot/ui-app/types';
|
||||
import { ElectionsInfo } from './types';
|
||||
import { ComponentProps } from './types';
|
||||
|
||||
import React from 'react';
|
||||
import { Columar, Column } from '@polkadot/ui-app';
|
||||
@ -13,9 +13,7 @@ import translate from '../translate';
|
||||
import Candidate from './Candidate';
|
||||
import Member from './Member';
|
||||
|
||||
interface Props extends I18nProps {
|
||||
electionsInfo: ElectionsInfo;
|
||||
}
|
||||
interface Props extends I18nProps, ComponentProps {}
|
||||
|
||||
class Members extends React.PureComponent<Props> {
|
||||
public render (): React.ReactNode {
|
||||
@ -28,7 +26,7 @@ class Members extends React.PureComponent<Props> {
|
||||
emptyText={t('No members found')}
|
||||
headerText={t('members')}
|
||||
>
|
||||
{members.map(([address, block]): React.ReactNode => (
|
||||
{Object.entries(members).map(([address, block]): React.ReactNode => (
|
||||
<Member
|
||||
address={address}
|
||||
block={block}
|
||||
@ -43,7 +41,7 @@ class Members extends React.PureComponent<Props> {
|
||||
{candidates.map((address): React.ReactNode => (
|
||||
<Candidate
|
||||
address={address}
|
||||
key={address}
|
||||
key={address.toString()}
|
||||
/>
|
||||
))}
|
||||
</Column>
|
||||
|
||||
@ -2,20 +2,16 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { ElectionsInfo } from './types';
|
||||
import { ComponentProps } from './types';
|
||||
|
||||
import BN from 'bn.js';
|
||||
import React from 'react';
|
||||
import { Button } from '@polkadot/ui-app';
|
||||
import TxModal, { TxModalState, TxModalProps } from '@polkadot/ui-app/TxModal';
|
||||
import TxModal, { TxModalState as State, TxModalProps } from '@polkadot/ui-app/TxModal';
|
||||
|
||||
import translate from '../translate';
|
||||
|
||||
interface Props extends TxModalProps {
|
||||
electionsInfo: ElectionsInfo;
|
||||
}
|
||||
|
||||
type State = TxModalState;
|
||||
interface Props extends ComponentProps, TxModalProps {}
|
||||
|
||||
class SubmitCandidacy extends TxModal<Props, State> {
|
||||
protected headerText = (): string => this.props.t('Submit your council candidacy');
|
||||
@ -44,14 +40,11 @@ class SubmitCandidacy extends TxModal<Props, State> {
|
||||
const { t } = this.props;
|
||||
|
||||
return (
|
||||
<Button.Group>
|
||||
<Button
|
||||
isPrimary
|
||||
label={t('Submit candidacy')}
|
||||
labelIcon='add'
|
||||
onClick={this.showModal}
|
||||
/>
|
||||
</Button.Group>
|
||||
<Button
|
||||
label={t('Submit candidacy')}
|
||||
labelIcon='add'
|
||||
onClick={this.showModal}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { I18nProps } from '@polkadot/ui-app/types';
|
||||
import { ElectionsInfo } from './types';
|
||||
import { ComponentProps } from './types';
|
||||
|
||||
import React from 'react';
|
||||
import { SummaryBox, CardSummary } from '@polkadot/ui-app';
|
||||
@ -12,29 +12,26 @@ import { formatNumber } from '@polkadot/util';
|
||||
|
||||
import translate from '../translate';
|
||||
|
||||
interface Props extends I18nProps {
|
||||
electionsInfo: ElectionsInfo;
|
||||
}
|
||||
interface Props extends I18nProps, ComponentProps {}
|
||||
|
||||
class Summary extends React.PureComponent<Props> {
|
||||
public render (): React.ReactNode {
|
||||
const { electionsInfo, t } = this.props;
|
||||
|
||||
const { members, candidateCount, desiredSeats, termDuration, voteCount } = electionsInfo;
|
||||
|
||||
return (
|
||||
<SummaryBox>
|
||||
<section>
|
||||
<CardSummary label={t('seats')}>
|
||||
{formatNumber(members.length)}/{formatNumber(desiredSeats)}
|
||||
{formatNumber(Object.keys(members).length)}/{formatNumber(desiredSeats)}
|
||||
</CardSummary>
|
||||
<CardSummary label={t('candidates')}>
|
||||
{formatNumber(candidateCount)}
|
||||
</CardSummary>
|
||||
</section>
|
||||
<section>
|
||||
<CardSummary label={t('total votes')}>
|
||||
{formatNumber(voteCount)}
|
||||
<CardSummary label={t('voting round')}>
|
||||
#{formatNumber(voteCount)}
|
||||
</CardSummary>
|
||||
</section>
|
||||
|
||||
|
||||
319
packages/app-council/src/Overview/Vote.tsx
Normal file
319
packages/app-council/src/Overview/Vote.tsx
Normal file
@ -0,0 +1,319 @@
|
||||
// Copyright 2017-2019 @polkadot/ui-staking authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { VoteIndex } from '@polkadot/types';
|
||||
// import { SetIndex } from '@polkadot/types/srml/types';
|
||||
import { DerivedVoterPositions } from '@polkadot/api-derive/types';
|
||||
import { ApiProps } from '@polkadot/ui-api/types';
|
||||
import { ComponentProps } from './types';
|
||||
|
||||
import BN from 'bn.js';
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { withApi, withCalls, withMulti } from '@polkadot/ui-api';
|
||||
import { AddressRow, Button, Icon, Toggle, TxButton } from '@polkadot/ui-app';
|
||||
import TxModal, { TxModalState, TxModalProps } from '@polkadot/ui-app/TxModal';
|
||||
|
||||
import translate from '../translate';
|
||||
|
||||
interface Props extends ApiProps, ComponentProps, TxModalProps {
|
||||
voterPositions?: DerivedVoterPositions;
|
||||
}
|
||||
|
||||
interface State extends TxModalState {
|
||||
approvals: boolean[] | null;
|
||||
oldApprovals: boolean[] | null;
|
||||
// voterPositions: DerivedVoterPositions;
|
||||
}
|
||||
|
||||
const AlreadyVoted = styled.article`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 0.5rem 0;
|
||||
|
||||
& > :first-child {
|
||||
flex: 1 1;
|
||||
}
|
||||
|
||||
& > :not(:first-child) {
|
||||
margin: 0;
|
||||
}
|
||||
`;
|
||||
|
||||
const Candidates = styled.div`
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
`;
|
||||
|
||||
const Candidate = styled.div`
|
||||
cursor: pointer;
|
||||
width: 25rem;
|
||||
min-width: calc(50% - 1rem);
|
||||
border-radius: 0.5rem;
|
||||
border: 1px solid #eee;
|
||||
padding: 0.5rem;
|
||||
margin: 0.5rem;
|
||||
transition: all 0.2s;
|
||||
|
||||
b {
|
||||
min-width: 5rem;
|
||||
}
|
||||
|
||||
&.aye {
|
||||
background-color: rgba(0, 255, 0, 0.05);
|
||||
|
||||
b {
|
||||
color: green;
|
||||
}
|
||||
}
|
||||
|
||||
&.nay {
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
`;
|
||||
|
||||
class Vote extends TxModal<Props, State> {
|
||||
public static emptyApprovals (length: number): boolean[] {
|
||||
return [...new Array(length).keys()].map((): boolean => false);
|
||||
}
|
||||
|
||||
public static getDerivedStateFromProps ({ electionsInfo: { candidateCount } }: Props, { approvals }: State): Partial<State> {
|
||||
const state: Partial<State> = {};
|
||||
// if (voterPositions) {
|
||||
// state.voters = Object.keys(voterSets).reduce(
|
||||
// (result: Record<string, VoterPosition>, accountId, globalIndex): Record<string, VoterPosition> => {
|
||||
// result[accountId] = {
|
||||
// setIndex: voterSets[accountId],
|
||||
// globalIndex: new BN(globalIndex)
|
||||
// };
|
||||
// return result;
|
||||
// },
|
||||
// {}
|
||||
// );
|
||||
// }
|
||||
|
||||
if (candidateCount && !approvals) {
|
||||
state.approvals = state.oldApprovals || Vote.emptyApprovals(candidateCount.toNumber());
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
public constructor (props: Props) {
|
||||
super(props);
|
||||
|
||||
this.defaultState = {
|
||||
...this.defaultState,
|
||||
approvals: null,
|
||||
oldApprovals: null
|
||||
};
|
||||
|
||||
this.state = {
|
||||
...this.defaultState
|
||||
};
|
||||
}
|
||||
|
||||
public componentDidMount (): void {
|
||||
this.fetchApprovals();
|
||||
}
|
||||
|
||||
public componentDidUpdate (_: Props, prevState: State): void {
|
||||
const { accountId } = this.state;
|
||||
|
||||
if (accountId !== prevState.accountId) {
|
||||
this.fetchApprovals();
|
||||
}
|
||||
}
|
||||
|
||||
protected headerText = (): string => this.props.t('Vote for current candidates');
|
||||
|
||||
protected accountLabel = (): string => this.props.t('Voting account');
|
||||
|
||||
protected accountHelp = (): string => this.props.t('This account will be use to approve or disapprove each candidate.');
|
||||
|
||||
protected txMethod = (): string => 'elections.setApprovals';
|
||||
|
||||
protected txParams = (): [boolean[] | null, VoteIndex, BN | null] => {
|
||||
const { electionsInfo: { nextVoterSet, voteCount }, voterPositions } = this.props;
|
||||
const { accountId, approvals } = this.state;
|
||||
|
||||
return [
|
||||
approvals ? approvals.slice(0, 1 + approvals.lastIndexOf(true)) : [],
|
||||
new VoteIndex(voteCount),
|
||||
voterPositions && accountId && voterPositions[accountId]
|
||||
? voterPositions[accountId].setIndex
|
||||
: nextVoterSet
|
||||
];
|
||||
}
|
||||
|
||||
protected isDisabled = (): boolean => {
|
||||
const { accountId, oldApprovals } = this.state;
|
||||
|
||||
return !accountId || !!oldApprovals;
|
||||
}
|
||||
|
||||
protected renderTrigger = (): React.ReactNode => {
|
||||
const { electionsInfo: { candidates }, t } = this.props;
|
||||
|
||||
return (
|
||||
<Button
|
||||
isDisabled={candidates.length === 0}
|
||||
isPrimary
|
||||
label={t('Vote')}
|
||||
labelIcon='check'
|
||||
onClick={this.showModal}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
protected renderContent = (): React.ReactNode => {
|
||||
const { electionsInfo: { candidates }, voterPositions, t } = this.props;
|
||||
const { accountId, approvals, oldApprovals } = this.state;
|
||||
|
||||
return (
|
||||
<>
|
||||
{
|
||||
(oldApprovals && accountId && voterPositions && voterPositions[accountId]) && (
|
||||
<AlreadyVoted className='warning padded'>
|
||||
<div>
|
||||
<Icon name='warning sign' />
|
||||
{t('You have already voted in this round')}
|
||||
</div>
|
||||
<Button.Group>
|
||||
<TxButton
|
||||
accountId={accountId}
|
||||
isNegative
|
||||
label={t('Retract vote')}
|
||||
labelIcon='delete'
|
||||
onSuccess={this.onRetractVote}
|
||||
params={[voterPositions[accountId].globalIndex]}
|
||||
tx='elections.retractVoter'
|
||||
/>
|
||||
</Button.Group>
|
||||
</AlreadyVoted>
|
||||
)
|
||||
}
|
||||
<Candidates>
|
||||
{
|
||||
candidates.map((accountId, index): React.ReactNode => {
|
||||
if (!approvals) {
|
||||
return null;
|
||||
}
|
||||
const { [index]: isAye } = approvals;
|
||||
return (
|
||||
<Candidate
|
||||
className={isAye ? 'aye' : 'nay'}
|
||||
key={accountId.toString()}
|
||||
{...(
|
||||
!oldApprovals
|
||||
? { onClick: (): void => this.onChangeVote(index)() }
|
||||
: {}
|
||||
)}
|
||||
>
|
||||
<AddressRow
|
||||
isInline
|
||||
value={accountId}
|
||||
>
|
||||
{this.renderToggle(index)}
|
||||
</AddressRow>
|
||||
</Candidate>
|
||||
);
|
||||
})
|
||||
}
|
||||
</Candidates>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
private renderToggle = (index: number): React.ReactNode => {
|
||||
const { t } = this.props;
|
||||
const { approvals, oldApprovals } = this.state;
|
||||
|
||||
if (!approvals) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { [index]: bool } = approvals;
|
||||
|
||||
return (
|
||||
<Toggle
|
||||
isDisabled={!!oldApprovals}
|
||||
label={
|
||||
bool
|
||||
? (
|
||||
<b>{t('Aye')}</b>
|
||||
)
|
||||
: (
|
||||
<b>{t('No vote')}</b>
|
||||
)
|
||||
}
|
||||
value={bool}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
private emptyApprovals = (): boolean[] => {
|
||||
const { electionsInfo: { candidateCount } } = this.props;
|
||||
|
||||
return Vote.emptyApprovals(candidateCount.toNumber());
|
||||
}
|
||||
|
||||
private fetchApprovals = (): void => {
|
||||
const { api, electionsInfo: { voteCount }, voterPositions } = this.props;
|
||||
const { accountId } = this.state;
|
||||
|
||||
if (!accountId) {
|
||||
return;
|
||||
}
|
||||
|
||||
api.derive.elections.approvalsOfAt(accountId, voteCount)
|
||||
.then((approvals: boolean[]): void => {
|
||||
if ((voterPositions && voterPositions[accountId.toString()]) && approvals && approvals.length && approvals !== this.state.approvals) {
|
||||
this.setState({
|
||||
approvals,
|
||||
oldApprovals: approvals
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
approvals: this.emptyApprovals()
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected onChangeAccount = (accountId: string | null): void => {
|
||||
this.setState({
|
||||
accountId,
|
||||
oldApprovals: null
|
||||
});
|
||||
}
|
||||
|
||||
private onChangeVote = (index: number): (isChecked?: boolean) => void =>
|
||||
(isChecked?: boolean): void => {
|
||||
this.setState(({ approvals }: State): Pick<State, never> => {
|
||||
if (!approvals) {
|
||||
return {};
|
||||
}
|
||||
return {
|
||||
approvals: approvals.map((b, i): boolean => i === index ? isChecked || !approvals[index] : b)
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
private onRetractVote = (): void => {
|
||||
this.setState({
|
||||
approvals: this.emptyApprovals(),
|
||||
oldApprovals: null
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default withMulti(
|
||||
Vote,
|
||||
translate,
|
||||
withApi,
|
||||
withCalls<Props>(
|
||||
['derive.elections.voterPositions', { propName: 'voterPositions' }]
|
||||
)
|
||||
);
|
||||
@ -2,54 +2,45 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { AccountId, BlockNumber } from '@polkadot/types';
|
||||
import { ElectionsInfo } from './types';
|
||||
import { createType, BlockNumber, VoteIndex } from '@polkadot/types';
|
||||
import { SetIndex } from '@polkadot/types/srml/elections/types';
|
||||
import { DerivedElectionsInfo } from '@polkadot/api-derive/types';
|
||||
import { ComponentProps as Props } from './types';
|
||||
|
||||
import BN from 'bn.js';
|
||||
import React from 'react';
|
||||
|
||||
import { withCalls } from '@polkadot/ui-api';
|
||||
import { Button } from '@polkadot/ui-app';
|
||||
|
||||
import Members from './Members';
|
||||
import SubmitCandidacy from './SubmitCandidacy';
|
||||
import Summary from './Summary';
|
||||
import Vote from './Vote';
|
||||
|
||||
interface Props {
|
||||
elections_members?: [string, BlockNumber][];
|
||||
elections_candidates?: string[];
|
||||
elections_candidateCount?: BN;
|
||||
elections_desiredSeats?: BN;
|
||||
elections_termDuration?: BN;
|
||||
elections_voteCount?: BN;
|
||||
}
|
||||
|
||||
interface State {
|
||||
electionsInfo: ElectionsInfo;
|
||||
}
|
||||
|
||||
class Overview extends React.PureComponent<Props, State> {
|
||||
public static getDerivedStateFromProps ({
|
||||
elections_members: members = [],
|
||||
elections_candidates: candidates = [],
|
||||
elections_candidateCount: candidateCount = new BN(0),
|
||||
elections_desiredSeats: desiredSeats = new BN(0),
|
||||
elections_termDuration: termDuration = new BN(0),
|
||||
elections_voteCount: voteCount = new BN(0)
|
||||
}: Props): State {
|
||||
const electionsInfo: ElectionsInfo = {
|
||||
members, candidates, candidateCount, desiredSeats, termDuration, voteCount
|
||||
};
|
||||
|
||||
return { electionsInfo };
|
||||
}
|
||||
const NULL_INFO: DerivedElectionsInfo = {
|
||||
members: {},
|
||||
candidates: [],
|
||||
candidateCount: new BN(0),
|
||||
desiredSeats: new BN(0),
|
||||
nextVoterSet: createType<SetIndex>('SetIndex', 0),
|
||||
termDuration: new BlockNumber(0),
|
||||
voteCount: new VoteIndex(0),
|
||||
voterCount: createType<SetIndex>('SetIndex', 0)
|
||||
};
|
||||
|
||||
class Overview extends React.PureComponent<Props> {
|
||||
public render (): React.ReactNode {
|
||||
const { electionsInfo } = this.state;
|
||||
const { electionsInfo = NULL_INFO } = this.props;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Summary electionsInfo={electionsInfo} />
|
||||
<SubmitCandidacy electionsInfo={electionsInfo} />
|
||||
<Button.Group>
|
||||
<SubmitCandidacy electionsInfo={electionsInfo} />
|
||||
<Button.Or />
|
||||
<Vote electionsInfo={electionsInfo} />
|
||||
</Button.Group>
|
||||
<Members electionsInfo={electionsInfo} />
|
||||
</>
|
||||
);
|
||||
@ -57,20 +48,10 @@ class Overview extends React.PureComponent<Props, State> {
|
||||
}
|
||||
|
||||
export default withCalls<Props>(
|
||||
['query.elections.members', {
|
||||
transform: (active: [AccountId, BlockNumber][]): [string, BlockNumber][] =>
|
||||
active.map(([accountId, blockNumber]): [string, BlockNumber] =>
|
||||
[accountId.toString(), blockNumber]
|
||||
)
|
||||
}],
|
||||
['query.elections.candidates', {
|
||||
transform: (candidates: AccountId[]): string[] =>
|
||||
candidates.map((accountId): string =>
|
||||
accountId.toString()
|
||||
)
|
||||
}],
|
||||
'query.elections.candidateCount',
|
||||
'query.elections.desiredSeats',
|
||||
'query.elections.termDuration',
|
||||
'query.elections.voteCount'
|
||||
[
|
||||
'derive.elections.info',
|
||||
{
|
||||
propName: 'electionsInfo'
|
||||
}
|
||||
]
|
||||
)(Overview);
|
||||
|
||||
@ -2,15 +2,16 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { BlockNumber } from '@polkadot/types';
|
||||
import { SetIndex } from '@polkadot/types/srml/types';
|
||||
import { DerivedElectionsInfo } from '@polkadot/api-derive/types';
|
||||
|
||||
import BN from 'bn.js';
|
||||
|
||||
export interface ElectionsInfo {
|
||||
members: [string, BlockNumber][];
|
||||
candidates: string[];
|
||||
candidateCount: BN;
|
||||
desiredSeats: BN;
|
||||
termDuration: BN;
|
||||
voteCount: BN;
|
||||
export interface ComponentProps {
|
||||
electionsInfo: DerivedElectionsInfo;
|
||||
}
|
||||
|
||||
export interface VoterPosition {
|
||||
setIndex: SetIndex;
|
||||
globalIndex: BN;
|
||||
}
|
||||
|
||||
@ -13,7 +13,7 @@ import Overview from './Overview';
|
||||
import Motions from './Motions';
|
||||
import translate from './translate';
|
||||
|
||||
type Props = AppProps & BareProps & I18nProps;
|
||||
interface Props extends AppProps, BareProps, I18nProps {}
|
||||
|
||||
interface State {
|
||||
tabs: TabItem[];
|
||||
|
||||
@ -106,6 +106,7 @@ class General extends React.PureComponent<Props, State> {
|
||||
return (
|
||||
<>
|
||||
<Toggle
|
||||
asSwitch
|
||||
className='settings--cutomToggle'
|
||||
defaultValue={isCustomNode}
|
||||
label={t('custom endpoint')}
|
||||
|
||||
@ -9,28 +9,33 @@ import SUICheckbox from 'semantic-ui-react/dist/commonjs/modules/Checkbox';
|
||||
import styled from 'styled-components';
|
||||
|
||||
interface Props extends BareProps {
|
||||
asSwitch?: boolean;
|
||||
defaultValue?: boolean;
|
||||
isDisabled?: boolean;
|
||||
label: React.ReactNode;
|
||||
onChange?: (isChecked: boolean) => void;
|
||||
value?: boolean;
|
||||
}
|
||||
|
||||
class Toggle extends React.PureComponent<Props> {
|
||||
public render (): React.ReactNode {
|
||||
const { className, defaultValue, label } = this.props;
|
||||
const { className, asSwitch, defaultValue, isDisabled, value, label } = this.props;
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
<SUICheckbox
|
||||
checked={value}
|
||||
disabled={isDisabled}
|
||||
defaultChecked={defaultValue}
|
||||
onChange={this.onChange}
|
||||
toggle
|
||||
toggle={asSwitch}
|
||||
/>
|
||||
<label>{label}</label>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
private onChange = (event: React.FormEvent<HTMLInputElement>, { checked }: any): void => {
|
||||
private onChange = (_: React.FormEvent<HTMLInputElement>, { checked }: any): void => {
|
||||
const { onChange } = this.props;
|
||||
|
||||
onChange && onChange(checked);
|
||||
|
||||
@ -37,6 +37,7 @@ interface Props extends ApiProps {
|
||||
isPrimary?: boolean;
|
||||
isUnsigned?: boolean;
|
||||
label: React.ReactNode;
|
||||
labelIcon?: string;
|
||||
onClick?: () => any;
|
||||
onFailed?: TxFailedCallback;
|
||||
onStart?: () => void;
|
||||
@ -61,7 +62,7 @@ class TxButtonInner extends React.PureComponent<InnerProps> {
|
||||
};
|
||||
|
||||
public render (): React.ReactNode {
|
||||
const { accountId, className, icon, iconSize, isBasic, isDisabled, isNegative, isPrimary, isUnsigned, label, tooltip } = this.props;
|
||||
const { accountId, className, icon, iconSize, isBasic, isDisabled, isNegative, isPrimary, isUnsigned, label, labelIcon, tooltip } = this.props;
|
||||
const { isSending } = this.state;
|
||||
const needsAccount = isUnsigned
|
||||
? false
|
||||
@ -78,6 +79,7 @@ class TxButtonInner extends React.PureComponent<InnerProps> {
|
||||
isNegative={isNegative}
|
||||
isPrimary={isUndefined(isPrimary) ? (!isNegative && !isBasic) : isPrimary}
|
||||
label={label}
|
||||
labelIcon={labelIcon}
|
||||
onClick={this.send}
|
||||
size={iconSize}
|
||||
/>
|
||||
|
||||
@ -67,16 +67,20 @@ class Vector extends React.PureComponent<Props, State> {
|
||||
style={style}
|
||||
>
|
||||
{values.map((value, index): React.ReactNode => (
|
||||
<Component
|
||||
defaultValue={value}
|
||||
isDisabled={isDisabled}
|
||||
key={index}
|
||||
label={`${index}: ${subType.type}`}
|
||||
onChange={this.onChange(index)}
|
||||
onEnter={onEnter}
|
||||
type={subType}
|
||||
withLabel={withLabel}
|
||||
/>
|
||||
this.isParamHidden(index)
|
||||
? null
|
||||
: (
|
||||
<Component
|
||||
defaultValue={value}
|
||||
isDisabled={isDisabled}
|
||||
key={index}
|
||||
label={`${index}: ${subType.type}`}
|
||||
onChange={this.onChange(index)}
|
||||
onEnter={onEnter}
|
||||
type={subType}
|
||||
withLabel={withLabel}
|
||||
/>
|
||||
)
|
||||
))}
|
||||
{this.renderButtons()}
|
||||
</Bare>
|
||||
@ -108,6 +112,16 @@ class Vector extends React.PureComponent<Props, State> {
|
||||
);
|
||||
}
|
||||
|
||||
private isParamHidden = (index: number): boolean => {
|
||||
const { isDisabled, type } = this.props;
|
||||
const { values } = this.state;
|
||||
|
||||
if (type.type === 'Vec<bool>' && isDisabled && values[index].value === false) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private onChange = (index: number): (value: RawParam) => void => {
|
||||
return (value: RawParam): void => {
|
||||
this.setState(
|
||||
|
||||
Loading…
Reference in New Issue
Block a user